Skip to content

Commit 82dff1c

Browse files
committed
Start towards caching and perf optimizations
1 parent 1641249 commit 82dff1c

File tree

4 files changed

+147
-25
lines changed

4 files changed

+147
-25
lines changed

Project.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
1515
Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba"
1616
QuasiMonteCarlo = "8a4e6c94-4038-4cdc-81c3-7e6ffdb2a71b"
1717
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
18+
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1819
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1920
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
2021
SimpleChains = "de6bee2f-e2f4-4ec7-b6ed-219cc6f6e9e5"

src/PSOGPU.jl

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import KernelAbstractions: @atomic, @atomicreplace, @atomicswap
88
using QuasiMonteCarlo
99
import DiffEqGPU: GPUTsit5, make_prob_compatible, vectorized_solve, vectorized_asolve
1010

11+
using Reexport
12+
@reexport using SciMLBase
13+
1114
## Use lb and ub either as StaticArray or pass them separately as CuArrays
1215
## Passing as CuArrays makes more sense, or maybe SArray? The based on no. of dimension
1316
struct SPSOParticle{T1, T2 <: eltype(T1)}
@@ -68,5 +71,5 @@ include("./bfgs.jl")
6871
include("./hybrid.jl")
6972

7073
export ParallelPSOKernel,
71-
ParallelSyncPSOKernel, ParallelPSOArray, SerialPSO, OptimizationProblem, solve
74+
ParallelSyncPSOKernel, ParallelPSOArray, SerialPSO
7275
end

src/solve.jl

+58
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,64 @@ function get_pos(particle)
22
return particle.position
33
end
44

