1 /*
2  * This file is part of qasmtools.
3  *
4  * Copyright (c) 2019 - 2021 softwareQ Inc. All rights reserved.
5  *
6  * MIT License
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 
27 /**
28  * \file qasmtools/ast/stmt.hpp
29  * \brief openQASM statements
30  */
31 
32 #pragma once
33 
34 #include "base.hpp"
35 #include "expr.hpp"
36 #include "var.hpp"
37 
38 #include <functional>
39 #include <vector>
40 
41 namespace qasmtools {
42 namespace ast {
43 
44 /**
45  * \class qasmtools::ast::Stmt
46  * \brief Base class for openQASM statements
47  */
48 class Stmt : public ASTNode {
49   public:
Stmt(parser::Position pos)50     Stmt(parser::Position pos) : ASTNode(pos) {}
51     virtual ~Stmt() = default;
52     virtual Stmt* clone() const override = 0;
53 
54     /**
55      * \brief Internal pretty-printer which can suppress the output of the
56      * stdlib
57      *
58      * \param suppress_std Whether to suppress output of the standard library
59      */
60     virtual std::ostream& pretty_print(std::ostream& os,
61                                        bool suppress_std) const = 0;
62 
pretty_print(std::ostream & os) const63     std::ostream& pretty_print(std::ostream& os) const override {
64         return pretty_print(os, false);
65     }
66 };
67 
68 /**
69  * \class qasmtools::ast::MeasureStmt
70  * \brief Class for measurement statements
71  * \see qasmtools::ast::Stmt
72  */
73 class MeasureStmt final : public Stmt {
74     VarAccess q_arg_; ///< the quantum bit|register
75     VarAccess c_arg_; ///< the classical bit|register
76 
77   public:
78     /**
79      * \brief Constructs a measurement statement
80      *
81      * \param pos The source position
82      * \param q_arg Rvalue reference to the quantum argument
83      * \param c_arg Rvalue reference to the classical argument
84      */
MeasureStmt(parser::Position pos,VarAccess && q_arg,VarAccess && c_arg)85     MeasureStmt(parser::Position pos, VarAccess&& q_arg, VarAccess&& c_arg)
86         : Stmt(pos), q_arg_(std::move(q_arg)), c_arg_(std::move(c_arg)) {}
87 
88     /**
89      * \brief Protected heap-allocated construction
90      */
create(parser::Position pos,VarAccess && q_arg,VarAccess && c_arg)91     static ptr<MeasureStmt> create(parser::Position pos, VarAccess&& q_arg,
92                                    VarAccess&& c_arg) {
93         return std::make_unique<MeasureStmt>(pos, std::move(q_arg),
94                                              std::move(c_arg));
95     }
96 
97     /**
98      * \brief Get the quantum argument
99      *
100      * \return Reference to the quantum argument
101      */
q_arg()102     VarAccess& q_arg() { return q_arg_; }
103 
104     /**
105      * \brief Get the classical argument
106      *
107      * \return Reference to the classical argument
108      */
c_arg()109     VarAccess& c_arg() { return c_arg_; }
110 
111     /**
112      * \brief Set the quantum argument
113      *
114      * \param arg Const reference to a new argument
115      */
set_qarg(const VarAccess & arg)116     void set_qarg(const VarAccess& arg) { q_arg_ = arg; }
117 
118     /**
119      * \brief Set the classical argument
120      *
121      * \param arg Const reference to a new argument
122      */
set_carg(const VarAccess & arg)123     void set_carg(const VarAccess& arg) { c_arg_ = arg; }
124 
accept(Visitor & visitor)125     void accept(Visitor& visitor) override { visitor.visit(*this); }
pretty_print(std::ostream & os,bool) const126     std::ostream& pretty_print(std::ostream& os, bool) const override {
127         os << "measure " << q_arg_ << " -> " << c_arg_ << ";\n";
128         return os;
129     }
clone() const130     MeasureStmt* clone() const override {
131         return new MeasureStmt(pos_, VarAccess(q_arg_), VarAccess(c_arg_));
132     }
133 };
134 
135 /**
136  * \class qasmtools::ast::ResetStmt
137  * \brief Class for reset statements
138  * \see qasmtools::ast::Stmt
139  */
140 class ResetStmt final : public Stmt {
141     VarAccess arg_; ///< the qbit|qreg
142 
143   public:
144     /**
145      * \brief Constructs a reset statement
146      *
147      * \param pos The source position
148      * \param arg Rvalue reference to the argument
149      */
ResetStmt(parser::Position pos,VarAccess && arg)150     ResetStmt(parser::Position pos, VarAccess&& arg)
151         : Stmt(pos), arg_(std::move(arg)) {}
152 
153     /**
154      * \brief Protected heap-allocated construction
155      */
create(parser::Position pos,VarAccess && arg)156     static ptr<ResetStmt> create(parser::Position pos, VarAccess&& arg) {
157         return std::make_unique<ResetStmt>(pos, std::move(arg));
158     }
159 
160     /**
161      * \brief Get the argument
162      *
163      * \return Reference to the argument
164      */
arg()165     VarAccess& arg() { return arg_; }
166 
167     /**
168      * \brief Set the argument
169      *
170      * \param arg Const reference to a new argument
171      */
set_arg(const VarAccess & arg)172     void set_arg(const VarAccess& arg) { arg_ = arg; }
173 
accept(Visitor & visitor)174     void accept(Visitor& visitor) override { visitor.visit(*this); }
pretty_print(std::ostream & os,bool) const175     std::ostream& pretty_print(std::ostream& os, bool) const override {
176         os << "reset " << arg_ << ";\n";
177         return os;
178     }
clone() const179     ResetStmt* clone() const override {
180         return new ResetStmt(pos_, VarAccess(arg_));
181     }
182 };
183 
184 /**
185  * \class qasmtools::ast::IfStmt
186  * \brief Class for if statements
187  * \see qasmtools::ast::Stmt
188  */
189 class IfStmt final : public Stmt {
190     symbol var_;     ///< classical register name
191     int cond_;       ///< value to check against
192     ptr<Stmt> then_; ///< statement to be executed if true
193 
194   public:
195     /**
196      * \brief Constructs an if statement
197      *
198      * \param pos The source position
199      * \param var The variable (classical register) being tested
200      * \param cond The integer value to test against
201      * \param then The statement to execute in the then branch
202      */
IfStmt(parser::Position pos,symbol var,int cond,ptr<Stmt> then)203     IfStmt(parser::Position pos, symbol var, int cond, ptr<Stmt> then)
204         : Stmt(pos), var_(var), cond_(cond), then_(std::move(then)) {}
205 
206     /**
207      * \brief Protected heap-allocated construction
208      */
create(parser::Position pos,symbol var,int cond,ptr<Stmt> then)209     static ptr<IfStmt> create(parser::Position pos, symbol var, int cond,
210                               ptr<Stmt> then) {
211         return std::make_unique<IfStmt>(pos, var, cond, std::move(then));
212     }
213 
214     /**
215      * \brief Get the tested variable
216      *
217      * \return Const reference to the variable name
218      */
var() const219     const symbol& var() const { return var_; }
220 
221     /**
222      * \brief Get the integer condition
223      *
224      * \return The integer tested against
225      */
cond() const226     int cond() const { return cond_; }
227 
228     /**
229      * \brief Get the then branch
230      *
231      * \return Reference to the "then" statement
232      */
then()233     Stmt& then() { return *then_; }
234 
235     /**
236      * \brief Set the then branch
237      *
238      * \param then The new statement
239      */
set_then(ptr<Stmt> then)240     void set_then(ptr<Stmt> then) { then_ = std::move(then); }
241 
accept(Visitor & visitor)242     void accept(Visitor& visitor) override { visitor.visit(*this); }
pretty_print(std::ostream & os,bool) const243     std::ostream& pretty_print(std::ostream& os, bool) const override {
244         os << "if (" << var_ << "==" << cond_ << ") " << *then_;
245         return os;
246     }
clone() const247     IfStmt* clone() const override {
248         return new IfStmt(pos_, var_, cond_, ptr<Stmt>(then_->clone()));
249     }
250 };
251 
252 /**
253  * \class qasmtools::ast::Gate
254  * \brief Statement sub-class for gate
255  */
256 class Gate : public Stmt {
257   public:
Gate(parser::Position pos)258     Gate(parser::Position pos) : Stmt(pos) {}
259     virtual ~Gate() = default;
260     virtual Gate* clone() const = 0;
261 };
262 
263 /**
264  * \class qasmtools::ast::UGate
265  * \brief Class for U gates
266  * \see qasmtools::ast::Gate
267  */
268 class UGate final : public Gate {
269     ptr<Expr> theta_;  ///< theta angle
270     ptr<Expr> phi_;    ///< phi angle
271     ptr<Expr> lambda_; ///< lambda angle
272 
273     VarAccess arg_; ///< quantum bit|register
274 
275   public:
276     /**
277      * \brief Constructs a U gate
278      *
279      * \param pos The source position
280      * \param theta The theta angle
281      * \param phi The phi angle
282      * \param lambda The lambda angle
283      * \param arg Rvalue reference to the quantum argument
284      */
UGate(parser::Position pos,ptr<Expr> theta,ptr<Expr> phi,ptr<Expr> lambda,VarAccess && arg)285     UGate(parser::Position pos, ptr<Expr> theta, ptr<Expr> phi,
286           ptr<Expr> lambda, VarAccess&& arg)
287         : Gate(pos), theta_(std::move(theta)), phi_(std::move(phi)),
288           lambda_(std::move(lambda)), arg_(std::move(arg)) {}
289 
290     /**
291      * \brief Protected heap-allocated construction
292      */
create(parser::Position pos,ptr<Expr> theta,ptr<Expr> phi,ptr<Expr> lambda,VarAccess && arg)293     static ptr<UGate> create(parser::Position pos, ptr<Expr> theta,
294                              ptr<Expr> phi, ptr<Expr> lambda, VarAccess&& arg) {
295         return std::make_unique<UGate>(pos, std::move(theta), std::move(phi),
296                                        std::move(lambda), std::move(arg));
297     }
298 
299     /**
300      * \brief Get the theta angle
301      *
302      * \return Reference to the angle expression
303      */
theta()304     Expr& theta() { return *theta_; }
305 
306     /**
307      * \brief Get the phi angle
308      *
309      * \return Reference to the angle expression
310      */
phi()311     Expr& phi() { return *phi_; }
312 
313     /**
314      * \brief Get the lambda angle
315      *
316      * \return Reference to the angle expression
317      */
lambda()318     Expr& lambda() { return *lambda_; }
319 
320     /**
321      * \brief Get the argument
322      *
323      * \return Reference to the quantum argument
324      */
arg()325     VarAccess& arg() { return arg_; }
326 
327     /**
328      * \brief Set the theta angle
329      *
330      * \param theta The new angle expression
331      */
set_theta(ptr<Expr> theta)332     void set_theta(ptr<Expr> theta) { theta_ = std::move(theta); }
333 
334     /**
335      * \brief Set the phi angle
336      *
337      * \param theta The new angle expression
338      */
set_phi(ptr<Expr> phi)339     void set_phi(ptr<Expr> phi) { phi_ = std::move(phi); }
340 
341     /**
342      * \brief Set the lambda angle
343      *
344      * \param theta The new angle expression
345      */
set_lambda(ptr<Expr> lambda)346     void set_lambda(ptr<Expr> lambda) { lambda_ = std::move(lambda); }
347 
348     /**
349      * \brief Set the argument
350      *
351      * \param arg The new argument
352      */
set_arg(const VarAccess & arg)353     void set_arg(const VarAccess& arg) { arg_ = arg; }
354 
accept(Visitor & visitor)355     void accept(Visitor& visitor) override { visitor.visit(*this); }
pretty_print(std::ostream & os,bool) const356     std::ostream& pretty_print(std::ostream& os, bool) const override {
357         os << "U(" << *theta_ << "," << *phi_ << "," << *lambda_ << ") " << arg_
358            << ";\n";
359         return os;
360     }
clone() const361     UGate* clone() const override {
362         return new UGate(pos_, ptr<Expr>(theta_->clone()),
363                          ptr<Expr>(phi_->clone()), ptr<Expr>(lambda_->clone()),
364                          VarAccess(arg_));
365     }
366 };
367 
368 /**
369  * \class qasmtools::ast::CNOTGate
370  * \brief Class for CX gates
371  * \see qasmtools::ast::Gate
372  */
373 class CNOTGate final : public Gate {
374     VarAccess ctrl_; ///< control qubit|qreg
375     VarAccess tgt_;  ///< target qubit|qreg
376 
377   public:
378     /**
379      * \brief Constructs a CNOT gate
380      *
381      * \param pos The source position
382      * \param ctrl Rvalue reference to the control argument
383      * \param tgt Rvalue reference to the target argument
384      */
CNOTGate(parser::Position pos,VarAccess && ctrl,VarAccess && tgt)385     CNOTGate(parser::Position pos, VarAccess&& ctrl, VarAccess&& tgt)
386         : Gate(pos), ctrl_(std::move(ctrl)), tgt_(std::move(tgt)) {}
387 
388     /**
389      * \brief Protected heap-allocated construction
390      */
create(parser::Position pos,VarAccess && ctrl,VarAccess && tgt)391     static ptr<CNOTGate> create(parser::Position pos, VarAccess&& ctrl,
392                                 VarAccess&& tgt) {
393         return std::make_unique<CNOTGate>(pos, std::move(ctrl), std::move(tgt));
394     }
395 
396     /**
397      * \brief Get the control argument
398      *
399      * \return Reference to the quantum argument
400      */
ctrl()401     VarAccess& ctrl() { return ctrl_; }
402 
403     /**
404      * \brief Get the target argument
405      *
406      * \return Reference to the quantum argument
407      */
tgt()408     VarAccess& tgt() { return tgt_; }
409 
410     /**
411      * \brief Set the control argument
412      *
413      * \param ctrl The new argument
414      */
set_ctrl(const VarAccess & ctrl)415     void set_ctrl(const VarAccess& ctrl) { ctrl_ = ctrl; }
416 
417     /**
418      * \brief Set the target argument
419      *
420      * \param tgt The new argument
421      */
set_tgt(const VarAccess & tgt)422     void set_tgt(const VarAccess& tgt) { tgt_ = tgt; }
423 
accept(Visitor & visitor)424     void accept(Visitor& visitor) override { visitor.visit(*this); }
pretty_print(std::ostream & os,bool) const425     std::ostream& pretty_print(std::ostream& os, bool) const override {
426         os << "CX " << ctrl_ << "," << tgt_ << ";\n";
427         return os;
428     }
clone() const429     CNOTGate* clone() const override {
430         return new CNOTGate(pos_, VarAccess(ctrl_), VarAccess(tgt_));
431     }
432 };
433 
434 /**
435  * \class qasmtools::ast::BarrierGate
436  * \brief Class for barrier gates
437  * \see qasmtools::ast::Gate
438  */
439 class BarrierGate final : public Gate {
440     std::vector<VarAccess> args_; ///< list of quantum bits|registers
441 
442   public:
443     /**
444      * \brief Constructs a barrier gate
445      *
446      * \param pos The source position
447      * \param args Rvalue reference to a list of arguments
448      */
BarrierGate(parser::Position pos,std::vector<VarAccess> && args)449     BarrierGate(parser::Position pos, std::vector<VarAccess>&& args)
450         : Gate(pos), args_(std::move(args)) {}
451 
452     /**
453      * \brief Protected heap-allocated construction
454      */
create(parser::Position pos,std::vector<VarAccess> && args)455     static ptr<BarrierGate> create(parser::Position pos,
456                                    std::vector<VarAccess>&& args) {
457         return std::make_unique<BarrierGate>(pos, std::move(args));
458     }
459 
460     /**
461      * \brief Get the number of arguments
462      *
463      * \return The number of arguments
464      */
num_args() const465     int num_args() const { return static_cast<int>(args_.size()); }
466 
467     /**
468      * \brief Get the list of arguments
469      *
470      * \return Reference to the list of arguments
471      */
args()472     std::vector<VarAccess>& args() { return args_; }
473 
474     /**
475      * \brief Get the ith argument
476      *
477      * \param i The number of the argument (0 indexed)
478      * \return Reference to the ith argument
479      */
arg(int i)480     VarAccess& arg(int i) { return args_[i]; }
481 
482     /**
483      * \brief Apply a function to each argument
484      *
485      * \param f Void function accepting a reference to the argument
486      */
foreach_arg(std::function<void (VarAccess &)> f)487     void foreach_arg(std::function<void(VarAccess&)> f) {
488         for (auto it = args_.begin(); it != args_.end(); it++)
489             f(*it);
490     }
491 
492     /**
493      * \brief Set the ith argument
494      *
495      * \param i The number of the argument (0 indexed)
496      * \param arg The new argument
497      */
set_arg(int i,const VarAccess & arg)498     void set_arg(int i, const VarAccess& arg) { args_[i] = arg; }
499 
accept(Visitor & visitor)500     void accept(Visitor& visitor) override { visitor.visit(*this); }
pretty_print(std::ostream & os,bool) const501     std::ostream& pretty_print(std::ostream& os, bool) const override {
502         os << "barrier ";
503         for (auto it = args_.begin(); it != args_.end(); it++) {
504             os << (it == args_.begin() ? "" : ",") << *it;
505         }
506         os << ";\n";
507         return os;
508     }
clone() const509     BarrierGate* clone() const override {
510         return new BarrierGate(pos_, std::vector<VarAccess>(args_));
511     }
512 };
513 
514 /**
515  * \class qasmtools::ast::DeclaredGate
516  * \brief Class for declared gate applications
517  * \see qasmtools::ast::Gate
518  */
519 class DeclaredGate final : public Gate {
520     symbol name_;                   ///< gate identifier
521     std::vector<ptr<Expr>> c_args_; ///< list of classical arguments
522     std::vector<VarAccess> q_args_; ///< list of quantum arguments
523 
524   public:
525     /**
526      * \brief Constructs an application of a declared gate
527      *
528      * \param pos The source position
529      * \param name The gate name
530      * \param c_args Rvalue reference to a list of classical arguments
531      * \param q_args Rvalue reference to a list of quantum arguments
532      */
DeclaredGate(parser::Position pos,symbol name,std::vector<ptr<Expr>> && c_args,std::vector<VarAccess> && q_args)533     DeclaredGate(parser::Position pos, symbol name,
534                  std::vector<ptr<Expr>>&& c_args,
535                  std::vector<VarAccess>&& q_args)
536         : Gate(pos), name_(name), c_args_(std::move(c_args)),
537           q_args_(std::move(q_args)) {}
538 
539     /**
540      * \brief Protected heap-allocated construction
541      */
create(parser::Position pos,symbol name,std::vector<ptr<Expr>> && c_args,std::vector<VarAccess> && q_args)542     static ptr<DeclaredGate> create(parser::Position pos, symbol name,
543                                     std::vector<ptr<Expr>>&& c_args,
544                                     std::vector<VarAccess>&& q_args) {
545         return std::make_unique<DeclaredGate>(pos, name, std::move(c_args),
546                                               std::move(q_args));
547     }
548 
549     /**
550      * \brief Get the gate name
551      *
552      * \return Const reference to the gate name
553      */
name() const554     const symbol& name() const { return name_; }
555 
556     /**
557      * \brief Get the number of classical arguments
558      *
559      * \return The number of arguments
560      */
num_cargs() const561     int num_cargs() const { return static_cast<int>(c_args_.size()); }
562 
563     /**
564      * \brief Get the number of quantum arguments
565      *
566      * \return The number of arguments
567      */
num_qargs() const568     int num_qargs() const { return static_cast<int>(q_args_.size()); }
569 
570     /**
571      * \brief Get the ith classical argument
572      *
573      * \param i The number of the argument, 0-indexed
574      * \return Reference to an expression
575      */
carg(int i)576     Expr& carg(int i) { return *(c_args_[i]); }
577 
578     /**
579      * \brief Get the ith quantum argument
580      *
581      * \param i The number of the argument, 0-indexed
582      * \return Reference to the argument
583      */
qarg(int i)584     VarAccess& qarg(int i) { return q_args_[i]; }
585 
586     /**
587      * \brief Get the list of quantum arguments
588      *
589      * \return Reference to the list of arguments
590      */
qargs()591     std::vector<VarAccess>& qargs() { return q_args_; }
592 
593     /**
594      * \brief Apply a function to each classical argument
595      *
596      * \param f Void function accepting an expression reference
597      */
foreach_carg(std::function<void (Expr &)> f)598     void foreach_carg(std::function<void(Expr&)> f) {
599         for (auto it = c_args_.begin(); it != c_args_.end(); it++)
600             f(**it);
601     }
602 
603     /**
604      * \brief Apply a function to each quantum argument
605      *
606      * \param f Void function accepting a reference to an argument
607      */
foreach_qarg(std::function<void (VarAccess &)> f)608     void foreach_qarg(std::function<void(VarAccess&)> f) {
609         for (auto it = q_args_.begin(); it != q_args_.end(); it++)
610             f(*it);
611     }
612 
613     /**
614      * \brief Set the ith classical argument
615      *
616      * \param i The number of the argument, 0-indexed
617      * \param expr An expression giving the new argument
618      */
set_carg(int i,ptr<Expr> expr)619     void set_carg(int i, ptr<Expr> expr) { c_args_[i] = std::move(expr); }
620 
621     /**
622      * \brief Set the ith quantum argument
623      *
624      * \param i The number of the argument, 0-indexed
625      * \param arg The new argument
626      */
set_qarg(int i,const VarAccess & arg)627     void set_qarg(int i, const VarAccess& arg) { q_args_[i] = arg; }
628 
accept(Visitor & visitor)629     void accept(Visitor& visitor) override { visitor.visit(*this); }
pretty_print(std::ostream & os,bool) const630     std::ostream& pretty_print(std::ostream& os, bool) const override {
631         os << name_;
632         if (c_args_.size() > 0) {
633             os << "(";
634             for (auto it = c_args_.begin(); it != c_args_.end(); it++) {
635                 os << (it == c_args_.begin() ? "" : ",") << **it;
636             }
637             os << ")";
638         }
639         os << " ";
640         for (auto it = q_args_.begin(); it != q_args_.end(); it++) {
641             os << (it == q_args_.begin() ? "" : ",") << *it;
642         }
643         os << ";\n";
644         return os;
645     }
clone() const646     DeclaredGate* clone() const override {
647         std::vector<ptr<Expr>> c_tmp;
648         for (auto it = c_args_.begin(); it != c_args_.end(); it++) {
649             c_tmp.emplace_back(ptr<Expr>((*it)->clone()));
650         }
651 
652         return new DeclaredGate(pos_, name_, std::move(c_tmp),
653                                 std::vector<VarAccess>(q_args_));
654     }
655 };
656 
657 } // namespace ast
658 } // namespace qasmtools
659