start pythonifying data

This commit is contained in:
queue-miscreant 2025-07-06 00:11:15 -05:00
parent 55cb9ddd3d
commit 2c1775e8f2
6 changed files with 331 additions and 156 deletions

View File

@ -0,0 +1,135 @@
from dataclasses import dataclass
import sympy
from sympy.abc import x
def display_integral_root(
root: int, multiplicity: int, pad_to: int | None = None
) -> str:
return (
"("
# denote sign
+ ("\\pm" if root != 0 else "")
+ str(root)
+ ")^{"
+ str(multiplicity)
# pad multiplicity
+ ("\\phantom{" + ("0" * pad_to) + "}" if pad_to is not None else "")
+ "}"
)
@dataclass
class SymmetricSpectrum:
"""
Dataclass for symmetric spectra -- those for which, if a spectrum contains a root,
it also contains its negative.
If a root polynomial is not symmetric, then its symmetric counterpart will *not* be specified.
"""
# Either the integral root or a polynomial with the root, and the multiplicity
roots: list[tuple[sympy.Expr | int, int]]
not_shown_count: int = 0
def to_markdown(self) -> str:
"""
Display a spectrum as a markdown string
"""
integer_roots = [
(root, multiplicity)
for root, multiplicity in self.roots
if isinstance(root, int)
]
# symmetrify polynomials
polynomial_roots = [
(root, multiplicity)
for base_root, multiplicity in self.roots
if isinstance(base_root, sympy.Expr)
for root in set(
[base_root, (-1) ** (sympy.degree(base_root)) * base_root.subs(x, -x)]
)
]
shown_roots = len(polynomial_roots) > 0 or len(integer_roots) > 0
not_shown_string = (
(
f"Not shown: {self.not_shown_count} other roots"
if shown_roots
else f"Not shown: all {self.not_shown_count} roots"
)
if self.not_shown_count > 0
else ""
)
# strictly integral spectra
if len(polynomial_roots) == 0 and len(integer_roots) != 0:
longest_integer = max(
len(str(multiplicity)) for _, multiplicity in integer_roots
)
return (
(
"$$\\begin{gather*}"
+ " \\\\ ".join(
display_integral_root(*rm, pad_to=longest_integer)
for rm in integer_roots
)
+ "\\end{gather*}$$"
)
if shown_roots
else ""
) + not_shown_string
# conditionally split integral roots onto multiple lines
integral_roots_lines = (
" ".join(display_integral_root(*rm) for rm in integer_roots)
if len(integer_roots) < 5
else (
" ".join(
display_integral_root(*rm)
for rm in integer_roots[: len(integer_roots) // 2]
)
+ "\\\\"
+ " ".join(
display_integral_root(*rm)
for rm in integer_roots[len(integer_roots) // 2 :]
)
)
)
return ((
"$$\\begin{gather*}"
+ integral_roots_lines
+ " \\\\ "
+ " \\\\ ".join(
sympy.latex(root**multiplicity)
for root, multiplicity in polynomial_roots
)
+ "\\end{gather*}$$"
)
if shown_roots
else ""
) + not_shown_string
def count_roots_spectrum(spectrum: SymmetricSpectrum) -> int:
return (
sum(
[
(
(multiplicity * 2 if root != 0 else multiplicity)
if isinstance(root, int)
else (int(sympy.degree(root, x)) * multiplicity)
)
for root, multiplicity in spectrum.roots
]
)
+ spectrum.not_shown_count
)
@dataclass
class GraphData:
vertex_count: int
edge_count: int
distance_classes: list[int]
spectrum: SymmetricSpectrum

View File

@ -0,0 +1,50 @@
from .base import SymmetricSpectrum
# TODO: other non-spectral data
complete_spectra = {
3: SymmetricSpectrum([(0, 4), (3, 1)]),
4: SymmetricSpectrum([(0, 4), (2, 9), (6, 1)]),
5: SymmetricSpectrum(
[
(0, 36),
(2, 25),
(5, 16),
(10, 1),
]
),
6: SymmetricSpectrum(
[
(0, 256),
(3, 125),
(5, 81),
(9, 25),
(15, 1),
]
),
7: SymmetricSpectrum(
[
(0, 400),
(1, 441),
(3, 1225),
(6, 196),
(7, 225),
(9, 196),
(14, 36),
(21, 1),
]
),
# TODO: missing a root (and its negative) of multiplicity 400
8: SymmetricSpectrum(
[
(0, 9864),
(2, 3136),
(4, 6125),
(7, 4096),
(8, 196),
(10, 784),
(12, 441),
(20, 49),
(28, 1),
]
),
}

View File

@ -0,0 +1,50 @@
from .base import SymmetricSpectrum, x
path_spectra = {
3: SymmetricSpectrum(
[(1, 2), (2, 1)]
),
4: SymmetricSpectrum(
[
(1, 3),
(3, 1),
(x**2 - 3, 2),
(x**2 - 2 * x - 1, 3),
(x**2 + 2 * x - 1, 3),
]
),
5: SymmetricSpectrum(
[
(0, 12),
(1, 6),
(4, 1),
(x**2 - 5, 6),
(x**2 + 5 * x + 5, 4),
(x**2 + 3 * x + 1, 4),
(x**2 + 2 * x - 1, 5),
(x**3 + 2 * x**2 - 5 * x - 4, 5),
]
),
6: SymmetricSpectrum(
[
(0, 20),
(1, 25),
(2, 15),
(3, 5),
(4, 5),
(5, 1),
(x**2 - 3, 20),
],
not_shown_count=558,
),
7: SymmetricSpectrum(
[
(0, 35),
(1, 20),
(2, 45),
(6, 1),
],
not_shown_count=4873,
),
8: SymmetricSpectrum([], not_shown_count=40320),
}

View File

@ -0,0 +1,55 @@
from .base import SymmetricSpectrum
star_spectra = {
3: SymmetricSpectrum([(1, 2), (2, 1)]),
4: SymmetricSpectrum(
[
(0, 4),
(1, 3),
(2, 6),
(3, 1),
]
),
5: SymmetricSpectrum(
[
(0, 30),
(1, 4),
(2, 28),
(3, 12),
(4, 1),
]
),
6: SymmetricSpectrum(
[
(0, 168),
(1, 30),
(2, 120),
(3, 105),
(4, 20),
(5, 1),
]
),
7: SymmetricSpectrum(
[
(0, 840),
(1, 468),
(2, 495),
(3, 830),
(4, 276),
(5, 30),
(6, 1),
]
),
8: SymmetricSpectrum(
[
(0, 3960),
(1, 5691),
(2, 2198),
(3, 6321),
(4, 3332),
(5, 595),
(6, 42),
(7, 1),
]
),
}

View File

@ -19,6 +19,8 @@ categories:
from IPython.display import Markdown
from tabulate import tabulate
from graph_data import complete, path, star
```
@ -234,51 +236,44 @@ In the case of these graphs, they are a partition of the factorial numbers.
```{python}
#| echo: false
#| classes: plain
mahonian = [
[1, 2, 2, 1],
[1, 3, 5, 6, 5, 3, 1],
[1, 4, 9, 15, 20, 22, 20, 15, 9, 4, 1],
[1, 5, 14, 29, 49, 71, 90, 101, 101, 90, 71, 49, 29, 14, 5, 1],
[1, 6, 20, 49, 98, 169, 259, 359, 455, 531, 573, 573, 531, 455, 359, 259, 169, 98, 49, 20, 6, 1],
]
whitney_second_kind = [
[1, 2, 2, 1],
[1, 3, 6, 9, 5],
[1, 4, 12, 30, 44, 26, 3],
[1, 5, 20, 70, 170, 250, 169, 35],
[1, 6, 30, 135, 460, 1110, 1689, 1254, 340, 15],
]
stirling_second_kind = [
[1, 3, 2],
[1, 6, 11, 6],
[1, 10, 35, 50, 24],
[1, 15, 85, 225, 274, 120],
[1, 21, 175, 735, 1624, 1764, 720],
]
half_break = lambda x: str(x[:len(x) // 2])[:-1] + "<br>" + str(x[len(x) // 2:])[1:]
Markdown(tabulate(
[
[
3,
"[1, 2, 2, 1]",
"[1, 2, 2, 1]",
"[1, 3, 2]"
],
[
4,
"[1, 3, 5, 6, 5, 3, 1]",
"[1, 2, 2, 1]",
"[1, 6, 11, 6]"
],
[
5,
"[1, 4, 9, 15, 20, 22, 20, 15, 9, 4, 1]",
"[1, 4, 12, 30, 44, 26, 3]",
"[1, 10, 35, 50, 24]"
],
[
6,
"[1, 5, 14, 29, 49, 71, 90, 101,<br>101, 90, 71, 49, 29, 14, 5, 1]",
"[1, 5, 20, 70, 170, 250, 169, 35]",
"[1, 15, 85, 225, 274, 120]"
],
[
7,
"[1, 6, 20, 49, 98, 169, 259, 359, 455, 531, 573,<br>573, 531, 455, 359, 259, 169, 98, 49, 20, 6, 1]",
"[1, 6, 30, 135, 460, 1110, 1689, 1254, 340, 15]",
"[1, 21, 175, 735, 1624, 1764, 720]"
],
[
"Rule",
"Mahonian numbers [OEIS A008302](http://oeis.org/A008302)",
"Whitney numbers of the second kind (star poset) [OEIS A007799](http://oeis.org/A007799)",
"Stirling numbers of the first kind [OEIS A132393](http://oeis.org/A132393)"
],
[i, j if i <= 5 else half_break(j), k, l]
for i, j, k, l in zip(range(3,8), mahonian, whitney_second_kind, stirling_second_kind)
],
headers=[
"*n*",
r"$dists(\exp( P_n ))$",
r"$dists(\exp( \bigstar_n ))$",
r"$dists(\exp( K_n ))$"
r"$\text{dists}(\exp( P_n ))$",
r"$\text{dists}(\exp( \bigstar_n ))$",
r"$\text{dists}(\exp( K_n ))$"
]
))
```
@ -297,125 +292,15 @@ Unfortunately, eigenvalues are not necessarily integers, and therefore not easil
```{python}
#| echo: false
# TODO: less raw latex, more data converted to latex
f = lambda x: x.replace("\n", "")
#| classes: plain
Markdown(tabulate(
[
[3, r"$(\pm 1)^2 (\pm 2)$", r"$(\pm 1)^2 (\pm 2)$", r"$(0)^4 (\pm 3)$"],
[4, f(r"""$$
\begin{gather*}
(\pm 1)^3 (\pm 3) \\
(x^2 -\ 3)^2 \\
(x^2 -\ 2x -\ 1)^3
(x^2 + 2x -\ 1)^3
\end{gather*}$$"""), f(r"""$$
\begin{gather*}
(0)^4 \\
(\pm 1)^3 \\
(\pm 2)^6 \\
(\pm 3)^{\phantom{0}}
\end{gather*}$$"""), f(r"""$$
\begin{gather*}
(0)^4 \\
(\pm 2)^9 \\
(\pm 6)^{\phantom{0}}
\end{gather*}$$""")],
[5, f(r"""$$
\begin{gather*}
(0)^{12} (\pm 1)^6 (\pm 4) \\
(x^2 -\ 5)^6 \\
(x^2 -\ 5x + 5)^4
(x^2 + 5x + 5)^4 \\
(x^2 -\ 3x + 1)^4
(x^2 + 3x + 1)^4 \\
(x^2 -\ 2x -\ 1)^5
(x^2 + 2x -\ 1)^5 \\
(x^3 -\ 2x^2 -\ 5x + 4)^5 \\
(x^3 + 2x^2 -\ 5x -\ 4)^5
\end{gather*}$$"""), f(r"""$$
\begin{gather*}
(0)^{30} \\
(\pm 1)^{4\phantom{0}} \\
(\pm 2)^{28} \\
(\pm 3)^{12} \\
(\pm 4)^{\phantom{00}}
\end{gather*}$$"""), f(r"""$$
\begin{gather*}
(0)^{36} \\
(\pm 2)^{25} \\
(\pm 5)^{16} \\
(\pm 10)^{\phantom{00}}
\end{gather*}$$""")],
[6, f(r"""$$\begin{gather*}
(0)^{20} (\pm 1)^{25} (\pm 2)^{15} \\
(\pm 3)^5 (\pm 4)^5 (\pm 5) \\
(x^2 -\ 3)^{20} \\
\end{gather*}
$$ <br> *Not shown: 558 other roots*"""), f(r"""$$
\begin{gather*}
(0)^{168} \\
(\pm 1)^{30\phantom{0}} \\
(\pm 2)^{120} \\
(\pm 3)^{105} \\
(\pm 4)^{20\phantom{0}} \\
(\pm 5)^{\phantom{000}}
\end{gather*}$$"""), f(r"""$$
\begin{gather*}
(0)^{256} \\
(\pm 3)^{125} \\
(\pm 5)^{81\phantom{0}} \\
(\pm 9)^{25\phantom{0}} \\
(\pm 15)^{\phantom{000}}
\end{gather*}$$""")],
[7, f(r"""
$(0)^{35} (\pm 1)^{20} (\pm 2)^{45} (6)$ <br>
*Not shown: 4873 other roots*
"""), f(r"""$$
\begin{gather*}
(0)^{840} \\
(\pm 1)^{468} \\
(\pm 2)^{495} \\
(\pm 3)^{830} \\
(\pm 4)^{276} \\
(\pm 5)^{30\phantom{0}} \\
(\pm 6)^{\phantom{000}}
\end{gather*}$$"""), f(r"""$$
\begin{gather*}
(0)^{400\phantom{0}} \\
(\pm 1)^{441\phantom{0}} \\
(\pm 3)^{1225} \\
(\pm 6)^{196\phantom{0}} \\
(\pm 7)^{225\phantom{0}} \\
(\pm 9)^{196\phantom{0}} \\
(\pm 14)^{36\phantom{00}} \\
(\pm 21)^{\phantom{0000}}
\end{gather*}$$""")],
[8, f(r"""*Not shown: all 40320 roots*"""), f(r"""$$
\begin{gather*}
(0)^{3960} \\
(\pm 1)^{5691} \\
(\pm 2)^{2198} \\
(\pm 3)^{6321} \\
(\pm 4)^{3332} \\
(\pm 5)^{595\phantom{0}} \\
(\pm 6)^{42\phantom{00}} \\
(\pm 7)^{\phantom{0000}}
\end{gather*}$$"""), f(r"""$$
\begin{gather*}
(0)^{9864} \\
(\pm 2)^{3136} \\
(\pm 4)^{6125} \\
(\pm 7)^{4096} \\
(\pm 8)^{196\phantom{0}} \\
(\pm 10)^{784\phantom{0}} \\
(\pm 12)^{441\phantom{0}} \\
(\pm 20)^{49\phantom{00}} \\
(\pm 28)^{\phantom{0000}}
\end{gather*}$$""")
],
[i, pathG.to_markdown(), starG.to_markdown(), completeG.to_markdown()]
for i in range(3, 9)
for pathG, starG, completeG in (
[path.path_spectra[i], star.star_spectra[i], complete.complete_spectra[i]],
)
],
headers=[
"*n*",