def __init__(self, *subtemplates: Union[AtomicPulseTemplate, MappingTuple, MappingPulseTemplate], identifier: Optional[str] = None, parameter_constraints: Optional[List] = None, measurements: Optional[List[MeasurementDeclaration]] = None, registry: PulseRegistryType = None, duration: Optional[ExpressionLike] = None) -> None: """Parallels multiple AtomicPulseTemplates of the same duration. If the duration keyword argument is given it is enforced that the instantiated pulse template has this duration. If duration is None the duration of the PT is the duration of the first subtemplate. There are probably changes to this behaviour in the future. Args: *subtemplates: Positional arguments are subtemplates to combine. identifier: Forwarded to AtomicPulseTemplate.__init__ parameter_constraints: Forwarded to ParameterConstrainer.__init__ measurements: Forwarded to AtomicPulseTemplate.__init__ duration: Enforced duration of the pulse template on instantiation. build_waveform checks all sub-waveforms have this duration. If True the equality of durations is only checked durtin instantiation not construction. """ 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] if duration in (True, False): warnings.warn("Boolean duration is deprecated since qupulse 0.6 and interpreted as None", category=DeprecationWarning, stacklevel=2) duration = None 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 duration is None: self._duration = None else: self._duration = ExpressionScalar(duration) self._register(registry=registry)
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: Optional[str]=None, registry: PulseRegistryType=None) -> 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] self._register(registry=registry)
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, registry: PulseRegistryType = None) -> None: """Creates a new FunctionPulseTemplate object. 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:`~qupulse.pulses.measurement.MeasurementDefiner` superclass parameter_constraints: A list of parameter constraints forwarded to the :class:`~qupulse.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 self._register(registry=registry)
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, registry: PulseRegistryType = None, duration: Union[str, Expression, bool] = False) -> None: """Parallels multiple AtomicPulseTemplates of the same duration. The duration equality check is performed on construction by default. If the duration keyword argument is given the check is performed on instantiation (when build_waveform is called). duration can be a Expression to enforce a certain duration or True for an unspecified duration. Args: *subtemplates: Positional arguments are subtemplates to combine. identifier: Forwarded to AtomicPulseTemplate.__init__ parameter_constraints: Forwarded to ParameterConstrainer.__init__ measurements: Forwarded to AtomicPulseTemplate.__init__ duration: Enforced duration of the pulse template on instantiation. build_waveform checks all sub-waveforms have this duration. If True the equality of durations is only checked durtin instantiation not construction. external_parameters: No functionality. (Deprecated) """ 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: warnings.warn( "external_parameters is an obsolete argument and will be removed in the future.", category=DeprecationWarning) if not duration: duration = self._subtemplates[0].duration for subtemplate in self._subtemplates[1:]: if almost_equal(duration.sympified_expression, subtemplate.duration.sympified_expression): continue else: raise ValueError( 'Could not assert duration equality of {} and {}'. format(duration, subtemplate.duration)) self._duration = None elif duration is True: self._duration = None else: self._duration = ExpressionScalar(duration) self._register(registry=registry)
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: bool = True, registry: PulseRegistryType = None) -> None: """ 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) if not entries: raise ValueError( "Cannot construct an empty TablePulseTemplate (no entries given). There is currently no " "specific reason for this. Please submit an issue if you need this 'feature'." ) 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, 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') self._register(registry=registry)