mhl 0.1.0 → 0.2.0

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.
@@ -1,8 +1,8 @@
1
- require 'matrix'
2
1
  require 'securerandom'
3
2
 
4
3
  require 'mhl/generic_particle'
5
4
 
5
+
6
6
  module MHL
7
7
  class QuantumParticle < GenericParticle
8
8
  attr_reader :position
@@ -10,7 +10,6 @@ module MHL
10
10
  # move particle using QPSO - Type II algorithm
11
11
  def move(alpha, mean_best, swarm_attractor)
12
12
  raise 'Particle attractor is nil!' if @attractor.nil?
13
- # raise 'Swarm attractor is nil!' if swarm_attractor.nil?
14
13
 
15
14
  dimension = @position.size
16
15
 
@@ -18,21 +17,41 @@ module MHL
18
17
  phi = Array.new(dimension) { SecureRandom.random_number }
19
18
 
20
19
  # p_i represents the p_{i,n} parameter in [SUN11], formulae 4.82 and 4.83
21
- p_i =
22
- Vector[*phi.zip(@attractor[:position]).map {|phi_j,p_j| phi_j * p_j }] +
23
- Vector[*phi.zip(swarm_attractor[:position]).map {|phi_j,g_j| (1.0 - phi_j) * g_j }]
20
+ p_i = phi.zip(@attractor[:position], swarm_attractor[:position]).map do |phi_j,p_j,g_j|
21
+ phi_j * p_j + (1.0 - phi_j) * g_j
22
+ end
24
23
 
25
24
  # delta represents the displacement for the current position.
26
25
  # See [SUN11], formula 4.82
27
- delta =
28
- @position.zip(mean_best).map {|x,y| alpha * (x-y).abs }. # \alpha * | X_{i,n} - C_n |
29
- map {|x| x * Math.log(1.0 / SecureRandom.random_number) } # log(\frac{1}{u_{i,n+1}})
26
+ delta = @position.zip(mean_best).map do |x_n,c_n|
27
+ # \alpha * | X_{i,n} - C_n | * log(\frac{1}{u_{i,n+1}})
28
+ alpha * (x_n - c_n).abs * Math.log(1.0 / SecureRandom.random_number)
29
+ end
30
30
 
31
31
  # update position
32
32
  if SecureRandom.random_number < 0.5
33
- @position = p_i + Vector[*delta]
33
+ @position = p_i.zip(delta).map {|p_in,delta_n| p_in + delta_n }
34
34
  else
35
- @position = p_i - Vector[*delta]
35
+ @position = p_i.zip(delta).map {|p_in,delta_n| p_in - delta_n }
36
+ end
37
+
38
+ @position
39
+ end
40
+
41
+ # implement confinement à la SPSO 2011. for more information, see equations
42
+ # 3.14 of [CLERC12].
43
+ def remain_within(constraints)
44
+ @position = @position.map.with_index do |x_j,j|
45
+ d_max = constraints[:max][j]
46
+ d_min = constraints[:min][j]
47
+ if x_j > d_max
48
+ # puts "resetting #{j}-th position component #{x_j} to #{d_max}"
49
+ x_j = d_max
50
+ elsif x_j < d_min
51
+ # puts "resetting #{j}-th position component #{x_j} to #{d_min}"
52
+ x_j = d_min
53
+ end
54
+ x_j
36
55
  end
37
56
  end
38
57
 
@@ -1,7 +1,5 @@
1
1
  require 'concurrent'
2
- require 'facter'
3
2
  require 'logger'
4
- require 'matrix'
5
3
  require 'securerandom'
6
4
 
7
5
  require 'mhl/qpso_swarm'
@@ -16,18 +14,20 @@ module MHL
16
14
  # Classical and Quantum Perspectives", CRC Press, 2011
17
15
  class QuantumPSOSolver
18
16
 
17
+ DEFAULT_SWARM_SIZE = 40
18
+
19
19
  def initialize(opts={})
20
- @swarm_size = opts[:swarm_size].to_i
21
- unless @swarm_size
22
- raise ArgumentError, 'Swarm size is a required parameter!'
23
- end
20
+ @swarm_size = opts[:swarm_size].try(:to_i) || DEFAULT_SWARM_SIZE
21
+
22
+ @constraints = opts[:constraints]
24
23
 
25
24
  @random_position_func = opts[:random_position_func]
26
25
 
27
26
  @start_positions = opts[:start_positions]
27
+
28
28
  @exit_condition = opts[:exit_condition]
29
29
 
