libqasm 0.6.7__cp311-cp311-macosx_10_9_x86_64.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/values.py ADDED
@@ -0,0 +1,1714 @@
1
+ import functools
2
+ import struct
3
+ import cqasm.v3x.primitives
4
+ import cqasm.v3x.semantic
5
+
6
+
7
+ _typemap = {}
8
+
9
+
10
+ def _cbor_read_intlike(cbor, offset, info):
11
+ """Parses the additional information and reads any additional bytes it
12
+ specifies the existence of, and returns the encoded integer. offset
13
+ should point to the byte immediately following the initial byte. Returns
14
+ the encoded integer and the offset immediately following the object."""
15
+
16
+ # Info less than 24 is a shorthand for the integer itself.
17
+ if info < 24:
18
+ return info, offset
19
+
20
+ # 24 is 8-bit following the info byte.
21
+ if info == 24:
22
+ return cbor[offset], offset + 1
23
+
24
+ # 25 is 16-bit following the info byte.
25
+ if info == 25:
26
+ val, = struct.unpack('>H', cbor[offset:offset+2])
27
+ return val, offset + 2
28
+
29
+ # 26 is 32-bit following the info byte.
30
+ if info == 26:
31
+ val, = struct.unpack('>I', cbor[offset:offset+4])
32
+ return val, offset + 4
33
+
34
+ # 27 is 64-bit following the info byte.
35
+ if info == 27:
36
+ val, = struct.unpack('>Q', cbor[offset:offset+8])
37
+ return val, offset + 8
38
+
39
+ # Info greater than or equal to 28 is illegal. Note that 31 is used for
40
+ # indefinite lengths, so this must be checked prior to calling this
41
+ # method.
42
+ raise ValueError("invalid CBOR: illegal additional info for integer or object length")
43
+
44
+
45
+ def _sub_cbor_to_py(cbor, offset):
46
+ """Converts the CBOR object starting at cbor[offset] to its Python
47
+ representation for as far as tree-gen supports CBOR. Returns this Python
48
+ representation and the offset immediately following the CBOR representation
49
+ thereof. Supported types:
50
+
51
+ - 0: unsigned integer (int)
52
+ - 1: negative integer (int)
53
+ - 2: byte string (bytes)
54
+ - 3: UTF-8 string (str)
55
+ - 4: array (list)
56
+ - 5: map (dict)
57
+ - 6: semantic tag (ignored)
58
+ - 7.20: false (bool)
59
+ - 7.21: true (bool)
60
+ - 7.22: null (NoneType)
61
+ - 7.27: double-precision float (float)
62
+
63
+ Both definite-length and indefinite-length notation is supported for sized
64
+ objects (strings, arrays, maps). A ValueError is thrown if the CBOR is
65
+ invalid or contains unsupported structures."""
66
+
67
+ # Read the initial byte.
68
+ initial = cbor[offset]
69
+ typ = initial >> 5
70
+ info = initial & 0x1F
71
+ offset += 1
72
+
73
+ # Handle unsigned integer (0) and negative integer (1).
74
+ if typ <= 1:
75
+ value, offset = _cbor_read_intlike(cbor, offset, info)
76
+ if typ == 1:
77
+ value = -1 - value
78
+ return value, offset
79
+
80
+ # Handle byte string (2) and UTF-8 string (3).
81
+ if typ <= 3:
82
+
83
+ # Gather components of the string in here.
84
+ if info == 31:
85
+
86
+ # Handle indefinite length strings. These consist of a
87
+ # break-terminated (0xFF) list of definite-length strings of the
88
+ # same type.
89
+ value = []
90
+ while True:
91
+ sub_initial = cbor[offset]; offset += 1
92
+ if sub_initial == 0xFF:
93
+ break
94
+ sub_typ = sub_initial >> 5
95
+ sub_info = sub_initial & 0x1F
96
+ if sub_typ != typ:
97
+ raise ValueError('invalid CBOR: illegal indefinite-length string component')
98
+
99
+ # Seek past definite-length string component. The size in
100
+ # bytes is encoded as an integer.
101
+ size, offset = _cbor_read_intlike(cbor, offset, sub_info)
102
+ value.append(cbor[offset:offset + size])
103
+ offset += size
104
+ value = b''.join(value)
105
+
106
+ else:
107
+
108
+ # Handle definite-length strings. The size in bytes is encoded as
109
+ # an integer.
110
+ size, offset = _cbor_read_intlike(cbor, offset, info)
111
+ value = cbor[offset:offset + size]
112
+ offset += size
113
+
114
+ if typ == 3:
115
+ value = value.decode('UTF-8')
116
+ return value, offset
117
+
118
+ # Handle array (4) and map (5).
119
+ if typ <= 5:
120
+
121
+ # Create result container.
122
+ container = [] if typ == 4 else {}
123
+
124
+ # Handle indefinite length arrays and maps.
125
+ if info == 31:
126
+
127
+ # Read objects/object pairs until we encounter a break.
128
+ while cbor[offset] != 0xFF:
129
+ if typ == 4:
130
+ value, offset = _sub_cbor_to_py(cbor, offset)
131
+ container.append(value)
132
+ else:
133
+ key, offset = _sub_cbor_to_py(cbor, offset)
134
+ if not isinstance(key, str):
135
+ raise ValueError('invalid CBOR: map key is not a UTF-8 string')
136
+ value, offset = _sub_cbor_to_py(cbor, offset)
137
+ container[key] = value
138
+
139
+ # Seek past the break.
140
+ offset += 1
141
+
142
+ else:
143
+
144
+ # Handle definite-length arrays and maps. The amount of
145
+ # objects/object pairs is encoded as an integer.
146
+ size, offset = _cbor_read_intlike(cbor, offset, info)
147
+ for _ in range(size):
148
+ if typ == 4:
149
+ value, offset = _sub_cbor_to_py(cbor, offset)
150
+ container.append(value)
151
+ else:
152
+ key, offset = _sub_cbor_to_py(cbor, offset)
153
+ if not isinstance(key, str):
154
+ raise ValueError('invalid CBOR: map key is not a UTF-8 string')
155
+ value, offset = _sub_cbor_to_py(cbor, offset)
156
+ container[key] = value
157
+
158
+ return container, offset
159
+
160
+ # Handle semantic tags.
161
+ if typ == 6:
162
+
163
+ # We don't use semantic tags for anything, but ignoring them is
164
+ # legal and reading past them is easy enough.
165
+ _, offset = _cbor_read_intlike(cbor, offset, info)
166
+ return _sub_cbor_to_py(cbor, offset)
167
+
168
+ # Handle major type 7. Here, the type is defined by the additional info.
169
+ # Additional info 24 is reserved for having the type specified by the
170
+ # next byte, but all such values are unassigned.
171
+ if info == 20:
172
+ # false
173
+ return False, offset
174
+
175
+ if info == 21:
176
+ # true
177
+ return True, offset
178
+
179
+ if info == 22:
180
+ # null
181
+ return None, offset
182
+
183
+ if info == 23:
184
+ # Undefined value.
185
+ raise ValueError('invalid CBOR: undefined value is not supported')
186
+
187
+ if info == 25:
188
+ # Half-precision float.
189
+ raise ValueError('invalid CBOR: half-precision float is not supported')
190
+
191
+ if info == 26:
192
+ # Single-precision float.
193
+ raise ValueError('invalid CBOR: single-precision float is not supported')
194
+
195
+ if info == 27:
196
+ # Double-precision float.
197
+ value, = struct.unpack('>d', cbor[offset:offset+8])
198
+ return value, offset + 8
199
+
200
+ if info == 31:
201
+ # Break value used for indefinite-length objects.
202
+ raise ValueError('invalid CBOR: unexpected break')
203
+
204
+ raise ValueError('invalid CBOR: unknown type code')
205
+
206
+
207
+ def _cbor_to_py(cbor):
208
+ """Converts the given CBOR object (bytes) to its Python representation for
209
+ as far as tree-gen supports CBOR. Supported types:
210
+
211
+ - 0: unsigned integer (int)
212
+ - 1: negative integer (int)
213
+ - 2: byte string (bytes)
214
+ - 3: UTF-8 string (str)
215
+ - 4: array (list)
216
+ - 5: map (dict)
217
+ - 6: semantic tag (ignored)
218
+ - 7.20: false (bool)
219
+ - 7.21: true (bool)
220
+ - 7.22: null (NoneType)
221
+ - 7.27: double-precision float (float)
222
+
223
+ Both definite-length and indefinite-length notation is supported for sized
224
+ objects (strings, arrays, maps). A ValueError is thrown if the CBOR is
225
+ invalid or contains unsupported structures."""
226
+
227
+ value, length = _sub_cbor_to_py(cbor, 0)
228
+ if length < len(cbor):
229
+ raise ValueError('invalid CBOR: garbage at the end')
230
+ return value
231
+
232
+
233
+ class _Cbor(bytes):
234
+ """Marker class indicating that this bytes object represents CBOR."""
235
+ pass
236
+
237
+
238
+ def _cbor_write_intlike(value, major=0):
239
+ """Converts the given integer to its minimal representation in CBOR. The
240
+ major code can be overridden to write lengths for strings, arrays, and
241
+ maps."""
242
+
243
+ # Negative integers use major code 1.
244
+ if value < 0:
245
+ major = 1
246
+ value = -1 - value
247
+ initial = major << 5
248
+
249
+ # Use the minimal representation.
250
+ if value < 24:
251
+ return struct.pack('>B', initial | value)
252
+ if value < 0x100:
253
+ return struct.pack('>BB', initial | 24, value)
254
+ if value < 0x10000:
255
+ return struct.pack('>BH', initial | 25, value)
256
+ if value < 0x100000000:
257
+ return struct.pack('>BI', initial | 26, value)
258
+ if value < 0x10000000000000000:
259
+ return struct.pack('>BQ', initial | 27, value)
260
+
261
+ raise ValueError('integer too large for CBOR (bigint not supported)')
262
+
263
+
264
+ def _py_to_cbor(value, type_converter=None):
265
+ """Inverse of _cbor_to_py(). type_converter optionally specifies a function
266
+ that takes a value and either converts it to a primitive for serialization,
267
+ converts it to a _Cbor object manually, or raises a TypeError if no
268
+ conversion is known. If no type_converter is specified, a TypeError is
269
+ raised in all cases the type_converter would otherwise be called. The cbor
270
+ serialization is returned using a _Cbor object, which is just a marker class
271
+ behaving just like bytes."""
272
+ if isinstance(value, _Cbor):
273
+ return value
274
+
275
+ if isinstance(value, int):
276
+ return _Cbor(_cbor_write_intlike(value))
277
+
278
+ if isinstance(value, float):
279
+ return _Cbor(struct.pack('>Bd', 0xFB, value))
280
+
281
+ if isinstance(value, str):
282
+ value = value.encode('UTF-8')
283
+ return _Cbor(_cbor_write_intlike(len(value), 3) + value)
284
+
285
+ if isinstance(value, bytes):
286
+ return _Cbor(_cbor_write_intlike(len(value), 2) + value)
287
+
288
+ if value is False:
289
+ return _Cbor(b'\xF4')
290
+
291
+ if value is True:
292
+ return _Cbor(b'\xF5')
293
+
294
+ if value is None:
295
+ return _Cbor(b'\xF6')
296
+
297
+ if isinstance(value, (list, tuple)):
298
+ cbor = [_cbor_write_intlike(len(value), 4)]
299
+ for val in value:
300
+ cbor.append(_py_to_cbor(val, type_converter))
301
+ return _Cbor(b''.join(cbor))
302
+
303
+ if isinstance(value, dict):
304
+ cbor = [_cbor_write_intlike(len(value), 5)]
305
+ for key, val in sorted(value.items()):
306
+ if not isinstance(key, str):
307
+ raise TypeError('dict keys must be strings')
308
+ cbor.append(_py_to_cbor(key, type_converter))
309
+ cbor.append(_py_to_cbor(val, type_converter))
310
+ return _Cbor(b''.join(cbor))
311
+
312
+ if type_converter is not None:
313
+ return _py_to_cbor(type_converter(value))
314
+
315
+ raise TypeError('unsupported type for conversion to cbor: %r' % (value,))
316
+
317
+
318
+ class NotWellFormed(ValueError):
319
+ """Exception class for well-formedness checks."""
320
+
321
+ def __init__(self, msg):
322
+ super().__init__('not well-formed: ' + str(msg))
323
+
324
+
325
+ class Node(object):
326
+ """Base class for nodes."""
327
+
328
+ __slots__ = ['_annot']
329
+
330
+ def __init__(self):
331
+ super().__init__()
332
+ self._annot = {}
333
+
334
+ def __getitem__(self, key):
335
+ """Returns the annotation object with the specified key, or raises
336
+ KeyError if not found."""
337
+ if not isinstance(key, str):
338
+ raise TypeError('indexing a node with something other than an '
339
+ 'annotation key string')
340
+ return self._annot[key]
341
+
342
+ def __setitem__(self, key, val):
343
+ """Assigns the annotation object with the specified key."""
344
+ if not isinstance(key, str):
345
+ raise TypeError('indexing a node with something other than an '
346
+ 'annotation key string')
347
+ self._annot[key] = val
348
+
349
+ def __delitem__(self, key):
350
+ """Deletes the annotation object with the specified key."""
351
+ if not isinstance(key, str):
352
+ raise TypeError('indexing a node with something other than an '
353
+ 'annotation key string')
354
+ del self._annot[key]
355
+
356
+ def __contains__(self, key):
357
+ """Returns whether an annotation exists for the specified key."""
358
+ return key in self._annot
359
+
360
+ @staticmethod
361
+ def find_reachable(self, id_map=None):
362
+ """Returns a dictionary mapping Python id() values to stable sequence
363
+ numbers for all nodes in the tree rooted at this node. If id_map is
364
+ specified, found nodes are appended to it. Note that this is overridden
365
+ by the actual node class implementations; this base function does very
366
+ little."""
367
+ if id_map is None:
368
+ id_map = {}
369
+ return id_map
370
+
371
+ def check_complete(self, id_map=None):
372
+ """Raises NotWellFormed if the tree rooted at this node is not
373
+ well-formed. If id_map is specified, this tree is only a subtree in the
374
+ context of a larger tree, and id_map must be a dict mapping from Python
375
+ id() codes to tree indices for all reachable nodes. Note that this is
376
+ overridden by the actual node class implementations; this base function
377
+ always raises an exception."""
378
+ raise NotWellFormed('found node of abstract type ' + type(self).__name__)
379
+
380
+ def check_well_formed(self):
381
+ """Checks whether the tree starting at this node is well-formed. That
382
+ is:
383
+
384
+ - all One, Link, and Many edges have (at least) one entry;
385
+ - all the One entries internally stored by Any/Many have an entry;
386
+ - all Link and filled OptLink nodes link to a node that's reachable
387
+ from this node;
388
+ - the nodes referred to be One/Maybe only appear once in the tree
389
+ (except through links).
390
+
391
+ If it isn't well-formed, a NotWellFormed is thrown."""
392
+ self.check_complete()
393
+
394
+ def is_well_formed(self):
395
+ """Returns whether the tree starting at this node is well-formed. That
396
+ is:
397
+
398
+ - all One, Link, and Many edges have (at least) one entry;
399
+ - all the One entries internally stored by Any/Many have an entry;
400
+ - all Link and filled OptLink nodes link to a node that's reachable
401
+ from this node;
402
+ - the nodes referred to be One/Maybe only appear once in the tree
403
+ (except through links)."""
404
+ try:
405
+ self.check_well_formed()
406
+ return True
407
+ except NotWellFormed:
408
+ return False
409
+
410
+ def copy(self):
411
+ """Returns a shallow copy of this node. Note that this is overridden by
412
+ the actual node class implementations; this base function always raises
413
+ an exception."""
414
+ raise TypeError('can\'t copy node of abstract type ' + type(self).__name__)
415
+
416
+ def clone(self):
417
+ """Returns a deep copy of this node. Note that this is overridden by
418
+ the actual node class implementations; this base function always raises
419
+ an exception."""
420
+ raise TypeError('can\'t clone node of abstract type ' + type(self).__name__)
421
+
422
+ @classmethod
423
+ def deserialize(cls, cbor):
424
+ """Attempts to deserialize the given cbor object (either as bytes or as
425
+ its Python primitive representation) into a node of this type."""
426
+ if isinstance(cbor, bytes):
427
+ cbor = _cbor_to_py(cbor)
428
+ seq_to_ob = {}
429
+ links = []
430
+ root = cls._deserialize(cbor, seq_to_ob, links)
431
+ for link_setter, seq in links:
432
+ ob = seq_to_ob.get(seq, None)
433
+ if ob is None:
434
+ raise ValueError('found link to nonexistent object')
435
+ link_setter(ob)
436
+ return root
437
+
438
+ def serialize(self):
439
+ """Serializes this node into its cbor representation in the form of a
440
+ bytes object."""
441
+ id_map = self.find_reachable()
442
+ self.check_complete(id_map)
443
+ return _py_to_cbor(self._serialize(id_map))
444
+
445
+ @staticmethod
446
+ def _deserialize(cbor, seq_to_ob, links):
447
+ if not isinstance(cbor, dict):
448
+ raise TypeError('node description object must be a dict')
449
+ typ = cbor.get('@t', None)
450
+ if typ is None:
451
+ raise ValueError('type (@t) field is missing from node serialization')
452
+ node_type = _typemap.get(cbor.get('@t'), None)
453
+ if node_type is None:
454
+ raise ValueError('unknown node type (@t): ' + str(cbor.get('@t')))
455
+ return node_type._deserialize(cbor, seq_to_ob, links)
456
+
457
+
458
+ @functools.total_ordering
459
+ class _Multiple(object):
460
+ """Base class for the Any* and Many* edge helper classes. Inheriting
461
+ classes must set the class constant _T to the node type they are made
462
+ for."""
463
+
464
+ __slots__ = ['_l']
465
+
466
+ def __init__(self, *args, **kwargs):
467
+ super().__init__()
468
+ self._l = list(*args, **kwargs)
469
+ for idx, val in enumerate(self._l):
470
+ if not isinstance(val, self._T):
471
+ raise TypeError(
472
+ 'object {!r} at index {:d} is not an instance of {!r}'
473
+ .format(val, idx, self._T))
474
+
475
+ def __repr__(self):
476
+ return '{}({!r})'.format(type(self).__name__, self._l)
477
+
478
+ def clone(self):
479
+ return self.__class__(map(lambda node: node.clone(), self._l))
480
+
481
+ def __len__(self):
482
+ return len(self._l)
483
+
484
+ def __getitem__(self, idx):
485
+ return self._l[idx]
486
+
487
+ def __setitem__(self, idx, val):
488
+ if not isinstance(val, self._T):
489
+ raise TypeError(
490
+ 'object {!r} is not an instance of {!r}'
491
+ .format(val, idx, self._T))
492
+ self._l[idx] = val
493
+
494
+ def __delitem__(self, idx):
495
+ del self._l[idx]
496
+
497
+ def __iter__(self):
498
+ return iter(self._l)
499
+
500
+ def __reversed__(self):
501
+ return reversed(self._l)
502
+
503
+ def __contains__(self, val):
504
+ return val in self._l
505
+
506
+ def append(self, val):
507
+ if not isinstance(val, self._T):
508
+ raise TypeError(
509
+ 'object {!r} is not an instance of {!r}'
510
+ .format(val, self._T))
511
+ self._l.append(val)
512
+
513
+ def extend(self, iterable):
514
+ for val in iterable:
515
+ self.append(val)
516
+
517
+ def insert(self, idx, val):
518
+ if not isinstance(val, self._T):
519
+ raise TypeError(
520
+ 'object {!r} is not an instance of {!r}'
521
+ .format(val, self._T))
522
+ self._l.insert(idx, val)
523
+
524
+ def remote(self, val):
525
+ self._l.remove(val)
526
+
527
+ def pop(self, idx=-1):
528
+ return self._l.pop(idx)
529
+
530
+ def clear(self):
531
+ self._l.clear()
532
+
533
+ def idx(self, val, start=0, end=-1):
534
+ return self._l.idx(val, start, end)
535
+
536
+ def count(self, val):
537
+ return self._l.count(val)
538
+
539
+ def sort(self, key=None, reverse=False):
540
+ self._l.sort(key=key, reverse=reverse)
541
+
542
+ def reverse(self):
543
+ self._l.reverse()
544
+
545
+ def copy(self):
546
+ return self.__class__(self)
547
+
548
+ def __eq__(self, other):
549
+ if not isinstance(other, _Multiple):
550
+ return False
551
+ return self._l == other._l
552
+
553
+ def __lt__(self, other):
554
+ return self._l < other._l
555
+
556
+ def __iadd__(self, other):
557
+ self.extend(other)
558
+
559
+ def __add__(self, other):
560
+ copy = self.copy()
561
+ copy += other
562
+ return copy
563
+
564
+ def __imul__(self, other):
565
+ self._l *= other
566
+
567
+ def __mul__(self, other):
568
+ copy = self.copy()
569
+ copy *= other
570
+ return copy
571
+
572
+ def __rmul__(self, other):
573
+ copy = self.copy()
574
+ copy *= other
575
+ return copy
576
+
577
+
578
+ class MultiNode(_Multiple):
579
+ """Wrapper for an edge with multiple Node objects."""
580
+
581
+ _T = Node
582
+
583
+
584
+ def _cloned(obj):
585
+ """Attempts to clone the given object by calling its clone() method, if it
586
+ has one."""
587
+ if hasattr(obj, 'clone'):
588
+ return obj.clone()
589
+ return obj
590
+
591
+
592
+ class ValueBase(Node):
593
+ __slots__ = []
594
+
595
+ def __init__(self):
596
+ super().__init__()
597
+
598
+ @staticmethod
599
+ def _deserialize(cbor, seq_to_ob, links):
600
+ """Attempts to deserialize the given cbor object (in Python primitive
601
+ representation) into a node of this type. All (sub)nodes are added to
602
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
603
+ registered in the links list by means of a two-tuple of the setter
604
+ function for the link field and the sequence number of the target node.
605
+ """
606
+ if not isinstance(cbor, dict):
607
+ raise TypeError('node description object must be a dict')
608
+ typ = cbor.get('@t', None)
609
+ if typ is None:
610
+ raise ValueError('type (@t) field is missing from node serialization')
611
+ if typ == 'ConstBool':
612
+ return ConstBool._deserialize(cbor, seq_to_ob, links)
613
+ if typ == 'ConstInt':
614
+ return ConstInt._deserialize(cbor, seq_to_ob, links)
615
+ if typ == 'ConstFloat':
616
+ return ConstFloat._deserialize(cbor, seq_to_ob, links)
617
+ if typ == 'IndexRef':
618
+ return IndexRef._deserialize(cbor, seq_to_ob, links)
619
+ if typ == 'VariableRef':
620
+ return VariableRef._deserialize(cbor, seq_to_ob, links)
621
+ raise ValueError('unknown or unexpected type (@t) found in node serialization')
622
+
623
+ def _serialize(self, id_map):
624
+ """Serializes this node to the Python primitive representation of its
625
+ CBOR serialization. The tree that the node belongs to must be
626
+ well-formed. id_map must match Python id() calls for all nodes to unique
627
+ integers, to use for the sequence number representation of links."""
628
+ cbor = {'@i': id_map[id(self)], '@t': 'ValueBase'}
629
+
630
+ # Serialize annotations.
631
+ for key, val in self._annot.items():
632
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
633
+
634
+ return cbor
635
+
636
+
637
+ class MultiValueBase(_Multiple):
638
+ """Wrapper for an edge with multiple ValueBase objects."""
639
+
640
+ _T = ValueBase
641
+
642
+
643
+ _typemap['ValueBase'] = ValueBase
644
+
645
+ class Constant(ValueBase):
646
+ """Constant value"""
647
+
648
+ __slots__ = []
649
+
650
+ def __init__(self):
651
+ super().__init__()
652
+
653
+ @staticmethod
654
+ def _deserialize(cbor, seq_to_ob, links):
655
+ """Attempts to deserialize the given cbor object (in Python primitive
656
+ representation) into a node of this type. All (sub)nodes are added to
657
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
658
+ registered in the links list by means of a two-tuple of the setter
659
+ function for the link field and the sequence number of the target node.
660
+ """
661
+ if not isinstance(cbor, dict):
662
+ raise TypeError('node description object must be a dict')
663
+ typ = cbor.get('@t', None)
664
+ if typ is None:
665
+ raise ValueError('type (@t) field is missing from node serialization')
666
+ if typ == 'ConstBool':
667
+ return ConstBool._deserialize(cbor, seq_to_ob, links)
668
+ if typ == 'ConstInt':
669
+ return ConstInt._deserialize(cbor, seq_to_ob, links)
670
+ if typ == 'ConstFloat':
671
+ return ConstFloat._deserialize(cbor, seq_to_ob, links)
672
+ raise ValueError('unknown or unexpected type (@t) found in node serialization')
673
+
674
+ def _serialize(self, id_map):
675
+ """Serializes this node to the Python primitive representation of its
676
+ CBOR serialization. The tree that the node belongs to must be
677
+ well-formed. id_map must match Python id() calls for all nodes to unique
678
+ integers, to use for the sequence number representation of links."""
679
+ cbor = {'@i': id_map[id(self)], '@t': 'Constant'}
680
+
681
+ # Serialize annotations.
682
+ for key, val in self._annot.items():
683
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
684
+
685
+ return cbor
686
+
687
+
688
+ class MultiConstant(_Multiple):
689
+ """Wrapper for an edge with multiple Constant objects."""
690
+
691
+ _T = Constant
692
+
693
+
694
+ _typemap['Constant'] = Constant
695
+
696
+ class ConstBool(Constant):
697
+ __slots__ = [
698
+ '_attr_value',
699
+ ]
700
+
701
+ def __init__(
702
+ self,
703
+ value=None,
704
+ ):
705
+ super().__init__()
706
+ self.value = value
707
+
708
+ @property
709
+ def value(self):
710
+ return self._attr_value
711
+
712
+ @value.setter
713
+ def value(self, val):
714
+ if val is None:
715
+ del self.value
716
+ return
717
+ if not isinstance(val, cqasm.v3x.primitives.Bool):
718
+ # Try to "typecast" if this isn't an obvious mistake.
719
+ if isinstance(val, Node):
720
+ raise TypeError('value must be of type cqasm.v3x.primitives.Bool')
721
+ val = cqasm.v3x.primitives.Bool(val)
722
+ self._attr_value = val
723
+
724
+ @value.deleter
725
+ def value(self):
726
+ self._attr_value = cqasm.v3x.primitives.Bool()
727
+
728
+ def __eq__(self, other):
729
+ """Equality operator. Ignores annotations!"""
730
+ if not isinstance(other, ConstBool):
731
+ return False
732
+ if self.value != other.value:
733
+ return False
734
+ return True
735
+
736
+ def dump(self, indent=0, annotations=None, links=1):
737
+ """Returns a debug representation of this tree as a multiline string.
738
+ indent is the number of double spaces prefixed before every line.
739
+ annotations, if specified, must be a set-like object containing the key
740
+ strings of the annotations that are to be printed. links specifies the
741
+ maximum link recursion depth."""
742
+ s = [' '*indent]
743
+ s.append('ConstBool(')
744
+ if annotations is None:
745
+ annotations = []
746
+ for key in annotations:
747
+ if key in self:
748
+ s.append(' # {}: {}'.format(key, self[key]))
749
+ s.append('\n')
750
+ indent += 1
751
+ s.append(' '*indent)
752
+ s.append('value: ')
753
+ s.append(str(self.value) + '\n')
754
+ indent -= 1
755
+ s.append(' '*indent)
756
+ s.append(')')
757
+ return ''.join(s)
758
+
759
+ __str__ = dump
760
+ __repr__ = dump
761
+
762
+ def find_reachable(self, id_map=None):
763
+ """Returns a dictionary mapping Python id() values to stable sequence
764
+ numbers for all nodes in the tree rooted at this node. If id_map is
765
+ specified, found nodes are appended to it."""
766
+ if id_map is None:
767
+ id_map = {}
768
+ if id(self) in id_map:
769
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
770
+ id_map[id(self)] = len(id_map)
771
+ return id_map
772
+
773
+ def check_complete(self, id_map=None):
774
+ """Raises NotWellFormed if the tree rooted at this node is not
775
+ well-formed. If id_map is specified, this tree is only a subtree in the
776
+ context of a larger tree, and id_map must be a dict mapping from Python
777
+ id() codes to tree indices for all reachable nodes."""
778
+ if id_map is None:
779
+ id_map = self.find_reachable()
780
+
781
+ def copy(self):
782
+ """Returns a shallow copy of this node."""
783
+ return ConstBool(
784
+ value=self._attr_value
785
+ )
786
+
787
+ def clone(self):
788
+ """Returns a deep copy of this node. This mimics the C++ interface,
789
+ deficiencies with links included; that is, links always point to the
790
+ original tree. If you're not cloning a subtree in a context where this
791
+ is the desired behavior, you may want to use the copy.deepcopy() from
792
+ the stdlib instead, which should copy links correctly."""
793
+ return ConstBool(
794
+ value=_cloned(self._attr_value)
795
+ )
796
+
797
+ @staticmethod
798
+ def _deserialize(cbor, seq_to_ob, links):
799
+ """Attempts to deserialize the given cbor object (in Python primitive
800
+ representation) into a node of this type. All (sub)nodes are added to
801
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
802
+ registered in the links list by means of a two-tuple of the setter
803
+ function for the link field and the sequence number of the target node.
804
+ """
805
+ if not isinstance(cbor, dict):
806
+ raise TypeError('node description object must be a dict')
807
+ typ = cbor.get('@t', None)
808
+ if typ is None:
809
+ raise ValueError('type (@t) field is missing from node serialization')
810
+ if typ != 'ConstBool':
811
+ raise ValueError('found node serialization for ' + typ + ', but expected ConstBool')
812
+
813
+ # Deserialize the value field.
814
+ field = cbor.get('value', None)
815
+ if not isinstance(field, dict):
816
+ raise ValueError('missing or invalid serialization of field value')
817
+ if hasattr(cqasm.v3x.primitives.Bool, 'deserialize_cbor'):
818
+ f_value = cqasm.v3x.primitives.Bool.deserialize_cbor(field)
819
+ else:
820
+ f_value = cqasm.v3x.primitives.deserialize(cqasm.v3x.primitives.Bool, field)
821
+
822
+ # Construct the ConstBool node.
823
+ node = ConstBool(f_value)
824
+
825
+ # Deserialize annotations.
826
+ for key, val in cbor.items():
827
+ if not (key.startswith('{') and key.endswith('}')):
828
+ continue
829
+ key = key[1:-1]
830
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
831
+
832
+ # Register node in sequence number lookup.
833
+ seq = cbor.get('@i', None)
834
+ if not isinstance(seq, int):
835
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
836
+ if seq in seq_to_ob:
837
+ raise ValueError('duplicate sequence number %d' % seq)
838
+ seq_to_ob[seq] = node
839
+
840
+ return node
841
+
842
+ def _serialize(self, id_map):
843
+ """Serializes this node to the Python primitive representation of its
844
+ CBOR serialization. The tree that the node belongs to must be
845
+ well-formed. id_map must match Python id() calls for all nodes to unique
846
+ integers, to use for the sequence number representation of links."""
847
+ cbor = {'@i': id_map[id(self)], '@t': 'ConstBool'}
848
+
849
+ # Serialize the value field.
850
+ if hasattr(self._attr_value, 'serialize_cbor'):
851
+ cbor['value'] = self._attr_value.serialize_cbor()
852
+ else:
853
+ cbor['value'] = cqasm.v3x.primitives.serialize(cqasm.v3x.primitives.Bool, self._attr_value)
854
+
855
+ # Serialize annotations.
856
+ for key, val in self._annot.items():
857
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
858
+
859
+ return cbor
860
+
861
+
862
+ class MultiConstBool(_Multiple):
863
+ """Wrapper for an edge with multiple ConstBool objects."""
864
+
865
+ _T = ConstBool
866
+
867
+
868
+ _typemap['ConstBool'] = ConstBool
869
+
870
+ class ConstFloat(Constant):
871
+ __slots__ = [
872
+ '_attr_value',
873
+ ]
874
+
875
+ def __init__(
876
+ self,
877
+ value=None,
878
+ ):
879
+ super().__init__()
880
+ self.value = value
881
+
882
+ @property
883
+ def value(self):
884
+ return self._attr_value
885
+
886
+ @value.setter
887
+ def value(self, val):
888
+ if val is None:
889
+ del self.value
890
+ return
891
+ if not isinstance(val, cqasm.v3x.primitives.Float):
892
+ # Try to "typecast" if this isn't an obvious mistake.
893
+ if isinstance(val, Node):
894
+ raise TypeError('value must be of type cqasm.v3x.primitives.Float')
895
+ val = cqasm.v3x.primitives.Float(val)
896
+ self._attr_value = val
897
+
898
+ @value.deleter
899
+ def value(self):
900
+ self._attr_value = cqasm.v3x.primitives.Float()
901
+
902
+ def __eq__(self, other):
903
+ """Equality operator. Ignores annotations!"""
904
+ if not isinstance(other, ConstFloat):
905
+ return False
906
+ if self.value != other.value:
907
+ return False
908
+ return True
909
+
910
+ def dump(self, indent=0, annotations=None, links=1):
911
+ """Returns a debug representation of this tree as a multiline string.
912
+ indent is the number of double spaces prefixed before every line.
913
+ annotations, if specified, must be a set-like object containing the key
914
+ strings of the annotations that are to be printed. links specifies the
915
+ maximum link recursion depth."""
916
+ s = [' '*indent]
917
+ s.append('ConstFloat(')
918
+ if annotations is None:
919
+ annotations = []
920
+ for key in annotations:
921
+ if key in self:
922
+ s.append(' # {}: {}'.format(key, self[key]))
923
+ s.append('\n')
924
+ indent += 1
925
+ s.append(' '*indent)
926
+ s.append('value: ')
927
+ s.append(str(self.value) + '\n')
928
+ indent -= 1
929
+ s.append(' '*indent)
930
+ s.append(')')
931
+ return ''.join(s)
932
+
933
+ __str__ = dump
934
+ __repr__ = dump
935
+
936
+ def find_reachable(self, id_map=None):
937
+ """Returns a dictionary mapping Python id() values to stable sequence
938
+ numbers for all nodes in the tree rooted at this node. If id_map is
939
+ specified, found nodes are appended to it."""
940
+ if id_map is None:
941
+ id_map = {}
942
+ if id(self) in id_map:
943
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
944
+ id_map[id(self)] = len(id_map)
945
+ return id_map
946
+
947
+ def check_complete(self, id_map=None):
948
+ """Raises NotWellFormed if the tree rooted at this node is not
949
+ well-formed. If id_map is specified, this tree is only a subtree in the
950
+ context of a larger tree, and id_map must be a dict mapping from Python
951
+ id() codes to tree indices for all reachable nodes."""
952
+ if id_map is None:
953
+ id_map = self.find_reachable()
954
+
955
+ def copy(self):
956
+ """Returns a shallow copy of this node."""
957
+ return ConstFloat(
958
+ value=self._attr_value
959
+ )
960
+
961
+ def clone(self):
962
+ """Returns a deep copy of this node. This mimics the C++ interface,
963
+ deficiencies with links included; that is, links always point to the
964
+ original tree. If you're not cloning a subtree in a context where this
965
+ is the desired behavior, you may want to use the copy.deepcopy() from
966
+ the stdlib instead, which should copy links correctly."""
967
+ return ConstFloat(
968
+ value=_cloned(self._attr_value)
969
+ )
970
+
971
+ @staticmethod
972
+ def _deserialize(cbor, seq_to_ob, links):
973
+ """Attempts to deserialize the given cbor object (in Python primitive
974
+ representation) into a node of this type. All (sub)nodes are added to
975
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
976
+ registered in the links list by means of a two-tuple of the setter
977
+ function for the link field and the sequence number of the target node.
978
+ """
979
+ if not isinstance(cbor, dict):
980
+ raise TypeError('node description object must be a dict')
981
+ typ = cbor.get('@t', None)
982
+ if typ is None:
983
+ raise ValueError('type (@t) field is missing from node serialization')
984
+ if typ != 'ConstFloat':
985
+ raise ValueError('found node serialization for ' + typ + ', but expected ConstFloat')
986
+
987
+ # Deserialize the value field.
988
+ field = cbor.get('value', None)
989
+ if not isinstance(field, dict):
990
+ raise ValueError('missing or invalid serialization of field value')
991
+ if hasattr(cqasm.v3x.primitives.Float, 'deserialize_cbor'):
992
+ f_value = cqasm.v3x.primitives.Float.deserialize_cbor(field)
993
+ else:
994
+ f_value = cqasm.v3x.primitives.deserialize(cqasm.v3x.primitives.Float, field)
995
+
996
+ # Construct the ConstFloat node.
997
+ node = ConstFloat(f_value)
998
+
999
+ # Deserialize annotations.
1000
+ for key, val in cbor.items():
1001
+ if not (key.startswith('{') and key.endswith('}')):
1002
+ continue
1003
+ key = key[1:-1]
1004
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
1005
+
1006
+ # Register node in sequence number lookup.
1007
+ seq = cbor.get('@i', None)
1008
+ if not isinstance(seq, int):
1009
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
1010
+ if seq in seq_to_ob:
1011
+ raise ValueError('duplicate sequence number %d' % seq)
1012
+ seq_to_ob[seq] = node
1013
+
1014
+ return node
1015
+
1016
+ def _serialize(self, id_map):
1017
+ """Serializes this node to the Python primitive representation of its
1018
+ CBOR serialization. The tree that the node belongs to must be
1019
+ well-formed. id_map must match Python id() calls for all nodes to unique
1020
+ integers, to use for the sequence number representation of links."""
1021
+ cbor = {'@i': id_map[id(self)], '@t': 'ConstFloat'}
1022
+
1023
+ # Serialize the value field.
1024
+ if hasattr(self._attr_value, 'serialize_cbor'):
1025
+ cbor['value'] = self._attr_value.serialize_cbor()
1026
+ else:
1027
+ cbor['value'] = cqasm.v3x.primitives.serialize(cqasm.v3x.primitives.Float, self._attr_value)
1028
+
1029
+ # Serialize annotations.
1030
+ for key, val in self._annot.items():
1031
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
1032
+
1033
+ return cbor
1034
+
1035
+
1036
+ class MultiConstFloat(_Multiple):
1037
+ """Wrapper for an edge with multiple ConstFloat objects."""
1038
+
1039
+ _T = ConstFloat
1040
+
1041
+
1042
+ _typemap['ConstFloat'] = ConstFloat
1043
+
1044
+ class ConstInt(Constant):
1045
+ __slots__ = [
1046
+ '_attr_value',
1047
+ ]
1048
+
1049
+ def __init__(
1050
+ self,
1051
+ value=None,
1052
+ ):
1053
+ super().__init__()
1054
+ self.value = value
1055
+
1056
+ @property
1057
+ def value(self):
1058
+ return self._attr_value
1059
+
1060
+ @value.setter
1061
+ def value(self, val):
1062
+ if val is None:
1063
+ del self.value
1064
+ return
1065
+ if not isinstance(val, cqasm.v3x.primitives.Int):
1066
+ # Try to "typecast" if this isn't an obvious mistake.
1067
+ if isinstance(val, Node):
1068
+ raise TypeError('value must be of type cqasm.v3x.primitives.Int')
1069
+ val = cqasm.v3x.primitives.Int(val)
1070
+ self._attr_value = val
1071
+
1072
+ @value.deleter
1073
+ def value(self):
1074
+ self._attr_value = cqasm.v3x.primitives.Int()
1075
+
1076
+ def __eq__(self, other):
1077
+ """Equality operator. Ignores annotations!"""
1078
+ if not isinstance(other, ConstInt):
1079
+ return False
1080
+ if self.value != other.value:
1081
+ return False
1082
+ return True
1083
+
1084
+ def dump(self, indent=0, annotations=None, links=1):
1085
+ """Returns a debug representation of this tree as a multiline string.
1086
+ indent is the number of double spaces prefixed before every line.
1087
+ annotations, if specified, must be a set-like object containing the key
1088
+ strings of the annotations that are to be printed. links specifies the
1089
+ maximum link recursion depth."""
1090
+ s = [' '*indent]
1091
+ s.append('ConstInt(')
1092
+ if annotations is None:
1093
+ annotations = []
1094
+ for key in annotations:
1095
+ if key in self:
1096
+ s.append(' # {}: {}'.format(key, self[key]))
1097
+ s.append('\n')
1098
+ indent += 1
1099
+ s.append(' '*indent)
1100
+ s.append('value: ')
1101
+ s.append(str(self.value) + '\n')
1102
+ indent -= 1
1103
+ s.append(' '*indent)
1104
+ s.append(')')
1105
+ return ''.join(s)
1106
+
1107
+ __str__ = dump
1108
+ __repr__ = dump
1109
+
1110
+ def find_reachable(self, id_map=None):
1111
+ """Returns a dictionary mapping Python id() values to stable sequence
1112
+ numbers for all nodes in the tree rooted at this node. If id_map is
1113
+ specified, found nodes are appended to it."""
1114
+ if id_map is None:
1115
+ id_map = {}
1116
+ if id(self) in id_map:
1117
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
1118
+ id_map[id(self)] = len(id_map)
1119
+ return id_map
1120
+
1121
+ def check_complete(self, id_map=None):
1122
+ """Raises NotWellFormed if the tree rooted at this node is not
1123
+ well-formed. If id_map is specified, this tree is only a subtree in the
1124
+ context of a larger tree, and id_map must be a dict mapping from Python
1125
+ id() codes to tree indices for all reachable nodes."""
1126
+ if id_map is None:
1127
+ id_map = self.find_reachable()
1128
+
1129
+ def copy(self):
1130
+ """Returns a shallow copy of this node."""
1131
+ return ConstInt(
1132
+ value=self._attr_value
1133
+ )
1134
+
1135
+ def clone(self):
1136
+ """Returns a deep copy of this node. This mimics the C++ interface,
1137
+ deficiencies with links included; that is, links always point to the
1138
+ original tree. If you're not cloning a subtree in a context where this
1139
+ is the desired behavior, you may want to use the copy.deepcopy() from
1140
+ the stdlib instead, which should copy links correctly."""
1141
+ return ConstInt(
1142
+ value=_cloned(self._attr_value)
1143
+ )
1144
+
1145
+ @staticmethod
1146
+ def _deserialize(cbor, seq_to_ob, links):
1147
+ """Attempts to deserialize the given cbor object (in Python primitive
1148
+ representation) into a node of this type. All (sub)nodes are added to
1149
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
1150
+ registered in the links list by means of a two-tuple of the setter
1151
+ function for the link field and the sequence number of the target node.
1152
+ """
1153
+ if not isinstance(cbor, dict):
1154
+ raise TypeError('node description object must be a dict')
1155
+ typ = cbor.get('@t', None)
1156
+ if typ is None:
1157
+ raise ValueError('type (@t) field is missing from node serialization')
1158
+ if typ != 'ConstInt':
1159
+ raise ValueError('found node serialization for ' + typ + ', but expected ConstInt')
1160
+
1161
+ # Deserialize the value field.
1162
+ field = cbor.get('value', None)
1163
+ if not isinstance(field, dict):
1164
+ raise ValueError('missing or invalid serialization of field value')
1165
+ if hasattr(cqasm.v3x.primitives.Int, 'deserialize_cbor'):
1166
+ f_value = cqasm.v3x.primitives.Int.deserialize_cbor(field)
1167
+ else:
1168
+ f_value = cqasm.v3x.primitives.deserialize(cqasm.v3x.primitives.Int, field)
1169
+
1170
+ # Construct the ConstInt node.
1171
+ node = ConstInt(f_value)
1172
+
1173
+ # Deserialize annotations.
1174
+ for key, val in cbor.items():
1175
+ if not (key.startswith('{') and key.endswith('}')):
1176
+ continue
1177
+ key = key[1:-1]
1178
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
1179
+
1180
+ # Register node in sequence number lookup.
1181
+ seq = cbor.get('@i', None)
1182
+ if not isinstance(seq, int):
1183
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
1184
+ if seq in seq_to_ob:
1185
+ raise ValueError('duplicate sequence number %d' % seq)
1186
+ seq_to_ob[seq] = node
1187
+
1188
+ return node
1189
+
1190
+ def _serialize(self, id_map):
1191
+ """Serializes this node to the Python primitive representation of its
1192
+ CBOR serialization. The tree that the node belongs to must be
1193
+ well-formed. id_map must match Python id() calls for all nodes to unique
1194
+ integers, to use for the sequence number representation of links."""
1195
+ cbor = {'@i': id_map[id(self)], '@t': 'ConstInt'}
1196
+
1197
+ # Serialize the value field.
1198
+ if hasattr(self._attr_value, 'serialize_cbor'):
1199
+ cbor['value'] = self._attr_value.serialize_cbor()
1200
+ else:
1201
+ cbor['value'] = cqasm.v3x.primitives.serialize(cqasm.v3x.primitives.Int, self._attr_value)
1202
+
1203
+ # Serialize annotations.
1204
+ for key, val in self._annot.items():
1205
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
1206
+
1207
+ return cbor
1208
+
1209
+
1210
+ class MultiConstInt(_Multiple):
1211
+ """Wrapper for an edge with multiple ConstInt objects."""
1212
+
1213
+ _T = ConstInt
1214
+
1215
+
1216
+ _typemap['ConstInt'] = ConstInt
1217
+
1218
+ class Reference(ValueBase):
1219
+ """Reference to some storage location"""
1220
+
1221
+ __slots__ = []
1222
+
1223
+ def __init__(self):
1224
+ super().__init__()
1225
+
1226
+ @staticmethod
1227
+ def _deserialize(cbor, seq_to_ob, links):
1228
+ """Attempts to deserialize the given cbor object (in Python primitive
1229
+ representation) into a node of this type. All (sub)nodes are added to
1230
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
1231
+ registered in the links list by means of a two-tuple of the setter
1232
+ function for the link field and the sequence number of the target node.
1233
+ """
1234
+ if not isinstance(cbor, dict):
1235
+ raise TypeError('node description object must be a dict')
1236
+ typ = cbor.get('@t', None)
1237
+ if typ is None:
1238
+ raise ValueError('type (@t) field is missing from node serialization')
1239
+ if typ == 'IndexRef':
1240
+ return IndexRef._deserialize(cbor, seq_to_ob, links)
1241
+ if typ == 'VariableRef':
1242
+ return VariableRef._deserialize(cbor, seq_to_ob, links)
1243
+ raise ValueError('unknown or unexpected type (@t) found in node serialization')
1244
+
1245
+ def _serialize(self, id_map):
1246
+ """Serializes this node to the Python primitive representation of its
1247
+ CBOR serialization. The tree that the node belongs to must be
1248
+ well-formed. id_map must match Python id() calls for all nodes to unique
1249
+ integers, to use for the sequence number representation of links."""
1250
+ cbor = {'@i': id_map[id(self)], '@t': 'Reference'}
1251
+
1252
+ # Serialize annotations.
1253
+ for key, val in self._annot.items():
1254
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
1255
+
1256
+ return cbor
1257
+
1258
+
1259
+ class MultiReference(_Multiple):
1260
+ """Wrapper for an edge with multiple Reference objects."""
1261
+
1262
+ _T = Reference
1263
+
1264
+
1265
+ _typemap['Reference'] = Reference
1266
+
1267
+ class IndexRef(Reference):
1268
+ """Represents an index for single-gate-multiple-qubit notation. The indices
1269
+ must not repeat."""
1270
+
1271
+ __slots__ = [
1272
+ '_attr_variable',
1273
+ '_attr_indices',
1274
+ ]
1275
+
1276
+ def __init__(
1277
+ self,
1278
+ variable=None,
1279
+ indices=None,
1280
+ ):
1281
+ super().__init__()
1282
+ self.variable = variable
1283
+ self.indices = indices
1284
+
1285
+ @property
1286
+ def variable(self):
1287
+ return self._attr_variable
1288
+
1289
+ @variable.setter
1290
+ def variable(self, val):
1291
+ if val is None:
1292
+ del self.variable
1293
+ return
1294
+ if not isinstance(val, cqasm.v3x.semantic.Variable):
1295
+ raise TypeError('variable must be of type cqasm.v3x.semantic.Variable')
1296
+ self._attr_variable = val
1297
+
1298
+ @variable.deleter
1299
+ def variable(self):
1300
+ self._attr_variable = None
1301
+
1302
+ @property
1303
+ def indices(self):
1304
+ return self._attr_indices
1305
+
1306
+ @indices.setter
1307
+ def indices(self, val):
1308
+ if val is None:
1309
+ del self.indices
1310
+ return
1311
+ if not isinstance(val, MultiConstInt):
1312
+ # Try to "typecast" if this isn't an obvious mistake.
1313
+ if isinstance(val, Node):
1314
+ raise TypeError('indices must be of type MultiConstInt')
1315
+ val = MultiConstInt(val)
1316
+ self._attr_indices = val
1317
+
1318
+ @indices.deleter
1319
+ def indices(self):
1320
+ self._attr_indices = MultiConstInt()
1321
+
1322
+ def __eq__(self, other):
1323
+ """Equality operator. Ignores annotations!"""
1324
+ if not isinstance(other, IndexRef):
1325
+ return False
1326
+ if self.variable is not other.variable:
1327
+ return False
1328
+ if self.indices != other.indices:
1329
+ return False
1330
+ return True
1331
+
1332
+ def dump(self, indent=0, annotations=None, links=1):
1333
+ """Returns a debug representation of this tree as a multiline string.
1334
+ indent is the number of double spaces prefixed before every line.
1335
+ annotations, if specified, must be a set-like object containing the key
1336
+ strings of the annotations that are to be printed. links specifies the
1337
+ maximum link recursion depth."""
1338
+ s = [' '*indent]
1339
+ s.append('IndexRef(')
1340
+ if annotations is None:
1341
+ annotations = []
1342
+ for key in annotations:
1343
+ if key in self:
1344
+ s.append(' # {}: {}'.format(key, self[key]))
1345
+ s.append('\n')
1346
+ indent += 1
1347
+ s.append(' '*indent)
1348
+ s.append('variable --> ')
1349
+ if self.variable is None:
1350
+ s.append('!MISSING\n')
1351
+ else:
1352
+ s.append('<\n')
1353
+ if links:
1354
+ s.append(self.variable.dump(indent + 1, annotations, links - 1) + '\n')
1355
+ else:
1356
+ s.append(' '*(indent+1) + '...\n')
1357
+ s.append(' '*indent + '>\n')
1358
+ s.append(' '*indent)
1359
+ s.append('indices: ')
1360
+ if not self.indices:
1361
+ s.append('!MISSING\n')
1362
+ else:
1363
+ s.append('[\n')
1364
+ for child in self.indices:
1365
+ s.append(child.dump(indent + 1, annotations, links) + '\n')
1366
+ s.append(' '*indent + ']\n')
1367
+ indent -= 1
1368
+ s.append(' '*indent)
1369
+ s.append(')')
1370
+ return ''.join(s)
1371
+
1372
+ __str__ = dump
1373
+ __repr__ = dump
1374
+
1375
+ def find_reachable(self, id_map=None):
1376
+ """Returns a dictionary mapping Python id() values to stable sequence
1377
+ numbers for all nodes in the tree rooted at this node. If id_map is
1378
+ specified, found nodes are appended to it."""
1379
+ if id_map is None:
1380
+ id_map = {}
1381
+ if id(self) in id_map:
1382
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
1383
+ id_map[id(self)] = len(id_map)
1384
+ for el in self._attr_indices:
1385
+ el.find_reachable(id_map)
1386
+ return id_map
1387
+
1388
+ def check_complete(self, id_map=None):
1389
+ """Raises NotWellFormed if the tree rooted at this node is not
1390
+ well-formed. If id_map is specified, this tree is only a subtree in the
1391
+ context of a larger tree, and id_map must be a dict mapping from Python
1392
+ id() codes to tree indices for all reachable nodes."""
1393
+ if id_map is None:
1394
+ id_map = self.find_reachable()
1395
+ if self._attr_variable is None:
1396
+ raise NotWellFormed('variable is required but not set')
1397
+ if self._attr_variable is not None:
1398
+ if id(self._attr_variable) not in id_map:
1399
+ raise NotWellFormed('variable links to unreachable node')
1400
+ if not self._attr_indices:
1401
+ raise NotWellFormed('indices needs at least one node but has zero')
1402
+ for child in self._attr_indices:
1403
+ child.check_complete(id_map)
1404
+
1405
+ def copy(self):
1406
+ """Returns a shallow copy of this node."""
1407
+ return IndexRef(
1408
+ variable=self._attr_variable,
1409
+ indices=self._attr_indices.copy()
1410
+ )
1411
+
1412
+ def clone(self):
1413
+ """Returns a deep copy of this node. This mimics the C++ interface,
1414
+ deficiencies with links included; that is, links always point to the
1415
+ original tree. If you're not cloning a subtree in a context where this
1416
+ is the desired behavior, you may want to use the copy.deepcopy() from
1417
+ the stdlib instead, which should copy links correctly."""
1418
+ return IndexRef(
1419
+ variable=self._attr_variable,
1420
+ indices=_cloned(self._attr_indices)
1421
+ )
1422
+
1423
+ @staticmethod
1424
+ def _deserialize(cbor, seq_to_ob, links):
1425
+ """Attempts to deserialize the given cbor object (in Python primitive
1426
+ representation) into a node of this type. All (sub)nodes are added to
1427
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
1428
+ registered in the links list by means of a two-tuple of the setter
1429
+ function for the link field and the sequence number of the target node.
1430
+ """
1431
+ if not isinstance(cbor, dict):
1432
+ raise TypeError('node description object must be a dict')
1433
+ typ = cbor.get('@t', None)
1434
+ if typ is None:
1435
+ raise ValueError('type (@t) field is missing from node serialization')
1436
+ if typ != 'IndexRef':
1437
+ raise ValueError('found node serialization for ' + typ + ', but expected IndexRef')
1438
+
1439
+ # Deserialize the variable field.
1440
+ field = cbor.get('variable', None)
1441
+ if not isinstance(field, dict):
1442
+ raise ValueError('missing or invalid serialization of field variable')
1443
+ if field.get('@T') != '$':
1444
+ raise ValueError('unexpected edge type for field variable')
1445
+ f_variable = None
1446
+ l_variable = field.get('@l', None)
1447
+
1448
+ # Deserialize the indices field.
1449
+ field = cbor.get('indices', None)
1450
+ if not isinstance(field, dict):
1451
+ raise ValueError('missing or invalid serialization of field indices')
1452
+ if field.get('@T') != '+':
1453
+ raise ValueError('unexpected edge type for field indices')
1454
+ data = field.get('@d', None)
1455
+ if not isinstance(data, list):
1456
+ raise ValueError('missing serialization of Any/Many contents')
1457
+ f_indices = MultiConstInt()
1458
+ for element in data:
1459
+ if element.get('@T') != '1':
1460
+ raise ValueError('unexpected edge type for Any/Many element')
1461
+ f_indices.append(ConstInt._deserialize(element, seq_to_ob, links))
1462
+
1463
+ # Construct the IndexRef node.
1464
+ node = IndexRef(f_variable, f_indices)
1465
+
1466
+ # Register links to be made after tree construction.
1467
+ links.append((lambda val: IndexRef.variable.fset(node, val), l_variable))
1468
+
1469
+ # Deserialize annotations.
1470
+ for key, val in cbor.items():
1471
+ if not (key.startswith('{') and key.endswith('}')):
1472
+ continue
1473
+ key = key[1:-1]
1474
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
1475
+
1476
+ # Register node in sequence number lookup.
1477
+ seq = cbor.get('@i', None)
1478
+ if not isinstance(seq, int):
1479
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
1480
+ if seq in seq_to_ob:
1481
+ raise ValueError('duplicate sequence number %d' % seq)
1482
+ seq_to_ob[seq] = node
1483
+
1484
+ return node
1485
+
1486
+ def _serialize(self, id_map):
1487
+ """Serializes this node to the Python primitive representation of its
1488
+ CBOR serialization. The tree that the node belongs to must be
1489
+ well-formed. id_map must match Python id() calls for all nodes to unique
1490
+ integers, to use for the sequence number representation of links."""
1491
+ cbor = {'@i': id_map[id(self)], '@t': 'IndexRef'}
1492
+
1493
+ # Serialize the variable field.
1494
+ field = {'@T': '$'}
1495
+ if self._attr_variable is None:
1496
+ field['@l'] = None
1497
+ else:
1498
+ field['@l'] = id_map[id(self._attr_variable)]
1499
+ cbor['variable'] = field
1500
+
1501
+ # Serialize the indices field.
1502
+ field = {'@T': '+'}
1503
+ lst = []
1504
+ for el in self._attr_indices:
1505
+ el = el._serialize(id_map)
1506
+ el['@T'] = '1'
1507
+ lst.append(el)
1508
+ field['@d'] = lst
1509
+ cbor['indices'] = field
1510
+
1511
+ # Serialize annotations.
1512
+ for key, val in self._annot.items():
1513
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
1514
+
1515
+ return cbor
1516
+
1517
+
1518
+ class MultiIndexRef(_Multiple):
1519
+ """Wrapper for an edge with multiple IndexRef objects."""
1520
+
1521
+ _T = IndexRef
1522
+
1523
+
1524
+ _typemap['IndexRef'] = IndexRef
1525
+
1526
+ class VariableRef(Reference):
1527
+ __slots__ = [
1528
+ '_attr_variable',
1529
+ ]
1530
+
1531
+ def __init__(
1532
+ self,
1533
+ variable=None,
1534
+ ):
1535
+ super().__init__()
1536
+ self.variable = variable
1537
+
1538
+ @property
1539
+ def variable(self):
1540
+ return self._attr_variable
1541
+
1542
+ @variable.setter
1543
+ def variable(self, val):
1544
+ if val is None:
1545
+ del self.variable
1546
+ return
1547
+ if not isinstance(val, cqasm.v3x.semantic.Variable):
1548
+ raise TypeError('variable must be of type cqasm.v3x.semantic.Variable')
1549
+ self._attr_variable = val
1550
+
1551
+ @variable.deleter
1552
+ def variable(self):
1553
+ self._attr_variable = None
1554
+
1555
+ def __eq__(self, other):
1556
+ """Equality operator. Ignores annotations!"""
1557
+ if not isinstance(other, VariableRef):
1558
+ return False
1559
+ if self.variable is not other.variable:
1560
+ return False
1561
+ return True
1562
+
1563
+ def dump(self, indent=0, annotations=None, links=1):
1564
+ """Returns a debug representation of this tree as a multiline string.
1565
+ indent is the number of double spaces prefixed before every line.
1566
+ annotations, if specified, must be a set-like object containing the key
1567
+ strings of the annotations that are to be printed. links specifies the
1568
+ maximum link recursion depth."""
1569
+ s = [' '*indent]
1570
+ s.append('VariableRef(')
1571
+ if annotations is None:
1572
+ annotations = []
1573
+ for key in annotations:
1574
+ if key in self:
1575
+ s.append(' # {}: {}'.format(key, self[key]))
1576
+ s.append('\n')
1577
+ indent += 1
1578
+ s.append(' '*indent)
1579
+ s.append('variable --> ')
1580
+ if self.variable is None:
1581
+ s.append('!MISSING\n')
1582
+ else:
1583
+ s.append('<\n')
1584
+ if links:
1585
+ s.append(self.variable.dump(indent + 1, annotations, links - 1) + '\n')
1586
+ else:
1587
+ s.append(' '*(indent+1) + '...\n')
1588
+ s.append(' '*indent + '>\n')
1589
+ indent -= 1
1590
+ s.append(' '*indent)
1591
+ s.append(')')
1592
+ return ''.join(s)
1593
+
1594
+ __str__ = dump
1595
+ __repr__ = dump
1596
+
1597
+ def find_reachable(self, id_map=None):
1598
+ """Returns a dictionary mapping Python id() values to stable sequence
1599
+ numbers for all nodes in the tree rooted at this node. If id_map is
1600
+ specified, found nodes are appended to it."""
1601
+ if id_map is None:
1602
+ id_map = {}
1603
+ if id(self) in id_map:
1604
+ raise NotWellFormed('node {!r} with id {} occurs more than once'.format(self, id(self)))
1605
+ id_map[id(self)] = len(id_map)
1606
+ return id_map
1607
+
1608
+ def check_complete(self, id_map=None):
1609
+ """Raises NotWellFormed if the tree rooted at this node is not
1610
+ well-formed. If id_map is specified, this tree is only a subtree in the
1611
+ context of a larger tree, and id_map must be a dict mapping from Python
1612
+ id() codes to tree indices for all reachable nodes."""
1613
+ if id_map is None:
1614
+ id_map = self.find_reachable()
1615
+ if self._attr_variable is None:
1616
+ raise NotWellFormed('variable is required but not set')
1617
+ if self._attr_variable is not None:
1618
+ if id(self._attr_variable) not in id_map:
1619
+ raise NotWellFormed('variable links to unreachable node')
1620
+
1621
+ def copy(self):
1622
+ """Returns a shallow copy of this node."""
1623
+ return VariableRef(
1624
+ variable=self._attr_variable
1625
+ )
1626
+
1627
+ def clone(self):
1628
+ """Returns a deep copy of this node. This mimics the C++ interface,
1629
+ deficiencies with links included; that is, links always point to the
1630
+ original tree. If you're not cloning a subtree in a context where this
1631
+ is the desired behavior, you may want to use the copy.deepcopy() from
1632
+ the stdlib instead, which should copy links correctly."""
1633
+ return VariableRef(
1634
+ variable=self._attr_variable
1635
+ )
1636
+
1637
+ @staticmethod
1638
+ def _deserialize(cbor, seq_to_ob, links):
1639
+ """Attempts to deserialize the given cbor object (in Python primitive
1640
+ representation) into a node of this type. All (sub)nodes are added to
1641
+ the seq_to_ob dict, indexed by their cbor sequence number. All links are
1642
+ registered in the links list by means of a two-tuple of the setter
1643
+ function for the link field and the sequence number of the target node.
1644
+ """
1645
+ if not isinstance(cbor, dict):
1646
+ raise TypeError('node description object must be a dict')
1647
+ typ = cbor.get('@t', None)
1648
+ if typ is None:
1649
+ raise ValueError('type (@t) field is missing from node serialization')
1650
+ if typ != 'VariableRef':
1651
+ raise ValueError('found node serialization for ' + typ + ', but expected VariableRef')
1652
+
1653
+ # Deserialize the variable field.
1654
+ field = cbor.get('variable', None)
1655
+ if not isinstance(field, dict):
1656
+ raise ValueError('missing or invalid serialization of field variable')
1657
+ if field.get('@T') != '$':
1658
+ raise ValueError('unexpected edge type for field variable')
1659
+ f_variable = None
1660
+ l_variable = field.get('@l', None)
1661
+
1662
+ # Construct the VariableRef node.
1663
+ node = VariableRef(f_variable)
1664
+
1665
+ # Register links to be made after tree construction.
1666
+ links.append((lambda val: VariableRef.variable.fset(node, val), l_variable))
1667
+
1668
+ # Deserialize annotations.
1669
+ for key, val in cbor.items():
1670
+ if not (key.startswith('{') and key.endswith('}')):
1671
+ continue
1672
+ key = key[1:-1]
1673
+ node[key] = cqasm.v3x.primitives.deserialize(key, val)
1674
+
1675
+ # Register node in sequence number lookup.
1676
+ seq = cbor.get('@i', None)
1677
+ if not isinstance(seq, int):
1678
+ raise ValueError('sequence number field (@i) is not an integer or missing from node serialization')
1679
+ if seq in seq_to_ob:
1680
+ raise ValueError('duplicate sequence number %d' % seq)
1681
+ seq_to_ob[seq] = node
1682
+
1683
+ return node
1684
+
1685
+ def _serialize(self, id_map):
1686
+ """Serializes this node to the Python primitive representation of its
1687
+ CBOR serialization. The tree that the node belongs to must be
1688
+ well-formed. id_map must match Python id() calls for all nodes to unique
1689
+ integers, to use for the sequence number representation of links."""
1690
+ cbor = {'@i': id_map[id(self)], '@t': 'VariableRef'}
1691
+
1692
+ # Serialize the variable field.
1693
+ field = {'@T': '$'}
1694
+ if self._attr_variable is None:
1695
+ field['@l'] = None
1696
+ else:
1697
+ field['@l'] = id_map[id(self._attr_variable)]
1698
+ cbor['variable'] = field
1699
+
1700
+ # Serialize annotations.
1701
+ for key, val in self._annot.items():
1702
+ cbor['{%s}' % key] = _py_to_cbor(cqasm.v3x.primitives.serialize(key, val))
1703
+
1704
+ return cbor
1705
+
1706
+
1707
+ class MultiVariableRef(_Multiple):
1708
+ """Wrapper for an edge with multiple VariableRef objects."""
1709
+
1710
+ _T = VariableRef
1711
+
1712
+
1713
+ _typemap['VariableRef'] = VariableRef
1714
+