def test_kak_decomposition_depth_partial_cz(): a, b = cirq.LineQubit.range(2) # Random. u = cirq.testing.random_unitary(4) operations_with_full = cirq.two_qubit_matrix_to_operations(a, b, u, True) c = cirq.Circuit.from_ops(operations_with_full) # 3 CP, 3+1 PhasedX, 1 Z assert len(c) <= 8 # Double-axis interaction. u = cirq.unitary(cirq.Circuit.from_ops(cirq.CNOT(a, b), cirq.CNOT(b, a))) operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, True) c = cirq.Circuit.from_ops(operations_with_part) # 2 CP, 2+1 PhasedX, 1 Z assert len(c) <= 6 # Partial single-axis interaction. u = cirq.unitary(cirq.CNOT**0.1) operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, True) c = cirq.Circuit.from_ops(operations_with_part) # 1 CP, 1+1 PhasedX, 1 Z assert len(c) <= 4 # Full single-axis interaction. u = cirq.unitary(cirq.ControlledGate(cirq.Y)) operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, True) c = cirq.Circuit.from_ops(operations_with_part) # 1 CP, 1+1 PhasedX, 1 Z assert len(c) <= 4
def test_two_to_ops_equivalent_and_bounded_for_known_and_random( max_partial_cz_depth, max_full_cz_depth, effect ): q0 = cirq.NamedQubit('q0') q1 = cirq.NamedQubit('q1') operations_with_partial = cirq.two_qubit_matrix_to_operations(q0, q1, effect, True) operations_with_full = cirq.two_qubit_matrix_to_operations(q0, q1, effect, False) assert_ops_implement_unitary(q0, q1, operations_with_partial, effect) assert_ops_implement_unitary(q0, q1, operations_with_full, effect) assert_cz_depth_below(operations_with_partial, max_partial_cz_depth, False) assert_cz_depth_below(operations_with_full, max_full_cz_depth, True)
def decompose_arbitrary_into_syc_analytic(qubit_a: cirq.Qid, qubit_b: cirq.Qid, op: cirq.Operation) -> cirq.OP_TREE: """Synthesize an arbitrary 2 qubit operation to a sycamore operation using the given Tabulation. Args: qubit_a: first qubit of the operation qubit_b: second qubit of the operation op: operation to decompose tabulation: A tabulation for the Sycamore gate. Returns: New operations iterable object """ new_ops = cirq.two_qubit_matrix_to_operations(qubit_a, qubit_b, op, allow_partial_czs=True) for new_op in new_ops: num_qubits = len(new_op.qubits) if num_qubits == 1: (a, ) = new_op.qubits yield from _phased_x_z_ops(cirq.unitary(new_op), a) elif num_qubits == 2: a, b = op.qubits yield from cirq.flatten_to_ops( known_two_q_operations_to_sycamore_operations(a, b, new_op))
def decompose_arbitrary_into_syc_analytic(qubit_a: cirq.Qid, qubit_b: cirq.Qid, op: cirq.Operation) -> cirq.OP_TREE: """Synthesize an arbitrary 2 qubit operation to a Sycamore operation using the given Tabulation. Args: qubit_a: First qubit of the operation. qubit_b: Second qubit of the operation. op: Operation to decompose. tabulation: A tabulation for the Sycamore gate. Yields: A `cirq.OP_TREE` which produces the given operation using Sycamores. """ new_ops = cirq.two_qubit_matrix_to_operations(qubit_a, qubit_b, op, allow_partial_czs=True) for new_op in new_ops: num_qubits = len(new_op.qubits) if num_qubits == 1: (a, ) = new_op.qubits yield from _phased_x_z_ops(cirq.unitary(new_op), a) elif num_qubits == 2: a, b = op.qubits yield from cirq.flatten_to_ops( known_two_q_operations_to_sycamore_operations(a, b, new_op))
def test_two_to_ops_equivalent_and_bounded_for_known_and_random( max_partial_cz_depth, max_full_cz_depth, effect): q0 = cirq.QubitId() q1 = cirq.QubitId() operations_with_partial = cirq.two_qubit_matrix_to_operations( q0, q1, effect, True) operations_with_full = cirq.two_qubit_matrix_to_operations( q0, q1, effect, False) assert_ops_implement_unitary(q0, q1, operations_with_partial, effect) assert_ops_implement_unitary(q0, q1, operations_with_full, effect) assert_cz_depth_below(operations_with_partial, max_partial_cz_depth, False) assert_cz_depth_below(operations_with_full, max_full_cz_depth, True)
def test_kak_decomposition_depth_full_cz(): a, b = cirq.LineQubit.range(2) # Random. u = cirq.testing.random_unitary(4) operations_with_full = cirq.two_qubit_matrix_to_operations(a, b, u, False) c = cirq.Circuit.from_ops(operations_with_full) # 3 CZ, 3+1 PhasedX, 1 Z assert len(c) <= 8 # Double-axis interaction. u = cirq.unitary(cirq.Circuit.from_ops(cirq.CNOT(a, b), cirq.CNOT(b, a))) operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, False) c = cirq.Circuit.from_ops(operations_with_part) # 2 CZ, 2+1 PhasedX, 1 Z assert len(c) <= 6 # Test unoptimized/un-cleaned length of Double-axis interaction. u = cirq.unitary(cirq.Circuit.from_ops(cirq.CNOT(a, b), cirq.CNOT(b, a))) operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, False, 1e-8, False) c = cirq.Circuit.from_ops(operations_with_part) assert len(c) > 6 # Length should be 13 with extra Pauli gates # Partial single-axis interaction. u = cirq.unitary(cirq.CNOT**0.1) operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, False) c = cirq.Circuit.from_ops(operations_with_part) # 2 CZ, 2+1 PhasedX, 1 Z assert len(c) <= 6 # Full single-axis interaction. u = cirq.unitary(cirq.ControlledGate(cirq.Y)) operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, False) c = cirq.Circuit.from_ops(operations_with_part) # 1 CZ, 1+1 PhasedX, 1 Z assert len(c) <= 4
def _convert_one(self, op: cirq.Operation) -> cirq.OP_TREE: # Known matrix? mat = cirq.unitary(op, None) if len(op.qubits) <= 2 else None if mat is not None and len(op.qubits) == 1: gates = cirq.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 cirq.two_qubit_matrix_to_operations(op.qubits[0], op.qubits[1], mat, allow_partial_czs=True, clean_operations=False) return NotImplemented
def matrix_to_sycamore_operations( target_qubits: List[cirq.GridQubit], matrix: np.ndarray) -> Tuple[cirq.OP_TREE, List[cirq.GridQubit]]: """A method to convert a unitary matrix to a list of Sycamore operations. This method will return a list of `cirq.Operation`s using the qubits and (optionally) ancilla qubits to implement the unitary matrix `matrix` on the target qubits `qubits`. The operations are also supported by `cirq.google.gate_sets.SYC_GATESET`. Args: target_qubits: list of qubits the returned operations will act on. The qubit order defined by the list is assumed to be used by the operations to implement `matrix`. matrix: a matrix that is guaranteed to be unitary and of size (2**len(qs), 2**len(qs)). Returns: A tuple of operations and ancilla qubits allocated. Operations: In case the matrix is supported, a list of operations `ops` is returned. `ops` acts on `qs` qubits and for which `cirq.unitary(ops)` is equal to `matrix` up to certain tolerance. In case the matrix is not supported, it might return NotImplemented to reduce the noise in the judge output. Ancilla qubits: In case ancilla qubits are allocated a list of ancilla qubits. Otherwise an empty list. . """ num_qubit = len(target_qubits) ops = [] if num_qubit == 1: ops = cirq.optimizers.single_qubit_matrix_to_gates(matrix) for i in range(len(ops)): ops[i] = ops[i].on(target_qubits[0]) elif num_qubit == 2: if np.all(matrix == cirq.unitary(cirq.google.SYC)): ops = [cirq.google.SYC(target_qubits[0], target_qubits[1])] else: ops = cirq.two_qubit_matrix_to_operations(target_qubits[0], target_qubits[1], matrix, allow_partial_czs=False) elif num_qubit == 3: if (matrix == np.diag([1, 1, 1, 1, 1, 1, 1, 1])).all(): ops = [] else: ops = cirq.three_qubit_matrix_to_operations( target_qubits[0], target_qubits[1], target_qubits[2], matrix) # print('target_qubit:', target_qubits) # print('matrix:', matrix, end='\n\n') # print('ops:') # for i in ops: # print(i) return ops, []
def generate_model_circuit( num_qubits: int, depth: int, *, random_state: Optional[np.random.RandomState] = None) -> cirq.Circuit: """Generates a model circuit with the given number of qubits and depth. The generated circuit consists of `depth` layers of random qubit permutations followed by random two-qubit gates that are sampled from the Haar measure on SU(4). Args: num_qubits: The number of qubits in the generated circuit. depth: The number of layers in the circuit. random_state: A way to seed the RandomState. Returns: The generated circuit. """ # Setup the circuit and its qubits. qubits = cirq.LineQubit.range(num_qubits) circuit = cirq.Circuit() if random_state is None: random_state = np.random # For each layer. for _ in range(depth): # Generate uniformly random permutation Pj of [0...n-1] perm = random_state.permutation(num_qubits) # For each consecutive pair in Pj, generate Haar random SU(4) # Decompose each SU(4) into CNOT + SU(2) and add to Ci for k in range(0, num_qubits - 1, 2): permuted_indices = [int(perm[k]), int(perm[k + 1])] special_unitary = cirq.testing.random_special_unitary( 4, random_state=random_state) # Convert the decomposed unitary to Cirq operations and add them to # the circuit. circuit.append( cirq.two_qubit_matrix_to_operations( qubits[permuted_indices[0]], qubits[permuted_indices[1]], special_unitary, allow_partial_czs=False)) # Don't measure all of the qubits at the end of the circuit because we will # need to classically simulate it to compute its heavy set. return circuit
def matrix_to_sycamore_operations( target_qubits: List[cirq.GridQubit], matrix: np.ndarray) -> Tuple[cirq.OP_TREE, List[cirq.GridQubit]]: """A method to convert a unitary matrix to a list of Sycamore operations. This method will return a list of `cirq.Operation`s using the qubits and (optionally) ancilla qubits to implement the unitary matrix `matrix` on the target qubits `qubits`. The operations are also supported by `cirq.google.gate_sets.SYC_GATESET`. Args: target_qubits: list of qubits the returned operations will act on. The qubit order defined by the list is assumed to be used by the operations to implement `matrix`. matrix: a matrix that is guaranteed to be unitary and of size (2**len(qs), 2**len(qs)). Returns: A tuple of operations and ancilla qubits allocated. Operations: In case the matrix is supported, a list of operations `ops` is returned. `ops` acts on `qs` qubits and for which `cirq.unitary(ops)` is equal to `matrix` up to certain tolerance. In case the matrix is not supported, it might return NotImplemented to reduce the noise in the judge output. Ancilla qubits: In case ancilla qubits are allocated a list of ancilla qubits. Otherwise an empty list. . """ ops = NotImplemented if len(target_qubits) == 1: ops = cirq.single_qubit_matrix_to_gates(matrix) for i, x in enumerate(ops): ops[i] = cirq.GateOperation(x, [target_qubits[0]]) elif len(target_qubits) == 2: ops = cirq.two_qubit_matrix_to_operations(target_qubits[0], target_qubits[1], matrix, allow_partial_czs=False, atol=1e-4) elif len(target_qubits) == 3: ops = cirq.three_qubit_matrix_to_operations(target_qubits[0], target_qubits[1], target_qubits[2], matrix, atol=1e-4) return ops, []
def test_two_qubit_matrix_to_operations_deprecated(): q0 = cirq.NamedQubit('q0') q1 = cirq.NamedQubit('q1') effect = np.array( [ [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0j], ] ) with cirq.testing.assert_deprecated('two_qubit_matrix_to_cz_operations', deadline='v0.15'): _ = cirq.two_qubit_matrix_to_operations(q0, q1, effect, True) with cirq.testing.assert_deprecated( 'two_qubit_matrix_to_diagonal_and_cz_operations', deadline='v0.15' ): _ = cirq.two_qubit_matrix_to_diagonal_and_operations(q0, q1, effect, True)
def _decompose_two_qubit(operation: cirq.Operation) -> cirq.OP_TREE: """Decomposes a two qubit unitary operation into ZPOW, XPOW, and CNOT.""" mat = cirq.unitary(operation) q0, q1 = operation.qubits naive = cirq.two_qubit_matrix_to_operations(q0, q1, mat, allow_partial_czs=False) temp = cirq.map_operations_and_unroll( cirq.Circuit(naive), lambda op, _: [cirq.H(op.qubits[1]), cirq.CNOT(*op.qubits), cirq.H(op.qubits[1])] if type(op.gate) == cirq.CZPowGate else op, ) temp = cirq.merge_single_qubit_gates_to_phased_x_and_z(temp) # A final pass breaks up PhasedXPow into Rz, Rx. yield cirq.map_operations_and_unroll( temp, lambda op, _: cirq.decompose_once(op) if type(op.gate) == cirq.PhasedXPowGate else op, ).all_operations()