TestTrivialModel.cpp revision 83e24dc4706a5b7089881a55daf05b3924fab3b7
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "NeuralNetworksWrapper.h"
18
19#include <gtest/gtest.h>
20
21using namespace android::nn::wrapper;
22
23namespace {
24
25typedef float Matrix3x4[3][4];
26typedef float Matrix4[4];
27
28class TrivialTest : public ::testing::Test {
29protected:
30    virtual void SetUp() { ASSERT_EQ(Initialize(), Result::NO_ERROR); }
31    virtual void TearDown() { Shutdown(); }
32
33    const Matrix3x4 matrix1 = {{1.f, 2.f, 3.f, 4.f}, {5.f, 6.f, 7.f, 8.f}, {9.f, 10.f, 11.f, 12.f}};
34    const Matrix3x4 matrix2 = {{100.f, 200.f, 300.f, 400.f},
35                               {500.f, 600.f, 700.f, 800.f},
36                               {900.f, 1000.f, 1100.f, 1200.f}};
37    const Matrix4 matrix2b = {100.f, 200.f, 300.f, 400.f};
38    const Matrix3x4 matrix3 = {{20.f, 30.f, 40.f, 50.f},
39                               {21.f, 22.f, 23.f, 24.f},
40                               {31.f, 32.f, 33.f, 34.f}};
41    const Matrix3x4 expected2 = {{101.f, 202.f, 303.f, 404.f},
42                                 {505.f, 606.f, 707.f, 808.f},
43                                 {909.f, 1010.f, 1111.f, 1212.f}};
44    const Matrix3x4 expected2b = {{101.f, 202.f, 303.f, 404.f},
45                                  {105.f, 206.f, 307.f, 408.f},
46                                  {109.f, 210.f, 311.f, 412.f}};
47    const Matrix3x4 expected2c = {{100.f, 400.f, 900.f, 1600.f},
48                                  {500.f, 1200.f, 2100.f, 3200.f},
49                                  {900.f, 2000.f, 3300.f, 4800.f}};
50
51    const Matrix3x4 expected3 = {{121.f, 232.f, 343.f, 454.f},
52                                 {526.f, 628.f, 730.f, 832.f},
53                                 {940.f, 1042.f, 1144.f, 1246.f}};
54    const Matrix3x4 expected3b = {{22.f, 34.f, 46.f, 58.f},
55                                  {31.f, 34.f, 37.f, 40.f},
56                                  {49.f, 52.f, 55.f, 58.f}};
57};
58
59// Create a model that can add two tensors using a one node graph.
60void CreateAddTwoTensorModel(Model* model) {
61    OperandType matrixType(Type::TENSOR_FLOAT32, {3, 4});
62    OperandType scalarType(Type::INT32, {});
63    int32_t activation(ANEURALNETWORKS_FUSED_NONE);
64    auto a = model->addOperand(&matrixType);
65    auto b = model->addOperand(&matrixType);
66    auto c = model->addOperand(&matrixType);
67    auto d = model->addOperand(&scalarType);
68    model->setOperandValue(d, &activation, sizeof(activation));
69    model->addOperation(ANEURALNETWORKS_ADD, {a, b, d}, {c});
70    model->setInputsAndOutputs({a, b}, {c});
71    ASSERT_TRUE(model->isValid());
72}
73
74// Create a model that can add three tensors using a two node graph,
75// with one tensor set as part of the model.
76void CreateAddThreeTensorModel(Model* model, const Matrix3x4 bias) {
77    OperandType matrixType(Type::TENSOR_FLOAT32, {3, 4});
78    OperandType scalarType(Type::INT32, {});
79    int32_t activation(ANEURALNETWORKS_FUSED_NONE);
80    auto a = model->addOperand(&matrixType);
81    auto b = model->addOperand(&matrixType);
82    auto c = model->addOperand(&matrixType);
83    auto d = model->addOperand(&matrixType);
84    auto e = model->addOperand(&matrixType);
85    auto f = model->addOperand(&scalarType);
86    model->setOperandValue(e, bias, sizeof(Matrix3x4));
87    model->setOperandValue(f, &activation, sizeof(activation));
88    model->addOperation(ANEURALNETWORKS_ADD, {a, c, f}, {b});
89    model->addOperation(ANEURALNETWORKS_ADD, {b, e, f}, {d});
90    model->setInputsAndOutputs({c, a}, {d});
91    ASSERT_TRUE(model->isValid());
92}
93
94// Check that the values are the same. This works only if dealing with integer
95// value, otherwise we should accept values that are similar if not exact.
96int CompareMatrices(const Matrix3x4& expected, const Matrix3x4& actual) {
97    int errors = 0;
98    for (int i = 0; i < 3; i++) {
99        for (int j = 0; j < 4; j++) {
100            if (expected[i][j] != actual[i][j]) {
101                printf("expected[%d][%d] != actual[%d][%d], %f != %f\n", i, j, i, j,
102                       static_cast<double>(expected[i][j]), static_cast<double>(actual[i][j]));
103                errors++;
104            }
105        }
106    }
107    return errors;
108}
109
110TEST_F(TrivialTest, AddTwo) {
111    Model modelAdd2;
112    CreateAddTwoTensorModel(&modelAdd2);
113
114    // Test the one node model.
115    Matrix3x4 actual;
116    memset(&actual, 0, sizeof(actual));
117    Compilation compilation(&modelAdd2);
118    compilation.compile();
119    Request request(&compilation);
120    ASSERT_EQ(request.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
121    ASSERT_EQ(request.setInput(1, matrix2, sizeof(Matrix3x4)), Result::NO_ERROR);
122    ASSERT_EQ(request.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
123    ASSERT_EQ(request.compute(), Result::NO_ERROR);
124    ASSERT_EQ(CompareMatrices(expected2, actual), 0);
125}
126
127TEST_F(TrivialTest, AddThree) {
128    Model modelAdd3;
129    CreateAddThreeTensorModel(&modelAdd3, matrix3);
130
131    // Test the three node model.
132    Matrix3x4 actual;
133    memset(&actual, 0, sizeof(actual));
134    Compilation compilation2(&modelAdd3);
135    compilation2.compile();
136    Request request2(&compilation2);
137    ASSERT_EQ(request2.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
138    ASSERT_EQ(request2.setInput(1, matrix2, sizeof(Matrix3x4)), Result::NO_ERROR);
139    ASSERT_EQ(request2.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
140    ASSERT_EQ(request2.compute(), Result::NO_ERROR);
141    ASSERT_EQ(CompareMatrices(expected3, actual), 0);
142
143    // Test it a second time to make sure the model is reusable.
144    memset(&actual, 0, sizeof(actual));
145    Compilation compilation3(&modelAdd3);
146    compilation3.compile();
147    Request request3(&compilation3);
148    ASSERT_EQ(request3.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
149    ASSERT_EQ(request3.setInput(1, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
150    ASSERT_EQ(request3.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
151    ASSERT_EQ(request3.compute(), Result::NO_ERROR);
152    ASSERT_EQ(CompareMatrices(expected3b, actual), 0);
153}
154
155TEST_F(TrivialTest, BroadcastAddTwo) {
156    Model modelBroadcastAdd2;
157    // activation: NONE.
158    int32_t activation_init[] = {ANEURALNETWORKS_FUSED_NONE};
159    OperandType scalarType(Type::INT32, {1});
160    auto activation = modelBroadcastAdd2.addOperand(&scalarType);
161    modelBroadcastAdd2.setOperandValue(activation, activation_init, sizeof(int32_t) * 1);
162
163    OperandType matrixType(Type::TENSOR_FLOAT32, {1, 1, 3, 4});
164    OperandType matrixType2(Type::TENSOR_FLOAT32, {4});
165
166    auto a = modelBroadcastAdd2.addOperand(&matrixType);
167    auto b = modelBroadcastAdd2.addOperand(&matrixType2);
168    auto c = modelBroadcastAdd2.addOperand(&matrixType);
169    modelBroadcastAdd2.addOperation(ANEURALNETWORKS_ADD, {a, b, activation}, {c});
170    modelBroadcastAdd2.setInputsAndOutputs({a, b}, {c});
171    ASSERT_TRUE(modelBroadcastAdd2.isValid());
172
173    // Test the one node model.
174    Matrix3x4 actual;
175    memset(&actual, 0, sizeof(actual));
176    Compilation compilation(&modelBroadcastAdd2);
177    compilation.compile();
178    Request request(&compilation);
179    ASSERT_EQ(request.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
180    ASSERT_EQ(request.setInput(1, matrix2b, sizeof(Matrix4)), Result::NO_ERROR);
181    ASSERT_EQ(request.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
182    ASSERT_EQ(request.compute(), Result::NO_ERROR);
183    ASSERT_EQ(CompareMatrices(expected2b, actual), 0);
184}
185
186TEST_F(TrivialTest, BroadcastMulTwo) {
187    Model modelBroadcastMul2;
188    // activation: NONE.
189    int32_t activation_init[] = {ANEURALNETWORKS_FUSED_NONE};
190    OperandType scalarType(Type::INT32, {1});
191    auto activation = modelBroadcastMul2.addOperand(&scalarType);
192    modelBroadcastMul2.setOperandValue(activation, activation_init, sizeof(int32_t) * 1);
193
194    OperandType matrixType(Type::TENSOR_FLOAT32, {1, 1, 3, 4});
195    OperandType matrixType2(Type::TENSOR_FLOAT32, {4});
196
197    auto a = modelBroadcastMul2.addOperand(&matrixType);
198    auto b = modelBroadcastMul2.addOperand(&matrixType2);
199    auto c = modelBroadcastMul2.addOperand(&matrixType);
200    modelBroadcastMul2.addOperation(ANEURALNETWORKS_MUL, {a, b, activation}, {c});
201    modelBroadcastMul2.setInputsAndOutputs({a, b}, {c});
202    ASSERT_TRUE(modelBroadcastMul2.isValid());
203
204    // Test the one node model.
205    Matrix3x4 actual;
206    memset(&actual, 0, sizeof(actual));
207    Compilation compilation(&modelBroadcastMul2);
208    compilation.compile();
209    Request request(&compilation);
210    ASSERT_EQ(request.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
211    ASSERT_EQ(request.setInput(1, matrix2b, sizeof(Matrix4)), Result::NO_ERROR);
212    ASSERT_EQ(request.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
213    ASSERT_EQ(request.compute(), Result::NO_ERROR);
214    ASSERT_EQ(CompareMatrices(expected2c, actual), 0);
215}
216
217}  // end namespace
218