Skip to content

feat: Typed Autocache with Generic Boundary Conditions#3

Merged
mgyoo86 merged 22 commits intomasterfrom
feat/generic_boundary
Dec 27, 2025
Merged

feat: Typed Autocache with Generic Boundary Conditions#3
mgyoo86 merged 22 commits intomasterfrom
feat/generic_boundary

Conversation

@mgyoo86
Copy link
Collaborator

@mgyoo86 mgyoo86 commented Dec 27, 2025

feat: Typed Autocache with Generic Boundary Conditions

Summary

Refactors the caching system to use fully parametric types and introduces a comprehensive boundary condition type hierarchy for type-stable, zero-allocation cubic spline interpolation.

Key Changes

  • BC Type System: New AbstractBC{T} hierarchy with D1, D2, BCPair, NaturalBC, ClampedBC, PeriodicBC
  • Parametric Cache: CacheBank{T,L,R,X} enables compile-time specialization for any BC combination
  • API Separation: 2-arg form (cubic_interp(x,y)CubicInterpolant) moved to dedicated file
  • Performance: unsafe_trunc for ~37% faster interval finding on uniform grids
  • Robustness: Sherman-Morrison denominator check for periodic BC edge cases

Breaking Changes

Before After
bc=:natural bc=NaturalBC()
bc=:periodic bc=PeriodicBC()
bc=(left_val, right_val) bc=BCPair(D1(left), D2(right))

New API Examples

# Typed BC (recommended)
cubic_interp(x, y; bc=NaturalBC())
cubic_interp(x, y; bc=ClampedBC())
cubic_interp(x, y; bc=PeriodicBC())

# Generic derivative BC
cubic_interp(x, y; bc=D1(0.5))                    # slope at both ends
cubic_interp(x, y; bc=BCPair(D1(0.5), D2(0.0)))   # mixed BC

Test Plan

  • All 1,288 existing tests pass
  • Type stability verified with @inferred tests
  • Zero-allocation confirmed for scalar evaluation paths
  • Float32/Float64 coverage complete
  • Mathematical correctness (polynomial reproduction tests)

Stats

  • 21 commits | 18 files | +2,234 / -635 lines

- Add bc_types.jl with AbstractBC, D1, D2, DerivativeBCData types
- Enable asymmetric BC: bc=(D1(slope), D2(curvature))
- Support :clamped symbol mapping to D1(0), D1(0)
- Remove bc::Symbol type restriction from cubic_interp API
- Fix bug where D1/D2 BC was ignored (always using :natural cache)
- Unify natural BC to use DerivativeBCData internally
- Simplify _build_cache with parametric dispatch
- Maintain zero-allocation for :natural/:periodic autocache
- Test BC type construction (D1, D2 with Float64/Float32)
- Verify symbol equivalence (:natural == D2(0), :clamped == D1(0))
- Mathematical correctness: polynomials (linear, quadratic, cubic)
  reproduced exactly within 1e-14 tolerance
- Test CubicSplineCache and CubicInterpolant with D1/D2
- Float32 support and edge cases
- Use rtol/atol constants for consistent tolerance management
- Move bc_types.jl include before utils.jl (fixes UndefVarError: AbstractBC)
- Consolidate _validate_bc functions in utils.jl
- Add :clamped to valid BC symbols
BC API now accepts:
- Symbol: :natural, :clamped, :periodic
- BCPair: BCPair(D1(0.5), D2(1.0))
- PointBC: D1(0.5) → symmetric BCPair
- PeriodicBC: PeriodicBC()

Key changes:
- _normalize_bc returns BCPair instead of Tuple
- Remove Tuple validation from _validate_bc
- Update CubicSplineCache to use BCPair fields
- Rename DerivativeBCData → BCPair
- Add type aliases: NaturalBCPair, ClampedBCPair
- Add PeriodicBC as user-facing type
- Add PointBC as abstract parent of D1, D2

BREAKING CHANGE: bc=(D1(0.5), D2(1.0)) no longer supported
Use: bc=BCPair(D1(0.5), D2(1.0))
- Add `bc::Union{Symbol,AbstractBC}=:natural` to all public API functions
- Standardize comments explaining BC dispatch logic:
  - Periodic: both :periodic symbol and PeriodicBC type
  - Autocache: only for :natural symbol
  - Fresh cache: :clamped, PointBC, BCPair
- Remove redundant PeriodicBC check after normalize (already checked before)
- Update CubicSplineCache constructor with consistent pattern
…BC combination

