Skip to content

Commit

Permalink
Rewrote CubicIntegerLattice as subclass of Coordinates
Browse files Browse the repository at this point in the history
  • Loading branch information
timbernat committed Apr 26, 2024
1 parent 564bebc commit 81a811d
Showing 1 changed file with 9 additions and 56 deletions.
65 changes: 9 additions & 56 deletions polymerist/maths/lattices/integer.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
'''Core tools for manipulating integer lattices in N-dimensions'''

from typing import Iterable
from ...genutils.typetools.numpytypes import Shape, N, M
from ...genutils.typetools.categorical import ListLike

import numpy as np
from itertools import product as cartesian_product

from ...genutils.typetools.numpytypes import Shape, N, M
from .coordinates import Coordinates


def generate_int_lattice(*dims : Iterable[int]) -> np.ndarray[Shape[M, N], int]:
Expand All @@ -17,12 +19,13 @@ def generate_int_lattice(*dims : Iterable[int]) -> np.ndarray[Shape[M, N], int]:
dtype=np.dtype((int, len(dims)))
)

class CubicIntegerLattice:
# TODO : implement enumeration of integral points within an N-simplex

class CubicIntegerLattice(Coordinates):
'''For representing an n-dimensional integer lattice, consisting of all n-tuples of integers with values constrained by side lengths in each dimension'''
def __init__(self, sidelens : np.ndarray[Shape[N], int]) -> None: # TODO: implement more flexible input support (i.e. star-unpacking, listlikes, etc.)
assert(sidelens.ndim == 1)
self.sidelens = sidelens # ordered vector of the number of points along each dimension
self.points : np.ndarray[Shape[M, N], int] = generate_int_lattice(*self.sidelens)
super().__init__(generate_int_lattice(*self.sidelens))

def sidelens_as_str(self, multip_char : str='x') -> str:
'''Stringify the lattice sidelengths'''
Expand All @@ -32,50 +35,16 @@ def __repr__(self) -> str:
return f'{self.__class__.__name__}({self.n_dims}-dimensional, {self.sidelens_as_str()})'

# LATTICE DIMENSIONS
@property
def n_dims(self) -> int: # referred to as "N" in typehints
'''The number of dimensions of the lattice'''
return self.sidelens.size

@property
def capacity(self) -> int: # referred to as "M" in typehints
'''The maximum number of points that the lattice could contains'''
return np.prod(self.sidelens)

@property
def n_points(self) -> int:
'''The actual number of points currently contained in the lattice'''
return self.points.shape[0]

# LEXICOGRAPHIC ORDERING
def __call__(self, index : int) -> np.ndarray[Shape[N], int]:
'''Retrieve the point at the given index'''
return self.points[index]

@property
def lex_ordered_weights(self) -> np.ndarray[Shape[N], int]:
'''Vector of the number of points corresponding
Can be viewed as a linear transformation between indices and point coordinates when coords are placed in lexicographic order'''
return np.concatenate(([1], np.cumprod(self.sidelens)[:-1]))

@property
def lex_ordered_idxs(self) -> np.ndarray[Shape[M, N], int]:
'''Returns a vector of the position that each point in self.points occupies when ordered lexicographically'''
return np.lexsort(self.points.T)

@property
def lex_ordered_points(self) -> np.ndarray[Shape[M, N], int]:
'''Return copy of the points in the lattice in lexicographic order'''
return self.points[self.lex_ordered_idxs]

# IN-PLACE POINT REORDERING
def lex_order_points(self) -> None:
'''Sort points in the lattice in lexicographic order'''
self.points = self.lex_ordered_points

def randomize_points(self) -> None:
'''Place the points in the lattice in a random order'''
np.random.shuffle(self.points)

# SUBLATTICE DECOMPOSITION
@property
Expand Down Expand Up @@ -106,20 +75,4 @@ def odd_sublattice(self) -> np.ndarray[Shape[M, N], int]:
def even_sublattice(self) -> np.ndarray[Shape[M, N], int]:
'''Returns points within the even sublattice of the lattice points'''
return self.points[self.even_idxs]
even_points = even_sublattice # alias for convenience

# LATTICE TRANSFORMATIONS
def linear_transformation(self, matrix : np.ndarray[Shape[N,N], float], periodic : bool=False) -> np.ndarray[Shape[M, N], float]:
'''Accepts an NxN matrix (where N is the dimension of the lattice)
Returns a linearly-transformed copy of the points currently in the lattice'''
assert(matrix.shape == (self.n_dims, self.n_dims))
return self.points @ matrix.T # NOTE: need to right-multiply and transpose, since ROWS of self.points need to be tranformed

def affine_transformation(self, matrix : np.ndarray[Shape[N,N], float], periodic : bool=False) -> np.ndarray[Shape[M, N], float]: # TOSELF: typehint on input matrix should be of shape N+1, N+1
'''Accepts an (N+1)x(N+1) matrix (where N is the dimension of the lattice)
Returns an affine-transformed copy of the points currently in the lattice'''
assert(matrix.shape == (self.n_dims + 1, self.n_dims + 1))
aug_points = np.concatenate([self.points, np.ones((self.n_points, 1), dtype=int)], axis=1) # augment points vectors with extra columns of ones
aug_transformed = aug_points @ matrix.T

return aug_transformed[: , :self.n_dims] / aug_transformed[:, self.n_dims, None] # downcast augmented transformed points from homogeneous coordinates, normalizing by projective part
even_points = even_sublattice # alias for convenience

0 comments on commit 81a811d

Please sign in to comment.