Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
qgd_N_Qubit_Decompositions_Wrapper.cpp
Go to the documentation of this file.
1 /*
2 \file qgd_N_Qubit_Decompositions_Wrapper.cpp
3 \brief Python interface for N-Qubit Decomposition classes
4 */
5 #define PY_SSIZE_T_CLEAN
6 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
7 
8 #include <Python.h>
9 #include <numpy/arrayobject.h>
10 #include "structmember.h"
11 #include <stdio.h>
12 #include <complex>
13 #include <cmath>
14 #include <cstring>
15 #include <cctype>
16 
17 // Cross-platform case-insensitive string comparison
18 #ifdef _WIN32
19  #define strcasecmp _stricmp
20 #else
21  #include <strings.h>
22 #endif
23 
24 #include "numpy_interface.h"
25 #include "matrix_any.h"
26 #include "matrix_real_any.h"
27 #include "N_Qubit_Decomposition.h"
31 #include "Gates_block.h"
32 
36 typedef struct qgd_Circuit_Wrapper {
37  PyObject_HEAD
40 
45  PyObject_HEAD
47  PyArrayObject* Umtx;
51 
53 
57 Matrix extract_matrix(PyObject* Umtx_arg, PyArrayObject** store_ref) {
58  if (!Umtx_arg) {
59  throw std::runtime_error("Umtx is NULL");
60  }
61  *store_ref = (PyArrayObject*)PyArray_FROM_OTF(Umtx_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
62  if (!*store_ref) {
63  throw std::runtime_error("Failed to convert Umtx");
64  }
65  if (!PyArray_IS_C_CONTIGUOUS(*store_ref)) {
66  std::cout << "Warning: Umtx is not memory contiguous" << std::endl;
67  }
68  return numpy2matrix(*store_ref);
69 }
70 
74 void extract_matrix_any(PyObject* matrix_arg, PyArrayObject** store_ref, Matrix& matrix64, Matrix_float& matrix32, bool& is_float32) {
75  if (!matrix_arg) {
76  throw std::runtime_error("matrix argument is NULL");
77  }
78 
79  int requested_type = NPY_COMPLEX128;
80  if (PyArray_Check(matrix_arg) && PyArray_TYPE(reinterpret_cast<PyArrayObject*>(matrix_arg)) == NPY_COMPLEX64) {
81  requested_type = NPY_COMPLEX64;
82  }
83 
84  *store_ref = (PyArrayObject*)PyArray_FROM_OTF(matrix_arg, requested_type, NPY_ARRAY_IN_ARRAY);
85  if (!*store_ref) {
86  throw std::runtime_error("Failed to convert matrix argument");
87  }
88  if (!PyArray_IS_C_CONTIGUOUS(*store_ref)) {
89  std::cout << "Warning: matrix argument is not memory contiguous" << std::endl;
90  }
91 
92  is_float32 = PyArray_TYPE(*store_ref) == NPY_COMPLEX64;
93  if (is_float32) {
94  matrix32 = numpy2matrix_float(*store_ref);
95  }
96  else {
97  matrix64 = numpy2matrix(*store_ref);
98  }
99 }
100 
104 void extract_parameters_any(PyObject* parameters_arg, PyArrayObject** store_ref, Matrix_real& parameters64, Matrix_real_float& parameters32, bool& is_float32) {
105  if (!parameters_arg) {
106  throw std::runtime_error("parameters argument is NULL");
107  }
108 
109  int requested_type = NPY_FLOAT64;
110  if (PyArray_Check(parameters_arg) && PyArray_TYPE(reinterpret_cast<PyArrayObject*>(parameters_arg)) == NPY_FLOAT32) {
111  requested_type = NPY_FLOAT32;
112  }
113 
114  *store_ref = (PyArrayObject*)PyArray_FROM_OTF(parameters_arg, requested_type, NPY_ARRAY_IN_ARRAY);
115  if (!*store_ref) {
116  throw std::runtime_error("Failed to convert parameters argument");
117  }
118 
119  is_float32 = PyArray_TYPE(*store_ref) == NPY_FLOAT32;
120  if (is_float32) {
121  parameters32 = numpy2matrix_real_float(*store_ref);
122  }
123  else {
124  parameters64 = numpy2matrix_real(*store_ref);
125  }
126 }
127 
129  Matrix_real parameters64(parameters32.rows, parameters32.cols, parameters32.stride);
130  for (int row=0; row<parameters32.rows; row++) {
131  for (int col=0; col<parameters32.cols; col++) {
132  int idx = row*parameters32.stride + col;
133  parameters64[idx] = static_cast<double>(parameters32[idx]);
134  }
135  }
136  return parameters64;
137 }
138 
142 guess_type extract_guess_type(PyObject* initial_guess) {
143  if (!initial_guess || initial_guess == Py_None) {
144  return RANDOM;
145  }
146 
147  PyObject* guess_str_obj = PyObject_Str(initial_guess);
148  if (!guess_str_obj) {
149  throw std::runtime_error("Failed to convert initial guess to string");
150  }
151  const char* guess_str = PyUnicode_AsUTF8(guess_str_obj);
152  if (!guess_str) {
153  throw std::runtime_error("Failed to convert initial guess to string");
154  }
155 
156  if (strcasecmp("zeros", guess_str) == 0) return ZEROS;
157  if (strcasecmp("random", guess_str) == 0) return RANDOM;
158  if (strcasecmp("close_to_zero", guess_str) == 0) return CLOSE_TO_ZERO;
159  std::cout << "Warning: Unknown guess '" << guess_str << "', using RANDOM" << std::endl;
160 
161  Py_XDECREF(guess_str_obj);
162  return RANDOM;
163 }
164 
168 std::vector<matrix_base<int>> extract_topology(PyObject* topology) {
169  std::vector<matrix_base<int>> result;
170  if (!topology || topology == Py_None) {
171  return result;
172  }
173  if (!PyList_Check(topology)) {
174  throw std::runtime_error("Topology must be a list");
175  }
176  Py_ssize_t n = PyList_Size(topology);
177  for (Py_ssize_t i = 0; i < n; i++) {
178  PyObject* item = PyList_GetItem(topology, i);
179  if (!PyTuple_Check(item)) {
180  throw std::runtime_error("Topology elements must be tuples");
181  }
182  matrix_base<int> pair(1, 2);
183  pair[0] = PyLong_AsLong(PyTuple_GetItem(item, 0));
184  pair[1] = PyLong_AsLong(PyTuple_GetItem(item, 1));
185  result.push_back(pair);
186  }
187  return result;
188 }
189 
193 std::map<std::string, Config_Element> extract_config(PyObject* config_arg) {
194  std::map<std::string, Config_Element> config;
195  if (!config_arg || config_arg == Py_None) {
196  return config;
197  }
198  if (!PyDict_Check(config_arg)) {
199  throw std::runtime_error("Config must be a dictionary");
200  }
201  PyObject *key, *value;
202  Py_ssize_t pos = 0;
203  while (PyDict_Next(config_arg, &pos, &key, &value)) {
204  std::string key_str = PyUnicode_AsUTF8(key);
205  Config_Element element;
206  if (PyBool_Check(value)) {
207  element.set_property(key_str, value == Py_True);
208  } else if (PyLong_Check(value)) {
209  element.set_property(key_str, PyLong_AsLongLong(value));
210  } else if (PyFloat_Check(value)) {
211  element.set_property(key_str, PyFloat_AsDouble(value));
212  }
213  config[key_str] = element;
214  }
215  return config;
216 }
217 
218 static bool config_requests_float(std::map<std::string, Config_Element>& config) {
219  bool use_float = false;
220  if (config.count("use_float") > 0) {
221  config["use_float"].get_property(use_float);
222  }
223  return use_float;
224 }
225 
227 
228 static int
230 {
231  static char* kwlist[] = {
232  (char*)"Umtx", (char*)"qbit_num", (char*)"optimize_layer_num",
233  (char*)"initial_guess", (char*)"config", NULL
234  };
235 
236  PyObject *Umtx_arg = NULL, *initial_guess = NULL, *config_arg = NULL;
237  int qbit_num = -1;
238  bool optimize_layer_num = false;
239 
240  if (!PyArg_ParseTupleAndKeywords(
241  args, kwds, "O|ibOO", kwlist,
242  &Umtx_arg, &qbit_num, &optimize_layer_num, &initial_guess, &config_arg)
243  ) {
244  return -1;
245  }
246 
247  try {
248  Matrix Umtx_mtx;
249  Matrix_float Umtx_mtx_float;
250  bool Umtx_is_float32 = false;
251  extract_matrix_any(Umtx_arg, &self->Umtx, Umtx_mtx, Umtx_mtx_float, Umtx_is_float32);
252  // calculate qbit_num from matrix size if not provided
253  if (qbit_num == -1) {
254  qbit_num = (int)std::round(std::log2(Umtx_is_float32 ? Umtx_mtx_float.rows : Umtx_mtx.rows));
255  }
256 
257  guess_type guess = extract_guess_type(initial_guess);
258  auto config = extract_config(config_arg);
259  const bool use_float_constructor = Umtx_is_float32 || config_requests_float(config);
260  if (use_float_constructor && !Umtx_is_float32) {
261  Umtx_mtx_float = Umtx_mtx.to_float32();
262  }
263 
264  if (use_float_constructor) {
265  self->decomp = new N_Qubit_Decomposition(Umtx_mtx_float, qbit_num, optimize_layer_num, config, guess);
266  }
267  else {
268  self->decomp = new N_Qubit_Decomposition(Umtx_mtx, qbit_num, optimize_layer_num, config, guess);
269  }
270 
271  return 0;
272  } catch (const std::exception& e) {
273  PyErr_SetString(PyExc_Exception, e.what());
274  return -1;
275  }
276 }
277 
278 static int
280 {
281  static char* kwlist[] = {
282  (char*)"Umtx", (char*)"qbit_num", (char*)"level_limit_max",
283  (char*)"level_limit_min", (char*)"topology", (char*)"config",
284  (char*)"accelerator_num", NULL
285  };
286  PyObject *Umtx_arg = NULL, *topology = NULL, *config_arg = NULL;
287  int qbit_num = -1, level_limit = 8, level_limit_min = 0, accelerator_num = 0;
288 
289  if (!PyArg_ParseTupleAndKeywords(
290  args, kwds, "O|iiiOOi", kwlist,
291  &Umtx_arg, &qbit_num, &level_limit, &level_limit_min, &topology, &config_arg, &accelerator_num)
292  ) {
293  return -1;
294  }
295 
296  try {
297  Matrix Umtx_mtx;
298  Matrix_float Umtx_mtx_float;
299  bool Umtx_is_float32 = false;
300  extract_matrix_any(Umtx_arg, &self->Umtx, Umtx_mtx, Umtx_mtx_float, Umtx_is_float32);
301  const int Umtx_rows = Umtx_is_float32 ? Umtx_mtx_float.rows : Umtx_mtx.rows;
302  const int Umtx_cols = Umtx_is_float32 ? Umtx_mtx_float.cols : Umtx_mtx.cols;
303 
304  // For state vector input: State Preparation passes (State, level_limit_max, level_limit_min, ...)
305  // without qbit_num, so we calculate qbit_num from state size and interpret the qbit_num
306  // position as level_limit_max. Example: (State_16x1, 5, 0) -> qbit_num=4, level_limit_max=5
307  if (Umtx_cols == 1 && qbit_num > 0) {
308  int level_limit_max_in = qbit_num;
309  qbit_num = (int)std::round(std::log2(Umtx_rows));
310  level_limit = level_limit_max_in;
311  }
312  else {
313  // For Unitary decomposition, calculate qbit_num from matrix size if not provided
314  if (qbit_num == -1) {
315  qbit_num = (int)std::round(std::log2(Umtx_rows));
316  }
317  }
318 
319  auto topology_cpp = extract_topology(topology);
320  auto config = extract_config(config_arg);
321  const bool use_float_constructor = Umtx_is_float32 || config_requests_float(config);
322  if (use_float_constructor && !Umtx_is_float32) {
323  Umtx_mtx_float = Umtx_mtx.to_float32();
324  }
325 
326  if (use_float_constructor) {
327  self->decomp = new N_Qubit_Decomposition_adaptive(
328  Umtx_mtx_float, qbit_num, level_limit, level_limit_min,
329  topology_cpp, config, accelerator_num
330  );
331  }
332  else {
333  self->decomp = new N_Qubit_Decomposition_adaptive(
334  Umtx_mtx, qbit_num, level_limit, level_limit_min,
335  topology_cpp, config, accelerator_num
336  );
337  }
338 
339  return 0;
340  } catch (const std::exception& e) {
341  PyErr_SetString(PyExc_Exception, e.what());
342  return -1;
343  }
344 }
345 
346 static int
348 {
349  static char* kwlist[] = {
350  (char*)"Umtx", (char*)"qbit_num", (char*)"initial_guess",
351  (char*)"config", (char*)"accelerator_num", NULL
352  };
353 
354  PyObject *Umtx_arg = NULL, *initial_guess = NULL, *config_arg = NULL;
355  int qbit_num = -1, accelerator_num = 0;
356 
357  if (!PyArg_ParseTupleAndKeywords(
358  args, kwds, "O|iOOi", kwlist,
359  &Umtx_arg, &qbit_num, &initial_guess, &config_arg, &accelerator_num)
360  ) {
361  return -1;
362  }
363 
364  try {
365  Matrix Umtx_mtx;
366  Matrix_float Umtx_mtx_float;
367  bool Umtx_is_float32 = false;
368  extract_matrix_any(Umtx_arg, &self->Umtx, Umtx_mtx, Umtx_mtx_float, Umtx_is_float32);
369  // calculate qbit_num from matrix size if not provided
370  if (qbit_num == -1) {
371  qbit_num = (int)std::round(std::log2(Umtx_is_float32 ? Umtx_mtx_float.rows : Umtx_mtx.rows));
372  }
373 
374  guess_type guess = extract_guess_type(initial_guess);
375  auto config = extract_config(config_arg);
376  const bool use_float_constructor = Umtx_is_float32 || config_requests_float(config);
377  if (use_float_constructor && !Umtx_is_float32) {
378  Umtx_mtx_float = Umtx_mtx.to_float32();
379  }
380 
381  if (use_float_constructor) {
382  self->decomp = new N_Qubit_Decomposition_custom(Umtx_mtx_float, qbit_num, false, config, guess, accelerator_num);
383  }
384  else {
385  self->decomp = new N_Qubit_Decomposition_custom(Umtx_mtx, qbit_num, false, config, guess, accelerator_num);
386  }
387 
388  return 0;
389  } catch (const std::exception& e) {
390  PyErr_SetString(PyExc_Exception, e.what());
391  return -1;
392  }
393 }
394 
395 template<typename DecompT>
396 static int search_wrapper_init(qgd_N_Qubit_Decomposition_Wrapper* self, PyObject* args, PyObject* kwds)
397 {
398  static char* kwlist[] = {
399  (char*)"Umtx", (char*)"qbit_num", (char*)"topology",
400  (char*)"config", (char*)"accelerator_num", NULL
401  };
402 
403  PyObject *Umtx_arg = NULL, *topology = NULL, *config_arg = NULL;
404  int qbit_num = -1, accelerator_num = 0;
405 
406  if (!PyArg_ParseTupleAndKeywords(
407  args, kwds, "O|iOOi", kwlist,
408  &Umtx_arg, &qbit_num, &topology, &config_arg, &accelerator_num)
409  ) {
410  return -1;
411  }
412 
413  try {
414  Matrix Umtx_mtx;
415  Matrix_float Umtx_mtx_float;
416  bool Umtx_is_float32 = false;
417  extract_matrix_any(Umtx_arg, &self->Umtx, Umtx_mtx, Umtx_mtx_float, Umtx_is_float32);
418  // calculate qbit_num from matrix size if not provided
419  if (qbit_num == -1) {
420  qbit_num = (int)std::round(std::log2(Umtx_is_float32 ? Umtx_mtx_float.rows : Umtx_mtx.rows));
421  }
422 
423  auto topology_cpp = extract_topology(topology);
424  auto config = extract_config(config_arg);
425  const bool use_float_constructor = Umtx_is_float32 || config_requests_float(config);
426  if (use_float_constructor && !Umtx_is_float32) {
427  Umtx_mtx_float = Umtx_mtx.to_float32();
428  }
429 
430  if (use_float_constructor) {
431  self->decomp = new DecompT(Umtx_mtx_float, qbit_num, topology_cpp, config, accelerator_num);
432  }
433  else {
434  self->decomp = new DecompT(Umtx_mtx, qbit_num, topology_cpp, config, accelerator_num);
435  }
436 
437  return 0;
438  } catch (const std::exception& e) {
439  PyErr_SetString(PyExc_Exception, e.what());
440  return -1;
441  }
442 }
443 
444 static int
446  return search_wrapper_init<N_Qubit_Decomposition_Tree_Search>(self, args, kwds);
447 }
448 
449 static int
451  return search_wrapper_init<N_Qubit_Decomposition_Tabu_Search>(self, args, kwds);
452 }
453 
457 template<typename DecompT>
458 void release_decomposition(DecompT* instance) {
459  if (instance != NULL) {
460  delete instance;
461  }
462 }
463 
467 static void
469 {
470  if (self->decomp != NULL) {
471  // deallocate the instance of class N_Qubit_Decomposition
472  release_decomposition(self->decomp);
473  self->decomp = NULL;
474  }
475  if (self->Umtx != NULL) {
476  // release the unitary to be decomposed
477  Py_DECREF(self->Umtx);
478  self->Umtx = NULL;
479  }
480  Py_TYPE(self)->tp_free((PyObject *) self);
481 }
482 
483 
487 static PyObject *
489 {
491  self = (qgd_N_Qubit_Decomposition_Wrapper *) type->tp_alloc(type, 0);
492  if (self != NULL) {
493  self->Umtx = NULL;
494  self->decomp = NULL;
495  }
496  return (PyObject *) self;
497 }
498 
500 
506 static PyObject *
508 {
509  // The tuple of expected keywords
510  static char *kwlist[] = {NULL};
511 
512  // parsing input arguments
513  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|", kwlist))
514  return Py_BuildValue("i", -1);
515 
516  // Try each decomposition type and call start_decomposition
517  if (N_Qubit_Decomposition_adaptive* p = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp)) {
518  p->start_decomposition();
519  return Py_BuildValue("i", 0);
520  }
521  if (N_Qubit_Decomposition_custom* p = dynamic_cast<N_Qubit_Decomposition_custom*>(self->decomp)) {
522  p->start_decomposition();
523  return Py_BuildValue("i", 0);
524  }
525  if (N_Qubit_Decomposition_Tree_Search* p = dynamic_cast<N_Qubit_Decomposition_Tree_Search*>(self->decomp)) {
526  p->start_decomposition();
527  return Py_BuildValue("i", 0);
528  }
529  if (N_Qubit_Decomposition_Tabu_Search* p = dynamic_cast<N_Qubit_Decomposition_Tabu_Search*>(self->decomp)) {
530  p->start_decomposition();
531  return Py_BuildValue("i", 0);
532  }
533  if (N_Qubit_Decomposition* p = dynamic_cast<N_Qubit_Decomposition*>(self->decomp)) {
534  p->start_decomposition();
535  return Py_BuildValue("i", 0);
536  }
537 
538  PyErr_SetString(PyExc_TypeError, "Unknown decomposition type");
539  return NULL;
540 }
541 
546 static PyObject *
548 {
549  // get the number of gates
550  int ret = self->decomp->get_gate_num();
551  return Py_BuildValue("i", ret);
552 }
553 
558 static PyObject *
560 {
561  if (self->decomp->get_use_float()) {
562  Matrix_real_float parameters_mtx = self->decomp->get_optimized_parameters_float();
563  parameters_mtx.set_owner(false);
564  return matrix_real_float_to_numpy( parameters_mtx );
565  }
566 
567  Matrix_real parameters_mtx = self->decomp->get_optimized_parameters();
568 
569  // convert to numpy array
570  parameters_mtx.set_owner(false);
571  PyObject* parameter_arr = matrix_real_to_numpy( parameters_mtx );
572 
573  return parameter_arr;
574 }
575 
580 static PyObject *
582 {
583  PyObject* qgd_Circuit = PyImport_ImportModule("squander.gates.qgd_Circuit");
584  if ( qgd_Circuit == NULL ) {
585  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.qgd_Circuit" );
586  return NULL;
587  }
588 
589  // retrieve the C++ variant of the flat circuit (flat circuit does not conatain any sub-circuits)
590  Gates_block* circuit = self->decomp->get_flat_circuit();
591 
592  // construct python interfarce for the circuit
593  PyObject* qgd_circuit_Dict = PyModule_GetDict( qgd_Circuit );
594 
595  // 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
596  PyObject* py_circuit_class = PyDict_GetItemString( qgd_circuit_Dict, "qgd_Circuit");
597 
598  // create gate parameters
599  PyObject* qbit_num = Py_BuildValue("i", circuit->get_qbit_num() );
600  PyObject* circuit_input = Py_BuildValue("(O)", qbit_num);
601 
602  PyObject* py_circuit = PyObject_CallObject(py_circuit_class, circuit_input);
603  qgd_Circuit_Wrapper* py_circuit_C = reinterpret_cast<qgd_Circuit_Wrapper*>( py_circuit );
604 
605  // replace the empty circuit with the extracted one
606  delete( py_circuit_C->gate );
607  py_circuit_C->gate = circuit;
608 
609  return py_circuit;
610 }
611 
615 static PyObject *
617 {
618  // list gates with start_index = 0
619  self->decomp->list_gates(0);
620  return Py_BuildValue("");
621 }
622 
627 static PyObject *
629 {
630  // initiate variables for input arguments
631  PyObject* max_layer_num;
632  // parsing input arguments
633  if (!PyArg_ParseTuple(args, "O", &max_layer_num)) {
634  return NULL;
635  }
636  // Check whether input is dictionary
637  if (!PyDict_Check(max_layer_num)) {
638  PyErr_SetString(PyExc_TypeError, "Input must be dictionary");
639  return NULL;
640  }
641 
642  PyObject *key = NULL, *value = NULL;
643  Py_ssize_t pos = 0;
644 
645  try {
646  while (PyDict_Next(max_layer_num, &pos, &key, &value)) {
647  // convert value from PyObject to int
648  if (!PyLong_Check(value)) {
649  PyErr_SetString(PyExc_TypeError, "Dictionary values must be integers");
650  return NULL;
651  }
652  int value_int = (int)PyLong_AsLong(value);
653 
654  // convert key from PyObject to int
655  if (!PyLong_Check(key)) {
656  PyErr_SetString(PyExc_TypeError, "Dictionary keys must be integers");
657  return NULL;
658  }
659  int key_int = (int)PyLong_AsLong(key);
660 
661  // set maximal layer nums on the C++ side (base class method)
662  self->decomp->set_max_layer_num(key_int, value_int);
663  }
664  Py_RETURN_NONE;
665  } catch (std::exception& e) {
666  PyErr_SetString(PyExc_Exception, e.what());
667  return NULL;
668  }
669 }
670 
675 static PyObject *
677 {
678  // initiate variables for input arguments
679  PyObject* iteration_loops;
680  // parsing input arguments
681  if (!PyArg_ParseTuple(args, "O", &iteration_loops)) {
682  return NULL;
683  }
684  // Check whether input is dictionary
685  if (!PyDict_Check(iteration_loops)) {
686  PyErr_SetString(PyExc_TypeError, "Input must be dictionary");
687  return NULL;
688  }
689 
690  PyObject *key = NULL, *value = NULL;
691  Py_ssize_t pos = 0;
692 
693  try {
694  while (PyDict_Next(iteration_loops, &pos, &key, &value)) {
695  // convert value from PyObject to int
696  if (!PyLong_Check(value)) {
697  PyErr_SetString(PyExc_TypeError, "Dictionary values must be integers");
698  return NULL;
699  }
700  int value_int = (int)PyLong_AsLong(value);
701 
702  // convert key from PyObject to int
703  if (!PyLong_Check(key)) {
704  PyErr_SetString(PyExc_TypeError, "Dictionary keys must be integers");
705  return NULL;
706  }
707  int key_int = (int)PyLong_AsLong(key);
708 
709  self->decomp->set_iteration_loops(key_int, value_int);
710  }
711  Py_RETURN_NONE;
712  } catch (std::exception& e) {
713  PyErr_SetString(PyExc_Exception, e.what());
714  return NULL;
715  }
716 }
717 
722 static PyObject *
724 {
725  int verbose;
726  if (!PyArg_ParseTuple(args, "i", &verbose)) {
727  return NULL;
728  }
729  try {
730  self->decomp->set_verbose(verbose);
731  Py_RETURN_NONE;
732  } catch (std::exception& e) {
733  PyErr_SetString(PyExc_Exception, e.what());
734  return NULL;
735  }
736 }
737 
742 static PyObject *
744 {
745  PyObject* debugfile = NULL;
746  if (!PyArg_ParseTuple(args, "O", &debugfile)) {
747  return NULL;
748  }
749  // determine the debugfile name type
750  PyObject* debugfile_string = PyObject_Str(debugfile);
751  PyObject* debugfile_string_unicode = PyUnicode_AsEncodedString(debugfile_string, "utf-8", "~E~");
752  const char* debugfile_C = PyBytes_AS_STRING(debugfile_string_unicode);
753  Py_XDECREF(debugfile_string);
754  Py_XDECREF(debugfile_string_unicode);
755  // determine the length of the filename and initialize C++ variant of the string
756  Py_ssize_t string_length = PyBytes_Size(debugfile_string_unicode);
757  std::string debugfile_Cpp(debugfile_C, string_length);
758  try {
759  // set the name of the debugfile on the C++ side
760  self->decomp->set_debugfile(debugfile_Cpp);
761  Py_RETURN_NONE;
762  } catch (std::exception& e) {
763  PyErr_SetString(PyExc_Exception, e.what());
764  return NULL;
765  }
766 }
767 
772 static PyObject *
774 {
775  PyObject* qbit_list;
776  if (!PyArg_ParseTuple(args, "O", &qbit_list)) {
777  return NULL;
778  }
779  bool is_list = PyList_Check(qbit_list), is_tuple = PyTuple_Check(qbit_list);
780  if (!is_list && !is_tuple) {
781  PyErr_SetString(PyExc_TypeError, "Input must be tuple or list");
782  return NULL;
783  }
784  Py_ssize_t element_num;
785  if (is_tuple) {
786  element_num = PyTuple_GET_SIZE(qbit_list);
787  } else {
788  element_num = PyList_GET_SIZE(qbit_list);
789  }
790  // create C++ variant of the tuple/list
791  std::vector<int> qbit_list_C((int)element_num);
792  for (Py_ssize_t idx = 0; idx < element_num; idx++) {
793  if (is_tuple) {
794  qbit_list_C[(int) idx] = (int) PyLong_AsLong( PyTuple_GetItem(qbit_list, idx) );
795  }
796  else {
797  qbit_list_C[(int) idx] = (int) PyLong_AsLong( PyList_GetItem(qbit_list, idx) );
798  }
799  }
800  try {
801  // reorder the qubits in the decomposition class
802  self->decomp->reorder_qubits(qbit_list_C);
803  Py_RETURN_NONE;
804  } catch (std::exception& e) {
805  PyErr_SetString(PyExc_Exception, e.what());
806  return NULL;
807  }
808 }
809 
814 static PyObject *
816 {
817  double tolerance;
818  if (!PyArg_ParseTuple(args, "d", &tolerance)) {
819  return NULL;
820  }
821  try {
822  self->decomp->set_optimization_tolerance(tolerance);
823  Py_RETURN_NONE;
824  } catch (std::exception& e) {
825  PyErr_SetString(PyExc_Exception, e.what());
826  return NULL;
827  }
828 }
829 
834 static PyObject *
836 {
837  double threshold;
838  if (!PyArg_ParseTuple(args, "d", &threshold)) {
839  return NULL;
840  }
841  try {
842  self->decomp->set_convergence_threshold(threshold);
843  Py_RETURN_NONE;
844  } catch (std::exception& e) {
845  PyErr_SetString(PyExc_Exception, e.what());
846  return NULL;
847  }
848 }
849 
854 static PyObject *
856 {
857  int optimization_blocks;
858  if (!PyArg_ParseTuple(args, "i", &optimization_blocks)) {
859  return NULL;
860  }
861  try {
862  self->decomp->set_optimization_blocks(optimization_blocks);
863  Py_RETURN_NONE;
864  } catch (std::exception& e) {
865  PyErr_SetString(PyExc_Exception, e.what());
866  return NULL;
867  }
868 }
869 
873 static PyObject *
875 {
876  try {
877  self->decomp->add_finalyzing_layer();
878  }
879  catch (std::string err) {
880  PyErr_SetString(PyExc_Exception, err.c_str());
881  return NULL;
882  }
883  catch(...) {
884  std::string err("Invalid pointer to decomposition class");
885  PyErr_SetString(PyExc_Exception, err.c_str());
886  return NULL;
887  }
888  return Py_BuildValue("i", 0);
889 }
890 
898 static PyObject *
900 {
901  // initiate variables for input arguments
902  PyObject* gate_structure_py;
903 
904  // parsing input arguments
905  if (!PyArg_ParseTuple(args, "|O", &gate_structure_py)) {
906  return Py_BuildValue("i", -1);
907  }
908 
909  // Check if input is a dictionary (map<int, Gates_block*> version: N_Qubit_Decomposition ONLY)
910  if (PyDict_Check(gate_structure_py)) {
911  PyObject *key = NULL, *value = NULL;
912  Py_ssize_t pos = 0;
913  std::map<int, Gates_block*> gate_structure;
914 
915  while (PyDict_Next(gate_structure_py, &pos, &key, &value)) {
916  // convert key from PyObject to int
917  if (!PyLong_Check(key)) {
918  PyErr_SetString(PyExc_TypeError, "Dictionary keys must be integers");
919  return NULL;
920  }
921  int key_int = (int)PyLong_AsLong(key);
922  // convert value from PyObject to qgd_Circuit_Wrapper
923  qgd_Circuit_Wrapper* qgd_op_block = (qgd_Circuit_Wrapper*)value;
924  gate_structure.insert(std::pair<int, Gates_block*>(key_int, qgd_op_block->gate));
925  }
926 
927  // The map version is only available in base N_Qubit_Decomposition class
928  N_Qubit_Decomposition* base_decomp = dynamic_cast<N_Qubit_Decomposition*>(self->decomp);
929  if (base_decomp != NULL) {
930  try {
931  base_decomp->set_custom_gate_structure(gate_structure);
932  return Py_BuildValue("i", 0);
933  } catch (std::string err) {
934  PyErr_SetString(PyExc_Exception, err.c_str());
935  return NULL;
936  } catch (std::exception& e) {
937  PyErr_SetString(PyExc_Exception, e.what());
938  return NULL;
939  } catch (...) {
940  std::string err("Invalid pointer to decomposition class");
941  PyErr_SetString(PyExc_Exception, err.c_str());
942  return NULL;
943  }
944  }
945  PyErr_SetString(PyExc_AttributeError, "Dictionary-based set_Gate_Structure is only available for N_Qubit_Decomposition");
946  return NULL;
947  }
948 
949  qgd_Circuit_Wrapper* qgd_op_block = (qgd_Circuit_Wrapper*)gate_structure_py;
950  try {
951  self->decomp->set_custom_gate_structure(qgd_op_block->gate);
952  return Py_BuildValue("i", 0);
953  } catch (std::string err) {
954  PyErr_SetString(PyExc_Exception, err.c_str());
955  return NULL;
956  } catch (std::exception& e) {
957  PyErr_SetString(PyExc_Exception, e.what());
958  return NULL;
959  } catch (...) {
960  std::string err("Invalid pointer to decomposition class");
961  PyErr_SetString(PyExc_Exception, err.c_str());
962  return NULL;
963  }
964 }
965 
970 static PyObject *
972 {
973  int parameter_num = self->decomp->get_parameter_num();
974  return Py_BuildValue("i", parameter_num);
975 }
976 
982 static PyObject *
984 {
985  PyObject* parameters_obj = NULL;
986  PyArrayObject* parameters_arr = NULL;
987  // parsing input arguments
988  if (!PyArg_ParseTuple(args, "|O", &parameters_obj )) {
989  return Py_BuildValue("i", -1);
990  }
991 
992  Matrix_real parameters_mtx;
993  Matrix_real_float parameters_mtx_float;
994  bool parameters_is_float32 = false;
995  try {
996  extract_parameters_any(parameters_obj, &parameters_arr, parameters_mtx, parameters_mtx_float, parameters_is_float32);
997  if (parameters_is_float32) {
998  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
999  }
1000  self->decomp->set_optimized_parameters(parameters_mtx.get_data(), parameters_mtx.size());
1001  }
1002  catch (std::string err ) {
1003  PyErr_SetString(PyExc_Exception, err.c_str());
1004  return NULL;
1005  }
1006  catch(...) {
1007  std::string err( "Invalid pointer to decomposition class");
1008  PyErr_SetString(PyExc_Exception, err.c_str());
1009  return NULL;
1010  }
1011  Py_DECREF(parameters_arr);
1012  return Py_BuildValue("i", 0);
1013 }
1014 
1020 static PyObject *
1022 {
1023  int number_of_iters = self->decomp->get_num_iters();
1024  return Py_BuildValue("i", number_of_iters);
1025 }
1026 
1033 static PyObject *
1035 {
1036  // initiate variables for input arguments
1037  PyObject* filename = NULL;
1038  // parsing input arguments
1039  if (!PyArg_ParseTuple(args, "|O", &filename)) {
1040  return Py_BuildValue("i", -1);
1041  }
1042  PyObject* filename_string = PyObject_Str(filename);
1043  PyObject* filename_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
1044  const char* filename_C = PyBytes_AS_STRING(filename_unicode);
1045  std::string filename_str(filename_C);
1046  // export unitary to file
1047  self->decomp->export_unitary(filename_str);
1048  return Py_BuildValue("i", 0);
1049 }
1050 
1056 static PyObject *
1058 {
1059  try {
1060  std::string project_name = self->decomp->get_project_name();
1061  return PyUnicode_FromString(project_name.c_str());
1062  } catch (std::exception& e) {
1063  PyErr_SetString(PyExc_Exception, e.what());
1064  return NULL;
1065  }
1066 }
1067 
1074 static PyObject *
1076 {
1077  // initiate variables for input arguments
1078  PyObject* project_name_new = NULL;
1079  // parsing input arguments
1080  if (!PyArg_ParseTuple(args, "|O", &project_name_new)) {
1081  return Py_BuildValue("i", -1);
1082  }
1083  PyObject* project_name_new_string = PyObject_Str(project_name_new);
1084  PyObject* project_name_new_unicode = PyUnicode_AsEncodedString(project_name_new_string, "utf-8", "~E~");
1085  const char* project_name_new_C = PyBytes_AS_STRING(project_name_new_unicode);
1086  std::string project_name_new_str(project_name_new_C);
1087  // set the project name
1088  self->decomp->set_project_name(project_name_new_str);
1089  return Py_BuildValue("i", 0);
1090 }
1091 
1097 static PyObject *
1099 {
1100  QGD_Complex16 global_phase_factor_C = self->decomp->get_global_phase_factor();
1101  PyObject* global_phase = PyFloat_FromDouble(std::atan2(global_phase_factor_C.imag, global_phase_factor_C.real));
1102  return global_phase;
1103 }
1104 
1111 static PyObject *
1113 {
1114  double phase_angle;
1115  if (!PyArg_ParseTuple(args, "d", &phase_angle)) {
1116  return Py_BuildValue("i", -1);
1117  }
1118  try {
1119  self->decomp->set_global_phase(phase_angle);
1120  Py_RETURN_NONE;
1121  } catch (std::exception& e) {
1122  PyErr_SetString(PyExc_Exception, e.what());
1123  return NULL;
1124  }
1125 }
1126 
1132 static PyObject *
1134 {
1135  try {
1136  self->decomp->apply_global_phase_factor();
1137  Py_RETURN_NONE;
1138  } catch (std::exception& e) {
1139  PyErr_SetString(PyExc_Exception, e.what());
1140  return NULL;
1141  }
1142 }
1143 
1149 static PyObject *
1151 {
1152  if (self->decomp->get_use_float()) {
1153  Matrix_float Unitary_mtx;
1154  try {
1155  Unitary_mtx = self->decomp->get_Umtx_float().copy();
1156  }
1157  catch (std::string err) {
1158  PyErr_SetString(PyExc_Exception, err.c_str());
1159  return NULL;
1160  }
1161  catch (...) {
1162  std::string err("Invalid pointer to decomposition class");
1163  PyErr_SetString(PyExc_Exception, err.c_str());
1164  return NULL;
1165  }
1166  Unitary_mtx.set_owner(false);
1167  return matrix_float_to_numpy(Unitary_mtx);
1168  }
1169 
1170  Matrix Unitary_mtx;
1171  try {
1172  Unitary_mtx = self->decomp->get_Umtx().copy();
1173  }
1174  catch (std::string err) {
1175  PyErr_SetString(PyExc_Exception, err.c_str());
1176  return NULL;
1177  }
1178  catch (...) {
1179  std::string err("Invalid pointer to decomposition class");
1180  PyErr_SetString(PyExc_Exception, err.c_str());
1181  return NULL;
1182  }
1183  // convert to numpy array
1184  Unitary_mtx.set_owner(false);
1185  PyObject *Unitary_py = matrix_to_numpy(Unitary_mtx);
1186  return Unitary_py;
1187 }
1188 
1196 static PyObject *
1198 {
1199  // The tuple of expected keywords
1200  static char *kwlist[] = {(char*)"optimizer", NULL};
1201 
1202  PyObject* optimizer_arg = NULL;
1203 
1204  // parsing input arguments
1205  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &optimizer_arg)) {
1206  std::string err("Unsuccessful argument parsing");
1207  PyErr_SetString(PyExc_Exception, err.c_str());
1208  return NULL;
1209  }
1210 
1211  if (optimizer_arg == NULL) {
1212  std::string err("optimizer argument not set");
1213  PyErr_SetString(PyExc_Exception, err.c_str());
1214  return NULL;
1215  }
1216 
1217  PyObject* optimizer_string = PyObject_Str(optimizer_arg);
1218  PyObject* optimizer_string_unicode = PyUnicode_AsEncodedString(optimizer_string, "utf-8", "~E~");
1219  const char* optimizer_C = PyBytes_AS_STRING(optimizer_string_unicode);
1220 
1221  optimization_aglorithms qgd_optimizer;
1222  if (strcmp("bfgs", optimizer_C) == 0 || strcmp("BFGS", optimizer_C) == 0) {
1223  qgd_optimizer = BFGS;
1224  }
1225  else if (strcmp("adam", optimizer_C) == 0 || strcmp("ADAM", optimizer_C) == 0) {
1226  qgd_optimizer = ADAM;
1227  }
1228  else if (strcmp("grad_descend", optimizer_C) == 0 || strcmp("GRAD_DESCEND", optimizer_C) == 0) {
1229  qgd_optimizer = GRAD_DESCEND;
1230  }
1231  else if (strcmp("adam_batched", optimizer_C) == 0 || strcmp("ADAM_BATCHED", optimizer_C) == 0) {
1232  qgd_optimizer = ADAM_BATCHED;
1233  }
1234  else if (strcmp("bfgs2", optimizer_C) == 0 || strcmp("BFGS2", optimizer_C) == 0) {
1235  qgd_optimizer = BFGS2;
1236  }
1237  else if (strcmp("agents", optimizer_C) == 0 || strcmp("AGENTS", optimizer_C) == 0) {
1238  qgd_optimizer = AGENTS;
1239  }
1240  else if (strcmp("cosine", optimizer_C) == 0 || strcmp("COSINE", optimizer_C) == 0) {
1241  qgd_optimizer = COSINE;
1242  }
1243  else if (strcmp("grad_descend_phase_shift_rule", optimizer_C) == 0 || strcmp("GRAD_DESCEND_PARAMETER_SHIFT_RULE", optimizer_C) == 0) {
1244  qgd_optimizer = GRAD_DESCEND_PARAMETER_SHIFT_RULE;
1245  }
1246  else if (strcmp("agents_combined", optimizer_C) == 0 || strcmp("AGENTS_COMBINED", optimizer_C) == 0) {
1247  qgd_optimizer = AGENTS_COMBINED;
1248  }
1249  else if (strcmp("bayes_opt", optimizer_C) == 0 || strcmp("BAYES_OPT", optimizer_C) == 0) {
1250  qgd_optimizer = BAYES_OPT;
1251  }
1252  else {
1253  std::cout << "Wrong optimizer: " << optimizer_C << ". Using default: BFGS" << std::endl;
1254  qgd_optimizer = BFGS;
1255  }
1256 
1257  try {
1258  self->decomp->set_optimizer(qgd_optimizer);
1259  }
1260  catch (std::string err) {
1261  PyErr_SetString(PyExc_Exception, err.c_str());
1262  std::cout << err << std::endl;
1263  return NULL;
1264  }
1265  catch(...) {
1266  std::string err("Invalid pointer to decomposition class");
1267  PyErr_SetString(PyExc_Exception, err.c_str());
1268  return NULL;
1269  }
1270  return Py_BuildValue("i", 0);
1271 }
1272 
1279 static PyObject *
1281 {
1282  int max_iterations;
1283  if (!PyArg_ParseTuple(args, "i", &max_iterations)) {
1284  return Py_BuildValue("i", -1);
1285  }
1286  try {
1287  self->decomp->set_max_inner_iterations(max_iterations);
1288  Py_RETURN_NONE;
1289  } catch (std::exception& e) {
1290  PyErr_SetString(PyExc_Exception, e.what());
1291  return NULL;
1292  }
1293 }
1294 
1302 static PyObject *
1304 {
1305  PyObject* parameters_obj = NULL;
1306  PyArrayObject* parameters_arr = NULL;
1307 
1308  // parsing input arguments
1309  if (!PyArg_ParseTuple(args, "|O", &parameters_obj))
1310  return Py_BuildValue("i", -1);
1311 
1312  Matrix_real parameters_mtx;
1313  Matrix_real_float parameters_mtx_float;
1314  bool parameters_is_float32 = false;
1315  try {
1316  extract_parameters_any(parameters_obj, &parameters_arr, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1317  }
1318  catch (std::exception& e) {
1319  PyErr_SetString(PyExc_Exception, e.what());
1320  return NULL;
1321  }
1322 
1323  PyObject *unitary_py = NULL;
1324  if (parameters_is_float32 || self->decomp->get_use_float()) {
1325  if (!parameters_is_float32) {
1326  parameters_mtx_float = Matrix_real_float(parameters_mtx.rows, parameters_mtx.cols, parameters_mtx.stride);
1327  for (int row=0; row<parameters_mtx.rows; row++) {
1328  for (int col=0; col<parameters_mtx.cols; col++) {
1329  int idx = row*parameters_mtx.stride + col;
1330  parameters_mtx_float[idx] = static_cast<float>(parameters_mtx[idx]);
1331  }
1332  }
1333  }
1334  Matrix_float unitary_mtx = self->decomp->get_matrix(parameters_mtx_float);
1335  unitary_mtx.set_owner(false);
1336  unitary_py = matrix_float_to_numpy(unitary_mtx);
1337  }
1338  else {
1339  Matrix unitary_mtx = self->decomp->get_matrix(parameters_mtx);
1340  unitary_mtx.set_owner(false);
1341  unitary_py = matrix_to_numpy(unitary_mtx);
1342  }
1343 
1344  Py_DECREF(parameters_arr);
1345 
1346  return unitary_py;
1347 }
1348 
1356 static PyObject *
1358 {
1359  // The tuple of expected keywords
1360  static char *kwlist[] = {(char*)"costfnc", NULL};
1361 
1362  int costfnc_arg = 0;
1363 
1364  // parsing input arguments
1365  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &costfnc_arg)) {
1366  std::string err("Unsuccessful argument parsing");
1367  PyErr_SetString(PyExc_Exception, err.c_str());
1368  return NULL;
1369  }
1370 
1371  cost_function_type qgd_costfnc = (cost_function_type)costfnc_arg;
1372 
1373  try {
1374  self->decomp->set_cost_function_variant(qgd_costfnc);
1375  }
1376  catch (std::string err) {
1377  PyErr_SetString(PyExc_Exception, err.c_str());
1378  std::cout << err << std::endl;
1379  return NULL;
1380  }
1381  catch(...) {
1382  std::string err("Invalid pointer to decomposition class");
1383  PyErr_SetString(PyExc_Exception, err.c_str());
1384  return NULL;
1385  }
1386  return Py_BuildValue("i", 0);
1387 }
1388 
1395 static PyObject *
1397 {
1398  PyObject* parameters_obj = NULL;
1399  PyArrayObject* parameters_arg = NULL;
1400 
1401  // parsing input arguments
1402  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1403  std::string err("Unsuccessful argument parsing not ");
1404  PyErr_SetString(PyExc_Exception, err.c_str());
1405  return NULL;
1406  }
1407 
1408  Matrix_real parameters_mtx;
1409  Matrix_real_float parameters_mtx_float;
1410  bool parameters_is_float32 = false;
1411  double f0;
1412 
1413  try {
1414  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1415  if (parameters_is_float32) {
1416  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1417  }
1418  f0 = self->decomp->optimization_problem(parameters_mtx);
1419  }
1420  catch (std::exception& e) {
1421  PyErr_SetString(PyExc_Exception, e.what());
1422  return NULL;
1423  }
1424  catch (std::string err) {
1425  PyErr_SetString(PyExc_Exception, err.c_str());
1426  return NULL;
1427  }
1428  catch (...) {
1429  std::string err("Invalid pointer to decomposition class");
1430  PyErr_SetString(PyExc_Exception, err.c_str());
1431  return NULL;
1432  }
1433 
1434  Py_DECREF(parameters_arg);
1435 
1436  return Py_BuildValue("d", f0);
1437 }
1438 
1445 static PyObject *
1447 {
1448  PyObject* parameters_obj = NULL;
1449  PyArrayObject* parameters_arg = NULL;
1450 
1451  // parsing input arguments
1452  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1453  std::string err("Unsuccessful argument parsing not ");
1454  PyErr_SetString(PyExc_Exception, err.c_str());
1455  return NULL;
1456  }
1457 
1458  Matrix_real parameters_mtx;
1459  Matrix_real_float parameters_mtx_float;
1460  bool parameters_is_float32 = false;
1461  Matrix Umtx;
1462  std::vector<Matrix> Umtx_deriv;
1463 
1464  try {
1465  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1466  if (parameters_is_float32) {
1467  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1468  }
1469  self->decomp->optimization_problem_combined_unitary(parameters_mtx, Umtx, Umtx_deriv);
1470  }
1471  catch (std::exception& e) {
1472  PyErr_SetString(PyExc_Exception, e.what());
1473  return NULL;
1474  }
1475  catch (std::string err) {
1476  PyErr_SetString(PyExc_Exception, err.c_str());
1477  return NULL;
1478  }
1479  catch (...) {
1480  std::string err("Invalid pointer to decomposition class");
1481  PyErr_SetString(PyExc_Exception, err.c_str());
1482  return NULL;
1483  }
1484 
1485  // convert to numpy array
1486  Umtx.set_owner(false);
1487  PyObject *unitary_py = matrix_to_numpy(Umtx);
1488  PyObject* graduni_py = PyList_New(Umtx_deriv.size());
1489  for (size_t i = 0; i < Umtx_deriv.size(); i++) {
1490  Umtx_deriv[i].set_owner(false);
1491  PyList_SetItem(graduni_py, i, matrix_to_numpy(Umtx_deriv[i]));
1492  }
1493 
1494  Py_DECREF(parameters_arg);
1495 
1496  PyObject* p = Py_BuildValue("(OO)", unitary_py, graduni_py);
1497  Py_DECREF(unitary_py);
1498  Py_DECREF(graduni_py);
1499  return p;
1500 }
1501 
1508 static PyObject *
1510 {
1511  PyObject* parameters_obj = NULL;
1512  PyArrayObject* parameters_arg = NULL;
1513 
1514  // parsing input arguments
1515  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1516  std::string err("Unsuccessful argument parsing not ");
1517  PyErr_SetString(PyExc_Exception, err.c_str());
1518  return NULL;
1519  }
1520 
1521  Matrix_real parameters_mtx;
1522  Matrix_real_float parameters_mtx_float;
1523  bool parameters_is_float32 = false;
1524  Matrix_real grad_mtx(parameters_mtx.size(), 1);
1525 
1526  try {
1527  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1528  if (parameters_is_float32) {
1529  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1530  }
1531  grad_mtx = Matrix_real(parameters_mtx.size(), 1);
1532  self->decomp->optimization_problem_grad(parameters_mtx, self->decomp, grad_mtx);
1533  }
1534  catch (std::exception& e) {
1535  PyErr_SetString(PyExc_Exception, e.what());
1536  return NULL;
1537  }
1538  catch (std::string err) {
1539  PyErr_SetString(PyExc_Exception, err.c_str());
1540  return NULL;
1541  }
1542  catch (...) {
1543  std::string err("Invalid pointer to decomposition class");
1544  PyErr_SetString(PyExc_Exception, err.c_str());
1545  return NULL;
1546  }
1547 
1548  // convert to numpy array
1549  PyObject *grad_py = NULL;
1550  if (parameters_is_float32 || self->decomp->get_use_float()) {
1551  Matrix_real_float grad_float(grad_mtx.rows, grad_mtx.cols, grad_mtx.stride);
1552  for (int row=0; row<grad_mtx.rows; row++) {
1553  for (int col=0; col<grad_mtx.cols; col++) {
1554  int idx = row*grad_mtx.stride + col;
1555  grad_float[idx] = static_cast<float>(grad_mtx[idx]);
1556  }
1557  }
1558  grad_float.set_owner(false);
1559  grad_py = matrix_real_float_to_numpy(grad_float);
1560  }
1561  else {
1562  grad_mtx.set_owner(false);
1563  grad_py = matrix_real_to_numpy(grad_mtx);
1564  }
1565 
1566  Py_DECREF(parameters_arg);
1567 
1568  return grad_py;
1569 }
1570 
1577 static PyObject *
1579 {
1580  PyObject* parameters_obj = NULL;
1581  PyArrayObject* parameters_arg = NULL;
1582 
1583  // parsing input arguments
1584  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1585  std::string err("Unsuccessful argument parsing not ");
1586  PyErr_SetString(PyExc_Exception, err.c_str());
1587  return NULL;
1588  }
1589 
1590  Matrix_real parameters_mtx;
1591  Matrix_real_float parameters_mtx_float;
1592  bool parameters_is_float32 = false;
1593  Matrix_real grad_mtx(parameters_mtx.size(), 1);
1594  double f0;
1595 
1596  try {
1597  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1598  if (parameters_is_float32) {
1599  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1600  }
1601  grad_mtx = Matrix_real(parameters_mtx.size(), 1);
1602  self->decomp->optimization_problem_combined(parameters_mtx, &f0, grad_mtx);
1603  }
1604  catch (std::exception& e) {
1605  PyErr_SetString(PyExc_Exception, e.what());
1606  return NULL;
1607  }
1608  catch (std::string err) {
1609  PyErr_SetString(PyExc_Exception, err.c_str());
1610  return NULL;
1611  }
1612  catch (...) {
1613  std::string err("Invalid pointer to decomposition class");
1614  PyErr_SetString(PyExc_Exception, err.c_str());
1615  return NULL;
1616  }
1617 
1618  // convert to numpy array
1619  PyObject *grad_py = NULL;
1620  if (parameters_is_float32 || self->decomp->get_use_float()) {
1621  Matrix_real_float grad_float(grad_mtx.rows, grad_mtx.cols, grad_mtx.stride);
1622  for (int row=0; row<grad_mtx.rows; row++) {
1623  for (int col=0; col<grad_mtx.cols; col++) {
1624  int idx = row*grad_mtx.stride + col;
1625  grad_float[idx] = static_cast<float>(grad_mtx[idx]);
1626  }
1627  }
1628  grad_float.set_owner(false);
1629  grad_py = matrix_real_float_to_numpy(grad_float);
1630  }
1631  else {
1632  grad_mtx.set_owner(false);
1633  grad_py = matrix_real_to_numpy(grad_mtx);
1634  }
1635 
1636  Py_DECREF(parameters_arg);
1637 
1638  PyObject* p = Py_BuildValue("(dO)", f0, grad_py);
1639  Py_DECREF(grad_py);
1640  return p;
1641 }
1642 
1649 static PyObject *
1651 {
1652  PyObject* parameters_obj = NULL;
1653  PyArrayObject* parameters_arg = NULL;
1654 
1655  // parsing input arguments
1656  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1657  std::string err("Unsuccessful argument parsing not ");
1658  PyErr_SetString(PyExc_Exception, err.c_str());
1659  return NULL;
1660  }
1661 
1662  Matrix_real parameters_mtx;
1663  Matrix_real_float parameters_mtx_float;
1664  bool parameters_is_float32 = false;
1665  Matrix_real result_mtx;
1666 
1667  try {
1668  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1669  if (parameters_is_float32) {
1670  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1671  }
1672  std::vector<Matrix_real> parameters_vec;
1673  parameters_vec.resize(parameters_mtx.rows);
1674  for (int row_idx = 0; row_idx < parameters_mtx.rows; row_idx++) {
1675  parameters_vec[row_idx] = Matrix_real(parameters_mtx.get_data() + row_idx * parameters_mtx.stride, 1, parameters_mtx.cols, parameters_mtx.stride);
1676  }
1677  result_mtx = self->decomp->optimization_problem_batched(parameters_vec);
1678  }
1679  catch (std::exception& e) {
1680  PyErr_SetString(PyExc_Exception, e.what());
1681  return NULL;
1682  }
1683  catch (std::string err) {
1684  PyErr_SetString(PyExc_Exception, err.c_str());
1685  return NULL;
1686  }
1687  catch (...) {
1688  std::string err("Invalid pointer to decomposition class");
1689  PyErr_SetString(PyExc_Exception, err.c_str());
1690  return NULL;
1691  }
1692 
1693  // convert to numpy array
1694  PyObject *result_py = NULL;
1695  if (parameters_is_float32 || self->decomp->get_use_float()) {
1696  Matrix_real_float result_float(result_mtx.rows, result_mtx.cols, result_mtx.stride);
1697  for (int row=0; row<result_mtx.rows; row++) {
1698  for (int col=0; col<result_mtx.cols; col++) {
1699  int idx = row*result_mtx.stride + col;
1700  result_float[idx] = static_cast<float>(result_mtx[idx]);
1701  }
1702  }
1703  result_float.set_owner(false);
1704  result_py = matrix_real_float_to_numpy(result_float);
1705  }
1706  else {
1707  result_mtx.set_owner(false);
1708  result_py = matrix_real_to_numpy(result_mtx);
1709  }
1710 
1711  Py_DECREF(parameters_arg);
1712 
1713  return result_py;
1714 }
1715 
1721 static PyObject *
1723 {
1724 #ifdef __DFE__
1725  try {
1726  self->decomp->upload_Umtx_to_DFE();
1727  Py_RETURN_NONE;
1728  } catch (std::string err) {
1729  PyErr_SetString(PyExc_Exception, err.c_str());
1730  return NULL;
1731  } catch (std::exception& e) {
1732  PyErr_SetString(PyExc_Exception, e.what());
1733  return NULL;
1734  } catch (...) {
1735  std::string err("Invalid pointer to decomposition class");
1736  PyErr_SetString(PyExc_Exception, err.c_str());
1737  return NULL;
1738  }
1739 #else
1740  PyErr_SetString(PyExc_NotImplementedError, "upload_Umtx_to_DFE is only available when compiled with DFE support");
1741  return NULL;
1742 #endif
1743 }
1744 
1750 static PyObject *
1752 {
1753  try {
1754  int trace_offset = self->decomp->get_trace_offset();
1755  return Py_BuildValue("i", trace_offset);
1756  } catch (std::string err) {
1757  PyErr_SetString(PyExc_Exception, err.c_str());
1758  return NULL;
1759  } catch (std::exception& e) {
1760  PyErr_SetString(PyExc_Exception, e.what());
1761  return NULL;
1762  } catch (...) {
1763  std::string err("Invalid pointer to decomposition class");
1764  PyErr_SetString(PyExc_Exception, err.c_str());
1765  return NULL;
1766  }
1767 }
1768 
1775 static PyObject *
1777 {
1778  static char *kwlist[] = {(char*)"trace_offset", NULL};
1779 
1780  int trace_offset = 0;
1781  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &trace_offset)) {
1782  std::string err("Invalid arguments: expected (trace_offset: int)");
1783  PyErr_SetString(PyExc_Exception, err.c_str());
1784  return NULL;
1785  }
1786 
1787  try {
1788  self->decomp->set_trace_offset(trace_offset);
1789  Py_RETURN_NONE;
1790  } catch (std::string err) {
1791  PyErr_SetString(PyExc_Exception, err.c_str());
1792  return NULL;
1793  } catch (std::exception& e) {
1794  PyErr_SetString(PyExc_Exception, e.what());
1795  return NULL;
1796  } catch (...) {
1797  std::string err("Invalid pointer to decomposition class");
1798  PyErr_SetString(PyExc_Exception, err.c_str());
1799  return NULL;
1800  }
1801 }
1802 
1808 static PyObject *
1810 {
1811  try {
1812  double error = self->decomp->get_decomposition_error();
1813  return Py_BuildValue("d", error);
1814  } catch (std::string err) {
1815  PyErr_SetString(PyExc_Exception, err.c_str());
1816  return NULL;
1817  } catch (std::exception& e) {
1818  PyErr_SetString(PyExc_Exception, e.what());
1819  return NULL;
1820  } catch (...) {
1821  std::string err("Invalid pointer to decomposition class");
1822  PyErr_SetString(PyExc_Exception, err.c_str());
1823  return NULL;
1824  }
1825 }
1826 
1833 static PyObject *
1835 {
1836  PyObject *parameters_obj = NULL, *input_state_obj = NULL;
1837  PyArrayObject *parameters_arr = NULL, *input_state_arg = NULL;
1838  PyObject *qubit_list_arg = NULL;
1839 
1840  // Parse input arguments
1841  if (!PyArg_ParseTuple(args, "|OOO", &parameters_obj, &input_state_obj, &qubit_list_arg)) {
1842  return Py_BuildValue("i", -1);
1843  }
1844 
1845  Matrix_real parameters_mtx;
1846  Matrix_real_float parameters_mtx_float;
1847  bool parameters_is_float32 = false;
1848  try {
1849  extract_parameters_any(parameters_obj, &parameters_arr, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1850  if (parameters_is_float32) {
1851  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1852  }
1853  }
1854  catch (std::exception& e) {
1855  PyErr_SetString(PyExc_Exception, e.what());
1856  return NULL;
1857  }
1858 
1859  // Convert input state array
1860  if (input_state_obj == NULL) {
1861  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
1862  return NULL;
1863  }
1864 
1865  Matrix input_state_mtx;
1866  Matrix_float input_state_mtx_float;
1867  bool input_state_is_float32 = false;
1868  try {
1869  extract_matrix_any(input_state_obj, &input_state_arg, input_state_mtx, input_state_mtx_float, input_state_is_float32);
1870  if (input_state_is_float32) {
1871  input_state_mtx = input_state_mtx_float.to_float64();
1872  }
1873  }
1874  catch (std::exception& e) {
1875  PyErr_SetString(PyExc_Exception, e.what());
1876  return NULL;
1877  }
1878 
1879  // Test C-style contiguous memory allocation
1880  if (!PyArray_IS_C_CONTIGUOUS(input_state_arg)) {
1881  PyErr_SetString(PyExc_Exception, "Input matrix is not memory contiguous");
1882  return NULL;
1883  }
1884 
1885  // Check qubit list argument
1886  if (qubit_list_arg == NULL || !PyList_Check(qubit_list_arg)) {
1887  PyErr_SetString(PyExc_Exception, "qubit_list should be a list");
1888  return NULL;
1889  }
1890 
1891  Py_ssize_t reduced_qbit_num = PyList_Size(qubit_list_arg);
1892  matrix_base<int> qbit_list_mtx((int)reduced_qbit_num, 1);
1893 
1894  for (int idx = 0; idx < reduced_qbit_num; idx++) {
1895  PyObject* item = PyList_GET_ITEM(qubit_list_arg, idx);
1896  qbit_list_mtx[idx] = (int)PyLong_AsLong(item);
1897  }
1898 
1899  double entropy = -1;
1900 
1901  try {
1902  entropy = self->decomp->get_second_Renyi_entropy(parameters_mtx, input_state_mtx, qbit_list_mtx);
1903  } catch (std::string err) {
1904  PyErr_SetString(PyExc_Exception, err.c_str());
1905  return NULL;
1906  } catch (std::exception& e) {
1907  PyErr_SetString(PyExc_Exception, e.what());
1908  return NULL;
1909  } catch (...) {
1910  std::string err("Invalid pointer to decomposition class");
1911  PyErr_SetString(PyExc_Exception, err.c_str());
1912  return NULL;
1913  }
1914 
1915  // Clean up references
1916  Py_DECREF(parameters_arr);
1917  Py_DECREF(input_state_arg);
1918 
1919  PyObject* p = Py_BuildValue("d", entropy);
1920  return p;
1921 }
1922 
1928 static PyObject *
1930 {
1931  try {
1932  int qbit_num = self->decomp->get_qbit_num();
1933  return Py_BuildValue("i", qbit_num);
1934  } catch (std::string err) {
1935  PyErr_SetString(PyExc_Exception, err.c_str());
1936  return NULL;
1937  } catch (std::exception& e) {
1938  PyErr_SetString(PyExc_Exception, e.what());
1939  return NULL;
1940  } catch (...) {
1941  std::string err("Invalid pointer to decomposition class");
1942  PyErr_SetString(PyExc_Exception, err.c_str());
1943  return NULL;
1944  }
1945 }
1946 
1948 
1953 static PyObject *
1955 {
1956  PyObject* identical_blocks_dict;
1957  if (!PyArg_ParseTuple(args, "O", &identical_blocks_dict)) {
1958  return NULL;
1959  }
1960  if (!PyDict_Check(identical_blocks_dict)) {
1961  PyErr_SetString(PyExc_TypeError, "Expected dictionary argument");
1962  return NULL;
1963  }
1964  try {
1965  N_Qubit_Decomposition* base_decomp = dynamic_cast<N_Qubit_Decomposition*>(self->decomp);
1966  if (base_decomp == NULL) {
1967  PyErr_SetString(PyExc_AttributeError, "set_identical_blocks is only available for N_Qubit_Decomposition");
1968  return NULL;
1969  }
1970  std::map<int, int> identical_blocks_map;
1971  PyObject *key, *value;
1972  Py_ssize_t pos = 0;
1973  while (PyDict_Next(identical_blocks_dict, &pos, &key, &value)) {
1974  if (!PyLong_Check(key) || !PyLong_Check(value)) {
1975  PyErr_SetString(PyExc_TypeError, "Dictionary keys and values must be integers");
1976  return NULL;
1977  }
1978  int qubit_idx = PyLong_AsLong(key);
1979  int blocks = PyLong_AsLong(value);
1980  identical_blocks_map[qubit_idx] = blocks;
1981  }
1982  base_decomp->set_identical_blocks(identical_blocks_map);
1983  Py_RETURN_NONE;
1984  } catch (std::exception& e) {
1985  PyErr_SetString(PyExc_Exception, e.what());
1986  return NULL;
1987  }
1988 }
1989 
1994 static PyObject *
1996 {
1997  try {
1998  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
1999  if (adaptive_decomp == NULL) {
2000  PyErr_SetString(PyExc_AttributeError, "get_initial_circuit is only available for N_Qubit_Decomposition_adaptive");
2001  return NULL;
2002  }
2003  adaptive_decomp->get_initial_circuit();
2004  return Py_BuildValue("i", 0);
2005  } catch (std::exception& e) {
2006  PyErr_SetString(PyExc_Exception, e.what());
2007  return NULL;
2008  }
2009 }
2010 
2015 static PyObject *
2017 {
2018  try {
2019  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2020  if (adaptive_decomp == NULL) {
2021  PyErr_SetString(PyExc_AttributeError, "compress_circuit is only available for N_Qubit_Decomposition_adaptive");
2022  return NULL;
2023  }
2024  adaptive_decomp->compress_circuit();
2025  Py_RETURN_NONE;
2026  } catch (std::exception& e) {
2027  PyErr_SetString(PyExc_Exception, e.what());
2028  return NULL;
2029  }
2030 }
2031 
2036 static PyObject *
2038 {
2039  try {
2040  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2041  if (adaptive_decomp == NULL) {
2042  PyErr_SetString(PyExc_AttributeError, "finalize_circuit is only available for N_Qubit_Decomposition_adaptive");
2043  return NULL;
2044  }
2045  adaptive_decomp->finalize_circuit();
2046  Py_RETURN_NONE;
2047  } catch (std::exception& e) {
2048  PyErr_SetString(PyExc_Exception, e.what());
2049  return NULL;
2050  }
2051 }
2052 
2057 static PyObject *
2059 {
2060  // initiate variables for input arguments
2061  PyObject* filename_py=NULL;
2062  // parsing input arguments
2063  if (!PyArg_ParseTuple(args, "|O", &filename_py )) {
2064  return Py_BuildValue("i", -1);
2065  }
2066  // determine the optimizaton method
2067  PyObject* filename_string = PyObject_Str(filename_py);
2068  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
2069  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
2070  std::string filename_str( filename_C );
2071  try {
2072  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2073  if (adaptive_decomp == NULL) {
2074  std::string err("set_Gate_Structure_From_Binary is only available for adaptive decomposition");
2075  PyErr_SetString(PyExc_Exception, err.c_str());
2076  return NULL;
2077  }
2078  adaptive_decomp->set_adaptive_gate_structure( filename_str );
2079  }
2080  catch (std::string err ) {
2081  PyErr_SetString(PyExc_Exception, err.c_str());
2082  return NULL;
2083  }
2084  catch(...) {
2085  std::string err( "Invalid pointer to decomposition class");
2086  PyErr_SetString(PyExc_Exception, err.c_str());
2087  return NULL;
2088  }
2089  return Py_BuildValue("i", 0);
2090 
2091 }
2092 
2097 static PyObject *
2099 {
2100  // initiate variables for input arguments
2101  PyObject* filename_py = NULL;
2102  // parsing input arguments
2103  if (!PyArg_ParseTuple(args, "|O", &filename_py)) {
2104  return Py_BuildValue("i", -1);
2105  }
2106  // Convert PyObject to UTF-8 encoded string
2107  PyObject* filename_string = PyObject_Str(filename_py);
2108  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
2109  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
2110  std::string filename_str(filename_C);
2111  try {
2112  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2113  if (adaptive_decomp == NULL) {
2114  PyErr_SetString(PyExc_AttributeError, "add_Gate_Structure_From_Binary is only available for N_Qubit_Decomposition_adaptive");
2115  return NULL;
2116  }
2117  adaptive_decomp->add_adaptive_gate_structure(filename_str);
2118  }
2119  catch (std::string err) {
2120  PyErr_SetString(PyExc_Exception, err.c_str());
2121  return NULL;
2122  }
2123  catch(...) {
2124  std::string err("Invalid pointer to decomposition class");
2125  PyErr_SetString(PyExc_Exception, err.c_str());
2126  return NULL;
2127  }
2128  return Py_BuildValue("i", 0);
2129 }
2130 
2135 static PyObject *
2137 {
2138  // initiate variables for input arguments
2139  PyObject* filename_py = NULL;
2140  // parsing input arguments
2141  if (!PyArg_ParseTuple(args, "|O", &filename_py)) {
2142  return Py_BuildValue("i", -1);
2143  }
2144  // Convert PyObject to UTF-8 encoded string
2145  PyObject* filename_string = PyObject_Str(filename_py);
2146  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
2147  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
2148  std::string filename_str(filename_C);
2149  try {
2150  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2151  if (adaptive_decomp == NULL) {
2152  PyErr_SetString(PyExc_AttributeError, "set_Unitary_From_Binary is only available for N_Qubit_Decomposition_adaptive");
2153  return NULL;
2154  }
2155  adaptive_decomp->set_unitary_from_file(filename_str);
2156  }
2157  catch (std::string err) {
2158  PyErr_SetString(PyExc_Exception, err.c_str());
2159  return NULL;
2160  }
2161  catch(...) {
2162  std::string err("Invalid pointer to decomposition class");
2163  PyErr_SetString(PyExc_Exception, err.c_str());
2164  return NULL;
2165  }
2166  return Py_BuildValue("i", 0);
2167 }
2168 
2173 static PyObject *
2175 {
2176  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2177  if (adaptive_decomp == NULL) {
2178  PyErr_SetString(PyExc_AttributeError, "add_Adaptive_Layers is only available for N_Qubit_Decomposition_adaptive");
2179  return NULL;
2180  }
2181  adaptive_decomp->add_adaptive_layers();
2182  return Py_BuildValue("i", 0);
2183 }
2184 
2189 static PyObject *
2191 {
2192  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2193  if (adaptive_decomp == NULL) {
2194  PyErr_SetString(PyExc_AttributeError, "add_Layer_To_Imported_Gate_Structure is only available for N_Qubit_Decomposition_adaptive");
2195  return NULL;
2196  }
2197  adaptive_decomp->add_layer_to_imported_gate_structure();
2198  return Py_BuildValue("i", 0);
2199 }
2200 
2205 static PyObject *
2207 {
2208  try {
2209  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2210  if (adaptive_decomp == NULL) {
2211  PyErr_SetString(PyExc_AttributeError, "apply_Imported_Gate_Structure is only available for N_Qubit_Decomposition_adaptive");
2212  return NULL;
2213  }
2214  adaptive_decomp->apply_imported_gate_structure();
2215  }
2216  catch (std::string err) {
2217  PyErr_SetString(PyExc_Exception, err.c_str());
2218  return NULL;
2219  }
2220  catch(...) {
2221  std::string err("Invalid pointer to decomposition class");
2222  PyErr_SetString(PyExc_Exception, err.c_str());
2223  return NULL;
2224  }
2225  return Py_BuildValue("i", 0);
2226 }
2227 
2228 // ========================================================================= METHODS SHARED ACROSS DECOMP CLASSES
2229 
2236 static PyObject *
2238 {
2239  if ( self->Umtx != NULL ) {
2240  // release the unitary to be decomposed
2241  Py_DECREF(self->Umtx);
2242  self->Umtx = NULL;
2243  }
2244 
2245  PyObject *Umtx_obj = NULL;
2246  //Parse arguments
2247  if (!PyArg_ParseTuple(args, "|O", &Umtx_obj )) {
2248  return Py_BuildValue("i", -1);
2249  }
2250 
2251  // convert python object array to numpy C API array
2252  if ( Umtx_obj == NULL ) {
2253  PyErr_SetString(PyExc_Exception, "Umtx argument in empty");
2254  return NULL;
2255  }
2256 
2257  Matrix Umtx_mtx;
2258  Matrix_float Umtx_mtx_float;
2259  bool Umtx_is_float32 = false;
2260  try {
2261  extract_matrix_any(Umtx_obj, &self->Umtx, Umtx_mtx, Umtx_mtx_float, Umtx_is_float32);
2262  }
2263  catch (std::exception& e) {
2264  PyErr_SetString(PyExc_Exception, e.what());
2265  return NULL;
2266  }
2267 
2268  // Try each decomposition type that supports set_unitary
2269  if (N_Qubit_Decomposition_adaptive* p = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp)) {
2270  if (Umtx_is_float32) {
2271  p->set_unitary(Umtx_mtx_float);
2272  }
2273  else {
2274  p->set_unitary(Umtx_mtx);
2275  }
2276  return Py_BuildValue("i", 0);
2277  }
2278  if (N_Qubit_Decomposition_Tree_Search* p = dynamic_cast<N_Qubit_Decomposition_Tree_Search*>(self->decomp)) {
2279  if (Umtx_is_float32) {
2280  p->set_unitary(Umtx_mtx_float);
2281  }
2282  else {
2283  p->set_unitary(Umtx_mtx);
2284  }
2285  return Py_BuildValue("i", 0);
2286  }
2287  if (N_Qubit_Decomposition_Tabu_Search* p = dynamic_cast<N_Qubit_Decomposition_Tabu_Search*>(self->decomp)) {
2288  if (Umtx_is_float32) {
2289  p->set_unitary(Umtx_mtx_float);
2290  }
2291  else {
2292  p->set_unitary(Umtx_mtx);
2293  }
2294  return Py_BuildValue("i", 0);
2295  }
2296 
2297  PyErr_SetString(PyExc_TypeError, "set_unitary not available for this decomposition type");
2298  return NULL;
2299 }
2300 
2301 
2303 
2308 static PyObject*
2310 {
2311  std::vector<Gate*>&& gates = self->decomp->get_gates();
2312  Matrix_real&& params = self->decomp->get_optimized_parameters();
2313 
2314  PyObject* gates_list = PyList_New(0);
2315  if (!gates_list) return NULL;
2316 
2317  for (size_t idx = 0; idx < gates.size(); idx++) {
2318  Gate* gate = gates[idx];
2319  if (!gate) continue;
2320 
2321  PyObject* gate_dict = PyDict_New();
2322  if (!gate_dict) {
2323  Py_DECREF(gates_list);
2324  return NULL;
2325  }
2326 
2327  // Map gate type to string
2328  const char* type_str = nullptr;
2329  switch(gate->get_type()) {
2330  case GENERAL_OPERATION: type_str = "GENERAL"; break;
2331  case CZ_OPERATION: type_str = "CZ"; break;
2332  case CNOT_OPERATION: type_str = "CNOT"; break;
2333  case CH_OPERATION: type_str = "CH"; break;
2334  case U3_OPERATION: type_str = "U3"; break;
2335  case RY_OPERATION: type_str = "RY"; break;
2336  case RX_OPERATION: type_str = "RX"; break;
2337  case RZ_OPERATION: type_str = "RZ"; break;
2338  case X_OPERATION: type_str = "X"; break;
2339  case SX_OPERATION: type_str = "SX"; break;
2340  case CRY_OPERATION: type_str = "CRY"; break;
2341  case SYC_OPERATION: type_str = "SYC"; break;
2342  case BLOCK_OPERATION: type_str = "BLOCK"; break;
2343  case ADAPTIVE_OPERATION: type_str = "ADAPTIVE"; break;
2344  case DECOMPOSITION_BASE_CLASS: type_str = "DECOMPOSITION_BASE_CLASS"; break;
2345  case SUB_MATRIX_DECOMPOSITION_CLASS: type_str = "SUB_MATRIX_DECOMPOSITION_CLASS"; break;
2346  case N_QUBIT_DECOMPOSITION_CLASS_BASE: type_str = "N_QUBIT_DECOMPOSITION_CLASS_BASE"; break;
2347  case N_QUBIT_DECOMPOSITION_CLASS: type_str = "N_QUBIT_DECOMPOSITION_CLASS"; break;
2348  case Y_OPERATION: type_str = "Y"; break;
2349  case Z_OPERATION: type_str = "Z"; break;
2350  case H_OPERATION: type_str = "H"; break;
2351  case CROT_OPERATION: type_str = "CROT"; break;
2352  case R_OPERATION: type_str = "R"; break;
2353  case T_OPERATION: type_str = "T"; break;
2354  case TDG_OPERATION: type_str = "TDG"; break;
2355  case U1_OPERATION: type_str = "U1"; break;
2356  case U2_OPERATION: type_str = "U2"; break;
2357  case CR_OPERATION: type_str = "CR"; break;
2358  case S_OPERATION: type_str = "S"; break;
2359  case SDG_OPERATION: type_str = "SDG"; break;
2360  case CU_OPERATION: type_str = "CU"; break;
2361  case CP_OPERATION: type_str = "CP"; break;
2362  case CRX_OPERATION: type_str = "CRX"; break;
2363  case CRZ_OPERATION: type_str = "CRZ"; break;
2364  case CCX_OPERATION: type_str = "CCX"; break;
2365  case SWAP_OPERATION: type_str = "SWAP"; break;
2366  case CSWAP_OPERATION: type_str = "CSWAP"; break;
2367  default: type_str = "UNKNOWN"; break;
2368  }
2369  PyDict_SetItemString(gate_dict, "type", PyUnicode_FromString(type_str));
2370 
2371  PyDict_SetItemString(gate_dict, "target_qbit", PyLong_FromLong(gate->get_target_qbit()));
2372 
2373  int control_qbit = gate->get_control_qbit();
2374  if (control_qbit >= 0) {
2375  PyDict_SetItemString(gate_dict, "control_qbit", PyLong_FromLong(control_qbit));
2376  }
2377 
2378  // Add parameters
2379  int pnum = gate->get_parameter_num();
2380  int pstart = gate->get_parameter_start_idx();
2381  if (pnum > 0 && pstart >= 0 && (pstart + pnum) <= (int)params.size()) {
2382  if (gate->get_type() == U3_OPERATION && pnum >= 3) {
2383  PyDict_SetItemString(gate_dict, "Theta", PyFloat_FromDouble(params[pstart]));
2384  PyDict_SetItemString(gate_dict, "Phi", PyFloat_FromDouble(params[pstart + 1]));
2385  PyDict_SetItemString(gate_dict, "Lambda", PyFloat_FromDouble(params[pstart + 2]));
2386  } else if (gate->get_type() == RX_OPERATION || gate->get_type() == RY_OPERATION || gate->get_type() == CRY_OPERATION) {
2387  PyDict_SetItemString(gate_dict, "Theta", PyFloat_FromDouble(params[pstart]));
2388  } else if (gate->get_type() == RZ_OPERATION) {
2389  PyDict_SetItemString(gate_dict, "Phi", PyFloat_FromDouble(params[pstart]));
2390  }
2391  }
2392 
2393  PyList_Append(gates_list, gate_dict);
2394  Py_DECREF(gate_dict);
2395  }
2396  return gates_list;
2397 }
2398 
2403 static PyObject*
2405 {
2406  // Import Qiskit_IO module
2407  PyObject* qiskit_io_module = PyImport_ImportModule("squander.IO_interfaces.Qiskit_IO");
2408  if (!qiskit_io_module) {
2409  PyErr_SetString(PyExc_ImportError, "Failed to import squander.IO_interfaces.Qiskit_IO");
2410  return NULL;
2411  }
2412 
2413  // Get the get_Qiskit_Circuit function
2414  PyObject* get_qiskit_func = PyObject_GetAttrString(qiskit_io_module, "get_Qiskit_Circuit");
2415  Py_DECREF(qiskit_io_module);
2416  if (!get_qiskit_func) {
2417  PyErr_SetString(PyExc_AttributeError, "get_Qiskit_Circuit not found in Qiskit_IO");
2418  return NULL;
2419  }
2420 
2421  // Get circuit and parameters
2423  if (!circuit) {
2424  Py_DECREF(get_qiskit_func);
2425  return NULL;
2426  }
2428  if (!parameters) {
2429  Py_DECREF(get_qiskit_func);
2430  Py_DECREF(circuit);
2431  return NULL;
2432  }
2433 
2434  // Call get_Qiskit_Circuit(circuit, parameters)
2435  PyObject* args = PyTuple_Pack(2, circuit, parameters);
2436  PyObject* result = PyObject_CallObject(get_qiskit_func, args);
2437 
2438  Py_DECREF(args);
2439  Py_DECREF(parameters);
2440  Py_DECREF(circuit);
2441  Py_DECREF(get_qiskit_func);
2442 
2443  return result;
2444 }
2445 
2451 #define CIRQ_ADD_SINGLE_QUBIT_GATE(name) do { \
2452  PyObject* gate_func = PyObject_GetAttrString(cirq_module, #name); \
2453  PyObject* gate_args = PyTuple_Pack(1, target_qubit); \
2454  PyObject* cirq_gate = PyObject_CallObject(gate_func, gate_args); \
2455  Py_DECREF(gate_args); Py_DECREF(gate_func); \
2456  if (cirq_gate) { \
2457  PyObject* append_args = PyTuple_Pack(1, cirq_gate); \
2458  PyObject_CallObject(append_func, append_args); \
2459  Py_DECREF(append_args); Py_DECREF(cirq_gate); \
2460  } \
2461 } while(0)
2462 
2463 // Helper macros for Cirq gate creation
2464 #define CIRQ_ADD_TWO_QUBIT_GATE(name) do { \
2465  PyObject* control_qbit_obj = PyDict_GetItemString(gate, "control_qbit"); \
2466  if (!control_qbit_obj) continue; \
2467  long control_idx = qbit_num - 1 - PyLong_AsLong(control_qbit_obj); \
2468  PyObject* control_qubit = PyList_GetItem(qubits, control_idx); \
2469  PyObject* gate_func = PyObject_GetAttrString(cirq_module, #name); \
2470  PyObject* gate_args = PyTuple_Pack(2, control_qubit, target_qubit); \
2471  PyObject* cirq_gate = PyObject_CallObject(gate_func, gate_args); \
2472  Py_DECREF(gate_args); Py_DECREF(gate_func); \
2473  if (cirq_gate) { \
2474  PyObject* append_args = PyTuple_Pack(1, cirq_gate); \
2475  PyObject_CallObject(append_func, append_args); \
2476  Py_DECREF(append_args); Py_DECREF(cirq_gate); \
2477  } \
2478 } while(0)
2479 
2480 #define CIRQ_ADD_ROTATION_GATE(name, param) do { \
2481  PyObject* param_obj = PyDict_GetItemString(gate, param); \
2482  if (!param_obj) continue; \
2483  PyObject* gate_func = PyObject_GetAttrString(cirq_module, #name); \
2484  PyObject* gate_args = PyTuple_Pack(1, param_obj); \
2485  PyObject* cirq_gate = PyObject_CallObject(gate_func, gate_args); \
2486  Py_DECREF(gate_args); Py_DECREF(gate_func); \
2487  if (cirq_gate) { \
2488  PyObject* on_method = PyObject_GetAttrString(cirq_gate, "on"); \
2489  PyObject* on_args = PyTuple_Pack(1, target_qubit); \
2490  PyObject* gate_op = PyObject_CallObject(on_method, on_args); \
2491  Py_DECREF(on_args); Py_DECREF(on_method); Py_DECREF(cirq_gate); \
2492  if (gate_op) { \
2493  PyObject* append_args = PyTuple_Pack(1, gate_op); \
2494  PyObject_CallObject(append_func, append_args); \
2495  Py_DECREF(append_args); Py_DECREF(gate_op); \
2496  } \
2497  } \
2498 } while(0)
2499 
2500 static PyObject*
2502 {
2503  PyObject* cirq_module = PyImport_ImportModule("cirq");
2504  if (!cirq_module) {
2505  PyErr_SetString(PyExc_ImportError, "Failed to import cirq. Please install cirq package.");
2506  return NULL;
2507  }
2508 
2509  PyObject* cirq_circuit_class = PyObject_GetAttrString(cirq_module, "Circuit");
2510  if (!cirq_circuit_class) {
2511  Py_DECREF(cirq_module);
2512  return NULL;
2513  }
2514 
2515  PyObject* cirq_circuit_obj = PyObject_CallObject(cirq_circuit_class, NULL);
2516  Py_DECREF(cirq_circuit_class);
2517  if (!cirq_circuit_obj) {
2518  Py_DECREF(cirq_module);
2519  return NULL;
2520  }
2521 
2522  // Create qubit register
2523  PyObject* cirq_line_qubit_class = PyObject_GetAttrString(cirq_module, "LineQubit");
2524  if (!cirq_line_qubit_class) {
2525  Py_DECREF(cirq_circuit_obj);
2526  Py_DECREF(cirq_module);
2527  return NULL;
2528  }
2529  PyObject* range_func = PyObject_GetAttrString(cirq_line_qubit_class, "range");
2530  Py_DECREF(cirq_line_qubit_class);
2531  if (!range_func) {
2532  Py_DECREF(cirq_circuit_obj);
2533  Py_DECREF(cirq_module);
2534  return NULL;
2535  }
2536 
2537  int qbit_num = self->decomp->get_qbit_num();
2538  PyObject* range_args = PyTuple_Pack(1, PyLong_FromLong(qbit_num));
2539  PyObject* qubits = PyObject_CallObject(range_func, range_args);
2540  Py_DECREF(range_args); Py_DECREF(range_func);
2541  if (!qubits) {
2542  Py_DECREF(cirq_circuit_obj);
2543  Py_DECREF(cirq_module);
2544  return NULL;
2545  }
2546 
2547  PyObject* gates_list = qgd_N_Qubit_Decomposition_Wrapper_get_Gates(self);
2548  if (!gates_list) {
2549  Py_DECREF(qubits);
2550  Py_DECREF(cirq_circuit_obj);
2551  Py_DECREF(cirq_module);
2552  return NULL;
2553  }
2554 
2555  PyObject* append_func = PyObject_GetAttrString(cirq_circuit_obj, "append");
2556  if (!append_func) {
2557  Py_DECREF(gates_list); Py_DECREF(qubits); Py_DECREF(cirq_circuit_obj); Py_DECREF(cirq_module);
2558  return NULL;
2559  }
2560 
2561  PyObject* cirq_google_module = PyObject_GetAttrString(cirq_module, "google");
2562 
2563  // Process gates in reverse order
2564  Py_ssize_t num_gates = PyList_Size(gates_list);
2565  for (Py_ssize_t idx = num_gates - 1; idx >= 0; idx--) {
2566  PyObject* gate = PyList_GetItem(gates_list, idx);
2567  if (!gate) continue;
2568 
2569  PyObject* gate_type = PyDict_GetItemString(gate, "type");
2570  if (!gate_type) continue;
2571  const char* gate_type_str = PyUnicode_AsUTF8(gate_type);
2572  if (!gate_type_str) continue;
2573 
2574  PyObject* target_qbit_obj = PyDict_GetItemString(gate, "target_qbit");
2575  if (!target_qbit_obj) continue;
2576 
2577  long target_idx = qbit_num - 1 - PyLong_AsLong(target_qbit_obj);
2578  PyObject* target_qubit = PyList_GetItem(qubits, target_idx);
2579  if (!target_qubit) continue;
2580 
2581  if (strcmp(gate_type_str, "CNOT") == 0) { CIRQ_ADD_TWO_QUBIT_GATE(CNOT); }
2582  else if (strcmp(gate_type_str, "CZ") == 0) { CIRQ_ADD_TWO_QUBIT_GATE(CZ); }
2583  else if (strcmp(gate_type_str, "CH") == 0) { CIRQ_ADD_TWO_QUBIT_GATE(CH); }
2584  else if (strcmp(gate_type_str, "SYC") == 0 && cirq_google_module) {
2585  PyObject* control_qbit_obj = PyDict_GetItemString(gate, "control_qbit");
2586  if (control_qbit_obj) {
2587  long control_idx = qbit_num - 1 - PyLong_AsLong(control_qbit_obj);
2588  PyObject* control_qubit = PyList_GetItem(qubits, control_idx);
2589 
2590  PyObject* syc_func = PyObject_GetAttrString(cirq_google_module, "SYC");
2591  PyObject* syc_args = PyTuple_Pack(2, control_qubit, target_qubit);
2592  PyObject* cirq_gate = PyObject_CallObject(syc_func, syc_args);
2593  Py_DECREF(syc_args);
2594  Py_DECREF(syc_func);
2595  if (cirq_gate) {
2596  PyObject* append_args = PyTuple_Pack(1, cirq_gate);
2597  PyObject_CallObject(append_func, append_args);
2598  Py_DECREF(append_args);
2599  Py_DECREF(cirq_gate);
2600  }
2601  }
2602  }
2603  else if (strcmp(gate_type_str, "CRY") == 0) {
2604  printf("CRY gate needs to be implemented\n");
2605  }
2606  else if (strcmp(gate_type_str, "U3") == 0) {
2607  printf("Unsupported gate in the Cirq export: U3 gate\n");
2608  Py_XDECREF(cirq_google_module);
2609  Py_DECREF(append_func);
2610  Py_DECREF(gates_list);
2611  Py_DECREF(qubits);
2612  Py_DECREF(cirq_circuit_obj);
2613  Py_DECREF(cirq_module);
2614  Py_RETURN_NONE;
2615  }
2616  else if (strcmp(gate_type_str, "RX") == 0) { CIRQ_ADD_ROTATION_GATE(rx, "Theta"); }
2617  else if (strcmp(gate_type_str, "RY") == 0) { CIRQ_ADD_ROTATION_GATE(ry, "Theta"); }
2618  else if (strcmp(gate_type_str, "RZ") == 0) { CIRQ_ADD_ROTATION_GATE(rz, "Phi"); }
2619  else if (strcmp(gate_type_str, "X") == 0) { CIRQ_ADD_SINGLE_QUBIT_GATE(x); }
2620  else if (strcmp(gate_type_str, "Y") == 0) { CIRQ_ADD_SINGLE_QUBIT_GATE(y); }
2621  else if (strcmp(gate_type_str, "Z") == 0) { CIRQ_ADD_SINGLE_QUBIT_GATE(z); }
2622  else if (strcmp(gate_type_str, "SX") == 0) { CIRQ_ADD_SINGLE_QUBIT_GATE(sx); }
2623  }
2624 
2625  Py_XDECREF(cirq_google_module);
2626  Py_DECREF(append_func);
2627  Py_DECREF(gates_list);
2628  Py_DECREF(qubits);
2629  Py_DECREF(cirq_module);
2630 
2631  return cirq_circuit_obj;
2632 }
2633 
2634 #undef CIRQ_ADD_SINGLE_QUBIT_GATE
2635 #undef CIRQ_ADD_TWO_QUBIT_GATE
2636 #undef CIRQ_ADD_ROTATION_GATE
2637 
2643 static PyObject*
2645 {
2646  // Import Qiskit_IO module
2647  PyObject* qiskit_io_module = PyImport_ImportModule("squander.IO_interfaces.Qiskit_IO");
2648  if (!qiskit_io_module) {
2649  PyErr_SetString(PyExc_ImportError, "Failed to import squander.IO_interfaces.Qiskit_IO");
2650  return NULL;
2651  }
2652  // Get the convert_Qiskit_to_Squander function
2653  PyObject* convert_func = PyObject_GetAttrString(qiskit_io_module, "convert_Qiskit_to_Squander");
2654  Py_DECREF(qiskit_io_module);
2655  if (!convert_func) {
2656  PyErr_SetString(PyExc_AttributeError, "convert_Qiskit_to_Squander not found in Qiskit_IO");
2657  return NULL;
2658  }
2659  // Call convert_Qiskit_to_Squander(qc_in) -> returns (circuit, parameters)
2660  PyObject* convert_args = PyTuple_Pack(1, qc_in);
2661  PyObject* convert_result = PyObject_CallObject(convert_func, convert_args);
2662  Py_DECREF(convert_args);
2663  Py_DECREF(convert_func);
2664  if (!convert_result || !PyTuple_Check(convert_result) || PyTuple_Size(convert_result) != 2) {
2665  Py_XDECREF(convert_result);
2666  PyErr_SetString(PyExc_ValueError, "convert_Qiskit_to_Squander should return (circuit, parameters)");
2667  return NULL;
2668  }
2669 
2670  PyObject *circuit_squander = PyTuple_GetItem(convert_result, 0), *parameters = PyTuple_GetItem(convert_result, 1);
2671 
2672  // Set gate structure
2673  PyObject* set_gate_args = PyTuple_Pack(1, circuit_squander);
2674  PyObject* set_gate_result = qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure(self, set_gate_args);
2675  Py_DECREF(set_gate_args);
2676  if (!set_gate_result) {
2677  Py_DECREF(convert_result);
2678  return NULL;
2679  }
2680  Py_DECREF(set_gate_result);
2681 
2682  // Set optimized parameters
2683  PyObject* set_params_args = PyTuple_Pack(1, parameters);
2684  PyObject* set_params_result = qgd_N_Qubit_Decomposition_Wrapper_set_Optimized_Parameters(self, set_params_args);
2685  Py_DECREF(set_params_args);
2686  Py_DECREF(convert_result);
2687  if (!set_params_result) {
2688  return NULL;
2689  }
2690  Py_DECREF(set_params_result);
2691 
2692  Py_RETURN_NONE;
2693 }
2694 
2700 static PyObject*
2702 {
2703  // Import qiskit module
2704  PyObject* qiskit_module = PyImport_ImportModule("qiskit");
2705  if (!qiskit_module) {
2706  PyErr_SetString(PyExc_ImportError, "Failed to import qiskit");
2707  return NULL;
2708  }
2709  // Get transpile function
2710  PyObject* transpile_func = PyObject_GetAttrString(qiskit_module, "transpile");
2711  Py_DECREF(qiskit_module);
2712  if (!transpile_func) {
2713  PyErr_SetString(PyExc_AttributeError, "transpile not found in qiskit");
2714  return NULL;
2715  }
2716 
2717  // Transpile: transpile(qc_in, optimization_level=0, basis_gates=['cz', 'u3'], layout_method='sabre')
2718  PyObject* basis_gates = PyList_New(2);
2719  PyList_SetItem(basis_gates, 0, PyUnicode_FromString("cz"));
2720  PyList_SetItem(basis_gates, 1, PyUnicode_FromString("u3"));
2721 
2722  PyObject* kwargs = PyDict_New();
2723  PyDict_SetItemString(kwargs, "optimization_level", PyLong_FromLong(0));
2724  PyDict_SetItemString(kwargs, "basis_gates", basis_gates);
2725  PyDict_SetItemString(kwargs, "layout_method", PyUnicode_FromString("sabre"));
2726 
2727  PyObject* transpile_args = PyTuple_Pack(1, qc_in);
2728  PyObject* qc = PyObject_Call(transpile_func, transpile_args, kwargs);
2729 
2730  Py_DECREF(transpile_args);
2731  Py_DECREF(kwargs);
2732  Py_DECREF(basis_gates);
2733  Py_DECREF(transpile_func);
2734  if (!qc) {
2735  return NULL;
2736  }
2737 
2738  // Print gate counts
2739  PyObject* count_ops_func = PyObject_GetAttrString(qc, "count_ops");
2740  if (count_ops_func) {
2741  PyObject* count_ops_result = PyObject_CallObject(count_ops_func, NULL);
2742  Py_DECREF(count_ops_func);
2743  if (count_ops_result) {
2744  printf("Gate counts in the imported Qiskit transpiled quantum circuit: ");
2745  PyObject_Print(count_ops_result, stdout, 0);
2746  printf("\n");
2747  Py_DECREF(count_ops_result);
2748  }
2749  }
2750 
2751  // Get circuit data
2752  PyObject* qc_data_attr = PyObject_GetAttrString(qc, "data");
2753  PyObject* qc_qubits_attr = PyObject_GetAttrString(qc, "qubits");
2754  PyObject* qc_num_qubits_attr = PyObject_GetAttrString(qc, "num_qubits");
2755  if (!qc_data_attr || !qc_qubits_attr || !qc_num_qubits_attr) {
2756  Py_XDECREF(qc_data_attr);
2757  Py_XDECREF(qc_qubits_attr);
2758  Py_XDECREF(qc_num_qubits_attr);
2759  Py_DECREF(qc);
2760  return NULL;
2761  }
2762 
2763  int register_size = PyLong_AsLong(qc_num_qubits_attr);
2764  Py_DECREF(qc_num_qubits_attr);
2765 
2766  // Import Circuit_Wrapper
2767  PyObject* circuit_wrapper_module = PyImport_ImportModule("squander.gates.qgd_Circuit_Wrapper");
2768  if (!circuit_wrapper_module) {
2769  Py_DECREF(qc_data_attr);
2770  Py_DECREF(qc_qubits_attr);
2771  Py_DECREF(qc);
2772  return NULL;
2773  }
2774 
2775  PyObject* circuit_wrapper_class = PyObject_GetAttrString(circuit_wrapper_module, "qgd_Circuit_Wrapper");
2776  Py_DECREF(circuit_wrapper_module);
2777  if (!circuit_wrapper_class) {
2778  Py_DECREF(qc_data_attr);
2779  Py_DECREF(qc_qubits_attr);
2780  Py_DECREF(qc);
2781  return NULL;
2782  }
2783 
2784  // Create main circuit: Circuit_ret = qgd_Circuit_Wrapper(register_size)
2785  PyObject* circuit_ret_args = PyTuple_Pack(1, PyLong_FromLong(register_size));
2786  PyObject* Circuit_ret_result = PyObject_CallObject(circuit_wrapper_class, circuit_ret_args);
2787  Py_DECREF(circuit_ret_args);
2788  Py_DECREF(circuit_wrapper_class);
2789  if (!Circuit_ret_result) {
2790  Py_DECREF(qc_data_attr);
2791  Py_DECREF(qc_qubits_attr);
2792  Py_DECREF(qc);
2793  return NULL;
2794  }
2795 
2796  // Create dictionary for single qubit gates: single_qubit_gates[qubit] = []
2797  PyObject* single_qubit_gates = PyDict_New();
2798  for (int idx = 0; idx < register_size; idx++) {
2799  PyObject* key = PyLong_FromLong(idx);
2800  PyObject* value = PyList_New(0);
2801  PyDict_SetItem(single_qubit_gates, key, value);
2802  Py_DECREF(key);
2803  Py_DECREF(value);
2804  }
2805 
2806  PyObject* optimized_parameters = PyList_New(0);
2807 
2808  // Process gates from qc.data
2809  Py_ssize_t qc_data_attr_size = PyList_Size(qc_data_attr);
2810  for (Py_ssize_t i = 0; i < qc_data_attr_size; i++) {
2811  PyObject* gate = PyList_GetItem(qc_data_attr, i);
2812  PyObject* gate_operation = PyObject_GetAttrString(gate, "operation");
2813  PyObject* gate_qubits = PyObject_GetAttrString(gate, "qubits");
2814  if (!gate_operation || !gate_qubits) {
2815  Py_XDECREF(gate_operation);
2816  Py_XDECREF(gate_qubits);
2817  continue;
2818  }
2819 
2820  PyObject* gate_operation_name_attr = PyObject_GetAttrString(gate_operation, "name");
2821  const char* name = PyUnicode_AsUTF8(gate_operation_name_attr);
2822 
2823  if (strcmp(name, "u3") == 0) {
2824  // Get qubit index
2825  PyObject* index_func = PyObject_GetAttrString(qc_qubits_attr, "index");
2826 
2827  PyObject* index_args = PyTuple_Pack(1, PyList_GetItem(gate_qubits, 0));
2828  PyObject* index_result = PyObject_CallObject(index_func, index_args);
2829  Py_DECREF(index_func);
2830  Py_DECREF(index_args);
2831 
2832  long qubit = PyLong_AsLong(index_result);
2833  Py_DECREF(index_result);
2834 
2835  // Store u3 gate info
2836  PyObject* gate_info_dict = PyDict_New();
2837  PyObject* gate_operation_params_attr = PyObject_GetAttrString(gate_operation, "params");
2838  PyDict_SetItemString(gate_info_dict, "params", gate_operation_params_attr);
2839  PyDict_SetItemString(gate_info_dict, "type", PyUnicode_FromString("u3"));
2840  Py_DECREF(gate_operation_params_attr);
2841 
2842  PyObject* qubit_list = PyDict_GetItem(single_qubit_gates, PyLong_FromLong(qubit));
2843  PyList_Append(qubit_list, gate_info_dict);
2844  Py_DECREF(gate_info_dict);
2845  } else if (strcmp(name, "cz") == 0) {
2846  // Get qubit indices
2847  PyObject* index_func = PyObject_GetAttrString(qc_qubits_attr, "index");
2848 
2849  PyObject* index_args0 = PyTuple_Pack(1, PyList_GetItem(gate_qubits, 0));
2850  PyObject* index_args0_result = PyObject_CallObject(index_func, index_args0);
2851  Py_DECREF(index_args0);
2852 
2853  PyObject* index_args1 = PyTuple_Pack(1, PyList_GetItem(gate_qubits, 1));
2854  PyObject* index_args1_result = PyObject_CallObject(index_func, index_args1);
2855  Py_DECREF(index_args1);
2856  Py_DECREF(index_func);
2857 
2858  long qubit0 = PyLong_AsLong(index_args0_result);
2859  long qubit1 = PyLong_AsLong(index_args1_result);
2860  Py_DECREF(index_args0_result);
2861  Py_DECREF(index_args1_result);
2862 
2863  // Create layer
2864  PyObject* layer_args = PyTuple_Pack(1, PyLong_FromLong(register_size));
2865  PyObject* circuit_wrapper_module2 = PyImport_ImportModule("squander.gates.qgd_Circuit_Wrapper");
2866  PyObject* circuit_wrapper_class2 = PyObject_GetAttrString(circuit_wrapper_module2, "qgd_Circuit_Wrapper");
2867  Py_DECREF(circuit_wrapper_module2);
2868 
2869  PyObject* Layer = PyObject_CallObject(circuit_wrapper_class2, layer_args);
2870  Py_DECREF(layer_args);
2871  Py_DECREF(circuit_wrapper_class2);
2872 
2873  // Add u3 gates for qubit0
2874  PyObject* qubit0_list = PyDict_GetItem(single_qubit_gates, PyLong_FromLong(qubit0));
2875  if (qubit0_list && PyList_Size(qubit0_list) > 0) {
2876  PyObject* gate0 = PyList_GetItem(qubit0_list, 0);
2877  PyList_SetSlice(qubit0_list, 0, 1, NULL); // pop first element
2878 
2879  PyObject* add_u3_func = PyObject_GetAttrString(Layer, "add_U3");
2880  PyObject* add_u3_args = Py_BuildValue("(iOOO)", qubit0, Py_True, Py_True, Py_True);
2881  PyObject_CallObject(add_u3_func, add_u3_args);
2882  Py_DECREF(add_u3_func);
2883  Py_DECREF(add_u3_args);
2884 
2885  // Add parameters (reversed)
2886  PyObject* params = PyDict_GetItemString(gate0, "params");
2887  PyObject* reversed_params = PyList_New(0);
2888  for (Py_ssize_t j = PyList_Size(params) - 1; j >= 0; j--) {
2889  PyList_Append(reversed_params, PyList_GetItem(params, j));
2890  }
2891  for (Py_ssize_t j = 0; j < PyList_Size(reversed_params); j++) {
2892  PyList_Append(optimized_parameters, PyList_GetItem(reversed_params, j));
2893  }
2894  Py_DECREF(reversed_params);
2895 
2896  // Divide last parameter by 2
2897  Py_ssize_t last_idx = PyList_Size(optimized_parameters) - 1;
2898  PyObject* last_param = PyList_GetItem(optimized_parameters, last_idx);
2899  double val = PyFloat_AsDouble(last_param) / 2.0;
2900  PyList_SetItem(optimized_parameters, last_idx, PyFloat_FromDouble(val));
2901  }
2902 
2903  // Add u3 gates for qubit1
2904  PyObject* qubit1_list = PyDict_GetItem(single_qubit_gates, PyLong_FromLong(qubit1));
2905  if (qubit1_list && PyList_Size(qubit1_list) > 0) {
2906  PyObject* gate1 = PyList_GetItem(qubit1_list, 0);
2907  PyList_SetSlice(qubit1_list, 0, 1, NULL);
2908 
2909  PyObject* add_u3_func = PyObject_GetAttrString(Layer, "add_U3");
2910  PyObject* u3_args = Py_BuildValue("(iOOO)", qubit1, Py_True, Py_True, Py_True);
2911  PyObject_CallObject(add_u3_func, u3_args);
2912  Py_DECREF(add_u3_func);
2913  Py_DECREF(u3_args);
2914 
2915  PyObject* params = PyDict_GetItemString(gate1, "params");
2916  PyObject* reversed_params = PyList_New(0);
2917  for (Py_ssize_t j = PyList_Size(params) - 1; j >= 0; j--) {
2918  PyList_Append(reversed_params, PyList_GetItem(params, j));
2919  }
2920  for (Py_ssize_t j = 0; j < PyList_Size(reversed_params); j++) {
2921  PyList_Append(optimized_parameters, PyList_GetItem(reversed_params, j));
2922  }
2923  Py_DECREF(reversed_params);
2924 
2925  Py_ssize_t last_idx = PyList_Size(optimized_parameters) - 1;
2926  PyObject* last_param = PyList_GetItem(optimized_parameters, last_idx);
2927  double val = PyFloat_AsDouble(last_param) / 2.0;
2928  PyList_SetItem(optimized_parameters, last_idx, PyFloat_FromDouble(val));
2929  }
2930 
2931  // Add RX, adaptive, RZ, RX sequence
2932  PyObject* qubit0_obj = PyLong_FromLong(qubit0);
2933  PyObject* qubit1_obj = PyLong_FromLong(qubit1);
2934 
2935  PyObject* add_rx_func = PyObject_GetAttrString(Layer, "add_RX");
2936  PyObject* add_rx_arg = PyTuple_Pack(1, qubit0_obj);
2937  PyObject_CallObject(add_rx_func, add_rx_arg);
2938  Py_DECREF(add_rx_func);
2939  Py_DECREF(add_rx_arg);
2940 
2941  PyObject* add_adaptive_func = PyObject_GetAttrString(Layer, "add_adaptive");
2942  PyObject* add_adaptive_args = PyTuple_Pack(2, qubit0_obj, qubit1_obj);
2943  PyObject_CallObject(add_adaptive_func, add_adaptive_args);
2944  Py_DECREF(add_adaptive_func);
2945  Py_DECREF(add_adaptive_args);
2946 
2947  PyObject* add_rz_func = PyObject_GetAttrString(Layer, "add_RZ");
2948  PyObject* add_rz_arg = PyTuple_Pack(1, qubit1_obj);
2949  PyObject_CallObject(add_rz_func, add_rz_arg);
2950  Py_DECREF(add_rz_func);
2951  Py_DECREF(add_rz_arg);
2952 
2953  PyObject* add_rx_func_2 = PyObject_GetAttrString(Layer, "add_RX");
2954  PyObject* add_rx_arg_2 = PyTuple_Pack(1, qubit0_obj);
2955  PyObject_CallObject(add_rx_func_2, add_rx_arg_2);
2956  Py_DECREF(add_rx_func_2);
2957  Py_DECREF(add_rx_arg_2);
2958 
2959  Py_DECREF(qubit0_obj);
2960  Py_DECREF(qubit1_obj);
2961 
2962  // Add hardcoded parameters
2963  PyList_Append(optimized_parameters, PyFloat_FromDouble(M_PI / 4.0));
2964  PyList_Append(optimized_parameters, PyFloat_FromDouble(M_PI / 2.0));
2965  PyList_Append(optimized_parameters, PyFloat_FromDouble(-M_PI / 2.0));
2966  PyList_Append(optimized_parameters, PyFloat_FromDouble(-M_PI / 4.0));
2967 
2968  // Add layer to circuit
2969  PyObject* add_circuit_func = PyObject_GetAttrString(Circuit_ret_result, "add_Circuit");
2970  PyObject* add_circuit_args = PyTuple_Pack(1, Layer);
2971  PyObject_CallObject(add_circuit_func, add_circuit_args);
2972  Py_DECREF(add_circuit_func);
2973  Py_DECREF(add_circuit_args);
2974  Py_DECREF(Layer);
2975  }
2976  Py_DECREF(gate_operation_name_attr);
2977  Py_DECREF(gate_operation);
2978  Py_DECREF(gate_qubits);
2979  }
2980 
2981  // Add remaining single qubit gates
2982  PyObject* circuit_module = PyImport_ImportModule("squander.gates.qgd_Circuit");
2983  PyObject* circuit_class = PyObject_GetAttrString(circuit_module, "qgd_Circuit");
2984  Py_DECREF(circuit_module);
2985 
2986  PyObject* final_layer_args = PyTuple_Pack(1, PyLong_FromLong(register_size));
2987  PyObject* final_layer_result = PyObject_CallObject(circuit_class, final_layer_args);
2988  Py_DECREF(circuit_class);
2989  Py_DECREF(final_layer_args);
2990 
2991  for (int qubit = 0; qubit < register_size; qubit++) {
2992  PyObject* gates_list = PyDict_GetItem(single_qubit_gates, PyLong_FromLong(qubit));
2993  Py_ssize_t gates_list_size = PyList_Size(gates_list);
2994 
2995  for (Py_ssize_t j = 0; j < gates_list_size; j++) {
2996  PyObject* gate_obj = PyList_GetItem(gates_list, j);
2997  PyObject* gate_obj_type = PyDict_GetItemString(gate_obj, "type");
2998  const char* gate_obj_type_str = PyUnicode_AsUTF8(gate_obj_type);
2999 
3000  if (strcmp(gate_obj_type_str, "u3") == 0) {
3001  PyObject* add_u3_func = PyObject_GetAttrString(final_layer_result, "add_U3");
3002  PyObject* add_u3_args = Py_BuildValue("(iOOO)", qubit, Py_True, Py_True, Py_True);
3003  PyObject_CallObject(add_u3_func, add_u3_args);
3004  Py_DECREF(add_u3_func);
3005  Py_DECREF(add_u3_args);
3006 
3007  PyObject* gate_obj_params = PyDict_GetItemString(gate_obj, "params");
3008  PyObject* reversed_params = PyList_New(0);
3009  for (Py_ssize_t k = PyList_Size(gate_obj_params) - 1; k >= 0; k--) {
3010  PyList_Append(reversed_params, PyList_GetItem(gate_obj_params, k));
3011  }
3012 
3013  // Convert parameters to float and append
3014  for (Py_ssize_t k = 0; k < PyList_Size(reversed_params); k++) {
3015  PyObject* param = PyList_GetItem(reversed_params, k);
3016  PyObject* param_float = PyFloat_FromDouble(PyFloat_AsDouble(param));
3017  PyList_Append(optimized_parameters, param_float);
3018  Py_DECREF(param_float);
3019  }
3020  Py_DECREF(reversed_params);
3021 
3022  // Divide last parameter by 2
3023  Py_ssize_t optimized_parameters_last_idx = PyList_Size(optimized_parameters) - 1;
3024  PyObject* optimized_parameters_last_param = PyList_GetItem(optimized_parameters, optimized_parameters_last_idx);
3025  double val = PyFloat_AsDouble(optimized_parameters_last_param) / 2.0;
3026  PyList_SetItem(optimized_parameters, optimized_parameters_last_idx, PyFloat_FromDouble(val));
3027  }
3028  }
3029  }
3030 
3031  PyObject* add_final_circuit_func = PyObject_GetAttrString(Circuit_ret_result, "add_Circuit");
3032  PyObject* add_final_circuit_args = PyTuple_Pack(1, final_layer_result);
3033  PyObject_CallObject(add_final_circuit_func, add_final_circuit_args);
3034  Py_DECREF(add_final_circuit_func);
3035  Py_DECREF(add_final_circuit_args);
3036  Py_DECREF(final_layer_result);
3037 
3038  // Convert parameters to numpy array and flip
3039  PyObject* numpy_module = PyImport_ImportModule("numpy");
3040  PyObject* numpy_asarray_func = PyObject_GetAttrString(numpy_module, "asarray");
3041  PyObject* numpy_flip_func = PyObject_GetAttrString(numpy_module, "flip");
3042  Py_DECREF(numpy_module);
3043 
3044  PyObject* dtype_dict = PyDict_New();
3045  PyDict_SetItemString(dtype_dict, "dtype", (PyObject*)&PyFloat_Type);
3046  PyObject* numpy_asarray_args = PyTuple_Pack(1, optimized_parameters);
3047  PyObject* numpy_asarray_result = PyObject_Call(numpy_asarray_func, numpy_asarray_args, dtype_dict);
3048  Py_DECREF(numpy_asarray_func);
3049  Py_DECREF(numpy_asarray_args);
3050  Py_DECREF(dtype_dict);
3051 
3052  PyObject* numpt_flip_args = PyTuple_Pack(2, numpy_asarray_result, PyLong_FromLong(0));
3053  PyObject* numpt_flip_result = PyObject_CallObject(numpy_flip_func, numpt_flip_args);
3054  Py_DECREF(numpy_flip_func);
3055  Py_DECREF(numpt_flip_args);
3056  Py_DECREF(numpy_asarray_result);
3057 
3058  // Set gate structure and parameters
3059  PyObject* set_gate_structure_args = PyTuple_Pack(1, Circuit_ret_result);
3060  PyObject* set_gate_structure_result = qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure(self, set_gate_structure_args);
3061  Py_DECREF(set_gate_structure_args);
3062  Py_DECREF(Circuit_ret_result);
3063  if (!set_gate_structure_result) {
3064  Py_DECREF(numpt_flip_result);
3065  Py_DECREF(optimized_parameters);
3066  Py_DECREF(single_qubit_gates);
3067  Py_DECREF(qc_data_attr);
3068  Py_DECREF(qc_qubits_attr);
3069  Py_DECREF(qc);
3070  return NULL;
3071  }
3072  Py_DECREF(set_gate_structure_result);
3073 
3074  PyObject* set_optimized_params_args = PyTuple_Pack(1, numpt_flip_result);
3075  PyObject* set_optimized_params_result = qgd_N_Qubit_Decomposition_Wrapper_set_Optimized_Parameters(self, set_optimized_params_args);
3076  Py_DECREF(set_optimized_params_args);
3077  Py_DECREF(numpt_flip_result);
3078  Py_DECREF(optimized_parameters);
3079  Py_DECREF(single_qubit_gates);
3080  Py_DECREF(qc_data_attr);
3081  Py_DECREF(qc_qubits_attr);
3082  Py_DECREF(qc);
3083  if (!set_optimized_params_result) {
3084  return NULL;
3085  }
3086  Py_DECREF(set_optimized_params_result);
3087 
3088  Py_RETURN_NONE;
3089 }
3090 
3096 static PyObject*
3098 {
3099  PyObject* qc_in = NULL;
3100  if (!PyArg_ParseTuple(args, "O", &qc_in)) {
3101  return NULL;
3102  }
3103  bool is_adaptive = (dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp) != nullptr);
3104  if (is_adaptive) {
3106  } else {
3108  }
3109 }
3110 
3111 
3112 
3113 
3114 
3116 
3117 extern "C"
3118 {
3119 
3124 #define DECOMPOSITION_WRAPPER_BASE_METHODS \
3125  {"Start_Decomposition", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Start_Decomposition, METH_VARARGS | METH_KEYWORDS, \
3126  "Method to start the decomposition"}, \
3127  {"get_Gate_Num", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Gate_Num, METH_NOARGS, \
3128  "Method to get the number of decomposing gates"}, \
3129  {"get_Optimized_Parameters", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Optimized_Parameters, METH_NOARGS, \
3130  "Method to get the array of optimized parameters"}, \
3131  {"get_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Circuit, METH_NOARGS, \
3132  "Method to get the incorporated circuit"}, \
3133  {"List_Gates", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_List_Gates, METH_NOARGS, \
3134  "Call to print the decomposing unitaries on standard output"}, \
3135  {"set_Max_Layer_Num", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Max_Layer_Num, METH_VARARGS, \
3136  "Set the maximal number of layers used in the subdecomposition"}, \
3137  {"set_Iteration_Loops", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Iteration_Loops, METH_VARARGS, \
3138  "Set the number of iteration loops during the subdecomposition"}, \
3139  {"set_Verbose", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Verbose, METH_VARARGS, \
3140  "Set the verbosity of the decomposition class"}, \
3141  {"set_Debugfile", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Debugfile, METH_VARARGS, \
3142  "Set the debugfile name of the decomposition class"}, \
3143  {"Reorder_Qubits", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Reorder_Qubits, METH_VARARGS, \
3144  "Method to reorder the qubits in the decomposition class"}, \
3145  {"set_Optimization_Tolerance", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Optimization_Tolerance, METH_VARARGS, \
3146  "Wrapper method to set the optimization tolerance"}, \
3147  {"set_Convergence_Threshold", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Convergence_Threshold, METH_VARARGS, \
3148  "Wrapper method to set the threshold of convergence"}, \
3149  {"set_Optimization_Blocks", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Optimization_Blocks, METH_VARARGS, \
3150  "Wrapper method to set the number of gate blocks to be optimized"}, \
3151  {"get_Parameter_Num", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Parameter_Num, METH_NOARGS, \
3152  "Get the number of free parameters"}, \
3153  {"set_Optimized_Parameters", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Optimized_Parameters, METH_VARARGS, \
3154  "Set the optimized parameters"}, \
3155  {"get_Num_of_Iters", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Num_of_Iters, METH_NOARGS, \
3156  "Get the number of iterations"}, \
3157  {"export_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_export_Unitary, METH_VARARGS, \
3158  "Export unitary matrix"}, \
3159  {"get_Project_Name", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Project_Name, METH_NOARGS, \
3160  "Get the name of SQUANDER project"}, \
3161  {"set_Project_Name", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Project_Name, METH_VARARGS, \
3162  "Set the name of SQUANDER project"}, \
3163  {"get_Global_Phase", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Global_Phase, METH_NOARGS, \
3164  "Call to get global phase"}, \
3165  {"set_Global_Phase", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Global_Phase, METH_VARARGS, \
3166  "Set global phase"}, \
3167  {"apply_Global_Phase_Factor", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_apply_Global_Phase_Factor, METH_NOARGS, \
3168  "Apply global phase factor"}, \
3169  {"get_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Unitary, METH_NOARGS, \
3170  "Get Unitary Matrix"}, \
3171  {"set_Optimizer", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Optimizer, METH_VARARGS | METH_KEYWORDS, \
3172  "Set the optimizer method"}, \
3173  {"set_Max_Iterations", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Max_Iterations, METH_VARARGS | METH_KEYWORDS, \
3174  "Set the number of maximum iterations"}, \
3175  {"get_Matrix", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Matrix, METH_VARARGS | METH_KEYWORDS, \
3176  "Method to retrieve the unitary of the circuit"}, \
3177  {"set_Cost_Function_Variant", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Cost_Function_Variant, METH_VARARGS | METH_KEYWORDS, \
3178  "Set the cost function variant"}, \
3179  {"Optimization_Problem", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem, METH_VARARGS, \
3180  "Optimization problem method"}, \
3181  {"Optimization_Problem_Combined_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Combined_Unitary, METH_VARARGS, \
3182  "Optimization problem combined unitary method"}, \
3183  {"Optimization_Problem_Grad", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Grad, METH_VARARGS, \
3184  "Optimization problem gradient method"}, \
3185  {"Optimization_Problem_Combined", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Combined, METH_VARARGS, \
3186  "Optimization problem combined method"}, \
3187  {"Optimization_Problem_Batch", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Batch, METH_VARARGS, \
3188  "Optimization problem batch method"}, \
3189  {"Upload_Umtx_to_DFE", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Upload_Umtx_to_DFE, METH_NOARGS, \
3190  "Upload unitary matrix to DFE"}, \
3191  {"get_Trace_Offset", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Trace_Offset, METH_NOARGS, \
3192  "Get trace offset"}, \
3193  {"set_Trace_Offset", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Trace_Offset, METH_VARARGS | METH_KEYWORDS, \
3194  "Set trace offset"}, \
3195  {"get_Decomposition_Error", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Decomposition_Error, METH_NOARGS, \
3196  "Get decomposition error"}, \
3197  {"get_Second_Renyi_Entropy", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Second_Renyi_Entropy, METH_VARARGS, \
3198  "Get second Renyi entropy"}, \
3199  {"get_Qbit_Num", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Qbit_Num, METH_NOARGS, \
3200  "Get the number of qubits"}, \
3201  {"set_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure, METH_VARARGS, \
3202  "Set custom gate structure for decomposition"}, \
3203  {"add_Finalyzing_Layer_To_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_add_Finalyzing_Layer_To_Gate_Structure, METH_NOARGS, \
3204  "Add finalizing layer to gate structure"}, \
3205  {"get_Gates", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Gates, METH_NOARGS, \
3206  "Get gates as a list of dictionaries"}, \
3207  {"get_Qiskit_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Qiskit_Circuit, METH_NOARGS, \
3208  "Export decomposition to Qiskit QuantumCircuit format"}, \
3209  {"get_Cirq_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Cirq_Circuit, METH_NOARGS, \
3210  "Export decomposition to Cirq Circuit format"}, \
3211  {"import_Qiskit_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_import_Qiskit_Circuit, METH_VARARGS, \
3212  "Import Qiskit QuantumCircuit"}, \
3213 
3214 
3218 static PyMethodDef qgd_N_Qubit_Decomposition_methods[] = {
3220  {"set_Identical_Blocks", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Identical_Blocks, METH_VARARGS,
3221  "Set the number of identical successive blocks during subdecomposition"},
3222  {NULL}
3223 };
3224 
3230  {"get_Initial_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Initial_Circuit, METH_NOARGS,
3231  "Method to get initial circuit in decomposition"},
3232  {"Compress_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Compress_Circuit, METH_NOARGS,
3233  "Method to compress gate structure"},
3234  {"Finalize_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Finalize_Circuit, METH_VARARGS | METH_KEYWORDS,
3235  "Method to finalize the decomposition"},
3236  {"set_Gate_Structure_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure_From_Binary, METH_VARARGS,
3237  "Set gate structure from binary"},
3238  {"add_Gate_Structure_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_add_Gate_Structure_From_Binary, METH_VARARGS,
3239  "Add gate structure from binary"},
3240  {"set_Unitary_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Unitary_From_Binary, METH_VARARGS,
3241  "Set unitary from binary"},
3242  {"add_Adaptive_Layers", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_add_Adaptive_Layers, METH_NOARGS,
3243  "Call to add adaptive layers to the gate structure"},
3244  {"add_Layer_To_Imported_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_add_Layer_To_Imported_Gate_Structure, METH_VARARGS,
3245  "Add layer to imported gate structure"},
3246  {"apply_Imported_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_apply_Imported_Gate_Structure, METH_NOARGS,
3247  "Apply imported gate structure"},
3248  {"set_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Unitary, METH_VARARGS,
3249  "Call to set unitary matrix"},
3250  {NULL}
3251 };
3252 
3258  {NULL}
3259 };
3260 
3266  {"set_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Unitary, METH_VARARGS,
3267  "Call to set unitary matrix"},
3268  {NULL}
3269 };
3270 
3276  {"set_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Unitary, METH_VARARGS,
3277  "Call to set unitary matrix"},
3278  {NULL}
3279 };
3280 
3281 #define decomposition_wrapper_type_template(decomp_class) \
3282 static PyTypeObject qgd_##decomp_class##_Wrapper_Type = { \
3283  PyVarObject_HEAD_INIT(NULL, 0) \
3284  "qgd_N_Qubit_Decomposition_Wrapper." #decomp_class, /* tp_name */ \
3285  sizeof(qgd_N_Qubit_Decomposition_Wrapper), /* tp_basicsize */ \
3286  0, /* tp_itemsize */ \
3287  (destructor) qgd_N_Qubit_Decomposition_Wrapper_dealloc, /* tp_dealloc */ \
3288  0, /* tp_vectorcall_offset */ \
3289  0, /* tp_getattr */ \
3290  0, /* tp_setattr */ \
3291  0, /* tp_as_async */ \
3292  0, /* tp_repr */ \
3293  0, /* tp_as_number */ \
3294  0, /* tp_as_sequence */ \
3295  0, /* tp_as_mapping */ \
3296  0, /* tp_hash */ \
3297  0, /* tp_call */ \
3298  0, /* tp_str */ \
3299  0, /* tp_getattro */ \
3300  0, /* tp_setattro */ \
3301  0, /* tp_as_buffer */ \
3302  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ \
3303  #decomp_class " decomposition wrapper", /* tp_doc */ \
3304  0, /* tp_traverse */ \
3305  0, /* tp_clear */ \
3306  0, /* tp_richcompare */ \
3307  0, /* tp_weaklistoffset */ \
3308  0, /* tp_iter */ \
3309  0, /* tp_iternext */ \
3310  qgd_##decomp_class##_methods, /* tp_methods */ \
3311  0, /* tp_members */ \
3312  0, /* tp_getset */ \
3313  0, /* tp_base */ \
3314  0, /* tp_dict */ \
3315  0, /* tp_descr_get */ \
3316  0, /* tp_descr_set */ \
3317  0, /* tp_dictoffset */ \
3318  (initproc) qgd_##decomp_class##_Wrapper_init, /* tp_init */ \
3319  0, /* tp_alloc */ \
3320  (newfunc) qgd_N_Qubit_Decomposition_Wrapper_new, /* tp_new */ \
3321  0, /* tp_free */ \
3322  0, /* tp_is_gc */ \
3323  0, /* tp_bases */ \
3324  0, /* tp_mro */ \
3325  0, /* tp_cache */ \
3326  0, /* tp_subclasses */ \
3327  0, /* tp_weaklist */ \
3328  0, /* tp_del */ \
3329  0, /* tp_version_tag */ \
3330  0, /* tp_finalize */ \
3331  0, /* tp_vectorcall */ \
3332 };
3333 
3339 
3340 
3345 static PyModuleDef qgd_N_Qubit_Decompositions_Wrapper_Module = {
3346  PyModuleDef_HEAD_INIT,
3347  "qgd_N_Qubit_Decompositions_Wrapper", /* m_name */
3348  "Python binding for N-Qubit Decompositions wrapper module", /* m_doc */
3349  -1, /* m_size */
3350  0, /* m_methods */
3351  0, /* m_slots */
3352  0, /* m_traverse */
3353  0, /* m_clear */
3354  0, /* m_free */
3355 };
3356 
3357 #define Py_INCREF_template(decomp_name) \
3358  Py_INCREF(&qgd_##decomp_name##_Wrapper_Type); \
3359  if (PyModule_AddObject(m, "qgd_" #decomp_name, (PyObject *) &qgd_##decomp_name##_Wrapper_Type) < 0) { \
3360  Py_DECREF(&qgd_##decomp_name##_Wrapper_Type); \
3361  Py_DECREF(m); \
3362  return NULL; \
3363  }
3364 
3368 PyMODINIT_FUNC
3370 {
3371  PyObject *m;
3372 
3373  // initialize numpy
3374  import_array();
3375 
3376  if (PyType_Ready(&qgd_N_Qubit_Decomposition_Wrapper_Type) < 0 ||
3377  PyType_Ready(&qgd_N_Qubit_Decomposition_adaptive_Wrapper_Type) < 0 ||
3378  PyType_Ready(&qgd_N_Qubit_Decomposition_custom_Wrapper_Type) < 0 ||
3379  PyType_Ready(&qgd_N_Qubit_Decomposition_Tree_Search_Wrapper_Type) < 0 ||
3380  PyType_Ready(&qgd_N_Qubit_Decomposition_Tabu_Search_Wrapper_Type) < 0) {
3381  return NULL;
3382  }
3383 
3384  m = PyModule_Create(&qgd_N_Qubit_Decompositions_Wrapper_Module);
3385  if (m == NULL)
3386  return NULL;
3387 
3393 
3394  return m;
3395 }
3396 
3397 } // extern "C"
Gates_block * get_flat_circuit()
Method to generate a flat circuit.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Max_Layer_Num(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Set the maximal number of layers used in the subdecomposition of the qbit-th qubit.
string project_name
Definition: test_Groq.py:98
parameter_num
[set adaptive gate structure]
Class to store single-precision real arrays and properties.
Matrix_float to_float32() const
Convert to single precision.
Definition: matrix.cpp:32
void release_decomposition(DecompT *instance)
Deallocate decomposition instance.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_List_Gates(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to list the gates decomposing the unitary.
void add_adaptive_layers()
Call to add adaptive layers to the gate structure stored by the class.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Optimization_Blocks(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Wrapper method to set the number of gate blocks to be optimized.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_apply_Global_Phase_Factor(qgd_N_Qubit_Decomposition_Wrapper *self)
Apply global phase factor to the unitary matrix.
int stride
The column stride of the array. (The array elements in one row are a_0, a_1, ... a_{cols-1}, 0, 0, 0, 0. The number of zeros is stride-cols)
Definition: matrix_base.hpp:46
Matrix to_float64() const
Convert to double precision.
Definition: matrix_float.cpp:8
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
key
Definition: noise.py:86
return Py_BuildValue("i", 0)
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_import_Qiskit_Circuit_adaptive(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *qc_in)
Method to import Qiskit circuit (adaptive-specific version with custom CZ decomposition) ...
Matrix_real numpy2matrix_real(PyArrayObject *arr)
Call to create a PIC matrix_real representation of a numpy array.
static int qgd_N_Qubit_Decomposition_custom_Wrapper_init(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
#define DECOMPOSITION_WRAPPER_BASE_METHODS
Base methods shared by all decomposition types These methods are available for all decomposition clas...
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Num_of_Iters(qgd_N_Qubit_Decomposition_Wrapper *self)
Get the number of free parameters in the gate structure used for the decomposition.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_add_Gate_Structure_From_Binary(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Wrapper function to append custom layers to the gate structure from binary file.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Optimized_Parameters(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Extract the optimized parameters.
PyMODINIT_FUNC PyInit_qgd_N_Qubit_Decompositions_Wrapper(void)
Method called when the Python module is initialized.
Header file for a class responsible for grouping gates into subcircuits. (Subcircuits can be nested) ...
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Call to evaluate the optimization problem (cost function)
PyObject * matrix_real_to_numpy(Matrix_real &mtx)
Call to make a numpy array from an instance of matrix class.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Iteration_Loops(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Set the number of iteration loops during the subdecomposition of the qbit-th qubit.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Unitary(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to get the unitary matrix.
U3 RX RZ H Y SX S T CNOT CH SYC CRZ PyObject PyObject * kwds
A class describing a universal configuration element.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Trace_Offset(qgd_N_Qubit_Decomposition_Wrapper *self)
Get trace offset of the compression.
#define Py_INCREF_template(decomp_name)
Optimization_Interface * decomp
An object to decompose the unitary.
Matrix_real_float numpy2matrix_real_float(PyArrayObject *arr)
Call to create a PIC matrix_real_float representation of a numpy array.
Matrix_real parameters_float_to_double(Matrix_real_float &parameters32)
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
scalar * get_data() const
Call to get the pointer to the stored data.
static int qgd_N_Qubit_Decomposition_adaptive_Wrapper_init(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
guess_type extract_guess_type(PyObject *initial_guess)
Extract guess_type from Python string/object.
static PyMethodDef qgd_N_Qubit_Decomposition_Tree_Search_methods[]
Method table for N_Qubit_Decomposition_Tree_Search.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Global_Phase(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to get the global phase factor (returns the angle of the global phase)
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_add_Layer_To_Imported_Gate_Structure(qgd_N_Qubit_Decomposition_Wrapper *self)
Wrapper method to add layer to imported gate structure.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Project_Name(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Call to set the project name.
A class representing a CZ operation.
Definition: CZ.h:36
optimization_aglorithms
implemented optimization strategies
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Wrapper function to set custom gate structure for the decomposition.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Trace_Offset(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
Set trace offset for the compression.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Identical_Blocks(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Set the number of identical successive blocks (N_Qubit_Decomposition only)
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Matrix(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
Call to get the matrix representation of the circuit with given parameters.
static int qgd_N_Qubit_Decomposition_Tree_Search_Wrapper_init(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
U3 RX RZ H Y SX S T CNOT CH SYC CRZ PyObject * args
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
PyObject_HEAD Gates_block * gate
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Start_Decomposition(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
Wrapper function to call the start_decomposition method of C++ class N_Qubit_Decomposition.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_add_Finalyzing_Layer_To_Gate_Structure(qgd_N_Qubit_Decomposition_Wrapper *self)
Wrapper function to add finalyzing layer (single qubit rotations on all qubits) to the gate structure...
void apply_imported_gate_structure()
Call to apply the imported gate structure on the unitary.
void set_custom_gate_structure(std::map< int, Gates_block *> gate_structure_in)
Call to set custom layers to the gate structure that are intended to be used in the subdecomposition...
void set_unitary_from_file(std::string filename)
Set unitary matrix from file.
PyObject * matrix_real_float_to_numpy(Matrix_real_float &mtx)
Call to make a numpy array from an instance of matrix_real_float class.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_import_Qiskit_Circuit_standard(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *qc_in)
Method to import Qiskit circuit (standard version for non-adaptive decompositions) ...
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Finalize_Circuit(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
Call to finalize circuit.
void set_adaptive_gate_structure(std::string filename)
Call to set custom layers to the gate structure that are intended to be used in the decomposition...
#define M_PI
Definition: qgd_math.h:42
static int qgd_N_Qubit_Decomposition_Wrapper_init(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
static PyMethodDef qgd_N_Qubit_Decomposition_Tabu_Search_methods[]
Method table for N_Qubit_Decomposition_Tabu_Search.
std::map< std::string, Config_Element > extract_config(PyObject *config_arg)
Extract config dictionary.
gate_type get_type()
Call to get the type of the operation.
Definition: Gate.cpp:1333
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Gate_Num(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to get the number of gates.
Umtx
The unitary to be decomposed.
Definition: example.py:53
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Grad(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Call to evaluate the gradient of the optimization problem.
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
Header file for a class implementing the adaptive gate decomposition algorithm of arXiv:2203...
static PyMethodDef qgd_N_Qubit_Decomposition_adaptive_methods[]
Method table for N_Qubit_Decomposition_adaptive.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Initial_Circuit(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to get initial circuit.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Combined_Unitary(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Call to evaluate the optimization problem with unitary and derivatives.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Global_Phase(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Call to set the global phase.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Reorder_Qubits(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Method to reorder the qubits in the decomposition class.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Cirq_Circuit(qgd_N_Qubit_Decomposition_Wrapper *self)
int get_parameter_start_idx()
Call to get the starting index of the parameters in the parameter array corresponding to the circuit ...
Definition: Gate.cpp:2535
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Max_Iterations(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Set the number of maximum iterations for optimization.
static int search_wrapper_init(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
virtual void compress_circuit()
Compress the circuit.
#define CIRQ_ADD_TWO_QUBIT_GATE(name)
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...
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Unitary_From_Binary(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Wrapper function to set unitary from binary file.
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
A class representing a CNOT operation.
Definition: CNOT.h:35
void add_adaptive_gate_structure(std::string filename)
Call to append custom layers to the gate structure that are intended to be used in the decomposition...
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_apply_Imported_Gate_Structure(qgd_N_Qubit_Decomposition_Wrapper *self)
Wrapper function to apply the imported gate structure on the unitary.
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
virtual void get_initial_circuit()
get initial circuit
Matrix copy() const
Call to create a copy of the matrix.
Definition: matrix.h:57
PyObject_HEAD PyArrayObject * Umtx
pointer to the unitary to be decomposed to keep it alive
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Project_Name(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to get the project name.
static void qgd_N_Qubit_Decomposition_Wrapper_dealloc(qgd_N_Qubit_Decomposition_Wrapper *self)
Called when Python object is destroyed.
Double-precision complex matrix (float64).
Definition: matrix.h:38
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Cost_Function_Variant(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
Call to set the cost function variant.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Debugfile(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Set the debugfile name of the decomposition class.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Combined(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Call to evaluate the optimization problem with cost and gradient combined.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Circuit(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to get the incorporated circuit.
dictionary config
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.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
Allocate memory for new Python object.
virtual int get_parameter_num()
Call to get the number of free parameters.
Definition: Gate.cpp:1324
cost_function_type
Type definition of the different types of the cost function.
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:44
Header file for a class implementing the adaptive gate decomposition algorithm of arXiv:2203...
virtual void finalize_circuit()
Finalize the circuit.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Qiskit_Circuit(qgd_N_Qubit_Decomposition_Wrapper *self)
Method to get Qiskit circuit representation.
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
guess_type
Type definition of the types of the initial guess.
void add_layer_to_imported_gate_structure()
Call to add an adaptive layer to the gate structure previously imported gate structure.
#define CIRQ_ADD_SINGLE_QUBIT_GATE(name)
Method to get Cirq circuit representation.
int get_target_qbit()
Call to get the index of the target qubit.
Definition: Gate.cpp:1203
static bool config_requests_float(std::map< std::string, Config_Element > &config)
void set_property(std::string name_, double val_)
Call to set a double value.
static int qgd_N_Qubit_Decomposition_Tabu_Search_Wrapper_init(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
Base class for the representation of general gate operations.
Definition: Gate.h:86
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Optimization_Tolerance(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Wrapper method to set the optimization tolerance.
PyObject * matrix_float_to_numpy(Matrix_float &mtx)
Call to make a numpy array from an instance of matrix_float class.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Decomposition_Error(qgd_N_Qubit_Decomposition_Wrapper *self)
Get the error of the decomposition.
std::vector< matrix_base< int > > extract_topology(PyObject *topology)
Extract topology list from Python.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Compress_Circuit(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to compress circuit.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Verbose(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Set the verbosity of the decomposition class.
Matrix numpy2matrix(PyArrayObject *arr)
Call to create a PIC matrix representation of a numpy array.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Gates(qgd_N_Qubit_Decomposition_Wrapper *self)
Method to get gates as a list of dictionaries (with parameters from optimized_parameters array) ...
PyObject_HEAD Gates_block * circuit
Pointer to the C++ class of the base Gate_block module.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_add_Adaptive_Layers(qgd_N_Qubit_Decomposition_Wrapper *self)
Wrapper method to add adaptive layers to the gate structure stored by the class.
double real
the real part of a complex number
Definition: QGDTypes.h:40
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_export_Unitary(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Export unitary matrix to binary file.
Header file for a class implementing the adaptive gate decomposition algorithm of arXiv:2203...
dictionary iteration_loops
#define CIRQ_ADD_ROTATION_GATE(name, param)
#define decomposition_wrapper_type_template(decomp_class)
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Second_Renyi_Entropy(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Get second Renyi entropy.
int get_qbit_num()
Call to get the number of qubits composing the unitary.
Definition: Gate.cpp:1342
void extract_parameters_any(PyObject *parameters_arg, PyArrayObject **store_ref, Matrix_real &parameters64, Matrix_real_float &parameters32, bool &is_float32)
Extract real float32/float64 parameters without forced precision conversion.
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.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Optimizer(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args, PyObject *kwds)
Call to set the optimizer algorithm.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Parameter_Num(qgd_N_Qubit_Decomposition_Wrapper *self)
Get the number of free parameters in the gate structure used for the decomposition.
static PyMethodDef qgd_N_Qubit_Decomposition_custom_methods[]
Method table for N_Qubit_Decomposition_custom.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Convergence_Threshold(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Wrapper method to set the threshold of convergence.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Optimized_Parameters(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to get the optimized parameters.
Header file for a class to determine the decomposition of an N-qubit unitary into a sequence of CNOT ...
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_import_Qiskit_Circuit(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Method to import Qiskit circuit.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure_From_Binary(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Wrapper function to set custom layers to the gate structure that are intended to be used in the decom...
Matrix extract_matrix(PyObject *Umtx_arg, PyArrayObject **store_ref)
Extract and validate Matrix from numpy array.
int set_identical_blocks(int n, int identical_blocks_in)
Set the number of identical successive blocks during the subdecomposition of the n-th qubit...
PyObject * matrix_to_numpy(Matrix &mtx)
Call to make a numpy array from an instance of matrix class.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Batch(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Call to evaluate the optimization problem for batched parameters.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Upload_Umtx_to_DFE(qgd_N_Qubit_Decomposition_Wrapper *self)
Upload unitary matrix to DFE.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_get_Qbit_Num(qgd_N_Qubit_Decomposition_Wrapper *self)
Get the number of qubits.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_set_Unitary(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Call to set unitary matrix.
Type definition of the unified N-Qubit Decomposition wrapper.
Matrix_float copy() const
Call to create a copy of the matrix.
Definition: matrix_float.h:60
qc
Definition: noise.py:8
static PyMethodDef qgd_N_Qubit_Decomposition_methods[]
Method table for base N_Qubit_Decomposition.
int get_control_qbit()
Call to get the index of the control qubit.
Definition: Gate.cpp:1211
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:41
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
void extract_matrix_any(PyObject *matrix_arg, PyArrayObject **store_ref, Matrix &matrix64, Matrix_float &matrix32, bool &is_float32)
Extract complex64/complex128 numpy input without rejecting float32 callers.
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42