Beispiel #1
0
def _decompose_abc(
        matrix: np.ndarray
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, float]:
    """Decomposes 2x2 unitary matrix.

    Returns 2x2 special unitary matrices A, B, C and phase delta, such that:
    * ABC = I.
    * AXBXC * exp(1j*delta) = matrix.

    See [1], chapter 4.
    """
    assert matrix.shape == (2, 2)
    delta = np.angle(np.linalg.det(matrix)) * 0.5
    alpha = np.angle(matrix[0, 0]) + np.angle(matrix[0, 1]) - 2 * delta
    beta = np.angle(matrix[0, 0]) - np.angle(matrix[0, 1])

    m00_abs = np.abs(matrix[0, 0])
    if np.abs(m00_abs - 1.0) < 1e-9:
        m00_abs = 1
    theta = 2 * np.arccos(m00_abs)

    a = unitary(ops.rz(-alpha)) @ unitary(ops.ry(-theta / 2))
    b = unitary(ops.ry(theta / 2)) @ unitary(ops.rz((alpha + beta) / 2))
    c = unitary(ops.rz((alpha - beta) / 2))

    x = unitary(ops.X)
    assert np.allclose(a @ b @ c, np.eye(2), atol=1e-2)
    assert np.allclose((a @ x @ b @ x @ c) * np.exp(1j * delta),
                       matrix,
                       atol=1e-2)

    return a, b, c, delta
Beispiel #2
0
def _decompose_interaction_into_two_b_gates_ignoring_single_qubit_ops(
        qubits: Sequence['cirq.Qid'],
        kak_interaction_coefficients: Iterable[float]
) -> List['cirq.Operation']:
    """
    References:
        Minimum construction of two-qubit quantum operations
        https://arxiv.org/abs/quant-ph/0312193
    """
    a, b = qubits
    x, y, z = kak_interaction_coefficients
    r = (np.sin(y) * np.cos(z))**2
    r = max(0.0, min(0.5, r))  # Clamp out-of-range floating point error.
    b1 = np.arccos(1 - 4 * r)
    a2 = np.cos(y * 2) * np.cos(z * 2) / (1 - 2 * r)
    a2 = max(0.0, min(1, a2))  # Clamp out-of-range floating point error.
    b2 = np.arcsin(np.sqrt(a2))
    s = 1 if z < 0 else -1
    return [
        _B(a, b),
        ops.ry(s * 2 * x).on(a),
        ops.rz(b2).on(b),
        ops.ry(b1).on(b),
        ops.rz(b2).on(b),
        _B(a, b),
    ]
def _init_ops(data: Dict[str, Any]) -> 'cirq.OP_TREE':
    if 'init' not in data:
        return []
    init = data['init']
    if not isinstance(init, List):
        raise ValueError(f'Circuit JSON init must be a list but was {init!r}.')
    init_ops = []
    for i in range(len(init)):
        state = init[i]
        q = devices.LineQubit(i)
        if state == 0:
            pass
        elif state == 1:
            init_ops.append(ops.X(q))
        elif state == '+':
            init_ops.append(ops.ry(np.pi / 2).on(q))
        elif state == '-':
            init_ops.append(ops.ry(-np.pi / 2).on(q))
        elif state == 'i':
            init_ops.append(ops.rx(-np.pi / 2).on(q))
        elif state == '-i':
            init_ops.append(ops.rx(np.pi / 2).on(q))
        else:
            raise ValueError(f'Unrecognized init state: {state!r}')
    return ops.Moment(init_ops)
Beispiel #4
0
def _decompose_interaction_into_two_b_gates_ignoring_single_qubit_ops(
    qubits: Sequence['cirq.Qid'], kak_interaction_coefficients: Iterable[float]
) -> List['cirq.Operation']:
    """Decompose using a minimal construction of two-qubit operations.

    References:
        Minimum construction of two-qubit quantum operations
        https://arxiv.org/abs/quant-ph/0312193
    """
    a, b = qubits
    x, y, z = kak_interaction_coefficients
    r = (np.sin(y) * np.cos(z)) ** 2
    r = max(0.0, r)  # Clamp out-of-range floating point error.
    if r > 0.499999999999:
        rb = [
            ops.ry(np.pi).on(b),
        ]
    else:
        b1 = np.cos(y * 2) * np.cos(z * 2) / (1 - 2 * r)
        b1 = max(0.0, min(1, b1))  # Clamp out-of-range floating point error.
        b2 = np.arcsin(np.sqrt(b1))
        b3 = np.arccos(1 - 4 * r)
        rb = [
            ops.rz(-b2).on(b),
            ops.ry(-b3).on(b),
            ops.rz(-b2).on(b),
        ]
    s = 1 if z < 0 else -1
    return [
        _B(a, b),
        ops.ry(s * 2 * x).on(a),
        *rb,
        _B(a, b),
    ]
    def convert_one(self, op: ops.Operation) -> ops.OP_TREE:
        """Convert a single (one- or two-qubit) operation into ion trap native gates.

        Args:
            op: The gate operation to be converted.

        Returns:
            The desired operations implemented with ion trap gates.

        Raises:
            TypeError: If the operation cannot be converted.
        """

        # Known gate name
        if not isinstance(op, ops.GateOperation):
            raise TypeError(f"{op!r} is not a gate operation.")

        if op in self.gateset:
            return [op]
        # one choice of known Hadamard gate decomposition
        if isinstance(op.gate, ops.HPowGate) and op.gate.exponent == 1:
            return [
                ops.rx(np.pi).on(op.qubits[0]),
                ops.ry(-1 * np.pi / 2).on(op.qubits[0])
            ]
        # one choice of known CNOT gate decomposition
        if isinstance(op.gate, ops.CNotPowGate) and op.gate.exponent == 1:
            return [
                ops.ry(np.pi / 2).on(op.qubits[0]),
                ms(np.pi / 4).on(op.qubits[0], op.qubits[1]),
                ops.rx(-1 * np.pi / 2).on(op.qubits[0]),
                ops.rx(-1 * np.pi / 2).on(op.qubits[1]),
                ops.ry(-1 * np.pi / 2).on(op.qubits[0]),
            ]
        # Known matrix
        mat = protocols.unitary(op, None) if len(op.qubits) <= 2 else None
        if mat is not None and len(op.qubits) == 1:
            gates = transformers.single_qubit_matrix_to_phased_x_z(mat)
            return [g.on(op.qubits[0]) for g in gates]
        if mat is not None and len(op.qubits) == 2:
            return two_qubit_matrix_to_ion_operations(op.qubits[0],
                                                      op.qubits[1], mat)

        if self.ignore_failures:
            return [op]

        raise TypeError("Don't know how to work with {!r}. "
                        "It isn't a native Ion Trap operation, "
                        "a 1 or 2 qubit gate with a known unitary, "
                        "or composite.".format(op.gate))
