autodiff.in.cpp 71 KB
Newer Older
1
//*****************************************************************************
2
// Copyright 2017-2019 Intel Corporation
3 4 5 6 7 8 9 10 11 12 13 14 15
//
// 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
// limitations under the License.
//*****************************************************************************
16 17

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

#include "gtest/gtest.h"

24 25 26 27
// clang-format off
#define AUTODIFF_BACKEND_${BACKEND_NAME}
// clang-format on

28
#include "ngraph/ngraph.hpp"
29
#include "ngraph/pass/manager.hpp"
30
#if defined(AUTODIFF_BACKEND_CPU)
31
#include "ngraph/runtime/cpu/op/batch_mat_mul_transpose.hpp"
32
#include "ngraph/runtime/cpu/pass/cpu_mat_fusion.hpp"
33
#endif
34
#include "ngraph/runtime/reference/avg_pool.hpp"
35
#include "util/autodiff/backprop_function.hpp"
adstraw's avatar
adstraw committed
36
#include "util/autodiff/numeric_compare.hpp"
37
#include "util/random.hpp"
38
#include "util/test_control.hpp"
39 40 41
using namespace std;
using namespace ngraph;

42 43 44
static string s_manifest = "${MANIFEST}";

NGRAPH_TEST(${BACKEND_NAME}, backwards_maxpool_n4_c1_hw4_2x2_max)
45
{
46
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
47

48
    Shape shape_a{1, 4, 4, 4}; // in CHWN
49
    Shape maxpool_shape{1, 4, 3, 3};
50 51 52

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

59
    shared_ptr<runtime::Tensor> ep = backend->create_tensor(element::i32, maxpool_shape);
60 61
    vector<int> dataEp(shape_size(maxpool_shape), 4);

62 63
    shared_ptr<runtime::Tensor> input = backend->create_tensor(element::i32, shape_a);
    shared_ptr<runtime::Tensor> output = backend->create_tensor(element::i32, shape_a);
64 65 66 67 68 69

    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};

70
    vector<int> expected{// delta
71 72 73 74 75 76 77 78 79
                         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);
80
    auto handle = backend->compile(df);
81
    handle->call_with_validate({output}, {input, ep});
82
    ASSERT_TRUE(read_vector<int>(output) == expected);
83 84
}

85
NGRAPH_TEST(${BACKEND_NAME}, backwards_maxpool_n2_c1_hw5_3x3_str2_max)
86
{
87
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
88

89
    Shape shape_a{1, 5, 5, 2}; // in CHWN
90
    Shape maxpool_shape{1, 2, 2, 2};
91 92 93

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

100
    shared_ptr<runtime::Tensor> ep = backend->create_tensor(element::i32, maxpool_shape);
101 102
    vector<int> dataEp(shape_size(maxpool_shape), 4);

103 104
    shared_ptr<runtime::Tensor> input = backend->create_tensor(element::i32, shape_a);
    shared_ptr<runtime::Tensor> output = backend->create_tensor(element::i32, shape_a);
105 106 107 108 109

    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};

110
    vector<int> expected{// delta
111 112 113 114 115 116 117 118
                         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);
119
    auto handle = backend->compile(df);
120
    handle->call_with_validate({output}, {input, ep});
121
    ASSERT_TRUE(read_vector<int>(output) == expected);
122 123
}

124 125 126 127
NGRAPH_TEST(${BACKEND_NAME}, backwards_maxpool_n2_c1_hw5_3x3_str2_max_pad1x2_2x3)
{
    auto backend = runtime::Backend::create("${BACKEND_NAME}");

128
    Shape shape_a{1, 5, 5, 2}; // in CHWN
129 130 131 132
    Shape maxpool_shape{1, 2, 4, 5};

    auto A = make_shared<op::Parameter>(element::f32, shape_a);
    auto reshape = make_shared<op::Reshape>(
133
        A, AxisVector{0, 3, 1, 2}, Shape{1, 2, 5, 5}); // convert CHWN to CNHW
134 135 136 137 138 139
    Shape window_shape{3, 3};
    auto window_movement_strides = Strides{2, 2};
    Shape pad_below{1, 2};
    Shape pad_above{3, 4};
    auto maxpool = make_shared<op::MaxPool>(
        reshape, window_shape, window_movement_strides, pad_below, pad_above);
140
    auto f = make_shared<Function>(maxpool, ParameterVector{A});
141

142
    shared_ptr<runtime::Tensor> ep = backend->create_tensor(element::f32, maxpool_shape);
143 144
    vector<float> dataEp(shape_size(maxpool_shape), 4);

145 146
    shared_ptr<runtime::Tensor> input = backend->create_tensor(element::f32, shape_a);
    shared_ptr<runtime::Tensor> output = backend->create_tensor(element::f32, shape_a);
147 148 149 150 151

    vector<float> 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};

152
    vector<float> expected{// delta
153 154 155 156 157 158 159 160
                           8, 0, 0, 0, 0, 4,  0, 0, 8, 0, 0, 8, 4, 8, 0, 0, 0,
                           0, 0, 4, 4, 0, 0,  0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                           0, 0, 0, 0, 4, 12, 4, 8, 4, 0, 0, 0, 0, 4, 8, 0};
    copy_data(ep, dataEp);
    copy_data(input, dataInput);

    auto C = make_shared<op::Parameter>(element::f32, maxpool_shape);
    auto df = autodiff::backprop_function(f);
161
    auto handle = backend->compile(df);
162
    handle->call_with_validate({output}, {input, ep});
163 164 165
    EXPECT_EQ(expected, read_vector<float>(output));
}

166
NGRAPH_TEST(${BACKEND_NAME}, backwards_avgpool_n1_c1_hw2x2)
167
{
168
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
169

170
    Shape padding{1, 1};
171

172 173
    Shape shape_a{1, 1, 2, 2};
    Shape avgpool_shape{1, 1, 2, 2};
174 175

    auto A = make_shared<op::Parameter>(element::i32, shape_a);
176
    Shape window_shape{2, 2};
177 178
    auto window_movement_strides = Strides{2, 2};
    auto avgpool =
179
        make_shared<op::AvgPool>(A, window_shape, window_movement_strides, padding, padding, false);
180
    auto f = make_shared<Function>(avgpool, ParameterVector{A});
181

182
    shared_ptr<runtime::Tensor> ep = backend->create_tensor(element::i32, avgpool_shape);
183 184
    vector<int> dataEp(shape_size(avgpool_shape), 4);

185
    shared_ptr<runtime::Tensor> input = backend->create_tensor(element::i32, shape_a);
186

187
    shared_ptr<runtime::Tensor> output = backend->create_tensor(element::i32, shape_a);
188 189 190 191 192 193 194 195 196 197

    vector<int> dataInput{4, 8, 12, 16};

    vector<int> expected{1, 2, 3, 4};

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

    auto C = make_shared<op::Parameter>(element::i32, avgpool_shape);
    auto df = autodiff::backprop_function(f);
198
    auto handle = backend->compile(df);
199
    handle->call_with_validate({output}, {input, ep});
200 201 202
    ASSERT_TRUE(read_vector<int>(output) == dataEp);
}

