def __init__(self, lhs: Union[PulseTemplate, ExpressionLike, Mapping[ChannelID, ExpressionLike]], arithmetic_operator: str, rhs: Union[PulseTemplate, ExpressionLike, Mapping[ChannelID, ExpressionLike]], *, identifier: Optional[str] = None): """ Args: lhs: Left hand side operand arithmetic_operator: String representation of the operator rhs: Right hand side operand identifier: Raises: TypeError if both or none of the operands are pulse templates """ PulseTemplate.__init__(self, identifier=identifier) if not isinstance(lhs, PulseTemplate) and not isinstance( rhs, PulseTemplate): raise TypeError( 'At least one of the operands needs to be a pulse template.') elif not isinstance(lhs, PulseTemplate) and isinstance( rhs, PulseTemplate): # +, - and * with (scalar, PT) if arithmetic_operator not in ('+', '-', '*'): raise ValueError( 'Operands (scalar, PulseTemplate) require an operator from {+, -, *}' ) scalar = lhs = self._parse_operand(lhs, rhs.defined_channels) pulse_template = rhs elif isinstance(lhs, PulseTemplate) and not isinstance(rhs, PulseTemplate): # +, -, *, / and // with (PT, scalar) if arithmetic_operator not in ('+', '-', '*', '/'): raise ValueError( 'Operands (PulseTemplate, scalar) require an operator from {+, -, *, /}' ) scalar = rhs = self._parse_operand(rhs, lhs.defined_channels) pulse_template = lhs else: # + and - with (AtomicPulseTemplate, AtomicPulseTemplate) as operands raise TypeError( 'ArithmeticPulseTemplate cannot combine two PulseTemplates') self._lhs = lhs self._rhs = rhs self._pulse_template = pulse_template self._scalar = scalar self._arithmetic_operator = arithmetic_operator
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, registry: PulseRegistryType = 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. Deprecated. identifier (str): A unique identifier for use in serialization. (optional) """ 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: warnings.warn( "external_parameters is an obsolete argument and will be removed in the future.", category=DeprecationWarning) self._register(registry=registry)
def plot(pulse: PulseTemplate, parameters: Dict[str, Parameter] = None, sample_rate: Real = 10, axes: Any = None, show: bool = True, plot_channels: Optional[Set[ChannelID]] = None, plot_measurements: Optional[Set[str]] = None, stepped: bool = True, maximum_points: int = 10**6, time_slice: Tuple[Real, Real] = None, **kwargs) -> Any: # pragma: no cover """Plots a pulse using matplotlib. The given pulse template will first be turned into a pulse program (represented by a Loop object) with the provided parameters. The render() function is then invoked to obtain voltage samples over the entire duration of the pulse which are then plotted in a matplotlib figure. Args: pulse: The pulse to be plotted. parameters: An optional mapping of parameter names to Parameter objects. sample_rate: The rate with which the waveforms are sampled for the plot in samples per time unit. (default = 10) axes: matplotlib Axes object the pulse will be drawn into if provided show: If true, the figure will be shown plot_channels: If specified only channels from this set will be plotted. If omitted all channels will be. stepped: If true pyplot.step is used for plotting plot_measurements: If specified measurements in this set will be plotted. If omitted no measurements will be. maximum_points: If the sampled waveform is bigger, it is not plotted time_slice: The time slice to be plotted. If None, the entire pulse will be shown. kwargs: Forwarded to pyplot. Overwrites other settings. Returns: matplotlib.pyplot.Figure instance in which the pulse is rendered Raises: PlottingNotPossibleException if the sequencing is interrupted before it finishes, e.g., because a parameter value could not be evaluated all Exceptions possibly raised during sequencing """ from matplotlib import pyplot as plt channels = pulse.defined_channels if parameters is None: parameters = dict() program = pulse.create_program( parameters=parameters, channel_mapping={ch: ch for ch in channels}, measurement_mapping={w: w for w in pulse.measurement_names}) if program is not None: times, voltages, measurements = render( program, sample_rate, render_measurements=bool(plot_measurements), time_slice=time_slice) else: times, voltages, measurements = np.array([]), dict(), [] duration = 0 if times.size == 0: warnings.warn("Pulse to be plotted is empty!") elif times.size > maximum_points: # todo [2018-05-30]: since it results in an empty return value this should arguably be an exception, not just a warning warnings.warn( "Sampled pulse of size {wf_len} is lager than {max_points}".format( wf_len=times.size, max_points=maximum_points)) return None else: duration = times[-1] if time_slice is None: time_slice = (0, duration) legend_handles = [] if axes is None: # plot to figure figure = plt.figure() axes = figure.add_subplot(111) if plot_channels is not None: voltages = { ch: voltage for ch, voltage in voltages.items() if ch in plot_channels } for ch_name, voltage in voltages.items(): label = 'channel {}'.format(ch_name) if stepped: line, = axes.step(times, voltage, **{ **dict(where='post', label=label), **kwargs }) else: line, = axes.plot(times, voltage, **{ **dict(label=label), **kwargs }) legend_handles.append(line) if plot_measurements: measurement_dict = dict() for name, begin, length in measurements: if name in plot_measurements: measurement_dict.setdefault(name, []).append( (begin, begin + length)) color_map = plt.cm.get_cmap('plasma') meas_colors = { name: color_map(i / len(measurement_dict)) for i, name in enumerate(measurement_dict.keys()) } for name, begin_end_list in measurement_dict.items(): for begin, end in begin_end_list: poly = axes.axvspan(begin, end, alpha=0.2, label=name, edgecolor='black', facecolor=meas_colors[name]) legend_handles.append(poly) axes.legend(handles=legend_handles) max_voltage = max( (max(channel, default=0) for channel in voltages.values()), default=0) min_voltage = min( (min(channel, default=0) for channel in voltages.values()), default=0) # add some margins in the presentation axes.set_xlim(-0.5 + time_slice[0], time_slice[1] + 0.5) voltage_difference = max_voltage - min_voltage if voltage_difference > 0: axes.set_ylim(min_voltage - 0.1 * voltage_difference, max_voltage + 0.1 * voltage_difference) axes.set_xlabel('Time (ns)') axes.set_ylabel('Voltage (a.u.)') if pulse.identifier: axes.set_title(pulse.identifier) if show: axes.get_figure().show() return axes.get_figure()
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: bool = None, registry: PulseRegistryType = None) -> None: """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. Channels that are mapped to None are dropped. 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. Mapping to None drops the channel. :param parameter_constraints: :param allow_partial_parameter_mapping: If None the value of the class variable ALLOW_PARTIAL_PARAMETER_MAPPING """ PulseTemplate.__init__(self, identifier=identifier) ParameterConstrainer.__init__( self, parameter_constraints=parameter_constraints) if allow_partial_parameter_mapping is None: allow_partial_parameter_mapping = self.ALLOW_PARTIAL_PARAMETER_MAPPING 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())) # we copy to modify in place channel_mapping = dict( ) if channel_mapping is None else channel_mapping.copy() 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) # fill up implicit mappings (unchanged channels) missing_channel_mappings = internal_channels - mapped_internal_channels for name in missing_channel_mappings: channel_mapping[name] = name # None is an allowed overlapping target as it marks dropped channels overlapping_targets = { channel for channel, n in collections.Counter( channel_mapping.values()).items() if n > 1 and channel is not None } if overlapping_targets: raise ValueError( 'Cannot map multiple channels to the same target(s) %r' % overlapping_targets, channel_mapping) if isinstance(template, MappingPulseTemplate) and template.identifier is None: # avoid nested mappings parameter_mapping = { p: Expression(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 = FrozenDict(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 self._register(registry=registry)
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: bool = False, registry: PulseRegistryType = None) -> None: """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: Expression(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 self._register(registry=registry)
def build_program_with_create_program(self, pulse_template: PulseTemplate, **kwargs): return pulse_template.create_program(**kwargs)