Skip to content

Commit

Permalink
LDPC + cats architecture added.
Browse files Browse the repository at this point in the history
  • Loading branch information
ElieGouzien committed Jan 17, 2024
1 parent ce28744 commit af92e56
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 28 deletions.
96 changes: 89 additions & 7 deletions cout_shor.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
wes=range(2, 30),
wms=range(2, 15),
cs=range(1, 40)),
'alice&bob3': dict(d1s=range(15), # only indexes
d2s=(None,),
# d and n are hard-coded!
ds=(22,),
ns=(21,),
wes=range(2, 30),
wms=range(2, 15),
cs=range(1, 40)),
None: dict(d1s=(None,),
d2s=(None,),
ds=(None,),
Expand All @@ -48,8 +56,7 @@ def _calc_ranges(base_params: Params, **kwargs):
"""
ranges = DEF_RANGES[base_params.type].copy()
ranges.update(kwargs)
if (base_params.type == 'alice&bob2'
and base_params.algo.prob == 'elliptic_log'):
if base_params.algo.prob == 'elliptic_log':
if ranges['cs'] != DEF_RANGES[base_params.type]['cs']:
warn('No coset representation for elliptic_log ; removing the '
'exploration !')
Expand Down Expand Up @@ -90,7 +97,8 @@ def iterate(base_params: Params, progress=PB_DEF, **kwargs):
and wm is not None and we is not None and wm > we):
continue
# Elliptic curve addition trick require one more qubit in we.
if (base_params.algo.prob == 'elliptic_log' and we <= 2):
if (base_params.algo.prob == 'elliptic_log'
and we is not None and we <= 2):
continue
yield base_params._replace(
algo=base_params.algo._replace(we=we, wm=wm, c=c),
Expand Down Expand Up @@ -203,6 +211,17 @@ def entree_tableau_rsa_alicebob2(params: Params):
logical_qubits(params, False)]


def entree_tableau_rsa_alicebob3(params: Params):
"""Give a table line, for RSA and alice&bob3."""
err_corr = ErrCorrCode(params)
cost, qubits = prepare_ressources(params)
return [params.algo.n, err_corr.ne, params.algo.c, params.algo.we,
params.algo.wm, params.low_level.n, params.low_level.d,
params.low_level.d1, err_corr._nb_factory, err_corr._factory_qubits,
qubits, format_time(cost.t), format_time(cost.exp_t),
logical_qubits(params, False)]


def entree_tableau_elliptic_log2(params: Params):
"""Give a table line, for elliptic curve logarithm and Alice&Bob2."""
err_corr = ErrCorrCode(params)
Expand All @@ -214,6 +233,17 @@ def entree_tableau_elliptic_log2(params: Params):
logical_qubits(params, False)]


def entree_tableau_elliptic_log3(params: Params):
"""Give a table line, for elliptic curve logarithm and Alice&Bob3."""
err_corr = ErrCorrCode(params)
cost, qubits = prepare_ressources(params)
return [params.algo.n, err_corr.ne, params.algo.we, params.algo.wm,
params.low_level.n, params.low_level.d, params.low_level.d1,
err_corr._nb_factory, err_corr._factory_qubits, qubits,
format_time(cost.t), format_time(cost.exp_t),
logical_qubits(params, False)]


def table_shape(largeurs, sep_places, sep="|"):
"""Give the shape of the table (for LaTeX)."""
liste = [f"S[table-figures-integer={size}]" if size is not None else 'c'
Expand Down Expand Up @@ -258,6 +288,23 @@ def _print_tableau_rsa_alicebob2(base_params: Params):
entetes, skip_size=(11, 12), seps=(2, 8))


def _print_tableau_rsa_alicebob3(base_params: Params):
"""Table for article, for RSA and alice&bob3."""
if base_params.type != 'alice&bob3' or base_params.algo.prob != 'rsa':
warn("Warning, parameters not compatible with the table; "
"columns might not correspond to what is expected!")
warn("Only one working point for error correction ; it might be overkill "
"for small numbers!")
entetes = ["$n$", '$n_e$', "$m$", "$w_e$", "$w_m$", r"$\alpha^2$",
"$d$", "$i$", r"$\#\text{factories}$", "factories qubits",
r"$n_{\text{qubits}}$", "$t$", r"$t_{\text{exp}}$",
"logical qubits"]
_print_tableau([128, 256, 512, 829, 1024, 2048],
[10] + [1]*8, base_params,
entree_tableau_rsa_alicebob3,
entetes, skip_size=(11, 12), seps=(2, 8))


def _print_tableau_elliptic_log2(base_params: Params):
"""Table for article, for ECDL and alice&bob2."""
if (base_params.type != 'alice&bob2'
Expand All @@ -275,6 +322,25 @@ def _print_tableau_elliptic_log2(base_params: Params):
seps=(2, 7))


def _print_tableau_elliptic_log3(base_params: Params):
"""Table for article, for ECDL and alice&bob3."""
if (base_params.type != 'alice&bob3'
or base_params.algo.prob != 'elliptic_log'):
warn("Warning, parameters not compatible with the table; "
"columns might not correspond to what is expected!")
warn("Warning, for small n values, magical state preparation parameters "
"might not be adapted (too much precision)!")
warn("Only one working point for error correction ; it might be overkill "
"for small numbers!")
entetes = ["$n$", '$n_e$', "$w_e$", "$w_m$", r"$\alpha^2$", "$d$",
"$i$", r"$\#\text{factories}$", "factories qubits",
r"$n_{\text{qubits}}$", "$t$", r"$t_{\text{exp}}$",
"logical qubits"]
_print_tableau([64, 128, 256, 512], [1]*7, base_params,
entree_tableau_elliptic_log3, entetes, skip_size=(10, 11),
seps=(2, 7))


def print_tableau(base_params: Params):
r"""Table for article supplemental material, autoselection of table type.
Expand All @@ -284,24 +350,40 @@ def print_tableau(base_params: Params):
"""
if base_params.type == 'alice&bob2' and base_params.algo.prob == 'rsa':
_print_tableau_rsa_alicebob2(base_params)
elif base_params.type == 'alice&bob3' and base_params.algo.prob == 'rsa':
_print_tableau_rsa_alicebob3(base_params)
elif (base_params.type == 'alice&bob2'
and base_params.algo.prob == 'elliptic_log'):
_print_tableau_elliptic_log2(base_params)
elif (base_params.type == 'alice&bob3'
and base_params.algo.prob == 'elliptic_log'):
_print_tableau_elliptic_log3(base_params)
else:
raise ValueError("No table for those parameters.")


# %% Executable part
if __name__ == '__main__':
params = Params('alice&bob2',
AlgoOpts(prob='elliptic_log', algo='Shor', s=None, n=256,
windowed=True, parallel_cnots=True),
LowLevelOpts(tc=500e-9))
# params = Params('alice&bob2',
# AlgoOpts(prob='elliptic_log', algo='Shor', s=None, n=256,
# windowed=True, parallel_cnots=True),
# LowLevelOpts(tc=500e-9))

# params = Params('alice&bob2',
# AlgoOpts(n=2048, windowed=True, parallel_cnots=True),
# LowLevelOpts(tc=500e-9))

params = Params('alice&bob3',
AlgoOpts(n=2048, windowed=True, parallel_cnots=True,
mesure_based_unlookup=True),
LowLevelOpts(tc=900e-9, tr=None))

# params = Params('alice&bob3',
# AlgoOpts(prob='elliptic_log', algo='Shor', s=None, n=256,
# windowed=True, parallel_cnots=True,
# mesure_based_unlookup=True),
# LowLevelOpts(tc=900e-9, tr=None))

print("\n"*2)
print("Windowed arithmetics")
print("====================")
Expand Down
112 changes: 93 additions & 19 deletions error_correction.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ def __new__(cls, params: Params, *args, **kwargs):
if cls is ErrCorrCode:
if params.type == 'alice&bob2':
return AliceAndBob2(params, *args, **kwargs)
elif params.type == 'alice&bob3':
return AliceAndBob3(params, *args, **kwargs)
elif params.type is None:
return NoCorrCode(params, *args, **kwargs)
else:
Expand Down Expand Up @@ -827,26 +829,39 @@ def _set_clifford_costs(self, err, log_qubits):
"""Set the cost of the Clifford operation.
err : single logical qubit error.
time : time for fault-tolerant error correction.
log_qubits : number of logical qubits.
log_qubits : nombre de qubits logiques.
Method shared between all Alice & Bob's architectures.
"""
d, tc = self.params.low_level.d, self.params.low_level.tc
time = tc*d # time for fault-tolerant error correction
# NOTE: repetition code: 5 steps in one cycle:
# ancilla preparation, cnot, cooldown to reconverge cats and
# avoid leakage, cnot, and measurement.
# NOTE: LDPC code: 9 steps in one cycle (for data qubits):
# 1 preparation, 1 cnot, 1 cooldown, 1 cnot, 1 cooldown, 1 cnot,
# 1 cooldown, 1 cnot, 1 measure, each taking tc/9.
# Routing and factory qubits use normal repetition code cycle when
# not interacting with data qubits, in 5 steps.
nb_steps = {'alice&bob2': 5, 'alice&bob3': 9}[self.params.type]
# NOTE: slight overestimation as not all logical qubits are always live.
nothing_1 = PhysicalCost(err, 0)
# Note : initialisation of data qubit at same time as ancillae
# Note: no tc/nb_steps as initialisation of data qubit at same time as
# ancillae.
self.init = PhysicalCost(err, time) + (log_qubits-1)*nothing_1
# Time of physical measurement : 0.2*tc
self.mesure = PhysicalCost(0.2*err/d, 0.2*tc) + (
(log_qubits-1)*(0.2/d)*nothing_1)
# Note : physical gate at same time as ancillae preparation
# Time of physical measurement : tc
self.mesure = (1/d/nb_steps)*PhysicalCost(err, time) + (
(log_qubits-1)*(1/d/nb_steps)*nothing_1)
# Note: physical gate at same time as ancillae preparation
self.gate1 = PhysicalCost(err, time) + (log_qubits-1)*nothing_1
# CNOT : see fig.25 (arXiv version)
# Note : not exact as ancillary larger than d
# Note : transversal CNOT counted with XX measurement.
self.cnot = (self.init + 2*nothing_1 # prepare |0>
+ PhysicalCost(1 - (1 - err)**2, time) + nothing_1 # XX
+ self.mesure + 2*(0.2/d)*nothing_1 # Z measurement
+ (log_qubits-2)*(2+0.2/d)*nothing_1)
# CNOT: see fig.25 (arXiv version)
# Note: not exact as ancillary larger than d
# Note: transversal CNOT counted with XX measurement.
# NOTE: ancilla use repetition code, so always 5 steps when alone
self.cnot = (5/nb_steps*self.init + 2*5/nb_steps*nothing_1
+ PhysicalCost(1 - (1 - err)**2, time) + nothing_1
+ self.mesure + 2*(1/d/nb_steps)*nothing_1
+ (log_qubits-2)*(2+1/d/nb_steps)*nothing_1)
# CZ are considered as costly as CNOT (not a lot in the algorithm)
# Processor properties
self.correct_time = time
Expand All @@ -862,6 +877,11 @@ def _teleport(self):
@staticmethod
def _toffoli_state_mesurement(params: Params, final_time):
"""Cost of measurement-based preparation of Toffoli magical state."""
if not ((params.type == 'alice&bob2' and params.low_level.tc == 500e-9)
or (params.type == 'alice&bob3'
and params.low_level.tc == 900e-9)):
raise ValueError("100 ns per gate for fast CNOT is hardcoded!")
# NOTE: CNOTs in the factory are longer than in error correction cycle.
if params.low_level.d1 == 0:
d1 = 3
n1 = 3.75
Expand Down Expand Up @@ -969,7 +989,8 @@ def _toffoli_state_mesurement(params: Params, final_time):
accept_prob = 1
else:
raise ValueError("'d1' is here used as an index in range(15).")
# t_cnot = 0.2*params.low_level.tc*89.2/n1
# nb_steps = {'alice&bob2': 5, 'alice&bob3': 9}[params.type]
# t_cnot = params.low_level.tc*89.2/n1/nb_steps
# assert isclose(time, time_steps * t_cnot, rel_tol=1e-3)
nb_factory = ceil(time / (final_time * accept_prob))
return (PhysicalCost(err_prob, time/(nb_factory*accept_prob)),
Expand Down Expand Up @@ -1040,6 +1061,49 @@ def _check_mesure_based_unlookup(self):
pass


class AliceAndBob3(AliceAndBob2):
"""Cat qubits + LDPC with flip-chip."""

# Note that self._toffoli_state_mesurement() assumes 500 ns cycle time,
# so no need to change it.

@staticmethod
def _single_qubit_err(params: Params, k1_k2):
"""Logical error for 1 qubit by logical cycle."""
d, n = params.low_level.d, params.low_level.n
if d != 22 or n != 21:
raise ValueError("d=22 and n=21 is hardcoded!")
pzl = 4.5e-19
pxl = 3.45e-18
err = d*(pzl + pxl) # taking into account d cycles
if err > 1:
raise RuntimeError("Error formula used outside of its domain!")
return err

def _set_physical_qubits(self, log_qubits, nb_factory, factory_dist):
"""Set the number of qubits.
log_qubits : number of logical qubits.
nb_factory : number of factories.
factory_dist : distance inside factories.
"""
d = self.params.low_level.d
# Record for table nb factories and total qubit number in it
self._nb_factory = nb_factory
# Only routing on one side ; assume that the factories are not stacked
# but just next to each other.
factory_qubits = ((2*factory_dist-1+3)*4 - 1)*nb_factory
self._factory_qubits = factory_qubits
# Processor with routing, without factories.
if d != 22:
raise NotImplementedError("Following code have d=22 hardcoded!")
data = data_rout = 4*log_qubits + 29
ancilla = 3*log_qubits + 29
ancilla_rout = 7*log_qubits/2 + 27
proc_only_qubits = data + ancilla + data_rout + ancilla_rout
self.proc_qubits = factory_qubits + proc_only_qubits


class NoCorrCode(ToffoliBasedCode):
"""No error correction."""

Expand All @@ -1058,9 +1122,16 @@ def __init__(self, params: Params):


# %% Ancillary functions related with the algorithm
# Mapping between type name and class.
CLASS_MAP = {'alice&bob2': AliceAndBob2,
'alice&bob3': AliceAndBob3,
None: NoCorrCode
}


def logical_qubits_exp_mod(params: Params, verb=True):
"""Logical qubits number for modular exponentiation."""
toffoli_based = params.type in ('alice&bob2', None)
toffoli_based = issubclass(CLASS_MAP[params.type], ToffoliBasedCode)
if toffoli_based and params.algo.windowed:
res = (3*params.algo.n + 2*params.algo.c + 2*params.algo.we
+ params.algo.wm - 1)
Expand All @@ -1075,7 +1146,7 @@ def logical_qubits_exp_mod(params: Params, verb=True):

def logical_qubits_elliptic_mul(params: Params, verb=True):
"""Logical qubits number for elliptic curve multiplication."""
# ancilla_free_add = params.type in ('alice&bob2', None)
# ancilla_free_add = issubclass(CLASS_MAP[params.type], ToffoliBasedCode)
ancilla_free_add = False
if params.algo.windowed:
if ancilla_free_add:
Expand All @@ -1098,8 +1169,11 @@ def logical_qubits(params: Params, verb=True):
Autoselection of the problem depending on parameters.
"""
if params.algo.prob == 'rsa':
return logical_qubits_exp_mod(params, verb)
res = logical_qubits_exp_mod(params, verb)
elif params.algo.prob == 'elliptic_log':
return logical_qubits_elliptic_mul(params, verb)
res = logical_qubits_elliptic_mul(params, verb)
else:
raise ValueError("Unknown problem type !")
if params.type == 'alice&bob3' and res % 2 == 1:
res += 1 # enforce even logical qubits for alice&bob3
return res
4 changes: 2 additions & 2 deletions tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
(only for type == '3dcolor', otherwise takes by
default the obvious option)
mesure_based_unlookup : unlookup based on measurement?
(only usable for type == 'alice&bob(2)' because |0>
(only usable for type == 'alice&bob(n)' because |0>
preparation is long)
parallel_cnots : CNOT with multiple targets cost as much as a single CNOT?
Be careful, it is not a full parallelisation of the CNOTs
Expand All @@ -61,7 +61,7 @@
Params.__doc__ = """Params(type, algo, low_level)
Parameters:
type : type of error correction : 'alice&bob2' or None
type : type of error correction : 'alice&bob2', 'alice&bob3' or None
algo : algorithm options, type AlgoOpts
low_level : low level options, type LowLevelOpts
"""
Expand Down

0 comments on commit af92e56

Please sign in to comment.