Skip to content

Commit 2acae82

Browse files
committed
Only evolve population one generation by default + add new evolve method
1 parent bf15b99 commit 2acae82

File tree

5 files changed

+64
-51
lines changed

5 files changed

+64
-51
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Breaking Changes:
1010
* Simplified error handling in favor of more single `Evolvable::Error` type with more informative messages.
1111
* Changed `Population#new_evolvables` to only accept a `count` parameter, removing the `evolvables` parameter.
1212
* Changed default `size` parameter in `Population#initialize` from 40 to 0.
13+
* Changed `Population#evolve` default `count` parameter from `Float::INFINITY` to `1`.
1314

1415
New Features:
1516
* Added gene clusters for organizing related genes through the `cluster` parameter in gene definitions.
@@ -19,6 +20,7 @@ New Features:
1920
* Improved inheritance support for evolvable classes, preserving gene configurations.
2021
* Added `evolve_selected` method to Population for evolving a specific subset of evolvables.
2122
* Added direct access to genes and gene clusters through automatically generated methods.
23+
* Added new evolution API methods: `evolve_to_goal` and `evolve_forever` for more intuitive control over the evolution process.
2224
* Comprehensive YARD documentation and README overhaul.
2325

2426
## 1.2.0 - 2022-1-22

bin/console

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require './examples/ascii_art'
1212
require './examples/ascii_gene'
1313
ascii_pop = AsciiArt.new_population(size: 8, mutation: { probability: 0.3, rate: 0.02 })
1414

15-
# stickman_pop.evolve
16-
# ascii_pop.evolve
15+
# stickman_pop.evolve_forever
16+
# ascii_pop.evolve_forever
1717

1818
binding.irb

exe/hello_evolvable_world

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class HelloWorld
3939
prepare_to_exit_loop && break if exit_loop?
4040

4141
population.reset_evolvables
42-
population.evolve
42+
population.evolve_to_goal
4343
end
4444
end
4545

@@ -101,7 +101,7 @@ end
101101
population = HelloWorld.new_population(size: 100,
102102
evaluation: { equalize: 0 },
103103
mutation: { probability: 0.6 })
104-
population.evolve
104+
population.evolve_to_goal
105105
HelloWorld.start_loop(population)
106106

107107
binding.irb

lib/evolvable/evaluation.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ module Evolvable
2525
#
2626
# ```ruby
2727
# robots = Robot.new_population(evaluation: :maximize) # Defaults to infinity
28-
# robots.evolve(goal_value: 100) # Evolve until fitness reaches 100+
28+
# robots.evolve_to_goal(100) # Evolve until fitness reaches 100+
2929
#
3030
# # Same as above
31-
# Robot.new_population(evaluation: { maximize: 100 }).evolve
31+
# Robot.new_population(evaluation: { maximize: 100 }).evolve_to_goal
3232
# ```
3333
#
3434
# - Minimize (lower is better)

