Skip to content

Commit a2e43fd

Browse files
committed
Merge remote-tracking branch 'origin/main' into issue-3631
2 parents ca1b4ab + 59ef9d2 commit a2e43fd

17 files changed

Lines changed: 375 additions & 54 deletions

.claude/sweep-accuracy-state.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module,last_inspected,issue,severity_max,categories_found,notes
22
aspect,2026-06-02,2827,MEDIUM,5,"Cat5 backend divergence: planar cupy _gpu snapped aspect>359.999 to 0 (no such clamp in numpy _cpu, whose range is [0,360) and never reaches 360), so cupy/dask+cupy disagreed with numpy by ~360 on near-degenerate gradients (gx~0+, gy>0). Removing the clamp exposed a 2nd divergence: GPU used coarse 57.29578 vs numpy 180/pi, flipping the >90 compass branch and yielding exact 360 vs 0 on uint32/uint64 random data. Fix #2827/PR #2833: GPU reuses RADIAN and wraps >=360 back to [0,360). Cats 1-4 clean; geodesic path canonicalizes consistently on CPU+GPU and was left untouched. CUDA available; cupy+dask+cupy verified (235 tests pass, numpy-vs-cupy max abs diff 0 over 360 rasters). Dedup: prior aspect fixes #2780 (cellsize)/#2774 (dask mem guard)/#2781 (oracle) all merged and unrelated. Note: PR review COMMENT could not be posted to GitHub (auto-mode permission denial); findings recorded in PR run instead."
33
balanced_allocation,2026-04-14T12:00:00Z,1203,,,float32 allocation array caused source ID mismatch for non-integer IDs. Fix in PR #1205.
4-
bilateral,2026-05-01,,,,"No CRIT/HIGH/MEDIUM. Sigma underflow validated via sqrt(tiny) bound; oversize sigma clamped. float64 throughout numpy/cupy. NaN center returns NaN; NaN neighbors skipped (denom not incremented). w_sum>0 guard avoids div-by-zero. map_overlap depth==kernel radius. CUDA bounds correct. Inf input could yield 0*inf=NaN in v_sum but unvalidated input is general xrspatial pattern, not bilateral-specific."
4+
bilateral,2026-07-03,#3625,HIGH,backend-inconsistency,"HIGH fixed: cupy backend silently ignored boundary param (nearest/reflect/wrap behaved as nan), diverging from numpy/dask+numpy/dask+cupy; issue #3625, fixed via _bilateral_cupy_boundary wrapper + GPU boundary parity test. assert_boundary_mode_correctness only covers numpy vs dask+numpy (coverage gap noted for test-coverage sweep). Reference checks: matches textbook Tomasi-Manduchi brute force to 3e-16 and scipy gaussian_filter Gaussian limit to 6e-13; skimage delta traces to its asymmetric spatial LUT window convention, not a divergence. Invariants (constant passthrough, rot90, convex hull) pass. LOW: docstring claims same dtype but float32 input gives float64 output (all backends). LOW: Inf-input divergence numpy(NaN) vs cupy(inf) at inf pixel, unvalidated-input pattern per previous audit."
55
contour,2026-05-01,,,,"Marching squares correct: NaN check uses self-inequality, loop bounds (ny-1,nx-1) cover all quads, dask overlap depth=1 matches 2x2 stencil, float64 cast consistent across backends, saddle disambiguation via center value. No CRIT/HIGH issues; minor LOW (Inf inputs not specifically rejected) not flagged."
66
corridor,2026-05-01,,LOW,1,"LOW: corridor inherits float32 from cost_distance; for very large accumulated costs, normalized = corridor - corridor_min loses precision near min (intrinsic to upstream dtype, not corridor itself). NaN handling correct (skipna min, np.isfinite check before normalize). All 4 backends route through pure xarray arithmetic; threshold uses dask/cupy/numpy where with try/except dispatch. No CRIT/HIGH issues."
77
cost_distance,2026-06-16,3369,CRITICAL,5,"CRITICAL heap overflow (#3369/this PR): numba Dijkstra kernels _cost_distance_kernel + _cost_distance_tile_kernel sized the binary min-heap at height*width, but a lazy-deletion heap pushes a pixel on every improving relaxation, so push count exceeds h*w on non-uniform friction. _heap_push then writes OOB -> heap corruption, SIGABRT (exit 134, 'corrupted size vs prev_size') on iterative dask path; UB on numpy path. Reference heapq Dijkstra hits 44 pushes on a 6x6=36 grid. Fix: max_heap = h*w*(n_neighbors+1), tile kernel adds +2*(w+h)+4 for phase-2 boundary seeds. Verified: cupy relax kernel (parallel Bellman-Ford) does NOT use this heap, GPU path unaffected. CUDA available; numpy/cupy/dask+numpy/dask+cupy all agree post-fix over 30+40 random adversarial grids; 88 module tests pass (4 new regression tests). Cats 1-4 clean: dist float64 / out float32 fine; inf/nan/zero friction all impassable (tested); bounds guards use >=h/>=w; planar algorithm, no curvature expected. Supersedes prior #1191 (cupy max_iterations h+w->h*w, fixed PR #1192)."

