Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
BaseContractor.h
Go to the documentation of this file.
1
11
12#pragma once
13
14#ifndef __BASE_CONTRACTOR_H_
15#define __BASE_CONTRACTOR_H_ 1
16
17#include "../TensorNetwork.h"
18#include "TensorContractor.h"
19
20#include <random>
21
23{
24
31{
32 public:
34
35 TensorsMap InitializeTensors(const TensorNetwork &network, Types::qubit_t qubit, std::vector<Eigen::Index> &keys,
36 std::unordered_map<Eigen::Index, Eigen::Index> &keysKeys, bool fillKeys = true, bool contract = true) override
37 {
38 maxTensorRank = 0;
39
40 TensorsMap tensors;
41
42 if (fillKeys)
43 keys.reserve(network.GetTensors().size());
44
45 const auto &qubitGroup = network.GetQubitGroup(qubit);
46
47 if (contract)
48 {
49 for (const auto &tensor : network.GetTensors())
50 {
51 if (!tensor)
52 continue;
53
54 const auto firstQubit = tensor->qubits[0];
55 if (qubitGroup.find(firstQubit) == qubitGroup.end())
56 continue;
57
58 const auto tensorId = tensor->GetId();
59 tensors[tensorId] = tensor->CloneWithoutTensorCopy();
60 }
61
62 for (const auto &[tensor1Id, tensor1] : tensors)
63 {
64 // check the tensor, see if it can be contracted, contract it if
65 // possible
66 const auto qubitsNr = tensor1->qubits.size();
67 if (qubitsNr <= 2)
68 {
69 // try to contract with something either that is in a previous
70 // position or something following (there should be always something
71 // following)
72 const auto tensor2Id = tensor1->connections[0];
73 const auto &tensor2 = tensors[tensor2Id];
74
75 const auto resultRank = GetResultRank(tensor1, tensor2);
76 ContractNodes(qubit, tensors, tensor1Id, tensor2Id, resultRank);
77
78 tensors.erase(tensor2Id);
79 }
80 else
81 {
82 // could be preceeded by another two qubit tensor that can be
83 // contracted without increasing the rank (that is, two two qubit
84 // gates on the same qubits)
85 const auto tensor2Id = tensor1->connections[0];
86 const auto tensor3Id = tensor1->connections[1];
87
88 if (tensor2Id == tensor3Id)
89 {
90 const auto &tensor2 = tensors[tensor2Id];
91
92 const auto resultRank = GetResultRank(tensor1, tensor2);
93 ContractNodes(qubit, tensors, tensor1Id, tensor2Id, resultRank);
94
95 tensors.erase(tensor2Id);
96 }
97 }
98 }
99
100 // now build the keys
101 for (const auto &[tensorId, tensor] : tensors)
102 {
103 if (fillKeys)
104 {
105 keysKeys[tensorId] = keys.size();
106 keys.push_back(tensorId);
107 }
108
109 maxTensorRank = std::max(maxTensorRank, tensor->GetRank());
110 }
111 }
112 else
113 {
114 for (const auto &tensor : network.GetTensors())
115 {
116 if (!tensor)
117 continue;
118
119 const auto firstQubit = tensor->qubits[0];
120 if (qubitGroup.find(firstQubit) == qubitGroup.end())
121 continue;
122
123 const auto tensorId = tensor->GetId();
124 tensors[tensorId] = tensor->CloneWithoutTensorCopy();
125
126 if (fillKeys)
127 {
128 keysKeys[tensorId] = keys.size();
129 keys.push_back(tensorId);
130 }
131
132 maxTensorRank = std::max(maxTensorRank, tensor->GetRank());
133 }
134 }
135
136 return tensors;
137 }
138
139 template <class PassedTensorsMap = TensorsMap>
140 inline Eigen::Index ContractNodes(Types::qubit_t qubit, PassedTensorsMap &tensors, Eigen::Index tensor1Id, Eigen::Index tensor2Id, Eigen::Index resultRank)
141 {
142 const auto &tensor1 = tensors[tensor1Id];
143 const auto &tensor2 = tensors[tensor2Id];
144
145 // contract them to get the result tensor
146 std::vector<std::pair<size_t, size_t>> indices;
147 for (size_t i = 0; i < tensor1->connections.size(); ++i)
148 if (tensor1->connections[i] == tensor2Id)
149 indices.emplace_back(i, tensor1->connectionsIndices[i]);
150
151 maxTensorRank = std::max<size_t>(maxTensorRank, resultRank);
152
153 const auto resultNode = std::make_shared<TensorNode>();
154 resultNode->tensor = std::make_shared<Utils::Tensor<>>(tensor1->tensor->Contract(*(tensor2->tensor), indices, enableMultithreading));
155 resultNode->SetId(tensor1Id);
156
157 const auto newRank = resultNode->GetRank();
158
159 resultNode->connections.resize(newRank);
160 resultNode->connectionsIndices.resize(newRank);
161 resultNode->qubits.resize(newRank);
162
163 // update tensors graph with the new result, removing the two old ones
164
165 // for the old ones, all connections of other tensors must be updated to
166 // point to the result tensor pay attention to the proper indices positions
167
168 // the first indices of the new tensor come from the first tensor, the
169 // following ones from the second tensor
170 bool properQubit = false;
171
172 size_t pos = 0;
173 for (size_t i = 0; i < tensor1->connections.size(); ++i)
174 {
175 if (qubit == tensor1->qubits[i])
176 properQubit = true;
177
178 const auto connectedTensorId = tensor1->connections[i];
179 if (connectedTensorId != tensor2Id)
180 {
181 // update connection of the tensor
182 const auto otherTensorIndex = tensor1->connectionsIndices[i];
183
184 resultNode->connections[pos] = connectedTensorId;
185 resultNode->connectionsIndices[pos] = otherTensorIndex;
186 resultNode->qubits[pos] = tensor1->qubits[i];
187
188 // also need to update the 'other' tensor that is connected to the first
189 // tensor
190 // tensors[connectedTensorId]->connections[otherTensorIndex] =
191 // tensor1Id; // no need to update this, the new tensor inherits the id
192 // from tensor1
193 tensors[connectedTensorId]->connectionsIndices[otherTensorIndex] = pos;
194
195 ++pos;
196 }
197 }
198
199 for (size_t i = 0; i < tensor2->connections.size(); ++i)
200 {
201 if (qubit == tensor2->qubits[i])
202 properQubit = true;
203
204 const auto connectedTensorId = tensor2->connections[i];
205 if (connectedTensorId != tensor1Id)
206 {
207 // update connection of the tensor
208 const auto otherTensorIndex = tensor2->connectionsIndices[i];
209
210 resultNode->connections[pos] = connectedTensorId;
211 resultNode->connectionsIndices[pos] = otherTensorIndex;
212 resultNode->qubits[pos] = tensor2->qubits[i];
213
214 // also need to update the 'other' tensor that is connected to the
215 // second tensor
216 tensors[connectedTensorId]->connections[otherTensorIndex] = tensor1Id;
217 tensors[connectedTensorId]->connectionsIndices[otherTensorIndex] = pos;
218
219 ++pos;
220 }
221 }
222
223 resultNode->contractsTheNeededQubit = properQubit || tensor1->contractsTheNeededQubit || tensor2->contractsTheNeededQubit;
224
225 // now update the tensors map
226 tensors.erase(tensor2Id);
227 tensors[tensor1Id] = resultNode;
228
229 return tensor1Id;
230 }
231
232 static inline size_t GetResultRank(const std::shared_ptr<TensorNode> &tensor1, const std::shared_ptr<TensorNode> &tensor2)
233 {
234 size_t rank = 0;
235
236 const Eigen::Index tensor1Id = tensor1->GetId();
237 const Eigen::Index tensor2Id = tensor2->GetId();
238
239 for (size_t i = 0; i < tensor1->connections.size(); ++i)
240 if (tensor1->connections[i] != tensor2Id)
241 ++rank;
242
243 for (size_t i = 0; i < tensor2->connections.size(); ++i)
244 if (tensor2->connections[i] != tensor1Id)
245 ++rank;
246
247 // even if the true rank of the result is 1 in such a case, return 0 as it
248 // has inside only a scalar
249 // if (rank == 0) rank = 1;
250
251 return rank;
252 }
253
254 size_t GetMaxTensorRank() const override
255 {
256 return maxTensorRank;
257 }
258
267 void SetMultithreading(bool multithreading = true) override
268 {
269 enableMultithreading = multithreading;
270 }
271
279 bool GetMultithreading() const override
280 {
282 }
283
284 protected:
285 size_t maxTensorRank = 0;
287};
288
289} // namespace TensorNetworks
290
291#endif // __BASE_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 GetMaxTensorRank() const override
void SetMultithreading(bool multithreading=true) override
Enable/disable multithreading.
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)
bool GetMultithreading() const override
Get the multithreading flag.
Tensor Contractor interface.
std::unordered_map< Eigen::Index, std::shared_ptr< TensorNode > > TensorsMap
const std::unordered_set< Types::qubit_t > & GetQubitGroup(Types::qubit_t q) const
const std::vector< std::shared_ptr< TensorNode > > & GetTensors() const
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:23