Skip to content

Move to method dispatch using act_on for CliffordState, Tableau and ChForm #3456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 29, 2020
Merged
20 changes: 10 additions & 10 deletions cirq/sim/clifford/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
from cirq.sim.clifford.clifford_simulator import (
CliffordSimulator,
CliffordState,
CliffordTrialResult,
CliffordSimulatorStepResult,
)

from cirq.sim.clifford.clifford_tableau import (
CliffordTableau,)

Expand All @@ -14,8 +7,15 @@
from cirq.sim.clifford.act_on_stabilizer_ch_form_args import (
ActOnStabilizerCHFormArgs,)

from cirq.sim.clifford.stabilizer_sampler import (
StabilizerSampler,)

from cirq.sim.clifford.stabilizer_state_ch_form import (
StabilizerStateChForm,)

from cirq.sim.clifford.clifford_simulator import (
CliffordSimulator,
CliffordState,
CliffordTrialResult,
CliffordSimulatorStepResult,
)

from cirq.sim.clifford.stabilizer_sampler import (
StabilizerSampler,)
107 changes: 14 additions & 93 deletions cirq/sim/clifford/clifford_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,10 @@

import cirq
from cirq import circuits, study, ops, protocols, value
from cirq.ops import pauli_gates
from cirq.ops.clifford_gate import SingleQubitCliffordGate
from cirq.ops.dense_pauli_string import DensePauliString
from cirq.protocols import unitary
from cirq.sim import simulator
from cirq.sim.clifford import clifford_tableau, stabilizer_state_ch_form
from cirq.protocols import act_on, unitary
from cirq.sim import clifford, simulator
from cirq._compat import deprecated, deprecated_parameter


Expand Down Expand Up @@ -277,9 +275,8 @@ def __init__(self, qubit_map, initial_state=0):
self.qubit_map = qubit_map
self.n = len(qubit_map)

self.tableau = clifford_tableau.CliffordTableau(self.n, initial_state)
self.ch_form = stabilizer_state_ch_form.StabilizerStateChForm(
self.n, initial_state)
self.tableau = clifford.CliffordTableau(self.n, initial_state)
self.ch_form = clifford.StabilizerStateChForm(self.n, initial_state)

