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))
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]}))
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)
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)
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)
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)
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
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
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()
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)) }
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)
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)
def duration(self) -> TimeType: return self.body_duration * TimeType.from_fraction(self.repetition_count, 1)
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)
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))
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)