Class: Gargor

Inherits:
Object
  • Object
show all
Defined in:
lib/gargor.rb,
lib/gargor/version.rb,
lib/gargor/parameter.rb,
lib/gargor/individual.rb

Defined Under Namespace

Classes: ArgumentError, ExterminationError, GargorError, Individual, Individuals, Parameter, ParameterError, ValidationError

Constant Summary collapse

GLOBAL_OPTS =
["population","max_generations","target_nodes",
"attack_cmd","elite","mutation","target_cooking_cmd",
"fitness_precision"]
VERSION =
File.read(File.expand_path(File.join(File.dirname(__FILE__),"..","..","VERSION")))

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.crossover(a, b) ⇒ Object



114
115
116
117
118
119
120
121
# File 'lib/gargor.rb', line 114

def crossover a,b
  return a.clone if a.params == b.params
  log "crossover: #{a} #{b}"
  total = a.fitness + b.fitness
  c = Individual.new
  c.params = a.params.clone
  c.overwrite_by(b,b.fitness,total)
end

.debug(message) ⇒ Object



41
42
43
# File 'lib/gargor.rb', line 41

def debug message
  log message,Logger::DEBUG
end

.first_generation?Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/gargor.rb', line 77

def first_generation?
  @@generation == 1
end

.float_rand(f, p = @@fitness_precision) ⇒ Object

浮動小数点対応のrand

Raises:



106
107
108
109
110
111
112
# File 'lib/gargor.rb', line 106

def float_rand(f,p = @@fitness_precision)
  raise ArgumentError,"max must be > 0" unless f > 0
  f *= @@fitness_precision
  i = f.to_i
  f = rand(i)
  f / @@fitness_precision.to_f
end

.individualsObject



201
202
203
# File 'lib/gargor.rb', line 201

def individuals
  @@individuals
end

.last_trialsObject



221
222
223
224
# File 'lib/gargor.rb', line 221

def last_trials
  last_trials_at_this_generation +
    (@@max_generations-@@generation)*(@@population-@@elite)
end

.last_trials_at_this_generationObject



217
218
219
# File 'lib/gargor.rb', line 217

def last_trials_at_this_generation
  @@individuals.select{ |i| i.fitness == nil }.count
end

.load_dsl(params_file) ⇒ Object



87
88
89
90
91
92
# File 'lib/gargor.rb', line 87

def load_dsl(params_file)
  @@dsl_file = params_file
  contents = File.read(params_file)
  new.instance_eval(contents)
  validate
end

.log(message, level = Logger::INFO) ⇒ Object



36
37
38
39
# File 'lib/gargor.rb', line 36

def log message,level=Logger::INFO
  return if $TESTING
  message.to_s.split("\n").each { |line| @@logger.add(level) {line} }
end

.logfile(file) ⇒ Object



209
210
211
# File 'lib/gargor.rb', line 209

def logfile file
  File.expand_path(File.join(File.dirname(@@dsl_file),file))
end

.mutateObject



94
95
96
97
98
99
100
101
102
103
# File 'lib/gargor.rb', line 94

def mutate
  individual = Individual.new
  @@param_procs.each { |name,proc|
    param =  Parameter.new(name)
    param.instance_eval(&proc)
    individual.params[name] = param
  }
  log "mutate #{individual}"
  individual
end

.mutation?(mutation = @@mutation) ⇒ Boolean

Returns:

  • (Boolean)


149
150
151
# File 'lib/gargor.rb', line 149

def mutation? mutation=@@mutation
  rand <= mutation
end

.next_generationObject



191
192
193
194
195
196
197
198
199
# File 'lib/gargor.rb', line 191

def next_generation
  log "<== end generation #{@@generation}"
  @@generation += 1
  return false if @@generation > @@max_generations

  log "==> next generation #{@@generation}"
  @@prev_generation = @@individuals
  true
end

.opt(name) ⇒ Object



205
206
207
# File 'lib/gargor.rb', line 205

def opt name
  Gargor.class_variable_get("@@#{name}")
end

.paramsObject



45
46
47
48
49
50
51
# File 'lib/gargor.rb', line 45

def params
  result = {}
  GLOBAL_OPTS.map { |name| 
    result[name] = Gargor.class_variable_get("@@#{name}")
  }
  result
end

.populateObject



