Sequential Quantum Gate Decomposer  v1.9.7
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 = NULL;
903 
904  // parsing input arguments: circuit, parameters
905  if (!PyArg_ParseTuple(args, "|O", &gate_structure_py)) {
906  return Py_BuildValue("i", -1);
907  }
908 
909  if (gate_structure_py == NULL) {
910  PyErr_SetString(PyExc_TypeError, "set_Gate_Structure requires a circuit argument");
911  return NULL;
912  }
913 
914  // Check if input is a dictionary (map<int, Gates_block*> version: N_Qubit_Decomposition ONLY)
915  if (PyDict_Check(gate_structure_py)) {
916  PyObject *key = NULL, *value = NULL;
917  Py_ssize_t pos = 0;
918  std::map<int, Gates_block*> gate_structure;
919 
920  while (PyDict_Next(gate_structure_py, &pos, &key, &value)) {
921  // convert key from PyObject to int
922  if (!PyLong_Check(key)) {
923  PyErr_SetString(PyExc_TypeError, "Dictionary keys must be integers");
924  return NULL;
925  }
926  int key_int = (int)PyLong_AsLong(key);
927  // convert value from PyObject to qgd_Circuit_Wrapper
928  qgd_Circuit_Wrapper* qgd_op_block = (qgd_Circuit_Wrapper*)value;
929  gate_structure.insert(std::pair<int, Gates_block*>(key_int, qgd_op_block->gate));
930  }
931 
932  // The map version is only available in base N_Qubit_Decomposition class
933  N_Qubit_Decomposition* base_decomp = dynamic_cast<N_Qubit_Decomposition*>(self->decomp);
934  if (base_decomp != NULL) {
935  try {
936  base_decomp->set_custom_gate_structure(gate_structure);
937  return Py_BuildValue("i", 0);
938  } catch (std::string err) {
939  PyErr_SetString(PyExc_Exception, err.c_str());
940  return NULL;
941  } catch (std::exception& e) {
942  PyErr_SetString(PyExc_Exception, e.what());
943  return NULL;
944  } catch (...) {
945  std::string err("Invalid pointer to decomposition class");
946  PyErr_SetString(PyExc_Exception, err.c_str());
947  return NULL;
948  }
949  }
950  PyErr_SetString(PyExc_AttributeError, "Dictionary-based set_Gate_Structure is only available for N_Qubit_Decomposition");
951  return NULL;
952  }
953 
954  qgd_Circuit_Wrapper* qgd_op_block = (qgd_Circuit_Wrapper*)gate_structure_py;
955  try {
956  self->decomp->set_custom_gate_structure(qgd_op_block->gate);
957  return Py_BuildValue("i", 0);
958  } catch (std::string err) {
959  PyErr_SetString(PyExc_Exception, err.c_str());
960  return NULL;
961  } catch (std::exception& e) {
962  PyErr_SetString(PyExc_Exception, e.what());
963  return NULL;
964  } catch (...) {
965  std::string err("Invalid pointer to decomposition class");
966  PyErr_SetString(PyExc_Exception, err.c_str());
967  return NULL;
968  }
969 }
970 
975 static PyObject *
977 {
978  int parameter_num = self->decomp->get_parameter_num();
979  return Py_BuildValue("i", parameter_num);
980 }
981 
987 static PyObject *
989 {
990  PyObject* parameters_obj = NULL;
991  PyArrayObject* parameters_arr = NULL;
992  // parsing input arguments
993  if (!PyArg_ParseTuple(args, "|O", &parameters_obj )) {
994  return Py_BuildValue("i", -1);
995  }
996 
997  Matrix_real parameters_mtx;
998  Matrix_real_float parameters_mtx_float;
999  bool parameters_is_float32 = false;
1000  try {
1001  extract_parameters_any(parameters_obj, &parameters_arr, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1002  if (parameters_is_float32) {
1003  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1004  }
1005  self->decomp->set_optimized_parameters(parameters_mtx.get_data(), parameters_mtx.size());
1006  }
1007  catch (std::string err ) {
1008  PyErr_SetString(PyExc_Exception, err.c_str());
1009  return NULL;
1010  }
1011  catch(...) {
1012  std::string err( "Invalid pointer to decomposition class");
1013  PyErr_SetString(PyExc_Exception, err.c_str());
1014  return NULL;
1015  }
1016  Py_DECREF(parameters_arr);
1017  return Py_BuildValue("i", 0);
1018 }
1019 
1025 static PyObject *
1027 {
1028  int number_of_iters = self->decomp->get_num_iters();
1029  return Py_BuildValue("i", number_of_iters);
1030 }
1031 
1038 static PyObject *
1040 {
1041  // initiate variables for input arguments
1042  PyObject* filename = NULL;
1043  // parsing input arguments
1044  if (!PyArg_ParseTuple(args, "|O", &filename)) {
1045  return Py_BuildValue("i", -1);
1046  }
1047  PyObject* filename_string = PyObject_Str(filename);
1048  PyObject* filename_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
1049  const char* filename_C = PyBytes_AS_STRING(filename_unicode);
1050  std::string filename_str(filename_C);
1051  // export unitary to file
1052  self->decomp->export_unitary(filename_str);
1053  return Py_BuildValue("i", 0);
1054 }
1055 
1062 static PyObject *
1064 {
1065  PyObject* filename = NULL;
1066  if (!PyArg_ParseTuple(args, "|O", &filename) || filename == NULL) {
1067  PyErr_SetString(PyExc_TypeError, "export_Gate_Structure_to_Binary requires a filename argument");
1068  return NULL;
1069  }
1070 
1071  PyObject* filename_string = PyObject_Str(filename);
1072  if (filename_string == NULL) {
1073  return NULL;
1074  }
1075  PyObject* filename_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
1076  Py_DECREF(filename_string);
1077  if (filename_unicode == NULL) {
1078  return NULL;
1079  }
1080  const char* filename_C = PyBytes_AS_STRING(filename_unicode);
1081  std::string filename_str(filename_C);
1082 
1083  try {
1084  Matrix_real parameters_mtx = self->decomp->get_optimized_parameters();
1085  export_gate_list_to_binary(parameters_mtx, static_cast<Gates_block*>(self->decomp), filename_str, self->decomp->verbose);
1086  }
1087  catch (std::string err) {
1088  Py_DECREF(filename_unicode);
1089  PyErr_SetString(PyExc_Exception, err.c_str());
1090  return NULL;
1091  }
1092  catch (std::exception& e) {
1093  Py_DECREF(filename_unicode);
1094  PyErr_SetString(PyExc_Exception, e.what());
1095  return NULL;
1096  }
1097  catch (...) {
1098  Py_DECREF(filename_unicode);
1099  PyErr_SetString(PyExc_Exception, "export_Gate_Structure_to_Binary: failed to export circuit");
1100  return NULL;
1101  }
1102 
1103  Py_DECREF(filename_unicode);
1104  return Py_BuildValue("i", 0);
1105 }
1106 
1112 static PyObject *
1114 {
1115  try {
1116  std::string project_name = self->decomp->get_project_name();
1117  return PyUnicode_FromString(project_name.c_str());
1118  } catch (std::exception& e) {
1119  PyErr_SetString(PyExc_Exception, e.what());
1120  return NULL;
1121  }
1122 }
1123 
1130 static PyObject *
1132 {
1133  // initiate variables for input arguments
1134  PyObject* project_name_new = NULL;
1135  // parsing input arguments
1136  if (!PyArg_ParseTuple(args, "|O", &project_name_new)) {
1137  return Py_BuildValue("i", -1);
1138  }
1139  PyObject* project_name_new_string = PyObject_Str(project_name_new);
1140  PyObject* project_name_new_unicode = PyUnicode_AsEncodedString(project_name_new_string, "utf-8", "~E~");
1141  const char* project_name_new_C = PyBytes_AS_STRING(project_name_new_unicode);
1142  std::string project_name_new_str(project_name_new_C);
1143  // set the project name
1144  self->decomp->set_project_name(project_name_new_str);
1145  return Py_BuildValue("i", 0);
1146 }
1147 
1153 static PyObject *
1155 {
1156  QGD_Complex16 global_phase_factor_C = self->decomp->get_global_phase_factor();
1157  PyObject* global_phase = PyFloat_FromDouble(std::atan2(global_phase_factor_C.imag, global_phase_factor_C.real));
1158  return global_phase;
1159 }
1160 
1167 static PyObject *
1169 {
1170  double phase_angle;
1171  if (!PyArg_ParseTuple(args, "d", &phase_angle)) {
1172  return Py_BuildValue("i", -1);
1173  }
1174  try {
1175  self->decomp->set_global_phase(phase_angle);
1176  Py_RETURN_NONE;
1177  } catch (std::exception& e) {
1178  PyErr_SetString(PyExc_Exception, e.what());
1179  return NULL;
1180  }
1181 }
1182 
1188 static PyObject *
1190 {
1191  try {
1192  self->decomp->apply_global_phase_factor();
1193  Py_RETURN_NONE;
1194  } catch (std::exception& e) {
1195  PyErr_SetString(PyExc_Exception, e.what());
1196  return NULL;
1197  }
1198 }
1199 
1205 static PyObject *
1207 {
1208  if (self->decomp->get_use_float()) {
1209  Matrix_float Unitary_mtx;
1210  try {
1211  Unitary_mtx = self->decomp->get_Umtx_float().copy();
1212  }
1213  catch (std::string err) {
1214  PyErr_SetString(PyExc_Exception, err.c_str());
1215  return NULL;
1216  }
1217  catch (...) {
1218  std::string err("Invalid pointer to decomposition class");
1219  PyErr_SetString(PyExc_Exception, err.c_str());
1220  return NULL;
1221  }
1222  Unitary_mtx.set_owner(false);
1223  return matrix_float_to_numpy(Unitary_mtx);
1224  }
1225 
1226  Matrix Unitary_mtx;
1227  try {
1228  Unitary_mtx = self->decomp->get_Umtx().copy();
1229  }
1230  catch (std::string err) {
1231  PyErr_SetString(PyExc_Exception, err.c_str());
1232  return NULL;
1233  }
1234  catch (...) {
1235  std::string err("Invalid pointer to decomposition class");
1236  PyErr_SetString(PyExc_Exception, err.c_str());
1237  return NULL;
1238  }
1239  // convert to numpy array
1240  Unitary_mtx.set_owner(false);
1241  PyObject *Unitary_py = matrix_to_numpy(Unitary_mtx);
1242  return Unitary_py;
1243 }
1244 
1252 static PyObject *
1254 {
1255  // The tuple of expected keywords
1256  static char *kwlist[] = {(char*)"optimizer", NULL};
1257 
1258  PyObject* optimizer_arg = NULL;
1259 
1260  // parsing input arguments
1261  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &optimizer_arg)) {
1262  std::string err("Unsuccessful argument parsing");
1263  PyErr_SetString(PyExc_Exception, err.c_str());
1264  return NULL;
1265  }
1266 
1267  if (optimizer_arg == NULL) {
1268  std::string err("optimizer argument not set");
1269  PyErr_SetString(PyExc_Exception, err.c_str());
1270  return NULL;
1271  }
1272 
1273  PyObject* optimizer_string = PyObject_Str(optimizer_arg);
1274  PyObject* optimizer_string_unicode = PyUnicode_AsEncodedString(optimizer_string, "utf-8", "~E~");
1275  const char* optimizer_C = PyBytes_AS_STRING(optimizer_string_unicode);
1276 
1277  optimization_aglorithms qgd_optimizer;
1278  if (strcmp("bfgs", optimizer_C) == 0 || strcmp("BFGS", optimizer_C) == 0) {
1279  qgd_optimizer = BFGS;
1280  }
1281  else if (strcmp("adam", optimizer_C) == 0 || strcmp("ADAM", optimizer_C) == 0) {
1282  qgd_optimizer = ADAM;
1283  }
1284  else if (strcmp("grad_descend", optimizer_C) == 0 || strcmp("GRAD_DESCEND", optimizer_C) == 0) {
1285  qgd_optimizer = GRAD_DESCEND;
1286  }
1287  else if (strcmp("adam_batched", optimizer_C) == 0 || strcmp("ADAM_BATCHED", optimizer_C) == 0) {
1288  qgd_optimizer = ADAM_BATCHED;
1289  }
1290  else if (strcmp("bfgs2", optimizer_C) == 0 || strcmp("BFGS2", optimizer_C) == 0) {
1291  qgd_optimizer = BFGS2;
1292  }
1293  else if (strcmp("agents", optimizer_C) == 0 || strcmp("AGENTS", optimizer_C) == 0) {
1294  qgd_optimizer = AGENTS;
1295  }
1296  else if (strcmp("cosine", optimizer_C) == 0 || strcmp("COSINE", optimizer_C) == 0) {
1297  qgd_optimizer = COSINE;
1298  }
1299  else if (strcmp("grad_descend_phase_shift_rule", optimizer_C) == 0 || strcmp("GRAD_DESCEND_PARAMETER_SHIFT_RULE", optimizer_C) == 0) {
1300  qgd_optimizer = GRAD_DESCEND_PARAMETER_SHIFT_RULE;
1301  }
1302  else if (strcmp("agents_combined", optimizer_C) == 0 || strcmp("AGENTS_COMBINED", optimizer_C) == 0) {
1303  qgd_optimizer = AGENTS_COMBINED;
1304  }
1305  else if (strcmp("bayes_opt", optimizer_C) == 0 || strcmp("BAYES_OPT", optimizer_C) == 0) {
1306  qgd_optimizer = BAYES_OPT;
1307  }
1308  else {
1309  std::cout << "Wrong optimizer: " << optimizer_C << ". Using default: BFGS" << std::endl;
1310  qgd_optimizer = BFGS;
1311  }
1312 
1313  try {
1314  self->decomp->set_optimizer(qgd_optimizer);
1315  }
1316  catch (std::string err) {
1317  PyErr_SetString(PyExc_Exception, err.c_str());
1318  std::cout << err << std::endl;
1319  return NULL;
1320  }
1321  catch(...) {
1322  std::string err("Invalid pointer to decomposition class");
1323  PyErr_SetString(PyExc_Exception, err.c_str());
1324  return NULL;
1325  }
1326  return Py_BuildValue("i", 0);
1327 }
1328 
1335 static PyObject *
1337 {
1338  int max_iterations;
1339  if (!PyArg_ParseTuple(args, "i", &max_iterations)) {
1340  return Py_BuildValue("i", -1);
1341  }
1342  try {
1343  self->decomp->set_max_inner_iterations(max_iterations);
1344  Py_RETURN_NONE;
1345  } catch (std::exception& e) {
1346  PyErr_SetString(PyExc_Exception, e.what());
1347  return NULL;
1348  }
1349 }
1350 
1358 static PyObject *
1360 {
1361  PyObject* parameters_obj = NULL;
1362  PyArrayObject* parameters_arr = NULL;
1363 
1364  // parsing input arguments
1365  if (!PyArg_ParseTuple(args, "|O", &parameters_obj))
1366  return Py_BuildValue("i", -1);
1367 
1368  Matrix_real parameters_mtx;
1369  Matrix_real_float parameters_mtx_float;
1370  bool parameters_is_float32 = false;
1371  try {
1372  extract_parameters_any(parameters_obj, &parameters_arr, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1373  }
1374  catch (std::exception& e) {
1375  PyErr_SetString(PyExc_Exception, e.what());
1376  return NULL;
1377  }
1378 
1379  PyObject *unitary_py = NULL;
1380  if (parameters_is_float32 || self->decomp->get_use_float()) {
1381  if (!parameters_is_float32) {
1382  parameters_mtx_float = Matrix_real_float(parameters_mtx.rows, parameters_mtx.cols, parameters_mtx.stride);
1383  for (int row=0; row<parameters_mtx.rows; row++) {
1384  for (int col=0; col<parameters_mtx.cols; col++) {
1385  int idx = row*parameters_mtx.stride + col;
1386  parameters_mtx_float[idx] = static_cast<float>(parameters_mtx[idx]);
1387  }
1388  }
1389  }
1390  Matrix_float unitary_mtx = self->decomp->get_matrix(parameters_mtx_float);
1391  unitary_mtx.set_owner(false);
1392  unitary_py = matrix_float_to_numpy(unitary_mtx);
1393  }
1394  else {
1395  Matrix unitary_mtx = self->decomp->get_matrix(parameters_mtx);
1396  unitary_mtx.set_owner(false);
1397  unitary_py = matrix_to_numpy(unitary_mtx);
1398  }
1399 
1400  Py_DECREF(parameters_arr);
1401 
1402  return unitary_py;
1403 }
1404 
1412 static PyObject *
1414 {
1415  // The tuple of expected keywords
1416  static char *kwlist[] = {(char*)"costfnc", NULL};
1417 
1418  int costfnc_arg = 0;
1419 
1420  // parsing input arguments
1421  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &costfnc_arg)) {
1422  std::string err("Unsuccessful argument parsing");
1423  PyErr_SetString(PyExc_Exception, err.c_str());
1424  return NULL;
1425  }
1426 
1427  cost_function_type qgd_costfnc = (cost_function_type)costfnc_arg;
1428 
1429  try {
1430  self->decomp->set_cost_function_variant(qgd_costfnc);
1431  }
1432  catch (std::string err) {
1433  PyErr_SetString(PyExc_Exception, err.c_str());
1434  std::cout << err << std::endl;
1435  return NULL;
1436  }
1437  catch(...) {
1438  std::string err("Invalid pointer to decomposition class");
1439  PyErr_SetString(PyExc_Exception, err.c_str());
1440  return NULL;
1441  }
1442  return Py_BuildValue("i", 0);
1443 }
1444 
1451 static PyObject *
1453 {
1454  PyObject* parameters_obj = NULL;
1455  PyArrayObject* parameters_arg = NULL;
1456 
1457  // parsing input arguments
1458  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1459  std::string err("Unsuccessful argument parsing not ");
1460  PyErr_SetString(PyExc_Exception, err.c_str());
1461  return NULL;
1462  }
1463 
1464  Matrix_real parameters_mtx;
1465  Matrix_real_float parameters_mtx_float;
1466  bool parameters_is_float32 = false;
1467  double f0;
1468 
1469  try {
1470  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1471  if (parameters_is_float32) {
1472  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1473  }
1474  f0 = self->decomp->optimization_problem(parameters_mtx);
1475  }
1476  catch (std::exception& e) {
1477  PyErr_SetString(PyExc_Exception, e.what());
1478  return NULL;
1479  }
1480  catch (std::string err) {
1481  PyErr_SetString(PyExc_Exception, err.c_str());
1482  return NULL;
1483  }
1484  catch (...) {
1485  std::string err("Invalid pointer to decomposition class");
1486  PyErr_SetString(PyExc_Exception, err.c_str());
1487  return NULL;
1488  }
1489 
1490  Py_DECREF(parameters_arg);
1491 
1492  return Py_BuildValue("d", f0);
1493 }
1494 
1501 static PyObject *
1503 {
1504  PyObject* parameters_obj = NULL;
1505  PyArrayObject* parameters_arg = NULL;
1506 
1507  // parsing input arguments
1508  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1509  std::string err("Unsuccessful argument parsing not ");
1510  PyErr_SetString(PyExc_Exception, err.c_str());
1511  return NULL;
1512  }
1513 
1514  Matrix_real parameters_mtx;
1515  Matrix_real_float parameters_mtx_float;
1516  bool parameters_is_float32 = false;
1517  Matrix Umtx;
1518  std::vector<Matrix> Umtx_deriv;
1519 
1520  try {
1521  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1522  if (parameters_is_float32) {
1523  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1524  }
1525  self->decomp->optimization_problem_combined_unitary(parameters_mtx, Umtx, Umtx_deriv);
1526  }
1527  catch (std::exception& e) {
1528  PyErr_SetString(PyExc_Exception, e.what());
1529  return NULL;
1530  }
1531  catch (std::string err) {
1532  PyErr_SetString(PyExc_Exception, err.c_str());
1533  return NULL;
1534  }
1535  catch (...) {
1536  std::string err("Invalid pointer to decomposition class");
1537  PyErr_SetString(PyExc_Exception, err.c_str());
1538  return NULL;
1539  }
1540 
1541  // convert to numpy array
1542  Umtx.set_owner(false);
1543  PyObject *unitary_py = matrix_to_numpy(Umtx);
1544  PyObject* graduni_py = PyList_New(Umtx_deriv.size());
1545  for (size_t i = 0; i < Umtx_deriv.size(); i++) {
1546  Umtx_deriv[i].set_owner(false);
1547  PyList_SetItem(graduni_py, i, matrix_to_numpy(Umtx_deriv[i]));
1548  }
1549 
1550  Py_DECREF(parameters_arg);
1551 
1552  PyObject* p = Py_BuildValue("(OO)", unitary_py, graduni_py);
1553  Py_DECREF(unitary_py);
1554  Py_DECREF(graduni_py);
1555  return p;
1556 }
1557 
1564 static PyObject *
1566 {
1567  PyObject* parameters_obj = NULL;
1568  PyArrayObject* parameters_arg = NULL;
1569 
1570  // parsing input arguments
1571  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1572  std::string err("Unsuccessful argument parsing not ");
1573  PyErr_SetString(PyExc_Exception, err.c_str());
1574  return NULL;
1575  }
1576 
1577  Matrix_real parameters_mtx;
1578  Matrix_real_float parameters_mtx_float;
1579  bool parameters_is_float32 = false;
1580  Matrix_real grad_mtx(parameters_mtx.size(), 1);
1581 
1582  try {
1583  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1584  if (parameters_is_float32) {
1585  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1586  }
1587  grad_mtx = Matrix_real(parameters_mtx.size(), 1);
1588  self->decomp->optimization_problem_grad(parameters_mtx, self->decomp, grad_mtx);
1589  }
1590  catch (std::exception& e) {
1591  PyErr_SetString(PyExc_Exception, e.what());
1592  return NULL;
1593  }
1594  catch (std::string err) {
1595  PyErr_SetString(PyExc_Exception, err.c_str());
1596  return NULL;
1597  }
1598  catch (...) {
1599  std::string err("Invalid pointer to decomposition class");
1600  PyErr_SetString(PyExc_Exception, err.c_str());
1601  return NULL;
1602  }
1603 
1604  // convert to numpy array
1605  PyObject *grad_py = NULL;
1606  if (parameters_is_float32 || self->decomp->get_use_float()) {
1607  Matrix_real_float grad_float(grad_mtx.rows, grad_mtx.cols, grad_mtx.stride);
1608  for (int row=0; row<grad_mtx.rows; row++) {
1609  for (int col=0; col<grad_mtx.cols; col++) {
1610  int idx = row*grad_mtx.stride + col;
1611  grad_float[idx] = static_cast<float>(grad_mtx[idx]);
1612  }
1613  }
1614  grad_float.set_owner(false);
1615  grad_py = matrix_real_float_to_numpy(grad_float);
1616  }
1617  else {
1618  grad_mtx.set_owner(false);
1619  grad_py = matrix_real_to_numpy(grad_mtx);
1620  }
1621 
1622  Py_DECREF(parameters_arg);
1623 
1624  return grad_py;
1625 }
1626 
1633 static PyObject *
1635 {
1636  PyObject* parameters_obj = NULL;
1637  PyArrayObject* parameters_arg = NULL;
1638 
1639  // parsing input arguments
1640  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1641  std::string err("Unsuccessful argument parsing not ");
1642  PyErr_SetString(PyExc_Exception, err.c_str());
1643  return NULL;
1644  }
1645 
1646  Matrix_real parameters_mtx;
1647  Matrix_real_float parameters_mtx_float;
1648  bool parameters_is_float32 = false;
1649  Matrix_real grad_mtx(parameters_mtx.size(), 1);
1650  double f0;
1651 
1652  try {
1653  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1654  if (parameters_is_float32) {
1655  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1656  }
1657  grad_mtx = Matrix_real(parameters_mtx.size(), 1);
1658  self->decomp->optimization_problem_combined(parameters_mtx, &f0, grad_mtx);
1659  }
1660  catch (std::exception& e) {
1661  PyErr_SetString(PyExc_Exception, e.what());
1662  return NULL;
1663  }
1664  catch (std::string err) {
1665  PyErr_SetString(PyExc_Exception, err.c_str());
1666  return NULL;
1667  }
1668  catch (...) {
1669  std::string err("Invalid pointer to decomposition class");
1670  PyErr_SetString(PyExc_Exception, err.c_str());
1671  return NULL;
1672  }
1673 
1674  // convert to numpy array
1675  PyObject *grad_py = NULL;
1676  if (parameters_is_float32 || self->decomp->get_use_float()) {
1677  Matrix_real_float grad_float(grad_mtx.rows, grad_mtx.cols, grad_mtx.stride);
1678  for (int row=0; row<grad_mtx.rows; row++) {
1679  for (int col=0; col<grad_mtx.cols; col++) {
1680  int idx = row*grad_mtx.stride + col;
1681  grad_float[idx] = static_cast<float>(grad_mtx[idx]);
1682  }
1683  }
1684  grad_float.set_owner(false);
1685  grad_py = matrix_real_float_to_numpy(grad_float);
1686  }
1687  else {
1688  grad_mtx.set_owner(false);
1689  grad_py = matrix_real_to_numpy(grad_mtx);
1690  }
1691 
1692  Py_DECREF(parameters_arg);
1693 
1694  PyObject* p = Py_BuildValue("(dO)", f0, grad_py);
1695  Py_DECREF(grad_py);
1696  return p;
1697 }
1698 
1705 static PyObject *
1707 {
1708  PyObject* parameters_obj = NULL;
1709  PyArrayObject* parameters_arg = NULL;
1710 
1711  // parsing input arguments
1712  if (!PyArg_ParseTuple(args, "|O", &parameters_obj)) {
1713  std::string err("Unsuccessful argument parsing not ");
1714  PyErr_SetString(PyExc_Exception, err.c_str());
1715  return NULL;
1716  }
1717 
1718  Matrix_real parameters_mtx;
1719  Matrix_real_float parameters_mtx_float;
1720  bool parameters_is_float32 = false;
1721  Matrix_real result_mtx;
1722 
1723  try {
1724  extract_parameters_any(parameters_obj, &parameters_arg, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1725  if (parameters_is_float32) {
1726  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1727  }
1728  std::vector<Matrix_real> parameters_vec;
1729  parameters_vec.resize(parameters_mtx.rows);
1730  for (int row_idx = 0; row_idx < parameters_mtx.rows; row_idx++) {
1731  parameters_vec[row_idx] = Matrix_real(parameters_mtx.get_data() + row_idx * parameters_mtx.stride, 1, parameters_mtx.cols, parameters_mtx.stride);
1732  }
1733  result_mtx = self->decomp->optimization_problem_batched(parameters_vec);
1734  }
1735  catch (std::exception& e) {
1736  PyErr_SetString(PyExc_Exception, e.what());
1737  return NULL;
1738  }
1739  catch (std::string err) {
1740  PyErr_SetString(PyExc_Exception, err.c_str());
1741  return NULL;
1742  }
1743  catch (...) {
1744  std::string err("Invalid pointer to decomposition class");
1745  PyErr_SetString(PyExc_Exception, err.c_str());
1746  return NULL;
1747  }
1748 
1749  // convert to numpy array
1750  PyObject *result_py = NULL;
1751  if (parameters_is_float32 || self->decomp->get_use_float()) {
1752  Matrix_real_float result_float(result_mtx.rows, result_mtx.cols, result_mtx.stride);
1753  for (int row=0; row<result_mtx.rows; row++) {
1754  for (int col=0; col<result_mtx.cols; col++) {
1755  int idx = row*result_mtx.stride + col;
1756  result_float[idx] = static_cast<float>(result_mtx[idx]);
1757  }
1758  }
1759  result_float.set_owner(false);
1760  result_py = matrix_real_float_to_numpy(result_float);
1761  }
1762  else {
1763  result_mtx.set_owner(false);
1764  result_py = matrix_real_to_numpy(result_mtx);
1765  }
1766 
1767  Py_DECREF(parameters_arg);
1768 
1769  return result_py;
1770 }
1771 
1777 static PyObject *
1779 {
1780 #ifdef __DFE__
1781  try {
1782  self->decomp->upload_Umtx_to_DFE();
1783  Py_RETURN_NONE;
1784  } catch (std::string err) {
1785  PyErr_SetString(PyExc_Exception, err.c_str());
1786  return NULL;
1787  } catch (std::exception& e) {
1788  PyErr_SetString(PyExc_Exception, e.what());
1789  return NULL;
1790  } catch (...) {
1791  std::string err("Invalid pointer to decomposition class");
1792  PyErr_SetString(PyExc_Exception, err.c_str());
1793  return NULL;
1794  }
1795 #else
1796  PyErr_SetString(PyExc_NotImplementedError, "upload_Umtx_to_DFE is only available when compiled with DFE support");
1797  return NULL;
1798 #endif
1799 }
1800 
1806 static PyObject *
1808 {
1809  try {
1810  int trace_offset = self->decomp->get_trace_offset();
1811  return Py_BuildValue("i", trace_offset);
1812  } catch (std::string err) {
1813  PyErr_SetString(PyExc_Exception, err.c_str());
1814  return NULL;
1815  } catch (std::exception& e) {
1816  PyErr_SetString(PyExc_Exception, e.what());
1817  return NULL;
1818  } catch (...) {
1819  std::string err("Invalid pointer to decomposition class");
1820  PyErr_SetString(PyExc_Exception, err.c_str());
1821  return NULL;
1822  }
1823 }
1824 
1831 static PyObject *
1833 {
1834  static char *kwlist[] = {(char*)"trace_offset", NULL};
1835 
1836  int trace_offset = 0;
1837  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &trace_offset)) {
1838  std::string err("Invalid arguments: expected (trace_offset: int)");
1839  PyErr_SetString(PyExc_Exception, err.c_str());
1840  return NULL;
1841  }
1842 
1843  try {
1844  self->decomp->set_trace_offset(trace_offset);
1845  Py_RETURN_NONE;
1846  } catch (std::string err) {
1847  PyErr_SetString(PyExc_Exception, err.c_str());
1848  return NULL;
1849  } catch (std::exception& e) {
1850  PyErr_SetString(PyExc_Exception, e.what());
1851  return NULL;
1852  } catch (...) {
1853  std::string err("Invalid pointer to decomposition class");
1854  PyErr_SetString(PyExc_Exception, err.c_str());
1855  return NULL;
1856  }
1857 }
1858 
1864 static PyObject *
1866 {
1867  try {
1868  double error = self->decomp->get_decomposition_error();
1869  return Py_BuildValue("d", error);
1870  } catch (std::string err) {
1871  PyErr_SetString(PyExc_Exception, err.c_str());
1872  return NULL;
1873  } catch (std::exception& e) {
1874  PyErr_SetString(PyExc_Exception, e.what());
1875  return NULL;
1876  } catch (...) {
1877  std::string err("Invalid pointer to decomposition class");
1878  PyErr_SetString(PyExc_Exception, err.c_str());
1879  return NULL;
1880  }
1881 }
1882 
1889 static PyObject *
1891 {
1892  PyObject *parameters_obj = NULL, *input_state_obj = NULL;
1893  PyArrayObject *parameters_arr = NULL, *input_state_arg = NULL;
1894  PyObject *qubit_list_arg = NULL;
1895 
1896  // Parse input arguments
1897  if (!PyArg_ParseTuple(args, "|OOO", &parameters_obj, &input_state_obj, &qubit_list_arg)) {
1898  return Py_BuildValue("i", -1);
1899  }
1900 
1901  Matrix_real parameters_mtx;
1902  Matrix_real_float parameters_mtx_float;
1903  bool parameters_is_float32 = false;
1904  try {
1905  extract_parameters_any(parameters_obj, &parameters_arr, parameters_mtx, parameters_mtx_float, parameters_is_float32);
1906  if (parameters_is_float32) {
1907  parameters_mtx = parameters_float_to_double(parameters_mtx_float);
1908  }
1909  }
1910  catch (std::exception& e) {
1911  PyErr_SetString(PyExc_Exception, e.what());
1912  return NULL;
1913  }
1914 
1915  // Convert input state array
1916  if (input_state_obj == NULL) {
1917  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
1918  return NULL;
1919  }
1920 
1921  Matrix input_state_mtx;
1922  Matrix_float input_state_mtx_float;
1923  bool input_state_is_float32 = false;
1924  try {
1925  extract_matrix_any(input_state_obj, &input_state_arg, input_state_mtx, input_state_mtx_float, input_state_is_float32);
1926  if (input_state_is_float32) {
1927  input_state_mtx = input_state_mtx_float.to_float64();
1928  }
1929  }
1930  catch (std::exception& e) {
1931  PyErr_SetString(PyExc_Exception, e.what());
1932  return NULL;
1933  }
1934 
1935  // Test C-style contiguous memory allocation
1936  if (!PyArray_IS_C_CONTIGUOUS(input_state_arg)) {
1937  PyErr_SetString(PyExc_Exception, "Input matrix is not memory contiguous");
1938  return NULL;
1939  }
1940 
1941  // Check qubit list argument
1942  if (qubit_list_arg == NULL || !PyList_Check(qubit_list_arg)) {
1943  PyErr_SetString(PyExc_Exception, "qubit_list should be a list");
1944  return NULL;
1945  }
1946 
1947  Py_ssize_t reduced_qbit_num = PyList_Size(qubit_list_arg);
1948  matrix_base<int> qbit_list_mtx((int)reduced_qbit_num, 1);
1949 
1950  for (int idx = 0; idx < reduced_qbit_num; idx++) {
1951  PyObject* item = PyList_GET_ITEM(qubit_list_arg, idx);
1952  qbit_list_mtx[idx] = (int)PyLong_AsLong(item);
1953  }
1954 
1955  double entropy = -1;
1956 
1957  try {
1958  entropy = self->decomp->get_second_Renyi_entropy(parameters_mtx, input_state_mtx, qbit_list_mtx);
1959  } catch (std::string err) {
1960  PyErr_SetString(PyExc_Exception, err.c_str());
1961  return NULL;
1962  } catch (std::exception& e) {
1963  PyErr_SetString(PyExc_Exception, e.what());
1964  return NULL;
1965  } catch (...) {
1966  std::string err("Invalid pointer to decomposition class");
1967  PyErr_SetString(PyExc_Exception, err.c_str());
1968  return NULL;
1969  }
1970 
1971  // Clean up references
1972  Py_DECREF(parameters_arr);
1973  Py_DECREF(input_state_arg);
1974 
1975  PyObject* p = Py_BuildValue("d", entropy);
1976  return p;
1977 }
1978 
1984 static PyObject *
1986 {
1987  try {
1988  int qbit_num = self->decomp->get_qbit_num();
1989  return Py_BuildValue("i", qbit_num);
1990  } catch (std::string err) {
1991  PyErr_SetString(PyExc_Exception, err.c_str());
1992  return NULL;
1993  } catch (std::exception& e) {
1994  PyErr_SetString(PyExc_Exception, e.what());
1995  return NULL;
1996  } catch (...) {
1997  std::string err("Invalid pointer to decomposition class");
1998  PyErr_SetString(PyExc_Exception, err.c_str());
1999  return NULL;
2000  }
2001 }
2002 
2004 
2009 static PyObject *
2011 {
2012  PyObject* identical_blocks_dict;
2013  if (!PyArg_ParseTuple(args, "O", &identical_blocks_dict)) {
2014  return NULL;
2015  }
2016  if (!PyDict_Check(identical_blocks_dict)) {
2017  PyErr_SetString(PyExc_TypeError, "Expected dictionary argument");
2018  return NULL;
2019  }
2020  try {
2021  N_Qubit_Decomposition* base_decomp = dynamic_cast<N_Qubit_Decomposition*>(self->decomp);
2022  if (base_decomp == NULL) {
2023  PyErr_SetString(PyExc_AttributeError, "set_identical_blocks is only available for N_Qubit_Decomposition");
2024  return NULL;
2025  }
2026  std::map<int, int> identical_blocks_map;
2027  PyObject *key, *value;
2028  Py_ssize_t pos = 0;
2029  while (PyDict_Next(identical_blocks_dict, &pos, &key, &value)) {
2030  if (!PyLong_Check(key) || !PyLong_Check(value)) {
2031  PyErr_SetString(PyExc_TypeError, "Dictionary keys and values must be integers");
2032  return NULL;
2033  }
2034  int qubit_idx = PyLong_AsLong(key);
2035  int blocks = PyLong_AsLong(value);
2036  identical_blocks_map[qubit_idx] = blocks;
2037  }
2038  base_decomp->set_identical_blocks(identical_blocks_map);
2039  Py_RETURN_NONE;
2040  } catch (std::exception& e) {
2041  PyErr_SetString(PyExc_Exception, e.what());
2042  return NULL;
2043  }
2044 }
2045 
2050 static PyObject *
2052 {
2053  try {
2054  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2055  if (adaptive_decomp == NULL) {
2056  PyErr_SetString(PyExc_AttributeError, "get_initial_circuit is only available for N_Qubit_Decomposition_adaptive");
2057  return NULL;
2058  }
2059  adaptive_decomp->get_initial_circuit();
2060  return Py_BuildValue("i", 0);
2061  } catch (std::exception& e) {
2062  PyErr_SetString(PyExc_Exception, e.what());
2063  return NULL;
2064  }
2065 }
2066 
2071 static PyObject *
2073 {
2074  try {
2075  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2076  if (adaptive_decomp == NULL) {
2077  PyErr_SetString(PyExc_AttributeError, "compress_circuit is only available for N_Qubit_Decomposition_adaptive");
2078  return NULL;
2079  }
2080  adaptive_decomp->compress_circuit();
2081  Py_RETURN_NONE;
2082  } catch (std::exception& e) {
2083  PyErr_SetString(PyExc_Exception, e.what());
2084  return NULL;
2085  }
2086 }
2087 
2092 static PyObject *
2094 {
2095  try {
2096  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2097  if (adaptive_decomp == NULL) {
2098  PyErr_SetString(PyExc_AttributeError, "Remove_Trivial_CRY_Gates is only available for N_Qubit_Decomposition_adaptive");
2099  return NULL;
2100  }
2101  adaptive_decomp->remove_trivial_CRY_gates();
2102  Py_RETURN_NONE;
2103  } catch (std::string& err) {
2104  PyErr_SetString(PyExc_Exception, err.c_str());
2105  return NULL;
2106  } catch (std::exception& e) {
2107  PyErr_SetString(PyExc_Exception, e.what());
2108  return NULL;
2109  }
2110 }
2111 
2116 static PyObject *
2118 {
2119  try {
2120  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2121  if (adaptive_decomp == NULL) {
2122  PyErr_SetString(PyExc_AttributeError, "finalize_circuit is only available for N_Qubit_Decomposition_adaptive");
2123  return NULL;
2124  }
2125  adaptive_decomp->finalize_circuit();
2126  Py_RETURN_NONE;
2127  } catch (std::exception& e) {
2128  PyErr_SetString(PyExc_Exception, e.what());
2129  return NULL;
2130  }
2131 }
2132 
2137 static PyObject *
2139 {
2140  // initiate variables for input arguments
2141  PyObject* filename_py=NULL;
2142  // parsing input arguments
2143  if (!PyArg_ParseTuple(args, "|O", &filename_py )) {
2144  return Py_BuildValue("i", -1);
2145  }
2146  // determine the optimizaton method
2147  PyObject* filename_string = PyObject_Str(filename_py);
2148  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
2149  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
2150  std::string filename_str( filename_C );
2151  try {
2152  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2153  if (adaptive_decomp == NULL) {
2154  std::string err("set_Gate_Structure_From_Binary is only available for adaptive decomposition");
2155  PyErr_SetString(PyExc_Exception, err.c_str());
2156  return NULL;
2157  }
2158  adaptive_decomp->set_adaptive_gate_structure( filename_str );
2159  }
2160  catch (std::string err ) {
2161  PyErr_SetString(PyExc_Exception, err.c_str());
2162  return NULL;
2163  }
2164  catch(...) {
2165  std::string err( "Invalid pointer to decomposition class");
2166  PyErr_SetString(PyExc_Exception, err.c_str());
2167  return NULL;
2168  }
2169  return Py_BuildValue("i", 0);
2170 
2171 }
2172 
2177 static PyObject *
2179 {
2180  // initiate variables for input arguments
2181  PyObject* filename_py = NULL;
2182  // parsing input arguments
2183  if (!PyArg_ParseTuple(args, "|O", &filename_py)) {
2184  return Py_BuildValue("i", -1);
2185  }
2186  // Convert PyObject to UTF-8 encoded string
2187  PyObject* filename_string = PyObject_Str(filename_py);
2188  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
2189  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
2190  std::string filename_str(filename_C);
2191  try {
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_Gate_Structure_From_Binary is only available for N_Qubit_Decomposition_adaptive");
2195  return NULL;
2196  }
2197  adaptive_decomp->add_adaptive_gate_structure(filename_str);
2198  }
2199  catch (std::string err) {
2200  PyErr_SetString(PyExc_Exception, err.c_str());
2201  return NULL;
2202  }
2203  catch(...) {
2204  std::string err("Invalid pointer to decomposition class");
2205  PyErr_SetString(PyExc_Exception, err.c_str());
2206  return NULL;
2207  }
2208  return Py_BuildValue("i", 0);
2209 }
2210 
2215 static PyObject *
2217 {
2218  // initiate variables for input arguments
2219  PyObject* filename_py = NULL;
2220  // parsing input arguments
2221  if (!PyArg_ParseTuple(args, "|O", &filename_py)) {
2222  return Py_BuildValue("i", -1);
2223  }
2224  // Convert PyObject to UTF-8 encoded string
2225  PyObject* filename_string = PyObject_Str(filename_py);
2226  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
2227  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
2228  std::string filename_str(filename_C);
2229  try {
2230  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2231  if (adaptive_decomp == NULL) {
2232  PyErr_SetString(PyExc_AttributeError, "set_Unitary_From_Binary is only available for N_Qubit_Decomposition_adaptive");
2233  return NULL;
2234  }
2235  adaptive_decomp->set_unitary_from_file(filename_str);
2236  }
2237  catch (std::string err) {
2238  PyErr_SetString(PyExc_Exception, err.c_str());
2239  return NULL;
2240  }
2241  catch(...) {
2242  std::string err("Invalid pointer to decomposition class");
2243  PyErr_SetString(PyExc_Exception, err.c_str());
2244  return NULL;
2245  }
2246  return Py_BuildValue("i", 0);
2247 }
2248 
2253 static PyObject *
2255 {
2256  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2257  if (adaptive_decomp == NULL) {
2258  PyErr_SetString(PyExc_AttributeError, "add_Adaptive_Layers is only available for N_Qubit_Decomposition_adaptive");
2259  return NULL;
2260  }
2261  adaptive_decomp->add_adaptive_layers();
2262  return Py_BuildValue("i", 0);
2263 }
2264 
2269 static PyObject *
2271 {
2272  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2273  if (adaptive_decomp == NULL) {
2274  PyErr_SetString(PyExc_AttributeError, "add_Layer_To_Imported_Gate_Structure is only available for N_Qubit_Decomposition_adaptive");
2275  return NULL;
2276  }
2277  adaptive_decomp->add_layer_to_imported_gate_structure();
2278  return Py_BuildValue("i", 0);
2279 }
2280 
2285 static PyObject *
2287 {
2288  try {
2289  N_Qubit_Decomposition_adaptive* adaptive_decomp = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp);
2290  if (adaptive_decomp == NULL) {
2291  PyErr_SetString(PyExc_AttributeError, "apply_Imported_Gate_Structure is only available for N_Qubit_Decomposition_adaptive");
2292  return NULL;
2293  }
2294  adaptive_decomp->apply_imported_gate_structure();
2295  }
2296  catch (std::string err) {
2297  PyErr_SetString(PyExc_Exception, err.c_str());
2298  return NULL;
2299  }
2300  catch(...) {
2301  std::string err("Invalid pointer to decomposition class");
2302  PyErr_SetString(PyExc_Exception, err.c_str());
2303  return NULL;
2304  }
2305  return Py_BuildValue("i", 0);
2306 }
2307 
2308 // ========================================================================= METHODS SHARED ACROSS DECOMP CLASSES
2309 
2316 static PyObject *
2318 {
2319  if ( self->Umtx != NULL ) {
2320  // release the unitary to be decomposed
2321  Py_DECREF(self->Umtx);
2322  self->Umtx = NULL;
2323  }
2324 
2325  PyObject *Umtx_obj = NULL;
2326  //Parse arguments
2327  if (!PyArg_ParseTuple(args, "|O", &Umtx_obj )) {
2328  return Py_BuildValue("i", -1);
2329  }
2330 
2331  // convert python object array to numpy C API array
2332  if ( Umtx_obj == NULL ) {
2333  PyErr_SetString(PyExc_Exception, "Umtx argument in empty");
2334  return NULL;
2335  }
2336 
2337  Matrix Umtx_mtx;
2338  Matrix_float Umtx_mtx_float;
2339  bool Umtx_is_float32 = false;
2340  try {
2341  extract_matrix_any(Umtx_obj, &self->Umtx, Umtx_mtx, Umtx_mtx_float, Umtx_is_float32);
2342  }
2343  catch (std::exception& e) {
2344  PyErr_SetString(PyExc_Exception, e.what());
2345  return NULL;
2346  }
2347 
2348  // Try each decomposition type that supports set_unitary
2349  if (N_Qubit_Decomposition_adaptive* p = dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp)) {
2350  if (Umtx_is_float32) {
2351  p->set_unitary(Umtx_mtx_float);
2352  }
2353  else {
2354  p->set_unitary(Umtx_mtx);
2355  }
2356  return Py_BuildValue("i", 0);
2357  }
2358  if (N_Qubit_Decomposition_Tree_Search* p = dynamic_cast<N_Qubit_Decomposition_Tree_Search*>(self->decomp)) {
2359  if (Umtx_is_float32) {
2360  p->set_unitary(Umtx_mtx_float);
2361  }
2362  else {
2363  p->set_unitary(Umtx_mtx);
2364  }
2365  return Py_BuildValue("i", 0);
2366  }
2367  if (N_Qubit_Decomposition_Tabu_Search* p = dynamic_cast<N_Qubit_Decomposition_Tabu_Search*>(self->decomp)) {
2368  if (Umtx_is_float32) {
2369  p->set_unitary(Umtx_mtx_float);
2370  }
2371  else {
2372  p->set_unitary(Umtx_mtx);
2373  }
2374  return Py_BuildValue("i", 0);
2375  }
2376 
2377  PyErr_SetString(PyExc_TypeError, "set_unitary not available for this decomposition type");
2378  return NULL;
2379 }
2380 
2381 
2383 
2388 static PyObject*
2390 {
2391  std::vector<Gate*>&& gates = self->decomp->get_gates();
2392  Matrix_real&& params = self->decomp->get_optimized_parameters();
2393 
2394  PyObject* gates_list = PyList_New(0);
2395  if (!gates_list) return NULL;
2396 
2397  for (size_t idx = 0; idx < gates.size(); idx++) {
2398  Gate* gate = gates[idx];
2399  if (!gate) continue;
2400 
2401  PyObject* gate_dict = PyDict_New();
2402  if (!gate_dict) {
2403  Py_DECREF(gates_list);
2404  return NULL;
2405  }
2406 
2407  // Map gate type to string
2408  const char* type_str = nullptr;
2409  switch(gate->get_type()) {
2410  case GENERAL_OPERATION: type_str = "GENERAL"; break;
2411  case CZ_OPERATION: type_str = "CZ"; break;
2412  case CNOT_OPERATION: type_str = "CNOT"; break;
2413  case CH_OPERATION: type_str = "CH"; break;
2414  case U3_OPERATION: type_str = "U3"; break;
2415  case RY_OPERATION: type_str = "RY"; break;
2416  case RX_OPERATION: type_str = "RX"; break;
2417  case RZ_OPERATION: type_str = "RZ"; break;
2418  case X_OPERATION: type_str = "X"; break;
2419  case SX_OPERATION: type_str = "SX"; break;
2420  case CRY_OPERATION: type_str = "CRY"; break;
2421  case SYC_OPERATION: type_str = "SYC"; break;
2422  case BLOCK_OPERATION: type_str = "BLOCK"; break;
2423  case ADAPTIVE_OPERATION: type_str = "ADAPTIVE"; break;
2424  case DECOMPOSITION_BASE_CLASS: type_str = "DECOMPOSITION_BASE_CLASS"; break;
2425  case SUB_MATRIX_DECOMPOSITION_CLASS: type_str = "SUB_MATRIX_DECOMPOSITION_CLASS"; break;
2426  case N_QUBIT_DECOMPOSITION_CLASS_BASE: type_str = "N_QUBIT_DECOMPOSITION_CLASS_BASE"; break;
2427  case N_QUBIT_DECOMPOSITION_CLASS: type_str = "N_QUBIT_DECOMPOSITION_CLASS"; break;
2428  case Y_OPERATION: type_str = "Y"; break;
2429  case Z_OPERATION: type_str = "Z"; break;
2430  case H_OPERATION: type_str = "H"; break;
2431  case CROT_OPERATION: type_str = "CROT"; break;
2432  case R_OPERATION: type_str = "R"; break;
2433  case T_OPERATION: type_str = "T"; break;
2434  case TDG_OPERATION: type_str = "TDG"; break;
2435  case U1_OPERATION: type_str = "U1"; break;
2436  case U2_OPERATION: type_str = "U2"; break;
2437  case CR_OPERATION: type_str = "CR"; break;
2438  case S_OPERATION: type_str = "S"; break;
2439  case SDG_OPERATION: type_str = "SDG"; break;
2440  case CU_OPERATION: type_str = "CU"; break;
2441  case CP_OPERATION: type_str = "CP"; break;
2442  case CRX_OPERATION: type_str = "CRX"; break;
2443  case CRZ_OPERATION: type_str = "CRZ"; break;
2444  case CCX_OPERATION: type_str = "CCX"; break;
2445  case SWAP_OPERATION: type_str = "SWAP"; break;
2446  case CSWAP_OPERATION: type_str = "CSWAP"; break;
2447  default: type_str = "UNKNOWN"; break;
2448  }
2449  PyDict_SetItemString(gate_dict, "type", PyUnicode_FromString(type_str));
2450 
2451  PyDict_SetItemString(gate_dict, "target_qbit", PyLong_FromLong(gate->get_target_qbit()));
2452 
2453  int control_qbit = gate->get_control_qbit();
2454  if (control_qbit >= 0) {
2455  PyDict_SetItemString(gate_dict, "control_qbit", PyLong_FromLong(control_qbit));
2456  }
2457 
2458  // Add parameters
2459  int pnum = gate->get_parameter_num();
2460  int pstart = gate->get_parameter_start_idx();
2461  if (pnum > 0 && pstart >= 0 && (pstart + pnum) <= (int)params.size()) {
2462  if (gate->get_type() == U3_OPERATION && pnum >= 3) {
2463  PyDict_SetItemString(gate_dict, "Theta", PyFloat_FromDouble(params[pstart]));
2464  PyDict_SetItemString(gate_dict, "Phi", PyFloat_FromDouble(params[pstart + 1]));
2465  PyDict_SetItemString(gate_dict, "Lambda", PyFloat_FromDouble(params[pstart + 2]));
2466  } else if (gate->get_type() == RX_OPERATION || gate->get_type() == RY_OPERATION || gate->get_type() == CRY_OPERATION) {
2467  PyDict_SetItemString(gate_dict, "Theta", PyFloat_FromDouble(params[pstart]));
2468  } else if (gate->get_type() == RZ_OPERATION) {
2469  PyDict_SetItemString(gate_dict, "Phi", PyFloat_FromDouble(params[pstart]));
2470  }
2471  }
2472 
2473  PyList_Append(gates_list, gate_dict);
2474  Py_DECREF(gate_dict);
2475  }
2476  return gates_list;
2477 }
2478 
2483 static PyObject*
2485 {
2486  // Import Qiskit_IO module
2487  PyObject* qiskit_io_module = PyImport_ImportModule("squander.IO_interfaces.Qiskit_IO");
2488  if (!qiskit_io_module) {
2489  PyErr_SetString(PyExc_ImportError, "Failed to import squander.IO_interfaces.Qiskit_IO");
2490  return NULL;
2491  }
2492 
2493  // Get the get_Qiskit_Circuit function
2494  PyObject* get_qiskit_func = PyObject_GetAttrString(qiskit_io_module, "get_Qiskit_Circuit");
2495  Py_DECREF(qiskit_io_module);
2496  if (!get_qiskit_func) {
2497  PyErr_SetString(PyExc_AttributeError, "get_Qiskit_Circuit not found in Qiskit_IO");
2498  return NULL;
2499  }
2500 
2501  // Get circuit and parameters
2503  if (!circuit) {
2504  Py_DECREF(get_qiskit_func);
2505  return NULL;
2506  }
2508  if (!parameters) {
2509  Py_DECREF(get_qiskit_func);
2510  Py_DECREF(circuit);
2511  return NULL;
2512  }
2513 
2514  // Call get_Qiskit_Circuit(circuit, parameters)
2515  PyObject* args = PyTuple_Pack(2, circuit, parameters);
2516  PyObject* result = PyObject_CallObject(get_qiskit_func, args);
2517 
2518  Py_DECREF(args);
2519  Py_DECREF(parameters);
2520  Py_DECREF(circuit);
2521  Py_DECREF(get_qiskit_func);
2522 
2523  return result;
2524 }
2525 
2531 #define CIRQ_ADD_SINGLE_QUBIT_GATE(name) do { \
2532  PyObject* gate_func = PyObject_GetAttrString(cirq_module, #name); \
2533  PyObject* gate_args = PyTuple_Pack(1, target_qubit); \
2534  PyObject* cirq_gate = PyObject_CallObject(gate_func, gate_args); \
2535  Py_DECREF(gate_args); Py_DECREF(gate_func); \
2536  if (cirq_gate) { \
2537  PyObject* append_args = PyTuple_Pack(1, cirq_gate); \
2538  PyObject_CallObject(append_func, append_args); \
2539  Py_DECREF(append_args); Py_DECREF(cirq_gate); \
2540  } \
2541 } while(0)
2542 
2543 // Helper macros for Cirq gate creation
2544 #define CIRQ_ADD_TWO_QUBIT_GATE(name) do { \
2545  PyObject* control_qbit_obj = PyDict_GetItemString(gate, "control_qbit"); \
2546  if (!control_qbit_obj) continue; \
2547  long control_idx = qbit_num - 1 - PyLong_AsLong(control_qbit_obj); \
2548  PyObject* control_qubit = PyList_GetItem(qubits, control_idx); \
2549  PyObject* gate_func = PyObject_GetAttrString(cirq_module, #name); \
2550  PyObject* gate_args = PyTuple_Pack(2, control_qubit, target_qubit); \
2551  PyObject* cirq_gate = PyObject_CallObject(gate_func, gate_args); \
2552  Py_DECREF(gate_args); Py_DECREF(gate_func); \
2553  if (cirq_gate) { \
2554  PyObject* append_args = PyTuple_Pack(1, cirq_gate); \
2555  PyObject_CallObject(append_func, append_args); \
2556  Py_DECREF(append_args); Py_DECREF(cirq_gate); \
2557  } \
2558 } while(0)
2559 
2560 #define CIRQ_ADD_ROTATION_GATE(name, param) do { \
2561  PyObject* param_obj = PyDict_GetItemString(gate, param); \
2562  if (!param_obj) continue; \
2563  PyObject* gate_func = PyObject_GetAttrString(cirq_module, #name); \
2564  PyObject* gate_args = PyTuple_Pack(1, param_obj); \
2565  PyObject* cirq_gate = PyObject_CallObject(gate_func, gate_args); \
2566  Py_DECREF(gate_args); Py_DECREF(gate_func); \
2567  if (cirq_gate) { \
2568  PyObject* on_method = PyObject_GetAttrString(cirq_gate, "on"); \
2569  PyObject* on_args = PyTuple_Pack(1, target_qubit); \
2570  PyObject* gate_op = PyObject_CallObject(on_method, on_args); \
2571  Py_DECREF(on_args); Py_DECREF(on_method); Py_DECREF(cirq_gate); \
2572  if (gate_op) { \
2573  PyObject* append_args = PyTuple_Pack(1, gate_op); \
2574  PyObject_CallObject(append_func, append_args); \
2575  Py_DECREF(append_args); Py_DECREF(gate_op); \
2576  } \
2577  } \
2578 } while(0)
2579 
2580 static PyObject*
2582 {
2583  PyObject* cirq_module = PyImport_ImportModule("cirq");
2584  if (!cirq_module) {
2585  PyErr_SetString(PyExc_ImportError, "Failed to import cirq. Please install cirq package.");
2586  return NULL;
2587  }
2588 
2589  PyObject* cirq_circuit_class = PyObject_GetAttrString(cirq_module, "Circuit");
2590  if (!cirq_circuit_class) {
2591  Py_DECREF(cirq_module);
2592  return NULL;
2593  }
2594 
2595  PyObject* cirq_circuit_obj = PyObject_CallObject(cirq_circuit_class, NULL);
2596  Py_DECREF(cirq_circuit_class);
2597  if (!cirq_circuit_obj) {
2598  Py_DECREF(cirq_module);
2599  return NULL;
2600  }
2601 
2602  // Create qubit register
2603  PyObject* cirq_line_qubit_class = PyObject_GetAttrString(cirq_module, "LineQubit");
2604  if (!cirq_line_qubit_class) {
2605  Py_DECREF(cirq_circuit_obj);
2606  Py_DECREF(cirq_module);
2607  return NULL;
2608  }
2609  PyObject* range_func = PyObject_GetAttrString(cirq_line_qubit_class, "range");
2610  Py_DECREF(cirq_line_qubit_class);
2611  if (!range_func) {
2612  Py_DECREF(cirq_circuit_obj);
2613  Py_DECREF(cirq_module);
2614  return NULL;
2615  }
2616 
2617  int qbit_num = self->decomp->get_qbit_num();
2618  PyObject* range_args = PyTuple_Pack(1, PyLong_FromLong(qbit_num));
2619  PyObject* qubits = PyObject_CallObject(range_func, range_args);
2620  Py_DECREF(range_args); Py_DECREF(range_func);
2621  if (!qubits) {
2622  Py_DECREF(cirq_circuit_obj);
2623  Py_DECREF(cirq_module);
2624  return NULL;
2625  }
2626 
2627  PyObject* gates_list = qgd_N_Qubit_Decomposition_Wrapper_get_Gates(self);
2628  if (!gates_list) {
2629  Py_DECREF(qubits);
2630  Py_DECREF(cirq_circuit_obj);
2631  Py_DECREF(cirq_module);
2632  return NULL;
2633  }
2634 
2635  PyObject* append_func = PyObject_GetAttrString(cirq_circuit_obj, "append");
2636  if (!append_func) {
2637  Py_DECREF(gates_list); Py_DECREF(qubits); Py_DECREF(cirq_circuit_obj); Py_DECREF(cirq_module);
2638  return NULL;
2639  }
2640 
2641  PyObject* cirq_google_module = PyObject_GetAttrString(cirq_module, "google");
2642 
2643  // Process gates in reverse order
2644  Py_ssize_t num_gates = PyList_Size(gates_list);
2645  for (Py_ssize_t idx = num_gates - 1; idx >= 0; idx--) {
2646  PyObject* gate = PyList_GetItem(gates_list, idx);
2647  if (!gate) continue;
2648 
2649  PyObject* gate_type = PyDict_GetItemString(gate, "type");
2650  if (!gate_type) continue;
2651  const char* gate_type_str = PyUnicode_AsUTF8(gate_type);
2652  if (!gate_type_str) continue;
2653 
2654  PyObject* target_qbit_obj = PyDict_GetItemString(gate, "target_qbit");
2655  if (!target_qbit_obj) continue;
2656 
2657  long target_idx = qbit_num - 1 - PyLong_AsLong(target_qbit_obj);
2658  PyObject* target_qubit = PyList_GetItem(qubits, target_idx);
2659  if (!target_qubit) continue;
2660 
2661  if (strcmp(gate_type_str, "CNOT") == 0) { CIRQ_ADD_TWO_QUBIT_GATE(CNOT); }
2662  else if (strcmp(gate_type_str, "CZ") == 0) { CIRQ_ADD_TWO_QUBIT_GATE(CZ); }
2663  else if (strcmp(gate_type_str, "CH") == 0) { CIRQ_ADD_TWO_QUBIT_GATE(CH); }
2664  else if (strcmp(gate_type_str, "SYC") == 0 && cirq_google_module) {
2665  PyObject* control_qbit_obj = PyDict_GetItemString(gate, "control_qbit");
2666  if (control_qbit_obj) {
2667  long control_idx = qbit_num - 1 - PyLong_AsLong(control_qbit_obj);
2668  PyObject* control_qubit = PyList_GetItem(qubits, control_idx);
2669 
2670  PyObject* syc_func = PyObject_GetAttrString(cirq_google_module, "SYC");
2671  PyObject* syc_args = PyTuple_Pack(2, control_qubit, target_qubit);
2672  PyObject* cirq_gate = PyObject_CallObject(syc_func, syc_args);
2673  Py_DECREF(syc_args);
2674  Py_DECREF(syc_func);
2675  if (cirq_gate) {
2676  PyObject* append_args = PyTuple_Pack(1, cirq_gate);
2677  PyObject_CallObject(append_func, append_args);
2678  Py_DECREF(append_args);
2679  Py_DECREF(cirq_gate);
2680  }
2681  }
2682  }
2683  else if (strcmp(gate_type_str, "CRY") == 0) {
2684  printf("CRY gate needs to be implemented\n");
2685  }
2686  else if (strcmp(gate_type_str, "U3") == 0) {
2687  printf("Unsupported gate in the Cirq export: U3 gate\n");
2688  Py_XDECREF(cirq_google_module);
2689  Py_DECREF(append_func);
2690  Py_DECREF(gates_list);
2691  Py_DECREF(qubits);
2692  Py_DECREF(cirq_circuit_obj);
2693  Py_DECREF(cirq_module);
2694  Py_RETURN_NONE;
2695  }
2696  else if (strcmp(gate_type_str, "RX") == 0) { CIRQ_ADD_ROTATION_GATE(rx, "Theta"); }
2697  else if (strcmp(gate_type_str, "RY") == 0) { CIRQ_ADD_ROTATION_GATE(ry, "Theta"); }
2698  else if (strcmp(gate_type_str, "RZ") == 0) { CIRQ_ADD_ROTATION_GATE(rz, "Phi"); }
2699  else if (strcmp(gate_type_str, "X") == 0) { CIRQ_ADD_SINGLE_QUBIT_GATE(x); }
2700  else if (strcmp(gate_type_str, "Y") == 0) { CIRQ_ADD_SINGLE_QUBIT_GATE(y); }
2701  else if (strcmp(gate_type_str, "Z") == 0) { CIRQ_ADD_SINGLE_QUBIT_GATE(z); }
2702  else if (strcmp(gate_type_str, "SX") == 0) { CIRQ_ADD_SINGLE_QUBIT_GATE(sx); }
2703  }
2704 
2705  Py_XDECREF(cirq_google_module);
2706  Py_DECREF(append_func);
2707  Py_DECREF(gates_list);
2708  Py_DECREF(qubits);
2709  Py_DECREF(cirq_module);
2710 
2711  return cirq_circuit_obj;
2712 }
2713 
2714 #undef CIRQ_ADD_SINGLE_QUBIT_GATE
2715 #undef CIRQ_ADD_TWO_QUBIT_GATE
2716 #undef CIRQ_ADD_ROTATION_GATE
2717 
2723 static PyObject*
2725 {
2726  // Import Qiskit_IO module
2727  PyObject* qiskit_io_module = PyImport_ImportModule("squander.IO_interfaces.Qiskit_IO");
2728  if (!qiskit_io_module) {
2729  PyErr_SetString(PyExc_ImportError, "Failed to import squander.IO_interfaces.Qiskit_IO");
2730  return NULL;
2731  }
2732 
2733  // Get the convert_Qiskit_to_Squander function
2734  PyObject* convert_func = PyObject_GetAttrString(qiskit_io_module, "convert_Qiskit_to_Squander");
2735  Py_DECREF(qiskit_io_module);
2736  if (!convert_func) {
2737  PyErr_SetString(PyExc_AttributeError, "convert_Qiskit_to_Squander not found in Qiskit_IO");
2738  return NULL;
2739  }
2740  // Call convert_Qiskit_to_Squander(qc_in) -> returns (circuit, parameters)
2741  PyObject* convert_args = PyTuple_Pack(1, qc_in);
2742  PyObject* convert_result = PyObject_CallObject(convert_func, convert_args);
2743  Py_DECREF(convert_args);
2744  Py_DECREF(convert_func);
2745  if (!convert_result || !PyTuple_Check(convert_result) || PyTuple_Size(convert_result) != 2) {
2746  Py_XDECREF(convert_result);
2747  PyErr_SetString(PyExc_ValueError, "convert_Qiskit_to_Squander should return (circuit, parameters)");
2748  return NULL;
2749  }
2750 
2751  PyObject *circuit_squander = PyTuple_GetItem(convert_result, 0), *parameters = PyTuple_GetItem(convert_result, 1);
2752 
2753  // Set gate structure
2754  PyObject* set_gate_args = PyTuple_Pack(1, circuit_squander);
2755  PyObject* set_gate_result = qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure(self, set_gate_args);
2756  Py_DECREF(set_gate_args);
2757  if (!set_gate_result) {
2758  Py_DECREF(convert_result);
2759  return NULL;
2760  }
2761  Py_DECREF(set_gate_result);
2762 
2763  // Set optimized parameters
2764  PyObject* set_params_args = PyTuple_Pack(1, parameters);
2765  PyObject* set_params_result = qgd_N_Qubit_Decomposition_Wrapper_set_Optimized_Parameters(self, set_params_args);
2766  Py_DECREF(set_params_args);
2767  Py_DECREF(convert_result);
2768  if (!set_params_result) {
2769  return NULL;
2770  }
2771  Py_DECREF(set_params_result);
2772 
2773  Py_RETURN_NONE;
2774 }
2775 
2781 static PyObject*
2783 {
2784  // Import qiskit module
2785  PyObject* qiskit_module = PyImport_ImportModule("qiskit");
2786  if (!qiskit_module) {
2787  PyErr_SetString(PyExc_ImportError, "Failed to import qiskit");
2788  return NULL;
2789  }
2790  // Get transpile function
2791  PyObject* transpile_func = PyObject_GetAttrString(qiskit_module, "transpile");
2792  Py_DECREF(qiskit_module);
2793  if (!transpile_func) {
2794  PyErr_SetString(PyExc_AttributeError, "transpile not found in qiskit");
2795  return NULL;
2796  }
2797 
2798  // Transpile: transpile(qc_in, optimization_level=0, basis_gates=['cz', 'u3'], layout_method='sabre')
2799  PyObject* basis_gates = PyList_New(2);
2800  PyList_SetItem(basis_gates, 0, PyUnicode_FromString("cz"));
2801  PyList_SetItem(basis_gates, 1, PyUnicode_FromString("u3"));
2802 
2803  PyObject* kwargs = PyDict_New();
2804  PyDict_SetItemString(kwargs, "optimization_level", PyLong_FromLong(0));
2805  PyDict_SetItemString(kwargs, "basis_gates", basis_gates);
2806  PyDict_SetItemString(kwargs, "layout_method", PyUnicode_FromString("sabre"));
2807 
2808  PyObject* transpile_args = PyTuple_Pack(1, qc_in);
2809  PyObject* qc = PyObject_Call(transpile_func, transpile_args, kwargs);
2810 
2811  Py_DECREF(transpile_args);
2812  Py_DECREF(kwargs);
2813  Py_DECREF(basis_gates);
2814  Py_DECREF(transpile_func);
2815  if (!qc) {
2816  return NULL;
2817  }
2818 
2819  // Print gate counts
2820  PyObject* count_ops_func = PyObject_GetAttrString(qc, "count_ops");
2821  if (count_ops_func) {
2822  PyObject* count_ops_result = PyObject_CallObject(count_ops_func, NULL);
2823  Py_DECREF(count_ops_func);
2824  if (count_ops_result) {
2825  printf("Gate counts in the imported Qiskit transpiled quantum circuit: ");
2826  PyObject_Print(count_ops_result, stdout, 0);
2827  printf("\n");
2828  Py_DECREF(count_ops_result);
2829  }
2830  }
2831 
2832  // Get circuit data
2833  PyObject* qc_data_attr = PyObject_GetAttrString(qc, "data");
2834  PyObject* qc_qubits_attr = PyObject_GetAttrString(qc, "qubits");
2835  PyObject* qc_num_qubits_attr = PyObject_GetAttrString(qc, "num_qubits");
2836  if (!qc_data_attr || !qc_qubits_attr || !qc_num_qubits_attr) {
2837  Py_XDECREF(qc_data_attr);
2838  Py_XDECREF(qc_qubits_attr);
2839  Py_XDECREF(qc_num_qubits_attr);
2840  Py_DECREF(qc);
2841  return NULL;
2842  }
2843 
2844  int register_size = PyLong_AsLong(qc_num_qubits_attr);
2845  Py_DECREF(qc_num_qubits_attr);
2846 
2847  // Import Circuit_Wrapper
2848  PyObject* circuit_wrapper_module = PyImport_ImportModule("squander.gates.qgd_Circuit_Wrapper");
2849  if (!circuit_wrapper_module) {
2850  Py_DECREF(qc_data_attr);
2851  Py_DECREF(qc_qubits_attr);
2852  Py_DECREF(qc);
2853  return NULL;
2854  }
2855 
2856  PyObject* circuit_wrapper_class = PyObject_GetAttrString(circuit_wrapper_module, "qgd_Circuit_Wrapper");
2857  Py_DECREF(circuit_wrapper_module);
2858  if (!circuit_wrapper_class) {
2859  Py_DECREF(qc_data_attr);
2860  Py_DECREF(qc_qubits_attr);
2861  Py_DECREF(qc);
2862  return NULL;
2863  }
2864 
2865  // Create main circuit: Circuit_ret = qgd_Circuit_Wrapper(register_size)
2866  PyObject* circuit_ret_args = PyTuple_Pack(1, PyLong_FromLong(register_size));
2867  PyObject* Circuit_ret_result = PyObject_CallObject(circuit_wrapper_class, circuit_ret_args);
2868  Py_DECREF(circuit_ret_args);
2869  Py_DECREF(circuit_wrapper_class);
2870  if (!Circuit_ret_result) {
2871  Py_DECREF(qc_data_attr);
2872  Py_DECREF(qc_qubits_attr);
2873  Py_DECREF(qc);
2874  return NULL;
2875  }
2876 
2877  // Create dictionary for single qubit gates: single_qubit_gates[qubit] = []
2878  PyObject* single_qubit_gates = PyDict_New();
2879  for (int idx = 0; idx < register_size; idx++) {
2880  PyObject* key = PyLong_FromLong(idx);
2881  PyObject* value = PyList_New(0);
2882  PyDict_SetItem(single_qubit_gates, key, value);
2883  Py_DECREF(key);
2884  Py_DECREF(value);
2885  }
2886 
2887  PyObject* optimized_parameters = PyList_New(0);
2888 
2889  // Process gates from qc.data
2890  Py_ssize_t qc_data_attr_size = PyList_Size(qc_data_attr);
2891  for (Py_ssize_t i = 0; i < qc_data_attr_size; i++) {
2892  PyObject* gate = PyList_GetItem(qc_data_attr, i);
2893  PyObject* gate_operation = PyObject_GetAttrString(gate, "operation");
2894  PyObject* gate_qubits = PyObject_GetAttrString(gate, "qubits");
2895  if (!gate_operation || !gate_qubits) {
2896  Py_XDECREF(gate_operation);
2897  Py_XDECREF(gate_qubits);
2898  continue;
2899  }
2900 
2901  PyObject* gate_operation_name_attr = PyObject_GetAttrString(gate_operation, "name");
2902  const char* name = PyUnicode_AsUTF8(gate_operation_name_attr);
2903 
2904  if (strcmp(name, "u3") == 0) {
2905  // Get qubit index
2906  PyObject* index_func = PyObject_GetAttrString(qc_qubits_attr, "index");
2907 
2908  PyObject* index_args = PyTuple_Pack(1, PyList_GetItem(gate_qubits, 0));
2909  PyObject* index_result = PyObject_CallObject(index_func, index_args);
2910  Py_DECREF(index_func);
2911  Py_DECREF(index_args);
2912 
2913  long qubit = PyLong_AsLong(index_result);
2914  Py_DECREF(index_result);
2915 
2916  // Store u3 gate info
2917  PyObject* gate_info_dict = PyDict_New();
2918  PyObject* gate_operation_params_attr = PyObject_GetAttrString(gate_operation, "params");
2919  PyDict_SetItemString(gate_info_dict, "params", gate_operation_params_attr);
2920  PyDict_SetItemString(gate_info_dict, "type", PyUnicode_FromString("u3"));
2921  Py_DECREF(gate_operation_params_attr);
2922 
2923  PyObject* qubit_list = PyDict_GetItem(single_qubit_gates, PyLong_FromLong(qubit));
2924  PyList_Append(qubit_list, gate_info_dict);
2925  Py_DECREF(gate_info_dict);
2926  } else if (strcmp(name, "cz") == 0) {
2927  // Get qubit indices
2928  PyObject* index_func = PyObject_GetAttrString(qc_qubits_attr, "index");
2929 
2930  PyObject* index_args0 = PyTuple_Pack(1, PyList_GetItem(gate_qubits, 0));
2931  PyObject* index_args0_result = PyObject_CallObject(index_func, index_args0);
2932  Py_DECREF(index_args0);
2933 
2934  PyObject* index_args1 = PyTuple_Pack(1, PyList_GetItem(gate_qubits, 1));
2935  PyObject* index_args1_result = PyObject_CallObject(index_func, index_args1);
2936  Py_DECREF(index_args1);
2937  Py_DECREF(index_func);
2938 
2939  long qubit0 = PyLong_AsLong(index_args0_result);
2940  long qubit1 = PyLong_AsLong(index_args1_result);
2941  Py_DECREF(index_args0_result);
2942  Py_DECREF(index_args1_result);
2943 
2944  // Create layer
2945  PyObject* layer_args = PyTuple_Pack(1, PyLong_FromLong(register_size));
2946  PyObject* circuit_wrapper_module2 = PyImport_ImportModule("squander.gates.qgd_Circuit_Wrapper");
2947  PyObject* circuit_wrapper_class2 = PyObject_GetAttrString(circuit_wrapper_module2, "qgd_Circuit_Wrapper");
2948  Py_DECREF(circuit_wrapper_module2);
2949 
2950  PyObject* Layer = PyObject_CallObject(circuit_wrapper_class2, layer_args);
2951  Py_DECREF(layer_args);
2952  Py_DECREF(circuit_wrapper_class2);
2953 
2954  // Add u3 gates for qubit0
2955  PyObject* qubit0_list = PyDict_GetItem(single_qubit_gates, PyLong_FromLong(qubit0));
2956  if (qubit0_list && PyList_Size(qubit0_list) > 0) {
2957  PyObject* gate0 = PyList_GetItem(qubit0_list, 0);
2958  PyList_SetSlice(qubit0_list, 0, 1, NULL); // pop first element
2959 
2960  PyObject* add_u3_func = PyObject_GetAttrString(Layer, "add_U3");
2961  PyObject* add_u3_args = Py_BuildValue("(iOOO)", qubit0, Py_True, Py_True, Py_True);
2962  PyObject_CallObject(add_u3_func, add_u3_args);
2963  Py_DECREF(add_u3_func);
2964  Py_DECREF(add_u3_args);
2965 
2966  // Add parameters (reversed)
2967  PyObject* params = PyDict_GetItemString(gate0, "params");
2968  PyObject* reversed_params = PyList_New(0);
2969  for (Py_ssize_t j = PyList_Size(params) - 1; j >= 0; j--) {
2970  PyList_Append(reversed_params, PyList_GetItem(params, j));
2971  }
2972  for (Py_ssize_t j = 0; j < PyList_Size(reversed_params); j++) {
2973  PyList_Append(optimized_parameters, PyList_GetItem(reversed_params, j));
2974  }
2975  Py_DECREF(reversed_params);
2976 
2977  // Divide last parameter by 2
2978  Py_ssize_t last_idx = PyList_Size(optimized_parameters) - 1;
2979  PyObject* last_param = PyList_GetItem(optimized_parameters, last_idx);
2980  double val = PyFloat_AsDouble(last_param) / 2.0;
2981  PyList_SetItem(optimized_parameters, last_idx, PyFloat_FromDouble(val));
2982  }
2983 
2984  // Add u3 gates for qubit1
2985  PyObject* qubit1_list = PyDict_GetItem(single_qubit_gates, PyLong_FromLong(qubit1));
2986  if (qubit1_list && PyList_Size(qubit1_list) > 0) {
2987  PyObject* gate1 = PyList_GetItem(qubit1_list, 0);
2988  PyList_SetSlice(qubit1_list, 0, 1, NULL);
2989 
2990  PyObject* add_u3_func = PyObject_GetAttrString(Layer, "add_U3");
2991  PyObject* u3_args = Py_BuildValue("(iOOO)", qubit1, Py_True, Py_True, Py_True);
2992  PyObject_CallObject(add_u3_func, u3_args);
2993  Py_DECREF(add_u3_func);
2994  Py_DECREF(u3_args);
2995 
2996  PyObject* params = PyDict_GetItemString(gate1, "params");
2997  PyObject* reversed_params = PyList_New(0);
2998  for (Py_ssize_t j = PyList_Size(params) - 1; j >= 0; j--) {
2999  PyList_Append(reversed_params, PyList_GetItem(params, j));
3000  }
3001  for (Py_ssize_t j = 0; j < PyList_Size(reversed_params); j++) {
3002  PyList_Append(optimized_parameters, PyList_GetItem(reversed_params, j));
3003  }
3004  Py_DECREF(reversed_params);
3005 
3006  Py_ssize_t last_idx = PyList_Size(optimized_parameters) - 1;
3007  PyObject* last_param = PyList_GetItem(optimized_parameters, last_idx);
3008  double val = PyFloat_AsDouble(last_param) / 2.0;
3009  PyList_SetItem(optimized_parameters, last_idx, PyFloat_FromDouble(val));
3010  }
3011 
3012  // Add RX, adaptive, RZ, RX sequence
3013  PyObject* qubit0_obj = PyLong_FromLong(qubit0);
3014  PyObject* qubit1_obj = PyLong_FromLong(qubit1);
3015 
3016  PyObject* add_rx_func = PyObject_GetAttrString(Layer, "add_RX");
3017  PyObject* add_rx_arg = PyTuple_Pack(1, qubit0_obj);
3018  PyObject_CallObject(add_rx_func, add_rx_arg);
3019  Py_DECREF(add_rx_func);
3020  Py_DECREF(add_rx_arg);
3021 
3022  PyObject* add_adaptive_func = PyObject_GetAttrString(Layer, "add_adaptive");
3023  PyObject* add_adaptive_args = PyTuple_Pack(2, qubit0_obj, qubit1_obj);
3024  PyObject_CallObject(add_adaptive_func, add_adaptive_args);
3025  Py_DECREF(add_adaptive_func);
3026  Py_DECREF(add_adaptive_args);
3027 
3028  PyObject* add_rz_func = PyObject_GetAttrString(Layer, "add_RZ");
3029  PyObject* add_rz_arg = PyTuple_Pack(1, qubit1_obj);
3030  PyObject_CallObject(add_rz_func, add_rz_arg);
3031  Py_DECREF(add_rz_func);
3032  Py_DECREF(add_rz_arg);
3033 
3034  PyObject* add_rx_func_2 = PyObject_GetAttrString(Layer, "add_RX");
3035  PyObject* add_rx_arg_2 = PyTuple_Pack(1, qubit0_obj);
3036  PyObject_CallObject(add_rx_func_2, add_rx_arg_2);
3037  Py_DECREF(add_rx_func_2);
3038  Py_DECREF(add_rx_arg_2);
3039 
3040  Py_DECREF(qubit0_obj);
3041  Py_DECREF(qubit1_obj);
3042 
3043  // Add hardcoded parameters
3044  PyList_Append(optimized_parameters, PyFloat_FromDouble(M_PI / 4.0));
3045  PyList_Append(optimized_parameters, PyFloat_FromDouble(M_PI / 2.0));
3046  PyList_Append(optimized_parameters, PyFloat_FromDouble(-M_PI / 2.0));
3047  PyList_Append(optimized_parameters, PyFloat_FromDouble(-M_PI / 4.0));
3048 
3049  // Add layer to circuit
3050  PyObject* add_circuit_func = PyObject_GetAttrString(Circuit_ret_result, "add_Circuit");
3051  PyObject* add_circuit_args = PyTuple_Pack(1, Layer);
3052  PyObject_CallObject(add_circuit_func, add_circuit_args);
3053  Py_DECREF(add_circuit_func);
3054  Py_DECREF(add_circuit_args);
3055  Py_DECREF(Layer);
3056  }
3057  Py_DECREF(gate_operation_name_attr);
3058  Py_DECREF(gate_operation);
3059  Py_DECREF(gate_qubits);
3060  }
3061 
3062  // Add remaining single qubit gates
3063  PyObject* circuit_module = PyImport_ImportModule("squander.gates.qgd_Circuit");
3064  PyObject* circuit_class = PyObject_GetAttrString(circuit_module, "qgd_Circuit");
3065  Py_DECREF(circuit_module);
3066 
3067  PyObject* final_layer_args = PyTuple_Pack(1, PyLong_FromLong(register_size));
3068  PyObject* final_layer_result = PyObject_CallObject(circuit_class, final_layer_args);
3069  Py_DECREF(circuit_class);
3070  Py_DECREF(final_layer_args);
3071 
3072  for (int qubit = 0; qubit < register_size; qubit++) {
3073  PyObject* gates_list = PyDict_GetItem(single_qubit_gates, PyLong_FromLong(qubit));
3074  Py_ssize_t gates_list_size = PyList_Size(gates_list);
3075 
3076  for (Py_ssize_t j = 0; j < gates_list_size; j++) {
3077  PyObject* gate_obj = PyList_GetItem(gates_list, j);
3078  PyObject* gate_obj_type = PyDict_GetItemString(gate_obj, "type");
3079  const char* gate_obj_type_str = PyUnicode_AsUTF8(gate_obj_type);
3080 
3081  if (strcmp(gate_obj_type_str, "u3") == 0) {
3082  PyObject* add_u3_func = PyObject_GetAttrString(final_layer_result, "add_U3");
3083  PyObject* add_u3_args = Py_BuildValue("(iOOO)", qubit, Py_True, Py_True, Py_True);
3084  PyObject_CallObject(add_u3_func, add_u3_args);
3085  Py_DECREF(add_u3_func);
3086  Py_DECREF(add_u3_args);
3087 
3088  PyObject* gate_obj_params = PyDict_GetItemString(gate_obj, "params");
3089  PyObject* reversed_params = PyList_New(0);
3090  for (Py_ssize_t k = PyList_Size(gate_obj_params) - 1; k >= 0; k--) {
3091  PyList_Append(reversed_params, PyList_GetItem(gate_obj_params, k));
3092  }
3093 
3094  // Convert parameters to float and append
3095  for (Py_ssize_t k = 0; k < PyList_Size(reversed_params); k++) {
3096  PyObject* param = PyList_GetItem(reversed_params, k);
3097  PyObject* param_float = PyFloat_FromDouble(PyFloat_AsDouble(param));
3098  PyList_Append(optimized_parameters, param_float);
3099  Py_DECREF(param_float);
3100  }
3101  Py_DECREF(reversed_params);
3102 
3103  // Divide last parameter by 2
3104  Py_ssize_t optimized_parameters_last_idx = PyList_Size(optimized_parameters) - 1;
3105  PyObject* optimized_parameters_last_param = PyList_GetItem(optimized_parameters, optimized_parameters_last_idx);
3106  double val = PyFloat_AsDouble(optimized_parameters_last_param) / 2.0;
3107  PyList_SetItem(optimized_parameters, optimized_parameters_last_idx, PyFloat_FromDouble(val));
3108  }
3109  }
3110  }
3111 
3112  PyObject* add_final_circuit_func = PyObject_GetAttrString(Circuit_ret_result, "add_Circuit");
3113  PyObject* add_final_circuit_args = PyTuple_Pack(1, final_layer_result);
3114  PyObject_CallObject(add_final_circuit_func, add_final_circuit_args);
3115  Py_DECREF(add_final_circuit_func);
3116  Py_DECREF(add_final_circuit_args);
3117  Py_DECREF(final_layer_result);
3118 
3119  // Convert parameters to numpy array and flip
3120  PyObject* numpy_module = PyImport_ImportModule("numpy");
3121  PyObject* numpy_asarray_func = PyObject_GetAttrString(numpy_module, "asarray");
3122  PyObject* numpy_flip_func = PyObject_GetAttrString(numpy_module, "flip");
3123  Py_DECREF(numpy_module);
3124 
3125  PyObject* dtype_dict = PyDict_New();
3126  PyDict_SetItemString(dtype_dict, "dtype", (PyObject*)&PyFloat_Type);
3127  PyObject* numpy_asarray_args = PyTuple_Pack(1, optimized_parameters);
3128  PyObject* numpy_asarray_result = PyObject_Call(numpy_asarray_func, numpy_asarray_args, dtype_dict);
3129  Py_DECREF(numpy_asarray_func);
3130  Py_DECREF(numpy_asarray_args);
3131  Py_DECREF(dtype_dict);
3132 
3133  PyObject* numpt_flip_args = PyTuple_Pack(2, numpy_asarray_result, PyLong_FromLong(0));
3134  PyObject* numpt_flip_result = PyObject_CallObject(numpy_flip_func, numpt_flip_args);
3135  Py_DECREF(numpy_flip_func);
3136  Py_DECREF(numpt_flip_args);
3137  Py_DECREF(numpy_asarray_result);
3138 
3139  // Set gate structure and parameters
3140  PyObject* set_gate_structure_args = PyTuple_Pack(2, Circuit_ret_result, numpt_flip_result);
3141  PyObject* set_gate_structure_result = qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure(self, set_gate_structure_args);
3142  Py_DECREF(set_gate_structure_args);
3143  Py_DECREF(Circuit_ret_result);
3144  Py_DECREF(numpt_flip_result);
3145  Py_DECREF(optimized_parameters);
3146  Py_DECREF(single_qubit_gates);
3147  Py_DECREF(qc_data_attr);
3148  Py_DECREF(qc_qubits_attr);
3149  Py_DECREF(qc);
3150  if (!set_gate_structure_result) {
3151  return NULL;
3152  }
3153  Py_DECREF(set_gate_structure_result);
3154 
3155  Py_RETURN_NONE;
3156 }
3157 
3163 static PyObject*
3165 {
3166  PyObject* qc_in = NULL;
3167  if (!PyArg_ParseTuple(args, "O", &qc_in)) {
3168  return NULL;
3169  }
3170  bool is_adaptive = (dynamic_cast<N_Qubit_Decomposition_adaptive*>(self->decomp) != nullptr);
3171  if (is_adaptive) {
3173  } else {
3175  }
3176 }
3177 
3178 
3179 
3180 
3181 
3183 
3184 extern "C"
3185 {
3186 
3191 #define DECOMPOSITION_WRAPPER_BASE_METHODS \
3192  {"Start_Decomposition", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Start_Decomposition, METH_VARARGS | METH_KEYWORDS, \
3193  "Method to start the decomposition"}, \
3194  {"get_Gate_Num", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Gate_Num, METH_NOARGS, \
3195  "Method to get the number of decomposing gates"}, \
3196  {"get_Optimized_Parameters", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Optimized_Parameters, METH_NOARGS, \
3197  "Method to get the array of optimized parameters"}, \
3198  {"get_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Circuit, METH_NOARGS, \
3199  "Method to get the incorporated circuit"}, \
3200  {"List_Gates", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_List_Gates, METH_NOARGS, \
3201  "Call to print the decomposing unitaries on standard output"}, \
3202  {"set_Max_Layer_Num", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Max_Layer_Num, METH_VARARGS, \
3203  "Set the maximal number of layers used in the subdecomposition"}, \
3204  {"set_Iteration_Loops", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Iteration_Loops, METH_VARARGS, \
3205  "Set the number of iteration loops during the subdecomposition"}, \
3206  {"set_Verbose", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Verbose, METH_VARARGS, \
3207  "Set the verbosity of the decomposition class"}, \
3208  {"set_Debugfile", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Debugfile, METH_VARARGS, \
3209  "Set the debugfile name of the decomposition class"}, \
3210  {"Reorder_Qubits", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Reorder_Qubits, METH_VARARGS, \
3211  "Method to reorder the qubits in the decomposition class"}, \
3212  {"set_Optimization_Tolerance", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Optimization_Tolerance, METH_VARARGS, \
3213  "Wrapper method to set the optimization tolerance"}, \
3214  {"set_Convergence_Threshold", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Convergence_Threshold, METH_VARARGS, \
3215  "Wrapper method to set the threshold of convergence"}, \
3216  {"set_Optimization_Blocks", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Optimization_Blocks, METH_VARARGS, \
3217  "Wrapper method to set the number of gate blocks to be optimized"}, \
3218  {"get_Parameter_Num", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Parameter_Num, METH_NOARGS, \
3219  "Get the number of free parameters"}, \
3220  {"set_Optimized_Parameters", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Optimized_Parameters, METH_VARARGS, \
3221  "Set the optimized parameters"}, \
3222  {"get_Num_of_Iters", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Num_of_Iters, METH_NOARGS, \
3223  "Get the number of iterations"}, \
3224  {"export_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_export_Unitary, METH_VARARGS, \
3225  "Export unitary matrix"}, \
3226  {"export_Gate_Structure_to_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_export_Gate_Structure_to_Binary, METH_VARARGS, \
3227  "Export the current gate structure and optimized parameters into Squander binary format"}, \
3228  {"get_Project_Name", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Project_Name, METH_NOARGS, \
3229  "Get the name of SQUANDER project"}, \
3230  {"set_Project_Name", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Project_Name, METH_VARARGS, \
3231  "Set the name of SQUANDER project"}, \
3232  {"get_Global_Phase", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Global_Phase, METH_NOARGS, \
3233  "Call to get global phase"}, \
3234  {"set_Global_Phase", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Global_Phase, METH_VARARGS, \
3235  "Set global phase"}, \
3236  {"apply_Global_Phase_Factor", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_apply_Global_Phase_Factor, METH_NOARGS, \
3237  "Apply global phase factor"}, \
3238  {"get_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Unitary, METH_NOARGS, \
3239  "Get Unitary Matrix"}, \
3240  {"set_Optimizer", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Optimizer, METH_VARARGS | METH_KEYWORDS, \
3241  "Set the optimizer method"}, \
3242  {"set_Max_Iterations", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Max_Iterations, METH_VARARGS | METH_KEYWORDS, \
3243  "Set the number of maximum iterations"}, \
3244  {"get_Matrix", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Matrix, METH_VARARGS | METH_KEYWORDS, \
3245  "Method to retrieve the unitary of the circuit"}, \
3246  {"set_Cost_Function_Variant", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Cost_Function_Variant, METH_VARARGS | METH_KEYWORDS, \
3247  "Set the cost function variant"}, \
3248  {"Optimization_Problem", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem, METH_VARARGS, \
3249  "Optimization problem method"}, \
3250  {"Optimization_Problem_Combined_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Combined_Unitary, METH_VARARGS, \
3251  "Optimization problem combined unitary method"}, \
3252  {"Optimization_Problem_Grad", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Grad, METH_VARARGS, \
3253  "Optimization problem gradient method"}, \
3254  {"Optimization_Problem_Combined", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Combined, METH_VARARGS, \
3255  "Optimization problem combined method"}, \
3256  {"Optimization_Problem_Batch", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Optimization_Problem_Batch, METH_VARARGS, \
3257  "Optimization problem batch method"}, \
3258  {"Upload_Umtx_to_DFE", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Upload_Umtx_to_DFE, METH_NOARGS, \
3259  "Upload unitary matrix to DFE"}, \
3260  {"get_Trace_Offset", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Trace_Offset, METH_NOARGS, \
3261  "Get trace offset"}, \
3262  {"set_Trace_Offset", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Trace_Offset, METH_VARARGS | METH_KEYWORDS, \
3263  "Set trace offset"}, \
3264  {"get_Decomposition_Error", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Decomposition_Error, METH_NOARGS, \
3265  "Get decomposition error"}, \
3266  {"get_Second_Renyi_Entropy", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Second_Renyi_Entropy, METH_VARARGS, \
3267  "Get second Renyi entropy"}, \
3268  {"get_Qbit_Num", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Qbit_Num, METH_NOARGS, \
3269  "Get the number of qubits"}, \
3270  {"set_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure, METH_VARARGS, \
3271  "Set custom gate structure for decomposition: set_Gate_Structure(circuit)"}, \
3272  {"add_Finalyzing_Layer_To_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_add_Finalyzing_Layer_To_Gate_Structure, METH_NOARGS, \
3273  "Add finalizing layer to gate structure"}, \
3274  {"get_Gates", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Gates, METH_NOARGS, \
3275  "Get gates as a list of dictionaries"}, \
3276  {"get_Qiskit_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Qiskit_Circuit, METH_NOARGS, \
3277  "Export decomposition to Qiskit QuantumCircuit format"}, \
3278  {"get_Cirq_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Cirq_Circuit, METH_NOARGS, \
3279  "Export decomposition to Cirq Circuit format"}, \
3280  {"import_Qiskit_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_import_Qiskit_Circuit, METH_VARARGS, \
3281  "Import Qiskit QuantumCircuit"}, \
3282 
3283 
3287 static PyMethodDef qgd_N_Qubit_Decomposition_methods[] = {
3289  {"set_Identical_Blocks", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Identical_Blocks, METH_VARARGS,
3290  "Set the number of identical successive blocks during subdecomposition"},
3291  {NULL}
3292 };
3293 
3299  {"get_Initial_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_get_Initial_Circuit, METH_NOARGS,
3300  "Method to get initial circuit in decomposition"},
3301  {"Compress_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Compress_Circuit, METH_NOARGS,
3302  "Method to compress gate structure"},
3303  {"Remove_Trivial_CRY_Gates", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Remove_Trivial_CRY_Gates, METH_NOARGS,
3304  "Method to remove blocks containing a trivial CRY gate (near identity); U3 gates are merged with subsequent gates"},
3305  {"Finalize_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_Finalize_Circuit, METH_VARARGS | METH_KEYWORDS,
3306  "Method to finalize the decomposition"},
3307  {"set_Gate_Structure_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Gate_Structure_From_Binary, METH_VARARGS,
3308  "Set gate structure from binary"},
3309  {"add_Gate_Structure_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_add_Gate_Structure_From_Binary, METH_VARARGS,
3310  "Add gate structure from binary"},
3311  {"set_Unitary_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Unitary_From_Binary, METH_VARARGS,
3312  "Set unitary from binary"},
3313  {"add_Adaptive_Layers", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_add_Adaptive_Layers, METH_NOARGS,
3314  "Call to add adaptive layers to the gate structure"},
3315  {"add_Layer_To_Imported_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_add_Layer_To_Imported_Gate_Structure, METH_VARARGS,
3316  "Add layer to imported gate structure"},
3317  {"apply_Imported_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_apply_Imported_Gate_Structure, METH_NOARGS,
3318  "Apply imported gate structure"},
3319  {"set_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Unitary, METH_VARARGS,
3320  "Call to set unitary matrix"},
3321  {NULL}
3322 };
3323 
3329  {NULL}
3330 };
3331 
3337  {"set_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Unitary, METH_VARARGS,
3338  "Call to set unitary matrix"},
3339  {NULL}
3340 };
3341 
3347  {"set_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_Wrapper_set_Unitary, METH_VARARGS,
3348  "Call to set unitary matrix"},
3349  {NULL}
3350 };
3351 
3352 #define decomposition_wrapper_type_template(decomp_class) \
3353 static PyTypeObject qgd_##decomp_class##_Wrapper_Type = { \
3354  PyVarObject_HEAD_INIT(NULL, 0) \
3355  "qgd_N_Qubit_Decomposition_Wrapper." #decomp_class, /* tp_name */ \
3356  sizeof(qgd_N_Qubit_Decomposition_Wrapper), /* tp_basicsize */ \
3357  0, /* tp_itemsize */ \
3358  (destructor) qgd_N_Qubit_Decomposition_Wrapper_dealloc, /* tp_dealloc */ \
3359  0, /* tp_vectorcall_offset */ \
3360  0, /* tp_getattr */ \
3361  0, /* tp_setattr */ \
3362  0, /* tp_as_async */ \
3363  0, /* tp_repr */ \
3364  0, /* tp_as_number */ \
3365  0, /* tp_as_sequence */ \
3366  0, /* tp_as_mapping */ \
3367  0, /* tp_hash */ \
3368  0, /* tp_call */ \
3369  0, /* tp_str */ \
3370  0, /* tp_getattro */ \
3371  0, /* tp_setattro */ \
3372  0, /* tp_as_buffer */ \
3373  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ \
3374  #decomp_class " decomposition wrapper", /* tp_doc */ \
3375  0, /* tp_traverse */ \
3376  0, /* tp_clear */ \
3377  0, /* tp_richcompare */ \
3378  0, /* tp_weaklistoffset */ \
3379  0, /* tp_iter */ \
3380  0, /* tp_iternext */ \
3381  qgd_##decomp_class##_methods, /* tp_methods */ \
3382  0, /* tp_members */ \
3383  0, /* tp_getset */ \
3384  0, /* tp_base */ \
3385  0, /* tp_dict */ \
3386  0, /* tp_descr_get */ \
3387  0, /* tp_descr_set */ \
3388  0, /* tp_dictoffset */ \
3389  (initproc) qgd_##decomp_class##_Wrapper_init, /* tp_init */ \
3390  0, /* tp_alloc */ \
3391  (newfunc) qgd_N_Qubit_Decomposition_Wrapper_new, /* tp_new */ \
3392  0, /* tp_free */ \
3393  0, /* tp_is_gc */ \
3394  0, /* tp_bases */ \
3395  0, /* tp_mro */ \
3396  0, /* tp_cache */ \
3397  0, /* tp_subclasses */ \
3398  0, /* tp_weaklist */ \
3399  0, /* tp_del */ \
3400  0, /* tp_version_tag */ \
3401  0, /* tp_finalize */ \
3402  0, /* tp_vectorcall */ \
3403 };
3404 
3410 
3411 
3416 static PyModuleDef qgd_N_Qubit_Decompositions_Wrapper_Module = {
3417  PyModuleDef_HEAD_INIT,
3418  "qgd_N_Qubit_Decompositions_Wrapper", /* m_name */
3419  "Python binding for N-Qubit Decompositions wrapper module", /* m_doc */
3420  -1, /* m_size */
3421  0, /* m_methods */
3422  0, /* m_slots */
3423  0, /* m_traverse */
3424  0, /* m_clear */
3425  0, /* m_free */
3426 };
3427 
3428 #define Py_INCREF_template(decomp_name) \
3429  Py_INCREF(&qgd_##decomp_name##_Wrapper_Type); \
3430  if (PyModule_AddObject(m, "qgd_" #decomp_name, (PyObject *) &qgd_##decomp_name##_Wrapper_Type) < 0) { \
3431  Py_DECREF(&qgd_##decomp_name##_Wrapper_Type); \
3432  Py_DECREF(m); \
3433  return NULL; \
3434  }
3435 
3439 PyMODINIT_FUNC
3441 {
3442  PyObject *m;
3443 
3444  // initialize numpy
3445  import_array();
3446 
3447  if (PyType_Ready(&qgd_N_Qubit_Decomposition_Wrapper_Type) < 0 ||
3448  PyType_Ready(&qgd_N_Qubit_Decomposition_adaptive_Wrapper_Type) < 0 ||
3449  PyType_Ready(&qgd_N_Qubit_Decomposition_custom_Wrapper_Type) < 0 ||
3450  PyType_Ready(&qgd_N_Qubit_Decomposition_Tree_Search_Wrapper_Type) < 0 ||
3451  PyType_Ready(&qgd_N_Qubit_Decomposition_Tabu_Search_Wrapper_Type) < 0) {
3452  return NULL;
3453  }
3454 
3455  m = PyModule_Create(&qgd_N_Qubit_Decompositions_Wrapper_Module);
3456  if (m == NULL)
3457  return NULL;
3458 
3464 
3465  return m;
3466 }
3467 
3468 } // 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.
virtual void remove_trivial_CRY_gates()
Remove blocks containing a trivial CRY gate from the circuit stored by the class. ...
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.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_Remove_Trivial_CRY_Gates(qgd_N_Qubit_Decomposition_Wrapper *self)
Call to remove blocks containing a trivial CRY gate (near identity); U3 gates are merged with subsequ...
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.
static PyObject * qgd_N_Qubit_Decomposition_Wrapper_export_Gate_Structure_to_Binary(qgd_N_Qubit_Decomposition_Wrapper *self, PyObject *args)
Export the current gate structure and optimized parameters into Squander binary format.
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.
void export_gate_list_to_binary(Matrix_real &parameters, Gates_block *gates_block, const std::string &filename, int verbosity)
Use to export a quantum circuit into binary format.
static PyObject * qgd_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