Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
numpy_interface.cpp
Go to the documentation of this file.
1 /*
2 Copyright 2020 Peter Rakyta, Ph.D.
3 
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 
16 */
17 
18 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
19 #include <Python.h>
20 #include <numpy/arrayobject.h>
21 #include "matrix.h"
22 #include "matrix_float.h"
23 #include "matrix_real.h"
24 #include "matrix_real_float.h"
25 
26 
31 void capsule_cleanup(PyObject* capsule) {
32 
33  void *memory = PyCapsule_GetPointer(capsule, NULL);
34  // I'm going to assume your memory needs to be freed with free().
35  // If it needs different cleanup, perform whatever that cleanup is
36  // instead of calling free().
37  scalable_aligned_free(memory);
38 
39 
40 }
41 
42 
43 
44 
52 PyObject* array_from_ptr(void * ptr, int dim, npy_intp* shape, int np_type) {
53 
54  if (PyArray_API == NULL) {
55  import_array();
56  }
57 
58 
59  // create numpy array
60  PyObject* arr = PyArray_SimpleNewFromData(dim, shape, np_type, ptr);
61 
62  // set memory keeper for the numpy array
63  if (ptr != NULL) {
64  PyObject *capsule = PyCapsule_New(ptr, NULL, capsule_cleanup);
65  PyArray_SetBaseObject((PyArrayObject *) arr, capsule);
66  }
67 
68  return arr;
69 
70 }
71 
72 
73 
78 PyObject* matrix_to_numpy( Matrix &mtx ) {
79  // initialize Numpy API
80  import_array();
81 
82 
83  npy_intp shape[2];
84  shape[0] = (npy_intp) mtx.rows;
85  shape[1] = (npy_intp) mtx.cols;
86 
87  QGD_Complex16* data = mtx.get_data();
88  return array_from_ptr( (void*) data, 2, shape, NPY_COMPLEX128);
89 
90 
91 }
92 
93 
99  // initialize Numpy API
100  import_array();
101 
102 
103  npy_intp shape[2];
104  shape[0] = (npy_intp) mtx.rows;
105  shape[1] = (npy_intp) mtx.cols;
106 
107  QGD_Complex8* data = mtx.get_data();
108  return array_from_ptr( (void*) data, 2, shape, NPY_COMPLEX64);
109 
110 
111 }
112 
113 
114 
120  // initialize Numpy API
121  import_array();
122 
123  npy_intp shape[2];
124 
125  // TODO: this logic should be implemented on higher level of API logic in the interfaces.
126  // fix this issue after redesigning the decomposition interfaces
127  int dims;
128  if ( mtx.rows == 1 ) {
129  dims = 1;
130  shape[0] = mtx.cols;
131  }
132  else if ( mtx.cols == 1 ) {
133  dims = 1;
134  shape[0] = mtx.rows;
135  }
136  else {
137  dims = 2;
138  shape[0] = (npy_intp) mtx.rows;
139  shape[1] = (npy_intp) mtx.cols;
140  }
141 
142  double* data = mtx.get_data();
143  return array_from_ptr( (void*) data, dims, shape, NPY_DOUBLE);
144 
145 
146 }
147 
148 
154  // initialize Numpy API
155  import_array();
156 
157  npy_intp shape[2];
158 
159  int dims;
160  if ( mtx.rows == 1 ) {
161  dims = 1;
162  shape[0] = mtx.cols;
163  }
164  else if ( mtx.cols == 1 ) {
165  dims = 1;
166  shape[0] = mtx.rows;
167  }
168  else {
169  dims = 2;
170  shape[0] = (npy_intp) mtx.rows;
171  shape[1] = (npy_intp) mtx.cols;
172  }
173 
174  float* data = mtx.get_data();
175  return array_from_ptr( (void*) data, dims, shape, NPY_FLOAT32);
176 
177 
178 }
179 
180 
181 
187  // initialize Numpy API
188  import_array();
189 
190 
191  npy_intp shape[2];
192  shape[0] = (npy_intp) mtx.rows;
193  shape[1] = (npy_intp) mtx.cols;
194 
195  int8_t* data = mtx.get_data();
196  return array_from_ptr( (void*) data, 2, shape, NPY_INT8);
197 
198 
199 }
200 
201 
202 
203 
207 Matrix
208 numpy2matrix(PyArrayObject* arr) {
209 
210  if ( (PyObject*)arr == Py_None ) {
211  return Matrix(0,0);
212  }
213 
214 #ifdef DEBUG
215  // test C-style contiguous memory allocation of the arrays
216  // in production this case has to be handled outside
217  assert( PyArray_IS_C_CONTIGUOUS(arr) && "array is not memory contiguous" );
218 #endif
219 
220  // get the pointer to the data stored in the input matrices
221  QGD_Complex16* data = (QGD_Complex16*)PyArray_DATA(arr);
222 
223  // get the dimensions of the array self->C
224  int dim_num = PyArray_NDIM( arr );
225  npy_intp* dims = PyArray_DIMS(arr);
226 
227  // create PIC version of the input matrices
228  if (dim_num == 2) {
229  Matrix mtx = Matrix(data, static_cast<int>(dims[0]), static_cast<int>(dims[1]));
230  return mtx;
231  }
232  else if (dim_num == 1) {
233  Matrix mtx = Matrix(data, static_cast<int>(dims[0]), 1);
234  return mtx;
235  }
236  else {
237  std::string err( "numpy2matrix: Wrong matrix dimension was given");
238  throw err;
239  }
240 
241 
242 
243 }
244 
245 
250 numpy2matrix_float(PyArrayObject* arr) {
251 
252  if ( (PyObject*)arr == Py_None ) {
253  return Matrix_float(0,0);
254  }
255 
256 #ifdef DEBUG
257  assert( PyArray_IS_C_CONTIGUOUS(arr) && "array is not memory contiguous" );
258 #endif
259 
260  QGD_Complex8* data = (QGD_Complex8*)PyArray_DATA(arr);
261 
262  int dim_num = PyArray_NDIM( arr );
263  npy_intp* dims = PyArray_DIMS(arr);
264 
265  if (dim_num == 2) {
266  Matrix_float mtx = Matrix_float(data, static_cast<int>(dims[0]), static_cast<int>(dims[1]));
267  return mtx;
268  }
269  else if (dim_num == 1) {
270  Matrix_float mtx = Matrix_float(data, static_cast<int>(dims[0]), 1);
271  return mtx;
272  }
273  else {
274  std::string err( "numpy2matrix_float: Wrong matrix dimension was given");
275  throw err;
276  }
277 }
278 
279 
284 numpy2matrix_real(PyArrayObject* arr) {
285 
286 
287  if ( (PyObject*)arr == Py_None ) {
288  return Matrix_real(0,0);
289  }
290 
291 #ifdef DEBUG
292  // test C-style contiguous memory allocation of the arrays
293  // in production this case has to be handled outside
294  assert( PyArray_IS_C_CONTIGUOUS(arr) && "array is not memory contiguous" );
295 #endif
296 
297  // get the pointer to the data stored in the input matrices
298  double *data = (double *)PyArray_DATA(arr);
299 
300  // get the dimensions of the array self->C
301  int dim_num = PyArray_NDIM( arr );
302  npy_intp* dims = PyArray_DIMS(arr);
303 
304  // create PIC version of the input matrices
305  if (dim_num == 2) {
306  Matrix_real mtx = Matrix_real(data, static_cast<int>(dims[0]), static_cast<int>(dims[1]));
307  return mtx;
308  }
309  else if (dim_num == 1) {
310  Matrix_real mtx = Matrix_real(data, static_cast<int>(dims[0]), 1);
311  return mtx;
312  }
313  else {
314  std::string err( "numpy2matrix: Wrong matrix dimension was given");
315  throw err;
316  }
317 
318 
319 
320 }
321 
322 
327 numpy2matrix_real_float(PyArrayObject* arr) {
328 
329  if ( (PyObject*)arr == Py_None ) {
330  return Matrix_real_float(0,0);
331  }
332 
333 #ifdef DEBUG
334  assert( PyArray_IS_C_CONTIGUOUS(arr) && "array is not memory contiguous" );
335 #endif
336 
337  float *data = (float *)PyArray_DATA(arr);
338 
339  int dim_num = PyArray_NDIM( arr );
340  npy_intp* dims = PyArray_DIMS(arr);
341 
342  if (dim_num == 2) {
343  Matrix_real_float mtx = Matrix_real_float(data, static_cast<int>(dims[0]), static_cast<int>(dims[1]));
344  return mtx;
345  }
346  else if (dim_num == 1) {
347  Matrix_real_float mtx = Matrix_real_float(data, static_cast<int>(dims[0]), 1);
348  return mtx;
349  }
350  else {
351  std::string err( "numpy2matrix_real_float: Wrong matrix dimension was given");
352  throw err;
353  }
354 }
355 
356 
357 
358 
359 
Header file of single-precision complex array storage with automatic and thread safe reference counti...
Class to store single-precision real arrays and properties.
Structure type representing single-precision complex numbers.
Definition: QGDTypes.h:46
data
load the unitary from file
Definition: example.py:51
scalar * get_data() const
Call to get the pointer to the stored data.
PyObject * matrix_int8_to_numpy(matrix_base< int8_t > &mtx)
Call to make a numpy array from an instance of matrix_base<int8_t> class.
Matrix_real_float numpy2matrix_real_float(PyArrayObject *arr)
Call to create a PIC matrix_real_float representation of a numpy array.
PyObject * matrix_float_to_numpy(Matrix_float &mtx)
Call to make a numpy array from an instance of matrix_float class.
int rows
The number of rows.
Definition: matrix_base.hpp:42
int cols
The number of columns.
Definition: matrix_base.hpp:44
PyObject * matrix_to_numpy(Matrix &mtx)
Call to make a numpy array from an instance of matrix class.
Matrix numpy2matrix(PyArrayObject *arr)
Call to create a matrix representation of a numpy array.
Matrix_float numpy2matrix_float(PyArrayObject *arr)
Call to create a matrix_float representation of a numpy array.
Header file of complex array storage array with automatic and thread safe reference counting...
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
Double-precision complex matrix (float64).
Definition: matrix.h:38
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
Matrix_real numpy2matrix_real(PyArrayObject *arr)
Call to create a PIC matrix_real representation of a numpy array.
PyObject * matrix_real_to_numpy(Matrix_real &mtx)
Call to make a numpy array from an instance of matrix class.
void capsule_cleanup(PyObject *capsule)
Method to cleanup the memory when the python object becomes released.
PyObject * array_from_ptr(void *ptr, int dim, npy_intp *shape, int np_type)
Call to make a numpy array from data stored via void pointer.
PyObject * matrix_real_float_to_numpy(Matrix_real_float &mtx)
Call to make a numpy array from an instance of matrix_real_float class.
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:41