Algorithmically 0.1.5 → 0.1.6
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/README.md +6 -6
- data/lib/Algorithmically/Evolutionary/genetic.rb +63 -61
- data/lib/Algorithmically/Neural/perceptron.rb +58 -56
- data/lib/Algorithmically/Stochastic/adaptive_random_search.rb +65 -63
- data/lib/Algorithmically/Stochastic/guided_local_search.rb +89 -87
- data/lib/Algorithmically/Stochastic/hill_climbing.rb +33 -31
- data/lib/Algorithmically/Stochastic/random_search.rb +28 -26
- data/lib/Algorithmically/version.rb +1 -1
- metadata +1 -1
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: c6b37344d80011486740e722946df13475c44ba5
         | 
| 4 | 
            +
              data.tar.gz: 46afbc8274889d7660acf6fd42f5e3e78260851e
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 1831736b94dc14f2234ebfe1c2ca1c3c33f9d1ff301dd81f070c97bce781e517e38929ba6579467f60fda7093f3d267aa71fc28736850f97ae42c9775c4c75e0
         | 
| 7 | 
            +
              data.tar.gz: a68582cd9aebbb76d27b047643d5160ba6ce1e352e45299512088dfcfbc326d5a8a2f22b58d21006098b1af2b9cdc7701210c7eb481294af92b99504e71e7066
         | 
    
        data/README.md
    CHANGED
    
    | @@ -22,20 +22,20 @@ Or install it yourself as: | |
| 22 22 |  | 
| 23 23 | 
             
            ### Evolutionary Algorithms
         | 
| 24 24 |  | 
| 25 | 
            -
                Algorithmically::Genetic.new(100, 64, 100, 0.98)
         | 
| 25 | 
            +
                Algorithmically::Evolutionary::Genetic.new(100, 64, 100, 0.98)
         | 
| 26 26 |  | 
| 27 27 | 
             
            ### Neural Algorithms
         | 
| 28 28 |  | 
| 29 | 
            -
                Algorithmically::Perceptron.new([[0,0,0], [0,1,1], [1,0,1], [1,1,1]], 2, 20, 0.1)
         | 
| 29 | 
            +
                Algorithmically::Neural::Perceptron.new([[0,0,0], [0,1,1], [1,0,1], [1,1,1]], 2, 20, 0.1)
         | 
| 30 30 |  | 
| 31 31 | 
             
            ### Stochastic Algorithms
         | 
| 32 32 |  | 
| 33 | 
            -
                Algorithmically::RandomSearch.new(2, 50)
         | 
| 33 | 
            +
                Algorithmically::Neural::RandomSearch.new(2, 50)
         | 
| 34 34 |  | 
| 35 | 
            -
                Algorithmically::AdaptiveRandomSearch.new(1000, 2, 0.05, 1.3, 3.0, 10, 30)
         | 
| 35 | 
            +
                Algorithmically::Neural::AdaptiveRandomSearch.new(1000, 2, 0.05, 1.3, 3.0, 10, 30)
         | 
| 36 36 |  | 
| 37 | 
            -
                Algorithmically::HillClimbing.new(2, 1000)
         | 
| 37 | 
            +
                Algorithmically::Neural::HillClimbing.new(2, 1000)
         | 
| 38 38 |  | 
| 39 | 
            -
                Algorithmically::GuidedLocalSearch.new(150, [[565,575],[25,185],[345,750],[945,685]], 20, 0.3)
         | 
| 39 | 
            +
                Algorithmically::Neural::GuidedLocalSearch.new(150, [[565,575],[25,185],[345,750],[945,685]], 20, 0.3)
         | 
| 40 40 |  | 
| 41 41 |  | 
| @@ -1,77 +1,79 @@ | |
| 1 | 
            -
            module  | 
| 1 | 
            +
            module Algorithmically
         | 
| 2 | 
            +
              module Evolutionary
         | 
| 2 3 |  | 
| 3 | 
            -
             | 
| 4 | 
            +
                class Genetic
         | 
| 4 5 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
                def onemax(bitstring)
         | 
| 12 | 
            -
                  sum = 0
         | 
| 13 | 
            -
                  bitstring.size.times { |i| sum+=1 if bitstring[i].chr=='1' }
         | 
| 14 | 
            -
                  sum
         | 
| 15 | 
            -
                end
         | 
| 6 | 
            +
                  def initialize(max_gens, num_bits, pop_size, p_crossover)
         | 
| 7 | 
            +
                    p_mutation = 1.0/num_bits
         | 
| 8 | 
            +
                    best = search(max_gens, num_bits, pop_size, p_crossover, p_mutation)
         | 
| 9 | 
            +
                    puts "done! Solution: f=#{best[:fitness]}, s=#{best[:bitstring]}"
         | 
| 10 | 
            +
                  end
         | 
| 16 11 |  | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 12 | 
            +
                  def onemax(bitstring)
         | 
