Maestro 0.1.0
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
LookaheadContractor.h
Go to the documentation of this file.
1
13
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
31public:
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
114private:
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)
139 std::swap(t1, t2);
140 if (visitedPairs.find(std::make_pair(t1, t2)) != visitedPairs.end())
141 continue;
142
143 visitedPairs.insert(std::make_pair(t1, t2));
144
145 const Eigen::Index newRank =
146 GetResultRank(tensor, dummyTensors[nextTensorId]);
147 // const Eigen::Index Cost = newRank - (tensor->GetRank() +
148 // dummyTensors[nextTensorId]->GetRank()) / 2; const Eigen::Index
149 // newCostOnPath = CostOnPath + Cost;
150 const Eigen::Index newCostOnPath =
151 useMaxRankCost ? std::max(newRank, CostOnPath)
152 : CostOnPath + newRank;
153 if (newCostOnPath < maxCost) {
154 // 1. if the curLevel is not the last level
155 // contract the dummy tensors
156 // then recursively call Lookahead (with CostOnPath = max(newRank,
157 // CostOnPath)
158
159 // TODO: improve this, this is slow - actually save only the
160 // contracted tensors and restore them (and the linked ones as
161 // well) after the recursive call
162 // auto newTensors = CloneTensorsToDummy(dummyTensors);
163
164 auto saveTensor2 = dummyTensors[nextTensorId];
165
166 ContractNodes(qubit, dummyTensors, curTensorId, nextTensorId,
167 newRank);
168
169 Lookahead(qubit, tensor1Id, tensor2Id, bestDepth,
170 curLevel == 1 ? curTensorId : curt1,
171 curLevel == 1 ? nextTensorId : curt2, dummyTensors,
172 curLevel + 1, maxCost, newCostOnPath);
173
174 // then restore the contraction for the next iteration
175
176 dummyTensors[curTensorId] = tensor;
177 dummyTensors[nextTensorId] = saveTensor2;
178
179 // need to restore the connections back from the other connected
180 // tensors, too, because the contracting affected them
181
182 for (Eigen::Index i = 0;
183 i < static_cast<Eigen::Index>(tensor->connections.size());
184 ++i) {
185 if (tensor->connections[i] != TensorNode::NotConnected &&
186 tensor->connections[i] != nextTensorId) {
187 const auto connectedTensorId = tensor->connections[i];
188 const auto otherTensorIndex = tensor->connectionsIndices[i];
189 // dummyTensors[otherTensorId]->connections[otherTensorIndex]
190 // = curTensorId;
191 dummyTensors[connectedTensorId]
192 ->connectionsIndices[otherTensorIndex] = i;
193 }
194 }
195
196 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(
197 saveTensor2->connections.size());
198 ++i) {
199 if (saveTensor2->connections[i] != TensorNode::NotConnected &&
200 saveTensor2->connections[i] != curTensorId) {
201 const auto connectedTensorId = saveTensor2->connections[i];
202 const auto otherTensorIndex =
203 saveTensor2->connectionsIndices[i];
204 dummyTensors[connectedTensorId]
205 ->connections[otherTensorIndex] = nextTensorId;
206 dummyTensors[connectedTensorId]
207 ->connectionsIndices[otherTensorIndex] = i;
208 }
209 }
210
211 // otherwise ugly things will happen
212 // tensorIt =
213 // std::make_reverse_iterator(dummyTensors.find(curTensorId));
214 tensorIt = dummyTensors.find(curTensorId);
215 }
216 }
217 }
218 }
219 }
220
221 if ((curLevel == numberOfLevels && CostOnPath < maxCost) ||
222 (dummyTensors.size() <= 1 &&
223 (CostOnPath < maxCost ||
224 (maxCost == CostOnPath && curLevel < bestDepth)))) {
225 maxCost = CostOnPath;
226 bestDepth = curLevel;
227 // set the tensor1Id and tensor2Id to the best ones (so far)
228 tensor1Id = curt1;
229 tensor2Id = curt2;
230 }
231 }
232
233 static DummyTensorsMap CloneTensorsToDummy(const TensorsMap &tensors) {
234 DummyTensorsMap clonedTensors;
235 for (const auto &[tensorId, tensor] : tensors)
236 clonedTensors[tensorId] = tensor->CloneWithADummyTensor();
237
238 return clonedTensors;
239 }
240
241 size_t numberOfLevels = 3;
242 bool useMaxRankCost = true;
243};
244
245} // namespace TensorNetworks
246
247#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::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.
std::unordered_set< TensorPair, boost::hash< TensorPair > > VisitedPairType
static constexpr Index NotConnected
Definition TensorNode.h:155
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:20