Release 18.08
[platform/upstream/armnn.git] / src / armnn / backends / WorkloadData.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
4 //
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
17 namespace armnn
18 {
19
20 //---------------------------------------------------------------
21 DataType GetBiasDataType(DataType inputDataType)
22 {
23     switch (inputDataType)
24     {
25         case DataType::Float16:
26             return DataType::Float16;
27         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
37 namespace
38 {
39
40 //---------------------------------------------------------------
41 //android ndk does not support std::to_string function.
42 template <typename T>
43 std::string to_string(T value)
44 {
45     std::ostringstream os;
46     os << value;
47     return os.str();
48 }
49
50 //---------------------------------------------------------------
51 void 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 //---------------------------------------------------------------
61 void 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 //---------------------------------------------------------------
75 void 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 //---------------------------------------------------------------
86 void 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 //---------------------------------------------------------------
97 void 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 //---------------------------------------------------------------
108 void 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 //---------------------------------------------------------------
119 void 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 //---------------------------------------------------------------
133 void 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 //---------------------------------------------------------------
144 void 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();
153     if (std::abs(biasTensor.GetQuantizationScale() - expectedScale) > 0.000000001f)
154     {
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 //---------------------------------------------------------------
165 void 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 //---------------------------------------------------------------
185 void 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.
225 void 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
244 void 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 //---------------------------------------------------------------
252 void 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 //---------------------------------------------------------------
297 void 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 //---------------------------------------------------------------
309 void 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 //---------------------------------------------------------------
324 void 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
343     //The dimensionality of all the windows has to match the dimensionality (not shape) of the input.
344     std::size_t inputDims = workloadInfo.m_InputTensorInfos[0].GetNumDimensions();
345     for(unsigned int w = 0; w < m_ViewOrigins.size(); ++w )
346     {
347         //Checks that the dimensionality of input is same as the split windows.
348         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 //---------------------------------------------------------------
372 void 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
404     //The dimensionality of all the windows has to match the dimensionality (not shape) of the output.
405     std::size_t outputDims = workloadInfo.m_OutputTensorInfos[0].GetNumDimensions();
406     for(unsigned int w = 0; w < m_ViewOrigins.size(); ++w )
407     {
408         //Checks that the dimensionality of output is same as the split windows.
409         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         }
420         //Checks that the merge windows are within the output tensor.
421         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 //---------------------------------------------------------------
434 void 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
461         // Validates type and quantization values.
462         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 //---------------------------------------------------------------
477 void 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
488 void 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 //---------------------------------------------------------------
503 void MultiplicationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
504 {
505     ValidateTwoInputs(workloadInfo, "MultiplicationQueueDescriptor");
506     ValidateSingleOutput(workloadInfo, "MultiplicationQueueDescriptor");
507
508     ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
509                                        workloadInfo.m_InputTensorInfos[1],
510                                        workloadInfo.m_OutputTensorInfos[0],
511                                        "MultiplicationQueueDescriptor",
512                                        "first input",
513                                        "second input");
514 }
515
516 void 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
544 void 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
570 void 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
583     //inputChannels * channelMultiplier should be equal to outputChannels.
584     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
612 void 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
637 void 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
646 void 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
654     // Resizes bilinear only changes width and height: batch and channel count must match.
655     {
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
678 void 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
697 void 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
711 void 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
728 void 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
741 void 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
752 void 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
758 void 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
780 void 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
801 } //namespace armnn