Skip to content

Commit 3b24190

Browse files
authored
Merge pull request #1365 from WebFuzzing/feature/one-plus-lambda-lambda
OnePlusLambdaLambda GA
2 parents e151e08 + 37b221a commit 3b24190

File tree

12 files changed

+1125
-2
lines changed

12 files changed

+1125
-2
lines changed

core/src/main/kotlin/org/evomaster/core/EMConfig.kt

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1162,7 +1162,7 @@ class EMConfig {
11621162

11631163
enum class Algorithm {
11641164
DEFAULT, SMARTS, MIO, RANDOM, WTS, MOSA, RW,
1165-
StandardGA, MonotonicGA, SteadyStateGA // These 3 are still work-in-progress
1165+
StandardGA, MonotonicGA, SteadyStateGA, BreederGA, CellularGA, OnePlusLambdaLambdaGA // GA variants still work-in-progress.
11661166
}
11671167

11681168
@Cfg("The algorithm used to generate test cases. The default depends on whether black-box or white-box testing is done.")
@@ -2742,6 +2742,39 @@ class EMConfig {
27422742
@Min(0.0)
27432743
var elitesCount: Int = 1
27442744

2745+
// Cellular GA neighborhood configuration
2746+
enum class CGANeighborhoodModel {
2747+
RING, L5, C9, C13
2748+
}
2749+
2750+
@Experimental
2751+
@Cfg("Cellular GA: neighborhood model (RING, L5, C9, C13)")
2752+
var cgaNeighborhoodModel: CGANeighborhoodModel = CGANeighborhoodModel.RING
2753+
2754+
/**
2755+
* Breeder GA: truncation fraction to build parents pool P'. Range (0,1].
2756+
*/
2757+
@Experimental
2758+
@PercentageAsProbability
2759+
@Cfg("Breeder GA: fraction of top individuals to keep in parents pool (truncation).")
2760+
var breederTruncationFraction: Double = 0.5
2761+
2762+
/**
2763+
* Breeder GA: minimum number of parents to keep after truncation.
2764+
*/
2765+
@Experimental
2766+
@Min(2.0)
2767+
@Cfg("Breeder GA: minimum number of individuals in parents pool after truncation.")
2768+
var breederParentsMin: Int = 2
2769+
2770+
/**
2771+
* OnePlusLambdaLambda GA: number of offspring (λ).
2772+
*/
2773+
@Experimental
2774+
@Min(1.0)
2775+
@Cfg("1+(λ,λ) GA: number of offspring (λ) per generation")
2776+
var onePlusLambdaLambdaOffspringSize: Int = 4
2777+
27452778
@Experimental
27462779
@Cfg("In REST APIs, when request Content-Type is JSON, POJOs are used instead of raw JSON string. " +
27472780
"Only available for JVM languages")

core/src/main/kotlin/org/evomaster/core/Main.kt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,15 @@ class Main {
645645
EMConfig.Algorithm.StandardGA ->
646646
Key.get(object : TypeLiteral<StandardGeneticAlgorithm<GraphQLIndividual>>() {})
647647

648+
EMConfig.Algorithm.BreederGA ->
649+
Key.get(object : TypeLiteral<BreederGeneticAlgorithm<GraphQLIndividual>>() {})
650+
651+
EMConfig.Algorithm.CellularGA ->
652+
Key.get(object : TypeLiteral<CellularGeneticAlgorithm<GraphQLIndividual>>() {})
653+
654+
EMConfig.Algorithm.OnePlusLambdaLambdaGA ->
655+
Key.get(object : TypeLiteral<OnePlusLambdaLambdaGeneticAlgorithm<GraphQLIndividual>>() {})
656+
648657

649658
else -> throw IllegalStateException("Unrecognized algorithm ${config.algorithm}")
650659
}
@@ -670,6 +679,15 @@ class Main {
670679

671680
EMConfig.Algorithm.RW ->
672681
Key.get(object : TypeLiteral<RandomWalkAlgorithm<RPCIndividual>>() {})
682+
683+
EMConfig.Algorithm.BreederGA ->
684+
Key.get(object : TypeLiteral<BreederGeneticAlgorithm<RPCIndividual>>() {})
685+
686+
EMConfig.Algorithm.CellularGA ->
687+
Key.get(object : TypeLiteral<CellularGeneticAlgorithm<RPCIndividual>>() {})
688+
689+
EMConfig.Algorithm.OnePlusLambdaLambdaGA ->
690+
Key.get(object : TypeLiteral<OnePlusLambdaLambdaGeneticAlgorithm<RPCIndividual>>() {})
673691
else -> throw IllegalStateException("Unrecognized algorithm ${config.algorithm}")
674692
}
675693
}
@@ -694,6 +712,15 @@ class Main {
694712

695713
EMConfig.Algorithm.RW ->
696714
Key.get(object : TypeLiteral<RandomWalkAlgorithm<WebIndividual>>() {})
715+
716+
EMConfig.Algorithm.BreederGA ->
717+
Key.get(object : TypeLiteral<BreederGeneticAlgorithm<WebIndividual>>() {})
718+
719+
EMConfig.Algorithm.CellularGA ->
720+
Key.get(object : TypeLiteral<CellularGeneticAlgorithm<WebIndividual>>() {})
721+
722+
EMConfig.Algorithm.OnePlusLambdaLambdaGA ->
723+
Key.get(object : TypeLiteral<OnePlusLambdaLambdaGeneticAlgorithm<WebIndividual>>() {})
697724
else -> throw IllegalStateException("Unrecognized algorithm ${config.algorithm}")
698725
}
699726
}
@@ -728,6 +755,15 @@ class Main {
728755
EMConfig.Algorithm.RW ->
729756
Key.get(object : TypeLiteral<RandomWalkAlgorithm<RestIndividual>>() {})
730757

758+
EMConfig.Algorithm.BreederGA ->
759+
Key.get(object : TypeLiteral<BreederGeneticAlgorithm<RestIndividual>>() {})
760+
761+
EMConfig.Algorithm.CellularGA ->
762+
Key.get(object : TypeLiteral<CellularGeneticAlgorithm<RestIndividual>>() {})
763+
764+
EMConfig.Algorithm.OnePlusLambdaLambdaGA ->
765+
Key.get(object : TypeLiteral<OnePlusLambdaLambdaGeneticAlgorithm<RestIndividual>>() {})
766+
731767
else -> throw IllegalStateException("Unrecognized algorithm ${config.algorithm}")
732768
}
733769
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.evomaster.core.search.algorithms
2+
3+
import org.evomaster.core.EMConfig
4+
import org.evomaster.core.search.Individual
5+
import org.evomaster.core.search.algorithms.wts.WtsEvalIndividual
6+
import kotlin.math.max
7+
8+
/**
9+
* Breeder Genetic Algorithm (BGA)
10+
*
11+
* Differences vs Standard GA:
12+
* - Uses truncation selection to build a parents pool P'.
13+
* - At each step, creates two offspring from two random parents in P',
14+
* then randomly selects ONE of the 2 offspring to add to the next population.
15+
*/
16+
class BreederGeneticAlgorithm<T> : AbstractGeneticAlgorithm<T>() where T : Individual {
17+
18+
override fun getType(): EMConfig.Algorithm {
19+
return EMConfig.Algorithm.BreederGA
20+
}
21+
22+
override fun searchOnce() {
23+
beginGeneration()
24+
frozenTargets = archive.notCoveredTargets()
25+
val n = config.populationSize
26+
27+
// Elitism base for next generation
28+
val nextPop = formTheNextPopulation(population)
29+
30+
// Build parents pool P' by truncation on current population
31+
val parentsPool = buildParentsPoolByTruncation(population)
32+
33+
while (nextPop.size < n) {
34+
beginStep()
35+
val p1 = randomness.choose(parentsPool)
36+
val p2 = randomness.choose(parentsPool)
37+
38+
// Work on copies
39+
val o1 = p1.copy()
40+
val o2 = p2.copy()
41+
42+
if (randomness.nextBoolean(config.xoverProbability)) {
43+
xover(o1, o2)
44+
}
45+
if (randomness.nextBoolean(config.fixedRateMutation)) {
46+
mutate(o1)
47+
}
48+
if (randomness.nextBoolean(config.fixedRateMutation)) {
49+
mutate(o2)
50+
}
51+
52+
// Randomly pick one child to carry over
53+
var chosen = o1
54+
if (!randomness.nextBoolean()) {
55+
chosen = o2
56+
}
57+
nextPop.add(chosen)
58+
59+
if (!time.shouldContinueSearch()) {
60+
endStep()
61+
break
62+
}
63+
endStep()
64+
}
65+
66+
population.clear()
67+
population.addAll(nextPop)
68+
endGeneration()
69+
}
70+
71+
private fun buildParentsPoolByTruncation(pop: List<WtsEvalIndividual<T>>): List<WtsEvalIndividual<T>> {
72+
if (pop.isEmpty()) {
73+
return pop
74+
}
75+
76+
val sorted = pop.sortedByDescending { score(it) }
77+
val k = max(config.breederParentsMin, (sorted.size * config.breederTruncationFraction).toInt())
78+
return sorted.take(k)
79+
}
80+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package org.evomaster.core.search.algorithms
2+
3+
import org.evomaster.core.EMConfig
4+
import org.evomaster.core.search.Individual
5+
import org.evomaster.core.search.algorithms.wts.WtsEvalIndividual
6+
7+
/**
8+
* Cellular Genetic Algorithm (cGA).
9+
*
10+
* The population is organized on a virtual grid defined by a neighborhood model
11+
* (e.g., ring, 5-linear, compact 9, compact 13). For each cell i in the current
12+
* generation, the algorithm performs a localized evolutionary step restricted to
13+
* i's neighborhood:
14+
*
15+
* 1) Build i's neighborhood according to the configured model (with wrap-around).
16+
* 2) Select two parents via tournament restricted to that neighborhood.
17+
* 3) Create two offspring and apply crossover with probability
18+
* `xoverProbability`.
19+
* 4) Retain the best offspring by fitness and apply mutation with probability
20+
* `fixedRateMutation`.
21+
* 5) Replace the individual at position i with the best between the mutated offspring
22+
* and the current individual at i (local elitism).
23+
*
24+
* One pass over all cells produces the next population. The process repeats until the
25+
* global stopping criterion is met.
26+
*/
27+
class CellularGeneticAlgorithm<T> : AbstractGeneticAlgorithm<T>() where T : Individual {
28+
29+
override fun getType(): EMConfig.Algorithm {
30+
return EMConfig.Algorithm.CellularGA
31+
}
32+
33+
override fun searchOnce() {
34+
beginGeneration()
35+
// Freeze targets for current generation
36+
frozenTargets = archive.notCoveredTargets()
37+
38+
val n = population.size
39+
val next: MutableList<WtsEvalIndividual<T>> = mutableListOf()
40+
41+
for (i in 0 until n) {
42+
beginStep()
43+
val p = population[i]
44+
45+
val neighbors = getNeighborhood(i)
46+
47+
// Cellular tournament selection within neighborhood
48+
val x = neighborhoodTournament(neighbors)
49+
val y = neighborhoodTournament(neighbors)
50+
51+
val o1 = x.copy()
52+
val o2 = y.copy()
53+
if (randomness.nextBoolean(config.xoverProbability)) {
54+
xover(o1, o2)
55+
}
56+
57+
val o: WtsEvalIndividual<T> = if (score(o1) >= score(o2)) o1 else o2
58+
59+
if (randomness.nextBoolean(config.fixedRateMutation)) {
60+
mutate(o)
61+
}
62+
63+
val bestLocal: WtsEvalIndividual<T> = if (score(o) >= score(p)) o else p
64+
next.add(bestLocal)
65+
endStep()
66+
}
67+
68+
population.clear()
69+
population.addAll(next)
70+
endGeneration()
71+
}
72+
73+
/**
74+
* Runs tournament selection restricted to a neighborhood subset.
75+
*/
76+
private fun neighborhoodTournament(neighbors: List<WtsEvalIndividual<T>>): WtsEvalIndividual<T> {
77+
val sel = selectionStrategy.select(neighbors, config.tournamentSize, randomness, ::score)
78+
observers.forEach { it.onSelection(sel) }
79+
return sel
80+
}
81+
82+
/**
83+
* Returns the neighborhood list for a given index based on the configured model.
84+
* The returned list always includes the current cell ("center") and neighbors,
85+
* in the order customary for the chosen model.
86+
*/
87+
private fun getNeighborhood(index: Int): List<WtsEvalIndividual<T>> {
88+
val model = config.cgaNeighborhoodModel
89+
val neighborhood = Neighborhood<T>(population.size)
90+
if (model == EMConfig.CGANeighborhoodModel.RING) {
91+
return neighborhood.ringTopology(population, index)
92+
}
93+
if (model == EMConfig.CGANeighborhoodModel.L5) {
94+
return neighborhood.linearFive(population, index)
95+
}
96+
if (model == EMConfig.CGANeighborhoodModel.C9) {
97+
return neighborhood.compactNine(population, index)
98+
}
99+
return neighborhood.compactThirteen(population, index)
100+
}
101+
}
102+
103+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.evomaster.core.search.algorithms
2+
3+
/**
4+
* An interface that defines the four neighbourhood models used with the cGA
5+
*/
6+
interface NeighborModels<T> {
7+
8+
fun ringTopology(collection: List<T>, position: Int): List<T>
9+
10+
fun linearFive(collection: List<T>, position: Int): List<T>
11+
12+
fun compactNine(collection: List<T>, position: Int): List<T>
13+
14+
fun compactThirteen(collection: List<T>, position: Int): List<T>
15+
}
16+
17+
18+
19+

0 commit comments

Comments
 (0)