Skip to content

Commit 9a57f5c

Browse files
angelacorteDanySK
authored andcommitted
refactor: try to not have duplicate code
1 parent 123d426 commit 9a57f5c

File tree

1 file changed

+97
-60
lines changed
  • alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/terminators

1 file changed

+97
-60
lines changed

alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/terminators/StableMetrics.kt

Lines changed: 97 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import it.unibo.alchemist.model.Time
77
import it.unibo.alchemist.model.Time.Companion.INFINITY
88
import it.unibo.alchemist.model.Time.Companion.ZERO
99
import kotlin.Long.Companion.MAX_VALUE
10-
import kotlin.require
1110

1211
/**
1312
* [stableForTotalSteps] for how many steps the [metricsToCheck] should be stable,
@@ -16,24 +15,39 @@ import kotlin.require
1615
* [stableForTotalTime] for how much time the [metricsToCheck] should be stable,
1716
* zero if taking into account just the steps.
1817
* [checkTimeInterval] every time-step it should check, if zero it checks at every invocation.
19-
* Both [stableForTotalSteps] and [stableForTotalTime] can be used together to check for stability.
2018
*/
2119
class StableMetrics<T>(
22-
private val stableForTotalSteps: Long,
23-
private val checkStepInterval: Long,
24-
private val stableForTotalTime: Time,
25-
private val checkTimeInterval: Time,
20+
private val stableForTotalSteps: Long, // total steps to be stable
21+
private val checkStepInterval: Long, // steps interval to check
22+
private val stableForTotalTime: Time, // total time to be stable
23+
private val checkTimeInterval: Time, // time interval to check
2624
private val metricsToCheck: (Environment<T, Position<*>>) -> Map<String, T>,
2725
) : TerminationPredicate<T, Position<*>> {
28-
// steps-related checks
29-
private var lastStepsChecked: Long = 0
30-
private var stableSteps: Long = 0
31-
private var lastUpdatedMetricsSteps: Map<String, T> = emptyMap()
26+
constructor(
27+
stableForTotalSteps: Long,
28+
checkStepInterval: Long,
29+
metricsToCheck: (Environment<T, Position<*>>) -> Map<String, T>,
30+
) : this(
31+
stableForTotalSteps = stableForTotalSteps,
32+
checkStepInterval = checkStepInterval,
33+
stableForTotalTime = ZERO,
34+
checkTimeInterval = ZERO,
35+
metricsToCheck = metricsToCheck,
36+
)
37+
constructor(
38+
stableForTotalTime: Time,
39+
checkTimeInterval: Time,
40+
metricsToCheck: (Environment<T, Position<*>>) -> Map<String, T>,
41+
) : this(
42+
stableForTotalSteps = 0,
43+
checkStepInterval = 0,
44+
stableForTotalTime = stableForTotalTime,
45+
checkTimeInterval = checkTimeInterval,
46+
metricsToCheck = metricsToCheck,
47+
)
3248

33-
// time-related checks
34-
private var lastTimeChecked: Time = ZERO
35-
private var stableTime: Time = ZERO
36-
private var lastUpdatedMetricsTime: Map<String, T> = emptyMap()
49+
private val stepTracker by lazy { StepMetricTracker() }
50+
private val timeTracker by lazy { TimeMetricTracker() }
3751
init {
3852
require((stableForTotalTime > ZERO) || (stableForTotalSteps > 0)) {
3953
"At least one of the stability conditions (stableForTime or stableForSteps) must be greater than zero."
@@ -47,36 +61,41 @@ class StableMetrics<T>(
4761
}
4862

4963
override fun invoke(environment: Environment<T, Position<*>>): Boolean = when {
50-
stableForTotalTime > ZERO && stableForTotalSteps > 0 -> {
51-
val timeStable = checkTime(environment)
52-
val stepStable = checkSteps(environment)
53-
timeStable && stepStable
64+
stableForTotalTime > DEFAULT_TIME && stableForTotalSteps > DEFAULT_STEP -> {
65+
val stableSteps = checkStability(environment, stepTracker, STEP_MAX_VALUE, DEFAULT_STEP)
66+
val stableTime = checkStability(environment, timeTracker, TIME_MAX_VALUE, DEFAULT_TIME)
67+
stableTime && stableSteps
5468
}
55-
stableForTotalTime > ZERO -> checkTime(environment)
56-
stableForTotalSteps > 0 -> checkSteps(environment)
57-
else -> error("This should never happen, at least one of the stability conditions must be greater than zero.")
69+
stableForTotalTime > DEFAULT_TIME ->
70+
checkStability(environment, timeTracker, TIME_MAX_VALUE, DEFAULT_TIME)
71+
stableForTotalSteps > DEFAULT_STEP ->
72+
checkStability(environment, stepTracker, STEP_MAX_VALUE, DEFAULT_STEP)
73+
else -> error("This should never happen.")
5874
}
5975

60-
private fun checkSteps(environment: Environment<T, Position<*>>): Boolean {
61-
require(stableForTotalSteps > 0) { "Steps check and equal interval must be positive." }
62-
val currentStep = environment.simulation.step
63-
val checkedStepsInterval = currentStep - lastStepsChecked
76+
private fun <Type : Comparable<Type>> checkStability(
77+
env: Environment<T, Position<*>>,
78+
tracker: MetricTracker<T, Type>,
79+
maxValue: Type,
80+
defaultValue: Type,
81+
): Boolean = with(tracker) {
82+
val current = current(env)
83+
val interval = evaluateInterval(current)
6484
return when {
65-
stableSteps == MAX_VALUE -> true
66-
checkedStepsInterval >= checkStepInterval -> {
67-
// update the last checked step and get the current metrics
68-
lastStepsChecked = currentStep
69-
val currentMetrics = metricsToCheck(environment).also {
85+
stableValue >= maxValue -> true
86+
shouldBeChecked(interval) -> {
87+
lastChecked = current
88+
val currentMetrics = metricsToCheck(env).also {
7089
require(it.isNotEmpty()) { "There should be at least one metric to check." }
7190
}
7291
when {
73-
lastUpdatedMetricsSteps == currentMetrics -> { // metrics are the same as before = stable
74-
stableSteps += checkedStepsInterval
75-
return (stableSteps >= stableForTotalSteps).also { if (it) stableSteps = MAX_VALUE }
92+
currentMetrics == lastMetrics -> {
93+
increaseStability(interval)
94+
return (stableValue >= totalStability).also { if (it) stableValue = maxValue }
7695
}
77-
else -> { // reset the counters
78-
stableSteps = 0
79-
lastUpdatedMetricsSteps = currentMetrics
96+
else -> {
97+
stableValue = defaultValue
98+
lastMetrics = currentMetrics
8099
false
81100
}
82101
}
@@ -85,30 +104,48 @@ class StableMetrics<T>(
85104
}
86105
}
87106

88-
private fun checkTime(environment: Environment<T, Position<*>>): Boolean {
89-
require(stableForTotalTime > ZERO) { "The amount of time to check the stability should be more than zero." }
90-
val currentTime = environment.simulation.time
91-
val checkedTimeInterval = currentTime - lastTimeChecked
92-
return when {
93-
stableTime == INFINITY -> true
94-
checkedTimeInterval >= checkTimeInterval -> {
95-
lastTimeChecked = currentTime
96-
val currentMetrics = metricsToCheck(environment).also {
97-
require(it.isNotEmpty()) { "There should be at least one metric to check." }
98-
}
99-
when {
100-
lastUpdatedMetricsTime == currentMetrics -> { // metrics are the same as before = stable
101-
stableTime += checkedTimeInterval
102-
return (stableTime >= stableForTotalTime).also { if (it) stableTime = INFINITY }
103-
}
104-
else -> { // reset the counters
105-
stableTime = ZERO
106-
lastUpdatedMetricsTime = currentMetrics
107-
false
108-
}
109-
}
110-
}
111-
else -> false
107+
private interface MetricTracker<T, Type : Comparable<Type>> {
108+
var lastChecked: Type
109+
var stableValue: Type
110+
var lastMetrics: Map<String, T>
111+
val totalStability: Type
112+
val checkInterval: Type
113+
fun current(env: Environment<T, Position<*>>): Type
114+
fun evaluateInterval(current: Type): Type
115+
fun shouldBeChecked(interval: Type): Boolean = interval >= checkInterval
116+
fun increaseStability(interval: Type)
117+
}
118+
119+
private inner class StepMetricTracker : MetricTracker<T, Long> {
120+
override var lastChecked = DEFAULT_STEP
121+
override var stableValue = DEFAULT_STEP
122+
override var lastMetrics: Map<String, T> = emptyMap()
123+
override val totalStability = stableForTotalSteps
124+
override val checkInterval = checkStepInterval
125+
override fun current(env: Environment<T, Position<*>>): Long = env.simulation.step
126+
override fun evaluateInterval(current: Long): Long = current - lastChecked
127+
override fun increaseStability(interval: Long) {
128+
stableValue += interval
129+
}
130+
}
131+
132+
private inner class TimeMetricTracker : MetricTracker<T, Time> {
133+
override var lastChecked = DEFAULT_TIME
134+
override var stableValue = DEFAULT_TIME
135+
override var lastMetrics: Map<String, T> = emptyMap()
136+
override val totalStability = stableForTotalTime
137+
override val checkInterval = checkTimeInterval
138+
override fun current(env: Environment<T, Position<*>>): Time = env.simulation.time
139+
override fun evaluateInterval(current: Time): Time = current - lastChecked
140+
override fun increaseStability(interval: Time) {
141+
stableValue += interval
112142
}
113143
}
144+
145+
companion object {
146+
const val STEP_MAX_VALUE: Long = MAX_VALUE
147+
const val DEFAULT_STEP: Long = 0L
148+
val DEFAULT_TIME: Time = ZERO
149+
val TIME_MAX_VALUE: Time = INFINITY
150+
}
114151
}

0 commit comments

Comments
 (0)