From fbc8d6ac3ebdba0aeff4865d4c0ce54e6a441eb7 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sun, 7 Jun 2026 16:15:33 -0400 Subject: [PATCH 01/12] Uniformize monorepo structure to OrdinaryDiffEq canonical style Make the repo match the OrdinaryDiffEq.jl canonical monorepo invariants: Root track: - Convert the bare [workspace] root into a real package Project.toml (name/uuid/version, [deps]+[sources] to lib/, [compat], [extras]+[targets].test). The umbrella now @reexport-s OUQBase so it is a buildable, meaningful package and the [sources] path graph is traversable by sublibrary matrix discovery. - Add a GROUP-dispatched root CI.yml (matrix over the root's own Core group; no hardcoded sublibrary rows) and a root Downgrade.yml calling downgrade.yml@v1 (no allow-reresolve). - Add root test/runtests.jl with a _detect_sublibrary_group dispatcher: if GROUP names lib/ it activates and Pkg.test-s that sublib under OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP; otherwise runs the root's own @safetestset groups (umbrella load smoke test). Sublibraries: - Delete lib/CanonicalMoments/test/Project.toml and lib/OUQBase/test/Project.toml; fold their deps into [extras] + [targets].test in each package Project.toml. - Remove OUQBase's [workspace] test-subproject wiring. - Drop TestItemRunner from CanonicalMoments test deps (unused). - Rewrite every sublib test/runtests.jl to read OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP and @safetestset-include per group (Core), mirroring OrdinaryDiffEq sublib runtests. Workflows: - SublibraryCI.yml: thread group-env-name OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP + check-bounds: auto. - DowngradeSublibraries.yml: add group-env-name/group-env-value Core, skip list of siblings + stdlibs; auto-discovers lib/*. - dependabot.yml: drop the removed test/ subprojects. No [compat] floors lowered; no tests skipped/broken/loosened. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/dependabot.yml | 2 - .github/workflows/CI.yml | 71 ++++++++++++ .github/workflows/Downgrade.yml | 26 +++++ .github/workflows/DowngradeSublibraries.yml | 5 +- .github/workflows/SublibraryCI.yml | 3 + Project.toml | 37 +++++-- lib/CanonicalMoments/Project.toml | 24 ++++- lib/CanonicalMoments/test/Project.toml | 13 --- lib/CanonicalMoments/test/runtests.jl | 12 +-- lib/DiscreteMeasures/Project.toml | 7 +- .../test/discrete_measures_tests.jl | 101 ++++++++++++++++++ lib/DiscreteMeasures/test/runtests.jl | 101 +----------------- lib/OUQBase/Project.toml | 13 ++- lib/OUQBase/test/Project.toml | 6 -- lib/OUQBase/test/runtests.jl | 9 +- src/OptimalUncertaintyQuantification.jl | 3 + test/runtests.jl | 70 ++++++++++++ test/umbrella_load.jl | 12 +++ 18 files changed, 377 insertions(+), 138 deletions(-) create mode 100644 .github/workflows/CI.yml create mode 100644 .github/workflows/Downgrade.yml delete mode 100644 lib/CanonicalMoments/test/Project.toml create mode 100644 lib/DiscreteMeasures/test/discrete_measures_tests.jl delete mode 100644 lib/OUQBase/test/Project.toml create mode 100644 test/runtests.jl create mode 100644 test/umbrella_load.jl diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 86c258b..01b4be8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,11 +11,9 @@ updates: - "/lib/CanonicalMoments" - "/lib/CanonicalMoments/docs" - "/lib/CanonicalMoments/examples" - - "/lib/CanonicalMoments/test" - "/lib/DiscreteMeasures" - "/lib/DiscreteMeasures/docs" - "/lib/OUQBase" - - "/lib/OUQBase/test" schedule: interval: "daily" groups: diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..9c33a8f --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,71 @@ +name: CI +on: + pull_request: + branches: + - main + paths-ignore: + - 'docs/**' + push: + branches: + - main + paths-ignore: + - 'docs/**' +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +jobs: + test: + permissions: + actions: write + contents: read + runs-on: ubuntu-latest + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + group: + - Core + version: + - 'lts' + - '1.11' + - '1' + - 'pre' + steps: + - uses: actions/checkout@v6 + - uses: julia-actions/setup-julia@v3 + with: + version: ${{ matrix.version }} + - uses: julia-actions/cache@v3 + - name: Develop local path deps (Julia < 1.11) + shell: julia --color=yes --project=. {0} + run: | + using Pkg + if VERSION < v"1.11.0-DEV.0" + toml = Pkg.TOML.parsefile("Project.toml") + if haskey(toml, "sources") + specs = Pkg.PackageSpec[] + for (dep, spec) in toml["sources"] + if spec isa Dict && haskey(spec, "path") && isdir(spec["path"]) + @info "Developing" dep spec["path"] + push!(specs, Pkg.PackageSpec(path=spec["path"])) + end + end + isempty(specs) || Pkg.develop(specs) + end + end + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + with: + coverage: false + check_bounds: auto + env: + GROUP: ${{ matrix.group }} + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v6 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: lcov.info + fail_ci_if_error: false + disable_safe_directory: true diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml new file mode 100644 index 0000000..e189b48 --- /dev/null +++ b/.github/workflows/Downgrade.yml @@ -0,0 +1,26 @@ +name: Downgrade +on: + pull_request: + branches: + - main + paths-ignore: + - 'docs/**' + push: + branches: + - main + paths-ignore: + - 'docs/**' +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +jobs: + test: + name: "Downgrade" + uses: "SciML/.github/.github/workflows/downgrade.yml@v1" + with: + julia-version: "1.10" + group: "Core" + skip: "Pkg,TOML,Test,Statistics,LinearAlgebra,SparseArrays,InteractiveUtils,CanonicalMoments,DiscreteMeasures,OUQBase" + secrets: "inherit" diff --git a/.github/workflows/DowngradeSublibraries.yml b/.github/workflows/DowngradeSublibraries.yml index 2a6f7bd..fe207b2 100644 --- a/.github/workflows/DowngradeSublibraries.yml +++ b/.github/workflows/DowngradeSublibraries.yml @@ -23,4 +23,7 @@ jobs: secrets: "inherit" with: julia-version: "1.10" - skip: "Pkg,TOML" + skip: "Pkg,TOML,Test,Statistics,LinearAlgebra,SparseArrays,InteractiveUtils,CanonicalMoments,DiscreteMeasures,OUQBase" + group-env-name: "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP" + group-env-value: "Core" + # Every lib/* sublibrary is downgrade-tested (projects auto-discovered, no exclusions). diff --git a/.github/workflows/SublibraryCI.yml b/.github/workflows/SublibraryCI.yml index a44aae5..45c8cff 100644 --- a/.github/workflows/SublibraryCI.yml +++ b/.github/workflows/SublibraryCI.yml @@ -19,4 +19,7 @@ concurrency: jobs: sublibraries: uses: "SciML/.github/.github/workflows/sublibrary-project-tests.yml@v1" + with: + group-env-name: OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP + check-bounds: auto secrets: "inherit" diff --git a/Project.toml b/Project.toml index e9ea91c..c2e3281 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,31 @@ -[workspace] -projects = [ - "lib/CanonicalMoments", - "lib/DiscreteMeasures", - "lib/OUQBase" -] \ No newline at end of file +name = "OptimalUncertaintyQuantification" +uuid = "91ab1271-1799-4997-981e-07ad84422b0d" +authors = ["Avinash Subramanian (JuliaHub), Benjamin Chung (JuliaHub), Adam Gerlach (AFRL)"] +version = "0.1.0" + +[deps] +CanonicalMoments = "58d2c334-1a3c-4862-bb37-9012b9e58a38" +DiscreteMeasures = "7766d772-2108-41ee-a4bd-11c51440a39b" +OUQBase = "01930cae-99d2-7439-8f4f-ace2ece9f1b9" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" + +[sources] +CanonicalMoments = { path = "lib/CanonicalMoments" } +DiscreteMeasures = { path = "lib/DiscreteMeasures" } +OUQBase = { path = "lib/OUQBase" } + +[compat] +julia = "1.10" +CanonicalMoments = "0.1" +DiscreteMeasures = "0.1" +OUQBase = "0.1" +Reexport = "1.2.2" +SafeTestsets = "0.1" + +[extras] +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Pkg", "SafeTestsets", "Test"] diff --git a/lib/CanonicalMoments/Project.toml b/lib/CanonicalMoments/Project.toml index 69b53b0..3c149ee 100644 --- a/lib/CanonicalMoments/Project.toml +++ b/lib/CanonicalMoments/Project.toml @@ -26,13 +26,33 @@ SymbolicsExt = "Symbolics" [compat] julia = "1.10" DiscreteMeasures = "0.1" +InteractiveUtils = "1.10" +IntervalArithmetic = "0.18 - 0.20, =0.20.9" LinearAlgebra = "1.10" +PolynomialRoots = "1.0" Polynomials = "4" +Random = "1.10" RecurrenceRelationships = "0.2" -Statistics = "1.10" -IntervalArithmetic = "0.18 - 0.20, =0.20.9" Reexport = "1.2.2" +SafeTestsets = "0.1" +SpecialFunctions = "2" +StaticArrays = "1" +Statistics = "1.10" Symbolics = "6.29.2" +Test = "1.10" [sources] DiscreteMeasures = { path = "../DiscreteMeasures" } + +[extras] +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +PolynomialRoots = "3a141323-8675-5d76-9d11-e1df1406c778" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["InteractiveUtils", "IntervalArithmetic", "PolynomialRoots", "Random", "SafeTestsets", "SpecialFunctions", "StaticArrays", "Test"] diff --git a/lib/CanonicalMoments/test/Project.toml b/lib/CanonicalMoments/test/Project.toml deleted file mode 100644 index fa5721e..0000000 --- a/lib/CanonicalMoments/test/Project.toml +++ /dev/null @@ -1,13 +0,0 @@ -[deps] -InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -PolynomialRoots = "3a141323-8675-5d76-9d11-e1df1406c778" -Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" -Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" -SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" -StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" -Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" diff --git a/lib/CanonicalMoments/test/runtests.jl b/lib/CanonicalMoments/test/runtests.jl index 287b6dc..1ad6f5d 100644 --- a/lib/CanonicalMoments/test/runtests.jl +++ b/lib/CanonicalMoments/test/runtests.jl @@ -1,12 +1,12 @@ -# using OptimalUncertaintyQuantification -using CanonicalMoments using SafeTestsets using Test -# @run_package_tests verbose = true +const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "ALL") -@safetestset "Orthogonal Polynomial Roots" include("orthopoly_roots.jl") -@safetestset "Moment Sequence" include("moment_sequence.jl") +if TEST_GROUP == "Core" || TEST_GROUP == "ALL" + @safetestset "Orthogonal Polynomial Roots" include("orthopoly_roots.jl") + @safetestset "Moment Sequence" include("moment_sequence.jl") +end # @safetestset "Root Domain Restriction" begin # using IntervalArithmetic @@ -57,7 +57,7 @@ using Test # for (c, setup) in zip((c1, c2, c3), (setup1, setup2, setup3)) # for _setup in setup # p = _setup.free -# for (lb, ub, _c, _p) in zip(ql, qu, c, p) +# for (lb, ub, _c, _p) in zip(ql, qu, c, p) # can = moments_to_canonical(lb, ub, _c, _p) # @test all(@. 0 ≤ can ≤ 1) diff --git a/lib/DiscreteMeasures/Project.toml b/lib/DiscreteMeasures/Project.toml index c2546c5..4944557 100644 --- a/lib/DiscreteMeasures/Project.toml +++ b/lib/DiscreteMeasures/Project.toml @@ -12,10 +12,13 @@ IntervalArithmeticExt = "IntervalArithmetic" [compat] julia = "1.10" IntervalArithmetic = "0.16 - 0.20, =0.20.9" +SafeTestsets = "0.1" +Test = "1.10" [extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "IntervalArithmetic"] +test = ["IntervalArithmetic", "SafeTestsets", "Test"] diff --git a/lib/DiscreteMeasures/test/discrete_measures_tests.jl b/lib/DiscreteMeasures/test/discrete_measures_tests.jl new file mode 100644 index 0000000..1f5d4b6 --- /dev/null +++ b/lib/DiscreteMeasures/test/discrete_measures_tests.jl @@ -0,0 +1,101 @@ +using DiscreteMeasures, IntervalArithmetic +using Test + +x = [[1.0, 2.0, 2.5], [3.0, 4.0, 5.0]] + +w = [[10.0, 20.0, 25], [30.0, 40.0, 6.0]] + +w3d = [1.0, 2.0] + +@testset "DiscreteMeasure" begin + dm = DiscreteMeasure(first(x), first(w)) + @test support(dm) == first(x) + @test weights(dm) == first(w) + @test order(dm) == 3 + @test ndims(dm) == 1 + + + dm3d = DiscreteMeasure(x, w3d) + @test support(dm3d) == x + @test weights(dm3d) == w3d + @test order(dm3d) == 2 + @test ndims(dm3d) == 3 +end + +@testset "ProductDiscreteMeasure" begin + dms = DiscreteMeasure.(x, w) + pdms = ProductDiscreteMeasure(dms) + @test marginals(pdms) == dms + @test support(pdms) == vec([[a, b] for a in first(x), b in last(x)]) + @test weights(pdms) == vec([a * b for a in first(w), b in last(w)]) + + x2 = [-1.0, -2.0] + w2 = [100.0, 200.0] + + dm2 = DiscreteMeasure(x2, w2) + pdms2 = ProductDiscreteMeasure(pdms, dm2) + @test marginals(pdms2) == [dms; dm2] + @test support(pdms2) == vec([[a, b, c] for a in first(x), b in last(x), c in x2]) + @test weights(pdms2) == vec([a * b * c for a in first(w), b in last(w), c in w2]) + + pdms3 = ProductDiscreteMeasure(pdms, dms) + @test marginals(pdms3) == [dms; dms] + @test support(pdms3) == + vec([[a, b, c, d] for a in first(x), b in last(x), c in first(x), d in last(x)]) + @test weights(pdms3) == + vec([a * b * c * d for a in first(w), b in last(w), c in first(w), d in last(w)]) + + dm1d = DiscreteMeasure(first(x), first(w)) + dm3d = DiscreteMeasure(x, w3d) + pdms = ProductDiscreteMeasure([dm3d, dm1d]) + @test marginals(pdms) == [dm3d; dm1d] + @test support(pdms) == vec([vcat(a, b) for a in support(dm3d), b in support(dm1d)]) + @test weights(pdms) == vec([a * b for a in weights(dm3d), b in weights(dm1d)]) + + @testset "Product of single DiscreteMeasure" begin + dm = DiscreteMeasure(x, w3d) + pdm = ProductDiscreteMeasure([dm]) + @test only(marginals(pdm)) == dm + @test support(pdm) == x + @test weights(pdm) == w3d + + end +end + +@testset "Expectation" begin + dm = DiscreteMeasure(first(x), first(w)) + dms = DiscreteMeasure.(x, w) + pdms = ProductDiscreteMeasure(dms) + dm1d = DiscreteMeasure(first(x), first(w)) + dm3d = DiscreteMeasure(x, w3d) + pdms4d = ProductDiscreteMeasure([dm3d, dm1d]) + + + f(x) = sin(first(x)) + + for μ in (dm, pdms, pdms4d) + @test expectation(f, μ) ≈ weights(μ)' * f.(support(μ)) + end +end + +@testset "Measure Restrictions" begin + for T in (Int64, Float64) + @test clamp_domain(T(-1), 0, 2) == T(0) + @test clamp_domain(T(1), 0, 2) == T(1) + @test clamp_domain(T(3), 0, 2) == T(2) + + @test clamp_weight(T(-1)) == T(0) + @test clamp_weight(T(1), T(2)) == T(1) + @test clamp_weight(T(3)) == T(1) + @test clamp_weight(T(3), T(2)) == T(2) + end + + @testset "Interval Ext" begin + @test clamp_domain(Interval(-2, -1), 0, 2) == ∅ + @test clamp_domain(Interval(-2, 1), 0, 2) == Interval(0, 1) + @test clamp_domain(Interval(0.1, 0.5), 0, 2) == Interval(0.1, 0.5) + @test clamp_domain(Interval(0.5, 5), 0, 2) == Interval(0.5, 2) + @test clamp_domain(Interval(3, 4), 0, 2) == ∅ + end + +end diff --git a/lib/DiscreteMeasures/test/runtests.jl b/lib/DiscreteMeasures/test/runtests.jl index 1f5d4b6..a98281f 100644 --- a/lib/DiscreteMeasures/test/runtests.jl +++ b/lib/DiscreteMeasures/test/runtests.jl @@ -1,101 +1,8 @@ -using DiscreteMeasures, IntervalArithmetic +using SafeTestsets using Test -x = [[1.0, 2.0, 2.5], [3.0, 4.0, 5.0]] - -w = [[10.0, 20.0, 25], [30.0, 40.0, 6.0]] - -w3d = [1.0, 2.0] - -@testset "DiscreteMeasure" begin - dm = DiscreteMeasure(first(x), first(w)) - @test support(dm) == first(x) - @test weights(dm) == first(w) - @test order(dm) == 3 - @test ndims(dm) == 1 - - - dm3d = DiscreteMeasure(x, w3d) - @test support(dm3d) == x - @test weights(dm3d) == w3d - @test order(dm3d) == 2 - @test ndims(dm3d) == 3 -end - -@testset "ProductDiscreteMeasure" begin - dms = DiscreteMeasure.(x, w) - pdms = ProductDiscreteMeasure(dms) - @test marginals(pdms) == dms - @test support(pdms) == vec([[a, b] for a in first(x), b in last(x)]) - @test weights(pdms) == vec([a * b for a in first(w), b in last(w)]) - - x2 = [-1.0, -2.0] - w2 = [100.0, 200.0] - - dm2 = DiscreteMeasure(x2, w2) - pdms2 = ProductDiscreteMeasure(pdms, dm2) - @test marginals(pdms2) == [dms; dm2] - @test support(pdms2) == vec([[a, b, c] for a in first(x), b in last(x), c in x2]) - @test weights(pdms2) == vec([a * b * c for a in first(w), b in last(w), c in w2]) - - pdms3 = ProductDiscreteMeasure(pdms, dms) - @test marginals(pdms3) == [dms; dms] - @test support(pdms3) == - vec([[a, b, c, d] for a in first(x), b in last(x), c in first(x), d in last(x)]) - @test weights(pdms3) == - vec([a * b * c * d for a in first(w), b in last(w), c in first(w), d in last(w)]) - - dm1d = DiscreteMeasure(first(x), first(w)) - dm3d = DiscreteMeasure(x, w3d) - pdms = ProductDiscreteMeasure([dm3d, dm1d]) - @test marginals(pdms) == [dm3d; dm1d] - @test support(pdms) == vec([vcat(a, b) for a in support(dm3d), b in support(dm1d)]) - @test weights(pdms) == vec([a * b for a in weights(dm3d), b in weights(dm1d)]) - - @testset "Product of single DiscreteMeasure" begin - dm = DiscreteMeasure(x, w3d) - pdm = ProductDiscreteMeasure([dm]) - @test only(marginals(pdm)) == dm - @test support(pdm) == x - @test weights(pdm) == w3d - - end -end - -@testset "Expectation" begin - dm = DiscreteMeasure(first(x), first(w)) - dms = DiscreteMeasure.(x, w) - pdms = ProductDiscreteMeasure(dms) - dm1d = DiscreteMeasure(first(x), first(w)) - dm3d = DiscreteMeasure(x, w3d) - pdms4d = ProductDiscreteMeasure([dm3d, dm1d]) - - - f(x) = sin(first(x)) - - for μ in (dm, pdms, pdms4d) - @test expectation(f, μ) ≈ weights(μ)' * f.(support(μ)) - end -end - -@testset "Measure Restrictions" begin - for T in (Int64, Float64) - @test clamp_domain(T(-1), 0, 2) == T(0) - @test clamp_domain(T(1), 0, 2) == T(1) - @test clamp_domain(T(3), 0, 2) == T(2) - - @test clamp_weight(T(-1)) == T(0) - @test clamp_weight(T(1), T(2)) == T(1) - @test clamp_weight(T(3)) == T(1) - @test clamp_weight(T(3), T(2)) == T(2) - end - - @testset "Interval Ext" begin - @test clamp_domain(Interval(-2, -1), 0, 2) == ∅ - @test clamp_domain(Interval(-2, 1), 0, 2) == Interval(0, 1) - @test clamp_domain(Interval(0.1, 0.5), 0, 2) == Interval(0.1, 0.5) - @test clamp_domain(Interval(0.5, 5), 0, 2) == Interval(0.5, 2) - @test clamp_domain(Interval(3, 4), 0, 2) == ∅ - end +const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "ALL") +if TEST_GROUP == "Core" || TEST_GROUP == "ALL" + @safetestset "Discrete Measures" include("discrete_measures_tests.jl") end diff --git a/lib/OUQBase/Project.toml b/lib/OUQBase/Project.toml index 43ba8ab..20952ce 100644 --- a/lib/OUQBase/Project.toml +++ b/lib/OUQBase/Project.toml @@ -20,19 +20,28 @@ SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" [compat] +julia = "1.10" DomainSets = "0.7.15" JuMP = "1.23.6" ModelingToolkit = "9.69" NaNMath = "1.1.3" +OptimizationBBO = "0.4" OrderedCollections = "1.7.0" Polynomials = "4.0.13" Reexport = "1.2.2" +SafeTestsets = "0.1" SciMLBase = "2.153" Symbolics = "6.39.1" +Test = "1.10" [sources] CanonicalMoments = { path = "../CanonicalMoments" } DiscreteMeasures = { path = "../DiscreteMeasures" } -[workspace] -projects = ["test"] +[extras] +OptimizationBBO = "3e6eede4-6085-4f62-9a71-46d9bc1eb92b" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["OptimizationBBO", "SafeTestsets", "Test"] diff --git a/lib/OUQBase/test/Project.toml b/lib/OUQBase/test/Project.toml deleted file mode 100644 index 5b835d0..0000000 --- a/lib/OUQBase/test/Project.toml +++ /dev/null @@ -1,6 +0,0 @@ -[deps] -ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" -OUQBase = "01930cae-99d2-7439-8f4f-ace2ece9f1b9" -OptimizationBBO = "3e6eede4-6085-4f62-9a71-46d9bc1eb92b" -Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/lib/OUQBase/test/runtests.jl b/lib/OUQBase/test/runtests.jl index 1d35e73..ce1e825 100644 --- a/lib/OUQBase/test/runtests.jl +++ b/lib/OUQBase/test/runtests.jl @@ -1 +1,8 @@ -include("FloodProblem/Q_only.jl") +using SafeTestsets +using Test + +const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "ALL") + +if TEST_GROUP == "Core" || TEST_GROUP == "ALL" + @safetestset "Flood Problem (Q only)" include("FloodProblem/Q_only.jl") +end diff --git a/src/OptimalUncertaintyQuantification.jl b/src/OptimalUncertaintyQuantification.jl index 301133f..6468157 100644 --- a/src/OptimalUncertaintyQuantification.jl +++ b/src/OptimalUncertaintyQuantification.jl @@ -1,3 +1,6 @@ module OptimalUncertaintyQuantification +using Reexport +@reexport using OUQBase + end # module diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 0000000..cb9439e --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,70 @@ +using Pkg +using SafeTestsets, Test + +const GROUP = get(ENV, "GROUP", "All") + +@time begin + # Detect sublibrary test groups. + # GROUP can be a bare sublibrary name (Core test group) or + # "{sublibrary}_{TEST_GROUP}" for any custom group (e.g., QA, GPU, etc.). + # Sublibraries declare their groups in test/test_groups.toml. + lib_dir = joinpath(dirname(@__DIR__), "lib") + + # Check if GROUP matches a sublibrary, possibly with a _SUFFIX for the test group. + # Scan underscores right-to-left to find the longest matching sublibrary prefix. + function _detect_sublibrary_group(group, lib_dir) + isdir(joinpath(lib_dir, group)) && return (group, "Core") + for i in length(group):-1:1 + if group[i] == '_' && isdir(joinpath(lib_dir, group[1:(i - 1)])) + return (group[1:(i - 1)], group[(i + 1):end]) + end + end + return (group, "Core") + end + base_group, test_group = _detect_sublibrary_group(GROUP, lib_dir) + + if isdir(joinpath(lib_dir, base_group)) + Pkg.activate(joinpath(lib_dir, base_group)) + # On Julia < 1.11, the [sources] section in Project.toml is not supported. + # Manually Pkg.develop local path dependencies so CI tests the PR branch code. + # We resolve transitively: each developed dependency's own [sources] are also + # developed, so that sibling packages reachable only through another + # sublibrary's sources are correctly found. + if VERSION < v"1.11.0-DEV.0" + developed = Set{String}() + # Never develop the active project: when sublibraries cyclically + # reference each other via [sources], the transitive walk below would + # otherwise try to `Pkg.develop` the active project itself, which Pkg + # refuses with "package has the same name or UUID as the active + # project". + push!(developed, normpath(joinpath(lib_dir, base_group))) + specs = Pkg.PackageSpec[] + queue = [joinpath(lib_dir, base_group)] + while !isempty(queue) + pkg_dir = popfirst!(queue) + toml_path = joinpath(pkg_dir, "Project.toml") + isfile(toml_path) || continue + toml = Pkg.TOML.parsefile(toml_path) + if haskey(toml, "sources") + for (dep_name, source_spec) in toml["sources"] + if source_spec isa Dict && haskey(source_spec, "path") + dep_path = normpath(joinpath(pkg_dir, source_spec["path"])) + if isdir(dep_path) && !(dep_path in developed) + push!(developed, dep_path) + @info "Queuing local source dependency" dep_name dep_path + push!(specs, Pkg.PackageSpec(path = dep_path)) + push!(queue, dep_path) + end + end + end + end + end + isempty(specs) || Pkg.develop(specs) + end + withenv("OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP" => test_group) do + Pkg.test(base_group, julia_args = ["--check-bounds=auto", "--compiled-modules=yes", "--depwarn=yes"], force_latest_compatible_version = false, allow_reresolve = true) + end + elseif GROUP == "All" || GROUP == "Core" + @time @safetestset "Umbrella Load" include("umbrella_load.jl") + end +end # @time diff --git a/test/umbrella_load.jl b/test/umbrella_load.jl new file mode 100644 index 0000000..6ec983c --- /dev/null +++ b/test/umbrella_load.jl @@ -0,0 +1,12 @@ +using OptimalUncertaintyQuantification +using Test + +@testset "Umbrella module loads" begin + @test OptimalUncertaintyQuantification isa Module + # The umbrella re-exports OUQBase's public interface. + @test isdefined(OptimalUncertaintyQuantification, :OUQSystem) + @test isdefined(OptimalUncertaintyQuantification, :AdmissibleSet) + # Re-exported names are available unqualified in the test scope. + @test OUQSystem isa Type + @test AdmissibleSet isa Type +end From efc619e25e908066d871b872b866a058be0e4df3 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Sun, 7 Jun 2026 19:19:17 -0400 Subject: [PATCH 02/12] Use canonical capitalized test-group names in sublib runtests Match SciML/OrdinaryDiffEq.jl casing: the sublibrary test/runtests.jl default group and comparison strings used all-caps "ALL"; canonicalize to Title-case "All" so they line up with the umbrella runner, which passes "Core"/"All" verbatim through OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP. Casing-only change; dispatch logic, env var names, and fallback chains are unchanged. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/CanonicalMoments/test/runtests.jl | 4 ++-- lib/DiscreteMeasures/test/runtests.jl | 4 ++-- lib/OUQBase/test/runtests.jl | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/CanonicalMoments/test/runtests.jl b/lib/CanonicalMoments/test/runtests.jl index 1ad6f5d..47eaa77 100644 --- a/lib/CanonicalMoments/test/runtests.jl +++ b/lib/CanonicalMoments/test/runtests.jl @@ -1,9 +1,9 @@ using SafeTestsets using Test -const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "ALL") +const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "All") -if TEST_GROUP == "Core" || TEST_GROUP == "ALL" +if TEST_GROUP == "Core" || TEST_GROUP == "All" @safetestset "Orthogonal Polynomial Roots" include("orthopoly_roots.jl") @safetestset "Moment Sequence" include("moment_sequence.jl") end diff --git a/lib/DiscreteMeasures/test/runtests.jl b/lib/DiscreteMeasures/test/runtests.jl index a98281f..21d62c7 100644 --- a/lib/DiscreteMeasures/test/runtests.jl +++ b/lib/DiscreteMeasures/test/runtests.jl @@ -1,8 +1,8 @@ using SafeTestsets using Test -const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "ALL") +const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "All") -if TEST_GROUP == "Core" || TEST_GROUP == "ALL" +if TEST_GROUP == "Core" || TEST_GROUP == "All" @safetestset "Discrete Measures" include("discrete_measures_tests.jl") end diff --git a/lib/OUQBase/test/runtests.jl b/lib/OUQBase/test/runtests.jl index ce1e825..f0e9f4c 100644 --- a/lib/OUQBase/test/runtests.jl +++ b/lib/OUQBase/test/runtests.jl @@ -1,8 +1,8 @@ using SafeTestsets using Test -const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "ALL") +const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "All") -if TEST_GROUP == "Core" || TEST_GROUP == "ALL" +if TEST_GROUP == "Core" || TEST_GROUP == "All" @safetestset "Flood Problem (Q only)" include("FloodProblem/Q_only.jl") end From ed1b238df4940f552fe680ba81510a89dbfa3633 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Sun, 7 Jun 2026 20:30:47 -0400 Subject: [PATCH 03/12] Add canonical config files, LICENSE.md renames, and fix typos Rename LICENSE->LICENSE.md (root + add to 3 sublibs), rename _typos.toml->.typos.toml with [default.extend-words], add .codecov.yml (comment:false), and complete .gitignore to match OrdinaryDiffEq canonical form. Fix genuine source/comment typos surfaced by typos so SpellCheck stays green without silencing real misspellings. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .codecov.yml | 1 + .gitignore | 14 +++++++++++-- _typos.toml => .typos.toml | 4 ++++ LICENSE => LICENSE.md | 0 lib/CanonicalMoments/LICENSE.md | 21 +++++++++++++++++++ .../examples/1_Stenger_flood.jl | 2 +- .../examples/multivariate_example.jl | 4 ++-- lib/CanonicalMoments/ext/ForwardDiffExt.jl | 4 ++-- .../src/measure_transform_algs.jl | 2 +- lib/CanonicalMoments/src/moment_sequences.jl | 2 +- lib/DiscreteMeasures/LICENSE.md | 21 +++++++++++++++++++ lib/OUQBase/LICENSE.md | 21 +++++++++++++++++++ .../winkler_extremal_measures.jl | 6 +++--- 13 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 .codecov.yml rename _typos.toml => .typos.toml (62%) rename LICENSE => LICENSE.md (100%) create mode 100644 lib/CanonicalMoments/LICENSE.md create mode 100644 lib/DiscreteMeasures/LICENSE.md create mode 100644 lib/OUQBase/LICENSE.md diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..69cb760 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1 @@ +comment: false diff --git a/.gitignore b/.gitignore index c64c6f3..036bd31 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,14 @@ +*.jl.cov +*.jl.*.cov +*.jl.mem +*.jl.*.mem Manifest.toml -.vscode +*/Manifest.toml *.DS_Store -*/Manifest.toml \ No newline at end of file +.vscode +profile.pb.gz +.*.swp +LocalPreferences.toml + +docs/build +.claude/ diff --git a/_typos.toml b/.typos.toml similarity index 62% rename from _typos.toml rename to .typos.toml index 31f6b47..5f9f145 100644 --- a/_typos.toml +++ b/.typos.toml @@ -1,3 +1,7 @@ +[default.extend-words] +# Abbreviation for "Theorem" used in citation references (e.g. "Dette Thm 1.3.2"). +Thm = "Thm" + [files] # Machine-generated Pluto.jl HTML exports; the only hits are author surnames # (e.g. "Fons van der Plas") in embedded boilerplate, not editable prose. diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/lib/CanonicalMoments/LICENSE.md b/lib/CanonicalMoments/LICENSE.md new file mode 100644 index 0000000..dd2d5f4 --- /dev/null +++ b/lib/CanonicalMoments/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 SciML Open Source Scientific Machine Learning + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/CanonicalMoments/examples/1_Stenger_flood.jl b/lib/CanonicalMoments/examples/1_Stenger_flood.jl index 65da7b9..410387e 100644 --- a/lib/CanonicalMoments/examples/1_Stenger_flood.jl +++ b/lib/CanonicalMoments/examples/1_Stenger_flood.jl @@ -45,7 +45,7 @@ pof_ = pof(ql, qu, c1, g_h(2), p1_free) pof2_ = pof(ql, qu, c2, g_h(2), p2_free) ## Compare to Stenger -### 1 Moment Contstraint +### 1 Moment Constraint threshold = 4 p1_free = [fill(0.5, 2) for i in 1:4] pof_ = pof(ql, qu, c1, g_h(threshold), p1_free) diff --git a/lib/CanonicalMoments/examples/multivariate_example.jl b/lib/CanonicalMoments/examples/multivariate_example.jl index 6a80627..a9d6f5f 100644 --- a/lib/CanonicalMoments/examples/multivariate_example.jl +++ b/lib/CanonicalMoments/examples/multivariate_example.jl @@ -21,8 +21,8 @@ raws = [ [54.5, 8911 / 3.0, 647569 / 4.0], #Zm ] -raw_seqences = RawMomentSequence.(raws, lb, ub) -transforms = DiscreteMeasureTransform1.(raw_seqences) +raw_sequences = RawMomentSequence.(raws, lb, ub) +transforms = DiscreteMeasureTransform1.(raw_sequences) p_frees = fill.([0.1, 0.2, 0.3, 0.4], length.(raws) .+ 1) diff --git a/lib/CanonicalMoments/ext/ForwardDiffExt.jl b/lib/CanonicalMoments/ext/ForwardDiffExt.jl index 3af7203..520f96a 100644 --- a/lib/CanonicalMoments/ext/ForwardDiffExt.jl +++ b/lib/CanonicalMoments/ext/ForwardDiffExt.jl @@ -14,11 +14,11 @@ function simple_real_roots( P = Polynomial(C) numerator = -Polynomial(∂C) - denomenator = Polynomials.derivative(P) + denominator = Polynomials.derivative(P) X = root_solver(P) return map(X) do xi - 𝕋(xi, numerator(xi) / denomenator(xi)) + 𝕋(xi, numerator(xi) / denominator(xi)) end end diff --git a/lib/CanonicalMoments/src/measure_transform_algs.jl b/lib/CanonicalMoments/src/measure_transform_algs.jl index 24b899c..db5fc24 100644 --- a/lib/CanonicalMoments/src/measure_transform_algs.jl +++ b/lib/CanonicalMoments/src/measure_transform_algs.jl @@ -12,7 +12,7 @@ Base.show(io::IO, alg::AbstractSupportAlg) = print(io, "support_alg") """ PolyRootsSupportAlg <: AbstractSupportAlg -An algorithm for computing the support of a measure by finding the roots of the denominator of teh Stieltjes transform. See Eq. 3.6.3 [1]. The default solver is set to `DEFAULT_ROOT_SOLVER`. +An algorithm for computing the support of a measure by finding the roots of the denominator of the Stieltjes transform. See Eq. 3.6.3 [1]. The default solver is set to `DEFAULT_ROOT_SOLVER`. Note, that the polynomial is guaranteed to have real, unique roots. diff --git a/lib/CanonicalMoments/src/moment_sequences.jl b/lib/CanonicalMoments/src/moment_sequences.jl index 26654e7..34c35da 100644 --- a/lib/CanonicalMoments/src/moment_sequences.jl +++ b/lib/CanonicalMoments/src/moment_sequences.jl @@ -137,7 +137,7 @@ end Checks if the distribution represented by the canonical moment sequence is symmetric around its mean. -This uses the property that odd-numbered canonical moments are ``1/2`` for symmetric distributions. See Corrollary 1.3.4 of [1]. +This uses the property that odd-numbered canonical moments are ``1/2`` for symmetric distributions. See Corollary 1.3.4 of [1]. [1] Dette, H., and Studden, W. J., “The Theory of Canonical Moments with Applications in Statistics, Probability, and Analysis,” Wiley-Interscience, New York, 1997. diff --git a/lib/DiscreteMeasures/LICENSE.md b/lib/DiscreteMeasures/LICENSE.md new file mode 100644 index 0000000..dd2d5f4 --- /dev/null +++ b/lib/DiscreteMeasures/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 SciML Open Source Scientific Machine Learning + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/OUQBase/LICENSE.md b/lib/OUQBase/LICENSE.md new file mode 100644 index 0000000..dd2d5f4 --- /dev/null +++ b/lib/OUQBase/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 SciML Open Source Scientific Machine Learning + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/OUQBase/src/reduction_transformations/winkler_extremal_measures.jl b/lib/OUQBase/src/reduction_transformations/winkler_extremal_measures.jl index 12c3fd9..ee9673a 100644 --- a/lib/OUQBase/src/reduction_transformations/winkler_extremal_measures.jl +++ b/lib/OUQBase/src/reduction_transformations/winkler_extremal_measures.jl @@ -51,7 +51,7 @@ end # We need to provide: # 1) right DiscreteMeasure/ProductDiscreteMeasure and the right set of full random variable group the random variable of the constraint belongs to. # 2) right optim_vars_sub -# Possible other things like dispatch on reduction_algorithm or backen d, but for now it is JuMP and WinklerExtremalMeasures. +# Possible other things like dispatch on reduction_algorithm or backend, but for now it is JuMP and WinklerExtremalMeasures. # We can rewrite it using map_vars_to_group. # 1D case: @@ -186,7 +186,7 @@ function convert_objective!( jump_on_var = @variable(optim_model, binary = true) #var_name = Symbol(:x, group_name, i, :_on) var_name = Symbol(:on, i) - set_name(jump_on_var, String(var_name)) # Binary indicator variable: 1 if support point i satisified probability condition + set_name(jump_on_var, String(var_name)) # Binary indicator variable: 1 if support point i satisfied probability condition optim_model[var_name] = jump_on_var #support_to_jump_binary_dict[substitute(support(discrete_measure)[i], optim_to_jump_dict)] = jump_on_var @@ -298,7 +298,7 @@ function construct_optimization_problem( _discrete_measure_map = discrete_measure_map(ouq_sys, ouq_sys.reduction_data, Symbolic()) # We want flat vectors even in multidimensional DM cases: - # This may fail if you have a product measure of discrete measures of multiple random varaibles. + # This may fail if you have a product measure of discrete measures of multiple random variables. weight_vars, support_vars = collect(flatten(flatten(weights.(values(_discrete_measure_map))))), collect(flatten(flatten(support.(values(_discrete_measure_map))))) # TODO: Modify to use `extract_decision_vars` From 49952254abc52acb265e52cec1e4809ed48ecc10 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Sun, 7 Jun 2026 20:32:02 -0400 Subject: [PATCH 04/12] Add sublibrary READMEs and test_groups.toml Add canonical component READMEs (Zulip + Global Docs + ColPrac + SciML Style badges, "component of the OptimalUncertaintyQuantification.jl monorepo" wording) to each lib/* sublibrary, and declare each sublibrary's real test groups in test/test_groups.toml. All three sublibraries only have a Core group (no QA/Aqua tests exist), tested across lts/1.11/1/pre. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/CanonicalMoments/README.md | 10 ++++++++++ lib/CanonicalMoments/test/test_groups.toml | 2 ++ lib/DiscreteMeasures/README.md | 10 ++++++++++ lib/DiscreteMeasures/test/test_groups.toml | 2 ++ lib/OUQBase/README.md | 10 ++++++++++ lib/OUQBase/test/test_groups.toml | 2 ++ 6 files changed, 36 insertions(+) create mode 100644 lib/CanonicalMoments/README.md create mode 100644 lib/CanonicalMoments/test/test_groups.toml create mode 100644 lib/DiscreteMeasures/README.md create mode 100644 lib/DiscreteMeasures/test/test_groups.toml create mode 100644 lib/OUQBase/README.md create mode 100644 lib/OUQBase/test/test_groups.toml diff --git a/lib/CanonicalMoments/README.md b/lib/CanonicalMoments/README.md new file mode 100644 index 0000000..dd95fab --- /dev/null +++ b/lib/CanonicalMoments/README.md @@ -0,0 +1,10 @@ +# CanonicalMoments.jl + +[![Join the chat at https://julialang.zulipchat.com #sciml-bridged](https://img.shields.io/static/v1?label=Zulip&message=chat&color=9558b2&labelColor=389826)](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) +[![Global Docs](https://img.shields.io/badge/docs-SciML-blue.svg)](https://docs.sciml.ai/OptimalUncertaintyQuantification/stable/) + +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) + +CanonicalMoments.jl is a component of the [OptimalUncertaintyQuantification.jl](https://github.com/SciML/OptimalUncertaintyQuantification.jl) monorepo. It provides canonical moment sequences, orthogonal polynomial root solvers, and the Stieltjes-transform machinery used to map moment information to discrete measures. +While completely independent and usable on its own, users wanting the full Optimal Uncertainty Quantification suite should use [OptimalUncertaintyQuantification.jl](https://github.com/SciML/OptimalUncertaintyQuantification.jl). diff --git a/lib/CanonicalMoments/test/test_groups.toml b/lib/CanonicalMoments/test/test_groups.toml new file mode 100644 index 0000000..b88daba --- /dev/null +++ b/lib/CanonicalMoments/test/test_groups.toml @@ -0,0 +1,2 @@ +[Core] +versions = ["lts", "1.11", "1", "pre"] diff --git a/lib/DiscreteMeasures/README.md b/lib/DiscreteMeasures/README.md new file mode 100644 index 0000000..64c46b5 --- /dev/null +++ b/lib/DiscreteMeasures/README.md @@ -0,0 +1,10 @@ +# DiscreteMeasures.jl + +[![Join the chat at https://julialang.zulipchat.com #sciml-bridged](https://img.shields.io/static/v1?label=Zulip&message=chat&color=9558b2&labelColor=389826)](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) +[![Global Docs](https://img.shields.io/badge/docs-SciML-blue.svg)](https://docs.sciml.ai/OptimalUncertaintyQuantification/stable/) + +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) + +DiscreteMeasures.jl is a component of the [OptimalUncertaintyQuantification.jl](https://github.com/SciML/OptimalUncertaintyQuantification.jl) monorepo. It provides the discrete and product discrete measure types, expectation operators, and support/weight utilities that the rest of the suite builds on. +While completely independent and usable on its own, users wanting the full Optimal Uncertainty Quantification suite should use [OptimalUncertaintyQuantification.jl](https://github.com/SciML/OptimalUncertaintyQuantification.jl). diff --git a/lib/DiscreteMeasures/test/test_groups.toml b/lib/DiscreteMeasures/test/test_groups.toml new file mode 100644 index 0000000..b88daba --- /dev/null +++ b/lib/DiscreteMeasures/test/test_groups.toml @@ -0,0 +1,2 @@ +[Core] +versions = ["lts", "1.11", "1", "pre"] diff --git a/lib/OUQBase/README.md b/lib/OUQBase/README.md new file mode 100644 index 0000000..dabd893 --- /dev/null +++ b/lib/OUQBase/README.md @@ -0,0 +1,10 @@ +# OUQBase.jl + +[![Join the chat at https://julialang.zulipchat.com #sciml-bridged](https://img.shields.io/static/v1?label=Zulip&message=chat&color=9558b2&labelColor=389826)](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) +[![Global Docs](https://img.shields.io/badge/docs-SciML-blue.svg)](https://docs.sciml.ai/OptimalUncertaintyQuantification/stable/) + +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) + +OUQBase.jl is a component of the [OptimalUncertaintyQuantification.jl](https://github.com/SciML/OptimalUncertaintyQuantification.jl) monorepo. It holds the core Optimal Uncertainty Quantification interface: admissible sets, OUQ systems and problems, and the reduction transformations (Winkler extremal measures and Stenger canonical moments) that turn an OUQ problem into a solvable optimization. +While completely independent and usable on its own, users wanting the full Optimal Uncertainty Quantification suite should use [OptimalUncertaintyQuantification.jl](https://github.com/SciML/OptimalUncertaintyQuantification.jl). diff --git a/lib/OUQBase/test/test_groups.toml b/lib/OUQBase/test/test_groups.toml new file mode 100644 index 0000000..b88daba --- /dev/null +++ b/lib/OUQBase/test/test_groups.toml @@ -0,0 +1,2 @@ +[Core] +versions = ["lts", "1.11", "1", "pre"] From f6db09e2b849d79be3acd37d36293e35bd63412c Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Sun, 7 Jun 2026 20:33:50 -0400 Subject: [PATCH 05/12] Add Documentation, DocPreviewCleanup, Downstream workflows and TagBot root job Add the central @v1 Documentation.yml and DocPreviewCleanup.yml callers and a canonical IntegrationTest (Downstream.yml) matching OrdinaryDiffEq's embedded form (empty matrix no-op until registered downstream consumers exist). Add the root TagBot-OptimalUncertaintyQuantification job alongside the existing subpackages matrix to match the canonical TagBot structure. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/DocPreviewCleanup.yml | 17 +++++++ .github/workflows/Documentation.yml | 12 +++++ .github/workflows/Downstream.yml | 62 +++++++++++++++++++++++++ .github/workflows/TagBot.yml | 10 ++++ 4 files changed, 101 insertions(+) create mode 100644 .github/workflows/DocPreviewCleanup.yml create mode 100644 .github/workflows/Documentation.yml create mode 100644 .github/workflows/Downstream.yml diff --git a/.github/workflows/DocPreviewCleanup.yml b/.github/workflows/DocPreviewCleanup.yml new file mode 100644 index 0000000..7b69480 --- /dev/null +++ b/.github/workflows/DocPreviewCleanup.yml @@ -0,0 +1,17 @@ +name: Doc Preview Cleanup + +on: + pull_request: + types: [closed] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: write + +jobs: + doc-preview-cleanup: + uses: "SciML/.github/.github/workflows/docs-preview-cleanup.yml@v1" + secrets: "inherit" diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml new file mode 100644 index 0000000..aba0b1d --- /dev/null +++ b/.github/workflows/Documentation.yml @@ -0,0 +1,12 @@ +name: Documentation +on: + push: + branches: + - main + tags: '*' + pull_request: +jobs: + build-and-deploy-docs: + name: "Documentation" + uses: "SciML/.github/.github/workflows/documentation.yml@v1" + secrets: "inherit" diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml new file mode 100644 index 0000000..923a053 --- /dev/null +++ b/.github/workflows/Downstream.yml @@ -0,0 +1,62 @@ +name: IntegrationTest +on: + push: + branches: [main] + tags: [v*] + pull_request: +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +jobs: + test: + name: ${{ matrix.package.repo }}/${{ matrix.package.group }}/${{ matrix.julia-version }} + runs-on: ${{ matrix.os }} + env: + GROUP: ${{ matrix.package.group }} + strategy: + fail-fast: false + matrix: + julia-version: ['1.10'] + os: [ubuntu-latest] + # OptimalUncertaintyQuantification.jl currently has no registered + # downstream consumers. The matrix is intentionally empty so this job + # is a no-op; add `{user: ..., repo: ..., group: ...}` entries here as + # downstream packages adopt it. An empty matrix skips the job cleanly + # rather than failing. + package: ${{ fromJSON('[]') }} + + steps: + - uses: actions/checkout@v6 + - uses: julia-actions/setup-julia@v3 + with: + version: ${{ matrix.julia-version }} + arch: x64 + - name: Clone Downstream + uses: actions/checkout@v6 + with: + repository: ${{ matrix.package.user }}/${{ matrix.package.repo }} + path: downstream + - name: Load this and run the downstream tests + shell: julia --color=yes --project=downstream {0} + run: | + using Pkg + try + Pkg.develop(map(path ->Pkg.PackageSpec.(;path="lib/$(path)"), readdir("./lib"))); + Pkg.test(coverage=true) # resolver may fail with test time deps + catch err + err isa Pkg.Resolve.ResolverError || rethrow() + # If we can't resolve that means this is incompatible by SemVer and this is fine + # It means we marked this as a breaking change, so we don't need to worry about + # mistakenly introducing a breaking change, as we have intentionally made one + @info "Not compatible with this release. No problem." exception=err + exit(0) # Exit immediately, as a success + end + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v6 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: lcov.info + fail_ci_if_error: false + disable_safe_directory: true diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index 2cadafa..3a3f9f0 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -23,6 +23,16 @@ permissions: statuses: read jobs: + TagBot-OptimalUncertaintyQuantification: + if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' + runs-on: ubuntu-latest + steps: + - name: Tag OptimalUncertaintyQuantification + uses: JuliaRegistries/TagBot@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ssh: ${{ secrets.DOCUMENTER_KEY }} + TagBot-Subpackages: if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' runs-on: ubuntu-latest From 3af232d33295a4e2e074aee66d6eeddda0320d64 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Sun, 7 Jun 2026 20:39:11 -0400 Subject: [PATCH 06/12] Canonicalize root CI cache/env and add umbrella docs wiring Add event-split Julia depot cache (push read+write, PR restore-only with matching restore-keys) and the JULIA_DEPOT_PATH pin to the root CI.yml to match OrdinaryDiffEq's canonical CI. Add umbrella docs/ wiring: docs/Project.toml (umbrella + sublibraries via [sources] with [compat]), docs/make.jl, docs/pages.jl, and docs/src/index.md. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/CI.yml | 39 +++++++++++++++++++++++- docs/Project.toml | 19 ++++++++++++ docs/make.jl | 32 ++++++++++++++++++++ docs/pages.jl | 3 ++ docs/src/index.md | 65 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 docs/Project.toml create mode 100644 docs/make.jl create mode 100644 docs/pages.jl create mode 100644 docs/src/index.md diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9c33a8f..d655ad6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -22,6 +22,15 @@ jobs: contents: read runs-on: ubuntu-latest timeout-minutes: 120 + env: + # Pin the depot path so the registry-refresh step and the subsequent + # julia-actions/julia-{buildpkg,runtest} steps all read and write the + # same depot on self-hosted runners (where the runner may otherwise + # inject ~/github-runners//.julia for some steps but not others, + # causing "expected package X to be registered" failures when General is + # refreshed into ~/.julia but resolve happens against the per-runner + # depot). + JULIA_DEPOT_PATH: ~/.julia strategy: fail-fast: false matrix: @@ -37,7 +46,35 @@ jobs: - uses: julia-actions/setup-julia@v3 with: version: ${{ matrix.version }} - - uses: julia-actions/cache@v3 + # Split cache restore/save by event to work around recurring GitHub + # Actions cache upload auth failures (Azure SAS "Server failed to + # authenticate the request"). PRs only restore from a recent main push + # cache; only main push runs write the cache. continue-on-error keeps a + # transient cache backend hiccup from flunking an otherwise green job. + - name: Cache Julia depot (push only — read+write) + if: github.event_name == 'push' + uses: julia-actions/cache@v3 + continue-on-error: true + with: + cache-compiled: 'false' + - name: Cache Julia depot (PR — restore only) + if: github.event_name != 'push' + uses: actions/cache/restore@v5 + continue-on-error: true + with: + path: | + ~/.julia/artifacts + ~/.julia/packages + ~/.julia/registries + ~/.julia/scratchspaces + ~/.julia/logs + # Primary key intentionally won't match (run_id is unique); the + # restore-keys prefix matches julia-actions/cache's key scheme + # `${cache-name};os=${runner.os};${matrix...}` so we pull the most + # recent cache from a main push for this same matrix cell. + key: julia-cache;workflow=${{ github.workflow }};job=${{ github.job }};os=${{ runner.os }};group=${{ matrix.group }};version=${{ matrix.version }};run_id=${{ github.run_id }};run_attempt=${{ github.run_attempt }} + restore-keys: | + julia-cache;workflow=${{ github.workflow }};job=${{ github.job }};os=${{ runner.os }};group=${{ matrix.group }};version=${{ matrix.version }}; - name: Develop local path deps (Julia < 1.11) shell: julia --color=yes --project=. {0} run: | diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 0000000..7b80851 --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,19 @@ +[deps] +CanonicalMoments = "58d2c334-1a3c-4862-bb37-9012b9e58a38" +DiscreteMeasures = "7766d772-2108-41ee-a4bd-11c51440a39b" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +OUQBase = "01930cae-99d2-7439-8f4f-ace2ece9f1b9" +OptimalUncertaintyQuantification = "91ab1271-1799-4997-981e-07ad84422b0d" + +[sources] +CanonicalMoments = {path = "../lib/CanonicalMoments"} +DiscreteMeasures = {path = "../lib/DiscreteMeasures"} +OUQBase = {path = "../lib/OUQBase"} +OptimalUncertaintyQuantification = {path = ".."} + +[compat] +CanonicalMoments = "0.1" +DiscreteMeasures = "0.1" +Documenter = "1" +OUQBase = "0.1" +OptimalUncertaintyQuantification = "0.1" diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 0000000..104ab53 --- /dev/null +++ b/docs/make.jl @@ -0,0 +1,32 @@ +using Documenter, OptimalUncertaintyQuantification +using OUQBase +using CanonicalMoments +using DiscreteMeasures + +cp(joinpath(@__DIR__, "Project.toml"), joinpath(@__DIR__, "src", "assets", "Project.toml"), force = true) + +# Keep pages.jl separate for the DiffEqDocs.jl build +include("pages.jl") + +makedocs( + sitename = "OptimalUncertaintyQuantification.jl", + authors = "Avinash Subramanian, Benjamin Chung, Adam Gerlach et al.", + clean = true, + doctest = false, + modules = [ + OptimalUncertaintyQuantification, + OUQBase, + CanonicalMoments, + DiscreteMeasures, + ], + warnonly = [:docs_block, :missing_docs, :eval_block], + format = Documenter.HTML( + canonical = "https://docs.sciml.ai/OptimalUncertaintyQuantification/stable/", + ), + pages = pages +) + +deploydocs( + repo = "github.com/SciML/OptimalUncertaintyQuantification.jl"; + push_preview = true +) diff --git a/docs/pages.jl b/docs/pages.jl new file mode 100644 index 0000000..1260bf6 --- /dev/null +++ b/docs/pages.jl @@ -0,0 +1,3 @@ +pages = [ + "OptimalUncertaintyQuantification.jl: Optimal Uncertainty Quantification" => "index.md", +] diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..2a343aa --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,65 @@ +# OptimalUncertaintyQuantification.jl: Optimal Uncertainty Quantification + +**DISTRIBUTION STATEMENT A. Approved for public release: distribution unlimited. Case Number: AFRL-2024-5455. Cleared 10/2/2024.** + +The Optimal Uncertainty Quantification (OUQ) algorithm provides a means of computing +the bounds of the expectations of quantities of interest despite not having complete +knowledge of the probability distribution of the uncertain variables. This is achieved +by finding the worst/best case distributions in some set ``\mathcal{A}`` of possible +distributions given the knowledge available. + +OptimalUncertaintyQuantification.jl implements the OUQ algorithm and its convex and +"moment class" forms in the Julia programming language, using techniques based on +*complete* and *rigorous* global methods in order to bound the effects of finite +computation on the OUQ bounds. + +## Installation + +To install OptimalUncertaintyQuantification.jl, use the Julia package manager: + +```julia +using Pkg +Pkg.add("OptimalUncertaintyQuantification") +``` + +## Contributing + +- Please refer to the + [SciML ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://github.com/SciML/ColPrac/blob/master/README.md) + for guidance on PRs, issues, and other matters relating to contributing to SciML. +- See the [SciML Style Guide](https://github.com/SciML/SciMLStyle) for common coding practices and other style decisions. +- There are a few community forums: + - The #diffeq-bridged and #sciml-bridged channels in the + [Julia Slack](https://julialang.org/slack/) + - The #diffeq-bridged and #sciml-bridged channels in the + [Julia Zulip](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) + - On the [Julia Discourse forums](https://discourse.julialang.org) + - See also [SciML Community page](https://sciml.ai/community/) + +## Reproducibility + +```@raw html +
The documentation of this SciML package was built using these direct dependencies, +``` + +```@example +using Pkg # hide +Pkg.status() # hide +``` + +```@raw html +
+``` + +```@raw html +
and using this machine and Julia version. +``` + +```@example +using InteractiveUtils # hide +versioninfo() # hide +``` + +```@raw html +
+``` From c0cf757f644a3b8d268fe0ed016f323211a60bbe Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Sun, 7 Jun 2026 20:43:23 -0400 Subject: [PATCH 07/12] Add missing [compat] bounds to sublibrary deps Add ADD-only [compat] entries for previously unbounded dependencies: CanonicalMoments weakdeps ChainRulesCore ("1") and ForwardDiff ("0.10, 1"); OUQBase deps CanonicalMoments/DiscreteMeasures ("0.1"), Optimization ("5"), PolynomialRoots ("1"), and SymbolicUtils ("3"). Bounds are set at or below the versions the umbrella and sublibrary test targets currently resolve (verified by instantiate/resolve on Julia 1.11), so no existing resolve is constrained out and no floor is lowered. CanonicalMoments Core tests pass (271/271) under the new bounds. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/CanonicalMoments/Project.toml | 2 ++ lib/OUQBase/Project.toml | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/CanonicalMoments/Project.toml b/lib/CanonicalMoments/Project.toml index 3c149ee..0ebc55f 100644 --- a/lib/CanonicalMoments/Project.toml +++ b/lib/CanonicalMoments/Project.toml @@ -25,7 +25,9 @@ SymbolicsExt = "Symbolics" [compat] julia = "1.10" +ChainRulesCore = "1" DiscreteMeasures = "0.1" +ForwardDiff = "0.10, 1" InteractiveUtils = "1.10" IntervalArithmetic = "0.18 - 0.20, =0.20.9" LinearAlgebra = "1.10" diff --git a/lib/OUQBase/Project.toml b/lib/OUQBase/Project.toml index 20952ce..e37e2e4 100644 --- a/lib/OUQBase/Project.toml +++ b/lib/OUQBase/Project.toml @@ -21,16 +21,21 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" [compat] julia = "1.10" +CanonicalMoments = "0.1" +DiscreteMeasures = "0.1" DomainSets = "0.7.15" JuMP = "1.23.6" ModelingToolkit = "9.69" NaNMath = "1.1.3" +Optimization = "5" OptimizationBBO = "0.4" OrderedCollections = "1.7.0" +PolynomialRoots = "1" Polynomials = "4.0.13" Reexport = "1.2.2" -SafeTestsets = "0.1" SciMLBase = "2.153" +SafeTestsets = "0.1" +SymbolicUtils = "3" Symbolics = "6.39.1" Test = "1.10" From caf4395a3d6575abb8a6b45f0645f54a77d1c74a Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Sun, 7 Jun 2026 20:44:54 -0400 Subject: [PATCH 08/12] Ensure docs assets dir exists before copying Project.toml mkpath the docs/src/assets directory in make.jl before cp so the documentation build does not error when the directory is absent. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/make.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/make.jl b/docs/make.jl index 104ab53..b08e616 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -3,6 +3,7 @@ using OUQBase using CanonicalMoments using DiscreteMeasures +mkpath(joinpath(@__DIR__, "src", "assets")) cp(joinpath(@__DIR__, "Project.toml"), joinpath(@__DIR__, "src", "assets", "Project.toml"), force = true) # Keep pages.jl separate for the DiffEqDocs.jl build From f012429011377aaa7a9370dd0b80b1a01de5f878 Mon Sep 17 00:00:00 2001 From: "Chris Rackauckas (Claude)" Date: Mon, 8 Jun 2026 03:36:57 -0400 Subject: [PATCH 09/12] test: move tests into canonical test// folders (Core) Restructure the root umbrella test and all three sublibrary test suites (CanonicalMoments, DiscreteMeasures, OUQBase) into per-group test// folders, matching the OrdinaryDiffEq/NonlinearSolve canonical GROUP-FOLDER layout. Every test now lives in exactly one group folder: - test/Core/umbrella_load.jl - lib/CanonicalMoments/test/Core/{orthopoly_roots,moment_sequence,stenger_setup}.jl - lib/DiscreteMeasures/test/Core/discrete_measures_tests.jl - lib/OUQBase/test/Core/FloodProblem/*.jl All include() paths in the dispatchers are updated to the Core/ prefix. No tests are added, removed, skipped, or loosened; the same @safetestset set runs from the new paths. All sublibs run only the Core group (no QA tools / no dep-adding groups), so no per-group Project.toml is needed and Core remains part of the base/All run. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/CanonicalMoments/test/{ => Core}/moment_sequence.jl | 0 lib/CanonicalMoments/test/{ => Core}/orthopoly_roots.jl | 0 lib/CanonicalMoments/test/{ => Core}/stenger_setup.jl | 0 lib/DiscreteMeasures/test/{ => Core}/discrete_measures_tests.jl | 0 lib/OUQBase/test/{ => Core}/FloodProblem/2D_independent.jl | 0 lib/OUQBase/test/{ => Core}/FloodProblem/Q_only.jl | 0 lib/OUQBase/test/{ => Core}/FloodProblem/full_dependent.jl | 0 lib/OUQBase/test/{ => Core}/FloodProblem/full_independent.jl | 0 .../test/{ => Core}/FloodProblem/test_canonical_moments.jl | 0 test/{ => Core}/umbrella_load.jl | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename lib/CanonicalMoments/test/{ => Core}/moment_sequence.jl (100%) rename lib/CanonicalMoments/test/{ => Core}/orthopoly_roots.jl (100%) rename lib/CanonicalMoments/test/{ => Core}/stenger_setup.jl (100%) rename lib/DiscreteMeasures/test/{ => Core}/discrete_measures_tests.jl (100%) rename lib/OUQBase/test/{ => Core}/FloodProblem/2D_independent.jl (100%) rename lib/OUQBase/test/{ => Core}/FloodProblem/Q_only.jl (100%) rename lib/OUQBase/test/{ => Core}/FloodProblem/full_dependent.jl (100%) rename lib/OUQBase/test/{ => Core}/FloodProblem/full_independent.jl (100%) rename lib/OUQBase/test/{ => Core}/FloodProblem/test_canonical_moments.jl (100%) rename test/{ => Core}/umbrella_load.jl (100%) diff --git a/lib/CanonicalMoments/test/moment_sequence.jl b/lib/CanonicalMoments/test/Core/moment_sequence.jl similarity index 100% rename from lib/CanonicalMoments/test/moment_sequence.jl rename to lib/CanonicalMoments/test/Core/moment_sequence.jl diff --git a/lib/CanonicalMoments/test/orthopoly_roots.jl b/lib/CanonicalMoments/test/Core/orthopoly_roots.jl similarity index 100% rename from lib/CanonicalMoments/test/orthopoly_roots.jl rename to lib/CanonicalMoments/test/Core/orthopoly_roots.jl diff --git a/lib/CanonicalMoments/test/stenger_setup.jl b/lib/CanonicalMoments/test/Core/stenger_setup.jl similarity index 100% rename from lib/CanonicalMoments/test/stenger_setup.jl rename to lib/CanonicalMoments/test/Core/stenger_setup.jl diff --git a/lib/DiscreteMeasures/test/discrete_measures_tests.jl b/lib/DiscreteMeasures/test/Core/discrete_measures_tests.jl similarity index 100% rename from lib/DiscreteMeasures/test/discrete_measures_tests.jl rename to lib/DiscreteMeasures/test/Core/discrete_measures_tests.jl diff --git a/lib/OUQBase/test/FloodProblem/2D_independent.jl b/lib/OUQBase/test/Core/FloodProblem/2D_independent.jl similarity index 100% rename from lib/OUQBase/test/FloodProblem/2D_independent.jl rename to lib/OUQBase/test/Core/FloodProblem/2D_independent.jl diff --git a/lib/OUQBase/test/FloodProblem/Q_only.jl b/lib/OUQBase/test/Core/FloodProblem/Q_only.jl similarity index 100% rename from lib/OUQBase/test/FloodProblem/Q_only.jl rename to lib/OUQBase/test/Core/FloodProblem/Q_only.jl diff --git a/lib/OUQBase/test/FloodProblem/full_dependent.jl b/lib/OUQBase/test/Core/FloodProblem/full_dependent.jl similarity index 100% rename from lib/OUQBase/test/FloodProblem/full_dependent.jl rename to lib/OUQBase/test/Core/FloodProblem/full_dependent.jl diff --git a/lib/OUQBase/test/FloodProblem/full_independent.jl b/lib/OUQBase/test/Core/FloodProblem/full_independent.jl similarity index 100% rename from lib/OUQBase/test/FloodProblem/full_independent.jl rename to lib/OUQBase/test/Core/FloodProblem/full_independent.jl diff --git a/lib/OUQBase/test/FloodProblem/test_canonical_moments.jl b/lib/OUQBase/test/Core/FloodProblem/test_canonical_moments.jl similarity index 100% rename from lib/OUQBase/test/FloodProblem/test_canonical_moments.jl rename to lib/OUQBase/test/Core/FloodProblem/test_canonical_moments.jl diff --git a/test/umbrella_load.jl b/test/Core/umbrella_load.jl similarity index 100% rename from test/umbrella_load.jl rename to test/Core/umbrella_load.jl From 7fa9273aaa9fbdea184afdc56352da245eae7376 Mon Sep 17 00:00:00 2001 From: "Chris Rackauckas (Claude)" Date: Mon, 8 Jun 2026 03:38:57 -0400 Subject: [PATCH 10/12] ci: normalize Core version matrix to [lts, 1, pre]; finish Core/ include paths Drop the non-canonical "1.11" entry from every sublibrary's [Core] test group (CanonicalMoments, DiscreteMeasures, OUQBase) and from the root CI.yml version matrix, so the base/Core groups run the canonical [lts, 1, pre] set matching the OrdinaryDiffEq/NonlinearSolve monorepo convention. There are no dep-adding, QA, or GPU groups in this repo, so no group is excluded from All and no per-group Project.toml or runner matrix is introduced. Also completes the test// restructuring by updating the include() paths in each runtests.jl dispatcher to the Core/ prefix (the prior commit moved the files but the path edits land here). No tests are added, removed, skipped, or loosened; no compat floors are lowered. Verified with the centralized compute_affected_sublibraries.jl --projects-matrix script: it emits exactly Core on [lts, 1, pre] for each of the three sublibs. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/CI.yml | 1 - lib/CanonicalMoments/test/runtests.jl | 6 +++--- lib/CanonicalMoments/test/test_groups.toml | 2 +- lib/DiscreteMeasures/test/runtests.jl | 2 +- lib/DiscreteMeasures/test/test_groups.toml | 2 +- lib/OUQBase/test/runtests.jl | 2 +- lib/OUQBase/test/test_groups.toml | 2 +- test/runtests.jl | 2 +- 8 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d655ad6..13f23c0 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -38,7 +38,6 @@ jobs: - Core version: - 'lts' - - '1.11' - '1' - 'pre' steps: diff --git a/lib/CanonicalMoments/test/runtests.jl b/lib/CanonicalMoments/test/runtests.jl index 47eaa77..ef6b2d7 100644 --- a/lib/CanonicalMoments/test/runtests.jl +++ b/lib/CanonicalMoments/test/runtests.jl @@ -4,8 +4,8 @@ using Test const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "All") if TEST_GROUP == "Core" || TEST_GROUP == "All" - @safetestset "Orthogonal Polynomial Roots" include("orthopoly_roots.jl") - @safetestset "Moment Sequence" include("moment_sequence.jl") + @safetestset "Orthogonal Polynomial Roots" include("Core/orthopoly_roots.jl") + @safetestset "Moment Sequence" include("Core/moment_sequence.jl") end # @safetestset "Root Domain Restriction" begin @@ -52,7 +52,7 @@ end # using IntervalArithmetic # import OptimalUncertaintyQuantification.CanonicalMoments: moments_to_canonical, canonical_to_position -# include("stenger_setup.jl") +# include("Core/stenger_setup.jl") # for (c, setup) in zip((c1, c2, c3), (setup1, setup2, setup3)) # for _setup in setup diff --git a/lib/CanonicalMoments/test/test_groups.toml b/lib/CanonicalMoments/test/test_groups.toml index b88daba..dda1469 100644 --- a/lib/CanonicalMoments/test/test_groups.toml +++ b/lib/CanonicalMoments/test/test_groups.toml @@ -1,2 +1,2 @@ [Core] -versions = ["lts", "1.11", "1", "pre"] +versions = ["lts", "1", "pre"] diff --git a/lib/DiscreteMeasures/test/runtests.jl b/lib/DiscreteMeasures/test/runtests.jl index 21d62c7..bcdf257 100644 --- a/lib/DiscreteMeasures/test/runtests.jl +++ b/lib/DiscreteMeasures/test/runtests.jl @@ -4,5 +4,5 @@ using Test const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "All") if TEST_GROUP == "Core" || TEST_GROUP == "All" - @safetestset "Discrete Measures" include("discrete_measures_tests.jl") + @safetestset "Discrete Measures" include("Core/discrete_measures_tests.jl") end diff --git a/lib/DiscreteMeasures/test/test_groups.toml b/lib/DiscreteMeasures/test/test_groups.toml index b88daba..dda1469 100644 --- a/lib/DiscreteMeasures/test/test_groups.toml +++ b/lib/DiscreteMeasures/test/test_groups.toml @@ -1,2 +1,2 @@ [Core] -versions = ["lts", "1.11", "1", "pre"] +versions = ["lts", "1", "pre"] diff --git a/lib/OUQBase/test/runtests.jl b/lib/OUQBase/test/runtests.jl index f0e9f4c..27353e9 100644 --- a/lib/OUQBase/test/runtests.jl +++ b/lib/OUQBase/test/runtests.jl @@ -4,5 +4,5 @@ using Test const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "All") if TEST_GROUP == "Core" || TEST_GROUP == "All" - @safetestset "Flood Problem (Q only)" include("FloodProblem/Q_only.jl") + @safetestset "Flood Problem (Q only)" include("Core/FloodProblem/Q_only.jl") end diff --git a/lib/OUQBase/test/test_groups.toml b/lib/OUQBase/test/test_groups.toml index b88daba..dda1469 100644 --- a/lib/OUQBase/test/test_groups.toml +++ b/lib/OUQBase/test/test_groups.toml @@ -1,2 +1,2 @@ [Core] -versions = ["lts", "1.11", "1", "pre"] +versions = ["lts", "1", "pre"] diff --git a/test/runtests.jl b/test/runtests.jl index cb9439e..4f9fe49 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -65,6 +65,6 @@ const GROUP = get(ENV, "GROUP", "All") Pkg.test(base_group, julia_args = ["--check-bounds=auto", "--compiled-modules=yes", "--depwarn=yes"], force_latest_compatible_version = false, allow_reresolve = true) end elseif GROUP == "All" || GROUP == "Core" - @time @safetestset "Umbrella Load" include("umbrella_load.jl") + @time @safetestset "Umbrella Load" include("Core/umbrella_load.jl") end end # @time From d07db6dc872c279fad772cf25ecb6d9014fb4666 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Mon, 8 Jun 2026 04:08:32 -0400 Subject: [PATCH 11/12] test(OUQBase): wire previously-orphaned FloodProblem tests into Core group The OUQBase test/runtests.jl only ever included Core/FloodProblem/Q_only.jl; the other four flood-problem files were unreachable since before the monorepo restructure (orphaned upstream). 2D_independent.jl, full_independent.jl, and full_dependent.jl only build OUQSystem fixtures (pushed into Main-scoped problem dictionaries via `isdefined(Main, ...)` guards), and test_canonical_moments.jl consumed those dictionaries through an `include` of a Problems/all_problems.jl aggregator that never existed in the repo (and whose relative path was off by one directory level). Reconstruct the missing test/Problems/all_problems.jl fixture aggregator: it defines the eight problem dictionaries and includes each FloodProblem setup file so they populate it. Fix the include path in test_canonical_moments.jl to reach the aggregator under the new test/Core/ layout, and run both the Q-only smoke solve and the canonical-moments objective assertion at top level in Main (so the Main-scoped fixture dictionaries are visible). Guard Q_only.jl's expensive trailing solve behind a LOAD_OUQ_PROBLEMS_FIXTURE flag so it runs once from its own group entry rather than again when loaded as a fixture. The canonical-moments objective test (@test isapprox(obj_val, 0.6897, atol=0.01)) now runs and passes; all four formerly-orphaned files are exercised as part of the Core group that the sublibrary CI matrix runs on [lts, 1, pre]. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/OUQBase/test/Core/FloodProblem/Q_only.jl | 22 ++++++++------- .../FloodProblem/test_canonical_moments.jl | 2 +- lib/OUQBase/test/Problems/all_problems.jl | 27 +++++++++++++++++++ lib/OUQBase/test/runtests.jl | 8 ++++++ 4 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 lib/OUQBase/test/Problems/all_problems.jl diff --git a/lib/OUQBase/test/Core/FloodProblem/Q_only.jl b/lib/OUQBase/test/Core/FloodProblem/Q_only.jl index 1f37c32..1a47e5e 100644 --- a/lib/OUQBase/test/Core/FloodProblem/Q_only.jl +++ b/lib/OUQBase/test/Core/FloodProblem/Q_only.jl @@ -99,12 +99,16 @@ isdefined(Main, :probability_canonical_moments_problems_analytic) && push!( isdefined(Main, :probability_solutions) && push!(probability_solutions, "Flood Q only" => 0.17) -# Hacky test for now: -ouq_prob = OUQProblem(ouq_sys_probability_canonical_moments, OptimizationModel(), Oracle()) -sol = solve( - ouq_prob.optim_model, - BBO_adaptive_de_rand_1_bin_radiuslimited(); - maxiters = 100, - maxtime = 60.0, - verbose = true, -) +# Smoke-solve the probability problem. Skipped when this file is loaded only as +# a problem fixture (the all_problems.jl aggregator sets this flag) so the +# expensive solve runs once, from its own test group entry. +if !(isdefined(Main, :LOAD_OUQ_PROBLEMS_FIXTURE) && Main.LOAD_OUQ_PROBLEMS_FIXTURE) + ouq_prob = OUQProblem(ouq_sys_probability_canonical_moments, OptimizationModel(), Oracle()) + sol = solve( + ouq_prob.optim_model, + BBO_adaptive_de_rand_1_bin_radiuslimited(); + maxiters = 100, + maxtime = 60.0, + verbose = true, + ) +end diff --git a/lib/OUQBase/test/Core/FloodProblem/test_canonical_moments.jl b/lib/OUQBase/test/Core/FloodProblem/test_canonical_moments.jl index cdc7c1e..52bc925 100644 --- a/lib/OUQBase/test/Core/FloodProblem/test_canonical_moments.jl +++ b/lib/OUQBase/test/Core/FloodProblem/test_canonical_moments.jl @@ -1,7 +1,7 @@ using CanonicalMoments, ModelingToolkit using OUQBase -include(joinpath(@__DIR__, "../../../Problems/all_problems.jl")) +include(joinpath(@__DIR__, "..", "..", "Problems", "all_problems.jl")) name = "Flood full independent" ouq_sys = probability_canonical_moments_problems[name] diff --git a/lib/OUQBase/test/Problems/all_problems.jl b/lib/OUQBase/test/Problems/all_problems.jl new file mode 100644 index 0000000..a70b559 --- /dev/null +++ b/lib/OUQBase/test/Problems/all_problems.jl @@ -0,0 +1,27 @@ +# Shared OUQ problem fixtures for the OUQBase test suite. +# +# Each FloodProblem/.jl setup file builds a family of OUQSystems and +# pushes them into the dictionaries below (guarded by `isdefined(Main, ...)`), +# so a test only needs to `include` this file to get every flood problem keyed +# by name. The setup files redefine their own top-level scratch variables +# (`rand_vars`, `constraints`, `H`, ...), so they must be included into the +# same (Main) scope sequentially. + +expectation_winkler_problems = Dict{String, Any}() +expectation_canonical_moments_problems = Dict{String, Any}() +expectation_canonical_moments_problems_analytic = Dict{String, Any}() +expectation_solutions = Dict{String, Any}() + +probability_winkler_problems = Dict{String, Any}() +probability_canonical_moments_problems = Dict{String, Any}() +probability_canonical_moments_problems_analytic = Dict{String, Any}() +probability_solutions = Dict{String, Any}() + +LOAD_OUQ_PROBLEMS_FIXTURE = true + +const _FLOOD_PROBLEM_DIR = joinpath(@__DIR__, "..", "Core", "FloodProblem") + +include(joinpath(_FLOOD_PROBLEM_DIR, "Q_only.jl")) +include(joinpath(_FLOOD_PROBLEM_DIR, "2D_independent.jl")) +include(joinpath(_FLOOD_PROBLEM_DIR, "full_independent.jl")) +include(joinpath(_FLOOD_PROBLEM_DIR, "full_dependent.jl")) diff --git a/lib/OUQBase/test/runtests.jl b/lib/OUQBase/test/runtests.jl index 27353e9..bdc2e37 100644 --- a/lib/OUQBase/test/runtests.jl +++ b/lib/OUQBase/test/runtests.jl @@ -5,4 +5,12 @@ const TEST_GROUP = get(ENV, "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP", "All" if TEST_GROUP == "Core" || TEST_GROUP == "All" @safetestset "Flood Problem (Q only)" include("Core/FloodProblem/Q_only.jl") + + # The flood-problem fixtures push every OUQSystem into Main-scoped problem + # dictionaries (guarded by `isdefined(Main, ...)`), and the canonical-moments + # objective test reads them back, so this group is built and run at top level + # in Main rather than inside an isolated @safetestset module. + @testset "Flood Problem (canonical moments objective)" begin + include("Core/FloodProblem/test_canonical_moments.jl") + end end From 0a04d7cc02be6f863cffdba30164b423fa16aba7 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Mon, 8 Jun 2026 05:14:01 -0400 Subject: [PATCH 12/12] CI: canonical TagBot thin caller + downgrade-caller cleanup Replace the verbose TagBot.yml with the canonical SciML thin caller (tagbot.yml@v1). The tagbot-subpackages matrix is omitted because none of the lib/ sublibraries (CanonicalMoments, DiscreteMeasures, OUQBase) are registered in the General registry yet, and TagBot errors on unregistered subdirs. Remove the hand-listed skip: inputs (stdlibs + in-repo sublibs) from the Downgrade.yml and DowngradeSublibraries.yml callers; the centralized workflows now auto-populate skip. Drop julia-version: "1.10" to take the central 'lts' default (the minimum-supported floor). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/Downgrade.yml | 2 - .github/workflows/DowngradeSublibraries.yml | 2 - .github/workflows/TagBot.yml | 53 ++------------------- 3 files changed, 5 insertions(+), 52 deletions(-) diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index e189b48..5feb9a6 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -20,7 +20,5 @@ jobs: name: "Downgrade" uses: "SciML/.github/.github/workflows/downgrade.yml@v1" with: - julia-version: "1.10" group: "Core" - skip: "Pkg,TOML,Test,Statistics,LinearAlgebra,SparseArrays,InteractiveUtils,CanonicalMoments,DiscreteMeasures,OUQBase" secrets: "inherit" diff --git a/.github/workflows/DowngradeSublibraries.yml b/.github/workflows/DowngradeSublibraries.yml index fe207b2..60d6d78 100644 --- a/.github/workflows/DowngradeSublibraries.yml +++ b/.github/workflows/DowngradeSublibraries.yml @@ -22,8 +22,6 @@ jobs: uses: "SciML/.github/.github/workflows/sublibrary-downgrade.yml@v1" secrets: "inherit" with: - julia-version: "1.10" - skip: "Pkg,TOML,Test,Statistics,LinearAlgebra,SparseArrays,InteractiveUtils,CanonicalMoments,DiscreteMeasures,OUQBase" group-env-name: "OPTIMALUNCERTAINTYQUANTIFICATION_TEST_GROUP" group-env-value: "Core" # Every lib/* sublibrary is downgrade-tested (projects auto-discovered, no exclusions). diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index 3a3f9f0..9ed2d3e 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -1,52 +1,9 @@ -name: TagBot +name: "TagBot" on: issue_comment: - types: - - created + types: [created] workflow_dispatch: - inputs: - lookback: - default: "3" - -permissions: - actions: read - checks: read - contents: write - deployments: read - issues: read - discussions: read - packages: read - pages: read - pull-requests: read - repository-projects: read - security-events: read - statuses: read - jobs: - TagBot-OptimalUncertaintyQuantification: - if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' - runs-on: ubuntu-latest - steps: - - name: Tag OptimalUncertaintyQuantification - uses: JuliaRegistries/TagBot@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - ssh: ${{ secrets.DOCUMENTER_KEY }} - - TagBot-Subpackages: - if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - package: - - CanonicalMoments - - DiscreteMeasures - - OUQBase - steps: - - name: Tag ${{ matrix.package }} - uses: JuliaRegistries/TagBot@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - ssh: ${{ secrets.DOCUMENTER_KEY }} - subdir: "lib/${{ matrix.package }}" + tagbot: + uses: "SciML/.github/.github/workflows/tagbot.yml@v1" + secrets: "inherit"