179
180
181
182
183
184
185
186
187
188
# File 'lib/gargor.rb', line 179

def populate
  @@individuals = if first_generation?
                    # 第一世代
                    populate_first_generation
                  else
                    # 次世代
                    raise ExterminationError unless prev_count >= 2
                    populate_next_generation
                  end
end

.populate_first_generationObject



133
134
135
136
137
138
139
140
141
142
# File 'lib/gargor.rb', line 133

def populate_first_generation
  individuals = Gargor::Individuals.new
  individuals << mutate.load_now
  loop{
    break if individuals.length >= @@population
    individuals << mutate
  }
  
  Gargor::Individuals.new(individuals.shuffle)
end

.populate_next_generationObject



165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/gargor.rb', line 165

def populate_next_generation
  log "population: #{@@prev_generation.length}"
  individuals = Gargor::Individuals.new(select_elites @@prev_generation,@@elite)

  loop{
    break if individuals.length >= @@population
    i = populate_one
    individuals << i unless individuals.has?(i)
  }
  log "populate:"
  individuals.each { |i| log i }
  Gargor::Individuals.new(individuals.shuffle)
end

.populate_oneObject



157
158
159
160
161
162
163
# File 'lib/gargor.rb', line 157

def populate_one
  if mutation?
    mutate
  else
    crossover(*select_parents(@@prev_generation))
  end
end

.prev_count(g = @@prev_generation) ⇒ Object

前世代の数



82
83
84
85
# File 'lib/gargor.rb', line 82

def prev_count g = @@prev_generation
  # fitness > 0 適応している個体
  g.select { |i| i.fitness && i.fitness > 0 }.count
end

.select_elites(g, count) ⇒ Object



144
145
146
147
# File 'lib/gargor.rb', line 144

def select_elites g,count
  return [] unless count > 0
  Gargor::Individuals.new(g.sort{ |a,b| a.fitness<=>b.fitness }.last(count))
end

.select_parents(g) ⇒ Object



153
154
155
# File 'lib/gargor.rb', line 153

def select_parents g
  [selection(g),selection(g)]
end

.selection(g) ⇒ Object

Raises:



123
124
125
126
127
128
129
130
131
# File 'lib/gargor.rb', line 123

def selection g
  total = g.inject(0) { |sum,i| sum += i.fitness }
  cur = float_rand(total)
  g.each { |i|
    return i if i.fitness > cur
    cur -= i.fitness
  }
  raise GargorError,"error selection"
end

.startObject



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/gargor.rb', line 53

def start
  @@logger = Logger.new(STDOUT)
  @@fitness_precision = 100000000
  @@prev_generation = nil
  @@individuals = []
  @@param_procs = {}
  @@population = 0
  @@max_generations = 1
  @@generation = 1
  @@elite = 0
  @@attack_cmd = "false"
  @@attack_proc = nil
  @@evaluate_proc = Proc.new { 0 }
  @@target_nodes = []
  @@dsl_file = nil
  true
end

.total_trialsObject



213
214
215
# File 'lib/gargor.rb', line 213

def total_trials
  @@population+(@@population-@@elite)*(@@max_generations-1)
end

.validateObject

Raises:



72
73
74
75
# File 'lib/gargor.rb', line 72

def validate
  raise ValidationError,"POPULATION isn't > 0" unless @@population > 0
  true
end

Instance Method Details

#attack(cmd, &block) ⇒ Object



232
233
234
235
# File 'lib/gargor.rb', line 232

def attack cmd,&block
  @@attack_cmd = cmd
  @@attack_proc = block
end

#evaluate(&block) ⇒ Object



237
238
239
# File 'lib/gargor.rb', line 237

def evaluate &block
  @@evaluate_proc = block
end

#log(message, level = Logger::INFO) ⇒ Object



32
33
34
# File 'lib/gargor.rb', line 32

def log message,level=Logger::INFO
  Gargor.log(message,level)
end

#logger(*args, &block) ⇒ Object



241
242
243
244
245
# File 'lib/gargor.rb', line 241

def logger *args, &block
  file = args.shift
  @@logger = Logger.new(Gargor.logfile(file),*args)
  block.call(@@logger) if block
end

#param(name, &block) ⇒ Object



228
229
230
# File 'lib/gargor.rb', line 228

def param name,&block
  @@param_procs[name] = block
end