From be88c647b7a047cb35d4a966f50cc31e31a5c862 Mon Sep 17 00:00:00 2001 From: phrb Date: Fri, 4 Sep 2015 19:49:01 -0300 Subject: [PATCH] Add 'iterative_first_improvement'. Add 'first_improvement' building block. --- .../iterative_first_improvement/rosenbrock.jl | 24 ++++++ .../iterative_first_improvement/sorting.jl | 73 +++++++++++++++++++ examples/rosenbrock.jl | 1 + examples/sorting.jl | 3 +- src/StochasticSearch.jl | 16 +++- src/core/search/blocks/first_improvement.jl | 44 +++++++++++ src/core/search/optimize.jl | 4 +- .../techniques/iterative_first_improvement.jl | 37 ++++++++++ .../search/techniques/simulated_annealing.jl | 14 ++-- .../search/tools/initialize_search_tasks!.jl | 9 +++ test/iterative_first_improvement.jl | 30 ++++++++ test/runtests.jl | 1 + test/simulated_annealing.jl | 3 +- 13 files changed, 245 insertions(+), 14 deletions(-) create mode 100644 examples/iterative_first_improvement/rosenbrock.jl create mode 100644 examples/iterative_first_improvement/sorting.jl create mode 100644 src/core/search/blocks/first_improvement.jl create mode 100644 src/core/search/techniques/iterative_first_improvement.jl create mode 100644 test/iterative_first_improvement.jl diff --git a/examples/iterative_first_improvement/rosenbrock.jl b/examples/iterative_first_improvement/rosenbrock.jl new file mode 100644 index 0000000..56854fa --- /dev/null +++ b/examples/iterative_first_improvement/rosenbrock.jl @@ -0,0 +1,24 @@ +@everywhere using StochasticSearch + +@everywhere function rosenbrock(x::Configuration) + return (1.0 - x["i0"].value)^2 + 100.0 * (x["i1"].value - x["i0"].value^2)^2 +end + +configuration = Configuration([NumberParameter(-2.0, 2.0, 0.0,"i0"), + NumberParameter(-2.0, 2.0, 0.0,"i1")], + "rosenbrock_config") + +iterations = 1_000 +report_after = 1_00 + +result = @task optimize(rosenbrock, + configuration, + [:iterative_first_improvement], + iterations = iterations, + report_after = report_after, + evaluations = 1, + instances = [1]) +partial = None +for i = 0:iterations + partial = consume(result) +end diff --git a/examples/iterative_first_improvement/sorting.jl b/examples/iterative_first_improvement/sorting.jl new file mode 100644 index 0000000..044d5aa --- /dev/null +++ b/examples/iterative_first_improvement/sorting.jl @@ -0,0 +1,73 @@ +@everywhere using StochasticSearch + +@everywhere function insertionsort!(A, i, j) + for m = i + 1:j + value = A[m] + n = m - 1 + while n >= i && A[n] > value + A[n + 1] = A[n] + n = n - 1 + end + A[n + 1] = value + end +end + +@everywhere function quicksort!(A,cutoff,i=1,j=length(A)) + if j > i + pivot = A[rand(i:j)] + left, right = i, j + while left <= right + while A[left] < pivot + left += 1 + end + while A[right] > pivot + right -= 1 + end + if left <= right + A[left], A[right] = A[right], A[left] + left += 1 + right -= 1 + end + end + if j - i <= cutoff + insertionsort!(A, i, j) + else + quicksort!(A,cutoff,i,right) + quicksort!(A,cutoff,left,j) + end + end + return A +end + +@everywhere function sorting_cutoff(config::Configuration, args::Dict{ASCIIString, Any}) + A = copy(args["array"]) + cutoff = config.value["cutoff"] + @elapsed quicksort!(A, cutoff) +end + +array_size = 100_000 +cutoff = 15 +iterations = 2_00 +report_after = 4 + +args = Dict{ASCIIString, Any}() +args["array"] = rand(array_size) + +# Making sure code is already compiled. +@elapsed quicksort!(rand(10), 5) + +configuration = Configuration([NumberParameter(0, array_size, cutoff, "cutoff")], + "Sorting Cutoff") + +result = @task optimize(sorting_cutoff, + configuration, + [:iterative_first_improvement, :simulated_annealing], + args = args, + iterations = iterations, + report_after = report_after, + evaluations = 6, + instances = [1, 1]) +partial = None +for i = 0:iterations + partial = consume(result) +end diff --git a/examples/rosenbrock.jl b/examples/rosenbrock.jl index 76ceeb6..2cf4109 100644 --- a/examples/rosenbrock.jl +++ b/examples/rosenbrock.jl @@ -13,6 +13,7 @@ report_after = 1_00 result = @task optimize(rosenbrock, configuration, + [:simulated_annealing], iterations = iterations, report_after = report_after, evaluations = 1, diff --git a/examples/sorting.jl b/examples/sorting.jl index ac46ae9..a2b1528 100644 --- a/examples/sorting.jl +++ b/examples/sorting.jl @@ -61,11 +61,12 @@ configuration = Configuration([NumberParameter(0, array_size, cutoff, "cutoff")] result = @task optimize(sorting_cutoff, configuration, + [:simulated_annealing], args = args, iterations = iterations, report_after = report_after, evaluations = 6, - instances = [4]) + instances = [1]) partial = None for i = 0:iterations partial = consume(result) diff --git a/src/StochasticSearch.jl b/src/StochasticSearch.jl index c260fd6..23208f2 100644 --- a/src/StochasticSearch.jl +++ b/src/StochasticSearch.jl @@ -9,8 +9,10 @@ module StochasticSearch export perturb!, perturb_elements!, neighbor!, optimize!, optimize, initialize_cost, initialize_search_tasks!, get_new_best, - measure_mean!, simulated_annealing, update!, - optimize, unit_value, unit_value! + measure_mean!, update!, + optimize, unit_value, unit_value!, + simulated_annealing, first_improvement, + iterative_first_improvement # New Methods for Base Functions import Base.convert, Base.show, Base.getindex, @@ -31,11 +33,19 @@ module StochasticSearch include("core/measurement/measure.jl") include("core/measurement/results.jl") - # Search + # Search Blocks + include("core/search/blocks/first_improvement.jl") + + # Techniques + include("core/search/techniques/iterative_first_improvement.jl") include("core/search/techniques/simulated_annealing.jl") + + # Tools include("core/search/tools/initialize_cost.jl") include("core/search/tools/initialize_search_tasks!.jl") include("core/search/tools/get_new_best.jl") + + # Optimize include("core/search/optimize.jl") # Optim.jl Interface diff --git a/src/core/search/blocks/first_improvement.jl b/src/core/search/blocks/first_improvement.jl new file mode 100644 index 0000000..99aeee9 --- /dev/null +++ b/src/core/search/blocks/first_improvement.jl @@ -0,0 +1,44 @@ +first_improvement(cost::Function, + args::Dict{ASCIIString, Any}, + initial_x::Configuration, + initial_cost::Float64, + evaluations::Int, + f_xs::Array{Float64}, + cutoff::Int) = begin + x = deepcopy(initial_x) + x_proposal = deepcopy(initial_x) + name = "First Improvement" + f_calls = 0 + iteration = 0 + f_x = initial_cost + while iteration <= cutoff + iteration += 1 + neighbor!(x_proposal) + f_proposal = @fetch (measure_mean!(cost, + x_proposal, + args, + evaluations, + f_xs)) + f_calls += evaluations + if f_proposal <= f_x + update!(x, x_proposal.parameters) + f_x = f_proposal + return Result(name, + initial_x, + x, + f_x, + iteration, + iteration, + f_calls, + false) + end + end + Result(name, + initial_x, + x, + f_x, + iteration, + iteration, + f_calls, + false) +end diff --git a/src/core/search/optimize.jl b/src/core/search/optimize.jl index 884349d..877e0c7 100644 --- a/src/core/search/optimize.jl +++ b/src/core/search/optimize.jl @@ -1,6 +1,6 @@ optimize(f::Function, - initial_x::Configuration; - methods::Array{Symbol} = [:simulated_annealing], + initial_x::Configuration, + methods::Array{Symbol}; args::Dict{ASCIIString, Any} = Dict{ASCIIString,Any}(), instances::Array{Int} = [1], iterations::Int = 1_000, diff --git a/src/core/search/techniques/iterative_first_improvement.jl b/src/core/search/techniques/iterative_first_improvement.jl new file mode 100644 index 0000000..0a2d6b0 --- /dev/null +++ b/src/core/search/techniques/iterative_first_improvement.jl @@ -0,0 +1,37 @@ +iterative_first_improvement(cost::Function, + args::Dict{ASCIIString, Any}, + initial_x::Configuration, + initial_cost::Float64; + cutoff::Int = 10, + evaluations::Int = 3, + iterations::Int = 100_000) = begin + x = deepcopy(initial_x) + name = "Iterative First Improvement" + f_calls = 0 + iteration = 0 + f_xs = Float64[] + for i = 1:evaluations + push!(f_xs, 0.0) + end + f_x = initial_cost + f_calls += evaluations + while iteration <= iterations + iteration += 1 + neighbor!(x) + # First Improvement never produces a worse result. + result = first_improvement(cost, + args, + x, + f_x, + evaluations, + f_xs, + cutoff) + f_calls += result.cost_calls + result.cost_calls = f_calls + result.start = initial_x + result.technique = name + result.iterations = iteration + result.current_iteration = iteration + produce(result) + end +end diff --git a/src/core/search/techniques/simulated_annealing.jl b/src/core/search/techniques/simulated_annealing.jl index ace84fe..75844a1 100644 --- a/src/core/search/techniques/simulated_annealing.jl +++ b/src/core/search/techniques/simulated_annealing.jl @@ -3,13 +3,13 @@ # log_temperature(t::Real) = 1 / log(t) -simulated_annealing{T <: Configuration}(cost::Function, - args::Dict{ASCIIString, Any}, - initial_x::T, - initial_cost::Float64; - temperature::Function = log_temperature, - evaluations::Int = 3, - iterations::Int = 100_000) = begin +simulated_annealing(cost::Function, + args::Dict{ASCIIString, Any}, + initial_x::Configuration, + initial_cost::Float64; + temperature::Function = log_temperature, + evaluations::Int = 3, + iterations::Int = 100_000) = begin x = deepcopy(initial_x) x_proposal = deepcopy(initial_x) name = "Simulated Annealing" diff --git a/src/core/search/tools/initialize_search_tasks!.jl b/src/core/search/tools/initialize_search_tasks!.jl index a7dc3db..b19cd1f 100644 --- a/src/core/search/tools/initialize_search_tasks!.jl +++ b/src/core/search/tools/initialize_search_tasks!.jl @@ -17,6 +17,15 @@ initialize_search_tasks!(f::Function, iterations = iterations, evaluations = evaluations)) end + elseif methods[i] == :iterative_first_improvement + for j = 1:instances[i] + push!(task_list, @task iterative_first_improvement(f, + args, + initial_x, + initial_f_x, + iterations = iterations, + evaluations = evaluations)) + end else error("Error: Unknown Method.") end diff --git a/test/iterative_first_improvement.jl b/test/iterative_first_improvement.jl new file mode 100644 index 0000000..79a1525 --- /dev/null +++ b/test/iterative_first_improvement.jl @@ -0,0 +1,30 @@ +using StochasticSearch, FactCheck, Base.Test + +facts("[Search]") do + context("iterative_first_improvement") do + function rosenbrock(x::Configuration) + return (1.0 - x["i0"].value)^2 + 100.0 * (x["i1"].value - x["i0"].value^2)^2 + end + configuration = Configuration([NumberParameter(-2.0,2.0,0.0,"i0"), + NumberParameter(-2.0,2.0,0.0,"i1")], + "rosenbrock_config") + iterations = 1_000 + report_after = 333 + run_task = @task optimize(rosenbrock, + configuration, + [:iterative_first_improvement], + iterations = iterations, + report_after = report_after) + result = None + for i = 1:iterations + result = consume(run_task) + end + rr = rosenbrock(result.minimum) + rc = result.cost_minimum + @test_approx_eq rc rr + @fact (configuration["i0"].value != result.minimum["i0"].value) --> true + @fact (rosenbrock(result.minimum) <= rosenbrock(configuration)) --> true + @fact_throws ErrorException optimize(rosenbrock, configuration, [:bozo_search]) + println(rosenbrock(result.minimum)) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 77c1da7..8b24a02 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,6 +5,7 @@ include("parameter.jl") include("configuration.jl") include("optim_interface.jl") include("simulated_annealing.jl") +include("iterative_first_improvement.jl") include("unit_value.jl") FactCheck.exitstatus() diff --git a/test/simulated_annealing.jl b/test/simulated_annealing.jl index 0f9b259..9542e26 100644 --- a/test/simulated_annealing.jl +++ b/test/simulated_annealing.jl @@ -12,6 +12,7 @@ facts("[Search]") do report_after = 333 run_task = @task optimize(rosenbrock, configuration, + [:simulated_annealing], iterations = iterations, report_after = report_after) result = None @@ -23,7 +24,7 @@ facts("[Search]") do @test_approx_eq rc rr @fact (configuration["i0"].value != result.minimum["i0"].value) --> true @fact (rosenbrock(result.minimum) <= rosenbrock(configuration)) --> true - @fact_throws ErrorException optimize(rosenbrock, configuration, methods = [:bozo_search]) + @fact_throws ErrorException optimize(rosenbrock, configuration, [:bozo_search]) println(rosenbrock(result.minimum)) end end