Maestro 0.2.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
17#include "PathIntegral.h"
18#include "../Circuit/Circuit.h"
19
20namespace Simulators {
21
23 public:
24 void SetTrimValue(double val) { simulator.SetTrimValue(val); }
25
26 double GetTrimValue() const { return simulator.GetTrimValue(); }
27
28 void SetMaxDoublingsForBackwardPaths(size_t doublings) {
29 simulator.SetMaxDoublingsForBackwardPaths(doublings);
30 }
31
32 size_t GetMaxDoublingsForBackwardPaths() const { return simulator.GetMaxDoublingsForBackwardPaths(); }
33
34 void Reset() { simulator.Reset(); }
35
36 bool SetCircuit(const std::shared_ptr<Circuits::Circuit<>>& circuit)
37 {
38 const auto convertedCircuit = ConvertCircuit(circuit);
39 if (convertedCircuit.empty()) return false;
40 simulator.SetCircuit(convertedCircuit);
41 return true;
42 }
43
44 std::complex<double> AmplitudeFromZero(const std::vector<bool>& endState)
45 {
46 return simulator.Propagate(endState);
47 }
48
49 std::complex<double> Amplitude(const std::vector<bool>& startState, const std::vector<bool>& endState)
50 {
51 return simulator.Propagate(startState, endState);
52 }
53
54 std::optional<QC::Gates::AppliedGate<>> ConvertGate(const std::shared_ptr<Circuits::IQuantumGate<>>& gate)
55 {
56 if (!gate) return std::nullopt;
57
58 switch (gate->GetGateType())
59 {
60 // single qubit gates
62 {
63 const auto g = std::static_pointer_cast<Circuits::PhaseGate<>>(gate);
64 pgate.SetPhaseShift(g->GetLambda());
65 return QC::Gates::AppliedGate<>(pgate.getRawOperatorMatrix(), gate->GetQubit(0));
66 }
68 return QC::Gates::AppliedGate<>(xgate.getRawOperatorMatrix(), gate->GetQubit(0));
70 return QC::Gates::AppliedGate<>(ygate.getRawOperatorMatrix(), gate->GetQubit(0));
72 return QC::Gates::AppliedGate<>(zgate.getRawOperatorMatrix(), gate->GetQubit(0));
74 return QC::Gates::AppliedGate<>(h.getRawOperatorMatrix(), gate->GetQubit(0));
76 return QC::Gates::AppliedGate<>(sgate.getRawOperatorMatrix(), gate->GetQubit(0));
78 return QC::Gates::AppliedGate<>(sdggate.getRawOperatorMatrix(), gate->GetQubit(0));
80 return QC::Gates::AppliedGate<>(tgate.getRawOperatorMatrix(), gate->GetQubit(0));
82 return QC::Gates::AppliedGate<>(tdggate.getRawOperatorMatrix(), gate->GetQubit(0));
84 return QC::Gates::AppliedGate<>(sxgate.getRawOperatorMatrix(), gate->GetQubit(0));
86 return QC::Gates::AppliedGate<>(sxdaggate.getRawOperatorMatrix(), gate->GetQubit(0));
88 return QC::Gates::AppliedGate<>(k.getRawOperatorMatrix(), gate->GetQubit(0));
90 {
91 const auto g = std::static_pointer_cast<Circuits::RxGate<>>(gate);
92 rxgate.SetTheta(g->GetTheta());
93 return QC::Gates::AppliedGate<>(rxgate.getRawOperatorMatrix(), gate->GetQubit(0));
94 }
96 {
97 const auto g = std::static_pointer_cast<Circuits::RyGate<>>(gate);
98 rygate.SetTheta(g->GetTheta());
99 return QC::Gates::AppliedGate<>(rygate.getRawOperatorMatrix(), gate->GetQubit(0));
100 }
102 {
103 const auto g = std::static_pointer_cast<Circuits::RzGate<>>(gate);
104 rzgate.SetTheta(g->GetTheta());
105 return QC::Gates::AppliedGate<>(rzgate.getRawOperatorMatrix(), gate->GetQubit(0));
106 }
108 {
109 const auto g = std::static_pointer_cast<Circuits::UGate<>>(gate);
110 ugate.SetParams(g->GetTheta(), g->GetPhi(), g->GetLambda(), g->GetGamma());
111 return QC::Gates::AppliedGate<>(ugate.getRawOperatorMatrix(), gate->GetQubit(0));
112 }
113
114 // two qubit gates
116 return QC::Gates::AppliedGate<>(swapgate.getRawOperatorMatrix(), gate->GetQubit(0), gate->GetQubit(1));
118 return QC::Gates::AppliedGate<>(cxgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
120 return QC::Gates::AppliedGate<>(cygate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
122 return QC::Gates::AppliedGate<>(czgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
124 {
125 const auto g = std::static_pointer_cast<Circuits::CPGate<>>(gate);
126 cpgate.SetPhaseShift(g->GetLambda());
127 return QC::Gates::AppliedGate<>(cpgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
128 }
130 {
131 const auto g = std::static_pointer_cast<Circuits::CRxGate<>>(gate);
132 crxgate.SetTheta(g->GetTheta());
133 return QC::Gates::AppliedGate<>(crxgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
134 }
136 {
137 const auto g = std::static_pointer_cast<Circuits::CRyGate<>>(gate);
138 crygate.SetTheta(g->GetTheta());
139 return QC::Gates::AppliedGate<>(crygate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
140 }
142 {
143 const auto g = std::static_pointer_cast<Circuits::CRzGate<>>(gate);
144 crzgate.SetTheta(g->GetTheta());
145 return QC::Gates::AppliedGate<>(crzgate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
146 }
148 return QC::Gates::AppliedGate<>(ch.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
150 return QC::Gates::AppliedGate<>(csx.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
152 return QC::Gates::AppliedGate<>(csxdag.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
154 {
155 const auto g = std::static_pointer_cast<Circuits::CUGate<>>(gate);
156 cugate.SetParams(g->GetTheta(), g->GetPhi(), g->GetLambda(), g->GetGamma());
157 return QC::Gates::AppliedGate<>(cugate.getRawOperatorMatrix(), gate->GetQubit(1), gate->GetQubit(0));
158 }
159
160 // three qubit gates
162 return QC::Gates::AppliedGate<>(ccxgate.getRawOperatorMatrix(), gate->GetQubit(2), gate->GetQubit(1), gate->GetQubit(0));
164 return QC::Gates::AppliedGate<>(cswapgate.getRawOperatorMatrix(), gate->GetQubit(2), gate->GetQubit(1), gate->GetQubit(0));
165
166 default:
167 return std::nullopt;
168 }
169 }
170
171 std::vector<QC::Gates::AppliedGate<>> ConvertCircuit(const std::shared_ptr<Circuits::Circuit<>>& circuit)
172 {
173 std::vector<QC::Gates::AppliedGate<>> result;
174 if (!circuit) return result;
175
176 const auto& operations = circuit->GetOperations();
177 result.reserve(operations.size());
178
179 for (const auto& op : operations)
180 {
181 if (!op || op->GetType() != Circuits::OperationType::kGate)
182 continue;
183
184 const auto& gate = std::static_pointer_cast<Circuits::IQuantumGate<>>(op);
185 if (auto applied = ConvertGate(gate))
186 result.emplace_back(std::move(*applied));
187 }
188
189 return result;
190 }
191
193 const QC::Gates::AppliedGate<>& gate,
194 std::unordered_map<QC::PathIntegral::FastVectorBool, std::complex<double>,
195 QC::PathIntegral::FastVectorBoolHash>& currentAmplitudes)
196 {
197 simulator.PropagateStep(gate, currentAmplitudes);
198 }
199
200 static size_t GetBranchingForQcsimCircuit(const std::vector<QC::Gates::AppliedGate<>>& circuit) {
201 size_t doublings = 0;
202 for (const auto& gate : circuit)
203 if (gate.isBranching()) ++doublings;
204
205 return doublings;
206 }
207
208 size_t GetBranchingForMaestroCircuit(const std::shared_ptr<Circuits::Circuit<>>& circuit) {
209 /*
210 const auto circ = ConvertCircuit(circuit);
211 return GetBranchingForQcsimCircuit(circ);
212 */
213
214 return GetBranchingForMaestroCircuitFromPos(circuit, 0);
215 }
216
218 const std::shared_ptr<Circuits::Circuit<>>& circuit, size_t pos) {
219 if (!circuit) return 0;
220
221 size_t doublings = 0;
222 const auto& operations = circuit->GetOperations();
223 for (size_t i = pos; i < operations.size(); ++i)
224 if (operations[i]->IsBranching()) ++doublings;
225
226 return doublings;
227 }
228
229 double QubitProbability(size_t qubit, bool value = true) {
230 return simulator.QubitProbability(qubit, value);
231 }
232
233 std::complex<double> AmplitudeForOutcome(size_t outcome) {
234 const auto& amplitudes = simulator.GetAmplitudes();
235 if (amplitudes.empty()) return 0.0;
236
237 QC::PathIntegral::FastVectorBool state(
238 amplitudes.begin()->first.size());
239 for (size_t q = 0; q < state.size(); ++q)
240 state.set(q, (outcome >> q) & 1);
241 if (auto it = amplitudes.find(state); it != amplitudes.end())
242 return it->second;
243 return 0.0;
244 }
245
246 double Probability(size_t outcome) {
247 return std::norm(AmplitudeForOutcome(outcome));
248 }
249
250 QC::PathIntegral::FastVectorBool MeasureNoCollapse() {
251 return simulator.MeasureNoCollapse();
252 }
253
254 bool MeasureQubit(size_t qubit) {
255 return simulator.MeasureQubit(qubit);
256 }
257
258 std::unique_ptr<PathIntegralSimulator> Clone() const {
259 auto clone = std::make_unique<PathIntegralSimulator>();
260 clone->simulator = simulator.Clone();
261 return clone;
262 }
263
264 std::unordered_map<QC::PathIntegral::FastVectorBool, std::complex<double>,
265 QC::PathIntegral::FastVectorBoolHash>&
267 {
268 return simulator.GetAmplitudes();
269 }
270
271 void SetStartZeroState(size_t numQubits) {
272 auto& amplitudes = simulator.GetAmplitudes();
273
274 amplitudes.clear();
275 QC::PathIntegral::FastVectorBool zeroState(numQubits);
276 amplitudes[zeroState] = 1.0;
277 }
278
279 double ExpectationValue(const std::string& pauliStringOrig) {
280 // save current amplitudes
281 const auto& currentAmplitudes = simulator.GetAmplitudes();
282 auto amplitudes = currentAmplitudes;
283
284 // now apply the pauli string as gates
285 for (size_t i = 0; i < pauliStringOrig.size(); ++i)
286 {
287 const char c = pauliStringOrig[i];
288 switch (c)
289 {
290 case 'X':
291 [[fallthrough]];
292 case 'x':
293 PropagateStep(QC::Gates::AppliedGate<>(xgate.getRawOperatorMatrix(), i), amplitudes);
294 break;
295 case 'Y':
296 [[fallthrough]];
297 case 'y':
298 PropagateStep(QC::Gates::AppliedGate<>(ygate.getRawOperatorMatrix(), i), amplitudes);
299 break;
300 case 'Z':
301 [[fallthrough]];
302 case 'z':
303 PropagateStep(QC::Gates::AppliedGate<>(zgate.getRawOperatorMatrix(), i), amplitudes);
304 break;
305 default:
306 break;
307 }
308 }
309
310 // use the new amplitudes to calculate the expectation value
311 double expectation = 0.0;
312
313 for (const auto& [state, amplitude] : amplitudes)
314 {
315 if (currentAmplitudes.find(state) == currentAmplitudes.end())
316 continue;
317
318 const auto& oldAmplitude = currentAmplitudes.at(state);
319 expectation += std::real(std::conj(oldAmplitude) * amplitude);
320 }
321
322 return expectation;
323 }
324
325 void ApplyGate(const QC::Gates::AppliedGate<>& gate) {
326 simulator.PropagateStep(gate, simulator.GetAmplitudes());
327 }
328
329 void SaveState() { simulator.SaveAmplitudes(); }
330
331 void RestoreState() { simulator.RestoreAmplitudes(); }
332
333 private:
334 QC::PathIntegral::PathIntegralSimulator simulator;
335
336 QC::Gates::PhaseShiftGate<> pgate;
337 QC::Gates::PauliXGate<> xgate;
338 QC::Gates::PauliYGate<> ygate;
339 QC::Gates::PauliZGate<> zgate;
340 QC::Gates::HadamardGate<> h;
341 // QC::Gates::UGate<> ugate;
342 QC::Gates::SGate<> sgate;
343 QC::Gates::SDGGate<> sdggate;
344 QC::Gates::TGate<> tgate;
345 QC::Gates::TDGGate<> tdggate;
346 QC::Gates::SquareRootNOTGate<> sxgate;
347 QC::Gates::SquareRootNOTDagGate<> sxdaggate;
348 QC::Gates::HyGate<> k;
349 QC::Gates::RxGate<> rxgate;
350 QC::Gates::RyGate<> rygate;
351 QC::Gates::RzGate<> rzgate;
352 QC::Gates::UGate<> ugate;
353 QC::Gates::CNOTGate<> cxgate;
354 QC::Gates::ControlledYGate<> cygate;
355 QC::Gates::ControlledZGate<> czgate;
356 QC::Gates::ControlledPhaseShiftGate<> cpgate;
357 QC::Gates::ControlledRxGate<> crxgate;
358 QC::Gates::ControlledRyGate<> crygate;
359 QC::Gates::ControlledRzGate<> crzgate;
360 QC::Gates::ControlledHadamardGate<> ch;
361 QC::Gates::ControlledSquareRootNOTGate<> csx;
362 QC::Gates::ControlledSquareRootNOTDagGate<> csxdag;
363 QC::Gates::SwapGate<> swapgate;
364 QC::Gates::ToffoliGate<> ccxgate;
365 QC::Gates::FredkinGate<> cswapgate;
366 QC::Gates::ControlledUGate<> cugate;
367 };
368
369}
370
371#endif // _PATH_INTEGRAL_SIMULATOR_H_
372
Circuit class for holding the sequence of operations.
Definition Circuit.h:46
The interface for quantum gates.
std::optional< QC::Gates::AppliedGate<> > ConvertGate(const std::shared_ptr< Circuits::IQuantumGate<> > &gate)
void PropagateStep(const QC::Gates::AppliedGate<> &gate, std::unordered_map< QC::PathIntegral::FastVectorBool, std::complex< double >, QC::PathIntegral::FastVectorBoolHash > &currentAmplitudes)
size_t GetBranchingForMaestroCircuit(const std::shared_ptr< Circuits::Circuit<> > &circuit)
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)
std::unordered_map< QC::PathIntegral::FastVectorBool, std::complex< double >, QC::PathIntegral::FastVectorBoolHash > & Amplitudes()
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 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:28