From 5470d9d0ddc0f4473409fc471f3fae98c817cffb Mon Sep 17 00:00:00 2001
From: Marcel Becker <stu117960@mail.uni-kiel.de>
Date: Wed, 15 Dec 2021 16:34:53 +0100
Subject: [PATCH] Using result object for InitialGuessSearchStrategy
 implementation

---
 .../strategies/searchstrategy/BinarySearch.kt |  7 +---
 .../searchstrategy/CompositeStrategy.kt       |  3 +-
 .../strategies/searchstrategy/FullSearch.kt   |  6 +--
 .../searchstrategy/GuessStrategy.kt           |  1 +
 .../InitialGuessSearchStrategy.kt             | 39 ++++++++++++++-----
 .../strategies/searchstrategy/LinearSearch.kt |  6 +--
 .../searchstrategy/SearchStrategy.kt          |  6 ++-
 .../main/kotlin/theodolite/util/Results.kt    | 17 ++++++--
 .../InitialGuessSearchStrategyTest.kt         | 21 +++-------
 9 files changed, 58 insertions(+), 48 deletions(-)

diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt
index d2298fe6f..ba7ac4553 100644
--- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt
+++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt
@@ -13,12 +13,7 @@ private val logger = KotlinLogging.logger {}
  * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
  */
 class BinarySearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) {
-    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>,
-                                      lastLowestResource: Resource?): Resource? {
-
-        if (lastLowestResource != null) {
-            logger.info { "Running LinearSearch with a set lastLowestResource value doesn't make sense." }
-        }
+    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
 
         val result = binarySearch(load, resources, 0, resources.size - 1)
         if (result == -1) {
diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt
index 777a87c15..41cc5c325 100644
--- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt
+++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt
@@ -20,8 +20,7 @@ class CompositeStrategy(
     val restrictionStrategies: Set<RestrictionStrategy>
 ) : SearchStrategy(benchmarkExecutor) {
 
-    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>,
-                                      lastLowestResource: Resource?): Resource? {
+    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
         var restrictedResources = resources.toList()
         for (strategy in this.restrictionStrategies) {
             restrictedResources = restrictedResources.intersect(strategy.apply(load, resources)).toList()
diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt
index 3a58bd29a..acdf6eab9 100644
--- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt
+++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt
@@ -17,11 +17,7 @@ private val logger = KotlinLogging.logger {}
  */
 class FullSearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) {
 
-    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>,
-                                      lastLowestResource: Resource?): Resource? {
-        if (lastLowestResource != null) {
-            logger.info { "Running LinearSearch with a set lastLowestResource value doesn't make sense." }
-        }
+    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
 
         var minimalSuitableResources: Resource? = null
         for (res in resources) {
diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/GuessStrategy.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/GuessStrategy.kt
index 786a3baf1..e2a961167 100644
--- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/GuessStrategy.kt
+++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/GuessStrategy.kt
@@ -2,6 +2,7 @@ package theodolite.strategies.searchstrategy
 
 import io.quarkus.runtime.annotations.RegisterForReflection
 import theodolite.util.Resource
+import theodolite.util.Results
 
 /**
  * Base class for the implementation of Guess strategies. Guess strategies are strategies to determine the resource
diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt
index 432f08fde..43ccdf735 100644
--- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt
+++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt
@@ -4,6 +4,7 @@ import mu.KotlinLogging
 import theodolite.execution.BenchmarkExecutor
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
+import theodolite.util.Results
 
 private val logger = KotlinLogging.logger {}
 
@@ -14,31 +15,51 @@ private val logger = KotlinLogging.logger {}
  * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
  * @param guessStrategy Strategy that provides us with a guess for the first resource amount.
  */
-class InitialGuessSearchStrategy(benchmarkExecutor: BenchmarkExecutor, guessStrategy: GuessStrategy) : SearchStrategy(benchmarkExecutor, guessStrategy) {
+class InitialGuessSearchStrategy(benchmarkExecutor: BenchmarkExecutor, guessStrategy: GuessStrategy, results: Results) :
+        SearchStrategy(benchmarkExecutor, guessStrategy, results) {
 
-    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>, lastLowestResource: Resource?): Resource? {
+    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
+
+        if(resources.isEmpty()) {
+            logger.info { "You need to specify resources to be checked for the InitialGuessSearchStrategy to work." }
+            return null
+        }
 
         if(guessStrategy == null){
             logger.info { "Your InitialGuessSearchStrategy doesn't have a GuessStrategy. This is not supported." }
             return null
         }
 
-        var lastLowestResourceToUse = this.guessStrategy.firstGuess(resources, lastLowestResource)
+        if(results == null){
+            logger.info { "The results need to be initialized." }
+            return null
+        }
+
+
+        var lastLowestResource : Resource? = null
+
+        // Getting the lastLowestResource from results and calling firstGuess() with it
+        if (!results.isEmpty()) {
+            val maxLoad: LoadDimension? = this.results.getMaxBenchmarkedLoad(load)
+            lastLowestResource = this.results.getMinRequiredInstances(maxLoad)
+            if (lastLowestResource.get() == Int.MAX_VALUE) lastLowestResource = null
+        }
+        lastLowestResource = this.guessStrategy.firstGuess(resources, lastLowestResource)
 
-        if (lastLowestResourceToUse != null) {
+        if (lastLowestResource != null) {
             val resourcesToCheck: List<Resource>
-            val startIndex: Int = resources.indexOf(lastLowestResourceToUse)
+            val startIndex: Int = resources.indexOf(lastLowestResource)
 
-            logger.info { "Running experiment with load '${load.get()}' and resources '${lastLowestResourceToUse.get()}'" }
+            logger.info { "Running experiment with load '${load.get()}' and resources '${lastLowestResource.get()}'" }
 
             // If the first experiment passes, starting downward linear search
             // otherwise starting upward linear search
-            if (this.benchmarkExecutor.runExperiment(load, lastLowestResourceToUse)) {
+            if (this.benchmarkExecutor.runExperiment(load, lastLowestResource)) {
 
                 resourcesToCheck = resources.subList(0, startIndex).reversed()
-                if (resourcesToCheck.isEmpty()) return lastLowestResourceToUse
+                if (resourcesToCheck.isEmpty()) return lastLowestResource
 
-                var currentMin: Resource = lastLowestResourceToUse
+                var currentMin: Resource = lastLowestResource
                 for (res in resourcesToCheck) {
 
                     logger.info { "Running experiment with load '${load.get()}' and resources '${res.get()}'" }
diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt
index 09f431e9f..f860f2496 100644
--- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt
+++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt
@@ -14,11 +14,7 @@ private val logger = KotlinLogging.logger {}
  */
 class LinearSearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) {
 
-    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>,
-                                      lastLowestResource: Resource?): Resource? {
-        if (lastLowestResource != null) {
-            logger.info { "Running LinearSearch with a set lastLowestResource value doesn't make sense." }
-        }
+    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
 
         for (res in resources) {
 
diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt
index e37cc32b5..357b1d22a 100644
--- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt
+++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt
@@ -4,6 +4,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection
 import theodolite.execution.BenchmarkExecutor
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
+import theodolite.util.Results
 
 /**
  *  Base class for the implementation for SearchStrategies. SearchStrategies determine the smallest suitable number of instances.
@@ -12,7 +13,8 @@ import theodolite.util.Resource
  * @param guessStrategy Guess strategy for the initial resource amount in case the InitialGuessStrategy is selected.
  */
 @RegisterForReflection
-abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor, val guessStrategy: GuessStrategy? = null) {
+abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor, val guessStrategy: GuessStrategy? = null,
+                              val results: Results? = null) {
     /**
      * Find smallest suitable resource from the specified resource list for the given load.
      *
@@ -22,5 +24,5 @@ abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor, val gues
      *
      * @return suitable resource for the specified load, or null if no suitable resource exists.
      */
-    abstract fun findSuitableResource(load: LoadDimension, resources: List<Resource>, lastLowestResource: Resource? = null): Resource?
+    abstract fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource?
 }
diff --git a/theodolite/src/main/kotlin/theodolite/util/Results.kt b/theodolite/src/main/kotlin/theodolite/util/Results.kt
index 60641ea02..2221c2e64 100644
--- a/theodolite/src/main/kotlin/theodolite/util/Results.kt
+++ b/theodolite/src/main/kotlin/theodolite/util/Results.kt
@@ -3,7 +3,7 @@ package theodolite.util
 import io.quarkus.runtime.annotations.RegisterForReflection
 
 /**
- * Central class that saves the state of a execution of Theodolite. For an execution, it is used to save the result of
+ * Central class that saves the state of an execution of Theodolite. For an execution, it is used to save the result of
  * individual experiments. Further, it is used by the RestrictionStrategy to
  * perform the [theodolite.strategies.restriction.RestrictionStrategy].
  */
@@ -44,16 +44,16 @@ class Results {
      * If no experiments have been marked as either successful or unsuccessful
      * yet, a Resource with the constant value Int.MIN_VALUE is returned.
      */
-    fun getMinRequiredInstances(load: LoadDimension?): Resource? {
+    fun getMinRequiredInstances(load: LoadDimension?): Resource {
         if (this.results.isEmpty()) {
             return Resource(Int.MIN_VALUE, emptyList())
         }
 
-        var minRequiredInstances: Resource? = Resource(Int.MAX_VALUE, emptyList())
+        var minRequiredInstances = Resource(Int.MAX_VALUE, emptyList())
         for (experiment in results) {
             // Get all successful experiments for requested load
             if (experiment.key.first == load && experiment.value) {
-                if (minRequiredInstances == null || experiment.key.second.get() < minRequiredInstances.get()) {
+                if (experiment.key.second.get() < minRequiredInstances.get()) {
                     // Found new smallest resources
                     minRequiredInstances = experiment.key.second
                 }
@@ -83,4 +83,13 @@ class Results {
         }
         return maxBenchmarkedLoad
     }
+
+    /**
+     * Checks whether the results are empty.
+     *
+     * @return true if [results] is empty.
+     */
+    fun isEmpty(): Boolean{
+        return results.isEmpty()
+    }
 }
diff --git a/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt b/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt
index f7f52a6da..1af6f548b 100644
--- a/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt
+++ b/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt
@@ -34,24 +34,21 @@ class InitialGuessSearchStrategyTest {
         val guessStrategy = PrevResourceMinGuess()
         val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
         val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
-        val strategy = InitialGuessSearchStrategy(benchmarkExecutor,guessStrategy)
+        val strategy = InitialGuessSearchStrategy(benchmarkExecutor,guessStrategy, results)
 
         val actual: ArrayList<Resource?> = ArrayList()
         val expected: ArrayList<Resource?> = ArrayList(listOf(0, 2, 2, 3, 4, 6).map { x -> Resource(x, emptyList()) })
         expected.add(null)
 
-        var currentResource : Resource? = mockResources[0]
         for (load in mockLoads) {
-            val returnVal : Resource? = strategy.findSuitableResource(load, mockResources, currentResource)
+            val returnVal : Resource? = strategy.findSuitableResource(load, mockResources)
             if(returnVal != null) {
                 logger.info { "returnVal '${returnVal.get()}'" }
             }
             else {
                 logger.info { "returnVal is null." }
             }
-
             actual.add(returnVal)
-            currentResource = returnVal
         }
 
         assertEquals(actual, expected)
@@ -75,24 +72,21 @@ class InitialGuessSearchStrategyTest {
         val guessStrategy = PrevResourceMinGuess()
         val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
         val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
-        val strategy = InitialGuessSearchStrategy(benchmarkExecutor,guessStrategy)
+        val strategy = InitialGuessSearchStrategy(benchmarkExecutor,guessStrategy, results)
 
         val actual: ArrayList<Resource?> = ArrayList()
         val expected: ArrayList<Resource?> = ArrayList(listOf(0, 2, 2, 1, 4, 6).map { x -> Resource(x, emptyList()) })
         expected.add(null)
 
-        var currentResource : Resource? = mockResources[0]
         for (load in mockLoads) {
-            val returnVal : Resource? = strategy.findSuitableResource(load, mockResources, currentResource)
+            val returnVal : Resource? = strategy.findSuitableResource(load, mockResources)
             if(returnVal != null) {
                 logger.info { "returnVal '${returnVal.get()}'" }
             }
             else {
                 logger.info { "returnVal is null." }
             }
-
             actual.add(returnVal)
-            currentResource = returnVal
         }
 
         assertEquals(actual, expected)
@@ -116,25 +110,22 @@ class InitialGuessSearchStrategyTest {
         val guessStrategy = PrevResourceMinGuess()
         val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
         val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
-        val strategy = InitialGuessSearchStrategy(benchmarkExecutor, guessStrategy)
+        val strategy = InitialGuessSearchStrategy(benchmarkExecutor, guessStrategy, results)
 
         val actual: ArrayList<Resource?> = ArrayList()
         var expected: ArrayList<Resource?> = ArrayList(listOf(2, 3, 0, 4, 6).map { x -> Resource(x, emptyList()) })
         expected.add(null)
         expected = ArrayList(listOf(null) + expected)
 
-        var currentResource : Resource? = mockResources[0]
         for (load in mockLoads) {
-            val returnVal : Resource? = strategy.findSuitableResource(load, mockResources, currentResource)
+            val returnVal : Resource? = strategy.findSuitableResource(load, mockResources)
             if(returnVal != null) {
                 logger.info { "returnVal '${returnVal.get()}'" }
             }
             else {
                 logger.info { "returnVal is null." }
             }
-
             actual.add(returnVal)
-            currentResource = returnVal
         }
 
         assertEquals(actual, expected)
-- 
GitLab