コード例 #1
0
ファイル: util_tests.py プロジェクト: zea2/qupulse
    def test_get_sample_times_single_wf(self):
        sample_rate = TimeType.from_fraction(12, 10)
        wf = DummyWaveform(duration=TimeType.from_fraction(40, 12))

        expected_times = np.arange(4) / 1.2
        times, n_samples = get_sample_times(wf, sample_rate_in_GHz=sample_rate)

        np.testing.assert_equal(times, expected_times)
        np.testing.assert_equal(n_samples, np.asarray(4))
コード例 #2
0
ファイル: expression_tests.py プロジェクト: zea2/qupulse
    def test_evaluate_with_exact_rationals(self):
        expr = ExpressionScalar('1 / 3')
        self.assertEqual(TimeType.from_fraction(1, 3), expr.evaluate_with_exact_rationals({}))

        expr = ExpressionScalar('a * (1 / 3)')
        self.assertEqual(TimeType.from_fraction(2, 3), expr.evaluate_with_exact_rationals({'a': 2}))

        expr = ExpressionScalar('dot(a, b) * (1 / 3)')
        self.assertEqual(TimeType.from_fraction(10, 3),
                         expr.evaluate_with_exact_rationals({'a': [2, 2], 'b': [1, 4]}))
コード例 #3
0
ファイル: alazar.py プロジェクト: qutech/qupulse
 def set_measurement_mask(self, program_name, mask_name, begins,
                          lengths) -> Tuple[np.ndarray, np.ndarray]:
     sample_factor = TimeType.from_fraction(
         int(
             self.default_config.captureClockConfiguration.
             numeric_sample_rate(self.card.model)), 10**9)
     return self._registered_programs[program_name].set_measurement_mask(
         mask_name, sample_factor, begins, lengths)
