autodiff.in.cpp 45.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// ----------------------------------------------------------------------------
// Copyright 2017 Nervana Systems Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------

#include <algorithm>
16
#include <functional>
17
#include <memory>
18
#include <tuple>
19 20 21 22

#include "gtest/gtest.h"

#include "ngraph/ngraph.hpp"
23
#include "util/autodiff/backprop_function.hpp"
adstraw's avatar
adstraw committed
24
#include "util/autodiff/numeric_compare.hpp"
25
#include "util/random.hpp"
26 27 28 29

using namespace std;
using namespace ngraph;

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
TEST(${BACKEND_NAME}, backwards_maxpool_n4_c1_hw4_2x2_max)
{
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
    auto backend = manager->allocate_backend();

    auto shape_a = Shape{1, 4, 4, 4}; //in CHWN
    auto maxpool_shape = Shape{1, 4, 3, 3};

    auto A = make_shared<op::Parameter>(element::i32, shape_a);
    auto reshape = make_shared<op::Reshape>(
        A, AxisVector{0, 3, 1, 2}, Shape{1, 4, 4, 4}); //convert CHWN to CNHW
    auto window_shape = Shape{2, 2};
    auto window_movement_strides = Strides{1, 1};
    auto maxpool = make_shared<op::MaxPool>(reshape, window_shape, window_movement_strides);
    auto f = make_shared<Function>(maxpool, op::Parameters{A});

    shared_ptr<runtime::TensorView> ep =
        backend->make_primary_tensor_view(element::i32, maxpool_shape);
    vector<int> dataEp(shape_size(maxpool_shape), 4);

    shared_ptr<runtime::TensorView> input =
        backend->make_primary_tensor_view(element::i32, shape_a);
    shared_ptr<runtime::TensorView> output =
        backend->make_primary_tensor_view(element::i32, shape_a);

    vector<int> dataInput{11, 65, 44, 28, 31, 33, 21, 66, 40, 49, 69, 57, 47, 30, 24, 27,
                          13, 56, 46, 60, 61, 41, 25, 42, 48, 53, 51, 43, 59, 58, 29, 71,
                          17, 22, 72, 18, 39, 35, 15, 38, 64, 52, 73, 67, 62, 50, 10, 68,
                          45, 63, 16, 14, 55, 54, 37, 20, 36, 12, 70, 34, 19, 26, 32, 23};

    vector<int> expected{//delta
                         0, 4, 0, 0, 0, 0, 0, 8, 0, 0, 8, 0, 0, 0, 0, 0, 0, 4, 4,  4, 12, 0,
                         0, 0, 0, 8, 0, 0, 4, 8, 0, 8, 0, 0, 8, 0, 0, 0, 0, 4, 16, 4, 16, 8,
                         0, 0, 0, 4, 0, 4, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0};

    copy_data(ep, dataEp);
    copy_data(input, dataInput);

    auto C = make_shared<op::Parameter>(element::i32, maxpool_shape);
    auto df = autodiff::backprop_function(f);
    auto external = manager->compile(df);
    auto cf = backend->make_call_frame(external);
    cf->tensor_call({input, ep}, {output});
73
    ASSERT_TRUE(read_vector<int>(output) == expected);
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
}

TEST(${BACKEND_NAME}, backwards_maxpool_n2_c1_hw5_3x3_str2_max)
{
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
    auto backend = manager->allocate_backend();

    auto shape_a = Shape{1, 5, 5, 2}; //in CHWN
    auto maxpool_shape = Shape{1, 2, 2, 2};

    auto A = make_shared<op::Parameter>(element::i32, shape_a);
    auto reshape = make_shared<op::Reshape>(
        A, AxisVector{0, 3, 1, 2}, Shape{1, 2, 5, 5}); //convert CHWN to CNHW
    auto window_shape = Shape{3, 3};
    auto window_movement_strides = Strides{2, 2};
    auto maxpool = make_shared<op::MaxPool>(reshape, window_shape, window_movement_strides);
    auto f = make_shared<Function>(maxpool, op::Parameters{A});

    shared_ptr<runtime::TensorView> ep =
        backend->make_primary_tensor_view(element::i32, maxpool_shape);
    vector<int> dataEp(shape_size(maxpool_shape), 4);

    shared_ptr<runtime::TensorView> input =
        backend->make_primary_tensor_view(element::i32, shape_a);
    shared_ptr<runtime::TensorView> output =
        backend->make_primary_tensor_view(element::i32, shape_a);

    vector<int> dataInput{58, 15, 51, 35, 18, 47, 31, 32, 52, 21, 36, 38, 57, 54, 25, 45, 23,
                          30, 16, 27, 48, 20, 41, 37, 43, 39, 22, 28, 33, 29, 12, 17, 44, 42,
                          19, 40, 10, 46, 34, 53, 26, 55, 50, 13, 24, 14, 49, 56, 59, 11};

    vector<int> expected{//delta
                         4, 0, 0, 0, 0, 4, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 4, 4, 0};

    copy_data(ep, dataEp);
    copy_data(input, dataInput);

    auto C = make_shared<op::Parameter>(element::i32, maxpool_shape);
    auto df = autodiff::backprop_function(f);
    auto external = manager->compile(df);
    auto cf = backend->make_call_frame(external);
    cf->tensor_call({input, ep}, {output});
117
    ASSERT_TRUE(read_vector<int>(output) == expected);
118 119
}

