Skip to content

Commit

Permalink
Merge pull request #337 from qutech/issues/329_pt_create_program_beha…
Browse files Browse the repository at this point in the history
…vior_test

Compatibility Tests for create_program
  • Loading branch information
terrorfisch committed Aug 9, 2018
2 parents 7eb2003 + bce8175 commit 6027d69
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 47 deletions.
2 changes: 1 addition & 1 deletion qctoolkit/_program/_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __init__(self,

@property
def compare_key(self) -> Tuple:
return self._waveform, self.repetition_count, tuple(c.compare_key for c in self)
return self._waveform, self.repetition_count, self._measurements, tuple(c.compare_key for c in self)

def append_child(self, loop: Optional['Loop']=None, **kwargs) -> None:
# do not invalidate but update cached duration
Expand Down
8 changes: 6 additions & 2 deletions qctoolkit/pulses/pulse_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ def _internal_create_program(self, *,
measurement_mapping: Dict[str, Optional[str]],
channel_mapping: Dict[ChannelID, Optional[ChannelID]],
parent_loop: Loop) -> None:
### current behavior (same as previously): only adds EXEC Loop and measurements if a waveform exists.
### measurements are directly added to parent_loop (to reflect behavior of Sequencer + MultiChannelProgram)
# todo (2018-08-08): could move measurements into own Loop object?

# todo (2018-07-05): why are parameter constraints not validated here?
try:
parameters = {parameter_name: parameters[parameter_name].get_value()
Expand All @@ -211,8 +215,8 @@ def _internal_create_program(self, *,
waveform = self.build_waveform(parameters,
channel_mapping=channel_mapping)
if waveform:
parent_loop.append_child(waveform=waveform, measurements=measurements)

parent_loop.add_measurements(measurements=measurements)
parent_loop.append_child(waveform=waveform)

@abstractmethod
def build_waveform(self,
Expand Down
27 changes: 26 additions & 1 deletion tests/pulses/loop_pulse_template_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
from qctoolkit._program.instructions import MEASInstruction
from qctoolkit._program._loop import Loop

from qctoolkit._program._loop import MultiChannelProgram
from qctoolkit.pulses.sequencing import Sequencer

from tests.pulses.sequencing_dummies import DummyCondition, DummyPulseTemplate, DummySequencer, DummyInstructionBlock,\
DummyParameter, MeasurementWindowTestCase, DummyWaveform
from tests.serialization_dummies import DummySerializer
Expand Down Expand Up @@ -310,8 +313,19 @@ def test_create_program_body_none(self) -> None:
self.assertEqual(1, program.repetition_count)
self.assertEqual([], program.children)

# ensure same result as from Sequencer
sequencer = Sequencer()
sequencer.push(flt, parameters=parameters, conditions={}, window_mapping=measurement_mapping,
channel_mapping=channel_mapping)
block = sequencer.build()
program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})]
self.assertEqual(program_old.repetition_count, program.repetition_count)
self.assertEqual(program_old.children, program.children)
self.assertEqual(program_old.waveform, program.waveform)
# program_old defines measurements while program does not

def test_create_program(self) -> None:
dt = DummyPulseTemplate(parameter_names={'i'}, waveform=DummyWaveform(duration=4.0), duration=4,
dt = DummyPulseTemplate(parameter_names={'i'}, waveform=DummyWaveform(duration=4.0, defined_channels={'A'}), duration=4,
measurements=[('b', 2, 1)])
flt = ForLoopPulseTemplate(body=dt, loop_index='i', loop_range=('a', 'b', 'c'),
measurements=[('A', 0, 1)], parameter_constraints=['c > 1'])
Expand All @@ -334,6 +348,14 @@ def test_create_program(self) -> None:
self.assertEqual(1, program.repetition_count)
self.assert_measurement_windows_equal({'b': ([2, 6], [1, 1]), 'B': ([0], [1])}, program.get_measurement_windows())

# ensure same result as from Sequencer
sequencer = Sequencer()
sequencer.push(flt, parameters=parameters, conditions={}, window_mapping=measurement_mapping,
channel_mapping=channel_mapping)
block = sequencer.build()
program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})]
self.assertEqual(program_old, program)

def test_create_program_append(self) -> None:
dt = DummyPulseTemplate(parameter_names={'i'}, waveform=DummyWaveform(duration=4.0), duration=4,
measurements=[('b', 2, 1)])
Expand All @@ -360,6 +382,9 @@ def test_create_program_append(self) -> None:
self.assertEqual(1, program.repetition_count)
self.assert_measurement_windows_equal({'b': ([4, 8], [1, 1]), 'B': ([2], [1])}, program.get_measurement_windows())

# not ensure same result as from Sequencer here - we're testing appending to an already existing parent loop
# which is a use case that does not immediately arise from using Sequencer


class ForLoopTemplateOldSequencingTests(unittest.TestCase):

