def _push_left_append(this: Schedule, other: ScheduleComponent) -> Schedule: """Return ``this`` with ``other`` inserted at the maximum time over all channels shared between ```this`` and ``other``. Args: this: Input schedule to which ``other`` will be inserted. other: Other schedule to insert. Returns: Push left appended schedule. """ this_channels = set(this.channels) other_channels = set(other.channels) shared_channels = list(this_channels & other_channels) ch_slacks = [ this.stop_time - this.ch_stop_time(channel) + other.ch_start_time(channel) for channel in shared_channels ] if ch_slacks: slack_chan = shared_channels[np.argmin(ch_slacks)] shared_insert_time = this.ch_stop_time( slack_chan) - other.ch_start_time(slack_chan) else: shared_insert_time = 0 # Handle case where channels not common to both might actually start # after ``this`` has finished. other_only_insert_time = other.ch_start_time(*(other_channels - this_channels)) # Choose whichever is greatest. insert_time = max(shared_insert_time, other_only_insert_time) return this.insert(insert_time, other, inplace=True)
def _push_right_prepend(this: ScheduleComponent, other: ScheduleComponent) -> Schedule: """Return ``this`` with ``other`` inserted at the latest possible time such that ``other`` ends before it overlaps with any of ``this``. If required ``this`` is shifted to start late enough so that there is room to insert ``other``. Args: this: Input schedule to which ``other`` will be inserted. other: Other schedule to insert. Returns: Push right prepended schedule. """ this_channels = set(this.channels) other_channels = set(other.channels) shared_channels = list(this_channels & other_channels) ch_slacks = [ this.ch_start_time(channel) - other.ch_stop_time(channel) for channel in shared_channels ] if ch_slacks: insert_time = min(ch_slacks) + other.start_time else: insert_time = this.stop_time - other.stop_time + other.start_time if insert_time < 0: this.shift(-insert_time, inplace=True) this.insert(0, other, inplace=True) else: this.insert(insert_time, other, inplace=True) return this
def draw( self, schedule: ScheduleComponent, dt: float, interp_method: Callable, plot_range: Tuple[float, float], scale: float = None, channel_scales: Dict[Channel, float] = None, plot_all: bool = True, table: bool = False, label: bool = False, framechange: bool = True, channels: List[Channel] = None, show_framechange_channels: bool = True, draw_title: bool = False, ): """Draw figure. Args: schedule: schedule object to plot. dt: Time interval of samples. Pulses are visualized in the unit of cycle time if not provided. interp_method: Interpolation function. See example. Interpolation is disabled in default. See `qiskit.visualization.pulse.interpolation` for more information. plot_range: A tuple of time range to plot. scale: Scaling of waveform amplitude. Pulses are automatically scaled channel by channel if not provided. channel_scales: Dictionary of scale factor for specific channels. Scale of channels not specified here is overwritten by `scale`. plot_all: When set `True` plot empty channels. table: When set `True` draw event table for supported commands. label: When set `True` draw label for individual instructions. framechange: When set `True` draw framechange indicators. channels: A list of channel names to plot. All non-empty channels are shown if not provided. show_framechange_channels: When set `True` plot channels with only framechange instructions. draw_title: Add a title to the plot when set to ``True``. Returns: matplotlib.figure.Figure: A matplotlib figure object for the pulse envelope. Raises: VisualizationError: When schedule cannot be drawn """ figure = self.plt_mod.figure(dpi=self.style.dpi, figsize=self.style.figsize) if channels is None: channels = [] interp_method = interp_method or step_wise if channel_scales is None: channel_scales = {} # setup plot range if plot_range: t0 = int(np.floor(plot_range[0])) tf = int(np.floor(plot_range[1])) else: t0 = 0 # when input schedule is empty or comprises only frame changes, # we need to overwrite pulse duration by an integer greater than zero, # otherwise waveform returns empty array and matplotlib will be crashed. if channels: tf = schedule.ch_duration(*channels) else: tf = schedule.stop_time tf = tf or 1 # prepare waveform channels (schedule_channels, output_channels, snapshot_channels) = self._build_channels(schedule, channels, t0, tf, show_framechange_channels) # count numbers of valid waveform scale_dict = self._scale_channels( output_channels, scale=scale, channel_scales=channel_scales, channels=channels, plot_all=plot_all, ) if table: tb, ax = self._draw_table(figure, schedule_channels, dt) else: tb = None ax = figure.add_subplot(111) ax.set_facecolor(self.style.bg_color) y0 = self._draw_channels( ax, output_channels, interp_method, t0, tf, scale_dict, label=label, framechange=framechange, ) y_ub = 0.5 + self.style.vertical_span y_lb = y0 + 0.5 - self.style.vertical_span self._draw_snapshots(ax, snapshot_channels, y_lb) ax.set_xlim(t0, tf) tick_labels = np.linspace(t0, tf, 5) ax.set_xticks(tick_labels) ax.set_xticklabels( [self.style.axis_formatter % label for label in tick_labels * dt], fontsize=self.style.axis_font_size, ) ax.set_ylim(y_lb, y_ub) ax.set_yticklabels([]) if tb is not None: bbox = tb.get_position() else: bbox = ax.get_position() # This check is here for backwards compatibility. Before, the check was around # the suptitle line, however since the font style can take on a type of None # we need to unfortunately check both the type and the value of the object. if isinstance(self.style.title_font_size, int) and self.style.title_font_size > 0: if draw_title: figure.suptitle( schedule.name, fontsize=self.style.title_font_size, y=bbox.y1 + 0.02, va="bottom", ) return figure