1# Copyright 2018 The Cirq Developers 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15from typing import List, Optional, cast, TYPE_CHECKING 16 17import numpy as np 18 19from cirq import ops, optimizers, protocols, linalg 20from cirq.circuits.circuit import Circuit 21from cirq.circuits.optimization_pass import ( 22 PointOptimizationSummary, 23 PointOptimizer, 24) 25 26if TYPE_CHECKING: 27 import cirq 28 29 30class ConvertToPauliStringPhasors(PointOptimizer): 31 """Attempts to convert single-qubit gates into single-qubit 32 PauliStringPhasor operations. 33 34 Checks if the operation has a known unitary effect. If so, and the gate is a 35 1-qubit gate, then decomposes it into x, y, or z rotations and creates a 36 PauliStringPhasor for each. 37 """ 38 39 def __init__( 40 self, ignore_failures: bool = False, keep_clifford: bool = False, atol: float = 1e-14 41 ) -> None: 42 """Inits ConvertToPauliStringPhasors. 43 44 Args: 45 ignore_failures: If set, gates that fail to convert are forwarded 46 unchanged. If not set, conversion failures raise a TypeError. 47 keep_clifford: If set, single qubit rotations in the Clifford group 48 are converted to SingleQubitCliffordGates. 49 atol: Maximum absolute error tolerance. The optimization is 50 permitted to round angles with a threshold determined by this 51 tolerance. 52 """ 53 super().__init__() 54 self.ignore_failures = ignore_failures 55 self.keep_clifford = keep_clifford 56 self.atol = atol 57 58 def _matrix_to_pauli_string_phasors(self, mat: np.ndarray, qubit: 'cirq.Qid') -> ops.OP_TREE: 59 rotations = optimizers.single_qubit_matrix_to_pauli_rotations(mat, self.atol) 60 out_ops: List[ops.Operation] = [] 61 for pauli, half_turns in rotations: 62 if self.keep_clifford and linalg.all_near_zero_mod(half_turns, 0.5): 63 cliff_gate = ops.SingleQubitCliffordGate.from_quarter_turns( 64 pauli, round(half_turns * 2) 65 ) 66 if out_ops and not isinstance(out_ops[-1], ops.PauliStringPhasor): 67 op = cast(ops.GateOperation, out_ops[-1]) 68 gate = cast(ops.SingleQubitCliffordGate, op.gate) 69 out_ops[-1] = gate.merged_with(cliff_gate)(qubit) 70 else: 71 out_ops.append(cliff_gate(qubit)) 72 else: 73 out_ops.append( 74 ops.PauliStringPhasor( 75 ops.PauliString(pauli.on(qubit)), exponent_neg=round(half_turns, 10) 76 ) 77 ) 78 return out_ops 79 80 def _convert_one(self, op: ops.Operation) -> ops.OP_TREE: 81 # Don't change if it's already a ops.PauliStringPhasor 82 if isinstance(op, ops.PauliStringPhasor): 83 return op 84 85 if ( 86 self.keep_clifford 87 and isinstance(op, ops.GateOperation) 88 and isinstance(op.gate, ops.SingleQubitCliffordGate) 89 ): 90 return op 91 92 # Single qubit gate with known matrix? 93 if len(op.qubits) == 1: 94 mat = protocols.unitary(op, None) 95 if mat is not None: 96 return self._matrix_to_pauli_string_phasors(mat, op.qubits[0]) 97 98 # Just let it be? 99 if self.ignore_failures: 100 return op 101 102 raise TypeError( 103 "Don't know how to work with {!r}. " 104 "It isn't a 1-qubit operation with a known unitary " 105 "effect.".format(op) 106 ) 107 108 def convert(self, op: ops.Operation) -> ops.OP_TREE: 109 converted = self._convert_one(op) 110 if converted is op: 111 return converted 112 return [self.convert(cast(ops.Operation, e)) for e in ops.flatten_op_tree(converted)] 113 114 def optimization_at( 115 self, circuit: Circuit, index: int, op: ops.Operation 116 ) -> Optional[PointOptimizationSummary]: 117 converted = self.convert(op) 118 if converted is op: 119 return None 120 121 return PointOptimizationSummary( 122 clear_span=1, new_operations=converted, clear_qubits=op.qubits 123 ) 124