def _draw_opaque_waveform(
    init_time: int,
    duration: int,
    pulse_shape: str,
    pnames: List[str],
    meta: Dict[str, Any],
    channel: pulse.channels.PulseChannel,
    formatter: Dict[str, Any],
) -> List[Union[drawings.LineData, drawings.BoxData, drawings.TextData]]:
    """A private function that generates drawings of stepwise pulse lines.

    Args:
        init_time: Time when the opaque waveform starts.
        duration: Duration of opaque waveform. This can be None or ParameterExpression.
        pulse_shape: String that represents pulse shape.
        pnames: List of parameter names.
        meta: Metadata dictionary of the waveform.
        channel: Channel associated with the waveform to draw.
        formatter: Dictionary of stylesheet settings.

    Returns:
        List of drawings.
    """
    fill_objs = []

    fc, ec = formatter['color.opaque_shape']
    # setup style options
    box_style = {
        'zorder': formatter['layer.fill_waveform'],
        'alpha': formatter['alpha.opaque_shape'],
        'linewidth': formatter['line_width.opaque_shape'],
        'linestyle': formatter['line_style.opaque_shape'],
        'facecolor': fc,
        'edgecolor': ec
    }

    if duration is None or isinstance(duration, circuit.ParameterExpression):
        duration = formatter['box_width.opaque_shape']

    box_obj = drawings.BoxData(data_type=types.WaveformType.OPAQUE,
                               channels=channel,
                               xvals=[init_time, init_time + duration],
                               yvals=[
                                   -0.5 * formatter['box_height.opaque_shape'],
                                   0.5 * formatter['box_height.opaque_shape']
                               ],
                               meta=meta,
                               ignore_scaling=True,
                               styles=box_style)
    fill_objs.append(box_obj)

    # parameter name
    func_repr = '{func}({params})'.format(func=pulse_shape,
                                          params=', '.join(pnames))

    text_style = {
        'zorder': formatter['layer.annotate'],
        'color': formatter['color.annotate'],
        'size': formatter['text_size.annotate'],
        'va': 'bottom',
        'ha': 'center'
    }

    text_obj = drawings.TextData(
        data_type=types.LabelType.OPAQUE_BOXTEXT,
        channels=channel,
        xvals=[init_time + 0.5 * duration],
        yvals=[0.5 * formatter['box_height.opaque_shape']],
        text=func_repr,
        ignore_scaling=True,
        styles=text_style)

    fill_objs.append(text_obj)

    return fill_objs
def gen_waveform_max_value(
        data: types.PulseInstruction, formatter: Dict[str, Any],
        device: device_info.DrawerBackendInfo) -> List[drawings.TextData]:
    """Generate the annotation for the maximum waveform height for
    the real and the imaginary part of the waveform envelope.

    Maximum values smaller than the vertical resolution limit is ignored.

    Stylesheets:
        - The `annotate` style is applied.

    Args:
        data: Waveform instruction data to draw.
        formatter: Dictionary of stylesheet settings.
        device: Backend configuration.

    Returns:
        List of `TextData` drawings.
    """
    if data.is_opaque:
        return []

    style = {
        'zorder': formatter['layer.annotate'],
        'color': formatter['color.annotate'],
        'size': formatter['text_size.annotate'],
        'ha': 'center'
    }

    # only pulses.
    if isinstance(data.inst, instructions.Play):
        # pulse
        operand = data.inst.pulse
        if isinstance(operand, pulse.ParametricPulse):
            pulse_data = operand.get_waveform()
        else:
            pulse_data = operand
        xdata = np.arange(pulse_data.duration) + data.t0
        ydata = pulse_data.samples
    else:
        return []

    # phase modulation
    if formatter['control.apply_phase_modulation']:
        ydata = np.asarray(ydata, dtype=complex) * np.exp(
            1j * data.frame.phase)
    else:
        ydata = np.asarray(ydata, dtype=complex)

    texts = []

    # max of real part
    re_maxind = np.argmax(np.abs(ydata.real))
    if np.abs(ydata.real[re_maxind]) > 0.01:
        # generator shows only 2 digits after the decimal point.
        if ydata.real[re_maxind] > 0:
            max_val = u'{val:.2f}\n\u25BE'.format(val=ydata.real[re_maxind])
            re_style = {'va': 'bottom'}
        else:
            max_val = u'\u25B4\n{val:.2f}'.format(val=ydata.real[re_maxind])
            re_style = {'va': 'top'}
        re_style.update(style)
        re_text = drawings.TextData(data_type=types.LabelType.PULSE_INFO,
                                    channels=data.inst.channel,
                                    xvals=[xdata[re_maxind]],
                                    yvals=[ydata.real[re_maxind]],
                                    text=max_val,
                                    styles=re_style)
        texts.append(re_text)

    # max of imag part
    im_maxind = np.argmax(np.abs(ydata.imag))
    if np.abs(ydata.imag[im_maxind]) > 0.01:
        # generator shows only 2 digits after the decimal point.
        if ydata.imag[im_maxind] > 0:
            max_val = u'{val:.2f}\n\u25BE'.format(val=ydata.imag[im_maxind])
            im_style = {'va': 'bottom'}
        else:
            max_val = u'\u25B4\n{val:.2f}'.format(val=ydata.imag[im_maxind])
            im_style = {'va': 'top'}
        im_style.update(style)
        im_text = drawings.TextData(data_type=types.LabelType.PULSE_INFO,
                                    channels=data.inst.channel,
                                    xvals=[xdata[im_maxind]],
                                    yvals=[ydata.imag[im_maxind]],
                                    text=max_val,
                                    styles=im_style)
        texts.append(im_text)

    return texts