30
- @pool = Concurrent::FixedThreadPool.new(Facter.value(:processorcount).to_i * 4)
30
+ @pool = Concurrent::FixedThreadPool.new(Concurrent::processor_count * 4)
31
31
 
32
32
  case opts[:logger]
33
33
  when :stdout
@@ -51,26 +51,40 @@ module MHL
51
51
  # object) that accepts the genotype as argument (that is, the set of
52
52
  # parameters) and returns the phenotype (that is, the function result)
53
53
  def solve(func, params={})
54
- # setup swarm
55
- if @start_positions.nil?
56
- swarm = QPSOSwarm.new(@swarm_size,
57
- Array.new(@swarm_size) { Vector[*@random_position_func.call] },
58
- params)
54
+ # initialize particle positions
55
+ init_pos = if @start_positions
56
+ # start positions have the highest priority
57
+ @start_positions
58
+ elsif @random_position_func
59
+ # random_position_func has the second highest priority
60
+ Array.new(@swarm_size) { @random_position_func.call }
61
+ elsif @constraints
62
+ # constraints were given, so we use them to initialize particle
63
+ # positions. to this end, we adopt the SPSO 2006-2011 random position
64
+ # initialization algorithm [CLERC12].
65
+ Array.new(@swarm_size) do
66
+ min = @constraints[:min]
67
+ max = @constraints[:max]
68
+ # randomization is independent along each dimension
69
+ random_pos = min.zip(max).map do |min_i,max_i|
70
+ min_i + SecureRandom.random_number * (max_i - min_i)
71
+ end
72
+ end
59
73
  else
60
- raise 'Unimplemented yet!'
61
- # particles = @start_positions.map do |pos|
62
- # { position: Vector[*pos] }
63
- # end
74
+ raise ArgumentError, "Not enough information to initialize particle positions!"
64
75
  end
65
76
 
77
+ swarm = QPSOSwarm.new(@swarm_size, init_pos,
78
+ params.merge(constraints: @constraints))
79
+
66
80
  # initialize variables
67
- gen = 0
81
+ iter = 0
68
82
  overall_best = nil
69
83
 
70
84
  # default behavior is to loop forever
71
85
  begin
72
- gen += 1
73
- @logger.info "QPSO - Starting generation #{gen}" if @logger
86
+ iter += 1
87
+ @logger.info "QPSO - Starting iteration #{iter}" if @logger
74
88
 
75
89
  # create latch to control program termination
76
90
  latch = Concurrent::CountDownLatch.new(@swarm_size)
@@ -91,7 +105,7 @@ module MHL
91
105
  swarm_attractor = swarm.update_attractor
92
106
 
93
107
  # print results
94
- puts "> gen #{gen}, best: #{swarm_attractor[:position]}, #{swarm_attractor[:height]}" unless @quiet
108
+ puts "> iter #{iter}, best: #{swarm_attractor[:position]}, #{swarm_attractor[:height]}" unless @quiet
95
109
 
96
110
  # calculate overall best (that plays the role of swarm attractor)
97
111
  if overall_best.nil?
@@ -103,7 +117,7 @@ module MHL
103
117
  # mutate swarm
104
118
  swarm.mutate
105
119
 
106
- end while @exit_condition.nil? or !@exit_condition.call(gen, overall_best)
120
+ end while @exit_condition.nil? or !@exit_condition.call(iter, overall_best)
107
121
 
108
122
  overall_best
109
123
  end
@@ -1,3 +1,3 @@
1
1
  module MHL
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -19,10 +19,9 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_dependency 'bitstring'
22
- spec.add_dependency 'concurrent-ruby', '~> 0.5'
22
+ spec.add_dependency 'concurrent-ruby', '~> 1.0'
23
23
  spec.add_dependency 'erv'
24
- spec.add_dependency 'facter'
25
24
 
26
- spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'bundler'
27
26
  spec.add_development_dependency 'rake'
28
27
  end
@@ -4,11 +4,12 @@ describe MHL::MultiSwarmQPSOSolver do
4
4
 
5
5
  it 'should solve a 2-dimension parabola in real space' do
6
6
  solver = MHL::MultiSwarmQPSOSolver.new(
7
- :swarm_size => 20,
8
7
  :num_swarms => 4,
9
- :random_position_func => lambda { Array.new(2) { rand(20).to_f } },
10
- :random_velocity_func => lambda { Array.new(2) { rand(10).to_f } },
11
- :exit_condition => lambda {|generation,best_sample| best_sample[:height].abs < 0.001 },
8
+ :constraints => {
9
+ :min => [ -100, -100 ],
10
+ :max => [ 100, 100 ],
11
+ },
12
+ :exit_condition => lambda {|iteration,best| best[:height].abs < 0.001 },
12
13
  :logger => :stdout,
13
14
  :log_level => ENV['DEBUG'] ? Logger::DEBUG : Logger::WARN,
14
15
  )
