Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/Benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ jobs:
- name: Cache Julia artifacts
uses: julia-actions/cache@v2

- name: Add FuseRegistry
run: julia -e 'using Pkg; Pkg.Registry.add(RegistrySpec(url="https://github.com/ProjectTorreyPines/FuseRegistry.jl.git")); Pkg.Registry.add("General"); Pkg.Registry.update()'

- name: Install dependencies
run: julia --project=benchmark -e 'using Pkg; Pkg.instantiate()'

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ jobs:

- uses: julia-actions/cache@v2

- name: Add FuseRegistry
run: julia -e 'using Pkg; Pkg.Registry.add(RegistrySpec(url="https://github.com/ProjectTorreyPines/FuseRegistry.jl.git")); Pkg.Registry.add("General"); Pkg.Registry.update()'

- uses: julia-actions/julia-buildpkg@v1

- uses: julia-actions/julia-runtest@v1
Expand Down
9 changes: 0 additions & 9 deletions .github/workflows/Documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,6 @@ jobs:

- uses: julia-actions/cache@v2

- name: Add FuseRegistry
run: |
rm -rf ~/.julia/registries/FuseRegistry
julia -e 'using Pkg; Pkg.Registry.add(RegistrySpec(url="https://github.com/ProjectTorreyPines/FuseRegistry.jl.git")); Pkg.Registry.add("General"); Pkg.Registry.update()'

- name: Replace git@github.com with https in Package.toml files
run: |
find ~/.julia/registries/FuseRegistry -type f -name 'Package.toml' -exec sed -i 's|git@github.com:|https://project-torrey-pines:${{secrets.PTP_READ_TOKEN}}@github.com/|g' {} +

- name: Install dependencies
run: |
julia --project=docs -e '
Expand Down
7 changes: 0 additions & 7 deletions .github/workflows/UpdateREADME.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,6 @@ jobs:
# ──────────────────────────────────────────────────────────────
# Benchmark Execution
# ──────────────────────────────────────────────────────────────
- name: Add registries
run: |
julia -e 'using Pkg
Pkg.Registry.add(RegistrySpec(url="https://github.com/ProjectTorreyPines/FuseRegistry.jl.git"))
Pkg.Registry.add("General")
Pkg.Registry.update()'

- name: Install benchmark dependencies
run: julia --project=benchmark -e 'using Pkg; Pkg.instantiate()'

Expand Down
16 changes: 8 additions & 8 deletions benchmark/integral_benchmark.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@ function run_integral_bench()
y = @. sin(2pi*x) + 0.3x

println("=== 1D Cubic Integration ===")
itp_cub = cubic_interp(x, y; extrap=:none)
itp_cub = cubic_interp(x, y; extrap=NoExtrap())
print(" full-domain: "); @btime integrate($itp_cub)
print(" sub-range: "); @btime integrate($itp_cub, 0.13, 0.87)

println("\n=== 1D Linear Integration ===")
itp_lin = linear_interp(x, y; extrap=:none)
itp_lin = linear_interp(x, y; extrap=NoExtrap())
print(" full-domain: "); @btime integrate($itp_lin)
print(" sub-range: "); @btime integrate($itp_lin, 0.13, 0.87)

println("\n=== 1D Quadratic Integration ===")
itp_quad = quadratic_interp(x, y; extrap=:none)
itp_quad = quadratic_interp(x, y; extrap=NoExtrap())
print(" full-domain: "); @btime integrate($itp_quad)
print(" sub-range: "); @btime integrate($itp_quad, 0.13, 0.87)

println("\n=== Extrap modes (cubic, sub-range) ===")
itp_const = cubic_interp(x, y; extrap=:constant)
itp_wrap = cubic_interp(x, y; extrap=:wrap)
print(" :none "); @btime integrate($itp_cub, 0.13, 0.87)
print(" :constant "); @btime integrate($itp_const, -0.2, 1.2)
print(" :wrap "); @btime integrate($itp_wrap, -0.2, 2.8)
itp_const = cubic_interp(x, y; extrap=ConstExtrap())
itp_wrap = cubic_interp(x, y; extrap=WrapExtrap())
print(" NoExtrap "); @btime integrate($itp_cub, 0.13, 0.87)
print(" ConstExtrap"); @btime integrate($itp_const, -0.2, 1.2)
print(" WrapExtrap "); @btime integrate($itp_wrap, -0.2, 2.8)

