QCOR
qcor_qsim.hpp
1 #pragma once
2 #include <memory>
3 #include <qalloc>
4 #include <xacc_internal_compiler.hpp>
5 
6 #include "Accelerator.hpp"
7 #include "AcceleratorBuffer.hpp"
8 #include "Circuit.hpp"
9 #include "kernel_evaluator.hpp"
10 #include "objective_function.hpp"
11 #include "qcor_observable.hpp"
12 #include "qcor_optimizer.hpp"
13 #include "qcor_utils.hpp"
14 #include "qrt.hpp"
15 #include "quantum_kernel.hpp"
16 
17 using CompositeInstruction = xacc::CompositeInstruction;
18 using Accelerator = xacc::Accelerator;
19 using Identifiable = xacc::Identifiable;
20 
21 namespace qcor {
22 namespace QuaSiMo {
23 // Struct captures a state-preparation circuit.
24 struct Ansatz {
25  std::shared_ptr<CompositeInstruction> circuit;
26  std::vector<std::string> symbol_names;
27  size_t nb_qubits;
28 };
29 
30 // State-preparation method (i.e. ansatz):
31 // For example,
32 // - Real time Hamiltonian evolution with Trotter approximation (of various
33 // orders)
34 // - Real time Hamiltonian evolution with Taylor series/LCU approach
35 // - Imaginary time evolution with QITE
36 // - Thermo-field doubles state preparation
37 // - Two-point correlation function measurements
38 // - QFT
39 class AnsatzGenerator : public Identifiable {
40  public:
41  virtual Ansatz create_ansatz(Operator *obs = nullptr,
42  const HeterogeneousMap &params = {}) = 0;
43 };
44 
45 // CostFunctionEvaluator take an unknown quantum state (the circuit that
46 // prepares the unknown state) and a target operator (e.g. Hamiltonian
47 // Observable) as input and produce a estimation as output.
48 class CostFunctionEvaluator : public Identifiable {
49  public:
50  // Evaluate the cost
51  virtual double evaluate(std::shared_ptr<CompositeInstruction> state_prep) = 0;
52  // Batching evaluation: observing multiple kernels in batches.
53  // E.g. for non-vqe cases (Trotter), we have all kernels ready for observable
54  // evaluation
55  virtual std::vector<double> evaluate(
56  std::vector<std::shared_ptr<CompositeInstruction>> state_prep_circuits) {
57  // Default is one-by-one, subclass to provide batching if supported.
58  std::vector<double> result;
59  for (auto &circuit : state_prep_circuits) {
60  result.emplace_back(evaluate(circuit));
61  }
62  return result;
63  }
64 
65  virtual bool initialize(Operator *observable,
66  const HeterogeneousMap &params = {});
67 
68  protected:
69  Operator *target_operator;
70  HeterogeneousMap hyperParams;
71 };
72 
73 // Trotter time-dependent simulation workflow
74 // Time-dependent Hamiltonian is a function mapping from time t to Observable
75 // operator.
76 using TdObservable = std::function<Operator(double)>;
77 
78 // Capture a quantum chemistry problem.
79 // TODO: generalize this to capture all potential use cases.
80 
82  QuantumSimulationModel() : observable(nullptr) {}
83  // Copy Constructor
85  : name(other.name),
86  observable(other.observable),
87  hamiltonian(other.hamiltonian),
88  user_defined_ansatz(other.user_defined_ansatz),
89  owns_observable(other.owns_observable) {
90  if (other.owns_observable) {
91  // Transfer ownership.
92  other.owns_observable = false;
93  }
94  }
95 
96  // Move Constructor
98  : name(other.name),
99  observable(other.observable),
100  hamiltonian(other.hamiltonian),
101  user_defined_ansatz(other.user_defined_ansatz),
102  owns_observable(other.owns_observable) {
103  if (other.owns_observable) {
104  // Transfer ownership.
105  other.owns_observable = false;
106  }
107  }
108 
109  // Model name.
110  std::string name;
111 
112  // The Observable operator that needs to be measured/minimized.
113  Operator *observable;
114 
115  // The system Hamiltonian which can be static or dynamic (time-dependent).
116  // This can be the same or different from the observable operator.
117  TdObservable hamiltonian;
118 
119  // QuantumSimulationModel also support a user-defined (fixed) ansatz.
120  std::shared_ptr<KernelFunctor> user_defined_ansatz;
121 
122  // clients can create raw operator pointers, and
123  // provided them to this Model and indicate that
124  // the Model owns the observable, and will delete it
125  // upon destruction
126  bool owns_observable = false;
127 
128  // Destructor, delete observable if we own it
130  if (owns_observable) {
131  printf("Deleting the raw ptr\n");
132  delete observable;
133  }
134  }
135 };
136 
137 // Generic model builder (factory)
138 // Create a model which capture the problem description.
140  public:
141  // Generic Heisenberg model
143  double Jx = 0.0;
144  double Jy = 0.0;
145  double Jz = 0.0;
146  double h_ext = 0.0;
147  // Support for H_BAR normalization
148  double H_BAR = 1.0;
149  // "X", "Y", or "Z"
150  std::string ext_dir = "Z";
151  int num_spins = 2;
152  std::vector<int> initial_spins;
153  // Time-dependent freq.
154  // Default to using the cosine function.
155  double freq = 0.0;
156  // User-provided custom time-dependent function:
157  std::function<double(double)> time_func;
158  // Allows a simple Pythonic kwarg-style initialization.
159  // i.e. all params have preset defaults, only update those that are
160  // specified.
161  void fromDict(const HeterogeneousMap &params);
162 
163  bool validateModel() const {
164  const bool ext_dir_valid =
165  (ext_dir == "X" || ext_dir == "Y" || ext_dir == "Z");
166  const bool initial_spins_valid =
167  (initial_spins.empty() || (initial_spins.size() == num_spins));
168  return ext_dir_valid && initial_spins_valid;
169  }
170  };
171 
172  // ======== Direct model builder ==============
173  // Strongly-typed parameters/argument.
174  // Build a simple Hamiltonian-based model: static Hamiltonian which is also
175  // the observable of interest.
176  static QuantumSimulationModel createModel(
177  Operator *obs, const HeterogeneousMap &params = {});
178  static QuantumSimulationModel createModel(
179  Operator &obs, const HeterogeneousMap &params = {}) {
180  return createModel(&obs, params);
181  }
182 
183  // Build a time-dependent problem model:
184  // - obs: observable operator to measure.
185  // - td_ham: time-dependent Hamiltonian to evolve the system.
186  // e.g. a function to map from time to Hamiltonian operator.
187  static QuantumSimulationModel createModel(
188  Operator *obs, TdObservable td_ham, const HeterogeneousMap &params = {});
189  // Pauli operator overload:
190  static QuantumSimulationModel createModel(
191  Operator &obs, TdObservable td_ham, const HeterogeneousMap &params = {}) {
192  return createModel(&obs, td_ham, params);
193  }
194 
195  // ======== High-level model builder ==============
196  // The idea here is to have contributed modules to translate problem
197  // descriptions in various formats, e.g. PyScf, Psi4, broombridge, etc. into
198  // QCOR's Observable type. Inputs:
199  // - format: key to look up module/plugin to digest the input data.
200  // - data: model descriptions in plain-text format, e.g. load from file.
201  // - params: extra parameters to pass to the parser/generator,
202  // e.g. any transformations required in order to generate the
203  // Observable.
204  static QuantumSimulationModel createModel(
205  const std::string &format, const std::string &data,
206  const HeterogeneousMap &params = {});
207  // Predefined model type that we support intrinsically.
208  enum class ModelType { Heisenberg };
209  static QuantumSimulationModel createModel(ModelType type,
210  const HeterogeneousMap &params);
211  // ========== QuantumSimulationModel with a fixed (pre-defined) ansatz
212  // ======== The ansatz is provided as a QCOR kernel.
213  template <typename... Args>
214  static inline QuantumSimulationModel createModel(
215  void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
216  Args...),
217  Operator *obs, size_t nbQubits, size_t nbParams) {
218  auto kernel_functor =
219  createKernelFunctor(quantum_kernel_functor, nbQubits, nbParams);
220 
221  QuantumSimulationModel model;
222  model.observable = obs;
223  model.user_defined_ansatz = kernel_functor;
224  return model;
225  }
226 
227  template <typename... Args>
228  static inline QuantumSimulationModel createModel(
229  void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
230  Args...),
231  Operator &obs, size_t nbQubits, size_t nbParams) {
232  return createModel(quantum_kernel_functor, &obs, nbQubits, nbParams);
233  }
234 
235  // Passing the state-preparation ansatz as a CompositeInstruction
236  static inline QuantumSimulationModel createModel(
237  std::shared_ptr<CompositeInstruction> composite, Operator *obs) {
238  QuantumSimulationModel model;
239  model.observable = obs;
240  model.user_defined_ansatz = createKernelFunctor(composite);
241  return model;
242  }
243 
244  static inline QuantumSimulationModel createModel(
245  std::shared_ptr<CompositeInstruction> composite, Operator &obs) {
246  return createModel(composite, &obs);
247  }
248 };
249 
250 // Quantum Simulation Workflow (Protocol)
251 // This can handle both variational workflow (optimization loop)
252 // as well as simple Trotter evolution workflow.
253 // Result is stored in a HetMap
254 using QuantumSimulationResult = HeterogeneousMap;
255 // Abstract workflow:
256 class QuantumSimulationWorkflow : public Identifiable {
257  public:
258  virtual bool initialize(const HeterogeneousMap &params) = 0;
259  virtual QuantumSimulationResult execute(
260  const QuantumSimulationModel &model) = 0;
261 
262  protected:
263  std::shared_ptr<CostFunctionEvaluator> evaluator;
264 };
265 
266 // Get workflow by name:
267 std::shared_ptr<QuantumSimulationWorkflow> getWorkflow(
268  const std::string &name, const HeterogeneousMap &init_params);
269 
270 // Get the Obj (cost) function evaluator:
271 std::shared_ptr<CostFunctionEvaluator> getObjEvaluator(
272  Operator *observable, const std::string &name = "default",
273  const HeterogeneousMap &init_params = {});
274 inline std::shared_ptr<CostFunctionEvaluator> getObjEvaluator(
275  Operator &obs, const std::string &name = "default",
276  const HeterogeneousMap &init_params = {}) {
277  return getObjEvaluator(&obs, name, init_params);
278 }
279 
280 // Helper to apply optimization/placement before evaluation:
281 void executePassManager(
282  std::vector<std::shared_ptr<CompositeInstruction>> evalKernels);
283 } // namespace QuaSiMo
284 } // namespace qcor
qcor::Operator
Definition: qcor_observable.hpp:24
qcor::QuaSiMo::Ansatz
Definition: qcor_qsim.hpp:24
qcor::QuaSiMo::QuantumSimulationWorkflow
Definition: qcor_qsim.hpp:256
qcor::QuaSiMo::AnsatzGenerator
Definition: qcor_qsim.hpp:39
qcor::QuaSiMo::QuantumSimulationModel
Definition: qcor_qsim.hpp:81
qcor
Definition: qcor_syntax_handler.cpp:15
qcor::QuaSiMo::CostFunctionEvaluator
Definition: qcor_qsim.hpp:48
qcor::QuaSiMo::ModelFactory::HeisenbergModel
Definition: qcor_qsim.hpp:142
qcor::QuaSiMo::ModelFactory
Definition: qcor_qsim.hpp:139