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
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)
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))
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))
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)
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
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), ]
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), ]
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
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.")
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()
""" 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)
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)) + "^")