blob: f9bdfde6223dfdc08210c4eec7f22da4f9ce26f0 [file] [log] [blame]
Sadik Armaganf0a6dec2021-03-25 07:46:55 +00001//
2// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5#pragma once
6
7#include "CommonTestUtils.hpp"
8
9#include <ResolveType.hpp>
10
11#include <armnn/INetwork.hpp>
12
13#include <armnn/utility/NumericCast.hpp>
14
Sadik Armagan1625efc2021-06-10 18:24:34 +010015#include <doctest/doctest.h>
Sadik Armaganf0a6dec2021-03-25 07:46:55 +000016
17#include <vector>
18
19namespace
20{
21
22armnn::INetworkPtr CreateFullyConnectedNetworkNonConstWeights(const armnn::TensorInfo& inputTensorInfo,
23 const armnn::TensorInfo& outputTensorInfo,
24 const armnn::TensorInfo& weightsTensorInfo,
25 armnn::FullyConnectedDescriptor descriptor)
26{
27 armnn::INetworkPtr network(armnn::INetwork::Create());
28
29 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
30 armnn::IConnectableLayer* weightsInputLayer = network->AddInputLayer(1, "Weights_Input");
Matthew Sloyan81beae32021-07-13 19:46:11 +010031 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
Sadik Armaganf0a6dec2021-03-25 07:46:55 +000032 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
33
34 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
35 Connect(weightsInputLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
36 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
37
38 return network;
39}
40
Matthew Sloyan81beae32021-07-13 19:46:11 +010041armnn::INetworkPtr CreateFullyConnectedNetworkNonConstWeightsConstBias(const armnn::TensorInfo& inputTensorInfo,
42 const armnn::TensorInfo& outputTensorInfo,
43 const armnn::TensorInfo& weightsTensorInfo,
44 const armnn::TensorInfo& biasTensorInfo,
45 const armnn::ConstTensor& biasConstantTensor,
46 armnn::FullyConnectedDescriptor descriptor)
47{
48 armnn::INetworkPtr network(armnn::INetwork::Create());
49
50 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
51 armnn::IConnectableLayer* weightsInputLayer = network->AddInputLayer(1, "Weights_Input");
52 armnn::IConnectableLayer* biasLayer = network->AddConstantLayer(biasConstantTensor, "Weights");
53 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
54 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
55
56 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
57 Connect(weightsInputLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
58 Connect(biasLayer, fullyConnectedLayer, biasTensorInfo, 0, 2);
59 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
60
61 return network;
62}
63
64armnn::INetworkPtr CreateFullyConnectedNetworkConstWeightsNonConstBias(const armnn::TensorInfo& inputTensorInfo,
65 const armnn::TensorInfo& outputTensorInfo,
66 const armnn::TensorInfo& weightsTensorInfo,
67 const armnn::TensorInfo& biasTensorInfo,
68 const armnn::ConstTensor& weightsConstantTensor,
69 armnn::FullyConnectedDescriptor descriptor)
70{
71 armnn::INetworkPtr network(armnn::INetwork::Create());
72
73 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
74 armnn::IConnectableLayer* weightsLayer = network->AddConstantLayer(weightsConstantTensor, "Weights");
75 armnn::IConnectableLayer* biasLayer = network->AddInputLayer(2, "Bias_Input");
76 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
77 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
78
79 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
80 Connect(weightsLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
81 Connect(biasLayer, fullyConnectedLayer, biasTensorInfo, 0, 2);
82 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
83
84 return network;
85}
86
Cathal Corbett521032f2021-10-07 11:46:40 +010087armnn::INetworkPtr CreateFullyConnectedNetworkNoTensorInfoConstWeights(const armnn::TensorInfo& inputTensorInfo,
88 const armnn::TensorInfo& outputTensorInfo,
89 const armnn::ConstTensor& weightsConstantTensor,
90 armnn::FullyConnectedDescriptor descriptor)
91{
92 armnn::INetworkPtr network(armnn::INetwork::Create());
93
94 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
95 armnn::IConnectableLayer* weightsLayer = network->AddConstantLayer(weightsConstantTensor, "Weights");
96 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
97 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
98
99 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
100 weightsLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(1));
101 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
102
103 return network;
104}
105
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100106armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedWeightsExplicit(const armnn::TensorInfo& inputTensorInfo,
107 const armnn::TensorInfo& outputTensorInfo,
108 const armnn::TensorInfo& biasTensorInfo,
109 armnn::FullyConnectedDescriptor descriptor)
110{
111 armnn::INetworkPtr network(armnn::INetwork::Create());
112
113 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
114 armnn::IConnectableLayer* biasLayer = network->AddInputLayer(2, "Bias_Input");
115 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
116 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
117
118 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
119 Connect(biasLayer, fullyConnectedLayer, biasTensorInfo, 0, 2);
120 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
121
122 return network;
123}
124
125armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedWeightsAndBias(const armnn::TensorInfo& inputTensorInfo,
126 const armnn::TensorInfo& outputTensorInfo,
127 armnn::FullyConnectedDescriptor descriptor)
128{
129 armnn::INetworkPtr network(armnn::INetwork::Create());
130
131 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
132 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
133 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
134
135 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
136 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
137
138 return network;
139}
140
141armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedBiasExplicit(const armnn::TensorInfo& inputTensorInfo,
142 const armnn::TensorInfo& outputTensorInfo,
143 const armnn::TensorInfo& weightsTensorInfo,
144 const armnn::ConstTensor& weightsConstantTensor,
145 armnn::FullyConnectedDescriptor descriptor)
146{
147 armnn::INetworkPtr network(armnn::INetwork::Create());
148
149 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
150 armnn::IConnectableLayer* weightsLayer = network->AddConstantLayer(weightsConstantTensor, "Weights");
151 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
152 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
153
154 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
155 Connect(weightsLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
156 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
157
158 return network;
159}
160
Sadik Armaganf0a6dec2021-03-25 07:46:55 +0000161template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
162void FullyConnectedWithDynamicWeightsEndToEnd(const std::vector<armnn::BackendId>& backends)
163{
164 using namespace armnn;
165
166 armnn::TensorInfo inputTensorInfo({ 1, 1, 2, 3 }, ArmnnType);
167 inputTensorInfo.SetQuantizationScale(0.1f);
168 inputTensorInfo.SetQuantizationOffset(63);
169
170 armnn::TensorInfo outputTensorInfo({ 1, 2 }, ArmnnType);
171 outputTensorInfo.SetQuantizationScale(5.f);
172 outputTensorInfo.SetQuantizationOffset(10);
173
174 armnn::TensorInfo weightsTensorInfo({ 2, 6 }, ArmnnType);
175 weightsTensorInfo.SetQuantizationScale(0.2f);
176 weightsTensorInfo.SetQuantizationOffset(93);
177
178 FullyConnectedDescriptor descriptor;
179 descriptor.m_ConstantWeights = false;
180 descriptor.m_BiasEnabled = false;
181 descriptor.m_TransposeWeightMatrix = true;
182
183 std::vector<T> inputData {
184 -1.2f, 6.1f, -3.5f,
185 18.8f, -5.5f, 2.9f
186 };
187
188 std::vector<T> weightsData {
189 -8.4f, 20.0f, -10.4f, -8, 16.4f, -11.8f,
190 23.4f, 10.4f, -14.0f, -3.8f, -11.8f, 11.4f
191 };
192
193 std::vector<T> floatExpectedOutputData {
194 -107.04f, 110.f
195 };
196 std::vector<T> expectedOutputData = armnnUtils::QuantizedVector<T>(floatExpectedOutputData);
197
198 armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeights(inputTensorInfo,
199 outputTensorInfo,
200 weightsTensorInfo,
201 descriptor);
202
Sadik Armagan1625efc2021-06-10 18:24:34 +0100203 CHECK(network);
Sadik Armaganf0a6dec2021-03-25 07:46:55 +0000204
205 std::map<int, std::vector<T>> inputTensorData = {{ 0, inputData }, {1, weightsData}};
206 std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutputData }};
207
208 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
209 inputTensorData,
210 expectedOutputTensorData,
211 backends,
212 1.0f);
213}
Matthew Sloyan81beae32021-07-13 19:46:11 +0100214
215template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
216void FullyConnectedWithDynamicOrConstantInputsEndToEnd(const std::vector<armnn::BackendId>& backends,
217 const bool transposeWeights,
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100218 const bool constantWeightsOrBias)
Matthew Sloyan81beae32021-07-13 19:46:11 +0100219{
220 unsigned int inputWidth = 1;
221 unsigned int inputHeight = 1;
222 unsigned int inputChannels = 5;
223 unsigned int inputNum = 2;
224
225 unsigned int outputChannels = 3;
226 unsigned int outputNum = 2;
227
228 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
229 unsigned int outputShape[] = { outputNum, outputChannels };
230 unsigned int weightsShape[] = { inputChannels, outputChannels };
231
232 if (transposeWeights)
233 {
234 std::swap(weightsShape[0], weightsShape[1]);
235 }
236
237 unsigned int biasShape[] = { outputChannels };
238
239 armnn::TensorInfo inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
240 armnn::TensorInfo outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
241 armnn::TensorInfo weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32);
242 armnn::TensorInfo biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32);
243
244 std::vector<float> input =
245 {
246 1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
247 5.0f, 4.0f, 3.0f, 2.0f, 1.0f
248 };
249
250 std::vector<float> weights =
251 {
252 .5f, 2.f, .5f,
253 .5f, 2.f, 1.f,
254 .5f, 2.f, 2.f,
255 .5f, 2.f, 3.f,
256 .5f, 2.f, 4.f
257 };
258
259 if (transposeWeights)
260 {
261 weights =
262 {
263 .5f, .5f, .5f, .5f, .5f,
264 2.f, 2.f, 2.f, 2.f, 2.f,
265 .5f, 1.f, 2.f, 3.f, 4.f
266 };
267 }
268
269 std::vector<float> biasValues = std::vector<float>({10.f, 20.f, 30.f});
270
271 std::vector<float> expectedOutput =
272 {
273 0.5f + 1.0f + 1.5f + 2.0f + 2.5f + biasValues[0],
274 2.0f + 4.0f + 6.0f + 8.0f + 10.f + biasValues[1],
275 0.5f + 2.0f + 6.0f + 12.f + 20.f + biasValues[2],
276
277 2.5f + 2.0f + 1.5f + 1.0f + 0.5f + biasValues[0],
278 10.0f + 8.0f + 6.0f + 4.0f + 2.f + biasValues[1],
279 2.5f + 4.0f + 6.0f + 6.f + 4.f + biasValues[2]
280 };
281
282 FullyConnectedDescriptor descriptor;
283 descriptor.m_BiasEnabled = true;
284 descriptor.m_TransposeWeightMatrix = transposeWeights;
285 descriptor.m_ConstantWeights = constantWeightsOrBias;
286
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100287 if (!constantWeightsOrBias)
Matthew Sloyan81beae32021-07-13 19:46:11 +0100288 {
289 // Tests non constant weights and constant bias.
290 ConstTensor biasConstantTensor(biasesDesc, biasValues.data());
291
292 armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeightsConstBias(inputTensorInfo,
293 outputTensorInfo,
294 weightsDesc,
295 biasesDesc,
296 biasConstantTensor,
297 descriptor);
298 CHECK(network);
299
300 std::map<int, std::vector<T>> inputTensorData = {{ 0, input }, {1, weights}};
301 std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutput }};
302
303 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
304 inputTensorData,
305 expectedOutputTensorData,
306 backends,
307 1.0f);
308 }
309 else
310 {
311 // Tests constant weights and non constant bias.
312 ConstTensor weightsConstantTensor(weightsDesc, weights.data());
313
314 armnn::INetworkPtr network = CreateFullyConnectedNetworkConstWeightsNonConstBias(inputTensorInfo,
315 outputTensorInfo,
316 weightsDesc,
317 biasesDesc,
318 weightsConstantTensor,
319 descriptor);
320 CHECK(network);
321
322 std::map<int, std::vector<T>> inputTensorData = {{ 0, input }, {2, biasValues}};
323 std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutput }};
324
325 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
326 inputTensorData,
327 expectedOutputTensorData,
328 backends,
329 1.0f);
330 }
331}
332
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100333template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
334void FullyConnectedErrorChecking(const std::vector<armnn::BackendId>& backends,
335 const bool explicitCheck,
336 const bool biasEnabled,
337 const bool connectedWeights,
338 const bool connectedBias,
339 const bool tensorInfoSet)
340{
341 unsigned int inputWidth = 1;
342 unsigned int inputHeight = 1;
343 unsigned int inputChannels = 5;
344 unsigned int inputNum = 2;
345
346 unsigned int outputChannels = 3;
347 unsigned int outputNum = 2;
348
349 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
350 unsigned int outputShape[] = { outputNum, outputChannels };
351 unsigned int weightsShape[] = { inputChannels, outputChannels };
352
353 unsigned int biasShape[] = { outputChannels };
354
355 armnn::TensorInfo inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
356 armnn::TensorInfo outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
357 armnn::TensorInfo weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32);
358 armnn::TensorInfo biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32);
359
360 std::vector<float> weights =
361 {
362 .5f, 2.f, .5f,
363 .5f, 2.f, 1.f,
364 .5f, 2.f, 2.f,
365 .5f, 2.f, 3.f,
366 .5f, 2.f, 4.f
367 };
368
369 FullyConnectedDescriptor descriptor;
370 descriptor.m_BiasEnabled = biasEnabled;
371
372 if(explicitCheck)
373 {
374 if(!biasEnabled)
375 {
376 try
377 {
378 CreateFullyConnectedNetworkNoConnectedWeightsExplicit(inputTensorInfo,
379 outputTensorInfo,
380 biasesDesc,
381 descriptor);
382 FAIL("LayerValidationException should have been thrown");
383 }
384 catch (const LayerValidationException& exc)
385 {
386 CHECK(strcmp(exc.what(), "Tried to connect bias to FullyConnected layer when bias is not enabled: "
387 "Failed to connect to input slot 2 on FullyConnected layer "
388 "\"Fully_Connected\" as the slot does not exist or is unavailable") == 0);
389 }
390 }
391 else if (!connectedWeights)
392 {
393 armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedWeightsExplicit(inputTensorInfo,
394 outputTensorInfo,
395 biasesDesc,
396 descriptor);
397 CHECK(network);
398
399 // Create runtime in which test will run
400 IRuntime::CreationOptions options;
401 IRuntimePtr runtime(IRuntime::Create(options));
402
403 try
404 {
405 Optimize(*network, backends, runtime->GetDeviceSpec());
406 FAIL("LayerValidationException should have been thrown");
407 }
408 catch (const LayerValidationException& exc)
409 {
Matthew Sloyan5d7b0a32021-10-18 13:07:49 +0100410 CHECK(strcmp(exc.what(), "Fully_Connected layer weights not set: Input slot(s) 1 not connected "
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100411 "to an output slot on FullyConnected layer \"Fully_Connected\"") == 0);
412 }
413 }
414 else if (!connectedBias)
415 {
416 // Tests with constant weights.
417 ConstTensor weightsConstantTensor(weightsDesc, weights.data());
418
419 armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedBiasExplicit(inputTensorInfo,
420 outputTensorInfo,
421 weightsDesc,
422 weightsConstantTensor,
423 descriptor);
424 CHECK(network);
425
426 // Create runtime in which test will run
427 IRuntime::CreationOptions options;
428 IRuntimePtr runtime(IRuntime::Create(options));
429
430 try
431 {
432 Optimize(*network, backends, runtime->GetDeviceSpec());
433 FAIL("LayerValidationException should have been thrown");
434 }
435 catch (const LayerValidationException& exc)
436 {
Matthew Sloyan5d7b0a32021-10-18 13:07:49 +0100437 CHECK(strcmp(exc.what(), "Fully_Connected layer bias not set: Input slot(s) 2 not connected "
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100438 "to an output slot on FullyConnected layer \"Fully_Connected\"") == 0);
439 }
440 }
441 }
442 else if(!connectedWeights && !connectedBias)
443 {
444 armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedWeightsAndBias(inputTensorInfo,
445 outputTensorInfo,
446 descriptor);
447 CHECK(network);
448
449 // Create runtime in which test will run
450 IRuntime::CreationOptions options;
451 IRuntimePtr runtime(IRuntime::Create(options));
452
453 try
454 {
455 Optimize(*network, backends, runtime->GetDeviceSpec());
456 FAIL("LayerValidationException should have been thrown");
457 }
458 catch (const LayerValidationException& exc)
459 {
Matthew Sloyan5d7b0a32021-10-18 13:07:49 +0100460 CHECK(strcmp(exc.what(), "Fully_Connected layer weights and bias not set: Input slot(s) 1 & 2 not "
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100461 "connected to an output slot on FullyConnected layer \"Fully_Connected\"") == 0);
462 }
463
464 }
465 else if(!tensorInfoSet)
466 {
467 // Tests with constant weights.
468 ConstTensor weightsConstantTensor(weightsDesc, weights.data());
469
470 armnn::INetworkPtr network = CreateFullyConnectedNetworkNoTensorInfoConstWeights(inputTensorInfo,
471 outputTensorInfo,
472 weightsConstantTensor,
473 descriptor);
474 CHECK(network);
475
476 // Create runtime in which test will run
477 IRuntime::CreationOptions options;
478 IRuntimePtr runtime(IRuntime::Create(options));
479
480 try
481 {
482 Optimize(*network, backends, runtime->GetDeviceSpec());
483 FAIL("LayerValidationException should have been thrown");
484 }
485 catch (const LayerValidationException& exc)
486 {
487 CHECK(strcmp(exc.what(), "Output slot TensorInfo not set on Constant layer \"Weights\"") == 0);
488 }
489 }
490}
491
Sadik Armaganf0a6dec2021-03-25 07:46:55 +0000492} // anonymous namespace