Skip to content
Open
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
34 changes: 34 additions & 0 deletions minorminer/_lattice_utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2025 D-Wave
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ================================================================================================

"""
Tools for embedding algorithms of lattices on Zephyr-topology graphs.

This package includes:

1. Auxiliary coordinate types to work with Zephyr nodes, external paths, and quotient external paths.
2. A chain object representing a pair of Zephyr nodes—one vertical and one horizontal—
typically used to form chain-length-2 embeddings of lattices on Zephyr.
3. A graph object whose nodes are the chains described above, enabling
connectivity checks based on z-coupling.
4. A lattice survey module that provides utilities for embedding lattices
on partially-yielded Zephyr graphs.
5. A qoutient tile object to facilitate embedding lattice-type graphs on Zephyr.
"""

from minorminer._lattice_utils.auxiliary_coordinates import *
from minorminer._lattice_utils.survey import *
from minorminer._lattice_utils.qtile import *
113 changes: 113 additions & 0 deletions minorminer/_lattice_utils/auxiliary_coordinates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright 2025 D-Wave
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ================================================================================================

"""
Specialized coordinate types for embedding-related algorithms on Zephyr topology.

These classes represent structured combinations of fields (u, w, k, j, z),
with each subclass omitting fields that are irrelevant to a specific context.
"""

from __future__ import annotations

from typing import Iterator

from minorminer.utils.zephyr.coordinate_systems import ZephyrCoord

__all__ = ["UWKJZ", "UWKJ", "UWJ"]


class UWKJZ(ZephyrCoord):
"""A subclass of ZephyrCoord that provides convenient representations and helpers for embedding-related algorithms.
It captures the full coordinate of a Zephyr node.

Example:
>>> from burnaby.lattice_embedding.auxiliary_coordinates import UWKJZ
>>> node = UWKJZ(u=0, w=2, k=3, j=1, z=0)
>>> external_path = node.uwkj
>>> quotient_external_path = node.uwj
>>> print(f"The node {node} lies on the external path {external_path}, and belongs to the quotient external path {quotient_external_path}.")
The node UWKJZ(u=0, w=2, k=3, j=1, z=0) lies on the external path UWKJ(u=0, w=2, k=3, j=1), and belongs to the quotient external path UWJ(u=0, w=2, j=1).
"""

_fields = ("u", "w", "k", "j", "z")

@property
def uwkj(self) -> UWKJ:
"""Returns the ``UWKJ`` object containing the (u, w, k, j) fields of the coordinate."""
return UWKJ(u=self.u, w=self.w, k=self.k, j=self.j)

@property
def uwj(self) -> UWJ:
"""Returns the ``UWJ`` object containing the (u, w, j) fields of the coordinate."""
return UWJ(u=self.u, w=self.w, j=self.j)

def to_tuple(self) -> tuple[int]:
"""Rerurns the tuple corresponding to the coordinate."""
return (self.u, self.w, self.k, self.j, self.z)

def __iter__(self) -> Iterator[int]:
return (
getattr(self, f)
for f in self._fields
# if getattr(self, f) is not None
)

def __repr__(self) -> str:
return f"{type(self).__name__}(u={self.u}, w={self.w}, k={self.k}, j={self.j}, z={self.z})"


class UWKJ(UWKJZ):
"""A specialization of ``UWKJZ`` that includes fields u, w, k, and j.

It omits z, and so captures `external paths` in Zephyr topology.

Provides convenient representations and helpers for embedding-related algorithms.
Example:
>>> from burnaby.lattice_embedding.auxiliary_coordinates import UWKJ
>>> external_path = UWKJ(u=0, w=6, k=2, j=1)
>>> quotient_external_path = external_path.uwj
>>> print(f"The external path {external_path} belongs to the quotient external path {quotient_external_path}.")
The external path UWKJ(u=0, w=6, k=2, j=1) belongs to the quotient external path UWJ(u=0, w=6, j=1).
"""

_fields = ("u", "w", "k", "j")

def to_tuple(self) -> tuple[int]:
"""Rerurns the tuple corresponding to the coordinate."""
return (self.u, self.w, self.k, self.j)

def __repr__(self) -> str:
return f"{type(self).__name__}(u={self.u}, w={self.w}, k={self.k}, j={self.j})"


class UWJ(UWKJ):
"""A specialization of ``UWKJ`` that includes fields u, w, and j.

It omits both z and k from the full coordinate and is meant to capture a `quotient of external paths` in Zephyr topology,
in the sense that many ``UWKJ``s can map to the same ``UWJ``.

Provides convenient representations and helpers for embedding-related algorithms.
"""

