Non-numerical Hessian for Radial[T]
This commit is contained in:
parent
8cd48a1acd
commit
f1a180b5a5
940
squish/core.c
940
squish/core.c
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,7 @@ ctypedef Matrix2x2 (*MatrixCopySclOp)(Matrix2x2*, FLOAT_T) nogil
|
||||
|
||||
ctypedef struct VectorSelfOps:
|
||||
Vector2D* (*neg)(Vector2D*) nogil
|
||||
Vector2D* (*rot)(Vector2D*) nogil
|
||||
|
||||
VectorSelfVecOp vadd
|
||||
VectorSelfVecOp vsub
|
||||
@ -66,6 +67,7 @@ ctypedef struct VectorSelfOps:
|
||||
|
||||
ctypedef struct VectorCopyOps:
|
||||
Vector2D (*neg)(Vector2D*) nogil
|
||||
Vector2D (*rot)(Vector2D*) nogil
|
||||
|
||||
VectorCopyVecOp vadd
|
||||
VectorCopyVecOp vsub
|
||||
@ -81,6 +83,7 @@ ctypedef struct VectorCopyOps:
|
||||
|
||||
ctypedef struct MatrixSelfOps:
|
||||
Matrix2x2* (*neg)(Matrix2x2*) nogil
|
||||
Matrix2x2* (*T)(Matrix2x2*) nogil
|
||||
|
||||
MatrixSelfMatOp madd
|
||||
MatrixSelfMatOp msub
|
||||
@ -96,6 +99,7 @@ ctypedef struct MatrixSelfOps:
|
||||
|
||||
ctypedef struct MatrixCopyOps:
|
||||
Matrix2x2 (*neg)(Matrix2x2*) nogil
|
||||
Matrix2x2 (*T)(Matrix2x2*) nogil
|
||||
|
||||
MatrixCopyMatOp madd
|
||||
MatrixCopyMatOp msub
|
||||
@ -115,7 +119,7 @@ ctypedef struct Vector2D:
|
||||
VectorCopyOps copy
|
||||
|
||||
bint (*equals)(Vector2D*, Vector2D) nogil
|
||||
Vector2D (*rot)(Vector2D*) nogil
|
||||
Matrix2x2 (*vecmul)(Vector2D*, Vector2D) nogil
|
||||
FLOAT_T (*dot)(Vector2D*, Vector2D) nogil
|
||||
FLOAT_T (*mag)(Vector2D*) nogil
|
||||
|
||||
|
||||
@ -18,19 +18,19 @@ cdef MatrixCopyOps MCO
|
||||
|
||||
VSO.neg, VSO.vadd, VSO.vsub, VSO.vmul, VSO.vdiv, VSO.sadd, VSO.ssub, VSO.smul, VSO.sdiv = \
|
||||
v_neg_s, v_vadd_s, v_vsub_s, v_vmul_s, v_vdiv_s, v_sadd_s, v_ssub_s, v_smul_s, v_sdiv_s
|
||||
VSO.matmul = v_matmul_s
|
||||
VSO.matmul, VSO.rot = v_matmul_s, rot_s
|
||||
|
||||
VCO.neg, VCO.vadd, VCO.vsub, VCO.vmul, VCO.vdiv, VCO.sadd, VCO.ssub, VCO.smul, VCO.sdiv = \
|
||||
v_neg_c, v_vadd_c, v_vsub_c, v_vmul_c, v_vdiv_c, v_sadd_c, v_ssub_c, v_smul_c, v_sdiv_c
|
||||
VCO.matmul = v_matmul_c
|
||||
VCO.matmul, VCO.rot = v_matmul_c, rot_c
|
||||
|
||||
MSO.neg, MSO.madd, MSO.msub, MSO.mmul, MSO.mdiv, MSO.sadd, MSO.ssub, MSO.smul, MSO.sdiv = \
|
||||
m_neg_s, m_madd_s, m_msub_s, m_mmul_s, m_mdiv_s, m_sadd_s, m_ssub_s, m_smul_s, m_sdiv_s
|
||||
MSO.matmul = m_matmul_s
|
||||
MSO.matmul, MSO.T = m_matmul_s, m_transpose_s
|
||||
|
||||
MCO.neg, MCO.madd, MCO.msub, MCO.mmul, MCO.mdiv, MCO.sadd, MCO.ssub, MCO.smul, MCO.sdiv = \
|
||||
m_neg_c, m_madd_c, m_msub_c, m_mmul_c, m_mdiv_c, m_sadd_c, m_ssub_c, m_smul_c, m_sdiv_c
|
||||
MCO.matmul = m_matmul_c
|
||||
MCO.matmul, MCO.T = m_matmul_c, m_transpose_c
|
||||
|
||||
"""
|
||||
If bound checking is desired, uncomment out ..._valid_indices functions.
|
||||
@ -143,7 +143,7 @@ cdef inline Vector2D _Vector2D(FLOAT_T x, FLOAT_T y) nogil:
|
||||
vec.x, vec.y = x, y
|
||||
vec.self, vec.copy = VSO, VCO
|
||||
|
||||
vec.equals, vec.rot, vec.dot, vec.mag = &v_equals, &rot, &dot, &mag
|
||||
vec.equals, vec.vecmul, vec.dot, vec.mag = &v_equals, &v_vecmul, &dot, &mag
|
||||
|
||||
return vec
|
||||
|
||||
@ -200,6 +200,10 @@ cdef inline Vector2D* v_matmul_s(Vector2D* self, Matrix2x2 m) nogil:
|
||||
self.x, self.y = self.x*m.a + self.y*m.c, self.x*m.b + self.y*m.d
|
||||
return self
|
||||
|
||||
cdef inline Vector2D* rot_s(Vector2D* self) nogil:
|
||||
self.x, self.y = -self.y, self.x
|
||||
return self
|
||||
|
||||
cdef inline Vector2D v_neg_c(Vector2D* self) nogil:
|
||||
return _Vector2D(-self.x, -self.y)
|
||||
|
||||
@ -232,7 +236,7 @@ cdef inline Vector2D v_matmul_c(Vector2D* self, Matrix2x2 m) nogil:
|
||||
self.x*m.a + self.y*m.c, self.x*m.b + self.y*m.d
|
||||
)
|
||||
|
||||
cdef inline Vector2D rot(Vector2D* self) nogil:
|
||||
cdef inline Vector2D rot_c(Vector2D* self) nogil:
|
||||
return _Vector2D(-self.y, self.x)
|
||||
|
||||
cdef inline FLOAT_T dot(Vector2D* self, Vector2D w) nogil:
|
||||
@ -241,6 +245,9 @@ cdef inline FLOAT_T dot(Vector2D* self, Vector2D w) nogil:
|
||||
cdef inline FLOAT_T mag(Vector2D* self) nogil:
|
||||
return <FLOAT_T>sqrt(<double>(self.x*self.x + self.y*self.y))
|
||||
|
||||
cdef inline Matrix2x2 v_vecmul(Vector2D* self, Vector2D v) nogil:
|
||||
return _Matrix2x2(self.x*v.x, self.x*v.y, self.y*v.x, self.y*v.y)
|
||||
|
||||
|
||||
#### Matrix2x2 Methods ####
|
||||
|
||||
@ -329,6 +336,10 @@ cdef inline Matrix2x2* m_matmul_s(Matrix2x2* self, Matrix2x2 m) nogil:
|
||||
self.c*m.a + self.d*m.c, self.c*m.b + self.d*m.d
|
||||
return self
|
||||
|
||||
cdef inline Matrix2x2* m_transpose_s(Matrix2x2* self) nogil:
|
||||
self.b, self.c = self.c, self.b
|
||||
return self
|
||||
|
||||
cdef inline Matrix2x2 m_neg_c(Matrix2x2* self) nogil:
|
||||
return _Matrix2x2(-self.a, -self.b, -self.c, -self.d)
|
||||
|
||||
@ -361,3 +372,6 @@ cdef inline Matrix2x2 m_matmul_c(Matrix2x2* self, Matrix2x2 m) nogil:
|
||||
self.a*m.a + self.b*m.c, self.a*m.b + self.b*m.d,
|
||||
self.c*m.a + self.d*m.c, self.c*m.b + self.d*m.d
|
||||
)
|
||||
|
||||
cdef inline Matrix2x2 m_transpose_c(Matrix2x2* self) nogil:
|
||||
return _Matrix2x2(self.a, self.c, self.b, self.d)
|
||||
@ -1,7 +1,13 @@
|
||||
from __future__ import annotations
|
||||
from typing import Tuple, List, Optional
|
||||
|
||||
import matplotlib.pyplot as plt, numpy as np, os
|
||||
import matplotlib as mpl
|
||||
import matplotlib.cm as cm
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.gridspec as gridspec
|
||||
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
|
||||
|
||||
import numpy as np, os, math
|
||||
from matplotlib.ticker import MaxNLocator, FormatStrFormatter
|
||||
from scipy.spatial import Voronoi, voronoi_plot_2d
|
||||
from multiprocessing import Pool, cpu_count
|
||||
@ -123,18 +129,49 @@ class Diagram:
|
||||
raise ValueError("Not a valid mode for diagrams!")
|
||||
|
||||
shape = self.diagrams.shape
|
||||
fig, axes = plt.subplots(*shape, figsize=(shape[1] * 8, shape[0] * 8))
|
||||
|
||||
plt.rcParams.update(
|
||||
{
|
||||
"axes.titlesize": 45,
|
||||
"axes.labelsize": 45,
|
||||
"xtick.labelsize": 40,
|
||||
"ytick.labelsize": 40,
|
||||
"xtick.major.width": 2,
|
||||
"ytick.major.width": 2,
|
||||
"xtick.major.size": 5,
|
||||
"ytick.major.size": 5,
|
||||
"xtick.minor.width": 1,
|
||||
"ytick.minor.width": 1,
|
||||
"xtick.minor.size": 3,
|
||||
"ytick.minor.size": 3,
|
||||
"legend.fontsize": 40,
|
||||
"lines.linewidth": 3,
|
||||
"font.family": "cm",
|
||||
"font.size": 40,
|
||||
"text.usetex": True,
|
||||
"text.latex.preamble": r"\usepackage{amsmath}",
|
||||
"figure.constrained_layout.use": True,
|
||||
}
|
||||
)
|
||||
|
||||
fig = plt.figure(figsize=(shape[1] * 15, shape[0] * 15))
|
||||
gs = fig.add_gridspec(shape[0], shape[1])
|
||||
# fig, axes = plt.subplots(*shape, figsize=(shape[1] * 15, shape[0] * 15))
|
||||
|
||||
if self.diagrams.shape == (1, 1):
|
||||
getattr(self, str(self.diagrams[0][0]) + "_plot")(frame, axes)
|
||||
ax = fig.add_subplot(gs[0])
|
||||
getattr(self, str(self.diagrams[0][0]) + "_plot")(frame, ax)
|
||||
else:
|
||||
axes = np.atleast_2d(axes)
|
||||
# axes = np.atleast_2d(axes)
|
||||
it = np.nditer(self.diagrams, flags=["multi_index"])
|
||||
for diagram in it:
|
||||
if diagram == "":
|
||||
continue
|
||||
getattr(self, str(diagram) + "_plot")(frame, axes[it.multi_index])
|
||||
|
||||
plt.tight_layout()
|
||||
ax = fig.add_subplot(gs[it.multi_index])
|
||||
getattr(self, str(diagram) + "_plot")(frame, ax)
|
||||
|
||||
# plt.tight_layout()
|
||||
if name is None:
|
||||
name = f"img{frame:05}.png"
|
||||
|
||||
@ -148,28 +185,60 @@ class Diagram:
|
||||
domain = self.sim.domains[i]
|
||||
n, w, h = domain.n, domain.w, domain.h
|
||||
scale = 1.5
|
||||
area = n <= 60
|
||||
area = n <= 50
|
||||
|
||||
offset = (
|
||||
2
|
||||
- 2 * domain.r * (6 * 3 ** (-0.25) * math.sqrt(2) * math.atanh(0.5))
|
||||
+ 2 * math.pi * domain.r ** 2
|
||||
)
|
||||
|
||||
# Make color map axis.
|
||||
divider = make_axes_locatable(ax)
|
||||
cax = divider.append_axes("right", size="5%", pad=0.05)
|
||||
|
||||
voronoi_plot_2d(
|
||||
self.sim.voronois[i], ax, show_vertices=False, point_size=7 - n / 100
|
||||
self.sim.voronois[i], ax, show_vertices=False, show_points=False
|
||||
)
|
||||
ax.plot([-w, 2 * w], [0, 0], "r")
|
||||
ax.plot([-w, 2 * w], [h, h], "r")
|
||||
ax.plot([0, 0], [-h, 2 * h], "r")
|
||||
ax.plot([w, w], [-h, 2 * h], "r")
|
||||
|
||||
# Mark location axis with 3 ticks.
|
||||
ax.set_xticks([0, w / 2, w])
|
||||
ax.set_yticks([0, h / 2, h])
|
||||
|
||||
# Obtain site energies, and make color map.
|
||||
energies = self.sim.stats[i]["site_energies"][:n]
|
||||
norm = mpl.colors.Normalize(vmin=-2, vmax=2, clip=True)
|
||||
mapper = cm.ScalarMappable(norm=norm, cmap=cm.magma)
|
||||
cbar = plt.colorbar(mapper, cax=cax, ticks=np.linspace(-2, 2, 5))
|
||||
|
||||
# Fill polygons with energy colormap.
|
||||
for j, p in enumerate(self.sim.voronois[i].point_region):
|
||||
region = self.sim.voronois[i].regions[p]
|
||||
if not -1 in region:
|
||||
polygon = [self.sim.voronois[i].vertices[k] for k in region]
|
||||
ax.fill(
|
||||
*zip(*polygon),
|
||||
color=mapper.to_rgba(energies[j % n] - offset),
|
||||
zorder=0,
|
||||
)
|
||||
|
||||
line_color, point_color = "white", "lightseagreen"
|
||||
|
||||
# Periodic border
|
||||
ax.plot([-w, 2 * w], [0, 0], line_color, linewidth="4")
|
||||
ax.plot([-w, 2 * w], [h, h], line_color, linewidth="4")
|
||||
ax.plot([0, 0], [-h, 2 * h], line_color, linewidth="4")
|
||||
ax.plot([w, w], [-h, 2 * h], line_color, linewidth="4")
|
||||
ax.axis("equal")
|
||||
ax.set_xlim([(1 - scale) * w / 2, (1 + scale) * w / 2])
|
||||
ax.set_xlim([(1 - 0.85 * scale) * w / 2, (1 + 0.85 * scale) * w / 2])
|
||||
ax.set_ylim([(1 - scale) * h / 2, (1 + scale) * h / 2])
|
||||
ax.title.set_text("Voronoi Visualization")
|
||||
|
||||
props = dict(boxstyle="round", facecolor="wheat", alpha=0.8)
|
||||
ax.set_title("Voronoi Tessellation")
|
||||
|
||||
# Marking sites and defects.
|
||||
defects = {5: {"x": [], "y": []}, 7: {"x": [], "y": []}}
|
||||
|
||||
for j in range(n):
|
||||
for s in SYMM:
|
||||
vec = self.sim.voronois[i].points[j] + s * self.sim.domains[i].dim
|
||||
|
||||
if area:
|
||||
txt = ax.text(
|
||||
*vec, str(round(self.sim.stats[i]["site_areas"][j], 3))
|
||||
@ -182,16 +251,35 @@ class Diagram:
|
||||
elif self.sim.stats[i]["site_edge_count"][j] == 7:
|
||||
defects[7]["x"].append(vec[0])
|
||||
defects[7]["y"].append(vec[1])
|
||||
else:
|
||||
ax.scatter(
|
||||
vec[0], vec[1], s=(40 - n / 100), color=point_color, zorder=1
|
||||
)
|
||||
|
||||
ax.scatter(defects[5]["x"], defects[5]["y"], marker="p", color="C0")
|
||||
ax.scatter(defects[7]["x"], defects[7]["y"], marker="*", color="C0")
|
||||
ax.scatter(
|
||||
defects[5]["x"],
|
||||
defects[5]["y"],
|
||||
marker="p",
|
||||
s=(200 - n / 100),
|
||||
color=point_color,
|
||||
zorder=1,
|
||||
)
|
||||
ax.scatter(
|
||||
defects[7]["x"],
|
||||
defects[7]["y"],
|
||||
marker="*",
|
||||
s=(200 - n / 100),
|
||||
color=point_color,
|
||||
zorder=1,
|
||||
)
|
||||
|
||||
# Make VEE text in top left.
|
||||
props = dict(boxstyle="round", facecolor="white", alpha=0.8, zorder=20)
|
||||
ax.text(
|
||||
0.05,
|
||||
0.95,
|
||||
f"Energy: {self.sim.energies[i]}",
|
||||
0.065,
|
||||
0.935,
|
||||
f"VEE: {round(sum(energies)/n - offset, 8)}",
|
||||
transform=ax.transAxes,
|
||||
fontsize=14,
|
||||
verticalalignment="top",
|
||||
bbox=props,
|
||||
)
|
||||
@ -305,14 +393,32 @@ class Diagram:
|
||||
def eigs_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
try:
|
||||
eigs = self.sim.stats[i]["eigs"]
|
||||
for i, eig in enumerate(eigs):
|
||||
if eig > 1e-4:
|
||||
ax.annotate(
|
||||
f"Coercivity: {eig:.5f}",
|
||||
xy=(i, eig),
|
||||
xytext=(50, -50),
|
||||
textcoords="offset points",
|
||||
arrowprops={"arrowstyle": "->", "color": "black"},
|
||||
)
|
||||
break
|
||||
|
||||
ax.plot(
|
||||
list(range(len(eigs))), eigs, marker="o", linestyle="dashed", color="C0"
|
||||
list(range(40)),
|
||||
eigs[:40],
|
||||
marker="o",
|
||||
linestyle="dashed",
|
||||
color="C0",
|
||||
markersize=8,
|
||||
)
|
||||
ax.plot([0, len(eigs)], [0, 0], color="red")
|
||||
|
||||
ax.plot([0, 40], [0, 0], color="red")
|
||||
except KeyError:
|
||||
ax.text(0.5, 0.5, "Not Computed")
|
||||
|
||||
ax.title.set_text("Hessian Eigenvalues")
|
||||
ax.grid()
|
||||
ax.set_title("Sorted Hessian Eigenvalues")
|
||||
ax.set_xlabel("")
|
||||
ax.set_ylabel("Value")
|
||||
|
||||
|
||||
4173
squish/energy.c
4173
squish/energy.c
File diff suppressed because it is too large
Load Diff
@ -4,10 +4,10 @@ from cython.parallel import parallel, prange
|
||||
cimport numpy as np
|
||||
from libc.math cimport NAN, pi as PI, atanh
|
||||
from squish.core cimport INT_T, FLOAT_T, Vector2D, Matrix2x2, BitSet, \
|
||||
_Vector2D, _Matrix2x2, _BitSet
|
||||
_Vector2D, _Matrix2x2, _BitSet
|
||||
from squish.voronoi cimport Site, HalfEdge, EdgeCacheMap, VoronoiInfo, \
|
||||
_Site, _HalfEdge, _EdgeCacheMap, _VoronoiInfo, VoronoiContainer, \
|
||||
R, NAN_MATRIX, NAN_VECTOR
|
||||
_Site, _HalfEdge, _EdgeCacheMap, _VoronoiInfo, VoronoiContainer, \
|
||||
R, NAN_MATRIX, NAN_VECTOR
|
||||
|
||||
#### Constants ####
|
||||
|
||||
@ -18,228 +18,405 @@ cdef EdgeCacheMap AREA_ECM = _EdgeCacheMap(0, 4, 6, 8, 10, 12, 13, -1, -1, 14)
|
||||
cdef EdgeCacheMap RADIALT_ECM = _EdgeCacheMap(0, 4, 6, 8, -1, 10, 11, 12, 13, 14)
|
||||
|
||||
cdef class AreaEnergy(VoronoiContainer):
|
||||
"""
|
||||
Class for formulas relevant to the Area energy.
|
||||
:param n: [int] how many sites to generate.
|
||||
:param w: [float] width of the bounding domain.
|
||||
:param h: [float] height of the bounding domain.
|
||||
:param r: [float] radius of zero energy circle.
|
||||
:param sites: [np.ndarray] collection of sites.
|
||||
"""
|
||||
"""
|
||||
Class for formulas relevant to the Area energy.
|
||||
:param n: [int] how many sites to generate.
|
||||
:param w: [float] width of the bounding domain.
|
||||
:param h: [float] height of the bounding domain.
|
||||
:param r: [float] radius of zero energy circle.
|
||||
:param sites: [np.ndarray] collection of sites.
|
||||
"""
|
||||
|
||||
attr_str = "area"
|
||||
title_str = "Area"
|
||||
attr_str = "area"
|
||||
title_str = "Area"
|
||||
|
||||
def __init__(AreaEnergy self, INT_T n, FLOAT_T w, FLOAT_T h, FLOAT_T r,
|
||||
np.ndarray[FLOAT_T, ndim=2] site_arr):
|
||||
self.edge_cache_map = &AREA_ECM
|
||||
self.energy = 0.0
|
||||
def __init__(AreaEnergy self, INT_T n, FLOAT_T w, FLOAT_T h, FLOAT_T r,
|
||||
np.ndarray[FLOAT_T, ndim=2] site_arr):
|
||||
self.edge_cache_map = &AREA_ECM
|
||||
self.energy = 0.0
|
||||
|
||||
super().__init__(n, w, h, r, site_arr)
|
||||
self.minimum = (<FLOAT_T>n)*(w*h/(<FLOAT_T>n)-PI*r**2)**2
|
||||
super().__init__(n, w, h, r, site_arr)
|
||||
self.minimum = (<FLOAT_T>n)*(w*h/(<FLOAT_T>n)-PI*r**2)**2
|
||||
|
||||
|
||||
cdef void precompute(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
cdef void precompute(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
|
||||
cdef Site xi
|
||||
cdef HalfEdge em, e, ep
|
||||
cdef Vector2D vdiff
|
||||
cdef Site xi
|
||||
cdef HalfEdge em, e, ep
|
||||
cdef Vector2D vdiff
|
||||
|
||||
cdef FLOAT_T [:] site_energy = np.full(self.sites.shape[0], PI*self.r**2)
|
||||
cdef FLOAT_T [:] site_energy = np.full(self.sites.shape[0], PI*self.r**2)
|
||||
|
||||
cdef INT_T i, j
|
||||
for i in prange(self.sites.shape[0], nogil=True):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
cdef INT_T i, j
|
||||
for i in prange(self.sites.shape[0], nogil=True):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
|
||||
site_energy[i] = (xi.cache.area(&xi, NAN) - site_energy[i])**2
|
||||
xi.cache.energy(&xi, site_energy[i])
|
||||
site_energy[i] = (xi.cache.area(&xi, NAN) - site_energy[i])**2
|
||||
xi.cache.energy(&xi, site_energy[i])
|
||||
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi):
|
||||
em, ep = e.prev(&e), e.next(&e)
|
||||
vdiff = em.origin(&em)
|
||||
vdiff.self.vsub(&vdiff, ep.origin(&ep))
|
||||
e.cache.dVdv(&e, R.vecmul(&R, vdiff))
|
||||
e.cache.H(&e, VoronoiContainer.calc_H(em, e))
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi):
|
||||
em, ep = e.prev(&e), e.next(&e)
|
||||
vdiff = em.origin(&em)
|
||||
vdiff.self.vsub(&vdiff, ep.origin(&ep))
|
||||
e.cache.dVdv(&e, R.vecmul(&R, vdiff))
|
||||
e.cache.H(&e, VoronoiContainer.calc_H(em, e))
|
||||
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
|
||||
self.energy = np.sum(site_energy[:self.n])
|
||||
self.energy = np.sum(site_energy[:self.n])
|
||||
|
||||
|
||||
cdef void calc_grad(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
cdef void calc_grad(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
|
||||
cdef Site xi, xf
|
||||
cdef HalfEdge e, f
|
||||
cdef Vector2D dedxi_p
|
||||
cdef BitSet edge_set
|
||||
cdef Site xi, xf
|
||||
cdef HalfEdge e, f
|
||||
cdef Vector2D dedxi_p
|
||||
cdef BitSet edge_set
|
||||
|
||||
cdef INT_T num_edges = self.edges.shape[0]
|
||||
cdef FLOAT_T A = PI*self.r**2
|
||||
cdef INT_T num_edges = self.edges.shape[0]
|
||||
cdef FLOAT_T A = PI*self.r**2
|
||||
|
||||
cdef FLOAT_T [:, ::1] dedx = np.zeros((self.n, 2), dtype=FLOAT)
|
||||
cdef FLOAT_T [:, ::1] dedx = np.zeros((self.n, 2), dtype=FLOAT)
|
||||
|
||||
cdef INT_T i, j
|
||||
for i in prange(self.n, nogil=True):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
edge_set = _BitSet(num_edges)
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi): # Looping through site edges.
|
||||
f = e
|
||||
while True: # Circling this vertex.
|
||||
if not edge_set.add(&edge_set, f.arr_index):
|
||||
xf = f.face(&f)
|
||||
dedxi_p = f.cache.dVdv(&f, NAN_VECTOR) #dVdv
|
||||
dedxi_p.self.smul(&dedxi_p, xf.cache.area(&xf, NAN) - A)
|
||||
dedxi_p.self.matmul(&dedxi_p, e.cache.H(&e, NAN_MATRIX))
|
||||
dedx[i][0] += dedxi_p.x
|
||||
dedx[i][1] += dedxi_p.y
|
||||
cdef INT_T i, j
|
||||
for i in prange(self.n, nogil=True):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
edge_set = _BitSet(num_edges)
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi): # Looping through site edges.
|
||||
f = e
|
||||
while True: # Circling this vertex.
|
||||
if not edge_set.add(&edge_set, f.arr_index):
|
||||
xf = f.face(&f)
|
||||
dedxi_p = f.cache.dVdv(&f, NAN_VECTOR) #dVdv
|
||||
dedxi_p.self.smul(&dedxi_p, xf.cache.area(&xf, NAN) - A)
|
||||
dedxi_p.self.matmul(&dedxi_p, e.cache.H(&e, NAN_MATRIX))
|
||||
dedx[i][0] += dedxi_p.x
|
||||
dedx[i][1] += dedxi_p.y
|
||||
|
||||
f = f.twin(&f)
|
||||
f = f.next(&f)
|
||||
if f.arr_index == e.arr_index:
|
||||
break
|
||||
f = f.twin(&f)
|
||||
f = f.next(&f)
|
||||
if f.arr_index == e.arr_index:
|
||||
break
|
||||
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
edge_set.free(&edge_set)
|
||||
self.grad = dedx
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
edge_set.free(&edge_set)
|
||||
self.grad = dedx
|
||||
|
||||
|
||||
cdef void calc_hess(self) except *:
|
||||
d = 10e-5
|
||||
HE = np.zeros((2*self.n, 2*self.n))
|
||||
new_sites = np.copy(self.site_arr) # Maintain one copy for speed.
|
||||
for i in range(self.n):
|
||||
for j in range(2):
|
||||
mod = self.w if j == 0 else self.h
|
||||
new_sites[i][j] = (new_sites[i][j] + d) % mod
|
||||
Ep = self.__class__(self.n, self.w, self.h, self.r, new_sites)
|
||||
new_sites[i][j] = (new_sites[i][j] - 2*d) % mod
|
||||
Em = self.__class__(self.n, self.w, self.h, self.r, new_sites)
|
||||
new_sites[i][j] = (new_sites[i][j] + d) % mod
|
||||
|
||||
HE[:, 2*i+j] = ((Ep.gradient - Em.gradient)/(2*d)).flatten()
|
||||
|
||||
# Average out discrepencies, since it should be symmetric.
|
||||
for i in range(2*self.n):
|
||||
for j in range(i, 2*self.n):
|
||||
HE[i][j] = (HE[i][j] + HE[j][i])/2
|
||||
HE[j][i] = HE[i][j]
|
||||
self.hess = HE
|
||||
|
||||
|
||||
cdef class RadialALEnergy(VoronoiContainer):
|
||||
"""
|
||||
Class for formulas relevant to the Area energy.
|
||||
:param n: [int] how many sites to generate.
|
||||
:param w: [float] width of the bounding domain.
|
||||
:param h: [float] height of the bounding domain.
|
||||
:param r: [float] radius of zero energy circle.
|
||||
:param sites: [np.ndarray] collection of sites.
|
||||
"""
|
||||
"""
|
||||
Class for formulas relevant to the Area energy.
|
||||
:param n: [int] how many sites to generate.
|
||||
:param w: [float] width of the bounding domain.
|
||||
:param h: [float] height of the bounding domain.
|
||||
:param r: [float] radius of zero energy circle.
|
||||
:param sites: [np.ndarray] collection of sites.
|
||||
"""
|
||||
|
||||
attr_str = "radial-al"
|
||||
title_str = "Radial[AL]"
|
||||
attr_str = "radial-al"
|
||||
title_str = "Radial[AL]"
|
||||
|
||||
|
||||
def __init__(AreaEnergy self, INT_T n, FLOAT_T w, FLOAT_T h, FLOAT_T r,
|
||||
np.ndarray[FLOAT_T, ndim=2] site_arr):
|
||||
#self.edge_cache_map = &AREA_EDGE_CACHE_MAP
|
||||
self.energy = 0.0
|
||||
def __init__(AreaEnergy self, INT_T n, FLOAT_T w, FLOAT_T h, FLOAT_T r,
|
||||
np.ndarray[FLOAT_T, ndim=2] site_arr):
|
||||
#self.edge_cache_map = &AREA_EDGE_CACHE_MAP
|
||||
self.energy = 0.0
|
||||
|
||||
super().__init__(n, w, h, r, site_arr)
|
||||
super().__init__(n, w, h, r, site_arr)
|
||||
|
||||
|
||||
cdef void precompute(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
cdef void precompute(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
cdef void calc_grad(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
cdef void calc_grad(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
cdef void calc_hess(self) except *:
|
||||
pass
|
||||
|
||||
|
||||
cdef class RadialTEnergy(VoronoiContainer):
|
||||
"""
|
||||
Class for formulas relevant to the Area energy.
|
||||
:param n: [int] how many sites to generate.
|
||||
:param w: [float] width of the bounding domain.
|
||||
:param h: [float] height of the bounding domain.
|
||||
:param r: [float] radius of zero energy circle.
|
||||
:param sites: [np.ndarray] collection of sites.
|
||||
"""
|
||||
"""
|
||||
Class for formulas relevant to the Area energy.
|
||||
:param n: [int] how many sites to generate.
|
||||
:param w: [float] width of the bounding domain.
|
||||
:param h: [float] height of the bounding domain.
|
||||
:param r: [float] radius of zero energy circle.
|
||||
:param sites: [np.ndarray] collection of sites.
|
||||
"""
|
||||
|
||||
attr_str = "radial-t"
|
||||
title_str = "Radial[T]"
|
||||
def __init__(AreaEnergy self, INT_T n, FLOAT_T w, FLOAT_T h, FLOAT_T r,
|
||||
np.ndarray[FLOAT_T, ndim=2] site_arr):
|
||||
self.edge_cache_map = &RADIALT_ECM
|
||||
self.energy = 0.0
|
||||
attr_str = "radial-t"
|
||||
title_str = "Radial[T]"
|
||||
def __init__(AreaEnergy self, INT_T n, FLOAT_T w, FLOAT_T h, FLOAT_T r,
|
||||
np.ndarray[FLOAT_T, ndim=2] site_arr):
|
||||
self.edge_cache_map = &RADIALT_ECM
|
||||
self.energy = 0.0
|
||||
|
||||
super().__init__(n, w, h, r, site_arr)
|
||||
super().__init__(n, w, h, r, site_arr)
|
||||
|
||||
|
||||
cdef void precompute(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
cdef void precompute(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
|
||||
cdef Site xi
|
||||
cdef HalfEdge e, ep
|
||||
cdef Vector2D la
|
||||
cdef Site xi
|
||||
cdef HalfEdge e, ep
|
||||
cdef Vector2D la
|
||||
|
||||
# All energy has a 2pir_0 term.
|
||||
cdef FLOAT_T [:] site_energy = np.full(self.sites.shape[0], 2*PI*self.r**2)
|
||||
cdef FLOAT_T [:] avg_radii = np.zeros(self.sites.shape[0])
|
||||
cdef FLOAT_T sm, sp
|
||||
# All energy has a 2pir_0 term.
|
||||
cdef FLOAT_T [:] site_energy = np.full(self.sites.shape[0], 2*PI*self.r**2)
|
||||
cdef FLOAT_T [:] avg_radii = np.zeros(self.sites.shape[0])
|
||||
cdef FLOAT_T sm, sp
|
||||
|
||||
cdef INT_T i, j
|
||||
for i in prange(self.sites.shape[0], nogil=True):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi):
|
||||
ep = e.next(&e)
|
||||
#e.cache.H(&e, VoronoiContainer.calc_H(em, e))
|
||||
cdef INT_T i, j
|
||||
for i in prange(self.sites.shape[0], nogil=True):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi):
|
||||
ep = e.next(&e)
|
||||
#e.cache.H(&e, VoronoiContainer.calc_H(em, e))
|
||||
|
||||
la = e.cache.la(&e, NAN_VECTOR)
|
||||
sp = la.dot(&la, ep.cache.da(&ep, NAN_VECTOR)) # dap . la
|
||||
sm = la.dot(&la, e.cache.da(&e, NAN_VECTOR)) # da . la
|
||||
la = e.cache.la(&e, NAN_VECTOR)
|
||||
sp = la.dot(&la, ep.cache.da(&ep, NAN_VECTOR)) # dap . la
|
||||
sm = la.dot(&la, e.cache.da(&e, NAN_VECTOR)) # da . la
|
||||
|
||||
sp = sp / (ep.cache.da_mag(&ep, NAN) * e.cache.la_mag(&e, NAN))
|
||||
sm = sm / (e.cache.da_mag(&e, NAN) * e.cache.la_mag(&e, NAN))
|
||||
sp = sp / (ep.cache.da_mag(&ep, NAN) * e.cache.la_mag(&e, NAN))
|
||||
sm = sm / (e.cache.da_mag(&e, NAN) * e.cache.la_mag(&e, NAN))
|
||||
|
||||
e.cache.calI(&e, <FLOAT_T>(atanh(<double>sp) - atanh(<double>sm)))
|
||||
e.cache.calI(&e, <FLOAT_T>(atanh(<double>sp) - atanh(<double>sm)))
|
||||
|
||||
avg_radii[i] += e.cache.ya_mag(&e, NAN) * e.cache.calI(&e, NAN) / 2
|
||||
avg_radii[i] += e.cache.ya_mag(&e, NAN) * e.cache.calI(&e, NAN) / 2
|
||||
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
|
||||
site_energy[i] += 2*(xi.cache.area(&xi, NAN) - self.r*avg_radii[i])
|
||||
site_energy[i] += 2*(xi.cache.area(&xi, NAN) - self.r*avg_radii[i])
|
||||
|
||||
xi.cache.avg_radius(&xi, avg_radii[i]/(2*PI))
|
||||
xi.cache.energy(&xi, site_energy[i])
|
||||
xi.cache.avg_radius(&xi, avg_radii[i]/(2*PI))
|
||||
xi.cache.energy(&xi, site_energy[i])
|
||||
|
||||
self.energy = np.sum(site_energy[:self.n])
|
||||
self.energy = np.sum(site_energy[:self.n])
|
||||
|
||||
|
||||
cdef void calc_grad(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
cdef void calc_grad(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
|
||||
cdef Site xi
|
||||
cdef HalfEdge e
|
||||
cdef Vector2D dedxi_p
|
||||
cdef Site xi
|
||||
cdef HalfEdge e
|
||||
cdef Vector2D dedxi_p
|
||||
|
||||
cdef FLOAT_T [:, ::1] dedx = np.zeros((self.n, 2), dtype=FLOAT)
|
||||
cdef FLOAT_T [:, ::1] dedx = np.zeros((self.n, 2), dtype=FLOAT)
|
||||
|
||||
cdef INT_T i, j
|
||||
for i in prange(self.n, nogil=True):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
cdef INT_T i, j
|
||||
for i in prange(self.n, nogil=True):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi): # Looping through site edges.
|
||||
dedxi_p = e.cache.ya(&e, NAN_VECTOR)
|
||||
dedxi_p.self.smul(
|
||||
&dedxi_p,
|
||||
e.cache.calI(&e, NAN) / e.cache.ya_mag(&e, NAN)
|
||||
)
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi): # Looping through site edges.
|
||||
dedxi_p = e.cache.ya(&e, NAN_VECTOR)
|
||||
dedxi_p.self.smul(
|
||||
&dedxi_p,
|
||||
e.cache.calI(&e, NAN) / e.cache.ya_mag(&e, NAN)
|
||||
)
|
||||
|
||||
dedx[i][0] += 2*self.r*dedxi_p.x
|
||||
dedx[i][1] += 2*self.r*dedxi_p.y
|
||||
dedx[i][0] += 2*self.r*dedxi_p.x
|
||||
dedx[i][1] += 2*self.r*dedxi_p.y
|
||||
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
|
||||
self.grad = dedx
|
||||
self.grad = dedx
|
||||
|
||||
cdef void calc_hess(self) except *:
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache,
|
||||
self.edge_cache, self.edge_cache_map)
|
||||
cdef Site xi, xk
|
||||
cdef HalfEdge em, e, ep, f
|
||||
cdef Vector2D temp1, temp2
|
||||
cdef Matrix2x2 dsite, q, tempm
|
||||
cdef BitSet edge_set
|
||||
|
||||
cdef INT_T num_edges = self.edges.shape[0]
|
||||
|
||||
cdef FLOAT_T [:,:] HE = np.zeros((2*self.n, 2*self.n))
|
||||
|
||||
cdef INT_T i, j, k
|
||||
for i in prange(self.sites.shape[0], nogil=True):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi):
|
||||
em, ep = e.prev(&e), e.next(&e)
|
||||
e.cache.H(&e, VoronoiContainer.calc_H(em, e))
|
||||
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
|
||||
|
||||
for i in range(self.n):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
edge_set = _BitSet(num_edges)
|
||||
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi):
|
||||
em, ep = e.prev(&e), e.next(&e)
|
||||
|
||||
# Calculating of p
|
||||
temp1 = em.cache.la(&em, NAN_VECTOR)
|
||||
temp1.self.rot(&temp1)
|
||||
temp1.self.sdiv(
|
||||
&temp1,
|
||||
em.cache.la_mag(&em, NAN) * em.cache.ya_mag(&em, NAN) / 2
|
||||
)
|
||||
temp2 = e.cache.la(&e, NAN_VECTOR)
|
||||
temp2.self.rot(&temp2)
|
||||
temp2.self.sdiv(
|
||||
&temp2,
|
||||
e.cache.la_mag(&e, NAN) * e.cache.ya_mag(&e, NAN) / 2
|
||||
)
|
||||
|
||||
temp1.self.vsub(&temp1, temp2) # (lm/Am - l/A)
|
||||
|
||||
temp2 = e.cache.da(&e, NAN_VECTOR) # rot(d) / |d|
|
||||
temp2.self.sdiv(&temp2, e.cache.da_mag(&e, NAN))
|
||||
temp2.self.rot(&temp2)
|
||||
|
||||
dsite = temp1.vecmul(&temp1, temp2)
|
||||
|
||||
HE[2*i, 2*i] -= dsite.a
|
||||
HE[2*i, 2*i+1] -= dsite.b
|
||||
HE[2*i+1, 2*i] -= dsite.c
|
||||
HE[2*i+1, 2*i+1] -= dsite.d
|
||||
|
||||
# Calculating of q
|
||||
temp2 = e.cache.la(&e, NAN_VECTOR)
|
||||
temp1 = temp2.copy.rot(&temp2)
|
||||
q = temp1.vecmul(&temp1, temp2)
|
||||
q.self.sdiv(&q, e.cache.la_mag(&e, NAN)**2)
|
||||
|
||||
q.self.msub(&q, R)
|
||||
q.self.smul(
|
||||
&q,
|
||||
e.cache.calI(&e, NAN) / e.cache.la_mag(&e, NAN)
|
||||
)
|
||||
|
||||
temp1 = e.cache.la(&e, NAN_VECTOR)
|
||||
temp1.self.rot(&temp1)
|
||||
tempm = temp1.vecmul(&temp1, temp1)
|
||||
tempm.self.smul(
|
||||
&tempm,
|
||||
ep.cache.da_mag(&ep, NAN) - e.cache.da_mag(&e, NAN)
|
||||
)
|
||||
tempm.self.sdiv(
|
||||
&tempm,
|
||||
e.cache.la_mag(&e, NAN)**3 * e.cache.ya_mag(&e, NAN) / 2
|
||||
)
|
||||
|
||||
q.self.madd(&q, tempm)
|
||||
|
||||
# Minus portion
|
||||
temp2 = em.cache.la(&em, NAN_VECTOR)
|
||||
temp1 = temp2.copy.rot(&temp2)
|
||||
tempm = temp1.vecmul(&temp1, temp2)
|
||||
tempm.self.sdiv(&tempm, em.cache.la_mag(&em, NAN)**2)
|
||||
|
||||
tempm = R.copy.msub(&R, tempm)
|
||||
tempm.self.smul(
|
||||
&tempm,
|
||||
em.cache.calI(&em, NAN) / em.cache.la_mag(&em, NAN)
|
||||
)
|
||||
|
||||
q.self.madd(&q, tempm)
|
||||
|
||||
temp1 = em.cache.la(&em, NAN_VECTOR)
|
||||
temp1.self.rot(&temp1)
|
||||
tempm = temp1.vecmul(&temp1, temp1)
|
||||
tempm.self.smul(
|
||||
&tempm,
|
||||
e.cache.da_mag(&e, NAN) - em.cache.da_mag(&em, NAN)
|
||||
)
|
||||
tempm.self.sdiv(
|
||||
&tempm,
|
||||
em.cache.la_mag(&em, NAN)**3 * em.cache.ya_mag(&em, NAN) / 2
|
||||
)
|
||||
|
||||
q.self.msub(&q, tempm)
|
||||
|
||||
# Add p to q, so p = p, q = p+q
|
||||
q.self.madd(&q, dsite)
|
||||
|
||||
f = e
|
||||
while True:
|
||||
xk = f.face(&f)
|
||||
k = xk.index(&xk) % self.n
|
||||
if k < 0:
|
||||
k = k + self.n
|
||||
|
||||
if not edge_set.add(&edge_set, f.arr_index):
|
||||
tempm = q.copy.matmul(&q, f.cache.H(&f, NAN_MATRIX))
|
||||
|
||||
HE[2*i, 2*k] += tempm.a
|
||||
HE[2*i, 2*k+1] += tempm.b
|
||||
HE[2*i+1, 2*k] += tempm.c
|
||||
HE[2*i+1, 2*k+1] += tempm.d
|
||||
|
||||
f = f.twin(&f)
|
||||
f = f.next(&f)
|
||||
if f.arr_index == e.arr_index:
|
||||
break
|
||||
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
edge_set.free(&edge_set)
|
||||
self.hess = -2*self.r*np.asarray(HE, dtype=FLOAT)
|
||||
self.hess = (
|
||||
( np.asarray(self.hess, dtype=FLOAT)
|
||||
+ np.asarray(self.hess, dtype=FLOAT).T )
|
||||
/ 2
|
||||
)
|
||||
|
||||
@ -2,11 +2,21 @@ from __future__ import annotations
|
||||
from typing import List, Tuple
|
||||
import numpy as np
|
||||
from numpy.linalg import norm as mag
|
||||
from math import gcd, sqrt, log, tan, atan, pi
|
||||
from math import gcd, sqrt, log, tan, atan, atanh, pi
|
||||
import cmath
|
||||
from fractions import Fraction
|
||||
|
||||
Config = Tuple[int, int]
|
||||
|
||||
|
||||
def e_hex(domain: DomainParams) -> float:
|
||||
return (
|
||||
2
|
||||
- 2 * domain.r * (6 * 3 ** (-0.25) * sqrt(2) * atanh(0.5))
|
||||
+ 2 * pi * domain.r ** 2
|
||||
)
|
||||
|
||||
|
||||
def configurations(domain: DomainParams) -> List[Config]:
|
||||
n = domain.n
|
||||
coprimes, valid = [], []
|
||||
@ -25,6 +35,9 @@ def configurations(domain: DomainParams) -> List[Config]:
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
for i in range(len(valid)):
|
||||
valid.append((valid[i][1], valid[i][0]))
|
||||
|
||||
return valid
|
||||
|
||||
|
||||
@ -106,3 +119,71 @@ def rot(v: numpy.ndarray) -> numpy.ndarray:
|
||||
w = np.copy(v)
|
||||
w[0], w[1] = -w[1], w[0]
|
||||
return w
|
||||
|
||||
|
||||
def divisors(n: int) -> List[int]:
|
||||
divs = [[i, n // i] for i in range(1, int(sqrt(n)) + 1) if n % i == 0]
|
||||
return sorted(set(list(sum(divs, []))))
|
||||
|
||||
|
||||
def factorize(n: int) -> Dict[int, int]:
|
||||
primes = [i for i in range(1, n + 1) if len(divisors(i)) == 2]
|
||||
prime_fac = {}
|
||||
for prime in primes:
|
||||
i = 0
|
||||
while n % prime ** (i + 1) == 0:
|
||||
i += 1
|
||||
if i > 0:
|
||||
prime_fac[prime] = i
|
||||
|
||||
return prime_fac
|
||||
|
||||
|
||||
def hexagon_alpha(n: int, fraction: bool = False) -> List[int]:
|
||||
if n % 2 == 1:
|
||||
return []
|
||||
|
||||
fac = factorize(n)
|
||||
q = 1
|
||||
for prime in fac:
|
||||
if prime % 3 != 2:
|
||||
q *= prime ** fac[prime]
|
||||
|
||||
divq = divisors(q)
|
||||
ratios, thres = [], 1 / sqrt(3)
|
||||
for g in divq:
|
||||
us = n // (2 * g)
|
||||
divu = divisors(us)
|
||||
for u in divu:
|
||||
d = 2 * g * u * u
|
||||
f = Fraction(n, d)
|
||||
if f <= thres:
|
||||
ratios.append(f)
|
||||
else:
|
||||
ratios.append(Fraction(d, 3 * n))
|
||||
|
||||
ratios = sorted(set(ratios))
|
||||
if fraction:
|
||||
return ratios
|
||||
else:
|
||||
return [float(x) * sqrt(3) for x in ratios]
|
||||
|
||||
|
||||
def hexagon_alpha_brute(n: int):
|
||||
w = cmath.rect(1, 2 * pi / 3)
|
||||
divs = divisors(n / 2)
|
||||
ratios, thres = [], 1 / sqrt(3)
|
||||
for a in range(n):
|
||||
for b in range(1, a + 1):
|
||||
if a == 0 and b == 0:
|
||||
continue
|
||||
z2, g = a * a - a * b + b * b, gcd(a - 2 * b, 2 * a - b)
|
||||
if z2 // g in divs:
|
||||
f = Fraction(n, 2 * z2)
|
||||
if f <= thres:
|
||||
ratios.append(f)
|
||||
else:
|
||||
ratios.append(Fraction(2 * z2, 3 * n))
|
||||
|
||||
ratios = sorted(set([float(x) * sqrt(3) for x in ratios]))
|
||||
return ratios
|
||||
|
||||
@ -159,6 +159,7 @@ class Simulation:
|
||||
sim, frames = Simulation.load(path)
|
||||
for frame in frames:
|
||||
sim.frames.append(sim.energy.mode(*frame["domain"], frame["arr"]))
|
||||
sim.frames[-1].stats = frame["stats"]
|
||||
|
||||
return sim
|
||||
|
||||
@ -238,8 +239,8 @@ class Flow(Simulation):
|
||||
|
||||
if self.adaptive:
|
||||
error = change - grad * self.step_size
|
||||
tol = 10 ** min(-3, -2 + log10(grad_norm))
|
||||
|
||||
tol = 10 ** min(-4, -3 + log10(grad_norm))
|
||||
# tol = 10 ** -7
|
||||
self.step_size *= (tol / np.linalg.norm(error)) ** 0.5
|
||||
|
||||
if not save:
|
||||
@ -315,7 +316,7 @@ class Search(Simulation):
|
||||
new_sites: Optional[numpy.ndarray] = None,
|
||||
) -> None:
|
||||
if log:
|
||||
print(f"Travel - {self.domain}", flush=True)
|
||||
print(f"Search - {self.domain} - {len(self)}", flush=True)
|
||||
if save and len(self) == 0:
|
||||
self.save(self.initial_data, True)
|
||||
|
||||
@ -328,16 +329,15 @@ class Search(Simulation):
|
||||
sim.run(False, log, log_steps)
|
||||
|
||||
self.frames.append(sim[-1])
|
||||
# Get Hessian,and check nullity. If > 2, perturb.
|
||||
hess = self.frames[i].hessian
|
||||
eigs = np.sort(np.linalg.eig(hess)[0])
|
||||
self.frames[i].stats["eigs"] = eigs
|
||||
|
||||
if save:
|
||||
self.save(self.frame_data(i))
|
||||
if log:
|
||||
print(f"Equilibrium: {i:04}\n", flush=True)
|
||||
|
||||
# Get Hessian,and check nullity. If > 2, perturb.
|
||||
hess = self.frames[i].hessian(10e-5)
|
||||
eigs = np.sort(np.linalg.eig(hess)[0])
|
||||
self.frames[i].stats["eigs"] = eigs
|
||||
|
||||
zero_eigs = np.count_nonzero(
|
||||
np.isclose(eigs, np.zeros((len(eigs),)), atol=1e-4)
|
||||
)
|
||||
|
||||
2878
squish/voronoi.c
2878
squish/voronoi.c
File diff suppressed because it is too large
Load Diff
@ -66,7 +66,7 @@ cdef class VoronoiContainer:
|
||||
cdef readonly INT_T n
|
||||
cdef readonly FLOAT_T w, h, r, energy
|
||||
cdef FLOAT_T [2] dim
|
||||
cdef FLOAT_T [:, ::1] points, vertices, site_cache, edge_cache, grad
|
||||
cdef FLOAT_T [:, ::1] points, vertices, site_cache, edge_cache, grad, hess
|
||||
cdef INT_T [:, ::1] sites, edges
|
||||
cdef EdgeCacheMap* edge_cache_map
|
||||
cdef dict __dict__
|
||||
@ -77,6 +77,7 @@ cdef class VoronoiContainer:
|
||||
cdef void common_cache(VoronoiContainer self) except *
|
||||
cdef void precompute(self) except *
|
||||
cdef void calc_grad(self) except *
|
||||
cdef void calc_hess(self) except *
|
||||
cdef void get_statistics(VoronoiContainer self) except *
|
||||
|
||||
@staticmethod
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import array, scipy.spatial, numpy as np
|
||||
import array, scipy.spatial, numpy as np, math
|
||||
from cython.parallel import parallel, prange
|
||||
|
||||
cimport numpy as np
|
||||
@ -6,8 +6,8 @@ from cpython cimport array
|
||||
from libc.math cimport isnan, NAN, pi as PI
|
||||
|
||||
from squish.core cimport INT_T, FLOAT_T, \
|
||||
IArray, FArray, Vector2D, Matrix2x2, \
|
||||
_IArray, _FArray, _Vector2D, _Matrix2x2
|
||||
IArray, FArray, Vector2D, Matrix2x2, BitSet, \
|
||||
_IArray, _FArray, _Vector2D, _Matrix2x2, _BitSet
|
||||
|
||||
from squish.voronoi cimport SiteCacheMap, EdgeCacheMap, VoronoiInfo, Site, HalfEdge
|
||||
|
||||
@ -554,8 +554,8 @@ cdef class VoronoiContainer:
|
||||
# vp - vm, vm - xi
|
||||
la, da = q.copy.vsub(&q, p), p.copy.vsub(&p, xi.vec(&xi))
|
||||
la_mag = la.mag(&la)
|
||||
area_p = la.dot(&la, da.rot(&da))
|
||||
Rla = la.rot(&la)
|
||||
area_p = la.dot(&la, da.copy.rot(&da))
|
||||
Rla = la.copy.rot(&la)
|
||||
ya = Rla.copy.smul(&Rla, -2*area_p/la.dot(&la, la))
|
||||
|
||||
# Calculating centroid.
|
||||
@ -591,14 +591,14 @@ cdef class VoronoiContainer:
|
||||
xj, xk = em.cache.ya(&em, NAN_VECTOR), ep.cache.ya(&ep, NAN_VECTOR)
|
||||
Rxjk = xk.copy.vsub(&xk, xj)
|
||||
Rxjk.self.smul(&Rxjk, 2)
|
||||
Rxjk = Rxjk.rot(&Rxjk)
|
||||
Rxjk.self.rot(&Rxjk)
|
||||
|
||||
v = ep.cache.da(&ep, NAN_VECTOR)
|
||||
top = R.copy.smul(&R, xj.dot(&xj, xj) - xk.dot(&xk, xk))
|
||||
top.self.msub(&top, _Matrix2x2(v.x*Rxjk.x, v.x*Rxjk.y, v.y*Rxjk.x, v.y*Rxjk.y))
|
||||
top.self.sdiv(&top, -Rxjk.dot(&Rxjk, xj))
|
||||
|
||||
return _Matrix2x2(top.a, top.c, top.b, top.d)
|
||||
return top
|
||||
|
||||
@staticmethod
|
||||
cdef inline bint sign(FLOAT_T [::1] ref, FLOAT_T [::1] p, FLOAT_T [::1] q):
|
||||
@ -621,6 +621,9 @@ cdef class VoronoiContainer:
|
||||
cdef void calc_grad(self) except *:
|
||||
pass
|
||||
|
||||
cdef void calc_hess(self) except *:
|
||||
pass
|
||||
|
||||
cdef void get_statistics(self) except *:
|
||||
self.stats = {}
|
||||
cache = self.site_cache[:self.n, :]
|
||||
@ -660,6 +663,11 @@ cdef class VoronoiContainer:
|
||||
def gradient(self):
|
||||
return np.asarray(self.grad, dtype=FLOAT)
|
||||
|
||||
@property
|
||||
def hessian(self):
|
||||
self.calc_hess()
|
||||
return np.asarray(self.hess, dtype=FLOAT)
|
||||
|
||||
def add_sites(self, add):
|
||||
return (self.site_arr + add) % np.asarray(self.dim, dtype=FLOAT)
|
||||
|
||||
@ -671,146 +679,6 @@ cdef class VoronoiContainer:
|
||||
|
||||
return -(step/2)*(k1+k2), -k1
|
||||
|
||||
|
||||
def approx_hessian(self, d: float) -> np.ndarray:
|
||||
"""
|
||||
Obtains the approximate Hessian.
|
||||
:param d: [float] small d for approximation.
|
||||
:return: 2Nx2N array that represents Hessian.
|
||||
"""
|
||||
HE = np.zeros((2*self.n, 2*self.n))
|
||||
new_sites = np.copy(self.site_arr) # Maintain one copy for speed.
|
||||
for i in range(self.n):
|
||||
for j in range(2):
|
||||
mod = self.w if j == 0 else self.h
|
||||
new_sites[i][j] = (new_sites[i][j] + d) % mod
|
||||
Ep = self.__class__(self.n, self.w, self.h, self.r, new_sites)
|
||||
new_sites[i][j] = (new_sites[i][j] - 2*d) % mod
|
||||
Em = self.__class__(self.n, self.w, self.h, self.r, new_sites)
|
||||
new_sites[i][j] = (new_sites[i][j] + d) % mod
|
||||
|
||||
HE[:, 2*i+j] = ((Ep.gradient - Em.gradient)/(2*d)).flatten()
|
||||
|
||||
# Average out discrepencies, since it should be symmetric.
|
||||
for i in range(2*self.n):
|
||||
for j in range(i, 2*self.n):
|
||||
HE[i][j] = (HE[i][j] + HE[j][i])/2
|
||||
HE[j][i] = HE[i][j]
|
||||
|
||||
return HE
|
||||
|
||||
def radialt_hessian(self) -> np.ndarray:
|
||||
HE = np.zeros((2*self.n, 2*self.n))
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache,
|
||||
self.edge_cache, self.edge_cache_map)
|
||||
cdef Site xi, xk
|
||||
cdef HalfEdge e, em, ep, fj, fk
|
||||
cdef Vector2D temp1, temp2, dau, dapu
|
||||
cdef Matrix2x2 sigI, sigJ, sigK, p, q, tempm, toI, toJ, toK
|
||||
|
||||
cdef INT_T i, j, k, z
|
||||
for i in range(self.n):
|
||||
xi = _Site(i, &info)
|
||||
ep = xi.edge(&xi)
|
||||
e = ep.prev(&ep)
|
||||
j = 0
|
||||
while j < xi.edge_num(&xi):
|
||||
e.cache.H(&e, VoronoiContainer.calc_H(e, ep))
|
||||
e = e.next(&e)
|
||||
j = j + 1
|
||||
|
||||
|
||||
for i in range(self.n):
|
||||
xi = _Site(i, &info)
|
||||
e = xi.edge(&xi)
|
||||
|
||||
z = 0
|
||||
while z < xi.edge_num(&xi):
|
||||
em, ep = e.prev(&e), e.next(&e)
|
||||
fj, fk = em.twin(&em), e.twin(&e)
|
||||
fj, fk = fj.next(&fj), fk.next(&fk)
|
||||
|
||||
xj, xk = fj.face(&fj), fk.face(&fk)
|
||||
j, k = xj.index(&xj) % self.n, xk.index(&xk) % self.n
|
||||
if k < 0:
|
||||
k = k + self.n
|
||||
if j < 0:
|
||||
j = j + self.n
|
||||
|
||||
sigI = e.cache.H(&e, NAN_MATRIX)
|
||||
sigJ = fj.cache.H(&fj, NAN_MATRIX)
|
||||
sigK = fk.cache.H(&fk, NAN_MATRIX)
|
||||
|
||||
### Calculating of p
|
||||
temp1 = e.cache.la(&e, NAN_VECTOR)
|
||||
temp1 = temp1.rot(&temp1)
|
||||
temp1.self.sdiv(
|
||||
&temp1,
|
||||
e.cache.la_mag(&e, NAN) * e.cache.ya_mag(&e, NAN) / 2
|
||||
)
|
||||
|
||||
dau = e.cache.da(&e, NAN_VECTOR)
|
||||
dau.self.sdiv(&dau, e.cache.da_mag(&e, NAN))
|
||||
dapu = ep.cache.da(&ep, NAN_VECTOR)
|
||||
dapu.self.sdiv(&dapu, ep.cache.da_mag(&ep, NAN))
|
||||
|
||||
temp2 = dapu.copy.vsub(&dapu, dau)
|
||||
temp2 = temp2.rot(&temp2)
|
||||
|
||||
p = _Matrix2x2(
|
||||
temp1.x*temp2.x, temp1.x*temp2.y,
|
||||
temp1.y*temp2.x, temp1.y*temp2.y
|
||||
)
|
||||
|
||||
### Calculating of q
|
||||
temp2 = e.cache.la(&e, NAN_VECTOR)
|
||||
temp1 = temp2.rot(&temp2)
|
||||
q = _Matrix2x2(
|
||||
temp1.x*temp2.x, temp1.x*temp2.y,
|
||||
temp1.y*temp2.x, temp1.y*temp2.y
|
||||
)
|
||||
q.self.sdiv(&q, e.cache.la_mag(&e, NAN)**2)
|
||||
|
||||
q.self.msub(&q, R)
|
||||
q.self.smul(
|
||||
&q,
|
||||
e.cache.calI(&e, NAN) / e.cache.la_mag(&e, NAN)
|
||||
)
|
||||
|
||||
temp2 = em.cache.la(&em, NAN_VECTOR)
|
||||
temp1 = temp2.rot(&temp2)
|
||||
tempm = _Matrix2x2(
|
||||
temp1.x*temp2.x, temp1.x*temp2.y,
|
||||
temp1.y*temp2.x, temp1.y*temp2.y
|
||||
)
|
||||
tempm.self.sdiv(&tempm, em.cache.la_mag(&em, NAN)**2)
|
||||
|
||||
tempm = R.copy.msub(&R, tempm)
|
||||
tempm.self.smul(
|
||||
&tempm,
|
||||
em.cache.calI(&em, NAN) / em.cache.la_mag(&em, NAN)
|
||||
)
|
||||
|
||||
q.self.madd(&q, tempm)
|
||||
|
||||
# Calculating components that go to the respective sites
|
||||
toI = sigI.copy.mmul(&sigI, p.copy.madd(&p, q))
|
||||
toI.self.msub(&toI, p)
|
||||
|
||||
toJ = sigJ.copy.mmul(&sigJ, p.copy.madd(&p, q))
|
||||
toK = sigK.copy.mmul(&sigK, p.copy.madd(&p, q))
|
||||
|
||||
HE[2*i: 2*(i+1), 2*i: 2*(i+1)] += np.array([[toI.a, toI.b], [toI.c, toI.d]])
|
||||
HE[2*i: 2*(i+1), 2*j: 2*(j+1)] += np.array([[toJ.a, toJ.b], [toJ.c, toJ.d]])
|
||||
HE[2*i: 2*(i+1), 2*k: 2*(k+1)] += np.array([[toK.a, toK.b], [toK.c, toK.d]])
|
||||
|
||||
e = e.next(&e)
|
||||
z = z + 1
|
||||
|
||||
|
||||
return -2*self.r*HE
|
||||
|
||||
def site_vert_arr(self): # -> List[np.ndarray]
|
||||
cdef VoronoiInfo info = _VoronoiInfo(self.sites, self.edges, self.points,
|
||||
self.vertices, self.site_cache, self.edge_cache, self.edge_cache_map)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user