blob: 9fbdfe94c271b31c80c387d4098c719a0ec566fc [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5#include "WorkloadData.hpp"
6
7#include "CpuTensorHandle.hpp"
telsoa014fcda012018-03-09 14:13:49 +00008
9#include <algorithm>
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000010#include <iomanip>
telsoa014fcda012018-03-09 14:13:49 +000011#include <string>
12#include <sstream>
telsoa014fcda012018-03-09 14:13:49 +000013
14#include <boost/format.hpp>
15
16namespace armnn
17{
18
19//---------------------------------------------------------------
20DataType GetBiasDataType(DataType inputDataType)
21{
22 switch (inputDataType)
23 {
telsoa01c577f2c2018-08-31 09:22:23 +010024 case DataType::Float16:
25 return DataType::Float16;
telsoa014fcda012018-03-09 14:13:49 +000026 case DataType::Float32:
27 return DataType::Float32;
28 case DataType::QuantisedAsymm8:
29 return DataType::Signed32;
30 default:
31 BOOST_ASSERT_MSG(false, "Invalid input data type");
32 return DataType::Float32;
33 }
34}
35
36namespace
37{
38
39//---------------------------------------------------------------
40//android ndk does not support std::to_string function.
41template <typename T>
42std::string to_string(T value)
43{
44 std::ostringstream os;
45 os << value;
46 return os.str();
47}
48
49//---------------------------------------------------------------
50void ValidatePointer(const void* ptr, std::string const& descName, std::string const& paramName)
51{
52 if (!ptr)
53 {
54 throw InvalidArgumentException(descName + ": Invalid null pointer. The " +
55 paramName + " parameter must be set.");
56 }
57}
58
59//---------------------------------------------------------------
60void ValidateTensorShapesMatch(const TensorInfo& first,
61 const TensorInfo& second,
62 std::string const& descName,
63 std::string const& firstName,
64 std::string const& secondName)
65{
66 if (first.GetShape() != second.GetShape())
67 {
68 throw InvalidArgumentException(descName + ": "
69 + firstName + " & " + secondName + " must have identical shapes");
70 }
71}
72
73//---------------------------------------------------------------
74void ValidateNoInputs(const WorkloadInfo& workloadInfo, std::string const& descName)
75{
76 if (workloadInfo.m_InputTensorInfos.size() != 0)
77 {
78 throw InvalidArgumentException(descName +
79 ": Requires no inputs. " +
80 to_string(workloadInfo.m_InputTensorInfos.size()) + " has been provided.");
81 }
82}
83
84//---------------------------------------------------------------
85void ValidateSingleInput(const WorkloadInfo& workloadInfo, std::string const& descName)
86{
87 if (workloadInfo.m_InputTensorInfos.size() != 1)
88 {
89 throw InvalidArgumentException(descName +
90 ": Requires exactly one input. " +
91 to_string(workloadInfo.m_InputTensorInfos.size()) + " has been provided." );
92 }
93}
94
95//---------------------------------------------------------------
96void ValidateTwoInputs(const WorkloadInfo& workloadInfo, std::string const& descName)
97{
98 if (workloadInfo.m_InputTensorInfos.size() != 2)
99 {
100 throw InvalidArgumentException(descName +
101 ": Requires exactly two workloadInfo.m_InputTensorInfos. " +
102 to_string(workloadInfo.m_InputTensorInfos.size()) + " have been provided.");
103 }
104}
105
106//---------------------------------------------------------------
107void ValidateSingleOutput(const WorkloadInfo& workloadInfo, std::string const& descName)
108{
109 if (workloadInfo.m_OutputTensorInfos.size() != 1)
110 {
111 throw InvalidArgumentException(descName +
112 ": Requires exactly one output. " +
113 to_string(workloadInfo.m_OutputTensorInfos.size()) + " has been provided.");
114 }
115}
116
117//---------------------------------------------------------------
118void ValidateTensorNumDimensions(const TensorInfo& tensor,
119 std::string const& descName,
120 unsigned int numDimensions,
121 std::string const& tensorName)
122{
123 if (tensor.GetNumDimensions() != numDimensions)
124 {
125 throw InvalidArgumentException(descName + ": Expected " + to_string(numDimensions) + " but got " +
126 to_string(tensor.GetNumDimensions()) + " dimensions for " +
127 tensorName + " tensor.");
128 }
129}
130
131//---------------------------------------------------------------
132void ValidateTensorDataType(const TensorInfo& tensor, DataType dataType,
133 const std::string& descName, std::string const& tensorName)
134{
135 if (tensor.GetDataType() != dataType)
136 {
137 throw InvalidArgumentException(descName + ": Expected data type " + GetDataTypeName(dataType) + " but got " +
138 GetDataTypeName(tensor.GetDataType()) + " for " + tensorName + " tensor.");
139 }
140}
141
142//---------------------------------------------------------------
143void ValidateBiasTensorQuantization(const TensorInfo& biasTensor, const TensorInfo& inputTensorInfo,
144 const TensorInfo& weightsTensorInfo, const std::string& descName)
145{
146 if (biasTensor.GetQuantizationOffset() != 0)
147 {
148 throw InvalidArgumentException(descName + ": Expected zero quantization offset for bias tensor but got " +
149 to_string(biasTensor.GetQuantizationOffset()));
150 }
151 const float expectedScale = inputTensorInfo.GetQuantizationScale() * weightsTensorInfo.GetQuantizationScale();
telsoa01c577f2c2018-08-31 09:22:23 +0100152 if (std::abs(biasTensor.GetQuantizationScale() - expectedScale) > 0.000000001f)
telsoa014fcda012018-03-09 14:13:49 +0000153 {
154 // Print the float values with extra precision to see very small differences
155 std::stringstream msg;
156 msg << std::setprecision(10) << descName << ": Expected " << expectedScale <<
157 " quantization scale for bias tensor (the product of the input and weight scales), but got " <<
158 biasTensor.GetQuantizationScale();
159 throw InvalidArgumentException(msg.str());
160 }
161}
162
163//---------------------------------------------------------------
164void ValidateTensors(const std::vector<ITensorHandle*>& vec,
165 unsigned int numExpected,
166 const std::string& descName,
167 const std::string& varName)
168{
169 if (vec.empty() && numExpected > 0)
170 {
171 throw InvalidArgumentException(descName + ": Invalid empty " + varName + " array.");
172 }
173
174 for (unsigned int i = 0; i < numExpected; ++i)
175 {
176 if (!vec[i])
177 {
178 throw InvalidArgumentException(descName + ": Invalid NULL for " + varName + to_string(i));
179 }
180 }
181}
182
183//---------------------------------------------------------------
184void ValidateBroadcastTensorShapesMatch(const TensorInfo& first,
185 const TensorInfo& second,
186 const TensorInfo& output,
187 std::string const& descName,
188 std::string const& firstName,
189 std::string const& secondName)
190{
191 // Tensors must have the same number of dimensions in order to be explicit about which dimensions will get
192 // broadcasted.
193 if (first.GetNumDimensions() != second.GetNumDimensions())
194 {
195 throw InvalidArgumentException(descName + ": Tensors "
196 + firstName + " & " + secondName
197 + " must have the same number of dimensions in order to be broadcasted");
198 }
199 uint32_t numDims = first.GetNumDimensions();
200 std::vector<uint32_t> outputDims(numDims, 0u);
201 for (uint32_t i = 0; i < numDims; i++)
202 {
203 const bool dimsNotEqual = first.GetShape()[i] != second.GetShape()[i];
204 const bool dimsNotOne = (first.GetShape()[i] != 1) && (second.GetShape()[i] != 1);
205 if (dimsNotEqual && dimsNotOne)
206 {
207 throw InvalidArgumentException("Broadcasting is not possible for incompatible shapes");
208 }
209 outputDims[i] = std::max(first.GetShape()[i], second.GetShape()[i]);
210 }
211 TensorShape broadcastShape = TensorShape(boost::numeric_cast<unsigned int>(outputDims.size()), outputDims.data());
212 if (broadcastShape != output.GetShape())
213 {
214 throw InvalidArgumentException(descName + ": The tensor shape resulting from adding "
215 + firstName + " & " + secondName
216 + " does not match the output shape");
217 }
218}
219
220//---------------------------------------------------------------
221/// Validates that the output tensor's quantization scale is greater than the product
222/// of the two input tensors' quantization scales. This is a requirement of the implementation of
223/// the quantized multiplication.
224void ValidateTensorQuantizationMultiplier(const TensorInfo& inputTensor1, const TensorInfo& inputTensor2,
225 const TensorInfo& outputTensorInfo, std::string const& descName,
226 const std::string& inputTensor1Name, const std::string& inputTensor2Name, const std::string& outputTensorName)
227{
228 if (outputTensorInfo.GetDataType() == DataType::QuantisedAsymm8)
229 {
230 if (outputTensorInfo.GetQuantizationScale() <=
231 inputTensor1.GetQuantizationScale() * inputTensor2.GetQuantizationScale())
232 {
233 std::stringstream msg;
234 msg << descName << ": Quantization scale of " << outputTensorName << " is not greater than " <<
235 "the product of the " << inputTensor1Name << " and " << inputTensor2Name << " tensors";
236 throw InvalidArgumentException(msg.str());
237 }
238 }
239}
240
241} //namespace
242
243void QueueDescriptor::ValidateInputsOutputs(const std::string& descName,
244 unsigned int numExpectedIn, unsigned int numExpectedOut) const
245{
246 ValidateTensors(m_Inputs, numExpectedIn, descName, "input");
247 ValidateTensors(m_Outputs, numExpectedOut, descName, "output");
248}
249
250//---------------------------------------------------------------
251void MemCopyQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
252{
253 ValidateSingleInput(workloadInfo, "MemCopyQueueDescriptor");
254 ValidateSingleOutput(workloadInfo, "MemCopyQueueDescriptor");
255
256 if (workloadInfo.m_InputTensorInfos.size() != workloadInfo.m_OutputTensorInfos.size())
257 {
258 throw InvalidArgumentException(boost::str(
259 boost::format("Number of input infos (%1%) does not match the number of output infos (%2%)")
260 % workloadInfo.m_InputTensorInfos.size() % workloadInfo.m_OutputTensorInfos.size()));
261 }
262
263 for (std::size_t i = 0; i < workloadInfo.m_InputTensorInfos.size(); ++i)
264 {
265 if (workloadInfo.m_InputTensorInfos[i].GetNumElements() !=
266 workloadInfo.m_OutputTensorInfos[i].GetNumElements())
267 {
268 throw InvalidArgumentException(boost::str(
269 boost::format("Number of elements for tensor input and output %1% does not match")
270 % i ));
271 }
272 }
273
274 if (m_Inputs.size() != m_Outputs.size())
275 {
276 throw InvalidArgumentException(boost::str(
277 boost::format("Number of inputs (%1%) does not match the number of outputs (%2%)")
278 % m_Inputs.size() % m_Outputs.size()));
279 }
280
281 for (unsigned int i = 0; i < m_Inputs.size(); ++i)
282 {
283 if (!m_Inputs[i])
284 {
285 throw InvalidArgumentException(boost::str(boost::format("Invalid null input %1%") % i));
286 }
287
288 if (!m_Outputs[i])
289 {
290 throw InvalidArgumentException(boost::str(boost::format("Invalid null output %1%") % i));
291 }
292 }
293}
294
295//---------------------------------------------------------------
296void ActivationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
297{
298 ValidateSingleInput(workloadInfo, "ActivationQueueDescriptor");
299 ValidateSingleOutput(workloadInfo, "ActivationQueueDescriptor");
300 ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
301 workloadInfo.m_OutputTensorInfos[0],
302 "ActivationQueueDescriptor",
303 "input",
304 "output");
305}
306
307//---------------------------------------------------------------
308void SoftmaxQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
309{
310 ValidateSingleInput(workloadInfo, "SoftmaxQueueDescriptor");
311 ValidateSingleOutput(workloadInfo, "SoftmaxQueueDescriptor");
312 ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "SoftmaxQueueDescriptor", 2, "input");
313 ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "SoftmaxQueueDescriptor", 2, "output");
314
315 ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
316 workloadInfo.m_OutputTensorInfos[0],
317 "SoftmaxQueueDescriptor",
318 "input",
319 "output");
320}
321
322//---------------------------------------------------------------
323void SplitterQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
324{
325 ValidateSingleInput(workloadInfo, "SplitterQueueDescriptor");
326
327 if (workloadInfo.m_OutputTensorInfos.size() <= 0)
328 {
329 throw InvalidArgumentException("SplitterQueueDescriptor: At least one output needs to be provided.");
330 }
331
332 if (workloadInfo.m_OutputTensorInfos.size() != m_ViewOrigins.size())
333 {
334 throw InvalidArgumentException(
335 "SplitterQueueDescriptor: Number of split windows "
336 "has to match number of workloadInfo.m_OutputTensorInfos. "
337 "Number of windows: " +
338 to_string(m_ViewOrigins.size()) +
339 ". Number of workloadInfo.m_OutputTensorInfos: " + to_string(workloadInfo.m_OutputTensorInfos.size()));
340 }
341
telsoa01c577f2c2018-08-31 09:22:23 +0100342 //The dimensionality of all the windows has to match the dimensionality (not shape) of the input.
telsoa014fcda012018-03-09 14:13:49 +0000343 std::size_t inputDims = workloadInfo.m_InputTensorInfos[0].GetNumDimensions();
344 for(unsigned int w = 0; w < m_ViewOrigins.size(); ++w )
345 {
telsoa01c577f2c2018-08-31 09:22:23 +0100346 //Checks that the dimensionality of input is same as the split windows.
telsoa014fcda012018-03-09 14:13:49 +0000347 ViewOrigin const& e = m_ViewOrigins[w];
348 if (e.m_Origin.size() != inputDims)
349 {
350 throw InvalidArgumentException("SplitterQueueDescriptor: Window origin have to "
351 "have the same dimensionality as the input tensor. "
352 "Window origin (index: " +
353 to_string(w) + ") has " + to_string(e.m_Origin.size()) +
354 " dimensions, the input "
355 "tensor has " +
356 to_string(inputDims) + " dimensions.");
357 }
358 for (unsigned int i = 0; i < e.m_Origin.size(); ++i)
359 {
360 if (e.m_Origin[i] + workloadInfo.m_OutputTensorInfos[w].GetShape()[i] >
361 workloadInfo.m_InputTensorInfos[0].GetShape()[i])
362 {
363 throw InvalidArgumentException("SplitterQueueDescriptor: Window extent coordinates have to "
364 "be smaller or equal than the size of the input in that coord.");
365 }
366 }
367 }
368}
369
370//---------------------------------------------------------------
371void MergerQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
372{
373 ValidateSingleOutput(workloadInfo, "MergerQueueDescriptor");
374
375 if (m_Inputs.size() <= 0)
376 {
377 throw InvalidArgumentException("MergerQueueDescriptor: At least one input needs to be provided.");
378 }
379 if (m_Outputs.size() <= 0)
380 {
381 throw InvalidArgumentException("MergerQueueDescriptor: At least one output needs to be provided.");
382 }
383
384 if (workloadInfo.m_InputTensorInfos.size() <= 0)
385 {
386 throw InvalidArgumentException("MergerQueueDescriptor: At least one TensorInfo input needs to be provided.");
387 }
388 if (workloadInfo.m_OutputTensorInfos.size() <= 0)
389 {
390 throw InvalidArgumentException("MergerQueueDescriptor: At least one TensorInfo output needs to be provided.");
391 }
392
393 if (workloadInfo.m_InputTensorInfos.size() != m_ViewOrigins.size())
394 {
395 throw InvalidArgumentException(
396 "MergerQueueDescriptor: Number of split windows "
397 "has to match number of workloadInfo.m_InputTensorInfos. "
398 "Number of windows: " +
399 to_string(m_ViewOrigins.size()) +
400 ". Number of workloadInfo.m_InputTensorInfos: " + to_string(workloadInfo.m_InputTensorInfos.size()));
401 }
402
telsoa01c577f2c2018-08-31 09:22:23 +0100403 //The dimensionality of all the windows has to match the dimensionality (not shape) of the output.
telsoa014fcda012018-03-09 14:13:49 +0000404 std::size_t outputDims = workloadInfo.m_OutputTensorInfos[0].GetNumDimensions();
405 for(unsigned int w = 0; w < m_ViewOrigins.size(); ++w )
406 {
telsoa01c577f2c2018-08-31 09:22:23 +0100407 //Checks that the dimensionality of output is same as the split windows.
telsoa014fcda012018-03-09 14:13:49 +0000408 ViewOrigin const& e = m_ViewOrigins[w];
409 if (e.m_Origin.size() != outputDims)
410 {
411 throw InvalidArgumentException("MergerQueueDescriptor: Window origin have to "
412 "have the same dimensionality as the output tensor. "
413 "Window origin (index: " +
414 to_string(w) + ") has " + to_string(e.m_Origin.size()) +
415 " dimensions, the output "
416 "tensor has " +
417 to_string(outputDims) + " dimensions.");
418 }
telsoa01c577f2c2018-08-31 09:22:23 +0100419 //Checks that the merge windows are within the output tensor.
telsoa014fcda012018-03-09 14:13:49 +0000420 for (unsigned int i = 0; i < e.m_Origin.size(); ++i)
421 {
422 if (e.m_Origin[i] + workloadInfo.m_InputTensorInfos[w].GetShape()[i]
423 > workloadInfo.m_OutputTensorInfos[0].GetShape()[i])
424 {
425 throw InvalidArgumentException("MergerQueueDescriptor: Window extent coordinates have to "
426 "be smaller or equal than the size of the output in that coord.");
427 }
428 }
429 }
430}
431
432//---------------------------------------------------------------
433void FullyConnectedQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
434{
435 ValidateSingleInput(workloadInfo, "FullyConnectedQueueDescriptor");
436 ValidateSingleOutput(workloadInfo, "FullyConnectedQueueDescriptor");
437 ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "FullyConnectedQueueDescriptor", 2, "output");
438
439 if (!(workloadInfo.m_InputTensorInfos[0].GetNumDimensions() == 2 ||
440 workloadInfo.m_InputTensorInfos[0].GetNumDimensions() == 4))
441 {
442 throw InvalidArgumentException("FullyConnectedQueueDescriptor: Input tensor must have 2 or 4 dimensions.");
443 }
444
445 if (m_Weight == nullptr)
446 {
447 throw InvalidArgumentException("FullyConnectedQueueDescriptor: Weight tensor descriptor is missing.");
448 }
449
450 ValidateTensorNumDimensions(m_Weight->GetTensorInfo(), "FullyConnectedQueueDescriptor", 2, "weight");
451
452 if (m_Parameters.m_BiasEnabled)
453 {
454 if (m_Bias == nullptr)
455 {
456 throw InvalidArgumentException("FullyConnectedQueueDescriptor: Bias is enabled but "
457 "bias value tensor descriptor is missing.");
458 }
459
telsoa01c577f2c2018-08-31 09:22:23 +0100460 // Validates type and quantization values.
telsoa014fcda012018-03-09 14:13:49 +0000461 ValidateBiasTensorQuantization(m_Bias->GetTensorInfo(),
462 workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(), "FullyConnectedQueueDescriptor");
463
464 ValidateTensorDataType(m_Bias->GetTensorInfo(),
465 GetBiasDataType(workloadInfo.m_InputTensorInfos[0].GetDataType()),
466 "FullyConnectedQueueDescriptor", "bias");
467
468 ValidateTensorNumDimensions(m_Bias->GetTensorInfo(), "FullyConnectedQueueDescriptor", 1, "bias");
469 }
470
471 ValidateTensorQuantizationMultiplier(workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(),
472 workloadInfo.m_OutputTensorInfos[0], "FullyConnectedQueueDescriptor", "input", "weights", "output");
473}
474
475//---------------------------------------------------------------
476void NormalizationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
477{
478 ValidateSingleInput(workloadInfo, "NormalizationQueueDescriptor");
479 ValidateSingleOutput(workloadInfo, "NormalizationQueueDescriptor");
480 ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
481 workloadInfo.m_OutputTensorInfos[0],
482 "NormalizationQueueDescriptor",
483 "input",
484 "output");
485}
486
487void AdditionQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
488{
489 ValidateTwoInputs(workloadInfo, "AdditionQueueDescriptor");
490 ValidateSingleOutput(workloadInfo, "AdditionQueueDescriptor");
491
492 ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
493 workloadInfo.m_InputTensorInfos[1],
494 workloadInfo.m_OutputTensorInfos[0],
495 "AdditionQueueDescriptor",
496 "first input",
497 "second input");
498
499}
500
501//---------------------------------------------------------------
502void MultiplicationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
503{
504 ValidateTwoInputs(workloadInfo, "MultiplicationQueueDescriptor");
505 ValidateSingleOutput(workloadInfo, "MultiplicationQueueDescriptor");
surmeh01bceff2f2018-03-29 16:29:27 +0100506
507 ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
508 workloadInfo.m_InputTensorInfos[1],
509 workloadInfo.m_OutputTensorInfos[0],
510 "MultiplicationQueueDescriptor",
511 "first input",
512 "second input");
telsoa014fcda012018-03-09 14:13:49 +0000513}
514
515void BatchNormalizationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
516{
517 ValidateSingleInput(workloadInfo, "BatchNormalizationQueueDescriptor");
518 ValidateSingleOutput(workloadInfo, "BatchNormalizationQueueDescriptor");
519 ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
520 workloadInfo.m_OutputTensorInfos[0],
521 "BatchNormalizationQueueDescriptor",
522 "input",
523 "output");
524 ValidatePointer(m_Mean, "BatchNormalizationQueueDescriptor", "mean");
525 ValidatePointer(m_Variance, "BatchNormalizationQueueDescriptor", "variance");
526 ValidatePointer(m_Beta, "BatchNormalizationQueueDescriptor", "beta");
527 ValidatePointer(m_Gamma, "BatchNormalizationQueueDescriptor", "gamma");
528
529
530 ValidateTensorNumDimensions(m_Mean->GetTensorInfo(), "BatchNormalizationQueueDescriptor", 1, "mean");
531 ValidateTensorNumDimensions(m_Variance->GetTensorInfo(), "BatchNormalizationQueueDescriptor", 1, "variance");
532 ValidateTensorNumDimensions(m_Beta->GetTensorInfo(), "BatchNormalizationQueueDescriptor", 1, "beta");
533 ValidateTensorNumDimensions(m_Gamma->GetTensorInfo(), "BatchNormalizationQueueDescriptor", 1, "gamma");
534
535 ValidateTensorShapesMatch(
536 m_Mean->GetTensorInfo(), m_Variance->GetTensorInfo(), "BatchNormalizationQueueDescriptor", "mean", "variance");
537 ValidateTensorShapesMatch(
538 m_Mean->GetTensorInfo(), m_Beta->GetTensorInfo(), "BatchNormalizationQueueDescriptor", "mean", "beta");
539 ValidateTensorShapesMatch(
540 m_Mean->GetTensorInfo(), m_Gamma->GetTensorInfo(), "BatchNormalizationQueueDescriptor", "mean", "gamma");
541}
542
543void Convolution2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
544{
545 ValidateSingleInput(workloadInfo, "Convolution2dQueueDescriptor");
546 ValidateSingleOutput(workloadInfo, "Convolution2dQueueDescriptor");
547
548 ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "Convolution2dQueueDescriptor", 4, "input");
549 ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "Convolution2dQueueDescriptor", 4, "output");
550
551 ValidatePointer(m_Weight, "Convolution2dQueueDescriptor", "weight");
552 ValidateTensorNumDimensions(m_Weight->GetTensorInfo(), "Convolution2dQueueDescriptor", 4, "weight");
553 ValidateTensorDataType(m_Weight->GetTensorInfo(), workloadInfo.m_InputTensorInfos[0].GetDataType(),
554 "Convolution2dQueueDescriptor", "weight");
555 if (m_Parameters.m_BiasEnabled)
556 {
557 ValidateTensorNumDimensions(m_Bias->GetTensorInfo(), "Convolution2dQueueDescriptor", 1, "bias");
558 ValidateTensorDataType(m_Bias->GetTensorInfo(),
559 GetBiasDataType(workloadInfo.m_InputTensorInfos[0].GetDataType()),
560 "Convolution2dQueueDescriptor", "bias");
561 ValidateBiasTensorQuantization(m_Bias->GetTensorInfo(),
562 workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(), "Convolution2dQueueDescriptor");
563 }
564
565 ValidateTensorQuantizationMultiplier(workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(),
566 workloadInfo.m_OutputTensorInfos[0], "Convolution2dQueueDescriptor", "input", "weights", "output");
567}
568
569void DepthwiseConvolution2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
570{
571 ValidateSingleInput(workloadInfo, "DepthwiseConvolution2dQueueDescriptor");
572 ValidateSingleOutput(workloadInfo, "DepthwiseConvolution2dQueueDescriptor");
573
574 ValidateTensorNumDimensions(
575 workloadInfo.m_InputTensorInfos[0], "DepthwiseConvolution2dQueueDescriptor", 4, "input");
576 ValidateTensorNumDimensions(
577 workloadInfo.m_OutputTensorInfos[0], "DepthwiseConvolution2dQueueDescriptor", 4, "output");
578
579 ValidatePointer(m_Weight, "DepthwiseConvolution2dQueueDescriptor", "weight");
580 ValidateTensorNumDimensions(m_Weight->GetTensorInfo(), "DepthwiseConvolution2dQueueDescriptor", 4, "weight");
581
Nikhil Rajcec6b652018-10-12 13:51:57 +0100582 const unsigned int channelIndex = (m_Parameters.m_DataLayout == DataLayout::NCHW) ? 1 : 3;
583
telsoa01c577f2c2018-08-31 09:22:23 +0100584 //inputChannels * channelMultiplier should be equal to outputChannels.
telsoa014fcda012018-03-09 14:13:49 +0000585 const unsigned int numWeightChannelMultiplier = m_Weight->GetTensorInfo().GetShape()[0];
Nikhil Rajcec6b652018-10-12 13:51:57 +0100586 const unsigned int numWeightInputChannels = m_Weight->GetTensorInfo().GetShape()[channelIndex];
587 const unsigned int numWeightOutputChannels = workloadInfo.m_OutputTensorInfos[0].GetShape()[channelIndex];
telsoa014fcda012018-03-09 14:13:49 +0000588 if (numWeightChannelMultiplier * numWeightInputChannels != numWeightOutputChannels)
589 {
590 throw InvalidArgumentException(
591 boost::str(boost::format("DepthwiseConvolution2dQueueDescriptor: output_channels (provided %1%) should be "
592 "equal to input_channels (provided %2%) multiplied by channel_multiplier "
593 "(provided %3%).")
594 % numWeightOutputChannels % numWeightInputChannels % numWeightChannelMultiplier));
595 }
596
597 if (m_Parameters.m_BiasEnabled)
598 {
599 ValidatePointer(m_Bias, "DepthwiseConvolution2dQueueDescriptor", "bias");
600 ValidateTensorNumDimensions(m_Bias->GetTensorInfo(), "DepthwiseConvolution2dQueueDescriptor", 1, "bias");
601 ValidateBiasTensorQuantization(m_Bias->GetTensorInfo(),
602 workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(), "DepthwiseConvolution2dQueueDescriptor");
603
604 ValidateTensorDataType(m_Bias->GetTensorInfo(),
605 GetBiasDataType(workloadInfo.m_InputTensorInfos[0].GetDataType()),
606 "DepthwiseConvolution2dQueueDescriptor", "bias");
607 }
608
609 ValidateTensorQuantizationMultiplier(workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(),
610 workloadInfo.m_OutputTensorInfos[0], "DepthwiseConvolution2dQueueDescriptor", "input", "weights", "output");
611}
612
613void PermuteQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
614{
615 ValidateSingleInput(workloadInfo, "PermuteQueueDescriptor");
616 ValidateSingleOutput(workloadInfo, "PermuteQueueDescriptor");
617
618 const PermutationVector& mapping = m_Parameters.m_DimMappings;
619
620 const TensorInfo& input = workloadInfo.m_InputTensorInfos[0];
621 const TensorInfo& output = workloadInfo.m_OutputTensorInfos[0];
622
623 ValidateTensorNumDimensions(input, "PermuteQueueDescriptor", mapping.GetSize(), "input");
624 ValidateTensorNumDimensions(output, "PermuteQueueDescriptor", mapping.GetSize(), "output");
625
626 for (unsigned int i = 0; i < mapping.GetSize(); ++i)
627 {
628 if (input.GetShape()[i] != output.GetShape()[mapping[i]])
629 {
630 throw InvalidArgumentException("PermuteQueueDescriptor: src dimension " + to_string(i) +
631 " (=" + to_string(input.GetShape()[i]) + ") " +
632 "must match dst dimension " + to_string(mapping[i]) +
633 " (=" + to_string(output.GetShape()[mapping[i]]) + ")");
634 }
635 }
636}
637
638void Pooling2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
639{
640 ValidateSingleInput(workloadInfo, "Pooling2dQueueDescriptor");
641 ValidateSingleOutput(workloadInfo, "Pooling2dQueueDescriptor");
642
643 ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "Pooling2dQueueDescriptor", 4, "input");
644 ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "Pooling2dQueueDescriptor", 4, "output");
645}
646
647void ResizeBilinearQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
648{
649 ValidateSingleInput(workloadInfo, "ResizeBilinearQueueDescriptor");
650 ValidateSingleOutput(workloadInfo, "ResizeBilinearQueueDescriptor");
651
652 ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "ResizeBilinearQueueDescriptor", 4, "input");
653 ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "ResizeBilinearQueueDescriptor", 4, "output");
654
telsoa01c577f2c2018-08-31 09:22:23 +0100655 // Resizes bilinear only changes width and height: batch and channel count must match.
telsoa014fcda012018-03-09 14:13:49 +0000656 {
657 const unsigned int inputBatchSize = workloadInfo.m_InputTensorInfos[0].GetShape()[0];
658 const unsigned int outputBatchSize = workloadInfo.m_OutputTensorInfos[0].GetShape()[0];
659 if (inputBatchSize != outputBatchSize)
660 {
661 throw InvalidArgumentException(
662 boost::str(boost::format("ResizeBilinearQueueDescriptor: Input batch size (%1%) "
663 "does not match output batch size (%2%)") % inputBatchSize % outputBatchSize));
664 }
665 }
666
667 {
James Conroy59540822018-10-11 12:39:05 +0100668 const unsigned int inputChannelCount =
669 workloadInfo.m_InputTensorInfos[0].GetShape()[this->m_Parameters.m_DataLayout.GetChannelsIndex()];
670 const unsigned int outputChannelCount =
671 workloadInfo.m_OutputTensorInfos[0].GetShape()[this->m_Parameters.m_DataLayout.GetChannelsIndex()];
telsoa014fcda012018-03-09 14:13:49 +0000672 if (inputChannelCount != outputChannelCount)
673 {
674 throw InvalidArgumentException(
675 boost::str(boost::format("ResizeBilinearQueueDescriptor: Input channel count (%1%) "
676 "does not match output channel count (%2%)") % inputChannelCount % outputChannelCount));
677 }
678 }
679}
680
681void FakeQuantizationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
682{
683 ValidateSingleInput(workloadInfo, "FakeQuantizationQueueDescriptor");
684 ValidateSingleOutput(workloadInfo, "FakeQuantizationQueueDescriptor");
685
686 ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "FakeQuantizationQueueDescriptor", 2, "input");
687 ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "FakeQuantizationQueueDescriptor", 2, "output");
688 ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
689 workloadInfo.m_OutputTensorInfos[0],
690 "FakeQuantizationQueueDescriptor",
691 "input",
692 "output");
693 if (m_Parameters.m_Min > m_Parameters.m_Max)
694 {
695 throw InvalidArgumentException("FakeQuantizationQueueDescriptor: min cannot be greater than max");
696 }
697
698}
699
700void L2NormalizationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
701{
702 ValidateSingleInput(workloadInfo, "L2NormalizationQueueDescriptor");
703 ValidateSingleOutput(workloadInfo, "L2NormalizationQueueDescriptor");
704
705 ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "L2NormalizationQueueDescriptor", 4, "input");
706 ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "L2NormalizationQueueDescriptor", 4, "output");
707 ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
708 workloadInfo.m_OutputTensorInfos[0],
709 "L2NormalizationQueueDescriptor",
710 "input",
711 "output");
712}
713
714void ConstantQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
715{
716 ValidateNoInputs(workloadInfo, "ConstantQueueDescriptor");
717 ValidateSingleOutput(workloadInfo, "ConstantQueueDescriptor");
718
719 if (!m_LayerOutput)
720 {
721 throw InvalidArgumentException("ConstantQueueDescriptor: No const input specified");
722 }
723
724 ValidateTensorShapesMatch(m_LayerOutput->GetTensorInfo(),
725 workloadInfo.m_OutputTensorInfos[0],
726 "ConstantQueueDescriptor",
727 "constant",
728 "output");
729}
730
731void ReshapeQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
732{
733 ValidateSingleInput(workloadInfo, "ReshapeQueueDescriptor");
734 ValidateSingleOutput(workloadInfo, "ReshapeQueueDescriptor");
735
736 if (workloadInfo.m_InputTensorInfos[0].GetNumElements() != workloadInfo.m_OutputTensorInfos[0].GetNumElements())
737 {
738 throw InvalidArgumentException("ReshapeQueueDescriptor: Input tensor has " +
739 to_string(workloadInfo.m_InputTensorInfos[0].GetNumElements()) + " but output tensor has " +
740 to_string(workloadInfo.m_OutputTensorInfos[0].GetNumElements()) + " elements.");
741 }
742}
743
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +0000744void SpaceToBatchNdQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
745{
746 ValidateSingleInput(workloadInfo, "SpaceToBatchNdQueueDescriptor");
747 ValidateSingleOutput(workloadInfo, "SpaceToBatchNdQueueDescriptor");
748
749 ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "SpaceToBatchNdQueueDescriptor", 4, "input");
750 ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "SpaceToBatchNdQueueDescriptor", 4, "output");
751
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +0000752 if (m_Parameters.m_BlockShape.size() != 2)
753 {
Nattapat Chaimanowong3ea76d52018-11-09 14:10:38 +0000754 throw InvalidArgumentException("Block Shape must contain 2 spatial dimensions");
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +0000755 }
756
757 if (m_Parameters.m_BlockShape.size() != m_Parameters.m_PadList.size())
758 {
Nattapat Chaimanowong3ea76d52018-11-09 14:10:38 +0000759 throw InvalidArgumentException("Pad List must contain the same number of dimensions as Block Shape.");
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +0000760 }
761
762 const TensorShape inputShape = workloadInfo.m_InputTensorInfos[0].GetShape();
763
764 std::pair<unsigned int, unsigned int> heightPad = m_Parameters.m_PadList[0];
765 std::pair<unsigned int, unsigned int> widthPad = m_Parameters.m_PadList[1];
766
Nattapat Chaimanowong3ea76d52018-11-09 14:10:38 +0000767 unsigned int inputHeight = inputShape[m_Parameters.m_DataLayout.GetHeightIndex()]
768 + heightPad.first + heightPad.second;
769
770 unsigned int inputWidth = inputShape[m_Parameters.m_DataLayout.GetWidthIndex()]
771 + widthPad.first + widthPad.second;
772
773 unsigned int numInputElements = inputShape[0] * inputHeight * inputWidth
774 * inputShape[m_Parameters.m_DataLayout.GetChannelsIndex()];
775
776 if (workloadInfo.m_OutputTensorInfos[0].GetNumElements() != numInputElements)
777 {
778 throw InvalidArgumentException("SpaceToBatchNdQueueDescriptor: Input tensor has " +
779 to_string(numInputElements) + " after padding but output tensor has " +
780 to_string(workloadInfo.m_OutputTensorInfos[0].GetNumElements()) + " elements.");
781 }
782
783 if (inputHeight % m_Parameters.m_BlockShape[0] != 0 || inputWidth % m_Parameters.m_BlockShape[1] != 0)
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +0000784 {
785 throw InvalidArgumentException(
786 "Input shape after padding must be divisible by Block Shape in all spatial dimensions");
787 }
788}
789
telsoa014fcda012018-03-09 14:13:49 +0000790void FloorQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
791{
792 ValidateSingleInput(workloadInfo, "FloorQueueDescriptor");
793 ValidateSingleOutput(workloadInfo, "FlootQueueDescriptor");
794
795 if (workloadInfo.m_InputTensorInfos[0] != workloadInfo.m_OutputTensorInfos[0])
796 {
797 throw InvalidArgumentException("FloorQueueDescriptor: Input and output tensor infos do not match.");
798 }
799}
800
telsoa01c577f2c2018-08-31 09:22:23 +0100801void LstmQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
802{
803 ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "LstmQueueDescriptor", 2, "input");
804 ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "LstmQueueDescriptor", 2, "output");
805}
806
807void ConvertFp32ToFp16QueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
808{
809 ValidateSingleInput(workloadInfo, "ConvertFp32ToFp16QueueDescriptor");
810 ValidateSingleOutput(workloadInfo, "ConvertFp32ToFp16QueueDescriptor");
811
812 if (workloadInfo.m_InputTensorInfos[0].GetDataType() != DataType::Float32)
813 {
814 throw InvalidArgumentException("ConvertFp32ToFp16QueueDescriptor: Input tensor type must be Float32.");
815 }
816
817 if (workloadInfo.m_OutputTensorInfos[0].GetDataType() != DataType::Float16)
818 {
819 throw InvalidArgumentException("ConvertFp32ToFp16QueueDescriptor: Output tensor type must be Float16.");
820 }
821
822 ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
823 workloadInfo.m_OutputTensorInfos[0],
824 "ConvertFp32ToFp16QueueDescriptor",
825 "input",
826 "output");
827}
828
829void ConvertFp16ToFp32QueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
830{
831 ValidateSingleInput(workloadInfo, "ConvertFp16ToFp32QueueDescriptor");
832 ValidateSingleOutput(workloadInfo, "ConvertFp16ToFp32QueueDescriptor");
833
834 if (workloadInfo.m_InputTensorInfos[0].GetDataType() != DataType::Float16)
835 {
836 throw InvalidArgumentException("ConvertFp16ToFp32QueueDescriptor: Input tensor type must be Float16.");
837 }
838 if (workloadInfo.m_OutputTensorInfos[0].GetDataType() != DataType::Float32)
839 {
840 throw InvalidArgumentException("ConvertFp16ToFp32QueueDescriptor: Output tensor type must be Float32.");
841 }
842
843 ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
844 workloadInfo.m_OutputTensorInfos[0],
845 "ConvertFp16ToFp32QueueDescriptor",
846 "input",
847 "output");
848}
849
Francis Murtaghe7a86a42018-08-29 12:42:10 +0100850void DivisionQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
851{
852 ValidateTwoInputs(workloadInfo, "DivisionQueueDescriptor");
853 ValidateSingleOutput(workloadInfo, "DivisionQueueDescriptor");
854
855 ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
856 workloadInfo.m_InputTensorInfos[1],
857 workloadInfo.m_OutputTensorInfos[0],
858 "DivisionQueueDescriptor",
859 "first input",
860 "second input");
861}
862
David Beckc2044fe2018-09-05 15:00:38 +0100863void SubtractionQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
864{
865 ValidateTwoInputs(workloadInfo, "SubtractionQueueDescriptor");
866 ValidateSingleOutput(workloadInfo, "SubtractionQueueDescriptor");
867
868 ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
869 workloadInfo.m_InputTensorInfos[1],
870 workloadInfo.m_OutputTensorInfos[0],
871 "SubtractionQueueDescriptor",
872 "first input",
873 "second input");
874}
875
narpra01a6bf9122018-09-10 09:50:09 +0100876void MeanQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
877{
878 ValidateSingleInput(workloadInfo, "MeanQueueDescriptor");
879 ValidateSingleOutput(workloadInfo, "MeanQueueDescriptor");
narpra01eb061912018-09-10 17:35:27 +0100880
881 const TensorInfo& input = workloadInfo.m_InputTensorInfos[0];
882 const TensorInfo& output = workloadInfo.m_OutputTensorInfos[0];
883
narpra0132b90462018-09-13 11:07:48 +0100884 if (m_Parameters.m_KeepDims)
narpra01eb061912018-09-10 17:35:27 +0100885 {
886 ValidateTensorNumDimensions(output, "MeanQueueDescriptor", input.GetNumDimensions(), "output");
887 }
narpra0132b90462018-09-13 11:07:48 +0100888 else if (m_Parameters.m_Axis.empty())
narpra01eb061912018-09-10 17:35:27 +0100889 {
890 ValidateTensorNumDimensions(output, "MeanQueueDescriptor", 1, "output");
891 }
892 else
893 {
narpra0132b90462018-09-13 11:07:48 +0100894 auto outputDim = input.GetNumDimensions() - boost::numeric_cast<unsigned int>(m_Parameters.m_Axis.size());
narpra01eb061912018-09-10 17:35:27 +0100895 ValidateTensorNumDimensions(output,
896 "MeanQueueDescriptor",
897 outputDim > 0 ? outputDim : 1,
898 "output");
899 }
narpra01a6bf9122018-09-10 09:50:09 +0100900}
901
jimfly012c9322a2018-09-19 10:59:49 +0100902void PadQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
903{
904 ValidateSingleInput(workloadInfo, "PadQueueDescriptor");
905 ValidateSingleOutput(workloadInfo, "PadQueueDescriptor");
906
907 const TensorInfo& input = workloadInfo.m_InputTensorInfos[0];
Nina Drozd661dfa72018-10-02 11:14:17 +0100908 const TensorInfo& output = workloadInfo.m_OutputTensorInfos[0];
909
jimfly012c9322a2018-09-19 10:59:49 +0100910 // input and output should have the same number of dimensions
911 ValidateTensorNumDimensions(output, "PadQueueDescriptor", input.GetNumDimensions(), "output");
912 // there should be entry in the pad list for each dimension in the input tensor
913 if (m_Parameters.m_PadList.size() != input.GetNumDimensions()) {
914 throw InvalidArgumentException("Pad List should contain the same number of entries as there"
915 " are dimensions in the input tensor that is " +
916 to_string(input.GetNumDimensions()) + " entries " +
917 " not " + to_string(m_Parameters.m_PadList.size()) + " entries.");
918 }
919}
920
Éanna Ó Catháin4e1e1362018-11-12 11:36:34 +0000921void BatchToSpaceNdQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
922{
923 ValidateSingleInput(workloadInfo, "BatchToSpaceNdQueueDescriptor");
924 ValidateSingleOutput(workloadInfo, "BatchToSpaceNdQueueDescriptor");
925}
926
927} //namespace armnn