def __init__(self, *subtemplates: Union[PulseTemplate, MappingTuple], external_parameters: Optional[Union[Iterable[str], Set[str]]]=None, identifier: Optional[str]=None, parameter_constraints: Optional[List[Union[str, Expression]]]=None, measurements: Optional[List[MeasurementDeclaration]]=None) -> None: """Create a new SequencePulseTemplate instance. Requires a (correctly ordered) list of subtemplates in the form (PulseTemplate, Dict(str -> str)) where the dictionary is a mapping between the external parameters exposed by this SequencePulseTemplate to the parameters declared by the subtemplates, specifying how the latter are derived from the former, i.e., the mapping is subtemplate_parameter_name -> mapping_expression (as str) where the free variables in the mapping_expression are parameters declared by this SequencePulseTemplate. The following requirements must be satisfied: - for each parameter declared by a subtemplate, a mapping expression must be provided - each free variable in a mapping expression must be declared as an external parameter of this SequencePulseTemplate Args: subtemplates (List(Subtemplate)): The list of subtemplates of this SequencePulseTemplate as tuples of the form (PulseTemplate, Dict(str -> str)). external_parameters (List(str)): A set of names for external parameters of this SequencePulseTemplate. identifier (str): A unique identifier for use in serialization. (optional) Raises: MissingMappingException, if a parameter of a subtemplate is not mapped to the external parameters of this SequencePulseTemplate. MissingParameterDeclarationException, if a parameter mapping requires a parameter that was not declared in the external parameters of this SequencePulseTemplate. """ PulseTemplate.__init__(self, identifier=identifier) ParameterConstrainer.__init__(self, parameter_constraints=parameter_constraints) MeasurementDefiner.__init__(self, measurements=measurements) self.__subtemplates = [MappingPulseTemplate.from_tuple(st) if isinstance(st, tuple) else st for st in subtemplates] # check that all subtemplates live on the same channels defined_channels = self.__subtemplates[0].defined_channels for subtemplate in self.__subtemplates[1:]: if subtemplate.defined_channels != defined_channels: raise ValueError('The subtemplates are defined for different channels') if external_parameters: external_parameters = set(external_parameters) remaining = external_parameters.copy() for subtemplate in self.__subtemplates: missing = subtemplate.parameter_names - external_parameters if missing: raise MissingParameterDeclarationException(subtemplate, missing.pop()) remaining -= subtemplate.parameter_names if not external_parameters >= self.constrained_parameters: raise MissingParameterDeclarationException(self, (self.constrained_parameters-external_parameters).pop()) remaining -= self.constrained_parameters if remaining: raise MissingMappingException(self, remaining.pop())
def __init__(self, *args, to_test_constructor=None, **kwargs): super().__init__(*args, **kwargs) if to_test_constructor is None: self.to_test_constructor = lambda parameter_constraints=None:\ ParameterConstrainer(parameter_constraints=parameter_constraints) else: self.to_test_constructor = to_test_constructor
def __init__(self, time_point_tuple_list: List[EntryInInit], channel_names: Sequence[ChannelID], *, parameter_constraints: Optional[List[Union[ str, ParameterConstraint]]] = None, measurements: Optional[List[MeasurementDeclaration]] = None, identifier=None): AtomicPulseTemplate.__init__(self, identifier=identifier, measurements=measurements) ParameterConstrainer.__init__( self, parameter_constraints=parameter_constraints) self._channels = tuple(channel_names) self._entries = [ PointPulseEntry(*tpt) for tpt in time_point_tuple_list ]
def __init__( self, body: PulseTemplate, loop_index: str, loop_range: Union[int, range, str, Tuple[Any, Any], Tuple[Any, Any, Any], ParametrizedRange], identifier: Optional[str] = None, *, measurements: Optional[Sequence[MeasurementDeclaration]] = None, parameter_constraints: Optional[Sequence] = None): """ Args: body: The loop body. It is expected to have `loop_index` as an parameter loop_index: Loop index of the for loop loop_range: Range to loop through identifier: Used for serialization """ LoopPulseTemplate.__init__(self, body=body, identifier=identifier) MeasurementDefiner.__init__(self, measurements=measurements) ParameterConstrainer.__init__( self, parameter_constraints=parameter_constraints) if isinstance(loop_range, ParametrizedRange): self._loop_range = loop_range elif isinstance(loop_range, (int, str)): self._loop_range = ParametrizedRange(loop_range) elif isinstance(loop_range, (tuple, list)): self._loop_range = ParametrizedRange(*loop_range) elif isinstance(loop_range, range): self._loop_range = ParametrizedRange(start=loop_range.start, stop=loop_range.stop, step=loop_range.step) else: raise ValueError('loop_range is not valid') if not loop_index.isidentifier(): raise InvalidParameterNameException(loop_index) body_parameters = self.body.parameter_names if loop_index not in body_parameters: raise LoopIndexNotUsedException(loop_index, body_parameters) self._loop_index = loop_index
def __init__( self, expression: Union[str, ExpressionScalar], duration_expression: Union[str, ExpressionScalar], channel: ChannelID = 'default', identifier: Optional[str] = None, *, measurements: Optional[List[MeasurementDeclaration]] = None, parameter_constraints: Optional[List[Union[ str, ParameterConstraint]]] = None ) -> None: """ Args: 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: A mathematical expression which reliably computes the duration of an instantiation of this FunctionPulseTemplate from provided parameter values. channel: The channel this pulse template is defined on. identifier: A unique identifier for use in serialization. measurements: A list of measurement declarations forwarded to the :class:`~qctoolkit.pulses.measurement.MeasurementDefiner` superclass parameter_constraints: A list of parameter constraints forwarded to the :class:`~`qctoolkit.pulses.measurement.ParameterConstrainer superclass """ AtomicPulseTemplate.__init__(self, identifier=identifier, measurements=measurements) ParameterConstrainer.__init__( self, parameter_constraints=parameter_constraints) self.__expression = ExpressionScalar.make(expression) self.__duration_expression = ExpressionScalar.make(duration_expression) self.__parameter_names = { *self.__duration_expression.variables, *self.__expression.variables } - {'t'} self.__channel = channel
def __init__( self, body: PulseTemplate, repetition_count: Union[int, str, ExpressionScalar], identifier: Optional[str] = None, *args, parameter_constraints: Optional[List] = None, measurements: Optional[List[MeasurementDeclaration]] = None ) -> None: """Create a new RepetitionPulseTemplate instance. Args: body (PulseTemplate): The PulseTemplate which will be repeated. repetition_count (int or ParameterDeclaration): The number of repetitions either as a constant integer value or as a parameter declaration. identifier (str): A unique identifier for use in serialization. (optional) """ if len(args) == 1 and parameter_constraints is None: warn( 'You used parameter_constraints as a positional argument. It will be keyword only in a future version.', DeprecationWarning) elif args: TypeError( 'RepetitionPulseTemplate expects 3 positional arguments, got ' + str(3 + len(args))) LoopPulseTemplate.__init__(self, identifier=identifier, body=body) ParameterConstrainer.__init__( self, parameter_constraints=parameter_constraints) MeasurementDefiner.__init__(self, measurements=measurements) repetition_count = ExpressionScalar.make(repetition_count) if (repetition_count < 0) is True: raise ValueError('Repetition count may not be negative') self._repetition_count = repetition_count
def __init__(self, entries: Dict[ChannelID, Sequence[EntryInInit]], identifier: Optional[str]=None, *, parameter_constraints: Optional[List[Union[str, ParameterConstraint]]]=None, measurements: Optional[List[MeasurementDeclaration]]=None, consistency_check=True): """ Construct a `TablePulseTemplate` from a dict which maps channels to their entries. By default the consistency of the provided entries is checked. There are two static functions for convenience construction: from_array and from_entry_list. Args: entries: A dictionary that maps channel ids to a list of entries. An entry is a (time, voltage[, interpolation strategy]) tuple or a TableEntry identifier: Used for serialization parameter_constraints: Constraint list that is forwarded to the ParameterConstrainer superclass measurements: Measurement declaration list that is forwarded to the MeasurementDefiner superclass consistency_check: If True the consistency of the times will be checked on construction as far as possible """ AtomicPulseTemplate.__init__(self, identifier=identifier, measurements=measurements) ParameterConstrainer.__init__(self, parameter_constraints=parameter_constraints) self._entries = dict((ch, list()) for ch in entries.keys()) for channel, channel_entries in entries.items(): if len(channel_entries) == 0: raise ValueError('Channel {} is empty'.format(channel)) for entry in channel_entries: self._add_entry(channel, TableEntry(*entry)) self._duration = self.calculate_duration() self._table_parameters = set( var for channel_entries in self.entries.values() for entry in channel_entries for var in itertools.chain(entry.t.variables, entry.v.variables) ) | self.constrained_parameters if self.duration == 0: warnings.warn('Table pulse template with duration 0 on construction.', category=ZeroDurationTablePulseTemplate) if consistency_check: # perform a simple consistency check. All inequalities with more than one free variable are ignored as the # sympy solver does not support them # collect all conditions inequalities = [eq.sympified_expression for eq in self._parameter_constraints] +\ [sympy.Le(previous_entry.t.underlying_expression, entry.t.underlying_expression) for channel_entries in self._entries.values() for previous_entry, entry in zip(channel_entries, channel_entries[1:])] # test if any condition is already dissatisfied if any(isinstance(eq, sympy.boolalg.BooleanAtom) and bool(eq) is False for eq in inequalities): raise ValueError('Table pulse template has impossible parametrization') # filter conditions that are inequalities with one free variable and test if the solution set is empty inequalities = [eq for eq in inequalities if isinstance(eq, sympy.Rel) and len(eq.free_symbols) == 1] if not sympy.reduce_inequalities(inequalities): raise ValueError('Table pulse template has impossible parametrization')
def __init__(self, template: PulseTemplate, *, identifier: Optional[str] = None, parameter_mapping: Optional[Dict[str, str]] = None, measurement_mapping: Optional[Dict[str, str]] = None, channel_mapping: Optional[Dict[ChannelID, ChannelID]] = None, parameter_constraints: Optional[List[str]] = None, allow_partial_parameter_mapping=False): """Standard constructor for the MappingPulseTemplate. Mappings that are not specified are defaulted to identity mappings. Channels and measurement names of the encapsulated template can be mapped partially by default. F.i. if channel_mapping only contains one of two channels the other channel name is mapped to itself. However, if a parameter mapping is specified and one or more parameters are not mapped a MissingMappingException is raised. To allow partial mappings and enable the same behaviour as for the channel and measurement name mapping allow_partial_parameter_mapping must be set to True. Furthermore parameter constrains can be specified. :param template: The encapsulated pulse template whose parameters, measurement names and channels are mapped :param parameter_mapping: if not none, mappings for all parameters must be specified :param measurement_mapping: mappings for other measurement names are inserted :param channel_mapping: mappings for other channels are auto inserted :param parameter_constraints: :param allow_partial_parameter_mapping: """ PulseTemplate.__init__(self, identifier=identifier) ParameterConstrainer.__init__( self, parameter_constraints=parameter_constraints) if parameter_mapping is None: parameter_mapping = dict( (par, par) for par in template.parameter_names) else: mapped_internal_parameters = set(parameter_mapping.keys()) internal_parameters = template.parameter_names missing_parameter_mappings = internal_parameters - mapped_internal_parameters if mapped_internal_parameters - internal_parameters: raise UnnecessaryMappingException( template, mapped_internal_parameters - internal_parameters) elif missing_parameter_mappings: if allow_partial_parameter_mapping: parameter_mapping.update( {p: p for p in missing_parameter_mappings}) else: raise MissingMappingException( template, internal_parameters - mapped_internal_parameters) parameter_mapping = dict( (k, Expression(v)) for k, v in parameter_mapping.items()) measurement_mapping = dict( ) if measurement_mapping is None else measurement_mapping internal_names = template.measurement_names mapped_internal_names = set(measurement_mapping.keys()) if mapped_internal_names - internal_names: raise UnnecessaryMappingException( template, mapped_internal_names - internal_names) missing_name_mappings = internal_names - mapped_internal_names measurement_mapping = dict( itertools.chain(((name, name) for name in missing_name_mappings), measurement_mapping.items())) channel_mapping = dict( ) if channel_mapping is None else channel_mapping internal_channels = template.defined_channels mapped_internal_channels = set(channel_mapping.keys()) if mapped_internal_channels - internal_channels: raise UnnecessaryMappingException( template, mapped_internal_channels - internal_channels) missing_channel_mappings = internal_channels - mapped_internal_channels channel_mapping = dict( itertools.chain( ((name, name) for name in missing_channel_mappings), channel_mapping.items())) if isinstance(template, MappingPulseTemplate) and template.identifier is None: # avoid nested mappings parameter_mapping = { p: expr.evaluate_symbolic(parameter_mapping) for p, expr in template.parameter_mapping.items() } measurement_mapping = { k: measurement_mapping[v] for k, v in template.measurement_mapping.items() } channel_mapping = { k: channel_mapping[v] for k, v in template.channel_mapping.items() } template = template.template self.__template = template self.__parameter_mapping = parameter_mapping self.__external_parameters = set( itertools.chain(*(expr.variables for expr in self.__parameter_mapping.values()))) self.__external_parameters |= self.constrained_parameters self.__measurement_mapping = measurement_mapping self.__channel_mapping = channel_mapping
def __init__( self, *subtemplates: Union[AtomicPulseTemplate, MappingTuple, MappingPulseTemplate], external_parameters: Optional[Set[str]] = None, identifier: Optional[str] = None, parameter_constraints: Optional[List] = None, measurements: Optional[List[MeasurementDeclaration]] = None ) -> None: AtomicPulseTemplate.__init__(self, identifier=identifier, measurements=measurements) ParameterConstrainer.__init__( self, parameter_constraints=parameter_constraints) self._subtemplates = [ st if isinstance(st, PulseTemplate) else MappingPulseTemplate.from_tuple(st) for st in subtemplates ] for subtemplate in self._subtemplates: if isinstance(subtemplate, AtomicPulseTemplate): continue elif isinstance(subtemplate, MappingPulseTemplate): if isinstance(subtemplate.template, AtomicPulseTemplate): continue else: raise TypeError( 'Non atomic subtemplate of MappingPulseTemplate: {}'. format(subtemplate.template)) else: raise TypeError( 'Non atomic subtemplate: {}'.format(subtemplate)) if not self._subtemplates: raise ValueError('Cannot create empty MultiChannelPulseTemplate') defined_channels = [st.defined_channels for st in self._subtemplates] # check there are no intersections between channels for i, channels_i in enumerate(defined_channels): for j, channels_j in enumerate(defined_channels[i + 1:]): if channels_i & channels_j: raise ChannelMappingException( 'subtemplate {}'.format(i + 1), 'subtemplate {}'.format(i + 2 + j), (channels_i | channels_j).pop()) if external_parameters is not None: remaining = external_parameters.copy() for subtemplate in self._subtemplates: missing = subtemplate.parameter_names - external_parameters if missing: raise MissingParameterDeclarationException( subtemplate, missing.pop()) remaining -= subtemplate.parameter_names missing = self.constrained_parameters - external_parameters if missing: raise MissingParameterDeclarationException(self, missing.pop()) remaining -= self.constrained_parameters if remaining: raise MissingMappingException(self, remaining.pop()) duration = self._subtemplates[0].duration for subtemplate in self._subtemplates[1:]: if (duration == subtemplate.duration) is True: continue else: raise ValueError( 'Could not assert duration equality of {} and {}'.format( duration, subtemplate.duration))