Skip to content

Commit

Permalink
Merge pull request #43 from tpapp/tp/reorganize-transformation-api
Browse files Browse the repository at this point in the history
Reorganize transformation API.
  • Loading branch information
tpapp committed Oct 24, 2023
2 parents f2c0e12 + ad889b6 commit bcfda39
Show file tree
Hide file tree
Showing 18 changed files with 382 additions and 128 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
fail-fast: false
matrix:
version:
- '1.6' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
- '1.9' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
- '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia.
os:
- ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
/docs/build
/docs/Manifest.toml
/test/coverage/Manifest.toml
/test/Manifest.toml
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "SpectralKit"
uuid = "5c252ae7-b5b6-46ab-a016-b0e3d78320b7"
authors = ["Tamas K. Papp <tkpapp@gmail.com>"]
version = "0.11.2"
version = "0.12.0"

[deps]
ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197"
Expand All @@ -14,7 +14,7 @@ ArgCheck = "1, 2"
DocStringExtensions = "0.8, 0.9"
SimpleUnPack = "1"
StaticArrays = "1"
julia = "1.2"
julia = "1.9"

[extras]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
Expand Down
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
Documenter = "~0.27"
Documenter = "1"
1 change: 0 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ makedocs(
authors = "Tamas K. Papp",
sitename = "SpectralKit.jl",
pages = Any["index.md"],
strict = true,
clean = true,
checkdocs = :exports,
)
Expand Down
37 changes: 29 additions & 8 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ basis = smolyak_basis(Chebyshev, InteriorGrid2(), SmolyakParameters(3), 2)
x = grid(basis)
θ = collocation_matrix(basis) \ f2.(from_pm1.(ct, x)) # find the coefficients
z = (0.5, 0.7) # evaluate at this point
isapprox(f2(z), linear_combination(basis, θ, to_pm1(ct, z)), rtol = 0.005)
isapprox(f2(z), (linear_combination(basis, θ) ∘ ct)(z), rtol = 0.005)
```

Note how the transformation can be combined with `` to a callable that evaluates a transformed linear combination at `z`.

## Constructing bases

### Grid specifications
Expand All @@ -73,13 +75,29 @@ InteriorGrid
InteriorGrid2
```

### Univariate and multivariate transformations
### Domains and transformations

Bases are defined on the domain ``[-1, 1]`` or ``[-1, 1]^n``. *Transformations* map other uni- and multivariate sets into these domains.
A transformation maps values between a *domain*, usually specified by
the basis, and the (co)domain that is specified by a transformation.
Transformations are not required to be subtypes of anything, but need
to support

```@docs
transform_to
transform_from
domain
```

In most cases you do not need to specify a domain directly: transformations specify their domains (eg from ``(0, ∞)``), and the codomain is determined by a basis. However, the following can be used to construct and query some concrete domains.

```@docs
domain_kind
coordinate_domains
```

Bases are defined on a *canonical domain*, such as ``[-1, 1]`` for Chebyshev polynomials. *Transformations* map other uni- and multivariate sets into these domains.

```@docs
to_pm1
from_pm1
BoundedLinear
InfRational
SemiInfRational
Expand Down Expand Up @@ -110,9 +128,10 @@ smolyak_basis
```@docs
is_function_basis
dimension
domain
```

See also [`domain`](@ref).

### Evaluation

```@docs
Expand Down Expand Up @@ -149,10 +168,12 @@ derivatives

This section of the documentation is probably only relevant to contributors and others who want to understand the internals.

### Simplified API for adding custom transformations
### Type hierarchies

Generally, the abstract types below are not part of the exposed API, and new types don't have to subtype them (unless they want to rely on the existing convenience methods). They are merely for code organization.

```@docs
SpectralKit.UnivariateTransformation
SpectralKit.AbstractUnivariateDomain
```

### Grid internals
Expand Down
3 changes: 2 additions & 1 deletion src/SpectralKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ using SimpleUnPack: @unpack

include("utilities.jl")
include("derivatives.jl")
include("domains.jl")
include("transformations.jl")
include("generic_api.jl")
include("chebyshev.jl")
include("smolyak_traversal.jl")
include("smolyak_api.jl")
include("transformations.jl")