Expand Down
19 changes: 19 additions & 0 deletions tests/pulses/mapping_pulse_template_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
from qctoolkit.expressions import Expression
from qctoolkit._program._loop import Loop

from qctoolkit._program._loop import MultiChannelProgram
from qctoolkit.pulses.sequencing import Sequencer

from tests.pulses.sequencing_dummies import DummyPulseTemplate, DummySequencer, DummyInstructionBlock, MeasurementWindowTestCase, DummyWaveform
from tests.serialization_tests import SerializableTests
from tests.serialization_dummies import DummySerializer
Expand Down Expand Up @@ -228,6 +231,14 @@ def test_create_program(self) -> None:
self.assertIs(template.waveform, program.children[0].waveform)
self.assert_measurement_windows_equal({'meas3': ([0], [1])}, program.get_measurement_windows())

# ensure same result as from Sequencer
sequencer = Sequencer()
sequencer.push(st, parameters=pre_parameters, conditions={}, window_mapping=pre_measurement_mapping,
channel_mapping=pre_channel_mapping)
block = sequencer.build()
program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})]
self.assertEqual(program_old, program)

def test_create_program_invalid_measurement_mapping(self) -> None:
measurement_mapping = {'meas1': 'meas2'}
parameter_mapping = {'t': 'k'}
Expand Down Expand Up @@ -335,6 +346,14 @@ def test_create_program_subtemplate_none(self) -> None:
self.assertEqual(0, len(program.children))
self.assertIsNone(program._measurements)

# ensure same result as from Sequencer
sequencer = Sequencer()
sequencer.push(st, parameters=pre_parameters, conditions={}, window_mapping=pre_measurement_mapping,
channel_mapping=pre_channel_mapping)
block = sequencer.build()
program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})]
self.assertEqual(program_old, program)


class MappingPulseTemplateOldSequencingTests(unittest.TestCase):

Expand Down
38 changes: 37 additions & 1 deletion tests/pulses/pulse_template_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
from qctoolkit.pulses.multi_channel_pulse_template import MultiChannelWaveform
from qctoolkit._program._loop import Loop

from qctoolkit._program._loop import MultiChannelProgram
from qctoolkit.pulses.sequencing import Sequencer

from tests.pulses.sequencing_dummies import DummyWaveform, DummySequencer, DummyInstructionBlock


Expand Down Expand Up @@ -253,10 +256,11 @@ def test_internal_create_program(self) -> None:

template = AtomicPulseTemplateStub(waveform=wf, measurements=measurement_windows, parameter_names={'foo'})
parameters = {'foo': ConstantParameter(7.2)}
measurement_mapping = {'M': 'N'}
channel_mapping = {'B': 'A'}
program = Loop()
template._internal_create_program(parameters=parameters,
measurement_mapping={'M': 'N'},
measurement_mapping=measurement_mapping,
channel_mapping=channel_mapping,
parent_loop=program)
self.assertEqual({k: p.get_value() for k, p in parameters.items()}, template.retrieved_parameters[-1])
Expand All @@ -267,6 +271,38 @@ def test_internal_create_program(self) -> None:
self.assertIs(program.children[0].waveform, wf)
self.assertEqual(expected_measurement_windows, program.get_measurement_windows())

# ensure same result as from Sequencer
sequencer = Sequencer()
sequencer.push(template, parameters=parameters, conditions={}, window_mapping=measurement_mapping,
channel_mapping=channel_mapping)
block = sequencer.build()
old_program = MultiChannelProgram(block, channels={'A'})
self.assertEqual(old_program.programs[frozenset({'A'})], program)

def test_internal_create_program_no_waveform(self) -> None:
measurement_windows = [('M', 0, 5)]

template = AtomicPulseTemplateStub(waveform=None, measurements=measurement_windows, parameter_names={'foo'})
parameters = {'foo': ConstantParameter(7.2)}
measurement_mapping = {'M': 'N'}
channel_mapping = {'B': 'A'}
program = Loop()
template._internal_create_program(parameters=parameters,
measurement_mapping=measurement_mapping,
channel_mapping=channel_mapping,
parent_loop=program)
self.assertEqual({k: p.get_value() for k, p in parameters.items()}, template.retrieved_parameters[-1])
self.assertIsNone(program.waveform)
self.assertFalse(program.children)
self.assertFalse(program._measurements)

# ensure same result as from Sequencer
sequencer = Sequencer()
sequencer.push(template, parameters=parameters, conditions={}, window_mapping=measurement_mapping, channel_mapping=channel_mapping)
block = sequencer.build()
old_program = MultiChannelProgram(block, channels={'A'})
self.assertEqual(old_program.programs[frozenset({'A'})], program)

def test_internal_create_program_invalid_measurement_mapping(self) -> None:
measurement_windows = [('M', 0, 5)]
wf = DummyWaveform(duration=6, defined_channels={'A'})
Expand Down

0 comments on commit 6027d69

Please sign in to comment.