| 13 | 
            +
                    sum = 0
         | 
| 14 | 
            +
                    bitstring.size.times { |i| sum+=1 if bitstring[i].chr=='1' }
         | 
| 15 | 
            +
                    sum
         | 
| 16 | 
            +
                  end
         | 
| 20 17 |  | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
                   | 
| 24 | 
            -
                  (pop[i][:fitness] > pop[j][:fitness]) ? pop[i] : pop[j]
         | 
| 25 | 
            -
                end
         | 
| 18 | 
            +
                  def random_bitstring(num_bits)
         | 
| 19 | 
            +
                    (0...num_bits).inject("") { |s, i| s<<((rand<0.5) ? "1" : "0") }
         | 
| 20 | 
            +
                  end
         | 
| 26 21 |  | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
                     | 
| 31 | 
            -
                    child << ((rand()<rate) ? ((bit=='1') ? "0" : "1") : bit)
         | 
| 22 | 
            +
                  def binary_tournament(pop)
         | 
| 23 | 
            +
                    i, j = rand(pop.size), rand(pop.size)
         | 
| 24 | 
            +
                    j = rand(pop.size) if j==i
         | 
| 25 | 
            +
                    (pop[i][:fitness] > pop[j][:fitness]) ? pop[i] : pop[j]
         | 
| 32 26 | 
             
                  end
         | 
| 33 | 
            -
                  child
         | 
| 34 | 
            -
                end
         | 
| 35 27 |  | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 28 | 
            +
                  def point_mutation(bitstring, rate=1.0/bitstring.size)
         | 
| 29 | 
            +
                    child = ""
         | 
| 30 | 
            +
                    bitstring.size.times do |i|
         | 
| 31 | 
            +
                      bit = bitstring[i].chr
         | 
| 32 | 
            +
                      child << ((rand()<rate) ? ((bit=='1') ? "0" : "1") : bit)
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                    child
         | 
| 35 | 
            +
                  end
         | 
| 41 36 |  | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
                     | 
| 46 | 
            -
                    p2 = selected[0] if i == selected.size-1
         | 
| 47 | 
            -
                    child = {}
         | 
| 48 | 
            -
                    child[:bitstring] = crossover(p1[:bitstring], p2[:bitstring], p_cross)
         | 
| 49 | 
            -
                    child[:bitstring] = point_mutation(child[:bitstring], p_mutation)
         | 
| 50 | 
            -
                    children << child
         | 
| 51 | 
            -
                    children.size >= pop_size
         | 
| 37 | 
            +
                  def crossover(parent1, parent2, rate)
         | 
| 38 | 
            +
                    ""+parent1 if rand()>=rate
         | 
| 39 | 
            +
                    point = 1 + rand(parent1.size-2)
         | 
| 40 | 
            +
                    parent1[0...point]+parent2[point...(parent1.size)]
         | 
| 52 41 | 
             
                  end
         | 
| 53 | 
            -
                  children
         | 
| 54 | 
            -
                end
         | 
| 55 42 |  | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
                     | 
| 43 | 
            +
                  def reproduce(selected, pop_size, p_cross, p_mutation)
         | 
| 44 | 
            +
                    children = []
         | 
| 45 | 
            +
                    selected.each_with_index do |p1, i|
         | 
| 46 | 
            +
                      p2 = (i.modulo(2)==0) ? selected[i+1] : selected[i-1]
         | 
| 47 | 
            +
                      p2 = selected[0] if i == selected.size-1
         | 
| 48 | 
            +
                      child = {}
         | 
| 49 | 
            +
                      child[:bitstring] = crossover(p1[:bitstring], p2[:bitstring], p_cross)
         | 
| 50 | 
            +
                      child[:bitstring] = point_mutation(child[:bitstring], p_mutation)
         | 
| 51 | 
            +
                      children << child
         | 
| 52 | 
            +
                      children.size >= pop_size
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                    children
         | 
| 59 55 | 
             
                  end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                   | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
                     | 
| 65 | 
            -
                     | 
| 66 | 
            -
                     | 
| 67 | 
            -
                     | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def search(max_gens, num_bits, pop_size, p_crossover, p_mutation)
         | 
| 58 | 
            +
                    population = Array.new(pop_size) do |i|
         | 
| 59 | 
            +
                      {:bitstring => random_bitstring(num_bits)}
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
                    population.each { |c| c[:fitness] = onemax(c[:bitstring]) }
         | 
| 62 | 
            +
                    best = population.sort { |x, y| y[:fitness] <=> x[:fitness] }.first
         | 
| 63 | 
            +
                    max_gens.times do |gen|
         | 
| 64 | 
            +
                      selected = Array.new(pop_size) { |i| binary_tournament(population) }
         | 
| 65 | 
            +
                      children = reproduce(selected, pop_size, p_crossover, p_mutation)
         | 
