Skip to content
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

Fix act-on specialization for all the Clifford simulators #4748

Merged
merged 50 commits into from
Jan 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
ea95c98
Change act_on to apply_to
daxfohl Nov 11, 2021
8dd48ae
revert global phase
daxfohl Nov 11, 2021
992c4f6
revert random gate
daxfohl Nov 12, 2021
d4366ea
docs
daxfohl Nov 12, 2021
4f768e8
Change act-on calls to apply where possible
daxfohl Nov 12, 2021
d6dc947
tests
daxfohl Nov 12, 2021
2ad141f
tests
daxfohl Nov 12, 2021
7f13f4d
tests
daxfohl Nov 12, 2021
14ad01a
imports
daxfohl Nov 12, 2021
208e9d3
tests
daxfohl Nov 12, 2021
7012fff
coverage
daxfohl Nov 12, 2021
d3abe6d
basic paulis
daxfohl Nov 19, 2021
d6b62c9
cz
daxfohl Nov 19, 2021
d1c7338
cx
daxfohl Nov 19, 2021
91a904b
swap
daxfohl Nov 19, 2021
bf13385
fix
daxfohl Nov 19, 2021
1417b14
remove apply_to_tableau
daxfohl Nov 19, 2021
6d142f9
ch
daxfohl Nov 19, 2021
5a513dd
ch
daxfohl Nov 19, 2021
3473a4c
remove apply_to_ch
daxfohl Nov 20, 2021
9039a1e
have both simulators accept all common gates
daxfohl Nov 21, 2021
2a800cb
remove prng in most cases
daxfohl Nov 21, 2021
82d13d1
fix swap phase
daxfohl Nov 21, 2021
5d54724
decompose
daxfohl Nov 22, 2021
10ea235
dead code
daxfohl Nov 22, 2021
077c019
dead code
daxfohl Nov 22, 2021
c82184e
mostly working
daxfohl Nov 22, 2021
2e2bbc8
almost
daxfohl Nov 22, 2021
955c8fb
working
daxfohl Nov 22, 2021
c0f4f40
format
daxfohl Nov 22, 2021
54245f1
random gate
daxfohl Nov 22, 2021
d78dc4d
random gate
daxfohl Nov 22, 2021
18db225
Merge branch 'master' into cliffordmerge
daxfohl Dec 13, 2021
fa9d083
global phase
daxfohl Dec 13, 2021
4de415d
improve mixture handling
daxfohl Dec 13, 2021
2c2de8e
Fix nonunitary mixtures
daxfohl Dec 14, 2021
153e742
Merge branch 'master' into clifford4
daxfohl Dec 14, 2021
ccdb002
fix swap_gates_test imports
daxfohl Dec 14, 2021
3cf04a1
Merge remote-tracking branch 'origin/clifford4' into clifford4
daxfohl Dec 14, 2021
2408489
abstract
daxfohl Dec 19, 2021
6a68460
small fixes
daxfohl Dec 20, 2021
dbaeeae
Merge branch 'master' into cliffordm
daxfohl Dec 20, 2021
e53ae43
lint
daxfohl Dec 20, 2021
3e2f94b
Merge branch 'master' into clifford4
daxfohl Dec 21, 2021
0c78fce
Merge branch 'master' into clifford4
daxfohl Dec 28, 2021
9ebe2a1
Put decompose strategy in allow_decompose branch
daxfohl Jan 12, 2022
3812527
Merge branch 'master' into cliffordm
daxfohl Jan 12, 2022
f162317
Change asserts to exceptions, rename control/target axes
daxfohl Jan 15, 2022
6bbe681
Merge branch 'master' into clifford4
daxfohl Jan 15, 2022
0795ef2
Merge branch 'master' into clifford4
CirqBot Jan 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cirq-core/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@
ActOnCliffordTableauArgs,
ActOnDensityMatrixArgs,
ActOnStabilizerCHFormArgs,
ActOnStabilizerArgs,
ActOnStateVectorArgs,
StabilizerStateChForm,
CIRCUIT_LIKE,
Expand Down
10 changes: 0 additions & 10 deletions cirq-core/cirq/ops/common_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,16 +316,6 @@ def __str__(self) -> str:
return f"depolarize(p={self._p})"
return f"depolarize(p={self._p},n_qubits={self._n_qubits})"

