diff --git a/papers/heawood_restrictions_on_nested_tire_graph_duals/experiments/monotonicity_test.py b/papers/heawood_restrictions_on_nested_tire_graph_duals/experiments/monotonicity_test.py new file mode 100644 index 0000000..0ef2c1e --- /dev/null +++ b/papers/heawood_restrictions_on_nested_tire_graph_duals/experiments/monotonicity_test.py @@ -0,0 +1,170 @@ +""" +Test the MONOTONICITY LEMMA behind the 2^(n-2) lower bound: + + adding an interior vertex never DECREASES |Phi| + (equivalently Phi(D') subset Phi(D) when D = D' + one interior vertex). + +If true, every disk reduces to the k=0 base case without increasing Phi, so +|Phi(D)| >= |Phi(D_0)| = 2^(n-2). A single insertion that shrinks Phi (or breaks +the inclusion) would refute the proof strategy. + +We build a validated base disk D' (Delaunay, convex non-cocircular boundary), then +insert a vertex two ways: + * degree-3 stack into a face (proved: should give equality) + * degree-4 open of an internal edge (a,b)|(c,d) (the first genuinely open case) +and compare Phi(D') to Phi(D). +""" + +import sys +from collections import Counter, defaultdict +from itertools import product + +import numpy as np +from scipy.spatial import Delaunay + +np.seterr(all="ignore") + + +def base_disk(n, k, rng): + ang = 2 * np.pi * np.arange(n) / n + rad = 1.0 + 0.15 * rng.random(n) + bpts = np.c_[rad * np.cos(ang), rad * np.sin(ang)] + if k: + r = 0.7 * np.sqrt(rng.random(k)); t = 2 * np.pi * rng.random(k) + pts = np.vstack([bpts, np.c_[r * np.cos(t), r * np.sin(t)]]) + else: + pts = bpts + faces = [tuple(int(x) for x in s) for s in Delaunay(pts).simplices] + return faces + + +def valid(faces, n, k): + if len(faces) != 2 * k + n - 2: + return False + ec = Counter() + for a, b, c in faces: + for e in ((a, b), (b, c), (a, c)): + ec[frozenset(e)] += 1 + return all(ec[frozenset((i, (i + 1) % n))] == 1 for i in range(n)) + + +def phi(faces, n): + verts = set(v for f in faces for v in f) + interior = sorted(v for v in verts if v >= n) + F = len(faces) + if F > 18: + return None + Bint = np.zeros((len(interior), F), dtype=np.int64) + Cinc = np.zeros((n, F), dtype=np.int64) + iidx = {w: r for r, w in enumerate(interior)} + for j, (a, b, c) in enumerate(faces): + for v in (a, b, c): + if v >= n: + Bint[iidx[v], j] = 1 + else: + Cinc[v, j] = 1 + labs = np.array(list(product((1, 2), repeat=F)), dtype=np.int64) + if interior: + labs = labs[np.all((labs @ Bint.T) % 3 == 0, axis=1)] + if labs.shape[0] == 0: + return set() + return set(map(tuple, np.unique((labs @ Cinc.T) % 3, axis=0))) + + +def insert_deg3(faces, fi, v): + a, b, c = faces[fi] + out = [f for i, f in enumerate(faces) if i != fi] + out += [(a, b, v), (b, c, v), (a, c, v)] + return out + + +def insert_deg4(faces, n): + """Open an internal edge (a,b) shared by faces (a,b,c),(a,b,d): replace those + two faces with the 4-star of a new center over the quad a-c-b-d.""" + ef = defaultdict(list) + for i, (a, b, c) in enumerate(faces): + for e in ((a, b), (b, c), (a, c)): + ef[frozenset(e)].append(i) + for e, fl in ef.items(): + if len(fl) != 2: + continue + a, b = tuple(e) + if (a < n and b < n and (b - a) % n in (1, n - 1)): + continue # boundary edge + f1, f2 = faces[fl[0]], faces[fl[1]] + c = next(x for x in f1 if x not in (a, b)) + d = next(x for x in f2 if x not in (a, b)) + if c == d: + continue + v = max(max(f) for f in faces) + 1 + out = [f for i, f in enumerate(faces) if i not in fl] + out += [(a, c, v), (c, b, v), (b, d, v), (d, a, v)] + return out, v + return None, None + + +def main(): + seed = int(sys.argv[1]) if len(sys.argv) > 1 else 0 + rng = np.random.default_rng(seed) + viol_size = 0 # |Phi(D)| < |Phi(D')| (monotonicity broken) + viol_incl = 0 # Phi(D') not subset Phi(D) + tested = 0 + deg3_equal = 0; deg3_tot = 0 + deg4_strict = 0; deg4_tot = 0 + examples = [] + + for _ in range(500): + n = int(rng.integers(4, 7)); k = int(rng.integers(0, 4)) + faces = base_disk(n, k, rng) + if not valid(faces, n, k) or len(faces) > 14: + continue + Pp = phi(faces, n) + if Pp is None: + continue + # degree-3 insertions: every face + for fi in range(len(faces)): + D = insert_deg3(faces, fi, max(max(f) for f in faces) + 1) + P = phi(D, n) + if P is None: + continue + tested += 1; deg3_tot += 1 + if not Pp <= P: + viol_incl += 1 + if len(P) < len(Pp): + viol_size += 1 + if len(examples) < 5: + examples.append(("deg3", n, len(Pp), len(P))) + if len(P) == len(Pp): + deg3_equal += 1 + # one degree-4 insertion + D4, v = insert_deg4(faces, n) + if D4 is not None: + P = phi(D4, n) + if P is not None: + tested += 1; deg4_tot += 1 + if not Pp <= P: + viol_incl += 1 + if len(P) < len(Pp): + viol_size += 1 + if len(examples) < 5: + examples.append(("deg4", n, len(Pp), len(P))) + if len(P) > len(Pp): + deg4_strict += 1 + + print(f"insertions tested: {tested}") + print(f" monotonicity violations (|Phi(D)| < |Phi(D')|): {viol_size}") + print(f" inclusion violations (Phi(D') not subset Phi(D)): {viol_incl}") + print(f" degree-3: {deg3_equal}/{deg3_tot} gave EXACT equality " + f"(proved un-stacking => should be all)") + print(f" degree-4: {deg4_strict}/{deg4_tot} strictly ENLARGED Phi") + if examples: + print(" VIOLATION examples (type,n,|Phi(D')|,|Phi(D)|):") + for e in examples: + print(f" {e}") + else: + print(" no violation: every insertion preserved or enlarged Phi, " + "and Phi(D') subset Phi(D) throughout.") + + +if __name__ == "__main__": + main()