FA-88526 / Mesh topology invariants / Open access
Boundary edge count is not refined · case 01
Open meshes report their original boundary edge count after subdivision.
ROOT CAUSE
The boundary counter is never doubled per level.
VERIFIED REPAIR
Double B on every level for both schemes.
Unsuccessful approach: Doubling only inside the Loop branch forgets Catmull-Clark boundaries.
Case contract
Input [V,E,arities,B,scheme,levels]: counts of a mesh with face sizes "arities" and B boundary edges. Loop (triangles only, else "non-triangle"): V+=E, E=2E+3F, F=4F. Catmull-Clark: V=V+E+F, E=2E+sum(arities), faces become sum(arities) quads. Boundary edges double per level. Return [V,E,F,B,V-E+F] after the given levels.
Why this case matters
Mesh processing pipelines (remeshing, export, simulation, printing) trust these topological counts and adjacency answers; a wrong invariant silently accepts broken meshes or rejects valid ones.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
V,E,ar,B,scheme,levels=x
ar=list(ar)
for _ in range(levels):
F=len(ar)
S=sum(ar)
if scheme=='loop':
if any(n!=3 for n in ar): return 'non-triangle'
V,E,ar=V+E,2*E+3*F,[3]*(4*F)
else:
V,E,ar=V+E+F,2*E+S,[4]*S
return [V,E,len(ar),B,V-E+len(ar)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['tetra loop 1', [[4, 6, [3, 3, 3, 3], 0, 'loop', 1]], [10, 24, 16, 0, 2]], ['tetra loop 2', [[4, 6, [3, 3, 3, 3], 0, 'loop', 2]], [34, 96, 64, 0, 2]], ['disk loop', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 1]], [16, 35, 20, 10, 1]], ['disk loop 2', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 2]], [51, 130, 80, 20, 1]], ['cube cc', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 1]], [26, 48, 24, 0, 2]], ['cube cc 2', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 2]], [98, 192, 96, 0, 2]], ['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]]], [['disk loop 2', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 2]], [51, 130, 80, 20, 1]], ['cube cc', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 1]], [26, 48, 24, 0, 2]], ['cube cc 2', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 2]], [98, 192, 96, 0, 2]], ['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]], ['quad and triangle cc', [[5, 6, [4, 3], 4, 'cc', 1]], [13, 19, 7, 8, 1]], ['quad and triangle cc 2', [[5, 6, [4, 3], 4, 'cc', 2]], [39, 66, 28, 16, 1]], ['pentagon cc', [[5, 5, [5], 5, 'cc', 1]], [11, 15, 5, 10, 1]]], [['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]], ['quad and triangle cc', [[5, 6, [4, 3], 4, 'cc', 1]], [13, 19, 7, 8, 1]], ['quad and triangle cc 2', [[5, 6, [4, 3], 4, 'cc', 2]], [39, 66, 28, 16, 1]], ['pentagon cc', [[5, 5, [5], 5, 'cc', 1]], [11, 15, 5, 10, 1]], ['quad under loop', [[4, 4, [4], 4, 'loop', 1]], 'non-triangle'], ['triangle then quad under loop', [[5, 6, [3, 4], 4, 'loop', 1]], 'non-triangle'], ['zero levels', [[4, 6, [3, 3, 3, 3], 0, 'loop', 0]], [4, 6, 4, 0, 2]]], [['quad and triangle cc', [[5, 6, [4, 3], 4, 'cc', 1]], [13, 19, 7, 8, 1]], ['pentagon cc', [[5, 5, [5], 5, 'cc', 1]], [11, 15, 5, 10, 1]], ['triangle then quad under loop', [[5, 6, [3, 4], 4, 'loop', 1]], 'non-triangle'], ['zero levels', [[4, 6, [3, 3, 3, 3], 0, 'loop', 0]], [4, 6, 4, 0, 2]], ['torus cc', [[9, 18, [4, 4, 4, 4, 4, 4, 4, 4, 4], 0, 'cc', 1]], [36, 72, 36, 0, 0]], ['triangle tetra cc', [[4, 6, [3, 3, 3, 3], 0, 'cc', 1]], [14, 24, 12, 0, 2]], ['single triangle loop 3', [[3, 3, [3], 3, 'loop', 3]], [45, 108, 64, 24, 1]]], [['tetra loop 1', [[4, 6, [3, 3, 3, 3], 0, 'loop', 1]], [10, 24, 16, 0, 2]], ['tetra loop 2', [[4, 6, [3, 3, 3, 3], 0, 'loop', 2]], [34, 96, 64, 0, 2]], ['disk loop', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 1]], [16, 35, 20, 10, 1]], ['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]], ['quad and triangle cc 2', [[5, 6, [4, 3], 4, 'cc', 2]], [39, 66, 28, 16, 1]], ['triangle tetra cc', [[4, 6, [3, 3, 3, 3], 0, 'cc', 1]], [14, 24, 12, 0, 2]], ['single triangle loop 3', [[3, 3, [3], 3, 'loop', 3]], [45, 108, 64, 24, 1]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| tetra loop 1 | [10, 24, 16, 0, 2] | [10, 24, 16, 0, 2] | Passed |
| tetra loop 2 | [34, 96, 64, 0, 2] | [34, 96, 64, 0, 2] | Passed |
| disk loop | [16, 35, 20, 5, 1] | [16, 35, 20, 10, 1] | Failed |
| disk loop 2 | [51, 130, 80, 5, 1] | [51, 130, 80, 20, 1] | Failed |
| cube cc | [26, 48, 24, 0, 2] | [26, 48, 24, 0, 2] | Passed |
| cube cc 2 | [98, 192, 96, 0, 2] | [98, 192, 96, 0, 2] | Passed |
| single quad cc | [25, 40, 16, 4, 1] | [25, 40, 16, 16, 1] | Failed |
SHA-256 / 6d943457acc89ca5d63a640d4c9cb6eb4021e755db3af8eeac82a6c597fa2f77
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
V,E,ar,B,scheme,levels=x
ar=list(ar)
for _ in range(levels):
F=len(ar)
S=sum(ar)
if scheme=='loop':
if any(n!=3 for n in ar): return 'non-triangle'
V,E,ar=V+E,2*E+3*F,[3]*(4*F)
B=2*B
else:
V,E,ar=V+E+F,2*E+S,[4]*S
return [V,E,len(ar),B,V-E+len(ar)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['tetra loop 1', [[4, 6, [3, 3, 3, 3], 0, 'loop', 1]], [10, 24, 16, 0, 2]], ['tetra loop 2', [[4, 6, [3, 3, 3, 3], 0, 'loop', 2]], [34, 96, 64, 0, 2]], ['disk loop', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 1]], [16, 35, 20, 10, 1]], ['disk loop 2', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 2]], [51, 130, 80, 20, 1]], ['cube cc', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 1]], [26, 48, 24, 0, 2]], ['cube cc 2', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 2]], [98, 192, 96, 0, 2]], ['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]]], [['disk loop 2', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 2]], [51, 130, 80, 20, 1]], ['cube cc', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 1]], [26, 48, 24, 0, 2]], ['cube cc 2', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 2]], [98, 192, 96, 0, 2]], ['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]], ['quad and triangle cc', [[5, 6, [4, 3], 4, 'cc', 1]], [13, 19, 7, 8, 1]], ['quad and triangle cc 2', [[5, 6, [4, 3], 4, 'cc', 2]], [39, 66, 28, 16, 1]], ['pentagon cc', [[5, 5, [5], 5, 'cc', 1]], [11, 15, 5, 10, 1]]], [['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]], ['quad and triangle cc', [[5, 6, [4, 3], 4, 'cc', 1]], [13, 19, 7, 8, 1]], ['quad and triangle cc 2', [[5, 6, [4, 3], 4, 'cc', 2]], [39, 66, 28, 16, 1]], ['pentagon cc', [[5, 5, [5], 5, 'cc', 1]], [11, 15, 5, 10, 1]], ['quad under loop', [[4, 4, [4], 4, 'loop', 1]], 'non-triangle'], ['triangle then quad under loop', [[5, 6, [3, 4], 4, 'loop', 1]], 'non-triangle'], ['zero levels', [[4, 6, [3, 3, 3, 3], 0, 'loop', 0]], [4, 6, 4, 0, 2]]], [['quad and triangle cc', [[5, 6, [4, 3], 4, 'cc', 1]], [13, 19, 7, 8, 1]], ['pentagon cc', [[5, 5, [5], 5, 'cc', 1]], [11, 15, 5, 10, 1]], ['triangle then quad under loop', [[5, 6, [3, 4], 4, 'loop', 1]], 'non-triangle'], ['zero levels', [[4, 6, [3, 3, 3, 3], 0, 'loop', 0]], [4, 6, 4, 0, 2]], ['torus cc', [[9, 18, [4, 4, 4, 4, 4, 4, 4, 4, 4], 0, 'cc', 1]], [36, 72, 36, 0, 0]], ['triangle tetra cc', [[4, 6, [3, 3, 3, 3], 0, 'cc', 1]], [14, 24, 12, 0, 2]], ['single triangle loop 3', [[3, 3, [3], 3, 'loop', 3]], [45, 108, 64, 24, 1]]], [['tetra loop 1', [[4, 6, [3, 3, 3, 3], 0, 'loop', 1]], [10, 24, 16, 0, 2]], ['tetra loop 2', [[4, 6, [3, 3, 3, 3], 0, 'loop', 2]], [34, 96, 64, 0, 2]], ['disk loop', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 1]], [16, 35, 20, 10, 1]], ['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]], ['quad and triangle cc 2', [[5, 6, [4, 3], 4, 'cc', 2]], [39, 66, 28, 16, 1]], ['triangle tetra cc', [[4, 6, [3, 3, 3, 3], 0, 'cc', 1]], [14, 24, 12, 0, 2]], ['single triangle loop 3', [[3, 3, [3], 3, 'loop', 3]], [45, 108, 64, 24, 1]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| tetra loop 1 | [10, 24, 16, 0, 2] | [10, 24, 16, 0, 2] | Passed |
| tetra loop 2 | [34, 96, 64, 0, 2] | [34, 96, 64, 0, 2] | Passed |
| disk loop | [16, 35, 20, 10, 1] | [16, 35, 20, 10, 1] | Passed |
| disk loop 2 | [51, 130, 80, 20, 1] | [51, 130, 80, 20, 1] | Passed |
| cube cc | [26, 48, 24, 0, 2] | [26, 48, 24, 0, 2] | Passed |
| cube cc 2 | [98, 192, 96, 0, 2] | [98, 192, 96, 0, 2] | Passed |
| single quad cc | [25, 40, 16, 4, 1] | [25, 40, 16, 16, 1] | Failed |
SHA-256 / a3a53b64efebb7373b6a12bd1e524373efcda5bb691b75ca756f2393c8b03b2b
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
V,E,ar,B,scheme,levels=x
ar=list(ar)
for _ in range(levels):
F=len(ar)
S=sum(ar)
if scheme=='loop':
if any(n!=3 for n in ar): return 'non-triangle'
V,E,ar=V+E,2*E+3*F,[3]*(4*F)
else:
V,E,ar=V+E+F,2*E+S,[4]*S
B=2*B
return [V,E,len(ar),B,V-E+len(ar)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['tetra loop 1', [[4, 6, [3, 3, 3, 3], 0, 'loop', 1]], [10, 24, 16, 0, 2]], ['tetra loop 2', [[4, 6, [3, 3, 3, 3], 0, 'loop', 2]], [34, 96, 64, 0, 2]], ['disk loop', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 1]], [16, 35, 20, 10, 1]], ['disk loop 2', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 2]], [51, 130, 80, 20, 1]], ['cube cc', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 1]], [26, 48, 24, 0, 2]], ['cube cc 2', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 2]], [98, 192, 96, 0, 2]], ['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]]], [['disk loop 2', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 2]], [51, 130, 80, 20, 1]], ['cube cc', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 1]], [26, 48, 24, 0, 2]], ['cube cc 2', [[8, 12, [4, 4, 4, 4, 4, 4], 0, 'cc', 2]], [98, 192, 96, 0, 2]], ['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]], ['quad and triangle cc', [[5, 6, [4, 3], 4, 'cc', 1]], [13, 19, 7, 8, 1]], ['quad and triangle cc 2', [[5, 6, [4, 3], 4, 'cc', 2]], [39, 66, 28, 16, 1]], ['pentagon cc', [[5, 5, [5], 5, 'cc', 1]], [11, 15, 5, 10, 1]]], [['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]], ['quad and triangle cc', [[5, 6, [4, 3], 4, 'cc', 1]], [13, 19, 7, 8, 1]], ['quad and triangle cc 2', [[5, 6, [4, 3], 4, 'cc', 2]], [39, 66, 28, 16, 1]], ['pentagon cc', [[5, 5, [5], 5, 'cc', 1]], [11, 15, 5, 10, 1]], ['quad under loop', [[4, 4, [4], 4, 'loop', 1]], 'non-triangle'], ['triangle then quad under loop', [[5, 6, [3, 4], 4, 'loop', 1]], 'non-triangle'], ['zero levels', [[4, 6, [3, 3, 3, 3], 0, 'loop', 0]], [4, 6, 4, 0, 2]]], [['quad and triangle cc', [[5, 6, [4, 3], 4, 'cc', 1]], [13, 19, 7, 8, 1]], ['pentagon cc', [[5, 5, [5], 5, 'cc', 1]], [11, 15, 5, 10, 1]], ['triangle then quad under loop', [[5, 6, [3, 4], 4, 'loop', 1]], 'non-triangle'], ['zero levels', [[4, 6, [3, 3, 3, 3], 0, 'loop', 0]], [4, 6, 4, 0, 2]], ['torus cc', [[9, 18, [4, 4, 4, 4, 4, 4, 4, 4, 4], 0, 'cc', 1]], [36, 72, 36, 0, 0]], ['triangle tetra cc', [[4, 6, [3, 3, 3, 3], 0, 'cc', 1]], [14, 24, 12, 0, 2]], ['single triangle loop 3', [[3, 3, [3], 3, 'loop', 3]], [45, 108, 64, 24, 1]]], [['tetra loop 1', [[4, 6, [3, 3, 3, 3], 0, 'loop', 1]], [10, 24, 16, 0, 2]], ['tetra loop 2', [[4, 6, [3, 3, 3, 3], 0, 'loop', 2]], [34, 96, 64, 0, 2]], ['disk loop', [[6, 10, [3, 3, 3, 3, 3], 5, 'loop', 1]], [16, 35, 20, 10, 1]], ['single quad cc', [[4, 4, [4], 4, 'cc', 2]], [25, 40, 16, 16, 1]], ['quad and triangle cc 2', [[5, 6, [4, 3], 4, 'cc', 2]], [39, 66, 28, 16, 1]], ['triangle tetra cc', [[4, 6, [3, 3, 3, 3], 0, 'cc', 1]], [14, 24, 12, 0, 2]], ['single triangle loop 3', [[3, 3, [3], 3, 'loop', 3]], [45, 108, 64, 24, 1]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| tetra loop 1 | [10, 24, 16, 0, 2] | [10, 24, 16, 0, 2] | Passed |
| tetra loop 2 | [34, 96, 64, 0, 2] | [34, 96, 64, 0, 2] | Passed |
| disk loop | [16, 35, 20, 10, 1] | [16, 35, 20, 10, 1] | Passed |
| disk loop 2 | [51, 130, 80, 20, 1] | [51, 130, 80, 20, 1] | Passed |
| cube cc | [26, 48, 24, 0, 2] | [26, 48, 24, 0, 2] | Passed |
| cube cc 2 | [98, 192, 96, 0, 2] | [98, 192, 96, 0, 2] | Passed |
| single quad cc | [25, 40, 16, 16, 1] | [25, 40, 16, 16, 1] | Passed |
SHA-256 / 19115b2d9330ecb2acf5e5ec7f5fc90670accb8a59e6ba6206a8383c0d088c1b
Verification & scope
Pure combinatorial teaching model over integer face lists with a stipulated contract; no geometry kernel or file format is implied. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:08.955681+00:00.
Case digest / 19949299c20b0467ddbc55e61ad31958742ad891d8e251b39173748871f99b84