File size: 4,367 Bytes
82a8f4b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import numpy as np
import matplotlib.pyplot as plt
from ideal_poly_volume_toolkit.geometry import delaunay_triangulation_indices, lift_to_sphere_with_inf
from scipy.spatial import ConvexHull
import torch

# Recreate the maximal configuration from seed 104
def get_maximal_config(seed=104):
    rng = np.random.default_rng(seed)
    K = 9
    
    # Initialize
    r = 0.5 + 1.5 * rng.random(K)
    theta = 2 * np.pi * rng.random(K)
    z_init = r * np.exp(1j * theta)
    
    real_parts = torch.tensor(z_init.real, dtype=torch.float64, requires_grad=True)
    imag_parts = torch.tensor(z_init.imag, dtype=torch.float64, requires_grad=True)
    
    def build_Z_free(real_parts, imag_parts):
        Z = torch.empty(real_parts.numel() + 2, dtype=torch.complex128)
        Z[0] = 0 + 0j
        Z[1] = 1 + 0j
        Z[2:] = torch.complex(real_parts, imag_parts)
        return Z
    
    opt = torch.optim.LBFGS([real_parts, imag_parts], lr=1.0, max_iter=20, line_search_fn='strong_wolfe')
    
    for it in range(100):
        with torch.no_grad():
            Z_np = build_Z_free(real_parts, imag_parts).detach().cpu().numpy()
            idx = delaunay_triangulation_indices(Z_np)
        
        def closure():
            opt.zero_grad(set_to_none=True)
            Z_t = build_Z_free(real_parts, imag_parts)
            total = torch.zeros((), dtype=torch.float64)
            for (i, j, k) in idx:
                from ideal_poly_volume_toolkit.geometry import triangle_volume_from_points_torch
                total = total + triangle_volume_from_points_torch(
                    Z_t[i], Z_t[j], Z_t[k], series_terms=96
                )
            loss = -total
            loss.backward()
            torch.nn.utils.clip_grad_norm_([real_parts, imag_parts], max_norm=10.0)
            return loss
        
        opt.step(closure)
    
    with torch.no_grad():
        Zf = build_Z_free(real_parts, imag_parts).detach().cpu().numpy()
        
    return Zf, idx

# Get the configuration
z_points, triangulation = get_maximal_config()

# Visualize in complex plane
plt.figure(figsize=(10, 10))

# Plot points
real_coords = z_points.real
imag_coords = z_points.imag
plt.scatter(real_coords, imag_coords, s=200, c='red', zorder=5)

# Label points
for i, z in enumerate(z_points):
    offset = 0.15
    plt.annotate(f'{i}', (z.real + offset, z.imag + offset), fontsize=12)

# Draw Delaunay triangles
for tri in triangulation:
    triangle_x = [real_coords[tri[j]] for j in range(3)] + [real_coords[tri[0]]]
    triangle_y = [imag_coords[tri[j]] for j in range(3)] + [imag_coords[tri[0]]]
    plt.plot(triangle_x, triangle_y, 'b-', linewidth=1.5, alpha=0.7)

plt.xlabel('Real', fontsize=14)
plt.ylabel('Imaginary', fontsize=14)
plt.title('Maximal 12-vertex Configuration (Volume = 13.032)\nDifferent combinatorial type than icosahedron!', fontsize=16)
plt.grid(True, alpha=0.3)
plt.axis('equal')

plt.tight_layout()
plt.savefig('maximal_12vertex_plane.png', dpi=150, bbox_inches='tight')
print("Saved: maximal_12vertex_plane.png")

# Analyze the sphere structure
z_with_inf = np.append(z_points, [np.inf])
sphere_points = lift_to_sphere_with_inf(z_with_inf)
hull = ConvexHull(sphere_points)

# Find which vertices have degree 4 and 6
vertex_degrees = {}
for i in range(12):
    degree = sum(1 for face in hull.simplices if i in face)
    vertex_degrees[i] = degree

print("\nVertex degrees in detail:")
deg_4_vertices = [v for v, d in vertex_degrees.items() if d == 4]
deg_5_vertices = [v for v, d in vertex_degrees.items() if d == 5]
deg_6_vertices = [v for v, d in vertex_degrees.items() if d == 6]

print(f"Degree 4 vertices: {deg_4_vertices}")
print(f"Degree 5 vertices: {deg_5_vertices}")
print(f"Degree 6 vertices: {deg_6_vertices}")

# Map back to complex plane
print("\nSpecial vertices in complex plane:")
for v in deg_4_vertices:
    if v < 11:
        print(f"  Vertex {v} (deg 4): z = {z_points[v]:.4f}")
    else:
        print(f"  Vertex {v} (deg 4): infinity")
        
for v in deg_6_vertices:
    if v < 11:
        print(f"  Vertex {v} (deg 6): z = {z_points[v]:.4f}")
    else:
        print(f"  Vertex {v} (deg 6): infinity")

print(f"\nThis polyhedron has the same number of faces (20) as the icosahedron,")
print(f"but a different combinatorial structure!")
print(f"It's a non-regular ideal polyhedron with 12 vertices.")

plt.close()