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