make kadic.py have parity with showKappaAdic.ojs

This commit is contained in:
queue-miscreant 2025-03-05 02:44:57 -06:00
parent f5ce842863
commit ca20316aba

View File

@ -1,10 +1,12 @@
import argparse
import csv
from itertools import accumulate, chain, repeat
from pathlib import Path
import random
from typing import Any, Generator, TypeVar
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from matplotlib.widgets import Slider, RadioButtons
from matplotlib.animation import FuncAnimation
import numpy as np
from numpy.typing import NDArray
@ -49,8 +51,11 @@ class Expansions:
def from_file(cls, base: float, expansions_file: Path, name: str | None = None):
ret = []
with open(expansions_file) as f:
for line in f.readlines():
ret.append(eval(line))
for row in csv.reader(f.readlines()):
row_even = [int(i) for i in row]
row_odd = row_even.copy()
row_odd[0] = 1
ret.extend([row_even, row_odd])
# Extract name of base from filename
if name is None:
@ -72,6 +77,22 @@ class Expansions:
[to_base(base, i) for i in range(count)],
)
@classmethod
def from_random(cls, base: int, count: int, digits_count: int):
return cls(
float(base),
f"{base}_(random)",
[[random.randint(0, 1) for _ in range(digits_count)] for _ in range(count)]
)
cendree_divmod = Expansions.from_file(
1 + 3**0.5, Path("./cendree_DivMod_count_1024_256_digits.csv"), "divMod_cendree"
)
cendree_quotrem = Expansions.from_file(
1 + 3**0.5, Path("./cendree_QuotRem_count_1024_256_digits.csv"), "quotRem_cendree"
)
def slices(xs):
for i in range(1, len(xs) + 1):
@ -105,45 +126,143 @@ def geosum(c, phis: list[complex]) -> complex:
)
def interactive(expansions: Expansions):
phiss = [
[_adic_angles(expansions.base, expansion, 200) for expansion in expansions.xs]
]
embedding: NDArray[np.complex64] = np.array(
[geosum(0.9, phis) for phis in phiss[0]]
)
class PadicPlot:
EXPANSIONS_LENGTH = 1024
# Create the figure and the line that we will manipulate
fig, ax = plt.subplots()
def __init__(self, expansions: Expansions, axes=None, plot=False, interactive=False, **kwargs):
self._expansions: Expansions = expansions
self._angles: NDArray[np.complex64]
self._embedding: NDArray[np.complex64]
line = ax.plot(embedding.real, embedding.imag, linestyle="None", marker=".")[0]
self._list_base: int = 2 if isinstance(expansions.base, float) else expansions.base
self._embedding_base: float = 2 if isinstance(expansions.base, float) else expansions.base
self._geometric: float = 0.9
self._update(set_data=False)
# adjust the main plot to make room for the sliders
fig.subplots_adjust(bottom=0.25)
self._axes = {}
self._inputs = {}
self._plot = None
# Make a horizontal slider to control the frequency.
axfreq = fig.add_axes([0.1, 0.1, 0.75, 0.03]) # type: ignore
freq_slider = Slider(
ax=axfreq,
label="$c$",
valmin=0.001,
valmax=0.999,
valinit=0.9,
)
axes = axes if axes is not None else plt.gca()
if interactive:
self.plot(axes=axes, **kwargs)
self._interactive(axes=axes)
elif plot:
self.plot(axes=axes, **kwargs)
# The function to be called anytime a slider's value changes
def update(val):
embedding: NDArray[np.complex64] = np.array(
[geosum(val, phis) for phis in phiss[0]]
def plot(self, axes, linestyle="None", marker=".", **kwargs):
self._axes["plot"] = axes
self._plot = axes.plot(
self._embedding.real,
self._embedding.imag,
linestyle=linestyle,
marker=marker,
**kwargs,
)[0]
plt.tight_layout()
def _interactive(self, axes):
# adjust the main plot to make room for the sliders
fig = axes.get_figure()
fig.subplots_adjust(bottom=0.25)
self._axes["adic"] = fig.add_axes([0.65, 0.025, 0.3, 0.175]) # type: ignore
self._inputs["adic"] = RadioButtons(
labels=["b-adics", "$\\kappa$-adic (balanced)", "$\\kappa$-adic (binary)", "Random Binary"],
ax=self._axes["adic"],
)
line.set_data(embedding.real, embedding.imag) # type: ignore
ax.set_xlim(min(embedding.real), max(embedding.real))
ax.set_ylim(min(embedding.imag), max(embedding.imag))
fig.canvas.draw_idle()
self._inputs["adic"].on_clicked(self._update_adic)
# register the update function with each slider
freq_slider.on_changed(update)
self._axes["list_base"] = fig.add_axes([0.1, 0.15, 0.45, 0.03]) # type: ignore
self._inputs["list_base"] = Slider(
ax=self._axes["list_base"],
label="$b$",
valmin=2,
valmax=10,
valstep=1,
valinit=2,
)
self._inputs["list_base"].on_changed(self._update_list_base)
self._axes["embedding_base"] = fig.add_axes([0.1, 0.1, 0.45, 0.03]) # type: ignore
self._inputs["embedding_base"] = Slider(
ax=self._axes["embedding_base"],
label="$p$",
valmin=1.1,
valmax=10,
valstep=0.1,
valinit=2,
)
self._inputs["embedding_base"].on_changed(self._update_embedding_base)
self._axes["geometric"] = fig.add_axes([0.1, 0.05, 0.45, 0.03]) # type: ignore
self._inputs["geometric"] = Slider(
ax=self._axes["geometric"],
label="$c$",
valmin=0.001,
valmax=0.999,
valinit=0.9,
)
self._inputs["geometric"].on_changed(self._update_geometric)
def _update_adic(self, val):
if val == "b-adics":
expansions = Expansions.from_integer(int(self._list_base), self.EXPANSIONS_LENGTH)
elif val == "$\\kappa$-adic (balanced)":
expansions = cendree_quotrem
elif val == "$\\kappa$-adic (binary)":
expansions = cendree_divmod
elif val == "Random Binary":
expansions = Expansions.from_random(2, self.EXPANSIONS_LENGTH, self.EXPANSIONS_LENGTH // 10)
else:
raise ValueError("Impossible")
self._expansions = expansions
self._update()
def _update_list_base(self, val):
self._list_base = val
if val == "b-adics":
self._expansions = Expansions.from_integer(int(val), self.EXPANSIONS_LENGTH)
self._update()
def _update_embedding_base(self, val):
self._embedding_base = val
self._update()
def _update_geometric(self, val):
self._geometric = val
self._update(False)
def _update(self, regen_angles: bool = True, set_data: bool = True):
if regen_angles:
self._angles = np.array([
_adic_angles(self._embedding_base, expansion, 15) for expansion in self._expansions.xs
])
# TODO: vectorize
self._embedding: NDArray[np.complex64] = np.array(
[geosum(self._geometric, phis) for phis in self._angles]
)
if set_data:
self._set_data()
def _set_data(self):
self._plot.set_data(self._embedding.real, self._embedding.imag) # type: ignore
self._axes["plot"].set_xlim(
min(self._embedding.real),
max(self._embedding.real)
)
self._axes["plot"].set_ylim(
min(self._embedding.imag),
max(self._embedding.imag)
)
self._axes["plot"].get_figure().canvas.draw_idle()
def interactive(expansions: Expansions):
PadicPlot(expansions=expansions, interactive=True)
plt.show()
@ -179,13 +298,6 @@ def anim(expansions: Expansions):
)
cendree_divmod = Expansions.from_file(
1 + 3**0.5, Path("./cendree_DivMod_count_1024_256_digits.txt"), "divMod_cendree"
)
cendree_quotrem = Expansions.from_file(
1 + 3**0.5, Path("./cendree_QuotRem_count_1024_256_digits.txt"), "quotRem_cendree"
)
def main():
parser = argparse.ArgumentParser()
@ -195,7 +307,13 @@ def main():
action="store_true",
help="Interactively set the geometric constant of the Fourier series.",
)
parser.add_argument("--base", "-p", type=int, help="Use p-adic integers")
parser.add_argument(
"--base",
"-p",
default=2,
type=int,
help="Use p-adic integers"
)
parser.add_argument(
"--count",
"-c",