| 66 | 
            +
                      children.each { |c| c[:fitness] = onemax(c[:bitstring]) }
         | 
| 67 | 
            +
                      children.sort! { |x, y| y[:fitness] <=> x[:fitness] }
         | 
| 68 | 
            +
                      best = children.first if children.first[:fitness] >= best[:fitness]
         | 
| 69 | 
            +
                      population = children
         | 
| 70 | 
            +
                      puts " > gen #{gen}, best: #{best[:fitness]}, #{best[:bitstring]}"
         | 
| 71 | 
            +
                      break best[:fitness] == num_bits
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                    best
         | 
| 71 74 | 
             
                  end
         | 
| 72 | 
            -
             | 
| 75 | 
            +
             | 
| 73 76 | 
             
                end
         | 
| 74 77 |  | 
| 75 78 | 
             
              end
         | 
| 76 | 
            -
             | 
| 77 79 | 
             
            end
         | 
| @@ -1,78 +1,80 @@ | |
| 1 | 
            -
            module  | 
| 1 | 
            +
            module Algorithmically
         | 
| 2 | 
            +
              module Neural
         | 
| 2 3 |  | 
| 3 | 
            -
             | 
| 4 | 
            +
                class Perceptron
         | 
| 4 5 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 6 | 
            +
                  def initialize(or_problem, inputs, iterations, learning_rate)
         | 
| 7 | 
            +
                    execute(or_problem, inputs, iterations, learning_rate)
         | 
| 8 | 
            +
                  end
         | 
| 8 9 |  | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 10 | 
            +
                  def random_vector(minmax)
         | 
| 11 | 
            +
                    Array.new(minmax.size) do |i|
         | 
| 12 | 
            +
                      minmax[i][0] + ((minmax[i][1] - minmax[i][0]) * rand())
         | 
| 13 | 
            +
                    end
         | 
| 12 14 | 
             
                  end
         | 
| 13 | 
            -
                end
         | 
| 14 15 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 16 | 
            +
                  def initialize_weights(problem_size)
         | 
| 17 | 
            +
                    minmax = Array.new(problem_size + 1) { [-1.0, 1.0] }
         | 
| 18 | 
            +
                    random_vector(minmax)
         | 
| 19 | 
            +
                  end
         | 
| 19 20 |  | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 21 | 
            +
                  def update_weights(num_inputs, weights, input, out_exp, out_act, l_rate)
         | 
| 22 | 
            +
                    num_inputs.times do |i|
         | 
| 23 | 
            +
                      weights[i] += l_rate * (out_exp - out_act) * input[i]
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                    weights[num_inputs] += l_rate * (out_exp - out_act) * 1.0
         | 
| 23 26 | 
             
                  end
         | 
| 24 | 
            -
                  weights[num_inputs] += l_rate * (out_exp - out_act) * 1.0
         | 
| 25 | 
            -
                end
         | 
| 26 27 |  | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 28 | 
            +
                  def activate(weights, vector)
         | 
| 29 | 
            +
                    sum = weights[weights.size-1] * 1.0
         | 
| 30 | 
            +
                    vector.each_with_index do |input, i|
         | 
| 31 | 
            +
                      sum += weights[i] * input
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                    sum
         | 
| 31 34 | 
             
                  end
         | 
| 32 | 
            -
                  sum
         | 
| 33 | 
            -
                end
         | 
| 34 35 |  | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 36 | 
            +
                  def transfer(activation)
         | 
| 37 | 
            +
                    (activation >= 0) ? 1.0 : 0.0
         | 
| 38 | 
            +
                  end
         | 
| 38 39 |  | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 40 | 
            +
                  def get_output(weights, vector)
         | 
| 41 | 
            +
                    activation = activate(weights, vector)
         | 
| 42 | 
            +
                    transfer(activation)
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  def train_weights(weights, domain, num_inputs, iterations, lrate)
         | 
| 46 | 
            +
                    iterations.times do |epoch|
         | 
| 47 | 
            +
                      error = 0.0
         | 
| 48 | 
            +
                      domain.each do |pattern|
         | 
| 49 | 
            +
                        input = Array.new(num_inputs) { |k| pattern[k].to_f }
         | 
| 50 | 
            +
                        output = get_output(weights, input)
         | 
| 51 | 
            +
                        expected = pattern.last.to_f
         | 
| 52 | 
            +
                        error += (output - expected).abs
         | 
| 53 | 
            +
                        update_weights(num_inputs, weights, input, expected, output, lrate)
         | 
| 54 | 
            +
                      end
         | 
| 55 | 
            +
                      puts "> epoch=#{epoch}, error=#{error}"
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 43 58 |  | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
                    error = 0.0
         | 
| 59 | 
            +
                  def test_weights(weights, domain, num_inputs)
         | 
| 60 | 
            +
                    correct = 0
         | 
| 47 61 | 
             
                    domain.each do |pattern|
         | 
