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