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"""A protocol for implementing high performance clifford tableau evolutions 15 for Clifford Simulator.""" 16 17from typing import Any, Dict, TYPE_CHECKING, List, Sequence, Iterable, Union 18 19import numpy as np 20 21from cirq.ops import common_gates 22from cirq.ops import pauli_gates 23from cirq.ops.clifford_gate import SingleQubitCliffordGate 24from cirq.protocols import has_unitary, num_qubits, unitary 25from cirq.qis.clifford_tableau import CliffordTableau 26from cirq.sim.act_on_args import ActOnArgs 27from cirq.type_workarounds import NotImplementedType 28 29if TYPE_CHECKING: 30 import cirq 31 32 33class ActOnCliffordTableauArgs(ActOnArgs): 34 """State and context for an operation acting on a clifford tableau. 35 36 To act on this object, directly edit the `tableau` property, which is 37 storing the density matrix of the quantum system with one axis per qubit. 38 """ 39 40 def __init__( 41 self, 42 tableau: CliffordTableau, 43 prng: np.random.RandomState, 44 log_of_measurement_results: Dict[str, Any], 45 qubits: Sequence['cirq.Qid'] = None, 46 ): 47 """Inits ActOnCliffordTableauArgs. 48 49 Args: 50 tableau: The CliffordTableau to act on. Operations are expected to 51 perform inplace edits of this object. 52 qubits: Determines the canonical ordering of the qubits. This 53 is often used in specifying the initial state, i.e. the 54 ordering of the computational basis states. 55 prng: The pseudo random number generator to use for probabilistic 56 effects. 57 log_of_measurement_results: A mutable object that measurements are 58 being recorded into. 59 """ 60 super().__init__(prng, qubits, log_of_measurement_results) 61 self.tableau = tableau 62 63 def _act_on_fallback_( 64 self, 65 action: Union['cirq.Operation', 'cirq.Gate'], 66 qubits: Sequence['cirq.Qid'], 67 allow_decompose: bool = True, 68 ) -> Union[bool, NotImplementedType]: 69 strats = [] 70 if allow_decompose: 71 strats.append(_strat_act_on_clifford_tableau_from_single_qubit_decompose) 72 for strat in strats: 73 result = strat(action, self, qubits) 74 if result is False: 75 break # coverage: ignore 76 if result is True: 77 return True 78 assert result is NotImplemented, str(result) 79 80 return NotImplemented 81 82 def _perform_measurement(self, qubits: Sequence['cirq.Qid']) -> List[int]: 83 """Returns the measurement from the tableau.""" 84 return [self.tableau._measure(self.qubit_map[q], self.prng) for q in qubits] 85 86 def _on_copy(self, target: 'ActOnCliffordTableauArgs'): 87 target.tableau = self.tableau.copy() 88 89 def sample( 90 self, 91 qubits: Sequence['cirq.Qid'], 92 repetitions: int = 1, 93 seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, 94 ) -> np.ndarray: 95 # Unnecessary for now but can be added later if there is a use case. 96 raise NotImplementedError() 97 98 99def _strat_act_on_clifford_tableau_from_single_qubit_decompose( 100 val: Any, args: 'cirq.ActOnCliffordTableauArgs', qubits: Sequence['cirq.Qid'] 101) -> bool: 102 if num_qubits(val) == 1: 103 if not has_unitary(val): 104 return NotImplemented 105 u = unitary(val) 106 clifford_gate = SingleQubitCliffordGate.from_unitary(u) 107 if clifford_gate is not None: 108 for axis, quarter_turns in clifford_gate.decompose_rotation(): 109 if axis == pauli_gates.X: 110 common_gates.XPowGate(exponent=quarter_turns / 2)._act_on_(args, qubits) 111 elif axis == pauli_gates.Y: 112 common_gates.YPowGate(exponent=quarter_turns / 2)._act_on_(args, qubits) 113 else: 114 assert axis == pauli_gates.Z 115 common_gates.ZPowGate(exponent=quarter_turns / 2)._act_on_(args, qubits) 116 return True 117 118 return NotImplemented 119