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

Rectangular lattice register and layout #665

Merged
merged 7 commits into from
Apr 29, 2024
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
1 change: 1 addition & 0 deletions pulser-core/pulser/json/supported.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"pulser.register.register3d": ("Register3D",),
"pulser.register.register_layout": ("RegisterLayout",),
"pulser.register.special_layouts": (
"RectangularLatticeLayout",
soufianekaghad98 marked this conversation as resolved.
Show resolved Hide resolved
"SquareLatticeLayout",
"TriangularLatticeLayout",
),
Expand Down
2 changes: 2 additions & 0 deletions pulser-core/pulser/register/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from pulser.register.special_layouts import (
SquareLatticeLayout,
TriangularLatticeLayout,
RectangularLatticeLayout,
)

__all__ = [
Expand All @@ -29,4 +30,5 @@
"RegisterLayout",
"SquareLatticeLayout",
"TriangularLatticeLayout",
"RectangularLatticeLayout",
]
39 changes: 32 additions & 7 deletions pulser-core/pulser/register/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def rectangle(
spacing: float = 4.0,
prefix: Optional[str] = None,
) -> Register:
"""Initializes the register with the qubits in a rectangular array.
"""Creates a rectangular array of qubits on a square lattice.

Args:
rows: Number of rows.
Expand All @@ -102,6 +102,32 @@ def rectangle(
Returns:
A register with qubits placed in a rectangular array.
"""
return cls.rectangular_lattice(rows, columns, spacing, spacing, prefix)

@classmethod
def rectangular_lattice(
cls,
rows: int,
columns: int,
row_spacing: float = 4.0,
col_spacing: float = 2.0,
prefix: Optional[str] = None,
) -> Register:
HGSilveri marked this conversation as resolved.
Show resolved Hide resolved
"""Creates a rectangular array of qubits on a rectangular lattice.

Args:
rows: Number of rows.
columns: Number of columns.
row_spacing: The distance between rows in μm.
col_spacing: The distance between columns in μm.
prefix: The prefix for the qubit ids. If defined, each qubit
id starts with the prefix, followed by an int from 0 to N-1
(e.g. prefix='q' -> IDs: 'q0', 'q1', 'q2', ...)

Returns:
Register with qubits placed in a rectangular array on a
rectangular lattice.
"""
# Check rows
if rows < 1:
raise ValueError(
Expand All @@ -117,13 +143,12 @@ def rectangle(
)

# Check spacing
if spacing <= 0.0:
raise ValueError(
f"Spacing between atoms (`spacing` = {spacing})"
" must be greater than 0."
)
if row_spacing <= 0.0 or col_spacing <= 0.0:
raise ValueError("Spacing between atoms must be greater than 0.")

coords = patterns.square_rect(rows, columns) * spacing
coords = patterns.square_rect(rows, columns)
coords[:, 0] = coords[:, 0] * col_spacing
coords[:, 1] = coords[:, 1] * row_spacing

return cls.from_coordinates(coords, center=True, prefix=prefix)

Expand Down
66 changes: 55 additions & 11 deletions pulser-core/pulser/register/special_layouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,33 @@
from pulser.register import Register


class SquareLatticeLayout(RegisterLayout):
"""A RegisterLayout with a square lattice pattern in a rectangular shape.
class RectangularLatticeLayout(RegisterLayout):
"""RegisterLayout with rectangular lattice pattern in a rectangular shape.

Args:
rows: The number of rows of traps.
columns: The number of columns of traps.
spacing: The distance between neighbouring traps (in µm).
col_spacing: Horizontal distance between neighbouring traps (in µm).
row_spacing: Vertical distance between neighbouring traps (in µm)
"""

def __init__(self, rows: int, columns: int, spacing: float):
"""Initializes a SquareLatticeLayout."""
def __init__(
self, rows: int, columns: int, col_spacing: float, row_spacing: float
):
"""Initializes a RectangularLatticeLayout."""
self._rows = int(rows)
self._columns = int(columns)
self._spacing = float(spacing)
self._col_spacing = float(col_spacing)
self._row_spacing = float(row_spacing)
slug = (
f"SquareLatticeLayout({self._rows}x{self._columns}, "
f"{self._spacing}µm)"
f"RectangularLatticeLayout({self._rows}x{self._columns}, "
f"{self._col_spacing}x{self._row_spacing}µm)"
)
self._traps = patterns.square_rect(self._rows, self._columns)
self._traps[:, 0] = self._traps[:, 0] * self._col_spacing
self._traps[:, 1] = self._traps[:, 1] * self._row_spacing
super().__init__(
patterns.square_rect(self._rows, self._columns) * self._spacing,
trap_coordinates=self._traps,
slug=slug,
)

Expand Down Expand Up @@ -84,16 +91,53 @@ def rectangular_register(
if rows > self._rows or columns > self._columns:
raise ValueError(
f"A '{rows}x{columns}' array doesn't fit a "
f"{self._rows}x{self._columns} SquareLatticeLayout."
f"{self._rows}x{self._columns} RectangularLatticeLayout."
)
points = patterns.square_rect(rows, columns) * self._spacing
points = patterns.square_rect(rows, columns)
points[:, 0] = points[:, 0] * self._col_spacing
points[:, 1] = points[:, 1] * self._row_spacing
trap_ids = self.get_traps_from_coordinates(*points)
qubit_ids = [f"{prefix}{i}" for i in range(len(trap_ids))]
return cast(
pulser.Register,
self.define_register(*trap_ids, qubit_ids=qubit_ids),
)

def _to_dict(self) -> dict[str, Any]:
return obj_to_dict(
self,
self._rows,
self._columns,
self._col_spacing,
self._row_spacing,
)


class SquareLatticeLayout(RectangularLatticeLayout):
"""A RegisterLayout with a square lattice pattern in a rectangular shape.

Args:
rows: The number of rows of traps.
columns: The number of columns of traps.
spacing: The distance between neighbouring traps (in µm).
"""

def __init__(self, rows: int, columns: int, spacing: float):
"""Initializes a SquareLatticeLayout."""
self._rows = int(rows)
self._columns = int(columns)
self._spacing = float(spacing)
self._col_spacing = self._spacing
self._row_spacing = self._spacing
super().__init__(
self._rows, self._columns, self._spacing, self._spacing
)
slug = (
f"SquareLatticeLayout({self._rows}x{self._columns}, "
f"{self._spacing}µm)"
)
object.__setattr__(self, "slug", slug)

HGSilveri marked this conversation as resolved.
Show resolved Hide resolved
def _to_dict(self) -> dict[str, Any]:
return obj_to_dict(self, self._rows, self._columns, self._spacing)

Expand Down
6 changes: 6 additions & 0 deletions tests/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from pulser.parametrized.decorators import parametrize
from pulser.register.register_layout import RegisterLayout
from pulser.register.special_layouts import (
RectangularLatticeLayout,
SquareLatticeLayout,
TriangularLatticeLayout,
)
Expand Down Expand Up @@ -92,6 +93,11 @@ def test_layout():
assert new_square_layout == square_layout
assert type(new_square_layout) is SquareLatticeLayout

rectangular_layout = RectangularLatticeLayout(8, 10, 6, 5)
new_rectangular_layout = encode_decode(rectangular_layout)
assert new_rectangular_layout == rectangular_layout
assert type(new_rectangular_layout) is RectangularLatticeLayout


def test_register_from_layout():
layout = RegisterLayout([[0, 0], [1, 1], [1, 0], [0, 1]])
Expand Down
18 changes: 18 additions & 0 deletions tests/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@ def test_creation():
Register(qubits, spacing=10, layout="square", trap_ids=(0, 1, 3))


def test_rectangular_lattice():
# Check rows
with pytest.raises(ValueError, match="The number of rows"):
Register.rectangular_lattice(0, 2, 3, 4)

# Check columns
with pytest.raises(ValueError, match="The number of columns"):
Register.rectangular_lattice(2, 0, 3, 4)

# Check row spacing
with pytest.raises(ValueError, match="Spacing"):
Register.rectangular_lattice(2, 2, 0.0, 5)

# Check col spacing
with pytest.raises(ValueError, match="Spacing"):
Register.rectangular_lattice(2, 2, 3, 0.0)


def test_rectangle():
# Check rows
with pytest.raises(ValueError, match="The number of rows"):
Expand Down
17 changes: 17 additions & 0 deletions tests/test_register_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from pulser.register import Register, Register3D
from pulser.register.register_layout import RegisterLayout
from pulser.register.special_layouts import (
RectangularLatticeLayout,
SquareLatticeLayout,
TriangularLatticeLayout,
)
Expand Down Expand Up @@ -190,6 +191,22 @@ def test_square_lattice_layout():
square.rectangular_register(10, 3)


def test_rectangular_lattice_layout():
rectangle = RectangularLatticeLayout(9, 7, 2, 4)
assert str(rectangle) == "RectangularLatticeLayout(9x7, 2.0x4.0µm)"
assert rectangle.square_register(3) == Register.rectangular_lattice(
3, 3, col_spacing=2, row_spacing=4, prefix="q"
)
# An even number of atoms on the side won't align the center with an atom
assert rectangle.square_register(4) != Register.rectangular_lattice(
4, 4, col_spacing=2, row_spacing=4, prefix="q"
)
with pytest.raises(ValueError, match="'8x8' array doesn't fit"):
rectangle.square_register(8)
with pytest.raises(ValueError, match="'10x3' array doesn't fit"):
rectangle.rectangular_register(10, 3)


def test_triangular_lattice_layout():
tri = TriangularLatticeLayout(50, 5)
assert str(tri) == "TriangularLatticeLayout(50, 5.0µm)"
Expand Down