blob: 0d2d2cb2de152108210d38d4cc23ce557db1a3e4 [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
Sadik Armagana097d2a2021-11-24 15:47:28 +00007#include <CommonTestUtils.hpp>
Sadik Armaganf0a6dec2021-03-25 07:46:55 +00008
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
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100113
114 ConstTensor biases;
115
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100116 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100117 armnn::IConnectableLayer* biasLayer = network->AddConstantLayer(biases, "Bias_Input");
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100118 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
119 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
120
121 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
122 Connect(biasLayer, fullyConnectedLayer, biasTensorInfo, 0, 2);
123 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
124
125 return network;
126}
127
128armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedWeightsAndBias(const armnn::TensorInfo& inputTensorInfo,
129 const armnn::TensorInfo& outputTensorInfo,
130 armnn::FullyConnectedDescriptor descriptor)
131{
132 armnn::INetworkPtr network(armnn::INetwork::Create());
133
134 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
135 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
136 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
137
138 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
139 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
140
141 return network;
142}
143
144armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedBiasExplicit(const armnn::TensorInfo& inputTensorInfo,
145 const armnn::TensorInfo& outputTensorInfo,
146 const armnn::TensorInfo& weightsTensorInfo,
147 const armnn::ConstTensor& weightsConstantTensor,
148 armnn::FullyConnectedDescriptor descriptor)
149{
150 armnn::INetworkPtr network(armnn::INetwork::Create());
151
152 armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
153 armnn::IConnectableLayer* weightsLayer = network->AddConstantLayer(weightsConstantTensor, "Weights");
154 armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
155 armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
156
157 Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
158 Connect(weightsLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
159 Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
160
161 return network;
162}
163
Sadik Armaganf0a6dec2021-03-25 07:46:55 +0000164template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
165void FullyConnectedWithDynamicWeightsEndToEnd(const std::vector<armnn::BackendId>& backends)
166{
167 using namespace armnn;
168
169 armnn::TensorInfo inputTensorInfo({ 1, 1, 2, 3 }, ArmnnType);
170 inputTensorInfo.SetQuantizationScale(0.1f);
171 inputTensorInfo.SetQuantizationOffset(63);
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100172 inputTensorInfo.SetConstant(true);
Sadik Armaganf0a6dec2021-03-25 07:46:55 +0000173
174 armnn::TensorInfo outputTensorInfo({ 1, 2 }, ArmnnType);
175 outputTensorInfo.SetQuantizationScale(5.f);
176 outputTensorInfo.SetQuantizationOffset(10);
177
178 armnn::TensorInfo weightsTensorInfo({ 2, 6 }, ArmnnType);
179 weightsTensorInfo.SetQuantizationScale(0.2f);
180 weightsTensorInfo.SetQuantizationOffset(93);
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100181 weightsTensorInfo.SetConstant(true);
Sadik Armaganf0a6dec2021-03-25 07:46:55 +0000182
183 FullyConnectedDescriptor descriptor;
184 descriptor.m_ConstantWeights = false;
185 descriptor.m_BiasEnabled = false;
186 descriptor.m_TransposeWeightMatrix = true;
187
188 std::vector<T> inputData {
189 -1.2f, 6.1f, -3.5f,
190 18.8f, -5.5f, 2.9f
191 };
192
193 std::vector<T> weightsData {
194 -8.4f, 20.0f, -10.4f, -8, 16.4f, -11.8f,
195 23.4f, 10.4f, -14.0f, -3.8f, -11.8f, 11.4f
196 };
197
198 std::vector<T> floatExpectedOutputData {
199 -107.04f, 110.f
200 };
201 std::vector<T> expectedOutputData = armnnUtils::QuantizedVector<T>(floatExpectedOutputData);
202
203 armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeights(inputTensorInfo,
204 outputTensorInfo,
205 weightsTensorInfo,
206 descriptor);
207
Sadik Armagan1625efc2021-06-10 18:24:34 +0100208 CHECK(network);
Sadik Armaganf0a6dec2021-03-25 07:46:55 +0000209
210 std::map<int, std::vector<T>> inputTensorData = {{ 0, inputData }, {1, weightsData}};
211 std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutputData }};
212
213 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
214 inputTensorData,
215 expectedOutputTensorData,
216 backends,
217 1.0f);
218}
Matthew Sloyan81beae32021-07-13 19:46:11 +0100219
220template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
221void FullyConnectedWithDynamicOrConstantInputsEndToEnd(const std::vector<armnn::BackendId>& backends,
222 const bool transposeWeights,
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100223 const bool constantWeightsOrBias)
Matthew Sloyan81beae32021-07-13 19:46:11 +0100224{
225 unsigned int inputWidth = 1;
226 unsigned int inputHeight = 1;
227 unsigned int inputChannels = 5;
228 unsigned int inputNum = 2;
229
230 unsigned int outputChannels = 3;
231 unsigned int outputNum = 2;
232
233 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
234 unsigned int outputShape[] = { outputNum, outputChannels };
235 unsigned int weightsShape[] = { inputChannels, outputChannels };
236
237 if (transposeWeights)
238 {
239 std::swap(weightsShape[0], weightsShape[1]);
240 }
241
242 unsigned int biasShape[] = { outputChannels };
243
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100244 armnn::TensorInfo inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32, 0.0f, 0, true);
Matthew Sloyan81beae32021-07-13 19:46:11 +0100245 armnn::TensorInfo outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100246 armnn::TensorInfo weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32, 0.0f, 0, true);
247 armnn::TensorInfo biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32, 0.0f, 0, true);
Matthew Sloyan81beae32021-07-13 19:46:11 +0100248
249 std::vector<float> input =
250 {
251 1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
252 5.0f, 4.0f, 3.0f, 2.0f, 1.0f
253 };
254
255 std::vector<float> weights =
256 {
257 .5f, 2.f, .5f,
258 .5f, 2.f, 1.f,
259 .5f, 2.f, 2.f,
260 .5f, 2.f, 3.f,
261 .5f, 2.f, 4.f
262 };
263
264 if (transposeWeights)
265 {
266 weights =
267 {
268 .5f, .5f, .5f, .5f, .5f,
269 2.f, 2.f, 2.f, 2.f, 2.f,
270 .5f, 1.f, 2.f, 3.f, 4.f
271 };
272 }
273
274 std::vector<float> biasValues = std::vector<float>({10.f, 20.f, 30.f});
275
276 std::vector<float> expectedOutput =
277 {
278 0.5f + 1.0f + 1.5f + 2.0f + 2.5f + biasValues[0],
279 2.0f + 4.0f + 6.0f + 8.0f + 10.f + biasValues[1],
280 0.5f + 2.0f + 6.0f + 12.f + 20.f + biasValues[2],
281
282 2.5f + 2.0f + 1.5f + 1.0f + 0.5f + biasValues[0],
283 10.0f + 8.0f + 6.0f + 4.0f + 2.f + biasValues[1],
284 2.5f + 4.0f + 6.0f + 6.f + 4.f + biasValues[2]
285 };
286
287 FullyConnectedDescriptor descriptor;
288 descriptor.m_BiasEnabled = true;
289 descriptor.m_TransposeWeightMatrix = transposeWeights;
290 descriptor.m_ConstantWeights = constantWeightsOrBias;
291
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100292 if (!constantWeightsOrBias)
Matthew Sloyan81beae32021-07-13 19:46:11 +0100293 {
294 // Tests non constant weights and constant bias.
295 ConstTensor biasConstantTensor(biasesDesc, biasValues.data());
296
297 armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeightsConstBias(inputTensorInfo,
298 outputTensorInfo,
299 weightsDesc,
300 biasesDesc,
301 biasConstantTensor,
302 descriptor);
303 CHECK(network);
304
305 std::map<int, std::vector<T>> inputTensorData = {{ 0, input }, {1, weights}};
306 std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutput }};
307
308 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
309 inputTensorData,
310 expectedOutputTensorData,
311 backends,
312 1.0f);
313 }
314 else
315 {
316 // Tests constant weights and non constant bias.
317 ConstTensor weightsConstantTensor(weightsDesc, weights.data());
318
319 armnn::INetworkPtr network = CreateFullyConnectedNetworkConstWeightsNonConstBias(inputTensorInfo,
320 outputTensorInfo,
321 weightsDesc,
322 biasesDesc,
323 weightsConstantTensor,
324 descriptor);
325 CHECK(network);
326
327 std::map<int, std::vector<T>> inputTensorData = {{ 0, input }, {2, biasValues}};
328 std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutput }};
329
330 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
331 inputTensorData,
332 expectedOutputTensorData,
333 backends,
334 1.0f);
335 }
336}
337
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100338template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
339void FullyConnectedErrorChecking(const std::vector<armnn::BackendId>& backends,
340 const bool explicitCheck,
341 const bool biasEnabled,
342 const bool connectedWeights,
343 const bool connectedBias,
344 const bool tensorInfoSet)
345{
346 unsigned int inputWidth = 1;
347 unsigned int inputHeight = 1;
348 unsigned int inputChannels = 5;
349 unsigned int inputNum = 2;
350
351 unsigned int outputChannels = 3;
352 unsigned int outputNum = 2;
353
354 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
355 unsigned int outputShape[] = { outputNum, outputChannels };
356 unsigned int weightsShape[] = { inputChannels, outputChannels };
357
358 unsigned int biasShape[] = { outputChannels };
359
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100360 armnn::TensorInfo inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32, 0.0f, 0, true);
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100361 armnn::TensorInfo outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100362 armnn::TensorInfo weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32, 0.0f, 0, true);
363 armnn::TensorInfo biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32, 0.0f, 0, true);
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100364
365 std::vector<float> weights =
366 {
367 .5f, 2.f, .5f,
368 .5f, 2.f, 1.f,
369 .5f, 2.f, 2.f,
370 .5f, 2.f, 3.f,
371 .5f, 2.f, 4.f
372 };
373
374 FullyConnectedDescriptor descriptor;
375 descriptor.m_BiasEnabled = biasEnabled;
376
377 if(explicitCheck)
378 {
379 if(!biasEnabled)
380 {
381 try
382 {
383 CreateFullyConnectedNetworkNoConnectedWeightsExplicit(inputTensorInfo,
384 outputTensorInfo,
385 biasesDesc,
386 descriptor);
387 FAIL("LayerValidationException should have been thrown");
388 }
389 catch (const LayerValidationException& exc)
390 {
391 CHECK(strcmp(exc.what(), "Tried to connect bias to FullyConnected layer when bias is not enabled: "
392 "Failed to connect to input slot 2 on FullyConnected layer "
393 "\"Fully_Connected\" as the slot does not exist or is unavailable") == 0);
394 }
395 }
396 else if (!connectedWeights)
397 {
398 armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedWeightsExplicit(inputTensorInfo,
399 outputTensorInfo,
400 biasesDesc,
401 descriptor);
402 CHECK(network);
403
404 // Create runtime in which test will run
405 IRuntime::CreationOptions options;
406 IRuntimePtr runtime(IRuntime::Create(options));
407
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100408 CHECK_THROWS_AS(Optimize(*network, backends, runtime->GetDeviceSpec()), LayerValidationException);
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100409 }
410 else if (!connectedBias)
411 {
412 // Tests with constant weights.
413 ConstTensor weightsConstantTensor(weightsDesc, weights.data());
414
415 armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedBiasExplicit(inputTensorInfo,
416 outputTensorInfo,
417 weightsDesc,
418 weightsConstantTensor,
419 descriptor);
420 CHECK(network);
421
422 // Create runtime in which test will run
423 IRuntime::CreationOptions options;
424 IRuntimePtr runtime(IRuntime::Create(options));
425
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100426 CHECK_THROWS_AS(Optimize(*network, backends, runtime->GetDeviceSpec()), LayerValidationException);
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100427 }
428 }
429 else if(!connectedWeights && !connectedBias)
430 {
431 armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedWeightsAndBias(inputTensorInfo,
432 outputTensorInfo,
433 descriptor);
434 CHECK(network);
435
436 // Create runtime in which test will run
437 IRuntime::CreationOptions options;
438 IRuntimePtr runtime(IRuntime::Create(options));
439
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100440 CHECK_THROWS_AS(Optimize(*network, backends, runtime->GetDeviceSpec()), LayerValidationException);
Cathal Corbettb8cc2b92021-10-08 14:43:11 +0100441 }
442 else if(!tensorInfoSet)
443 {
444 // Tests with constant weights.
445 ConstTensor weightsConstantTensor(weightsDesc, weights.data());
446
447 armnn::INetworkPtr network = CreateFullyConnectedNetworkNoTensorInfoConstWeights(inputTensorInfo,
448 outputTensorInfo,
449 weightsConstantTensor,
450 descriptor);
451 CHECK(network);
452
453 // Create runtime in which test will run
454 IRuntime::CreationOptions options;
455 IRuntimePtr runtime(IRuntime::Create(options));
456
457 try
458 {
459 Optimize(*network, backends, runtime->GetDeviceSpec());
460 FAIL("LayerValidationException should have been thrown");
461 }
462 catch (const LayerValidationException& exc)
463 {
464 CHECK(strcmp(exc.what(), "Output slot TensorInfo not set on Constant layer \"Weights\"") == 0);
465 }
466 }
467}
468
Sadik Armaganf0a6dec2021-03-25 07:46:55 +0000469} // anonymous namespace