203
NGRAPH_TEST(${BACKEND_NAME}, backwards_avgpool_n1_c1_hw4x4)
204
{
205
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
206

207 208
    Shape shape_a{1, 1, 4, 4};
    Shape avgpool_shape{1, 1, 3, 3};
209 210

    auto A = make_shared<op::Parameter>(element::i32, shape_a);
211
    Shape window_shape{2, 2};
212 213
    auto window_movement_strides = Strides{1, 1};
    auto avgpool = make_shared<op::AvgPool>(A, window_shape, window_movement_strides);
214
    auto f = make_shared<Function>(avgpool, ParameterVector{A});
215

216
    shared_ptr<runtime::Tensor> ep = backend->create_tensor(element::i32, avgpool_shape);
217 218
    vector<int> dataEp(shape_size(avgpool_shape), 4);

219
    shared_ptr<runtime::Tensor> input = backend->create_tensor(element::i32, shape_a);
220

221
    shared_ptr<runtime::Tensor> output = backend->create_tensor(element::i32, shape_a);
222 223 224 225 226 227 228 229 230 231

    vector<int> dataInput{1, 3, 1, 3, 1, 3, 1, 3, 3, 5, 3, 5, 3, 5, 3, 5};

    vector<int> expected{1, 2, 2, 1, 2, 4, 4, 2, 2, 4, 4, 2, 1, 2, 2, 1};

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

    auto C = make_shared<op::Parameter>(element::i32, avgpool_shape);
    auto df = autodiff::backprop_function(f);
232
    auto handle = backend->compile(df);
233
    handle->call_with_validate({output}, {input, ep});
234 235 236
    ASSERT_TRUE(read_vector<int>(output) == expected);
}

237
NGRAPH_TEST(${BACKEND_NAME}, backwards_avgpool_n2_c2_hw4x4)
238
{
239
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
240

241 242
    Shape shape_a{2, 2, 4, 4};
    Shape avgpool_shape{2, 2, 2, 2};
243 244

    auto A = make_shared<op::Parameter>(element::i32, shape_a);
245
    Shape window_shape{2, 2};
246 247
    auto window_movement_strides = Strides{2, 2};
    auto avgpool = make_shared<op::AvgPool>(A, window_shape, window_movement_strides);
248
    auto f = make_shared<Function>(avgpool, ParameterVector{A});
249

250
    shared_ptr<runtime::Tensor> ep = backend->create_tensor(element::i32, avgpool_shape);
251 252
    vector<int> dataEp(shape_size(avgpool_shape), 12);

253
    shared_ptr<runtime::Tensor> input = backend->create_tensor(element::i32, shape_a);
254

255
    shared_ptr<runtime::Tensor> output = backend->create_tensor(element::i32, shape_a);
256

257
    vector<int> dataInput{// i1c1
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
                          1,
                          2,
                          6,
                          7,
                          3,
                          4,
                          4,
                          3,
                          19,
                          1,
                          2,
                          3,
                          18,
                          2,
                          3,
                          2,
274
                          // i1c2
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
                          4,
                          1,
                          5,
                          5,
                          1,
                          4,
                          5,
                          5,
                          12,
                          8,
                          2,
                          3,
                          15,
                          5,
                          3,
                          2,
291
                          // i2c1
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
                          2,
                          3,
                          7,
                          7,
                          3,
                          2,
                          3,
                          3,
                          13,
                          7,
                          1,
                          2,
                          7,
                          13,
                          3,
                          4,
308
                          // i2c2
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
                          1,
                          1,
                          2,
                          2,
                          7,
                          1,
                          2,
                          14,
                          6,
                          16,
                          4,
                          1,
                          14,
                          4,
                          4,
                          1};

    vector<int> expected(shape_size(shape_a), 3);
    copy_data(ep, dataEp);
    copy_data(input, dataInput);

    auto C = make_shared<op::Parameter>(element::i32, avgpool_shape);
    auto df = autodiff::backprop_function(f);
332
    auto handle = backend->compile(df);
333
    handle->call_with_validate({output}, {input, ep});
334 335 336
    ASSERT_TRUE(read_vector<int>(output) == expected);
}

337
NGRAPH_TEST(${BACKEND_NAME}, backwards_avgpool_n2_c2_hw4x4_numeric)
338
{
339
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
340
    Shape shape_a{2, 2, 4, 4};
341 342 343 344
    test::Uniform<float> rng(1.0f, 10.0f);

    auto make_graph = [shape_a]() {
        auto A = make_shared<op::Parameter>(element::f32, shape_a);
345
        Shape window_shape{2, 2};
346 347
        auto window_movement_strides = Strides{2, 2};
        auto avgpool = make_shared<op::AvgPool>(A, window_shape, window_movement_strides);
348
        return make_shared<Function>(avgpool, ParameterVector{A});
349 350 351

    };

352 353
    auto f = make_graph();
    auto g = make_graph();
354
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
355
    {
356
        auto x = rng.initialize(backend->create_tensor(element::f32, shape_a));
357
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x}, .01f, .01f));
358 359 360
    }
}

361
NGRAPH_TEST(${BACKEND_NAME}, backwards_avgpool_n2_c2_hw4x4_win_2x2_str_1x1_numeric)
362
{
363
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
364
    Shape shape_a{2, 2, 4, 4};
365 366 367 368
    test::Uniform<float> rng(1.0f, 10.0f);

    auto make_graph = [shape_a]() {
        auto A = make_shared<op::Parameter>(element::f32, shape_a);
369
        Shape window_shape{2, 2};
370 371
        auto window_movement_strides = Strides{1, 1};
        auto avgpool = make_shared<op::AvgPool>(A, window_shape, window_movement_strides);
372
        return make_shared<Function>(avgpool, ParameterVector{A});
373 374 375

    };

376 377
    auto f = make_graph();
    auto g = make_graph();
378
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
379
    {
380
        auto x = rng.initialize(backend->create_tensor(element::f32, shape_a));
381
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x}, .01f, .01f));
382 383 384
    }
}

385
NGRAPH_TEST(${BACKEND_NAME}, backwards_avgpool_n2_c2_hw2x2_win_2x2_str_1x1_padding_numeric)
386
{
387
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
388
    Shape shape_a{2, 2, 4, 4};
389 390 391 392
    test::Uniform<float> rng(1.0f, 10.0f);

    auto make_graph = [shape_a]() {
        auto A = make_shared<op::Parameter>(element::f32, shape_a);
393 394
        Shape window_shape{2, 2};
        Shape padding{1, 1};
395
        auto window_movement_strides = Strides{2, 2};
396 397
        auto avgpool = make_shared<op::AvgPool>(
            A, window_shape, window_movement_strides, padding, padding, false);
398
        return make_shared<Function>(avgpool, ParameterVector{A});
399 400 401

    };

402 403
    auto f = make_graph();
    auto g = make_graph();
404
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
405
    {
406
        auto x = rng.initialize(backend->create_tensor(element::f32, shape_a));
407
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x}, .01f, .01f));
408 409 410
    }
}

411
NGRAPH_TEST(${BACKEND_NAME}, backwards_abs)
412
{
413
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
414 415 416 417 418

    // 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);
419
    Shape shape{2, 3};
420 421

    auto make_graph = [shape]() {
422
        auto X = make_shared<op::Parameter>(element::f32, shape);
423 424
        return make_shared<Function>(make_shared<op::Abs>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
425 426
    };

427 428
    auto f = make_graph();
    auto g = make_graph();
429
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
430
    {
431
        auto x_neg = rng_neg.initialize(backend->create_tensor<float>(shape));
432

433
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_neg}, .01f, .01f));
434

435
        auto x_pos = rng_pos.initialize(backend->create_tensor<float>(shape));
436

437
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_pos}, .01f, .01f));
438
    }
439 440
}

441
NGRAPH_TEST(${BACKEND_NAME}, backwards_acos)
442 443 444 445 446 447 448 449 450 451 452 453
{
    auto backend = runtime::Backend::create("${BACKEND_NAME}");

    test::Uniform<float> rng(-0.9f, 0.9f);
    Shape shape{2, 3};
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));

    auto make_graph = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Acos>(X0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
454
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
455 456
}

