from collections import defaultdict
from collections.abc import Mapping, Sequence
from functools import partial
from itertools import count, product
from ..layout import Layout, QubitDict
from .rot_surface_codes import (
get_data_index,
rot_surface_stability_rectangle,
shift_direction,
)
from .util import check_distance, invert_shift, is_valid, set_missing_neighbours_to_none
[docs]
def repetition_code(
stab_type: str,
distance: int,
logical_qubit_label: str = "L0",
init_point: tuple[int | float, int | float] = (1, 1),
init_data_qubit_id: int = 1,
init_zanc_qubit_id: int = 1,
init_xanc_qubit_id: int = 1,
init_ind: int = 0,
init_logical_ind: int = 0,
interaction_order: str | Mapping[str, Sequence[str]] = "shallow",
) -> Layout:
"""Generates a repetition code layout.
Parameters
----------
stab_type
Stabilizer type for the repetition code: ``"x_type"`` or ``"z_type"``.
distance
The distance of the code.
logical_qubit_label
Label for the logical qubit, by default ``"L0"``.
init_point
Coordinates for the bottom left (i.e. southest west) data qubit.
By default ``1``, so the label is ``"D1"``.
init_data_qubit_id
Index for the bottom left (i.e. southest west) data qubit.
By default ``1``, so the label is ``"D1"``.
init_zanc_qubit_id
Index for the bottom left (i.e. southest west) Z-type ancilla qubit.
By default ``1``, so the label is ``"Z1"``.
init_xanc_qubit_id
Index for the bottom left (i.e. southest west) X-type ancilla qubit.
By default ``1``, so the label is ``"X1"``.
init_ind
Minimum index that is going to be associated to a qubit.
init_logical_ind
Minimum index that is going to be associated to a logical qubit.
interaction_order
There are two options for built-in interaction orders: ``"shallow"`` and
``"surface_code"``. The ``"shallow"`` one corresponds to two two-qubit-gate
layers, while the ``"surface_code"`` one corresponds to the same schedule
as the surface code layout (mimic what it is usually done in experiments).
One can always specify the interaction order in the same format as for
the rotated surface code layouts.
By default ``"shallow"``.
Returns
-------
Layout
The layout of the code.
"""
check_distance(distance)
if stab_type == "x_type":
distance_x, distance_z = 1, distance
elif stab_type == "z_type":
distance_x, distance_z = distance, 1
else:
raise ValueError(
f"'stab_type' must be 'x_type' or 'z_type', but '{stab_type}' was given."
)
if not isinstance(init_point, tuple):
raise TypeError(
f"'init_point' must be a tuple, but {type(init_point)} was given."
)
if (len(init_point) != 2) or any(
not isinstance(p, (float, int)) for p in init_point
):
raise TypeError("'init_point' must have two elements that are floats or ints.")
if not isinstance(logical_qubit_label, str):
raise TypeError(
"'logical_qubit_label' must be a string, "
f"but {type(logical_qubit_label)} was given."
)
if not isinstance(init_data_qubit_id, int):
raise TypeError(
"'init_data_qubit_id' must be an int, "
f"but {type(init_data_qubit_id)} was given."
)
if not isinstance(init_zanc_qubit_id, int):
raise TypeError(
"'init_zanc_qubit_id' must be an int, "
f"but {type(init_zanc_qubit_id)} was given."
)
if not isinstance(init_xanc_qubit_id, int):
raise TypeError(
"'init_xanc_qubit_id' must be an int, "
f"but {type(init_xanc_qubit_id)} was given."
)
name = f"d-{distance} repetition code layout."
code = "repetition_code"
description = ""
if interaction_order == "shallow":
interaction_order = dict(steps=["left", "right"])
elif interaction_order == "surface_code":
interaction_order = dict(
x_type=["north_east", "north_west", "south_east", "south_west"],
z_type=["north_east", "south_east", "north_west", "south_west"],
)
elif isinstance(interaction_order, dict):
pass
else:
raise ValueError(
"'interaction_order' must be 'shallow' or 'surface_code', "
f"but {interaction_order} was given."
)
log_op = [f"D{i + init_data_qubit_id}" for i in range(distance)]
log_other_op = [f"D{init_data_qubit_id}"]
if stab_type == "x_type":
log_x, log_z = log_other_op, log_op
else:
log_x, log_z = log_op, log_other_op
logical_qubits = {
logical_qubit_label: dict(log_x=log_x, log_z=log_z, ind=init_logical_ind)
}
layout_setup = dict(
name=name,
code=code,
logical_qubits=logical_qubits,
description=description,
distance_x=distance_x,
distance_z=distance_z,
distance=distance,
interaction_order=interaction_order,
)
col_size = 2 * distance + 1
row_size = 2 * 1 + 1
data_indexer = partial(get_data_index, row_size=1, start_ind=init_data_qubit_id)
valid_coord = partial(is_valid, max_size_col=col_size, max_size_row=row_size)
pos_shifts = (1, -1)
nbr_shifts = tuple(product(pos_shifts, repeat=2))
layout_data: list[QubitDict] = []
neighbor_data: defaultdict[str, dict[str, str | None]] = defaultdict(dict)
ind = init_ind
# change initial point because by default the code places the "D1" qubit
# in the (1,1) point.
init_point = (init_point[0] - 1, init_point[1] - 1)
for col in range(1, col_size, 2):
index = data_indexer(col, 1)
qubit_info = dict(
qubit=f"D{index}",
role="data",
coords=[col + init_point[0], 1 + init_point[1]],
stab_type=None,
ind=ind,
)
layout_data.append(qubit_info)
ind += 1
s_index = count(start=init_zanc_qubit_id)
prefix = "X" if stab_type == "x_type" else "Z"
for col in range(2, col_size - 1, 2):
for row in range(col % 4, row_size, 4):
anc_qubit = f"{prefix}{next(s_index)}"
qubit_info = dict(
qubit=anc_qubit,
role="anc",
coords=[col + init_point[0], row + init_point[1]],
stab_type=stab_type,
ind=ind,
)
layout_data.append(qubit_info)
ind += 1
for col_shift, row_shift in nbr_shifts:
data_row, data_col = row + row_shift, col + col_shift
if not valid_coord(data_col, data_row):
continue
data_index = data_indexer(data_col, data_row)
data_qubit = f"D{data_index}"
direction = shift_direction(col_shift, row_shift)
neighbor_data[anc_qubit][direction] = data_qubit
inv_shifts = invert_shift(col_shift, row_shift)
inv_direction = shift_direction(*inv_shifts)
neighbor_data[data_qubit][inv_direction] = anc_qubit
set_missing_neighbours_to_none(neighbor_data)
for qubit_info in layout_data:
qubit = qubit_info["qubit"]
qubit_info["neighbors"] = neighbor_data[qubit]
layout_setup["layout"] = layout_data
layout = Layout(layout_setup)
return layout
[docs]
def repetition_stability(
num_stabs: int,
stab_type: str,
observable: str = "O0",
init_point: tuple[int | float, int | float] = (1, 1),
init_data_qubit_id: int = 1,
init_zanc_qubit_id: int = 1,
init_xanc_qubit_id: int = 1,
init_ind: int = 0,
interaction_order: str = "shallow",
) -> Layout:
"""
Generates a repetition layout for stability experiments.
Parameters
----------
stab_type
Stabilizer type for the repetition layout: ``"x_type"`` or ``"z_type"``.
num_stabs
Number of stabilizers that this layout will contain. Note that all stabilizers
will be of type ``stab_type``.
observable
Label for the observable, by default ``"L0"``.
init_point
Coordinates for the bottom left (i.e. southest west) data qubit.
By default ``(1, 1)``.
init_data_qubit_id
Index for the bottom left (i.e. southest west) data qubit.
By default ``1``, so the label is ``"D1"``.
init_zanc_qubit_id
Index for the bottom left (i.e. southest west) Z-type ancilla qubit.
By default ``1``, so the label is ``"Z1"``.
init_xanc_qubit_id
Index for the bottom left (i.e. southest west) X-type ancilla qubit.
By default ``1``, so the label is ``"X1"``.
init_ind
Minimum index that is going to be associated to a qubit.
interaction_order
There are two options for interaction order: ``"shallow"`` and
``"surface_code"``. The ``"shallow"`` one corresponds to two two-qubit-gate
layers, while the ``"surface_code"`` one corresponds to the same schedule
as the surface code layout (mimic what it is usually done in experiments).
By default ``"shallow"``.
Returns
-------
Layout
The layout.
"""
layout = rot_surface_stability_rectangle(
stab_type=stab_type,
width=num_stabs - 1,
height=1,
observable=observable,
init_point=init_point,
init_data_qubit_id=init_data_qubit_id,
init_zanc_qubit_id=init_zanc_qubit_id,
init_xanc_qubit_id=init_xanc_qubit_id,
init_ind=init_ind,
)
layout.code = "repetition_stability"
layout.name = f"w-{num_stabs - 1} repetition layout for stability experiments."
if interaction_order == "shallow":
layout.interaction_order = dict(steps=["left", "right"])
elif interaction_order == "surface_code":
layout.interaction_order = dict(
x_type=["north_east", "north_west", "south_east", "south_west"],
z_type=["north_east", "south_east", "north_west", "south_west"],
)
else:
raise ValueError(
"'interaction_order' must be 'shallow' or 'surface_code', "
f"but {interaction_order} was given."
)
return layout