• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

feihoo87 / waveforms / 6534953321

16 Oct 2023 02:19PM UTC coverage: 35.674% (-22.7%) from 58.421%
6534953321

push

github

feihoo87
fix Coveralls

5913 of 16575 relevant lines covered (35.67%)

3.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.51
/waveforms/qlisp/parse.py
1
from __future__ import annotations
9✔
2

3
import functools
9✔
4
import inspect
9✔
5
import math
9✔
6
import operator as op
9✔
7
from collections import defaultdict
9✔
8
from dataclasses import dataclass, field
9✔
9
from typing import Any, NamedTuple, Union
9✔
10

11
import numpy as np
9✔
12
from waveforms.waveform import Waveform, zero
9✔
13

14
from .base import ABCCompileConfigMixin, ADChannel, MultADChannel
9✔
15
from .tokenize import Expression, Number, Symbol, tokenize
9✔
16

17
################ Parsing
18

19

20
def parse(program):
9✔
21
    "Read a Scheme expression from a string."
22

23
    stack = []
9✔
24
    end = False
9✔
25

26
    for token in tokenize(program):
9✔
27
        if end:
9✔
28
            raise SyntaxError('unexpected EOF while reading')
×
29
        if token.type == '(':
9✔
30
            stack.append([])
9✔
31
        elif token.type == 'QUOTE':
9✔
32
            stack.append([Symbol('quote')])
9✔
33
        elif token.type == ')':
9✔
34
            try:
9✔
35
                s_expr = Expression(stack.pop())
9✔
36
            except IndexError:
×
37
                raise SyntaxError(
×
38
                    f'unexpected ) at L{token.line}C{token.column}')
39
            while True:
9✔
40
                if stack:
9✔
41
                    stack[-1].append(s_expr)
9✔
42
                    if Symbol('quote') == stack[-1][0]:
9✔
43
                        s_expr = Expression(stack.pop())
×
44
                    else:
45
                        break
9✔
46
                else:
47
                    end = s_expr
9✔
48
                    break
9✔
49
        else:
50
            s_expr = token.value
9✔
51
            while True:
9✔
52
                if stack:
9✔
53
                    stack[-1].append(s_expr)
9✔
54
                    if Symbol('quote') == stack[-1][0]:
9✔
55
                        s_expr = Expression(stack.pop())
9✔
56
                    else:
57
                        break
9✔
58
                elif Symbol('quote') == s_expr[0]:
9✔
59
                    end = s_expr
9✔
60
                    break
9✔
61
                else:
62
                    raise SyntaxError(
×
63
                        f'expected ( before L{token.line}C{token.column}')
64
    if not end:
9✔
65
        raise SyntaxError(f'expected ) after L{token.line}C{token.column}')
×
66
    return end
9✔
67

68

69
################ Environments
70

71

72
class Env(dict):
9✔
73
    "An environment: a dict of {'var':val} pairs, with an outer Env."
74

75
    def __init__(self, params=(), args=(), outer=None):
9✔
76
        self.update(zip(params, args))
9✔
77
        self.outer = outer
9✔
78

79
    def find(self, var):
9✔
80
        "Find the innermost Env where var appears."
81
        return self if (var in self) else self.outer.find(var)
9✔
82

83
    def lookup(self, name):
9✔
84
        "Find the value of a variable, starting in this Env."
85
        try:
9✔
86
            return self.find(name)[name]
9✔
87
        except:
×
88
            raise KeyError(f'can not find {name}')
×
89

90
    def set(self, name, value):
9✔
91
        "Set a variable to a value."
92
        self.find(name)[name] = value
9✔
93

94
    def assign(self, name, value):
9✔
95
        "Set a variable to a value."
96
        self[name] = value
9✔
97

98

99
################ Procedures
100

101

102
class Procedure():
9✔
103
    "A user-defined Scheme procedure."
104

105
    def __init__(self, params, body, env):
9✔
106
        self.params, self.body, self.env = params, body, env
9✔
107

108

109
class Gate(Procedure):
9✔
110
    "A quantum operation."
111

112
    def __init__(self, name, params, qubits, body, env, bindings=None):
9✔
113
        self.name = name
9✔
114
        self.params = params
9✔
115
        self.bindings = bindings
9✔
116
        self.qubits = qubits
9✔
117
        self.body = body
9✔
118
        self.env = env
9✔
119

120

121
class Channel(NamedTuple):
9✔
122
    qubits: tuple
9✔
123
    name: str
