49 using Map = std::vector<Index>;
50 using Operation = std::shared_ptr<Circuits::IOperation<>>;
61 bool valid =
false, swap =
false;
63 std::vector<std::pair<Index, size_t>> singlesBefore;
75 std::vector<Gate> gates;
76 std::vector<Index> operationToGate;
90 : n_(n), operatorChain_(operatorChain), policy_(policy), operations_(ValidateOperations(n, std::move(operations))), gates_(operations_->gates),
91 operationToGate_(operations_->operationToGate)
94 throw std::invalid_argument(
"Invalid bounded routing configuration");
97 maximum_.resize(n - 1);
98 const double d = operatorChain ? 4. : 2.;
99 const double limit =
static_cast<double>(std::numeric_limits<Index>::max());
100 const double physical = cap > 0 ?
static_cast<double>(cap) : limit;
101 for (
size_t b = 0; b + 1 < n; ++b)
102 maximum_[b] = CutMaximum(std::min(b + 1, n - b - 1), d, physical);
112 if (n < 2 || n >
static_cast<size_t>(std::numeric_limits<Index>::max()))
113 throw std::invalid_argument(
"Invalid bounded routing qubit count");
114 auto prepared = std::make_shared<PreparedOperations>();
115 prepared->qubits = n;
116 prepared->operationToGate.assign(operations.size(), -1);
117 std::vector<size_t> singles(n, 0);
118 std::vector<Index> touchedSingles;
119 for (
size_t i = 0; i < operations.size(); ++i)
121 const auto &operation = operations[i];
125 const auto qubits = operation->AffectedQubits();
126 const bool validQubits = std::all_of(qubits.begin(), qubits.end(), [n](
auto q) { return q < n; });
127 if (validQubits && qubits.size() == 1)
131 if (!singles[qubits[0]] && touchedSingles.size() <= n / 2)
132 touchedSingles.push_back(
static_cast<Index>(qubits[0]));
133 ++singles[qubits[0]];
136 if (validQubits && qubits.size() == 2 && qubits[0] != qubits[1])
141 const auto info = std::dynamic_pointer_cast<RoutingGateInfo>(operation);
142 const auto gate = std::dynamic_pointer_cast<Circuits::IQuantumGate<>>(operation);
143 item.swap = (info && info->isSwap) ||
145 item.rank = info ? info->schmidtRank : gate ? GateSchmidtRank(*gate) : 4.;
146 prepared->operationToGate[i] =
static_cast<Index>(prepared->gates.size());
149 const auto emitSingle = [&item, &singles](
Index q) {
150 item.singlesBefore.emplace_back(q, singles[q]);
156 if (touchedSingles.size() > n / 2)
158 for (
size_t q = 0; q < n; ++q)
160 emitSingle(
static_cast<Index>(q));
164 std::sort(touchedSingles.begin(), touchedSingles.end());
165 for (
const auto q : touchedSingles)
168 touchedSingles.clear();
169 prepared->gates.push_back(std::move(item));
175 template <
class Bond>
Index Select(
const Map &map,
const Bond *bonds,
size_t bondCount,
size_t operationIndex)
177 const Index entry = operationIndex < operationToGate_.size() ? operationToGate_[operationIndex] : -1;
178 if (entry < 0 || !gates_[entry].valid || gates_[entry].swap || !Seed(map, bonds, bondCount))
180 const auto &gate = gates_[entry];
181 const Index low = std::min(map[gate.a], map[gate.b]);
182 const Index high = std::max(map[gate.a], map[gate.b]);
186 const auto candidates = CandidatesFor(entry);
187 const size_t horizon = policy_ ==
Policy::Work4 ? 4 : CommonHorizon(entry, candidates.count);
188 Index chosen = candidates.positions[0];
189 double best = std::numeric_limits<double>::infinity();
191 for (
size_t i = 0; i < candidates.count; ++i)
193 const double score = Evaluate(entry, candidates.positions[i], horizon, best);
197 chosen = candidates.positions[i];
203 template <
class Bond>
Index Select(
const Map &map,
const std::vector<Bond> &bonds,
size_t operationIndex)
205 return Select(map, bonds.data(), bonds.size(), operationIndex);
212 std::vector<double> bonds;
217 std::array<Index, 5> positions{};
225 const std::vector<Gate> &gates_;
226 const std::vector<Index> &operationToGate_;
227 std::vector<double> maximum_;
228 State initial_, scratch_;
234 if (n < 2 || n >
static_cast<size_t>(std::numeric_limits<Index>::max()) || !operations || operations->qubits != n)
235 throw std::invalid_argument(
"Incompatible bounded routing operation table");
239 static double Cube(
double x)
244 static double CutMaximum(
size_t sites,
double d,
double cap)
247 for (
size_t k = 0; k < sites && result < cap; ++k)
248 result = std::min(cap, result * d);
260 static bool ExactSwapMatrix(
const Eigen::MatrixXcd &matrix)
262 if (matrix.rows() != 4 || matrix.cols() != 4)
264 for (
int row = 0; row < 4; ++row)
265 for (
int col = 0; col < 4; ++col)
266 if (matrix(row, col) != std::complex<double>(row == (((col & 1) << 1) | (col >> 1)) ? 1. : 0., 0.))
271 void Allocate(State &state)
273 state.map.resize(n_);
274 state.inverse.resize(n_);
275 state.bonds.resize(n_ - 1);
278 template <
class Bond>
bool Seed(
const Map &map,
const Bond *bonds,
size_t bondCount)
280 if (map.size() != n_ || bondCount != n_ - 1 || bonds ==
nullptr)
282 std::fill(initial_.inverse.begin(), initial_.inverse.end(), -1);
283 for (
size_t q = 0; q < n_; ++q)
285 if (map[q] < 0 ||
static_cast<size_t>(map[q]) >= n_ || initial_.inverse[map[q]] != -1)
287 initial_.map[q] = map[q];
288 initial_.inverse[map[q]] =
static_cast<Index>(q);
290 for (
size_t b = 0; b < bondCount; ++b)
292 const double rank =
static_cast<double>(bonds[b]);
293 if (!std::isfinite(rank) || rank < 1.)
295 initial_.bonds[b] = rank;
302 static Index LocalMeeting(
const std::vector<double> &dims,
Index low,
Index high,
bool operatorChain)
304 const Index span = high - low;
307 std::array<double, 130> bonds{};
308 std::array<double, 128> prefixes{}, suffixes{};
309 std::vector<double> heap;
310 double *b = bonds.data(), *p = prefixes.data(), *s = suffixes.data();
313 heap.assign(
static_cast<size_t>(3 * span + 2), 0.);
318 b[0] = low ? dims[low - 1] : 1.;
319 for (
Index i = 0; i < span; ++i)
320 b[i + 1] = dims[low + i];
321 b[span + 1] = high < static_cast<Index>(dims.size()) ? dims[high] : 1.;
322 const double d = operatorChain ? 4. : 2.;
323 const double e1 = std::max(b[0], b[1]), e2 = std::max(b[span], b[span + 1]);
324 for (
Index i = 0; i < span - 1; ++i)
325 p[i + 1] = p[i] + std::max(0., Cube(std::min(e1, d * b[i + 1])) - Cube(b[i + 1]));
326 for (
Index i = span - 1; i > 0; --i)
327 s[i - 1] = s[i] + std::max(0., Cube(std::min(e2, d * b[i + 1])) - Cube(b[i + 1]));
329 double best = std::numeric_limits<double>::infinity();
330 for (
Index i = 0; i < span; ++i)
332 const double tie = 1e-6 * (b[span] * b[span + 1] > b[0] * b[1] ? span - 1 - i : i);
333 const double score = Cube(b[i + 1]) + .5 * (p[i] + s[i]) + .05 * b[i] * b[i + 1] * b[i + 2] + tie;
343 CandidateSet CandidatesFor(
Index entry)
const
346 const auto &gate = gates_[entry];
347 const Index low = std::min(initial_.map[gate.a], initial_.map[gate.b]);
348 const Index high = std::max(initial_.map[gate.a], initial_.map[gate.b]);
349 const auto add = [&out](
Index position) {
350 for (
size_t i = 0; i < out.count; ++i)
351 if (out.positions[i] == position)
353 out.positions[out.count++] = position;
356 add(LocalMeeting(initial_.bonds, low, high, operatorChain_));
359 add(low + (high - low) / 2);
361 for (
Index b = low + 1; b < high; ++b)
362 if (initial_.bonds[b] < initial_.bonds[minimum])
368 size_t CommonHorizon(
Index entry,
size_t candidates)
const
370 size_t steps = 0, upperBound = 0;
372 for (
size_t step = 0; step < 24 && static_cast<size_t>(entry) + step < gates_.size(); ++step)
374 const auto &gate = gates_[
static_cast<size_t>(entry) + step];
379 size_t updates = gate.swap ? 1 : n_ - 1;
380 if (step == 0 && !gate.swap)
381 updates =
static_cast<size_t>(std::abs(initial_.map[gate.a] - initial_.map[gate.b]));
382 const size_t singles = step ? gate.singlesBefore.size() : 0;
383 if (updates > perCandidateBudget - upperBound || singles > perCandidateBudget - upperBound - updates)
385 upperBound += updates + singles;
391 double Grow(
double left,
double middle,
double right,
double maximum,
bool swap,
double gateRank)
const
393 const double d = operatorChain_ ? 4. : 2., rank = operatorChain_ ? gateRank * gateRank : gateRank;
396 if (left == 1. && middle == 1. && right == 1.)
398 return std::max(1., std::min({d * left, d * right, rank * middle, maximum}));
400 const auto at = [&](
double r) {
return std::max(1., std::min(.7 * std::min(d * std::min(left, right), middle * r), maximum)); };
401 return rank >= 2. ? at(rank) : middle + (std::max(rank, 1.) - 1.) * (at(2.) - middle);
404 double StepWork(
double left,
double middle,
double right,
double post,
bool swap)
const
406 const double d = operatorChain_ ? 4. : 2.;
407 if (swap && left == 1. && middle == 1. && right == 1.)
409 const double m = d * left, n = d * right;
410 return m * n * (middle + std::min(m, n) + post);
415 std::swap(scratch_.map[a], scratch_.map[b]);
416 scratch_.inverse[scratch_.map[a]] = a;
417 scratch_.inverse[scratch_.map[b]] = b;
420 void Adjacent(
Index bond,
bool swap,
double gateRank,
double &work)
422 const double left = bond ? scratch_.bonds[bond - 1] : 1., middle = scratch_.bonds[bond];
423 const double right = bond + 1 <
static_cast<Index>(n_ - 1) ? scratch_.bonds[bond + 1] : 1.;
424 const double post = Grow(left, middle, right, maximum_[bond], swap, gateRank);
425 work += StepWork(left, middle, right, post, swap);
426 scratch_.bonds[bond] = post;
428 Relabel(scratch_.inverse[bond], scratch_.inverse[bond + 1]);
431 double Evaluate(
Index entry,
Index meeting,
size_t horizon,
double incumbent)
435 const double d = operatorChain_ ? 4. : 2.;
436 for (
size_t step = 0; step < horizon && static_cast<size_t>(entry) + step < gates_.size(); ++step)
438 const auto &gate = gates_[
static_cast<size_t>(entry) + step];
442 for (
const auto &single : gate.singlesBefore)
444 const Index position = scratch_.map[single.first];
445 const double left = position ? scratch_.bonds[position - 1] : 1.;
446 const double right = position < static_cast<Index>(n_ - 1) ? scratch_.bonds[position] : 1.;
447 work +=
static_cast<double>(single.second) * d * d * left * right;
451 Relabel(gate.a, gate.b);
454 const Index low = std::min(scratch_.map[gate.a], scratch_.map[gate.b]);
455 const Index high = std::max(scratch_.map[gate.a], scratch_.map[gate.b]);
456 const Index meet = step == 0 ? meeting : LocalMeeting(scratch_.bonds, low, high, operatorChain_);
459 for (
Index bond = low; bond < meet; ++bond)
460 Adjacent(bond,
true, 4., work);
461 for (
Index bond = high - 1; bond > meet; --bond)
462 Adjacent(bond,
true, 4., work);
463 Adjacent(meet,
false, gate.rank, work);
466 if (work >= incumbent)