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 AddGate(
const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
69 const auto gateQubitsNumber = gate.getQubitsNumber();
70 if (gateQubitsNumber > 1)
71 AddTwoQubitsGate(gate, q1, q2);
73 AddOneQubitGate(gate, q1);
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())
165 const double result = Contract(usedQubits);
167 tensors.resize(savedTensorsNrLocal);
168 lastTensors.swap(saveLastTensorsLocal);
169 lastTensorIndices.swap(saveLastTensorIndicesLocal);
182 const bool expected = (outcome & mask) == 0;
189 const double prob = Contract();
198 const double prob = uniformZeroOne(rng);
212 auto tensorNode = std::make_shared<TensorNode>();
213 tensorNode->SetProjector(qubit, zero);
215 const auto newTensorId =
static_cast<Index>(tensors.size());
216 tensorNode->SetId(newTensorId);
225 const auto tensorOnQubitId = lastTensors[qubit];
226 const auto indexOnQubit = lastTensorIndices[qubit];
229 const auto &lastTensor = tensors[tensorOnQubitId];
230 lastTensor->connections[indexOnQubit] = newTensorId;
231 lastTensor->connectionsIndices[indexOnQubit] = 0;
235 tensorNode->connections[0] = tensorOnQubitId;
236 tensorNode->connectionsIndices[0] = indexOnQubit;
239 tensors.emplace_back(std::move(tensorNode));
242 lastTensors[qubit] = newTensorId;
243 lastTensorIndices[qubit] =
251 const double renorm = 1. / sqrt(prob);
253 projMat(0, 0) = renorm;
255 projMat(1, 1) = renorm;
257 const QC::Gates::SingleQubitGate<TensorNode::MatrixClass> projOp(projMat);
272 const std::vector<std::shared_ptr<TensorNode>> &
GetTensors()
const {
276 const std::unordered_set<Types::qubit_t> &
278 return qubitsGroups.at(qubitsMap[q]);
282 saveTensors.resize(tensors.size());
283 for (
size_t i = 0; i < tensors.size(); ++i)
284 saveTensors[i] = tensors[i]->CloneWithoutTensorCopy();
286 saveQubitsMap = qubitsMap;
287 saveQubitsGroups = qubitsGroups;
293 savedTensorsNr = tensors.size();
294 saveLastTensors = lastTensors;
295 saveLastTensorsSuper = lastTensorsSuper;
296 saveLastTensorIndices = lastTensorIndices;
297 saveLastTensorIndicesSuper = lastTensorIndicesSuper;
303 tensors.resize(saveTensors.size());
304 for (
size_t i = 0; i < tensors.size(); ++i)
305 tensors[i] = saveTensors[i]->CloneWithoutTensorCopy();
307 qubitsMap = saveQubitsMap;
308 qubitsGroups = saveQubitsGroups;
312 tensors.resize(savedTensorsNr);
313 lastTensors = saveLastTensors;
314 lastTensorsSuper = saveLastTensorsSuper;
315 lastTensorIndices = saveLastTensorIndices;
316 lastTensorIndicesSuper = saveLastTensorIndicesSuper;
320 tensors.swap(saveTensors);
322 qubitsMap.swap(saveQubitsMap);
323 qubitsGroups.swap(saveQubitsGroups);
331 tensors.resize(savedTensorsNr);
333 lastTensors.swap(saveLastTensors);
334 lastTensorsSuper.swap(saveLastTensorsSuper);
335 lastTensorIndices.swap(saveLastTensorIndices);
336 lastTensorIndicesSuper.swap(saveLastTensorIndicesSuper);
344 saveQubitsMap.clear();
345 saveQubitsGroups.clear();
351 saveLastTensors.clear();
352 saveLastTensorsSuper.clear();
354 saveLastTensorIndices.clear();
355 saveLastTensorIndicesSuper.clear();
361 const auto lastTensorId = lastTensors[q];
362 const auto lastTensorSuperId = lastTensorsSuper[q];
363 const auto &lastTensor = tensors[lastTensorId];
364 const auto &lastTensorSuper = tensors[lastTensorSuperId];
366 const auto tensorIndexOnQubit = lastTensorIndices[q];
367 const auto tensorSuperIndexOnQubit = lastTensorIndicesSuper[q];
370 lastTensor->connections[tensorIndexOnQubit] = lastTensorSuperId;
371 lastTensor->connectionsIndices[tensorIndexOnQubit] =
372 tensorSuperIndexOnQubit;
375 lastTensorSuper->connections[tensorSuperIndexOnQubit] = lastTensorId;
376 lastTensorSuper->connectionsIndices[tensorSuperIndexOnQubit] =
383 const auto lastTensorId = lastTensors[q];
384 const auto lastTensorSuperId = lastTensorsSuper[q];
385 const auto &lastTensor = tensors[lastTensorId];
386 const auto &lastTensorSuper = tensors[lastTensorSuperId];
388 const auto tensorIndexOnQubit = lastTensorIndices[q];
389 const auto tensorSuperIndexOnQubit = lastTensorIndicesSuper[q];
393 lastTensor->connectionsIndices[tensorIndexOnQubit] =
397 lastTensorSuper->connections[tensorSuperIndexOnQubit] =
399 lastTensorSuper->connectionsIndices[tensorSuperIndexOnQubit] =
413 enableMultithreading = multithreading;
425 std::unique_ptr<TensorNetwork>
Clone()
const {
426 auto cloned = std::make_unique<TensorNetwork>(0);
428 cloned->tensors = tensors;
430 cloned->lastTensors =
432 cloned->lastTensorsSuper = 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;
457 cloned->saveLastTensorIndicesSuper =
458 saveLastTensorIndicesSuper;
461 cloned->saveQubitsMap =
464 cloned->saveQubitsGroups =
469 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)) {
556 lastTensor->tensor = std::make_shared<Utils::Tensor<>>(
557 std::move(lastTensor->tensor->Contract(*(tensorNode->tensor),
558 lastTensorIndexForQubit, 0,
559 enableMultithreading)));
564 static const std::vector<size_t> indices{0, 1, 3, 2};
565 if (lastTensorIndexForQubit == 2) {
569 const auto otherQubit = lastTensor->qubits[3];
570 const auto otherTensorId = lastTensors[otherQubit];
572 std::swap(lastTensor->qubits[2], lastTensor->qubits[3]);
573 std::swap(lastTensor->connections[2], lastTensor->connections[3]);
574 std::swap(lastTensor->connectionsIndices[2],
575 lastTensor->connectionsIndices[3]);
577 lastTensorIndices[q] = 3;
579 if (otherTensorId == lastTensorId)
580 lastTensorIndices[otherQubit] = 2;
584 const auto &otherTensor =
585 tensors[lastTensor->connections[2]];
587 const auto otherIndex =
588 lastTensor->connectionsIndices[2];
589 otherTensor->connectionsIndices[otherIndex] =
594 tensorNode->SetSuper();
596 lastTensorId = lastTensorsSuper[q];
597 const auto &lastTensorSuper = tensors[lastTensorId];
598 lastTensorIndexForQubit = lastTensorIndicesSuper[q];
600 lastTensorSuper->tensor = std::make_shared<Utils::Tensor<>>(std::move(
601 lastTensorSuper->tensor->Contract(*(tensorNode->tensor),
602 lastTensorIndexForQubit, 0,
603 enableMultithreading)));
605 if (lastTensorIndexForQubit == 2) {
608 const auto otherQubit = lastTensorSuper->qubits[3];
609 const auto otherTensorId = lastTensorsSuper[otherQubit];
611 std::swap(lastTensorSuper->qubits[2], lastTensorSuper->qubits[3]);
612 std::swap(lastTensorSuper->connections[2],
613 lastTensorSuper->connections[3]);
614 std::swap(lastTensorSuper->connectionsIndices[2],
615 lastTensorSuper->connectionsIndices[3]);
617 lastTensorIndicesSuper[q] = 3;
619 if (otherTensorId == lastTensorId)
620 lastTensorIndicesSuper[otherQubit] = 2;
624 const auto &otherTensor =
625 tensors[lastTensorSuper->connections[2]];
627 const auto otherIndex =
628 lastTensorSuper->connectionsIndices[2];
629 otherTensor->connectionsIndices[otherIndex] =
634 auto tensorId =
static_cast<Index>(tensors.size());
635 tensorNode->SetId(tensorId);
644 lastTensor->connections[lastTensorIndexForQubit] = tensorId;
645 lastTensor->connectionsIndices[lastTensorIndexForQubit] =
648 tensorNode->connections[0] = lastTensorId;
649 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
651 tensors.emplace_back(std::move(tensorNode));
654 lastTensors[q] = tensorId;
656 lastTensorIndices[q] = 1;
660 tensorNode = std::make_shared<TensorNode>();
662 tensorNode->SetGate(gate, q);
663 tensorNode->SetSuper();
665 tensorId =
static_cast<Index>(tensors.size());
666 tensorNode->SetId(tensorId);
675 lastTensorId = lastTensorsSuper[q];
676 const auto &lastTensorSuper = tensors[lastTensorId];
677 lastTensorIndexForQubit = lastTensorIndicesSuper[q];
678 lastTensorSuper->connections[lastTensorIndexForQubit] = tensorId;
679 lastTensorSuper->connectionsIndices[lastTensorIndexForQubit] = 0;
681 tensorNode->connections[0] = lastTensorId;
682 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
684 tensors.emplace_back(std::move(tensorNode));
687 lastTensorsSuper[q] = tensorId;
689 lastTensorIndicesSuper[q] = 1;
693 void AddTwoQubitsGate(
694 const QC::Gates::QuantumGateWithOp<TensorNode::MatrixClass> &gate,
696 const size_t q1group = qubitsMap[q1];
697 const size_t q2group = qubitsMap[q2];
699 if (q1group != q2group) {
701 for (
const auto qg : qubitsGroups[q2group]) {
702 qubitsMap[qg] = q1group;
703 qubitsGroups[q1group].insert(qg);
705 qubitsGroups.erase(q2group);
712 auto tensorNode = std::make_shared<TensorNode>();
713 tensorNode->SetGate(gate, q1, q2);
715 auto tensorId =
static_cast<Index>(tensors.size());
716 tensorNode->SetId(tensorId);
725 auto lastTensorId = lastTensors[q1];
727 const auto &lastTensor = tensors[lastTensorId];
728 auto lastTensorIndexForQubit = lastTensorIndices[q1];
729 lastTensor->connections[lastTensorIndexForQubit] = tensorId;
730 lastTensor->connectionsIndices[lastTensorIndexForQubit] =
733 tensorNode->connections[0] = lastTensorId;
734 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
736 lastTensorId = lastTensors[q2];
737 const auto &lastTensor2 = tensors[lastTensorId];
738 lastTensorIndexForQubit = lastTensorIndices[q2];
739 lastTensor2->connections[lastTensorIndexForQubit] = tensorId;
740 lastTensor2->connectionsIndices[lastTensorIndexForQubit] =
743 tensorNode->connections[1] = lastTensorId;
744 tensorNode->connectionsIndices[1] = lastTensorIndices[q2];
746 tensors.emplace_back(std::move(tensorNode));
749 lastTensors[q1] = tensorId;
750 lastTensors[q2] = tensorId;
752 lastTensorIndices[q1] = 2;
753 lastTensorIndices[q2] = 3;
757 tensorNode = std::make_shared<TensorNode>();
759 tensorNode->SetGate(gate, q1, q2);
760 tensorNode->SetSuper();
762 tensorId =
static_cast<Index>(tensors.size());
763 tensorNode->SetId(tensorId);
772 lastTensorId = lastTensorsSuper[q1];
773 const auto &lastTensorSuper = tensors[lastTensorId];
774 lastTensorIndexForQubit = lastTensorIndicesSuper[q1];
775 lastTensorSuper->connections[lastTensorIndexForQubit] = tensorId;
776 lastTensorSuper->connectionsIndices[lastTensorIndexForQubit] = 0;
778 tensorNode->connections[0] = lastTensorId;
779 tensorNode->connectionsIndices[0] = lastTensorIndexForQubit;
781 lastTensorId = lastTensorsSuper[q2];
782 const auto &lastTensor2Super = tensors[lastTensorId];
783 lastTensorIndexForQubit = lastTensorIndicesSuper[q2];
784 lastTensor2Super->connections[lastTensorIndexForQubit] = tensorId;
785 lastTensor2Super->connectionsIndices[lastTensorIndexForQubit] = 1;
787 tensorNode->connections[1] = lastTensorId;
788 tensorNode->connectionsIndices[1] = lastTensorIndicesSuper[q2];
790 tensors.emplace_back(std::move(tensorNode));
793 lastTensorsSuper[q1] = tensorId;
794 lastTensorsSuper[q2] = tensorId;
796 lastTensorIndicesSuper[q1] = 2;
797 lastTensorIndicesSuper[q2] = 3;
803 contractor->SetMultithreading(enableMultithreading);
810 for (
auto &group : qubitsGroups) {
811 const double groupResult = contractor->Contract(
813 *group.second.begin());
815 if (groupResult == 0)
817 result *= groupResult;
829 double Contract(
const std::unordered_set<Types::qubit_t> &qubits) {
832 contractor->SetMultithreading(enableMultithreading);
839 for (
auto &group : qubitsGroups) {
841 for (
const auto q : group.second)
842 if (qubits.find(q) != qubits.end()) {
845 const double groupResult = contractor->Contract(
848 if (groupResult == 0)
850 result *= groupResult;
864 void Clean(
size_t numQubits) { SetQubitsTensors(numQubits); }
866 void SetQubitsTensors(
size_t numQubits) {
869 qubitsGroups[q].insert(
872 auto tensorNode = std::make_shared<TensorNode>();
873 tensorNode->SetQubit(q);
874 tensorNode->SetId(
static_cast<Index>(tensors.size()));
876 lastTensors[q] = tensorNode->GetId();
877 lastTensorIndices[q] = 0;
878 tensors.emplace_back(std::move(tensorNode));
880 tensorNode = std::make_shared<TensorNode>();
881 tensorNode->SetQubit(q);
882 tensorNode->SetId(
static_cast<Index>(tensors.size()));
883 tensorNode->SetSuper();
885 lastTensorsSuper[q] = tensorNode->GetId();
886 lastTensorIndicesSuper[q] = 0;
887 tensors.emplace_back(std::move(tensorNode));
891 void SetQubitsTensorsClear(
size_t numQubits) {
894 qubitsGroups[q].insert(
897 lastTensors[q] = 2 * q;
898 lastTensorIndices[q] = 0;
900 lastTensorsSuper[q] = lastTensors[q] + 1;
901 lastTensorIndicesSuper[q] = 0;
905 std::vector<std::shared_ptr<TensorNode>>
915 std::vector<Index> lastTensorIndicesSuper;
918 std::vector<size_t> qubitsMap;
920 std::unordered_map<size_t, std::unordered_set<Types::qubit_t>>
924 size_t savedTensorsNr = 0;
925 std::vector<std::shared_ptr<TensorNode>>
930 std::vector<Index> saveLastTensorsSuper;
934 saveLastTensorIndices;
935 std::vector<Index> saveLastTensorIndicesSuper;
941 std::unordered_map<size_t, std::unordered_set<Types::qubit_t>>
945 std::shared_ptr<ITensorContractor> contractor;
947 bool enableMultithreading =
true;
950 std::uniform_real_distribution<double> uniformZeroOne;