Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
qgd_N_Qubit_Decomposition_adaptive_Wrapper.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:42:56 2020
3 Copyright 2020 Peter Rakyta, Ph.D.
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see http://www.gnu.org/licenses/.
19 
20 @author: Peter Rakyta, Ph.D.
21 */
22 /*
23 \file qgd_N_Qubit_Decomposition_adaptive_Wrapper.cpp
24 \brief Python interface for the N_Qubit_Decomposition class
25 */
26 
27 #define PY_SSIZE_T_CLEAN
28 
29 
30 #include <Python.h>
31 #include <numpy/arrayobject.h>
32 #include "structmember.h"
33 #include <stdio.h>
35 #include "Gates_block.h"
36 
37 #include "numpy_interface.h"
38 
39 
40 
41 
45 typedef struct qgd_Circuit_Wrapper {
46  PyObject_HEAD
49 
50 
55  PyObject_HEAD
57  PyArrayObject* Umtx;
61 
63 
64 
65 
75 create_N_Qubit_Decomposition_adaptive( Matrix& Umtx, int qbit_num, int level_limit, int level_limit_min, std::vector<matrix_base<int>> topology_in, std::map<std::string, Config_Element>& config, int accelerator_num ) {
76 
77  return new N_Qubit_Decomposition_adaptive( Umtx, qbit_num, level_limit, level_limit_min, topology_in, config, accelerator_num );
78 }
79 
80 
81 
82 
87 void
89 
90  if (instance != NULL ) {
91  delete instance;
92  }
93  return;
94 }
95 
96 
97 
98 
99 
100 
101 extern "C"
102 {
103 
104 
109 static void
111 {
112 
113  if ( self->decomp != NULL ) {
114  // deallocate the instance of class N_Qubit_Decomposition
116  self->decomp = NULL;
117  }
118 
119 
120  if ( self->Umtx != NULL ) {
121  // release the unitary to be decomposed
122  Py_DECREF(self->Umtx);
123  self->Umtx = NULL;
124  }
125 
126  Py_TYPE(self)->tp_free((PyObject *) self);
127 
128 }
129 
134 static PyObject *
136 {
138  self = (qgd_N_Qubit_Decomposition_adaptive_Wrapper *) type->tp_alloc(type, 0);
139  if (self != NULL) {
140 
141  self->decomp = NULL;
142  self->Umtx = NULL;
143 
144  }
145 
146  return (PyObject *) self;
147 }
148 
149 
156 static int
158 {
159  // The tuple of expected keywords
160  static char *kwlist[] = {(char*)"Umtx", (char*)"qbit_num", (char*)"level_limit_min", (char*)"method", (char*)"topology", (char*)"config", (char*)"accelerator_num", NULL};
161 
162  // initiate variables for input arguments
163  PyArrayObject *Umtx_arg = NULL;
164  PyObject *config_arg = NULL;
165  int qbit_num = -1;
166  int level_limit = 0;
167  int level_limit_min = 0;
168  PyObject *topology = NULL;
169  int accelerator_num = 0;
170 
171  // parsing input arguments
172  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OiiiOOi", kwlist,
173  &Umtx_arg, &qbit_num, &level_limit, &level_limit_min, &topology, &config_arg, &accelerator_num))
174  return -1;
175 
176  // convert python object array to numpy C API array
177  if ( Umtx_arg == NULL ) return -1;
178  self->Umtx = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*) Umtx_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
179 
180  // test C-style contiguous memory allocation of the array
181  if ( !PyArray_IS_C_CONTIGUOUS(self->Umtx) ) {
182  std::cout << "Umtx is not memory contiguous" << std::endl;
183  }
184 
185 
186  // create QGD version of the Umtx
187  Matrix Umtx_mtx = numpy2matrix(self->Umtx);
188 
189  // elaborate connectivity topology
190  bool is_None = topology == Py_None;
191  bool is_list = PyList_Check(topology);
192 
193  // Check whether input is a list
194  if (!is_list && !is_None) {
195  printf("Input topology must be a list!\n");
196  return -1;
197  }
198 
199  // create C++ variant of the list
200  std::vector<matrix_base<int>> topology_Cpp;
201 
202  if ( !is_None ) {
203 
204  // get the number of qbubits
205  Py_ssize_t element_num = PyList_GET_SIZE(topology);
206 
207  for ( Py_ssize_t idx=0; idx<element_num; idx++ ) {
208  PyObject *item = PyList_GetItem(topology, idx );
209 
210  // Check whether input is a list
211  if (!PyTuple_Check(item)) {
212  printf("Elements of topology must be a tuple!\n");
213  return -1;
214  }
215 
216  matrix_base<int> item_Cpp(1,2);
217  item_Cpp[0] = (int) PyLong_AsLong( PyTuple_GetItem(item, 0 ) );
218  item_Cpp[1] = (int) PyLong_AsLong( PyTuple_GetItem(item, 1 ) );
219 
220  topology_Cpp.push_back( item_Cpp );
221  }
222  }
223 
224 
225  // parse config and create C++ version of the hyperparameters
226 
227  bool is_dict = PyDict_Check( config_arg );
228  if (!is_dict) {
229  printf("Config object must be a python dictionary!\n");
230  return -1;
231  }
232 
233  // integer type config metadata utilized during the optimization
234  std::map<std::string, Config_Element> config;
235 
236 
237  // keys and values of the config dict
238  PyObject *key, *value;
239  Py_ssize_t pos = 0;
240 
241  while (PyDict_Next(config_arg, &pos, &key, &value)) {
242 
243  // determine the initial guess type
244  PyObject* key_string = PyObject_Str(key);
245  PyObject* key_string_unicode = PyUnicode_AsEncodedString(key_string, "utf-8", "~E~");
246  const char* key_C = PyBytes_AS_STRING(key_string_unicode);
247 
248  std::string key_Cpp( key_C );
249  Config_Element element;
250 
251  if ( PyLong_Check( value ) ) {
252  element.set_property( key_Cpp, PyLong_AsLongLong( value ) );
253  config[ key_Cpp ] = element;
254  }
255  else if ( PyFloat_Check( value ) ) {
256  element.set_property( key_Cpp, PyFloat_AsDouble( value ) );
257  config[ key_Cpp ] = element;
258  }
259  else {
260 
261  }
262 
263  }
264 
265 
266  // create an instance of the class N_Qubit_Decomposition
267  if (qbit_num > 0 ) {
268  try {
269  self->decomp = create_N_Qubit_Decomposition_adaptive( Umtx_mtx, qbit_num, level_limit, level_limit_min, topology_Cpp, config, accelerator_num);
270  }
271  catch (std::string err ) {
272  PyErr_SetString(PyExc_Exception, err.c_str());
273  return -1;
274  }
275  }
276  else {
277  std::cout << "The number of qubits should be given as a positive integer, " << qbit_num << " was given" << std::endl;
278  return -1;
279  }
280 
281 
282 
283  return 0;
284 }
285 
291 static PyObject *
293 {
294 
295  // The tuple of expected keywords
296  static char *kwlist[] = {NULL};
297 
298 
299  // parsing input arguments
300  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|", kwlist))
301  return Py_BuildValue("i", -1);
302 
303  // starting the decomposition
304  try {
305  self->decomp->start_decomposition();
306  }
307  catch (std::string err) {
308  PyErr_SetString(PyExc_Exception, err.c_str());
309  std::cout << err << std::endl;
310  return NULL;
311  }
312  catch(...) {
313  std::string err( "Invalid pointer to decomposition class");
314  PyErr_SetString(PyExc_Exception, err.c_str());
315  return NULL;
316  }
317 
318 
319 
320  return Py_BuildValue("i", 0);
321 
322 }
323 
324 static PyObject *
326 {
327 
328  // starting the decomposition
329  try {
330  self->decomp->get_initial_circuit();
331  }
332  catch (std::string err) {
333  PyErr_SetString(PyExc_Exception, err.c_str());
334  std::cout << err << std::endl;
335  return NULL;
336  }
337  catch(...) {
338  std::string err( "Invalid pointer to decomposition class");
339  PyErr_SetString(PyExc_Exception, err.c_str());
340  return NULL;
341  }
342 
343 
344 
345  return Py_BuildValue("i", 0);
346 
347 }
348 
349 static PyObject *
351 {
352 
353  // starting the decomposition
354  try {
355  self->decomp->compress_circuit();
356  }
357  catch (std::string err) {
358  PyErr_SetString(PyExc_Exception, err.c_str());
359  std::cout << err << std::endl;
360  return NULL;
361  }
362  catch(...) {
363  std::string err( "Invalid pointer to decomposition class");
364  PyErr_SetString(PyExc_Exception, err.c_str());
365  return NULL;
366  }
367 
368 
369 
370  return Py_BuildValue("i", 0);
371 
372 }
373 
374 static PyObject *
376 {
377 
378  // The tuple of expected keywords
379  static char *kwlist[] = {NULL};
380 
381 
382  // parsing input arguments
383  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|", kwlist))
384  return Py_BuildValue("i", -1);
385 
386  // starting the decomposition
387  try {
388  self->decomp->finalize_circuit();
389  }
390  catch (std::string err) {
391  PyErr_SetString(PyExc_Exception, err.c_str());
392  std::cout << err << std::endl;
393  return NULL;
394  }
395  catch(...) {
396  std::string err( "Invalid pointer to decomposition class");
397  PyErr_SetString(PyExc_Exception, err.c_str());
398  return NULL;
399  }
400 
401 
402 
403  return Py_BuildValue("i", 0);
404 
405 }
406 
412 static PyObject *
414 
415  // get the number of gates
416  int ret = self->decomp->get_gate_num();
417 
418 
419  return Py_BuildValue("i", ret);
420 
421 }
422 
423 
424 
425 
426 
427 
428 
433 static PyObject *
435 
436  QGD_Complex16 global_phase_factor_C = self->decomp->get_global_phase_factor();
437  PyObject* global_phase = PyFloat_FromDouble( std::atan2(global_phase_factor_C.imag,global_phase_factor_C.real));
438 
439  return global_phase;
440 
441 }
442 
449 
450  double new_global_phase;
451  if (!PyArg_ParseTuple(args, "|d", &new_global_phase )) return Py_BuildValue("i", -1);
452  self->decomp->set_global_phase(new_global_phase);
453 
454  return Py_BuildValue("i", 0);
455 
456 }
457 
463 
464  // get the number of gates
465  self->decomp->apply_global_phase_factor();
466 
467  return Py_BuildValue("i", 0);
468 
469 }
470 
471 
476 static PyObject *
478 
479 
480  PyObject* qgd_Circuit = PyImport_ImportModule("squander.gates.qgd_Circuit");
481 
482  if ( qgd_Circuit == NULL ) {
483  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.qgd_Circuit" );
484  return NULL;
485  }
486 
487  // retrieve the C++ variant of the flat circuit (flat circuit does not conatain any sub-circuits)
488  Gates_block* circuit = self->decomp->get_flat_circuit();
489 
490 
491 
492  // construct python interfarce for the circuit
493  PyObject* qgd_circuit_Dict = PyModule_GetDict( qgd_Circuit );
494 
495  // 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
496  PyObject* py_circuit_class = PyDict_GetItemString( qgd_circuit_Dict, "qgd_Circuit");
497 
498  // create gate parameters
499  PyObject* qbit_num = Py_BuildValue("i", circuit->get_qbit_num() );
500  PyObject* circuit_input = Py_BuildValue("(O)", qbit_num);
501 
502  PyObject* py_circuit = PyObject_CallObject(py_circuit_class, circuit_input);
503  qgd_Circuit_Wrapper* py_circuit_C = reinterpret_cast<qgd_Circuit_Wrapper*>( py_circuit );
504 
505 
506  // replace the empty circuit with the extracted one
507 
508  delete( py_circuit_C->gate );
509  py_circuit_C->gate = circuit;
510 
511 
512  return py_circuit;
513 
514 }
515 
516 
517 
522 static PyObject *
524 
525  self->decomp->list_gates( 0 );
526 
527  return Py_None;
528 }
529 
530 
531 
532 
537 static PyObject *
539 
540  int parameter_num = self->decomp->get_parameter_num();
541  Matrix_real parameters_mtx(1, parameter_num);
542  double* parameters = parameters_mtx.get_data();
543  self->decomp->get_optimized_parameters(parameters);
544 
545  // convert to numpy array
546  parameters_mtx.set_owner(false);
547  PyObject * parameter_arr = matrix_real_to_numpy( parameters_mtx );
548 
549  return parameter_arr;
550 
551 }
552 
553 
554 
555 
559 static PyObject *
561 
562  int parameter_num = self->decomp->get_parameter_num();
563 
564  return Py_BuildValue("i", parameter_num);
565 }
566 
570 static PyObject *
572 
573  int number_of_iters = self->decomp->get_num_iters();
574 
575  return Py_BuildValue("i", number_of_iters);
576 }
577 
578 
583 static PyObject *
585 
586  PyArrayObject* parameters_arr = NULL;
587 
588 
589  // parsing input arguments
590  if (!PyArg_ParseTuple(args, "|O", &parameters_arr ))
591  return Py_BuildValue("i", -1);
592 
593 
594  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
595  Py_INCREF(parameters_arr);
596  }
597  else {
598  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
599  }
600 
601 
602  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arr );
603 
604  try {
605  self->decomp->set_optimized_parameters(parameters_mtx.get_data(), parameters_mtx.size());
606  }
607  catch (std::string err ) {
608  PyErr_SetString(PyExc_Exception, err.c_str());
609  return NULL;
610  }
611  catch(...) {
612  std::string err( "Invalid pointer to decomposition class");
613  PyErr_SetString(PyExc_Exception, err.c_str());
614  return NULL;
615  }
616 
617 
618 
619  Py_DECREF(parameters_arr);
620 
621  return Py_BuildValue("i", 0);
622 }
623 
624 
625 
626 
627 
632 static PyObject *
634 
635  // initiate variables for input arguments
636  PyObject* max_layer_num;
637 
638  // parsing input arguments
639  if (!PyArg_ParseTuple(args, "|O", &max_layer_num )) return Py_BuildValue("i", -1);
640 
641  // Check whether input is dictionary
642  if (!PyDict_Check(max_layer_num)) {
643  printf("Input must be dictionary!\n");
644  return Py_BuildValue("i", -1);
645  }
646 
647 
648  PyObject* key = NULL;
649  PyObject* value = NULL;
650  Py_ssize_t pos = 0;
651 
652 
653  while (PyDict_Next(max_layer_num, &pos, &key, &value)) {
654 
655  // convert value fron PyObject to int
656  assert(PyLong_Check(value) == 1);
657  int value_int = (int) PyLong_AsLong(value);
658 
659  // convert keylue fron PyObject to int
660  assert(PyLong_Check(key) == 1);
661  int key_int = (int) PyLong_AsLong(key);
662 
663  // set maximal layer nums on the C++ side
664  self->decomp->set_max_layer_num( key_int, value_int );
665 
666  }
667 
668  return Py_BuildValue("i", 0);
669 }
670 
671 
672 
673 
674 
675 
682 static PyObject *
684 
685  // initiate variables for input arguments
686  PyObject* iteration_loops;
687 
688  // parsing input arguments
689  if (!PyArg_ParseTuple(args, "|O", &iteration_loops )) return Py_BuildValue("i", -1);
690 
691  // Check whether input is dictionary
692  if (!PyDict_Check(iteration_loops)) {
693  printf("Input must be dictionary!\n");
694  return Py_BuildValue("i", -1);
695  }
696 
697 
698  PyObject* key = NULL;
699  PyObject* value = NULL;
700  Py_ssize_t pos = 0;
701 
702 
703  while (PyDict_Next(iteration_loops, &pos, &key, &value)) {
704 
705  // convert value fron PyObject to int
706  assert(PyLong_Check(value) == 1);
707  int value_int = (int) PyLong_AsLong(value);
708 
709  // convert keylue fron PyObject to int
710  assert(PyLong_Check(key) == 1);
711  int key_int = (int) PyLong_AsLong(key);
712 
713  // set maximal layer nums on the C++ side
714  self->decomp->set_iteration_loops( key_int, value_int );
715 
716  }
717 
718  return Py_BuildValue("i", 0);
719 }
720 
726 static PyObject *
728 
729  // initiate variables for input arguments
730  int max_iters_input;
731 
732  // parsing input arguments
733  if (!PyArg_ParseTuple(args, "|i", &max_iters_input )) return Py_BuildValue("i", -1);
734 
735 
736  //set the maximum number of iterations
737  self->decomp->set_max_inner_iterations(max_iters_input);
738 
739 
740  return Py_BuildValue("i", 0);
741 }
742 
749 static PyObject *
751 
752  // initiate variables for input arguments
753  int verbose;
754 
755  // parsing input arguments
756  if (!PyArg_ParseTuple(args, "|i", &verbose )) return Py_BuildValue("i", -1);
757 
758 
759  // set maximal layer nums on the C++ side
760  self->decomp->set_verbose( verbose );
761 
762 
763  return Py_BuildValue("i", 0);
764 }
765 
766 
773 static PyObject *
775 
776 
777  PyObject *debugfile = NULL;
778 
779  // parsing input arguments
780  if (!PyArg_ParseTuple(args, "|O", &debugfile )) return Py_BuildValue("s", -1);
781 
782  // determine the debugfile name type
783  PyObject* debugfile_string = PyObject_Str(debugfile);
784  PyObject* debugfile_string_unicode = PyUnicode_AsEncodedString(debugfile_string, "utf-8", "~E~");
785  const char* debugfile_C = PyBytes_AS_STRING(debugfile_string_unicode);
786 
787 
788  Py_XDECREF(debugfile_string);
789  Py_XDECREF(debugfile_string_unicode);
790 
791  // determine the length of the filename and initialize C++ variant of the string
792  Py_ssize_t string_length = PyBytes_Size(debugfile_string_unicode);
793  std::string debugfile_Cpp(debugfile_C, string_length);
794 
795  // set the name of the debugfile on the C++ side
796  self->decomp->set_debugfile( debugfile_Cpp );
797 
798 
799  return Py_BuildValue("s", NULL);
800 }
801 
802 
803 
810 static PyObject *
812 
813  // initiate variables for input arguments
814  double tolerance;
815 
816  // parsing input arguments
817  if (!PyArg_ParseTuple(args, "|d", &tolerance )) return Py_BuildValue("i", -1);
818 
819 
820  // set maximal layer nums on the C++ side
821  self->decomp->set_optimization_tolerance( tolerance );
822 
823 
824  return Py_BuildValue("i", 0);
825 }
826 
827 
828 
835 static PyObject *
837 
838  // initiate variables for input arguments
839  double threshold;
840 
841  // parsing input arguments
842  if (!PyArg_ParseTuple(args, "|d", &threshold )) return Py_BuildValue("i", -1);
843 
844 
845  // set maximal layer nums on the C++ side
846  self->decomp->set_convergence_threshold( threshold );
847 
848  return Py_BuildValue("i", 0);
849 }
850 
851 
858 static PyObject *
860 
861  // initiate variables for input arguments
862  double optimization_block;
863 
864  // parsing input arguments
865  if (!PyArg_ParseTuple(args, "|d", &optimization_block )) return Py_BuildValue("i", -1);
866 
867 
868  // set maximal layer nums on the C++ side
869  self->decomp->set_optimization_blocks( optimization_block );
870 
871 
872  return Py_BuildValue("i", 0);
873 }
874 
875 
876 
882 static PyObject *
884 
885  // initiate variables for input arguments
886  PyObject* gate_structure_py;
887 
888  // parsing input arguments
889  if (!PyArg_ParseTuple(args, "|O", &gate_structure_py )) return Py_BuildValue("i", -1);
890 
891 
892  // convert gate structure from PyObject to qgd_Circuit_Wrapper
893  qgd_Circuit_Wrapper* qgd_op_block = (qgd_Circuit_Wrapper*) gate_structure_py;
894 
895  try {
896  self->decomp->set_custom_gate_structure( qgd_op_block->gate );
897  }
898  catch (std::string err ) {
899  PyErr_SetString(PyExc_Exception, err.c_str());
900  return NULL;
901  }
902  catch(...) {
903  std::string err( "Invalid pointer to decomposition class");
904  PyErr_SetString(PyExc_Exception, err.c_str());
905  return NULL;
906  }
907 
908 
909  return Py_BuildValue("i", 0);
910 
911 
912 }
913 
914 
915 
919 static PyObject *
921 
922 
923 
924  // initiate variables for input arguments
925  PyObject* filename_py=NULL;
926 
927  // parsing input arguments
928  if (!PyArg_ParseTuple(args, "|O", &filename_py )) return Py_BuildValue("i", -1);
929 
930  // determine the optimizaton method
931  PyObject* filename_string = PyObject_Str(filename_py);
932  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
933  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
934  std::string filename_str( filename_C );
935 
936 
937  try {
938  self->decomp->add_adaptive_gate_structure( filename_str );
939  }
940  catch (std::string err ) {
941  PyErr_SetString(PyExc_Exception, err.c_str());
942  return NULL;
943  }
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 
951  return Py_BuildValue("i", 0);
952 
953 }
954 
955 
956 
960 static PyObject *
962 
963 
964 
965  // initiate variables for input arguments
966  PyObject* filename_py=NULL;
967 
968  // parsing input arguments
969  if (!PyArg_ParseTuple(args, "|O", &filename_py )) return Py_BuildValue("i", -1);
970 
971  // determine the optimizaton method
972  PyObject* filename_string = PyObject_Str(filename_py);
973  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
974  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
975  std::string filename_str( filename_C );
976 
977 
978  try {
979  self->decomp->set_adaptive_gate_structure( filename_str );
980  }
981  catch (std::string err ) {
982  PyErr_SetString(PyExc_Exception, err.c_str());
983  return NULL;
984  }
985  catch(...) {
986  std::string err( "Invalid pointer to decomposition class");
987  PyErr_SetString(PyExc_Exception, err.c_str());
988  return NULL;
989  }
990 
991 
992 
993  return Py_BuildValue("i", 0);
994 
995 }
996 
997 static PyObject *
999  // initiate variables for input arguments
1000  PyObject* filename_py=NULL;
1001 
1002  // parsing input arguments
1003  if (!PyArg_ParseTuple(args, "|O", &filename_py )) return Py_BuildValue("i", -1);
1004 
1005  PyObject* filename_string = PyObject_Str(filename_py);
1006  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
1007  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
1008  std::string filename_str( filename_C );
1009 
1010 
1011  try {
1012  self->decomp->set_unitary_from_file( filename_str );
1013  }
1014  catch (std::string err ) {
1015  PyErr_SetString(PyExc_Exception, err.c_str());
1016  return NULL;
1017  }
1018  catch(...) {
1019  std::string err( "Invalid pointer to decomposition class");
1020  PyErr_SetString(PyExc_Exception, err.c_str());
1021  return NULL;
1022  }
1023 
1024 
1025  return Py_BuildValue("i", 0);
1026 }
1027 
1031 static PyObject *
1033 
1034 
1035  try {
1036  self->decomp->add_finalyzing_layer();
1037  }
1038  catch (std::string err ) {
1039  PyErr_SetString(PyExc_Exception, err.c_str());
1040  return NULL;
1041  }
1042  catch(...) {
1043  std::string err( "Invalid pointer to decomposition class");
1044  PyErr_SetString(PyExc_Exception, err.c_str());
1045  return NULL;
1046  }
1047 
1048 
1049 
1050  return Py_BuildValue("i", 0);
1051 
1052 }
1053 
1054 
1055 
1059 static PyObject *
1061 
1062 
1063 
1064  try {
1065  self->decomp->apply_imported_gate_structure();
1066  }
1067  catch (std::string err ) {
1068  PyErr_SetString(PyExc_Exception, err.c_str());
1069  return NULL;
1070  }
1071  catch(...) {
1072  std::string err( "Invalid pointer to decomposition class");
1073  PyErr_SetString(PyExc_Exception, err.c_str());
1074  return NULL;
1075  }
1076 
1077 
1078 
1079 
1080  return Py_BuildValue("i", 0);
1081 
1082 }
1083 
1088 static PyObject *
1090 
1091 
1092  std::string project_name = self->decomp->get_project_name();
1093 
1094  // convert to python string
1095  PyObject* project_name_pyhton = PyUnicode_FromString(project_name.c_str());
1096 
1097  return project_name_pyhton;
1098 }
1099 
1104 static PyObject *
1106  // initiate variables for input arguments
1107  PyObject* project_name_new=NULL;
1108 
1109  // parsing input arguments
1110  if (!PyArg_ParseTuple(args, "|O", &project_name_new)) return Py_BuildValue("i", -1);
1111 
1112 
1113  PyObject* project_name_new_string = PyObject_Str(project_name_new);
1114  PyObject* project_name_new_unicode = PyUnicode_AsEncodedString(project_name_new_string, "utf-8", "~E~");
1115  const char* project_name_new_C = PyBytes_AS_STRING(project_name_new_unicode);
1116  std::string project_name_new_str = ( project_name_new_C );
1117 
1118  // convert to python string
1119  self->decomp->set_project_name(project_name_new_str);
1120 
1121  return Py_BuildValue("i", 0);
1122 }
1123 
1124 
1129 static PyObject *
1131 
1132 
1133  double decomposition_error = self->decomp->get_decomposition_error();
1134 
1135 
1136  return Py_BuildValue("d", decomposition_error);
1137 }
1138 
1143 static PyObject *
1145  // initiate variables for input arguments
1146  PyObject* filename=NULL;
1147 
1148  // parsing input arguments
1149  if (!PyArg_ParseTuple(args, "|O", &filename)) return Py_BuildValue("i", -1);
1150 
1151 
1152  PyObject* filename_string = PyObject_Str(filename);
1153  PyObject* filename_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
1154  const char* filename_C = PyBytes_AS_STRING(filename_unicode);
1155  std::string filename_str = ( filename_C );
1156 
1157  // convert to python string
1158  self->decomp->export_unitary(filename_str);
1159 
1160  return Py_BuildValue("i", 0);
1161 }
1162 
1167 static PyObject *
1169 
1170 
1171  Matrix Unitary_mtx;
1172 
1173  try {
1174  Unitary_mtx = self->decomp->get_Umtx().copy();
1175  }
1176  catch (std::string err ) {
1177  PyErr_SetString(PyExc_Exception, err.c_str());
1178  return NULL;
1179  }
1180  catch (...) {
1181  std::string err( "Invalid pointer to decomposition class");
1182  PyErr_SetString(PyExc_Exception, err.c_str());
1183  return NULL;
1184  }
1185 
1186 
1187  // convert to numpy array
1188  Unitary_mtx.set_owner(false);
1189  PyObject *Unitary_py = matrix_to_numpy( Unitary_mtx );
1190 
1191  return Unitary_py;
1192 }
1193 
1194 
1199 static PyObject *
1201 {
1202 
1203 
1204  PyArrayObject* parameters_arg = NULL;
1205 
1206 
1207  // parsing input arguments
1208  if (!PyArg_ParseTuple(args, "|O", &parameters_arg )) {
1209 
1210  std::string err( "Unsuccessful argument parsing not ");
1211  PyErr_SetString(PyExc_Exception, err.c_str());
1212  return NULL;
1213 
1214  }
1215 
1216  // establish memory contiguous arrays for C calculations
1217  if ( PyArray_IS_C_CONTIGUOUS(parameters_arg) && PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ){
1218  Py_INCREF(parameters_arg);
1219  }
1220  else if (PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ) {
1221  parameters_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arg, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
1222  }
1223  else {
1224  std::string err( "Parameters should be should be real (given in float64 format)");
1225  PyErr_SetString(PyExc_Exception, err.c_str());
1226  return NULL;
1227  }
1228 
1229 
1230  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arg );
1231  double f0;
1232 
1233  try {
1234  f0 = self->decomp->optimization_problem(parameters_mtx );
1235  }
1236  catch (std::string err ) {
1237  PyErr_SetString(PyExc_Exception, err.c_str());
1238  return NULL;
1239  }
1240  catch (...) {
1241  std::string err( "Invalid pointer to decomposition class");
1242  PyErr_SetString(PyExc_Exception, err.c_str());
1243  return NULL;
1244  }
1245 
1246  Py_DECREF(parameters_arg);
1247 
1248 
1249  return Py_BuildValue("d", f0);
1250 }
1251 
1252 
1257 static PyObject *
1259 {
1260 
1261 
1262  PyArrayObject* parameters_arg = NULL;
1263 
1264 
1265  // parsing input arguments
1266  if (!PyArg_ParseTuple(args, "|O", &parameters_arg )) {
1267 
1268  std::string err( "Unsuccessful argument parsing not ");
1269  PyErr_SetString(PyExc_Exception, err.c_str());
1270  return NULL;
1271 
1272  }
1273 
1274  // establish memory contiguous arrays for C calculations
1275  if ( PyArray_IS_C_CONTIGUOUS(parameters_arg) && PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ){
1276  Py_INCREF(parameters_arg);
1277  }
1278  else if (PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ) {
1279  parameters_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arg, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
1280  }
1281  else {
1282  std::string err( "Parameters should be should be real (given in float64 format)");
1283  PyErr_SetString(PyExc_Exception, err.c_str());
1284  return NULL;
1285  }
1286 
1287 
1288  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arg );
1289  Matrix_real grad_mtx(parameters_mtx.size(), 1);
1290 
1291  try {
1292  self->decomp->optimization_problem_grad(parameters_mtx, self->decomp, grad_mtx );
1293  }
1294  catch (std::string err ) {
1295  PyErr_SetString(PyExc_Exception, err.c_str());
1296  return NULL;
1297  }
1298  catch (...) {
1299  std::string err( "Invalid pointer to decomposition class");
1300  PyErr_SetString(PyExc_Exception, err.c_str());
1301  return NULL;
1302  }
1303 
1304  // convert to numpy array
1305  grad_mtx.set_owner(false);
1306  PyObject *grad_py = matrix_real_to_numpy( grad_mtx );
1307 
1308  Py_DECREF(parameters_arg);
1309 
1310 
1311  return grad_py;
1312 }
1313 
1318 static PyObject *
1320 {
1321 
1322 
1323  PyArrayObject* parameters_arg = NULL;
1324 
1325 
1326  // parsing input arguments
1327  if (!PyArg_ParseTuple(args, "|O", &parameters_arg )) {
1328 
1329  std::string err( "Unsuccessful argument parsing not ");
1330  PyErr_SetString(PyExc_Exception, err.c_str());
1331  return NULL;
1332 
1333  }
1334 
1335  // establish memory contiguous arrays for C calculations
1336  if ( PyArray_IS_C_CONTIGUOUS(parameters_arg) && PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ){
1337  Py_INCREF(parameters_arg);
1338  }
1339  else if (PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ) {
1340  parameters_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arg, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
1341  }
1342  else {
1343  std::string err( "Parameters should be should be real (given in float64 format)");
1344  PyErr_SetString(PyExc_Exception, err.c_str());
1345  return NULL;
1346  }
1347 
1348 
1349  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arg );
1350  Matrix_real grad_mtx(parameters_mtx.size(), 1);
1351  double f0;
1352 
1353  try {
1354  self->decomp->optimization_problem_combined(parameters_mtx, &f0, grad_mtx );
1355  }
1356  catch (std::string err ) {
1357  PyErr_SetString(PyExc_Exception, err.c_str());
1358  return NULL;
1359  }
1360  catch (...) {
1361  std::string err( "Invalid pointer to decomposition class");
1362  PyErr_SetString(PyExc_Exception, err.c_str());
1363  return NULL;
1364  }
1365 
1366  // convert to numpy array
1367  grad_mtx.set_owner(false);
1368  PyObject *grad_py = matrix_real_to_numpy( grad_mtx );
1369 
1370  Py_DECREF(parameters_arg);
1371 
1372 
1373  PyObject* p = Py_BuildValue("(dO)", f0, grad_py);
1374  Py_DECREF(grad_py);
1375  return p;
1376 }
1377 
1382 static PyObject *
1384 {
1385 
1386 
1387  PyArrayObject* parameters_arg = NULL;
1388 
1389 
1390  // parsing input arguments
1391  if (!PyArg_ParseTuple(args, "|O", &parameters_arg )) {
1392 
1393  std::string err( "Unsuccessful argument parsing not ");
1394  PyErr_SetString(PyExc_Exception, err.c_str());
1395  return NULL;
1396 
1397  }
1398 
1399  // establish memory contiguous arrays for C calculations
1400  if ( PyArray_IS_C_CONTIGUOUS(parameters_arg) && PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ){
1401  Py_INCREF(parameters_arg);
1402  }
1403  else if (PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ) {
1404  parameters_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arg, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
1405  }
1406  else {
1407  std::string err( "Parameters should be should be real (given in float64 format)");
1408  PyErr_SetString(PyExc_Exception, err.c_str());
1409  return NULL;
1410  }
1411 
1412 
1413  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arg );
1414  Matrix Umtx;
1415  std::vector<Matrix> Umtx_deriv;
1416 
1417  try {
1418  self->decomp->optimization_problem_combined_unitary(parameters_mtx, Umtx, Umtx_deriv );
1419  }
1420  catch (std::string err ) {
1421  PyErr_SetString(PyExc_Exception, err.c_str());
1422  return NULL;
1423  }
1424  catch (...) {
1425  std::string err( "Invalid pointer to decomposition class");
1426  PyErr_SetString(PyExc_Exception, err.c_str());
1427  return NULL;
1428  }
1429 
1430  // convert to numpy array
1431  Umtx.set_owner(false);
1432  PyObject *unitary_py = matrix_to_numpy( Umtx );
1433  PyObject* graduni_py = PyList_New(Umtx_deriv.size());
1434  for (size_t i = 0; i < Umtx_deriv.size(); i++) {
1435  Umtx_deriv[i].set_owner(false);
1436  PyList_SetItem(graduni_py, i, matrix_to_numpy(Umtx_deriv[i]));
1437  }
1438 
1439  Py_DECREF(parameters_arg);
1440 
1441 
1442  PyObject* p = Py_BuildValue("(OO)", unitary_py, graduni_py);
1443  Py_DECREF(unitary_py); Py_DECREF(graduni_py);
1444  return p;
1445 }
1446 
1451 static PyObject *
1453 {
1454 
1455 
1456  PyArrayObject* parameters_arg = NULL;
1457 
1458 
1459  // parsing input arguments
1460  if (!PyArg_ParseTuple(args, "|O", &parameters_arg )) {
1461 
1462  std::string err( "Unsuccessful argument parsing not ");
1463  PyErr_SetString(PyExc_Exception, err.c_str());
1464  return NULL;
1465 
1466  }
1467 
1468  // establish memory contiguous arrays for C calculations
1469  if ( PyArray_IS_C_CONTIGUOUS(parameters_arg) && PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ){
1470  Py_INCREF(parameters_arg);
1471  }
1472  else if (PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ) {
1473  parameters_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arg, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
1474  }
1475  else {
1476  std::string err( "Parameters should be should be real (given in float64 format)");
1477  PyErr_SetString(PyExc_Exception, err.c_str());
1478  return NULL;
1479  }
1480 
1481 
1482  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arg );
1483  Matrix_real result_mtx;
1484 
1485  try {
1486  std::vector<Matrix_real> parameters_vec;
1487  parameters_vec.resize(parameters_mtx.rows);
1488  for( int row_idx=0; row_idx<parameters_mtx.rows; row_idx++ ) {
1489  parameters_vec[row_idx] = Matrix_real( parameters_mtx.get_data() + row_idx*parameters_mtx.stride, 1, parameters_mtx.cols, parameters_mtx.stride );
1490  }
1491  result_mtx = self->decomp->optimization_problem_batched( parameters_vec );
1492  }
1493  catch (std::string err ) {
1494  PyErr_SetString(PyExc_Exception, err.c_str());
1495  return NULL;
1496  }
1497  catch (...) {
1498  std::string err( "Invalid pointer to decomposition class");
1499  PyErr_SetString(PyExc_Exception, err.c_str());
1500  return NULL;
1501  }
1502 
1503  // convert to numpy array
1504  result_mtx.set_owner(false);
1505  PyObject *result_py = matrix_real_to_numpy( result_mtx );
1506 
1507  Py_DECREF(parameters_arg);
1508 
1509  return result_py;
1510 }
1511 
1512 
1513 
1514 
1515 static PyObject *
1517 
1518  if ( self->Umtx != NULL ) {
1519  // release the unitary to be decomposed
1520  Py_DECREF(self->Umtx);
1521  self->Umtx = NULL;
1522  }
1523 
1524  PyArrayObject *Umtx_arg = NULL;
1525  //Parse arguments
1526  if (!PyArg_ParseTuple(args, "|O", &Umtx_arg )) return Py_BuildValue("i", -1);
1527 
1528  // convert python object array to numpy C API array
1529  if ( Umtx_arg == NULL ) {
1530  PyErr_SetString(PyExc_Exception, "Umtx argument in empty");
1531  return NULL;
1532  }
1533 
1534  self->Umtx = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)Umtx_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
1535 
1536  // test C-style contiguous memory allocation of the array
1537  if ( !PyArray_IS_C_CONTIGUOUS(self->Umtx) ) {
1538  std::cout << "Umtx is not memory contiguous" << std::endl;
1539  }
1540 
1541 
1542  // create QGD version of the Umtx
1543  Matrix Umtx_mtx = numpy2matrix(self->Umtx);
1544  self->decomp->set_unitary(Umtx_mtx);
1545 
1546  return Py_BuildValue("i", 0);
1547 }
1548 
1553 static PyObject *
1555 
1556  // initiate variables for input arguments
1557  PyObject* qbit_list;
1558 
1559  // parsing input arguments
1560  if (!PyArg_ParseTuple(args, "|O", &qbit_list )) return Py_BuildValue("i", -1);
1561 
1562  bool is_tuple = PyTuple_Check(qbit_list);
1563  bool is_list = PyList_Check(qbit_list);
1564 
1565  // Check whether input is dictionary
1566  if (!is_list && !is_tuple) {
1567  printf("Input must be tuple or list!\n");
1568  return Py_BuildValue("i", -1);
1569  }
1570 
1571  // get the number of qbubits
1572  Py_ssize_t element_num;
1573 
1574  if (is_tuple) {
1575  element_num = PyTuple_GET_SIZE(qbit_list);
1576  }
1577  else {
1578  element_num = PyList_GET_SIZE(qbit_list);
1579  }
1580 
1581 
1582  // create C++ variant of the tuple/list
1583  std::vector<int> qbit_list_C( (int) element_num);
1584  for ( Py_ssize_t idx=0; idx<element_num; idx++ ) {
1585  if (is_tuple) {
1586  qbit_list_C[(int) idx] = (int) PyLong_AsLong( PyTuple_GetItem(qbit_list, idx ) );
1587  }
1588  else {
1589  qbit_list_C[(int) idx] = (int) PyLong_AsLong( PyList_GetItem(qbit_list, idx ) );
1590  }
1591 
1592  }
1593 
1594 
1595  // reorder the qubits in the decomposition class
1596  self->decomp->reorder_qubits( qbit_list_C );
1597 
1598 
1599 
1600 
1601  return Py_BuildValue("i", 0);
1602 }
1603 
1604 
1605 
1610 static PyObject *
1612 
1613 
1614  self->decomp->add_adaptive_layers();
1615 
1616 
1617  return Py_BuildValue("i", 0);
1618 }
1619 
1620 
1621 
1626 static PyObject *
1628 
1629 
1630  self->decomp->add_layer_to_imported_gate_structure();
1631 
1632 
1633  return Py_BuildValue("i", 0);
1634 }
1635 
1636 
1637 
1638 
1639 
1644 static PyObject *
1646 
1647  PyArrayObject* parameters_arr = NULL;
1648 
1649 
1650  // parsing input arguments
1651  if (!PyArg_ParseTuple(args, "|O", &parameters_arr ))
1652  return Py_BuildValue("i", -1);
1653 
1654 
1655  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1656  Py_INCREF(parameters_arr);
1657  }
1658  else {
1659  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
1660  }
1661 
1662 
1663  // get the C++ wrapper around the data
1664  Matrix_real&& parameters_mtx = numpy2matrix_real( parameters_arr );
1665 
1666 
1667  Matrix unitary_mtx;
1668 
1669  unitary_mtx = self->decomp->get_matrix( parameters_mtx );
1670 
1671 
1672  // convert to numpy array
1673  unitary_mtx.set_owner(false);
1674  PyObject *unitary_py = matrix_to_numpy( unitary_mtx );
1675 
1676 
1677  Py_DECREF(parameters_arr);
1678 
1679  return unitary_py;
1680 }
1681 
1682 
1683 
1689 static PyObject *
1691 {
1692 
1693  // The tuple of expected keywords
1694  static char *kwlist[] = {(char*)"optimizer", NULL};
1695 
1696  PyObject* optimizer_arg = NULL;
1697 
1698 
1699  // parsing input arguments
1700  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &optimizer_arg)) {
1701 
1702  std::string err( "Unsuccessful argument parsing not ");
1703  PyErr_SetString(PyExc_Exception, err.c_str());
1704  return NULL;
1705 
1706  }
1707 
1708 
1709  if ( optimizer_arg == NULL ) {
1710  std::string err( "optimizer argument not set");
1711  PyErr_SetString(PyExc_Exception, err.c_str());
1712  return NULL;
1713  }
1714 
1715 
1716 
1717  PyObject* optimizer_string = PyObject_Str(optimizer_arg);
1718  PyObject* optimizer_string_unicode = PyUnicode_AsEncodedString(optimizer_string, "utf-8", "~E~");
1719  const char* optimizer_C = PyBytes_AS_STRING(optimizer_string_unicode);
1720 
1721  optimization_aglorithms qgd_optimizer;
1722  if ( strcmp("bfgs", optimizer_C) == 0 || strcmp("BFGS", optimizer_C) == 0) {
1723  qgd_optimizer = BFGS;
1724  }
1725  else if ( strcmp("adam", optimizer_C)==0 || strcmp("ADAM", optimizer_C)==0) {
1726  qgd_optimizer = ADAM;
1727  }
1728  else if ( strcmp("grad_descend", optimizer_C)==0 || strcmp("GRAD_DESCEND", optimizer_C)==0) {
1729  qgd_optimizer = GRAD_DESCEND;
1730  }
1731  else if ( strcmp("adam_batched", optimizer_C)==0 || strcmp("ADAM_BATCHED", optimizer_C)==0) {
1732  qgd_optimizer = ADAM_BATCHED;
1733  }
1734  else if ( strcmp("bfgs2", optimizer_C)==0 || strcmp("BFGS2", optimizer_C)==0) {
1735  qgd_optimizer = BFGS2;
1736  }
1737  else if ( strcmp("agents", optimizer_C)==0 || strcmp("AGENTS", optimizer_C)==0) {
1738  qgd_optimizer = AGENTS;
1739  }
1740  else if ( strcmp("cosine", optimizer_C)==0 || strcmp("COSINE", optimizer_C)==0) {
1741  qgd_optimizer = COSINE;
1742  }
1743  else if ( strcmp("grad_descend_phase_shift_rule", optimizer_C)==0 || strcmp("GRAD_DESCEND_PARAMETER_SHIFT_RULE", optimizer_C)==0) {
1744  qgd_optimizer = GRAD_DESCEND_PARAMETER_SHIFT_RULE;
1745  }
1746  else if ( strcmp("agents_combined", optimizer_C)==0 || strcmp("AGENTS_COMBINED", optimizer_C)==0) {
1747  qgd_optimizer = AGENTS_COMBINED;
1748  }
1749  else if ( strcmp("bayes_opt", optimizer_C)==0 || strcmp("BAYES_OPT", optimizer_C)==0) {
1750  qgd_optimizer = BAYES_OPT;
1751  }
1752  else {
1753  std::cout << "Wrong optimizer: " << optimizer_C << ". Using default: BFGS" << std::endl;
1754  qgd_optimizer = BFGS;
1755  }
1756 
1757 
1758  try {
1759  self->decomp->set_optimizer(qgd_optimizer);
1760  }
1761  catch (std::string err) {
1762  PyErr_SetString(PyExc_Exception, err.c_str());
1763  std::cout << err << std::endl;
1764  return NULL;
1765  }
1766  catch(...) {
1767  std::string err( "Invalid pointer to decomposition class");
1768  PyErr_SetString(PyExc_Exception, err.c_str());
1769  return NULL;
1770  }
1771 
1772 
1773  return Py_BuildValue("i", 0);
1774 
1775 }
1776 
1777 
1778 
1784 static PyObject *
1786 {
1787 
1788  // The tuple of expected keywords
1789  static char *kwlist[] = {(char*)"costfnc", NULL};
1790 
1791  int costfnc_arg = 0;
1792 
1793 
1794  // parsing input arguments
1795  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &costfnc_arg)) {
1796 
1797  std::string err( "Unsuccessful argument parsing");
1798  PyErr_SetString(PyExc_Exception, err.c_str());
1799  return NULL;
1800 
1801  }
1802 
1803  cost_function_type qgd_costfnc = (cost_function_type)costfnc_arg;
1804 
1805 
1806  try {
1807  self->decomp->set_cost_function_variant(qgd_costfnc);
1808  }
1809  catch (std::string err) {
1810  PyErr_SetString(PyExc_Exception, err.c_str());
1811  std::cout << err << std::endl;
1812  return NULL;
1813  }
1814  catch(...) {
1815  std::string err( "Invalid pointer to decomposition class");
1816  PyErr_SetString(PyExc_Exception, err.c_str());
1817  return NULL;
1818  }
1819 
1820 
1821  return Py_BuildValue("i", 0);
1822 
1823 }
1824 
1825 
1826 
1827 
1828 
1829 
1834 static PyObject *
1836 {
1837 
1838  // The tuple of expected keywords
1839  static char *kwlist[] = {(char*)"trace_offset", NULL};
1840 
1841  int trace_offset_arg = 0;
1842 
1843 
1844  // parsing input arguments
1845  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &trace_offset_arg)) {
1846 
1847  std::string err( "Unsuccessful argument parsing");
1848  PyErr_SetString(PyExc_Exception, err.c_str());
1849  return NULL;
1850 
1851  }
1852 
1853 
1854  try {
1855  self->decomp->set_trace_offset(trace_offset_arg);
1856  }
1857  catch (std::string err) {
1858  PyErr_SetString(PyExc_Exception, err.c_str());
1859  std::cout << err << std::endl;
1860  return NULL;
1861  }
1862  catch(...) {
1863  std::string err( "Invalid pointer to decomposition class");
1864  PyErr_SetString(PyExc_Exception, err.c_str());
1865  return NULL;
1866  }
1867 
1868 
1869  return Py_BuildValue("i", 0);
1870 
1871 }
1872 
1873 
1874 
1879 static PyObject *
1881 {
1882 
1883  int trace_offset = 0;
1884 
1885  try {
1886  trace_offset = self->decomp->get_trace_offset();
1887  }
1888  catch (std::string err) {
1889  PyErr_SetString(PyExc_Exception, err.c_str());
1890  std::cout << err << std::endl;
1891  return NULL;
1892  }
1893  catch(...) {
1894  std::string err( "Invalid pointer to decomposition class");
1895  PyErr_SetString(PyExc_Exception, err.c_str());
1896  return NULL;
1897  }
1898 
1899 
1900  return Py_BuildValue("i", trace_offset);
1901 
1902 }
1903 
1904 
1905 
1906 
1910 static PyObject *
1912 
1913 #ifdef __DFE__
1914 
1915  try {
1916  self->decomp->upload_Umtx_to_DFE();
1917  }
1918  catch (std::string err) {
1919  PyErr_SetString(PyExc_Exception, err.c_str());
1920  std::cout << err << std::endl;
1921  return NULL;
1922  }
1923  catch(...) {
1924  std::string err( "Invalid pointer to decomposition class");
1925  PyErr_SetString(PyExc_Exception, err.c_str());
1926  return NULL;
1927  }
1928 
1929 #endif
1930 
1931  return Py_BuildValue("i", 0);
1932 
1933 }
1934 
1935 
1936 
1937 
1938 
1939 
1940 
1944 static PyObject *
1946 {
1947 
1948 
1949  PyArrayObject * parameters_arr = NULL;
1950  PyArrayObject * input_state_arg = NULL;
1951  PyObject * qubit_list_arg = NULL;
1952 
1953 
1954  // parsing input arguments
1955  if (!PyArg_ParseTuple(args, "|OOO", &parameters_arr, &input_state_arg, &qubit_list_arg ))
1956  return Py_BuildValue("i", -1);
1957 
1958 
1959  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
1960  Py_INCREF(parameters_arr);
1961  }
1962  else {
1963  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF((PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
1964  }
1965 
1966  // get the C++ wrapper around the data
1967  Matrix_real&& parameters_mtx = numpy2matrix_real( parameters_arr );
1968 
1969 
1970  // convert python object array to numpy C API array
1971  if ( input_state_arg == NULL ) {
1972  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
1973  return NULL;
1974  }
1975 
1976  PyArrayObject* input_state = (PyArrayObject*)PyArray_FROM_OTF((PyObject*)input_state_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
1977 
1978  // test C-style contiguous memory allocation of the array
1979  if ( !PyArray_IS_C_CONTIGUOUS(input_state) ) {
1980  PyErr_SetString(PyExc_Exception, "input mtrix is not memory contiguous");
1981  return NULL;
1982  }
1983 
1984 
1985  // create QGD version of the input matrix
1986  Matrix input_state_mtx = numpy2matrix(input_state);
1987 
1988 
1989  // check input argument qbit_list
1990  if ( qubit_list_arg == NULL || (!PyList_Check( qubit_list_arg )) ) {
1991  PyErr_SetString(PyExc_Exception, "qubit_list should be a list");
1992  return NULL;
1993  }
1994 
1995  Py_ssize_t reduced_qbit_num = PyList_Size( qubit_list_arg );
1996 
1997  matrix_base<int> qbit_list_mtx( (int)reduced_qbit_num, 1);
1998  for ( int idx=0; idx<reduced_qbit_num; idx++ ) {
1999 
2000  PyObject* item = PyList_GET_ITEM( qubit_list_arg, idx );
2001  qbit_list_mtx[idx] = (int) PyLong_AsLong( item );
2002 
2003  }
2004 
2005 
2006  double entropy = -1;
2007 
2008 
2009  try {
2010  entropy = self->decomp->get_second_Renyi_entropy( parameters_mtx, input_state_mtx, qbit_list_mtx );
2011  }
2012  catch (std::string err) {
2013  PyErr_SetString(PyExc_Exception, err.c_str());
2014  std::cout << err << std::endl;
2015  return NULL;
2016  }
2017  catch(...) {
2018  std::string err( "Invalid pointer to decomposition class");
2019  PyErr_SetString(PyExc_Exception, err.c_str());
2020  return NULL;
2021  }
2022 
2023 
2024  Py_DECREF(parameters_arr);
2025  Py_DECREF(input_state);
2026 
2027 
2028 
2029  PyObject* p = Py_BuildValue("d", entropy);
2030 
2031  return p;
2032 }
2033 
2034 
2038 static PyObject *
2040 
2041  int qbit_num = 0;
2042 
2043  try {
2044  qbit_num = self->decomp->get_qbit_num();
2045  }
2046  catch (std::string err) {
2047  PyErr_SetString(PyExc_Exception, err.c_str());
2048  std::cout << err << std::endl;
2049  return NULL;
2050  }
2051  catch(...) {
2052  std::string err( "Invalid pointer to decomposition class");
2053  PyErr_SetString(PyExc_Exception, err.c_str());
2054  return NULL;
2055  }
2056 
2057 
2058  return Py_BuildValue("i", qbit_num );
2059 
2060 }
2061 
2062 
2063 
2068  {NULL} /* Sentinel */
2069 };
2070 
2075  {"Start_Decomposition", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Start_Decomposition, METH_VARARGS | METH_KEYWORDS,
2076  "Method to start the decomposition."
2077  },
2078  {"get_Initial_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Initial_Circuit, METH_NOARGS,
2079  "Method to get initial circuit in decomposition."
2080  },
2081  {"Compress_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Compress_Circuit, METH_NOARGS,
2082  "Method to compress gate structure."
2083  },
2084  {"Finalize_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Finalize_Circuit, METH_VARARGS | METH_KEYWORDS,
2085  "Method to finalize the decomposition."
2086  },
2087  {"get_Gate_Num", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_gate_num, METH_NOARGS,
2088  "Method to get the number of decomposing gates."
2089  },
2090  {"get_Parameter_Num", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Parameter_Num, METH_NOARGS,
2091  "Call to get the number of free parameters in the gate structure used for the decomposition"
2092  },
2093  {"get_Optimized_Parameters", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Optimized_Parameters, METH_NOARGS,
2094  "Method to get the array of optimized parameters."
2095  },
2096  {"set_Optimized_Parameters", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Optimized_Parameters, METH_VARARGS,
2097  "Method to set the initial array of optimized parameters."
2098  },
2099  {"get_Circuit", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_circuit, METH_NOARGS,
2100  "Method to get the incorporated circuit."
2101  },
2102  {"List_Gates", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_List_Gates, METH_NOARGS,
2103  "Call to print the decomposing nitaries on standard output"
2104  },
2105  {"get_Num_of_Iters", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Num_of_Iters, METH_NOARGS,
2106  "Method to get the number of iterations."
2107  },
2108  {"set_Max_Layer_Num", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Max_Layer_Num, METH_VARARGS,
2109  "Call to set the maximal number of layers used in the subdecomposition of the qbit-th qubit."
2110  },
2111  {"set_Iteration_Loops", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Iteration_Loops, METH_VARARGS,
2112  "Call to set the number of iteration loops during the subdecomposition of the qbit-th qubit."
2113  },
2114  {"set_Verbose", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Verbose, METH_VARARGS,
2115  "Call to set the verbosity of the qgd_N_Qubit_Decomposition class."
2116  },
2117  {"set_Debugfile", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Debugfile, METH_VARARGS,
2118  "Set the debugfile name of the N_Qubit_Decomposition class."
2119  },
2120  {"set_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Gate_Structure, METH_VARARGS,
2121  "Call to set adaptive custom gate structure in the decomposition."
2122  },
2123  {"Reorder_Qubits", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Reorder_Qubits, METH_VARARGS,
2124  "Wrapper method to reorder the qubits in the decomposition class."
2125  },
2126  {"set_Optimization_Tolerance", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Optimization_Tolerance, METH_VARARGS,
2127  "Wrapper method to set the optimization tolerance of the optimization process during the decomposition."
2128  },
2129  {"set_Convergence_Threshold", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Convergence_Threshold, METH_VARARGS,
2130  "Wrapper method to set the threshold of convergence in the optimization processes."
2131  },
2132  {"set_Optimization_Blocks", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Optimization_Blocks, METH_VARARGS,
2133  "Wrapper method to to set the number of gate blocks to be optimized in one shot."
2134  },
2135  {"set_Gate_Structure_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Gate_Structure_From_Binary, METH_VARARGS,
2136  "Call to set custom layers to the gate structure that are intended to be used in the decomposition from a binary file created from SQUANDER"
2137  },
2138  {"add_Gate_Structure_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Gate_Structure_From_Binary, METH_VARARGS,
2139  "Call to append custom layers to the gate structure that are intended to be used in the decomposition from a binary file created from SQUANDER"
2140  },
2141  {"set_Unitary_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Unitary_From_Binary, METH_VARARGS,
2142  "Call to set unitary matrix from a binary file created from SQUANDER"
2143  },
2144  {"set_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Unitary, METH_VARARGS,
2145  "Call to set unitary matrix to a numpy matrix"
2146  },
2147  {"export_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_export_Unitary, METH_VARARGS,
2148  "Call to export unitary matrix to a binary file"
2149  },
2150  {"get_Project_Name", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Project_Name, METH_NOARGS,
2151  "Call to get the name of SQUANDER project"
2152  },
2153  {"set_Project_Name", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Project_Name, METH_VARARGS,
2154  "Call to set the name of SQUANDER project"
2155  },
2156  {"get_Global_Phase", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Global_Phase, METH_NOARGS,
2157  "Call to get global phase"
2158  },
2159  {"set_Global_Phase", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Global_Phase, METH_VARARGS,
2160  "Call to set global phase"
2161  },
2162  {"apply_Global_Phase_Factor", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_apply_Global_Phase_Factor, METH_NOARGS,
2163  "Call to apply global phase on Unitary matrix"
2164  },
2165  {"add_Finalyzing_Layer_To_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Finalyzing_Layer_To_Gate_Structure, METH_NOARGS,
2166  "Call to add finalyzing layer (single qubit rotations on all of the qubits) to the gate structure."
2167  },
2168  {"get_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Unitary, METH_NOARGS,
2169  "Call to get Unitary Matrix"
2170  },
2171  {"add_Adaptive_Layers", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Adaptive_Layers, METH_NOARGS,
2172  "Call to add adaptive layers to the gate structure stored by the class."
2173  },
2174  {"add_Layer_To_Imported_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Layer_To_Imported_Gate_Structure, METH_NOARGS,
2175  "Call to add an adaptive layer to the gate structure previously imported gate structure"
2176  },
2177  {"apply_Imported_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_apply_Imported_Gate_Structure, METH_NOARGS,
2178  "Call to apply the imported gate structure on the unitary. The transformed unitary is to be decomposed in the calculations, and the imported gate structure is released."
2179  },
2180  {"set_Optimizer", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Optimizer, METH_VARARGS | METH_KEYWORDS,
2181  "Wrapper method to to set the optimizer method for the gate synthesis."
2182  },
2183  {"set_Max_Iterations", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Max_Iterations, METH_VARARGS | METH_VARARGS,
2184  "Wrapper method to to set the maximum number of iterations for the gate synthesis."
2185  },
2186  {"get_Matrix", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Matrix, METH_VARARGS,
2187  "Method to retrieve the unitary of the circuit."
2188  },
2189  {"set_Cost_Function_Variant", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Cost_Function_Variant, METH_VARARGS | METH_KEYWORDS,
2190  "Wrapper method to to set the variant of the cost function. Input argument 0 stands for FROBENIUS_NORM, 1 for FROBENIUS_NORM_CORRECTION1, 2 for FROBENIUS_NORM_CORRECTION2, 3 for HILBERT_SCHMIDT_TEST, 4 for HILBERT_SCHMIDT_TEST_CORRECTION1, 5 for HILBERT_SCHMIDT_TEST_CORRECTION2."
2191  },
2192  {"Optimization_Problem", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem, METH_VARARGS,
2193  "Wrapper function to evaluate the cost function."
2194  },
2195  {"Optimization_Problem_Combined_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem_Combined_Unitary, METH_VARARGS,
2196  "Wrapper function to evaluate the unitary function and the gradient components."
2197  },
2198  {"Optimization_Problem_Grad", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem_Grad, METH_VARARGS,
2199  "Wrapper function to evaluate the gradient components."
2200  },
2201  {"Optimization_Problem_Combined", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem_Combined, METH_VARARGS,
2202  "Wrapper function to evaluate the cost function and the gradient components."
2203  },
2204  {"Optimization_Problem_Batch", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem_Batch, METH_VARARGS,
2205  "Wrapper function to evaluate the cost function for a batch of input parameters."
2206  },
2207  {"Upload_Umtx_to_DFE", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_Upload_Umtx_to_DFE, METH_NOARGS,
2208  "Call to upload the unitary to the DFE. (Has no effect for non-DFE builds)"
2209  },
2210  {"get_Trace_Offset", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Trace_Offset, METH_NOARGS,
2211  "Call to get the trace offset used in the cost function. In this case Tr(A) = sum_(i-offset=j) A_{ij}"
2212  },
2213  {"set_Trace_Offset", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Trace_Offset, METH_VARARGS | METH_KEYWORDS,
2214  "Call to set the trace offset used in the cost function. In this case Tr(A) = sum_(i-offset=j) A_{ij}"
2215  },
2216  {"get_Decomposition_Error", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Decomposition_Error, METH_NOARGS,
2217  "Call to get the error of the decomposition. (i.e. the final value of the cost function)"
2218  },
2219  {"get_Second_Renyi_Entropy", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Second_Renyi_Entropy, METH_VARARGS,
2220  "Wrapper function to evaluate the second Rényi entropy of a quantum circuit at a specific parameter set."
2221  },
2222  {"get_Qbit_Num", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Qbit_Num, METH_NOARGS,
2223  "Call to get the number of qubits in the circuit"
2224  },
2225  {NULL} /* Sentinel */
2226 };
2227 
2232  PyVarObject_HEAD_INIT(NULL, 0)
2233  "qgd_N_Qubit_Decomposition_adaptive_Wrapper.qgd_N_Qubit_Decomposition_adaptive_Wrapper", /*tp_name*/
2234  sizeof(qgd_N_Qubit_Decomposition_adaptive_Wrapper), /*tp_basicsize*/
2235  0, /*tp_itemsize*/
2236  (destructor) qgd_N_Qubit_Decomposition_adaptive_Wrapper_dealloc, /*tp_dealloc*/
2237  #if PY_VERSION_HEX < 0x030800b4
2238  0, /*tp_print*/
2239  #endif
2240  #if PY_VERSION_HEX >= 0x030800b4
2241  0, /*tp_vectorcall_offset*/
2242  #endif
2243  0, /*tp_getattr*/
2244  0, /*tp_setattr*/
2245  #if PY_MAJOR_VERSION < 3
2246  0, /*tp_compare*/
2247  #endif
2248  #if PY_MAJOR_VERSION >= 3
2249  0, /*tp_as_async*/
2250  #endif
2251  0, /*tp_repr*/
2252  0, /*tp_as_number*/
2253  0, /*tp_as_sequence*/
2254  0, /*tp_as_mapping*/
2255  0, /*tp_hash*/
2256  0, /*tp_call*/
2257  0, /*tp_str*/
2258  0, /*tp_getattro*/
2259  0, /*tp_setattro*/
2260  0, /*tp_as_buffer*/
2261  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
2262  "Object to represent a Circuit class of the QGD package.", /*tp_doc*/
2263  0, /*tp_traverse*/
2264  0, /*tp_clear*/
2265  0, /*tp_richcompare*/
2266  0, /*tp_weaklistoffset*/
2267  0, /*tp_iter*/
2268  0, /*tp_iternext*/
2271  0, /*tp_getset*/
2272  0, /*tp_base*/
2273  0, /*tp_dict*/
2274  0, /*tp_descr_get*/
2275  0, /*tp_descr_set*/
2276  0, /*tp_dictoffset*/
2277  (initproc) qgd_N_Qubit_Decomposition_adaptive_Wrapper_init, /*tp_init*/
2278  0, /*tp_alloc*/
2280  0, /*tp_free*/
2281  0, /*tp_is_gc*/
2282  0, /*tp_bases*/
2283  0, /*tp_mro*/
2284  0, /*tp_cache*/
2285  0, /*tp_subclasses*/
2286  0, /*tp_weaklist*/
2287  0, /*tp_del*/
2288  0, /*tp_version_tag*/
2289  #if PY_VERSION_HEX >= 0x030400a1
2290  0, /*tp_finalize*/
2291  #endif
2292  #if PY_VERSION_HEX >= 0x030800b1
2293  0, /*tp_vectorcall*/
2294  #endif
2295  #if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
2296  0, /*tp_print*/
2297  #endif
2298 };
2299 
2304  PyModuleDef_HEAD_INIT,
2305  "qgd_N_Qubit_Decomposition_adaptive_Wrapper",
2306  "Python binding for QGD N_Qubit_Decomposition class",
2307  -1,
2308 };
2309 
2310 
2314 PyMODINIT_FUNC
2316 {
2317  // initialize Numpy API
2318  import_array();
2319 
2320  PyObject *m;
2321  if (PyType_Ready(&qgd_N_Qubit_Decomposition_adaptive_Wrapper_Type) < 0)
2322  return NULL;
2323 
2324  m = PyModule_Create(&qgd_N_Qubit_Decomposition_adaptive_Wrapper_Module);
2325  if (m == NULL)
2326  return NULL;
2327 
2328  Py_INCREF(&qgd_N_Qubit_Decomposition_adaptive_Wrapper_Type);
2329  if (PyModule_AddObject(m, "qgd_N_Qubit_Decomposition_adaptive_Wrapper", (PyObject *) &qgd_N_Qubit_Decomposition_adaptive_Wrapper_Type) < 0) {
2330  Py_DECREF(&qgd_N_Qubit_Decomposition_adaptive_Wrapper_Type);
2331  Py_DECREF(m);
2332  return NULL;
2333  }
2334 
2335  return m;
2336 }
2337 
2338 
2339 } //extern C
2340 
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_List_Gates(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Lists the gates decomposing the initial unitary.
Gates_block * get_flat_circuit()
Method to generate a flat circuit.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem_Combined(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper function to evaluate the cost function an dthe gradient components.
N_Qubit_Decomposition_adaptive * decomp
An object to decompose the unitary.
string project_name
Definition: test_Groq.py:98
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Start_Decomposition(qgd_N_Qubit_Decomposition_adaptive_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_adaptive_Wrapper_set_Cost_Function_Variant(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args, PyObject *kwds)
Wrapper function to set a variant for the cost function.
parameter_num
[set adaptive gate structure]
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Convergence_Threshold(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper method to set the threshold of convergence in the optimization processes. ...
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem_Batch(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper function to evaluate the cost function for many input paramaters at once. ...
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Debugfile(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Set the debugfile name of the N_Qubit_Decomposition class.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Optimized_Parameters(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Extract the optimized parameters.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Gate_Structure_From_Binary(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper function to append custom layers to the gate structure that are intended to be used in the de...
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
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper function to evaluate the cost function.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Unitary(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Matrix_real numpy2matrix_real(PyArrayObject *arr)
Call to create a PIC matrix_real representation of a numpy array.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Reorder_Qubits(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper method to reorder the qubits in the decomposition class.
static int qgd_N_Qubit_Decomposition_adaptive_Wrapper_init(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args, PyObject *kwds)
Method called when a python instance of the class qgd_N_Qubit_Decomposition_adaptive_Wrapper is initi...
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Parameter_Num(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Get the number of free parameters in the gate structure used for the decomposition.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Trace_Offset(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args, PyObject *kwds)
Wrapper function to set the trace offset used in the cost function.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Global_Phase(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
sets the global phase to the new angle given
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Gate_Structure(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper function to set custom gate structure for the decomposition.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Adaptive_Layers(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Wrapper method to add adaptive layers to the gate structure stored by the class.
Header file for a class responsible for grouping gates into subcircuits. (Subcircuits can be nested) ...
PyObject * matrix_real_to_numpy(Matrix_real &mtx)
Call to make a numpy array from an instance of matrix class.
static void qgd_N_Qubit_Decomposition_adaptive_Wrapper_dealloc(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Method called when a python instance of the class qgd_N_Qubit_Decomposition_adaptive_Wrapper is destr...
A class describing a universal configuration element.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Initial_Circuit(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
static PyModuleDef qgd_N_Qubit_Decomposition_adaptive_Wrapper_Module
Structure containing metadata about the module.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Layer_To_Imported_Gate_Structure(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Wrapper method to reorder the qubits in the decomposition class.
scalar * get_data() const
Call to get the pointer to the stored data.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Project_Name(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
set project name
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Max_Iterations(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Set the number of maximum iterations.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Finalize_Circuit(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args, PyObject *kwds)
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Unitary(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
get Unitary
static PyMethodDef qgd_N_Qubit_Decomposition_adaptive_Wrapper_methods[]
Structure containing metadata about the methods of class qgd_N_Qubit_Decomposition_adaptive_Wrapper.
optimization_aglorithms
implemented optimization strategies
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_apply_Imported_Gate_Structure(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Wrapper function to apply the imported gate structure on the unitary.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Optimization_Tolerance(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper method to set the optimization tolerance of the optimization process during the decomposition...
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Project_Name(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
get project name
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Finalyzing_Layer_To_Gate_Structure(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Wrapper function to add finalyzing layer (single qubit rotations on all of the qubits) to the gate st...
int rows
The number of rows.
Definition: matrix_base.hpp:42
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem_Grad(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper function to evaluate the cost function an dthe gradient components.
int cols
The number of columns.
Definition: matrix_base.hpp:44
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Num_of_Iters(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Get the number of free parameters in the gate structure used for the decomposition.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Gate_Structure_From_Binary(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper function to set custom layers to the gate structure that are intended to be used in the decom...
Type definition of the qgd_N_Qubit_Decomposition_adaptive_Wrapper Python class of the qgd_N_Qubit_Dec...
void release_N_Qubit_Decomposition_adaptive(N_Qubit_Decomposition_adaptive *instance)
Call to deallocate an instance of N_Qubit_Decomposition_adaptive class.
Umtx
The unitary to be decomposed.
Definition: example.py:53
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Iteration_Loops(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Set the number of iteration loops during the subdecomposition of the qbit-th qubit.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Compress_Circuit(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
PyMODINIT_FUNC PyInit_qgd_N_Qubit_Decomposition_adaptive_Wrapper(void)
Method called when the Python module is initialized.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Optimization_Problem_Combined_Unitary(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper function to evaluate the unitary function and the unitary derivates.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_export_Unitary(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
export unitary to binary file
void set_owner(bool owner_in)
Call to set the current class instance to be (or not to be) the owner of the stored data array...
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
A 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_adaptive_Wrapper_apply_Global_Phase_Factor(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
applies the global phase to the Unitary matrix
dictionary config
gate systhesis #####################################
Class to store data of complex arrays and its properties.
Definition: matrix.h:38
int size() const
Call to get the number of the allocated elements.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Verbose(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Set the verbosity of the N_Qubit_Decomposition class.
cost_function_type
Type definition of the fifferent 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:41
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Unitary_From_Binary(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
static PyMemberDef qgd_N_Qubit_Decomposition_adaptive_Wrapper_members[]
Structure containing metadata about the members of class qgd_N_Qubit_Decomposition_adaptive_Wrapper.
void set_property(std::string name_, double val_)
Call to set a double value.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Optimized_Parameters(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Extract the optimized parameters.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Decomposition_Error(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Cll to get the error of the decomposition (i.e.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Second_Renyi_Entropy(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper function to evaluate the second Rényi entropy of a quantum circuit at a specific parameter s...
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Matrix(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Retrieve the unified unitary operation of the circuit.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_circuit(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Wrapper function to retrieve the circuit (Squander format) incorporated in the instance.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_gate_num(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Wrapper function to get the number of decomposing gates.
Matrix numpy2matrix(PyArrayObject *arr)
Call to create a PIC matrix representation of a numpy array.
PyObject_HEAD Gates_block * circuit
Pointer to the C++ class of the base Gate_block module.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
Method called when a python instance of the class qgd_N_Qubit_Decomposition_adaptive_Wrapper is alloc...
Matrix copy()
Call to create a copy of the matrix.
Definition: matrix.cpp:105
double real
the real part of a complex number
Definition: QGDTypes.h:40
Header file for a class implementing the adaptive gate decomposition algorithm of arXiv:2203...
int get_qbit_num()
Call to get the number of qubits composing the unitary.
Definition: Gate.cpp:504
static PyTypeObject qgd_N_Qubit_Decomposition_adaptive_Wrapper_Type
A structure describing the type of the class qgd_N_Qubit_Decomposition_adaptive_Wrapper.
Type definition of the qgd_Circuit_Wrapper Python class of the qgd_Circuit_Wrapper module...
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Optimizer(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args, PyObject *kwds)
Wrapper function to set custom gate structure for the decomposition.
PyObject_HEAD PyArrayObject * Umtx
pointer to the unitary to be decomposed to keep it alive
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_Upload_Umtx_to_DFE(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Call to upload the unitary to the DFE.
PyObject * matrix_to_numpy(Matrix &mtx)
Call to make a numpy array from an instance of matrix class.
tuple decomposition_error
Definition: example.py:97
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Qbit_Num(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Call to retrieve the number of qubits in the circuit.
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:39
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Optimization_Blocks(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Wrapper method to to set the number of gate blocks to be optimized in one shot.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Trace_Offset(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
Wrapper function to get the trace offset used in the cost function.
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Max_Layer_Num(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args)
Set the maximal number of layers used in the subdecomposition of the qbit-th qubit.
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42
static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Global_Phase(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self)
returns the angle of the global phase (the radius us always sqrt(2))
N_Qubit_Decomposition_adaptive * create_N_Qubit_Decomposition_adaptive(Matrix &Umtx, int qbit_num, int level_limit, int level_limit_min, std::vector< matrix_base< int >> topology_in, std::map< std::string, Config_Element > &config, int accelerator_num)
Creates an instance of class N_Qubit_Decomposition and return with a pointer pointing to the class in...