lib/evolvable/population.rb

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,28 @@ module Evolvable
55
# @readme
66
# Populations orchestrate the evolutionary process through four key components:
77
#
8-
# 1. **Evaluation**: Sorts instances by fitness
8+
# 1. **Evaluation**: Sorts evolvable instances by fitness
99
# 2. **Selection**: Chooses parents for combination
10-
# 3. **Combination**: Creates new instances from parents
11-
# 4. **Mutation**: Adds genetic diversity
10+
# 3. **Combination**: Creates new evolvables from selected parents
11+
# 4. **Mutation**: Introduces variation to maintain genetic diversity
1212
#
1313
# **Features**:
1414
#
15-
# Initialize your population with parameters
15+
# Initialize a population with default or custom parameters:
1616
#
1717
# ```ruby
1818
# population = YourEvolvable.new_population(
19-
# size: 50, # Population size (default is 0)
20-
# evaluation: :minimize, # Set goal type (default is :maximize)
21-
# evaluation: { equalize: 0 }, # Supports setting an explicit goal (for fitness to equal 0)
22-
# selection: { size: 10 }, # Parent selection count (default is 2)
23-
# mutation: { probability: 0.2, # Odds of instance undergoing mutation (default: 0.03)
24-
# rate: 0.02 } # Odds of gene being mutated (1 gene is mutated by default)
19+
# size: 50,
20+
# evaluation: { equalize: 0 },
21+
# selection: { size: 10 },
22+
# mutation: { probability: 0.2, rate: 0.02 }
2523
# )
2624
# ```
2725
#
28-
# Supports setting custom objects for the following components:
26+
# Or inject fully customized strategy objects:
27+
#
2928
# ```ruby
3029
# population = YourEvolvable.new_population(
31-
# gene_space: Your::GeneSpace.new,
3230
# evaluation: Your::Evaluation.new,
3331
# evolution: Your::Evolution.new,
3432
# selection: Your::Selection.new,
@@ -37,48 +35,36 @@ module Evolvable
3735
# )
3836
# ```
3937
#
40-
# Evolve your population with a certain number of generations and/or until a goal is met
41-
#
42-
# ```ruby
43-
# population.evolve(count: 20, goal_value: 100)
44-
# ```
45-
#
46-
# Initialize new evolvables with the evolution strategy defined by the population
47-
#
48-
# ```ruby
49-
# new_evolvable = population.new_evolvable
50-
# ten_evolvables = population.new_evolvables(count: 10)
51-
# ```
52-
#
53-
# Initialize an evolvable with a custom genome
38+
# Evolve your population:
5439
#
5540
# ```ruby
56-
# outsider_genome = other_population.best_evolvable.genome
57-
# outsider_evolvable = population.new_evolvable(genome: outsider_genome)
41+
# population.evolve(count: 20) # Run for 20 generations
42+
# population.evolve_to_goal # Run until the current goal is met
43+
# population.evolve_to_goal(0.0) # Run until a specific goal is met
44+
# population.evolve_forever # Run indefinitely, ignoring any goal
45+
# population.evolve_selected([...]) # Use a custom subset of evolvables
5846
# ```
5947
#
60-
# Hook into class methods that are called during evolution
48+
# Create new evolvables:
6149
#
6250
# ```ruby
63-
# class YourEvolvable
64-
# include Evolvable
65-
#
66-
# def self.before_evaluation(population); end
67-
# def self.before_evolution(population); end
68-
# def self.after_evolution(population); end
69-
# end
51+
# new = population.new_evolvable
52+
# many = population.new_evolvables(count: 10)
53+
# with_genome = population.new_evolvable(genome: another.genome)
7054
# ```
7155
#
72-
# Return the bsest evolvable
56+
# Customize the evolution lifecycle by implementing hooks:
7357
#
7458
# ```ruby
75-
# best_evolvable = population.best_evolvable if population.met_goal?
59+
# def self.before_evaluation(pop); end
60+
# def self.before_evolution(pop); end
61+
# def self.after_evolution(pop); end
7662
# ```
7763
#
78-
# Check if the population's goal has been met
64+
# Evaluate progress:
7965
#
8066
# ```ruby
81-
# population.met_goal?
67+
# best = population.best_evolvable if population.met_goal?
8268
# ```
8369
#
8470
class Population
@@ -215,13 +201,13 @@ def evaluation=(val)
215201
:goal=
216202

217203
#
218-
# Evolves the population for a specified number of generations or until a goal is met.
204+
# Evolves the population for a specified number of generations or until the goal is achieved.
219205
#
220-
# @param count [Integer, Float] The number of evolutions to run (default: Float::INFINITY)
221-
# @param goal_value [Numeric, nil] Optional goal value to evolve toward
222-
# @return [Evolvable::Population] The evolved population
206+
# @param count [Integer, Float] Number of generations to evolve. Use `Float::INFINITY` for indefinite evolution. Defaults to `1`.
207+
# @param goal_value [Numeric, nil] Optional target value for the goal. If provided, evolution halts when this value is met.
208+
# @return [Evolvable::Population] The evolved population.
223209
#
224-
def evolve(count: Float::INFINITY, goal_value: nil)
210+
def evolve(count: 1, goal_value: nil)
225211
goal.value = goal_value if goal_value
226212
1.upto(count) do
227213
before_evaluation(self)
@@ -235,6 +221,31 @@ def evolve(count: Float::INFINITY, goal_value: nil)
235221
end
236222
end
237223

224+
#
225+
# Evolves the population until the goal is met.
226+
#
227+
# If no goal value is provided, it uses the currently defined `goal.value`.
228+
#
229+
# @param goal_value [Numeric, nil] Optional target value. Overrides the current goal if provided.
230+
# @return [Evolvable::Population] The evolved population.
231+
#
232+
def evolve_to_goal(goal_value = nil)
233+
goal_value ||= goal.value
234+
evolve(count: Float::INFINITY, goal_value: goal_value)
235+
end
236+
237+
#
238+
# Evolves the population indefinitely, ignoring any goal.
239+
#
240+
# Clears any previously set `goal.value` to ensure evolution continues indefinitely.
241+
#
242+
# @return [Evolvable::Population] The evolved population.
243+
#
244+
def evolve_forever
245+
goal.value = nil
246+
evolve(count: Float::INFINITY)
247+
end
248+
238249
#
239250
# Evolves the population using a pre-selected set of evolvables.
240251
# This allows for custom selection beyond the built-in selection strategies.

0 commit comments

Comments
 (0)