457
NGRAPH_TEST(${BACKEND_NAME}, backwards_add)
458
{
459
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
460

461
    test::Uniform<float> rng(-1.0f, 1.0f);
462
    Shape shape{2, 3};
463 464
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
465

466
    auto make_graph = [shape]() {
467 468
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
469
        return make_shared<Function>(X0 + X1, std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
470
    };
471
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
472 473
}

474
NGRAPH_TEST(${BACKEND_NAME}, backwards_add_nested)
475
{
476
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
477

478
    test::Uniform<float> rng(-1.0f, 1.0f);
479
    Shape shape{2, 3};
480 481
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape));
482 483

    auto make_graph = [shape]() {
484 485
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
486 487
        return make_shared<Function>((X0 + X1) + (X1 + X0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
488
    };
489
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
490 491
}

492
NGRAPH_TEST(${BACKEND_NAME}, backwards_asin)
493 494 495 496 497 498 499 500 501 502 503 504
{
    auto backend = runtime::Backend::create("${BACKEND_NAME}");

    test::Uniform<float> rng(-0.9f, 0.9f);
    Shape shape{2, 3};
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));

    auto make_graph = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Asin>(X0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
505
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
506 507
}

508
NGRAPH_TEST(${BACKEND_NAME}, backwards_atan)
509 510 511 512 513 514 515 516 517 518 519 520
{
    auto backend = runtime::Backend::create("${BACKEND_NAME}");

    test::Uniform<float> rng(-10.0f, 10.0f);
    Shape shape{2, 3};
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));

    auto make_graph = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Atan>(X0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
521
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
522 523
}

524
NGRAPH_TEST(${BACKEND_NAME}, backwards_broadcast0)
525
{
526
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
527

528
    test::Uniform<float> rng(-1.0f, 1.0f);
529
    Shape shape{3};
530
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
531 532

    auto make_graph = [shape]() {
533
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
534 535 536
        return make_shared<Function>(make_shared<op::Broadcast>(X0, Shape{2, 3}, AxisSet{0}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
537
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
538 539
}

540
NGRAPH_TEST(${BACKEND_NAME}, backwards_broadcast1)
541
{
542
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
543

544
    test::Uniform<float> rng(-1.0f, 1.0f);
545
    Shape shape{3};
546
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
547 548

    auto make_graph = [shape]() {
549
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
550 551 552
        return make_shared<Function>(make_shared<op::Broadcast>(X0, Shape{3, 2}, AxisSet{1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
553
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
554 555
}

556
NGRAPH_TEST(${BACKEND_NAME}, backwards_concat_vector)
557
{
558
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
559 560

    test::Uniform<float> rng(-1.0f, 1.0f);
561
    Shape shape_0{3};
562
    auto x0 = rng.initialize(backend->create_tensor(element::f32, shape_0));
563
    Shape shape_1{2};
564
    auto x1 = rng.initialize(backend->create_tensor(element::f32, shape_1));
565
    Shape shape_2{1};
566
    auto x2 = rng.initialize(backend->create_tensor(element::f32, shape_2));
567 568

    auto make_graph = [shape_0, shape_1, shape_2]() {
569 570 571
        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);
572
        return make_shared<Function>(make_shared<op::Concat>(NodeVector{X0, X1, X2}, 0),
573 574
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };
575 576
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1, x2}, .01f, .01f));
577 578
}

579
NGRAPH_TEST(${BACKEND_NAME}, backwards_concat_axis_0)
580
{
581
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
582 583

    test::Uniform<float> rng(-1.0f, 1.0f);
584
    Shape shape_0{3, 2};
585
    auto x0 = rng.initialize(backend->create_tensor(element::f32, shape_0));
586
    Shape shape_1{2, 2};
587
    auto x1 = rng.initialize(backend->create_tensor(element::f32, shape_1));
588
    Shape shape_2{1, 2};
589
    auto x2 = rng.initialize(backend->create_tensor(element::f32, shape_2));
590 591

    auto make_graph = [shape_0, shape_1, shape_2]() {
592 593 594
        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);
595
        return make_shared<Function>(make_shared<op::Concat>(NodeVector{X0, X1, X2}, 0),
596 597
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };
598 599
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1, x2}, .01f, .01f));
600 601
}

602
NGRAPH_TEST(${BACKEND_NAME}, backwards_concat_axis_1)
603
{
604
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
605 606

    test::Uniform<float> rng(-1.0f, 1.0f);
607
    Shape shape_0{2, 3};
608
    auto x0 = rng.initialize(backend->create_tensor(element::f32, shape_0));
609
    Shape shape_1{2, 2};
610
    auto x1 = rng.initialize(backend->create_tensor(element::f32, shape_1));
611
    Shape shape_2{2, 1};
612
    auto x2 = rng.initialize(backend->create_tensor(element::f32, shape_2));
613 614

    auto make_graph = [shape_0, shape_1, shape_2]() {
615 616 617
        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);
618
        return make_shared<Function>(make_shared<op::Concat>(NodeVector{X0, X1, X2}, 1),
619 620
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };
621 622
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1, x2}, .01f, .01f));
623 624
}

625
NGRAPH_TEST(${BACKEND_NAME}, backwards_ceiling)
626
{
627
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
628 629 630 631 632 633

    // 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);
634
    Shape shape{2, 3};
635 636

    auto make_graph = [shape]() {
637
        auto X = make_shared<op::Parameter>(element::f32, shape);
638 639
        return make_shared<Function>(make_shared<op::Ceiling>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
640 641
    };

642 643
    auto f = make_graph();
    auto g = make_graph();
644
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
645
    {
646
        auto x_minusone = rng_minusone.initialize(backend->create_tensor<float>(shape));
647

648
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_minusone}, .01f, .01f));
649

650
        auto x_plusone = rng_plusone.initialize(backend->create_tensor<float>(shape));
651

652
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_plusone}, .01f, .01f));
653

654
        auto x_plustwo = rng_plustwo.initialize(backend->create_tensor<float>(shape));
655

656
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_plustwo}, .01f, .01f));
657 658 659
    }
}

660
NGRAPH_TEST(${BACKEND_NAME}, backwards_cos)
661
{
662
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
663 664

    test::Uniform<float> rng(-10.0f, 10.0f);
665
    Shape shape{2, 3};
666
    auto make_graph = [shape]() {
667
        auto X = make_shared<op::Parameter>(element::f32, shape);
668 669
        return make_shared<Function>(make_shared<op::Cos>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
670 671
    };

672 673
    auto f = make_graph();
    auto g = make_graph();
674
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
675
    {
676
        auto x = rng.initialize(backend->create_tensor<float>(shape));
677

678
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x}, .01f, .01f));
679 680 681
    }
}

682
NGRAPH_TEST(${BACKEND_NAME}, backwards_cosh)
683
{
684
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
685 686

    test::Uniform<float> rng(-10.0f, 10.0f);
687
    Shape shape{2, 3};
688
    auto make_graph = [shape]() {
689
        auto X = make_shared<op::Parameter>(element::f32, shape);
690 691
        return make_shared<Function>(make_shared<op::Cosh>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
692 693
    };

694 695
    auto f = make_graph();
    auto g = make_graph();
696
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
697
    {
698
        auto x = rng.initialize(backend->create_tensor<float>(shape));
699

700
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x}, .01f, .01f));
701 702 703
    }
}

704
NGRAPH_TEST(${BACKEND_NAME}, backwards_divide)
705
{
706
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
707

708 709 710
    test::Uniform<float> rng(-1.0f, 1.0f);
    test::Uniform<float> rng1(1.0f, 2.0f);
    test::Uniform<float> rng2(-2.0f, -1.0f);
711
    Shape shape{2, 3};
712 713 714
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng1.initialize(backend->create_tensor<float>(shape));
    auto x2 = rng2.initialize(backend->create_tensor<float>(shape));
715

716
    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
        return make_shared<Function>(X0 / X1, std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
720
    };
721 722
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x2}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
723
}
724