Beispiel #6
0
def test_non_identity_scale_1q():
    """Tests that when scale factor = 1, the circuit is the
    same.
    """
    qreg = LineQubit.range(3)
    circ = Circuit([ops.rx(np.pi * 1.0).on_each(qreg)],
                   [ops.ry(np.pi * 1.0).on(qreg[0])])
    np.random.seed(42)
    stretch = 2
    base_noise = 0.001
    noises = np.random.normal(loc=0.0,
                              scale=np.sqrt((stretch - 1) * base_noise),
                              size=(4, ))
    np.random.seed(42)

    scaled = scale_parameters(circ,
                              scale_factor=stretch,
                              sigma=base_noise,
                              seed=42)
    result = []
    for moment in scaled:
        for op in moment.operations:
            gate = deepcopy(op.gate)
            param = gate.exponent
            result.append(param * np.pi - np.pi)
    assert np.all(np.isclose(result - noises, 0))
Beispiel #7
0
def test_from_braket_parameterized_single_qubit_gates(qubit_index):
    braket_circuit = BKCircuit()
    pgates = [
        braket_gates.Rx,
        braket_gates.Ry,
        braket_gates.Rz,
        braket_gates.PhaseShift,
    ]
    angles = np.random.RandomState(11).random(len(pgates))
    instructions = [
        Instruction(rot(a), target=qubit_index)
        for rot, a in zip(pgates, angles)
    ]
    for instr in instructions:
        braket_circuit.add_instruction(instr)
    cirq_circuit = from_braket(braket_circuit)

    for i, op in enumerate(cirq_circuit.all_operations()):
        assert np.allclose(instructions[i].operator.to_matrix(),
                           protocols.unitary(op))

    qubit = LineQubit(qubit_index)
    expected_cirq_circuit = Circuit(
        ops.rx(angles[0]).on(qubit),
        ops.ry(angles[1]).on(qubit),
        ops.rz(angles[2]).on(qubit),
        ops.Z.on(qubit)**(angles[3] / np.pi),
    )
    assert _equal(cirq_circuit,
                  expected_cirq_circuit,
                  require_qubit_equality=True)
def prepare_two_qubit_state_using_cz(
        q0: 'cirq.Qid', q1: 'cirq.Qid',
        state: 'cirq.STATE_VECTOR_LIKE') -> List['cirq.Operation']:
    """Prepares the given 2q state from |00> using at-most 1 CZ gate + single qubit rotations.

    Entangled states are prepared using exactly 1 CZ gate while product states are prepared
    using only single qubit rotations (0 CZ gates)

    Args:
        q0: The first qubit being operated on.
        q1: The other qubit being operated on.
        state: 4x1 matrix representing two qubit state vector, ordered as 00, 01, 10, 11.

    Returns:
        List of operations (at-most 1 CZ + single qubit rotations) preparing `state` from |00>.
    """
    state = qis.to_valid_state_vector(state, num_qubits=2)
    state = state / np.linalg.norm(state)
    u, s, vh = np.linalg.svd(state.reshape(2, 2))
    if np.isclose(s[0], 1):
        # Product state can be prepare with just single qubit unitaries.
        return _1q_matrices_to_ops(u, vh.T, q0, q1, True)
    alpha = np.arccos(np.clip(s[0], 0, 1))
    op_list = [ops.ry(2 * alpha).on(q0), ops.H.on(q1), ops.CZ.on(q0, q1)]
    intermediate_state = circuits.Circuit(op_list).final_state_vector(
        ignore_terminal_measurements=False, dtype=np.complex64)
    u_CZ, _, vh_CZ = np.linalg.svd(intermediate_state.reshape(2, 2))
    return op_list + _1q_matrices_to_ops(np.dot(
        u, np.linalg.inv(u_CZ)), np.dot(vh.T, np.linalg.inv(vh_CZ.T)), q0, q1)
