Skip to content

Commit

Permalink
More performance (#344)
Browse files Browse the repository at this point in the history
* faster `__changebasis_I_old`

* faster `issqsubset`

* update `_find_j_range_*`

* faster `changebasis_I`
  • Loading branch information
hyrodium authored Nov 12, 2023
1 parent db826f7 commit a649f92
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 28 deletions.
23 changes: 8 additions & 15 deletions src/_BSplineSpace.jl
Original file line number Diff line number Diff line change
Expand Up @@ -177,28 +177,21 @@ Check inclusive relationship between B-spline spaces.
```
"""
function issqsubset(P::BSplineSpace{p}, P′::BSplineSpace{p′}) where {p, p′}
# TODO: fix https://github.com/hyrodium/BasicBSpline.jl/issues/329
p₊ = p′ - p
p₊ < 0 && return false

k = knotvector(P)
k′ = knotvector(P′)
!(k[1+p] == k′[1+p′] < k′[end-p′] == k[end-p]) && return false

l = length(k)
l′ = length(k′)
p₊ = p′ - p

if p₊ < 0
return false
elseif domain(P) domain(P′)
# The domains must be equal for P ⊑ P′.
return false
elseif !(k′[1+p′] < k′[end-p′])
# The width(domain(P′)) must be non-zero for P ⊑ P′.
# There may be some edge-case like P′ ⊑ P′, but we don't need `true` for that situation.
return false
end

inner_knotvector = view(k, p+2:l-p-1)
inner_knotvector′ = view(k′, p′+2:l′-p′-1)

return inner_knotvector + p₊ * unique(inner_knotvector) inner_knotvector′
_P = BSplineSpace{p}(inner_knotvector)
_P′ = BSplineSpace{p′}(inner_knotvector′)
return _P _P′
end

const = issqsubset
Expand Down
25 changes: 12 additions & 13 deletions src/_ChangeBasis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ function _changebasis_R(P::BSplineSpace{0,T,KnotVector{T}}, P′::BSplineSpace{p
return A⁰
end

function _find_j_range_R(P::BSplineSpace{p}, P′::BSplineSpace{p′}, i, j_begin, j_end) where {p, p′}
function _find_j_range_R(P::BSplineSpace{p}, P′::BSplineSpace{p′}, i, j_range) where {p, p′}
k = knotvector(P)
k′ = knotvector(P′)
n′ = dim(P′)
Pi = BSplineSpace{p}(view(k, i:i+p+1))
j_begin, j_end = extrema(j_range)

# Find `j_end`. This is the same as:
# j_end::Int = findnext(j->Pi ⊆ BSplineSpace{p′}(view(k′, j_begin:j+p′+1)), 1:n′, j_end)
Expand Down Expand Up @@ -145,8 +146,7 @@ function _changebasis_R(P::BSplineSpace{p,T,KnotVector{T}}, P′::BSplineSpace{p
J = Vector{Int32}(undef, n_nonzero)
V = Vector{U}(undef, n_nonzero)
s = 1
j_begin = 1
j_end = 1
j_range = 1:1 # j_begin:j_end
for i in 1:n
# Skip for degenerated basis
isdegenerate_R(P,i) && continue
Expand Down Expand Up @@ -205,7 +205,7 @@ function _changebasis_R(P::BSplineSpace{p,T,KnotVector{T}}, P′::BSplineSpace{p
=#

# Precalculate the range of j
j_range = _find_j_range_R(P, P′, i, j_begin, j_end)
j_range = _find_j_range_R(P, P′, i, j_range)
j_begin, j_end = extrema(j_range)

# Rule-0: outside of j_range
Expand Down Expand Up @@ -395,20 +395,19 @@ function _changebasis_I_old(P::BSplineSpace{p,T}, P′::BSplineSpace{p′,T′})
return A
end

function __changebasis_I_old(P1, P2)
function __changebasis_I_old(P1::BSplineSpace{p,T}, P2::BSplineSpace{p′,T′}) where {p,p′,T,T′}
U = StaticArrays.arithmetic_closure(promote_type(T, T′))
_P1 = _nondegeneratize_I(P1)
_P2 = _nondegeneratize_I(P2)
A = _changebasis_I_old(P1,P2)
_A = zero(A)
_A = sparse(Int32[], Int32[], U[], dim(P1), dim(P2))
_A[isnondegenerate_I.(P1,1:dim(P1)), isnondegenerate_I.(P2,1:dim(P2))] = _changebasis_I_old(_P1,_P2)
return _A
end

function _find_j_range_I(P::BSplineSpace{p}, P′::BSplineSpace{p′}, i, j_begin, j_end) where {p, p′}
# TODO: avoid `_changebasis_I_old`
function _find_j_range_I(P::BSplineSpace{p}, P′::BSplineSpace{p′}, i, j_range, Aᵖ_old) where {p, p′}
# TODO: remove `Aᵖ_old` argument
# TODO: fix performance https://github.com/hyrodium/BasicBSpline.jl/pull/323#issuecomment-1723216566
# TODO: remove threshold such as 1e-14
Aᵖ_old = __changebasis_I_old(P,P′)
j_begin = findfirst(e->abs(e)>1e-14, Aᵖ_old[i, :])
j_end = findlast(e->abs(e)>1e-14, Aᵖ_old[i, :])
return j_begin:j_end
Expand Down Expand Up @@ -465,8 +464,8 @@ function _changebasis_I(P::BSplineSpace{p,T,<:AbstractKnotVector{T}}, P′::BSpl
V = Vector{U}(undef, n_nonzero)
# R = fill(-1, n_nonzero)
s = 1
j_begin = 1
j_end = 1
j_range = 1:1 # j_begin:j_end
Aᵖ_old = __changebasis_I_old(P, P′)
for i in 1:n
# Skip for degenerated basis
isdegenerate_I(P,i) && continue
Expand Down Expand Up @@ -546,7 +545,7 @@ function _changebasis_I(P::BSplineSpace{p,T,<:AbstractKnotVector{T}}, P′::BSpl
=#

# Precalculate the range of j
j_range = _find_j_range_I(P, P′, i, j_begin, j_end)
j_range = _find_j_range_I(P, P′, i, j_range, Aᵖ_old)
j_begin, j_end = extrema(j_range)

# Rule-0: outside of j_range
Expand Down

0 comments on commit a649f92

Please sign in to comment.