Skip to content

Increment 16b-0: the noder's verifier by sort and sweep (+ the 16b design) - #107

Merged
skavhaug merged 20 commits into
masterfrom
increment16b0-verifier-sweep
Sep 28, 2026
Merged

skavhaug merged 20 commits into
masterfrom
increment16b0-verifier-sweep

Conversation

@skavhaug

Copy link
Copy Markdown
Member

What

  • The 16b design (docs/increments/16b-terrain-polygons.md): terrain polygons with CORINE over Norway, with Ola's rulings Q1-Q6 (2026-09-28).
  • 16b-0: the noder's verifier (check_guarantee_14) found intersecting pairs by brute force, which was 99.8 % of node on real CORINE. terrain::noding::sweep_box_pairs reports each intersecting closed-box edge/edge and edge/cell pair exactly once, by sort and sweep; the verifier uses it, stays always on (Ola's Q1), pads cells by one spacing and clamps to ±DBL_MAX. 98 production lines (C++ header only). Noded output unchanged.
  • Red 62740a8 (step 0 confirmed the premise first), test amendment cb09085 (the GIL probe's ladder enlarged), green aa35a38, 75f3845 (red-step guard dropped; huge-spacing clamp pinned). Invariant-critical prop_noding_verifier_sweep against a brute-force oracle; mutation round 11/14 killed, 3 equivalent.

Acceptance (@Perf, battery; docs/benchmarks/2026-09-28/16b0-acceptance*)

CORINE square before after
48 km, layout A 24.85 s 0.067 s (372x)
48 km, layout B 84.05 s 0.14 s (586x)

1 m benchmark and thread sweep: no change (pooled median −0.28 %), meshes identical. First CORINE baseline recorded (at 10 m the quality start makes the mesh 2.27x its size without; input for 20c).

Review

@Reviewer: CHANGES REQUESTED twice (bookkeeping, citations, a test gap), approved on the final docs-only fix. Local: ctest 803/803 (Release, ASan/UBSan, -ffp-contract=off), pytest 2103 passed.

🤖 Generated with Claude Code

skavhaug and others added 20 commits September 28, 2026 01:05
… 1: measurements and prior art

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…le measurement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ests, test data, acceptance, questions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, order claim stated as measured

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The design record, docs/increments/16b-terrain-polygons.md, and the ROADMAP
16b row pointing at it. Measured on Ola's CORINE GeoPackage over three DTM10
tiles: CORINE is an exact partition after reprojection, the noder merges its
doubled edges correctly, and the noder's verification is quadratic (25 s of
a 30 s run on a 48 km square), so 16b-0 indexes it. Q1-Q6 open for Ola.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ead it)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
R8 step 0 was run first. Scratch _core builds, one unchanged and one with
check_guarantee_14 returning at its first line, were timed through
clc_nodetime.py on CORINE squares in 6603_4, on battery. At 48 km (51 791
segments) node takes 24.83 s with the verifier on and 0.047 s with it off.
From 12 km to 48 km the segments grow 13.8x; the time grows 185x with the
verifier on and 15x with it off. So the verifier is the quadratic, as the
design supposed. The table is in 16b-terrain-polygons.md, R8.

prop_noding_verifier_sweep is the invariant-critical suite. It has three
layers:
- sweep_box_pairs, the pinned kernel-free search, reports exactly the pairs
  of closed boxes the brute force finds, each once, with no cell-cell pair.
  Inputs: random boxes on a small lattice (ties, zero width, corner contact,
  long boxes) in unit, 0.1, +2^51 and -2^49 frames, and named cases (x
  touching, same-x verticals, corners, one ulp apart, many boxes at one x, a
  long box spanning all, cell before edge and edge before cell). Also early
  stop and determinism.
- build() returns the brute-force status (I6) on random hand-built
  candidates (three layouts, six spacing/origin settings including next to
  kMaxGridIndex), on real noder output with one edge or one node added, and
  on named cases: duplicates in both orientations, collinear partial overlaps
  (vertical, horizontal, diagonal), stacked verticals, T-junctions, a cell
  corner graze, a corner-only box contact, long spanning edges, and huge
  coordinates. Each random family must give at least 15 or 20 of each
  status.
