Maestro 0.3.1
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
Expr.h
Go to the documentation of this file.
1
12#pragma once
13
14#ifndef _EXPR_H_
15#define _EXPR_H_
16
17#ifdef DEBUG
18#define BOOST_SPIRIT_QI_DEBUG
19#endif // DEBUG
20
21#define _USE_MATH_DEFINES
22#include <math.h>
23
24#include <boost/fusion/include/adapt_struct.hpp>
25#include <boost/fusion/include/io.hpp>
26#include <boost/phoenix.hpp>
27#include <boost/phoenix/object.hpp>
28#include <boost/spirit/include/qi.hpp>
29
30#include <algorithm>
31#include <cmath>
32#include <optional>
33#include <string>
34#include <variant>
35#include <vector>
36
37namespace qasm {
38namespace qi = boost::spirit::qi;
39namespace ascii = boost::spirit::ascii;
40namespace phx = boost::phoenix;
41
43 public:
44 virtual ~AbstractSyntaxTree() = default;
45 virtual double Eval() const { return 0; }
46 virtual double Eval(
47 const std::unordered_map<std::string, double> &variables) const {
48 return 0;
49 }
50
51 protected:
52 AbstractSyntaxTree() = default;
57};
58
59typedef std::shared_ptr<AbstractSyntaxTree> AbstractSyntaxTreePtr;
60
61template <typename Expr>
62static AbstractSyntaxTreePtr Clone(Expr const &t) {
63 return std::make_shared<Expr>(t);
64}
65
66// for expressions (to be evaluated, typically those are values for parameters
67// for gates)
68
70 public:
71 Constant(double value = 0) : value(value) {}
72 Constant(int value) : value(value) {}
73
74 Constant &operator=(int value) {
75 this->value = value;
76 return *this;
77 }
78 Constant &operator=(double value) {
79 this->value = value;
80 return *this;
81 }
82
83 double Eval() const override { return value; }
84 double Eval(
85 const std::unordered_map<std::string, double> &variables) const override {
86 return value;
87 }
88
89 private:
90 double value;
91};
92
94 template <typename>
95 struct result {
96 typedef Constant type;
97 };
98
99 template <typename C>
100 Constant operator()(C op) const {
101 return Constant(op);
102 }
103};
104
105inline phx::function<MakeConstantExpression> MakeConstant;
106
108 public:
109 Variable(const std::string &value = "") : value(value) {}
110
111 Variable &operator=(int value) {
112 this->value = value;
113 return *this;
114 }
115
116 double Eval() const override { return 0; }
117 double Eval(
118 const std::unordered_map<std::string, double> &variables) const override {
119 auto it = variables.find(value);
120 if (it != variables.end())
121 return it->second;
122 else
123 throw std::invalid_argument("Variable not found: " + value);
124
125 return 0;
126 }
127
128 private:
129 std::string value;
130};
131
133 template <typename>
134 struct result {
135 typedef Variable type;
136 };
137
138 template <typename V>
139 Variable operator()(V v) const {
140 return Variable(v);
141 }
142};
143
144inline phx::function<MakeVariableExpression> MakeVariable;
145
146// Operator codes are the source characters, with one exception: 'X' stands
147// for bitwise XOR. QASM2 spells exponentiation '^' and QASM3 spells it '**'
148// while giving '^' to bitwise XOR, so the two meanings cannot share a code -
149// '^' is kept for exponentiation (built by both the QASM2 '^' rule and the
150// '**' rule) and XOR gets its own. Which node is built is decided by the
151// grammar, per language version; see `factor2`/`expression` in qasm.h.
153 public:
154 template <typename L, typename R>
155 BinaryOperator(char op, const L &left, const R &right)
156 : op(op), left(Clone(left)), right(Clone(right)) {}
157
158 double Eval() const override {
159 switch (op) {
160 case '+':
161 return left->Eval() + right->Eval();
162 case '-':
163 return left->Eval() - right->Eval();
164 case '*':
165 return left->Eval() * right->Eval();
166 case '/':
167 return left->Eval() / right->Eval();
168 case '^':
169 return pow(left->Eval(), right->Eval());
170 case 'X':
171 return static_cast<double>(AsInteger(left->Eval()) ^
172 AsInteger(right->Eval()));
173 default:
174 throw std::invalid_argument("Unknown binary operator");
175 }
176
177 return 0;
178 }
179
180 double Eval(
181 const std::unordered_map<std::string, double> &variables) const override {
182 switch (op) {
183 case '+':
184 return left->Eval(variables) + right->Eval(variables);
185 case '-':
186 return left->Eval(variables) - right->Eval(variables);
187 case '*':
188 return left->Eval(variables) * right->Eval(variables);
189 case '/':
190 return left->Eval(variables) / right->Eval(variables);
191 case '^':
192 return pow(left->Eval(variables), right->Eval(variables));
193 case 'X':
194 return static_cast<double>(AsInteger(left->Eval(variables)) ^
195 AsInteger(right->Eval(variables)));
196 default:
197 throw std::invalid_argument("Unknown binary operator");
198 }
199
200 return 0;
201 }
202
203 private:
204 // Bitwise XOR is only defined on integers, but every value in this
205 // expression tree is a double. A non-integral or out-of-range operand is
206 // rejected by name rather than truncated: truncating would turn e.g.
207 // `rx(0.5 ^ 1)` into a silently wrong rotation angle, which is exactly the
208 // failure mode QASM3's '^' was introducing here in the first place.
209 static long long AsInteger(double value) {
210 if (!std::isfinite(value))
211 throw std::invalid_argument(
212 "Bitwise XOR ('^') requires finite integer operands, got: " +
213 std::to_string(value));
214
215 const double rounded = std::round(value);
216
217 if (std::abs(value - rounded) > 1e-9)
218 throw std::invalid_argument(
219 "Bitwise XOR ('^') requires integer operands, got: " +
220 std::to_string(value));
221
222 // The magnitude bound keeps the cast below defined; anything near it is
223 // far outside the range of a meaningful gate parameter anyway.
224 if (std::abs(rounded) > 4.5e15)
225 throw std::invalid_argument(
226 "Bitwise XOR ('^') operand is out of the supported integer range: " +
227 std::to_string(value));
228
229 return static_cast<long long>(rounded);
230 }
231
232 char op;
233 AbstractSyntaxTreePtr left, right;
234};
235
237 template <typename, typename, typename>
238 struct result {
240 };
241
242 template <typename C, typename L, typename R>
243 BinaryOperator operator()(C op, const L &lhs, const R &rhs) const {
244 return BinaryOperator(op, lhs, rhs);
245 }
246};
247
248inline phx::function<MakeBinaryExpression> MakeBinary;
249
251 public:
252 UnaryOperator() : op('+') {}
253
254 template <typename R>
255 UnaryOperator(char op, const R &right) : op(op), right(Clone(right)) {}
256
257 double Eval() const override {
258 switch (op) {
259 case '+':
260 return right->Eval();
261 case '-':
262 return -right->Eval();
263 default:
264 throw std::invalid_argument("Unknown unary operator");
265 }
266
267 return 0;
268 }
269
270 double Eval(
271 const std::unordered_map<std::string, double> &variables) const override {
272 switch (op) {
273 case '+':
274 return right->Eval(variables);
275 case '-':
276 return -right->Eval(variables);
277 default:
278 throw std::invalid_argument("Unknown unary operator");
279 }
280 return 0;
281 }
282
283 private:
284 char op;
286};
287
289 template <typename, typename>
290 struct result {
292 };
293
294 template <typename C, typename R>
295 UnaryOperator operator()(C op, const R &rhs) const {
296 return UnaryOperator(op, rhs);
297 }
298};
299
300inline phx::function<MakeUnaryExpression> MakeUnary;
301
303 public:
304 template <typename F>
305 Function(const std::string &func, const F &param)
306 : func(func), param(Clone(param)) {}
307
308 double Eval() const override {
309 if (func == "sin")
310 return sin(param->Eval());
311 else if (func == "cos")
312 return cos(param->Eval());
313 else if (func == "tan")
314 return tan(param->Eval());
315 else if (func == "exp")
316 return exp(param->Eval());
317 else if (func == "ln")
318 return log(param->Eval());
319 else if (func == "sqrt")
320 return sqrt(param->Eval());
321
322 throw std::invalid_argument("Unknown function");
323
324 return 0;
325 }
326
327 double Eval(
328 const std::unordered_map<std::string, double> &variables) const override {
329 if (func == "sin")
330 return sin(param->Eval(variables));
331 else if (func == "cos")
332 return cos(param->Eval(variables));
333 else if (func == "tan")
334 return tan(param->Eval(variables));
335 else if (func == "exp")
336 return exp(param->Eval(variables));
337 else if (func == "ln")
338 return log(param->Eval(variables));
339 else if (func == "sqrt")
340 return sqrt(param->Eval(variables));
341
342 throw std::invalid_argument("Unknown function");
343
344 return 0;
345 }
346
347 private:
348 std::string func;
350};
351
353 template <typename, typename, typename>
354 struct result {
355 typedef Function type;
356 };
357
358 template <typename Params>
359 Function operator()(const std::string &funcName, const Params &params) const {
360 return Function(funcName, params);
361 }
362};
363
364inline phx::function<MakeFunctionExpression> MakeFunction;
365
367 public:
369 ~Expression() override {}
370
371 template <typename E>
372 Expression(E const &e) : expr(Clone(e)) {}
373
374 double Eval() const override { return expr->Eval(); }
375
376 double Eval(
377 const std::unordered_map<std::string, double> &variables) const override {
378 return expr->Eval(variables);
379 }
380
381 friend AbstractSyntaxTreePtr Clone(Expression const &e) { return e.expr; }
382
383 private:
385};
386} // namespace qasm
387
388#endif
AbstractSyntaxTree(AbstractSyntaxTree &&)=default
AbstractSyntaxTree(const AbstractSyntaxTree &)=default
virtual ~AbstractSyntaxTree()=default
virtual double Eval(const std::unordered_map< std::string, double > &variables) const
Definition Expr.h:46
virtual double Eval() const
Definition Expr.h:45
AbstractSyntaxTree & operator=(const AbstractSyntaxTree &)=default
AbstractSyntaxTree & operator=(AbstractSyntaxTree &&)=default
double Eval() const override
Definition Expr.h:158
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:180
BinaryOperator(char op, const L &left, const R &right)
Definition Expr.h:155
Constant(int value)
Definition Expr.h:72
double Eval() const override
Definition Expr.h:83
Constant(double value=0)
Definition Expr.h:71
Constant & operator=(int value)
Definition Expr.h:74
Constant & operator=(double value)
Definition Expr.h:78
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:84
~Expression() override
Definition Expr.h:369
friend AbstractSyntaxTreePtr Clone(Expression const &e)
Definition Expr.h:381
double Eval() const override
Definition Expr.h:374
Expression(E const &e)
Definition Expr.h:372
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:376
Function(const std::string &func, const F &param)
Definition Expr.h:305
double Eval() const override
Definition Expr.h:308
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:327
UnaryOperator(char op, const R &right)
Definition Expr.h:255
double Eval() const override
Definition Expr.h:257
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:270
Variable & operator=(int value)
Definition Expr.h:111
double Eval() const override
Definition Expr.h:116
Variable(const std::string &value="")
Definition Expr.h:109
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:117
phx::function< MakeFunctionExpression > MakeFunction
Definition Expr.h:364
phx::function< MakeUnaryExpression > MakeUnary
Definition Expr.h:300
phx::function< MakeBinaryExpression > MakeBinary
Definition Expr.h:248
static AbstractSyntaxTreePtr Clone(Expr const &t)
Definition Expr.h:62
std::shared_ptr< AbstractSyntaxTree > AbstractSyntaxTreePtr
Definition Expr.h:59
phx::function< MakeVariableExpression > MakeVariable
Definition Expr.h:144
phx::function< MakeConstantExpression > MakeConstant
Definition Expr.h:105
BinaryOperator operator()(C op, const L &lhs, const R &rhs) const
Definition Expr.h:243
Constant operator()(C op) const
Definition Expr.h:100
Function operator()(const std::string &funcName, const Params &params) const
Definition Expr.h:359
UnaryOperator operator()(C op, const R &rhs) const
Definition Expr.h:295
Variable operator()(V v) const
Definition Expr.h:139