Example #1
0
def _draw_line_between_qubits(drawing: Drawing,
                              bit_gate_rank: _types.BitRankType,
                              control_qubit: int, target_qubit: int,
                              bit_mapping: dict,
                              index_to_draw: int = None) -> None:
    """Draw a line between the two given qubits.

    :param drawing: Drawing that will be used to draw.
    :param bit_gate_rank: Structure used to keep track of the used and free
    places. See module docstring for more information.
    :param control_qubit: First qubit.
    :param target_qubit: Second qubit.
    :param bit_mapping: Unused at the moment.
    :param index_to_draw: If precised, force the line to be drawn at the given
    index. Else, the index is computed and the line drawn at the first free
    place possible.
    """
    if index_to_draw is None:
        index_to_draw, _ = _helpers.get_max_index(bit_gate_rank,
                                                  qubits=[control_qubit,
                                                          target_qubit])
    x_coord = _helpers.get_x_from_index(index_to_draw)
    y1_coord = _helpers.get_y_from_quantum_register(control_qubit, bit_mapping)
    y2_coord = _helpers.get_y_from_quantum_register(target_qubit, bit_mapping)
    drawing.add(drawing.line(start=(x_coord, y1_coord), end=(x_coord, y2_coord),
                             stroke=_constants.GATE_BORDER_COLOR,
                             stroke_width=_constants.STROKE_THICKNESS))
Example #2
0
def _draw_swap_gate(drawing: Drawing, bit_gate_rank: _types.BitRankType,
                    qubit1: int, qubit2: int, bit_mapping: dict) -> None:
    index_to_draw, _ = _helpers.get_max_index(bit_gate_rank,
                                              qubits=[qubit1, qubit2])
    x_coord = _helpers.get_x_from_index(index_to_draw)
    yq1_coord = _helpers.get_y_from_quantum_register(qubit1, bit_mapping)
    yq2_coord = _helpers.get_y_from_quantum_register(qubit2, bit_mapping)

    _draw_swap_cross(drawing, x_coord, yq1_coord)
    _draw_swap_cross(drawing, x_coord, yq2_coord)
    _draw_line_between_qubits(drawing, bit_gate_rank, qubit1, qubit2,
                              bit_mapping, index_to_draw)
Example #3
0
def _draw_unitary_gate(drawing: Drawing, bit_gate_rank: _types.BitRankType,
                       qubit: int, gate_name: str, bit_mapping: dict,
                       index_to_draw: int = None,
                       is_controlled_gate: bool = False) -> None:
    if index_to_draw is None:
        index_to_draw = bit_gate_rank['qubits'][qubit]
    x_coord = _helpers.get_x_from_index(index_to_draw)
    y_coord = _helpers.get_y_from_quantum_register(qubit, bit_mapping)

    # Draw the good gate shape
    if is_controlled_gate:
        _draw_gate_circle(drawing, x_coord, y_coord)
    else:
        _draw_gate_rect(drawing, x_coord, y_coord)

    desired_width = _constants.GATE_SIZE - 2 * _constants.GATE_INSIDE_MARGIN
    desired_height = _constants.GATE_SIZE - 2 * _constants.GATE_INSIDE_MARGIN
    font_size = _helpers.adapt_text_font_size(gate_name, desired_width,
                                              desired_height)
    if is_controlled_gate:
        font_size *= _constants.FONT_SIZE_REDUCTION_FACTOR_FOR_CONTROLLED_GATES

    vertical_multiplier = _constants.FONT_SIZE_CENTER_VERTICALLY_MULTIPLIER

    drawing.add(drawing.text(gate_name, insert=(
        x_coord, y_coord + vertical_multiplier * font_size),
                             text_anchor="middle", font_size=font_size))