コード例 #4
0
    def arm_program(self, program_name: str) -> None:
        to_arm = self._registered_programs[program_name]
        if self.update_settings or self.__armed_program is not to_arm:
            config = self.config
            config.masks, config.operations, total_record_size = self._registered_programs[
                program_name].iter(self._make_mask)

            sample_factor = TimeType.from_fraction(
                self.config.captureClockConfiguration.numeric_sample_rate(
                    self.card.model), 10**9)

            if not config.operations:
                raise RuntimeError(
                    "No operations: Arming program without operations is an error as there will "
                    "be no result: %r" % program_name)

            elif not config.masks:
                raise RuntimeError(
                    "No masks although there are operations in program: %r" %
                    program_name)

            elif self._registered_programs[
                    program_name].sample_factor != sample_factor:
                raise RuntimeError(
                    "Masks were registered with a different sample rate {}!={}"
                    .format(
                        self._registered_programs[program_name].sample_factor,
                        sample_factor))

            assert total_record_size > 0

            minimum_record_size = self.__card.minimum_record_size
            total_record_size = ((
                (total_record_size - 1) // minimum_record_size) +
                                 1) * minimum_record_size

            if config.totalRecordSize == 0:
                config.totalRecordSize = total_record_size
            elif config.totalRecordSize < total_record_size:
                raise ValueError(
                    'specified total record size is smaller than needed {} < {}'
                    .format(config.totalRecordSize, total_record_size))

            old_aimed_buffer_size = config.aimedBufferSize

            # work around for measurments not working with one buffer
            if config.totalRecordSize < 5 * config.aimedBufferSize:
                config.aimedBufferSize = config.totalRecordSize // 5

            self.__card.applyConfiguration(config, True)

            # "Hide" work around from the user
            config.aimedBufferSize = old_aimed_buffer_size

            self.update_settings = False
            self.__armed_program = to_arm
        self.__card.startAcquisition(1)
コード例 #5
0
ファイル: util_tests.py プロジェクト: zea2/qupulse
    def test_get_sample_times(self):
        sample_rate = TimeType.from_fraction(12, 10)
        wf1 = DummyWaveform(duration=TimeType.from_fraction(20, 12))
        wf2 = DummyWaveform(duration=TimeType.from_fraction(400000000001, 120000000000))
        wf3 = DummyWaveform(duration=TimeType.from_fraction(1, 10**15))

        expected_times = np.arange(4) / 1.2
        times, n_samples = get_sample_times([wf1, wf2], sample_rate_in_GHz=sample_rate)
        np.testing.assert_equal(expected_times, times)
        np.testing.assert_equal(n_samples, np.asarray([2, 4]))

        with self.assertRaises(AssertionError):
            get_sample_times([], sample_rate_in_GHz=sample_rate)

        with self.assertRaisesRegex(ValueError, "non integer length"):
            get_sample_times([wf1, wf2], sample_rate_in_GHz=sample_rate, tolerance=0.)

        with self.assertRaisesRegex(ValueError, "length <= zero"):
            get_sample_times([wf1, wf3], sample_rate_in_GHz=sample_rate)
コード例 #6
0
ファイル: alazar.py プロジェクト: zea2/qupulse
    def register_measurement_windows(self,
                                     program_name: str,
                                     windows: Dict[str, Tuple[np.ndarray, np.ndarray]]) -> None:
        program = self._registered_programs[program_name]
        sample_factor = TimeType.from_fraction(int(self.config.captureClockConfiguration.numeric_sample_rate(self.card.model)),
                                 10 ** 9)
        program.clear_masks()

        for mask_name, (begins, lengths) in windows.items():
            program.set_measurement_mask(mask_name, sample_factor, begins, lengths)
コード例 #7
0
 def body_duration(self) -> TimeType:
     if self._cached_body_duration is None:
         if self.is_leaf():
             if self.waveform:
                 self._cached_body_duration = self.waveform.duration
             else:
                 self._cached_body_duration = TimeType.from_fraction(0, 1)
         else:
             self._cached_body_duration = sum(child.duration for child in self)
     return self._cached_body_duration
コード例 #8
0
ファイル: alazar.py プロジェクト: qutech-lab/qupulse
    def arm_program(self, program_name: str) -> None:
        logger.debug("Arming program %s on %r", program_name, self.__card)

        to_arm = self._registered_programs[program_name]
        if self.update_settings or self.__armed_program is not to_arm:
            logger.info("Arming %r by calling applyConfiguration. Update settings flag: %r",
                        self.__card, self.update_settings)

            config = copy.deepcopy(self.default_config)
            config.masks, config.operations, total_record_size = self._registered_programs[program_name].iter(
                self._make_mask)

            sample_rate = config.captureClockConfiguration.numeric_sample_rate(self.card.model)

            # sample rate in GHz
            sample_factor = TimeType.from_fraction(sample_rate, 10 ** 9)

            if not config.operations:
                raise RuntimeError("No operations: Arming program without operations is an error as there will "
                                   "be no result: %r" % program_name)

            elif not config.masks:
                raise RuntimeError("No masks although there are operations in program: %r" % program_name)

            elif self._registered_programs[program_name].sample_factor != sample_factor:
                raise RuntimeError("Masks were registered with a different sample rate {}!={}".format(
                    self._registered_programs[program_name].sample_factor, sample_factor))

            assert total_record_size > 0

            # extend the total record size to be a multiple of record_size_factor
            record_size_factor = self.record_size_factor
            total_record_size = (((total_record_size - 1) // record_size_factor) + 1) * record_size_factor

            if config.totalRecordSize == 0:
                config.totalRecordSize = total_record_size
            elif config.totalRecordSize < total_record_size:
                raise ValueError('specified total record size is smaller than needed {} < {}'.format(config.totalRecordSize,
                                                                                                     total_record_size))
            self.__card.applyConfiguration(config, True)
            self._current_config = config

            self.update_settings = False
            self.__armed_program = to_arm

        elif self.__armed_program is to_arm and self._remaining_auto_triggers > 0:
            self._remaining_auto_triggers -= 1
            logger.info("Relying on atsaverage auto-arm with %d auto triggers remaining after this one",
                        self._remaining_auto_triggers)
            return

        self.__card.startAcquisition(to_arm.auto_rearm_count)
        self._remaining_auto_triggers = to_arm.auto_rearm_count - 1
コード例 #9
0
ファイル: tabor_tests.py プロジェクト: qutech/qupulse
 def setUp(self) -> None:
     self._instr_props = None
     self.program_entry_kwargs = dict(
         amplitudes=(1., 1.),
         offsets=(0., 0.),
         voltage_transformations=(mock.Mock(wraps=lambda x: x),
                                  mock.Mock(wraps=lambda x: x)),
         sample_rate=TimeType.from_fraction(192, 1),
         mode=None)
     if tabor_control is not None:
         self._instr_props = tabor_control.util.model_properties_dict[
             'WX2184C'].copy()
コード例 #10
0
 def setUp(self) -> None:
     # we currently allow overlapping masks in AlazarProgram (It will throw an error on upload)
     # This probably will change in the future
     self.masks = {
         'unsorted': (np.array([1., 100, 13]), np.array([10., 999, 81])),
         'sorted': (np.array([30., 100, 1300]), np.array([10., 990, 811])),
         'overlapping': (np.array([30., 100,
                                   300]), np.array([20., 900, 100]))
     }
     self.sample_factor = TimeType.from_fraction(10**8, 10**9)
     self.expected = {
         'unsorted': (np.array([0, 1, 10]).astype(np.uint64),
                      np.array([1, 8, 99]).astype(np.uint64)),
         'sorted': (np.array([3, 10, 130]).astype(np.uint64),
                    np.array([1, 99, 81]).astype(np.uint64)),
         'overlapping': (np.array([3, 10, 30]).astype(np.uint64),
                         np.array([2, 90, 10]).astype(np.uint64))
     }
コード例 #11
0
ファイル: sympy_tests.py プロジェクト: zea2/qupulse
eval_sum = [
    (Sum(a_[i], (i, 0, Len(a) - 1)), {
        'a': np.array([1, 2, 3])
    }, 6),
]

eval_array_expression = [(np.array([a * c, b * c]), {
    'a': 2,
    'b': 3,
    'c': 4
}, np.array([8, 12]))]

eval_exact_rational = [
    (a * Rational('1/3'), {
        'a': 2
    }, TimeType.from_fraction(2, 3)),
    (a * Rational('1/3'), {
        'a': Rational(1, 5)
    }, TimeType.from_fraction(1, 15)),
    # TODO: this fails
    # (np.array([a, Rational(1, 3)]), {'a': 2}, np.array([2, TimeType.from_fraction(1, 3)]))
]


class TestCase(unittest.TestCase):
    def assertRaises(self, expected_exception, *args, **kwargs):
        if expected_exception is None:
            return contextlib.suppress()
        else:
            return super().assertRaises(expected_exception, *args, **kwargs)
コード例 #12
0
    def test_parse_program(self):
        ill_formed_program = Loop(children=[Loop(children=[Loop()])])

        with self.assertRaisesRegex(AssertionError, 'Invalid program depth'):
            parse_program(ill_formed_program, (), (), TimeType(), (), (), ())

        channels = ('A', 'B', None, None)
        markers = (('A1', None), (None, None), (None, 'C2'), (None, None))

        # we do test offset handling separately
        amplitudes = (1, 1, 1, 1)
        offsets = (0, 0, 0, 0)
        voltage_transformations = tuple(
            mock.Mock(wraps=lambda x: x) for _ in range(4))

        used_channels = {'A', 'B', 'A1', 'C2'}
        wf_defined_channels = used_channels & {'other'}

        sampled_6 = [
            np.zeros((6, )),
            np.arange(6) / 6,
            np.ones((6, )) * 0.42,
            np.array([0., .1, .2, 0., 1, 0]),
            np.array([1., .0, .0, 0., 0, 1])
        ]

        sampled_4 = [
            np.zeros((4, )),
            np.arange(-4, 0) / 4,
            np.ones((4, )) * 0.2,
            np.array([0., -.1, -.2, 0.]),
            np.array([0., 0, 0, 1.])
        ]

        sample_rate_in_GHz = TimeType.from_fraction(1, 2)

        # channel A is the same in wfs_6[1] and wfs_6[2]
        wfs_6 = [
            DummyWaveform(duration=12,
                          sample_output={
                              'A': sampled_6[0],
                              'B': sampled_6[0],
                              'A1': sampled_6[0],
                              'C2': sampled_6[0]
                          },
                          defined_channels=used_channels),
            DummyWaveform(duration=12,
                          sample_output={
                              'A': sampled_6[1],
                              'B': sampled_6[2],
                              'A1': sampled_6[3],
                              'C2': sampled_6[4]
                          },
                          defined_channels=used_channels),
            DummyWaveform(duration=12,
                          sample_output={
                              'A': sampled_6[1],
                              'B': sampled_6[0],
                              'A1': sampled_6[3],
                              'C2': sampled_6[2]
                          },
                          defined_channels=used_channels)
        ]
        wfs_4 = [
            DummyWaveform(duration=8,
                          sample_output={
                              'A': sampled_4[0],
                              'B': sampled_4[0],
                              'A1': sampled_4[2],
                              'C2': sampled_4[3]
                          },
                          defined_channels=used_channels),
            DummyWaveform(duration=8,
                          sample_output={
                              'A': sampled_4[1],
                              'B': sampled_4[2],
                              'A1': sampled_4[2],
                              'C2': sampled_4[3]
                          },
                          defined_channels=used_channels),
            DummyWaveform(duration=8,
                          sample_output={
                              'A': sampled_4[2],
                              'B': sampled_4[0],
                              'A1': sampled_4[2],
                              'C2': sampled_4[3]
                          },
                          defined_channels=used_channels)
        ]

        # unset is equal to sampled_n[0]
        binary_waveforms_6 = [(0, 0, 0), (0, 0, 0), (0, 0, 0), (1, 3, 0),
                              (2, 0, 0), (0, 0, 4), (1, 3, 0), (0, 0, 0),
                              (0, 0, 2)]

        binary_waveforms_4 = [(0, 2, 0), (0, 0, 0), (0, 0, 3), (1, 2, 0),
                              (2, 0, 0), (0, 0, 3), (2, 2, 0), (0, 0, 0),
                              (0, 0, 3)]

        n_bin_waveforms = len(set(binary_waveforms_6)) + len(
            set(binary_waveforms_4))

        tek_waveforms_6 = [
            tek_awg.Waveform(channel=voltage_to_uint16(sampled_6[ch], 1, 0,
                                                       14),
                             marker_1=sampled_6[m1],
                             marker_2=sampled_6[m2])
            for (ch, m1, m2) in binary_waveforms_6
        ]

        tek_waveforms_4 = [
            tek_awg.Waveform(channel=voltage_to_uint16(sampled_4[ch], 1, 0,
                                                       14),
                             marker_1=sampled_4[m1],
                             marker_2=sampled_4[m2])
            for (ch, m1, m2) in binary_waveforms_4
        ]

        tek_waveforms = set(tek_waveforms_4 + tek_waveforms_6)

        # equivalent of wfs_6
        tek_6 = [
            tek_waveforms_6[:3] + [6], tek_waveforms_6[3:6] + [6],
            tek_waveforms_6[6:] + [6]
        ]
        tek_4 = [
            tek_waveforms_4[:3] + [4], tek_waveforms_4[3:6] + [4],
            tek_waveforms_4[6:] + [4]
        ]

        program = [(wfs_6[0], 1), (wfs_4[0], 2), (wfs_6[0], 3), (wfs_6[1], 4),
                   (wfs_4[1], 5), (wfs_6[2], 6), (wfs_4[2], 7), (wfs_6[1], 8),
                   (wfs_6[2], 9)]

        expected_sequence_entries_wfs = [
            tek_6[0], tek_4[0], tek_6[0], tek_6[1], tek_4[1], tek_6[2],
            tek_4[2], tek_6[1], tek_6[2]
        ]

        expected_sequence_entries = tuple(
            tek_awg.SequenceEntry(entries=wfs, loop_count=idx + 1)
            for idx, wfs in enumerate(expected_sequence_entries_wfs))

        loop_program = Loop(children=[
            Loop(waveform=waveform, repetition_count=repetition_count)
            for waveform, repetition_count in program
        ])

        sequence_entries, waveforms = parse_program(
            program=loop_program,
            channels=channels,
            markers=markers,
            sample_rate=sample_rate_in_GHz * 10**9,
            amplitudes=amplitudes,
            voltage_transformations=voltage_transformations,
            offsets=offsets)

        waveform_set = set(waveforms)
        self.assertEqual(len(waveform_set), len(waveforms))
        self.assertIn(4, waveforms)
        self.assertIn(6, waveforms)

        waveform_set = waveform_set - {4, 6}
        self.assertEqual(len(waveform_set), n_bin_waveforms)
        self.assertEqual(tek_waveforms, waveform_set)

        self.assertEqual(len(sequence_entries), 9)
        self.assertEqual(expected_sequence_entries, sequence_entries)
コード例 #13
0
 def duration(self) -> TimeType:
     return self.body_duration * TimeType.from_fraction(self.repetition_count, 1)
コード例 #14
0
    def test_upload_offset_handling(self):

        program = Loop(
            waveform=ConstantWaveform(channel=1, duration=192, amplitude=0.1))

        channel_pair = TaborChannelPair(self.instrument,
                                        identifier='asd',
                                        channels=(1, 2))

        channels = (1, None)
        markers = (None, None)

        tabor_program_kwargs = dict(
            channels=channels,
            markers=markers,
            device_properties=channel_pair.device.dev_properties)

        amplitudes = (0.5, 0.3)

        test_sample_rate = TimeType.from_fraction(1, 1)
        test_amplitudes = (0.5 / 2, 0.3 / 2)
        test_offset = 0.1
        test_transform = (lambda x: x, lambda x: x)

        with patch('qupulse.hardware.awgs.tabor.TaborProgram',
                   wraps=TaborProgram) as tabor_program_mock:
            with patch.object(self.instrument,
                              'offset',
                              return_value=test_offset) as offset_mock:
                tabor_program_mock.get_sampled_segments = mock.Mock(
                    wraps=tabor_program_mock.get_sampled_segments)

                self.instrument.amplitude = mock.Mock(side_effect=amplitudes)
                self.instrument.sample_rate = mock.Mock(return_value=10**9)

                channel_pair.amplitude_offset_handling = AWGAmplitudeOffsetHandling.CONSIDER_OFFSET
                channel_pair.upload('test1', program, channels, markers,
                                    test_transform)

                tabor_program_mock.assert_called_once_with(
                    program,
                    **tabor_program_kwargs,
                    sample_rate=test_sample_rate,
                    amplitudes=test_amplitudes,
                    offsets=(test_offset, test_offset),
                    voltage_transformations=test_transform)
                self.assertEqual([mock.call(1), mock.call(2)],
                                 offset_mock.call_args_list)
                offset_mock.reset_mock()
                tabor_program_mock.reset_mock()

                self.instrument.amplitude = mock.Mock(side_effect=amplitudes)
                self.instrument.sample_rate = mock.Mock(return_value=10**9)
                channel_pair.amplitude_offset_handling = AWGAmplitudeOffsetHandling.IGNORE_OFFSET
                channel_pair.upload('test2', program, (1, None), (None, None),
                                    test_transform)

                tabor_program_mock.assert_called_once_with(
                    program,
                    **tabor_program_kwargs,
                    sample_rate=test_sample_rate,
                    amplitudes=test_amplitudes,
                    offsets=(0., 0.),
                    voltage_transformations=test_transform)
                self.assertEqual([], offset_mock.call_args_list)
コード例 #15
0
    def test_upload(self):
        segments = np.array([1, 2, 3, 4, 5])
        segment_lengths = np.array([0, 16, 0, 16, 0], dtype=np.uint16).tolist()

        segment_references = np.array([1, 1, 2, 0, 1], dtype=np.uint32)

        w2s = np.array([-1, -1, 1, 2, -1], dtype=np.int64)
        ta = np.array([True, False, False, False, True])
        ti = np.array([-1, 3, -1, -1, -1])

        channels = (1, None)
        markers = (None, None)
        voltage_transformations = (lambda x: x, lambda x: x)
        sample_rate = TimeType.from_fraction(1, 1)

        with mock.patch('qupulse.hardware.awgs.tabor.TaborProgram',
                        specs=TaborProgram) as DummyTaborProgram:
            tabor_program = DummyTaborProgram.return_value
            tabor_program.get_sampled_segments.return_value = (segments,
                                                               segment_lengths)

            program = Loop(waveform=DummyWaveform(duration=192))

            channel_pair = TaborChannelPair(self.instrument,
                                            identifier='asd',
                                            channels=(1, 2))
            channel_pair._segment_references = segment_references

            def dummy_find_place(segments_, segement_lengths_):
                self.assertIs(segments_, segments)
                self.assertIs(segment_lengths, segement_lengths_)
                return w2s, ta, ti

            def dummy_upload_segment(segment_index, segment):
                self.assertEqual(segment_index, 3)
                self.assertEqual(segment, 2)

            def dummy_amend_segments(segments_):
                np.testing.assert_equal(segments_, np.array([1, 5]))
                return np.array([5, 6], dtype=np.int64)

            self.instrument.amplitude = mock.Mock(return_value=1.)
            self.instrument.sample_rate = mock.Mock(return_value=10**9)

            channel_pair._find_place_for_segments_in_memory = dummy_find_place
            channel_pair._upload_segment = dummy_upload_segment
            channel_pair._amend_segments = dummy_amend_segments

            channel_pair.upload('test', program, channels, markers,
                                voltage_transformations)

            DummyTaborProgram.assert_called_once_with(
                program,
                channels=tuple(channels),
                markers=markers,
                device_properties=channel_pair.device.dev_properties,
                sample_rate=sample_rate,
                amplitudes=(.5, .5),
                offsets=(0., 0.),
                voltage_transformations=voltage_transformations)

            self.assertEqual(self.instrument.amplitude.call_args_list,
                             [mock.call(1), mock.call(2)])
            self.instrument.sample_rate.assert_called_once_with(1)

            # the other references are increased in amend and upload segment method
            np.testing.assert_equal(channel_pair._segment_references,
                                    np.array([1, 2, 3, 0, 1]))

            self.assertEqual(len(channel_pair._known_programs), 1)
            np.testing.assert_equal(
                channel_pair._known_programs['test'].waveform_to_segment,
                np.array([5, 3, 1, 2, 6], dtype=np.int64))
コード例 #16
0
ファイル: loop_tests.py プロジェクト: qutech-lab/qupulse
    def test_roll_constant_waveforms(self):
        root = Loop(waveform=ConstantWaveform.from_mapping(
            16, {
                'A': 1.,
                'B': 0.5
            }),
                    repetition_count=4)
        expected = copy.deepcopy(root)
        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
        self.assertEqual(root, expected)

        root = Loop(waveform=ConstantWaveform.from_mapping(
            32, {
                'A': 1.,
                'B': 0.5
            }),
                    repetition_count=4)
        expected = Loop(waveform=ConstantWaveform.from_mapping(
            16, {
                'A': 1.,
                'B': 0.5
            }),
                        repetition_count=8)
        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
        self.assertEqual(root, expected)

        root = Loop(waveform=ConstantWaveform.from_mapping(
            16 * 3, {
                'A': 1.,
                'B': 0.5
            }),
                    repetition_count=4)
        expected = Loop(waveform=ConstantWaveform.from_mapping(
            16, {
                'A': 1.,
                'B': 0.5
            }),
                        repetition_count=12)
        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
        self.assertEqual(root, expected)

        root = Loop(waveform=ConstantWaveform.from_mapping(
            16 * 3, {
                'A': 1.,
                'B': 0.5
            }),
                    repetition_count=4)
        expected = copy.deepcopy(root)
        roll_constant_waveforms(root, 2, 16, TimeType.from_fraction(1, 1))
        self.assertEqual(root, expected)

        root = Loop(waveform=ConstantWaveform.from_mapping(
            16 * 5, {
                'A': 1.,
                'B': 0.5
            }),
                    repetition_count=4)
        expected = copy.deepcopy(root)
        roll_constant_waveforms(root, 2, 16, TimeType.from_fraction(1, 1))
        self.assertEqual(root, expected)

        root = Loop(children=[
            Loop(waveform=ConstantWaveform.from_mapping(
                32, {
                    'A': 1.,
                    'B': 0.5
                }),
                 repetition_count=4),
            Loop(waveform=ConstantWaveform.from_mapping(
                16, {
                    'A': 1.,
                    'B': 0.3
                }),
                 repetition_count=4),
            Loop(waveform=ConstantWaveform.from_mapping(
                128, {
                    'A': .1,
                    'B': 0.5
                }),
                 repetition_count=2)
        ])
        expected = Loop(children=[
            Loop(waveform=ConstantWaveform.from_mapping(
                16, {
                    'A': 1.,
                    'B': 0.5
                }),
                 repetition_count=8),
            Loop(waveform=ConstantWaveform.from_mapping(
                16, {
                    'A': 1.,
                    'B': 0.3
                }),
                 repetition_count=4),
            Loop(waveform=ConstantWaveform.from_mapping(
                16, {
                    'A': .1,
                    'B': 0.5
                }),
                 repetition_count=2 * 128 // 16)
        ])
        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
        self.assertEqual(root, expected)

        not_constant_wf = DummyWaveform(sample_output=np.array([.1, .2, .3]),
                                        duration=TimeType.from_fraction(32, 1),
                                        defined_channels={'A', 'B'})

        root = Loop(waveform=not_constant_wf, repetition_count=4)
        expected = copy.deepcopy(root)
        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
        self.assertEqual(root, expected)

        scope = DictScope.from_mapping({'a': 4, 'b': 3}, volatile={'a'})
        rep_count = VolatileRepetitionCount(expression=ExpressionScalar('a+b'),
                                            scope=scope)
        root = Loop(waveform=ConstantWaveform.from_mapping(
            32, {
                'A': 1.,
                'B': 0.5
            }),
                    repetition_count=rep_count)
        expected = Loop(waveform=ConstantWaveform.from_mapping(
            16, {
                'A': 1.,
                'B': 0.5
            }),
                        repetition_count=rep_count * 2)
        self.assertNotEqual(root.repetition_count, expected.repetition_count)
        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
        self.assertEqual(root, expected)