def test_undefined_comparison(self): valued = Expression(2) unknown = Expression('a') self.assertIsNone(unknown < 0) self.assertIsNone(unknown > 0) self.assertIsNone(unknown >= 0) self.assertIsNone(unknown <= 0) self.assertFalse(unknown == 0) self.assertIsNone(0 < unknown) self.assertIsNone(0 > unknown) self.assertIsNone(0 <= unknown) self.assertIsNone(0 >= unknown) self.assertFalse(0 == unknown) self.assertIsNone(unknown < valued) self.assertIsNone(unknown > valued) self.assertIsNone(unknown >= valued) self.assertIsNone(unknown <= valued) self.assertFalse(unknown == valued) valued, unknown = unknown, valued self.assertIsNone(unknown < valued) self.assertIsNone(unknown > valued) self.assertIsNone(unknown >= valued) self.assertIsNone(unknown <= valued) self.assertFalse(unknown == valued) valued, unknown = unknown, valued self.assertFalse(unknown == valued)
def __init__(self, expression: Union[str, Expression], duration_expression: Union[str, Expression], measurement: bool = False, identifier: str = None) -> None: """Create a new FunctionPulseTemplate instance. Args: expression (str or Expression): The function represented by this FunctionPulseTemplate as a mathematical expression where 't' denotes the time variable and other variables will be parameters of the pulse. duration_expression (str or Expression): A mathematical expression which reliably computes the duration of an instantiation of this FunctionPulseTemplate from provided parameter values. measurement (bool): True, if this FunctionPulseTemplate shall define a measurement window. (optional, default = False) identifier (str): A unique identifier for use in serialization. (optional) """ super().__init__(identifier) self.__expression = expression if not isinstance(self.__expression, Expression): self.__expression = Expression(self.__expression) self.__duration_expression = duration_expression if not isinstance(self.__duration_expression, Expression): self.__duration_expression = Expression(self.__duration_expression) self.__is_measurement_pulse = measurement # type: bool self.__parameter_names = set(self.__duration_expression.variables() + self.__expression.variables()) - set( ['t'])
def test_symbolic_math(self): a = Expression('a') b = Expression('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_build_waveform(self) -> None: wf = self.fpt.build_waveform(self.args) self.assertIsNotNone(wf) self.assertIsInstance(wf, FunctionWaveform) expected_waveform = FunctionWaveform(dict(a=3, y=1), Expression(self.f), Expression(self.duration)) self.assertEqual(expected_waveform, wf)
def test_evaluate_numeric(self) -> None: e = Expression('a * b + c') params = {'a': 2, 'b': 1.5, 'c': -7} self.assertEqual(2 * 1.5 - 7, e.evaluate_numeric(**params)) with self.assertRaises(NonNumericEvaluation): params['a'] = sympify('h') e.evaluate_numeric(**params)
def test_sample(self) -> None: f = Expression("(t+1)**b") length = Expression("c**b") par = {"b": 2, "c": 10} fw = FunctionWaveform(par, f, length) a = np.arange(4) expected_result = [[1, 4, 9, 16]] result = fw.sample(a) self.assertTrue(np.all(result == expected_result))
def test_parameter_names_and_declarations_expression_input(self) -> None: template = FunctionPulseTemplate(Expression("3 * foo + bar * t"), Expression("5 * hugo")) expected_parameter_names = {'foo', 'bar', 'hugo'} self.assertEqual(expected_parameter_names, template.parameter_names) self.assertEqual( {ParameterDeclaration(name) for name in expected_parameter_names}, template.parameter_declarations)
def test_evaluate_numpy(self): e = Expression('a * b + c') params = { 'a': 2 * np.ones(4), 'b': 1.5 * np.ones(4), 'c': -7 * np.ones(4) } np.testing.assert_equal((2 * 1.5 - 7) * np.ones(4), e.evaluate_numeric(**params))
def test_equality(self) -> None: wf1a = FunctionWaveform(Expression('2*t'), 3, channel='A') wf1b = FunctionWaveform(Expression('2*t'), 3, channel='A') wf3 = FunctionWaveform(Expression('2*t+2'), 3, channel='A') wf4 = FunctionWaveform(Expression('2*t'), 4, channel='A') self.assertEqual(wf1a, wf1a) self.assertEqual(wf1a, wf1b) self.assertNotEqual(wf1a, wf3) self.assertNotEqual(wf1a, wf4)
def __init__(self, relation: Union[str, sympy.Expr]): super().__init__() if isinstance(relation, str) and '==' in relation: # The '==' operator is interpreted by sympy as exactly, however we need a symbolical evaluation self._expression = sympy.Eq(*sympy.sympify(relation.split('=='))) else: self._expression = sympy.sympify(relation) if not isinstance(self._expression, sympy.boolalg.Boolean): raise ValueError('Constraint is not boolean') self._expression = Expression(self._expression)
def test_get_serialization_data(self) -> None: expected_data = dict(measurements=self.measurements, entries=self.entries, parameter_constraints=[ str(Expression('ilse>2')), str(Expression('k>foo')) ]) data = self.template.get_serialization_data(self.serializer) self.assertEqual(expected_data, data)
def test_known_interpolation_strategies(self): strategies = [("linear", LinearInterpolationStrategy()), ("hold", HoldInterpolationStrategy()), ("jump", JumpInterpolationStrategy())] for strat_name, strat_val in strategies: entry = TableEntry('a', Expression('b'), strat_name) self.assertEqual(entry.t, Expression('a')) self.assertEqual(entry.v, Expression('b')) self.assertEqual(entry.interp, strat_val)
def test_get_serialization_data(self) -> None: expected_data = dict(measurements=self.measurements, time_point_tuple_list=self.entries, channel_names=(0, 'A'), parameter_constraints=[ str(Expression('ilse>2')), str(Expression('k>foo')) ]) data = self.template.get_serialization_data(self.serializer) self.assertEqual(expected_data, data)
def test_add(self) -> None: map = PulseTemplateParameterMapping({'bar'}) dummy1 = DummyPulseTemplate(parameter_names={'foo', 'hugo'}) dummy2 = DummyPulseTemplate(parameter_names={'grr'}) map.add(dummy1, 'foo', '4*bar') map.add(dummy2, 'grr', Expression('bar ** 2')) map.add(dummy1, 'hugo', '3') map.add(dummy2, 'grr', Expression('sin(bar)')) self.assertEqual(dict(foo=Expression('4*bar'), hugo=Expression('3')), map.get_template_map(dummy1)) self.assertEqual(dict(grr=Expression('sin(bar)')), map.get_template_map(dummy2))
def test_map_parameters(self) -> None: map = PulseTemplateParameterMapping({'bar', 'barbar'}) dummy = DummyPulseTemplate(parameter_names={'foo', 'hugo'}) map.add(dummy, 'hugo', '4*bar') map.add(dummy, 'foo', Expression('barbar')) mapped = map.map_parameters( dummy, dict(bar=ConstantParameter(3), barbar=ConstantParameter(5))) self.assertEqual( dict(hugo=MappedParameter(Expression('4*bar'), dict(bar=ConstantParameter(3))), foo=MappedParameter(Expression('barbar'), dict(barbar=ConstantParameter(5)))), mapped)
def __init__(self, measurements: Optional[List[MeasurementDeclaration]]): if measurements is None: self._measurement_windows = [] else: self._measurement_windows = [ (name, begin if isinstance(begin, Expression) else Expression(begin), length if isinstance(length, Expression) else Expression(length)) for name, begin, length in measurements ] for _, _, length in self._measurement_windows: if (length < 0) is True: raise ValueError( 'Measurement window length may not be negative')
def test_requires_stop_and_get_value(self) -> None: p = MappedParameter(Expression("foo + bar * hugo")) with self.assertRaises(ParameterNotProvidedException): p.requires_stop with self.assertRaises(ParameterNotProvidedException): p.get_value() foo = DummyParameter(-1.1) bar = DummyParameter(0.5) hugo = DummyParameter(5.2, requires_stop=True) ilse = DummyParameter(2356.4, requires_stop=True) p.dependencies = {'foo': foo, 'hugo': hugo, 'ilse': ilse} with self.assertRaises(ParameterNotProvidedException): p.requires_stop with self.assertRaises(ParameterNotProvidedException): p.get_value() p.dependencies = {'foo': foo, 'bar': bar, 'hugo': hugo} self.assertTrue(p.requires_stop) with self.assertRaises(Exception): p.get_value() hugo = DummyParameter(5.2, requires_stop=False) p.dependencies = {'foo': foo, 'bar': bar, 'hugo': hugo, 'ilse': ilse} self.assertFalse(p.requires_stop) self.assertEqual(1.5, p.get_value())
def test_equality(self) -> None: wf1a = FunctionWaveform(dict(a=2, b=1), Expression('a*t'), Expression('b')) wf1b = FunctionWaveform(dict(a=2, b=1), Expression('a*t'), Expression('b')) wf2 = FunctionWaveform(dict(a=3, b=1), Expression('a*t'), Expression('b')) wf3 = FunctionWaveform(dict(a=2, b=1), Expression('a*t+2'), Expression('b')) wf4 = FunctionWaveform(dict(a=2, c=2), Expression('a*t'), Expression('c')) self.assertEqual(wf1a, wf1a) self.assertEqual(wf1a, wf1b) self.assertNotEqual(wf1a, wf2) self.assertNotEqual(wf1a, wf3) self.assertNotEqual(wf1a, wf4)
def test_map_parameters_not_provided(self) -> None: map = PulseTemplateParameterMapping({'bar', 'barbar'}) dummy = DummyPulseTemplate(parameter_names={'foo', 'hugo'}) map.add(dummy, 'hugo', '4*bar') map.add(dummy, 'foo', Expression('barbar')) with self.assertRaises(ParameterNotProvidedException): map.map_parameters(dummy, dict(bar=ConstantParameter(3)))
def test_deserialize(self) -> None: dummy1 = DummyPulseTemplate(parameter_names={'foo'}, num_channels=2) dummy2 = DummyPulseTemplate(parameter_names={}, num_channels=1) exp = Expression("bar - 35") data = dict(external_parameters=['bar'], subtemplates=[ dict(template=str(id(dummy1)), parameter_mappings=dict(foo=str(exp)), channel_mappings=[0, 2]), dict(template=str(id(dummy2)), parameter_mappings=dict(), channel_mappings=[1]) ]) serializer = DummySerializer(serialize_callback=lambda x: str(x) if isinstance(x, Expression) else str(id(x))) serializer.subelements[str(id(dummy1))] = dummy1 serializer.subelements[str(id(dummy2))] = dummy2 serializer.subelements[str(exp)] = exp template = MultiChannelPulseTemplate.deserialize(serializer, **data) self.assertEqual(set(data['external_parameters']), template.parameter_names) self.assertEqual({ParameterDeclaration('bar')}, template.parameter_declarations) recovered_data = template.get_serialization_data(serializer) self.assertEqual(data, recovered_data)
def test_time_is_0_on_instantiation(self): table = TablePulseTemplate({0: [('a', 1)]}) self.assertEqual(table.duration, Expression('a')) self.assertEqual(table.parameter_names, {'a'}) self.assertIsNone( table.build_waveform(parameters=dict(a=0), channel_mapping={0: 0}))
def test_get_most_simple_representation(self): cpl = Expression('1 + 1j').get_most_simple_representation() self.assertIsInstance(cpl, complex) self.assertEqual(cpl, 1 + 1j) integer = Expression('3').get_most_simple_representation() self.assertIsInstance(integer, int) self.assertEqual(integer, 3) flt = Expression('3.').get_most_simple_representation() self.assertIsInstance(flt, float) self.assertEqual(flt, 3.) st = Expression('a + b').get_most_simple_representation() self.assertIsInstance(st, str) self.assertEqual(st, 'a + b')
def add(self, template: PulseTemplate, parameter: str, mapping_expression: Union[str, Expression]): """Add a new mapping for a parameter of a pulse template. Args: template (PulseTemplate): The pulse template for which a parameter mapping will be added. parameter (str): The name of the parameter of the pulse template that will be mapped. mapping_expression (str or Expression): The mathematical expression that specifies the mapping from external parameters to the parameter of the pulse template. Raises: UnnecessaryMappingException, if parameter is not declared by template. MissingParameterDeclarationException, if mapping_expression requires a variable that is not a parameter in the external parameters of this PulseTemplateParameterMapping. """ if parameter not in template.parameter_names: raise UnnecessaryMappingException(template, parameter) if isinstance(mapping_expression, str): mapping_expression = Expression(mapping_expression) required_externals = set(mapping_expression.variables()) non_declared_externals = required_externals - self.__external_parameters if non_declared_externals: raise MissingParameterDeclarationException( template, non_declared_externals.pop()) template_map = self.__get_template_map(template) template_map[parameter] = mapping_expression self.__map[template] = template_map
def test_expression_variable_missing(self): variable = 's' expression = Expression('s*t') self.assertEqual( str(ExpressionVariableMissingException(variable, expression)), "Could not evaluate <s*t>: A value for variable <s> is missing!")
def test_get_remaining_mappings(self) -> None: map = PulseTemplateParameterMapping({'bar', 'barbar'}) dummy = DummyPulseTemplate(parameter_names={'foo', 'hugo'}) self.assertEqual({'foo', 'hugo'}, map.get_remaining_mappings(dummy)) map.add(dummy, 'hugo', '4*bar') self.assertEqual({'foo'}, map.get_remaining_mappings(dummy)) map.add(dummy, 'foo', Expression('barbar')) self.assertEqual(set(), map.get_remaining_mappings(dummy))
def test_number_math(self): a = Expression('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_deserialize(self) -> None: basic_data = dict(duration_expression=str(self.s2), expression=str(self.s), measurement=False, identifier='hugo') serializer = DummySerializer(serialize_callback=lambda x: str(x)) serializer.subelements[str(self.s2)] = Expression(self.s2) serializer.subelements[str(self.s)] = Expression(self.s) template = FunctionPulseTemplate.deserialize(serializer, **basic_data) self.assertEqual('hugo', template.identifier) self.assertEqual({'a', 'b', 'c'}, template.parameter_names) self.assertEqual( {ParameterDeclaration(name) for name in {'a', 'b', 'c'}}, template.parameter_declarations) serialized_data = template.get_serialization_data(serializer) del basic_data['identifier'] self.assertEqual(basic_data, serialized_data)
def test_get_serialization_data(self) -> None: exp = Expression("foo + bar * hugo") p = MappedParameter(exp) serializer = DummySerializer() data = p.get_serialization_data(serializer) self.assertEqual( dict(type=serializer.get_type_identifier(p), expression=str(id(exp))), data)
def test_build_waveform(self) -> None: with self.assertRaises(ParameterConstraintViolation): self.fpt.build_waveform(self.invalid_par_vals, channel_mapping={'A': 'B'}) wf = self.fpt.build_waveform(self.valid_par_vals, channel_mapping={'A': 'B'}) self.assertIsNotNone(wf) self.assertIsInstance(wf, FunctionWaveform) expression = Expression(self.s).evaluate_symbolic(self.valid_par_vals) duration = Expression( self.s2).evaluate_numeric(c=self.valid_par_vals['c']) expected_waveform = FunctionWaveform(expression, duration=duration, channel='B') self.assertEqual(expected_waveform, wf)
def test_deserialize(self) -> None: basic_data = dict(duration_expression=str(self.s2), expression=self.s, channel='A', identifier='hugo', measurement_declarations=self.meas_list, parameter_constraints=self.constraints) serializer = DummySerializer( serialize_callback=lambda x: x.original_expression) serializer.subelements[self.s2] = Expression(self.s2) serializer.subelements[self.s] = Expression(self.s) template = FunctionPulseTemplate.deserialize(serializer, **basic_data) self.assertEqual('hugo', template.identifier) self.assertEqual({'a', 'b', 'c', 'x', 'z', 'j', 'u', 'd'}, template.parameter_names) self.assertEqual(template.measurement_declarations, self.meas_list) serialized_data = template.get_serialization_data(serializer) del basic_data['identifier'] self.assertEqual(basic_data, serialized_data)
def __init__(self, expression: str, duration_expression: str, measurement: bool=False) -> None: super().__init__() self.__expression = Expression(expression) self.__duration_expression = Expression(duration_expression) self.__is_measurement_pulse = measurement # type: bool self.__parameter_names = set(self.__duration_expression.variables() + self.__expression.variables()) - set(['t'])
class FunctionPulseTemplate(PulseTemplate): """Defines a pulse via a time-domain expression. FunctionPulseTemplate stores the expression and its external parameters. The user must provide two things: one expression that calculates the length of the pulse from the external parameters and the time-domain pulse shape itself as a expression. The external parameters are derived from the expressions themselves. Like other PulseTemplates the FunctionPulseTemplate can be declared to be a measurement pulse. The independent variable in the expression is called 't' and is given in units of nano-seconds. """ def __init__(self, expression: str, duration_expression: str, measurement: bool=False) -> None: super().__init__() self.__expression = Expression(expression) self.__duration_expression = Expression(duration_expression) self.__is_measurement_pulse = measurement # type: bool self.__parameter_names = set(self.__duration_expression.variables() + self.__expression.variables()) - set(['t']) @property def parameter_names(self) -> Set[str]: """Return the set of names of declared parameters.""" return self.__parameter_names @property def parameter_declarations(self) -> Set[ParameterDeclaration]: """Return a set of all parameter declaration objects of this TablePulseTemplate.""" return set() def get_pulse_length(self, parameters) -> float: """Return the length of this pulse for the given parameters.""" missing = self.__parameter_names - set(parameters.keys()) for m in missing: raise ParameterNotProvidedException(m) return self.__duration_expression.evaluate(**parameters) def get_measurement_windows(self, parameters: Optional[Dict[str, Parameter]] = {}) -> List[MeasurementWindow]: """Return all measurement windows defined in this PulseTemplate. A ExpressionPulseTemplate specifies either no measurement windows or exactly one that spans its entire duration, depending on whether the measurement_pulse flag was given during construction. """ if not self.__is_measurement_pulse: return else: return [(0, self.get_pulse_length(parameters))] @property def is_interruptable(self) -> bool: """Return true, if this PulseTemplate contains points at which it can halt if interrupted.""" return False def build_sequence(self, sequencer: Sequencer, parameters: Dict[str, Parameter], conditions: Dict[str, 'Condition'], instruction_block: InstructionBlock) -> None: waveform = FunctionWaveform(parameters, self.__expression, self.__duration_expression) instruction_block.add_instruction_exec(waveform) def requires_stop(self, parameters: Dict[str, Parameter], conditions: Dict[str, 'Condition']) -> bool: return any(parameters[name].requires_stop for name in parameters.keys() if (name in self.parameter_names) and not isinstance(parameters[name], numbers.Number)) def get_serialization_data(self, serializer: Serializer) -> None: root = dict() root['type'] = 'FunctionPulseTemplate' root['parameter_names'] = self.__parameter_names root['duration_expression'] = self.__duration_expression.string root['expression'] = self.__expression.string root['measurement'] = self.__is_measurement_pulse return root @staticmethod def deserialize(serializer: 'Serializer', **kwargs) -> 'Serializable': return FunctionPulseTemplate(kwargs['expression'], kwargs['duration_expression'], kwargs['Measurement'])