XACC
ast.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 QASM_AST_H_
33 #define QASM_AST_H_
34 
35 namespace qpp {
36 namespace qasm {
37 
38 using ident = std::string;
39 
43 enum class BinaryOp { Plus, Minus, Times, Divide, Pow };
44 
48 enum class UnaryOp { Neg, Sin, Cos, Tan, Ln, Sqrt, Exp };
49 
50 static const Gates& gt = Gates::get_instance();
51 static std::unordered_map<ident,
52  std::function<cmat(const std::vector<double>&)>>
53  known_matrices{
55  {"cx", [](const std::vector<double>&) { return gt.CNOT; }},
56  {"id", [](const std::vector<double>&) { return gt.Id2; }},
57  {"x", [](const std::vector<double>&) { return gt.X; }},
58  {"y", [](const std::vector<double>&) { return gt.Y; }},
59  {"z", [](const std::vector<double>&) { return gt.Z; }},
60  {"h", [](const std::vector<double>&) { return gt.H; }},
61  {"s", [](const std::vector<double>&) { return gt.S; }},
62  {"sdg", [](const std::vector<double>&) { return gt.S.adjoint(); }},
63  {"t", [](const std::vector<double>&) { return gt.T; }},
64  {"tdg", [](const std::vector<double>&) { return gt.T.adjoint(); }},
65  {"rx",
66  [](const std::vector<double>& args) {
67  assert(args.size() > 0);
68  return gt.RX(args[0]);
69  }},
70  {"rz",
71  [](const std::vector<double>& args) {
72  assert(args.size() > 0);
73  return gt.RZ(args[0]);
74  }},
75  {"ry",
76  [](const std::vector<double>& args) {
77  assert(args.size() > 0);
78  return gt.RY(args[0]);
79  }},
80  {"cz", [](const std::vector<double>&) { return gt.CZ; }},
81  {"cy",
82  [](const std::vector<double>&) {
83  cmat mat{cmat::Identity(4, 4)};
84  mat.block(2, 2, 2, 2) = gt.Y;
85  return mat;
86  }},
87  {"swap", [](const std::vector<double>&) { return gt.SWAP; }},
88  {"ch",
89  [](const std::vector<double>&) {
90  cmat mat{cmat::Identity(4, 4)};
91  mat.block(2, 2, 2, 2) = gt.H;
92  return mat;
93  }},
94  {"ccx", [](const std::vector<double>&) { return gt.TOF; }},
95  {"crz", [](const std::vector<double>& args) {
96  assert(args.size() > 0);
97  cmat mat{cmat::Identity(4, 4)};
98  mat.block(2, 2, 2, 2) = gt.RZ(args[0]);
99  return mat;
100  }}};
101 
109 class Value {
110  public:
111  virtual ~Value() = default;
112 };
113 
123 class Context {
124  QCircuit* circuit_;
125 
126  // Hack for MSCV
127  using hash_ident_uptr = std::unordered_map<ident, std::unique_ptr<Value>>;
128  struct Environment {
129  Environment() noexcept : val_(){};
130  Environment(Environment&& rhs) noexcept : val_(std::move(rhs.val_)) {}
131  hash_ident_uptr val_;
132  };
133 
134  std::vector<Environment> env_{};
135  std::list<idx> qubit_pool_{};
136  idx max_bit_ = -1;
137  idx max_qubit_ = -1;
138 
139  // For controlled contexts
140  std::vector<idx> cctrls_{};
141  std::vector<idx> cctrl_shift_{};
142 
143  public:
149  Context(QCircuit* qc) : circuit_(qc) {}
150 
154  Context(const Context&) = delete;
155 
159  Context& operator=(const Context&) = delete;
160 
166  QCircuit* get_circuit() { return circuit_; }
167 
168  /*-------------- (Qu)bit management & allocation ---------------*/
169 
175  idx alloc_bit() {
176  idx ret = ++max_bit_;
177 
178  // check if circuit has enough classical bits
179  if (max_bit_ >= circuit_->get_nc()) {
180  circuit_->add_dit(1, ret);
181  }
182 
183  return ret;
184  }
185 
191  idx alloc_qubit() {
192  idx ret = ++max_qubit_;
193 
194  // check if circuit has enough classical bits
195  if (max_qubit_ >= circuit_->get_nq()) {
196  circuit_->add_qudit(1, ret);
197  }
198 
199  return ret;
200  }
201 
202  /*------------------ Environment management --------------*/
206  void enter_scope() { env_.push_back({}); }
207 
211  void exit_scope() { env_.pop_back(); }
212 
223  Value* lookup(const ident& id, const Location& loc) {
224  for (auto table = env_.rbegin(); table != env_.rend(); table++) {
225  auto it = table->val_.find(id);
226  if (it != table->val_.end())
227  return it->second.get();
228  }
229 
230  std::cerr << loc << ": Undeclared identifier " << id << "\n";
231  throw exception::Undeclared("qpp::qasm::Context::lookup()");
232  }
233 
241  void set(const ident& id, std::unique_ptr<Value> val) {
242  if (env_.empty())
243  env_.push_back({});
244  env_.back().val_[id] = std::move(val);
245  }
246 
247  /*------------------- Classical controls ----------------*/
255  void set_ccontrols(const std::vector<idx>& cctrls,
256  const std::vector<idx>& shift) {
257  cctrls_ = cctrls;
258  cctrl_shift_ = shift;
259  }
260 
264  void clear_ccontrols() {
265  cctrls_.clear();
266  cctrl_shift_.clear();
267  }
268 
274  bool ccontrolled() { return !cctrls_.empty(); }
275 
281  const std::vector<idx>& get_cctrls() { return cctrls_; }
282 
288  const std::vector<idx>& get_shift() { return cctrl_shift_; }
289 };
290 
291 /*------------------- Virtual base classes -------------------*/
292 
297 class Statement : public IDisplay {
298  protected:
299  Location loc_;
300 
301  public:
307  Statement(Location loc) : loc_(loc) {}
308 
312  Statement(const Statement&) = delete;
313 
317  virtual ~Statement() = default;
318 
327  virtual void evaluate(Context& ctx) const = 0;
328 
335  void pretty_print(std::ostream& os, const std::string& prefix) const {
336  os << "(" << loc_ << "):" << prefix << *this;
337  }
338 };
339 using StatementPtr = std::unique_ptr<Statement>;
340 
345 class Gate : public Statement {
346  public:
352  Gate(Location loc) : Statement(loc) {}
353 
357  Gate(const Gate&) = delete;
358 
362  virtual ~Gate() = default;
363 };
364 using GatePtr = std::unique_ptr<Gate>;
365 
371 class Decl : public Statement {
372  protected:
373  ident id_;
374 
375  public:
382  Decl(Location loc, ident id) : Statement(loc), id_(id) {}
383 
387  Decl(const Decl&) = delete;
388 
392  virtual ~Decl() = default;
393 };
394 
399 class Expr : public IDisplay {
400  protected:
401  Location loc_;
402 
403  public:
409  Expr(Location loc) : loc_(loc) {}
410 
414  Expr(const Expr&) = delete;
415 
419  virtual ~Expr() = default;
420 
429  virtual double evaluate(Context& ctx) const = 0;
430 };
431 using ExprPtr = std::unique_ptr<Expr>;
432 
433 /*-------------------- Non-virtual leaf classes ------------------*/
438 class QASM : public IDisplay {
439  int bits_ = -1;
440  int qubits_ = -1;
441  std::vector<StatementPtr> body_;
442 
443  public:
452  QASM(int bits, int qubits, std::vector<StatementPtr>&& body)
453  : bits_(bits), qubits_(qubits), body_(std::move(body)) {}
454 
460  std::unique_ptr<QCircuit> to_QCircuit() {
461  auto ret = std::unique_ptr<QCircuit>(new QCircuit(qubits_, bits_));
462  Context ctx(ret.get());
463  for (auto it = body_.begin(); it != body_.end(); it++) {
464  (*it)->evaluate(ctx);
465  }
466 
467  return ret;
468  }
469 
473  std::ostream& display(std::ostream& os) const {
474  os << "OPENQASM 2.0;\ninclude \"qelib1.inc\";\n";
475  for (auto it = body_.begin(); it != body_.end(); it++) {
476  os << **it;
477  }
478 
479  return os;
480  }
481 
488  void pretty_print(std::ostream& os) const {
489  os << "(Included header): OPENQASM 2.0;\n(Included header): include "
490  "\"qelib1.inc\";\n";
491  for (auto it = body_.begin(); it != body_.end(); it++) {
492  (*it)->pretty_print(os, "\t");
493  }
494  }
495 };
496 
502 class Circuit : public Value {
503  public:
504  const std::vector<ident>& c_params_;
505  const std::vector<ident>& q_params_;
506  const std::vector<GatePtr>& body_;
507 
508  Circuit(const std::vector<ident>& c_params,
509  const std::vector<ident>& q_params,
510  const std::vector<GatePtr>& body)
511  : c_params_(c_params), q_params_(q_params), body_(body) {}
512  ~Circuit() = default;
513 };
514 
520 class Register : public Value {
521  public:
522  bool quantum_;
523  std::vector<idx> indices_;
524 
525  Register(bool quantum, std::vector<idx> indices)
526  : quantum_(quantum), indices_(indices) {}
527  ~Register() = default;
528 };
529 
535 class Qubit : public Value {
536  public:
537  idx index_;
538 
539  Qubit(idx index) : index_(index) {}
540  ~Qubit() = default;
541 };
542 
548 class Number : public Value {
549  public:
550  double value_;
551 
552  Number(double value) : value_(value) {}
553  ~Number() = default;
554 };
555 
560 class Varinfo : public IDisplay {
561  Location loc_;
562  ident id_;
563  int offset_;
564 
565  public:
573  Varinfo(Location loc, ident id, int offset = -1)
574  : loc_(loc), id_(id), offset_(offset) {}
575 
583  std::vector<idx> as_creg(Context& ctx) const {
584  auto reg = dynamic_cast<const Register*>(ctx.lookup(id_, loc_));
585 
586  if (reg == nullptr || reg->quantum_) {
587  std::cerr << loc_ << ": Identifier " << id_
588  << " does not refer to a bit register\n";
589  throw exception::SemanticError("qpp::qasm::Varinfo::as_creg()");
590  }
591 
592  if (offset_ == -1) {
593  // creg
594  return std::vector<idx>{reg->indices_};
595  } else {
596  // creg deref
597 
598  // check register size
599  if ((idx) offset_ >= reg->indices_.size()) {
600  std::cerr << loc_ << ": Index out of bounds\n";
601  throw exception::SemanticError("qpp::qasm::Varinfo::as_qreg()");
602  }
603 
604  return std::vector<idx>{reg->indices_[offset_]};
605  }
606  }
607 
616  std::vector<idx> as_qreg(Context& ctx) const {
617  if (offset_ == -1) {
618  // qubit or qreg
619  auto tmp = ctx.lookup(id_, loc_);
620 
621  if (auto qubit = dynamic_cast<const Qubit*>(tmp)) {
622  return std::vector<idx>{qubit->index_};
623  } else {
624  auto reg = dynamic_cast<const Register*>(tmp);
625  // check register type
626  if (reg == nullptr || !reg->quantum_) {
627  std::cerr << loc_ << ": Identifier " << id_
628  << " does not refer to a qubit register\n";
629  throw exception::SemanticError(
630  "qpp::qasm::Varinfo::as_qreg()");
631  }
632 
633  return std::vector<idx>{reg->indices_};
634  }
635  } else {
636  // qreg deref
637  auto reg = dynamic_cast<const Register*>(ctx.lookup(id_, loc_));
638 
639  // check register type
640  if (reg == nullptr || !reg->quantum_) {
641  std::cerr << loc_ << ": Identifier " << id_
642  << " does not refer to a qubit register\n";
643  throw exception::SemanticError("qpp::qasm::Varinfo::as_qreg()");
644  }
645 
646  // check register size
647  if ((idx) offset_ >= reg->indices_.size()) {
648  std::cerr << loc_ << ": Index out of bounds\n";
649  throw exception::SemanticError("qpp::qasm::Varinfo::as_qreg()");
650  }
651 
652  return std::vector<idx>{reg->indices_[offset_]};
653  }
654  }
655 
659  std::ostream& display(std::ostream& os) const {
660  os << id_;
661  if (offset_ != -1)
662  os << "[" << offset_ << "]";
663 
664  return os;
665  }
666 };
667 
673 class GateDecl final : public Decl {
674  bool opaque_;
675  std::vector<ident> c_params_;
676  std::vector<ident> q_params_;
677  std::vector<GatePtr> body_;
678 
679  public:
689  GateDecl(Location loc, ident id, bool opaque, std::vector<ident> c_params,
690  std::vector<ident> q_params, std::vector<GatePtr> body)
691  : Decl(loc, id), opaque_(opaque), c_params_(c_params),
692  q_params_(q_params), body_(std::move(body)) {}
693 
697  void evaluate(Context& ctx) const {
698  ctx.set(id_, std::unique_ptr<Value>(
699  new Circuit(c_params_, q_params_, body_)));
700  }
701 
705  std::ostream& display(std::ostream& os) const {
706  os << (opaque_ ? "opaque " : "gate ") << id_;
707  if (c_params_.size() > 0) {
708  os << "(";
709  for (auto it = c_params_.begin(); it != c_params_.end(); it++) {
710  os << (it == c_params_.begin() ? "" : ",") << *it;
711  }
712  os << ")";
713  }
714  os << " ";
715  for (auto it = q_params_.begin(); it != q_params_.end(); it++) {
716  os << (it == q_params_.begin() ? "" : ",") << *it;
717  }
718  if (opaque_) {
719  os << ";\n";
720  } else {
721  os << "{\n";
722  for (auto it = body_.begin(); it != body_.end(); it++) {
723  os << "\t" << **it;
724  }
725  os << "}\n";
726  }
727  return os;
728  }
729 
734  void pretty_print(std::ostream& os, const std::string& prefix) const {
735  os << "(" << loc_ << "):" << prefix;
736  os << (opaque_ ? "opaque " : "gate ") << id_;
737  if (c_params_.size() > 0) {
738  os << "(";
739  for (auto it = c_params_.begin(); it != c_params_.end(); it++) {
740  os << (it == c_params_.begin() ? "" : ",") << *it;
741  }
742  os << ")";
743  }
744  os << " ";
745  for (auto it = q_params_.begin(); it != q_params_.end(); it++) {
746  os << (it == q_params_.begin() ? "" : ",") << *it;
747  }
748  if (opaque_) {
749  os << ";\n";
750  } else {
751  auto nprefix = prefix + "\t";
752  os << "{\n";
753  for (auto it = body_.begin(); it != body_.end(); it++) {
754  (*it)->pretty_print(os, nprefix);
755  }
756  os << "(" << loc_ << "):" << prefix << "}\n";
757  }
758  }
759 };
760 
766 class RegisterDecl final : public Decl {
767  bool quantum_;
768  idx length_;
769 
770  public:
779  RegisterDecl(Location loc, ident id, bool quantum, idx length)
780  : Decl(loc, id), quantum_(quantum), length_(length) {}
781 
785  void evaluate(Context& ctx) const {
786  std::vector<idx> indices(length_);
787  for (idx i = 0; i < length_; i++) {
788  indices[i] = quantum_ ? ctx.alloc_qubit() : ctx.alloc_bit();
789  }
790 
791  ctx.set(id_, std::unique_ptr<Value>(new Register(quantum_, indices)));
792  }
793 
797  std::ostream& display(std::ostream& os) const {
798  os << (quantum_ ? "qreg " : "creg ") << id_ << "[" << length_ << "];\n";
799  return os;
800  }
801 };
802 
808 class MeasureStatement final : public Statement {
809  Varinfo q_arg_;
810  Varinfo c_arg_;
811 
812  public:
820  MeasureStatement(Location loc, Varinfo q_arg, Varinfo c_arg)
821  : Statement(loc), q_arg_(q_arg), c_arg_(c_arg) {}
822 
826  void evaluate(Context& ctx) const {
827  auto q_args = q_arg_.as_qreg(ctx);
828  auto c_args = c_arg_.as_creg(ctx);
829  auto circuit = ctx.get_circuit();
830 
831  // check register lengths
832  if (q_args.size() != c_args.size()) {
833  std::cerr << "(" << loc_ << "): Registers have different lengths\n";
834  throw exception::ParseError(
835  "qpp::qasm::MeasureStatement::evaluate()");
836  }
837 
838  // apply measurements non-desctructively
839  for (idx i = 0; i < q_args.size(); i++) {
840  circuit->measureZ(q_args[i], c_args[i], false);
841  }
842  }
843 
847  std::ostream& display(std::ostream& os) const {
848  os << "measure " << q_arg_ << " -> " << c_arg_ << ";\n";
849  return os;
850  }
851 };
852 
858 class ResetStatement final : public Statement {
859  Varinfo arg_;
860 
861  public:
868  ResetStatement(Location loc, Varinfo arg) : Statement(loc), arg_(arg) {}
869 
873  void evaluate(Context& ctx) const {
874  auto q_args = arg_.as_qreg(ctx);
875  auto circuit = ctx.get_circuit();
876 
877  circuit->reset(q_args);
878  }
879 
883  std::ostream& display(std::ostream& os) const {
884  os << "reset " << arg_ << ";\n";
885  return os;
886  }
887 };
888 
894 class IfStatement final : public Statement {
895  ident id_;
896  int value_;
897  StatementPtr then_;
898 
899  public:
908  IfStatement(Location loc, ident id, int value, StatementPtr then)
909  : Statement(loc), id_(id), value_(value), then_(std::move(then)) {}
910 
914  void evaluate(Context& ctx) const {
915  auto creg = dynamic_cast<const Register*>(ctx.lookup(id_, loc_));
916 
917  // check register type
918  if (creg == nullptr || creg->quantum_) {
919  std::cerr << loc_ << ": Identifier " << id_
920  << " does not refer to a classic register\n";
921  throw exception::SemanticError(
922  "qpp::qasm::IfStatement::evaluate()");
923  }
924 
925  // create the shift
926  int tmp = value_;
927  std::vector<idx> shift(creg->indices_.size(), 0);
928  for (idx i = 0; i < creg->indices_.size(); i++) {
929  if (tmp % 2 == 0)
930  shift[i] = 1;
931  tmp >>= 1;
932  }
933 
934  // apply controls to then branch
935  ctx.set_ccontrols(creg->indices_, shift);
936  then_->evaluate(ctx);
937  ctx.clear_ccontrols();
938  }
939 
943  std::ostream& display(std::ostream& os) const {
944  os << "if (" << id_ << "==" << value_ << ") " << *then_;
945  return os;
946  }
947 };
948 
954 class UGate final : public Gate {
955  ExprPtr theta_;
956  ExprPtr phi_;
957  ExprPtr lambda_;
958 
959  Varinfo arg_;
960  public:
970  UGate(Location loc, ExprPtr theta, ExprPtr phi, ExprPtr lambda, Varinfo arg)
971  : Gate(loc), theta_(std::move(theta)), phi_(std::move(phi)),
972  lambda_(std::move(lambda)), arg_(arg) {}
973 
977  void evaluate(Context& ctx) const {
978  auto theta = theta_->evaluate(ctx);
979  auto phi = phi_->evaluate(ctx);
980  auto lambda = lambda_->evaluate(ctx);
981  auto args = arg_.as_qreg(ctx);
982  auto circuit = ctx.get_circuit();
983 
984  // generate the matrix
985  cmat u{cmat::Zero(2, 2)};
986 
987  // standard QASM spec, as defined in
988  // https://arxiv.org/pdf/1707.03429.pdf
989  // u << cos(theta / 2) * std::exp(-1_i * (phi + lambda) / 2.0),
990  // -(sin(theta / 2)) * std::exp(-1_i * (phi - lambda) / 2.0),
991  // sin(theta / 2) * std::exp(1_i * (phi - lambda) / 2.0),
992  // cos(theta / 2) * std::exp(1_i * (phi + lambda) / 2.0);
993 
994  // Qiskit spec, as defined in
995  // https://github.com/Qiskit/qiskit-terra/tree/master/qiskit/extensions/standard
996  // We use these definitions, see
997  // https://github.com/vsoftco/qpp/issues/65 for the reasons why.
998  u << cos(theta / 2), -(sin(theta / 2)) * std::exp(1_i * lambda),
999  sin(theta / 2) * std::exp(1_i * phi),
1000  cos(theta / 2) * std::exp(1_i * (phi + lambda));
1001 
1002  // apply the gate
1003  for (auto i : args) {
1004  if (ctx.ccontrolled()) {
1005  circuit->cCTRL(u, ctx.get_cctrls(), i, ctx.get_shift(),
1006  "Controlled-U");
1007  } else {
1008  circuit->gate(u, i, "U");
1009  }
1010  }
1011  }
1012 
1016  std::ostream& display(std::ostream& os) const {
1017  os << "U(" << *theta_ << "," << *phi_ << "," << *lambda_ << ") " << arg_
1018  << ";\n";
1019  return os;
1020  }
1021 };
1022 
1028 class CNOTGate final : public Gate {
1029  Varinfo ctrl_;
1030  Varinfo tgt_;
1031 
1032  public:
1040  CNOTGate(Location loc, Varinfo ctrl, Varinfo tgt)
1041  : Gate(loc), ctrl_(ctrl), tgt_(tgt) {}
1042 
1046  void evaluate(Context& ctx) const {
1047  auto ctrls = ctrl_.as_qreg(ctx);
1048  auto tgts = tgt_.as_qreg(ctx);
1049  auto circuit = ctx.get_circuit();
1050 
1051  // different combinations of controls and targets
1052  if (ctrls.size() == 1 && tgts.size() == 1) {
1053  if (ctx.ccontrolled()) {
1054  std::vector<idx> tmp{ctrls[0], tgts[0]};
1055  circuit->cCTRL_custom(gt.CNOT, ctx.get_cctrls(), tmp,
1056  ctx.get_shift(), "CX");
1057  } else {
1058  circuit->gate(gt.CNOT, ctrls[0], tgts[0], "CX");
1059  }
1060  } else if (ctrls.size() > 1 && tgts.size() == 1) {
1061  for (idx i = 0; i < ctrls.size(); i++) {
1062  if (ctx.ccontrolled()) {
1063  std::vector<idx> tmp{ctrls[i], tgts[0]};
1064  circuit->cCTRL_custom(gt.CNOT, ctx.get_cctrls(), tmp,
1065  ctx.get_shift(), "CX");
1066  } else {
1067  circuit->gate(gt.CNOT, ctrls[i], tgts[0], "CX");
1068  }
1069  }
1070  } else if (ctrls.size() == 1 && tgts.size() > 1) {
1071  for (idx i = 0; i < tgts.size(); i++) {
1072  if (ctx.ccontrolled()) {
1073  std::vector<idx> tmp{ctrls[0], tgts[i]};
1074  circuit->cCTRL_custom(gt.CNOT, ctx.get_cctrls(), tmp,
1075  ctx.get_shift(), "CX");
1076  } else {
1077  circuit->gate(gt.CNOT, ctrls[0], tgts[i], "CX");
1078  }
1079  }
1080  } else if (ctrls.size() == tgts.size()) {
1081  for (idx i = 0; i < ctrls.size(); i++) {
1082  if (ctx.ccontrolled()) {
1083  std::vector<idx> tmp{ctrls[i], tgts[i]};
1084  circuit->cCTRL_custom(gt.CNOT, ctx.get_cctrls(), tmp,
1085  ctx.get_shift(), "CX");
1086  } else {
1087  circuit->gate(gt.CNOT, ctrls[i], tgts[i], "CX");
1088  }
1089  }
1090  } else {
1091  std::cerr << loc_ << ": Registers have different lengths\n";
1092  throw exception::SemanticError("qpp::qasm::CNOTGate::evaluate()");
1093  }
1094  }
1095 
1099  std::ostream& display(std::ostream& os) const {
1100  os << "CX " << ctrl_ << tgt_ << ";\n";
1101  return os;
1102  }
1103 };
1104 
1110 class BarrierGate final : public Gate {
1111  std::vector<Varinfo> args_;
1112 
1113  public:
1120  BarrierGate(Location loc, std::vector<Varinfo> args)
1121  : Gate(loc), args_(args) {}
1122 
1126  void evaluate(Context& ctx) const {
1127  // just check validity and do nothing
1128  idx mapping_size = 1;
1129 
1130  for (auto& arg : args_) {
1131  auto tmp = arg.as_qreg(ctx);
1132  if (tmp.size() > 1) {
1133  if (mapping_size == 1) {
1134  mapping_size = tmp.size();
1135  } else if (mapping_size != tmp.size()) {
1136  std::cerr << loc_ << ": Registers have different lengths\n";
1137  throw exception::SemanticError(
1138  "qpp::qasm::BarrierGate::evaluate()");
1139  }
1140  }
1141  }
1142  }
1143 
1147  std::ostream& display(std::ostream& os) const {
1148  os << "barrier ";
1149  for (auto it = args_.begin(); it != args_.end(); it++) {
1150  os << (it == args_.begin() ? "" : ",") << *it;
1151  }
1152  os << ";\n";
1153  return os;
1154  }
1155 };
1156 
1162 class DeclaredGate final : public Gate {
1163  ident id_;
1164  std::vector<ExprPtr> c_args_;
1165  std::vector<Varinfo> q_args_;
1166 
1167  public:
1176  DeclaredGate(Location loc, ident id, std::vector<ExprPtr>&& c_args,
1177  std::vector<Varinfo> q_args)
1178  : Gate(loc), id_(id), c_args_(std::move(c_args)),
1179  q_args_(std::move(q_args)) {}
1180 
1184  void evaluate(Context& ctx) const {
1185  auto gate = dynamic_cast<const Circuit*>(ctx.lookup(id_, loc_));
1186 
1187  // check gate type
1188  if (gate == nullptr) {
1189  std::cerr << loc_ << ": Identifier " << id_
1190  << " does not refer to a gate declaration\n";
1191  throw exception::SemanticError(
1192  "qpp::qasm::DeclaredGate::evaluate()");
1193  }
1194 
1195  // check argument lengths
1196  if (c_args_.size() != gate->c_params_.size()) {
1197  std::cerr << loc_ << ": " << id_ << " expects "
1198  << gate->c_params_.size();
1199  std::cerr << " classic arguments, got " << c_args_.size() << "\n";
1200  throw exception::SemanticError(
1201  "qpp::qasm::DeclaredGate::evaluate()");
1202  } else if (q_args_.size() != gate->q_params_.size()) {
1203  std::cerr << loc_ << ": " << id_ << " expects "
1204  << gate->q_params_.size();
1205  std::cerr << " quantum arguments, got " << q_args_.size() << "\n";
1206  throw exception::SemanticError(
1207  "qpp::qasm::DeclaredGate::evaluate()");
1208  }
1209 
1210  // evaluate arguments
1211  std::vector<double> c_args(c_args_.size());
1212  std::vector<std::vector<idx>> q_args(q_args_.size());
1213  for (idx i = 0; i < c_args_.size(); i++)
1214  c_args[i] = c_args_[i]->evaluate(ctx);
1215  for (idx i = 0; i < q_args_.size(); i++)
1216  q_args[i] = q_args_[i].as_qreg(ctx);
1217 
1218  // map gate across registers
1219  idx mapping_size = 1;
1220  std::vector<bool> mapped(q_args.size(), false);
1221  for (idx i = 0; i < q_args.size(); i++) {
1222  if (q_args[i].size() > 1) {
1223  mapped[i] = true;
1224  if (mapping_size == 1) {
1225  mapping_size = q_args[i].size();
1226  } else if (mapping_size != q_args[i].size()) {
1227  std::cerr << loc_ << ": Registers have different lengths\n";
1228  throw exception::SemanticError(
1229  "qpp::qasm::DeclaredGate::evaluate()");
1230  }
1231  }
1232  }
1233 
1234  auto it = known_matrices.find(id_);
1235  if (it != known_matrices.end()) {
1236  // apply the known matrix directly
1237  auto circuit = ctx.get_circuit();
1238  auto mat = it->second(c_args);
1239 
1240  // map the gate accross registers
1241  for (idx j = 0; j < mapping_size; j++) {
1242  // map virtual qubits to physical qubits
1243  std::vector<idx> mapped_args(q_args.size());
1244  for (idx i = 0; i < q_args.size(); i++) {
1245  mapped_args[i] = mapped[i] ? q_args[i][j] : q_args[i][0];
1246  }
1247 
1248  // apply (possibly classical controlled) gate
1249  if (ctx.ccontrolled()) {
1250  circuit->cCTRL_custom(mat, ctx.get_cctrls(), mapped_args,
1251  ctx.get_shift(), id_);
1252  } else {
1253  circuit->gate_custom(mat, mapped_args, id_);
1254  }
1255  }
1256  } else {
1257  // push classical arguments onto a new scope
1258  ctx.enter_scope();
1259  for (idx i = 0; i < c_args.size(); i++) {
1260  ctx.set(gate->c_params_[i],
1261  std::unique_ptr<Value>(new Number(c_args[i])));
1262  }
1263 
1264  // map the gate
1265  for (idx j = 0; j < mapping_size; j++) {
1266  ctx.enter_scope();
1267  for (idx i = 0; i < q_args.size(); i++) {
1268  ctx.set(gate->q_params_[i],
1269  std::unique_ptr<Value>(new Qubit(
1270  mapped[i] ? q_args[i][j] : q_args[i][0])));
1271  }
1272 
1273  // evaluate the gate
1274  for (auto it = gate->body_.begin(); it != gate->body_.end();
1275  it++) {
1276  (*it)->evaluate(ctx);
1277  }
1278 
1279  ctx.exit_scope();
1280  }
1281  ctx.exit_scope();
1282  }
1283  }
1284 
1288  std::ostream& display(std::ostream& os) const {
1289  os << id_;
1290  if (c_args_.size() > 0) {
1291  os << "(";
1292  for (auto it = c_args_.begin(); it != c_args_.end(); it++) {
1293  os << (it == c_args_.begin() ? "" : ",") << **it;
1294  }
1295  os << ")";
1296  }
1297  os << " ";
1298  for (auto it = q_args_.begin(); it != q_args_.end(); it++) {
1299  os << (it == q_args_.begin() ? "" : ",") << *it;
1300  }
1301  os << ";\n";
1302  return os;
1303  }
1304 };
1305 
1311 class RealExpr final : public Expr {
1312  double value_;
1313 
1314  public:
1321  RealExpr(Location loc, double value) : Expr(loc), value_(value) {}
1322 
1326  double evaluate(Context& ctx) const {
1327  (void) ctx;
1328  return value_;
1329  }
1330 
1334  std::ostream& display(std::ostream& os) const {
1335  os << value_;
1336  return os;
1337  }
1338 };
1339 
1345 class IntExpr final : public Expr {
1346  int value_;
1347 
1348  public:
1355  IntExpr(Location loc, int value) : Expr(loc), value_(value) {}
1356 
1360  double evaluate(Context& ctx) const {
1361  (void) ctx;
1362  return (double) value_;
1363  }
1364 
1368  std::ostream& display(std::ostream& os) const {
1369  os << value_;
1370  return os;
1371  }
1372 };
1373 
1379 class PiExpr final : public Expr {
1380  public:
1386  PiExpr(Location loc) : Expr(loc) {}
1387 
1391  double evaluate(Context& ctx) const {
1392  (void) ctx;
1393  return qpp::pi;
1394  }
1395 
1399  std::ostream& display(std::ostream& os) const {
1400  os << "pi";
1401  return os;
1402  }
1403 };
1404 
1410 class VarExpr final : public Expr {
1411  ident value_;
1412 
1413  public:
1420  VarExpr(Location loc, ident value) : Expr(loc), value_(value) {}
1421 
1425  double evaluate(Context& ctx) const {
1426  auto val = dynamic_cast<const Number*>(ctx.lookup(value_, loc_));
1427 
1428  if (val == nullptr) {
1429  std::cerr << loc_ << ": Identifier " << value_;
1430  std::cerr << " does not refer to a classical parameter\n";
1431  throw exception::ParseError("qpp::qasm::VarExpr::evaluate()");
1432  }
1433 
1434  return val->value_;
1435  }
1436 
1440  std::ostream& display(std::ostream& os) const {
1441  os << value_;
1442  return os;
1443  }
1444 };
1445 
1451 class BExpr final : public Expr {
1452  ExprPtr lexp_;
1453  BinaryOp bop_;
1454  ExprPtr rexp_;
1455 
1456  public:
1465  BExpr(Location loc, ExprPtr lexp, BinaryOp bop, ExprPtr rexp)
1466  : Expr(loc), lexp_(std::move(lexp)), bop_(bop), rexp_(std::move(rexp)) {
1467  }
1468 
1472  double evaluate(Context& ctx) const {
1473  double lval = lexp_->evaluate(ctx);
1474  double rval = rexp_->evaluate(ctx);
1475 
1476  switch (bop_) {
1477  case BinaryOp::Plus:
1478  return lval + rval;
1479  case BinaryOp::Minus:
1480  return lval - rval;
1481  case BinaryOp::Times:
1482  return lval * rval;
1483  case BinaryOp::Divide:
1484  return lval / rval;
1485  case BinaryOp::Pow:
1486  return pow(lval, rval);
1487  default:
1488  return 0;
1489  }
1490  }
1491 
1495  std::ostream& display(std::ostream& os) const {
1496  os << "(" << *lexp_;
1497  switch (bop_) {
1498  case BinaryOp::Plus:
1499  os << "+";
1500  break;
1501  case BinaryOp::Minus:
1502  os << "-";
1503  break;
1504  case BinaryOp::Times:
1505  os << "*";
1506  break;
1507  case BinaryOp::Divide:
1508  os << "/";
1509  break;
1510  case BinaryOp::Pow:
1511  os << "^";
1512  break;
1513  }
1514  os << *rexp_ << ")";
1515  return os;
1516  }
1517 };
1518 
1524 class UExpr final : public Expr {
1525  UnaryOp uop_;
1526  ExprPtr exp_;
1527 
1528  public:
1536  UExpr(Location loc, UnaryOp uop, ExprPtr exp)
1537  : Expr(loc), uop_(uop), exp_(std::move(exp)) {}
1538 
1542  double evaluate(Context& ctx) const {
1543  double val = exp_->evaluate(ctx);
1544 
1545  switch (uop_) {
1546  case UnaryOp::Neg:
1547  return -val;
1548  case UnaryOp::Sin:
1549  return sin(val);
1550  case UnaryOp::Cos:
1551  return cos(val);
1552  case UnaryOp::Tan:
1553  return tan(val);
1554  case UnaryOp::Ln:
1555  return log(val);
1556  case UnaryOp::Sqrt:
1557  return sqrt(val);
1558  case UnaryOp::Exp:
1559  return exp(val);
1560  default:
1561  return 0;
1562  }
1563  }
1564 
1568  std::ostream& display(std::ostream& os) const {
1569  switch (uop_) {
1570  case UnaryOp::Neg:
1571  os << "-" << *exp_;
1572  break;
1573  case UnaryOp::Sin:
1574  os << "sin(" << *exp_ << ")";
1575  break;
1576  case UnaryOp::Cos:
1577  os << "cos(" << *exp_ << ")";
1578  break;
1579  case UnaryOp::Tan:
1580  os << "tan(" << *exp_ << ")";
1581  break;
1582  case UnaryOp::Ln:
1583  os << "ln(" << *exp_ << ")";
1584  break;
1585  case UnaryOp::Sqrt:
1586  os << "sqrt(" << *exp_ << ")";
1587  break;
1588  case UnaryOp::Exp:
1589  os << "exp(" << *exp_ << ")";
1590  break;
1591  }
1592  return os;
1593  }
1594 };
1595 
1596 } /* namespace qasm */
1597 } /* namespace qpp */
1598 
1599 #endif /* QASM_AST_H_ */
Quantum++ main namespace.
Definition: circuits.h:35