diff --git a/Project.toml b/Project.toml index 3bc0c2a..be0af51 100644 --- a/Project.toml +++ b/Project.toml @@ -1,15 +1,17 @@ name = "LogDensityProblems" uuid = "6fdf6af0-433a-55f7-b3ed-c6c6e0b8df7c" -authors = ["Tamas K. Papp "] version = "2.1.2" +authors = ["Tamas K. Papp "] [deps] ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197" +Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] ArgCheck = "1, 2" +Compat = "4.16.0" DocStringExtensions = "0.8, 0.9" julia = "1.10" diff --git a/docs/src/index.md b/docs/src/index.md index ed4e65c..0c41d0e 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -170,6 +170,10 @@ end If the gradient is a mutable vector (eg `Vector`), it should not be reused for another purpose. Practically, each call to [`LogDensityProblems.logdensity_and_gradient`](@ref) should allocate a new one, or use immutables like `StaticArrays.SVector` for small dimensions. +# Precomputation API + +FIXME + # Various utilities You may find these utilities useful for debugging and optimization. @@ -180,7 +184,7 @@ LogDensityProblems.stresstest # [Log densities API](@id log-density-api) -Use the functions below for evaluating gradients and querying their dimension and other information. These symbols are not exported, as they are mostly used by package developers and in any case would need to be `import`ed or qualified to add methods to. +Use the functions below for evaluating gradients and querying their dimension and other information. These symbols are `public`, but not exported, as they are mostly used by package developers and in any case would need to be `import`ed or qualified to add methods to. ```@docs LogDensityProblems.capabilities @@ -190,3 +194,13 @@ LogDensityProblems.logdensity LogDensityProblems.logdensity_and_gradient LogDensityProblems.logdensity_gradient_and_hessian ``` + +The methods below only need to be implemented for storing precomputed information with coordinates, otherwise the defaults just work. + +```@docs +LogDensityProblems.precompute +LogDensityProblems.move +LogDensityProblems.move! +``` + + diff --git a/src/LogDensityProblems.jl b/src/LogDensityProblems.jl index 2cea958..56802cc 100644 --- a/src/LogDensityProblems.jl +++ b/src/LogDensityProblems.jl @@ -15,6 +15,10 @@ module LogDensityProblems using ArgCheck: @argcheck using DocStringExtensions: SIGNATURES, TYPEDEF using Random: AbstractRNG, default_rng +using Compat: @compat + +@compat public LogDensityOrder, capabilities, dimension, logdensity, logdensity_and_gradient, + logdensity_gradient_and_hessian, precompute, move, move! #### #### interface for problems @@ -51,7 +55,7 @@ return values are invalid*. # Interface description -The following methods need to be implemented for the interface: +The following methods **need to be implemented** for the interface: 1. [`dimension`](@ref) returns the *dimension* of the domain, @@ -61,7 +65,34 @@ The following methods need to be implemented for the interface: 4. [`logdensity_gradient_and_hessian`](@ref) when `K ≥ 2`. -See also [`LogDensityProblems.stresstest`](@ref) for stress testing. +The precomputation API (see below) has sensible fallbacks and should only be implemented +as needed. + +# Coordinate points with precomputed information + +The interface also allows for encapsulating extra information associated with +coordinates. The idea is that a coordinate ``x ∈ ℝⁿ`` may be associated with quantities +precomputed from `x` (such as solutions to implicit equations), which can be updated at +a lower computational cost when the position is changed instead of recomputed from +scratch. + +The following methods **may be implemented**; but if they are not needed for your +application this package provides sensible defaults. + +1. A type that encapsulates the precomputed information. It should be + `<:AbstractVector{T}` for some `T` and support the read-only interface for vectors, + ie `Base.size` and `Base.getindex`. When used as a vector, it should just correspond + to the position in ``ℝⁿ``. + +2. [`precompute`](@ref), which precomputes the relevant information and returns objects of the + type above. + +3. [`move`](@ref) and [`move!`](@ref) to change the coordinates and recompute the + associated information. + +# See also + +[`LogDensityProblems.stresstest`](@ref) for stress testing. """ capabilities(T::Type) = nothing @@ -151,6 +182,53 @@ The first argument (the log density) can be shifted by a constant, see the note """ function logdensity_gradient_and_hessian end +""" +$(SIGNATURES) + +Precompute information associated with the coordinates `x` and return it as a +user-defined type. + +Cf [`move`](@ref). +""" +function precompute(ℓ, x::AbstractVector{T}) where T + if T <: AbstractFloat + x + else + float.(x) + end +end + +""" +$(SIGNATURES) + +Change the coordinates by `Δ`. Conceptually equivalent to `x .+ Δ`, which is the +fallback implementation, but user-defined types for the second argument can take +advantage of the information there to compute the new associated information for small +changes. + +The result should be **the same type** as `x` if `eltype(x) ≡ eltype(Δ)`. + +Caller ensures that `x` is the result of [`precompute`](@ref). It is valid for an +implementation to error in all other cases. +""" +move(ℓ, x::AbstractVector, Δ) = x .+ Δ + +""" +$(SIGNATURES) + +Equivalent to [`move`](@ref), but *may* modify `x`, which is returned, or choose to +return a new value. + +If a new value is returned, it should be the same type as `x` if `eltype(x) ≡ eltype(Δ)`. + +Caller ensures that `x` is the result of [`precompute`](@ref). It is valid for an +implementation to error in all other cases. +""" +function move!(ℓ, x::AbstractVector, Δ) + x .+= Δ + x +end + include("utilities.jl") end # module