- Replace manual type aliases with fully parametric CacheEntry{T,L,R,X} and CacheBank{T,L,R,X}
- Add dynamic bank registry via IdDict with lock-free read path
- Enable autocache for all BCPair types, not just :natural
- Add core helper functions (_cubic_interp_bcpair!, _cubic_interp_periodic!, etc.)
- Use BC value override pattern: cache by BC types, apply values at solve time
- Rename stats.size to stats.total_entries for clarity
- Replace lock() do closure with try-finally to avoid closure allocation
- Consolidate _pass1_identity_check + _lookup_or_insert_locked! into single _lookup_or_insert!
- Skip range normalization for already StepRangeLen inputs (from range(a,b,n))
- Verified: cache hit = 0 bytes, cache miss = ~6KB (LU factorization)
- Add helper functions: _is_periodic_bc, _get_cache_and_solve!, _get_cache_and_solve_periodic!
- Simplify 4-arg cubic_interp! using helpers to reduce code duplication
- Create cubic_interpolant.jl with:
  - CubicInterpolant callable methods (scalar, vector, in-place)
  - 2-arg cubic_interp(x, y; bc, extrap, autocache) API
  - _build_interpolant_bcpair and _build_interpolant_periodic helpers
- Update include order in FastInterpolations.jl

Note: Periodic BC has ~2x performance regression vs master (714ns vs 340ns)
that needs investigation in follow-up work.
- Remove Union{Symbol,AbstractBC} from all public APIs
- Remove _symbol_to_bc() and _validate_bc(::Symbol) helpers
- Remove Val(:natural)/Val(:periodic) deprecated autocache API
- Update all tests to use NaturalBC(), ClampedBC(), PeriodicBC()
- Simplify _normalize_bc to handle AbstractBC types only

BREAKING CHANGE: bc=:natural/:periodic/:clamped no longer supported,
use bc=NaturalBC()/PeriodicBC()/ClampedBC() instead
- Add tests for BCPair with dynamically changing derivative values
- Verify cache hit when BC TYPE is same but VALUES differ
- Test scalar, vector, loop accumulation scenarios
- Cover D2-D1, D2-D2, D1-D1 type combinations
- Include Float32 variant
- Reduce type params from {T,C,Y,Z,E} to {T,C} for type stability
- Add ExtrapVal union type for union-splitting optimization
- Replace @_dispatch_extrap macro with _symbol_to_extrap_val helper
- Remove double z copy (pass z_workspace, constructor copies once)
- Constructor now copies y,z to Vector{T} for consistent typing

Benefits:
- 2-arg cubic_interp(x,y) now passes @inferred (type stable)
- All extrap values return same CubicInterpolant type
- 480 bytes saved per CubicInterpolant creation (1632→1152)
- Scalar call remains zero-allocation (~140ns)
- Add test_type_stability.jl with 57 @inferred tests covering:
  - 2-arg form (CubicInterpolant) for all BC types
  - extrap variations returning consistent types
  - BC + extrap combinations
  - Cache-based API type stability
  - Float32 and Range vs Vector inputs
  - linear_interp type stability

- Rename test_cubic_callable.jl → test_cubic_interpolant.jl
- Remove unused NaturalBCPair/ClampedBCPair type aliases
  (replaced by NaturalBC/ClampedBC types)
- Remove unused _eval_with_bc and _cubic_vector_loop! for Nothing BC type
  (BCPair and PeriodicData are the only valid bc_data types now)
- Add coverage tests for get_cubic_cache ClampedBC and PointBC typed APIs
- Add coverage tests for Int Vector/Range fallback paths in autocache
- Add coverage tests for periodic cache self-healing path
- Add BC type conversion and _normalize_bc coverage tests
Improved comment explaining why y and z are always copied:
- Ensures interpolant immutability after construction
- Guarantees identical results for same query
- Prevents silent corruption from external modifications
Replace floor(Int, ...) with unsafe_trunc(Int, ...) in
_find_interval_with_bounds for Range grids. This is safe because:
1. xi is validated by _check_domain before reaching this point
2. clamp handles edge cases from floating point arithmetic

