XACC
circuits.h
Go to the documentation of this file.
1 /*
2  * This file is part of Quantum++.
3  *
4  * MIT License
5  *
6  * Copyright (c) 2013 - 2020 Vlad Gheorghiu.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 
32 #ifndef CLASSES_CIRCUITS_CIRCUITS_H_
33 #define CLASSES_CIRCUITS_CIRCUITS_H_
34 
35 namespace qpp {
41 class QCircuit : public IDisplay, public IJSON {
42  friend class QEngine;
43 
44  idx nq_;
45  idx nc_;
46  idx d_;
47  std::string name_;
48  std::vector<bool> measured_;
49 
50  std::unordered_map<std::size_t, cmat>
51  cmat_hash_tbl_{};
52  std::unordered_map<std::string, idx> count_{};
54  std::unordered_map<std::string, idx>
55  measurement_count_{};
56 
66  void add_hash_(const cmat& U, std::size_t hashU) {
67  // EXCEPTION CHECKS
68 
69  auto search = cmat_hash_tbl_.find(hashU);
70  static internal::EqualEigen equal_eigen;
71  if (search != cmat_hash_tbl_.end()) // found the hash in the table
72  {
73  // have a hash collision
74  if (!equal_eigen(search->second, U))
75  throw exception::CustomException("qpp::QCircuit::add_hash_()",
76  "Matrix hash collision");
77  }
78  // END EXCEPTION CHECKS
79  cmat_hash_tbl_.insert({hashU, U});
80  }
81 
82  public:
86  enum class GateType {
87  NONE,
88 
89  SINGLE,
90 
91  TWO,
92 
93  THREE,
94 
95  CUSTOM,
96 
97  FAN,
98 
99  SINGLE_CTRL_SINGLE_TARGET,
100 
102  SINGLE_CTRL_MULTIPLE_TARGET,
103 
105  MULTIPLE_CTRL_SINGLE_TARGET,
106 
108  MULTIPLE_CTRL_MULTIPLE_TARGET,
109 
112  CUSTOM_CTRL,
113 
115  SINGLE_cCTRL_SINGLE_TARGET,
116 
118  SINGLE_cCTRL_MULTIPLE_TARGET,
119 
122  MULTIPLE_cCTRL_SINGLE_TARGET,
123 
126  MULTIPLE_cCTRL_MULTIPLE_TARGET,
127 
130  CUSTOM_cCTRL,
131  };
133 
142  friend std::ostream& operator<<(std::ostream& os,
143  const GateType& gate_type) {
144  switch (gate_type) {
145  case GateType::NONE:
146  os << "GATE NONE";
147  break;
148  case GateType::SINGLE:
149  os << "SINGLE";
150  break;
151  case GateType::TWO:
152  os << "TWO";
153  break;
154  case GateType::THREE:
155  os << "THREE";
156  break;
157  case GateType::FAN:
158  os << "FAN";
159  break;
160  case GateType::CUSTOM:
161  os << "CUSTOM";
162  break;
163  case GateType::SINGLE_CTRL_SINGLE_TARGET:
164  os << "SINGLE_CTRL_SINGLE_TARGET";
165  break;
166  case GateType::SINGLE_CTRL_MULTIPLE_TARGET:
167  os << "SINGLE_CTRL_MULTIPLE_TARGET";
168  break;
169  case GateType::MULTIPLE_CTRL_SINGLE_TARGET:
170  os << "MULTIPLE_CTRL_SINGLE_TARGET";
171  break;
172  case GateType::MULTIPLE_CTRL_MULTIPLE_TARGET:
173  os << "MULTIPLE_CTRL_MULTIPLE_TARGET";
174  break;
175  case GateType::CUSTOM_CTRL:
176  os << "CUSTOM_CTRL";
177  break;
178  case GateType::SINGLE_cCTRL_SINGLE_TARGET:
179  os << "SINGLE_cCTRL_SINGLE_TARGET";
180  break;
181  case GateType::SINGLE_cCTRL_MULTIPLE_TARGET:
182  os << "SINGLE_cCTRL_MULTIPLE_TARGET";
183  break;
184  case GateType::MULTIPLE_cCTRL_SINGLE_TARGET:
185  os << "MULTIPLE_cCTRL_SINGLE_TARGET";
186  break;
187  case GateType::MULTIPLE_cCTRL_MULTIPLE_TARGET:
188  os << "MULTIPLE_cCTRL_MULTIPLE_TARGET";
189  break;
190  case GateType::CUSTOM_cCTRL:
191  os << "CUSTOM_cCTRL";
192  break;
193  }
194 
195  return os;
196  }
197 
201  struct GateStep {
202  GateType gate_type_ = GateType::NONE;
203  std::size_t gate_hash_{};
204  std::vector<idx> ctrl_{};
205  std::vector<idx> target_{};
206  std::vector<idx> shift_{};
207  std::string name_{};
208 
212  GateStep() = default;
213 
223  explicit GateStep(GateType gate_type, std::size_t gate_hash,
224  const std::vector<idx>& ctrl,
225  const std::vector<idx>& target,
226  const std::vector<idx>& shift = {},
227  std::string name = {})
228  : gate_type_{gate_type}, gate_hash_{gate_hash}, ctrl_{ctrl},
229  target_{target}, shift_{shift}, name_{name} {}
230  };
231 
239  friend std::ostream& operator<<(std::ostream& os,
240  const GateStep& gate_step) {
241  os << gate_step.gate_type_ << ", ";
242  if (gate_step.gate_type_ >= GateType::SINGLE_cCTRL_SINGLE_TARGET)
243  os << "c_ctrl = " << disp(gate_step.ctrl_, ", ") << ", ";
244  else if (gate_step.gate_type_ >= GateType::SINGLE_CTRL_SINGLE_TARGET)
245  os << "ctrl = " << disp(gate_step.ctrl_, ", ") << ", ";
246  os << "target = " << disp(gate_step.target_, ", ") << ", ";
247  if (!gate_step.shift_.empty())
248  os << "shift = " << disp(gate_step.shift_, ", ") << ", ";
249  os << "name = " << '\"' << gate_step.name_ << '\"';
250 
251  return os;
252  }
253 
257  enum class MeasureType {
258  NONE,
259 
260  MEASURE_Z,
261 
262  MEASURE_Z_MANY,
263 
264  MEASURE_V,
265 
268  MEASURE_V_MANY,
269 
272  MEASURE_Z_ND,
273 
274  MEASURE_Z_MANY_ND,
275 
276  MEASURE_V_ND,
277 
280  MEASURE_V_MANY_ND,
281 
285  RESET,
286 
287  RESET_MANY,
288 
289  DISCARD,
290 
291  DISCARD_MANY,
292  };
293 
302  friend std::ostream& operator<<(std::ostream& os,
303  const MeasureType& measure_type) {
304  switch (measure_type) {
305  case MeasureType::NONE:
306  os << "MEASURE NONE";
307  break;
308  case MeasureType::MEASURE_Z:
309  os << "MEASURE_Z";
310  break;
311  case MeasureType::MEASURE_Z_MANY:
312  os << "MEASURE_Z_MANY";
313  break;
314  case MeasureType::MEASURE_V:
315  os << "MEASURE_V";
316  break;
317  case MeasureType::MEASURE_V_MANY:
318  os << "MEASURE_V_MANY";
319  break;
320  case MeasureType::MEASURE_Z_ND:
321  os << "MEASURE_Z_ND";
322  break;
323  case MeasureType::MEASURE_Z_MANY_ND:
324  os << "MEASURE_Z_MANY_ND";
325  break;
326  case MeasureType::MEASURE_V_ND:
327  os << "MEASURE_V_ND";
328  break;
329  case MeasureType::MEASURE_V_MANY_ND:
330  os << "MEASURE_V_MANY_ND";
331  break;
332  case MeasureType::RESET:
333  os << "RESET";
334  break;
335  case MeasureType::RESET_MANY:
336  os << "RESET_MANY";
337  break;
338  case MeasureType::DISCARD:
339  os << "DISCARD";
340  break;
341  case MeasureType::DISCARD_MANY:
342  os << "DISCARD_MANY";
343  break;
344  }
345 
346  return os;
347  }
348 
352  struct MeasureStep {
353  MeasureType measurement_type_ = MeasureType::NONE;
354  std::vector<std::size_t> mats_hash_{};
355  std::vector<idx> target_{};
357  idx c_reg_{};
358  std::string name_{};
360 
364  MeasureStep() = default;
365 
376  explicit MeasureStep(MeasureType measurement_type,
377  const std::vector<std::size_t>& mats_hash,
378  const std::vector<idx>& target, idx c_reg,
379  std::string name = {})
380  : measurement_type_{measurement_type}, mats_hash_{mats_hash},
381  target_{target}, c_reg_{c_reg}, name_{name} {}
382  };
383 
391  friend std::ostream& operator<<(std::ostream& os,
392  const MeasureStep& measure_step) {
393  os << measure_step.measurement_type_ << ", ";
394  os << "target = " << disp(measure_step.target_, ", ") << ", ";
395  if (measure_step.measurement_type_ != MeasureType::RESET &&
396  measure_step.measurement_type_ != MeasureType::RESET_MANY &&
397  measure_step.measurement_type_ != MeasureType::DISCARD &&
398  measure_step.measurement_type_ != MeasureType::DISCARD_MANY)
399  os << "c_reg = " << measure_step.c_reg_ << ", ";
400  os << "name = " << '\"' << measure_step.name_ << '\"';
401 
402  return os;
403  }
404 
408  enum class StepType {
409  NONE,
410  GATE,
411  MEASUREMENT,
412  NOP,
413  };
414 
415  private:
416  std::vector<GateStep> gates_{};
417  std::vector<MeasureStep> measurements_{};
418  std::vector<StepType> step_types_{};
419 
425  const std::vector<MeasureStep>& get_measurements_() const noexcept {
426  return measurements_;
427  }
428 
434  const std::vector<GateStep>& get_gates_() const noexcept { return gates_; }
435 
441  const std::unordered_map<std::size_t, cmat>& get_cmat_hash_tbl_() const
442  noexcept {
443  return cmat_hash_tbl_;
444  }
445 
446  public:
453  class iterator {
455  const QCircuit* qc_{nullptr};
456 
461  class value_type_ : public IDisplay {
462  public:
465  const QCircuit* value_type_qc_;
466 
467  StepType type_{StepType::NONE};
468  idx ip_{static_cast<idx>(-1)};
469  std::vector<GateStep>::const_iterator
470  gates_ip_{};
471  std::vector<MeasureStep>::const_iterator
472  measurements_ip_{};
473 
480  explicit value_type_(const QCircuit* value_type_qc)
481  : value_type_qc_{value_type_qc} {}
482 
483  // silence -Weffc++ class has pointer data members
487  value_type_(const value_type_&) = default;
488 
489  // silence -Weffc++ class has pointer data members
495  value_type_& operator=(const value_type_&) = default;
496 
497  private:
507  std::ostream& display(std::ostream& os) const override {
508  // field spacing for the step number
509  idx text_width =
510  std::to_string(value_type_qc_->get_step_count()).size() + 1;
511 
512  // gate step
513  if (type_ == StepType::GATE) {
514  os << std::left;
515  os << std::setw(text_width) << ip_;
516  os << std::right;
517  idx pos = std::distance(std::begin(value_type_qc_->gates_),
518  gates_ip_);
519  os << value_type_qc_->get_gates_()[pos];
520  }
521  // measurement step
522  else if (type_ == StepType::MEASUREMENT) {
523  os << std::left;
524  os << std::setw(text_width) << ip_;
525  os << std::right;
526  idx pos =
527  std::distance(std::begin(value_type_qc_->measurements_),
528  measurements_ip_);
529 
530  switch (
531  value_type_qc_->measurements_[pos].measurement_type_) {
532  case MeasureType::NONE:
533  break;
534  case MeasureType::MEASURE_Z:
535  case MeasureType::MEASURE_Z_MANY:
536  case MeasureType::MEASURE_V:
537  case MeasureType::MEASURE_V_MANY:
538  os << "|> ";
539  break;
540  case MeasureType::MEASURE_Z_ND:
541  case MeasureType::MEASURE_Z_MANY_ND:
542  case MeasureType::MEASURE_V_ND:
543  case MeasureType::MEASURE_V_MANY_ND:
544  os << "|] ";
545  break;
546  case MeasureType::RESET:
547  case MeasureType::RESET_MANY:
548  os << "|* ";
549  break;
550  case MeasureType::DISCARD:
551  case MeasureType::DISCARD_MANY:
552  os << "|x ";
553  break;
554  } /* end switch */
555 
556  os << value_type_qc_->get_measurements_()[pos];
557  }
558  // no-op
559  else if (type_ == StepType::NOP) {
560  os << std::left;
561  os << std::setw(text_width) << ip_;
562  os << std::right;
563  os << "NOP";
564  }
565  // otherwise
566  else {
567  }
568 
569  return os;
570  }
571  }; /* class value_type_ */
572 
573  value_type_ elem_{nullptr};
574 
575  public:
579  iterator() = default;
580 
581  // silence -Weffc++ class has pointer data members
585  iterator(const iterator&) = default;
586 
587  // silence -Weffc++ class has pointer data members
593  iterator& operator=(const iterator&) = default;
594 
600  iterator& operator++() {
601  // EXCEPTION CHECKS
602 
603  // protects against incrementing invalid iterators
604  if (qc_ == nullptr) {
605  throw exception::InvalidIterator(
606  "qpp::QCircuit::iterator::operator++()");
607  }
608 
609  // protects against incrementing an empty circuit iterator
610  if (qc_->get_step_count() == 0) {
611  throw exception::InvalidIterator(
612  "qpp::QCircuit::iterator::operator++()");
613  }
614 
615  // protects against incrementing past the end
616  if (elem_.ip_ == qc_->get_step_count()) {
617  throw exception::InvalidIterator(
618  "qpp::QCircuit::iterator::operator++()");
619  }
620  // END EXCEPTION CHECKS
621 
622  // gate step
623  if (elem_.type_ == StepType::GATE) {
624  std::advance(elem_.gates_ip_, 1);
625  }
626  // measurement step
627  else if (elem_.type_ == StepType::MEASUREMENT) {
628  std::advance(elem_.measurements_ip_, 1);
629  }
630  // no-op
631  else if (elem_.type_ == StepType::NOP) {
632  }
633  // otherwise
634  else {
635  }
636 
637  // increment the instruction pointer
638  ++elem_.ip_;
639 
640  // if we hit the end
641  if (elem_.ip_ == qc_->get_step_count()) {
642  elem_.type_ = StepType::NONE;
643  } else {
644  // set the next step type
645  elem_.type_ = qc_->step_types_[elem_.ip_];
646  }
647 
648  return *this;
649  }
650 
656  iterator operator++(int) {
657  iterator retval = *this;
658  ++(*this);
659  return retval;
660  }
661 
668  bool operator==(const iterator& rhs) const {
669  return std::tie(elem_.type_, elem_.ip_, elem_.gates_ip_,
670  elem_.measurements_ip_) ==
671  std::tie(rhs.elem_.type_, rhs.elem_.ip_, rhs.elem_.gates_ip_,
672  rhs.elem_.measurements_ip_);
673  }
674 
682  bool operator!=(iterator rhs) const { return !(*this == rhs); }
683 
689  const value_type_& operator*() const {
690  // EXCEPTION CHECKS
691 
692  // protects against de-referencing past the last element or against
693  // de-referencing invalid iterators
694  if (qc_ == nullptr || elem_.ip_ == qc_->get_step_count())
695  throw exception::InvalidIterator(
696  "qpp::QCircuit::iterator::operator*()");
697  // END EXCEPTION CHECKS
698 
699  return elem_;
700  }
701 
707  void set_begin_(const QCircuit* qc) {
708  qc_ = qc;
709  elem_ = value_type_{qc_};
710 
711  if (qc_ != nullptr) {
712  if (qc_->get_step_count() != 0) // non-empty circuit
713  {
714  elem_.type_ = qc_->step_types_[0];
715  elem_.ip_ = 0;
716  }
717  elem_.gates_ip_ = std::begin(qc_->gates_);
718  elem_.measurements_ip_ = std::begin(qc_->measurements_);
719  }
720  }
721 
727  void set_end_(const QCircuit* qc) {
728  qc_ = qc;
729  elem_ = value_type_{qc_};
730 
731  if (qc_ != nullptr) {
732  if (qc->get_step_count() != 0) {
733  elem_.ip_ = qc->get_step_count();
734  }
735  elem_.gates_ip_ = std::end(qc->gates_);
736  elem_.measurements_ip_ = std::end(qc->measurements_);
737  }
738  }
739 
740  // iterator traits
741  using difference_type = ptrdiff_t;
742  using value_type = value_type_;
743  using pointer = const value_type*;
744  using reference = const value_type&;
745  using iterator_category = std::forward_iterator_tag;
746  };
747 
748  using const_iterator = iterator;
749 
755  iterator begin() {
756  iterator it;
757  it.set_begin_(this);
758 
759  return it;
760  }
761 
767  const_iterator begin() const noexcept {
768  iterator it;
769  it.set_begin_(this);
770 
771  return it;
772  }
773 
779  const_iterator cbegin() const noexcept {
780  iterator it;
781  it.set_begin_(this);
782 
783  return it;
784  }
785 
791  iterator end() {
792  iterator it;
793  it.set_end_(this);
794 
795  return it;
796  }
797 
803  const_iterator end() const noexcept {
804  iterator it;
805  it.set_end_(this);
806 
807  return it;
808  }
809 
815  const_iterator cend() const noexcept {
816  iterator it;
817  it.set_end_(this);
818 
819  return it;
820  }
821 
833  explicit QCircuit(idx nq, idx nc = 0, idx d = 2, std::string name = {})
834  : nq_{nq}, nc_{nc}, d_{d}, name_{name}, measured_(nq, false) {
835  // EXCEPTION CHECKS
836 
837  // if (nq == 0)
838  // throw exception::ZeroSize("qpp::QCircuit::QCircuit()");
839  if (d < 2)
840  throw exception::OutOfRange("qpp::QCircuit::QCircuit()");
841  // END EXCEPTION CHECKS
842  }
843 
847  virtual ~QCircuit() = default;
848 
849  // getters
855  idx get_nq() const noexcept { return nq_; }
856 
862  idx get_nc() const noexcept { return nc_; }
863 
869  idx get_d() const noexcept { return d_; }
870 
876  std::string get_name() const { return name_; }
883  idx get_measured(idx i) const {
884  // EXCEPTION CHECKS
885 
886  if (i >= nq_)
887  throw exception::OutOfRange("qpp::QCircuit::get_measured()");
888  // END EXCEPTION CHECKS
889 
890  return measured_[i];
891  }
892 
898  std::vector<idx> get_measured() const {
899  std::vector<idx> result;
900  for (idx i = 0; i < nq_; ++i)
901  if (get_measured(i))
902  result.emplace_back(i);
903 
904  return result;
905  }
906 
912  std::vector<idx> get_non_measured() const {
913  std::vector<idx> result;
914  for (idx i = 0; i < nq_; ++i)
915  if (!get_measured(i))
916  result.emplace_back(i);
917 
918  return result;
919  }
920 
927  idx get_gate_count(const std::string& name) const {
928  idx result = 0;
929 
930  // name not found in the hash table
931  try {
932  result = count_.at(name);
933  } catch (...) {
934  return 0;
935  }
936 
937  return result;
938  }
939 
945  idx get_gate_count() const {
946  idx result = 0;
947 
948  for (auto&& elem : count_)
949  result += elem.second;
950 
951  return result;
952  }
953 
960  idx get_gate_depth(const std::string& name) const {
961  bool found = false;
962  std::vector<idx> heights(nc_ + nq_, 0);
963 
964  // iterate over all steps in the circuit
965  for (auto&& step : *this) {
966  // gates
967  if (step.type_ == StepType::GATE) {
968  GateStep gate_step = *step.gates_ip_;
969 
970  if (name != __FILE__ "__total_gate_depth__" &&
971  gate_step.name_ != name)
972  continue; // we skip this gate step
973 
974  found = true; // gate was found in the circuit
975 
976  std::vector<idx> ctrl = gate_step.ctrl_;
977  std::vector<idx> target = gate_step.target_;
978  std::vector<idx> ctrl_target;
979  ctrl_target.reserve(ctrl.size() + target.size());
980  ctrl_target.insert(ctrl_target.end(), ctrl.begin(), ctrl.end());
981  ctrl_target.insert(ctrl_target.end(), target.begin(),
982  target.end());
983 
984  idx max_height = 0;
985  switch (gate_step.gate_type_) {
986  case GateType::NONE:
987  case GateType::SINGLE:
988  case GateType::TWO:
989  case GateType::THREE:
990  case GateType::CUSTOM:
991  case GateType::FAN:
992  case GateType::SINGLE_CTRL_SINGLE_TARGET:
993  case GateType::SINGLE_CTRL_MULTIPLE_TARGET:
994  case GateType::MULTIPLE_CTRL_SINGLE_TARGET:
995  case GateType::MULTIPLE_CTRL_MULTIPLE_TARGET:
996  case GateType::CUSTOM_CTRL:
997  // compute the "height" of the to-be-placed gate
998  for (auto&& i : ctrl_target)
999  if (heights[nc_ + i] > max_height)
1000  max_height = heights[nc_ + i];
1001  // apply (ctrl) gate
1002  for (auto&& i : ctrl_target)
1003  heights[nc_ + i] = max_height + 1;
1004  break;
1005  case GateType::SINGLE_cCTRL_SINGLE_TARGET:
1006  case GateType::SINGLE_cCTRL_MULTIPLE_TARGET:
1007  case GateType::MULTIPLE_cCTRL_SINGLE_TARGET:
1008  case GateType::MULTIPLE_cCTRL_MULTIPLE_TARGET:
1009  case GateType::CUSTOM_cCTRL:
1010  // compute the "height" of the to-be-placed gate
1011  for (auto&& i : ctrl)
1012  if (heights[i] > max_height)
1013  max_height = heights[i];
1014  for (auto&& i : target)
1015  if (heights[nc_ + i] > max_height)
1016  max_height = heights[nc_ + i];
1017  // apply classical ctrl
1018  for (auto&& i : ctrl)
1019  heights[i] = max_height + 1;
1020  // apply gate
1021  for (auto&& i : target)
1022  heights[nc_ + i] = max_height + 1;
1023  break;
1024  } // end switch
1025  } // end if (step.type_ == StepType::GATE)
1026  } // end for
1027 
1028  return found ? *std::max_element(std::begin(heights), std::end(heights))
1029  : 0;
1030  }
1031 
1037  idx get_gate_depth() const {
1038  return get_gate_depth(__FILE__ "__total_gate_depth__");
1039  }
1040 
1047  idx get_measurement_depth(const std::string& name) const {
1048  bool found = false;
1049  std::vector<idx> heights(nc_ + nq_, 0);
1050 
1051  // iterate over all steps in the circuit
1052  for (auto&& step : *this) {
1053  // measurements
1054  if (step.type_ == StepType::MEASUREMENT) {
1055  MeasureStep measure_step = *step.measurements_ip_;
1056  if (name != __FILE__ "__total_measurement_depth__" &&
1057  measure_step.name_ != name)
1058  continue; // we skip this measurement step
1059 
1060  found = true; // measurement was found in the circuit
1061 
1062  std::vector<idx> target = measure_step.target_;
1063  idx c_reg = measure_step.c_reg_;
1064 
1065  idx max_height = 0;
1066  switch (measure_step.measurement_type_) {
1067  case MeasureType::NONE:
1068  case MeasureType::MEASURE_Z:
1069  case MeasureType::MEASURE_Z_MANY:
1070  case MeasureType::MEASURE_V:
1071  case MeasureType::MEASURE_V_MANY:
1072  case MeasureType::MEASURE_Z_ND:
1073  case MeasureType::MEASURE_Z_MANY_ND:
1074  case MeasureType::MEASURE_V_ND:
1075  case MeasureType::MEASURE_V_MANY_ND:
1076  // compute the "height" of the to-be-placed measurement
1077  if (heights[c_reg] > max_height)
1078  max_height = heights[c_reg];
1079  for (auto&& i : target)
1080  if (heights[nc_ + i] > max_height)
1081  max_height = heights[nc_ + i];
1082  // apply measurement
1083  heights[c_reg] = max_height + 1;
1084  for (auto&& i : target)
1085  heights[nc_ + i] = max_height + 1;
1086  break;
1087  case MeasureType::RESET:
1088  case MeasureType::RESET_MANY:
1089  case MeasureType::DISCARD:
1090  case MeasureType::DISCARD_MANY:
1091  for (auto&& i : target)
1092  if (heights[nc_ + i] > max_height)
1093  max_height = heights[nc_ + i];
1094  // apply reset/discard
1095  for (auto&& i : target)
1096  heights[nc_ + i] = max_height + 1;
1097  break;
1098  } // end switch
1099  } // if (step.type_ == StepType::MEASUREMENT) }
1100  } // end for
1101 
1102  return found ? *std::max_element(std::begin(heights), std::end(heights))
1103  : 0;
1104  }
1105 
1111  idx get_measurement_depth() const {
1112  return get_measurement_depth(__FILE__ "__total_measurement_depth__");
1113  }
1114 
1115  // computes the depth greedily, measuring the "height" (depth) of the
1116  // "pieces" (gates) placed in a Tetris-like style
1123  idx get_depth() const { return get_gate_depth() + get_measurement_depth(); }
1124 
1131  idx get_measurement_count(const std::string& name) const {
1132  idx result = 0;
1133 
1134  // name not found in the hash table
1135  try {
1136  result = measurement_count_.at(name);
1137  } catch (...) {
1138  return 0;
1139  }
1140 
1141  return result;
1142  }
1143 
1149  idx get_measurement_count() const {
1150  idx result = 0;
1151 
1152  for (auto&& elem : measurement_count_)
1153  result += elem.second;
1154 
1155  return result;
1156  }
1157 
1164  idx get_step_count() const noexcept { return step_types_.size(); }
1165 
1171  idx get_nop_count() const {
1172  return std::count_if(
1173  std::begin(step_types_), std::end(step_types_),
1174  [](const StepType& s) { return s == StepType::NOP; });
1175  }
1176  // end getters
1177 
1178  // setters
1185  QCircuit& set_name(const std::string& name) {
1186  name_ = name;
1187  return *this;
1188  }
1189  // end setters
1190 
1202  QCircuit& add_qudit(idx n = 1, idx i = -1) {
1203  // EXCEPTION CHECKS
1204 
1205  if (i == static_cast<idx>(-1)) {
1206  i = nq_;
1207  measured_.insert(std::end(measured_), n, false);
1208  } else if (i > nq_)
1209  throw exception::OutOfRange("qpp::QCircuit::add_qudit()");
1210  // END EXCEPTION CHECKS
1211 
1212  nq_ += n;
1213 
1214  // updated the measured qudits
1215  measured_.insert(std::next(std::begin(measured_), i), n, false);
1216 
1217  // update gate indexes
1218  for (auto& gate : gates_) {
1219  for (auto& pos : gate.ctrl_) {
1220  if (pos >= i)
1221  pos += n;
1222  }
1223  for (auto& pos : gate.target_) {
1224  if (pos >= i)
1225  pos += n;
1226  }
1227  }
1228 
1229  // update measurement indexes
1230  for (auto& measurement : measurements_) {
1231  for (auto& pos : measurement.target_) {
1232  if (pos >= i)
1233  pos += n;
1234  }
1235  }
1236 
1237  return *this;
1238  }
1239 
1251  QCircuit& add_dit(idx n = 1, idx i = -1) {
1252  // EXCEPTION CHECKS
1253 
1254  if (i == static_cast<idx>(-1))
1255  i = nc_;
1256  else if (i > nc_)
1257  throw exception::OutOfRange("qpp::QCircuit::add_dit()");
1258  // END EXCEPTION CHECKS
1259 
1260  nc_ += n;
1261 
1262  // update gate indexes
1263  for (auto& gate : gates_) {
1264  switch (gate.gate_type_) {
1265  // classically-controlled gates
1266  case GateType::SINGLE_cCTRL_SINGLE_TARGET:
1267  case GateType::SINGLE_cCTRL_MULTIPLE_TARGET:
1268  case GateType::MULTIPLE_cCTRL_SINGLE_TARGET:
1269  case GateType::MULTIPLE_cCTRL_MULTIPLE_TARGET:
1270  case GateType::CUSTOM_cCTRL:
1271  for (auto& pos : gate.ctrl_) {
1272  if (pos >= i)
1273  pos += n;
1274  }
1275  break;
1276  default:
1277  break;
1278  }
1279  }
1280 
1281  // update measurement indexes
1282  for (auto& measurement : measurements_) {
1283  if (measurement.c_reg_ >= i)
1284  measurement.c_reg_ += n;
1285  }
1286 
1287  return *this;
1288  }
1289 
1298  QCircuit& gate(const cmat& U, idx i, std::string name = {}) {
1299  // EXCEPTION CHECKS
1300 
1301  try {
1302  // check valid target
1303  if (i >= nq_)
1304  throw exception::OutOfRange("qpp::QCircuit::gate()");
1305  // check not measured before
1306  if (get_measured(i))
1307  throw exception::QuditAlreadyMeasured("qpp::QCircuit::gate()");
1308 
1309  // check square matrix for the gate
1310  if (!internal::check_square_mat(U))
1311  throw exception::MatrixNotSquare("qpp::QCircuit::gate()");
1312  // check correct dimension
1313  if (static_cast<idx>(U.rows()) != d_)
1314  throw exception::DimsMismatchMatrix("qpp::QCircuit::gate()");
1315  } catch (exception::Exception&) {
1316  std::cerr << "At STEP " << get_step_count() << "\n";
1317  throw;
1318  }
1319  // END EXCEPTION CHECKS
1320 
1321  if (name.empty())
1322  name = qpp::Gates::get_instance().get_name(U);
1323  std::size_t hashU = hash_eigen(U);
1324  add_hash_(U, hashU);
1325  gates_.emplace_back(GateType::SINGLE, hashU, std::vector<idx>{},
1326  std::vector<idx>{i}, std::vector<idx>{}, name);
1327  step_types_.emplace_back(StepType::GATE);
1328  ++count_[name];
1329 
1330  return *this;
1331  }
1332 
1342  QCircuit& gate(const cmat& U, idx i, idx j, std::string name = {}) {
1343  // EXCEPTION CHECKS
1344 
1345  try {
1346  // check valid target
1347  if (i >= nq_ || j >= nq_ || i == j)
1348  throw exception::OutOfRange("qpp::QCircuit::gate()");
1349  if (get_measured(i) || get_measured(j))
1350  throw exception::QuditAlreadyMeasured("qpp::QCircuit::gate()");
1351 
1352  // check square matrix for the gate
1353  if (!internal::check_square_mat(U))
1354  throw exception::MatrixNotSquare("qpp::QCircuit::gate()");
1355  // check correct dimension
1356  if (static_cast<idx>(U.rows()) != d_ * d_)
1357  throw exception::DimsMismatchMatrix("qpp::QCircuit::gate()");
1358  } catch (exception::Exception&) {
1359  std::cerr << "At STEP " << get_step_count() << "\n";
1360  throw;
1361  }
1362  // END EXCEPTION CHECKS
1363 
1364  if (name.empty())
1365  name = qpp::Gates::get_instance().get_name(U);
1366  std::size_t hashU = hash_eigen(U);
1367  add_hash_(U, hashU);
1368  gates_.emplace_back(GateType::TWO, hashU, std::vector<idx>{},
1369  std::vector<idx>{i, j}, std::vector<idx>{}, name);
1370  step_types_.emplace_back(StepType::GATE);
1371  ++count_[name];
1372 
1373  return *this;
1374  }
1375 
1386  QCircuit& gate(const cmat& U, idx i, idx j, idx k, std::string name = {}) {
1387  // EXCEPTION CHECKS
1388 
1389  try {
1390  // check valid target
1391  if (i >= nq_ || j >= nq_ || k >= nq_ || (i == j) || (i == k) ||
1392  (j == k))
1393  throw exception::OutOfRange("qpp::QCircuit::gate()");
1394  if (get_measured(i) || get_measured(j) || get_measured(k))
1395  throw exception::QuditAlreadyMeasured("qpp::QCircuit::gate()");
1396 
1397  // check square matrix for the gate
1398  if (!internal::check_square_mat(U))
1399  throw exception::MatrixNotSquare("qpp::QCircuit::gate()");
1400  // check correct dimension
1401  if (static_cast<idx>(U.rows()) != d_ * d_ * d_)
1402  throw exception::DimsMismatchMatrix("qpp::QCircuit::gate()");
1403  } catch (exception::Exception&) {
1404  std::cerr << "At STEP " << get_step_count() << "\n";
1405  throw;
1406  }
1407  // END EXCEPTION CHECKS
1408 
1409  if (name.empty())
1410  name = qpp::Gates::get_instance().get_name(U);
1411  std::size_t hashU = hash_eigen(U);
1412  add_hash_(U, hashU);
1413  gates_.emplace_back(GateType::THREE, hashU, std::vector<idx>{},
1414  std::vector<idx>{i, j, k}, std::vector<idx>{},
1415  name);
1416  step_types_.emplace_back(StepType::GATE);
1417  ++count_[name];
1418 
1419  return *this;
1420  }
1421 
1432  QCircuit& gate_fan(const cmat& U, const std::vector<idx>& target,
1433  std::string name = {}) {
1434  // EXCEPTION CHECKS
1435 
1436  try {
1437  // check valid target
1438  if (target.empty())
1439  throw exception::ZeroSize("qpp::QCircuit::gate_fan()");
1440  for (auto&& elem : target) {
1441  if (elem >= nq_)
1442  throw exception::OutOfRange("qpp::QCircuit::gate_fan()");
1443  // check target was not measured before
1444  if (get_measured(elem))
1445  throw exception::QuditAlreadyMeasured(
1446  "qpp::QCircuit::gate_fan()");
1447  }
1448  // check no duplicates target
1449  if (!internal::check_no_duplicates(target))
1450  throw exception::Duplicates("qpp::QCircuit::gate_fan()");
1451 
1452  // check square matrix for the gate
1453  if (!internal::check_square_mat(U))
1454  throw exception::MatrixNotSquare("qpp::QCircuit::gate_fan()");
1455  // check correct dimension
1456  if (static_cast<idx>(U.rows()) != d_)
1457  throw exception::DimsMismatchMatrix(
1458  "qpp::QCircuit::gate_fan()");
1459  } catch (exception::Exception&) {
1460  std::cerr << "At STEP " << get_step_count() << "\n";
1461  throw;
1462  }
1463  // END EXCEPTION CHECKS
1464 
1465  if (name.empty())
1466  name = qpp::Gates::get_instance().get_name(U);
1467  std::size_t hashU = hash_eigen(U);
1468  add_hash_(U, hashU);
1469  gates_.emplace_back(GateType::FAN, hashU, std::vector<idx>{}, target,
1470  std::vector<idx>{}, name);
1471  step_types_.emplace_back(StepType::GATE);
1472  count_[name] += target.size();
1473 
1474  return *this;
1475  }
1476 
1477  // std::initializer_list overload, avoids ambiguity for 2-element lists, see
1478  // http://stackoverflow.com
1479  // /questions/26750039/ambiguity-when-using-initializer-list-as-parameter
1490  QCircuit& gate_fan(const cmat& U, const std::initializer_list<idx>& target,
1491  std::string name = {}) {
1492  return gate_fan(U, std::vector<idx>(target), name);
1493  }
1494 
1503  QCircuit& gate_fan(const cmat& U, std::string name = {}) {
1504  // EXCEPTION CHECKS
1505 
1506  try {
1507  // check square matrix for the gate
1508  if (!internal::check_square_mat(U))
1509  throw exception::MatrixNotSquare("qpp::QCircuit::gate_fan()");
1510  // check correct dimension
1511  if (static_cast<idx>(U.rows()) != d_)
1512  throw exception::DimsMismatchMatrix(
1513  "qpp::QCircuit::gate_fan()");
1514  } catch (exception::Exception&) {
1515  std::cerr << "At STEP " << get_step_count() << "\n";
1516  throw;
1517  }
1518  // END EXCEPTION CHECKS
1519 
1520  if (name.empty())
1521  name = qpp::Gates::get_instance().get_name(U);
1522  std::size_t hashU = hash_eigen(U);
1523  add_hash_(U, hashU);
1524  gates_.emplace_back(GateType::FAN, hashU, std::vector<idx>{},
1525  get_non_measured(), std::vector<idx>{}, name);
1526  step_types_.emplace_back(StepType::GATE);
1527  count_[name] += get_non_measured().size();
1528 
1529  return *this;
1530  }
1531 
1541  QCircuit& gate_custom(const cmat& U, const std::vector<idx>& target,
1542  std::string name = {}) {
1543  // EXCEPTION CHECKS
1544 
1545  idx n = static_cast<idx>(target.size());
1546  idx D = static_cast<idx>(std::llround(std::pow(d_, n)));
1547 
1548  try {
1549  // check valid target
1550  if (target.empty())
1551  throw exception::ZeroSize("qpp::QCircuit::gate_custom()");
1552  for (auto&& elem : target) {
1553  if (elem >= nq_)
1554  throw exception::OutOfRange("qpp::QCircuit::gate_custom()");
1555  // check target was not measured before
1556  if (get_measured(elem))
1557  throw exception::QuditAlreadyMeasured(
1558  "qpp::QCircuit::gate_custom()");
1559  }
1560  // check no duplicates target
1561  if (!internal::check_no_duplicates(target))
1562  throw exception::Duplicates("qpp::QCircuit::gate_custom()");
1563 
1564  // check square matrix for the gate
1565  if (!internal::check_square_mat(U))
1566  throw exception::MatrixNotSquare(
1567  "qpp::QCircuit::gate_custom()");
1568  // check correct dimension
1569  if (static_cast<idx>(U.rows()) != D)
1570  throw exception::DimsMismatchMatrix(
1571  "qpp::QCircuit::gate_custom()");
1572  } catch (exception::Exception&) {
1573  std::cerr << "At STEP " << get_step_count() << "\n";
1574  throw;
1575  }
1576  // END EXCEPTION CHECKS
1577 
1578  if (name.empty())
1579  name = qpp::Gates::get_instance().get_name(U);
1580  std::size_t hashU = hash_eigen(U);
1581  add_hash_(U, hashU);
1582  gates_.emplace_back(GateType::CUSTOM, hashU, std::vector<idx>{}, target,
1583  std::vector<idx>{}, name);
1584  step_types_.emplace_back(StepType::GATE);
1585  ++count_[name];
1586 
1587  return *this;
1588  }
1589 
1599  QCircuit& QFT(const std::vector<idx>& target, bool swap = true) {
1600  // EXCEPTION CHECKS
1601  try {
1602  // check valid target
1603  if (target.empty())
1604  throw exception::ZeroSize("qpp::QCircuit::QFT()");
1605  for (auto&& elem : target) {
1606  if (elem >= nq_)
1607  throw exception::OutOfRange("qpp::QCircuit::QFT()");
1608  // check target was not measured before
1609  if (get_measured(elem))
1610  throw exception::QuditAlreadyMeasured(
1611  "qpp::QCircuit::QFT()");
1612  }
1613  // check no duplicates target
1614  if (!internal::check_no_duplicates(target))
1615  throw exception::Duplicates("qpp::QCircuit::QFT()");
1616  } catch (exception::Exception&) {
1617  std::cerr << "At STEP " << get_step_count() << "\n";
1618  throw;
1619  }
1620  // END EXCEPTION CHECKS
1621 
1622  idx n_subsys = target.size();
1623  if (d_ == 2) // qubits
1624  {
1625  for (idx i = 0; i < n_subsys; ++i) {
1626  // apply Hadamard on qubit i
1627  gate(Gates::get_instance().H, target[i]);
1628  // apply controlled rotations
1629  for (idx j = 2; j <= n_subsys - i; ++j) {
1630  // construct Rj
1631  cmat Rj(2, 2);
1632  Rj << 1, 0, 0, exp(2.0 * pi * 1_i / std::pow(2, j));
1633  CTRL(Rj, target[i + j - 1], target[i], {},
1634  "CTRL-R" + std::to_string(j));
1635  }
1636  }
1637  if (swap) {
1638  // we have the qubits in reversed order, we must swap them
1639  for (idx i = 0; i < n_subsys / 2; ++i) {
1640  gate(Gates::get_instance().SWAP, target[i],
1641  target[n_subsys - i - 1]);
1642  }
1643  }
1644 
1645  } else { // qudits
1646  for (idx i = 0; i < n_subsys; ++i) {
1647  // apply qudit Fourier on qudit i
1648  gate(Gates::get_instance().Fd(d_), target[i], "Fd");
1649  // apply controlled rotations
1650  for (idx j = 2; j <= n_subsys - i; ++j) {
1651  // construct Rj
1652  cmat Rj = cmat::Zero(d_, d_);
1653  for (idx m = 0; m < d_; ++m) {
1654  Rj(m, m) = exp(2.0 * pi * m * 1_i / std::pow(d_, j));
1655  }
1656  CTRL(Rj, target[i + j - 1], target[i], {},
1657  "CTRL-R" + std::to_string(j) + "d");
1658  }
1659  }
1660  if (swap) {
1661  // we have the qudits in reversed order, we must swap them
1662  for (idx i = 0; i < n_subsys / 2; ++i) {
1663  gate(Gates::get_instance().SWAPd(d_), target[i],
1664  target[n_subsys - i - 1], "SWAPd");
1665  }
1666  }
1667  }
1668 
1669  return *this;
1670  }
1671 
1672  // std::initializer_list overload, avoids ambiguity for {idx} -> bool
1682  QCircuit& QFT(const std::initializer_list<idx>& target, bool swap = true) {
1683  return QFT(std::vector<idx>(target), swap);
1684  }
1685 
1693  QCircuit& QFT(bool swap = true) { return QFT(get_non_measured(), swap); }
1694 
1704  QCircuit& TFQ(const std::vector<idx>& target,
1705  bool swap QPP_UNUSED_ = true) {
1706  // EXCEPTION CHECKS
1707  try {
1708  // check valid target
1709  if (target.empty())
1710  throw exception::ZeroSize("qpp::QCircuit::TFQ()");
1711  for (auto&& elem : target) {
1712  if (elem >= nq_)
1713  throw exception::OutOfRange("qpp::QCircuit::TFQ()");
1714  // check target was not measured before
1715  if (get_measured(elem))
1716  throw exception::QuditAlreadyMeasured(
1717  "qpp::QCircuit::TFQ()");
1718  }
1719  // check no duplicates target
1720  if (!internal::check_no_duplicates(target))
1721  throw exception::Duplicates("qpp::QCircuit::TFQ()");
1722  } catch (exception::Exception&) {
1723  std::cerr << "At STEP " << get_step_count() << "\n";
1724  throw;
1725  }
1726  // END EXCEPTION CHECKS
1727 
1728  idx n_subsys = target.size();
1729  if (d_ == 2) // qubits
1730  {
1731  if (swap) {
1732  // we have the qubits in reversed order, we must swap them
1733  for (idx i = n_subsys / 2; i-- > 0;) {
1734  gate(Gates::get_instance().SWAP, target[i],
1735  target[n_subsys - i - 1]);
1736  }
1737  }
1738  for (idx i = n_subsys; i-- > 0;) {
1739  // apply controlled rotations
1740  for (idx j = n_subsys - i + 1; j-- > 2;) {
1741  // construct Rj
1742  cmat Rj(2, 2);
1743  Rj << 1, 0, 0, exp(-2.0 * pi * 1_i / std::pow(2, j));
1744  CTRL(Rj, target[i + j - 1], target[i], {},
1745  "CTRL-R" + std::to_string(j) + "+");
1746  }
1747  // apply Hadamard on qubit i
1748  gate(Gates::get_instance().H, target[i]);
1749  }
1750  } else { // qudits
1751  if (swap) {
1752  // we have the qudits in reversed order, we must swap them
1753  for (idx i = n_subsys / 2; i-- > 0;) {
1754  gate(Gates::get_instance().SWAPd(d_), target[i],
1755  target[n_subsys - i - 1], "SWAPd");
1756  }
1757  }
1758  for (idx i = n_subsys; i-- > 0;) {
1759  // apply controlled rotations
1760  for (idx j = n_subsys - i + 1; j-- > 2;) {
1761  // construct Rj
1762  cmat Rj = cmat::Zero(d_, d_);
1763  for (idx m = 0; m < d_; ++m) {
1764  Rj(m, m) = exp(-2.0 * pi * m * 1_i / std::pow(d_, j));
1765  }
1766  CTRL(Rj, target[i + j - 1], target[i], {},
1767  "CTRL-R" + std::to_string(j) + "d+");
1768  }
1769  // apply qudit Fourier on qudit i
1770  gate(qpp::adjoint(Gates::get_instance().Fd(d_)), target[i],
1771  "Fd+");
1772  }
1773  }
1774 
1775  return *this;
1776  }
1777 
1778  // std::initializer_list overload, avoids ambiguity for {idx} -> bool
1788  QCircuit& TFQ(const std::initializer_list<idx>& target, bool swap = true) {
1789  return TFQ(std::vector<idx>(target), swap);
1790  }
1791 
1799  QCircuit& TFQ(bool swap = true) { return TFQ(get_non_measured(), swap); }
1800 
1801  // single ctrl single target
1814  QCircuit& CTRL(const cmat& U, idx ctrl, idx target, idx shift = 0,
1815  std::string name = {}) {
1816  // EXCEPTION CHECKS
1817 
1818  try {
1819  // check valid ctrl and target
1820  if (ctrl >= nq_ || target >= nq_ || ctrl == target)
1821  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1822  if (get_measured(ctrl) || get_measured(target))
1823  throw exception::QuditAlreadyMeasured("qpp::QCircuit::CTRL()");
1824 
1825  // check square matrix for the gate
1826  if (!internal::check_square_mat(U))
1827  throw exception::MatrixNotSquare("qpp::QCircuit::CTRL()");
1828  // check correct dimension
1829  if (static_cast<idx>(U.rows()) != d_)
1830  throw exception::DimsMismatchMatrix("qpp::QCircuit::CTRL()");
1831 
1832  // check shift
1833  if (shift >= d_)
1834  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1835  } catch (exception::Exception&) {
1836  std::cerr << "At STEP " << get_step_count() << "\n";
1837  throw;
1838  }
1839  // END EXCEPTION CHECKS
1840 
1841  if (name.empty()) {
1842  std::string gate_name = qpp::Gates::get_instance().get_name(U);
1843  name = gate_name.empty() ? "CTRL" : "CTRL-" + gate_name;
1844  }
1845  std::size_t hashU = hash_eigen(U);
1846  add_hash_(U, hashU);
1847  gates_.emplace_back(GateType::SINGLE_CTRL_SINGLE_TARGET, hashU,
1848  std::vector<idx>{ctrl}, std::vector<idx>{target},
1849  std::vector<idx>{shift}, name);
1850  step_types_.emplace_back(StepType::GATE);
1851  ++count_[name];
1852 
1853  return *this;
1854  }
1855 
1856  // single ctrl multiple target
1870  QCircuit& CTRL(const cmat& U, idx ctrl, const std::vector<idx>& target,
1871  idx shift = 0, std::string name = {}) {
1872  // EXCEPTION CHECKS
1873 
1874  try {
1875  // check valid ctrl
1876  if (ctrl >= nq_)
1877  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1878  if (get_measured(ctrl))
1879  throw exception::QuditAlreadyMeasured("qpp::QCircuit::CTRL()");
1880 
1881  // check valid target
1882  if (target.empty())
1883  throw exception::ZeroSize("qpp::QCircuit::CTRL()");
1884  for (auto&& elem : target) {
1885  if (elem >= nq_)
1886  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1887  // check target was not measured before
1888  if (get_measured(elem))
1889  throw exception::QuditAlreadyMeasured(
1890  "qpp::QCircuit::CTRL()");
1891  }
1892  // check no duplicates target
1893  if (!internal::check_no_duplicates(target))
1894  throw exception::Duplicates("qpp::QCircuit::CTRL()");
1895 
1896  // check ctrl and target don't share common elements
1897  for (auto&& elem : target)
1898  if (elem == ctrl)
1899  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1900 
1901  // check square matrix for the gate
1902  if (!internal::check_square_mat(U))
1903  throw exception::MatrixNotSquare("qpp::QCircuit::CTRL()");
1904  // check correct dimension
1905  if (static_cast<idx>(U.rows()) != d_)
1906  throw exception::DimsMismatchMatrix("qpp::QCircuit::CTRL()");
1907 
1908  // check shift
1909  if (shift >= d_)
1910  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1911  } catch (exception::Exception&) {
1912  std::cerr << "At STEP " << get_step_count() << "\n";
1913  throw;
1914  }
1915  // END EXCEPTION CHECKS
1916 
1917  if (name.empty()) {
1918  std::string gate_name = qpp::Gates::get_instance().get_name(U);
1919  name = gate_name.empty() ? "CTRL" : "CTRL-" + gate_name;
1920  }
1921  std::size_t hashU = hash_eigen(U);
1922  add_hash_(U, hashU);
1923  gates_.emplace_back(GateType::SINGLE_CTRL_MULTIPLE_TARGET, hashU,
1924  std::vector<idx>{ctrl}, target,
1925  std::vector<idx>{shift}, name);
1926  step_types_.emplace_back(StepType::GATE);
1927  ++count_[name];
1928 
1929  return *this;
1930  }
1931 
1932  // multiple ctrl single target
1946  QCircuit& CTRL(const cmat& U, const std::vector<idx>& ctrl, idx target,
1947  const std::vector<idx>& shift = {}, std::string name = {}) {
1948  // EXCEPTION CHECKS
1949 
1950  try {
1951  // check valid ctrl
1952  for (auto&& elem : ctrl) {
1953  if (elem >= nq_)
1954  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1955  // check ctrl was not measured before
1956  if (get_measured(elem))
1957  throw exception::QuditAlreadyMeasured(
1958  "qpp::QCircuit::CTRL()");
1959  }
1960  // check no duplicates ctrl
1961  if (!internal::check_no_duplicates(ctrl))
1962  throw exception::Duplicates("qpp::QCircuit::CTRL()");
1963 
1964  // check valid target
1965  if (target >= nq_)
1966  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1967  if (get_measured(target))
1968  throw exception::QuditAlreadyMeasured("qpp::QCircuit::CTRL()");
1969 
1970  // check ctrl and target don't share common elements
1971  for (auto&& elem : ctrl)
1972  if (elem == target)
1973  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1974 
1975  // check square matrix for the gate
1976  if (!internal::check_square_mat(U))
1977  throw exception::MatrixNotSquare("qpp::QCircuit::CTRL()");
1978  // check correct dimension
1979  if (static_cast<idx>(U.rows()) != d_)
1980  throw exception::DimsMismatchMatrix("qpp::QCircuit::CTRL()");
1981 
1982  // check shift
1983  if (!shift.empty() && (shift.size() != ctrl.size()))
1984  throw exception::SizeMismatch("qpp::QCircuit::CTRL()");
1985  if (!shift.empty())
1986  for (auto&& elem : shift)
1987  if (elem >= d_)
1988  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
1989  } catch (exception::Exception&) {
1990  std::cerr << "At STEP " << get_step_count() << "\n";
1991  throw;
1992  }
1993  // END EXCEPTION CHECKS
1994 
1995  if (name.empty()) {
1996  std::string gate_name = qpp::Gates::get_instance().get_name(U);
1997  name = gate_name.empty() ? "CTRL" : "CTRL-" + gate_name;
1998  }
1999  std::size_t hashU = hash_eigen(U);
2000  add_hash_(U, hashU);
2001  gates_.emplace_back(GateType::MULTIPLE_CTRL_SINGLE_TARGET, hashU, ctrl,
2002  std::vector<idx>{target}, shift, name);
2003  step_types_.emplace_back(StepType::GATE);
2004  ++count_[name];
2005 
2006  return *this;
2007  }
2008 
2009  // multiple ctrl multiple target
2010  // FIXME
2025  QCircuit& CTRL(const cmat& U, const std::vector<idx>& ctrl,
2026  const std::vector<idx>& target,
2027  const std::vector<idx>& shift = {}, std::string name = {}) {
2028  // EXCEPTION CHECKS
2029 
2030  try {
2031  // check valid ctrl
2032  for (auto&& elem : ctrl) {
2033  if (elem >= nq_)
2034  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
2035  // check ctrl was not measured before
2036  if (get_measured(elem))
2037  throw exception::QuditAlreadyMeasured(
2038  "qpp::QCircuit::CTRL()");
2039  }
2040  // check no duplicates ctrl
2041  if (!internal::check_no_duplicates(ctrl))
2042  throw exception::Duplicates("qpp::QCircuit::CTRL()");
2043 
2044  // check valid target
2045  if (target.empty())
2046  throw exception::ZeroSize("qpp::QCircuit::CTRL()");
2047  for (auto&& elem : target) {
2048  if (elem >= nq_)
2049  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
2050  // check target was not measured before
2051  if (get_measured(elem))
2052  throw exception::QuditAlreadyMeasured(
2053  "qpp::QCircuit::CTRL()");
2054  }
2055  // check no duplicates target
2056  if (!internal::check_no_duplicates(target))
2057  throw exception::Duplicates("qpp::QCircuit::CTRL()");
2058 
2059  // check ctrl and target don't share common elements
2060  for (auto&& elem_ctrl : ctrl)
2061  for (auto&& elem_target : target)
2062  if (elem_ctrl == elem_target)
2063  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
2064 
2065  // check square matrix for the gate
2066  if (!internal::check_square_mat(U))
2067  throw exception::MatrixNotSquare("qpp::QCircuit::CTRL()");
2068  // check correct dimension
2069  if (static_cast<idx>(U.rows()) != d_)
2070  throw exception::DimsMismatchMatrix("qpp::QCircuit::CTRL()");
2071 
2072  // check shift
2073  if (!shift.empty() && (shift.size() != ctrl.size()))
2074  throw exception::SizeMismatch("qpp::QCircuit::CTRL()");
2075  if (!shift.empty())
2076  for (auto&& elem : shift)
2077  if (elem >= d_)
2078  throw exception::OutOfRange("qpp::QCircuit::CTRL()");
2079  } catch (exception::Exception&) {
2080  std::cerr << "At STEP " << get_step_count() << "\n";
2081  throw;
2082  }
2083  // END EXCEPTION CHECKS
2084 
2085  if (name.empty()) {
2086  std::string gate_name = qpp::Gates::get_instance().get_name(U);
2087  name = gate_name.empty() ? "CTRL" : "CTRL-" + gate_name;
2088  }
2089  std::size_t hashU = hash_eigen(U);
2090  add_hash_(U, hashU);
2091  gates_.emplace_back(GateType::MULTIPLE_CTRL_MULTIPLE_TARGET, hashU,
2092  ctrl, std::vector<idx>{target}, shift, name);
2093  step_types_.emplace_back(StepType::GATE);
2094  ++count_[name];
2095 
2096  return *this;
2097  }
2098 
2099  // custom multiple control composed target
2115  QCircuit& CTRL_custom(const cmat& U, const std::vector<idx>& ctrl,
2116  const std::vector<idx>& target,
2117  const std::vector<idx>& shift = {},
2118  std::string name = {}) {
2119  // EXCEPTION CHECKS
2120 
2121  idx n = static_cast<idx>(target.size());
2122  idx D = static_cast<idx>(std::llround(std::pow(d_, n)));
2123 
2124  try {
2125  // check valid ctrl
2126  for (auto&& elem : ctrl) {
2127  if (elem >= nq_)
2128  throw exception::OutOfRange("qpp::QCircuit::CTRL_custom()");
2129  // check ctrl was not measured before
2130  if (get_measured(elem))
2131  throw exception::QuditAlreadyMeasured(
2132  "qpp::QCircuit::CTRL_custom()");
2133  }
2134  // check no duplicates ctrl
2135  if (!internal::check_no_duplicates(ctrl))
2136  throw exception::Duplicates("qpp::QCircuit::CTRL_custom()");
2137 
2138  // check valid target
2139  if (target.empty())
2140  throw exception::ZeroSize("qpp::QCircuit::CTRL_custom()");
2141  for (auto&& elem : target) {
2142  if (elem >= nq_)
2143  throw exception::OutOfRange("qpp::QCircuit::CTRL_custom()");
2144  // check target was not measured before
2145  if (get_measured(elem))
2146  throw exception::QuditAlreadyMeasured(
2147  "qpp::QCircuit::CTRL_custom()");
2148  }
2149 
2150  // check ctrl and target don't share common elements
2151  for (auto&& elem_ctrl : ctrl)
2152  for (auto&& elem_target : target)
2153  if (elem_ctrl == elem_target)
2154  throw exception::OutOfRange(
2155  "qpp::QCircuit::CTRL_custom()");
2156 
2157  // check square matrix for the gate
2158  if (!internal::check_square_mat(U))
2159  throw exception::MatrixNotSquare(
2160  "qpp::QCircuit::CTRL_custom()");
2161  // check correct dimension
2162  if (static_cast<idx>(U.rows()) != D)
2163  throw exception::DimsMismatchMatrix(
2164  "qpp::QCircuit::CTRL_custom()");
2165 
2166  // check shift
2167  if (!shift.empty() && (shift.size() != ctrl.size()))
2168  throw exception::SizeMismatch("qpp::QCircuit::CTRL_custom()");
2169  if (!shift.empty())
2170  for (auto&& elem : shift)
2171  if (elem >= d_)
2172  throw exception::OutOfRange(
2173  "qpp::QCircuit::CTRL_custom()");
2174  } catch (exception::Exception&) {
2175  std::cerr << "At STEP " << get_step_count() << "\n";
2176  throw;
2177  }
2178  // END EXCEPTION CHECKS
2179 
2180  if (name.empty()) {
2181  std::string gate_name = qpp::Gates::get_instance().get_name(U);
2182  name = gate_name.empty() ? "CTRL" : "CTRL-" + gate_name;
2183  }
2184  std::size_t hashU = hash_eigen(U);
2185  add_hash_(U, hashU);
2186  gates_.emplace_back(GateType::CUSTOM_CTRL, hashU, ctrl, target, shift,
2187  name);
2188  step_types_.emplace_back(StepType::GATE);
2189  ++count_[name];
2190 
2191  return *this;
2192  }
2193 
2194  // single ctrl single target
2195  // FIXME, use the corresponding dits
2208  QCircuit& cCTRL(const cmat& U, idx ctrl_dit, idx target, idx shift = 0,
2209  std::string name = {}) {
2210  // EXCEPTION CHECKS
2211 
2212  try {
2213  // check valid ctrl_dit and target
2214  if (ctrl_dit >= nc_ || target >= nq_)
2215  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2216  if (get_measured(target))
2217  throw exception::QuditAlreadyMeasured("qpp::QCircuit::cCTRL()");
2218 
2219  // check square matrix for the gate
2220  if (!internal::check_square_mat(U))
2221  throw exception::MatrixNotSquare("qpp::QCircuit::cCTRL()");
2222  // check correct dimension
2223  if (static_cast<idx>(U.rows()) != d_)
2224  throw exception::DimsMismatchMatrix("qpp::QCircuit::cCTRL()");
2225 
2226  // check shift
2227  if (shift >= d_)
2228  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2229  } catch (exception::Exception&) {
2230  std::cerr << "At STEP " << get_step_count() << "\n";
2231  throw;
2232  }
2233  // END EXCEPTION CHECKS
2234 
2235  if (name.empty()) {
2236  std::string gate_name = qpp::Gates::get_instance().get_name(U);
2237  name = gate_name.empty() ? "cCTRL" : "cCTRL-" + gate_name;
2238  }
2239 
2240  std::size_t hashU = hash_eigen(U);
2241  add_hash_(U, hashU);
2242  gates_.emplace_back(GateType::SINGLE_cCTRL_SINGLE_TARGET, hashU,
2243  std::vector<idx>{ctrl_dit},
2244  std::vector<idx>{target}, std::vector<idx>{shift},
2245  name);
2246  step_types_.emplace_back(StepType::GATE);
2247  ++count_[name];
2248 
2249  return *this;
2250  }
2251 
2252  // single ctrl multiple targets
2266  QCircuit& cCTRL(const cmat& U, idx ctrl_dit, const std::vector<idx>& target,
2267  idx shift = 0, std::string name = {}) {
2268  // EXCEPTION CHECKS
2269 
2270  try {
2271  // check valid ctrl_dit
2272  if (ctrl_dit >= nc_)
2273  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2274 
2275  // check valid target
2276  if (target.empty())
2277  throw exception::ZeroSize("qpp::QCircuit::cCTRL()");
2278  for (auto&& elem : target) {
2279  if (elem >= nq_)
2280  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2281  // check target was not measured before
2282  if (get_measured(elem))
2283  throw exception::QuditAlreadyMeasured(
2284  "qpp::QCircuit::cCTRL()");
2285  }
2286  // check no duplicates target
2287  if (!internal::check_no_duplicates(target))
2288  throw exception::Duplicates("qpp::QCircuit::cCTRL()");
2289 
2290  // check square matrix for the gate
2291  if (!internal::check_square_mat(U))
2292  throw exception::MatrixNotSquare("qpp::QCircuit::cCTRL()");
2293  // check correct dimension
2294  if (static_cast<idx>(U.rows()) != d_)
2295  throw exception::DimsMismatchMatrix("qpp::QCircuit::cCTRL()");
2296 
2297  // check shift
2298  if (shift >= d_)
2299  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2300  } catch (exception::Exception&) {
2301  std::cerr << "At STEP " << get_step_count() << "\n";
2302  throw;
2303  }
2304  // END EXCEPTION CHECKS
2305 
2306  if (name.empty()) {
2307  std::string gate_name = qpp::Gates::get_instance().get_name(U);
2308  name = gate_name.empty() ? "cCTRL" : "cCTRL-" + gate_name;
2309  }
2310  std::size_t hashU = hash_eigen(U);
2311  add_hash_(U, hashU);
2312  gates_.emplace_back(GateType::SINGLE_cCTRL_MULTIPLE_TARGET, hashU,
2313  std::vector<idx>{ctrl_dit}, target,
2314  std::vector<idx>{shift}, name);
2315  step_types_.emplace_back(StepType::GATE);
2316  ++count_[name];
2317 
2318  return *this;
2319  }
2320 
2321  // multiple ctrl single target
2335  QCircuit& cCTRL(const cmat& U, const std::vector<idx>& ctrl_dits,
2336  idx target, const std::vector<idx>& shift = {},
2337  std::string name = {}) {
2338  // EXCEPTION CHECKS
2339 
2340  try {
2341  // check valid ctrl_dits
2342  for (auto&& elem : ctrl_dits) {
2343  if (elem >= nc_)
2344  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2345  }
2346  // check no duplicates ctrl_dits
2347  if (!internal::check_no_duplicates(ctrl_dits))
2348  throw exception::Duplicates("qpp::QCircuit::cCTRL()");
2349 
2350  // check valid target
2351  if (target >= nq_)
2352  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2353  // check target was not measured before
2354  if (get_measured(target))
2355  throw exception::QuditAlreadyMeasured("qpp::QCircuit::cCTRL()");
2356 
2357  // check square matrix for the gate
2358  if (!internal::check_square_mat(U))
2359  throw exception::MatrixNotSquare("qpp::QCircuit::cCTRL()");
2360  // check correct dimension
2361  if (static_cast<idx>(U.rows()) != d_)
2362  throw exception::DimsMismatchMatrix("qpp::QCircuit::cCTRL()");
2363 
2364  // check shift
2365  if (!shift.empty() && (shift.size() != ctrl_dits.size()))
2366  throw exception::SizeMismatch("qpp::QCircuit::cCTRL()");
2367  if (!shift.empty())
2368  for (auto&& elem : shift)
2369  if (elem >= d_)
2370  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2371 
2372  } catch (exception::Exception&) {
2373  std::cerr << "At STEP " << get_step_count() << "\n";
2374  throw;
2375  }
2376  // END EXCEPTION CHECKS
2377 
2378  if (name.empty()) {
2379  std::string gate_name = qpp::Gates::get_instance().get_name(U);
2380  name = gate_name.empty() ? "cCTRL" : "cCTRL-" + gate_name;
2381  }
2382  std::size_t hashU = hash_eigen(U);
2383  add_hash_(U, hashU);
2384  gates_.emplace_back(GateType::MULTIPLE_cCTRL_SINGLE_TARGET, hashU,
2385  ctrl_dits, std::vector<idx>{target}, shift, name);
2386  step_types_.emplace_back(StepType::GATE);
2387  ++count_[name];
2388 
2389  return *this;
2390  }
2391 
2392  // multiple ctrl multiple targets
2408  QCircuit& cCTRL(const cmat& U, const std::vector<idx>& ctrl_dits,
2409  const std::vector<idx>& target,
2410  const std::vector<idx>& shift = {}, std::string name = {}) {
2411  // EXCEPTION CHECKS
2412 
2413  try {
2414  // check valid ctrl_dits
2415  for (auto&& elem : ctrl_dits) {
2416  if (elem >= nc_)
2417  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2418  }
2419  // check no duplicates ctrl_dits
2420  if (!internal::check_no_duplicates(ctrl_dits))
2421  throw exception::Duplicates("qpp::QCircuit::cCTRL()");
2422 
2423  // check valid target
2424  if (target.empty())
2425  throw exception::ZeroSize("qpp::QCircuit::cCTRL()");
2426  for (auto&& elem : target) {
2427  if (elem >= nq_)
2428  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2429  // check target was not measured before
2430  if (get_measured(elem))
2431  throw exception::QuditAlreadyMeasured(
2432  "qpp::QCircuit::cCTRL()");
2433  }
2434  // check no duplicates target
2435  if (!internal::check_no_duplicates(target))
2436  throw exception::Duplicates("qpp::QCircuit::cCTRL()");
2437 
2438  // check square matrix for the gate
2439  if (!internal::check_square_mat(U))
2440  throw exception::MatrixNotSquare("qpp::QCircuit::cCTRL()");
2441  // check correct dimension
2442  if (static_cast<idx>(U.rows()) != d_)
2443  throw exception::DimsMismatchMatrix("qpp::QCircuit::cCTRL()");
2444 
2445  // check shift
2446  if (!shift.empty() && (shift.size() != ctrl_dits.size()))
2447  throw exception::SizeMismatch("qpp::QCircuit::cCTRL()");
2448  if (!shift.empty())
2449  for (auto&& elem : shift)
2450  if (elem >= d_)
2451  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2452  } catch (exception::Exception&) {
2453  std::cerr << "At STEP " << get_step_count() << "\n";
2454  throw;
2455  }
2456  // END EXCEPTION CHECKS
2457 
2458  if (name.empty()) {
2459  std::string gate_name = qpp::Gates::get_instance().get_name(U);
2460  name = gate_name.empty() ? "cCTRL" : "cCTRL-" + gate_name;
2461  }
2462  std::size_t hashU = hash_eigen(U);
2463  add_hash_(U, hashU);
2464  gates_.emplace_back(GateType::MULTIPLE_cCTRL_MULTIPLE_TARGET, hashU,
2465  ctrl_dits, std::vector<idx>{target}, shift, name);
2466  step_types_.emplace_back(StepType::GATE);
2467  ++count_[name];
2468 
2469  return *this;
2470  }
2471 
2472  // custom controlled gate with multiple controls and multiple targets
2488  QCircuit& cCTRL_custom(const cmat& U, const std::vector<idx>& ctrl_dits,
2489  const std::vector<idx>& target,
2490  const std::vector<idx>& shift = {},
2491  std::string name = {}) {
2492  // EXCEPTION CHECKS
2493 
2494  idx n = static_cast<idx>(target.size());
2495  idx D = static_cast<idx>(std::llround(std::pow(d_, n)));
2496 
2497  try {
2498  // check valid ctrl_dits
2499  for (auto&& elem : ctrl_dits) {
2500  if (elem >= nc_)
2501  throw exception::OutOfRange(
2502  "qpp::QCircuit::cCTRL_custom()");
2503  }
2504  // check no duplicates ctrl_dits
2505  if (!internal::check_no_duplicates(ctrl_dits))
2506  throw exception::Duplicates("qpp::QCircuit::cCTRL_custom()");
2507 
2508  // check valid target
2509  if (target.empty())
2510  throw exception::ZeroSize("qpp::QCircuit::cCTRL_custom()");
2511  for (auto&& elem : target) {
2512  if (elem >= nq_)
2513  throw exception::OutOfRange(
2514  "qpp::QCircuit::cCTRL_custom()");
2515  // check target was not measured before
2516  if (get_measured(elem))
2517  throw exception::QuditAlreadyMeasured(
2518  "qpp::QCircuit::cCTRL_custom()");
2519  }
2520  // check no duplicates target
2521  if (!internal::check_no_duplicates(target))
2522  throw exception::Duplicates("qpp::QCircuit::cCTRL_custom()");
2523 
2524  // check square matrix for the gate
2525  if (!internal::check_square_mat(U))
2526  throw exception::MatrixNotSquare(
2527  "qpp::QCircuit::cCTRL_custom()");
2528  // check correct dimension
2529  if (static_cast<idx>(U.rows()) != D)
2530  throw exception::DimsMismatchMatrix(
2531  "qpp::QCircuit::cCTRL_custom()");
2532 
2533  // check shift
2534  if (!shift.empty() && (shift.size() != ctrl_dits.size()))
2535  throw exception::SizeMismatch("qpp::QCircuit::cCTRL()");
2536  if (!shift.empty())
2537  for (auto&& elem : shift)
2538  if (elem >= d_)
2539  throw exception::OutOfRange("qpp::QCircuit::cCTRL()");
2540  } catch (exception::Exception&) {
2541  std::cerr << "At STEP " << get_step_count() << "\n";
2542  throw;
2543  }
2544  // END EXCEPTION CHECKS
2545 
2546  if (name.empty()) {
2547  std::string gate_name = qpp::Gates::get_instance().get_name(U);
2548  name = gate_name.empty() ? "cCTRL" : "cCTRL-" + gate_name;
2549  }
2550  std::size_t hashU = hash_eigen(U);
2551  add_hash_(U, hashU);
2552  gates_.emplace_back(GateType::CUSTOM_cCTRL, hashU, ctrl_dits, target,
2553  shift, name);
2554  step_types_.emplace_back(StepType::GATE);
2555  ++count_[name];
2556 
2557  return *this;
2558  }
2559 
2560  // Z measurement of single qudit
2571  QCircuit& measureZ(idx target, idx c_reg, bool destructive = true,
2572  std::string name = {}) {
2573  // EXCEPTION CHECKS
2574 
2575  try {
2576  // measuring non-existing qudit
2577  if (target >= nq_)
2578  throw exception::OutOfRange("qpp::QCircuit::measureZ()");
2579  // trying to put the result into a non-existing classical slot
2580  if (c_reg >= nc_)
2581  throw exception::OutOfRange("qpp::QCircuit::measureZ()");
2582  // qudit was measured before
2583  if (get_measured(target))
2584  throw exception::QuditAlreadyMeasured(
2585  "qpp:QCircuit::measureZ()");
2586  } catch (exception::Exception&) {
2587  std::cerr << "At STEP " << get_step_count() << "\n";
2588  throw;
2589  }
2590  // END EXCEPTION CHECKS
2591 
2592  if (name.empty())
2593  name = "mZ";
2594  if (destructive) {
2595  measured_[target] = true;
2596  measurements_.emplace_back(MeasureType::MEASURE_Z,
2597  std::vector<std::size_t>{},
2598  std::vector<idx>{target}, c_reg, name);
2599  } else {
2600  measurements_.emplace_back(MeasureType::MEASURE_Z_ND,
2601  std::vector<std::size_t>{},
2602  std::vector<idx>{target}, c_reg, name);
2603  }
2604  step_types_.emplace_back(StepType::MEASUREMENT);
2605  ++measurement_count_[name];
2606 
2607  return *this;
2608  }
2609 
2610  // Z measurement of multiple qudits
2624  QCircuit& measureZ(const std::vector<idx>& target, idx c_reg,
2625  bool destructive = true, std::string name = {}) {
2626  // EXCEPTION CHECKS
2627 
2628  try {
2629  // check valid target
2630  if (target.empty())
2631  throw exception::ZeroSize("qpp::QCircuit::measureZ()");
2632  for (auto&& elem : target) {
2633  // measuring non-existing qudit
2634  if (elem >= nq_)
2635  throw exception::OutOfRange("qpp::QCircuit::measureZ()");
2636  // qudit was measured before
2637  if (get_measured(elem))
2638  throw exception::QuditAlreadyMeasured(
2639  "qpp:QCircuit::measureZ()");
2640  }
2641  // trying to put the result into a non-existing classical slot
2642  if (c_reg >= nc_)
2643  throw exception::OutOfRange("qpp::QCircuit::measureZ()");
2644  } catch (exception::Exception&) {
2645  std::cerr << "At STEP " << get_step_count() << "\n";
2646  throw;
2647  }
2648  // END EXCEPTION CHECKS
2649 
2650  if (name.empty())
2651  name = "mZ";
2652 
2653  if (destructive) {
2654  for (auto&& elem : target) {
2655  measured_[elem] = true;
2656  }
2657  measurements_.emplace_back(MeasureType::MEASURE_Z_MANY,
2658  std::vector<std::size_t>{}, target,
2659  c_reg, name);
2660  } else {
2661  measurements_.emplace_back(MeasureType::MEASURE_Z_MANY_ND,
2662  std::vector<std::size_t>{}, target,
2663  c_reg, name);
2664  }
2665  step_types_.emplace_back(StepType::MEASUREMENT);
2666  ++measurement_count_[name];
2667 
2668  return *this;
2669  }
2670 
2671  // measurement of single qudit in the orthonormal basis or rank-1 projectors
2672  // specified by the columns of matrix V
2686  QCircuit& measureV(const cmat& V, idx target, idx c_reg,
2687  bool destructive = true, std::string name = {}) {
2688  // EXCEPTION CHECKS
2689 
2690  try {
2691  // measuring non-existing qudit
2692  if (target >= nq_)
2693  throw exception::OutOfRange("qpp::QCircuit::measureV()");
2694  // trying to put the result into a non-existing classical slot
2695  if (c_reg >= nc_)
2696  throw exception::OutOfRange("qpp::QCircuit::measureV()");
2697  // qudit was measured before
2698  if (get_measured(target))
2699  throw exception::QuditAlreadyMeasured(
2700  "qpp:QCircuit::measureV()");
2701  } catch (exception::Exception&) {
2702  std::cerr << "At STEP " << get_step_count() << "\n";
2703  throw;
2704  }
2705  // END EXCEPTION CHECKS
2706 
2707  if (name.empty())
2708  name = "m" + qpp::Gates::get_instance().get_name(V);
2709 
2710  if (destructive) {
2711  measured_[target] = true;
2712  measurements_.emplace_back(MeasureType::MEASURE_V,
2713  std::vector<std::size_t>{hash_eigen(V)},
2714  std::vector<idx>{target}, c_reg, name);
2715  } else {
2716  measurements_.emplace_back(MeasureType::MEASURE_V_ND,
2717  std::vector<std::size_t>{hash_eigen(V)},
2718  std::vector<idx>{target}, c_reg, name);
2719  }
2720  step_types_.emplace_back(StepType::MEASUREMENT);
2721  ++measurement_count_[name];
2722 
2723  return *this;
2724  }
2725 
2726  // measurement of multiple qudits in the orthonormal basis or rank-1
2727  // projectors specified by the columns of matrix V
2741  QCircuit& measureV(const cmat& V, const std::vector<idx>& target, idx c_reg,
2742  bool destructive = true, std::string name = {}) {
2743  // EXCEPTION CHECKS
2744 
2745  try {
2746  // check valid target
2747  if (target.empty())
2748  throw exception::ZeroSize("qpp::QCircuit::measureV()");
2749  for (auto&& elem : target) {
2750  if (elem >= nq_)
2751  throw exception::OutOfRange("qpp::QCircuit::measureV()");
2752  // check target was not measured before
2753  if (get_measured(elem))
2754  throw exception::QuditAlreadyMeasured(
2755  "qpp::QCircuit::measureV()");
2756  }
2757  // check no duplicates target
2758  if (!internal::check_no_duplicates(target))
2759  throw exception::Duplicates("qpp::QCircuit::measureV()");
2760 
2761  // trying to put the result into a non-existing classical slot
2762  if (c_reg >= nc_)
2763  throw exception::OutOfRange("qpp::QCircuit::measureV()");
2764  // qudit was measured before
2765  for (auto&& elem : target)
2766  if (get_measured(elem))
2767  throw exception::QuditAlreadyMeasured(
2768  "qpp::QCircuit::measureV()");
2769  } catch (exception::Exception&) {
2770  std::cerr << "At STEP " << get_step_count() << "\n";
2771  throw;
2772  }
2773  // END EXCEPTION CHECKS
2774 
2775  if (name.empty())
2776  name = "m" + qpp::Gates::get_instance().get_name(V);
2777 
2778  if (destructive) {
2779  for (auto&& elem : target)
2780  measured_[elem] = true;
2781  measurements_.emplace_back(MeasureType::MEASURE_V_MANY,
2782  std::vector<std::size_t>{hash_eigen(V)},
2783  target, c_reg, name);
2784  } else {
2785  measurements_.emplace_back(MeasureType::MEASURE_V_MANY_ND,
2786  std::vector<std::size_t>{hash_eigen(V)},
2787  target, c_reg, name);
2788  }
2789  step_types_.emplace_back(StepType::MEASUREMENT);
2790  ++measurement_count_[name];
2791 
2792  return *this;
2793  }
2794 
2803  QCircuit& discard(idx target, std::string name = {}) {
2804  // EXCEPTION CHECKS
2805 
2806  try {
2807  // discarding non-existing qudit
2808  if (target >= nq_)
2809  throw exception::OutOfRange("qpp::QCircuit::discard()");
2810  // qudit was measured before
2811  if (get_measured(target))
2812  throw exception::QuditAlreadyMeasured(
2813  "qpp:QCircuit::discard()");
2814  } catch (exception::Exception&) {
2815  std::cerr << "At STEP " << get_step_count() << "\n";
2816  throw;
2817  }
2818  // END EXCEPTION CHECKS
2819 
2820  if (name.empty())
2821  name = "discard";
2822 
2823  measured_[target] = true;
2824  measurements_.emplace_back(MeasureType::DISCARD,
2825  std::vector<std::size_t>{},
2826  std::vector<idx>{target}, -1, name);
2827 
2828  step_types_.emplace_back(StepType::MEASUREMENT);
2829  ++measurement_count_[name];
2830 
2831  return *this;
2832  }
2833 
2842  QCircuit& discard(const std::vector<idx>& target, std::string name = {}) {
2843  // EXCEPTION CHECKS
2844 
2845  try {
2846  // check valid target
2847  if (target.empty())
2848  throw exception::ZeroSize("qpp::QCircuit::discard()");
2849  for (auto&& elem : target) {
2850  // discarding non-existing qudit
2851  if (elem >= nq_)
2852  throw exception::OutOfRange("qpp::QCircuit::discard()");
2853  // qudit was measured before
2854  if (get_measured(elem))
2855  throw exception::QuditAlreadyMeasured(
2856  "qpp:QCircuit::discard()");
2857  }
2858  } catch (exception::Exception&) {
2859  std::cerr << "At STEP " << get_step_count() << "\n";
2860  throw;
2861  }
2862  // END EXCEPTION CHECKS
2863 
2864  if (name.empty())
2865  name = "discard";
2866 
2867  for (auto&& elem : target) {
2868  measured_[elem] = true;
2869  }
2870  measurements_.emplace_back(MeasureType::DISCARD_MANY,
2871  std::vector<std::size_t>{}, target, -1,
2872  name);
2873  step_types_.emplace_back(StepType::MEASUREMENT);
2874  ++measurement_count_[name];
2875 
2876  return *this;
2877  }
2878 
2887  QCircuit& nop() {
2888  step_types_.emplace_back(StepType::NOP);
2889 
2890  return *this;
2891  }
2892 
2893  // reset single qudit
2903  QCircuit& reset(idx target, std::string name = {}) {
2904  // EXCEPTION CHECKS
2905 
2906  try {
2907  // resetting non-existing qudit
2908  if (target >= nq_)
2909  throw exception::OutOfRange("qpp::QCircuit::reset()");
2910  // qudit was measured before
2911  if (get_measured(target))
2912  throw exception::QuditAlreadyMeasured("qpp:QCircuit::reset()");
2913  } catch (exception::Exception&) {
2914  std::cerr << "At STEP " << get_step_count() << "\n";
2915  throw;
2916  }
2917  // END EXCEPTION CHECKS
2918 
2919  if (name.empty())
2920  name = "reset";
2921  measurements_.emplace_back(MeasureType::RESET,
2922  std::vector<std::size_t>{},
2923  std::vector<idx>{target}, -1, name);
2924  step_types_.emplace_back(StepType::MEASUREMENT);
2925  ++measurement_count_[name];
2926 
2927  return *this;
2928  }
2929 
2930  // reset multiple qudits
2940  QCircuit& reset(const std::vector<idx>& target, std::string name = {}) {
2941  // EXCEPTION CHECKS
2942 
2943  try {
2944  // check valid target
2945  if (target.empty())
2946  throw exception::ZeroSize("qpp::QCircuit::reset()");
2947  for (auto&& elem : target) {
2948  // resetting non-existing qudit
2949  if (elem >= nq_)
2950  throw exception::OutOfRange("qpp::QCircuit::reset()");
2951  // qudit was measured before
2952  if (get_measured(elem))
2953  throw exception::QuditAlreadyMeasured(
2954  "qpp:QCircuit::reset()");
2955  }
2956  } catch (exception::Exception&) {
2957  std::cerr << "At STEP " << get_step_count() << "\n";
2958  throw;
2959  }
2960  // END EXCEPTION CHECKS
2961 
2962  if (name.empty())
2963  name = "reset";
2964  measurements_.emplace_back(MeasureType::RESET_MANY,
2965  std::vector<std::size_t>{}, target, -1,
2966  name);
2967  step_types_.emplace_back(StepType::MEASUREMENT);
2968  ++measurement_count_[name];
2969 
2970  return *this;
2971  }
2972 
2982  QCircuit& replicate(idx n) {
2983  // EXCEPTION CHECKS
2984 
2985  if (n == 0)
2986  throw exception::OutOfRange("qpp::QCircuit::replicate()");
2987  if (!get_measured().empty())
2988  throw exception::QuditAlreadyMeasured("qpp::QCircuit::replicate()");
2989  if (n == 1)
2990  return *this;
2991  // END EXCEPTION CHECKS
2992 
2993  auto gates_copy = gates_;
2994  idx gates_size = gates_.size();
2995 
2996  auto step_types_copy = step_types_;
2997  idx step_types_size = step_types_.size();
2998 
2999  gates_.resize(gates_.size() * n);
3000  step_types_.resize(step_types_size * n);
3001 
3002  for (idx i = 0; i < n - 1; ++i) {
3003  std::copy(std::begin(gates_copy), std::end(gates_copy),
3004  std::next(std::begin(gates_), (i + 1) * gates_size));
3005  std::copy(
3006  std::begin(step_types_copy), std::end(step_types_copy),
3007  std::next(std::begin(step_types_), (i + 1) * step_types_size));
3008  }
3009 
3010  for (auto& elem : count_)
3011  elem.second *= n;
3012 
3013  return *this;
3014  }
3015 
3038  QCircuit& add_circuit(QCircuit other, bigint pos_qudit, idx pos_dit = -1) {
3039  // EXCEPTION CHECKS
3040 
3041  // check equal dimensions
3042  if (other.d_ != d_)
3043  throw exception::DimsNotEqual("qpp::QCircuit::add_circuit()");
3044  // check classical dits
3045  if (pos_dit == static_cast<idx>(-1))
3046  pos_dit = nc_;
3047  else if (pos_dit > nc_)
3048  throw exception::OutOfRange("qpp::QCircuit::add_circuit()");
3049  // check overlapping qudits (in the current instance) were not already
3050  // destructively measured
3051  if (pos_qudit >= 0 && static_cast<idx>(pos_qudit) < nq_) {
3052  for (idx i = 0;
3053  i < std::min(static_cast<idx>(nq_ - pos_qudit), other.nq_);
3054  ++i)
3055  if (get_measured(pos_qudit + i))
3056  throw exception::QuditAlreadyMeasured(
3057  "qpp::QCircuit::add_circuit()");
3058  }
3059  // END EXCEPTION CHECKS
3060 
3061  // STEP 0: add additional qudits (if needed)
3062  idx extra_qudits = 0;
3063  // add qudits before beginning
3064  if (pos_qudit < 0) {
3065  extra_qudits = std::abs(pos_qudit);
3066  add_qudit(extra_qudits, 0);
3067  } else {
3068  idx tmp = pos_qudit + other.nq_;
3069  if (tmp > nq_) {
3070  extra_qudits = tmp - nq_;
3071  add_qudit(extra_qudits);
3072  }
3073 
3074  // STEP 1: modify the copy of the to-be-added circuit
3075 
3076  // update gate indexes
3077  for (auto& gate : other.gates_) {
3078  switch (gate.gate_type_) {
3079  // classically-controlled gates
3080  case GateType::SINGLE_cCTRL_SINGLE_TARGET:
3081  case GateType::SINGLE_cCTRL_MULTIPLE_TARGET:
3082  case GateType::MULTIPLE_cCTRL_SINGLE_TARGET:
3083  case GateType::MULTIPLE_cCTRL_MULTIPLE_TARGET:
3084  case GateType::CUSTOM_cCTRL:
3085  for (auto& pos : gate.ctrl_) {
3086  pos += pos_dit;
3087  }
3088  break;
3089  // quantumly-controlled gates
3090  default:
3091  for (auto& pos : gate.ctrl_) {
3092  pos += pos_qudit;
3093  }
3094  break;
3095  }
3096 
3097  // non-controlled gates
3098  for (auto& pos : gate.target_) {
3099  pos += pos_qudit;
3100  }
3101  }
3102 
3103  // update measurement indexes
3104  for (auto& measurement : other.measurements_) {
3105  for (auto& pos : measurement.target_) {
3106  pos += pos_qudit;
3107  }
3108  measurement.c_reg_ += pos_dit;
3109  }
3110  } // end else
3111 
3112  // STEP 2: append the copy of the to-be-added circuit to the current
3113  // instance
3114  // insert classical dits from the to-be-added circuit
3115  add_dit(other.nc_, pos_dit);
3116 
3117  // insert the measured vector
3118  measured_.insert(std::next(std::begin(measured_), pos_dit),
3119  std::begin(other.measured_),
3120  std::end(other.measured_));
3121 
3122  // append gate steps vector
3123  gates_.insert(std::end(gates_), std::begin(other.gates_),
3124  std::end(other.gates_));
3125 
3126  // append measurement steps vector
3127  measurements_.insert(std::end(measurements_),
3128  std::begin(other.measurements_),
3129  std::end(other.measurements_));
3130 
3131  // append step types vector
3132  step_types_.insert(std::end(step_types_), std::begin(other.step_types_),
3133  std::end(other.step_types_));
3134 
3135  // STEP 3: modify gate counts, hash tables etc accordingly
3136  // update matrix hash table
3137  for (auto& elem : other.cmat_hash_tbl_)
3138  cmat_hash_tbl_[elem.first] = elem.second;
3139  // update gate counts
3140  for (auto& elem : other.count_)
3141  count_[elem.first] += elem.second;
3142  // update measurement counts
3143  for (auto& elem : other.measurement_count_)
3144  measurement_count_[elem.first] += elem.second;
3145 
3146  return *this;
3147  }
3148 
3156  QCircuit& kron(const QCircuit& qc) {
3157  add_circuit(qc, nq_);
3158 
3159  return *this;
3160  }
3161 
3167  QCircuit& adjoint() {
3168  // EXCEPTION CHECKS
3169 
3170  if (get_measured().size() > 0)
3171  throw exception::QuditAlreadyMeasured("qpp::QCircuit()::adjoint()");
3172  // END EXCEPTION CHECKS
3173 
3174  auto htbl = cmat_hash_tbl_; // copy the gate hash table of other
3175  cmat_hash_tbl_.clear();
3176 
3177  std::reverse(std::begin(gates_), std::end(gates_));
3178  std::reverse(std::begin(step_types_), std::end(step_types_));
3179 
3180  for (auto& elem : gates_) {
3181  // get the gate and its corresponding hash
3182  std::size_t hashU = elem.gate_hash_;
3183  cmat U = htbl[hashU];
3184 
3185  // compute the adjoints
3186  cmat Udagger = qpp::adjoint(U);
3187  std::size_t hashUdagger = hash_eigen(Udagger);
3188 
3189  // modify and add hash
3190  elem.gate_hash_ = hashUdagger;
3191  if (!elem.name_.empty())
3192  elem.name_ += "+";
3193  add_hash_(Udagger, hashUdagger);
3194  }
3195 
3196  return *this;
3197  }
3198 
3209  std::string to_JSON(bool enclosed_in_curly_brackets = true) const override {
3210  std::string result;
3211 
3212  if (enclosed_in_curly_brackets)
3213  result += "{";
3214 
3215  result += "\"nq\" : " + std::to_string(nq_);
3216  result += ", \"nc\" : " + std::to_string(nc_);
3217  result += ", \"d\" : " + std::to_string(d_);
3218  result += ", \"name\" : \"" + name_ + '\"';
3219 
3220  bool is_first = true;
3221  std::ostringstream ss;
3222  result += ", \"steps\" : [";
3223  for (auto&& elem : *this) {
3224  if (is_first) {
3225  is_first = false;
3226  } else {
3227  result += ", ";
3228  }
3229  result += "{\"step\" : " + std::to_string(elem.ip_) + ", ";
3230  result += "\"type\" : ";
3231  // gate step
3232  if (elem.type_ == StepType::GATE) {
3233  idx pos = std::distance(std::begin(elem.value_type_qc_->gates_),
3234  elem.gates_ip_);
3235  ss.str("");
3236  ss.clear();
3237  ss << gates_[pos].gate_type_;
3238  result += '\"' + ss.str() + "\", ";
3239  if (!gates_[pos].ctrl_.empty()) {
3240  ss.str("");
3241  ss.clear();
3242  ss << disp(gates_[pos].ctrl_, ", ");
3243  if (gates_[pos].gate_type_ >=
3244  GateType::SINGLE_cCTRL_SINGLE_TARGET)
3245  result += "\"c_ctrl\" : " + ss.str() + ", ";
3246  else
3247  result += "\"ctrl\" : " + ss.str() + ", ";
3248  }
3249  ss.str("");
3250  ss.clear();
3251  ss << disp(gates_[pos].target_, ", ");
3252  result += "\"target\" : " + ss.str() + ", ";
3253 
3254  if (!gates_[pos].shift_.empty()) {
3255  ss.str("");
3256  ss.clear();
3257  ss << disp(gates_[pos].shift_, ", ");
3258  result += "\"shift\" : " + ss.str() + ", ";
3259  }
3260 
3261  result += "\"name\" : ";
3262  result += '\"' + gates_[pos].name_ + "\"}";
3263  }
3264  // measurement step
3265  else if (elem.type_ == StepType::MEASUREMENT) {
3266  idx pos = std::distance(
3267  std::begin(elem.value_type_qc_->measurements_),
3268  elem.measurements_ip_);
3269  ss.str("");
3270  ss.clear();
3271  ss << measurements_[pos].measurement_type_;
3272  result += '\"' + ss.str() + "\", ";
3273  ss.str("");
3274  ss.clear();
3275  ss << disp(measurements_[pos].target_, ", ");
3276  result += "\"target\" : " + ss.str() + ", ";
3277 
3278  if (measurements_[pos].measurement_type_ !=
3279  MeasureType::RESET &&
3280  measurements_[pos].measurement_type_ !=
3281  MeasureType::RESET_MANY &&
3282  measurements_[pos].measurement_type_ !=
3283  MeasureType::DISCARD &&
3284  measurements_[pos].measurement_type_ !=
3285  MeasureType::DISCARD_MANY)
3286  result += "\"c_reg\" : " +
3287  std::to_string(measurements_[pos].c_reg_) + ", ";
3288 
3289  result += "\"name\" : ";
3290  result += '\"' + measurements_[pos].name_ + "\"}";
3291 
3292  }
3293  // no-op
3294  else if (elem.type_ == StepType::NOP) {
3295  result += std::string{"\"NOP\""} + "}";
3296  }
3297  // otherwise
3298  else {
3299  }
3300  } // end for
3301  result += "], "; // end steps
3302 
3303  result += "\"step count\" : " + std::to_string(get_step_count()) + ", ";
3304  result +=
3305  "\"total gate count\" : " + std::to_string(get_gate_count()) + ", ";
3306  result +=
3307  "\"total gate depth\" : " + std::to_string(get_gate_depth()) + ", ";
3308  result += "\"total measurement count\" : " +
3309  std::to_string(get_measurement_count()) + ", ";
3310  result += "\"total measurement depth\" : " +
3311  std::to_string(get_measurement_depth()) + ", ";
3312  result += "\"total depth\" : " + std::to_string(get_depth()) + ", ";
3313 
3314  ss.str("");
3315  ss.clear();
3316  ss << disp(get_measured(), ", ");
3317  result += "\"measured/discarded (destructive)\" : " + ss.str() + ", ";
3318 
3319  ss.str("");
3320  ss.clear();
3321  ss << disp(get_non_measured(), ", ");
3322  result += "\"non-measured/non-discarded\" : " + ss.str();
3323 
3324  if (enclosed_in_curly_brackets)
3325  result += "}";
3326 
3327  return result;
3328  } /* to_JSON() */
3329 
3336  friend QCircuit adjoint(QCircuit qc) {
3337  // EXCEPTION CHECKS
3338 
3339  if (qc.get_measured().size() > 0)
3340  throw exception::QuditAlreadyMeasured("qpp::adjoint()");
3341  // END EXCEPTION CHECKS
3342 
3343  return qc.adjoint();
3344  }
3345 
3354  friend QCircuit kron(const QCircuit& qc1, const QCircuit& qc2) {
3355  QCircuit qc{qc1}; // create a copy
3356 
3357  return qc.kron(qc2);
3358  }
3359 
3370  friend QCircuit replicate(QCircuit qc, idx n) {
3371  // EXCEPTION CHECKS
3372 
3373  if (n == 0)
3374  throw exception::OutOfRange("qpp::replicate()");
3375  if (!qc.get_measured().empty())
3376  throw exception::QuditAlreadyMeasured("qpp::replicate()");
3377  if (n == 1)
3378  return qc;
3379  // END EXCEPTION CHECKS
3380 
3381  return qc.replicate(n);
3382  }
3383 
3406  friend QCircuit add_circuit(QCircuit qc1, const QCircuit& qc2,
3407  bigint pos_qudit, idx pos_dit = -1) {
3408  return qc1.add_circuit(qc2, pos_qudit, pos_dit);
3409  }
3410 
3411  private:
3421  std::ostream& display(std::ostream& os) const override {
3422  os << "nq = " << nq_ << ", nc = " << nc_ << ", d = " << d_;
3423 
3424  if (!name_.empty()) // if the circuit is named
3425  os << ", name = \"" << name_ << "\"\n";
3426  else
3427  os << ", name = \"\"\n";
3428 
3429  for (auto&& elem : *this) {
3430  os << elem << '\n';
3431  }
3432 
3433  os << "step count: " << get_step_count() << '\n';
3434  os << "total gate count: " << get_gate_count() << '\n';
3435  os << "total gate depth: " << get_gate_depth() << '\n';
3436  os << "total measurement count: " << get_measurement_count() << '\n';
3437  os << "total measurement depth: " << get_measurement_depth() << '\n';
3438  os << "total depth: " << get_depth() << '\n';
3439  os << "measured/discarded (destructive): " << disp(get_measured(), ", ")
3440  << '\n';
3441  os << "non-measured/non-discarded: " << disp(get_non_measured(), ", ");
3442 
3443  return os;
3444  }
3445 }; /* class QCircuit */
3446 
3447 } /* namespace qpp */
3448 
3449 #endif /* CLASSES_CIRCUITS_CIRCUITS_H_ */
Quantum++ main namespace.
Definition: circuits.h:35