725
NGRAPH_TEST(${BACKEND_NAME}, backwards_dot_scalar_scalar)
726
{
727
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
728

729
    test::Uniform<float> rng(-1.0f, 1.0f);
730 731
    Shape shape0{};
    Shape shape1{};
732 733
    auto x0 = rng.initialize(backend->create_tensor<float>(shape0));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape1));
734 735

    auto make_graph = [shape0, shape1]() {
736 737
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
738 739 740
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
741
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
742 743
}

744
NGRAPH_TEST(${BACKEND_NAME}, backwards_dot_scalar_tensor)
745
{
746
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
747

748
    test::Uniform<float> rng(-1.0f, 1.0f);
749 750
    Shape shape0{};
    Shape shape1{3, 4};
751 752
    auto x0 = rng.initialize(backend->create_tensor<float>(shape0));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape1));
753 754

    auto make_graph = [shape0, shape1]() {
755 756
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
757 758 759
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
760
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
761 762
}

763
NGRAPH_TEST(${BACKEND_NAME}, backwards_dot_tensor_scalar)
764
{
765
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
766

767
    test::Uniform<float> rng(-1.0f, 1.0f);
768 769
    Shape shape0{3, 4};
    Shape shape1{};
770 771
    auto x0 = rng.initialize(backend->create_tensor<float>(shape0));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape1));
772 773

    auto make_graph = [shape0, shape1]() {
774 775
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
776 777 778
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
779
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
780 781
}

782
NGRAPH_TEST(${BACKEND_NAME}, backwards_dot_vector_vector)
783
{
784
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
785

786
    test::Uniform<float> rng(-1.0f, 1.0f);
787 788
    Shape shape0{3};
    Shape shape1{3};
789 790
    auto x0 = rng.initialize(backend->create_tensor<float>(shape0));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape1));
791 792

    auto make_graph = [shape0, shape1]() {
793 794
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
795 796 797
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
798
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
799 800
}

801
NGRAPH_TEST(${BACKEND_NAME}, backwards_dot_tensor_vector)
802
{
803
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
804

805
    test::Uniform<float> rng(-1.0f, 1.0f);
806 807
    Shape shape0{4, 3};
    Shape shape1{3};
808 809
    auto x0 = rng.initialize(backend->create_tensor<float>(shape0));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape1));
810 811

    auto make_graph = [shape0, shape1]() {
812 813
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
814 815 816
        return make_shared<Function>(make_shared<op::Dot>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
817
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
818 819
}

820
NGRAPH_TEST(${BACKEND_NAME}, backwards_dot_tensor2_tensor2)
821
{
822
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
823

824
    test::Uniform<float> rng(-1.0f, 1.0f);
825 826
    Shape shape0{4, 3};
    Shape shape1{3, 5};
827 828
    auto x0 = rng.initialize(backend->create_tensor<float>(shape0));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape1));
829 830

    auto make_graph = [shape0, shape1]() {
831 832
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
833 834
        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
835
    };
836
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
Adam Procter's avatar
Adam Procter committed
837 838
}

839
NGRAPH_TEST(${BACKEND_NAME}, backwards_dot_tensor3_tensor3)
Adam Procter's avatar
Adam Procter committed
840
{
841
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
Adam Procter's avatar
Adam Procter committed
842 843

    test::Uniform<float> rng(-1.0f, 1.0f);
844 845
    Shape shape0{2, 4, 3};
    Shape shape1{4, 3, 3};
846 847
    auto x0 = rng.initialize(backend->create_tensor<float>(shape0));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape1));
Adam Procter's avatar
Adam Procter committed
848 849

    auto make_graph = [shape0, shape1]() {
850 851
        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
852 853
        return make_shared<Function>(make_shared<op::Dot>(X0, X1, 2),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
854
    };
855
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
856 857
}

858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
#if defined(AUTODIFF_BACKEND_CPU) || defined(AUTODIFF_BACKEND_INTERPRETER)
// XXX lfeng: remove backend check once all backends support this
NGRAPH_TEST(${BACKEND_NAME}, backwards_batchmatmul_tensor2_tensor2)
{
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
    test::Uniform<float> rng(-1.0f, 1.0f);
    Shape shape0{3, 4, 5};
    Shape shape1{3, 5, 6};
    auto x0 = rng.initialize(backend->create_tensor<float>(shape0));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape1));

    auto make_graph = [shape0, shape1]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape0);
        auto X1 = make_shared<op::Parameter>(element::f32, shape1);
        return make_shared<Function>(make_shared<op::BatchMatMul>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };

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

880
#if defined(AUTODIFF_BACKEND_CPU) && defined(NGRAPH_JSON_ENABLE)
881
NGRAPH_TEST(${BACKEND_NAME}, backwards_batchmatmultranspose_tensor2_tensor2)
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
{
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
    std::string backend_name = "${BACKEND_NAME}";

    const std::string file_name("mxnet/batch_dot_3.json");
    auto f = make_function_from_file(file_name);

    test::Uniform<float> rng(-1.0f, 1.0f);
    std::vector<std::shared_ptr<ngraph::runtime::Tensor>> args;
    for (shared_ptr<op::Parameter> param : f->get_parameters())
    {
        args.push_back(rng.initialize(backend->create_tensor<float>(param->get_shape())));
    }

    auto g = make_function_from_file(file_name);
    pass::Manager pass_manager;
    pass_manager.register_pass<runtime::cpu::pass::CPUBatchFusion>();
    pass_manager.run_passes(g);
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, args, .01f, .01f));
}
#endif

904
NGRAPH_TEST(${BACKEND_NAME}, backwards_exp)
Scott Cyphers's avatar
Scott Cyphers committed
905
{
906
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
907

908
    test::Uniform<float> rng(-1.0f, 1.0f);
909
    Shape shape{2, 3};
910
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
911 912

    auto make_graph = [shape]() {
913
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
914 915
        return make_shared<Function>(make_shared<op::Exp>(X0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
Scott Cyphers's avatar
Scott Cyphers committed
916
    };
917
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
918 919
}

920
NGRAPH_TEST(${BACKEND_NAME}, backwards_floor)
921
{
922
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
923 924 925 926 927 928

    // 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);
929
    Shape shape{2, 3};
930 931

    auto make_graph = [shape]() {
932
        auto X = make_shared<op::Parameter>(element::f32, shape);
933 934
        return make_shared<Function>(make_shared<op::Floor>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
935 936
    };

937 938
    auto f = make_graph();
    auto g = make_graph();
939
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
940
    {
941
        auto x_minusone = rng_minusone.initialize(backend->create_tensor<float>(shape));
942

943
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_minusone}, .01f, .01f));
944

945
        auto x_plusone = rng_plusone.initialize(backend->create_tensor<float>(shape));
946

947
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_plusone}, .01f, .01f));
948

949
        auto x_plustwo = rng_plustwo.initialize(backend->create_tensor<float>(shape));
950

951
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_plustwo}, .01f, .01f));
952 953 954
    }
}

955
NGRAPH_TEST(${BACKEND_NAME}, backwards_log)
Scott Cyphers's avatar
Scott Cyphers committed
956
{
957
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
958

959
    test::Uniform<float> rng(1.0f, 2.0f);
960
    Shape shape{2, 3};
961
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
962 963

    auto make_graph = [shape]() {
964
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
965 966
        return make_shared<Function>(make_shared<op::Log>(X0),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
Scott Cyphers's avatar
Scott Cyphers committed
967
    };
968
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
969 970
}

971
NGRAPH_TEST(${BACKEND_NAME}, backwards_maximum)
972
{
973
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
974

975
    test::Uniform<float> rng(-1.0f, 1.0f);
976
    Shape shape{2, 3};
977 978
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape));
979 980

    auto make_graph = [shape]() {
981 982
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
983 984 985
        return make_shared<Function>(make_shared<op::Maximum>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
986
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
987 988
}

989
NGRAPH_TEST(${BACKEND_NAME}, backwards_minimum)
990
{
991
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
992

993
    test::Uniform<float> rng(-1.0f, 1.0f);
994
    Shape shape{2, 3};
995 996
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape));
997 998

    auto make_graph = [shape]() {
999 1000
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
1001 1002 1003
        return make_shared<Function>(make_shared<op::Minimum>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };
1004
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
1005 1006
}