- node<K> is unchanged: digests over integers of six fixtures, recorded from
  6b4fcb9, and every Ok output on random polylines passes the brute force.
The oracle is tests/cpp/support/verifier_oracle.hpp, frozen from
check_guarantee_14 at 6b4fcb9. The interface is pinned under "Pinned by the
red suite (16b-0)".

Mutation round against an uncommitted scratch sweep. It passes this suite,
test_noding_noded_pslg_builder, prop_noding_no_crossings and
test_noding_node. 11 mutants killed: eviction by > (edges; cells), open
y-interval, no y-test, arriving edge not tested against active cells,
arriving cell not tested against active edges, eviction after the test (one
late), sort by xmax, sort by (kind, index), sort by ymin, and a false from
a callback not stopping the sweep. Three survive, and all three are
equivalent on admissible input:
- cells before edges on an x tie: both directions are tested either way.
- an unpadded cell box: it is segment_meets_cell's own box test.
- the cell box taken from the node point: on grid, a segment meeting a
  closed cell has a box containing the node; a search of 4e5 spacings and
  indices up to kMaxGridIndex found no cell bound reaching a neighbour
  node's coordinate.
The eviction, late-eviction and xmax-sort mutants are killed only by the
sweep layer, because on-grid violating pairs never meet at a single x.

The suite is registered only once noded_pslg_builder.hpp names
sweep_box_pairs (the 21a/21b guard), and the header is a configure
dependency. The guard fired in both scratch trees, whose header names it.
The main build exits 0 and ctest passes 781/781.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test_node_releases_the_gil refuses to pass on a window under 0.05 s. The
ladder_pslg fixture (48 rungs a side) cleared that floor only because the
verifier's pair search was quadratic; with the sort-and-sweep verifier it
nodes in ~0.003 s and the probe's precondition fails. The fixture is now
300 rungs a side (90 000 crossings): ~0.26 s here with the sweep, a 5x
margin over the floor for slower CI runners. The test's bound, tick
threshold and intent are unchanged.

Measured on this Mac, _core.node on the ladder with the sweep:
48 rungs 0.003 s, 96 0.014 s, 150 0.042 s, 200 0.088 s, 300 0.26 s.
Against the old quadratic verifier (red commit 62740a8, built in a
separate worktree) the test still passes but takes ~210 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sweep_box_pairs (terrain::noding, kernel-free) reports each intersecting
closed-box edge/edge and edge/cell pair exactly once; check_guarantee_14 uses
it, with cell boxes padded by one spacing (R8) and clamped to +-DBL_MAX. The
verifier stays always on (Ola's Q1). Written by @developer; committed by the
main session after @tester's GIL-fixture amendment (cb09085).

48 km CORINE square (battery): node 24.83 s -> 0.067 s. ctest 802/802
(Release, ASan/UBSan, -ffp-contract=off); pytest 2044 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…onstraint noted; 16b-0 LOC as built

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two @Reviewer findings on the 16b-0 green, as one test-only commit because
both touch only test registration and a test suite, and neither changes
production code.

B1: tests/cpp/CMakeLists.txt no longer guards prop_noding_verifier_sweep on
noded_pslg_builder.hpp naming sweep_box_pairs (the configure dependency,
file(READ)/string(FIND)/if are gone); the suite is registered
unconditionally, as the 21a/21b scaffold removals did. The 16b increment
file's "Pinned by the red suite (16b-0)" bullet now says the guard was
dropped after green.

S1: the +-DBL_MAX clamp on the padded node cells in check_guarantee_14 had no
test. test_noding_noded_pslg_builder gains "a huge spacing whose padded cell
overflows is accepted, not thrown on": SnapGrid{1e308}, nodes (0,0) and
(1,0), one open chain; build() must return Ok without throwing. Under a
no-clamp mutant of the header it fails with std::invalid_argument
"terrain::Box2: corners must be finite".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ical; node on 48 km CORINE layout B 84.1 s -> 0.14 s; first CORINE baseline

Three bench.py pairs vs master dc372a5, back to back on battery; CORINE
node timings (layouts A/B, 12-144 km), the ladder and comb worst cases,
and the 48 km CORINE mesh baseline at 1 m and 10 m with the quality
start on and off. Evidence in docs/benchmarks/2026-09-28/16b0-acceptance/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e pooled bench change stated reproducibly

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two rng() draws passed as function arguments are unsequenced: clang
evaluates them left to right, GCC right to left, so Linux built a
different fixture 5 in prop_noding_verifier_sweep and missed the
recorded macOS digest. The draws now go into locals in clang's order,
so kRecorded stays valid. The same pattern in prop_pslg_invariants'
generated_specs (holes(rng), lines(rng)) is sequenced the same way.
Braced-init draws elsewhere are already sequenced left to right.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng 4 revised)