def prepare_two_qubit_state_using_sqrt_iswap(
    q0: 'cirq.Qid',
    q1: 'cirq.Qid',
    state: 'cirq.STATE_VECTOR_LIKE',
    *,
    use_sqrt_iswap_inv: bool = True,
) -> List['cirq.Operation']:
    """Prepares the given 2q state from |00> using at-most 1 √iSWAP gate + single qubit rotations.

    Entangled states are prepared using exactly 1 √iSWAP gate while product states are prepared
    using only single qubit rotations (0 √iSWAP gates)

    Args:
        q0: The first qubit being operated on.
        q1: The other qubit being operated on.
        state: 4x1 matrix representing two qubit state vector, ordered as 00, 01, 10, 11.
        use_sqrt_iswap_inv: If True, uses `cirq.SQRT_ISWAP_INV` instead of `cirq.SQRT_ISWAP`.

    Returns:
        List of operations (at-most 1 √iSWAP + single qubit rotations) preparing `state` from |00>.
    """
    state = qis.to_valid_state_vector(state, num_qubits=2)
    state = state / np.linalg.norm(state)
    u, s, vh = np.linalg.svd(state.reshape(2, 2))
    if np.isclose(s[0], 1):
        # Product state can be prepare with just single qubit unitaries.
        return _1q_matrices_to_ops(u, vh.T, q0, q1, True)
    alpha = np.arccos(np.sqrt(np.clip(1 - s[0] * 2 * s[1], 0, 1)))
    sqrt_iswap_gate = ops.SQRT_ISWAP_INV if use_sqrt_iswap_inv else ops.SQRT_ISWAP
    op_list = [ops.ry(2 * alpha).on(q0), sqrt_iswap_gate.on(q0, q1)]
    intermediate_state = circuits.Circuit(op_list).final_state_vector()
    u_iSWAP, _, vh_iSWAP = np.linalg.svd(intermediate_state.reshape(2, 2))
    return op_list + _1q_matrices_to_ops(
        np.dot(u, np.linalg.inv(u_iSWAP)),
        np.dot(vh.T, np.linalg.inv(vh_iSWAP.T)), q0, q1)
Beispiel #10
0
 def _decompose_single_qubit_operation(self, op: 'cirq.Operation', _: int) -> DecomposeResult:
     if isinstance(op.gate, ops.HPowGate) and op.gate.exponent == 1:
         return [ops.rx(np.pi).on(op.qubits[0]), ops.ry(-1 * np.pi / 2).on(op.qubits[0])]
     if protocols.has_unitary(op):
         gates = transformers.single_qubit_matrix_to_phased_x_z(protocols.unitary(op))
         return [g.on(op.qubits[0]) for g in gates]
     return NotImplemented
Beispiel #11
0
def _ccnot_congruent(c0: 'cirq.Qid', c1: 'cirq.Qid',
                     target: 'cirq.Qid') -> List['cirq.Operation']:
    """Implements 3-qubit gate 'congruent' to CCNOT.

    Returns sequence of operations which is equivalent to applying
    CCNOT(c0, c1, target) and multiplying phase of |101> sate by -1.
    See lemma 6.2 in [1]."""
    return [
        ops.ry(-np.pi / 4).on(target),
        ops.CNOT(c1, target),
        ops.ry(-np.pi / 4).on(target),
        ops.CNOT(c0, target),
        ops.ry(np.pi / 4).on(target),
        ops.CNOT(c1, target),
        ops.ry(np.pi / 4).on(target),
    ]
Beispiel #12
0
 def _decompose_(self, qubits):
     q = qubits[0]
     return [
         ops.rz(self.lmda * np.pi).on(q),
         ops.ry(self.theta * np.pi).on(q),
         ops.rz(self.phi * np.pi).on(q),
     ]
Beispiel #13
0
def _translate_one_qubit_braket_instruction_to_cirq_operation(
    instr: Instruction,
) -> List[cirq_ops.Operation]:
    """Converts the one-qubit braket instruction to Cirq.

    Args:
        instr: One-qubit Braket instruction to convert.

    Raises:
        ValueError: If the instruction cannot be converted to Cirq.
    """
    qubits = [LineQubit(int(qubit)) for qubit in instr.target]
    gate = instr.operator

    # One-qubit non-parameterized gates.
    if isinstance(gate, braket_gates.I):
        return [cirq_ops.I.on(*qubits)]
    elif isinstance(gate, braket_gates.X):
        return [cirq_ops.X.on(*qubits)]
    elif isinstance(gate, braket_gates.Y):
        return [cirq_ops.Y.on(*qubits)]
    elif isinstance(gate, braket_gates.Z):
        return [cirq_ops.Z.on(*qubits)]
    elif isinstance(gate, braket_gates.H):
        return [cirq_ops.H.on(*qubits)]
    elif isinstance(gate, braket_gates.S):
        return [cirq_ops.S.on(*qubits)]
    elif isinstance(gate, braket_gates.Si):
        return [protocols.inverse(cirq_ops.S.on(*qubits))]
    elif isinstance(gate, braket_gates.T):
        return [cirq_ops.T.on(*qubits)]
    elif isinstance(gate, braket_gates.Ti):
        return [protocols.inverse(cirq_ops.T.on(*qubits))]
    elif isinstance(gate, braket_gates.V):
        return [cirq_ops.X.on(*qubits) ** 0.5]
    elif isinstance(gate, braket_gates.Vi):
        return [cirq_ops.X.on(*qubits) ** -0.5]

    # One-qubit parameterized gates.
    elif isinstance(gate, braket_gates.Rx):
        return [cirq_ops.rx(gate.angle).on(*qubits)]
    elif isinstance(gate, braket_gates.Ry):
        return [cirq_ops.ry(gate.angle).on(*qubits)]
    elif isinstance(gate, braket_gates.Rz):
        return [cirq_ops.rz(gate.angle).on(*qubits)]
    elif isinstance(gate, braket_gates.PhaseShift):
        return [cirq_ops.Z.on(*qubits) ** (gate.angle / np.pi)]

    else:
        _raise_braket_to_cirq_error(instr)

    return None  # type: ignore[return-value]  # pragma: no cover
