Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 28 additions & 4 deletions src/qldpc/circuits/transversal.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ def get_transversal_automorphism_group(

Uses the methods of https://arxiv.org/abs/2409.18175.
"""
if not local_gates:
return abstract.Group(abstract.GroupMember(range(len(code))))

local_gates = _validate_local_gates(local_gates)
allow_swaps = "SWAP" in local_gates
local_gates.discard("SWAP")
Expand All @@ -122,10 +125,31 @@ def get_transversal_automorphism_group(
# we are looking for transversal gates involving only two-qubit SWAPs
matrix: npt.NDArray[np.int_] = parity_checks
_code = codes.QuditCode(matrix).maybe_to_css()
if isinstance(_code, codes.CSSCode) and np.array_equal(
(canonicalized_code := _code.canonicalized).matrix_x, canonicalized_code.matrix_z
):
matrix = canonicalized_code.matrix_x
if isinstance(_code, codes.CSSCode):
canonicalized_code = _code.canonicalized
if np.array_equal(canonicalized_code.matrix_x, canonicalized_code.matrix_z):
# Self-dual CSS: column permutations preserving H_x automatically preserve H_z
matrix = canonicalized_code.matrix_x
else:
# Non-self-dual CSS: the joint parity-check matrix has block-diagonal support, so
# its column-permutation automorphism group factors as Aut(H_x) x Aut(H_z) on
# disjoint column sets. SWAPs act diagonally on X- and Z-columns, so the SWAP
# transversal group is Aut(H_x) ∩ Aut(H_z). Recurse on fake self-dual codes built
# from H_x and H_z so the self-dual branch above handles each half, then intersect.
fake_code_x = codes.CSSCode(
canonicalized_code.matrix_x, canonicalized_code.matrix_x
)
fake_code_z = codes.CSSCode(
canonicalized_code.matrix_z, canonicalized_code.matrix_z
)
group_x = get_transversal_automorphism_group(
fake_code_x, ["SWAP"], deform_code=False, with_magma=with_magma
)
group_z = get_transversal_automorphism_group(
fake_code_z, ["SWAP"], deform_code=False, with_magma=with_magma
)
generators = _sympy_group_intersection_generators(group_x, group_z)
return abstract.Group(*map(abstract.GroupMember, generators))

else:
# we are looking for transversal gates involving two-qubit SWAPs and single-qubit Cliffords
Expand Down
25 changes: 15 additions & 10 deletions src/qldpc/circuits/transversal_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,22 @@

def test_transversal_ops() -> None:
"""Construct SWAP-transversal logical Cliffords of a code."""
code = codes.ToricCode(2)
code: codes.CSSCode

for local_gates in [
["SWAP"],
["SWAP", "H"],
["SWAP", "S"],
["SWAP", "SQRT_X"],
["SWAP", "H", "S"],
]:
transversal_ops = circuits.get_transversal_ops(code, local_gates)
assert len(transversal_ops) == len(local_gates) + 1
code = codes.ToricCode(2)
assert len(circuits.get_transversal_ops(code, [])) == 0
assert len(circuits.get_transversal_ops(code, ["SWAP"])) == 2
assert len(circuits.get_transversal_ops(code, ["SWAP", "H"])) == 3
assert len(circuits.get_transversal_ops(code, ["SWAP", "S"])) == 3
assert len(circuits.get_transversal_ops(code, ["SWAP", "SQRT_X"])) == 3
assert len(circuits.get_transversal_ops(code, ["SWAP", "H", "S"])) == 4

# non-self-dual [[8, 3, 2]] CSS code with nontrivial permutation automorphisms
code = codes.CSSCode(
[[1, 1, 1, 1, 0, 0, 0, 0], [1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1]],
[[1, 0, 1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0, 1]],
)
assert len(circuits.get_transversal_ops(code, ["SWAP"])) == 3

with pytest.raises(ValueError, match="Local Clifford gates"):
circuits.get_transversal_automorphism_group(code, ["SQRT_Y"])
Expand Down
36 changes: 31 additions & 5 deletions src/qldpc/external/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,13 +371,15 @@ def get_primitive_central_idempotents(group: str, field: int) -> IdempotentsList
"SmallGroup(1,1)": [[]],
"Group(())": [[]],
"SmallGroup(21,1)": [[(1, 2, 4), (3, 6, 5)], [(0, 1, 2, 3, 4, 5, 6)]],
# FiveQubitCode automorphism group (SWAP only)
"AutomorphismGroup(CheckMatCode([[1,0,0,0,1,1,1,0,1,1],[0,1,0,0,1,0,0,1,1,0],[0,0,1,0,1,1,1,0,0,0],[0,0,0,1,1,1,0,1,1,1]],GF(2)))": [
[(3, 7), (4, 5), (8, 9)],
[(1, 5), (2, 7), (3, 9), (6, 8)],
[(2, 6), (3, 8), (4, 5), (7, 9)],
[(0, 9, 2, 8), (1, 6), (3, 5, 4, 7)],
[(0, 7, 3), (1, 9, 8), (2, 4, 5)],
], # FiveQubitCode automorphism group (SWAP only)
],
# FiveQubitCode automorphism group (SWAP + Cliffords)
"AutomorphismGroup(CheckMatCode([[1,0,0,0,1,1,1,0,1,1,0,1,0,1,0],[0,1,0,0,1,0,0,1,1,0,0,1,1,1,1],[0,0,1,0,1,1,1,0,0,0,1,1,1,0,1],[0,0,0,1,1,1,0,1,1,1,1,0,1,0,0]],GF(2)))": [
[(0, 1), (5, 12), (6, 14), (7, 9)],
[(2, 3), (6, 9), (7, 14), (8, 11)],
Expand All @@ -388,12 +390,14 @@ def get_primitive_central_idempotents(group: str, field: int) -> IdempotentsList
[(2, 11), (3, 8), (6, 14), (7, 9)],
[(3, 8), (4, 10), (5, 12), (7, 9)],
[(3, 9), (4, 12), (5, 10), (7, 8)],
], # FiveQubitCode automorphism group (SWAP + Cliffords)
],
# ToricCode(2) automorphism group (SWAP only)
"AutomorphismGroup(CheckMatCode([[1,1,1,1]],GF(2)))": [
[(0, 1)],
[(1, 2)],
[(2, 3)],
], # ToricCode(2) automorphism group (SWAP only)
],
# ToricCode(2) automorphism group (SWAP + H/S/SQRT_X)
"AutomorphismGroup(CheckMatCode([[1,1,1,1,0,0,0,0],[0,0,0,0,1,1,1,1]],GF(2)))": [
[(2, 3), (4, 7), (5, 6)],
[(4, 7, 6, 5)],
Expand All @@ -403,7 +407,8 @@ def get_primitive_central_idempotents(group: str, field: int) -> IdempotentsList
[(1, 3), (4, 6), (5, 7)],
[(0, 6, 3, 7), (1, 5), (2, 4)],
[(0, 7), (1, 4), (2, 5, 3, 6)],
], # ToricCode(2) automorphism group (SWAP + H/S/SQRT_X)
],
# ToricCode(2) automorphism group (SWAP + Cliffords)
"AutomorphismGroup(CheckMatCode([[1,1,1,1,0,0,0,0,1,1,1,1],[0,0,0,0,1,1,1,1,1,1,1,1]],GF(2)))": [
[(4, 9), (5, 8, 6, 11), (7, 10)],
[(4, 7, 6, 5), (9, 11, 10)],
Expand All @@ -419,7 +424,28 @@ def get_primitive_central_idempotents(group: str, field: int) -> IdempotentsList
[(0, 4, 10, 1, 5, 9, 2, 7, 8), (3, 6, 11)],
[(2, 3), (4, 7), (8, 11)],
[(0, 10, 2, 11, 1, 9), (3, 8), (4, 7)],
], # ToricCode(2) automorphism group (SWAP + Cliffords)
],
# [[8, 3, 2]] X check automorphism group
"AutomorphismGroup(CheckMatCode([[1,1,0,0,0,0,1,1],[0,0,1,1,0,0,1,1],[0,0,0,0,1,1,1,1]],GF(2)))": [
[(6, 7)],
[(4, 5), (6, 7)],
[(4, 7, 5, 6)],
[(2, 3), (6, 7)],
[(2, 6, 4), (3, 7, 5)],
[(0, 3, 5, 1, 2, 4)],
[(0, 7), (1, 6), (2, 3), (4, 5)],
],
# [[8, 3, 2]] Z check automorphism group
"AutomorphismGroup(CheckMatCode([[1,0,1,0,1,0,1,0],[0,1,0,1,0,1,0,1]],GF(2)))": [
[(1, 7), (3, 5), (4, 6)],
[(1, 7, 5, 3)],
[(5, 7)],
[(3, 5, 7)],
[(1, 3, 5), (2, 4, 6)],
[(1, 5), (2, 6), (3, 7)],
[(0, 5, 6, 7), (1, 4), (2, 3)],
[(0, 7), (1, 2), (3, 6, 5, 4)],
],
}

KNOWN_PRIMITIVE_CENTRAL_IDEMPOTENTS: dict[tuple[str, int], IdempotentsList] = {
Expand Down