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