coloring_nested_tire_graphs: SR + PDS chain consistency holds robustly
Redo step 2 and step 3 under SR (no chord effect, the correct model
under PDS where O-faces are not G-faces).
Step 2 (pairwise, 14 cases): all compatible. T_1's γ-side projection
saturates {1,2,3}^γ under outward PDS (m_1 ≥ γ from step 1), so
intersection = T_2's projection, always nonempty.
Step 3 (chain consistency, 10 chains up to 6 tires): all compatible.
Forward propagation along the chain shows monotonically growing
support sizes (roughly 3x per step), never empties. Free choice
accumulates outward.
Implication: chain consistency under SR + PDS is essentially
automatic for "open" chains. The remaining gap is the boundary
condition at the outermost level (e.g., the outer triangle of a
triangulated sphere has only 6 valid σ-permutations); whether the
forward state always contains one of those is the next experiment.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
"""Chain pigeonhole under SR + PDS, no chord constraints.
|
||||
|
||||
Under the correct PDS modelling, each tire's T'_{f'} has γ inner-spoke
|
||||
pendants regardless of O's chord structure (chord edges of O become
|
||||
G'-edges between inner-spoke vertices, but those edges are NOT in
|
||||
T'_{f'} since neither endpoint is in V(f')). So the only inputs are
|
||||
cycle lengths (m, k) per tire.
|
||||
|
||||
For each tire T with B_out length m and B_in length k:
|
||||
Π_T ⊆ {1,2,3}^m × {1,2,3}^k
|
||||
= { (σ_U, σ_D) : σ comes from a proper edge 3-colouring of C_{m+k} }
|
||||
|
||||
Step 2 (pairwise): for adjacent tires T_1, T_2 sharing γ, do
|
||||
π_U(T_1)|_γ and π_D(T_2)|_γ overlap? By step-1 saturation, yes —
|
||||
T_1's γ-side saturates {1,2,3}^γ when m_1 ≥ |γ|, etc.
|
||||
|
||||
Step 3 (chain consistency): chain T_1 | T_2 | ... | T_n. σ at each
|
||||
shared cycle must be jointly consistent. Propagate forward via the
|
||||
joint supports and check if state ever empties.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
from itertools import product
|
||||
|
||||
from tire_fiber_chords import d_positions_for, u_positions_for
|
||||
from tire_fiber_chords_fast import proper_cycle_colorings, induced_sigma_vec
|
||||
|
||||
|
||||
_pi_cache: dict = {}
|
||||
|
||||
|
||||
def joint_support_SR(m: int, k: int) -> set[tuple[tuple[int, ...], tuple[int, ...]]]:
|
||||
"""Π_T = {(σ_U, σ_D)} pairs for SR tire (no chord)."""
|
||||
key = (m, k)
|
||||
if key in _pi_cache:
|
||||
return _pi_cache[key]
|
||||
n = m + k
|
||||
d_pos = d_positions_for(m, k)
|
||||
u_pos = u_positions_for(m, k)
|
||||
c = proper_cycle_colorings(n)
|
||||
sigma = induced_sigma_vec(c)
|
||||
sigma_u = sigma[:, u_pos]
|
||||
sigma_d = sigma[:, d_pos]
|
||||
pairs = set(zip(map(tuple, sigma_u.tolist()),
|
||||
map(tuple, sigma_d.tolist())))
|
||||
_pi_cache[key] = pairs
|
||||
return pairs
|
||||
|
||||
|
||||
# --- Step 2 ---
|
||||
|
||||
def step2_SR(gamma: int, m_1: int, k_2: int) -> dict:
|
||||
"""T_1 has (m=m_1, k=γ). T_2 has (m=γ, k=k_2)."""
|
||||
pi1 = joint_support_SR(m_1, gamma)
|
||||
pi2 = joint_support_SR(gamma, k_2)
|
||||
sd1 = {p[1] for p in pi1} # γ-projection from T_1's side
|
||||
su2 = {p[0] for p in pi2} # γ-projection from T_2's side
|
||||
fwd = sd1 & su2
|
||||
rev = sd1 & {s[::-1] for s in su2}
|
||||
return {
|
||||
'γ': gamma, 'm_1': m_1, 'k_2': k_2,
|
||||
'|sd1|': len(sd1), '|su2|': len(su2), '3^γ': 3**gamma,
|
||||
'sd1_saturates': len(sd1) == 3**gamma,
|
||||
'su2_saturates': len(su2) == 3**gamma,
|
||||
'fwd': len(fwd), 'rev': len(rev),
|
||||
'compatible': bool(fwd or rev),
|
||||
}
|
||||
|
||||
|
||||
# --- Step 3 ---
|
||||
|
||||
def step3_chain(chain: list[tuple[int, int]]) -> dict:
|
||||
"""Forward-propagate joint supports along a tire chain.
|
||||
|
||||
chain[i] = (m_i, k_i) for tire T_i. Adjacency requires k_{i+1} = m_i
|
||||
(T_{i+1}'s B_in = T_i's B_out = shared γ).
|
||||
|
||||
State at step i = set of σ at the "current" shared cycle γ_i = L_i,
|
||||
which is reachable through T_1, ..., T_i.
|
||||
|
||||
Returns: compatibility result and the state size trajectory.
|
||||
"""
|
||||
# Validate adjacency
|
||||
for i in range(len(chain) - 1):
|
||||
if chain[i][0] != chain[i + 1][1]:
|
||||
return {'error': f'chain adjacency mismatch at i={i}: '
|
||||
f'T_{i+1}.m={chain[i][0]} != T_{i+2}.k={chain[i+1][1]}'}
|
||||
|
||||
pis = [joint_support_SR(m, k) for (m, k) in chain]
|
||||
|
||||
# Initial state: σ at L_1 from T_1's σ_U-projection (any σ_D ok since
|
||||
# the innermost boundary L_0 has no further constraint).
|
||||
state = {p[0] for p in pis[0]}
|
||||
trajectory = [len(state)]
|
||||
|
||||
for i in range(1, len(chain)):
|
||||
new_state = set()
|
||||
pi = pis[i]
|
||||
# T_i has B_in = L_i, B_out = L_{i+1}. pair = (σ at L_{i+1}, σ at L_i).
|
||||
# For each pair, if σ at L_i in current state, add σ at L_{i+1} to new state.
|
||||
for sigma_outer, sigma_inner in pi:
|
||||
if sigma_inner in state:
|
||||
new_state.add(sigma_outer)
|
||||
trajectory.append(len(new_state))
|
||||
if not new_state:
|
||||
return {
|
||||
'chain': chain,
|
||||
'compatible': False,
|
||||
'failed_at_tire_index': i,
|
||||
'trajectory': trajectory,
|
||||
}
|
||||
state = new_state
|
||||
|
||||
return {
|
||||
'chain': chain,
|
||||
'compatible': True,
|
||||
'final_state_size': len(state),
|
||||
'trajectory': trajectory,
|
||||
}
|
||||
|
||||
|
||||
def step3_chain_with_reflections(chain: list[tuple[int, int]]) -> dict:
|
||||
"""Like step3_chain but also tries flipping orientation at each
|
||||
junction (since adjacent tires may have reversed γ-orientations
|
||||
in the actual plane embedding).
|
||||
|
||||
More expensive: 2^(len(chain)-1) orientation choices."""
|
||||
n = len(chain)
|
||||
pis = [joint_support_SR(m, k) for (m, k) in chain]
|
||||
|
||||
# Try every combination of orientation flips at junctions.
|
||||
# flip[i] = True means flip σ at L_{i+1} when bridging T_i → T_{i+1}.
|
||||
# Equivalent: reverse the σ_D of T_{i+1} before matching.
|
||||
|
||||
best = None
|
||||
for flips in product([False, True], repeat=n - 1):
|
||||
state = {p[0] for p in pis[0]}
|
||||
traj = [len(state)]
|
||||
ok = True
|
||||
for i in range(1, n):
|
||||
pi = pis[i]
|
||||
new_state = set()
|
||||
if flips[i - 1]:
|
||||
# Reverse σ at L_i (the boundary between T_{i-1} and T_i)
|
||||
state_match = state
|
||||
for sigma_outer, sigma_inner in pi:
|
||||
if sigma_inner[::-1] in state_match:
|
||||
new_state.add(sigma_outer)
|
||||
else:
|
||||
for sigma_outer, sigma_inner in pi:
|
||||
if sigma_inner in state:
|
||||
new_state.add(sigma_outer)
|
||||
traj.append(len(new_state))
|
||||
if not new_state:
|
||||
ok = False
|
||||
break
|
||||
state = new_state
|
||||
if ok:
|
||||
if best is None or len(state) > best['final_state_size']:
|
||||
best = {
|
||||
'chain': chain,
|
||||
'compatible': True,
|
||||
'final_state_size': len(state),
|
||||
'trajectory': traj,
|
||||
'flips': flips,
|
||||
}
|
||||
|
||||
if best is not None:
|
||||
return best
|
||||
return {
|
||||
'chain': chain,
|
||||
'compatible': False,
|
||||
'tried_orientations': 2 ** (n - 1),
|
||||
}
|
||||
|
||||
|
||||
# --- Test scenarios ---
|
||||
|
||||
PAIRWISE_CASES = [
|
||||
# (γ, m_1, k_2): T_1 has B_in=γ; T_2 has B_out=γ.
|
||||
(3, 3, 3),
|
||||
(4, 4, 3),
|
||||
(4, 4, 4),
|
||||
(5, 5, 3),
|
||||
(5, 6, 5),
|
||||
(6, 6, 3),
|
||||
(6, 6, 4),
|
||||
(6, 6, 5),
|
||||
(6, 6, 6),
|
||||
(8, 8, 4),
|
||||
(8, 8, 6),
|
||||
(8, 10, 8),
|
||||
(10, 10, 8),
|
||||
(12, 12, 6),
|
||||
]
|
||||
|
||||
# 3-tire and longer chains.
|
||||
# Each tuple (m, k) -- adjacent (m_i, k_i), (m_{i+1}, k_{i+1}) need k_{i+1} = m_i.
|
||||
CHAIN_CASES = [
|
||||
# Strictly outward-growing PDS:
|
||||
[(4, 3), (5, 4), (6, 5)],
|
||||
[(4, 3), (6, 4), (8, 6)],
|
||||
[(5, 4), (6, 5), (7, 6)],
|
||||
[(6, 3), (8, 6), (10, 8)],
|
||||
# Stalled growth:
|
||||
[(4, 3), (5, 4), (5, 5)],
|
||||
[(4, 3), (4, 4), (5, 4)],
|
||||
# Shrinking (anti-PDS, for stress):
|
||||
[(3, 4), (3, 3)], # would need k_2=m_1=3 -- ok
|
||||
# Longer chains:
|
||||
[(4, 3), (5, 4), (6, 5), (7, 6)],
|
||||
[(4, 3), (5, 4), (6, 5), (7, 6), (8, 7)],
|
||||
[(4, 3), (5, 4), (6, 5), (7, 6), (8, 7), (9, 8)],
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 80)
|
||||
print("Step 2 under SR + PDS (no chord)")
|
||||
print("=" * 80)
|
||||
print(f"{'γ':>3} {'m_1':>4} {'k_2':>4} {'|sd1|':>6} {'|su2|':>6} {'3^γ':>7} "
|
||||
f"{'sat1':>5} {'sat2':>5} {'fwd':>5} {'rev':>5} compat")
|
||||
for γ, m_1, k_2 in PAIRWISE_CASES:
|
||||
r = step2_SR(γ, m_1, k_2)
|
||||
sat1 = "✓" if r['sd1_saturates'] else "·"
|
||||
sat2 = "✓" if r['su2_saturates'] else "·"
|
||||
ok = "YES" if r['compatible'] else "NO"
|
||||
print(f"{γ:>3} {m_1:>4} {k_2:>4} {r['|sd1|']:>6} {r['|su2|']:>6} {r['3^γ']:>7} "
|
||||
f"{sat1:>5} {sat2:>5} {r['fwd']:>5} {r['rev']:>5} {ok}")
|
||||
|
||||
print()
|
||||
print("=" * 80)
|
||||
print("Step 3: chain consistency (3+ tires) under SR + PDS")
|
||||
print("=" * 80)
|
||||
for chain in CHAIN_CASES:
|
||||
adj_ok = all(chain[i][0] == chain[i + 1][1] for i in range(len(chain) - 1))
|
||||
adj = "OK" if adj_ok else "(adjacency mismatch)"
|
||||
if not adj_ok:
|
||||
print(f" SKIP {chain}: {adj}")
|
||||
continue
|
||||
# Use the orientation-checking version
|
||||
r = step3_chain_with_reflections(chain)
|
||||
chain_str = " | ".join(f"({m},{k})" for (m, k) in chain)
|
||||
if r.get('compatible'):
|
||||
print(f" {chain_str}: COMPATIBLE, final state size {r['final_state_size']}, "
|
||||
f"trajectory {r['trajectory']}")
|
||||
else:
|
||||
failed = r.get('failed_at_tire_index', '?')
|
||||
print(f" {chain_str}: **INCOMPATIBLE** (failed at tire index {failed})")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,32 @@
|
||||
================================================================================
|
||||
Step 2 under SR + PDS (no chord)
|
||||
================================================================================
|
||||
γ m_1 k_2 |sd1| |su2| 3^γ sat1 sat2 fwd rev compat
|
||||
3 3 3 27 27 27 ✓ ✓ 27 27 YES
|
||||
4 4 3 81 78 81 ✓ · 78 78 YES
|
||||
4 4 4 81 81 81 ✓ ✓ 81 81 YES
|
||||
5 5 3 243 171 243 ✓ · 171 171 YES
|
||||
5 6 5 243 243 243 ✓ ✓ 243 243 YES
|
||||
6 6 3 729 396 729 ✓ · 396 396 YES
|
||||
6 6 4 729 549 729 ✓ · 549 549 YES
|
||||
6 6 5 729 726 729 ✓ · 726 726 YES
|
||||
6 6 6 729 729 729 ✓ ✓ 729 729 YES
|
||||
8 8 4 6561 2943 6561 ✓ · 2943 2943 YES
|
||||
8 8 6 6561 5601 6561 ✓ · 5601 5601 YES
|
||||
8 10 8 6561 6561 6561 ✓ ✓ 6561 6561 YES
|
||||
10 10 8 59049 53049 59049 ✓ · 53049 53049 YES
|
||||
12 12 6 531441 160503 531441 ✓ · 160503 160503 YES
|
||||
|
||||
================================================================================
|
||||
Step 3: chain consistency (3+ tires) under SR + PDS
|
||||
================================================================================
|
||||
(4,3) | (5,4) | (6,5): COMPATIBLE, final state size 714, trajectory [78, 234, 714]
|
||||
(4,3) | (6,4) | (8,6): COMPATIBLE, final state size 4914, trajectory [78, 540, 4914]
|
||||
(5,4) | (6,5) | (7,6): COMPATIBLE, final state size 2172, trajectory [240, 720, 2172]
|
||||
(6,3) | (8,6) | (10,8): COMPATIBLE, final state size 46074, trajectory [396, 4410, 46074]
|
||||
(4,3) | (5,4) | (5,5): COMPATIBLE, final state size 240, trajectory [78, 234, 240]
|
||||
(4,3) | (4,4) | (5,4): COMPATIBLE, final state size 234, trajectory [78, 78, 234]
|
||||
(3,4) | (3,3): COMPATIBLE, final state size 27, trajectory [27, 27]
|
||||
(4,3) | (5,4) | (6,5) | (7,6): COMPATIBLE, final state size 2160, trajectory [78, 234, 708, 2160]
|
||||
(4,3) | (5,4) | (6,5) | (7,6) | (8,7): COMPATIBLE, final state size 6528, trajectory [78, 234, 714, 2160, 6528]
|
||||
(4,3) | (5,4) | (6,5) | (7,6) | (8,7) | (9,8): COMPATIBLE, final state size 19644, trajectory [78, 234, 714, 2160, 6516, 19644]
|
||||
Reference in New Issue
Block a user