_fields = ("u", "w", "j")

def to_tuple(self) -> tuple[int]:
"""Rerurns the tuple corresponding to the coordinate."""
return (self.u, self.w, self.j)

def __repr__(self) -> str:
return f"{type(self).__name__}(u={self.u}, w={self.w}, j={self.j})"
130 changes: 130 additions & 0 deletions minorminer/_lattice_utils/qtile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Copyright 2025 D-Wave Systems Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ================================================================================================


from __future__ import annotations

from typing import Callable, Iterable, Iterator, NamedTuple

from minorminer.utils.zephyr.coordinate_systems import *
from minorminer.utils.zephyr.node_edge import EdgeKind, ZEdge, ZNode
from minorminer.utils.zephyr.plane_shift import ZPlaneShift
from minorminer.utils.zephyr.qfloor import QuoTile

__all__ = ["QuoTile", "QuoFloor"]


class QuoTile:
"""Initializes a 'QuoTile' instance from a collection of ``ZNode`` objects.

Args:
zns (Iterable[ZNode]): The ``ZNode``s the tile contains.

Example:
.. code-block:: python
>>> from minorminer.utils.zephyr.node_edge import ZNode
>>> from minorminer.utils.zephyr.qfloor import QuoTile
>>> ccoords = [(k, 1+k) for k in range(4)] + [(1+k, k) for k in range(4)]
>>> zns = [ZNode(coord=ccoord) for ccoord in ccoords]
>>> tile = QuoTile(zns)
>>> print(f"{tile.edges() = }\n{tile.seed = }\n{tile.shifts = }")
"""

def __init__(
self,
zns: Iterable[ZNode],
) -> None:
if len(zns) == 0:
raise ValueError(f"Expected zns to be non-empty, got {zns}")
for zn in zns:
if not zn.is_quo():
raise ValueError(f"Expected elements of {zns} to be quotient, got {zn}")
zns_shape = {zn.shape for zn in zns}
if len(zns_shape) != 1:
raise ValueError(
f"Expected all elements of zns to have the same shape, got {zns_shape}"
)
temp_zns = sorted(list(set(zns)))
seed_convert = temp_zns[0].convert_to_z
zns_result = []
for zn in temp_zns:
zn.convert_to_z = seed_convert
zns_result.append(zn)
self._zns: list[ZNode] = zns_result

@property
def zns(self) -> list[ZNode]:
"""Returns the sorted list of :class:`ZNode`s the tile contains."""
return self._zns

def edges(
self,
nbr_kind: EdgeKind | Iterable[EdgeKind] | None = None,
where: Callable[[CartesianCoord | ZephyrCoord], bool] = lambda coord: True,
) -> list[ZEdge]:
"""Returns the list of edges of the graph induced on the tile, when restricted by ``nbr_kind`` and ``where``.

Args:
nbr_kind (EdgeKind | Iterable[EdgeKind] | None, optional):
Edge kind filter. Restricts returned edges to those having the given edge kind(s).
If None, no filtering is applied. Defaults to None.
where (Callable[[CartesianCoord | ZephyrCoord], bool]):
A coordinate filter. Applies to ``ccoord`` if ``self.convert_to_z`` is ``False``,
or to ``zcoord`` if :py:attr:`self.convert_to_z` is ``True``. Defaults to always.

Returns:
list[ZEdge]: List of edges of the graph induced on the tile, when restricted by ``nbr_kind`` and ``where``.
"""
zns = self._zns
tile_coords = [zn.zcoord for zn in zns] if self.convert_to_z else [zn.ccoord for zn in zns]
where_tile = lambda coord: where(coord) and coord in tile_coords
_edges = {
edge for zn in zns for edge in zn.incident_edges(nbr_kind=nbr_kind, where=where_tile)
}
return list(_edges)

def __len__(self) -> int:
return len(self._zns)

def __iter__(self) -> Iterator[ZNode]:
for zn in self._zns:
yield zn

def __getitem__(self, key) -> ZNode:
return self._zns[key]

def __hash__(self) -> int:
return hash(self._zns)

def __eq__(self, other: QuoTile) -> bool:
return self._zns == other._zns

def __add__(self, shift: ZPlaneShift) -> QuoTile:
return QuoTile(zns=[zn + shift for zn in self._zns])

def __repr__(self) -> str:
return f"{type(self).__name__}{self._zns!r}"

def __str__(self) -> str:
return f"{type(self).__name__}{self._zns}"



class QuoFloor(NamedTuple):
tile: QuoTile
dim: tuple[int, int]
"""A helper object with a tile representing the top left corner tile and 2-d dimension
"""
Loading