def _matrix_to_pauli_string_phasors(self, mat: np.ndarray, qubit: ops.Qid) -> ops.OP_TREE: rotations = optimizers.single_qubit_matrix_to_pauli_rotations( mat, self.atol) out_ops = [] # type: List[ops.Operation] for pauli, half_turns in rotations: if (self.keep_clifford and linalg.all_near_zero_mod(half_turns, 0.5)): cliff_gate = ops.SingleQubitCliffordGate.from_quarter_turns( pauli, round(half_turns * 2)) if out_ops and not isinstance(out_ops[-1], ops.PauliStringPhasor): op = cast(ops.GateOperation, out_ops[-1]) gate = cast(ops.SingleQubitCliffordGate, op.gate) out_ops[-1] = gate.merged_with(cliff_gate)(qubit) else: out_ops.append( cliff_gate(qubit)) else: pauli_string = ops.PauliString.from_single(qubit, pauli) out_ops.append( ops.PauliStringPhasor(pauli_string, exponent_neg=round(half_turns, 10))) return out_ops
def try_merge_clifford(cliff_op: ops.GateOperation, start_i: int) -> bool: (orig_qubit, ) = cliff_op.qubits remaining_cliff_gate = ops.SingleQubitCliffordGate.I for pauli, quarter_turns in reversed( cast(ops.SingleQubitCliffordGate, cliff_op.gate).decompose_rotation()): trans = remaining_cliff_gate.transform(pauli) pauli = trans.to quarter_turns *= -1 if trans.flip else 1 string_op = ops.PauliStringPhasor(ops.PauliString( pauli(cliff_op.qubits[0])), exponent_neg=quarter_turns / 2) merge_i, merge_op, num_passed = find_merge_point( start_i, string_op, quarter_turns == 2) assert merge_i > start_i assert len(merge_op.pauli_string) == 1, 'PauliString length != 1' qubit, pauli = next(iter(merge_op.pauli_string.items())) quarter_turns = round(merge_op.exponent_relative * 2) quarter_turns *= int(merge_op.pauli_string.coefficient.real) quarter_turns %= 4 part_cliff_gate = ops.SingleQubitCliffordGate.from_quarter_turns( pauli, quarter_turns) other_op = all_ops[merge_i] if merge_i < len(all_ops) else None if other_op is not None and qubit not in set(other_op.qubits): other_op = None if isinstance(other_op, ops.GateOperation) and isinstance( other_op.gate, ops.SingleQubitCliffordGate): # Merge with another SingleQubitCliffordGate new_op = part_cliff_gate.merged_with(other_op.gate)(qubit) all_ops[merge_i] = new_op elif (isinstance(other_op, ops.GateOperation) and isinstance(other_op.gate, ops.CZPowGate) and other_op.gate.exponent == 1 and quarter_turns == 2): # Pass whole Pauli gate over CZ, possibly adding a Z gate if pauli != ops.pauli_gates.Z: other_qubit = other_op.qubits[other_op.qubits.index(qubit) - 1] all_ops.insert(merge_i + 1, ops.SingleQubitCliffordGate.Z(other_qubit)) all_ops.insert(merge_i + 1, part_cliff_gate(qubit)) elif isinstance(other_op, ops.PauliStringPhasor): # Pass over a non-Clifford gate mod_op = other_op.pass_operations_over( [part_cliff_gate(qubit)]) all_ops[merge_i] = mod_op all_ops.insert(merge_i + 1, part_cliff_gate(qubit)) elif merge_i > start_i + 1 and num_passed > 0: # Moved Clifford through the circuit but nothing to merge all_ops.insert(merge_i, part_cliff_gate(qubit)) else: # Couldn't move Clifford remaining_cliff_gate = remaining_cliff_gate.merged_with( part_cliff_gate) if remaining_cliff_gate == ops.SingleQubitCliffordGate.I: all_ops.pop(start_i) return True all_ops[start_i] = remaining_cliff_gate(orig_qubit) return False
def try_merge_clifford(cliff_op: ops.GateOperation, start_i: int) -> bool: orig_qubit, = cliff_op.qubits remaining_cliff_gate = ops.SingleQubitCliffordGate.I for pauli, quarter_turns in reversed( cast(ops.SingleQubitCliffordGate, cliff_op.gate).decompose_rotation()): trans = remaining_cliff_gate.transform(pauli) pauli = trans.to quarter_turns *= -1 if trans.flip else 1 string_op = ops.PauliStringPhasor(ops.PauliString( pauli(cliff_op.qubits[0])), exponent_neg=quarter_turns / 2) merge_i, merge_op, num_passed = find_merge_point( start_i, string_op, quarter_turns == 2) assert merge_i > start_i assert len(merge_op.pauli_string) == 1, 'PauliString length != 1' qubit, pauli = next(iter(merge_op.pauli_string.items())) quarter_turns = round(merge_op.exponent_relative * 2) if merge_op.pauli_string.coefficient not in [1, -1]: # TODO: Add support for more general phases. # Github issue: https://github.com/quantumlib/Cirq/issues/2962 # Legacy coverage ignore, we need test code that hits this. # coverage: ignore raise NotImplementedError( 'Only +1/-1 pauli string coefficients currently supported') quarter_turns *= int(merge_op.pauli_string.coefficient.real) quarter_turns %= 4 part_cliff_gate = ops.SingleQubitCliffordGate.from_quarter_turns( pauli, quarter_turns) other_op = all_ops[merge_i] if merge_i < len(all_ops) else None if other_op is not None and qubit not in set(other_op.qubits): other_op = None if (isinstance(other_op, ops.GateOperation) and isinstance( other_op.gate, ops.SingleQubitCliffordGate)): # Merge with another SingleQubitCliffordGate new_op = part_cliff_gate.merged_with(other_op.gate)(qubit) all_ops[merge_i] = new_op elif (isinstance(other_op, ops.GateOperation) and isinstance(other_op.gate, ops.CZPowGate) and other_op.gate.exponent == 1 and quarter_turns == 2): # Pass whole Pauli gate over CZ, possibly adding a Z gate if pauli != ops.pauli_gates.Z: other_qubit = other_op.qubits[other_op.qubits.index(qubit) - 1] all_ops.insert(merge_i + 1, ops.SingleQubitCliffordGate.Z(other_qubit)) all_ops.insert(merge_i + 1, part_cliff_gate(qubit)) elif isinstance(other_op, ops.PauliStringPhasor): # Pass over a non-Clifford gate mod_op = other_op.pass_operations_over( [part_cliff_gate(qubit)]) all_ops[merge_i] = mod_op all_ops.insert(merge_i + 1, part_cliff_gate(qubit)) elif merge_i > start_i + 1 and num_passed > 0: # Moved Clifford through the circuit but nothing to merge all_ops.insert(merge_i, part_cliff_gate(qubit)) else: # Couldn't move Clifford remaining_cliff_gate = remaining_cliff_gate.merged_with( part_cliff_gate) if remaining_cliff_gate == ops.SingleQubitCliffordGate.I: all_ops.pop(start_i) return True all_ops[start_i] = remaining_cliff_gate(orig_qubit) return False