.claude/sweep-api-consistency-state.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module,last_inspected,issue,severity_max,categories_found,notes
2+
bump,2026-07-02,3608,MEDIUM,1;2;3;5,"Sweep 2026-07-02 (deep-sweep-api-consistency-bump-2026-07-02). 3 MEDIUM, 1 LOW. Compared bump() against sibling synth generators perlin/worley/generate_terrain (all: agg first-positional, name= param, output named, attrs preserved). FIXED (issue #3608, PR #3610): (Cat 2/5) bump returned an unnamed DataArray with no name= param while all 3 siblings name their output; added keyword-only name='bump' set on both agg and width/height return paths. Bundled (Cat 3) count docstring said required int but signature is Optional[int]=None with width*height//10 default (capped 10M) -- corrected. Added test_bump_names_output_like_siblings; test_bump.py 19 pass incl cupy+dask+cupy (GPU host). DOCUMENTED not fixed (breaking): (Cat 1 MEDIUM) agg is keyword-only/last in bump but positional-first in siblings, so bump(some_dataarray) raises TypeError where perlin(some_dataarray) works; moving agg positional would break the historical bump(width,height) signature -- needs its own deprecation plan, not a clean shim. (Cat 3 LOW) height_func has no type hint and a freeform docstring type line; left alone per LOW=document-only. Not findings: Cat 4 no default drift within bump; Cat 5 bump is re-exported in __init__.py (no __all__ but matches module convention). attrs=dict(res=1) left untouched (metadata-sweep concern, and documented in the plot example)."
23
classify,2026-06-20,3398,MEDIUM,1;3,"Sweep 2026-06-20 (deep-sweep-api-consistency-classify-2026-06-20). 1 MEDIUM Cat 1 finding filed as #3398 and fixed on this branch. (MEDIUM Cat 1 positional-order drift) natural_breaks ordered its params (agg, num_sample, name, k) while the other two classifiers that take the same trio order them (agg, k, num_sample, name): quantile(agg, k=4, num_sample, name), maximum_breaks(agg, k=5, num_sample, name). So natural_breaks(raster, 5) silently set num_sample=5 instead of k=5. Fix reorders natural_breaks to (agg, k=5, num_sample=20000, name) and adds a _natural_breaks_legacy_order shim: when k= is a keyword AND a second positional is present (the only way pre-1.0 callers passed num_sample, since k was last and always keyword), the positional is treated as the old num_sample with a DeprecationWarning. Keeps the one example notebook call natural_breaks(raster, 20000, k=4) working. Bundled trivial Cat 3 fix in same PR: binary() was the only public classifier with no type hints -- added agg: xr.DataArray, name: Optional[str], -> xr.DataArray to match the other 9. Tests: test_natural_breaks_positional_k_matches_siblings (new positional k == keyword k) and test_natural_breaks_legacy_positional_num_sample_warns (legacy order warns + maps identically). Full test_classify.py (now 91) + test_validation.py pass. Cat 4 considered NOT a finding: quantile k=4 (quartiles) vs k=5 (quintiles) elsewhere is the documented PySAL/mapclassify convention, not drift. No Cat 2 return drift (all 10 publics return xr.DataArray/Dataset via @supports_dataset, coords/dims/attrs preserved). No Cat 5 orphan API (all 10 re-exported in __init__.py; no __all__ but consistent with module convention). Cross-cutting, notes only: first-arg agg (classify family) vs raster (reproject/rasterize/polygonize) is library-wide drift, out of per-module scope. cuda-validated: CUDA_AVAILABLE=True on this host; natural_breaks new order + legacy shim smoke-tested on numpy AND cupy entry points (both warn + remap), dataset path binds name correctly, binary verified on cupy."
34
focal,2026-06-10,3215;3216,MEDIUM,3;4,"Sweep 2026-06-10 (deep-sweep-api-consistency-focal-2026-06-10). 2 MEDIUM findings filed, fixed on branches -01/-02 off this one. (#3215, MEDIUM Cat 4 cross-backend default parity, branch -01) apply() default func=_calc_mean is an @ngjit CPU function but the cupy/dask+cupy paths launch func as a CUDA kernel via _focal_stats_func_cupy func[griddim, blockdim], so apply(cupy_agg, kernel) raises TypeError 'CPUDispatcher' object is not subscriptable (dask+cupy builds the graph and fails at compute). Prior 2026-05-29 sweep dispositioned this LOW as 'documented in the docstring', but the docstring covers explicit funcs -- the default itself is unusable on 2 of 4 backends. Fix: func=None sentinel resolved per backend (_calc_mean CPU, _focal_mean_cuda GPU), explicit-func behavior unchanged; same PR adds the missing name= param to the apply() docstring (signature has name='focal_apply'; mean/focal_stats/hotspots document theirs). (#3216, MEDIUM Cat 3, branch -02) hotspots() docstring lists 3 backends but dask_cupy_func=_hotspots_dask_cupy is dispatched and works; kernel param documented as binary ('values of 1 indicate the kernel') while hotspots accepts weighted kernels and the Gi* formula in the same docstring uses weights w_ij (apply/focal_stats reject non-binary via _validate_binary_kernel, hotspots deliberately does not). Docs-only fix. LOW documented, not fixed: among the 4 focal publics only mean() has @supports_dataset (Dataset-support drift; feature gap, not an API bug). Cross-cutting, notes only per template: emerging_hotspots(raster=), viewshed(raster=), calc_cellsize(raster) still use raster while focal standardized on agg with a DeprecationWarning shim (#2689/PR #2699); library-wide first-arg drift, belongs to those modules' sweeps. No Cat 1 in-module (agg canonical, raster alias warns, both-args raises). No Cat 2 return drift (mean/apply/hotspots 2D same-type, focal_stats 3D (stats,y,x) as documented). No Cat 5 orphan API (apply/focal_stats/hotspots documented in focal.rst autosummary and consumed via xrspatial.focal module path; only mean re-exported top-level; emerging_hotspots top-level vs hotspots module-level asymmetry noted, additive export would be a design call, not filed). cuda-validated: CUDA_AVAILABLE=True on this host; mean/apply/focal_stats/hotspots smoke-tested on cupy with kwarg parity; the apply default crash reproduced on GPU; hotspots weighted-kernel acceptance verified empirically."
45
geotiff,2026-07-02,3593,MEDIUM,3,"Re-sweep 2026-07-02 (deep-sweep-api-consistency-geotiff-2026-07-02); prior pass 2026-07-01 (#3593 -> merged c2bf13c0/#3596). No new API-consistency issues; prior findings re-confirmed. Public surface unchanged since prior pass: commits since 2026-07-01 touch geotiff (#3595/#3599 PAM overwrite, #3592/#3598 PAM docs, #3600 color_ramp streaming stats, #3588 isort, #3601/#3602 removed unused analytics module) but none alter open_geotiff (28 params) / to_geotiff (24 params) signatures. Verified this pass: 222 signature+contract tests pass (geotiff/tests/unit/test_signatures.py + parity/test_signature_contract.py, which enforces read-entry docstring/param parity and the release-contract tier table incl. the #3593 extra_tags[experimental] fix); all 3 deprecated read aliases emit DeprecationWarning empirically (name->default_name, mask_nodata->masked, mask_and_scale->unpack); xarray backend declares open_dataset_parameters=('filename_or_obj','drop_variables') and raises pointed ValueErrors for like=/bare coregister/auto_reproject. Cross-cutting, notes only (offender out of geotiff scope, not filed): reproject/merge use chunk_size= where geotiff/templates use chunks= (dask/xarray convention); open_geotiff kept name= as a deprecated alias for default_name= (rioxarray parity). cuda-validated: CUDA_AVAILABLE=True; cupy write round-trip + all 4 read paths (eager/gpu/dask/dask+gpu) accept identical public kwargs (masked=/gpu=/chunks=), no backend signature drift."

