Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes.d/771.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The `repr` implementation of `Loop` will now return an evaluable python expression. The old behaviour moved to the `str` implementation.
55 changes: 47 additions & 8 deletions qupulse/_program/_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from enum import Enum
import warnings
import bisect
import reprlib

import numpy as np
import sympy.ntheory
Expand Down Expand Up @@ -198,23 +199,20 @@ def encapsulate(self) -> None:
self._measurements = None
self.assert_tree_integrity()

def _get_repr(self, first_prefix, other_prefixes) -> Generator[str, None, None]:
def _get_str(self, first_prefix, other_prefixes) -> Generator[str, None, None]:
if self.is_leaf():
yield '%sEXEC %r %d times' % (first_prefix, self._waveform, self.repetition_count)
else:
yield '%sLOOP %d times:' % (first_prefix, self.repetition_count)

for elem in self:
yield from cast(Loop, elem)._get_repr(other_prefixes + ' ->', other_prefixes + ' ')

def __repr__(self) -> str:
is_circular = is_tree_circular(self)
if is_circular:
return '{}: Circ {}'.format(id(self), is_circular)
yield from cast(Loop, elem)._get_str(other_prefixes + ' ->', other_prefixes + ' ')

@reprlib.recursive_repr()
def __str__(self) -> str:
str_len = 0
repr_list = []
for sub_repr in self._get_repr('', ''):
for sub_repr in self._get_str('', ''):
str_len += len(sub_repr)

if self.MAX_REPR_SIZE and str_len > self.MAX_REPR_SIZE:
Expand All @@ -224,6 +222,47 @@ def __repr__(self) -> str:
repr_list.append(sub_repr)
return '\n'.join(repr_list)

@reprlib.recursive_repr()
def __repr__(self):
children = self.children
waveform = self.waveform
measurements = self._measurements
repetition_count = self.repetition_count

max_repr_size = self.MAX_REPR_SIZE

reprs = {}
if children:
if len(children) < max_repr_size // len('Loop(...)'):
reprs['children'] = repr(list(children))
else:
reprs['children'] = '[...]'

if waveform:
waveform_repr = repr(waveform)
if len(waveform_repr) >= max_repr_size:
waveform_repr = '...'
reprs['waveform'] = waveform_repr

if measurements:
meas_repr = repr(measurements)
if len(meas_repr) >= max_repr_size:
meas_repr = '[...]'
reprs['measurements'] = meas_repr

if repetition_count != 1:
reprs['repetition_count'] = repr(repetition_count)

kwargs = ', '.join(f'{attr}={val}' for attr, val in reprs.items())

type_name = type(self).__name__
max_kwargs = max(self.MAX_REPR_SIZE - len(type_name) - 2, 0)

if len(kwargs) > max_kwargs:
kwargs = '...'

return f'{type(self).__name__}({kwargs})'

def copy_tree_structure(self, new_parent: Union['Loop', bool]=False) -> 'Loop':
return type(self)(parent=self.parent if new_parent is False else new_parent,
waveform=self._waveform,
Expand Down
31 changes: 20 additions & 11 deletions tests/_program/loop_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def test_get_measurement_windows(self):
# no measurements left
self.assertEqual({}, prog.get_measurement_windows())

def test_repr(self):
def test_str(self):
wf_gen = WaveformGenerator(num_channels=1)
wfs = [wf_gen() for _ in range(11)]

Expand All @@ -154,10 +154,19 @@ def test_repr(self):
loop.waveform = wfs.pop(0)
self.assertEqual(len(wfs), 0)

self.assertEqual(repr(tree), expected)
self.assertEqual(str(tree), expected)

with mock.patch.object(Loop, 'MAX_REPR_SIZE', 1):
self.assertEqual(repr(tree), '...')
self.assertEqual(str(tree), '...')

def test_repr(self):
root_loop = self.get_test_loop()

root_repr = repr(root_loop)

root_eval = eval(root_repr)

self.assertEqual(root_loop, root_eval)

def test_is_leaf(self):
root_loop = self.get_test_loop(waveform_generator=WaveformGenerator(1))
Expand Down Expand Up @@ -195,12 +204,12 @@ def test_flatten_and_balance(self):
after = before.copy_tree_structure()
after.flatten_and_balance(2)

wf_reprs = dict(zip(ascii_uppercase,
(repr(loop.waveform)
wf_strs = dict(zip(ascii_uppercase,
(str(loop.waveform)
for loop in before.get_depth_first_iterator()
if loop.is_leaf())))

before_repr = """\
before_str = """\
LOOP 1 times:
->EXEC {A} 1 times
->LOOP 10 times:
Expand All @@ -220,10 +229,10 @@ def test_flatten_and_balance(self):
->EXEC {I} 8 times
->LOOP 9 times:
->EXEC {J} 10 times
->EXEC {K} 11 times""".format(**wf_reprs)
self.assertEqual(repr(before), before_repr)
->EXEC {K} 11 times""".format(**wf_strs)
self.assertEqual(str(before), before_str)

expected_after_repr = """\
expected_after_str = """\
LOOP 1 times:
->LOOP 1 times:
->EXEC {A} 1 times
Expand Down Expand Up @@ -261,9 +270,9 @@ def test_flatten_and_balance(self):
->EXEC {I} 8 times
->LOOP 9 times:
->EXEC {J} 10 times
->EXEC {K} 11 times""".format(**wf_reprs)
->EXEC {K} 11 times""".format(**wf_strs)

self.assertEqual(expected_after_repr, repr(after))
self.assertEqual(expected_after_str, str(after))

def test_flatten_and_balance_comparison_based(self):
wfs = [DummyWaveform(duration=i) for i in range(2)]
Expand Down