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

Add inverse method for Clifford tableau #4111

Merged
merged 23 commits into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3bf0090
add to_phased_xz_gate function for SingleQubitCliffordGate
BichengYing Apr 25, 2021
72208c9
format using flynt instead
BichengYing Apr 25, 2021
0566724
Merge branch 'master' into clifford
BichengYing Apr 25, 2021
0ad7d86
Merge branch 'master' into clifford
BichengYing May 5, 2021
d680b29
Move `import DensePauliString` under CliffordTableau into runtime
BichengYing May 6, 2021
70283c4
Add merged_with method of two clifford tableau
BichengYing May 9, 2021
701f0df
Fix lint and coverage error
BichengYing May 9, 2021
e086c41
Varying the seed in the loop
BichengYing May 10, 2021
ae82bcd
Merge branch 'master' into single_clifford
BichengYing May 10, 2021
733c738
Address the comments and rename the function to `then`
BichengYing May 12, 2021
c7877e4
Ignore the pylint warning
BichengYing May 13, 2021
3e341c5
Minor update for more comments
BichengYing May 14, 2021
4d84529
Merge branch 'master' into single_clifford
BichengYing May 14, 2021
6a19c8b
Update cirq-core/cirq/qis/clifford_tableau.py
BichengYing May 15, 2021
356b1ed
Add dimension and type check and corresponding test.
BichengYing May 15, 2021
a4dd451
Merge branch 'single_clifford' into inv_tableau
BichengYing May 16, 2021
0d32604
Add `inverse` function for clifford tableau
BichengYing May 16, 2021
716f6db
revert phased xz commitment
BichengYing May 16, 2021
46260ba
Add more comments
BichengYing May 18, 2021
e9f4822
Revert "Add more comments"
BichengYing May 18, 2021
2d1e21c
Merge branch 'master' into inv_tableau
BichengYing May 21, 2021
372cf9f
Format
BichengYing May 21, 2021
b355f1a
Add more comments
BichengYing May 21, 2021
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
19 changes: 19 additions & 0 deletions cirq-core/cirq/qis/clifford_tableau.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,25 @@ def then(self, second: 'CliffordTableau') -> 'CliffordTableau':

return merged_tableau

def inverse(self) -> 'CliffordTableau':
"""Returns the inverse Clifford tableau of this tableau."""
ret_table = CliffordTableau(num_qubits=self.n)
# It relies on the symplectic property of Clifford tableau.
# [A^T C^T [0 I [A B [0 I
# B^T D^T] I 0] C D] = I 0]
# So the inverse is [[D^T B^T], [C^T A^T]]
ret_table.xs[: self.n] = self.zs[self.n :].T
ret_table.zs[: self.n] = self.zs[: self.n].T
ret_table.xs[self.n :] = self.xs[self.n :].T
ret_table.zs[self.n :] = self.xs[: self.n].T

# Update the sign -- rs.
# The idea is noting the sign of tabluea `a` contributes to the composed tableau
# `a.then(b)` directly. (While the sign in `b` need take very complicated transformation.)
# Refer above `then` function implementation for more details.
ret_table.rs = ret_table.then(self).rs
return ret_table

def __matmul__(self, second: 'CliffordTableau'):
if not isinstance(second, CliffordTableau):
return NotImplemented
Expand Down
81 changes: 81 additions & 0 deletions cirq-core/cirq/qis/clifford_tableau_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,84 @@ def test_tableau_then_with_bad_input():

with pytest.raises(TypeError):
t1.then(cirq.X)


def test_inverse():
t = cirq.CliffordTableau(num_qubits=1)
assert t.inverse() == t

t = cirq.CliffordTableau(num_qubits=1)
_X(t, 0)
_S(t, 0)
expected_t = cirq.CliffordTableau(num_qubits=1)
_S(expected_t, 0) # the inverse of S gate is S*S*S.
_S(expected_t, 0)
_S(expected_t, 0)
_X(expected_t, 0)
assert t.inverse() == expected_t
assert t.then(t.inverse()) == cirq.CliffordTableau(num_qubits=1)
assert t.inverse().then(t) == cirq.CliffordTableau(num_qubits=1)

t = cirq.CliffordTableau(num_qubits=2)
_H(t, 0)
_H(t, 1)
# Because the ops are the same in either forward or backward way,
# t is self-inverse operator.
assert t.inverse() == t
assert t.then(t.inverse()) == cirq.CliffordTableau(num_qubits=2)
assert t.inverse().then(t) == cirq.CliffordTableau(num_qubits=2)

t = cirq.CliffordTableau(num_qubits=2)
_X(t, 0)
_CNOT(t, 0, 1)
expected_t = cirq.CliffordTableau(num_qubits=2)
_CNOT(t, 0, 1)
_X(t, 0)
assert t.inverse() == expected_t
assert t.then(t.inverse()) == cirq.CliffordTableau(num_qubits=2)
assert t.inverse().then(t) == cirq.CliffordTableau(num_qubits=2)

def random_circuit_and_its_inverse(num_ops, num_qubits, seed=12345):
prng = np.random.RandomState(seed)
candidate_op = [_H, _S, _X, _Z]
if num_qubits > 1:
candidate_op = [_H, _S, _X, _Z, _CNOT]

seq_op = []
inv_seq_ops = []
for _ in range(num_ops):
op = prng.randint(len(candidate_op))
if op != 4:
args = (prng.randint(num_qubits),)
else:
args = prng.choice(num_qubits, 2, replace=False)
seq_op.append((candidate_op[op], args))
if op == 1: # S gate
inv_seq_ops.extend([(_S, args), (_S, args), (_S, args)])
else:
inv_seq_ops.append((candidate_op[op], args))
return seq_op, inv_seq_ops[::-1]

# Do small random circuits test 100 times.
for seed in range(100):
t, expected_t, _ = _three_identical_table(7)
seq_op, inv_seq_ops = random_circuit_and_its_inverse(num_ops=50, num_qubits=7, seed=seed)
for op, args in seq_op:
op(t, *args)
for op, args in inv_seq_ops:
op(expected_t, *args)
assert t.inverse() == expected_t
assert t.then(t.inverse()) == cirq.CliffordTableau(num_qubits=7)
assert t.inverse().then(t) == cirq.CliffordTableau(num_qubits=7)

# Since inverse Clifford Tableau operation is O(n^3) (same order of composing two tableaux),
# running 100 qubits case is still fast.
t, expected_t, _ = _three_identical_table(100)
seq_op, inv_seq_ops = random_circuit_and_its_inverse(num_ops=1000, num_qubits=100)
for op, args in seq_op:
op(t, *args)
for op, args in inv_seq_ops:
op(expected_t, *args)
assert t.inverse() == expected_t
assert t.then(t.inverse()) == cirq.CliffordTableau(num_qubits=100)
assert t.inverse().then(t) == cirq.CliffordTableau(num_qubits=100)