9✔
124

125

126
################ eval
127

128

129
class LispError(Exception):
9✔
130
    pass
9✔
131

132

133
def error(msg, *args):
9✔
134
    raise LispError(msg, *args)
×
135

136

137
def make_waveform(*args):
9✔
138
    from waveforms import Waveform, wave_eval
9✔
139

140
    if len(args) == 1 and isinstance(args[0], str):
9✔
141
        return wave_eval(args[0])
9✔
142
    elif len(args) == 1 and isinstance(args[0], Waveform):
9✔
143
        return args[0]
×
144
    else:
145
        try:
9✔
146
            return Waveform.fromlist(list(args))
9✔
147
        except:
×
148
            raise RuntimeError('invalid waveform')
×
149

150

151
def standard_env():
9✔
152
    "An environment with some Scheme standard procedures."
153
    env = Env()
9✔
154
    env.update(vars(math))  # sin, cos, sqrt, pi, ...
9✔
155
    env.update({
9✔
156
        '+': lambda *x: functools.reduce(op.add, x),
157
        '*': lambda *x: functools.reduce(op.mul, x),
158
        '&': lambda *x: functools.reduce(op.and_, x),
159
        '|': lambda *x: functools.reduce(op.or_, x),
160
        '-': op.sub, '/': op.truediv, '%': op.mod, '**': op.pow,
161
        '>': op.gt, '<': op.lt, '>=': op.ge, '<=': op.le, '==': op.eq,
162
        '>>': op.rshift, '<<': op.lshift, '^': op.xor,
163
        'pow':     op.pow,
164
        'abs':     abs,
165
        'append':  op.add,
166
        'car':     lambda x: x[0],
167
        'cdr':     lambda x: x[1:],
168
        'cons':    lambda x,y: (x,) + y,
169
        'is?':     op.is_,
170
        'eq?':     op.eq,
171
        'equal?':  op.eq,
172
        'length':  len,
173
        'list':    lambda *x: x,
174
        'list?':   lambda x: isinstance(x, tuple),
175
        'max':     max,
176
        'min':     min,
177
        'and':     all,
178
        'or':      any,
179
        'all':     all,
180
        'any':     any,
181
        'not':     op.not_,
182
        'null?':   lambda x: x == (),
183
        'number?': lambda x: isinstance(x, Number),
184
        'procedure?': lambda x: isinstance(x, Procedure) or callable(x),
185
        'round':   round,
186
        'symbol?': lambda x: isinstance(x, Symbol),
187
        'string?': lambda x: isinstance(x, str),
188
        'display!':print,
189
        'error!':  error,
190
        'waveform':make_waveform,
191
        'pi':      np.pi,
192
        'e':       np.e,
193
        'inf':     np.inf,
194
        '-inf':    -np.inf,
195
        'nil':     None,
196
        'null':    None,
197
        'None':    None,
198
        'true':    True,
199
        'false':   False,
200
        'True':    True,
201
        'False':   False,
202
    }) # yapf: disable
203
    return env
9✔
204

205

206
class Capture(NamedTuple):
9✔
207
    qubit: str
9✔
208
    cbit: int
9✔
209
    time: float
9✔
210
    signal: Union[str, tuple[str]]
9✔
211
    params: dict
9✔
212
    hardware: Union[ADChannel, MultADChannel] = None
9✔
213
    shift: float = 0
9✔
214

215

216
@dataclass
9✔
217
class Stack():
9✔
218
    cfg: ABCCompileConfigMixin = field(default_factory=dict)
9✔
219
    scopes: list[dict[str, Any]] = field(default_factory=lambda: [dict()])
9✔
220
    qlisp: list = field(default_factory=list)
9✔
221
    time: dict[str,
9✔
222
               float] = field(default_factory=lambda: defaultdict(lambda: 0))
223
    addressTable: dict = field(default_factory=dict)
9✔
224
    waveforms: dict[str, Waveform] = field(
9✔
225
        default_factory=lambda: defaultdict(zero))
226
    raw_waveforms: dict[tuple[str, ...], Waveform] = field(
9✔
227
        default_factory=lambda: defaultdict(zero))
228
    measures: dict[int, Capture] = field(default_factory=dict)
9✔
229
    phases_ext: dict[str, dict[Union[int, str], float]] = field(
9✔
230
        default_factory=lambda: defaultdict(lambda: defaultdict(lambda: 0)))
231
    biases: dict[str,
9✔
232
                 float] = field(default_factory=lambda: defaultdict(lambda: 0))
