Skip to content

Commit 40ff049

Browse files
committed
Merge pull request #30 from alyst/tli_enh2
Misc enhancements
2 parents ee41857 + 93704e6 commit 40ff049

7 files changed

Lines changed: 168 additions & 70 deletions

src/BlackBoxOptim.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
VERSION >= v"0.4.0-dev+6521" && __precompile__(true)
2+
13
module BlackBoxOptim
24

35
using Distributions, StatsBase, Compat
@@ -45,10 +47,10 @@ export Optimizer, AskTellOptimizer, SteppingOptimizer, PopulationOptimizer,
4547
width_of_confidence_interval, fitness_improvement_potential,
4648

4749
# OptimizationResults
48-
minimum, f_minimum, iteration_converged, parameters,
50+
minimum, f_minimum, iteration_converged, parameters, population,
4951

5052
# OptController
51-
numruns,
53+
numruns, lastrun, problem,
5254

5355
# Search spaces
5456
ParamBounds, Individual, SearchSpace, FixedDimensionSearchSpace, ContinuousSearchSpace,

src/opt_controller.jl

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,17 @@ type OptController{O<:Optimizer, P<:OptimizationProblem}
220220
optimizer::O # optimization algorithm
221221
problem::P # opt problem
222222
parameters::ParamsDictChain
223-
runcontrollers::Vector{Any}
223+
runcontrollers::Vector{OptRunController{O}}
224224
end
225225

226226
function OptController{O<:Optimizer, P<:OptimizationProblem}(optimizer::O, problem::P,
227227
params::ParamsDictChain)
228-
OptController{O,P}(optimizer, problem, params, Any[])
228+
OptController{O, P}(optimizer, problem, params, OptRunController{O}[])
229229
end
230230

231+
problem(oc::OptController) = oc.problem
231232
numruns(oc::OptController) = length(oc.runcontrollers)
233+
lastrun(oc::OptController) = oc.runcontrollers[end]
232234

