blob: 75ea8e967ceb266af0ec12c60124bee9765782ef [file] [log] [blame]
Tim Hall79d07d22020-04-27 18:20:16 +01001/*
2 * Copyright (c) 2020 Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Licensed under the Apache License, Version 2.0 (the License); you may
7 * not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#define PY_SSIZE_T_CLEAN
20#include <Python.h>
Mauricio Briceno67e11f72021-05-05 12:47:28 +020021#include <numpy/ndarrayobject.h>
Tim Hall79d07d22020-04-27 18:20:16 +010022
23#include "mlw_decode.h"
24#include "mlw_encode.h"
25
Mauricio Briceno67e11f72021-05-05 12:47:28 +020026/* C extension wrapper for mlw_reorder_encode
27 *
28 * This method is exposed directly in python with the arguments with a
29 * prototype of the form:
30 *
31 * output = mlw_codec.reorder_encode(
32 * ifm_ublock_depth,
33 * ofm_ublock_depth,
34 * input,
35 * ofm_block_depth,
36 * is_depthwise,
37 * is_partkernel,
38 * ifm_bitdepth,
39 * decomp_h,
40 * decomp_w,
41 * verbose=0)
42 *
Fredrik Svedberg93d5c352021-05-11 13:51:47 +020043 * output: (bytearray, int)
Mauricio Briceno67e11f72021-05-05 12:47:28 +020044 */
45
46static PyObject *
47method_reorder_encode (PyObject *self, PyObject *args)
48{
49 /* Object to hold the input integer list. */
50 int ifm_ublock_depth;
51 int ofm_ublock_depth;
52 PyObject *input_object;
53 int ofm_block_depth;
54 int is_depthwise;
55 int is_partkernel;
56 int ifm_bitdepth;
57 int decomp_h;
58 int decomp_w;
59
60 /* Object to hold the input verbosity integer, the verbose argument
61 * is optional so defaulted to 0.
62 */
63 int verbose = 0;
64
65 /* Arguments to the method are delivered as a tuple, unpack the
66 * tuple to get the individual arguments, note the second is
67 * optional.
68 */
69 if (!PyArg_ParseTuple(args, "iiOiiiiii|i",
70 &ifm_ublock_depth,
71 &ofm_ublock_depth,
72 &input_object,
73 &ofm_block_depth,
74 &is_depthwise,
75 &is_partkernel,
76 &ifm_bitdepth,
77 &decomp_h,
78 &decomp_w,
79 &verbose))
80 return NULL;
81
Fredrik Svedberg93d5c352021-05-11 13:51:47 +020082 PyArrayObject* input_ndarray_object = (PyArrayObject*)PyArray_FROM_OTF(
Mauricio Briceno67e11f72021-05-05 12:47:28 +020083 input_object,
84 NPY_INT64,
85 NPY_ARRAY_ALIGNED);
86 if (input_ndarray_object == NULL)
87 {
88 return NULL;
89 }
90
91 if ((int)PyArray_NDIM(input_ndarray_object) < 4)
92 {
93 PyErr_SetString(PyExc_ValueError, "Invalid input shape");
94 return NULL;
95 }
96
97 int ofm_depth = (int)PyArray_DIM(input_ndarray_object, 0);
98 int kernel_height = (int)PyArray_DIM(input_ndarray_object, 1);
99 int kernel_width = (int)PyArray_DIM(input_ndarray_object, 2);
100 int ifm_depth = (int)PyArray_DIM(input_ndarray_object, 3);
101
102 int64_t* brick_weights = (int64_t*)PyArray_DATA(input_ndarray_object);
103 int brick_strides[4];
104 for (int i = 0; i < 4; i++)
105 {
106 brick_strides[i] = (int)PyArray_STRIDE(input_ndarray_object, i);
107 }
108 if ((unsigned)PyArray_ITEMSIZE(input_ndarray_object) != sizeof(int64_t))
109 {
110 PyErr_SetString(PyExc_ValueError, "Invalid input type");
111 return NULL;
112 }
113 uint8_t* output_buffer = NULL;
Fredrik Svedberg93d5c352021-05-11 13:51:47 +0200114 int64_t padded_length;
Mauricio Briceno67e11f72021-05-05 12:47:28 +0200115
116 int output_length = mlw_reorder_encode(
117 ifm_ublock_depth,
118 ofm_ublock_depth,
119 ofm_depth,
120 kernel_height,
121 kernel_width,
122 ifm_depth,
123 brick_strides,
124 brick_weights,
125 ofm_block_depth,
126 is_depthwise,
127 is_partkernel,
128 ifm_bitdepth,
129 decomp_h,
130 decomp_w,
131 &output_buffer,
132 &padded_length,
133 verbose);
134
Mauricio Briceno67e11f72021-05-05 12:47:28 +0200135 PyObject *output_byte_array = PyByteArray_FromStringAndSize((char*)output_buffer, output_length);
136 PyObject *padded_length_obj = Py_BuildValue("i", padded_length);
137
138 /* Discard the output buffer */
139 mlw_free_outbuf(output_buffer);
140
141 PyObject* ret = PyTuple_Pack(2, output_byte_array, padded_length_obj);
Fredrik Svedberg93d5c352021-05-11 13:51:47 +0200142
143 Py_DECREF(input_ndarray_object);
Mauricio Briceno67e11f72021-05-05 12:47:28 +0200144 Py_DECREF(output_byte_array);
145 Py_DECREF(padded_length_obj);
146 return ret;
147}
148
Tim Hall79d07d22020-04-27 18:20:16 +0100149/* C extension wrapper for mlw_encode
150 *
151 * This method is exposed directly in python with the arguments with a
152 * prototype of the form:
153 *
154 * output = mlw_codec.encode(input, verbose=0)
155 *
156 * input: [int]
157 * verbose: int
158 * output: bytearray
159 */
160
161static PyObject *
162method_encode (PyObject *self, PyObject *args)
163{
164 /* Object to hold the input integer list. */
165 PyObject *input_list_object;
166
167 /* Object to hold the input verbosity integer, the verbose argument
168 * is optional so defaulted to 0.
169 */
170 int verbose = 0;
171
172 /* Arguments to the method are delivered as a tuple, unpack the
173 * tuple to get the individual arguments, note the second is
174 * optional.
175 */
176 if (!PyArg_ParseTuple(args, "O|i", &input_list_object, &verbose))
177 return NULL;
178
179 /* Unpack the length of the input integer list. */
Louis Verhaard60232142021-01-22 14:11:15 +0100180 Py_ssize_t input_length = PyObject_Length (input_list_object);
181 if (input_length < 0) {
182 return NULL;
183 }
Tim Hall79d07d22020-04-27 18:20:16 +0100184
185 /* We need to marshall the integer list into an input buffer
186 * suitable for mlw_encode, use a temporary heap allocated buffer
187 * for that purpose.
188 */
189 int16_t *input_buffer = (int16_t *) malloc(sizeof(int16_t *) * input_length);
Mauricio Briceno67e11f72021-05-05 12:47:28 +0200190 uint8_t *output_buffer = NULL;
Tim Hall79d07d22020-04-27 18:20:16 +0100191 if (input_buffer == NULL)
192 return PyErr_NoMemory();
193
194 /* Unpack the input integer list into the temporary buffer.
195 */
196 for (int i = 0; i < input_length; i++)
197 {
198 PyObject *item;
199 item = PyList_GetItem(input_list_object, i);
Louis Verhaard60232142021-01-22 14:11:15 +0100200 long value = PyLong_AsLong(item);
201 if (value < -255 || value > 255) {
202 PyErr_SetString(PyExc_ValueError, "Input value out of bounds");
203 return NULL;
204 }
205 input_buffer[i] = value;
Tim Hall79d07d22020-04-27 18:20:16 +0100206 }
Louis Verhaard60232142021-01-22 14:11:15 +0100207 if (PyErr_Occurred() != NULL) {
208 PyErr_SetString(PyExc_ValueError, "Invalid input");
209 return NULL;
210 }
Tim Hall79d07d22020-04-27 18:20:16 +0100211
Tim Hall79d07d22020-04-27 18:20:16 +0100212 int output_length = mlw_encode(input_buffer, input_length, &output_buffer, verbose);
213
214 PyObject *output_byte_array = PyByteArray_FromStringAndSize ((char *) output_buffer, output_length);
215
216 /* Discard the temporary input and output buffers. */
217 free (input_buffer);
Mauricio Briceno67e11f72021-05-05 12:47:28 +0200218 mlw_free_outbuf(output_buffer);
Tim Hall79d07d22020-04-27 18:20:16 +0100219
220 return output_byte_array;
221}
222
223/* C extension wrapper for mlw_decode
224 *
225 * This method is exposed directly in python with the arguments with a
226 * prototype of the form:
227 *
228 * output = mlw_codec.decode(input, verbose=0)
229 *
230 * input: bytearray
231 * verbose: int
232 * output: [int]
233 */
234
235static PyObject *
236method_decode(PyObject *self, PyObject *args)
237{
238 /* Object to hold the input bytearray. */
239 PyObject *input_bytearray_object;
240
241 /* Object to hold the input verbosity integer, the verbose argument
242 * is optional so defaulted to 0.
243 */
244 int verbose = 0;
245
246 /* Arguments to the method are delivered as a tuple, unpack the
247 * tuple to get the individual arguments, note the second is
248 * optional.
249 */
250 if (!PyArg_ParseTuple(args, "Y|i", &input_bytearray_object, &verbose))
251 return NULL;
252
253 /* Unpack the input buffer and length from the bytearray object. */
254 uint8_t *input_buffer = (uint8_t *) PyByteArray_AsString(input_bytearray_object);
Louis Verhaard60232142021-01-22 14:11:15 +0100255 Py_ssize_t input_length = PyByteArray_Size(input_bytearray_object);
Tim Hall79d07d22020-04-27 18:20:16 +0100256
257 /* We don't know the output length required, we guess, but the guess
258 * will be too small, the mlw_decode call will do a resize (upwards)
259 * anyway.
260 */
Louis Verhaard60232142021-01-22 14:11:15 +0100261 int16_t *output_buffer = (int16_t *) malloc (input_length);
Tim Hall79d07d22020-04-27 18:20:16 +0100262 if (output_buffer == NULL)
263 return PyErr_NoMemory();
264
265 int output_length = mlw_decode (input_buffer, input_length, &output_buffer, verbose);
266
267 /* Construct a new integer list and marshall the output buffer
268 * contents into the list. */
269 PyObject *output_list = PyList_New(output_length);
270 for (int i = 0; i <output_length; i++)
271 PyList_SetItem (output_list, i, PyLong_FromLong (output_buffer[i]));
272
273 free (output_buffer);
274
275 return output_list;
276}
277
278/* mlw_codec method descriptors.
279 */
280
281static PyMethodDef mlw_methods[] = {
282 {"decode", method_decode, METH_VARARGS, "Python interface for decode"},
283 {"encode", method_encode, METH_VARARGS, "Python interface for encode"},
Mauricio Briceno67e11f72021-05-05 12:47:28 +0200284 {"reorder_encode", method_reorder_encode, METH_VARARGS, "Python interface for reorder and encode"},
Tim Hall79d07d22020-04-27 18:20:16 +0100285 {NULL, NULL, 0, NULL}
286};
287
288/* mlw_codec module descriptor.
289 */
290
291static struct PyModuleDef mlw_codecmodule = {
292 PyModuleDef_HEAD_INIT,
293 "mlw_codec",
294 "Python interface for the mlw encoder",
295 -1,
296 mlw_methods
297};
298
Mauricio Briceno67e11f72021-05-05 12:47:28 +0200299PyMODINIT_FUNC PyInit_mlw_codec(void)
300{
301 PyObject* ret = PyModule_Create(&mlw_codecmodule);
302 import_array();
303 return ret;
Tim Hall79d07d22020-04-27 18:20:16 +0100304}