def controlled_op_to_operations(control: ops.QubitId, target: ops.QubitId, operation: np.ndarray, tolerance: float = 0.0) -> List[ops.Operation]: """Decomposes a controlled single-qubit operation into Z/XY/CZ gates. Args: control: The control qubit. target: The qubit to apply an operation to, when the control is on. operation: The single-qubit operation being controlled. tolerance: A limit on the amount of error introduced by the construction. Returns: A list of Operations that apply the controlled operation. """ u, z_phase, global_phase = single_qubit_op_to_framed_phase_form(operation) if abs(z_phase - 1) <= tolerance: return [] u_gates = single_qubit_matrix_to_gates(u, tolerance) if u_gates and isinstance(u_gates[-1], ops.RotZGate): # Don't keep border operations that commute with CZ. del u_gates[-1] ops_before = [gate(target) for gate in u_gates] ops_after = ops.inverse(ops_before) effect = ops.CZ(control, target)**(cmath.phase(z_phase) / math.pi) kickback = ops.Z(control)**(cmath.phase(global_phase) / math.pi) return list( ops.flatten_op_tree( (ops_before, effect, kickback if abs(global_phase - 1) > tolerance else [], ops_after)))
def default_decompose(self) -> ops.OP_TREE: if len(self.pauli_string) <= 0: return qubits = self.qubits any_qubit = qubits[0] to_z_ops = ops.freeze_op_tree(self.pauli_string.to_z_basis_ops()) xor_decomp = tuple(xor_nonlocal_decompose(qubits, any_qubit)) yield to_z_ops yield xor_decomp if isinstance(self.half_turns, value.Symbol): if self.pauli_string.negated: yield ops.X(any_qubit) yield ops.RotZGate(half_turns=self.half_turns)(any_qubit) if self.pauli_string.negated: yield ops.X(any_qubit) else: half_turns = self.half_turns * (-1 if self.pauli_string.negated else 1) yield ops.Z(any_qubit)**half_turns yield ops.inverse(xor_decomp) yield ops.inverse(to_z_ops)
def default_decompose(self) -> ops.OP_TREE: if len(self.pauli_string) <= 0: return qubits = self.qubits any_qubit = qubits[0] to_z_ops = ops.freeze_op_tree(self.pauli_string.to_z_basis_ops()) xor_decomp = tuple(xor_nonlocal_decompose(qubits, any_qubit)) yield to_z_ops yield xor_decomp if isinstance(self.half_turns, value.Symbol): if self.pauli_string.negated: yield ops.X(any_qubit) yield ops.RotZGate(half_turns=self.half_turns)(any_qubit) if self.pauli_string.negated: yield ops.X(any_qubit) else: half_turns = self.half_turns * (-1 if self.pauli_string.negated else 1) yield ops.Z(any_qubit) ** half_turns yield ops.inverse(xor_decomp) yield ops.inverse(to_z_ops)
def verify_decomposition_inverse(*op_tree, max_test_value=2**100, depth=1): qubits = set() op_list = tuple(ops.flatten_op_tree(op_tree)) for op in op_list: qubits.update(op.qubits) qubits = sorted(qubits) num_qubits = len(qubits) sub_ops = tuple(ops.flatten_op_tree(decompose_depth(op_list, d=depth))) sub_ops_inv = tuple(ops.flatten_op_tree(decompose_depth(ops.inverse(op_list), d=depth))) for trits in itertools.product((0,1,2), repeat=num_qubits): state = trit_list_to_state(qubits, trits) state_orig = state.copy() for op in sub_ops: op.apply_to_ternary_state(state) for op in sub_ops_inv: op.apply_to_ternary_state(state) if state != state_orig: input_state_str = ''.join(map(str, trits)) output_state_str = ''.join(map(str, (state[q] for q in qubits))) raise RuntimeError( 'Gate {!r} inverse is invalid for input {} != {}'.format( op_list, input_state_str, output_state_str))
def controlled_op_to_operations( control: ops.QubitId, target: ops.QubitId, operation: np.ndarray, tolerance: float = 0.0) -> List[ops.Operation]: """Decomposes a controlled single-qubit operation into Z/XY/CZ gates. Args: control: The control qubit. target: The qubit to apply an operation to, when the control is on. operation: The single-qubit operation being controlled. tolerance: A limit on the amount of error introduced by the construction. Returns: A list of Operations that apply the controlled operation. """ u, z_phase, global_phase = single_qubit_op_to_framed_phase_form(operation) if abs(z_phase - 1) <= tolerance: return [] u_gates = single_qubit_matrix_to_gates(u, tolerance) if u_gates and isinstance(u_gates[-1], ops.RotZGate): # Don't keep border operations that commute with CZ. del u_gates[-1] ops_before = [gate(target) for gate in u_gates] ops_after = ops.inverse(ops_before) effect = ops.CZ(control, target) ** (cmath.phase(z_phase) / math.pi) kickback = ops.Z(control) ** (cmath.phase(global_phase) / math.pi) return list(ops.flatten_op_tree(( ops_before, effect, kickback if abs(global_phase - 1) > tolerance else [], ops_after)))