45 using MatrixClass = QC::TensorNetworks::MPSSimulatorInterface::MatrixClass;
46 using GateClass = QC::TensorNetworks::MPSSimulatorInterface::GateClass;
50 explicit MPSDummySimulator(
size_t N,
bool operatorChain =
false) : nrQubits(N), maxVirtualExtent(0)
52 physExtent = operatorChain ? 4 : 2;
57 minLayoutGain = operatorChain ? 1. : 4.;
63 std::unique_ptr<MPSDummySimulator>
Clone()
const
65 auto clone = std::unique_ptr<MPSDummySimulator>(
new MPSDummySimulator(nrQubits, LightweightInitTag{}));
66 clone->qubitsMap = qubitsMap;
67 clone->qubitsMapInv = qubitsMapInv;
68 clone->physExtent = physExtent;
69 clone->maxVirtualExtent = maxVirtualExtent;
70 clone->runtimeBondCap = runtimeBondCap;
71 clone->truncationWeight = truncationWeight;
72 clone->swapGatesRelabelQubits = swapGatesRelabelQubits;
73 clone->bondCost = bondCost;
74 clone->maxBondDim = maxBondDim;
75 clone->currentBondDim = currentBondDim;
77 clone->totalSwappingCost = totalSwappingCost;
79 clone->growthFactorSwap = growthFactorSwap;
80 clone->growthFactorGate = growthFactorGate;
81 clone->minLayoutGain = minLayoutGain;
93 return physExtent == 4;
98 if (swapGatesRelabelQubits == enabled)
100 swapGatesRelabelQubits = enabled;
108 if (q1 < 0 || q2 < 0 || q1 >=
static_cast<IndexType>(nrQubits) || q2 >=
static_cast<IndexType>(nrQubits))
109 throw std::invalid_argument(
"Qubit index out of bounds");
110 std::swap(qubitsMap[q1], qubitsMap[q2]);
111 qubitsMapInv[qubitsMap[q1]] = q1;
112 qubitsMapInv[qubitsMap[q2]] = q2;
117 return growthFactorSwap;
122 return growthFactorGate;
135 const auto limit = std::numeric_limits<IndexType>::max();
136 const auto headroom = cap > limit / 4 ? limit : 4 * cap;
137 const size_t half = nrQubits / 2;
138 const size_t safeExponent = operatorChain ? 31 : 62;
139 const auto physical = half > safeExponent ? limit :
static_cast<IndexType>(1ULL << (half * (operatorChain ? 2 : 1)));
140 return std::max(cap, std::min(headroom, physical));
158 const double runtimeDim = runtimeBondCap > 0 ? std::min(dimension,
static_cast<double>(runtimeBondCap)) : dimension;
159 const double runtime = runtimeDim * runtimeDim * runtimeDim;
160 const double overflow = runtimeBondCap > 0 ? std::max(0., dimension / runtimeBondCap - 1.) : 0.;
161 return runtime + truncationWeight * runtime * overflow;
166 if (cap < 0 || !std::isfinite(weight) || weight < 0.)
167 throw std::invalid_argument(
"Invalid routing cost model");
168 if (runtimeBondCap == cap && truncationWeight == weight)
170 runtimeBondCap = cap;
171 truncationWeight = weight;
172 for (
size_t i = 0; i < bondCost.size(); ++i)
173 bondCost[i] =
BondCost(currentBondDim[i]);
179 for (
auto &entry : memoCache)
185 if (growthFactorSwap == factor)
187 growthFactorSwap = factor;
193 if (growthFactorGate == factor)
195 growthFactorGate = factor;
204 return minLayoutGain;
210 throw std::invalid_argument(
"Minimum layout gain must be at least 1");
211 minLayoutGain = gain;
224 currentBondDim.clear();
228 currentBondDim.assign(nrQubits - 1, 1.0);
229 bondCost.assign(nrQubits - 1, 1.0);
236 if (maxVirtualExtent != val)
238 maxVirtualExtent = val;
244 const double untruncatedMaxExtent = std::pow(physExtent, nrQubits / 2);
247 if (untruncatedMaxExtent >= std::numeric_limits<IndexType>::max() || std::isnan(untruncatedMaxExtent) || std::isinf(untruncatedMaxExtent))
248 maxVirtualExtentLimit = std::numeric_limits<IndexType>::max() - 1;
249 else if (untruncatedMaxExtent < 2)
250 maxVirtualExtentLimit = 2;
252 const IndexType resolvedMaxVirtualExtent = val == 0 ? maxVirtualExtentLimit : std::min(val, maxVirtualExtentLimit);
253 const bool alreadyInitialized = (maxBondDim.size() + 1 == nrQubits && currentBondDim.size() + 1 == nrQubits && bondCost.size() + 1 == nrQubits);
254 if (alreadyInitialized && maxVirtualExtent == resolvedMaxVirtualExtent)
257 maxVirtualExtent = resolvedMaxVirtualExtent;
260 maxBondDim.resize(nrQubits - 1);
261 if (!alreadyInitialized)
263 currentBondDim.assign(nrQubits - 1, 1.0);
264 bondCost.assign(nrQubits - 1, 1.0);
269 for (
IndexType i = 0; i < static_cast<IndexType>(nrQubits); ++i)
271 double maxExtent1 = std::pow((
double)physExtent, (
double)i + 1.);
272 double maxExtent2 = std::pow((
double)physExtent, (
double)nrQubits - i - 1.);
274 if (maxExtent1 >= (
double)std::numeric_limits<size_t>::max() || std::isnan(maxExtent1) || std::isinf(maxExtent1))
275 maxExtent1 = (double)std::numeric_limits<size_t>::max() - 1;
276 else if (maxExtent1 < 1)
279 if (maxExtent2 > (
double)std::numeric_limits<size_t>::max() || std::isnan(maxExtent2) || std::isinf(maxExtent2))
280 maxExtent2 = (double)std::numeric_limits<size_t>::max() - 1;
281 else if (maxExtent2 < 1)
284 size_t maxRightExtent = (size_t)std::min<double>({maxExtent1, maxExtent2, (double)maxVirtualExtent});
285 if (maxRightExtent < 2)
288 if (i <
static_cast<IndexType>(nrQubits) - 1)
290 maxBondDim[i] =
static_cast<double>(maxRightExtent);
291 if (alreadyInitialized)
293 currentBondDim[i] = std::clamp(currentBondDim[i], 1.0, maxBondDim[i]);
294 bondCost[i] =
BondCost(currentBondDim[i]);
302 std::cout <<
"Qubits map: ";
303 for (
int q = 0; q < static_cast<int>(qubitsMap.size()); ++q)
304 std::cout << q <<
"->" << qubitsMap[q] <<
" ";
305 std::cout << std::endl;
308 void ApplyGate(
const QC::Gates::AppliedGate<MatrixClass> &gate)
310 ApplyGate(gate, gate.getQubit1(), gate.getQubit2());
317 const auto qbits = gate->AffectedQubits();
319 if (qbits.size() == 1)
324 else if (qbits.size() == 2)
326 static const QC::Gates::SwapGate<MatrixClass> dummySwapGate;
328 const auto gateptr = std::dynamic_pointer_cast<Circuits::IQuantumGate<>>(gate);
329 const auto info = std::dynamic_pointer_cast<RoutingGateInfo>(gate);
331 ApplyTwoQubitFastGate(qbits[0], qbits[1], info->isSwap, info->schmidtRank);
333 ApplyGate(dummySwapGate, qbits[0], qbits[1]);
339 static const QC::Gates::CNOTGate<MatrixClass> dummyControlled2qGate;
341 const auto gateptr = std::static_pointer_cast<Circuits::IQuantumGate<>>(gate);
343 const size_t q1 = qbits[0];
344 const size_t q2 = qbits[1];
345 const size_t q3 = qbits[2];
348 ApplyGate(dummyControlled2qGate, q3, q2);
349 ApplyGate(dummyControlled2qGate, q2, q1);
350 ApplyGate(dummyControlled2qGate, q3, q2);
351 ApplyGate(dummyControlled2qGate, q2, q1);
352 ApplyGate(dummyControlled2qGate, q3, q1);
356 ApplyGate(dummyControlled2qGate, q2, q3);
357 ApplyGate(dummyControlled2qGate, q3, q2);
358 ApplyGate(dummyControlled2qGate, q2, q1);
359 ApplyGate(dummyControlled2qGate, q3, q2);
360 ApplyGate(dummyControlled2qGate, q2, q1);
361 ApplyGate(dummyControlled2qGate, q3, q1);
362 ApplyGate(dummyControlled2qGate, q2, q3);
370 throw std::invalid_argument(
"Qubit index out of bounds");
372 throw std::invalid_argument(
"Qubit index out of bounds");
377 if (gate.getQubitsNumber() > 1)
379 if (gate.getQubitsNumber() == 2 && gate.isSwapGate() && swapGatesRelabelQubits)
383 ApplyTwoQubitGate(qubit, controllingQubit1, gate.isControlled() ? 2. : 4., gate.isSwapGate());
400 throw std::invalid_argument(
"Qubit index out of bounds");
402 throw std::invalid_argument(
"Qubit index out of bounds");
404 IndexType qubit2 = qubitsMap[controllingQubit1];
406 if (abs(qubit1 - qubit2) > 1)
408 SwapQubits(qubit, controllingQubit1);
409 qubit1 = qubitsMap[qubit];
410 qubit2 = qubitsMap[controllingQubit1];
411 assert(abs(qubit1 - qubit2) == 1);
426 if (bond < 0 || bond >=
static_cast<IndexType>(currentBondDim.size()))
428 totalSwappingCost += bondCost[bond];
429 growBondDimension(bond, isSwapGate, schmidtRank);
433 void ApplyGates(
const std::vector<QC::Gates::AppliedGate<MatrixClass>> &gates)
435 for (
const auto &gate : gates)
441 for (
const auto &gate : gates)
447 qubitsMap = actualMap;
448 for (
size_t i = 0; i < actualMap.size(); ++i)
449 qubitsMapInv[actualMap[i]] = i;
455 totalSwappingCost = 0;
461 assert(dims.size() == currentBondDim.size());
462 for (
size_t i = 0; i < dims.size(); ++i)
463 currentBondDim[i] = std::min(dims[i], maxBondDim[i]);
466 for (
size_t i = 0; i < currentBondDim.size(); ++i)
467 bondCost[i] =
BondCost(currentBondDim[i]);
472 return currentBondDim;
492 totalSwappingCost = cost;
497 return totalSwappingCost;
517 long long int currentGateIndex,
int lookaheadDepth,
int lookaheadDepthWithHeuristic,
double currentCost,
double &bestCost,
518 bool useSameDummy =
false)
520 const size_t maxNeeded =
static_cast<size_t>(std::max(0, lookaheadDepth)) + 10;
521 std::array<FastGateOp, 128> stackWindow;
522 std::vector<FastGateOp> heapWindow;
523 FastGateOp *windowPtr = stackWindow.data();
524 if (maxNeeded > stackWindow.size())
526 heapWindow.resize(maxNeeded);
527 windowPtr = heapWindow.data();
529 const size_t windowSize = ExtractFastGateWindow(upcomingGates, currentGateIndex, maxNeeded, windowPtr);
532 if (currentCost < bestCost)
533 bestCost = currentCost;
537 EvaluateMeetingPositionCostFast(meetPosition, windowPtr, windowSize, 0, lookaheadDepth, lookaheadDepthWithHeuristic, currentCost, bestCost,
547 int lookaheadDepth,
int lookaheadDepthWithHeuristic,
double currentCost,
double &bestCost)
549 if (currentGateIndex < 0 || currentGateIndex >=
static_cast<long long int>(upcomingGates.size()))
551 if (currentCost < bestCost)
552 bestCost = currentCost;
556 const size_t maxNeeded =
static_cast<size_t>(std::max(0, lookaheadDepth)) + 10;
557 std::array<FastGateOp, 128> stackWindow;
558 std::vector<FastGateOp> heapWindow;
559 FastGateOp *windowPtr = stackWindow.data();
560 if (maxNeeded > stackWindow.size())
562 heapWindow.resize(maxNeeded);
563 windowPtr = heapWindow.data();
565 const size_t windowSize = ExtractFastGateWindow(upcomingGates, currentGateIndex, maxNeeded, windowPtr);
568 if (currentCost < bestCost)
569 bestCost = currentCost;
573 uint64_t stateHash = 14695981039346656037ULL;
574 auto mixHash = [&](uint64_t v) {
576 stateHash *= 1099511628211ULL;
578 auto mixDouble = [&](
double d) {
580 std::memcpy(&bits, &d,
sizeof(
double));
584 mixHash(
static_cast<uint64_t
>(windowSize));
585 mixHash(
static_cast<uint64_t
>(lookaheadDepth));
586 mixHash(
static_cast<uint64_t
>(lookaheadDepthWithHeuristic));
587 mixHash(
static_cast<uint64_t
>(physExtent));
588 mixHash(
static_cast<uint64_t
>(maxVirtualExtent));
589 mixHash(
static_cast<uint64_t
>(swapGatesRelabelQubits));
590 mixDouble(growthFactorSwap);
591 mixDouble(growthFactorGate);
593 mixHash(
static_cast<uint64_t
>(q));
594 for (
double d : currentBondDim)
596 for (
double c : bondCost)
598 for (
double m : maxBondDim)
600 for (
size_t w = 0; w < windowSize; ++w)
602 const FastGateOp &fg = windowPtr[w];
603 mixHash(
static_cast<uint64_t
>(fg.q1));
604 mixHash(
static_cast<uint64_t
>(fg.q2));
605 mixHash(
static_cast<uint64_t
>(fg.q3));
606 mixHash(
static_cast<uint64_t
>(fg.numQubits));
607 mixHash(
static_cast<uint64_t
>(fg.isSwap ? 1 : 0));
608 mixDouble(fg.schmidtRank);
611 MemoEntry &slot = memoCache[(
static_cast<size_t>(currentGateIndex) ^
static_cast<size_t>(stateHash)) & (kMemoCacheSize - 1)];
612 if (slot.valid && slot.hash == stateHash)
614 const double candCost = currentCost + slot.costDelta;
615 if (candCost < bestCost)
620 double localBestCost = std::numeric_limits<double>::infinity();
621 const IndexType bestPos = FindBestMeetingPositionFast(windowPtr, windowSize, 0, lookaheadDepth, lookaheadDepthWithHeuristic, 0.0, localBestCost);
624 slot.hash = stateHash;
625 slot.bestPos = bestPos;
626 slot.costDelta = localBestCost;
628 if (currentCost + localBestCost < bestCost)
629 bestCost = currentCost + localBestCost;
639 if (layers.empty() || nrQubits <= 2)
642 auto evaluateCost = [&,
this](
const std::vector<IndexType> &candidateMap) ->
double {
643 auto saveQubitsMap = qubitsMap;
644 auto saveQubitsMapInv = qubitsMapInv;
645 auto saveCurrentBondDim = currentBondDim;
646 auto saveTotalSwappingCost = totalSwappingCost;
647 auto saveBondCost = bondCost;
651 for (
const auto &layer : layers)
654 qubitsMap = std::move(saveQubitsMap);
655 qubitsMapInv = std::move(saveQubitsMapInv);
656 currentBondDim = std::move(saveCurrentBondDim);
657 totalSwappingCost = saveTotalSwappingCost;
658 bondCost = std::move(saveBondCost);
663 auto evaluateCostBounded = [&,
this](
const std::vector<IndexType> &candidateMap,
double bound) ->
double {
664 auto saveQubitsMap = qubitsMap;
665 auto saveQubitsMapInv = qubitsMapInv;
666 auto saveCurrentBondDim = currentBondDim;
667 auto saveTotalSwappingCost = totalSwappingCost;
668 auto saveBondCost = bondCost;
672 for (
const auto &layer : layers)
679 qubitsMap = std::move(saveQubitsMap);
680 qubitsMapInv = std::move(saveQubitsMapInv);
681 currentBondDim = std::move(saveCurrentBondDim);
682 totalSwappingCost = saveTotalSwappingCost;
683 bondCost = std::move(saveBondCost);
689 qubitsMap = std::move(saveQubitsMap);
690 qubitsMapInv = std::move(saveQubitsMapInv);
691 currentBondDim = std::move(saveCurrentBondDim);
692 totalSwappingCost = saveTotalSwappingCost;
693 bondCost = std::move(saveBondCost);
704 std::vector<std::vector<QubitPair>> layerPairs;
705 std::vector<IndexType> initialLabels(nrQubits);
706 std::iota(initialLabels.begin(), initialLabels.end(), 0);
708 for (
size_t li = 0; li < layers.size(); ++li)
710 const auto &layer = layers[li];
711 std::vector<QubitPair> lp;
712 for (
const auto &op : layer->GetOperations())
714 auto qbits = op->AffectedQubits();
715 if (qbits.size() >= 2)
717 if (swapGatesRelabelQubits)
719 const auto gate = std::dynamic_pointer_cast<Circuits::IQuantumGate<>>(op);
720 const auto info = std::dynamic_pointer_cast<RoutingGateInfo>(op);
723 std::swap(initialLabels[qbits[0]], initialLabels[qbits[1]]);
728 for (
auto &q : qbits)
729 q = initialLabels[q];
731 if (qbits[0] > qbits[1])
732 std::swap(qbits[0], qbits[1]);
738 layerPairs.push_back(std::move(lp));
740 if (layerPairs.empty())
751 auto buildChain = [&](
const std::vector<std::vector<QubitPair>> &orderedLP) -> std::vector<long long int> {
752 std::vector<std::deque<IndexType>> groups;
753 std::vector<int> qubitGroup(nrQubits, -1);
754 std::unordered_set<IndexType> placedQubits;
756 for (
const auto &lp : orderedLP)
758 for (
const auto &p : lp)
760 const int g1 = qubitGroup[p.q1];
761 const int g2 = qubitGroup[p.q2];
763 if (g1 < 0 && g2 < 0)
766 const int gIdx =
static_cast<int>(groups.size());
767 groups.push_back({p.q1, p.q2});
768 qubitGroup[p.q1] = gIdx;
769 qubitGroup[p.q2] = gIdx;
770 placedQubits.insert(p.q1);
771 placedQubits.insert(p.q2);
773 else if (g1 >= 0 && g2 < 0)
776 auto &grp = groups[g1];
778 for (
size_t i = 0; i < grp.size(); ++i)
785 if (idx + 1 <= grp.size() - idx)
786 grp.push_front(p.q2);
789 qubitGroup[p.q2] = g1;
790 placedQubits.insert(p.q2);
792 else if (g1 < 0 && g2 >= 0)
795 auto &grp = groups[g2];
797 for (
size_t i = 0; i < grp.size(); ++i)
803 if (idx + 1 <= grp.size() - idx)
804 grp.push_front(p.q1);
807 qubitGroup[p.q1] = g2;
808 placedQubits.insert(p.q1);
813 auto &grpA = groups[g1];
814 auto &grpB = groups[g2];
815 size_t idxA = 0, idxB = 0;
816 for (
size_t i = 0; i < grpA.size(); ++i)
822 for (
size_t i = 0; i < grpB.size(); ++i)
830 const size_t distAB = grpA.size() + idxB - idxA;
831 const size_t distBA = grpB.size() + idxA - idxB;
834 if (distAB <= distBA)
836 for (
const auto q : grpB)
843 for (
const auto q : grpA)
848 for (
const auto q : groups[mergedGroup])
849 qubitGroup[q] = mergedGroup;
853 if (placedQubits.size() ==
static_cast<size_t>(nrQubits))
857 if (placedQubits.size() ==
static_cast<size_t>(nrQubits))
862 std::vector<IndexType> chain;
863 chain.reserve(nrQubits);
864 for (
const auto &grp : groups)
865 for (
const auto q : grp)
870 if (qubitGroup[q] < 0)
873 assert(chain.size() ==
static_cast<size_t>(nrQubits));
876 std::vector<long long int> result(nrQubits);
877 for (
size_t i = 0; i < chain.size(); ++i)
878 result[chain[i]] =
static_cast<long long int>(i);
894 const size_t nQ =
static_cast<size_t>(nrQubits);
895 std::vector<std::vector<std::pair<IndexType, double>>> adjList(nQ);
897 auto &listU = adjList[
static_cast<size_t>(u)];
898 for (
auto &nb : listU)
906 listU.push_back({v, w});
909 const double decay = std::exp(-2.5 /
static_cast<double>(std::max<size_t>(1, layerPairs.size())));
910 double layerWeight = 1.0;
911 for (
const auto &lp : layerPairs)
913 for (
const auto &p : lp)
915 if (p.q1 != p.q2 && p.q1 >= 0 && p.q1 < nrQubits && p.q2 >= 0 && p.q2 < nrQubits)
917 addEdgeWeight(p.q1, p.q2, layerWeight);
918 addEdgeWeight(p.q2, p.q1, layerWeight);
921 layerWeight *= decay;
924 std::vector<WeightedEdge> edges;
927 auto &listU = adjList[
static_cast<size_t>(u)];
928 std::sort(listU.begin(), listU.end(), [](
const auto &a,
const auto &b) { return a.first < b.first; });
929 for (
const auto &nb : listU)
931 if (u < nb.first && nb.second > 0.0)
932 edges.push_back({u, nb.first, nb.second});
935 std::sort(edges.begin(), edges.end(), [](
const WeightedEdge &a,
const WeightedEdge &b) {
938 return std::tie(a.u, a.v) < std::tie(b.u, b.v);
944 const auto &list = (adjList[u].size() <= adjList[v].size()) ? adjList[u] : adjList[v];
945 const IndexType target = (adjList[u].size() <= adjList[v].size()) ? v : u;
947 std::lower_bound(list.begin(), list.end(), target, [](
const std::pair<IndexType, double> &elem,
IndexType val) { return elem.first < val; });
948 return (it != list.end() && it->first == target) ? it->second : 0.0;
954 const double swaps =
static_cast<double>(std::max<IndexType>(0, std::abs(posU - posV) - 1));
955 return swaps * (1.0 + 0.25 * std::sqrt(swaps));
958 auto qubitGraphCost = [&](
IndexType u,
IndexType posU,
const std::vector<IndexType> &pos) ->
double {
960 for (
const auto &nb : adjList[u])
961 c += nb.second * pairSwapPenalty(posU, pos[nb.first]);
965 auto totalGraphCost = [&](
const std::vector<IndexType> &pos) ->
double {
967 for (
const auto &e : edges)
968 c += e.w * pairSwapPenalty(pos[e.u], pos[e.v]);
974 auto refineGraphLayout = [&](std::vector<IndexType> pos) -> std::vector<IndexType> {
975 if (edges.empty() || nrQubits <= 2)
979 if (totalGraphCost(pos) <= 1e-12)
982 std::vector<IndexType> order(nQ);
986 const IndexType maxWindow = std::clamp<IndexType>(
static_cast<IndexType>(512 / std::max<size_t>(1, nQ / 16)), 8, 48);
987 const int maxIter = (nQ > 64) ? 5 : 12;
988 std::vector<IndexType> candPositions;
989 candPositions.reserve(
static_cast<size_t>(maxWindow) + 16);
991 for (
int iter = 0; iter < maxIter; ++iter)
993 bool improved =
false;
994 for (
IndexType p1 = 0; p1 < nrQubits; ++p1)
997 if (qubitGraphCost(u, p1, pos) <= 1e-12)
1000 candPositions.clear();
1001 const IndexType p2Max = std::min<IndexType>(nrQubits - 1, p1 + maxWindow);
1002 for (
IndexType p2 = p1 + 1; p2 <= p2Max; ++p2)
1003 candPositions.push_back(p2);
1004 for (
const auto &nb : adjList[u])
1007 for (
IndexType delta = -1; delta <= 1; ++delta)
1010 if (p2 > p1 + maxWindow && p2 < nrQubits)
1011 candPositions.push_back(p2);
1019 const double wuv = getEdgeWeight(u, v);
1020 const double pairTerm = wuv * pairSwapPenalty(p1, p2);
1021 const double before = qubitGraphCost(u, p1, pos) + qubitGraphCost(v, p2, pos) - pairTerm;
1025 const double after = qubitGraphCost(u, p2, pos) + qubitGraphCost(v, p1, pos) - pairTerm;
1027 if (after + 1e-9 < before)
1049 const double before = qubitGraphCost(qA, pA, pos) + qubitGraphCost(qB, pB, pos);
1054 const double after = qubitGraphCost(qA, pB, pos) + qubitGraphCost(qB, pA, pos);
1055 return after - before;
1058 for (
IndexType fromP = 0; fromP < nrQubits; ++fromP)
1061 if (qubitGraphCost(q, fromP, pos) <= 1e-12)
1064 const IndexType maxRight = std::min<IndexType>(nrQubits - 1, fromP + maxWindow);
1065 const IndexType minLeft = std::max<IndexType>(0, fromP - maxWindow);
1068 double bestDelta = 0.0;
1069 double cumDelta = 0.0;
1071 for (
IndexType curP = fromP; curP < maxRight; ++curP)
1073 cumDelta += swapAdjacentPositions(curP, curP + 1);
1074 if (cumDelta + 1e-9 < bestDelta)
1076 bestDelta = cumDelta;
1080 for (
IndexType curP = maxRight; curP > fromP; --curP)
1081 swapAdjacentPositions(curP, curP - 1);
1084 for (
IndexType curP = fromP; curP > minLeft; --curP)
1086 cumDelta += swapAdjacentPositions(curP, curP - 1);
1087 if (cumDelta + 1e-9 < bestDelta)
1089 bestDelta = cumDelta;
1093 for (
IndexType curP = minLeft; curP < bestP; ++curP)
1094 swapAdjacentPositions(curP, curP + 1);
1100 if (!improved || totalGraphCost(pos) <= 1e-12)
1108 auto buildWeightedChain = [&]() -> std::vector<IndexType> {
1109 std::vector<std::deque<IndexType>> groups;
1110 std::vector<int> qubitGroup(nrQubits, -1);
1111 std::vector<IndexType> posInGroup(nrQubits, 0);
1113 auto refreshGroupPositions = [&](
int gIdx) {
1114 const auto &grp = groups[gIdx];
1115 for (
size_t i = 0; i < grp.size(); ++i)
1116 posInGroup[grp[i]] =
static_cast<IndexType>(i);
1119 for (
const auto &e : edges)
1121 const int g1 = qubitGroup[e.u];
1122 const int g2 = qubitGroup[e.v];
1123 if (g1 < 0 && g2 < 0)
1125 const int gIdx =
static_cast<int>(groups.size());
1126 groups.push_back({e.u, e.v});
1127 qubitGroup[e.u] = gIdx;
1128 qubitGroup[e.v] = gIdx;
1129 posInGroup[e.u] = 0;
1130 posInGroup[e.v] = 1;
1132 else if (g1 >= 0 && g2 < 0)
1134 auto &grp = groups[g1];
1136 double costFront = 0.0;
1137 double costBack = 0.0;
1138 for (
const auto &nb : adjList[e.v])
1140 if (qubitGroup[nb.first] == g1)
1142 const IndexType idx = posInGroup[nb.first];
1143 costFront += nb.second * pairSwapPenalty(0, idx + 1);
1144 costBack += nb.second * pairSwapPenalty(gSize, idx);
1147 if (costFront <= costBack)
1149 grp.push_front(e.v);
1150 qubitGroup[e.v] = g1;
1151 refreshGroupPositions(g1);
1155 posInGroup[e.v] = gSize;
1157 qubitGroup[e.v] = g1;
1160 else if (g1 < 0 && g2 >= 0)
1162 auto &grp = groups[g2];
1164 double costFront = 0.0;
1165 double costBack = 0.0;
1166 for (
const auto &nb : adjList[e.u])
1168 if (qubitGroup[nb.first] == g2)
1170 const IndexType idx = posInGroup[nb.first];
1171 costFront += nb.second * pairSwapPenalty(0, idx + 1);
1172 costBack += nb.second * pairSwapPenalty(gSize, idx);
1175 if (costFront <= costBack)
1177 grp.push_front(e.u);
1178 qubitGroup[e.u] = g2;
1179 refreshGroupPositions(g2);
1183 posInGroup[e.u] = gSize;
1185 qubitGroup[e.u] = g2;
1191 const int gA = (groups[g1].size() >= groups[g2].size()) ? g1 : g2;
1192 const int gB = (gA == g1) ? g2 : g1;
1193 auto &grpA = groups[gA];
1194 auto &grpB = groups[gB];
1198 std::array<double, 4> orientCosts{0.0, 0.0, 0.0, 0.0};
1199 for (
IndexType ib = 0; ib < sizeB; ++ib)
1202 for (
const auto &nb : adjList[qb])
1204 if (qubitGroup[nb.first] == gA)
1206 const IndexType ia = posInGroup[nb.first];
1207 for (
int flipA = 0; flipA < 2; ++flipA)
1209 const IndexType effA = flipA ? (sizeA - 1 - ia) : ia;
1210 for (
int flipB = 0; flipB < 2; ++flipB)
1212 const IndexType effB = sizeA + (flipB ? (sizeB - 1 - ib) : ib);
1213 orientCosts[flipA * 2 + flipB] += nb.second * pairSwapPenalty(effA, effB);
1221 for (
int k = 1; k < 4; ++k)
1222 if (orientCosts[k] < orientCosts[bestOrient])
1226 std::reverse(grpA.begin(), grpA.end());
1228 std::reverse(grpB.begin(), grpB.end());
1229 for (
const auto q : grpB)
1235 refreshGroupPositions(gA);
1239 std::vector<IndexType> chain;
1241 for (
const auto &grp : groups)
1242 for (
const auto q : grp)
1244 for (
IndexType q = 0; q < nrQubits; ++q)
1245 if (qubitGroup[q] < 0)
1248 std::vector<IndexType> result(nQ);
1249 for (
size_t i = 0; i < chain.size(); ++i)
1250 result[chain[i]] =
static_cast<IndexType>(i);
1255 auto buildSpectralChain = [&]() -> std::vector<IndexType> {
1256 std::vector<double> deg(nQ, 0.0);
1257 for (
size_t u = 0; u < nQ; ++u)
1258 for (
const auto &nb : adjList[u])
1259 deg[u] += nb.second;
1261 std::vector<double> v0(nQ, 0.0);
1262 double v0Norm2 = 0.0;
1263 for (
size_t u = 0; u < nQ; ++u)
1265 v0[u] = std::sqrt(std::max(deg[u], 1e-12));
1266 v0Norm2 += v0[u] * v0[u];
1268 const double invV0Norm = 1.0 / std::sqrt(std::max(v0Norm2, 1e-24));
1269 for (
size_t u = 0; u < nQ; ++u)
1272 std::vector<double> x(nQ);
1273 for (
size_t u = 0; u < nQ; ++u)
1274 x[u] =
static_cast<double>(u) - 0.5 *
static_cast<double>(nQ - 1);
1276 for (
int iter = 0; iter < 40; ++iter)
1279 for (
size_t u = 0; u < nQ; ++u)
1280 dot0 += x[u] * v0[u];
1281 for (
size_t u = 0; u < nQ; ++u)
1282 x[u] -= dot0 * v0[u];
1284 std::vector<double> y(nQ, 0.0);
1285 for (
size_t u = 0; u < nQ; ++u)
1290 const double invSqrtDu = 1.0 / std::sqrt(deg[u]);
1291 for (
const auto &nb : adjList[u])
1292 if (deg[nb.first] > 1e-12)
1293 y[u] += 0.5 * nb.second * invSqrtDu * (x[nb.first] / std::sqrt(deg[nb.first]));
1298 for (
size_t u = 0; u < nQ; ++u)
1299 dot0 += y[u] * v0[u];
1301 for (
size_t u = 0; u < nQ; ++u)
1303 y[u] -= dot0 * v0[u];
1304 norm2 += y[u] * y[u];
1308 const double invNorm = 1.0 / std::sqrt(norm2);
1309 for (
size_t u = 0; u < nQ; ++u)
1310 x[u] = y[u] * invNorm;
1314 std::vector<int64_t> quantKey(nQ, 0);
1315 for (
size_t u = 0; u < nQ; ++u)
1317 const double coord = x[u] / std::sqrt(std::max(deg[u], 1e-12));
1318 quantKey[u] = std::isfinite(coord) ?
static_cast<int64_t
>(std::llround(coord * 1e9)) : 0;
1321 std::vector<IndexType> order(nQ);
1322 std::iota(order.begin(), order.end(), 0);
1323 std::sort(order.begin(), order.end(), [&](
IndexType a,
IndexType b) { return std::tie(quantKey[a], a) < std::tie(quantKey[b], b); });
1325 std::vector<IndexType> result(nQ);
1326 for (
size_t i = 0; i < nQ; ++i)
1327 result[order[i]] =
static_cast<IndexType>(i);
1336 std::vector<long long int> identityMap(nrQubits);
1337 std::iota(identityMap.begin(), identityMap.end(), 0);
1338 auto optMap = buildChain(layerPairs);
1339 auto optCost = evaluateCost(optMap);
1340 const double identityCost = optMap == identityMap ? optCost : evaluateCost(identityMap);
1341 if (identityCost < optCost)
1343 optCost = identityCost;
1344 optMap = identityMap;
1352 const bool denseGraph = edges.size() > 4 * nQ;
1353 if (!denseGraph && totalGraphCost(optMap) > 1e-12)
1355 std::vector<std::vector<IndexType>> graphCandidates;
1356 graphCandidates.push_back(refineGraphLayout(identityMap));
1357 graphCandidates.push_back(refineGraphLayout(optMap));
1358 if (layers.size() > 2)
1360 graphCandidates.push_back(refineGraphLayout(buildWeightedChain()));
1361 graphCandidates.push_back(refineGraphLayout(buildSpectralChain()));
1364 for (
const auto &cand : graphCandidates)
1366 if (cand == identityMap || cand == optMap)
1368 const double cost = evaluateCostBounded(cand, optCost);
1377 if (layers.size() <= 2)
1378 return optMap != identityMap && identityCost < minLayoutGain * optCost ? identityMap : optMap;
1384 std::vector<long long int> trialMap = identityMap;
1385 std::mt19937 rng(42);
1386 for (
int i = 0; i < nrShuffles; ++i)
1388 std::shuffle(trialMap.begin(), trialMap.end(), rng);
1389 const double tryCost = evaluateCostBounded(trialMap, optCost);
1390 if (tryCost < optCost)
1397 std::uniform_int_distribution<IndexType> qubitDist(0, nrQubits - 1);
1398 std::uniform_int_distribution<int> nrSwapsDist(1, std::min<int>(3,
static_cast<int>(nrQubits) / 2));
1400 const int maxNoImprove = std::max(nrShuffles,
static_cast<int>(nrQubits));
1401 const int maxTotalShuffles = maxNoImprove * 3;
1402 int noImproveCount = 0;
1404 for (
int s = 0; s < maxTotalShuffles && noImproveCount < maxNoImprove; ++s)
1406 auto tryMap = optMap;
1407 const int swapsCount = nrSwapsDist(rng);
1408 for (
int sw = 0; sw < swapsCount; ++sw)
1414 std::swap(tryMap[a], tryMap[b]);
1417 const auto cost = evaluateCostBounded(tryMap, optCost);
1434 auto candidate = optMap;
1435 bool improved =
true;
1436 for (
int improvementCount = 0; improved && improvementCount < nrSwaps; ++improvementCount)
1439 for (
IndexType i = 0; i < nrQubits; ++i)
1441 for (
IndexType j = i + 1; j < nrQubits; ++j)
1443 std::swap(candidate[i], candidate[j]);
1444 const auto cost = evaluateCostBounded(candidate, optCost);
1453 std::swap(candidate[i], candidate[j]);
1460 if (optMap != identityMap && identityCost < minLayoutGain * optCost)
1466 void InitQubitsMap()
1472 qubitsMapInv[i] = qubitsMap[i] = i;
1474 totalSwappingCost = 0;
1475 if (!currentBondDim.empty())
1476 std::fill(currentBondDim.begin(), currentBondDim.end(), 1.0);
1477 if (!bondCost.empty())
1478 std::fill(bondCost.begin(), bondCost.end(), 1.0);
1481 struct LightweightInitTag
1495 if (realq1 > realq2)
1497 std::swap(realq1, realq2);
1498 std::swap(qubit1, qubit2);
1501 if (realq2 - realq1 <= 1)
1504 const IndexType meetPos = ComputeHeuristicMeetPosition(realq1, realq2);
1515 assert(realq1 < realq2);
1518 double bestCost = bondCost[realq1];
1520 for (
IndexType m = realq1 + 1; m < realq2; ++m)
1522 if (bondCost[m] < bestCost)
1524 bestCost = bondCost[m];
1538 double EstimateGrownBondDimension(
IndexType bond,
double leftDim,
double betweenDim,
double rightNeighborDim,
bool swap,
double schmidtRank)
const
1540 const double rank =
IsOperatorChain() ? schmidtRank * schmidtRank : schmidtRank;
1541 auto grownAt = [&](
double r) {
1542 double newMaxDim = (swap && leftDim == rightNeighborDim) ? betweenDim : static_cast<double>(physExtent) * std::min(leftDim, rightNeighborDim);
1543 newMaxDim = std::min(newMaxDim, betweenDim * r);
1544 const double growthFactor = swap ? growthFactorSwap : growthFactorGate;
1545 return std::max(std::min(newMaxDim * growthFactor, maxBondDim[bond]), 1.);
1548 return grownAt(rank);
1549 return betweenDim + (std::max(rank, 1.) - 1.) * (grownAt(2.) - betweenDim);
1557 assert(realq1 < realq2);
1559 const double leftOuter = (realq1 > 0) ? bondCost[realq1 - 1] : 0.0;
1560 const double rightOuter = (realq2 < static_cast<IndexType>(bondCost.size())) ? bondCost[realq2] : 0.0;
1561 const double c1 = std::max(leftOuter, bondCost[realq1]);
1562 const double c2 = std::max(rightOuter, bondCost[realq2 - 1]);
1569 return (realq1 <=
static_cast<IndexType>(nrQubits) - 1 - realq2) ? realq1 : (realq2 - 1);
1577 uint8_t numQubits = 2;
1578 bool isSwap =
false;
1579 double schmidtRank = 4;
1582 static constexpr size_t kMaxStackQubits = 64;
1584 struct FastStateSnapshot
1586 std::array<IndexType, kMaxStackQubits> qMap;
1587 std::array<IndexType, kMaxStackQubits> qMapInv;
1588 std::array<double, kMaxStackQubits> curBondDim;
1589 std::array<double, kMaxStackQubits> bCost;
1590 std::vector<IndexType> heapMap;
1591 std::vector<double> heapBonds;
1592 double totalCost = 0.0;
1596 const size_t n = sim.nrQubits;
1597 const size_t nb = (n > 0) ? (n - 1) : 0;
1598 totalCost = sim.totalSwappingCost;
1599 if (n <= kMaxStackQubits)
1601 std::memcpy(qMap.data(), sim.qubitsMap.data(), n *
sizeof(
IndexType));
1602 std::memcpy(qMapInv.data(), sim.qubitsMapInv.data(), n *
sizeof(
IndexType));
1605 std::memcpy(curBondDim.data(), sim.currentBondDim.data(), nb *
sizeof(
double));
1606 std::memcpy(bCost.data(), sim.bondCost.data(), nb *
sizeof(
double));
1611 heapMap.resize(2 * n);
1612 std::memcpy(heapMap.data(), sim.qubitsMap.data(), n *
sizeof(
IndexType));
1613 std::memcpy(heapMap.data() + n, sim.qubitsMapInv.data(), n *
sizeof(
IndexType));
1614 heapBonds.resize(2 * nb);
1617 std::memcpy(heapBonds.data(), sim.currentBondDim.data(), nb *
sizeof(
double));
1618 std::memcpy(heapBonds.data() + nb, sim.bondCost.data(), nb *
sizeof(
double));
1625 const size_t n = sim.nrQubits;
1626 const size_t nb = (n > 0) ? (n - 1) : 0;
1627 sim.totalSwappingCost = totalCost;
1628 if (n <= kMaxStackQubits)
1630 std::memcpy(sim.qubitsMap.data(), qMap.data(), n *
sizeof(
IndexType));
1631 std::memcpy(sim.qubitsMapInv.data(), qMapInv.data(), n *
sizeof(
IndexType));
1634 std::memcpy(sim.currentBondDim.data(), curBondDim.data(), nb *
sizeof(
double));
1635 std::memcpy(sim.bondCost.data(), bCost.data(), nb *
sizeof(
double));
1640 std::memcpy(sim.qubitsMap.data(), heapMap.data(), n *
sizeof(
IndexType));
1641 std::memcpy(sim.qubitsMapInv.data(), heapMap.data() + n, n *
sizeof(
IndexType));
1644 std::memcpy(sim.currentBondDim.data(), heapBonds.data(), nb *
sizeof(
double));
1645 std::memcpy(sim.bondCost.data(), heapBonds.data() + nb, nb *
sizeof(
double));
1651 size_t ExtractFastGateWindow(
const std::vector<std::shared_ptr<Circuits::IOperation<>>> &upcomingGates,
long long int startGateIndex,
size_t maxCount,
1652 FastGateOp *outOps)
const
1655 if (startGateIndex < 0)
1658 for (
long long int idx = startGateIndex; idx < static_cast<long long int>(upcomingGates.size()) && count < maxCount; ++idx)
1660 const auto &op = upcomingGates[idx];
1663 const auto qbits = op->AffectedQubits();
1664 if (qbits.size() < 2)
1668 fg.q1 =
static_cast<IndexType>(qbits[0]);
1669 fg.q2 =
static_cast<IndexType>(qbits[1]);
1670 fg.numQubits =
static_cast<uint8_t
>(qbits.size());
1674 if (qbits.size() == 2)
1676 const auto gateptr = std::dynamic_pointer_cast<Circuits::IQuantumGate<>>(op);
1677 const auto info = std::dynamic_pointer_cast<RoutingGateInfo>(op);
1680 fg.isSwap = info->isSwap;
1681 fg.schmidtRank = info->schmidtRank;
1693 else if (qbits.size() == 3)
1695 fg.q3 =
static_cast<IndexType>(qbits[2]);
1696 const auto gateptr = std::static_pointer_cast<Circuits::IQuantumGate<>>(op);
1701 outOps[count++] = fg;
1707 void ApplyTwoQubitFastGate(
IndexType qubit,
IndexType controllingQubit1,
bool isSwap,
double schmidtRank)
1709 if (isSwap && swapGatesRelabelQubits)
1712 IndexType qubit2 = qubitsMap[controllingQubit1];
1714 if (abs(qubit1 - qubit2) > 1)
1716 SwapQubits(qubit, controllingQubit1);
1717 qubit1 = qubitsMap[qubit];
1718 qubit2 = qubitsMap[controllingQubit1];
1721 const IndexType bond = std::min(qubit1, qubit2);
1722 totalSwappingCost += bondCost[bond];
1723 growBondDimension(bond, isSwap, schmidtRank);
1727 void ApplyFastGate(
const FastGateOp &g)
1729 if (g.numQubits == 2)
1731 ApplyTwoQubitFastGate(g.q1, g.q2, g.isSwap, g.schmidtRank);
1733 else if (g.numQubits == 3)
1737 ApplyTwoQubitFastGate(g.q3, g.q2,
false, 2);
1738 ApplyTwoQubitFastGate(g.q2, g.q1,
false, 2);
1739 ApplyTwoQubitFastGate(g.q3, g.q2,
false, 2);
1740 ApplyTwoQubitFastGate(g.q2, g.q1,
false, 2);
1741 ApplyTwoQubitFastGate(g.q3, g.q1,
false, 2);
1745 ApplyTwoQubitFastGate(g.q2, g.q3,
false, 2);
1746 ApplyTwoQubitFastGate(g.q3, g.q2,
false, 2);
1747 ApplyTwoQubitFastGate(g.q2, g.q1,
false, 2);
1748 ApplyTwoQubitFastGate(g.q3, g.q2,
false, 2);
1749 ApplyTwoQubitFastGate(g.q2, g.q1,
false, 2);
1750 ApplyTwoQubitFastGate(g.q3, g.q1,
false, 2);
1751 ApplyTwoQubitFastGate(g.q2, g.q3,
false, 2);
1756 bool IsRelabelingSwap(
const FastGateOp &gate)
const
1758 return swapGatesRelabelQubits && gate.numQubits == 2 && gate.isSwap;
1764 double weight = 0.0;
1769 size_t CollectNextPartnerPullsFast(
IndexType qubit1,
IndexType qubit2,
const FastGateOp *window,
size_t windowSize,
size_t stepIdx,
1770 std::array<PartnerPull, 16> &pulls,
double &totalWeight)
const
1772 size_t numPulls = 0;
1774 bool foundQ1 =
false;
1775 bool foundQ2 =
false;
1776 double weight = 1.0;
1777 const size_t maxStep = std::min(windowSize, stepIdx + 9);
1778 std::array<std::pair<IndexType, IndexType>, 8> relabels;
1779 size_t numRelabels = 0;
1781 for (
size_t i = numRelabels; i > 0; --i)
1783 const auto [a, b] = relabels[i - 1];
1792 for (
size_t s = stepIdx + 1; s < maxStep; ++s)
1794 const FastGateOp &fg = window[s];
1795 if (IsRelabelingSwap(fg))
1797 relabels[numRelabels++] = {fg.q1, fg.q2};
1800 const IndexType qA = originalLabel(fg.q1);
1801 const IndexType qB = originalLabel(fg.q2);
1804 if (partnerQ == qubit1 || partnerQ == qubit2)
1806 const bool isFirst = (activeQ == qubit1) ? !foundQ1 : !foundQ2;
1807 const double w = isFirst ? weight : (weight * 0.35);
1808 if (numPulls < pulls.size())
1810 pulls[numPulls++] = {qubitsMap[partnerQ], w};
1813 if (activeQ == qubit1)
1819 if (qA == qubit1 || qA == qubit2)
1820 recordPartner(qA, qB);
1821 if (qB == qubit1 || qB == qubit2)
1822 recordPartner(qB, qA);
1824 if (foundQ1 && foundQ2)
1833 size_t windowSize,
size_t stepIdx)
const
1835 assert(realq1 < realq2);
1837 std::array<PartnerPull, 16> pulls;
1838 double totalWeight = 0.0;
1839 const size_t numPulls = CollectNextPartnerPullsFast(qubit1, qubit2, window, windowSize, stepIdx, pulls, totalWeight);
1840 if (numPulls == 0 || totalWeight <= 0.0)
1841 return ComputeAnchorHeavierMeetPosition(realq1, realq2);
1844 double bestDist = std::numeric_limits<double>::infinity();
1845 double bestLocalBondCost = std::numeric_limits<double>::infinity();
1847 for (
IndexType m = realq1; m < realq2; ++m)
1850 for (
size_t k = 0; k < numPulls; ++k)
1851 dist += pulls[k].weight *
static_cast<double>(std::abs(m - pulls[k].pos));
1853 const double localCost = bondCost[m];
1854 if (dist < bestDist - 1e-9 || (std::abs(dist - bestDist) <= 1e-9 && localCost < bestLocalBondCost))
1857 bestLocalBondCost = localCost;
1868 size_t windowSize,
size_t stepIdx,
bool includeGravity)
const
1870 assert(realq1 < realq2);
1875 const FastGateOp &fg = window[stepIdx];
1876 const bool isSwap = (fg.numQubits == 2) ? fg.isSwap : false;
1877 const double schmidtRank = fg.schmidtRank;
1879 constexpr IndexType kMaxStackSpan = 128;
1880 std::array<double, kMaxStackSpan> rightSweepDimStack{};
1881 std::array<double, kMaxStackSpan> prefixPostCostStack{};
1882 std::array<double, kMaxStackSpan> leftSweepDimStack{};
1883 std::array<double, kMaxStackSpan> suffixPostCostStack{};
1885 std::vector<double> heapBuf;
1886 double *rightSweepDim = rightSweepDimStack.data();
1887 double *prefixPostCost = prefixPostCostStack.data();
1888 double *leftSweepDim = leftSweepDimStack.data();
1889 double *suffixPostCost = suffixPostCostStack.data();
1891 if (span > kMaxStackSpan)
1893 heapBuf.resize(
static_cast<size_t>(span) * 4, 0.0);
1894 rightSweepDim = heapBuf.data();
1895 prefixPostCost = rightSweepDim + span;
1896 leftSweepDim = prefixPostCost + span;
1897 suffixPostCost = leftSweepDim + span;
1900 prefixPostCost[0] = 0.0;
1901 for (
IndexType i = 0; i < span - 1; ++i)
1904 const double leftD = (i == 0) ? ((realq1 > 0) ? currentBondDim[realq1 - 1] : 1.0) : rightSweepDim[i - 1];
1905 const double betweenD = currentBondDim[bond];
1906 const double rightD = currentBondDim[bond + 1];
1907 const double newD = EstimateGrownBondDimension(bond, leftD, betweenD, rightD,
true, 4);
1908 rightSweepDim[i] = newD;
1909 prefixPostCost[i + 1] = prefixPostCost[i] +
BondCost(newD);
1912 suffixPostCost[span - 1] = 0.0;
1913 for (
IndexType i = span - 1; i > 0; --i)
1916 const double leftD = currentBondDim[bond - 1];
1917 const double betweenD = currentBondDim[bond];
1918 const double rightD =
1919 (i == span - 1) ? ((realq2 <
static_cast<IndexType>(currentBondDim.size())) ? currentBondDim[realq2] : 1.0) : leftSweepDim[i + 1];
1920 const double newD = EstimateGrownBondDimension(bond, leftD, betweenD, rightD,
true, 4);
1921 leftSweepDim[i] = newD;
1922 suffixPostCost[i - 1] = suffixPostCost[i] +
BondCost(newD);
1925 std::array<PartnerPull, 16> pulls;
1926 double totalPullWeight = 0.0;
1927 const size_t numPulls = CollectNextPartnerPullsFast(qubit1, qubit2, window, windowSize, stepIdx, pulls, totalPullWeight);
1928 const double gravityWeight = includeGravity ? 0.15 : 1e-6;
1929 const double invNorm = (numPulls > 0 && totalPullWeight > 0.0) ? (gravityWeight / (
static_cast<double>(span) * totalPullWeight)) : 0.0;
1932 double bestScore = std::numeric_limits<double>::infinity();
1937 const double leftD = (i == 0) ? ((realq1 > 0) ? currentBondDim[realq1 - 1] : 1.0) : rightSweepDim[i - 1];
1938 const double rightD =
1939 (i == span - 1) ? ((realq2 <
static_cast<IndexType>(currentBondDim.size())) ? currentBondDim[realq2] : 1.0) : leftSweepDim[i + 1];
1940 const double gateD = EstimateGrownBondDimension(m, leftD, currentBondDim[m], rightD, isSwap, schmidtRank);
1941 const double gateCost =
BondCost(gateD);
1947 double score = postBondCost;
1950 double pullDist = 0.0;
1951 for (
size_t k = 0; k < numPulls; ++k)
1952 pullDist += pulls[k].weight *
static_cast<double>(std::abs(m - pulls[k].pos));
1953 score *= (1.0 + invNorm * pullDist);
1956 if (score < bestScore)
1967 const FastGateOp *window,
size_t windowSize,
size_t stepIdx)
const
1969 if (realq2 - realq1 <= 1)
1975 return ComputeMinOneStepCostMeetPositionFast(realq1, realq2, qubit1, qubit2, window, windowSize, stepIdx,
true);
1977 return ComputeMinOneStepCostMeetPositionFast(realq1, realq2, qubit1, qubit2, window, windowSize, stepIdx,
false);
1979 return ComputeNextPartnerGravityMeetPositionFast(realq1, realq2, qubit1, qubit2, window, windowSize, stepIdx);
1981 return ComputeAnchorHeavierMeetPosition(realq1, realq2);
1983 return ComputeHeuristicMeetPosition(realq1, realq2);
1989 return (realq1 <=
static_cast<IndexType>(nrQubits) - 1 - realq2) ? realq1 : (realq2 - 1);
1991 const IndexType center = std::max<IndexType>(0, (
static_cast<IndexType>(nrQubits) - 2) / 2);
1992 return std::clamp(center, realq1, realq2 - 1);
1996 return ComputeHeuristicMeetPosition(realq1, realq2);
2002 IndexType EvaluateHeuristicRolloutsFast(
const FastGateOp *window,
size_t windowSize,
size_t stepIdx,
int lookaheadDepth,
double currentCost,
2005 if (stepIdx >= windowSize)
2007 if (currentCost < bestCost)
2008 bestCost = currentCost;
2012 const FastGateOp &firstOp = window[stepIdx];
2015 IndexType firstReal1 = qubitsMap[firstQ1];
2016 IndexType firstReal2 = qubitsMap[firstQ2];
2017 if (firstReal1 > firstReal2)
2018 std::swap(firstReal1, firstReal2);
2021 IndexType bestFirstPosition = IsRelabelingSwap(firstOp) ? -1
2022 : (firstReal2 - firstReal1 > 1) ? SelectMeetPositionForPolicyFast(defaultFirstPolicy, firstReal1, firstReal2, firstQ1,
2023 firstQ2, window, windowSize, stepIdx)
2026 static constexpr std::array<HeuristicPolicy, 9> kFullPortfolio = {
2038 const size_t numPolicies = (lookaheadDepth <= 0) ? 1 : kFullPortfolio.size();
2039 const size_t endStep = std::min(windowSize, stepIdx +
static_cast<size_t>(std::max(0, lookaheadDepth)) + 1);
2041 FastStateSnapshot baseState;
2042 baseState.SaveFrom(*
this);
2044 for (
size_t pi = 0; pi < numPolicies; ++pi)
2048 baseState.RestoreTo(*
this);
2051 double rolloutCost = currentCost;
2052 IndexType chosenFirstPos = bestFirstPosition;
2053 bool pruned =
false;
2055 for (
size_t s = stepIdx; s < endStep; ++s)
2057 const FastGateOp &fg = window[s];
2063 std::swap(rq1, rq2);
2065 IndexType meetPos = IsRelabelingSwap(fg) ? -1 : rq1;
2066 if (!IsRelabelingSwap(fg) && rq2 - rq1 > 1)
2068 meetPos = SelectMeetPositionForPolicyFast(policy, rq1, rq2, q1, q2, window, windowSize, s);
2073 chosenFirstPos = meetPos;
2078 if (rolloutCost >= bestCost)
2085 if (!pruned && rolloutCost < bestCost)
2087 bestCost = rolloutCost;
2088 bestFirstPosition = chosenFirstPos;
2092 baseState.RestoreTo(*
this);
2093 return bestFirstPosition;
2096 void EvaluateMeetingPositionCostFast(
IndexType meetPosition,
const FastGateOp *window,
size_t windowSize,
size_t stepIdx,
int lookaheadDepth,
2097 int lookaheadDepthWithHeuristic,
double currentCost,
double &bestCost,
bool mutateSelf)
2099 if (stepIdx >= windowSize)
2101 if (currentCost < bestCost)
2102 bestCost = currentCost;
2106 FastStateSnapshot saved;
2108 saved.SaveFrom(*
this);
2111 const FastGateOp &fg = window[stepIdx];
2114 if (realq1 > realq2)
2115 std::swap(realq1, realq2);
2117 if (!IsRelabelingSwap(fg) && realq2 - realq1 > 1)
2123 if (currentCost < bestCost)
2125 if (lookaheadDepth <= 0 || stepIdx + 1 >= windowSize)
2127 bestCost = currentCost;
2131 FindBestMeetingPositionFast(window, windowSize, stepIdx + 1, lookaheadDepth - 1, lookaheadDepthWithHeuristic, currentCost, bestCost);
2136 saved.RestoreTo(*
this);
2139 IndexType FindBestMeetingPositionFast(
const FastGateOp *window,
size_t windowSize,
size_t stepIdx,
int lookaheadDepth,
int lookaheadDepthWithHeuristic,
2140 double currentCost,
double &bestCost)
2142 if (stepIdx >= windowSize)
2144 if (currentCost < bestCost)
2145 bestCost = currentCost;
2149 const FastGateOp &fg = window[stepIdx];
2153 if (IsRelabelingSwap(fg))
2155 EvaluateMeetingPositionCostFast(-1, window, windowSize, stepIdx, lookaheadDepth, lookaheadDepthWithHeuristic, currentCost, bestCost,
false);
2161 if (realq1 > realq2)
2162 std::swap(realq1, realq2);
2164 if (lookaheadDepth <= 0 || lookaheadDepth <= lookaheadDepthWithHeuristic)
2166 return EvaluateHeuristicRolloutsFast(window, windowSize, stepIdx, std::max(0, lookaheadDepth), currentCost, bestCost);
2169 if (realq2 - realq1 <= 1)
2171 EvaluateMeetingPositionCostFast(realq1, window, windowSize, stepIdx, lookaheadDepth, lookaheadDepthWithHeuristic, currentCost, bestCost,
false);
2180 bestPosition = EvaluateHeuristicRolloutsFast(window, windowSize, stepIdx, lookaheadDepth, currentCost, bestCost);
2183 const size_t span =
static_cast<size_t>(realq2 - realq1);
2184 std::array<IndexType, 128> stackCandidates;
2185 std::vector<IndexType> heapCandidates;
2186 IndexType *candidates = stackCandidates.data();
2187 size_t maxCandidates = stackCandidates.size();
2188 if (span > stackCandidates.size())
2190 heapCandidates.resize(span);
2191 candidates = heapCandidates.data();
2192 maxCandidates = span;
2195 size_t numCandidates = 0;
2196 auto addCandidate = [&](
IndexType pos) {
2197 if (pos < realq1 || pos >= realq2)
2199 for (
size_t i = 0; i < numCandidates; ++i)
2200 if (candidates[i] == pos)
2202 if (numCandidates < maxCandidates)
2203 candidates[numCandidates++] = pos;
2207 addCandidate(bestPosition);
2208 addCandidate(ComputeMinOneStepCostMeetPositionFast(realq1, realq2, qubit1, qubit2, window, windowSize, stepIdx,
true));
2209 addCandidate(ComputeMinOneStepCostMeetPositionFast(realq1, realq2, qubit1, qubit2, window, windowSize, stepIdx,
false));
2210 addCandidate(ComputeHeuristicMeetPosition(realq1, realq2));
2211 addCandidate(ComputeAnchorHeavierMeetPosition(realq1, realq2));
2212 addCandidate(ComputeNextPartnerGravityMeetPositionFast(realq1, realq2, qubit1, qubit2, window, windowSize, stepIdx));
2213 addCandidate(realq1);
2214 addCandidate(realq2 - 1);
2215 const IndexType centerPos = std::clamp(std::max<IndexType>(0, (
static_cast<IndexType>(nrQubits) - 2) / 2), realq1, realq2 - 1);
2216 addCandidate(centerPos);
2220 for (
IndexType m = realq1; m < realq2; ++m)
2224 bestPosition = candidates[0];
2226 for (
size_t i = 0; i < numCandidates; ++i)
2229 const double oldCost = bestCost;
2230 EvaluateMeetingPositionCostFast(m, window, windowSize, stepIdx, lookaheadDepth, lookaheadDepthWithHeuristic, currentCost, bestCost,
false);
2232 if (bestCost < oldCost)
2236 return bestPosition;
2243 size_t stepIdx,
bool includeGravity)
const
2245 assert(realq1 < realq2);
2251 constexpr IndexType kMaxStackSpan = 128;
2252 std::array<double, kMaxStackSpan + 2> bondsStack{};
2253 std::array<double, kMaxStackSpan> prefixCutStack{};
2254 std::array<double, kMaxStackSpan> suffixCutStack{};
2255 std::array<double, kMaxStackSpan> interShiftStack{};
2256 std::vector<double> heapBuf;
2258 double *bonds = bondsStack.data();
2259 double *prefixCut = prefixCutStack.data();
2260 double *suffixCut = suffixCutStack.data();
2261 double *interShift = interShiftStack.data();
2262 if (span > kMaxStackSpan)
2264 heapBuf.resize(
static_cast<size_t>(span) * 4 + 2, 0.0);
2265 bonds = heapBuf.data();
2266 prefixCut = bonds + span + 2;
2267 suffixCut = prefixCut + span;
2268 interShift = suffixCut + span;
2271 bonds[0] = (realq1 > 0) ? currentBondDim[realq1 - 1] : 1.0;
2273 bonds[i + 1] = currentBondDim[realq1 + i];
2274 bonds[span + 1] = (realq2 < nBonds) ? currentBondDim[realq2] : 1.0;
2276 const double E1 = std::max(bonds[0], bonds[1]);
2277 const double E2 = std::max(bonds[span], bonds[span + 1]);
2278 const double d =
static_cast<double>(physExtent);
2281 for (
IndexType i = 0; i < span - 1; ++i)
2283 const double bDim = bonds[i + 1];
2287 const double inflated = std::min(E1, d * bDim);
2288 dmg = std::max(0.0,
BondCost(inflated) - bondCost[realq1 + i]);
2290 prefixCut[i + 1] = prefixCut[i] + dmg;
2293 suffixCut[span - 1] = 0.0;
2294 for (
IndexType i = span - 1; i > 0; --i)
2296 const double bDim = bonds[i + 1];
2300 const double inflated = std::min(E2, d * bDim);
2301 dmg = std::max(0.0,
BondCost(inflated) - bondCost[realq1 + i]);
2303 suffixCut[i - 1] = suffixCut[i] + dmg;
2306 std::array<PartnerPull, 16> pulls;
2307 double totalPullWeight = 0.0;
2308 size_t numPulls = 0;
2310 if (includeGravity && window && stepIdx + 1 < windowSize)
2312 numPulls = CollectNextPartnerPullsFast(qubit1, qubit2, window, windowSize, stepIdx, pulls, totalPullWeight);
2318 std::array<IndexType, kMaxStackSpan> nextPartnerPosStack;
2319 std::array<double, kMaxStackSpan> nextPartnerWStack{};
2320 std::vector<IndexType> nextPartnerPosHeap;
2321 std::vector<double> nextPartnerWHeap;
2322 IndexType *nextPartnerPos = nextPartnerPosStack.data();
2323 double *nextPartnerW = nextPartnerWStack.data();
2324 if (span > kMaxStackSpan)
2326 nextPartnerPosHeap.assign(
static_cast<size_t>(span), -1);
2327 nextPartnerWHeap.assign(
static_cast<size_t>(span), 0.0);
2328 nextPartnerPos = nextPartnerPosHeap.data();
2329 nextPartnerW = nextPartnerWHeap.data();
2335 nextPartnerPos[i] = -1;
2336 nextPartnerW[i] = 0.0;
2340 const size_t maxLook = std::min(windowSize, stepIdx + 13);
2341 double wDecay = 1.0;
2343 std::array<std::pair<IndexType, IndexType>, 12> relabels;
2344 size_t numRelabels = 0;
2346 for (
size_t i = numRelabels; i > 0; --i)
2348 const auto [a, b] = relabels[i - 1];
2357 for (
size_t s = stepIdx + 1; s < maxLook && foundCount < span - 1; ++s)
2359 const FastGateOp &fg = window[s];
2360 if (IsRelabelingSwap(fg))
2362 if (numRelabels < relabels.size())
2363 relabels[numRelabels++] = {fg.q1, fg.q2};
2366 const IndexType qA = originalLabel(fg.q1);
2367 const IndexType qB = originalLabel(fg.q2);
2370 if (rA > realq1 && rA < realq2 && nextPartnerPos[rA - realq1] < 0)
2372 nextPartnerPos[rA - realq1] = rB;
2373 nextPartnerW[rA - realq1] = wDecay;
2376 if (rB > realq1 && rB < realq2 && nextPartnerPos[rB - realq1] < 0)
2378 nextPartnerPos[rB - realq1] = rA;
2379 nextPartnerW[rB - realq1] = wDecay;
2385 double runningLeftShift = 0.0;
2386 interShift[0] = 0.0;
2393 const double delta = (p < kPos) ? -1.0 : ((p > kPos) ? +1.0 : 0.0);
2394 runningLeftShift += nextPartnerW[i] * delta;
2396 interShift[i] = runningLeftShift;
2399 double runningRightShift = 0.0;
2400 for (
IndexType i = span - 2; i >= 0; --i)
2402 const IndexType p = nextPartnerPos[i + 1];
2406 const double delta = (p > kPos) ? -1.0 : ((p < kPos) ? +1.0 : 0.0);
2407 runningRightShift += nextPartnerW[i + 1] * delta;
2409 interShift[i] += runningRightShift;
2413 const double c1 = bonds[0] * bonds[1];
2414 const double c2 = bonds[span] * bonds[span + 1];
2415 const double effCap = (runtimeBondCap > 0) ?
static_cast<double>(runtimeBondCap) : std::numeric_limits<double>::infinity();
2418 double bestScore = std::numeric_limits<double>::infinity();
2423 const double bLeft = std::min(bonds[i], effCap);
2424 const double bMid = std::min(bonds[i + 1], effCap);
2425 const double bRight = std::min(bonds[i + 2], effCap);
2426 const double localBondCost = bondCost[m];
2427 const double cutDamage = 0.5 * (prefixCut[i] + suffixCut[i]);
2428 const double outerContext = 0.05 * (bLeft * bMid * bRight);
2429 const double tieBreak = (c2 > c1) ? (1e-6 *
static_cast<double>(span - 1 - i)) : (1e-6 *
static_cast<double>(i));
2430 const double baseCost = localBondCost + cutDamage + outerContext + tieBreak;
2432 double pullCost = 0.0;
2435 for (
size_t k = 0; k < numPulls; ++k)
2436 pullCost += pulls[k].weight *
static_cast<double>(std::abs(m - pulls[k].pos));
2437 pullCost += 1.15 * interShift[i];
2440 const double score = baseCost + 0.35 * localBondCost * (pullCost /
static_cast<double>(span));
2441 if (score < bestScore)
2455 int lookaheadDepth,
int lookaheadDepthWithHeuristic,
double currentCost,
double &bestCost)
2457 if (currentGateIndex < 0 || currentGateIndex >=
static_cast<long long int>(upcomingGates.size()))
2459 if (currentCost < bestCost)
2460 bestCost = currentCost;
2464 const int exactSteps = std::max(0, lookaheadDepth - std::max(0, lookaheadDepthWithHeuristic));
2468 if (lookaheadDepth <= 0 || lookaheadDepthWithHeuristic <= 0)
2469 return FindBestMeetingPosition(upcomingGates, currentGateIndex, lookaheadDepth, lookaheadDepthWithHeuristic, currentCost, bestCost);
2471 const size_t maxNeeded =
static_cast<size_t>(std::max(16, 2 * std::max(0, lookaheadDepth) + 10));
2472 std::array<FastGateOp, 128> stackWindow;
2473 std::vector<FastGateOp> heapWindow;
2474 FastGateOp *windowPtr = stackWindow.data();
2475 if (maxNeeded > stackWindow.size())
2477 heapWindow.resize(maxNeeded);
2478 windowPtr = heapWindow.data();
2480 const size_t windowSize = ExtractFastGateWindow(upcomingGates, currentGateIndex, maxNeeded, windowPtr);
2481 if (windowSize == 0)
2483 if (currentCost < bestCost)
2484 bestCost = currentCost;
2490 const size_t endStep = std::min(windowSize,
static_cast<size_t>(std::max(0, lookaheadDepth)) + 1);
2492 const FastGateOp &fg0 = windowPtr[0];
2493 if (IsRelabelingSwap(fg0))
2495 EvaluateMeetingPositionCostFast(-1, windowPtr, windowSize, 0, lookaheadDepth, lookaheadDepthWithHeuristic, currentCost, bestCost,
false);
2499 auto searchAdaptive = [&](
auto &self,
size_t stepIdx,
double stepStartCost,
double &boundCost) ->
IndexType {
2500 if (stepIdx >= endStep)
2502 if (stepStartCost < boundCost)
2503 boundCost = stepStartCost;
2507 const FastGateOp &fg = windowPtr[stepIdx];
2510 if (!IsRelabelingSwap(fg))
2512 rq1 = qubitsMap[fg.q1];
2513 rq2 = qubitsMap[fg.q2];
2515 std::swap(rq1, rq2);
2518 FastStateSnapshot stepState;
2519 stepState.SaveFrom(*
this);
2528 IndexType bestPos = IsRelabelingSwap(fg) ? -1
2529 : (rq2 - rq1 <= 1) ? rq1
2530 : SelectMeetingPositionP0Fast(rq1, rq2, fg.q1, fg.q2, windowPtr, windowSize, stepIdx,
true);
2532 const double boundBefore = boundCost;
2534 EvaluateHeuristicRolloutsFast(windowPtr, windowSize, stepIdx,
static_cast<int>(endStep - stepIdx) - 1, stepStartCost, boundCost);
2535 if (boundCost < boundBefore)
2536 bestPos = rolloutPos;
2538 stepState.RestoreTo(*
this);
2539 if (stepIdx >=
static_cast<size_t>(exactSteps))
2542 if (IsRelabelingSwap(fg))
2545 if (stepStartCost < boundCost)
2547 if (stepIdx + 1 >= endStep)
2548 boundCost = stepStartCost;
2550 (
void)self(self, stepIdx + 1, stepStartCost, boundCost);
2552 stepState.RestoreTo(*
this);
2561 if (nextCost < boundCost)
2563 if (stepIdx + 1 >= endStep)
2564 boundCost = nextCost;
2566 (
void)self(self, stepIdx + 1, nextCost, boundCost);
2568 stepState.RestoreTo(*
this);
2572 const IndexType mP0Grav = SelectMeetingPositionP0Fast(rq1, rq2, fg.q1, fg.q2, windowPtr, windowSize, stepIdx,
true);
2573 const IndexType mP0Pure = SelectMeetingPositionP0Fast(rq1, rq2, fg.q1, fg.q2, windowPtr, windowSize, stepIdx,
false);
2574 const IndexType mAnchor = ComputeAnchorHeavierMeetPosition(rq1, rq2);
2576 double minBondInSpan = currentBondDim[rq1];
2577 for (
IndexType b = rq1 + 1; b < rq2; ++b)
2578 minBondInSpan = std::min(minBondInSpan, currentBondDim[b]);
2580 const double E1 = std::max((rq1 > 0) ? currentBondDim[rq1 - 1] : 1.0, currentBondDim[rq1]);
2581 const double E2 = std::max(currentBondDim[rq2 - 1], (rq2 <
static_cast<IndexType>(currentBondDim.size())) ? currentBondDim[rq2] : 1.0);
2582 const bool hasLowEntanglementCut = (minBondInSpan < 16.0) && (runtimeBondCap <= 0 || minBondInSpan <
static_cast<double>(runtimeBondCap)) &&
2583 (minBondInSpan <= 0.5 * std::min(E1, E2));
2586 std::array<IndexType, 128> stackCandidates{};
2587 std::vector<IndexType> heapCandidates;
2588 IndexType *candidates = stackCandidates.data();
2589 size_t maxCandidates = stackCandidates.size();
2590 if (
static_cast<size_t>(span) > stackCandidates.size())
2592 heapCandidates.resize(
static_cast<size_t>(span));
2593 candidates = heapCandidates.data();
2594 maxCandidates = heapCandidates.size();
2597 size_t numCandidates = 0;
2599 if (pos < rq1 || pos >= rq2)
2601 for (
size_t i = 0; i < numCandidates; ++i)
2602 if (candidates[i] == pos)
2604 if (numCandidates < maxCandidates)
2605 candidates[numCandidates++] = pos;
2610 if (!hasLowEntanglementCut)
2616 addCand(rq1 + span / 2);
2617 if (span <= 12 || stepIdx > 0 || exactSteps >= 2)
2624 for (
size_t ci = 0; ci < numCandidates; ++ci)
2626 const IndexType candPos = candidates[ci];
2627 stepState.RestoreTo(*
this);
2633 if (nextCost >= boundCost)
2636 if (stepIdx + 1 >= endStep)
2638 boundCost = nextCost;
2643 const double prevBound = boundCost;
2644 (void)self(self, stepIdx + 1, nextCost, boundCost);
2645 if (boundCost < prevBound)
2650 stepState.RestoreTo(*
this);
2654 double bestRolloutCost = bestCost;
2655 const IndexType chosenPos = searchAdaptive(searchAdaptive, 0, currentCost, bestRolloutCost);
2656 if (bestRolloutCost < bestCost)
2657 bestCost = bestRolloutCost;
2670 if (realq1 > realq2)
2672 std::swap(realq1, realq2);
2673 std::swap(qubit1, qubit2);
2676 if (realq2 - realq1 <= 1)
2679 assert(meetPosition >= realq1 && meetPosition < realq2);
2684 while (movingReal < meetPosition)
2686 const IndexType toReal = movingReal + 1;
2687 const IndexType toInv = qubitsMapInv[toReal];
2689 qubitsMap[toInv] = movingReal;
2690 qubitsMapInv[movingReal] = toInv;
2692 qubitsMap[qubit1] = toReal;
2693 qubitsMapInv[toReal] = qubit1;
2695 totalSwappingCost += bondCost[movingReal];
2696 growBondDimension(movingReal,
true);
2698 movingReal = toReal;
2705 while (movingReal > meetPosition + 1)
2707 const IndexType toReal = movingReal - 1;
2708 const IndexType toInv = qubitsMapInv[toReal];
2710 qubitsMap[toInv] = movingReal;
2711 qubitsMapInv[movingReal] = toInv;
2713 qubitsMap[qubit2] = toReal;
2714 qubitsMapInv[toReal] = qubit2;
2716 totalSwappingCost += bondCost[toReal];
2717 growBondDimension(toReal,
true);
2719 movingReal = toReal;
2723 assert(abs(qubitsMap[qubit1] - qubitsMap[qubit2]) == 1);
2731 double costDelta = 0.0;
2735 static constexpr size_t kMemoCacheSize = 64;
2736 std::array<MemoEntry, kMemoCacheSize> memoCache{};
2740 std::vector<IndexType> qubitsMap;
2741 std::vector<IndexType> qubitsMapInv;
2743 size_t physExtent = 2;
2745 std::vector<double> bondCost;
2747 double truncationWeight = 0.;
2748 bool swapGatesRelabelQubits =
false;
2749 std::vector<double> maxBondDim;
2750 std::vector<double> currentBondDim;
2752 double totalSwappingCost = 0;
2754 double growthFactorSwap = 1.;
2755 double growthFactorGate = 0.7;
2756 double minLayoutGain = 4.;
2758 void growBondDimension(
IndexType bond,
bool swap =
true,
double schmidtRank = 4)
2787 const IndexType rightNeigborBond = bond + 1;
2788 const double betweenDim = currentBondDim[bond];
2790 const double leftDim = leftBond >= 0 ? currentBondDim[leftBond] : 1;
2791 const double rightNeighborDim = rightNeigborBond < static_cast<IndexType>(currentBondDim.size()) ? currentBondDim[rightNeigborBond] : 1;
2793 currentBondDim[bond] = EstimateGrownBondDimension(bond, leftDim, betweenDim, rightNeighborDim, swap, schmidtRank);
2795 bondCost[bond] =
BondCost(currentBondDim[bond]);