Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
Variational_Quantum_Eigensolver_Base.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 
24 #include "../density_matrix/include/density_matrix.h"
25 #include "../density_matrix/include/noisy_circuit.h"
26 
27 #include "../../gates/include/H.h"
28 #include "../../gates/include/SDG.h"
29 #include <cmath>
30 #include <nlohmann/json.hpp>
31 #include <random>
32 #include <string>
33 #include <tbb/tbb.h>
34 #include <tuple>
35 #include <vector>
37 
38 static tbb::spin_mutex my_mutex;
39 
40 namespace {
41 
42 const char *VQE_BACKEND_MODE_CONFIG_KEY = "backend_mode";
43 
44 const char *density_noise_type_to_label(density_noise_type noise_type) {
45  switch (noise_type) {
47  return "local_depolarizing";
49  return "amplitude_damping";
51  return "phase_damping";
52  default:
53  return "unknown_density_noise";
54  }
55 }
56 
57 const char *
58 vqe_circuit_source_to_label(vqe_circuit_source_type circuit_source) {
59  switch (circuit_source) {
61  return "unset";
63  return "generated_hea";
65  return "generated_hea_zyz";
67  return "binary_import";
69  return "custom_gate_structure";
70  default:
71  return "unknown_circuit_source";
72  }
73 }
74 
76 parse_vqe_backend_mode(std::map<std::string, Config_Element> &config) {
77 
78  long long backend_mode_value = STATE_VECTOR_BACKEND;
79  if (config.count(VQE_BACKEND_MODE_CONFIG_KEY) > 0) {
80  config[VQE_BACKEND_MODE_CONFIG_KEY].get_property(backend_mode_value);
81  }
82 
83  switch (backend_mode_value) {
85  return STATE_VECTOR_BACKEND;
88  default:
89  throw std::string(
90  "Variational_Quantum_Eigensolver_Base::Variational_Quantum_Eigensolver_"
91  "Base: unsupported backend_mode value");
92  }
93 }
94 
95 } // namespace
96 
103 
104  // logical value describing whether the decomposition was finalized or not
105  decomposition_finalized = false;
106 
107  // error of the unitarity of the final decomposition
108  decomposition_error = DBL_MAX;
109 
110  // The current minimum of the optimization problem
111  current_minimum = DBL_MAX;
112 
113  global_target_minimum = -DBL_MAX;
114 
115  // logical value describing whether the optimization problem was solved or not
117 
118  // The maximal allowed error of the optimization problem
119  optimization_tolerance = -DBL_MAX;
120 
121  // The convergence threshold in the optimization process
122  convergence_threshold = -DBL_MAX;
123 
124  alg = AGENTS;
125 
127 
128  adaptive_eta = false;
129 
130  cost_fnc = VQE;
131 
132  ansatz = HEA;
135  density_noise_specs.clear();
136 }
137 
139  const std::vector<DensityNoiseSpec> &density_noise_specs_in) {
140 
141  for (const auto &spec : density_noise_specs_in) {
142  if (spec.target_qbit < 0 || spec.target_qbit >= qbit_num) {
143  throw std::string("Variational_Quantum_Eigensolver_Base::set_density_"
144  "noise_specs: target_qbit out of range");
145  }
146 
147  if (spec.after_gate_index < 0) {
148  throw std::string("Variational_Quantum_Eigensolver_Base::set_density_"
149  "noise_specs: after_gate_index must be non-negative");
150  }
151 
152  if (spec.value < 0.0 || spec.value > 1.0) {
153  throw std::string("Variational_Quantum_Eigensolver_Base::set_density_"
154  "noise_specs: noise values must be in [0, 1]");
155  }
156  }
157 
158  density_noise_specs = density_noise_specs_in;
159 }
160 
162 
163  return alg == BAYES_OPT || alg == COSINE;
164 }
165 
166 std::string
168 
169  return vqe_circuit_source_to_label(circuit_source);
170 }
171 
172 std::vector<DensityBridgeOperationInfo>
174 
176 
177  std::vector<DensityBridgeOperationInfo> info;
178  info.reserve(get_gate_num() + density_noise_specs.size());
179 
180  int param_start = 0;
181  int gate_num = get_gate_num();
182  for (int gate_idx = 0; gate_idx < gate_num; ++gate_idx) {
183  Gate *gate = get_gate(gate_idx);
184 
185  DensityBridgeOperationInfo gate_info;
186  gate_info.name = gate->get_name();
187  gate_info.is_unitary = true;
188  gate_info.source_gate_index = gate_idx;
189  gate_info.target_qbit = gate->get_target_qbit();
190  gate_info.control_qbit = gate->get_control_qbit();
191  gate_info.param_count = gate->get_parameter_num();
192  gate_info.param_start = param_start;
193  gate_info.has_fixed_value = false;
194  gate_info.fixed_value = 0.0;
195  info.push_back(gate_info);
196 
197  param_start += gate_info.param_count;
198 
199  for (const auto &spec : density_noise_specs) {
200  if (spec.after_gate_index != gate_idx) {
201  continue;
202  }
203 
204  DensityBridgeOperationInfo noise_info;
205  noise_info.name = density_noise_type_to_label(spec.type);
206  noise_info.is_unitary = false;
207  noise_info.source_gate_index = gate_idx;
208  noise_info.target_qbit = spec.target_qbit;
209  noise_info.control_qbit = -1;
210  noise_info.param_count = 0;
211  noise_info.param_start = param_start;
212  noise_info.has_fixed_value = true;
213  noise_info.fixed_value = spec.value;
214  info.push_back(noise_info);
215  }
216  }
217 
218  return info;
219 }
220 
222  bool require_optimizer_support, bool require_gradient_support) {
223 
225  if (!density_noise_specs.empty()) {
226  throw std::string("Variational_Quantum_Eigensolver_Base::validate_"
227  "density_anchor_support: state_vector backend does not "
228  "support density_matrix-only noise configuration");
229  }
230  return;
231  }
232 
233  int expected_dimension = Power_of_2(qbit_num);
234  if (Hamiltonian.rows != expected_dimension ||
235  Hamiltonian.cols != expected_dimension) {
236  throw std::string("Variational_Quantum_Eigensolver_Base::validate_density_"
237  "anchor_support: density_matrix backend requires a "
238  "square Hamiltonian matching the configured qubit count");
239  }
240 
241  if (require_gradient_support) {
242  throw std::string(
243  "Variational_Quantum_Eigensolver_Base::validate_density_anchor_support:"
244  " density_matrix backend does not support gradient-based optimization");
245  }
246 
247  if (ansatz != HEA) {
248  throw std::string(
249  "Variational_Quantum_Eigensolver_Base::validate_density_anchor_support:"
250  " density_matrix backend currently supports only the HEA ansatz");
251  }
252 
253  if (require_optimizer_support && !density_optimizer_supported()) {
254  throw std::string("Variational_Quantum_Eigensolver_Base::validate_density_"
255  "anchor_support: density_matrix backend currently "
256  "supports only BAYES_OPT or COSINE optimization traces");
257  }
258 
260  throw std::string(
261  "Variational_Quantum_Eigensolver_Base::validate_density_anchor_support:"
262  " density_matrix backend requires a generated HEA circuit");
263  }
264 
266  std::string error(
267  "Variational_Quantum_Eigensolver_Base::validate_density_anchor_support:"
268  " unsupported circuit source in density backend path: ");
269  error += vqe_circuit_source_to_label(circuit_source);
270  throw error;
271  }
272 
273  int gate_num = get_gate_num();
274  if (gate_num <= 0) {
275  throw std::string(
276  "Variational_Quantum_Eigensolver_Base::validate_density_anchor_support:"
277  " density_matrix backend requires a generated HEA circuit");
278  }
279 
280  for (int gate_idx = 0; gate_idx < gate_num; ++gate_idx) {
281  Gate *gate = get_gate(gate_idx);
282  switch (gate->get_type()) {
283  case U3_OPERATION:
284  case CNOT_OPERATION:
285  break;
286  default: {
287  std::string error(
288  "Variational_Quantum_Eigensolver_Base::validate_density_anchor_"
289  "support: first unsupported gate in density backend path is ");
290  error += gate->get_name();
291  throw error;
292  }
293  }
294  }
295 
296  for (size_t spec_idx = 0; spec_idx < density_noise_specs.size(); ++spec_idx) {
297  const auto &spec = density_noise_specs[spec_idx];
298  if (spec.after_gate_index >= gate_num) {
299  std::string error(
300  "Variational_Quantum_Eigensolver_Base::validate_density_anchor_"
301  "support: unsupported density-noise insertion at index ");
302  error += std::to_string(spec_idx);
303  error += " because after_gate_index exceeds generated gate count";
304  throw error;
305  }
306  }
307 }
308 
310  squander::density::NoisyCircuit &circuit, int gate_index) const {
311 
312  for (const auto &spec : density_noise_specs) {
313  if (spec.after_gate_index != gate_index) {
314  continue;
315  }
316 
317  switch (spec.type) {
319  circuit.add_local_depolarizing(spec.target_qbit, spec.value);
320  break;
322  circuit.add_amplitude_damping(spec.target_qbit, spec.value);
323  break;
324  case PHASE_DAMPING_NOISE:
325  circuit.add_phase_damping(spec.target_qbit, spec.value);
326  break;
327  default: {
328  std::string error(
329  "Variational_Quantum_Eigensolver_Base::append_density_noise_for_gate_"
330  "index: unsupported density-noise type ");
331  error += density_noise_type_to_label(spec.type);
332  throw error;
333  }
334  }
335  }
336 }
337 
341 
343 
344  int gate_num = get_gate_num();
345  for (int gate_idx = 0; gate_idx < gate_num; ++gate_idx) {
346  Gate *gate = get_gate(gate_idx);
347 
348  switch (gate->get_type()) {
349  case U3_OPERATION:
350  circuit.add_U3(gate->get_target_qbit());
351  break;
352  case CNOT_OPERATION:
353  circuit.add_CNOT(gate->get_target_qbit(), gate->get_control_qbit());
354  break;
355  default: {
356  std::string error(
357  "Variational_Quantum_Eigensolver_Base::lower_anchor_circuit_to_noisy_"
358  "circuit: unsupported gate in density backend path: ");
359  error += gate->get_name();
360  throw error;
361  }
362  }
363 
364  append_density_noise_for_gate_index(circuit, gate_idx);
365  }
366 }
367 
368 double
370  const squander::density::DensityMatrix &rho) const {
371 
372  if (Hamiltonian.rows != rho.get_dim() || Hamiltonian.cols != rho.get_dim()) {
373  throw std::string("Variational_Quantum_Eigensolver_Base::expectation_value_"
374  "of_density_energy_real: dimension mismatch");
375  }
376 
377  double energy_real = 0.0;
378  for (int row = 0; row < Hamiltonian.rows; ++row) {
379  for (int idx = Hamiltonian.indptr[row]; idx < Hamiltonian.indptr[row + 1];
380  ++idx) {
381  int col = Hamiltonian.indices[idx];
382  const QGD_Complex16 &h_val = Hamiltonian.data[idx];
383  const QGD_Complex16 &rho_val = rho(col, row);
384 
385  energy_real += h_val.real * rho_val.real - h_val.imag * rho_val.imag;
386  }
387  }
388 
389  if (!std::isfinite(energy_real)) {
390  throw std::string("Variational_Quantum_Eigensolver_Base::expectation_value_"
391  "of_density_energy_real: produced a non-finite energy");
392  }
393 
394  return energy_real;
395 }
396 
399 
401  (initial_state.size() == 0)
404 
407  circuit.apply_to(parameters, rho);
408 
410 }
411 
421  Matrix_sparse Hamiltonian_in, int qbit_num_in,
422  std::map<std::string, Config_Element> &config_in, int accelerator_num)
423  : Optimization_Interface(Matrix(Power_of_2(qbit_num_in), 1), qbit_num_in,
424  false, config_in, RANDOM, accelerator_num) {
425 
426  Hamiltonian = Hamiltonian_in;
427  // config maps
428  config = config_in;
429  backend_mode = parse_vqe_backend_mode(config);
430  // logical value describing whether the decomposition was finalized or not
431  decomposition_finalized = false;
432 
433  // error of the unitarity of the final decomposition
434  decomposition_error = DBL_MAX;
435 
436  // The current minimum of the optimization problem
437  current_minimum = DBL_MAX;
438 
439  // logical value describing whether the optimization problem was solved or not
441 
442  // override optimization parameters governing the convergence used in gate
443  // decomposition applications
444  global_target_minimum = -DBL_MAX;
445  optimization_tolerance = -DBL_MAX;
446  convergence_threshold = -DBL_MAX;
447 
449 
450  adaptive_eta = false;
451 
452  qbit_num = qbit_num_in;
453 
454  std::random_device rd;
455 
456  gen = std::mt19937(rd());
457 
458  alg = BAYES_OPT;
459 
460  cost_fnc = VQE;
461 
462  ansatz = HEA;
464  density_noise_specs.clear();
465 }
466 
471 
477 
478  // initialize the initial state if it was not given
479  if (initial_state.size() == 0) {
481  }
482 
483  if (gates.size() == 0) {
484  std::string error("Variational_Quantum_Eigensolver_Base::Get_ground_state: "
485  "for VQE process the circuit needs to be initialized");
486  throw error;
487  }
488 
490  if (num_of_parameters == 0) {
491  std::string error("Variational_Quantum_Eigensolver_Base::Get_ground_state: "
492  "No intial parameters were given");
493  throw error;
494  }
495 
496  if (num_of_parameters != get_parameter_num()) {
497  std::string error("Variational_Quantum_Eigensolver_Base::Get_ground_state: "
498  "The number of initial parameters does not match with "
499  "the number of parameters in the circuit");
500  throw error;
501  }
502 
503  validate_density_anchor_support(true, false);
504 
505  // start the VQE process
506  Matrix_real solution_guess = optimized_parameters_mtx.copy();
507  solve_layer_optimization_problem(num_of_parameters, solution_guess);
508 
509  return;
510 }
511 
523  Matrix &State_left, Matrix &State_right) {
524 
525  if (State_left.rows != State_right.rows ||
526  Hamiltonian.rows != State_right.rows) {
527  std::string error("Variational_Quantum_Eigensolver_Base::Expectation_value_"
528  "of_energy: States on the right and left should be of "
529  "the same dimension as the Hamiltonian");
530  throw error;
531  }
532 
533  Matrix tmp = mult(Hamiltonian, State_right);
534  QGD_Complex16 Energy;
535  Energy.real = 0.0;
536  Energy.imag = 0.0;
537 
538  tbb::combinable<double> priv_partial_energy_real{[]() { return 0.0; }};
539  tbb::combinable<double> priv_partial_energy_imag{[]() { return 0.0; }};
540 
541  tbb::parallel_for(
542  tbb::blocked_range<int>(0, State_left.rows, 1024),
543  [&](tbb::blocked_range<int> r) {
544  double &energy_local_real = priv_partial_energy_real.local();
545  double &energy_local_imag = priv_partial_energy_imag.local();
546 
547  for (int idx = r.begin(); idx < r.end(); idx++) {
548  energy_local_real += State_left[idx].real * tmp[idx].real +
549  State_left[idx].imag * tmp[idx].imag;
550  energy_local_imag += State_left[idx].real * tmp[idx].imag -
551  State_left[idx].imag * tmp[idx].real;
552  }
553  });
554 
555  // calculate the final cost function
556  priv_partial_energy_real.combine_each(
557  [&Energy](double a) { Energy.real = Energy.real + a; });
558 
559  priv_partial_energy_imag.combine_each(
560  [&Energy](double a) { Energy.imag = Energy.imag + a; });
561 
562  tmp.release_data(); // TODO: this is not necessary, beacause it is called with
563  // the destructor of the class Matrix
564 
565  {
566  tbb::spin_mutex::scoped_lock my_lock{my_mutex};
567 
568  number_of_iters++;
569  }
570 
571  return Energy;
572 }
573 
585  Matrix &State_left, Matrix &State_right) {
586 
587  if (State_left.rows != State_right.rows ||
588  Hamiltonian.rows != State_right.rows) {
589  std::string error("Variational_Quantum_Eigensolver_Base::Expectation_value_"
590  "of_energy_real: States on the right and left should be "
591  "of the same dimension as the Hamiltonian");
592  throw error;
593  }
594 
595  Matrix tmp = mult(Hamiltonian, State_right);
596  double Energy = 0.0;
597 
598  tbb::combinable<double> priv_partial_energy{[]() { return 0.0; }};
599 
600  tbb::parallel_for(tbb::blocked_range<int>(0, State_left.rows, 1024),
601  [&](tbb::blocked_range<int> r) {
602  double &energy_local = priv_partial_energy.local();
603 
604  for (int idx = r.begin(); idx < r.end(); idx++) {
605  energy_local += State_left[idx].real * tmp[idx].real +
606  State_left[idx].imag * tmp[idx].imag;
607  }
608  });
609 
610  // calculate the final cost function
611  priv_partial_energy.combine_each(
612  [&Energy](double a) { Energy = Energy + a; });
613 
614  tmp.release_data(); // TODO: this is not necessary, beacause it is called with
615  // the destructor of the class Matrix
616 
617  {
618  tbb::spin_mutex::scoped_lock my_lock{my_mutex};
619 
620  number_of_iters++;
621  }
622 
623  return Energy;
624 }
625 
626 //----------------------------------------------------------------------------------------------------------------------------------------
627 //----------------------------------------------------------------------------------------------------------------------------------------
628 //----------------------------------------------------------------------------------------------------------------------------------------
629 //----------------------------------------------------------------------------------------------------------------------------------------
697  Matrix &State, int shots, uint64_t seed, double p_readout) {
698 
699  if (State.rows != Hamiltonian.rows) {
700  throw std::string(
701  "Expectation_value_with_shot_noise_real: dimension mismatch");
702  }
703 
704  const int N = State.rows;
705  const int n_qubits = static_cast<int>(std::round(std::log2(N)));
706  if ((1 << n_qubits) != N) {
707  throw std::string(
708  "Expectation_value_with_shot_noise_real: State.rows not power of two");
709  }
710 
711  // -------------------------------------------------------
712  // 1) Z-basis probabilities from |ψ⟩ (for Z and ZZ terms)
713  // -------------------------------------------------------
714  std::vector<double> probsZ(N);
715  double sum_pZ = 0.0;
716  for (int idx = 0; idx < N; ++idx) {
717  double re = State[idx].real;
718  double im = State[idx].imag;
719  double p = re * re + im * im;
720  probsZ[idx] = p;
721  sum_pZ += p;
722  }
723  if (std::abs(sum_pZ - 1.0) > 1e-6) {
724  // Slight numerical mismatch is OK; normalize anyway
725  for (auto &v : probsZ)
726  v /= sum_pZ;
727  } else {
728  for (auto &v : probsZ)
729  v /= sum_pZ;
730  }
731 
732  std::mt19937_64 rng(seed ? seed : std::random_device{}());
733  std::discrete_distribution<int> distZ(probsZ.begin(), probsZ.end());
734  std::uniform_real_distribution<double> unif01(0.0, 1.0);
735 
736  // -------------------------------------------------------
737  // 2) Global X-basis distribution for ALL XX terms
738  // We apply H on every qubit that appears in any XX term
739  // -------------------------------------------------------
740  bool has_xx = !xx_terms.empty();
741  std::discrete_distribution<int>
742  distX; // will be initialized if has_xx == true
743 
744  if (has_xx) {
745 
746  // Which qubits need H for XX measurement?
747  std::vector<bool> need_H(n_qubits, false);
748  for (const auto &t : xx_terms) {
749  int i, j;
750  double coeff;
751  std::tie(i, j, coeff) = t;
752  if (i < 0 || i >= n_qubits || j < 0 || j >= n_qubits) {
753  throw std::string("XX term index out of range in "
754  "Expectation_value_with_shot_noise_real");
755  }
756  need_H[i] = true;
757  need_H[j] = true;
758  }
759 
760  // Rotated state |ψ_X⟩ = (⊗_q∈S H_q) |ψ⟩,
761  // where S is the set of qubits that appear in any XX term.
762  Matrix psi_rot = State; // copy original state |ψ⟩
763  for (int q = 0; q < n_qubits; ++q) {
764  if (need_H[q]) {
765  H h_gate(n_qubits, q);
766  h_gate.apply_to(psi_rot, 0);
767  }
768  }
769 
770  // Build X-basis probabilities
771  std::vector<double> probsX(N);
772  double sum_pX = 0.0;
773  for (int idx = 0; idx < N; ++idx) {
774  double re = psi_rot[idx].real;
775  double im = psi_rot[idx].imag;
776  double p = re * re + im * im;
777  probsX[idx] = p;
778  sum_pX += p;
779  }
780  if (sum_pX <= 0.0) {
781  throw std::string("X-rotated state has zero norm in "
782  "Expectation_value_with_shot_noise_real");
783  }
784  for (auto &v : probsX)
785  v /= sum_pX;
786 
787  // One global X-basis distribution for all XX terms
788  distX = std::discrete_distribution<int>(probsX.begin(), probsX.end());
789  }
790 
791  // -------------------------------------------------------
792  // 2b) Global Y-basis distribution for ALL YY terms
793  // We apply S† (SDG) then H on every qubit that appears in any YY term
794  // This rotates Y eigenstates to Z eigenstates for measurement
795  // -------------------------------------------------------
796  bool has_yy = !yy_terms.empty();
797  std::discrete_distribution<int>
798  distY; // will be initialized if has_yy == true
799 
800  if (has_yy) {
801 
802  // Which qubits need S†H for YY measurement?
803  std::vector<bool> need_SDG_H(n_qubits, false);
804  for (const auto &t : yy_terms) {
805  int i, j;
806  double coeff;
807  std::tie(i, j, coeff) = t;
808  if (i < 0 || i >= n_qubits || j < 0 || j >= n_qubits) {
809  throw std::string("YY term index out of range in "
810  "Expectation_value_with_shot_noise_real");
811  }
812  need_SDG_H[i] = true;
813  need_SDG_H[j] = true;
814  }
815 
816  // Rotated state |ψ_Y⟩ = (⊗_q∈S H_q S†_q) |ψ⟩,
817  // where S is the set of qubits that appear in any YY term.
818  // S† rotates Y to X, then H rotates X to Z
819  Matrix psi_rot_y = State; // copy original state |ψ⟩
820  for (int q = 0; q < n_qubits; ++q) {
821  if (need_SDG_H[q]) {
822  // Apply S† (SDG) first to rotate Y to X
823  SDG sdg_gate(n_qubits, q);
824  sdg_gate.apply_to(psi_rot_y, 0);
825  // Then apply H to rotate X to Z
826  H h_gate(n_qubits, q);
827  h_gate.apply_to(psi_rot_y, 0);
828  }
829  }
830 
831  // Build Y-basis probabilities (after S†H rotation, Z-eigenvalues encode
832  // Y-eigenvalues)
833  std::vector<double> probsY(N);
834  double sum_pY = 0.0;
835  for (int idx = 0; idx < N; ++idx) {
836  double re = psi_rot_y[idx].real;
837  double im = psi_rot_y[idx].imag;
838  double p = re * re + im * im;
839  probsY[idx] = p;
840  sum_pY += p;
841  }
842  if (sum_pY <= 0.0) {
843  throw std::string("Y-rotated state has zero norm in "
844  "Expectation_value_with_shot_noise_real");
845  }
846  for (auto &v : probsY)
847  v /= sum_pY;
848 
849  // One global Y-basis distribution for all YY terms
850  distY = std::discrete_distribution<int>(probsY.begin(), probsY.end());
851  }
852 
853  // -------------------------------------------------------
854  // 3) Monte–Carlo sampling
855  // For each "shot":
856  // - sample one Z-basis outcome for ZZ and Z terms
857  // - sample one X-basis outcome for ALL XX terms
858  // - sample one Y-basis outcome for ALL YY terms
859  // -------------------------------------------------------
860  double sum = 0.0;
861  double sum_sq = 0.0;
862 
863  for (int s = 0; s < shots; ++s) {
864 
865  double E = 0.0;
866 
867  // --------- Z-basis measurement: ZZ + Z ----------
868  int idx = distZ(rng);
869 
870  if (p_readout > 0.0) {
871  int observed = idx;
872  for (int q = 0; q < n_qubits; ++q) {
873  if (unif01(rng) < p_readout) {
874  observed ^= (1 << q);
875  }
876  }
877  idx = observed;
878  }
879 
880  // ZZ terms: coeff * Z_i Z_j
881  for (const auto &t : zz_terms) {
882  int i, j;
883  double coeff;
884  std::tie(i, j, coeff) = t;
885 
886  const int zi = ((idx >> i) & 1) ? -1 : +1;
887  const int zj = ((idx >> j) & 1) ? -1 : +1;
888  E += coeff * (zi * zj);
889  }
890 
891  // Z terms: h_i * Z_i
892  for (const auto &z : z_terms) {
893  const int i = z.first;
894  const double h = z.second;
895  const int zi = ((idx >> i) & 1) ? -1 : +1;
896  E += h * zi;
897  }
898 
899  // --------- X-basis measurement: ALL XX terms -----
900  if (has_xx) {
901  int idx_x = distX(rng);
902 
903  if (p_readout > 0.0) {
904  int observed = idx_x;
905  for (int q = 0; q < n_qubits; ++q) {
906  if (unif01(rng) < p_readout) {
907  observed ^= (1 << q);
908  }
909  }
910  idx_x = observed;
911  }
912 
913  // After H-rotation, Z-eigenvalues encode X-eigenvalues
914  for (const auto &t : xx_terms) {
915  int i, j;
916  double coeff;
917  std::tie(i, j, coeff) = t;
918 
919  const int xi = ((idx_x >> i) & 1) ? -1 : +1;
920  const int xj = ((idx_x >> j) & 1) ? -1 : +1;
921  E += coeff * (xi * xj);
922  }
923  }
924 
925  // --------- Y-basis measurement: ALL YY terms -----
926  if (has_yy) {
927  int idx_y = distY(rng);
928 
929  if (p_readout > 0.0) {
930  int observed = idx_y;
931  for (int q = 0; q < n_qubits; ++q) {
932  if (unif01(rng) < p_readout) {
933  observed ^= (1 << q);
934  }
935  }
936  idx_y = observed;
937  }
938 
939  // After S†H-rotation, Z-eigenvalues encode Y-eigenvalues
940  for (const auto &t : yy_terms) {
941  int i, j;
942  double coeff;
943  std::tie(i, j, coeff) = t;
944 
945  const int yi = ((idx_y >> i) & 1) ? -1 : +1;
946  const int yj = ((idx_y >> j) & 1) ? -1 : +1;
947  E += coeff * (yi * yj);
948  }
949  }
950 
951  sum += E;
952  sum_sq += E * E;
953  }
954 
955  const double mean = sum / static_cast<double>(shots);
956  const double mean_sq = sum_sq / static_cast<double>(shots);
957  double variance = mean_sq - mean * mean;
958  if (variance < 0.0 && variance > -1e-12) {
959  variance = 0.0;
960  }
961  const double std_error =
962  (variance > 0.0) ? std::sqrt(variance / static_cast<double>(shots)) : 0.0;
963 
964  {
965  tbb::spin_mutex::scoped_lock my_lock{my_mutex};
966  number_of_iters++;
967  }
968 
969  return {mean, variance, std_error};
970 }
971 
972 //----------------------------------------------------------------------------------------------------------------------------------------
973 //----------------------------------------------------------------------------------------------------------------------------------------
974 //----------------------------------------------------------------------------------------------------------------------------------------
975 //----------------------------------------------------------------------------------------------------------------------------------------
976 
985  Matrix_real parameters, void *void_instance) {
986 
987  double Energy = 0.0;
988 
990  reinterpret_cast<Variational_Quantum_Eigensolver_Base *>(void_instance);
991  instance->validate_density_anchor_support(false, false);
992 
993  if (instance->backend_mode == DENSITY_MATRIX_BACKEND) {
994  return instance->evaluate_density_matrix_backend(parameters);
995  }
996 
997  Matrix State = instance->initial_state.copy();
998 
999  instance->apply_to(parameters, State);
1000  Energy = instance->Expectation_value_of_energy_real(State, State);
1001 
1002  return Energy;
1003 }
1004 
1005 #ifdef __GROQ__
1006 
1014 double Variational_Quantum_Eigensolver_Base::optimization_problem_Groq(
1015  Matrix_real &parameters, int chosen_device) {
1016 
1017  Matrix State;
1018 
1019  // tbb::tick_count t0_DFE = tbb::tick_count::now();
1020  std::vector<int> target_qbits;
1021  std::vector<int> control_qbits;
1022  std::vector<Matrix> u3_qbit;
1023  extract_gate_kernels_target_and_control_qubits(u3_qbit, target_qbits,
1024  control_qbits, parameters);
1025 
1026  // initialize the initial state on the chip if it was not given
1027  if (initial_state.size() == 0) {
1028 
1029  Matrix State_zero(0, 0);
1030  apply_to_groq_sv(accelerator_num, chosen_device, qbit_num, u3_qbit,
1031  target_qbits, control_qbits, State_zero, id);
1032 
1033  State = State_zero;
1034 
1035  } else {
1036 
1037  State = initial_state.copy();
1038  // apply state transformation via the Groq chip
1039  apply_to_groq_sv(accelerator_num, chosen_device, qbit_num, u3_qbit,
1040  target_qbits, control_qbits, State, id);
1041  }
1042 
1043  /*
1045  Matrix State_copy = State.copy();
1046 
1047 
1048  Decomposition_Base::apply_to(parameters, State_copy );
1049 
1050 
1051  double diff = 0.0;
1052  for( int64_t idx=0; idx<State_copy.size(); idx++ ) {
1053 
1054  QGD_Complex16 element = State[idx];
1055  QGD_Complex16 element_copy = State_copy[idx];
1056  QGD_Complex16 element_diff;
1057  element_diff.real = element.real - element_copy.real;
1058  element_diff.imag = element.imag - element_copy.imag;
1059 
1060  double diff_increment = element_diff.real*element_diff.real +
1061  element_diff.imag*element_diff.imag; diff = diff + diff_increment;
1062  }
1063 
1064  std::cout << "Variational_Quantum_Eigensolver_Base::apply_to checking
1065  diff: " << diff << std::endl;
1066 
1067 
1068  if ( diff > 1e-4 ) {
1069  std::string error("Groq and CPU results do not match");
1070  throw(error);
1071  }
1072 
1074  */
1075 
1076  double Energy = Expectation_value_of_energy_real(State, State);
1077 
1078  return Energy;
1079 }
1080 
1081 #endif
1082 
1089  Matrix_real &parameters) {
1090 
1091  validate_density_anchor_support(false, false);
1092 
1094  return evaluate_density_matrix_backend(parameters);
1095  }
1096 
1097  Matrix State;
1098 
1099 #ifdef __GROQ__
1100  if (accelerator_num > 0) {
1101 
1102  return optimization_problem_Groq(parameters, 0);
1103 
1104  } else {
1105 #endif
1106  // initialize the initial state if it was not given
1107  if (initial_state.size() == 0) {
1109  }
1110 
1111  State = initial_state.copy();
1112  apply_to(parameters, State);
1113 
1114 #ifdef __GROQ__
1115  }
1116 #endif
1117 
1118  double Energy = Expectation_value_of_energy_real(State, State);
1119 
1120  return Energy;
1121 }
1122 
1133  void *void_instance, double *f0,
1134  Matrix_real &grad) {
1135 
1137  reinterpret_cast<Variational_Quantum_Eigensolver_Base *>(void_instance);
1138  instance->validate_density_anchor_support(false, true);
1139 
1140  // initialize the initial state if it was not given
1141  if (instance->initial_state.size() == 0) {
1142  instance->initialize_zero_state();
1143  }
1144 
1145  // the number of free parameters
1146  int parameter_num_loc = parameters.size();
1147 
1148  Matrix_real cost_function_terms;
1149 
1150  // vector containing gradients of the transformed matrix
1151  std::vector<Matrix> State_deriv;
1152  Matrix State;
1153 
1154  int parallel = get_parallel_configuration();
1155 
1156  tbb::parallel_invoke(
1157  [&] {
1158  State = instance->initial_state.copy();
1159  instance->apply_to(parameters, State);
1160  *f0 = instance->Expectation_value_of_energy_real(State, State);
1161  },
1162  [&] {
1163  Matrix State_loc = instance->initial_state.copy();
1164 
1165  State_deriv =
1166  instance->apply_derivate_to(parameters, State_loc, parallel);
1167  State_loc.release_data();
1168  });
1169 
1170  tbb::parallel_for(tbb::blocked_range<int>(0, parameter_num_loc, 2),
1171  [&](tbb::blocked_range<int> r) {
1172  for (int idx = r.begin(); idx < r.end(); ++idx) {
1173  grad[idx] =
1174  2 * instance->Expectation_value_of_energy_real(
1175  State_deriv[idx], State);
1176  }
1177  });
1178 
1179  /*
1180  double delta = 0.0000001;
1181 
1182  tbb::parallel_for( tbb::blocked_range<int>(0,parameter_num_loc,1),
1183  [&](tbb::blocked_range<int> r) { for (int idx=r.begin(); idx<r.end(); ++idx) {
1184  Matrix_real param_tmp = parameters.copy();
1185  param_tmp[idx] += delta;
1186 
1187  Matrix State = instance->initial_state.copy();
1188  instance->apply_to(param_tmp, State);
1189  double f_loc = instance->Expectation_value_of_energy_real(State,
1190  State);
1191 
1192 
1193  grad[idx] = (f_loc-*f0)/delta;
1194  }
1195  });
1196 
1197 */
1198  return;
1199 }
1200 
1208  Matrix_real parameters, double *f0, Matrix_real grad) {
1209 
1210  optimization_problem_combined_non_static(parameters, this, f0, grad);
1211  return;
1212 }
1213 
1223  Matrix_real parameters, void *void_instance, Matrix_real &grad) {
1224 
1225  double f0;
1227  reinterpret_cast<Variational_Quantum_Eigensolver_Base *>(void_instance);
1228  instance->optimization_problem_combined_non_static(parameters, void_instance,
1229  &f0, grad);
1230  return;
1231 }
1232 
1240 double
1242 
1243  // get the transformed matrix with the gates in the list
1244  Matrix_real parameters_mtx(parameters, 1, parameter_num);
1245 
1246  return optimization_problem(parameters_mtx);
1247 }
1248 
1254 
1255  int initialize_state;
1256  if (config.count("initialize_state") > 0) {
1257  long long value;
1258  config["initialize_state"].get_property(value);
1259  initialize_state = (int)value;
1260  } else {
1261  initialize_state = 1;
1262  }
1263 
1264  if (initialize_state == 0) {
1265  initial_state = Matrix(0, 0);
1266  return;
1267  }
1268 
1269  initial_state = Matrix(1 << qbit_num, 1);
1270 
1271  initial_state[0].real = 1.0;
1272  initial_state[0].imag = 0.0;
1273  memset(initial_state.get_data() + 2, 0,
1274  (initial_state.size() * 2 - 2) * sizeof(double));
1275 
1276  initial_state[1].real = 0.0;
1277  initial_state[1].imag = 0.0;
1278 
1279  return;
1280 }
1281 
1288 
1289  ansatz = ansatz_in;
1290 
1291  return;
1292 }
1293 
1301  int inner_blocks) {
1302 
1303  switch (ansatz) {
1304 
1305  case HEA: {
1306 
1307  release_gates();
1308 
1309  if (qbit_num < 1) {
1310  std::string error(
1311  "Variational_Quantum_Eigensolver_Base::generate_initial_circuit: "
1312  "number of qubits should be at least 1");
1313  throw error;
1314  }
1315 
1316  if (qbit_num == 1) {
1317  for (int layer_idx = 0; layer_idx < layers; layer_idx++) {
1318  for (int idx = 0; idx < inner_blocks; idx++) {
1319  add_u3(0);
1320  }
1321  }
1322 
1324  return;
1325  }
1326 
1327  for (int layer_idx = 0; layer_idx < layers; layer_idx++) {
1328 
1329  for (int idx = 0; idx < inner_blocks; idx++) {
1330  add_u3(1);
1331  add_u3(0);
1332  add_cnot(1, 0);
1333  }
1334 
1335  for (int control_qbit = 1; control_qbit < qbit_num - 1;
1336  control_qbit = control_qbit + 2) {
1337  if (control_qbit + 2 < qbit_num) {
1338 
1339  for (int idx = 0; idx < inner_blocks; idx++) {
1340  add_u3(control_qbit + 1);
1341  add_u3(control_qbit + 2);
1342 
1344  }
1345  }
1346 
1347  for (int idx = 0; idx < inner_blocks; idx++) {
1348  add_u3(control_qbit + 1);
1350 
1352  }
1353  }
1354  }
1355 
1357  return;
1358  }
1359  case HEA_ZYZ: {
1360 
1361  release_gates();
1362 
1363  if (qbit_num < 2) {
1364  std::string error(
1365  "Variational_Quantum_Eigensolver_Base::generate_initial_circuit: "
1366  "number of qubits should be at least 2");
1367  throw error;
1368  }
1369 
1370  for (int layer_idx = 0; layer_idx < layers; layer_idx++) {
1371 
1372  for (int idx = 0; idx < inner_blocks; idx++) {
1373  Gates_block *block_1 = new Gates_block(qbit_num);
1374  Gates_block *block_2 = new Gates_block(qbit_num);
1375 
1376  // single qubit gates in a ablock are then merged into a single gate
1377  // kernel.
1378  block_1->add_rz(1);
1379  block_1->add_ry(1);
1380  block_1->add_rz(1);
1381 
1382  add_gate(block_1);
1383 
1384  block_2->add_rz(0);
1385  block_2->add_ry(0);
1386  block_2->add_rz(0);
1387 
1388  add_gate(block_2);
1389 
1390  add_cnot(1, 0);
1391  }
1392 
1393  for (int control_qbit = 1; control_qbit < qbit_num - 1;
1394  control_qbit = control_qbit + 2) {
1395  if (control_qbit + 2 < qbit_num) {
1396 
1397  for (int idx = 0; idx < inner_blocks; idx++) {
1398 
1399  Gates_block *block_1 = new Gates_block(qbit_num);
1400  Gates_block *block_2 = new Gates_block(qbit_num);
1401 
1402  block_1->add_rz(control_qbit + 1);
1403  block_1->add_ry(control_qbit + 1);
1404  block_1->add_rz(control_qbit + 1);
1405  add_gate(block_1);
1406 
1407  block_2->add_rz(control_qbit + 2);
1408  block_2->add_ry(control_qbit + 2);
1409  block_2->add_rz(control_qbit + 2);
1410  add_gate(block_2);
1411 
1413  }
1414  }
1415 
1416  for (int idx = 0; idx < inner_blocks; idx++) {
1417 
1418  Gates_block *block_1 = new Gates_block(qbit_num);
1419  Gates_block *block_2 = new Gates_block(qbit_num);
1420 
1421  block_1->add_rz(control_qbit + 1);
1422  block_1->add_ry(control_qbit + 1);
1423  block_1->add_rz(control_qbit + 1);
1424  add_gate(block_1);
1425 
1426  block_2->add_rz(control_qbit);
1427  block_2->add_ry(control_qbit);
1428  block_2->add_rz(control_qbit);
1429  add_gate(block_2);
1430 
1432  }
1433  }
1434  }
1435 
1437  return;
1438  }
1439  default:
1440  std::string error("Variational_Quantum_Eigensolver_Base::generate_initial_"
1441  "circuit: ansatz not implemented");
1442  throw error;
1443  }
1444 }
1445 
1447  std::string filename) {
1448 
1449  release_gates();
1450  Matrix_real optimized_parameters_mtx_tmp;
1451  Gates_block *gate_structure_tmp =
1452  import_gate_list_from_binary(optimized_parameters_mtx_tmp, filename);
1453 
1454  if (gates.size() > 0) {
1455  gate_structure_tmp->combine(static_cast<Gates_block *>(this));
1456 
1457  release_gates();
1458  combine(gate_structure_tmp);
1459 
1460  Matrix_real optimized_parameters_mtx_tmp2(
1461  1,
1462  optimized_parameters_mtx_tmp.size() + optimized_parameters_mtx.size());
1463  memcpy(optimized_parameters_mtx_tmp2.get_data(),
1464  optimized_parameters_mtx_tmp.get_data(),
1465  optimized_parameters_mtx_tmp.size() * sizeof(double));
1466  memcpy(optimized_parameters_mtx_tmp2.get_data() +
1467  optimized_parameters_mtx_tmp.size(),
1469  optimized_parameters_mtx.size() * sizeof(double));
1470  optimized_parameters_mtx = optimized_parameters_mtx_tmp2;
1471  } else {
1472  combine(gate_structure_tmp);
1473  optimized_parameters_mtx = optimized_parameters_mtx_tmp;
1474 
1475  std::stringstream sstream;
1476  // get the number of gates used in the decomposition
1477  std::map<std::string, int> &&gate_nums = get_gate_nums();
1478 
1479  for (auto it = gate_nums.begin(); it != gate_nums.end(); it++) {
1480  sstream << it->second << " " << it->first << " gates" << std::endl;
1481  }
1482  print(sstream, 1);
1483  }
1484 
1486 }
1487 
1489  Gates_block *gate_structure_in) {
1490 
1493 }
1494 
1500  Matrix initial_state_in) {
1501 
1502  // check the size of the input state
1503  if (initial_state_in.size() != 1 << qbit_num) {
1504  std::string error("Variational_Quantum_Eigensolver_Base::set_initial_state:"
1505  " teh number of elements in the input state does not "
1506  "match with the number of qubits.");
1507  throw error;
1508  }
1509 
1510  initial_state = Matrix(1 << qbit_num, 1);
1511  memcpy(initial_state.get_data(), initial_state_in.get_data(),
1512  initial_state_in.size() * sizeof(QGD_Complex16));
1513 }
int shots
Definition: noise.py:65
optimization_aglorithms alg
The optimization algorithm to be used in the optimization.
bool adaptive_eta
logical variable indicating whether adaptive learning reate is used in the ADAM algorithm ...
void apply_to_groq_sv(int reserved_device_num, int chosen_device_num, int qbit_num, std::vector< Matrix > &u3_qbit, std::vector< int > &target_qbits, std::vector< int > &control_qbits, Matrix &quantum_state, int id_in)
Call to pefrom the state vector simulation on the Groq hardware.
void set_gate_structure(std::string filename)
Call to set custom layers to the gate structure that are intended to be used in the VQE process...
ansatz_type ansatz
Ansatz type (HEA stands for hardware efficient ansatz)
vqe_circuit_source_type circuit_source
Provenance of the currently configured circuit.
void print(const std::stringstream &sstream, int verbose_level=1) const
Call to print output messages in the function of the verbosity level.
Definition: logging.cpp:55
ansatz_type
Type definition of the fifferent types of ansatz.
std::vector< std::tuple< int, int, double > > yy_terms
Coupling terms of the form coeff * Y_i * Y_j.
bool decomposition_finalized
The optimized parameters for the gates.
std::vector< std::pair< int, double > > z_terms
Local magnetic field terms of the form coeff * Z_i.
std::map< std::string, int > get_gate_nums()
Call to get the number of the individual gate types in the list of gates.
void add_CNOT(int target, int control)
SimulationResult Expectation_value_with_shot_noise_real(Matrix &State, int shots, uint64_t seed, double p_readout)
Evaluate the expectation value of the energy with simulated shot noise and readout error...
void add_local_depolarizing(int target)
Add parametric local single-qubit depolarizing noise (1 parameter)
Class to solve VQE problems.
double expectation_value_of_density_energy_real(const squander::density::DensityMatrix &rho) const
void set_custom_gate_structure(Gates_block *gate_structure_in)
Call to set custom layers to the gate structure that are intended to be used in the subdecomposition...
std::vector< int > target_qbits
Vector of target qubit indices (for multi-qubit gates)
Definition: Gate.h:102
Matrix_real copy() const
Call to create a copy of the matrix.
int control_qbit
The index of the qubit which acts as a control qubit (control_qbit >= 0) in controlled operations...
Definition: Gate.h:100
bool optimization_problem_solved
logical value describing whether the optimization problem was solved or not
void add_gate(Gate *gate)
Append a general gate to the list of gates.
void add_phase_damping(int target)
Add parametric phase damping noise (1 parameter)
std::vector< std::tuple< int, int, double > > zz_terms
Coupling terms of the form coeff * Z_i * Z_j.
cost_function_type cost_fnc
The chosen variant of the cost function.
Variational_Quantum_Eigensolver_Base()
Nullary constructor of the class.
void apply_to(const double *params, int param_count, DensityMatrix &rho)
Apply entire circuit to density matrix.
Reviewable metadata describing one lowered density-bridge operation.
void release_gates()
Call to release the stored gates.
vqe_backend_type
Supported execution backends for the VQE workflow contract.
static tbb::spin_mutex my_mutex
void release_data()
Call to release the data stored by the matrix.
QGD_Complex16 * data
Definition: matrix_sparse.h:47
std::atomic< int > number_of_iters
number of iterations
std::vector< DensityBridgeOperationInfo > inspect_density_bridge()
Describe the supported density bridge for the current VQE instance.
void add_cnot(int target_qbit, int control_qbit)
Append a CNOT gate gate to the list of gates.
QGD_Complex16 mult(QGD_Complex16 &a, QGD_Complex16 &b)
Call to calculate the product of two complex scalars.
Definition: common.cpp:298
Class to store data of complex arrays and its properties.
Definition: matrix_sparse.h:38
scalar * get_data() const
Call to get the pointer to the stored data.
virtual void optimization_problem_combined(Matrix_real parameters, double *f0, Matrix_real grad) override
Call to calculate both the cost function and the its gradient components.
Matrix initial_state
Quantum state used as an initial state in the VQE iterations.
Gates_block * import_gate_list_from_binary(Matrix_real &parameters, const std::string &filename, int verbosity)
Use to import a quantum circuit from a binary format.
int get_gate_num()
Call to get the number of gates grouped in the class.
virtual double optimization_problem_non_static(Matrix_real parameters, void *void_instance) override
The optimization problem of the final optimization.
std::vector< Gate * > gates
The list of stored gates.
Definition: Gates_block.h:49
void generate_circuit(int layers, int inner_blocks)
Call to generate the circuit ansatz.
void add_U3(int target)
3 parameters
double optimization_tolerance
The maximal allowed error of the optimization problem (The error of the decomposition would scale wit...
static void optimization_problem_grad_vqe(Matrix_real parameters, void *void_instance, Matrix_real &grad)
Calculate the derivative of the cost function with respect to the free parameters.
int accelerator_num
number of utilized accelerators
int rows
The number of rows.
Definition: matrix_base.hpp:42
void lower_anchor_circuit_to_noisy_circuit(squander::density::NoisyCircuit &circuit)
vqe_backend_type backend_mode
Effective execution backend selected for this VQE instance.
void start_optimization()
Call to start solving the VQE problem to get the approximation for the ground state.
void set_density_noise_specs(const std::vector< DensityNoiseSpec > &density_noise_specs_in)
Configure ordered fixed local-noise insertions for the density backend.
double convergence_threshold
The convergence threshold in the optimization process.
gate_type get_type()
Call to get the type of the operation.
Definition: Gate.cpp:1333
void add_u3(int target_qbit)
Append a U3 gate to the list of gates.
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
void add_rz(int target_qbit)
Append a RZ gate to the list of gates.
QGD_Complex16 Expectation_value_of_energy(Matrix &State_left, Matrix &State_right)
Call to evaluate the expectation value of the energy <State_left| H | State_right>.
virtual void optimization_problem_combined_non_static(Matrix_real parameters, void *void_instance, double *f0, Matrix_real &grad) override
Call to calculate both the cost function and the its gradient components.
void combine(Gates_block *op_block)
Call to append the gates of an gate block to the current block.
void add_ry(int target_qbit)
Append a RY gate to the list of gates.
Quantum density matrix ρ for mixed-state representation.
std::string get_density_bridge_source_label() const
Return the current circuit-source label used by bridge inspection.
virtual void apply_to(Matrix_real &parameters_mtx, Matrix &input, int parallel=0) override
Call to apply the gate on the input array/matrix Gates_block*input.
Container for statistics returned by shot-noise simulations.
density_noise_type
Supported local noise channels.
void append_density_noise_for_gate_index(squander::density::NoisyCircuit &circuit, int gate_index) const
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
void set_initial_state(Matrix initial_state_in)
Call to set the initial quantum state in the VQE iterations.
A base class to solve VQE problems This class can be used to approximate the ground state of the inpu...
void set_ansatz(ansatz_type ansatz_in)
Call to set the ansatz type.
Matrix copy() const
Call to create a copy of the matrix.
Definition: matrix.h:57
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
Definition: H.h:11
dictionary config
int size() const
Call to get the number of the allocated elements.
std::vector< int > control_qbits
Vector of control qubit indices (for multi-qubit gates)
Definition: Gate.h:104
Gates_block()
Default constructor of the class.
Definition: Gates_block.cpp:82
virtual int get_parameter_num()
Call to get the number of free parameters.
Definition: Gate.cpp:1324
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:44
Matrix_sparse Hamiltonian
The Hamiltonian of the system.
int get_target_qbit()
Call to get the index of the target qubit.
Definition: Gate.cpp:1203
std::map< std::string, Config_Element > config
config metadata utilized during the optimization
Base class for the representation of general gate operations.
Definition: Gate.h:86
std::string get_name()
Call to get the name label of the gate.
Definition: Gate.cpp:2740
int get_dim() const
Get matrix dimension (2^qbit_num)
int parameter_num
the number of free parameters of the operation
Definition: Gate.h:108
void solve_layer_optimization_problem(int num_of_parameters, Matrix_real solution_guess)
Call to solve layer by layer the optimization problem via calling one of the implemented algorithms...
volatile double current_minimum
The current minimum of the optimization problem.
double Expectation_value_of_energy_real(Matrix &State_left, Matrix &State_right)
Call to evaluate the expectation value of the energy <State_left| H | State_right>.
std::vector< std::tuple< int, int, double > > xx_terms
Coupling terms of the form coeff * X_i * X_j.
double real
the real part of a complex number
Definition: QGDTypes.h:40
void initialize_zero_state()
Initialize the state used in the quantun circuit.
int qbit_num
number of qubits spanning the matrix of the operation
Definition: Gate.h:94
vqe_circuit_source_type
Provenance of the active VQE circuit used for backend validation.
std::vector< DensityNoiseSpec > density_noise_specs
Ordered fixed local-noise insertions for the density backend.
double decomposition_error
error of the final decomposition
Gate * get_gate(int idx)
Call to get the gates stored in the class.
Quantum circuit with noise for density matrix simulation.
Definition: noisy_circuit.h:57
virtual void apply_to(Matrix &input, int parallel)
Call to apply the gate on the input array/matrix.
Definition: Gate.cpp:433
int random_shift_count_max
the maximal number of parameter randomization tries to escape a local minimum.
void add_amplitude_damping(int target)
Add parametric amplitude damping noise (1 parameter)
Matrix_real optimized_parameters_mtx
The optimized parameters for the gates.
int get_parallel_configuration()
Get the parallel configuration from the config.
int get_parameter_num() override
Call to get the number of free parameters.
Definition: SDG.h:11
void set_custom_gate_structure(Gates_block *gate_structure_in)
Call to set custom gate structure for VQE experiments.
double global_target_minimum
The global target minimum of the optimization problem.
int get_control_qbit()
Call to get the index of the control qubit.
Definition: Gate.cpp:1211
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:41
void validate_density_anchor_support(bool require_optimizer_support=false, bool require_gradient_support=false)
std::mt19937 gen
Standard mersenne_twister_engine seeded with rd()
virtual std::vector< Matrix > apply_derivate_to(Matrix_real &parameters_mtx, Matrix &input, int parallel) override
Call to evaluate the derivate of the circuit on an inout with respect to all of the free parameters...
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42
virtual ~Variational_Quantum_Eigensolver_Base()
Destructor of the class.
virtual double optimization_problem(Matrix_real &parameters) override
The optimization problem of the final optimization.