1007
NGRAPH_TEST(${BACKEND_NAME}, backwards_multiply)
1008
{
1009
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1010

1011
    test::Uniform<float> rng(-1.0f, 1.0f);
1012
    Shape shape{2, 3};
1013 1014
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape));
1015

1016
    auto make_graph = [shape]() {
1017 1018
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
1019
        return make_shared<Function>(X0 * X1, std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
1020
    };
1021
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
1022
}
Scott Cyphers's avatar
Scott Cyphers committed
1023

1024
NGRAPH_TEST(${BACKEND_NAME}, backwards_negative)
Scott Cyphers's avatar
Scott Cyphers committed
1025
{
1026
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
1027

1028
    test::Uniform<float> rng(-1.0f, 1.0f);
1029
    Shape shape{2, 3};
1030
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
1031 1032

    auto make_graph = [shape]() {
1033
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
1034
        return make_shared<Function>(-X0, std::vector<std::shared_ptr<op::Parameter>>{X0});
Scott Cyphers's avatar
Scott Cyphers committed
1035
    };
1036
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
1037 1038
}

1039
NGRAPH_TEST(${BACKEND_NAME}, backwards_parameter)
Scott Cyphers's avatar
Scott Cyphers committed
1040
{
1041
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
1042

1043
    test::Uniform<float> rng(-1.0f, 1.0f);
1044
    Shape shape{2, 3};
1045
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
1046
    auto make_graph = [shape]() {
1047
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
1048
        return make_shared<Function>(X0, std::vector<std::shared_ptr<op::Parameter>>{X0});
Scott Cyphers's avatar
Scott Cyphers committed
1049
    };
1050
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
1051 1052
}

1053
NGRAPH_TEST(${BACKEND_NAME}, backwards_power)
1054
{
1055
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1056 1057 1058

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

    auto make_graph = [shape]() {
1062 1063
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
1064 1065 1066 1067
        return make_shared<Function>(std::make_shared<op::Power>(X0, X1),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
    };

1068 1069
    auto x0 = rng_pos.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng_neg.initialize(backend->create_tensor<float>(shape));
1070

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

1073 1074
    x0 = rng_pos.initialize(backend->create_tensor<float>(shape));
    x1 = rng_pos.initialize(backend->create_tensor<float>(shape));
1075

1076
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
1077 1078
}

1079
NGRAPH_TEST(${BACKEND_NAME}, backwards_relu)
1080
{
1081
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1082 1083 1084 1085

    test::Uniform<float> rng_neg(-1.0f, -0.01f);
    test::Uniform<float> rng_pos(0.01f, 1.0f);
    Shape shape{2, 3};
1086 1087
    auto x0 = rng_neg.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng_pos.initialize(backend->create_tensor<float>(shape));
1088 1089 1090 1091 1092 1093 1094

    auto make_graph = [shape]() {
        auto X = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Relu>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };

1095 1096
    auto f = make_graph();
    auto g = make_graph();
1097 1098
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
    {
1099
        auto x_neg = rng_neg.initialize(backend->create_tensor<float>(shape));
1100

1101
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_neg}, .01f, .01f));
1102

1103
        auto x_pos = rng_pos.initialize(backend->create_tensor<float>(shape));
1104

1105
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_pos}, .01f, .01f));
1106 1107 1108
    }
}

1109
NGRAPH_TEST(${BACKEND_NAME}, backwards_replace_slice)
1110
{
1111
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1112 1113

    test::Uniform<float> rng(-10.0f, 10.0f);
1114 1115
    Shape shape_x{5, 5};
    Shape shape_y{2, 2};
1116
    auto make_graph = [shape_x, shape_y]() {
1117 1118
        auto X = make_shared<op::Parameter>(element::f32, shape_x);
        auto Y = make_shared<op::Parameter>(element::f32, shape_y);
1119 1120 1121 1122 1123
        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});
    };

1124 1125
    auto f = make_graph();
    auto g = make_graph();
1126
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
1127
    {
1128 1129
        auto x = rng.initialize(backend->create_tensor<float>(shape_x));
        auto y = rng.initialize(backend->create_tensor<float>(shape_y));
1130

1131
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x, y}, .01f, .01f));
1132 1133 1134
    }
}

1135
NGRAPH_TEST(${BACKEND_NAME}, backwards_reshape)
1136
{
1137
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1138

1139
    test::Uniform<float> rng(-1.0f, 1.0f);
1140
    Shape shape{3, 4};
1141
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
1142 1143

    auto make_graph = [shape]() {
1144
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
1145 1146 1147
        return make_shared<Function>(make_shared<op::Reshape>(X0, AxisVector{1, 0}, Shape{4, 3}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1148
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
1149 1150
}

1151
NGRAPH_TEST(${BACKEND_NAME}, backwards_select)
1152
{
1153
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1154 1155

    test::Uniform<float> rng(-10.0f, 10.0f);
1156
    Shape shape{2, 3};
1157
    auto make_graph = [shape]() {
1158 1159 1160
        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);
1161 1162 1163 1164
        return make_shared<Function>(make_shared<op::Select>(X0, X1, X2),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };

1165 1166
    auto f = make_graph();
    auto g = make_graph();
1167
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
1168
    {
1169
        auto x0 = backend->create_tensor(element::boolean, shape);
1170
        write_vector(x0, vector<char>{0, 1, 0, 1, 0, 1});
1171 1172 1173 1174
        auto x1 = rng.initialize(backend->create_tensor<float>(shape));
        auto x2 = rng.initialize(backend->create_tensor<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare_selective<float>(
1175
            backend.get(), f, g, {x0, x1, x2}, .01f, .01f, std::vector<bool>{false, true, true}));
1176 1177 1178
    }
}

1179
NGRAPH_TEST(${BACKEND_NAME}, backwards_select_nested)
1180
{
1181
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1182 1183

    test::Uniform<float> rng(-10.0f, 10.0f);
1184
    Shape shape{2, 3};
1185
    auto make_graph = [shape]() {
1186 1187 1188
        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);
1189
        return make_shared<Function>(make_shared<op::Select>(X0, X2 + X1, X2 - X1),
1190 1191 1192
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
    };

1193 1194
    auto f = make_graph();
    auto g = make_graph();
1195
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
1196
    {
1197
        auto x0 = backend->create_tensor(element::boolean, shape);
1198
        write_vector(x0, vector<char>{0, 1, 0, 1, 0, 1});
1199 1200 1201 1202
        auto x1 = rng.initialize(backend->create_tensor<float>(shape));
        auto x2 = rng.initialize(backend->create_tensor<float>(shape));

        EXPECT_TRUE(autodiff_numeric_compare_selective<float>(
1203
            backend.get(), f, g, {x0, x1, x2}, .01f, .01f, std::vector<bool>{false, true, true}));
1204 1205 1206
    }
}

1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
NGRAPH_TEST(${BACKEND_NAME}, backwards_sigmoid)
{
    auto backend = runtime::Backend::create("${BACKEND_NAME}");

    test::Uniform<float> rng_neg(-1.0f, -0.01f);
    test::Uniform<float> rng_pos(0.01f, 1.0f);
    Shape shape{2, 3};
    auto x0 = rng_neg.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng_pos.initialize(backend->create_tensor<float>(shape));

    auto make_graph = [shape]() {
        auto X = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Sigmoid>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };

1223 1224
    auto f = make_graph();
    auto g = make_graph();
1225 1226 1227 1228
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
    {
        auto x_neg = rng_neg.initialize(backend->create_tensor<float>(shape));

1229
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_neg}, .01f, .01f));
1230 1231 1232

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

1233
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_pos}, .01f, .01f));
1234 1235 1236
    }
}

