Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
TensorChainRouting.h
Go to the documentation of this file.
1#pragma once
2#include "MPSDummySimulator.h"
3#include "RoutingConfig.h"
4
6{
7// CPU and GPU adapters supply native maps and bonds; all history and planning
8// policy lives here so a boundary has identical semantics on both backends.
9template <class Backend> class TensorChainRouting : public Backend
10{
11 protected:
12 using Backend::configuration;
13 using Backend::nrQubits;
14 using Backend::RefreshRoutingCallback;
15 using Backend::upcomingGateIndex;
16 using Backend::upcomingGates;
17
18 explicit TensorChainRouting(bool isOperatorChain, bool relabelSwaps = false) : operatorChain(isOperatorChain), swapGatesRelabelQubits(relabelSwaps)
19 {
20 }
21
22 // Only an exact SWAP can use the logical relabelling path. Approximate SWAPs
23 // still update tensors and must be recorded as ordinary matrix gates.
24 static bool IsExactSwapMatrix(const Eigen::Matrix4cd &gate)
25 {
26 for (Eigen::Index row = 0; row < 4; ++row)
27 for (Eigen::Index col = 0; col < 4; ++col)
28 {
29 const Eigen::Index swapped = ((col & 1) << 1) | (col >> 1);
30 if (gate(row, col) != std::complex<double>(row == swapped ? 1. : 0., 0.))
31 return false;
32 }
33 return true;
34 }
35
36 virtual std::vector<long long> CurrentRoutingMap() const = 0;
37 virtual std::vector<double> CurrentBondDimensions() const = 0;
38 const bool operatorChain;
40
41 public:
42 bool SwapGatesRelabelQubits() const override
43 {
45 }
46
47 bool IsRoutingLookaheadEnabled() const override
48 {
49 return useOptimalMeetingPosition && lookaheadDepth > 0 && lookaheadDepth != std::numeric_limits<int>::max() && nrQubits > 1 &&
50 CurrentRoutingMap().size() == nrQubits;
51 }
52
53 void SetLookaheadDepth(int depth) override
54 {
55 lookaheadDepth = depth;
57 RefreshRoutingCallback();
58 }
59
60 void SetLookaheadDepthWithHeuristic(int depth) override
61 {
63 if (lookaheadDepth < depth)
64 SetLookaheadDepth(depth);
65 }
66
67 double getGrowthFactorSwap() const override
68 {
69 return growthFactorSwap;
70 }
71
72 double getGrowthFactorGate() const override
73 {
74 return growthFactorGate;
75 }
76
77 void setGrowthFactorSwap(double factor) override
78 {
79 growthFactorSwap = factor;
80 if (dummySim)
81 dummySim->setGrowthFactorSwap(factor);
82 }
83
84 void setGrowthFactorGate(double factor) override
85 {
86 growthFactorGate = factor;
87 if (dummySim)
88 dummySim->setGrowthFactorGate(factor);
89 }
90
91 size_t GetCurrentMaxBondDimension() const override
92 {
93 return curMaxBondDim;
94 }
95
96 protected:
97 const char *MaxBondDimensionConfigKey() const
98 {
99 return operatorChain && configuration.IsSet("matrix_product_operator_max_bond_dimension") ? "matrix_product_operator_max_bond_dimension"
100 : "matrix_product_state_max_bond_dimension";
101 }
102
107
112
113 // Called when configuration, register size or lookahead changes, never on
114 // the gate hot path. Invalidation retains memoized plans: their keys include
115 // the full map, bond estimates and upcoming gate window.
117 {
118 const auto cap = configuration.GetConfigurationAsInt(MaxBondDimensionConfigKey());
119 const bool enabled = RoutingConfig::VirtualBondsEnabled(configuration);
120 const double weight = RoutingConfig::TruncationWeight(configuration);
121 const bool tracking = enabled && useOptimalMeetingPosition && lookaheadDepth > 0 && lookaheadDepth != std::numeric_limits<int>::max() && nrQubits > 1 &&
123 if (cap != physicalBondCap || enabled != virtualBondsEnabled || tracking != trackVirtualBonds || weight != truncationWeight)
125 physicalBondCap = cap;
126 virtualBondsEnabled = enabled;
127 truncationWeight = weight;
128 trackVirtualBonds = tracking;
129 if (dummySim)
131 }
132
134 {
135 return trackVirtualBonds;
136 }
137
139 {
140 dummyHistoryValid = false;
141 }
142
144 {
146 return;
147 try
148 {
149 if (dummySim->getQubitsMap() != CurrentRoutingMap())
151 }
152 catch (...)
153 {
155 }
156 }
157
158 void SetSwapGatesRelabelQubits(bool enabled)
159 {
160 if (swapGatesRelabelQubits == enabled)
161 return;
162 swapGatesRelabelQubits = enabled;
164 if (dummySim)
166 }
167
169 {
170 dummySim->SetSwapGatesRelabelQubits(swapGatesRelabelQubits);
171 dummySim->SetMaxBondDimension(EffectiveLookaheadMaxBondDimension(dummySim->getNrQubits()));
173 dummySim->setGrowthFactorGate(growthFactorGate);
174 dummySim->setGrowthFactorSwap(growthFactorSwap);
175 }
176
177 void EnsureDummySimulator(size_t nQ)
178 {
179 if (!dummySim || dummySim->getNrQubits() != nQ)
180 {
181 dummySim = std::make_unique<MPSDummySimulator>(nQ, operatorChain);
184 }
185 }
186
187 void SeedDummyHistory(const std::vector<long long> &map)
188 {
190 const auto bonds = CurrentBondDimensions();
191 if (map.size() != nrQubits || bonds.size() + 1 != nrQubits)
192 return;
193 dummySim->SetRoutingMap(map);
194 dummySim->SetCurrentBondDimensions(bonds);
195 dummyHistoryValid = true;
196 }
197
198 template <class Bond> long long PlanMeetingPosition(const Bond *bonds, size_t count)
199 {
200 if (!useOptimalMeetingPosition || lookaheadDepth <= 0 || lookaheadDepth == std::numeric_limits<int>::max() || decompositionDepth || !bonds ||
201 nrQubits <= 1 || count + 1 != nrQubits || upcomingGateIndex < 0 || static_cast<size_t>(upcomingGateIndex) >= upcomingGates.size())
202 return -1;
203 const auto &op = upcomingGates[upcomingGateIndex];
204 if (!op || op->GetType() != Circuits::OperationType::kGate)
205 return -1;
206 const auto qubits = op->AffectedQubits();
207 if (qubits.size() != 2 || qubits[0] >= nrQubits || qubits[1] >= nrQubits || qubits[0] == qubits[1])
208 return -1;
209 const auto map = CurrentRoutingMap();
210 if (map.size() != nrQubits)
211 return -1;
212 EnsureDummySimulator(nrQubits);
213 std::vector<double> estimates(bonds, bonds + count);
214 if (trackVirtualBonds && dummyHistoryValid && dummySim->getQubitsMap() == map)
215 {
216 const auto &previous = dummySim->getCurrentBondDimensions();
217 for (size_t i = 0; i < count; ++i)
218 if (bonds[i] >= physicalBondCap)
219 estimates[i] = std::max(estimates[i], previous[i]);
220 }
221 dummySim->SetRoutingMap(map);
222 dummySim->SetCurrentBondDimensions(estimates);
223 dummySim->setTotalSwappingCost(0.);
225 double bestCost = std::numeric_limits<double>::infinity();
226 const auto position =
227 dummySim->SelectMeetingPositionAdaptive(upcomingGates, upcomingGateIndex, lookaheadDepth, lookaheadDepthWithHeuristic, 0., bestCost);
228 if (position < std::min(map[qubits[0]], map[qubits[1]]) || position >= std::max(map[qubits[0]], map[qubits[1]]))
229 return -1;
231 dummySim->SwapQubitsToPosition(qubits[0], qubits[1], position);
232 return position;
233 }
234
235 // Effective Schmidt rank (EffectiveSchmidtRank) of a QCSim gate object.
236 template <class Gate> static double NativeGateSchmidtRank(const Gate &gate)
237 {
238 return gate.isSwapGate() ? 4. : EffectiveSchmidtRank(gate.getRawOperatorMatrix());
239 }
240
241 // schmidtRank: the executed gate's effective Schmidt rank (2 for CX, 4 for
242 // SWAP or a generic gate, near 1 for a small-angle controlled rotation).
243 void RecordExecutedTwoQubitGate(Types::qubit_t q0, Types::qubit_t q1, bool isSwap, double schmidtRank) noexcept
244 {
246 return;
247 // Recording is advisory. Never throw after a successful native gate:
248 // observers must still advance the logical operation index.
249 try
250 {
251 if (q0 >= nrQubits || q1 >= nrQubits || q0 == q1)
252 return InvalidateDummyHistory();
253 const auto map = CurrentRoutingMap();
254 if (map.size() != nrQubits)
255 return InvalidateDummyHistory();
256 EnsureDummySimulator(nrQubits);
257 if (isSwap && swapGatesRelabelQubits)
258 {
260 return SeedDummyHistory(map);
261 dummySim->RelabelQubits(q0, q1);
262 if (dummySim->getQubitsMap() != map)
263 SeedDummyHistory(map);
264 return; // The tensors and their bond estimates have not changed.
265 }
266 if (std::abs(map[q0] - map[q1]) != 1)
267 return InvalidateDummyHistory();
269 return SeedDummyHistory(map); // Native bonds already include this gate.
270 const auto &predicted = dummySim->getQubitsMap();
271 const auto position = std::min(map[q0], map[q1]);
272 if (std::abs(predicted[q0] - predicted[q1]) > 1 && position >= std::min(predicted[q0], predicted[q1]) &&
273 position < std::max(predicted[q0], predicted[q1]))
274 dummySim->SwapQubitsToPosition(q0, q1, position);
275 // Never attach estimates to different chain cuts by overwriting
276 // the permutation. Unknown native reorders require a fresh seed.
277 if (dummySim->getQubitsMap() != map)
278 return SeedDummyHistory(map);
279 // The rank describes the actual native call: a lone fused CX keeps
280 // rank 2; a merged matrix is ranked from its own entries.
281 dummySim->RecordAdjacentTwoQubitGate(position, isSwap, schmidtRank);
282 }
283 catch (...)
284 {
286 }
287 }
288
289 std::vector<double> CaptureDummyHistory() const
290 {
291 return trackVirtualBonds && dummyHistoryValid && dummySim ? dummySim->getCurrentBondDimensions() : std::vector<double>{};
292 }
293
294 void RestoreDummyHistory(const std::vector<double> &bonds)
295 {
297 if (!trackVirtualBonds || bonds.empty() || bonds.size() + 1 != nrQubits)
298 return;
299 EnsureDummySimulator(nrQubits);
300 const auto map = CurrentRoutingMap();
301 if (map.size() != nrQubits)
302 return;
303 dummySim->SetRoutingMap(map);
304 dummySim->SetCurrentBondDimensions(bonds);
305 dummyHistoryValid = true;
306 }
307
312
317
319 {
322 return;
323 const auto map = CurrentRoutingMap();
324 if (map.size() != nrQubits)
325 return;
326 dummySim->SetRoutingMap(map);
327 dummySim->setTotalSwappingCost(0.);
328 dummySim->ResetBondDimensions();
329 dummyHistoryValid = true;
330 }
331
333 {
334 dummySim.reset();
335 savedDummyBondDims.clear();
337 }
338
339 // Decompositions must advance the public circuit counter once. Their
340 // primitive gates use native routing because the lookahead entry describes
341 // the enclosing three-qubit operation, not a particular primitive.
343 {
344 public:
345 explicit DecompositionScope(TensorChainRouting &owner) : owner(owner), notificationsEnabled(owner.NotificationsEnabled())
346 {
347 ++owner.decompositionDepth;
348 owner.DontNotify();
349 }
350
352 {
353 if (notificationsEnabled)
354 owner.Notify();
355 else
356 owner.DontNotify();
357 --owner.decompositionDepth;
358 }
359
360 private:
361 TensorChainRouting &owner;
362 const bool notificationsEnabled;
363 };
364
368 std::unique_ptr<MPSDummySimulator> dummySim;
369 std::vector<double> savedDummyBondDims;
370 bool dummyHistoryValid = false;
371 size_t curMaxBondDim = 0;
374 double truncationWeight = 1.;
375 unsigned decompositionDepth = 0;
376};
377} // namespace Simulators::Private
static IndexType EffectiveLookaheadMaxBondDimension(size_t nrQubits, bool operatorChain, IndexType cap)
void SeedDummyHistory(const std::vector< long long > &map)
static double NativeGateSchmidtRank(const Gate &gate)
void RecordExecutedTwoQubitGate(Types::qubit_t q0, Types::qubit_t q1, bool isSwap, double schmidtRank) noexcept
void setGrowthFactorSwap(double factor) override
virtual std::vector< long long > CurrentRoutingMap() const =0
MPSDummySimulator::IndexType physicalBondCap
void SetLookaheadDepthWithHeuristic(int depth) override
virtual std::vector< double > CurrentBondDimensions() const =0
long long PlanMeetingPosition(const Bond *bonds, size_t count)
MPSDummySimulator::IndexType EffectiveLookaheadMaxBondDimension(size_t nQ, MPSDummySimulator::IndexType cap) const
void setGrowthFactorGate(double factor) override
TensorChainRouting(bool isOperatorChain, bool relabelSwaps=false)
static bool IsExactSwapMatrix(const Eigen::Matrix4cd &gate)
std::unique_ptr< MPSDummySimulator > dummySim
void RestoreDummyHistory(const std::vector< double > &bonds)
size_t GetCurrentMaxBondDimension() const override
std::vector< double > CaptureDummyHistory() const
MPSDummySimulator::IndexType EffectiveLookaheadMaxBondDimension(size_t nQ) const
@ kGate
the usual quantum gate, result stays in simulator's state
Definition Operations.h:30
bool VirtualBondsEnabled(const Config &config)
double TruncationWeight(const Config &config)
double EffectiveSchmidtRank(const Eigen::MatrixXcd &u)
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:23