Class: MHL::ParticleSwarmOptimizationSolver
- Inherits:
-
Object
- Object
- MHL::ParticleSwarmOptimizationSolver
- Defined in:
- lib/mhl/particle_swarm_optimization_solver.rb
Instance Method Summary collapse
-
#initialize(opts = {}) ⇒ ParticleSwarmOptimizationSolver
constructor
A new instance of ParticleSwarmOptimizationSolver.
-
#solve(func) ⇒ Object
This is the method that solves the optimization problem.
Constructor Details
#initialize(opts = {}) ⇒ ParticleSwarmOptimizationSolver
Returns a new instance of ParticleSwarmOptimizationSolver.
8 9 10 11 12 13 14 15 16 17 18 19 |
# File 'lib/mhl/particle_swarm_optimization_solver.rb', line 8 def initialize(opts={}) @swarm_size = opts[:swarm_size].to_i unless @swarm_size raise ArgumentError, 'Swarm size is a required parameter!' end @random_position_func = opts[:random_position_func] @random_velocity_func = opts[:random_velocity_func] @start_positions = opts[:start_positions] @exit_condition = opts[:exit_condition] end |
Instance Method Details
#solve(func) ⇒ Object
This is the method that solves the optimization problem
Parameter func is supposed to be a method (or a Proc, a lambda, or any callable object) that accepts the genotype as argument (that is, the set of parameters) and returns the phenotype (that is, the function result)
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/mhl/particle_swarm_optimization_solver.rb', line 26 def solve(func) # setup particles if @start_positions.nil? particles = Array.new(@swarm_size) do { position: Vector[*@random_position_func.call], velocity: Vector[*@random_velocity_func.call] } end else particles = @start_positions.each_slice(2).map do |pos,vel| { position: Vector[*pos], velocity: Vector[*vel] } end end # initialize variables gen = 0 overall_best = nil # completely made up values alpha = 0.5 beta = 0.3 gamma = 0.7 delta = 0.5 epsilon = 0.6 # default behavior is to loop forever begin gen += 1 puts "Starting generation #{gen} at #{Time.now}" # assess height for every particle particles.each do |p| p[:task] = Concurrent::Future.new { func.call(p[:position]) } end # wait for all the evaluations to end particles.each_with_index do |p,i| p[:height] = p[:task].value if p[:highest_value].nil? or p[:height] > p[:highest_value] p[:highest_value] = p[:height] p[:highest_position] = p[:position] end end # find highest particle highest_particle = particles.max_by {|x| x[:height] } # calculate overall best if overall_best.nil? overall_best = highest_particle else overall_best = [ overall_best, highest_particle ].max_by {|x| x[:height] } end # mutate swarm particles.each do |p| # randomly sample particles and use them as informants informants = random_portion(particles) # make sure that p is included among the informants informants << p unless informants.include? p # get fittest informant fittest_informant = informants.max_by {|x| x[:height] } # update velocity p[:velocity] = alpha * p[:velocity] + beta * (p[:highest_position] - p[:position]) + gamma * (fittest_informant[:highest_position] - p[:position]) + delta * (overall_best[:highest_position] - p[:position]) # update position p[:position] = p[:position] + epsilon * p[:velocity] end end while @exit_condition.nil? or !@exit_condition.call(gen, overall_best) end |