def gen_ibmq_latex_waveform_name(
        data: types.PulseInstruction, formatter: Dict[str, Any],
        device: device_info.DrawerBackendInfo) -> List[drawings.TextData]:
    r"""Generate the formatted instruction name associated with the waveform.

    Channel name and ID string are removed and the rotation angle is expressed in units of pi.
    The controlled rotation angle associated with the CR pulse name is divided by 2.

    Note that in many scientific articles the controlled rotation angle implies
    the actual rotation angle, but in IQX backend the rotation angle represents
    the difference between rotation angles with different control qubit states.

    For example:
        - 'X90p_d0_abcdefg' is converted into 'X(\frac{\pi}{2})'
        - 'CR90p_u0_abcdefg` is converted into 'CR(\frac{\pi}{4})'

    Stylesheets:
        - The `annotate` style is applied.

    Notes:
        This generator can convert pulse names used in the IQX backends.
        If pulses are provided by the third party providers or the user defined,
        the generator output may be the as-is pulse name.

    Args:
        data: Waveform instruction data to draw.
        formatter: Dictionary of stylesheet settings.
        device: Backend configuration.

    Returns:
        List of `TextData` drawings.
    """
    if data.is_opaque:
        return []

    style = {
        'zorder': formatter['layer.annotate'],
        'color': formatter['color.annotate'],
        'size': formatter['text_size.annotate'],
        'va': 'center',
        'ha': 'center'
    }

    if isinstance(data.inst, pulse.instructions.Acquire):
        systematic_name = 'Acquire'
        latex_name = None
    elif isinstance(data.inst, instructions.Delay):
        systematic_name = data.inst.name or 'Delay'
        latex_name = None
    elif isinstance(data.inst.channel, pulse.channels.MeasureChannel):
        systematic_name = 'Measure'
        latex_name = None
    else:
        systematic_name = data.inst.pulse.name or data.inst.pulse.__class__.__name__

        template = r'(?P<op>[A-Z]+)(?P<angle>[0-9]+)?(?P<sign>[pm])_(?P<ch>[dum])[0-9]+'
        match_result = re.match(template, systematic_name)
        if match_result is not None:
            match_dict = match_result.groupdict()
            sign = '' if match_dict['sign'] == 'p' else '-'
            if match_dict['op'] == 'CR':
                # cross resonance
                if match_dict['ch'] == 'u':
                    op_name = r'{\rm CR}'
                else:
                    op_name = r'\overline{\rm CR}'
                # IQX name def is not standard. Echo CR is annotated with pi/4 rather than pi/2
                angle_val = match_dict['angle']
                frac = Fraction(int(int(angle_val) / 2), 180)
                if frac.numerator == 1:
                    angle = r'\pi/{denom:d}'.format(denom=frac.denominator)
                else:
                    angle = r'{num:d}/{denom:d} \pi'.format(
                        num=frac.numerator, denom=frac.denominator)
            else:
                # single qubit pulse
                op_name = r'{{\rm {}}}'.format(match_dict['op'])
                angle_val = match_dict['angle']
                if angle_val is None:
                    angle = r'\pi'
                else:
                    frac = Fraction(int(angle_val), 180)
                    if frac.numerator == 1:
                        angle = r'\pi/{denom:d}'.format(denom=frac.denominator)
                    else:
                        angle = r'{num:d}/{denom:d} \pi'.format(
                            num=frac.numerator, denom=frac.denominator)
            latex_name = r'{}({}{})'.format(op_name, sign, angle)
        else:
            latex_name = None

    text = drawings.TextData(data_type=types.LabelType.PULSE_NAME,
                             channels=data.inst.channel,
                             xvals=[data.t0 + 0.5 * data.inst.duration],
                             yvals=[-formatter['label_offset.pulse_name']],
                             text=systematic_name,
                             latex=latex_name,
                             ignore_scaling=True,
                             styles=style)

    return [text]