1237
NGRAPH_TEST(${BACKEND_NAME}, backwards_sign)
1238
{
1239
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1240 1241 1242 1243 1244

    // 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);
1245
    Shape shape{2, 3};
1246 1247

    auto make_graph = [shape]() {
1248
        auto X = make_shared<op::Parameter>(element::f32, shape);
1249 1250
        return make_shared<Function>(make_shared<op::Sign>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
1251 1252
    };

1253 1254
    auto f = make_graph();
    auto g = make_graph();
1255
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
1256
    {
1257
        auto x_neg = rng_neg.initialize(backend->create_tensor<float>(shape));
1258

1259
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_neg}, .01f, .01f));
1260

1261
        auto x_pos = rng_pos.initialize(backend->create_tensor<float>(shape));
1262

1263
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_pos}, .01f, .01f));
1264 1265 1266
    }
}

1267
NGRAPH_TEST(${BACKEND_NAME}, backwards_sin)
1268
{
1269
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1270 1271

    test::Uniform<float> rng(-10.0f, 10.0f);
1272
    Shape shape{2, 3};
1273
    auto make_graph = [shape]() {
1274
        auto X = make_shared<op::Parameter>(element::f32, shape);
1275 1276
        return make_shared<Function>(make_shared<op::Sin>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
1277 1278
    };

1279 1280
    auto f = make_graph();
    auto g = make_graph();
1281
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
1282
    {
1283
        auto x = rng.initialize(backend->create_tensor<float>(shape));
1284

1285
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x}, .01f, .01f));
1286 1287 1288
    }
}

1289
NGRAPH_TEST(${BACKEND_NAME}, backwards_sinh)
1290
{
1291
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1292 1293

    test::Uniform<float> rng(-10.0f, 10.0f);
1294
    Shape shape{2, 3};
1295
    auto make_graph = [shape]() {
1296
        auto X = make_shared<op::Parameter>(element::f32, shape);
1297 1298
        return make_shared<Function>(make_shared<op::Sinh>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
1299 1300
    };

1301 1302
    auto f = make_graph();
    auto g = make_graph();
1303
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
1304
    {
1305
        auto x = rng.initialize(backend->create_tensor<float>(shape));
1306

1307
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x}, .01f, .01f));
1308 1309 1310
    }
}

1311
NGRAPH_TEST(${BACKEND_NAME}, backwards_slice)
1312
{
1313
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1314
    test::Uniform<float> rng(-10.0f, 10.0f);
1315
    Shape shape{5, 5};
1316
    auto make_graph = [shape]() {
1317
        auto X = make_shared<op::Parameter>(element::f32, shape);
1318 1319 1320 1321
        return make_shared<Function>(make_shared<op::Slice>(X, Coordinate{2, 3}, Coordinate{4, 5}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };

1322 1323
    auto f = make_graph();
    auto g = make_graph();
1324
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
1325
    {
1326
        auto x = rng.initialize(backend->create_tensor<float>(shape));
1327

1328
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x}, .01f, .01f));
1329 1330 1331
    }
}

1332
NGRAPH_TEST(${BACKEND_NAME}, backwards_softmax_all)
1333
{
1334
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1335

adstraw's avatar
adstraw committed
1336
    test::Uniform<float> rng(-1.0f, 1.0f);
1337
    Shape shape{2, 3};
1338
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
adstraw's avatar
adstraw committed
1339

1340
    auto make_graph = [shape]() {
adstraw's avatar
adstraw committed
1341 1342 1343
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{0, 1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
1344
    };
1345
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
adstraw's avatar
adstraw committed
1346
}
1347

1348
NGRAPH_TEST(${BACKEND_NAME}, backwards_softmax_axis)
adstraw's avatar
adstraw committed
1349
{
1350
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1351

adstraw's avatar
adstraw committed
1352 1353
    test::Uniform<float> rng(-1.0f, 1.0f);
    Shape shape{2, 3};
1354
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
adstraw's avatar
adstraw committed
1355 1356 1357 1358 1359 1360

    auto make_graph = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1361
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
adstraw's avatar
adstraw committed
1362 1363
}

1364
NGRAPH_TEST(${BACKEND_NAME}, backwards_softmax_underflow)
adstraw's avatar
adstraw committed
1365
{
1366
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
adstraw's avatar
adstraw committed
1367 1368 1369 1370

    auto low = std::numeric_limits<float>::lowest();

    Shape shape{2, 3};
1371
    auto x0 = backend->create_tensor(element::f32, shape);
adstraw's avatar
adstraw committed
1372 1373 1374 1375 1376 1377 1378
    copy_data(x0, vector<float>{low, 1, 2, 3, 4, 5});

    auto make_graph = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{0, 1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1379
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0}, .01f, .01f));
adstraw's avatar
adstraw committed
1380 1381
}

1382
NGRAPH_TEST(${BACKEND_NAME}, backwards_softmax_3d)
adstraw's avatar
adstraw committed
1383
{
1384
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
adstraw's avatar
adstraw committed
1385 1386 1387

    test::Uniform<float> rng(-1.0f, 1.0f);
    Shape shape{2, 3, 4};
1388
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
adstraw's avatar
adstraw committed
1389 1390 1391 1392 1393 1394

    auto make_graph0 = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{0}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1395
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph0, {x0}, .01f, .01f));
adstraw's avatar
adstraw committed
1396 1397 1398 1399 1400 1401

    auto make_graph1 = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1402
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph1, {x0}, .01f, .01f));
adstraw's avatar
adstraw committed
1403 1404 1405 1406 1407 1408

    auto make_graph2 = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{2}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1409
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph2, {x0}, .01f, .01f));
adstraw's avatar
adstraw committed
1410 1411 1412 1413 1414 1415

    auto make_graph01 = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{0, 1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1416
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph01, {x0}, .01f, .01f));
adstraw's avatar
adstraw committed
1417 1418 1419 1420 1421 1422

    auto make_graph02 = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{0, 2}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1423
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph02, {x0}, .01f, .01f));
adstraw's avatar
adstraw committed
1424 1425 1426 1427 1428 1429

    auto make_graph12 = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{1, 2}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1430
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph12, {x0}, .01f, .01f));
adstraw's avatar
adstraw committed
1431 1432 1433 1434 1435 1436

    auto make_graph012 = [shape]() {
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        return make_shared<Function>(make_shared<op::Softmax>(X0, AxisSet{0, 1, 2}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X0});
    };
1437
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph012, {x0}, .01f, .01f));
1438 1439
}

1440
NGRAPH_TEST(${BACKEND_NAME}, backwards_subtract)
Scott Cyphers's avatar
Scott Cyphers committed
1441
{
1442
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
1443

1444
    test::Uniform<float> rng(-1.0f, 1.0f);
1445
    Shape shape{2, 3};
1446 1447
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
1448 1449

    auto make_graph = [shape]() {
1450 1451
        auto X0 = make_shared<op::Parameter>(element::f32, shape);
        auto X1 = make_shared<op::Parameter>(element::f32, shape);
1452
        return make_shared<Function>(X0 - X1, std::vector<std::shared_ptr<op::Parameter>>{X0, X1});
Scott Cyphers's avatar
Scott Cyphers committed
1453
    };
1454
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
1455
}
Scott Cyphers's avatar
Scott Cyphers committed
1456

1457
NGRAPH_TEST(${BACKEND_NAME}, backwards_sum_v2s)
1458
{
1459
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1460 1461

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

    auto make_graph = [shape]() {
1466
        auto X = make_shared<op::Parameter>(element::f32, shape);
1467 1468 1469
        return make_shared<Function>(make_shared<op::Sum>(X, AxisSet{0}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };
1470
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x}, .01f, .01f));
1471 1472
}

