Skip to content

Commit

Permalink
minor update
Browse files Browse the repository at this point in the history
  • Loading branch information
k-tamuraphys committed Nov 30, 2021
1 parent 28836ab commit 531b885
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 326 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"""
from .lattice import (
BoundaryCondition,
DrawStyle,
LatticeDrawStyle,
HyperCubicLattice,
Lattice,
LineLattice,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""A class containing information about the Lattice."""
from .boundary_condition import BoundaryCondition
from .hyper_cubic_lattice import HyperCubicLattice
from .lattice import DrawStyle, Lattice
from .lattice import LatticeDrawStyle, Lattice
from .line_lattice import LineLattice
from .square_lattice import SquareLattice
from .triangular_lattice import TriangularLattice
Original file line number Diff line number Diff line change
Expand Up @@ -18,157 +18,10 @@
import numpy as np
from retworkx import PyGraph

from .lattice import DrawStyle, Lattice
from .lattice import LatticeDrawStyle, Lattice
from .boundary_condition import BoundaryCondition


def _coordinate_to_index(coord: np.ndarray, size: Tuple[int, ...]) -> int:
"""Convert the coordinate of a lattice point to an integer for labeling.
When size=(l0, l1, l2, ...), then a coordinate (x0, x1, x2, ...) is converted as
x0 + x1*l0 + x2*l0*l1 + ... .
Args:
coord: Input coordinate to be converted.
size: Lengths of each dimension.
Returns:
int: Return x0 + x1*l0 + x2*l0*l1 + ...
when coord=np.array([x0, x1, x2...]) and size=(l0, l1, l2, ...).
"""
dim = len(size)
base = np.array([np.prod(size[:i]) for i in range(dim)], dtype=int)
return np.dot(coord, base).item()


def _self_loops(size: Tuple[int, ...], onsite_parameter: complex) -> List[Tuple[int, int, complex]]:
"""Return a list consisting of the self-loops on all the nodes.
Args:
size : Lengths of each dimension.
onsite_parameter: Weight of the self-loops.
Returns:
List[Tuple[int, int, complex]] : List of the self-loops.
"""
num_nodes = np.prod(size)
return [(node_a, node_a, onsite_parameter) for node_a in range(num_nodes)]


def _bulk_edges(
size: Tuple[int, ...], edge_parameter: Tuple[complex, ...]
) -> List[Tuple[int, int, complex]]:
"""Return a list consisting of the edges in the bulk, which don't cross the boundaries.
Args:
size : Lengths of each dimension.
edge_parameter : Weights on the edges in each direction.
Returns:
List[Tuple[int, int, complex]] : List of weighted edges that don't cross the boundaries.
"""
list_of_edges = []
dim = len(size)
coordinates = list(product(*map(range, size)))
# add edges excluding the boundary edges
for coord in np.array(coordinates):
for i in range(dim):
if coord[i] != size[i] - 1:
relative_vector = np.eye(dim, dtype=int)[i]
node_a = _coordinate_to_index(coord, size)
node_b = _coordinate_to_index(coord + relative_vector, size)
list_of_edges.append((node_a, node_b, edge_parameter[i]))
return list_of_edges


def _boundary_edges(
size: Tuple[int, ...],
edge_parameter: Tuple[complex, ...],
boundary_condition: Tuple[BoundaryCondition, ...],
) -> List[Tuple[int, int, complex]]:
"""Return a list consisting of the edges that cross the boundaries
depending on the boundary conditions.
Args:
size : Lengths of each dimension.
edge_parameter : Weights on the edges in each direction.
boundary_condition : Boundary condition for each dimension.
The available boundary conditions are: BoundaryCondition.OPEN and BoundaryCondition.PERIODIC.
Raises:
ValueError: Given boundary condition is invalid values.
Returns:
List[Tuple[int, int, complex]]: List of weighted edges that cross the boundaries.
"""
list_of_edges = []
dim = len(size)
for i in range(dim):
# add edges when the boundary condition is periodic.
# when the boundary condition in the i-th direction is periodic,
# it makes sense only when size[i] is greater than 2.
if boundary_condition[i] == BoundaryCondition.PERIODIC:
if size[i] <= 2:
continue
size_list = list(size)
size_list[i] = 1
coordinates = list(product(*map(range, size_list)))
relative_vector = np.eye(dim, dtype=int)[i]
for coord in np.array(coordinates):
node_b = _coordinate_to_index(coord, size)
node_a = _coordinate_to_index((coord - relative_vector) % size, size)
list_of_edges.append((node_b, node_a, edge_parameter[i].conjugate()))
elif boundary_condition[i] == BoundaryCondition.OPEN:
continue
else:
raise ValueError(
f"Invalid `boundary condition` {boundary_condition[i]} is given."
"`boundary condition` must be " + " or ".join(str(bc) for bc in BoundaryCondition)
)
return list_of_edges