Beispiel #14
0
def _translate_one_qubit_braket_instruction_to_cirq_operation(
    instr: Instruction, ) -> List["cirq.Operation"]:
    """Converts the one-qubit braket instruction to Cirq.

    Args:
        instr: One-qubit Braket instruction to convert.

    Raises:
        ValueError: If the instruction cannot be converted to Cirq.
    """
    qubits = [LineQubit(int(qubit)) for qubit in instr.target]
    gate = instr.operator

    # One-qubit non-parameterized gates.
    if isinstance(gate, braket_gates.I):
        return [cirq_ops.I.on(*qubits)]
    elif isinstance(gate, braket_gates.X):
        return [cirq_ops.X.on(*qubits)]
    elif isinstance(gate, braket_gates.Y):
        return [cirq_ops.Y.on(*qubits)]
    elif isinstance(gate, braket_gates.Z):
        return [cirq_ops.Z.on(*qubits)]
    elif isinstance(gate, braket_gates.H):
        return [cirq_ops.H.on(*qubits)]
    elif isinstance(gate, braket_gates.S):
        return [cirq_ops.S.on(*qubits)]
    elif isinstance(gate, braket_gates.Si):
        return [cirq_ops.S.on(*qubits)**-1.0]
    elif isinstance(gate, braket_gates.T):
        return [cirq_ops.T.on(*qubits)]
    elif isinstance(gate, braket_gates.Ti):
        return [cirq_ops.T.on(*qubits)**-1.0]
    elif isinstance(gate, braket_gates.V):
        return [cirq_ops.X.on(*qubits)**0.5]
    elif isinstance(gate, braket_gates.Vi):
        return [cirq_ops.X.on(*qubits)**-0.5]

    # One-qubit parameterized gates.
    elif isinstance(gate, braket_gates.Rx):
        return [cirq_ops.rx(gate.angle).on(*qubits)]
    elif isinstance(gate, braket_gates.Ry):
        return [cirq_ops.ry(gate.angle).on(*qubits)]
    elif isinstance(gate, braket_gates.Rz):
        return [cirq_ops.rz(gate.angle).on(*qubits)]
    elif isinstance(gate, braket_gates.PhaseShift):
        return [cirq_ops.Z.on(*qubits)**(gate.angle / np.pi)]

    else:
        raise ValueError(
            f"Unable to convert the instruction {instr} to Cirq. If you think "
            "this is a bug, you can open an issue on the Mitiq GitHub at "
            "https://github.com/unitaryfund/mitiq.")
Beispiel #15
0
def test_circuit_from_quil():
    q0, q1, q2 = LineQubit.range(3)
    cirq_circuit = Circuit([
        I(q0),
        I(q1),
        I(q2),
        X(q0),
        Y(q1),
        Z(q2),
        H(q0),
        S(q1),
        T(q2),
        Z(q0)**(1 / 8),
        Z(q1)**(1 / 8),
        Z(q2)**(1 / 8),
        rx(np.pi / 2)(q0),
        ry(np.pi / 2)(q1),
        rz(np.pi / 2)(q2),
        CZ(q0, q1),
        CNOT(q1, q2),
        cphase(np.pi / 2)(q0, q1),
        cphase00(np.pi / 2)(q1, q2),
        cphase01(np.pi / 2)(q0, q1),
        cphase10(np.pi / 2)(q1, q2),
        ISWAP(q0, q1),
        pswap(np.pi / 2)(q1, q2),
        SWAP(q0, q1),
        xy(np.pi / 2)(q1, q2),
        CCNOT(q0, q1, q2),
        CSWAP(q0, q1, q2),
        MeasurementGate(1, key="ro[0]")(q0),
        MeasurementGate(1, key="ro[1]")(q1),
        MeasurementGate(1, key="ro[2]")(q2),
    ])
    # build the same Circuit, using Quil
    quil_circuit = circuit_from_quil(QUIL_PROGRAM)
    # test Circuit equivalence
    assert cirq_circuit == quil_circuit

    pyquil_circuit = Program(QUIL_PROGRAM)
    # drop declare and measures, get Program unitary
    pyquil_unitary = program_unitary(pyquil_circuit[1:-3], n_qubits=3)
    # fix qubit order convention
    cirq_circuit_swapped = Circuit(SWAP(q0, q2), cirq_circuit[:-1],
                                   SWAP(q0, q2))
    # get Circuit unitary
    cirq_unitary = cirq_circuit_swapped.unitary()
    # test unitary equivalence
    assert np.isclose(pyquil_unitary, cirq_unitary).all()
