File size: 6,926 Bytes
f9b644c
 
 
 
 
 
 
 
 
d7d27f0
 
f9b644c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/usr/bin/env python3
"""
Check arithmeticity of local maxima found during 20-vertex optimization.
Tests the conjecture that local maxima are more likely to be arithmetic.
"""

import numpy as np
import json
import os
from scipy.spatial import Delaunay
from ideal_poly_volume_toolkit.rivin_holonomy import Triangulation, generators_from_triangulation

def build_triangulation_from_config(vertices_dict):
    """Convert vertex configuration to triangulation for holonomy computation."""
    # Extract finite vertices (excluding infinity)
    vertices = []
    for k, v in sorted(vertices_dict.items()):
        if k != 'z_inf':
            vertices.append(complex(v['real'], v['imag']))
    
    # Convert to 2D points
    points_2d = np.array([[v.real, v.imag] for v in vertices])
    
    # Get Delaunay triangulation
    tri = Delaunay(points_2d)
    triangles = tri.simplices
    
    # Build adjacency structure
    F = len(triangles)
    adjacency = {}
    edge_id_map = {}
    edge_id = 0
    
    for i, tri_i in enumerate(triangles):
        for side_i in range(3):
            v1_i, v2_i = tri_i[side_i], tri_i[(side_i + 1) % 3]
            edge = tuple(sorted([v1_i, v2_i]))
            
            for j, tri_j in enumerate(triangles):
                if i == j:
                    continue
                for side_j in range(3):
                    v1_j, v2_j = tri_j[side_j], tri_j[(side_j + 1) % 3]
                    if set([v1_j, v2_j]) == set([v1_i, v2_i]):
                        if (i, side_i) not in adjacency:
                            if edge not in edge_id_map:
                                edge_id_map[edge] = edge_id
                                edge_id += 1
                            adjacency[(i, side_i)] = (j, side_j, edge_id_map[edge])
    
    # Define order and orientation
    order = {t: [0, 1, 2] for t in range(F)}
    orientation = {}
    for edge, eid in edge_id_map.items():
        for (t, s), (u, su, e) in adjacency.items():
            if e == eid:
                orientation[eid] = ((t, s), (u, su))
                break
    
    return Triangulation(F, adjacency, order, orientation), triangles

def check_arithmeticity(config):
    """Check if a configuration has arithmetic holonomy."""
    try:
        T, triangles = build_triangulation_from_config(config['vertices'])
        
        # Zero shears for ideal polyhedra
        Z = {eid: 0.0 for eid in range(len(T.Ori))}
        
        # Compute generators
        gens = generators_from_triangulation(T, Z, root=0)
        
        # Extract traces
        traces = []
        integral_traces = 0
        close_to_algebraic = 0
        
        for i, (u, v, tokens, M) in enumerate(gens):
            trace = M[0][0] + M[1][1]
            traces.append(trace)
            
            # Check proximity to integers
            nearest_int = round(trace)
            if abs(trace - nearest_int) < 0.001:
                integral_traces += 1
            
            # Check proximity to simple algebraic numbers
            # (e.g., ±√2, ±√3, golden ratio, etc.)
            algebraic_values = [
                np.sqrt(2), -np.sqrt(2), np.sqrt(3), -np.sqrt(3),
                (1 + np.sqrt(5))/2, (1 - np.sqrt(5))/2,  # golden ratio
                np.sqrt(5), -np.sqrt(5)
            ]
            
            for alg in algebraic_values:
                if abs(trace - alg) < 0.001:
                    close_to_algebraic += 1
                    break
        
        # Analyze trace field
        all_integral = (integral_traces == len(traces))
        mostly_integral = (integral_traces >= len(traces) * 0.8)
        has_algebraic = (close_to_algebraic > 0)
        
        return {
            'traces': traces,
            'num_generators': len(traces),
            'integral_traces': integral_traces,
            'all_integral': all_integral,
            'mostly_integral': mostly_integral,
            'has_algebraic': has_algebraic,
            'likely_arithmetic': all_integral or (mostly_integral and has_algebraic)
        }
    
    except Exception as e:
        return {
            'error': str(e),
            'likely_arithmetic': False
        }

def analyze_all_local_maxima():
    """Analyze all saved local maxima for arithmeticity."""
    
    # Check if local maxima file exists
    if not os.path.exists('20vertex_local_maxima.json'):
        print("No local maxima file found yet. Waiting for optimization to save some...")
        return
    
    with open('20vertex_local_maxima.json', 'r') as f:
        data = json.load(f)
    
    print(f"\n{'='*70}")
    print("ARITHMETICITY ANALYSIS OF LOCAL MAXIMA")
    print(f"{'='*70}")
    print(f"Found {data['local_maxima_count']} local maxima to analyze\n")
    
    arithmetic_count = 0
    results = []
    
    for i, config in enumerate(data['configurations']):
        print(f"\nConfiguration {i+1}:")
        print(f"  Volume: {config['volume']:.8f}")
        print(f"  Ratio to dodecahedron: {config['volume']/data['dodecahedron_volume']:.6f}")
        
        arith_result = check_arithmeticity(config)
        
        if 'error' in arith_result:
            print(f"  Error in analysis: {arith_result['error']}")
        else:
            print(f"  Generators: {arith_result['num_generators']}")
            print(f"  Integral traces: {arith_result['integral_traces']}/{arith_result['num_generators']}")
            print(f"  Traces: {[f'{t:.6f}' for t in arith_result['traces']]}")
            
            if arith_result['likely_arithmetic']:
                print(f"  *** LIKELY ARITHMETIC! ***")
                arithmetic_count += 1
        
        results.append({
            'volume': config['volume'],
            'arithmetic': arith_result.get('likely_arithmetic', False),
            'traces': arith_result.get('traces', [])
        })
    
    # Summary
    print(f"\n{'='*70}")
    print("SUMMARY:")
    print(f"{'='*70}")
    print(f"Total local maxima analyzed: {len(data['configurations'])}")
    print(f"Likely arithmetic: {arithmetic_count} ({arithmetic_count/len(data['configurations'])*100:.1f}%)")
    
    # Save results
    output = {
        'analysis_date': str(datetime.now()),
        'local_maxima_count': len(data['configurations']),
        'arithmetic_count': arithmetic_count,
        'results': results,
        'conjecture_support': arithmetic_count > len(data['configurations']) * 0.5
    }
    
    with open('20vertex_arithmetic_analysis.json', 'w') as f:
        json.dump(output, f, indent=2)
    
    print(f"\nResults saved to 20vertex_arithmetic_analysis.json")
    
    if output['conjecture_support']:
        print("\n*** CONJECTURE SUPPORTED: Majority of local maxima appear arithmetic! ***")
    else:
        print("\n*** More data needed to support conjecture ***")

if __name__ == "__main__":
    from datetime import datetime
    analyze_all_local_maxima()