def _default_position(
size: Tuple[int, ...], boundary_condition: Tuple[BoundaryCondition, ...]
) -> Optional[Dict[int, List[float]]]:
"""Return a dictionary of default positions for visualization of a one- or two-dimensional lattice.
Args:
size : Lengths of each dimension.
boundary_condition : Boundary condition for each dimension.
The available boundary conditions are: BoundaryCondition.OPEN and BoundaryCondition.PERIODIC.
Returns:
Optional[Dict[int, List[float]]]: The keys are the labels of lattice points,
and the values are two-dimensional coordinates.
When the dimension is larger than 2, it returns None.
"""
dim = len(size)
if dim == 1:
if boundary_condition[0] == BoundaryCondition.OPEN:
pos = {i: [float(i), 0.0] for i in range(size[0])}
elif boundary_condition[0] == BoundaryCondition.PERIODIC:
theta = 2 * pi / size[0]
pos = {
i: np.array([np.cos(i * theta), np.sin(i * theta)]).tolist() for i in range(size[0])
}
elif dim == 2:
pos = {}
width = np.array([0.0, 0.0])
for i in (0, 1):
if boundary_condition[i] == BoundaryCondition.PERIODIC:
# the positions are shifted along the y-direction
# when the boundary condition in the x-direction is periodic and vice versa.
# The width of the shift is fixed to 0.2.
width[(i + 1) % 2] = 0.2
for index in range(np.prod(size)):
# maps an index to two-dimensional coordinate
# the positions are shifted so that the edges between boundaries can be seen
# for the periodic cases.
coord = np.array(divmod(index, size[0]))[::-1] + width * np.sin(
pi * np.array(divmod(index, size[0])) / (np.array(size)[::-1] - 1)
)
pos[index] = coord.tolist()
else:
pos = None

return pos


class HyperCubicLattice(Lattice):
"""Hyper-cubic lattice in d dimensions.
Expand All @@ -195,6 +48,133 @@ class HyperCubicLattice(Lattice):
The boundary conditions are open for all the directions.
"""

def _coordinate_to_index(self, coord: np.ndarray) -> int:
"""Convert the coordinate of a lattice point to an integer for labeling.
When size=(l0, l1, l2, ...), then a coordinate (x0, x1, x2, ...) is converted as
x0 + x1*l0 + x2*l0*l1 + ... .
Args:
coord: Input coordinate to be converted.
Returns:
int: Return x0 + x1*l0 + x2*l0*l1 + ...
when coord=np.array([x0, x1, x2...]) and size=(l0, l1, l2, ...).
"""
size = self.size
dim = len(size)
base = np.array([np.prod(size[:i]) for i in range(dim)], dtype=int)
return np.dot(coord, base).item()

def _self_loops(self) -> List[Tuple[int, int, complex]]:
"""Return a list consisting of the self-loops on all the nodes.
Returns:
List[Tuple[int, int, complex]] : List of the self-loops.
"""
num_nodes = np.prod(self.size)
return [(node_a, node_a, self.onsite_parameter) for node_a in range(num_nodes)]

def _bulk_edges(self) -> List[Tuple[int, int, complex]]:
"""Return a list consisting of the edges in the bulk, which don't cross the boundaries.
Returns:
List[Tuple[int, int, complex]] : List of weighted edges that don't cross the boundaries.
"""
list_of_edges = []
size = self.size
edge_parameter = self.edge_parameter
dim = len(size)
coordinates = list(product(*map(range, size)))
# add edges excluding the boundary edges
for coord in np.array(coordinates):
for i in range(dim):
if coord[i] != size[i] - 1:
relative_vector = np.eye(dim, dtype=int)[i]
node_a = self._coordinate_to_index(coord)
node_b = self._coordinate_to_index(coord + relative_vector)
list_of_edges.append((node_a, node_b, edge_parameter[i]))
return list_of_edges

def _boundary_edges(self) -> List[Tuple[int, int, complex]]:
"""Return a list consisting of the edges that cross the boundaries
depending on the boundary conditions.
Raises:
ValueError: Given boundary condition is invalid values.
Returns:
List[Tuple[int, int, complex]]: List of weighted edges that cross the boundaries.
"""
list_of_edges = []
size = self.size
edge_parameter = self.edge_parameter
boundary_condition = self.boundary_condition
dim = len(size)
for i in range(dim):
# add edges when the boundary condition is periodic.
# when the boundary condition in the i-th direction is periodic,
# it makes sense only when size[i] is greater than 2.
if boundary_condition[i] == BoundaryCondition.PERIODIC:
if size[i] <= 2:
continue
size_list = list(size)
size_list[i] = 1
coordinates = list(product(*map(range, size_list)))
relative_vector = np.eye(dim, dtype=int)[i]
for coord in np.array(coordinates):
node_b = self._coordinate_to_index(coord)
node_a = self._coordinate_to_index((coord - relative_vector) % size)
list_of_edges.append((node_b, node_a, edge_parameter[i].conjugate()))
elif boundary_condition[i] == BoundaryCondition.OPEN:
continue
else:
raise ValueError(
f"Invalid `boundary condition` {boundary_condition[i]} is given."
"`boundary condition` must be "
+ " or ".join(str(bc) for bc in BoundaryCondition)
)
return list_of_edges

