Skip to content

Commit cd6c29a

Browse files
Merge pull request #38 from SciML/u/bfgs
Update Hybrid API and misc cleanups
2 parents d8c0343 + 0224a9e commit cd6c29a

File tree

14 files changed

+240
-139
lines changed

14 files changed

+240
-139
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
matrix:
2121
version:
2222
- '1.6'
23-
- '1.9'
23+
- '1'
2424
os:
2525
- ubuntu-latest
2626
arch:

Project.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ version = "1.0.0-DEV"
55

66
[deps]
77
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
8-
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
8+
DiffEqGPU = "071ae1c0-96b5-11e9-1965-c90190d839ea"
9+
Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
910
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
1011
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
1112
MLDatasets = "eb30cadb-4394-5ae3-aed4-317e484a6458"
@@ -18,7 +19,6 @@ Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
1819
SimpleChains = "de6bee2f-e2f4-4ec7-b6ed-219cc6f6e9e5"
1920
SimpleNonlinearSolve = "727e6d20-b764-4bd8-a329-72de5adea6c7"
2021
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
21-
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"
2222

2323
[compat]
2424
julia = "1.6"
@@ -29,4 +29,4 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
2929
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3030

3131
[targets]
32-
test = ["Test", "StaticArrays", "LinearAlgebra"]
32+
test = ["Test", "StaticArrays", "LinearAlgebra", "Optimization", "ForwardDiff", ]

src/PSOGPU.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ module PSOGPU
33
using SciMLBase, StaticArrays, Setfield, KernelAbstractions
44
using QuasiMonteCarlo, Optimization, SimpleNonlinearSolve, ForwardDiff
55
import Adapt
6-
7-
# import DiffEqGPU: GPUTsit5, vectorized_asolve, make_prob_compatible
6+
import Enzyme: autodiff_deferred, Active, Reverse
7+
import DiffEqGPU: GPUTsit5, vectorized_asolve, make_prob_compatible
88

99
## Use lb and ub either as StaticArray or pass them separately as CuArrays
1010
## Passing as CuArrays makes more sense, or maybe SArray? The based on no. of dimension
@@ -48,11 +48,11 @@ end
4848

4949
include("./algorithms.jl")
5050
include("./utils.jl")
51-
# include("./ode_pso.jl")
51+
include("./ode_pso.jl")
5252
include("./kernels.jl")
5353
include("./lowerlevel_solve.jl")
5454
include("./solve.jl")
55-
include("./lbfgs.jl")
55+
include("./bfgs.jl")
5656
include("./hybrid.jl")
5757

5858
export ParallelPSOKernel,