120
TEST(${BACKEND_NAME}, backwards_abs)
121
{
122
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
123 124 125 126 127 128 129 130 131
    auto backend = manager->allocate_backend();

    // The numeric derivative and the symbolic one may disagree around 0, so we will dance around
    // that point by skipping (-0.01,0.01).
    test::Uniform<float> rng_neg(-1.0f, -0.01f);
    test::Uniform<float> rng_pos(0.01f, 1.0f);
    auto shape = Shape{2, 3};

    auto make_graph = [shape]() {
132
        auto X = make_shared<op::Parameter>(element::f32, shape);
133 134
        return make_shared<Function>(make_shared<op::Abs>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
135 136 137 138 139 140 141 142 143 144 145 146 147 148
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x_neg = rng_neg.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_neg}, .01f, .01f));

        auto x_pos = rng_pos.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_pos}, .01f, .01f));
    }
149 150
}

151
TEST(${BACKEND_NAME}, backwards_add)
152
{
153
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
154 155
    auto backend = manager->allocate_backend();

156
    test::Uniform<float> rng(-1.0f, 1.0f);
157
    auto shape = Shape{2, 3};
158 159
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
160

161
    auto make_graph = [shape]() {
162 163
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
164
        return make_shared<Function>(X0 + X1, std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
165
    };
166 167
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
168 169
}

170
TEST(${BACKEND_NAME}, backwards_add_nested)
171
{
172
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
173 174
    auto backend = manager->allocate_backend();

175
    test::Uniform<float> rng(-1.0f, 1.0f);
176
    auto shape = Shape{2, 3};
177 178
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
179 180

    auto make_graph = [shape]() {
181 182
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
183 184
        return make_shared<Function>((X0 + X1) + (X1 + X0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
185
    };
186 187
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
188 189
}

190
TEST(${BACKEND_NAME}, backwards_broadcast0)
191
{
192
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
193 194
    auto backend = manager->allocate_backend();

195
    test::Uniform<float> rng(-1.0f, 1.0f);
196
    auto shape = Shape{3};
197
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
198 199

    auto make_graph = [shape]() {
200
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
201 202 203
        return make_shared<Function>(make_shared<op::Broadcast>(X0, Shape{2, 3}, AxisSet{0}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
204
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x0}, .01f, .01f));
205 206
}

207
TEST(${BACKEND_NAME}, backwards_broadcast1)
208
{
209
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
210 211
    auto backend = manager->allocate_backend();

212
    test::Uniform<float> rng(-1.0f, 1.0f);
213
    auto shape = Shape{3};
214
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
215 216

    auto make_graph = [shape]() {
217
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
218 219 220
        return make_shared<Function>(make_shared<op::Broadcast>(X0, Shape{3, 2}, AxisSet{1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
221
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x0}, .01f, .01f));
222 223
}

224
TEST(${BACKEND_NAME}, backwards_concat_vector)
225
{
226
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
227 228 229 230
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-1.0f, 1.0f);
    auto shape_0 = Shape{3};
231
    auto x0 = rng.initialize(backend->make_primary_tensor_view(element::f32, shape_0));
232
    auto shape_1 = Shape{2};
233
    auto x1 = rng.initialize(backend->make_primary_tensor_view(element::f32, shape_1));
234
    auto shape_2 = Shape{1};
235
    auto x2 = rng.initialize(backend->make_primary_tensor_view(element::f32, shape_2));
236 237

    auto make_graph = [shape_0, shape_1, shape_2]() {
238 239 240
        auto X0 = make_shared<op::Parameter>(element::f32, shape_0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape_1);
        auto X2 = make_shared<op::Parameter>(element::f32, shape_2);
241 242 243 244 245 246 247
        return make_shared<Function>(make_shared<op::Concat>(Nodes{X0, X1, X2}, 0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1, x2}, .01f, .01f));
}

248
TEST(${BACKEND_NAME}, backwards_concat_axis_0)
249
{
250
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
251 252 253 254
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-1.0f, 1.0f);
    auto shape_0 = Shape{3, 2};
255
    auto x0 = rng.initialize(backend->make_primary_tensor_view(element::f32, shape_0));
256
    auto shape_1 = Shape{2, 2};
257
    auto x1 = rng.initialize(backend->make_primary_tensor_view(element::f32, shape_1));
258
    auto shape_2 = Shape{1, 2};
259
    auto x2 = rng.initialize(backend->make_primary_tensor_view(element::f32, shape_2));
260 261

    auto make_graph = [shape_0, shape_1, shape_2]() {
262 263 264
        auto X0 = make_shared<op::Parameter>(element::f32, shape_0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape_1);
        auto X2 = make_shared<op::Parameter>(element::f32, shape_2);
265 266 267 268 269 270 271
        return make_shared<Function>(make_shared<op::Concat>(Nodes{X0, X1, X2}, 0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1, x2}, .01f, .01f));
}

272
TEST(${BACKEND_NAME}, backwards_concat_axis_1)
273
{
274
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
275 276 277 278
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-1.0f, 1.0f);
    auto shape_0 = Shape{2, 3};
279
    auto x0 = rng.initialize(backend->make_primary_tensor_view(element::f32, shape_0));
280
    auto shape_1 = Shape{2, 2};
281
    auto x1 = rng.initialize(backend->make_primary_tensor_view(element::f32, shape_1));
282
    auto shape_2 = Shape{2, 1};
283
    auto x2 = rng.initialize(backend->make_primary_tensor_view(element::f32, shape_2));
284 285

    auto make_graph = [shape_0, shape_1, shape_2]() {
286 287 288
        auto X0 = make_shared<op::Parameter>(element::f32, shape_0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape_1);
        auto X2 = make_shared<op::Parameter>(element::f32, shape_2);
289 290 291 292 293 294 295
        return make_shared<Function>(make_shared<op::Concat>(Nodes{X0, X1, X2}, 1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1, x2}, .01f, .01f));
}

296
TEST(${BACKEND_NAME}, backwards_ceiling)
297
{
298
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
299 300 301 302 303 304 305 306 307 308
    auto backend = manager->allocate_backend();

    // The numeric derivative and the symbolic one may disagree near integers, so we will dance around
    // them.
    test::Uniform<float> rng_minusone(-0.95f, -0.05f);
    test::Uniform<float> rng_plusone(0.05f, 0.95f);
    test::Uniform<float> rng_plustwo(1.05f, 1.95f);
    auto shape = Shape{2, 3};

    auto make_graph = [shape]() {
309
        auto X = make_shared<op::Parameter>(element::f32, shape);
310 311
        return make_shared<Function>(make_shared<op::Ceiling>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x_minusone = rng_minusone.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare<float>(
            manager, backend, make_graph, {x_minusone}, .01f, .01f));

        auto x_plusone = rng_plusone.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_plusone}, .01f, .01f));

        auto x_plustwo = rng_plustwo.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_plustwo}, .01f, .01f));
    }
}

