Maestro 0.1.0
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
LookaheadContractor.h
Go to the documentation of this file.
1
14#pragma once
15
16#ifndef __LOOKAHEAD_CONTRACTOR_H_
17#define __LOOKAHEAD_CONTRACTOR_H_ 1
18
19#include "BaseContractor.h"
20
21#include <boost/container_hash/hash.hpp>
22
23namespace TensorNetworks {
24
31 public:
32 using TensorPair = std::pair<Eigen::Index, Eigen::Index>;
34 std::unordered_set<TensorPair, boost::hash<TensorPair>>;
35 using DummyTensorsMap = std::map<Eigen::Index, std::shared_ptr<TensorNode>>;
36
43 double Contract(const TensorNetwork &network, Types::qubit_t qubit) override {
44 std::vector<Eigen::Index> keys;
45 std::unordered_map<Eigen::Index, Eigen::Index> keysKeys;
46
47 TensorsMap tensors =
48 InitializeTensors(network, qubit, keys, keysKeys, false);
49 DummyTensorsMap dummyTensors = CloneTensorsToDummy(tensors);
50
51 // while there is more than one tensor...
52 while (tensors.size() > 1) {
53 // just to have them set to something
54 auto it = tensors.begin();
55 Eigen::Index tensor1Id = it->first;
56 ++it;
57 Eigen::Index tensor2Id = it->first;
58
59 const auto saveMaxTensorRank = maxTensorRank;
60
61 Eigen::Index maxCost = std::numeric_limits<Eigen::Index>::max();
62 size_t bestDepth = numberOfLevels;
63 Lookahead(qubit, tensor1Id, tensor2Id, bestDepth, 0, 0, dummyTensors, 1,
64 maxCost, 0);
65
66 const auto &tensor = tensors[tensor1Id];
67 const auto resultRank = GetResultRank(tensor, tensors[tensor2Id]);
68
69 // also contract the dummies for the next step
70 ContractNodes(qubit, dummyTensors, tensor1Id, tensor2Id, resultRank);
71
72 maxTensorRank = saveMaxTensorRank;
73
74 ContractNodes(qubit, tensors, tensor1Id, tensor2Id, resultRank);
75
76 if (resultRank == 0) {
77 if (tensors.size() == 1 || tensor->contractsTheNeededQubit)
78 return std::real(tensor->tensor->atOffset(0));
79 // erasing this tensor happens because (not the case anymore, it's
80 // avoided) the tensor network might be a disjoint one and a subnetwork
81 // is contracted that does not contain the needed qubit
82 tensors.erase(tensor1Id);
83 dummyTensors.erase(tensor1Id);
84 }
85 }
86
87 return std::real(tensors.begin()->second->tensor->atOffset(0));
88 }
89
90 size_t GetNumberOfLevels() const { return numberOfLevels; }
91
92 void SetNumberOfLevels(size_t levels) { numberOfLevels = levels; }
93
94 bool GetUseMaxRankCost() const { return useMaxRankCost; }
95
96 void SetUseMaxRankCost(bool val = true) { useMaxRankCost = val; }
97
103 std::shared_ptr<ITensorContractor> Clone() const override {
104 auto cloned = std::make_shared<LookaheadContractor>();
105
106 cloned->maxTensorRank = maxTensorRank;
107 cloned->enableMultithreading = enableMultithreading;
108 cloned->numberOfLevels = numberOfLevels;
109 cloned->useMaxRankCost = useMaxRankCost;
110
111 return cloned;
112 }
113
114 private:
115 void Lookahead(Types::qubit_t qubit, Eigen::Index &tensor1Id,
116 Eigen::Index &tensor2Id, size_t &bestDepth, Eigen::Index curt1,
117 Eigen::Index curt2, DummyTensorsMap &dummyTensors,
118 size_t curLevel, Eigen::Index &maxCost,
119 Eigen::Index CostOnPath) {
120 if (dummyTensors.size() > 1 && curLevel < numberOfLevels) {
121 std::unordered_set<TensorPair, boost::hash<TensorPair>> visitedPairs;
122
123 for (auto tensorIt = dummyTensors.begin(); tensorIt != dummyTensors.end();
124 ++tensorIt) {
125 auto tensor = tensorIt->second;
126 const auto curTensorId = tensor->GetId();
127
128 assert(tensor->tensor->IsDummy());
129
130 // TODO: should I really try outer products here as well?
131 for (Eigen::Index ti = 0;
132 ti < static_cast<Eigen::Index>(tensor->connections.size()); ++ti) {
133 const auto nextTensorId = tensor->connections[ti];
134
135 if (nextTensorId != TensorNode::NotConnected) {
136 auto t1 = curTensorId;
137 auto t2 = nextTensorId;
138 if (t1 > t2) std::swap(t1, t2);
139 if (visitedPairs.find(std::make_pair(t1, t2)) != visitedPairs.end())
140 continue;
141
142 visitedPairs.insert(std::make_pair(t1, t2));
143
144 const Eigen::Index newRank =
145 GetResultRank(tensor, dummyTensors[nextTensorId]);
146 // const Eigen::Index Cost = newRank - (tensor->GetRank() +
147 // dummyTensors[nextTensorId]->GetRank()) / 2; const Eigen::Index
148 // newCostOnPath = CostOnPath + Cost;
149 const Eigen::Index newCostOnPath =
150 useMaxRankCost ? std::max(newRank, CostOnPath)
151 : CostOnPath + newRank;
152 if (newCostOnPath < maxCost) {
153 // 1. if the curLevel is not the last level
154 // contract the dummy tensors
155 // then recursively call Lookahead (with CostOnPath = max(newRank,
156 // CostOnPath)
157
158 // TODO: improve this, this is slow - actually save only the
159 // contracted tensors and restore them (and the linked ones as
160 // well) after the recursive call
161 // auto newTensors = CloneTensorsToDummy(dummyTensors);
162
163 auto saveTensor2 = dummyTensors[nextTensorId];
164
165 ContractNodes(qubit, dummyTensors, curTensorId, nextTensorId,
166 newRank);
167
168 Lookahead(qubit, tensor1Id, tensor2Id, bestDepth,
169 curLevel == 1 ? curTensorId : curt1,
170 curLevel == 1 ? nextTensorId : curt2, dummyTensors,
171 curLevel + 1, maxCost, newCostOnPath);
172
173 // then restore the contraction for the next iteration
174
175 dummyTensors[curTensorId] = tensor;
176 dummyTensors[nextTensorId] = saveTensor2;
177
178 // need to restore the connections back from the other connected
179 // tensors, too, because the contracting affected them
180
181 for (Eigen::Index i = 0;
182 i < static_cast<Eigen::Index>(tensor->connections.size());
183 ++i) {
184 if (tensor->connections[i] != TensorNode::NotConnected &&
185 tensor->connections[i] != nextTensorId) {
186 const auto connectedTensorId = tensor->connections[i];
187 const auto otherTensorIndex = tensor->connectionsIndices[i];
188 // dummyTensors[otherTensorId]->connections[otherTensorIndex]
189 // = curTensorId;
190 dummyTensors[connectedTensorId]
191 ->connectionsIndices[otherTensorIndex] = i;
192 }
193 }
194
195 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(
196 saveTensor2->connections.size());
197 ++i) {
198 if (saveTensor2->connections[i] != TensorNode::NotConnected &&
199 saveTensor2->connections[i] != curTensorId) {
200 const auto connectedTensorId = saveTensor2->connections[i];
201 const auto otherTensorIndex =
202 saveTensor2->connectionsIndices[i];
203 dummyTensors[connectedTensorId]
204 ->connections[otherTensorIndex] = nextTensorId;
205 dummyTensors[connectedTensorId]
206 ->connectionsIndices[otherTensorIndex] = i;
207 }
208 }
209
210 // otherwise ugly things will happen
211 // tensorIt =
212 // std::make_reverse_iterator(dummyTensors.find(curTensorId));
213 tensorIt = dummyTensors.find(curTensorId);
214 }
215 }
216 }
217 }
218 }
219
220 if ((curLevel == numberOfLevels && CostOnPath < maxCost) ||
221 (dummyTensors.size() <= 1 &&
222 (CostOnPath < maxCost ||
223 (maxCost == CostOnPath && curLevel < bestDepth)))) {
224 maxCost = CostOnPath;
225 bestDepth = curLevel;
226 // set the tensor1Id and tensor2Id to the best ones (so far)
227 tensor1Id = curt1;
228 tensor2Id = curt2;
229 }
230 }
231
232 static DummyTensorsMap CloneTensorsToDummy(const TensorsMap &tensors) {
233 DummyTensorsMap clonedTensors;
234 for (const auto &[tensorId, tensor] : tensors)
235 clonedTensors[tensorId] = tensor->CloneWithADummyTensor();
236
237 return clonedTensors;
238 }
239
240 size_t numberOfLevels = 3;
241 bool useMaxRankCost = true;
242};
243
244} // namespace TensorNetworks
245
246#endif // __LOOKAHEAD_CONTRACTOR_H_
The Base Class Tensor Contractor.
bool enableMultithreading
A flag to indicate if multithreading should be enabled.
ITensorContractor::TensorsMap TensorsMap
TensorsMap InitializeTensors(const TensorNetwork &network, Types::qubit_t qubit, std::vector< Eigen::Index > &keys, std::unordered_map< Eigen::Index, Eigen::Index > &keysKeys, bool fillKeys=true, bool contract=true) override
Eigen::Index ContractNodes(Types::qubit_t qubit, PassedTensorsMap &tensors, Eigen::Index tensor1Id, Eigen::Index tensor2Id, Eigen::Index resultRank)
size_t maxTensorRank
The maximum rank of the tensors in the network.
static size_t GetResultRank(const std::shared_ptr< TensorNode > &tensor1, const std::shared_ptr< TensorNode > &tensor2)
The Lookahead Tensor Contractor.
std::map< Eigen::Index, std::shared_ptr< TensorNode > > DummyTensorsMap
std::unordered_set< TensorPair, boost::hash< TensorPair > > VisitedPairType
std::pair< Eigen::Index, Eigen::Index > TensorPair
double Contract(const TensorNetwork &network, Types::qubit_t qubit) override
Contract the tensor network.
std::shared_ptr< ITensorContractor > Clone() const override
Clone the tensor contractor.
static constexpr Index NotConnected
Definition TensorNode.h:151