Survey: All h11=2 Calabi-Yau threefolds#
This notebook runs the full EKC construction on all 36 polytopes in the CYTools database with \(h^{1,1} = 2\), collecting statistics on phases, contraction types, Coxeter groups, and cone generators.
Uses the adaptive GV degree strategy (start at max_deg=4, bump if needed).
import cybir
from cybir import CYBirationalClass, patch_cytools
from cybir.core.types import ContractionType
import cytools
import numpy as np
from collections import Counter
import time
patch_cytools()
Load all h11=2 polytopes#
polys = cytools.fetch_polytopes(h11=2, lattice="N")
print(f"Loaded {len(polys)} polytopes with h11=2")
Loaded 36 polytopes with h11=2
Run EKC construction on all polytopes#
For each polytope, construct the birational class (fundamental domain), then apply the Coxeter orbit expansion.
results = []
for i, p in enumerate(polys):
cy = p.triangulate().get_cy()
t0 = time.perf_counter()
ekc = CYBirationalClass.from_gv(cy, max_deg=4, verbose=False)
n_fund = len(ekc.phases)
ekc.apply_coxeter_orbit()
n_total = len(ekc.phases)
elapsed = time.perf_counter() - t0
# Collect contraction type counts
type_counts = Counter(
c.contraction_type.display_name() for c in ekc.contractions
)
results.append({
"idx": i,
"ekc": ekc,
"n_fund": n_fund,
"n_total": n_total,
"n_contractions": len(ekc.contractions),
"type_counts": type_counts,
"n_sym_flops": len(ekc._sym_flop_pairs),
"coxeter_type": ekc.coxeter_type,
"coxeter_order": ekc.coxeter_order,
"n_inf_gens": len(ekc.infinity_cone_gens),
"n_eff_gens": len(ekc.eff_cone_gens),
"time": elapsed,
})
print(f" #{i}: {elapsed:.2f}s fund={n_fund} total={n_total} "
f"sym_flops={len(ekc._sym_flop_pairs)} "
f"coxeter={ekc.coxeter_type}")
#0: 1.41s fund=1 total=1 sym_flops=0 coxeter=None
#1: 0.39s fund=1 total=1 sym_flops=0 coxeter=None
#2: 0.22s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#3: 0.30s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#4: 0.30s fund=1 total=1 sym_flops=0 coxeter=None
#5: 0.72s fund=2 total=2 sym_flops=0 coxeter=None
#6: 0.25s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#7: 0.30s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#8: 0.26s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#9: 0.29s fund=1 total=1 sym_flops=0 coxeter=None
#10: 0.29s fund=2 total=2 sym_flops=0 coxeter=None
#11: 0.29s fund=2 total=2 sym_flops=0 coxeter=None
#12: 0.60s fund=2 total=2 sym_flops=0 coxeter=None
#13: 0.29s fund=2 total=2 sym_flops=0 coxeter=None
#14: 0.25s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#15: 0.35s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#16: 0.43s fund=1 total=1 sym_flops=0 coxeter=None
#17: 0.34s fund=1 total=1 sym_flops=0 coxeter=None
#18: 0.27s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#19: 0.32s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#20: 0.27s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#21: 0.25s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#22: 0.33s fund=1 total=1 sym_flops=0 coxeter=None
#23: 0.31s fund=1 total=1 sym_flops=0 coxeter=None
#24: 0.33s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#25: 0.24s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#26: 0.28s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#27: 0.33s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#28: 0.26s fund=1 total=2 sym_flops=1 coxeter=[('A', 1, 2)]
#29: 0.28s fund=1 total=1 sym_flops=0 coxeter=None
#30: 0.46s fund=2 total=2 sym_flops=0 coxeter=None
#31: 0.31s fund=1 total=1 sym_flops=0 coxeter=None
#32: 0.29s fund=1 total=1 sym_flops=0 coxeter=None
#33: 0.27s fund=1 total=1 sym_flops=0 coxeter=None
#34: 0.28s fund=1 total=1 sym_flops=0 coxeter=None
#35: 0.28s fund=1 total=1 sym_flops=0 coxeter=None
Summary statistics#
n = len(results)
print(f"Polytopes: {n}")
print(f"Total time: {sum(r['time'] for r in results):.1f}s")
print(f"Avg time: {sum(r['time'] for r in results)/n:.2f}s")
print()
# Phase counts
fund_phases = [r["n_fund"] for r in results]
total_phases = [r["n_total"] for r in results]
print(f"Fundamental domain phases: min={min(fund_phases)} max={max(fund_phases)} "
f"avg={sum(fund_phases)/n:.1f}")
print(f"Total phases (after orbit): min={min(total_phases)} max={max(total_phases)} "
f"avg={sum(total_phases)/n:.1f}")
print()
# Coxeter groups
with_orbit = [r for r in results if r["coxeter_order"] and r["coxeter_order"] > 1]
print(f"Polytopes with nontrivial Coxeter group: {len(with_orbit)}/{n}")
if with_orbit:
type_dist = Counter(str(r["coxeter_type"]) for r in with_orbit)
for t, c in type_dist.most_common():
print(f" {t}: {c} polytopes")
Polytopes: 36
Total time: 12.6s
Avg time: 0.35s
Fundamental domain phases: min=1 max=2 avg=1.2
Total phases (after orbit): min=1 max=2 avg=1.6
Polytopes with nontrivial Coxeter group: 16/36
[('A', 1, 2)]: 16 polytopes
Contraction type distribution#
# Aggregate contraction types across all polytopes
total_types = Counter()
for r in results:
total_types.update(r["type_counts"])
print("Contraction types across all polytopes:")
for name, count in total_types.most_common():
print(f" {name}: {count}")
Contraction types across all polytopes:
CFT: 34
asymptotic: 32
symmetric flop: 32
generic flop: 12
su(2) enhancement: 6
Per-polytope detail table#
print(f"{'#':>3} {'Fund':>4} {'Total':>5} {'Contr':>5} {'SymFlop':>7} "
f"{'InfGen':>6} {'EffGen':>6} {'Coxeter':>12} {'Time':>5}")
print("-" * 72)
for r in results:
cox_str = str(r['coxeter_type']) if r['coxeter_type'] else "-"
print(f"{r['idx']:3d} {r['n_fund']:4d} {r['n_total']:5d} "
f"{r['n_contractions']:5d} {r['n_sym_flops']:7d} "
f"{r['n_inf_gens']:6d} {r['n_eff_gens']:6d} "
f"{cox_str:>12} {r['time']:5.2f}s")
# Fund Total Contr SymFlop InfGen EffGen Coxeter Time
------------------------------------------------------------------------
0 1 1 2 0 2 2 - 1.41s
1 1 1 2 0 2 3 - 0.39s
2 1 2 4 1 2 5 [('A', 1, 2)] 0.22s
3 1 2 4 1 2 5 [('A', 1, 2)] 0.30s
4 1 1 2 0 2 2 - 0.30s
5 2 2 4 0 2 5 - 0.72s
6 1 2 4 1 2 3 [('A', 1, 2)] 0.25s
7 1 2 4 1 2 3 [('A', 1, 2)] 0.30s
8 1 2 4 1 2 3 [('A', 1, 2)] 0.26s
9 1 1 2 0 1 3 - 0.29s
10 2 2 4 0 2 3 - 0.29s
11 2 2 4 0 1 4 - 0.29s
12 2 2 4 0 2 4 - 0.60s
13 2 2 4 0 1 4 - 0.29s
14 1 2 4 1 2 5 [('A', 1, 2)] 0.25s
15 1 2 4 1 2 5 [('A', 1, 2)] 0.35s
16 1 1 2 0 2 3 - 0.43s
17 1 1 2 0 1 3 - 0.34s
18 1 2 4 1 2 5 [('A', 1, 2)] 0.27s
19 1 2 4 1 2 5 [('A', 1, 2)] 0.32s
20 1 2 4 1 2 5 [('A', 1, 2)] 0.27s
21 1 2 4 1 2 5 [('A', 1, 2)] 0.25s
22 1 1 2 0 2 3 - 0.33s
23 1 1 2 0 1 4 - 0.31s
24 1 2 4 1 2 5 [('A', 1, 2)] 0.33s
25 1 2 4 1 2 5 [('A', 1, 2)] 0.24s
26 1 2 4 1 2 5 [('A', 1, 2)] 0.28s
27 1 2 4 1 2 3 [('A', 1, 2)] 0.33s
28 1 2 4 1 2 3 [('A', 1, 2)] 0.26s
29 1 1 2 0 2 2 - 0.28s
30 2 2 4 0 2 4 - 0.46s
31 1 1 2 0 1 3 - 0.31s
32 1 1 2 0 2 3 - 0.29s
33 1 1 2 0 2 3 - 0.27s
34 1 1 2 0 2 3 - 0.28s
35 1 1 2 0 2 3 - 0.28s
Inspect a polytope with nontrivial Coxeter group#
Pick the first polytope that has symmetric flops and show its full structure.
# Find first polytope with symmetric flops
sym_example = next((r for r in results if r["n_sym_flops"] > 0), None)
if sym_example:
ekc = sym_example["ekc"]
print(f"Polytope #{sym_example['idx']}")
print(f"Coxeter type: {ekc.coxeter_type}")
print(f"Coxeter group order: {ekc.coxeter_order}")
print(f"Fundamental phases: {sym_example['n_fund']}")
print(f"Total phases: {sym_example['n_total']}")
print()
print("Phases:")
for phase in ekc.phases:
print(f" {phase.label}: int_nums shape = {phase.int_nums.shape}")
print()
print("Contractions:")
for c in ekc.contractions:
print(f" {c.contraction_type.display_name()}: "
f"curve = {c.contraction_curve}")
print()
print(f"Infinity cone generators: {ekc.infinity_cone_gens}")
print(f"Effective cone generators: {ekc.eff_cone_gens}")
if ekc.coxeter_matrix is not None:
print(f"\nCoxeter matrix:\n{ekc.coxeter_matrix}")
else:
print("No polytopes with symmetric flops found.")
Polytope #2
Coxeter type: [('A', 1, 2)]
Coxeter group order: 2
Fundamental phases: 1
Total phases: 2
Phases:
CY_0: int_nums shape = (2, 2, 2)
CY_1: int_nums shape = (2, 2, 2)
Contractions:
CFT: curve = [ 1 -1]
symmetric flop: curve = [1 0]
CFT: curve = [-2 -1]
symmetric flop: curve = [-1 0]
Infinity cone generators: frozenset({(-1, 1), (-2, -1)})
Effective cone generators: frozenset({(0, 1), (-1, 0), (1, 0), (1, 1), (-1, 2)})
Coxeter matrix:
[[-1 1]
[ 0 1]]
Inspect the most complex polytope#
The one with the most phases in the fundamental domain.
most_phases = max(results, key=lambda r: r["n_fund"])
ekc = most_phases["ekc"]
print(f"Polytope #{most_phases['idx']}: {most_phases['n_fund']} phases")
print()
# Graph structure
print("Phase graph:")
for phase in ekc.phases:
neighbors = ekc.graph.neighbors(phase.label)
print(f" {phase.label} -> {[n.label for n in neighbors]}")
print()
print("Contraction types:")
type_counts = Counter(c.contraction_type.display_name() for c in ekc.contractions)
for name, count in type_counts.most_common():
print(f" {name}: {count}")
Polytope #5: 2 phases
Phase graph:
CY_0 -> ['CY_1', 'CY_0']
CY_1 -> ['CY_0', 'CY_1']
Contraction types:
generic flop: 2
CFT: 2