Maestro 0.3.11
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
38{
39namespace qi = boost::spirit::qi;
40namespace ascii = boost::spirit::ascii;
41namespace phx = boost::phoenix;
42
44{
45 public:
46 virtual ~AbstractSyntaxTree() = default;
47
48 virtual double Eval() const
49 {
50 return 0;
51 }
52
53 virtual double Eval(const std::unordered_map<std::string, double> &variables) const
54 {
55 return 0;
56 }
57
58 protected:
59 AbstractSyntaxTree() = default;
64};
65
66typedef std::shared_ptr<AbstractSyntaxTree> AbstractSyntaxTreePtr;
67
68template <typename Expr> static AbstractSyntaxTreePtr Clone(Expr const &t)
69{
70 return std::make_shared<Expr>(t);
71}
72
73// for expressions (to be evaluated, typically those are values for parameters
74// for gates)
75
77{
78 public:
79 Constant(double value = 0) : value(value)
80 {
81 }
82
83 Constant(int value) : value(value)
84 {
85 }
86
87 Constant &operator=(int value)
88 {
89 this->value = value;
90 return *this;
91 }
92
93 Constant &operator=(double value)
94 {
95 this->value = value;
96 return *this;
97 }
98
99 double Eval() const override
100 {
101 return value;
102 }
103
104 double Eval(const std::unordered_map<std::string, double> &variables) const override
105 {
106 return value;
107 }
108
109 private:
110 double value;
111};
112
114{
115 template <typename> struct result
116 {
117 typedef Constant type;
118 };
119
120 template <typename C> Constant operator()(C op) const
121 {
122 return Constant(op);
123 }
124};
125
126inline phx::function<MakeConstantExpression> MakeConstant;
127
129{
130 public:
131 Variable(const std::string &value = "") : value(value)
132 {
133 }
134
135 Variable &operator=(int value)
136 {
137 this->value = value;
138 return *this;
139 }
140
141 double Eval() const override
142 {
143 return 0;
144 }
145
146 double Eval(const std::unordered_map<std::string, double> &variables) const override
147 {
148 auto it = variables.find(value);
149 if (it != variables.end())
150 return it->second;
151 else
152 throw std::invalid_argument("Variable not found: " + value);
153
154 return 0;
155 }
156
157 private:
158 std::string value;
159};
160
162{
163 template <typename> struct result
164 {
165 typedef Variable type;
166 };
167
168 template <typename V> Variable operator()(V v) const
169 {
170 return Variable(v);
171 }
172};
173
174inline phx::function<MakeVariableExpression> MakeVariable;
175
176// Operator codes are the source characters, with one exception: 'X' stands
177// for bitwise XOR. QASM2 spells exponentiation '^' and QASM3 spells it '**'
178// while giving '^' to bitwise XOR, so the two meanings cannot share a code -
179// '^' is kept for exponentiation (built by both the QASM2 '^' rule and the
180// '**' rule) and XOR gets its own. Which node is built is decided by the
181// grammar, per language version; see `factor2`/`expression` in Qasm.h.
183{
184 public:
185 template <typename L, typename R> BinaryOperator(char op, const L &left, const R &right) : op(op), left(Clone(left)), right(Clone(right))
186 {
187 }
188
189 double Eval() const override
190 {
191 switch (op)
192 {
193 case '+':
194 return left->Eval() + right->Eval();
195 case '-':
196 return left->Eval() - right->Eval();
197 case '*':
198 return left->Eval() * right->Eval();
199 case '/':
200 return left->Eval() / right->Eval();
201 case '^':
202 return pow(left->Eval(), right->Eval());
203 case 'X':
204 return static_cast<double>(AsInteger(left->Eval()) ^ AsInteger(right->Eval()));
205 default:
206 throw std::invalid_argument("Unknown binary operator");
207 }
208
209 return 0;
210 }
211
212 double Eval(const std::unordered_map<std::string, double> &variables) const override
213 {
214 switch (op)
215 {
216 case '+':
217 return left->Eval(variables) + right->Eval(variables);
218 case '-':
219 return left->Eval(variables) - right->Eval(variables);
220 case '*':
221 return left->Eval(variables) * right->Eval(variables);
222 case '/':
223 return left->Eval(variables) / right->Eval(variables);
224 case '^':
225 return pow(left->Eval(variables), right->Eval(variables));
226 case 'X':
227 return static_cast<double>(AsInteger(left->Eval(variables)) ^ AsInteger(right->Eval(variables)));
228 default:
229 throw std::invalid_argument("Unknown binary operator");
230 }
231
232 return 0;
233 }
234
235 private:
236 // Bitwise XOR is only defined on integers, but every value in this
237 // expression tree is a double. A non-integral or out-of-range operand is
238 // rejected by name rather than truncated: truncating would turn e.g.
239 // `rx(0.5 ^ 1)` into a silently wrong rotation angle, which is exactly the
240 // failure mode QASM3's '^' was introducing here in the first place.
241 static long long AsInteger(double value)
242 {
243 if (!std::isfinite(value))
244 throw std::invalid_argument("Bitwise XOR ('^') requires finite integer operands, got: " + std::to_string(value));
245
246 const double rounded = std::round(value);
247
248 if (std::abs(value - rounded) > 1e-9)
249 throw std::invalid_argument("Bitwise XOR ('^') requires integer operands, got: " + std::to_string(value));
250
251 // The magnitude bound keeps the cast below defined; anything near it is
252 // far outside the range of a meaningful gate parameter anyway.
253 if (std::abs(rounded) > 4.5e15)
254 throw std::invalid_argument("Bitwise XOR ('^') operand is out of the supported integer range: " + std::to_string(value));
255
256 return static_cast<long long>(rounded);
257 }
258
259 char op;
260 AbstractSyntaxTreePtr left, right;
261};
262
264{
265 template <typename, typename, typename> struct result
266 {
268 };
269
270 template <typename C, typename L, typename R> BinaryOperator operator()(C op, const L &lhs, const R &rhs) const
271 {
272 return BinaryOperator(op, lhs, rhs);
273 }
274};
275
276inline phx::function<MakeBinaryExpression> MakeBinary;
277
279{
280 public:
281 UnaryOperator() : op('+')
282 {
283 }
284
285 template <typename R> UnaryOperator(char op, const R &right) : op(op), right(Clone(right))
286 {
287 }
288
289 double Eval() const override
290 {
291 switch (op)
292 {
293 case '+':
294 return right->Eval();
295 case '-':
296 return -right->Eval();
297 default:
298 throw std::invalid_argument("Unknown unary operator");
299 }
300
301 return 0;
302 }
303
304 double Eval(const std::unordered_map<std::string, double> &variables) const override
305 {
306 switch (op)
307 {
308 case '+':
309 return right->Eval(variables);
310 case '-':
311 return -right->Eval(variables);
312 default:
313 throw std::invalid_argument("Unknown unary operator");
314 }
315 return 0;
316 }
317
318 private:
319 char op;
321};
322
324{
325 template <typename, typename> struct result
326 {
328 };
329
330 template <typename C, typename R> UnaryOperator operator()(C op, const R &rhs) const
331 {
332 return UnaryOperator(op, rhs);
333 }
334};
335
336inline phx::function<MakeUnaryExpression> MakeUnary;
337
339{
340 public:
341 template <typename F> Function(const std::string &func, const F &param) : func(func), param(Clone(param))
342 {
343 }
344
345 double Eval() const override
346 {
347 if (func == "sin")
348 return sin(param->Eval());
349 else if (func == "cos")
350 return cos(param->Eval());
351 else if (func == "tan")
352 return tan(param->Eval());
353 else if (func == "exp")
354 return exp(param->Eval());
355 else if (func == "ln")
356 return log(param->Eval());
357 else if (func == "sqrt")
358 return sqrt(param->Eval());
359
360 throw std::invalid_argument("Unknown function");
361
362 return 0;
363 }
364
365 double Eval(const std::unordered_map<std::string, double> &variables) const override
366 {
367 if (func == "sin")
368 return sin(param->Eval(variables));
369 else if (func == "cos")
370 return cos(param->Eval(variables));
371 else if (func == "tan")
372 return tan(param->Eval(variables));
373 else if (func == "exp")
374 return exp(param->Eval(variables));
375 else if (func == "ln")
376 return log(param->Eval(variables));
377 else if (func == "sqrt")
378 return sqrt(param->Eval(variables));
379
380 throw std::invalid_argument("Unknown function");
381
382 return 0;
383 }
384
385 private:
386 std::string func;
388};
389
391{
392 template <typename, typename, typename> struct result
393 {
394 typedef Function type;
395 };
396
397 template <typename Params> Function operator()(const std::string &funcName, const Params &params) const
398 {
399 return Function(funcName, params);
400 }
401};
402
403inline phx::function<MakeFunctionExpression> MakeFunction;
404
406{
407 public:
409 {
410 }
411
412 ~Expression() override
413 {
414 }
415
416 template <typename E> Expression(E const &e) : expr(Clone(e))
417 {
418 }
419
420 double Eval() const override
421 {
422 return expr->Eval();
423 }
424
425 double Eval(const std::unordered_map<std::string, double> &variables) const override
426 {
427 return expr->Eval(variables);
428 }
429
431 {
432 return e.expr;
433 }
434
435 private:
437};
438} // namespace qasm
439
440#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:53
virtual double Eval() const
Definition Expr.h:48
AbstractSyntaxTree & operator=(const AbstractSyntaxTree &)=default
AbstractSyntaxTree & operator=(AbstractSyntaxTree &&)=default
double Eval() const override
Definition Expr.h:189
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:212
BinaryOperator(char op, const L &left, const R &right)
Definition Expr.h:185
Constant(int value)
Definition Expr.h:83
double Eval() const override
Definition Expr.h:99
Constant(double value=0)
Definition Expr.h:79
Constant & operator=(int value)
Definition Expr.h:87
Constant & operator=(double value)
Definition Expr.h:93
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:104
~Expression() override
Definition Expr.h:412
friend AbstractSyntaxTreePtr Clone(Expression const &e)
Definition Expr.h:430
double Eval() const override
Definition Expr.h:420
Expression(E const &e)
Definition Expr.h:416
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:425
Function(const std::string &func, const F &param)
Definition Expr.h:341
double Eval() const override
Definition Expr.h:345
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:365
UnaryOperator(char op, const R &right)
Definition Expr.h:285
double Eval() const override
Definition Expr.h:289
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:304
Variable & operator=(int value)
Definition Expr.h:135
double Eval() const override
Definition Expr.h:141
Variable(const std::string &value="")
Definition Expr.h:131
double Eval(const std::unordered_map< std::string, double > &variables) const override
Definition Expr.h:146
phx::function< MakeFunctionExpression > MakeFunction
Definition Expr.h:403
phx::function< MakeUnaryExpression > MakeUnary
Definition Expr.h:336
phx::function< MakeBinaryExpression > MakeBinary
Definition Expr.h:276
static AbstractSyntaxTreePtr Clone(Expr const &t)
Definition Expr.h:68
std::shared_ptr< AbstractSyntaxTree > AbstractSyntaxTreePtr
Definition Expr.h:66
phx::function< MakeVariableExpression > MakeVariable
Definition Expr.h:174
phx::function< MakeConstantExpression > MakeConstant
Definition Expr.h:126
BinaryOperator operator()(C op, const L &lhs, const R &rhs) const
Definition Expr.h:270
Constant operator()(C op) const
Definition Expr.h:120
Function operator()(const std::string &funcName, const Params &params) const
Definition Expr.h:397
UnaryOperator operator()(C op, const R &rhs) const
Definition Expr.h:330
Variable operator()(V v) const
Definition Expr.h:168