233235
function update_parameters!{O<:Optimizer, P<:OptimizationProblem}(oc::OptController{O,P},
234236
parameters::Associative = @compat(Dict{Any,Any}()))
@@ -239,7 +241,7 @@ function update_parameters!{O<:Optimizer, P<:OptimizationProblem}(oc::OptControl
239241
# been setup.
240242
for k in keys(parameters)
241243
if k [:MaxTime, :MaxSteps, :MaxFuncEvals, :ShowTrace]
242-
throw("It is currently not supported to change parameters that can affect the original opt problem or optimizer (here: $(k))")
244+
throw(ArgumentError("It is currently not supported to change parameters that can affect the original opt problem or optimizer (here: $(k))"))
243245
end
244246
end
245247

@@ -250,7 +252,14 @@ function update_parameters!{O<:Optimizer, P<:OptimizationProblem}(oc::OptControl
250252
end
251253
end
252254

253-
# Start a new optimization run, possibly with new parameters.
255+
function init_rng!(parameters::Parameters)
256+
if parameters[:RandomizeRngSeed]
257+
parameters[:RngSeed] = rand(1:1_000_000)
258+
srand(parameters[:RngSeed])
259+
end
260+
end
261+
262+
# Start a new optimization run, possibly with new parameters and report on results.
254263
function run!{O<:Optimizer, P<:OptimizationProblem}(oc::OptController{O,P})
255264
ctrl = OptRunController(oc.optimizer, oc.problem, oc.parameters)
256265
push!(oc.runcontrollers, ctrl)
@@ -260,49 +269,50 @@ function run!{O<:Optimizer, P<:OptimizationProblem}(oc::OptController{O,P})
260269
init_rng!(oc.parameters)
261270
end
262271

263-
run_optimization(ctrl, oc.parameters)
264-
end
265-
266-
function init_rng!(parameters::Parameters)
267-
if parameters[:RandomizeRngSeed]
268-
parameters[:RngSeed] = rand(1:1_000_000)
269-
srand(parameters[:RngSeed])
270-
end
271-
end
272-
273-
# Run one optimization run and report on results.
274-
function run_optimization(ctrl::OptRunController, params::Associative)
275-
# Run the optimization. Try to return something sensible
276-
# even if interrupted with Ctrl-C.
277272
try
278273
run!(ctrl)
279274
show_report(ctrl)
280275

281-
if params[:SaveFitnessTraceToCsv]
276+
if oc.parameters[:SaveFitnessTraceToCsv]
282277
write_results(ctrl)
283278
end
284279

285-
return make_opt_result(ctrl, params)
280+
return make_opt_results(ctrl, oc)
286281
catch ex
287282
# If it was a ctrl-c interrupt we try to make a result and return it...
288283
if isa(ex, InterruptException)
289-
return make_opt_result(ctrl, params)
284+
warn("Optimization interrupted, recovering intermediate results...")
285+
return make_opt_results(ctrl, oc)
290286
else
291287
rethrow(ex)
292288
end
293289
end
294290
end
295291

296-
function make_opt_result(ctrl::OptRunController, params::Associative)
297-
SingleObjectiveOptimizationResults{Vector{Float64},Float64}(
298-
string(params[:Method]),
299-
best_candidate(ctrl),
292+
make_opt_results{O<:Optimizer}(ctrl::OptRunController{O}, oc::OptController{O}) =
293+
SimpleOptimizationResults{fitness_type(problem(ctrl)), Individual}(
294+
string(oc.parameters[:Method]),
300295
best_fitness(ctrl),
296+
best_candidate(ctrl),
301297
stop_reason(ctrl),
302298
num_steps(ctrl),
303299
start_time(ctrl),
304300
elapsed_time(ctrl),
305-
params,
301+
oc.parameters,
306302
num_func_evals(ctrl)
307303
)
308-
end
304+
305+
make_opt_results{O<:PopulationOptimizer}(ctrl::OptRunController{O}, oc::OptController{O}) =
306+
PopulationOptimizationResults{fitness_type(problem(ctrl)), Individual,
307+
typeof(population(ctrl.optimizer))}(
308+
string(oc.parameters[:Method]),
309+
best_fitness(ctrl),
310+
best_candidate(ctrl),
311+
stop_reason(ctrl),
312+
num_steps(ctrl),
313+
start_time(ctrl),
314+
elapsed_time(ctrl),
315+
oc.parameters,
316+
num_func_evals(ctrl),
317+
population(ctrl.optimizer)
318+
)

src/optimization_result.jl

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
abstract OptimizationResults
1+
abstract OptimizationResults{F}
22

3-
immutable SingleObjectiveOptimizationResults{RT,FT} <: OptimizationResults
3+
immutable SimpleOptimizationResults{F,RT} <: OptimizationResults{F}
44
method::ASCIIString # Symbol instead or flexible?
5+
best_fitness::F
56
best_candidate::RT
6-
best_fitness::FT
77
stop_reason::ASCIIString # FIXME turn into type hierarchy of immutable reasons with their attached info
88
iterations::Int
99
start_time::Float64
@@ -22,7 +22,24 @@ parameters(or::OptimizationResults) = or.parameters
2222
f_calls(or::OptimizationResults) = or.f_calls
2323

2424
# Alternative nomenclature that mimics Optim.jl more closely.
25-
import Base.minimum
26-
Base.minimum(or::OptimizationResults) = or.best_candidate
27-
f_minimum(or::OptimizationResults) = or.best_fitness
25+
# FIXME should be it be enabled only for MinimizingFitnessScheme?
26+
Base.minimum(or::OptimizationResults) = best_candidate(or)
27+
f_minimum(or::OptimizationResults) = best_fitness(or)
28+
# FIXME lookup stop_reason
2829
iteration_converged(or::OptimizationResults) = iterations(or) >= parameters(or)[:MaxSteps]
30+
31+
# optimization results for PopulationOptimizer storing the final population
32+
immutable PopulationOptimizationResults{F,RT,P} <: OptimizationResults{F}
33+
method::ASCIIString # Symbol instead or flexible?
34+
best_fitness::F
35+
best_candidate::RT
36+
stop_reason::ASCIIString # FIXME turn into type hierarchy of immutable reasons with their attached info
37+
iterations::Int
38+
start_time::Float64
39+
elasped_time::Float64 # seconds
40+
parameters::Parameters
41+
f_calls::Int
42+
population::P
43+
end
44+
45+
population(or::PopulationOptimizationResults) = or.population

src/population.jl

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,19 @@ params_std(pop::FitPopulation) = std(pop.individuals, 1)
4242

4343
fitness(pop::FitPopulation, ix::Int) = pop.fitness[ix]
4444

45-
getindex(pop::FitPopulation, rows, cols) = pop.individuals[rows, cols]
46-
getindex(pop::FitPopulation, ::Colon, cols) = pop.individuals[:, cols] # FIXME remove v0.3 workaround
47-
getindex(pop::FitPopulation, indi_ixs) = pop.individuals[:, indi_ixs]
45+
Base.getindex(pop::FitPopulation, rows, cols) = pop.individuals[rows, cols]
46+
Base.getindex(pop::FitPopulation, ::Colon, cols) = pop.individuals[:, cols] # FIXME remove v0.3 workaround
47+
Base.getindex(pop::FitPopulation, indi_ixs) = pop.individuals[:, indi_ixs]
48+
49+
function Base.append!{F}(pop::FitPopulation{F}, extra_pop::FitPopulation{F})
50+
numdims(pop) == numdims(extra_pop) ||
51+
throw(DimensionMismatch("Cannot append population, "*
52+
"the number of parameters differs "*
53+
"($(numdims(pop)) vs $(numdims(extra_pop)))"))
54+
pop.individuals = hcat(pop.individuals, extra_pop.individuals)
55+
append!(pop.fitness, extra_pop.fitness)
56+
return pop
57+
end
4858

4959
fitness_type{F}(pop::FitPopulation{F}) = F
5060
candidate_type{F}(pop::FitPopulation{F}) = Candidate{F}

test/test_differential_evolution.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ context("SimpleSelector") do
1717
@fact length(sampled) => numSamples
1818

1919
# All sampled indices are indices into the population
20-
@fact all([in(index, 1:popsize(DE)) for index in sampled]) => true
20+
@fact all(index -> in(index, 1:popsize(DE)), sampled) => true
2121
end
2222
end
2323

@@ -37,7 +37,7 @@ context("RadiusLimitedSelector") do
3737
@fact length(sampled) => numSamples
3838

3939
# All sampled indices are indices into the population
40-
@fact all([in(index, 1:popsize(DE)) for index in sampled]) => true
40+
@fact all(index -> in(index, 1:popsize(DE)), sampled) => true
4141

4242
mini, maxi = minimum(sampled), maximum(sampled)
4343
if (maxi - mini) > max(numSamples+2, sel.radius)

test/test_population.jl

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,44 @@ facts("Population") do
1111
@fact isnafitness(fitness(p1, 1), fs) => true
1212
@fact isnafitness(fitness(p1, 4), fs) => true
1313

14-
@fact BlackBoxOptim.candi_pool_size(p1) => 0
15-
candi1 = BlackBoxOptim.acquire_candi(p1, 1)
16-
@fact BlackBoxOptim.candi_pool_size(p1) => 0
17-
@fact candi1.index => 1
18-
@fact isnafitness(candi1.fitness, fs) => true
19-
20-
candi2 = BlackBoxOptim.acquire_candi(p1, 2)
21-
@fact BlackBoxOptim.candi_pool_size(p1) => 0
22-
@fact candi2.index => 2
23-
@fact isnafitness(candi2.fitness, fs) => true
24-
25-
BlackBoxOptim.release_candi(p1, candi2)
26-
@fact BlackBoxOptim.candi_pool_size(p1) => 1
27-
28-
candi1.fitness = 5.0
29-
BlackBoxOptim.accept_candi!(p1, candi1)
30-
@fact BlackBoxOptim.candi_pool_size(p1) => 2
31-
@fact fitness(p1, 1) => 5.0
14+
context("candidates pool") do
15+
@fact BlackBoxOptim.candi_pool_size(p1) => 0
16+
candi1 = BlackBoxOptim.acquire_candi(p1, 1)
17+
@fact BlackBoxOptim.candi_pool_size(p1) => 0
18+
@fact candi1.index => 1
19+
@fact isnafitness(candi1.fitness, fs) => true
20+
21+
candi2 = BlackBoxOptim.acquire_candi(p1, 2)
22+
@fact BlackBoxOptim.candi_pool_size(p1) => 0
23+
@fact candi2.index => 2
24+
@fact isnafitness(candi2.fitness, fs) => true
25+
26+
BlackBoxOptim.release_candi(p1, candi2)
27+
@fact BlackBoxOptim.candi_pool_size(p1) => 1
28+
29+
candi1.fitness = 5.0
30+
BlackBoxOptim.accept_candi!(p1, candi1)
31+
@fact BlackBoxOptim.candi_pool_size(p1) => 2
32+
@fact fitness(p1, 1) => 5.0
33+
end
34+
35+
context("append!()") do
36+
p2 = FitPopulation(fs, 5, 2)
37+
@fact isa(p2, FitPopulation) => true
38+
39+
candi22 = BlackBoxOptim.acquire_candi(p2, 2)
40+
candi22.fitness = 4.0
41+
BlackBoxOptim.accept_candi!(p2, candi22)
42+
43+
append!(p1, p2)
44+
@fact numdims(p1) --> 2
45+
@fact popsize(p1) --> 15
46+
@fact p1[11] --> p2[1]
47+
@fact fitness(p1, 12) --> 4.0
48+
49+
p3 = FitPopulation(fs, 5, 1)
50+
@fact_throws DimensionMismatch append!(p1, p3)
51+
end
3252
end
3353

3454
end

test/test_toplevel_bboptimize.jl

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,57 @@ facts("Top-level interface") do
33
return( sum( 100*( x[2:end] - x[1:end-1].^2 ).^2 + ( x[1:end-1] - 1 ).^2 ) )
44
end
55

6-
context("run a simple optimization with mostly defaults") do
7-
res = bboptimize(rosenbrock; SearchRange = (-5.0, 5.0), NumDimensions = 2,
8-
MaxSteps = 2000, ShowTrace = false)
9-
@fact best_fitness(res) => less_than(0.1)
10-
xbest = best_candidate(res)
11-
@fact typeof(xbest) => Vector{Float64}
12-
@fact length(xbest) => 2
13-
14-
# We also mimic some of the Optim.jl api (although it is a bit strange...)
15-
@fact f_minimum(res) => less_than(0.1)
16-
@fact minimum(res) => xbest
17-
@fact iteration_converged(res) => true
6+
context("run a simple optimization") do
7+
context("using bboptimize() with mostly defaults") do
8+
res = bboptimize(rosenbrock; SearchRange = (-5.0, 5.0), NumDimensions = 2,
9+
MaxSteps = 2000, ShowTrace = false)
10+
@fact best_fitness(res) => less_than(0.1)
11+
xbest = best_candidate(res)
12+
@fact typeof(xbest) => Vector{Float64}
13+
@fact length(xbest) => 2
14+
15+
# We also mimic some of the Optim.jl api (although it is a bit strange...)
16+
@fact f_minimum(res) => less_than(0.1)
17+
@fact minimum(res) => xbest
18+
@fact iteration_converged(res) => true
19+
end
20+
21+
context("using bbsetup()/bboptimize() with mostly defaults") do
22+
opt = bbsetup(rosenbrock; SearchRange = (-5.0, 5.0), NumDimensions = 2,
23+
MaxSteps = 2000, ShowTrace = false)
24+
@fact numruns(opt) --> 0
25+
@fact isa(problem(opt), BlackBoxOptim.FunctionBasedProblem) --> true
26+
res = bboptimize(opt)
27+
@fact numruns(opt) --> 1
28+
@fact isa(lastrun(opt), BlackBoxOptim.OptRunController) --> true
29+
30+
@fact best_fitness(res) => less_than(0.1)
31+
xbest = best_candidate(res)
32+
end
33+
34+
context("using non-population optimizer") do
35+
res = bboptimize(rosenbrock; Method=:generating_set_search,
36+
SearchRange = (-5.0, 5.0), NumDimensions = 2,
37+
MaxSteps = 2000, ShowTrace = false)
38+
@fact isa(res, BlackBoxOptim.SimpleOptimizationResults) --> true
39+
@fact best_fitness(res) => less_than(1.0)
40+
xbest = best_candidate(res)
41+
@fact typeof(xbest) => Vector{Float64}
42+
end
43+
44+
context("using population optimizer") do
45+
res = bboptimize(rosenbrock; Method=:adaptive_de_rand_1_bin,
46+
SearchRange = (-5.0, 5.0), NumDimensions = 2,
47+
MaxSteps = 2000, ShowTrace = false)
48+
@fact isa(res, BlackBoxOptim.PopulationOptimizationResults) --> true
49+
@fact best_fitness(res) => less_than(0.1)
50+
xbest = best_candidate(res)
51+
@fact typeof(xbest) => Vector{Float64}
52+
xpop = population(res)
53+
@fact isa(xpop, BlackBoxOptim.Population) --> true
54+
@fact popsize(xpop) --> greater_than(0)
55+
@fact numdims(xpop) --> 2
56+
end
1857
end
1958

2059
context("continue running an optimization after it finished") do

0 commit comments

Comments
 (0)