Skip to content

Commit a8330da

Browse files
authored
Merge mli_hlp_create_subtensor_tests and docs update (PR#488)
mli_hlp_create_subtesor tests
2 parents 218052e + 79100ff commit a8330da

File tree

3 files changed

+400
-74
lines changed

3 files changed

+400
-74
lines changed

doc/documents/utility_functions/util_help_func.rst

Lines changed: 73 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ getting information from data structures and performing various operations on th
1616

1717
- :ref:`get_zero_offset_val`
1818

19-
..
20-
- :ref:`point_sub_tensor`
19+
- :ref:`create_sub_tensor`
2120

2221
- :ref:`num_of_accu_bits`
2322

@@ -224,88 +223,91 @@ Conditions:
224223

225224
- ``in`` must contain a valid data format
226225
- zero_idx must be less or equal to number of zero offset values in the tensor
227-
228-
.. _point_sub_tensor:
229226

230-
..
231-
Point to Sub-Tensor
232-
~~~~~~~~~~~~~~~~~~~
233227

234-
.. warning::
228+
.. _create_sub_tensor:
235229

236-
The interface of this function is subject to change. Avoid using it.
230+
Create Subtensor
231+
~~~~~~~~~~~~~~~~~~~
237232

238-
..
233+
This function points to sub tensors in the input tensor. This function can
234+
be considered as indexing in a multidimensional array without copying or
235+
used to create a slice/fragment of the input tensor without copying the data.
239236

240-
This function points to sub tensors in the input tensor. This function can
241-
be considered as indexing in a multidimensional array without copying or
242-
used to create a slice/fragment of the input tensor without copying the data.
237+
For example, given a HWC tensor, this function could be used to create a HWC
238+
tensor for the top half of the HW image for all channels.
243239

244-
For example, given a HWC tensor, this function could be used to create a HWC
245-
tensor for the top half of the HW image for all channels.
240+
The function prototype:
246241

247-
The configuration struct is defined as follows and the fields are explained in
248-
Table :ref:`t_mli_sub_tensor_cfg_desc`.
242+
.. code:: c
249243
250-
.. code:: c
244+
mli_status mli_hlp_create_subtensor(
245+
const mli_tensor *in,
246+
const mli_subtensor_cfg *cfg,
247+
mli_tensor *out);
248+
..
249+
250+
The configuration struct is defined as follows and the fields are explained in
251+
Table :ref:`t_mli_sub_tensor_cfg_desc`.
252+
253+
.. code:: c
251254
252-
typedef struct {
255+
typedef struct {
253256
uint32_t offset[MLI_MAX_RANK];
254257
uint32_t size[MLI_MAX_RANK];
255258
uint32_t sub_tensor_rank;
256-
} mli_sub_tensor_cfg;
257-
..
258-
259-
.. _t_mli_sub_tensor_cfg_desc:
260-
.. table:: mli_sub_tensor_cfg Structure Field Description
261-
:align: center
262-
:widths: auto
263-
264-
+---------------------+----------------+---------------------------------------------------------+
265-
| **Field Name** | **Type** | Description |
266-
+=====================+================+=========================================================+
267-
| | | Start coordinate in the input tensor. Values must |
268-
| ``offset`` | ``uint32_t[]`` | be smaller than the shape of the input tensor. Size |
269-
| | | of the array must be equal to the rank of the input |
270-
| | | tensor. |
271-
+---------------------+----------------+---------------------------------------------------------+
272-
| | | Size of the sub tensor in elements per dimension: |
273-
| ``size`` | ``uint32_t[]`` | |
274-
| | | Restrictions: Size[d] + offset[d] <= input->shape[d] |
275-
+---------------------+----------------+---------------------------------------------------------+
276-
| | | Rank of the sub tensor that is produced. Must be |
277-
| | | smaller or equal to the rank of the input tensor. If |
278-
| ``sub_tensor_rank`` | ``uint32_t`` | the ``sub_tensor_rank`` is smaller than the input rank, |
279-
| | | the dimensions with a size of 1 is removed in the |
280-
| | | output shape starting from the first dimension until |
281-
| | | the requested ``sub_tensor_rank`` value is reached. |
282-
+---------------------+----------------+---------------------------------------------------------+
283-
..
284-
285-
This function computes the new data pointer based on the offset vector and it sets
286-
the shape of the output tensor according to the size vector. The ``mem_stride`` fields
287-
are copied from the input to the output, so after this operation, the output tensor might
288-
not be a contiguous block of data.
289-
290-
The function also reduces the rank of the output tensor if requested by the
291-
configuration. Only the dimensions with a size of 1 can be removed. Data format and
292-
quantization parameters are copied from the input to the output tensor.
293-
294-
The capacity field of the output is the input capacity decremented with the same
295-
value as that used to increment the data pointer.
296-
297-
The function prototype:
298-
299-
.. code:: c
300-
301-
mli_status mli_hlp_subtensor(
302-
const mli_tensor *in,
303-
const mli_subtensor_cfg *cfg,
304-
mli_tensor *out);
305-
..
259+
} mli_sub_tensor_cfg;
260+
..
261+
262+
.. _t_mli_sub_tensor_cfg_desc:
263+
.. table:: mli_sub_tensor_cfg Structure Field Description
264+
:align: center
265+
:widths: auto
306266

307-
Depending on the debug level (see section :ref:`err_codes`), this function performs a parameter
308-
check and returns the result as an ``mli_status`` code as described in section :ref:`kernl_sp_conf`.
267+
+---------------------+----------------+---------------------------------------------------------+
268+
| **Field Name** | **Type** | Description |
269+
+=====================+================+=========================================================+
270+
| | | Start coordinate in the input tensor. Values must |
271+
| ``offset`` | ``uint32_t[]`` | be smaller than the shape of the input tensor. Size |
272+
| | | of the array must be equal to the rank of the input |
273+
| | | tensor. |
274+
+---------------------+----------------+---------------------------------------------------------+
275+
| | | Size of the sub tensor in elements per dimension: |
276+
| ``size`` | ``uint32_t[]`` | |
277+
| | | Restrictions: size[d] + offset[d] <= input->shape[d] |
278+
+---------------------+----------------+---------------------------------------------------------+
279+
| | | Rank of the sub tensor that is produced. Must be |
280+
| | | smaller or equal to the rank of the input tensor. If |
281+
| ``sub_tensor_rank`` | ``uint32_t`` | the ``sub_tensor_rank`` is smaller than the input rank, |
282+
| | | the dimensions with a size of 1 is removed in the |
283+
| | | output shape starting from the first dimension until |
284+
| | | the requested ``sub_tensor_rank`` value is reached. |
285+
+---------------------+----------------+---------------------------------------------------------+
286+
..
287+
288+
This function computes the new data pointer based on the offset vector and it sets
289+
the shape of the output tensor according to the size vector. The ``mem_stride`` fields
290+
are copied from the input to the output, so after this operation, the output tensor might
291+
not be a contiguous block of data.
292+
293+
The function also reduces the rank of the output tensor if requested by the
294+
configuration. Only the dimensions with a size of 1 can be removed. Data format and
295+
quantization parameters are copied from the input to the output tensor.
296+
297+
The capacity field of the output is the input capacity decremented with the same
298+
value as that used to increment the data pointer.
299+
300+
301+
302+
Conditions:
303+
304+
- ``in`` tensor must be valid
305+
- ``cfg`` structure fields must be aligned with ``in`` tensor shape (see :ref:`t_mli_sub_tensor_cfg_desc`)
306+
- ``out`` must point to a tensor structure. It will be completely filled by the function.
307+
308+
309+
Depending on the debug level (see section :ref:`err_codes`), this function performs a parameter
310+
check and returns the result as an ``mli_status`` code as described in section :ref:`kernl_sp_conf`.
309311

310312

311313
.. _num_of_accu_bits:

user_tests/tests/mli_hlp_tensor_struct/tests_mli_hlp_tensor_struct.cc

Lines changed: 131 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,17 @@ using mli::tst::tensor_quantizer;
3737
using mli::tst::quality_metrics;
3838
using mli::tst::crc32_calc;
3939
using mli::tst::reporter_basic;
40+
using mli::tst::reporter_full;
4041
using mli::tst::memory_manager;
4142

4243
constexpr int kMemSize = 1000;
4344
static IO_DATA_ATTR int8_t scratch_mem_in[kMemSize] = { 0 };
45+
static IO_DATA_ATTR int8_t scratch_mem_out[kMemSize] = { 0 };
4446

4547
bool run_test_count_and_size();
4648
bool run_test_quant_params_getters();
4749
bool run_test_accu_bits_getters();
50+
bool run_test_create_subtensor();
4851

4952
// Main entry point. Running test procedues for various helpers
5053
//=============================================================
@@ -58,6 +61,9 @@ int main() {
5861
if (final_status == kSuccess)
5962
final_status = run_test_accu_bits_getters();
6063

64+
if (final_status == kSuccess)
65+
final_status = run_test_create_subtensor();
66+
6167
return (final_status == kSuccess) ? 0 : 1;
6268
}
6369

@@ -104,7 +110,7 @@ bool run_test_count_and_size() {
104110
if (is_test_passed && (tensor_quantizer::validate_tensor(input) != tensor_quantizer::kOk)) {
105111
is_test_passed = false;
106112
reporter.report_case(cur_test->descr,
107-
"At quantization step: more memory for in tensor is be required", is_test_passed);
113+
"At quantization step: more memory for in tensor is required", is_test_passed);
108114
}
109115

110116
if (is_test_passed && (mem_in_keeper.is_memory_corrupted())) {
@@ -132,7 +138,7 @@ bool run_test_count_and_size() {
132138
mem_in_keeper.is_memory_corrupted())) {
133139
is_test_passed = false;
134140
reporter.report_case(cur_test->descr,
135-
"At function run: memory is corrupted after functions invokation", is_test_passed);
141+
"At function run: memory is corrupted after functions invocation", is_test_passed);
136142
}
137143

