Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
PathIntegralSimulator.h
Go to the documentation of this file.
1
9
10#pragma once
11
12#ifndef _PATH_INTEGRAL_SIMULATOR_H_
13#define _PATH_INTEGRAL_SIMULATOR_H_
14
15#include <optional>
16#include <type_traits>
17#include <utility>
18
20#include "PathIntegral.h"
21
22namespace Simulators
23{
24
26{
27 public:
28 using AmplitudeMap = std::remove_reference_t<decltype(std::declval<QC::PathIntegral::PathIntegralSimulator &>().GetAmplitudes())>;
29
30 void SetSeed(uint64_t seed)
31 {
32 simulator.SetSeed(seed);
33 }
34
35 void SetTrimValue(double val)
36 {
37 simulator.SetTrimValue(val);
38 }
39
40 double GetTrimValue() const
41 {
42 return simulator.GetTrimValue();
43 }
44
45 void SetMaxDoublingsForBackwardPaths(size_t doublings)
46 {
47 simulator.SetMaxDoublingsForBackwardPaths(doublings);
48 }
49
51 {
52 return simulator.GetMaxDoublingsForBackwardPaths();
53 }
54
55 void Reset()
56 {
57 simulator.Reset();
58 }
59
60 bool SetCircuit(const std::shared_ptr<Circuits::Circuit<>> &circuit)
61 {
62 const auto convertedCircuit = ConvertCircuit(circuit);
63 if (convertedCircuit.empty())
64 return false;
65 simulator.SetCircuit(convertedCircuit);
66 return true;
67 }
68
69 std::complex<double> AmplitudeFromZero(const std::vector<bool> &endState)
70 {
71 return simulator.Propagate(endState);
72 }
73
74 std::complex<double> Amplitude(const std::vector<bool> &startState, const std::vector<bool> &endState)
75 {
76 return simulator.Propagate(startState, endState);
77 }
78
79 std::optional<QC::Gates::AppliedGate<>> ConvertGate(const std::shared_ptr<Circuits::IQuantumGate<>> &gate)
80 {
81 if (!gate)
82 return std::nullopt;
83
84 switch (gate->GetGateType())
85 {
86 // single qubit gates
88 const auto g = std::static_pointer_cast<Circuits::PhaseGate<>>(gate);
89 pgate.SetPhaseShift(g->GetLambda());
90 return QC::Gates::AppliedGate<>(pgate.getRawOperatorMatrix(), gate->GetQubit(0));
91 }
93 return QC::Gates::AppliedGate<>(xgate.getRawOperatorMatrix(), gate->GetQubit(0));
95 return QC::Gates::AppliedGate<>(ygate.getRawOperatorMatrix(), gate->GetQubit(0));
97 return QC::Gates::AppliedGate<>(zgate.getRawOperatorMatrix(), gate->GetQubit(0));
99 return QC::Gates::AppliedGate<>(h.getRawOperatorMatrix(), gate->GetQubit(0));
101 return QC::Gates::AppliedGate<>(sgate.getRawOperatorMatrix(), gate->GetQubit(0));
103 return QC::Gates::AppliedGate<>(sdggate.getRawOperatorMatrix(), gate->GetQubit(0));
105 return QC::Gates::AppliedGate<>(tgate.getRawOperatorMatrix(), gate->GetQubit(0));
107 return QC::Gates::AppliedGate<>(tdggate.getRawOperatorMatrix(), gate->GetQubit(0));
109 return QC::Gates::AppliedGate<>(sxgate.getRawOperatorMatrix(), gate->GetQubit(0));
111 return QC::Gates::AppliedGate<>(sxdaggate.getRawOperatorMatrix(), gate->GetQubit(0));
113 return QC::Gates::AppliedGate<>(k.getRawOperatorMatrix(), gate->GetQubit(0));
115 const auto g = std::static_pointer_cast<Circuits::RxGate<>>(gate);
116 rxgate.SetTheta(g->GetTheta());
117 return QC::Gates::AppliedGate<>(rxgate.getRawOperatorMatrix(), gate->GetQubit(0));
118 }
120 const auto g = std::static_pointer_cast<Circuits::RyGate<>>(gate);
121 rygate.SetTheta(g->GetTheta());
122 return QC::Gates::AppliedGate<>(rygate.getRawOperatorMatrix(), gate->GetQubit(0));
123 }
125 const auto g = std::static_pointer_cast<Circuits::RzGate<>>(gate);
126 rzgate.SetTheta(g->GetTheta());
127 return QC::Gates::AppliedGate<>(rzgate.getRawOperatorMatrix(), gate->GetQubit(0));
128 }
130 const auto g = std::static_pointer_cast<Circuits::UGate<>>(gate);
131 ugate.SetParams(g->GetTheta(), g->GetPhi(), g->GetLambda(), g->GetGamma());
132 return QC::Gates::AppliedGate<>(ugate.getRawOperatorMatrix(), gate->GetQubit(0));
133 }
134
135 // two qubit gates
137 return QC::Gates::AppliedGate<>(swapgate.getRawOperatorMatrix(), gate->GetQubit(0), gate->GetQubit(1));
139 return QC::Gates::AppliedGate<>(cxgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
141 return QC::Gates::AppliedGate<>(cygate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
143 return QC::Gates::AppliedGate<>(czgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
145 const auto g = std::static_pointer_cast<Circuits::CPGate<>>(gate);
146 cpgate.SetPhaseShift(g->GetLambda());
147 return QC::Gates::AppliedGate<>(cpgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
148 }
150 const auto g = std::static_pointer_cast<Circuits::CRxGate<>>(gate);
151 crxgate.SetTheta(g->GetTheta());
152 return QC::Gates::AppliedGate<>(crxgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
153 }
155 const auto g = std::static_pointer_cast<Circuits::CRyGate<>>(gate);
156 crygate.SetTheta(g->GetTheta());
157 return QC::Gates::AppliedGate<>(crygate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
158 }
160 const auto g = std::static_pointer_cast<Circuits::CRzGate<>>(gate);
161 crzgate.SetTheta(g->GetTheta());
162 return QC::Gates::AppliedGate<>(crzgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
163 }
165 return QC::Gates::AppliedGate<>(ch.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
167 return QC::Gates::AppliedGate<>(csx.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
169 return QC::Gates::AppliedGate<>(csxdag.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
171 const auto g = std::static_pointer_cast<Circuits::CUGate<>>(gate);
172 cugate.SetParams(g->GetTheta(), g->GetPhi(), g->GetLambda(), g->GetGamma());
173 return QC::Gates::AppliedGate<>(cugate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
174 }
175
176 // three qubit gates
178 return QC::Gates::AppliedGate<>(ccxgate.getRawOperatorMatrix(), gate->GetQubit(2), gate->GetQubit(1), gate->GetQubit(0));
180 return QC::Gates::AppliedGate<>(cswapgate.getRawOperatorMatrix(), gate->GetQubit(2), gate->GetQubit(1), gate->GetQubit(0));
181
182 default:
183 return std::nullopt;
184 }
185 }
186
187 std::vector<QC::Gates::AppliedGate<>> ConvertCircuit(const std::shared_ptr<Circuits::Circuit<>> &circuit)
188 {
189 std::vector<QC::Gates::AppliedGate<>> result;
190 if (!circuit)
191 return result;
192
193 const auto &operations = circuit->GetOperations();
194 result.reserve(operations.size());
195
196 for (const auto &op : operations)
197 {
198 if (!op || op->GetType() != Circuits::OperationType::kGate)
199 continue;
200
201 const auto &gate = std::static_pointer_cast<Circuits::IQuantumGate<>>(op);
202 if (auto applied = ConvertGate(gate))
203 result.emplace_back(std::move(*applied));
204 }
205
206 return result;
207 }
208
209 void PropagateStep(const QC::Gates::AppliedGate<> &gate, AmplitudeMap &currentAmplitudes)
210 {
211 simulator.PropagateStep(gate, currentAmplitudes);
212 }
213
214 static size_t GetBranchingForQcsimCircuit(const std::vector<QC::Gates::AppliedGate<>> &circuit)
215 {
216 size_t doublings = 0;
217 for (const auto &gate : circuit)
218 if (gate.isBranching())
219 ++doublings;
220
221 return doublings;
222 }
223
224 size_t GetBranchingForMaestroCircuit(const std::shared_ptr<Circuits::Circuit<>> &circuit)
225 {
226 /*
227 const auto circ = ConvertCircuit(circuit);
228 return GetBranchingForQcsimCircuit(circ);
229 */
230
231 return GetBranchingForMaestroCircuitFromPos(circuit, 0);
232 }
233
234 size_t GetBranchingForMaestroCircuitFromPos(const std::shared_ptr<Circuits::Circuit<>> &circuit, size_t pos)
235 {
236 if (!circuit)
237 return 0;
238
239 size_t doublings = 0;
240 const auto &operations = circuit->GetOperations();
241 for (size_t i = pos; i < operations.size(); ++i)
242 if (operations[i]->IsBranching())
243 ++doublings;
244
245 return doublings;
246 }
247
248 double QubitProbability(size_t qubit, bool value = true)
249 {
250 return simulator.QubitProbability(qubit, value);
251 }
252
253 std::complex<double> AmplitudeForOutcome(size_t outcome)
254 {
255 const auto &amplitudes = simulator.GetAmplitudes();
256 if (amplitudes.empty())
257 return 0.0;
258
259 QC::PathIntegral::FastVectorBool state(amplitudes.begin()->first.size());
260 for (size_t q = 0; q < state.size(); ++q)
261 state.set(q, (outcome >> q) & 1);
262 if (auto it = amplitudes.find(state); it != amplitudes.end())
263 return it->second;
264 return 0.0;
265 }
266
267 double Probability(size_t outcome)
268 {
269 return std::norm(AmplitudeForOutcome(outcome));
270 }
271
272 QC::PathIntegral::FastVectorBool MeasureNoCollapse()
273 {
274 return simulator.MeasureNoCollapse();
275 }
276
277 bool MeasureQubit(size_t qubit)
278 {
279 return simulator.MeasureQubit(qubit);
280 }
281
282 std::unique_ptr<PathIntegralSimulator> Clone() const
283 {
284 auto clone = std::make_unique<PathIntegralSimulator>();
285 clone->simulator = simulator.Clone();
286 return clone;
287 }
288
290 {
291 return simulator.GetAmplitudes();
292 }
293
294 void SetStartZeroState(size_t numQubits)
295 {
296 auto &amplitudes = simulator.GetAmplitudes();
297
298 amplitudes.clear();
299 QC::PathIntegral::FastVectorBool zeroState(numQubits);
300 amplitudes[zeroState] = 1.0;
301 }
302
303 double ExpectationValue(const std::string &pauliStringOrig)
304 {
305 // save current amplitudes
306 const auto &currentAmplitudes = simulator.GetAmplitudes();
307 auto amplitudes = currentAmplitudes;
308
309 // now apply the pauli string as gates
310 for (size_t i = 0; i < pauliStringOrig.size(); ++i)
311 {
312 const char c = pauliStringOrig[i];
313 switch (c)
314 {
315 case 'X':
316 [[fallthrough]];
317 case 'x':
318 PropagateStep(QC::Gates::AppliedGate<>(xgate.getRawOperatorMatrix(), i), amplitudes);
319 break;
320 case 'Y':
321 [[fallthrough]];
322 case 'y':
323 PropagateStep(QC::Gates::AppliedGate<>(ygate.getRawOperatorMatrix(), i), amplitudes);
324 break;
325 case 'Z':
326 [[fallthrough]];
327 case 'z':
328 PropagateStep(QC::Gates::AppliedGate<>(zgate.getRawOperatorMatrix(), i), amplitudes);
329 break;
330 default:
331 break;
332 }
333 }
334
335 // use the new amplitudes to calculate the expectation value
336 double expectation = 0.0;
337
338 for (const auto &[state, amplitude] : amplitudes)
339 {
340 if (currentAmplitudes.find(state) == currentAmplitudes.end())
341 continue;
342
343 const auto &oldAmplitude = currentAmplitudes.at(state);
344 expectation += std::real(std::conj(oldAmplitude) * amplitude);
345 }
346
347 return expectation;
348 }
349
350 void ApplyGate(const QC::Gates::AppliedGate<> &gate)
351 {
352 simulator.PropagateStep(gate, simulator.GetAmplitudes());
353 }
354
356 {
357 simulator.SaveAmplitudes();
358 }
359
361 {
362 simulator.RestoreAmplitudes();
363 }
364
365 private:
366 QC::PathIntegral::PathIntegralSimulator simulator;
367
368 // These gates are never modified, and their matrix accessors return const
369 // references. Sharing them avoids allocation on construction and cloning.
370 inline static const QC::Gates::PauliXGate<> xgate;
371 inline static const QC::Gates::PauliYGate<> ygate;
372 inline static const QC::Gates::PauliZGate<> zgate;
373 inline static const QC::Gates::HadamardGate<> h;
374 inline static const QC::Gates::SGate<> sgate;
375 inline static const QC::Gates::SDGGate<> sdggate;
376 inline static const QC::Gates::TGate<> tgate;
377 inline static const QC::Gates::TDGGate<> tdggate;
378 inline static const QC::Gates::SquareRootNOTGate<> sxgate;
379 inline static const QC::Gates::SquareRootNOTDagGate<> sxdaggate;
380 inline static const QC::Gates::HyGate<> k;
381 inline static const QC::Gates::CNOTGate<> cxgate;
382 inline static const QC::Gates::ControlledYGate<> cygate;
383 inline static const QC::Gates::ControlledZGate<> czgate;
384 inline static const QC::Gates::ControlledHadamardGate<> ch;
385 inline static const QC::Gates::ControlledSquareRootNOTGate<> csx;
386 inline static const QC::Gates::ControlledSquareRootNOTDagGate<> csxdag;
387 inline static const QC::Gates::SwapGate<> swapgate;
388 inline static const QC::Gates::ToffoliGate<> ccxgate;
389 inline static const QC::Gates::FredkinGate<> cswapgate;
390
391 // Setters update these matrices before each application. Keep them local
392 // to each simulator so independent instances and clones cannot interfere.
393 QC::Gates::PhaseShiftGate<> pgate;
394 QC::Gates::RxGate<> rxgate;
395 QC::Gates::RyGate<> rygate;
396 QC::Gates::RzGate<> rzgate;
397 QC::Gates::UGate<> ugate;
398 QC::Gates::ControlledPhaseShiftGate<> cpgate;
399 QC::Gates::ControlledRxGate<> crxgate;
400 QC::Gates::ControlledRyGate<> crygate;
401 QC::Gates::ControlledRzGate<> crzgate;
402 QC::Gates::ControlledUGate<> cugate;
403};
404
405} // namespace Simulators
406
407#endif // _PATH_INTEGRAL_SIMULATOR_H_
Circuit class for holding the sequence of operations.
Definition Circuit.h:51
The interface for quantum gates.
std::optional< QC::Gates::AppliedGate<> > ConvertGate(const std::shared_ptr< Circuits::IQuantumGate<> > &gate)
size_t GetBranchingForMaestroCircuit(const std::shared_ptr< Circuits::Circuit<> > &circuit)
std::remove_reference_t< decltype(std::declval< QC::PathIntegral::PathIntegralSimulator & >().GetAmplitudes())> AmplitudeMap
std::complex< double > AmplitudeFromZero(const std::vector< bool > &endState)
bool SetCircuit(const std::shared_ptr< Circuits::Circuit<> > &circuit)
double ExpectationValue(const std::string &pauliStringOrig)
std::complex< double > Amplitude(const std::vector< bool > &startState, const std::vector< bool > &endState)
size_t GetBranchingForMaestroCircuitFromPos(const std::shared_ptr< Circuits::Circuit<> > &circuit, size_t pos)
static size_t GetBranchingForQcsimCircuit(const std::vector< QC::Gates::AppliedGate<> > &circuit)
void ApplyGate(const QC::Gates::AppliedGate<> &gate)
double QubitProbability(size_t qubit, bool value=true)
std::complex< double > AmplitudeForOutcome(size_t outcome)
std::vector< QC::Gates::AppliedGate<> > ConvertCircuit(const std::shared_ptr< Circuits::Circuit<> > &circuit)
void PropagateStep(const QC::Gates::AppliedGate<> &gate, AmplitudeMap &currentAmplitudes)
void SetMaxDoublingsForBackwardPaths(size_t doublings)
QC::PathIntegral::FastVectorBool MeasureNoCollapse()
std::unique_ptr< PathIntegralSimulator > Clone() const
@ kGate
the usual quantum gate, result stays in simulator's state
Definition Operations.h:30
Bounded online routing based on current physical bond dimensions.