def _json_dict_(self):
return {
Expand Down Expand Up @@ -336,94 +333,18 @@ def wave_function(self):
return self.state_vector()

def apply_unitary(self, op: 'cirq.Operation'):
if len(op.qubits) == 1:
self.apply_single_qubit_unitary(op)
elif isinstance(op, GlobalPhaseOperation):
self.ch_form.omega *= op.coefficient
elif op.gate == cirq.CNOT:
self.tableau._CNOT(self.qubit_map[op.qubits[0]],
self.qubit_map[op.qubits[1]])
self.ch_form._CNOT(self.qubit_map[op.qubits[0]],
self.qubit_map[op.qubits[1]])
elif op.gate == cirq.CZ:
self.tableau._CZ(self.qubit_map[op.qubits[0]],
self.qubit_map[op.qubits[1]])
self.ch_form._CZ(self.qubit_map[op.qubits[0]],
self.qubit_map[op.qubits[1]])
else:
tableau_args = clifford.ActOnCliffordTableauArgs(
self.tableau, [self.qubit_map[i] for i in op.qubits],
np.random.RandomState(), {})
ch_form_args = clifford.ActOnStabilizerCHFormArgs(
self.ch_form, [self.qubit_map[i] for i in op.qubits])
try:
act_on(op, tableau_args)
act_on(op, ch_form_args)
except TypeError:
raise ValueError('%s cannot be run with Clifford simulator.' %
str(op.gate)) # type: ignore

def apply_single_qubit_unitary(self, op: 'cirq.Operation'):
qubit = self.qubit_map[op.qubits[0]]
if op.gate == cirq.I:
return

if op.gate == cirq.X:
self._apply_X(qubit)
return

if op.gate == cirq.Y:
self._apply_Y(qubit)
return

if op.gate == cirq.Z:
self._apply_Z(qubit)
return

if op.gate == cirq.H:
self._apply_H(qubit)
return

u = unitary(op)
clifford_gate = SingleQubitCliffordGate.from_unitary(u)
if clifford_gate is None:
raise ValueError('%s cannot be run with Clifford simulator.' %
str(op.gate))

h = unitary(ops.H)
s = unitary(ops.S)
applied_unitary = np.eye(2)
for axis, quarter_turns in clifford_gate.decompose_rotation():
for _ in range(quarter_turns % 4):
if axis == pauli_gates.X:
self._apply_H(qubit)
self._apply_S(qubit)
self._apply_H(qubit)
applied_unitary = h @ s @ h @ applied_unitary
elif axis == pauli_gates.Y:
self._apply_S(qubit)
self._apply_S(qubit)
self._apply_H(qubit)
applied_unitary = h @ s @ s @ applied_unitary
else:
assert axis == pauli_gates.Z
self._apply_S(qubit)
applied_unitary = s @ applied_unitary

max_idx = max(np.ndindex(*u.shape), key=lambda t: abs(u[t]))
phase_shift = u[max_idx] / applied_unitary[max_idx]
self.ch_form.omega *= phase_shift

def _apply_H(self, qubit: int):
self.tableau._H(qubit)
self.ch_form._H(qubit)

def _apply_S(self, qubit: int):
self.tableau._S(qubit)
self.ch_form._S(qubit)

def _apply_X(self, qubit: int):
self.tableau._X(qubit)
self.ch_form._X(qubit)

def _apply_Z(self, qubit: int):
self.tableau._Z(qubit)
self.ch_form._Z(qubit)

def _apply_Y(self, qubit: int):
self.tableau._Y(qubit)
self.ch_form._Y(qubit)
return

@deprecated_parameter(
deadline='v0.10.0',
Expand Down
29 changes: 0 additions & 29 deletions cirq/sim/clifford/clifford_tableau.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,35 +136,6 @@ def _str_full_(self) -> str:

return string

def _CZ(self, q, r):
self._H(r)
self._CNOT(q, r)
self._H(r)

def _X(self, q):
self.rs[:] ^= self.zs[:, q]

def _Y(self, q):
self.rs[:] ^= self.xs[:, q] ^ self.zs[:, q]

def _Z(self, q):
self.rs[:] ^= self.xs[:, q]

def _S(self, q):
self.rs[:] ^= (self.xs[:, q] & self.zs[:, q])
self.zs[:, q] ^= self.xs[:, q]

def _H(self, q):
(self.xs[:, q], self.zs[:, q]) = (self.zs[:, q].copy(),
self.xs[:, q].copy())
self.rs[:] ^= (self.xs[:, q] & self.zs[:, q])

def _CNOT(self, q1, q2):
self.rs[:] ^= self.xs[:,q1] & self.zs[:,q2] & \
(~(self.xs[:,q2] ^ self.zs[:,q1]))
self.xs[:, q2] ^= self.xs[:, q1]
self.zs[:, q1] ^= self.zs[:, q2]

def _rowsum(self, q1, q2):
"""Implements the "rowsum" routine defined by
Aaronson and Gottesman.
Expand Down
48 changes: 4 additions & 44 deletions cirq/sim/clifford/stabilizer_state_ch_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import cirq
from cirq import protocols, value
from cirq.ops import pauli_gates
from cirq.sim import clifford
from cirq.value import big_endian_int_to_digits
from cirq._compat import deprecated

Expand Down Expand Up @@ -60,7 +62,8 @@ def __init__(self, num_qubits: int, initial_state: int = 0) -> None:
digit_count=num_qubits,
base=2)):
if val:
self._X(i)
protocols.act_on(pauli_gates.X,
clifford.ActOnStabilizerCHFormArgs(self, [i]))

def _json_dict_(self) -> Dict[str, Any]:
return protocols.obj_to_dict_helper(
Expand Down Expand Up @@ -133,66 +136,23 @@ def state_vector(self) -> np.ndarray:
def wave_function(self) -> np.ndarray:
return self.state_vector()

def _S(self, q):
self.M[q, :] ^= self.G[q, :]
self.gamma[q] = (self.gamma[q] - 1) % 4

def _S_right(self, q):
r"""Right multiplication version of S gate."""
self.M[:, q] ^= self.F[:, q]
self.gamma[:] = (self.gamma[:] - self.F[:, q]) % 4

def _Z(self, q):
self._S(q)
self._S(q)

def _X(self, q):
self._H(q)
self._Z(q)
self._H(q)

def _Y(self, q):
self._Z(q)
self._X(q)
self.omega *= 1j

def _CZ(self, q, r):
self.M[q, :] ^= self.G[r, :]
self.M[r, :] ^= self.G[q, :]

def _CZ_right(self, q, r):
r"""Right multiplication version of CZ gate."""
self.M[:, q] ^= self.F[:, r]
self.M[:, r] ^= self.F[:, q]
self.gamma[:] = (self.gamma[:] + 2 * self.F[:, q] * self.F[:, r]) % 4

def _CNOT(self, q, r):
self.gamma[q] = (self.gamma[q] + self.gamma[r] + 2 *
(sum(self.M[q, :] & self.F[r, :]) % 2)) % 4
self.G[r, :] ^= self.G[q, :]
self.F[q, :] ^= self.F[r, :]
self.M[q, :] ^= self.M[r, :]

def _CNOT_right(self, q, r):
r"""Right multiplication version of CNOT gate."""
self.G[:, q] ^= self.G[:, r]
self.F[:, r] ^= self.F[:, q]
self.M[:, q] ^= self.M[:, r]

def _H(self, p):
t = self.s ^ (self.G[p, :] & self.v)
u = self.s ^ (self.F[p, :] & (~self.v)) ^ (self.M[p, :] & self.v)

alpha = sum(self.G[p, :] & (~self.v) & self.s) % 2
beta = sum(self.M[p, :] & (~self.v) & self.s)
beta += sum(self.F[p, :] & self.v & self.M[p, :])
beta += sum(self.F[p, :] & self.v & self.s)
beta %= 2

delta = (self.gamma[p] + 2 * (alpha + beta)) % 4

self.update_sum(t, u, delta=delta, alpha=alpha)

def update_sum(self, t, u, delta=0, alpha=0):
""" Implements the transformation (Proposition 4 in Bravyi et al)

Expand Down