29 void SetSeed(uint64_t seed) { rng.seed(seed); }
40 : rng(std::random_device{}()), uniformZeroOne(0, 1) {
41 lastTensors.resize(numQubits);
42 lastTensorsSuper.resize(numQubits);
43 lastTensorIndices.resize(numQubits);
44 lastTensorIndicesSuper.resize(numQubits);
46 qubitsMap.resize(numQubits);
68 const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
70 const auto gateQubitsNumber = gate.getQubitsNumber();
71 if (gateQubitsNumber > 1)
72 AddTwoQubitsGate(gate, q1, q2);
74 AddOneQubitGate(gate, q1);
78 if (!contractor)
return 0;
79 contractor->SetMultithreading(enableMultithreading);
83 const auto lastTensorIdOnQubit = lastTensors[qubit];
84 const auto lastTensorIndexOnQubit = lastTensorIndices[qubit];
92 const double result = contractor->Contract(*
this, qubit);
97 lastTensors[qubit] = lastTensorIdOnQubit;
98 lastTensorIndices[qubit] = lastTensorIndexOnQubit;
101 tensors.resize(tensors.size() - 1);
124 if (pauliString.empty())
126 else if (!contractor)
129 auto savedTensorsNrLocal = tensors.size();
130 auto saveLastTensorsLocal = lastTensors;
131 auto saveLastTensorIndicesLocal = lastTensorIndices;
134 const QC::Gates::PauliXGate<TensorNode::MatrixClass> XGate;
135 const QC::Gates::PauliYGate<TensorNode::MatrixClass> YGate;
136 const QC::Gates::PauliZGate<TensorNode::MatrixClass> ZGate;
138 std::unordered_set<Types::qubit_t> usedQubits;
141 const char op = toupper(pauliString[q]);
144 AddOneQubitExpectationValueOp(XGate, q);
145 usedQubits.insert(q);
148 AddOneQubitExpectationValueOp(YGate, q);
149 usedQubits.insert(q);
152 AddOneQubitExpectationValueOp(ZGate, q);
153 usedQubits.insert(q);
162 if (usedQubits.empty())
return 1.;
164 const double result = Contract(usedQubits);
166 tensors.resize(savedTensorsNrLocal);
167 lastTensors.swap(saveLastTensorsLocal);
168 lastTensorIndices.swap(saveLastTensorIndicesLocal);
181 const bool expected = (outcome & mask) == 0;
188 const double prob = Contract();
197 const double prob = uniformZeroOne(rng);
211 auto tensorNode = std::make_shared<TensorNode>();
212 tensorNode->SetProjector(qubit, zero);
214 const auto newTensorId =
static_cast<Index>(tensors.size());
215 tensorNode->SetId(newTensorId);
224 const auto tensorOnQubitId = lastTensors[qubit];
225 const auto indexOnQubit = lastTensorIndices[qubit];
228 const auto &lastTensor = tensors[tensorOnQubitId];
229 lastTensor->connections[indexOnQubit] = newTensorId;
230 lastTensor->connectionsIndices[indexOnQubit] = 0;
234 tensorNode->connections[0] = tensorOnQubitId;
235 tensorNode->connectionsIndices[0] = indexOnQubit;
238 tensors.emplace_back(std::move(tensorNode));
241 lastTensors[qubit] = newTensorId;
242 lastTensorIndices[qubit] =
250 const double renorm = 1. / sqrt(prob);
252 projMat(0, 0) = renorm;
254 projMat(1, 1) = renorm;
256 const QC::Gates::SingleQubitGate<TensorNode::MatrixClass> projOp(projMat);
271 const std::vector<std::shared_ptr<TensorNode>> &
GetTensors()
const {
277 return qubitsGroups.at(qubitsMap[q]);
281 saveTensors.resize(tensors.size());
282 for (
size_t i = 0; i < tensors.size(); ++i)
283 saveTensors[i] = tensors[i]->CloneWithoutTensorCopy();
285 saveQubitsMap = qubitsMap;
286 saveQubitsGroups = qubitsGroups;
292 savedTensorsNr = tensors.size();
293 saveLastTensors = lastTensors;
294 saveLastTensorsSuper = lastTensorsSuper;
295 saveLastTensorIndices = lastTensorIndices;
296 saveLastTensorIndicesSuper = lastTensorIndicesSuper;
302 tensors.resize(saveTensors.size());
303 for (
size_t i = 0; i < tensors.size(); ++i)
304 tensors[i] = saveTensors[i]->CloneWithoutTensorCopy();
306 qubitsMap = saveQubitsMap;
307 qubitsGroups = saveQubitsGroups;
311 tensors.resize(savedTensorsNr);
312 lastTensors = saveLastTensors;
313 lastTensorsSuper = saveLastTensorsSuper;
314 lastTensorIndices = saveLastTensorIndices;
315 lastTensorIndicesSuper = saveLastTensorIndicesSuper;
319 tensors.swap(saveTensors);
321 qubitsMap.swap(saveQubitsMap);
322 qubitsGroups.swap(saveQubitsGroups);
330 tensors.resize(savedTensorsNr);
332 lastTensors.swap(saveLastTensors);
333 lastTensorsSuper.swap(saveLastTensorsSuper);
334 lastTensorIndices.swap(saveLastTensorIndices);
335 lastTensorIndicesSuper.swap(saveLastTensorIndicesSuper);
343 saveQubitsMap.clear();
344 saveQubitsGroups.clear();
350 saveLastTensors.clear();
351 saveLastTensorsSuper.clear();
353 saveLastTensorIndices.clear();
354 saveLastTensorIndicesSuper.clear();
360 const auto lastTensorId = lastTensors[q];
361 const auto lastTensorSuperId = lastTensorsSuper[q];
362 const auto &lastTensor = tensors[lastTensorId];
363 const auto &lastTensorSuper = tensors[lastTensorSuperId];
365 const auto tensorIndexOnQubit = lastTensorIndices[q];
366 const auto tensorSuperIndexOnQubit = lastTensorIndicesSuper[q];
369 lastTensor->connections[tensorIndexOnQubit] = lastTensorSuperId;
370 lastTensor->connectionsIndices[tensorIndexOnQubit] =
371 tensorSuperIndexOnQubit;
374 lastTensorSuper->connections[tensorSuperIndexOnQubit] = lastTensorId;
375 lastTensorSuper->connectionsIndices[tensorSuperIndexOnQubit] =
382 const auto lastTensorId = lastTensors[q];
383 const auto lastTensorSuperId = lastTensorsSuper[q];
384 const auto &lastTensor = tensors[lastTensorId];
385 const auto &lastTensorSuper = tensors[lastTensorSuperId];
387 const auto tensorIndexOnQubit = lastTensorIndices[q];
388 const auto tensorSuperIndexOnQubit = lastTensorIndicesSuper[q];
392 lastTensor->connectionsIndices[tensorIndexOnQubit] =
396 lastTensorSuper->connections[tensorSuperIndexOnQubit] =
398 lastTensorSuper->connectionsIndices[tensorSuperIndexOnQubit] =
412 enableMultithreading = multithreading;
424 std::unique_ptr<TensorNetwork>
Clone()
const {
425 auto cloned = std::make_unique<TensorNetwork>(0);
427 cloned->tensors = tensors;
429 cloned->lastTensors =
431 cloned->lastTensorsSuper =
435 cloned->lastTensorIndices =
437 cloned->lastTensorIndicesSuper =
438 lastTensorIndicesSuper;
441 cloned->qubitsMap = qubitsMap;
443 cloned->qubitsGroups = qubitsGroups;
446 cloned->savedTensorsNr = savedTensorsNr;
447 cloned->saveTensors = saveTensors;
449 cloned->saveLastTensors =
451 cloned->saveLastTensorsSuper =
452 saveLastTensorsSuper;
455 cloned->saveLastTensorIndices =
456 saveLastTensorIndices;
458 cloned->saveLastTensorIndicesSuper =
459 saveLastTensorIndicesSuper;
462 cloned->saveQubitsMap =
465 cloned->saveQubitsGroups =
469 if (contractor) cloned->contractor = contractor->Clone();
475 void AddOneQubitExpectationValueOp(
476 const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
478 auto tensorNode = std::make_shared<TensorNode>();
479 tensorNode->SetGate(gate, q);
481 auto lastTensorId = lastTensors[q];
482 const auto &lastTensor = tensors[lastTensorId];
490 auto lastTensorIndexForQubit = lastTensorIndices[q];
492 auto tensorId =
static_cast<Index>(tensors.size());
493 tensorNode->SetId(tensorId);
502 lastTensor->connections[lastTensorIndexForQubit] = tensorId;
503 lastTensor->connectionsIndices[lastTensorIndexForQubit] =
506 tensorNode->connections[0] = lastTensorId;
507 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
509 tensors.emplace_back(std::move(tensorNode));
512 lastTensors[q] = tensorId;
514 lastTensorIndices[q] = 1;
517 void AddOneQubitGate(
518 const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
520 bool contract =
true) {
523 auto tensorNode = std::make_shared<TensorNode>();
524 tensorNode->SetGate(gate, q);
526 auto lastTensorId = lastTensors[q];
527 const auto &lastTensor = tensors[lastTensorId];
535 auto lastTensorIndexForQubit = lastTensorIndices[q];
543 (contractWithTwoQubitTensors || lastTensor->qubits.size() <= 2)) {
557 std::make_shared<Utils::Tensor<>>(lastTensor->tensor->Contract(
558 *(tensorNode->tensor), lastTensorIndexForQubit, 0,
559 enableMultithreading));
563 static const std::vector<size_t> indices{0, 1, 3, 2};
564 if (lastTensorIndexForQubit == 2) {
568 const auto otherQubit = lastTensor->qubits[3];
569 const auto otherTensorId = lastTensors[otherQubit];
571 std::swap(lastTensor->qubits[2], lastTensor->qubits[3]);
572 std::swap(lastTensor->connections[2], lastTensor->connections[3]);
573 std::swap(lastTensor->connectionsIndices[2],
574 lastTensor->connectionsIndices[3]);
576 lastTensorIndices[q] = 3;
578 if (otherTensorId == lastTensorId)
579 lastTensorIndices[otherQubit] = 2;
583 const auto &otherTensor =
584 tensors[lastTensor->connections[2]];
586 const auto otherIndex =
587 lastTensor->connectionsIndices[2];
588 otherTensor->connectionsIndices[otherIndex] =
593 tensorNode->SetSuper();
595 lastTensorId = lastTensorsSuper[q];
596 const auto &lastTensorSuper = tensors[lastTensorId];
597 lastTensorIndexForQubit = lastTensorIndicesSuper[q];
599 lastTensorSuper->tensor =
600 std::make_shared<Utils::Tensor<>>(lastTensorSuper->tensor->Contract(
601 *(tensorNode->tensor), lastTensorIndexForQubit, 0,
602 enableMultithreading));
604 if (lastTensorIndexForQubit == 2) {
607 const auto otherQubit = lastTensorSuper->qubits[3];
608 const auto otherTensorId = lastTensorsSuper[otherQubit];
610 std::swap(lastTensorSuper->qubits[2], lastTensorSuper->qubits[3]);
611 std::swap(lastTensorSuper->connections[2],
612 lastTensorSuper->connections[3]);
613 std::swap(lastTensorSuper->connectionsIndices[2],
614 lastTensorSuper->connectionsIndices[3]);
616 lastTensorIndicesSuper[q] = 3;
618 if (otherTensorId == lastTensorId)
619 lastTensorIndicesSuper[otherQubit] = 2;
623 const auto &otherTensor =
624 tensors[lastTensorSuper->connections[2]];
626 const auto otherIndex =
627 lastTensorSuper->connectionsIndices[2];
628 otherTensor->connectionsIndices[otherIndex] =
633 auto tensorId =
static_cast<Index>(tensors.size());
634 tensorNode->SetId(tensorId);
643 lastTensor->connections[lastTensorIndexForQubit] = tensorId;
644 lastTensor->connectionsIndices[lastTensorIndexForQubit] =
647 tensorNode->connections[0] = lastTensorId;
648 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
650 tensors.emplace_back(std::move(tensorNode));
653 lastTensors[q] = tensorId;
655 lastTensorIndices[q] = 1;
659 tensorNode = std::make_shared<TensorNode>();
661 tensorNode->SetGate(gate, q);
662 tensorNode->SetSuper();
664 tensorId =
static_cast<Index>(tensors.size());
665 tensorNode->SetId(tensorId);
674 lastTensorId = lastTensorsSuper[q];
675 const auto &lastTensorSuper = tensors[lastTensorId];
676 lastTensorIndexForQubit = lastTensorIndicesSuper[q];
677 lastTensorSuper->connections[lastTensorIndexForQubit] = tensorId;
678 lastTensorSuper->connectionsIndices[lastTensorIndexForQubit] = 0;
680 tensorNode->connections[0] = lastTensorId;
681 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
683 tensors.emplace_back(std::move(tensorNode));
686 lastTensorsSuper[q] = tensorId;
688 lastTensorIndicesSuper[q] = 1;
692 void AddTwoQubitsGate(
693 const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
695 const size_t q1group = qubitsMap[q1];
696 const size_t q2group = qubitsMap[q2];
698 if (q1group != q2group) {
700 for (
const auto qg : qubitsGroups[q2group]) {
701 qubitsMap[qg] = q1group;
702 qubitsGroups[q1group].insert(qg);
704 qubitsGroups.erase(q2group);
711 auto tensorNode = std::make_shared<TensorNode>();
712 tensorNode->SetGate(gate, q1, q2);
714 auto tensorId =
static_cast<Index>(tensors.size());
715 tensorNode->SetId(tensorId);
724 auto lastTensorId = lastTensors[q1];
726 const auto &lastTensor = tensors[lastTensorId];
727 auto lastTensorIndexForQubit = lastTensorIndices[q1];
728 lastTensor->connections[lastTensorIndexForQubit] = tensorId;
729 lastTensor->connectionsIndices[lastTensorIndexForQubit] =
732 tensorNode->connections[0] = lastTensorId;
733 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
735 lastTensorId = lastTensors[q2];
736 const auto &lastTensor2 = tensors[lastTensorId];
737 lastTensorIndexForQubit = lastTensorIndices[q2];
738 lastTensor2->connections[lastTensorIndexForQubit] = tensorId;
739 lastTensor2->connectionsIndices[lastTensorIndexForQubit] =
742 tensorNode->connections[1] = lastTensorId;
743 tensorNode->connectionsIndices[1] = lastTensorIndices[q2];
745 tensors.emplace_back(std::move(tensorNode));
748 lastTensors[q1] = tensorId;
749 lastTensors[q2] = tensorId;
751 lastTensorIndices[q1] = 2;
752 lastTensorIndices[q2] = 3;
756 tensorNode = std::make_shared<TensorNode>();
758 tensorNode->SetGate(gate, q1, q2);
759 tensorNode->SetSuper();
761 tensorId =
static_cast<Index>(tensors.size());
762 tensorNode->SetId(tensorId);
771 lastTensorId = lastTensorsSuper[q1];
772 const auto &lastTensorSuper = tensors[lastTensorId];
773 lastTensorIndexForQubit = lastTensorIndicesSuper[q1];
774 lastTensorSuper->connections[lastTensorIndexForQubit] = tensorId;
775 lastTensorSuper->connectionsIndices[lastTensorIndexForQubit] = 0;
777 tensorNode->connections[0] = lastTensorId;
778 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
780 lastTensorId = lastTensorsSuper[q2];
781 const auto &lastTensor2Super = tensors[lastTensorId];
782 lastTensorIndexForQubit = lastTensorIndicesSuper[q2];
783 lastTensor2Super->connections[lastTensorIndexForQubit] = tensorId;
784 lastTensor2Super->connectionsIndices[lastTensorIndexForQubit] = 1;
786 tensorNode->connections[1] = lastTensorId;
787 tensorNode->connectionsIndices[1] = lastTensorIndicesSuper[q2];
789 tensors.emplace_back(std::move(tensorNode));
792 lastTensorsSuper[q1] = tensorId;
793 lastTensorsSuper[q2] = tensorId;
795 lastTensorIndicesSuper[q1] = 2;
796 lastTensorIndicesSuper[q2] = 3;
800 if (!contractor)
return 0;
801 contractor->SetMultithreading(enableMultithreading);
808 for (
auto &group : qubitsGroups) {
809 const double groupResult = contractor->Contract(
811 *group.second.begin());
813 if (groupResult == 0)
return 0;
814 result *= groupResult;
826 double Contract(
const std::unordered_set<Types::qubit_t> &qubits) {
827 if (!contractor)
return 0;
828 contractor->SetMultithreading(enableMultithreading);
835 for (
auto &group : qubitsGroups) {
837 for (
const auto q : group.second)
838 if (qubits.find(q) != qubits.end()) {
841 const double groupResult = contractor->Contract(
844 if (groupResult == 0)
return 0;
845 result *= groupResult;
859 void Clean(
size_t numQubits) { SetQubitsTensors(numQubits); }
861 void SetQubitsTensors(
size_t numQubits) {
864 qubitsGroups[q].insert(
867 auto tensorNode = std::make_shared<TensorNode>();
868 tensorNode->SetQubit(q);
869 tensorNode->SetId(
static_cast<Index>(tensors.size()));
871 lastTensors[q] = tensorNode->GetId();
872 lastTensorIndices[q] = 0;
873 tensors.emplace_back(std::move(tensorNode));
875 tensorNode = std::make_shared<TensorNode>();
876 tensorNode->SetQubit(q);
877 tensorNode->SetId(
static_cast<Index>(tensors.size()));
878 tensorNode->SetSuper();
880 lastTensorsSuper[q] = tensorNode->GetId();
881 lastTensorIndicesSuper[q] = 0;
882 tensors.emplace_back(std::move(tensorNode));
886 void SetQubitsTensorsClear(
size_t numQubits) {
889 qubitsGroups[q].insert(
892 lastTensors[q] = 2 * q;
893 lastTensorIndices[q] = 0;
895 lastTensorsSuper[q] = lastTensors[q] + 1;
896 lastTensorIndicesSuper[q] = 0;
900 std::vector<std::shared_ptr<TensorNode>>
910 std::vector<Index> lastTensorIndicesSuper;
913 std::vector<size_t> qubitsMap;
915 std::unordered_map<size_t, std::unordered_set<Types::qubit_t>>
919 size_t savedTensorsNr = 0;
920 std::vector<std::shared_ptr<TensorNode>>
925 std::vector<Index> saveLastTensorsSuper;
929 saveLastTensorIndices;
931 saveLastTensorIndicesSuper;
937 std::unordered_map<size_t, std::unordered_set<Types::qubit_t>>
941 std::shared_ptr<ITensorContractor> contractor;
943 bool enableMultithreading =
true;
946 std::uniform_real_distribution<double> uniformZeroOne;