Benchmark results:
- _find_interval_with_bounds: 5.5 ns → 3.5 ns (37% faster)
Document caller requirements for _find_interval_with_bounds:
- xi must be validated by _check_domain
- step(x) > 0 (ascending grid)
- NaN/Inf is undefined behavior (caller's responsibility)

Move safety rationale from inline comment to docstring.
…tion

Remove explicit extrapolation=Extension to use default (none) for
fair comparison with FastInterpolations defaults.
Prevent division by zero if (1 + v'q) ≈ 0, which would indicate a
degenerate periodic grid. Provides detailed error message with
diagnostic values (denom, α, q[1], q[n]) for debugging.

Performance impact: negligible (~0.3ns, branch prediction success).
- cubic_autocache.jl: Document why registry uses Any type, add
  non-atomic statistics warning, explain zero BC placeholders with
  reference to 3-arg _solve_system! call site
- cubic_interpolant.jl: Add warning about get_cubic_cache returning
  placeholder zeros in bc_data
- cubic_solver.jl: Document 2-arg vs 3-arg _solve_system! overloads
  and their respective use cases (manual cache vs autocache)
- Rename get_cubic_cache → _get_cubic_cache (internal API)
- Remove 2-arg _solve_system!(cache, y) convenience dispatch for BCPair
- All callers now explicitly pass bc_data: _solve_system!(cache, y, cache.bc_data)
- Add 3-arg PeriodicData version (ignores bc_data, for API consistency)
- Pass BCPair directly instead of tuple unpacking/repacking
- Store actual BC values on cache miss (not placeholder zeros)

This makes the solver API explicit and consistent, simplifying future refactoring.
@codecov
Copy link

codecov bot commented Dec 27, 2025

Codecov Report

❌ Patch coverage is 99.14286% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.97%. Comparing base (4648113) to head (ca6c1ab).
⚠️ Report is 23 commits behind head on master.

Files with missing lines Patch % Lines
src/cubic_autocache.jl 99.14% 1 Missing ⚠️
src/cubic_interpolant.jl 98.41% 1 Missing ⚠️
src/cubic_solver.jl 98.36% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master       #3      +/-   ##
==========================================
+ Coverage   98.72%   98.97%   +0.25%     
==========================================
  Files           8       10       +2     
  Lines         627      781     +154     
==========================================
+ Hits          619      773     +154     
  Misses          8        8              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the cubic spline interpolation system from symbol-based boundary conditions (:natural, :periodic) to a fully parametric type hierarchy with generic boundary conditions. The refactor introduces compile-time BC type specialization while maintaining BC value flexibility at runtime.

Key Changes:

  • New AbstractBC{T} type hierarchy with D1, D2, BCPair, NaturalBC, ClampedBC, and PeriodicBC types
  • Parametric CacheBank{T,L,R,X} structure enabling cache sharing based on BC types (not values)
  • Separated 2-arg API into dedicated cubic_interpolant.jl file
  • Performance: unsafe_trunc for ~37% faster uniform grid lookups, Sherman-Morrison robustness check
  • Type-stable interpolant construction with immutable y/z copies

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/bc_types.jl New BC type hierarchy with normalization and promotion helpers
src/cubic_autocache.jl Refactored to parametric cache banks with dynamic registry
src/cubic_solver.jl Generic BC solvers using type dispatch for D1/D2 combinations
src/cubic_interp.jl Unified 4-arg API with BC normalization and core implementations
src/cubic_interpolant.jl Separated 2-arg API and CubicInterpolant callable methods
src/cubic_types.jl Simplified CubicInterpolant type parameters, ExtrapVal union
src/cubic_eval.jl BC-aware dispatch methods for evaluation
src/utils.jl Performance: unsafe_trunc, BC validation for new types
src/FastInterpolations.jl Updated exports for BC types
test/*.jl Comprehensive updates from symbol-based to typed BC API
Comments suppressed due to low confidence (1)

test/test_cubic_interpolant.jl:18

  • The test assertion has changed from === (identity check) to == (equality check) to accommodate the fact that CubicInterpolant now always copies the y vector in its constructor for immutability.

While the comment explains this is intentional, this represents a subtle behavior change: the interpolant no longer shares memory with the input y vector. This could impact performance for large arrays and breaks user expectations if they were relying on shared memory (though that would be poor practice).

Consider documenting this behavior change in the PR description's breaking changes section if not already included.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Change denominator check from eps(T) to sqrt(eps(T)) for better
  numerical stability (~1e-8 for Float64 vs ~2e-16)
- Catches near-degenerate cases before precision loss occurs
- Add tolerance value to error message for debugging
- Remove get_cubic_cache from public exports (internal API only)
@mgyoo86 mgyoo86 merged commit 27c01a6 into master Dec 27, 2025
8 checks passed
@mgyoo86 mgyoo86 deleted the feat/generic_boundary branch January 16, 2026 03:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant