igriv's picture
Add Rivin-Delaunay optimization and arithmeticity checking
c89947b
"""
Interface to plantri for enumerating planar triangulations.
plantri is installed via passagemath-plantri and provides a command-line
tool for generating planar graphs and triangulations.
"""
import subprocess
import sys
import os
from pathlib import Path
from typing import Optional, List, Tuple
import tempfile
def find_plantri_executable() -> Optional[str]:
"""
Find the plantri executable.
Returns:
Path to plantri executable, or None if not found
"""
# Try common locations
possible_paths = [
# From passagemath-plantri installation
Path(sys.prefix) / 'lib' / f'python{sys.version_info.major}.{sys.version_info.minor}' / 'site-packages' / 'sage_wheels' / 'bin' / 'plantri',
# System installation
'plantri',
]
for path in possible_paths:
try:
result = subprocess.run([str(path)], capture_output=True, timeout=1)
return str(path)
except (FileNotFoundError, subprocess.TimeoutExpired):
continue
return None
def count_triangulations(n_vertices: int, only_polytopes: bool = True) -> int:
"""
Count the number of planar triangulations with n vertices.
Args:
n_vertices: Number of vertices
only_polytopes: If True, only count 3-connected (convex polytope) triangulations
Returns:
Number of triangulations
"""
plantri = find_plantri_executable()
if plantri is None:
raise RuntimeError("plantri executable not found. Install with: pip install passagemath-plantri")
# -p flag: only 3-connected planar (polytopes)
args = [plantri, '-p' if only_polytopes else '', str(n_vertices)]
args = [a for a in args if a] # Remove empty strings
result = subprocess.run(args, capture_output=True, text=True)
# Parse output: "N polytopes written to stdout; cpu=X.XX sec"
for line in result.stderr.split('\n'):
if 'written' in line:
parts = line.split()
return int(parts[0])
raise RuntimeError(f"Could not parse plantri output: {result.stderr}")
def enumerate_triangulations(n_vertices: int,
only_polytopes: bool = True,
output_format: str = 'planar_code') -> bytes:
"""
Generate all planar triangulations with n vertices.
Args:
n_vertices: Number of vertices
only_polytopes: If True, only generate 3-connected (convex polytope) triangulations
output_format: Output format ('planar_code' is the default binary format)
Returns:
Binary output in planar_code format
"""
plantri = find_plantri_executable()
if plantri is None:
raise RuntimeError("plantri executable not found. Install with: pip install passagemath-plantri")
args = [plantri, '-p' if only_polytopes else '', str(n_vertices)]
args = [a for a in args if a] # Remove empty strings
result = subprocess.run(args, capture_output=True)
if result.returncode not in [0, 1]: # plantri often returns 1 even on success
raise RuntimeError(f"plantri failed: {result.stderr.decode()}")
return result.stdout
# Lookup table for small values (OEIS A000109 for 3-connected planar graphs)
KNOWN_COUNTS = {
4: 1, # Tetrahedron
5: 1, # Triangular dipyramid
6: 7, # Including octahedron
7: 34,
8: 257,
9: 2606,
10: 32300,
11: 440564,
12: 6384634,
}
def get_triangulation_count(n_vertices: int, use_cache: bool = True) -> int:
"""
Get the number of 3-connected planar triangulations with n vertices.
Args:
n_vertices: Number of vertices
use_cache: If True, use precomputed values for small n
Returns:
Number of triangulations
"""
if use_cache and n_vertices in KNOWN_COUNTS:
return KNOWN_COUNTS[n_vertices]
return count_triangulations(n_vertices, only_polytopes=True)
if __name__ == '__main__':
# Test
print("Testing plantri interface...")
for n in range(4, 10):
count = get_triangulation_count(n)
print(f" n={n}: {count} triangulations")
print("\n✓ plantri interface working!")