Skip to content

Commit c425bbf

Browse files
authored
new chebinterp(f::Function, order, lb, ub) method (#30)
1 parent 6e79fb9 commit c425bbf

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ x = chebpoints(order, lb, ub) # an array of `SVector` from [StaticArrays.jl](htt
1717
c = chebinterp(f.(x), lb, ub)
1818
```
1919
and then evaluate the interpolant for a point `y` (a vector)
20-
via `c(y)`.
20+
via `c(y)`. Alternatively, you can combine `chebpoints` and `chebinterp` into a single call:
21+
```jl
22+
c = chebinterp(f, order, lb, ub)
23+
```
24+
which evaluates the given function at Chebyshev points for you.
2125

2226
We also provide a function `chebgradient(c,y)` that returns a tuple `(c(y), ∇c(y))` of
2327
the interpolant and its gradient at a point `y`. (You can also use automatic differentiation, e.g. via the [ForwardDiff.jl package](https://github.com/JuliaDiff/ForwardDiff.jl),
@@ -49,6 +53,7 @@ Here is an example interpolating the (highly oscillatory) 1d function `f(x) = si
4953
f(x) = sin(2x + 3cos(4x))
5054
x = chebpoints(200, 0, 10)
5155
c = chebinterp(f.(x), 0, 10)
56+
# alternatively, c = chebinterp(f, 200, 0, 10)
5257
```
5358
We can then compare the exact function and its interpolant at a set of points:
5459
```jl
@@ -90,6 +95,7 @@ g(x) = sin(x[1] + cos(x[2]))
9095
lb, ub = [1,3], [2, 4] # lower and upper bounds of the domain, respectively
9196
x = chebpoints((10,20), lb, ub)
9297
c = chebinterp(g.(x), lb, ub)
98+
# alternatively, c = chebinterp(g, (10,20), lb, ub)
9399
```
94100
Let's evaluate the interpolant at an arbitrary point `(1.2, 3.4)` and compare it to the exact value:
95101
```jl

src/interp.jl

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ function chebpoint(i::CartesianIndex{N}, order::NTuple{N,Int}, lb::SVector{N}, u
66
@. lb + (1 + cos(T($SVector($Tuple(i))) * π / $SVector(ifelse.(iszero.(order),2,order)))) * (ub - lb) * $(T(0.5))
77
end
88

9-
chebpoints(order::NTuple{N,Int}, lb::SVector{N}, ub::SVector{N}) where {N} =
10-
[chebpoint(i,order,lb,ub) for i in CartesianIndices(map(n -> n==0 ? (1:1) : (0:n), order))]
9+
function chebpoints(order::NTuple{N,Int}, lb::SVector{N}, ub::SVector{N}) where {N}
10+
all((0), order) || throw(ArgumentError("invalid negative order $order"))
11+
return [chebpoint(i,order,lb,ub) for i in CartesianIndices(map(n -> n==0 ? (1:1) : (0:n), order))]
12+
end
1113

1214
"""
1315
chebpoints(order, lb, ub)
@@ -26,13 +28,12 @@ the order in that direction.)
2628
function chebpoints(order, lb, ub)
2729
N = length(order)
2830
N == length(lb) == length(ub) || throw(DimensionMismatch())
29-
all((0), order) || throw(ArgumentError("invalid negative order $order"))
3031
return chebpoints(NTuple{N,Int}(order), SVector{N}(lb), SVector{N}(ub))
3132
end
3233

3334
# return array of scalars in 1d
3435
chebpoints(order::Integer, lb::Real, ub::Real) =
35-
first.(chebpoints(order, SVector(lb), SVector(ub)))
36+
first.(chebpoints((Int(order),), SVector(lb), SVector(ub)))
3637

3738
# O(n log n) method to compute Chebyshev coefficients
3839
function chebcoefs(vals::AbstractArray{<:Number,N}) where {N}
@@ -97,17 +98,24 @@ end
9798

9899
# precision for float(vals), handling arrays of numbers and arrays of arrays of numbers
99100
epsvals(vals) = eps(float(real(eltype(eltype(vals)))))
101+
epsbounds(lb, ub) = eps(float(real(promote_type(eltype(lb), eltype(ub)))))
100102

101103
"""
102104
chebinterp(vals, lb, ub; tol=eps)
105+
chebinterp(f::Function, order, lb, ub; tol=eps)
103106
104107
Given a multidimensional array `vals` of function values (at
105108
points corresponding to the coordinates returned by `chebpoints`),
106109
and arrays `lb` and `ub` of the lower and upper coordinate bounds
107110
of the domain in each direction, returns a Chebyshev interpolation
108111
object (a `ChebPoly`).
109112
110-
This object `c = chebinterp(vals, lb, ub)` can be used to
113+
Alternatively, one can supply a function `f` and an `order` (an integer
114+
or a tuple of integers), and it will call [`chebpoints`](@ref) for you
115+
to obtain the Chebyshev points and then compute `vals` by evaluating
116+
`f` at these points.
117+
118+
This object `c = chebinterp(...)` can be used to
111119
evaluate the interpolating polynomial at a point `x` via
112120
`c(x)`.
113121
@@ -137,3 +145,12 @@ of vectors are painful to construct.)
137145
"""
138146
chebinterp_v1(vals::AbstractArray{T}, lb, ub; tol::Real=epsvals(vals)) where {T<:Number} =
139147
chebinterp(dropdims(reinterpret(SVector{size(vals,1),T}, Array(vals)), dims=1), lb, ub; tol=tol)
148+
149+
chebinterp(f::Function, order::NTuple{N,Int}, lb::SVector{N,<:Real}, ub::SVector{N,<:Real}; tol::Real=epsbounds(lb,ub)) where {N} =
150+
chebinterp(map(f, chebpoints(order, lb, ub)), lb, ub; tol=tol)
151+
152+
chebinterp(f::Function, order::NTuple{N,Int}, lb::AbstractArray{<:Real}, ub::AbstractArray{<:Real}; tol::Real=epsbounds(lb,ub)) where {N} =
153+
chebinterp(f, order, SVector{N}(lb), SVector{N}(ub); tol=tol)
154+
155+
chebinterp(f::Function, order::Integer, lb::Real, ub::Real; tol::Real=epsbounds(lb,ub)) =
156+
chebinterp(x -> f(@inbounds x[1]), (Int(order),), SVector(lb), SVector(ub); tol=tol)

test/runtests.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ Random.seed!(314159) # make chainrules tests deterministic
1414
x = chebpoints(48, lb, ub)
1515
@test eltype(x) == T
1616
interp = chebinterp(f.(x), lb, ub, tol=0)
17+
interp2 = chebinterp(f, 48, lb, ub, tol=0)
1718
@test interp isa FastChebInterp.ChebPoly{1,T,T}
19+
@test interp2.coefs interp.coefs
1820
@test repr("text/plain", interp) == "ChebPoly{1,$T,$T} order (48,) polynomial on [-0.3,0.9]"
1921
@test ndims(interp) == 1
2022
x1 = T(0.2)
21-
@test interp(x1) f(x1)
23+
@test interp(x1) f(x1) interp2(x1)
2224
@test chebgradient(interp, x1) ′ (f(x1), f′(x1))
2325
test_frule(interp, x1, rtol=sqrt(eps(T)), atol=sqrt(eps(T)))
2426
test_rrule(interp, x1, rtol=sqrt(eps(T)), atol=sqrt(eps(T)))
@@ -33,7 +35,9 @@ end
3335
x = chebpoints((48,39), lb, ub)
3436
@test eltype(x) == SVector{2,T}
3537
interp = chebinterp(f.(x), lb, ub)
38+
interp2 = chebinterp(f, (48,39), lb, ub)
3639
@test interp isa FastChebInterp.ChebPoly{2,T,T}
40+
@test interp2.coefs interp.coefs
3741
interp0 = chebinterp(f.(x), lb, ub, tol=0)
3842
@test repr("text/plain", interp0) == "ChebPoly{2,$T,$T} order (48, 39) polynomial on [-0.3,0.9] × [0.1,1.2]"
3943
@test ndims(interp) == 2

0 commit comments

Comments
 (0)