Maestro 0.1.0
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
StochasticContractor.h
Go to the documentation of this file.
1
14#pragma once
15
16#ifndef __STOCHASTIC_CONTRACTOR_H_
17#define __STOCHASTIC_CONTRACTOR_H_ 1
18
19#include "BaseContractor.h"
20
21#include <boost/container_hash/hash.hpp>
22#include <random>
23
24namespace TensorNetworks {
25
32 public:
39 StochasticContractor() : gen(std::random_device{}()) {}
40
47 double Contract(const TensorNetwork &network, Types::qubit_t qubit) override {
48 // algorithm very similar with the 'Algorithm 2' from arXiv:1709.03636v2
49 // [quant-ph] 22 Dec 2018 qTorch: The quantum tensor contraction handler
50 // there are some changes, though, in picking up the contraction
51
52 long long int CostThreshold = -1;
53 size_t rejections = 0;
54
55 std::vector<Eigen::Index> keys;
56 std::unordered_map<Eigen::Index, Eigen::Index> keysKeys;
57
58 auto tensors = InitializeTensors(network, qubit, keys, keysKeys);
59
60 using TensorPair = std::pair<Eigen::Index, Eigen::Index>;
61 std::unordered_set<TensorPair, boost::hash<TensorPair>> visitedPairs;
62
63 // while there is more than one tensor...
64 while (tensors.size() > 1) {
65 // choose a random tensor
66 std::uniform_int_distribution<Eigen::Index> tensor1Dist(
67 0LL, static_cast<Eigen::Index>(keys.size()) - 1);
68 auto pos1 = tensor1Dist(gen);
69 Eigen::Index tensor1Id = keys[pos1];
70 const auto tensor1 = tensors[tensor1Id];
71
72 // then a random tensor it is connected to
73 // this favors the ones that have more connection with the chosen one
74
75 std::uniform_int_distribution<Eigen::Index> tensor2Dist(
76 0LL, static_cast<Eigen::Index>(tensor1->connections.size()) - 1);
77 auto pos2 = tensor2Dist(gen);
78 Eigen::Index tensor2Id = tensor1->connections[pos2];
79
80 auto t1 = tensor1Id;
81 auto t2 = tensor2Id;
82 if (t1 > t2) std::swap(t1, t2);
83 if (visitedPairs.find(std::make_pair(t1, t2)) != visitedPairs.end()) {
84 ++rejections;
85 if (rejections >= MaxRejections) {
86 ++CostThreshold;
87 rejections = 0;
88 visitedPairs.clear();
89 }
90 continue;
91 } else
92 visitedPairs.insert(std::make_pair(t1, t2));
93
94 const auto tensor2 = tensors[tensor2Id];
95
96 const auto keysKeysIt = keysKeys.find(tensor2Id);
97 if (keysKeysIt != keysKeys.end()) pos2 = keysKeysIt->second;
98
99 const Eigen::Index resultRank = GetResultRank(tensor1, tensor2);
100 // const Eigen::Index Cost = resultRank - std::max(tensor1->GetRank(),
101 // tensor2->GetRank());
102 const Eigen::Index Cost =
103 resultRank - (tensor1->GetRank() + tensor2->GetRank()) / 2;
104 if (Cost <= CostThreshold) {
105 rejections = 0;
106 CostThreshold = -1;
107
108 ContractNodes(qubit, tensors, tensor1Id, tensor2Id, resultRank);
109
110 visitedPairs.clear();
111
112 // also remove them from 'keys' vector
113 // actually only one needs to be removed, the other one will be replaced
114 // by the result tensor
115 if (pos1 == static_cast<Eigen::Index>(keys.size()) - 1) pos1 = pos2;
116
117 keys[pos2] = keys.back();
118 keysKeys[keys[pos2]] = pos2;
119 keysKeys.erase(tensor2Id);
120 keys.resize(keys.size() - 1);
121
122 if (resultRank == 0) {
123 if (tensors.size() == 1 ||
124 tensors[tensor1Id]->contractsTheNeededQubit)
125 return std::real(tensors[tensor1Id]->tensor->atOffset(0));
126
127 // erasing this tensor happens because (not the case anymore, it's
128 // avoided) the tensor network might be a disjoint one and a
129 // subnetwork is contracted that does not contain the needed qubit
130 keys[pos1] = keys.back();
131 keysKeys[keys[pos1]] = pos1;
132 keysKeys.erase(tensor1Id);
133 keys.resize(keys.size() - 1);
134
135 tensors.erase(tensor1Id);
136 }
137 } else {
138 ++rejections;
139 if (rejections >= MaxRejections) {
140 ++CostThreshold;
141 rejections = 0;
142 visitedPairs.clear();
143 }
144 }
145 }
146
147 return std::real(tensors.begin()->second->tensor->atOffset(0));
148 }
149
150 size_t GetMaxRejections() const { return MaxRejections; }
151
152 void SetMaxRejections(size_t maxRejections) { MaxRejections = maxRejections; }
153
159 std::shared_ptr<ITensorContractor> Clone() const override {
160 auto cloned = std::make_shared<StochasticContractor>();
161
162 cloned->maxTensorRank = maxTensorRank;
163 cloned->enableMultithreading = enableMultithreading;
164 cloned->MaxRejections = MaxRejections;
165
166 return cloned;
167 }
168
169 protected:
170 std::mt19937 gen;
171 size_t MaxRejections = 15;
172};
173
174} // namespace TensorNetworks
175
176#endif // __STOCHASTIC_CONTRACTOR_H_
The Base Class Tensor Contractor.
bool enableMultithreading
A flag to indicate if multithreading should be enabled.
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 Stochastic Tensor Contractor.
std::shared_ptr< ITensorContractor > Clone() const override
Clone the tensor contractor.
void SetMaxRejections(size_t maxRejections)
double Contract(const TensorNetwork &network, Types::qubit_t qubit) override
Contract the tensor network.