Skip to content

Commit 3a17466

Browse files
committed
improve lips algorithm
1 parent ab6d739 commit 3a17466

2 files changed

Lines changed: 53 additions & 45 deletions

File tree

core/src/main/kotlin/org/evomaster/core/search/algorithms/LIPSAlgorithm.kt

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@ import org.evomaster.core.EMConfig
44
import org.evomaster.core.search.FitnessValue
55
import org.evomaster.core.search.Individual
66
import org.evomaster.core.search.algorithms.wts.WtsEvalIndividual
7+
import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming
8+
import org.evomaster.core.search.service.IdMapper
79

810
/**
9-
* LIPS (Linearly Independent Path-based Search).
10-
* Single-objective GA that optimizes one target (branch) at a time.
11+
* Linearly Independent Path-based Search (LIPS).
1112
*
12-
* - Picks a current target from the uncovered set.
13-
* - Runs one GA generation to evolve the population towards the current target.
14-
* - when current target is covered or its budget is spent, select a new current target.
13+
* A single-objective GA that optimizes one branch target at a time.
14+
*
15+
* - Initializes a random individual i and build the initial population P = random ∪ {i}.
16+
* - Maintains a current branch target.
17+
* - Per-target budget is a fair share of the global TIME/ACTIONS budget; switches target when the target is covered or its budget is exhausted.
1518
*/
19+
20+
1621
class LIPSAlgorithm<T> : AbstractGeneticAlgorithm<T>() where T : Individual {
1722

1823
private var currentTarget: Int? = null
@@ -21,51 +26,33 @@ class LIPSAlgorithm<T> : AbstractGeneticAlgorithm<T>() where T : Individual {
2126
override fun getType(): EMConfig.Algorithm = EMConfig.Algorithm.LIPS
2227

2328
override fun initPopulation() {
24-
println("[LIPS DEBUG] initPopulation: enter")
2529
population.clear()
26-
println("[LIPS DEBUG] initPopulation: population cleared")
27-
2830
// 1) Generate Random Individual
29-
println("[LIPS DEBUG] initPopulation: sampling initial individual")
3031
val i = sampleSuite()
31-
println("[LIPS DEBUG] initPopulation: sampled initial individual")
32-
3332
// 2) P <- RandomPopulation(ps-1) ∪ {i}
3433
population.add(i)
35-
println("[LIPS DEBUG] initPopulation: added initial individual, pop=${population.size}")
3634
while (population.size < config.populationSize) {
3735
population.add(sampleSuite())
38-
if (population.size % 5 == 0 || population.size == config.populationSize) {
39-
println("[LIPS DEBUG] initPopulation: growing pop=${population.size}/${config.populationSize}")
40-
}
4136
}
42-
println("[LIPS DEBUG] initPopulation: exit with pop=${population.size}")
4337
}
4438

4539
override fun searchOnce() {
4640
beginGeneration()
47-
println("[LIPS DEBUG] searchOnce: popSize=${population.size}")
41+
// record budget usage for this generation
42+
val startActions = time.evaluatedActions
43+
val startSeconds = time.getElapsedSeconds()
4844

4945
// Compute uncovered goals
5046
val uncovered = archive.notCoveredTargets()
51-
println("[LIPS DEBUG] searchOnce: uncoveredSize=${uncovered.size}")
52-
if (uncovered.isEmpty()) {
53-
println("[LIPS DEBUG] searchOnce: uncovered is EMPTY; ending generation")
54-
endGeneration()
55-
return
56-
} else {
57-
println("[LIPS DEBUG] searchOnce: uncovered is NOT EMPTY")
58-
}
5947

6048
// current target is null if covered by previous generation or out of budget
61-
62-
// Pick target if null, or if previously covered
63-
if (currentTarget == null || !uncovered.contains(currentTarget)) {
64-
val target = uncovered.last()
49+
// Pick target if null, or if previously covered (check coverage directly)
50+
val needNewTarget = currentTarget == null || archive.isCovered(currentTarget!!)
51+
if (needNewTarget) {
52+
val target = firstUncoveredBranch()
6553
currentTarget = target
6654
// Initialize budget for this NEW target
6755
budgetLeftForCurrentTarget = calculatePerTargetBudget(uncovered.size)
68-
println("[LIPS DEBUG] selectTarget: target=$target uncovered=${uncovered.size} budgetLeftForCurrentTarget=$budgetLeftForCurrentTarget")
6956
}
7057

7158
// Focus scoring on the single selected target
@@ -74,10 +61,6 @@ class LIPSAlgorithm<T> : AbstractGeneticAlgorithm<T>() where T : Individual {
7461
val n = config.populationSize
7562
val nextPop: MutableList<WtsEvalIndividual<T>> = formTheNextPopulation(population)
7663

77-
// record budget usage for this generation
78-
val startActions = time.evaluatedActions
79-
val startSeconds = time.getElapsedSeconds()
80-
8164
while (nextPop.size < n) {
8265
beginStep()
8366

@@ -114,17 +97,10 @@ class LIPSAlgorithm<T> : AbstractGeneticAlgorithm<T>() where T : Individual {
11497
population.addAll(nextPop)
11598

11699
// Update budget usage for this target
117-
val usedForTarget = usedForCurrentTarget(startActions, startSeconds)
118-
budgetLeftForCurrentTarget -= usedForTarget
119-
println("[LIPS DEBUG] afterGen: target=${currentTarget} used=$usedForTarget budgetLeftForCurrentTarget=$budgetLeftForCurrentTarget")
100+
updatePerTargetBudget(startActions, startSeconds)
120101

121-
// Check if target is covered or out of budget
122-
val coveredNow = population.any { score(it) >= 1.0 }
123-
val outOfBudget = budgetLeftForCurrentTarget <= 0
124-
125-
if (coveredNow || outOfBudget) {
126-
currentTarget = null
127-
}
102+
// Switch target if covered or out of budget
103+
if (shouldSwitchTarget()) currentTarget = null
128104

129105
endGeneration()
130106
}
@@ -158,6 +134,34 @@ class LIPSAlgorithm<T> : AbstractGeneticAlgorithm<T>() where T : Individual {
158134
else -> 0
159135
}
160136
}
137+
138+
private fun updatePerTargetBudget(actionsAtGenStart: Int, secondsAtGenStart: Int) {
139+
val usedForTarget = usedForCurrentTarget(actionsAtGenStart, secondsAtGenStart)
140+
budgetLeftForCurrentTarget -= usedForTarget
141+
}
142+
143+
private fun shouldSwitchTarget(): Boolean {
144+
val coveredNow = population.any { score(it) >= 1.0 }
145+
val outOfBudget = budgetLeftForCurrentTarget <= 0
146+
return coveredNow || outOfBudget
147+
}
148+
149+
fun firstUncoveredBranch(): Int? {
150+
if (populations.isEmpty()) return null
151+
152+
// Iterate targets by numeric id in descending order
153+
val orderedIds = populations.keys.sortedDescending()
154+
155+
for (targetId in orderedIds) {
156+
val description = archive.getIdMapper().getDescriptiveId(targetId)
157+
if (description.startsWith(ObjectiveNaming.BRANCH)) {
158+
if (!isCovered(targetId)) {
159+
return targetId
160+
}
161+
}
162+
}
163+
return null
164+
}
161165
}
162166

163167

core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,11 @@ class Archive<T> where T : Individual {
616616
return samplingCounter
617617
}
618618

619+
/**
620+
* Expose the IdMapper used by the archive (read-only access pattern).
621+
*/
622+
fun getIdMapper(): IdMapper = idMapper
623+
619624
/**
620625
* @return a list of pairs which is composed of target id (first) and corresponding tests (second)
621626
*/
@@ -700,5 +705,4 @@ class Archive<T> where T : Individual {
700705
it.individual.seeTopGenes().all { g-> g.isLocallyValid() }
701706
}
702707
}
703-
704708
}

0 commit comments

Comments
 (0)