mhl 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6451520f742d76561c0caf54cde03a7da4643898
4
- data.tar.gz: f37fb25a97bb1b718c5ab2e7078f6853574b3749
3
+ metadata.gz: 3e0490b924de2e42d7fd0935673abd7d232b07ed
4
+ data.tar.gz: 51076e97078fc49710f9b6d86dc236e09cac5d99
5
5
  SHA512:
6
- metadata.gz: aa0d5beb542b0889e042486421d8d878b427042c046d8c72b9cea1c4913d9bb01a5ec0f9d38bb34c6d48de49723346c2f9430d30a593bf1eb01d9786292270ea
7
- data.tar.gz: a848727a22034af0a4a59bd58c3db415af71715e3e9fce35d17305312d4649cbecf93d2ee78eee157e3d5dcfc8b158a6ba7b85901e2236c87ce82d159878b7ad
6
+ metadata.gz: a1aac62bdff38e7c43d0bd58c06703ccf0df63a365b9c9e004b8f1a2a8f2cba59d2772a6e37c2dbba2c928e3ce5dffad6fd8b7c258c40979789a09217fdde090
7
+ data.tar.gz: 383e9ce734b17fbb47f843ff91e487b53dfed05a1df65bb70a09678b61eb98948b542eff2f3b45f9435d030ae5f7cbc816dc12ac2ba4447ad86e704469a1db97
data/README.md CHANGED
@@ -1,10 +1,33 @@
1
- #ruby-mhl
1
+ # ruby-mhl - A Ruby metaheuristics library
2
2
 
3
- A Ruby metaheuristics library
3
+ ruby-mhl is a scientific library that provides a fairly large array of advanced
4
+ computational intelligence methods for continuous optimization solutions.
5
+
6
+ More specifically, ruby-mhl currently supports several implementations of
7
+ Genetic Algorithms (bitstring and integer vector genotype representations) and
8
+ Particle Swarm Optimization (constrained PSO, quantum-inspired PSO, and a
9
+ multi-swarm version of quantum-inspired PSO), extended with adaptation
10
+ mechanisms to provide support for dynamic optimization problems.
11
+
12
+ ruby-mhl was designed for _high duty_ target functions, whose evaluation
13
+ typically involves one or more simulation runs, possibly defined on very
14
+ complex domains (or search spaces), and implemented in JRuby for performance
15
+ reasons. To this end, ruby-mhl automatically takes advantage of the parallelism
16
+ provided by the processor.
4
17
 
5
18
 
6
19
  ## Installation
7
20
 
