From 81a811dd30aa91ad819d0c7d38587d98daab4a06 Mon Sep 17 00:00:00 2001 From: Timotej Bernat Date: Thu, 25 Apr 2024 19:51:58 -0600 Subject: [PATCH] Rewrote CubicIntegerLattice as subclass of Coordinates --- polymerist/maths/lattices/integer.py | 65 ++++------------------------ 1 file changed, 9 insertions(+), 56 deletions(-) diff --git a/polymerist/maths/lattices/integer.py b/polymerist/maths/lattices/integer.py index ee81a18..a4cf4d7 100644 --- a/polymerist/maths/lattices/integer.py +++ b/polymerist/maths/lattices/integer.py @@ -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]: @@ -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''' @@ -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 @@ -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 \ No newline at end of file + even_points = even_sublattice # alias for convenience \ No newline at end of file