Esempio n. 4
0
def gen_frame_symbol(
        data: types.PulseInstruction, formatter: Dict[str, Any],
        device: device_info.DrawerBackendInfo) -> List[drawings.TextData]:
    """Generate a frame change symbol with instruction meta data from provided frame instruction.

    Stylesheets:
        - The `frame_change` style is applied.
        - The symbol type in unicode is specified in `formatter.unicode_symbol.frame_change`.
        - The symbol type in latex is specified in `formatter.latex_symbol.frame_change`.

    Args:
        data: Frame change instruction data to draw.
        formatter: Dictionary of stylesheet settings.
        device: Backend configuration.

    Returns:
        List of `TextData` drawings.
    """
    if data.frame.phase == 0 and data.frame.freq == 0:
        return []

    style = {
        'zorder': formatter['layer.frame_change'],
        'color': formatter['color.frame_change'],
        'size': formatter['text_size.frame_change'],
        'va': 'center',
        'ha': 'center'
    }

    program = []
    for inst in data.inst:
        if isinstance(
                inst,
            (instructions.SetFrequency, instructions.ShiftFrequency)):
            try:
                program.append('{}({:.2e} Hz)'.format(inst.__class__.__name__,
                                                      inst.frequency))
            except TypeError:
                # parameter expression
                program.append('{}({})'.format(inst.__class__.__name__,
                                               inst.frequency.name))
        elif isinstance(inst,
                        (instructions.SetPhase, instructions.ShiftPhase)):
            try:
                program.append('{}({:.2f} rad.)'.format(
                    inst.__class__.__name__, inst.phase))
            except TypeError:
                # parameter expression
                program.append('{}({})'.format(inst.__class__.__name__,
                                               inst.phase.name))

    meta = {
        'total phase change': data.frame.phase,
        'total frequency change': data.frame.freq,
        'program': ', '.join(program),
        't0 (cycle time)': data.t0,
        't0 (sec)': data.t0 * data.dt if data.dt else 'N/A'
    }

    text = drawings.TextData(data_type=types.SymbolType.FRAME,
                             channels=data.inst[0].channel,
                             xvals=[data.t0],
                             yvals=[0],
                             text=formatter['unicode_symbol.frame_change'],
                             latex=formatter['latex_symbol.frame_change'],
                             ignore_scaling=True,
                             meta=meta,
                             styles=style)

    return [text]
