Skip to content

Add exact-arithmetic test of the projection - #1280

Open
justinhwang wants to merge 1 commit into
uber:masterfrom
justinhwang:exact-projection-test
Open

justinhwang wants to merge 1 commit into
uber:masterfrom
justinhwang:exact-projection-test

Conversation

@justinhwang

@justinhwang justinhwang commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Every floating-point test in H3 compares one implementation with another, or with values an implementation produced earlier. That shows agreement, not accuracy: both sides can be wrong in the same way. This adds testExactProjectionExhaustive, which measures how far _faceIjkToVec3, cellToLatLng and every vertex of cellToBoundary land from the exact value of the formula they implement, using a 256-bit MPFR evaluation as the reference.

The reference does not reproduce the transcendental calls. It evaluates the algebraic form of the same chain: atan2 then cos/sin on the azimuth becomes x/r, y/r; the Class III rotation and the face axis subtraction become angle-addition identities with sqrt(3/28), sqrt(25/28) and sin/cos of the twenty faceAxesAzRadsCII literals; atan then cos/sin on the gnomonic radius becomes 1/sqrt(1+r²), r/sqrt(1+r²). Everything else is rational arithmetic and square roots, which MPFR does correctly rounded. faceCenterPoint, faceAxesAzRadsCII and RES0_U_GNOMONIC are taken as exact definitions; M_SQRT3_2, M_RSQRT7, M_ONETHIRD are the exact numbers their constants approximate. Separation is the chord between the library's answer (converted back to a unit vector at full precision) and the exact unit vector, reported in degrees of arc, which is uniform over the sphere where a per-coordinate lat/lng comparison is not.

