Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
qgd_Circuit_Wrapper.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:42:56 2020
3 Copyright 2020 Peter Rakyta, Ph.D.
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see http://www.gnu.org/licenses/.
19 
20 @author: Peter Rakyta, Ph.D.
21 */
26 #define PY_SSIZE_T_CLEAN
27 
28 #include <Python.h>
29 #include "structmember.h"
30 #include "Gates_block.h"
31 #include "CZ.h"
32 #include "CH.h"
33 #include "CNOT.h"
34 #include "U1.h"
35 #include "U2.h"
36 #include "U3.h"
37 #include "RX.h"
38 #include "R.h"
39 #include "RY.h"
40 #include "CRY.h"
41 #include "CRX.h"
42 #include "CRZ.h"
43 #include "CP.h"
44 #include "CCX.h"
45 #include "SWAP.h"
46 #include "CSWAP.h"
47 #include "CROT.h"
48 #include "CR.h"
49 #include "RZ.h"
50 #include "H.h"
51 #include "X.h"
52 #include "Y.h"
53 #include "Z.h"
54 #include "SX.h"
55 #include "SXdg.h"
56 #include "SYC.h"
57 #include "Adaptive.h"
58 #include "RXX.h"
59 #include "RYY.h"
60 #include "RZZ.h"
61 
62 #include "numpy_interface.h"
63 
64 #ifdef __DFE__
65 #include <numpy/arrayobject.h>
66 #include "numpy_interface.h"
67 #endif
68 
72 typedef struct {
73  PyObject_HEAD
76 } qgd_Gate;
77 
81 typedef struct qgd_Circuit_Wrapper {
82  PyObject_HEAD
86 
94  return new Gates_block(qbit_num);
95 }
96 
101 void
103  delete instance;
104  return;
105 }
106 
107 
108 
109 
110 
111 extern "C"
112 {
113 
118 static void
120 {
121 
122  // deallocate the instance of class N_Qubit_Decomposition
123  release_Circuit( self->circuit );
124 
125  Py_TYPE(self)->tp_free((PyObject *) self);
126 }
127 
132 static PyObject *
133 qgd_Circuit_Wrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
134 {
135 
136  // The tuple of expected keywords
137  static char *kwlist[] = {(char*)"qbit_num", NULL};
138 
139  // initiate variables for input arguments
140  int qbit_num = 0;
141 
142  // parsing input arguments
143  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &qbit_num)) {
144  std::string err( "Unable to parse arguments");
145  PyErr_SetString(PyExc_Exception, err.c_str());
146  return NULL;
147  }
148 
149  qgd_Circuit_Wrapper *self;
150  self = (qgd_Circuit_Wrapper *) type->tp_alloc(type, 0);
151 
152  if (self != NULL) {
153  self->circuit = create_Circuit( qbit_num );
154  }
155 
156  return (PyObject *) self;
157 }
158 
159 
167 static int
169 {
170  return 0;
171 }
172 
173 
177 static PyMemberDef qgd_Circuit_Wrapper_Members[] = {
178  {NULL} /* Sentinel */
179 };
180 
181 
182 #define qgd_Circuit_Wrapper_add_one_qubit_gate(gate_name, GATE_NAME)\
183 static PyObject * \
184 qgd_Circuit_Wrapper_add_##GATE_NAME(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds) \
185 {\
186  static char *kwlist[] = {(char*)"target_qbit", NULL};\
187 \
188  int target_qbit = -1; \
189 \
190  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist,\
191  &target_qbit))\
192  return Py_BuildValue("i", -1);\
193 \
194  if (target_qbit != -1 ) {\
195  self->circuit->add_##gate_name(target_qbit);\
196  }\
197 \
198  return Py_BuildValue("i", 0);\
199 }
200 
201 #define qgd_Circuit_Wrapper_add_qbit_only_gate(gate_name, GATE_NAME)\
202 static PyObject * \
203 qgd_Circuit_Wrapper_add_##GATE_NAME(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds) \
204 {\
205  (void)args;\
206  (void)kwds;\
207  self->circuit->add_##gate_name();\
208  return Py_BuildValue("i", 0);\
209 }
210 
211 #define qgd_Circuit_Wrapper_add_two_qubit_gate(gate_name, GATE_NAME)\
212 static PyObject * \
213 qgd_Circuit_Wrapper_add_##GATE_NAME(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds) \
214 {\
215  static char *kwlist[] = {(char*)"target_qbit", (char*)"control_qbit", NULL};\
216  int target_qbit = -1; \
217  int control_qbit = -1; \
218  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwlist,\
219  &target_qbit, &control_qbit))\
220  return Py_BuildValue("i", -1);\
221  if (target_qbit != -1 ) {\
222  self->circuit->add_##gate_name(target_qbit, control_qbit);\
223  }\
224  return Py_BuildValue("i", 0);\
225 }
226 
227 
229 
231 
233 
235 
237 
239 
241 
243 
245 
247 
249 
251 
253 
255 
257 
259 
261 
263 
265 
267 
269 
271 
273 
275 
277 
278 // SWAP gate now uses vector-based interface
279 static PyObject *
280 qgd_Circuit_Wrapper_add_SWAP(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
281 {
282  static char *kwlist[] = {(char*)"target_qbits", NULL};
283  PyObject* target_qbits_py = NULL;
284 
285  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &target_qbits_py))
286  return Py_BuildValue("i", -1);
287 
288  if (target_qbits_py != NULL && PyList_Check(target_qbits_py)) {
289  std::vector<int> target_qbits;
290  Py_ssize_t list_size = PyList_Size(target_qbits_py);
291  for (Py_ssize_t i = 0; i < list_size; i++) {
292  PyObject* item = PyList_GetItem(target_qbits_py, i);
293  target_qbits.push_back(PyLong_AsLong(item));
294  }
295  self->circuit->add_swap(target_qbits);
296 
297  }
298 
299  return Py_BuildValue("i", 0);
300 }
301 
302 static PyObject *
303 qgd_Circuit_Wrapper_add_RXX(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
304 {
305  static char *kwlist[] = {(char*)"target_qbits", NULL};
306  PyObject* target_qbits_py = NULL;
307 
308  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &target_qbits_py))
309  return Py_BuildValue("i", -1);
310 
311  if (target_qbits_py != NULL && PyList_Check(target_qbits_py)) {
312  std::vector<int> target_qbits;
313  Py_ssize_t list_size = PyList_Size(target_qbits_py);
314  for (Py_ssize_t i = 0; i < list_size; i++) {
315  PyObject* item = PyList_GetItem(target_qbits_py, i);
316  target_qbits.push_back(PyLong_AsLong(item));
317  }
318  self->circuit->add_rxx(target_qbits);
319 
320  }
321 
322  return Py_BuildValue("i", 0);
323 }
324 
325 static PyObject *
326 qgd_Circuit_Wrapper_add_RYY(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
327 {
328  static char *kwlist[] = {(char*)"target_qbits", NULL};
329  PyObject* target_qbits_py = NULL;
330 
331  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &target_qbits_py))
332  return Py_BuildValue("i", -1);
333 
334  if (target_qbits_py != NULL && PyList_Check(target_qbits_py)) {
335  std::vector<int> target_qbits;
336  Py_ssize_t list_size = PyList_Size(target_qbits_py);
337  for (Py_ssize_t i = 0; i < list_size; i++) {
338  PyObject* item = PyList_GetItem(target_qbits_py, i);
339  target_qbits.push_back(PyLong_AsLong(item));
340  }
341  self->circuit->add_ryy(target_qbits);
342 
343  }
344 
345  return Py_BuildValue("i", 0);
346 }
347 
348 static PyObject *
349 qgd_Circuit_Wrapper_add_RZZ(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
350 {
351  static char *kwlist[] = {(char*)"target_qbits", NULL};
352  PyObject* target_qbits_py = NULL;
353 
354  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &target_qbits_py))
355  return Py_BuildValue("i", -1);
356 
357  if (target_qbits_py != NULL && PyList_Check(target_qbits_py)) {
358  std::vector<int> target_qbits;
359  Py_ssize_t list_size = PyList_Size(target_qbits_py);
360  for (Py_ssize_t i = 0; i < list_size; i++) {
361  PyObject* item = PyList_GetItem(target_qbits_py, i);
362  target_qbits.push_back(PyLong_AsLong(item));
363  }
364  self->circuit->add_rzz(target_qbits);
365 
366  }
367 
368  return Py_BuildValue("i", 0);
369 }
370 
372 
374 
376 
377 qgd_Circuit_Wrapper_add_two_qubit_gate(adaptive, adaptive)
378 
379 
385 static PyObject *
386 qgd_Circuit_Wrapper_add_CCX(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
387 {
388 
389  // The tuple of expected keywords
390  static char *kwlist[] = {(char*)"target_qbit", (char*)"control_qbits", NULL};
391 
392  // initiate variables for input arguments
393  int target_qbit = -1;
394  PyObject* control_qbits_py = NULL;
395 
396  // parsing input arguments
397  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist,
398  &target_qbit, &control_qbits_py))
399  return Py_BuildValue("i", -1);
400 
401  // adding CCX gate to the end of the gate structure
402  if (target_qbit != -1 && control_qbits_py != NULL && PyList_Check(control_qbits_py)) {
403  std::vector<int> control_qbits;
404  Py_ssize_t list_size = PyList_Size(control_qbits_py);
405  for (Py_ssize_t i = 0; i < list_size; i++) {
406  PyObject* item = PyList_GetItem(control_qbits_py, i);
407  if (PyLong_Check(item)) {
408  control_qbits.push_back(PyLong_AsLong(item));
409  }
410  }
411  if (control_qbits.size() >= 2) {
412  self->circuit->add_ccx(target_qbit, control_qbits);
413  }
414  }
415 
416  return Py_BuildValue("i", 0);
417 
418 }
419 
420 
427 static PyObject *
428 qgd_Circuit_Wrapper_add_CSWAP(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
429 {
430 
431  // The tuple of expected keywords
432  static char *kwlist[] = {(char*)"target_qbits", (char*)"control_qbits", NULL};
433 
434  // initiate variables for input arguments
435  PyObject* target_qbits_py = NULL;
436  PyObject* control_qbits_py = NULL;
437 
438  // parsing input arguments
439  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist,
440  &target_qbits_py, &control_qbits_py))
441  return Py_BuildValue("i", -1);
442 
443  // adding CSWAP gate to the end of the gate structure
444  if (target_qbits_py != NULL && PyList_Check(target_qbits_py) &&
445  control_qbits_py != NULL && PyList_Check(control_qbits_py)) {
446 
447  std::vector<int> target_qbits;
448  Py_ssize_t target_size = PyList_Size(target_qbits_py);
449  for (Py_ssize_t i = 0; i < target_size; i++) {
450  PyObject* item = PyList_GetItem(target_qbits_py, i);
451  if (PyLong_Check(item)) {
452  target_qbits.push_back(PyLong_AsLong(item));
453  }
454  }
455 
456  std::vector<int> control_qbits;
457  Py_ssize_t control_size = PyList_Size(control_qbits_py);
458  for (Py_ssize_t i = 0; i < control_size; i++) {
459  PyObject* item = PyList_GetItem(control_qbits_py, i);
460  if (PyLong_Check(item)) {
461  control_qbits.push_back(PyLong_AsLong(item));
462  }
463  }
464 
465  if (target_qbits.size() >= 2 && control_qbits.size() >= 1) {
466  self->circuit->add_cswap(target_qbits, control_qbits);
467  }
468  }
469 
470  return Py_BuildValue("i", 0);
471 
472 }
473 
480 static PyObject *
482 {
483 
484  // initiate variables for input arguments
485  PyObject *Py_Circuit;
486 
487  // parsing input arguments
488  if (!PyArg_ParseTuple(args, "|O",
489  &Py_Circuit))
490  return Py_BuildValue("i", -1);
491 
492 
493  qgd_Circuit_Wrapper* qgd_op_block = (qgd_Circuit_Wrapper*) Py_Circuit;
494 
495 
496  // adding general gate to the end of the gate structure
497  self->circuit->add_gate( static_cast<Gate*>( qgd_op_block->circuit->clone() ) );
498 
499  return Py_BuildValue("i", 0);
500 
501 }
502 
503 
510 static PyObject *
511 qgd_Circuit_Wrapper_add_GENERAL(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
512 {
513 
514  static char *kwlist[] = {(char*)"operation_mtx", (char*)"target_qbits", (char*)"control_qbits", (char*)"is_f32", NULL};
515 
516  PyObject* operation_mtx_obj = NULL;
517  PyObject* target_qbits_py = NULL;
518  PyObject* control_qbits_py = NULL;
519  int is_f32 = 0;
520 
521  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOp", kwlist,
522  &operation_mtx_obj, &target_qbits_py, &control_qbits_py, &is_f32)) {
523  return Py_BuildValue("i", -1);
524  }
525 
526  if (operation_mtx_obj == NULL) {
527  PyErr_SetString(PyExc_ValueError, "operation_mtx must be provided");
528  return NULL;
529  }
530 
531  PyArrayObject* operation_mtx = NULL;
532  if (is_f32) {
533  operation_mtx = (PyArrayObject*)PyArray_FROM_OTF(operation_mtx_obj, NPY_COMPLEX64, NPY_ARRAY_IN_ARRAY);
534  } else {
535  operation_mtx = (PyArrayObject*)PyArray_FROM_OTF(operation_mtx_obj, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
536  }
537 
538  if (operation_mtx == NULL) {
539  return NULL;
540  }
541 
542  if (PyArray_NDIM(operation_mtx) != 2) {
543  Py_DECREF(operation_mtx);
544  PyErr_SetString(PyExc_ValueError, "operation_mtx must be a 2D square matrix");
545  return NULL;
546  }
547 
548  const npy_intp* shape = PyArray_DIMS(operation_mtx);
549  if (shape[0] != shape[1]) {
550  Py_DECREF(operation_mtx);
551  PyErr_SetString(PyExc_ValueError, "operation_mtx must be square");
552  return NULL;
553  }
554 
555  const int matrix_size = 1 << self->circuit->get_qbit_num();
556  if ((int)shape[0] != matrix_size) {
557  Py_DECREF(operation_mtx);
558  PyErr_SetString(PyExc_ValueError, "operation_mtx size does not match circuit qubit count");
559  return NULL;
560  }
561 
562  std::vector<int> target_qbits;
563  if (target_qbits_py != NULL && target_qbits_py != Py_None) {
564  if (!PyList_Check(target_qbits_py)) {
565  Py_DECREF(operation_mtx);
566  PyErr_SetString(PyExc_TypeError, "target_qbits must be a list of integers");
567  return NULL;
568  }
569 
570  Py_ssize_t list_size = PyList_Size(target_qbits_py);
571  for (Py_ssize_t i = 0; i < list_size; i++) {
572  PyObject* item = PyList_GetItem(target_qbits_py, i);
573  if (!PyLong_Check(item)) {
574  Py_DECREF(operation_mtx);
575  PyErr_SetString(PyExc_TypeError, "target_qbits must contain integers");
576  return NULL;
577  }
578  int qbit = (int)PyLong_AsLong(item);
579  if (qbit < 0 || qbit >= self->circuit->get_qbit_num()) {
580  Py_DECREF(operation_mtx);
581  PyErr_SetString(PyExc_ValueError, "target_qbits contains out-of-range index");
582  return NULL;
583  }
584  target_qbits.push_back(qbit);
585  }
586  }
587 
588  std::vector<int> control_qbits;
589  if (control_qbits_py != NULL && control_qbits_py != Py_None) {
590  if (!PyList_Check(control_qbits_py)) {
591  Py_DECREF(operation_mtx);
592  PyErr_SetString(PyExc_TypeError, "control_qbits must be a list of integers");
593  return NULL;
594  }
595 
596  Py_ssize_t list_size = PyList_Size(control_qbits_py);
597  for (Py_ssize_t i = 0; i < list_size; i++) {
598  PyObject* item = PyList_GetItem(control_qbits_py, i);
599  if (!PyLong_Check(item)) {
600  Py_DECREF(operation_mtx);
601  PyErr_SetString(PyExc_TypeError, "control_qbits must contain integers");
602  return NULL;
603  }
604  int qbit = (int)PyLong_AsLong(item);
605  if (qbit < 0 || qbit >= self->circuit->get_qbit_num()) {
606  Py_DECREF(operation_mtx);
607  PyErr_SetString(PyExc_ValueError, "control_qbits contains out-of-range index");
608  return NULL;
609  }
610  control_qbits.push_back(qbit);
611  }
612  }
613 
614  try {
615  Matrix operation_qgd;
616  if (is_f32) {
617  Matrix_float operation32 = numpy2matrix_float(operation_mtx);
618  operation_qgd = operation32.to_float64();
619  } else {
620  operation_qgd = numpy2matrix(operation_mtx);
621  }
622 
623  self->circuit->add_general_operation(operation_qgd, target_qbits, control_qbits);
624  }
625  catch (std::string err) {
626  Py_DECREF(operation_mtx);
627  PyErr_SetString(PyExc_Exception, err.c_str());
628  return NULL;
629  }
630  catch (...) {
631  Py_DECREF(operation_mtx);
632  PyErr_SetString(PyExc_Exception, "Failed to add GENERAL_OPERATION gate");
633  return NULL;
634  }
635 
636  Py_DECREF(operation_mtx);
637  return Py_BuildValue("i", 0);
638 }
639 
640 #ifdef __DFE__
641 
642 static PyObject*
643 DFEgateQGD_to_Python(DFEgate_kernel_type* DFEgates, int gatesNum)
644 {
645  PyObject* o = PyList_New(0);
646  for (int i = 0; i < gatesNum; i++) {
647  PyObject* gate = Py_BuildValue("iiibbbb", DFEgates[i].ThetaOver2, DFEgates[i].Phi,
648  DFEgates[i].Lambda, DFEgates[i].target_qbit, DFEgates[i].control_qbit,
649  DFEgates[i].gate_type, DFEgates[i].metadata);
650 
651  if (gate == NULL || PyList_Append(o, gate) != 0) {
652  Py_XDECREF(gate);
653  Py_DECREF(o);
654  delete [] DFEgates;
655  return NULL;
656  }
657 
658  Py_DECREF(gate);
659  }
660  delete [] DFEgates;
661  return o;
662 }
663 
664 static DFEgate_kernel_type*
665 DFEgatePython_to_QGD(PyObject* obj)
666 {
667  Py_ssize_t gatesNum = PyList_Size(obj); //assert type is list
668  DFEgate_kernel_type* DFEgates = new DFEgate_kernel_type[gatesNum];
669  for (Py_ssize_t i = 0; i < gatesNum; i++) {
670  PyObject* t = PyList_GetItem(obj, i);
671  //assert type is tuple and PyTuple_Size(t) == 7
672  DFEgates[i].ThetaOver2 = PyLong_AsLong(PyTuple_GetItem(t, 0));
673  DFEgates[i].Phi = PyLong_AsLong(PyTuple_GetItem(t, 1));
674  DFEgates[i].Lambda = PyLong_AsLong(PyTuple_GetItem(t, 2));
675  DFEgates[i].target_qbit = PyLong_AsLong(PyTuple_GetItem(t, 3));
676  DFEgates[i].control_qbit = PyLong_AsLong(PyTuple_GetItem(t, 4));
677  DFEgates[i].gate_type = PyLong_AsLong(PyTuple_GetItem(t, 5));
678  DFEgates[i].metadata = PyLong_AsLong(PyTuple_GetItem(t, 6));
679  }
680  return DFEgates;
681 }
682 
683 static PyObject *
684 qgd_Circuit_Wrapper_convert_to_DFE_gates_with_derivates(qgd_Circuit_Wrapper *self, PyObject *args)
685 {
686  bool only_derivates = false;
687  PyArrayObject* parameters_mtx_np = NULL;
688 
689  if (!PyArg_ParseTuple(args, "|Ob",
690  &parameters_mtx_np, &only_derivates))
691  return Py_BuildValue("");
692 
693  if ( parameters_mtx_np == NULL ) {
694  return Py_BuildValue("");
695  }
696 
697  PyArrayObject* parameters_mtx = (PyArrayObject*)PyArray_FROM_OTF((PyObject*)parameters_mtx_np, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
698 
699  // test C-style contiguous memory allocation of the array
700  if ( !PyArray_IS_C_CONTIGUOUS(parameters_mtx) ) {
701  std::cout << "parameters_mtx is not memory contiguous" << std::endl;
702  }
703 
704  // create QGD version of the parameters_mtx
705  Matrix_real parameters_mtx_mtx = numpy2matrix_real(parameters_mtx);
706 
707  int gatesNum = -1, gateSetNum = -1, redundantGateSets = -1;
708  DFEgate_kernel_type* ret = self->circuit->convert_to_DFE_gates_with_derivates(parameters_mtx_mtx, gatesNum, gateSetNum, redundantGateSets, only_derivates);
709  return Py_BuildValue("Oii", DFEgateQGD_to_Python(ret, gatesNum), gateSetNum, redundantGateSets);
710 }
711 
712 static PyObject *
713 qgd_Circuit_Wrapper_adjust_parameters_for_derivation(qgd_Circuit_Wrapper *self, PyObject *args)
714 {
715  int gatesNum = -1;
716  PyObject* dfegates = NULL;
717  if (!PyArg_ParseTuple(args, "|Oi",
718  &dfegates, &gatesNum))
719  return Py_BuildValue("");
720  int gate_idx = -1, gate_set_index = -1;
721  DFEgate_kernel_type* dfegates_qgd = DFEgatePython_to_QGD(dfegates);
722  self->circuit->adjust_parameters_for_derivation(dfegates_qgd, gatesNum, gate_idx, gate_set_index);
723  return Py_BuildValue("Oii", DFEgateQGD_to_Python(dfegates_qgd, gatesNum), gate_idx, gate_set_index);
724 }
725 
726 /*static PyObject *
727 qgd_Circuit_Wrapper_convert_to_DFE_gates(qgd_Circuit_Wrapper *self, PyObject *args)
728 {
729  PyObject* parameters_mtx_np = NULL;
730  if (!PyArg_ParseTuple(args, "|O",
731  &parameters_mtx_np))
732  return Py_BuildValue("");
733 
734  if ( parameters_mtx_np == NULL ) return Py_BuildValue("");
735  PyObject* parameters_mtx = PyArray_FROM_OTF(parameters_mtx_np, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
736 
737  // test C-style contiguous memory allocation of the array
738  if ( !PyArray_IS_C_CONTIGUOUS(parameters_mtx) ) {
739  std::cout << "parameters_mtx is not memory contiguous" << std::endl;
740  }
741 
742  // create QGD version of the parameters_mtx
743  Matrix_real parameters_mtx_mtx = numpy2matrix_real(parameters_mtx);
744 
745  int gatesNum = -1;
746  DFEgate_kernel_type* ret = self->circuit->convert_to_DFE_gates(parameters_mtx_mtx, gatesNum);
747  return DFEgateQGD_to_Python(ret, gatesNum);
748 }*/
749 
750 static PyObject *
751 qgd_Circuit_Wrapper_convert_to_DFE_gates(qgd_Circuit_Wrapper *self, PyObject *args)
752 {
753  int start_index = -1;
754  PyArrayObject* parameters_mtx_np = NULL;
755  PyObject* dfegates = NULL;
756 
757  if (!PyArg_ParseTuple(args, "|OOi",
758  &parameters_mtx_np, &dfegates, &start_index))
759  return Py_BuildValue("");
760 
761 
762  if ( parameters_mtx_np == NULL ) {
763  return Py_BuildValue("");
764  }
765 
766  PyArrayObject* parameters_mtx = (PyArrayObject*)PyArray_FROM_OTF((PyObject*)parameters_mtx_np, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
767 
768  // test C-style contiguous memory allocation of the array
769  if ( !PyArray_IS_C_CONTIGUOUS(parameters_mtx) ) {
770  std::cout << "parameters_mtx is not memory contiguous" << std::endl;
771  }
772 
773  // create QGD version of the parameters_mtx
774  Matrix_real parameters_mtx_mtx = numpy2matrix_real(parameters_mtx);
775  DFEgate_kernel_type* dfegates_qgd = DFEgatePython_to_QGD(dfegates);
776 
777  Py_ssize_t gatesNum = PyList_Size(dfegates);
778 
779  self->circuit->convert_to_DFE_gates(parameters_mtx_mtx, dfegates_qgd, start_index);
780 
781  return DFEgateQGD_to_Python(dfegates_qgd, gatesNum);
782 }
783 
784 #endif
785 
792 static PyObject *
793 qgd_Circuit_Wrapper_get_Matrix( qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds ) {
794 
795  PyArrayObject * parameters_arr = NULL;
796  int is_f32 = 0;
797 
798  static char *kwlist[] = {(char*)"", (char*)"is_f32", NULL};
799 
800  // parsing input arguments
801  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|p", kwlist, &parameters_arr, &is_f32))
802  return Py_BuildValue("i", -1);
803 
804  if (is_f32) {
805  if (!PyArray_IS_C_CONTIGUOUS(parameters_arr)) {
806  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF((PyObject*)parameters_arr, NPY_FLOAT, NPY_ARRAY_IN_ARRAY);
807  } else {
808  Py_INCREF(parameters_arr);
809  }
810  Matrix_real_float parameters_mtx = numpy2matrix_real_float(parameters_arr);
811  Matrix_float mtx = self->circuit->get_matrix(parameters_mtx);
812  mtx.set_owner(false);
813  PyObject *mtx_py = matrix_float_to_numpy(mtx);
814  Py_DECREF(parameters_arr);
815  return mtx_py;
816  }
817 
818  if (PyArray_IS_C_CONTIGUOUS(parameters_arr)) {
819  Py_INCREF(parameters_arr);
820  } else {
821  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF((PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
822  }
823 
824  // get the C++ wrapper around the data
825  Matrix_real parameters_mtx = numpy2matrix_real(parameters_arr);
826 
827  Matrix mtx = self->circuit->get_matrix(parameters_mtx);
828 
829  // convert to numpy array
830  mtx.set_owner(false);
831  PyObject *mtx_py = matrix_to_numpy(mtx);
832 
833  Py_DECREF(parameters_arr);
834 
835  return mtx_py;
836 }
837 
838 
839 
843 static PyObject *
845 
846  int parameter_num = self->circuit->get_parameter_num();
847 
848  return Py_BuildValue("i", parameter_num);
849 }
850 
851 
852 
860 static PyObject *
861 qgd_Circuit_Wrapper_apply_to( qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds ) {
862 
863  PyArrayObject * parameters_arr = NULL;
864  PyArrayObject * unitary_arg = NULL;
865 
866  int parallel = 1;
867  int is_f32 = 0;
868 
869  static char *kwlist[] = {(char*)"", (char*)"", (char*)"parallel", (char*)"is_f32", NULL};
870 
871 
872  // parsing input arguments
873  if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|ip", kwlist, &parameters_arr, &unitary_arg, &parallel, &is_f32 )) {
874  PyErr_SetString(PyExc_Exception, "Unable to parse input");
875  return NULL;
876  }
877 
878 
879 
880  if ( unitary_arg == NULL ) {
881  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
882  return NULL;
883  }
884 
885 
886  if ( parameters_arr == NULL ) {
887  PyErr_SetString(PyExc_Exception, "Parameters were not given");
888  return NULL;
889  }
890 
891 
892  if ( !PyArray_IS_C_CONTIGUOUS(unitary_arg) ) {
893  PyErr_SetString(PyExc_Exception, "input matrix is not memory contiguous");
894  return NULL;
895  }
896 
897 
898  // ---- float32 path ----
899  if (is_f32) {
900 
901  if ( PyArray_TYPE(unitary_arg) != NPY_COMPLEX64 ) {
902  PyErr_SetString(PyExc_TypeError, "input matrix or state should be complex64 when is_f32=True");
903  return NULL;
904  }
905 
906  if ( PyArray_TYPE(parameters_arr) != NPY_FLOAT ) {
907  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float32 when is_f32=True");
908  return NULL;
909  }
910 
911  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
912  Py_INCREF(parameters_arr);
913  }
914  else {
915  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_FLOAT, NPY_ARRAY_IN_ARRAY);
916  }
917 
918  Matrix_real_float parameters_mtx = numpy2matrix_real_float( parameters_arr );
919  Matrix_float input_mtx = numpy2matrix_float( unitary_arg );
920 
921  try {
922  self->circuit->apply_to( parameters_mtx, input_mtx, parallel );
923  }
924  catch (std::string err) {
925  Py_DECREF(parameters_arr);
926  PyErr_SetString(PyExc_Exception, err.c_str());
927  return NULL;
928  }
929  catch(...) {
930  Py_DECREF(parameters_arr);
931  std::string err( "Invalid pointer to circuit class");
932  PyErr_SetString(PyExc_Exception, err.c_str());
933  return NULL;
934  }
935 
936  if (input_mtx.data != PyArray_DATA(unitary_arg)) {
937  memcpy(PyArray_DATA(unitary_arg), input_mtx.data, input_mtx.size() * sizeof(QGD_Complex8));
938  }
939 
940  Py_DECREF(parameters_arr);
941  return Py_BuildValue("i", 0);
942  }
943 
944 
945  // ---- float64 path ----
946  if ( PyArray_TYPE(parameters_arr) != NPY_DOUBLE ) {
947  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float64 when is_f32=False");
948  return NULL;
949  }
950 
951 
952  if ( PyArray_TYPE(unitary_arg) != NPY_COMPLEX128 ) {
953  PyErr_SetString(PyExc_TypeError, "input matrix or state should be complex128 when is_f32=False");
954  return NULL;
955  }
956 
957 
958  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
959  Py_INCREF(parameters_arr);
960  }
961  else {
962  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
963  }
964 
965  // get the C++ wrapper around the data
966  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arr );
967 
968 
969  // create QGD version of the input matrix
970  Matrix unitary_mtx = numpy2matrix(unitary_arg);
971 
972  try {
973  self->circuit->apply_to( parameters_mtx, unitary_mtx, parallel );
974  }
975  catch (std::string err) {
976 
977  Py_DECREF(parameters_arr);
978 
979  PyErr_SetString(PyExc_Exception, err.c_str());
980  return NULL;
981  }
982  catch(...) {
983 
984  Py_DECREF(parameters_arr);
985 
986  std::string err( "Invalid pointer to circuit class");
987  PyErr_SetString(PyExc_Exception, err.c_str());
988  return NULL;
989  }
990 
991  if (unitary_mtx.data != PyArray_DATA(unitary_arg)) {
992  memcpy(PyArray_DATA(unitary_arg), unitary_mtx.data, unitary_mtx.size() * sizeof(QGD_Complex16));
993  }
994 
995  Py_DECREF(parameters_arr);
996 
997  return Py_BuildValue("i", 0);
998 }
999 
1000 
1001 
1009 static PyObject *
1010 qgd_Circuit_Wrapper_apply_from_right( qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds ) {
1011 
1012  PyArrayObject * parameters_arr = NULL;
1013  PyArrayObject * unitary_arg = NULL;
1014 
1015  int parallel = 1;
1016  int is_f32 = 0;
1017 
1018  static char *kwlist[] = {(char*)"", (char*)"", (char*)"parallel", (char*)"is_f32", NULL};
1019 
1020  if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|ip", kwlist, &parameters_arr, &unitary_arg, &parallel, &is_f32 )) {
1021  PyErr_SetString(PyExc_Exception, "Unable to parse input");
1022  return NULL;
1023  }
1024 
1025  if ( unitary_arg == NULL ) {
1026  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
1027  return NULL;
1028  }
1029 
1030  if ( parameters_arr == NULL ) {
1031  PyErr_SetString(PyExc_Exception, "Parameters were not given");
1032  return NULL;
1033  }
1034 
1035  if ( !PyArray_IS_C_CONTIGUOUS(unitary_arg) ) {
1036  PyErr_SetString(PyExc_Exception, "input matrix is not memory contiguous");
1037  return NULL;
1038  }
1039 
1040  // ---- float32 path ----
1041  if (is_f32) {
1042 
1043  if ( PyArray_TYPE(unitary_arg) != NPY_COMPLEX64 ) {
1044  PyErr_SetString(PyExc_TypeError, "input matrix should be complex64 when is_f32=True");
1045  return NULL;
1046  }
1047 
1048  if ( PyArray_TYPE(parameters_arr) != NPY_FLOAT ) {
1049  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float32 when is_f32=True");
1050  return NULL;
1051  }
1052 
1053  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1054  Py_INCREF(parameters_arr);
1055  }
1056  else {
1057  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_FLOAT, NPY_ARRAY_IN_ARRAY);
1058  }
1059 
1060  Matrix_real_float parameters_mtx = numpy2matrix_real_float( parameters_arr );
1061  Matrix_float input_mtx = numpy2matrix_float( unitary_arg );
1062 
1063  try {
1064  self->circuit->apply_from_right( parameters_mtx, input_mtx );
1065  }
1066  catch (std::string err) {
1067  Py_DECREF(parameters_arr);
1068  PyErr_SetString(PyExc_Exception, err.c_str());
1069  return NULL;
1070  }
1071  catch(...) {
1072  Py_DECREF(parameters_arr);
1073  std::string err( "Invalid pointer to circuit class");
1074  PyErr_SetString(PyExc_Exception, err.c_str());
1075  return NULL;
1076  }
1077 
1078  if (input_mtx.data != PyArray_DATA(unitary_arg)) {
1079  memcpy(PyArray_DATA(unitary_arg), input_mtx.data, input_mtx.size() * sizeof(QGD_Complex8));
1080  }
1081 
1082  Py_DECREF(parameters_arr);
1083  return Py_BuildValue("i", 0);
1084  }
1085 
1086  // ---- float64 path ----
1087  if ( PyArray_TYPE(parameters_arr) != NPY_DOUBLE ) {
1088  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float64 when is_f32=False");
1089  return NULL;
1090  }
1091 
1092  if ( PyArray_TYPE(unitary_arg) != NPY_COMPLEX128 ) {
1093  PyErr_SetString(PyExc_TypeError, "input matrix should be complex128 when is_f32=False");
1094  return NULL;
1095  }
1096 
1097  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1098  Py_INCREF(parameters_arr);
1099  }
1100  else {
1101  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
1102  }
1103 
1104  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arr );
1105  Matrix unitary_mtx = numpy2matrix( unitary_arg );
1106 
1107  try {
1108  self->circuit->apply_from_right( parameters_mtx, unitary_mtx );
1109  }
1110  catch (std::string err) {
1111  Py_DECREF(parameters_arr);
1112  PyErr_SetString(PyExc_Exception, err.c_str());
1113  return NULL;
1114  }
1115  catch(...) {
1116  Py_DECREF(parameters_arr);
1117  std::string err( "Invalid pointer to circuit class");
1118  PyErr_SetString(PyExc_Exception, err.c_str());
1119  return NULL;
1120  }
1121 
1122  if (unitary_mtx.data != PyArray_DATA(unitary_arg)) {
1123  memcpy(PyArray_DATA(unitary_arg), unitary_mtx.data, unitary_mtx.size() * sizeof(QGD_Complex16));
1124  }
1125 
1126  Py_DECREF(parameters_arr);
1127  return Py_BuildValue("i", 0);
1128 }
1129 
1130 
1131 
1136 static void
1138  Matrix* m = static_cast<Matrix*>( PyCapsule_GetPointer(cap, "squander.circuit_matrix_owner") );
1139  delete m;
1140 }
1141 
1146 static void
1148  Matrix_float* m = static_cast<Matrix_float*>( PyCapsule_GetPointer(cap, "squander.circuit_matrix_float_owner") );
1149  delete m;
1150 }
1151 
1152 
1160 static PyObject *
1161 qgd_Circuit_Wrapper_apply_to_list( qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds ) {
1162 
1163  static char *kwlist[] = {(char*)"inputs", (char*)"parameters", (char*)"parallel", (char*)"is_f32", NULL};
1164 
1165  PyObject * inputs_obj = NULL;
1166  PyArrayObject * parameters_arr = NULL;
1167  int parallel = 1;
1168  int is_f32 = 0;
1169 
1170  if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|ip", kwlist, &inputs_obj, &parameters_arr, &parallel, &is_f32 )) {
1171  PyErr_SetString(PyExc_Exception, "Unable to parse input");
1172  return NULL;
1173  }
1174 
1175  if ( inputs_obj == NULL ) {
1176  PyErr_SetString(PyExc_Exception, "Input list was not given");
1177  return NULL;
1178  }
1179 
1180  if ( parameters_arr == NULL ) {
1181  PyErr_SetString(PyExc_Exception, "Parameters were not given");
1182  return NULL;
1183  }
1184 
1185  PyObject* seq = PySequence_Fast(inputs_obj, "inputs must be a sequence of numpy arrays");
1186  if (seq == NULL) {
1187  return NULL;
1188  }
1189 
1190  const Py_ssize_t n = PySequence_Fast_GET_SIZE(seq);
1191  PyObject** items = PySequence_Fast_ITEMS(seq);
1192 
1193  // ---- float32 path ----
1194  if (is_f32) {
1195 
1196  if ( PyArray_TYPE(parameters_arr) != NPY_FLOAT ) {
1197  Py_DECREF(seq);
1198  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float32 when is_f32=True");
1199  return NULL;
1200  }
1201 
1202  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1203  Py_INCREF(parameters_arr);
1204  }
1205  else {
1206  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_FLOAT, NPY_ARRAY_IN_ARRAY);
1207  }
1208 
1209  Matrix_real_float parameters_mtx = numpy2matrix_real_float( parameters_arr );
1210 
1211  std::vector<Matrix_float> inputs;
1212  inputs.reserve((size_t)n);
1213 
1214  for (Py_ssize_t idx = 0; idx < n; idx++) {
1215  PyObject* item = items[idx];
1216  if (!PyArray_Check(item)) {
1217  Py_DECREF(seq);
1218  Py_DECREF(parameters_arr);
1219  PyErr_SetString(PyExc_TypeError, "All inputs should be numpy arrays");
1220  return NULL;
1221  }
1222 
1223  PyArrayObject* input = (PyArrayObject*)item;
1224  if ( PyArray_TYPE(input) != NPY_COMPLEX64 ) {
1225  Py_DECREF(seq);
1226  Py_DECREF(parameters_arr);
1227  PyErr_SetString(PyExc_TypeError, "All inputs should be complex64 when is_f32=True");
1228  return NULL;
1229  }
1230 
1231  if ( !PyArray_IS_C_CONTIGUOUS(input) ) {
1232  Py_DECREF(seq);
1233  Py_DECREF(parameters_arr);
1234  PyErr_SetString(PyExc_TypeError, "All inputs should be C-contiguous");
1235  return NULL;
1236  }
1237 
1238  inputs.push_back( numpy2matrix_float(input) );
1239  }
1240 
1241  try {
1242  self->circuit->apply_to_list( parameters_mtx, inputs, parallel );
1243  }
1244  catch (std::string err) {
1245  Py_DECREF(seq);
1246  Py_DECREF(parameters_arr);
1247  PyErr_SetString(PyExc_Exception, err.c_str());
1248  return NULL;
1249  }
1250  catch(...) {
1251  Py_DECREF(seq);
1252  Py_DECREF(parameters_arr);
1253  std::string err( "Invalid pointer to circuit class");
1254  PyErr_SetString(PyExc_Exception, err.c_str());
1255  return NULL;
1256  }
1257 
1258  // Copy back any data that was reallocated inside C++
1259  for (Py_ssize_t idx = 0; idx < n; idx++) {
1260  PyArrayObject* input = (PyArrayObject*)items[idx];
1261  Matrix_float& mtx = inputs[(size_t)idx];
1262  if (mtx.data != PyArray_DATA(input)) {
1263  memcpy(PyArray_DATA(input), mtx.data, mtx.size() * sizeof(QGD_Complex8));
1264  }
1265  }
1266 
1267  Py_DECREF(seq);
1268  Py_DECREF(parameters_arr);
1269  return Py_BuildValue("i", 0);
1270  }
1271 
1272  // ---- float64 path ----
1273  if ( PyArray_TYPE(parameters_arr) != NPY_DOUBLE ) {
1274  Py_DECREF(seq);
1275  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float64 when is_f32=False");
1276  return NULL;
1277  }
1278 
1279  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1280  Py_INCREF(parameters_arr);
1281  }
1282  else {
1283  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
1284  }
1285 
1286  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arr );
1287 
1288  std::vector<Matrix> inputs;
1289  inputs.reserve((size_t)n);
1290 
1291  for (Py_ssize_t idx = 0; idx < n; idx++) {
1292  PyObject* item = items[idx];
1293  if (!PyArray_Check(item)) {
1294  Py_DECREF(seq);
1295  Py_DECREF(parameters_arr);
1296  PyErr_SetString(PyExc_TypeError, "All inputs should be numpy arrays");
1297  return NULL;
1298  }
1299 
1300  PyArrayObject* input = (PyArrayObject*)item;
1301  if ( PyArray_TYPE(input) != NPY_COMPLEX128 ) {
1302  Py_DECREF(seq);
1303  Py_DECREF(parameters_arr);
1304  PyErr_SetString(PyExc_TypeError, "All inputs should be complex128 when is_f32=False");
1305  return NULL;
1306  }
1307 
1308  if ( !PyArray_IS_C_CONTIGUOUS(input) ) {
1309  Py_DECREF(seq);
1310  Py_DECREF(parameters_arr);
1311  PyErr_SetString(PyExc_TypeError, "All inputs should be C-contiguous");
1312  return NULL;
1313  }
1314 
1315  inputs.push_back( numpy2matrix(input) );
1316  }
1317 
1318  try {
1319  self->circuit->apply_to_list( parameters_mtx, inputs, parallel );
1320  }
1321  catch (std::string err) {
1322  Py_DECREF(seq);
1323  Py_DECREF(parameters_arr);
1324  PyErr_SetString(PyExc_Exception, err.c_str());
1325  return NULL;
1326  }
1327  catch(...) {
1328  Py_DECREF(seq);
1329  Py_DECREF(parameters_arr);
1330  std::string err( "Invalid pointer to circuit class");
1331  PyErr_SetString(PyExc_Exception, err.c_str());
1332  return NULL;
1333  }
1334 
1335  // Copy back any data that was reallocated inside C++
1336  for (Py_ssize_t idx = 0; idx < n; idx++) {
1337  PyArrayObject* input = (PyArrayObject*)items[idx];
1338  Matrix& mtx = inputs[(size_t)idx];
1339  if (mtx.data != PyArray_DATA(input)) {
1340  memcpy(PyArray_DATA(input), mtx.data, mtx.size() * sizeof(QGD_Complex16));
1341  }
1342  }
1343 
1344  Py_DECREF(seq);
1345  Py_DECREF(parameters_arr);
1346  return Py_BuildValue("i", 0);
1347 }
1348 
1349 
1357 static PyObject *
1358 qgd_Circuit_Wrapper_apply_derivate_to( qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds ) {
1359 
1360  static char *kwlist[] = {(char*)"parameters", (char*)"unitary", (char*)"parallel", (char*)"is_f32", NULL};
1361 
1362  PyArrayObject * parameters_arr = NULL;
1363  PyArrayObject * unitary_arg = NULL;
1364  int parallel = 1;
1365  int is_f32 = 0;
1366 
1367  if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|ip", kwlist, &parameters_arr, &unitary_arg, &parallel, &is_f32 )) {
1368  PyErr_SetString(PyExc_Exception, "Unable to parse input");
1369  return NULL;
1370  }
1371 
1372  if ( unitary_arg == NULL ) {
1373  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
1374  return NULL;
1375  }
1376 
1377  if ( parameters_arr == NULL ) {
1378  PyErr_SetString(PyExc_Exception, "Parameters were not given");
1379  return NULL;
1380  }
1381 
1382  if ( !PyArray_IS_C_CONTIGUOUS(unitary_arg) ) {
1383  PyErr_SetString(PyExc_Exception, "input matrix is not memory contiguous");
1384  return NULL;
1385  }
1386 
1387  // ---- float32 path ----
1388  if (is_f32) {
1389 
1390  if ( PyArray_TYPE(parameters_arr) != NPY_FLOAT ) {
1391  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float32 when is_f32=True");
1392  return NULL;
1393  }
1394 
1395  if ( PyArray_TYPE(unitary_arg) != NPY_COMPLEX64 ) {
1396  PyErr_SetString(PyExc_TypeError, "Input matrix should be complex64 when is_f32=True");
1397  return NULL;
1398  }
1399 
1400  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1401  Py_INCREF(parameters_arr);
1402  }
1403  else {
1404  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_FLOAT, NPY_ARRAY_IN_ARRAY);
1405  }
1406 
1407  Matrix_real_float parameters_mtx = numpy2matrix_real_float( parameters_arr );
1408  Matrix_float unitary_mtx = numpy2matrix_float( unitary_arg );
1409 
1410  std::vector<Matrix_float> derivs;
1411  try {
1412  derivs = self->circuit->apply_derivate_to( parameters_mtx, unitary_mtx, parallel );
1413  }
1414  catch (std::string err) {
1415  Py_DECREF(parameters_arr);
1416  PyErr_SetString(PyExc_Exception, err.c_str());
1417  return NULL;
1418  }
1419  catch(...) {
1420  Py_DECREF(parameters_arr);
1421  std::string err( "Invalid pointer to circuit class");
1422  PyErr_SetString(PyExc_Exception, err.c_str());
1423  return NULL;
1424  }
1425 
1426  PyObject* deriv_list = PyList_New((Py_ssize_t)derivs.size());
1427  if (deriv_list == NULL) {
1428  Py_DECREF(parameters_arr);
1429  return NULL;
1430  }
1431 
1432  for (Py_ssize_t idx = 0; idx < (Py_ssize_t)derivs.size(); ++idx) {
1433  Matrix_float* owned = new Matrix_float(derivs[(size_t)idx]);
1434  PyObject* cap = PyCapsule_New(
1435  owned, "squander.circuit_matrix_float_owner",
1437  if (cap == NULL) {
1438  delete owned;
1439  Py_DECREF(deriv_list);
1440  Py_DECREF(parameters_arr);
1441  return NULL;
1442  }
1443  npy_intp shape[2] = { (npy_intp)owned->rows, (npy_intp)owned->cols };
1444  PyObject* deriv_py = PyArray_SimpleNewFromData(
1445  2, shape, NPY_COMPLEX64, owned->get_data());
1446  if (deriv_py == NULL) {
1447  Py_DECREF(cap);
1448  Py_DECREF(deriv_list);
1449  Py_DECREF(parameters_arr);
1450  return NULL;
1451  }
1452  PyArray_SetBaseObject((PyArrayObject*)deriv_py, cap);
1453  PyList_SET_ITEM(deriv_list, idx, deriv_py);
1454  }
1455 
1456  Py_DECREF(parameters_arr);
1457  return deriv_list;
1458  }
1459 
1460  // ---- float64 path ----
1461  if ( PyArray_TYPE(parameters_arr) != NPY_DOUBLE ) {
1462  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float64 when is_f32=False");
1463  return NULL;
1464  }
1465 
1466  if ( PyArray_TYPE(unitary_arg) != NPY_COMPLEX128 ) {
1467  PyErr_SetString(PyExc_TypeError, "Input matrix should be complex128 when is_f32=False");
1468  return NULL;
1469  }
1470 
1471  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1472  Py_INCREF(parameters_arr);
1473  }
1474  else {
1475  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
1476  }
1477 
1478  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arr );
1479  Matrix unitary_mtx = numpy2matrix( unitary_arg );
1480 
1481  std::vector<Matrix> derivs;
1482  try {
1483  derivs = self->circuit->apply_derivate_to( parameters_mtx, unitary_mtx, parallel );
1484  }
1485  catch (std::string err) {
1486  Py_DECREF(parameters_arr);
1487  PyErr_SetString(PyExc_Exception, err.c_str());
1488  return NULL;
1489  }
1490  catch(...) {
1491  Py_DECREF(parameters_arr);
1492  std::string err( "Invalid pointer to circuit class");
1493  PyErr_SetString(PyExc_Exception, err.c_str());
1494  return NULL;
1495  }
1496 
1497  PyObject* deriv_list = PyList_New((Py_ssize_t)derivs.size());
1498  if (deriv_list == NULL) {
1499  Py_DECREF(parameters_arr);
1500  return NULL;
1501  }
1502 
1503  for (Py_ssize_t idx = 0; idx < (Py_ssize_t)derivs.size(); ++idx) {
1504  Matrix* owned = new Matrix(derivs[(size_t)idx]);
1505  PyObject* cap = PyCapsule_New(
1506  owned, "squander.circuit_matrix_owner",
1508  if (cap == NULL) {
1509  delete owned;
1510  Py_DECREF(deriv_list);
1511  Py_DECREF(parameters_arr);
1512  return NULL;
1513  }
1514  npy_intp shape[2] = { (npy_intp)owned->rows, (npy_intp)owned->cols };
1515  PyObject* deriv_py = PyArray_SimpleNewFromData(
1516  2, shape, NPY_COMPLEX128, owned->get_data());
1517  if (deriv_py == NULL) {
1518  Py_DECREF(cap);
1519  Py_DECREF(deriv_list);
1520  Py_DECREF(parameters_arr);
1521  return NULL;
1522  }
1523  PyArray_SetBaseObject((PyArrayObject*)deriv_py, cap);
1524  PyList_SET_ITEM(deriv_list, idx, deriv_py);
1525  }
1526 
1527  Py_DECREF(parameters_arr);
1528  return deriv_list;
1529 }
1530 
1531 
1540 static PyObject *
1541 qgd_Circuit_Wrapper_apply_to_combined( qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds ) {
1542 
1543  static char *kwlist[] = {(char*)"parameters", (char*)"unitary", (char*)"parallel", (char*)"is_f32", NULL};
1544 
1545  PyArrayObject * parameters_arr = NULL;
1546  PyArrayObject * unitary_arg = NULL;
1547  int parallel = 1;
1548  int is_f32 = 0;
1549 
1550  if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|ip", kwlist, &parameters_arr, &unitary_arg, &parallel, &is_f32 )) {
1551  PyErr_SetString(PyExc_Exception, "Unable to parse input");
1552  return NULL;
1553  }
1554 
1555  if ( unitary_arg == NULL ) {
1556  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
1557  return NULL;
1558  }
1559 
1560  if ( parameters_arr == NULL ) {
1561  PyErr_SetString(PyExc_Exception, "Parameters were not given");
1562  return NULL;
1563  }
1564 
1565  if ( !PyArray_IS_C_CONTIGUOUS(unitary_arg) ) {
1566  PyErr_SetString(PyExc_Exception, "input matrix is not memory contiguous");
1567  return NULL;
1568  }
1569 
1570  // ---- float32 path ----
1571  if (is_f32) {
1572 
1573  if ( PyArray_TYPE(parameters_arr) != NPY_FLOAT ) {
1574  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float32 when is_f32=True");
1575  return NULL;
1576  }
1577 
1578  if ( PyArray_TYPE(unitary_arg) != NPY_COMPLEX64 ) {
1579  PyErr_SetString(PyExc_TypeError, "Input matrix should be complex64 when is_f32=True");
1580  return NULL;
1581  }
1582 
1583  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1584  Py_INCREF(parameters_arr);
1585  }
1586  else {
1587  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_FLOAT, NPY_ARRAY_IN_ARRAY);
1588  }
1589 
1590  Matrix_real_float parameters_mtx = numpy2matrix_real_float( parameters_arr );
1591  Matrix_float unitary_mtx = numpy2matrix_float( unitary_arg );
1592 
1593  std::vector<Matrix_float> combined;
1594  try {
1595  combined = self->circuit->apply_to_combined( parameters_mtx, unitary_mtx, parallel );
1596  }
1597  catch (std::string err) {
1598  Py_DECREF(parameters_arr);
1599  PyErr_SetString(PyExc_Exception, err.c_str());
1600  return NULL;
1601  }
1602  catch(...) {
1603  Py_DECREF(parameters_arr);
1604  std::string err( "Invalid pointer to circuit class");
1605  PyErr_SetString(PyExc_Exception, err.c_str());
1606  return NULL;
1607  }
1608 
1609  PyObject* combined_list = PyList_New((Py_ssize_t)combined.size());
1610  if (combined_list == NULL) {
1611  Py_DECREF(parameters_arr);
1612  return NULL;
1613  }
1614 
1615  for (Py_ssize_t idx = 0; idx < (Py_ssize_t)combined.size(); ++idx) {
1616  Matrix_float* owned = new Matrix_float(combined[(size_t)idx]);
1617  PyObject* cap = PyCapsule_New(
1618  owned, "squander.circuit_matrix_float_owner",
1620  if (cap == NULL) {
1621  delete owned;
1622  Py_DECREF(combined_list);
1623  Py_DECREF(parameters_arr);
1624  return NULL;
1625  }
1626  npy_intp shape[2] = { (npy_intp)owned->rows, (npy_intp)owned->cols };
1627  PyObject* out_py = PyArray_SimpleNewFromData(
1628  2, shape, NPY_COMPLEX64, owned->get_data());
1629  if (out_py == NULL) {
1630  Py_DECREF(cap);
1631  Py_DECREF(combined_list);
1632  Py_DECREF(parameters_arr);
1633  return NULL;
1634  }
1635  PyArray_SetBaseObject((PyArrayObject*)out_py, cap);
1636  PyList_SET_ITEM(combined_list, idx, out_py);
1637  }
1638 
1639  Py_DECREF(parameters_arr);
1640  return combined_list;
1641  }
1642 
1643  // ---- float64 path ----
1644  if ( PyArray_TYPE(parameters_arr) != NPY_DOUBLE ) {
1645  PyErr_SetString(PyExc_TypeError, "Parameter vector should be float64 when is_f32=False");
1646  return NULL;
1647  }
1648 
1649  if ( PyArray_TYPE(unitary_arg) != NPY_COMPLEX128 ) {
1650  PyErr_SetString(PyExc_TypeError, "Input matrix should be complex128 when is_f32=False");
1651  return NULL;
1652  }
1653 
1654  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1655  Py_INCREF(parameters_arr);
1656  }
1657  else {
1658  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
1659  }
1660 
1661  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arr );
1662  Matrix unitary_mtx = numpy2matrix( unitary_arg );
1663 
1664  std::vector<Matrix> combined;
1665  try {
1666  combined = self->circuit->apply_to_combined( parameters_mtx, unitary_mtx, parallel );
1667  }
1668  catch (std::string err) {
1669  Py_DECREF(parameters_arr);
1670  PyErr_SetString(PyExc_Exception, err.c_str());
1671  return NULL;
1672  }
1673  catch(...) {
1674  Py_DECREF(parameters_arr);
1675  std::string err( "Invalid pointer to circuit class");
1676  PyErr_SetString(PyExc_Exception, err.c_str());
1677  return NULL;
1678  }
1679 
1680  PyObject* combined_list = PyList_New((Py_ssize_t)combined.size());
1681  if (combined_list == NULL) {
1682  Py_DECREF(parameters_arr);
1683  return NULL;
1684  }
1685 
1686  for (Py_ssize_t idx = 0; idx < (Py_ssize_t)combined.size(); ++idx) {
1687  Matrix* owned = new Matrix(combined[(size_t)idx]);
1688  PyObject* cap = PyCapsule_New(
1689  owned, "squander.circuit_matrix_owner",
1691  if (cap == NULL) {
1692  delete owned;
1693  Py_DECREF(combined_list);
1694  Py_DECREF(parameters_arr);
1695  return NULL;
1696  }
1697  npy_intp shape[2] = { (npy_intp)owned->rows, (npy_intp)owned->cols };
1698  PyObject* out_py = PyArray_SimpleNewFromData(
1699  2, shape, NPY_COMPLEX128, owned->get_data());
1700  if (out_py == NULL) {
1701  Py_DECREF(cap);
1702  Py_DECREF(combined_list);
1703  Py_DECREF(parameters_arr);
1704  return NULL;
1705  }
1706  PyArray_SetBaseObject((PyArrayObject*)out_py, cap);
1707  PyList_SET_ITEM(combined_list, idx, out_py);
1708  }
1709 
1710  Py_DECREF(parameters_arr);
1711  return combined_list;
1712 }
1713 
1714 
1715 
1722 static PyObject *
1724 {
1725 
1726 
1727  PyArrayObject * parameters_arr = NULL;
1728  PyArrayObject * input_state_arg = NULL;
1729  PyObject * qubit_list_arg = NULL;
1730 
1731 
1732  // parsing input arguments
1733  if (!PyArg_ParseTuple(args, "|OOO", &parameters_arr, &input_state_arg, &qubit_list_arg ))
1734  return Py_BuildValue("i", -1);
1735 
1736 
1737  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1738  Py_INCREF(parameters_arr);
1739  }
1740  else {
1741  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
1742  }
1743 
1744  // get the C++ wrapper around the data
1745  Matrix_real&& parameters_mtx = numpy2matrix_real( parameters_arr );
1746 
1747 
1748  // convert python object array to numpy C API array
1749  if ( input_state_arg == NULL ) {
1750  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
1751  return NULL;
1752  }
1753 
1754  PyArrayObject* input_state = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)input_state_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
1755 
1756  // test C-style contiguous memory allocation of the array
1757  if ( !PyArray_IS_C_CONTIGUOUS(input_state) ) {
1758  PyErr_SetString(PyExc_Exception, "input matrix is not memory contiguous");
1759  return NULL;
1760  }
1761 
1762 
1763 
1764 
1765  // create QGD version of the input matrix
1766  Matrix input_state_mtx = numpy2matrix(input_state);
1767 
1768 
1769  // check input argument qbit_list
1770  if ( qubit_list_arg == NULL || (!PyList_Check( qubit_list_arg )) ) {
1771  PyErr_SetString(PyExc_Exception, "qubit_list should be a list");
1772  return NULL;
1773  }
1774 
1775  Py_ssize_t reduced_qbit_num = PyList_Size( qubit_list_arg );
1776 
1777  matrix_base<int> qbit_list_mtx( (int)reduced_qbit_num, 1);
1778  for ( int idx=0; idx<reduced_qbit_num; idx++ ) {
1779 
1780  PyObject* item = PyList_GET_ITEM( qubit_list_arg, idx );
1781  qbit_list_mtx[idx] = (int) PyLong_AsLong( item );
1782 
1783  }
1784 
1785 
1786  double entropy = -1;
1787 
1788 
1789  try {
1790  entropy = self->circuit->get_second_Renyi_entropy( parameters_mtx, input_state_mtx, qbit_list_mtx );
1791  }
1792  catch (std::string err) {
1793  PyErr_SetString(PyExc_Exception, err.c_str());
1794  return NULL;
1795  }
1796  catch(...) {
1797  std::string err( "Invalid pointer to circuit class");
1798  PyErr_SetString(PyExc_Exception, err.c_str());
1799  return NULL;
1800  }
1801 
1802 
1803  Py_DECREF(parameters_arr);
1804  Py_DECREF(input_state);
1805 
1806 
1807 
1808  PyObject* p = Py_BuildValue("d", entropy);
1809 
1810  return p;
1811 }
1812 
1813 
1814 
1820 static PyObject *
1822 
1823  int qbit_num = 0;
1824 
1825  try {
1826  qbit_num = self->circuit->get_qbit_num();
1827  }
1828  catch (std::string err) {
1829  PyErr_SetString(PyExc_Exception, err.c_str());
1830  std::cout << err << std::endl;
1831  return NULL;
1832  }
1833  catch(...) {
1834  std::string err( "Invalid pointer to circuit class");
1835  PyErr_SetString(PyExc_Exception, err.c_str());
1836  return NULL;
1837  }
1838 
1839 
1840  return Py_BuildValue("i", qbit_num );
1841 
1842 }
1843 
1844 
1845 
1846 
1853 static PyObject *
1855 
1856  int qbit_num = 0;
1857 
1858  // parsing input arguments
1859  if (!PyArg_ParseTuple(args, "|i", &qbit_num )) {
1860  std::string err( "Unable to parse arguments");
1861  PyErr_SetString(PyExc_Exception, err.c_str());
1862  return NULL;
1863  }
1864 
1865 
1866  try {
1867  self->circuit->set_qbit_num( qbit_num );
1868  }
1869  catch (std::string err) {
1870  PyErr_SetString(PyExc_Exception, err.c_str());
1871  std::cout << err << std::endl;
1872  return NULL;
1873  }
1874  catch(...) {
1875  std::string err( "Invalid pointer to circuit class");
1876  PyErr_SetString(PyExc_Exception, err.c_str());
1877  return NULL;
1878  }
1879 
1880 
1881  return Py_BuildValue("");
1882 
1883 }
1884 
1885 
1886 
1892 static PyObject *
1894 
1895  PyObject* ret = PyList_New(0);
1896 
1897  try {
1898  std::vector<int>&& qbits = self->circuit->get_involved_qubits();
1899  for (size_t idx = 0; idx < qbits.size(); idx++) {
1900  PyObject* qbit = Py_BuildValue("i", qbits[idx]);
1901  if ( qbit == NULL || PyList_Append(ret, qbit) != 0 ) {
1902  Py_XDECREF(qbit);
1903  Py_DECREF(ret);
1904  return NULL;
1905  }
1906  Py_DECREF(qbit);
1907  }
1908 
1909  }
1910  catch (std::string err) {
1911  PyErr_SetString(PyExc_Exception, err.c_str());
1912  std::cout << err << std::endl;
1913  Py_DECREF(ret);
1914  return NULL;
1915  }
1916  catch(...) {
1917  std::string err( "Invalid pointer to circuit class");
1918  PyErr_SetString(PyExc_Exception, err.c_str());
1919  Py_DECREF(ret);
1920  return NULL;
1921  }
1922 
1923  return ret;
1924 
1925 }
1926 
1927 
1928 static PyObject *
1930 
1931  int min_fusion = -1;
1932 
1933  // parsing input arguments
1934  if (!PyArg_ParseTuple(args, "|i", &min_fusion )) {
1935  std::string err( "Unable to parse arguments");
1936  PyErr_SetString(PyExc_Exception, err.c_str());
1937  return NULL;
1938  }
1939 
1940 
1941  try {
1942  self->circuit->set_min_fusion( min_fusion );
1943  }
1944  catch (std::string err) {
1945  PyErr_SetString(PyExc_Exception, err.c_str());
1946  std::cout << err << std::endl;
1947  return NULL;
1948  }
1949  catch(...) {
1950  std::string err( "Invalid pointer to circuit class");
1951  PyErr_SetString(PyExc_Exception, err.c_str());
1952  return NULL;
1953  }
1954 
1955 
1956  return Py_BuildValue("");
1957 
1958 }
1959 
1960 
1967 static PyObject *
1969 
1970 
1971  PyObject* qbit_map_arg = NULL;
1972  int qbit_num = 0;
1973 
1974 
1975  // parsing input arguments
1976  if (!PyArg_ParseTuple(args, "|Oi", &qbit_map_arg, &qbit_num ))
1977  return Py_BuildValue("i", -1);
1978 
1979 
1980  // parse qbit map and create C++ version of the map
1981 
1982  bool is_dict = (PyDict_Check( qbit_map_arg ) != 0);
1983  if (!is_dict) {
1984  printf("Qubit map object must be a python dictionary!\n");
1985  return Py_BuildValue("i", -1);
1986  }
1987 
1988  // integer type config metadata utilized during the optimization
1989  std::map<int, int> qbit_map;
1990 
1991 
1992  // keys and values of the config dict (borrowed references)
1993  PyObject *key, *value;
1994  Py_ssize_t pos = 0;
1995 
1996  while (PyDict_Next(qbit_map_arg, &pos, &key, &value)) {
1997 
1998 
1999  if ( PyLong_Check( value ) && PyLong_Check( key ) ) {
2000  int key_Cpp = (int)PyLong_AsLongLong( key );
2001  qbit_map[ key_Cpp ] = (int)PyLong_AsLongLong( value );
2002  }
2003  else {
2004  std::string err( "Key and value in the qbit_map should be integers");
2005  PyErr_SetString(PyExc_Exception, err.c_str());
2006  return NULL;
2007  }
2008 
2009  }
2010 
2011 
2012  Gates_block* remapped_circuit = NULL;
2013 
2014  try {
2015  remapped_circuit = self->circuit->create_remapped_circuit( qbit_map, qbit_num);
2016 
2017  }
2018  catch (std::string err) {
2019  PyErr_SetString(PyExc_Exception, err.c_str());
2020  std::cout << err << std::endl;
2021  return NULL;
2022  }
2023  catch(...) {
2024  std::string err( "Invalid pointer to circuit class");
2025  PyErr_SetString(PyExc_Exception, err.c_str());
2026  return NULL;
2027  }
2028 
2029 
2030 
2031  // import gate operation modules
2032  PyObject* qgd_circuit = PyImport_ImportModule("squander.gates.qgd_Circuit");
2033 
2034  if ( qgd_circuit == NULL ) {
2035  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.qgd_Circuit" );
2036  return NULL;
2037  }
2038 
2039  PyObject* qgd_circuit_Dict = PyModule_GetDict( qgd_circuit );
2040 
2041  // PyDict_GetItemString creates a borrowed reference to the item in the dict. Reference counting is not increased on this element, dont need to decrease the reference counting at the end
2042  PyObject* py_circuit_class = PyDict_GetItemString( qgd_circuit_Dict, "qgd_Circuit");
2043 
2044  PyObject* qbit_num_py = Py_BuildValue("i", qbit_num);
2045  if ( qbit_num_py == NULL ) {
2046  Py_DECREF( qgd_circuit );
2047  return NULL;
2048  }
2049 
2050  PyObject* circuit_input = Py_BuildValue("(O)", qbit_num_py);
2051  Py_DECREF( qbit_num_py );
2052  if ( circuit_input == NULL ) {
2053  Py_DECREF( qgd_circuit );
2054  return NULL;
2055  }
2056  PyObject* py_circuit = PyObject_CallObject(py_circuit_class, circuit_input);
2057 
2058 
2059  // replace dummy data with real gate data
2060  qgd_Circuit_Wrapper* py_circuit_C = reinterpret_cast<qgd_Circuit_Wrapper*>( py_circuit );
2061 
2062  delete( py_circuit_C->circuit );
2063  py_circuit_C->circuit = remapped_circuit;
2064 
2065  Py_DECREF( qgd_circuit );
2066  Py_DECREF( circuit_input );
2067 
2068 
2069  return py_circuit;
2070 
2071 }
2072 
2073 
2074 #define get_gate_template_two_qubit(GATE_NAME) \
2075  else if (gate->get_type() == GATE_NAME##_OPERATION) { \
2076  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate ); \
2077  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, #GATE_NAME); \
2078  PyObject* gate_input = Py_BuildValue("(OOO)", qbit_num, target_qbit, control_qbit); \
2079  py_gate = PyObject_CallObject(py_gate_class, gate_input); \
2080  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate ); \
2081  delete( py_gate_C->gate ); \
2082  py_gate_C->gate = static_cast<Gate*>( gate->clone() ); \
2083  Py_DECREF( qgd_gate ); \
2084  Py_DECREF( gate_input ); \
2085  }
2086 
2087 #define get_gate_template_one_qubit(GATE_NAME) \
2088  else if (gate->get_type() == GATE_NAME##_OPERATION) { \
2089  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate ); \
2090  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, #GATE_NAME); \
2091  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbit); \
2092  py_gate = PyObject_CallObject(py_gate_class, gate_input); \
2093  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate ); \
2094  delete( py_gate_C->gate ); \
2095  py_gate_C->gate = static_cast<Gate*>( gate->clone() ); \
2096  Py_DECREF( qgd_gate ); \
2097  Py_DECREF( gate_input ); \
2098  }
2099 
2100 
2107 static PyObject *
2109 
2110 
2111  Gate* gate = circuit->get_gate( idx );
2112 
2113  // create dummy gate parameters to instantiate dummy object, which are then filled up with valid data
2114  PyObject* qbit_num = Py_BuildValue("i", gate->get_qbit_num() );
2115  PyObject* target_qbit = Py_BuildValue("i", gate->get_target_qbit() );
2116  PyObject* control_qbit = Py_BuildValue("i", gate->get_control_qbit() );
2117 
2118  // The python instance of the gate
2119  PyObject* py_gate = NULL;
2120 
2121  // import gate operation modules
2122  PyObject* qgd_gate = PyImport_ImportModule("squander.gates.gates_Wrapper");
2123 
2124  if ( qgd_gate == NULL ) {
2125  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.gates_Wrapper" );
2126  return NULL;
2127  }
2128  if (gate->get_type() == CNOT_OPERATION) {
2129 
2130 
2131  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2132  // PyDict_GetItemString creates a borrowed reference to the item in the dict. Reference counting is not increased on this element, dont need to decrease the reference counting at the end
2133  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "CNOT");
2134 
2135  PyObject* gate_input = Py_BuildValue("(OOO)", qbit_num, target_qbit, control_qbit);
2136  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2137 
2138  // replace dummy data with real gate data
2139  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2140  delete( py_gate_C->gate );
2141  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2142 
2143  Py_DECREF( qgd_gate );
2144  Py_DECREF( gate_input );
2145 
2146 
2147  }
2158  else if (gate->get_type() == SWAP_OPERATION){
2159  // SWAP now uses vector-based interface
2160  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2161  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2162  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2163  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2164  }
2165 
2166  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2167  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "SWAP");
2168 
2169  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbits_list);
2170  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2171 
2172  // replace dummy data with real gate data
2173  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2174  delete( py_gate_C->gate );
2175  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2176 
2177  Py_DECREF( qgd_gate );
2178  Py_DECREF( gate_input );
2179  Py_DECREF( target_qbits_list );
2180  }
2181 
2182  else if (gate->get_type() == RXX_OPERATION){
2183  // RXX now uses vector-based interface
2184  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2185  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2186  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2187  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2188  }
2189 
2190  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2191  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "RXX");
2192 
2193  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbits_list);
2194  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2195 
2196  // replace dummy data with real gate data
2197  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2198  delete( py_gate_C->gate );
2199  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2200 
2201  Py_DECREF( qgd_gate );
2202  Py_DECREF( gate_input );
2203  Py_DECREF( target_qbits_list );
2204  }
2205 
2206  else if (gate->get_type() == RYY_OPERATION){
2207  // RYY uses vector-based interface
2208  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2209  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2210  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2211  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2212  }
2213 
2214  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2215  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "RYY");
2216 
2217  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbits_list);
2218  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2219 
2220  // replace dummy data with real gate data
2221  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2222  delete( py_gate_C->gate );
2223  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2224 
2225  Py_DECREF( qgd_gate );
2226  Py_DECREF( gate_input );
2227  Py_DECREF( target_qbits_list );
2228  }
2229 
2230  else if (gate->get_type() == RZZ_OPERATION){
2231  // RZZ uses vector-based interface
2232  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2233  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2234  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2235  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2236  }
2237 
2238  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2239  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "RZZ");
2240 
2241  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbits_list);
2242  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2243 
2244  // replace dummy data with real gate data
2245  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2246  delete( py_gate_C->gate );
2247  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2248 
2249  Py_DECREF( qgd_gate );
2250  Py_DECREF( gate_input );
2251  Py_DECREF( target_qbits_list );
2252  }
2253 
2268  else if (gate->get_type() == SDG_OPERATION){
2269 
2270 
2271  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2272  // PyDict_GetItemString creates a borrowed reference to the item in the dict. Reference counting is not increased on this element, dont need to decrease the reference counting at the end
2273  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "Sdg");
2274 
2275  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbit);
2276  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2277 
2278  // replace dummy data with real gate data
2279  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2280  delete( py_gate_C->gate );
2281  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2282 
2283  Py_DECREF( qgd_gate );
2284  Py_DECREF( gate_input );
2285 
2286 
2287  }
2288  else if (gate->get_type() == SXDG_OPERATION){
2289 
2290 
2291  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2292  // PyDict_GetItemString creates a borrowed reference to the item in the dict. Reference counting is not increased on this element, dont need to decrease the reference counting at the end
2293  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "SXdg");
2294 
2295  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbit);
2296  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2297 
2298  // replace dummy data with real gate data
2299  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2300  delete( py_gate_C->gate );
2301  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2302 
2303  Py_DECREF( qgd_gate );
2304  Py_DECREF( gate_input );
2305 
2306 
2307  }
2308  else if (gate->get_type() == TDG_OPERATION){
2309 
2310 
2311  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2312  // PyDict_GetItemString creates a borrowed reference to the item in the dict. Reference counting is not increased on this element, dont need to decrease the reference counting at the end
2313  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "Tdg");
2314 
2315  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbit);
2316  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2317 
2318  // replace dummy data with real gate data
2319  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2320  delete( py_gate_C->gate );
2321  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2322 
2323  Py_DECREF( qgd_gate );
2324  Py_DECREF( gate_input );
2325 
2326 
2327  }
2328  else if (gate->get_type() == CCX_OPERATION){
2329  // CCX now uses vector-based interface
2330  std::vector<int> control_qbits_vec = gate->get_control_qbits();
2331  PyObject* control_qbits_list = PyList_New((Py_ssize_t)control_qbits_vec.size());
2332  for (size_t i = 0; i < control_qbits_vec.size(); i++) {
2333  PyList_SetItem(control_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", control_qbits_vec[i]));
2334  }
2335 
2336  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2337  // PyDict_GetItemString creates a borrowed reference to the item in the dict. Reference counting is not increased on this element, dont need to decrease the reference counting at the end
2338  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "CCX");
2339 
2340  PyObject* gate_input = Py_BuildValue("(OOO)", qbit_num, target_qbit, control_qbits_list);
2341  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2342 
2343  // replace dummy data with real gate data
2344  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2345  delete( py_gate_C->gate );
2346  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2347 
2348  Py_DECREF( qgd_gate );
2349  Py_DECREF( gate_input );
2350  Py_DECREF( control_qbits_list );
2351 
2352  Py_XDECREF(qbit_num);
2353  Py_XDECREF(target_qbit);
2354  Py_XDECREF(control_qbit);
2355 
2356  return py_gate;
2357 
2358  }
2359  else if (gate->get_type() == CSWAP_OPERATION){
2360  // CSWAP now uses vector-based interface
2361  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2362  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2363  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2364  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2365  }
2366 
2367  std::vector<int> control_qbits_vec = gate->get_control_qbits();
2368  PyObject* control_qbits_list = PyList_New((Py_ssize_t)control_qbits_vec.size());
2369  for (size_t i = 0; i < control_qbits_vec.size(); i++) {
2370  PyList_SetItem(control_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", control_qbits_vec[i]));
2371  }
2372 
2373  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2374  // PyDict_GetItemString creates a borrowed reference to the item in the dict. Reference counting is not increased on this element, dont need to decrease the reference counting at the end
2375  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "CSWAP");
2376 
2377  PyObject* gate_input = Py_BuildValue("(OOO)", qbit_num, target_qbits_list, control_qbits_list);
2378  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2379 
2380  // replace dummy data with real gate data
2381  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2382  delete( py_gate_C->gate );
2383  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2384 
2385  Py_DECREF( qgd_gate );
2386  Py_DECREF( gate_input );
2387  Py_DECREF( target_qbits_list );
2388  Py_DECREF( control_qbits_list );
2389 
2390  Py_XDECREF(qbit_num);
2391  Py_XDECREF(target_qbit);
2392  Py_XDECREF(control_qbit);
2393 
2394  return py_gate;
2395 
2396  }
2397  else if (gate->get_type() == BLOCK_OPERATION) {
2398 
2399  // import gate operation modules
2400  PyObject* qgd_circuit = PyImport_ImportModule("squander.gates.qgd_Circuit");
2401 
2402  if ( qgd_circuit == NULL ) {
2403  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.qgd_Circuit" );
2404  return NULL;
2405  }
2406 
2407  PyObject* qgd_circuit_Dict = PyModule_GetDict( qgd_circuit );
2408 
2409  // PyDict_GetItemString creates a borrowed reference to the item in the dict. Reference counting is not increased on this element, dont need to decrease the reference counting at the end
2410  PyObject* py_circuit_class = PyDict_GetItemString( qgd_circuit_Dict, "qgd_Circuit");
2411 
2412  PyObject* circuit_input = Py_BuildValue("(O)", qbit_num );
2413  py_gate = PyObject_CallObject(py_circuit_class, circuit_input);
2414 
2415  // replace dummy data with real gate data
2416  qgd_Circuit_Wrapper* py_gate_C = reinterpret_cast<qgd_Circuit_Wrapper*>( py_gate );
2417 
2418  Gates_block* circuit = reinterpret_cast<Gates_block*>( gate );
2419  delete( py_gate_C->circuit );
2420  py_gate_C->circuit = circuit->clone();
2421 
2422  Py_DECREF( qgd_circuit );
2423  Py_DECREF( circuit_input );
2424 
2425  }
2426  else {
2427 
2428  Py_DECREF( qgd_gate );
2429  Py_XDECREF(qbit_num);
2430  Py_XDECREF(target_qbit);
2431  Py_XDECREF(control_qbit);
2432  PyErr_SetString(PyExc_Exception, "qgd_Circuit_Wrapper::get_gate: unimplemented gate type" );
2433  return NULL;
2434  }
2435 
2436  Py_XDECREF(qbit_num);
2437  Py_XDECREF(target_qbit);
2438  Py_XDECREF(control_qbit);
2439 
2440  return py_gate;
2441 
2442 }
2443 
2444 
2445 
2452 static PyObject *
2454 
2455  // initiate variables for input arguments
2456  int idx;
2457 
2458  // parsing input arguments
2459  if (!PyArg_ParseTuple(args, "|i", &idx )) return Py_BuildValue("i", -1);
2460 
2461 
2462  return get_gate( self->circuit, idx );
2463 
2464 
2465 }
2466 
2467 
2473 static PyObject *
2475 
2476  std::map< std::string, int > gate_nums;
2477 
2478  try {
2479  gate_nums = self->circuit->get_gate_nums();
2480  }
2481  catch (std::string err) {
2482  PyErr_SetString(PyExc_Exception, err.c_str());
2483  return NULL;
2484  }
2485  catch(...) {
2486  std::string err( "Invalid pointer to circuit class");
2487  PyErr_SetString(PyExc_Exception, err.c_str());
2488  return NULL;
2489  }
2490 
2491 
2492  PyObject* gate_nums_py = PyDict_New();
2493  if( gate_nums_py == NULL ) {
2494  std::string err( "Failed to create dictionary");
2495  PyErr_SetString(PyExc_Exception, err.c_str());
2496  return NULL;
2497  }
2498 
2499  for( auto it = gate_nums.begin(); it != gate_nums.end(); it++ ) {
2500 
2501  PyObject* key = Py_BuildValue( "s", it->first.c_str() );
2502  PyObject* val = Py_BuildValue("i", it->second );
2503 
2504  if ( key == NULL || val == NULL ) {
2505  Py_XDECREF(key);
2506  Py_XDECREF(val);
2507  Py_DECREF(gate_nums_py);
2508  return NULL;
2509  }
2510 
2511  if ( PyDict_SetItem(gate_nums_py, key, val) != 0 ) {
2512  Py_DECREF(key);
2513  Py_DECREF(val);
2514  Py_DECREF(gate_nums_py);
2515  return NULL;
2516  }
2517 
2518  Py_DECREF(key);
2519  Py_DECREF(val);
2520  }
2521 
2522  return gate_nums_py;
2523 
2524 }
2525 
2526 
2527 
2528 
2534 static PyObject *
2536 
2537 
2538 
2539 
2540  // get the number of gates
2541  int op_num = self->circuit->get_gate_num();
2542 
2543  // preallocate Python tuple for the output
2544  PyObject* ret = PyTuple_New( (Py_ssize_t) op_num );
2545 
2546 
2547 
2548  // iterate over the gates to get the gate list
2549  for (int idx = 0; idx < op_num; idx++ ) {
2550 
2551  // get metadata about the idx-th gate
2552  PyObject* gate = get_gate( self->circuit, idx );
2553 
2554  // adding gate information to the tuple
2555  PyTuple_SetItem( ret, (Py_ssize_t) idx, gate );
2556 
2557  }
2558 
2559 
2560  return ret;
2561 
2562 }
2563 
2564 
2565 
2572 static PyObject *
2574 
2575  // the gate for which we look for the parents
2576  PyObject* py_gate = NULL;
2577 
2578  // parsing input arguments
2579  if (!PyArg_ParseTuple(args, "|O", &py_gate )) return Py_BuildValue("i", -1);
2580 
2581 
2582  if( py_gate == NULL ) {
2583  return PyTuple_New( 0 );
2584  }
2585 
2586  qgd_Gate* gate_struct = reinterpret_cast<qgd_Gate*>( py_gate );
2587  std::vector<Gate*> parents = gate_struct->gate->get_parents();
2588 
2589  // preallocate tuple for the output
2590  PyObject* parent_tuple = PyTuple_New( (Py_ssize_t) parents.size() );
2591 
2592  std::vector<Gate*>&& gates = self->circuit->get_gates();
2593 
2594 
2595  // find the indices of the parents
2596  for(size_t idx=0; idx<parents.size(); idx++) {
2597 
2598  Gate* parent_gate = parents[idx];
2599 
2600  // find the index of the parent_gate
2601  int parent_idx = -1;
2602  for( size_t jdx=0; jdx<gates.size(); jdx++ ) {
2603 
2604  Gate* gate = gates[jdx];
2605 
2606  if( parent_gate == gate ) {
2607  parent_idx = static_cast<int>(jdx);
2608  break;
2609  }
2610 
2611  if( jdx == static_cast<size_t>(gates.size()-1) ) {
2612  std::string err( "Parent gate did not found in the circuit. May be the gate is not in the circuit");
2613  PyErr_SetString(PyExc_Exception, err.c_str());
2614  return NULL;
2615  }
2616 
2617  }
2618 
2619  // adding parent_idx the tuple
2620  PyTuple_SetItem( parent_tuple, (Py_ssize_t) idx, Py_BuildValue("i", parent_idx) );
2621 
2622 
2623  }
2624 
2625 
2626  return parent_tuple;
2627 
2628 
2629 }
2630 
2631 
2632 
2633 
2640 static PyObject *
2642 
2643  // the gate for which we look for the children
2644  PyObject* py_gate = NULL;
2645 
2646  // parsing input arguments
2647  if (!PyArg_ParseTuple(args, "|O", &py_gate )) return Py_BuildValue("i", -1);
2648 
2649 
2650  if( py_gate == NULL ) {
2651  return PyTuple_New( 0 );
2652  }
2653 
2654  qgd_Gate* gate_struct = reinterpret_cast<qgd_Gate*>( py_gate );
2655  std::vector<Gate*> children = gate_struct->gate->get_children();
2656 
2657  // preallocate tuple for the output
2658  PyObject* children_tuple = PyTuple_New( (Py_ssize_t) children.size() );
2659 
2660  std::vector<Gate*>&& gates = self->circuit->get_gates();
2661 
2662 
2663  // find the indices of the children
2664  for(size_t idx=0; idx<children.size(); idx++) {
2665 
2666  Gate* child_gate = children[idx];
2667 
2668  // find the index of the child_gate
2669  int child_idx = -1;
2670  for( size_t jdx=0; jdx<gates.size(); jdx++ ) {
2671 
2672  Gate* gate = gates[jdx];
2673 
2674  if( child_gate == gate ) {
2675  child_idx = static_cast<int>(jdx);
2676  break;
2677  }
2678 
2679  if( jdx == static_cast<size_t>(gates.size()-1) ) {
2680  std::string err( "Child gate did not found in the circuit. May be the gate is not in the circuit");
2681  PyErr_SetString(PyExc_Exception, err.c_str());
2682  return NULL;
2683  }
2684 
2685  }
2686 
2687  // adding child_idx the tuple
2688  PyTuple_SetItem( children_tuple, (Py_ssize_t) idx, Py_BuildValue("i", child_idx) );
2689 
2690 
2691  }
2692 
2693 
2694  return children_tuple;
2695 
2696 
2697 }
2698 
2699 
2700 
2707 static PyObject *
2709 
2710  PyArrayObject * parameters_arr = NULL;
2711 
2712 
2713  // parsing input arguments
2714  if (!PyArg_ParseTuple(args, "|O", &parameters_arr ))
2715  return Py_BuildValue("i", -1);
2716 
2717 
2718  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
2719  Py_INCREF(parameters_arr);
2720  }
2721  else {
2722  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
2723  }
2724 
2725  // get the C++ wrapper around the data
2726  Matrix_real&& parameters_mtx = numpy2matrix_real( parameters_arr );
2727 
2728 
2729 
2730  Matrix_real extracted_parameters;
2731 
2732  try {
2733  extracted_parameters = self->circuit->extract_parameters( parameters_mtx );
2734  }
2735  catch (std::string err) {
2736  PyErr_SetString(PyExc_Exception, err.c_str());
2737  return NULL;
2738  }
2739  catch(...) {
2740  std::string err( "Invalid pointer to circuit class");
2741  PyErr_SetString(PyExc_Exception, err.c_str());
2742  return NULL;
2743  }
2744 
2745 
2746  // convert to numpy array
2747  extracted_parameters.set_owner(false);
2748  PyObject *extracted_parameters_py = matrix_real_to_numpy( extracted_parameters );
2749 
2750 
2751  return extracted_parameters_py;
2752 }
2753 
2754 
2755 
2761 static PyObject *
2763 
2764  Gates_block* flat_circuit = self->circuit->get_flat_circuit();
2765  int qbit_num = flat_circuit->get_qbit_num();
2766 
2767  // import gate operation modules
2768  PyObject* qgd_circuit = PyImport_ImportModule("squander.gates.qgd_Circuit");
2769 
2770  if ( qgd_circuit == NULL ) {
2771  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.qgd_Circuit" );
2772  return NULL;
2773  }
2774 
2775  PyObject* qgd_circuit_Dict = PyModule_GetDict( qgd_circuit );
2776 
2777  // PyDict_GetItemString creates a borrowed reference to the item in the dict. Reference counting is not increased on this element, dont need to decrease the reference counting at the end
2778  PyObject* py_circuit_class = PyDict_GetItemString( qgd_circuit_Dict, "qgd_Circuit");
2779 
2780  PyObject* qbit_num_py = Py_BuildValue("i", qbit_num);
2781  if ( qbit_num_py == NULL ) {
2782  Py_DECREF( qgd_circuit );
2783  return NULL;
2784  }
2785 
2786  PyObject* circuit_input = Py_BuildValue("(O)", qbit_num_py);
2787  Py_DECREF( qbit_num_py );
2788  if ( circuit_input == NULL ) {
2789  Py_DECREF( qgd_circuit );
2790  return NULL;
2791  }
2792  PyObject* py_circuit = PyObject_CallObject(py_circuit_class, circuit_input);
2793 
2794  // replace dummy data with real gate data
2795  qgd_Circuit_Wrapper* py_circuit_C = reinterpret_cast<qgd_Circuit_Wrapper*>( py_circuit );
2796  delete( py_circuit_C->circuit );
2797  py_circuit_C->circuit = flat_circuit;
2798 
2799 
2800  Py_DECREF( qgd_circuit );
2801  Py_DECREF( circuit_input );
2802 
2803  return py_circuit;
2804 }
2805 
2806 
2807 
2808 
2814 static PyObject *
2816 
2817 
2818  // get the number of gates
2819  int op_num = self->circuit->get_gate_num();
2820 
2821  // preallocate Python tuple for the output
2822  PyObject* ret = PyTuple_New( (Py_ssize_t) op_num+1 );
2823 
2824 
2825 
2826 
2827  // add qbit num value to the return tuple
2828  PyObject* qbit_num_dict = PyDict_New();
2829 
2830  if( qbit_num_dict == NULL ) {
2831  std::string err( "Failed to create dictionary");
2832  PyErr_SetString(PyExc_Exception, err.c_str());
2833  return NULL;
2834  }
2835 
2836  int qbit_num = self->circuit->get_qbit_num();
2837  PyObject* qbit_num_val = Py_BuildValue("i", qbit_num );
2838  if (qbit_num_val == NULL || PyDict_SetItemString(qbit_num_dict, "qbit_num", qbit_num_val) != 0) {
2839  Py_XDECREF(qbit_num_val);
2840  Py_DECREF(qbit_num_dict);
2841  Py_DECREF(ret);
2842  return NULL;
2843  }
2844 
2845  PyTuple_SetItem( ret, 0, qbit_num_dict );
2846 
2847  Py_DECREF( qbit_num_val );
2848 
2849 
2850  PyObject* method_name = PyUnicode_FromString("__getstate__");
2851  if (method_name == NULL) {
2852  Py_DECREF(ret);
2853  return NULL;
2854  }
2855 
2856  PyObject* qbit_num_key = PyUnicode_FromString("qbit_num");
2857  if (qbit_num_key == NULL) {
2858  Py_DECREF(method_name);
2859  Py_DECREF(ret);
2860  return NULL;
2861  }
2862 
2863  // iterate over the gates to get the gate list
2864  for (int idx = 0; idx < op_num; idx++ ) {
2865 
2866  // get metadata about the idx-th gate
2867  PyObject* gate = get_gate( self->circuit, idx );
2868  if (gate == NULL) {
2869  Py_DECREF(qbit_num_key);
2870  Py_DECREF(method_name);
2871  Py_DECREF(ret);
2872  return NULL;
2873  }
2874 
2875  PyObject* gate_state = PyObject_CallMethodObjArgs( gate, method_name, NULL );
2876  if (gate_state == NULL) {
2877  Py_DECREF(gate);
2878  Py_DECREF(qbit_num_key);
2879  Py_DECREF(method_name);
2880  Py_DECREF(ret);
2881  return NULL;
2882  }
2883 
2884 
2885  // remove the field qbit_num from gate dict sice this will be redundant information
2886  if ( PyDict_Contains(gate_state, qbit_num_key) == 1 ) {
2887 
2888  if ( PyDict_DelItem(gate_state, qbit_num_key) != 0 ) {
2889  std::string err( "Failed to delete item qbit_num from gate state");
2890  PyErr_SetString(PyExc_Exception, err.c_str());
2891  Py_DECREF(gate_state);
2892  Py_DECREF(gate);
2893  Py_DECREF(qbit_num_key);
2894  Py_DECREF(method_name);
2895  Py_DECREF(ret);
2896  return NULL;
2897  }
2898 
2899  }
2900 
2901 
2902  // adding gate information to the tuple
2903  PyTuple_SetItem( ret, (Py_ssize_t) idx+1, gate_state );
2904 
2905 
2906 
2907  Py_DECREF( gate );
2908  //Py_DECREF( gate_state );
2909 
2910  }
2911 
2912  Py_DECREF( qbit_num_key );
2913  Py_DECREF( method_name );
2914 
2915  return ret;
2916 }
2917 
2918 
2919 
2926 static PyObject *
2928 
2929  PyObject* state = NULL;
2930 
2931  // parsing input arguments
2932  if (!PyArg_ParseTuple(args, "|O", &state )) {
2933  std::string err( "Unable to parse state argument");
2934  PyErr_SetString(PyExc_Exception, err.c_str());
2935  return NULL;
2936  }
2937 
2938  if ( PyTuple_Size(state) == 0 ) {
2939  std::string err( "State should contain at least one element");
2940  PyErr_SetString(PyExc_Exception, err.c_str());
2941  return NULL;
2942  }
2943 
2944  PyObject* qbit_num_dict = PyTuple_GetItem( state, 0); // borrowed reference
2945 
2946 
2947  PyObject* qbit_num_key = Py_BuildValue( "s", "qbit_num" );
2948 
2949  if ( PyDict_Contains(qbit_num_dict, qbit_num_key) == 0 ) {
2950  std::string err( "The first entry of the circuit state should be the number of qubits");
2951  PyErr_SetString(PyExc_Exception, err.c_str());
2952 
2953  Py_DECREF( qbit_num_key );
2954  return NULL;
2955  }
2956 
2957  PyObject* qbit_num_py = PyDict_GetItem(qbit_num_dict, qbit_num_key); // borrowed reference
2958 
2959  if( !PyLong_Check(qbit_num_py) ) {
2960  std::string err( "The number of qubits should be an integer value");
2961  PyErr_SetString(PyExc_Exception, err.c_str());
2962 
2963  Py_DECREF( qbit_num_key );
2964  return NULL;
2965  }
2966 
2967 
2968  int qbit_num = (int)PyLong_AsLong( qbit_num_py );
2969 
2970 
2971  // import gate operation modules
2972  PyObject* qgd_gate = PyImport_ImportModule("squander.gates.gates_Wrapper");
2973 
2974  if ( qgd_gate == NULL ) {
2975  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.gates_Wrapper" );
2976  Py_DECREF( qbit_num_key );
2977  return NULL;
2978  }
2979 
2980  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate ); // borrowed reference ???
2981  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "Gate"); // borrowed reference
2982  PyObject* setstate_name = Py_BuildValue( "s", "__setstate__" );
2983  PyObject* dummy_target_qbit = Py_BuildValue( "i", 0 );
2984 
2985  // now build up the quantum circuit
2986  try {
2987 
2988  self->circuit->release_gates();
2989  self->circuit->set_qbit_num( qbit_num );
2990 
2991  int gates_idx_max = (int) PyTuple_Size(state);
2992 
2993  for( int gate_idx=1; gate_idx < gates_idx_max; gate_idx++ ) {
2994 
2995 
2996  // get gate state as python dictionary
2997  PyObject* gate_state_dict = PyTuple_GetItem( state, gate_idx); // borrowed reference
2998 
2999  if( !PyDict_Check( gate_state_dict ) ) {
3000  std::string err( "Gate state should be given by a dictionary");
3001  PyErr_SetString(PyExc_Exception, err.c_str());
3002 
3003  Py_DECREF( qgd_gate );
3004  Py_DECREF( qbit_num_key );
3005  Py_DECREF( setstate_name );
3006  Py_DECREF( dummy_target_qbit );
3007  return NULL;
3008  }
3009 
3010  PyDict_SetItem(gate_state_dict, qbit_num_key, qbit_num_py);
3011 
3012  PyObject* gate_input = Py_BuildValue( "(O)", qbit_num_py );
3013  PyObject* py_gate = PyObject_CallObject(py_gate_class, gate_input);
3014  if (py_gate == NULL) {
3015  Py_DECREF(gate_input);
3016  Py_DECREF( qgd_gate );
3017  Py_DECREF( qbit_num_key );
3018  Py_DECREF( setstate_name );
3019  Py_DECREF( dummy_target_qbit );
3020  return NULL;
3021  }
3022 
3023  // turn the generic gate into a specific gate
3024  PyObject* setstate_ret = PyObject_CallMethodObjArgs( py_gate, setstate_name, gate_state_dict, NULL );
3025  if (setstate_ret == NULL) {
3026  Py_DECREF( gate_input );
3027  Py_DECREF( py_gate );
3028  Py_DECREF( qgd_gate );
3029  Py_DECREF( qbit_num_key );
3030  Py_DECREF( setstate_name );
3031  Py_DECREF( dummy_target_qbit );
3032  return NULL;
3033  }
3034  Py_DECREF(setstate_ret);
3035 
3036  Gate* gate_loc = static_cast<Gate*>( ((qgd_Gate*)py_gate)->gate->clone() );
3037  self->circuit->add_gate( gate_loc );
3038 
3039 
3040  Py_DECREF( gate_input );
3041  Py_DECREF( py_gate );
3042 
3043 
3044 
3045  }
3046 
3047 
3048  }
3049  catch (std::string err) {
3050  Py_DECREF( qgd_gate );
3051  Py_DECREF( qbit_num_key );
3052  Py_DECREF( setstate_name );
3053  Py_DECREF( dummy_target_qbit );
3054  PyErr_SetString(PyExc_Exception, err.c_str());
3055  return NULL;
3056  }
3057  catch(...) {
3058  Py_DECREF( qgd_gate );
3059  Py_DECREF( qbit_num_key );
3060  Py_DECREF( setstate_name );
3061  Py_DECREF( dummy_target_qbit );
3062  std::string err( "Invalid pointer to circuit class");
3063  PyErr_SetString(PyExc_Exception, err.c_str());
3064  return NULL;
3065  }
3066 
3067 
3068 
3069 
3070  Py_DECREF( qgd_gate );
3071  //Py_DECREF( qgd_gate_Dict );
3072  Py_DECREF( qbit_num_key );
3073  Py_DECREF( setstate_name );
3074  Py_DECREF( dummy_target_qbit );
3075 
3076 
3077  return Py_BuildValue("");
3078 }
3079 
3080 
3081 
3087 static PyObject *
3089 
3090  int start_index = self->circuit->get_parameter_start_idx();
3091 
3092  return Py_BuildValue("i", start_index);
3093 
3094 }
3095 
3096 
3097 static PyMethodDef qgd_Circuit_Wrapper_Methods[] = {
3098  {"add_U1", (PyCFunction) qgd_Circuit_Wrapper_add_U1, METH_VARARGS | METH_KEYWORDS,
3099  "Call to add a U1 gate to the front of the gate structure"
3100  },
3101  {"add_U2", (PyCFunction) qgd_Circuit_Wrapper_add_U2, METH_VARARGS | METH_KEYWORDS,
3102  "Call to add a U2 gate to the front of the gate structure"
3103  },
3104  {"add_U3", (PyCFunction) qgd_Circuit_Wrapper_add_U3, METH_VARARGS | METH_KEYWORDS,
3105  "Call to add a U3 gate to the front of the gate structure"
3106  },
3107  {"add_RX", (PyCFunction) qgd_Circuit_Wrapper_add_RX, METH_VARARGS | METH_KEYWORDS,
3108  "Call to add a RX gate to the front of the gate structure"
3109  },
3110  {"add_RXX", (PyCFunction) qgd_Circuit_Wrapper_add_RXX, METH_VARARGS | METH_KEYWORDS,
3111  "Call to add a RXX gate to the front of the gate structure"
3112  },
3113  {"add_RYY", (PyCFunction) qgd_Circuit_Wrapper_add_RYY, METH_VARARGS | METH_KEYWORDS,
3114  "Call to add a RYY gate to the front of the gate structure"
3115  },
3116  {"add_RZZ", (PyCFunction) qgd_Circuit_Wrapper_add_RZZ, METH_VARARGS | METH_KEYWORDS,
3117  "Call to add a RZZ gate to the front of the gate structure"
3118  },
3119  {"add_R", (PyCFunction) qgd_Circuit_Wrapper_add_R, METH_VARARGS | METH_KEYWORDS,
3120  "Call to add a R gate to the front of the gate structure"
3121  },
3122  {"add_RY", (PyCFunction) qgd_Circuit_Wrapper_add_RY, METH_VARARGS | METH_KEYWORDS,
3123  "Call to add a RY gate to the front of the gate structure"
3124  },
3125  {"add_RZ", (PyCFunction) qgd_Circuit_Wrapper_add_RZ, METH_VARARGS | METH_KEYWORDS,
3126  "Call to add a RZ gate to the front of the gate structure"
3127  },
3128  {"add_CNOT", (PyCFunction) qgd_Circuit_Wrapper_add_CNOT, METH_VARARGS | METH_KEYWORDS,
3129  "Call to add a CNOT gate to the front of the gate structure"
3130  },
3131  {"add_CZ", (PyCFunction) qgd_Circuit_Wrapper_add_CZ, METH_VARARGS | METH_KEYWORDS,
3132  "Call to add a CZ gate to the front of the gate structure"
3133  },
3134  {"add_CU", (PyCFunction) qgd_Circuit_Wrapper_add_CU, METH_VARARGS | METH_KEYWORDS,
3135  "Call to add a CU gate to the front of the gate structure"
3136  },
3137  {"add_CH", (PyCFunction) qgd_Circuit_Wrapper_add_CH, METH_VARARGS | METH_KEYWORDS,
3138  "Call to add a CH gate to the front of the gate structure"
3139  },
3140  {"add_SYC", (PyCFunction) qgd_Circuit_Wrapper_add_SYC, METH_VARARGS | METH_KEYWORDS,
3141  "Call to add a Sycamore gate to the front of the gate structure"
3142  },
3143  {"add_H", (PyCFunction) qgd_Circuit_Wrapper_add_H, METH_VARARGS | METH_KEYWORDS,
3144  "Call to add a Hadamard gate to the front of the gate structure"
3145  },
3146  {"add_X", (PyCFunction) qgd_Circuit_Wrapper_add_X, METH_VARARGS | METH_KEYWORDS,
3147  "Call to add a X gate to the front of the gate structure"
3148  },
3149  {"add_Y", (PyCFunction) qgd_Circuit_Wrapper_add_Y, METH_VARARGS | METH_KEYWORDS,
3150  "Call to add a Y gate to the front of the gate structure"
3151  },
3152  {"add_Z", (PyCFunction) qgd_Circuit_Wrapper_add_Z, METH_VARARGS | METH_KEYWORDS,
3153  "Call to add a Z gate to the front of the gate structure"
3154  },
3155  {"add_SX", (PyCFunction) qgd_Circuit_Wrapper_add_SX, METH_VARARGS | METH_KEYWORDS,
3156  "Call to add a SX gate to the front of the gate structure"
3157  },
3158  {"add_SXdg", (PyCFunction) qgd_Circuit_Wrapper_add_SXdg, METH_VARARGS | METH_KEYWORDS,
3159  "Call to add a SXdg gate to the front of the gate structure"
3160  },
3161  {"add_S", (PyCFunction) qgd_Circuit_Wrapper_add_S, METH_VARARGS | METH_KEYWORDS,
3162  "Call to add a S gate to the front of the gate structure"
3163  },
3164  {"add_Sdg", (PyCFunction) qgd_Circuit_Wrapper_add_Sdg, METH_VARARGS | METH_KEYWORDS,
3165  "Call to add a Sdg gate to the front of the gate structure"
3166  },
3167  {"add_T", (PyCFunction) qgd_Circuit_Wrapper_add_T, METH_VARARGS | METH_KEYWORDS,
3168  "Call to add a T gate to the front of the gate structure"
3169  },
3170  {"add_Tdg", (PyCFunction) qgd_Circuit_Wrapper_add_Tdg, METH_VARARGS | METH_KEYWORDS,
3171  "Call to add a Tdg gate to the front of the gate structure"
3172  },
3173  {"add_CRY", (PyCFunction) qgd_Circuit_Wrapper_add_CRY, METH_VARARGS | METH_KEYWORDS,
3174  "Call to add a CRY gate to the front of the gate structure"
3175  },
3176  {"add_CRX", (PyCFunction) qgd_Circuit_Wrapper_add_CRX, METH_VARARGS | METH_KEYWORDS,
3177  "Call to add a CRY gate to the front of the gate structure"
3178  },
3179  {"add_CRZ", (PyCFunction) qgd_Circuit_Wrapper_add_CRZ, METH_VARARGS | METH_KEYWORDS,
3180  "Call to add a CRY gate to the front of the gate structure"
3181  },
3182  {"add_CP", (PyCFunction) qgd_Circuit_Wrapper_add_CP, METH_VARARGS | METH_KEYWORDS,
3183  "Call to add a CRY gate to the front of the gate structure"
3184  },
3185  {"add_CCX", (PyCFunction) qgd_Circuit_Wrapper_add_CCX, METH_VARARGS | METH_KEYWORDS,
3186  "Call to add a CCX gate to the front of the gate structure"
3187  },
3188  {"add_SWAP", (PyCFunction) qgd_Circuit_Wrapper_add_SWAP, METH_VARARGS | METH_KEYWORDS,
3189  "Call to add a SWAP gate to the front of the gate structure"
3190  },
3191  {"add_CSWAP", (PyCFunction) qgd_Circuit_Wrapper_add_CSWAP, METH_VARARGS | METH_KEYWORDS,
3192  "Call to add a CSWAP gate to the front of the gate structure"
3193  },
3194  {"add_CROT", (PyCFunction) qgd_Circuit_Wrapper_add_CROT, METH_VARARGS | METH_KEYWORDS,
3195  "Call to add a CROT gate to the front of the gate structure"
3196  },
3197  {"add_CR", (PyCFunction) qgd_Circuit_Wrapper_add_CR, METH_VARARGS | METH_KEYWORDS,
3198  "Call to add a CR gate to the front of the gate structure"
3199  },
3200  {"add_adaptive", (PyCFunction) qgd_Circuit_Wrapper_add_adaptive, METH_VARARGS | METH_KEYWORDS,
3201  "Call to add an adaptive gate to the front of the gate structure"
3202  },
3203  {"add_Circuit", (PyCFunction) qgd_Circuit_Wrapper_add_Circuit, METH_VARARGS,
3204  "Call to add a block of operations to the front of the gate structure."
3205  },
3206  {"add_GENERAL", (PyCFunction) qgd_Circuit_Wrapper_add_GENERAL, METH_VARARGS | METH_KEYWORDS,
3207  "Call to add a GENERAL_OPERATION gate from an explicit matrix."
3208  },
3209 #ifdef __DFE__
3210  {"convert_to_DFE_gates_with_derivates", (PyCFunction) qgd_Circuit_Wrapper_convert_to_DFE_gates_with_derivates, METH_VARARGS,
3211  "Call to convert to DFE gates with derivates."
3212  },
3213  {"adjust_parameters_for_derivation", (PyCFunction) qgd_Circuit_Wrapper_adjust_parameters_for_derivation, METH_VARARGS,
3214  "Call to adjust parameters for derivation."
3215  },
3216  {"convert_to_DFE_gates", (PyCFunction) qgd_Circuit_Wrapper_convert_to_DFE_gates, METH_VARARGS,
3217  "Call to convert to DFE gates."
3218  },
3219  {"convert_to_DFE_gates", (PyCFunction) qgd_Circuit_Wrapper_convert_to_DFE_gates, METH_VARARGS,
3220  "Call to convert to DFE gates."
3221  },
3222 #endif
3223  {"get_Matrix", (PyCFunction) qgd_Circuit_Wrapper_get_Matrix, METH_VARARGS | METH_KEYWORDS,
3224  "Method to get the matrix of the operation."
3225  },
3226  {"get_Parameter_Num", (PyCFunction) qgd_Circuit_Wrapper_get_Parameter_Num, METH_NOARGS,
3227  "Call to get the number of free parameters in the circuit"
3228  },
3229  {"apply_to", (PyCFunction) qgd_Circuit_Wrapper_apply_to, METH_VARARGS | METH_KEYWORDS,
3230  "Call to apply the gate on the input matrix (or state). Keyword is_f32=True selects float32/complex64 path."
3231  },
3232  {"apply_from_right", (PyCFunction) qgd_Circuit_Wrapper_apply_from_right, METH_VARARGS | METH_KEYWORDS,
3233  "Call to apply the gate from the right on the input matrix. Keyword is_f32=True selects float32/complex64 path."
3234  },
3235  {"apply_to_list", (PyCFunction) qgd_Circuit_Wrapper_apply_to_list, METH_VARARGS | METH_KEYWORDS,
3236  "Call to apply the circuit on a list of input matrices (float64/complex128 only)."
3237  },
3238  {"apply_derivate_to", (PyCFunction) qgd_Circuit_Wrapper_apply_derivate_to, METH_VARARGS | METH_KEYWORDS,
3239  "Call to evaluate the derivative of the circuit on an input matrix. Returns a list of derivative matrices."
3240  },
3241  {"apply_to_combined", (PyCFunction) qgd_Circuit_Wrapper_apply_to_combined, METH_VARARGS | METH_KEYWORDS,
3242  "Call to evaluate forward action and derivatives of the circuit on an input matrix. Returns [forward, derivatives...]."
3243  },
3244  {"get_Second_Renyi_Entropy", (PyCFunction) qgd_Circuit_Wrapper_get_Second_Renyi_Entropy, METH_VARARGS,
3245  "Wrapper function to evaluate the second Rényi entropy of a quantum circuit at a specific parameter set."
3246  },
3247  {"get_Qbit_Num", (PyCFunction) qgd_Circuit_Wrapper_get_Qbit_Num, METH_NOARGS,
3248  "Call to get the number of qubits in the circuit"
3249  },
3250  {"set_Qbit_Num", (PyCFunction) qgd_Circuit_Wrapper_set_Qbit_Num, METH_VARARGS,
3251  "Call to set the number of qubits in the circuit"
3252  },
3253  {"get_Qbits", (PyCFunction) qgd_Circuit_Wrapper_get_Qbits, METH_NOARGS,
3254  "Call to get the list of qubits involved in the circuit"
3255  },
3256  {"set_min_fusion", (PyCFunction) qgd_Circuit_Wrapper_set_Min_Fusion, METH_VARARGS,
3257  "Call to set the min fusion in the circuit"
3258  },
3259  {"Remap_Qbits", (PyCFunction) qgd_Circuit_Wrapper_Remap_Qbits, METH_VARARGS,
3260  "Call to remap the qubits in the circuit."
3261  },
3262  {"get_Gate", (PyCFunction) qgd_Circuit_Wrapper_get_gate, METH_VARARGS,
3263  "Method to get the i-th decomposing gates."
3264  },
3265  {"get_Gates", (PyCFunction) qgd_Circuit_Wrapper_get_gates, METH_NOARGS,
3266  "Method to get the tuple of decomposing gates."
3267  },
3268  {"get_Gate_Nums", (PyCFunction) qgd_Circuit_Wrapper_get_Gate_Nums, METH_NOARGS,
3269  "Method to get statistics on the gate counts in the circuit."
3270  },
3271  {"get_Parameter_Start_Index", (PyCFunction) qgd_Circuit_Wrapper_get_Parameter_Start_Index, METH_NOARGS,
3272  "Call to get the starting index of the parameters in the parameter array corresponding to the circuit in which the current gate is incorporated."
3273  },
3274  {"Extract_Parameters", (PyCFunction) qgd_Circuit_Wrapper_Extract_Parameters, METH_VARARGS,
3275  "Call to extract the parameters corresponding to the gate from a parameter array associated with the circuit in which the gate is embedded."
3276  },
3277  {"get_Flat_Circuit", (PyCFunction) qgd_Circuit_Wrapper_get_Flat_Circuit, METH_NOARGS,
3278  "Method to generate a flat circuit. A flat circuit does not contain subcircuits: there are no Gates_block instances (containing subcircuits) in the resulting circuit. If the original circuit contains subcircuits, the gates in the subcircuits are directly incorporated in the resulting flat circuit."
3279  },
3280  {"get_Parents", (PyCFunction) qgd_Circuit_Wrapper_get_parents, METH_VARARGS,
3281  "Method to get the list of parent gate indices. Then the parent gates can be obtained from the list of gates involved in the circuit."
3282  },
3283  {"get_Children", (PyCFunction) qgd_Circuit_Wrapper_get_children, METH_VARARGS,
3284  "Method to get the list of child gate indices. Then the children gates can be obtained from the list of gates involved in the circuit."
3285  },
3286  {"__getstate__", (PyCFunction) qgd_Circuit_Wrapper_getstate, METH_NOARGS,
3287  "Method to extract the stored quantum circuit in a human-readable data serialized and pickle-able format."
3288  },
3289  {"__setstate__", (PyCFunction) qgd_Circuit_Wrapper_setstate, METH_VARARGS,
3290  "Call to set the state of a quantum circuit from a human-readable data serialized and pickle-able format."
3291  },
3292 
3293 
3294  {NULL} /* Sentinel */
3295 };
3296 
3297 
3302  PyVarObject_HEAD_INIT(NULL, 0)
3303  "qgd_Circuit_Wrapper.qgd_Circuit_Wrapper", /*tp_name*/
3304  sizeof(qgd_Circuit_Wrapper), /*tp_basicsize*/
3305  0, /*tp_itemsize*/
3306  (destructor) qgd_Circuit_Wrapper_dealloc, /*tp_dealloc*/
3307  #if PY_VERSION_HEX < 0x030800b4
3308  0, /*tp_print*/
3309  #endif
3310  #if PY_VERSION_HEX >= 0x030800b4
3311  0, /*tp_vectorcall_offset*/
3312  #endif
3313  0, /*tp_getattr*/
3314  0, /*tp_setattr*/
3315  #if PY_MAJOR_VERSION < 3
3316  0, /*tp_compare*/
3317  #endif
3318  #if PY_MAJOR_VERSION >= 3
3319  0, /*tp_as_async*/
3320  #endif
3321  0, /*tp_repr*/
3322  0, /*tp_as_number*/
3323  0, /*tp_as_sequence*/
3324  0, /*tp_as_mapping*/
3325  0, /*tp_hash*/
3326  0, /*tp_call*/
3327  0, /*tp_str*/
3328  0, /*tp_getattro*/
3329  0, /*tp_setattro*/
3330  0, /*tp_as_buffer*/
3331  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
3332  "Object to represent a qgd_Circuit_Wrapper class of the QGD package.", /*tp_doc*/
3333  0, /*tp_traverse*/
3334  0, /*tp_clear*/
3335  0, /*tp_richcompare*/
3336  0, /*tp_weaklistoffset*/
3337  0, /*tp_iter*/
3338  0, /*tp_iternext*/
3339  qgd_Circuit_Wrapper_Methods, /*tp_methods*/
3340  qgd_Circuit_Wrapper_Members, /*tp_members*/
3341  0, /*tp_getset*/
3342  0, /*tp_base*/
3343  0, /*tp_dict*/
3344  0, /*tp_descr_get*/
3345  0, /*tp_descr_set*/
3346  0, /*tp_dictoffset*/
3347  (initproc) qgd_Circuit_Wrapper_init, /*tp_init*/
3348  0, /*tp_alloc*/
3349  qgd_Circuit_Wrapper_new, /*tp_new*/
3350  0, /*tp_free*/
3351  0, /*tp_is_gc*/
3352  0, /*tp_bases*/
3353  0, /*tp_mro*/
3354  0, /*tp_cache*/
3355  0, /*tp_subclasses*/
3356  0, /*tp_weaklist*/
3357  0, /*tp_del*/
3358  0, /*tp_version_tag*/
3359  #if PY_VERSION_HEX >= 0x030400a1
3360  0, /*tp_finalize*/
3361  #endif
3362  #if PY_VERSION_HEX >= 0x030800b1
3363  0, /*tp_vectorcall*/
3364  #endif
3365  #if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
3366  0, /*tp_print*/
3367  #endif
3368 };
3369 
3373 static PyModuleDef qgd_Circuit_Wrapper_Module = {
3374  PyModuleDef_HEAD_INIT,
3375  "qgd_Circuit_Wrapper",
3376  "Python binding for QGD Circuit class",
3377  -1,
3378 };
3379 
3383 PyMODINIT_FUNC
3385 {
3386 
3387  // initialize Numpy API
3388  import_array();
3389 
3390  PyObject *m;
3391  if (PyType_Ready(&qgd_Circuit_Wrapper_Type) < 0)
3392  return NULL;
3393 
3394  m = PyModule_Create(&qgd_Circuit_Wrapper_Module);
3395  if (m == NULL)
3396  return NULL;
3397 
3398  Py_INCREF(&qgd_Circuit_Wrapper_Type);
3399  if (PyModule_AddObject(m, "qgd_Circuit_Wrapper", (PyObject *) &qgd_Circuit_Wrapper_Type) < 0) {
3400  Py_DECREF(&qgd_Circuit_Wrapper_Type);
3401  Py_DECREF(m);
3402  return NULL;
3403  }
3404 
3405  return m;
3406 }
3407 
3408 
3409 
3410 }
static PyObject * qgd_Circuit_Wrapper_set_Min_Fusion(qgd_Circuit_Wrapper *self, PyObject *args)
static PyObject * qgd_Circuit_Wrapper_get_Parameter_Num(qgd_Circuit_Wrapper *self)
Get the number of free parameters in the gate structure used for the decomposition.
Gates_block * get_flat_circuit()
Method to generate a flat circuit.
Copyright (C) Miklos Maroti, 2021 SPDX-License-Identifier: Apache-2.0.
Definition: U3.h:19
PyMODINIT_FUNC PyInit_qgd_Circuit_Wrapper(void)
Method called when the Python module is initialized.
Definition: X.h:11
static PyObject * qgd_Circuit_Wrapper_apply_derivate_to(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Evaluate the derivative of the circuit on an input matrix with respect to all free parameters (float6...
Header file for a class representing a CSWAP (Controlled SWAP) operation.
static PyObject * qgd_Circuit_Wrapper_get_Parameter_Start_Index(qgd_Circuit_Wrapper *self)
Call to get the starting index of the parameters in the parameter array corresponding to the circuit ...
Header file for a class representing a CP gate.
parameter_num
[set adaptive gate structure]
Class to store single-precision real arrays and properties.
PyObject_HEAD Gate * gate
Pointer to the C++ class of the base Gate gate.
Definition: U2.h:11
static PyObject * qgd_Circuit_Wrapper_get_parents(qgd_Circuit_Wrapper *self, PyObject *args)
Wrapper function to get the indices of parent gates.
Header file for a class representing a controlled Z rotation gate.
Definition: S.h:11
A class representing a controlled RX gate.
Definition: CRX.h:35
A class representing a CP gate.
Definition: CP.h:35
#define qgd_Circuit_Wrapper_add_two_qubit_gate(gate_name, GATE_NAME)
static PyObject * qgd_Circuit_Wrapper_get_Qbits(qgd_Circuit_Wrapper *self)
Call to retrieve the list of qubits involved in the circuit.
static PyObject * qgd_Circuit_Wrapper_get_gates(qgd_Circuit_Wrapper *self)
Call to get the incorporated gates in a Python list.
#define qgd_Circuit_Wrapper_add_one_qubit_gate(gate_name, GATE_NAME)
#define get_gate_template_one_qubit(GATE_NAME)
virtual Gate * clone()
Call to create a clone of the present class.
Definition: Gate.cpp:1351
void add_gate(Gate *gate)
Append a general gate to the list of gates.
Matrix to_float64() const
Convert to double precision.
Definition: matrix_float.cpp:8
key
Definition: noise.py:86
Header file for a class representing a controlled rotation gate around the Y axis.
return Py_BuildValue("i", 0)
static PyObject * get_gate(Gates_block *circuit, int &idx)
Call to get the metadata organized into Python dictionary of the idx-th gate.
Matrix_real numpy2matrix_real(PyArrayObject *arr)
Call to create a PIC matrix_real representation of a numpy array.
static int qgd_Circuit_Wrapper_init(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Method called when a python instance of the class qgd_Circuit_Wrapper is initialized.
static PyObject * qgd_Circuit_Wrapper_get_Gate_Nums(qgd_Circuit_Wrapper *self)
Call to get the counts of individual gates in the circuit.
static PyObject * qgd_Circuit_Wrapper_get_Qbit_Num(qgd_Circuit_Wrapper *self)
Call to retrieve the number of qubits in the circuit.
static PyObject * qgd_Circuit_Wrapper_get_children(qgd_Circuit_Wrapper *self, PyObject *args)
Wrapper function to get the indices of children gates.
Structure type representing single-precision complex numbers.
Definition: QGDTypes.h:46
Header file for a class representing a controlled rotation gate around the Y axis.
scalar * data
pointer to the stored data
Definition: matrix_base.hpp:48
Gates_block * create_remapped_circuit(const std::map< int, int > &qbit_map)
Call to create a new circuit with remapped qubits.
Header file for a class representing a CH operation.
Header file for a class representing a controlled X rotation gate.
Header file for a class representing a SWAP operation.
Type definition of the qgd_Gate Python class of the qgd_Gate module.
Header file for a class responsible for grouping gates into subcircuits. (Subcircuits can be nested) ...
PyObject * matrix_real_to_numpy(Matrix_real &mtx)
Call to make a numpy array from an instance of matrix class.
Definition: Tdg.h:11
virtual Gates_block * clone() override
Create a clone of the present class.
static PyObject * qgd_Circuit_Wrapper_Remap_Qbits(qgd_Circuit_Wrapper *self, PyObject *args)
Call to remap the qubits in the circuit.
Definition: U1.h:11
U3 RX RZ H Y SX S T CNOT CH SYC CRZ PyObject PyObject * kwds
Matrix_real_float numpy2matrix_real_float(PyArrayObject *arr)
Call to create a PIC matrix_real_float representation of a numpy array.
void release_Circuit(Gates_block *instance)
Call to deallocate an instance of Gates_block class.
Header file for a class representing a CCX (Toffoli) operation.
scalar * get_data() const
Call to get the pointer to the stored data.
static PyTypeObject qgd_Circuit_Wrapper_Type
A structure describing the type of the class Circuit.
static PyObject * qgd_Circuit_Wrapper_add_CSWAP(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Wrapper function to add a CSWAP gate to the front of the gate structure.
Class representing a RZZ gate.
static PyMemberDef qgd_Circuit_Wrapper_Members[]
Structure containing metadata about the members of class qgd_Circuit_Wrapper.
A class representing a CROT gate.
Definition: CROT.h:38
static PyObject * qgd_Circuit_Wrapper_apply_to(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Apply the gate circuit operation on the input matrix.
A class representing a CZ operation.
Definition: CZ.h:36
Header file for a class representing a CNOT operation.
Definition: Z.h:11
Definition: RZ.h:11
A class representing a CRY gate.
Definition: CRY.h:37
static void circuit_matrix_owner_capsule_destruct(PyObject *cap)
Capsule destructor: calls delete on a heap-allocated Matrix, which correctly decrements the reference...
U3 RX RZ H Y SX S T CNOT CH SYC CRZ PyObject * args
std::vector< int > get_control_qbits() const
Call to get the vector of control qubits.
Definition: Gate.cpp:1135
int rows
The number of rows.
Definition: matrix_base.hpp:42
A class representing a CH operation.
Definition: CH.h:36
int cols
The number of columns.
Definition: matrix_base.hpp:44
static PyObject * qgd_Circuit_Wrapper_setstate(qgd_Circuit_Wrapper *self, PyObject *args)
Call to set the state of a quantum circuit from a human-readable data serialized and pickle-able form...
Definition: CU.h:11
PyObject_HEAD Gates_block * gate
matrix_size
[load Umtx]
Definition: example.py:58
static PyObject * qgd_Circuit_Wrapper_add_Circuit(qgd_Circuit_Wrapper *self, PyObject *args)
Wrapper function to add a block of operations to the front of the gate structure. ...
static PyObject * qgd_Circuit_Wrapper_add_GENERAL(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Wrapper function to add a GENERAL_OPERATION gate from an explicit matrix.
gate_type get_type()
Call to get the type of the operation.
Definition: Gate.cpp:1333
static PyObject * qgd_Circuit_Wrapper_set_Qbit_Num(qgd_Circuit_Wrapper *self, PyObject *args)
Call to set the number of qubits in the circuit.
static PyObject * qgd_Circuit_Wrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
Method called when a python instance of the class qgd_Circuit_Wrapper is allocated.
static PyObject * qgd_Circuit_Wrapper_get_Second_Renyi_Entropy(qgd_Circuit_Wrapper *self, PyObject *args)
Wrapper function to evaluate the second Rényi entropy of a quantum circuit at a specific parameter s...
std::vector< Gate * > get_parents()
Call to get the parents of the current gate.
Definition: Gate.cpp:1301
static PyObject * qgd_Circuit_Wrapper_get_Flat_Circuit(qgd_Circuit_Wrapper *self)
Method to generate a flat circuit.
static PyObject * qgd_Circuit_Wrapper_get_gate(qgd_Circuit_Wrapper *self, PyObject *args)
Wrapper function to get a gate from the circuit.
Header file for a class representing a CZ operation.
static void circuit_matrix_float_owner_capsule_destruct(PyObject *cap)
Capsule destructor: calls delete on a heap-allocated Matrix_float, which correctly decrements the ref...
void set_owner(bool owner_in)
Call to set the current class instance to be (or not to be) the owner of the stored data array...
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
A class representing a CNOT operation.
Definition: CNOT.h:35
static void qgd_Circuit_Wrapper_dealloc(qgd_Circuit_Wrapper *self)
Method called when a python instance of the class qgd_Circuit_Wrapper is destroyed.
static PyObject * qgd_Circuit_Wrapper_apply_to_list(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Apply the circuit to a list of input matrices with float32/float64 dispatch.
static PyMethodDef qgd_Circuit_Wrapper_Methods[]
std::vector< Gate * > get_children()
Call to get the children of the current gate.
Definition: Gate.cpp:1312
std::vector< int > get_target_qbits() const
Call to get the vector of target qubits.
Definition: Gate.cpp:1143
Double-precision complex matrix (float64).
Definition: matrix.h:38
Definition: H.h:11
Definition: Y.h:11
Header file for a class representing a gate used in adaptive decomposition.
Definition: RY.h:11
int size() const
Call to get the number of the allocated elements.
Matrix_float numpy2matrix_float(PyArrayObject *arr)
Call to create a PIC matrix_float representation of a numpy array.
A class representing a controlled RZ gate.
Definition: CRZ.h:35
A class representing a CRY gate.
Definition: CR.h:37
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:44
static PyObject * qgd_Circuit_Wrapper_Extract_Parameters(qgd_Circuit_Wrapper *self, PyObject *args)
Call to extract the parameters corresponding to the gate from a parameter array associated with the c...
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
Definition: R.h:11
Fixed point data related to a gate operation.
Definition: common_DFE.h:62
int get_target_qbit()
Call to get the index of the target qubit.
Definition: Gate.cpp:1203
Base class for the representation of general gate operations.
Definition: Gate.h:86
Definition: SXdg.h:11
static PyModuleDef qgd_Circuit_Wrapper_Module
Structure containing metadata about the module.
PyObject * matrix_float_to_numpy(Matrix_float &mtx)
Call to make a numpy array from an instance of matrix_float class.
Header file for a class representing a controlled rotation gate around the Y axis.
Matrix numpy2matrix(PyArrayObject *arr)
Call to create a PIC matrix representation of a numpy array.
static PyObject * qgd_Circuit_Wrapper_getstate(qgd_Circuit_Wrapper *self)
Method to extract the stored quantum circuit in a human-readable data serialized and pickle-able form...
PyObject_HEAD Gates_block * circuit
Pointer to the C++ class of the base Gate_block module.
static PyObject * qgd_Circuit_Wrapper_add_RXX(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
static PyObject * qgd_Circuit_Wrapper_add_RZZ(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
A class representing a SYC operation.
Definition: SYC.h:36
Definition: SX.h:11
int get_qbit_num()
Call to get the number of qubits composing the unitary.
Definition: Gate.cpp:1342
gate_type
Type definition of operation types (also generalized for decomposition classes derived from the class...
Definition: Gate.h:39
Type definition for qgd_Circuit_Wrapper.
Definition: T.h:11
Gate * get_gate(int idx)
Call to get the gates stored in the class.
Definition: RX.h:11
Gates_block * create_Circuit(int qbit_num)
Creates an instance of class Gates_block (Circuit) and returns a pointer to the class instance...
#define get_gate_template_two_qubit(GATE_NAME)
static PyObject * qgd_Circuit_Wrapper_apply_from_right(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Apply the gate circuit from the right on the input matrix with float32/float64 dispatch.
static PyObject * qgd_Circuit_Wrapper_add_RYY(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
PyObject * matrix_to_numpy(Matrix &mtx)
Call to make a numpy array from an instance of matrix class.
static PyObject * qgd_Circuit_Wrapper_get_Matrix(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Extract the optimized parameters and return the matrix representation of the gate circuit...
static PyObject * qgd_Circuit_Wrapper_apply_to_combined(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Evaluate forward circuit action and all derivatives in one call.
int get_control_qbit()
Call to get the index of the control qubit.
Definition: Gate.cpp:1211
PyObject * target_qbits_py
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:41
Class representing a RYY gate.
CR static adaptive PyObject * qgd_Circuit_Wrapper_add_CCX(qgd_Circuit_Wrapper *self, PyObject *args, PyObject *kwds)
Wrapper function to add a CCX gate to the front of the gate structure.
Header file for a class representing a Sycamore gate.