39 : rng(std::random_device{}()), uniformZeroOne(0, 1) {
40 lastTensors.resize(numQubits);
41 lastTensorsSuper.resize(numQubits);
42 lastTensorIndices.resize(numQubits);
43 lastTensorIndicesSuper.resize(numQubits);
45 qubitsMap.resize(numQubits);
67 const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
68 Types::qubit_t q1, Types::qubit_t q2 = 0) {
69 const auto gateQubitsNumber = gate.getQubitsNumber();
70 if (gateQubitsNumber > 1)
71 AddTwoQubitsGate(gate, q1, q2);
73 AddOneQubitGate(gate, q1);
77 if (!contractor)
return 0;
78 contractor->SetMultithreading(enableMultithreading);
82 const auto lastTensorIdOnQubit = lastTensors[qubit];
83 const auto lastTensorIndexOnQubit = lastTensorIndices[qubit];
91 const double result = contractor->Contract(*
this, qubit);
96 lastTensors[qubit] = lastTensorIdOnQubit;
97 lastTensorIndices[qubit] = lastTensorIndexOnQubit;
100 tensors.resize(tensors.size() - 1);
123 if (pauliString.empty())
125 else if (!contractor)
128 auto savedTensorsNrLocal = tensors.size();
129 auto saveLastTensorsLocal = lastTensors;
130 auto saveLastTensorIndicesLocal = lastTensorIndices;
133 const QC::Gates::PauliXGate<TensorNode::MatrixClass> XGate;
134 const QC::Gates::PauliYGate<TensorNode::MatrixClass> YGate;
135 const QC::Gates::PauliZGate<TensorNode::MatrixClass> ZGate;
137 std::unordered_set<Types::qubit_t> usedQubits;
139 for (Types::qubit_t q = 0; q < pauliString.size(); ++q) {
140 const char op = toupper(pauliString[q]);
143 AddOneQubitExpectationValueOp(XGate, q);
144 usedQubits.insert(q);
147 AddOneQubitExpectationValueOp(YGate, q);
148 usedQubits.insert(q);
151 AddOneQubitExpectationValueOp(ZGate, q);
152 usedQubits.insert(q);
161 if (usedQubits.empty())
return 1.;
163 const double result = Contract(usedQubits);
165 tensors.resize(savedTensorsNrLocal);
166 lastTensors.swap(saveLastTensorsLocal);
167 lastTensorIndices.swap(saveLastTensorIndicesLocal);
180 const bool expected = (outcome & mask) == 0;
187 const double prob = Contract();
196 const double prob = uniformZeroOne(rng);
210 auto tensorNode = std::make_shared<TensorNode>();
211 tensorNode->SetProjector(qubit, zero);
213 const auto newTensorId =
static_cast<Index>(tensors.size());
214 tensorNode->SetId(newTensorId);
223 const auto tensorOnQubitId = lastTensors[qubit];
224 const auto indexOnQubit = lastTensorIndices[qubit];
227 const auto &lastTensor = tensors[tensorOnQubitId];
228 lastTensor->connections[indexOnQubit] = newTensorId;
229 lastTensor->connectionsIndices[indexOnQubit] = 0;
233 tensorNode->connections[0] = tensorOnQubitId;
234 tensorNode->connectionsIndices[0] = indexOnQubit;
237 tensors.emplace_back(std::move(tensorNode));
240 lastTensors[qubit] = newTensorId;
241 lastTensorIndices[qubit] =
249 const double renorm = 1. / sqrt(prob);
251 projMat(0, 0) = renorm;
253 projMat(1, 1) = renorm;
255 const QC::Gates::SingleQubitGate<TensorNode::MatrixClass> projOp(projMat);
270 const std::vector<std::shared_ptr<TensorNode>> &
GetTensors()
const {
275 Types::qubit_t q)
const {
276 return qubitsGroups.at(qubitsMap[q]);
280 saveTensors.resize(tensors.size());
281 for (
size_t i = 0; i < tensors.size(); ++i)
282 saveTensors[i] = tensors[i]->CloneWithoutTensorCopy();
284 saveQubitsMap = qubitsMap;
285 saveQubitsGroups = qubitsGroups;
291 savedTensorsNr = tensors.size();
292 saveLastTensors = lastTensors;
293 saveLastTensorsSuper = lastTensorsSuper;
294 saveLastTensorIndices = lastTensorIndices;
295 saveLastTensorIndicesSuper = lastTensorIndicesSuper;
301 tensors.resize(saveTensors.size());
302 for (
size_t i = 0; i < tensors.size(); ++i)
303 tensors[i] = saveTensors[i]->CloneWithoutTensorCopy();
305 qubitsMap = saveQubitsMap;
306 qubitsGroups = saveQubitsGroups;
310 tensors.resize(savedTensorsNr);
311 lastTensors = saveLastTensors;
312 lastTensorsSuper = saveLastTensorsSuper;
313 lastTensorIndices = saveLastTensorIndices;
314 lastTensorIndicesSuper = saveLastTensorIndicesSuper;
318 tensors.swap(saveTensors);
320 qubitsMap.swap(saveQubitsMap);
321 qubitsGroups.swap(saveQubitsGroups);
329 tensors.resize(savedTensorsNr);
331 lastTensors.swap(saveLastTensors);
332 lastTensorsSuper.swap(saveLastTensorsSuper);
333 lastTensorIndices.swap(saveLastTensorIndices);
334 lastTensorIndicesSuper.swap(saveLastTensorIndicesSuper);
342 saveQubitsMap.clear();
343 saveQubitsGroups.clear();
349 saveLastTensors.clear();
350 saveLastTensorsSuper.clear();
352 saveLastTensorIndices.clear();
353 saveLastTensorIndicesSuper.clear();
359 const auto lastTensorId = lastTensors[q];
360 const auto lastTensorSuperId = lastTensorsSuper[q];
361 const auto &lastTensor = tensors[lastTensorId];
362 const auto &lastTensorSuper = tensors[lastTensorSuperId];
364 const auto tensorIndexOnQubit = lastTensorIndices[q];
365 const auto tensorSuperIndexOnQubit = lastTensorIndicesSuper[q];
368 lastTensor->connections[tensorIndexOnQubit] = lastTensorSuperId;
369 lastTensor->connectionsIndices[tensorIndexOnQubit] =
370 tensorSuperIndexOnQubit;
373 lastTensorSuper->connections[tensorSuperIndexOnQubit] = lastTensorId;
374 lastTensorSuper->connectionsIndices[tensorSuperIndexOnQubit] =
381 const auto lastTensorId = lastTensors[q];
382 const auto lastTensorSuperId = lastTensorsSuper[q];
383 const auto &lastTensor = tensors[lastTensorId];
384 const auto &lastTensorSuper = tensors[lastTensorSuperId];
386 const auto tensorIndexOnQubit = lastTensorIndices[q];
387 const auto tensorSuperIndexOnQubit = lastTensorIndicesSuper[q];
391 lastTensor->connectionsIndices[tensorIndexOnQubit] =
395 lastTensorSuper->connections[tensorSuperIndexOnQubit] =
397 lastTensorSuper->connectionsIndices[tensorSuperIndexOnQubit] =
411 enableMultithreading = multithreading;
423 std::unique_ptr<TensorNetwork>
Clone()
const {
424 auto cloned = std::make_unique<TensorNetwork>(0);
426 cloned->tensors = tensors;
428 cloned->lastTensors =
430 cloned->lastTensorsSuper =
434 cloned->lastTensorIndices =
436 cloned->lastTensorIndicesSuper =
437 lastTensorIndicesSuper;
440 cloned->qubitsMap = qubitsMap;
442 cloned->qubitsGroups = qubitsGroups;
445 cloned->savedTensorsNr = savedTensorsNr;
446 cloned->saveTensors = saveTensors;
448 cloned->saveLastTensors =
450 cloned->saveLastTensorsSuper =
451 saveLastTensorsSuper;
454 cloned->saveLastTensorIndices =
455 saveLastTensorIndices;
457 cloned->saveLastTensorIndicesSuper =
458 saveLastTensorIndicesSuper;
461 cloned->saveQubitsMap =
464 cloned->saveQubitsGroups =
468 if (contractor) cloned->contractor = contractor->Clone();
474 void AddOneQubitExpectationValueOp(
475 const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
477 auto tensorNode = std::make_shared<TensorNode>();
478 tensorNode->SetGate(gate, q);
480 auto lastTensorId = lastTensors[q];
481 const auto &lastTensor = tensors[lastTensorId];
489 auto lastTensorIndexForQubit = lastTensorIndices[q];
491 auto tensorId =
static_cast<Index>(tensors.size());
492 tensorNode->SetId(tensorId);
501 lastTensor->connections[lastTensorIndexForQubit] = tensorId;
502 lastTensor->connectionsIndices[lastTensorIndexForQubit] =
505 tensorNode->connections[0] = lastTensorId;
506 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
508 tensors.emplace_back(std::move(tensorNode));
511 lastTensors[q] = tensorId;
513 lastTensorIndices[q] = 1;
516 void AddOneQubitGate(
517 const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
518 Types::qubit_t q,
bool contractWithTwoQubitTensors =
false,
519 bool contract =
true) {
522 auto tensorNode = std::make_shared<TensorNode>();
523 tensorNode->SetGate(gate, q);
525 auto lastTensorId = lastTensors[q];
526 const auto &lastTensor = tensors[lastTensorId];
534 auto lastTensorIndexForQubit = lastTensorIndices[q];
542 (contractWithTwoQubitTensors || lastTensor->qubits.size() <= 2)) {
556 std::make_shared<Utils::Tensor<>>(lastTensor->tensor->Contract(
557 *(tensorNode->tensor), lastTensorIndexForQubit, 0,
558 enableMultithreading));
562 static const std::vector<size_t> indices{0, 1, 3, 2};
563 if (lastTensorIndexForQubit == 2) {
567 const auto otherQubit = lastTensor->qubits[3];
568 const auto otherTensorId = lastTensors[otherQubit];
570 std::swap(lastTensor->qubits[2], lastTensor->qubits[3]);
571 std::swap(lastTensor->connections[2], lastTensor->connections[3]);
572 std::swap(lastTensor->connectionsIndices[2],
573 lastTensor->connectionsIndices[3]);
575 lastTensorIndices[q] = 3;
577 if (otherTensorId == lastTensorId)
578 lastTensorIndices[otherQubit] = 2;
582 const auto &otherTensor =
583 tensors[lastTensor->connections[2]];
585 const auto otherIndex =
586 lastTensor->connectionsIndices[2];
587 otherTensor->connectionsIndices[otherIndex] =
592 tensorNode->SetSuper();
594 lastTensorId = lastTensorsSuper[q];
595 const auto &lastTensorSuper = tensors[lastTensorId];
596 lastTensorIndexForQubit = lastTensorIndicesSuper[q];
598 lastTensorSuper->tensor = std::make_shared<Utils::Tensor<>>(
599 std::move(lastTensorSuper->tensor->Contract(
600 *(tensorNode->tensor), lastTensorIndexForQubit, 0,
601 enableMultithreading)));
603 if (lastTensorIndexForQubit == 2) {
606 const auto otherQubit = lastTensorSuper->qubits[3];
607 const auto otherTensorId = lastTensorsSuper[otherQubit];
609 std::swap(lastTensorSuper->qubits[2], lastTensorSuper->qubits[3]);
610 std::swap(lastTensorSuper->connections[2],
611 lastTensorSuper->connections[3]);
612 std::swap(lastTensorSuper->connectionsIndices[2],
613 lastTensorSuper->connectionsIndices[3]);
615 lastTensorIndicesSuper[q] = 3;
617 if (otherTensorId == lastTensorId)
618 lastTensorIndicesSuper[otherQubit] = 2;
622 const auto &otherTensor =
623 tensors[lastTensorSuper->connections[2]];
625 const auto otherIndex =
626 lastTensorSuper->connectionsIndices[2];
627 otherTensor->connectionsIndices[otherIndex] =
632 auto tensorId =
static_cast<Index>(tensors.size());
633 tensorNode->SetId(tensorId);
642 lastTensor->connections[lastTensorIndexForQubit] = tensorId;
643 lastTensor->connectionsIndices[lastTensorIndexForQubit] =
646 tensorNode->connections[0] = lastTensorId;
647 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
649 tensors.emplace_back(std::move(tensorNode));
652 lastTensors[q] = tensorId;
654 lastTensorIndices[q] = 1;
658 tensorNode = std::make_shared<TensorNode>();
660 tensorNode->SetGate(gate, q);
661 tensorNode->SetSuper();
663 tensorId =
static_cast<Index>(tensors.size());
664 tensorNode->SetId(tensorId);
673 lastTensorId = lastTensorsSuper[q];
674 const auto &lastTensorSuper = tensors[lastTensorId];
675 lastTensorIndexForQubit = lastTensorIndicesSuper[q];
676 lastTensorSuper->connections[lastTensorIndexForQubit] = tensorId;
677 lastTensorSuper->connectionsIndices[lastTensorIndexForQubit] = 0;
679 tensorNode->connections[0] = lastTensorId;
680 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
682 tensors.emplace_back(std::move(tensorNode));
685 lastTensorsSuper[q] = tensorId;
687 lastTensorIndicesSuper[q] = 1;
691 void AddTwoQubitsGate(
692 const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
693 Types::qubit_t q1, Types::qubit_t q2) {
694 const size_t q1group = qubitsMap[q1];
695 const size_t q2group = qubitsMap[q2];
697 if (q1group != q2group) {
699 for (
const auto qg : qubitsGroups[q2group]) {
700 qubitsMap[qg] = q1group;
701 qubitsGroups[q1group].insert(qg);
703 qubitsGroups.erase(q2group);
710 auto tensorNode = std::make_shared<TensorNode>();
711 tensorNode->SetGate(gate, q1, q2);
713 auto tensorId =
static_cast<Index>(tensors.size());
714 tensorNode->SetId(tensorId);
723 auto lastTensorId = lastTensors[q1];
725 const auto &lastTensor = tensors[lastTensorId];
726 auto lastTensorIndexForQubit = lastTensorIndices[q1];
727 lastTensor->connections[lastTensorIndexForQubit] = tensorId;
728 lastTensor->connectionsIndices[lastTensorIndexForQubit] =
731 tensorNode->connections[0] = lastTensorId;
732 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
734 lastTensorId = lastTensors[q2];
735 const auto &lastTensor2 = tensors[lastTensorId];
736 lastTensorIndexForQubit = lastTensorIndices[q2];
737 lastTensor2->connections[lastTensorIndexForQubit] = tensorId;
738 lastTensor2->connectionsIndices[lastTensorIndexForQubit] =
741 tensorNode->connections[1] = lastTensorId;
742 tensorNode->connectionsIndices[1] = lastTensorIndices[q2];
744 tensors.emplace_back(std::move(tensorNode));
747 lastTensors[q1] = tensorId;
748 lastTensors[q2] = tensorId;
750 lastTensorIndices[q1] = 2;
751 lastTensorIndices[q2] = 3;
755 tensorNode = std::make_shared<TensorNode>();
757 tensorNode->SetGate(gate, q1, q2);
758 tensorNode->SetSuper();
760 tensorId =
static_cast<Index>(tensors.size());
761 tensorNode->SetId(tensorId);
770 lastTensorId = lastTensorsSuper[q1];
771 const auto &lastTensorSuper = tensors[lastTensorId];
772 lastTensorIndexForQubit = lastTensorIndicesSuper[q1];
773 lastTensorSuper->connections[lastTensorIndexForQubit] = tensorId;
774 lastTensorSuper->connectionsIndices[lastTensorIndexForQubit] = 0;
776 tensorNode->connections[0] = lastTensorId;
777 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
779 lastTensorId = lastTensorsSuper[q2];
780 const auto &lastTensor2Super = tensors[lastTensorId];
781 lastTensorIndexForQubit = lastTensorIndicesSuper[q2];
782 lastTensor2Super->connections[lastTensorIndexForQubit] = tensorId;
783 lastTensor2Super->connectionsIndices[lastTensorIndexForQubit] = 1;
785 tensorNode->connections[1] = lastTensorId;
786 tensorNode->connectionsIndices[1] = lastTensorIndicesSuper[q2];
788 tensors.emplace_back(std::move(tensorNode));
791 lastTensorsSuper[q1] = tensorId;
792 lastTensorsSuper[q2] = tensorId;
794 lastTensorIndicesSuper[q1] = 2;
795 lastTensorIndicesSuper[q2] = 3;
799 if (!contractor)
return 0;
800 contractor->SetMultithreading(enableMultithreading);
807 for (
auto &group : qubitsGroups) {
808 const double groupResult = contractor->Contract(
810 *group.second.begin());
812 if (groupResult == 0)
return 0;
813 result *= groupResult;
825 double Contract(
const std::unordered_set<Types::qubit_t> &qubits) {
826 if (!contractor)
return 0;
827 contractor->SetMultithreading(enableMultithreading);
834 for (
auto &group : qubitsGroups) {
836 for (
const auto q : group.second)
837 if (qubits.find(q) != qubits.end()) {
840 const double groupResult = contractor->Contract(
843 if (groupResult == 0)
return 0;
844 result *= groupResult;
858 void Clean(
size_t numQubits) { SetQubitsTensors(numQubits); }
860 void SetQubitsTensors(
size_t numQubits) {
861 for (Types::qubit_t q = 0; q < numQubits; ++q) {
863 qubitsGroups[q].insert(
866 auto tensorNode = std::make_shared<TensorNode>();
867 tensorNode->SetQubit(q);
868 tensorNode->SetId(
static_cast<Index>(tensors.size()));
870 lastTensors[q] = tensorNode->GetId();
871 lastTensorIndices[q] = 0;
872 tensors.emplace_back(std::move(tensorNode));
874 tensorNode = std::make_shared<TensorNode>();
875 tensorNode->SetQubit(q);
876 tensorNode->SetId(
static_cast<Index>(tensors.size()));
877 tensorNode->SetSuper();
879 lastTensorsSuper[q] = tensorNode->GetId();
880 lastTensorIndicesSuper[q] = 0;
881 tensors.emplace_back(std::move(tensorNode));
885 void SetQubitsTensorsClear(
size_t numQubits) {
886 for (Types::qubit_t q = 0; q < numQubits; ++q) {
888 qubitsGroups[q].insert(
891 lastTensors[q] = 2 * q;
892 lastTensorIndices[q] = 0;
894 lastTensorsSuper[q] = lastTensors[q] + 1;
895 lastTensorIndicesSuper[q] = 0;
899 std::vector<std::shared_ptr<TensorNode>>
909 std::vector<Index> lastTensorIndicesSuper;
912 std::vector<size_t> qubitsMap;
914 std::unordered_map<size_t, std::unordered_set<Types::qubit_t>>
918 size_t savedTensorsNr = 0;
919 std::vector<std::shared_ptr<TensorNode>>
924 std::vector<Index> saveLastTensorsSuper;
928 saveLastTensorIndices;
930 saveLastTensorIndicesSuper;
936 std::unordered_map<size_t, std::unordered_set<Types::qubit_t>>
940 std::shared_ptr<ITensorContractor> contractor;
942 bool enableMultithreading =
true;
945 std::uniform_real_distribution<double> uniformZeroOne;