end # module
6 changes: 3 additions & 3 deletions src/chebyshev.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ $(TYPEDEF)
The first `N` Chebyhev polynomials of the first kind, defined on `[-1,1]`.
"""
struct Chebyshev{K<:AbstractGrid} <: FunctionBasis
struct Chebyshev{K<:AbstractGrid} <: UnivariateBasis
"Grid specification."
grid_kind::K
"The number of basis functions."
Expand All @@ -33,7 +33,7 @@ end

@inline dimension(basis::Chebyshev) = basis.N

@inline domain(::Chebyshev) = (-1, 1)
@inline domain(::Chebyshev) = PM1()

function Base.show(io::IO, chebyshev::Chebyshev)
@unpack grid_kind, N = chebyshev
Expand Down Expand Up @@ -88,7 +88,7 @@ The actual type can be broadened as required. Methods are type stable.
function gridpoint(::Type{T}, basis::Chebyshev{InteriorGrid}, i::Integer) where {T <: Real}
@unpack N = basis
@argcheck 1 i N # FIXME use boundscheck
sinpi((N - 2 * i + 1) / T(2 * N))::T # use formula Xu (2016)
sinpi((N - 2 * i + 1) / T(2 * N))::T # use formula from Xu (2016)
end

function gridpoint(::Type{T}, basis::Chebyshev{EndpointGrid}, i::Integer) where {T <: Real}
Expand Down
135 changes: 135 additions & 0 deletions src/domains.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#####
##### domains
#####

export domain_kind, coordinate_domains

####
#### generic
####

"""
$(SIGNATURES)
Return the *kind* of a domain type (preferred) or value. Errors for objects/types which
are not domains. Also works for domains of transformations.
The following return values are possible:
1. `:univariate`, the bounds of which can be accessed using `minimum`, `maximum`, and
`extrema`,
2. `:multivariate`, which supports `length`, `getindex` (`[]`), and conversion with `Tuple`.
"""
@inline domain_kind(x) = domain_kind(typeof(x))

domain_kind(::Type{T}) where T = throw(MethodError(domain_kind, (T,)))

####
#### univariate domains
####

"""
Univariate domain representation. Supports `extrema`, `minimum`, `maximum`.
!!! note
Implementations only need to define `extrema`.
"""
abstract type AbstractUnivariateDomain end

@inline domain_kind(::Type{<:AbstractUnivariateDomain}) = :univariate

Broadcast.broadcastable(domain::AbstractUnivariateDomain) = Ref(domain)

Base.minimum(domain::AbstractUnivariateDomain) = extrema(domain)[1]

Base.maximum(domain::AbstractUnivariateDomain) = extrema(domain)[2]

"""
Represents a univariate domain. Use `extrema`, `minimum`, or `maximum` to access the
bounds. Internal, not exported.
"""
struct UnivariateDomain{T} <: AbstractUnivariateDomain
min::T
max::T
end

Base.extrema(domain::UnivariateDomain) = (domain.min, domain.max)

"""
Represents the interval ``[-1, 1]``.
This is the domain of the Chebyshev polynomials. For internal use, not exported.
"""
struct PM1 <: AbstractUnivariateDomain end

Base.extrema(::PM1) = (-1, 1)

Base.show(io::IO, ::PM1) = print(io, "[-1,1]")

###
### multivariate domains
###

"""
Representation of a multivariate domain as the product of coordinate domains.
"""
struct CoordinateDomains{T<:Tuple}
domains::T
function CoordinateDomains(domains::Tuple)
@argcheck all(d -> domain_kind(d) :univariate, domains)
new{typeof(domains)}(domains)
end
end

@inline domain_kind(::Type{<:CoordinateDomains}) = :multivariate

function Base.show(io::IO, domain::CoordinateDomains)
(; domains) = domain
if allequal(domains)
print(io, domains[1])
print_unicode_superscript(io, length(domains))
else
join(io, domains, "×")
end
end

@inline Base.length(domain::CoordinateDomains) = length(domain.domains)

@inline Base.getindex(domain::CoordinateDomains, i) = getindex(domain.domains, i)

Base.Tuple(domain::CoordinateDomains) = domain.domains

"""
$(SIGNATURES)
Create domains which are the product of univariate domains. The result support `length`,
indexing with integers, and `Tuple` for conversion.
"""
function coordinate_domains(domains::Tuple)
CoordinateDomains(domains)
end

"""
$(SIGNATURES)
"""
function coordinate_domains(domains::Vararg)
CoordinateDomains(domains)
end

"""
$(SIGNATURES)
Create a coordinate domain which is the product of `domain` repeated `N` times.
"""
function coordinate_domains(::Val{N}, domain) where N
@argcheck N isa Integer && N 1
CoordinateDomains(ntuple(_ -> domain, Val(N)))
end

"""
$(SIGNATURES)
"""
@inline function coordinate_domains(N::Integer, domain)
coordinate_domains(Val(N), domain)
end
Loading

2 comments on commit bcfda39

@tpapp
Copy link
Owner Author

@tpapp tpapp commented on bcfda39 Oct 24, 2023

Choose a reason for hiding this comment

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

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

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

Registration pull request created: JuliaRegistries/General/94015

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.12.0 -m "<description of version>" bcfda3917a5417e94ecf6af8a0058c2a4e26e325
git push origin v0.12.0

Please sign in to comment.