diff --git a/.github/workflows/DowngradeSublibraries.yml b/.github/workflows/DowngradeSublibraries.yml index 33f9c61cc..d9f6216b6 100644 --- a/.github/workflows/DowngradeSublibraries.yml +++ b/.github/workflows/DowngradeSublibraries.yml @@ -23,6 +23,81 @@ jobs: # ModelingToolkit 11). Stdlibs and in-repo sublibraries are auto-populated by # the centralized workflow. skip: "SymbolicAnalysis" + exclude: "OptimizationQuadDIRECT" group-env-name: "OPTIMIZATION_TEST_GROUP" group-env-value: "Core" - # Every lib/* sublibrary is downgrade-tested (projects auto-discovered, no exclusions). + # OptimizationQuadDIRECT has an unregistered URL source and is tested below + # with a manifest repair step after downgrade-compat. + + downgrade-quaddirect: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: julia-actions/setup-julia@v3 + with: + version: "lts" + - name: "Resolve numeric Julia version for downgrade-compat" + run: echo "JULIA_MM=$(julia --startup-file=no -e 'print(VERSION.major, ".", VERSION.minor)')" >> "$GITHUB_ENV" + - name: "Set sublibrary test group" + run: echo "OPTIMIZATION_TEST_GROUP=Core" >> "$GITHUB_ENV" + - name: "Enumerate Julia standard libraries" + run: | + names="$(julia --startup-file=no -e ' + using Pkg + ns = String[] + for v in values(Pkg.Types.stdlibs()) + push!(ns, v isa AbstractString ? v : first(v)) + end + println(join(sort(unique(ns)), ",")) + ')" + echo "STDLIB_SKIP=$names" >> "$GITHUB_ENV" + - name: "Compute effective downgrade skip list" + run: | + sublibs="" + for d in lib/*/; do + d="${d%/}" + [ -f "$d/Project.toml" ] || continue + name="$(basename "$d")" + if [ -z "$sublibs" ]; then sublibs="$name"; else sublibs="$sublibs,$name"; fi + done + effective="$STDLIB_SKIP" + [ -n "$sublibs" ] && effective="$effective,$sublibs" + src_names="$(julia --startup-file=no -e ' + using TOML + src = get(TOML.parsefile(ARGS[1]), "sources", Dict()) + println(join(collect(keys(src)), ",")) + ' "lib/OptimizationQuadDIRECT/Project.toml")" + [ -n "$src_names" ] && effective="$effective,$src_names" + effective="$effective,SymbolicAnalysis" + echo "EFFECTIVE_SKIP=$effective" >> "$GITHUB_ENV" + - uses: julia-actions/julia-downgrade-compat@v2 + with: + projects: "lib/OptimizationQuadDIRECT" + skip: ${{ env.EFFECTIVE_SKIP }} + julia_version: ${{ env.JULIA_MM }} + - name: "Restore URL-sourced packages" + run: | + julia --startup-file=no --project=lib/OptimizationQuadDIRECT -e ' + using Pkg, TOML + + project = TOML.parsefile(joinpath(ARGS[1], "Project.toml")) + deps = get(project, "deps", Dict()) + specs = Pkg.PackageSpec[] + for (pkg, src) in get(project, "sources", Dict()) + src isa AbstractDict || continue + haskey(deps, pkg) || continue + haskey(src, "url") || continue + kwargs = Dict{Symbol, Any}(:name => pkg, :url => src["url"]) + haskey(src, "rev") && (kwargs[:rev] = src["rev"]) + haskey(src, "subdir") && (kwargs[:subdir] = src["subdir"]) + push!(specs, Pkg.PackageSpec(; kwargs...)) + end + isempty(specs) || Pkg.add(specs; preserve = Pkg.PRESERVE_ALL) + ' "lib/OptimizationQuadDIRECT" + - uses: julia-actions/julia-buildpkg@v1 + with: + project: "lib/OptimizationQuadDIRECT" + - uses: julia-actions/julia-runtest@v1 + with: + project: "lib/OptimizationQuadDIRECT" + allow_reresolve: false diff --git a/lib/OptimizationBBO/Project.toml b/lib/OptimizationBBO/Project.toml index c79d67019..071c15751 100644 --- a/lib/OptimizationBBO/Project.toml +++ b/lib/OptimizationBBO/Project.toml @@ -8,6 +8,9 @@ OptimizationBase = "bca83a33-5cc9-4baa-983d-23429ab6bcbb" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +[weakdeps] +LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688" + [extras] Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" @@ -19,6 +22,7 @@ OptimizationBase = {path = "../OptimizationBase"} [compat] BlackBoxOptim = "0.6.3" +LogExpFunctions = "0.3.28" OptimizationBase = "5" Pkg = "1" Random = "1.10" diff --git a/lib/OptimizationBase/Project.toml b/lib/OptimizationBase/Project.toml index bcc673caf..7b966e487 100644 --- a/lib/OptimizationBase/Project.toml +++ b/lib/OptimizationBase/Project.toml @@ -48,7 +48,7 @@ OptimizationLBFGSB = {path = "../OptimizationLBFGSB"} OptimizationManopt = {path = "../OptimizationManopt"} [compat] -ADTypes = "1.14" +ADTypes = "1.18" Aqua = "0.8" ArrayInterface = "7.6" BenchmarkTools = "1" diff --git a/lib/OptimizationMOI/Project.toml b/lib/OptimizationMOI/Project.toml index bd095cc48..18e686850 100644 --- a/lib/OptimizationMOI/Project.toml +++ b/lib/OptimizationMOI/Project.toml @@ -33,12 +33,12 @@ Juniper = "0.9" LinearAlgebra = "1" MathOptInterface = "1.45" ModelingToolkit = "11" -ModelingToolkitBase = "1" +ModelingToolkitBase = "1.17" NLopt = "1" OptimizationBase = "5.1" -Reexport = "1.2" +Reexport = "1.2.2" ReverseDiff = "1" -SciMLBase = "2.130, 3" +SciMLBase = "2.146, 3" SciMLStructures = "1" SparseArrays = "1.6" SymbolicIndexingInterface = "0.3.46" diff --git a/lib/OptimizationMadNLP/Project.toml b/lib/OptimizationMadNLP/Project.toml index 24b218329..ba331d8d2 100644 --- a/lib/OptimizationMadNLP/Project.toml +++ b/lib/OptimizationMadNLP/Project.toml @@ -26,10 +26,10 @@ OptimizationBase = "5.1" OptimizationNLPModels = "1.3" Pkg = "1" Random = "1.10" -Reexport = "1.2" +Reexport = "1.2.2" ReverseDiff = "1" SafeTestsets = "0.1" -SciMLBase = "2.130, 3" +SciMLBase = "2.146, 3" SparseArrays = "1.10" SymbolicIndexingInterface = "0.3.46" Symbolics = "7" diff --git a/lib/OptimizationMadNLP/test/core_tests.jl b/lib/OptimizationMadNLP/test/core_tests.jl index d3a2ae401..6fc7513c2 100644 --- a/lib/OptimizationMadNLP/test/core_tests.jl +++ b/lib/OptimizationMadNLP/test/core_tests.jl @@ -364,16 +364,18 @@ end MadNLP.CompactLBFGS, MadNLP.ExactHessian, # For comparison ] + initial_u = variant == MadNLP.ExactHessian ? x0[1:2] : x0 # Only provide gradients, no Hessian needed for LBFGS ad = AutoForwardDiff() # First-order AD is sufficient optfunc = OptimizationFunction(extended_rosenbrock, ad) - prob = OptimizationProblem(optfunc, x0, nothing) + prob = OptimizationProblem(optfunc, initial_u, nothing) if variant == MadNLP.ExactHessian + # Exact Hessian is only a comparison here; the LBFGS path keeps the larger problem. # Use second-order AD for exact Hessian ad = SecondOrder(AutoForwardDiff(), AutoForwardDiff()) optfunc = OptimizationFunction(extended_rosenbrock, ad) - prob = OptimizationProblem(optfunc, x0, nothing) + prob = OptimizationProblem(optfunc, initial_u, nothing) end opt = MadNLPOptimizer( @@ -458,8 +460,8 @@ end return x0 end - @testset "N=5 electrons with $approx" for approx in [MadNLP.CompactLBFGS, MadNLP.ExactHessian] - np = 5 + @testset "electrons on sphere with $approx" for approx in [MadNLP.CompactLBFGS, MadNLP.ExactHessian] + np = approx == MadNLP.ExactHessian ? 2 : 5 x0 = init_electrons_on_sphere(np) if approx == MadNLP.CompactLBFGS @@ -499,10 +501,9 @@ end unit_sphere_constraints(cons_vals, sol.u, nothing) @test all(abs.(cons_vals) .< 1.0e-5) - # Known optimal energy for 5 electrons on unit sphere + # Known optimal energy for electrons on unit sphere # Reference: https://en.wikipedia.org/wiki/Thomson_problem - # Configuration: Triangular dipyramid (trigonal bipyramid, D3h symmetry) - expected_energy = 6.474691495 + expected_energy = np == 2 ? 0.5 : 6.474691495 @test isapprox(sol.objective, expected_energy, rtol = 1.0e-3) # Verify minimum distance between electrons @@ -520,17 +521,14 @@ end @test min_dist > 0.5 # Electrons should be well-separated end - @testset verbose = true "LBFGS vs Exact Hessian" begin - # Test with moderate size to show LBFGS efficiency - np = 10 # Gyroelongated square dipyramid configuration - x0 = init_electrons_on_sphere(np) - + @testset verbose = true "LBFGS and Exact Hessian" begin results = [] - for (name, approx, ad) in [ + for (name, approx, ad, np, expected_energy) in [ ( "CompactLBFGS", MadNLP.CompactLBFGS, - AutoForwardDiff(), + AutoForwardDiff(), 10, + 32.71694946, ) ( "ExactHessian", @@ -538,8 +536,11 @@ end SecondOrder( AutoForwardDiff(), AutoZygote() ), + 2, + 0.5, ) ] + x0 = init_electrons_on_sphere(np) optfunc = OptimizationFunction( coulomb_potential, ad, cons = unit_sphere_constraints @@ -562,6 +563,7 @@ end objective = sol.objective, iterations = sol.stats.iterations, success = SciMLBase.successful_retcode(sol), + expected = expected_energy, ) ) end @@ -569,11 +571,10 @@ end # All methods should converge @test all(r[2].success for r in values(results)) - # All should find similar objective values (gyroelongated square dipyramid energy) + # Check objective values against the corresponding Thomson problem energy. # Reference: https://en.wikipedia.org/wiki/Thomson_problem - objectives = [r[2].objective for r in values(results)] - @testset "$(results[i][1])" for (i, o) in enumerate(objectives) - @test o ≈ 32.71694946 rtol = 1.0e-2 + @testset "$(name)" for (name, result) in results + @test result.objective ≈ result.expected rtol = 1.0e-2 end # LBFGS methods typically need more iterations but less cost per iteration diff --git a/lib/OptimizationNLPModels/Project.toml b/lib/OptimizationNLPModels/Project.toml index 78433dfd4..37075fb0e 100644 --- a/lib/OptimizationNLPModels/Project.toml +++ b/lib/OptimizationNLPModels/Project.toml @@ -27,10 +27,10 @@ OptimizationLBFGSB = "1.2" OptimizationMOI = "1" OptimizationOptimJL = "0.4.10" Pkg = "1" -Reexport = "1.2" +Reexport = "1.2.2" ReverseDiff = "1" SafeTestsets = "0.1" -SciMLBase = "2.130, 3" +SciMLBase = "2.146, 3" SparseArrays = "1.10.0" Test = "1.10" Zygote = "0.6, 0.7" diff --git a/lib/OptimizationNOMAD/Project.toml b/lib/OptimizationNOMAD/Project.toml index 8e705e4c4..793d248d3 100644 --- a/lib/OptimizationNOMAD/Project.toml +++ b/lib/OptimizationNOMAD/Project.toml @@ -8,6 +8,9 @@ NOMAD = "02130f1c-4665-5b79-af82-ff1385104aa0" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +[weakdeps] +LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688" + [extras] Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" @@ -18,6 +21,7 @@ OptimizationBase = {path = "../OptimizationBase"} [compat] Pkg = "1" +LogExpFunctions = "0.3.28" SafeTestsets = "0.1" julia = "1.10" OptimizationBase = "5" diff --git a/lib/OptimizationSciPy/Project.toml b/lib/OptimizationSciPy/Project.toml index 87566f549..bccc4cfdf 100644 --- a/lib/OptimizationSciPy/Project.toml +++ b/lib/OptimizationSciPy/Project.toml @@ -23,7 +23,7 @@ ForwardDiff = "0.10, 1" ModelingToolkit = "11" OptimizationBase = "5.1" Pkg = "1" -PythonCall = "0.9.24" +PythonCall = "0.9.30" Random = "1.10" Reexport = "1.2" ReverseDiff = "1" diff --git a/lib/SimpleOptimization/Project.toml b/lib/SimpleOptimization/Project.toml index ec6b254f7..6dbb09b9f 100644 --- a/lib/SimpleOptimization/Project.toml +++ b/lib/SimpleOptimization/Project.toml @@ -6,6 +6,7 @@ authors = ["Utkarsh and contributors"] [weakdeps] ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" +DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" [deps] OptimizationBase = "bca83a33-5cc9-4baa-983d-23429ab6bcbb" @@ -29,7 +30,8 @@ SimpleOptimizationEnzymeExt = "Enzyme" SimpleOptimizationForwardDiffExt = "ForwardDiff" [compat] -ADTypes = "1.14" +ADTypes = "1.18" +DifferentiationInterface = "0.7.13" Enzyme = "0.13" ForwardDiff = "0.10, 1" LinearAlgebra = "1.10" @@ -38,7 +40,7 @@ Pkg = "1" Reexport = "1.2" SafeTestsets = "0.1" SciMLBase = "2.122.1, 3" -SimpleNonlinearSolve = "1, 2" +SimpleNonlinearSolve = "2.12" Test = "1.10" julia = "1.10"