Reformatted using spaces and python-black.
This commit is contained in:
parent
b4d6b5dbd8
commit
e7e7cefb56
@ -1,92 +0,0 @@
|
||||
from __future__ import annotations
|
||||
from typing import List, Tuple, Dict
|
||||
import argparse, math, numpy as np, os, pickle
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.ticker as mtick
|
||||
from multiprocessing import Pool, cpu_count
|
||||
from pathlib import Path
|
||||
|
||||
import squish.ordered as order
|
||||
from squish import Simulation, DomainParams
|
||||
from squish.common import Energy, OUTPUT_DIR
|
||||
|
||||
|
||||
def axis_settings(ax, widths):
|
||||
ax.invert_xaxis()
|
||||
ax.grid(zorder=0)
|
||||
ax.set_xticks([round(w,2) for w in widths[::-2]])
|
||||
ax.set_xticklabels(ax.get_xticks(), rotation = 90)
|
||||
plt.subplots_adjust(.07, .12, .97, .9)
|
||||
|
||||
|
||||
def main():
|
||||
# Loading arguments.
|
||||
parser = argparse.ArgumentParser("Outputs ordered equilibria lowest eigenvalues.")
|
||||
parser.add_argument('n_objects', metavar='N', type=int,
|
||||
help="folder that contains simulation files, or cached data file.")
|
||||
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', default=False,
|
||||
help="suppress all normal output")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
widths = np.round(np.linspace(3.0, 10.0, 141),2)
|
||||
|
||||
values = []
|
||||
store_data = {}
|
||||
|
||||
for i, width in enumerate(widths):
|
||||
domain = DomainParams(args.n_objects, width, 10, 4.0)
|
||||
eig_vals = []
|
||||
store_data[width] = {}
|
||||
configs = order.configurations(domain)
|
||||
for j, config in enumerate(configs):
|
||||
if config == (1,0):
|
||||
continue
|
||||
points = order.sites(domain, config)
|
||||
|
||||
hess = Energy("radial-t").mode(*domain, points).hessian(10e-5)
|
||||
eigs = np.sort(np.linalg.eig(hess)[0])[::-1]
|
||||
store_data[width][config] = eigs
|
||||
|
||||
# zero_ind = np.where(np.isclose(eigs, 0))[0][0]
|
||||
# if zero_ind == 0:
|
||||
# eig_vals.append(eigs[2])
|
||||
# else:
|
||||
# eig_vals.append(eigs[zero_ind-1])
|
||||
|
||||
hashes = int(21*j/len(widths))
|
||||
print(f'Generating at {width}, {i+1}/{len(widths)}... |{"#"*hashes}{" "*(20-hashes)}|' + \
|
||||
f' {j+1}/{len(configs)} configs done.', flush=True, end='\r')
|
||||
|
||||
print(flush=True)
|
||||
|
||||
with open("coercivity_eigs.pkl", "wb") as f:
|
||||
pickle.dump(store_data, f, pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
return
|
||||
fig, ax = plt.subplots(figsize=(12, 8))
|
||||
plt.subplots_adjust(.07, .12, .97, .9)
|
||||
|
||||
ax.plot(widths, values)
|
||||
|
||||
ax.invert_xaxis()
|
||||
ax.grid(zorder=0)
|
||||
ax.set_xticks([round(w,2) for w in widths[::-2]])
|
||||
ax.set_xticklabels(ax.get_xticks(), rotation = 90)
|
||||
|
||||
fig.suptitle("Coercivity")
|
||||
#ax.set_xlim([0, 5])
|
||||
ax.legend()
|
||||
ax.set_xlabel("Width")
|
||||
ax.set_ylabel("Smallest positive eigenvalue")
|
||||
|
||||
fig.savefig(OUTPUT_DIR / "Coercivity.png")
|
||||
print(f"Wrote to {OUTPUT_DIR / 'Coercivity.png'}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.environ["QT_LOGGING_RULES"] = "*=false"
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("Program terminated by user.")
|
||||
113
scripts/coercivity.py
Normal file
113
scripts/coercivity.py
Normal file
@ -0,0 +1,113 @@
|
||||
from __future__ import annotations
|
||||
from typing import List, Tuple, Dict
|
||||
import argparse, math, numpy as np, os, pickle
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.ticker as mtick
|
||||
from multiprocessing import Pool, cpu_count
|
||||
from pathlib import Path
|
||||
|
||||
import squish.ordered as order
|
||||
from squish import Simulation, DomainParams
|
||||
from squish.common import Energy, OUTPUT_DIR
|
||||
|
||||
|
||||
def axis_settings(ax, widths):
|
||||
ax.invert_xaxis()
|
||||
ax.grid(zorder=0)
|
||||
ax.set_xticks([round(w, 2) for w in widths[::-2]])
|
||||
ax.set_xticklabels(ax.get_xticks(), rotation=90)
|
||||
plt.subplots_adjust(0.07, 0.12, 0.97, 0.9)
|
||||
|
||||
|
||||
def main():
|
||||
# Loading arguments.
|
||||
parser = argparse.ArgumentParser("Outputs ordered equilibria lowest eigenvalues.")
|
||||
parser.add_argument(
|
||||
"n_objects",
|
||||
metavar="N",
|
||||
type=int,
|
||||
help="folder that contains simulation files, or cached data file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q",
|
||||
"--quiet",
|
||||
dest="quiet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="suppress all normal output",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
widths = np.round(np.linspace(3.0, 10.0, 141), 2)
|
||||
|
||||
values = []
|
||||
with open("coercivity_eigs.pkl", "rb") as f:
|
||||
store_data = pickle.load(f)
|
||||
|
||||
for width in widths:
|
||||
eig_vals = []
|
||||
for config, eigs in store_data[width].items():
|
||||
zero_ind = np.where(np.isclose(eigs, 0, atol=1e-4))[0][0]
|
||||
if zero_ind == 0:
|
||||
eig_vals.append(eigs[2])
|
||||
else:
|
||||
eig_vals.append(eigs[0])
|
||||
|
||||
values.append(min(eig_vals))
|
||||
|
||||
# for i, width in enumerate(widths):
|
||||
# domain = DomainParams(args.n_objects, width, 10, 4.0)
|
||||
# eig_vals = []
|
||||
# store_data[width] = {}
|
||||
# configs = order.configurations(domain)
|
||||
# for j, config in enumerate(configs):
|
||||
# if config == (1,0):
|
||||
# continue
|
||||
# points = order.sites(domain, config)
|
||||
|
||||
# hess = Energy("radial-t").mode(*domain, points).hessian(10e-5)
|
||||
# eigs = np.sort(np.linalg.eig(hess)[0])[::-1]
|
||||
# store_data[width][config] = eigs
|
||||
|
||||
# zero_ind = np.where(np.isclose(eigs, 0))[0][0]
|
||||
# if zero_ind == 0:
|
||||
# eig_vals.append(eigs[2])
|
||||
# else:
|
||||
# eig_vals.append(eigs[0])
|
||||
|
||||
# hashes = int(21*j/len(widths))
|
||||
# print(f'Generating at {width}, {i+1}/{len(widths)}... |{"#"*hashes}{" "*(20-hashes)}|' + \
|
||||
# f' {j+1}/{len(configs)} configs done.', flush=True, end='\r')
|
||||
|
||||
# print(flush=True)
|
||||
|
||||
# with open("coercivity_eigs.pkl", "wb") as f:
|
||||
# pickle.dump(store_data, f, pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
# return
|
||||
fig, ax = plt.subplots(figsize=(12, 8))
|
||||
plt.subplots_adjust(0.07, 0.12, 0.97, 0.9)
|
||||
|
||||
ax.plot(widths, values)
|
||||
|
||||
ax.invert_xaxis()
|
||||
ax.grid(zorder=0)
|
||||
ax.set_xticks([round(w, 2) for w in widths[::-2]])
|
||||
ax.set_xticklabels(ax.get_xticks(), rotation=90)
|
||||
|
||||
fig.suptitle("Coercivity")
|
||||
# ax.set_xlim([0, 5])
|
||||
ax.set_xlabel("Width")
|
||||
ax.set_ylabel("Eigenvalue")
|
||||
|
||||
fig.savefig(OUTPUT_DIR / "Coercivity.png")
|
||||
print(f"Wrote to {OUTPUT_DIR / 'Coercivity.png'}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ["QT_LOGGING_RULES"] = "*=false"
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("Program terminated by user.")
|
||||
@ -9,27 +9,40 @@ from squish.common import OUTPUT_DIR
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser("Graphs convergence graphs for a collection of simulations.")
|
||||
parser.add_argument('sims_path', metavar='path/to/data',
|
||||
help="folder that contains simulation files at various step sizes.")
|
||||
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', default=False,
|
||||
help="suppress all normal output")
|
||||
parser = argparse.ArgumentParser(
|
||||
"Graphs convergence graphs for a collection of simulations."
|
||||
)
|
||||
parser.add_argument(
|
||||
"sims_path",
|
||||
metavar="path/to/data",
|
||||
help="folder that contains simulation files at various step sizes.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q",
|
||||
"--quiet",
|
||||
dest="quiet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="suppress all normal output",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
data = {}
|
||||
|
||||
for file in Path(args.sims_path).iterdir():
|
||||
sim, frames = Simulation.load(file / "data.squish")
|
||||
sim, frames = Simulation.load(file)
|
||||
|
||||
step = sim.step_size
|
||||
data[step] = {"times": [], "values": [], "diffs": []}
|
||||
for i, frame_info in enumerate(frames):
|
||||
data[step]["times"].append(step * i)
|
||||
data[step]["values"].append(np.linalg.norm(frame_info["arr"]))
|
||||
data[step]["diffs"].append(np.linalg.norm(all_info[-1]["arr"] - frame_info["arr"]))
|
||||
data[step]["diffs"].append(
|
||||
np.linalg.norm(all_info[-1]["arr"] - frame_info["arr"])
|
||||
)
|
||||
|
||||
fig, ax = plt.subplots(1, 2, figsize=(16, 8))
|
||||
plt.subplots_adjust(.07, .12, .97, .9)
|
||||
plt.subplots_adjust(0.07, 0.12, 0.97, 0.9)
|
||||
|
||||
for step, d in data.items():
|
||||
ax[0].plot(d["times"], d["values"], label=step)
|
||||
@ -50,7 +63,7 @@ def main():
|
||||
print(f"Wrote to {OUTPUT_DIR / 'Equilibrium Convergence.png'}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
os.environ["QT_LOGGING_RULES"] = "*=false"
|
||||
try:
|
||||
main()
|
||||
|
||||
@ -10,29 +10,48 @@ from squish.common import OUTPUT_DIR
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser("Graphs average defects at N.")
|
||||
parser.add_argument('sims_path', metavar='path/to/data',
|
||||
help="folder that contains simulation files at various Ns.")
|
||||
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', default=False,
|
||||
help="suppress all normal output")
|
||||
parser.add_argument(
|
||||
"sims_path",
|
||||
metavar="path/to/data",
|
||||
help="folder that contains simulation files at various Ns.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q",
|
||||
"--quiet",
|
||||
dest="quiet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="suppress all normal output",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
data = {}
|
||||
|
||||
|
||||
for file in Path(args.sims_path).iterdir():
|
||||
sim, frames = Simulation.load(file / "data.squish")
|
||||
files = list(Path(args.sims_path).iterdir())
|
||||
for i, file in enumerate(files):
|
||||
sim, frames = Simulation.load(file)
|
||||
avg_defects = 0
|
||||
count = 0
|
||||
|
||||
for frame in frames:
|
||||
if np.var(frame["stats"]["avg_radius"]) > 1e-8:
|
||||
avg_defects += np.count_nonzero(frame["stats"]["site_edge_count"] != 6)
|
||||
|
||||
count += 1
|
||||
|
||||
|
||||
avg_defects /= (1 if count == 0 else count)
|
||||
avg_defects /= 1 if count == 0 else count
|
||||
data[sim.domain.n] = avg_defects
|
||||
|
||||
hashes = int(21 * i / len(files))
|
||||
print(
|
||||
f'Processed N={sim.domain.n:03} |{"#"*hashes}{" "*(20-hashes)}|'
|
||||
+ f" {i+1}/{len(files)} simulations processed.",
|
||||
flush=True,
|
||||
end="\r",
|
||||
)
|
||||
|
||||
print(flush=True)
|
||||
|
||||
data = sorted(data.items())
|
||||
ns, defects = np.array([x[0] for x in data]), np.array([x[1] for x in data])
|
||||
|
||||
@ -44,9 +63,9 @@ def main():
|
||||
corrected.append(x)
|
||||
|
||||
fig, ax = plt.subplots(1, 2, figsize=(16, 8))
|
||||
plt.subplots_adjust(.07, .12, .97, .9)
|
||||
plt.subplots_adjust(0.07, 0.12, 0.97, 0.9)
|
||||
|
||||
fig.suptitle("Defects at N")
|
||||
fig.suptitle("Defects vs. Number of Sites (N)")
|
||||
|
||||
m0, b0 = np.polyfit(ns, defects, 1)
|
||||
|
||||
@ -57,30 +76,31 @@ def main():
|
||||
ax[0].set_xlabel("N")
|
||||
ax[0].set_ylabel("Average Defects")
|
||||
|
||||
x, y = np.log10(ns), np.log10(corrected)
|
||||
x, y = np.log(ns), np.log(corrected)
|
||||
m, b = np.polyfit(x, y, 1)
|
||||
|
||||
x2, y2 = x[14:], np.log10(defects[14:])
|
||||
x2, y2 = x[40:], np.log(defects[40:])
|
||||
m2, b2 = np.polyfit(x2, y2, 1)
|
||||
|
||||
ax[1].plot(x, y, linestyle='dotted', color='C0')
|
||||
ax[1].plot(x, np.log10(defects))
|
||||
ax[1].plot(x, y, linestyle="dotted", color="C0")
|
||||
ax[1].plot(x, np.log(defects))
|
||||
|
||||
ax[1].plot(x, m*x+b, label=f"All N: {m:.5f}")
|
||||
ax[1].plot(x2, m2*x2+b2, label=f"N $\\geq$ 25: {m2:.5f}")
|
||||
# ax[1].plot(x, m*x+b, label=f"All N: {m:.5f}")
|
||||
ax[1].plot(x2, m2 * x2 + b2, label=f"N $\\geq$ 40: {m2:.5f}")
|
||||
ax[1].grid(zorder=0)
|
||||
ax[1].legend()
|
||||
ax[1].set_xlabel("log10 N")
|
||||
ax[1].set_ylabel("log10 Average Defects")
|
||||
|
||||
|
||||
|
||||
ax[1].set_xticks(np.arange(3.75, 6.25, 0.25))
|
||||
ax[1].set_yticks(np.arange(0, 4.5, 0.5))
|
||||
ax[1].set_xlim(3.75, 6)
|
||||
ax[1].set_ylim(0, 4)
|
||||
ax[1].set_xlabel("$\\ln$ N")
|
||||
ax[1].set_ylabel("$\\ln$ Average Defects")
|
||||
|
||||
fig.savefig(OUTPUT_DIR / "DefectsN.png")
|
||||
print(f"Wrote to {OUTPUT_DIR / 'DefectsN.png'}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
os.environ["QT_log10GING_RULES"] = "*=false"
|
||||
try:
|
||||
main()
|
||||
|
||||
@ -10,28 +10,32 @@ from squish.common import OUTPUT_DIR
|
||||
|
||||
|
||||
def eq_file_process(file: Path) -> Tuple[float, List[float], List[float]]:
|
||||
sim, frames = Simulation.load(file / 'data.squish')
|
||||
sim, frames = Simulation.load(file)
|
||||
|
||||
alls = []
|
||||
for frame_info in frames:
|
||||
alls.append([
|
||||
alls.append(
|
||||
[
|
||||
frame_info["energy"],
|
||||
np.var(frame_info["stats"]["avg_radius"]) <= 1e-8,
|
||||
np.count_nonzero(frame_info["stats"]["site_edge_count"] != 6)
|
||||
])
|
||||
np.count_nonzero(frame_info["stats"]["site_edge_count"] != 6),
|
||||
]
|
||||
)
|
||||
|
||||
sim, frames = Simulation.load(file / 'data.squish')
|
||||
sim, frames = Simulation.load(file)
|
||||
sim.frames = list(frames)
|
||||
counts = sim.get_distinct()
|
||||
|
||||
distincts = []
|
||||
for j, frame_info in enumerate(sim.frames):
|
||||
distincts.append([
|
||||
distincts.append(
|
||||
[
|
||||
frame_info["energy"],
|
||||
np.var(frame_info["stats"]["avg_radius"]) <= 1e-8,
|
||||
np.count_nonzero(frame_info["stats"]["site_edge_count"] != 6),
|
||||
counts[j]
|
||||
])
|
||||
counts[j],
|
||||
]
|
||||
)
|
||||
|
||||
return sim.domain.w, alls, distincts
|
||||
|
||||
@ -39,7 +43,7 @@ def eq_file_process(file: Path) -> Tuple[float, List[float], List[float]]:
|
||||
def get_equilibria_data(filepath: Path) -> Tuple[Dict, numpy.ndarray, DomainParams]:
|
||||
data = {"all": {}, "distinct": {}}
|
||||
files = list(Path(filepath).iterdir())
|
||||
sim, frames = Simulation.load(files[0] / 'data.squish')
|
||||
sim, frames = Simulation.load(files[0])
|
||||
|
||||
with Pool(cpu_count()) as pool:
|
||||
for i, res in enumerate(pool.imap_unordered(eq_file_process, files)):
|
||||
@ -47,8 +51,12 @@ def get_equilibria_data(filepath: Path) -> Tuple[Dict, numpy.ndarray, DomainPara
|
||||
data["distinct"][res[0]] = res[2]
|
||||
|
||||
hashes = int(21 * i / len(files))
|
||||
print(f'Loading simulations for N={sim.domain.n}... |{"#"*hashes}{" "*(20-hashes)}|' + \
|
||||
f' {i+1}/{len(files)} simulations loaded.', flush=True, end='\r')
|
||||
print(
|
||||
f'Loading simulations for N={sim.domain.n}... |{"#"*hashes}{" "*(20-hashes)}|'
|
||||
+ f" {i+1}/{len(files)} simulations loaded.",
|
||||
flush=True,
|
||||
end="\r",
|
||||
)
|
||||
print(flush=True)
|
||||
|
||||
widths = np.asarray(sorted(data["all"]))
|
||||
@ -59,10 +67,19 @@ def get_equilibria_data(filepath: Path) -> Tuple[Dict, numpy.ndarray, DomainPara
|
||||
def main():
|
||||
# Loading arguments.
|
||||
parser = argparse.ArgumentParser("Outputs width search data into diagrams")
|
||||
parser.add_argument('sims_path', metavar='path/to/data',
|
||||
help="folder that contains simulation files of all searches for all N.")
|
||||
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', default=False,
|
||||
help="suppress all normal output")
|
||||
parser.add_argument(
|
||||
"sims_path",
|
||||
metavar="path/to/data",
|
||||
help="folder that contains simulation files of all searches for all N.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q",
|
||||
"--quiet",
|
||||
dest="quiet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="suppress all normal output",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@ -78,7 +95,9 @@ def main():
|
||||
disorder_count = []
|
||||
for width in widths:
|
||||
equal_shape = list([c[1] for c in sim_data["all"][width]])
|
||||
disorder_count.append(100*equal_shape.count(False)/len(sim_data["all"][width]))
|
||||
disorder_count.append(
|
||||
100 * equal_shape.count(False) / len(sim_data["all"][width])
|
||||
)
|
||||
|
||||
disorder_dict[domain.n] = disorder_count
|
||||
|
||||
@ -95,13 +114,19 @@ def main():
|
||||
fig, ax = plt.subplots(figsize=(12, 8))
|
||||
|
||||
extent = [min(widths), max(widths), min_n, max_n + 1]
|
||||
ax.imshow(disorder_arr, cmap='plasma', interpolation='nearest', aspect='auto', extent=extent)
|
||||
ax.imshow(
|
||||
disorder_arr,
|
||||
cmap="plasma",
|
||||
interpolation="nearest",
|
||||
aspect="auto",
|
||||
extent=extent,
|
||||
)
|
||||
|
||||
ax.invert_xaxis()
|
||||
ax.set_xticks([round(w, 2) for w in widths[::-2]])
|
||||
ax.set_xticklabels(ax.get_xticks(), rotation=90)
|
||||
ax.set_yticks(list(range(min_n, max_n + 1)))
|
||||
plt.subplots_adjust(.07, .12, .97, .9)
|
||||
plt.subplots_adjust(0.07, 0.12, 0.97, 0.9)
|
||||
|
||||
ax.title.set_text(filepath)
|
||||
ax.set_xlabel("Width")
|
||||
@ -111,7 +136,7 @@ def main():
|
||||
print(f"Wrote to {OUTPUT_DIR / filepath}.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
os.environ["QT_LOGGING_RULES"] = "*=false"
|
||||
try:
|
||||
main()
|
||||
|
||||
@ -9,13 +9,27 @@ from squish.common import OUTPUT_DIR
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser("Graphs perturbation graphs for a collection of simulations.")
|
||||
parser.add_argument('sims_path', metavar='path/to/data',
|
||||
help="folder that contains simulations of perturbations from an equilibrium.")
|
||||
parser.add_argument('end_path', metavar='path/to/equilbrium',
|
||||
help="NumPy binary (.npy) file that contains the equilibrium to compare to.")
|
||||
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', default=False,
|
||||
help="suppress all normal output")
|
||||
parser = argparse.ArgumentParser(
|
||||
"Graphs perturbation graphs for a collection of simulations."
|
||||
)
|
||||
parser.add_argument(
|
||||
"sims_path",
|
||||
metavar="path/to/data",
|
||||
help="folder that contains simulations of perturbations from an equilibrium.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"end_path",
|
||||
metavar="path/to/equilbrium",
|
||||
help="NumPy binary (.npy) file that contains the equilibrium to compare to.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q",
|
||||
"--quiet",
|
||||
dest="quiet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="suppress all normal output",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@ -23,10 +37,10 @@ def main():
|
||||
|
||||
data = {}
|
||||
for file in Path(args.sims_path).iterdir():
|
||||
k = float(file.name.split('k')[-1])
|
||||
k = float(file.name.split("k")[-1])
|
||||
delta = 10 ** k
|
||||
|
||||
sim, frames = Simulation.load(file / 'data.squish')
|
||||
sim, frames = Simulation.load(file)
|
||||
data[delta] = {"norm": [], "time": [], "k": k}
|
||||
|
||||
for i, frame in enumerate(frames):
|
||||
@ -36,24 +50,27 @@ def main():
|
||||
data[delta]["time"].append(sim.step_size * i)
|
||||
|
||||
fig, ax = plt.subplots(figsize=(12, 8))
|
||||
plt.subplots_adjust(.07, .12, .97, .9)
|
||||
plt.subplots_adjust(0.07, 0.12, 0.97, 0.9)
|
||||
|
||||
for delta in sorted(data):
|
||||
ax.plot(np.log10(np.array(data[delta]["time"])+1), np.log10(data[delta]["norm"]),
|
||||
label=f"k = {data[delta]['k']}")
|
||||
ax.plot(
|
||||
np.log10(np.array(data[delta]["time"]) + 1),
|
||||
np.log10(data[delta]["norm"]),
|
||||
label=f"k = {data[delta]['k']}",
|
||||
)
|
||||
|
||||
fig.suptitle("Equilibrium Perturbations")
|
||||
ax.grid(zorder=0)
|
||||
# ax.set_xlim([0, 5])
|
||||
ax.legend()
|
||||
ax.set_xlabel("Log Time")
|
||||
ax.set_ylabel("Log L2 Norm of Difference")
|
||||
ax.set_xlabel("$\\log_{10}$ Time")
|
||||
ax.set_ylabel("$\\log_{10}$ L2 Norm of Difference")
|
||||
|
||||
fig.savefig(OUTPUT_DIR / "Equilibrium Perturbations.png")
|
||||
print(f"Wrote to {OUTPUT_DIR / 'Equilibrium Perturbations.png'}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
os.environ["QT_LOGGING_RULES"] = "*=false"
|
||||
try:
|
||||
main()
|
||||
|
||||
@ -15,15 +15,22 @@ def order_process(domain: DomainParams) -> Tuple[float, float, float]:
|
||||
energies = []
|
||||
configs = order.configurations(domain)
|
||||
for config in configs:
|
||||
energies.append(2*domain.w*domain.h + \
|
||||
2*math.pi*domain.n*(domain.r**2 - 2*domain.r*order.avg_radius(domain, config)))
|
||||
energies.append(
|
||||
2 * domain.w * domain.h
|
||||
+ 2
|
||||
* math.pi
|
||||
* domain.n
|
||||
* (domain.r ** 2 - 2 * domain.r * order.avg_radius(domain, config))
|
||||
)
|
||||
|
||||
return domain.w, min(energies), max(energies)
|
||||
|
||||
|
||||
def get_ordered_energies(orig_domain: DomainParams, widths: np.ndarray) -> Dict:
|
||||
data = {}
|
||||
domains = [DomainParams(orig_domain.n, w, orig_domain.h, orig_domain.r) for w in widths]
|
||||
domains = [
|
||||
DomainParams(orig_domain.n, w, orig_domain.h, orig_domain.r) for w in widths
|
||||
]
|
||||
|
||||
with Pool(cpu_count()) as pool:
|
||||
mins, maxes = {}, {}
|
||||
@ -32,8 +39,12 @@ def get_ordered_energies(orig_domain: DomainParams, widths: np.ndarray) -> Dict:
|
||||
maxes[res[0]] = res[2]
|
||||
|
||||
hashes = int(21 * i / len(widths))
|
||||
print(f'Generating at width {res[0]:.02f}... |{"#"*hashes}{" "*(20-hashes)}|' + \
|
||||
f' {i+1}/{len(widths)} completed.', flush=True, end='\r')
|
||||
print(
|
||||
f'Generating at width {res[0]:.02f}... |{"#"*hashes}{" "*(20-hashes)}|'
|
||||
+ f" {i+1}/{len(widths)} completed.",
|
||||
flush=True,
|
||||
end="\r",
|
||||
)
|
||||
|
||||
print(flush=True)
|
||||
|
||||
@ -44,28 +55,32 @@ def get_ordered_energies(orig_domain: DomainParams, widths: np.ndarray) -> Dict:
|
||||
|
||||
|
||||
def eq_file_process(file: Path) -> Tuple[float, List[float], List[float]]:
|
||||
sim, frames = Simulation.load(file / 'data.squish')
|
||||
sim, frames = Simulation.load(file)
|
||||
|
||||
alls = []
|
||||
for frame_info in frames:
|
||||
alls.append([
|
||||
alls.append(
|
||||
[
|
||||
frame_info["energy"],
|
||||
np.var(frame_info["stats"]["avg_radius"]) <= 1e-8,
|
||||
np.count_nonzero(frame_info["stats"]["site_edge_count"] != 6)
|
||||
])
|
||||
np.count_nonzero(frame_info["stats"]["site_edge_count"] != 6),
|
||||
]
|
||||
)
|
||||
|
||||
sim, frames = Simulation.load(file / 'data.squish')
|
||||
sim, frames = Simulation.load(file)
|
||||
sim.frames = list(frames)
|
||||
counts = sim.get_distinct()
|
||||
|
||||
distincts = []
|
||||
for j, frame_info in enumerate(sim.frames):
|
||||
distincts.append([
|
||||
distincts.append(
|
||||
[
|
||||
frame_info["energy"],
|
||||
np.var(frame_info["stats"]["avg_radius"]) <= 1e-8,
|
||||
np.count_nonzero(frame_info["stats"]["site_edge_count"] != 6),
|
||||
counts[j]
|
||||
])
|
||||
counts[j],
|
||||
]
|
||||
)
|
||||
|
||||
return sim.domain.w, alls, distincts
|
||||
|
||||
@ -80,11 +95,15 @@ def get_equilibria_data(filepath: Path) -> Tuple[Dict, numpy.ndarray, DomainPara
|
||||
data["distinct"][res[0]] = res[2]
|
||||
|
||||
hashes = int(21 * i / len(files))
|
||||
print(f'Loading simulations... |{"#"*hashes}{" "*(20-hashes)}|' + \
|
||||
f' {i+1}/{len(files)} simulations loaded.', flush=True, end='\r')
|
||||
print(
|
||||
f'Loading simulations... |{"#"*hashes}{" "*(20-hashes)}|'
|
||||
+ f" {i+1}/{len(files)} simulations loaded.",
|
||||
flush=True,
|
||||
end="\r",
|
||||
)
|
||||
print(flush=True)
|
||||
|
||||
sim, frames = Simulation.load(files[0] / 'data.squish')
|
||||
sim, frames = Simulation.load(files[0])
|
||||
widths = np.asarray(sorted(data["all"]))
|
||||
domain = DomainParams(sim.domain.n, widths[-1], sim.domain.h, sim.domain.r)
|
||||
return data, widths, domain
|
||||
@ -95,16 +114,25 @@ def axis_settings(ax, widths):
|
||||
ax.grid(zorder=0)
|
||||
ax.set_xticks([round(w, 2) for w in widths[::-2]])
|
||||
ax.set_xticklabels(ax.get_xticks(), rotation=90)
|
||||
plt.subplots_adjust(.07, .12, .97, .9)
|
||||
plt.subplots_adjust(0.07, 0.12, 0.97, 0.9)
|
||||
|
||||
|
||||
def main():
|
||||
# Loading arguments.
|
||||
parser = argparse.ArgumentParser("Outputs width search data into diagrams")
|
||||
parser.add_argument('sims_path', metavar='path/to/data',
|
||||
help="folder that contains simulation files, or cached data file.")
|
||||
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', default=False,
|
||||
help="suppress all normal output")
|
||||
parser.add_argument(
|
||||
"sims_path",
|
||||
metavar="path/to/data",
|
||||
help="folder that contains simulation files, or cached data file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q",
|
||||
"--quiet",
|
||||
dest="quiet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="suppress all normal output",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@ -121,10 +149,17 @@ def main():
|
||||
all_disorder_count = []
|
||||
for width in widths:
|
||||
equal_shape = list([c[1] for c in data["all"][width]])
|
||||
all_disorder_count.append(100*equal_shape.count(False)/len(data["all"][width]))
|
||||
all_disorder_count.append(
|
||||
100 * equal_shape.count(False) / len(data["all"][width])
|
||||
)
|
||||
|
||||
ax.plot(widths, all_disorder_count)
|
||||
axis_settings(ax, widths)
|
||||
|
||||
with open("N83-prob.txt", "w") as f:
|
||||
f.write(", ".join([str(x) for x in widths]) + "\n")
|
||||
f.write(", ".join([str(x) for x in all_disorder_count]))
|
||||
|
||||
ax.yaxis.set_major_formatter(mtick.PercentFormatter())
|
||||
ax.title.set_text(f"Probability of Disorder - N{domain.n}")
|
||||
ax.set_xlabel("Width")
|
||||
@ -133,7 +168,6 @@ def main():
|
||||
ax.set_yticks(np.arange(boa_y_min, 100.01, 2.5))
|
||||
fig.savefig(fig_folder / "Probability of Disorder.png")
|
||||
|
||||
|
||||
# Density of States diagram.
|
||||
fig, ax = plt.subplots(figsize=(16, 8))
|
||||
distinct_ordered, distinct_unordered = [], []
|
||||
@ -143,13 +177,13 @@ def main():
|
||||
distinct_unordered.append(equal_shape.count(False))
|
||||
|
||||
ax2 = ax.twinx()
|
||||
ax.plot(widths, distinct_unordered, label="Unordered Equilibria", color='C0')
|
||||
ax2.plot(widths, distinct_ordered, label="Ordered Equilibria", color='C1')
|
||||
ax.plot(widths, distinct_unordered, label="Unordered Equilibria", color="C0")
|
||||
ax2.plot(widths, distinct_ordered, label="Ordered Equilibria", color="C1")
|
||||
axis_settings(ax, widths)
|
||||
ax.title.set_text(f"Density of States - N{domain.n}")
|
||||
ax.set_xlabel("Width")
|
||||
ax.set_ylabel("Number of States (Disordered)", color='C0')
|
||||
ax2.set_ylabel("Number of States (Ordered)", color='C1')
|
||||
ax.set_ylabel("Number of States (Disordered)", color="C0")
|
||||
ax2.set_ylabel("Number of States (Ordered)", color="C1")
|
||||
|
||||
dos_y_max_unorder = 1.05 * max(distinct_unordered)
|
||||
dos_y_max_order = 1.05 * max(distinct_ordered)
|
||||
@ -157,7 +191,6 @@ def main():
|
||||
# ax.set_yticks(np.arange(0, dos_y_max_unorder, round(dos_y_max_unorder/200, 1)*10))
|
||||
ax2.set_yticks(np.arange(0, dos_y_max_order))
|
||||
|
||||
|
||||
fig.savefig(fig_folder / "Density Of States.png")
|
||||
|
||||
# Defect density diagram
|
||||
@ -165,7 +198,10 @@ def main():
|
||||
|
||||
defects = []
|
||||
for width in widths:
|
||||
defects.append(sum([c[2] for c in data["all"][width] if not c[1]])/len(data["all"][width]))
|
||||
defects.append(
|
||||
sum([c[2] for c in data["all"][width] if not c[1]])
|
||||
/ len(data["all"][width])
|
||||
)
|
||||
|
||||
ax.plot(widths, defects)
|
||||
axis_settings(ax, widths)
|
||||
@ -175,7 +211,6 @@ def main():
|
||||
ax.set_yticks(np.arange(0, 1 + max(defects), 0.5))
|
||||
fig.savefig(fig_folder / "Defects.png")
|
||||
|
||||
|
||||
# Bifurcation diagram
|
||||
fig, ax = plt.subplots(figsize=(16, 8))
|
||||
|
||||
@ -204,15 +239,19 @@ def main():
|
||||
|
||||
min_unorder_off = min_unorder - offset
|
||||
max_unorder_off = max_unorder - offset
|
||||
ax.plot(widths, min_order - offset, color='C1')
|
||||
ax.plot(widths, min_order - offset, color="C1")
|
||||
# ax.plot(widths, max_order - offset, color='C1', linestyle='dotted')
|
||||
ax.plot(widths, min_unorder_off, color='C0')
|
||||
ax.plot(widths, max_unorder_off, color='C0', linestyle='dotted')
|
||||
ax.plot(widths, min_unorder_off, color="C0")
|
||||
ax.plot(widths, max_unorder_off, color="C0", linestyle="dotted")
|
||||
axis_settings(ax, widths)
|
||||
|
||||
with open("N83-od.txt", "w") as f:
|
||||
f.write(", ".join([str(x) for x in widths]) + "\n")
|
||||
f.write(", ".join([str(x) for x in min_unorder_off]) + "\n")
|
||||
f.write(", ".join([str(x) for x in max_unorder_off]))
|
||||
|
||||
for i in null_unorder:
|
||||
ax.scatter(widths[i], 0,
|
||||
marker='X', color="blue", s=50, zorder=4)
|
||||
ax.scatter(widths[i], 0, marker="X", color="blue", s=50, zorder=4)
|
||||
# ax.scatter(widths[i], max_unorder[i] - offset[i],
|
||||
# marker='X', edgecolors="blue", facecolors='none', s=100, zorder=4)
|
||||
|
||||
@ -220,13 +259,16 @@ def main():
|
||||
ax.set_xlabel("Width")
|
||||
ax.set_ylabel("Reduced Energy")
|
||||
bif_y_max = np.max(np.abs(np.concatenate((min_unorder_off, max_unorder_off))))
|
||||
bif_top = np.arange(0, bif_y_max, round(bif_y_max/20, -math.floor(math.log10(bif_y_max/20))))
|
||||
bif_top = np.arange(
|
||||
0, bif_y_max, round(bif_y_max / 20, -math.floor(math.log10(bif_y_max / 20)))
|
||||
)
|
||||
ax.set_yticks(np.concatenate((-bif_top[1:][::-1], bif_top)))
|
||||
fig.savefig(fig_folder / "Bifurcation.png")
|
||||
|
||||
print(f"Wrote to {fig_folder}.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ["QT_LOGGING_RULES"] = "*=false"
|
||||
try:
|
||||
main()
|
||||
|
||||
@ -10,16 +10,20 @@ OUTPUT_DIR = Path("squish_output")
|
||||
STR_TO_ENERGY = {
|
||||
"area": AreaEnergy,
|
||||
"radial-al": RadialALEnergy,
|
||||
"radial-t" : RadialTEnergy
|
||||
"radial-t": RadialTEnergy,
|
||||
}
|
||||
|
||||
|
||||
def generate_filepath(sim: SimulationMode, fol: Union[str, Path], prec: int = 2) -> Path:
|
||||
def generate_filepath(
|
||||
sim: SimulationMode, fol: Union[str, Path], prec: int = 2
|
||||
) -> Path:
|
||||
energy = sim.energy.title_str
|
||||
width, height = round(sim.domain.w, prec), round(sim.domain.h, prec)
|
||||
|
||||
base_path = f"{fol}/{energy}{sim.title_str} - " + \
|
||||
f"N{sim.domain.n} - {width:.{prec}f}x{height:.{prec}f}"
|
||||
base_path = (
|
||||
f"{fol}/{energy}{sim.title_str} - "
|
||||
+ f"N{sim.domain.n} - {width:.{prec}f}x{height:.{prec}f}"
|
||||
)
|
||||
|
||||
i = 1
|
||||
real_path = Path(base_path)
|
||||
@ -42,8 +46,7 @@ class DomainParams:
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ['n', 'w', 'h', 'r', 'dim']
|
||||
|
||||
__slots__ = ["n", "w", "h", "r", "dim"]
|
||||
|
||||
def __init__(self, n: int, w: float, h: float, r: float) -> None:
|
||||
if n < 2:
|
||||
@ -58,7 +61,6 @@ class DomainParams:
|
||||
self.n, self.w, self.h, self.r = int(n), float(w), float(h), float(r)
|
||||
self.dim = np.array([self.w, self.h])
|
||||
|
||||
|
||||
def __iter__(self) -> Iterator:
|
||||
return iter((self.n, self.w, self.h, self.r))
|
||||
|
||||
@ -74,26 +76,23 @@ class Energy:
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ['mode']
|
||||
|
||||
__slots__ = ["mode"]
|
||||
|
||||
def __init__(self, mode: Union[str, VoronoiContainer]) -> None:
|
||||
if isinstance(mode, str):
|
||||
try:
|
||||
self.mode = STR_TO_ENERGY[mode.lower()]
|
||||
except KeyError:
|
||||
raise ValueError(f"\'{mode}\' is not a valid energy!")
|
||||
raise ValueError(f"'{mode}' is not a valid energy!")
|
||||
else:
|
||||
if mode is not VoronoiContainer and issubclass(mode, VoronoiContainer):
|
||||
raise ValueError("Provided class is not a valid energy!")
|
||||
self.mode = mode
|
||||
|
||||
|
||||
@property
|
||||
def attr_str(self) -> str:
|
||||
return self.mode.attr_str
|
||||
|
||||
|
||||
@property
|
||||
def title_str(self) -> str:
|
||||
return self.mode.title_str
|
||||
@ -8,7 +8,10 @@ from multiprocessing import Pool, cpu_count
|
||||
|
||||
from .common import DomainParams, OUTPUT_DIR
|
||||
|
||||
SYMM = np.array([[0,0], [1,0], [1,1], [0,1], [-1,1], [-1,0], [-1,-1], [0,-1], [1,-1]])
|
||||
SYMM = np.array(
|
||||
[[0, 0], [1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
|
||||
)
|
||||
|
||||
|
||||
class SimData:
|
||||
"""Stores diagram information for a simulation.
|
||||
@ -22,8 +25,7 @@ class SimData:
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ['path', 'domains', 'energies', 'voronois', 'stats']
|
||||
|
||||
__slots__ = ["path", "domains", "energies", "voronois", "stats"]
|
||||
|
||||
def __init__(self, sim: Simulation) -> None:
|
||||
if sim is not None:
|
||||
@ -33,11 +35,9 @@ class SimData:
|
||||
self.voronois = list([s.vor_data for s in sim])
|
||||
self.stats = list([s.stats for s in sim])
|
||||
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.domains)
|
||||
|
||||
|
||||
def slice(self, indices: List[int]) -> SimData:
|
||||
new_data = SimData(None)
|
||||
new_data.path = self.path
|
||||
@ -49,17 +49,24 @@ class SimData:
|
||||
|
||||
return new_data
|
||||
|
||||
|
||||
def hist(self, stat: str, i: int, bins: int = 10, bounds: Optional[Tuple[float, float]] = None,
|
||||
cumul: bool = False, avg: bool = False) -> Tuple[numpy.ndarray, numpy.ndarray]:
|
||||
def hist(
|
||||
self,
|
||||
stat: str,
|
||||
i: int,
|
||||
bins: int = 10,
|
||||
bounds: Optional[Tuple[float, float]] = None,
|
||||
cumul: bool = False,
|
||||
avg: bool = False,
|
||||
) -> Tuple[numpy.ndarray, numpy.ndarray]:
|
||||
"""Generates a histogram from the selected data.
|
||||
|
||||
Arguments:
|
||||
stat (str): name of data to obtain
|
||||
i (int): which frame to select from
|
||||
bins (int): number of bins for the histogram.
|
||||
bounds (Optional[Tuple[float, float]]): upper and lower bounds of the histogram.
|
||||
this will automatically take the minimum and maximum value of not set.
|
||||
bounds (Optional[Tuple[float, float]]): upper and lower bounds of the
|
||||
histogram. This will automatically take the minimum and maximum value
|
||||
if not set.
|
||||
cumul (bool): aggregates all data up to frame i if True.
|
||||
avg (bool): will average the data based on number of frames if True.
|
||||
|
||||
@ -67,6 +74,7 @@ class SimData:
|
||||
Tuple[numpy.ndarray, numpy.ndarray]: the histogram and its bins.
|
||||
|
||||
"""
|
||||
|
||||
if cumul:
|
||||
values = np.concatenate([f[stat] for f in self.stats[: (i + 1)]])
|
||||
else:
|
||||
@ -81,7 +89,9 @@ class SimData:
|
||||
return hist, bin_list[not (bins % 2) :]
|
||||
|
||||
hist, bin_edges = np.histogram(values, bins=bins, range=bounds)
|
||||
bin_list = np.array([(bin_edges[i] + bin_edges[i+1])/2 for i in range(len(bin_edges)-1)])
|
||||
bin_list = np.array(
|
||||
[(bin_edges[i] + bin_edges[i + 1]) / 2 for i in range(len(bin_edges) - 1)]
|
||||
)
|
||||
|
||||
if avg and cumul:
|
||||
return hist / (i + 1), bin_list
|
||||
@ -99,15 +109,15 @@ class Diagram:
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ['sim', 'diagrams', 'cumulative']
|
||||
__slots__ = ["sim", "diagrams", "cumulative"]
|
||||
|
||||
|
||||
def __init__(self, sim: Simulation, diagrams: np.ndarray, cumulative: bool = False) -> None:
|
||||
def __init__(
|
||||
self, sim: Simulation, diagrams: np.ndarray, cumulative: bool = False
|
||||
) -> None:
|
||||
self.sim = SimData(sim)
|
||||
self.diagrams = np.atleast_2d(diagrams)
|
||||
self.cumulative = cumulative
|
||||
|
||||
|
||||
def generate_frame(self, frame: int, mode: str, fol: str, name: str = None) -> None:
|
||||
if mode not in ["save", "open"]:
|
||||
raise ValueError("Not a valid mode for diagrams!")
|
||||
@ -115,14 +125,14 @@ class Diagram:
|
||||
shape = self.diagrams.shape
|
||||
fig, axes = plt.subplots(*shape, figsize=(shape[1] * 8, shape[0] * 8))
|
||||
if self.diagrams.shape == (1, 1):
|
||||
getattr(self, str(self.diagrams[0][0]) + '_plot')(frame, axes)
|
||||
getattr(self, str(self.diagrams[0][0]) + "_plot")(frame, axes)
|
||||
else:
|
||||
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])
|
||||
getattr(self, str(diagram) + "_plot")(frame, axes[it.multi_index])
|
||||
|
||||
plt.tight_layout()
|
||||
if name is None:
|
||||
@ -134,33 +144,36 @@ class Diagram:
|
||||
elif mode == "show":
|
||||
plt.show()
|
||||
|
||||
|
||||
def voronoi_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
domain = self.sim.domains[i]
|
||||
n, w, h = domain.n, domain.w, domain.h
|
||||
scale = 1.5
|
||||
area = n <= 60
|
||||
|
||||
voronoi_plot_2d(self.sim.voronois[i], ax, show_vertices=False, point_size = 7-n/100)
|
||||
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')
|
||||
ax.axis('equal')
|
||||
voronoi_plot_2d(
|
||||
self.sim.voronois[i], ax, show_vertices=False, point_size=7 - n / 100
|
||||
)
|
||||
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")
|
||||
ax.axis("equal")
|
||||
ax.set_xlim([(1 - scale) * w / 2, (1 + 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)
|
||||
props = dict(boxstyle="round", facecolor="wheat", alpha=0.8)
|
||||
|
||||
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)
|
||||
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)))
|
||||
txt = ax.text(
|
||||
*vec, str(round(self.sim.stats[i]["site_areas"][j], 3))
|
||||
)
|
||||
txt.set_clip_on(True)
|
||||
|
||||
if self.sim.stats[i]["site_edge_count"][j] == 5:
|
||||
@ -173,17 +186,22 @@ class Diagram:
|
||||
ax.scatter(defects[5]["x"], defects[5]["y"], marker="p", color="red")
|
||||
ax.scatter(defects[7]["x"], defects[7]["y"], marker="*", color="red")
|
||||
|
||||
|
||||
ax.text(0.05, 0.95, f'Energy: {self.sim.energies[i]}', transform=ax.transAxes, fontsize=14,
|
||||
verticalalignment='top', bbox=props)
|
||||
|
||||
ax.text(
|
||||
0.05,
|
||||
0.95,
|
||||
f"Energy: {self.sim.energies[i]}",
|
||||
transform=ax.transAxes,
|
||||
fontsize=14,
|
||||
verticalalignment="top",
|
||||
bbox=props,
|
||||
)
|
||||
|
||||
def energy_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
ax.set_xlim([0, len(self.sim)])
|
||||
|
||||
energies = self.sim.energies[: (i + 1)]
|
||||
ax.plot(list(range(i + 1)), energies)
|
||||
ax.title.set_text('Energy vs. Time')
|
||||
ax.title.set_text("Energy vs. Time")
|
||||
# max_value = round(self.sim[0].energy)
|
||||
# min_value = round(self.sim[-1].energy)
|
||||
# diff = max_value-min_value
|
||||
@ -192,12 +210,11 @@ class Diagram:
|
||||
ax.set_ylabel("Energy")
|
||||
ax.grid()
|
||||
|
||||
|
||||
def site_areas_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
y, x = self.sim.hist("site_areas", i, cumul=self.cumulative, avg=True)
|
||||
|
||||
ax.bar(x, y, width=0.8 * (x[1] - x[0]))
|
||||
ax.title.set_text('Site Areas')
|
||||
ax.title.set_text("Site Areas")
|
||||
ax.set_xlabel("Area")
|
||||
ax.set_ylabel("Average Occurances")
|
||||
ax.set_xticks(x)
|
||||
@ -207,24 +224,26 @@ class Diagram:
|
||||
# if color != 'C0':
|
||||
# xtick.set_color(color)
|
||||
|
||||
|
||||
def site_edge_count_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
y, x = self.sim.hist("site_edge_count", i, bounds=(1, 11), cumul=self.cumulative, avg=True)
|
||||
y, x = self.sim.hist(
|
||||
"site_edge_count", i, bounds=(1, 11), cumul=self.cumulative, avg=True
|
||||
)
|
||||
|
||||
ax.bar(x, y, width=0.8 * (x[1] - x[0]))
|
||||
ax.title.set_text('Edges per Site')
|
||||
ax.title.set_text("Edges per Site")
|
||||
ax.set_xlabel("Number of Edges")
|
||||
ax.set_ylabel("Average Occurances")
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels([int(z) for z in x])
|
||||
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
|
||||
|
||||
|
||||
def site_isos_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
y, x = self.sim.hist("site_isos", i, bounds=(0,1), cumul=self.cumulative, avg=True)
|
||||
y, x = self.sim.hist(
|
||||
"site_isos", i, bounds=(0, 1), cumul=self.cumulative, avg=True
|
||||
)
|
||||
|
||||
ax.bar(x, y, width=0.8 * (x[1] - x[0]))
|
||||
ax.title.set_text('Isoparametric Values')
|
||||
ax.title.set_text("Isoparametric Values")
|
||||
ax.set_xlabel("Isoparametric Value")
|
||||
ax.set_ylabel("Average Occurances")
|
||||
ax.set_xticks(x)
|
||||
@ -234,83 +253,84 @@ class Diagram:
|
||||
# if color != 'C0':
|
||||
# xtick.set_color(color)
|
||||
|
||||
|
||||
def site_energies_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
y, x = self.sim.hist("site_energies", i, cumul=self.cumulative, avg=True)
|
||||
|
||||
ax.bar(x, y, width=0.8 * (x[1] - x[0]))
|
||||
ax.title.set_text('Site Energies')
|
||||
ax.title.set_text("Site Energies")
|
||||
ax.set_xlabel("Energy")
|
||||
ax.set_ylabel("Average Occurances")
|
||||
ax.set_xticks(x)
|
||||
ax.ticklabel_format(useOffset=False)
|
||||
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
|
||||
|
||||
|
||||
def avg_radius_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
y, x = self.sim.hist("avg_radius", i, cumul=self.cumulative, avg=True)
|
||||
|
||||
ax.bar(x, y, width=0.8 * (x[1] - x[0]))
|
||||
ax.title.set_text('Site Average Radii')
|
||||
ax.title.set_text("Site Average Radii")
|
||||
ax.set_xlabel("Average Radius")
|
||||
ax.set_ylabel("Average Occurances")
|
||||
ax.set_xticks(x)
|
||||
ax.ticklabel_format(useOffset=False)
|
||||
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
|
||||
|
||||
|
||||
def isoparam_avg_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
y, x = self.sim.hist("isoparam_avg", i, cumul=self.cumulative, avg=True)
|
||||
|
||||
ax.bar(x, y, width=0.8 * (x[1] - x[0]))
|
||||
ax.title.set_text('Site Isoperimetric Averages')
|
||||
ax.title.set_text("Site Isoperimetric Averages")
|
||||
ax.set_xlabel("Isoperimetric Average")
|
||||
ax.set_ylabel("Average Occurances")
|
||||
ax.set_xticks(x)
|
||||
ax.ticklabel_format(useOffset=False)
|
||||
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
|
||||
|
||||
|
||||
def edge_lengths_plot(self, i: int, ax: AxesSubplot) -> None:
|
||||
y, x = self.sim.hist("edge_lengths", i, 30, cumul=self.cumulative, avg=True)
|
||||
|
||||
ax.bar(x, y, width=0.8 * (x[1] - x[0]))
|
||||
ax.title.set_text('Edge Lengths')
|
||||
ax.title.set_text("Edge Lengths")
|
||||
ax.set_xlabel("Length")
|
||||
ax.set_ylabel("Average Occurances")
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(ax.get_xticks(), rotation=90)
|
||||
ax.xaxis.set_major_formatter(FormatStrFormatter('%.3f'))
|
||||
ax.xaxis.set_major_formatter(FormatStrFormatter("%.3f"))
|
||||
# ax.ticklabel_format(useOffset=False)
|
||||
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
|
||||
# for xtick, color in zip(ax.get_xticklabels(), lengths_bar[2]):
|
||||
# if color != 'C0':
|
||||
# xtick.set_color(color)
|
||||
|
||||
|
||||
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')
|
||||
ax.plot(
|
||||
list(range(len(eigs))), eigs, marker="o", linestyle="dashed", color="C0"
|
||||
)
|
||||
ax.plot([0, len(eigs)], [0, 0], color="red")
|
||||
except KeyError:
|
||||
ax.text(0.5, 0.5, "Not Computed")
|
||||
|
||||
ax.title.set_text('Hessian Eigenvalues')
|
||||
ax.title.set_text("Hessian Eigenvalues")
|
||||
ax.set_xlabel("")
|
||||
ax.set_ylabel("Value")
|
||||
|
||||
|
||||
def render_frames(self, frames: List[int], fol: str = 'frames') -> None:
|
||||
def render_frames(self, frames: List[int], fol: str = "frames") -> None:
|
||||
OUTPUT_DIR.mkdir(exist_ok=True)
|
||||
self.sim.path.mkdir(exist_ok=True)
|
||||
(self.sim.path / fol).mkdir(exist_ok=True)
|
||||
combo_list = []
|
||||
for i in range(cpu_count()):
|
||||
start, end = int(i*len(frames)/cpu_count()), int((i+1)*len(frames)/cpu_count())
|
||||
start, end = (
|
||||
int(i * len(frames) / cpu_count()),
|
||||
int((i + 1) * len(frames) / cpu_count()),
|
||||
)
|
||||
new_dia = Diagram(None, self.diagrams, self.cumulative)
|
||||
new_dia.sim = self.sim.slice(frames[start:end])
|
||||
combo_list.append((new_dia, fol, start, len(frames[start:end]), len(frames)))
|
||||
combo_list.append(
|
||||
(new_dia, fol, start, len(frames[start:end]), len(frames))
|
||||
)
|
||||
|
||||
# Free up memory, since it's already duplicated to other cores.
|
||||
self.sim = self.sim.slice([])
|
||||
@ -319,8 +339,7 @@ class Diagram:
|
||||
pass
|
||||
|
||||
print(flush=True)
|
||||
print(f'Wrote to \"{self.sim.path / fol}\".', flush=True)
|
||||
|
||||
print(f'Wrote to "{self.sim.path / fol}".', flush=True)
|
||||
|
||||
def render_video(self, time: int, mode: str) -> None:
|
||||
if mode not in ["use_all", "sample"]:
|
||||
@ -334,23 +353,27 @@ class Diagram:
|
||||
frames = list(range(len(self.sim)))
|
||||
fps = len(self.sim) / time
|
||||
else:
|
||||
frames = list(np.round(np.linspace(0, len(self.sim)-1, fps*time)).astype(int))
|
||||
frames = list(
|
||||
np.round(np.linspace(0, len(self.sim) - 1, fps * time)).astype(int)
|
||||
)
|
||||
|
||||
self.render_frames(frames, 'temp')
|
||||
path = self.sim.path / 'simulation.mp4'
|
||||
self.render_frames(frames, "temp")
|
||||
path = self.sim.path / "simulation.mp4"
|
||||
|
||||
print("Assembling MP4...", flush=True)
|
||||
os.system(f'ffmpeg -hide_banner -loglevel error -r {fps} -i' + \
|
||||
f' \"{self.sim.path}/temp/img%05d.png\"' + \
|
||||
f' -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -vf' + \
|
||||
f' "scale=trunc(iw/2)*2:trunc(ih/2)*2" -f mp4 "{path}"')
|
||||
os.system(
|
||||
f"ffmpeg -hide_banner -loglevel error -r {fps} -i"
|
||||
+ f' "{self.sim.path}/temp/img%05d.png"'
|
||||
+ f" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -vf"
|
||||
+ f' "scale=trunc(iw/2)*2:trunc(ih/2)*2" -f mp4 "{path}"'
|
||||
)
|
||||
|
||||
# Remove files.
|
||||
for i in range(len(frames)):
|
||||
os.remove(self.sim.path / f"temp/img{i:05}.png")
|
||||
|
||||
os.rmdir(self.sim.path / 'temp')
|
||||
print(f'Wrote to \"{path}\".', flush=True)
|
||||
os.rmdir(self.sim.path / "temp")
|
||||
print(f'Wrote to "{path}".', flush=True)
|
||||
|
||||
|
||||
def render_frame_range(combo: Tuple[Diagram, str, int, int, int]) -> None:
|
||||
@ -359,5 +382,9 @@ def render_frame_range(combo: Tuple[Diagram, str, int, int, int]) -> None:
|
||||
self.generate_frame(i, "save", fol, f"img{i+offset:05}.png")
|
||||
i = len(list((self.sim.path / fol).iterdir()))
|
||||
hashes = int(21 * i / num_frames)
|
||||
print(f'Generating frames... |{"#"*hashes}{" "*(20-hashes)}|' + \
|
||||
f' {i}/{num_frames} frames rendered.', flush=True, end='\r')
|
||||
print(
|
||||
f'Generating frames... |{"#"*hashes}{" "*(20-hashes)}|'
|
||||
+ f" {i}/{num_frames} frames rendered.",
|
||||
flush=True,
|
||||
end="\r",
|
||||
)
|
||||
|
||||
@ -6,6 +6,7 @@ from math import gcd, sqrt, log, tan, atan, pi
|
||||
|
||||
Config = Tuple[int, int]
|
||||
|
||||
|
||||
def configurations(domain: DomainParams) -> List[Config]:
|
||||
n, w, h = domain.n, domain.w, domain.h
|
||||
valid = []
|
||||
@ -18,7 +19,12 @@ def configurations(domain: DomainParams) -> List[Config]:
|
||||
if gcd(eq_x, eq_y) != 1:
|
||||
continue
|
||||
|
||||
vecs = configs[i]*np.dstack((w*mults, h*mults)).swapaxes(0,1)/n % domain.dim
|
||||
vecs = (
|
||||
configs[i]
|
||||
* np.dstack((w * mults, h * mults)).swapaxes(0, 1)
|
||||
/ n
|
||||
% domain.dim
|
||||
)
|
||||
vmod2 = np.squeeze(np.matmul(vecs, vecs.transpose(0, 2, 1)))
|
||||
vmodv = np.squeeze(vecs).dot(vecs[1].T).T.flatten()
|
||||
|
||||
@ -28,7 +34,9 @@ def configurations(domain: DomainParams) -> List[Config]:
|
||||
return valid
|
||||
|
||||
|
||||
def get_config_generators(domain: DomainParams, config: Config) -> Tuple[Config, Config]:
|
||||
def get_config_generators(
|
||||
domain: DomainParams, config: Config
|
||||
) -> Tuple[Config, Config]:
|
||||
n, w, h = domain.n, domain.w, domain.h
|
||||
q1 = sites(domain, config)
|
||||
v = q1[1]
|
||||
@ -39,9 +47,13 @@ def get_config_generators(domain: DomainParams, config: Config) -> Tuple[Config,
|
||||
tol = 1e-3
|
||||
vdot = np.matmul(all_sites, v)
|
||||
in_box = all_sites[np.where((-tol <= vdot) & (vdot <= (v.dot(v) + tol)))[0]]
|
||||
in_box = np.expand_dims(in_box, 0).swapaxes(0,1) # Used for the next step, getting site*site
|
||||
in_box = np.expand_dims(in_box, 0).swapaxes(
|
||||
0, 1
|
||||
) # Used for the next step, getting site*site
|
||||
|
||||
w = in_box[np.argmin(np.squeeze(np.matmul(in_box, in_box.transpose(0,2,1))))].flatten()
|
||||
w = in_box[
|
||||
np.argmin(np.squeeze(np.matmul(in_box, in_box.transpose(0, 2, 1))))
|
||||
].flatten()
|
||||
|
||||
return tuple(v), tuple(w)
|
||||
|
||||
@ -57,7 +69,11 @@ def area(domain: DomainParams, config: Config) -> float:
|
||||
v, w = np.array(v), np.array(w)
|
||||
c = circumcenter(v, w)
|
||||
|
||||
return mag(v)*mag(v/2 - c) + mag(w)*mag(w/2-c) + mag(v-w)*mag((v+w)/2-c)
|
||||
return (
|
||||
mag(v) * mag(v / 2 - c)
|
||||
+ mag(w) * mag(w / 2 - c)
|
||||
+ mag(v - w) * mag((v + w) / 2 - c)
|
||||
)
|
||||
|
||||
|
||||
def avg_radius(domain: DomainParams, config: Config) -> float:
|
||||
@ -65,12 +81,15 @@ def avg_radius(domain: DomainParams, config: Config) -> float:
|
||||
v, w = np.array(v), np.array(w)
|
||||
c = circumcenter(v, w)
|
||||
|
||||
return 2*(avg_rp(mag(v), 2*mag(v/2 - c)) + avg_rp(mag(w), 2*mag(w/2-c)) + \
|
||||
avg_rp(mag(v-w),2*mag((v+w)/2-c)))
|
||||
return 2 * (
|
||||
avg_rp(mag(v), 2 * mag(v / 2 - c))
|
||||
+ avg_rp(mag(w), 2 * mag(w / 2 - c))
|
||||
+ avg_rp(mag(v - w), 2 * mag((v + w) / 2 - c))
|
||||
)
|
||||
|
||||
|
||||
def avg_rp(d: float, l: float) -> float:
|
||||
return (d/(4*pi))*log(tan(.5*(atan(l/d)+pi/2))**2)
|
||||
return (d / (4 * pi)) * log(tan(0.5 * (atan(l / d) + pi / 2)) ** 2)
|
||||
|
||||
|
||||
def circumcenter(v: numpy.ndarray, w: numpy.ndarray) -> Config:
|
||||
|
||||
@ -21,10 +21,11 @@ class Simulation:
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ['domain', 'energy', 'path', 'frames']
|
||||
__slots__ = ["domain", "energy", "path", "frames"]
|
||||
|
||||
def __init__(self, domain: DomainParams, energy: Energy, \
|
||||
name: Optional[str, int] = None) -> None:
|
||||
def __init__(
|
||||
self, domain: DomainParams, energy: Energy, name: Optional[str, int] = None
|
||||
) -> None:
|
||||
self.domain, self.energy = domain, energy
|
||||
self.frames = []
|
||||
|
||||
@ -35,18 +36,15 @@ class Simulation:
|
||||
else:
|
||||
self.path = OUTPUT_DIR / name
|
||||
|
||||
|
||||
def __iter__(self) -> Iterator:
|
||||
return iter(self.frames)
|
||||
|
||||
def __getitem__(self, key: int) -> Energy:
|
||||
return self.frames[key]
|
||||
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.frames)
|
||||
|
||||
|
||||
def add_frame(self, points: Optional[numpy.ndarray] = None) -> None:
|
||||
if points is None:
|
||||
points = np.random.random_sample((self.domain.n, 2)) * self.domain.dim
|
||||
@ -59,7 +57,6 @@ class Simulation:
|
||||
|
||||
self.frames.append(self.energy.mode(*self.domain, points % self.domain.dim))
|
||||
|
||||
|
||||
def get_distinct(self) -> List[int]:
|
||||
"""Gets the distinct configurations based on the average radii of the sites.
|
||||
and returns the number of configurations for each distinct configuration.
|
||||
@ -67,7 +64,6 @@ class Simulation:
|
||||
|
||||
distinct_avg_radii, distinct_count, new_frames = [], [], []
|
||||
|
||||
|
||||
for frame in self.frames:
|
||||
try:
|
||||
stats = frame.stats
|
||||
@ -90,38 +86,35 @@ class Simulation:
|
||||
self.frames = new_frames
|
||||
return distinct_count
|
||||
|
||||
|
||||
def save(self, info: Dict, overwrite: bool = False) -> None:
|
||||
OUTPUT_DIR.mkdir(exist_ok=True)
|
||||
self.path.mkdir(exist_ok=True)
|
||||
path = self.path / 'data.squish'
|
||||
path = self.path / "data.squish"
|
||||
|
||||
with open(path, 'wb' if overwrite else 'ab') as out:
|
||||
with open(path, "wb" if overwrite else "ab") as out:
|
||||
pickle.dump(info, out, pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
|
||||
def save_all(self) -> None:
|
||||
self.save(self.initial_data, True)
|
||||
for i in range(len(self.frames)):
|
||||
self.save(self.frame_data(i))
|
||||
|
||||
|
||||
def frame_data(self, index: int) -> None:
|
||||
f = self[index]
|
||||
info = {
|
||||
"arr": f.site_arr,
|
||||
"domain": (f.n, f.w, f.h, f.r),
|
||||
"energy": f.energy,
|
||||
"stats": f.stats
|
||||
"stats": f.stats,
|
||||
}
|
||||
return info
|
||||
|
||||
|
||||
@staticmethod
|
||||
def load(path: str) -> Tuple[Simulation, Generator]:
|
||||
path = Path(path)
|
||||
|
||||
def frames() -> Dict:
|
||||
with open(path / 'data.squish', 'rb') as infile:
|
||||
with open(path / "data.squish", "rb") as infile:
|
||||
first = True
|
||||
while True:
|
||||
try:
|
||||
@ -133,17 +126,18 @@ class Simulation:
|
||||
except EOFError:
|
||||
break
|
||||
|
||||
with open(path / 'data.squish', 'rb') as infile:
|
||||
with open(path / "data.squish", "rb") as infile:
|
||||
sim_info = pickle.load(infile)
|
||||
|
||||
domain = DomainParams(*sim_info["domain"])
|
||||
energy = Energy(sim_info["energy"])
|
||||
sim = STR_TO_SIM[sim_info["mode"]](domain, energy, *list(sim_info.values())[3:])
|
||||
sim = STR_TO_SIM[sim_info["mode"]](
|
||||
domain, energy, *list(sim_info.values())[3:]
|
||||
)
|
||||
sim.path = path
|
||||
|
||||
return sim, frames()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def from_file(path: str) -> Simulation:
|
||||
sim, frames = Simulation.load(path)
|
||||
@ -167,16 +161,22 @@ class Flow(Simulation):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ['step_size', 'thres', 'adaptive']
|
||||
__slots__ = ["step_size", "thres", "adaptive"]
|
||||
attr_str = "flow"
|
||||
title_str = "Flow"
|
||||
|
||||
def __init__(self, domain: DomainParams, energy: Energy, step_size: float, thres: float,
|
||||
adaptive: bool, name: Optional[str] = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
domain: DomainParams,
|
||||
energy: Energy,
|
||||
step_size: float,
|
||||
thres: float,
|
||||
adaptive: bool,
|
||||
name: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(domain, energy, name=name)
|
||||
self.step_size, self.thres, self.adaptive = step_size, thres, adaptive
|
||||
|
||||
|
||||
@property
|
||||
def initial_data(self) -> Dict:
|
||||
info = {
|
||||
@ -185,16 +185,23 @@ class Flow(Simulation):
|
||||
"energy": self.energy.attr_str,
|
||||
"step_size": self.step_size,
|
||||
"thres": self.thres,
|
||||
"adaptive": self.adaptive
|
||||
"adaptive": self.adaptive,
|
||||
}
|
||||
return info
|
||||
|
||||
|
||||
def run(self, save: bool, log: bool, log_steps: int,
|
||||
new_sites: Optional[numpy.ndarray] = None) -> None:
|
||||
if log: print(f"Find - {self.domain}", flush=True)
|
||||
if save and len(self) == 0: self.save(self.initial_data, True)
|
||||
if len(self) == 0: self.add_frame(new_sites)
|
||||
def run(
|
||||
self,
|
||||
save: bool,
|
||||
log: bool,
|
||||
log_steps: int,
|
||||
new_sites: Optional[numpy.ndarray] = None,
|
||||
) -> None:
|
||||
if log:
|
||||
print(f"Find - {self.domain}", flush=True)
|
||||
if save and len(self) == 0:
|
||||
self.save(self.initial_data, True)
|
||||
if len(self) == 0:
|
||||
self.add_frame(new_sites)
|
||||
|
||||
i, stop = len(self) - 1, False
|
||||
|
||||
@ -217,7 +224,7 @@ class Flow(Simulation):
|
||||
error = change - grad * self.step_size
|
||||
tol = 10 ** min(-3, -2 + log10(grad_norm))
|
||||
|
||||
self.step_size *= (tol/np.linalg.norm(error))**.5
|
||||
self.step_size *= (tol / np.linalg.norm(error)) ** 0.5
|
||||
|
||||
if not save:
|
||||
del self.frames[0]
|
||||
@ -227,10 +234,12 @@ class Flow(Simulation):
|
||||
|
||||
i += 1
|
||||
if (log and i % log_steps == 0) or stop:
|
||||
print(f'Iteration: {i:05} | Energy: {frame.energy: .5f}' + \
|
||||
f' | Gradient: {grad_norm:.8f} | Step: {self.step_size: .5f} | ' + \
|
||||
f'Time: {end-start: .3f} |', flush=True)
|
||||
|
||||
print(
|
||||
f"Iteration: {i:05} | Energy: {frame.energy: .5f}"
|
||||
+ f" | Gradient: {grad_norm:.8f} | Step: {self.step_size: .5f} | "
|
||||
+ f"Time: {end-start: .3f} |",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
|
||||
class Search(Simulation):
|
||||
@ -249,18 +258,25 @@ class Search(Simulation):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ['step_size', 'thres', 'adaptive', 'kernel_step', 'count']
|
||||
__slots__ = ["step_size", "thres", "adaptive", "kernel_step", "count"]
|
||||
attr_str = "search"
|
||||
title_str = "Search"
|
||||
|
||||
def __init__(self, domain: DomainParams, energy: Energy, step_size: float, thres: float,
|
||||
adaptive: bool, kernel_step: float, count: int,
|
||||
name: Optional[str] = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
domain: DomainParams,
|
||||
energy: Energy,
|
||||
step_size: float,
|
||||
thres: float,
|
||||
adaptive: bool,
|
||||
kernel_step: float,
|
||||
count: int,
|
||||
name: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(domain, energy, name=name)
|
||||
self.step_size, self.thres, self.adaptive = step_size, thres, adaptive
|
||||
self.kernel_step, self.count = kernel_step, count
|
||||
|
||||
|
||||
@property
|
||||
def initial_data(self) -> Dict:
|
||||
info = {
|
||||
@ -271,39 +287,53 @@ class Search(Simulation):
|
||||
"thres": self.thres,
|
||||
"adaptive": self.adaptive,
|
||||
"kernel_step": self.kernel_step,
|
||||
"count": self.count
|
||||
"count": self.count,
|
||||
}
|
||||
return info
|
||||
|
||||
|
||||
def run(self, save: bool, log: bool, log_steps: int,
|
||||
new_sites: Optional[numpy.ndarray] = None) -> None:
|
||||
if log: print(f'Travel - {self.domain}', flush=True)
|
||||
if save and len(self) == 0: self.save(self.initial_data, True)
|
||||
def run(
|
||||
self,
|
||||
save: bool,
|
||||
log: bool,
|
||||
log_steps: int,
|
||||
new_sites: Optional[numpy.ndarray] = None,
|
||||
) -> None:
|
||||
if log:
|
||||
print(f"Travel - {self.domain}", flush=True)
|
||||
if save and len(self) == 0:
|
||||
self.save(self.initial_data, True)
|
||||
|
||||
for i in range(len(self), self.count):
|
||||
# Get to equilibrium.
|
||||
sim = Flow(self.domain, self.energy, self.step_size, self.thres, self.adaptive)
|
||||
sim = Flow(
|
||||
self.domain, self.energy, self.step_size, self.thres, self.adaptive
|
||||
)
|
||||
sim.add_frame(new_sites)
|
||||
sim.run(False, log, log_steps)
|
||||
|
||||
self.frames.append(sim[-1])
|
||||
if save: self.save(self.frame_data(i))
|
||||
if log: print(f'Equilibrium: {i:04}\n', flush=True)
|
||||
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))
|
||||
zero_eigs = np.count_nonzero(
|
||||
np.isclose(eigs, np.zeros((len(eigs),)), atol=1e-4)
|
||||
)
|
||||
|
||||
if zero_eigs == 2:
|
||||
new_sites = None
|
||||
else:
|
||||
print("Warning: Nullity > 2. Expected if AreaEnergy.", flush=True)
|
||||
ns = null_space(hess, 10e-4).T
|
||||
vec = ns[random.randint(0, len(ns)-1)].reshape((self.domain.n, 2)) # Random vector.
|
||||
vec = ns[random.randint(0, len(ns) - 1)].reshape(
|
||||
(self.domain.n, 2)
|
||||
) # Random vector.
|
||||
new_sites = self.frames[i].add_sites(self.kernel_step * vec)
|
||||
|
||||
|
||||
@ -312,7 +342,7 @@ class Shrink(Simulation):
|
||||
|
||||
Attributes:
|
||||
domain (DomainParams): domain parameters for this simulation.
|
||||
energy (Energy): energy being used for caluclations.
|
||||
energy (Energy): energy being used for calcu1lations.
|
||||
path (Path): path to the location of where to store simulation files.
|
||||
frames (List[VoronoiContainer]): stores frames of the simulation.
|
||||
step_size (float): size fo step by for each iteration.
|
||||
@ -323,18 +353,27 @@ class Shrink(Simulation):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ['step_size', 'thres', 'adaptive', 'delta', 'stop_width']
|
||||
__slots__ = ["step_size", "thres", "adaptive", "delta", "stop_width"]
|
||||
attr_str = "shrink"
|
||||
title_str = "Shrink"
|
||||
|
||||
|
||||
def __init__(self, domain: DomainParams, energy: Energy, step_size: float, thres: float,
|
||||
adaptive: bool, delta: float, stop_width: float,
|
||||
name: Optional[str] = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
domain: DomainParams,
|
||||
energy: Energy,
|
||||
step_size: float,
|
||||
thres: float,
|
||||
adaptive: bool,
|
||||
delta: float,
|
||||
stop_width: float,
|
||||
name: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(domain, energy, name=name)
|
||||
self.step_size, self.thres, self.adaptive = step_size, thres, adaptive
|
||||
self.delta, self.stop_width = self.domain.w*delta/100, self.domain.w*stop_width
|
||||
|
||||
self.delta, self.stop_width = (
|
||||
self.domain.w * delta / 100,
|
||||
self.domain.w * stop_width,
|
||||
)
|
||||
|
||||
@property
|
||||
def initial_data(self) -> Dict:
|
||||
@ -346,37 +385,45 @@ class Shrink(Simulation):
|
||||
"thres": self.thres,
|
||||
"adaptive": self.adaptive,
|
||||
"delta": self.delta,
|
||||
"stop_width": self.stop_width
|
||||
"stop_width": self.stop_width,
|
||||
}
|
||||
return info
|
||||
|
||||
|
||||
def run(self, save: bool, log: bool, log_steps: int,
|
||||
new_sites: Optional[numpy.ndarray] = None) -> None:
|
||||
if log: print(f'Shrink - {self.domain}', flush=True)
|
||||
if save and len(self) == 0: self.save(self.initial_data, True)
|
||||
def run(
|
||||
self,
|
||||
save: bool,
|
||||
log: bool,
|
||||
log_steps: int,
|
||||
new_sites: Optional[numpy.ndarray] = None,
|
||||
) -> None:
|
||||
if log:
|
||||
print(f"Shrink - {self.domain}", flush=True)
|
||||
if save and len(self) == 0:
|
||||
self.save(self.initial_data, True)
|
||||
|
||||
width = self.domain.w if len(self.frames) == 0 else self.frames[-1].w
|
||||
i = 0
|
||||
while width >= self.stop_width:
|
||||
# Get to equilibrium.
|
||||
new_domain = DomainParams(self.domain.n, width, self.domain.h, self.domain.r)
|
||||
sim = Flow(new_domain, self.energy, self.step_size, self.thres, self.adaptive)
|
||||
new_domain = DomainParams(
|
||||
self.domain.n, width, self.domain.h, self.domain.r
|
||||
)
|
||||
sim = Flow(
|
||||
new_domain, self.energy, self.step_size, self.thres, self.adaptive
|
||||
)
|
||||
sim.add_frame(new_sites)
|
||||
sim.run(False, log, log_steps)
|
||||
new_sites = sim[-1].site_arr
|
||||
|
||||
self.frames.append(sim[-1])
|
||||
if save: self.save(self.frame_data(i))
|
||||
if save:
|
||||
self.save(self.frame_data(i))
|
||||
|
||||
if log: print(f'Width: {width:.4f}\n')
|
||||
if log:
|
||||
print(f"Width: {width:.4f}\n")
|
||||
|
||||
width -= self.delta
|
||||
i += 1
|
||||
|
||||
|
||||
STR_TO_SIM = {
|
||||
"flow": Flow,
|
||||
"search": Search,
|
||||
"shrink": Shrink
|
||||
}
|
||||
STR_TO_SIM = {"flow": Flow, "search": Search, "shrink": Shrink}
|
||||
|
||||
170
squish/squish.py
170
squish/squish.py
@ -13,11 +13,12 @@ dia_presets = {
|
||||
"energy": [["voronoi", "energy"]],
|
||||
"stats": [
|
||||
["voronoi", "eigs", "site_edge_count"],
|
||||
["site_isos", "site_energies", "edge_lengths"]
|
||||
["site_isos", "site_energies", "edge_lengths"],
|
||||
],
|
||||
"eigs": [["voronoi", "eigs"]]
|
||||
"eigs": [["voronoi", "eigs"]],
|
||||
}
|
||||
|
||||
|
||||
def check_params(container: Dict, needed: List[str], valid: Dict) -> None:
|
||||
"""Checks container for the necessary items, and raises
|
||||
an error if the parameter is not found.
|
||||
@ -25,41 +26,65 @@ def check_params(container: Dict, needed: List[str], valid: Dict) -> None:
|
||||
Args:
|
||||
container (Dict): contains the submitted parameters.
|
||||
needed (List[str]): contains the needed paramters.
|
||||
valid: (Dict): if there are specific valid parameters, it will also check for these.
|
||||
valid: (Dict): if there are specific valid parameters, it will also check these.
|
||||
|
||||
"""
|
||||
|
||||
for need in needed:
|
||||
if need not in container:
|
||||
raise ValueError(f"Parameter \'{need}\' is required.")
|
||||
raise ValueError(f"Parameter '{need}' is required.")
|
||||
|
||||
if need in valid:
|
||||
if type(valid[need]) is list:
|
||||
if container[need] not in valid[need]:
|
||||
raise ValueError(f"Parameter \'{need}\' must be one of these values: " + \
|
||||
f"{str(valid[need])[1:-1]}.")
|
||||
raise ValueError(
|
||||
f"Parameter '{need}' must be one of these values: "
|
||||
+ f"{str(valid[need])[1:-1]}."
|
||||
)
|
||||
elif valid[need] == "positive":
|
||||
if container[need] < 0:
|
||||
raise ValueError(f"Parameter \'{need}\' must be positive.")
|
||||
raise ValueError(f"Parameter '{need}' must be positive.")
|
||||
|
||||
|
||||
def main():
|
||||
# Loading configuration and settings.
|
||||
parser = argparse.ArgumentParser("Squish")
|
||||
parser.add_argument('sim_conf', metavar='/path/to/config.json',
|
||||
help="configuration file for a simulation")
|
||||
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', default=False,
|
||||
help="suppress all normal output")
|
||||
parser.add_argument('-l', '--log', dest='log_steps', default=50, type=int,
|
||||
help="number of iterations before logging")
|
||||
parser.add_argument('-i', dest='input_file', metavar='/path/to/sim',
|
||||
help="folder that contains outputted simulation files.", default=None)
|
||||
parser.add_argument(
|
||||
"sim_conf",
|
||||
metavar="/path/to/config.json",
|
||||
help="configuration file for a simulation",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q",
|
||||
"--quiet",
|
||||
dest="quiet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="suppress all normal output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--log",
|
||||
dest="log_steps",
|
||||
default=50,
|
||||
type=int,
|
||||
help="number of iterations before logging",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
dest="input_file",
|
||||
metavar="/path/to/sim",
|
||||
help="folder that contains outputted simulation files.",
|
||||
default=None,
|
||||
)
|
||||
|
||||
parser.add_argument('--n_objects', dest='n', type=int, help="objects in domain")
|
||||
parser.add_argument('--width', dest='w', type=float, help="width of domain")
|
||||
parser.add_argument('--height', dest='h', type=float, help="height of domain")
|
||||
parser.add_argument('--natural_radius', dest='r', type=float, help="natural radius of object")
|
||||
parser.add_argument('--energy', dest='energy', help="energy type of system")
|
||||
parser.add_argument("--n_objects", dest="n", type=int, help="objects in domain")
|
||||
parser.add_argument("--width", dest="w", type=float, help="width of domain")
|
||||
parser.add_argument("--height", dest="h", type=float, help="height of domain")
|
||||
parser.add_argument(
|
||||
"--natural_radius", dest="r", type=float, help="natural radius of object"
|
||||
)
|
||||
parser.add_argument("--energy", dest="energy", help="energy type of system")
|
||||
|
||||
args = parser.parse_args()
|
||||
do_sim(args, args.input_file)
|
||||
@ -69,38 +94,63 @@ def do_sim(args, file):
|
||||
with open(args.sim_conf) as f:
|
||||
params = json.load(f)
|
||||
|
||||
|
||||
check_params(params, ["domain", "simulation"], {})
|
||||
dmn_params, sim_params = params["domain"], params["simulation"]
|
||||
|
||||
overrides = {args.n: "n_objects", args.w: "width", args.h: "height", args.r: "natural_radius",
|
||||
args.energy: "energy"}
|
||||
overrides = {
|
||||
args.n: "n_objects",
|
||||
args.w: "width",
|
||||
args.h: "height",
|
||||
args.r: "natural_radius",
|
||||
args.energy: "energy",
|
||||
}
|
||||
for arg, arg_name in overrides.items():
|
||||
if arg is not None:
|
||||
dmn_params[arg_name] = arg
|
||||
|
||||
check_params(dmn_params, ["n_objects", "width", "height", "natural_radius", "energy"], {
|
||||
"n_objects": "positive", "width": "positive", "height": "positive",
|
||||
"natural_radius": "positive", "energy": ["area", "radial-al", "radial-t"]
|
||||
})
|
||||
domain = DomainParams(dmn_params["n_objects"], dmn_params["width"], \
|
||||
dmn_params["height"], dmn_params["natural_radius"])
|
||||
check_params(
|
||||
dmn_params,
|
||||
["n_objects", "width", "height", "natural_radius", "energy"],
|
||||
{
|
||||
"n_objects": "positive",
|
||||
"width": "positive",
|
||||
"height": "positive",
|
||||
"natural_radius": "positive",
|
||||
"energy": ["area", "radial-al", "radial-t"],
|
||||
},
|
||||
)
|
||||
domain = DomainParams(
|
||||
dmn_params["n_objects"],
|
||||
dmn_params["width"],
|
||||
dmn_params["height"],
|
||||
dmn_params["natural_radius"],
|
||||
)
|
||||
energy = Energy(dmn_params["energy"])
|
||||
|
||||
points = None
|
||||
if "points" in dmn_params:
|
||||
if type(dmn_params["points"]) is str:
|
||||
with open(Path(dmn_params["points"]), 'rb') as f:
|
||||
with open(Path(dmn_params["points"]), "rb") as f:
|
||||
points = np.load(f)
|
||||
else:
|
||||
points = np.asarray(dmn_params["points"])
|
||||
|
||||
check_params(sim_params, ["mode", "step_size", "threshold", "save_sim", "adaptive"], {
|
||||
"mode": ["flow", "search", "shrink"], "step_size": "positive", "threshold": "positive",
|
||||
})
|
||||
mode, step, thres, adaptive, save_sim = sim_params["mode"], sim_params["step_size"], \
|
||||
sim_params["threshold"], sim_params["adaptive"], \
|
||||
sim_params["save_sim"]
|
||||
check_params(
|
||||
sim_params,
|
||||
["mode", "step_size", "threshold", "save_sim", "adaptive"],
|
||||
{
|
||||
"mode": ["flow", "search", "shrink"],
|
||||
"step_size": "positive",
|
||||
"threshold": "positive",
|
||||
},
|
||||
)
|
||||
mode, step, thres, adaptive, save_sim = (
|
||||
sim_params["mode"],
|
||||
sim_params["step_size"],
|
||||
sim_params["threshold"],
|
||||
sim_params["adaptive"],
|
||||
sim_params["save_sim"],
|
||||
)
|
||||
|
||||
name = sim_params.get("name")
|
||||
|
||||
@ -108,17 +158,37 @@ def do_sim(args, file):
|
||||
if mode == "flow":
|
||||
sim = Flow(domain, energy, step, thres, adaptive, name=name)
|
||||
elif mode == "search":
|
||||
check_params(sim_params, ["manifold_step_size", "eq_stop_count"], {
|
||||
"manifold_step_size": "positive", "eq_stop_count": "positive"
|
||||
})
|
||||
sim = Search(domain, energy, step, thres, adaptive, sim_params["manifold_step_size"],
|
||||
sim_params["eq_stop_count"], name=name)
|
||||
check_params(
|
||||
sim_params,
|
||||
["manifold_step_size", "eq_stop_count"],
|
||||
{"manifold_step_size": "positive", "eq_stop_count": "positive"},
|
||||
)
|
||||
sim = Search(
|
||||
domain,
|
||||
energy,
|
||||
step,
|
||||
thres,
|
||||
adaptive,
|
||||
sim_params["manifold_step_size"],
|
||||
sim_params["eq_stop_count"],
|
||||
name=name,
|
||||
)
|
||||
elif mode == "shrink":
|
||||
check_params(sim_params, ["width_change", "width_stop"], {
|
||||
"width_change": "positive", "width_stop": "positive"
|
||||
})
|
||||
sim = Shrink(domain, energy, step, thres, adaptive, sim_params["width_change"],
|
||||
sim_params["width_stop"], name=name)
|
||||
check_params(
|
||||
sim_params,
|
||||
["width_change", "width_stop"],
|
||||
{"width_change": "positive", "width_stop": "positive"},
|
||||
)
|
||||
sim = Shrink(
|
||||
domain,
|
||||
energy,
|
||||
step,
|
||||
thres,
|
||||
adaptive,
|
||||
sim_params["width_change"],
|
||||
sim_params["width_stop"],
|
||||
name=name,
|
||||
)
|
||||
else:
|
||||
sim = Simulation.from_file(file)
|
||||
|
||||
@ -126,12 +196,12 @@ def do_sim(args, file):
|
||||
if "diagram" in params:
|
||||
save_diagram = True
|
||||
dia_params = params["diagram"]
|
||||
check_params(dia_params, ["filetype", "figures"], {
|
||||
"filetype": ["img", "mp4"]
|
||||
})
|
||||
check_params(dia_params, ["filetype", "figures"], {"filetype": ["img", "mp4"]})
|
||||
if dia_params["filetype"] == "mp4":
|
||||
if which("ffmpeg") is None:
|
||||
raise ValueError("The program 'ffmpeg' needs to be installed on your system.")
|
||||
raise ValueError(
|
||||
"The program 'ffmpeg' needs to be installed on your system."
|
||||
)
|
||||
|
||||
if type(dia_params["figures"]) is str:
|
||||
dia_params["figures"] = np.asarray(dia_presets[dia_params["figures"]])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user