333
TEST(${BACKEND_NAME}, backwards_cos)
334
{
335
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
336 337 338 339 340
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-10.0f, 10.0f);
    auto shape = Shape{2, 3};
    auto make_graph = [shape]() {
341
        auto X = make_shared<op::Parameter>(element::f32, shape);
342 343
        return make_shared<Function>(make_shared<op::Cos>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
344 345 346 347 348 349 350 351 352 353
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
    }
}

354
TEST(${BACKEND_NAME}, backwards_cosh)
355
{
356
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
357 358 359 360 361
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-10.0f, 10.0f);
    auto shape = Shape{2, 3};
    auto make_graph = [shape]() {
362
        auto X = make_shared<op::Parameter>(element::f32, shape);
363 364
        return make_shared<Function>(make_shared<op::Cosh>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
365 366 367 368 369 370 371 372 373 374
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
    }
}

375
TEST(${BACKEND_NAME}, backwards_divide)
376
{
377
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
378
    auto backend = manager->allocate_backend();
379

380 381 382
    test::Uniform<float> rng(-1.0f, 1.0f);
    test::Uniform<float> rng1(1.0f, 2.0f);
    test::Uniform<float> rng2(-2.0f, -1.0f);
383
    auto shape = Shape{2, 3};
384 385 386
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x1 = rng1.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x2 = rng2.initialize(backend->make_primary_tensor_view<float>(shape));
387

388
    auto make_graph = [shape]() {
389 390
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
391
        return make_shared<Function>(X0 / X1, std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
392
    };
393 394 395 396
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x2}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
397
}
398

