/******************************************************************************** * Copyright (c) 2023 CEA-List * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-2.0 * ********************************************************************************/ #include <catch2/catch_test_macros.hpp> #include <memory> #include <string> #include "aidge/data/Tensor.hpp" #include "aidge/graph/Node.hpp" #include "aidge/graph/GraphView.hpp" #include "aidge/graph/OpArgs.hpp" #include "aidge/scheduler/SequentialScheduler.hpp" #include "aidge/scheduler/ParallelScheduler.hpp" #include "aidge/backend/cpu.hpp" using namespace Aidge; TEST_CASE("[cpu/scheduler] SequentialScheduler(forward)") { std::shared_ptr<Tensor> inputTensor = std::make_shared<Tensor>(Array4D<int, 2, 1, 5, 5>{{{{{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}}}}}); std::shared_ptr<Tensor> weight1 = std::make_shared<Tensor>( Array4D<int, 3, 1, 3, 3>{{{{{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}}}}}); std::shared_ptr<Tensor> bias1 = std::make_shared<Tensor>(Array1D<int, 3>{{1, 2, 3}}); SECTION("Test Sequential graph") { std::shared_ptr<GraphView> g = Sequential({ Conv(1, 3, {3, 3}, "conv1"), Conv(3, 4, {1, 1}, "conv2"), Conv(4, 3, {1, 1}, "conv3"), FC(27, 5, false, "fc")}); g->getNode("conv1")->getOperator()->setInput(0, inputTensor); g->getNode("conv1")->getOperator()->setInput(1, weight1); g->getNode("conv1")->getOperator()->setInput(2, bias1); std::shared_ptr<Tensor> weight2 = std::make_shared<Tensor>(Array4D<int, 4, 3, 1, 1>{{{{{1}}, {{2}}, {{3}}}, {{{4}}, {{5}}, {{6}}}, {{{7}}, {{8}}, {{9}}}, {{{10}}, {{11}}, {{12}}}}}); std::shared_ptr<Tensor> bias2 = std::make_shared<Tensor>(Array1D<int, 4>{{1, 2, 3, 4}}); g->getNode("conv2")->getOperator()->setInput(1, weight2); g->getNode("conv2")->getOperator()->setInput(2, bias2); // *(g->getNode("conv2")->getOperator()->input(1, weight2); std::shared_ptr<Tensor> weight3 = std::make_shared<Tensor>( Array4D<int, 3, 4, 1, 1>{{{{{1}}, {{2}}, {{3}}, {{4}}}, {{{5}}, {{6}}, {{7}}, {{8}}}, {{{9}}, {{10}}, {{11}}, {{12}}}}}); std::shared_ptr<Tensor> bias3 = std::make_shared<Tensor>(Array1D<int, 3>{{1, 2, 3}}); g->getNode("conv3")->getOperator()->setInput(1, weight3); g->getNode("conv3")->getOperator()->setInput(2, bias3); std::shared_ptr<Tensor> weightfc = std::make_shared<Tensor>( Array2D<int, 5, 27>{{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, {13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6}, {7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3}, {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}}); std::shared_ptr<Tensor> biasfc = std::make_shared<Tensor>(Array1D<int, 5>{{1, 2, 3, 4, 5}}); g->getNode("fc")->getOperator()->setInput(1, weightfc); g->getNode("fc")->getOperator()->setInput(2, biasfc); // input->addChild(g); g->setDataType(Aidge::DataType::Int32); g->setBackend("cpu"); g->forwardDims(); SequentialScheduler scheduler(g); REQUIRE_NOTHROW(scheduler.forward()); scheduler.saveSchedulingDiagram("schedulingSequential"); std::shared_ptr<Tensor> expectedOutput1 = std::make_shared<Tensor>(Array4D<int, 2, 3, 3, 3>{ {{{{367, 412, 457}, {592, 637, 682}, {817, 862, 907}}, {{854, 980, 1106}, {1484, 1610, 1736}, {2114, 2240, 2366}}, {{1341, 1548, 1755}, {2376, 2583, 2790}, {3411, 3618, 3825}}}, {{{1492, 1537, 1582}, {1717, 1762, 1807}, {1942, 1987, 2032}}, {{4004, 4130, 4256}, {4634, 4760, 4886}, {5264, 5390, 5516}}, {{6516, 6723, 6930}, {7551, 7758, 7965}, {8586, 8793, 9000}}}}}); std::shared_ptr<Tensor> expectedOutput2 = std::make_shared<Tensor>(Array4D<int, 2, 4, 3, 3>{ {{{{6099, 7017, 7935}, {10689, 11607, 12525}, {15279, 16197, 17115}}, {{13786, 15838, 17890}, {24046, 26098, 28150}, {34306, 36358, 38410}}, {{21473, 24659, 27845}, {37403, 40589, 43775}, {53333, 56519, 59705}}, {{29160, 33480, 37800}, {50760, 55080, 59400}, {72360, 76680, 81000}}}, {{{29049, 29967, 30885}, {33639, 34557, 35475}, {38229, 39147, 40065}}, {{65086, 67138, 69190}, {75346, 77398, 79450}, {85606, 87658, 89710}}, {{101123, 104309, 107495}, {117053, 120239, 123425}, {132983, 136169, 139355}}, {{137160, 141480, 145800}, {158760, 163080, 167400}, {180360, 184680, 189000}}}}}); std::shared_ptr<Tensor> expectedOutput3 = std::make_shared<Tensor>(Array4D<int, 2, 3, 3, 3>{ {{{{214731, 246591, 278451}, {374031, 405891, 437751}, {533331, 565191, 597051}}, {{496804, 570568, 644332}, {865624, 939388, 1013152}, {1234444, 1308208, 1381972}}, {{778877, 894545, 1010213}, {1357217, 1472885, 1588553}, {1935557, 2051225, 2166893}}}, {{{1011231, 1043091, 1074951}, {1170531, 1202391, 1234251}, {1329831, 1361691, 1393551}}, {{2340904, 2414668, 2488432}, {2709724, 2783488, 2857252}, {3078544, 3152308, 3226072}}, {{3670577, 3786245, 3901913}, {4248917, 4364585, 4480253}, {4827257, 4942925, 5058593}}}}}); Tensor expectedOutput4 = Array2D<int, 2, 5>{ {{205050376, 198925904, 181355097, 196978090, 238868348}, {598467376, 561797804, 560823897, 593043790, 698672948}}}; std::shared_ptr<Tensor> other1 = std::static_pointer_cast<OperatorTensor>(g->getNode("conv1")->getOperator())->getOutput(0); bool equal1 = (*other1 == *expectedOutput1); REQUIRE(equal1); std::shared_ptr<Tensor> other2 = std::static_pointer_cast<OperatorTensor>(g->getNode("conv2")->getOperator())->getOutput(0); bool equal2 = (*other2 == *expectedOutput2); REQUIRE(equal2); std::shared_ptr<Tensor> other3 = std::static_pointer_cast<OperatorTensor>(g->getNode("conv3")->getOperator())->getOutput(0); bool equal3 = (*other3 == *expectedOutput3); REQUIRE(equal3); std::shared_ptr<Tensor> other4 = std::static_pointer_cast<OperatorTensor>(g->getNode("fc")->getOperator())->getOutput(0); bool equal4 = (*other4 == expectedOutput4); REQUIRE(equal4); } SECTION("Test Parallel graph") { std::shared_ptr<GraphView> g = Sequential({Conv(1, 3, {3, 3}, "inputConv"), Parallel({ Conv(3, 3, {1, 1}, "conv1.1"), Conv(3, 3, {1, 1}, "conv1.2"), Conv(3, 3, {1, 1}, "conv1.3")}), Add(3, "add1"), Conv(3, 2, {1, 1}, "conv2"), FC(18, 5, false, "out")}); g->getNode("inputConv")->getOperator()->setInput(0, inputTensor); g->getNode("inputConv")->getOperator()->setInput(1, weight1); g->getNode("inputConv")->getOperator()->setInput(2, bias1); std::shared_ptr<Tensor> conv11Weight = std::make_shared<Tensor>(Array4D<int, 3, 3, 1, 1>{ {{{{1}}, {{2}}, {{3}}}, {{{4}}, {{5}}, {{6}}}, {{{7}}, {{8}}, {{9}}}}}); g->getNode("conv1.1")->getOperator()->setInput(1, conv11Weight); g->getNode("conv1.1")->getOperator()->setInput(2, bias1); std::shared_ptr<Tensor> conv12Weight = std::make_shared<Tensor>(Array4D<int, 3, 3, 1, 1>{ {{{{11}}, {{12}}, {{13}}}, {{{14}}, {{15}}, {{16}}}, {{{17}}, {{18}}, {{19}}}}}); g->getNode("conv1.2")->getOperator()->setInput(1, conv12Weight); g->getNode("conv1.2")->getOperator()->setInput(2, bias1); std::shared_ptr<Tensor> conv13Weight = std::make_shared<Tensor>(Array4D<int, 3, 3, 1, 1>{ {{{{21}}, {{22}}, {{23}}}, {{{24}}, {{25}}, {{26}}}, {{{27}}, {{28}}, {{29}}}}}); g->getNode("conv1.3")->getOperator()->setInput(1, conv13Weight); g->getNode("conv1.3")->getOperator()->setInput(2, bias1); std::shared_ptr<Tensor> conv2Weight = std::make_shared<Tensor>( Array4D<int, 2, 3, 1, 1>{{{{{1}}, {{2}}, {{3}}}, {{{4}}, {{5}}, {{6}}}}}); std::shared_ptr<Tensor> bias2 = std::make_shared<Tensor>(Array1D<int, 2>{{1, 2}}); g->getNode("conv2")->getOperator()->setInput(1, conv2Weight); g->getNode("conv2")->getOperator()->setInput(2, bias2); std::shared_ptr<Tensor> fcWeight = std::make_shared<Tensor>( Array2D<int, 5, 18>{{{1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3}, {4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1}, {2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4}, {5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2}, {3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5}}}); std::shared_ptr<Tensor> fcBias = std::make_shared<Tensor>(Array1D<int, 5>{{1, 2, 3, 4, 5}}); g->getNode("out")->getOperator()->setInput(1, fcWeight); g->getNode("out")->getOperator()->setInput(2, fcBias); std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>( Array2D<int, 2, 5>{{{124324368, 130692907, 133325056, 125044620, 142843879}, {369195468, 394615207, 382643056, 379441320, 416291779}}}); g->setBackend("cpu"); g->setDataType(Aidge::DataType::Int32); g->forwardDims(); SequentialScheduler scheduler(g); REQUIRE_NOTHROW(scheduler.forward()); scheduler.saveSchedulingDiagram("schedulingSequential"); std::shared_ptr<Tensor> result = std::static_pointer_cast<Tensor>(g->getNode("out")->getOperator()->getRawOutput(0)); bool equal = (*result == *expectedOutput); REQUIRE(equal); } SECTION("Test Residual graph") { } SECTION("Test Recurrent graph (sequential)") { std::shared_ptr<Tensor> in = std::make_shared<Tensor>( Array2D<int, 2, 3>{{{1, 2, 3}, {4, 5, 6}}}); std::shared_ptr<Tensor> initTensor = std::make_shared<Tensor>( Array2D<int, 2, 3>{{{0, 0, 0}, {1, 1, 1}}}); std::shared_ptr<Tensor> biasTensor = std::make_shared<Tensor>( Array2D<int, 2, 3>{{{2, 0, 0}, {1, 0, 0}}}); auto add1 = Add(2, "add1"); auto mem = Memorize(3, "mem1"); auto add2 = Add(2, "add2"); auto bias = Producer(biasTensor, "bias"); auto init = Producer(initTensor, "init"); auto input = Producer(in, "input"); std::shared_ptr<GraphView> g = Sequential({add1, mem, add2}); init->addChild(mem, 0, 1); mem->addChild(add1, 1, 1); bias->addChild(add2, 0, 1); input->addChild(add1, 0, 0); // Update GraphView inputs/outputs following previous connections: g->add({mem, add1, add2, init, bias, input}); g->setBackend("cpu"); g->setDataType(Aidge::DataType::Int32); g->save("graphRecurrent"); g->forwardDims(); SequentialScheduler scheduler(g); REQUIRE_NOTHROW(scheduler.forward(true)); scheduler.saveStaticSchedulingDiagram("static_schedule"); scheduler.saveSchedulingDiagram("schedulingRecurrent_seq"); std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>( Array2D<int, 2, 3>{{{5, 6, 9}, {14, 16, 19}}}); std::shared_ptr<Tensor> result = std::static_pointer_cast<Tensor>(g->getNode("add2")->getOperator()->getRawOutput(0)); result->print(); expectedOutput->print(); bool equal = (*result == *expectedOutput); REQUIRE(equal); } SECTION("Test Recurrent graph (parallel)") { std::shared_ptr<Tensor> in = std::make_shared<Tensor>( Array2D<int, 2, 3>{{{1, 2, 3}, {4, 5, 6}}}); std::shared_ptr<Tensor> initTensor = std::make_shared<Tensor>( Array2D<int, 2, 3>{{{0, 0, 0}, {1, 1, 1}}}); std::shared_ptr<Tensor> biasTensor = std::make_shared<Tensor>( Array2D<int, 2, 3>{{{2, 0, 0}, {1, 0, 0}}}); auto add1 = Add(2, "add1"); auto mem = Memorize(3, "mem1"); auto add2 = Add(2, "add2"); auto bias = Producer(biasTensor, "bias"); auto init = Producer(initTensor, "init"); auto input = Producer(in, "input"); std::shared_ptr<GraphView> g = Sequential({add1, mem, add2}); init->addChild(mem, 0, 1); mem->addChild(add1, 1, 1); bias->addChild(add2, 0, 1); input->addChild(add1, 0, 0); // Update GraphView inputs/outputs following previous connections: g->add({mem, add1, add2, init, bias, input}); g->setBackend("cpu"); g->setDataType(Aidge::DataType::Int32); g->save("graphRecurrent"); g->forwardDims(); ParallelScheduler scheduler(g); REQUIRE_NOTHROW(scheduler.forward(true)); scheduler.saveSchedulingDiagram("schedulingRecurrent_par"); std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>( Array2D<int, 2, 3>{{{5, 6, 9}, {14, 16, 19}}}); std::shared_ptr<Tensor> result = std::static_pointer_cast<Tensor>(g->getNode("add2")->getOperator()->getRawOutput(0)); result->print(); expectedOutput->print(); bool equal = (*result == *expectedOutput); REQUIRE(equal); } SECTION("Test ConnectInput graph") { std::shared_ptr<GraphView> g = Sequential({ Conv(1, 3, {3, 3}, "conv1"), Conv(3, 4, {1, 1}, "conv2"), Conv(4, 3, {1, 1}, "conv3"), FC(27, 5, false, "fc")}); // g->getNode("conv1")->getOperator()->setInput(0, inputTensor); g->getNode("conv1")->getOperator()->setInput(1, weight1); g->getNode("conv1")->getOperator()->setInput(2, bias1); std::shared_ptr<Tensor> weight2 = std::make_shared<Tensor>(Array4D<int, 4, 3, 1, 1>{{{{{1}}, {{2}}, {{3}}}, {{{4}}, {{5}}, {{6}}}, {{{7}}, {{8}}, {{9}}}, {{{10}}, {{11}}, {{12}}}}}); std::shared_ptr<Tensor> bias2 = std::make_shared<Tensor>(Array1D<int, 4>{{1, 2, 3, 4}}); g->getNode("conv2")->getOperator()->setInput(1, weight2); g->getNode("conv2")->getOperator()->setInput(2, bias2); // *(g->getNode("conv2")->getOperator()->input(1, weight2); std::shared_ptr<Tensor> weight3 = std::make_shared<Tensor>( Array4D<int, 3, 4, 1, 1>{{{{{1}}, {{2}}, {{3}}, {{4}}}, {{{5}}, {{6}}, {{7}}, {{8}}}, {{{9}}, {{10}}, {{11}}, {{12}}}}}); std::shared_ptr<Tensor> bias3 = std::make_shared<Tensor>(Array1D<int, 3>{{1, 2, 3}}); g->getNode("conv3")->getOperator()->setInput(1, weight3); g->getNode("conv3")->getOperator()->setInput(2, bias3); std::shared_ptr<Tensor> weightfc = std::make_shared<Tensor>( Array2D<int, 5, 27>{{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, {13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6}, {7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3}, {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}}); std::shared_ptr<Tensor> biasfc = std::make_shared<Tensor>(Array1D<int, 5>{{1, 2, 3, 4, 5}}); g->getNode("fc")->getOperator()->setInput(1, weightfc); g->getNode("fc")->getOperator()->setInput(2, biasfc); // input->addChild(g); g->setDataType(Aidge::DataType::Int32); g->setBackend("cpu"); std::vector<std::vector<Aidge::DimSize_t>> dims = {inputTensor->dims()}; g->forwardDims(dims); SequentialScheduler scheduler(g); std::vector<std::shared_ptr<Aidge::Tensor>> dataIn = {inputTensor}; REQUIRE_NOTHROW(scheduler.forward(true, dataIn)); scheduler.saveSchedulingDiagram("schedulingSequential"); std::shared_ptr<Tensor> expectedOutput1 = std::make_shared<Tensor>(Array4D<int, 2, 3, 3, 3>{ {{{{367, 412, 457}, {592, 637, 682}, {817, 862, 907}}, {{854, 980, 1106}, {1484, 1610, 1736}, {2114, 2240, 2366}}, {{1341, 1548, 1755}, {2376, 2583, 2790}, {3411, 3618, 3825}}}, {{{1492, 1537, 1582}, {1717, 1762, 1807}, {1942, 1987, 2032}}, {{4004, 4130, 4256}, {4634, 4760, 4886}, {5264, 5390, 5516}}, {{6516, 6723, 6930}, {7551, 7758, 7965}, {8586, 8793, 9000}}}}}); std::shared_ptr<Tensor> expectedOutput2 = std::make_shared<Tensor>(Array4D<int, 2, 4, 3, 3>{ {{{{6099, 7017, 7935}, {10689, 11607, 12525}, {15279, 16197, 17115}}, {{13786, 15838, 17890}, {24046, 26098, 28150}, {34306, 36358, 38410}}, {{21473, 24659, 27845}, {37403, 40589, 43775}, {53333, 56519, 59705}}, {{29160, 33480, 37800}, {50760, 55080, 59400}, {72360, 76680, 81000}}}, {{{29049, 29967, 30885}, {33639, 34557, 35475}, {38229, 39147, 40065}}, {{65086, 67138, 69190}, {75346, 77398, 79450}, {85606, 87658, 89710}}, {{101123, 104309, 107495}, {117053, 120239, 123425}, {132983, 136169, 139355}}, {{137160, 141480, 145800}, {158760, 163080, 167400}, {180360, 184680, 189000}}}}}); std::shared_ptr<Tensor> expectedOutput3 = std::make_shared<Tensor>(Array4D<int, 2, 3, 3, 3>{ {{{{214731, 246591, 278451}, {374031, 405891, 437751}, {533331, 565191, 597051}}, {{496804, 570568, 644332}, {865624, 939388, 1013152}, {1234444, 1308208, 1381972}}, {{778877, 894545, 1010213}, {1357217, 1472885, 1588553}, {1935557, 2051225, 2166893}}}, {{{1011231, 1043091, 1074951}, {1170531, 1202391, 1234251}, {1329831, 1361691, 1393551}}, {{2340904, 2414668, 2488432}, {2709724, 2783488, 2857252}, {3078544, 3152308, 3226072}}, {{3670577, 3786245, 3901913}, {4248917, 4364585, 4480253}, {4827257, 4942925, 5058593}}}}}); Tensor expectedOutput4 = Array2D<int, 2, 5>{ {{205050376, 198925904, 181355097, 196978090, 238868348}, {598467376, 561797804, 560823897, 593043790, 698672948}}}; std::shared_ptr<Tensor> other1 = std::static_pointer_cast<OperatorTensor>(g->getNode("conv1")->getOperator())->getOutput(0); bool equal1 = (*other1 == *expectedOutput1); REQUIRE(equal1); std::shared_ptr<Tensor> other2 = std::static_pointer_cast<OperatorTensor>(g->getNode("conv2")->getOperator())->getOutput(0); bool equal2 = (*other2 == *expectedOutput2); REQUIRE(equal2); std::shared_ptr<Tensor> other3 = std::static_pointer_cast<OperatorTensor>(g->getNode("conv3")->getOperator())->getOutput(0); bool equal3 = (*other3 == *expectedOutput3); REQUIRE(equal3); std::shared_ptr<Tensor> other4 = std::static_pointer_cast<OperatorTensor>(g->getNode("fc")->getOperator())->getOutput(0); bool equal4 = (*other4 == expectedOutput4); REQUIRE(equal4); } }