@@ -7,7 +7,6 @@ import it.unibo.alchemist.model.Time
77import it.unibo.alchemist.model.Time.Companion.INFINITY
88import it.unibo.alchemist.model.Time.Companion.ZERO
99import 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 */
2119class 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