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

Experiments: save quasi-cyclic codes with a minimum encoding rate #91

Merged
merged 12 commits into from
Apr 19, 2024
25 changes: 9 additions & 16 deletions experiments/quasi_cyclic/make_summary_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,15 @@
" d = code distance (minimal weight of a nontrivial logical operator)",
"code distance is estimated by the method of arXiv:2308.07915,"
+ f" minimizing over {NUM_TRIALS} trials",
"also included:",
" D = (Euclidean) communication distance required for a 'folded toric layout' of the code",
" r = k d^2 / n",
"the last column reports r = k d^2 / n",
"topological 2D codes such as the toric code strictly satisfy r <= 1",
"we only keep track of codes with r > 1",
"",
"Rx, Ry, ax, ay, bx, by, n, k, d, D, r",
"Rx, Ry, ax, ay, bx, by, n, k, d, r",
]

# data format
fmt = "%d, %d, %d, %d, %d, %d, %d, %d, %d, %.3f, %.3f"
fmt = "%d, %d, %d, %d, %d, %d, %d, %d, %d, %.3f"

##################################################

Expand All @@ -63,24 +61,19 @@
continue

# retrieve code parameters
nn, kk, dd, comm_dist = cache[key]

nn, kk, dd = cache[key]
if dd is None:
continue

# figure of merit relative to the surface code
# only report codes that outperform the surface code
merit = kk * dd**2 / nn
if merit <= 1:
# this code doesn't even beat the surface code, so we don't care about it
continue

# add a summary of this code to the appropriate group of data
summary = (*dims, *exponents, nn, kk, dd, comm_dist, merit)
data.append(summary)
if merit > 1:
code_data = (*dims, *exponents, nn, kk, dd, merit)
data.append(code_data)

##################################################

# save data
# save codes to a data file
os.makedirs(save_dir, exist_ok=True)
path = os.path.join(save_dir, "codes.csv")
header = "\n".join(headers)
Expand Down
62 changes: 30 additions & 32 deletions experiments/quasi_cyclic/run_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@
import sys

import diskcache
import numpy as np
from sympy.abc import x, y

import qldpc
import qldpc.cache

NUM_TRIALS = 1000 # for code distance calculations
MAX_COMMUNICATION_DISTANCE = 12
MIN_ORDER = 3 # minimum cyclic group order
MIN_RATE = 1 / 25 # ignore codes with lower encoding rates
NUM_TRIALS = 1000 # for code distance calculations

CACHE_DIR = os.path.dirname(__file__)
CACHE_NAME = ".code_cache"
Expand All @@ -38,48 +37,37 @@
def get_quasi_cyclic_code_params(
dims: tuple[int, int],
exponents: tuple[int, int, int, int],
min_rate: float,
num_trials: int,
*,
silent: bool = False,
) -> tuple[int, int, int | None, float] | None:
"""Compute communication distance and code distance for a quasi-cyclic code.
) -> tuple[int, int, int | None]:
"""Compute the code parameters of a quasi-cyclic code.

If the code is trivial or the communication distance is beyond the cutoff, return None.
If the code dimension (number of encoded logical qubits) is below some cutoff, return None.
"""
# construct the code itself
ax, ay, bx, by = exponents
poly_a = 1 + x + x**ax * y**ay
poly_b = 1 + y + x**bx * y**by
code = qldpc.codes.QCCode(dims, poly_a, poly_b)

if code.dimension == 0:
return None

# identify maximum Euclidean distance between check/data qubits required for each toric layout
max_distances = []
for plaquette_map, torus_shape in code.toric_layouts:
shifts_x, shifts_z = code.get_check_shifts(plaquette_map, torus_shape, open_boundaries=True)
distances = set(np.sqrt(xx**2 + yy**2) for xx, yy in shifts_x | shifts_z)
max_distances.append(max(distances))

# minimize distance requirement over possible toric layouts
comm_distance = min(max_distances)

if comm_distance > MAX_COMMUNICATION_DISTANCE:
return code.num_qubits, code.dimension, None, comm_distance
if code.dimension < code.num_qubits * min_rate:
return code.num_qubits, code.dimension, None

if not silent:
print("starting", dims, exponents)
print(" starting", dims, exponents)

distance = code.get_distance_bound(num_trials=num_trials)
assert isinstance(distance, int)

return code.num_qubits, code.dimension, distance, comm_distance
return code.num_qubits, code.dimension, distance


def run_and_save(
dims: tuple[int, int],
exponents: tuple[int, int, int, int],
min_rate: float,
num_trials: int,
cache: diskcache.Cache,
*,
Expand All @@ -89,17 +77,27 @@ def run_and_save(
if not silent and not any(exponents[1:]):
print(dims, exponents)

params = get_quasi_cyclic_code_params(dims, exponents, num_trials, silent=silent)
if params is not None:
nn, kk, dd, comm_dist = params
cache[dims, exponents, num_trials] = (nn, kk, dd, comm_dist)
if not silent and dd is not None:
merit = kk * dd**2 / nn
print(dims, exponents, (nn, kk, dd), f"{comm_dist:.2f}", f"{merit:.2f}")
key = (dims, exponents, num_trials)

# check whether we already have data for this code
if key in cache:
nn, kk, dd = cache[key]
# if we know the distance or the encoding rate is too low, there's nothing more to do
if dd is not None or kk < nn * min_rate:
return None

# compute and save code parameters
params = get_quasi_cyclic_code_params(dims, exponents, min_rate, num_trials, silent=silent)
cache[key] = params

nn, kk, dd = params
if not silent and dd is not None:
merit = kk * dd**2 / nn
print("", dims, exponents, (nn, kk, dd), f"{merit:.2f}")


def redundant(dims: tuple[int, int], exponents: tuple[int, int, int, int]) -> bool:
"""Are the given code parameters redundant?"""
"""Is the given quasi-cyclic code redundant?"""
dim_x, dim_y = dims
ax, ay, bx, by = exponents
return (
Expand All @@ -124,4 +122,4 @@ def redundant(dims: tuple[int, int], exponents: tuple[int, int, int, int]) -> bo
range(dim_x), range(dim_y), range(dim_x), range(dim_y)
):
if not redundant(dims, exponents):
executor.submit(run_and_save, dims, exponents, NUM_TRIALS, cache)
executor.submit(run_and_save, dims, exponents, MIN_RATE, NUM_TRIALS, cache)