The same test exists for the pure-Go port (uber/h3-go#155) with the same oracle; the sampled cells are chosen by the same rule so the two can be compared cell for cell.

What is covered

  • Every cell at resolutions 0–2 by default (5,882 cells, 41k boundary vertices, 0.3 s), plus one deterministic descendant of each base cell at every resolution to 15.
  • H3_EXACT_PROJECTION_RES=4 raises the full enumeration to res 4 (335k cells, 2.0M vertices, ~13 s). H3_EXACT_PROJECTION_REPORT=1 prints the worst case per resolution and a histogram in ulps instead of asserting.
  • Boundaries follow _faceIjkToCellBoundary / _faceIjkPentToCellBoundary step for step in the test, including the _v2dIntersect edge-crossing vertices, and assert the vertex count matches cellToBoundary. Nothing in the boundary code is refactored.

Results (macOS arm64, Apple clang, after #1255)

Worst separation, one ulp of a unit-vector component = 1.3e-14°:

Res cells _faceIjkToVec3 cellToLatLng cellToBoundary vertices
0 122 2.8e-14° (2.2 ulp) 2.8e-14° (2.2) 2.9e-14° (2.3)
1 842 2.6e-14° (2.0) 2.5e-14° (2.0) 4.0e-14° (3.1)
2 5,882 4.8e-14° (3.8) 4.9e-14° (3.8) 4.1e-14° (3.2)
3 41,162 3.6e-14° (2.8) 3.6e-14° (2.9) 4.7e-14° (3.7)
4 288,122 5.1e-14° (4.0) 5.3e-14° (4.1) 5.0e-14° (4.0)
5–15 (sampled) 122 each ≤ 4.1e-14° (3.2) ≤ 4.7e-14° (3.7) ≤ 5.3e-14° (4.1)

The bound is set at 1e-13° (about 8 ulp), roughly twice the worst observed. Before #1255 the cellToLatLng column reached 6.9e-12° (541 ulp) at res 4 near the pole; this test is what found that.

Linux (Debian bookworm, gcc 12.2, glibc 2.36, Docker)

Full enumeration to res 3, samples to 15. Worst separation over all outputs per platform:

Platform Res 0–3 (every cell) Res 4–15 (sampled) Worst overall
Linux aarch64 4.8e-14° (3.8 ulp) 5.3e-14° (4.1 ulp) 5.3e-14°, res 11 vertex
Linux x86-64 4.8e-14° (3.8 ulp) 5.4e-14° (4.2 ulp) 5.4e-14°, res 11 vertex
macOS arm64 (above) 4.9e-14° (3.8 ulp) 5.3e-14° (4.1 ulp) 5.3e-14°, res 4 center

The three libms land within half an ulp of each other at every resolution, so the cross-platform drift seen in #917 / #891 is each platform being 2–4 ulp from the exact value in slightly different directions, not one platform being wrong.

Build and CI

  • ENABLE_EXACT_PROJECTION_TEST defaults to ON when mpfr.h, libmpfr and libgmp are found and OFF with a status message otherwise. Nothing else in the build touches MPFR.
  • Linux gcc jobs install libmpfr-dev, macOS jobs brew install mpfr. The clang sanitizer jobs and the 32-bit jobs set it OFF explicitly (libmpfr is not instrumented, and there is no 32-bit MPFR in the multilib install). Windows is unchanged (not found, so skipped).
  • The Exhaustive suffix keeps it out of test-fast, so the Valgrind job does not run it.

Library change

faceCenterPoint, faceAxesAzRadsCII and faceNeighbors in faceijk.c lose static and gain extern declarations in faceijk.h, in the same way baseCells.h exports its tables, so the test reads the library's own definitions instead of a copy. No behaviour change.

Test plan

  • ctest: 326/326 pass (Release, macOS arm64), including the new test
  • Report mode at res 4 as above
  • Report mode on Linux aarch64 and x86-64 (Docker, Debian bookworm) as above; 3709/3709 assertions in default mode on both

🤖 Generated with Claude Code

Adds testExactProjectionExhaustive, which measures how far the Vec3d
from _faceIjkToVec3, cellToLatLng and every vertex of cellToBoundary
land from the exact value of the projection they implement, using a
256-bit MPFR evaluation of the algebraic form of the same chain as the
reference rather than another implementation. Separations are measured
as the chord in unit-vector space and reported in degrees of arc.

Every cell at resolutions 0 to 2 is checked by default, plus one
deterministic descendant per base cell at resolutions 3 to 15 (the
same cells as h3-go's exact_projection_test.go). H3_EXACT_PROJECTION_RES
raises the full enumeration and H3_EXACT_PROJECTION_REPORT=1 prints
the worst case per resolution and a histogram in ulps instead of
asserting. The bound is 1e-13 degrees; the worst observed is 5.3e-14
degrees (4.1 ulp) over every cell to resolution 4 on macOS arm64.

faceCenterPoint, faceAxesAzRadsCII and faceNeighbors are exported from
faceijk.c so the test reads the library's own definitions. The test is
built only when MPFR and GMP are found (ENABLE_EXACT_PROJECTION_TEST),
installed in the gcc Linux and macOS CI jobs, and left off in the clang
sanitizer jobs since libmpfr is not instrumented.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8d58aadd-75f3-40f7-89e3-0ab021d6a456
Commits

Reviewing files that changed from the base of the PR and between 24946ad and 9226cd0.

Files selected for processing: 9
  • .github/workflows/test-linux-32bit.yml
  • .github/workflows/test-linux.yml
  • .github/workflows/test-macos.yml
  • CMakeLists.txt
  • CMakeTests.cmake
  • src/apps/miscapps/generateFaceCenterPoint.c
  • src/apps/testapps/testExactProjectionExhaustive.c
  • src/h3lib/include/faceijk.h
  • src/h3lib/lib/faceijk.c

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Walkthrough

The PR adds an MPFR-based test that compares H3 projection outputs with high-precision references. CMake detects the required libraries and registers the test when enabled. CI workflows enable it in selected Linux GCC and macOS jobs and disable it in other configurations.

Changes

Exact Projection Validation

Layer / File(s) Summary
Expose projection geometry tables
src/h3lib/include/faceijk.h, src/h3lib/lib/faceijk.c, src/apps/miscapps/generateFaceCenterPoint.c
The face-center, face-axis azimuth, and face-neighbor tables now have external linkage and declarations. The face-center generator emits an externally linked array.
Add MPFR reference and exhaustive checks
src/apps/testapps/testExactProjectionExhaustive.c, CMakeTests.cmake, CMakeLists.txt
The new test compares cell centers, vectors, and boundary vertices with high-precision reference values. CMake detects MPFR and GMP, registers the test when enabled, and includes its source in the test application list.
Configure CI test coverage
.github/workflows/test-linux.yml, .github/workflows/test-macos.yml, .github/workflows/test-linux-32bit.yml
Linux GCC and macOS jobs install MPFR and enable the test. Linux 32-bit jobs and the Clang sanitizer job disable it.

Priority: Normal

Estimated code review effort: 4 (Complex) | ~50 minutes

Change: Other

Sequence Diagram(s)

sequenceDiagram
  participant Test as testExactProjectionExhaustive
  participant MPFR
  participant CellToLatLng as cellToLatLng
  participant FaceIjk as _faceIjkToVec3
  participant Boundary as cellToBoundary
  Test->>MPFR: Compute high-precision projection reference
  Test->>CellToLatLng: Get cell center coordinates
  Test->>FaceIjk: Get projected vector
  Test->>Boundary: Get cell boundary vertices
  Test->>Test: Compare outputs with reference values
Loading

Suggested reviewers: isaacbrodsky, wewei, ajfriend

Merge Risk: Minimal · up to 9226c

This PR adds an optional exact-arithmetic projection test, gated on MPFR and GMP, and exports three internal tables without changing library behavior. No actionable merge-blocking risk was found. The Linux CI results are still pending.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 51.85% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 27 functions across 4 files. (5 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title "Add exact-arithmetic test of the projection" matches the main change. The PR adds testExactProjectionExhaustive, which compares the projection code against a 256-bit MPFR reference. The tit…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The pull request description directly explains the new exact-arithmetic projection test, its MPFR and GMP dependencies, CI configuration, exported tables, test coverage, and reported results. These de…
Full details: Title check

Explanation

The title "Add exact-arithmetic test of the projection" matches the main change. The PR adds testExactProjectionExhaustive, which compares the projection code against a 256-bit MPFR reference. The title is short, specific, and does not use vague terms. It omits the supporting build and export changes, which is acceptable.

Full details: Description check

Explanation

The pull request description directly explains the new exact-arithmetic projection test, its MPFR and GMP dependencies, CI configuration, exported tables, test coverage, and reported results. These details match the changeset.

Full details: Docstring Coverage

Explanation

Docstring coverage is 51.85% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 27 functions across 4 files. (5 skipped: 5 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 99.758%. remained the same — justinhwang:exact-projection-test into uber:master

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants