Class: MHL::QuantumPSOSolver

Inherits:
Object
  • Object
show all
Defined in:
lib/mhl/quantum_particle_swarm_optimization_solver.rb

Overview

This solver implements the QPSO Type 2 algorithm.

For more information, refer to equation 4.82 of:

SUN11

Jun Sun, Choi-Hong Lai, Xiao-Jun Wu, “Particle Swarm Optimisation:

Classical and Quantum Perspectives“, CRC Press, 2011

Constant Summary collapse

DEFAULT_SWARM_SIZE =
40

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ QuantumPSOSolver

Returns a new instance of QuantumPSOSolver.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/mhl/quantum_particle_swarm_optimization_solver.rb', line 19

def initialize(opts={})
  @swarm_size = opts[:swarm_size].try(:to_i) || DEFAULT_SWARM_SIZE

  @constraints = opts[:constraints]

  @random_position_func = opts[:random_position_func]

  @start_positions = opts[:start_positions]

  @exit_condition  = opts[:exit_condition]

  @pool = Concurrent::FixedThreadPool.new(Concurrent::processor_count * 4)

  case opts[:logger]
  when :stdout
    @logger = Logger.new(STDOUT)
  when :stderr
    @logger = Logger.new(STDERR)
  else
    @logger = opts[:logger]
  end

  @quiet = opts[:quiet]

  if @logger
    @logger.level = opts[:log_level] or Logger::WARN
  end
end

Instance Method Details

#solve(func, params = {}) ⇒ 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)



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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/mhl/quantum_particle_swarm_optimization_solver.rb', line 53

def solve(func, params={})
  # initialize particle positions
  init_pos = if @start_positions
    # start positions have the highest priority
    @start_positions
  elsif @random_position_func
    # random_position_func has the second highest priority
    Array.new(@swarm_size) { @random_position_func.call }
  elsif @constraints
    # constraints were given, so we use them to initialize particle
    # positions. to this end, we adopt the SPSO 2006-2011 random position
    # initialization algorithm [CLERC12].
    Array.new(@swarm_size) do
      min = @constraints[:min]
      max = @constraints[:max]
      # randomization is independent along each dimension
      random_pos = min.zip(max).map do |min_i,max_i|
        min_i + SecureRandom.random_number * (max_i - min_i)
      end
    end
  else
    raise ArgumentError, "Not enough information to initialize particle positions!"
  end

  swarm = QPSOSwarm.new(@swarm_size, init_pos,
                        params.merge(constraints: @constraints))

  # initialize variables
  iter = 0
  overall_best = nil

  # default behavior is to loop forever
  begin
    iter += 1
    @logger.info "QPSO - Starting iteration #{iter}" if @logger

    # create latch to control program termination
    latch = Concurrent::CountDownLatch.new(@swarm_size)

    swarm.each do |particle|
      @pool.post do
        # evaluate target function
        particle.evaluate(func)
        # update latch
        latch.count_down
      end
    end

    # wait for all the evaluations to end
    latch.wait

    # get swarm attractor (the highest particle)
    swarm_attractor = swarm.update_attractor

    # print results
    puts "> iter #{iter}, best: #{swarm_attractor[:position]}, #{swarm_attractor[:height]}" unless @quiet

    # calculate overall best (that plays the role of swarm attractor)
    if overall_best.nil?
      overall_best = swarm_attractor
    else
      overall_best = [ overall_best, swarm_attractor ].max_by {|x| x[:height] }
    end

    # mutate swarm
    swarm.mutate

  end while @exit_condition.nil? or !@exit_condition.call(iter, overall_best)

  overall_best
end