Add bridge switch / bridge-derived level graph; set up exhaustive test
- Define bridge switch (E/O switch whose new same-parity edge is a bridge in its parity subgraph) and bridge-derived level graph in the paper. Note that bridge switches preserve bipartite parity subgraphs, so every bridge-derived level graph is automatically valid. - Discover the E/O-switch relation is directed (irreversible when a switch produces a cross-parity edge); T*_9 reaches an ELG forward but no ELG reaches it, explaining why it is not derived. This rules out a simple switch-invariant characterization. - Bridge orbits are far smaller than full E/O orbits (~10^4 vs ~10^8 for some labellings), making exhaustive search feasible. Each of the 4 open duals has ~150 valid parity partitions; exhaustive bridge-orbit search per partition can decide bridge-derivability conclusively. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
"""Test whether the 4 open Holton-McKay duals are bridge-derived level
|
||||
graphs: reachable from an Even Level Graph by bridge switches (E/O
|
||||
switches whose new same-parity edge is a bridge in its parity subgraph).
|
||||
|
||||
Because bridge switches keep parity subgraphs bipartite, the reachable
|
||||
set is much smaller than the full E/O orbit. We search BACKWARD from the
|
||||
dual: a bridge-switch predecessor of G is G' such that G' -> G is a
|
||||
bridge switch. We look for an ELG (matching the orbit labelling) in the
|
||||
backward bridge-orbit.
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, '/Users/didericis/Code/math-research/papers/'
|
||||
'level_resolutions_of_maximal_planar_graphs/experiments')
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import networkx as nx
|
||||
import time
|
||||
from load_holton_mckay import parse_planar_code
|
||||
from tutte_dual_treecolor import dual_triangulation
|
||||
from test_conjecture import bfs_levels, is_even_level_graph
|
||||
|
||||
|
||||
def sig(G):
|
||||
return frozenset(frozenset(e) for e in G.edges())
|
||||
|
||||
|
||||
def parity_subgraph(G, labels, parity):
|
||||
nodes = [v for v in G.nodes() if labels[v] % 2 == parity]
|
||||
return G.subgraph(nodes)
|
||||
|
||||
|
||||
def edge_is_bridge_in_parity(H, labels, a, b):
|
||||
"""Given triangulation H that CONTAINS edge ab, with a,b same parity:
|
||||
is ab a bridge of its parity subgraph (i.e., not on any cycle)?
|
||||
For cross-parity ab, returns True (enters no parity subgraph)."""
|
||||
if labels[a] % 2 != labels[b] % 2:
|
||||
return True
|
||||
sub = parity_subgraph(H, labels, labels[a] % 2).copy()
|
||||
sub.remove_edge(a, b)
|
||||
return not nx.has_path(sub, a, b)
|
||||
|
||||
|
||||
def forward_bridge_neighbors(G, labels):
|
||||
"""Forward bridge switches from G."""
|
||||
ok, emb = nx.check_planarity(G)
|
||||
if not ok:
|
||||
return
|
||||
for u, v in list(G.edges()):
|
||||
if labels[u] % 2 != labels[v] % 2:
|
||||
continue # only flip same-parity edges
|
||||
f1 = emb.traverse_face(u, v)
|
||||
f2 = emb.traverse_face(v, u)
|
||||
if len(f1) != 3 or len(f2) != 3:
|
||||
continue
|
||||
w = next(a for a in f1 if a != u and a != v)
|
||||
x = next(b for b in f2 if b != u and b != v)
|
||||
if w == x or G.has_edge(w, x):
|
||||
continue
|
||||
Gp = G.copy(); Gp.remove_edge(u, v); Gp.add_edge(w, x)
|
||||
# bridge condition checked on the post-switch state Gp:
|
||||
if not edge_is_bridge_in_parity(Gp, labels, w, x):
|
||||
continue
|
||||
yield Gp
|
||||
|
||||
|
||||
def backward_bridge_neighbors(G, labels):
|
||||
"""States G' with G' -> G a bridge switch. G' = G - uv + wx where uv
|
||||
is an edge of G, wx its diagonal, wx same-parity (it was the flipped
|
||||
edge in G'), and in G' the edge uv (the *new* edge of the switch
|
||||
G'->G) must be a bridge in G'-minus-uv's parity subgraph...
|
||||
|
||||
We must reconstruct: G' has wx (same-parity), flipping wx gives uv.
|
||||
For G'->G to be a *bridge* switch, the NEW edge uv (added to G when
|
||||
going G'->G) must be a bridge in G's parity subgraph if uv is
|
||||
same-parity. So check the bridge condition on uv in (G without uv)."""
|
||||
ok, emb = nx.check_planarity(G)
|
||||
if not ok:
|
||||
return
|
||||
for u, v in list(G.edges()):
|
||||
f1 = emb.traverse_face(u, v)
|
||||
f2 = emb.traverse_face(v, u)
|
||||
if len(f1) != 3 or len(f2) != 3:
|
||||
continue
|
||||
w = next(a for a in f1 if a != u and a != v)
|
||||
x = next(b for b in f2 if b != u and b != v)
|
||||
if w == x or G.has_edge(w, x):
|
||||
continue
|
||||
if labels[w] % 2 != labels[x] % 2:
|
||||
continue # wx must be same-parity to be flippable in G'
|
||||
# Switch G' -> G adds edge uv to the post-state G. Bridge switch
|
||||
# requires uv to be a bridge of its parity subgraph in G:
|
||||
if not edge_is_bridge_in_parity(G, labels, u, v):
|
||||
continue
|
||||
Gp = G.copy(); Gp.remove_edge(u, v); Gp.add_edge(w, x)
|
||||
yield Gp
|
||||
|
||||
|
||||
def search_bridge_derived(G, labels, max_states=2_000_000, time_limit=600):
|
||||
"""Backward bridge-orbit BFS; return an ELG (matching labels) if found."""
|
||||
t0 = time.time()
|
||||
seen = {sig(G)}
|
||||
frontier = [G]
|
||||
rounds = 0
|
||||
while frontier and len(seen) < max_states:
|
||||
if time.time() - t0 > time_limit:
|
||||
return None, len(seen), rounds, 'timeout'
|
||||
new = []
|
||||
for H in frontier:
|
||||
for s in H.nodes():
|
||||
ok, lvls = is_even_level_graph(H, frozenset({s}))
|
||||
if not ok:
|
||||
continue
|
||||
same = all(lvls[u] % 2 == labels[u] % 2 for u in H.nodes())
|
||||
opp = all(lvls[u] % 2 != labels[u] % 2 for u in H.nodes())
|
||||
if same or opp:
|
||||
return H, len(seen), rounds, 'found'
|
||||
for Hp in backward_bridge_neighbors(H, labels):
|
||||
sg = sig(Hp)
|
||||
if sg not in seen:
|
||||
seen.add(sg); new.append(Hp)
|
||||
frontier = new
|
||||
rounds += 1
|
||||
return None, len(seen), rounds, ('exhausted' if not frontier else 'capped')
|
||||
|
||||
|
||||
def main():
|
||||
graphs = parse_planar_code('experiments/nonham38m4.pc')
|
||||
for i in [0, 3, 4, 5]:
|
||||
G, _ = dual_triangulation(graphs[i][0])
|
||||
print(f'=== dual {i} ===')
|
||||
found = False
|
||||
for src in list(G.nodes()):
|
||||
labels = bfs_levels(G, frozenset({src}))
|
||||
result, n_states, rnds, status = search_bridge_derived(
|
||||
G, labels, max_states=500000, time_limit=120)
|
||||
tag = 'FOUND ELG' if result is not None else 'none'
|
||||
print(f' src={src}: {tag} ({status}, {n_states} states, {rnds} rounds)')
|
||||
if result is not None:
|
||||
found = True
|
||||
break
|
||||
print(f' dual {i}: bridge-derived = {found}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,125 @@
|
||||
"""Verify the directedness of the E/O-switch relation, and its effect on
|
||||
'derived'. Compute, for T*_9 (n=9, known NOT derived) with a fixed
|
||||
labelling:
|
||||
- forward orbit: states reachable FROM T*_9 (T*_9 ->* H)
|
||||
- backward orbit: states that reach T*_9 (H ->* T*_9)
|
||||
and check each for an Even Level Graph.
|
||||
|
||||
'Derived' (directed) = some ELG reaches G = ELG in backward orbit.
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, '/Users/didericis/Code/math-research/papers/'
|
||||
'level_resolutions_of_maximal_planar_graphs/experiments')
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import networkx as nx
|
||||
from triangulation_gen import enumerate_all_triangulations
|
||||
from test_conjecture import bfs_levels, is_even_level_graph
|
||||
|
||||
|
||||
def sig(G):
|
||||
return frozenset(frozenset(e) for e in G.edges())
|
||||
|
||||
|
||||
def forward_neighbors(G, labels):
|
||||
ok, emb = nx.check_planarity(G)
|
||||
if not ok:
|
||||
return
|
||||
for u, v in list(G.edges()):
|
||||
if labels[u] % 2 != labels[v] % 2:
|
||||
continue
|
||||
f1 = emb.traverse_face(u, v)
|
||||
f2 = emb.traverse_face(v, u)
|
||||
if len(f1) != 3 or len(f2) != 3:
|
||||
continue
|
||||
w = next(x for x in f1 if x != u and x != v)
|
||||
x = next(y for y in f2 if y != u and y != v)
|
||||
if w == x or G.has_edge(w, x):
|
||||
continue
|
||||
Gp = G.copy(); Gp.remove_edge(u, v); Gp.add_edge(w, x)
|
||||
yield Gp
|
||||
|
||||
|
||||
def backward_neighbors(G, labels):
|
||||
"""States G' with G' ->forward G. G' = G - f + diag_G(f) for each edge
|
||||
f of G whose diagonal is same-parity (so flipping it in G' is valid)."""
|
||||
ok, emb = nx.check_planarity(G)
|
||||
if not ok:
|
||||
return
|
||||
for u, v in list(G.edges()):
|
||||
f1 = emb.traverse_face(u, v)
|
||||
f2 = emb.traverse_face(v, u)
|
||||
if len(f1) != 3 or len(f2) != 3:
|
||||
continue
|
||||
w = next(x for x in f1 if x != u and x != v)
|
||||
x = next(y for y in f2 if y != u and y != v)
|
||||
if w == x or G.has_edge(w, x):
|
||||
continue
|
||||
# diagonal of edge uv is wx; predecessor un-flips: remove uv? No.
|
||||
# Predecessor G' has edge wx (same-parity), flipping wx gives uv...
|
||||
# We want G = G' - (wx) + (uv). So G' = G - uv + wx, and the flipped
|
||||
# edge in G' is wx which must be same-parity.
|
||||
if labels[w] % 2 != labels[x] % 2:
|
||||
continue
|
||||
Gp = G.copy(); Gp.remove_edge(u, v); Gp.add_edge(w, x)
|
||||
yield Gp
|
||||
|
||||
|
||||
def orbit(G_start, labels, neighbor_fn, max_states=300000):
|
||||
seen = {sig(G_start): G_start}
|
||||
frontier = [G_start]
|
||||
while frontier and len(seen) < max_states:
|
||||
new = []
|
||||
for H in frontier:
|
||||
for Hp in neighbor_fn(H, labels):
|
||||
s = sig(Hp)
|
||||
if s not in seen:
|
||||
seen[s] = Hp
|
||||
new.append(Hp)
|
||||
frontier = new
|
||||
return list(seen.values())
|
||||
|
||||
|
||||
def has_elg(orbit_states, labels):
|
||||
"""Find an Even Level Graph in the orbit whose BFS-parity (from some
|
||||
source) MATCHES the orbit labelling (up to global parity swap).
|
||||
Only such a graph is a valid 'derived from this ELG' witness."""
|
||||
for H in orbit_states:
|
||||
for s in H.nodes():
|
||||
ok, lvls = is_even_level_graph(H, frozenset({s}))
|
||||
if not ok:
|
||||
continue
|
||||
same = all(lvls[u] % 2 == labels[u] % 2 for u in H.nodes())
|
||||
opp = all(lvls[u] % 2 != labels[u] % 2 for u in H.nodes())
|
||||
if same or opp:
|
||||
return True, H, s
|
||||
return False, None, None
|
||||
|
||||
|
||||
def main():
|
||||
tris = enumerate_all_triangulations(9)
|
||||
Tstar = next(G for G in tris
|
||||
if sorted([G.degree(v) for v in G.nodes()], reverse=True)
|
||||
== [5, 5, 5, 5, 5, 5, 4, 4, 4])
|
||||
|
||||
src = 0
|
||||
labels = bfs_levels(Tstar, frozenset({src}))
|
||||
print(f'T*_9, labelling = BFS parity from source {src}')
|
||||
|
||||
fwd = orbit(Tstar, labels, forward_neighbors)
|
||||
f_elg, _, _ = has_elg(fwd, labels)
|
||||
print(f' FORWARD orbit (T*_9 ->* H): size {len(fwd)}, contains ELG: {f_elg}')
|
||||
|
||||
bwd = orbit(Tstar, labels, backward_neighbors)
|
||||
b_elg, He, se = has_elg(bwd, labels)
|
||||
print(f' BACKWARD orbit (H ->* T*_9): size {len(bwd)}, contains ELG: {b_elg}')
|
||||
if b_elg:
|
||||
print(f' -> ELG predecessor exists (src {se}); T*_9 WOULD be derived')
|
||||
else:
|
||||
print(f' -> no ELG reaches T*_9; T*_9 is NOT derived (directed)')
|
||||
|
||||
print(f'\nConclusion: directedness matters = {f_elg != b_elg}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,143 @@
|
||||
"""Exhaustive bridge-derived test for the 4 open Holton-McKay duals.
|
||||
|
||||
Step A (gauge): for each dual, count the valid parity partitions
|
||||
(bipartitions L with both parity subgraphs bipartite) and measure a few
|
||||
bridge-orbit sizes run to FULL exhaustion (no timeout).
|
||||
|
||||
Step B (decide): for each dual, for every valid parity partition L,
|
||||
exhaust the backward bridge-orbit and look for an ELG with BFS-parity L.
|
||||
YES if found for any L; NO (conclusive) if none found for all L.
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, '/Users/didericis/Code/math-research/papers/'
|
||||
'level_resolutions_of_maximal_planar_graphs/experiments')
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import networkx as nx
|
||||
import time
|
||||
from load_holton_mckay import parse_planar_code
|
||||
from tutte_dual_treecolor import dual_triangulation
|
||||
from test_conjecture import is_even_level_graph
|
||||
from bridge_derived_test import (
|
||||
sig, parity_subgraph, edge_is_bridge_in_parity,
|
||||
backward_bridge_neighbors,
|
||||
)
|
||||
|
||||
|
||||
def valid_parity_partitions(G):
|
||||
"""Yield labels-dicts for every bipartition (fix node 0 in even class)
|
||||
such that both induced parity subgraphs are bipartite. Labels are 0
|
||||
(even) / 1 (odd)."""
|
||||
nodes = sorted(G.nodes())
|
||||
n = len(nodes)
|
||||
assert nodes[0] == 0
|
||||
for mask in range(2 ** (n - 1)):
|
||||
labels = {0: 0}
|
||||
for i in range(n - 1):
|
||||
labels[nodes[i + 1]] = (mask >> i) & 1
|
||||
even = [v for v in nodes if labels[v] == 0]
|
||||
odd = [v for v in nodes if labels[v] == 1]
|
||||
if not odd:
|
||||
continue
|
||||
if nx.is_bipartite(G.subgraph(even)) and nx.is_bipartite(G.subgraph(odd)):
|
||||
yield labels
|
||||
|
||||
|
||||
def is_elg_with_parity(H, labels):
|
||||
"""Is H an Even Level Graph for some source whose BFS-parity matches
|
||||
`labels` (up to global swap)? Only even-class vertices can be the
|
||||
source (level 0 is even)."""
|
||||
even = [v for v in H.nodes() if labels[v] == 0]
|
||||
odd_set = set(v for v in H.nodes() if labels[v] == 1)
|
||||
for s in even:
|
||||
# quick reject: all neighbours of s must be odd-class (level 1)
|
||||
if any(nb not in odd_set for nb in H.neighbors(s)):
|
||||
# could still match under global swap; handle swap separately
|
||||
pass
|
||||
ok, lvls = is_even_level_graph(H, frozenset({s}))
|
||||
if not ok:
|
||||
continue
|
||||
if all(lvls[u] % 2 == labels[u] for u in H.nodes()):
|
||||
return True
|
||||
# global swap: source in odd class, BFS-parity is complement of labels
|
||||
odd = [v for v in H.nodes() if labels[v] == 1]
|
||||
for s in odd:
|
||||
ok, lvls = is_even_level_graph(H, frozenset({s}))
|
||||
if not ok:
|
||||
continue
|
||||
if all(lvls[u] % 2 != labels[u] for u in H.nodes()):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def exhaust_bridge_orbit_for_elg(G, labels, cap=3_000_000):
|
||||
"""Exhaust backward bridge-orbit (no ELG check during BFS), then scan
|
||||
for an ELG witness. Returns ('found',H) / ('exhausted',size) /
|
||||
('capped',size)."""
|
||||
seen = {sig(G): G}
|
||||
frontier = [G]
|
||||
while frontier and len(seen) < cap:
|
||||
new = []
|
||||
for H in frontier:
|
||||
for Hp in backward_bridge_neighbors(H, labels):
|
||||
sg = sig(Hp)
|
||||
if sg not in seen:
|
||||
seen[sg] = Hp
|
||||
new.append(Hp)
|
||||
frontier = new
|
||||
status = 'capped' if frontier else 'exhausted'
|
||||
if status == 'exhausted':
|
||||
for H in seen.values():
|
||||
if is_elg_with_parity(H, labels):
|
||||
return 'found', H
|
||||
return status, len(seen)
|
||||
|
||||
|
||||
def decide_dual(i, cap=3_000_000, log=print):
|
||||
graphs = parse_planar_code('experiments/nonham38m4.pc')
|
||||
G, _ = dual_triangulation(graphs[i][0])
|
||||
parts = list(valid_parity_partitions(G))
|
||||
log(f'dual {i}: {len(parts)} valid parity partitions')
|
||||
any_capped = False
|
||||
max_orbit = 0
|
||||
t0 = time.time()
|
||||
for j, labels in enumerate(parts):
|
||||
st, info = exhaust_bridge_orbit_for_elg(G, labels, cap=cap)
|
||||
if st == 'found':
|
||||
log(f' partition {j}: FOUND ELG -> dual {i} IS bridge-derived '
|
||||
f'({time.time()-t0:.0f}s)')
|
||||
return 'bridge-derived'
|
||||
if st == 'capped':
|
||||
any_capped = True
|
||||
log(f' partition {j}: orbit exceeded cap ({info}); inconclusive')
|
||||
else:
|
||||
max_orbit = max(max_orbit, info)
|
||||
if (j + 1) % 25 == 0:
|
||||
log(f' ...{j+1}/{len(parts)} partitions, max orbit {max_orbit}, '
|
||||
f'{time.time()-t0:.0f}s')
|
||||
if any_capped:
|
||||
log(f' dual {i}: no witness, but some orbits hit cap -> INCONCLUSIVE '
|
||||
f'({time.time()-t0:.0f}s)')
|
||||
return 'inconclusive'
|
||||
log(f' dual {i}: NOT bridge-derived (all {len(parts)} orbits exhausted, '
|
||||
f'max orbit {max_orbit}, {time.time()-t0:.0f}s)')
|
||||
return 'not-bridge-derived'
|
||||
|
||||
|
||||
def gauge(dual_indices):
|
||||
graphs = parse_planar_code('experiments/nonham38m4.pc')
|
||||
for i in dual_indices:
|
||||
G, _ = dual_triangulation(graphs[i][0])
|
||||
t0 = time.time()
|
||||
parts = list(valid_parity_partitions(G))
|
||||
print(f'dual {i}: {len(parts)} valid parity partitions '
|
||||
f'(enumerated in {time.time()-t0:.1f}s)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys as _s
|
||||
if len(_s.argv) > 1 and _s.argv[1] == 'gauge':
|
||||
gauge([0, 3, 4, 5])
|
||||
elif len(_s.argv) > 1:
|
||||
for idx in _s.argv[1:]:
|
||||
decide_dual(int(idx))
|
||||
@@ -0,0 +1,128 @@
|
||||
"""Hunt for a structural invariant of E/O-switch orbits that separates
|
||||
'derived from an ELG' from 'not derived'.
|
||||
|
||||
Strategy: at n=9, T*_9 is NOT a derived level graph (it is intertwining-
|
||||
only). Compare its E/O orbit (under a fixed labelling) against the orbit
|
||||
of a graph that IS derived. Look for an invariant constant on orbits
|
||||
that differs between the two.
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, '/Users/didericis/Code/math-research/papers/'
|
||||
'level_resolutions_of_maximal_planar_graphs/experiments')
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import networkx as nx
|
||||
from triangulation_gen import enumerate_all_triangulations
|
||||
from test_conjecture import canonical_sig, bfs_levels, is_even_level_graph
|
||||
|
||||
|
||||
def eo_switch_neighbors(G, labels):
|
||||
"""Yield all triangulations reachable from G by one E/O switch
|
||||
(flip a same-parity edge). Directed: this is the FORWARD relation."""
|
||||
ok, emb = nx.check_planarity(G)
|
||||
if not ok:
|
||||
return
|
||||
for u, v in list(G.edges()):
|
||||
if labels[u] % 2 != labels[v] % 2:
|
||||
continue
|
||||
f1 = emb.traverse_face(u, v)
|
||||
f2 = emb.traverse_face(v, u)
|
||||
if len(f1) != 3 or len(f2) != 3:
|
||||
continue
|
||||
w = next(x for x in f1 if x != u and x != v)
|
||||
x = next(y for y in f2 if y != u and y != v)
|
||||
if w == x or G.has_edge(w, x):
|
||||
continue
|
||||
Gp = G.copy()
|
||||
Gp.remove_edge(u, v)
|
||||
Gp.add_edge(w, x)
|
||||
yield Gp
|
||||
|
||||
|
||||
def undirected_orbit(G_start, labels, max_states=200000):
|
||||
"""Connected component of G_start in the UNDIRECTED switch graph
|
||||
(treat each switch as bidirectional by also exploring forward from
|
||||
every reached state -- since a forward switch's reverse is itself a
|
||||
forward switch from the target when the new edge is same-parity, and
|
||||
we explore all forward edges from every node, the BFS closure equals
|
||||
the weakly-connected component)."""
|
||||
sig0 = frozenset(frozenset(e) for e in G_start.edges())
|
||||
seen = {sig0: G_start}
|
||||
frontier = [G_start]
|
||||
while frontier and len(seen) < max_states:
|
||||
new = []
|
||||
for H in frontier:
|
||||
for Hp in eo_switch_neighbors(H, labels):
|
||||
sig = frozenset(frozenset(e) for e in Hp.edges())
|
||||
if sig not in seen:
|
||||
seen[sig] = Hp
|
||||
new.append(Hp)
|
||||
frontier = new
|
||||
return list(seen.values())
|
||||
|
||||
|
||||
def parity_subgraph_edge_counts(G, labels):
|
||||
even = [v for v in G.nodes() if labels[v] % 2 == 0]
|
||||
odd = [v for v in G.nodes() if labels[v] % 2 == 1]
|
||||
e_even = G.subgraph(even).number_of_edges()
|
||||
e_odd = G.subgraph(odd).number_of_edges()
|
||||
cross = G.number_of_edges() - e_even - e_odd
|
||||
return e_even, e_odd, cross
|
||||
|
||||
|
||||
def orbit_report(G, labels, name):
|
||||
orbit = undirected_orbit(G, labels)
|
||||
has_elg = any(
|
||||
any(is_even_level_graph(H, frozenset({s}))[0] for s in H.nodes())
|
||||
for H in orbit
|
||||
)
|
||||
# Invariant candidates over the orbit
|
||||
ec = set()
|
||||
for H in orbit:
|
||||
ec.add(parity_subgraph_edge_counts(H, labels))
|
||||
print(f'{name}: orbit size {len(orbit)}, contains ELG: {has_elg}')
|
||||
print(f' (e_even, e_odd, cross) values in orbit: {sorted(ec)}')
|
||||
return orbit, has_elg
|
||||
|
||||
|
||||
def main():
|
||||
tris = enumerate_all_triangulations(9)
|
||||
# T*_9 is the iso class that is NOT derived (intertwining-only).
|
||||
# We earlier identified it as iso index 49 via canonical_sig matching
|
||||
# the degree sequence (5,5,5,5,5,5,4,4,4).
|
||||
Tstar = None
|
||||
for G in tris:
|
||||
if sorted([G.degree(v) for v in G.nodes()], reverse=True) == \
|
||||
[5, 5, 5, 5, 5, 4, 4, 4, 4]:
|
||||
pass
|
||||
for G in tris:
|
||||
ds = sorted([G.degree(v) for v in G.nodes()], reverse=True)
|
||||
if ds == [5, 5, 5, 5, 5, 5, 4, 4, 4]:
|
||||
Tstar = G
|
||||
break
|
||||
print(f'T*_9 degree seq: '
|
||||
f'{sorted([Tstar.degree(v) for v in Tstar.nodes()], reverse=True)}')
|
||||
|
||||
# Pick a labelling of T*_9: a valid parity partition (bipartite parity
|
||||
# subgraphs). Use one we know: V_E={0,1,3,6}? But labels here come from
|
||||
# the enumerate ordering. Instead, search labellings = BFS parities.
|
||||
print('\n--- T*_9 (NOT derived) orbits under BFS-source labellings ---')
|
||||
for s in list(Tstar.nodes()):
|
||||
labels = bfs_levels(Tstar, frozenset({s}))
|
||||
orbit, has_elg = orbit_report(Tstar, labels, f'T*_9 src={s}')
|
||||
if has_elg:
|
||||
print(' ^ unexpectedly found ELG')
|
||||
|
||||
# A derived graph: pick one that is an ELG itself.
|
||||
print('\n--- A derived graph (an ELG) for comparison ---')
|
||||
for G in tris:
|
||||
elg_src = next((s for s in G.nodes()
|
||||
if is_even_level_graph(G, frozenset({s}))[0]), None)
|
||||
if elg_src is not None:
|
||||
labels = bfs_levels(G, frozenset({elg_src}))
|
||||
orbit_report(G, labels, f'derived(ELG) src={elg_src}')
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -34,18 +34,20 @@
|
||||
\newlabel{sec:even-level-graphs}{{4}{3}{Even Level Graphs}{section.4}{}}
|
||||
\newlabel{def:even-level-graph}{{4.1}{3}{Even Level Graph}{theorem.4.1}{}}
|
||||
\newlabel{thm:even-level-4colorable}{{4.2}{3}{}{theorem.4.2}{}}
|
||||
\citation{holton-mckay}
|
||||
\newlabel{def:derived-level-graph}{{4.3}{4}{Derived level graph}{theorem.4.3}{}}
|
||||
\newlabel{def:intertwining-tree}{{4.4}{4}{Intertwining tree}{theorem.4.4}{}}
|
||||
\newlabel{thm:intertwining-iff-hamiltonian-dual}{{4.5}{4}{}{theorem.4.5}{}}
|
||||
\newlabel{conj:every-triangulation-derived}{{4.6}{4}{}{theorem.4.6}{}}
|
||||
\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Empirical status}}{4}{section*.1}\protected@file@percent }
|
||||
\newlabel{def:bridge-switch}{{4.4}{4}{Bridge switch}{theorem.4.4}{}}
|
||||
\newlabel{def:bridge-derived-level-graph}{{4.5}{4}{Bridge-derived level graph}{theorem.4.5}{}}
|
||||
\newlabel{def:intertwining-tree}{{4.6}{4}{Intertwining tree}{theorem.4.6}{}}
|
||||
\newlabel{thm:intertwining-iff-hamiltonian-dual}{{4.7}{4}{}{theorem.4.7}{}}
|
||||
\citation{holton-mckay}
|
||||
\bibcite{holton-mckay}{1}
|
||||
\newlabel{conj:every-triangulation-derived}{{4.8}{5}{}{theorem.4.8}{}}
|
||||
\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Empirical status}}{5}{section*.1}\protected@file@percent }
|
||||
\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{The boundary case $n = 21$}}{5}{section*.2}\protected@file@percent }
|
||||
\newlabel{tocindent-1}{0pt}
|
||||
\newlabel{tocindent0}{14.69437pt}
|
||||
\newlabel{tocindent1}{17.77782pt}
|
||||
\newlabel{tocindent2}{0pt}
|
||||
\newlabel{tocindent3}{0pt}
|
||||
\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{The boundary case $n = 21$}}{5}{section*.2}\protected@file@percent }
|
||||
\@writefile{toc}{\contentsline {section}{\tocsection {}{}{References}}{5}{section*.3}\protected@file@percent }
|
||||
\gdef \@abspage@last{5}
|
||||
\@writefile{toc}{\contentsline {section}{\tocsection {}{}{References}}{6}{section*.3}\protected@file@percent }
|
||||
\gdef \@abspage@last{6}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 21 MAY 2026 20:44
|
||||
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 21 MAY 2026 23:45
|
||||
entering extended mode
|
||||
restricted \write18 enabled.
|
||||
%&-line parsing enabled.
|
||||
@@ -387,24 +387,24 @@ LaTeX Warning: `h' float specifier changed to `ht'.
|
||||
ng>] [4]
|
||||
|
||||
Package hyperref Warning: Token not allowed in a PDF string (Unicode):
|
||||
(hyperref) removing `math shift' on input line 324.
|
||||
(hyperref) removing `math shift' on input line 355.
|
||||
|
||||
|
||||
Package hyperref Warning: Token not allowed in a PDF string (Unicode):
|
||||
(hyperref) removing `math shift' on input line 324.
|
||||
(hyperref) removing `math shift' on input line 355.
|
||||
|
||||
[5] (./paper.aux)
|
||||
[5] [6] (./paper.aux)
|
||||
Package rerunfilecheck Info: File `paper.out' has not changed.
|
||||
(rerunfilecheck) Checksum: AECCB746CF11915BCB68F1E7FF8075A7;1047.
|
||||
)
|
||||
Here is how much of TeX's memory you used:
|
||||
9737 strings out of 478268
|
||||
150746 string characters out of 5846347
|
||||
453840 words of memory out of 5000000
|
||||
27645 multiletter control sequences out of 15000+600000
|
||||
9742 strings out of 478268
|
||||
150825 string characters out of 5846347
|
||||
454933 words of memory out of 5000000
|
||||
27647 multiletter control sequences out of 15000+600000
|
||||
475666 words of font info for 53 fonts, out of 8000000 for 9000
|
||||
1302 hyphenation exceptions out of 8191
|
||||
69i,8n,76p,781b,427s stack positions out of 10000i,1000n,20000p,200000b,200000s
|
||||
69i,8n,76p,781b,504s stack positions out of 10000i,1000n,20000p,200000b,200000s
|
||||
</usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx10.pfb
|
||||
></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmcsc10.pfb
|
||||
></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmex10.pfb>
|
||||
@@ -418,10 +418,10 @@ texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb></usr/local/te
|
||||
xlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb></usr/local/tex
|
||||
live/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti8.pfb></usr/local/texli
|
||||
ve/2022/texmf-dist/fonts/type1/public/amsfonts/symbols/msam10.pfb>
|
||||
Output written on paper.pdf (5 pages, 545810 bytes).
|
||||
Output written on paper.pdf (6 pages, 548304 bytes).
|
||||
PDF statistics:
|
||||
162 PDF objects out of 1000 (max. 8388607)
|
||||
119 compressed objects within 2 object streams
|
||||
30 named destinations out of 1000 (max. 500000)
|
||||
171 PDF objects out of 1000 (max. 8388607)
|
||||
127 compressed objects within 2 object streams
|
||||
33 named destinations out of 1000 (max. 500000)
|
||||
77 words of extra memory for PDF output out of 10000 (max. 10000000)
|
||||
|
||||
|
||||
Binary file not shown.
@@ -250,6 +250,37 @@ A derived level graph $G'$ is \emph{valid} if both $E_{G,S}(G')$ and
|
||||
$O_{G,S}(G')$ contain only even cycles.
|
||||
\end{definition}
|
||||
|
||||
\begin{definition}[Bridge switch]
|
||||
\label{def:bridge-switch}
|
||||
Let $G'$ be a triangulation reached from an Even Level Graph $G$, with
|
||||
parity classes inherited from $G$ as in
|
||||
Definition~\ref{def:derived-level-graph}. An edge switch on an edge
|
||||
$e \in E \cup O$ of $G'$, replacing $uvw, uvx$ by the edge $wx$, is a
|
||||
\emph{bridge switch} if either
|
||||
\begin{itemize}
|
||||
\item the new edge $wx$ is a cross-parity edge (one endpoint even, the
|
||||
other odd), so $wx$ enters neither parity subgraph; or
|
||||
\item $wx$ is a same-parity edge and is a \emph{bridge} in the parity
|
||||
subgraph it joins -- that is, $w$ and $x$ lie in different connected
|
||||
components of that parity subgraph, so adding $wx$ creates no new cycle.
|
||||
\end{itemize}
|
||||
\end{definition}
|
||||
|
||||
\begin{definition}[Bridge-derived level graph]
|
||||
\label{def:bridge-derived-level-graph}
|
||||
A \emph{bridge-derived level graph} of an Even Level Graph $G$ is a
|
||||
triangulation obtained from $G$ by a sequence of bridge switches
|
||||
(Definition~\ref{def:bridge-switch}).
|
||||
\end{definition}
|
||||
|
||||
Because a bridge switch never closes a cycle in a parity subgraph, it
|
||||
never introduces an odd cycle there. As an Even Level Graph has
|
||||
bipartite parity subgraphs (every level cycle is even), every
|
||||
bridge-derived level graph has bipartite parity subgraphs as well, and
|
||||
so is automatically a valid derived level graph. Equivalently, the
|
||||
first Betti number of each parity subgraph is non-increasing along any
|
||||
sequence of bridge switches.
|
||||
|
||||
\begin{definition}[Intertwining tree]
|
||||
\label{def:intertwining-tree}
|
||||
A maximal planar graph $G$ is an \emph{intertwining tree} if its
|
||||
|
||||
Reference in New Issue
Block a user