From 4eef57916e134653542afa5e88e924b99f3d81e7 Mon Sep 17 00:00:00 2001 From: Vaibhav Dixit Date: Thu, 26 Oct 2023 23:51:34 -0400 Subject: [PATCH 1/4] init implementation --- src/ensemble.jl | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/ensemble.jl diff --git a/src/ensemble.jl b/src/ensemble.jl new file mode 100644 index 000000000..9ef9da0b1 --- /dev/null +++ b/src/ensemble.jl @@ -0,0 +1,13 @@ +struct EnsembleOptimizationProblem{T1} <: SciMLBase.AbstractEnsembleProblem + prob::OptimizationProblem{iip, F, T1} where {iip, F} + u0s::Vector{T1} +end + +function SciMLBase.__init(prob::EnsembleOptimizationProblem{T}, args...; kwargs...) where {T <: OptimizationProblem} + probs = [remake(prob.prob, u0=u0; kwargs...) for u0 in prob.u0s] + return [SciMLBase.__init(prob, args...; kwargs...) for prob in probs] +end + +function SciMLBase.__solve(caches::Vector{OptimizationCache}, args...; kwargs...) + return [SciMLBase.__solve(cache, args...; kwargs...) for cache in caches] +end \ No newline at end of file From b5709fe8ba87c1344455055ddadf2406f8706a75 Mon Sep 17 00:00:00 2001 From: Vaibhav Dixit Date: Fri, 27 Oct 2023 19:40:59 -0400 Subject: [PATCH 2/4] Use EnsembleProblem instead of custom struct --- src/Optimization.jl | 1 + src/ensemble.jl | 21 ++++++++++++++------- test/ensemble.jl | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 test/ensemble.jl diff --git a/src/Optimization.jl b/src/Optimization.jl index da63f8178..54d4903ba 100644 --- a/src/Optimization.jl +++ b/src/Optimization.jl @@ -23,6 +23,7 @@ include("utils.jl") include("function.jl") include("adtypes.jl") include("cache.jl") +include("ensemble.jl") @static if !isdefined(Base, :get_extension) function __init__() diff --git a/src/ensemble.jl b/src/ensemble.jl index 9ef9da0b1..d5a7fe1dc 100644 --- a/src/ensemble.jl +++ b/src/ensemble.jl @@ -1,13 +1,20 @@ -struct EnsembleOptimizationProblem{T1} <: SciMLBase.AbstractEnsembleProblem - prob::OptimizationProblem{iip, F, T1} where {iip, F} - u0s::Vector{T1} +function SciMLBase.EnsembleProblem(prob::OptimizationProblem, u0s::Vector{Vector{T}}; kwargs...) where {T} + prob_func = (prob, i, repeat = nothing) -> remake(prob, u0 = u0s[i]) + return SciMLBase.EnsembleProblem(prob; prob_func, kwargs...) end -function SciMLBase.__init(prob::EnsembleOptimizationProblem{T}, args...; kwargs...) where {T <: OptimizationProblem} - probs = [remake(prob.prob, u0=u0; kwargs...) for u0 in prob.u0s] - return [SciMLBase.__init(prob, args...; kwargs...) for prob in probs] +function SciMLBase.init(prob::EnsembleProblem{T}, args...; kwargs...) where {T <: OptimizationProblem} + SciMLBase.__init(prob, args...; kwargs...) end -function SciMLBase.__solve(caches::Vector{OptimizationCache}, args...; kwargs...) +function SciMLBase.__init(prob::EnsembleProblem{T}, args...; trajectories, kwargs...) where {T <: OptimizationProblem} + return [SciMLBase.__init(prob.prob_func(prob.prob, i), args...; kwargs...) for i in 1:trajectories] +end + +function SciMLBase.solve!(cache::Vector{<:OptimizationCache}; kwargs...) + return [SciMLBase.solve!(cache[i]; kwargs...) for i in eachindex(cache)] +end + +function SciMLBase.__solve(caches::Vector{<:OptimizationCache}, args...; kwargs...) return [SciMLBase.__solve(cache, args...; kwargs...) for cache in caches] end \ No newline at end of file diff --git a/test/ensemble.jl b/test/ensemble.jl new file mode 100644 index 000000000..b05b7e36a --- /dev/null +++ b/test/ensemble.jl @@ -0,0 +1,15 @@ +using Optimization, OptimizationOptimJL, ForwardDiff + +x0 = zeros(2) +rosenbrock(x, p = nothing) = (1 - x[1])^2 + 100 * (x[2] - x[1]^2)^2 +l1 = rosenbrock(x0) + +optf = OptimizationFunction(rosenbrock, Optimization.AutoForwardDiff()) +prob = OptimizationProblem(optf, x0) +sol1 = Optimization.solve(prob, OptimizationOptimJL.BFGS(), maxiters = 5) + + +ensembleprob = Optimization.EnsembleProblem(prob, [x0, x0 .+ rand(2), x0 .+ rand(2), x0 .+ rand(2)]) +sol = Optimization.solve(ensembleprob, OptimizationOptimJL.BFGS(), trajectories = 4, maxiters = 5) + +@test findmin(i -> sol[i].objective, 1:4) < sol1.objective \ No newline at end of file From 299f4cd386867de77e3e2823ea89dae27c23f553 Mon Sep 17 00:00:00 2001 From: Vaibhav Dixit Date: Mon, 30 Oct 2023 12:50:26 -0400 Subject: [PATCH 3/4] Use SciMLBase ensemble solve --- src/ensemble.jl | 15 ++------------- test/ensemble.jl | 9 ++++++--- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/ensemble.jl b/src/ensemble.jl index d5a7fe1dc..54bb265d5 100644 --- a/src/ensemble.jl +++ b/src/ensemble.jl @@ -3,18 +3,7 @@ function SciMLBase.EnsembleProblem(prob::OptimizationProblem, u0s::Vector{Vector return SciMLBase.EnsembleProblem(prob; prob_func, kwargs...) end -function SciMLBase.init(prob::EnsembleProblem{T}, args...; kwargs...) where {T <: OptimizationProblem} - SciMLBase.__init(prob, args...; kwargs...) -end - -function SciMLBase.__init(prob::EnsembleProblem{T}, args...; trajectories, kwargs...) where {T <: OptimizationProblem} - return [SciMLBase.__init(prob.prob_func(prob.prob, i), args...; kwargs...) for i in 1:trajectories] -end -function SciMLBase.solve!(cache::Vector{<:OptimizationCache}; kwargs...) - return [SciMLBase.solve!(cache[i]; kwargs...) for i in eachindex(cache)] +function SciMLBase.solve(prob::EnsembleProblem{T}, args...; kwargs...) where {T <: OptimizationProblem} + return SciMLBase.__solve(prob, args...; kwargs...) end - -function SciMLBase.__solve(caches::Vector{<:OptimizationCache}, args...; kwargs...) - return [SciMLBase.__solve(cache, args...; kwargs...) for cache in caches] -end \ No newline at end of file diff --git a/test/ensemble.jl b/test/ensemble.jl index b05b7e36a..43ffb2eaf 100644 --- a/test/ensemble.jl +++ b/test/ensemble.jl @@ -1,4 +1,4 @@ -using Optimization, OptimizationOptimJL, ForwardDiff +using Optimization, OptimizationOptimJL, ForwardDiff, Test x0 = zeros(2) rosenbrock(x, p = nothing) = (1 - x[1])^2 + 100 * (x[2] - x[1]^2)^2 @@ -10,6 +10,9 @@ sol1 = Optimization.solve(prob, OptimizationOptimJL.BFGS(), maxiters = 5) ensembleprob = Optimization.EnsembleProblem(prob, [x0, x0 .+ rand(2), x0 .+ rand(2), x0 .+ rand(2)]) -sol = Optimization.solve(ensembleprob, OptimizationOptimJL.BFGS(), trajectories = 4, maxiters = 5) -@test findmin(i -> sol[i].objective, 1:4) < sol1.objective \ No newline at end of file +sol = Optimization.solve(ensembleprob, OptimizationOptimJL.BFGS(), EnsembleThreads(), trajectories = 4, maxiters = 5) +@test findmin(i -> sol[i].objective, 1:4)[1] < sol1.objective + +sol = Optimization.solve(ensembleprob, OptimizationOptimJL.BFGS(), EnsembleDistributed(), trajectories = 4, maxiters = 5) +@test findmin(i -> sol[i].objective, 1:4)[1] < sol1.objective \ No newline at end of file From 9daba63cf3e87e240c861fedff2c52bf050eef78 Mon Sep 17 00:00:00 2001 From: Vaibhav Dixit Date: Mon, 30 Oct 2023 13:11:20 -0400 Subject: [PATCH 4/4] Add qmc based method for bounded problems --- Project.toml | 1 + src/Optimization.jl | 2 +- src/ensemble.jl | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 431e82167..e5fa453e4 100644 --- a/Project.toml +++ b/Project.toml @@ -13,6 +13,7 @@ LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" ProgressLogging = "33c8b6b6-d38a-422a-b730-caa89a2f386c" +QuasiMonteCarlo = "8a4e6c94-4038-4cdc-81c3-7e6ffdb2a71b" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" Requires = "ae029012-a4dd-5104-9daa-d747884805df" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" diff --git a/src/Optimization.jl b/src/Optimization.jl index 54d4903ba..d67fe9bc9 100644 --- a/src/Optimization.jl +++ b/src/Optimization.jl @@ -12,7 +12,7 @@ if !isdefined(Base, :get_extension) end using Logging, ProgressLogging, ConsoleProgressMonitor, TerminalLoggers, LoggingExtras -using ArrayInterface, Base.Iterators, SparseArrays, LinearAlgebra +using ArrayInterface, Base.Iterators, SparseArrays, LinearAlgebra, QuasiMonteCarlo using Pkg import SciMLBase: OptimizationProblem, OptimizationFunction, ObjSense, diff --git a/src/ensemble.jl b/src/ensemble.jl index 54bb265d5..5319cd5a3 100644 --- a/src/ensemble.jl +++ b/src/ensemble.jl @@ -3,6 +3,16 @@ function SciMLBase.EnsembleProblem(prob::OptimizationProblem, u0s::Vector{Vector return SciMLBase.EnsembleProblem(prob; prob_func, kwargs...) end +function SciMLBase.EnsembleProblem(prob::OptimizationProblem, trajectories::Int; kwargs...) + if prob.lb != nothing && prob.ub != nothing + u0s = QuasiMonteCarlo.sample(trajectories, prob.lb, prob.ub, LatinHypercubeSample()) + prob_func = (prob, i, repeat = nothing) -> remake(prob, u0 = u0s[:, i]) + else + error("EnsembleProblem requires either initial points as second argument or lower and upper bounds to be defined with the trajectories second argument method.") + end + return SciMLBase.EnsembleProblem(prob; prob_func, kwargs...) +end + function SciMLBase.solve(prob::EnsembleProblem{T}, args...; kwargs...) where {T <: OptimizationProblem} return SciMLBase.__solve(prob, args...; kwargs...)