233
    end: float = 0
9✔
234
    _ret: list = field(default_factory=list)
9✔
235

236
    @property
9✔
237
    def channel(self):
9✔
238
        return self.raw_waveforms
×
239

240
    @property
9✔
241
    def phases(self):
9✔
242

243
        class D():
9✔
244
            __slots__ = ('ctx', )
9✔
245

246
            def __init__(self, ctx):
9✔
247
                self.ctx = ctx
9✔
248

249
            def __getitem__(self, qubit):
9✔
250
                return self.ctx.phases_ext[qubit][1]
×
251

252
            def __setitem__(self, qubit, phase):
9✔
253
                self.ctx.phases_ext[qubit][1] = phase
9✔
254

255
        return D(self)
9✔
256

257
    @property
9✔
258
    def params(self):
9✔
259
        return self.scopes[-1]
×
260

261
    @property
9✔
262
    def vars(self):
9✔
263
        return self.scopes[-2]
×
264

265
    @property
9✔
266
    def globals(self):
9✔
267
        return self.scopes[0]
×
268

269
    def qubit(self, q):
9✔
270
        return self.addressTable[q]
×
271

272
    def push(self, x):
9✔
273
        self._ret.append(x)
9✔
274

275
    def pop(self):
9✔
276
        return self._ret.pop()
9✔
277

278
    def pick(self):
9✔
279
        return self._ret[-1]
×
280

281

282
def eval_quote(exp, env, stack):
9✔
283
    (_, exp) = exp
9✔
284
    stack.push(exp)
9✔
285

286

287
def eval_if(exp, env, stack):
9✔
288
    (_, test, conseq, alt) = exp
×
289
    eval(test, env, stack)
×
290
    if stack.pop():
×
291
        eval(conseq, env, stack)
×
292
    else:
293
        eval(alt, env, stack)
×
294

295

296
def eval_cond(exp, env, stack):
9✔
297
    for (test, conseq) in exp[1:]:
×
298
        eval(test, env, stack)
×
299
        if stack.pop():
×
300
            eval(conseq, env, stack)
×
301
            break
×
302
    stack.push(None)
×
303

304

305
def eval_while(exp, env, stack):
9✔
306
    (_, test, body) = exp
9✔
307
    ret = None
9✔
308
    while True:
9✔
309
        eval(test, env, stack)
9✔
310
        if not stack.pop():
9✔
311
            break
9✔
312
        eval(body, env, stack)
9✔
313
        ret = stack.pop()
9✔
314
    stack.push(ret)
9✔
315

316

317
def eval_define(exp, env, stack):
9✔
318
    (_, var, exp) = exp
9✔
319
    eval(exp, env, stack)
9✔
320
    if not isinstance(var, Symbol):
9✔
321
        raise TypeError(f'var must be a symbol')
×
322
    env.assign(var.name, stack.pop())
9✔
323
    stack.push(None)
9✔
324

325

326
def eval_set(exp, env, stack):
9✔
327
    (_, var, exp) = exp
×
328
    eval(exp, env, stack)
×
329
    eval(var, env, stack)
×
330
    var = stack.pop()
×
331
    if not isinstance(var, Symbol):
×
332
        raise TypeError(f'var must be a symbol')
×
333
    env.set(var.name, stack.pop())
×
334
    stack.push(None)
×
335

336

337
def eval_setq(exp, env, stack):
9✔
338
    (_, var, exp) = exp
9✔
339
    if not isinstance(var, Symbol):
9✔
340
        raise TypeError(f'var must be a symbol')
×
341
    eval(exp, env, stack)
9✔
342
    env.set(var.name, stack.pop())
9✔
343
    stack.push(None)
9✔
344

345

346
def eval_lambda(exp, env, stack):
9✔
347
    (_, params, body) = exp
9✔
348
    for i, p in enumerate(params, start=1):
9✔
349
        if not isinstance(p, Symbol):
9✔
350
            raise TypeError(f'the {i} param must be a symbol')
×
351
    stack.push(Procedure([p.name for p in params], body, env))
9✔
352

353

354
def eval_defun(exp, env, stack):
9✔
355
    (_, var, params, *body) = exp
9✔
356
    if not isinstance(var, Symbol):
9✔
357
        raise TypeError(f'var must be a symbol')
×
358
    env.assign(
9✔
359
        var.name,
360
        Procedure([p.name for p in params],
361
                  Expression([Symbol('begin'), *body]), env))
362
    stack.push(None)
9✔
363

364

365
def eval_gate(exp, env, stack):
9✔
366
    (_, var, qubits, *body) = exp
9✔
367
    if isinstance(var, Expression):
9✔
368
        gate, *params = var
9✔
369
    elif isinstance(var, Symbol):
9✔
370
        gate = var
9✔
371
        params = []
9✔
372
    else:
373
        raise TypeError(f'var must be a symbol')
×
374
    env.assign(
9✔
375
        gate.name,
376
        Gate(gate.name, [p.name for p in params], [q.name for q in qubits],
377
             Expression([Symbol('begin'), *body]), env))
378
    stack.push(None)
9✔
379

380

381
def eval_begin(exp, env, stack):
9✔
382
    for x in exp[1:-1]:
9✔
383
        eval(x, env, stack)
9✔
384
        stack.pop()
9✔
385
    eval(exp[-1], env, stack)
9✔
386

387

388
def eval_let(exp, env, stack):
9✔
389
    (_, bindings, body) = exp
9✔
390
    let_env = Env(params=[], args=[], outer=env)
9✔
391
    for (var, val) in bindings:
9✔
392
        eval(val, env, stack)
9✔
393
        if not isinstance(var, Symbol):
9✔
394
            raise TypeError(f'var must be a symbol')
×
395
        let_env.assign(var.name, stack.pop())
9✔
396
    eval(body, let_env, stack)
9✔
397

398

399
def eval_letstar(exp, env, stack):
9✔
400
    (_, bindings, body) = exp
×
401
    letstar_env = Env(params=[], args=[], outer=env)
×
402
    for (var, val) in bindings:
×
403
        eval(val, letstar_env, stack)
×
404
        if not isinstance(var, Symbol):
×
405
            raise TypeError(f'var must be a symbol')
×
406
        letstar_env.assign(var.name, stack.pop())
×
407
    eval(body, letstar_env, stack)
×
408

409

410
def python_to_qlisp(exp):
9✔
411
    if isinstance(exp, (Expression, Channel)):
9✔
412
        return exp
9✔
413
    elif isinstance(exp, tuple):
9✔
414
        return Expression([python_to_qlisp(x) for x in exp])
9✔
415
    elif isinstance(exp, str):
9✔
416
        if len(exp) >= 2 and exp.startswith('"') and exp.endswith('"'):
9✔
417
            return exp[1:-1]
×
418
        else:
419
            return Symbol(exp)
9✔
420
    else:
421
        return exp
9✔
422

423

424
def get_ret(gen, env, stack):
9✔
425
    ret = yield from gen
9✔
426
    eval(python_to_qlisp(ret), env, stack)
9✔
427

428

429
def apply_gate(gate: Gate, args, env, stack):
9✔
430
    if gate.bindings is None and len(gate.params) > 0:
9✔
431
        bindings = dict(zip(gate.params, args))
9✔
432
        stack.push(
9✔
433
            Gate(gate.name, gate.params, gate.qubits, gate.body, gate.env,
434
                 bindings))
435
        return
9✔
436
    else:
437
        qubits = args
9✔
438

439
    if isinstance(gate.body, Expression):
9✔
440
        inner_env = Env(gate.qubits, qubits, gate.env)
9✔
441
        if gate.bindings is not None:
9✔
442
            inner_env.update(gate.bindings)
9✔
443
        eval(gate.body, inner_env, stack)
9✔
444
    else:
445
        pass
×
446

447

448
def apply(proc, args, env, stack):
9✔
449
    if isinstance(proc, Gate):
9✔
450
        apply_gate(proc, args, env, stack)
9✔
451
    elif isinstance(proc, Procedure):
9✔
452
        eval(proc.body, Env(proc.params, args, proc.env), stack)
9✔
453
    elif callable(proc):
9✔
454
        x = proc(*args)
9✔
455
        if inspect.isgenerator(x):
9✔
456
            for instruction in get_ret(x, env, stack):
9✔
457
                instruction = python_to_qlisp(instruction)
9✔
458
                if instruction[0].name.startswith('!'):
9✔
459
                    eval_instruction(instruction, env, stack)
9✔
460
                    # try:
461
                    #     cmd, target, *args = instruction
462
                    #     for a in reversed(args):
463
                    #         eval(a, env, stack)
464
                    #     args = [stack.pop() for _ in args]
465
                    #     if isinstance(target, Symbol):
466
                    #         target = target.name