@@ -4,10 +4,11 @@ describe MHL::ParticleSwarmOptimizationSolver do
4
4
 
5
5
  it 'should solve a 2-dimension parabola in real space' do
6
6
  solver = MHL::ParticleSwarmOptimizationSolver.new(
7
- :swarm_size => 40,
8
- :random_position_func => lambda { Array.new(2) { rand(20) } },
9
- :random_velocity_func => lambda { Array.new(2) { rand(10) } },
10
- :exit_condition => lambda {|generation,best_sample| best_sample[:height].abs < 0.001 },
7
+ :constraints => {
8
+ :min => [ -100, -100 ],
9
+ :max => [ 100, 100 ],
10
+ },
11
+ :exit_condition => lambda {|iteration,best| best[:height].abs < 0.001 },
11
12
  :logger => :stderr,
12
13
  :log_level => ENV['DEBUG'] ? Logger::DEBUG : Logger::WARN,
13
14
  )
@@ -4,9 +4,11 @@ describe MHL::QuantumPSOSolver do
4
4
 
5
5
  it 'should solve a 2-dimension parabola in real space' do
6
6
  solver = MHL::QuantumPSOSolver.new(
7
- :swarm_size => 40,
8
- :random_position_func => lambda { Array.new(2) { rand(20) } },
9
- :exit_condition => lambda {|generation,best_sample| best_sample[:height].abs < 0.001 },
7
+ :constraints => {
8
+ :min => [ -100, -100 ],
9
+ :max => [ 100, 100 ],
10
+ },
11
+ :exit_condition => lambda {|iteration,best| best[:height].abs < 0.001 },
10
12
  :logger => :stderr,
11
13
  :log_level => ENV['DEBUG'] ? Logger::DEBUG : Logger::WARN,
12
14
  )
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mhl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mauro Tortonesi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-19 00:00:00.000000000 Z
11
+ date: 2016-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - '>='
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '0'
19
19
  name: bitstring
@@ -21,27 +21,27 @@ dependencies:
21
21
  type: :runtime
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - ~>
30
+ - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '0.5'
32
+ version: '1.0'
33
33
  name: concurrent-ruby
34
34
  prerelease: false
35
35
  type: :runtime
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.5'
40
+ version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - '>='
44
+ - - ">="
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
47
  name: erv
@@ -49,41 +49,27 @@ dependencies:
49
49
  type: :runtime
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - '>='
58
+ - - ">="
59
59
  - !ruby/object:Gem::Version
60
60
  version: '0'
61
- name: facter
62
- prerelease: false
63
- type: :runtime
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - '>='
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- requirement: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - ~>
73
- - !ruby/object:Gem::Version
74
- version: '1.3'
75
61
  name: bundler
76
62
  prerelease: false
77
63
  type: :development
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
- - - ~>
66
+ - - ">="
81
67
  - !ruby/object:Gem::Version
82
- version: '1.3'
68
+ version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  requirement: !ruby/object:Gem::Requirement
85
71
  requirements:
86
- - - '>='
72
+ - - ">="
87
73
  - !ruby/object:Gem::Version
88
74
  version: '0'
89
75
  name: rake
@@ -91,7 +77,7 @@ dependencies:
91
77
  type: :development
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
- - - '>='
80
+ - - ">="
95
81
  - !ruby/object:Gem::Version
96
82
  version: '0'
97
83
  description: A Ruby Metaheuristics library
@@ -137,17 +123,17 @@ require_paths:
137
123
  - lib
138
124
  required_ruby_version: !ruby/object:Gem::Requirement
139
125
  requirements:
140
- - - '>='
126
+ - - ">="
141
127
  - !ruby/object:Gem::Version
142
128
  version: '0'
143
129
  required_rubygems_version: !ruby/object:Gem::Requirement
144
130
  requirements:
145
- - - '>='
131
+ - - ">="
146
132
  - !ruby/object:Gem::Version
147
133
  version: '0'
148
134
  requirements: []
149
135
  rubyforge_project:
150
- rubygems_version: 2.1.9
136
+ rubygems_version: 2.6.4
151
137
  signing_key:
152
138
  specification_version: 4
153
139
  summary: A scientific library for Ruby that provides several metaheuristics