5+
mutable struct PSOCache{TP, TAlg, TPart, TGbest}
6+
prob::TP
7+
alg::TAlg
8+
particles::TPart
9+
gbest::TGbest
10+
end
11+
12+
function SciMLBase.init(
13+
prob::OptimizationProblem, opt::ParallelPSOKernel, args...; kwargs...)
14+
backend = opt.backend
15+
@assert prob.u0 isa SArray
16+
17+
## initialize cache
18+
19+
## Bounds check
20+
lb, ub = check_init_bounds(prob)
21+
lb, ub = check_init_bounds(prob)
22+
prob = remake(prob; lb = lb, ub = ub)
23+
24+
init_gbest, particles = init_particles(prob, opt, typeof(prob.u0))
25+
26+
# TODO: Do the equivalent of cu()/roc()
27+
particles_eltype = eltype(particles) === Float64 ? Float32 : eltype(particles)
28+
gpu_particles = KernelAbstractions.allocate(backend,
29+
particles_eltype,
30+
size(particles))
31+
copyto!(gpu_particles, particles)
32+
gpu_init_gbest = KernelAbstractions.allocate(backend, typeof(init_gbest), (1,))
33+
copyto!(gpu_init_gbest, [init_gbest])
34+
return PSOCache{
35+
typeof(prob), typeof(opt), typeof(gpu_particles), typeof(gpu_init_gbest)}(
36+
prob, opt, gpu_particles, gpu_init_gbest)
37+
end
38+
39+
function SciMLBase.solve!(
40+
cache::PSOCache, opt::ParallelPSOKernel, args...; maxiters = 100, kwargs...)
41+
prob = cache.prob
42+
t0 = time()
43+
gbest, particles = vectorized_solve!(cache.prob,
44+
cache.gbest,
45+
cache.particles,
46+
opt,
47+
Val(opt.global_update),
48+
args...;
49+
maxiters, kwargs...)
50+
t1 = time()
51+
52+
particles_positions = get_pos.(particles)
53+
SciMLBase.build_solution(SciMLBase.DefaultOptimizationCache(prob.f, prob.p), opt,
54+
gbest.position, prob.f(gbest.position, prob.p), original = particles_positions,
55+
stats = Optimization.OptimizationStats(; time = t1 - t0))
56+
end
57+
58+
function SciMLBase.solve(prob::OptimizationProblem, opt::ParallelPSOKernel,
59+
args...; maxiters = 100, kwargs...)
60+
solve!(init(prob, opt, args...; maxiters, kwargs...), opt)
61+
end
62+
563
function SciMLBase.__solve(prob::OptimizationProblem,
664
opt::PSOAlgorithm,
765
args...;

src/utils.jl

+84-24
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
@inbounds function uniform_itr(
2+
dim::Int, lb::AbstractArray{T}, ub::AbstractArray{T}) where {T}
3+
(rand(T) * (ub[i] - lb[i]) + lb[i] for i in 1:dim)
4+
end
5+
16
function uniform(dim::Int, lb::AbstractArray{T}, ub::AbstractArray{T}) where {T}
27
arr = rand(T, dim)
38
@inbounds for i in 1:dim
@@ -6,7 +11,7 @@ function uniform(dim::Int, lb::AbstractArray{T}, ub::AbstractArray{T}) where {T}
611
return arr
712
end
813

9-
function init_particles(prob, opt, ::Type{T}) where {T <: SArray}
14+
function init_particles!(particles, prob, opt, ::Type{T}) where {T <: SArray}
1015
dim = length(prob.u0)
1116
lb = prob.lb
1217
ub = prob.ub
@@ -15,47 +20,102 @@ function init_particles(prob, opt, ::Type{T}) where {T <: SArray}
1520
num_particles = opt.num_particles
1621

1722
if lb === nothing || (all(isinf, lb) && all(isinf, ub))
18-
gbest_position = Array{eltype(prob.u0), 1}(undef, dim)
19-
for i in 1:dim
20-
if abs(prob.u0[i]) > 0
21-
gbest_position[i] = prob.u0[i] + rand(eltype(prob.u0)) * abs(prob.u0[i])
22-
else
23-
gbest_position[i] = rand(eltype(prob.u0))
24-
end
23+
gbest_position = StaticArrays.sacollect(T,
24+
ifelse(
25+
abs(prob.u0[i]) > 0, prob.u0[i] + rand(eltype(prob.u0)) * abs(prob.u0[i]),
26+
rand(eltype(prob.u0))) for i in 1:dim)
27+
else
28+
gbest_position = StaticArrays.sacollect(T, uniform_itr(dim, lb, ub))
29+
end
30+
31+
gbest_position = convert(T, gbest_position)
32+
gbest_cost = cost_func(gbest_position, p)
33+
if !isnothing(prob.f.cons)
34+
penalty = calc_penalty(gbest_position, prob, 1, opt.θ, opt.γ, opt.h)
35+
gbest_cost = cost_func(gbest_position, p) + penalty
36+
else
37+
gbest_cost = cost_func(gbest_position, p)
38+
end
39+
gbest_cost = cost_func(gbest_position, p)
40+
# particles = SPSOParticle[]
41+
42+
if !(lb === nothing || (all(isinf, lb) && all(isinf, ub)))
43+
positions = QuasiMonteCarlo.sample(num_particles, lb, ub, LatinHypercubeSample())
44+
end
45+
46+
for i in 1:num_particles
47+
if lb === nothing || (all(isinf, lb) && all(isinf, ub))
48+
position = StaticArrays.sacollect(T,
49+
ifelse(abs(prob.u0[i]) > 0,
50+
prob.u0[i] + rand(eltype(prob.u0)) * abs(prob.u0[i]),
51+
rand(eltype(prob.u0))) for i in 1:dim)
52+
else
53+
@inbounds position = StaticArrays.sacollect(T, positions[j, i] for j in 1:dim)
2554
end
55+
56+
velocity = zero(T)
57+
58+
if !isnothing(prob.f.cons)
59+
penalty = calc_penalty(position, prob, 1, opt.θ, opt.γ, opt.h)
60+
cost = cost_func(position, p) + penalty
61+
else
62+
cost = cost_func(position, p)
63+
end
64+
65+
best_position = position
66+
best_cost = cost
67+
@inbounds particles[i] = SPSOParticle(
68+
position, velocity, cost, best_position, best_cost)
69+
70+
if best_cost < gbest_cost
71+
gbest_position = best_position
72+
gbest_cost = best_cost
73+
end
74+
end
75+
gbest = SPSOGBest(gbest_position, gbest_cost)
76+
return gbest, particles
77+
end
78+
79+
function init_particles(prob, opt, ::Type{T}) where {T <: SArray}
80+
dim = length(prob.u0)
81+
lb = prob.lb
82+
ub = prob.ub
83+
cost_func = prob.f
84+
p = prob.p
85+
num_particles = opt.num_particles
86+
87+
if lb === nothing || (all(isinf, lb) && all(isinf, ub))
88+
gbest_position = StaticArrays.sacollect(T,
89+
ifelse(
90+
abs(prob.u0[i]) > 0, prob.u0[i] + rand(eltype(prob.u0)) * abs(prob.u0[i]),
91+
rand(eltype(prob.u0))) for i in 1:dim)
2692
else
27-
gbest_position = uniform(dim, lb, ub)
93+
gbest_position = StaticArrays.sacollect(T, uniform_itr(dim, lb, ub))
2894
end
2995

30-
gbest_position = SVector{length(gbest_position), eltype(gbest_position)}(gbest_position)
96+
gbest_cost = cost_func(gbest_position, p)
3197
if !isnothing(prob.f.cons)
3298
penalty = calc_penalty(gbest_position, prob, 1, opt.θ, opt.γ, opt.h)
3399
gbest_cost = cost_func(gbest_position, p) + penalty
34100
else
35101
gbest_cost = cost_func(gbest_position, p)
36102
end
37-
# gbest_cost = cost_func(gbest_position, p)
38-
particles = SPSOParticle[]
103+
particles = SPSOParticle{T, eltype(T)}[]
39104

40105
if !(lb === nothing || (all(isinf, lb) && all(isinf, ub)))
41106
positions = QuasiMonteCarlo.sample(num_particles, lb, ub, LatinHypercubeSample())
42107
end
43108

44109
for i in 1:num_particles
45110
if lb === nothing || (all(isinf, lb) && all(isinf, ub))
46-
position = Array{eltype(prob.u0), 1}(undef, dim)
47-
for i in 1:dim
48-
if abs(prob.u0[i]) > 0
49-
position[i] = prob.u0[i] + rand(eltype(prob.u0)) * abs(prob.u0[i])
50-
else
51-
position[i] = rand(eltype(prob.u0))
52-
end
53-
end
111+
@inbounds position = StaticArrays.sacollect(T,
112+
ifelse(abs(prob.u0[i]) > 0,
113+
prob.u0[i] + rand(eltype(prob.u0)) * abs(prob.u0[i]),
114+
rand(eltype(prob.u0))) for i in 1:dim)
54115
else
55-
position = @view positions[:, i]
116+
@inbounds position = StaticArrays.sacollect(T, positions[j, i] for j in 1:dim)
56117
end
57-
position = SVector{length(position), eltype(position)}(position)
58-
velocity = @SArray zeros(eltype(position), dim)
118+
velocity = zero(T)
59119

60120
if !isnothing(prob.f.cons)
61121
penalty = calc_penalty(position, prob, 1, opt.θ, opt.γ, opt.h)
@@ -74,7 +134,7 @@ function init_particles(prob, opt, ::Type{T}) where {T <: SArray}
74134
end
75135
end
76136
gbest = SPSOGBest(gbest_position, gbest_cost)
77-
return gbest, convert(Vector{typeof(particles[1])}, particles)
137+
return gbest, particles
78138
end
79139

80140
function init_particles(prob, opt, ::Type{T}) where {T <: AbstractArray}

0 commit comments

Comments
 (0)