def _default_position(self) -> Optional[Dict[int, List[float]]]:
"""Return a dictionary of default positions for visualization of
a one- or two-dimensional lattice.
Returns:
Optional[Dict[int, List[float]]]: The keys are the labels of lattice points,
and the values are two-dimensional coordinates.
When the dimension is larger than 2, it returns None.
"""
size = self.size
boundary_condition = self.boundary_condition
dim = len(size)
if dim == 1:
if boundary_condition[0] == BoundaryCondition.OPEN:
pos = {i: [float(i), 0.0] for i in range(size[0])}
elif boundary_condition[0] == BoundaryCondition.PERIODIC:
theta = 2 * pi / size[0]
pos = {
i: np.array([np.cos(i * theta), np.sin(i * theta)]).tolist()
for i in range(size[0])
}
elif dim == 2:
pos = {}
width = np.array([0.0, 0.0])
for i in (0, 1):
if boundary_condition[i] == BoundaryCondition.PERIODIC:
# the positions are shifted along the y-direction
# when the boundary condition in the x-direction is periodic and vice versa.
# The width of the shift is fixed to 0.2.
width[(i + 1) % 2] = 0.2
for index in range(np.prod(size)):
# maps an index to two-dimensional coordinate
# the positions are shifted so that the edges between boundaries can be seen
# for the periodic cases.
coord = np.array(divmod(index, size[0]))[::-1] + width * np.sin(
pi * np.array(divmod(index, size[0])) / (np.array(size)[::-1] - 1)
)
pos[index] = coord.tolist()
else:
pos = None

return pos

def __init__(
self,
size: Tuple[int, ...],
Expand Down Expand Up @@ -262,17 +242,15 @@ def __init__(
graph.add_nodes_from(range(np.prod(size)))

# add edges excluding the boundary edges
bulk_edge_list = _bulk_edges(self.size, self.edge_parameter)
bulk_edge_list = self._bulk_edges()
graph.add_edges_from(bulk_edge_list)

# add self-loops.
self_loop_list = _self_loops(self.size, self.onsite_parameter)
self_loop_list = self._self_loops()
graph.add_edges_from(self_loop_list)

# add edges that cross the boundaries
boundary_edge_list = _boundary_edges(
self.size, self.edge_parameter, self.boundary_condition
)
boundary_edge_list = self._boundary_edges()
graph.add_edges_from(boundary_edge_list)

# a list of edges that depend on the boundary condition
Expand All @@ -281,12 +259,12 @@ def __init__(
super().__init__(graph)

# default position for one and two-dimensional cases.
self.pos = _default_position(size, boundary_condition)
self.pos = self._default_position()

def draw_without_boundary(
self,
self_loop: bool = False,
style: Optional[DrawStyle] = None,
style: Optional[LatticeDrawStyle] = None,
):
r"""Draw the lattice with no edges between the boundaries.
Expand All @@ -304,9 +282,9 @@ def draw_without_boundary(
graph = self.graph

if style is None:
style = DrawStyle()
elif not isinstance(style, DrawStyle):
style = DrawStyle(**style)
style = LatticeDrawStyle()
elif not isinstance(style, LatticeDrawStyle):
style = LatticeDrawStyle(**style)

if style.pos is None:
style.pos = self.pos
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@


@dataclass
class DrawStyle:
class LatticeDrawStyle:
"""A stylesheet for lattice figure.
Please see
https://qiskit.org/documentation/retworkx/stubs/retworkx.visualization.mpl_draw.html#retworkx.visualization.mpl_draw
Expand Down Expand Up @@ -227,7 +227,7 @@ def _mpl(graph: PyGraph, self_loop: bool, **kwargs):
def draw(
self,
self_loop: bool = False,
style: Optional[DrawStyle] = None,
style: Optional[LatticeDrawStyle] = None,
):
"""Draw the lattice.
Expand All @@ -242,9 +242,9 @@ def draw(
graph = self.graph

if style is None:
style = DrawStyle()
elif not isinstance(style, DrawStyle):
style = DrawStyle(**style)
style = LatticeDrawStyle()
elif not isinstance(style, LatticeDrawStyle):
style = LatticeDrawStyle(**style)

if style.pos is None:
style.pos = self.pos
Expand Down
Loading

0 comments on commit 531b885

Please sign in to comment.