Non-numerical Hessian for Radial[T]

This commit is contained in:
Kenneth Jao 2022-01-27 20:25:30 -05:00
parent 8cd48a1acd
commit f1a180b5a5
11 changed files with 4879 additions and 4083 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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)

View File

@ -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"]
ax.plot(
list(range(len(eigs))), eigs, marker="o", linestyle="dashed", color="C0"
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"},
)
ax.plot([0, len(eigs)], [0, 0], color="red")
break
ax.plot(
list(range(40)),
eigs[:40],
marker="o",
linestyle="dashed",
color="C0",
markersize=8,
)
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")

File diff suppressed because it is too large Load Diff

View File

@ -113,6 +113,29 @@ cdef class AreaEnergy(VoronoiContainer):
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.
@ -148,6 +171,9 @@ cdef class RadialALEnergy(VoronoiContainer):
pass
cdef void calc_hess(self) except *:
pass
cdef class RadialTEnergy(VoronoiContainer):
"""
@ -243,3 +269,154 @@ cdef class RadialTEnergy(VoronoiContainer):
j = j + 1
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
)

View File

@ -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

View File

@ -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)
)

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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)