Sequential Quantum Gate Decomposer  v1.9.7
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 
2129 
2130  if (gate->get_type() == CNOT_OPERATION) {
2131 
2132 
2133  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2134  // 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
2135  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "CNOT");
2136 
2137  PyObject* gate_input = Py_BuildValue("(OOO)", qbit_num, target_qbit, control_qbit);
2138  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2139 
2140  // replace dummy data with real gate data
2141  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2142  delete( py_gate_C->gate );
2143  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2144 
2145  Py_DECREF( qgd_gate );
2146  Py_DECREF( gate_input );
2147 
2148 
2149  }
2160  else if (gate->get_type() == ADAPTIVE_OPERATION) {
2161  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2162  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "CRY");
2163  PyObject* gate_input = Py_BuildValue("(OOO)", qbit_num, target_qbit, control_qbit);
2164  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2165  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2166  delete( py_gate_C->gate );
2167  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2168  }
2169  else if (gate->get_type() == SWAP_OPERATION){
2170  // SWAP now uses vector-based interface
2171  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2172  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2173  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2174  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2175  }
2176 
2177  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2178  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "SWAP");
2179 
2180  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbits_list);
2181  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2182 
2183  // replace dummy data with real gate data
2184  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2185  delete( py_gate_C->gate );
2186  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2187 
2188  Py_DECREF( qgd_gate );
2189  Py_DECREF( gate_input );
2190  Py_DECREF( target_qbits_list );
2191  }
2192 
2193  else if (gate->get_type() == RXX_OPERATION){
2194  // RXX now uses vector-based interface
2195  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2196  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2197  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2198  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2199  }
2200 
2201  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2202  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "RXX");
2203 
2204  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbits_list);
2205  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2206 
2207  // replace dummy data with real gate data
2208  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2209  delete( py_gate_C->gate );
2210  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2211 
2212  Py_DECREF( qgd_gate );
2213  Py_DECREF( gate_input );
2214  Py_DECREF( target_qbits_list );
2215  }
2216 
2217  else if (gate->get_type() == RYY_OPERATION){
2218  // RYY uses vector-based interface
2219  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2220  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2221  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2222  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2223  }
2224 
2225  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2226  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "RYY");
2227 
2228  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbits_list);
2229  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2230 
2231  // replace dummy data with real gate data
2232  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2233  delete( py_gate_C->gate );
2234  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2235 
2236  Py_DECREF( qgd_gate );
2237  Py_DECREF( gate_input );
2238  Py_DECREF( target_qbits_list );
2239  }
2240 
2241  else if (gate->get_type() == RZZ_OPERATION){
2242  // RZZ uses vector-based interface
2243  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2244  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2245  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2246  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2247  }
2248 
2249  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2250  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "RZZ");
2251 
2252  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbits_list);
2253  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2254 
2255  // replace dummy data with real gate data
2256  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2257  delete( py_gate_C->gate );
2258  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2259 
2260  Py_DECREF( qgd_gate );
2261  Py_DECREF( gate_input );
2262  Py_DECREF( target_qbits_list );
2263  }
2264 
2279  else if (gate->get_type() == SDG_OPERATION){
2280 
2281 
2282  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2283  // 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
2284  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "Sdg");
2285 
2286  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbit);
2287  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2288 
2289  // replace dummy data with real gate data
2290  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2291  delete( py_gate_C->gate );
2292  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2293 
2294  Py_DECREF( qgd_gate );
2295  Py_DECREF( gate_input );
2296 
2297 
2298  }
2299  else if (gate->get_type() == SXDG_OPERATION){
2300 
2301 
2302  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2303  // 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
2304  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "SXdg");
2305 
2306  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbit);
2307  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2308 
2309  // replace dummy data with real gate data
2310  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2311  delete( py_gate_C->gate );
2312  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2313 
2314  Py_DECREF( qgd_gate );
2315  Py_DECREF( gate_input );
2316 
2317 
2318  }
2319  else if (gate->get_type() == TDG_OPERATION){
2320 
2321 
2322  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2323  // 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
2324  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "Tdg");
2325 
2326  PyObject* gate_input = Py_BuildValue("(OO)", qbit_num, target_qbit);
2327  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2328 
2329  // replace dummy data with real gate data
2330  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2331  delete( py_gate_C->gate );
2332  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2333 
2334  Py_DECREF( qgd_gate );
2335  Py_DECREF( gate_input );
2336 
2337 
2338  }
2339  else if (gate->get_type() == CCX_OPERATION){
2340  // CCX now uses vector-based interface
2341  std::vector<int> control_qbits_vec = gate->get_control_qbits();
2342  PyObject* control_qbits_list = PyList_New((Py_ssize_t)control_qbits_vec.size());
2343  for (size_t i = 0; i < control_qbits_vec.size(); i++) {
2344  PyList_SetItem(control_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", control_qbits_vec[i]));
2345  }
2346 
2347  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2348  // 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
2349  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "CCX");
2350 
2351  PyObject* gate_input = Py_BuildValue("(OOO)", qbit_num, target_qbit, control_qbits_list);
2352  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2353 
2354  // replace dummy data with real gate data
2355  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2356  delete( py_gate_C->gate );
2357  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2358 
2359  Py_DECREF( qgd_gate );
2360  Py_DECREF( gate_input );
2361  Py_DECREF( control_qbits_list );
2362 
2363  Py_XDECREF(qbit_num);
2364  Py_XDECREF(target_qbit);
2365  Py_XDECREF(control_qbit);
2366 
2367  return py_gate;
2368 
2369  }
2370  else if (gate->get_type() == CSWAP_OPERATION){
2371  // CSWAP now uses vector-based interface
2372  std::vector<int> target_qbits_vec = gate->get_target_qbits();
2373  PyObject* target_qbits_list = PyList_New((Py_ssize_t)target_qbits_vec.size());
2374  for (size_t i = 0; i < target_qbits_vec.size(); i++) {
2375  PyList_SetItem(target_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", target_qbits_vec[i]));
2376  }
2377 
2378  std::vector<int> control_qbits_vec = gate->get_control_qbits();
2379  PyObject* control_qbits_list = PyList_New((Py_ssize_t)control_qbits_vec.size());
2380  for (size_t i = 0; i < control_qbits_vec.size(); i++) {
2381  PyList_SetItem(control_qbits_list, (Py_ssize_t)i, Py_BuildValue("i", control_qbits_vec[i]));
2382  }
2383 
2384  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate );
2385  // 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
2386  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "CSWAP");
2387 
2388  PyObject* gate_input = Py_BuildValue("(OOO)", qbit_num, target_qbits_list, control_qbits_list);
2389  py_gate = PyObject_CallObject(py_gate_class, gate_input);
2390 
2391  // replace dummy data with real gate data
2392  qgd_Gate* py_gate_C = reinterpret_cast<qgd_Gate*>( py_gate );
2393  delete( py_gate_C->gate );
2394  py_gate_C->gate = static_cast<Gate*>( gate->clone() );
2395 
2396  Py_DECREF( qgd_gate );
2397  Py_DECREF( gate_input );
2398  Py_DECREF( target_qbits_list );
2399  Py_DECREF( control_qbits_list );
2400 
2401  Py_XDECREF(qbit_num);
2402  Py_XDECREF(target_qbit);
2403  Py_XDECREF(control_qbit);
2404 
2405  return py_gate;
2406 
2407  }
2408  else if (gate->get_type() == BLOCK_OPERATION) {
2409 
2410  // import gate operation modules
2411  PyObject* qgd_circuit = PyImport_ImportModule("squander.gates.qgd_Circuit");
2412 
2413  if ( qgd_circuit == NULL ) {
2414  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.qgd_Circuit" );
2415  return NULL;
2416  }
2417 
2418  PyObject* qgd_circuit_Dict = PyModule_GetDict( qgd_circuit );
2419 
2420  // 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
2421  PyObject* py_circuit_class = PyDict_GetItemString( qgd_circuit_Dict, "qgd_Circuit");
2422 
2423  PyObject* circuit_input = Py_BuildValue("(O)", qbit_num );
2424  py_gate = PyObject_CallObject(py_circuit_class, circuit_input);
2425 
2426  // replace dummy data with real gate data
2427  qgd_Circuit_Wrapper* py_gate_C = reinterpret_cast<qgd_Circuit_Wrapper*>( py_gate );
2428 
2429  Gates_block* circuit = reinterpret_cast<Gates_block*>( gate );
2430  delete( py_gate_C->circuit );
2431  py_gate_C->circuit = circuit->clone();
2432 
2433  Py_DECREF( qgd_circuit );
2434  Py_DECREF( circuit_input );
2435 
2436  }
2437  else {
2438 
2439  Py_DECREF( qgd_gate );
2440  Py_XDECREF(qbit_num);
2441  Py_XDECREF(target_qbit);
2442  Py_XDECREF(control_qbit);
2443  PyErr_SetString(PyExc_Exception, "qgd_Circuit_Wrapper::get_gate: unimplemented gate type" );
2444  return NULL;
2445  }
2446 
2447  Py_XDECREF(qbit_num);
2448  Py_XDECREF(target_qbit);
2449  Py_XDECREF(control_qbit);
2450 
2451  return py_gate;
2452 
2453 }
2454 
2455 
2456 
2463 static PyObject *
2465 
2466  // initiate variables for input arguments
2467  int idx;
2468 
2469  // parsing input arguments
2470  if (!PyArg_ParseTuple(args, "|i", &idx )) return Py_BuildValue("i", -1);
2471 
2472 
2473  return get_gate( self->circuit, idx );
2474 
2475 
2476 }
2477 
2478 
2484 static PyObject *
2486 
2487  std::map< std::string, int > gate_nums;
2488 
2489  try {
2490  gate_nums = self->circuit->get_gate_nums();
2491  }
2492  catch (std::string err) {
2493  PyErr_SetString(PyExc_Exception, err.c_str());
2494  return NULL;
2495  }
2496  catch(...) {
2497  std::string err( "Invalid pointer to circuit class");
2498  PyErr_SetString(PyExc_Exception, err.c_str());
2499  return NULL;
2500  }
2501 
2502 
2503  PyObject* gate_nums_py = PyDict_New();
2504  if( gate_nums_py == NULL ) {
2505  std::string err( "Failed to create dictionary");
2506  PyErr_SetString(PyExc_Exception, err.c_str());
2507  return NULL;
2508  }
2509 
2510  for( auto it = gate_nums.begin(); it != gate_nums.end(); it++ ) {
2511 
2512  PyObject* key = Py_BuildValue( "s", it->first.c_str() );
2513  PyObject* val = Py_BuildValue("i", it->second );
2514 
2515  if ( key == NULL || val == NULL ) {
2516  Py_XDECREF(key);
2517  Py_XDECREF(val);
2518  Py_DECREF(gate_nums_py);
2519  return NULL;
2520  }
2521 
2522  if ( PyDict_SetItem(gate_nums_py, key, val) != 0 ) {
2523  Py_DECREF(key);
2524  Py_DECREF(val);
2525  Py_DECREF(gate_nums_py);
2526  return NULL;
2527  }
2528 
2529  Py_DECREF(key);
2530  Py_DECREF(val);
2531  }
2532 
2533  return gate_nums_py;
2534 
2535 }
2536 
2537 
2538 
2539 
2545 static PyObject *
2547 
2548 
2549 
2550 
2551  // get the number of gates
2552  int op_num = self->circuit->get_gate_num();
2553 
2554  // preallocate Python tuple for the output
2555  PyObject* ret = PyTuple_New( (Py_ssize_t) op_num );
2556 
2557 
2558 
2559  // iterate over the gates to get the gate list
2560  for (int idx = 0; idx < op_num; idx++ ) {
2561 
2562  // get metadata about the idx-th gate
2563  PyObject* gate = get_gate( self->circuit, idx );
2564 
2565  // adding gate information to the tuple
2566  PyTuple_SetItem( ret, (Py_ssize_t) idx, gate );
2567 
2568 
2569  }
2570 
2571  return ret;
2572 
2573 }
2574 
2575 
2576 
2583 static PyObject *
2585 
2586  // the gate for which we look for the parents
2587  PyObject* py_gate = NULL;
2588 
2589  // parsing input arguments
2590  if (!PyArg_ParseTuple(args, "|O", &py_gate )) return Py_BuildValue("i", -1);
2591 
2592 
2593  if( py_gate == NULL ) {
2594  return PyTuple_New( 0 );
2595  }
2596 
2597  qgd_Gate* gate_struct = reinterpret_cast<qgd_Gate*>( py_gate );
2598  std::vector<Gate*> parents = gate_struct->gate->get_parents();
2599 
2600  // preallocate tuple for the output
2601  PyObject* parent_tuple = PyTuple_New( (Py_ssize_t) parents.size() );
2602 
2603  std::vector<Gate*>&& gates = self->circuit->get_gates();
2604 
2605 
2606  // find the indices of the parents
2607  for(size_t idx=0; idx<parents.size(); idx++) {
2608 
2609  Gate* parent_gate = parents[idx];
2610 
2611  // find the index of the parent_gate
2612  int parent_idx = -1;
2613  for( size_t jdx=0; jdx<gates.size(); jdx++ ) {
2614 
2615  Gate* gate = gates[jdx];
2616 
2617  if( parent_gate == gate ) {
2618  parent_idx = static_cast<int>(jdx);
2619  break;
2620  }
2621 
2622  if( jdx == static_cast<size_t>(gates.size()-1) ) {
2623  std::string err( "Parent gate did not found in the circuit. May be the gate is not in the circuit");
2624  PyErr_SetString(PyExc_Exception, err.c_str());
2625  return NULL;
2626  }
2627 
2628  }
2629 
2630  // adding parent_idx the tuple
2631  PyTuple_SetItem( parent_tuple, (Py_ssize_t) idx, Py_BuildValue("i", parent_idx) );
2632 
2633 
2634  }
2635 
2636 
2637  return parent_tuple;
2638 
2639 
2640 }
2641 
2642 
2643 
2644 
2651 static PyObject *
2653 
2654  // the gate for which we look for the children
2655  PyObject* py_gate = NULL;
2656 
2657  // parsing input arguments
2658  if (!PyArg_ParseTuple(args, "|O", &py_gate )) return Py_BuildValue("i", -1);
2659 
2660 
2661  if( py_gate == NULL ) {
2662  return PyTuple_New( 0 );
2663  }
2664 
2665  qgd_Gate* gate_struct = reinterpret_cast<qgd_Gate*>( py_gate );
2666  std::vector<Gate*> children = gate_struct->gate->get_children();
2667 
2668  // preallocate tuple for the output
2669  PyObject* children_tuple = PyTuple_New( (Py_ssize_t) children.size() );
2670 
2671  std::vector<Gate*>&& gates = self->circuit->get_gates();
2672 
2673 
2674  // find the indices of the children
2675  for(size_t idx=0; idx<children.size(); idx++) {
2676 
2677  Gate* child_gate = children[idx];
2678 
2679  // find the index of the child_gate
2680  int child_idx = -1;
2681  for( size_t jdx=0; jdx<gates.size(); jdx++ ) {
2682 
2683  Gate* gate = gates[jdx];
2684 
2685  if( child_gate == gate ) {
2686  child_idx = static_cast<int>(jdx);
2687  break;
2688  }
2689 
2690  if( jdx == static_cast<size_t>(gates.size()-1) ) {
2691  std::string err( "Child gate did not found in the circuit. May be the gate is not in the circuit");
2692  PyErr_SetString(PyExc_Exception, err.c_str());
2693  return NULL;
2694  }
2695 
2696  }
2697 
2698  // adding child_idx the tuple
2699  PyTuple_SetItem( children_tuple, (Py_ssize_t) idx, Py_BuildValue("i", child_idx) );
2700 
2701 
2702  }
2703 
2704 
2705  return children_tuple;
2706 
2707 
2708 }
2709 
2710 
2711 
2718 static PyObject *
2720 
2721  PyArrayObject * parameters_arr = NULL;
2722 
2723 
2724  // parsing input arguments
2725  if (!PyArg_ParseTuple(args, "|O", &parameters_arr ))
2726  return Py_BuildValue("i", -1);
2727 
2728 
2729  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
2730  Py_INCREF(parameters_arr);
2731  }
2732  else {
2733  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
2734  }
2735 
2736  // get the C++ wrapper around the data
2737  Matrix_real&& parameters_mtx = numpy2matrix_real( parameters_arr );
2738 
2739 
2740 
2741  Matrix_real extracted_parameters;
2742 
2743  try {
2744  extracted_parameters = self->circuit->extract_parameters( parameters_mtx );
2745  }
2746  catch (std::string err) {
2747  PyErr_SetString(PyExc_Exception, err.c_str());
2748  return NULL;
2749  }
2750  catch(...) {
2751  std::string err( "Invalid pointer to circuit class");
2752  PyErr_SetString(PyExc_Exception, err.c_str());
2753  return NULL;
2754  }
2755 
2756 
2757  // convert to numpy array
2758  extracted_parameters.set_owner(false);
2759  PyObject *extracted_parameters_py = matrix_real_to_numpy( extracted_parameters );
2760 
2761 
2762  return extracted_parameters_py;
2763 }
2764 
2765 
2766 
2773 static PyObject *
2775 
2776  PyObject* filename_py = NULL;
2777  PyObject* parameters_obj = NULL;
2778 
2779  if (!PyArg_ParseTuple(args, "OO", &filename_py, &parameters_obj)) {
2780  return NULL;
2781  }
2782 
2783  PyObject* filename_string = PyObject_Str(filename_py);
2784  if (filename_string == NULL) {
2785  return NULL;
2786  }
2787  PyObject* filename_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
2788  Py_DECREF(filename_string);
2789  if (filename_unicode == NULL) {
2790  return NULL;
2791  }
2792  const char* filename_C = PyBytes_AS_STRING(filename_unicode);
2793  std::string filename_str(filename_C);
2794 
2795  PyArrayObject* parameters_arr = NULL;
2796  if ( PyArray_Check(parameters_obj) && PyArray_IS_C_CONTIGUOUS((PyArrayObject*)parameters_obj) &&
2797  PyArray_TYPE((PyArrayObject*)parameters_obj) == NPY_DOUBLE ) {
2798  parameters_arr = (PyArrayObject*)parameters_obj;
2799  Py_INCREF(parameters_arr);
2800  }
2801  else {
2802  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF(parameters_obj, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
2803  }
2804 
2805  if (parameters_arr == NULL) {
2806  Py_DECREF(filename_unicode);
2807  PyErr_SetString(PyExc_TypeError, "export_to_Binary: parameters must be a float64 numpy array");
2808  return NULL;
2809  }
2810 
2811  Matrix_real parameters_mtx = numpy2matrix_real(parameters_arr);
2812 
2813  try {
2814  if (parameters_mtx.size() != self->circuit->get_parameter_num()) {
2815  std::string err("export_to_Binary: number of parameters (" +
2816  std::to_string(parameters_mtx.size()) +
2817  ") must equal the number of free parameters in the circuit (" +
2818  std::to_string(self->circuit->get_parameter_num()) + ").");
2819  throw err;
2820  }
2821  export_gate_list_to_binary(parameters_mtx, self->circuit, filename_str);
2822  }
2823  catch (std::string err) {
2824  Py_DECREF(parameters_arr);
2825  Py_DECREF(filename_unicode);
2826  PyErr_SetString(PyExc_Exception, err.c_str());
2827  return NULL;
2828  }
2829  catch (...) {
2830  Py_DECREF(parameters_arr);
2831  Py_DECREF(filename_unicode);
2832  PyErr_SetString(PyExc_Exception, "export_to_Binary: failed to export circuit");
2833  return NULL;
2834  }
2835 
2836  Py_DECREF(parameters_arr);
2837  Py_DECREF(filename_unicode);
2838  Py_RETURN_NONE;
2839 
2840 }
2841 
2842 
2848 static PyObject *
2850 
2851  Gates_block* flat_circuit = self->circuit->get_flat_circuit();
2852  int qbit_num = flat_circuit->get_qbit_num();
2853 
2854  // import gate operation modules
2855  PyObject* qgd_circuit = PyImport_ImportModule("squander.gates.qgd_Circuit");
2856 
2857  if ( qgd_circuit == NULL ) {
2858  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.qgd_Circuit" );
2859  return NULL;
2860  }
2861 
2862  PyObject* qgd_circuit_Dict = PyModule_GetDict( qgd_circuit );
2863 
2864  // 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
2865  PyObject* py_circuit_class = PyDict_GetItemString( qgd_circuit_Dict, "qgd_Circuit");
2866 
2867  PyObject* qbit_num_py = Py_BuildValue("i", qbit_num);
2868  if ( qbit_num_py == NULL ) {
2869  Py_DECREF( qgd_circuit );
2870  return NULL;
2871  }
2872 
2873  PyObject* circuit_input = Py_BuildValue("(O)", qbit_num_py);
2874  Py_DECREF( qbit_num_py );
2875  if ( circuit_input == NULL ) {
2876  Py_DECREF( qgd_circuit );
2877  return NULL;
2878  }
2879  PyObject* py_circuit = PyObject_CallObject(py_circuit_class, circuit_input);
2880 
2881  // replace dummy data with real gate data
2882  qgd_Circuit_Wrapper* py_circuit_C = reinterpret_cast<qgd_Circuit_Wrapper*>( py_circuit );
2883  delete( py_circuit_C->circuit );
2884  py_circuit_C->circuit = flat_circuit;
2885 
2886 
2887  Py_DECREF( qgd_circuit );
2888  Py_DECREF( circuit_input );
2889 
2890  return py_circuit;
2891 }
2892 
2893 
2894 
2895 
2901 static PyObject *
2903 
2904 
2905  // get the number of gates
2906  int op_num = self->circuit->get_gate_num();
2907 
2908  // preallocate Python tuple for the output
2909  PyObject* ret = PyTuple_New( (Py_ssize_t) op_num+1 );
2910 
2911 
2912 
2913 
2914  // add qbit num value to the return tuple
2915  PyObject* qbit_num_dict = PyDict_New();
2916 
2917  if( qbit_num_dict == NULL ) {
2918  std::string err( "Failed to create dictionary");
2919  PyErr_SetString(PyExc_Exception, err.c_str());
2920  return NULL;
2921  }
2922 
2923  int qbit_num = self->circuit->get_qbit_num();
2924  PyObject* qbit_num_val = Py_BuildValue("i", qbit_num );
2925  if (qbit_num_val == NULL || PyDict_SetItemString(qbit_num_dict, "qbit_num", qbit_num_val) != 0) {
2926  Py_XDECREF(qbit_num_val);
2927  Py_DECREF(qbit_num_dict);
2928  Py_DECREF(ret);
2929  return NULL;
2930  }
2931 
2932  PyTuple_SetItem( ret, 0, qbit_num_dict );
2933 
2934  Py_DECREF( qbit_num_val );
2935 
2936 
2937  PyObject* method_name = PyUnicode_FromString("__getstate__");
2938  if (method_name == NULL) {
2939  Py_DECREF(ret);
2940  return NULL;
2941  }
2942 
2943  PyObject* qbit_num_key = PyUnicode_FromString("qbit_num");
2944  if (qbit_num_key == NULL) {
2945  Py_DECREF(method_name);
2946  Py_DECREF(ret);
2947  return NULL;
2948  }
2949 
2950  // iterate over the gates to get the gate list
2951  for (int idx = 0; idx < op_num; idx++ ) {
2952 
2953  // get metadata about the idx-th gate
2954  PyObject* gate = get_gate( self->circuit, idx );
2955  if (gate == NULL) {
2956  Py_DECREF(qbit_num_key);
2957  Py_DECREF(method_name);
2958  Py_DECREF(ret);
2959  return NULL;
2960  }
2961 
2962  PyObject* gate_state = PyObject_CallMethodObjArgs( gate, method_name, NULL );
2963  if (gate_state == NULL) {
2964  Py_DECREF(gate);
2965  Py_DECREF(qbit_num_key);
2966  Py_DECREF(method_name);
2967  Py_DECREF(ret);
2968  return NULL;
2969  }
2970 
2971 
2972  // remove the field qbit_num from gate dict sice this will be redundant information
2973  if ( PyDict_Contains(gate_state, qbit_num_key) == 1 ) {
2974 
2975  if ( PyDict_DelItem(gate_state, qbit_num_key) != 0 ) {
2976  std::string err( "Failed to delete item qbit_num from gate state");
2977  PyErr_SetString(PyExc_Exception, err.c_str());
2978  Py_DECREF(gate_state);
2979  Py_DECREF(gate);
2980  Py_DECREF(qbit_num_key);
2981  Py_DECREF(method_name);
2982  Py_DECREF(ret);
2983  return NULL;
2984  }
2985 
2986  }
2987 
2988 
2989  // adding gate information to the tuple
2990  PyTuple_SetItem( ret, (Py_ssize_t) idx+1, gate_state );
2991 
2992 
2993 
2994  Py_DECREF( gate );
2995  //Py_DECREF( gate_state );
2996 
2997  }
2998 
2999  Py_DECREF( qbit_num_key );
3000  Py_DECREF( method_name );
3001 
3002  return ret;
3003 }
3004 
3005 
3006 
3013 static PyObject *
3015 
3016  PyObject* state = NULL;
3017 
3018  // parsing input arguments
3019  if (!PyArg_ParseTuple(args, "|O", &state )) {
3020  std::string err( "Unable to parse state argument");
3021  PyErr_SetString(PyExc_Exception, err.c_str());
3022  return NULL;
3023  }
3024 
3025  if ( PyTuple_Size(state) == 0 ) {
3026  std::string err( "State should contain at least one element");
3027  PyErr_SetString(PyExc_Exception, err.c_str());
3028  return NULL;
3029  }
3030 
3031  PyObject* qbit_num_dict = PyTuple_GetItem( state, 0); // borrowed reference
3032 
3033 
3034  PyObject* qbit_num_key = Py_BuildValue( "s", "qbit_num" );
3035 
3036  if ( PyDict_Contains(qbit_num_dict, qbit_num_key) == 0 ) {
3037  std::string err( "The first entry of the circuit state should be the number of qubits");
3038  PyErr_SetString(PyExc_Exception, err.c_str());
3039 
3040  Py_DECREF( qbit_num_key );
3041  return NULL;
3042  }
3043 
3044  PyObject* qbit_num_py = PyDict_GetItem(qbit_num_dict, qbit_num_key); // borrowed reference
3045 
3046  if( !PyLong_Check(qbit_num_py) ) {
3047  std::string err( "The number of qubits should be an integer value");
3048  PyErr_SetString(PyExc_Exception, err.c_str());
3049 
3050  Py_DECREF( qbit_num_key );
3051  return NULL;
3052  }
3053 
3054 
3055  int qbit_num = (int)PyLong_AsLong( qbit_num_py );
3056 
3057 
3058  // import gate operation modules
3059  PyObject* qgd_gate = PyImport_ImportModule("squander.gates.gates_Wrapper");
3060 
3061  if ( qgd_gate == NULL ) {
3062  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.gates_Wrapper" );
3063  Py_DECREF( qbit_num_key );
3064  return NULL;
3065  }
3066 
3067  PyObject* qgd_gate_Dict = PyModule_GetDict( qgd_gate ); // borrowed reference ???
3068  PyObject* py_gate_class = PyDict_GetItemString( qgd_gate_Dict, "Gate"); // borrowed reference
3069  PyObject* setstate_name = Py_BuildValue( "s", "__setstate__" );
3070  PyObject* dummy_target_qbit = Py_BuildValue( "i", 0 );
3071 
3072  // now build up the quantum circuit
3073  try {
3074 
3075  self->circuit->release_gates();
3076  self->circuit->set_qbit_num( qbit_num );
3077 
3078  int gates_idx_max = (int) PyTuple_Size(state);
3079 
3080  for( int gate_idx=1; gate_idx < gates_idx_max; gate_idx++ ) {
3081 
3082 
3083  // get gate state as python dictionary
3084  PyObject* gate_state_dict = PyTuple_GetItem( state, gate_idx); // borrowed reference
3085 
3086  if( !PyDict_Check( gate_state_dict ) ) {
3087  std::string err( "Gate state should be given by a dictionary");
3088  PyErr_SetString(PyExc_Exception, err.c_str());
3089 
3090  Py_DECREF( qgd_gate );
3091  Py_DECREF( qbit_num_key );
3092  Py_DECREF( setstate_name );
3093  Py_DECREF( dummy_target_qbit );
3094  return NULL;
3095  }
3096 
3097  PyDict_SetItem(gate_state_dict, qbit_num_key, qbit_num_py);
3098 
3099  PyObject* gate_input = Py_BuildValue( "(O)", qbit_num_py );
3100  PyObject* py_gate = PyObject_CallObject(py_gate_class, gate_input);
3101  if (py_gate == NULL) {
3102  Py_DECREF(gate_input);
3103  Py_DECREF( qgd_gate );
3104  Py_DECREF( qbit_num_key );
3105  Py_DECREF( setstate_name );
3106  Py_DECREF( dummy_target_qbit );
3107  return NULL;
3108  }
3109 
3110  // turn the generic gate into a specific gate
3111  PyObject* setstate_ret = PyObject_CallMethodObjArgs( py_gate, setstate_name, gate_state_dict, NULL );
3112  if (setstate_ret == NULL) {
3113  Py_DECREF( gate_input );
3114  Py_DECREF( py_gate );
3115  Py_DECREF( qgd_gate );
3116  Py_DECREF( qbit_num_key );
3117  Py_DECREF( setstate_name );
3118  Py_DECREF( dummy_target_qbit );
3119  return NULL;
3120  }
3121  Py_DECREF(setstate_ret);
3122 
3123  Gate* gate_loc = static_cast<Gate*>( ((qgd_Gate*)py_gate)->gate->clone() );
3124  self->circuit->add_gate( gate_loc );
3125 
3126 
3127  Py_DECREF( gate_input );
3128  Py_DECREF( py_gate );
3129 
3130 
3131 
3132  }
3133 
3134 
3135  }
3136  catch (std::string err) {
3137  Py_DECREF( qgd_gate );
3138  Py_DECREF( qbit_num_key );
3139  Py_DECREF( setstate_name );
3140  Py_DECREF( dummy_target_qbit );
3141  PyErr_SetString(PyExc_Exception, err.c_str());
3142  return NULL;
3143  }
3144  catch(...) {
3145  Py_DECREF( qgd_gate );
3146  Py_DECREF( qbit_num_key );
3147  Py_DECREF( setstate_name );
3148  Py_DECREF( dummy_target_qbit );
3149  std::string err( "Invalid pointer to circuit class");
3150  PyErr_SetString(PyExc_Exception, err.c_str());
3151  return NULL;
3152  }
3153 
3154 
3155 
3156 
3157  Py_DECREF( qgd_gate );
3158  //Py_DECREF( qgd_gate_Dict );
3159  Py_DECREF( qbit_num_key );
3160  Py_DECREF( setstate_name );
3161  Py_DECREF( dummy_target_qbit );
3162 
3163 
3164  return Py_BuildValue("");
3165 }
3166 
3167 
3168 
3174 static PyObject *
3176 
3177  int start_index = self->circuit->get_parameter_start_idx();
3178 
3179  return Py_BuildValue("i", start_index);
3180 
3181 }
3182 
3183 
3184 static PyMethodDef qgd_Circuit_Wrapper_Methods[] = {
3185  {"add_U1", (PyCFunction) qgd_Circuit_Wrapper_add_U1, METH_VARARGS | METH_KEYWORDS,
3186  "Call to add a U1 gate to the front of the gate structure"
3187  },
3188  {"add_U2", (PyCFunction) qgd_Circuit_Wrapper_add_U2, METH_VARARGS | METH_KEYWORDS,
3189  "Call to add a U2 gate to the front of the gate structure"
3190  },
3191  {"add_U3", (PyCFunction) qgd_Circuit_Wrapper_add_U3, METH_VARARGS | METH_KEYWORDS,
3192  "Call to add a U3 gate to the front of the gate structure"
3193  },
3194  {"add_RX", (PyCFunction) qgd_Circuit_Wrapper_add_RX, METH_VARARGS | METH_KEYWORDS,
3195  "Call to add a RX gate to the front of the gate structure"
3196  },
3197  {"add_RXX", (PyCFunction) qgd_Circuit_Wrapper_add_RXX, METH_VARARGS | METH_KEYWORDS,
3198  "Call to add a RXX gate to the front of the gate structure"
3199  },
3200  {"add_RYY", (PyCFunction) qgd_Circuit_Wrapper_add_RYY, METH_VARARGS | METH_KEYWORDS,
3201  "Call to add a RYY gate to the front of the gate structure"
3202  },
3203  {"add_RZZ", (PyCFunction) qgd_Circuit_Wrapper_add_RZZ, METH_VARARGS | METH_KEYWORDS,
3204  "Call to add a RZZ gate to the front of the gate structure"
3205  },
3206  {"add_R", (PyCFunction) qgd_Circuit_Wrapper_add_R, METH_VARARGS | METH_KEYWORDS,
3207  "Call to add a R gate to the front of the gate structure"
3208  },
3209  {"add_RY", (PyCFunction) qgd_Circuit_Wrapper_add_RY, METH_VARARGS | METH_KEYWORDS,
3210  "Call to add a RY gate to the front of the gate structure"
3211  },
3212  {"add_RZ", (PyCFunction) qgd_Circuit_Wrapper_add_RZ, METH_VARARGS | METH_KEYWORDS,
3213  "Call to add a RZ gate to the front of the gate structure"
3214  },
3215  {"add_CNOT", (PyCFunction) qgd_Circuit_Wrapper_add_CNOT, METH_VARARGS | METH_KEYWORDS,
3216  "Call to add a CNOT gate to the front of the gate structure"
3217  },
3218  {"add_CZ", (PyCFunction) qgd_Circuit_Wrapper_add_CZ, METH_VARARGS | METH_KEYWORDS,
3219  "Call to add a CZ gate to the front of the gate structure"
3220  },
3221  {"add_CU", (PyCFunction) qgd_Circuit_Wrapper_add_CU, METH_VARARGS | METH_KEYWORDS,
3222  "Call to add a CU gate to the front of the gate structure"
3223  },
3224  {"add_CH", (PyCFunction) qgd_Circuit_Wrapper_add_CH, METH_VARARGS | METH_KEYWORDS,
3225  "Call to add a CH gate to the front of the gate structure"
3226  },
3227  {"add_SYC", (PyCFunction) qgd_Circuit_Wrapper_add_SYC, METH_VARARGS | METH_KEYWORDS,
3228  "Call to add a Sycamore gate to the front of the gate structure"
3229  },
3230  {"add_H", (PyCFunction) qgd_Circuit_Wrapper_add_H, METH_VARARGS | METH_KEYWORDS,
3231  "Call to add a Hadamard gate to the front of the gate structure"
3232  },
3233  {"add_X", (PyCFunction) qgd_Circuit_Wrapper_add_X, METH_VARARGS | METH_KEYWORDS,
3234  "Call to add a X gate to the front of the gate structure"
3235  },
3236  {"add_Y", (PyCFunction) qgd_Circuit_Wrapper_add_Y, METH_VARARGS | METH_KEYWORDS,
3237  "Call to add a Y gate to the front of the gate structure"
3238  },
3239  {"add_Z", (PyCFunction) qgd_Circuit_Wrapper_add_Z, METH_VARARGS | METH_KEYWORDS,
3240  "Call to add a Z gate to the front of the gate structure"
3241  },
3242  {"add_SX", (PyCFunction) qgd_Circuit_Wrapper_add_SX, METH_VARARGS | METH_KEYWORDS,
3243  "Call to add a SX gate to the front of the gate structure"
3244  },
3245  {"add_SXdg", (PyCFunction) qgd_Circuit_Wrapper_add_SXdg, METH_VARARGS | METH_KEYWORDS,
3246  "Call to add a SXdg gate to the front of the gate structure"
3247  },
3248  {"add_S", (PyCFunction) qgd_Circuit_Wrapper_add_S, METH_VARARGS | METH_KEYWORDS,
3249  "Call to add a S gate to the front of the gate structure"
3250  },
3251  {"add_Sdg", (PyCFunction) qgd_Circuit_Wrapper_add_Sdg, METH_VARARGS | METH_KEYWORDS,
3252  "Call to add a Sdg gate to the front of the gate structure"
3253  },
3254  {"add_T", (PyCFunction) qgd_Circuit_Wrapper_add_T, METH_VARARGS | METH_KEYWORDS,
3255  "Call to add a T gate to the front of the gate structure"
3256  },
3257  {"add_Tdg", (PyCFunction) qgd_Circuit_Wrapper_add_Tdg, METH_VARARGS | METH_KEYWORDS,
3258  "Call to add a Tdg gate to the front of the gate structure"
3259  },
3260  {"add_CRY", (PyCFunction) qgd_Circuit_Wrapper_add_CRY, METH_VARARGS | METH_KEYWORDS,
3261  "Call to add a CRY gate to the front of the gate structure"
3262  },
3263  {"add_CRX", (PyCFunction) qgd_Circuit_Wrapper_add_CRX, METH_VARARGS | METH_KEYWORDS,
3264  "Call to add a CRY gate to the front of the gate structure"
3265  },
3266  {"add_CRZ", (PyCFunction) qgd_Circuit_Wrapper_add_CRZ, METH_VARARGS | METH_KEYWORDS,
3267  "Call to add a CRY gate to the front of the gate structure"
3268  },
3269  {"add_CP", (PyCFunction) qgd_Circuit_Wrapper_add_CP, METH_VARARGS | METH_KEYWORDS,
3270  "Call to add a CRY gate to the front of the gate structure"
3271  },
3272  {"add_CCX", (PyCFunction) qgd_Circuit_Wrapper_add_CCX, METH_VARARGS | METH_KEYWORDS,
3273  "Call to add a CCX gate to the front of the gate structure"
3274  },
3275  {"add_SWAP", (PyCFunction) qgd_Circuit_Wrapper_add_SWAP, METH_VARARGS | METH_KEYWORDS,
3276  "Call to add a SWAP gate to the front of the gate structure"
3277  },
3278  {"add_CSWAP", (PyCFunction) qgd_Circuit_Wrapper_add_CSWAP, METH_VARARGS | METH_KEYWORDS,
3279  "Call to add a CSWAP gate to the front of the gate structure"
3280  },
3281  {"add_CROT", (PyCFunction) qgd_Circuit_Wrapper_add_CROT, METH_VARARGS | METH_KEYWORDS,
3282  "Call to add a CROT gate to the front of the gate structure"
3283  },
3284  {"add_CR", (PyCFunction) qgd_Circuit_Wrapper_add_CR, METH_VARARGS | METH_KEYWORDS,
3285  "Call to add a CR gate to the front of the gate structure"
3286  },
3287  {"add_adaptive", (PyCFunction) qgd_Circuit_Wrapper_add_adaptive, METH_VARARGS | METH_KEYWORDS,
3288  "Call to add an adaptive gate to the front of the gate structure"
3289  },
3290  {"add_Circuit", (PyCFunction) qgd_Circuit_Wrapper_add_Circuit, METH_VARARGS,
3291  "Call to add a block of operations to the front of the gate structure."
3292  },
3293  {"add_GENERAL", (PyCFunction) qgd_Circuit_Wrapper_add_GENERAL, METH_VARARGS | METH_KEYWORDS,
3294  "Call to add a GENERAL_OPERATION gate from an explicit matrix."
3295  },
3296 #ifdef __DFE__
3297  {"convert_to_DFE_gates_with_derivates", (PyCFunction) qgd_Circuit_Wrapper_convert_to_DFE_gates_with_derivates, METH_VARARGS,
3298  "Call to convert to DFE gates with derivates."
3299  },
3300  {"adjust_parameters_for_derivation", (PyCFunction) qgd_Circuit_Wrapper_adjust_parameters_for_derivation, METH_VARARGS,
3301  "Call to adjust parameters for derivation."
3302  },
3303  {"convert_to_DFE_gates", (PyCFunction) qgd_Circuit_Wrapper_convert_to_DFE_gates, METH_VARARGS,
3304  "Call to convert to DFE gates."
3305  },
3306  {"convert_to_DFE_gates", (PyCFunction) qgd_Circuit_Wrapper_convert_to_DFE_gates, METH_VARARGS,
3307  "Call to convert to DFE gates."
3308  },
3309 #endif
3310  {"get_Matrix", (PyCFunction) qgd_Circuit_Wrapper_get_Matrix, METH_VARARGS | METH_KEYWORDS,
3311  "Method to get the matrix of the operation."
3312  },
3313  {"get_Parameter_Num", (PyCFunction) qgd_Circuit_Wrapper_get_Parameter_Num, METH_NOARGS,
3314  "Call to get the number of free parameters in the circuit"
3315  },
3316  {"apply_to", (PyCFunction) qgd_Circuit_Wrapper_apply_to, METH_VARARGS | METH_KEYWORDS,
3317  "Call to apply the gate on the input matrix (or state). Keyword is_f32=True selects float32/complex64 path."
3318  },
3319  {"apply_from_right", (PyCFunction) qgd_Circuit_Wrapper_apply_from_right, METH_VARARGS | METH_KEYWORDS,
3320  "Call to apply the gate from the right on the input matrix. Keyword is_f32=True selects float32/complex64 path."
3321  },
3322  {"apply_to_list", (PyCFunction) qgd_Circuit_Wrapper_apply_to_list, METH_VARARGS | METH_KEYWORDS,
3323  "Call to apply the circuit on a list of input matrices (float64/complex128 only)."
3324  },
3325  {"apply_derivate_to", (PyCFunction) qgd_Circuit_Wrapper_apply_derivate_to, METH_VARARGS | METH_KEYWORDS,
3326  "Call to evaluate the derivative of the circuit on an input matrix. Returns a list of derivative matrices."
3327  },
3328  {"apply_to_combined", (PyCFunction) qgd_Circuit_Wrapper_apply_to_combined, METH_VARARGS | METH_KEYWORDS,
3329  "Call to evaluate forward action and derivatives of the circuit on an input matrix. Returns [forward, derivatives...]."
3330  },
3331  {"get_Second_Renyi_Entropy", (PyCFunction) qgd_Circuit_Wrapper_get_Second_Renyi_Entropy, METH_VARARGS,
3332  "Wrapper function to evaluate the second Rényi entropy of a quantum circuit at a specific parameter set."
3333  },
3334  {"get_Qbit_Num", (PyCFunction) qgd_Circuit_Wrapper_get_Qbit_Num, METH_NOARGS,
3335  "Call to get the number of qubits in the circuit"
3336  },
3337  {"set_Qbit_Num", (PyCFunction) qgd_Circuit_Wrapper_set_Qbit_Num, METH_VARARGS,
3338  "Call to set the number of qubits in the circuit"
3339  },
3340  {"get_Qbits", (PyCFunction) qgd_Circuit_Wrapper_get_Qbits, METH_NOARGS,
3341  "Call to get the list of qubits involved in the circuit"
3342  },
3343  {"set_min_fusion", (PyCFunction) qgd_Circuit_Wrapper_set_Min_Fusion, METH_VARARGS,
3344  "Call to set the min fusion in the circuit"
3345  },
3346  {"Remap_Qbits", (PyCFunction) qgd_Circuit_Wrapper_Remap_Qbits, METH_VARARGS,
3347  "Call to remap the qubits in the circuit."
3348  },
3349  {"get_Gate", (PyCFunction) qgd_Circuit_Wrapper_get_gate, METH_VARARGS,
3350  "Method to get the i-th decomposing gates."
3351  },
3352  {"get_Gates", (PyCFunction) qgd_Circuit_Wrapper_get_gates, METH_NOARGS,
3353  "Method to get the tuple of decomposing gates."
3354  },
3355  {"get_Gate_Nums", (PyCFunction) qgd_Circuit_Wrapper_get_Gate_Nums, METH_NOARGS,
3356  "Method to get statistics on the gate counts in the circuit."
3357  },
3358  {"get_Parameter_Start_Index", (PyCFunction) qgd_Circuit_Wrapper_get_Parameter_Start_Index, METH_NOARGS,
3359  "Call to get the starting index of the parameters in the parameter array corresponding to the circuit in which the current gate is incorporated."
3360  },
3361  {"Extract_Parameters", (PyCFunction) qgd_Circuit_Wrapper_Extract_Parameters, METH_VARARGS,
3362  "Call to extract the parameters corresponding to the gate from a parameter array associated with the circuit in which the gate is embedded."
3363  },
3364  {"export_to_Binary", (PyCFunction) qgd_Circuit_Wrapper_export_to_Binary, METH_VARARGS,
3365  "Export the circuit and its parameters into Squander binary format. Arguments: filename (str), parameters (numpy array)."
3366  },
3367  {"get_Flat_Circuit", (PyCFunction) qgd_Circuit_Wrapper_get_Flat_Circuit, METH_NOARGS,
3368  "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."
3369  },
3370  {"get_Parents", (PyCFunction) qgd_Circuit_Wrapper_get_parents, METH_VARARGS,
3371  "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."
3372  },
3373  {"get_Children", (PyCFunction) qgd_Circuit_Wrapper_get_children, METH_VARARGS,
3374  "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."
3375  },
3376  {"__getstate__", (PyCFunction) qgd_Circuit_Wrapper_getstate, METH_NOARGS,
3377  "Method to extract the stored quantum circuit in a human-readable data serialized and pickle-able format."
3378  },
3379  {"__setstate__", (PyCFunction) qgd_Circuit_Wrapper_setstate, METH_VARARGS,
3380  "Call to set the state of a quantum circuit from a human-readable data serialized and pickle-able format."
3381  },
3382 
3383 
3384  {NULL} /* Sentinel */
3385 };
3386 
3387 
3392  PyVarObject_HEAD_INIT(NULL, 0)
3393  "qgd_Circuit_Wrapper.qgd_Circuit_Wrapper", /*tp_name*/
3394  sizeof(qgd_Circuit_Wrapper), /*tp_basicsize*/
3395  0, /*tp_itemsize*/
3396  (destructor) qgd_Circuit_Wrapper_dealloc, /*tp_dealloc*/
3397  #if PY_VERSION_HEX < 0x030800b4
3398  0, /*tp_print*/
3399  #endif
3400  #if PY_VERSION_HEX >= 0x030800b4
3401  0, /*tp_vectorcall_offset*/
3402  #endif
3403  0, /*tp_getattr*/
3404  0, /*tp_setattr*/
3405  #if PY_MAJOR_VERSION < 3
3406  0, /*tp_compare*/
3407  #endif
3408  #if PY_MAJOR_VERSION >= 3
3409  0, /*tp_as_async*/
3410  #endif
3411  0, /*tp_repr*/
3412  0, /*tp_as_number*/
3413  0, /*tp_as_sequence*/
3414  0, /*tp_as_mapping*/
3415  0, /*tp_hash*/
3416  0, /*tp_call*/
3417  0, /*tp_str*/
3418  0, /*tp_getattro*/
3419  0, /*tp_setattro*/
3420  0, /*tp_as_buffer*/
3421  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
3422  "Object to represent a qgd_Circuit_Wrapper class of the QGD package.", /*tp_doc*/
3423  0, /*tp_traverse*/
3424  0, /*tp_clear*/
3425  0, /*tp_richcompare*/
3426  0, /*tp_weaklistoffset*/
3427  0, /*tp_iter*/
3428  0, /*tp_iternext*/
3429  qgd_Circuit_Wrapper_Methods, /*tp_methods*/
3430  qgd_Circuit_Wrapper_Members, /*tp_members*/
3431  0, /*tp_getset*/
3432  0, /*tp_base*/
3433  0, /*tp_dict*/
3434  0, /*tp_descr_get*/
3435  0, /*tp_descr_set*/
3436  0, /*tp_dictoffset*/
3437  (initproc) qgd_Circuit_Wrapper_init, /*tp_init*/
3438  0, /*tp_alloc*/
3439  qgd_Circuit_Wrapper_new, /*tp_new*/
3440  0, /*tp_free*/
3441  0, /*tp_is_gc*/
3442  0, /*tp_bases*/
3443  0, /*tp_mro*/
3444  0, /*tp_cache*/
3445  0, /*tp_subclasses*/
3446  0, /*tp_weaklist*/
3447  0, /*tp_del*/
3448  0, /*tp_version_tag*/
3449  #if PY_VERSION_HEX >= 0x030400a1
3450  0, /*tp_finalize*/
3451  #endif
3452  #if PY_VERSION_HEX >= 0x030800b1
3453  0, /*tp_vectorcall*/
3454  #endif
3455  #if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
3456  0, /*tp_print*/
3457  #endif
3458 };
3459 
3463 static PyModuleDef qgd_Circuit_Wrapper_Module = {
3464  PyModuleDef_HEAD_INIT,
3465  "qgd_Circuit_Wrapper",
3466  "Python binding for QGD Circuit class",
3467  -1,
3468 };
3469 
3473 PyMODINIT_FUNC
3475 {
3476 
3477  // initialize Numpy API
3478  import_array();
3479 
3480  PyObject *m;
3481  if (PyType_Ready(&qgd_Circuit_Wrapper_Type) < 0)
3482  return NULL;
3483 
3484  m = PyModule_Create(&qgd_Circuit_Wrapper_Module);
3485  if (m == NULL)
3486  return NULL;
3487 
3488  Py_INCREF(&qgd_Circuit_Wrapper_Type);
3489  if (PyModule_AddObject(m, "qgd_Circuit_Wrapper", (PyObject *) &qgd_Circuit_Wrapper_Type) < 0) {
3490  Py_DECREF(&qgd_Circuit_Wrapper_Type);
3491  Py_DECREF(m);
3492  return NULL;
3493  }
3494 
3495  return m;
3496 }
3497 
3498 
3499 
3500 }
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 PyObject * qgd_Circuit_Wrapper_export_to_Binary(qgd_Circuit_Wrapper *self, PyObject *args)
Export the circuit and its parameters into Squander binary format.
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)
void export_gate_list_to_binary(Matrix_real &parameters, Gates_block *gates_block, const std::string &filename, int verbosity)
Use to export a quantum circuit into binary format.
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.