XACC
engines.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_ENGINES_H_
33 #define CLASSES_CIRCUITS_ENGINES_H_
34 
35 namespace qpp {
41 class QEngine : public IDisplay, public IJSON {
42  protected:
43  const QCircuit* qc_;
44 
49  struct state_ {
50  const QCircuit* qc_;
51  ket psi_{};
53  std::vector<double> probs_{};
54  std::vector<idx> dits_{};
55  std::vector<idx> subsys_{};
56 
64  explicit state_(const QCircuit* qc) : qc_{qc} {
65  // EXECEPTION CHECKS
66 
67  if (qc->get_nq() == 0)
68  throw exception::ZeroSize("qpp::QEngine::state_::reset()");
69  // END EXCEPTION CHECKS
70  reset();
71  }
72 
73  // silence -Weffc++ class has pointer data members
77  state_(const state_&) = default;
78 
79  // silence -Weffc++ class has pointer data members
85  state_& operator=(const state_&) = default;
86 
90  void reset() {
91  psi_ = States::get_instance().zero(qc_->get_nq(), qc_->get_d());
92  probs_ = std::vector<double>(qc_->get_nc(), 0);
93  dits_ = std::vector<idx>(qc_->get_nc(), 0);
94  subsys_ = std::vector<idx>(qc_->get_nq(), 0);
95  std::iota(std::begin(subsys_), std::end(subsys_), 0);
96  }
97  } st_;
98  std::map<std::string, idx, internal::EqualSameSizeStringDits>
99  stats_;
100 
106  void set_measured_(idx i) {
107  if (get_measured(i))
108  throw exception::QuditAlreadyMeasured(
109  "qpp::QEngine::set_measured_()");
110  st_.subsys_[i] = static_cast<idx>(-1); // set qudit i to measured state
111  for (idx m = i; m < qc_->get_nq(); ++m) {
112  if (!get_measured(m)) {
113  --st_.subsys_[m];
114  }
115  }
116  }
117 
118  // giving a vector of non-measured qudits, get their relative position wrt
119  // the measured qudits
127  std::vector<idx> get_relative_pos_(std::vector<idx> v) {
128  idx vsize = v.size();
129  for (idx i = 0; i < vsize; ++i) {
130  if (get_measured(v[i]))
131  throw exception::QuditAlreadyMeasured(
132  "qpp::QEngine::get_relative_pos_()");
133  v[i] = st_.subsys_[v[i]];
134  }
135  return v;
136  }
137 
138  public:
150  explicit QEngine(const QCircuit& qc)
151  : qc_{std::addressof(qc)}, st_{qc_}, stats_{} {}
152 
153  // silence -Weffc++ class has pointer data members
157  QEngine(const QEngine&) = default;
158 
159  // silence -Weffc++ class has pointer data members
165  QEngine& operator=(const QEngine&) = default;
166 
170  QEngine(QCircuit&&) = delete;
171 
175  virtual ~QEngine() = default;
176 
177  // getters
183  ket get_psi() const { return st_.psi_; }
184 
190  std::vector<idx> get_dits() const { return st_.dits_; }
191 
198  idx get_dit(idx i) const {
199  if (i >= qc_->get_nc())
200  throw exception::OutOfRange("qpp::QEngine::get_dit()");
201 
202  return st_.dits_[i];
203  }
204 
225  std::vector<double> get_probs() const { return st_.probs_; }
226 
233  bool get_measured(idx i) const {
234  return st_.subsys_[i] == static_cast<idx>(-1);
235  }
236 
242  std::vector<idx> get_measured() const {
243  std::vector<idx> result;
244  for (idx i = 0; i < qc_->get_nq(); ++i)
245  if (get_measured(i))
246  result.emplace_back(i);
247 
248  return result;
249  }
250 
256  std::vector<idx> get_non_measured() const {
257  std::vector<idx> result;
258  for (idx i = 0; i < qc_->get_nq(); ++i)
259  if (!get_measured(i))
260  result.emplace_back(i);
261 
262  return result;
263  }
264 
270  const QCircuit& get_circuit() const& noexcept { return *qc_; }
271 
277  QCircuit get_circuit() const&& noexcept { return *qc_; }
278 
288  std::map<std::string, idx, internal::EqualSameSizeStringDits>
289  get_stats() const {
290  return stats_;
291  }
292  // end getters
293 
294  // setters
302  QEngine& set_dit(idx i, idx value) {
303  if (i >= qc_->get_nc())
304  throw exception::OutOfRange("qpp::QEngine::set_dit()");
305  st_.dits_[i] = value;
306 
307  return *this;
308  }
309 
319  QEngine& set_psi(const ket& psi) {
320  // EXCEPTION CHECKS
321 
322  idx n = get_non_measured().size();
323  idx D = static_cast<idx>(std::llround(std::pow(qc_->get_d(), n)));
324  if (static_cast<idx>(psi.rows()) != D)
325  throw exception::DimsNotEqual("qpp::QEngine::set_psi()");
326  // END EXCEPTION CHECKS
327 
328  st_.psi_ = psi;
329 
330  return *this;
331  }
332  // end setters
333 
339  QEngine& reset_stats() {
340  stats_ = {};
341 
342  return *this;
343  }
344 
353  QEngine& reset() {
354  st_.reset();
355 
356  return *this;
357  }
358 
368  virtual QEngine& execute(const QCircuit::iterator::value_type& elem) {
369  // EXCEPTION CHECKS
370 
371  // iterator must point to the same quantum circuit description
372  if (elem.value_type_qc_ != qc_)
373  throw exception::InvalidIterator("qpp::QEngine::execute()");
374  // the rest of exceptions are caught by the iterator::operator*()
375  // END EXCEPTION CHECKS
376 
377  auto h_tbl = qc_->get_cmat_hash_tbl_();
378  idx d = qc_->get_d();
379 
380  // gate step
381  if (elem.type_ == QCircuit::StepType::GATE) {
382  auto gates = qc_->get_gates_();
383 
384  idx q_ip =
385  std::distance(std::begin(qc_->get_gates_()), elem.gates_ip_);
386 
387  std::vector<idx> ctrl_rel_pos;
388  std::vector<idx> target_rel_pos =
389  get_relative_pos_(gates[q_ip].target_);
390 
391  switch (gates[q_ip].gate_type_) {
392  case QCircuit::GateType::NONE:
393  break;
394  case QCircuit::GateType::SINGLE:
395  case QCircuit::GateType::TWO:
396  case QCircuit::GateType::THREE:
397  case QCircuit::GateType::CUSTOM:
398  st_.psi_ = apply(st_.psi_, h_tbl[gates[q_ip].gate_hash_],
399  target_rel_pos, d);
400  break;
401  case QCircuit::GateType::FAN:
402  for (idx m = 0; m < gates[q_ip].target_.size(); ++m)
403  st_.psi_ =
404  apply(st_.psi_, h_tbl[gates[q_ip].gate_hash_],
405  {target_rel_pos[m]}, d);
406  break;
407  case QCircuit::GateType::SINGLE_CTRL_SINGLE_TARGET:
408  case QCircuit::GateType::SINGLE_CTRL_MULTIPLE_TARGET:
409  case QCircuit::GateType::MULTIPLE_CTRL_SINGLE_TARGET:
410  case QCircuit::GateType::MULTIPLE_CTRL_MULTIPLE_TARGET:
411  case QCircuit::GateType::CUSTOM_CTRL:
412  ctrl_rel_pos = get_relative_pos_(gates[q_ip].ctrl_);
413  st_.psi_ = applyCTRL(
414  st_.psi_, h_tbl[gates[q_ip].gate_hash_], ctrl_rel_pos,
415  target_rel_pos, d, gates[q_ip].shift_);
416  break;
417  case QCircuit::GateType::SINGLE_cCTRL_SINGLE_TARGET:
418  case QCircuit::GateType::SINGLE_cCTRL_MULTIPLE_TARGET:
419  case QCircuit::GateType::MULTIPLE_cCTRL_SINGLE_TARGET:
420  case QCircuit::GateType::MULTIPLE_cCTRL_MULTIPLE_TARGET:
421  case QCircuit::GateType::CUSTOM_cCTRL:
422  if (st_.dits_.empty()) {
423  st_.psi_ =
424  apply(st_.psi_, h_tbl[gates[q_ip].gate_hash_],
425  target_rel_pos, d);
426  } else {
427  bool should_apply = true;
428  idx first_dit;
429  // we have a shift
430  if (!gates[q_ip].shift_.empty()) {
431  first_dit = (st_.dits_[(gates[q_ip].ctrl_)[0]] +
432  gates[q_ip].shift_[0]) %
433  d;
434  for (idx m = 1; m < gates[q_ip].ctrl_.size(); ++m) {
435  if ((st_.dits_[(gates[q_ip].ctrl_)[m]] +
436  gates[q_ip].shift_[m]) %
437  d !=
438  first_dit) {
439  should_apply = false;
440  break;
441  }
442  }
443  }
444  // no shift
445  else {
446  first_dit = st_.dits_[(gates[q_ip].ctrl_)[0]];
447  for (idx m = 1; m < gates[q_ip].ctrl_.size(); ++m) {
448  if (st_.dits_[(gates[q_ip].ctrl_)[m]] !=
449  first_dit) {
450  should_apply = false;
451  break;
452  }
453  }
454  }
455  if (should_apply) {
456  st_.psi_ = apply(
457  st_.psi_,
458  powm(h_tbl[gates[q_ip].gate_hash_], first_dit),
459  target_rel_pos, d);
460  }
461  }
462  break;
463  } // end switch on gate type
464  } // end if gate step
465 
466  // measurement step
467  else if (elem.type_ == QCircuit::StepType::MEASUREMENT) {
468  auto measurements = qc_->get_measurements_();
469  idx m_ip = std::distance(std::begin(qc_->get_measurements_()),
470  elem.measurements_ip_);
471 
472  std::vector<idx> target_rel_pos =
473  get_relative_pos_(measurements[m_ip].target_);
474 
475  std::vector<idx> resZ;
476  double probZ;
477 
478  idx mres = 0;
479  std::vector<double> probs;
480  std::vector<cmat> states;
481 
482  switch (measurements[m_ip].measurement_type_) {
483  case QCircuit::MeasureType::NONE:
484  break;
485  case QCircuit::MeasureType::MEASURE_Z:
486  std::tie(resZ, probZ, st_.psi_) =
487  measure_seq(st_.psi_, target_rel_pos, d);
488  st_.dits_[measurements[m_ip].c_reg_] = resZ[0];
489  st_.probs_[measurements[m_ip].c_reg_] = probZ;
490  set_measured_(measurements[m_ip].target_[0]);
491  break;
492  case QCircuit::MeasureType::MEASURE_Z_MANY:
493  std::tie(resZ, probZ, st_.psi_) =
494  measure_seq(st_.psi_, target_rel_pos, d);
495  st_.dits_[measurements[m_ip].c_reg_] = multiidx2n(
496  resZ, std::vector<idx>(target_rel_pos.size(), d));
497  st_.probs_[measurements[m_ip].c_reg_] = probZ;
498  for (auto&& elem : measurements[m_ip].target_)
499  set_measured_(elem);
500  break;
501  case QCircuit::MeasureType::MEASURE_V:
502  std::tie(mres, probs, states) = measure(
503  st_.psi_, h_tbl[measurements[m_ip].mats_hash_[0]],
504  target_rel_pos, d);
505  st_.psi_ = states[mres];
506  st_.dits_[measurements[m_ip].c_reg_] = mres;
507  st_.probs_[measurements[m_ip].c_reg_] = probs[mres];
508  set_measured_(measurements[m_ip].target_[0]);
509  break;
510  case QCircuit::MeasureType::MEASURE_V_MANY:
511  std::tie(mres, probs, states) = measure(
512  st_.psi_, h_tbl[measurements[m_ip].mats_hash_[0]],
513  target_rel_pos, d);
514  st_.psi_ = states[mres];
515  st_.dits_[measurements[m_ip].c_reg_] = mres;
516  st_.probs_[measurements[m_ip].c_reg_] = probs[mres];
517  for (auto&& elem : measurements[m_ip].target_)
518  set_measured_(elem);
519  break;
520  case QCircuit::MeasureType::MEASURE_Z_ND:
521  std::tie(resZ, probZ, st_.psi_) =
522  measure_seq(st_.psi_, target_rel_pos, d, false);
523  st_.dits_[measurements[m_ip].c_reg_] = resZ[0];
524  st_.probs_[measurements[m_ip].c_reg_] = probZ;
525  break;
526  case QCircuit::MeasureType::MEASURE_Z_MANY_ND:
527  std::tie(resZ, probZ, st_.psi_) =
528  measure_seq(st_.psi_, target_rel_pos, d, false);
529  st_.dits_[measurements[m_ip].c_reg_] = multiidx2n(
530  resZ, std::vector<idx>(target_rel_pos.size(), d));
531  st_.probs_[measurements[m_ip].c_reg_] = probZ;
532  break;
533  case QCircuit::MeasureType::MEASURE_V_ND:
534  case QCircuit::MeasureType::MEASURE_V_MANY_ND:
535  std::tie(mres, probs, states) = measure(
536  st_.psi_, h_tbl[measurements[m_ip].mats_hash_[0]],
537  target_rel_pos, d, false);
538  st_.psi_ = states[mres];
539  st_.dits_[measurements[m_ip].c_reg_] = mres;
540  st_.probs_[measurements[m_ip].c_reg_] = probs[mres];
541  break;
542  case QCircuit::MeasureType::RESET:
543  case QCircuit::MeasureType::RESET_MANY:
544  st_.psi_ = qpp::reset(st_.psi_, target_rel_pos, d);
545  break;
546  case QCircuit::MeasureType::DISCARD:
547  std::tie(std::ignore, std::ignore, st_.psi_) =
548  measure_seq(st_.psi_, target_rel_pos, d);
549  set_measured_(measurements[m_ip].target_[0]);
550  break;
551  case QCircuit::MeasureType::DISCARD_MANY:
552  std::tie(std::ignore, std::ignore, st_.psi_) =
553  measure_seq(st_.psi_, target_rel_pos, d);
554  for (auto&& elem : measurements[m_ip].target_)
555  set_measured_(elem);
556  break;
557  } // end switch on measurement type
558  } // end else if measurement step
559 
560  // no-op
561  else if (elem.type_ == QCircuit::StepType::NOP) {
562  }
563 
564  // otherwise
565  else {
566  }
567 
568  return *this;
569  }
570 
579  QEngine& execute(const QCircuit::iterator& it) { return execute(*it); }
580 
591  QEngine& execute(idx reps = 1, bool clear_stats = true) {
592  auto initial_engine_state = st_; // saves the engine entry state
593 
594  if (clear_stats)
595  reset_stats();
596 
597  for (idx i = 0; i < reps; ++i) {
598  // sets the state of the engine to the entry state
599  st_ = initial_engine_state;
600 
601  for (auto&& elem : *qc_)
602  (void) execute(elem);
603 
604  // we measured at least one qudit
605  if (qc_->get_measurement_count() > 0) {
606  std::vector<idx> m_res = get_dits();
607 
608  std::stringstream ss;
609  ss << disp(m_res, " ", "", "");
610  ++stats_[ss.str()];
611  }
612  }
613 
614  return *this;
615  }
616 
627  std::string to_JSON(bool enclosed_in_curly_brackets = true) const override {
628  std::string result;
629 
630  if (enclosed_in_curly_brackets)
631  result += "{";
632 
633  std::ostringstream ss;
634  ss << disp(get_measured(), ", ");
635  result += "\"measured/discarded (destructive)\" : " + ss.str() + ", ";
636 
637  ss.str("");
638  ss.clear();
639  ss << disp(get_non_measured(), ", ");
640  result += "\"non-measured/non-discarded\" : " + ss.str();
641 
642  ss.str("");
643  ss.clear();
644  result += ", \"last probs\": ";
645  ss << disp(get_probs(), ", ");
646  result += ss.str();
647 
648  ss.str("");
649  ss.clear();
650  result += ", \"last dits\": ";
651  ss << disp(get_dits(), ", ");
652  result += ss.str();
653 
654  ss.str("");
655  ss.clear();
656 
657  // compute the statistics
658  if (!stats_.empty()) {
659  result += ", \"stats\": {";
660  idx reps = 0;
661  for (auto&& elem : get_stats())
662  reps += elem.second;
663  result += "\"reps\": " + std::to_string(reps) + ", ";
664  result += "\"outcomes\": " + std::to_string(stats_.size()) + ", ";
665 
666  std::vector<idx> dits_dims(qc_->get_nc(), qc_->get_d());
667  bool is_first = true;
668  for (auto&& elem : get_stats()) {
669  if (is_first)
670  is_first = false;
671  else
672  ss << ", ";
673  ss << "\""
674  << "[" << elem.first << "]"
675  << "\" : " << elem.second;
676  }
677  ss << '}';
678  result += ss.str();
679  }
680 
681  if (enclosed_in_curly_brackets)
682  result += "}";
683 
684  return result;
685  }
686 
687  private:
697  std::ostream& display(std::ostream& os) const override {
698  os << "measured/discarded (destructive): " << disp(get_measured(), ", ")
699  << '\n';
700  os << "non-measured/non-discarded: " << disp(get_non_measured(), ", ")
701  << '\n';
702  os << "last probs: " << disp(get_probs(), ", ") << '\n';
703  os << "last dits: " << disp(get_dits(), ", ");
704 
705  // compute the statistics
706  if (!stats_.empty()) {
707  idx reps = 0;
708  for (auto&& elem : get_stats())
709  reps += elem.second;
710  os << "\nstats:\n";
711  os << '\t' << "reps: " << reps << '\n';
712  os << '\t' << "outcomes: " << stats_.size() << '\n';
713  std::vector<idx> dits_dims(qc_->get_nc(), qc_->get_d());
714  bool is_first = true;
715  for (auto&& elem : get_stats()) {
716  if (is_first)
717  is_first = false;
718  else
719  os << '\n';
720  os << '\t' << "[" << elem.first << "]"
721  << ": " << elem.second;
722  }
723  }
724 
725  return os;
726  }
727 }; /* class QEngine */
728 
740 template <typename NoiseModel>
741 class QNoisyEngine : public QEngine {
742  NoiseModel noise_;
743  std::vector<std::vector<idx>> noise_results_;
744  public:
752  explicit QNoisyEngine(const QCircuit& qc, const NoiseModel& noise)
753  : QEngine{qc}, noise_{noise}, noise_results_(qc.get_step_count()) {
754  // EXCEPTION CHECKS
755 
756  // check noise has the correct dimensionality
757  if (qc.get_d() != noise.get_d())
758  throw exception::DimsNotEqual("qpp::QNoisyEngine::QNoisyEngine()");
759  // END EXCEPTION CHECKS
760  }
761 
762  using QEngine::execute;
763 
769  QNoisyEngine& execute(const QCircuit::iterator::value_type& elem) override {
770  // get the relative position of the target
771  std::vector<idx> target_rel_pos = get_relative_pos_(get_non_measured());
772  if (elem.type_ != QCircuit::StepType::MEASUREMENT) {
773  // apply the noise
774  for (auto&& i : target_rel_pos) {
775  st_.psi_ = noise_(st_.psi_, i);
776  // record the Kraus operator that occurred
777  noise_results_[elem.ip_].emplace_back(noise_.get_last_idx());
778  }
779  }
780  // execute the circuit step
781  (void) QEngine::execute(elem);
782 
783  return *this;
784  }
785 
786  // getters
799  std::vector<std::vector<idx>> get_noise_results() const {
800  return noise_results_;
801  }
802  // end getters
803 }; /* class QNoisyEngine */
804 
805 } /* namespace qpp */
806 
807 #endif /* CLASSES_CIRCUITS_ENGINES_H_ */
Quantum++ main namespace.
Definition: circuits.h:35