println("\n=== 2D Cubic Integration ===")
xg = collect(range(0.0, 1.0, length=51))
Expand Down
1 change: 0 additions & 1 deletion docs/src/api/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ PolyFit
LinearFit
QuadraticFit
CubicFit
ParabolaFit
```

### Endpoint Wrappers (Quadratic Splines)
Expand Down
2 changes: 1 addition & 1 deletion docs/src/boundary-conditions/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,6 @@ PeriodicBC() # S(x) = S(x + τ) with C² continuity
## See Also

- [PointBC Details](pointbc.md) — In-depth explanation of PolyFit with visualizations
- [PeriodicBC Details](periodicbc.md) — Inclusive vs exclusive endpoints, period inference, comparison with `extrap=:wrap`
- [PeriodicBC Details](periodicbc.md) — Inclusive vs exclusive endpoints, period inference, comparison with `WrapExtrap()`
- [Quadratic Interpolation](../interpolation/quadratic.md) — BC examples in context
- [Cubic Interpolation](../interpolation/cubic.md) — BCPair and PeriodicBC details
6 changes: 3 additions & 3 deletions docs/src/boundary-conditions/periodicbc.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ True periodic boundary conditions for cubic splines, ensuring C² continuity (va

---

## PeriodicBC vs `extrap=:wrap`
## PeriodicBC vs `WrapExtrap()`

These two features sound similar but solve fundamentally different problems:

| | `PeriodicBC()` | `extrap=:wrap` |
| | `PeriodicBC()` | `WrapExtrap()` |
|---|---|---|
| **What it does** | Solves a cyclic tridiagonal system (Sherman-Morrison) so the spline is **C² continuous** at the period boundary | Maps out-of-domain queries back into `[x₁, xₙ]` via modular arithmetic |
| **Smoothness** | ``S, S', S''`` all match at the wrap point | No smoothness guarantee — may have jumps in value, slope, or curvature |
Expand All @@ -17,7 +17,7 @@ These two features sound similar but solve fundamentally different problems:
| **Use case** | Physically periodic signals (angles, phases, Fourier-sampled data) | Quick "repeat" behavior without physical periodicity |

!!! tip "Rule of Thumb"
If your data is truly periodic (e.g., one full period of `sin(x)`), use `PeriodicBC`. If you just want out-of-domain queries to wrap around, use `extrap=:wrap`.
If your data is truly periodic (e.g., one full period of `sin(x)`), use `PeriodicBC`. If you just want out-of-domain queries to wrap around, use `extrap=WrapExtrap()`.

---

Expand Down
78 changes: 43 additions & 35 deletions docs/src/extrapolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,34 @@ Extrapolation controls behavior when query points fall outside the data domain `

## Overview

Use the `extrap` keyword argument to specify extrapolation behavior:
All extrapolation modes are concrete subtypes of [`AbstractExtrap`](@ref). Pass them via the `extrap` keyword argument:

```julia-repl
# One-shot: specify extrap per call
cubic_interp(x, y, xq; extrap=:constant)
linear_interp(x, y, xq; extrap=:extension)
cubic_interp(x, y, xq; extrap=ConstExtrap())
linear_interp(x, y, xq; extrap=ExtendExtrap())

# Interpolant: extrap is fixed at creation
itp = cubic_interp(x, y; extrap=:extension) # all future calls use :extension
itp(xq) # uses :extension
itp = cubic_interp(x, y; extrap=ExtendExtrap()) # all future calls use ExtendExtrap
itp(xq) # uses ExtendExtrap
```

Both `linear_interp` and `cubic_interp` support the same extrapolation modes.
All interpolation methods (`linear_interp`, `quadratic_interp`, `cubic_interp`, `constant_interp`) support the same extrapolation modes.