| 48 | 
            -
                       | 
| 49 | 
            -
                      output = get_output(weights,  | 
| 50 | 
            -
                       | 
| 51 | 
            -
                      error += (output - expected).abs
         | 
| 52 | 
            -
                      update_weights(num_inputs, weights, input, expected, output, lrate)
         | 
| 62 | 
            +
                      input_vector = Array.new(num_inputs) { |k| pattern[k].to_f }
         | 
| 63 | 
            +
                      output = get_output(weights, input_vector)
         | 
| 64 | 
            +
                      correct += 1 if output.round == pattern.last
         | 
| 53 65 | 
             
                    end
         | 
| 54 | 
            -
                    puts " | 
| 66 | 
            +
                    puts "Finished test with a score of #{correct}/#{domain.size}"
         | 
| 67 | 
            +
                    correct
         | 
| 55 68 | 
             
                  end
         | 
| 56 | 
            -
                end
         | 
| 57 69 |  | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
                     | 
| 62 | 
            -
                     | 
| 63 | 
            -
                    correct += 1 if output.round == pattern.last
         | 
| 70 | 
            +
                  def execute(domain, num_inputs, iterations, learning_rate)
         | 
| 71 | 
            +
                    weights = initialize_weights(num_inputs)
         | 
| 72 | 
            +
                    train_weights(weights, domain, num_inputs, iterations, learning_rate)
         | 
| 73 | 
            +
                    test_weights(weights, domain, num_inputs)
         | 
| 74 | 
            +
                    weights
         | 
| 64 75 | 
             
                  end
         | 
| 65 | 
            -
                  puts "Finished test with a score of #{correct}/#{domain.size}"
         | 
| 66 | 
            -
                  correct
         | 
| 67 | 
            -
                end
         | 
| 68 76 |  | 
| 69 | 
            -
                def execute(domain, num_inputs, iterations, learning_rate)
         | 
| 70 | 
            -
                  weights = initialize_weights(num_inputs)
         | 
| 71 | 
            -
                  train_weights(weights, domain, num_inputs, iterations, learning_rate)
         | 
| 72 | 
            -
                  test_weights(weights, domain, num_inputs)
         | 
| 73 | 
            -
                  weights
         | 
| 74 77 | 
             
                end
         | 
| 75 78 |  | 
| 76 79 | 
             
              end
         | 
| 77 | 
            -
             | 
| 78 80 | 
             
            end
         | 
| @@ -1,82 +1,84 @@ | |
| 1 | 
            -
            module  | 
| 1 | 
            +
            module Algorithmically
         | 
| 2 | 
            +
              module Stochastic
         | 
| 2 3 |  | 
| 3 | 
            -
             | 
| 4 | 
            +
                class AdaptiveRandomSearch
         | 
| 4 5 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 6 | 
            +
                  def initialize(max_iter, bounds, init_factor, s_factor, l_factor, iter_mult, max_no_impr)
         | 
| 7 | 
            +
                    problem_size = bounds
         | 
| 8 | 
            +
                    @bounds1 = Array.new(problem_size) { |i| [-5, +5] }
         | 
| 9 | 
            +
                    @max_iter = max_iter
         | 
| 10 | 
            +
                    @init_factor = init_factor
         | 
| 11 | 
            +
                    @s_factor = s_factor
         | 
| 12 | 
            +
                    @l_factor = l_factor
         | 
| 13 | 
            +
                    @iter_mult = iter_mult
         | 
| 14 | 
            +
                    @max_no_impr = max_no_impr
         | 
| 15 | 
            +
                    @best = search(@max_iter, @bounds1, @init_factor, @s_factor, @l_factor, @iter_mult, @max_no_impr)
         | 
| 16 | 
            +
                    puts "Done. Best Solution: c=#{@best[:cost]}, v=#{@best[:vector].inspect}"
         | 
| 17 | 
            +
                  end
         | 
| 17 18 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 19 | 
            +
                  def objective_function(vector)
         | 
| 20 | 
            +
                    vector.inject(0) { |sum, x| sum + (x ** 2.0) }
         | 
| 21 | 
            +
                  end
         | 
| 21 22 |  | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 23 | 
            +
                  def rand_in_bounds(min, max)
         | 
| 24 | 
            +
                    min + ((max-min) * rand())
         | 
| 25 | 
            +
                  end
         | 
| 25 26 |  | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 27 | 
            +
                  def random_vector(minmax)
         | 
| 28 | 
            +
                    Array.new(minmax.size) do |i|
         | 
| 29 | 
            +
                      rand_in_bounds(minmax[i][0], minmax[i][1])
         | 
| 30 | 
            +
                    end
         | 
| 29 31 | 
             
                  end
         | 
| 30 | 
            -
                end
         | 
| 31 32 |  | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 33 | 
            +
                  def take_step(minmax, current, step_size)
         | 
| 34 | 
            +
                    position = Array.new(current.size)
         | 
| 35 | 
            +
                    position.size.times do |i|
         | 
| 36 | 
            +
                      min = [minmax[i][0], current[i]-step_size].max
         | 
| 37 | 
            +
                      max = [minmax[i][1], current[i]+step_size].min
         | 
| 38 | 
            +
                      position[i] = rand_in_bounds(min, max)
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                    position
         | 
| 38 41 | 
             
                  end
         | 
| 39 | 
            -
                  position
         | 
| 40 | 
            -
                end
         | 
| 41 42 |  | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 43 | 
            +
                  def large_step_size(iter, step_size, s_factor, l_factor, iter_mult)
         | 
| 44 | 
            +
                    step_size * l_factor if iter > 0 and iter.modulo(iter_mult) == 0
         | 
| 45 | 
            +
                    step_size * s_factor
         | 
| 46 | 
            +
                  end
         | 
| 46 47 |  | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 48 | 
            +
                  def take_steps(bounds, current, step_size, big_stepsize)
         | 
| 49 | 
            +
                    step, big_step = {}, {}
         | 
| 50 | 
            +
                    step[:vector] = take_step(bounds, current[:vector], step_size)
         | 
| 51 | 
            +
                    step[:cost] = objective_function(step[:vector])
         | 
| 52 | 
            +
                    big_step[:vector] = take_step(bounds, current[:vector], big_stepsize)
         | 
| 53 | 
            +
                    big_step[:cost] = objective_function(big_step[:vector])
         | 
| 54 | 
            +
                    return step, big_step
         | 
| 55 | 
            +
                  end
         | 
| 55 56 |  | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 57 | 
            +
                  def search(max_iter, bounds, init_factor, s_factor, l_factor, iter_mult, max_no_impr)
         | 
| 58 | 
            +
                    step_size = (bounds[0][1]-bounds[0][0]) * init_factor
         | 
| 59 | 
            +
                    current, count = {}, 0
         | 
| 60 | 
            +
                    current[:vector] = random_vector(bounds)
         | 
| 61 | 
            +
                    current[:cost] = objective_function(current[:vector])
         | 
| 62 | 
            +
                    max_iter.times do |iter|
         | 
| 63 | 
            +
                      big_stepsize = large_step_size(iter, step_size, s_factor, l_factor, iter_mult)
         | 
| 64 | 
            +
                      step, big_step = take_steps(bounds, current, step_size, big_stepsize)
         | 
| 65 | 
            +
                      if step[:cost] <= current[:cost] or big_step[:cost] <= current[:cost]
         | 
| 66 | 
            +
                        if big_step[:cost] <= step[:cost]
         | 
| 67 | 
            +
                          step_size, current = big_stepsize, big_step
         | 
| 68 | 
            +
                        else
         | 
| 69 | 
            +
                          current = step
         | 
| 70 | 
            +
                        end
         | 
| 71 | 
            +
                        count = 0
         | 
| 67 72 | 
             
                      else
         | 
| 68 | 
            -
                         | 
| 73 | 
            +
                        count += 1
         | 
| 74 | 
            +
                        count, step_size = 0, (step_size/s_factor) if count >= max_no_impr
         | 
| 69 75 | 
             
                      end
         | 
| 70 | 
            -
                       | 
| 71 | 
            -
                    else
         | 
| 72 | 
            -
                      count += 1
         | 
| 73 | 
            -
                      count, step_size = 0, (step_size/s_factor) if count >= max_no_impr
         | 
| 76 | 
            +
                      puts " > iteration #{(iter+1)}, best=#{current[:cost]}"
         | 
| 74 77 | 
             
                    end
         | 
| 75 | 
            -
                     | 
| 78 | 
            +
                    current
         | 
| 76 79 | 
             
                  end
         | 
| 77 | 
            -
             | 
| 80 | 
            +
             | 
| 78 81 | 
             
                end
         | 
| 79 82 |  | 
| 80 83 | 
             
              end
         | 
| 81 | 
            -
             | 
| 82 84 | 
             
            end
         | 
| @@ -1,107 +1,109 @@ | |
| 1 | 
            -
            module  | 
| 1 | 
            +
            module Algorithmically
         | 
| 2 | 
            +
              module Stochastic
         | 
| 2 3 |  | 
| 3 | 
            -
             | 
| 4 | 
            +
                class GuidedLocalSearch
         | 
| 4 5 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
                def euc_2d(c1, c2)
         | 
| 18 | 
            -
                  Math.sqrt((c1[0] - c2[0])**2.0 + (c1[1] - c2[1])**2.0).round
         | 
| 19 | 
            -
                end
         | 
| 6 | 
            +
                  def initialize(max_iterations, berlin52, max_no_improv, lambda)
         | 
| 7 | 
            +
                    @berlin52 = berlin52
         | 
| 8 | 
            +
                    @max_iterations = max_iterations
         | 
| 9 | 
            +
                    @max_no_improv = max_no_improv
         | 
| 10 | 
            +
                    alpha = 0.3
         | 
| 11 | 
            +
                    local_search_optima = 12000.0
         | 
| 12 | 
            +
                    lambda = alpha * (local_search_optima/berlin52.size.to_f)
         | 
| 13 | 
            +
                    @lambda = lambda
         | 
| 14 | 
            +
                    best = search(@max_iterations, @berlin52, @max_no_improv, lambda)
         | 
| 15 | 
            +
                    puts "Done. Best Solution: c=#{best[:cost]}, v=#{best[:vector].inspect}"
         | 
| 16 | 
            +
                  end
         | 
| 20 17 |  | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
                  perm.each_index do |i|
         | 
| 24 | 
            -
                    r = rand(perm.size-i) + i
         | 
| 25 | 
            -
                    perm[r], perm[i] = perm[i], perm[r]
         | 
| 18 | 
            +
                  def euc_2d(c1, c2)
         | 
| 19 | 
            +
                    Math.sqrt((c1[0] - c2[0])**2.0 + (c1[1] - c2[1])**2.0).round
         | 
| 26 20 | 
             
                  end
         | 
| 27 | 
            -
                  perm
         | 
| 28 | 
            -
                end
         | 
| 29 21 |  | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
                   | 
| 38 | 
            -
                  perm[c1...c2] = perm[c1...c2].reverse
         | 
| 39 | 
            -
                  perm
         | 
| 40 | 
            -
                end
         | 
| 22 | 
            +
                  def random_permutation(cities)
         | 
| 23 | 
            +
                    perm = Array.new(cities.size) { |i| i }
         | 
| 24 | 
            +
                    perm.each_index do |i|
         | 
| 25 | 
            +
                      r = rand(perm.size-i) + i
         | 
| 26 | 
            +
                      perm[r], perm[i] = perm[i], perm[r]
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                    perm
         | 
| 29 | 
            +
                  end
         | 
| 41 30 |  | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
                     | 
| 31 | 
            +
                  def stochastic_two_opt(permutation)
         | 
| 32 | 
            +
                    perm = Array.new(permutation)
         | 
| 33 | 
            +
                    c1, c2 = rand(perm.size), rand(perm.size)
         | 
| 34 | 
            +
                    exclude = [c1]
         | 
| 35 | 
            +
                    exclude << ((c1==0) ? perm.size-1 : c1-1)
         | 
| 36 | 
            +
                    exclude << ((c1==perm.size-1) ? 0 : c1+1)
         | 
| 37 | 
            +
                    c2 = rand(perm.size) while exclude.include?(c2)
         | 
| 46 38 | 
             
                    c1, c2 = c2, c1 if c2 < c1
         | 
| 47 | 
            -
                     | 
| 48 | 
            -
                     | 
| 49 | 
            -
                    augmented += d + (lambda * (penalties[c1][c2]))
         | 
| 39 | 
            +
                    perm[c1...c2] = perm[c1...c2].reverse
         | 
| 40 | 
            +
                    perm
         | 
| 50 41 | 
             
                  end
         | 
| 51 | 
            -
                  [distance, augmented]
         | 
| 52 | 
            -
                end
         | 
| 53 42 |  | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 43 | 
            +
                  def augmented_cost(permutation, penalties, cities, lambda)
         | 
| 44 | 
            +
                    distance, augmented = 0, 0
         | 
| 45 | 
            +
                    permutation.each_with_index do |c1, i|
         | 
| 46 | 
            +
                      c2 = (i==permutation.size-1) ? permutation[0] : permutation[i+1]
         | 
| 47 | 
            +
                      c1, c2 = c2, c1 if c2 < c1
         | 
| 48 | 
            +
                      d = euc_2d(cities[c1], cities[c2])
         | 
| 49 | 
            +
                      distance += d
         | 
| 50 | 
            +
                      augmented += d + (lambda * (penalties[c1][c2]))
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                    [distance, augmented]
         | 
| 53 | 
            +
                  end
         | 
| 58 54 |  | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
                   | 
| 63 | 
            -
                    candidate = {:vector => stochastic_two_opt(current[:vector])}
         | 
| 64 | 
            -
                    cost(candidate, penalties, cities, lambda)
         | 
| 65 | 
            -
                    count = (candidate[:aug_cost] < current[:aug_cost]) ? 0 : count+1
         | 
| 66 | 
            -
                    current = candidate if candidate[:aug_cost] < current[:aug_cost]
         | 
| 67 | 
            -
                  end until count >= max_no_improv
         | 
| 68 | 
            -
                  current
         | 
| 69 | 
            -
                end
         | 
| 55 | 
            +
                  def cost(cand, penalties, cities, lambda)
         | 
| 56 | 
            +
                    cost, acost = augmented_cost(cand[:vector], penalties, cities, lambda)
         | 
| 57 | 
            +
                    cand[:cost], cand[:aug_cost] = cost, acost
         | 
| 58 | 
            +
                  end
         | 
| 70 59 |  | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
                     | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 60 | 
            +
                  def local_search(current, cities, penalties, max_no_improv, lambda)
         | 
| 61 | 
            +
                    cost(current, penalties, cities, lambda)
         | 
| 62 | 
            +
                    count = 0
         | 
| 63 | 
            +
                    begin
         | 
| 64 | 
            +
                      candidate = {:vector => stochastic_two_opt(current[:vector])}
         | 
| 65 | 
            +
                      cost(candidate, penalties, cities, lambda)
         | 
| 66 | 
            +
                      count = (candidate[:aug_cost] < current[:aug_cost]) ? 0 : count+1
         | 
| 67 | 
            +
                      current = candidate if candidate[:aug_cost] < current[:aug_cost]
         | 
| 68 | 
            +
                    end until count >= max_no_improv
         | 
| 69 | 
            +
                    current
         | 
| 77 70 | 
             
                  end
         | 
| 78 | 
            -
                  utilities
         | 
| 79 | 
            -
                end
         | 
| 80 71 |  | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 72 | 
            +
                  def calculate_feature_utilities(penal, cities, permutation)
         | 
| 73 | 
            +
                    utilities = Array.new(permutation.size, 0)
         | 
| 74 | 
            +
                    permutation.each_with_index do |c1, i|
         | 
| 75 | 
            +
                      c2 = (i==permutation.size-1) ? permutation[0] : permutation[i+1]
         | 
| 76 | 
            +
                      c1, c2 = c2, c1 if c2 < c1
         | 
| 77 | 
            +
                      utilities[i] = euc_2d(cities[c1], cities[c2]) / (1.0 + penal[c1][c2])
         | 
| 78 | 
            +
                    end
         | 
| 79 | 
            +
                    utilities
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def update_penalties!(penalties, cities, permutation, utilities)
         | 
| 83 | 
            +
                    max = utilities.max()
         | 
| 84 | 
            +
                    permutation.each_with_index do |c1, i|
         | 
| 85 | 
            +
                      c2 = (i==permutation.size-1) ? permutation[0] : permutation[i+1]
         | 
| 86 | 
            +
                      c1, c2 = c2, c1 if c2 < c1
         | 
| 87 | 
            +
                      penalties[c1][c2] += 1 if utilities[i] == max
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                    penalties
         | 
| 87 90 | 
             
                  end
         | 
| 88 | 
            -
                  penalties
         | 
| 89 | 
            -
                end
         | 
| 90 91 |  | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 92 | 
            +
                  def search(max_iterations, cities, max_no_improv, lambda)
         | 
| 93 | 
            +
                    current = {:vector => random_permutation(cities)}
         | 
| 94 | 
            +
                    best = nil
         | 
| 95 | 
            +
                    penalties = Array.new(cities.size) { Array.new(cities.size, 0) }
         | 
| 96 | 
            +
                    max_iterations.times do |iter|
         | 
| 97 | 
            +
                      current=local_search(current, cities, penalties, max_no_improv, lambda)
         | 
| 98 | 
            +
                      utilities=calculate_feature_utilities(penalties, cities, current[:vector])
         | 
| 99 | 
            +
                      update_penalties!(penalties, cities, current[:vector], utilities)
         | 
| 100 | 
            +
                      best = current if best.nil? or current[:cost] < best[:cost]
         | 
| 101 | 
            +
                      puts " > iter=#{(iter+1)}, best=#{best[:cost]}, aug=#{best[:aug_cost]}"
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                    best
         | 
| 101 104 | 
             
                  end
         | 
| 102 | 
            -
             | 
| 105 | 
            +
             | 
| 103 106 | 
             
                end
         | 
| 104 107 |  | 
| 105 108 | 
             
              end
         | 
| 106 | 
            -
             | 
| 107 109 | 
             
            end
         | 
| @@ -1,42 +1,44 @@ | |
| 1 | 
            -
            module  | 
| 1 | 
            +
            module Algorithmically
         | 
| 2 | 
            +
              module Stochastic
         | 
| 2 3 |  | 
| 3 | 
            -
             | 
| 4 | 
            +
                class HillClimbing
         | 
| 4 5 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 6 | 
            +
                  def initialize(max_iterations, num_bits)
         | 
| 7 | 
            +
                    best = search(max_iterations, num_bits)
         | 
| 8 | 
            +
                    puts "Done. Best Solution: c=#{best[:cost]}, v=#{best[:vector].join}"
         | 
| 9 | 
            +
                  end
         | 
| 9 10 |  | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 11 | 
            +
                  def onemax(vector)
         | 
| 12 | 
            +
                    vector.inject(0.0) { |sum, v| sum + ((v=="1") ? 1 : 0) }
         | 
| 13 | 
            +
                  end
         | 
| 13 14 |  | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 15 | 
            +
                  def random_bitstring(num_bits)
         | 
| 16 | 
            +
                    Array.new(num_bits) { |i| (rand<0.5) ? "1" : "0" }
         | 
| 17 | 
            +
                  end
         | 
| 17 18 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 19 | 
            +
                  def random_neighbor(bitstring)
         | 
| 20 | 
            +
                    mutant = Array.new(bitstring)
         | 
| 21 | 
            +
                    pos = rand(bitstring.size)
         | 
| 22 | 
            +
                    mutant[pos] = (mutant[pos]=='1') ? '0' : '1'
         | 
| 23 | 
            +
                    mutant
         | 
| 24 | 
            +
                  end
         | 
| 24 25 |  | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 26 | 
            +
                  def search(max_iterations, num_bits)
         | 
| 27 | 
            +
                    candidate = {}
         | 
| 28 | 
            +
                    candidate[:vector] = random_bitstring(num_bits)
         | 
| 29 | 
            +
                    candidate[:cost] = onemax(candidate[:vector])
         | 
| 30 | 
            +
                    max_iterations.times do |iter|
         | 
| 31 | 
            +
                      neighbor = {}
         | 
| 32 | 
            +
                      neighbor[:vector] = random_neighbor(candidate[:vector])
         | 
| 33 | 
            +
                      neighbor[:cost] = onemax(neighbor[:vector])
         | 
| 34 | 
            +
                      candidate = neighbor if neighbor[:cost] >= candidate[:cost]
         | 
| 35 | 
            +
                      puts " > iteration #{(iter+1)}, best=#{candidate[:cost]}"
         | 
| 36 | 
            +
                      break if candidate[:cost] == num_bits
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                    candidate
         | 
| 36 39 | 
             
                  end
         | 
| 37 | 
            -
             | 
| 40 | 
            +
             | 
| 38 41 | 
             
                end
         | 
| 39 42 |  | 
| 40 43 | 
             
              end
         | 
| 41 | 
            -
             | 
| 42 44 | 
             
            end
         | 
| @@ -1,37 +1,39 @@ | |
| 1 | 
            -
            module  | 
| 1 | 
            +
            module Algorithmically
         | 
| 2 | 
            +
              module Stochastic
         | 
| 2 3 |  | 
| 3 | 
            -
             | 
| 4 | 
            +
                class RandomSearch
         | 
| 4 5 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 6 | 
            +
                  def initialize(size, max_iter)
         | 
| 7 | 
            +
                    problem_size = size
         | 
| 8 | 
            +
                    search_space = Array.new(problem_size) { |i| [-5, +5] }
         | 
| 9 | 
            +
                    maximum_iterations = max_iter
         | 
| 10 | 
            +
                    best_solution = self.search(search_space, maximum_iterations)
         | 
| 11 | 
            +
                    puts "Done. Best Solution: c = #{best_solution[:cost]}, v = #{best_solution[:vector].inspect}"
         | 
| 12 | 
            +
                  end
         | 
| 12 13 |  | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 14 | 
            +
                  def objective_function(vector)
         | 
| 15 | 
            +
                    vector.inject(0) { |sum, x| sum + (x ** 2.0) }
         | 
| 16 | 
            +
                  end
         | 
| 16 17 |  | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 18 | 
            +
                  def random_vector(minmax)
         | 
| 19 | 
            +
                    Array.new(minmax.size) do |i|
         | 
| 20 | 
            +
                      minmax[i][0] + ((minmax[i][1]) - minmax[i][0] * rand())
         | 
| 21 | 
            +
                    end
         | 
| 20 22 | 
             
                  end
         | 
| 21 | 
            -
                end
         | 
| 22 23 |  | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 24 | 
            +
                  def search(search_space, maximum_iterations)
         | 
| 25 | 
            +
                    best_solution = nil
         | 
| 26 | 
            +
                    maximum_iterations.times do |iterate|
         | 
| 27 | 
            +
                      search_candidate = {}
         | 
| 28 | 
            +
                      search_candidate[:vector] = random_vector(search_space)
         | 
| 29 | 
            +
                      search_candidate[:cost] = objective_function(search_candidate[:vector])
         | 
| 30 | 
            +
                      best_solution = search_candidate if best_solution.nil? or search_candidate[:cost] < best_solution[:cost]
         | 
| 31 | 
            +
                      puts " > iteration = #{(iterate + 1)}, best = #{best_solution[:cost]}"
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                    best_solution
         | 
| 31 34 | 
             
                  end
         | 
| 32 | 
            -
             | 
| 35 | 
            +
             | 
| 33 36 | 
             
                end
         | 
| 34 37 |  | 
| 35 38 | 
             
              end
         | 
| 36 | 
            -
             | 
| 37 39 | 
             
            end
         |