399
TEST(${BACKEND_NAME}, backwards_dot_scalar_scalar)
400
{
401
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
402 403
    auto backend = manager->allocate_backend();

404
    test::Uniform<float> rng(-1.0f, 1.0f);
405 406
    auto shape0 = Shape{};
    auto shape1 = Shape{};
407 408
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape0));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape1));
409 410

    auto make_graph = [shape0, shape1]() {
411 412
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
413 414 415
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
416 417
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
418 419
}

420
TEST(${BACKEND_NAME}, backwards_dot_scalar_tensor)
421
{
422
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
423 424
    auto backend = manager->allocate_backend();

425
    test::Uniform<float> rng(-1.0f, 1.0f);
426 427
    auto shape0 = Shape{};
    auto shape1 = Shape{3, 4};
428 429
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape0));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape1));
430 431

    auto make_graph = [shape0, shape1]() {
432 433
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
434 435 436
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
437 438
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
439 440
}

441
TEST(${BACKEND_NAME}, backwards_dot_tensor_scalar)
442
{
443
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
444 445
    auto backend = manager->allocate_backend();

446
    test::Uniform<float> rng(-1.0f, 1.0f);
447 448
    auto shape0 = Shape{3, 4};
    auto shape1 = Shape{};
449 450
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape0));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape1));
451 452

    auto make_graph = [shape0, shape1]() {
453 454
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
455 456 457
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
458 459
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
460 461
}

462
TEST(${BACKEND_NAME}, backwards_dot_vector_vector)
463
{
464
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
465 466
    auto backend = manager->allocate_backend();

467
    test::Uniform<float> rng(-1.0f, 1.0f);
468 469
    auto shape0 = Shape{3};
    auto shape1 = Shape{3};
470 471
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape0));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape1));
472 473

    auto make_graph = [shape0, shape1]() {
474 475
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
476 477 478
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
479 480
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
481 482
}

483
TEST(${BACKEND_NAME}, backwards_dot_tensor_vector)
484
{
485
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
486 487
    auto backend = manager->allocate_backend();

488
    test::Uniform<float> rng(-1.0f, 1.0f);
489 490
    auto shape0 = Shape{4, 3};
    auto shape1 = Shape{3};
491 492
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape0));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape1));
493 494

    auto make_graph = [shape0, shape1]() {
495 496
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
497 498 499
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
500 501
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
502 503
}

504
TEST(${BACKEND_NAME}, backwards_dot_tensor2_tensor2)
505
{
506
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
507 508
    auto backend = manager->allocate_backend();

509
    test::Uniform<float> rng(-1.0f, 1.0f);
510 511
    auto shape0 = Shape{4, 3};
    auto shape1 = Shape{3, 5};
512 513
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape0));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape1));
514 515

    auto make_graph = [shape0, shape1]() {
516 517
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
518 519
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
Adam Procter's avatar
Adam Procter committed
520 521 522 523 524 525 526
    };
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
}

TEST(${BACKEND_NAME}, backwards_dot_tensor3_tensor3)
{
527
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
Adam Procter's avatar
Adam Procter committed
528 529 530 531 532 533 534 535 536
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-1.0f, 1.0f);
    auto shape0 = Shape{2, 4, 3};
    auto shape1 = Shape{4, 3, 3};
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape0));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape1));

    auto make_graph = [shape0, shape1]() {
537 538
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
Adam Procter's avatar
Adam Procter committed
539 540
        return make_shared<Function>(make_shared<op::Dot>(X0, X1, 2),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
541
    };
542 543
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
544 545
}