1473
NGRAPH_TEST(${BACKEND_NAME}, backwards_sum_m2s)
1474
{
1475
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1476 1477

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

    auto make_graph = [shape]() {
1482
        auto X = make_shared<op::Parameter>(element::f32, shape);
1483 1484 1485
        return make_shared<Function>(make_shared<op::Sum>(X, AxisSet{0, 1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };
1486
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x}, .01f, .01f));
1487 1488
}

1489
NGRAPH_TEST(${BACKEND_NAME}, backwards_sum_m2v_0)
1490
{
1491
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1492 1493

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

    auto make_graph = [shape]() {
1498
        auto X = make_shared<op::Parameter>(element::f32, shape);
1499 1500 1501
        return make_shared<Function>(make_shared<op::Sum>(X, AxisSet{0}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };
1502
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x}, .01f, .01f));
1503 1504
}

1505
NGRAPH_TEST(${BACKEND_NAME}, backwards_sum_m2v_1)
1506
{
1507
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1508 1509

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

    auto make_graph = [shape]() {
1514
        auto X = make_shared<op::Parameter>(element::f32, shape);
1515 1516 1517
        return make_shared<Function>(make_shared<op::Sum>(X, AxisSet{1}),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
    };
1518
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x}, .01f, .01f));
1519 1520
}

1521
NGRAPH_TEST(${BACKEND_NAME}, backwards_tan)
1522
{
1523
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1524 1525 1526 1527

    auto pi = 3.14159f;

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

1532
    Shape shape{2, 3};
1533 1534

    auto make_graph = [shape]() {
1535
        auto X = make_shared<op::Parameter>(element::f32, shape);
1536 1537
        return make_shared<Function>(make_shared<op::Tan>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
1538 1539
    };

1540 1541
    auto f = make_graph();
    auto g = make_graph();
1542
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
1543
    {
1544
        auto x_r = rng_r.initialize(backend->create_tensor<float>(shape));
1545

1546
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_r}, .01f, .01f));
1547

1548
        auto x_l = rng_l.initialize(backend->create_tensor<float>(shape));
1549

1550
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x_l}, .01f, .01f));
1551 1552 1553
    }
}

1554
NGRAPH_TEST(${BACKEND_NAME}, backwards_tanh)
1555
{
1556
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1557 1558

    test::Uniform<float> rng(-10.0f, 10.0f);
1559
    Shape shape{2, 3};
1560
    auto make_graph = [shape]() {
1561
        auto X = make_shared<op::Parameter>(element::f32, shape);
1562 1563
        return make_shared<Function>(make_shared<op::Tanh>(X),
                                     std::vector<std::shared_ptr<op::Parameter>>{X});
1564 1565
    };

1566 1567
    auto f = make_graph();
    auto g = make_graph();
1568
    for (auto i = 0; i < ${TEST_LOOPS}; i++)
1569
    {
1570
        auto x = rng.initialize(backend->create_tensor<float>(shape));
1571

1572
        EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), f, g, {x}, .01f, .01f));
1573 1574 1575
    }
}

1576
NGRAPH_TEST(${BACKEND_NAME}, backwards_abc)
Scott Cyphers's avatar
Scott Cyphers committed
1577
{
1578
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
Scott Cyphers's avatar
Scott Cyphers committed
1579

1580
    test::Uniform<float> rng(-1.0f, 1.0f);
1581
    Shape shape{2, 3};
1582 1583 1584
    auto x0 = rng.initialize(backend->create_tensor<float>(shape));
    auto x1 = rng.initialize(backend->create_tensor<float>(shape));
    auto x2 = rng.initialize(backend->create_tensor<float>(shape));
Scott Cyphers's avatar
Scott Cyphers committed
1585 1586

    auto make_graph = [shape]() {
1587 1588 1589
        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);
1590 1591
        return make_shared<Function>((X0 + X1) * X2,
                                     std::vector<std::shared_ptr<op::Parameter>>{X0, X1, X2});
Scott Cyphers's avatar
Scott Cyphers committed
1592
    };
1593

1594 1595
    EXPECT_TRUE(
        autodiff_numeric_compare<float>(backend.get(), make_graph, {x0, x1, x2}, .01f, .01f));
Scott Cyphers's avatar
Scott Cyphers committed
1596
}
1597

1598
NGRAPH_TEST(${BACKEND_NAME}, backwards_reverse_3d_02)
1599
{
1600
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1601 1602

    test::Uniform<float> rng(-1.0f, 1.0f);
1603
    Shape shape{2, 4, 5};
1604
    auto x = rng.initialize(backend->create_tensor(element::f32, shape));
1605 1606 1607 1608 1609 1610

    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});
    };
1611
    EXPECT_TRUE(autodiff_numeric_compare<float>(backend.get(), make_graph, {x}, .01f, .01f));
1612
}
1613

1614
NGRAPH_TEST(${BACKEND_NAME}, backwards_maxpool_n4c1h4w4_kh2kw2_sh1sw1)
1615
{
1616
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1617
    Shape shape_a{4, 1, 4, 4}; // in NCHW
1618 1619 1620 1621 1622
    Shape maxpool_shape{4, 1, 3, 3};

    auto A = make_shared<op::Parameter>(element::f32, shape_a);
    Shape window_shape{2, 2};
    auto window_movement_strides = Strides{1, 1};
1623
    auto maxpool = make_shared<op::MaxPool>(A, window_shape, window_movement_strides);
1624
    auto f = make_shared<Function>(maxpool, ParameterVector{A});
1625
    shared_ptr<runtime::Tensor> ep = backend->create_tensor(element::f32, maxpool_shape);
1626 1627
    vector<float> dataEp(shape_size(maxpool_shape), 4);

1628 1629
    shared_ptr<runtime::Tensor> input = backend->create_tensor(element::f32, shape_a);
    shared_ptr<runtime::Tensor> output = backend->create_tensor(element::f32, shape_a);
1630 1631

    vector<float> dataInput{11, 65, 44, 28, 31, 33, 21, 66, 40, 49, 69, 57, 47, 30, 24, 27,
pthoreho's avatar
pthoreho committed
1632 1633 1634
                            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};
1635

1636
    vector<float> expected{// delta
1637 1638 1639
                           0, 8, 0, 0, 0, 0, 0, 4, 0, 8, 16, 0, 0, 0, 0,  0, 0, 4, 0, 4, 8,  0,
                           0, 0, 0, 4, 4, 0, 4, 4, 0, 4, 0,  0, 8, 0, 4,  0, 0, 0, 8, 0, 16, 0,
                           0, 0, 0, 0, 0, 8, 0, 0, 4, 0, 4,  0, 4, 0, 16, 0, 0, 0, 0, 0};
1640 1641 1642 1643 1644 1645

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

    auto C = make_shared<op::Parameter>(element::f32, maxpool_shape);
    auto df = autodiff::backprop_function(f);
1646
    auto handle = backend->compile(df);
1647
    handle->call_with_validate({output}, {input, ep});
1648 1649 1650
    ASSERT_TRUE(read_vector<float>(output) == expected);
}