def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']) -> bool:
from cirq.sim import clifford

if isinstance(args, clifford.ActOnCliffordTableauArgs):
if args.prng.random() < self._p:
gate = args.prng.choice([pauli_gates.X, pauli_gates.Y, pauli_gates.Z])
protocols.act_on(gate, args, qubits)
return True
return NotImplemented

def _circuit_diagram_info_(self, args: 'protocols.CircuitDiagramInfoArgs') -> Tuple[str, ...]:
result: Tuple[str, ...]
if args.precision is not None:
Expand Down
247 changes: 0 additions & 247 deletions cirq-core/cirq/ops/common_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@
"""


def _act_with_gates(args, qubits, *gates: 'cirq.SupportsActOnQubits') -> None:
"""Act on the given args with the given gates in order."""
for gate in gates:
assert gate._act_on_(args, qubits)


def _pi(rads):
return sympy.pi if protocols.is_parameterized(rads) else np.pi

Expand Down Expand Up @@ -108,35 +102,6 @@ def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs') -> Optional[np.nda
args.available_buffer *= p
return args.available_buffer

def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']):
from cirq.sim import clifford

if isinstance(args, clifford.ActOnCliffordTableauArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
tableau = args.tableau
q = args.qubit_map[qubits[0]]
effective_exponent = self._exponent % 2
if effective_exponent == 0.5:
tableau.xs[:, q] ^= tableau.zs[:, q]
tableau.rs[:] ^= tableau.xs[:, q] & tableau.zs[:, q]
elif effective_exponent == 1:
tableau.rs[:] ^= tableau.zs[:, q]
elif effective_exponent == 1.5:
tableau.rs[:] ^= tableau.xs[:, q] & tableau.zs[:, q]
tableau.xs[:, q] ^= tableau.zs[:, q]
return True

if isinstance(args, clifford.ActOnStabilizerCHFormArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
_act_with_gates(args, qubits, H, ZPowGate(exponent=self._exponent), H)
# Adjust the global phase based on the global_shift parameter.
args.state.omega *= np.exp(1j * np.pi * self.global_shift * self.exponent)
return True

return NotImplemented

def in_su2(self) -> 'Rx':
"""Returns an equal-up-global-phase gate from the group SU2."""
return Rx(rads=self._exponent * _pi(self._exponent))
Expand Down Expand Up @@ -362,51 +327,6 @@ def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs') -> Optional[np.nda
args.available_buffer *= p
return args.available_buffer

def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']):
from cirq.sim import clifford

if isinstance(args, clifford.ActOnCliffordTableauArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
tableau = args.tableau
q = args.qubit_map[qubits[0]]
effective_exponent = self._exponent % 2
if effective_exponent == 0.5:
tableau.rs[:] ^= tableau.xs[:, q] & (~tableau.zs[:, q])
(tableau.xs[:, q], tableau.zs[:, q]) = (
tableau.zs[:, q].copy(),
tableau.xs[:, q].copy(),
)
elif effective_exponent == 1:
tableau.rs[:] ^= tableau.xs[:, q] ^ tableau.zs[:, q]
elif effective_exponent == 1.5:
tableau.rs[:] ^= ~(tableau.xs[:, q]) & tableau.zs[:, q]
(tableau.xs[:, q], tableau.zs[:, q]) = (
tableau.zs[:, q].copy(),
tableau.xs[:, q].copy(),
)
return True

if isinstance(args, clifford.ActOnStabilizerCHFormArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
effective_exponent = self._exponent % 2
state = args.state
Z = ZPowGate()
if effective_exponent == 0.5:
_act_with_gates(args, qubits, Z, H)
state.omega *= (1 + 1j) / (2 ** 0.5)
elif effective_exponent == 1:
_act_with_gates(args, qubits, Z, H, Z, H)
state.omega *= 1j
elif effective_exponent == 1.5:
_act_with_gates(args, qubits, H, Z)
state.omega *= (1 - 1j) / (2 ** 0.5)
# Adjust the global phase based on the global_shift parameter.
args.state.omega *= np.exp(1j * np.pi * self.global_shift * self.exponent)
return True
return NotImplemented

def in_su2(self) -> 'Ry':
"""Returns an equal-up-global-phase gate from the group SU2."""
return Ry(rads=self._exponent * _pi(self._exponent))
Expand Down Expand Up @@ -580,42 +500,6 @@ def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs') -> Optional[np.nda
args.target_tensor *= p
return args.target_tensor

def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']):
from cirq.sim import clifford

if isinstance(args, clifford.ActOnCliffordTableauArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
tableau = args.tableau
q = args.qubit_map[qubits[0]]
effective_exponent = self._exponent % 2
if effective_exponent == 0.5:
tableau.rs[:] ^= tableau.xs[:, q] & tableau.zs[:, q]
tableau.zs[:, q] ^= tableau.xs[:, q]
elif effective_exponent == 1:
tableau.rs[:] ^= tableau.xs[:, q]
elif effective_exponent == 1.5:
tableau.rs[:] ^= tableau.xs[:, q] & (~tableau.zs[:, q])
tableau.zs[:, q] ^= tableau.xs[:, q]
return True

if isinstance(args, clifford.ActOnStabilizerCHFormArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
q = args.qubit_map[qubits[0]]
effective_exponent = self._exponent % 2
state = args.state
for _ in range(int(effective_exponent * 2)):
# Prescription for S left multiplication.
# Reference: https://arxiv.org/abs/1808.00128 Proposition 4 end
state.M[q, :] ^= state.G[q, :]
state.gamma[q] = (state.gamma[q] - 1) % 4
# Adjust the global phase based on the global_shift parameter.
args.state.omega *= np.exp(1j * np.pi * self.global_shift * self.exponent)
return True

return NotImplemented

def _decompose_into_clifford_with_qubits_(self, qubits):
from cirq.ops.clifford_gate import SingleQubitCliffordGate

Expand Down Expand Up @@ -899,49 +783,6 @@ def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs') -> Optional[np.nda
args.target_tensor *= np.sqrt(2) * p
return args.target_tensor

def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']):
from cirq.sim import clifford

if isinstance(args, clifford.ActOnCliffordTableauArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
tableau = args.tableau
q = args.qubit_map[qubits[0]]
if self._exponent % 2 == 1:
(tableau.xs[:, q], tableau.zs[:, q]) = (
tableau.zs[:, q].copy(),
tableau.xs[:, q].copy(),
)
tableau.rs[:] ^= tableau.xs[:, q] & tableau.zs[:, q]
return True

if isinstance(args, clifford.ActOnStabilizerCHFormArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
q = args.qubit_map[qubits[0]]
state = args.state
if self._exponent % 2 == 1:
# Prescription for H left multiplication
# Reference: https://arxiv.org/abs/1808.00128
# Equations 48, 49 and Proposition 4
t = state.s ^ (state.G[q, :] & state.v)
u = state.s ^ (state.F[q, :] & (~state.v)) ^ (state.M[q, :] & state.v)

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

delta = (state.gamma[q] + 2 * (alpha + beta)) % 4

state.update_sum(t, u, delta=delta, alpha=alpha)
# Adjust the global phase based on the global_shift parameter.
args.state.omega *= np.exp(1j * np.pi * self.global_shift * self.exponent)
return True

return NotImplemented

def _decompose_(self, qubits):
q = qubits[0]

Expand Down Expand Up @@ -1063,52 +904,6 @@ def _apply_unitary_(
args.target_tensor *= p
return args.target_tensor

def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']):
from cirq.sim import clifford

if isinstance(args, clifford.ActOnCliffordTableauArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
tableau = args.tableau
q1 = args.qubit_map[qubits[0]]
q2 = args.qubit_map[qubits[1]]
if self._exponent % 2 == 1:
(tableau.xs[:, q2], tableau.zs[:, q2]) = (
tableau.zs[:, q2].copy(),
tableau.xs[:, q2].copy(),
)
tableau.rs[:] ^= tableau.xs[:, q2] & tableau.zs[:, q2]
tableau.rs[:] ^= (
tableau.xs[:, q1]
& tableau.zs[:, q2]
& (~(tableau.xs[:, q2] ^ tableau.zs[:, q1]))
)
tableau.xs[:, q2] ^= tableau.xs[:, q1]
tableau.zs[:, q1] ^= tableau.zs[:, q2]
(tableau.xs[:, q2], tableau.zs[:, q2]) = (
tableau.zs[:, q2].copy(),
tableau.xs[:, q2].copy(),
)
tableau.rs[:] ^= tableau.xs[:, q2] & tableau.zs[:, q2]
return True

if isinstance(args, clifford.ActOnStabilizerCHFormArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
q1 = args.qubit_map[qubits[0]]
q2 = args.qubit_map[qubits[1]]
state = args.state
if self._exponent % 2 == 1:
# Prescription for CZ left multiplication.
# Reference: https://arxiv.org/abs/1808.00128 Proposition 4 end
state.M[q1, :] ^= state.G[q2, :]
state.M[q2, :] ^= state.G[q1, :]
# Adjust the global phase based on the global_shift parameter.
args.state.omega *= np.exp(1j * np.pi * self.global_shift * self.exponent)
return True

return NotImplemented

def _pauli_expansion_(self) -> value.LinearDict[str]:
if protocols.is_parameterized(self):
return NotImplemented
Expand Down Expand Up @@ -1291,48 +1086,6 @@ def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs') -> Optional[np.nda
args.target_tensor *= p
return args.target_tensor

def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']):
from cirq.sim import clifford

if isinstance(args, clifford.ActOnCliffordTableauArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
tableau = args.tableau
q1 = args.qubit_map[qubits[0]]
q2 = args.qubit_map[qubits[1]]
if self._exponent % 2 == 1:
tableau.rs[:] ^= (
tableau.xs[:, q1]
& tableau.zs[:, q2]
& (~(tableau.xs[:, q2] ^ tableau.zs[:, q1]))
)
tableau.xs[:, q2] ^= tableau.xs[:, q1]
tableau.zs[:, q1] ^= tableau.zs[:, q2]
return True

if isinstance(args, clifford.ActOnStabilizerCHFormArgs):
if not protocols.has_stabilizer_effect(self):
return NotImplemented
q1 = args.qubit_map[qubits[0]]
q2 = args.qubit_map[qubits[1]]
state = args.state
if self._exponent % 2 == 1:
# Prescription for CX left multiplication.
# Reference: https://arxiv.org/abs/1808.00128 Proposition 4 end
state.gamma[q1] = (
state.gamma[q1]
+ state.gamma[q2]
+ 2 * (sum(state.M[q1, :] & state.F[q2, :]) % 2)
) % 4
state.G[q2, :] ^= state.G[q1, :]
state.F[q1, :] ^= state.F[q2, :]
state.M[q1, :] ^= state.M[q2, :]
# Adjust the global phase based on the global_shift parameter.
args.state.omega *= np.exp(1j * np.pi * self.global_shift * self.exponent)
return True

return NotImplemented

def _pauli_expansion_(self) -> value.LinearDict[str]:
if protocols.is_parameterized(self):
return NotImplemented
Expand Down
14 changes: 0 additions & 14 deletions cirq-core/cirq/ops/global_phase_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,20 +87,6 @@ def _apply_unitary_(self, args) -> np.ndarray:
def _has_stabilizer_effect_(self) -> bool:
return True

def _act_on_(self, args: 'cirq.ActOnArgs', qubits):
from cirq.sim import clifford

if isinstance(args, clifford.ActOnCliffordTableauArgs):
# Since CliffordTableau does not keep track of the global phase,
# it's safe to just ignore it here.
return True

if isinstance(args, clifford.ActOnStabilizerCHFormArgs):
args.state.omega *= self.coefficient
return True

return NotImplemented

def __str__(self) -> str:
return str(self.coefficient)

Expand Down
15 changes: 0 additions & 15 deletions cirq-core/cirq/ops/random_gate_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
cast,
SupportsFloat,
Optional,
Sequence,
)

import numpy as np
Expand Down Expand Up @@ -123,20 +122,6 @@ def _trace_distance_bound_(self) -> float:
result *= float(self.probability)
return result

def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']):
from cirq.sim import clifford

if self._is_parameterized_():
return NotImplemented
if isinstance(args, clifford.ActOnCliffordTableauArgs):
if args.prng.random() < self.probability:
# Note: because we're doing this probabilistically, it's not
# safe to fallback to other strategies if act_on fails. Those
# strategies could double-count the probability.
protocols.act_on(self.sub_gate, args, qubits)
return True
return NotImplemented

def _json_dict_(self) -> Dict[str, Any]:
return protocols.obj_to_dict_helper(self, ['sub_gate', 'probability'])

Expand Down
Loading