21
+ To install ruby-mhl you first have to install Java and JRuby. This is a system
22
+ dependent step, so I won't show you how to do it. However, if you are on Linux
23
+ or OS X I recommend you to use [rbenv](https://github.com/rbenv/rbenv) to
24
+ install and manage your Ruby installations.
25
+
26
+ Once you have JRuby installed, you need to install bundler:
27
+
28
+ gem install bundler
29
+
30
+
8
31
  ### Stable version
9
32
 
10
33
  You can get the stable version of ruby-mhl by installing the mhl gem from
@@ -12,6 +35,16 @@ RubyGems:
12
35
 
13
36
  gem install mhl
14
37
 
38
+ or by adding:
39
+
40
+ ```ruby
41
+ gem 'mhl'
42
+ ```
43
+
44
+ to your application's Gemfile and running:
45
+
46
+ bundle install
47
+
15
48
  ### Development version
16
49
 
17
50
  If you want to try the development version of ruby-mhl, instead, just place
@@ -26,46 +59,204 @@ in your Gemfile and run:
26
59
  bundle install
27
60
 
28
61
 
29
- ## Examples
62
+ ## Genetic Algorithm (GA)
63
+
64
+ ruby-mhl provides a GA solver capable of working with either the traditional
65
+ bitstring chromosome representation or a integer vector representation variant.
66
+
67
+ #### Example: Solving the parabola function with a integer vector GA
30
68
 
31
69
  Here is an example demonstrating how to find the argument that minimizes the
32
- 2-dimension parabola x_1 ^ 2 + x_2 ^ 2 equation with a genetic algorithm:
70
+ 2-dimension parabola _f(x) = x<sub>1</sub><sup>2</sup> +
71
+ x<sub>2</sub><sup>2</sup>_ equation with a genetic algorithm:
33
72
 
34
73
  ```ruby
35
74
  require 'mhl'
36
75
 
37
76
  solver = MHL::GeneticAlgorithmSolver.new(
38
- :population_size => 40,
77
+ :population_size => 80,
39
78
  :genotype_space_type => :integer,
40
79
  :mutation_probability => 0.5,
41
80
  :recombination_probability => 0.5,
42
81
  :genotype_space_conf => {
43
82
  :dimensions => 2,
44
83
  :recombination_type => :intermediate,
45
- :random_func => lambda { Array.new(2) { rand(20) } }
84
+ :random_func => lambda { Array.new(2) { rand(100) } }
46
85
  },
47
86
  :exit_condition => lambda {|generation,best| best[:fitness] == 0}
48
87
  )
49
88
  solver.solve(Proc.new{|x| -(x[0] ** 2 + x[1] ** 2) })
50
89
  ```
51
90
 
52
- and with particle swarm optimization:
91
+
92
+ ## Particle Swarm Optimization (PSO)
93
+
94
+ ruby-mhl implements the constrained version of PSO, defined by equation 4.30 of
95
+ [SUN11], which we report here for full clarity. The velocity and position
96
+ update equation for particle _i_ are:
97
+
98
+ <!--
99
+ ```latex
100
+ \begin{aligned}
101
+ V_{i,j}(t+1) =& \; \chi [ V_{i,j}(t) + \\
102
+ & \quad C_1 * r_{i,j}(t) * (P_{i,j}(t) - X_{i,j}(t)) + \\
103
+ & \quad C_2 * R_{i,j}(t) * (G_j(t) - X_{i,j}(t)) ] \\
104
+ X_{i,j}(t+1) =& \; X_{i,j}(t) + V_{i,j}(t+1)
105
+ \end{aligned}
106
+ ```
107
+ -->
108
+
109
+ ![Movement equations for Constrained PSO](http://mathurl.com/z9zxe8q.png)
110
+
111
+ In which _X<sub>i</sub>(t) = (X<sub>i,1</sub>(t), ..., X<sub>i,N</sub>(t))_ is
112
+ the particle location, whose components _X<sub>i,j</sub>(t)_ represent the
113
+ decision variables of the problem; _V<sub>i</sub>(t) = (V<sub>i,1</sub>(t),
114
+ ..., V<sub>i,N</sub>(t))_ is a velocity vector which captures the movement of
115
+ the particle; _P<sub>i</sub>(t) = (P<sub>i,1</sub>(t), ...,
116
+ P<sub>i,N</sub>(t))_ is a _particle attractor_ representing the 'highest'
117
+ (best) position that the particle has encountered so far; _G(t)_ is the _swarm
118
+ attractor_, representing the 'highest' (best) position that the entire swarm
119
+ has encountered so far; _r<sub>i,j</sub>(t)_ and _R<sub>i,j</sub>(t)_ are
120
+ random sequences uniformly sampled in the (0,1) interval; and _C<sub>1</sub>_
121
+ and _C<sub>2</sub>_ are constants.
122
+
123
+ Note that, in order to guarantee convergence, we must have:
124
+
125
+ <!--
126
+ ```latex
127
+ \begin{aligned}
128
+ \phi =& C_1 + C_2 > 4\\
129
+ \chi =& \frac{2}{\lvert 2-\phi-\sqrt{\phi^2-4\phi} \rvert}
130
+ \end{aligned}
131
+ ```
132
+ -->
133
+
134
+ ![Convergence criteria for Constrained PSO](http://mathurl.com/zjakqww.png)
135
+
136
+ As a result, by default ruby-mhl sets _C<sub>1</sub> = C<sub>2</sub> = 2.05_
137
+ and calculates &chi; accordingly (approximately 0.72984), which is considered
138
+ the best practice [BLACKWELL04]. For more information about this (much more
139
+ than you'll ever want to know, believe me) please refer to [CLERC02].
140
+
141
+ #### Example: Solving the parabola function with PSO
142
+
143
+ Here is an example demonstrating how to find the argument that minimizes the
144
+ 2-dimension parabola _f(x) = x<sub>1</sub><sup>2</sup> +
145
+ x<sub>2</sub><sup>2</sup>_ equation with PSO:
53
146
 
54
147
  ```ruby
55
148
  require 'mhl'
56
149
 
57
150
  solver = MHL::ParticleSwarmOptimizationSolver.new(
58
- :swarm_size => 40,
59
- :random_position_func => lambda { Array.new(2) { rand(20) } },
60
- :random_velocity_func => lambda { Array.new(2) { rand(10) } },
61
- :exit_condition => lambda {|generation,best| best[:height].abs < 0.001 },
151
+ :swarm_size => 40, # 40 is the default swarm size
152
+ :constraints => {
153
+ :min => [ -100, -100 ],
154
+ :max => [ 100, 100 ],
155
+ },
156
+ :exit_condition => lambda {|iteration,best| best[:height].abs < 0.001 },
62
157
  )
63
158
  solver.solve(Proc.new{|x| -(x[0] ** 2 + x[1] ** 2) })
64
159
  ```
65
160
 
66
- Other examples and a full documentation will be publised as ruby-mhl matures.
161
+
162
+ ## Quantum-Inspired Particle Swarm Optimization (QPSO)
163
+
164
+ Quantum-inspired PSO is another particularly interesting PSO variant. It aims
165
+ at simulating interactions between a group of humans by borrowing concepts
166
+ from (the uncertainty typical of) quantum mechanics.
167
+
168
+ ruby-mhl implements the Quantum-inspired version of PSO (QPSO), Type 2, as
169
+ defined by equation 4.82 of [SUN11], which we report here for full clarity.
170
+
171
+ <!--
172
+ ```latex
173
+ \begin{equation}
174
+ \begin{aligned}
175
+ C_j(t) &= \frac{1}{M} \sum_{i=1}^{M} P_{i,j}(t) \\
176
+ p_{i,j}(t) &= \phi_{i,j}(t) P_{i,j}(t) + (1-\phi_{i,j}(t)) G_j(t) \\
177
+ X_{i,j}(t+1) &= p_{i,j}(t) + \alpha \lvert X_{i,j}(t) - C_j(t) \rvert \ln \frac{1}{u_{i,j}(t+1)}
178
+ \end{aligned}
179
+ \end{equation}
180
+ ```
181
+ -->
182
+
183
+ ![Movement Equations for Quantum-inspired PSO](http://mathurl.com/jkw88ue.png)
184
+
185
+ where _P<sub>i</sub>(t)_ is the personal best of particle _i_; _C(t)_ is
186
+ the mean of the personal bests of all the particles in the swarm; _G(t)_ is the
187
+ swarm attractor; and _&phi;<sub>i,j</sub>(t)_ and _u<sub>i,j</sub>(t+1)_ are
188
+ sequences of random numbers uniformly distributed on the (0,1) interval.
189
+
190
+
191
+ #### Example: Solving the parabola function with QPSO
192
+
193
+ Here is an example demonstrating how to find the argument that minimizes the
194
+ 2-dimension parabola _f(x) = x<sub>1</sub><sup>2</sup> +
195
+ x<sub>2</sub><sup>2</sup>_ equation with PSO:
196
+
197
+ ```ruby
198
+ require 'mhl'
199
+
200
+ solver = MHL::QuantumPSOSolver.new(
201
+ :swarm_size => 40, # 40 is the default swarm size
202
+ :constraints => {
203
+ :min => [ -100, -100 ],
204
+ :max => [ 100, 100 ],
205
+ },
206
+ :exit_condition => lambda {|iteration,best| best[:height].abs < 0.001 },
207
+ )
208
+ solver.solve(Proc.new{|x| -(x[0] ** 2 + x[1] ** 2) })
209
+ ```
67
210
 
68
211
 
69
212
  ## License
70
213
 
71
214
  MIT
215
+
216
+
217
+ ## Publications
218
+
219
+ ruby-mhl was used in the following scientific publications:
220
+
221
+ [TORTONESI16] M. Tortonesi, L. Foschini, "Business-driven Service Placement for
222
+ Highly Dynamic and Distributed Cloud Systems", IEEE Transactions on Cloud
223
+ Computing, 2016 (in print).
224
+
225
+ [TORTONESI15] M.Tortonesi, "Exploring Continuous Optimization Solutions for
226
+ Business-driven IT Managment Problems", in Proceedings of the 14th
227
+ IFIP/IEEE Integrated Network Management Symposium (IM 2015) - Short papers
228
+ track, 11-15 May 2015, Ottawa, Canada.
229
+
230
+ [GRABARNIK14] G. Grabarnik, L. Shwartz, M. Tortonesi, "Business-Driven
231
+ Optimization of Component Placement for Complex Services in Federated Clouds",
232
+ in Proceedings of the 14th IEEE/IFIP Network Operations and Management
233
+ Symposium (NOMS 2014) - Mini-conference track, 5-9 May 2014, Krakow, Poland.
234
+
235
+ [FOSCHINI13] L. Foschini, M. Tortonesi, "Adaptive and Business-driven Service
236
+ Placement in Federated Cloud Computing Environments", in Proceedings of the 8th
237
+ IFIP/IEEE International Workshop on Business-driven IT Management (BDIM 2013),
238
+ 27 May 2013, Ghent, Belgium.
239
+
240
+ If you are interested in ruby-mhl, please consider reading and citing them.
241
+
242
+
243
+ ## References
244
+
245
+ [SUN11] Jun Sun, Choi-Hong Lai, Xiao-Jun Wu, "Particle Swarm Optimisation:
246
+ Classical and Quantum Perspectives", CRC Press, 2011.
247
+
248
+ [CLERC02] M. Clerc, J. Kennedy, "The particle swarm - explosion,
249
+ stability, and convergence in a multidimensional complex space", IEEE
250
+ Transactions on Evolutionary Computation, Vol. 6, No. 1, pp. 58-73,
251
+ 2002, DOI: 10.1109/4235.985692
252
+
253
+ [BLACKWELL04] Tim Blackwell, Jürgen Branke, "Multi-swarm Optimization in
254
+ Dynamic Environments", Applications of Evolutionary Computing, pp. 489-500,
255
+ Springer, 2004. DOI: 10.1007/978-3-540-24653-4\_50
256
+
257
+ [REZAEEJORDEHI13] A. Rezaee Jordehi, J. Jasni, "Parameter selection in particle
258
+ swarm optimisation: a survey", Journal of Experimental & Theoretical Artificial
259
+ Intelligence, Vol. 25, No. 4, pp. 527-542, 2013. DOI: 10.1080/0952813X.2013.782348
260
+
261
+ [CLERC12] M. Clerc, "Standard Particle Swarm Optimisation - From 2006 to 2011",
262
+ available at: [http://clerc.maurice.free.fr/pso/SPSO\_descriptions.pdf](http://clerc.maurice.free.fr/pso/SPSO_descriptions.pdf)
@@ -1,5 +1,3 @@
1
- require 'matrix'
2
-
3
1
  require 'mhl/generic_swarm'
4
2
 
5
3
 
@@ -13,7 +11,7 @@ module MHL
13
11
  def initialize(size, initial_positions, initial_velocities, params={})
14
12
  @size = size
15
13
 
16
- # retrieve ratio between charged (QPSO) and neutral (PSO w/ inertia) particles
14
+ # retrieve ratio between charged (QPSO) and neutral (constrained PSO) particles
17
15
  ratio = (params[:charged_to_neutral_ratio] || DEFAULT_CHARGED_TO_NEUTRAL_RATIO).to_f
18
16
  unless ratio > 0.0
19
17
  raise ArgumentError, 'Parameter :charged_to_neutral_ratio should be a real greater than zero!'
@@ -35,51 +33,65 @@ module MHL
35
33
  # find problem dimension
36
34
  @dimension = initial_positions[0].size
37
35
 
38
- @generation = 1
36
+ @iteration = 1
39
37
 
40
38
  # define procedure to get dynamic value for alpha
41
39
  @get_alpha = if params.has_key? :alpha and params[:alpha].respond_to? :call
42
40
  params[:alpha]
43
41
  else
44
- ->(gen) { (params[:alpha] || DEFAULT_ALPHA).to_f }
42
+ ->(it) { (params[:alpha] || DEFAULT_ALPHA).to_f }
45
43
  end
46
44
 
47
45
  # get values for parameters C1 and C2
48
46
  @c1 = (params[:c1] || DEFAULT_C1).to_f
49
47
  @c2 = (params[:c1] || DEFAULT_C2).to_f
50
48
 
51
- # define procedure to get dynamic value for omega
52
- @get_omega = if params.has_key? :omega and params[:omega].respond_to? :call
53
- params[:omega]
49
+ # define procedure to get dynamic value for chi
50
+ @get_chi = if params.has_key? :chi and params[:chi].respond_to? :call
51
+ params[:chi]
54
52
  else
55
- ->(gen) { (params[:omega] || DEFAULT_OMEGA).to_f }
53
+ ->(it) { (params[:chi] || DEFAULT_CHI).to_f }
54
+ end
55
+
56
+ if params.has_key? :constraints
57
+ puts "ChargedSwarm called w/ constraints: #{params[:constraints]}"
56
58
  end
59
+
60
+ @constraints = params[:constraints]
57
61
  end
58
62
 
59
63
  def mutate
60
64
  # get alpha parameter
61
- alpha = @get_alpha.call(@generation)
65
+ alpha = @get_alpha.call(@iteration)
62
66
 
63
- # get omega parameter
64
- omega = @get_omega.call(@generation)
67
+ # get chi parameter
68
+ chi = @get_chi.call(@iteration)
65
69
 
66
- # this calculates the C_n parameter (basically, the centroid of particle
67
- # attractors) as defined in [SUN11], formulae 4.81 and 4.82
70
+ # this calculates the C_n parameter (the centroid of the set of all the
71
+ # particle attractors) as defined in equations 4.81 and 4.82 of [SUN11].
68
72
  #
69
- # (note: the neutral particles influence the behavior of the charged ones
70
- # not only by defining the swarm attractor, but also by forming this centroid)
71
- c_n = @particles.inject(Vector[*[0]*@dimension]) {|s,p| s += p.attractor[:position] } / @size.to_f
73
+ # Note: we consider ALL the particles here, not just the charged (QPSO)
74
+ # ones. As a result, the neutral particles influence the behavior of the
75
+ # charged ones not only by defining the swarm attractor, but also the
76
+ # centroid.
77
+ attractors = @particles.map {|p| p.attractor[:position] }
78
+ c_n = 0.upto(@dimension-1).map do |j|
79
+ attractors.inject(0.0) {|s,attr| s += attr[j] } / @size.to_f
80
+ end
72
81
 
73
82
  @particles.each_with_index do |p,i|
74
83
  # remember: the particles are kept in a PSO-first and QPSO-last order
75
84
  if i < @num_neutral_particles
76
- p.move(omega, @c1, @c2, @swarm_attractor)
85
+ p.move(chi, @c1, @c2, @swarm_attractor)
77
86
  else
78
87
  p.move(alpha, c_n, @swarm_attractor)
79
88
  end
89
+ if @constraints
90
+ p.remain_within(@constraints)
91
+ end
80
92
  end
81
93
 
82
- @generation += 1
94
+ @iteration += 1
83
95
  end
84
96
  end
85
97
  end
@@ -19,34 +19,6 @@ module MHL
19
19
  end
20
20
  end
21
21
 
22
- def remain_within(constraints)
23
- new_pos = @position.map.with_index do |x,i|
24
- puts "resetting #{x} within #{constraints[:min][i]} and #{constraints[:max][i]}"
25
- d_max = constraints[:max][i]
26
- d_min = constraints[:min][i]
27
- d_size = d_max - d_min
28
- if x > d_max
29
- while x > d_max + d_size
30
- x -= d_size
31
- end
32
- if x > d_max
33
- x = 2 * d_max - x
34
- end
35
- elsif x < d_min
36
- while x < d_min - d_size
37
- x += d_size
38
- end
39
- if x < d_min
40
- x = 2 * d_min - x
41
- end
42
- end
43
- puts "now x is #{x}"
44
- x
45
- end
46
- puts "new_pos: #{new_pos}"
47
- @position = new_pos # Vector[new_pos]
48
- end
49
-
50
22
  end
51
23
 
52
24
  end
@@ -3,20 +3,22 @@ require 'forwardable'
3
3
  module MHL
4
4
  class GenericSwarmBehavior
5
5
 
6
- # The following values were taken from [BLACKWELLBRANKE04] Tim Blackwell,
7
- # Jürgen Branke, "Multi-swarm Optimization in Dynamic Environments",
8
- # Applications of Evolutionary Computing, pp. 489-500, Springer, 2004.
9
- # DOI: 10.1007/978-3-540-24653-4_50
6
+ # The following values are considered a best practice [SUN11] [CLERC02]
7
+ # [BLACKWELLBRANKE04].
10
8
  # C_1 is the cognitive acceleration coefficient
11
9
  DEFAULT_C1 = 2.05
12
10
  # C_2 is the social acceleration coefficient
13
11
  DEFAULT_C2 = 2.05
12
+ # \chi is the constraining factor for normal particles
14
13
  PHI = DEFAULT_C1 + DEFAULT_C2
15
- # \omega is the inertia weight
16
- DEFAULT_OMEGA = 2.0 / (2 - PHI - Math.sqrt(PHI ** 2 - 4.0 * PHI)).abs
17
-
18
- # \alpha is the inertia weight
19
- # According to [SUN11], this looks like a sensible default parameter
14
+ DEFAULT_CHI = 2.0 / (2 - PHI - Math.sqrt((PHI ** 2 - 4.0 * PHI))).abs
15
+
16
+ # \alpha is the contraction-expansion (CE) coefficient for quantum
17
+ # particles [SUN11].
18
+ # In order for the QPSO algorithm to converge, \alpha must be lower than
19
+ # $e^{\gamma} \approx 1.781$, where $\gamma \approx 0.5772156649$ is the
20
+ # Euler constant. According to [SUN11], 0.75 looks like a sensible default
21
+ # parameter.
20
22
  DEFAULT_ALPHA = 0.75
21
23
 
22
24
  extend Forwardable
@@ -29,7 +31,7 @@ module MHL
29
31
  particle_attractors = @particles.map { |p| p.attractor }
30
32
 
31
33
  # update swarm attractor (if needed)
32
- if @swarm_attractor.nil?
34
+ unless (defined?(@swarm_attractor))
33
35
  @swarm_attractor = particle_attractors.max_by {|p| p[:height] }
34
36
  else
35
37
  @swarm_attractor = [ @swarm_attractor, *particle_attractors ].max_by {|p| p[:height] }