From 44135f8c455d97321982277b4bd0bb79d5428f21 Mon Sep 17 00:00:00 2001 From: bibenga <{ID}+{username}@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:32:23 +0000 Subject: [PATCH 1/2] add GreatDeluge --- lib/alns/accept/great_deluge.rb | 29 +++++++++++++++++++++++++++++ lib/alns/accept/threshold.rb | 28 ++++++++++++++++++++++++++++ test/accept/great_deluge_test.rb | 18 ++++++++++++++++++ test/accept/hill_climbing_test.rb | 2 +- test/{models.rb => fake_state.rb} | 6 +++++- test/select/roulette_wheel_test.rb | 2 +- test/solver_test.rb | 2 +- test/stop/no_improvement_test.rb | 2 +- 8 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 lib/alns/accept/great_deluge.rb create mode 100644 lib/alns/accept/threshold.rb create mode 100644 test/accept/great_deluge_test.rb rename test/{models.rb => fake_state.rb} (63%) diff --git a/lib/alns/accept/great_deluge.rb b/lib/alns/accept/great_deluge.rb new file mode 100644 index 0000000..e1aa648 --- /dev/null +++ b/lib/alns/accept/great_deluge.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'alns/accept/base' + +module ALNS + module Accept + class GreatDeluge < Base + def initialize(alpha, beta) + if alpha <= 1 || !(0 < beta && beta < 1) + raise ArgumentError, 'alpha must be > 1 and beta must be in (0, 1).' + end + + @alpha = alpha + @beta = beta + @threshold = nil + end + + def accept?(_rnd, best, _current, candidate) + @threshold = @alpha * best.objective if @threshold.nil? + + diff = @threshold - candidate.objective + + @threshold -= @beta * diff + + diff.positive? + end + end + end +end diff --git a/lib/alns/accept/threshold.rb b/lib/alns/accept/threshold.rb new file mode 100644 index 0000000..1827b55 --- /dev/null +++ b/lib/alns/accept/threshold.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module ALNS + module Accept + module Threshold + LINEAR = 1 + EXPONENTIAL = 2 + + def self.to_s(value) + case value + when LINEAR then 'LINEAR' + when EXPONENTIAL then 'EXPONENTIAL' + else + raise ArgumentError, "Invalid method: #{value}" + end + end + + def self.update(current, step, method) + case method + when LINEAR then current - step + when EXPONENTIAL then current * step + else + raise ArgumentError, "Invalid method: #{value}" + end + end + end + end +end diff --git a/test/accept/great_deluge_test.rb b/test/accept/great_deluge_test.rb new file mode 100644 index 0000000..1f94cde --- /dev/null +++ b/test/accept/great_deluge_test.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require_relative '../test_helper' +require 'minitest/autorun' +require 'alns/accept/great_deluge' +require_relative '../fake_state' + +class GreatDelugeTest < Minitest::Test + def test_accept? + accept = ALNS::Accept::GreatDeluge.new(2, 0.01) + + assert !accept.accept?(nil, Zero, Zero, One) + + assert accept.accept?(nil, Zero, Zero, Zero) + + assert accept.accept?(nil, Zero, Zero, Zero) + end +end diff --git a/test/accept/hill_climbing_test.rb b/test/accept/hill_climbing_test.rb index 2ab80ca..b8a9e8c 100644 --- a/test/accept/hill_climbing_test.rb +++ b/test/accept/hill_climbing_test.rb @@ -3,7 +3,7 @@ require_relative '../test_helper' require 'minitest/autorun' require 'alns/accept/hill_climbing' -require_relative '../models' +require_relative '../fake_state' class HillClimbingTest < Minitest::Test def test_accept? diff --git a/test/models.rb b/test/fake_state.rb similarity index 63% rename from test/models.rb rename to test/fake_state.rb index 4decf0f..ff1cedd 100644 --- a/test/models.rb +++ b/test/fake_state.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'minitest/autorun' require 'alns/state' class FakeState < ALNS::State @@ -11,3 +10,8 @@ def initialize(val) @objective = val end end + +Sentinel = FakeState.new(0) +Zero = FakeState.new(0) +One = FakeState.new(1) +Two = FakeState.new(2) diff --git a/test/select/roulette_wheel_test.rb b/test/select/roulette_wheel_test.rb index 7923c16..ec4afc4 100644 --- a/test/select/roulette_wheel_test.rb +++ b/test/select/roulette_wheel_test.rb @@ -3,7 +3,7 @@ require_relative '../test_helper' require 'minitest/autorun' require 'alns/select/roulette_wheel' -require_relative '../models' +require_relative '../fake_state' class RouletteWheelTest < Minitest::Test def setup diff --git a/test/solver_test.rb b/test/solver_test.rb index 7ab739b..da0ce58 100644 --- a/test/solver_test.rb +++ b/test/solver_test.rb @@ -6,7 +6,7 @@ require 'alns/accept/hill_climbing' require 'alns/select/roulette_wheel' require 'alns/stop/max_iterations' -require_relative 'models' +require_relative 'fake_state' class SolverTest < Minitest::Test def setup diff --git a/test/stop/no_improvement_test.rb b/test/stop/no_improvement_test.rb index c92fd8b..bbb58ae 100644 --- a/test/stop/no_improvement_test.rb +++ b/test/stop/no_improvement_test.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require_relative '../test_helper' -require_relative '../models' +require_relative '../fake_state' require 'minitest/autorun' require 'alns/stop/no_improvement' From 9814852e438cb2399c331d33e56ad9f9c854ed34 Mon Sep 17 00:00:00 2001 From: bibenga <{ID}+{username}@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:37:39 +0000 Subject: [PATCH 2/2] update example --- examples/tsp.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/tsp.rb b/examples/tsp.rb index a215b78..a7c44cb 100644 --- a/examples/tsp.rb +++ b/examples/tsp.rb @@ -8,6 +8,7 @@ require 'alns' require 'alns/state' require 'alns/accept/hill_climbing' +require 'alns/accept/great_deluge' require 'alns/select/roulette_wheel' require 'alns/stop/max_iterations' require 'alns/stop/max_runtime' @@ -81,6 +82,7 @@ def self.solve select = ALNS::Select::RouletteWheel.new([3, 2, 1, 0.5], 0.8, num_destroy, num_repair) accept = ALNS::Accept::HillClimbing.new + # accept = ALNS::Accept::GreatDeluge.new(1.05, 0.01) stop = ALNS::Stop::MaxIterations.new(max_iterations) # stop = ALNS::Stop::MaxRuntime.new(2)