From b68b3b5825b7390696bb0876261945b5be079657 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 10 Jul 2026 15:15:06 -0400 Subject: [PATCH] Document public API coverage Co-Authored-By: Chris Rackauckas --- docs/src/api.md | 10 +- src/SurrogatesBase.jl | 220 ++++++++++++++++++++++++++++++++++++------ test/qa/qa.jl | 39 ++++++++ 3 files changed, 236 insertions(+), 33 deletions(-) diff --git a/docs/src/api.md b/docs/src/api.md index 8c8915b..ccbe5c0 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -1,5 +1,11 @@ # API -```@autodocs -Modules = [SurrogatesBase] +```@docs +SurrogatesBase.AbstractDeterministicSurrogate +SurrogatesBase.AbstractStochasticSurrogate +SurrogatesBase.update! +SurrogatesBase.parameters +SurrogatesBase.update_hyperparameters! +SurrogatesBase.hyperparameters +SurrogatesBase.finite_posterior ``` diff --git a/src/SurrogatesBase.jl b/src/SurrogatesBase.jl index 1f5391c..915ba50 100644 --- a/src/SurrogatesBase.jl +++ b/src/SurrogatesBase.jl @@ -9,26 +9,40 @@ export finite_posterior """ abstract type AbstractDeterministicSurrogate <: Function end + (s::AbstractDeterministicSurrogate)(xs) -An abstract type for deterministic surrogates. +Interface tag for deterministic surrogate models. - (s::AbstractDeterministicSurrogate)(xs) +Subtypes approximate a deterministic function, or a deterministic statistic of a +conditional distribution, from observed data. A deterministic surrogate is callable on a +collection of input points `xs` and should return one surrogate value for each point. + +# Required Methods + + - `(s)(xs)`: evaluate the surrogate at the points in `xs`. + - [`update!(s, new_xs, new_ys)`](@ref): incorporate new observations. + +# Optional Methods -Subtypes of `AbstractDeterministicSurrogate` are callable with a `Vector` of points `xs`. -The result is a `Vector` of evaluations of the surrogate at points `xs`, corresponding to -approximations of the underlying function at points `xs` respectively. + - [`parameters(s)`](@ref): return learned parameter values. + - [`hyperparameters(s)`](@ref): return tunable hyperparameter values. + - [`update_hyperparameters!(s, prior)`](@ref): update tunable hyperparameters. # Examples ```jldoctest -julia> struct ZeroSurrogate <: AbstractDeterministicSurrogate end +julia> struct ConstantSurrogate{T} <: AbstractDeterministicSurrogate + value::T + end -julia> (::ZeroSurrogate)(xs) = 0 +julia> (s::ConstantSurrogate)(xs) = fill(s.value, length(xs)); -julia> s = ZeroSurrogate(); +julia> surrogate = ConstantSurrogate(1.5); -julia> s([4]) == 0 -true +julia> surrogate([[0.0, 1.0], [1.0, 2.0]]) +2-element Vector{Float64}: + 1.5 + 1.5 ``` """ abstract type AbstractDeterministicSurrogate <: Function end @@ -36,7 +50,22 @@ abstract type AbstractDeterministicSurrogate <: Function end """ abstract type AbstractStochasticSurrogate end -An abstract type for stochastic surrogates. +Interface tag for stochastic surrogate models. + +Subtypes approximate a conditional distribution, stochastic process, or uncertainty-aware +surrogate from observed data. + +# Required Methods + + - [`update!(s, new_xs, new_ys)`](@ref): incorporate new observations. + - [`finite_posterior(s, xs)`](@ref): return a finite-dimensional posterior object at + the points in `xs`. + +# Optional Methods + + - [`parameters(s)`](@ref): return learned parameter values. + - [`hyperparameters(s)`](@ref): return tunable hyperparameter values. + - [`update_hyperparameters!(s, prior)`](@ref): update tunable hyperparameters. See also [`finite_posterior`](@ref). """ @@ -45,30 +74,113 @@ abstract type AbstractStochasticSurrogate end """ update!(s, new_xs::AbstractVector, new_ys::AbstractVector) -Include data `new_ys` at points `new_xs` into the surrogate `s`, i.e., refit the surrogate `s` -to incorporate new data points. +Incorporate observations `new_ys` at points `new_xs` into the surrogate `s`. + +Implementations usually mutate and return `s`. For deterministic surrogates, `new_ys` +contains function evaluations or deterministic statistics. For stochastic surrogates, +`new_ys` contains observed samples from the modeled conditional distribution. + +# Arguments + + - `s`: surrogate to refit or update. + - `new_xs`: input points to add to `s`. + - `new_ys`: observed values corresponding to `new_xs`. + +Use `update!(s, eachslice(X; dims = 2), new_ys)` when columns of a matrix `X` are the input +points. + +# Examples + +```jldoctest +julia> mutable struct UpdateExampleSurrogate <: AbstractDeterministicSurrogate + xs::Vector{Float64} + ys::Vector{Float64} + end + +julia> (s::UpdateExampleSurrogate)(xs) = fill(last(s.ys), length(xs)); -If the surrogate `s` is a deterministic surrogate, the `new_ys` correspond to function -evaluations, if `s` is a stochastic surrogate, the `new_ys` are samples from a conditional -probability distribution. +julia> function SurrogatesBase.update!(s::UpdateExampleSurrogate, new_xs, new_ys) + append!(s.xs, new_xs) + append!(s.ys, new_ys) + return s + end; -Use `update!(s, eachslice(X, dims = 2), new_ys)` if `X` is a matrix. +julia> surrogate = UpdateExampleSurrogate(Float64[], Float64[]); + +julia> update!(surrogate, [1.0, 2.0], [3.0, 4.0]) === surrogate +true + +julia> surrogate.ys +2-element Vector{Float64}: + 3.0 + 4.0 +``` """ function update! end """ parameters(s) -Returns current values of parameters used in surrogate `s`. +Return the current learned parameter values of the surrogate `s`. + +This is an optional interface method for surrogate implementations that expose fitted +parameters separately from tunable hyperparameters. + +# Examples + +```jldoctest +julia> struct ParameterExampleSurrogate <: AbstractDeterministicSurrogate + weights::Vector{Float64} + end + +julia> (s::ParameterExampleSurrogate)(xs) = fill(sum(s.weights), length(xs)); + +julia> SurrogatesBase.parameters(s::ParameterExampleSurrogate) = s.weights; + +julia> parameters(ParameterExampleSurrogate([1.0, 2.0])) +2-element Vector{Float64}: + 1.0 + 2.0 +``` """ function parameters end """ update_hyperparameters!(s, prior) -Update the hyperparameters of the surrogate `s` by performing hyperparameter optimization -using the information in `prior`. After changing hyperparameters of `s`, fit `s` to past -data. +Update tunable hyperparameters of the surrogate `s` using information in `prior`. + +Implementations usually mutate and return `s`. After changing hyperparameters, the +surrogate should be refit to its existing observations when the hyperparameters affect the +fitted representation. + +# Arguments + + - `s`: surrogate whose hyperparameters are updated. + - `prior`: implementation-defined prior, bounds, or configuration used by the update. + +# Examples + +```jldoctest +julia> mutable struct HyperparameterUpdateExample <: AbstractDeterministicSurrogate + scale::Float64 + end + +julia> (s::HyperparameterUpdateExample)(xs) = fill(s.scale, length(xs)); + +julia> function SurrogatesBase.update_hyperparameters!(s::HyperparameterUpdateExample, prior) + s.scale = (s.scale + prior.scale) / 2 + return s + end; + +julia> surrogate = HyperparameterUpdateExample(2.0); + +julia> update_hyperparameters!(surrogate, (; scale = 4.0)) === surrogate +true + +julia> surrogate.scale +3.0 +``` See also [`hyperparameters`](@ref). """ @@ -77,7 +189,25 @@ function update_hyperparameters! end """ hyperparameters(s) -Returns current values of hyperparameters. +Return the current tunable hyperparameter values of the surrogate `s`. + +This is an optional interface method for surrogate implementations with configuration +values that control fitting or posterior construction. + +# Examples + +```jldoctest +julia> struct HyperparameterReadExample <: AbstractDeterministicSurrogate + settings::NamedTuple + end + +julia> (s::HyperparameterReadExample)(xs) = fill(s.settings.scale, length(xs)); + +julia> SurrogatesBase.hyperparameters(s::HyperparameterReadExample) = s.settings; + +julia> hyperparameters(HyperparameterReadExample((; scale = 2.0))) +(scale = 2.0,) +``` See also [`update_hyperparameters!`](@ref). """ @@ -86,18 +216,46 @@ function hyperparameters end """ finite_posterior(s::AbstractStochasticSurrogate, xs::AbstractVector) -Return a posterior distribution at points `xs`. +Return a finite-dimensional posterior object at points `xs`. + +The returned object represents the joint posterior over the requested points. An +`AbstractStochasticSurrogate` implementation may support some or all of the following +methods on that object: + + - `mean(finite_posterior(s, xs))`: posterior means at `xs`. + - `var(finite_posterior(s, xs))`: posterior variances at `xs`. + - `mean_and_var(finite_posterior(s, xs))`: posterior means and variances at `xs`. + - `rand(finite_posterior(s, xs))`: a sample from the joint posterior at `xs`. + +Use `mean(finite_posterior(s, eachslice(X; dims = 2)))` when columns of a matrix `X` are +the input points. -An `AbstractStochasticSurrogate` might implement some or all of the following methods on -the returned object: +# Examples + +```jldoctest +julia> using Statistics + +julia> struct PosteriorExampleSurrogate <: AbstractStochasticSurrogate + value::Float64 + end -- `mean(finite_posterior(s,xs))` returns a `Vector` of posterior means at `xs` -- `var(finite_posterior(s,xs))` returns a `Vector` of posterior variances at `xs` -- `mean_and_var(finite_posterior(s,xs))` returns a `Tuple` consisting of a `Vector` of posterior means and a `Vector` of posterior variances at `xs` -- `rand(finite_posterior(s,xs))` returns a `Vector`, which is a sample from the joint -posterior at points `xs` +julia> struct PosteriorExample + means::Vector{Float64} + end -Use `mean(finite_posterior(s, eachslice(X, dims = 2)))` if `X` is a matrix. +julia> Statistics.mean(p::PosteriorExample) = p.means; + +julia> function SurrogatesBase.finite_posterior(s::PosteriorExampleSurrogate, xs) + return PosteriorExample(fill(s.value, length(xs))) + end; + +julia> posterior = finite_posterior(PosteriorExampleSurrogate(1.25), [0.0, 1.0]); + +julia> mean(posterior) +2-element Vector{Float64}: + 1.25 + 1.25 +``` """ function finite_posterior end diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 6301136..314e9d2 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -7,6 +7,45 @@ import Statistics run_qa(SurrogatesBase; explicit_imports = true) +function documented_api_names() + api_path = joinpath(@__DIR__, "..", "..", "docs", "src", "api.md") + prefix = string(nameof(SurrogatesBase), ".") + documented_names = Symbol[] + + for line in eachline(api_path) + stripped = strip(line) + startswith(stripped, prefix) || continue + push!(documented_names, Symbol(stripped[(lastindex(prefix) + 1):end])) + end + + return sort!(unique(documented_names)) +end + +function has_source_docstring(mod::Module, name::Symbol) + doc = Docs.doc(getfield(mod, name)) + doc === nothing && return false + + return !occursin("No documentation found", sprint(show, MIME("text/plain"), doc)) +end + +@testset "public API documentation coverage" begin + public_names = sort!( + setdiff( + names(SurrogatesBase; all = false, imported = false), + [nameof(SurrogatesBase)] + ) + ) + + missing_docstrings = [ + name for name in public_names + if !has_source_docstring(SurrogatesBase, name) + ] + missing_docs_entries = setdiff(public_names, documented_api_names()) + + @test missing_docstrings == Symbol[] + @test missing_docs_entries == Symbol[] +end + # JET.report_call type-stability analysis of concrete user-defined surrogates. # This goes beyond run_qa's package-level JET.test_package: it checks that the # interface contract (update!/finite_posterior/parameters and the call method)