| Mode | Behavior |
| Type | Behavior |
|------|----------|
| `:none` | Throws `DomainError` (default) |
| `:constant` | Returns boundary values |
| `:extension` | Extends boundary polynomial |
| `:wrap` | Wraps coordinates periodically (no smoothness enforced) |
| [`NoExtrap()`](@ref NoExtrap) | Throws `DomainError` (default) |
| [`ConstExtrap()`](@ref ConstExtrap) | Returns boundary values |
| [`ExtendExtrap()`](@ref ExtendExtrap) | Extends boundary polynomial |
| [`WrapExtrap()`](@ref WrapExtrap) | Wraps coordinates periodically (no smoothness enforced) |

```
AbstractExtrap
├── NoExtrap # DomainError on out-of-domain (default)
├── ConstExtrap # clamp to y[1] / y[end]
├── ExtendExtrap # continue boundary polynomial
└── WrapExtrap # modular coordinate mapping
```

## Examples

Expand All @@ -40,64 +48,64 @@ xq = range(x[1] - 1.5, x[end] + 1.5, 300)
nothing # hide
```

## `extrap=:none` (Default)
## `NoExtrap()` (Default)

Throws `DomainError` for out-of-domain queries. Use when extrapolation is unexpected.

```julia
julia> cubic_interp(x, y, -1.0; extrap=:none) # scalar query outside domain
julia> cubic_interp(x, y, -1.0; extrap=NoExtrap()) # scalar query outside domain
ERROR: DomainError with -1.0:
query point outside interpolation domain [0.0, 6.0]

julia> cubic_interp(x, y, xq; extrap=:none) # vector query (xq includes out-of-domain points)
julia> cubic_interp(x, y, xq; extrap=NoExtrap()) # vector query (xq includes out-of-domain points)
ERROR: DomainError with -1.5:
query point outside interpolation domain [0.0, 6.0]
```

Only interior queries succeed:

```@example extrap
yq = cubic_interp(x, y, range(x[1], x[end], 200); extrap=:none)
yq = cubic_interp(x, y, range(x[1], x[end], 200); extrap=NoExtrap())

plot(title="extrap=:none", xlabel="x", ylabel="y", legend=:topright, xlims=(x[1] - 1.5, x[end] + 1.5)) # hide
plot(title="NoExtrap()", xlabel="x", ylabel="y", legend=:topright, xlims=(x[1] - 1.5, x[end] + 1.5)) # hide
vspan!([x[1] - 1.5, x[1]], alpha=0.1, color=:gray, label="out of domain") # hide
vspan!([x[end], x[end] + 1.5], alpha=0.1, color=:gray, label=nothing) # hide
plot!(range(x[1], x[end], 200), yq, label="spline", linewidth=2) # hide
scatter!(x, y, label="data", markersize=7, color=:black) # hide
vline!([x[1], x[end]], color=:gray, linestyle=:dot, alpha=0.5, label=nothing) # hide
```

## `extrap=:constant`
## `ConstExtrap()`

Returns boundary values: `y[1]` for left, `y[end]` for right.

```@example extrap
yq = cubic_interp(x, y, xq; extrap=:constant)
yq = cubic_interp(x, y, xq; extrap=ConstExtrap())

plot(title="extrap=:constant", xlabel="x", ylabel="y", legend=:topright) # hide
plot(title="ConstExtrap()", xlabel="x", ylabel="y", legend=:topright) # hide
vspan!([x[1] - 1.5, x[1]], alpha=0.1, color=:gray, label="out of domain") # hide
vspan!([x[end], x[end] + 1.5], alpha=0.1, color=:gray, label=nothing) # hide
plot!(xq, yq, label="spline", linewidth=2) # hide
scatter!(x, y, label="data", markersize=7, color=:black) # hide
vline!([x[1], x[end]], color=:gray, linestyle=:dot, alpha=0.5, label=nothing) # hide
```

## `extrap=:extension`
## `ExtendExtrap()`

Extends the boundary polynomial beyond the domain.

```@example extrap
yq = cubic_interp(x, y, xq; extrap=:extension)
yq = cubic_interp(x, y, xq; extrap=ExtendExtrap())