1651
NGRAPH_TEST(${BACKEND_NAME}, backwards_maxpool_n2c1h5w5_kh3kw3_sh2sw2)
1652
{
1653
    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1654

1655
    Shape shape_a{1, 2, 5, 5}; // in NCHW
1656 1657 1658 1659 1660
    Shape maxpool_shape{1, 2, 2, 2};

    auto A = make_shared<op::Parameter>(element::f32, shape_a);
    Shape window_shape{3, 3};
    auto window_movement_strides = Strides{2, 2};
1661
    auto maxpool = make_shared<op::MaxPool>(A, window_shape, window_movement_strides);
1662
    auto f = make_shared<Function>(maxpool, ParameterVector{A});
1663

1664
    shared_ptr<runtime::Tensor> ep = backend->create_tensor(element::f32, maxpool_shape);
1665 1666
    vector<float> dataEp(shape_size(maxpool_shape), 4);

1667 1668
    shared_ptr<runtime::Tensor> input = backend->create_tensor(element::f32, shape_a);
    shared_ptr<runtime::Tensor> output = backend->create_tensor(element::f32, shape_a);
1669 1670

    vector<float> dataInput{58, 15, 51, 35, 18, 47, 31, 32, 52, 21, 36, 38, 57, 54, 25, 45, 23,
pthoreho's avatar
pthoreho committed
1671 1672
                            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};
1673

1674
    vector<float> expected{// delta
1675 1676 1677
                           4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 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, 0, 4, 0, 0, 0, 0, 0, 0, 0,  4, 4, 0};
1678 1679 1680 1681 1682 1683

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

    auto C = make_shared<op::Parameter>(element::f32, maxpool_shape);
    auto df = autodiff::backprop_function(f);
1684
    auto handle = backend->compile(df);
1685
    handle->call_with_validate({output}, {input, ep});
1686
    ASSERT_TRUE(read_vector<float>(output) == expected);
1687
}
1688

1689
NGRAPH_TEST(${BACKEND_NAME}, backwards_batch_norm_training)
1690
{
1691
    const Shape input_shape{10, 4, 5, 5};
1692 1693
    const Shape channel_shape{input_shape.at(1)};
    const double eps = 1e-3;
1694

1695 1696
    // Need to keep the output elements for mean and variance from going out of scope
    // and getting freed.
1697 1698
    NodeVector goes;

1699 1700
    auto make_graph = [&input_shape, &channel_shape, &eps, &goes] {
        const element::Type& et = element::f32;
1701 1702 1703 1704 1705 1706 1707 1708 1709 1710
        auto input = make_shared<op::Parameter>(et, input_shape);
        auto gamma = make_shared<op::Parameter>(et, channel_shape);
        auto beta = make_shared<op::Parameter>(et, channel_shape);
        auto BN = make_shared<op::BatchNormTraining>(input, gamma, beta, eps);
        auto normed_input = make_shared<op::Result>(make_shared<op::GetOutputElement>(BN, 0));
        auto mean = make_shared<op::Result>(make_shared<op::GetOutputElement>(BN, 1));
        auto variance = make_shared<op::Result>(make_shared<op::GetOutputElement>(BN, 2));
        goes.push_back(mean);
        goes.push_back(variance);
        // TODO autodiff testing with more than one result
1711 1712
        auto f =
            make_shared<Function>(ResultVector{normed_input}, ParameterVector{input, gamma, beta});
1713 1714 1715 1716
        return f;
    };

    auto backend = runtime::Backend::create("${BACKEND_NAME}");
1717
    using T = float;
1718
    test::Uniform<T> rng(-5.0, 2.0);
1719 1720 1721
    auto input = rng.initialize(backend->create_tensor<T>(input_shape));
    auto gamma = rng.initialize(backend->create_tensor<T>(channel_shape));
    auto beta = rng.initialize(backend->create_tensor<T>(channel_shape));
1722

1723
    EXPECT_TRUE(
1724
        autodiff_numeric_compare<T>(backend.get(), make_graph, {input, gamma, beta}, .005, .005));
1725 1726
}

1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739
NGRAPH_TEST(${BACKEND_NAME}, backwards_reverse_sequence_n3_c2_h3)
{
    auto backend = runtime::Backend::create("${BACKEND_NAME}");

    Shape shape{3, 2, 3};
    auto A = make_shared<op::Parameter>(element::i32, shape);
    Shape seq_len_shape{2};
    auto B = make_shared<op::Parameter>(element::i32, seq_len_shape);

    size_t batch_axis = 1;
    size_t sequence_axis = 0;

    auto rs = std::make_shared<op::ReverseSequence>(A, B, batch_axis, sequence_axis);
1740
    auto f = make_shared<Function>(rs, ParameterVector{A, B});
1741

1742 1743 1744 1745 1746
    shared_ptr<runtime::Tensor> a = backend->create_tensor(element::i32, shape);
    shared_ptr<runtime::Tensor> b = backend->create_tensor(element::i32, seq_len_shape);
    shared_ptr<runtime::Tensor> c = backend->create_tensor(element::i32, shape);
    shared_ptr<runtime::Tensor> da = backend->create_tensor(element::i32, shape);
    shared_ptr<runtime::Tensor> db = backend->create_tensor(element::i32, seq_len_shape);
1747

1748
    // input values don't matter
1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761
    vector<int> va(shape_size(shape), 0);

    vector<int> vc{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
    vector<int> expected{13, 14, 15, 16, 17, 18, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6};

    copy_data(c, vc);
    copy_data(a, va);

    std::vector<int> seq_lenghts{3, 3};
    copy_data(b, seq_lenghts);

    auto C = make_shared<op::Parameter>(element::i32, shape);
    auto df = autodiff::backprop_function(f);
1762
    auto handle = backend->compile(df);
1763
    handle->call_with_validate({da, db}, {a, b, c});
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779
    ASSERT_EQ(read_vector<int>(da), expected);
}

NGRAPH_TEST(${BACKEND_NAME}, backwards_reverse_sequence_n4d2c3h2w2)
{
    auto backend = runtime::Backend::create("${BACKEND_NAME}");

    Shape shape{4, 2, 3, 2, 2};
    auto A = make_shared<op::Parameter>(element::i32, shape);
    Shape seq_len_shape{4};
    auto B = make_shared<op::Parameter>(element::i32, seq_len_shape);

    size_t batch_axis = 0;
    size_t sequence_axis = 2;

    auto rs = std::make_shared<op::ReverseSequence>(A, B, batch_axis, sequence_axis);
1780
    auto f = make_shared<Function>(rs, ParameterVector{A, B});
1781

1782 1783 1784 1785 1786
    shared_ptr<runtime::Tensor> a = backend->create_tensor(element::i32, shape);
    shared_ptr<runtime::Tensor> b = backend->create_tensor(element::i32, seq_len_shape);
    shared_ptr<runtime::Tensor> c = backend->create_tensor(element::i32, shape);
    shared_ptr<runtime::Tensor> da = backend->create_tensor(element::i32, shape);
    shared_ptr<runtime::Tensor> db = backend->create_tensor(element::i32, seq_len_shape);
1787

1788
    // input values don't matter
1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
    vector<int> va(shape_size(shape), 0);

    std::vector<int> vc{0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
                        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 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, 73, 74, 75, 76, 77, 78, 79,
                        80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95};

    std::vector<int> expected{0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
                              16, 17, 18, 19, 20, 21, 22, 23, 28, 29, 30, 31, 24, 25, 26, 27,
                              32, 33, 34, 35, 40, 41, 42, 43, 36, 37, 38, 39, 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, 76, 77, 78, 79, 72, 73, 74, 75,
                              80, 81, 82, 83, 88, 89, 90, 91, 84, 85, 86, 87, 92, 93, 94, 95};

    copy_data(c, vc);
    copy_data(a, va);

    std::vector<int> seq_lenghts{1, 2, 1, 2};
    copy_data(b, seq_lenghts);

    auto C = make_shared<op::Parameter>(element::i32, shape);
    auto df = autodiff::backprop_function(f);
1813
    auto handle = backend->compile(df);
1814
    handle->call_with_validate({da, db}, {a, b, c});
1815 1816
    ASSERT_EQ(read_vector<int>(da), expected);
}
1817 1818 1819 1820 1821 1822

// clang-format off
#ifdef AUTODIFF_BACKEND_${BACKEND_NAME}
#undef AUTODIFF_BACKEND_${BACKEND_NAME}
#endif
// clang-format on