546
TEST(${BACKEND_NAME}, backwards_exp)
Scott Cyphers's avatar
Scott Cyphers committed
547
{
548
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
549 550
    auto backend = manager->allocate_backend();

551
    test::Uniform<float> rng(-1.0f, 1.0f);
Scott Cyphers's avatar
Scott Cyphers committed
552
    auto shape = Shape{2, 3};
553
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
554 555

    auto make_graph = [shape]() {
556
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
557 558
        return make_shared<Function>(make_shared<op::Exp>(X0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
Scott Cyphers's avatar
Scott Cyphers committed
559
    };
560
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x0}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
561 562
}

563
TEST(${BACKEND_NAME}, backwards_floor)
564
{
565
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
566 567 568 569 570 571 572 573 574 575
    auto backend = manager->allocate_backend();

    // The numeric derivative and the symbolic one may disagree near integers, so we will dance around
    // them.
    test::Uniform<float> rng_minusone(-0.95f, -0.05f);
    test::Uniform<float> rng_plusone(0.05f, 0.95f);
    test::Uniform<float> rng_plustwo(1.05f, 1.95f);
    auto shape = Shape{2, 3};

    auto make_graph = [shape]() {
576
        auto X = make_shared<op::Parameter>(element::f32, shape);
577 578
        return make_shared<Function>(make_shared<op::Floor>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x_minusone = rng_minusone.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare<float>(
            manager, backend, make_graph, {x_minusone}, .01f, .01f));

        auto x_plusone = rng_plusone.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_plusone}, .01f, .01f));

        auto x_plustwo = rng_plustwo.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_plustwo}, .01f, .01f));
    }
}

600
TEST(${BACKEND_NAME}, backwards_log)
Scott Cyphers's avatar
Scott Cyphers committed
601
{
602
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
603 604
    auto backend = manager->allocate_backend();

605
    test::Uniform<float> rng(1.0f, 2.0f);
Scott Cyphers's avatar
Scott Cyphers committed
606
    auto shape = Shape{2, 3};
607
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
608 609

    auto make_graph = [shape]() {
610
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
611 612
        return make_shared<Function>(make_shared<op::Log>(X0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
Scott Cyphers's avatar
Scott Cyphers committed
613
    };
614
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x0}, .01f, .01f));
615 616
}

617
TEST(${BACKEND_NAME}, backwards_maximum)
618
{
619
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
620 621
    auto backend = manager->allocate_backend();

622
    test::Uniform<float> rng(-1.0f, 1.0f);
623
    auto shape = Shape{2, 3};
624 625
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
626 627

    auto make_graph = [shape]() {
628 629
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
630 631 632
        return make_shared<Function>(make_shared<op::Maximum>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
633 634
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
635 636
}

637
TEST(${BACKEND_NAME}, backwards_minimum)
638
{
639
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
640 641
    auto backend = manager->allocate_backend();

642
    test::Uniform<float> rng(-1.0f, 1.0f);
643
    auto shape = Shape{2, 3};
644 645
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
646 647

    auto make_graph = [shape]() {
648 649
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
650 651 652
        return make_shared<Function>(make_shared<op::Minimum>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
653 654
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
655 656
}

657
TEST(${BACKEND_NAME}, backwards_multiply)
658
{
659
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
660
    auto backend = manager->allocate_backend();
661

662
    test::Uniform<float> rng(-1.0f, 1.0f);
663
    auto shape = Shape{2, 3};
664 665
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
666

667
    auto make_graph = [shape]() {
668 669
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
670
        return make_shared<Function>(X0 * X1, std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
671
    };
672 673
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
674
}
Scott Cyphers's avatar
Scott Cyphers committed
675

676
TEST(${BACKEND_NAME}, backwards_negative)
Scott Cyphers's avatar
Scott Cyphers committed
677
{
678
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
679 680
    auto backend = manager->allocate_backend();

681
    test::Uniform<float> rng(-1.0f, 1.0f);
Scott Cyphers's avatar
Scott Cyphers committed
682
    auto shape = Shape{2, 3};
683
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
684 685

    auto make_graph = [shape]() {
686
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
687
        return make_shared<Function>(-X0, std::vector<std::shared_ptr<op::Parameter>>{X0});
Scott Cyphers's avatar
Scott Cyphers committed
688
    };
689
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x0}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
690 691
}

692
TEST(${BACKEND_NAME}, backwards_parameter)
Scott Cyphers's avatar
Scott Cyphers committed
693
{
694
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
695 696
    auto backend = manager->allocate_backend();

697
    test::Uniform<float> rng(-1.0f, 1.0f);
Scott Cyphers's avatar
Scott Cyphers committed
698
    auto shape = Shape{2, 3};
699
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
700
    auto make_graph = [shape]() {
701
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
702
        return make_shared<Function>(X0, std::vector<std::shared_ptr<op::Parameter>>{X0});
Scott Cyphers's avatar
Scott Cyphers committed
703
    };
704
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x0}, .01f, .01f));
705 706
}

707
TEST(${BACKEND_NAME}, backwards_power)
708
{
709
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
710 711 712 713 714 715 716
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng_neg(-5.0f, -0.5f);
    test::Uniform<float> rng_pos(0.5f, 5.0f);
    auto shape = Shape{2, 3};

    auto make_graph = [shape]() {
717 718
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
        return make_shared<Function>(std::make_shared<op::Power>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };

    auto x0 = rng_neg.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x1 = rng_pos.initialize(backend->make_primary_tensor_view<float>(shape));

    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));

    x0 = rng_pos.initialize(backend->make_primary_tensor_view<float>(shape));
    x1 = rng_neg.initialize(backend->make_primary_tensor_view<float>(shape));

    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));

    x0 = rng_neg.initialize(backend->make_primary_tensor_view<float>(shape));
    x1 = rng_neg.initialize(backend->make_primary_tensor_view<float>(shape));

    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));

    x0 = rng_pos.initialize(backend->make_primary_tensor_view<float>(shape));
    x1 = rng_pos.initialize(backend->make_primary_tensor_view<float>(shape));

    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
}

