Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
qgd_VQE_Base_Wrapper.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:13:26 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 @author: Peter Rakyta, Ph.D.
18 */
23 #define PY_SSIZE_T_CLEAN
24 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
25 
26 #include <Python.h>
27 #include <numpy/arrayobject.h>
28 #include "structmember.h"
29 #include <stdio.h>
31 
32 #include "numpy_interface.h"
33 
34 
35 
36 
37 
41 typedef struct qgd_Circuit_Wrapper {
42  PyObject_HEAD
45 
46 
51  PyObject_HEAD
53  PyObject *Hamiltonian;
56 
58 
59 
60 
70 create_qgd_Variational_Quantum_Eigensolver_Base( Matrix_sparse Hamiltonian, int qbit_num, std::map<std::string, Config_Element>& config, int accelerator_num) {
71 
72  return new Variational_Quantum_Eigensolver_Base( Hamiltonian, qbit_num, config, accelerator_num);
73 }
74 
75 
80 void
82 
83  if (instance != NULL ) {
84  delete instance;
85  }
86  return;
87 }
88 
89 
90 
91 extern "C"
92 {
93 
94 
99 static void
101 {
102 
103  if ( self->vqe != NULL ) {
104  // deallocate the instance of class N_Qubit_Decomposition
106  self->vqe = NULL;
107  }
108 
109  if ( self->Hamiltonian != NULL ) {
110  // release the unitary to be decomposed
111  Py_DECREF(self->Hamiltonian);
112  self->Hamiltonian = NULL;
113  }
114 
115  Py_TYPE(self)->tp_free((PyObject *) self);
116 
117 }
118 
123 static PyObject *
125 {
127  self = (qgd_Variational_Quantum_Eigensolver_Base_Wrapper *) type->tp_alloc(type, 0);
128  if (self != NULL) {}
129 
130  self->vqe = NULL;
131  self->Hamiltonian = NULL;
132 
133  return (PyObject *) self;
134 }
135 
136 
143 static int
145 {
146  // The tuple of expected keywords
147  static char *kwlist[] = {(char*)"Hamiltonian_data", (char*)"Hamiltonian_indices", (char*)"Hamiltonian_indptr", (char*)"qbit_num", (char*)"config", (char*)"accelerator_num", NULL};
148 
149  // initiate variables for input arguments
150  PyArrayObject *Hamiltonian_data_arg = NULL;
151  PyArrayObject *Hamiltonian_indices_arg = NULL;
152  PyArrayObject *Hamiltonian_indptr_arg = NULL;
153  int qbit_num = -1;
154  PyObject *config_arg = NULL;
155  int accelerator_num = 0;
156 
157  // parsing input arguments
158  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOiOi", kwlist,
159  &Hamiltonian_data_arg, &Hamiltonian_indices_arg, &Hamiltonian_indptr_arg, &qbit_num, &config_arg, &accelerator_num))
160  return -1;
161 
162  int shape = Power_of_2(qbit_num);
163  // convert python object array to numpy C API array
164  if ( Hamiltonian_data_arg == NULL ) return -1;
165 
166  Hamiltonian_data_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)Hamiltonian_data_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
167  QGD_Complex16* Hamiltonian_data = (QGD_Complex16*)PyArray_DATA(Hamiltonian_data_arg);
168  int NNZ = static_cast<int>(PyArray_DIMS(Hamiltonian_data_arg)[0]);
169 
170  if ( Hamiltonian_indices_arg == NULL ) return -1;
171 
172  Hamiltonian_indices_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)Hamiltonian_indices_arg, NPY_INT32, NPY_ARRAY_IN_ARRAY);
173  int* Hamiltonian_indices = (int*)PyArray_DATA(Hamiltonian_indices_arg);
174 
175  if ( Hamiltonian_indptr_arg == NULL ) return -1;
176 
177  Hamiltonian_indptr_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)Hamiltonian_indptr_arg, NPY_INT32, NPY_ARRAY_IN_ARRAY);
178  int* Hamiltonian_indptr = (int*)PyArray_DATA(Hamiltonian_indptr_arg);
179 
180  Matrix_sparse Hamiltonian_mtx = Matrix_sparse(Hamiltonian_data, shape, shape, NNZ, Hamiltonian_indices, Hamiltonian_indptr);
181 
182  // integer type config metadata utilized during the optimization
183  std::map<std::string, Config_Element> config;
184 
185 
186  // keys and values of the config dict
187  PyObject *key, *value;
188  Py_ssize_t pos = 0;
189 
190  while (PyDict_Next(config_arg, &pos, &key, &value)) {
191 
192  // determine the initial guess type
193  PyObject* key_string = PyObject_Str(key);
194  PyObject* key_string_unicode = PyUnicode_AsEncodedString(key_string, "utf-8", "~E~");
195  const char* key_C = PyBytes_AS_STRING(key_string_unicode);
196 
197  std::string key_Cpp( key_C );
198  Config_Element element;
199 
200  if (PyBool_Check(value)) {
201  element.set_property(key_Cpp, value == Py_True);
202  config[key_Cpp] = element;
203  }
204  else if ( PyLong_Check( value ) ) {
205  element.set_property( key_Cpp, PyLong_AsLongLong( value ) );
206  config[ key_Cpp ] = element;
207  }
208  else if ( PyFloat_Check( value ) ) {
209  element.set_property( key_Cpp, PyFloat_AsDouble( value ) );
210  config[ key_Cpp ] = element;
211  }
212  else {
213 
214  }
215 
216  }
217 
218 
219  // create an instance of the class N_Qubit_Decomposition
220  if (qbit_num > 0 ) {
221  self->vqe = create_qgd_Variational_Quantum_Eigensolver_Base(Hamiltonian_mtx, qbit_num, config, accelerator_num);
222  }
223  else {
224  std::cout << "The number of qubits should be given as a positive integer, " << qbit_num << " was given" << std::endl;
225  return -1;
226  }
227 
228 
229  return 0;
230 }
231 
232 
233 
238 static PyObject *
240 
241  int parameter_num = self->vqe->get_parameter_num();
242 
243  Matrix_real parameters_mtx(1, parameter_num);
244  double* parameters = parameters_mtx.get_data();
245  self->vqe->get_optimized_parameters(parameters);
246 
247 
248  // convert to numpy array
249  parameters_mtx.set_owner(false);
250  PyObject * parameter_arr = matrix_real_to_numpy( parameters_mtx );
251 
252  return parameter_arr;
253 }
254 
255 
256 static PyObject *
258 {
259 
260  // starting the decomposition
261  try {
262  self->vqe->start_optimization();
263  }
264  catch (std::string err) {
265  PyErr_SetString(PyExc_Exception, err.c_str());
266  std::cout << err << std::endl;
267  return NULL;
268  }
269  catch(...) {
270  std::string err( "Invalid pointer to decomposition class");
271  PyErr_SetString(PyExc_Exception, err.c_str());
272  return NULL;
273  }
274 
275 
276 
277  return Py_BuildValue("i", 0);
278 
279 }
280 
281 
285 static PyObject *
287 
288  int qbit_num = 0;
289 
290  try {
291  qbit_num = self->vqe->get_qbit_num();
292  }
293  catch (std::string err) {
294  PyErr_SetString(PyExc_Exception, err.c_str());
295  std::cout << err << std::endl;
296  return NULL;
297  }
298  catch(...) {
299  std::string err( "Invalid pointer to decomposition class");
300  PyErr_SetString(PyExc_Exception, err.c_str());
301  return NULL;
302  }
303 
304 
305  return Py_BuildValue("i", qbit_num );
306 
307 }
308 
309 
310 
314 static PyObject *
316 
317  PyArrayObject * parameters_arr = NULL;
318 
319 
320  // parsing input arguments
321  if (!PyArg_ParseTuple(args, "|O", &parameters_arr ))
322  return Py_BuildValue("i", -1);
323 
324 
325  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
326  Py_INCREF(parameters_arr);
327  }
328  else {
329  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
330  }
331 
332 
333  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arr );
334 
335 
336 
337  try {
338  self->vqe->set_optimized_parameters(parameters_mtx.get_data(), parameters_mtx.size());
339  }
340  catch (std::string err ) {
341  PyErr_SetString(PyExc_Exception, err.c_str());
342  return NULL;
343  }
344  catch(...) {
345  std::string err( "Invalid pointer to decomposition class");
346  PyErr_SetString(PyExc_Exception, err.c_str());
347  return NULL;
348  }
349 
350  Py_DECREF(parameters_arr);
351 
352  return Py_BuildValue("i", 0);
353 }
354 
355 
356 
360 static PyObject *
362 
363  PyArrayObject * initial_state_arg = NULL;
364 
365  // parsing input arguments
366  if (!PyArg_ParseTuple(args, "|O", &initial_state_arg )) {
367  PyErr_SetString(PyExc_Exception, "error occured during input parsing");
368  return NULL;
369  }
370 
371  if ( PyArray_IS_C_CONTIGUOUS(initial_state_arg) ) {
372  Py_INCREF(initial_state_arg);
373  }
374  else {
375  initial_state_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)initial_state_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
376  }
377 
378 
379  Matrix initial_state_mtx = numpy2matrix( initial_state_arg );
380 
381 
382 
383  try {
384  self->vqe->set_initial_state( initial_state_mtx );
385  }
386  catch (std::string err ) {
387  PyErr_SetString(PyExc_Exception, err.c_str());
388  return NULL;
389  }
390  catch(...) {
391  std::string err( "Invalid pointer to decomposition class");
392  PyErr_SetString(PyExc_Exception, err.c_str());
393  return NULL;
394  }
395 
396 
397 
398 
399  Py_DECREF(initial_state_arg);
400 
401  return Py_BuildValue("i", 0);
402 }
403 
404 
408 static PyObject *
411 
412  PyObject* density_noise_arg = NULL;
413  if (!PyArg_ParseTuple(args, "|O", &density_noise_arg )) {
414  PyErr_SetString(PyExc_Exception, "error occured during density-noise input parsing");
415  return NULL;
416  }
417 
418  std::vector<DensityNoiseSpec> density_noise_specs;
419  if (density_noise_arg != NULL && density_noise_arg != Py_None) {
420  if (!PyList_Check(density_noise_arg)) {
421  PyErr_SetString(PyExc_Exception, "density_noise should be given as a list of dictionaries");
422  return NULL;
423  }
424 
425  for (Py_ssize_t idx = 0; idx < PyList_Size(density_noise_arg); ++idx) {
426  PyObject* noise_item = PyList_GetItem(density_noise_arg, idx);
427  if (!PyDict_Check(noise_item)) {
428  PyErr_SetString(PyExc_Exception, "Each density_noise item should be a dictionary");
429  return NULL;
430  }
431 
432  PyObject* channel_obj = PyDict_GetItemString(noise_item, "channel");
433  PyObject* target_obj = PyDict_GetItemString(noise_item, "target");
434  PyObject* after_gate_index_obj = PyDict_GetItemString(noise_item, "after_gate_index");
435  PyObject* value_obj = PyDict_GetItemString(noise_item, "value");
436  if (!channel_obj || !target_obj || !after_gate_index_obj || !value_obj) {
437  PyErr_SetString(PyExc_Exception, "Each density_noise item should define channel, target, after_gate_index, and value");
438  return NULL;
439  }
440 
441  PyObject* channel_unicode = PyUnicode_AsEncodedString(channel_obj, "utf-8", "~E~");
442  if (channel_unicode == NULL) {
443  PyErr_SetString(PyExc_Exception, "density_noise channel should be a UTF-8 string");
444  return NULL;
445  }
446 
447  const char* channel_C = PyBytes_AS_STRING(channel_unicode);
448  std::string channel(channel_C);
449  Py_DECREF(channel_unicode);
450 
452  if (channel == "local_depolarizing") {
454  }
455  else if (channel == "amplitude_damping") {
457  }
458  else if (channel == "phase_damping") {
459  spec.type = PHASE_DAMPING_NOISE;
460  }
461  else {
462  std::string err("Unsupported density_noise channel: " + channel);
463  PyErr_SetString(PyExc_Exception, err.c_str());
464  return NULL;
465  }
466 
467  spec.target_qbit = (int)PyLong_AsLong(target_obj);
468  spec.after_gate_index = (int)PyLong_AsLong(after_gate_index_obj);
469  spec.value = PyFloat_AsDouble(value_obj);
470  if (PyErr_Occurred()) {
471  PyErr_SetString(PyExc_Exception, "Failed to parse density_noise numeric values");
472  return NULL;
473  }
474 
475  density_noise_specs.push_back(spec);
476  }
477  }
478 
479  try {
480  self->vqe->set_density_noise_specs( density_noise_specs );
481  }
482  catch (std::string err ) {
483  PyErr_SetString(PyExc_Exception, err.c_str());
484  return NULL;
485  }
486  catch(...) {
487  std::string err( "Invalid pointer to decomposition class");
488  PyErr_SetString(PyExc_Exception, err.c_str());
489  return NULL;
490  }
491 
492  return Py_BuildValue("i", 0);
493 }
494 
495 
499 static PyObject *
502 
503  auto set_dict_item = [](PyObject* dict, const char* key, PyObject* value) -> int {
504  if (value == NULL) {
505  return -1;
506  }
507 
508  int status = PyDict_SetItemString(dict, key, value);
509  Py_DECREF(value);
510  return status;
511  };
512  auto new_none = []() -> PyObject* {
513  Py_INCREF(Py_None);
514  return Py_None;
515  };
516 
517  PyObject* result = NULL;
518  PyObject* operations_list = NULL;
519 
520  try {
521  std::vector<DensityBridgeOperationInfo> operations =
522  self->vqe->inspect_density_bridge();
523 
524  int gate_count = 0;
525  int noise_count = 0;
526 
527  result = PyDict_New();
528  if (result == NULL) {
529  return NULL;
530  }
531 
532  operations_list = PyList_New((Py_ssize_t)operations.size());
533  if (operations_list == NULL) {
534  Py_DECREF(result);
535  return NULL;
536  }
537 
538  for (size_t idx = 0; idx < operations.size(); ++idx) {
539  const auto& operation = operations[idx];
540  if (operation.is_unitary) {
541  gate_count++;
542  }
543  else {
544  noise_count++;
545  }
546 
547  PyObject* op_dict = PyDict_New();
548  if (op_dict == NULL) {
549  Py_DECREF(operations_list);
550  Py_DECREF(result);
551  return NULL;
552  }
553 
554  if (set_dict_item(op_dict, "index", PyLong_FromLong((long)idx)) != 0 ||
555  set_dict_item(
556  op_dict,
557  "kind",
558  PyUnicode_FromString(operation.is_unitary ? "gate" : "noise")
559  ) != 0 ||
560  set_dict_item(
561  op_dict, "name", PyUnicode_FromString(operation.name.c_str())
562  ) != 0 ||
563  set_dict_item(op_dict, "is_unitary", PyBool_FromLong(operation.is_unitary ? 1 : 0)) != 0 ||
564  set_dict_item(
565  op_dict,
566  "source_gate_index",
567  PyLong_FromLong(operation.source_gate_index)
568  ) != 0 ||
569  set_dict_item(
570  op_dict, "target_qbit", PyLong_FromLong(operation.target_qbit)
571  ) != 0 ||
572  set_dict_item(
573  op_dict,
574  "control_qbit",
575  operation.control_qbit >= 0
576  ? PyLong_FromLong(operation.control_qbit)
577  : new_none()
578  ) != 0 ||
579  set_dict_item(
580  op_dict, "param_count", PyLong_FromLong(operation.param_count)
581  ) != 0 ||
582  set_dict_item(
583  op_dict, "param_start", PyLong_FromLong(operation.param_start)
584  ) != 0 ||
585  set_dict_item(
586  op_dict,
587  "fixed_value",
588  operation.has_fixed_value
589  ? PyFloat_FromDouble(operation.fixed_value)
590  : new_none()
591  ) != 0) {
592  Py_DECREF(op_dict);
593  Py_DECREF(operations_list);
594  Py_DECREF(result);
595  return NULL;
596  }
597 
598  PyList_SET_ITEM(operations_list, (Py_ssize_t)idx, op_dict);
599  }
600 
601  if (set_dict_item(result, "backend", PyUnicode_FromString("density_matrix")) != 0 ||
602  set_dict_item(
603  result,
604  "source_type",
605  PyUnicode_FromString(
606  self->vqe->get_density_bridge_source_label().c_str()
607  )
608  ) != 0 ||
609  set_dict_item(
610  result, "qbit_num", PyLong_FromLong(self->vqe->get_qbit_num())
611  ) != 0 ||
612  set_dict_item(
613  result,
614  "parameter_count",
615  PyLong_FromLong(self->vqe->get_parameter_num())
616  ) != 0 ||
617  set_dict_item(
618  result,
619  "operation_count",
620  PyLong_FromLong((long)operations.size())
621  ) != 0 ||
622  set_dict_item(result, "gate_count", PyLong_FromLong(gate_count)) != 0 ||
623  set_dict_item(result, "noise_count", PyLong_FromLong(noise_count)) != 0 ||
624  PyDict_SetItemString(result, "operations", operations_list) != 0) {
625  Py_DECREF(operations_list);
626  Py_DECREF(result);
627  return NULL;
628  }
629 
630  Py_DECREF(operations_list);
631  return result;
632  }
633  catch (std::string err) {
634  Py_XDECREF(operations_list);
635  Py_XDECREF(result);
636  PyErr_SetString(PyExc_Exception, err.c_str());
637  return NULL;
638  }
639  catch (...) {
640  Py_XDECREF(operations_list);
641  Py_XDECREF(result);
642  std::string err("Invalid pointer to decomposition class");
643  PyErr_SetString(PyExc_Exception, err.c_str());
644  return NULL;
645  }
646 }
647 
648 
652 static PyObject *
654 
655 
656 
657  // initiate variables for input arguments
658  PyObject* filename_py=NULL;
659 
660  // parsing input arguments
661  if (!PyArg_ParseTuple(args, "|O", &filename_py )) return Py_BuildValue("i", -1);
662 
663  // determine the optimizaton method
664  PyObject* filename_string = PyObject_Str(filename_py);
665  PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
666  const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
667  std::string filename_str( filename_C );
668 
669 
670  try {
671  self->vqe->set_gate_structure( filename_str );
672  }
673  catch (std::string err ) {
674  PyErr_SetString(PyExc_Exception, err.c_str());
675  return NULL;
676  }
677  catch(...) {
678  std::string err( "Invalid pointer to decomposition class");
679  PyErr_SetString(PyExc_Exception, err.c_str());
680  return NULL;
681  }
682 
683 
684 
685  return Py_BuildValue("i", 0);
686 
687 }
688 
689 static PyObject *
691 
692  // initiate variables for input arguments
693  double tolerance;
694 
695  // parsing input arguments
696  if (!PyArg_ParseTuple(args, "|d", &tolerance )) return Py_BuildValue("i", -1);
697 
698 
699  // set maximal layer nums on the C++ side
700  self->vqe->set_optimization_tolerance( tolerance );
701 
702 
703  return Py_BuildValue("i", 0);
704 }
705 
706 
707 
708 static PyObject *
710 
711  PyArrayObject * parameters_arr = NULL;
712  PyArrayObject * unitary_arg = NULL;
713 
714 
715  // parsing input arguments
716  if (!PyArg_ParseTuple(args, "|OO", &parameters_arr, &unitary_arg ))
717  return Py_BuildValue("i", -1);
718 
719 
720  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
721  Py_INCREF(parameters_arr);
722  }
723  else {
724  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
725  }
726 
727  // get the C++ wrapper around the data
728  Matrix_real&& parameters_mtx = numpy2matrix_real( parameters_arr );
729 
730 
731  // convert python object array to numpy C API array
732  if ( unitary_arg == NULL ) {
733  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
734  return NULL;
735  }
736 
737  PyArrayObject* unitary = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)unitary_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
738 
739  // test C-style contiguous memory allocation of the array
740  if ( !PyArray_IS_C_CONTIGUOUS(unitary) ) {
741  PyErr_SetString(PyExc_Exception, "input mtrix is not memory contiguous");
742  return NULL;
743  }
744 
745 
746  // create QGD version of the input matrix
747  Matrix unitary_mtx = numpy2matrix(unitary);
748 
749 
750  self->vqe->apply_to( parameters_mtx, unitary_mtx );
751 
752  if (unitary_mtx.data != PyArray_DATA(unitary)) {
753  memcpy(PyArray_DATA(unitary), unitary_mtx.data, unitary_mtx.size() * sizeof(QGD_Complex16));
754  }
755 
756  Py_DECREF(parameters_arr);
757  Py_DECREF(unitary);
758 
759  return Py_BuildValue("i", 0);
760 }
761 
762 
763 
764 static PyObject *
766 {
767 
768  // The tuple of expected keywords
769  static char *kwlist[] = {(char*)"optimizer", NULL};
770 
771  PyObject* optimizer_arg = NULL;
772 
773 
774  // parsing input arguments
775  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &optimizer_arg)) {
776 
777  std::string err( "Unsuccessful argument parsing not ");
778  PyErr_SetString(PyExc_Exception, err.c_str());
779  return NULL;
780 
781  }
782 
783  if ( optimizer_arg == NULL ) {
784  std::string err( "optimizer argument not set");
785  PyErr_SetString(PyExc_Exception, err.c_str());
786  return NULL;
787  }
788 
789 
790  PyObject* optimizer_string = PyObject_Str(optimizer_arg);
791  PyObject* optimizer_string_unicode = PyUnicode_AsEncodedString(optimizer_string, "utf-8", "~E~");
792  const char* optimizer_C = PyBytes_AS_STRING(optimizer_string_unicode);
793 
794  optimization_aglorithms qgd_optimizer;
795  if ( strcmp("agents", optimizer_C) == 0 || strcmp("AGENTS", optimizer_C) == 0) {
796  qgd_optimizer = AGENTS;
797  }
798  else if ( strcmp("agents_combined", optimizer_C)==0 || strcmp("AGENTS_COMBINED", optimizer_C)==0) {
799  qgd_optimizer = AGENTS_COMBINED;
800  }
801  else if ( strcmp("cosined", optimizer_C)==0 || strcmp("COSINE", optimizer_C)==0) {
802  qgd_optimizer = COSINE;
803  }
804  else if ( strcmp("grad_descend_phase_shift_rule", optimizer_C)==0 || strcmp("GRAD_DESCEND_PARAMETER_SHIFT_RULE", optimizer_C)==0) {
805  qgd_optimizer = GRAD_DESCEND_PARAMETER_SHIFT_RULE;
806  }
807  else if ( strcmp("bfgs", optimizer_C)==0 || strcmp("BFGS", optimizer_C)==0) {
808  qgd_optimizer = BFGS;
809  }
810  else if ( strcmp("adam", optimizer_C)==0 || strcmp("ADAM", optimizer_C)==0) {
811  qgd_optimizer = ADAM;
812  }
813  else if ( strcmp("grad_descend", optimizer_C)==0 || strcmp("GRAD_DESCEND", optimizer_C)==0) {
814  qgd_optimizer = GRAD_DESCEND;
815  }
816  else if ( strcmp("bayes_opt", optimizer_C)==0 || strcmp("BAYES_OPT", optimizer_C)==0) {
817  qgd_optimizer = BAYES_OPT;
818  }
819  else if ( strcmp("bayes_agents", optimizer_C)==0 || strcmp("BAYES_AGENTS", optimizer_C)==0) {
820  qgd_optimizer = BAYES_AGENTS;
821  }
822  else {
823  std::string err("Unsupported optimizer: ");
824  err += optimizer_C;
825  PyErr_SetString(PyExc_Exception, err.c_str());
826  return NULL;
827  }
828 
829 
830  try {
831  self->vqe->set_optimizer(qgd_optimizer);
832  }
833  catch (std::string err) {
834  PyErr_SetString(PyExc_Exception, err.c_str());
835  std::cout << err << std::endl;
836  return NULL;
837  }
838  catch(...) {
839  std::string err( "Invalid pointer to decomposition class");
840  PyErr_SetString(PyExc_Exception, err.c_str());
841  return NULL;
842  }
843 
844 
845  return Py_BuildValue("i", 0);
846 
847 }
848 
849 static PyObject *
851 {
852 
853  // The tuple of expected keywords
854  static char *kwlist[] = {(char*)"optimizer", NULL};
855 
856  PyObject* ansatz_arg = NULL;
857 
858 
859  // parsing input arguments
860  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &ansatz_arg)) {
861 
862  std::string err( "Unsuccessful argument parsing not ");
863  PyErr_SetString(PyExc_Exception, err.c_str());
864  return NULL;
865 
866  }
867 
868 
869  if ( ansatz_arg == NULL ) {
870  std::string err( "optimizer argument not set");
871  PyErr_SetString(PyExc_Exception, err.c_str());
872  return NULL;
873  }
874 
875 
876 
877  PyObject* ansatz_string = PyObject_Str(ansatz_arg);
878  PyObject* ansatz_string_unicode = PyUnicode_AsEncodedString(ansatz_string, "utf-8", "~E~");
879  const char* ansatz_C = PyBytes_AS_STRING(ansatz_string_unicode);
880 
881 
882  ansatz_type qgd_ansatz;
883 
884  if ( strcmp("hea", ansatz_C) == 0 || strcmp("HEA", ansatz_C) == 0) {
885  qgd_ansatz = HEA;
886  }
887  else if ( strcmp("hea_zyz", ansatz_C) == 0 || strcmp("HEA_ZYZ", ansatz_C) == 0) {
888  qgd_ansatz = HEA_ZYZ;
889  }
890  else {
891  std::string err("Unsupported ansatz: ");
892  err += ansatz_C;
893  PyErr_SetString(PyExc_Exception, err.c_str());
894  return NULL;
895  }
896 
897 
898  try {
899  self->vqe->set_ansatz(qgd_ansatz);
900  }
901  catch (std::string err) {
902  PyErr_SetString(PyExc_Exception, err.c_str());
903  std::cout << err << std::endl;
904  return NULL;
905  }
906  catch(...) {
907  std::string err( "Invalid pointer to decomposition class");
908  PyErr_SetString(PyExc_Exception, err.c_str());
909  return NULL;
910  }
911 
912 
913  return Py_BuildValue("i", 0);
914 
915 }
916 
917 
921 static PyObject *
923 {
924 
925 
926  PyArrayObject * parameters_arr = NULL;
927  PyArrayObject * input_state_arg = NULL;
928  PyObject * qubit_list_arg = NULL;
929 
930 
931  // parsing input arguments
932  if (!PyArg_ParseTuple(args, "|OOO", &parameters_arr, &input_state_arg, &qubit_list_arg ))
933  return Py_BuildValue("i", -1);
934 
935 
936  if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
937  Py_INCREF(parameters_arr);
938  }
939  else {
940  parameters_arr = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
941  }
942 
943  // get the C++ wrapper around the data
944  Matrix_real&& parameters_mtx = numpy2matrix_real( parameters_arr );
945 
946  // convert python object array to numpy C API array
947  if ( input_state_arg == NULL ) {
948  PyErr_SetString(PyExc_Exception, "Input matrix was not given");
949  return NULL;
950  }
951 
952  PyArrayObject* input_state = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)input_state_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
953 
954  // test C-style contiguous memory allocation of the array
955  if ( !PyArray_IS_C_CONTIGUOUS(input_state) ) {
956  PyErr_SetString(PyExc_Exception, "input mtrix is not memory contiguous");
957  return NULL;
958  }
959 
960 
961  // create QGD version of the input matrix
962  Matrix input_state_mtx = numpy2matrix(input_state);
963 
964 
965  // check input argument qbit_list
966  if ( qubit_list_arg == NULL || (!PyList_Check( qubit_list_arg )) ) {
967  PyErr_SetString(PyExc_Exception, "qubit_list should be a list");
968  return NULL;
969  }
970 
971  Py_ssize_t reduced_qbit_num = PyList_Size( qubit_list_arg );
972 
973  matrix_base<int> qbit_list_mtx( (int)reduced_qbit_num, 1);
974  for ( int idx=0; idx<reduced_qbit_num; idx++ ) {
975 
976  PyObject* item = PyList_GET_ITEM( qubit_list_arg, idx );
977  qbit_list_mtx[idx] = (int) PyLong_AsLong( item );
978 
979  }
980 
981 
982  double entropy = -1;
983 
984 
985  try {
986  entropy = self->vqe->get_second_Renyi_entropy( parameters_mtx, input_state_mtx, qbit_list_mtx );
987  }
988  catch (std::string err) {
989  PyErr_SetString(PyExc_Exception, err.c_str());
990  std::cout << err << std::endl;
991  return NULL;
992  }
993  catch(...) {
994  std::string err( "Invalid pointer to decomposition class");
995  PyErr_SetString(PyExc_Exception, err.c_str());
996  return NULL;
997  }
998 
999 
1000  Py_DECREF(parameters_arr);
1001  Py_DECREF(input_state);
1002 
1003 
1004 
1005  PyObject* p = Py_BuildValue("d", entropy);
1006 
1007  return p;
1008 }
1009 
1010 
1011 static PyObject *
1013 
1014  // initiate variables for input arguments
1015  int layers;
1016  int inner_blocks;
1017 
1018  // parsing input arguments
1019  if (!PyArg_ParseTuple(args, "|ii", &layers, &inner_blocks )) return Py_BuildValue("i", -1);
1020 
1021 
1022  try {
1023  self->vqe->generate_circuit( layers, inner_blocks );
1024  }
1025  catch (std::string err) {
1026  PyErr_SetString(PyExc_Exception, err.c_str());
1027  std::cout << err << std::endl;
1028  return NULL;
1029  }
1030  catch(...) {
1031  std::string err( "Invalid pointer to decomposition class");
1032  PyErr_SetString(PyExc_Exception, err.c_str());
1033  return NULL;
1034  }
1035 
1036  return Py_BuildValue("i", 0);
1037 
1038 
1039 }
1040 
1041 static PyObject *
1043 {
1044 
1045 
1046  PyArrayObject* parameters_arg = NULL;
1047 
1048 
1049  // parsing input arguments
1050  if (!PyArg_ParseTuple(args, "|O", &parameters_arg )) {
1051 
1052  std::string err( "Unsuccessful argument parsing not ");
1053  PyErr_SetString(PyExc_Exception, err.c_str());
1054  return NULL;
1055 
1056  }
1057 
1058  // establish memory contiguous arrays for C calculations
1059  if ( PyArray_IS_C_CONTIGUOUS(parameters_arg) && PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ){
1060  Py_INCREF(parameters_arg);
1061  }
1062  else if (PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ) {
1063  parameters_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arg, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
1064  }
1065  else {
1066  std::string err( "Parameters should be should be real (given in float64 format)");
1067  PyErr_SetString(PyExc_Exception, err.c_str());
1068  return NULL;
1069  }
1070 
1071 
1072  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arg );
1073  double f0;
1074 
1075  try {
1076  f0 = self->vqe->optimization_problem(parameters_mtx );
1077  }
1078  catch (std::string err ) {
1079  PyErr_SetString(PyExc_Exception, err.c_str());
1080  return NULL;
1081  }
1082  catch (...) {
1083  std::string err( "Invalid pointer to decomposition class");
1084  PyErr_SetString(PyExc_Exception, err.c_str());
1085  return NULL;
1086  }
1087 
1088  Py_DECREF(parameters_arg);
1089 
1090 
1091  return Py_BuildValue("d", f0);
1092 }
1093 
1094 //============================================================================================================================================
1095 //============================================================================================================================================
1096 //============================================================================================================================================
1118 static PyObject *
1121 {
1122  PyArrayObject *state_left_arg = NULL;
1123  PyArrayObject *state_right_arg = NULL;
1124 
1125  // Parse two numpy arrays
1126  if (!PyArg_ParseTuple(args, "|OO", &state_left_arg, &state_right_arg)) {
1127  PyErr_SetString(PyExc_Exception, "Failed to parse input states");
1128  return NULL;
1129  }
1130 
1131  if (!state_left_arg || !state_right_arg) {
1132  PyErr_SetString(PyExc_Exception, "Two state vectors are required");
1133  return NULL;
1134  }
1135 
1136  // Convert Python arrays → Matrix
1137  Matrix state_left = numpy2matrix(state_left_arg);
1138  Matrix state_right = numpy2matrix(state_right_arg);
1139 
1140  double energy = 0.0;
1141  try {
1142  energy = self->vqe->Expectation_value_of_energy_real(state_left, state_right);
1143  } catch (std::string &err) {
1144  PyErr_SetString(PyExc_Exception, err.c_str());
1145  return NULL;
1146  } catch (...) {
1147  PyErr_SetString(PyExc_Exception, "Unexpected error in Expectation_value_of_energy_real");
1148  return NULL;
1149  }
1150 
1151  return Py_BuildValue("d", energy);
1152 }
1153 
1183 static PyObject *
1186 {
1187  PyObject *dict_arg = NULL;
1188  if (!PyArg_ParseTuple(args, "|O", &dict_arg)) {
1189  PyErr_SetString(PyExc_Exception, "Failed to parse dictionary argument.");
1190  return NULL;
1191  }
1192  if (!PyDict_Check(dict_arg)) {
1193  PyErr_SetString(PyExc_Exception, "Expected a Python dictionary as input.");
1194  return NULL;
1195  }
1196 
1197  // required keys
1198  PyObject* shots_obj = PyDict_GetItemString(dict_arg, "shots");
1199  PyObject* p_readout_obj = PyDict_GetItemString(dict_arg, "p_readout");
1200  if (!shots_obj || !p_readout_obj) {
1201  PyErr_SetString(PyExc_Exception, "Missing 'shots' or 'p_readout' in dictionary.");
1202  return NULL;
1203  }
1204  const int shots = (int)PyLong_AsLong(shots_obj);
1205  const double p_readout = PyFloat_AsDouble(p_readout_obj);
1206 
1207  // Optional seed
1208  uint64_t seed = 0;
1209  PyObject *seed_obj = PyDict_GetItemString(dict_arg, "seed");
1210  if (seed_obj && PyLong_Check(seed_obj)) {
1211  seed = (uint64_t)PyLong_AsUnsignedLongLong(seed_obj);
1212  }
1213 
1214  // --- Parse ZZ terms ---
1215  PyObject *zz_list = PyDict_GetItemString(dict_arg, "zz_terms");
1216  if (!zz_list || !PyList_Check(zz_list)) {
1217  PyErr_SetString(PyExc_Exception, "Missing or invalid 'zz_terms' list.");
1218  return NULL;
1219  }
1220  self->vqe->zz_terms.clear();
1221  for (Py_ssize_t k = 0; k < PyList_Size(zz_list); ++k) {
1222  PyObject *item = PyList_GetItem(zz_list, k);
1223  int i_idx = (int)PyLong_AsLong(PyDict_GetItemString(item, "i"));
1224  int j_idx = (int)PyLong_AsLong(PyDict_GetItemString(item, "j"));
1225  double coeff = PyFloat_AsDouble(PyDict_GetItemString(item, "coeff"));
1226  self->vqe->zz_terms.emplace_back(i_idx, j_idx, coeff);
1227  }
1228 
1229  // --- Parse Z terms (optional) ---
1230  self->vqe->z_terms.clear();
1231  PyObject *z_list = PyDict_GetItemString(dict_arg, "z_terms");
1232  if (z_list && PyList_Check(z_list)) {
1233  for (Py_ssize_t k = 0; k < PyList_Size(z_list); ++k) {
1234  PyObject *item = PyList_GetItem(z_list, k);
1235  int i_idx = (int)PyLong_AsLong(PyDict_GetItemString(item, "i"));
1236  double coeff = PyFloat_AsDouble(PyDict_GetItemString(item, "coeff"));
1237  self->vqe->z_terms.emplace_back(i_idx, coeff);
1238  }
1239  }
1240 
1241  // --- Parse XX terms (new!) ---
1242  self->vqe->xx_terms.clear();
1243  PyObject *xx_list = PyDict_GetItemString(dict_arg, "xx_terms");
1244  if (xx_list && PyList_Check(xx_list)) {
1245  for (Py_ssize_t k = 0; k < PyList_Size(xx_list); ++k) {
1246  PyObject *item = PyList_GetItem(xx_list, k);
1247 
1248  int i_idx = (int)PyLong_AsLong(PyDict_GetItemString(item, "i"));
1249  int j_idx = (int)PyLong_AsLong(PyDict_GetItemString(item, "j"));
1250  double coeff = PyFloat_AsDouble(PyDict_GetItemString(item, "coeff"));
1251 
1252  self->vqe->xx_terms.emplace_back(i_idx, j_idx, coeff);
1253  }
1254  }
1255 
1256  // --- Build evolved quantum state ---
1257  Matrix state = self->vqe->get_initial_state().copy();
1258 
1259  // Retrieve current optimized parameters
1260  Matrix_real parameters = self->vqe->get_optimized_parameters();
1261 
1262  // Apply ansatz
1263  self->vqe->apply_to(parameters, state);
1264 
1265  SimulationResult res;
1266  try {
1267  res = self->vqe->Expectation_value_with_shot_noise_real(state, shots, seed, p_readout);
1268  }
1269  catch (const std::string &err) {
1270  PyErr_SetString(PyExc_Exception, err.c_str());
1271  return NULL;
1272  }
1273  catch (const char *err) {
1274  PyErr_SetString(PyExc_Exception, err);
1275  return NULL;
1276  }
1277  catch (const std::exception &e) {
1278  PyErr_SetString(PyExc_Exception, e.what());
1279  return NULL;
1280  }
1281  catch (...) {
1282  PyErr_SetString(PyExc_Exception, "Unknown C++ exception in Expectation_Value_Shot_Noise");
1283  return NULL;
1284  }
1285 
1286  return Py_BuildValue("{s:d, s:d, s:d}",
1287  "mean", res.mean,
1288  "variance", res.variance,
1289  "std_error", res.std_error);
1290 }
1291 
1292 //============================================================================================================================================
1293 //============================================================================================================================================
1294 //============================================================================================================================================
1295 
1296 
1297 
1298 
1299 
1304 static PyObject *
1306 {
1307 
1308 
1309  PyObject* parameter_list = NULL;
1310 
1311 
1312  // parsing input arguments
1313  if (!PyArg_ParseTuple(args, "|O", &parameter_list )) {
1314 
1315  std::string err( "Unsuccessful argument parsing not ");
1316  PyErr_SetString(PyExc_Exception, err.c_str());
1317  return NULL;
1318 
1319  }
1320 
1321  // check input argument qbit_list
1322  if ( parameter_list == NULL || (!PyList_Check( parameter_list )) ) {
1323  PyErr_SetString(PyExc_Exception, "Parameters should be given as a list of parameter arrays");
1324  return NULL;
1325  }
1326 
1327  int tasks = (int)PyList_Size( parameter_list );
1328  Matrix_real result_mtx;
1329 
1330 
1331  try {
1332  std::vector<Matrix_real> parameters_vec;
1333  parameters_vec.resize(tasks);
1334 
1335  for( int idx=0; idx<tasks; idx++ ) {
1336  PyArrayObject* parameters_py = (PyArrayObject*)PyList_GET_ITEM( parameter_list, idx );
1337 
1338  // establish memory contiguous arrays for C calculations
1339  if ( PyArray_IS_C_CONTIGUOUS(parameters_py) && PyArray_TYPE(parameters_py) == NPY_FLOAT64 ){
1340  Py_INCREF(parameters_py);
1341  }
1342  else if (PyArray_TYPE(parameters_py) == NPY_FLOAT64 ) {
1343  parameters_py = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_py, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
1344  }
1345  else {
1346  Py_DECREF(parameters_py);
1347  std::string err( "Parameters should be should be real (given in float64 format)");
1348  PyErr_SetString(PyExc_Exception, err.c_str());
1349  return NULL;
1350  }
1351 
1352  Matrix_real parameters_mtx = numpy2matrix_real( parameters_py );
1353  parameters_vec[idx] = parameters_mtx;
1354 
1355  Py_DECREF(parameters_py);
1356 
1357  }
1358 
1359 
1360  result_mtx = self->vqe->optimization_problem_batched( parameters_vec );
1361  }
1362  catch (std::string err ) {
1363  PyErr_SetString(PyExc_Exception, err.c_str());
1364  return NULL;
1365  }
1366  catch (...) {
1367  std::string err( "Invalid pointer to decomposition class");
1368  PyErr_SetString(PyExc_Exception, err.c_str());
1369  return NULL;
1370  }
1371 
1372  // convert to numpy array
1373  result_mtx.set_owner(false);
1374  PyObject *result_py = matrix_real_to_numpy( result_mtx );
1375 
1376 
1377  return result_py;
1378 }
1379 
1380 
1381 
1382 
1386 static PyObject *
1388 
1389  int parameter_num = self->vqe->get_parameter_num();
1390 
1391  return Py_BuildValue("i", parameter_num);
1392 }
1393 
1394 
1395 
1396 
1397 
1402 static PyObject *
1404 
1405 
1406  PyObject* qgd_Circuit = PyImport_ImportModule("squander.gates.qgd_Circuit");
1407 
1408  if ( qgd_Circuit == NULL ) {
1409  PyErr_SetString(PyExc_Exception, "Module import error: squander.gates.qgd_Circuit" );
1410  return NULL;
1411  }
1412 
1413  // retrieve the C++ variant of the flat circuit (flat circuit does not conatain any sub-circuits)
1414  Gates_block* circuit = self->vqe->get_flat_circuit();
1415 
1416 
1417 
1418  // construct python interfarce for the circuit
1419  PyObject* qgd_circuit_Dict = PyModule_GetDict( qgd_Circuit );
1420 
1421  // 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
1422  PyObject* py_circuit_class = PyDict_GetItemString( qgd_circuit_Dict, "qgd_Circuit");
1423 
1424  // create gate parameters
1425  PyObject* qbit_num = Py_BuildValue("i", circuit->get_qbit_num() );
1426  PyObject* circuit_input = Py_BuildValue("(O)", qbit_num);
1427 
1428  PyObject* py_circuit = PyObject_CallObject(py_circuit_class, circuit_input);
1429  qgd_Circuit_Wrapper* py_circuit_C = reinterpret_cast<qgd_Circuit_Wrapper*>( py_circuit );
1430 
1431 
1432  // replace the empty circuit with the extracted one
1433 
1434  delete( py_circuit_C->gate );
1435  py_circuit_C->gate = circuit;
1436 
1437 
1438  return py_circuit;
1439 
1440 }
1441 
1446 static PyObject *
1448 {
1449 
1450 
1451  PyArrayObject* parameters_arg = NULL;
1452 
1453 
1454  // parsing input arguments
1455  if (!PyArg_ParseTuple(args, "|O", &parameters_arg )) {
1456 
1457  std::string err( "Unsuccessful argument parsing not ");
1458  PyErr_SetString(PyExc_Exception, err.c_str());
1459  return NULL;
1460 
1461  }
1462 
1463  // establish memory contiguous arrays for C calculations
1464  if ( PyArray_IS_C_CONTIGUOUS(parameters_arg) && PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ){
1465  Py_INCREF(parameters_arg);
1466  }
1467  else if (PyArray_TYPE(parameters_arg) == NPY_FLOAT64 ) {
1468  parameters_arg = (PyArrayObject*)PyArray_FROM_OTF( (PyObject*)parameters_arg, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY);
1469  }
1470  else {
1471  std::string err( "Parameters should be should be real (given in float64 format)");
1472  PyErr_SetString(PyExc_Exception, err.c_str());
1473  return NULL;
1474  }
1475 
1476 
1477  Matrix_real parameters_mtx = numpy2matrix_real( parameters_arg );
1478  Matrix_real grad_mtx(parameters_mtx.size(), 1);
1479 
1480  try {
1481  self->vqe->optimization_problem_grad(parameters_mtx, self->vqe, grad_mtx );
1482  }
1483  catch (std::string err ) {
1484  PyErr_SetString(PyExc_Exception, err.c_str());
1485  return NULL;
1486  }
1487  catch (...) {
1488  std::string err( "Invalid pointer to decomposition class");
1489  PyErr_SetString(PyExc_Exception, err.c_str());
1490  return NULL;
1491  }
1492 
1493  // convert to numpy array
1494  grad_mtx.set_owner(false);
1495  PyObject *grad_py = matrix_real_to_numpy( grad_mtx );
1496 
1497  Py_DECREF(parameters_arg);
1498 
1499 
1500  return grad_py;
1501 }
1502 
1503 
1507 static PyObject *
1509  // initiate variables for input arguments
1510  PyObject* project_name_new=NULL;
1511 
1512  // parsing input arguments
1513  if (!PyArg_ParseTuple(args, "|O", &project_name_new)) return Py_BuildValue("i", -1);
1514 
1515 
1516  PyObject* project_name_new_string = PyObject_Str(project_name_new);
1517  PyObject* project_name_new_unicode = PyUnicode_AsEncodedString(project_name_new_string, "utf-8", "~E~");
1518  const char* project_name_new_C = PyBytes_AS_STRING(project_name_new_unicode);
1519  std::string project_name_new_str = ( project_name_new_C );
1520 
1521  // convert to python string
1522  self->vqe->set_project_name(project_name_new_str);
1523 
1524  return Py_BuildValue("i", 0);
1525 }
1526 
1527 
1533 static PyObject *
1535 
1536  // initiate variables for input arguments
1537  PyObject* gate_structure_py;
1538 
1539  // parsing input arguments
1540  if (!PyArg_ParseTuple(args, "|O", &gate_structure_py )) return Py_BuildValue("i", -1);
1541 
1542 
1543  // convert gate structure from PyObject to qgd_Circuit_Wrapper
1544  qgd_Circuit_Wrapper* qgd_op_block = (qgd_Circuit_Wrapper*) gate_structure_py;
1545 
1546  try {
1547  self->vqe->set_custom_gate_structure( qgd_op_block->gate );
1548  }
1549  catch (std::string err ) {
1550  PyErr_SetString(PyExc_Exception, err.c_str());
1551  return NULL;
1552  }
1553  catch(...) {
1554  std::string err( "Invalid pointer to decomposition class");
1555  PyErr_SetString(PyExc_Exception, err.c_str());
1556  return NULL;
1557  }
1558 
1559 
1560  return Py_BuildValue("i", 0);
1561 
1562 
1563 }
1564 
1569  {NULL} /* Sentinel */
1570 };
1571 
1576  {"Start_Optimization", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Start_Optimization, METH_NOARGS,
1577  "Method to start the decomposition."
1578  },
1579  {"get_Optimized_Parameters", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Optimized_Parameters, METH_NOARGS,
1580  "Method to get the array of optimized parameters."
1581  },
1582  {"set_Optimized_Parameters", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Optimized_Parameters, METH_VARARGS,
1583  "Method to set the array of optimized parameters."
1584  },
1585  {"set_Optimization_Tolerance", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Optimization_Tolerance, METH_VARARGS,
1586  "Method to set optimization tolerance"
1587  },
1588  {"get_Circuit", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_circuit, METH_NOARGS,
1589  "Method to get the incorporated circuit."
1590  },
1591  {"set_Project_Name", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Project_Name, METH_VARARGS,
1592  "method to set project name."
1593  },
1594  {"set_Gate_Structure_From_Binary", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Gate_Structure_From_Binary, METH_VARARGS,
1595  "Method to set the gate structure from a file created in SQUANDER."
1596  },
1597  {"apply_to", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_apply_to, METH_VARARGS,
1598  "Call to apply the gate on the input matrix."
1599  },
1600  {"set_Optimizer", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Optimizer, METH_VARARGS | METH_KEYWORDS,
1601  "Method to set optimizer."
1602  },
1603  {"set_Ansatz", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Ansatz, METH_VARARGS | METH_KEYWORDS,
1604  "Method to set ansatz type."
1605  },
1606  {"get_Parameter_Num", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Parameter_Num, METH_NOARGS,
1607  "Call to get the number of free parameters in the gate structure used for the decomposition"
1608  },
1609  {"Generate_Circuit", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Generate_Circuit, METH_VARARGS,
1610  "Method to set the circuit based on the ansatz type."
1611  },
1612  //=======================================================================================================================================
1613  {"Expectation_Value_Shot_Noise", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Expectation_Value_Shot_Noise, METH_VARARGS,
1614  "Estimate ⟨H⟩ via Monte–Carlo shot sampling; input is a dict with 'shots', 'p_readout', term lists, and optional 'seed'. Returns {'mean', 'variance', 'std_error'}."
1615  },
1616  {"Expectation_value_of_energy_real", (PyCFunction)qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Expectation_value_of_energy_real, METH_VARARGS,
1617  "Compute the real part of ⟨ψ_left|H|ψ_right⟩; inputs are two complex state vectors. "},
1618 
1619  //=======================================================================================================================================
1620  {"Optimization_Problem", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Optimization_Problem, METH_VARARGS,
1621  "Method to get the expected energy of the circuit at parameters."
1622  },
1623  {"Optimization_Problem_Batch", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Optimization_Problem_Batch, METH_VARARGS,
1624  "Wrapper function to evaluate the cost function for a batch of input parameters."
1625  },
1626  {"get_Second_Renyi_Entropy", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Second_Renyi_Entropy, METH_VARARGS,
1627  "Wrapper function to evaluate the second Rényi entropy of a quantum circuit at a specific parameter set."
1628  },
1629  {"get_Qbit_Num", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Qbit_Num, METH_NOARGS,
1630  "Call to get the number of qubits in the circuit"
1631  },
1632  {"set_Initial_State", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Initial_State, METH_VARARGS,
1633  "Call to set the initial state used in the VQE process."
1634  },
1635  {"set_Density_Matrix_Noise", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Density_Matrix_Noise, METH_VARARGS,
1636  "Configure ordered fixed local-noise insertions for the density-matrix backend."
1637  },
1638  {"get_Density_Matrix_Bridge_Metadata", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Density_Matrix_Bridge_Metadata, METH_NOARGS,
1639  "Return machine-readable metadata describing the currently supported density-matrix bridge path."
1640  },
1641  {"set_Gate_Structure", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Gate_Structure, METH_VARARGS,
1642  "Call to set custom gate structure for VQE experiments."
1643  },
1644  {"Optimization_Problem_Grad", (PyCFunction) qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Optimization_Problem_Grad, METH_VARARGS,
1645  "Method to get the expected energy of the circuit at parameters."
1646  },
1647  {NULL} /* Sentinel */
1648 };
1649 
1654  PyVarObject_HEAD_INIT(NULL, 0)
1655  "qgd_N_Qubit_Decomposition_Wrapper.qgd_N_Qubit_Decomposition_Wrapper", /*tp_name*/
1656  sizeof(qgd_Variational_Quantum_Eigensolver_Base_Wrapper), /*tp_basicsize*/
1657  0, /*tp_itemsize*/
1659  #if PY_VERSION_HEX < 0x030800b4
1660  0, /*tp_print*/
1661  #endif
1662  #if PY_VERSION_HEX >= 0x030800b4
1663  0, /*tp_vectorcall_offset*/
1664  #endif
1665  0, /*tp_getattr*/
1666  0, /*tp_setattr*/
1667  #if PY_MAJOR_VERSION < 3
1668  0, /*tp_compare*/
1669  #endif
1670  #if PY_MAJOR_VERSION >= 3
1671  0, /*tp_as_async*/
1672  #endif
1673  0, /*tp_repr*/
1674  0, /*tp_as_number*/
1675  0, /*tp_as_sequence*/
1676  0, /*tp_as_mapping*/
1677  0, /*tp_hash*/
1678  0, /*tp_call*/
1679  0, /*tp_str*/
1680  0, /*tp_getattro*/
1681  0, /*tp_setattro*/
1682  0, /*tp_as_buffer*/
1683  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1684  "Object to represent a Gates_block class of the QGD package.", /*tp_doc*/
1685  0, /*tp_traverse*/
1686  0, /*tp_clear*/
1687  0, /*tp_richcompare*/
1688  0, /*tp_weaklistoffset*/
1689  0, /*tp_iter*/
1690  0, /*tp_iternext*/
1693  0, /*tp_getset*/
1694  0, /*tp_base*/
1695  0, /*tp_dict*/
1696  0, /*tp_descr_get*/
1697  0, /*tp_descr_set*/
1698  0, /*tp_dictoffset*/
1700  0, /*tp_alloc*/
1702  0, /*tp_free*/
1703  0, /*tp_is_gc*/
1704  0, /*tp_bases*/
1705  0, /*tp_mro*/
1706  0, /*tp_cache*/
1707  0, /*tp_subclasses*/
1708  0, /*tp_weaklist*/
1709  0, /*tp_del*/
1710  0, /*tp_version_tag*/
1711  #if PY_VERSION_HEX >= 0x030400a1
1712  0, /*tp_finalize*/
1713  #endif
1714  #if PY_VERSION_HEX >= 0x030800b1
1715  0, /*tp_vectorcall*/
1716  #endif
1717  #if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
1718  0, /*tp_print*/
1719  #endif
1720 };
1721 
1726  PyModuleDef_HEAD_INIT,
1727  "qgd_N_Qubit_Decomposition_Wrapper",
1728  "Python binding for QGD N_Qubit_Decomposition class",
1729  -1,
1730 };
1731 
1732 
1736 PyMODINIT_FUNC
1738 {
1739  // initialize Numpy API
1740  import_array();
1741 
1742  PyObject *m;
1743  if (PyType_Ready(&qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Type) < 0)
1744  return NULL;
1745 
1746  m = PyModule_Create(&qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Module);
1747  if (m == NULL)
1748  return NULL;
1749 
1750  Py_INCREF(&qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Type);
1751  if (PyModule_AddObject(m, "qgd_Variational_Quantum_Eigensolver_Base_Wrapper", (PyObject *) &qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Type) < 0) {
1752  Py_DECREF(&qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Type);
1753  Py_DECREF(m);
1754  return NULL;
1755  }
1756 
1757  return m;
1758 }
1759 
1760 
1761 } //extern C
1762 
1763 
void release_Variational_Quantum_Eigensolver_Base(Variational_Quantum_Eigensolver_Base *instance)
Call to deallocate an instance of N_Qubit_Decomposition class.
int shots
Definition: noise.py:65
Gates_block * get_flat_circuit()
Method to generate a flat circuit.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Optimization_Problem(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Ansatz(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args, PyObject *kwds)
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Optimization_Tolerance(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
parameter_num
[set adaptive gate structure]
ansatz_type
Type definition of the fifferent types of ansatz.
PyMODINIT_FUNC PyInit_qgd_Variational_Quantum_Eigensolver_Base_Wrapper(void)
Method called when the Python module is initialized.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Optimized_Parameters(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Set parameters for the solver.
Type definition of the qgd_N_Qubit_Decomposition_Wrapper Python class of the qgd_N_Qubit_Decompositio...
Class to solve VQE problems.
Variational_Quantum_Eigensolver_Base * create_qgd_Variational_Quantum_Eigensolver_Base(Matrix_sparse Hamiltonian, int qbit_num, 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...
key
Definition: noise.py:86
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_apply_to(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
return Py_BuildValue("i", 0)
Matrix_real numpy2matrix_real(PyArrayObject *arr)
Call to create a PIC matrix_real representation of a numpy array.
scalar * data
pointer to the stored data
Definition: matrix_base.hpp:48
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Expectation_Value_Shot_Noise(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Wrapper to compute an estimated expectation value using Monte–Carlo shot sampling with optional rea...
static void qgd_Variational_Quantum_Eigensolver_Base_Wrapper_dealloc(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self)
Method called when a python instance of the class qgd_N_Qubit_Decomposition_Wrapper is destroyed...
PyObject * matrix_real_to_numpy(Matrix_real &mtx)
Call to make a numpy array from an instance of matrix class.
U3 RX RZ H Y SX S T CNOT CH SYC CRZ PyObject PyObject * kwds
Class to store data of complex arrays and its properties.
Definition: matrix_sparse.h:38
A class describing a universal configuration element.
scalar * get_data() const
Call to get the pointer to the stored data.
PyObject_HEAD PyObject * Hamiltonian
pointer to the unitary to be decomposed to keep it alive
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Expectation_value_of_energy_real(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Wrapper function to evaluate the real part of the energy expectation value ⟨ψ_left | H | ψ_rightâ...
optimization_aglorithms
implemented optimization strategies
static PyModuleDef qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Module
Structure containing metadata about the module.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Optimized_Parameters(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self)
Extract the optimized parameters.
U3 RX RZ H Y SX S T CNOT CH SYC CRZ PyObject * args
PyObject_HEAD Gates_block * gate
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_circuit(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self)
Wrapper function to retrieve the circuit (Squander format) incorporated in the instance.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Qbit_Num(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self)
Call to retrieve the number of qubits in the circuit.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Start_Optimization(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self)
Container for statistics returned by shot-noise simulations.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Second_Renyi_Entropy(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Wrapper function to evaluate the second Rényi entropy of a quantum circuit at a specific parameter s...
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_Variational_Quantum_Eigensolver_Base_Wrapper_set_Gate_Structure_From_Binary(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Wrapper function to set custom layers to the gate structure that are intended to be used in the decom...
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Gate_Structure(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Wrapper function to set custom gate structure for the decomposition.
A base class to solve VQE problems This class can be used to approximate the ground state of the inpu...
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Optimization_Problem_Grad(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Wrapper function to evaluate the cost function an dthe gradient components.
int Power_of_2(int n)
Calculates the n-th power of 2.
Definition: common.cpp:136
Double-precision complex matrix (float64).
Definition: matrix.h:38
dictionary config
int size() const
Call to get the number of the allocated elements.
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:44
Variational_Quantum_Eigensolver_Base * vqe
An object to decompose the unitary.
void set_property(std::string name_, double val_)
Call to set a double value.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
Method called when a python instance of the class qgd_N_Qubit_Decomposition_Wrapper is allocated...
static PyTypeObject qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Type
A structure describing the type of the class qgd_N_Qubit_Decomposition_Wrapper.
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 PyMemberDef qgd_Variational_Quantum_Eigensolver_Base_Wrapper_members[]
Structure containing metadata about the members of class qgd_N_Qubit_Decomposition_Wrapper.
Ordered fixed-noise insertion metadata.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Optimizer(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args, PyObject *kwds)
int get_qbit_num()
Call to get the number of qubits composing the unitary.
Definition: Gate.cpp:1342
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Initial_State(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Set the initial state used in the VQE process.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Generate_Circuit(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Type definition for qgd_Circuit_Wrapper.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Density_Matrix_Noise(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Configure ordered fixed density-noise insertions for the density backend.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Density_Matrix_Bridge_Metadata(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self)
Return a machine-readable description of the supported density bridge.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_Optimization_Problem_Batch(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Wrapper function to evaluate the cost function for many input paramaters at once. ...
static PyMethodDef qgd_Variational_Quantum_Eigensolver_Base_Wrapper_methods[]
Structure containing metadata about the methods of class qgd_N_Qubit_Decomposition_Wrapper.
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:41
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_set_Project_Name(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args)
Call to set a project name.
static PyObject * qgd_Variational_Quantum_Eigensolver_Base_Wrapper_get_Parameter_Num(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self)
Get the number of free parameters in the gate structure used for the decomposition.
static int qgd_Variational_Quantum_Eigensolver_Base_Wrapper_init(qgd_Variational_Quantum_Eigensolver_Base_Wrapper *self, PyObject *args, PyObject *kwds)
Method called when a python instance of the class qgd_N_Qubit_Decomposition_Wrapper is initialized...