annealing 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +4 -4
- data/.rubocop.yml +2 -2
- data/.rubocop_todo.yml +23 -11
- data/AGENTS.md +53 -0
- data/CHANGELOG.md +13 -1
- data/Gemfile +10 -7
- data/Gemfile.lock +76 -42
- data/README.md +32 -17
- data/annealing.gemspec +1 -2
- data/bin/run +9 -4
- data/lib/annealing/configuration/coolers.rb +36 -0
- data/lib/annealing/configuration/terminators.rb +25 -0
- data/lib/annealing/configuration.rb +63 -15
- data/lib/annealing/metal.rb +16 -26
- data/lib/annealing/simulator.rb +40 -43
- data/lib/annealing/version.rb +1 -1
- data/lib/annealing.rb +6 -11
- data/mise.toml +2 -0
- metadata +9 -29
- data/lib/annealing/configuration/configurator.rb +0 -41
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f42d7b6ef54f628c761f716ab2dffe55f227b1c3bac9c4e962c971bddc835961
|
|
4
|
+
data.tar.gz: 6a4a6673ce1e9893d6da3d8e4ff37781f98dd6108ab33819e6d1a1bb7d9cbf6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 03b9fbc0ef5c027ba6f9d33a9eee1ae120c134f4d436916f076b1829ca170343b778da279096e01eda72d377c7370baf170e88315951c70e23fe39cd86315579
|
|
7
|
+
data.tar.gz: 3402a3824fdc915abccda29fc57ce0ab320547b44fd74a7df5b70c3171e1a08fe88b55d81582419813fb13b71e182f0dae51d4967e309a3017517abe210ffa8b
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -18,9 +18,9 @@ jobs:
|
|
|
18
18
|
runs-on: ubuntu-latest
|
|
19
19
|
strategy:
|
|
20
20
|
matrix:
|
|
21
|
-
ruby-version: ['
|
|
21
|
+
ruby-version: ['4.0']
|
|
22
22
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
24
|
- name: Set up Ruby
|
|
25
25
|
uses: ruby/setup-ruby@v1
|
|
26
26
|
with:
|
|
@@ -32,10 +32,10 @@ jobs:
|
|
|
32
32
|
runs-on: ubuntu-latest
|
|
33
33
|
strategy:
|
|
34
34
|
matrix:
|
|
35
|
-
ruby-version: ['
|
|
35
|
+
ruby-version: ['3.4', '4.0']
|
|
36
36
|
|
|
37
37
|
steps:
|
|
38
|
-
- uses: actions/checkout@
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
39
|
- name: Set up Ruby
|
|
40
40
|
uses: ruby/setup-ruby@v1
|
|
41
41
|
with:
|
data/.rubocop.yml
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
inherit_from: .rubocop_todo.yml
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
plugins:
|
|
4
4
|
- rubocop-performance
|
|
5
5
|
- rubocop-minitest
|
|
6
6
|
- rubocop-rake
|
|
7
7
|
|
|
8
8
|
AllCops:
|
|
9
|
-
TargetRubyVersion:
|
|
9
|
+
TargetRubyVersion: 3.4
|
|
10
10
|
NewCops: enable
|
|
11
11
|
DefaultFormatter: progress
|
|
12
12
|
DisplayCopNames: true
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,42 +1,54 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config --auto-gen-only-exclude`
|
|
3
|
-
# on 2022-
|
|
3
|
+
# on 2022-12-12 19:04:19 UTC using RuboCop version 1.23.0.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
|
8
8
|
|
|
9
|
-
# Offense count:
|
|
9
|
+
# Offense count: 7
|
|
10
10
|
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes, Max.
|
|
11
11
|
Metrics/AbcSize:
|
|
12
12
|
Exclude:
|
|
13
|
-
- '
|
|
13
|
+
- 'lib/annealing/configuration.rb'
|
|
14
|
+
- 'test/annealing/configuration_test.rb'
|
|
14
15
|
- 'test/annealing/metal_test.rb'
|
|
15
16
|
- 'test/annealing/simulator_test.rb'
|
|
16
17
|
- 'test/annealing_test.rb'
|
|
17
18
|
|
|
18
|
-
# Offense count:
|
|
19
|
+
# Offense count: 2
|
|
19
20
|
# Configuration parameters: CountComments, Max, CountAsOne.
|
|
20
21
|
Metrics/ClassLength:
|
|
21
22
|
Exclude:
|
|
22
|
-
- 'test/annealing/
|
|
23
|
-
- 'test/annealing/metal_test.rb'
|
|
23
|
+
- 'test/annealing/configuration_test.rb'
|
|
24
24
|
- 'test/annealing/simulator_test.rb'
|
|
25
25
|
|
|
26
|
-
# Offense count:
|
|
26
|
+
# Offense count: 1
|
|
27
|
+
# Configuration parameters: IgnoredMethods, Max.
|
|
28
|
+
Metrics/CyclomaticComplexity:
|
|
29
|
+
Exclude:
|
|
30
|
+
- 'lib/annealing/configuration.rb'
|
|
31
|
+
|
|
32
|
+
# Offense count: 11
|
|
27
33
|
# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods.
|
|
28
34
|
Metrics/MethodLength:
|
|
29
35
|
Exclude:
|
|
30
36
|
- 'lib/annealing/configuration.rb'
|
|
31
|
-
- '
|
|
32
|
-
- 'test/annealing/configuration/configurator_test.rb'
|
|
37
|
+
- 'test/annealing/configuration_test.rb'
|
|
33
38
|
- 'test/annealing/metal_test.rb'
|
|
34
39
|
- 'test/annealing/simulator_test.rb'
|
|
35
40
|
- 'test/annealing_test.rb'
|
|
36
41
|
|
|
37
|
-
# Offense count:
|
|
42
|
+
# Offense count: 1
|
|
43
|
+
# Configuration parameters: IgnoredMethods, Max.
|
|
44
|
+
Metrics/PerceivedComplexity:
|
|
45
|
+
Exclude:
|
|
46
|
+
- 'lib/annealing/configuration.rb'
|
|
47
|
+
|
|
48
|
+
# Offense count: 5
|
|
38
49
|
# Configuration parameters: Max.
|
|
39
50
|
Minitest/MultipleAssertions:
|
|
40
51
|
Exclude:
|
|
41
|
-
- 'test/annealing/configuration/
|
|
52
|
+
- 'test/annealing/configuration/terminators_test.rb'
|
|
42
53
|
- 'test/annealing/configuration_test.rb'
|
|
54
|
+
- 'test/annealing/simulator_test.rb'
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Reference for AI agents (and humans) working in this repo. A Ruby gem implementing
|
|
4
|
+
simulated annealing. See `README.md` for the public API and configuration precedence
|
|
5
|
+
(global < instance < just-in-time).
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
- Setup: `bin/setup` (runs `bundle install`).
|
|
10
|
+
- Full default check (order matters): `bundle exec rake` — runs `test`, then `end_to_end_test`, then `rubocop`.
|
|
11
|
+
- Run tests only: `bundle exec rake test`.
|
|
12
|
+
- Run a single test file: `bundle exec ruby -Ilib:test test/path/to_test.rb` (or `rake test TEST=...`).
|
|
13
|
+
- Lint only: `bundle exec rubocop` (or `bundle exec rake rubocop`).
|
|
14
|
+
- Interactive console: `bin/console`. Runnable demo: `bin/run`.
|
|
15
|
+
|
|
16
|
+
## Gotchas
|
|
17
|
+
|
|
18
|
+
- `rake`'s default task includes `bin/run` via the `end_to_end_test` task. It runs a
|
|
19
|
+
real (slow, non-deterministic due to `rand`) annealing simulation; skip it with
|
|
20
|
+
`rake test rubocop` when you only want fast feedback.
|
|
21
|
+
- Tests use Minitest. `test/test_helper.rb` requires `ruby-prof` and `debug` — both are
|
|
22
|
+
dev deps in the `Gemfile`; ensure they're installed (`bundle install`) before running tests.
|
|
23
|
+
- `Annealing.configuration` is a global singleton. Tests reset it in
|
|
24
|
+
`Minitest::Test#teardown` by setting `Annealing.configuration = nil`. When writing
|
|
25
|
+
tests that touch global config, favor scoped instance/JIT config or rely on teardown.
|
|
26
|
+
- `Metal#cool!` is non-idempotent and depends on `rand`; never assume deterministic
|
|
27
|
+
output from a single run.
|
|
28
|
+
- `Simulator#run` raises `ConfigurationError` ( subclass of `Annealing::Error`) on
|
|
29
|
+
invalid config; validation happens lazily at run time, not at `Simulator.new`.
|
|
30
|
+
|
|
31
|
+
## Style / rubocop
|
|
32
|
+
|
|
33
|
+
- Inherited from `.rubocop.yml` + `.rubocop_todo.yml`. `TargetRubyVersion: 3.4`,
|
|
34
|
+
`Layout/LineLength: Max 120`, double-quoted strings, NewCops enabled with rubocop-
|
|
35
|
+
performance/-minitest/-rake plugins loaded.
|
|
36
|
+
- CI lint job runs `bundle exec rubocop --parallel` on Ruby 4.0; test matrix is 3.4/4.0
|
|
37
|
+
(see `.github/workflows/ruby.yml`). Keep code compatible with 3.4.
|
|
38
|
+
|
|
39
|
+
## Architecture
|
|
40
|
+
|
|
41
|
+
- Entry: `Annealing.simulate` (lib/annealing.rb) — thin wrapper over
|
|
42
|
+
`Annealing::Simulator.new(...).run(...).state`.
|
|
43
|
+
- `Simulator` (lib/annealing/simulator.rb) drives the loop, merging configs at runtime
|
|
44
|
+
and calling `Metal#cool!` each step until `termination_condition` returns truthy.
|
|
45
|
+
- `Metal` (lib/annealing/metal.rb) holds current `state`, `temperature`, and lazily
|
|
46
|
+
computed `energy`; `cool!` applies the probabilistic accept/reject step.
|
|
47
|
+
- `Configuration` (lib/annealing/configuration.rb) plus concrete
|
|
48
|
+
`Configuration::Coolers` and `Configuration::Terminators` provide built-in callables.
|
|
49
|
+
|
|
50
|
+
## Release
|
|
51
|
+
|
|
52
|
+
Bump `lib/annealing/version.rb`, then `bundle exec rake release` (tags, pushes, releases
|
|
53
|
+
to rubygems.org; `rubygems_mfa_required` is set in the gemspec).
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
## [
|
|
1
|
+
## [0.4.1] - 2026-07-16
|
|
2
2
|
|
|
3
|
+
- Add return_best config option. When true, returns the best (lowest energy) state. When false, returns the final state (preserves old behavior).
|
|
4
|
+
- Bump minimum supported Ruby version to 3.4.0 and add Ruby 4.0 support.
|
|
5
|
+
- Refresh development dependencies (rubocop, ruby-prof, and plugins) to versions
|
|
6
|
+
compatible with Ruby 4.0.
|
|
7
|
+
- Bump RuboCop `TargetRubyVersion` to 3.4 and CI test matrix to 3.4 / 4.0.
|
|
8
|
+
|
|
9
|
+
## [0.4.0] - 2023-03-09
|
|
10
|
+
|
|
11
|
+
- Rename method and add comments to clarify use by @chrisbloom7 in #20
|
|
12
|
+
- Config Cleanup: remove logger config by @chrisbloom7 in #21
|
|
13
|
+
- Simply configuration and overrides by @chrisbloom7 in #22
|
|
14
|
+
- Idea: Add library of built-in functions by @chrisbloom7 in #23
|
|
3
15
|
|
|
4
16
|
## [0.3.0] - 2022-02-21
|
|
5
17
|
|
data/Gemfile
CHANGED
|
@@ -5,10 +5,13 @@ source "https://rubygems.org"
|
|
|
5
5
|
# Specify your gem's dependencies in annealing.gemspec
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
gem "
|
|
10
|
-
gem "
|
|
11
|
-
gem "
|
|
12
|
-
gem "rubocop
|
|
13
|
-
gem "rubocop-
|
|
14
|
-
gem "rubocop-
|
|
8
|
+
group :development do
|
|
9
|
+
gem "debug", ">= 1.0.0", require: false
|
|
10
|
+
gem "minitest", "~> 5.22"
|
|
11
|
+
gem "rake", "~> 13.0", ">= 13.0.6"
|
|
12
|
+
gem "rubocop", "~> 1.78"
|
|
13
|
+
gem "rubocop-minitest", "~> 0.37"
|
|
14
|
+
gem "rubocop-performance", "~> 1.25"
|
|
15
|
+
gem "rubocop-rake", "~> 0.7"
|
|
16
|
+
gem "ruby-prof", "~> 1.7", ">= 1.7.0"
|
|
17
|
+
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,49 +1,83 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
annealing (0.
|
|
4
|
+
annealing (0.4.1)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
-
ast (2.4.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
ast (2.4.3)
|
|
10
|
+
base64 (0.3.0)
|
|
11
|
+
debug (1.11.1)
|
|
12
|
+
irb (~> 1.10)
|
|
13
|
+
reline (>= 0.3.8)
|
|
14
|
+
erb (6.0.4)
|
|
15
|
+
io-console (0.8.2)
|
|
16
|
+
irb (1.18.0)
|
|
17
|
+
pp (>= 0.6.0)
|
|
18
|
+
prism (>= 1.3.0)
|
|
19
|
+
rdoc (>= 4.0.0)
|
|
20
|
+
reline (>= 0.4.2)
|
|
21
|
+
json (2.21.1)
|
|
22
|
+
language_server-protocol (3.17.0.6)
|
|
23
|
+
lint_roller (1.1.0)
|
|
24
|
+
logger (1.7.0)
|
|
25
|
+
minitest (5.27.0)
|
|
26
|
+
parallel (2.1.0)
|
|
27
|
+
parser (3.3.12.0)
|
|
19
28
|
ast (~> 2.4.1)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
racc
|
|
30
|
+
pp (0.6.4)
|
|
31
|
+
prettyprint
|
|
32
|
+
prettyprint (0.2.0)
|
|
33
|
+
prism (1.9.0)
|
|
34
|
+
racc (1.8.1)
|
|
35
|
+
rainbow (3.1.1)
|
|
36
|
+
rake (13.4.2)
|
|
37
|
+
rbs (4.0.3)
|
|
38
|
+
logger
|
|
39
|
+
prism (>= 1.6.0)
|
|
40
|
+
tsort
|
|
41
|
+
rdoc (8.0.0)
|
|
42
|
+
erb
|
|
43
|
+
prism (>= 1.6.0)
|
|
44
|
+
rbs (>= 4.0.0)
|
|
45
|
+
tsort
|
|
46
|
+
regexp_parser (2.12.0)
|
|
47
|
+
reline (0.6.3)
|
|
24
48
|
io-console (~> 0.5)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
49
|
+
rubocop (1.88.2)
|
|
50
|
+
json (~> 2.3)
|
|
51
|
+
language_server-protocol (~> 3.17.0.2)
|
|
52
|
+
lint_roller (~> 1.1.0)
|
|
53
|
+
parallel (>= 1.10)
|
|
54
|
+
parser (>= 3.3.0.2)
|
|
29
55
|
rainbow (>= 2.2.2, < 4.0)
|
|
30
|
-
regexp_parser (>=
|
|
31
|
-
|
|
32
|
-
rubocop-ast (>= 1.12.0, < 2.0)
|
|
56
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
57
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
33
58
|
ruby-progressbar (~> 1.7)
|
|
34
|
-
unicode-display_width (>=
|
|
35
|
-
rubocop-ast (1.
|
|
36
|
-
parser (>= 3.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
rubocop (>= 1.
|
|
41
|
-
rubocop-ast (>=
|
|
42
|
-
rubocop-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
60
|
+
rubocop-ast (1.50.0)
|
|
61
|
+
parser (>= 3.3.7.2)
|
|
62
|
+
prism (~> 1.7)
|
|
63
|
+
rubocop-minitest (0.39.1)
|
|
64
|
+
lint_roller (~> 1.1)
|
|
65
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
66
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
|
67
|
+
rubocop-performance (1.26.1)
|
|
68
|
+
lint_roller (~> 1.1)
|
|
69
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
70
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
71
|
+
rubocop-rake (0.7.1)
|
|
72
|
+
lint_roller (~> 1.1)
|
|
73
|
+
rubocop (>= 1.72.1)
|
|
74
|
+
ruby-prof (1.7.2)
|
|
75
|
+
base64
|
|
76
|
+
ruby-progressbar (1.13.0)
|
|
77
|
+
tsort (0.2.0)
|
|
78
|
+
unicode-display_width (3.2.0)
|
|
79
|
+
unicode-emoji (~> 4.1)
|
|
80
|
+
unicode-emoji (4.2.0)
|
|
47
81
|
|
|
48
82
|
PLATFORMS
|
|
49
83
|
ruby
|
|
@@ -51,13 +85,13 @@ PLATFORMS
|
|
|
51
85
|
DEPENDENCIES
|
|
52
86
|
annealing!
|
|
53
87
|
debug (>= 1.0.0)
|
|
54
|
-
minitest (~> 5.
|
|
88
|
+
minitest (~> 5.22)
|
|
55
89
|
rake (~> 13.0, >= 13.0.6)
|
|
56
|
-
rubocop (~> 1.
|
|
57
|
-
rubocop-minitest (~> 0.
|
|
58
|
-
rubocop-performance (~> 1.
|
|
59
|
-
rubocop-rake (~> 0.
|
|
60
|
-
ruby-prof (~> 1.
|
|
90
|
+
rubocop (~> 1.78)
|
|
91
|
+
rubocop-minitest (~> 0.37)
|
|
92
|
+
rubocop-performance (~> 1.25)
|
|
93
|
+
rubocop-rake (~> 0.7)
|
|
94
|
+
ruby-prof (~> 1.7, >= 1.7.0)
|
|
61
95
|
|
|
62
96
|
BUNDLED WITH
|
|
63
|
-
2.
|
|
97
|
+
2.7.2
|
data/README.md
CHANGED
|
@@ -102,12 +102,26 @@ The annealer supports a number of configuration options. See the [configuration
|
|
|
102
102
|
|
|
103
103
|
### `cool_down`
|
|
104
104
|
|
|
105
|
-
By default, the simulation will decrease the `temperature` linearly by `cooling_rate` on each step of the annealing process. In some cases you may wish to override this to use a different cooling algorithm. To do so, you can specify a custom `cool_down` function.
|
|
105
|
+
By default, the simulation will decrease the `temperature` linearly by `cooling_rate` on each step of the annealing process. In some cases you may wish to override this to use a different cooling algorithm. To do so, you can use one of the other built-in cooling functions or you can specify a custom `cool_down` function. Custom functions can be any object that responds to `#call` and accepts four arguments: the `energy` calculation of the current object, the current `temperature` of the annealer, the `cooling_rate` for the simulation, and the number of `steps` the annealer has taken so far. It should return the new temperature as a Float.
|
|
106
106
|
|
|
107
107
|
```ruby
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
# Use the built-in linear cool-down function (the default)
|
|
109
|
+
Annealing.configuration.cool_down = Annealing::Configuration::Coolers.linear
|
|
110
|
+
|
|
111
|
+
# Use the built-in exponential cool-down function
|
|
112
|
+
Annealing.configuration.cool_down = Annealing::Configuration::Coolers.exponential
|
|
113
|
+
|
|
114
|
+
# Use the built-in geometric cool-down function with a custom ratio (default ratio is 2)
|
|
115
|
+
Annealing.configuration.cool_down = Annealing::Configuration::Coolers.exponential(1.5)
|
|
116
|
+
|
|
117
|
+
# Use a custom cool down function
|
|
118
|
+
Annealing.configuration.cool_down = lambda do |energy, temperature, cooling_rate, steps|
|
|
119
|
+
# Reduce temperature exponentially when the temperature is above 500, then linearly
|
|
120
|
+
if temperature > 500
|
|
121
|
+
Annealing::Configuration::Coolers.exponential.call(energy, temperature, cooling_rate, steps)
|
|
122
|
+
else
|
|
123
|
+
Annealing::Configuration::Coolers.linear.call(energy, temperature, cooling_rate, steps)
|
|
124
|
+
end
|
|
111
125
|
end
|
|
112
126
|
```
|
|
113
127
|
|
|
@@ -161,16 +175,6 @@ calculator = PotentialSalesCalculator.new(8)
|
|
|
161
175
|
Annealing.configuration.energy_calculator = calculator.method(:energy)
|
|
162
176
|
```
|
|
163
177
|
|
|
164
|
-
### `logger`
|
|
165
|
-
|
|
166
|
-
The default logger is a standard Ruby Logger that writes to stdout. You can specify a different `logger` if you wish.
|
|
167
|
-
|
|
168
|
-
```ruby
|
|
169
|
-
logger = Logger.new('logfile.log')
|
|
170
|
-
logger.level = Logger::DEBUG
|
|
171
|
-
Annealing.configuration.logger = logger
|
|
172
|
-
```
|
|
173
|
-
|
|
174
178
|
### `state_change`
|
|
175
179
|
|
|
176
180
|
As with `energy_calculator`, you must specify a `state_change` function in order to run any simulations; no default function is provided. The function can be any object that responds to `#call` and accepts a single argument: the `state` representing the current state that should be changed. It should return the changed state.
|
|
@@ -193,15 +197,26 @@ Annealing.configuration.state_change = instance.method(:state_change)
|
|
|
193
197
|
|
|
194
198
|
### `termination_condition`
|
|
195
199
|
|
|
196
|
-
By default, a simulation will run until the temperature reaches 0. In some cases, you might want to specify a termination condition that will stop the annealing process as soon as some other condition is met regardless of the current temperature.
|
|
200
|
+
By default, a simulation will run until the temperature reaches 0. In some cases, you might want to specify a termination condition that will stop the annealing process as soon as some other condition is met regardless of the current temperature. To do so, you can use one of the other built-in termination condition functions or you can specify a custom one. Custom `termination_condition` functions can be any object that responds to `#call` and accepts three arguments: the current `state` of the object, the `energy` calculation of the current object, and the current `temperature` of the simulation. It should return a boolean value where `true` indicates the simulation should stop.
|
|
197
201
|
|
|
198
202
|
```ruby
|
|
203
|
+
# Use the built-in zero-temperature termination condition function
|
|
204
|
+
Annealing.configuration.termination_condition = Annealing::Configuration::Terminators.temp_is_zero?
|
|
205
|
+
|
|
206
|
+
# Use the built-in zero-energy termination condition function
|
|
207
|
+
Annealing.configuration.termination_condition = Annealing::Configuration::Terminators.energy_or_temp_is_zero?
|
|
208
|
+
|
|
209
|
+
# Use a custom termination condition function
|
|
199
210
|
Annealing.configuration.termination_condition = lambda do |_state, energy, temperature|
|
|
200
|
-
# Stop
|
|
201
|
-
|
|
211
|
+
# Stop if the energy is below 500 and the temperature is below 100, or the temperature is already 0
|
|
212
|
+
temperature <= 0 || (energy <= 500 && temperature <= 500)
|
|
202
213
|
end
|
|
203
214
|
```
|
|
204
215
|
|
|
216
|
+
### 'return_best'
|
|
217
|
+
|
|
218
|
+
If true (the default), will return the best (lowest energy) state seen during the simulation. If false, Simulator#run will return the final state reached when the simulation completes.
|
|
219
|
+
|
|
205
220
|
## Configuration precedence
|
|
206
221
|
|
|
207
222
|
Configuration options can be set globally using `Annealing.configuration` or `Annealing.configure`, on `Annealing::Simulator.new` to be used on all subsequent runs of that instance, and just-in-time on `Annealing.simulate` and `Annealing::Simulator#run`. They are applied in reverse order of precedence.
|
data/annealing.gemspec
CHANGED
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.summary = "Simulated Annealing"
|
|
13
13
|
spec.description = "Simulated Annealing algoritm implementation."
|
|
14
14
|
spec.homepage = "https://github.com/3zcurdia/annealing"
|
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new(">=
|
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.4.0")
|
|
16
16
|
|
|
17
17
|
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
|
18
18
|
|
|
@@ -29,6 +29,5 @@ Gem::Specification.new do |spec|
|
|
|
29
29
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
30
30
|
spec.require_paths = ["lib"]
|
|
31
31
|
|
|
32
|
-
spec.add_development_dependency "ruby-prof", "~> 1.4", ">= 1.4.3"
|
|
33
32
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
34
33
|
end
|
data/bin/run
CHANGED
|
@@ -39,20 +39,25 @@ state_change = lambda do |state|
|
|
|
39
39
|
swapped
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
Annealing.configuration.logger.level = Logger::DEBUG
|
|
43
42
|
simulator = Annealing::Simulator.new(temperature: 10_000, cooling_rate: 0.01)
|
|
44
43
|
solution = simulator.run(locations,
|
|
45
44
|
energy_calculator: energy_calculator,
|
|
46
45
|
state_change: state_change)
|
|
47
46
|
|
|
47
|
+
initial_energy = energy_calculator.call(locations)
|
|
48
48
|
puts "\nInitial itinerary:"
|
|
49
|
-
locations.each_cons(2).
|
|
49
|
+
locations.each_cons(2).with_index do |(location1, location2), index|
|
|
50
50
|
puts "Stop ##{index + 1}: #{location1.name} -> #{location2.name} (#{location1.distance(location2)})"
|
|
51
51
|
end
|
|
52
|
-
puts "-------\nEnergy: #{
|
|
52
|
+
puts "-------\nEnergy: #{initial_energy}"
|
|
53
53
|
|
|
54
54
|
puts "\nAnnealed itinerary:"
|
|
55
|
-
solution.state.each_cons(2).
|
|
55
|
+
solution.state.each_cons(2).with_index do |(location1, location2), index|
|
|
56
56
|
puts "Stop ##{index + 1}: #{location1.name} -> #{location2.name} (#{location1.distance(location2)})"
|
|
57
57
|
end
|
|
58
58
|
puts "-------\nEnergy: #{solution.energy}"
|
|
59
|
+
|
|
60
|
+
if solution.energy > initial_energy
|
|
61
|
+
raise "Annealing failed: solution energy #{solution.energy}\
|
|
62
|
+
ought to be less than or equal to than initial energy #{initial_energy}"
|
|
63
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Annealing
|
|
4
|
+
class Configuration
|
|
5
|
+
# Built-in cool down functions
|
|
6
|
+
module Coolers
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Reduce temperature linearly by the cooling rate
|
|
10
|
+
def linear
|
|
11
|
+
lambda { |_energy, temperature, cooling_rate, _steps|
|
|
12
|
+
temperature - cooling_rate
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Reduce temperature exponentially on each step by the cooling rate
|
|
17
|
+
def exponential
|
|
18
|
+
lambda { |_energy, temperature, cooling_rate, steps|
|
|
19
|
+
temperature - (Math.exp(steps - 1) * cooling_rate)
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Reduce temperature geometrically at a given ratio by the cooling rate
|
|
24
|
+
def geometric(ratio = 2)
|
|
25
|
+
unless ratio.positive?
|
|
26
|
+
raise(Annealing::Configuration::ConfigurationError,
|
|
27
|
+
"geometric ratio must be positive")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
lambda { |_energy, temperature, cooling_rate, steps|
|
|
31
|
+
temperature - (cooling_rate * (ratio**(steps - 1)))
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Annealing
|
|
4
|
+
class Configuration
|
|
5
|
+
# Built-in termination condition check functions
|
|
6
|
+
module Terminators
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Returns true when the temperature is at or below zero
|
|
10
|
+
def temp_is_zero?
|
|
11
|
+
lambda { |_state, _energy, temperature|
|
|
12
|
+
temperature <= 0
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns true if a 0-energy state is detected, or when the temperature
|
|
17
|
+
# is at or below zero
|
|
18
|
+
def energy_or_temp_is_zero?
|
|
19
|
+
lambda { |state, energy, temperature|
|
|
20
|
+
energy <= 0 || temp_is_zero?.call(state, energy, temperature)
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -3,28 +3,76 @@
|
|
|
3
3
|
module Annealing
|
|
4
4
|
# It enables the gem configuration
|
|
5
5
|
class Configuration
|
|
6
|
+
DEFAULT_COOLING_RATE = 0.0003
|
|
7
|
+
DEFAULT_INITIAL_TEMPERATURE = 10_000.0
|
|
8
|
+
DEFAULT_RETURN_BEST = false
|
|
9
|
+
|
|
10
|
+
class ConfigurationError < Annealing::Error; end
|
|
11
|
+
|
|
6
12
|
attr_accessor :cool_down,
|
|
7
13
|
:cooling_rate,
|
|
8
14
|
:energy_calculator,
|
|
9
|
-
:
|
|
15
|
+
:return_best,
|
|
10
16
|
:state_change,
|
|
11
17
|
:temperature,
|
|
12
18
|
:termination_condition
|
|
13
19
|
|
|
14
|
-
def initialize
|
|
15
|
-
@cool_down =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@
|
|
20
|
-
@
|
|
21
|
-
@
|
|
22
|
-
|
|
23
|
-
@
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
def initialize(config_hash = {})
|
|
21
|
+
@cool_down = config_hash.fetch(:cool_down, Coolers.linear)
|
|
22
|
+
@cooling_rate = config_hash.fetch(:cooling_rate,
|
|
23
|
+
DEFAULT_COOLING_RATE).to_f
|
|
24
|
+
@energy_calculator = config_hash.fetch(:energy_calculator, nil)
|
|
25
|
+
@return_best = config_hash.fetch(:return_best, DEFAULT_RETURN_BEST)
|
|
26
|
+
@state_change = config_hash.fetch(:state_change, nil)
|
|
27
|
+
@temperature = config_hash.fetch(:temperature,
|
|
28
|
+
DEFAULT_INITIAL_TEMPERATURE).to_f
|
|
29
|
+
@termination_condition = config_hash.fetch(:termination_condition,
|
|
30
|
+
Terminators.temp_is_zero?)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Return new configuration that merges new attributes with current
|
|
34
|
+
def merge(config_hash)
|
|
35
|
+
self.class.new(attributes.merge(config_hash))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def validate!
|
|
39
|
+
message = if !callable?(cool_down)
|
|
40
|
+
"Missing cool down function"
|
|
41
|
+
elsif cooling_rate.negative?
|
|
42
|
+
"Cooling rate cannot be negative"
|
|
43
|
+
elsif !callable?(energy_calculator)
|
|
44
|
+
"Missing energy calculator function"
|
|
45
|
+
elsif ![true, false].include?(return_best)
|
|
46
|
+
"'Return best' specification must be either true or false"
|
|
47
|
+
elsif !callable?(state_change)
|
|
48
|
+
"Missing state change function"
|
|
49
|
+
elsif temperature.negative?
|
|
50
|
+
"Initial temperature cannot be negative"
|
|
51
|
+
elsif !callable?(termination_condition)
|
|
52
|
+
"Missing termination condition function"
|
|
53
|
+
end
|
|
54
|
+
raise(ConfigurationError, message) if message
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def attributes
|
|
60
|
+
{
|
|
61
|
+
cool_down: cool_down,
|
|
62
|
+
cooling_rate: cooling_rate,
|
|
63
|
+
energy_calculator: energy_calculator,
|
|
64
|
+
return_best: return_best,
|
|
65
|
+
state_change: state_change,
|
|
66
|
+
temperature: temperature,
|
|
67
|
+
termination_condition: termination_condition
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def callable?(attribute)
|
|
72
|
+
attribute.respond_to?(:call)
|
|
28
73
|
end
|
|
29
74
|
end
|
|
30
75
|
end
|
|
76
|
+
|
|
77
|
+
require "annealing/configuration/coolers"
|
|
78
|
+
require "annealing/configuration/terminators"
|
data/lib/annealing/metal.rb
CHANGED
|
@@ -3,28 +3,23 @@
|
|
|
3
3
|
module Annealing
|
|
4
4
|
# It manages the total energy of a given collection
|
|
5
5
|
class Metal
|
|
6
|
-
|
|
7
|
-
attr_reader :state, :temperature
|
|
6
|
+
attr_reader :configuration, :state, :temperature
|
|
8
7
|
|
|
9
|
-
def initialize(current_state, current_temperature,
|
|
10
|
-
|
|
8
|
+
def initialize(current_state, current_temperature, configuration = nil)
|
|
9
|
+
@configuration = configuration || Annealing.configuration.merge({})
|
|
11
10
|
@state = current_state
|
|
12
11
|
@temperature = current_temperature
|
|
13
|
-
|
|
14
|
-
raise(ArgumentError, "Missing energy calculator function") unless energy_calculator.respond_to?(:call)
|
|
15
|
-
|
|
16
|
-
raise(ArgumentError, "Missing state change function") unless state_change.respond_to?(:call)
|
|
17
12
|
end
|
|
18
13
|
|
|
19
14
|
def energy
|
|
20
|
-
@energy ||= energy_calculator.call(state)
|
|
15
|
+
@energy ||= configuration.energy_calculator.call(state)
|
|
21
16
|
end
|
|
22
17
|
|
|
23
18
|
# This method is not idempotent!
|
|
24
19
|
# It relies on random probability to select the next state
|
|
25
20
|
def cool!(new_temperature)
|
|
26
21
|
cooled_metal = cool(new_temperature)
|
|
27
|
-
if
|
|
22
|
+
if prefer?(cooled_metal)
|
|
28
23
|
cooled_metal
|
|
29
24
|
else
|
|
30
25
|
@temperature = new_temperature
|
|
@@ -32,32 +27,27 @@ module Annealing
|
|
|
32
27
|
end
|
|
33
28
|
end
|
|
34
29
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
temperature: temperature,
|
|
38
|
-
energy: energy,
|
|
39
|
-
value: state)
|
|
30
|
+
def lower_energy?(cooled_metal)
|
|
31
|
+
cooled_metal.energy < energy
|
|
40
32
|
end
|
|
41
33
|
|
|
42
34
|
private
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
current_config_for(:state_change)
|
|
36
|
+
# True if cooled_metal.energy is lower than current energy, otherwise let
|
|
37
|
+
# probability determine if we should accept a higher value over a lower
|
|
38
|
+
# value
|
|
39
|
+
def prefer?(cooled_metal)
|
|
40
|
+
lower_energy?(cooled_metal) || prefer_despite_higher_energy?(cooled_metal)
|
|
50
41
|
end
|
|
51
42
|
|
|
52
|
-
def
|
|
43
|
+
def prefer_despite_higher_energy?(cooled_metal)
|
|
53
44
|
energy_delta = energy - cooled_metal.energy
|
|
54
|
-
energy_delta.
|
|
55
|
-
(Math::E**(energy_delta / cooled_metal.temperature)) > rand
|
|
45
|
+
(Math::E**(energy_delta / cooled_metal.temperature)) > rand
|
|
56
46
|
end
|
|
57
47
|
|
|
58
48
|
def cool(new_temperature)
|
|
59
|
-
next_state = state_change.call(state)
|
|
60
|
-
Metal.new(next_state, new_temperature,
|
|
49
|
+
next_state = configuration.state_change.call(state)
|
|
50
|
+
Metal.new(next_state, new_temperature, configuration)
|
|
61
51
|
end
|
|
62
52
|
end
|
|
63
53
|
end
|
data/lib/annealing/simulator.rb
CHANGED
|
@@ -3,68 +3,65 @@
|
|
|
3
3
|
module Annealing
|
|
4
4
|
# It runs simulated annealing
|
|
5
5
|
class Simulator
|
|
6
|
-
|
|
6
|
+
attr_reader :configuration
|
|
7
7
|
|
|
8
|
-
def initialize(
|
|
9
|
-
|
|
8
|
+
def initialize(config_hash = {})
|
|
9
|
+
@configuration = Annealing.configuration.merge(config_hash)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
# rubocop:disable Metrics/MethodLength
|
|
12
13
|
def run(initial_state, config_hash = {})
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
current = Metal.new(initial_state,
|
|
16
|
-
|
|
17
|
-
Annealing.logger.debug("Original: #{current}")
|
|
14
|
+
with_runtime_config(config_hash) do |runtime_config|
|
|
15
|
+
initial_temperature = runtime_config.temperature
|
|
16
|
+
current = Metal.new(initial_state, initial_temperature, runtime_config)
|
|
17
|
+
best = current
|
|
18
18
|
steps = 0
|
|
19
|
-
until termination_condition_met?(
|
|
19
|
+
until termination_condition_met?(current, runtime_config)
|
|
20
20
|
steps += 1
|
|
21
|
-
current = reduce_temperature(
|
|
21
|
+
current = reduce_temperature(current, steps, runtime_config)
|
|
22
|
+
# If the current state has lower energy than the previous best (lowest energy) state
|
|
23
|
+
# we've seen so far, the current state is the new best state.
|
|
24
|
+
best = current if best.lower_energy?(current)
|
|
22
25
|
end
|
|
23
|
-
|
|
24
|
-
current
|
|
26
|
+
final_or_best(current, best, runtime_config)
|
|
25
27
|
end
|
|
26
28
|
end
|
|
29
|
+
# rubocop:enable Metrics/MethodLength
|
|
27
30
|
|
|
28
31
|
private
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
# Wrapper for public methods that may use a custom configuration
|
|
34
|
+
def with_runtime_config(config_hash)
|
|
35
|
+
runtime_config = if config_hash.is_a?(Hash) && config_hash.any?
|
|
36
|
+
configuration.merge(config_hash)
|
|
37
|
+
else
|
|
38
|
+
configuration
|
|
39
|
+
end
|
|
40
|
+
runtime_config.validate!
|
|
41
|
+
yield(runtime_config)
|
|
32
42
|
end
|
|
33
43
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
current_config_for(:logger)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def temperature
|
|
43
|
-
current_config_for(:temperature).to_f
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def termination_condition
|
|
47
|
-
current_config_for(:termination_condition)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def reduce_temperature(cool_down, metal, steps)
|
|
51
|
-
new_temperature = cool_down.call(metal.energy, metal.temperature,
|
|
52
|
-
cooling_rate, steps)
|
|
44
|
+
def reduce_temperature(metal, steps, config)
|
|
45
|
+
new_temperature = config.cool_down.call(metal.energy,
|
|
46
|
+
metal.temperature,
|
|
47
|
+
config.cooling_rate,
|
|
48
|
+
steps)
|
|
53
49
|
metal.cool!(new_temperature)
|
|
54
50
|
end
|
|
55
51
|
|
|
56
|
-
def termination_condition_met?(
|
|
57
|
-
termination_condition.call(metal.state,
|
|
52
|
+
def termination_condition_met?(metal, config)
|
|
53
|
+
config.termination_condition.call(metal.state,
|
|
54
|
+
metal.energy,
|
|
55
|
+
metal.temperature)
|
|
58
56
|
end
|
|
59
57
|
|
|
60
|
-
def
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
raise(ArgumentError, "Missing termination condition function") unless termination_condition.respond_to?(:call)
|
|
58
|
+
def final_or_best(final, best, config)
|
|
59
|
+
if config.return_best
|
|
60
|
+
# preserve the temperature
|
|
61
|
+
Metal.new(best.state, final.temperature, config)
|
|
62
|
+
else
|
|
63
|
+
final
|
|
64
|
+
end
|
|
68
65
|
end
|
|
69
66
|
end
|
|
70
67
|
end
|
data/lib/annealing/version.rb
CHANGED
data/lib/annealing.rb
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "logger"
|
|
4
|
-
require "annealing/version"
|
|
5
|
-
require "annealing/configuration"
|
|
6
|
-
require "annealing/configuration/configurator"
|
|
7
|
-
require "annealing/metal"
|
|
8
|
-
require "annealing/simulator"
|
|
9
|
-
|
|
10
3
|
# Simulated Annealing algoritm
|
|
11
4
|
# https://en.wikipedia.org/wiki/Simulated_annealing
|
|
12
5
|
module Annealing
|
|
@@ -23,13 +16,15 @@ module Annealing
|
|
|
23
16
|
|
|
24
17
|
def self.configure
|
|
25
18
|
yield(configuration)
|
|
19
|
+
configuration
|
|
26
20
|
end
|
|
27
21
|
|
|
28
22
|
def self.simulate(initial_state, config_hash = {})
|
|
29
23
|
Simulator.new.run(initial_state, config_hash).state
|
|
30
24
|
end
|
|
31
|
-
|
|
32
|
-
def self.logger
|
|
33
|
-
configuration.logger
|
|
34
|
-
end
|
|
35
25
|
end
|
|
26
|
+
|
|
27
|
+
require "annealing/configuration"
|
|
28
|
+
require "annealing/metal"
|
|
29
|
+
require "annealing/simulator"
|
|
30
|
+
require "annealing/version"
|
data/mise.toml
ADDED
metadata
CHANGED
|
@@ -1,36 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: annealing
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Luis Ezcurdia
|
|
8
8
|
- Chris Bloom
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: exe
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
13
|
-
dependencies:
|
|
14
|
-
- !ruby/object:Gem::Dependency
|
|
15
|
-
name: ruby-prof
|
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
|
17
|
-
requirements:
|
|
18
|
-
- - "~>"
|
|
19
|
-
- !ruby/object:Gem::Version
|
|
20
|
-
version: '1.4'
|
|
21
|
-
- - ">="
|
|
22
|
-
- !ruby/object:Gem::Version
|
|
23
|
-
version: 1.4.3
|
|
24
|
-
type: :development
|
|
25
|
-
prerelease: false
|
|
26
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
27
|
-
requirements:
|
|
28
|
-
- - "~>"
|
|
29
|
-
- !ruby/object:Gem::Version
|
|
30
|
-
version: '1.4'
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: 1.4.3
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
34
13
|
description: Simulated Annealing algoritm implementation.
|
|
35
14
|
email:
|
|
36
15
|
- ing.ezcurdia@gmail.com
|
|
@@ -43,6 +22,7 @@ files:
|
|
|
43
22
|
- ".gitignore"
|
|
44
23
|
- ".rubocop.yml"
|
|
45
24
|
- ".rubocop_todo.yml"
|
|
25
|
+
- AGENTS.md
|
|
46
26
|
- CHANGELOG.md
|
|
47
27
|
- CODE_OF_CONDUCT.md
|
|
48
28
|
- Gemfile
|
|
@@ -56,10 +36,12 @@ files:
|
|
|
56
36
|
- bin/setup
|
|
57
37
|
- lib/annealing.rb
|
|
58
38
|
- lib/annealing/configuration.rb
|
|
59
|
-
- lib/annealing/configuration/
|
|
39
|
+
- lib/annealing/configuration/coolers.rb
|
|
40
|
+
- lib/annealing/configuration/terminators.rb
|
|
60
41
|
- lib/annealing/metal.rb
|
|
61
42
|
- lib/annealing/simulator.rb
|
|
62
43
|
- lib/annealing/version.rb
|
|
44
|
+
- mise.toml
|
|
63
45
|
homepage: https://github.com/3zcurdia/annealing
|
|
64
46
|
licenses:
|
|
65
47
|
- MIT
|
|
@@ -68,7 +50,6 @@ metadata:
|
|
|
68
50
|
source_code_uri: https://github.com/3zcurdia/annealing
|
|
69
51
|
changelog_uri: https://github.com/3zcurdia/annealing/blob/main/CHANGELOG.md
|
|
70
52
|
rubygems_mfa_required: 'true'
|
|
71
|
-
post_install_message:
|
|
72
53
|
rdoc_options: []
|
|
73
54
|
require_paths:
|
|
74
55
|
- lib
|
|
@@ -76,15 +57,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
76
57
|
requirements:
|
|
77
58
|
- - ">="
|
|
78
59
|
- !ruby/object:Gem::Version
|
|
79
|
-
version:
|
|
60
|
+
version: 3.4.0
|
|
80
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
62
|
requirements:
|
|
82
63
|
- - ">="
|
|
83
64
|
- !ruby/object:Gem::Version
|
|
84
65
|
version: '0'
|
|
85
66
|
requirements: []
|
|
86
|
-
rubygems_version:
|
|
87
|
-
signing_key:
|
|
67
|
+
rubygems_version: 4.0.10
|
|
88
68
|
specification_version: 4
|
|
89
69
|
summary: Simulated Annealing
|
|
90
70
|
test_files: []
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Annealing
|
|
4
|
-
class Configuration
|
|
5
|
-
# Configuration mixin
|
|
6
|
-
module Configurator
|
|
7
|
-
def self.included(base)
|
|
8
|
-
base.send :include, InstanceMethods
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# Mixin methods
|
|
12
|
-
module InstanceMethods
|
|
13
|
-
def init_configuration(config_hash = {})
|
|
14
|
-
@instance_configuration = config_hash
|
|
15
|
-
@temporary_configuration = {}
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def with_configuration_overrides(local_config_hash = {})
|
|
19
|
-
@temporary_configuration = local_config_hash
|
|
20
|
-
yield
|
|
21
|
-
ensure
|
|
22
|
-
@temporary_configuration = {}
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def configuration_overrides
|
|
26
|
-
instance_configuration.merge(temporary_configuration)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
attr_accessor :instance_configuration, :temporary_configuration
|
|
32
|
-
|
|
33
|
-
def current_config_for(config)
|
|
34
|
-
temporary_configuration[config] ||
|
|
35
|
-
instance_configuration[config] ||
|
|
36
|
-
Annealing.configuration.public_send(config)
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|