748
TEST(${BACKEND_NAME}, backwards_replace_slice)
749
{
750
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
751 752 753 754 755 756
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-10.0f, 10.0f);
    auto shape_x = Shape{5, 5};
    auto shape_y = Shape{2, 2};
    auto make_graph = [shape_x, shape_y]() {
757 758
        auto X = make_shared<op::Parameter>(element::f32, shape_x);
        auto Y = make_shared<op::Parameter>(element::f32, shape_y);
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
        return make_shared<Function>(
            make_shared<op::ReplaceSlice>(X, Y, Coordinate{2, 3}, Coordinate{4, 5}),
            std::vector<std::shared_ptr<op::Parameter>>{X, Y});
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape_x));
        auto y = rng.initialize(backend->make_primary_tensor_view<float>(shape_y));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x, y}, .01f, .01f));
    }
}

774
TEST(${BACKEND_NAME}, backwards_reshape)
775
{
776
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
777 778
    auto backend = manager->allocate_backend();

779
    test::Uniform<float> rng(-1.0f, 1.0f);
780
    auto shape = Shape{3, 4};
781
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
782 783

    auto make_graph = [shape]() {
784
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
785 786 787
        return make_shared<Function>(make_shared<op::Reshape>(X0, AxisVector{1, 0}, Shape{4, 3}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
788
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x0}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
789 790
}

791
TEST(${BACKEND_NAME}, backwards_select)
792
{
793
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
794 795 796 797 798
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-10.0f, 10.0f);
    auto shape = Shape{2, 3};
    auto make_graph = [shape]() {
799 800 801
        auto X0 = make_shared<op::Parameter>(element::boolean, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
        auto X2 = make_shared<op::Parameter>(element::f32, shape);
802 803 804 805 806 807
        return make_shared<Function>(make_shared<op::Select>(X0, X1, X2),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };

    for (auto i = 0; i < 100; i++)
    {
808
        auto x0 = backend->make_primary_tensor_view(element::boolean, shape);
809
        write_vector(x0, vector<char>{0, 1, 0, 1, 0, 1});
810 811 812 813 814 815 816 817 818 819 820 821 822 823
        auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
        auto x2 = rng.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare_selective<float>(manager,
                                                      backend,
                                                      make_graph,
                                                      {x0, x1, x2},
                                                      .01f,
                                                      .01f,
                                                      std::vector<bool>{false, true, true}));
    }
}

824
TEST(${BACKEND_NAME}, backwards_select_nested)
825
{
826
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
827 828 829 830 831
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-10.0f, 10.0f);
    auto shape = Shape{2, 3};
    auto make_graph = [shape]() {
832 833 834
        auto X0 = make_shared<op::Parameter>(element::boolean, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
        auto X2 = make_shared<op::Parameter>(element::f32, shape);
835 836 837 838 839 840
        return make_shared<Function>(make_shared<op::Select>(X0, X1 + X2, X2 - X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };

    for (auto i = 0; i < 100; i++)
    {
841
        auto x0 = backend->make_primary_tensor_view(element::boolean, shape);
842
        write_vector(x0, vector<char>{0, 1, 0, 1, 0, 1});
843 844 845 846 847 848 849 850 851 852 853 854 855 856
        auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
        auto x2 = rng.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare_selective<float>(manager,
                                                      backend,
                                                      make_graph,
                                                      {x0, x1, x2},
                                                      .01f,
                                                      .01f,
                                                      std::vector<bool>{false, true, true}));
    }
}

857
TEST(${BACKEND_NAME}, backwards_sign)
858
{
859
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
860 861 862 863 864 865 866 867 868
    auto backend = manager->allocate_backend();

    // The numeric derivative and the symbolic one may disagree around 0, so we will dance around
    // that point by skipping (-0.01,0.01).
    test::Uniform<float> rng_neg(-1.0f, -0.01f);
    test::Uniform<float> rng_pos(0.01f, 1.0f);
    auto shape = Shape{2, 3};

    auto make_graph = [shape]() {
869
        auto X = make_shared<op::Parameter>(element::f32, shape);
870 871
        return make_shared<Function>(make_shared<op::Sign>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x_neg = rng_neg.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_neg}, .01f, .01f));

        auto x_pos = rng_pos.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_pos}, .01f, .01f));
    }
}