Beispiel #16
0
"""
IQM's Valkmusa quantum architecture.
"""
from math import pi as PI
from typing import Optional

from cirq import ops

from .iqm_device import IQMDevice

PI_2 = PI / 2

# common gates used in gate decompositions
Lx = ops.rx(PI_2)
Lxi = ops.rx(-PI_2)
Ly = ops.ry(PI_2)
Lyi = ops.ry(-PI_2)
Lz = ops.rz(PI_2)
Lzi = ops.rz(-PI_2)


class Valkmusa(IQMDevice):
    """IQM's two-qubit transmon device.

    The qubits are connected thus::

      QB1 - QB2

    Each qubit can be rotated about any axis in the xy plane by an arbitrary angle.
    The native two qubit-gate is ISwapPowGate.
    The qubits are always measured simultaneously at the end of the computation.
def generate_all_single_qubit_rotation_cell_makers() -> Iterator[CellMaker]:

    # Fixed single qubit rotations.
    yield _gate("H", ops.H)
    yield _gate("X", ops.X)
    yield _gate("Y", ops.Y)
    yield _gate("Z", ops.Z)
    yield _gate("X^½", ops.X**(1 / 2))
    yield _gate("X^⅓", ops.X**(1 / 3))
    yield _gate("X^¼", ops.X**(1 / 4))
    yield _gate("X^⅛", ops.X**(1 / 8))
    yield _gate("X^⅟₁₆", ops.X**(1 / 16))
    yield _gate("X^⅟₃₂", ops.X**(1 / 32))
    yield _gate("X^-½", ops.X**(-1 / 2))
    yield _gate("X^-⅓", ops.X**(-1 / 3))
    yield _gate("X^-¼", ops.X**(-1 / 4))
    yield _gate("X^-⅛", ops.X**(-1 / 8))
    yield _gate("X^-⅟₁₆", ops.X**(-1 / 16))
    yield _gate("X^-⅟₃₂", ops.X**(-1 / 32))
    yield _gate("Y^½", ops.Y**(1 / 2))
    yield _gate("Y^⅓", ops.Y**(1 / 3))
    yield _gate("Y^¼", ops.Y**(1 / 4))
    yield _gate("Y^⅛", ops.Y**(1 / 8))
    yield _gate("Y^⅟₁₆", ops.Y**(1 / 16))
    yield _gate("Y^⅟₃₂", ops.Y**(1 / 32))
    yield _gate("Y^-½", ops.Y**(-1 / 2))
    yield _gate("Y^-⅓", ops.Y**(-1 / 3))
    yield _gate("Y^-¼", ops.Y**(-1 / 4))
    yield _gate("Y^-⅛", ops.Y**(-1 / 8))
    yield _gate("Y^-⅟₁₆", ops.Y**(-1 / 16))
    yield _gate("Y^-⅟₃₂", ops.Y**(-1 / 32))
    yield _gate("Z^½", ops.Z**(1 / 2))
    yield _gate("Z^⅓", ops.Z**(1 / 3))
    yield _gate("Z^¼", ops.Z**(1 / 4))
    yield _gate("Z^⅛", ops.Z**(1 / 8))
    yield _gate("Z^⅟₁₆", ops.Z**(1 / 16))
    yield _gate("Z^⅟₃₂", ops.Z**(1 / 32))
    yield _gate("Z^⅟₆₄", ops.Z**(1 / 64))
    yield _gate("Z^⅟₁₂₈", ops.Z**(1 / 128))
    yield _gate("Z^-½", ops.Z**(-1 / 2))
    yield _gate("Z^-⅓", ops.Z**(-1 / 3))
    yield _gate("Z^-¼", ops.Z**(-1 / 4))
    yield _gate("Z^-⅛", ops.Z**(-1 / 8))
    yield _gate("Z^-⅟₁₆", ops.Z**(-1 / 16))

    # Dynamic single qubit rotations.
    yield _gate("X^t", ops.X**sympy.Symbol('t'))
    yield _gate("Y^t", ops.Y**sympy.Symbol('t'))
    yield _gate("Z^t", ops.Z**sympy.Symbol('t'))
    yield _gate("X^-t", ops.X**-sympy.Symbol('t'))
    yield _gate("Y^-t", ops.Y**-sympy.Symbol('t'))
    yield _gate("Z^-t", ops.Z**-sympy.Symbol('t'))
    yield _gate("e^iXt", ops.rx(2 * sympy.pi * sympy.Symbol('t')))
    yield _gate("e^iYt", ops.ry(2 * sympy.pi * sympy.Symbol('t')))
    yield _gate("e^iZt", ops.rz(2 * sympy.pi * sympy.Symbol('t')))
    yield _gate("e^-iXt", ops.rx(-2 * sympy.pi * sympy.Symbol('t')))
    yield _gate("e^-iYt", ops.ry(-2 * sympy.pi * sympy.Symbol('t')))
    yield _gate("e^-iZt", ops.rz(-2 * sympy.pi * sympy.Symbol('t')))

    # Formulaic single qubit rotations.
    yield _formula_gate("X^ft", "sin(pi*t)", lambda e: ops.X**e)
    yield _formula_gate("Y^ft", "sin(pi*t)", lambda e: ops.Y**e)
    yield _formula_gate("Z^ft", "sin(pi*t)", lambda e: ops.Z**e)
    yield _formula_gate("Rxft", "pi*t*t", ops.rx)
    yield _formula_gate("Ryft", "pi*t*t", ops.ry)
    yield _formula_gate("Rzft", "pi*t*t", ops.rz)
Beispiel #18
0
class QasmParser:
    """Parser for QASM strings.

    Example:

        qasm = "OPENQASM 2.0; qreg q1[2]; CX q1[0], q1[1];"
        parsedQasm = QasmParser().parse(qasm)
    """
    def __init__(self):
        self.parser = yacc.yacc(module=self, debug=False, write_tables=False)
        self.circuit = Circuit()
        self.qregs: Dict[str, int] = {}
        self.cregs: Dict[str, int] = {}
        self.qelibinc = False
        self.lexer = QasmLexer()
        self.supported_format = False
        self.parsedQasm: Optional[Qasm] = None
        self.qubits: Dict[str, ops.Qid] = {}
        self.functions = {
            'sin': np.sin,
            'cos': np.cos,
            'tan': np.tan,
            'exp': np.exp,
            'ln': np.log,
            'sqrt': np.sqrt,
            'acos': np.arccos,
            'atan': np.arctan,
            'asin': np.arcsin,
        }

        self.binary_operators = {
            '+': operator.add,
            '-': operator.sub,
            '*': operator.mul,
            '/': operator.truediv,
            '^': operator.pow,
        }

    basic_gates: Dict[str, QasmGateStatement] = {
        'CX':
        QasmGateStatement(qasm_gate='CX',
                          cirq_gate=CX,
                          num_params=0,
                          num_args=2),
        'U':
        QasmGateStatement(
            qasm_gate='U',
            num_params=3,
            num_args=1,
            # QasmUGate expects half turns
            cirq_gate=(lambda params: QasmUGate(*[p / np.pi for p in params])),
        ),
    }

    qelib_gates = {
        'rx':
        QasmGateStatement(qasm_gate='rx',
                          cirq_gate=(lambda params: ops.rx(params[0])),
                          num_params=1,
                          num_args=1),
        'ry':
        QasmGateStatement(qasm_gate='ry',
                          cirq_gate=(lambda params: ops.ry(params[0])),
                          num_params=1,
                          num_args=1),
        'rz':
        QasmGateStatement(qasm_gate='rz',
                          cirq_gate=(lambda params: ops.rz(params[0])),
                          num_params=1,
                          num_args=1),
        'id':
        QasmGateStatement(qasm_gate='id',
                          cirq_gate=ops.IdentityGate(1),
                          num_params=0,
                          num_args=1),
        'u1':
        QasmGateStatement(
            qasm_gate='u1',
            cirq_gate=(lambda params: QasmUGate(0, 0, params[0] / np.pi)),
            num_params=1,
            num_args=1,
        ),
        'u2':
        QasmGateStatement(
            qasm_gate='u2',
            cirq_gate=(lambda params: QasmUGate(0.5, params[0] / np.pi, params[
                1] / np.pi)),
            num_params=2,
            num_args=1,
        ),
        'u3':
        QasmGateStatement(
            qasm_gate='u3',
            num_params=3,
            num_args=1,
            cirq_gate=(lambda params: QasmUGate(*[p / np.pi for p in params])),
        ),
        'x':
        QasmGateStatement(qasm_gate='x',
                          num_params=0,
                          num_args=1,
                          cirq_gate=ops.X),
        'y':
        QasmGateStatement(qasm_gate='y',
                          num_params=0,
                          num_args=1,
                          cirq_gate=ops.Y),
        'z':
        QasmGateStatement(qasm_gate='z',
                          num_params=0,
                          num_args=1,
                          cirq_gate=ops.Z),
        'h':
        QasmGateStatement(qasm_gate='h',
                          num_params=0,
                          num_args=1,
                          cirq_gate=ops.H),
        's':
        QasmGateStatement(qasm_gate='s',
                          num_params=0,
                          num_args=1,
                          cirq_gate=ops.S),
        't':
        QasmGateStatement(qasm_gate='t',
                          num_params=0,
                          num_args=1,
                          cirq_gate=ops.T),
        'cx':
        QasmGateStatement(qasm_gate='cx',
                          cirq_gate=CX,
                          num_params=0,
                          num_args=2),
        'cy':
        QasmGateStatement(qasm_gate='cy',
                          cirq_gate=ops.ControlledGate(ops.Y),
                          num_params=0,
                          num_args=2),
        'cz':
        QasmGateStatement(qasm_gate='cz',
                          cirq_gate=ops.CZ,
                          num_params=0,
                          num_args=2),
        'ch':
        QasmGateStatement(qasm_gate='ch',
                          cirq_gate=ops.ControlledGate(ops.H),
                          num_params=0,
                          num_args=2),
        'swap':
        QasmGateStatement(qasm_gate='swap',
                          cirq_gate=ops.SWAP,
                          num_params=0,
                          num_args=2),
        'cswap':
        QasmGateStatement(qasm_gate='cswap',
                          num_params=0,
                          num_args=3,
                          cirq_gate=ops.CSWAP),
        'ccx':
        QasmGateStatement(qasm_gate='ccx',
                          num_params=0,
                          num_args=3,
                          cirq_gate=ops.CCX),
        'sdg':
        QasmGateStatement(qasm_gate='sdg',
                          num_params=0,
                          num_args=1,
                          cirq_gate=ops.S**-1),
        'tdg':
        QasmGateStatement(qasm_gate='tdg',
                          num_params=0,
                          num_args=1,
                          cirq_gate=ops.T**-1),
    }

    all_gates = {**basic_gates, **qelib_gates}

    tokens = QasmLexer.tokens
    start = 'start'

    precedence = (
        ('left', '+', '-'),
        ('left', '*', '/'),
        ('right', '^'),
    )

    def p_start(self, p):
        """start : qasm"""
        p[0] = p[1]

    def p_qasm_format_only(self, p):
        """qasm : format"""
        self.supported_format = True
        p[0] = Qasm(self.supported_format, self.qelibinc, self.qregs,
                    self.cregs, self.circuit)

    def p_qasm_no_format_specified_error(self, p):
        """qasm : QELIBINC
        | circuit"""
        if self.supported_format is False:
            raise QasmException("Missing 'OPENQASM 2.0;' statement")

    def p_qasm_include(self, p):
        """qasm : qasm QELIBINC"""
        self.qelibinc = True
        p[0] = Qasm(self.supported_format, self.qelibinc, self.qregs,
                    self.cregs, self.circuit)

    def p_qasm_circuit(self, p):
        """qasm : qasm circuit"""
        p[0] = Qasm(self.supported_format, self.qelibinc, self.qregs,
                    self.cregs, p[2])

    def p_format(self, p):
        """format : FORMAT_SPEC"""
        if p[1] != "2.0":
            raise QasmException(
                "Unsupported OpenQASM version: {}, "
                "only 2.0 is supported currently by Cirq".format(p[1]))

    # circuit : new_reg circuit
    #         | gate_op circuit
    #         | measurement circuit
    #         | empty

    def p_circuit_reg(self, p):
        """circuit : new_reg circuit"""
        p[0] = self.circuit

    def p_circuit_gate_or_measurement(self, p):
        """circuit :  circuit gate_op
        |  circuit measurement"""
        self.circuit.append(p[2])
        p[0] = self.circuit

    def p_circuit_empty(self, p):
        """circuit : empty"""
        p[0] = self.circuit

    # qreg and creg

    def p_new_reg(self, p):
        """new_reg : QREG ID '[' NATURAL_NUMBER ']' ';'
        | CREG ID '[' NATURAL_NUMBER ']' ';'"""
        name, length = p[2], p[4]
        if name in self.qregs.keys() or name in self.cregs.keys():
            raise QasmException("{} is already defined at line {}".format(
                name, p.lineno(2)))
        if length == 0:
            raise QasmException(
                "Illegal, zero-length register '{}' at line {}".format(
                    name, p.lineno(4)))
        if p[1] == "qreg":
            self.qregs[name] = length
        else:
            self.cregs[name] = length
        p[0] = (name, length)

    # gate operations
    # gate_op : ID qargs
    #         | ID ( params ) qargs

    def p_gate_op_no_params(self, p):
        """gate_op :  ID qargs"""
        self._resolve_gate_operation(p[2], gate=p[1], p=p, params=[])

    def p_gate_op_with_params(self, p):
        """gate_op :  ID '(' params ')' qargs"""
        self._resolve_gate_operation(args=p[5], gate=p[1], p=p, params=p[3])

    def _resolve_gate_operation(self, args: List[List[ops.Qid]], gate: str,
                                p: Any, params: List[float]):
        gate_set = self.basic_gates if not self.qelibinc else self.all_gates
        if gate not in gate_set.keys():
            msg = 'Unknown gate "{}" at line {}{}'.format(
                gate,
                p.lineno(1),
                ", did you forget to include qelib1.inc?"
                if not self.qelibinc else "",
            )
            raise QasmException(msg)
        p[0] = gate_set[gate].on(args=args, params=params, lineno=p.lineno(1))

    # params : parameter ',' params
    #        | parameter

    def p_params_multiple(self, p):
        """params : expr ',' params"""
        p[3].insert(0, p[1])
        p[0] = p[3]

    def p_params_single(self, p):
        """params : expr """
        p[0] = [p[1]]

    # expr : term
    #            | func '(' expression ')' """
    #            | binary_op
    #            | unary_op

    def p_expr_term(self, p):
        """expr : term"""
        p[0] = p[1]

    def p_expr_parens(self, p):
        """expr : '(' expr ')'"""
        p[0] = p[2]

    def p_expr_function_call(self, p):
        """expr : ID '(' expr ')'"""
        func = p[1]
        if func not in self.functions.keys():
            raise QasmException(
                "Function not recognized: '{}' at line {}".format(
                    func, p.lineno(1)))
        p[0] = self.functions[func](p[3])

    def p_expr_unary(self, p):
        """expr : '-' expr
        | '+' expr"""
        if p[1] == '-':
            p[0] = -p[2]
        else:
            p[0] = p[2]

    def p_expr_binary(self, p):
        """expr : expr '*' expr
        | expr '/' expr
        | expr '+' expr
        | expr '-' expr
        | expr '^' expr
        """
        p[0] = self.binary_operators[p[2]](p[1], p[3])

    def p_term(self, p):
        """term : NUMBER
        | NATURAL_NUMBER
        | PI"""
        p[0] = p[1]

    # qargs : qarg ',' qargs
    #      | qarg ';'

    def p_args_multiple(self, p):
        """qargs : qarg ',' qargs"""
        p[3].insert(0, p[1])
        p[0] = p[3]

    def p_args_single(self, p):
        """qargs : qarg ';'"""
        p[0] = [p[1]]

    # qarg : ID
    #     | ID '[' NATURAL_NUMBER ']'

    def p_quantum_arg_register(self, p):
        """qarg : ID """
        reg = p[1]
        if reg not in self.qregs.keys():
            raise QasmException(
                'Undefined quantum register "{}" at line {}'.format(
                    reg, p.lineno(1)))
        qubits = []
        for idx in range(self.qregs[reg]):
            arg_name = self.make_name(idx, reg)
            if arg_name not in self.qubits.keys():
                self.qubits[arg_name] = NamedQubit(arg_name)
            qubits.append(self.qubits[arg_name])
        p[0] = qubits

    # carg : ID
    #     | ID '[' NATURAL_NUMBER ']'

    def p_classical_arg_register(self, p):
        """carg : ID """
        reg = p[1]
        if reg not in self.cregs.keys():
            raise QasmException(
                'Undefined classical register "{}" at line {}'.format(
                    reg, p.lineno(1)))

        p[0] = [self.make_name(idx, reg) for idx in range(self.cregs[reg])]

    def make_name(self, idx, reg):
        return str(reg) + "_" + str(idx)

    def p_quantum_arg_bit(self, p):
        """qarg : ID '[' NATURAL_NUMBER ']' """
        reg = p[1]
        idx = p[3]
        arg_name = self.make_name(idx, reg)
        if reg not in self.qregs.keys():
            raise QasmException(
                'Undefined quantum register "{}" at line {}'.format(
                    reg, p.lineno(1)))
        size = self.qregs[reg]
        if idx >= size:
            raise QasmException('Out of bounds qubit index {} '
                                'on register {} of size {} '
                                'at line {}'.format(idx, reg, size,
                                                    p.lineno(1)))
        if arg_name not in self.qubits.keys():
            self.qubits[arg_name] = NamedQubit(arg_name)
        p[0] = [self.qubits[arg_name]]

    def p_classical_arg_bit(self, p):
        """carg : ID '[' NATURAL_NUMBER ']' """
        reg = p[1]
        idx = p[3]
        arg_name = self.make_name(idx, reg)
        if reg not in self.cregs.keys():
            raise QasmException(
                'Undefined classical register "{}" at line {}'.format(
                    reg, p.lineno(1)))

        size = self.cregs[reg]
        if idx >= size:
            raise QasmException('Out of bounds bit index {} '
                                'on classical register {} of size {} '
                                'at line {}'.format(idx, reg, size,
                                                    p.lineno(1)))
        p[0] = [arg_name]

    # measurement operations
    # measurement : MEASURE qarg ARROW carg

    def p_measurement(self, p):
        """measurement : MEASURE qarg ARROW carg ';'"""
        qreg = p[2]
        creg = p[4]

        if len(qreg) != len(creg):
            raise QasmException(
                'mismatched register sizes {} -> {} for measurement '
                'at line {}'.format(len(qreg), len(creg), p.lineno(1)))

        p[0] = [
            ops.MeasurementGate(num_qubits=1, key=creg[i]).on(qreg[i])
            for i in range(len(qreg))
        ]

    def p_error(self, p):
        if p is None:
            raise QasmException('Unexpected end of file')

        raise QasmException("""Syntax error: '{}'
{}
at line {}, column {}""".format(p.value, self.debug_context(p), p.lineno,
                                self.find_column(p)))

    def find_column(self, p):
        line_start = self.qasm.rfind('\n', 0, p.lexpos) + 1
        return (p.lexpos - line_start) + 1

    def p_empty(self, p):
        """empty :"""

    def parse(self, qasm: str) -> Qasm:
        if self.parsedQasm is None:
            self.qasm = qasm
            self.lexer.input(self.qasm)
            self.parsedQasm = self.parser.parse(lexer=self.lexer)
        return self.parsedQasm

    def debug_context(self, p):
        debug_start = max(self.qasm.rfind('\n', 0, p.lexpos) + 1, p.lexpos - 5)
        debug_end = min(self.qasm.find('\n', p.lexpos, p.lexpos + 5),
                        p.lexpos + 5)

        return ("..." + self.qasm[debug_start:debug_end] + "\n" +
                (" " * (3 + p.lexpos - debug_start)) + "^")