.claude/sweep-documentation-state.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
module,last_inspected,issue,severity_max,categories_found,notes,doc_coverage
2+
bump,2026-07-02,#3606,MEDIUM,2;5,"height_func Parameters description gave a per-point f(x,y) contract; actual call is height_func(locations) with a (count,2) array returning a length-count heights array (TypeError on literal use). count typed int with no default; actually Optional, defaults to min(w*h//10, 10_000_000). Docstring plot example executes clean (numpy). Fixed both in PR. LOW-only: no Raises section for MemoryError/ValueError (left documented). CUDA available; cupy/dask+cupy example paths n/a (single .. plot:: is numpy).",1/1
23
classify,2026-06-25,3506,MEDIUM,1;3,"Cat3: reclassify (numpy/dask/cupy blocks) + equal_interval example outputs were stale/wrong, binary used np.nan in array repr; corrected to actual output (tests confirm code is correct). Cat1: added missing Examples to std_mean, head_tail_breaks, percentiles, maximum_breaks, box_plot. Fixed in deep-sweep-documentation-classify-2026-06-25 (PR for #3506). Cat2 natural_breaks num_sample-None omission already tracked in #3501 (left alone). All 10 public funcs listed in reference/classification.rst (no Cat4 gap). CUDA available: ran numpy examples; cupy/dask reprs reviewed statically.",10/10
4+
convolution,2026-07-02,,MEDIUM,1;3;5,"Deep-sweep 2026-07-02 (deep-sweep-documentation-convolution). Public API per reference/focal.rst 'Focal Statistics': convolution_2d, annulus_kernel, calc_cellsize, circle_kernel, custom_kernel (via xrspatial.focal); convolve_2d listed in metadata public_funcs but not in any reference page. Cat1 MEDIUM: custom_kernel had a one-line stub (added Parameters/Returns/Examples); convolve_2d had NO docstring (added numpydoc). Cat5 MEDIUM: convolution_2d agg backend line omitted Dask+CuPy (code dispatches _convolve_2d_dask_cupy) and had typos 'to processed'/'CuPybacked' -> fixed to name all 4 backends. Cat3 MEDIUM: annulus_kernel example print used comma-array format + stray trailing ')' (real print output has no commas/paren) -> corrected to measured output; convolution_2d dask .compute() example claimed dtype=float32 but np.ones input stays float64 -> removed label; cupy type repr 'cupy.core.core.ndarray' -> 'cupy.ndarray'. Cat3 LOW (fixed in review follow-up): calc_cellsize km example wrote output line as '>>> (1000.0, 1000.0)' (stray prompt on an output line) -> removed the prompt to match the other two output lines in the same example. Cat2/Cat4 clean: params match signatures; all 5 reference-listed funcs present, no stale/dup entries. Verified: numpy+dask+cupy examples executed (CUDA available on host, cupy example ran), values match; test_convolution.py 6 pass; flake8 unchanged (1 pre-existing F401 not_implemented_func baseline).",6/6
35
fire,2026-06-25,,MEDIUM,1;5,"all 7 public funcs (dnbr, rdnbr, burn_severity_class, fireline_intensity, flame_length, rate_of_spread, kbdi) lacked Examples section (Cat1 MEDIUM) and backend-support note (Cat5 MEDIUM); fixed in deep-sweep-documentation-fire-2026-06-25-01; repo issues disabled so no issue number; examples run and outputs match numpy backend; all 7 listed in reference/fire.rst; no Cat2/Cat3/Cat4 issues",7/7
46
flood,2026-06-25,,HIGH,1;4;5,"Cat4 HIGH: vegetation_roughness, vegetation_curve_number, flood_depth_vegetation public but absent from reference/flood.rst; Cat1 MEDIUM: no Examples on any of 7 public funcs; Cat5 MEDIUM: backend support undocumented (all 4 backends) + NaN propagation undocumented for curve_number_runoff/travel_time. Fixed in deep-sweep-documentation-flood-2026-06-25: added 3 rst entries, Examples+backend Notes to all 7 funcs (examples executed OK on CUDA host), NaN notes. PR #3502 opened with the fix; gh issue create blocked by auto-mode classifier so no issue number.",7/7
57
geotiff,2026-07-02,,LOW,5,"Re-audit 2026-07-02 (deep-sweep-documentation-geotiff): prior #3592 PAM-sidecar doc fix confirmed merged (HEAD 8ecf14d9). Public surface = open_geotiff, to_geotiff (both re-exported at top level). Cat1 clean: both have full numpydoc Parameters/Returns/Notes/Examples (to_geotiff also Raises); module __doc__ present. Cat2 clean: programmatic inspect.signature vs Parameters diff = 0 for both (28/28 open_geotiff, 24/24 to_geotiff params; no phantom params, no default drift -- sentinel defaults for deprecated aliases documented as such). Cat3 clean: doctest examples are copy-paste-safe +SKIP blocks, pytest --doctest-modules on __init__.py + eager.py = 2 passed 0 fail; rst examples are non-executed code-block:: python. Cat4 clean: both funcs in reference/geotiff.rst autosummary, no dupes/stale entries; SUPPORTED_FEATURES + all referenced error classes (MixedBandMetadataError, PixelSafetyLimitError, CloudSizeLimitError, VRTStableSourcesOnlyError) import OK. Cat5 clean: Returns backend claim (NumPy/Dask/CuPy/Dask+CuPy) matches gpu/chunks dispatch. LOW (not fixed, no /rockout per MEDIUM+ gate): 8 docstrings incl. both public funcs cite docs/source/reference/geotiff_release_contract.rst but the page exists only as .md; open_geotiff hedges 'once that page lands' (intentional fwd-ref), to_geotiff/_writers/_backends cite it plainly. Literal code-span, not a Sphinx xref, so no build break. CUDA available on host.",2/2
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
module,last_inspected,issue,severity_max,categories_found,notes
2-
geotiff,2026-07-02,3604,MEDIUM,2;4,"to_geotiff 0D/1D DataArray raised opaque IndexError from _coords.py coords_to_transform (dims[-2]) instead of clean 'Expected 2D or 3D' ValueError; numpy path + 4D DataArray already clean. Fixed via early ndim guard before dispatch (eager/vrt/gpu) + 3 tests; PR #3604. Read-side param validation + typed-error hierarchy + allow_rotated/allow_invalid_nodata VRT+chunked opt-in threading verified clean (CUDA available, GPU paths run). gh issue create blocked by auto-mode; PR opened. Cat 2+4."
1+
module,last_inspected,issue,severity_max,categories_found,notes
2+
bump,2026-07-02,,HIGH,1;2;3;4,"bump() agg template unvalidated: plain ndarray -> ArrayTypeFunctionMapping 'Unsupported Array Type'; 3D/1D DataArray -> 'too many values to unpack'. count/spread unvalidated (internal numpy errors / silent). Added _validate_raster(agg,ndim=2) + _validate_scalar for count,spread. all 4 backends verified (CUDA present)."
3+
geotiff,2026-07-02,3604,MEDIUM,2;4,"to_geotiff 0D/1D DataArray raised opaque IndexError from _coords.py coords_to_transform (dims[-2]) instead of clean 'Expected 2D or 3D' ValueError; numpy path + 4D DataArray already clean. Fixed via early ndim guard before dispatch (eager/vrt/gpu) + 3 tests; PR #3604. Read-side param validation + typed-error hierarchy + allow_rotated/allow_invalid_nodata VRT+chunked opt-in threading verified clean (CUDA available, GPU paths run). gh issue create blocked by auto-mode; PR opened. Cat 2+4."

0 commit comments

Comments
 (0)