Example #4
0
def _draw_measure_gate(drawing: Drawing, bit_gate_rank: _types.BitRankType,
                       measured_qubit: int, target_clbit: int,
                       show_clbits: bool, bit_mapping: dict) -> None:
    index_to_draw, _ = _helpers.get_max_index(bit_gate_rank,
                                              qubits=[measured_qubit],
                                              clbits=[target_clbit])

    x_coord = _helpers.get_x_from_index(index_to_draw)
    yq_coord = _helpers.get_y_from_quantum_register(measured_qubit, bit_mapping)
    if show_clbits:
        yc_coord = _helpers.get_y_from_classical_register(target_clbit, len(
            bit_gate_rank['qubits']), bit_mapping)
        # Draw the line between the 2 bits
        _draw_classical_double_line(drawing, x_coord, yq_coord, x_coord,
                                    yc_coord)

        # Draw the little thing that tells where we put the measure.
        anchor = tuple((x_coord - _constants.MEASURE_GATE_CLBIT_SIZE / 2,
                        yc_coord - _constants.MEASURE_GATE_CLBIT_SIZE / 2))
        sizes = tuple((_constants.MEASURE_GATE_CLBIT_SIZE,
                       _constants.MEASURE_GATE_CLBIT_SIZE))
        drawing.add(drawing.rect(insert=anchor, size=sizes,
                                 fill=_constants.MEASURE_GATE_CLBIT_FILL_COLOR,
                                 stroke=_constants.GATE_BORDER_COLOR,
                                 stroke_width=_constants.STROKE_THICKNESS))
        # Draw the "measure" gate.
        _draw_unitary_gate(drawing, bit_gate_rank, measured_qubit, "M",
                           bit_mapping, index_to_draw=index_to_draw)

    else:
        # Draw the "measure" gate.
        _draw_unitary_gate(drawing, bit_gate_rank, measured_qubit,
                           "M" + str(target_clbit), bit_mapping,
                           index_to_draw=index_to_draw)
Example #5
0
def _draw_classically_conditioned_part(drawing: Drawing,
                                       bit_gate_rank: _types.BitRankType,
                                       instruction, bit_mapping: dict) -> None:
    """Draw the line and the controls for classically controlled instructions.

    :param drawing: an instance of svgwrite.Drawing, used to write the SVG.
    :param bit_gate_rank: see module documentation for more information on this
    data structure.
    :param instruction: A QISKit instruction. The dict has a key 'conditional'
    associated to an other Python dict with entries:
        'type': the type of the instruction. For example 'equals'.
        'mask': the classical bits used (?)
        'val' : the value compared with 'type' comparator to the classical bit.
    :param bit_mapping:
    :raise NotImplementedError: if the given instruction affects more than 1
    qubit.
    """

    qubits = instruction['qubits']
    if len(qubits) > 1:
        raise NotImplementedError("Classically controlled multi-qubit "
                                  "instructions are not implemented for the "
                                  "moment.")
    total_qubits_number = len(bit_gate_rank['qubits'])
    total_clbits_number = len(bit_gate_rank['clbits'])

    # We take the binary little-endian representation of the value that
    # should be compared with the value stored in classical registers.
    # int(x, 0) let the 'int' function choose automatically the good basis.
    value = int(instruction['conditional']['val'], 0)
    mask = int(instruction['conditional']['mask'], 0)
    # The [2:] is to remove the "Ob" part returned by the "bin" function.
    # The [::-1] is to reverse the list order, to have a little-endian
    # representation.
    little_endian_bit_value = bin(value)[2:][::-1]
    number_of_clbits = len(bin(mask)[2:])

    assert number_of_clbits <= total_clbits_number

    # Compute the important coordinates.
    index_to_draw, _ = _helpers.get_max_index(bit_gate_rank,
                                              instruction=instruction)
    x_coord = _helpers.get_x_from_index(index_to_draw)
    yq_coord = _helpers.get_y_from_quantum_register(qubits[0], bit_mapping)
    yc_coord = _helpers.get_y_from_classical_register(number_of_clbits - 1,
                                                      total_qubits_number,
                                                      bit_mapping)
    # Then draw the double line representing the classical control.
    _draw_classical_double_line(drawing, x_coord, yq_coord, x_coord, yc_coord)

    # Finally draw all the controlled circles
    for classical_register_index in range(number_of_clbits):
        y_coord = _helpers.get_y_from_classical_register(
            classical_register_index, total_qubits_number, bit_mapping)
        clbit_should_be_1 = (
            classical_register_index < len(little_endian_bit_value) and
            little_endian_bit_value[classical_register_index] == '1')
        _draw_control_circle(drawing, x_coord, y_coord, clbit_should_be_1)
