diff --git a/theodolite/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt b/theodolite/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt index c4007e7f156edd505397e75b54b963fec6d0ecc9..49503b8a7c89825189dfe2d300c2c4364aba8d57 100644 --- a/theodolite/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt +++ b/theodolite/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt @@ -149,7 +149,7 @@ class TheodoliteExecutor( } private fun calculateDemandMetric(loads: List<Int>, results: Results): List<List<String>> { - return loads.map { listOf(it.toString(), results.getMinRequiredYDimensionValue(it).toString()) } + return loads.map { listOf(it.toString(), results.getOptYDimensionValue(it).toString()) } } } diff --git a/theodolite/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt b/theodolite/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt index a791bb2a8d383865a8bbb9412b9a519dc23bf6df..be9eede1066cf166f54f66ad483824acdb955c30 100644 --- a/theodolite/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt +++ b/theodolite/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt @@ -12,8 +12,8 @@ class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) { override fun apply(xValue: Int, yValues: List<Int>): List<Int> { val maxXValue: Int? = this.results.getMaxBenchmarkedXDimensionValue(xValue) - var lowerBound: Int = this.results.getMinRequiredYDimensionValue(maxXValue) - if (lowerBound == Int.MIN_VALUE || lowerBound == Int.MAX_VALUE) { + var lowerBound: Int? = this.results.getOptYDimensionValue(maxXValue) + if (lowerBound == null) { lowerBound = yValues[0] } return yValues.filter { x -> x >= lowerBound } diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt index d8c5c1c4e849cffe17ba6b16688203d189813fd7..cfdb5de3e6cca43f100308bd2652b242c2ee5109 100644 --- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt +++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt @@ -40,8 +40,7 @@ class InitialGuessSearchStrategy(benchmarkExecutor: BenchmarkExecutor, guessStra // Getting the lastLowestResource from results and calling firstGuess() with it if (!results.isEmpty()) { val maxLoad: Int? = this.results.getMaxBenchmarkedXDimensionValue(load) - lastLowestResource = this.results.getMinRequiredYDimensionValue(maxLoad) - if (lastLowestResource == Int.MAX_VALUE) lastLowestResource = null + lastLowestResource = this.results.getOptYDimensionValue(maxLoad) } lastLowestResource = this.guessStrategy.firstGuess(resources, lastLowestResource) @@ -110,8 +109,7 @@ class InitialGuessSearchStrategy(benchmarkExecutor: BenchmarkExecutor, guessStra // Getting the lastLowestLoad from results and calling firstGuess() with it if (!results.isEmpty()) { val maxResource: Int? = this.results.getMaxBenchmarkedXDimensionValue(resource) - lastMaxLoad = this.results.getMaxRequiredYDimensionValue(maxResource) - if (lastMaxLoad == Int.MIN_VALUE) lastMaxLoad = null + lastMaxLoad = this.results.getOptYDimensionValue(maxResource) } lastMaxLoad = this.guessStrategy.firstGuess(loads, lastMaxLoad) diff --git a/theodolite/src/main/kotlin/theodolite/util/Results.kt b/theodolite/src/main/kotlin/theodolite/util/Results.kt index 9feee0643d15644aa15c8af86d72aa3b61e9f81d..aeccfdc6ad5d7c946ebb5c9e6b8bafb72e78dc27 100644 --- a/theodolite/src/main/kotlin/theodolite/util/Results.kt +++ b/theodolite/src/main/kotlin/theodolite/util/Results.kt @@ -9,27 +9,57 @@ import theodolite.strategies.Metric * perform the [theodolite.strategies.restriction.RestrictionStrategy]. */ @RegisterForReflection -//TODO: Initializing überall anpassen class Results (val metric: Metric) { - //TODO: enum statt Boolean + //TODO: enum statt Boolean für successful + + // (load,resource) -> Boolean map private val results: MutableMap<Pair<Int, Int>, Boolean> = mutableMapOf() - //TODO: min instance (or max respectively) also as fields so we do not loop over results, speichert alle results für alle load/resource pairs + // if metric is "demand" : load -> resource + // if metric is "capacity": resource -> load + private var optInstances: MutableMap<Int, Int> = mutableMapOf() + /** - * Set the result for an experiment. + * Set the result for an experiment and update the [optInstances] list accordingly. * - * @param experiment A pair that identifies the experiment by the LoadDimension and Resource. + * @param experiment A pair that identifies the experiment by the Load and Resource. * @param successful the result of the experiment. Successful == true and Unsuccessful == false. */ fun setResult(experiment: Pair<Int, Int>, successful: Boolean) { this.results[experiment] = successful + + if(metric.value == "demand" && optInstances.containsKey(experiment.first)){ + + if (optInstances[experiment.first]!! > experiment.second){ + optInstances[experiment.first] = experiment.second + } + } + else if(metric.value == "demand" && !optInstances.containsKey(experiment.first)){ + if(!successful){ + optInstances[experiment.first] = Int.MAX_VALUE + }else { + optInstances[experiment.first] = experiment.second + } + } + else if(metric.value == "capacity" && optInstances.containsKey(experiment.second)){ + if (optInstances[experiment.second]!! < experiment.first){ + optInstances[experiment.second] = experiment.first + } + } + else if(metric.value == "capacity" && !optInstances.containsKey(experiment.second)){ + if(!successful){ + optInstances[experiment.second] = Int.MIN_VALUE + } else { + optInstances[experiment.second] = experiment.first + } + } } /** * Get the result for an experiment. * - * @param experiment A pair that identifies the experiment by the LoadDimension and Resource. + * @param experiment A pair that identifies the experiment by the Load and Resource. * @return true if the experiment was successful and false otherwise. If the result has not been reported so far, * null is returned. * @@ -39,84 +69,48 @@ class Results (val metric: Metric) { } /** - * Get the smallest suitable number of instances for a specified LoadDimension. + * Get the smallest suitable number of instances for a specified x-Value. + * The x-Value corresponds to the... + * - load, if the metric is "demand". + * - resource, if the metric is "capacity". * * @param xValue the Value of the x-dimension of the current metric * * @return the smallest suitable number of resources/loads (depending on metric). - * If there is no experiment that has been executed yet, Int.MIN_VALUE is returned. - * If there is no experiment for the given [xValue] or there is none marked successful yet, - * Int.MAX_VALUE is returned. + * If there is no experiment that has been executed yet, there is no experiment + * for the given [xValue] or there is none marked successful yet, null is returned. */ - fun getMinRequiredYDimensionValue(xValue: Int?): Int { - if (this.results.isEmpty()) { //should add || xValue == null - return Int.MIN_VALUE - } - - var minRequiredYValue = Int.MAX_VALUE - for (experiment in results) { - // Get all successful experiments for requested xValue - if (getXDimensionValue(experiment.key) == xValue && experiment.value) { - val experimentYValue = getYDimensionValue(experiment.key) - if (experimentYValue < minRequiredYValue) { - // Found new smallest resources - minRequiredYValue = experimentYValue - } + fun getOptYDimensionValue(xValue: Int?): Int? { + if (xValue != null) { + val res = optInstances[xValue] + if (res != Int.MAX_VALUE && res != Int.MIN_VALUE) { + return res } } - return minRequiredYValue + return null } - /** - * Get the largest y-Value for which the given x-Value has a positive experiment outcome. - * x- and y-values depend on the metric in use. - * - * @param xValue the Value of the x-dimension of the current metric + * Get the largest x-Value that has been tested and reported so far (successful or unsuccessful), + * which is smaller than the specified x-Value. * - * @return the largest suitable number of resources/loads (depending on metric). - * If there wasn't any experiment executed yet, Int.MAX_VALUE is returned. - * If the experiments for the specified [xValue] wasn't executed yet or the experiments were not successful - * Int.MIN_VALUE is returned. - */ - fun getMaxRequiredYDimensionValue(xValue: Int?): Int { - if (this.results.isEmpty()) { //should add || xValue == null - return Int.MAX_VALUE - } - - var maxRequiredYValue = Int.MIN_VALUE - for (experiment in results) { - // Get all successful experiments for requested xValue - if (getXDimensionValue(experiment.key) == xValue && experiment.value) { - val experimentYValue = getYDimensionValue(experiment.key) - if (experimentYValue > maxRequiredYValue) { - // Found new largest value - maxRequiredYValue = experimentYValue - } - } - } - return maxRequiredYValue - } - - // TODO: SÖREN FRAGEN WARUM WIR DAS BRAUCHEN UND NICHT EINFACH PREV, WEIL NICHT DURCHGELAUFEN? - // TODO Kommentar zu XDimension und YDimension - /** - * Get the largest LoadDimension that has been reported executed successfully (or unsuccessfully) so far, for a - * LoadDimension and is smaller than the given LoadDimension. + * The x-Value corresponds to the... + * - load, if the metric is "demand". + * - resource, if the metric is "capacity". * - * @param load the LoadDimension + * @param xValue the Value of the x-dimension of the current metric * - * @return the largest LoadDimension or null, if there is none for this LoadDimension + * @return the largest tested x-Value or null, if there wasn't any tested which is smaller than the [xValue]. */ fun getMaxBenchmarkedXDimensionValue(xValue: Int): Int? { var maxBenchmarkedXValue: Int? = null - for (experiment in results) { - val experimentXValue = getXDimensionValue(experiment.key) - if (experimentXValue <= xValue) { //warum \leq? + for (instance in optInstances) { + val instanceXValue= instance.key + if (instanceXValue <= xValue) { if (maxBenchmarkedXValue == null) { - maxBenchmarkedXValue = experimentXValue - } else if (maxBenchmarkedXValue < experimentXValue) { - maxBenchmarkedXValue = experimentXValue + maxBenchmarkedXValue = instanceXValue + } else if (maxBenchmarkedXValue < instanceXValue) { + maxBenchmarkedXValue = instanceXValue } } } @@ -131,18 +125,4 @@ class Results (val metric: Metric) { fun isEmpty(): Boolean{ return results.isEmpty() } - - fun getYDimensionValue(experimentKey: Pair<Int, Int>): Int{ - if(metric.value == "demand"){ - return experimentKey.second - } - return experimentKey.first - } - - fun getXDimensionValue(experimentKey: Pair<Int, Int>): Int{ - if(metric.value == "demand"){ - return experimentKey.first - } - return experimentKey.second - } } diff --git a/theodolite/src/test/kotlin/theodolite/strategies/restriction/LowerBoundRestrictionTest.kt b/theodolite/src/test/kotlin/theodolite/strategies/restriction/LowerBoundRestrictionTest.kt index 230b0f785ea72f061c12fe43049403efa149856f..e659d5f542910611af96d7eb6a68140ebd43065b 100644 --- a/theodolite/src/test/kotlin/theodolite/strategies/restriction/LowerBoundRestrictionTest.kt +++ b/theodolite/src/test/kotlin/theodolite/strategies/restriction/LowerBoundRestrictionTest.kt @@ -63,7 +63,7 @@ internal class LowerBoundRestrictionTest { results.setResult(10000, 1, false) results.setResult(20000, 2, true) - val minRequiredInstances = results.getMinRequiredYDimensionValue(20000) + val minRequiredInstances = results.getOptYDimensionValue(20000) assertNotNull(minRequiredInstances) assertEquals(2, minRequiredInstances!!) @@ -79,7 +79,7 @@ internal class LowerBoundRestrictionTest { results.setResult(10000, 1, false) results.setResult(20000, 2, false) - val minRequiredInstances = results.getMinRequiredYDimensionValue(20000) + val minRequiredInstances = results.getOptYDimensionValue(20000) assertNotNull(minRequiredInstances) assertEquals(2, minRequiredInstances!!) diff --git a/theodolite/src/test/kotlin/theodolite/util/ResultsTest.kt b/theodolite/src/test/kotlin/theodolite/util/ResultsTest.kt index f8f86daeeb77e3928fa7a686dbf05aa4b714af51..f3556d8072e25e90fe76500dfdc215ba17999751 100644 --- a/theodolite/src/test/kotlin/theodolite/util/ResultsTest.kt +++ b/theodolite/src/test/kotlin/theodolite/util/ResultsTest.kt @@ -11,60 +11,99 @@ import theodolite.strategies.Metric internal class ResultsTest { @Test - fun testMinRequiredInstancesWhenSuccessful() { + fun testMinRequiredInstancesWhenSuccessfulDemand() { val results = Results(Metric.from("demand")) - results.setResult(10000, 1, true) - results.setResult(10000, 2, true) - results.setResult(20000, 1, false) - results.setResult(20000, 2, true) + results.setResult(Pair(10000, 1), true) + results.setResult(Pair(10000, 2), true) + results.setResult(Pair(20000, 1), false) + results.setResult(Pair(20000, 2), true) - val minRequiredInstances = results.getMinRequiredYDimensionValue(20000) + val minRequiredInstances = results.getOptYDimensionValue(20000) assertNotNull(minRequiredInstances) - assertEquals(2, minRequiredInstances!!) + assertEquals(2, minRequiredInstances) } @Test @Disabled + // TODO necessary? fun testMinRequiredInstancesWhenNotSuccessful() { // This test is currently not implemented this way, but might later be the desired behavior. val results = Results(Metric.from("demand")) - results.setResult(10000, 1, true) - results.setResult(10000, 2, true) - results.setResult(20000, 1, false) - results.setResult(20000, 2, false) + results.setResult(Pair(10000, 1), true) + results.setResult(Pair(10000, 2), true) + results.setResult(Pair(20000, 1), false) + results.setResult(Pair(20000, 2), false) - val minRequiredInstances = results.getMinRequiredYDimensionValue(20000) + val minRequiredInstances = results.getOptYDimensionValue(20000) assertNotNull(minRequiredInstances) assertEquals(2, minRequiredInstances!!) } - private fun Results.setResult(load: Int, resource: Int, successful: Boolean) { - this.setResult(Pair(load, resource), successful) - } - - @Test - fun testGetMaxBenchmarkedLoadWhenAllSuccessful() { + fun testGetMaxBenchmarkedLoadWhenAllSuccessfulDemand() { val results = Results(Metric.from("demand")) - results.setResult(10000, 1, true) - results.setResult(10000, 2, true) + results.setResult(Pair(10000, 1), true) + results.setResult(Pair(10000, 2), true) - val test1 = results.getMaxBenchmarkedXDimensionValue(100000)!! + val test1 = results.getMaxBenchmarkedXDimensionValue(100000) + assertNotNull(test1) assertEquals(10000, test1) } @Test - fun testGetMaxBenchmarkedLoadWhenLargestNotSuccessful() { + fun testGetMaxBenchmarkedLoadWhenLargestNotSuccessfulDemand() { val results = Results(Metric.from("demand")) - results.setResult(10000, 1, true) - results.setResult(10000, 2, true) - results.setResult(20000, 1, false) + results.setResult(Pair(10000, 1), true) + results.setResult(Pair(10000, 2), true) + results.setResult(Pair(20000, 1), false) - val test2 = results.getMaxBenchmarkedXDimensionValue(100000)!! + val test2 = results.getMaxBenchmarkedXDimensionValue(100000) + assertNotNull(test2) assertEquals(20000, test2) } + + @Test + fun testMaxRequiredInstancesWhenSuccessfulCapacity() { + val results = Results(Metric.from("capacity")) + results.setResult(Pair(10000, 1), true) + results.setResult(Pair(20000, 1), false) + results.setResult(Pair(10000, 2), true) + results.setResult(Pair(20000, 2), true) + + val maxRequiredInstances = results.getOptYDimensionValue(2) + + assertNotNull(maxRequiredInstances) + assertEquals(20000, maxRequiredInstances) + } + + + @Test + fun testGetMaxBenchmarkedLoadWhenAllSuccessfulCapacity() { + val results = Results(Metric.from("capacity")) + results.setResult(Pair(10000, 1), true) + results.setResult(Pair(10000, 2), true) + + val test1 = results.getMaxBenchmarkedXDimensionValue(5) + + assertNotNull(test1) + assertEquals(2, test1) + } + + @Test + fun testGetMaxBenchmarkedLoadWhenLargestNotSuccessfulCapacity() { + val results = Results(Metric.from("capacity")) + results.setResult(Pair(10000, 1), true) + results.setResult(Pair(20000, 1), true) + results.setResult(Pair(10000, 2), false) + + + val test2 = results.getMaxBenchmarkedXDimensionValue(5) + + assertNotNull(test2) + assertEquals(2, test2) + } }