Skip to content

Commit

Permalink
Fix evaluation of some recombined bases with Float32 (#84)
Browse files Browse the repository at this point in the history
* Fix evaluation using natural BCs with Float32

* Add tests

* v0.17.1

* Fix ambiguity
  • Loading branch information
jipolanco authored Feb 19, 2024
1 parent ddc0cab commit a5fbe1d
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "BSplineKit"
uuid = "093aae92-e908-43d7-9660-e50ee39d5a0a"
authors = ["Juan Ignacio Polanco <[email protected]>"]
version = "0.17.0"
version = "0.17.1"

[deps]
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
Expand Down
20 changes: 12 additions & 8 deletions src/Recombinations/matrices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,17 @@ end

# Default element type of recombination matrix.
# In some specific cases we can use Bool...
_default_eltype(::BoundaryCondition) = Float64
_default_eltype(::Derivative{0}) = Bool # Dirichlet BCs
_default_eltype(::Derivative{1}) = Bool # Neumann BCs
_default_eltype(::Vararg{AbstractDifferentialOp}) = Float64
_default_eltype(::Type{T}, ::BoundaryCondition) where {T <: AbstractFloat} = T
_default_eltype(::Type{T}, ::Derivative{0}) where {T <: AbstractFloat} = Bool # Dirichlet BCs
_default_eltype(::Type{T}, ::Derivative{1}) where {T <: AbstractFloat} = Bool # Neumann BCs
_default_eltype(::Type{T}, ::Vararg{AbstractDifferentialOp}) where {T <: AbstractFloat} = T

# Case (D(0), D(1), D(2), ...)
_default_eltype(::Derivative{0}, ::Derivative{1}, ::Vararg{Derivative}) = Bool # TODO this isn't always right, is it?
_default_eltype(ops::DiffOpList) = _default_eltype(ops...)
_default_eltype(
::Type{T}, ::Derivative{0}, ::Derivative{1}, ::Vararg{Derivative},
) where {T <: AbstractFloat} = Bool # TODO this isn't always right, is it?
_default_eltype(::Type{T}, ops::DiffOpList) where {T <: AbstractFloat} =
_default_eltype(T, ops...)

# Same BCs on both sides.
RecombineMatrix(B::BSplineBasis, ops) = RecombineMatrix(B, ops, ops)
Expand All @@ -166,8 +169,9 @@ RecombineMatrix(ops, B::BSplineBasis, args...) = RecombineMatrix(B, ops, args...

# No element type specified.
function RecombineMatrix(B::BSplineBasis, ops_l, ops_r)
Tl = _default_eltype(ops_l)
Tr = _default_eltype(ops_r)
S = eltype(knots(B))
Tl = _default_eltype(S, ops_l)
Tr = _default_eltype(S, ops_r)
T = promote_type(Tl, Tr)
RecombineMatrix(B, ops_l, ops_r, T)
end
Expand Down
27 changes: 22 additions & 5 deletions test/natural.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ using Test

import LinearAlgebra

function test_natural(ord::BSplineOrder)
B = BSplineBasis(ord, -1:0.1:1)
function test_natural(::Type{T}, ord::BSplineOrder) where {T}
ts = range(T(-1), T(1); length = 21)
B = @inferred BSplineBasis(ord, ts)
k = order(B)
@test B isa AbstractBSplineBasis{k, T}

if isodd(k)
# `Natural` boundary condition only supported for even-order splines (got k = $k)
@test_throws ArgumentError RecombinedBSplineBasis(B, Natural())
Expand All @@ -19,9 +22,12 @@ function test_natural(ord::BSplineOrder)
rng = MersenneTwister(42)

R = @inferred RecombinedBSplineBasis(B, Natural())
S = Spline(R, 1 .+ rand(rng, length(R))) # coefficients in [1, 2]
@test R isa AbstractBSplineBasis{k, T}
S = Spline(R, 1 .+ rand(rng, T, length(R))) # coefficients in [1, 2]

M = recombination_matrix(R)
@test M isa AbstractMatrix{T}

if k == 2
# In this case, basis functions are not really recombined, and the
# resulting basis is identical to the original one.
Expand All @@ -31,7 +37,12 @@ function test_natural(ord::BSplineOrder)
for x boundaries(R)
Sx = @inferred S(x)
@test Sx > 1
ϵ = 2e-8 * Sx # threshold
rtol = if T === Float64
2e-8
elseif T === Float32
2f-4
end
ϵ = rtol * Sx # threshold
@test abs((Derivative(1) * S)(x)) > ϵ # different from zero
for n = 2:(k ÷ 2)
Sder = Derivative(n) * S
Expand All @@ -46,6 +57,12 @@ end
# (in this case the recombination is equivalent to the original B-spline basis).
@testset "Natural splines" begin
@testset "k = $k" for k (2, 4, 5, 6, 8)
test_natural(BSplineOrder(k))
test_natural(Float64, BSplineOrder(k))
end
@testset "Float32" begin
# Note: results are very wrong with high-order splines (k ≥ 6) when using Float32.
# The zero derivative condition is not well verified.
test_natural(Float32, BSplineOrder(2))
test_natural(Float32, BSplineOrder(4))
end
end

0 comments on commit a5fbe1d

Please sign in to comment.