plot(title="extrap=:extension", xlabel="x", ylabel="y", legend=:topright) # hide
plot(title="ExtendExtrap()", xlabel="x", ylabel="y", legend=:topright) # hide
vspan!([x[1] - 1.5, x[1]], alpha=0.1, color=:gray, label="out of domain") # hide
vspan!([x[end], x[end] + 1.5], alpha=0.1, color=:gray, label=nothing) # hide
plot!(xq, yq, label="spline", linewidth=2) # hide
scatter!(x, y, label="data", markersize=7, color=:black) # hide
vline!([x[1], x[end]], color=:gray, linestyle=:dot, alpha=0.5, label=nothing) # hide
```

## `extrap=:wrap`
## `WrapExtrap()`

Wraps coordinates periodically:

Expand All @@ -111,9 +119,9 @@ This is **purely coordinate mapping**—it does not enforce any physical conditi
If you need C² continuity at the periodic boundary, use [`bc=PeriodicBC()`](interpolation/cubic.md) with `cubic_interp`. This enforces ``S(x_1) = S(x_{\text{end}})``, ``S'(x_1) = S'(x_{\text{end}})``, and ``S''(x_1) = S''(x_{\text{end}})``.

```@example extrap
yq = cubic_interp(x, y, xq; extrap=:wrap)
yq = cubic_interp(x, y, xq; extrap=WrapExtrap())

plot(title="extrap=:wrap", xlabel="x", ylabel="y", legend=:topright) # hide
plot(title="WrapExtrap()", xlabel="x", ylabel="y", legend=:topright) # hide
vspan!([x[1] - 1.5, x[1]], alpha=0.1, color=:gray, label="out of domain") # hide
vspan!([x[end], x[end] + 1.5], alpha=0.1, color=:gray, label=nothing) # hide
plot!(xq, yq, label="spline", linewidth=2) # hide
Expand All @@ -125,16 +133,16 @@ vline!([x[1], x[end]], color=:gray, linestyle=:dot, alpha=0.5, label=nothing) #

```@example extrap
# All three modes on same plot
y_const = cubic_interp(x, y, xq; extrap=:constant)
y_ext = cubic_interp(x, y, xq; extrap=:extension)
y_wrap = cubic_interp(x, y, xq; extrap=:wrap)
y_const = cubic_interp(x, y, xq; extrap=ConstExtrap())
y_ext = cubic_interp(x, y, xq; extrap=ExtendExtrap())
y_wrap = cubic_interp(x, y, xq; extrap=WrapExtrap())

plot(title="Extrapolation Comparison", xlabel="x", ylabel="y", legend=:topright, size=(700, 400)) # hide
vspan!([x[1] - 1.5, x[1]], alpha=0.1, color=:gray, label=nothing) # hide
vspan!([x[end], x[end] + 1.5], alpha=0.1, color=:gray, label=nothing) # hide
plot!(xq, y_const, label=":constant", linewidth=2) # hide
plot!(xq, y_ext, label=":extension", linewidth=2, linestyle=:dash) # hide
plot!(xq, y_wrap, label=":wrap", linewidth=2, linestyle=:dashdot) # hide
plot!(xq, y_const, label="ConstExtrap", linewidth=2) # hide
plot!(xq, y_ext, label="ExtendExtrap", linewidth=2, linestyle=:dash) # hide
plot!(xq, y_wrap, label="WrapExtrap", linewidth=2, linestyle=:dashdot) # hide
scatter!(x, y, label="data", markersize=7, color=:black) # hide
vline!([x[1], x[end]], color=:gray, linestyle=:dot, alpha=0.5, label=nothing) # hide
```
Expand All @@ -143,10 +151,10 @@ vline!([x[1], x[end]], color=:gray, linestyle=:dot, alpha=0.5, label=nothing) #

| Mode | Behavior | Use Case |
|------|----------|----------|
| `:none` | `DomainError` | Strict domain enforcement (default) |
| `:constant` | Returns boundary values | Physical constraints |
| `:extension` | Continues boundary polynomial | Smooth continuation |
| `:wrap` | Wraps coordinates (no smoothness) | Cyclic data (see [`PeriodicBC`](interpolation/cubic.md) for C² continuity) |
| `NoExtrap()` | `DomainError` | Strict domain enforcement (default) |
| `ConstExtrap()` | Returns boundary values | Physical constraints |
| `ExtendExtrap()` | Continues boundary polynomial | Smooth continuation |
| `WrapExtrap()` | Wraps coordinates (no smoothness) | Cyclic data (see [`PeriodicBC`](interpolation/cubic.md) for C² continuity) |

## See Also