Example #6
0
def _draw_gate(drawing: Drawing, bit_gate_rank: _types.BitRankType, instruction,
               show_clbits: bool, bit_mapping: dict) -> None:
    unitary_gate_names = set('xyzhst')
    supported_base_gates = unitary_gate_names | {'sdg', 'tdg'}
    supported_u_gates = {"u{}".format(i) for i in [1, 2, 3]} | {'u'}
    supported_unitary_gates = supported_base_gates | supported_u_gates
    supported_controlled_gates = (
        {"c{}".format(name) for name in supported_unitary_gates} |
        {"cc{}".format(name) for name in supported_unitary_gates})
    supported_special_gates = {'measure', 'barrier', 'reset', 'swap'}
    supported_gates = (supported_unitary_gates | supported_controlled_gates |
                       supported_special_gates)

    name = instruction['name']
    qubits = instruction['qubits']
    name_conditional_part = ""

    if 'conditional' in instruction:
        if show_clbits:
            _draw_classically_conditioned_part(drawing, bit_gate_rank,
                                               instruction, bit_mapping)
        else:
            # TODO: Change 'c' by the name of the classical register.
            name_conditional_part = "[c={}]".format(
                int(instruction['conditional']['val'], 0))

    # Tag needed later
    drawing_controlled_gate = False
    # Compute the x coordinate of the gate.
    index_to_draw, _ = _helpers.get_max_index(bit_gate_rank,
                                              instruction=instruction)

    # If it is a measure gate then call the specialized function to draw it.
    if name == 'measure':
        _draw_measure_gate(drawing, bit_gate_rank, qubits[0],
                           instruction['clbits'][0], show_clbits, bit_mapping)

    # If it is a barrier gate then we do not draw anything
    if name == 'barrier':
        pass

    # If it is a reset gate, then draw a unitary gate with 'reset' name.
    if name == 'reset':
        _draw_unitary_gate(drawing, bit_gate_rank, qubits[0],
                           name + name_conditional_part, bit_mapping)

    # If it is a swap gate, then draw the specific gate.
    if name == 'swap':
        _draw_swap_gate(drawing, bit_gate_rank, qubits[0], qubits[1],
                        bit_mapping)

    # If the gate is a controlled one then draw the controlled part and let the
    # code just after draw the main gate.
    if name.lower().startswith('c'):
        # Used to draw the line, we need the two qubits at the extremities.
        upper_qubit = min(qubits)
        lower_qubit = max(qubits)
        control_qubits = qubits[:-1]  # All the qubits except the last one
        target_qubit = qubits[-1]  # The last qubit is the target

        # Draw the line, then the little control circle
        _draw_line_between_qubits(drawing, bit_gate_rank, upper_qubit,
                                  lower_qubit, bit_mapping, index_to_draw)
        for control_qubit in control_qubits:
            _draw_control_circle(drawing,
                                 _helpers.get_x_from_index(index_to_draw),
                                 _helpers.get_y_from_quantum_register(
                                     control_qubit, bit_mapping), True)
        # Then if it's a (C)CX gate, draw the stylised (C)CX gate.
        if name.lower().lstrip('c') == 'x':
            _draw_cnot_cross(drawing, _helpers.get_x_from_index(index_to_draw),
                             _helpers.get_y_from_quantum_register(target_qubit,
                                                                  bit_mapping))
        # Else keep the information that we should draw a controlled gate.
        else:
            drawing_controlled_gate = True
            name = name[1:]
            qubits = qubits[1:]

    # Draw the main gate.
    # 1. Special case for gates with parameters
    if instruction.get('params', None):
        def _round_numeric_param(numeric_param: float) -> str:
            if abs(numeric_param) < 1e-10:
                # Avoid the "0.0"
                return "0"
            return str(
                round(numeric_param, _constants.PARAMETERS_ROUND_DECIMAL))

        _draw_unitary_gate(drawing, bit_gate_rank, qubits[0],
                           name + name_conditional_part + "({})".format(
                               ",".join(map(_round_numeric_param,
                                            instruction['params']))), bit_mapping,
                           is_controlled_gate=drawing_controlled_gate,
                           index_to_draw=index_to_draw)

    # 2. For all the gates without parameters, simply draw them
    elif name.lower() in supported_base_gates:
        _draw_unitary_gate(drawing, bit_gate_rank, qubits[0],
                           name.upper() + name_conditional_part, bit_mapping,
                           is_controlled_gate=drawing_controlled_gate,
                           index_to_draw=index_to_draw)

    # Warn the user we encountered a non-implemented gate.
    if instruction['name'].lower() not in supported_gates:
        print("WARNING: Gate '{}' is not implemented".format(instruction['name']))

    # And finally take care of our data structure that keeps track of the
    # position
    # where we want to draw.
    _helpers._update_data_structure(bit_gate_rank, instruction)