Class: DSPy::Teleprompt::GEPA::GeneticEngine
- Inherits:
-
Object
- Object
- DSPy::Teleprompt::GEPA::GeneticEngine
- Extended by:
- T::Sig
- Defined in:
- lib/dspy/teleprompt/gepa.rb
Overview
GeneticEngine orchestrates the genetic algorithm for prompt evolution Manages population, selection, and evolution across generations
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#generation ⇒ Object
readonly
Returns the value of attribute generation.
-
#metric ⇒ Object
readonly
Returns the value of attribute metric.
-
#population ⇒ Object
readonly
Returns the value of attribute population.
Instance Method Summary collapse
- #evaluate_population(trainset) ⇒ Object
- #evolve_generation(trainset) ⇒ Object
- #get_best_candidate ⇒ Object
-
#initialize(config:, metric:) ⇒ GeneticEngine
constructor
A new instance of GeneticEngine.
- #initialize_population(program) ⇒ Object
- #population_diversity ⇒ Object
- #run_evolution(program, trainset) ⇒ Object
Constructor Details
#initialize(config:, metric:) ⇒ GeneticEngine
Returns a new instance of GeneticEngine.
1338 1339 1340 1341 1342 1343 1344 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1338 def initialize(config:, metric:) @config = config @metric = metric @population = T.let([], T::Array[T.untyped]) @generation = 0 @fitness_scores = T.let([], T::Array[Float]) end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
1326 1327 1328 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1326 def config @config end |
#generation ⇒ Object (readonly)
Returns the value of attribute generation.
1335 1336 1337 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1335 def generation @generation end |
#metric ⇒ Object (readonly)
Returns the value of attribute metric.
1329 1330 1331 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1329 def metric @metric end |
#population ⇒ Object (readonly)
Returns the value of attribute population.
1332 1333 1334 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1332 def population @population end |
Instance Method Details
#evaluate_population(trainset) ⇒ Object
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1377 def evaluate_population(trainset) @fitness_scores = @population.map do |candidate| scores = trainset.map do |example| prediction = candidate.call(**example.input_values) @metric.call(example, prediction).to_f rescue => e # Handle evaluation errors gracefully 0.0 end scores.sum / scores.size end @fitness_scores end |
#evolve_generation(trainset) ⇒ Object
1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1395 def evolve_generation(trainset) current_scores = evaluate_population(trainset) # Simple selection: keep top 50% and mutate them sorted_indices = (0...@population.size).sort_by { |i| -current_scores[i] } survivors = sorted_indices.take(@config.population_size / 2) new_population = [] # Keep best performers survivors.each { |i| new_population << @population[i] } # Fill rest with mutations of survivors while new_population.size < @config.population_size parent_index = survivors.sample parent = @population[parent_index] # Generate mutation variants = generate_instruction_variants(parent.signature_class.description) mutated = create_program_with_instruction(parent, variants.first || parent.signature_class.description) new_population << mutated end @population = new_population @generation += 1 end |
#get_best_candidate ⇒ Object
1461 1462 1463 1464 1465 1466 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1461 def get_best_candidate return @population.first if @fitness_scores.empty? best_index = @fitness_scores.each_with_index.max_by { |score, _| score }[1] @population[best_index] end |
#initialize_population(program) ⇒ Object
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1348 def initialize_population(program) @population = [] # Start with original program @population << program # Generate instruction variants to fill population original_instruction = program.signature_class.description variants = generate_instruction_variants(original_instruction) # Create program copies with different instructions variants.take(@config.population_size - 1).each do |variant| variant_program = create_program_with_instruction(program, variant) @population << variant_program end # If we need more candidates, duplicate and mutate while @population.size < @config.population_size base_program = @population.sample mutated = create_program_with_instruction(base_program, generate_instruction_variants(base_program.signature_class.description).first) @population << mutated end @generation = 0 end |
#population_diversity ⇒ Object
1470 1471 1472 1473 1474 1475 1476 1477 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1470 def population_diversity return 0.0 if @population.empty? instructions = @population.map(&:signature_class).map(&:description) unique_instructions = instructions.uniq.size unique_instructions.to_f / @population.size.to_f end |
#run_evolution(program, trainset) ⇒ Object
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 |
# File 'lib/dspy/teleprompt/gepa.rb', line 1424 def run_evolution(program, trainset) initialize_population(program) history = [] # Initial evaluation initial_scores = evaluate_population(trainset) history << { generation: 0, best_fitness: initial_scores.max, avg_fitness: initial_scores.sum / initial_scores.size, diversity: population_diversity } # Evolution loop @config.num_generations.times do evolve_generation(trainset) scores = evaluate_population(trainset) history << { generation: @generation, best_fitness: scores.max, avg_fitness: scores.sum / scores.size, diversity: population_diversity } end { best_candidate: get_best_candidate, best_fitness: @fitness_scores.max, generation_history: history, final_population: @population.dup } end |