From f93626d459c05b86122d0f30b5dbe80bf1ec8bc7 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 01:05:12 +0200 Subject: [PATCH 01/19] Increment 16b: design for terrain polygons (CORINE over Norway), part 1: measurements and prior art Co-Authored-By: Claude Opus 5.5 --- docs/increments/16b-terrain-polygons.md | 283 ++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 docs/increments/16b-terrain-polygons.md diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md new file mode 100644 index 00000000..7529e511 --- /dev/null +++ b/docs/increments/16b-terrain-polygons.md @@ -0,0 +1,283 @@ +# Increment 16b — terrain polygons: interior polygons and polylines as constraints (CORINE over Norway) + +Status: **designed, not built.** Questions Q1-Q6 at the end are open. Written +by `@architect` before `@tester`, per `docs/increments/README.md` step 1, on +branch `increment16b-terrain-polygons` off the unmerged 15b branch +(`increment15b-domain-crs`). Nothing in this design depends on 15c or 15d. + +**Closes.** `ROADMAP.md`'s 16b row, and increment 16's R6 ("16b's CLI, +designed, not built"). After this increment, + +```sh +rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20220924/ \ + --domain catchment.geojson --tolerance 1 \ + --features ../rasputin_data/corine_sql/.../U2018_CLC2018_V2020_20u1.gpkg \ + --features-layer U2018_CLC2018_V2020_20u1 --features-map corine \ + --out catchment.vtk +``` + +meshes the catchment with every CORINE land-cover boundary inside it as a +constraint, each constraint edge carrying the vocabulary bits its classes map +to, read without GDAL. The same works with a GeoJSON `FeatureCollection` +(`--features lakes.geojson`). + +**Not closed.** Which triangles lie inside which polygon (a region label per +triangle; proposed as 16c, Q2). Coarsening input geometry (Ola's "later +stage"). Hydro-flattening lakes. A class map read from a file (Q4). The +computation frame for a geographic DEM (15c). 20c's soft quality criterion, +which this increment gives its first real input with interior constraints. + +## Ola's direction this design is built on + +1. **Input is geometry** (16, direction 1-3): one confining polygon, any + number of polygons inside it, any number of polylines. "They are not + discretized, they are already discrete. We will come to input geometry + coarsening at a later stage." Vertices are used as given. +2. **Input vertices stay where they are, with bilinear z** (16 R2). A crossing + the noder makes is one more such vertex (16 R6). +3. **16 R6 is the input model**: an interior polygon's exterior and holes are + `Breakline` chains, closed; a polyline is an open `Breakline`; each carries + the feature's property bits (increment 7). No new role. +4. **Real data is the working example** (2026-09-27): CORINE Land Cover 2018 + over Norwegian DTM10, read GDAL-free from Ola's local GeoPackage. +5. **Inputs come in their own CRS** and are reprojected into the DEM's, which + is the computation CRS for a projected DEM (15b, R9). "Feature geometry + (16b) follows the same path" (ROADMAP, "inputs in their own CRS"). +6. **Order of work** (2026-09-27): 15a/15b, then 16b, then auto-catchment. + +## What was measured + +In the scratchpad, never in the tree: `clc_probe.py` (read, reproject, clip, +statistics), `clc_noder.py` (two chain layouts through the real `_engine`), +`clc_mesh.py` (the whole `cli._dem_mesh` path with feature chains patched in). +The installed `_core` was built from this branch's C++, which has not changed +since `4be156c` (`git log -1 -- include src bindings`). On AC power +(`pmset -g batt`: "AC attached"), Apple clang Release, default threads. + +### M1. The GeoPackage, read without GDAL + +- One features table for Europe, `U2018_CLC2018_V2020_20u1`: columns + `OBJECTID`, `Shape` (`MULTIPOLYGON`, EPSG:3035), `Code_18` (`TEXT(3)`, the + CLC code), `Remark`, `Area_Ha`, `ID`. 2 375 406 rows in its R-tree + (`rtree_U2018_CLC2018_V2020_20u1_Shape`, columns `id, minx, maxx, miny, + maxy`). Five more tables for the French overseas departments, each in its + own CRS. Read with `sqlite3.connect("file:...?mode=ro", uri=True)`. +- Every geometry blob seen (74 + 972 + 859 rows) has flags byte `0x01`: + little-endian, **no envelope**, not empty, standard WKB after the 8-byte + header. `shapely.wkb.loads` reads the rest. +- The table's insert/update triggers call `ST_IsEmpty`, a SpatiaLite function + plain SQLite lacks. Read-only access never fires them. +- The R-tree query costs 11-95 ms per 50 km tile (74-972 candidate rows). +- The largest single feature over Norway is the sea polygon (`523`, + `OBJECTID` 2372334): a 5.75 MB blob, about 360 000 vertices, 593 × 915 km. + Any coastal request reads all of it. + +### M2. What CORINE looks like over three DTM10 tiles + +Features intersecting the tile's node rectangle, reprojected 3035 → 25833 +vertex by vertex (15b's `reprojector`), clipped to the rectangle with shapely. + +| tile | area | features | classes | raw vertices | clipped ring vertices | parse + reproject | +|---|---|---|---|---|---|---| +| `7908_3` (the benchmark tile, coast) | 71 % covered | 60 | 8 | 201 304 | 10 989 | 142 ms | +| `6602_2` (Oslo, fjord, forest) | 100 % | 875 | 22 | 1 186 709 | 119 715 | 1 742 ms | +| `6603_4` (forest, bogs, lakes) | 100 % | 727 | 17 | 979 519 | 114 900 | 1 460 ms | + +`7908_3`'s 11 068 vertices in the ROADMAP row are the same count with each +ring's closing vertex included (79 rings). The uncovered 29 % is open sea +beyond CORINE's `523` polygon. + +| tile | segments | min | p1 | median | < 1 m | < 10 m (a cell) | vertex pairs < 1 m apart | closest pair | +|---|---|---|---|---|---|---|---|---| +| `7908_3` | 10 989 | 0.268 m | 19.1 m | 54.5 m | 6 | 61 | 3 | 0.268 m | +| `6602_2` | 119 715 | 0.004 m | 0.71 m | 55.9 m | 5 061 | 8 772 | 2 639 | 0.004 m (52 pairs < 1 cm) | +| `6603_4` | 114 900 | 0.190 m | 0.61 m | 51.5 m | 9 626 | 16 201 | 4 991 | 0.190 m | + +**CORINE is an exact partition, bit for bit, after reprojection.** Keyed on +the undirected pair of endpoint coordinates (Python floats, `==`): + +| tile | segments shared by exactly 2 features | unshared | of which on the clip border | same-direction duplicates | shared with the same class | vertex within 1 mm of another segment's interior | +|---|---|---|---|---|---|---| +| `7908_3` | 5 114 | 761 | 40 | 0 | 0 | 0 | +| `6602_2` | 59 749 | 217 | 217 | 0 | 0 | 0 | +| `6603_4` | 57 356 | 188 | 188 | 0 | 0 | 0 | + +- Every interior boundary arrives **twice, in opposite orientation**, with + identical endpoint coordinates: pyproj maps equal inputs to equal outputs. +- There are **no T-junctions and no partial overlaps** within 1 mm. +- `7908_3`'s 721 unshared, off-border segments are the outer edge of the sea + polygon and the coast where nothing lies beyond. +- No two neighbours share a class in these tiles, though CORINE's readme + warns "some redundant lines between neighbouring polygons with the same + code are still present" (`Documents/readme_U2018_CLC2018_V2020_20u1.txt`). +- 2 to 4 vertices per tile sit exactly on a DEM node; the rest are off-node. + +### M3. The noder on CORINE: correct, and quadratic + +A 10 km square and a 48 km square in `6603_4`, the square as `Outer`, the +features' linework inside it as `Breakline`s with one scratch bit, through +`cli._engine` (`build_pslg` → `node` → `triangulate`, 1 mm snap). Two layouts: + +- **A, deduplicated in Python:** every segment once, rings clipped as lines + (`MultiLineString ∩ square`), joined by `shapely.line_merge`. +- **B, rings as given:** every clipped polygon ring as a closed `Breakline` + (16 R6's letter), so every shared edge is given twice, in opposite + orientation, and the clip's border segments lie on the `Outer` ring. + +| square | layout | chains | input vertices | chain positions | noded vertices | noded edges | start triangles | `node` | +|---|---|---|---|---|---|---|---|---| +| 10 km | A | 108 | 2 607 | — | 2 607 | 2 638 | 5 165 | 69 ms | +| 10 km | B | 63 | 2 607 | 5 295 | 2 607 | 2 638 | 5 165 | 223 ms | +| 48 km | A | 1 415 | 51 395 | — | 51 395 | 51 788 | 102 627 | 24.8 s | +| 48 km | B | 988 | 51 395 | 104 406 | 51 395 | 51 788 | 102 627 | 83.1 s | + +- **The noder merges every doubled edge exactly and unions its bits.** A and + B give the same noded edge set and the same mask on every edge, on both + squares (compared as sets of coordinate pairs, 0 differences). This is 5b's + amended guarantee 14(a) (duplicate edges permitted, equal node-id pairs) and + its node-id dedup (`05b-noder-driver.md`, "The merge happens at the node-id + edge-key dedup"), holding on real data. +- **The noder is quadratic in edges.** Layout A: 1 764 segments 0.03 s, + 8 568 0.69 s, 19 140 3.39 s, 51 791 24.8 s: each 2.7× more edges costs + about 7×. Doubling the edges (B) costs 3.4×. Densifying the four 48 km + domain edges to 100 m changed nothing (28.0 s), so long segments are not + the cause. +- **The likely cause is 5b's verification pass**, read, not isolated: + `NodedPslgBuilder::check_guarantee_14` (`noding/noded_pslg_builder.hpp`) + checks 14(a) over every pair of edges and 14(b) over every (edge, node) pair, + "NO BROAD PHASE ... The candidates are small by construction -- a noded + constraint set, not a mesh". CORINE makes that premise false. A `sample` + profile puts almost all time in one function called from the noder's + driver; the symbols are stripped, so it names no function. A scratch build + with the check disabled, which would isolate it, was not run (the sandbox + refused the build). R8 makes the isolation 16b-0's first step. + +### M4. Meshing with CORINE: the whole pipeline + +The 48 km square in `6603_4` (2 304 km², forest and bogs), layout A, through +`cli._dem_mesh` (quality start 25°, constraint feet on), against the same +square with no features. + +| tol | features | start quality nodes | feet | triangles | min ∠ median | < 1° | worst | `node` | refine | whole | +|---|---|---|---|---|---|---|---|---|---|---| +| 1 m | none | 0 | 0 | 6 444 390 | 36.9° | 0.00 % | 0.029° | — | — | 5.1 s | +| 1 m | CORINE | 154 640 | 30 045 | 6 612 554 (+2.6 %) | 36.9° | 0.03 % | 0.0007° | 24.7 s | — | 30.2 s | +| 10 m | none | 0 | 0 | 132 563 | 34.5° | 0.02 % | 0.24° | — | 0.5 s | 0.55 s | +| 10 m | CORINE, quality off | 0 | 354 | 211 487 (1.6×) | 28.5° | 3.47 % | 0.015° | 24.7 s | 0.22 s | 25.0 s | +| 10 m | CORINE, quality 25° | 154 640 | 341 | 480 127 (3.6×) | 36.9° | 0.25 % | 0.0013° | 24.7 s | 0.37 s | 25.2 s | + +- Every run met its tolerance, and no valid node was left uncovered. +- At 1 m the DEM dominates: CORINE adds 2.6 % triangles. At 10 m the input + geometry dominates: 51 395 forced vertices, median 50 m apart, against a + mesh that would otherwise need 66 523. +- **The quality start triples the 10 m mesh.** Dense boundaries make many + start triangles with circumradius above the 14 m floor, and increment 20's + pass inserts 154 640 DEM nodes to fix them. That is 20c's question (soft + criterion, C1-C3), now with real data to decide it on. +- **Constraint feet (20b) work on interior constraints**: 30 045 at 1 m, 0 + refused. 20b R8 had this as "not measured". +- The worst angles (0.0007°, 0.0013°) come from input segments shorter than a + centimetre and vertices closer than a metre (M2), which Ola's model keeps. +- Peak RSS 2.4 GB at 1 m (6.6 M triangles), 0.68 GB at 10 m. + +## Prior art: legacy and literature + +### Literature + +- **Constrained Delaunay triangulation with breaklines**: Chew, "Constrained + Delaunay triangulations", *Algorithmica* 4:97-108, 1989. Unchanged; 16b + only supplies more constraints. Recalled, not reread. +- **Snap rounding**, which the noder is (increment 5): Hobby, "Practical + segment intersection with finite precision output", *Comput. Geom.* + 13:199-214, 1999; Goodrich, Guibas, Hershberger and Tanenbaum, "Snap + rounding line segments efficiently in two and three dimensions", SoCG 1997; + cited in `05-noder.md`. 16b changes only how the result is verified (R8). +- **Finding intersecting pairs without checking every pair.** Bentley and + Ottmann, "Algorithms for reporting and counting geometric intersections", + *IEEE Trans. Comput.* C-28(9):643-647, 1979 (plane sweep); and the simpler + one-axis "sort and sweep" over bounding-box intervals used as a broad phase + in collision detection (Cohen, Lin, Manocha and Ponamgi, "I-COLLIDE", *Symp. + Interactive 3D Graphics* 1995; Baraff's thesis, Cornell, 1992). Both + recalled. R8 uses sort and sweep for the verifier. **Difference:** it only + proposes candidate pairs for the exact `classify` and + `segment_meets_cell` the verifier already runs, so it decides nothing and + needs no exactness of its own. +- **Polygon clipping** is GEOS's OverlayNG (Martin Davis, 2020, the engine + behind `shapely.intersection` since GEOS 3.9; documentation recalled). 16b + clips linework, not areas (R6), and only in Python. +- **Region attributes on a CDT**: Shewchuk, "Triangle: Engineering a 2D + quality mesh generator and Delaunay triangulator", *Applied Computational + Geometry*, LNCS 1148, 1996. Triangle's `-A` spreads a regional attribute + from a seed point across unconstrained edges until it meets constraints. + That is the method for 16c's region labels (Q2), recorded here so 16c starts + from it. Recalled. +- **Small input features limit mesh quality**: Ruppert, "A Delaunay refinement + algorithm for quality 2-dimensional mesh generation", *J. Algorithms* + 18:548-585, 1995; Shewchuk, "Delaunay refinement algorithms for triangular + mesh generation", *Comput. Geom.* 22:21-74, 2002. Refinement's size is + governed by the local feature size, which a 4 mm input segment sets to 4 mm. + Neither increment 20's quality start nor 20b's feet claims Ruppert's bound; + M4's worst angles are what that costs with CORINE. Recalled. +- **Land cover and hydrology in TINs**: Jones, Wright and Maidment, + "Watershed delineation with triangle-based terrain models", *J. Hydraulic + Eng.* 116(10):1232-1251, 1990; Vivoni, Ivanov, Bras and Entekhabi, + "Generation of triangulated irregular networks based on hydrological + similarity", *J. Hydrologic Eng.* 9(4):288-302, 2004 (tRIBS). Both use + hydrographic and land-cover lines as TIN constraints. Recalled, not reread; + cited for the practice, not for a method carried here. +- **Coverage simplification**, for the later coarsening increment, not here: + Visvalingam and Whyatt, "Line generalisation by repeated elimination of + points", *Cartographic J.* 30(1):46-51, 1993; GEOS's `CoverageSimplifier` + (2024) applies it to a polygon partition without opening gaps. CORINE's + exact partition (M2) is what such a method needs. +- **GeoPackage**: OGC 12-128r18, *GeoPackage Encoding Standard* 1.3.1, 2021: + the geometry blob header (magic `GP`, version, flags, `srs_id`, envelope) + in its section 2.1.3 and the R-tree extension in Annex F.3. Recalled; R3's + header layout was checked against the real file (M1), not against the text. + +**Novelty: none claimed.** Nothing here is new. If a later increment claims +anything about exact partitions as TIN constraints (for example a region +labelling with a guarantee), it searches first: Google Scholar for "land cover +polygons constrained Delaunay terrain", "coverage constraints TIN +generation", "regional attributes constrained triangulation". No web search +tool was available to this round. + +### Legacy + +```sh +$ grep -rliE 'corine|land_cover|LandCover|clc|geopackage|gpkg|sqlite' legacy/ +legacy/rasputin/land_cover_repository.py +legacy/rasputin/globcov_repository.py +legacy/rasputin/wfs_repository.py +legacy/rasputin/tin_repository.py +legacy/rasputin/application.py +legacy/rasputin/web_visualize.py +legacy/rasputin/gml_repository.py +legacy/tests/test_gml_repository.py +legacy/tests/test_land_cover_repository.py +``` + +What matters, read directly: + +- `legacy/rasputin/gml_repository.py:13-64`: CORINE's 44 classes as an + `Enum` keyed by the CLC code, read from a Norwegian GML delivery (field + `clc18_kode`) with `lxml`. `:111-114`: classes above 500 are lakes (a + "lake material"). **Carried:** the CLC code as the key, and "5xx is water" + as the one rule the default map uses (R4). **Not carried:** GML, `lxml`, + and the whole file parsed per call. +- `gml_repository.py:147-150` and `:161-163`: `constraints()` returns `[]`. + The legacy never made land cover a constraint. `:185-225`: it labelled the + **finished** mesh by testing each cell centre against every polygon in + Python (`# TODO: Move to C++ for speed!`), and raised on a centre inside + none. `legacy/rasputin/application.py:125-151` writes that as the face + fields `cover_type` and `cover_color`. **Not carried in 16b:** labels are + 16c's (Q2), and a flood fill over unconstrained edges (Triangle's regional + attributes) replaces the per-centre test, which is ambiguous for a triangle + thinner than the snap. +- `gml_repository.py:141-145`: the data CRS from the GML's `srsName`, via + `+init=`. Not carried: CRS comes from the GeoPackage's own + `gpkg_spatial_ref_sys` (R3) through `crs.parse_crs`. + +`@migration-expert` is not needed: nothing numeric is ported, and the one +rule carried (5xx is water) is the CLC nomenclature's own first digit +(`Legend/CLC_legend.csv` in Ola's download). From ed2abc33c641ad07bd0683b5cdde9c96a2a4f414 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 01:08:34 +0200 Subject: [PATCH 02/19] Increment 16b design: fix legacy line citations, add the quarter-circle measurement Co-Authored-By: Claude Opus 5.5 --- docs/increments/16b-terrain-polygons.md | 28 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index 7529e511..31787b82 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -162,7 +162,7 @@ square with no features. |---|---|---|---|---|---|---|---|---|---|---| | 1 m | none | 0 | 0 | 6 444 390 | 36.9° | 0.00 % | 0.029° | — | — | 5.1 s | | 1 m | CORINE | 154 640 | 30 045 | 6 612 554 (+2.6 %) | 36.9° | 0.03 % | 0.0007° | 24.7 s | — | 30.2 s | -| 10 m | none | 0 | 0 | 132 563 | 34.5° | 0.02 % | 0.24° | — | 0.5 s | 0.55 s | +| 10 m | none | 0 | 0 | 132 563 | 34.5° | 0.02 % | 0.24° | — | — | 0.55 s | | 10 m | CORINE, quality off | 0 | 354 | 211 487 (1.6×) | 28.5° | 3.47 % | 0.015° | 24.7 s | 0.22 s | 25.0 s | | 10 m | CORINE, quality 25° | 154 640 | 341 | 480 127 (3.6×) | 36.9° | 0.25 % | 0.0013° | 24.7 s | 0.37 s | 25.2 s | @@ -180,6 +180,20 @@ square with no features. centimetre and vertices closer than a metre (M2), which Ola's model keeps. - Peak RSS 2.4 GB at 1 m (6.6 M triangles), 0.68 GB at 10 m. +### M5. The quarter circle on the committed tile, with CORINE + +Increment 16's T-real domain (536 ring vertices, 30 km radius about +(850 250, 7 899 750) in `7908_3`), with the CORINE linework inside it (layout +A), quality start 25°, feet on. This is the case a committed extract makes +testable in CI (see "Test data"). + +| tol | features | input vertices | triangles | < 1° | worst | `node` | +|---|---|---|---|---|---|---| +| 1 m | none | 536 | 428 217 | 0.00 % | 0.40° | — | +| 1 m | CORINE | 5 627 | 441 863 (+3.2 %) | 0.08 % | 0.040° | 0.33 s | +| 10 m | none | 536 | 31 581 | 0.01 % | 0.29° | — | +| 10 m | CORINE | 5 627 | 54 840 (1.7×) | 0.04 % | 0.040° | 0.33 s | + ## Prior art: legacy and literature ### Literature @@ -259,22 +273,22 @@ legacy/tests/test_land_cover_repository.py What matters, read directly: -- `legacy/rasputin/gml_repository.py:13-64`: CORINE's 44 classes as an +- `legacy/rasputin/gml_repository.py:13-66`: CORINE's 44 classes as an `Enum` keyed by the CLC code, read from a Norwegian GML delivery (field - `clc18_kode`) with `lxml`. `:111-114`: classes above 500 are lakes (a + `clc18_kode`) with `lxml`. `:124-127`: classes above 500 are lakes (a "lake material"). **Carried:** the CLC code as the key, and "5xx is water" as the one rule the default map uses (R4). **Not carried:** GML, `lxml`, and the whole file parsed per call. -- `gml_repository.py:147-150` and `:161-163`: `constraints()` returns `[]`. - The legacy never made land cover a constraint. `:185-225`: it labelled the +- `gml_repository.py:181-182`: `constraints()` returns `[]`. + The legacy never made land cover a constraint. `:184-226`: it labelled the **finished** mesh by testing each cell centre against every polygon in Python (`# TODO: Move to C++ for speed!`), and raised on a centre inside - none. `legacy/rasputin/application.py:125-151` writes that as the face + none. `legacy/rasputin/application.py:126-148` writes that as the face fields `cover_type` and `cover_color`. **Not carried in 16b:** labels are 16c's (Q2), and a flood fill over unconstrained edges (Triangle's regional attributes) replaces the per-centre test, which is ambiguous for a triangle thinner than the snap. -- `gml_repository.py:141-145`: the data CRS from the GML's `srsName`, via +- `gml_repository.py:155-159`: the data CRS from the GML's `srsName`, via `+init=`. Not carried: CRS comes from the GeoPackage's own `gpkg_spatial_ref_sys` (R3) through `crs.parse_crs`. From 03f39ae57964f1109875453180f932c092d594c0 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 01:10:06 +0200 Subject: [PATCH 03/19] Increment 16b design: blueprint and rulings R1-R11 Co-Authored-By: Claude Opus 5.5 --- docs/increments/16b-terrain-polygons.md | 386 ++++++++++++++++++++++++ 1 file changed, 386 insertions(+) diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index 31787b82..94142c51 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -295,3 +295,389 @@ What matters, read directly: `@migration-expert` is not needed: nothing numeric is ported, and the one rule carried (5xx is water) is the CLC nomenclature's own first digit (`Legend/CLC_legend.csv` in Ola's download). + +## The blueprint: data flow and boundaries + +``` +cli.py (composition root) flags -> FeatureRequest + │ + ├─ dem_input.open_dem(DemRequest) 15a/15b, unchanged + ├─ domain.read_domain(...).to_crs(dem crs) 16/15b, unchanged + │ + ├─ feature_input.open_features(request, domain, dem_crs) -> FeatureSet [16b-1] + │ per source: + │ GeoJSON: read the file (as domain.py does) + │ GeoPackage: io.repository.open_geopackage(path) read-only connection + │ io.geopackage.layer_info / query_features(conn, layer, box) + │ region = domain boundary, densified, in the SOURCE crs, + margin (R5) + │ pre-clip each geometry to region (source crs) (R5) + │ ClassMap: attribute value -> vocabulary names, or drop, or refuse (R4) + │ crs.reprojector(source, dem crs), vertex by vertex (R5) + │ clip each ring or line to the domain polygon, as LINEWORK (R6) + │ + ├─ chains.start_chains(domain, features, vocabulary) -> StartChains [16b-1] + │ Outer, Holes (mask 0), then every clipped ring or line as a Breakline + │ with its mask; closed iff unclipped (index repeated) (R7) + │ + ├─ _engine: build_pslg -> node -> triangulate noder merges doubled edges (R7) + │ verifier indexed (R8, 16b-0) + └─ refine, trim, write: unchanged code; new fields (R10) +``` + +Boundaries, each one checkable by an import test (`TestModuleIsolation` +style): + +- **No path below `cli.py` except** `domain.py` and `feature_input.py` (which + read GeoJSON text, as `domain.py` already does), `dem_input.py` (which only + builds a repository) and `io/repository.py` (which opens files). The + GeoPackage decoder in `io/geopackage.py` takes an open `sqlite3.Connection`, + never a path: the connection is its stream. +- **No CRS in C++.** Feature geometry reaches `_core` as `float64` `(x, y)` in + the DEM's CRS, like the domain. +- **No `_core` in** `feature_input.py`, `chains.py`, `io/geopackage.py`: all + three are testable with no compiled extension. Roles leave `chains.py` as + the strings `"outer"`, `"hole"`, `"breakline"`, and `cli.py` maps them onto + `ChainRole` through its existing `ROLES` table. +- **`features.py` keeps importing nothing first-party** (increment 7). The + class maps live in `feature_input.py`, which imports it. +- **Declarative:** `FeatureRequest` is frozen data. `open_features` is a + function of the request, the domain and the files, and returns frozen data. + An async caller runs it in `asyncio.to_thread` (sqlite3 and GEOS block). + +## Rulings + +### R1. The input, and the flags + +- **`--features PATH`**, one source, by suffix: `.geojson`/`.json` is a + GeoJSON `FeatureCollection`; `.gpkg` is a GeoPackage. Anything else is + refused naming the suffix. Several sources in one run are the API's + (`FeatureRequest.sources` is a tuple) and a later CLI change (Q4). +- **Only with `--domain`** (and so only with `--tolerance`, 16 R1). Features + are clipped to the confining polygon (R6); the stride-grid path has none. + Refused otherwise, as a usage error. +- **`--features-crs TEXT`**, anything `crs.parse_crs` reads. GeoJSON: the + file's `crs` member, else EPSG:4326 (RFC 7946), exactly `domain.py`'s rule. + GeoPackage: the layer's `srs_id` through `gpkg_spatial_ref_sys` (R3). Given + and disagreeing with the file's own (by `pyproj.CRS` equality) is refused, + as for `--domain-crs`. +- **`--features-layer NAME`**, GeoPackage only: the features table. Required + when the file has more than one `features` row in `gpkg_contents` (CORINE + has six: Europe and five overseas departments); the only one otherwise. + Refused for GeoJSON. +- **`--features-map NAME`**, one of the built-in class maps (R4): `property` + (the default), `corine`, `corine-water`. A map read from a file is Q4. +- **Accepted geometry**: `Polygon`, `MultiPolygon`, `LineString`, + `MultiLineString`. Z and M are dropped (`shapely.force_2d`). A `Point`, + `MultiPoint` or `GeometryCollection` is refused naming the feature: input is + geometry, never loose points (Ola's direction 1). An empty geometry, or a + GeoPackage blob with the empty flag, is skipped and counted. +- **Feature polygons are not validated.** Their rings enter as linework (16 + R6: no role, no winding), and a self-intersecting ring is a breakline the + noder nodes like any crossing. Only the confining polygon is validated + (16 R1). This is deliberate: refusing CORINE because one of its 2.4 M + polygons is invalid would help nobody, and nothing downstream reads a + feature as an area. + +### R2. Homes + +| module | holds | imports | +|---|---|---| +| `io/geopackage.py` (new) | `GpkgLayer` (frozen: table, geometry column, `srs_id`, CRS text, R-tree table or `None`); `layer_info(conn, table)`; `decode_geometry(blob) -> BaseGeometry`; `query_features(conn, layer, box, attribute) -> Iterator[RawFeature]` | `sqlite3` (types only), `struct`, shapely, pydantic | +| `io/repository.py` | adds `open_geopackage(path) -> sqlite3.Connection`, read-only | as today, plus `sqlite3` | +| `tin_engine/feature_input.py` (new) | `ClassMap`, the built-in maps; `FeatureSource`, `FeatureRequest`; `TerrainFeature`, `FeatureSet`; `FeatureError`; `open_features(request, domain, dem_crs) -> FeatureSet` | json, shapely, numpy, pydantic, `crs`, `domain`, `features`, `io.repository`, `io.geopackage` | +| `tin_engine/chains.py` (new) | `StartChains`; `start_chains(domain, features, vocabulary)`: 16 R6's table as code. `cli._domain_chains` moves here and is its first half | numpy, shapely, `domain`, `feature_input`, `features` | +| `tin_engine/features.py` | two more vocabulary entries (R4, Q3) | unchanged: nothing first-party | +| `cli.py` | four options, refusals, one call each to `open_features` and `start_chains`, the fields (R10) | adds `feature_input`, `chains` | +| `include/terrain/noding/noded_pslg_builder.hpp` | the verifier's sort-and-sweep (R8, 16b-0) | unchanged | + +**15's Q3 stays true as written:** `io/repository.py` is still "the one +module in `io/` that opens files, read-only". SQLite cannot read from a +Python stream, so the GeoPackage decoder's stream is the open connection. +`io/__init__.py`'s rule gains one clause saying so. + +### R3. Reading a GeoPackage without GDAL + +- **Open read-only**: `sqlite3.connect(path.resolve().as_uri() + "?mode=ro", + uri=True)`, closed with `contextlib.closing` (sqlite3's own context manager + commits, it does not close). Read-only also means the file's triggers, + which need SpatiaLite (M1), never fire. +- **Check it is a GeoPackage**: `PRAGMA application_id` is `0x47504B47` + (`GPKG`; Ola's file answers `1196444487`, which is that). Otherwise + refused. `user_version` is recorded, not checked (Ola's is `10200`, 1.2). +- **The layer**: its `gpkg_contents` row (`data_type = 'features'`), its + `gpkg_geometry_columns` row (column name, `srs_id`), and its + `gpkg_spatial_ref_sys` row. The CRS text is `EPSG:` + when `organization` is `EPSG` (case-insensitive), else the row's + `definition` WKT; either goes through `crs.parse_crs`. Ola's file: srs 3035, + organization `EPSG`. +- **The spatial filter**: when `gpkg_extensions` lists `gpkg_rtree_index` for + the layer and `rtree__` exists, the query joins it on the + box (`maxx >= ? AND minx <= ? AND maxy >= ? AND miny <= ?`, closed). SQLite + stores R-tree bounds as 32-bit floats rounded outward, so this is a + superset, which is all a candidate query needs. Without an index the table + is scanned and the report says so. **Rows are ordered by the primary key** + (`ORDER BY `), because an R-tree join returns rows in no stated order + and the mesh depends on input order (R7). +- **The blob header** (OGC 12-128, 2.1.3), `decode_geometry`: + - bytes 0-1 `GP`, byte 2 version `0`; else refused; + - byte 3 flags: bit 0 the header's byte order (for `srs_id` and the + envelope), bits 1-3 the envelope code (0: none; 1: 32 bytes; 2 and 3: + 48; 4: 64; 5-7 refused), bit 4 empty (skip and count), bit 5 extended + geometry type (refused: not a standard WKB geometry); + - bytes 4-7 `srs_id`, which must equal the layer's; else refused, naming the + row; + - the rest is WKB for `shapely.wkb.loads`, which reads either byte order. +- **Refusals name the file, the layer and, for a row, its primary key.** +- **The SQLite R-tree module must be compiled in.** It is in the venv's + Python 3.14 (SQLite 3.53.4: `CREATE VIRTUAL TABLE ... USING rtree` works); + whether it is in CI's Python 3.12-3.14 on `ubuntu-latest` is not verified. + If not, the query fails with `no such module: rtree`; the reader turns that + into a refusal naming the cause rather than falling back silently, and the + test suite's in-memory GeoPackages detect the module and say so. + +### R4. From feature attributes to edge bits: class maps + +```python +class ClassMap(BaseModel): # frozen, extra="forbid" + name: str + attribute: str # e.g. "property", "Code_18" + classes: Mapping[str, tuple[str, ...]] # value -> vocabulary names + otherwise: Literal["refuse", "drop"] | tuple[str, ...] = "refuse" + notice: str = "" # attribution to carry into the file (R10) +``` + +- **A value maps to vocabulary names**; the edge mask is + `vocabulary.mask(*names)` (increment 7: no bare masks). `()` is a + constraint with no bits. `"drop"` means the feature is not a constraint at + all. `"refuse"` refuses the run, naming the feature and the value. +- **Every name in a map is checked against the vocabulary when the run + starts**, through `mask`, which raises on a name it does not know. A map + can therefore not smuggle a bit number in. +- **Built-in maps:** + - `property`: attribute `property`; each vocabulary name maps to itself; + `otherwise = "refuse"`. A feature's `property` may be one name or a list + of names. This is 16 R6's "each with a `property` naming a vocabulary + entry". + - `corine`: attribute `Code_18`; `511`, `512`, `521`, `522`, `523` map to + `("land_cover", "water")`; `otherwise = ("land_cover",)`. Every CLC + boundary is a constraint. The rule is the nomenclature's first digit + (5 = water bodies; `Legend/CLC_legend.csv`), which the legacy used too. + - `corine-water`: the same five codes map to `("water",)`; + `otherwise = "drop"`. Only shorelines, river banks and the coast. +- **A shared edge gets the union** of its two features' masks: the noder's + existing merge (increment 7, "Union, not priority"). A CORINE edge between + a lake and a forest carries `land_cover | water`; so does one between a + lake and a river. **"Water on exactly one side" is not an edge bit.** It + needs to know which side is which, which is a region label (16c, Q2), not a + property of the edge. Under `corine-water` an edge is kept when at least + one side is water, because the kept feature's ring carries it. +- **Why not one bit per CLC class.** 44 classes do not fit the 32-bit word, + and increment 7 ruled land cover "stays face-based". The bits say what + kind of line an edge is; which class lies on each side is 16c's. +- **Vocabulary additions** (Q3): `land_cover` at bit 7 and `water` at bit 8. + `coastline` (bit 3) stays for linear coastline data; CORINE's sea + boundary is `water`, because a lagoon or estuary boundary is water too. + The fingerprint changes; no stored artifact carries one yet (R10 is the + first), so nothing is invalidated. + +### R5. CRS, and what is read + +- **Features go into the DEM's CRS by `crs.reprojector`**, vertex by vertex, + as `DomainPolygon.to_crs` does (15b R9): edges straight in the DEM's CRS, + one `from_crs` site, `always_xy`. A vertex with no image (`inf`) is + refused, naming the feature. `features_crs` and `features_transform` are + recorded (R10). +- **The region.** The domain (already in the DEM's CRS) has its boundary + densified to at most 1 km per segment and transformed into the source CRS; + its convex hull, buffered by 100 m, is the region. The GeoPackage query box + is the region's bounds. The 100 m is a safety margin, not a tolerance: + a 1 km chord bends between two conformal or equal-area projections by + orders of magnitude less (15 B7 measured millimetres per kilometre within a + UTM zone), and anything the margin lets in is clipped exactly in R6. +- **The pre-clip.** Each candidate geometry is intersected with the region + in the source CRS before it is reprojected. This bounds the work by the + domain, not by the feature: CORINE's sea polygon is 360 000 vertices (M1), + of which a catchment needs a few thousand; and it keeps vertices far from + the domain out of a transform that may have no image for them. The + pre-clip only removes geometry the exact clip would remove; I4 pins that + it changes nothing inside the domain. +- **GeoJSON sources** are read whole (`json.loads`, as `domain.py`), then + filtered by `intersects(region)` and pre-clipped the same way. Streaming + large GeoJSON is not needed at this scale (the probe's whole-tile CORINE + as GeoJSON was 8 MB) and not designed. + +### R6. Clipping to the confining polygon: linework, not areas + +This is the ruling 16 R6 left to 16b ("a feature leaving the confining +polygon is 16b's ruling"). + +- **Each polygon ring and each line is clipped as a line**: + `shapely.intersection(ring_as_linestring, domain.polygon)` in the DEM's CRS, + domain holes included. What is outside the domain is dropped; a feature + wholly outside is dropped and counted. +- **Not an area clip.** `polygon ∩ domain` would give each clipped polygon a + stretch of the domain's own boundary as a ring edge, so the domain boundary + would carry `land_cover` bits wherever a polygon was cut. Clipping rings as + lines gives open pieces that end on the boundary and adds no edge along it. +- **A piece that lies along the domain boundary** (a feature edge + coinciding with it, possible when the domain was cut from the same data) + is kept. The noder merges it with the `Outer` or `Hole` edge and the union + gives that edge the feature's bits. That is true of the data, so it is + right. +- **Crossing points** are GEOS's doubles, within rounding of the domain edge. + The noder snaps them and splits the ring there (05b step 4), as for any + crossing. No special case. +- **Point pieces** (a ring touching the boundary at one point) are dropped: + a point is not geometry here. +- **An unclipped ring enters closed; a clipped one as open pieces.** A ring + wholly inside comes back from GEOS as one closed line (first coordinate + equals last), which `start_chains` writes as a closed breakline by repeating + the first index (increment 8, ruling 2). GEOS may start it at a different + vertex; that is harmless. +- **Extent.** After the clip every vertex is inside the domain's closure up + to GEOS rounding, and the domain is already inside the DEM's node + rectangle (16 R1). A crossing point on a domain edge that lies on the + rectangle's border rounds to the noder's 1 mm grid like every other vertex; + a DEM whose border is not on that grid can put it up to 0.5 mm outside, + which `refine` refuses (`OutsideGrid`). That risk already exists for the + domain's own vertices under 16 U6 and is not made worse; one fixture pins + the border-on-grid case. +- **Increment 8's gallery rows are the fixtures**, as GeoJSON features over + a synthetic DEM: `road-enters-forest` (a road ending inside a closed ring), + `wall-leaves-domain` (a line crossing the outer ring: on this path the + exterior half is clipped away before the engine, so the mesh has no + finding), and `bridge-over-lake` (a road crossing a ring twice). The + gallery's own fixtures are unchanged: they test the engine, not the clip. + +### R7. Shared boundaries: the noder merges them + +- **Rings go to the engine as given**, clipped (R6), one chain per ring or + piece: 16 R6's letter. Every CORINE interior edge therefore arrives twice, + in opposite orientation. +- **The noder already does the right thing** (M3): it merges the two copies + into one edge by the node-id key and unions their masks (05b guarantee + 14(a), amended; "the merge happens at the node-id edge-key dedup"). Layouts + A and B gave identical noded edge sets and masks on real data. +- **Rejected: deduplicate in Python first** (layout A). It is exact on CORINE + (M2: shared endpoints are bit-identical after reprojection) and halves the + noder's input. But it is a second merge site with its own union to get + right, about 30 lines, and after R8 it saves a constant factor, not an + order. It is the first thing to add if @perf finds `node` significant after + R8. +- **Rejected: `shapely.unary_union` of the linework.** GEOS's noding may snap + vertices, and it drops per-edge attributes. +- **Same-class neighbours** (CORINE's "redundant lines", readme) stay + constraints: the union of two equal masks is that mask, and dropping the + edge would need to know both sides, which R7 deliberately does not compute. +- **Order is the source's**: features by primary key (GeoPackage) or file + order (GeoJSON); within a polygon, exterior then holes; within a + `MultiPolygon`, part order. Node ids are a function of the node set + (05b), but refinement's result depends on vertex order (M3: layouts A and + B, whose start meshes are identical, refined to 261 377 and 261 375 + triangles), so a stated order is what makes a run reproducible. + +### R8. The noder's verifier, indexed [16b-0] + +M3's quadratic makes a 48 km CORINE square spend 25 s in `node`, and a +catchment the size of Glomma's (about 42 000 km², 18× that square) would +spend hours. The verifier's premise, "the candidates are small by +construction", is false for land cover. + +- **Step 0, before any code: isolate the cost.** Build with + `check_guarantee_14` returning early, rerun `clc_nodetime.py`'s sizes, and + confirm the quadratic goes with it. If it does not, the cost is in steps + 1-5, and `@architect` re-scopes 16b-0 before `@tester` starts. +- **The replacement: sort and sweep on x.** Items are the edges (their + bounding boxes) and the node cells (each node's closed cell box, padded by + one grid spacing on every side: false positives are free, and the padding + removes any doubt about how the cell's bounds round). Sort by `(xmin, + kind, index)`. Keep the active edges whose `xmax >= ` the current `xmin` + (closed). Test an arriving edge against every active edge, and an arriving + cell against every active edge, whose closed y-intervals intersect; and an + arriving edge against every active cell likewise. The tests are the + existing `classify` and `segment_meets_cell`, unchanged. +- **Completeness in one line each:** two segments that cross or overlap + share a point, so their closed boxes intersect; a segment that meets a + closed cell meets its box. Box bounds are `min`/`max` of the endpoints' + doubles, which is exact. +- **Independent of `BroadPhase`.** No shared code and a different algorithm, + so 5b's reason for having no index ("a verification pass sharing the index + would be blind wherever the index is") still holds: the verifier does not + share the driver's index. +- **What stays the same:** the statuses, and which inputs are refused. **What + may change:** which violating pair the message names first. No test asserts + wording (house rule). +- **Worst case** is still quadratic (every x-interval overlapping every + other, such as many long parallel east-west lines); typical inputs are + `O(n log n + k)`. The header comment says so, replacing the false premise. +- **The brute force stays, as the test oracle**, in `tests/cpp/support/`. + +### R9. Vertex density, z, and refinement + +- **Vertices are used as given** (Ola's direction 1): no simplification, no + minimum segment length, no merging of close vertices beyond the noder's + 1 mm snap (16 U6). Measured: no two CORINE vertices within 1 mm in three + tiles, 52 pairs within 1 cm in one (M2). A pair within the snap would merge + into one node; the report shows input and noded vertex counts, so it is + visible. +- **Segments shorter than a cell** (9 000-16 000 per inland tile, the + shortest 4 mm) enter unchanged. They cost angles, not correctness: the + worst angle falls to 0.0007° (M4) because a 4 mm segment is a 4 mm local + feature (Ruppert 1995). Coarsening is the later increment's; CORINE's exact + partition (M2) is what a coverage-preserving simplifier needs. +- **z is 16 R2's**: bilinear at an off-node vertex, `value_at` at a node. + Nothing about a feature changes its height: a lake's shore vertices get the + DEM's bilinear height, not the lake level. Hydro-flattening is out of + scope. +- **NoData**: a feature vertex in a cell with a NoData corner is invalid, + and its triangles are carved (16 R2). Unchanged. +- **Refinement code is unchanged.** Interior constraints are what 14b R5 and + 20b R8 already cover: never flipped, split with bits kept, feet on them + (M4: 30 045 feet at 1 m, none refused). The tolerance guarantee is + unchanged: error is measured at DEM nodes only. +- **The quality start (20) is unchanged, and M4 is its input for 20c.** On + CORINE it inserts 154 640 nodes and triples the 10 m mesh. 16b does not + change it; Q5 asks whether the acceptance runs should record both settings. + +### R10. What the file records, and the report + +- **`features`**: source file name, layer (GeoPackage), map name, and counts + after the clip: e.g. `U2018_CLC2018_V2020_20u1.gpkg:U2018_CLC2018_V2020_20u1, + map corine, 727 features (12 dropped outside), 988 chains, 51 395 + vertices`. `.vtk` field and `.ply` comment, like `domain`. +- **`features_crs`** and **`features_transform`**, as `domain_crs` and + `domain_transform` (15b). +- **`edge_vocabulary`**: `DEFAULT_VOCABULARY.fingerprint()` and the names by + bit (`0 river, 1 road, ..., 7 land_cover, 8 water`). This is increment 7's + mechanism 2 ("a field of whatever serialized artifact carries a mesh"), + which no writer has implemented yet: without it the edge masks in the file + cannot be read. Written whenever the file has an edge block, features or + not. +- **`features_notice`**: the map's `notice`, when it has one. The CORINE maps + carry the Copernicus attribution (see "Test data"), because a mesh built + from CORINE and shared must say so and say it was modified. +- **`elevation_source`**'s start clause becomes + `start domain boundary and features, vertex z bilinear` when features are + given. +- **stderr**: features read, dropped outside, clipped, and input versus + noded vertex counts. `--stats`' phase table gains `features read` and + `features clip`. + +### R11. Scale and memory + +- **Per request, not per dataset.** The 8.9 GB GeoPackage is never loaded: + the R-tree gives candidates in 11-95 ms per tile-sized region (M1), and + SQLite reads only the pages it touches. +- **Python memory** is the candidates' geometry: about 1 M raw vertices per + tile-sized region before the pre-clip (16 MB of coordinates plus shapely's + overhead), about 120 000 after. The probe's peak RSS for a whole tile was + 330-340 MB with nothing else loaded. The DEM dominates a real run (a + 50 km tile is 100 MB of float32; M4's 1 m run peaked at 2.4 GB for 6.6 M + triangles). +- **The engine** holds the chain positions (about twice the unique vertices, + R7) and the noded graph: tens of MB for a catchment. +- **A whole-Norway request is not a use case.** A coarse box around Norway + holds 424 319 CORINE features and 850 MB of blobs. The use case is a + catchment, which auto-catchment (next in the order of work) will supply. +- **Until R8 lands, `node` is the bottleneck** (M3, M4). After it, @perf + measures `node` on the 48 km square and on a larger block. From 65b1a6bc78d545b85c01fe4819e648f83292680b Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 01:11:25 +0200 Subject: [PATCH 04/19] Increment 16b design: invariants, degeneracy policy, LOC and split, tests, test data, acceptance, questions Co-Authored-By: Claude Opus 5.5 --- docs/increments/16b-terrain-polygons.md | 283 +++++++++++++++++++++++- 1 file changed, 281 insertions(+), 2 deletions(-) diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index 94142c51..19316153 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -503,8 +503,9 @@ class ClassMap(BaseModel): # frozen, extra="forbid" it changes nothing inside the domain. - **GeoJSON sources** are read whole (`json.loads`, as `domain.py`), then filtered by `intersects(region)` and pre-clipped the same way. Streaming - large GeoJSON is not needed at this scale (the probe's whole-tile CORINE - as GeoJSON was 8 MB) and not designed. + large GeoJSON is not needed at this scale (the 2026-09-27 probe's CORINE + over `7908_3` as GeoJSON, 88 features in EPSG:25833, is 8 MB) and not + designed. ### R6. Clipping to the confining polygon: linework, not areas @@ -681,3 +682,281 @@ construction", is false for land cover. catchment, which auto-catchment (next in the order of work) will supply. - **Until R8 lands, `node` is the bottleneck** (M3, M4). After it, @perf measures `node` on the 48 km square and on a larger block. + +## Invariants + +- **I1. Bits, from the input.** For every noded constraint edge, its mask + contains the union of the masks of every feature whose clipped linework + contributes geometry to it (05b's guarantee 15, applied to features). On a + partition, an interior edge's mask is exactly the union of its two + features' map masks. The oracle is built from the features and the map, + never from the noder's provenance (05b, "Guarantee 15's oracle"). +- **I2. The clip keeps input vertices exact.** Every vertex of the engine's + feature input is either a reprojected source vertex, bit for bit, or lies + within 1e-6 m of the domain's boundary (a crossing). None lies outside the + domain's closure by more than that. +- **I3. Determinism.** The same request gives a bit-identical file whatever + order SQLite returns candidate rows in. +- **I4. The pre-clip changes nothing inside the domain.** The engine input is + identical with the pre-clip and without it. +- **I5. Ring shape.** A ring wholly inside the domain enters as one closed + `Breakline` (first index repeated); a clipped ring as open `Breakline`s; a + line as open `Breakline`s. No feature chain is `Outer` or `Hole`. +- **I6. The verifier's verdict is the brute force's.** For every input the + sweep returns the status the brute-force check returns. +- **I7. The tolerance guarantee is unchanged**: every valid DEM node in the + domain is within tolerance of the output mesh (14 T3's oracle), with + features. + +## Degeneracy policy + +| case | outcome | +|---|---| +| a feature edge collinear with a domain edge, fully or partly | kept; the noder merges or splits it and unions bits onto the domain edge (R6) | +| a ring touching the domain boundary at one point | the point piece is dropped; the ring's pieces end there | +| a feature wholly outside the domain, or inside a domain hole | dropped, counted | +| no feature left after the clip | meshed with the domain alone; the report says 0 features (a catchment inside one land-cover polygon is legitimate) | +| two feature vertices within the 1 mm snap | one node (16 U6); input and noded vertex counts in the report | +| a closed feature ring collapsing to two nodes after the snap | the noder's existing handling of a collapsed breakline; one fixture pins the status | +| a self-intersecting feature ring | linework; the noder nodes the crossing (R1) | +| the same edge in two features, same class | one constraint edge, same mask (R7) | +| a partial overlap or T-junction between two sources | the noder splits and merges, as for any input (05b) | +| `Point`, `MultiPoint`, `GeometryCollection` | refused, naming the feature (R1) | +| an empty geometry or empty-flag blob | skipped, counted | +| an unknown attribute value | per the map: refused (naming the feature and value), dropped, or the map's default (R4) | +| a vertex with no image under the transform | refused, naming the feature (R5) | +| a GeoPackage without an R-tree | full scan, reported (R3) | +| SQLite without the R-tree module | refused, naming the cause (R3) | +| a blob with `srs_id` other than the layer's, an extended type, a bad magic or version, envelope code 5-7 | refused, naming the row (R3) | +| not a GeoPackage (`application_id`) | refused (R3) | + +## Not in scope + +- Region labels per triangle (16c, Q2), and "water on exactly one side". +- Coarsening or simplifying feature geometry. +- Hydro-flattening lakes or rivers. +- Several `--features` in one CLI run, and a class map read from a file (Q4). +- Grade separation (increment 8, ruling 1: never). +- Python-side deduplication of shared edges (R7; kept in pocket). +- Streaming GeoJSON; reading GML, Shapefile, FlatGeobuf or any other format. +- Changing the quality start or feet (20c). + +## Sub-increments and LOC + +Counted in `CLAUDE.md` §2's unit. Estimates, then the worst overrun seen so +far (+39 %, increment 16) applied. + +| | file | what | est. | +|---|---|---|---| +| **16b-0** | `noding/noded_pslg_builder.hpp` | sort-and-sweep for 14(a) and 14(b), cell padding, header comment | ~50 | +| | | **16b-0 total** | **~50 (worst ~70)** | +| **16b-1** | `io/geopackage.py` | `GpkgLayer`, `layer_info`, `decode_geometry`, `query_features` | ~70 | +| | `io/repository.py`, `io/__init__.py` | `open_geopackage` | ~8 | +| | `feature_input.py` | `ClassMap` and three built-in maps, request and result models, GeoJSON reading, region, pre-clip, reprojection, clip, counts, refusals | ~130 | +| | `chains.py` | `start_chains` (the domain half moved from `cli.py`, net ~+25) | ~45 | +| | `features.py` | two vocabulary entries | ~2 | +| **16b-2** | `cli.py` | four options and their refusals, the two calls, `features`, `features_crs`, `features_transform`, `features_notice`, `edge_vocabulary`, the sentence, the report; less `_domain_chains` | ~65 | +| | `stats.py` | two phase rows | ~4 | +| | | **16b-1 + 16b-2 total** | **~325 (worst ~450)** | + +**Two PRs, recommended:** + +1. **16b-0 alone.** It is C++ in the noder's invariant-critical verifier, it + stands on its own (every noder input benefits), its test is an + equivalence against brute force, and it is the one piece whose premise + (M3's attribution) must be confirmed before anything is written (R8 step + 0). About 50 lines. +2. **16b-1 and 16b-2 together.** 16b-1 alone would ship a reader nobody can + run; the CLI is what makes the acceptance runnable, the same argument + increment 16 made against splitting. About 325, worst about 450, under + 700. + +If 16b-2 grows past the ceiling in review, the seam is `feature_input.py` +plus `io/geopackage.py` (testable from Python with no CLI) first, `chains.py` +and `cli.py` second. + +## Tests for `@tester` + +**Invariant-critical (mutation testing required): two suites.** + +- **`prop_noding_verifier_sweep` (C++, 16b-0)**, I6. Random PSLGs through + the builder in both forms, the sweep in production and the brute force + from `tests/cpp/support/`, asserting equal status. The generators must + include what the sweep can get wrong: duplicate edges in both + orientations; collinear partial overlaps; boxes that touch at exactly + equal `x` (an edge's `xmax` equal to another's `xmin`); vertical and + horizontal edges (zero-width boxes); a node whose cell touches an edge's + box only at a corner; long edges spanning every other box. Plus the + existing 14(a) and 14(b) refusal fixtures, unchanged. + Mutants: `xmax > xmin` for `>=` in eviction; open y-interval test; cells + unpadded and cell box computed from the node point instead of its cell; + arriving edges not tested against active cells; the active list evicted + one item late or early; sort by `xmax` instead of `xmin`. +- **`test_feature_chains_bits` (Python, 16b-1)**, I1, I2 and I5, with the + oracle built from the input features and the map. On synthetic partitions + (a 2×2 grid of squares with one class each, a square with a hole holding a + second polygon, a lake with a river ending on its shore) and on the + committed CORINE extract through the real `_engine`. + Mutants: masks taken from the first contributing feature only (no union); + a clipped ring closed by repeating its first index; holes' rings dropped; + area clipping instead of line clipping (the domain boundary then carries + bits: caught by I1's "exactly" on a partition, and by a direct assertion + that no `Outer` edge carries bits where no feature edge lies on it); + `corine`'s 5xx rule off by one class (`52x` treated as land only). + +Everything else is unit, property or integration testing: + +- **GeoPackage decoding**: blobs with every envelope code 0-4, big- and + little-endian headers, the empty flag, the extended flag (refused), a bad + magic, version 1, envelope code 5, an `srs_id` differing from the layer's. + Built in memory: a test helper writes a minimal GeoPackage (the four + `gpkg_*` tables, one feature table, its R-tree) with `sqlite3` to + `tmp_path`, so the reader opens a real file. The helper is test support. +- **Layer resolution**: one table (default), several (refused without + `--features-layer`), an unknown name, `data_type` other than `features`, + no R-tree (full scan, reported), a non-EPSG `organization` (WKT path), + `application_id` wrong. +- **Class maps**: `property` with one name, a list, an unknown name + (refused), no `property` (refused); `corine` on every 5xx code and on + `111` and `999`; `corine-water` dropping non-water; a map naming a name the + vocabulary lacks (refused when the run starts). +- **CRS**: a GeoJSON in EPSG:4326 over a 25833 synthetic DEM; a GeoPackage + in 3035; `--features-crs` agreeing and disagreeing; a vertex with no image. +- **Region and pre-clip**: I4 on the extract (engine input with and without + the pre-clip, compared bit for bit). +- **Clip degeneracies** from the table above, one fixture each. +- **Determinism**, I3: the extract's rows returned in a shuffled order (a + second GeoPackage written with rows inserted in reverse) gives a + bit-identical `.vtk`. +- **CLI**: `--features` without `--domain`; an unknown suffix; + `--features-layer` on GeoJSON; the four new fields and the notice in `.vtk` + and `.ply`; `edge_vocabulary` present on a run without features too. +- **Increment 8's three rows** as GeoJSON over a synthetic DEM, end to end: + the road ends inside the forest ring; the wall's exterior half is absent + and the mesh has no edge outside the domain; the bridge road is split at + two shore crossings with `road` and `water` bits on the right edges. +- **I7 on the extract**: every valid node of the committed tile inside the + quarter circle within tolerance, at 10 m (fast), with CORINE. + +## Test data + +- **Synthetic**: GeoJSON documents built in the tests; minimal GeoPackages + written by the helper above; the micro-DEMs increment 16's suites already + use. +- **Committed: a CORINE extract over the committed tile `7908_3`.** An + extraction script under `tests/fixtures/corine/` (test support, as + `tests/fixtures/dtm10/extract.py` is) reads Ola's GeoPackage, takes the + features whose R-tree box meets the tile's 3035 image, clips them in 3035 + to that image buffered by 1 km (so the partition is exact over the tile), + and writes them to a small GeoPackage with the source's table name, + columns, SRS row and R-tree, blobs in the source's header layout (flags + `0x01`). Expected size: about 11 000 vertices over the tile (M2), a few + hundred kB. It makes the quarter circle with CORINE (M5) a CI test. +- **Local only, for @perf**: Ola's GeoPackage and the 48 km square in + `6603_4` (M4), and a larger block once R8 lands. +- **Attribution.** The GeoPackage's own metadata + (`Metadata/U2018_CLC2018_V2020_20u1.xml` in Ola's download) grants "full, + open and free access" under Regulation (EU) No 1159/2013, on four + conditions: inform the public of the source; do not suggest EU endorsement; + state it clearly where the data "has been adapted or modified"; and + acknowledge that the data were produced "with funding by the European + Union". The extract is modified (clipped), so its `README` says, at least: + source (CORINE Land Cover 2018, version 2020_20u1, Copernicus Land + Monitoring Service, European Environment Agency), that it is clipped and + re-encoded, the funding sentence, and no endorsement. The `corine` maps' + `notice` carries a one-line form of the same into every mesh built from + them (R10). **Not verified:** the exact attribution line the Copernicus + Land Monitoring Service website asks for today (recalled as "© European + Union, Copernicus Land Monitoring Service 2018, European Environment Agency + (EEA)"); no web access in this round. Ola or the main session should check + `land.copernicus.eu` before the extract is committed. +- **Found in passing:** `tests/fixtures/corine/0000_4326_corine2018_4e6064_GML.gml` + (30 MB, 399 CORINE features over Norway in EPSG:4326, written by OGR, + committed with the foundation reset `3096ccc`) carries no attribution. Only + `legacy/tests/test_gml_repository.py` names that directory. 16b's PR adds the + same attribution for it, or removes it if Ola prefers (Q6), per the rule + that a documentation defect found in an increment is fixed in its PR. + +## Acceptance + +- **16b-0**: R8 step 0's isolation recorded; `prop_noding_verifier_sweep` + green with its mutants killed; `node` on M3's 48 km square in `6603_4` + (layout B, the one 16b uses) far below its 83 s, with the ratio across + M3's sizes showing the quadratic gone. Every existing noder and gallery + test green; the gallery pictures unchanged. +- **16b-1/2**: the quarter circle on the committed tile with the committed + extract, `--features-map corine`, at 1 m and 10 m, writes a `.vtk` + ParaView opens, with the fields of R10; constraint edges carry + `land_cover` and, on the coast, `water`; the tolerance holds; M5 is the + reference. Then Ola's own case: a catchment in EPSG:4326 over the DTM10 + archive with Ola's GeoPackage. +- **@perf, for both PRs.** README's acceptance rule applies: 16b-0 changes + what the start mesh costs, and 16b-2 changes what `_dem_mesh`, the code + that drives `refine`, feeds it. The 1 m benchmark and thread sweep with + `tools/bench.py` must show no change on the standard inputs (they have no + features; their start ring is small). In addition, @perf records the first + **CORINE baseline**: M4's 48 km square at 1 m and 10 m, `node` and + `refine` phases, triangles, peak memory, power state, under + `docs/benchmarks//`. The quality-start tripling (M4) is recorded + there as 20c's input. +- All gates in `CLAUDE.md` §4, including the TSan job; CI green. + +## Questions for Ola + +**Q1. Where may the noder's verification spend its time?** M3: 25 s of a +30 s run is the noder on a 48 km square of CORINE, and it grows with the +square of the edge count. +- **(a) Index the verifier (16b-0, R8), keep it on for every run. + Recommended.** About 50 lines of C++, same statuses, and the brute force + stays as its test oracle. +- (b) Skip verification in Release builds. Fastest, but 5b made the + verification the thing that turns a missed T-junction into a refusal + instead of a wrong mesh; switching it off gives that up. +- (c) Leave it; features on small catchments only. Not viable for the + auto-catchment work that comes next. + +**Q2. Region labels: which triangles lie in which polygon.** 16b makes every +land-cover boundary a constraint edge, but the mesh does not say which class +each triangle has, and "shoreline" (water on one side only) needs that. +- **(a) A separate increment 16c, next after 16b: a label per triangle by + flood fill across unconstrained edges from one seed per feature, the method + of Shewchuk's Triangle (`-A`), written as a cell field `land_cover` with the + CLC code. Recommended.** About 80 lines, pure Python over the trimmed mesh + and the features; the legacy wrote the same field by testing cell centres. +- (b) Fold it into 16b. Pushes 16b-1/2 to about 400 lines (worst 560), + still under 700, but adds a second invariant-critical suite to the round. +- (c) Not needed; the edges are enough. + +**Q3. Two new vocabulary entries, `land_cover` (bit 7) and `water` (bit 8).** +- **(a) Both, as R4. Recommended.** The CORINE maps need a bit for "a + land-cover boundary" and one for water; `coastline` stays for linear + coastline data. +- (b) One bit, `land_cover`, and water left to 16c's labels. +- (c) Reuse `coastline` for every water boundary. Wrong for lakes and rivers. + +**Q4. The CLI's reach in 16b.** +- **(a) One `--features` source and the three built-in maps; several sources + and maps read from a file later. Recommended.** The API already takes + several sources; the CLI question is how flags pair with sources, which + deserves its own small design once a second source (roads, rivers) is on + the table. +- (b) Repeatable `--features` now, each followed by its own + `--features-map`, paired by position. About 20 more lines, and + position-paired flags are easy to get wrong. +- (c) A JSON request file (`--features-spec`) listing sources and maps. The + most general; about 40 more lines. + +**Q5. The quality start on CORINE.** It triples the 10 m mesh (M4: 211 487 +triangles without it, 480 127 with it), because dense boundaries make many +bad start triangles. +- **(a) Change nothing in 16b; @perf records both settings, and 20c decides + with this data. Recommended.** +- (b) Turn the quality start off when features are given. Quick, but it + decides 20c's question by the back door. + +**Q6. The 30 MB CORINE GML already in `tests/fixtures/corine/`** (legacy +test data, written by OGR, no attribution). +- **(a) Keep it and add the attribution in 16b's PR. Recommended**: removing + data from the tree is your call, and the legacy suite names the directory. +- (b) Remove it: nothing outside `legacy/` uses it, and 16b's extract + replaces it for everything current. From 7578011d0e7f1ed3b7c36138b05d4da8ec503222 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 01:11:54 +0200 Subject: [PATCH 05/19] Increment 16b design: the GML fixture is unread, LOC without stats.py, order claim stated as measured Co-Authored-By: Claude Opus 5.5 --- docs/increments/16b-terrain-polygons.md | 30 ++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index 19316153..591e2458 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -573,9 +573,10 @@ polygon is 16b's ruling"). - **Order is the source's**: features by primary key (GeoPackage) or file order (GeoJSON); within a polygon, exterior then holes; within a `MultiPolygon`, part order. Node ids are a function of the node set - (05b), but refinement's result depends on vertex order (M3: layouts A and - B, whose start meshes are identical, refined to 261 377 and 261 375 - triangles), so a stated order is what makes a run reproducible. + (05b), but refinement's result depends on vertex order: on the 10 km + square, the same linework given in two chain orders (`clc_mesh.py`'s + `dedupe` and `dup` runs) made start meshes with the same 2 607 vertices and + 5 165 triangles, and refined to 261 377 and 261 375 triangles, so a stated order is what makes a run reproducible. ### R8. The noder's verifier, indexed [16b-0] @@ -662,7 +663,8 @@ construction", is false for land cover. given. - **stderr**: features read, dropped outside, clipped, and input versus noded vertex counts. `--stats`' phase table gains `features read` and - `features clip`. + `features clip` (`PhaseClock` rows are named by the caller; `stats.py` does + not change). ### R11. Scale and memory @@ -756,8 +758,7 @@ far (+39 %, increment 16) applied. | | `chains.py` | `start_chains` (the domain half moved from `cli.py`, net ~+25) | ~45 | | | `features.py` | two vocabulary entries | ~2 | | **16b-2** | `cli.py` | four options and their refusals, the two calls, `features`, `features_crs`, `features_transform`, `features_notice`, `edge_vocabulary`, the sentence, the report; less `_domain_chains` | ~65 | -| | `stats.py` | two phase rows | ~4 | -| | | **16b-1 + 16b-2 total** | **~325 (worst ~450)** | +| | | **16b-1 + 16b-2 total** | **~320 (worst ~445)** | **Two PRs, recommended:** @@ -768,7 +769,7 @@ far (+39 %, increment 16) applied. 0). About 50 lines. 2. **16b-1 and 16b-2 together.** 16b-1 alone would ship a reader nobody can run; the CLI is what makes the acceptance runnable, the same argument - increment 16 made against splitting. About 325, worst about 450, under + increment 16 made against splitting. About 320, worst about 445, under 700. If 16b-2 grows past the ceiling in review, the seam is `feature_input.py` @@ -872,8 +873,10 @@ Everything else is unit, property or integration testing: `land.copernicus.eu` before the extract is committed. - **Found in passing:** `tests/fixtures/corine/0000_4326_corine2018_4e6064_GML.gml` (30 MB, 399 CORINE features over Norway in EPSG:4326, written by OGR, - committed with the foundation reset `3096ccc`) carries no attribution. Only - `legacy/tests/test_gml_repository.py` names that directory. 16b's PR adds the + committed with the foundation reset `3096ccc`) carries no attribution, and + nothing in the tree reads it: `grep -rln corine src_python tests tools` finds + only the file itself, and the legacy test reads `$RASPUTIN_DATA_DIR/corine` + (`legacy/tests/test_gml_repository.py:18`). 16b's PR adds the same attribution for it, or removes it if Ola prefers (Q6), per the rule that a documentation defect found in an increment is fixed in its PR. @@ -956,7 +959,8 @@ bad start triangles. **Q6. The 30 MB CORINE GML already in `tests/fixtures/corine/`** (legacy test data, written by OGR, no attribution). -- **(a) Keep it and add the attribution in 16b's PR. Recommended**: removing - data from the tree is your call, and the legacy suite names the directory. -- (b) Remove it: nothing outside `legacy/` uses it, and 16b's extract - replaces it for everything current. +- **(a) Remove it in 16b's PR. Recommended**: nothing in the tree reads it + (the legacy test reads `$RASPUTIN_DATA_DIR/corine`), it is 30 MB of GML + this project will never parse, and 16b's extract replaces it as CORINE + test data. +- (b) Keep it and add the attribution. From 4265f3092cfb27cd7e437f6778868e9add458c65 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 01:12:09 +0200 Subject: [PATCH 06/19] Increment 16b: design for terrain polygons (CORINE over Norway) 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 --- ROADMAP.md | 2 +- docs/increments/16b-terrain-polygons.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 9841f8fd..89e563ca 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -45,7 +45,7 @@ increment that most needs a picture to check against | — | The serial phase: profile refine's serial insert-and-flip phase, then parallelise what the profile blames. Scaling tops out at about 2.0-2.2×. Profiled 2026-09-27: serial part about a third of 1-thread refine, mostly Lawson legalisation; the scan stops near 5× from load imbalance | profiled; designed as increment 21 (Ola's rulings 2026-09-27: L1 determinism, one path, at most 2 % more triangles). **21a shipped with branch `increment21a-quick-wins`'s PR**: dynamic scan blocks, active merge, reused flip stack; mesh bit-identical; on AC refine -10 to -15 % at 8 threads, ceiling 2.1x -> 2.3x (`docs/benchmarks/2026-09-27/21a-acceptance.md`). **21b shipped with branch `increment21b-lattice-incircle`'s PR**: an int64 lattice incircle answers 99.97-100 % of refine's incircle tests; mesh bit-identical; on battery refine -15 % at 8 threads, ceiling 2.3x -> 2.5x (`docs/benchmarks/2026-09-27/21b-acceptance.md`). **21c measured** (`docs/benchmarks/2026-09-27/21c/README.md`): option C costs 6.5-10 % more triangles; A1 about 1 %; A0 is bit-identical (on these two inputs, not proven), and with evaluate-once is modelled at only 7-11 % faster refine at 8 threads and slower at 4. **21d is deferred** (Ola, 2026-09-27: "Review and push 21c, then basin-work"), behind the work in "Order of work" below; domain decomposition parallelises scan and split together | `docs/increments/21-parallel-refine.md`, `docs/benchmarks/2026-09-27/serial-profile/README.md` | | — | Release hardening: measure libc++'s `_LIBCPP_HARDENING_MODE_FAST` (and libstdc++'s assertions on the GCC leg), which bounds-check `std::vector`, `span` and the like in Release, on the 1 m benchmark; switch it on if the cost is small. Today an out-of-range index in shipped code the tests miss is undefined behaviour and crashes Python (Ola, 2026-09-27: "I'm surprised we don't have proper memory control"; CI's ASan/UBSan/TSan cover what the tests reach) | to measure (Ola, 2026-09-27); 21d, which it was placed after, is deferred behind the basin work | none yet | | 15 | A DEM in several tiles, and the domain in its own CRS: 15a Norway, many tiles in one projected CRS (Ola's 254 DTM10 UTM33 tiles; only the selected tiles must share a lattice, since 8 of them sit half a cell off); 15b the domain polygon reprojected into the DEM's CRS; 15c and 15d the São Francisco basin (geographic DEMs meshed in the DEM's own lattice frame, not resampled; window decoding and memory) | **15a shipped with branch `increment15-dem-mosaic`'s PR**: `--dem DIR` or several files, `--bbox`, tiles selected and stitched on one lattice, Q1-Q5 refusals; a box over tiles whose overlaps agree meshes (the design's acceptance box is refused: real DTM10 overlaps disagree by up to 25 m, which Ola's Q1 refuses; the overlap policy is open, 2026-09-27). **15b built with branch `increment15b-domain-crs`'s PR**: `--domain` in its own CRS (e.g. EPSG:4326) over a UTM33 mosaic, reprojected vertex by vertex. Then 16b (Ola, 2026-09-27: Norway first) | `docs/increments/15-dem-mosaic.md` | -| 16b | Interior polygons and polylines as constraints ("terrain polygons": lakes, land cover, roads, rivers): `--features PATH`, a GeoJSON `FeatureCollection`, each feature naming a vocabulary property; closed or open `Breakline`s, crossings noded, off-node vertices with bilinear z. **Its working example is real data** (Ola, 2026-09-27): CORINE Land Cover 2018 over the benchmark tile `7908_3_10m_z33.tif`. The source is Ola's local copy, `rasputin_data/corine_sql/.../U2018_CLC2018_V2020_20u1.gpkg` (8.2 GB, EPSG:3035, a sibling of this repository, which also holds the 254-tile DTM10 archive for gap 6). It reads without GDAL: sqlite3 over its R-tree, the GeoPackage blob header stripped, `shapely.wkb`, then `pyproj` to 25833, so CRS stops in Python as before. Probed 2026-09-27 in 0.2 s: 60 polygons in 8 classes (heath, bare rock, sparse vegetation, bogs, intertidal flats, water, sea, urban), 11 068 vertices clipped to the tile, median segment 54 m against 10 m cells. The EEA's public ArcGIS service (`image.discomap.eea.europa.eu`, `Corine/CLC2018_WM`) returns the same 11 068 clipped vertices and is the route for anyone without the file. What it forces on 16b's design: neighbouring polygons share their boundaries, so each shared edge arrives twice; the polygons run past the domain and must be clipped; the extract is committed as a fixture with the Copernicus attribution. Placed before 20c because 20c may split constraint segments and should be designed and measured on inputs that have interior ones | designed in 16's R6, no increment file yet; after the serial phase, before 20c | `docs/increments/16-domain-polygon.md` (R6) | +| 16b | Interior polygons and polylines as constraints ("terrain polygons": lakes, land cover, roads, rivers): `--features PATH`, a GeoJSON `FeatureCollection`, each feature naming a vocabulary property; closed or open `Breakline`s, crossings noded, off-node vertices with bilinear z. **Its working example is real data** (Ola, 2026-09-27): CORINE Land Cover 2018 over the benchmark tile `7908_3_10m_z33.tif`. The source is Ola's local copy, `rasputin_data/corine_sql/.../U2018_CLC2018_V2020_20u1.gpkg` (8.2 GB, EPSG:3035, a sibling of this repository, which also holds the 254-tile DTM10 archive for gap 6). It reads without GDAL: sqlite3 over its R-tree, the GeoPackage blob header stripped, `shapely.wkb`, then `pyproj` to 25833, so CRS stops in Python as before. Probed 2026-09-27 in 0.2 s: 60 polygons in 8 classes (heath, bare rock, sparse vegetation, bogs, intertidal flats, water, sea, urban), 11 068 vertices clipped to the tile, median segment 54 m against 10 m cells. The EEA's public ArcGIS service (`image.discomap.eea.europa.eu`, `Corine/CLC2018_WM`) returns the same 11 068 clipped vertices and is the route for anyone without the file. What it forces on 16b's design: neighbouring polygons share their boundaries, so each shared edge arrives twice; the polygons run past the domain and must be clipped; the extract is committed as a fixture with the Copernicus attribution. Placed before 20c because 20c may split constraint segments and should be designed and measured on inputs that have interior ones | **designed** (`@architect`, 2026-09-28): two PRs, 16b-0 (the noder's verifier indexed: it is quadratic, 25 s of a 30 s CORINE run) and 16b-1/2 (GeoJSON and GeoPackage features, class maps, clip as linework, the noder merges shared edges); Q1-Q6 open for Ola; before 20c | `docs/increments/16b-terrain-polygons.md`, `docs/increments/16-domain-polygon.md` (R6) | | 20c | Soft quality criterion: a penalty that each Steiner node or constraint split must pay for in angle gained, instead of 20's hard 25°; applied at the start and during DEM refinement; may split constraint segments when that improves the mesh. Ola's rulings on 20's C1-C3 | to design after 16b (`@architect` measures cost against 20 first) | `docs/increments/20-start-quality.md` (Ola's rulings) | | — | Auto-catchment: the watershed upstream of a coordinate, computed from the DEM and handed to `--domain`, so a catchment no longer has to be supplied as a file (Ola, 2026-09-27: "not far into the future"). The textbook route is depression handling (Priority-Flood, Barnes, Lehman and Mulla 2014), D8 flow directions (O'Callaghan and Mark 1984) and accumulation, the pour point snapped to the strongest flow nearby, the upstream cells traced and their outline turned into a polygon; the literature check is `@architect`'s. Open for its design: whether it runs in the C++ core (a 10 m tile is 25 M cells); how a stair-stepped cell outline becomes a domain polygon, which meets input coarsening; and that a real catchment crosses tile edges, so it needs gap 6 (a DEM in several tiles) first. Legacy has nothing on it (`grep -rliE "watershed|flow.?acc|flow.?dir|pour.?point|catchment" legacy` returns no files) | to design; after gap 6, which it needs; placed after 20c, can move ahead of it on Ola's word | none yet | | — | `raster/`: grid-to-world geometry and bilinear sampling | shipped (`7785fea`), **no record** | none — predates the protocol | diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index 591e2458..1516f575 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -279,7 +279,7 @@ What matters, read directly: "lake material"). **Carried:** the CLC code as the key, and "5xx is water" as the one rule the default map uses (R4). **Not carried:** GML, `lxml`, and the whole file parsed per call. -- `gml_repository.py:181-182`: `constraints()` returns `[]`. +- `legacy/rasputin/gml_repository.py:181-182`: `constraints()` returns `[]`. The legacy never made land cover a constraint. `:184-226`: it labelled the **finished** mesh by testing each cell centre against every polygon in Python (`# TODO: Move to C++ for speed!`), and raised on a centre inside @@ -288,7 +288,7 @@ What matters, read directly: 16c's (Q2), and a flood fill over unconstrained edges (Triangle's regional attributes) replaces the per-centre test, which is ambiguous for a triangle thinner than the snap. -- `gml_repository.py:155-159`: the data CRS from the GML's `srsName`, via +- `legacy/rasputin/gml_repository.py:155-159`: the data CRS from the GML's `srsName`, via `+init=`. Not carried: CRS comes from the GeoPackage's own `gpkg_spatial_ref_sys` (R3) through `crs.parse_crs`. From 4b286cfbcca23d42a02d3591d4c7250d05ce630d Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 01:13:47 +0200 Subject: [PATCH 07/19] 16b: CORINE attribution checked against the EEA record Co-Authored-By: Claude Opus 5.5 --- docs/increments/16b-terrain-polygons.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index 1516f575..3b76b416 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -866,11 +866,18 @@ Everything else is unit, property or integration testing: Monitoring Service, European Environment Agency), that it is clipped and re-encoded, the funding sentence, and no endorsement. The `corine` maps' `notice` carries a one-line form of the same into every mesh built from - them (R10). **Not verified:** the exact attribution line the Copernicus - Land Monitoring Service website asks for today (recalled as "© European - Union, Copernicus Land Monitoring Service 2018, European Environment Agency - (EEA)"); no web access in this round. Ola or the main session should check - `land.copernicus.eu` before the extract is committed. + them (R10). **Checked by the main session (2026-09-28)** against the EEA's + current catalogue record for CLC2018 vector v2020_20u1 + (`sdi.eea.europa.eu/catalogue/copernicus/api/records/71c95a07-e296-44fc-b22b-415f42acfdf0`): + the governing act is now Regulation (EU) 2021/696 with access still under + 1159/2013, and the record states **three** conditions: inform the public of + the source; state clearly where the data were adapted or modified; do not + convey EU endorsement. It prescribes no exact wording and does not repeat + the funding sentence (keeping it, from the download's own metadata, is + harmless). The recalled "© European Union, Copernicus Land Monitoring + Service 2018, European Environment Agency (EEA)" satisfies condition 1. + SQLite's R-tree module is present locally (3.53.4); CI's Linux Python is + not yet checked, so 16b-1's first CI run settles it. - **Found in passing:** `tests/fixtures/corine/0000_4326_corine2018_4e6064_GML.gml` (30 MB, 399 CORINE features over Norway in EPSG:4326, written by OGR, committed with the foundation reset `3096ccc`) carries no attribution, and From 6b4fcb9021b78628217ad8a21c054199ce9a1b31 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 07:39:58 +0200 Subject: [PATCH 08/19] 16b: Ola's rulings on Q1-Q6 (Q6: keep the legacy GML, attribute it, read it) Co-Authored-By: Claude Opus 5.5 --- docs/increments/16b-terrain-polygons.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index 3b76b416..d0b2fca3 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -45,6 +45,29 @@ which this increment gives its first real input with interior constraints. (16b) follows the same path" (ROADMAP, "inputs in their own CRS"). 6. **Order of work** (2026-09-27): 15a/15b, then 16b, then auto-catchment. +## Ruled by Ola + +- **2026-09-28, Q1-Q5: "Yes to Q1 - Q5"**, to the recommendations in + "Questions for Ola": (Q1) the verifier's pair search is indexed by sort and + sweep in 16b-0 and stays always on; (Q2) triangle labelling is a separate + increment, 16c; (Q3) new vocabulary bits `land_cover` (bit 7) and `water` + (bit 8); (Q4) one `--features` source per run plus the built-in maps; (Q5) + the quality start is unchanged, @perf records both settings, and 20c + decides. +- **2026-09-28, Q6: (b), after the main session's closer look.** The 30 MB + `tests/fixtures/corine/0000_4326_corine2018_4e6064_GML.gml` is not stray: it + was added in 2020 (`8e30af4`, "Adding data for small test") together with + `7908_3_10m_z33.tif`, the benchmark tile, as the legacy `ingoya_test` pair. + It holds 200 CORINE 2018 polygons around Ingøya in EPSG:4326, in a + Norwegian redistribution's schema (`clc18_kode`, `sl_sdeid`), exported by + OGR as GML2 with `gml:coordinates`. It stays, gains a `NOTICE` (the + Copernicus attribution and this origin), and 16b reads it as a second real + fixture: a small GML2 reader (standard-library XML, no GDAL, about 40 + lines) and a class map for `clc18_kode`. This adds to 16b-1's estimate. +- **16b's own extract is vector** (the GeoPackage's `MULTIPOLYGON`), as is the + GML; the only raster land cover in view is MapBiomas for the São Francisco + basin, which needs a raster-to-polygon step later. + ## What was measured In the scratchpad, never in the tree: `clc_probe.py` (read, reproject, clip, @@ -911,7 +934,7 @@ Everything else is unit, property or integration testing: there as 20c's input. - All gates in `CLAUDE.md` §4, including the TSan job; CI green. -## Questions for Ola +## Questions for Ola (ruled 2026-09-28, see "Ruled by Ola") **Q1. Where may the noder's verification spend its time?** M3: 25 s of a 30 s run is the noder on a 48 km square of CORINE, and it grows with the From 62740a8b5e1c521658f865a402fd7a4a7b04d8de Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 11:23:05 +0200 Subject: [PATCH 09/19] red: increment 16b-0 (the verifier's pair search by sort and sweep) 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 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 --- docs/increments/16b-terrain-polygons.md | 52 ++ tests/cpp/CMakeLists.txt | 21 + .../property/prop_noding_verifier_sweep.cpp | 866 ++++++++++++++++++ tests/cpp/support/verifier_oracle.hpp | 133 +++ 4 files changed, 1072 insertions(+) create mode 100644 tests/cpp/property/prop_noding_verifier_sweep.cpp create mode 100644 tests/cpp/support/verifier_oracle.hpp diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index d0b2fca3..1ec94354 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -637,6 +637,58 @@ construction", is false for land cover. `O(n log n + k)`. The header comment says so, replacing the false premise. - **The brute force stays, as the test oracle**, in `tests/cpp/support/`. +#### Step 0, run (2026-09-28, battery) + +Scratch `_core` builds of this branch's C++, one unchanged and one with +`check_guarantee_14` returning `pass()` at its first line, through +`clc_nodetime.py` (layout A) on squares centred in `6603_4`: + +| square | segments | `node`, verifier on | `node`, verifier off | +|---|---|---|---| +| 12 km | 3 766 | 0.134 s | 0.003 s | +| 24 km | 14 842 | 2.07 s | 0.012 s | +| 36 km | 30 513 | 8.99 s | 0.027 s | +| 48 km | 51 791 | 24.83 s | 0.047 s | + +The verifier is 99.8 % of `node` at 48 km. 13.8× the segments cost 185× with +it on (13.8² = 190) and 15× with it off, so the quadratic goes with it. Both +builds returned `Ok`. M3's attribution holds, and 16b-0 is scoped as designed. + +#### Pinned by the red suite (16b-0) + +`@tester` chose the following in the red step. +`tests/cpp/property/prop_noding_verifier_sweep.cpp` holds it; the oracle is +`tests/cpp/support/verifier_oracle.hpp`. + +- In `namespace terrain::noding`, reachable through + `include/terrain/noding/noded_pslg_builder.hpp`: + + ```cpp + template + [[nodiscard]] bool sweep_box_pairs(std::span edges, + std::span cells, + OnEdgePair&& on_edge_pair, // bool(std::size_t, std::size_t) + OnEdgeCell&& on_edge_cell); // bool(std::size_t edge, std::size_t cell) + ``` + + It is kernel-free. It calls `on_edge_pair(i, j)` **exactly once** for every + unordered pair of edge boxes whose closed boxes intersect, with `i != j` in + either order. It calls `on_edge_cell(e, c)` exactly once for every edge box + and cell box whose closed boxes intersect. It makes no other call and never + pairs two cells. "Exactly" is stricter than R8's "false positives are free". + R8's own algorithm meets it, and it is what lets the suite see an item kept + active too long. +- **A callback returning `false` stops the sweep.** No further call is made + and the function returns `false`. It returns `true` if no callback returned + `false`. The same input gives the same calls in the same order. +- `NodedPslgBuilder::build()` returns the brute force's status on every + candidate (I6). Which violating pair the message names is not pinned. +- `node`'s output on six integer fixtures at spacing 0.125 is pinned by a + digest over integers only (status, grid points, chains, indices, masks, + `node_of_input_vertex`), recorded from 6b4fcb9's brute-force verifier. +- Registration: the suite builds only once `noded_pslg_builder.hpp` names + `sweep_box_pairs` (the 21a/21b guard). The review drops the guard. + ### R9. Vertex density, z, and refinement - **Vertices are used as given** (Ola's direction 1): no simplification, no diff --git a/tests/cpp/CMakeLists.txt b/tests/cpp/CMakeLists.txt index f179e0a1..61715689 100644 --- a/tests/cpp/CMakeLists.txt +++ b/tests/cpp/CMakeLists.txt @@ -301,3 +301,24 @@ target_link_libraries(test_mesh_lawson_stack PRIVATE Threads::Threads) # starts no threads, so it is not in the TSan job. add_terrain_backend_test(test_mesh_lattice_incircle unit/test_mesh_lattice_incircle.cpp) + +# The verifier's pair search by sort and sweep, increment 16b-0 +# (docs/increments/16b-terrain-polygons.md, R8, I6 and "Pinned by the red suite +# (16b-0)"). prop_noding_verifier_sweep is the invariant-critical suite: +# sweep_box_pairs reports exactly the brute force's box pairs, build() returns +# the brute-force verifier's status (tests/cpp/support/verifier_oracle.hpp, +# frozen at 6b4fcb9), and node's output digests are today's. It starts no +# threads, so it is not in the TSan job. +# +# RED UNTIL THE INTERFACE EXISTS: registered only once noded_pslg_builder.hpp +# names sweep_box_pairs (defined there or in a header it includes), so the red +# does not break the rest of the build. The header is a configure dependency, +# so the green commit's edit registers the suite without a manual cmake. The +# review drops this guard. +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + "${PROJECT_SOURCE_DIR}/include/terrain/noding/noded_pslg_builder.hpp") +file(READ "${PROJECT_SOURCE_DIR}/include/terrain/noding/noded_pslg_builder.hpp" _builder_16b0) +string(FIND "${_builder_16b0}" "sweep_box_pairs" _sweep_box_pairs_at) +if(NOT _sweep_box_pairs_at EQUAL -1) + add_terrain_backend_test(prop_noding_verifier_sweep property/prop_noding_verifier_sweep.cpp) +endif() diff --git a/tests/cpp/property/prop_noding_verifier_sweep.cpp b/tests/cpp/property/prop_noding_verifier_sweep.cpp new file mode 100644 index 00000000..f60cc75e --- /dev/null +++ b/tests/cpp/property/prop_noding_verifier_sweep.cpp @@ -0,0 +1,866 @@ +// Property suite for increment 16b-0: the verifier's pair search by sort and +// sweep (docs/increments/16b-terrain-polygons.md, R8, I6 and "Pinned by the red +// suite (16b-0)"). +// +// INVARIANT-CRITICAL, mutation round required. Three layers, each able to fail +// where the others cannot: +// +// A. sweep_box_pairs, the pinned kernel-free pair search, against +// verifier_oracle::brute_force_box_pairs: EXACTLY the pairs of closed boxes +// that intersect, each reported once, no cell-cell pair. This is the only +// layer that can see a sweep which drops boxes touching at one x: on-grid +// input never gives a violating pair whose boxes meet only at an x (two +// segments sharing just that x meet at an endpoint, which classify calls +// Touching, and a padded cell's x-bounds sit at half-integer grid offsets). +// B. NodedPslgBuilder::build() against brute_force_guarantee_14 (I6): the +// same status on random and adversarial candidates, built by hand as in +// test_noding_noded_pslg_builder.cpp, plus candidates made by adding one +// edge or one node to a real noder output. +// C. node unchanged: its output on integer fixtures at a dyadic spacing, +// digested over integers only, equals the digest recorded from today's +// brute-force verifier (6b4fcb9) before any production change; and every +// Ok output of node on random polylines passes the brute force. +// +// The existing 14(a) and 14(b) refusal fixtures stay in +// test_noding_noded_pslg_builder.cpp, unchanged. +// +// DefaultKernel only, for the reason prop_noding_no_crossings.cpp gives. +// Random inputs draw raw std::mt19937_64 words, never a std::*_distribution, +// whose output the standard does not fix across libraries. + +#include +#include +#include + +#include "noding_generators.h" +#include "verifier_oracle.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using terrain::Box2; +using terrain::Chain; +using terrain::ChainRole; +using terrain::EdgeProperties; +using terrain::GridPoint; +using terrain::Point2; +using terrain::SnapGrid; +using terrain::noding::NodedPslgBuilder; +using terrain::noding::NodeOptions; +using terrain::noding::NodeStatus; +using terrain::noding::sweep_box_pairs; +using terrain::pred::DefaultKernel; +using verifier_oracle::BoxPairs; +using verifier_oracle::brute_force_box_pairs; + +namespace { + +// =========================================================================== +// A. sweep_box_pairs +// =========================================================================== + +struct SweepRun { + BoxPairs pairs; + std::size_t calls{0}; + bool repeated{false}; // a pair reported twice + bool malformed{false}; // i == j, or an index out of range + bool completed{false}; // the return value +}; + +// Runs the sweep, recording every call. stop_after == 0 never stops; k > 0 +// makes the k-th call (of either kind) return false. +[[nodiscard]] SweepRun run_sweep(std::span edges, std::span cells, + std::size_t stop_after = 0) { + SweepRun run; + auto keep_going = [&run, stop_after] { return stop_after == 0 || run.calls < stop_after; }; + run.completed = sweep_box_pairs( + edges, cells, + [&](std::size_t i, std::size_t j) { + ++run.calls; + if (i == j || i >= edges.size() || j >= edges.size()) { + run.malformed = true; + } + if (!run.pairs.edge_edge.emplace(std::min(i, j), std::max(i, j)).second) { + run.repeated = true; + } + return keep_going(); + }, + [&](std::size_t e, std::size_t c) { + ++run.calls; + if (e >= edges.size() || c >= cells.size()) { + run.malformed = true; + } + if (!run.pairs.edge_cell.emplace(e, c).second) { + run.repeated = true; + } + return keep_going(); + }); + return run; +} + +void require_exact_pairs(const std::vector& edges, const std::vector& cells) { + const BoxPairs want = brute_force_box_pairs(edges, cells); + const SweepRun got = run_sweep(edges, cells); + INFO(edges.size() << " edge boxes, " << cells.size() << " cell boxes; oracle " + << want.edge_edge.size() << " edge pairs, " << want.edge_cell.size() + << " edge-cell pairs; sweep " << got.pairs.edge_edge.size() << " and " + << got.pairs.edge_cell.size()); + REQUIRE(got.completed); + REQUIRE_FALSE(got.malformed); + REQUIRE_FALSE(got.repeated); + REQUIRE(got.pairs.edge_edge == want.edge_edge); + REQUIRE(got.pairs.edge_cell == want.edge_cell); + REQUIRE(got.calls == want.edge_edge.size() + want.edge_cell.size()); +} + +[[nodiscard]] Box2 box(double x0, double y0, double x1, double y1) { + return Box2{Point2{x0, y0}, Point2{x1, y1}}; +} + +// Random boxes on a small integer lattice, so that equal xmin, xmax == another's +// xmin, zero width, zero height and corner contact are common rather than rare. +// A tenth are long: they span most of the range, which is the case that keeps +// an item active across every other arrival. +[[nodiscard]] std::vector random_boxes(std::mt19937_64& rng, std::size_t n, + std::uint64_t range, double scale, + double offset) { + std::vector out; + out.reserve(n); + auto extent = [&rng, range]() -> std::uint64_t { + switch (rng() % 10) { + case 0: return 0; // zero width or height + case 1: return range - rng() % 3; // long + default: return rng() % 4; + } + }; + for (std::size_t k = 0; k < n; ++k) { + const std::uint64_t x0 = rng() % range; + const std::uint64_t y0 = rng() % range; + const std::uint64_t x1 = x0 + extent(); + const std::uint64_t y1 = y0 + extent(); + out.push_back(box(static_cast(x0) * scale + offset, + static_cast(y0) * scale + offset, + static_cast(x1) * scale + offset, + static_cast(y1) * scale + offset)); + } + return out; +} + +TEST_CASE("sweep_box_pairs: nothing to pair", "[16b-0][sweep]") { + const std::vector none; + const std::vector one{box(0, 0, 1, 1)}; + const std::vector cells{box(0, 0, 1, 1), box(0, 0, 1, 1), box(0.5, 0.5, 2, 2)}; + + SECTION("no boxes at all") { require_exact_pairs(none, none); } + SECTION("one edge box") { require_exact_pairs(one, none); } + SECTION("cells only: overlapping cells are never paired with each other") { + const SweepRun run = run_sweep(none, cells); + REQUIRE(run.completed); + REQUIRE(run.calls == 0); + } +} + +TEST_CASE("sweep_box_pairs: closed boxes touching in one coordinate are a pair", + "[16b-0][sweep]") { + // Each fixture is given in both index orders: which of the two arrives first + // in the sweep must not matter. + auto both_orders = [](Box2 a, Box2 b) { + require_exact_pairs({a, b}, {}); + require_exact_pairs({b, a}, {}); + require_exact_pairs({a}, {b}); + require_exact_pairs({b}, {a}); + }; + + SECTION("an edge's xmax equal to the other's xmin") { + both_orders(box(0, 0, 10, 1), box(10, 0, 20, 1)); + } + SECTION("two zero-width boxes at the same x, overlapping in y") { + both_orders(box(5, 0, 5, 4), box(5, 2, 5, 6)); + } + SECTION("a zero-width box on the other's right face") { + both_orders(box(0, 0, 10, 10), box(10, 3, 10, 4)); + } + SECTION("ymax equal to the other's ymin, x-intervals overlapping") { + both_orders(box(0, 0, 10, 5), box(3, 5, 4, 9)); + } + SECTION("corners only") { both_orders(box(0, 0, 1, 1), box(1, 1, 2, 2)); } + SECTION("a point box on a corner") { both_orders(box(0, 0, 1, 1), box(1, 0, 1, 0)); } + SECTION("two coincident point boxes") { both_orders(box(3, 3, 3, 3), box(3, 3, 3, 3)); } + SECTION("the same at huge magnitude") { + const double big = 0x1p52; + both_orders(box(-big, big, big, big), box(big, big, 2 * big, 2 * big)); + } +} + +TEST_CASE("sweep_box_pairs: boxes one ulp apart, or apart in only one coordinate, are not " + "a pair", + "[16b-0][sweep]") { + const double after10 = std::nextafter(10.0, 20.0); + auto neither_order = [](Box2 a, Box2 b) { + require_exact_pairs({a, b}, {}); + require_exact_pairs({b, a}, {}); + require_exact_pairs({a}, {b}); + require_exact_pairs({b}, {a}); + }; + + SECTION("one ulp apart in x") { neither_order(box(0, 0, 10, 1), box(after10, 0, 20, 1)); } + SECTION("one ulp apart in y") { neither_order(box(0, 0, 1, 10), box(0, after10, 1, 20)); } + // An item kept active past its xmax, or a sweep that skips the y-test, pairs + // these. + SECTION("x-disjoint, y-equal") { neither_order(box(0, 0, 1, 1), box(2, 0, 3, 1)); } + SECTION("y-disjoint under a long box's x-range") { + neither_order(box(0, 0, 100, 1), box(50, 2, 51, 3)); + } +} + +TEST_CASE("sweep_box_pairs: many boxes at one x", "[16b-0][sweep]") { + // Twenty zero-width boxes on x = 5, end to end and overlapping, and twenty + // cells straddling x = 5. Equal xmin is a tie in the sort key everywhere. + std::vector edges; + std::vector cells; + for (int k = 0; k < 20; ++k) { + edges.push_back(box(5, k, 5, k + 1 + (k % 3))); + cells.push_back(box(5 - (k % 2), k - 0.5, 5 + (k % 2), k + 0.5)); + } + require_exact_pairs(edges, cells); + std::ranges::reverse(edges); + require_exact_pairs(edges, cells); +} + +TEST_CASE("sweep_box_pairs: a long box spanning every other one", "[16b-0][sweep]") { + std::vector edges{box(0, 0, 1000, 1000)}; + std::vector cells; + for (int k = 0; k < 50; ++k) { + edges.push_back(box(20 * k, 20 * k, 20 * k + 5, 20 * k + 5)); + cells.push_back(box(20 * k + 5, 20 * k + 5, 20 * k + 10, 20 * k + 10)); + } + edges.push_back(box(-5, 1000, 1005, 1000)); // along the top face + require_exact_pairs(edges, cells); +} + +TEST_CASE("sweep_box_pairs: an edge arriving after a cell and a cell arriving after an " + "edge are both paired", + "[16b-0][sweep]") { + // The two directions are separate code paths in R8: an arriving edge against + // active cells, an arriving cell against active edges. + require_exact_pairs({box(5, 0, 9, 1)}, {box(0, 0, 6, 1)}); // cell first + require_exact_pairs({box(0, 0, 6, 1)}, {box(5, 0, 9, 1)}); // edge first + require_exact_pairs({box(5, 0, 9, 1)}, {box(5, 0, 6, 1)}); // same xmin + require_exact_pairs({box(6, 0, 9, 1)}, {box(0, 0, 6, 1)}); // cell xmax == edge xmin + require_exact_pairs({box(0, 0, 6, 1)}, {box(6, 0, 9, 1)}); // edge xmax == cell xmin +} + +TEST_CASE("sweep_box_pairs reports exactly the brute force's pairs on random boxes", + "[16b-0][sweep][random]") { + struct Frame { + double scale; + double offset; + }; + // Unit, a non-dyadic scale, huge positive and huge negative coordinates. + const Frame frame = GENERATE(Frame{1.0, 0.0}, Frame{0.1, 0.0}, Frame{1.0, 0x1p51}, + Frame{0.1, -0x1p49}); + const std::uint64_t seed = GENERATE(range(1, 41)); + std::mt19937_64 rng{seed}; + const std::uint64_t range_ = 4 + rng() % 40; + const std::size_t n_edges = rng() % 60; + const std::size_t n_cells = rng() % 60; + const std::vector edges = random_boxes(rng, n_edges, range_, frame.scale, frame.offset); + const std::vector cells = random_boxes(rng, n_cells, range_, frame.scale, frame.offset); + INFO("seed " << seed << ", range " << range_); + require_exact_pairs(edges, cells); +} + +TEST_CASE("sweep_box_pairs stops at the first callback that returns false", + "[16b-0][sweep]") { + std::mt19937_64 rng{7}; + const std::vector edges = random_boxes(rng, 30, 10, 1.0, 0.0); + const std::vector cells = random_boxes(rng, 30, 10, 1.0, 0.0); + const std::size_t total = run_sweep(edges, cells).calls; + REQUIRE(total > 20); // the probe must have something to stop + + for (std::size_t k = 1; k <= total; ++k) { + INFO("stop at call " << k << " of " << total); + const SweepRun run = run_sweep(edges, cells, k); + REQUIRE_FALSE(run.completed); + REQUIRE(run.calls == k); + } +} + +TEST_CASE("sweep_box_pairs is a pure function: the same calls in the same order", + "[16b-0][sweep]") { + std::mt19937_64 rng{11}; + const std::vector edges = random_boxes(rng, 40, 12, 1.0, 0.0); + const std::vector cells = random_boxes(rng, 40, 12, 1.0, 0.0); + auto sequence = [&] { + std::vector> seq; + (void)sweep_box_pairs( + std::span{edges}, std::span{cells}, + [&](std::size_t i, std::size_t j) { seq.emplace_back(i, j); return true; }, + [&](std::size_t e, std::size_t c) { seq.emplace_back(e, 1000 + c); return true; }); + return seq; + }; + REQUIRE(sequence() == sequence()); +} + +// =========================================================================== +// B. NodedPslgBuilder::build() against the brute force (I6) +// =========================================================================== + +struct Candidate { + SnapGrid grid{0.1}; + std::vector vertices; + std::vector chains; + std::vector chain_indices; + std::vector edge_properties; + std::vector node_of_input_vertex; + + // Appends a chain over existing vertex ids, keeping 16's structure. + void add_chain(const std::vector& ids, ChainRole role = ChainRole::Breakline, + EdgeProperties props = EdgeProperties::bit(0)) { + chains.push_back(Chain{static_cast(chain_indices.size()), + static_cast(ids.size()), role, props}); + chain_indices.insert(chain_indices.end(), ids.begin(), ids.end()); + const std::size_t edges = terrain::is_closed(role) ? ids.size() : ids.size() - 1; + edge_properties.insert(edge_properties.end(), edges, props); + } + + // Returns the id of g's vertex, adding it if it is new. + std::uint32_t vertex(GridPoint g) { + const Point2 p = grid.world(g); + for (std::uint32_t k = 0; k < vertices.size(); ++k) { + if (grid.snap(vertices[k]) == g) { + return k; + } + } + vertices.push_back(p); + return static_cast(vertices.size() - 1); + } + + [[nodiscard]] NodeStatus built() const { + return NodedPslgBuilder{grid, vertices, chains, chain_indices, edge_properties, + node_of_input_vertex} + .build() + .status; + } + + [[nodiscard]] NodeStatus oracle() const { + return verifier_oracle::brute_force_guarantee_14(grid, vertices, chains, + chain_indices); + } +}; + +// The first edge of chain `chain` in the flat edge numbering. +[[nodiscard]] std::size_t base_edge_offset(const Candidate& c, std::size_t chain) { + std::size_t e = 0; + for (std::size_t k = 0; k < chain; ++k) { + const Chain& ch = c.chains[k]; + e += terrain::is_closed(ch.role) ? ch.count : ch.count - 1; + } + return e; +} + +// The status must be the oracle's, and the fixture's own expectation, so a +// fixture that stops meaning what its name says fails here rather than quietly +// comparing two agreeing answers to the wrong question. +void require_verdict(const Candidate& c, NodeStatus expected) { + REQUIRE(c.oracle() == expected); + REQUIRE(c.built() == expected); +} + +enum class Layout { Free, AxisLines, Diagonals }; + +// A random candidate in grid space, then mapped to the world through +// world(g + origin). Structure (16), 11, 12 and 13 hold by construction, so the +// only status build() can return is 14's. +[[nodiscard]] Candidate random_candidate(std::uint64_t seed, double spacing, + std::int64_t origin, Layout layout) { + std::mt19937_64 rng{seed}; + Candidate c; + c.grid = SnapGrid{spacing}; + const std::int64_t w = 4 + static_cast(rng() % 12); + + auto pick = [&]() -> GridPoint { + const auto a = static_cast(rng() % static_cast(w)); + const auto b = static_cast(rng() % static_cast(w)); + switch (layout) { + case Layout::Free: return {a, b}; + case Layout::AxisLines: return rng() % 2 ? GridPoint{w / 2, b} : GridPoint{a, w / 3}; + case Layout::Diagonals: return rng() % 2 ? GridPoint{a, a} : GridPoint{a, w - 1 - a}; + } + return {a, b}; + }; + + const std::size_t n_vertices = 3 + rng() % 10; + for (std::size_t k = 0; k < n_vertices; ++k) { + const GridPoint g = pick(); + (void)c.vertex(GridPoint{g.ix + origin, g.iy + origin}); + } + const auto n = static_cast(c.vertices.size()); + if (n < 3) { + return c; + } + + const std::size_t n_chains = 1 + rng() % 4; + for (std::size_t k = 0; k < n_chains; ++k) { + const bool closed = rng() % 5 == 0; + const std::size_t len = (closed ? 3 : 2) + rng() % 3; + std::vector ids{static_cast(rng() % n)}; + while (ids.size() < len) { + const auto next = static_cast(rng() % n); + if (next != ids.back()) { + ids.push_back(next); + } + } + if (closed && ids.back() == ids.front()) { + ids.back() = (ids.front() + 1) % n == ids[ids.size() - 2] + ? (ids.front() + 2) % n + : (ids.front() + 1) % n; + } + if (closed && (ids.back() == ids.front() || ids.back() == ids[ids.size() - 2])) { + continue; // n == 3 corner cases: skip rather than emit a zero-length edge + } + c.add_chain(ids, closed ? ChainRole::Hole : ChainRole::Breakline, + EdgeProperties::bit(static_cast(k % 3))); + } + if (c.chains.empty()) { + c.add_chain({0, 1}); + } + return c; +} + +TEST_CASE("I6: build() returns the brute force's status on random candidates", + "[16b-0][builder][random]") { + struct Setting { + double spacing; + std::int64_t origin; + }; + // A non-dyadic spacing, a dyadic one, and grids pushed next to kMaxGridIndex + // where a world coordinate's ulp is a large part of a cell. + const Setting s = GENERATE(Setting{0.1, 0}, Setting{0.125, -40}, Setting{1.0, 0}, + Setting{0.1, (std::int64_t{1} << 50) + 12345}, + Setting{1.0, terrain::kMaxGridIndex - 64}, + Setting{0.3, -(terrain::kMaxGridIndex - 64)}); + const Layout layout = GENERATE(Layout::Free, Layout::AxisLines, Layout::Diagonals); + + std::size_t ok = 0; + std::size_t refused = 0; + for (std::uint64_t seed = 1; seed <= 300; ++seed) { + const Candidate c = random_candidate(seed, s.spacing, s.origin, layout); + const NodeStatus want = c.oracle(); + INFO("seed " << seed << ", spacing " << s.spacing << ", origin " << s.origin); + REQUIRE(c.built() == want); + (want == NodeStatus::Ok ? ok : refused) += 1; + } + // The probe must be able to fail both ways: a family that is all Ok cannot + // catch a sweep that misses pairs, and one that is all refused cannot catch + // a sweep that invents them. + INFO(ok << " Ok, " << refused << " refused"); + REQUIRE(ok >= 15); + REQUIRE(refused >= 15); +} + +// Real noder output is the "nearly noded" case: every pair is legal until one +// thing is added, and then exactly the pairs that touch the addition matter. +[[nodiscard]] Candidate from_noded(const terrain::NodedPslg& p) { + Candidate c; + c.grid = p.grid(); + c.vertices.assign(p.vertices().begin(), p.vertices().end()); + c.chains.assign(p.chains().begin(), p.chains().end()); + c.chain_indices.assign(p.chain_indices().begin(), p.chain_indices().end()); + c.edge_properties.assign(p.edge_properties().begin(), p.edge_properties().end()); + c.node_of_input_vertex.assign(p.node_of_input_vertex().begin(), + p.node_of_input_vertex().end()); + return c; +} + +TEST_CASE("I6: build() returns the brute force's status on noder output with one edge or " + "one node added", + "[16b-0][builder][random]") { + std::size_t ok = 0; + std::size_t refused = 0; + for (std::uint64_t seed = 1; seed <= 40; ++seed) { + terrain::testing::PolylineOptions opts; + opts.breakline_count = 6; + opts.reach = seed % 2 ? 100.0 : 15.0; + const terrain::Pslg pslg = + terrain::testing::to_pslg(terrain::testing::random_polylines(seed, opts)); + const auto noded = terrain::noding::node(pslg, NodeOptions{0.1}); + REQUIRE(noded.ok()); + const Candidate base = from_noded(*noded.pslg); + REQUIRE(base.oracle() == NodeStatus::Ok); + REQUIRE(base.built() == NodeStatus::Ok); + + std::mt19937_64 rng{seed * 7919}; + for (int trial = 0; trial < 12; ++trial) { + Candidate c = base; + const auto n = static_cast(c.vertices.size()); + if (trial % 3 == 0) { + // An edge between two existing nodes. + const auto a = static_cast(rng() % n); + const auto b = static_cast(rng() % n); + if (a == b) { + continue; + } + c.add_chain({a, b}); + } else { + // A lone node at, or one cell beside, a random edge's midpoint: + // grazes, near misses and T-junctions for 14(b). + const std::size_t e = rng() % c.edge_properties.size(); + std::size_t chain = 0; + while (chain + 1 < c.chains.size() && + e >= base_edge_offset(c, chain + 1)) { + ++chain; + } + const std::size_t k = e - base_edge_offset(c, chain); + const Chain& ch = c.chains[chain]; + const std::uint32_t u = c.chain_indices[ch.begin + k]; + const std::uint32_t v = c.chain_indices[ch.begin + (k + 1) % ch.count]; + const GridPoint gu = c.grid.snap(c.vertices[u]); + const GridPoint gv = c.grid.snap(c.vertices[v]); + const GridPoint mid{(gu.ix + gv.ix) / 2 + static_cast(rng() % 3) - 1, + (gu.iy + gv.iy) / 2 + static_cast(rng() % 3) - 1}; + const auto before = c.vertices.size(); + (void)c.vertex(mid); + if (c.vertices.size() == before) { + continue; + } + } + const NodeStatus want = c.oracle(); + INFO("seed " << seed << ", trial " << trial); + REQUIRE(c.built() == want); + (want == NodeStatus::Ok ? ok : refused) += 1; + } + } + INFO(ok << " Ok, " << refused << " refused"); + REQUIRE(ok >= 20); + REQUIRE(refused >= 20); +} + +// --- Named adversarial candidates ------------------------------------------ +// +// Built on grid indices. Each asserts the oracle's verdict AND a stated one. + +[[nodiscard]] Candidate on_grid(double spacing = 0.1) { + Candidate c; + c.grid = SnapGrid{spacing}; + return c; +} + +TEST_CASE("I6 fixtures: duplicate edges in both orientations are legal", "[16b-0][builder]") { + Candidate c = on_grid(); + const auto a = c.vertex({0, 0}); + const auto b = c.vertex({10, 3}); + const auto d = c.vertex({20, 0}); + c.add_chain({a, b, d}); + c.add_chain({d, b, a}, ChainRole::Breakline, EdgeProperties::bit(1)); + c.add_chain({a, b}, ChainRole::Breakline, EdgeProperties::bit(2)); + require_verdict(c, NodeStatus::Ok); +} + +TEST_CASE("I6 fixtures: collinear partial overlaps are refused", "[16b-0][builder]") { + SECTION("vertical, the two boxes sharing one x (zero width)") { + Candidate c = on_grid(); + c.add_chain({c.vertex({5, 0}), c.vertex({5, 4})}); + c.add_chain({c.vertex({5, 2}), c.vertex({5, 6})}); + require_verdict(c, NodeStatus::NotConverged); + } + SECTION("horizontal") { + Candidate c = on_grid(); + c.add_chain({c.vertex({0, 5}), c.vertex({4, 5})}); + c.add_chain({c.vertex({2, 5}), c.vertex({6, 5})}); + require_verdict(c, NodeStatus::NotConverged); + } + SECTION("diagonal, one containing the other") { + Candidate c = on_grid(); + c.add_chain({c.vertex({0, 0}), c.vertex({9, 9})}); + c.add_chain({c.vertex({3, 3}), c.vertex({6, 6})}); + require_verdict(c, NodeStatus::NotConverged); + } +} + +TEST_CASE("I6 fixtures: vertical edges stacked end to end at one x are legal", + "[16b-0][builder]") { + Candidate c = on_grid(); + std::vector ids; + for (std::int64_t k = 0; k <= 12; k += 3) { + ids.push_back(c.vertex({7, k})); + } + for (std::size_t k = 0; k + 1 < ids.size(); ++k) { + c.add_chain({ids[k], ids[k + 1]}); + } + // And three more zero-width columns beside it, touching nothing. + for (std::int64_t x = 9; x <= 11; ++x) { + c.add_chain({c.vertex({x, 0}), c.vertex({x, 12})}); + } + require_verdict(c, NodeStatus::Ok); +} + +TEST_CASE("I6 fixtures: a T-junction is refused, a node two cells off is not", + "[16b-0][builder]") { + SECTION("an endpoint in the other edge's interior") { + Candidate c = on_grid(); + c.add_chain({c.vertex({0, 0}), c.vertex({20, 0})}); + c.add_chain({c.vertex({10, 0}), c.vertex({10, 8})}); + require_verdict(c, NodeStatus::NotConverged); + } + SECTION("a lone node on a vertical edge") { + Candidate c = on_grid(); + c.add_chain({c.vertex({4, 0}), c.vertex({4, 20})}); + (void)c.vertex({4, 11}); + require_verdict(c, NodeStatus::NotConverged); + } + SECTION("a lone node two cells off") { + Candidate c = on_grid(); + c.add_chain({c.vertex({0, 0}), c.vertex({20, 0})}); + (void)c.vertex({10, 2}); + require_verdict(c, NodeStatus::Ok); + } +} + +TEST_CASE("I6 fixtures: an edge through a cell corner meets that cell", "[16b-0][builder]") { + // (0,0)-(1,1) in grid units passes through (1/2, 1/2), the shared corner of + // the cells of (0,0), (1,0), (0,1) and (1,1): closed cells, so (1,0) and + // (0,1) are met. + Candidate c = on_grid(0.125); + c.add_chain({c.vertex({0, 0}), c.vertex({1, 1})}); + (void)c.vertex({1, 0}); + require_verdict(c, NodeStatus::NotConverged); +} + +TEST_CASE("I6 fixtures: a node whose cell box meets an edge's box only at a corner", + "[16b-0][builder]") { + // The edge (0,0)-(4,2) has box [0,4]x[0,2]; the cell of (5,3) is + // [4.5,5.5]x[2.5,3.5], and padded by one spacing its box overlaps the + // edge's box in the corner [3.5,4]x[1.5,2] only. The segment does not reach + // the cell: a candidate pair for the sweep, Ok for the predicate. + Candidate c = on_grid(0.125); + c.add_chain({c.vertex({0, 0}), c.vertex({4, 2})}); + (void)c.vertex({5, 3}); + require_verdict(c, NodeStatus::Ok); +} + +TEST_CASE("I6 fixtures: one long edge spanning every other box", "[16b-0][builder]") { + SECTION("with a node on it far along the sweep: refused") { + Candidate c = on_grid(); + c.add_chain({c.vertex({0, 0}), c.vertex({1000, 1000})}); + for (std::int64_t k = 0; k < 40; ++k) { + c.add_chain({c.vertex({25 * k + 3, 25 * k}), c.vertex({25 * k + 5, 25 * k})}); + } + require_verdict(c, NodeStatus::Ok); + (void)c.vertex({997, 997}); + require_verdict(c, NodeStatus::NotConverged); + } + SECTION("crossed by the last edge in x order: refused") { + Candidate c = on_grid(); + c.add_chain({c.vertex({0, 0}), c.vertex({1000, 0})}); + for (std::int64_t k = 1; k < 40; ++k) { + c.add_chain({c.vertex({25 * k, 3}), c.vertex({25 * k + 2, 9})}); + } + require_verdict(c, NodeStatus::Ok); + c.add_chain({c.vertex({999, -3}), c.vertex({999, 3})}); + require_verdict(c, NodeStatus::NotConverged); + } +} + +TEST_CASE("I6 fixtures: a crossing next to kMaxGridIndex", "[16b-0][builder]") { + const std::int64_t o = terrain::kMaxGridIndex - 32; + Candidate c = on_grid(1.0); + c.add_chain({c.vertex({o, o}), c.vertex({o + 20, o + 20})}); + require_verdict(c, NodeStatus::Ok); + c.add_chain({c.vertex({o, o + 20}), c.vertex({o + 20, o})}); + require_verdict(c, NodeStatus::NotConverged); + + Candidate t = on_grid(1.0); + t.add_chain({t.vertex({-o, -o}), t.vertex({-o + 30, -o})}); + (void)t.vertex({-o + 17, -o}); + require_verdict(t, NodeStatus::NotConverged); +} + +// =========================================================================== +// C. node unchanged +// =========================================================================== + +class Fnv { +public: + void add(std::uint64_t v) noexcept { + for (int k = 0; k < 8; ++k) { + h_ ^= (v >> (8 * k)) & 0xffu; + h_ *= 1099511628211ull; + } + } + [[nodiscard]] std::uint64_t value() const noexcept { return h_; } + +private: + std::uint64_t h_{14695981039346656037ull}; +}; + +// Integers only: the status, every node's GridPoint, the chains, the flat +// indices, the masks and node_of_input_vertex. Doubles are left out so FMA +// contraction and libm cannot move the value while the noder's decisions stand. +[[nodiscard]] std::uint64_t noded_digest(const terrain::noding::NodeOutcome& out) { + Fnv f; + f.add(static_cast(out.status)); + if (!out.ok()) { + return f.value(); + } + const terrain::NodedPslg& p = *out.pslg; + f.add(p.vertices().size()); + for (const Point2& v : p.vertices()) { + const GridPoint g = p.grid().snap(v); + f.add(static_cast(g.ix)); + f.add(static_cast(g.iy)); + } + for (const Chain& ch : p.chains()) { + f.add(ch.begin); + f.add(ch.count); + f.add(static_cast(ch.role)); + } + for (const std::uint32_t i : p.chain_indices()) { + f.add(i); + } + for (const EdgeProperties e : p.edge_properties()) { + f.add(e.bits()); + } + for (const std::uint32_t i : p.node_of_input_vertex()) { + f.add(i); + } + return f.value(); +} + +struct Lines { + std::vector vertices; + std::vector> runs; + std::vector roles; + + std::uint32_t at(double x, double y) { + vertices.push_back(Point2{x, y}); + return static_cast(vertices.size() - 1); + } + void line(std::vector> pts, + ChainRole role = ChainRole::Breakline) { + std::vector run; + for (const auto& [x, y] : pts) { + run.push_back(at(x, y)); + } + runs.push_back(std::move(run)); + roles.push_back(role); + } + [[nodiscard]] terrain::Pslg pslg() const { + terrain::PslgBuilder b{vertices}; + for (std::size_t c = 0; c < runs.size(); ++c) { + b.add_chain(runs[c], roles[c], EdgeProperties::bit(static_cast(c % 4))); + } + auto r = std::move(b).build(); + REQUIRE(r.ok()); + return std::move(*r.pslg); + } +}; + +[[nodiscard]] Lines square(double m) { + Lines l; + l.line({{0, 0}, {m, 0}, {m, m}, {0, m}}, ChainRole::Outer); + return l; +} + +[[nodiscard]] Lines fixture(int which) { + Lines l = square(40); + switch (which) { + case 0: // a hash: two by two crossing lines + l.line({{10, 2}, {10, 38}}); + l.line({{30, 2}, {30, 38}}); + l.line({{2, 10}, {38, 10}}); + l.line({{2, 30}, {38, 30}}); + break; + case 1: // a star of six lines through one point + for (int k = 0; k < 6; ++k) { + const double dx = std::array{1, 2, 3, 1, -1, 5}[k]; + const double dy = std::array{0, 1, 3, 3, 2, -2}[k]; + l.line({{20 - 3 * dx, 20 - 3 * dy}, {20 + 3 * dx, 20 + 3 * dy}}); + } + break; + case 2: // collinear partial overlaps on one line, and on the outer ring + l.line({{0, 5}, {10, 5}}); + l.line({{5, 5}, {15, 5}}); + l.line({{7, 5}, {20, 5}}); + l.line({{3, 0}, {17, 0}}); + break; + case 3: // a road along a river, reversed, and a T-junction onto it + l.line({{2, 20}, {20, 21}, {38, 20}}); + l.line({{38, 20}, {20, 21}, {2, 20}}); + l.line({{20, 21}, {20, 35}}); + l.line({{11, 20.5}, {11, 3}}); + break; + case 4: // many verticals at one x, overlapping and crossed + for (int k = 0; k < 6; ++k) { + l.line({{17, 2.0 + 5 * k}, {17, 9.0 + 5 * k}}); + } + l.line({{3, 3}, {37, 37}}); + break; + default: { // 40 random polylines from raw mt19937_64 words + l = square(1000); + std::mt19937_64 rng{16}; + for (int k = 0; k < 40; ++k) { + std::vector> pts; + for (int v = 0; v < 3; ++v) { + pts.emplace_back(static_cast(1 + rng() % 998), + static_cast(1 + rng() % 998)); + } + l.line(pts); + } + } + } + return l; +} + +TEST_CASE("node's output is today's, digest for digest", "[16b-0][noder]") { + // Recorded from 6b4fcb9's node (the brute-force verifier) with this + // digest, before any production change on the branch. Dyadic spacing and + // integer or half-integer input: nothing here depends on rounding. + constexpr std::uint64_t kRecorded[] = { + 0x80d6a6a851692c2eull, 0xb71d5b267cb92eacull, 0x9632a57ca8ae58bdull, + 0x1facb9f200887f70ull, 0x055c808b84bf1a80ull, 0xe9be0099ef219da5ull, + }; + const int which = GENERATE(range(0, 6)); + const auto out = terrain::noding::node(fixture(which).pslg(), + NodeOptions{0.125}); + INFO("fixture " << which << ", status " << static_cast(out.status) << ", digest 0x" + << std::hex << noded_digest(out)); + REQUIRE(noded_digest(out) == kRecorded[which]); +} + +TEST_CASE("node's Ok output passes the brute force", "[16b-0][noder][random]") { + for (std::uint64_t seed = 1; seed <= 30; ++seed) { + terrain::testing::PolylineOptions opts; + opts.breakline_count = 10; + opts.reach = seed % 3 == 0 ? 100.0 : 20.0; + const auto out = terrain::noding::node( + terrain::testing::to_pslg(terrain::testing::random_polylines(seed, opts)), + NodeOptions{seed % 2 ? 0.1 : 0.125}); + INFO("seed " << seed); + REQUIRE(out.ok()); + REQUIRE(from_noded(*out.pslg).oracle() == NodeStatus::Ok); + } +} + +} // namespace + diff --git a/tests/cpp/support/verifier_oracle.hpp b/tests/cpp/support/verifier_oracle.hpp new file mode 100644 index 00000000..c6dbb457 --- /dev/null +++ b/tests/cpp/support/verifier_oracle.hpp @@ -0,0 +1,133 @@ +#pragma once + +// THE FROZEN ORACLE for increment 16b-0 (docs/increments/16b-terrain-polygons.md, +// R8 and I6): "the brute force stays, as the test oracle, in +// tests/cpp/support/". +// +// brute_force_guarantee_14 is NodedPslgBuilder::check_guarantee_14 from +// include/terrain/noding/noded_pslg_builder.hpp COPIED at 6b4fcb9 (the 16b +// rulings commit), before any production change on the branch. The edits are +// the ones a free function needs: the builder's members become parameters, +// indices_of and edge_count are inlined, and the result is the STATUS alone +// (Ok or NotConverged), because R8 lets the sweep name a different violating +// pair first and no test asserts wording. Every (edge, edge) and every +// (edge, node) pair is tested with the same classify and segment_meets_cell +// the builder uses: the producer's relation, never exact incidence. +// +// brute_force_box_pairs is the other half of the oracle: the pairs of CLOSED +// boxes that intersect, by testing every pair. It is the specification +// sweep_box_pairs is held to. +// +// FROZEN: no test may change this file to agree with new code. +// +// Test-only. Nothing under include/ may include it. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace verifier_oracle { + +using terrain::Box2; +using terrain::Chain; +using terrain::Point2; +using terrain::Segment2; +using terrain::SnapGrid; +using terrain::noding::NodeStatus; +using terrain::noding::SegmentRelation; + +// Guarantee 14 by brute force, over arrays that already satisfy 11, 12, 13 and +// 16 (the caller's generator establishes them; the builder checks them first). +template +[[nodiscard]] NodeStatus brute_force_guarantee_14(const SnapGrid& grid, + std::span vertices, + std::span chains, + std::span chain_indices) { + struct Edge { + Segment2 segment; + std::uint32_t lo, hi; + }; + std::vector edges; + for (const Chain& ch : chains) { + const auto idx = chain_indices.subspan(ch.begin, ch.count); + const std::size_t n = idx.size(); + const std::size_t count = terrain::is_closed(ch.role) ? n : n - 1; + for (std::size_t k = 0; k < count; ++k) { + const std::uint32_t u = idx[k]; + const std::uint32_t v = idx[(k + 1) % n]; + edges.push_back( + Edge{Segment2{vertices[u], vertices[v]}, std::min(u, v), std::max(u, v)}); + } + } + + for (std::size_t i = 0; i < edges.size(); ++i) { + for (std::size_t j = i + 1; j < edges.size(); ++j) { + const SegmentRelation r = + terrain::noding::classify(edges[i].segment, edges[j].segment); + const bool same_pair = edges[i].lo == edges[j].lo && edges[i].hi == edges[j].hi; + if (r == SegmentRelation::Crossing || + (r == SegmentRelation::Overlapping && !same_pair)) { + return NodeStatus::NotConverged; + } + } + } + + for (const Edge& e : edges) { + for (std::uint32_t g = 0; g < vertices.size(); ++g) { + if (g == e.lo || g == e.hi) { + continue; + } + if (terrain::noding::segment_meets_cell(grid, e.segment, + grid.snap(vertices[g]))) { + return NodeStatus::NotConverged; + } + } + } + return NodeStatus::Ok; +} + +[[nodiscard]] inline bool closed_boxes_meet(const Box2& a, const Box2& b) noexcept { + return a.lo().x <= b.hi().x && b.lo().x <= a.hi().x && a.lo().y <= b.hi().y && + b.lo().y <= a.hi().y; +} + +// The unordered edge pairs are stored as (min, max). +struct BoxPairs { + std::set> edge_edge; + std::set> edge_cell; // (edge, cell) + + friend bool operator==(const BoxPairs&, const BoxPairs&) = default; +}; + +[[nodiscard]] inline BoxPairs brute_force_box_pairs(std::span edges, + std::span cells) { + BoxPairs out; + for (std::size_t i = 0; i < edges.size(); ++i) { + for (std::size_t j = i + 1; j < edges.size(); ++j) { + if (closed_boxes_meet(edges[i], edges[j])) { + out.edge_edge.emplace(i, j); + } + } + for (std::size_t c = 0; c < cells.size(); ++c) { + if (closed_boxes_meet(edges[i], cells[c])) { + out.edge_cell.emplace(i, c); + } + } + } + return out; +} + +} // namespace verifier_oracle From cb09085e419f8f088b2a2ea5b8b7db1607bace59 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 11:39:35 +0200 Subject: [PATCH 10/19] 16b-0 tests: the GIL probe's ladder enlarged, now that noding is fast 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 --- tests/python/test_core_noding.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/python/test_core_noding.py b/tests/python/test_core_noding.py index fd58364c..3f963e39 100644 --- a/tests/python/test_core_noding.py +++ b/tests/python/test_core_noding.py @@ -765,8 +765,13 @@ def ladder_pslg() -> Any: """Enough noding work for the ticker to resolve: a grid of crossing breaklines, every crossing a node the driver has to construct. Deterministic by construction -- no random numbers -- and the counts are chosen so that - every crossing is strictly interior and no two coincide.""" - rungs = 48 + every crossing is strictly interior and no two coincide. + + Sized for the probe's 0.05 s floor with margin for slower runners: 300 + rungs a side (90 000 crossings) nodes in ~0.26 s on an M-series Mac. It was + 48 until 16b-0, when the verifier's pair search went from quadratic to sort + and sweep and the old ladder fell to ~0.003 s.""" + rungs = 300 offsets = np.linspace(20.0, 680.0, rungs) ring = np.array([[0.0, 0.0], [700.0, 0.0], [700.0, 700.0], [0.0, 700.0]]) + ORIGIN points = [ring] From aa35a3826b310046f8a5b4514b24bc9ac0233b1b Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 11:41:18 +0200 Subject: [PATCH 11/19] green: increment 16b-0 (the verifier's pair search by sort and sweep) 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 --- include/terrain/noding/noded_pslg_builder.hpp | 169 +++++++++++++++--- 1 file changed, 143 insertions(+), 26 deletions(-) diff --git a/include/terrain/noding/noded_pslg_builder.hpp b/include/terrain/noding/noded_pslg_builder.hpp index d46cb169..08e1320b 100644 --- a/include/terrain/noding/noded_pslg_builder.hpp +++ b/include/terrain/noding/noded_pslg_builder.hpp @@ -48,6 +48,7 @@ // node's, because the statuses it raises (RingCollapsed, NonSimpleRing) are // diagnoses about the spacing rather than claims about these arrays. +#include #include #include #include @@ -61,10 +62,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -146,6 +149,92 @@ namespace detail { } // namespace detail +// The pair search behind guarantee 14: every pair of CLOSED boxes that +// intersect, found by sort and sweep on x (increment 16b-0, R8). Kernel-free: +// it compares box bounds and nothing else, and what a pair MEANS is the +// callbacks' business. +// +// on_edge_pair(i, j) is called exactly once for every unordered pair of edge +// boxes that meet (i != j, either order); on_edge_cell(e, c) exactly once for +// every edge box and cell box that meet. Two cells are never paired. A callback +// returning false stops the sweep at once, and the function returns false; +// otherwise it returns true. The same input gives the same calls in the same +// order. +// +// The sweep: items sorted by (xmin, kind, index), a total order, so the sort is +// deterministic. Each arriving item is tested against the ACTIVE items whose +// xmax >= its xmin (closed), and those whose xmax is below it are dropped on the +// way past, for good: every later item starts at or right of this one. A pair is +// therefore reported once, when its later-sorted member arrives, and only if +// the earlier one still spans that x -- which, with the y-test, is exactly the +// closed-box intersection. +// +// COST. Typical inputs are O(n log n + k) for k reported pairs plus the +// x-overlapping pairs whose y-intervals miss. The worst case is still +// quadratic: many long east-west boxes all overlap in x. It is the pair search +// only; it shares no code with the driver's BroadPhase, and must not (see +// check_guarantee_14). +template +[[nodiscard]] bool sweep_box_pairs(std::span edges, std::span cells, + OnEdgePair&& on_edge_pair, OnEdgeCell&& on_edge_cell) { + struct Item { + double xmin; + bool is_cell; // edges before cells on an x tie; either order is complete + std::size_t index; + }; + std::vector items; + items.reserve(edges.size() + cells.size()); + for (std::size_t i = 0; i < edges.size(); ++i) { + items.push_back(Item{edges[i].lo().x, false, i}); + } + for (std::size_t c = 0; c < cells.size(); ++c) { + items.push_back(Item{cells[c].lo().x, true, c}); + } + std::ranges::sort(items, [](const Item& a, const Item& b) { + return std::tie(a.xmin, a.is_cell, a.index) < std::tie(b.xmin, b.is_cell, b.index); + }); + + // Reports the members of `active` meeting `b`, dropping (swap-and-pop) those + // that end left of it. False as soon as `report` returns false. + const auto scan = [](std::vector& active, std::span boxes, + const Box2& b, auto&& report) { + for (std::size_t k = 0; k < active.size();) { + const Box2& a = boxes[active[k]]; + if (a.hi().x < b.lo().x) { + active[k] = active.back(); + active.pop_back(); + continue; + } + if (a.lo().y <= b.hi().y && b.lo().y <= a.hi().y && !report(active[k])) { + return false; + } + ++k; + } + return true; + }; + + std::vector active_edges; + std::vector active_cells; + for (const Item& t : items) { + if (t.is_cell) { + const auto meet = [&](std::size_t e) { return on_edge_cell(e, t.index); }; + if (!scan(active_edges, edges, cells[t.index], meet)) { + return false; + } + active_cells.push_back(t.index); + continue; + } + const auto pair = [&](std::size_t e) { return on_edge_pair(e, t.index); }; + const auto meet = [&](std::size_t c) { return on_edge_cell(t.index, c); }; + if (!scan(active_edges, edges, edges[t.index], pair) || + !scan(active_cells, cells, edges[t.index], meet)) { + return false; + } + active_edges.push_back(t.index); + } + return true; +} + class NodedPslgBuilder { public: // Takes the candidate arrays by value and consumes them. The kernel is a @@ -293,14 +382,21 @@ class NodedPslgBuilder { std::uint32_t lo, hi; // the UNDIRECTED node-id pair, which is 14(a)'s key }; - // Guarantee 14, brute force over every (edge, edge) and every (node, edge) - // pair. NO BROAD PHASE: the index is the driver's, and a verification pass - // sharing it would be blind wherever the index is. The candidates are small - // by construction -- a noded constraint set, not a mesh. + // Guarantee 14, over the (edge, edge) and (edge, node) pairs whose closed + // boxes meet, found by sweep_box_pairs. Complete: two segments that cross or + // overlap share a point, so their boxes meet, and a segment meeting a closed + // cell meets any box containing the cell. Box bounds are min/max of the + // endpoints' doubles, so exact. NOT the driver's BroadPhase: the index is the + // driver's, and a verification pass sharing it would be blind wherever the + // index is. The sweep is a different algorithm and shares no code with it. + // The tests on each pair are the brute force's, unchanged; only which + // violating pair the message names first may differ from it. template [[nodiscard]] NodeOutcome check_guarantee_14() { std::vector edges; + std::vector edge_boxes; edges.reserve(edge_base_.back()); + edge_boxes.reserve(edge_base_.back()); for (std::size_t c = 0; c < chains_.size(); ++c) { const std::span idx = indices_of(c); const std::size_t n = idx.size(); @@ -309,43 +405,64 @@ class NodedPslgBuilder { const std::uint32_t v = idx[(k + 1) % n]; edges.push_back(Edge{Segment2{vertices_[u], vertices_[v]}, std::min(u, v), std::max(u, v)}); + edge_boxes.push_back(bbox(edges.back().segment)); } } + // Node g's closed cell, padded by one spacing on every side: a false + // positive only costs a segment_meets_cell call, and the padding leaves + // no question of how the cell's bounds round. Clamped to the finite + // range, where Box2 lives; the edges are finite (guarantee 11), so the + // clamp changes no answer. + constexpr double kBig = std::numeric_limits::max(); + const double pad = grid_.spacing(); + std::vector cell_boxes; + cell_boxes.reserve(vertices_.size()); + for (const Point2& v : vertices_) { + const GridPoint g = grid_.snap(v); + const Point2 lo = grid_.cell_min(g); + const Point2 hi = grid_.cell_max(g); + cell_boxes.emplace_back( + Point2{std::max(lo.x - pad, -kBig), std::max(lo.y - pad, -kBig)}, + Point2{std::min(hi.x + pad, kBig), std::min(hi.y + pad, kBig)}); + } + + std::string why; // 14(a), amended: Disjoint or Touching, OR Overlapping with EQUAL node-id // pairs. Duplicate edges are legal -- a road noded along a river yields // two chains carrying the same edge, and classify on two identical // segments returns Overlapping. A PARTIAL overlap is still a violation, // and the clause is stated over node ids rather than over collinearity so // that the decision stays on integers. - for (std::size_t i = 0; i < edges.size(); ++i) { - for (std::size_t j = i + 1; j < edges.size(); ++j) { - const SegmentRelation r = classify(edges[i].segment, edges[j].segment); - const bool same_pair = edges[i].lo == edges[j].lo && edges[i].hi == edges[j].hi; - if (r == SegmentRelation::Crossing || - (r == SegmentRelation::Overlapping && !same_pair)) { - return detail::not_noded( - std::format("edges ({}, {}) and ({}, {}) meet off a node", edges[i].lo, - edges[i].hi, edges[j].lo, edges[j].hi)); - } + const auto edge_pair = [&](std::size_t i, std::size_t j) { + const Edge& a = edges[i]; + const Edge& b = edges[j]; + const SegmentRelation r = classify(a.segment, b.segment); + const bool same_pair = a.lo == b.lo && a.hi == b.hi; + if (r == SegmentRelation::Crossing || + (r == SegmentRelation::Overlapping && !same_pair)) { + why = std::format("edges ({}, {}) and ({}, {}) meet off a node", a.lo, a.hi, + b.lo, b.hi); + return false; } - } - + return true; + }; // 14(b), the hot-pixel clause. segment_meets_cell, NEVER on_segment: // world(g) is not affine at a non-dyadic spacing, so a snapped node is // NEAR a segment and not ON it, and an exact-incidence spelling here // blesses the T-junctions the split pass missed instead of catching them. - for (const Edge& e : edges) { - for (std::uint32_t g = 0; g < vertices_.size(); ++g) { - if (g == e.lo || g == e.hi) { - continue; - } - if (segment_meets_cell(grid_, e.segment, grid_.snap(vertices_[g]))) { - return detail::not_noded(std::format( - "node {}'s cell meets edge ({}, {}), which it does not end", g, e.lo, - e.hi)); - } + const auto edge_cell = [&](std::size_t i, std::size_t g) { + const Edge& e = edges[i]; + if (g == e.lo || g == e.hi || + !segment_meets_cell(grid_, e.segment, grid_.snap(vertices_[g]))) { + return true; } + why = std::format("node {}'s cell meets edge ({}, {}), which it does not end", g, + e.lo, e.hi); + return false; + }; + if (!sweep_box_pairs(edge_boxes, cell_boxes, edge_pair, edge_cell)) { + return detail::not_noded(std::move(why)); } return detail::pass(); } From 5e299791ad0d3a063e7907eac8f459187b04abdb Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 12:18:47 +0200 Subject: [PATCH 12/19] 16b-0 review fixes: 05b's line citation pinned to 6732564 and 05c's constraint noted; 16b-0 LOC as built Co-Authored-By: Claude Opus 5.5 --- docs/increments/05b-noder-driver.md | 3 ++- docs/increments/05c-noder-wiring.md | 2 ++ docs/increments/16b-terrain-polygons.md | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/increments/05b-noder-driver.md b/docs/increments/05b-noder-driver.md index 15332f79..08eb58d7 100644 --- a/docs/increments/05b-noder-driver.md +++ b/docs/increments/05b-noder-driver.md @@ -1560,7 +1560,8 @@ written, each the sole killer of its side: outcome and guarantee 16 together. Measured here, not asserted, with the guard patched into -`include/terrain/noding/noded_pslg_builder.hpp:217`, the header `touch`ed after +`include/terrain/noding/noded_pslg_builder.hpp:217` at `6732564` (the line +`if (is_closed(ch.role) && count < 3) {`), the header `touch`ed after each restore per `.claude/REQUIRED-READING.md`'s C++ stale-artifact hazard, and `ctest --test-dir build` run over all 611: diff --git a/docs/increments/05c-noder-wiring.md b/docs/increments/05c-noder-wiring.md index e7b70e24..79fc1a65 100644 --- a/docs/increments/05c-noder-wiring.md +++ b/docs/increments/05c-noder-wiring.md @@ -508,6 +508,8 @@ guard patched into ..." — cites `noded_pslg_builder.hpp:217` as the site, and that line number is downstream of this string. Collapsing the two lines into one moves it and falsifies a mutation record this branch is not re-running. The replacement above is two lines for that reason. +(Since 16b-0 the line has moved anyway; 05b's citation is now pinned to +`6732564` and names the line by its text, so the constraint no longer binds.) `@tester` and `@developer` should read the whole of this as: the presentation text for `NotConverged` (below) **must not promise the cap**, because the CLI diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index 1ec94354..1c013af3 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -827,6 +827,7 @@ far (+39 %, increment 16) applied. |---|---|---|---| | **16b-0** | `noding/noded_pslg_builder.hpp` | sort-and-sweep for 14(a) and 14(b), cell padding, header comment | ~50 | | | | **16b-0 total** | **~50 (worst ~70)** | +| | | *16b-0 as built (`aa35a38`, CLAUDE.md §2, blank lines excluded): +98 / −21, net 77, over the worst case; the sweep ~50, padded and clamped cell boxes ~12, the 14(a)/(b) loops as callbacks +36 / −21. All three were named in the estimate row: the overrun is in the estimate, not unplanned scope* | | | **16b-1** | `io/geopackage.py` | `GpkgLayer`, `layer_info`, `decode_geometry`, `query_features` | ~70 | | | `io/repository.py`, `io/__init__.py` | `open_geopackage` | ~8 | | | `feature_input.py` | `ClassMap` and three built-in maps, request and result models, GeoJSON reading, region, pre-clip, reprojection, clip, counts, refusals | ~130 | From 75f3845b322e5e805089332b805b4f76158d8f12 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 12:19:52 +0200 Subject: [PATCH 13/19] 16b-0 tests: the red-step guard dropped; the huge-spacing clamp pinned 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 --- docs/increments/16b-terrain-polygons.md | 6 +++-- tests/cpp/CMakeLists.txt | 15 ++--------- .../unit/test_noding_noded_pslg_builder.cpp | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/docs/increments/16b-terrain-polygons.md b/docs/increments/16b-terrain-polygons.md index 1c013af3..7324457b 100644 --- a/docs/increments/16b-terrain-polygons.md +++ b/docs/increments/16b-terrain-polygons.md @@ -686,8 +686,10 @@ builds returned `Ok`. M3's attribution holds, and 16b-0 is scoped as designed. - `node`'s output on six integer fixtures at spacing 0.125 is pinned by a digest over integers only (status, grid points, chains, indices, masks, `node_of_input_vertex`), recorded from 6b4fcb9's brute-force verifier. -- Registration: the suite builds only once `noded_pslg_builder.hpp` names - `sweep_box_pairs` (the 21a/21b guard). The review drops the guard. +- Registration: the red commit built the suite only once + `noded_pslg_builder.hpp` named `sweep_box_pairs` (the 21a/21b guard). The + guard was dropped after green, and the suite is now registered + unconditionally. ### R9. Vertex density, z, and refinement diff --git a/tests/cpp/CMakeLists.txt b/tests/cpp/CMakeLists.txt index 61715689..fb00cc92 100644 --- a/tests/cpp/CMakeLists.txt +++ b/tests/cpp/CMakeLists.txt @@ -309,16 +309,5 @@ add_terrain_backend_test(test_mesh_lattice_incircle unit/test_mesh_lattice_incir # the brute-force verifier's status (tests/cpp/support/verifier_oracle.hpp, # frozen at 6b4fcb9), and node's output digests are today's. It starts no # threads, so it is not in the TSan job. -# -# RED UNTIL THE INTERFACE EXISTS: registered only once noded_pslg_builder.hpp -# names sweep_box_pairs (defined there or in a header it includes), so the red -# does not break the rest of the build. The header is a configure dependency, -# so the green commit's edit registers the suite without a manual cmake. The -# review drops this guard. -set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS - "${PROJECT_SOURCE_DIR}/include/terrain/noding/noded_pslg_builder.hpp") -file(READ "${PROJECT_SOURCE_DIR}/include/terrain/noding/noded_pslg_builder.hpp" _builder_16b0) -string(FIND "${_builder_16b0}" "sweep_box_pairs" _sweep_box_pairs_at) -if(NOT _sweep_box_pairs_at EQUAL -1) - add_terrain_backend_test(prop_noding_verifier_sweep property/prop_noding_verifier_sweep.cpp) -endif() + +add_terrain_backend_test(prop_noding_verifier_sweep property/prop_noding_verifier_sweep.cpp) diff --git a/tests/cpp/unit/test_noding_noded_pslg_builder.cpp b/tests/cpp/unit/test_noding_noded_pslg_builder.cpp index 4d553c90..42f9cb47 100644 --- a/tests/cpp/unit/test_noding_noded_pslg_builder.cpp +++ b/tests/cpp/unit/test_noding_noded_pslg_builder.cpp @@ -50,6 +50,7 @@ #include #include +#include #include #include #include @@ -260,6 +261,31 @@ TEST_CASE("a coordinate past kMaxGridIndex is refused, not asserted on", REQUIRE_FALSE(out.ok()); } +TEST_CASE("a huge spacing whose padded cell overflows is accepted, not thrown on", + "[noding][noded_pslg_builder]") { + // The +-DBL_MAX clamp on the padded node cells in check_guarantee_14. At + // spacing 1e308 node (1, 0) sits at x = 1e308, its cell reaches 1.5e308 and + // one spacing of padding takes it past DBL_MAX to +inf. Without the clamp + // Box2's constructor throws std::invalid_argument ("corners must be + // finite") out of build(); with it, this one-edge candidate is valid + // (every guarantee holds) and build() says so. + const SnapGrid grid{1e308}; + const std::vector vertices = world_of(grid, {GridPoint{0, 0}, GridPoint{1, 0}}); + const std::vector chains{Chain{0, 2, ChainRole::Breakline, kRoad}}; + const std::vector chain_indices{0, 1}; + const std::vector edge_properties{kRoad}; + const std::vector node_of_input_vertex{}; + REQUIRE(grid.cell_max(grid.snap(vertices[1])).x + grid.spacing() == + std::numeric_limits::infinity()); + + NodeOutcome out; + REQUIRE_NOTHROW(out = NodedPslgBuilder{grid, vertices, chains, chain_indices, + edge_properties, node_of_input_vertex} + .build()); + REQUIRE(out.status == NodeStatus::Ok); + REQUIRE(out.ok()); +} + // --------------------------------------------------------------------------- // Guarantees 12 and 13. // --------------------------------------------------------------------------- From 98ed09bdc556735941314bb2c79253483881464a Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Mon, 28 Sep 2026 15:55:54 +0200 Subject: [PATCH 14/19] 16b-0 acceptance: ACCEPTED (battery); 1 m bench unchanged, mesh identical; 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 --- docs/benchmarks/2026-09-28/16b0-acceptance.md | 33 + .../16b0-acceptance/16b0-r2/README.md | 80 + .../16b0-acceptance/16b0-r2/raw.tsv | 210 ++ .../16b0-acceptance/16b0-r2/run.json | 2956 ++++++++++++++++ .../16b0-acceptance/16b0-r3/README.md | 79 + .../16b0-acceptance/16b0-r3/raw.tsv | 210 ++ .../16b0-acceptance/16b0-r3/run.json | 2955 ++++++++++++++++ .../2026-09-28/16b0-acceptance/16b0/README.md | 79 + .../2026-09-28/16b0-acceptance/16b0/raw.tsv | 210 ++ .../2026-09-28/16b0-acceptance/16b0/run.json | 2955 ++++++++++++++++ .../2026-09-28/16b0-acceptance/README.md | 269 ++ .../16b0-acceptance/base-dc372a5-r2/README.md | 81 + .../16b0-acceptance/base-dc372a5-r2/raw.tsv | 210 ++ .../16b0-acceptance/base-dc372a5-r2/run.json | 2957 ++++++++++++++++ .../16b0-acceptance/base-dc372a5-r3/README.md | 79 + .../16b0-acceptance/base-dc372a5-r3/raw.tsv | 210 ++ .../16b0-acceptance/base-dc372a5-r3/run.json | 2955 ++++++++++++++++ .../16b0-acceptance/base-dc372a5/README.md | 83 + .../16b0-acceptance/base-dc372a5/raw.tsv | 210 ++ .../16b0-acceptance/base-dc372a5/run.json | 2959 +++++++++++++++++ .../16b0-acceptance/corine/logs/mesh-1.log | 42 + .../16b0-acceptance/corine/logs/mesh-2.log | 42 + .../16b0-acceptance/corine/logs/mesh-3.log | 42 + .../16b0-acceptance/corine/logs/node.log | 91 + .../16b0-acceptance/corine/logs/pair3.log | 15 + .../16b0-acceptance/corine/logs/pairs.log | 34 + .../corine/scripts/clc_mesh.py | 84 + .../corine/scripts/clc_probe.py | 120 + .../corine/scripts/corine_mesh.sh | 13 + .../corine/scripts/corine_node.sh | 15 + .../16b0-acceptance/corine/scripts/drv.py | 143 + .../16b0-acceptance/corine/scripts/pair3.sh | 15 + .../16b0-acceptance/corine/scripts/pairs.sh | 15 + 33 files changed, 20451 insertions(+) create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance.md create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/README.md create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/raw.tsv create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/run.json create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/README.md create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/raw.tsv create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/run.json create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/16b0/README.md create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/16b0/raw.tsv create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/16b0/run.json create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/README.md create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/base-dc372a5-r2/README.md create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/base-dc372a5-r2/raw.tsv create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/base-dc372a5-r2/run.json create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/base-dc372a5-r3/README.md create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/base-dc372a5-r3/raw.tsv create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/base-dc372a5-r3/run.json create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/base-dc372a5/README.md create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/base-dc372a5/raw.tsv create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/base-dc372a5/run.json create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/corine/logs/mesh-1.log create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/corine/logs/mesh-2.log create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/corine/logs/mesh-3.log create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/corine/logs/node.log create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/corine/logs/pair3.log create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/corine/logs/pairs.log create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/corine/scripts/clc_mesh.py create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/corine/scripts/clc_probe.py create mode 100755 docs/benchmarks/2026-09-28/16b0-acceptance/corine/scripts/corine_mesh.sh create mode 100755 docs/benchmarks/2026-09-28/16b0-acceptance/corine/scripts/corine_node.sh create mode 100644 docs/benchmarks/2026-09-28/16b0-acceptance/corine/scripts/drv.py create mode 100755 docs/benchmarks/2026-09-28/16b0-acceptance/corine/scripts/pair3.sh create mode 100755 docs/benchmarks/2026-09-28/16b0-acceptance/corine/scripts/pairs.sh diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance.md b/docs/benchmarks/2026-09-28/16b0-acceptance.md new file mode 100644 index 00000000..758a6c6b --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance.md @@ -0,0 +1,33 @@ +# Increment 16b-0 acceptance (@perf, 2026-09-28): summary + +**Verdict: ACCEPTED.** Measured on battery against a battery baseline, back +to back. The branch is `4e83389`; the base is master `dc372a5`. Method, +tables and raw data: `16b0-acceptance/README.md`. + +- **1 m benchmark and thread sweep** (`tools/bench.py`, three pairs): no + change. Pooled over all 42 cells, the change is a median of -0.28 % (range + -5.9 % to +2.8 %). bench.py said ACCEPTED on pairs 1 and 3. On pair 2 it + flagged two tile cells (+6.9 % at 10 threads, +8.0 % at 16); those cells + were +0.5 % to +2.1 % in the other pairs. The same build moves by up to + +13.5 % between runs, and `refine` does not compile the changed header. + The mesh sha256 is equal on both domains in all six runs. Ceiling: + 2.5-2.9x, as the base. +- **`node` on M3's CORINE squares.** Layout B at 48 km: **84.05 s -> 0.1435 s + (586x)**, against the Acceptance section's 83.1 s. Layout A at + 12/24/36/48 km: 0.0041/0.0174/0.0364/0.0668 s (reference + 0.0041/0.0167/0.0364/0.0670). From 12 to 48 km the segments grow 13.8x; + `node` grows 16-18x, against 185x on master. A 144 km block (870 k segments, + layout B) nodes in 2.2 s. The noded output is identical between layouts A + and B and between master and 16b-0. +- **The admitted worst case.** The ladder at 300 rungs takes 0.262 s + (master: 210 s); at 1 200 rungs (2.9 M noded edges) it takes 17.3 s. The + comb of parallel east-west lines is quadratic, as R8 admits: 4x the lines + costs 14.4-15.6x, and 64 000 lines take 21.4 s. The split between the + driver and the verifier has not been isolated. +- **The first CORINE baseline** (48 km square in `6603_4`, layout A, 16b-0). + At 1 m: 6.75 M triangles, `node` 0.068 s, refine 5.48 s, peak RSS 2.35 GB. + At 10 m, **the quality start makes the mesh 3.61x the featureless one** + (480 961 triangles, against 212 255 with the quality start off and 133 379 + without features). That is 20c's input. `node` is no longer the cost. +- **Not measured**: a verifier-only split on the ladder and comb; master at + 96/144 km and on the large synthetic cases; any AC run. diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/README.md b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/README.md new file mode 100644 index 00000000..e544a38e --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/README.md @@ -0,0 +1,80 @@ +# Benchmark run `16b0-acceptance/16b0-r2` + +Generated by `tools/bench.py`. + +## Verdict + +- REGRESSION: tile refine_s[t=10] 0.1860 -> 0.1989 (+6.9 %) +- REGRESSION: tile refine_s[t=16] 0.1906 -> 0.2059 (+8.0 %) + +Time threshold: 5.0 %. + +## Method + +- Started 2026-09-28T15:28:37.286233+02:00; tree `4e833893377430683ac1dce967783ead5ff9a2de` (dirty); bench.py blob `77765b181714c31714da476c2335c799c6a84021`. Build: Release, AppleClang 21.0.0.21000101, `-O3 -DNDEBUG`, _core sha256 `190587e7ba0ea96a74a8621767d7d20257c3968126a710db1364e1195afb27d2`. +- Apple M1 Max, 8 P + 2 E cores, 32 GiB, macOS 27.0, Python 3.14.7, numpy 2.5.3. +- Power **battery** (72%), `pmset -g batt` before and after (in run.json). +- DEM `/Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif` (sha256 `aabd0cbc28471ce8593e4c278381811bec3fbf4e1194058b3b4888d00c3af575`), tolerance 1.0, extra mesh args ``; domains: `quarter` (/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-26/quarter.geojson), `tile` (the whole tile). +- One child per sample, `/Users/skavhaug/projects/rasputin/.venv/bin/python /Users/skavhaug/projects/rasputin/tools/bench.py _child --pkg /Users/skavhaug/projects/rasputin/build-bench/pkg --threads -- mesh --dem /Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif --tolerance 1 --out --binary`, repeats interleaved over thread counts; t=0 is the CLI's default, other counts are forced into `refine`. Quality: one `--ascii` run per domain at t=0, kept out of the repository; rerun the child with `--ascii --out PATH` to regenerate it. + +## `quarter` + +| threads | n | median s | min s | max s | +|---:|---:|---:|---:|---:| +| 0 | 5 | 0.1645 | 0.1634 | 0.1955 | +| 1 | 5 | 0.4241 | 0.4132 | 0.4549 | +| 2 | 5 | 0.2765 | 0.2663 | 0.3091 | +| 3 | 5 | 0.2262 | 0.2212 | 0.2617 | +| 4 | 5 | 0.2013 | 0.1978 | 0.2109 | +| 5 | 5 | 0.1881 | 0.1847 | 0.1912 | +| 6 | 5 | 0.1753 | 0.1733 | 0.1833 | +| 7 | 5 | 0.1704 | 0.1669 | 0.2283 | +| 8 | 5 | 0.1634 | 0.1624 | 0.1708 | +| 9 | 5 | 0.1660 | 0.1641 | 0.1819 | +| 10 | 5 | 0.1644 | 0.1626 | 0.1710 | +| 11 | 5 | 0.1635 | 0.1602 | 0.1774 | +| 12 | 5 | 0.1652 | 0.1628 | 0.1880 | +| 13 | 5 | 0.1691 | 0.1643 | 0.1740 | +| 14 | 5 | 0.1673 | 0.1603 | 0.1754 | +| 15 | 5 | 0.1638 | 0.1620 | 0.1718 | +| 16 | 5 | 0.1703 | 0.1644 | 0.1737 | +| 17 | 5 | 0.1659 | 0.1631 | 0.2303 | +| 18 | 5 | 0.1657 | 0.1647 | 0.1892 | +| 19 | 5 | 0.1679 | 0.1650 | 0.1710 | +| 20 | 5 | 0.1691 | 0.1631 | 0.1808 | + +Ceiling: 2.51x at 20 threads over 1, best 2.59x at 8; 2026-09-26: about 2.2x, flat from about 7. + +Quality: worst angle 0.3955 deg, median 45.00, share under 1 deg 0.00003, max degree 18, within tolerance True, Delaunay 0 violations of 641791 edges (80136 decided exactly), mesh sha256 `ccebf96a86c6c5e244e4a0281919de4e866fcfe789b66024a290ac2af33771a1`. + +## `tile` + +| threads | n | median s | min s | max s | +|---:|---:|---:|---:|---:| +| 0 | 5 | 0.1923 | 0.1851 | 0.2074 | +| 1 | 5 | 0.4771 | 0.4620 | 0.5637 | +| 2 | 5 | 0.3092 | 0.3050 | 0.3685 | +| 3 | 5 | 0.2618 | 0.2510 | 0.3227 | +| 4 | 5 | 0.2296 | 0.2250 | 0.2526 | +| 5 | 5 | 0.2175 | 0.2092 | 0.2440 | +| 6 | 5 | 0.2071 | 0.1982 | 0.2148 | +| 7 | 5 | 0.1965 | 0.1940 | 0.2545 | +| 8 | 5 | 0.1872 | 0.1838 | 0.1938 | +| 9 | 5 | 0.1923 | 0.1879 | 0.1940 | +| 10 | 5 | 0.1989 | 0.1834 | 0.2022 | +| 11 | 5 | 0.1952 | 0.1865 | 0.2134 | +| 12 | 5 | 0.1915 | 0.1863 | 0.2067 | +| 13 | 5 | 0.1904 | 0.1832 | 0.2016 | +| 14 | 5 | 0.1924 | 0.1810 | 0.1956 | +| 15 | 5 | 0.1893 | 0.1822 | 0.2128 | +| 16 | 5 | 0.2059 | 0.1891 | 0.2099 | +| 17 | 5 | 0.1879 | 0.1855 | 0.2008 | +| 18 | 5 | 0.1915 | 0.1860 | 0.2001 | +| 19 | 5 | 0.1936 | 0.1872 | 0.2199 | +| 20 | 5 | 0.1892 | 0.1846 | 0.2069 | + +Ceiling: 2.52x at 20 threads over 1, best 2.55x at 8; 2026-09-26: about 2.2x, flat from about 7. + +Quality: worst angle 0.6296 deg, median 45.00, share under 1 deg 0.00000, max degree 74, within tolerance True, Delaunay 0 violations of 692056 edges (94469 decided exactly), mesh sha256 `11741a81adfa17b34a1ea56875ec9e791414d17625eb1c58305e7c0cf10e005c`. + + diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/raw.tsv b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/raw.tsv new file mode 100644 index 00000000..99fb326d --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/raw.tsv @@ -0,0 +1,210 @@ +quarter 0 0 0.164856 battery +quarter 1 0 0.424082 battery +quarter 2 0 0.266252 battery +quarter 3 0 0.221238 battery +quarter 4 0 0.201271 battery +quarter 5 0 0.184698 battery +quarter 6 0 0.175319 battery +quarter 7 0 0.228301 battery +quarter 8 0 0.164411 battery +quarter 9 0 0.164101 battery +quarter 10 0 0.164431 battery +quarter 11 0 0.163459 battery +quarter 12 0 0.168335 battery +quarter 13 0 0.165939 battery +quarter 14 0 0.165833 battery +quarter 15 0 0.162024 battery +quarter 16 0 0.164361 battery +quarter 17 0 0.165863 battery +quarter 18 0 0.165617 battery +quarter 19 0 0.167948 battery +quarter 20 0 0.164801 battery +quarter 0 1 0.163427 battery +quarter 1 1 0.413246 battery +quarter 2 1 0.270238 battery +quarter 3 1 0.224355 battery +quarter 4 1 0.210909 battery +quarter 5 1 0.191175 battery +quarter 6 1 0.183334 battery +quarter 7 1 0.175429 battery +quarter 8 1 0.170815 battery +quarter 9 1 0.166130 battery +quarter 10 1 0.171008 battery +quarter 11 1 0.170351 battery +quarter 12 1 0.164255 battery +quarter 13 1 0.169057 battery +quarter 14 1 0.173357 battery +quarter 15 1 0.162385 battery +quarter 16 1 0.170326 battery +quarter 17 1 0.163591 battery +quarter 18 1 0.164713 battery +quarter 19 1 0.169461 battery +quarter 20 1 0.180846 battery +quarter 0 2 0.164512 battery +quarter 1 2 0.423277 battery +quarter 2 2 0.276467 battery +quarter 3 2 0.226166 battery +quarter 4 2 0.200911 battery +quarter 5 2 0.188123 battery +quarter 6 2 0.174297 battery +quarter 7 2 0.166889 battery +quarter 8 2 0.163436 battery +quarter 9 2 0.165976 battery +quarter 10 2 0.164209 battery +quarter 11 2 0.163496 battery +quarter 12 2 0.165217 battery +quarter 13 2 0.173999 battery +quarter 14 2 0.175413 battery +quarter 15 2 0.171639 battery +quarter 16 2 0.172204 battery +quarter 17 2 0.169337 battery +quarter 18 2 0.169689 battery +quarter 19 2 0.171044 battery +quarter 20 2 0.169149 battery +quarter 0 3 0.163975 battery +quarter 1 3 0.429503 battery +quarter 2 3 0.277596 battery +quarter 3 3 0.261673 battery +quarter 4 3 0.201673 battery +quarter 5 3 0.184714 battery +quarter 6 3 0.175594 battery +quarter 7 3 0.170433 battery +quarter 8 3 0.162405 battery +quarter 9 3 0.165696 battery +quarter 10 3 0.164350 battery +quarter 11 3 0.160207 battery +quarter 12 3 0.162803 battery +quarter 13 3 0.164296 battery +quarter 14 3 0.160301 battery +quarter 15 3 0.163777 battery +quarter 16 3 0.168804 battery +quarter 17 3 0.163083 battery +quarter 18 3 0.189214 battery +quarter 19 3 0.165009 battery +quarter 20 3 0.180621 battery +quarter 0 4 0.195525 battery +quarter 1 4 0.454863 battery +quarter 2 4 0.309056 battery +quarter 3 4 0.227071 battery +quarter 4 4 0.197814 battery +quarter 5 4 0.188775 battery +quarter 6 4 0.173264 battery +quarter 7 4 0.167388 battery +quarter 8 4 0.162682 battery +quarter 9 4 0.181897 battery +quarter 10 4 0.162612 battery +quarter 11 4 0.177442 battery +quarter 12 4 0.187968 battery +quarter 13 4 0.170796 battery +quarter 14 4 0.167294 battery +quarter 15 4 0.171820 battery +quarter 16 4 0.173676 battery +quarter 17 4 0.230277 battery +quarter 18 4 0.165675 battery +quarter 19 4 0.167223 battery +quarter 20 4 0.163051 battery +tile 0 0 0.199172 battery +tile 1 0 0.485446 battery +tile 2 0 0.304994 battery +tile 3 0 0.264228 battery +tile 4 0 0.229603 battery +tile 5 0 0.244030 battery +tile 6 0 0.214772 battery +tile 7 0 0.196473 battery +tile 8 0 0.192940 battery +tile 9 0 0.192287 battery +tile 10 0 0.183433 battery +tile 11 0 0.187442 battery +tile 12 0 0.206694 battery +tile 13 0 0.190414 battery +tile 14 0 0.180978 battery +tile 15 0 0.189293 battery +tile 16 0 0.205891 battery +tile 17 0 0.185504 battery +tile 18 0 0.191501 battery +tile 19 0 0.219947 battery +tile 20 0 0.188464 battery +tile 0 1 0.192257 battery +tile 1 1 0.477126 battery +tile 2 1 0.306291 battery +tile 3 1 0.261793 battery +tile 4 1 0.226898 battery +tile 5 1 0.212809 battery +tile 6 1 0.208831 battery +tile 7 1 0.254464 battery +tile 8 1 0.187157 battery +tile 9 1 0.193956 battery +tile 10 1 0.186898 battery +tile 11 1 0.195916 battery +tile 12 1 0.186337 battery +tile 13 1 0.183243 battery +tile 14 1 0.192367 battery +tile 15 1 0.187355 battery +tile 16 1 0.192028 battery +tile 17 1 0.185490 battery +tile 18 1 0.190231 battery +tile 19 1 0.190470 battery +tile 20 1 0.184570 battery +tile 0 2 0.186083 battery +tile 1 2 0.463480 battery +tile 2 2 0.309213 battery +tile 3 2 0.251740 battery +tile 4 2 0.252622 battery +tile 5 2 0.209215 battery +tile 6 2 0.205321 battery +tile 7 2 0.196465 battery +tile 8 2 0.185962 battery +tile 9 2 0.193449 battery +tile 10 2 0.198917 battery +tile 11 2 0.195188 battery +tile 12 2 0.196845 battery +tile 13 2 0.197664 battery +tile 14 2 0.193239 battery +tile 15 2 0.210892 battery +tile 16 2 0.206729 battery +tile 17 2 0.197747 battery +tile 18 2 0.200052 battery +tile 19 2 0.204749 battery +tile 20 2 0.206931 battery +tile 0 3 0.207412 battery +tile 1 3 0.563681 battery +tile 2 3 0.368479 battery +tile 3 3 0.322723 battery +tile 4 3 0.242480 battery +tile 5 3 0.222402 battery +tile 6 3 0.207134 battery +tile 7 3 0.217667 battery +tile 8 3 0.193842 battery +tile 9 3 0.191138 battery +tile 10 3 0.198865 battery +tile 11 3 0.213369 battery +tile 12 3 0.191476 battery +tile 13 3 0.201642 battery +tile 14 3 0.186999 battery +tile 15 3 0.182186 battery +tile 16 3 0.189111 battery +tile 17 3 0.187891 battery +tile 18 3 0.186033 battery +tile 19 3 0.187163 battery +tile 20 3 0.189194 battery +tile 0 4 0.185090 battery +tile 1 4 0.461952 battery +tile 2 4 0.309837 battery +tile 3 4 0.251035 battery +tile 4 4 0.224994 battery +tile 5 4 0.217515 battery +tile 6 4 0.198155 battery +tile 7 4 0.194027 battery +tile 8 4 0.183795 battery +tile 9 4 0.187885 battery +tile 10 4 0.202212 battery +tile 11 4 0.186472 battery +tile 12 4 0.189074 battery +tile 13 4 0.185436 battery +tile 14 4 0.195562 battery +tile 15 4 0.212794 battery +tile 16 4 0.209865 battery +tile 17 4 0.200799 battery +tile 18 4 0.194508 battery +tile 19 4 0.193629 battery +tile 20 4 0.190021 battery diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/run.json b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/run.json new file mode 100644 index 00000000..5d5cbd73 --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r2/run.json @@ -0,0 +1,2956 @@ +{ + "label": "16b0-acceptance/16b0-r2", + "started": "2026-09-28T15:28:37.286233+02:00", + "tree": { + "commit": "4e833893377430683ac1dce967783ead5ff9a2de", + "dirty": true + }, + "bench_blob": "77765b181714c31714da476c2335c799c6a84021", + "child_argv": [ + "/Users/skavhaug/projects/rasputin/.venv/bin/python", + "/Users/skavhaug/projects/rasputin/tools/bench.py", + "_child", + "--pkg", + "/Users/skavhaug/projects/rasputin/build-bench/pkg", + "--threads", + "", + "--", + "mesh", + "--dem", + "/Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif", + "--tolerance", + "1", + "--out", + "", + "--binary" + ], + "build": { + "no_build": false, + "type": "Release", + "cxx_flags_release": "-O3 -DNDEBUG", + "compiler": "AppleClang 21.0.0.21000101", + "so_sha256": "190587e7ba0ea96a74a8621767d7d20257c3968126a710db1364e1195afb27d2" + }, + "machine": { + "cpu_brand": "Apple M1 Max", + "p_cores": 8, + "e_cores": 2, + "memory_bytes": 34359738368, + "macos": "27.0", + "python": "3.14.7", + "numpy": "2.5.3" + }, + "power": { + "state": "battery", + "percent": 72, + "raw": "Now drawing from 'Battery Power'\n -InternalBattery-0 (id=7929955)\t73%; discharging; 2:45 remaining present: true\n--- after ---\nNow drawing from 'Battery Power'\n -InternalBattery-0 (id=7929955)\t72%; discharging; 2:25 remaining present: true\n" + }, + "inputs": { + "dem": "/Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif", + "dem_sha256": "aabd0cbc28471ce8593e4c278381811bec3fbf4e1194058b3b4888d00c3af575", + "domains": [ + { + "name": "quarter", + "path": "/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-26/quarter.geojson", + "sha256": "b7f7bbe96848383c77863ce29ee28c45bce087601a1738ee82ddb4ffb8279d1e" + }, + { + "name": "tile", + "path": null, + "sha256": null + } + ], + "tolerance": 1.0, + "extra_args": [] + }, + "samples": [ + { + "domain": "quarter", + "threads": 0, + "repeat": 0, + "refine_s": 0.16485562501475215, + "app_s": 0.28941516700433567, + "proc_s": 0.8857948329532519, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 0, + "refine_s": 0.4240817920072004, + "app_s": 0.5397300830227323, + "proc_s": 0.789844625047408, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 0, + "refine_s": 0.2662517089629546, + "app_s": 0.3744215000187978, + "proc_s": 0.6302744169952348, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 0, + "refine_s": 0.22123808303149417, + "app_s": 0.3312386249890551, + "proc_s": 0.5890292080002837, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 0, + "refine_s": 0.20127125002909452, + "app_s": 0.31237408303422853, + "proc_s": 0.5688475829665549, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 0, + "refine_s": 0.18469825002830476, + "app_s": 0.29279091698117554, + "proc_s": 0.5546606249990873, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 0, + "refine_s": 0.1753185410052538, + "app_s": 0.28357733302982524, + "proc_s": 0.5437019999953918, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 0, + "refine_s": 0.22830112499650568, + "app_s": 0.35108804202172905, + "proc_s": 0.6203443750273436, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 0, + "refine_s": 0.16441104200202972, + "app_s": 0.27274041599594057, + "proc_s": 0.5332577080116607, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 0, + "refine_s": 0.16410149994771928, + "app_s": 0.27133091702125967, + "proc_s": 0.5228062079986557, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 0, + "refine_s": 0.16443050000816584, + "app_s": 0.2759707499644719, + "proc_s": 0.527242083044257, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 0, + "refine_s": 0.16345887497300282, + "app_s": 0.27082145895110443, + "proc_s": 0.5202467089984566, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 0, + "refine_s": 0.1683345829951577, + "app_s": 0.2763988749939017, + "proc_s": 0.5353921669884585, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 0, + "refine_s": 0.16593941702740267, + "app_s": 0.2756954589858651, + "proc_s": 0.532268832961563, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 0, + "refine_s": 0.16583299997728318, + "app_s": 0.2734826249652542, + "proc_s": 0.5322057080338709, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 0, + "refine_s": 0.16202395799336955, + "app_s": 0.2694804999628104, + "proc_s": 0.5178995829774067, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 0, + "refine_s": 0.16436095803510398, + "app_s": 0.2738338749622926, + "proc_s": 0.5338976660277694, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 0, + "refine_s": 0.16586275002919137, + "app_s": 0.27893554198089987, + "proc_s": 0.5504482910037041, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 0, + "refine_s": 0.16561695898417383, + "app_s": 0.2751966249779798, + "proc_s": 0.5271273750113323, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 0, + "refine_s": 0.16794800001662225, + "app_s": 0.27588062500581145, + "proc_s": 0.5321771669550799, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 0, + "refine_s": 0.16480054199928418, + "app_s": 0.2743955409969203, + "proc_s": 0.5312088750069961, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 1, + "refine_s": 0.1634274579701014, + "app_s": 0.27461362502072006, + "proc_s": 0.5247552910004742, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 1, + "refine_s": 0.41324575000908226, + "app_s": 0.5199976660078391, + "proc_s": 0.7732188749941997, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 1, + "refine_s": 0.27023816597647965, + "app_s": 0.3788190420018509, + "proc_s": 0.6302099169697613, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 1, + "refine_s": 0.22435545799089596, + "app_s": 0.3322445420199074, + "proc_s": 0.5851760830264539, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 1, + "refine_s": 0.2109092500177212, + "app_s": 0.322234750026837, + "proc_s": 0.5981063750223257, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 1, + "refine_s": 0.19117499998537824, + "app_s": 0.3060393749619834, + "proc_s": 0.5728237500297837, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 1, + "refine_s": 0.18333362496923655, + "app_s": 0.3018885410274379, + "proc_s": 0.5739226249861531, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 1, + "refine_s": 0.1754289999953471, + "app_s": 0.2890164169948548, + "proc_s": 0.5569832919863984, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 1, + "refine_s": 0.17081454198341817, + "app_s": 0.2833015830256045, + "proc_s": 0.5485742909950204, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 1, + "refine_s": 0.16612954199081287, + "app_s": 0.27973524999106303, + "proc_s": 0.5477596250129864, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 1, + "refine_s": 0.1710080840275623, + "app_s": 0.2849290409940295, + "proc_s": 0.5561171669978648, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 1, + "refine_s": 0.17035062500508502, + "app_s": 0.28713587496895343, + "proc_s": 0.5766584170050919, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 1, + "refine_s": 0.1642548749805428, + "app_s": 0.28103787498548627, + "proc_s": 0.5409011250012554, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 1, + "refine_s": 0.16905729100108147, + "app_s": 0.28266937495209277, + "proc_s": 0.5478366670431569, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 1, + "refine_s": 0.17335720802657306, + "app_s": 0.28684520901879296, + "proc_s": 0.5485204579890706, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 1, + "refine_s": 0.16238458297448233, + "app_s": 0.26869095902657136, + "proc_s": 0.5174249999690801, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 1, + "refine_s": 0.17032591701718047, + "app_s": 0.2788412910304032, + "proc_s": 0.5378813330316916, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 1, + "refine_s": 0.16359124996233732, + "app_s": 0.27019175002351403, + "proc_s": 0.5307134579634294, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 1, + "refine_s": 0.16471275000367314, + "app_s": 0.27356195799075067, + "proc_s": 0.5322843749891035, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 1, + "refine_s": 0.16946137498598546, + "app_s": 0.2815514579997398, + "proc_s": 0.5387026250245981, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 1, + "refine_s": 0.18084637494757771, + "app_s": 0.293268250010442, + "proc_s": 0.5522387499804609, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 2, + "refine_s": 0.16451245895586908, + "app_s": 0.27666441595647484, + "proc_s": 0.53710079198936, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 2, + "refine_s": 0.4232765000197105, + "app_s": 0.5323246250045486, + "proc_s": 0.7953527920180932, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 2, + "refine_s": 0.27646716695744544, + "app_s": 0.3828098750091158, + "proc_s": 0.6365509169991128, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 2, + "refine_s": 0.22616637498140335, + "app_s": 0.33385937503771856, + "proc_s": 0.5846442080219276, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 2, + "refine_s": 0.20091133401729167, + "app_s": 0.30775687500135973, + "proc_s": 0.5620848329854198, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 2, + "refine_s": 0.18812333297682926, + "app_s": 0.2985256250249222, + "proc_s": 0.5554761670064181, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 2, + "refine_s": 0.17429729201830924, + "app_s": 0.28210654202848673, + "proc_s": 0.5361017910181545, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 2, + "refine_s": 0.16688937495928258, + "app_s": 0.27437162498245016, + "proc_s": 0.5299989169579931, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 2, + "refine_s": 0.1634364589699544, + "app_s": 0.271342416992411, + "proc_s": 0.5254227090044878, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 2, + "refine_s": 0.16597604198614135, + "app_s": 0.2751319159870036, + "proc_s": 0.5356596250203438, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 2, + "refine_s": 0.16420916694914922, + "app_s": 0.2733380420249887, + "proc_s": 0.5260945410118438, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 2, + "refine_s": 0.1634957090136595, + "app_s": 0.273061084037181, + "proc_s": 0.5339634160045534, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 2, + "refine_s": 0.16521733300760388, + "app_s": 0.2725114999921061, + "proc_s": 0.5319139590137638, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 2, + "refine_s": 0.1739992499933578, + "app_s": 0.2835108329891227, + "proc_s": 0.5429093330167234, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 2, + "refine_s": 0.17541254102252424, + "app_s": 0.28377441701013595, + "proc_s": 0.5518609160208143, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 2, + "refine_s": 0.17163929098751396, + "app_s": 0.279703333042562, + "proc_s": 0.5598416249849834, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 2, + "refine_s": 0.17220370803261176, + "app_s": 0.27868912497069687, + "proc_s": 0.5451784159522504, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 2, + "refine_s": 0.16933691600570455, + "app_s": 0.27675662498222664, + "proc_s": 0.5468282499932684, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 2, + "refine_s": 0.1696887079742737, + "app_s": 0.2762095829821192, + "proc_s": 0.5453190419939347, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 2, + "refine_s": 0.17104362498503178, + "app_s": 0.28037425002548844, + "proc_s": 0.5488943330128677, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 2, + "refine_s": 0.16914937499677762, + "app_s": 0.2766326669952832, + "proc_s": 0.5458345420192927, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 3, + "refine_s": 0.16397541703190655, + "app_s": 0.26811458298470825, + "proc_s": 0.5179095410276204, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 3, + "refine_s": 0.4295027909683995, + "app_s": 0.5400577919790521, + "proc_s": 0.79411637497833, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 3, + "refine_s": 0.27759629098000005, + "app_s": 0.384084791992791, + "proc_s": 0.6460125830490142, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 3, + "refine_s": 0.2616728750290349, + "app_s": 0.36712141596945, + "proc_s": 0.6188138340367004, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 3, + "refine_s": 0.20167283399496228, + "app_s": 0.30768954200902954, + "proc_s": 0.567090249969624, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 3, + "refine_s": 0.1847141669713892, + "app_s": 0.292709541041404, + "proc_s": 0.5527817090041935, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 3, + "refine_s": 0.17559358302969486, + "app_s": 0.2848775410093367, + "proc_s": 0.5378245419706218, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 3, + "refine_s": 0.170433041988872, + "app_s": 0.2859451250405982, + "proc_s": 0.5599378749611787, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 3, + "refine_s": 0.1624052079860121, + "app_s": 0.2697792500257492, + "proc_s": 0.5286541659734212, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 3, + "refine_s": 0.16569558304036036, + "app_s": 0.2739926250069402, + "proc_s": 0.5307973329909146, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 3, + "refine_s": 0.1643501669750549, + "app_s": 0.27717549999943003, + "proc_s": 0.5351423330139369, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 3, + "refine_s": 0.16020687500713393, + "app_s": 0.2735065419692546, + "proc_s": 0.5288752499618568, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 3, + "refine_s": 0.16280254197772592, + "app_s": 0.27322545903734863, + "proc_s": 0.5263322500395589, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 3, + "refine_s": 0.1642955410061404, + "app_s": 0.27639083296526223, + "proc_s": 0.5397066670120694, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 3, + "refine_s": 0.16030120803043246, + "app_s": 0.2693545420188457, + "proc_s": 0.5274489169823937, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 3, + "refine_s": 0.16377741703763604, + "app_s": 0.28324033302487805, + "proc_s": 0.5455887090065517, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 3, + "refine_s": 0.16880437498912215, + "app_s": 0.3140958329895511, + "proc_s": 0.6161726670106873, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 3, + "refine_s": 0.1630828749621287, + "app_s": 0.2825123749789782, + "proc_s": 0.5483857920044102, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 3, + "refine_s": 0.18921354197664186, + "app_s": 0.3460199579712935, + "proc_s": 0.6510580420144834, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 3, + "refine_s": 0.16500854201149195, + "app_s": 0.2757602079655044, + "proc_s": 0.5455160830169916, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 3, + "refine_s": 0.1806214590324089, + "app_s": 0.3007308340165764, + "proc_s": 0.5855299999821, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 4, + "refine_s": 0.19552475004456937, + "app_s": 0.3119281670078635, + "proc_s": 0.587306790985167, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 4, + "refine_s": 0.45486345898825675, + "app_s": 0.5768688329844736, + "proc_s": 0.8852530830190517, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 4, + "refine_s": 0.3090555419912562, + "app_s": 0.4270149999647401, + "proc_s": 0.7125362500082701, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 4, + "refine_s": 0.22707120899576694, + "app_s": 0.3377184999990277, + "proc_s": 0.6167563329800032, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 4, + "refine_s": 0.19781408295966685, + "app_s": 0.3076567079988308, + "proc_s": 0.5686223339871503, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 4, + "refine_s": 0.18877470801817253, + "app_s": 0.30796687497058883, + "proc_s": 0.5741344160051085, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 4, + "refine_s": 0.17326350003713742, + "app_s": 0.28111983300186694, + "proc_s": 0.5335372499539517, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 4, + "refine_s": 0.16738762496970594, + "app_s": 0.2786986669525504, + "proc_s": 0.5285929999663495, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 4, + "refine_s": 0.16268191701965407, + "app_s": 0.26940437499433756, + "proc_s": 0.5171912920195609, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 4, + "refine_s": 0.18189741694368422, + "app_s": 0.302961542038247, + "proc_s": 0.5599164169980213, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 4, + "refine_s": 0.1626118749845773, + "app_s": 0.274246541026514, + "proc_s": 0.526281166006811, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 4, + "refine_s": 0.17744174995459616, + "app_s": 0.2994947909610346, + "proc_s": 0.5974470000364818, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 4, + "refine_s": 0.18796841701259837, + "app_s": 0.30176104203565046, + "proc_s": 0.5973611249937676, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 4, + "refine_s": 0.17079629097133875, + "app_s": 0.29191120801260695, + "proc_s": 0.5830624169902876, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 4, + "refine_s": 0.16729379096068442, + "app_s": 0.2806526659987867, + "proc_s": 0.5597729169530794, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 4, + "refine_s": 0.17182033299468458, + "app_s": 0.2834594590240158, + "proc_s": 0.5574531250167638, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 4, + "refine_s": 0.17367608298081905, + "app_s": 0.29247174999909475, + "proc_s": 0.5668028749641962, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 4, + "refine_s": 0.23027745902072638, + "app_s": 0.3888930829707533, + "proc_s": 0.6964206249685958, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 4, + "refine_s": 0.16567479202058166, + "app_s": 0.2859887080267072, + "proc_s": 0.5904182919766754, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 4, + "refine_s": 0.1672225000220351, + "app_s": 0.2744919579708949, + "proc_s": 0.5392536249710247, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 4, + "refine_s": 0.16305083298357204, + "app_s": 0.27254508301848546, + "proc_s": 0.534689707972575, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 0, + "refine_s": 0.19917220796924084, + "app_s": 0.35077041696058586, + "proc_s": 0.6473450839985162, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 0, + "refine_s": 0.4854459579801187, + "app_s": 0.6362973329960369, + "proc_s": 0.9348453329876065, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 0, + "refine_s": 0.3049939170014113, + "app_s": 0.44040870800381526, + "proc_s": 0.7067406660062261, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 0, + "refine_s": 0.2642280840082094, + "app_s": 0.39718966698274016, + "proc_s": 0.6576588329626247, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 0, + "refine_s": 0.2296028749551624, + "app_s": 0.3634288340108469, + "proc_s": 0.6189805410103872, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 0, + "refine_s": 0.2440299169975333, + "app_s": 0.3987625419977121, + "proc_s": 0.6664796670083888, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 0, + "refine_s": 0.2147720840293914, + "app_s": 0.35582716698991135, + "proc_s": 0.6276478749932721, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 0, + "refine_s": 0.19647329195868224, + "app_s": 0.3368403749773279, + "proc_s": 0.6097120839986019, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 0, + "refine_s": 0.19293991604354233, + "app_s": 0.3313615840161219, + "proc_s": 0.5860520829446614, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 0, + "refine_s": 0.19228729099268094, + "app_s": 0.33282008301466703, + "proc_s": 0.6011360419797711, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 0, + "refine_s": 0.1834328330005519, + "app_s": 0.31821629201294854, + "proc_s": 0.5854827500297688, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 0, + "refine_s": 0.1874422500259243, + "app_s": 0.3233438329771161, + "proc_s": 0.5746111660264432, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 0, + "refine_s": 0.20669354195706546, + "app_s": 0.34913562500150874, + "proc_s": 0.636073291010689, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 0, + "refine_s": 0.19041433301754296, + "app_s": 0.32640750001883134, + "proc_s": 0.6021562080131844, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 0, + "refine_s": 0.18097808299353346, + "app_s": 0.31570029095746577, + "proc_s": 0.5738937089918181, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 0, + "refine_s": 0.18929341598413885, + "app_s": 0.3274840419762768, + "proc_s": 0.5911951250163838, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 0, + "refine_s": 0.20589070895221084, + "app_s": 0.34111783298430964, + "proc_s": 0.6016529169864953, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 0, + "refine_s": 0.1855040829977952, + "app_s": 0.323038250033278, + "proc_s": 0.6032313749892637, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 0, + "refine_s": 0.19150129100307822, + "app_s": 0.33292179100681096, + "proc_s": 0.5979577499674633, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 0, + "refine_s": 0.21994679101044312, + "app_s": 0.37563308305107057, + "proc_s": 0.6618305000010878, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 0, + "refine_s": 0.1884642499499023, + "app_s": 0.3410930840182118, + "proc_s": 0.6281422500032932, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 1, + "refine_s": 0.19225737499073148, + "app_s": 0.3363465419970453, + "proc_s": 0.5975041670026258, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 1, + "refine_s": 0.4771259999834001, + "app_s": 0.615186334005557, + "proc_s": 0.8891082920017652, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 1, + "refine_s": 0.30629112501628697, + "app_s": 0.4428792090038769, + "proc_s": 0.6954196669976227, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 1, + "refine_s": 0.26179320801747963, + "app_s": 0.3965745840105228, + "proc_s": 0.6497008330188692, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 1, + "refine_s": 0.2268982919631526, + "app_s": 0.3711410830146633, + "proc_s": 0.6214504579547793, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 1, + "refine_s": 0.21280891698552296, + "app_s": 0.3503777500009164, + "proc_s": 0.6120180420111865, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 1, + "refine_s": 0.20883062499342486, + "app_s": 0.34419829200487584, + "proc_s": 0.5982147080358118, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 1, + "refine_s": 0.25446424999972805, + "app_s": 0.39134591701440513, + "proc_s": 0.6450530420406722, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 1, + "refine_s": 0.18715691700344905, + "app_s": 0.3228826660197228, + "proc_s": 0.5866993329836987, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 1, + "refine_s": 0.19395633303793147, + "app_s": 0.32944554201094434, + "proc_s": 0.59078154200688, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 1, + "refine_s": 0.18689783400623128, + "app_s": 0.32050974998855963, + "proc_s": 0.5736664169817232, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 1, + "refine_s": 0.19591583404690027, + "app_s": 0.33625962503720075, + "proc_s": 0.5982348329853266, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 1, + "refine_s": 0.18633679096819833, + "app_s": 0.3211309580365196, + "proc_s": 0.5725181250018068, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 1, + "refine_s": 0.1832428749767132, + "app_s": 0.31723587500164285, + "proc_s": 0.5677173329750076, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 1, + "refine_s": 0.19236691598780453, + "app_s": 0.32447579101426527, + "proc_s": 0.5741594169521704, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 1, + "refine_s": 0.18735495797591284, + "app_s": 0.3211781249847263, + "proc_s": 0.5793153750128113, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 1, + "refine_s": 0.19202829198911786, + "app_s": 0.32497775001684204, + "proc_s": 0.5747826659935527, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 1, + "refine_s": 0.1854902079794556, + "app_s": 0.3189247500267811, + "proc_s": 0.5760443749604747, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 1, + "refine_s": 0.19023100001504645, + "app_s": 0.3250996670103632, + "proc_s": 0.5745392499957234, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 1, + "refine_s": 0.19047004100866616, + "app_s": 0.32647391594946384, + "proc_s": 0.5989939169958234, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 1, + "refine_s": 0.18457037495682016, + "app_s": 0.31770258402684703, + "proc_s": 0.5690247080055997, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 2, + "refine_s": 0.18608304200461134, + "app_s": 0.3207204589853063, + "proc_s": 0.5732912499806844, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 2, + "refine_s": 0.463479541009292, + "app_s": 0.5968683749670163, + "proc_s": 0.8544134170515463, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 2, + "refine_s": 0.3092129579745233, + "app_s": 0.44437400001334026, + "proc_s": 0.6934190419851802, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 2, + "refine_s": 0.25173995801014826, + "app_s": 0.38824841601308435, + "proc_s": 0.6429891669540666, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 2, + "refine_s": 0.2526217499980703, + "app_s": 0.40419258299516514, + "proc_s": 0.687310624984093, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 2, + "refine_s": 0.20921495801303536, + "app_s": 0.3440692500444129, + "proc_s": 0.6281452080002055, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 2, + "refine_s": 0.20532141701551154, + "app_s": 0.34618345799390227, + "proc_s": 0.6078537920257077, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 2, + "refine_s": 0.19646529195597395, + "app_s": 0.3378194170072675, + "proc_s": 0.5974621249479242, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 2, + "refine_s": 0.1859615419525653, + "app_s": 0.3211138339829631, + "proc_s": 0.5731485420255922, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 2, + "refine_s": 0.19344941701274365, + "app_s": 0.3342689999844879, + "proc_s": 0.601802040997427, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 2, + "refine_s": 0.19891687500057742, + "app_s": 0.3395740420091897, + "proc_s": 0.617706332996022, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 2, + "refine_s": 0.1951877909596078, + "app_s": 0.3371910840505734, + "proc_s": 0.6141137079684995, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 2, + "refine_s": 0.19684520800365135, + "app_s": 0.33817304199328646, + "proc_s": 0.6176347500295378, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 2, + "refine_s": 0.19766437500948086, + "app_s": 0.34274712501792237, + "proc_s": 0.6167138330056332, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 2, + "refine_s": 0.19323874998372048, + "app_s": 0.3326750409905799, + "proc_s": 0.6125452910200693, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 2, + "refine_s": 0.2108918339945376, + "app_s": 0.3531314590363763, + "proc_s": 0.63594154198654, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 2, + "refine_s": 0.20672874996671453, + "app_s": 0.35374891699757427, + "proc_s": 0.6431773749645799, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 2, + "refine_s": 0.19774695800151676, + "app_s": 0.3418373329914175, + "proc_s": 0.6289436249644496, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 2, + "refine_s": 0.2000522090238519, + "app_s": 0.3424452080507763, + "proc_s": 0.6470812920015305, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 2, + "refine_s": 0.20474887499585748, + "app_s": 0.35919545899378136, + "proc_s": 0.6498212910373695, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 2, + "refine_s": 0.2069310000515543, + "app_s": 0.3475773750105873, + "proc_s": 0.6329257499892265, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 3, + "refine_s": 0.20741154201095924, + "app_s": 0.35303658299380913, + "proc_s": 0.6458747080178, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 3, + "refine_s": 0.5636805830290541, + "app_s": 0.7050725830486044, + "proc_s": 0.989417709002737, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 3, + "refine_s": 0.3684789580293, + "app_s": 0.5160477919853292, + "proc_s": 0.8100966669735499, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 3, + "refine_s": 0.3227231669588946, + "app_s": 0.4732847500126809, + "proc_s": 0.7737054999452084, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 3, + "refine_s": 0.24248016596538946, + "app_s": 0.38563308300217614, + "proc_s": 0.6711207080516033, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 3, + "refine_s": 0.22240241698455065, + "app_s": 0.3689771670033224, + "proc_s": 0.6456559579819441, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 3, + "refine_s": 0.20713362499373034, + "app_s": 0.3485042920219712, + "proc_s": 0.6304971249774098, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 3, + "refine_s": 0.21766704198671505, + "app_s": 0.36557058396283537, + "proc_s": 0.653636374976486, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 3, + "refine_s": 0.19384245801484212, + "app_s": 0.3286877910140902, + "proc_s": 0.6013096669921651, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 3, + "refine_s": 0.19113845797255635, + "app_s": 0.3319181249826215, + "proc_s": 0.5903944169986062, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 3, + "refine_s": 0.19886499998392537, + "app_s": 0.34311370900832117, + "proc_s": 0.6080488750012591, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 3, + "refine_s": 0.21336858300492167, + "app_s": 0.3539418340078555, + "proc_s": 0.6312262499704957, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 3, + "refine_s": 0.19147550000343472, + "app_s": 0.3284717909991741, + "proc_s": 0.5939591249916703, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 3, + "refine_s": 0.2016421250300482, + "app_s": 0.3424822920351289, + "proc_s": 0.6347988339839503, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 3, + "refine_s": 0.18699891597498208, + "app_s": 0.3209556249785237, + "proc_s": 0.573080874979496, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 3, + "refine_s": 0.18218608398456126, + "app_s": 0.31848987500416115, + "proc_s": 0.5725254170247354, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 3, + "refine_s": 0.18911091599147767, + "app_s": 0.3232910420047119, + "proc_s": 0.5712318749865517, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 3, + "refine_s": 0.18789108301280066, + "app_s": 0.3246953329653479, + "proc_s": 0.5740485839778557, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 3, + "refine_s": 0.18603287503356114, + "app_s": 0.3191799999913201, + "proc_s": 0.5678427090169862, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 3, + "refine_s": 0.18716295901685953, + "app_s": 0.3205465830396861, + "proc_s": 0.5722935420344584, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 3, + "refine_s": 0.18919370794901624, + "app_s": 0.3236441249609925, + "proc_s": 0.5714962920174003, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 4, + "refine_s": 0.1850895420066081, + "app_s": 0.32071679102955386, + "proc_s": 0.5675027089891955, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 4, + "refine_s": 0.46195212501334026, + "app_s": 0.5967188339564018, + "proc_s": 0.8504110419889912, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 4, + "refine_s": 0.3098374580149539, + "app_s": 0.44202529202448204, + "proc_s": 0.691721833019983, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 4, + "refine_s": 0.251035374996718, + "app_s": 0.38643566699465737, + "proc_s": 0.6516602080082521, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 4, + "refine_s": 0.22499433299526572, + "app_s": 0.3585833749966696, + "proc_s": 0.6063032919773832, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 4, + "refine_s": 0.2175148749956861, + "app_s": 0.3601654159720056, + "proc_s": 0.6125090419664048, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 4, + "refine_s": 0.19815520802512765, + "app_s": 0.333049583947286, + "proc_s": 0.5957159579847939, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 4, + "refine_s": 0.19402695901226252, + "app_s": 0.33111779199680313, + "proc_s": 0.6103017500136048, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 4, + "refine_s": 0.18379479204304516, + "app_s": 0.31620370800374076, + "proc_s": 0.5654291249811649, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 4, + "refine_s": 0.18788491701707244, + "app_s": 0.3429739999701269, + "proc_s": 0.647131250007078, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 4, + "refine_s": 0.20221187500283122, + "app_s": 0.3433958329842426, + "proc_s": 0.5996139589697123, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 4, + "refine_s": 0.1864718750002794, + "app_s": 0.322388915985357, + "proc_s": 0.5775644580135122, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 4, + "refine_s": 0.18907416705042124, + "app_s": 0.3247494170209393, + "proc_s": 0.5807200840208679, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 4, + "refine_s": 0.18543629202758893, + "app_s": 0.3195472500519827, + "proc_s": 0.5677466670167632, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 4, + "refine_s": 0.19556162500521168, + "app_s": 0.32766524999169633, + "proc_s": 0.5779434579890221, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 4, + "refine_s": 0.21279362501809373, + "app_s": 0.35280670796055347, + "proc_s": 0.6004228750243783, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 4, + "refine_s": 0.20986462500877678, + "app_s": 0.3426174169871956, + "proc_s": 0.6042510829865932, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 4, + "refine_s": 0.2007990829879418, + "app_s": 0.3389747080509551, + "proc_s": 0.6127103330218233, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 4, + "refine_s": 0.19450820802012458, + "app_s": 0.33476137497927994, + "proc_s": 0.6108257910236716, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 4, + "refine_s": 0.19362895900849253, + "app_s": 0.33304608397884294, + "proc_s": 0.60031650000019, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 4, + "refine_s": 0.19002066703978926, + "app_s": 0.326836165972054, + "proc_s": 0.5838672089739703, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + } + ], + "stats": [ + { + "domain": "quarter", + "threads": 0, + "n": 5, + "median": 0.16451245895586908, + "min": 0.1634274579701014, + "max": 0.19552475004456937 + }, + { + "domain": "quarter", + "threads": 1, + "n": 5, + "median": 0.4240817920072004, + "min": 0.41324575000908226, + "max": 0.45486345898825675 + }, + { + "domain": "quarter", + "threads": 2, + "n": 5, + "median": 0.27646716695744544, + "min": 0.2662517089629546, + "max": 0.3090555419912562 + }, + { + "domain": "quarter", + "threads": 3, + "n": 5, + "median": 0.22616637498140335, + "min": 0.22123808303149417, + "max": 0.2616728750290349 + }, + { + "domain": "quarter", + "threads": 4, + "n": 5, + "median": 0.20127125002909452, + "min": 0.19781408295966685, + "max": 0.2109092500177212 + }, + { + "domain": "quarter", + "threads": 5, + "n": 5, + "median": 0.18812333297682926, + "min": 0.18469825002830476, + "max": 0.19117499998537824 + }, + { + "domain": "quarter", + "threads": 6, + "n": 5, + "median": 0.1753185410052538, + "min": 0.17326350003713742, + "max": 0.18333362496923655 + }, + { + "domain": "quarter", + "threads": 7, + "n": 5, + "median": 0.170433041988872, + "min": 0.16688937495928258, + "max": 0.22830112499650568 + }, + { + "domain": "quarter", + "threads": 8, + "n": 5, + "median": 0.1634364589699544, + "min": 0.1624052079860121, + "max": 0.17081454198341817 + }, + { + "domain": "quarter", + "threads": 9, + "n": 5, + "median": 0.16597604198614135, + "min": 0.16410149994771928, + "max": 0.18189741694368422 + }, + { + "domain": "quarter", + "threads": 10, + "n": 5, + "median": 0.1643501669750549, + "min": 0.1626118749845773, + "max": 0.1710080840275623 + }, + { + "domain": "quarter", + "threads": 11, + "n": 5, + "median": 0.1634957090136595, + "min": 0.16020687500713393, + "max": 0.17744174995459616 + }, + { + "domain": "quarter", + "threads": 12, + "n": 5, + "median": 0.16521733300760388, + "min": 0.16280254197772592, + "max": 0.18796841701259837 + }, + { + "domain": "quarter", + "threads": 13, + "n": 5, + "median": 0.16905729100108147, + "min": 0.1642955410061404, + "max": 0.1739992499933578 + }, + { + "domain": "quarter", + "threads": 14, + "n": 5, + "median": 0.16729379096068442, + "min": 0.16030120803043246, + "max": 0.17541254102252424 + }, + { + "domain": "quarter", + "threads": 15, + "n": 5, + "median": 0.16377741703763604, + "min": 0.16202395799336955, + "max": 0.17182033299468458 + }, + { + "domain": "quarter", + "threads": 16, + "n": 5, + "median": 0.17032591701718047, + "min": 0.16436095803510398, + "max": 0.17367608298081905 + }, + { + "domain": "quarter", + "threads": 17, + "n": 5, + "median": 0.16586275002919137, + "min": 0.1630828749621287, + "max": 0.23027745902072638 + }, + { + "domain": "quarter", + "threads": 18, + "n": 5, + "median": 0.16567479202058166, + "min": 0.16471275000367314, + "max": 0.18921354197664186 + }, + { + "domain": "quarter", + "threads": 19, + "n": 5, + "median": 0.16794800001662225, + "min": 0.16500854201149195, + "max": 0.17104362498503178 + }, + { + "domain": "quarter", + "threads": 20, + "n": 5, + "median": 0.16914937499677762, + "min": 0.16305083298357204, + "max": 0.18084637494757771 + }, + { + "domain": "tile", + "threads": 0, + "n": 5, + "median": 0.19225737499073148, + "min": 0.1850895420066081, + "max": 0.20741154201095924 + }, + { + "domain": "tile", + "threads": 1, + "n": 5, + "median": 0.4771259999834001, + "min": 0.46195212501334026, + "max": 0.5636805830290541 + }, + { + "domain": "tile", + "threads": 2, + "n": 5, + "median": 0.3092129579745233, + "min": 0.3049939170014113, + "max": 0.3684789580293 + }, + { + "domain": "tile", + "threads": 3, + "n": 5, + "median": 0.26179320801747963, + "min": 0.251035374996718, + "max": 0.3227231669588946 + }, + { + "domain": "tile", + "threads": 4, + "n": 5, + "median": 0.2296028749551624, + "min": 0.22499433299526572, + "max": 0.2526217499980703 + }, + { + "domain": "tile", + "threads": 5, + "n": 5, + "median": 0.2175148749956861, + "min": 0.20921495801303536, + "max": 0.2440299169975333 + }, + { + "domain": "tile", + "threads": 6, + "n": 5, + "median": 0.20713362499373034, + "min": 0.19815520802512765, + "max": 0.2147720840293914 + }, + { + "domain": "tile", + "threads": 7, + "n": 5, + "median": 0.19647329195868224, + "min": 0.19402695901226252, + "max": 0.25446424999972805 + }, + { + "domain": "tile", + "threads": 8, + "n": 5, + "median": 0.18715691700344905, + "min": 0.18379479204304516, + "max": 0.19384245801484212 + }, + { + "domain": "tile", + "threads": 9, + "n": 5, + "median": 0.19228729099268094, + "min": 0.18788491701707244, + "max": 0.19395633303793147 + }, + { + "domain": "tile", + "threads": 10, + "n": 5, + "median": 0.19886499998392537, + "min": 0.1834328330005519, + "max": 0.20221187500283122 + }, + { + "domain": "tile", + "threads": 11, + "n": 5, + "median": 0.1951877909596078, + "min": 0.1864718750002794, + "max": 0.21336858300492167 + }, + { + "domain": "tile", + "threads": 12, + "n": 5, + "median": 0.19147550000343472, + "min": 0.18633679096819833, + "max": 0.20669354195706546 + }, + { + "domain": "tile", + "threads": 13, + "n": 5, + "median": 0.19041433301754296, + "min": 0.1832428749767132, + "max": 0.2016421250300482 + }, + { + "domain": "tile", + "threads": 14, + "n": 5, + "median": 0.19236691598780453, + "min": 0.18097808299353346, + "max": 0.19556162500521168 + }, + { + "domain": "tile", + "threads": 15, + "n": 5, + "median": 0.18929341598413885, + "min": 0.18218608398456126, + "max": 0.21279362501809373 + }, + { + "domain": "tile", + "threads": 16, + "n": 5, + "median": 0.20589070895221084, + "min": 0.18911091599147767, + "max": 0.20986462500877678 + }, + { + "domain": "tile", + "threads": 17, + "n": 5, + "median": 0.18789108301280066, + "min": 0.1854902079794556, + "max": 0.2007990829879418 + }, + { + "domain": "tile", + "threads": 18, + "n": 5, + "median": 0.19150129100307822, + "min": 0.18603287503356114, + "max": 0.2000522090238519 + }, + { + "domain": "tile", + "threads": 19, + "n": 5, + "median": 0.19362895900849253, + "min": 0.18716295901685953, + "max": 0.21994679101044312 + }, + { + "domain": "tile", + "threads": 20, + "n": 5, + "median": 0.18919370794901624, + "min": 0.18457037495682016, + "max": 0.2069310000515543 + } + ], + "quality": { + "quarter": { + "worst_angle": 0.3955267116350698, + "angle_median": 45.0, + "share_under_1": 0.000028023175165862168, + "max_degree": 18, + "within_tolerance": true, + "delaunay_checked": 641791, + "delaunay_ambiguous": 80136, + "delaunay_violations": 0, + "mesh_sha256": "ccebf96a86c6c5e244e4a0281919de4e866fcfe789b66024a290ac2af33771a1" + }, + "tile": { + "worst_angle": 0.6295986084102668, + "angle_median": 45.0, + "share_under_1": 2.1538262723728703e-6, + "max_degree": 74, + "within_tolerance": true, + "delaunay_checked": 692056, + "delaunay_ambiguous": 94469, + "delaunay_violations": 0, + "mesh_sha256": "11741a81adfa17b34a1ea56875ec9e791414d17625eb1c58305e7c0cf10e005c" + } + }, + "accept_quality": false, + "threshold_pct": 5.0, + "verdict": [ + "REGRESSION: tile refine_s[t=10] 0.1860 -> 0.1989 (+6.9 %)", + "REGRESSION: tile refine_s[t=16] 0.1906 -> 0.2059 (+8.0 %)" + ] +} diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/README.md b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/README.md new file mode 100644 index 00000000..224bcf3f --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/README.md @@ -0,0 +1,79 @@ +# Benchmark run `16b0-acceptance/16b0-r3` + +Generated by `tools/bench.py`. + +## Verdict + +- ACCEPTED + +Time threshold: 5.0 %. + +## Method + +- Started 2026-09-28T15:50:52.507316+02:00; tree `4e833893377430683ac1dce967783ead5ff9a2de` (dirty); bench.py blob `77765b181714c31714da476c2335c799c6a84021`. Build: Release, AppleClang 21.0.0.21000101, `-O3 -DNDEBUG`, _core sha256 `190587e7ba0ea96a74a8621767d7d20257c3968126a710db1364e1195afb27d2`. +- Apple M1 Max, 8 P + 2 E cores, 32 GiB, macOS 27.0, Python 3.14.7, numpy 2.5.3. +- Power **battery** (62%), `pmset -g batt` before and after (in run.json). +- DEM `/Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif` (sha256 `aabd0cbc28471ce8593e4c278381811bec3fbf4e1194058b3b4888d00c3af575`), tolerance 1.0, extra mesh args ``; domains: `quarter` (/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-26/quarter.geojson), `tile` (the whole tile). +- One child per sample, `/Users/skavhaug/projects/rasputin/.venv/bin/python /Users/skavhaug/projects/rasputin/tools/bench.py _child --pkg /Users/skavhaug/projects/rasputin/build-bench/pkg --threads -- mesh --dem /Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif --tolerance 1 --out --binary`, repeats interleaved over thread counts; t=0 is the CLI's default, other counts are forced into `refine`. Quality: one `--ascii` run per domain at t=0, kept out of the repository; rerun the child with `--ascii --out PATH` to regenerate it. + +## `quarter` + +| threads | n | median s | min s | max s | +|---:|---:|---:|---:|---:| +| 0 | 5 | 0.1648 | 0.1576 | 0.1665 | +| 1 | 5 | 0.4077 | 0.4019 | 0.4090 | +| 2 | 5 | 0.2637 | 0.2619 | 0.2708 | +| 3 | 5 | 0.2196 | 0.2169 | 0.2240 | +| 4 | 5 | 0.1987 | 0.1962 | 0.2023 | +| 5 | 5 | 0.1823 | 0.1813 | 0.1844 | +| 6 | 5 | 0.1730 | 0.1724 | 0.1774 | +| 7 | 5 | 0.1654 | 0.1649 | 0.1660 | +| 8 | 5 | 0.1616 | 0.1604 | 0.1624 | +| 9 | 5 | 0.1657 | 0.1590 | 0.1662 | +| 10 | 5 | 0.1636 | 0.1604 | 0.1732 | +| 11 | 5 | 0.1643 | 0.1602 | 0.1686 | +| 12 | 5 | 0.1648 | 0.1592 | 0.1660 | +| 13 | 5 | 0.1646 | 0.1622 | 0.1681 | +| 14 | 5 | 0.1621 | 0.1580 | 0.1663 | +| 15 | 5 | 0.1618 | 0.1579 | 0.1656 | +| 16 | 5 | 0.1617 | 0.1591 | 0.1646 | +| 17 | 5 | 0.1670 | 0.1597 | 0.1679 | +| 18 | 5 | 0.1616 | 0.1588 | 0.1650 | +| 19 | 5 | 0.1644 | 0.1597 | 0.1681 | +| 20 | 5 | 0.1655 | 0.1589 | 0.1675 | + +Ceiling: 2.46x at 20 threads over 1, best 2.52x at 8; 2026-09-26: about 2.2x, flat from about 7. + +Quality: worst angle 0.3955 deg, median 45.00, share under 1 deg 0.00003, max degree 18, within tolerance True, Delaunay 0 violations of 641791 edges (80136 decided exactly), mesh sha256 `ccebf96a86c6c5e244e4a0281919de4e866fcfe789b66024a290ac2af33771a1`. + +## `tile` + +| threads | n | median s | min s | max s | +|---:|---:|---:|---:|---:| +| 0 | 5 | 0.1860 | 0.1803 | 0.1913 | +| 1 | 5 | 0.4641 | 0.4559 | 0.4735 | +| 2 | 5 | 0.3027 | 0.3007 | 0.3087 | +| 3 | 5 | 0.2490 | 0.2473 | 0.2586 | +| 4 | 5 | 0.2288 | 0.2249 | 0.2327 | +| 5 | 5 | 0.2122 | 0.2090 | 0.2174 | +| 6 | 5 | 0.2010 | 0.1998 | 0.2041 | +| 7 | 5 | 0.1892 | 0.1875 | 0.1938 | +| 8 | 5 | 0.1846 | 0.1830 | 0.1922 | +| 9 | 5 | 0.1870 | 0.1863 | 0.1918 | +| 10 | 5 | 0.1847 | 0.1827 | 0.1869 | +| 11 | 5 | 0.1821 | 0.1802 | 0.1862 | +| 12 | 5 | 0.1857 | 0.1818 | 0.1886 | +| 13 | 5 | 0.1858 | 0.1806 | 0.1874 | +| 14 | 5 | 0.1868 | 0.1847 | 0.1893 | +| 15 | 5 | 0.1874 | 0.1841 | 0.1900 | +| 16 | 5 | 0.1881 | 0.1808 | 0.3363 | +| 17 | 5 | 0.1867 | 0.1861 | 0.1922 | +| 18 | 5 | 0.1878 | 0.1819 | 0.1893 | +| 19 | 5 | 0.1855 | 0.1812 | 0.1876 | +| 20 | 5 | 0.1868 | 0.1838 | 0.1955 | + +Ceiling: 2.48x at 20 threads over 1, best 2.55x at 11; 2026-09-26: about 2.2x, flat from about 7. + +Quality: worst angle 0.6296 deg, median 45.00, share under 1 deg 0.00000, max degree 74, within tolerance True, Delaunay 0 violations of 692056 edges (94469 decided exactly), mesh sha256 `11741a81adfa17b34a1ea56875ec9e791414d17625eb1c58305e7c0cf10e005c`. + + diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/raw.tsv b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/raw.tsv new file mode 100644 index 00000000..65db7731 --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/raw.tsv @@ -0,0 +1,210 @@ +quarter 0 0 0.166481 battery +quarter 1 0 0.405261 battery +quarter 2 0 0.270764 battery +quarter 3 0 0.220589 battery +quarter 4 0 0.198687 battery +quarter 5 0 0.181251 battery +quarter 6 0 0.172357 battery +quarter 7 0 0.165467 battery +quarter 8 0 0.160377 battery +quarter 9 0 0.163851 battery +quarter 10 0 0.160368 battery +quarter 11 0 0.164279 battery +quarter 12 0 0.164843 battery +quarter 13 0 0.164594 battery +quarter 14 0 0.157954 battery +quarter 15 0 0.165580 battery +quarter 16 0 0.164488 battery +quarter 17 0 0.167250 battery +quarter 18 0 0.158800 battery +quarter 19 0 0.168066 battery +quarter 20 0 0.165644 battery +quarter 0 1 0.164839 battery +quarter 1 1 0.408072 battery +quarter 2 1 0.261864 battery +quarter 3 1 0.219569 battery +quarter 4 1 0.196977 battery +quarter 5 1 0.182081 battery +quarter 6 1 0.173046 battery +quarter 7 1 0.165415 battery +quarter 8 1 0.161031 battery +quarter 9 1 0.165703 battery +quarter 10 1 0.165899 battery +quarter 11 1 0.165694 battery +quarter 12 1 0.164795 battery +quarter 13 1 0.163269 battery +quarter 14 1 0.162087 battery +quarter 15 1 0.158569 battery +quarter 16 1 0.159104 battery +quarter 17 1 0.159696 battery +quarter 18 1 0.161525 battery +quarter 19 1 0.159745 battery +quarter 20 1 0.161523 battery +quarter 0 2 0.157587 battery +quarter 1 2 0.407698 battery +quarter 2 2 0.263888 battery +quarter 3 2 0.216852 battery +quarter 4 2 0.202285 battery +quarter 5 2 0.182614 battery +quarter 6 2 0.177380 battery +quarter 7 2 0.164853 battery +quarter 8 2 0.161551 battery +quarter 9 2 0.158993 battery +quarter 10 2 0.173229 battery +quarter 11 2 0.160158 battery +quarter 12 2 0.159211 battery +quarter 13 2 0.162187 battery +quarter 14 2 0.158945 battery +quarter 15 2 0.157893 battery +quarter 16 2 0.161743 battery +quarter 17 2 0.167041 battery +quarter 18 2 0.162097 battery +quarter 19 2 0.164408 battery +quarter 20 2 0.158897 battery +quarter 0 3 0.164897 battery +quarter 1 3 0.401922 battery +quarter 2 3 0.262830 battery +quarter 3 3 0.218695 battery +quarter 4 3 0.196153 battery +quarter 5 3 0.184371 battery +quarter 6 3 0.172357 battery +quarter 7 3 0.165154 battery +quarter 8 3 0.162062 battery +quarter 9 3 0.166247 battery +quarter 10 3 0.162591 battery +quarter 11 3 0.168609 battery +quarter 12 3 0.165994 battery +quarter 13 3 0.168050 battery +quarter 14 3 0.166291 battery +quarter 15 3 0.165218 battery +quarter 16 3 0.164604 battery +quarter 17 3 0.167852 battery +quarter 18 3 0.165019 battery +quarter 19 3 0.165914 battery +quarter 20 3 0.165518 battery +quarter 0 4 0.163720 battery +quarter 1 4 0.409047 battery +quarter 2 4 0.263727 battery +quarter 3 4 0.224029 battery +quarter 4 4 0.200721 battery +quarter 5 4 0.182262 battery +quarter 6 4 0.172986 battery +quarter 7 4 0.166046 battery +quarter 8 4 0.162408 battery +quarter 9 4 0.165879 battery +quarter 10 4 0.163631 battery +quarter 11 4 0.162644 battery +quarter 12 4 0.163050 battery +quarter 13 4 0.167118 battery +quarter 14 4 0.165520 battery +quarter 15 4 0.161838 battery +quarter 16 4 0.159733 battery +quarter 17 4 0.160825 battery +quarter 18 4 0.161620 battery +quarter 19 4 0.160130 battery +quarter 20 4 0.167519 battery +tile 0 0 0.180976 battery +tile 1 0 0.461853 battery +tile 2 0 0.300733 battery +tile 3 0 0.258581 battery +tile 4 0 0.229479 battery +tile 5 0 0.209039 battery +tile 6 0 0.203668 battery +tile 7 0 0.187490 battery +tile 8 0 0.186555 battery +tile 9 0 0.187453 battery +tile 10 0 0.184981 battery +tile 11 0 0.185451 battery +tile 12 0 0.181775 battery +tile 13 0 0.185528 battery +tile 14 0 0.186793 battery +tile 15 0 0.187384 battery +tile 16 0 0.188100 battery +tile 17 0 0.186719 battery +tile 18 0 0.189336 battery +tile 19 0 0.187046 battery +tile 20 0 0.188083 battery +tile 0 1 0.191256 battery +tile 1 1 0.455926 battery +tile 2 1 0.302733 battery +tile 3 1 0.247282 battery +tile 4 1 0.224920 battery +tile 5 1 0.217112 battery +tile 6 1 0.200941 battery +tile 7 1 0.188713 battery +tile 8 1 0.183285 battery +tile 9 1 0.187049 battery +tile 10 1 0.184661 battery +tile 11 1 0.180190 battery +tile 12 1 0.185688 battery +tile 13 1 0.186606 battery +tile 14 1 0.187952 battery +tile 15 1 0.188043 battery +tile 16 1 0.336332 battery +tile 17 1 0.186300 battery +tile 18 1 0.181904 battery +tile 19 1 0.181180 battery +tile 20 1 0.186806 battery +tile 0 2 0.180310 battery +tile 1 2 0.473511 battery +tile 2 2 0.308704 battery +tile 3 2 0.248402 battery +tile 4 2 0.228766 battery +tile 5 2 0.209698 battery +tile 6 2 0.199782 battery +tile 7 2 0.193831 battery +tile 8 2 0.183001 battery +tile 9 2 0.186472 battery +tile 10 2 0.184595 battery +tile 11 2 0.182057 battery +tile 12 2 0.185717 battery +tile 13 2 0.180570 battery +tile 14 2 0.189255 battery +tile 15 2 0.190038 battery +tile 16 2 0.201310 battery +tile 17 2 0.187842 battery +tile 18 2 0.188732 battery +tile 19 2 0.187617 battery +tile 20 2 0.183849 battery +tile 0 3 0.186005 battery +tile 1 3 0.464107 battery +tile 2 3 0.302330 battery +tile 3 3 0.249606 battery +tile 4 3 0.232677 battery +tile 5 3 0.212168 battery +tile 6 3 0.204135 battery +tile 7 3 0.189247 battery +tile 8 3 0.184576 battery +tile 9 3 0.191768 battery +tile 10 3 0.186918 battery +tile 11 3 0.186208 battery +tile 12 3 0.182687 battery +tile 13 3 0.187372 battery +tile 14 3 0.186492 battery +tile 15 3 0.184108 battery +tile 16 3 0.187191 battery +tile 17 3 0.186085 battery +tile 18 3 0.187830 battery +tile 19 3 0.185521 battery +tile 20 3 0.195496 battery +tile 0 4 0.186902 battery +tile 1 4 0.465867 battery +tile 2 4 0.303577 battery +tile 3 4 0.249012 battery +tile 4 4 0.228043 battery +tile 5 4 0.217425 battery +tile 6 4 0.201002 battery +tile 7 4 0.191475 battery +tile 8 4 0.192170 battery +tile 9 4 0.186339 battery +tile 10 4 0.182748 battery +tile 11 4 0.180598 battery +tile 12 4 0.188599 battery +tile 13 4 0.185844 battery +tile 14 4 0.184674 battery +tile 15 4 0.184120 battery +tile 16 4 0.180781 battery +tile 17 4 0.192226 battery +tile 18 4 0.185830 battery +tile 19 4 0.183661 battery +tile 20 4 0.184597 battery diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/run.json b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/run.json new file mode 100644 index 00000000..d71fb40a --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0-r3/run.json @@ -0,0 +1,2955 @@ +{ + "label": "16b0-acceptance/16b0-r3", + "started": "2026-09-28T15:50:52.507316+02:00", + "tree": { + "commit": "4e833893377430683ac1dce967783ead5ff9a2de", + "dirty": true + }, + "bench_blob": "77765b181714c31714da476c2335c799c6a84021", + "child_argv": [ + "/Users/skavhaug/projects/rasputin/.venv/bin/python", + "/Users/skavhaug/projects/rasputin/tools/bench.py", + "_child", + "--pkg", + "/Users/skavhaug/projects/rasputin/build-bench/pkg", + "--threads", + "", + "--", + "mesh", + "--dem", + "/Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif", + "--tolerance", + "1", + "--out", + "", + "--binary" + ], + "build": { + "no_build": false, + "type": "Release", + "cxx_flags_release": "-O3 -DNDEBUG", + "compiler": "AppleClang 21.0.0.21000101", + "so_sha256": "190587e7ba0ea96a74a8621767d7d20257c3968126a710db1364e1195afb27d2" + }, + "machine": { + "cpu_brand": "Apple M1 Max", + "p_cores": 8, + "e_cores": 2, + "memory_bytes": 34359738368, + "macos": "27.0", + "python": "3.14.7", + "numpy": "2.5.3" + }, + "power": { + "state": "battery", + "percent": 62, + "raw": "Now drawing from 'Battery Power'\n -InternalBattery-0 (id=7929955)\t63%; discharging; 2:28 remaining present: true\n--- after ---\nNow drawing from 'Battery Power'\n -InternalBattery-0 (id=7929955)\t62%; discharging; 2:58 remaining present: true\n" + }, + "inputs": { + "dem": "/Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif", + "dem_sha256": "aabd0cbc28471ce8593e4c278381811bec3fbf4e1194058b3b4888d00c3af575", + "domains": [ + { + "name": "quarter", + "path": "/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-26/quarter.geojson", + "sha256": "b7f7bbe96848383c77863ce29ee28c45bce087601a1738ee82ddb4ffb8279d1e" + }, + { + "name": "tile", + "path": null, + "sha256": null + } + ], + "tolerance": 1.0, + "extra_args": [] + }, + "samples": [ + { + "domain": "quarter", + "threads": 0, + "repeat": 0, + "refine_s": 0.1664806249900721, + "app_s": 0.2872392500285059, + "proc_s": 0.8627555000130087, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 0, + "refine_s": 0.40526066604070365, + "app_s": 0.5117707499885, + "proc_s": 0.768482125014998, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 0, + "refine_s": 0.2707637079874985, + "app_s": 0.38169237499823794, + "proc_s": 0.6311903329915367, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 0, + "refine_s": 0.22058908303733915, + "app_s": 0.32502399996155873, + "proc_s": 0.5744309579604305, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 0, + "refine_s": 0.19868716702330858, + "app_s": 0.3101829580264166, + "proc_s": 0.5624195419950411, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 0, + "refine_s": 0.18125120899640024, + "app_s": 0.29039354203268886, + "proc_s": 0.5369748750235885, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 0, + "refine_s": 0.17235658399295062, + "app_s": 0.28100995800923556, + "proc_s": 0.5329520840314217, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 0, + "refine_s": 0.1654671669821255, + "app_s": 0.2744964580051601, + "proc_s": 0.5259632089873776, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 0, + "refine_s": 0.1603774999966845, + "app_s": 0.26817516703158617, + "proc_s": 0.5131736249895766, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 0, + "refine_s": 0.16385104099754244, + "app_s": 0.26874508295441046, + "proc_s": 0.5170851669972762, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 0, + "refine_s": 0.1603675420046784, + "app_s": 0.26728937501320615, + "proc_s": 0.5141372919897549, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 0, + "refine_s": 0.16427899996051565, + "app_s": 0.27338091598358005, + "proc_s": 0.5211164579959586, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 0, + "refine_s": 0.16484325000783429, + "app_s": 0.2730388750205748, + "proc_s": 0.5252181250252761, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 0, + "refine_s": 0.1645944170304574, + "app_s": 0.2708107500220649, + "proc_s": 0.5217046670150012, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 0, + "refine_s": 0.15795433399034664, + "app_s": 0.26691808400209993, + "proc_s": 0.5159436659887433, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 0, + "refine_s": 0.16557962499791756, + "app_s": 0.27355558396084234, + "proc_s": 0.5199992500129156, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 0, + "refine_s": 0.16448812501039356, + "app_s": 0.2695443749544211, + "proc_s": 0.5158076249645092, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 0, + "refine_s": 0.1672496670507826, + "app_s": 0.2717411249759607, + "proc_s": 0.5176931250025518, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 0, + "refine_s": 0.15880020800977945, + "app_s": 0.2651012919959612, + "proc_s": 0.5130249169887975, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 0, + "refine_s": 0.1680661659920588, + "app_s": 0.2751356669468805, + "proc_s": 0.5214782499824651, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 0, + "refine_s": 0.1656444999971427, + "app_s": 0.2712791250087321, + "proc_s": 0.518828583008144, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 1, + "refine_s": 0.16483875003177673, + "app_s": 0.27094916702480987, + "proc_s": 0.5190539169707336, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 1, + "refine_s": 0.4080717909964733, + "app_s": 0.5137509170453995, + "proc_s": 0.7608117499621585, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 1, + "refine_s": 0.2618640000000596, + "app_s": 0.36996374995214865, + "proc_s": 0.6344616250135005, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 1, + "refine_s": 0.21956925000995398, + "app_s": 0.324569208023604, + "proc_s": 0.570900124963373, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 1, + "refine_s": 0.1969769160496071, + "app_s": 0.30258204200072214, + "proc_s": 0.5508912079967558, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 1, + "refine_s": 0.18208087497623637, + "app_s": 0.28809225000441074, + "proc_s": 0.5385190409724601, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 1, + "refine_s": 0.1730458340025507, + "app_s": 0.28007712500402704, + "proc_s": 0.5273256250075065, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 1, + "refine_s": 0.16541541699552909, + "app_s": 0.2724116659956053, + "proc_s": 0.5204181249719113, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 1, + "refine_s": 0.16103070799726993, + "app_s": 0.26683320797747, + "proc_s": 0.5143139580031857, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 1, + "refine_s": 0.1657028750050813, + "app_s": 0.2764597500208765, + "proc_s": 0.52627254195977, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 1, + "refine_s": 0.1658988749841228, + "app_s": 0.2746437080204487, + "proc_s": 0.5271326669608243, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 1, + "refine_s": 0.16569375002291054, + "app_s": 0.27291004097787663, + "proc_s": 0.5257485419861041, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 1, + "refine_s": 0.16479483299190179, + "app_s": 0.27320879203034565, + "proc_s": 0.523999749973882, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 1, + "refine_s": 0.16326937498524785, + "app_s": 0.27373958396492526, + "proc_s": 0.532236416998785, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 1, + "refine_s": 0.16208654205547646, + "app_s": 0.2799216670100577, + "proc_s": 0.5692616659798659, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 1, + "refine_s": 0.158569291990716, + "app_s": 0.26457075000507757, + "proc_s": 0.5186565829790197, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 1, + "refine_s": 0.15910387499025092, + "app_s": 0.2699403330334462, + "proc_s": 0.5173719159793109, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 1, + "refine_s": 0.1596961670438759, + "app_s": 0.2690234170295298, + "proc_s": 0.5153135000145994, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 1, + "refine_s": 0.16152483300538734, + "app_s": 0.2699075830169022, + "proc_s": 0.52366795897251, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 1, + "refine_s": 0.15974475000984967, + "app_s": 0.2650584999937564, + "proc_s": 0.5177312080049887, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 1, + "refine_s": 0.16152300004614517, + "app_s": 0.267149374994915, + "proc_s": 0.5145812919945456, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 2, + "refine_s": 0.1575865829945542, + "app_s": 0.26376825000625104, + "proc_s": 0.5087786249932833, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 2, + "refine_s": 0.4076976249925792, + "app_s": 0.5132638749782927, + "proc_s": 0.758827542013023, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 2, + "refine_s": 0.2638877920107916, + "app_s": 0.3712174999527633, + "proc_s": 0.6202782089821994, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 2, + "refine_s": 0.2168518750113435, + "app_s": 0.32198562496341765, + "proc_s": 0.5740649159997702, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 2, + "refine_s": 0.20228541700635105, + "app_s": 0.3132272909861058, + "proc_s": 0.560635874979198, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 2, + "refine_s": 0.18261362495832145, + "app_s": 0.29706154199084267, + "proc_s": 0.5576626250403933, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 2, + "refine_s": 0.17738029203610495, + "app_s": 0.2984577500028536, + "proc_s": 0.5537522079539485, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 2, + "refine_s": 0.16485262499190867, + "app_s": 0.2773374590324238, + "proc_s": 0.5298222919809632, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 2, + "refine_s": 0.16155074996640906, + "app_s": 0.2671445829910226, + "proc_s": 0.5169919169857167, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 2, + "refine_s": 0.1589934160001576, + "app_s": 0.26887583395000547, + "proc_s": 0.5195850000018254, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 2, + "refine_s": 0.17322870896896347, + "app_s": 0.2803869160125032, + "proc_s": 0.5344701249850914, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 2, + "refine_s": 0.1601576249813661, + "app_s": 0.26799283298896626, + "proc_s": 0.5186744580278173, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 2, + "refine_s": 0.15921091701602563, + "app_s": 0.2677964169997722, + "proc_s": 0.5202006250037812, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 2, + "refine_s": 0.1621871660463512, + "app_s": 0.2703508749837056, + "proc_s": 0.5229316250188276, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 2, + "refine_s": 0.1589449580060318, + "app_s": 0.267010458977893, + "proc_s": 0.5159742079558782, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 2, + "refine_s": 0.15789279097225517, + "app_s": 0.2657452910207212, + "proc_s": 0.5142495830077678, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 2, + "refine_s": 0.16174270794726908, + "app_s": 0.2709272089996375, + "proc_s": 0.5210741669870913, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 2, + "refine_s": 0.16704066604143009, + "app_s": 0.272637958987616, + "proc_s": 0.5345223339973018, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 2, + "refine_s": 0.1620971669908613, + "app_s": 0.27432683401275426, + "proc_s": 0.5236863750033081, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 2, + "refine_s": 0.16440820804564282, + "app_s": 0.2716153750079684, + "proc_s": 0.5200380840105936, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 2, + "refine_s": 0.15889670798787847, + "app_s": 0.2685711670201272, + "proc_s": 0.5264308750047348, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 3, + "refine_s": 0.16489670798182487, + "app_s": 0.2796939170220867, + "proc_s": 0.5295735829859041, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 3, + "refine_s": 0.40192229102831334, + "app_s": 0.5101622079964727, + "proc_s": 0.7578630829812028, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 3, + "refine_s": 0.26282966701546684, + "app_s": 0.3754978340002708, + "proc_s": 0.6294087499845773, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 3, + "refine_s": 0.21869512496050447, + "app_s": 0.3246418749913573, + "proc_s": 0.5755927920108661, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 3, + "refine_s": 0.19615291705122218, + "app_s": 0.3027726669679396, + "proc_s": 0.5484729579766281, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 3, + "refine_s": 0.18437074997927994, + "app_s": 0.2943614999530837, + "proc_s": 0.5403466659481637, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 3, + "refine_s": 0.17235733300913125, + "app_s": 0.27750575001118705, + "proc_s": 0.5332787919905968, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 3, + "refine_s": 0.1651542920153588, + "app_s": 0.26983324997127056, + "proc_s": 0.5156229170388542, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 3, + "refine_s": 0.1620616249856539, + "app_s": 0.2683385830023326, + "proc_s": 0.5160649580066092, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 3, + "refine_s": 0.16624720901018009, + "app_s": 0.27214054099749774, + "proc_s": 0.5230710000032559, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 3, + "refine_s": 0.16259145794901997, + "app_s": 0.2675022080074996, + "proc_s": 0.5143355000182055, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 3, + "refine_s": 0.1686091250157915, + "app_s": 0.27509295800700784, + "proc_s": 0.5206533340387978, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 3, + "refine_s": 0.16599416703684255, + "app_s": 0.27246195898624137, + "proc_s": 0.5185757919680327, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 3, + "refine_s": 0.16805016598664224, + "app_s": 0.27382762503111735, + "proc_s": 0.5208878749981523, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 3, + "refine_s": 0.16629149997606874, + "app_s": 0.27218687505228445, + "proc_s": 0.5216169169871137, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 3, + "refine_s": 0.1652182080433704, + "app_s": 0.2705468749627471, + "proc_s": 0.5184155839961022, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 3, + "refine_s": 0.1646038339822553, + "app_s": 0.2709396669524722, + "proc_s": 0.5179692090023309, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 3, + "refine_s": 0.16785187501227483, + "app_s": 0.2758420419995673, + "proc_s": 0.5229232920100912, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 3, + "refine_s": 0.1650192920351401, + "app_s": 0.271431291999761, + "proc_s": 0.5219185840105638, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 3, + "refine_s": 0.1659135000081733, + "app_s": 0.27873316599288955, + "proc_s": 0.530198999971617, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 3, + "refine_s": 0.1655181249952875, + "app_s": 0.2768457079655491, + "proc_s": 0.5252565830014646, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 4, + "refine_s": 0.16371962503762916, + "app_s": 0.2748225829564035, + "proc_s": 0.5235860840184614, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 4, + "refine_s": 0.4090467500500381, + "app_s": 0.5149518749676645, + "proc_s": 0.7647613330045715, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 4, + "refine_s": 0.26372737501515076, + "app_s": 0.3701905829948373, + "proc_s": 0.616504583042115, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 4, + "refine_s": 0.22402887500356883, + "app_s": 0.3284906670451164, + "proc_s": 0.5745570830185898, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 4, + "refine_s": 0.20072100003017113, + "app_s": 0.3076503330375999, + "proc_s": 0.569496167008765, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 4, + "refine_s": 0.1822617080179043, + "app_s": 0.2893305419711396, + "proc_s": 0.5476015000022016, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 4, + "refine_s": 0.1729861669591628, + "app_s": 0.27938687504502013, + "proc_s": 0.5306391249760054, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 4, + "refine_s": 0.16604570898925886, + "app_s": 0.2725997500238009, + "proc_s": 0.5202285000123084, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 4, + "refine_s": 0.16240820899838582, + "app_s": 0.27343166701029986, + "proc_s": 0.5196180419879965, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 4, + "refine_s": 0.16587866703048348, + "app_s": 0.2725792090059258, + "proc_s": 0.5195914159994572, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 4, + "refine_s": 0.1636313329800032, + "app_s": 0.2707643749890849, + "proc_s": 0.5165189999970607, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 4, + "refine_s": 0.16264354198938236, + "app_s": 0.2670614579692483, + "proc_s": 0.5135328749893233, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 4, + "refine_s": 0.16304950002813712, + "app_s": 0.2689462499693036, + "proc_s": 0.5175859579467215, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 4, + "refine_s": 0.1671176670351997, + "app_s": 0.2750072090420872, + "proc_s": 0.5228784999926575, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 4, + "refine_s": 0.16551958298077807, + "app_s": 0.27372520801145583, + "proc_s": 0.5204962079878896, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 4, + "refine_s": 0.16183837497374043, + "app_s": 0.26686812500702217, + "proc_s": 0.513697249989491, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 4, + "refine_s": 0.1597325830371119, + "app_s": 0.269454916997347, + "proc_s": 0.523795125016477, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 4, + "refine_s": 0.1608245000243187, + "app_s": 0.2707971250056289, + "proc_s": 0.5293533750227652, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 4, + "refine_s": 0.16162029100814834, + "app_s": 0.26835945900529623, + "proc_s": 0.5321605419740081, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 4, + "refine_s": 0.16012954199686646, + "app_s": 0.2771063330001198, + "proc_s": 0.5266959160217084, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 4, + "refine_s": 0.16751870897132903, + "app_s": 0.27946816699113697, + "proc_s": 0.52879487501923, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 0, + "refine_s": 0.18097566696815193, + "app_s": 0.3258591250050813, + "proc_s": 0.5759775830083527, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 0, + "refine_s": 0.4618529579602182, + "app_s": 0.5929079579655081, + "proc_s": 0.8479463339899667, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 0, + "refine_s": 0.30073325004195794, + "app_s": 0.43204579094890505, + "proc_s": 0.6755566669744439, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 0, + "refine_s": 0.25858149997657165, + "app_s": 0.3924262499785982, + "proc_s": 0.6409407919854857, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 0, + "refine_s": 0.22947875002864748, + "app_s": 0.36052525002742186, + "proc_s": 0.6072715829941444, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 0, + "refine_s": 0.20903929200721905, + "app_s": 0.3422634159796871, + "proc_s": 0.5951150410110131, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 0, + "refine_s": 0.20366808300605044, + "app_s": 0.3361521670012735, + "proc_s": 0.5833509169751778, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 0, + "refine_s": 0.18748983304249123, + "app_s": 0.32292804098688066, + "proc_s": 0.5693749589845538, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 0, + "refine_s": 0.18655458302237093, + "app_s": 0.3184415830182843, + "proc_s": 0.56651391700143, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 0, + "refine_s": 0.18745312502142042, + "app_s": 0.32121049996931106, + "proc_s": 0.5707192499539815, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 0, + "refine_s": 0.18498112499946728, + "app_s": 0.3194097090163268, + "proc_s": 0.5704214159632102, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 0, + "refine_s": 0.18545095797162503, + "app_s": 0.3183995000435971, + "proc_s": 0.5670825829729438, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 0, + "refine_s": 0.18177475000265986, + "app_s": 0.31402979197446257, + "proc_s": 0.5598560830112547, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 0, + "refine_s": 0.18552787497173995, + "app_s": 0.317411791998893, + "proc_s": 0.5633137500262819, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 0, + "refine_s": 0.18679345800774172, + "app_s": 0.3192858749534935, + "proc_s": 0.5658997499849647, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 0, + "refine_s": 0.1873843749635853, + "app_s": 0.3221769580268301, + "proc_s": 0.5756653330172412, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 0, + "refine_s": 0.1881004169699736, + "app_s": 0.3217714999918826, + "proc_s": 0.572671709000133, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 0, + "refine_s": 0.18671874998835847, + "app_s": 0.3205857089487836, + "proc_s": 0.5674413749948144, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 0, + "refine_s": 0.18933625001227483, + "app_s": 0.32080479100113735, + "proc_s": 0.5687164579867385, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 0, + "refine_s": 0.18704562494531274, + "app_s": 0.32122762501239777, + "proc_s": 0.5690365419723094, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 0, + "refine_s": 0.18808258295757696, + "app_s": 0.3228696250007488, + "proc_s": 0.5722224579658359, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 1, + "refine_s": 0.19125641701975837, + "app_s": 0.3243794580339454, + "proc_s": 0.574231915990822, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 1, + "refine_s": 0.45592629100428894, + "app_s": 0.587808208016213, + "proc_s": 0.8406786660198122, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 1, + "refine_s": 0.30273295898223296, + "app_s": 0.4367849170230329, + "proc_s": 0.6822837920044549, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 1, + "refine_s": 0.2472820829716511, + "app_s": 0.38350524997804314, + "proc_s": 0.6294433340081014, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 1, + "refine_s": 0.22491983301006258, + "app_s": 0.35688441700767726, + "proc_s": 0.6067288750200532, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 1, + "refine_s": 0.21711249998770654, + "app_s": 0.35031895799329504, + "proc_s": 0.6036875830031931, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 1, + "refine_s": 0.20094104198506102, + "app_s": 0.33581841696286574, + "proc_s": 0.5842414170037955, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 1, + "refine_s": 0.1887129579554312, + "app_s": 0.32699550001416355, + "proc_s": 0.5751264579594135, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 1, + "refine_s": 0.18328479101182893, + "app_s": 0.3169229170307517, + "proc_s": 0.5666655409731902, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 1, + "refine_s": 0.1870493749738671, + "app_s": 0.32169862498994917, + "proc_s": 0.5730421670014039, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 1, + "refine_s": 0.18466108298161998, + "app_s": 0.3205665829591453, + "proc_s": 0.5688497920054942, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 1, + "refine_s": 0.18019024998648092, + "app_s": 0.31250524998176843, + "proc_s": 0.5640920419828035, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 1, + "refine_s": 0.18568774999585003, + "app_s": 0.31926004198612645, + "proc_s": 0.5656551250140183, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 1, + "refine_s": 0.18660620803711936, + "app_s": 0.3192503750324249, + "proc_s": 0.565355124999769, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 1, + "refine_s": 0.18795158399734646, + "app_s": 0.32200666703283787, + "proc_s": 0.5695021249703132, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 1, + "refine_s": 0.18804345797980204, + "app_s": 0.32110091601498425, + "proc_s": 0.5704796250211075, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 1, + "refine_s": 0.33633199997711927, + "app_s": 0.5656230419990607, + "proc_s": 0.9152295000385493, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 1, + "refine_s": 0.18630029197083786, + "app_s": 0.31978995801182464, + "proc_s": 0.5689224589732476, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 1, + "refine_s": 0.18190408300142735, + "app_s": 0.31367741600843146, + "proc_s": 0.5605150829651393, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 1, + "refine_s": 0.1811799579882063, + "app_s": 0.31179283396340907, + "proc_s": 0.5574436669703573, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 1, + "refine_s": 0.1868055000086315, + "app_s": 0.31953745801001787, + "proc_s": 0.566879499994684, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 2, + "refine_s": 0.18030999996699393, + "app_s": 0.31436120899161324, + "proc_s": 0.562882833997719, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 2, + "refine_s": 0.4735108750173822, + "app_s": 0.6101167919696309, + "proc_s": 0.8561810000101104, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 2, + "refine_s": 0.30870408297050744, + "app_s": 0.4406961250351742, + "proc_s": 0.6963259170297533, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 2, + "refine_s": 0.2484023330034688, + "app_s": 0.3815416669822298, + "proc_s": 0.6299443329917267, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 2, + "refine_s": 0.22876645799260587, + "app_s": 0.3655477919965051, + "proc_s": 0.6170215000165626, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 2, + "refine_s": 0.20969791698735207, + "app_s": 0.3433292089612223, + "proc_s": 0.5941263329586945, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 2, + "refine_s": 0.19978162500774488, + "app_s": 0.3388825000147335, + "proc_s": 0.5948432089644484, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 2, + "refine_s": 0.19383133301744238, + "app_s": 0.33890566701302305, + "proc_s": 0.5897257500328124, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 2, + "refine_s": 0.1830007079988718, + "app_s": 0.31837212498066947, + "proc_s": 0.5659929999965243, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 2, + "refine_s": 0.18647199997212738, + "app_s": 0.33694954204838723, + "proc_s": 0.5877417500014417, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 2, + "refine_s": 0.18459470802918077, + "app_s": 0.319392874953337, + "proc_s": 0.5704410420148633, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 2, + "refine_s": 0.18205666699213907, + "app_s": 0.3181829589884728, + "proc_s": 0.5725481659756042, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 2, + "refine_s": 0.18571654200786725, + "app_s": 0.32188274996588007, + "proc_s": 0.5694306659861468, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 2, + "refine_s": 0.1805701669654809, + "app_s": 0.3155025420128368, + "proc_s": 0.5712722910102457, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 2, + "refine_s": 0.1892547500319779, + "app_s": 0.31957412499468774, + "proc_s": 0.5660705409827642, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 2, + "refine_s": 0.1900379169965163, + "app_s": 0.32296158297685906, + "proc_s": 0.5719765829853714, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 2, + "refine_s": 0.20131033298093826, + "app_s": 0.3553390830056742, + "proc_s": 0.708964042016305, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 2, + "refine_s": 0.18784162495285273, + "app_s": 0.3262666660011746, + "proc_s": 0.5847746249637567, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 2, + "refine_s": 0.18873158300993964, + "app_s": 0.3206290839589201, + "proc_s": 0.5683471249649301, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 2, + "refine_s": 0.18761712504783645, + "app_s": 0.32361333403969184, + "proc_s": 0.5710223329951987, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 2, + "refine_s": 0.1838492089882493, + "app_s": 0.31540158396819606, + "proc_s": 0.567744166997727, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 3, + "refine_s": 0.1860047910013236, + "app_s": 0.3198946669581346, + "proc_s": 0.5703106670407578, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 3, + "refine_s": 0.46410699997795746, + "app_s": 0.5952089169877581, + "proc_s": 0.8419179160264321, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 3, + "refine_s": 0.3023304169764742, + "app_s": 0.4349810000276193, + "proc_s": 0.6937016249867156, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 3, + "refine_s": 0.24960599996848032, + "app_s": 0.3878149589872919, + "proc_s": 0.6411925420397893, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 3, + "refine_s": 0.23267691698856652, + "app_s": 0.3639243749785237, + "proc_s": 0.6204404580057599, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 3, + "refine_s": 0.21216787496814504, + "app_s": 0.3440249169943854, + "proc_s": 0.5915166669874452, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 3, + "refine_s": 0.20413549995282665, + "app_s": 0.33469316601986066, + "proc_s": 0.5832572910003364, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 3, + "refine_s": 0.1892468750011176, + "app_s": 0.3248203750117682, + "proc_s": 0.5724006249802187, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 3, + "refine_s": 0.18457562499679625, + "app_s": 0.32902533299056813, + "proc_s": 0.5778546669753268, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 3, + "refine_s": 0.1917680840124376, + "app_s": 0.3237007090356201, + "proc_s": 0.5727768750512041, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 3, + "refine_s": 0.18691795802442357, + "app_s": 0.32020950003061444, + "proc_s": 0.567754166026134, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 3, + "refine_s": 0.18620808399282396, + "app_s": 0.3202697079977952, + "proc_s": 0.5731857909704559, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 3, + "refine_s": 0.18268654204439372, + "app_s": 0.31352104200050235, + "proc_s": 0.5613139169872738, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 3, + "refine_s": 0.18737216701265424, + "app_s": 0.3187845830107108, + "proc_s": 0.5741891670040786, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 3, + "refine_s": 0.18649208400165662, + "app_s": 0.321439791994635, + "proc_s": 0.5693484579678625, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 3, + "refine_s": 0.18410762498388067, + "app_s": 0.31772012502187863, + "proc_s": 0.5656339999986812, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 3, + "refine_s": 0.18719087500358, + "app_s": 0.3226075840066187, + "proc_s": 0.5700976249645464, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 3, + "refine_s": 0.1860848749638535, + "app_s": 0.3214949579560198, + "proc_s": 0.5687226670561358, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 3, + "refine_s": 0.1878297919756733, + "app_s": 0.32308170897886157, + "proc_s": 0.5753001249977387, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 3, + "refine_s": 0.18552062497474253, + "app_s": 0.3191166250035167, + "proc_s": 0.5715214579831809, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 3, + "refine_s": 0.19549583399202675, + "app_s": 0.33380533300805837, + "proc_s": 0.5817849999875762, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 4, + "refine_s": 0.18690237501868978, + "app_s": 0.3238335830392316, + "proc_s": 0.5712085830164142, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 4, + "refine_s": 0.46586704201763496, + "app_s": 0.6020363750285469, + "proc_s": 0.8502733329660259, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 4, + "refine_s": 0.30357729201205075, + "app_s": 0.4398701249738224, + "proc_s": 0.6887697500060312, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 4, + "refine_s": 0.24901158298598602, + "app_s": 0.3869269579881802, + "proc_s": 0.6378255419549532, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 4, + "refine_s": 0.22804254101356491, + "app_s": 0.3687878340133466, + "proc_s": 0.624142375017982, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 4, + "refine_s": 0.21742533397627994, + "app_s": 0.35111766698537394, + "proc_s": 0.6011825420428067, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 4, + "refine_s": 0.20100212504621595, + "app_s": 0.33953620900865644, + "proc_s": 0.5862372919800691, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 4, + "refine_s": 0.19147537497337908, + "app_s": 0.32262237498071045, + "proc_s": 0.5724824590142816, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 4, + "refine_s": 0.19217020802898332, + "app_s": 0.33902508299797773, + "proc_s": 0.5914926250115968, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 4, + "refine_s": 0.18633879197295755, + "app_s": 0.32051795901497826, + "proc_s": 0.5800697919912636, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 4, + "refine_s": 0.18274758296320215, + "app_s": 0.31700041698059067, + "proc_s": 0.5632800000021234, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 4, + "refine_s": 0.18059766601072624, + "app_s": 0.3136074159992859, + "proc_s": 0.5600309580331668, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 4, + "refine_s": 0.18859929096652195, + "app_s": 0.32154358399566263, + "proc_s": 0.5694490410387516, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 4, + "refine_s": 0.18584395799553022, + "app_s": 0.32410379097564146, + "proc_s": 0.5749052090104669, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 4, + "refine_s": 0.18467416701605543, + "app_s": 0.32378091604914516, + "proc_s": 0.584585957985837, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 4, + "refine_s": 0.18411999999079853, + "app_s": 0.31777083297492936, + "proc_s": 0.5710235420265235, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 4, + "refine_s": 0.18078137503471226, + "app_s": 0.3141241249977611, + "proc_s": 0.5660171250347048, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 4, + "refine_s": 0.19222591602010652, + "app_s": 0.32618370797717944, + "proc_s": 0.5764665830065496, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 4, + "refine_s": 0.18582966696703807, + "app_s": 0.31527966703288257, + "proc_s": 0.5620770830428228, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 4, + "refine_s": 0.1836614590138197, + "app_s": 0.31740629201522097, + "proc_s": 0.5662184589891694, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 4, + "refine_s": 0.1845967079862021, + "app_s": 0.3153867920045741, + "proc_s": 0.5615939580020495, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + } + ], + "stats": [ + { + "domain": "quarter", + "threads": 0, + "n": 5, + "median": 0.16483875003177673, + "min": 0.1575865829945542, + "max": 0.1664806249900721 + }, + { + "domain": "quarter", + "threads": 1, + "n": 5, + "median": 0.4076976249925792, + "min": 0.40192229102831334, + "max": 0.4090467500500381 + }, + { + "domain": "quarter", + "threads": 2, + "n": 5, + "median": 0.26372737501515076, + "min": 0.2618640000000596, + "max": 0.2707637079874985 + }, + { + "domain": "quarter", + "threads": 3, + "n": 5, + "median": 0.21956925000995398, + "min": 0.2168518750113435, + "max": 0.22402887500356883 + }, + { + "domain": "quarter", + "threads": 4, + "n": 5, + "median": 0.19868716702330858, + "min": 0.19615291705122218, + "max": 0.20228541700635105 + }, + { + "domain": "quarter", + "threads": 5, + "n": 5, + "median": 0.1822617080179043, + "min": 0.18125120899640024, + "max": 0.18437074997927994 + }, + { + "domain": "quarter", + "threads": 6, + "n": 5, + "median": 0.1729861669591628, + "min": 0.17235658399295062, + "max": 0.17738029203610495 + }, + { + "domain": "quarter", + "threads": 7, + "n": 5, + "median": 0.16541541699552909, + "min": 0.16485262499190867, + "max": 0.16604570898925886 + }, + { + "domain": "quarter", + "threads": 8, + "n": 5, + "median": 0.16155074996640906, + "min": 0.1603774999966845, + "max": 0.16240820899838582 + }, + { + "domain": "quarter", + "threads": 9, + "n": 5, + "median": 0.1657028750050813, + "min": 0.1589934160001576, + "max": 0.16624720901018009 + }, + { + "domain": "quarter", + "threads": 10, + "n": 5, + "median": 0.1636313329800032, + "min": 0.1603675420046784, + "max": 0.17322870896896347 + }, + { + "domain": "quarter", + "threads": 11, + "n": 5, + "median": 0.16427899996051565, + "min": 0.1601576249813661, + "max": 0.1686091250157915 + }, + { + "domain": "quarter", + "threads": 12, + "n": 5, + "median": 0.16479483299190179, + "min": 0.15921091701602563, + "max": 0.16599416703684255 + }, + { + "domain": "quarter", + "threads": 13, + "n": 5, + "median": 0.1645944170304574, + "min": 0.1621871660463512, + "max": 0.16805016598664224 + }, + { + "domain": "quarter", + "threads": 14, + "n": 5, + "median": 0.16208654205547646, + "min": 0.15795433399034664, + "max": 0.16629149997606874 + }, + { + "domain": "quarter", + "threads": 15, + "n": 5, + "median": 0.16183837497374043, + "min": 0.15789279097225517, + "max": 0.16557962499791756 + }, + { + "domain": "quarter", + "threads": 16, + "n": 5, + "median": 0.16174270794726908, + "min": 0.15910387499025092, + "max": 0.1646038339822553 + }, + { + "domain": "quarter", + "threads": 17, + "n": 5, + "median": 0.16704066604143009, + "min": 0.1596961670438759, + "max": 0.16785187501227483 + }, + { + "domain": "quarter", + "threads": 18, + "n": 5, + "median": 0.16162029100814834, + "min": 0.15880020800977945, + "max": 0.1650192920351401 + }, + { + "domain": "quarter", + "threads": 19, + "n": 5, + "median": 0.16440820804564282, + "min": 0.15974475000984967, + "max": 0.1680661659920588 + }, + { + "domain": "quarter", + "threads": 20, + "n": 5, + "median": 0.1655181249952875, + "min": 0.15889670798787847, + "max": 0.16751870897132903 + }, + { + "domain": "tile", + "threads": 0, + "n": 5, + "median": 0.1860047910013236, + "min": 0.18030999996699393, + "max": 0.19125641701975837 + }, + { + "domain": "tile", + "threads": 1, + "n": 5, + "median": 0.46410699997795746, + "min": 0.45592629100428894, + "max": 0.4735108750173822 + }, + { + "domain": "tile", + "threads": 2, + "n": 5, + "median": 0.30273295898223296, + "min": 0.30073325004195794, + "max": 0.30870408297050744 + }, + { + "domain": "tile", + "threads": 3, + "n": 5, + "median": 0.24901158298598602, + "min": 0.2472820829716511, + "max": 0.25858149997657165 + }, + { + "domain": "tile", + "threads": 4, + "n": 5, + "median": 0.22876645799260587, + "min": 0.22491983301006258, + "max": 0.23267691698856652 + }, + { + "domain": "tile", + "threads": 5, + "n": 5, + "median": 0.21216787496814504, + "min": 0.20903929200721905, + "max": 0.21742533397627994 + }, + { + "domain": "tile", + "threads": 6, + "n": 5, + "median": 0.20100212504621595, + "min": 0.19978162500774488, + "max": 0.20413549995282665 + }, + { + "domain": "tile", + "threads": 7, + "n": 5, + "median": 0.1892468750011176, + "min": 0.18748983304249123, + "max": 0.19383133301744238 + }, + { + "domain": "tile", + "threads": 8, + "n": 5, + "median": 0.18457562499679625, + "min": 0.1830007079988718, + "max": 0.19217020802898332 + }, + { + "domain": "tile", + "threads": 9, + "n": 5, + "median": 0.1870493749738671, + "min": 0.18633879197295755, + "max": 0.1917680840124376 + }, + { + "domain": "tile", + "threads": 10, + "n": 5, + "median": 0.18466108298161998, + "min": 0.18274758296320215, + "max": 0.18691795802442357 + }, + { + "domain": "tile", + "threads": 11, + "n": 5, + "median": 0.18205666699213907, + "min": 0.18019024998648092, + "max": 0.18620808399282396 + }, + { + "domain": "tile", + "threads": 12, + "n": 5, + "median": 0.18568774999585003, + "min": 0.18177475000265986, + "max": 0.18859929096652195 + }, + { + "domain": "tile", + "threads": 13, + "n": 5, + "median": 0.18584395799553022, + "min": 0.1805701669654809, + "max": 0.18737216701265424 + }, + { + "domain": "tile", + "threads": 14, + "n": 5, + "median": 0.18679345800774172, + "min": 0.18467416701605543, + "max": 0.1892547500319779 + }, + { + "domain": "tile", + "threads": 15, + "n": 5, + "median": 0.1873843749635853, + "min": 0.18410762498388067, + "max": 0.1900379169965163 + }, + { + "domain": "tile", + "threads": 16, + "n": 5, + "median": 0.1881004169699736, + "min": 0.18078137503471226, + "max": 0.33633199997711927 + }, + { + "domain": "tile", + "threads": 17, + "n": 5, + "median": 0.18671874998835847, + "min": 0.1860848749638535, + "max": 0.19222591602010652 + }, + { + "domain": "tile", + "threads": 18, + "n": 5, + "median": 0.1878297919756733, + "min": 0.18190408300142735, + "max": 0.18933625001227483 + }, + { + "domain": "tile", + "threads": 19, + "n": 5, + "median": 0.18552062497474253, + "min": 0.1811799579882063, + "max": 0.18761712504783645 + }, + { + "domain": "tile", + "threads": 20, + "n": 5, + "median": 0.1868055000086315, + "min": 0.1838492089882493, + "max": 0.19549583399202675 + } + ], + "quality": { + "quarter": { + "worst_angle": 0.3955267116350698, + "angle_median": 45.0, + "share_under_1": 0.000028023175165862168, + "max_degree": 18, + "within_tolerance": true, + "delaunay_checked": 641791, + "delaunay_ambiguous": 80136, + "delaunay_violations": 0, + "mesh_sha256": "ccebf96a86c6c5e244e4a0281919de4e866fcfe789b66024a290ac2af33771a1" + }, + "tile": { + "worst_angle": 0.6295986084102668, + "angle_median": 45.0, + "share_under_1": 2.1538262723728703e-6, + "max_degree": 74, + "within_tolerance": true, + "delaunay_checked": 692056, + "delaunay_ambiguous": 94469, + "delaunay_violations": 0, + "mesh_sha256": "11741a81adfa17b34a1ea56875ec9e791414d17625eb1c58305e7c0cf10e005c" + } + }, + "accept_quality": false, + "threshold_pct": 5.0, + "verdict": [ + "ACCEPTED" + ] +} diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/16b0/README.md b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0/README.md new file mode 100644 index 00000000..78110ec6 --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0/README.md @@ -0,0 +1,79 @@ +# Benchmark run `16b0-acceptance/16b0` + +Generated by `tools/bench.py`. + +## Verdict + +- ACCEPTED + +Time threshold: 5.0 %. + +## Method + +- Started 2026-09-28T15:23:54.569302+02:00; tree `4e833893377430683ac1dce967783ead5ff9a2de` (dirty); bench.py blob `77765b181714c31714da476c2335c799c6a84021`. Build: Release, AppleClang 21.0.0.21000101, `-O3 -DNDEBUG`, _core sha256 `190587e7ba0ea96a74a8621767d7d20257c3968126a710db1364e1195afb27d2`. +- Apple M1 Max, 8 P + 2 E cores, 32 GiB, macOS 27.0, Python 3.14.7, numpy 2.5.3. +- Power **battery** (74%), `pmset -g batt` before and after (in run.json). +- DEM `/Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif` (sha256 `aabd0cbc28471ce8593e4c278381811bec3fbf4e1194058b3b4888d00c3af575`), tolerance 1.0, extra mesh args ``; domains: `quarter` (/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-26/quarter.geojson), `tile` (the whole tile). +- One child per sample, `/Users/skavhaug/projects/rasputin/.venv/bin/python /Users/skavhaug/projects/rasputin/tools/bench.py _child --pkg /Users/skavhaug/projects/rasputin/build-bench/pkg --threads -- mesh --dem /Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif --tolerance 1 --out --binary`, repeats interleaved over thread counts; t=0 is the CLI's default, other counts are forced into `refine`. Quality: one `--ascii` run per domain at t=0, kept out of the repository; rerun the child with `--ascii --out PATH` to regenerate it. + +## `quarter` + +| threads | n | median s | min s | max s | +|---:|---:|---:|---:|---:| +| 0 | 5 | 0.1671 | 0.1632 | 0.1756 | +| 1 | 5 | 0.4539 | 0.4204 | 0.4752 | +| 2 | 5 | 0.2775 | 0.2697 | 0.3016 | +| 3 | 5 | 0.2250 | 0.2211 | 0.2276 | +| 4 | 5 | 0.2019 | 0.1996 | 0.2054 | +| 5 | 5 | 0.1859 | 0.1838 | 0.1895 | +| 6 | 5 | 0.1768 | 0.1752 | 0.1876 | +| 7 | 5 | 0.1710 | 0.1681 | 0.2086 | +| 8 | 5 | 0.1708 | 0.1637 | 0.1786 | +| 9 | 5 | 0.1642 | 0.1632 | 0.1828 | +| 10 | 5 | 0.1652 | 0.1630 | 0.1688 | +| 11 | 5 | 0.1639 | 0.1630 | 0.1684 | +| 12 | 5 | 0.1637 | 0.1616 | 0.1679 | +| 13 | 5 | 0.1677 | 0.1622 | 0.1745 | +| 14 | 5 | 0.1639 | 0.1628 | 0.1696 | +| 15 | 5 | 0.1636 | 0.1618 | 0.1687 | +| 16 | 5 | 0.1655 | 0.1645 | 0.1735 | +| 17 | 5 | 0.1642 | 0.1638 | 0.1738 | +| 18 | 5 | 0.1689 | 0.1672 | 0.1784 | +| 19 | 5 | 0.1647 | 0.1637 | 0.1718 | +| 20 | 5 | 0.1657 | 0.1635 | 0.1803 | + +Ceiling: 2.74x at 20 threads over 1, best 2.77x at 15; 2026-09-26: about 2.2x, flat from about 7. + +Quality: worst angle 0.3955 deg, median 45.00, share under 1 deg 0.00003, max degree 18, within tolerance True, Delaunay 0 violations of 641791 edges (80136 decided exactly), mesh sha256 `ccebf96a86c6c5e244e4a0281919de4e866fcfe789b66024a290ac2af33771a1`. + +## `tile` + +| threads | n | median s | min s | max s | +|---:|---:|---:|---:|---:| +| 0 | 5 | 0.1922 | 0.1814 | 0.2239 | +| 1 | 5 | 0.4809 | 0.4670 | 0.4932 | +| 2 | 5 | 0.3136 | 0.3101 | 0.3242 | +| 3 | 5 | 0.2540 | 0.2525 | 0.2662 | +| 4 | 5 | 0.2342 | 0.2271 | 0.2498 | +| 5 | 5 | 0.2109 | 0.2089 | 0.2231 | +| 6 | 5 | 0.1993 | 0.1969 | 0.2112 | +| 7 | 5 | 0.1946 | 0.1927 | 0.1988 | +| 8 | 5 | 0.1844 | 0.1829 | 0.1925 | +| 9 | 5 | 0.1861 | 0.1840 | 0.2246 | +| 10 | 5 | 0.1877 | 0.1852 | 0.2152 | +| 11 | 5 | 0.1849 | 0.1815 | 0.2075 | +| 12 | 5 | 0.1872 | 0.1810 | 0.1924 | +| 13 | 5 | 0.1894 | 0.1831 | 0.1948 | +| 14 | 5 | 0.1924 | 0.1843 | 0.2189 | +| 15 | 5 | 0.1860 | 0.1834 | 0.1931 | +| 16 | 5 | 0.1872 | 0.1853 | 0.1953 | +| 17 | 5 | 0.1945 | 0.1857 | 0.2281 | +| 18 | 5 | 0.1869 | 0.1817 | 0.2101 | +| 19 | 5 | 0.1862 | 0.1830 | 0.2105 | +| 20 | 5 | 0.1958 | 0.1853 | 0.2227 | + +Ceiling: 2.46x at 20 threads over 1, best 2.61x at 8; 2026-09-26: about 2.2x, flat from about 7. + +Quality: worst angle 0.6296 deg, median 45.00, share under 1 deg 0.00000, max degree 74, within tolerance True, Delaunay 0 violations of 692056 edges (94469 decided exactly), mesh sha256 `11741a81adfa17b34a1ea56875ec9e791414d17625eb1c58305e7c0cf10e005c`. + + diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/16b0/raw.tsv b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0/raw.tsv new file mode 100644 index 00000000..e3c3aee4 --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0/raw.tsv @@ -0,0 +1,210 @@ +quarter 0 0 0.175566 battery +quarter 1 0 0.453930 battery +quarter 2 0 0.280509 battery +quarter 3 0 0.221061 battery +quarter 4 0 0.200027 battery +quarter 5 0 0.189014 battery +quarter 6 0 0.176802 battery +quarter 7 0 0.168114 battery +quarter 8 0 0.170786 battery +quarter 9 0 0.163181 battery +quarter 10 0 0.165813 battery +quarter 11 0 0.168403 battery +quarter 12 0 0.163672 battery +quarter 13 0 0.167723 battery +quarter 14 0 0.163114 battery +quarter 15 0 0.163587 battery +quarter 16 0 0.164454 battery +quarter 17 0 0.164159 battery +quarter 18 0 0.178430 battery +quarter 19 0 0.164704 battery +quarter 20 0 0.180269 battery +quarter 0 1 0.163205 battery +quarter 1 1 0.454671 battery +quarter 2 1 0.301613 battery +quarter 3 1 0.227577 battery +quarter 4 1 0.201863 battery +quarter 5 1 0.185890 battery +quarter 6 1 0.187567 battery +quarter 7 1 0.208580 battery +quarter 8 1 0.178605 battery +quarter 9 1 0.182780 battery +quarter 10 1 0.168805 battery +quarter 11 1 0.164186 battery +quarter 12 1 0.163670 battery +quarter 13 1 0.169843 battery +quarter 14 1 0.169305 battery +quarter 15 1 0.168720 battery +quarter 16 1 0.173463 battery +quarter 17 1 0.166878 battery +quarter 18 1 0.168270 battery +quarter 19 1 0.165788 battery +quarter 20 1 0.165298 battery +quarter 0 2 0.165217 battery +quarter 1 2 0.475227 battery +quarter 2 2 0.277515 battery +quarter 3 2 0.226306 battery +quarter 4 2 0.202968 battery +quarter 5 2 0.189450 battery +quarter 6 2 0.179506 battery +quarter 7 2 0.168816 battery +quarter 8 2 0.171381 battery +quarter 9 2 0.169109 battery +quarter 10 2 0.165169 battery +quarter 11 2 0.162956 battery +quarter 12 2 0.163236 battery +quarter 13 2 0.174516 battery +quarter 14 2 0.162768 battery +quarter 15 2 0.163084 battery +quarter 16 2 0.165016 battery +quarter 17 2 0.163815 battery +quarter 18 2 0.167153 battery +quarter 19 2 0.163665 battery +quarter 20 2 0.166734 battery +quarter 0 3 0.167177 battery +quarter 1 3 0.420424 battery +quarter 2 3 0.274252 battery +quarter 3 3 0.224956 battery +quarter 4 3 0.199602 battery +quarter 5 3 0.183789 battery +quarter 6 3 0.175191 battery +quarter 7 3 0.172156 battery +quarter 8 3 0.163738 battery +quarter 9 3 0.164162 battery +quarter 10 3 0.162997 battery +quarter 11 3 0.163950 battery +quarter 12 3 0.161585 battery +quarter 13 3 0.162178 battery +quarter 14 3 0.163901 battery +quarter 15 3 0.161819 battery +quarter 16 3 0.166441 battery +quarter 17 3 0.173811 battery +quarter 18 3 0.169855 battery +quarter 19 3 0.171821 battery +quarter 20 3 0.165746 battery +quarter 0 4 0.167143 battery +quarter 1 4 0.433319 battery +quarter 2 4 0.269702 battery +quarter 3 4 0.224660 battery +quarter 4 4 0.205373 battery +quarter 5 4 0.184595 battery +quarter 6 4 0.175549 battery +quarter 7 4 0.171008 battery +quarter 8 4 0.167069 battery +quarter 9 4 0.163998 battery +quarter 10 4 0.163831 battery +quarter 11 4 0.163584 battery +quarter 12 4 0.167869 battery +quarter 13 4 0.167746 battery +quarter 14 4 0.169594 battery +quarter 15 4 0.166467 battery +quarter 16 4 0.165452 battery +quarter 17 4 0.163776 battery +quarter 18 4 0.168949 battery +quarter 19 4 0.163790 battery +quarter 20 4 0.163496 battery +tile 0 0 0.192183 battery +tile 1 0 0.493202 battery +tile 2 0 0.320959 battery +tile 3 0 0.266243 battery +tile 4 0 0.249760 battery +tile 5 0 0.220689 battery +tile 6 0 0.205233 battery +tile 7 0 0.198789 battery +tile 8 0 0.191310 battery +tile 9 0 0.186106 battery +tile 10 0 0.187657 battery +tile 11 0 0.207511 battery +tile 12 0 0.187169 battery +tile 13 0 0.189365 battery +tile 14 0 0.193337 battery +tile 15 0 0.193148 battery +tile 16 0 0.185301 battery +tile 17 0 0.185706 battery +tile 18 0 0.181664 battery +tile 19 0 0.182962 battery +tile 20 0 0.195802 battery +tile 0 1 0.223860 battery +tile 1 1 0.492838 battery +tile 2 1 0.324168 battery +tile 3 1 0.253771 battery +tile 4 1 0.234180 battery +tile 5 1 0.210867 battery +tile 6 1 0.199186 battery +tile 7 1 0.194619 battery +tile 8 1 0.184366 battery +tile 9 1 0.183997 battery +tile 10 1 0.185191 battery +tile 11 1 0.184591 battery +tile 12 1 0.192388 battery +tile 13 1 0.188130 battery +tile 14 1 0.218907 battery +tile 15 1 0.185427 battery +tile 16 1 0.187223 battery +tile 17 1 0.228107 battery +tile 18 1 0.193267 battery +tile 19 1 0.185965 battery +tile 20 1 0.187776 battery +tile 0 2 0.187591 battery +tile 1 2 0.467009 battery +tile 2 2 0.310134 battery +tile 3 2 0.252502 battery +tile 4 2 0.227144 battery +tile 5 2 0.210419 battery +tile 6 2 0.196896 battery +tile 7 2 0.192672 battery +tile 8 2 0.182906 battery +tile 9 2 0.216702 battery +tile 10 2 0.194575 battery +tile 11 2 0.184894 battery +tile 12 2 0.181004 battery +tile 13 2 0.194845 battery +tile 14 2 0.188570 battery +tile 15 2 0.183405 battery +tile 16 2 0.185300 battery +tile 17 2 0.197346 battery +tile 18 2 0.210148 battery +tile 19 2 0.186239 battery +tile 20 2 0.222750 battery +tile 0 3 0.181425 battery +tile 1 3 0.480935 battery +tile 2 3 0.313593 battery +tile 3 3 0.257971 battery +tile 4 3 0.233870 battery +tile 5 3 0.223124 battery +tile 6 3 0.211232 battery +tile 7 3 0.195815 battery +tile 8 3 0.192501 battery +tile 9 3 0.185738 battery +tile 10 3 0.186156 battery +tile 11 3 0.181456 battery +tile 12 3 0.186354 battery +tile 13 3 0.183068 battery +tile 14 3 0.184277 battery +tile 15 3 0.185959 battery +tile 16 3 0.191059 battery +tile 17 3 0.194454 battery +tile 18 3 0.186388 battery +tile 19 3 0.188255 battery +tile 20 3 0.185337 battery +tile 0 4 0.196971 battery +tile 1 4 0.468405 battery +tile 2 4 0.312397 battery +tile 3 4 0.253955 battery +tile 4 4 0.237196 battery +tile 5 4 0.208875 battery +tile 6 4 0.199331 battery +tile 7 4 0.192676 battery +tile 8 4 0.184280 battery +tile 9 4 0.224618 battery +tile 10 4 0.215193 battery +tile 11 4 0.191398 battery +tile 12 4 0.190491 battery +tile 13 4 0.190940 battery +tile 14 4 0.192413 battery +tile 15 4 0.189777 battery +tile 16 4 0.195261 battery +tile 17 4 0.185715 battery +tile 18 4 0.186861 battery +tile 19 4 0.210506 battery +tile 20 4 0.197134 battery diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/16b0/run.json b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0/run.json new file mode 100644 index 00000000..51617dd1 --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/16b0/run.json @@ -0,0 +1,2955 @@ +{ + "label": "16b0-acceptance/16b0", + "started": "2026-09-28T15:23:54.569302+02:00", + "tree": { + "commit": "4e833893377430683ac1dce967783ead5ff9a2de", + "dirty": true + }, + "bench_blob": "77765b181714c31714da476c2335c799c6a84021", + "child_argv": [ + "/Users/skavhaug/projects/rasputin/.venv/bin/python", + "/Users/skavhaug/projects/rasputin/tools/bench.py", + "_child", + "--pkg", + "/Users/skavhaug/projects/rasputin/build-bench/pkg", + "--threads", + "", + "--", + "mesh", + "--dem", + "/Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif", + "--tolerance", + "1", + "--out", + "", + "--binary" + ], + "build": { + "no_build": false, + "type": "Release", + "cxx_flags_release": "-O3 -DNDEBUG", + "compiler": "AppleClang 21.0.0.21000101", + "so_sha256": "190587e7ba0ea96a74a8621767d7d20257c3968126a710db1364e1195afb27d2" + }, + "machine": { + "cpu_brand": "Apple M1 Max", + "p_cores": 8, + "e_cores": 2, + "memory_bytes": 34359738368, + "macos": "27.0", + "python": "3.14.7", + "numpy": "2.5.3" + }, + "power": { + "state": "battery", + "percent": 74, + "raw": "Now drawing from 'Battery Power'\n -InternalBattery-0 (id=7929955)\t75%; discharging; 3:35 remaining present: true\n--- after ---\nNow drawing from 'Battery Power'\n -InternalBattery-0 (id=7929955)\t74%; discharging; 3:05 remaining present: true\n" + }, + "inputs": { + "dem": "/Users/skavhaug/projects/rasputin/tests/fixtures/dem_archive/7908_3_10m_z33.tif", + "dem_sha256": "aabd0cbc28471ce8593e4c278381811bec3fbf4e1194058b3b4888d00c3af575", + "domains": [ + { + "name": "quarter", + "path": "/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-26/quarter.geojson", + "sha256": "b7f7bbe96848383c77863ce29ee28c45bce087601a1738ee82ddb4ffb8279d1e" + }, + { + "name": "tile", + "path": null, + "sha256": null + } + ], + "tolerance": 1.0, + "extra_args": [] + }, + "samples": [ + { + "domain": "quarter", + "threads": 0, + "repeat": 0, + "refine_s": 0.1755662080249749, + "app_s": 0.3698303330456838, + "proc_s": 1.031858500035014, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 0, + "refine_s": 0.45392958301818, + "app_s": 0.5964225829811767, + "proc_s": 0.8801177919958718, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 0, + "refine_s": 0.28050891699967906, + "app_s": 0.39515325002139434, + "proc_s": 0.652341625012923, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 0, + "refine_s": 0.2210613749921322, + "app_s": 0.3395836660056375, + "proc_s": 0.6006140829995275, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 0, + "refine_s": 0.20002737501636147, + "app_s": 0.31301120802527294, + "proc_s": 0.5784688749699853, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 0, + "refine_s": 0.18901395902503282, + "app_s": 0.3011606250074692, + "proc_s": 0.5564286670414731, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 0, + "refine_s": 0.17680195899447426, + "app_s": 0.28914458298822865, + "proc_s": 0.5568516670027748, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 0, + "refine_s": 0.1681140839937143, + "app_s": 0.2758476249873638, + "proc_s": 0.5370369159500115, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 0, + "refine_s": 0.17078595800558105, + "app_s": 0.2869603330036625, + "proc_s": 0.5474640830070712, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 0, + "refine_s": 0.16318124998360872, + "app_s": 0.2745704999542795, + "proc_s": 0.5402777079725638, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 0, + "refine_s": 0.16581329196924344, + "app_s": 0.2813673330238089, + "proc_s": 0.5365602499805391, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 0, + "refine_s": 0.16840333299478516, + "app_s": 0.2776892500114627, + "proc_s": 0.5450266670086421, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 0, + "refine_s": 0.16367212496697903, + "app_s": 0.28042037499835715, + "proc_s": 0.5413236249587499, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 0, + "refine_s": 0.16772250004578382, + "app_s": 0.28507237503072247, + "proc_s": 0.5561928329989314, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 0, + "refine_s": 0.1631142920232378, + "app_s": 0.2694608750171028, + "proc_s": 0.5295657500391826, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 0, + "refine_s": 0.16358679201221094, + "app_s": 0.2769815000356175, + "proc_s": 0.5386707920115441, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 0, + "refine_s": 0.16445408400613815, + "app_s": 0.2746094590402208, + "proc_s": 0.5352777919615619, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 0, + "refine_s": 0.1641586250043474, + "app_s": 0.2747528330073692, + "proc_s": 0.5306259579956532, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 0, + "refine_s": 0.17843000002903864, + "app_s": 0.30001441697822884, + "proc_s": 0.5721297499840148, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 0, + "refine_s": 0.1647042499971576, + "app_s": 0.282513708982151, + "proc_s": 0.5403320000041276, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 0, + "refine_s": 0.18026908399770036, + "app_s": 0.2902139170328155, + "proc_s": 0.5472450829693116, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 1, + "refine_s": 0.16320504201576114, + "app_s": 0.2793240830069408, + "proc_s": 0.5430498329806142, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 1, + "refine_s": 0.4546708330162801, + "app_s": 0.56909020798048, + "proc_s": 0.8391004169825464, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 1, + "refine_s": 0.30161304102512076, + "app_s": 0.414292499946896, + "proc_s": 0.6691351250046864, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 1, + "refine_s": 0.22757683298550546, + "app_s": 0.3439359160256572, + "proc_s": 0.6092236249824055, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 1, + "refine_s": 0.20186291600111872, + "app_s": 0.32355620799353346, + "proc_s": 0.5817214160342701, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 1, + "refine_s": 0.1858903750544414, + "app_s": 0.30203270801575854, + "proc_s": 0.5664997919811867, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 1, + "refine_s": 0.1875669159926474, + "app_s": 0.29860504099633545, + "proc_s": 0.5768726249807514, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 1, + "refine_s": 0.20858012500684708, + "app_s": 0.3233266670140438, + "proc_s": 0.5883244579890743, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 1, + "refine_s": 0.17860545800067484, + "app_s": 0.29222037497675046, + "proc_s": 0.5780632080277428, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 1, + "refine_s": 0.18277966597815976, + "app_s": 0.3065654590027407, + "proc_s": 0.5917619580286555, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 1, + "refine_s": 0.16880466596921906, + "app_s": 0.2956641670316458, + "proc_s": 0.566733083978761, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 1, + "refine_s": 0.16418595798313618, + "app_s": 0.28513895900687203, + "proc_s": 0.5513982910197228, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 1, + "refine_s": 0.16367008298402652, + "app_s": 0.28116333298385143, + "proc_s": 0.5473743330221623, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 1, + "refine_s": 0.16984250000678003, + "app_s": 0.28919979196507484, + "proc_s": 0.5498045419808477, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 1, + "refine_s": 0.16930458298884332, + "app_s": 0.2765065000276081, + "proc_s": 0.5369281669845805, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 1, + "refine_s": 0.1687199579901062, + "app_s": 0.28896841703681275, + "proc_s": 0.5592051669955254, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 1, + "refine_s": 0.1734626250108704, + "app_s": 0.2934851250029169, + "proc_s": 0.5486983749433421, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 1, + "refine_s": 0.16687816701596603, + "app_s": 0.28641262504970655, + "proc_s": 0.5543689159676433, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 1, + "refine_s": 0.16826954204589128, + "app_s": 0.276926624996122, + "proc_s": 0.5378318749717437, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 1, + "refine_s": 0.16578787501202896, + "app_s": 0.2853290000348352, + "proc_s": 0.5449402089579962, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 1, + "refine_s": 0.1652977079502307, + "app_s": 0.27440104202833027, + "proc_s": 0.5443476670188829, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 2, + "refine_s": 0.16521745797945186, + "app_s": 0.27293529198504984, + "proc_s": 0.5389827089966275, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 2, + "refine_s": 0.47522700001718476, + "app_s": 0.5843865409842692, + "proc_s": 0.8416552500566468, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 2, + "refine_s": 0.27751512499526143, + "app_s": 0.38538720802171156, + "proc_s": 0.6433856249786913, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 2, + "refine_s": 0.22630600002594292, + "app_s": 0.33523324999259785, + "proc_s": 0.6013271249830723, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 2, + "refine_s": 0.20296762499492615, + "app_s": 0.312253458017949, + "proc_s": 0.5644991669687442, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 2, + "refine_s": 0.18945012497715652, + "app_s": 0.2997990839648992, + "proc_s": 0.5711774160154164, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 2, + "refine_s": 0.17950645799282938, + "app_s": 0.28658824996091425, + "proc_s": 0.5385939159896225, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 2, + "refine_s": 0.16881599999032915, + "app_s": 0.28034983295947313, + "proc_s": 0.5327422500122339, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 2, + "refine_s": 0.17138125002384186, + "app_s": 0.27748308301670477, + "proc_s": 0.5303975000279024, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 2, + "refine_s": 0.1691093749832362, + "app_s": 0.2846406669705175, + "proc_s": 0.5669477499905042, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 2, + "refine_s": 0.16516904102172703, + "app_s": 0.2761500420165248, + "proc_s": 0.5331756660016254, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 2, + "refine_s": 0.16295624995836988, + "app_s": 0.2714043750311248, + "proc_s": 0.5196677920175716, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 2, + "refine_s": 0.16323558398289606, + "app_s": 0.27033783303340897, + "proc_s": 0.5211525000049733, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 2, + "refine_s": 0.17451645800611004, + "app_s": 0.2924950830056332, + "proc_s": 0.5555950829875655, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 2, + "refine_s": 0.16276783304056153, + "app_s": 0.2718530419515446, + "proc_s": 0.5495248750085011, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 2, + "refine_s": 0.16308370797196403, + "app_s": 0.271027457958553, + "proc_s": 0.5212943340302445, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 2, + "refine_s": 0.16501624998636544, + "app_s": 0.2736170419957489, + "proc_s": 0.5284806659910828, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 2, + "refine_s": 0.1638147920020856, + "app_s": 0.2715895419823937, + "proc_s": 0.521642834006343, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 2, + "refine_s": 0.16715337499044836, + "app_s": 0.2745617920299992, + "proc_s": 0.5292878750478849, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 2, + "refine_s": 0.16366516600828618, + "app_s": 0.2753796670003794, + "proc_s": 0.5249614999629557, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 2, + "refine_s": 0.16673354199156165, + "app_s": 0.28099279198795557, + "proc_s": 0.5327180420281366, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 3, + "refine_s": 0.16717691597295925, + "app_s": 0.2843662080122158, + "proc_s": 0.5430021670181304, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 3, + "refine_s": 0.42042391700670123, + "app_s": 0.5432647920097224, + "proc_s": 0.7968455419759266, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 3, + "refine_s": 0.27425245800986886, + "app_s": 0.38604937499621883, + "proc_s": 0.638677374983672, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 3, + "refine_s": 0.2249560839845799, + "app_s": 0.3357907910249196, + "proc_s": 0.5909269999829121, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 3, + "refine_s": 0.19960245897527784, + "app_s": 0.31248850002884865, + "proc_s": 0.5625923750340007, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 3, + "refine_s": 0.1837893750052899, + "app_s": 0.2979429999832064, + "proc_s": 0.551647457992658, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 3, + "refine_s": 0.17519091599388048, + "app_s": 0.2890143329859711, + "proc_s": 0.537807332992088, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 3, + "refine_s": 0.17215562501223758, + "app_s": 0.2863401250215247, + "proc_s": 0.5412542499834672, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 3, + "refine_s": 0.16373816702980548, + "app_s": 0.27480145799927413, + "proc_s": 0.5449297910090536, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 3, + "refine_s": 0.1641622920287773, + "app_s": 0.2753716669976711, + "proc_s": 0.535893207998015, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 3, + "refine_s": 0.16299662500387058, + "app_s": 0.27524937503039837, + "proc_s": 0.5296556659741327, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 3, + "refine_s": 0.1639499160228297, + "app_s": 0.2804791670059785, + "proc_s": 0.5343499169684947, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 3, + "refine_s": 0.16158516699215397, + "app_s": 0.28091750002931803, + "proc_s": 0.540133332950063, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 3, + "refine_s": 0.16217779199359939, + "app_s": 0.2749993749894202, + "proc_s": 0.5262845000252128, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 3, + "refine_s": 0.1639009999926202, + "app_s": 0.2890730829676613, + "proc_s": 0.5470902499509975, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 3, + "refine_s": 0.16181858303025365, + "app_s": 0.28851375001249835, + "proc_s": 0.546184542006813, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 3, + "refine_s": 0.1664405839983374, + "app_s": 0.2968672920251265, + "proc_s": 0.5545601659687236, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 3, + "refine_s": 0.17381083400687203, + "app_s": 0.29832008300581947, + "proc_s": 0.5635241249692626, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 3, + "refine_s": 0.16985491703962907, + "app_s": 0.2907197499880567, + "proc_s": 0.5607527919928543, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 3, + "refine_s": 0.17182108398992568, + "app_s": 0.2933305409969762, + "proc_s": 0.580770583008416, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 3, + "refine_s": 0.16574620798928663, + "app_s": 0.28365366702200845, + "proc_s": 0.5392090000095777, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 0, + "repeat": 4, + "refine_s": 0.16714329196838662, + "app_s": 0.27515070902882144, + "proc_s": 0.5338649170007557, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 1, + "repeat": 4, + "refine_s": 0.4333191249752417, + "app_s": 0.543005459010601, + "proc_s": 0.7938407500041649, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 2, + "repeat": 4, + "refine_s": 0.26970162498764694, + "app_s": 0.3804795420146547, + "proc_s": 0.6381546250195242, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 3, + "repeat": 4, + "refine_s": 0.22465979197295383, + "app_s": 0.3350764579954557, + "proc_s": 0.5887944999849424, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 4, + "repeat": 4, + "refine_s": 0.2053732920321636, + "app_s": 0.3179334159940481, + "proc_s": 0.5916313329944387, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 5, + "repeat": 4, + "refine_s": 0.18459450005320832, + "app_s": 0.29427529202075675, + "proc_s": 0.5702649589511566, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 6, + "repeat": 4, + "refine_s": 0.17554862500401214, + "app_s": 0.2882329170242883, + "proc_s": 0.5470656669931486, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 7, + "repeat": 4, + "refine_s": 0.1710083750076592, + "app_s": 0.28003162500681356, + "proc_s": 0.5419785000267439, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 8, + "repeat": 4, + "refine_s": 0.16706929198699072, + "app_s": 0.2795882500358857, + "proc_s": 0.5495724999927916, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 9, + "repeat": 4, + "refine_s": 0.16399791603907943, + "app_s": 0.28503716603154317, + "proc_s": 0.5505290419678204, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 10, + "repeat": 4, + "refine_s": 0.16383129096357152, + "app_s": 0.27196470799390227, + "proc_s": 0.5343691669986583, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 11, + "repeat": 4, + "refine_s": 0.16358366602798924, + "app_s": 0.2717865839949809, + "proc_s": 0.5332734999828972, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 12, + "repeat": 4, + "refine_s": 0.16786904097534716, + "app_s": 0.2844747499912046, + "proc_s": 0.5394099580007605, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 13, + "repeat": 4, + "refine_s": 0.1677464580279775, + "app_s": 0.27710658399155363, + "proc_s": 0.5298613329650834, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 14, + "repeat": 4, + "refine_s": 0.1695938750053756, + "app_s": 0.27656441600993276, + "proc_s": 0.5300069159711711, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 15, + "repeat": 4, + "refine_s": 0.16646737500559539, + "app_s": 0.2792192500201054, + "proc_s": 0.5330007079755887, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 16, + "repeat": 4, + "refine_s": 0.16545162501279265, + "app_s": 0.27267066697822884, + "proc_s": 0.5206445420044474, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 17, + "repeat": 4, + "refine_s": 0.1637762090540491, + "app_s": 0.2770057920133695, + "proc_s": 0.5269216249580495, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 18, + "repeat": 4, + "refine_s": 0.16894941602367908, + "app_s": 0.27770774997770786, + "proc_s": 0.5301190000027418, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 19, + "repeat": 4, + "refine_s": 0.1637902919901535, + "app_s": 0.27238820801721886, + "proc_s": 0.5246299579739571, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "quarter", + "threads": 20, + "repeat": 4, + "refine_s": 0.1634957919595763, + "app_s": 0.2735055419616401, + "proc_s": 0.5308611250366084, + "max_error": 0.9999796549479072, + "rounds": 41, + "inserted": 213464, + "flips": 445657 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 0, + "refine_s": 0.19218254199950024, + "app_s": 0.3468022919842042, + "proc_s": 0.6374982089619152, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 0, + "refine_s": 0.4932019170373678, + "app_s": 0.6287764169974253, + "proc_s": 0.8895087920245714, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 0, + "refine_s": 0.32095912494696677, + "app_s": 0.4613355409819633, + "proc_s": 0.724279917019885, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 0, + "refine_s": 0.2662428329931572, + "app_s": 0.4054988749558106, + "proc_s": 0.66263874998549, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 0, + "refine_s": 0.24976029200479388, + "app_s": 0.3895472090225667, + "proc_s": 0.6571183750056662, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 0, + "refine_s": 0.2206885000341572, + "app_s": 0.3572729169973172, + "proc_s": 0.6253655829932541, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 0, + "refine_s": 0.205232625012286, + "app_s": 0.34288254100829363, + "proc_s": 0.6042599999927916, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 0, + "refine_s": 0.19878887495724484, + "app_s": 0.33626433298923075, + "proc_s": 0.5973710829857737, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 0, + "refine_s": 0.19131025002570823, + "app_s": 0.3308047500322573, + "proc_s": 0.5843279169639573, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 0, + "refine_s": 0.18610595900099725, + "app_s": 0.3288664580322802, + "proc_s": 0.6057604579837061, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 0, + "refine_s": 0.18765695800539106, + "app_s": 0.322349458001554, + "proc_s": 0.5801773749990389, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 0, + "refine_s": 0.2075111250160262, + "app_s": 0.3498448329628445, + "proc_s": 0.6083874590112828, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 0, + "refine_s": 0.18716875003883615, + "app_s": 0.32300958305131644, + "proc_s": 0.585114249959588, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 0, + "refine_s": 0.1893645420204848, + "app_s": 0.32755041698692366, + "proc_s": 0.6045164170209318, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 0, + "refine_s": 0.19333704200107604, + "app_s": 0.3306741670239717, + "proc_s": 0.5838643749593757, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 0, + "refine_s": 0.19314774998929352, + "app_s": 0.33003654098138213, + "proc_s": 0.59347316599451, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 0, + "refine_s": 0.18530091596767306, + "app_s": 0.3268100839923136, + "proc_s": 0.5901637079659849, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 0, + "refine_s": 0.18570566695416346, + "app_s": 0.3236977499909699, + "proc_s": 0.5808465000009164, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 0, + "refine_s": 0.18166449997806922, + "app_s": 0.3215505830012262, + "proc_s": 0.5894456249661744, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 0, + "refine_s": 0.18296170904068276, + "app_s": 0.3260467920335941, + "proc_s": 0.578251332975924, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 0, + "refine_s": 0.19580170803237706, + "app_s": 0.33404787501785904, + "proc_s": 0.5925997500307858, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 1, + "refine_s": 0.22385962499538437, + "app_s": 0.3638513750047423, + "proc_s": 0.6254413339775056, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 1, + "refine_s": 0.4928378330077976, + "app_s": 0.6398995409836061, + "proc_s": 0.918095542001538, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 1, + "refine_s": 0.3241677089827135, + "app_s": 0.459787999978289, + "proc_s": 0.7172753749764524, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 1, + "refine_s": 0.2537713749916293, + "app_s": 0.3859474589698948, + "proc_s": 0.6388330830377527, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 1, + "refine_s": 0.2341800419962965, + "app_s": 0.3696899999631569, + "proc_s": 0.6299209170392714, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 1, + "refine_s": 0.21086720802122727, + "app_s": 0.3448099590023048, + "proc_s": 0.5966398330056109, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 1, + "refine_s": 0.19918625004356727, + "app_s": 0.3331277089891955, + "proc_s": 0.586327958968468, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 1, + "refine_s": 0.19461925001814961, + "app_s": 0.33297091600252315, + "proc_s": 0.584005042037461, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 1, + "refine_s": 0.1843659169971943, + "app_s": 0.32277058303589, + "proc_s": 0.5862011670251377, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 1, + "refine_s": 0.18399741599569097, + "app_s": 0.32272512500640005, + "proc_s": 0.5791249999892898, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 1, + "refine_s": 0.18519095901865512, + "app_s": 0.32305412500863895, + "proc_s": 0.5783502500271425, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 1, + "refine_s": 0.1845905830268748, + "app_s": 0.31966699997428805, + "proc_s": 0.568415040965192, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 1, + "refine_s": 0.19238824996864423, + "app_s": 0.32936224999139085, + "proc_s": 0.5872080409899354, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 1, + "refine_s": 0.1881301249959506, + "app_s": 0.3203154999646358, + "proc_s": 0.5821200420032255, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 1, + "refine_s": 0.21890695800539106, + "app_s": 0.395300958014559, + "proc_s": 0.6841593749704771, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 1, + "refine_s": 0.18542666599387303, + "app_s": 0.32317391701508313, + "proc_s": 0.5871957920026034, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 1, + "refine_s": 0.1872234579641372, + "app_s": 0.3220074169803411, + "proc_s": 0.578909040952567, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 1, + "refine_s": 0.22810724994633347, + "app_s": 0.3733628340414725, + "proc_s": 0.6323042089934461, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 1, + "refine_s": 0.19326733303023502, + "app_s": 0.3346299169934355, + "proc_s": 0.641594749991782, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 1, + "refine_s": 0.18596529198111966, + "app_s": 0.32202708401018754, + "proc_s": 0.5794702920247801, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 1, + "refine_s": 0.18777641700580716, + "app_s": 0.32380608294624835, + "proc_s": 0.5767798330052756, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 2, + "refine_s": 0.1875912499963306, + "app_s": 0.3194604170275852, + "proc_s": 0.5663495420012623, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 2, + "refine_s": 0.46700854203663766, + "app_s": 0.6012590830214322, + "proc_s": 0.8518909579725005, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 2, + "refine_s": 0.310134457948152, + "app_s": 0.44338712497847155, + "proc_s": 0.6942057500127703, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 2, + "refine_s": 0.2525020409957506, + "app_s": 0.3858551669982262, + "proc_s": 0.6361081669456325, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 2, + "refine_s": 0.22714404098223895, + "app_s": 0.3622442079940811, + "proc_s": 0.6159850830445066, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 2, + "refine_s": 0.21041854098439217, + "app_s": 0.34649566700682044, + "proc_s": 0.6010498339892365, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 2, + "refine_s": 0.1968956669443287, + "app_s": 0.3293502079905011, + "proc_s": 0.5745015420252457, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 2, + "refine_s": 0.19267204200150445, + "app_s": 0.3244135419954546, + "proc_s": 0.5711819169810042, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 2, + "refine_s": 0.18290629098191857, + "app_s": 0.317752415954601, + "proc_s": 0.5677526249783114, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 2, + "refine_s": 0.21670175000326708, + "app_s": 0.3565285830409266, + "proc_s": 0.6344465839792974, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 2, + "refine_s": 0.1945748750003986, + "app_s": 0.34293525002431124, + "proc_s": 0.629234624968376, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 2, + "refine_s": 0.18489354202756658, + "app_s": 0.3193198749795556, + "proc_s": 0.5838422079687007, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 2, + "refine_s": 0.1810037920367904, + "app_s": 0.3177123749628663, + "proc_s": 0.5819363750051707, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 2, + "refine_s": 0.1948451659991406, + "app_s": 0.33447525004157797, + "proc_s": 0.5885960409650579, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 2, + "refine_s": 0.1885699579725042, + "app_s": 0.32793466601287946, + "proc_s": 0.5927052500192076, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 2, + "refine_s": 0.1834050840116106, + "app_s": 0.31453525001415983, + "proc_s": 0.5598560830112547, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 2, + "refine_s": 0.1853002499556169, + "app_s": 0.324012000055518, + "proc_s": 0.5717074160347693, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 2, + "refine_s": 0.19734645797871053, + "app_s": 0.33767399995122105, + "proc_s": 0.6034742079791613, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 2, + "refine_s": 0.21014770801411942, + "app_s": 0.3504142920137383, + "proc_s": 0.6266499169869348, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 2, + "refine_s": 0.1862394589697942, + "app_s": 0.3203290830133483, + "proc_s": 0.5810667499899864, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 2, + "refine_s": 0.22274995897896588, + "app_s": 0.3573714999947697, + "proc_s": 0.6091424999758601, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 3, + "refine_s": 0.18142450001323596, + "app_s": 0.3198369579622522, + "proc_s": 0.5813920000218786, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 3, + "refine_s": 0.48093483300181106, + "app_s": 0.616463583020959, + "proc_s": 0.8646051249816082, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 3, + "refine_s": 0.313593165948987, + "app_s": 0.4482417079852894, + "proc_s": 0.7033321249764413, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 3, + "refine_s": 0.2579711669823155, + "app_s": 0.39185408398043364, + "proc_s": 0.6526504170033149, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 3, + "refine_s": 0.23386999999638647, + "app_s": 0.3686269999598153, + "proc_s": 0.6340217090328224, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 3, + "refine_s": 0.2231235000072047, + "app_s": 0.36574741697404534, + "proc_s": 0.6449944589985535, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 3, + "refine_s": 0.21123170899227262, + "app_s": 0.35729170800186694, + "proc_s": 0.631999375007581, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 3, + "refine_s": 0.19581545796245337, + "app_s": 0.34814854100113735, + "proc_s": 0.6211294169770554, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 3, + "refine_s": 0.19250075001036748, + "app_s": 0.3268261250341311, + "proc_s": 0.5733433330315165, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 3, + "refine_s": 0.18573791696690023, + "app_s": 0.3203199169947766, + "proc_s": 0.5854668339597993, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 3, + "refine_s": 0.1861559580429457, + "app_s": 0.31904379202751443, + "proc_s": 0.5726668750285171, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 3, + "refine_s": 0.18145633302628994, + "app_s": 0.31322887499118224, + "proc_s": 0.5647232919582166, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 3, + "refine_s": 0.1863538749748841, + "app_s": 0.32191633398178965, + "proc_s": 0.5695632909773849, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 3, + "refine_s": 0.18306750000920147, + "app_s": 0.3183670000289567, + "proc_s": 0.5827999170287512, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 3, + "refine_s": 0.18427679198794067, + "app_s": 0.31632366601843387, + "proc_s": 0.5702386249904521, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 3, + "refine_s": 0.18595929199364036, + "app_s": 0.31744424998760223, + "proc_s": 0.5634784160065465, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 3, + "refine_s": 0.19105899997521192, + "app_s": 0.3254358749836683, + "proc_s": 0.5728529170155525, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 3, + "refine_s": 0.19445399998221546, + "app_s": 0.3303953750291839, + "proc_s": 0.5862195829977281, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 3, + "refine_s": 0.18638845800887793, + "app_s": 0.31987845798721537, + "proc_s": 0.5678018750040792, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 3, + "refine_s": 0.1882549169822596, + "app_s": 0.322474584041629, + "proc_s": 0.5825747920316644, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 3, + "refine_s": 0.18533716595266014, + "app_s": 0.3191215000115335, + "proc_s": 0.5751382500166073, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 0, + "repeat": 4, + "refine_s": 0.19697062496561557, + "app_s": 0.3326757079921663, + "proc_s": 0.5950728749739937, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 1, + "repeat": 4, + "refine_s": 0.4684047920163721, + "app_s": 0.6030207090079784, + "proc_s": 0.8590820410172455, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 2, + "repeat": 4, + "refine_s": 0.31239716697018594, + "app_s": 0.4478870840393938, + "proc_s": 0.7222833340056241, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 3, + "repeat": 4, + "refine_s": 0.2539550000219606, + "app_s": 0.38831541704712436, + "proc_s": 0.6361111250007525, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 4, + "repeat": 4, + "refine_s": 0.23719612503191456, + "app_s": 0.3769279170082882, + "proc_s": 0.6535788340261206, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 5, + "repeat": 4, + "refine_s": 0.20887545798905194, + "app_s": 0.35012579103931785, + "proc_s": 0.6073625419521704, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 6, + "repeat": 4, + "refine_s": 0.19933108298573643, + "app_s": 0.3326672080438584, + "proc_s": 0.5871368339867331, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 7, + "repeat": 4, + "refine_s": 0.19267625000793487, + "app_s": 0.3341012089513242, + "proc_s": 0.5826445000129752, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 8, + "repeat": 4, + "refine_s": 0.18427983298897743, + "app_s": 0.3164395830244757, + "proc_s": 0.5679402910172939, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 9, + "repeat": 4, + "refine_s": 0.22461833304259926, + "app_s": 0.4248082499834709, + "proc_s": 0.8571257920120843, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 10, + "repeat": 4, + "refine_s": 0.2151929999818094, + "app_s": 0.35943466698518023, + "proc_s": 0.6437141660135239, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 11, + "repeat": 4, + "refine_s": 0.19139804202131927, + "app_s": 0.3365792080294341, + "proc_s": 0.6148614999838173, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 12, + "repeat": 4, + "refine_s": 0.19049137498950586, + "app_s": 0.3352868329966441, + "proc_s": 0.6012289579957724, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 13, + "repeat": 4, + "refine_s": 0.19093991699628532, + "app_s": 0.32580287498421967, + "proc_s": 0.5755357920425013, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 14, + "repeat": 4, + "refine_s": 0.19241300004068762, + "app_s": 0.3275207090191543, + "proc_s": 0.5967206250061281, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 15, + "repeat": 4, + "refine_s": 0.1897774999961257, + "app_s": 0.32348299998557195, + "proc_s": 0.5844872499583289, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 16, + "repeat": 4, + "refine_s": 0.19526058301562443, + "app_s": 0.33598900004290044, + "proc_s": 0.6175715830177069, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 17, + "repeat": 4, + "refine_s": 0.18571458297083154, + "app_s": 0.3273588750162162, + "proc_s": 0.5932792089879513, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 18, + "repeat": 4, + "refine_s": 0.1868612079997547, + "app_s": 0.3227453750441782, + "proc_s": 0.5833787909941748, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 19, + "repeat": 4, + "refine_s": 0.21050558297429234, + "app_s": 0.3535387919982895, + "proc_s": 0.6234238750184886, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + }, + { + "domain": "tile", + "threads": 20, + "repeat": 4, + "refine_s": 0.19713425001828, + "app_s": 0.3359581249533221, + "proc_s": 0.6148512500221841, + "max_error": 0.9999759197235107, + "rounds": 53, + "inserted": 219837, + "flips": 445675 + } + ], + "stats": [ + { + "domain": "quarter", + "threads": 0, + "n": 5, + "median": 0.16714329196838662, + "min": 0.16320504201576114, + "max": 0.1755662080249749 + }, + { + "domain": "quarter", + "threads": 1, + "n": 5, + "median": 0.45392958301818, + "min": 0.42042391700670123, + "max": 0.47522700001718476 + }, + { + "domain": "quarter", + "threads": 2, + "n": 5, + "median": 0.27751512499526143, + "min": 0.26970162498764694, + "max": 0.30161304102512076 + }, + { + "domain": "quarter", + "threads": 3, + "n": 5, + "median": 0.2249560839845799, + "min": 0.2210613749921322, + "max": 0.22757683298550546 + }, + { + "domain": "quarter", + "threads": 4, + "n": 5, + "median": 0.20186291600111872, + "min": 0.19960245897527784, + "max": 0.2053732920321636 + }, + { + "domain": "quarter", + "threads": 5, + "n": 5, + "median": 0.1858903750544414, + "min": 0.1837893750052899, + "max": 0.18945012497715652 + }, + { + "domain": "quarter", + "threads": 6, + "n": 5, + "median": 0.17680195899447426, + "min": 0.17519091599388048, + "max": 0.1875669159926474 + }, + { + "domain": "quarter", + "threads": 7, + "n": 5, + "median": 0.1710083750076592, + "min": 0.1681140839937143, + "max": 0.20858012500684708 + }, + { + "domain": "quarter", + "threads": 8, + "n": 5, + "median": 0.17078595800558105, + "min": 0.16373816702980548, + "max": 0.17860545800067484 + }, + { + "domain": "quarter", + "threads": 9, + "n": 5, + "median": 0.1641622920287773, + "min": 0.16318124998360872, + "max": 0.18277966597815976 + }, + { + "domain": "quarter", + "threads": 10, + "n": 5, + "median": 0.16516904102172703, + "min": 0.16299662500387058, + "max": 0.16880466596921906 + }, + { + "domain": "quarter", + "threads": 11, + "n": 5, + "median": 0.1639499160228297, + "min": 0.16295624995836988, + "max": 0.16840333299478516 + }, + { + "domain": "quarter", + "threads": 12, + "n": 5, + "median": 0.16367008298402652, + "min": 0.16158516699215397, + "max": 0.16786904097534716 + }, + { + "domain": "quarter", + "threads": 13, + "n": 5, + "median": 0.1677464580279775, + "min": 0.16217779199359939, + "max": 0.17451645800611004 + }, + { + "domain": "quarter", + "threads": 14, + "n": 5, + "median": 0.1639009999926202, + "min": 0.16276783304056153, + "max": 0.1695938750053756 + }, + { + "domain": "quarter", + "threads": 15, + "n": 5, + "median": 0.16358679201221094, + "min": 0.16181858303025365, + "max": 0.1687199579901062 + }, + { + "domain": "quarter", + "threads": 16, + "n": 5, + "median": 0.16545162501279265, + "min": 0.16445408400613815, + "max": 0.1734626250108704 + }, + { + "domain": "quarter", + "threads": 17, + "n": 5, + "median": 0.1641586250043474, + "min": 0.1637762090540491, + "max": 0.17381083400687203 + }, + { + "domain": "quarter", + "threads": 18, + "n": 5, + "median": 0.16894941602367908, + "min": 0.16715337499044836, + "max": 0.17843000002903864 + }, + { + "domain": "quarter", + "threads": 19, + "n": 5, + "median": 0.1647042499971576, + "min": 0.16366516600828618, + "max": 0.17182108398992568 + }, + { + "domain": "quarter", + "threads": 20, + "n": 5, + "median": 0.16574620798928663, + "min": 0.1634957919595763, + "max": 0.18026908399770036 + }, + { + "domain": "tile", + "threads": 0, + "n": 5, + "median": 0.19218254199950024, + "min": 0.18142450001323596, + "max": 0.22385962499538437 + }, + { + "domain": "tile", + "threads": 1, + "n": 5, + "median": 0.48093483300181106, + "min": 0.46700854203663766, + "max": 0.4932019170373678 + }, + { + "domain": "tile", + "threads": 2, + "n": 5, + "median": 0.313593165948987, + "min": 0.310134457948152, + "max": 0.3241677089827135 + }, + { + "domain": "tile", + "threads": 3, + "n": 5, + "median": 0.2539550000219606, + "min": 0.2525020409957506, + "max": 0.2662428329931572 + }, + { + "domain": "tile", + "threads": 4, + "n": 5, + "median": 0.2341800419962965, + "min": 0.22714404098223895, + "max": 0.24976029200479388 + }, + { + "domain": "tile", + "threads": 5, + "n": 5, + "median": 0.21086720802122727, + "min": 0.20887545798905194, + "max": 0.2231235000072047 + }, + { + "domain": "tile", + "threads": 6, + "n": 5, + "median": 0.19933108298573643, + "min": 0.1968956669443287, + "max": 0.21123170899227262 + }, + { + "domain": "tile", + "threads": 7, + "n": 5, + "median": 0.19461925001814961, + "min": 0.19267204200150445, + "max": 0.19878887495724484 + }, + { + "domain": "tile", + "threads": 8, + "n": 5, + "median": 0.1843659169971943, + "min": 0.18290629098191857, + "max": 0.19250075001036748 + }, + { + "domain": "tile", + "threads": 9, + "n": 5, + "median": 0.18610595900099725, + "min": 0.18399741599569097, + "max": 0.22461833304259926 + }, + { + "domain": "tile", + "threads": 10, + "n": 5, + "median": 0.18765695800539106, + "min": 0.18519095901865512, + "max": 0.2151929999818094 + }, + { + "domain": "tile", + "threads": 11, + "n": 5, + "median": 0.18489354202756658, + "min": 0.18145633302628994, + "max": 0.2075111250160262 + }, + { + "domain": "tile", + "threads": 12, + "n": 5, + "median": 0.18716875003883615, + "min": 0.1810037920367904, + "max": 0.19238824996864423 + }, + { + "domain": "tile", + "threads": 13, + "n": 5, + "median": 0.1893645420204848, + "min": 0.18306750000920147, + "max": 0.1948451659991406 + }, + { + "domain": "tile", + "threads": 14, + "n": 5, + "median": 0.19241300004068762, + "min": 0.18427679198794067, + "max": 0.21890695800539106 + }, + { + "domain": "tile", + "threads": 15, + "n": 5, + "median": 0.18595929199364036, + "min": 0.1834050840116106, + "max": 0.19314774998929352 + }, + { + "domain": "tile", + "threads": 16, + "n": 5, + "median": 0.1872234579641372, + "min": 0.1853002499556169, + "max": 0.19526058301562443 + }, + { + "domain": "tile", + "threads": 17, + "n": 5, + "median": 0.19445399998221546, + "min": 0.18570566695416346, + "max": 0.22810724994633347 + }, + { + "domain": "tile", + "threads": 18, + "n": 5, + "median": 0.1868612079997547, + "min": 0.18166449997806922, + "max": 0.21014770801411942 + }, + { + "domain": "tile", + "threads": 19, + "n": 5, + "median": 0.1862394589697942, + "min": 0.18296170904068276, + "max": 0.21050558297429234 + }, + { + "domain": "tile", + "threads": 20, + "n": 5, + "median": 0.19580170803237706, + "min": 0.18533716595266014, + "max": 0.22274995897896588 + } + ], + "quality": { + "quarter": { + "worst_angle": 0.3955267116350698, + "angle_median": 45.0, + "share_under_1": 0.000028023175165862168, + "max_degree": 18, + "within_tolerance": true, + "delaunay_checked": 641791, + "delaunay_ambiguous": 80136, + "delaunay_violations": 0, + "mesh_sha256": "ccebf96a86c6c5e244e4a0281919de4e866fcfe789b66024a290ac2af33771a1" + }, + "tile": { + "worst_angle": 0.6295986084102668, + "angle_median": 45.0, + "share_under_1": 2.1538262723728703e-6, + "max_degree": 74, + "within_tolerance": true, + "delaunay_checked": 692056, + "delaunay_ambiguous": 94469, + "delaunay_violations": 0, + "mesh_sha256": "11741a81adfa17b34a1ea56875ec9e791414d17625eb1c58305e7c0cf10e005c" + } + }, + "accept_quality": false, + "threshold_pct": 5.0, + "verdict": [ + "ACCEPTED" + ] +} diff --git a/docs/benchmarks/2026-09-28/16b0-acceptance/README.md b/docs/benchmarks/2026-09-28/16b0-acceptance/README.md new file mode 100644 index 00000000..50b66159 --- /dev/null +++ b/docs/benchmarks/2026-09-28/16b0-acceptance/README.md @@ -0,0 +1,269 @@ +# Increment 16b-0 acceptance (@perf, 2026-09-28) + +**Verdict: ACCEPTED** (battery against a battery baseline measured back to +back). The standard 1 m benchmark is unchanged and the meshes are +bit-identical. `node` on M3's 48 km CORINE square, layout B, fell from +84.1 s to 0.144 s. Short summary: `../16b0-acceptance.md`. + +## What was measured, and on what + +- **Branch** `increment16b0-verifier-sweep` at `4e83389` (master merged in). + **Base**: master `dc372a5` (#106), in a temporary detached `git worktree` + in the session scratchpad. The worktree was removed afterwards. + `git diff --stat dc372a5 4e83389 -- include src_cpp src_python bindings` + lists one file, `include/terrain/noding/noded_pslg_builder.hpp`. That header + is included by `node.hpp`, `core/noded_pslg.hpp` and `bindings/core.cpp`. + Nothing under `include/terrain/refinement/` includes it. +- **Builds**: `bench.py`'s own Release builds into `/build-bench`. + `_core` sha256 prefixes: base `2b69e078db7f`, 16b-0 `190587e7ba0e`, the + same in all three runs of each. Parts 2-4 import those same `build-bench/pkg` + trees (bench.py's child technique: drop the editable finder, put the pkg + first, assert that `tin_engine` and `_core` load from it). So the old + verifier was measured with the same compiler and flags as the new one. No + C++ was patched, so there was no scratch build to sanitize. +- **Machine**: Apple M1 Max (8 P + 2 E), 32 GiB, macOS 27.0. + `caffeinate -ims` was held for every run. +- **Power: battery, discharging, all runs.** The percentages fell from 76 % + (15:21) to 62 % (15:53). The `pmset -g batt` line is logged before every + case in `corine/logs/*.log`, and before and after each bench run in its + `run.json`. An earlier attempt at 12:22 hit a Mac sleep and a bench.py + mesh-dir error. It produced no evidence, and none of its timings are used + (the log was kept in the scratchpad as `pairs-void-sleep.log`, not here). +- **Mesh digests.** The bench meshes are hashed by bench.py. For `node`, a + digest over the noded vertices and the per-edge masks was taken (`drv.py`, + `digest`). + +## 1. The 1 m benchmark and thread sweep (`tools/bench.py`, blob `77765b1`) + +Three pairs, run in the order base, 16b-0, base, 16b-0, base, 16b-0, between +15:21 and 15:53 CEST. Every 16b-0 run names its base explicitly +(`--baseline`). Command (`corine/scripts/pairs.sh`, `pair3.sh`): + +``` +.venv/bin/python tools/bench.py run --label 16b0-acceptance/