Expand Down
10 changes: 5 additions & 5 deletions docs/src/guides/autodiff_support.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ using ForwardDiff, FastInterpolations

x = 0.0:0.5:5.0
y = sin.(x)
itp = cubic_interp(x, y; extrap=:extension)
itp = cubic_interp(x, y; extrap=ExtendExtrap())

# Compute derivative via AD
grad = ForwardDiff.derivative(itp, 1.5)
Expand All @@ -57,7 +57,7 @@ nothing #hide
```@example ad
# Series interpolant support
y1, y2 = sin.(x), cos.(x)
sitp = cubic_interp(x, [y1, y2]; extrap=:extension)
sitp = cubic_interp(x, [y1, y2]; extrap=ExtendExtrap())
grad_series = ForwardDiff.derivative(q -> sum(sitp(q)), 1.5)
nothing #hide
```
Expand Down Expand Up @@ -85,7 +85,7 @@ using Zygote, FastInterpolations
# Note: Use collect() to convert range to Vector for Zygote compatibility
x = collect(0.0:0.5:5.0)
y = 2.0 .* x .+ 1.0
itp = linear_interp(x, y; extrap=:extension)
itp = linear_interp(x, y; extrap=ExtendExtrap())

# Single interpolant works
grad = Zygote.gradient(itp, 1.5)[1] # Returns 2.0
Expand All @@ -97,7 +97,7 @@ nothing #hide
- **Complex output**: Must use `real()` or `imag()` wrapper
```@example zygote
y_complex = (2.0 + 1.0im) .* x
itp_complex = linear_interp(x, y_complex; extrap=:extension)
itp_complex = linear_interp(x, y_complex; extrap=ExtendExtrap())

# ❌ Zygote.gradient(itp_complex, 1.5) fails on complex output
# ✅ Use this instead:
Expand All @@ -117,7 +117,7 @@ using Enzyme, FastInterpolations

x = 0.0:0.5:5.0
y = x .^ 2
itp = quadratic_interp(x, y; extrap=:extension)
itp = quadratic_interp(x, y; extrap=ExtendExtrap())

# Use autodiff API
f(xq) = itp(xq)
Expand Down
6 changes: 3 additions & 3 deletions docs/src/guides/optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ xg = range(-0.7, 1.5, length=101)
yg = range(-0.7, 1.5, length=101)
zg = [rosenbrock(xi, yi) for xi in xg, yi in yg]

itp = cubic_interp((xg, yg), zg; extrap=:extension, bc=CubicFit())
itp = cubic_interp((xg, yg), zg; extrap=ExtendExtrap(), bc=CubicFit())
```

!!! tip "Why `extrap=:extension`?"
!!! tip "Why `ExtendExtrap()`?"
Trust-region and line-search methods may evaluate points outside the grid during
intermediate steps. `:extension` extrapolates smoothly from the boundary, preventing
intermediate steps. `ExtendExtrap()` extrapolates smoothly from the boundary, preventing
errors without distorting the objective landscape near the boundary.

## Three Ways to Run Optimization
Expand Down
2 changes: 1 addition & 1 deletion docs/src/interpolation/cubic.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ PeriodicBC(endpoint=:exclusive, period=2π) # any grid → explicit period
PeriodicBC uses the **Sherman-Morrison formula** to solve a cyclic tridiagonal system.
This is fundamentally different from BCPair's standard tridiagonal solver.

👉 See [PeriodicBC Details](../boundary-conditions/periodicbc.md) for endpoint conventions, period inference, and comparison with `extrap=:wrap`.
👉 See [PeriodicBC Details](../boundary-conditions/periodicbc.md) for endpoint conventions, period inference, and comparison with `WrapExtrap()`.

---

Expand Down
4 changes: 2 additions & 2 deletions docs/src/interpolation/derivatives.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ root = find_zero(d1, 0.5) # Find where derivative = 0
- **Extrapolation** settings are inherited by derivatives:

```julia
itp = cubic_interp(x, y; extrap=:extension)
itp = cubic_interp(x, y; extrap=ExtendExtrap())
d1 = deriv1(itp)
d1(-0.5) # Uses :extension extrapolation
d1(-0.5) # Uses ExtendExtrap extrapolation
```

## API Summary
Expand Down
Loading