138144
if (is_test_passed && cur_test->elem_size != cur_elem_size ) {
@@ -233,7 +239,7 @@ bool run_test_quant_params_getters() {
233239
mem_in_keeper.is_memory_corrupted())) {
234240
is_test_passed = false;
235241
reporter.report_case(cur_test->descr,
236-
"FAILED at func run: memory is corrupted after functions invokation", is_test_passed);
242+
"FAILED at func run: memory is corrupted after functions invocation", is_test_passed);
237243
}
238244

239245
if (is_test_passed) {
@@ -338,3 +344,125 @@ bool run_test_accu_bits_getters() {
338344
return test_status;
339345
}
340346

347+
// Tests procedure for mli_hlp_create_subtensor function.
348+
//=======================================================================
349+
struct create_subtensor_test_operands {
350+
const char* descr;
351+
tensor_quantizer in;
352+
tensor_quantizer out;
353+
mli_sub_tensor_cfg cfg;
354+
const quality_metrics threshold;
355+
const crc32_calc check_sum;
356+
};
357+
358+
// Checksums of test tensors for various mli calculations mode.
359+
// When developer finished implementation of kernel and consider it as ok, one needs to populate
360+
// proper checksums for tests in order to highlight any change which affects results.
361+
362+
const crc32_calc test_1_chksum_fx16 { 0x418F5ED6 }, test_1_chksum_fx8 { 0x0820E5D9 },
363+
test_1_chksum_sa8 { 0xBB54537D }, test_1_chksum_sa8_pa { 0x63BAA2A1 },
364+
test_1_chksum_sa32 { 0xDC93E12C }, test_1_chksum_sa32_pa { 0x98D5A2D6 },
365+
test_2_chksum_fx16 { 0xD7B05DED }, test_2_chksum_fx8 { 0x7582D890 },
366+
test_2_chksum_sa8 { 0x4CB81C56 }, test_2_chksum_sa8_pa { 0x2F0B06B8 },
367+
test_2_chksum_sa32 { 0x0B379B11 }, test_2_chksum_sa32_pa { 0x87591FC2 };
368+
369+
const quality_metrics thresholds_test_general { quality_metrics::kPassValueMaxAbsErr, quality_metrics::kPassValueSnr,
370+
quality_metrics::kPassValueSnrDb, /*QuantErrPerc = */100.0f};
371+
372+
bool run_test_create_subtensor() {
373+
bool test_status = true;
374+
const reporter_full reporter;
375+
static const create_subtensor_test_operands create_subtensor_tests_list[] = {
376+
{"FX16 tensor ", input_1_fx16, test_1_out_fx16, test_1_cfg,
377+
thresholds_test_general, test_1_chksum_fx16},
378+
{"FX8 tensor ", input_1_fx8, test_1_out_fx8, test_1_cfg,
379+
thresholds_test_general, test_1_chksum_fx8},
380+
{"SA8 tensor ", input_1_sa8, test_1_out_sa8, test_1_cfg,
381+
thresholds_test_general, test_1_chksum_sa8},
382+
{"SA8 tensor per axis", input_1_sa8_per_axis, test_1_out_sa8_per_axis, test_1_cfg,
383+
thresholds_test_general, test_1_chksum_sa8_pa},
384+
{"SA32 tensor ", input_1_sa32, test_1_out_sa32, test_1_cfg,
385+
thresholds_test_general, test_1_chksum_sa32},
386+
{"SA32 tensor per axis", input_1_sa32_per_axis, test_1_out_sa32_per_axis, test_1_cfg,
387+
thresholds_test_general, test_1_chksum_sa32_pa},
388+
{"FX16 tensor (rank & offset) ", input_1_fx16, test_2_out_fx16, test_2_cfg,
389+
thresholds_test_general, test_2_chksum_fx16},
390+
{"FX8 tensor (rank & offset)", input_1_fx8, test_2_out_fx8, test_2_cfg,
391+
thresholds_test_general, test_2_chksum_fx8},
392+
{"SA8 tensor (rank & offset)", input_1_sa8, test_2_out_sa8, test_2_cfg,
393+
thresholds_test_general, test_2_chksum_sa8},
394+
{"SA8 per axis (rank & offset)", input_1_sa8_per_axis, test_2_out_sa8_per_axis, test_2_cfg,
395+
thresholds_test_general, test_2_chksum_sa8_pa},
396+
{"SA32 tensor (rank & offset)", input_1_sa32, test_2_out_sa32, test_2_cfg,
397+
thresholds_test_general, test_2_chksum_sa32},
398+
{"SA32 per axis (rank & offset)", input_1_sa32_per_axis, test_2_out_sa32_per_axis, test_2_cfg,
399+
thresholds_test_general, test_2_chksum_sa32_pa}
400+
};
401+
constexpr int kTestsNum = sizeof(create_subtensor_tests_list) / sizeof(create_subtensor_tests_list[0]);
402+
403+
reporter.report_header("MLI|Helpers|Create Subtensor Tests");
404+
for (int i = 0; i < kTestsNum; ++i) {
405+
memory_manager mem_in_keeper((int8_t*)(scratch_mem_in), sizeof(scratch_mem_in));
406+
memory_manager mem_out_keeper((int8_t*)(scratch_mem_out), sizeof(scratch_mem_out));
407+
bool is_test_passed = true;
408+
const create_subtensor_test_operands* cur_test = &create_subtensor_tests_list[i];
409+
quality_metrics test_metrics;
410+
411+
if (!(cur_test->in.is_valid())) {
412+
is_test_passed = false;
413+
reporter.report_message(cur_test->descr, "At init: Bad source data for input tensor");
414+
}
415+
416+
const mli_tensor input = cur_test->in.get_quantized_tensor(mem_in_keeper.allocate_memory(cur_test->in));
417+
mli_tensor out = { 0 };
418+
if (is_test_passed &&
419+
tensor_quantizer::validate_tensor(input) != tensor_quantizer::kOk) {
420+
is_test_passed = false;
421+
reporter.report_message(cur_test->descr,
422+
"At quantization step: more memory for in tensor is required");
423+
}
424+
425+
if (is_test_passed &&
426+
mem_in_keeper.is_memory_corrupted()) {
427+
is_test_passed = false;
428+
reporter.report_message(cur_test->descr,
429+
"At quantization step: memory beside one of operands is corrupted");
430+
}
431+
432+
// Run specific kernel for test
433+
crc32_calc data_crc_before, data_crc_after;
434+
if (is_test_passed) {
435+
data_crc_before(input);
436+
if (mli_hlp_create_subtensor(&input, &cur_test->cfg, &out) != MLI_STATUS_OK) {
437+
is_test_passed = false;
438+
reporter.report_message(cur_test->descr, "FAILED at kernel run: kernel returned bad status");
439+
}
440+
data_crc_after(input);
441+
}
442+
443+
if (is_test_passed &&
444+
(data_crc_before.get() != data_crc_after.get() ||
445+
mem_in_keeper.is_memory_corrupted())) {
446+
is_test_passed = false;
447+
reporter.report_message(cur_test->descr,
448+
"At function run: memory is corrupted after functions invocation");
449+
}
450+
451+
if (is_test_passed &&
452+
test_metrics.calculate_metrics(out, cur_test->out) == false) {
453+
reporter.report_message(cur_test->descr, "FAILED at comparison output with reference");
454+
is_test_passed = false;
455+
}
456+
457+
if (is_test_passed) {
458+
crc32_calc data_crc;
459+
data_crc(input);
460+
data_crc(out);
461+
is_test_passed &= reporter.evaluate_and_report_case(cur_test->descr, test_metrics, cur_test->threshold,
462+
data_crc, cur_test->check_sum);
463+
}
464+
test_status &= is_test_passed;
465+
}
466+
reporter.report_outline("[AUTO] Group: mli_hlp_create_subtensor", test_status);
467+
return test_status;
468+
}

0 commit comments

Comments
 (0)