Source code for tightbinder.utils

"""
Miscellaneous routines used in the other modules.
"""


from typing import Callable, Union
import numpy as np
from scipy.spatial import Delaunay
import hashlib


[docs] def generate_basis_combinations(ndim: int) -> np.ndarray: """ Routine to generate the coefficients corresponding to linear combinations of some basis vectors. Possible coefficients for each vector are -1, 0 and 1. :param ndim: Dimension of the basis. :return: Matrix where each row are the coefficients for a given combination of the basis vectors, i.e. [c1, c2, ...] such that v = c1*basis_1 + c2*basis_2 + ... """ mesh_points = [] if ndim == 0: return mesh_points for i in range(ndim): mesh_points.append(list(range(-1, 2))) mesh_points = np.array(np.meshgrid(*mesh_points)).T.reshape(-1, ndim) return mesh_points
[docs] def condense_vector(vector: Union[list, np.ndarray], step: int) -> np.ndarray: """ Routine to reduce the dimensionality of a given vector by summing each consecutive n (step) numbers. :param vector: Array to be reduced :param step: Number of components to sum :returns: Condensed array """ if (len(vector) % step) != 0: raise ArithmeticError("Step must divide vector length") if step > len(vector): raise ArithmeticError("Step can not be larger than vector length") reduced_vector = [] for i in range(len(vector)//step): atom_amplitudes = vector[i*step:(i+1)*step] reduced_vector.append(np.sum(atom_amplitudes)) return np.array(reduced_vector)
[docs] def scale_array(array: Union[list, np.ndarray], factor: int = 10) -> np.ndarray: """ Routine to scale a vector by a factor max_value/max(vector), where max_value is the new maximum value. :param array: Array to scale. :param factor: Factor to multiply the array. Defaults to 10 :return: Scaled array. """ n = len(array) array = np.array(array) * n * factor return array
[docs] def pretty_print_dictionary(d: dict, indent: int = 0) -> None: """ Routine to pretty print the contents of a dictionary. Note that this is mainly used to indent recursively all dict. contents, so by default it should be left at zero. :param d: Dictionary to pretty print :param indent: Number of tabs to indent the contents of the dictionary. """ for key, value in d.items(): print('\t' * indent + str(key)) if isinstance(value, dict): pretty_print_dictionary(value, indent+1) else: print('\t' * (indent+1) + str(value))
[docs] def overrides(interface_class: type) -> Callable: """ Decorator to print whether a method is being overwritten or not. :param interface_class: Base class where the method to be overwritten is present. :return: Decorator. """ def overrider(method): assert(method.__name__ in dir(interface_class)) return method return overrider
[docs] def alpha_shape_2d(points: np.ndarray, alpha: float, only_outer: bool = True): """ Compute the alpha shape (concave hull) of a set of 2D points. From: https://stackoverflow.com/questions/50549128/boundary-enclosing-a-given-set-of-points :param points: np.array of shape (n, 2) points. :param alpha: alpha value. :param only_outer: boolean value to specify if we keep only the outer border or also inner edges. :return: set of (i,j) pairs representing edges of the alpha-shape. (i,j) are the indices in the points array. """ assert points.shape[0] > 3, "Need at least four points" def add_edge(edges, i, j): """ Add an edge between the i-th and j-th points, if not in the list already """ if (i, j) in edges or (j, i) in edges: # already added assert (j, i) in edges, "Can't go twice over same directed edge right?" if only_outer: # if both neighboring triangles are in shape, it's not a boundary edge edges.remove((j, i)) return edges.add((i, j)) tri = Delaunay(points) edges = set() # Loop over triangles: # ia, ib, ic = indices of corner points of the triangle for ia, ib, ic in tri.vertices: pa = points[ia] pb = points[ib] pc = points[ic] # Computing radius of triangle circumcircle # www.mathalino.com/reviewer/derivation-of-formulas/derivation-of-formula-for-radius-of-circumcircle a = np.sqrt((pa[0] - pb[0]) ** 2 + (pa[1] - pb[1]) ** 2) b = np.sqrt((pb[0] - pc[0]) ** 2 + (pb[1] - pc[1]) ** 2) c = np.sqrt((pc[0] - pa[0]) ** 2 + (pc[1] - pa[1]) ** 2) s = (a + b + c) / 2.0 area = np.sqrt(s * (s - a) * (s - b) * (s - c)) circum_r = a * b * c / (4.0 * area) if circum_r < alpha: add_edge(edges, ia, ib) add_edge(edges, ib, ic) add_edge(edges, ic, ia) return edges
[docs] def hash_numpy_array(array: np.ndarray) -> float: """ Function to hash a numpy array. """ # return hashlib.md5(array.data.tobytes()).hexdigest() # This is non deterministic. return np.sum(array ** 2)