from numpy import array as np_array
from .integer_lattice import IntegerLattice
from types import MethodType
[docs]
def Grid(dim, periodic=True, diagonal=False,
multiedges=None, weights=None, copy=False):
r"""
Two-dimensionsal grid constructor.
The grid can be designed with either
cyclic boundary conditions or borders.
Moreover, the grid's representation can be either
*natural* or *diagonal*.
In the *natural* representation,
neighboring vertices lie along the X and Y axes,
while in the *diagonal* representation,
they lie along the diagonals.
Parameters
----------
dim : int or tuple of int
Grid dimensions in ``(dim[0], dim[1])`` format,
where ``dim[0]`` is the number of vertices in the X-axis,
and ``dim[1]`` is the number of vertices in the Y-axis.
If ``dim`` is an integer, creates a square grid.
periodic : bool, default=True
``True`` if the grid has cyclic boundary conditions,
``False`` if it has borders.
diagonal : bool, default=False
``True`` if the grid has the diagonal representation,
``False`` if it has the natural representation.
multiedges, weights: matrix or dict, default=None
See :ref:`graph_constructors`.
copy : bool, default=False
See :ref:`graph_constructors`.
Returns
-------
:class:`hiperwalk.Graph`
See :ref:`graph_constructors` for details.
See Also
--------
:ref:`graph_constructors`.
Notes
-----
The **order of neighbors** depends on the grid.
.. testsetup::
import hiperwalk as hpw
Natural Grid:
The natural grid is created when ``diagonal=False``.
The neighbors are given in the following order.
* 00 = 0: right;
* 01 = 1: left;
* 10 = 2: up;
* 11 = 3: down.
The most significant bit corresponds to the axis:
0 represents the X-axis and 1 represents the Y-axis.
The least significant bit indicates the direction
along the given axis, with 0 signifying
forward and 1 signifying backward.
Consider a vertex :math:`(x, y)`. Then,
:math:`(x \pm 1, y)` and :math:`(x, y \pm 1)`
are adjacent vertices.
The order of neighbors is depicted in
:ref:`fig-natural-grid-order`.
.. graphviz:: ../../graphviz/grid/natural-grid-neigh-order.dot
:align: center
:layout: neato
:name: fig-natural-grid-order
:caption: Figure: The order of neighbors in the natural grid.
For example,
consider the :math:`3 \times 3` periodic natural grid
(:ref:`fig-3x3-natural-grid`).
.. graphviz:: ../../graphviz/grid/periodic-natural.dot
:align: center
:layout: neato
:name: fig-3x3-natural-grid
:caption: Figure: Periodic natural 3x3-grid.
The neighbors of :math:`(0, 0)` and :math:`(1, 1)` with
respect to the order of neighbors are
.. doctest::
>>> nat = hpw.Grid(3, diagonal=False, periodic=True)
>>> neigh = nat.neighbors((0, 0))
>>> [tuple(nat.vertex_coordinates(v)) for v in neigh]
[(1, 0), (2, 0), (0, 1), (0, 2)]
>>>
>>> neigh = nat.neighbors((1, 1))
>>> [tuple(nat.vertex_coordinates(v)) for v in neigh]
[(2, 1), (0, 1), (1, 2), (1, 0)]
Diagonal Grid:
The diagonal grid is created when ``diagonal=True``.
The neighbors are given in the following order.
* 00 = 0: right, up;
* 01 = 1: right, down;
* 10 = 2: left, up;
* 11 = 3: left, down.
Each binary value indicates the direction along
a given axis,
with 0 representing forward and 1 representing backward.
The most significant bit corresponds to the
direction along the X-axis,
while the least significant bit corresponds to the
direction along the Y-axis.
Consider a vertex :math:`(x, y)`. Then,
its four neighbors are :math:`(x \pm 1, y \pm 1)`.
The order of neighbors is depicted in
:ref:`fig-diagonal-grid-order`.
.. graphviz:: ../../graphviz/grid/diagonal-grid-neigh-order.dot
:align: center
:layout: neato
:name: fig-diagonal-grid-order
:caption: Figure: The order of neighbors in the diagonal grid.
For example,
consider the :math:`3 \times 3` periodic diagonal grid
(:ref:`fig-3x3-diagonal-grid`).
.. graphviz:: ../../graphviz/grid/periodic-diagonal.dot
:align: center
:layout: neato
:name: fig-3x3-diagonal-grid
:caption: Figure: Periodic diagonal 3x3-grid.
The neighbors of :math:`(0, 0)` and :math:`(1, 1)` with
respect to the order of neighbors are
.. doctest::
>>> diag = hpw.Grid(3, diagonal=True, periodic=True)
>>> neigh = diag.neighbors((0, 0))
>>> [tuple(diag.vertex_coordinates(v)) for v in neigh]
[(1, 1), (1, 2), (2, 1), (2, 2)]
>>>
>>> neigh = diag.neighbors((1, 1))
>>> [tuple(diag.vertex_coordinates(v)) for v in neigh]
[(2, 2), (2, 0), (0, 2), (0, 0)]
In the case of a diagonal grid with borders,
there exist two independent subgrids.
In other words, a vertex in one subgrid is not accessible from
a vertex in the other subgrid.
.. graphviz:: ../../graphviz/grid/bounded-diagonal.dot
:align: center
:layout: neato
:name: fig-bounded-diagonal-grid
:caption: Figure: Bounded 3x3-grid in the diagonal representation.
Two independent subgrids also occur if
the diagonal grid has periodic boundary conditions and both
dimensions are even.
Figure :ref:`fig-even-dim-diagonal` illustrates
an example of this case.
.. graphviz:: ../../graphviz/grid/even-dim-diagonal.dot
:align: center
:layout: neato
:name: fig-even-dim-diagonal
:caption: Figure: 4x4-grid with cyclic boundary conditions.
"""
try:
if len(dim) != 2:
raise ValueError("Expected 2-dimensional tuple. "
+ "Received " + str(dim)
+ " instead.")
except TypeError:
# then int
dim = (dim, dim)
if not diagonal:
basis = [1, -1, 2, -2]
else:
basis = np_array([[1, 1], [1, -1],
[-1, 1], [-1, -1]])
g = IntegerLattice(dim, basis=basis, periodic=periodic,
multiedges=multiedges, weights=weights,
copy=copy)
g.diagonal = diagonal
return g