-
Notifications
You must be signed in to change notification settings - Fork 54
EFT-based compensated arithmetic for spherical geometry (GCA intersection, point-in-face, face bounds) #1513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
rajeeja
wants to merge
65
commits into
main
Choose a base branch
from
rajeeja/accusphere
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
65 commits
Select commit
Hold shift + click to select a range
878e075
Port AccuSphGeom EFT algorithms and add spherical geometry user guide
rajeeja 1bbf349
Fix pre-commit: remove unused imports and variable
rajeeja 1034a18
Fix pre-commit: split semicolons in notebook cells
rajeeja fb74e75
Address Hongyu's review: port AccuSphGeom compensated arithmetic, rew…
rajeeja 256b347
Fix RTD: remove RST-invalid numbered list from gca_const_lat_intersec…
rajeeja e8999e6
Fix RTD notebook kernel metadata
rajeeja 0220276
Address AccuSphGeom review feedback
rajeeja 0fe327c
Separate intersection kernels into three layers: numerical core, stat…
rajeeja 94c06fe
Fix NaN/Inf propagation in intersection kernels for denom=0 and plana…
rajeeja 2f4147b
Add geometry kernel benchmarks and to_raster auto-extent test
rajeeja 3b3f30c
Address AccuSphGeom review: precision wording, dead code removal, cor…
rajeeja e33a26c
Pin tornado<6.5.7 to fix Windows CI; remove dead shim; fix notebook A…
rajeeja f3c8618
Trim computing.py docstring; drop inline=always from L1 kernels
rajeeja 59a8020
Revert tornado pin; root cause is openssl 3.6.3 on Windows
rajeeja 35c85a8
Port AccuSphGeom EFT algorithms and add spherical geometry user guide
rajeeja 6c1497e
Fix pre-commit: remove unused imports and variable
rajeeja 57373d0
Fix pre-commit: split semicolons in notebook cells
rajeeja 3a55e5d
Address Hongyu's review: port AccuSphGeom compensated arithmetic, rew…
rajeeja 683bc9a
Fix RTD: remove RST-invalid numbered list from gca_const_lat_intersec…
rajeeja 3ae95f3
Fix RTD notebook kernel metadata
rajeeja 7e35cee
Address AccuSphGeom review feedback
rajeeja 1f844e1
Separate intersection kernels into three layers: numerical core, stat…
rajeeja ec81e16
Fix NaN/Inf propagation in intersection kernels for denom=0 and plana…
rajeeja 58de273
Merge origin/rajeeja/accusphere: resolve geometry kernel conflicts
rajeeja 896d968
Merge branch 'main' into rajeeja/accusphere
rajeeja c0f281d
computing: use FMA two_prod where available (portable Veltkamp fallback)
rajeeja 3438b90
intersections: add allocation-free scalar const-lat L1 kernel
rajeeja aab2bbd
Merge branch 'main' into rajeeja/accusphere
rajeeja 16cb171
Merge branch 'main' into rajeeja/accusphere
rajeeja 69c6d48
style: apply ruff formatting to computing.py
rajeeja 4c4fa94
Merge branch 'main' into rajeeja/accusphere
rajeeja 7968008
Merge branch 'main' into rajeeja/accusphere
rajeeja e323380
Merge branch 'main' into rajeeja/accusphere
hongyuchen1030 5b6082b
Merge remote-tracking branch 'origin/main' into update-accusphere-1513
rajeeja 456c587
Merge remote-tracking branch 'origin/main' into update-accusphere-1513
rajeeja 07ac029
Clean up spherical geometry review items
rajeeja 273f968
Merge branch 'main' into rajeeja/accusphere
rajeeja dba0206
Scalarize constant-latitude intersection dispatcher to remove per-cal…
rajeeja 4366e02
Merge remote-tracking branch 'origin/main' into rajeeja/accusphere
rajeeja ffcc91e
Merge remote-tracking branch 'origin/rajeeja/accusphere' into rajeeja…
rajeeja 64885f6
Merge branch 'main' into rajeeja/accusphere
rajeeja a463913
Import benchmark kernels inside setup to avoid asv collection failures
rajeeja 465fd4c
Cmd/accusphere (#1579)
cmdupuis3 75f06c0
Merge remote-tracking branch 'origin/main' into rajeeja/accusphere
rajeeja 0d131d7
Add thread-scaling benchmark for FP64 vs AccuX constlat dispatcher
rajeeja b9f5bb3
Cap thread-scaling sweep at performance cores to avoid E-core artifact
rajeeja f61555b
Fix thread-scaling plot ticks to show only 1,2,4,8
rajeeja 146672c
o Fix pre-commit
rajeeja e9b394f
o doc fixes
rajeeja a7d8e88
o Remove benchmark image from repo
rajeeja a5fd603
Split out point-in-face and lat-lon bounds to a separate PR
rajeeja d2550bc
Merge remote-tracking branch 'origin/main' into rajeeja/accusphere
rajeeja e87873f
o computing: match AccuSphGeom acc_sqrt_re exactly (residual order, b…
rajeeja 4af6ed1
o intersections: compensated norm in AccuXGCA kernel, branch-free mas…
rajeeja ff2d6e0
o arcs: branch-free on_minor_arc int mask at tol 1e-8 matching AccuSp…
rajeeja 3e3a972
o benchmarks: drop point-in-polygon kernel bench, moved to point-in-f…
rajeeja 5c80ff7
o benchmarks: drop thread-scaling constlat, moved to separate scaling…
rajeeja 66b907b
o docs: move spherical-geometry notebook out, depends on split-out po…
rajeeja 8e6c88a
o docs: drop spherical-geometry notebook from user-guide toctree
rajeeja 64ecad4
o test_plot: drop to_raster_auto_extent, unrelated to AccuXGCA/AccuXC…
rajeeja 793f351
o fix on_minor_arc antipodal false positive
rajeeja f78f5ff
o add on_minor_arc degeneracy regression tests
rajeeja 1d00adb
fix diff_of_products addition grouping to match AccuSphGeom eft.hpp
rajeeja 9d88f83
Merge branch 'main' into rajeeja/accusphere
rajeeja 48831eb
o intersections: collapse pos/neg-valid branches via XOR in gca_const…
rajeeja File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,207 @@ | ||
| """Micro-benchmarks for the EFT-based spherical geometry kernels. | ||
|
|
||
| These benchmarks target the individual functions introduced in the AccuSphGeom | ||
| port (PR #1513) so that the per-kernel overhead of compensated arithmetic can | ||
| be measured independently of higher-level UXarray operations. | ||
|
|
||
| All Numba functions are warmed (compiled) during ``setup`` so that benchmark | ||
| timings reflect steady-state throughput, not JIT compilation. | ||
|
|
||
| The ``uxarray`` kernels are imported inside each ``setup`` (not at module | ||
| import) so that if a symbol is missing in the environment asv built, only the | ||
| affected benchmark errors out rather than aborting collection of every | ||
| benchmark in this directory. | ||
| """ | ||
|
|
||
| import numpy as np | ||
|
|
||
|
|
||
| def _unit(v): | ||
| return v / np.linalg.norm(v) | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Representative inputs — chosen to exercise the near-tangent regime | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| # Two arcs that intersect at a small angle (near-tangent, stress-tests EFT) | ||
| _W0 = _unit(np.array([1.0, 0.0, 0.1])) | ||
| _W1 = _unit(np.array([0.0, 1.0, 0.1])) | ||
| _V0 = _unit(np.array([0.5, -0.1, 0.8])) | ||
| _V1 = _unit(np.array([0.5, 0.9, 0.05])) | ||
|
|
||
| # Arc for const-lat test | ||
| _X1 = _unit(np.array([1.0, 0.0, 0.3])) | ||
| _X2 = _unit(np.array([0.0, 1.0, 0.3])) | ||
| _CONST_Z = 0.3 | ||
|
|
||
|
|
||
| class EFTPrimitives: | ||
| """Benchmark the low-level EFT building blocks: two_sum, two_prod, | ||
| diff_of_products, and acc_sqrt_re.""" | ||
|
|
||
| def setup(self): | ||
| from uxarray.utils.computing import ( | ||
| acc_sqrt_re, | ||
| diff_of_products, | ||
| two_prod, | ||
| two_sum, | ||
| ) | ||
|
|
||
| self.two_sum = two_sum | ||
| self.two_prod = two_prod | ||
| self.diff_of_products = diff_of_products | ||
| self.acc_sqrt_re = acc_sqrt_re | ||
|
|
||
| # Warm Numba | ||
| two_sum(1.0, 1e-16) | ||
| two_prod(1.23456789, 9.87654321) | ||
| diff_of_products(1.0, 2.0, 3.0, 4.0) | ||
| acc_sqrt_re(1.0 - 1e-15) | ||
|
|
||
| def time_two_sum(self): | ||
| self.two_sum(1.23456789012345678, 9.87654321098765432e-16) | ||
|
|
||
| def time_two_prod(self): | ||
| self.two_prod(1.23456789012345678, 9.87654321098765432) | ||
|
|
||
| def time_diff_of_products(self): | ||
| self.diff_of_products(1.23456789, 9.87654321, 1.23456788, 9.87654322) | ||
|
|
||
| def time_acc_sqrt_re(self): | ||
| self.acc_sqrt_re(1.0 - 1e-15) | ||
|
|
||
|
|
||
| class AccucrossKernels: | ||
| """Benchmark the compensated cross-product kernels.""" | ||
|
|
||
| def setup(self): | ||
| from uxarray.utils.computing import accucross, accucross_pair | ||
|
|
||
| self.accucross = accucross | ||
| self.accucross_pair = accucross_pair | ||
|
|
||
| accucross(_W0[0], _W0[1], _W0[2], _W1[0], _W1[1], _W1[2]) | ||
| accucross_pair( | ||
| 1.0, | ||
| 0.0, | ||
| 0.0, | ||
| 0.0, | ||
| 0.0, | ||
| 0.0, | ||
| 0.0, | ||
| 1.0, | ||
| 0.0, | ||
| 0.0, | ||
| 0.0, | ||
| 0.0, | ||
| ) | ||
|
|
||
| def time_accucross(self): | ||
| self.accucross(_W0[0], _W0[1], _W0[2], _W1[0], _W1[1], _W1[2]) | ||
|
|
||
| def time_accucross_pair(self): | ||
| n1x_hi, n1y_hi, n1z_hi, n1x_lo, n1y_lo, n1z_lo = self.accucross( | ||
| _W0[0], _W0[1], _W0[2], _W1[0], _W1[1], _W1[2] | ||
| ) | ||
| n2x_hi, n2y_hi, n2z_hi, n2x_lo, n2y_lo, n2z_lo = self.accucross( | ||
| _V0[0], _V0[1], _V0[2], _V1[0], _V1[1], _V1[2] | ||
| ) | ||
| self.accucross_pair( | ||
| n1x_hi, | ||
| n1y_hi, | ||
| n1z_hi, | ||
| n1x_lo, | ||
| n1y_lo, | ||
| n1z_lo, | ||
| n2x_hi, | ||
| n2y_hi, | ||
| n2z_hi, | ||
| n2x_lo, | ||
| n2y_lo, | ||
| n2z_lo, | ||
| ) | ||
|
|
||
|
|
||
| class OrientPredicates: | ||
| """Benchmark the orient3d and on_minor_arc predicates.""" | ||
|
|
||
| def setup(self): | ||
| from uxarray.grid.arcs import on_minor_arc, orient3d_on_sphere | ||
|
|
||
| self.orient3d_on_sphere = orient3d_on_sphere | ||
| self.on_minor_arc = on_minor_arc | ||
|
|
||
| orient3d_on_sphere(_W0, _W1, _V0) | ||
| on_minor_arc(_V0, _W0, _W1) | ||
|
|
||
| def time_orient3d_on_sphere(self): | ||
| self.orient3d_on_sphere(_W0, _W1, _V0) | ||
|
|
||
| def time_on_minor_arc(self): | ||
| self.on_minor_arc(_V0, _W0, _W1) | ||
|
|
||
|
|
||
| class GCAGCAIntersection: | ||
| """Benchmark all three layers of the GCA-GCA intersection stack.""" | ||
|
|
||
| def setup(self): | ||
| from uxarray.grid.intersections import ( | ||
| _accux_gca, | ||
| _try_gca_gca_intersection, | ||
| gca_gca_intersection, | ||
| ) | ||
|
|
||
| self._accux_gca = _accux_gca | ||
| self._try_gca_gca_intersection = _try_gca_gca_intersection | ||
| self.gca_gca_intersection = gca_gca_intersection | ||
|
|
||
| self.gca_a = np.stack([_W0, _W1]) | ||
| self.gca_b = np.stack([_V0, _V1]) | ||
| _accux_gca(_W0, _W1, _V0, _V1) | ||
| _try_gca_gca_intersection(_W0, _W1, _V0, _V1) | ||
| gca_gca_intersection(self.gca_a, self.gca_b) | ||
|
|
||
| def time_accux_gca_kernel(self): | ||
| """Layer 1: pure numerical kernel.""" | ||
| self._accux_gca(_W0, _W1, _V0, _V1) | ||
|
|
||
| def time_try_gca_gca_intersection(self): | ||
| """Layer 2: batch/status layer.""" | ||
| self._try_gca_gca_intersection(_W0, _W1, _V0, _V1) | ||
|
|
||
| def time_gca_gca_intersection(self): | ||
| """Layer 3: dispatcher (full public API).""" | ||
| self.gca_gca_intersection(self.gca_a, self.gca_b) | ||
|
|
||
|
|
||
| class GCAConstLatIntersection: | ||
| """Benchmark all three layers of the GCA / constant-latitude intersection stack.""" | ||
|
|
||
| def setup(self): | ||
| from uxarray.grid.intersections import ( | ||
| _accux_constlat, | ||
| _try_gca_const_lat_intersection, | ||
| gca_const_lat_intersection, | ||
| ) | ||
|
|
||
| self._accux_constlat = _accux_constlat | ||
| self._try_gca_const_lat_intersection = _try_gca_const_lat_intersection | ||
| self.gca_const_lat_intersection = gca_const_lat_intersection | ||
|
|
||
| self.gca_cart = np.stack([_X1, _X2]) | ||
| _accux_constlat(_X1, _X2, _CONST_Z) | ||
| _try_gca_const_lat_intersection(self.gca_cart, _CONST_Z) | ||
| gca_const_lat_intersection(self.gca_cart, _CONST_Z) | ||
|
|
||
| def time_accux_constlat_kernel(self): | ||
| """Layer 1: pure numerical kernel.""" | ||
| self._accux_constlat(_X1, _X2, _CONST_Z) | ||
|
|
||
| def time_try_gca_const_lat_intersection(self): | ||
| """Layer 2: batch/status layer.""" | ||
| self._try_gca_const_lat_intersection(self.gca_cart, _CONST_Z) | ||
|
|
||
| def time_gca_const_lat_intersection(self): | ||
| """Layer 3: dispatcher (full public API).""" | ||
| self.gca_const_lat_intersection(self.gca_cart, _CONST_Z) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the work.
Before reviewing, I want to share an API-level benchmark setup I recently implemented for the AccuSphGeom package. I think it can serve as a useful reference for structuring this kind of benchmark in either Python or C++.
The benchmark covers both GCA x GCA and GCA x ConstLat intersection APIs. It compares the naive floating-point API against the corresponding AccuX API under scalar, SIMD-vectorized, and OpenMP-parallel execution. I also wrote a detailed README explaining the motivation, scientific reasoning, benchmark design, and expected interpretation of the results.
The README includes a checklist for verifying that the benchmark is moving in the correct direction. I am copying it here for convenience. Once you go through this checklist and verify the current implementation against it, I will proceed with reviewing the changes.
Checklist for reproducing the benchmarks
This checklist is intended to help users reproduce the benchmark on their own machines. This performance test is non-trivial, delicate, and often confusing for users who are not already familiar with SIMD and HPC performance benchmarking. The goal is to make the setup easier to follow, verify, and reproduce. The bold items are the most important sanity checks.
-O3 -march=native -ffp-contract=off -fno-fast-math.accux_*API.double, notEigenPack<1>.AccuXimplementation.AccuXarithmetic, temporarily replace only the implementation body inside theaccux_*API with the same implementation used in the naive floating-point API. Keep the API entry point, return type, and output storage unchanged.accux_*API.AccuXimplementation.DATA_SIZEingca_constLat/run_gca_constlat_SIMDPack_benchmark.shorkDefaultDataSizeingca_constLat/benchmark_gca_constlat_SIMDPack.cppto match your measured saturation size.AccuXAPI performance is also stable at the chosen data size.accux_*APIs show no meaningful computational overhead relative to the naive floating-point APIs.