libqasm 0.6.6__cp312-cp312-macosx_11_0_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cqasm/v3x/semantic.py ADDED
@@ -0,0 +1,2367 @@
1
+ import functools
2
+ import struct
3
+ import cqasm.v3x.instruction
4
+ import cqasm.v3x.primitives
5
+ import cqasm.v3x.types
6
+ import cqasm.v3x.values
7
+
8
+
9
+ _typemap = {}
10
+
11
+
12
+ def _cbor_read_intlike(cbor, offset, info):
13
+ """Parses the additional information and reads any additional bytes it
14
+ specifies the existence of, and returns the encoded integer. offset
15
+ should point to the byte immediately following the initial byte. Returns
16
+ the encoded integer and the offset immediately following the object."""
17
+
18
+ # Info less than 24 is a shorthand for the integer itself.
19
+ if info < 24:
20
+ return info, offset
21
+
22
+ # 24 is 8-bit following the info byte.
23
+ if info == 24:
24
+ return cbor[offset], offset + 1
25
+
26
+ # 25 is 16-bit following the info byte.
27
+ if info == 25:
28
+ val, = struct.unpack('>H', cbor[offset:offset+2])
29
+ return val, offset + 2
30
+
31
+ # 26 is 32-bit following the info byte.
32
+ if info == 26:
33
+ val, = struct.unpack('>I', cbor[offset:offset+4])
34
+ return val, offset + 4
35
+
36
+ # 27 is 64-bit following the info byte.
37
+ if info == 27:
38
+ val, = struct.unpack('>Q', cbor[offset:offset+8])
39
+ return val, offset + 8
40
+
41
+ # Info greater than or equal to 28 is illegal. Note that 31 is used for
42
+ # indefinite lengths, so this must be checked prior to calling this
43
+ # method.
44
+ raise ValueError("invalid CBOR: illegal additional info for integer or object length")
45
+
46
+
47
+ def _sub_cbor_to_py(cbor, offset):
48
+ """Converts the CBOR object starting at cbor[offset] to its Python
49
+ representation for as far as tree-gen supports CBOR. Returns this Python
50
+ representation and the offset immediately following the CBOR representation
51
+ thereof. Supported types:
52
+
53
+ - 0: unsigned integer (int)
54
+ - 1: negative integer (int)
55
+ - 2: byte string (bytes)
56
+ - 3: UTF-8 string (str)
57
+ - 4: array (list)
58
+ - 5: map (dict)
59
+ - 6: semantic tag (ignored)
60
+ - 7.20: false (bool)
61
+ - 7.21: true (bool)
62
+ - 7.22: null (NoneType)
63
+ - 7.27: double-precision float (float)
64
+
65
+ Both definite-length and indefinite-length notation is supported for sized
66
+ objects (strings, arrays, maps). A ValueError is thrown if the CBOR is
67
+ invalid or contains unsupported structures."""
68
+
69
+ # Read the initial byte.
70
+ initial = cbor[offset]
71
+ typ = initial >> 5
72
+ info = initial & 0x1F
73
+ offset += 1
74
+
75
+ # Handle unsigned integer (0) and negative integer (1).
76
+ if typ <= 1:
77
+ value, offset = _cbor_read_intlike(cbor, offset, info)
78
+ if typ == 1:
79
+ value = -1 - value
80
+ return value, offset
81
+
82
+ # Handle byte string (2) and UTF-8 string (3).
83
+ if typ <= 3:
84
+
85
+ # Gather components of the string in here.
86
+ if info == 31:
87
+
88
+ # Handle indefinite length strings. These consist of a
89
+ # break-terminated (0xFF) list of definite-length strings of the
90
+ # same type.
91
+ value = []
92
+ while True:
93
+ sub_initial = cbor[offset]; offset += 1
94
+ if sub_initial == 0xFF:
95
+ break
96
+ sub_typ = sub_initial >> 5
97
+ sub_info = sub_initial & 0x1F
98
+ if sub_typ != typ:
99
+ raise ValueError('invalid CBOR: illegal indefinite-length string component')
100
+
101
+ # Seek past definite-length string component. The size in
102
+ # bytes is encoded as an integer.
103
+ size, offset = _cbor_read_intlike(cbor, offset, sub_info)
104
+ value.append(cbor[offset:offset + size])
105
+ offset += size
106
+ value = b''.join(value)
107
+
108
+ else:
109
+
110
+ # Handle definite-length strings. The size in bytes is encoded as
111
+ # an integer.
112
+ size, offset = _cbor_read_intlike(cbor, offset, info)
113
+ value = cbor[offset:offset + size]
114
+ offset += size
115
+
116
+ if typ == 3:
117
+ value = value.decode('UTF-8')
118
+ return value, offset
119
+
120
+ # Handle array (4) and map (5).
121
+ if typ <= 5:
122
+
123
+ # Create result container.
124
+ container = [] if typ == 4 else {}
125
+
126
+ # Handle indefinite length arrays and maps.
127
+ if info == 31:
128
+
129
+ # Read objects/object pairs until we encounter a break.
130
+ while cbor[offset] != 0xFF:
131
+ if typ == 4:
132
+ value, offset = _sub_cbor_to_py(cbor, offset)
133
+ container.append(value)
134
+ else:
135
+ key, offset = _sub_cbor_to_py(cbor, offset)
136
+ if not isinstance(key, str):
137
+ raise ValueError('invalid CBOR: map key is not a UTF-8 string')
138
+ value, offset = _sub_cbor_to_py(cbor, offset)
139
+ container[key] = value
140
+
141
+ # Seek past the break.
142
+ offset += 1
143
+
144
+ else:
145
+
146
+ # Handle definite-length arrays and maps. The amount of
147
+ # objects/object pairs is encoded as an integer.
148
+ size, offset = _cbor_read_intlike(cbor, offset, info)
149
+ for _ in range(size):
150
+ if typ == 4:
151
+ value, offset = _sub_cbor_to_py(cbor, offset)
152
+ container.append(value)
153
+ else:
154
+ key, offset = _sub_cbor_to_py(cbor, offset)
155
+ if not isinstance(key, str):
156
+ raise ValueError('invalid CBOR: map key is not a UTF-8 string')
157
+ value, offset = _sub_cbor_to_py(cbor, offset)
158
+ container[key] = value
159
+
160
+ return container, offset
161
+
162
+ # Handle semantic tags.
163
+ if typ == 6:
164
+
165
+ # We don't use semantic tags for anything, but ignoring them is
166
+ # legal and reading past them is easy enough.
167
+ _, offset = _cbor_read_intlike(cbor, offset, info)
168
+ return _sub_cbor_to_py(cbor, offset)
169
+
170
+ # Handle major type 7. Here, the type is defined by the additional info.
171
+ # Additional info 24 is reserved for having the type specified by the
172
+ # next byte, but all such values are unassigned.
173
+ if info == 20:
174
+ # false
175
+ return False, offset
176
+
177
+ if info == 21:
178
+ # true
179
+ return True, offset
180
+
181
+ if info == 22:
182
+ # null
183
+ return None, offset
184
+
185
+ if info == 23:
186
+ # Undefined value.
187
+ raise ValueError('invalid CBOR: undefined value is not supported')
188
+
189
+ if info == 25:
190
+ # Half-precision float.
191
+ raise ValueError('invalid CBOR: half-precision float is not supported')
192
+
193
+ if info == 26:
194
+ # Single-precision float.
195
+ raise ValueError('invalid CBOR: single-precision float is not supported')
196
+
197
+ if info == 27:
198
+ # Double-precision float.
199
+ value, = struct.unpack('>d', cbor[offset:offset+8])
200
+ return value, offset + 8
201
+
202
+ if info == 31:
203
+ # Break value used for indefinite-length objects.
204
+ raise ValueError('invalid CBOR: unexpected break')
205
+
206
+ raise ValueError('invalid CBOR: unknown type code')
207
+
208
+
209
+ def _cbor_to_py(cbor):
210
+ """Converts the given CBOR object (bytes) to its Python representation for
211
+ as far as tree-gen supports CBOR. Supported types:
212
+
213
+ - 0: unsigned integer (int)
214
+ - 1: negative integer (int)
215
+ - 2: byte string (bytes)
216
+ - 3: UTF-8 string (str)
217
+ - 4: array (list)
218
+ - 5: map (dict)
219
+ - 6: semantic tag (ignored)
220
+ - 7.20: false (bool)
221
+ - 7.21: true (bool)
222
+ - 7.22: null (NoneType)
223
+ - 7.27: double-precision float (float)
224
+
225
+ Both definite-length and indefinite-length notation is supported for sized
226
+ objects (strings, arrays, maps). A ValueError is thrown if the CBOR is
227
+ invalid or contains unsupported structures."""
228
+
229
+ value, length = _sub_cbor_to_py(cbor, 0)
230
+ if length < len(cbor):
231
+ raise ValueError('invalid CBOR: garbage at the end')
232
+ return value
233
+
234
+
235
+ class _Cbor(bytes):
236
+ """Marker class indicating that this bytes object represents CBOR."""
237
+ pass
238
+
239
+
240
+ def _cbor_write_intlike(value, major=0):
241
+ """Converts the given integer to its minimal representation in CBOR. The
242
+ major code can be overridden to write lengths for strings, arrays, and
243
+ maps."""
244
+
245
+ # Negative integers use major code 1.
246
+ if value < 0:
247
+ major = 1
248
+ value = -1 - value
249
+ initial = major << 5
250
+
251
+ # Use the minimal representation.
252
+ if value < 24:
253
+ return struct.pack('>B', initial | value)
254
+ if value < 0x100:
255
+ return struct.pack('>BB', initial | 24, value)
256
+ if value < 0x10000:
257
+ return struct.pack('>BH', initial | 25, value)
258
+ if value < 0x100000000:
259
+ return struct.pack('>BI', initial | 26, value)
260
+ if value < 0x10000000000000000:
261
+ return struct.pack('>BQ', initial | 27, value)
262
+
263
+ raise ValueError('integer too large for CBOR (bigint not supported)')
264
+
265
+
266
+ def _py_to_cbor(value, type_converter=None):
267
+ """Inverse of _cbor_to_py(). type_converter optionally specifies a function
268
+ that takes a value and either converts it to a primitive for serialization,
269
+ converts it to a _Cbor object manually, or raises a TypeError if no
270
+ conversion is known. If no type_converter is specified, a TypeError is
271
+ raised in all cases the type_converter would otherwise be called. The cbor
272
+ serialization is returned using a _Cbor object, which is just a marker class
273
+ behaving just like bytes."""
274
+ if isinstance(value, _Cbor):
275
+ return value
276
+
277
+ if isinstance(value, int):
278
+ return _Cbor(_cbor_write_intlike(value))
279
+
280
+ if isinstance(value, float):
281
+ return _Cbor(struct.pack('>Bd', 0xFB, value))
282
+
283
+ if isinstance(value, str):
284
+ value = value.encode('UTF-8')
285
+ return _Cbor(_cbor_write_intlike(len(value), 3) + value)
286
+
287
+ if isinstance(value, bytes):
288
+ return _Cbor(_cbor_write_intlike(len(value), 2) + value)
289
+
290
+ if value is False:
291
+ return _Cbor(b'\xF4')
292
+
293
+ if value is True:
294
+ return _Cbor(b'\xF5')
295
+
296
+ if value is None:
297
+ return _Cbor(b'\xF6')
298
+
299
+ if isinstance(value, (list, tuple)):
300
+ cbor = [_cbor_write_intlike(len(value), 4)]
301
+ for val in value:
302
+ cbor.append(_py_to_cbor(val, type_converter))
303
+ return _Cbor(b''.join(cbor))
304
+
305
+ if isinstance(value, dict):
306
+ cbor = [_cbor_write_intlike(len(value), 5)]
307
+ for key, val in sorted(value.items()):
308
+ if not isinstance(key, str):
309
+ raise TypeError('dict keys must be strings')
310
+ cbor.append(_py_to_cbor(key, type_converter))
311
+ cbor.append(_py_to_cbor(val, type_converter))
312
+ return _Cbor(b''.join(cbor))
313
+
314
+ if type_converter is not None:
315
+ return _py_to_cbor(type_converter(value))
316
+
317
+ raise TypeError('unsupported type for conversion to cbor: %r' % (value,))
318
+
319
+
320
+ class NotWellFormed(ValueError):
321
+ """Exception class for well-formedness checks."""
322
+
323
+ def __init__(self, msg):
324
+ super().__init__('not well-formed: ' + str(msg))
325
+
326
+
327
+ class Node(object):
328
+ """Base class for nodes."""
329
+
330
+ __slots__ = ['_annot']
331
+
332
+ def __init__(self):
333
+ super().__init__()
334
+ self._annot = {}
335
+
336
+ def __getitem__(self, key):
337
+ """Returns the annotation object with the specified key, or raises
338
+ KeyError if not found."""
339
+ if not isinstance(key, str):
340
+ raise TypeError('indexing a node with something other than an '
341
+ 'annotation key string')
342
+ return self._annot[key]
343
+
344
+ def __setitem__(self, key, val):
345
+ """Assigns the annotation object with the specified key."""
346
+ if not isinstance(key, str):
347
+ raise TypeError('indexing a node with something other than an '
348
+ 'annotation key string')
349
+ self._annot[key] = val
350
+
351
+ def __delitem__(self, key):
352
+ """Deletes the annotation object with the specified key."""
353
+ if not isinstance(key, str):
354
+ raise TypeError('indexing a node with something other than an '
355
+ 'annotation key string')
356
+ del self._annot[key]
357
+
358
+ def __contains__(self, key):
359
+ """Returns whether an annotation exists for the specified key."""
360
+ return key in self._annot
361
+
362
+ @staticmethod
363
+ def find_reachable(self, id_map=None):
364
+ """Returns a dictionary mapping Python id() values to stable sequence
365
+ numbers for all nodes in the tree rooted at this node. If id_map is
366
+ specified, found nodes are appended to it. Note that this is overridden
367
+ by the actual node class implementations; this base function does very
368
+ little."""
369
+ if id_map is None:
370
+ id_map = {}
371
+ return id_map
372
+
373
+ def check_complete(self, id_map=None):
374
+ """Raises NotWellFormed if the tree rooted at this node is not
375
+ well-formed. If id_map is specified, this tree is only a subtree in the
376
+ context of a larger tree, and id_map must be a dict mapping from Python
377
+ id() codes to tree indices for all reachable nodes. Note that this is
378
+ overridden by the actual node class implementations; this base function
379
+ always raises an exception."""
380
+ raise NotWellFormed('found node of abstract type ' + type(self).__name__)
381
+
382
+ def check_well_formed(self):
383
+ """Checks whether the tree starting at this node is well-formed. That
384
+ is:
385
+
386
+ - all One, Link, and Many edges have (at least) one entry;
387
+ - all the One entries internally stored by Any/Many have an entry;
388
+ - all Link and filled OptLink nodes link to a node that's reachable
389
+ from this node;
390
+ - the nodes referred to be One/Maybe only appear once in the tree
391
+ (except through links).
392
+
393
+ If it isn't well-formed, a NotWellFormed is thrown."""
394
+ self.check_complete()
395
+
396
+ def is_well_formed(self):
397
+ """Returns whether the tree starting at this node is well-formed. That
398
+ is:
399
+
400
+ - all One, Link, and Many edges have (at least) one entry;
401
+ - all the One entries internally stored by Any/Many have an entry;
402
+ - all Link and filled OptLink nodes link to a node that's reachable
403
+ from this node;
404
+ - the nodes referred to be One/Maybe only appear once in the tree
405
+ (except through links)."""
406
+ try:
407
+ self.check_well_formed()
408
+ return True
409
+ except NotWellFormed:
410
+ return False
411
+
412
+ def copy(self):
413
+ """Returns a shallow copy of this node. Note that this is overridden by
414
+ the actual node class implementations; this base function always raises
415
+ an exception."""
416
+ raise TypeError('can\'t copy node of abstract type ' + type(self).__name__)
417
+
418
+ def clone(self):
419
+ """Returns a deep copy of this node. Note that this is overridden by
420
+ the actual node class implementations; this base function always raises
421
+ an exception."""
422
+ raise TypeError('can\'t clone node of abstract type ' + type(self).__name__)
423
+
424
+ @classmethod
425
+ def deserialize(cls, cbor):
426
+ """Attempts to deserialize the given cbor object (either as bytes or as
427
+ its Python primitive representation) into a node of this type."""
428
+ if isinstance(cbor, bytes):
429
+ cbor = _cbor_to_py(cbor)
430
+ seq_to_ob = {}
431
+ links = []
432
+ root = cls._deserialize(cbor, seq_to_ob, links)
433
+ for link_setter, seq in links:
434
+ ob = seq_to_ob.get(seq, None)
435
+ if ob is None:
436
+ raise ValueError('found link to nonexistent object')
437
+ link_setter(ob)
438
+ return root
439
+
440
+ def serialize(self):
441
+ """Serializes this node into its cbor representation in the form of a
442
+ bytes object."""
443
+ id_map = self.find_reachable()
444
+ self.check_complete(id_map)
445
+ return _py_to_cbor(self._serialize(id_map))
446
+
447
+ @staticmethod
448
+ def _deserialize(cbor, seq_to_ob, links):
449
+ if not isinstance(cbor, dict):
450
+ raise TypeError('node description object must be a dict')
451
+ typ = cbor.get('@t', None)
452
+ if typ is None:
453
+ raise ValueError('type (@t) field is missing from node serialization')
454
+ node_type = _typemap.get(cbor.get('@t'), None)
455
+ if node_type is None:
456
+ raise ValueError('unknown node type (@t): ' + str(cbor.get('@t')))
457
+ return node_type._deserialize(cbor, seq_to_ob, links)
458
+
459
+
460
+ @functools.total_ordering
461
+ class _Multiple(object):
462
+ """Base class for the Any* and Many* edge helper classes. Inheriting
463
+ classes must set the class constant _T to the node type they are made
464
+ for."""
465
+
466
+ __slots__ = ['_l']
467
+
468
+ def __init__(self, *args, **kwargs):
469
+ super().__init__()
470
+ self._l = list(*args, **kwargs)
471
+ for idx, val in enumerate(self._l):
472
+ if not isinstance(val, self._T):
473
+ raise TypeError(
474
+ 'object {!r} at index {:d} is not an instance of {!r}'
475
+ .format(val, idx, self._T))
476
+
477
+ def __repr__(self):
478
+ return '{}({!r})'.format(type(self).__name__, self._l)
479
+
480
+ def clone(self):
481
+ return self.__class__(map(lambda node: node.clone(), self._l))
482
+
483
+ def __len__(self):
484
+ return len(self._l)
485
+
486
+ def __getitem__(self, idx):
487
+ return self._l[idx]
488
+
489
+ def __setitem__(self, idx, val):
490
+ if not isinstance(val, self._T):
491
+ raise TypeError(
492
+ 'object {!r} is not an instance of {!r}'
493
+ .format(val, idx, self._T))
494
+ self._l[idx] = val
495
+
496
+ def __delitem__(self, idx):
497
+ del self._l[idx]
498
+
499
+ def __iter__(self):
500
+ return iter(self._l)
501
+
502
+ def __reversed__(self):
503
+ return reversed(self._l)
504
+
505
+ def __contains__(self, val):
506
+ return val in self._l
507
+
508
+ def append(self, val):
509
+ if not isinstance(val, self._T):
510
+ raise TypeError(
511
+ 'object {!r} is not an instance of {!r}'
512
+ .format(val, self._T))
513
+ self._l.append(val)
514
+
515
+ def extend(self, iterable):
516
+ for val in iterable:
517
+ self.append(val)
518
+
519
+ def insert(self, idx, val):
520
+ if not isinstance(val, self._T):
521
+ raise TypeError(
522
+ 'object {!r} is not an instance of {!r}'
523
+ .format(val, self._T))
524
+ self._l.insert(idx, val)
525
+
526
+ def remote(self, val):
527
+ self._l.remove(val)
528
+
529
+ def pop(self, idx=-1):
530
+ return self._l.pop(idx)
531
+
532
+ def clear(self):
533
+ self._l.clear()
534
+
535
+ def idx(self, val, start=0, end=-1):
536
+ return self._l.idx(val, start, end)
537
+
538
+ def count(self, val):
539
+ return self._l.count(val)
540
+
541
+ def sort(self, key=None, reverse=False):
542
+ self._l.sort(key=key, reverse=reverse)
543
+
544
+ def reverse(self):
545
+ self._l.reverse()
546
+
547
+ def copy(self):
548
+ return self.__class__(self)
549
+
550
+ def __eq__(self, other):
551
+ if not isinstance(other, _Multiple):
552
+ return False
553
+ return self._l == other._l
554
+
555
+ def __lt__(self, other):
556
+ return self._l < other._l
557
+
558
+ def __iadd__(self, other):
559
+ self.extend(other)
560
+
561
+ def __add__(self, other):
562
+ copy = self.copy()
563
+ copy += other
564
+ return copy
565
+
566
+ def __imul__(self, other):
567
+ self._l *= other
568
+
569
+ def __mul__(self, other):
570
+ copy = self.copy()
571
+ copy *= other
572
+ return copy
573
+
574
+ def __rmul__(self, other):
575
+ copy = self.copy()
576
+ copy *= other
577
+ return copy
578
+
579
+
580
+ class MultiNode(_Multiple):
581
+ """Wrapper for an edge with multiple Node objects."""
582
+
583
+ _T = Node
584
+
585
+
586
+ def _cloned(obj):
587
+ """Attempts to clone the given object by calling its clone() method, if it
588
+ has one."""
589
+ if hasattr(obj, 'clone'):
590
+ return obj.clone()
591
+ return obj
592
+
593
+
594
+ class Annotated(Node):
595
+ """Represents a node that carries annotation data."""
596
+
597
+ __slots__ = [
598
+ '_attr_annotations',
599
+ ]
600
+
601
+ def __init__(
602
+ self,
603
+ annotations=None,
604
+ ):
605
+ super().__init__()
606
+ self.annotations = annotations
607
+
608
+ @property
609
+ def annotations(self):
610
+ return self._attr_annotations
611
+
612
+ @annotations.setter
613
+ def annotations(self, val):
614
+ if val is None:
615
+ del self.annotations
616
+ return
617
+ if not isinstance(val, MultiAnnotationData):
618
+ # Try to "typecast" if this isn't an obvious mistake.
619
+ if isinstance(val, Node):
620
+ raise TypeError('annotations must be of type MultiAnnotationData')
621
+ val = MultiAnnotationData(val)
622
+ self._attr_annotations = val
623
+
624
+ @annotations.deleter
625
+ def annotations(self):
626
+ self._attr_annotations = MultiAnnotationData()
627
+
628
+ @staticmethod
629
+ def _deserialize(cbor, seq_to_ob, links):
630
+ """Attempts to deserialize the given cbor object (in Python primitive
631
+ representation) into a node of this type. All (sub)nodes are added to
632
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
633
+ registered in the links list by means of a two-tuple of the setter
634
+ function for the link field and the sequence number of the target node.
635
+ """
636
+ if not isinstance(cbor, dict):
637
+ raise TypeError('node description object must be a dict')
638
+ typ = cbor.get('@t', None)
639
+ if typ is None:
640
+ raise ValueError('type (@t) field is missing from node serialization')
641
+ if typ == 'Variable':
642
+ return Variable._deserialize(cbor, seq_to_ob, links)
643
+ if typ == 'Instruction':
644
+ return Instruction._deserialize(cbor, seq_to_ob, links)
645
+ raise ValueError('unknown or unexpected type (@t) found in node serialization')
646
+
647
+ def _serialize(self, id_map):
648
+ """Serializes this node to the Python primitive representation of its
649
+ CBOR serialization. The tree that the node belongs to must be
650
+ well-formed. id_map must match Python id() calls for all nodes to unique
651
+ integers, to use for the sequence number representation of links."""
652
+ cbor = {'@i': id_map[id(self)], '@t': 'Annotated'}
653
+
654
+ # Serialize the annotations field.
655
+ field = {'@T': '*'}
656
+ lst = []
657
+ for el in self._attr_annotations:
658
+ el = el._serialize(id_map)
659
+ el['@T'] = '1'
660
+ lst.append(el)
661
+ field['@d'] = lst
662
+ cbor['annotations'] = field
663
+
664
+ # Serialize annotations.
665
+ for key, val in self._annot.items():
666
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
667
+
668
+ return cbor
669
+
670
+
671
+ class MultiAnnotated(_Multiple):
672
+ """Wrapper for an edge with multiple Annotated objects."""
673
+
674
+ _T = Annotated
675
+
676
+
677
+ _typemap['Annotated'] = Annotated
678
+
679
+ class AnnotationData(Node):
680
+ """Represents an annotation."""
681
+
682
+ __slots__ = [
683
+ '_attr_interface',
684
+ '_attr_operation',
685
+ '_attr_operands',
686
+ ]
687
+
688
+ def __init__(
689
+ self,
690
+ interface=None,
691
+ operation=None,
692
+ operands=None,
693
+ ):
694
+ super().__init__()
695
+ self.interface = interface
696
+ self.operation = operation
697
+ self.operands = operands
698
+
699
+ @property
700
+ def interface(self):
701
+ """The interface this annotation is intended for. If a target doesn't
702
+ support an interface, it should silently ignore the annotation."""
703
+ return self._attr_interface
704
+
705
+ @interface.setter
706
+ def interface(self, val):
707
+ if val is None:
708
+ del self.interface
709
+ return
710
+ if not isinstance(val, cqasm.v3x.primitives.Str):
711
+ # Try to "typecast" if this isn't an obvious mistake.
712
+ if isinstance(val, Node):
713
+ raise TypeError('interface must be of type cqasm.v3x.primitives.Str')
714
+ val = cqasm.v3x.primitives.Str(val)
715
+ self._attr_interface = val
716
+
717
+ @interface.deleter
718
+ def interface(self):
719
+ self._attr_interface = cqasm.v3x.primitives.Str()
720
+
721
+ @property
722
+ def operation(self):
723
+ """The operation within the interface that this annotation is intended
724
+ for. If a target supports the corresponding interface but not the
725
+ operation, it should throw an error."""
726
+ return self._attr_operation
727
+
728
+ @operation.setter
729
+ def operation(self, val):
730
+ if val is None:
731
+ del self.operation
732
+ return
733
+ if not isinstance(val, cqasm.v3x.primitives.Str):
734
+ # Try to "typecast" if this isn't an obvious mistake.
735
+ if isinstance(val, Node):
736
+ raise TypeError('operation must be of type cqasm.v3x.primitives.Str')
737
+ val = cqasm.v3x.primitives.Str(val)
738
+ self._attr_operation = val
739
+
740
+ @operation.deleter
741
+ def operation(self):
742
+ self._attr_operation = cqasm.v3x.primitives.Str()
743
+
744
+ @property
745
+ def operands(self):
746
+ """Any operands attached to the annotation."""
747
+ return self._attr_operands
748
+
749
+ @operands.setter
750
+ def operands(self, val):
751
+ if val is None:
752
+ del self.operands
753
+ return
754
+ if not isinstance(val, cqasm.v3x.values.MultiNode):
755
+ # Try to "typecast" if this isn't an obvious mistake.
756
+ if isinstance(val, Node):
757
+ raise TypeError('operands must be of type cqasm.v3x.values.MultiNode')
758
+ val = cqasm.v3x.values.MultiNode(val)
759
+ self._attr_operands = val
760
+
761
+ @operands.deleter
762
+ def operands(self):
763
+ self._attr_operands = cqasm.v3x.values.MultiNode()
764
+
765
+ def __eq__(self, other):
766
+ """Equality operator. Ignores annotations!"""
767
+ if not isinstance(other, AnnotationData):
768
+ return False
769
+ if self.interface != other.interface:
770
+ return False
771
+ if self.operation != other.operation:
772
+ return False
773
+ if self.operands != other.operands:
774
+ return False
775
+ return True
776
+
777
+ def dump(self, indent=0, annotations=None, links=1):
778
+ """Returns a debug representation of this tree as a multiline string.
779
+ indent is the number of double spaces prefixed before every line.
780
+ annotations, if specified, must be a set-like object containing the key
781
+ strings of the annotations that are to be printed. links specifies the
782
+ maximum link recursion depth."""
783
+ s = [' '*indent]
784
+ s.append('AnnotationData(')
785
+ if annotations is None:
786
+ annotations = []
787
+ for key in annotations:
788
+ if key in self:
789
+ s.append(' # {}: {}'.format(key, self[key]))
790
+ s.append('\n')
791
+ indent += 1
792
+ s.append(' '*indent)
793
+ s.append('interface: ')
794
+ s.append(str(self.interface) + '\n')
795
+ s.append(' '*indent)
796
+ s.append('operation: ')
797
+ s.append(str(self.operation) + '\n')
798
+ s.append(' '*indent)
799
+ s.append('operands: ')
800
+ if not self.operands:
801
+ s.append('-\n')
802
+ else:
803
+ s.append('[\n')
804
+ for child in self.operands:
805
+ s.append(child.dump(indent + 1, annotations, links) + '\n')
806
+ s.append(' '*indent + ']\n')
807
+ indent -= 1
808
+ s.append(' '*indent)
809
+ s.append(')')
810
+ return ''.join(s)
811
+
812
+ __str__ = dump
813
+ __repr__ = dump
814
+
815
+ def find_reachable(self, id_map=None):
816
+ """Returns a dictionary mapping Python id() values to stable sequence
817
+ numbers for all nodes in the tree rooted at this node. If id_map is
818
+ specified, found nodes are appended to it."""
819
+ if id_map is None:
820
+ id_map = {}
821
+ if id(self) in id_map:
822
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
823
+ id_map[id(self)] = len(id_map)
824
+ for el in self._attr_operands:
825
+ el.find_reachable(id_map)
826
+ return id_map
827
+
828
+ def check_complete(self, id_map=None):
829
+ """Raises NotWellFormed if the tree rooted at this node is not
830
+ well-formed. If id_map is specified, this tree is only a subtree in the
831
+ context of a larger tree, and id_map must be a dict mapping from Python
832
+ id() codes to tree indices for all reachable nodes."""
833
+ if id_map is None:
834
+ id_map = self.find_reachable()
835
+ for child in self._attr_operands:
836
+ child.check_complete(id_map)
837
+
838
+ def copy(self):
839
+ """Returns a shallow copy of this node."""
840
+ return AnnotationData(
841
+ interface=self._attr_interface,
842
+ operation=self._attr_operation,
843
+ operands=self._attr_operands.copy()
844
+ )
845
+
846
+ def clone(self):
847
+ """Returns a deep copy of this node. This mimics the C++ interface,
848
+ deficiencies with links included; that is, links always point to the
849
+ original tree. If you're not cloning a subtree in a context where this
850
+ is the desired behavior, you may want to use the copy.deepcopy() from
851
+ the stdlib instead, which should copy links correctly."""
852
+ return AnnotationData(
853
+ interface=_cloned(self._attr_interface),
854
+ operation=_cloned(self._attr_operation),
855
+ operands=_cloned(self._attr_operands)
856
+ )
857
+
858
+ @staticmethod
859
+ def _deserialize(cbor, seq_to_ob, links):
860
+ """Attempts to deserialize the given cbor object (in Python primitive
861
+ representation) into a node of this type. All (sub)nodes are added to
862
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
863
+ registered in the links list by means of a two-tuple of the setter
864
+ function for the link field and the sequence number of the target node.
865
+ """
866
+ if not isinstance(cbor, dict):
867
+ raise TypeError('node description object must be a dict')
868
+ typ = cbor.get('@t', None)
869
+ if typ is None:
870
+ raise ValueError('type (@t) field is missing from node serialization')
871
+ if typ != 'AnnotationData':
872
+ raise ValueError('found node serialization for ' + typ + ', but expected AnnotationData')
873
+
874
+ # Deserialize the interface field.
875
+ field = cbor.get('interface', None)
876
+ if not isinstance(field, dict):
877
+ raise ValueError('missing or invalid serialization of field interface')
878
+ if hasattr(cqasm.v3x.primitives.Str, 'deserialize_cbor'):
879
+ f_interface = cqasm.v3x.primitives.Str.deserialize_cbor(field)
880
+ else:
881
+ f_interface = cqasm.v3x.primitives.deserialize(cqasm.v3x.primitives.Str, field)
882
+
883
+ # Deserialize the operation field.
884
+ field = cbor.get('operation', None)
885
+ if not isinstance(field, dict):
886
+ raise ValueError('missing or invalid serialization of field operation')
887
+ if hasattr(cqasm.v3x.primitives.Str, 'deserialize_cbor'):
888
+ f_operation = cqasm.v3x.primitives.Str.deserialize_cbor(field)
889
+ else:
890
+ f_operation = cqasm.v3x.primitives.deserialize(cqasm.v3x.primitives.Str, field)
891
+
892
+ # Deserialize the operands field.
893
+ field = cbor.get('operands', None)
894
+ if not isinstance(field, dict):
895
+ raise ValueError('missing or invalid serialization of field operands')
896
+ if field.get('@T') != '*':
897
+ raise ValueError('unexpected edge type for field operands')
898
+ data = field.get('@d', None)
899
+ if not isinstance(data, list):
900
+ raise ValueError('missing serialization of Any/Many contents')
901
+ f_operands = cqasm.v3x.values.MultiNode()
902
+ for element in data:
903
+ if element.get('@T') != '1':
904
+ raise ValueError('unexpected edge type for Any/Many element')
905
+ f_operands.append(cqasm.v3x.values.Node._deserialize(element, seq_to_ob, links))
906
+
907
+ # Construct the AnnotationData node.
908
+ node = AnnotationData(f_interface, f_operation, f_operands)
909
+
910
+ # Deserialize annotations.
911
+ for key, val in cbor.items():
912
+ if not (key.startswith('{') and key.endswith('}')):
913
+ continue
914
+ key = key[1:-1]
915
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
916
+
917
+ # Register node in sequence number lookup.
918
+ seq = cbor.get('@i', None)
919
+ if not isinstance(seq, int):
920
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
921
+ if seq in seq_to_ob:
922
+ raise ValueError('duplicate sequence number %d' % seq)
923
+ seq_to_ob[seq] = node
924
+
925
+ return node
926
+
927
+ def _serialize(self, id_map):
928
+ """Serializes this node to the Python primitive representation of its
929
+ CBOR serialization. The tree that the node belongs to must be
930
+ well-formed. id_map must match Python id() calls for all nodes to unique
931
+ integers, to use for the sequence number representation of links."""
932
+ cbor = {'@i': id_map[id(self)], '@t': 'AnnotationData'}
933
+
934
+ # Serialize the interface field.
935
+ if hasattr(self._attr_interface, 'serialize_cbor'):
936
+ cbor['interface'] = self._attr_interface.serialize_cbor()
937
+ else:
938
+ cbor['interface'] = cqasm.v3x.primitives.serialize(cqasm.v3x.primitives.Str, self._attr_interface)
939
+
940
+ # Serialize the operation field.
941
+ if hasattr(self._attr_operation, 'serialize_cbor'):
942
+ cbor['operation'] = self._attr_operation.serialize_cbor()
943
+ else:
944
+ cbor['operation'] = cqasm.v3x.primitives.serialize(cqasm.v3x.primitives.Str, self._attr_operation)
945
+
946
+ # Serialize the operands field.
947
+ field = {'@T': '*'}
948
+ lst = []
949
+ for el in self._attr_operands:
950
+ el = el._serialize(id_map)
951
+ el['@T'] = '1'
952
+ lst.append(el)
953
+ field['@d'] = lst
954
+ cbor['operands'] = field
955
+
956
+ # Serialize annotations.
957
+ for key, val in self._annot.items():
958
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
959
+
960
+ return cbor
961
+
962
+
963
+ class MultiAnnotationData(_Multiple):
964
+ """Wrapper for an edge with multiple AnnotationData objects."""
965
+
966
+ _T = AnnotationData
967
+
968
+
969
+ _typemap['AnnotationData'] = AnnotationData
970
+
971
+ class Block(Node):
972
+ __slots__ = [
973
+ '_attr_statements',
974
+ ]
975
+
976
+ def __init__(
977
+ self,
978
+ statements=None,
979
+ ):
980
+ super().__init__()
981
+ self.statements = statements
982
+
983
+ @property
984
+ def statements(self):
985
+ return self._attr_statements
986
+
987
+ @statements.setter
988
+ def statements(self, val):
989
+ if val is None:
990
+ del self.statements
991
+ return
992
+ if not isinstance(val, MultiStatement):
993
+ # Try to "typecast" if this isn't an obvious mistake.
994
+ if isinstance(val, Node):
995
+ raise TypeError('statements must be of type MultiStatement')
996
+ val = MultiStatement(val)
997
+ self._attr_statements = val
998
+
999
+ @statements.deleter
1000
+ def statements(self):
1001
+ self._attr_statements = MultiStatement()
1002
+
1003
+ def __eq__(self, other):
1004
+ """Equality operator. Ignores annotations!"""
1005
+ if not isinstance(other, Block):
1006
+ return False
1007
+ if self.statements != other.statements:
1008
+ return False
1009
+ return True
1010
+
1011
+ def dump(self, indent=0, annotations=None, links=1):
1012
+ """Returns a debug representation of this tree as a multiline string.
1013
+ indent is the number of double spaces prefixed before every line.
1014
+ annotations, if specified, must be a set-like object containing the key
1015
+ strings of the annotations that are to be printed. links specifies the
1016
+ maximum link recursion depth."""
1017
+ s = [' '*indent]
1018
+ s.append('Block(')
1019
+ if annotations is None:
1020
+ annotations = []
1021
+ for key in annotations:
1022
+ if key in self:
1023
+ s.append(' # {}: {}'.format(key, self[key]))
1024
+ s.append('\n')
1025
+ indent += 1
1026
+ s.append(' '*indent)
1027
+ s.append('statements: ')
1028
+ if not self.statements:
1029
+ s.append('-\n')
1030
+ else:
1031
+ s.append('[\n')
1032
+ for child in self.statements:
1033
+ s.append(child.dump(indent + 1, annotations, links) + '\n')
1034
+ s.append(' '*indent + ']\n')
1035
+ indent -= 1
1036
+ s.append(' '*indent)
1037
+ s.append(')')
1038
+ return ''.join(s)
1039
+
1040
+ __str__ = dump
1041
+ __repr__ = dump
1042
+
1043
+ def find_reachable(self, id_map=None):
1044
+ """Returns a dictionary mapping Python id() values to stable sequence
1045
+ numbers for all nodes in the tree rooted at this node. If id_map is
1046
+ specified, found nodes are appended to it."""
1047
+ if id_map is None:
1048
+ id_map = {}
1049
+ if id(self) in id_map:
1050
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
1051
+ id_map[id(self)] = len(id_map)
1052
+ for el in self._attr_statements:
1053
+ el.find_reachable(id_map)
1054
+ return id_map
1055
+
1056
+ def check_complete(self, id_map=None):
1057
+ """Raises NotWellFormed if the tree rooted at this node is not
1058
+ well-formed. If id_map is specified, this tree is only a subtree in the
1059
+ context of a larger tree, and id_map must be a dict mapping from Python
1060
+ id() codes to tree indices for all reachable nodes."""
1061
+ if id_map is None:
1062
+ id_map = self.find_reachable()
1063
+ for child in self._attr_statements:
1064
+ child.check_complete(id_map)
1065
+
1066
+ def copy(self):
1067
+ """Returns a shallow copy of this node."""
1068
+ return Block(
1069
+ statements=self._attr_statements.copy()
1070
+ )
1071
+
1072
+ def clone(self):
1073
+ """Returns a deep copy of this node. This mimics the C++ interface,
1074
+ deficiencies with links included; that is, links always point to the
1075
+ original tree. If you're not cloning a subtree in a context where this
1076
+ is the desired behavior, you may want to use the copy.deepcopy() from
1077
+ the stdlib instead, which should copy links correctly."""
1078
+ return Block(
1079
+ statements=_cloned(self._attr_statements)
1080
+ )
1081
+
1082
+ @staticmethod
1083
+ def _deserialize(cbor, seq_to_ob, links):
1084
+ """Attempts to deserialize the given cbor object (in Python primitive
1085
+ representation) into a node of this type. All (sub)nodes are added to
1086
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
1087
+ registered in the links list by means of a two-tuple of the setter
1088
+ function for the link field and the sequence number of the target node.
1089
+ """
1090
+ if not isinstance(cbor, dict):
1091
+ raise TypeError('node description object must be a dict')
1092
+ typ = cbor.get('@t', None)
1093
+ if typ is None:
1094
+ raise ValueError('type (@t) field is missing from node serialization')
1095
+ if typ != 'Block':
1096
+ raise ValueError('found node serialization for ' + typ + ', but expected Block')
1097
+
1098
+ # Deserialize the statements field.
1099
+ field = cbor.get('statements', None)
1100
+ if not isinstance(field, dict):
1101
+ raise ValueError('missing or invalid serialization of field statements')
1102
+ if field.get('@T') != '*':
1103
+ raise ValueError('unexpected edge type for field statements')
1104
+ data = field.get('@d', None)
1105
+ if not isinstance(data, list):
1106
+ raise ValueError('missing serialization of Any/Many contents')
1107
+ f_statements = MultiStatement()
1108
+ for element in data:
1109
+ if element.get('@T') != '1':
1110
+ raise ValueError('unexpected edge type for Any/Many element')
1111
+ f_statements.append(Statement._deserialize(element, seq_to_ob, links))
1112
+
1113
+ # Construct the Block node.
1114
+ node = Block(f_statements)
1115
+
1116
+ # Deserialize annotations.
1117
+ for key, val in cbor.items():
1118
+ if not (key.startswith('{') and key.endswith('}')):
1119
+ continue
1120
+ key = key[1:-1]
1121
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
1122
+
1123
+ # Register node in sequence number lookup.
1124
+ seq = cbor.get('@i', None)
1125
+ if not isinstance(seq, int):
1126
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
1127
+ if seq in seq_to_ob:
1128
+ raise ValueError('duplicate sequence number %d' % seq)
1129
+ seq_to_ob[seq] = node
1130
+
1131
+ return node
1132
+
1133
+ def _serialize(self, id_map):
1134
+ """Serializes this node to the Python primitive representation of its
1135
+ CBOR serialization. The tree that the node belongs to must be
1136
+ well-formed. id_map must match Python id() calls for all nodes to unique
1137
+ integers, to use for the sequence number representation of links."""
1138
+ cbor = {'@i': id_map[id(self)], '@t': 'Block'}
1139
+
1140
+ # Serialize the statements field.
1141
+ field = {'@T': '*'}
1142
+ lst = []
1143
+ for el in self._attr_statements:
1144
+ el = el._serialize(id_map)
1145
+ el['@T'] = '1'
1146
+ lst.append(el)
1147
+ field['@d'] = lst
1148
+ cbor['statements'] = field
1149
+
1150
+ # Serialize annotations.
1151
+ for key, val in self._annot.items():
1152
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
1153
+
1154
+ return cbor
1155
+
1156
+
1157
+ class MultiBlock(_Multiple):
1158
+ """Wrapper for an edge with multiple Block objects."""
1159
+
1160
+ _T = Block
1161
+
1162
+
1163
+ _typemap['Block'] = Block
1164
+
1165
+ class Statement(Annotated):
1166
+ __slots__ = []
1167
+
1168
+ def __init__(
1169
+ self,
1170
+ annotations=None,
1171
+ ):
1172
+ super().__init__(annotations=annotations)
1173
+
1174
+ @staticmethod
1175
+ def _deserialize(cbor, seq_to_ob, links):
1176
+ """Attempts to deserialize the given cbor object (in Python primitive
1177
+ representation) into a node of this type. All (sub)nodes are added to
1178
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
1179
+ registered in the links list by means of a two-tuple of the setter
1180
+ function for the link field and the sequence number of the target node.
1181
+ """
1182
+ if not isinstance(cbor, dict):
1183
+ raise TypeError('node description object must be a dict')
1184
+ typ = cbor.get('@t', None)
1185
+ if typ is None:
1186
+ raise ValueError('type (@t) field is missing from node serialization')
1187
+ if typ == 'Instruction':
1188
+ return Instruction._deserialize(cbor, seq_to_ob, links)
1189
+ raise ValueError('unknown or unexpected type (@t) found in node serialization')
1190
+
1191
+ def _serialize(self, id_map):
1192
+ """Serializes this node to the Python primitive representation of its
1193
+ CBOR serialization. The tree that the node belongs to must be
1194
+ well-formed. id_map must match Python id() calls for all nodes to unique
1195
+ integers, to use for the sequence number representation of links."""
1196
+ cbor = {'@i': id_map[id(self)], '@t': 'Statement'}
1197
+
1198
+ # Serialize the annotations field.
1199
+ field = {'@T': '*'}
1200
+ lst = []
1201
+ for el in self._attr_annotations:
1202
+ el = el._serialize(id_map)
1203
+ el['@T'] = '1'
1204
+ lst.append(el)
1205
+ field['@d'] = lst
1206
+ cbor['annotations'] = field
1207
+
1208
+ # Serialize annotations.
1209
+ for key, val in self._annot.items():
1210
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
1211
+
1212
+ return cbor
1213
+
1214
+
1215
+ class MultiStatement(_Multiple):
1216
+ """Wrapper for an edge with multiple Statement objects."""
1217
+
1218
+ _T = Statement
1219
+
1220
+
1221
+ _typemap['Statement'] = Statement
1222
+
1223
+ class Instruction(Statement):
1224
+ """Regular instruction (a gate, or a measure instruction)."""
1225
+
1226
+ __slots__ = [
1227
+ '_attr_instruction_ref',
1228
+ '_attr_name',
1229
+ '_attr_operands',
1230
+ ]
1231
+
1232
+ def __init__(
1233
+ self,
1234
+ instruction_ref=None,
1235
+ name=None,
1236
+ operands=None,
1237
+ annotations=None,
1238
+ ):
1239
+ super().__init__(annotations=annotations)
1240
+ self.instruction_ref = instruction_ref
1241
+ self.name = name
1242
+ self.operands = operands
1243
+
1244
+ @property
1245
+ def instruction_ref(self):
1246
+ return self._attr_instruction_ref
1247
+
1248
+ @instruction_ref.setter
1249
+ def instruction_ref(self, val):
1250
+ if val is None:
1251
+ del self.instruction_ref
1252
+ return
1253
+ if not isinstance(val, cqasm.v3x.instruction.InstructionRef):
1254
+ # Try to "typecast" if this isn't an obvious mistake.
1255
+ if isinstance(val, Node):
1256
+ raise TypeError('instruction_ref must be of type cqasm.v3x.instruction.InstructionRef')
1257
+ val = cqasm.v3x.instruction.InstructionRef(val)
1258
+ self._attr_instruction_ref = val
1259
+
1260
+ @instruction_ref.deleter
1261
+ def instruction_ref(self):
1262
+ self._attr_instruction_ref = cqasm.v3x.instruction.InstructionRef()
1263
+
1264
+ @property
1265
+ def name(self):
1266
+ return self._attr_name
1267
+
1268
+ @name.setter
1269
+ def name(self, val):
1270
+ if val is None:
1271
+ del self.name
1272
+ return
1273
+ if not isinstance(val, cqasm.v3x.primitives.Str):
1274
+ # Try to "typecast" if this isn't an obvious mistake.
1275
+ if isinstance(val, Node):
1276
+ raise TypeError('name must be of type cqasm.v3x.primitives.Str')
1277
+ val = cqasm.v3x.primitives.Str(val)
1278
+ self._attr_name = val
1279
+
1280
+ @name.deleter
1281
+ def name(self):
1282
+ self._attr_name = cqasm.v3x.primitives.Str()
1283
+
1284
+ @property
1285
+ def operands(self):
1286
+ return self._attr_operands
1287
+
1288
+ @operands.setter
1289
+ def operands(self, val):
1290
+ if val is None:
1291
+ del self.operands
1292
+ return
1293
+ if not isinstance(val, cqasm.v3x.values.MultiValueBase):
1294
+ # Try to "typecast" if this isn't an obvious mistake.
1295
+ if isinstance(val, Node):
1296
+ raise TypeError('operands must be of type cqasm.v3x.values.MultiValueBase')
1297
+ val = cqasm.v3x.values.MultiValueBase(val)
1298
+ self._attr_operands = val
1299
+
1300
+ @operands.deleter
1301
+ def operands(self):
1302
+ self._attr_operands = cqasm.v3x.values.MultiValueBase()
1303
+
1304
+ def __eq__(self, other):
1305
+ """Equality operator. Ignores annotations!"""
1306
+ if not isinstance(other, Instruction):
1307
+ return False
1308
+ if self.instruction_ref != other.instruction_ref:
1309
+ return False
1310
+ if self.name != other.name:
1311
+ return False
1312
+ if self.operands != other.operands:
1313
+ return False
1314
+ if self.annotations != other.annotations:
1315
+ return False
1316
+ return True
1317
+
1318
+ def dump(self, indent=0, annotations=None, links=1):
1319
+ """Returns a debug representation of this tree as a multiline string.
1320
+ indent is the number of double spaces prefixed before every line.
1321
+ annotations, if specified, must be a set-like object containing the key
1322
+ strings of the annotations that are to be printed. links specifies the
1323
+ maximum link recursion depth."""
1324
+ s = [' '*indent]
1325
+ s.append('Instruction(')
1326
+ if annotations is None:
1327
+ annotations = []
1328
+ for key in annotations:
1329
+ if key in self:
1330
+ s.append(' # {}: {}'.format(key, self[key]))
1331
+ s.append('\n')
1332
+ indent += 1
1333
+ s.append(' '*indent)
1334
+ s.append('instruction_ref: ')
1335
+ s.append(str(self.instruction_ref) + '\n')
1336
+ s.append(' '*indent)
1337
+ s.append('name: ')
1338
+ s.append(str(self.name) + '\n')
1339
+ s.append(' '*indent)
1340
+ s.append('operands: ')
1341
+ if not self.operands:
1342
+ s.append('-\n')
1343
+ else:
1344
+ s.append('[\n')
1345
+ for child in self.operands:
1346
+ s.append(child.dump(indent + 1, annotations, links) + '\n')
1347
+ s.append(' '*indent + ']\n')
1348
+ s.append(' '*indent)
1349
+ s.append('annotations: ')
1350
+ if not self.annotations:
1351
+ s.append('-\n')
1352
+ else:
1353
+ s.append('[\n')
1354
+ for child in self.annotations:
1355
+ s.append(child.dump(indent + 1, annotations, links) + '\n')
1356
+ s.append(' '*indent + ']\n')
1357
+ indent -= 1
1358
+ s.append(' '*indent)
1359
+ s.append(')')
1360
+ return ''.join(s)
1361
+
1362
+ __str__ = dump
1363
+ __repr__ = dump
1364
+
1365
+ def find_reachable(self, id_map=None):
1366
+ """Returns a dictionary mapping Python id() values to stable sequence
1367
+ numbers for all nodes in the tree rooted at this node. If id_map is
1368
+ specified, found nodes are appended to it."""
1369
+ if id_map is None:
1370
+ id_map = {}
1371
+ if id(self) in id_map:
1372
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
1373
+ id_map[id(self)] = len(id_map)
1374
+ for el in self._attr_operands:
1375
+ el.find_reachable(id_map)
1376
+ for el in self._attr_annotations:
1377
+ el.find_reachable(id_map)
1378
+ return id_map
1379
+
1380
+ def check_complete(self, id_map=None):
1381
+ """Raises NotWellFormed if the tree rooted at this node is not
1382
+ well-formed. If id_map is specified, this tree is only a subtree in the
1383
+ context of a larger tree, and id_map must be a dict mapping from Python
1384
+ id() codes to tree indices for all reachable nodes."""
1385
+ if id_map is None:
1386
+ id_map = self.find_reachable()
1387
+ for child in self._attr_operands:
1388
+ child.check_complete(id_map)
1389
+ for child in self._attr_annotations:
1390
+ child.check_complete(id_map)
1391
+
1392
+ def copy(self):
1393
+ """Returns a shallow copy of this node."""
1394
+ return Instruction(
1395
+ instruction_ref=self._attr_instruction_ref,
1396
+ name=self._attr_name,
1397
+ operands=self._attr_operands.copy(),
1398
+ annotations=self._attr_annotations.copy()
1399
+ )
1400
+
1401
+ def clone(self):
1402
+ """Returns a deep copy of this node. This mimics the C++ interface,
1403
+ deficiencies with links included; that is, links always point to the
1404
+ original tree. If you're not cloning a subtree in a context where this
1405
+ is the desired behavior, you may want to use the copy.deepcopy() from
1406
+ the stdlib instead, which should copy links correctly."""
1407
+ return Instruction(
1408
+ instruction_ref=_cloned(self._attr_instruction_ref),
1409
+ name=_cloned(self._attr_name),
1410
+ operands=_cloned(self._attr_operands),
1411
+ annotations=_cloned(self._attr_annotations)
1412
+ )
1413
+
1414
+ @staticmethod
1415
+ def _deserialize(cbor, seq_to_ob, links):
1416
+ """Attempts to deserialize the given cbor object (in Python primitive
1417
+ representation) into a node of this type. All (sub)nodes are added to
1418
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
1419
+ registered in the links list by means of a two-tuple of the setter
1420
+ function for the link field and the sequence number of the target node.
1421
+ """
1422
+ if not isinstance(cbor, dict):
1423
+ raise TypeError('node description object must be a dict')
1424
+ typ = cbor.get('@t', None)
1425
+ if typ is None:
1426
+ raise ValueError('type (@t) field is missing from node serialization')
1427
+ if typ != 'Instruction':
1428
+ raise ValueError('found node serialization for ' + typ + ', but expected Instruction')
1429
+
1430
+ # Deserialize the instruction_ref field.
1431
+ field = cbor.get('instruction_ref', None)
1432
+ if not isinstance(field, dict):
1433
+ raise ValueError('missing or invalid serialization of field instruction_ref')
1434
+ if hasattr(cqasm.v3x.instruction.InstructionRef, 'deserialize_cbor'):
1435
+ f_instruction_ref = cqasm.v3x.instruction.InstructionRef.deserialize_cbor(field)
1436
+ else:
1437
+ f_instruction_ref = cqasm.v3x.primitives.deserialize(cqasm.v3x.instruction.InstructionRef, field)
1438
+
1439
+ # Deserialize the name field.
1440
+ field = cbor.get('name', None)
1441
+ if not isinstance(field, dict):
1442
+ raise ValueError('missing or invalid serialization of field name')
1443
+ if hasattr(cqasm.v3x.primitives.Str, 'deserialize_cbor'):
1444
+ f_name = cqasm.v3x.primitives.Str.deserialize_cbor(field)
1445
+ else:
1446
+ f_name = cqasm.v3x.primitives.deserialize(cqasm.v3x.primitives.Str, field)
1447
+
1448
+ # Deserialize the operands field.
1449
+ field = cbor.get('operands', None)
1450
+ if not isinstance(field, dict):
1451
+ raise ValueError('missing or invalid serialization of field operands')
1452
+ if field.get('@T') != '*':
1453
+ raise ValueError('unexpected edge type for field operands')
1454
+ data = field.get('@d', None)
1455
+ if not isinstance(data, list):
1456
+ raise ValueError('missing serialization of Any/Many contents')
1457
+ f_operands = cqasm.v3x.values.MultiValueBase()
1458
+ for element in data:
1459
+ if element.get('@T') != '1':
1460
+ raise ValueError('unexpected edge type for Any/Many element')
1461
+ f_operands.append(cqasm.v3x.values.ValueBase._deserialize(element, seq_to_ob, links))
1462
+
1463
+ # Deserialize the annotations field.
1464
+ field = cbor.get('annotations', None)
1465
+ if not isinstance(field, dict):
1466
+ raise ValueError('missing or invalid serialization of field annotations')
1467
+ if field.get('@T') != '*':
1468
+ raise ValueError('unexpected edge type for field annotations')
1469
+ data = field.get('@d', None)
1470
+ if not isinstance(data, list):
1471
+ raise ValueError('missing serialization of Any/Many contents')
1472
+ f_annotations = MultiAnnotationData()
1473
+ for element in data:
1474
+ if element.get('@T') != '1':
1475
+ raise ValueError('unexpected edge type for Any/Many element')
1476
+ f_annotations.append(AnnotationData._deserialize(element, seq_to_ob, links))
1477
+
1478
+ # Construct the Instruction node.
1479
+ node = Instruction(f_instruction_ref, f_name, f_operands, f_annotations)
1480
+
1481
+ # Deserialize annotations.
1482
+ for key, val in cbor.items():
1483
+ if not (key.startswith('{') and key.endswith('}')):
1484
+ continue
1485
+ key = key[1:-1]
1486
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
1487
+
1488
+ # Register node in sequence number lookup.
1489
+ seq = cbor.get('@i', None)
1490
+ if not isinstance(seq, int):
1491
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
1492
+ if seq in seq_to_ob:
1493
+ raise ValueError('duplicate sequence number %d' % seq)
1494
+ seq_to_ob[seq] = node
1495
+
1496
+ return node
1497
+
1498
+ def _serialize(self, id_map):
1499
+ """Serializes this node to the Python primitive representation of its
1500
+ CBOR serialization. The tree that the node belongs to must be
1501
+ well-formed. id_map must match Python id() calls for all nodes to unique
1502
+ integers, to use for the sequence number representation of links."""
1503
+ cbor = {'@i': id_map[id(self)], '@t': 'Instruction'}
1504
+
1505
+ # Serialize the instruction_ref field.
1506
+ if hasattr(self._attr_instruction_ref, 'serialize_cbor'):
1507
+ cbor['instruction_ref'] = self._attr_instruction_ref.serialize_cbor()
1508
+ else:
1509
+ cbor['instruction_ref'] = cqasm.v3x.primitives.serialize(cqasm.v3x.instruction.InstructionRef, self._attr_instruction_ref)
1510
+
1511
+ # Serialize the name field.
1512
+ if hasattr(self._attr_name, 'serialize_cbor'):
1513
+ cbor['name'] = self._attr_name.serialize_cbor()
1514
+ else:
1515
+ cbor['name'] = cqasm.v3x.primitives.serialize(cqasm.v3x.primitives.Str, self._attr_name)
1516
+
1517
+ # Serialize the operands field.
1518
+ field = {'@T': '*'}
1519
+ lst = []
1520
+ for el in self._attr_operands:
1521
+ el = el._serialize(id_map)
1522
+ el['@T'] = '1'
1523
+ lst.append(el)
1524
+ field['@d'] = lst
1525
+ cbor['operands'] = field
1526
+
1527
+ # Serialize the annotations field.
1528
+ field = {'@T': '*'}
1529
+ lst = []
1530
+ for el in self._attr_annotations:
1531
+ el = el._serialize(id_map)
1532
+ el['@T'] = '1'
1533
+ lst.append(el)
1534
+ field['@d'] = lst
1535
+ cbor['annotations'] = field
1536
+
1537
+ # Serialize annotations.
1538
+ for key, val in self._annot.items():
1539
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
1540
+
1541
+ return cbor
1542
+
1543
+
1544
+ class MultiInstruction(_Multiple):
1545
+ """Wrapper for an edge with multiple Instruction objects."""
1546
+
1547
+ _T = Instruction
1548
+
1549
+
1550
+ _typemap['Instruction'] = Instruction
1551
+
1552
+ class Program(Node):
1553
+ __slots__ = [
1554
+ '_attr_api_version',
1555
+ '_attr_version',
1556
+ '_attr_block',
1557
+ '_attr_variables',
1558
+ ]
1559
+
1560
+ def __init__(
1561
+ self,
1562
+ api_version=None,
1563
+ version=None,
1564
+ block=None,
1565
+ variables=None,
1566
+ ):
1567
+ super().__init__()
1568
+ self.api_version = api_version
1569
+ self.version = version
1570
+ self.block = block
1571
+ self.variables = variables
1572
+
1573
+ @property
1574
+ def api_version(self):
1575
+ """API version. This may be greater than or equal to the file version.
1576
+ This controls which fields of the tree are used, where such usage
1577
+ depends on the version."""
1578
+ return self._attr_api_version
1579
+
1580
+ @api_version.setter
1581
+ def api_version(self, val):
1582
+ if val is None:
1583
+ del self.api_version
1584
+ return
1585
+ if not isinstance(val, cqasm.v3x.primitives.Version):
1586
+ # Try to "typecast" if this isn't an obvious mistake.
1587
+ if isinstance(val, Node):
1588
+ raise TypeError('api_version must be of type cqasm.v3x.primitives.Version')
1589
+ val = cqasm.v3x.primitives.Version(val)
1590
+ self._attr_api_version = val
1591
+
1592
+ @api_version.deleter
1593
+ def api_version(self):
1594
+ self._attr_api_version = cqasm.v3x.primitives.Version()
1595
+
1596
+ @property
1597
+ def version(self):
1598
+ """File version."""
1599
+ return self._attr_version
1600
+
1601
+ @version.setter
1602
+ def version(self, val):
1603
+ if val is None:
1604
+ del self.version
1605
+ return
1606
+ if not isinstance(val, Version):
1607
+ # Try to "typecast" if this isn't an obvious mistake.
1608
+ if isinstance(val, Node):
1609
+ raise TypeError('version must be of type Version')
1610
+ val = Version(val)
1611
+ self._attr_version = val
1612
+
1613
+ @version.deleter
1614
+ def version(self):
1615
+ self._attr_version = None
1616
+
1617
+ @property
1618
+ def block(self):
1619
+ """Global scope block."""
1620
+ return self._attr_block
1621
+
1622
+ @block.setter
1623
+ def block(self, val):
1624
+ if val is None:
1625
+ del self.block
1626
+ return
1627
+ if not isinstance(val, Block):
1628
+ # Try to "typecast" if this isn't an obvious mistake.
1629
+ if isinstance(val, Node):
1630
+ raise TypeError('block must be of type Block')
1631
+ val = Block(val)
1632
+ self._attr_block = val
1633
+
1634
+ @block.deleter
1635
+ def block(self):
1636
+ self._attr_block = None
1637
+
1638
+ @property
1639
+ def variables(self):
1640
+ """The list of variables."""
1641
+ return self._attr_variables
1642
+
1643
+ @variables.setter
1644
+ def variables(self, val):
1645
+ if val is None:
1646
+ del self.variables
1647
+ return
1648
+ if not isinstance(val, MultiVariable):
1649
+ # Try to "typecast" if this isn't an obvious mistake.
1650
+ if isinstance(val, Node):
1651
+ raise TypeError('variables must be of type MultiVariable')
1652
+ val = MultiVariable(val)
1653
+ self._attr_variables = val
1654
+
1655
+ @variables.deleter
1656
+ def variables(self):
1657
+ self._attr_variables = MultiVariable()
1658
+
1659
+ def __eq__(self, other):
1660
+ """Equality operator. Ignores annotations!"""
1661
+ if not isinstance(other, Program):
1662
+ return False
1663
+ if self.api_version != other.api_version:
1664
+ return False
1665
+ if self.version != other.version:
1666
+ return False
1667
+ if self.block != other.block:
1668
+ return False
1669
+ if self.variables != other.variables:
1670
+ return False
1671
+ return True
1672
+
1673
+ def dump(self, indent=0, annotations=None, links=1):
1674
+ """Returns a debug representation of this tree as a multiline string.
1675
+ indent is the number of double spaces prefixed before every line.
1676
+ annotations, if specified, must be a set-like object containing the key
1677
+ strings of the annotations that are to be printed. links specifies the
1678
+ maximum link recursion depth."""
1679
+ s = [' '*indent]
1680
+ s.append('Program(')
1681
+ if annotations is None:
1682
+ annotations = []
1683
+ for key in annotations:
1684
+ if key in self:
1685
+ s.append(' # {}: {}'.format(key, self[key]))
1686
+ s.append('\n')
1687
+ indent += 1
1688
+ s.append(' '*indent)
1689
+ s.append('api_version: ')
1690
+ s.append(str(self.api_version) + '\n')
1691
+ s.append(' '*indent)
1692
+ s.append('version: ')
1693
+ if self.version is None:
1694
+ s.append('!MISSING\n')
1695
+ else:
1696
+ s.append('<\n')
1697
+ s.append(self.version.dump(indent + 1, annotations, links) + '\n')
1698
+ s.append(' '*indent + '>\n')
1699
+ s.append(' '*indent)
1700
+ s.append('block: ')
1701
+ if self.block is None:
1702
+ s.append('!MISSING\n')
1703
+ else:
1704
+ s.append('<\n')
1705
+ s.append(self.block.dump(indent + 1, annotations, links) + '\n')
1706
+ s.append(' '*indent + '>\n')
1707
+ s.append(' '*indent)
1708
+ s.append('variables: ')
1709
+ if not self.variables:
1710
+ s.append('-\n')
1711
+ else:
1712
+ s.append('[\n')
1713
+ for child in self.variables:
1714
+ s.append(child.dump(indent + 1, annotations, links) + '\n')
1715
+ s.append(' '*indent + ']\n')
1716
+ indent -= 1
1717
+ s.append(' '*indent)
1718
+ s.append(')')
1719
+ return ''.join(s)
1720
+
1721
+ __str__ = dump
1722
+ __repr__ = dump
1723
+
1724
+ def find_reachable(self, id_map=None):
1725
+ """Returns a dictionary mapping Python id() values to stable sequence
1726
+ numbers for all nodes in the tree rooted at this node. If id_map is
1727
+ specified, found nodes are appended to it."""
1728
+ if id_map is None:
1729
+ id_map = {}
1730
+ if id(self) in id_map:
1731
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
1732
+ id_map[id(self)] = len(id_map)
1733
+ if self._attr_version is not None:
1734
+ self._attr_version.find_reachable(id_map)
1735
+ if self._attr_block is not None:
1736
+ self._attr_block.find_reachable(id_map)
1737
+ for el in self._attr_variables:
1738
+ el.find_reachable(id_map)
1739
+ return id_map
1740
+
1741
+ def check_complete(self, id_map=None):
1742
+ """Raises NotWellFormed if the tree rooted at this node is not
1743
+ well-formed. If id_map is specified, this tree is only a subtree in the
1744
+ context of a larger tree, and id_map must be a dict mapping from Python
1745
+ id() codes to tree indices for all reachable nodes."""
1746
+ if id_map is None:
1747
+ id_map = self.find_reachable()
1748
+ if self._attr_version is None:
1749
+ raise NotWellFormed('version is required but not set')
1750
+ if self._attr_version is not None:
1751
+ self._attr_version.check_complete(id_map)
1752
+ if self._attr_block is None:
1753
+ raise NotWellFormed('block is required but not set')
1754
+ if self._attr_block is not None:
1755
+ self._attr_block.check_complete(id_map)
1756
+ for child in self._attr_variables:
1757
+ child.check_complete(id_map)
1758
+
1759
+ def copy(self):
1760
+ """Returns a shallow copy of this node."""
1761
+ return Program(
1762
+ api_version=self._attr_api_version,
1763
+ version=self._attr_version,
1764
+ block=self._attr_block,
1765
+ variables=self._attr_variables.copy()
1766
+ )
1767
+
1768
+ def clone(self):
1769
+ """Returns a deep copy of this node. This mimics the C++ interface,
1770
+ deficiencies with links included; that is, links always point to the
1771
+ original tree. If you're not cloning a subtree in a context where this
1772
+ is the desired behavior, you may want to use the copy.deepcopy() from
1773
+ the stdlib instead, which should copy links correctly."""
1774
+ return Program(
1775
+ api_version=_cloned(self._attr_api_version),
1776
+ version=_cloned(self._attr_version),
1777
+ block=_cloned(self._attr_block),
1778
+ variables=_cloned(self._attr_variables)
1779
+ )
1780
+
1781
+ @staticmethod
1782
+ def _deserialize(cbor, seq_to_ob, links):
1783
+ """Attempts to deserialize the given cbor object (in Python primitive
1784
+ representation) into a node of this type. All (sub)nodes are added to
1785
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
1786
+ registered in the links list by means of a two-tuple of the setter
1787
+ function for the link field and the sequence number of the target node.
1788
+ """
1789
+ if not isinstance(cbor, dict):
1790
+ raise TypeError('node description object must be a dict')
1791
+ typ = cbor.get('@t', None)
1792
+ if typ is None:
1793
+ raise ValueError('type (@t) field is missing from node serialization')
1794
+ if typ != 'Program':
1795
+ raise ValueError('found node serialization for ' + typ + ', but expected Program')
1796
+
1797
+ # Deserialize the api_version field.
1798
+ field = cbor.get('api_version', None)
1799
+ if not isinstance(field, dict):
1800
+ raise ValueError('missing or invalid serialization of field api_version')
1801
+ if hasattr(cqasm.v3x.primitives.Version, 'deserialize_cbor'):
1802
+ f_api_version = cqasm.v3x.primitives.Version.deserialize_cbor(field)
1803
+ else:
1804
+ f_api_version = cqasm.v3x.primitives.deserialize(cqasm.v3x.primitives.Version, field)
1805
+
1806
+ # Deserialize the version field.
1807
+ field = cbor.get('version', None)
1808
+ if not isinstance(field, dict):
1809
+ raise ValueError('missing or invalid serialization of field version')
1810
+ if field.get('@T') != '1':
1811
+ raise ValueError('unexpected edge type for field version')
1812
+ if field.get('@t', None) is None:
1813
+ f_version = None
1814
+ else:
1815
+ f_version = Version._deserialize(field, seq_to_ob, links)
1816
+
1817
+ # Deserialize the block field.
1818
+ field = cbor.get('block', None)
1819
+ if not isinstance(field, dict):
1820
+ raise ValueError('missing or invalid serialization of field block')
1821
+ if field.get('@T') != '1':
1822
+ raise ValueError('unexpected edge type for field block')
1823
+ if field.get('@t', None) is None:
1824
+ f_block = None
1825
+ else:
1826
+ f_block = Block._deserialize(field, seq_to_ob, links)
1827
+
1828
+ # Deserialize the variables field.
1829
+ field = cbor.get('variables', None)
1830
+ if not isinstance(field, dict):
1831
+ raise ValueError('missing or invalid serialization of field variables')
1832
+ if field.get('@T') != '*':
1833
+ raise ValueError('unexpected edge type for field variables')
1834
+ data = field.get('@d', None)
1835
+ if not isinstance(data, list):
1836
+ raise ValueError('missing serialization of Any/Many contents')
1837
+ f_variables = MultiVariable()
1838
+ for element in data:
1839
+ if element.get('@T') != '1':
1840
+ raise ValueError('unexpected edge type for Any/Many element')
1841
+ f_variables.append(Variable._deserialize(element, seq_to_ob, links))
1842
+
1843
+ # Construct the Program node.
1844
+ node = Program(f_api_version, f_version, f_block, f_variables)
1845
+
1846
+ # Deserialize annotations.
1847
+ for key, val in cbor.items():
1848
+ if not (key.startswith('{') and key.endswith('}')):
1849
+ continue
1850
+ key = key[1:-1]
1851
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
1852
+
1853
+ # Register node in sequence number lookup.
1854
+ seq = cbor.get('@i', None)
1855
+ if not isinstance(seq, int):
1856
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
1857
+ if seq in seq_to_ob:
1858
+ raise ValueError('duplicate sequence number %d' % seq)
1859
+ seq_to_ob[seq] = node
1860
+
1861
+ return node
1862
+
1863
+ def _serialize(self, id_map):
1864
+ """Serializes this node to the Python primitive representation of its
1865
+ CBOR serialization. The tree that the node belongs to must be
1866
+ well-formed. id_map must match Python id() calls for all nodes to unique
1867
+ integers, to use for the sequence number representation of links."""
1868
+ cbor = {'@i': id_map[id(self)], '@t': 'Program'}
1869
+
1870
+ # Serialize the api_version field.
1871
+ if hasattr(self._attr_api_version, 'serialize_cbor'):
1872
+ cbor['api_version'] = self._attr_api_version.serialize_cbor()
1873
+ else:
1874
+ cbor['api_version'] = cqasm.v3x.primitives.serialize(cqasm.v3x.primitives.Version, self._attr_api_version)
1875
+
1876
+ # Serialize the version field.
1877
+ field = {'@T': '1'}
1878
+ if self._attr_version is None:
1879
+ field['@t'] = None
1880
+ else:
1881
+ field.update(self._attr_version._serialize(id_map))
1882
+ cbor['version'] = field
1883
+
1884
+ # Serialize the block field.
1885
+ field = {'@T': '1'}
1886
+ if self._attr_block is None:
1887
+ field['@t'] = None
1888
+ else:
1889
+ field.update(self._attr_block._serialize(id_map))
1890
+ cbor['block'] = field
1891
+
1892
+ # Serialize the variables field.
1893
+ field = {'@T': '*'}
1894
+ lst = []
1895
+ for el in self._attr_variables:
1896
+ el = el._serialize(id_map)
1897
+ el['@T'] = '1'
1898
+ lst.append(el)
1899
+ field['@d'] = lst
1900
+ cbor['variables'] = field
1901
+
1902
+ # Serialize annotations.
1903
+ for key, val in self._annot.items():
1904
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
1905
+
1906
+ return cbor
1907
+
1908
+
1909
+ class MultiProgram(_Multiple):
1910
+ """Wrapper for an edge with multiple Program objects."""
1911
+
1912
+ _T = Program
1913
+
1914
+
1915
+ _typemap['Program'] = Program
1916
+
1917
+ class Variable(Annotated):
1918
+ __slots__ = [
1919
+ '_attr_name',
1920
+ '_attr_typ',
1921
+ ]
1922
+
1923
+ def __init__(
1924
+ self,
1925
+ name=None,
1926
+ typ=None,
1927
+ annotations=None,
1928
+ ):
1929
+ super().__init__(annotations=annotations)
1930
+ self.name = name
1931
+ self.typ = typ
1932
+
1933
+ @property
1934
+ def name(self):
1935
+ return self._attr_name
1936
+
1937
+ @name.setter
1938
+ def name(self, val):
1939
+ if val is None:
1940
+ del self.name
1941
+ return
1942
+ if not isinstance(val, cqasm.v3x.primitives.Str):
1943
+ # Try to "typecast" if this isn't an obvious mistake.
1944
+ if isinstance(val, Node):
1945
+ raise TypeError('name must be of type cqasm.v3x.primitives.Str')
1946
+ val = cqasm.v3x.primitives.Str(val)
1947
+ self._attr_name = val
1948
+
1949
+ @name.deleter
1950
+ def name(self):
1951
+ self._attr_name = cqasm.v3x.primitives.Str()
1952
+
1953
+ @property
1954
+ def typ(self):
1955
+ return self._attr_typ
1956
+
1957
+ @typ.setter
1958
+ def typ(self, val):
1959
+ if val is None:
1960
+ del self.typ
1961
+ return
1962
+ if not isinstance(val, cqasm.v3x.types.TypeBase):
1963
+ # Try to "typecast" if this isn't an obvious mistake.
1964
+ if isinstance(val, Node):
1965
+ raise TypeError('typ must be of type cqasm.v3x.types.TypeBase')
1966
+ val = cqasm.v3x.types.TypeBase(val)
1967
+ self._attr_typ = val
1968
+
1969
+ @typ.deleter
1970
+ def typ(self):
1971
+ self._attr_typ = None
1972
+
1973
+ def __eq__(self, other):
1974
+ """Equality operator. Ignores annotations!"""
1975
+ if not isinstance(other, Variable):
1976
+ return False
1977
+ if self.name != other.name:
1978
+ return False
1979
+ if self.typ != other.typ:
1980
+ return False
1981
+ if self.annotations != other.annotations:
1982
+ return False
1983
+ return True
1984
+
1985
+ def dump(self, indent=0, annotations=None, links=1):
1986
+ """Returns a debug representation of this tree as a multiline string.
1987
+ indent is the number of double spaces prefixed before every line.
1988
+ annotations, if specified, must be a set-like object containing the key
1989
+ strings of the annotations that are to be printed. links specifies the
1990
+ maximum link recursion depth."""
1991
+ s = [' '*indent]
1992
+ s.append('Variable(')
1993
+ if annotations is None:
1994
+ annotations = []
1995
+ for key in annotations:
1996
+ if key in self:
1997
+ s.append(' # {}: {}'.format(key, self[key]))
1998
+ s.append('\n')
1999
+ indent += 1
2000
+ s.append(' '*indent)
2001
+ s.append('name: ')
2002
+ s.append(str(self.name) + '\n')
2003
+ s.append(' '*indent)
2004
+ s.append('typ: ')
2005
+ if self.typ is None:
2006
+ s.append('!MISSING\n')
2007
+ else:
2008
+ s.append('<\n')
2009
+ s.append(self.typ.dump(indent + 1, annotations, links) + '\n')
2010
+ s.append(' '*indent + '>\n')
2011
+ s.append(' '*indent)
2012
+ s.append('annotations: ')
2013
+ if not self.annotations:
2014
+ s.append('-\n')
2015
+ else:
2016
+ s.append('[\n')
2017
+ for child in self.annotations:
2018
+ s.append(child.dump(indent + 1, annotations, links) + '\n')
2019
+ s.append(' '*indent + ']\n')
2020
+ indent -= 1
2021
+ s.append(' '*indent)
2022
+ s.append(')')
2023
+ return ''.join(s)
2024
+
2025
+ __str__ = dump
2026
+ __repr__ = dump
2027
+
2028
+ def find_reachable(self, id_map=None):
2029
+ """Returns a dictionary mapping Python id() values to stable sequence
2030
+ numbers for all nodes in the tree rooted at this node. If id_map is
2031
+ specified, found nodes are appended to it."""
2032
+ if id_map is None:
2033
+ id_map = {}
2034
+ if id(self) in id_map:
2035
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
2036
+ id_map[id(self)] = len(id_map)
2037
+ if self._attr_typ is not None:
2038
+ self._attr_typ.find_reachable(id_map)
2039
+ for el in self._attr_annotations:
2040
+ el.find_reachable(id_map)
2041
+ return id_map
2042
+
2043
+ def check_complete(self, id_map=None):
2044
+ """Raises NotWellFormed if the tree rooted at this node is not
2045
+ well-formed. If id_map is specified, this tree is only a subtree in the
2046
+ context of a larger tree, and id_map must be a dict mapping from Python
2047
+ id() codes to tree indices for all reachable nodes."""
2048
+ if id_map is None:
2049
+ id_map = self.find_reachable()
2050
+ if self._attr_typ is None:
2051
+ raise NotWellFormed('typ is required but not set')
2052
+ if self._attr_typ is not None:
2053
+ self._attr_typ.check_complete(id_map)
2054
+ for child in self._attr_annotations:
2055
+ child.check_complete(id_map)
2056
+
2057
+ def copy(self):
2058
+ """Returns a shallow copy of this node."""
2059
+ return Variable(
2060
+ name=self._attr_name,
2061
+ typ=self._attr_typ,
2062
+ annotations=self._attr_annotations.copy()
2063
+ )
2064
+
2065
+ def clone(self):
2066
+ """Returns a deep copy of this node. This mimics the C++ interface,
2067
+ deficiencies with links included; that is, links always point to the
2068
+ original tree. If you're not cloning a subtree in a context where this
2069
+ is the desired behavior, you may want to use the copy.deepcopy() from
2070
+ the stdlib instead, which should copy links correctly."""
2071
+ return Variable(
2072
+ name=_cloned(self._attr_name),
2073
+ typ=_cloned(self._attr_typ),
2074
+ annotations=_cloned(self._attr_annotations)
2075
+ )
2076
+
2077
+ @staticmethod
2078
+ def _deserialize(cbor, seq_to_ob, links):
2079
+ """Attempts to deserialize the given cbor object (in Python primitive
2080
+ representation) into a node of this type. All (sub)nodes are added to
2081
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
2082
+ registered in the links list by means of a two-tuple of the setter
2083
+ function for the link field and the sequence number of the target node.
2084
+ """
2085
+ if not isinstance(cbor, dict):
2086
+ raise TypeError('node description object must be a dict')
2087
+ typ = cbor.get('@t', None)
2088
+ if typ is None:
2089
+ raise ValueError('type (@t) field is missing from node serialization')
2090
+ if typ != 'Variable':
2091
+ raise ValueError('found node serialization for ' + typ + ', but expected Variable')
2092
+
2093
+ # Deserialize the name field.
2094
+ field = cbor.get('name', None)
2095
+ if not isinstance(field, dict):
2096
+ raise ValueError('missing or invalid serialization of field name')
2097
+ if hasattr(cqasm.v3x.primitives.Str, 'deserialize_cbor'):
2098
+ f_name = cqasm.v3x.primitives.Str.deserialize_cbor(field)
2099
+ else:
2100
+ f_name = cqasm.v3x.primitives.deserialize(cqasm.v3x.primitives.Str, field)
2101
+
2102
+ # Deserialize the typ field.
2103
+ field = cbor.get('typ', None)
2104
+ if not isinstance(field, dict):
2105
+ raise ValueError('missing or invalid serialization of field typ')
2106
+ if field.get('@T') != '1':
2107
+ raise ValueError('unexpected edge type for field typ')
2108
+ if field.get('@t', None) is None:
2109
+ f_typ = None
2110
+ else:
2111
+ f_typ = cqasm.v3x.types.TypeBase._deserialize(field, seq_to_ob, links)
2112
+
2113
+ # Deserialize the annotations field.
2114
+ field = cbor.get('annotations', None)
2115
+ if not isinstance(field, dict):
2116
+ raise ValueError('missing or invalid serialization of field annotations')
2117
+ if field.get('@T') != '*':
2118
+ raise ValueError('unexpected edge type for field annotations')
2119
+ data = field.get('@d', None)
2120
+ if not isinstance(data, list):
2121
+ raise ValueError('missing serialization of Any/Many contents')
2122
+ f_annotations = MultiAnnotationData()
2123
+ for element in data:
2124
+ if element.get('@T') != '1':
2125
+ raise ValueError('unexpected edge type for Any/Many element')
2126
+ f_annotations.append(AnnotationData._deserialize(element, seq_to_ob, links))
2127
+
2128
+ # Construct the Variable node.
2129
+ node = Variable(f_name, f_typ, f_annotations)
2130
+
2131
+ # Deserialize annotations.
2132
+ for key, val in cbor.items():
2133
+ if not (key.startswith('{') and key.endswith('}')):
2134
+ continue
2135
+ key = key[1:-1]
2136
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
2137
+
2138
+ # Register node in sequence number lookup.
2139
+ seq = cbor.get('@i', None)
2140
+ if not isinstance(seq, int):
2141
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
2142
+ if seq in seq_to_ob:
2143
+ raise ValueError('duplicate sequence number %d' % seq)
2144
+ seq_to_ob[seq] = node
2145
+
2146
+ return node
2147
+
2148
+ def _serialize(self, id_map):
2149
+ """Serializes this node to the Python primitive representation of its
2150
+ CBOR serialization. The tree that the node belongs to must be
2151
+ well-formed. id_map must match Python id() calls for all nodes to unique
2152
+ integers, to use for the sequence number representation of links."""
2153
+ cbor = {'@i': id_map[id(self)], '@t': 'Variable'}
2154
+
2155
+ # Serialize the name field.
2156
+ if hasattr(self._attr_name, 'serialize_cbor'):
2157
+ cbor['name'] = self._attr_name.serialize_cbor()
2158
+ else:
2159
+ cbor['name'] = cqasm.v3x.primitives.serialize(cqasm.v3x.primitives.Str, self._attr_name)
2160
+
2161
+ # Serialize the typ field.
2162
+ field = {'@T': '1'}
2163
+ if self._attr_typ is None:
2164
+ field['@t'] = None
2165
+ else:
2166
+ field.update(self._attr_typ._serialize(id_map))
2167
+ cbor['typ'] = field
2168
+
2169
+ # Serialize the annotations field.
2170
+ field = {'@T': '*'}
2171
+ lst = []
2172
+ for el in self._attr_annotations:
2173
+ el = el._serialize(id_map)
2174
+ el['@T'] = '1'
2175
+ lst.append(el)
2176
+ field['@d'] = lst
2177
+ cbor['annotations'] = field
2178
+
2179
+ # Serialize annotations.
2180
+ for key, val in self._annot.items():
2181
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
2182
+
2183
+ return cbor
2184
+
2185
+
2186
+ class MultiVariable(_Multiple):
2187
+ """Wrapper for an edge with multiple Variable objects."""
2188
+
2189
+ _T = Variable
2190
+
2191
+
2192
+ _typemap['Variable'] = Variable
2193
+
2194
+ class Version(Node):
2195
+ __slots__ = [
2196
+ '_attr_items',
2197
+ ]
2198
+
2199
+ def __init__(
2200
+ self,
2201
+ items=None,
2202
+ ):
2203
+ super().__init__()
2204
+ self.items = items
2205
+
2206
+ @property
2207
+ def items(self):
2208
+ return self._attr_items
2209
+
2210
+ @items.setter
2211
+ def items(self, val):
2212
+ if val is None:
2213
+ del self.items
2214
+ return
2215
+ if not isinstance(val, cqasm.v3x.primitives.Version):
2216
+ # Try to "typecast" if this isn't an obvious mistake.
2217
+ if isinstance(val, Node):
2218
+ raise TypeError('items must be of type cqasm.v3x.primitives.Version')
2219
+ val = cqasm.v3x.primitives.Version(val)
2220
+ self._attr_items = val
2221
+
2222
+ @items.deleter
2223
+ def items(self):
2224
+ self._attr_items = cqasm.v3x.primitives.Version()
2225
+
2226
+ def __eq__(self, other):
2227
+ """Equality operator. Ignores annotations!"""
2228
+ if not isinstance(other, Version):
2229
+ return False
2230
+ if self.items != other.items:
2231
+ return False
2232
+ return True
2233
+
2234
+ def dump(self, indent=0, annotations=None, links=1):
2235
+ """Returns a debug representation of this tree as a multiline string.
2236
+ indent is the number of double spaces prefixed before every line.
2237
+ annotations, if specified, must be a set-like object containing the key
2238
+ strings of the annotations that are to be printed. links specifies the
2239
+ maximum link recursion depth."""
2240
+ s = [' '*indent]
2241
+ s.append('Version(')
2242
+ if annotations is None:
2243
+ annotations = []
2244
+ for key in annotations:
2245
+ if key in self:
2246
+ s.append(' # {}: {}'.format(key, self[key]))
2247
+ s.append('\n')
2248
+ indent += 1
2249
+ s.append(' '*indent)
2250
+ s.append('items: ')
2251
+ s.append(str(self.items) + '\n')
2252
+ indent -= 1
2253
+ s.append(' '*indent)
2254
+ s.append(')')
2255
+ return ''.join(s)
2256
+
2257
+ __str__ = dump
2258
+ __repr__ = dump
2259
+
2260
+ def find_reachable(self, id_map=None):
2261
+ """Returns a dictionary mapping Python id() values to stable sequence
2262
+ numbers for all nodes in the tree rooted at this node. If id_map is
2263
+ specified, found nodes are appended to it."""
2264
+ if id_map is None:
2265
+ id_map = {}
2266
+ if id(self) in id_map:
2267
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
2268
+ id_map[id(self)] = len(id_map)
2269
+ return id_map
2270
+
2271
+ def check_complete(self, id_map=None):
2272
+ """Raises NotWellFormed if the tree rooted at this node is not
2273
+ well-formed. If id_map is specified, this tree is only a subtree in the
2274
+ context of a larger tree, and id_map must be a dict mapping from Python
2275
+ id() codes to tree indices for all reachable nodes."""
2276
+ if id_map is None:
2277
+ id_map = self.find_reachable()
2278
+
2279
+ def copy(self):
2280
+ """Returns a shallow copy of this node."""
2281
+ return Version(
2282
+ items=self._attr_items
2283
+ )
2284
+
2285
+ def clone(self):
2286
+ """Returns a deep copy of this node. This mimics the C++ interface,
2287
+ deficiencies with links included; that is, links always point to the
2288
+ original tree. If you're not cloning a subtree in a context where this
2289
+ is the desired behavior, you may want to use the copy.deepcopy() from
2290
+ the stdlib instead, which should copy links correctly."""
2291
+ return Version(
2292
+ items=_cloned(self._attr_items)
2293
+ )
2294
+
2295
+ @staticmethod
2296
+ def _deserialize(cbor, seq_to_ob, links):
2297
+ """Attempts to deserialize the given cbor object (in Python primitive
2298
+ representation) into a node of this type. All (sub)nodes are added to
2299
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
2300
+ registered in the links list by means of a two-tuple of the setter
2301
+ function for the link field and the sequence number of the target node.
2302
+ """
2303
+ if not isinstance(cbor, dict):
2304
+ raise TypeError('node description object must be a dict')
2305
+ typ = cbor.get('@t', None)
2306
+ if typ is None:
2307
+ raise ValueError('type (@t) field is missing from node serialization')
2308
+ if typ != 'Version':
2309
+ raise ValueError('found node serialization for ' + typ + ', but expected Version')
2310
+
2311
+ # Deserialize the items field.
2312
+ field = cbor.get('items', None)
2313
+ if not isinstance(field, dict):
2314
+ raise ValueError('missing or invalid serialization of field items')
2315
+ if hasattr(cqasm.v3x.primitives.Version, 'deserialize_cbor'):
2316
+ f_items = cqasm.v3x.primitives.Version.deserialize_cbor(field)
2317
+ else:
2318
+ f_items = cqasm.v3x.primitives.deserialize(cqasm.v3x.primitives.Version, field)
2319
+
2320
+ # Construct the Version node.
2321
+ node = Version(f_items)
2322
+
2323
+ # Deserialize annotations.
2324
+ for key, val in cbor.items():
2325
+ if not (key.startswith('{') and key.endswith('}')):
2326
+ continue
2327
+ key = key[1:-1]
2328
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
2329
+
2330
+ # Register node in sequence number lookup.
2331
+ seq = cbor.get('@i', None)
2332
+ if not isinstance(seq, int):
2333
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
2334
+ if seq in seq_to_ob:
2335
+ raise ValueError('duplicate sequence number %d' % seq)
2336
+ seq_to_ob[seq] = node
2337
+
2338
+ return node
2339
+
2340
+ def _serialize(self, id_map):
2341
+ """Serializes this node to the Python primitive representation of its
2342
+ CBOR serialization. The tree that the node belongs to must be
2343
+ well-formed. id_map must match Python id() calls for all nodes to unique
2344
+ integers, to use for the sequence number representation of links."""
2345
+ cbor = {'@i': id_map[id(self)], '@t': 'Version'}
2346
+
2347
+ # Serialize the items field.
2348
+ if hasattr(self._attr_items, 'serialize_cbor'):
2349
+ cbor['items'] = self._attr_items.serialize_cbor()
2350
+ else:
2351
+ cbor['items'] = cqasm.v3x.primitives.serialize(cqasm.v3x.primitives.Version, self._attr_items)
2352
+
2353
+ # Serialize annotations.
2354
+ for key, val in self._annot.items():
2355
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
2356
+
2357
+ return cbor
2358
+
2359
+
2360
+ class MultiVersion(_Multiple):
2361
+ """Wrapper for an edge with multiple Version objects."""
2362
+
2363
+ _T = Version
2364
+
2365
+
2366
+ _typemap['Version'] = Version
2367
+