From 183f2f9fb484c91d332f75ba8556aad737dc6185 Mon Sep 17 00:00:00 2001 From: Manuel Schrauth Date: Fri, 9 Sep 2022 09:34:38 +0200 Subject: [PATCH 1/4] =?UTF-8?q?first=20steps;=20fixing=20Weierstra=C3=9F?= =?UTF-8?q?=20transformations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hypertiling/representations.py | 40 ++++++++++++++++++++++ hypertiling/static/kernellegacydunham.py | 43 +++++++++++++++++------- 2 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 hypertiling/representations.py diff --git a/hypertiling/representations.py b/hypertiling/representations.py new file mode 100644 index 0000000..7c72e17 --- /dev/null +++ b/hypertiling/representations.py @@ -0,0 +1,40 @@ +import math +from hypertiling.check_numba import check_numba +from numpy import array as nparray + + + +@check_numba +def p2w(z): + '''Convert Poincare to Weierstraß representation ''' + x, y = z.real, z.imag + xx = x * x + yy = y * y + factor = 1 / (1 - xx - yy) + return factor * nparray([(1 + xx + yy), 2 * x, 2 * y]) + + +@check_numba +def w2p(point): + '''Convert Weierstraß to Poincare representation ''' + [t, x, y] = point + factor = 1 / (1 + t) + return complex(x * factor, y * factor) + + +@check_numba +def p2w_xyt(z): + '''Convert Poincare to Weierstraß representation ''' + x, y = z.real, z.imag + xx = x * x + yy = y * y + factor = 1 / (1 - xx - yy) + return factor * nparray([2 * x, 2 * y, (1 + xx + yy)]) + + +@check_numba +def w2p_xyt(point): + '''Convert Weierstraß to Poincare representation ''' + [x, y, t] = point + factor = 1 / (1 + t) + return complex(x * factor, y * factor) diff --git a/hypertiling/static/kernellegacydunham.py b/hypertiling/static/kernellegacydunham.py index eff71d2..df8269c 100644 --- a/hypertiling/static/kernellegacydunham.py +++ b/hypertiling/static/kernellegacydunham.py @@ -8,6 +8,18 @@ from ..transformation import p2w from ..arraytransformation import mfull_point from ..util import fund_radius +from ..representations import p2w_xyt, w2p_xyt + + + + +def transformW_poly(polygon, transformation): + for pointP in polygon.verticesP: + pointP = transformW_site(pointP, transformation) + +def transformW_site(pointP, transformation): + return w2p_xyt(transformation @ p2w_xyt(pointP)) + class KernelLegacyDunham(HyperbolicTilingBase): """ @@ -41,18 +53,24 @@ class KernelLegacyDunham(HyperbolicTilingBase): self.RotCenterG = np.eye(3) # G for usage in generate() self.RotCenterR = np.eye(3) # R for usage in replicate(...) - self.fund_poly = self.create_fundamental_polygon() - self.polygons = [self.fund_poly] + #self.fund_poly = self.create_fundamental_polygon() + + + + + + + - def create_fundamental_polygon(self): # constructs the verticesP of the fundamental hyperbolic {p,q} polygon - r = fund_radius(self.p, self.q) - polygon = HyperPolygon(self.p) - angle = np.pi / self.p - for i in range(self.p): # for every corner of the polygon - z = complex(r * np.cos(angle + 2 * np.pi * i / self.p), r * np.sin(angle + 2 * np.pi * i / self.p)) - polygon.verticesP[i] = z - polygon.verticesW[:, i] = p2w(z) - return polygon + # def create_fundamental_polygon(self): # constructs the verticesP of the fundamental hyperbolic {p,q} polygon + # r = fund_radius(self.p, self.q) + # polygon = HyperPolygon(self.p) + # angle = np.pi / self.p + # for i in range(self.p): # for every corner of the polygon + # z = complex(r * np.cos(angle + 2 * np.pi * i / self.p), r * np.sin(angle + 2 * np.pi * i / self.p)) + # polygon.verticesP[i] = z + # polygon.verticesW[:, i] = p2w(z) + #return polygon def generate(self): if self.nlayers == 1: @@ -69,7 +87,8 @@ class KernelLegacyDunham(HyperbolicTilingBase): def replicate(self, Polygons, InitialTran, LayersToDo, AdjacencyType): poly = copy.deepcopy(self.fund_poly) - poly.transform(InitialTran) + #poly.transform(InitialTran) + transformW_poly(poly,InitialTran) Polygons.append(poly) # appending anything and removing duplicates afterwards is faster ExposedEdges = 0 VertexPgons = 0 -- GitLab From 9173a60637156204c9d9c0e407fa29d27e3f234c Mon Sep 17 00:00:00 2001 From: Manuel Schrauth Date: Fri, 9 Sep 2022 10:21:14 +0200 Subject: [PATCH 2/4] fixing Dunham kernel required to move some minor things around and introduce new transformation function --- hypertiling/static/kernelbase.py | 19 ++++++++----------- hypertiling/static/kernellegacydunham.py | 10 +++++++--- hypertiling/static/kernelstaticfast.py | 9 ++++++--- hypertiling/static/kernelstaticprecise.py | 10 +++++++--- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/hypertiling/static/kernelbase.py b/hypertiling/static/kernelbase.py index 8e9c13d..aa033b2 100644 --- a/hypertiling/static/kernelbase.py +++ b/hypertiling/static/kernelbase.py @@ -7,6 +7,10 @@ from ..arraytransformation import mfull, mrotate, morigin from ..transformation import p2w, moeb_rotate_trafo, mymoeb from ..util import fund_radius +# Magic number: real irrational number \Gamma(\frac{1}{4}) +# used as an angular offset, rotates the entire construction by a bit during construction +MANGLE = 3.6256099082219083119306851558676720029951676828800654674333779995 + # the main object of this library # essentially represents a list of polygons which constitute the hyperbolic lattice class HyperbolicTilingBase: @@ -50,16 +54,9 @@ class HyperbolicTilingBase: # technical parameters # do not change, unless you know what you are doing!) self.degtol = 1 # sector boundary tolerance - - # angular offset, rotates the entire construction by a bit during construction - if center == "cell": - self.mangle = self.degphi/math.sqrt(5) - elif center == "vertex": - self.mangle = self.degqhi/math.sqrt(5) - - # fundamental polygon of the tiling - self.fund_poly = self.create_fundamental_polygon(center) + # # fundamental polygon of the tiling + # self.fund_poly = self.create_fundamental_polygon(center) # prepare list to store polygons self.polygons = [] @@ -131,7 +128,7 @@ class HyperbolicTilingBase: - def create_fundamental_polygon(self, center='cell'): + def create_fundamental_polygon(self, center='cell', rotate_by=MANGLE): """ Constructs the vertices of the fundamental hyperbolic {p,q} polygon @@ -161,7 +158,7 @@ class HyperbolicTilingBase: polygon.angle = math.degrees(math.atan2(polygon.verticesP[self.p].imag, polygon.verticesP[self.p].real)) polygon.angle += 360 if polygon.angle < 0 else 0 - mrotate(self.p, -2*math.pi/360*self.mangle, polygon.verticesP) + mrotate(self.p, -2*math.pi/360*rotate_by, polygon.verticesP) return polygon diff --git a/hypertiling/static/kernellegacydunham.py b/hypertiling/static/kernellegacydunham.py index df8269c..979a4fd 100644 --- a/hypertiling/static/kernellegacydunham.py +++ b/hypertiling/static/kernellegacydunham.py @@ -14,8 +14,11 @@ from ..representations import p2w_xyt, w2p_xyt def transformW_poly(polygon, transformation): - for pointP in polygon.verticesP: - pointP = transformW_site(pointP, transformation) + new_verts = np.zeros_like(polygon.verticesP) + for i, pointP in enumerate(polygon.verticesP): + new_verts[i] = transformW_site(pointP, transformation) + polygon.verticesP = new_verts + def transformW_site(pointP, transformation): return w2p_xyt(transformation @ p2w_xyt(pointP)) @@ -53,7 +56,8 @@ class KernelLegacyDunham(HyperbolicTilingBase): self.RotCenterG = np.eye(3) # G for usage in generate() self.RotCenterR = np.eye(3) # R for usage in replicate(...) - #self.fund_poly = self.create_fundamental_polygon() + # fundamental polygon of the tiling + self.fund_poly = self.create_fundamental_polygon(center, rotate_by=360/p/2) diff --git a/hypertiling/static/kernelstaticfast.py b/hypertiling/static/kernelstaticfast.py index 7f9a7ec..06b422c 100644 --- a/hypertiling/static/kernelstaticfast.py +++ b/hypertiling/static/kernelstaticfast.py @@ -8,12 +8,14 @@ from .hyperpolygon import HyperPolygon from ..transformation import p2w, moeb_rotate_trafo from ..arraytransformation import mfull_point from ..distance import disk_distance +from .kernelbase import MANGLE class KernelStaticFast(KernelCommon): """ Tiling construction algorithm written by M. Schrauth and F. Dusel """ def __init__ (self, p, q, n, center): super(KernelStaticFast, self).__init__(p, q, n, center) + self.center = center self.dgts = 8 self.accuracy = 10**(-self.dgts) # numerical accuracy @@ -30,6 +32,7 @@ class KernelStaticFast(KernelCommon): self.polygons = [] # add fundamental polygon to list + self.fund_poly = self.create_fundamental_polygon(self.center) self.polygons.append(self.fund_poly) # angle width of the fundamental sector @@ -67,7 +70,7 @@ class KernelStaticFast(KernelCommon): # cut away cells outside the fundamental sector # allow some tolerance at the upper boundary - if self.mangle-1e-14 <= cangle < sect_angle_deg+self.degtol+self.mangle: + if MANGLE-1e-14 <= cangle < sect_angle_deg+self.degtol+MANGLE: # try adding to centerlist; it is a set() and takes care of duplicates center = np.round(center, self.dgts) @@ -88,7 +91,7 @@ class KernelStaticFast(KernelCommon): self.polygons.append(adj_pgon) # if angle is in slice, add to centerset_extra - if self.mangle-1e-14 <= cangle <= self.degtol+self.mangle: + if MANGLE-1e-14 <= cangle <= self.degtol+MANGLE: centerset_extra.add(center) startpgon = endpgon @@ -112,7 +115,7 @@ class KernelStaticFast(KernelCommon): # filter out rotational duplicates deletelist = [] for kk, pgon in enumerate(self.polygons): - if pgon.angle > sect_angle_deg-self.degtol+self.mangle: + if pgon.angle > sect_angle_deg-self.degtol+MANGLE: center = moeb_rotate_trafo(-sect_angle, pgon.centerP()) diff --git a/hypertiling/static/kernelstaticprecise.py b/hypertiling/static/kernelstaticprecise.py index 47aa4c5..7f0d5c0 100644 --- a/hypertiling/static/kernelstaticprecise.py +++ b/hypertiling/static/kernelstaticprecise.py @@ -8,6 +8,7 @@ from ..transformation import moeb_rotate_trafo from ..arraytransformation import mfull_point from ..util import fund_radius from .kernelstaticprecise_util import CenterContainer +from .kernelbase import MANGLE class KernelStaticPrecise(KernelCommon): @@ -17,6 +18,8 @@ class KernelStaticPrecise(KernelCommon): def __init__(self, p, q, n, center): super(KernelStaticPrecise, self).__init__(p, q, n, center) + self.center = center + def generate_sector(self): """ @@ -30,6 +33,7 @@ class KernelStaticPrecise(KernelCommon): self.polygons = [] # add fundamental polygon to list + self.fund_poly = self.create_fundamental_polygon(self.center) self.polygons.append(self.fund_poly) # angle width of the fundamental sector @@ -73,7 +77,7 @@ class KernelStaticPrecise(KernelCommon): # cut away cells outside the fundamental sector # allow some tolerance at the upper boundary - if (self.mangle <= cangle < sect_angle_deg + self.degtol + self.mangle) and (abs(center) > fr): + if (MANGLE <= cangle < sect_angle_deg + self.degtol + MANGLE) and (abs(center) > fr): if not centerarray.fp_has(center): centerarray.add(center) @@ -87,7 +91,7 @@ class KernelStaticPrecise(KernelCommon): self.polygons.append(adj_pgon) # if angle is in slice, add to centerset_extra - if self.mangle <= cangle <= self.degtol + self.mangle: + if MANGLE <= cangle <= self.degtol + MANGLE: if not centerset_extra.fp_has(center): centerset_extra.add(center) @@ -103,7 +107,7 @@ class KernelStaticPrecise(KernelCommon): for kk, pgon in enumerate(self.polygons): angle = math.degrees(math.atan2(pgon.verticesP[self.p].imag, pgon.verticesP[self.p].real)) angle += 360 if angle < 0 else 0 - if angle > sect_angle_deg - self.degtol + self.mangle: + if angle > sect_angle_deg - self.degtol + MANGLE: center = moeb_rotate_trafo(-sect_angle, pgon.verticesP[self.p]) if centerset_extra.fp_has(center): deletelist.append(kk) -- GitLab From df6053108ff525b1ebe54780c379eb7300db763b Mon Sep 17 00:00:00 2001 From: Manuel Schrauth Date: Fri, 9 Sep 2022 10:27:40 +0200 Subject: [PATCH 3/4] add warnings --- hypertiling/core.py | 9 +++------ hypertiling/static/kernellegacydunham.py | 2 -- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/hypertiling/core.py b/hypertiling/core.py index c2931b0..8fbb608 100644 --- a/hypertiling/core.py +++ b/hypertiling/core.py @@ -40,11 +40,8 @@ def HyperbolicTiling(p, q, n, center="cell", kernel="SPK"): if kernel not in KERNELS: raise KeyError("[hypertiling] Error: No valid kernel specified") - if kernel == "dunham": - print("Caution: This kernel is slow and error-prone. Use at own risk!") + if kernel == "Dunham": + print("[hypertiling] Warning: Dunham kernel is only implemented for legacy reasons and largely untested. See documentation!") if center == "vertex": - print( - "KernelDunham doesn't support vertex-centered tilings yet. " + - "A cell-centered tiling will be generated instead...") - # raise NotImplementedError("[hypertiling] Error: Dunham kernel is currently broken (fixme!)") + print("[hypertiling] Warning: Dunham kernel does not support vertex centered tilings yet!") return KERNELS[kernel](p, q, n, center) diff --git a/hypertiling/static/kernellegacydunham.py b/hypertiling/static/kernellegacydunham.py index 979a4fd..24442a3 100644 --- a/hypertiling/static/kernellegacydunham.py +++ b/hypertiling/static/kernellegacydunham.py @@ -29,8 +29,6 @@ class KernelLegacyDunham(HyperbolicTilingBase): Original construction algorithm by D. Dunham (1982) works for every valid combination {p,q} however produces a lot of duplicates - - currently broken! fixme """ def __init__ (self, p, q, n, center="cell"): -- GitLab From f21b22190c282ae88458bb8409ca86a55aa0ed49 Mon Sep 17 00:00:00 2001 From: Manuel Schrauth Date: Fri, 9 Sep 2022 10:35:17 +0200 Subject: [PATCH 4/4] clean up and add documentation --- hypertiling/static/hyperpolygon.py | 3 --- hypertiling/static/kernellegacydunham.py | 18 ++++++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/hypertiling/static/hyperpolygon.py b/hypertiling/static/hyperpolygon.py index 54f6c60..739e847 100644 --- a/hypertiling/static/hyperpolygon.py +++ b/hypertiling/static/hyperpolygon.py @@ -75,10 +75,7 @@ class HyperPolygon: # Poincare disk coordinates self.verticesP = np.zeros(shape=self.p + 1, dtype=np.complex128) # vertices + center - # self.verticesdP = np.zeros(shape=self.p+1, dtype=np.complex128) - # List of edges (untested, compare self.populate_edge_list) - self.edges = [] def centerP(self): return self.verticesP[self.p] diff --git a/hypertiling/static/kernellegacydunham.py b/hypertiling/static/kernellegacydunham.py index 24442a3..01c9072 100644 --- a/hypertiling/static/kernellegacydunham.py +++ b/hypertiling/static/kernellegacydunham.py @@ -10,17 +10,27 @@ from ..util import fund_radius from ..representations import p2w_xyt, w2p_xyt +# NOTE: This kernel implements the "original" construction algorithm of D. Dunham (1982) +# The algorithm uses Weierstraß (hyperboloid) coordinates; since those are not natively supported +# by our HyperPolygon class we need the following two transformation functions: - - -def transformW_poly(polygon, transformation): +def transformW_poly(polygon: HyperPolygon, transformation): + """ + Apply Weierstraß transformation matrix to entire HyperPolygon, i.e. vertices and center coordiantes + """ new_verts = np.zeros_like(polygon.verticesP) for i, pointP in enumerate(polygon.verticesP): new_verts[i] = transformW_site(pointP, transformation) polygon.verticesP = new_verts -def transformW_site(pointP, transformation): +def transformW_site(pointP: np.complex128, transformation): + """ + Apply Weierstraß transformation to Poincare site + 1. Transform site from Poincare to Weierstraß + 2. Apply Weierstraß transformation + 3. Transform back + """ return w2p_xyt(transformation @ p2w_xyt(pointP)) -- GitLab