Esempio n. 5
0
def gen_formatted_frame_values(
        data: types.PulseInstruction, formatter: Dict[str, Any],
        device: device_info.DrawerBackendInfo) -> List[drawings.TextData]:
    """Generate the formatted virtual Z rotation label and the frequency change label
    from provided frame instruction.

    Phase value is placed on top of the symbol, and frequency value is placed below the symbol.
    See :py:func:`gen_formatted_phase` and :py:func:`gen_formatted_freq_mhz` for details.

    Stylesheets:
        - The `frame_change` style is applied.
        - The `annotate` style is applied for font size.

    Args:
        data: Frame change instruction data to draw.
        formatter: Dictionary of stylesheet settings.
        device: Backend configuration.

    Returns:
        List of `TextData` drawings.
    """
    texts = []

    _max_denom = 10
    _unit = 'MHz'

    style = {
        'zorder': formatter['layer.frame_change'],
        'color': formatter['color.frame_change'],
        'size': formatter['text_size.annotate'],
        'ha': 'center'
    }

    # phase value
    if data.frame.phase != 0:
        plain_phase, latex_phase = _phase_to_text(formatter=formatter,
                                                  phase=data.frame.phase,
                                                  max_denom=_max_denom,
                                                  flip=True)
        phase_style = {'va': 'center'}
        phase_style.update(style)

        phase = drawings.TextData(
            data_type=types.LabelType.FRAME,
            channels=data.inst[0].channel,
            xvals=[data.t0],
            yvals=[formatter['label_offset.frame_change']],
            text='VZ({phase})'.format(phase=plain_phase),
            latex=r'{{\rm VZ}}({phase})'.format(phase=latex_phase),
            ignore_scaling=True,
            styles=phase_style)
        texts.append(phase)

    # frequency value
    if data.frame.freq != 0:
        plain_freq, latex_freq = _freq_to_text(formatter=formatter,
                                               freq=data.frame.freq,
                                               unit=_unit)
        freq_style = {'va': 'center'}
        freq_style.update(style)

        freq = drawings.TextData(
            data_type=types.LabelType.FRAME,
            channels=data.inst[0].channel,
            xvals=[data.t0],
            yvals=[2 * formatter['label_offset.frame_change']],
            text=u'\u0394f = {freq}'.format(freq=plain_freq),
            latex=r'\Delta f = {freq}'.format(freq=latex_freq),
            ignore_scaling=True,
            styles=freq_style)
        texts.append(freq)

    return texts
Esempio n. 6
0
def gen_frame_symbol(
        data: types.PulseInstruction, formatter: Dict[str, Any],
        device: device_info.DrawerBackendInfo) -> List[drawings.TextData]:
    """Generate a frame change symbol with instruction meta data from provided frame instruction.

    Stylesheets:
        - The `frame_change` style is applied.
        - The symbol type in unicode is specified in `formatter.unicode_symbol.frame_change`.
        - The symbol type in latex is specified in `formatter.latex_symbol.frame_change`.

    Args:
        data: Frame change instruction data to draw.
        formatter: Dictionary of stylesheet settings.
        device: Backend configuration.

    Returns:
        List of `TextData` drawings.
    """
    if data.frame.phase == 0 and data.frame.freq == 0:
        return []

    style = {
        "zorder": formatter["layer.frame_change"],
        "color": formatter["color.frame_change"],
        "size": formatter["text_size.frame_change"],
        "va": "center",
        "ha": "center",
    }

    program = []
    for inst in data.inst:
        if isinstance(
                inst,
            (instructions.SetFrequency, instructions.ShiftFrequency)):
            try:
                program.append(
                    f"{inst.__class__.__name__}({inst.frequency:.2e} Hz)")
            except TypeError:
                # parameter expression
                program.append(f"{inst.__class__.__name__}({inst.frequency})")
        elif isinstance(inst,
                        (instructions.SetPhase, instructions.ShiftPhase)):
            try:
                program.append(
                    f"{inst.__class__.__name__}({inst.phase:.2f} rad.)")
            except TypeError:
                # parameter expression
                program.append(f"{inst.__class__.__name__}({inst.phase})")

    meta = {
        "total phase change": data.frame.phase,
        "total frequency change": data.frame.freq,
        "program": ", ".join(program),
        "t0 (cycle time)": data.t0,
        "t0 (sec)": data.t0 * data.dt if data.dt else "N/A",
    }

    text = drawings.TextData(
        data_type=types.SymbolType.FRAME,
        channels=data.inst[0].channel,
        xvals=[data.t0],
        yvals=[0],
        text=formatter["unicode_symbol.frame_change"],
        latex=formatter["latex_symbol.frame_change"],
        ignore_scaling=True,
        meta=meta,
        styles=style,
    )

    return [text]