888
TEST(${BACKEND_NAME}, backwards_sin)
889
{
890
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
891 892 893 894 895
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-10.0f, 10.0f);
    auto shape = Shape{2, 3};
    auto make_graph = [shape]() {
896
        auto X = make_shared<op::Parameter>(element::f32, shape);
897 898
        return make_shared<Function>(make_shared<op::Sin>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
899 900 901 902 903 904 905 906 907 908
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
    }
}

909
TEST(${BACKEND_NAME}, backwards_sinh)
910
{
911
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
912 913 914 915 916
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-10.0f, 10.0f);
    auto shape = Shape{2, 3};
    auto make_graph = [shape]() {
917
        auto X = make_shared<op::Parameter>(element::f32, shape);
918 919
        return make_shared<Function>(make_shared<op::Sinh>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
920 921 922 923 924 925 926 927 928 929
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
    }
}

930
TEST(${BACKEND_NAME}, backwards_slice)
931
{
932
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
933 934 935 936 937
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-10.0f, 10.0f);
    auto shape = Shape{5, 5};
    auto make_graph = [shape]() {
938
        auto X = make_shared<op::Parameter>(element::f32, shape);
939 940 941 942 943 944 945 946 947 948 949 950
        return make_shared<Function>(make_shared<op::Slice>(X, Coordinate{2, 3}, Coordinate{4, 5}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
    }
}

951
TEST(${BACKEND_NAME}, backwards_sqrt)
952
{
953
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
954 955 956 957 958 959
    auto backend = manager->allocate_backend();

    // Deriv has an asymptote at 0 so we'll stay away from there.
    test::Uniform<float> rng(0.1f, 10.0f);
    auto shape = Shape{2, 3};
    auto make_graph = [shape]() {
960
        auto X = make_shared<op::Parameter>(element::f32, shape);
961 962
        return make_shared<Function>(make_shared<op::Sqrt>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
963 964 965 966 967 968 969 970 971 972
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
    }
}

973
TEST(${BACKEND_NAME}, backwards_subtract)
Scott Cyphers's avatar
Scott Cyphers committed
974
{
975
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
976 977
    auto backend = manager->allocate_backend();

978
    test::Uniform<float> rng(-1.0f, 1.0f);
Scott Cyphers's avatar
Scott Cyphers committed
979
    auto shape = Shape{2, 3};
980 981
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
982 983

    auto make_graph = [shape]() {
984 985
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
986
        return make_shared<Function>(X0 - X1, std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
Scott Cyphers's avatar
Scott Cyphers committed
987
    };
988 989
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
990
}
Scott Cyphers's avatar
Scott Cyphers committed
991

992
TEST(${BACKEND_NAME}, backwards_sum_v2s)
993
{
994
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
995 996 997 998 999 1000 1001
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-1.0f, 1.0f);
    auto shape = Shape{8};
    auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

    auto make_graph = [shape]() {
1002
        auto X = make_shared<op::Parameter>(element::f32, shape);
1003 1004 1005 1006 1007 1008
        return make_shared<Function>(make_shared<op::Sum>(X, AxisSet{0}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
}

1009
TEST(${BACKEND_NAME}, backwards_sum_m2s)
1010
{
1011
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
1012 1013 1014 1015 1016 1017 1018
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-1.0f, 1.0f);
    auto shape = Shape{8, 9};
    auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

    auto make_graph = [shape]() {
1019
        auto X = make_shared<op::Parameter>(element::f32, shape);
1020 1021 1022 1023 1024 1025
        return make_shared<Function>(make_shared<op::Sum>(X, AxisSet{0, 1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
}

1026
TEST(${BACKEND_NAME}, backwards_sum_m2v_0)
1027
{
1028
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
1029 1030 1031 1032 1033 1034 1035
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-1.0f, 1.0f);
    auto shape = Shape{8, 9};
    auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

    auto make_graph = [shape]() {
1036
        auto X = make_shared<op::Parameter>(element::f32, shape);
1037 1038 1039 1040 1041 1042
        return make_shared<Function>(make_shared<op::Sum>(X, AxisSet{0}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
}

1043
TEST(${BACKEND_NAME}, backwards_sum_m2v_1)
1044
{
1045
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
1046 1047 1048 1049 1050 1051 1052
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-1.0f, 1.0f);
    auto shape = Shape{8, 9};
    auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

    auto make_graph = [shape]() {
1053
        auto X = make_shared<op::Parameter>(element::f32, shape);
1054 1055 1056 1057 1058 1059
        return make_shared<Function>(make_shared<op::Sum>(X, AxisSet{1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
}

1060
TEST(${BACKEND_NAME}, backwards_tan)
1061
{
1062
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
    auto backend = manager->allocate_backend();

    auto pi = 3.14159f;

    // Stay away from the asymptotes at 6 and 12 o'clock.
    auto slop = 0.1f;
    test::Uniform<float> rng_r(-pi / 2 + slop, pi / 2 - slop);
    test::Uniform<float> rng_l(pi / 2 + slop, (3 * pi) / 2 - slop);

    auto shape = Shape{2, 3};

    auto make_graph = [shape]() {
1075
        auto X = make_shared<op::Parameter>(element::f32, shape);
1076 1077
        return make_shared<Function>(make_shared<op::Tan>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x_r = rng_r.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_r}, .01f, .01f));

        auto x_l = rng_l.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(
            autodiff_numeric_compare<float>(manager, backend, make_graph, {x_l}, .01f, .01f));
    }
}

1094
TEST(${BACKEND_NAME}, backwards_tanh)
1095
{
1096
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
1097 1098 1099 1100 1101
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-10.0f, 10.0f);
    auto shape = Shape{2, 3};
    auto make_graph = [shape]() {
1102
        auto X = make_shared<op::Parameter>(element::f32, shape);
1103 1104
        return make_shared<Function>(make_shared<op::Tanh>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
    };

    for (auto i = 0; i < 100; i++)
    {
        auto x = rng.initialize(backend->make_primary_tensor_view<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
    }
}

1115
TEST(${BACKEND_NAME}, backwards_abc)
Scott Cyphers's avatar
Scott Cyphers committed
1116
{
1117
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
1118 1119
    auto backend = manager->allocate_backend();

1120
    test::Uniform<float> rng(-1.0f, 1.0f);
Scott Cyphers's avatar
Scott Cyphers committed
1121
    auto shape = Shape{2, 3};
1122 1123 1124
    auto x0 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x1 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
    auto x2 = rng.initialize(backend->make_primary_tensor_view<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
1125 1126

    auto make_graph = [shape]() {
1127 1128 1129
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
        auto X2 = make_shared<op::Parameter>(element::f32, shape);
1130 1131
        return make_shared<Function>((X0 + X1) * X2,
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
Scott Cyphers's avatar
Scott Cyphers committed
1132
    };
1133 1134
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(manager, backend, make_graph, {x0, x1, x2}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
1135
}
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152

TEST(${BACKEND_NAME}, backwards_reverse_3d_02)
{
    auto manager = runtime::Manager::get("${BACKEND_NAME}");
    auto backend = manager->allocate_backend();

    test::Uniform<float> rng(-1.0f, 1.0f);
    auto shape = Shape{2, 4, 5};
    auto x = rng.initialize(backend->make_primary_tensor_view(element::f32, shape));

    auto make_graph = [shape]() {
        auto X = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Reverse>(X, AxisSet{0, 2}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };
    EXPECT_TRUE(autodiff_numeric_compare<float>(manager, backend, make_graph, {x}, .01f, .01f));
}