467
                    #     execute(stack, cmd.name, target, *args)
468
                    # except:
469
                    #     raise Exception(f'bad instruction {instruction}')
470
                else:
471
                    eval(instruction, env, stack)
×
472
                    stack.pop()
×
473
        else:
474
            stack.push(x)
9✔
475
    else:
476
        raise TypeError('{} is not callable'.format(proc))
×
477

478

479
def eval_apply(exp, env, stack):
9✔
480
    (_, proc, args) = exp
9✔
481
    for exp in args[::-1]:
9✔
482
        eval(exp, env, stack)
9✔
483
    eval(proc, env, stack)
9✔
484
    proc = stack.pop()
9✔
485
    args = [stack.pop() for _ in args]
9✔
486
    apply(proc, args, env, stack)
9✔
487

488

489
def eval_channel(exp, env, stack):
9✔
490
    (_, *qubits, name) = exp
9✔
491
    if isinstance(name, Symbol):
9✔
492
        name = name.name
×
493
    qubits = tuple(q.name for q in qubits)
9✔
494
    stack.push(Channel(name, qubits))
9✔
495

496

497
def eval_instruction(instruction, env, stack):
9✔
498
    env = Env(params=[], args=[], outer=env)
9✔
499
    try:
9✔
500
        cmd, target, *args = instruction
9✔
501
        for a in reversed(args):
9✔
502
            eval(a, env, stack)
9✔
503
        args = [stack.pop() for _ in args]
9✔
504
        eval(target, env, stack)
9✔
505
        target = stack.pop()
9✔
506
        execute(stack, cmd.name, target, *args)
9✔
507
    except:
×
508
        raise Exception(f'bad instruction {instruction}')
×
509

510

511
__eval_table = {
9✔
512
    'quote': eval_quote,
513
    'if': eval_if,
514
    'cond': eval_cond,
515
    'while': eval_while,
516
    'define': eval_define,
517
    'defun': eval_defun,
518
    'gate': eval_gate,
519
    'set!': eval_set,
520
    'setq': eval_setq,
521
    'lambda': eval_lambda,
522
    'begin': eval_begin,
523
    'let': eval_let,
524
    'let*': eval_letstar,
525
    'apply': eval_apply,
526
    'channel': eval_channel,
527
}
528

529

530
def eval(x, env, stack):
9✔
531
    "Evaluate an expression in an environment."
532
    if isinstance(x, Symbol):  # variable reference
9✔
533
        stack.push(env.lookup(x.name))
9✔
534
    elif isinstance(x, Expression):
9✔
535
        if len(x) == 0:
9✔
536
            stack.push(None)
×
537
        elif isinstance(x[0], Symbol) and x[0].name.startswith('!'):
9✔
538
            eval_instruction(x, env, stack)
9✔
539
        elif isinstance(x[0], Symbol) and x[0].name in __eval_table:
9✔
540
            __eval_table[x[0].name](x, env, stack)
9✔
541
        else:
542
            eval(Expression([Symbol('apply'), x[0], x[1:]]), env, stack)
9✔
543
    else:  # constant literal
544
        stack.push(x)
9✔
545

546

547
################ virtual machine
548

549

550
def execute(stack, cmd, target, *args):
9✔
551
    if cmd == '!nop':
9✔
552
        pass
×
553
    elif cmd == '!set_waveform':
9✔
554
        stack.raw_waveforms[target] = args[0]
9✔
555
    elif cmd == '!add_waveform':
9✔
556
        stack.raw_waveforms[target] += args[0]
×
557
    elif cmd == '!set_phase':
9✔
558
        stack.phases[target] = args[0]
9✔
559
    else:
560
        pass
×
561
    stack.push(None)
9✔
562

563

564
################ Interaction: A REPL
565

566

567
def lispstr(exp):
9✔
568
    "Convert a Python object back into a Lisp-readable string."
569
    if isinstance(exp, Expression):
×
570
        return '(' + ' '.join(map(lispstr, exp)) + ')'
×
571
    else:
572
        return str(exp)
×
573

574

575
def repl(prompt='lis.py> '):
9✔
576
    "A prompt-read-eval-print loop."
577
    global_stack = Stack()
×
578
    global_env = standard_env()
×
579
    while True:
×
580
        eval(parse(input(prompt)), global_env, global_stack)
×
581
        val = global_stack.pop()
×
582
        if val is not None:
×
583
            print(lispstr(val))
×
584

585

586
if __name__ == "__main__":
9✔
587
    repl()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc