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