src/algorithms.jl

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
abstract type PSOAlgorithm end
3-
3+
abstract type HybridPSOAlgorithm{LocalOpt} end
44
"""
55
```julia
66
ParallelPSOKernel(num_particles = 100)
@@ -118,3 +118,28 @@ end
118118

119119
SciMLBase.allowsbounds(::PSOAlgorithm) = true
120120
SciMLBase.allowsconstraints(::PSOAlgorithm) = true
121+
122+
struct LBFGS
123+
threshold::Int
124+
end
125+
126+
function LBFGS(; threshold = 10)
127+
LBFGS(threshold)
128+
end
129+
130+
struct BFGS end
131+
132+
struct HybridPSO{Backend, LocalOpt} <: HybridPSOAlgorithm{LocalOpt}
133+
pso::PSOAlgorithm
134+
local_opt::LocalOpt
135+
backend::Backend
136+
end
137+
138+
function HybridPSO(;
139+
backend = CPU(),
140+
pso = PSOGPU.ParallelPSOKernel(100; global_update = false, backend),
141+
local_opt = LBFGS())
142+
HybridPSO(pso, local_opt, backend)
143+
end
144+
145+
SciMLBase.allowsbounds(::HybridPSOAlgorithm{LocalOpt}) where {LocalOpt} = true

src/bfgs.jl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
function SciMLBase.__solve(prob::SciMLBase.OptimizationProblem,
2+
opt::LBFGS,
3+
args...;
4+
abstol = nothing,
5+
reltol = nothing,
6+
maxiters = 1000,
7+
kwargs...)
8+
f = Base.Fix2(prob.f.f, prob.p)
9+
function _g(θ, _p = nothing)
10+
return ForwardDiff.gradient(f, θ)
11+
end
12+
t0 = time()
13+
nlprob = NonlinearProblem{false}(_g, prob.u0)
14+
nlsol = solve(nlprob,
15+
SimpleLimitedMemoryBroyden(; threshold = opt.threshold, linesearch = Val(true));
16+
maxiters,
17+
abstol,
18+
reltol)
19+
θ = nlsol.u
20+
t1 = time()
21+
22+
SciMLBase.build_solution(SciMLBase.DefaultOptimizationCache(prob.f, prob.p),
23+
opt,
24+
θ,
25+
prob.f(θ, prob.p))
26+
end
27+
28+
function SciMLBase.__solve(prob::SciMLBase.OptimizationProblem,
29+
opt::BFGS,
30+
args...;
31+
abstol = nothing,
32+
reltol = nothing,
33+
maxiters = 1000,
34+
kwargs...)
35+
f = Base.Fix2(prob.f.f, prob.p)
36+
∇f = instantiate_gradient(f, prob.f.adtype)
37+
38+
t0 = time()
39+
nlprob = NonlinearProblem{false}(∇f, prob.u0)
40+
nlsol = solve(nlprob,
41+
SimpleBroyden(; linesearch = Val(true));
42+
maxiters,
43+
abstol,
44+
reltol)
45+
θ = nlsol.u
46+
t1 = time()
47+
48+
SciMLBase.build_solution(SciMLBase.DefaultOptimizationCache(prob.f, prob.p),
49+
opt,
50+
θ,
51+
prob.f(θ, prob.p))
52+
end

src/hybrid.jl

Lines changed: 30 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,53 @@
1-
struct HybridPSOLBFGS
2-
pso::PSOAlgorithm
3-
lbfgs::LBFGS
4-
end
5-
6-
function HybridPSOLBFGS(; pso = PSOGPU.ParallelPSOKernel(100 ; global_update = false), lbfgs = LBFGS())
7-
HybridPSOLBFGS(pso, lbfgs)
8-
end
9-
10-
@kernel function lbfgs_run!(nlcaches, x0s, result)
11-
i = @index(Global, Linear)
12-
# nlcache = reinit!(nlcaches[i], x0s[i])
13-
# @show nlcache.u
14-
res = solve!(nlcaches[i])
15-
# @show res
16-
# @show res.resid
17-
result[i] = res
18-
end
19-
20-
@kernel function simplelbfgs_run!(nlprob, x0s, result)
1+
@kernel function simplebfgs_run!(nlprob, x0s, result, opt, maxiters, abstol, reltol)
212
i = @index(Global, Linear)
223
nlcache = remake(nlprob; u0 = x0s[i])
23-
result[i] = solve(nlcache, SimpleLimitedMemoryBroyden(; threshold = 10))
24-
end
25-
26-
@kernel function simplebfgs_run!(nlprob, x0s, result, opt, maxiters)
27-
i = @index(Global, Linear)
28-
nlcache = remake(nlprob; u0 = x0s[i])
29-
sol = SciMLBase.__solve(nlcache, opt, maxiters = maxiters, abstol = 1f-6, reltol = 1f-6)
4+
sol = solve(nlcache, opt; maxiters, abstol, reltol)
305
result[i] = sol.u
316
end
327

33-
function SciMLBase.__solve(prob::SciMLBase.OptimizationProblem, opt::HybridPSOLBFGS, args...; maxiters = 1000, kwargs...)
8+
function SciMLBase.__solve(prob::SciMLBase.OptimizationProblem,
9+
opt::HybridPSO{Backend, LocalOpt},
10+
args...;
11+
abstol = nothing,
12+
reltol = nothing,
13+
maxiters = 100,
14+
local_maxiters = 10,
15+
kwargs...) where {Backend, LocalOpt <: Union{LBFGS, BFGS}}
3416
t0 = time()
3517
psoalg = opt.pso
36-
lbfgsalg = opt.lbfgs
18+
local_opt = opt.local_opt
19+
backend = opt.backend
3720

3821
sol_pso = solve(prob, psoalg, args...; maxiters, kwargs...)
39-
40-
# @show sol_pso.u
41-
4222
x0s = sol_pso.original
43-
# @show prob.u0
44-
# @show x0s
4523
prob = remake(prob, lb = nothing, ub = nothing)
46-
# @show length(x0s)
47-
# f = Optimization.instantiate_function(prob.f, prob.u0, prob.f.adtype, prob.p, 0)
4824
f = Base.Fix2(prob.f.f, prob.p)
49-
function _g(θ, _p = nothing)
50-
return ForwardDiff.gradient(f , θ)
51-
end
52-
# @show prob.u0
53-
# nlcaches = [init(NonlinearProblem(NonlinearFunction(_g), x0), LimitedMemoryBroyden(; threshold = lbfgsalg.m, linesearch = LiFukushimaLineSearch()))
54-
# for x0 in x0s
55-
# ]
56-
# @show nlcaches[1]
57-
# @show ismutable(nlcaches[1])
58-
backend = lbfgsalg.backend
59-
# kernel = lbfgs_run!(backend)
60-
# result = KernelAbstractions.allocate(lbfgsalg.backend, SciMLBase.NonlinearSolution, length(x0s))
61-
62-
# kernel(nlcaches, x0s, result; ndrange = length(x0s))
63-
64-
# kernel = simplelbfgs_run!(backend)
65-
# result = KernelAbstractions.allocate(backend, SciMLBase.NonlinearSolution, length(x0s))
66-
# nlprob = NonlinearProblem(NonlinearFunction(_g), prob.u0)
67-
# kernel(nlprob, x0s, result; ndrange = length(x0s))
25+
∇f = instantiate_gradient(f, prob.f.adtype)
6826

6927
kernel = simplebfgs_run!(backend)
7028
result = KernelAbstractions.allocate(backend, typeof(prob.u0), length(x0s))
71-
nlprob = NonlinearProblem{false}(_g, prob.u0)
29+
nlprob = NonlinearProblem{false}(∇f, prob.u0)
30+
31+
nlalg = LocalOpt isa LBFGS ?
32+
SimpleLimitedMemoryBroyden(;
33+
threshold = local_opt.threshold,
34+
linesearch = Val(true)) : SimpleBroyden(; linesearch = Val(true))
35+
36+
kernel(nlprob,
37+
x0s,
38+
result,
39+
nlalg,
40+
local_maxiters,
41+
abstol,
42+
reltol;
43+
ndrange = length(x0s))
7244

73-
kernel(nlprob, x0s, result, SimpleLimitedMemoryBroyden(; threshold = lbfgsalg.m, linesearch = Val(true)), maxiters; ndrange = length(x0s))
74-
75-
# @show result
7645
t1 = time()
7746
sol_bfgs = (x -> prob.f(x, prob.p)).(result)
78-
# sol_bfgs = [prob.f(θ, prob.p) for θ in result]
79-
# @show typeof(sol_bfgs)
80-
81-
sol_bfgs = (x -> isnan(x) ? Inf32 : x).(sol_bfgs)
82-
# @show minimum(sol_bfgs)
47+
sol_bfgs = (x -> isnan(x) ? convert(eltype(prob.u0), Inf) : x).(sol_bfgs)
8348

8449
minobj, ind = findmin(sol_bfgs)
8550

8651
SciMLBase.build_solution(SciMLBase.DefaultOptimizationCache(prob.f, prob.p), opt,
87-
view(result, ind) , minobj)
52+
view(result, ind), minobj)
8853
end

src/lbfgs.jl

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/solve.jl

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ function get_pos(particle)
22
return particle.position
33
end
44

5-
function SciMLBase.__solve(prob::OptimizationProblem, opt::PSOAlgorithm, args...; kwargs...)
5+
function SciMLBase.__solve(prob::OptimizationProblem,
6+
opt::PSOAlgorithm,
7+
args...;
8+
maxiters = 100,
9+
kwargs...)
610
lb, ub = check_init_bounds(prob)
711
lb, ub = check_init_bounds(prob)
812
prob = remake(prob; lb = lb, ub = ub)
913

10-
gbest, particles = pso_solve(prob, opt)
14+
gbest, particles = pso_solve(prob, opt, args...; maxiters, kwargs...)
1115
particles_positions = get_pos.(particles)
1216
SciMLBase.build_solution(SciMLBase.DefaultOptimizationCache(prob.f, prob.p), opt,
1317
gbest.position, prob.f(gbest.position, prob.p), original = particles_positions)
@@ -44,7 +48,12 @@ function pso_solve(prob::OptimizationProblem,
4448
args...;
4549
kwargs...)
4650
init_gbest, particles = init_particles(prob, opt, typeof(prob.u0))
47-
gbest, particles = vectorized_solve!(prob, init_gbest, particles, opt, args...; kwargs...)
51+
gbest, particles = vectorized_solve!(prob,
52+
init_gbest,
53+
particles,
54+
opt,
55+
args...;
56+
kwargs...)
4857
gbest, particles
4958
end
5059

@@ -65,6 +74,6 @@ function pso_solve(prob::OptimizationProblem,
6574
copyto!(gpu_particles, particles)
6675
init_gbest = init_gbest
6776

68-
gbest = vectorized_solve!(prob, init_gbest, gpu_particles, opt; kwargs...)
77+
gbest = vectorized_solve!(prob, init_gbest, gpu_particles, opt, args...; kwargs...)
6978
gbest
7079
end

src/utils.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,11 @@ Based on the paper: Particle swarm optimization method for constrained optimizat
193193
penalty = h(T(iter)) * penalty
194194
penalty
195195
end
196+
197+
@inline function instantiate_gradient(f, adtype::AutoForwardDiff)
198+
(θ, p) -> ForwardDiff.gradient(f, θ)
199+
end
200+
201+
@inline function instantiate_gradient(f, adtype::AutoEnzyme)
202+
(θ, p) -> autodiff_deferred(Reverse, f, Active, Active(θ))[1][1]
203+
end

test/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[deps]
22
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
33
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
4+
Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba"
45
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
56
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
67
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"

0 commit comments

Comments
 (0)