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