def test_format(self): expr = ExpressionScalar('17') e_format = '{:.4e}'.format(expr) self.assertEqual(e_format, "1.7000e+01") empty_format = "{}".format(expr) self.assertEqual(empty_format, '17') expr_with_var = ExpressionScalar('17*a') with self.assertRaises(TypeError): # throw error on implicit float cast '{:.4e}'.format(expr_with_var) empty_format = "{}".format(expr_with_var) self.assertEqual(empty_format, '17*a')
def __init__(self, duration: float, amplitude_dict: Dict[str, Any], identifier: Optional[str] = None, name: Optional[str] = None, measurements: Optional[List[MeasurementDeclaration]] = [], **kwargs: Any) -> None: """ A qupulse waveform representing a multi-channel pulse with constant values Args: duration: Duration of the template amplitude_dict: Dictionary with values for the channels name: Name for the template """ super().__init__(identifier=identifier, measurements=measurements, **kwargs) self._duration = ExpressionScalar(duration) self._amplitude_dict = amplitude_dict if name is None: name = 'constant_pulse' self._name = name
def test_body_scope_generator(self): dt = DummyPulseTemplate(parameter_names={'i', 'k'}) flt = ForLoopPulseTemplate(body=dt, loop_index='i', loop_range=('a', 'b', 'c')) expected_range = range(2, 17, 3) outer_scope = DictScope.from_kwargs(k=5, a=expected_range.start, b=expected_range.stop, c=expected_range.step, volatile={'i', 'j'}) forward_scopes = list( flt._body_scope_generator(outer_scope, forward=True)) backward_scopes = list( flt._body_scope_generator(outer_scope, forward=False)) volatile_dict = FrozenDict(j=ExpressionScalar('j')) self.assertEqual(forward_scopes, list(reversed(backward_scopes))) for scope, i in zip(forward_scopes, expected_range): self.assertEqual(volatile_dict, scope.get_volatile_parameters()) expected_dict_equivalent = dict(k=5, i=i, a=expected_range.start, b=expected_range.stop, c=expected_range.step) self.assertEqual(expected_dict_equivalent, dict(scope.items()))
def test_expression_variable_missing(self): variable = 's' expression = ExpressionScalar('s*t') self.assertEqual( str(ExpressionVariableMissingException(variable, expression)), "Could not evaluate <s*t>: A value for variable <s> is missing!")
def integral(self) -> Dict[ChannelID, ExpressionScalar]: return { self.__channel: ExpressionScalar( sympy.integrate(self.__expression.sympified_expression, ('t', 0, self.duration.sympified_expression))) }
def calculate_duration(self) -> ExpressionScalar: duration_expressions = [ entries[-1].t for entries in self._entries.values() ] duration_expression = sympy.Max(*(expr.sympified_expression for expr in duration_expressions)) return ExpressionScalar(duration_expression)
def __init__(self, requires_stop: bool = False, is_interruptable: bool = False, parameter_names: Set[str] = {}, defined_channels: Set[ChannelID] = {'default'}, duration: Any = 0, waveform: Waveform = tuple(), measurement_names: Set[str] = set(), measurements: list = list(), integrals: Dict[ChannelID, ExpressionScalar] = { 'default': ExpressionScalar(0) }, program: Optional[Loop] = None, identifier=None, registry=None) -> None: super().__init__(identifier=identifier, measurements=measurements) self.requires_stop_ = requires_stop self.requires_stop_arguments = [] self.is_interruptable_ = is_interruptable self.parameter_names_ = parameter_names self.build_sequence_arguments = [] self.defined_channels_ = defined_channels self._duration = Expression(duration) self.waveform = waveform self.build_waveform_calls = [] self.measurement_names_ = set(measurement_names) self._integrals = integrals self.create_program_calls = [] self._program = program self._register(registry=registry)
def duration(self) -> ExpressionScalar: step_size = self._loop_range.step.sympified_expression loop_index = sympy.symbols(self._loop_index) sum_index = sympy.symbols(self._loop_index) # replace loop_index with sum_index dependable expression body_duration = self.body.duration.sympified_expression.subs({ loop_index: self._loop_range.start.sympified_expression + sum_index * step_size }) # number of sum contributions step_count = sympy.ceiling( (self._loop_range.stop.sympified_expression - self._loop_range.start.sympified_expression) / step_size) sum_start = 0 sum_stop = sum_start + (sympy.functions.Max(step_count, 1) - 1) # expression used if step_count >= 0 finite_duration_expression = sympy.Sum( body_duration, (sum_index, sum_start, sum_stop)) duration_expression = sympy.Piecewise( (0, step_count <= 0), (finite_duration_expression, True)) return ExpressionScalar(duration_expression)
def integral(self) -> Dict[ChannelID, ExpressionScalar]: step_size = self._loop_range.step.sympified_expression loop_index = sympy.symbols(self._loop_index) sum_index = sympy.symbols(self._loop_index) body_integrals = self.body.integral body_integrals = { c: body_integrals[c].sympified_expression.subs({ loop_index: self._loop_range.start.sympified_expression + sum_index * step_size }) for c in body_integrals } # number of sum contributions step_count = sympy.ceiling( (self._loop_range.stop.sympified_expression - self._loop_range.start.sympified_expression) / step_size) sum_start = 0 sum_stop = sum_start + (sympy.functions.Max(step_count, 1) - 1) for c in body_integrals: channel_integral_expr = sympy.Sum(body_integrals[c], (sum_index, sum_start, sum_stop)) body_integrals[c] = ExpressionScalar(channel_integral_expr) return body_integrals
def integral(self) -> Dict[ChannelID, ExpressionScalar]: expressions = {channel: 0 for channel in self._channels} for first_entry, second_entry in zip(self._entries[:-1], self._entries[1:]): substitutions = { 't0': first_entry.t.sympified_expression, 't1': second_entry.t.sympified_expression } v0 = sympy.IndexedBase( Broadcast(first_entry.v.underlying_expression, (len(self.defined_channels), ))) v1 = sympy.IndexedBase( Broadcast(second_entry.v.underlying_expression, (len(self.defined_channels), ))) for i, channel in enumerate(self._channels): substitutions['v0'] = v0[i] substitutions['v1'] = v1[i] expressions[ channel] += first_entry.interp.integral.sympified_expression.subs( substitutions) expressions = { c: ExpressionScalar(expressions[c]) for c in expressions } return expressions
def __init__(self, requires_stop: bool=False, parameter_names: Set[str]=set(), defined_channels: Set[ChannelID]=None, duration: Any=0, waveform: Waveform=tuple(), measurement_names: Set[str] = set(), measurements: list=list(), integrals: Dict[ChannelID, ExpressionScalar]=None, program: Optional[Loop]=None, identifier=None, registry=None) -> None: super().__init__(identifier=identifier, measurements=measurements) self.requires_stop_ = requires_stop self.requires_stop_arguments = [] if defined_channels is None: defined_channels = {'default'} if integrals is None: integrals = {ch: ExpressionScalar(0) for ch in defined_channels} self.parameter_names_ = parameter_names self.defined_channels_ = defined_channels self._duration = Expression(duration) self.waveform = waveform self.build_waveform_calls = [] self.measurement_names_ = set(measurement_names) self._integrals = integrals self.create_program_calls = [] self._program = program self._register(registry=registry) if integrals is not None: assert isinstance(integrals, Mapping)
def concatenate(*table_pulse_templates: TablePulseTemplate, **kwargs) -> TablePulseTemplate: """Concatenate two or more table pulse templates""" first_template, *other_templates = table_pulse_templates entries = {channel: [] for channel in first_template.defined_channels} duration = ExpressionScalar(0) for i, template in enumerate(table_pulse_templates): if not isinstance(template, TablePulseTemplate): raise TypeError('Template number %d is not a TablePulseTemplate' % i) new_duration = duration + template.duration if template.defined_channels != first_template.defined_channels: raise ValueError('Template number %d has differing defined channels' % i, first_template.defined_channels, template.defined_channels) for channel, channel_entries in template.entries.items(): first_t, first_v, _ = channel_entries[0] if i > 0 and first_t != 0: if (first_v == 0) is False: entries[channel].append((duration, first_v, 'hold')) for t, v, interp in channel_entries: entries[channel].append((duration.sympified_expression + t, v, interp)) last_t, last_v, _ = channel_entries[-1] if i < len(other_templates) and last_t != new_duration: entries[channel].append((new_duration, last_v, TablePulseTemplate.interpolation_strategies['hold'])) duration = new_duration return TablePulseTemplate(entries, **kwargs)
def test_simple_attributes(self): lhs = DummyPulseTemplate(defined_channels={'a', 'b'}, duration=ExpressionScalar('t_dur'), measurement_names={'m1'}) rhs = 4 arith = ArithmeticPulseTemplate(lhs, '+', rhs) self.assertIs(lhs.duration, arith.duration) self.assertIs(lhs.measurement_names, arith.measurement_names)
def _sequence_integral( cls, entry_sequence: Sequence['TableEntry'], expression_extractor: Callable[[Expression], sympy.Expr] ) -> ExpressionScalar: """Returns an expression for the time integral over the complete sequence of table entries. Args: entry_sequence: Sequence of table entries. Assumed to be ordered by time. expression_extractor: Convert each entry's voltage into a sympy expression. Can be used to select single channels from a vectorized expression. Returns: Scalar expression for the integral. """ expr = 0 for first_entry, second_entry in pairwise(entry_sequence): substitutions = { 't0': first_entry.t.sympified_expression, 'v0': expression_extractor(first_entry.v), 't1': second_entry.t.sympified_expression, 'v1': expression_extractor(second_entry.v) } expr += second_entry.interp.integral.sympified_expression.subs( substitutions, simultaneous=True) return ExpressionScalar(expr)
def test_scaling(self): from qupulse.pulses import plotting parameters = {**self.parameters, 'foo': 5.3} t_ref, reference, _ = plotting.render(self.complex_pt.create_program(parameters=parameters)) for factor in (5, 5.3, 'foo'): scaled = factor * self.complex_pt real_scale = ExpressionScalar(factor).evaluate_numeric(**parameters) program = scaled.create_program(parameters=parameters) t, rendered, _ = plotting.render(program, 10.) np.testing.assert_equal(t_ref, t) for ch, volts in rendered.items(): np.testing.assert_allclose(reference[ch] * real_scale, volts) divided = self.complex_pt / factor t, rendered, _ = plotting.render(divided.create_program(parameters=parameters), 10.) np.testing.assert_equal(t_ref, t) for ch, volts in rendered.items(): np.testing.assert_allclose(reference[ch] / real_scale, volts) sel_scaled = {'X': factor} * self.complex_pt t, rendered, _ = plotting.render(sel_scaled.create_program(parameters=parameters), 10.) np.testing.assert_equal(t_ref, t) for ch, volts in rendered.items(): if ch == 'X': np.testing.assert_allclose(reference[ch] * real_scale, volts) else: np.testing.assert_equal(reference[ch], volts)
def test_internal_create_program(self): sub_templates = PulseTemplateStub(defined_channels={'a'}, duration=ExpressionScalar('t1')),\ PulseTemplateStub(defined_channels={'a'}, duration=ExpressionScalar('t2')) wfs = DummyWaveform(duration=1), DummyWaveform(duration=2) spt = SequencePulseTemplate(*sub_templates, measurements=[('m', 'a', 'b')]) kwargs = dict(scope=DictScope.from_kwargs(t1=.4, t2=.5, a=.1, b=.2, irrelevant=42), measurement_mapping={'m': 'l'}, channel_mapping={'g': 'h'}, global_transformation=TransformationStub(), to_single_waveform={'to', 'single', 'waveform'}) program = Loop() expected_program = Loop( children=[Loop(waveform=wfs[0]), Loop(waveform=wfs[1])], measurements=[('l', .1, .2)]) with mock.patch.object(spt, 'validate_scope') as validate_scope: with mock.patch.object(spt, 'get_measurement_windows', return_value=[('l', .1, .2) ]) as get_measurement_windows: with mock.patch.object(sub_templates[0], '_create_program', wraps=get_appending_internal_create_program(wfs[0], True)) as create_0,\ mock.patch.object(sub_templates[1], '_create_program', wraps=get_appending_internal_create_program(wfs[1], True)) as create_1: spt._internal_create_program(**kwargs, parent_loop=program) self.assertEqual(expected_program, program) validate_scope.assert_called_once_with(kwargs['scope']) get_measurement_windows.assert_called_once_with( kwargs['scope'], kwargs['measurement_mapping']) create_0.assert_called_once_with(**kwargs, parent_loop=program) create_1.assert_called_once_with(**kwargs, parent_loop=program)
def test_number_math(self): a = ExpressionScalar('a') b = 3.3 self.assertExpressionEqual(a + b, b + a) self.assertExpressionEqual(a - b, -(b - a)) self.assertExpressionEqual(a * b, b * a) self.assertExpressionEqual(a / b, 1 / (b / a))
def test_sympy_math(self): a = ExpressionScalar('a') b = sympify('b') self.assertExpressionEqual(a + b, b + a) self.assertExpressionEqual(a - b, -(b - a)) self.assertExpressionEqual(a * b, b * a) self.assertExpressionEqual(a / b, 1 / (b / a))
def test_parse_operand(self): operand = {'a': 3, 'b': 'x'} with self.assertRaises(ValueError): ArithmeticPulseTemplate._parse_operand(operand, {'a'}) self.assertEqual( dict(a=ExpressionScalar(3), b=ExpressionScalar('x')), ArithmeticPulseTemplate._parse_operand(operand, {'a', 'b', 'c'})) expr_op = ExpressionScalar(3) self.assertIs( expr_op, ArithmeticPulseTemplate._parse_operand(expr_op, {'a', 'b', 'c'})) self.assertEqual( ExpressionScalar('foo'), ArithmeticPulseTemplate._parse_operand('foo', {'a', 'b', 'c'}))
def test_integral(self): integrals_lhs = dict(a=ExpressionScalar('a_lhs'), b=ExpressionScalar('b')) integrals_rhs = dict(a=ExpressionScalar('a_rhs'), c=ExpressionScalar('c')) lhs = DummyPulseTemplate(duration=4, defined_channels={'a', 'b'}, parameter_names={'x', 'y'}, integrals=integrals_lhs) rhs = DummyPulseTemplate(duration=4, defined_channels={'a', 'c'}, parameter_names={'x', 'z'}, integrals=integrals_rhs) expected_plus = dict(a=ExpressionScalar('a_lhs + a_rhs'), b=ExpressionScalar('b'), c=ExpressionScalar('c')) expected_minus = dict(a=ExpressionScalar('a_lhs - a_rhs'), b=ExpressionScalar('b'), c=ExpressionScalar('-c')) self.assertEqual(expected_plus, (lhs + rhs).integral) self.assertEqual(expected_minus, (lhs - rhs).integral)
def test_float_sample_time(self): # issue 624 body_wf = FunctionWaveform.from_expression(ExpressionScalar('sin(t)'), 1./3., channel='a') rwf = RepetitionWaveform(body_wf, 2) sample_times = np.arange(160) / 80. / 3. sampled = rwf.unsafe_sample(sample_times=sample_times, channel='a') inner_sample_times = np.concatenate((sample_times[:80], sample_times[80:] - 1./3.)) np.testing.assert_equal(sampled, np.sin(inner_sample_times))
def __new__(cls, t: ValueInInit, v: ValueInInit, interp: Optional[Union[str, InterpolationStrategy]]='default'): if interp in TablePulseTemplate.interpolation_strategies: interp = TablePulseTemplate.interpolation_strategies[interp] if interp is not None and not isinstance(interp, InterpolationStrategy): raise KeyError(interp, 'is not a valid interpolation strategy') return super().__new__(cls, ExpressionScalar.make(t), Expression.make(v), interp)
def from_expression( cls, expression: ExpressionScalar, duration: float, channel: ChannelID) -> Union['FunctionWaveform', ConstantWaveform]: if expression.variables: return cls(expression, duration, channel) else: return ConstantWaveform(amplitude=expression.evaluate_numeric(), duration=duration, channel=channel)
def test_make(self): self.assertTrue(Expression.make('a') == 'a') self.assertTrue(Expression.make('a + b') == 'a + b') self.assertTrue(Expression.make(9) == 9) self.assertIsInstance(Expression.make([1, 'a']), ExpressionVector) self.assertIsInstance(ExpressionScalar.make('a'), ExpressionScalar) self.assertIsInstance(ExpressionVector.make(['a']), ExpressionVector)
def test_update_volatile_parameters_with_depth1(self): parameters = {'s': 10, 'not': 13} s = VolatileRepetitionCount(expression=ExpressionScalar('s'), scope=DictScope(values=FrozenDict(s=3), volatile=set('s'))) wf_1 = DummyWaveform(defined_channels={'A'}, duration=1) wf_2 = DummyWaveform(defined_channels={'A'}, duration=1) program = Loop(children=[ Loop(waveform=wf_1, repetition_count=s), Loop(waveform=wf_2, repetition_count=4), Loop(waveform=wf_1, repetition_count=1) ], repetition_count=1) t_program = TaborProgram(program, channels=(None, 'A'), markers=(None, None), device_properties=self.instr_props, **self.program_entry_kwargs) self.assertEqual(t_program.get_sequencer_tables(), [[(TableDescription(3, 0, 0), s.volatile_property), (TableDescription(4, 1, 0), None), (TableDescription(1, 0, 0), None)]]) self.assertEqual(t_program.get_advanced_sequencer_table(), [TableDescription(1, 1, 0)]) modifications = t_program.update_volatile_parameters(parameters) expected_seq = VolatileRepetitionCount( expression=ExpressionScalar('s'), scope=DictScope(values=FrozenDict(s=10), volatile=set('s'))) expected_modifications = {(0, 0): TableDescription(10, 0, 0)} self.assertEqual( t_program.get_sequencer_tables(), [[(TableDescription(10, 0, 0), expected_seq.volatile_property), (TableDescription(4, 1, 0), None), (TableDescription(1, 0, 0), None)]]) self.assertEqual(t_program.get_advanced_sequencer_table(), [TableDescription(1, 1, 0)]) self.assertEqual(modifications, expected_modifications)
def test_sequence_as_expression(self): def get_sympy(v): return v.sympified_expression t = sympy.Dummy('t') times = { t: 0.5, 't0': 0.3, 't1': 0.7, 't2': 1.3, } entries = [TableEntry(0, 0, None), TableEntry(1, 0, 'hold')] self.assertEqual( ExpressionScalar(0), TableEntry._sequence_as_expression( entries, get_sympy, t, pre_value=None, post_value=None).sympified_expression.subs(times)) entries = [TableEntry(0, 1, None), TableEntry(1, 1, 'hold')] self.assertEqual( ExpressionScalar(1), TableEntry._sequence_as_expression( entries, get_sympy, t, pre_value=None, post_value=None).sympified_expression.subs(times)) entries = [TableEntry(0, 0, None), TableEntry(1, 1, 'linear')] self.assertEqual( ExpressionScalar(.5), TableEntry._sequence_as_expression( entries, get_sympy, t, pre_value=None, post_value=None).sympified_expression.subs(times)) entries = [ TableEntry('t0', 'a', 'linear'), TableEntry('t1', 'b', 'linear'), TableEntry('t2', 'c', 'hold') ] self.assertEqual( ExpressionScalar('(a+b)*.5'), TableEntry._sequence_as_expression( entries, get_sympy, t, pre_value=None, post_value=None).sympified_expression.subs(times))
def test_integral(self): template = DummyPulseTemplate(duration='t1', defined_channels={'X', 'Y'}, parameter_names={'a', 'b'}, measurement_names={'M'}, integrals={ 'X': ExpressionScalar('a'), 'Y': ExpressionScalar(4) }) overwritten_channels = {'Y': 'c', 'Z': 'a'} pccpt = ParallelConstantChannelPulseTemplate(template, overwritten_channels) expected_integral = { 'X': ExpressionScalar('a'), 'Y': ExpressionScalar('c*t1'), 'Z': ExpressionScalar('a*t1') } self.assertEqual(expected_integral, pccpt.integral)
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 test_non_numeric_evaluation(self): expression = ExpressionScalar('a*b') call_arguments = dict() expected = "The result of evaluate_numeric is of type {} " \ "which is not a number".format(float) self.assertEqual(str(NonNumericEvaluation(expression, 1., call_arguments)), expected) expected = "The result of evaluate_numeric is of type {} " \ "which is not a number".format(np.zeros(1).dtype) self.assertEqual(str(NonNumericEvaluation(expression, np.zeros(1), call_arguments)), expected)
def integral(self) -> Dict[ChannelID, ExpressionScalar]: expressions = dict() for channel, channel_entries in self._entries.items(): expr = 0 for first_entry, second_entry in zip(channel_entries[:-1], channel_entries[1:]): substitutions = { 't0': ExpressionScalar(first_entry.t).sympified_expression, 'v0': ExpressionScalar(first_entry.v).sympified_expression, 't1': ExpressionScalar(second_entry.t).sympified_expression, 'v1': ExpressionScalar(second_entry.v).sympified_expression } expr += first_entry.interp.integral.sympified_expression.subs( substitutions) expressions[channel] = ExpressionScalar(expr) return expressions