From bf332dd098c4327f32fb8abcac1f1e36a7239d48 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 10 Jul 2026 16:21:35 -0400 Subject: [PATCH 1/3] Use SciMLTesting API docs QA Co-Authored-By: Chris Rackauckas --- docs/Project.toml | 3 + docs/src/basics/solve.md | 11 +++ src/common.jl | 145 +++++++++++++++++++++++++++++++++++++ test/qa/Project.toml | 3 +- test/qa/public_api_docs.jl | 46 ------------ test/qa/qa.jl | 3 +- 6 files changed, 163 insertions(+), 48 deletions(-) delete mode 100644 test/qa/public_api_docs.jl diff --git a/docs/Project.toml b/docs/Project.toml index 02a151cd..e13c6271 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -8,6 +8,9 @@ ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Integrals = "de52edbc-65ea-441a-8357-d3a637375a31" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" +[sources] +Integrals = {path = ".."} + [compat] Cuba = "2" Cubature = "1" diff --git a/docs/src/basics/solve.md b/docs/src/basics/solve.md index c572b001..6b1030b0 100644 --- a/docs/src/basics/solve.md +++ b/docs/src/basics/solve.md @@ -3,3 +3,14 @@ ```@docs solve(prob::IntegralProblem, alg::SciMLBase.AbstractIntegralAlgorithm) ``` + +## Related SciML interface utilities + +```@docs +init(prob::IntegralProblem, alg::SciMLBase.AbstractIntegralAlgorithm) +init(prob::SampledIntegralProblem, alg::SciMLBase.AbstractIntegralAlgorithm) +solve!(cache::Integrals.IntegralCache) +solve!(cache::Integrals.SampledIntegralCache) +isinplace(cache::Integrals.IntegralCache) +ReturnCode +``` diff --git a/src/common.jl b/src/common.jl index 96eb0dc6..2c289e8f 100644 --- a/src/common.jl +++ b/src/common.jl @@ -11,10 +11,71 @@ mutable struct IntegralCache{iip, F, D, P, PK, A, S, K, Tc, VT} verbosity::VT end +""" + isinplace(cache::IntegralCache) -> Bool + +Return whether an initialized integral cache wraps an in-place integrand. + +## Arguments + + - `cache`: Cache returned by [`init`](@ref) for an `IntegralProblem`. + +## Returns + +Returns `true` when the cached integral function mutates its output argument, and +`false` otherwise. + +## Example + +```julia +using Integrals + +prob = IntegralProblem((x, p) -> x^2, (0.0, 1.0)) +cache = init(prob, QuadGKJL()) +isinplace(cache) +``` +""" SciMLBase.isinplace(::IntegralCache{iip}) where {iip} = iip init_cacheval(::SciMLBase.AbstractIntegralAlgorithm, args...) = nothing +""" + init(prob::IntegralProblem, alg::SciMLBase.AbstractIntegralAlgorithm; kwargs...) + +Initialize a reusable integral solver cache for `prob` and `alg`. + +## Arguments + + - `prob`: Integral problem containing the integrand, domain, parameters, and problem + keyword options. + - `alg`: Integration algorithm, such as `QuadGKJL()`, `HCubatureJL()`, or another + subtype of `SciMLBase.AbstractIntegralAlgorithm`. + +## Keyword Arguments + + - `sensealg`: Sensitivity algorithm used by automatic differentiation paths. Defaults to + `ReCallVJP(ZygoteVJP())`. + - `verbose`: Verbosity control via [`IntegralVerbosity`](@ref). Defaults to + `IntegralVerbosity()`. + - `do_inf_transformation`: Deprecated. Infinite-domain transformations are always + applied when needed. + - Additional keyword arguments are forwarded to the eventual solve. + +## Returns + +Returns an internal cache object that stores the transformed algorithm, cached backend +state, verbosity settings, and solve keyword arguments for reuse with [`solve!`](@ref). + +## Example + +```julia +using Integrals + +prob = IntegralProblem((x, p) -> sin(x), (0.0, pi)) +cache = init(prob, QuadGKJL(); reltol = 1e-10) +solve!(cache) +``` +""" function SciMLBase.init( prob::IntegralProblem{iip}, alg::SciMLBase.AbstractIntegralAlgorithm; @@ -160,6 +221,30 @@ function SciMLBase.solve( return solve!(init(prob, alg; kwargs...)) end +""" + solve!(cache::IntegralCache) + +Solve an initialized integral cache. + +## Arguments + + - `cache`: Cache returned by [`init`](@ref) for an `IntegralProblem`. + +## Returns + +Returns a SciMLBase integral solution whose `u` field is the integral estimate and whose +`resid` field is the backend residual or error estimate when available. + +## Example + +```julia +using Integrals + +prob = IntegralProblem((x, p) -> x^2, (0.0, 1.0)) +cache = init(prob, QuadGKJL()) +sol = solve!(cache) +``` +""" function SciMLBase.solve!(cache::IntegralCache) return __solve( cache, cache.alg, cache.sensealg, cache.domain, cache.p; @@ -195,6 +280,42 @@ function Base.setproperty!(cache::SampledIntegralCache, name::Symbol, x) return setfield!(cache, name, x) end +""" + init(prob::SampledIntegralProblem, alg::SciMLBase.AbstractIntegralAlgorithm; kwargs...) + +Initialize a reusable sampled-data integral solver cache. + +## Arguments + + - `prob`: Sampled integral problem containing sampled values, sampling points, and the + integration dimension. + - `alg`: Sampled-data integration algorithm, such as `TrapezoidalRule()` or + `SimpsonsRule()`. + +## Keyword Arguments + + - `verbose`: Verbosity control via [`IntegralVerbosity`](@ref). Defaults to + `IntegralVerbosity()`. + +No other keyword arguments are accepted by this cache initializer. + +## Returns + +Returns an internal cache object that can be reused with [`solve!`](@ref). Updating the +sample locations marks the cached weights stale so they are recomputed before the next +solve. + +## Example + +```julia +using Integrals + +x = range(0, 1, length = 21) +prob = SampledIntegralProblem(x .^ 2, x) +cache = init(prob, SimpsonsRule()) +solve!(cache) +``` +""" function SciMLBase.init( prob::SampledIntegralProblem, alg::SciMLBase.AbstractIntegralAlgorithm; @@ -260,6 +381,30 @@ function SciMLBase.solve( return solve!(init(prob, alg; kwargs...)) end +""" + solve!(cache::SampledIntegralCache) + +Solve an initialized sampled-data integral cache. + +## Arguments + + - `cache`: Cache returned by [`init`](@ref) for a `SampledIntegralProblem`. + +## Returns + +Returns a SciMLBase integral solution whose `u` field is the sampled integral estimate. + +## Example + +```julia +using Integrals + +x = range(0, 1, length = 21) +prob = SampledIntegralProblem(x .^ 2, x) +cache = init(prob, TrapezoidalRule()) +sol = solve!(cache) +``` +""" function SciMLBase.solve!(cache::SampledIntegralCache) return __solvebp(cache, cache.alg; cache.kwargs...) end diff --git a/test/qa/Project.toml b/test/qa/Project.toml index c4db8a0d..75aa006c 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -8,12 +8,13 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] Integrals = {path = "../.."} +SciMLTesting = {url = "https://github.com/SciML/SciMLTesting.jl", rev = "a5cecca928f2c684c23505c3cd83921d71753e2e"} [compat] Aqua = "0.8" Integrals = "5" JET = "0.9, 0.10, 0.11.3" SafeTestsets = "0.1, 1" -SciMLTesting = "1.7" +SciMLTesting = "2.1" Test = "1.10" julia = "1.10" diff --git a/test/qa/public_api_docs.jl b/test/qa/public_api_docs.jl deleted file mode 100644 index ba29cf14..00000000 --- a/test/qa/public_api_docs.jl +++ /dev/null @@ -1,46 +0,0 @@ -using Integrals, Test - -function _has_docstring(mod::Module, name::Symbol) - return haskey(Docs.meta(mod), Docs.Binding(mod, name)) -end - -function _rendered_docs_entries() - docs_root = normpath(joinpath(@__DIR__, "..", "..", "docs", "src")) - entries = Set{Symbol}() - for (root, _, files) in walkdir(docs_root) - for file in files - endswith(file, ".md") || continue - in_docs_block = false - for line in eachline(joinpath(root, file)) - stripped = strip(line) - if stripped == "```@docs" - in_docs_block = true - continue - elseif startswith(stripped, "```") - in_docs_block = false - continue - end - in_docs_block || continue - isempty(stripped) && continue - startswith(stripped, "#") && continue - identifier = replace(stripped, r"\(.*" => "") - identifier = split(identifier, ".")[end] - isempty(identifier) || push!(entries, Symbol(identifier)) - end - end - end - return entries -end - -@testset "Integrals-owned public API docs" begin - public_names = filter(sort!(collect(names(Integrals; all = false)))) do name - name !== :Integrals && parentmodule(getfield(Integrals, name)) === Integrals - end - - undocumented = filter(name -> !_has_docstring(Integrals, name), public_names) - @test isempty(undocumented) - - rendered_entries = _rendered_docs_entries() - missing_rendered = filter(name -> name ∉ rendered_entries, public_names) - @test isempty(missing_rendered) -end diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 0c11f729..43a7750a 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -1,11 +1,12 @@ using SciMLTesting, Integrals, Test using JET -include("public_api_docs.jl") +const UPSTREAM_RENDERED_API = (:SciMLBase, :remake) run_qa( Integrals; explicit_imports = true, + api_docs_kwargs = (; rendered = true, rendered_ignore = UPSTREAM_RENDERED_API), aqua_kwargs = (; # IntegralProblem / SampledIntegralProblem are SciMLBase types this package # owns the integral-solver methods for, so dispatching on them is not piracy. From 76aeeef67e57f46935782a1e9484a247e1ef3591 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 10 Jul 2026 19:15:08 -0400 Subject: [PATCH 2/3] Pin SciMLTesting v2.1 for QA Co-Authored-By: Chris Rackauckas --- Project.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 0f866cb7..67a5b44f 100644 --- a/Project.toml +++ b/Project.toml @@ -43,6 +43,9 @@ IntegralsMCIntegrationExt = "MCIntegration" IntegralsMooncakeExt = ["Mooncake", "Zygote", "ChainRulesCore"] IntegralsZygoteExt = ["Zygote", "ChainRulesCore"] +[sources] +SciMLTesting = {url = "https://github.com/SciML/SciMLTesting.jl", rev = "a5cecca928f2c684c23505c3cd83921d71753e2e"} + [compat] ADTypes = "1.22.0" Arblib = "1" @@ -68,7 +71,7 @@ Reexport = "1.2" SafeTestsets = "0.1, 1" SciMLBase = "2.104, 3.0" SciMLLogging = "1.10.1, 2" -SciMLTesting = "1" +SciMLTesting = "2.1" StaticArrays = "1.9.8" Test = "1.10" Unitful = "1" From e863ec4bb5e5ddd9b4809d561c0ba94d07433047 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 10 Jul 2026 22:19:47 -0400 Subject: [PATCH 3/3] Use SciMLTesting release for QA docs Co-Authored-By: Chris Rackauckas --- Project.toml | 9 ++++----- test/qa/Project.toml | 1 - test/runtests.jl | 6 ++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index 67a5b44f..9cfd94e7 100644 --- a/Project.toml +++ b/Project.toml @@ -43,9 +43,6 @@ IntegralsMCIntegrationExt = "MCIntegration" IntegralsMooncakeExt = ["Mooncake", "Zygote", "ChainRulesCore"] IntegralsZygoteExt = ["Zygote", "ChainRulesCore"] -[sources] -SciMLTesting = {url = "https://github.com/SciML/SciMLTesting.jl", rev = "a5cecca928f2c684c23505c3cd83921d71753e2e"} - [compat] ADTypes = "1.22.0" Arblib = "1" @@ -65,13 +62,14 @@ LinearAlgebra = "1.10" MCIntegration = "0.4.2" MonteCarloIntegration = "0.2" Mooncake = "0.5.6" +Pkg = "1.10" QuadGK = "2.11" Random = "1.10" Reexport = "1.2" SafeTestsets = "0.1, 1" SciMLBase = "2.104, 3.0" SciMLLogging = "1.10.1, 2" -SciMLTesting = "2.1" +SciMLTesting = "1" StaticArrays = "1.9.8" Test = "1.10" Unitful = "1" @@ -90,6 +88,7 @@ FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" FastTanhSinhQuadrature = "b650e0df-f744-4436-b963-b44034668c57" HAdaptiveIntegration = "eaa5ad34-b243-48e9-b09c-54bc0655cecf" MCIntegration = "ea1e2de9-7db7-4b42-91ee-0cd1bf6df167" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" @@ -97,4 +96,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [targets] -test = ["ADTypes", "Arblib", "StaticArrays", "SafeTestsets", "SciMLTesting", "Test", "Distributions", "ChainRulesCore", "FastGaussQuadrature", "FastTanhSinhQuadrature", "Cuba", "Cubature", "MCIntegration", "DifferentiationInterface", "HAdaptiveIntegration", "Unitful"] +test = ["ADTypes", "Arblib", "StaticArrays", "SafeTestsets", "SciMLTesting", "Test", "Distributions", "ChainRulesCore", "FastGaussQuadrature", "FastTanhSinhQuadrature", "Cuba", "Cubature", "MCIntegration", "DifferentiationInterface", "HAdaptiveIntegration", "Pkg", "Unitful"] diff --git a/test/qa/Project.toml b/test/qa/Project.toml index 75aa006c..d0b954c9 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -8,7 +8,6 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] Integrals = {path = "../.."} -SciMLTesting = {url = "https://github.com/SciML/SciMLTesting.jl", rev = "a5cecca928f2c684c23505c3cd83921d71753e2e"} [compat] Aqua = "0.8" diff --git a/test/runtests.jl b/test/runtests.jl index a18a7cc4..3907163d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,2 +1,8 @@ +if get(ENV, "GROUP", "All") == "QA" + import Pkg + Pkg.activate(joinpath(@__DIR__, "qa")) + Pkg.instantiate() +end + using SciMLTesting run_tests()