Add sage code
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
# math-research
|
||||||
|
|
||||||
|
Personal mathematics research repository by Eric Bauerfeld. Papers are written in AMS-LaTeX using the `amsart` document class.
|
||||||
|
|
||||||
|
## Papers
|
||||||
|
|
||||||
|
### `kempe_style_search_for_smaller_contradiction`
|
||||||
|
**Humans Suffice: A Novel Proof of the Four Color Theorem**
|
||||||
|
|
||||||
|
An in-progress proof of the Four Color Theorem via a minimal counterexample argument. The paper builds on Kempe's 1879 strategy — establishing valid cases for vertices of degree ≤ 4, then extending the argument to the degree-5 case using properties of non-adjacent degree-5 vertices, merged subgraphs, and locked colorings.
|
||||||
|
|
||||||
|
### `plane_depth_labelling`
|
||||||
|
**Plane Depth Labelling**
|
||||||
|
|
||||||
|
Early-stage paper. Title and author information set; content in progress.
|
||||||
|
|
||||||
|
## Creating a New Paper
|
||||||
|
|
||||||
|
Use `run.sh` to scaffold a new paper from the AMS-LaTeX template:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./run.sh init_paper "Your Paper Title"
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a new directory (name derived from the title) containing a `paper.tex` pre-filled with the title and author.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
The Python library code in `lib/` requires SageMath. To set up the linting environment, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./run.sh setup
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a `.venv` using the SageMath Python interpreter and installs `pylint` into it.
|
||||||
|
|
||||||
|
## Linting
|
||||||
|
|
||||||
|
To lint the `lib/` directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./run.sh lint
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs `pyright` (via `npx`) and `pylint` using the SageMath Python interpreter.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Papers are compiled with LaTeX. From within a paper directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
latexmk -pdf paper.tex
|
||||||
|
```
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
"""Utilities for converting integer colorings to named colors."""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
COLORS = ['blue', 'red', 'green', 'yellow', 'purple']
|
||||||
|
|
||||||
|
def convert_coloring(
|
||||||
|
coloring: dict[Any, int],
|
||||||
|
vertices: list[Any] | None = None
|
||||||
|
) -> dict[str, list[Any]]:
|
||||||
|
"""Convert an integer coloring dict to a dict mapping color names to vertex lists."""
|
||||||
|
colors: dict[str, list[Any]] = {}
|
||||||
|
for k, v in coloring.items():
|
||||||
|
if vertices and k not in vertices:
|
||||||
|
continue
|
||||||
|
color = COLORS[v]
|
||||||
|
if color not in colors:
|
||||||
|
colors[color] = []
|
||||||
|
colors[color].append(k)
|
||||||
|
return colors
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
"""Utilities for working with planar dual graphs."""
|
||||||
|
from typing import Any
|
||||||
|
from sage.all import Graph
|
||||||
|
|
||||||
|
|
||||||
|
def find_vertex_for_dual_face(dual_face: Any) -> Any | None:
|
||||||
|
"""Return the primal vertex shared by all faces in the dual face, or None."""
|
||||||
|
shared_vertices = None
|
||||||
|
for dual_edge in dual_face:
|
||||||
|
vertices = set(map(lambda e: e[0], dual_edge[0]))
|
||||||
|
if shared_vertices:
|
||||||
|
shared_vertices.intersection_update(vertices)
|
||||||
|
else:
|
||||||
|
shared_vertices = vertices
|
||||||
|
if len(shared_vertices) == 1:
|
||||||
|
return shared_vertices.pop()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _dual_edge_has_vertex(dual_edge: Any, vertex: Any) -> bool:
|
||||||
|
return any(vertex in edge for dual_vertex in dual_edge for edge in dual_vertex)
|
||||||
|
|
||||||
|
|
||||||
|
def find_dual_face_for_removed_vertex(planar_dual: Graph, vertex: Any) -> Any | None:
|
||||||
|
"""Return the dual face of length 5 whose every dual edge contains the given vertex."""
|
||||||
|
for dual_face in list[Any](planar_dual.faces()): # type: ignore[call-arg]
|
||||||
|
if len(dual_face) == 5 and all(_dual_edge_has_vertex(de, vertex) for de in dual_face):
|
||||||
|
return dual_face
|
||||||
|
return None
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
"""Utilities for finding primal edges corresponding to dual edges."""
|
||||||
|
from typing import Any, cast
|
||||||
|
|
||||||
|
def get_edge_for_dual_edge(dual_edge: Any) -> tuple[int, int, None]:
|
||||||
|
"""Return the primal edge shared by both faces of the given dual edge."""
|
||||||
|
edges: list[set[Any]] = []
|
||||||
|
for e in (dual_edge[0] + dual_edge[1]):
|
||||||
|
edge = set(e)
|
||||||
|
if edge in edges:
|
||||||
|
return cast(tuple[int, int, None], e)
|
||||||
|
edges.append(edge)
|
||||||
|
raise ValueError(f"Error finding edge for {dual_edge}")
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
"""Utilities for constructing edge graphs from planar graphs."""
|
||||||
|
from typing import Any, cast
|
||||||
|
from sage.all import Graph
|
||||||
|
|
||||||
|
|
||||||
|
def get_edge_graph_pos(edge_graph: Graph, pos_src: dict[Any, Any]) -> dict[Any, tuple[Any, Any]]:
|
||||||
|
"""Return a position dict for edge graph vertices, using midpoints of the source positions."""
|
||||||
|
pos: dict[Any, tuple[Any, Any]] = {}
|
||||||
|
for e in cast(list[Any], edge_graph.vertices()): # type: ignore
|
||||||
|
pos[e] = (
|
||||||
|
(pos_src[e[0]][0] + pos_src[e[1]][0]) / 2,
|
||||||
|
(pos_src[e[0]][1] + pos_src[e[1]][1]) / 2
|
||||||
|
)
|
||||||
|
return pos
|
||||||
|
|
||||||
|
|
||||||
|
def get_edge_graph(graph: Graph, set_pos: bool = True) -> Graph:
|
||||||
|
"""Return the edge graph of the given graph, optionally setting vertex positions."""
|
||||||
|
pos_src: dict[Any, Any] | None = None
|
||||||
|
pos: dict[Any, tuple[Any, Any]] = {}
|
||||||
|
if set_pos:
|
||||||
|
pos_src = cast(dict[Any, Any], graph.get_pos()) # type: ignore
|
||||||
|
|
||||||
|
g = Graph()
|
||||||
|
for e in cast(list[Any], graph.edges()): # type: ignore
|
||||||
|
g.add_vertex(e) # type: ignore
|
||||||
|
if pos_src:
|
||||||
|
pos[e] = (
|
||||||
|
(pos_src[e[0]][0] + pos_src[e[1]][0]) / 2,
|
||||||
|
(pos_src[e[0]][1] + pos_src[e[1]][1]) / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
for v in cast(list[Any], graph.vertices()): # type: ignore
|
||||||
|
incident_edges = cast(list[Any], graph.edges_incident(v)) # type: ignore
|
||||||
|
if len(incident_edges) == 1:
|
||||||
|
continue
|
||||||
|
for i, e1 in enumerate(incident_edges):
|
||||||
|
e2 = incident_edges[(i + 1) % len(incident_edges)]
|
||||||
|
g.add_edge(e1, e2) # type: ignore
|
||||||
|
if set_pos:
|
||||||
|
g.set_pos(pos) # type: ignore
|
||||||
|
return g
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
"""Utilities for computing Tait colorings from vertex colorings of the dual graph."""
|
||||||
|
from typing import Any, cast
|
||||||
|
from sage.all import Graph
|
||||||
|
from lib.edge_for_dual_edge import get_edge_for_dual_edge
|
||||||
|
|
||||||
|
def get_tait_coloring(planar_dual: Graph, coloring: dict[Any, int]) -> dict[Any, int]:
|
||||||
|
"""Return a Tait (edge 3-coloring) from a vertex 4-coloring of the dual graph."""
|
||||||
|
tait_coloring: dict[Any, int] = {}
|
||||||
|
for dual_edge in cast(list[Any], planar_dual.edges()): # type: ignore
|
||||||
|
edge = get_edge_for_dual_edge(dual_edge)
|
||||||
|
colors = {coloring[edge[0]], coloring[edge[1]]}
|
||||||
|
if colors in ({0, 1}, {2, 3}):
|
||||||
|
tait_coloring[dual_edge] = 0
|
||||||
|
elif colors in ({1, 2}, {0, 3}):
|
||||||
|
tait_coloring[dual_edge] = 1
|
||||||
|
else:
|
||||||
|
tait_coloring[dual_edge] = 2
|
||||||
|
return tait_coloring
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
"""Module for performing tutte embeddings"""
|
||||||
|
from typing import Iterable, cast, Any
|
||||||
|
from sage.all import vector, Graph, cos, pi, sin # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def create_tutte_embedding(
|
||||||
|
graph: Graph,
|
||||||
|
outer_face: list[Any],
|
||||||
|
max_iter: int=50,
|
||||||
|
set_pos: bool = True) -> dict[Any, Any]:
|
||||||
|
"""
|
||||||
|
Performs a tutte force embedding with a prescribed outer face on
|
||||||
|
a given sage graph.
|
||||||
|
|
||||||
|
For a description on tutte embeddings, see the video [here](\
|
||||||
|
https://www.youtube.com/watch?v=mEzPPMhR8XE).
|
||||||
|
"""
|
||||||
|
radius = 1000
|
||||||
|
pos: dict[Any, Any] = {}
|
||||||
|
num_outer_points = len(outer_face)
|
||||||
|
for i, v in enumerate(outer_face):
|
||||||
|
pos[v] = (
|
||||||
|
radius * cos((i / num_outer_points) * 2 * pi),
|
||||||
|
radius * sin((i / num_outer_points) * 2 * pi)
|
||||||
|
)
|
||||||
|
|
||||||
|
# start off setting all other points to (0, 0)
|
||||||
|
num_inner_points = cast(int, graph.order()) - num_outer_points
|
||||||
|
|
||||||
|
for i, v in enumerate(cast(Iterable[Any], graph.vertices())): # type: ignore
|
||||||
|
if v in pos:
|
||||||
|
continue
|
||||||
|
pos[v] = (
|
||||||
|
radius/3 * cos((i / num_inner_points) * 2 * pi),
|
||||||
|
radius/3 * sin((i / num_inner_points) * 2 * pi)
|
||||||
|
)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < max_iter:
|
||||||
|
for v in cast(Iterable[Any], graph.vertices()): # type: ignore
|
||||||
|
if v in outer_face:
|
||||||
|
continue
|
||||||
|
neighbors = cast(list[Any], graph.neighbors(v)) # type: ignore
|
||||||
|
deg = len(neighbors)
|
||||||
|
x_desired = sum(map(lambda n: pos[n][0], neighbors)) / deg
|
||||||
|
y_desired = sum(map(lambda n: pos[n][1], neighbors)) / deg
|
||||||
|
pos[v] = tuple(
|
||||||
|
vector(pos[v]) - vector([ # type: ignore
|
||||||
|
pos[v][0] - x_desired, pos[v][1] - y_desired
|
||||||
|
])
|
||||||
|
)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if set_pos:
|
||||||
|
graph.set_pos(pos) # type: ignore
|
||||||
|
|
||||||
|
return pos
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"""Utilities for verifying vertex colorings."""
|
||||||
|
from typing import Any, cast
|
||||||
|
from sage.all import Graph
|
||||||
|
|
||||||
|
def check_vertex_coloring(graph: Graph, vertex_coloring: dict[Any, int]) -> bool:
|
||||||
|
"""Raise ValueError if any two adjacent vertices share a color."""
|
||||||
|
for v in cast(list[Any], graph.vertices()): # type: ignore
|
||||||
|
for nv in cast(list[Any], graph.neighbors(v)): # type: ignore
|
||||||
|
if vertex_coloring[nv] == vertex_coloring[v]:
|
||||||
|
raise ValueError(f"Color {nv} equal to color {v}")
|
||||||
|
return True
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extraPaths": [
|
||||||
|
"/Applications/SageMath-10-8.app/Contents/Frameworks/Sage.framework/Versions/10.8/local/lib/python3.13/site-packages"
|
||||||
|
],
|
||||||
|
"reportUnknownMemberType": "warning",
|
||||||
|
"reportUnknownArgumentType": "warning",
|
||||||
|
"reportUnknownParameterType": "warning",
|
||||||
|
"reportMissingParameterType": "warning",
|
||||||
|
"reportUnknownVariableType": "warning"
|
||||||
|
}
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PYTHON_PATH="/Applications/SageMath-10-8.app/Contents/Frameworks/Sage.framework/Versions/10.8/local/bin/python3"
|
||||||
|
SAGE_SITE_PACKAGES="/Applications/SageMath-10-8.app/Contents/Frameworks/Sage.framework/Versions/10.8/local/lib/python3.13/site-packages"
|
||||||
|
VENV_PYTHON="$SCRIPT_DIR/.venv/bin/python3"
|
||||||
|
|
||||||
init_paper() {
|
init_paper() {
|
||||||
local raw="${1:-.}"
|
local raw="${1:-.}"
|
||||||
@@ -16,13 +19,31 @@ init_paper() {
|
|||||||
echo "Initialized paper.tex in $dest"
|
echo "Initialized paper.tex in $dest"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
"$PYTHON_PATH" -m venv "$SCRIPT_DIR/.venv"
|
||||||
|
"$VENV_PYTHON" -m pip install pylint
|
||||||
|
}
|
||||||
|
|
||||||
|
lint() {
|
||||||
|
npx pyright lib/ --pythonpath "$PYTHON_PATH"
|
||||||
|
"$VENV_PYTHON" -m pylint lib/ \
|
||||||
|
--init-hook="import sys; sys.path.insert(0, '${SAGE_SITE_PACKAGES}'); sys.path.insert(0, '${SCRIPT_DIR}')" \
|
||||||
|
--disable=fixme
|
||||||
|
}
|
||||||
|
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
init_paper)
|
init_paper)
|
||||||
shift
|
shift
|
||||||
init_paper "$@"
|
init_paper "$@"
|
||||||
;;
|
;;
|
||||||
|
setup)
|
||||||
|
setup
|
||||||
|
;;
|
||||||
|
lint)
|
||||||
|
lint
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: $0 {init_paper} [dest_dir]" >&2
|
echo "Usage: $0 {init_paper|setup|lint} [args]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
Reference in New Issue
Block a user