Ola revised increment 13's ruling 4 on 2026-09-28: with no constraint
edges the VTK writer omits the LINES block. vtk 9.7.1 (CI's) rejects
`LINES 0 0` ("Error reading lines", 0 cells); a file with no LINES block
loads 4 cells and its cell data in 9.7.0 and 9.7.1 alike (re-probed on
both by stripping the block from today's writer output).

- test_io_vtk_legacy.py TestZeroEdges: no LINES section, no LINES
  keyword in the ASCII bytes, and a nonempty mesh still writes LINES
  before POLYGONS.
- test_io_vtk_readback.py TestEmptyCases: docstring only; its readback
  assertions already state the target.
- 13-bundled-mesh.md ruling 4: a dated revision note; the original text
  is kept.

Red under the current writer: the 3 new TestZeroEdges unit tests, and
under vtk 9.7.1 the 6 zero-edge readback cases (0 cells, expected 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ruling 4 as revised by Ola on 2026-09-28: with no constraint edges the
legacy VTK writer leaves the LINES block out, since vtk 9.7.1 rejects
`LINES 0 0`. The module docstring's rulings 4 and 5 paragraph states the
revision. Nonempty meshes are byte-identical to the previous writer
(ASCII and binary, probed against 6cb72e6's module).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g unsequenced draws sequenced

- 13-bundled-mesh.md: the Tests line "Zero edges: `LINES 0 0`" gets a
  dated revision (ruling 4: no `LINES` block), and the Empty-cases
  finding is qualified as vtk 9.7.0's (9.7.1 rejects `LINES 0 0`).
  History kept.
- 56d50e8 fixed the only two digest-relevant sites, not every one.
  These are the rest. Draws in call arguments or in the operands of one
  expression are unsequenced (clang left first, GCC right first), so
  GCC and clang tested different samples and a failing seed did not
  reproduce across compilers. All of them now go through locals in
  clang's left-to-right order, so today's macOS samples are unchanged:
  - test_mesh_row_spans.cpp: Rng::coord's `node() + unit()`, and every
    generator (node_only, mixed, sliver, collinear_but_one's two coins,
    near_node's two nudges, horizontal, vertical, on_border) via new
    Rng::node_vertex / coord_vertex helpers and locals.
  - prop_refinement_scan_equivalence.cpp: Rng::coord's operand pair and
    the mv(draw, draw) sites in meshes(), through the same helpers.
  - test_predicates_detria_exact.cpp: as_ccw(blow_up(..) x3) on the
    near-cocircular cluster; blow_up draws twice per call.
  A scan over tests/cpp for calls with draws in two or more arguments
  flags all of these on the pre-fix files and nothing after. The two
  engine-wrapping structs were read by eye for operand pairs.

test_mesh_row_spans, prop_refinement_scan_equivalence and
test_predicates_detria_exact pass on macOS (clang).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@skavhaug
skavhaug merged commit 14f5fe3 into master Sep 28, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant