From 9233428f7f0fc6c9ebdb1181e965f98b10948900 Mon Sep 17 00:00:00 2001 From: David Molnar Date: Thu, 21 Jun 2018 12:46:05 +0200 Subject: [PATCH 01/36] add basic VNS/TS Algorithm (Schneider) structure --- .../at/ac/tuwien/otl/evrptw/EVRPTWSolver.kt | 7 +- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 9 +- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 93 +++++++++++++++++++ .../evrptw/metaheuristic/IMetaHeuristic.kt | 16 ++++ .../metaheuristic/NeighbourhoodStructure.kt | 15 +++ 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/IMetaHeuristic.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/EVRPTWSolver.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/EVRPTWSolver.kt index 1b1d3d9..2e1327a 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/EVRPTWSolver.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/EVRPTWSolver.kt @@ -3,6 +3,7 @@ package at.ac.tuwien.otl.evrptw import at.ac.tuwien.otl.evrptw.construction.IConstructionHeuristic import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.metaheuristic.IMetaHeuristic /** *

About this class

@@ -15,7 +16,9 @@ import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution */ class EVRPTWSolver { - fun solve(instance: EVRPTWInstance, constructionHeuristic: IConstructionHeuristic): EVRPTWSolution { - return constructionHeuristic.generateSolution(instance) + fun solve(instance: EVRPTWInstance, constructionHeuristic: IConstructionHeuristic, metaHeuristic: IMetaHeuristic): EVRPTWSolution { + val solution = constructionHeuristic.generateSolution(instance) + + return metaHeuristic.improveSolution(solution) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index d22a896..2f9f5e5 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -5,6 +5,7 @@ import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import at.ac.tuwien.otl.evrptw.loader.InstanceLoader import java.util.HashSet import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance +import at.ac.tuwien.otl.evrptw.metaheuristic.HybridVnsTsMetaHeuristic import java.util.concurrent.TimeUnit /** @@ -23,6 +24,7 @@ class Main { private val testInstances = listOf("c101C10", "r102C10", "rc201C10") private val instances = listOf("c103_21", "c105_21", "c204_21", "r102_21", "r107_21", "r205_21", "r211_21", "rc101_21", "rc106_21", "rc203_21") private val constructionHeuristic = TimeOrientedNearestNeighbourHeuristic(false) + private val metaHeuristic = HybridVnsTsMetaHeuristic() private val solver = EVRPTWSolver() private const val rampUpRuns = 20 private const val measuredRuns = 30 @@ -30,7 +32,7 @@ class Main { @JvmStatic fun main(args: Array) { // Ramp-up phase, ignore runtimes - for (j in 1..rampUpRuns) { + /*for (j in 1..rampUpRuns) { for (i in 0..9) { runAlgorithmOnInstance(i, false) } @@ -49,7 +51,8 @@ class Main { println("\nAvg. runtime for each instance across $measuredRuns runs") for (i in 0..9) { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") - } + }*/ + runAlgorithmOnInstance(0, false) } private fun runAlgorithmOnInstance(instanceId: Int, detailed: Boolean): Long { @@ -57,7 +60,7 @@ class Main { val instanceLoader = InstanceLoader() val instance = instanceLoader.load(instanceString) val start = System.nanoTime() - val solution = solver.solve(instance, constructionHeuristic) + val solution = solver.solve(instance, constructionHeuristic, metaHeuristic) val durationInNano = System.nanoTime() - start val nodesMissing = allNodesInRoutes(instance, solution.routes) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt new file mode 100644 index 0000000..d64095c --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -0,0 +1,93 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution + +/** + *

About this class

+ + *

Description of this class

+ * + * @author David Molnar + * @version 1.0.0 + * @since 1.0.0 + */ +class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHeuristic { + private val neighbourhoodStructures = mapOf( + 1 to NeighbourhoodStructure(2, 1), + 2 to NeighbourhoodStructure(2, 2), + 3 to NeighbourhoodStructure(2, 3), + 4 to NeighbourhoodStructure(2, 4), + 5 to NeighbourhoodStructure(2, 5), + 6 to NeighbourhoodStructure(3, 1), + 7 to NeighbourhoodStructure(3, 2), + 8 to NeighbourhoodStructure(3, 3), + 9 to NeighbourhoodStructure(3, 4), + 10 to NeighbourhoodStructure(3, 5), + 11 to NeighbourhoodStructure(4, 1), + 12 to NeighbourhoodStructure(4, 2), + 13 to NeighbourhoodStructure(4, 3), + 14 to NeighbourhoodStructure(4, 4), + 15 to NeighbourhoodStructure(4, 5) + ) + + override fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution { + + var bestSolution = evrptwSolution + var k = 1 + var i = 0 + var feasibilityPhase = true + + val n_dist = 200 + val n_feas = 500 + val n_tabu = 100 + + while (feasibilityPhase || (!feasibilityPhase && i < n_dist)) { + val neighbourhoodStructure = neighbourhoodStructures[k] + val newSolution = generateRandomPoint(bestSolution, neighbourhoodStructure!!) + val optimizedNewSolution = applyTabuSearch(newSolution, n_tabu) + + if (acceptSimulatedAnnealing(optimizedNewSolution, bestSolution)) { + bestSolution = optimizedNewSolution + k = 1 + } else { + k = (k % neighbourhoodStructures.size) + 1 + } + + if (feasibilityPhase) { + if (!feasible(bestSolution)) { + if (i == n_feas) { + addVehicle(bestSolution) + i = -1 + } + } else { + feasibilityPhase = false + i = -1 + } + } + i++ + } + return evrptwSolution + } + + private fun generateRandomPoint(solution: EVRPTWSolution, neighbourhoodStructure: NeighbourhoodStructure): EVRPTWSolution { + return solution + } + + private fun applyTabuSearch(solution: EVRPTWSolution, n_tabu: Int): EVRPTWSolution { + return solution + } + + private fun acceptSimulatedAnnealing(optimizedNewSolution: EVRPTWSolution, bestSolution: EVRPTWSolution): Boolean { + return true + } + + private fun feasible(solution: EVRPTWSolution): Boolean { + return true + } + + private fun addVehicle(solution: EVRPTWSolution) { + + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/IMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/IMetaHeuristic.kt new file mode 100644 index 0000000..f31c0e5 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/IMetaHeuristic.kt @@ -0,0 +1,16 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution + +/** + *

About this class

+ + *

Description of this class

+ * + * @author David Molnar + * @version 1.0.0 + * @since 1.0.0 + */ +interface IMetaHeuristic { + fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt new file mode 100644 index 0000000..9a83fe3 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt @@ -0,0 +1,15 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic + +/** + *

About this class

+ + *

Description of this class

+ * + * @author David Molnar + * @version 1.0.0 + * @since 1.0.0 + */ +data class NeighbourhoodStructure( + val numberOfInvolvedRoutes: Int, + val maxVertices: Int +) \ No newline at end of file From 9cbe81019d6798a453d197fa16a9e7fb62c69f52 Mon Sep 17 00:00:00 2001 From: David Molnar Date: Thu, 21 Jun 2018 15:27:27 +0200 Subject: [PATCH 02/36] implement generate random point (still buggy) --- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 3 +- .../evrptw/metaheuristic/ExchangeSequence.kt | 17 ++++++ .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 56 ++++++++++++++++++- .../metaheuristic/NeighbourhoodStructure.kt | 4 +- 4 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ExchangeSequence.kt diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 2f9f5e5..44b734c 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -3,9 +3,9 @@ package at.ac.tuwien.otl.evrptw import at.ac.tuwien.otl.evrptw.construction.TimeOrientedNearestNeighbourHeuristic import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import at.ac.tuwien.otl.evrptw.loader.InstanceLoader -import java.util.HashSet import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.metaheuristic.HybridVnsTsMetaHeuristic +import java.util.* import java.util.concurrent.TimeUnit /** @@ -52,6 +52,7 @@ class Main { for (i in 0..9) { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ +// println(Random().nextInt(1-1) + 1) runAlgorithmOnInstance(0, false) } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ExchangeSequence.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ExchangeSequence.kt new file mode 100644 index 0000000..e82f781 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ExchangeSequence.kt @@ -0,0 +1,17 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance + +/** + *

About this class

+ + *

Description of this class

+ * + * @author David Molnar + * @version 1.0.0 + * @since 1.0.0 + */ +data class ExchangeSequence( + val nodes: List, + val startIndex: Int +) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index d64095c..8873109 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -1,6 +1,9 @@ package at.ac.tuwien.otl.evrptw.metaheuristic +import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import java.util.* +import java.util.logging.Logger /** *

About this class

@@ -12,6 +15,8 @@ import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution * @since 1.0.0 */ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHeuristic { + private val log: Logger = Logger.getLogger(this.javaClass.name) + private val random: Random = Random(123456) private val neighbourhoodStructures = mapOf( 1 to NeighbourhoodStructure(2, 1), 2 to NeighbourhoodStructure(2, 2), @@ -66,10 +71,54 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe } i++ } + return evrptwSolution } private fun generateRandomPoint(solution: EVRPTWSolution, neighbourhoodStructure: NeighbourhoodStructure): EVRPTWSolution { + val routes: MutableList> = mutableListOf() + + if (solution.routes.size < neighbourhoodStructure.numberOfInvolvedRoutes) { + log("ERROR: fewer routes than involved routes") + } + + for (i in 0 until neighbourhoodStructure.numberOfInvolvedRoutes) { + val index = random.nextInt(solution.routes.size) + + routes.add(solution.routes[index]) + solution.routes.removeAt(index) + } + + val exchangeSequences = mutableListOf() + + for (route in routes) { + val upperBound = Math.min(neighbourhoodStructure.maxVertices, route.size - 2) + val numberOfSuccessiveVertices = if (upperBound == 1) { + 1 + } else { + random.nextInt(upperBound) + 1 + } + val startIndex = random.nextInt((route.size - numberOfSuccessiveVertices - 1)) + 1 + + val exchangeSequence = ExchangeSequence(route.subList(startIndex, startIndex + numberOfSuccessiveVertices), startIndex) + exchangeSequences.add(exchangeSequence) + + route.subList(startIndex, startIndex + numberOfSuccessiveVertices).clear() + } + + for (i in 0 until routes.size) { + val startIndex = exchangeSequences[i].startIndex + val sequence: List = if (i == 0) { + exchangeSequences[exchangeSequences.size - 1].nodes + } else { + exchangeSequences[i - 1].nodes + } + + routes[i].addAll(startIndex, sequence) + } + + solution.routes.addAll(routes) + return solution } @@ -86,8 +135,11 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe } private fun addVehicle(solution: EVRPTWSolution) { - } - + private fun log(message: String) { + if (logEnabled) { + log.info(message) + } + } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt index 9a83fe3..0531ed1 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt @@ -10,6 +10,6 @@ package at.ac.tuwien.otl.evrptw.metaheuristic * @since 1.0.0 */ data class NeighbourhoodStructure( - val numberOfInvolvedRoutes: Int, - val maxVertices: Int + val numberOfInvolvedRoutes: Int, + val maxVertices: Int ) \ No newline at end of file From a5b68f31f57ccc5dead0145f704b748ddf16aa87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Thu, 21 Jun 2018 19:32:12 +0200 Subject: [PATCH 03/36] refactor code, fix ConcurrentModificationException --- solutions/c103_21_sol.txt | 58 ++++++------ .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 1 - .../ExchangeSequence.kt | 4 +- .../otl/evrptw/dto/NeighbourhoodStructure.kt | 35 ++++++++ .../otl/evrptw/metaheuristic/Constants.kt | 19 ++++ .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 88 +++---------------- .../NeighbourSolutionGenerator.kt | 74 ++++++++++++++++ .../metaheuristic/NeighbourhoodStructure.kt | 15 ---- 8 files changed, 169 insertions(+), 125 deletions(-) rename src/main/kotlin/at/ac/tuwien/otl/evrptw/{metaheuristic => dto}/ExchangeSequence.kt (70%) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/NeighbourhoodStructure.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt delete mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index 14b1086..e7343cd 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,32 +1,32 @@ # solution for c103_21 2249.872316017405 -D0, C20, C21, C22, C24, C29, C30, C26, C7, C3, D0 -D0, C67, C65, C63, C62, C69, C68, C64, C41, C42, C44, D0 -D0, C23, C25, C27, C28, C34, C36, D0 -D0, C75, C1, C2, C4, C6, C9, D0 -D0, C46, C45, C48, C50, C51, C52, C47, C43, D0 -D0, C90, C89, C88, C85, C84, C83, C86, C87, C91, D0 -D0, C74, C72, D0 -D0, C40, C49, D0 -D0, C10, C11, C5, D0 -D0, C66, D0 -D0, C8, D0 -D0, C98, C95, C96, C99, D0 -D0, C32, C31, C33, D0 -D0, C61, D0 -D0, C17, C18, C15, D0 -D0, C59, C55, D0 +D0, C2, C12, S4, C87, C99, C79, D0 +D0, C70, C47, C20, C60, C84, C83, C86, C93, C91, D0 +D0, S12, C97, D0 +D0, C7, C57, D0 +D0, C11, D0 +D0, C37, C22, C53, D0 +D0, C74, C29, C54, D0 +D0, C63, C49, C66, C6, D0 +D0, C44, C16, C27, C28, C8, C32, D0 +D0, C58, D0 +D0, C88, D0 +D0, C92, S20, C19, C75, D0 +D0, C17, C55, C38, C45, C59, D0 +D0, C1, D0 +D0, C33, C52, C89, C30, D0 +D0, C15, S7, D0 +D0, C42, C36, C26, C56, C73, S18, D0 +D0, C95, S20, D0 +D0, C82, C10, C18, C21, C25, C9, C31, C35, D0 +D0, C34, D0 D0, C14, D0 -D0, C12, D0 -D0, C82, D0 -D0, S12, C39, C38, C37, C35, D0 -D0, S4, C94, C93, C92, C97, C100, D0 -D0, C13, D0 -D0, C19, D0 -D0, S7, C16, D0 -D0, C57, D0 -D0, S16, C53, C56, C54, D0 -D0, S14, C60, C58, D0 -D0, S20, C78, C79, C77, C76, C81, D0 -D0, S18, C80, D0 -D0, S20, C73, C70, C71, D0 +D0, C96, C48, C43, D0 +D0, C78, D0 +D0, C80, D0 +D0, C76, S16, C40, C24, C81, C71, C68, C5, C3, D0 +D0, C67, C65, C61, C62, C39, S14, C64, C23, C98, C51, D0 +D0, C50, D0 +D0, C72, C94, C69, D0 +D0, C46, C77, C13, C85, C90, C100, D0 +D0, C4, C41, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 44b734c..4ccbaf7 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -5,7 +5,6 @@ import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import at.ac.tuwien.otl.evrptw.loader.InstanceLoader import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.metaheuristic.HybridVnsTsMetaHeuristic -import java.util.* import java.util.concurrent.TimeUnit /** diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ExchangeSequence.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/ExchangeSequence.kt similarity index 70% rename from src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ExchangeSequence.kt rename to src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/ExchangeSequence.kt index e82f781..fa35973 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ExchangeSequence.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/ExchangeSequence.kt @@ -1,6 +1,4 @@ -package at.ac.tuwien.otl.evrptw.metaheuristic - -import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance +package at.ac.tuwien.otl.evrptw.dto /** *

About this class

diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/NeighbourhoodStructure.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/NeighbourhoodStructure.kt new file mode 100644 index 0000000..2fb8675 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/NeighbourhoodStructure.kt @@ -0,0 +1,35 @@ +package at.ac.tuwien.otl.evrptw.dto + +/** + *

About this class

+ + *

Description of this class

+ * + * @author David Molnar + * @version 1.0.0 + * @since 1.0.0 + */ +data class NeighbourhoodStructure( + val numberOfInvolvedRoutes: Int, + val maxVertices: Int +) { + companion object { + val STRUCTURES = mapOf( + 1 to NeighbourhoodStructure(2, 1), + 2 to NeighbourhoodStructure(2, 2), + 3 to NeighbourhoodStructure(2, 3), + 4 to NeighbourhoodStructure(2, 4), + 5 to NeighbourhoodStructure(2, 5), + 6 to NeighbourhoodStructure(3, 1), + 7 to NeighbourhoodStructure(3, 2), + 8 to NeighbourhoodStructure(3, 3), + 9 to NeighbourhoodStructure(3, 4), + 10 to NeighbourhoodStructure(3, 5), + 11 to NeighbourhoodStructure(4, 1), + 12 to NeighbourhoodStructure(4, 2), + 13 to NeighbourhoodStructure(4, 3), + 14 to NeighbourhoodStructure(4, 4), + 15 to NeighbourhoodStructure(4, 5) + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt new file mode 100644 index 0000000..bfcba26 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -0,0 +1,19 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class Constants private constructor() { + + companion object { + const val N_DIST = 200 + const val N_FEAS = 500 + const val N_TABU = 100 + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 8873109..abd3e1f 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -1,8 +1,11 @@ package at.ac.tuwien.otl.evrptw.metaheuristic -import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution -import java.util.* +import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU +import java.util.Random import java.util.logging.Logger /** @@ -17,50 +20,28 @@ import java.util.logging.Logger class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHeuristic { private val log: Logger = Logger.getLogger(this.javaClass.name) private val random: Random = Random(123456) - private val neighbourhoodStructures = mapOf( - 1 to NeighbourhoodStructure(2, 1), - 2 to NeighbourhoodStructure(2, 2), - 3 to NeighbourhoodStructure(2, 3), - 4 to NeighbourhoodStructure(2, 4), - 5 to NeighbourhoodStructure(2, 5), - 6 to NeighbourhoodStructure(3, 1), - 7 to NeighbourhoodStructure(3, 2), - 8 to NeighbourhoodStructure(3, 3), - 9 to NeighbourhoodStructure(3, 4), - 10 to NeighbourhoodStructure(3, 5), - 11 to NeighbourhoodStructure(4, 1), - 12 to NeighbourhoodStructure(4, 2), - 13 to NeighbourhoodStructure(4, 3), - 14 to NeighbourhoodStructure(4, 4), - 15 to NeighbourhoodStructure(4, 5) - ) + private val neighbourSolutionGenerator = NeighbourSolutionGenerator() override fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution { - var bestSolution = evrptwSolution var k = 1 var i = 0 var feasibilityPhase = true - val n_dist = 200 - val n_feas = 500 - val n_tabu = 100 - - while (feasibilityPhase || (!feasibilityPhase && i < n_dist)) { - val neighbourhoodStructure = neighbourhoodStructures[k] - val newSolution = generateRandomPoint(bestSolution, neighbourhoodStructure!!) - val optimizedNewSolution = applyTabuSearch(newSolution, n_tabu) + while (feasibilityPhase || (!feasibilityPhase && i < N_DIST)) { + val newSolution = neighbourSolutionGenerator.generateRandomPoint(bestSolution, k) + val optimizedNewSolution = applyTabuSearch(newSolution, N_TABU) if (acceptSimulatedAnnealing(optimizedNewSolution, bestSolution)) { bestSolution = optimizedNewSolution k = 1 } else { - k = (k % neighbourhoodStructures.size) + 1 + k = (k % NeighbourhoodStructure.STRUCTURES.size) + 1 } if (feasibilityPhase) { if (!feasible(bestSolution)) { - if (i == n_feas) { + if (i == N_FEAS) { addVehicle(bestSolution) i = -1 } @@ -75,53 +56,6 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe return evrptwSolution } - private fun generateRandomPoint(solution: EVRPTWSolution, neighbourhoodStructure: NeighbourhoodStructure): EVRPTWSolution { - val routes: MutableList> = mutableListOf() - - if (solution.routes.size < neighbourhoodStructure.numberOfInvolvedRoutes) { - log("ERROR: fewer routes than involved routes") - } - - for (i in 0 until neighbourhoodStructure.numberOfInvolvedRoutes) { - val index = random.nextInt(solution.routes.size) - - routes.add(solution.routes[index]) - solution.routes.removeAt(index) - } - - val exchangeSequences = mutableListOf() - - for (route in routes) { - val upperBound = Math.min(neighbourhoodStructure.maxVertices, route.size - 2) - val numberOfSuccessiveVertices = if (upperBound == 1) { - 1 - } else { - random.nextInt(upperBound) + 1 - } - val startIndex = random.nextInt((route.size - numberOfSuccessiveVertices - 1)) + 1 - - val exchangeSequence = ExchangeSequence(route.subList(startIndex, startIndex + numberOfSuccessiveVertices), startIndex) - exchangeSequences.add(exchangeSequence) - - route.subList(startIndex, startIndex + numberOfSuccessiveVertices).clear() - } - - for (i in 0 until routes.size) { - val startIndex = exchangeSequences[i].startIndex - val sequence: List = if (i == 0) { - exchangeSequences[exchangeSequences.size - 1].nodes - } else { - exchangeSequences[i - 1].nodes - } - - routes[i].addAll(startIndex, sequence) - } - - solution.routes.addAll(routes) - - return solution - } - private fun applyTabuSearch(solution: EVRPTWSolution, n_tabu: Int): EVRPTWSolution { return solution } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt new file mode 100644 index 0000000..40023c0 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt @@ -0,0 +1,74 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.ExchangeSequence +import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure +import java.util.Random + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class NeighbourSolutionGenerator { + + private val random = Random(123456) + + fun generateRandomPoint(solution: EVRPTWSolution, neighbour: Int): EVRPTWSolution { + val routes: MutableList> = mutableListOf() + val neighbourhoodStructure = NeighbourhoodStructure.STRUCTURES[neighbour]!! + + if (solution.routes.size < neighbourhoodStructure.numberOfInvolvedRoutes) { + throw RuntimeException("ERROR: fewer routes than involved routes") + } + + for (i in 0 until neighbourhoodStructure.numberOfInvolvedRoutes) { + val index = random.nextInt(solution.routes.size) + + routes.add(solution.routes[index]) + solution.routes.removeAt(index) + } + + val exchangeSequences = mutableListOf() + + for (route in routes) { + val upperBound = Math.min(neighbourhoodStructure.maxVertices, route.size - 2) + val numberOfSuccessiveVertices = if (upperBound == 1) { + 1 + } else { + random.nextInt(upperBound) + 1 + } + val startIndex = random.nextInt((route.size - numberOfSuccessiveVertices - 1)) + 1 + + val exchangeSequence = ExchangeSequence( + route.subList( + startIndex, + startIndex + numberOfSuccessiveVertices + ).toList(), startIndex + ) + exchangeSequences.add(exchangeSequence) + + route.subList(startIndex, startIndex + numberOfSuccessiveVertices).clear() + } + + for (i in 0 until routes.size) { + val startIndex = exchangeSequences[i].startIndex + val sequence: List = if (i == 0) { + exchangeSequences[exchangeSequences.size - 1].nodes + } else { + exchangeSequences[i - 1].nodes + } + + routes[i].addAll(startIndex, sequence) + } + + solution.routes.addAll(routes) + + return solution + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt deleted file mode 100644 index 0531ed1..0000000 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourhoodStructure.kt +++ /dev/null @@ -1,15 +0,0 @@ -package at.ac.tuwien.otl.evrptw.metaheuristic - -/** - *

About this class

- - *

Description of this class

- * - * @author David Molnar - * @version 1.0.0 - * @since 1.0.0 - */ -data class NeighbourhoodStructure( - val numberOfInvolvedRoutes: Int, - val maxVertices: Int -) \ No newline at end of file From 1977c3a46cd3bfad4cbf2b7772fd3f80ca0cc99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Thu, 21 Jun 2018 22:38:33 +0200 Subject: [PATCH 04/36] implement basics of TabuSearch, extract constants, refactor main algorithm --- .../otl/evrptw/metaheuristic/Constants.kt | 4 ++ .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 9 ++-- .../metaheuristic/tabusearch/TabuSearch.kt | 45 +++++++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index bfcba26..5f9258a 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -15,5 +15,9 @@ class Constants private constructor() { const val N_DIST = 200 const val N_FEAS = 500 const val N_TABU = 100 + const val N_PENALTY = 2 + const val COOLING_FACTOR = 0.9 + const val TABU_TENURE_MIN = 15 + const val TABU_TENURE_MAX = 30 } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index abd3e1f..10c164f 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -4,7 +4,7 @@ import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS -import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU +import at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch.TabuSearch import java.util.Random import java.util.logging.Logger @@ -21,6 +21,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe private val log: Logger = Logger.getLogger(this.javaClass.name) private val random: Random = Random(123456) private val neighbourSolutionGenerator = NeighbourSolutionGenerator() + private val tabuSearch = TabuSearch() override fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution { var bestSolution = evrptwSolution @@ -30,7 +31,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe while (feasibilityPhase || (!feasibilityPhase && i < N_DIST)) { val newSolution = neighbourSolutionGenerator.generateRandomPoint(bestSolution, k) - val optimizedNewSolution = applyTabuSearch(newSolution, N_TABU) + val optimizedNewSolution = tabuSearch.apply(newSolution) if (acceptSimulatedAnnealing(optimizedNewSolution, bestSolution)) { bestSolution = optimizedNewSolution @@ -56,10 +57,6 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe return evrptwSolution } - private fun applyTabuSearch(solution: EVRPTWSolution, n_tabu: Int): EVRPTWSolution { - return solution - } - private fun acceptSimulatedAnnealing(optimizedNewSolution: EVRPTWSolution, bestSolution: EVRPTWSolution): Boolean { return true } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt new file mode 100644 index 0000000..2f9a194 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -0,0 +1,45 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class TabuSearch { + + fun apply(solution: EVRPTWSolution): EVRPTWSolution { + var overallBestSolution = solution + var bestCandidate = overallBestSolution + val tabuList = mutableListOf(bestCandidate) // add incoming solution to prevent its reversal + var iteration = 0 + while (iteration < N_TABU) { + bestCandidate = bestSolutionOfNeighbourhood(bestCandidate, tabuList) + tabuList.add(bestCandidate) + refreshTabuList(tabuList) + if (evaluateSolution(bestCandidate) < evaluateSolution(overallBestSolution)) { + overallBestSolution = bestCandidate + } + iteration++ + } + return overallBestSolution + } + + private fun bestSolutionOfNeighbourhood(solution: EVRPTWSolution, tabuList: List): EVRPTWSolution { + return solution + } + + private fun refreshTabuList(list: MutableList) { + // remove elements older than some constant + } + + private fun evaluateSolution(solution: EVRPTWSolution): Double { + return 42.0 + } +} \ No newline at end of file From 79eafe51d97fc529e6b594d5bb5052cf5435a15e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Thu, 21 Jun 2018 23:39:33 +0200 Subject: [PATCH 05/36] implement EVRPTWSolution copy constructor, adjust MetaHeuristic, NeighbourSolutionGenerator, TabuSearch --- solutions/c103_21_sol.txt | 58 +++++++++---------- .../tuwien/otl/evrptw/dto/EVRPTWSolution.kt | 47 +++++++++++++++ .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 2 - .../NeighbourSolutionGenerator.kt | 16 ++--- .../metaheuristic/tabusearch/TabuSearch.kt | 4 +- 5 files changed, 88 insertions(+), 39 deletions(-) diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index e7343cd..14b1086 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,32 +1,32 @@ # solution for c103_21 2249.872316017405 -D0, C2, C12, S4, C87, C99, C79, D0 -D0, C70, C47, C20, C60, C84, C83, C86, C93, C91, D0 -D0, S12, C97, D0 -D0, C7, C57, D0 -D0, C11, D0 -D0, C37, C22, C53, D0 -D0, C74, C29, C54, D0 -D0, C63, C49, C66, C6, D0 -D0, C44, C16, C27, C28, C8, C32, D0 -D0, C58, D0 -D0, C88, D0 -D0, C92, S20, C19, C75, D0 -D0, C17, C55, C38, C45, C59, D0 -D0, C1, D0 -D0, C33, C52, C89, C30, D0 -D0, C15, S7, D0 -D0, C42, C36, C26, C56, C73, S18, D0 -D0, C95, S20, D0 -D0, C82, C10, C18, C21, C25, C9, C31, C35, D0 -D0, C34, D0 +D0, C20, C21, C22, C24, C29, C30, C26, C7, C3, D0 +D0, C67, C65, C63, C62, C69, C68, C64, C41, C42, C44, D0 +D0, C23, C25, C27, C28, C34, C36, D0 +D0, C75, C1, C2, C4, C6, C9, D0 +D0, C46, C45, C48, C50, C51, C52, C47, C43, D0 +D0, C90, C89, C88, C85, C84, C83, C86, C87, C91, D0 +D0, C74, C72, D0 +D0, C40, C49, D0 +D0, C10, C11, C5, D0 +D0, C66, D0 +D0, C8, D0 +D0, C98, C95, C96, C99, D0 +D0, C32, C31, C33, D0 +D0, C61, D0 +D0, C17, C18, C15, D0 +D0, C59, C55, D0 D0, C14, D0 -D0, C96, C48, C43, D0 -D0, C78, D0 -D0, C80, D0 -D0, C76, S16, C40, C24, C81, C71, C68, C5, C3, D0 -D0, C67, C65, C61, C62, C39, S14, C64, C23, C98, C51, D0 -D0, C50, D0 -D0, C72, C94, C69, D0 -D0, C46, C77, C13, C85, C90, C100, D0 -D0, C4, C41, D0 +D0, C12, D0 +D0, C82, D0 +D0, S12, C39, C38, C37, C35, D0 +D0, S4, C94, C93, C92, C97, C100, D0 +D0, C13, D0 +D0, C19, D0 +D0, S7, C16, D0 +D0, C57, D0 +D0, S16, C53, C56, C54, D0 +D0, S14, C60, C58, D0 +D0, S20, C78, C79, C77, C76, C81, D0 +D0, S18, C80, D0 +D0, S20, C73, C70, C71, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt index 5aa6422..c0bbaff 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt @@ -1,6 +1,7 @@ package at.ac.tuwien.otl.evrptw.dto import java.io.File +import java.util.stream.Collectors /** *

About this class

@@ -16,6 +17,52 @@ data class EVRPTWSolution( val routes: MutableList>, var cost: Double ) { + /** + * Copy constructor. + */ + constructor(solution: EVRPTWSolution) : this(solution.instance, solution.routes + .stream() + .map { + it + .stream() + .map { node -> + when (node) { + is EVRPTWInstance.Depot -> EVRPTWInstance.Depot( + node.id, + node.name, + node.location.x, + node.location.y, + node.timeWindow.start, + node.timeWindow.end + ) + is EVRPTWInstance.Customer -> EVRPTWInstance.Customer( + node.id, + node.name, + node.location.x, + node.location.y, + node.timeWindow.start, + node.timeWindow.end, + node.demand, + node.serviceTime + ) + else -> EVRPTWInstance.RechargingStation( + (node as EVRPTWInstance.RechargingStation).id, + node.name, + node.location.x, + node.location.y, + node.timeWindow.start, + node.timeWindow.end, + node.rechargingRate + ) + } + } + .collect(Collectors.toList()) + .toMutableList() + } + .collect(Collectors.toList()) + .toMutableList(), + solution.cost) + fun writeToFile() { File("solutions/" + instance.name + "_sol.txt").bufferedWriter().use { out -> out.write("# solution for " + instance.name) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 10c164f..891bdb3 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -5,7 +5,6 @@ import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS import at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch.TabuSearch -import java.util.Random import java.util.logging.Logger /** @@ -19,7 +18,6 @@ import java.util.logging.Logger */ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHeuristic { private val log: Logger = Logger.getLogger(this.javaClass.name) - private val random: Random = Random(123456) private val neighbourSolutionGenerator = NeighbourSolutionGenerator() private val tabuSearch = TabuSearch() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt index 40023c0..da6b0e2 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt @@ -4,7 +4,7 @@ import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.dto.ExchangeSequence import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure -import java.util.Random +import java.util.* /** *

About this class

@@ -20,18 +20,20 @@ class NeighbourSolutionGenerator { private val random = Random(123456) fun generateRandomPoint(solution: EVRPTWSolution, neighbour: Int): EVRPTWSolution { + val result = EVRPTWSolution(solution) // we need a fresh very deep copy + val routes: MutableList> = mutableListOf() val neighbourhoodStructure = NeighbourhoodStructure.STRUCTURES[neighbour]!! - if (solution.routes.size < neighbourhoodStructure.numberOfInvolvedRoutes) { + if (result.routes.size < neighbourhoodStructure.numberOfInvolvedRoutes) { throw RuntimeException("ERROR: fewer routes than involved routes") } for (i in 0 until neighbourhoodStructure.numberOfInvolvedRoutes) { - val index = random.nextInt(solution.routes.size) + val index = random.nextInt(result.routes.size) - routes.add(solution.routes[index]) - solution.routes.removeAt(index) + routes.add(result.routes[index]) + result.routes.removeAt(index) } val exchangeSequences = mutableListOf() @@ -67,8 +69,8 @@ class NeighbourSolutionGenerator { routes[i].addAll(startIndex, sequence) } - solution.routes.addAll(routes) + result.routes.addAll(routes) - return solution + return result } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 2f9a194..283cddb 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -15,10 +15,11 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU class TabuSearch { fun apply(solution: EVRPTWSolution): EVRPTWSolution { - var overallBestSolution = solution + var overallBestSolution = EVRPTWSolution(solution) // create deep copy var bestCandidate = overallBestSolution val tabuList = mutableListOf(bestCandidate) // add incoming solution to prevent its reversal var iteration = 0 + while (iteration < N_TABU) { bestCandidate = bestSolutionOfNeighbourhood(bestCandidate, tabuList) tabuList.add(bestCandidate) @@ -28,6 +29,7 @@ class TabuSearch { } iteration++ } + return overallBestSolution } From 8deb1180a80f98120b8837764c9ba2f3b342df79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Thu, 21 Jun 2018 23:40:20 +0200 Subject: [PATCH 06/36] remove wildcard import --- .../otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt index da6b0e2..a87685b 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt @@ -4,7 +4,7 @@ import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.dto.ExchangeSequence import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure -import java.util.* +import java.util.Random /** *

About this class

From 8d83618c85fe70c1f24fec54586b3f2f2286932f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Fri, 22 Jun 2018 16:34:37 +0200 Subject: [PATCH 07/36] add apache commons lang dependency s.t. equals methods can be generated in original java classes; introduce neighbourhood explorers; adjust TabuSearch; rename NeighbourSolutionGenerator to ShakingNeighbourSolutionGenerator; extend calculateTotalCost of the verifier by the "detailedMode" flag --- build.gradle | 1 + .../tuwien/otl/evrptw/dto/EVRPTWInstance.java | 235 ++++++++++++++++++ .../tuwien/otl/evrptw/dto/EVRPTWSolution.kt | 2 +- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 4 +- ...t => ShakingNeighbourSolutionGenerator.kt} | 2 +- .../neighbourhood/INeighbourhoodExplorer.kt | 24 ++ .../neighbourhood/StationInReExplorer.kt | 20 ++ .../TwoOptArcExchangeExplorer.kt | 32 +++ .../metaheuristic/tabusearch/TabuSearch.kt | 48 +++- .../evrptw/verifier/EVRPTWRouteVerifier.java | 4 +- 10 files changed, 356 insertions(+), 16 deletions(-) rename src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/{NeighbourSolutionGenerator.kt => ShakingNeighbourSolutionGenerator.kt} (98%) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/INeighbourhoodExplorer.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt diff --git a/build.gradle b/build.gradle index e16ae2b..8b8d0de 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,7 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" ktlint "com.github.shyiko:ktlint:0.23.0" compile fileTree(dir: 'libs', include: '*.jar') + compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.0.3' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.0.3' diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWInstance.java b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWInstance.java index 5f3d01c..65b86e7 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWInstance.java +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWInstance.java @@ -17,6 +17,9 @@ package at.ac.tuwien.otl.evrptw.dto; import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance.Node.TimeWindow; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -186,6 +189,40 @@ public double getRechargingRate(Node node) { return rechargingStations.get(node.id - 1).rechargingRate; } + @Override + public boolean equals(final Object o) { + if (this == o) return true; + + if (!(o instanceof EVRPTWInstance)) return false; + + final EVRPTWInstance that = (EVRPTWInstance) o; + + return new EqualsBuilder() + .append(getName(), that.getName()) + .append(getDepot(), that.getDepot()) + .append(getCustomers(), that.getCustomers()) + .append(getCustomerMap(), that.getCustomerMap()) + .append(getRechargingStations(), that.getRechargingStations()) + .append(getRechargingStationMap(), that.getRechargingStationMap()) + .append(getNodes(), that.getNodes()) + .append(getVehicleType(), that.getVehicleType()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(getName()) + .append(getDepot()) + .append(getCustomers()) + .append(getCustomerMap()) + .append(getRechargingStations()) + .append(getRechargingStationMap()) + .append(getNodes()) + .append(getVehicleType()) + .toHashCode(); + } + public static class Node { final int id; @@ -197,6 +234,26 @@ public int getId() { this.id = id; } + @Override + public boolean equals(final Object o) { + if (this == o) return true; + + if (!(o instanceof Node)) return false; + + final Node node = (Node) o; + + return new EqualsBuilder() + .append(getId(), node.getId()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(getId()) + .toHashCode(); + } + public static class Location { final double x, y; @@ -212,6 +269,28 @@ public double getX() { public double getY() { return y; } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + + if (!(o instanceof Location)) return false; + + final Location location = (Location) o; + + return new EqualsBuilder() + .append(getX(), location.getX()) + .append(getY(), location.getY()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(getX()) + .append(getY()) + .toHashCode(); + } } public static class TimeWindow { @@ -229,6 +308,28 @@ public double getStart() { public double getEnd() { return end; } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + + if (!(o instanceof TimeWindow)) return false; + + final TimeWindow that = (TimeWindow) o; + + return new EqualsBuilder() + .append(getStart(), that.getStart()) + .append(getEnd(), that.getEnd()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(getStart()) + .append(getEnd()) + .toHashCode(); + } } } @@ -270,6 +371,36 @@ public double getServiceTime() { return serviceTime; } + @Override + public boolean equals(final Object o) { + if (this == o) return true; + + if (!(o instanceof Customer)) return false; + + final Customer customer = (Customer) o; + + return new EqualsBuilder() + .appendSuper(super.equals(o)) + .append(getDemand(), customer.getDemand()) + .append(getServiceTime(), customer.getServiceTime()) + .append(getName(), customer.getName()) + .append(getLocation(), customer.getLocation()) + .append(getTimeWindow(), customer.getTimeWindow()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .appendSuper(super.hashCode()) + .append(getName()) + .append(getLocation()) + .append(getDemand()) + .append(getTimeWindow()) + .append(getServiceTime()) + .toHashCode(); + } + @Override public String toString() { return name; @@ -300,6 +431,32 @@ public TimeWindow getTimeWindow() { return timeWindow; } + @Override + public boolean equals(final Object o) { + if (this == o) return true; + + if (!(o instanceof Depot)) return false; + + final Depot depot = (Depot) o; + + return new EqualsBuilder() + .appendSuper(super.equals(o)) + .append(getName(), depot.getName()) + .append(getLocation(), depot.getLocation()) + .append(getTimeWindow(), depot.getTimeWindow()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .appendSuper(super.hashCode()) + .append(getName()) + .append(getLocation()) + .append(getTimeWindow()) + .toHashCode(); + } + @Override public String toString() { return name; @@ -337,6 +494,34 @@ public double getRechargingRate() { return rechargingRate; } + @Override + public boolean equals(final Object o) { + if (this == o) return true; + + if (!(o instanceof RechargingStation)) return false; + + final RechargingStation that = (RechargingStation) o; + + return new EqualsBuilder() + .appendSuper(super.equals(o)) + .append(getRechargingRate(), that.getRechargingRate()) + .append(getName(), that.getName()) + .append(getLocation(), that.getLocation()) + .append(getTimeWindow(), that.getTimeWindow()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .appendSuper(super.hashCode()) + .append(getName()) + .append(getLocation()) + .append(getTimeWindow()) + .append(getRechargingRate()) + .toHashCode(); + } + @Override public String toString() { return name; @@ -351,6 +536,28 @@ private VehicleType(int id, String name) { this.id = id; this.name = name; } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + + if (!(o instanceof VehicleType)) return false; + + final VehicleType that = (VehicleType) o; + + return new EqualsBuilder() + .append(id, that.id) + .append(name, that.name) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(id) + .append(name) + .toHashCode(); + } } public static class BEVehicleType extends VehicleType { @@ -381,5 +588,33 @@ public double getEnergyConsumption() { public double getFixedCosts() { return fixedCosts; } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + + if (!(o instanceof BEVehicleType)) return false; + + final BEVehicleType that = (BEVehicleType) o; + + return new EqualsBuilder() + .appendSuper(super.equals(o)) + .append(getEnergyCapacity(), that.getEnergyCapacity()) + .append(getEnergyConsumption(), that.getEnergyConsumption()) + .append(getLoadCapacity(), that.getLoadCapacity()) + .append(getFixedCosts(), that.getFixedCosts()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .appendSuper(super.hashCode()) + .append(getEnergyCapacity()) + .append(getEnergyConsumption()) + .append(getLoadCapacity()) + .append(getFixedCosts()) + .toHashCode(); + } } } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt index c0bbaff..a3a8391 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt @@ -13,7 +13,7 @@ import java.util.stream.Collectors * @since 0.1.0 */ data class EVRPTWSolution( - private val instance: EVRPTWInstance, + val instance: EVRPTWInstance, val routes: MutableList>, var cost: Double ) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 891bdb3..78bb334 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -18,8 +18,8 @@ import java.util.logging.Logger */ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHeuristic { private val log: Logger = Logger.getLogger(this.javaClass.name) - private val neighbourSolutionGenerator = NeighbourSolutionGenerator() - private val tabuSearch = TabuSearch() + private val neighbourSolutionGenerator = ShakingNeighbourSolutionGenerator() + private val tabuSearch = TabuSearch(logEnabled) override fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution { var bestSolution = evrptwSolution diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt similarity index 98% rename from src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt rename to src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt index a87685b..9656a2c 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/NeighbourSolutionGenerator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt @@ -15,7 +15,7 @@ import java.util.Random * @version 1.0.0 * @since 1.0.0 */ -class NeighbourSolutionGenerator { +class ShakingNeighbourSolutionGenerator { private val random = Random(123456) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/INeighbourhoodExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/INeighbourhoodExplorer.kt new file mode 100644 index 0000000..f743bc6 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/INeighbourhoodExplorer.kt @@ -0,0 +1,24 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +interface INeighbourhoodExplorer { + + /** + * Explores (enumerates) every possible solution in the search space + * of this neighbourhood based on the initial solution and returns them + * in a list. + * + * @param initialSolution an initial solution for which the neighbours should + * be calculated + * @return a list of neighbour solutions, not necessarily sorted + */ + fun exploreEverySolution(initialSolution: T): List +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt new file mode 100644 index 0000000..b2a995f --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt @@ -0,0 +1,20 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class StationInReExplorer : INeighbourhoodExplorer { + + override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { + // inserts or removes recharging stations between nodes + return listOf(EVRPTWSolution(initialSolution)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt new file mode 100644 index 0000000..3d4f976 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt @@ -0,0 +1,32 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import java.util.Random + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class TwoOptArcExchangeExplorer : + INeighbourhoodExplorer { + + private val random = Random(java.lang.Double.doubleToLongBits(Math.random())) + + override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { + // .... + return listOf(EVRPTWSolution(initialSolution)) + } + + private fun calculateTotalCostBasedOnOtherSolution( + baselineSolution: EVRPTWSolution, + newSolution: EVRPTWSolution + ): Double { + // ... + return baselineSolution.cost + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 283cddb..d9329be 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -2,6 +2,10 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.StationInReExplorer +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExplorer +import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier +import java.util.logging.Logger /** *

About this class

@@ -12,36 +16,60 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU * @version 1.0.0 * @since 1.0.0 */ -class TabuSearch { +class TabuSearch(private val logEnabled: Boolean = true) { + private val log: Logger = Logger.getLogger(this.javaClass.name) + private val explorers = listOf(TwoOptArcExchangeExplorer(), StationInReExplorer()) fun apply(solution: EVRPTWSolution): EVRPTWSolution { var overallBestSolution = EVRPTWSolution(solution) // create deep copy var bestCandidate = overallBestSolution - val tabuList = mutableListOf(bestCandidate) // add incoming solution to prevent its reversal + val tabuMap = mutableMapOf(bestCandidate to 1) // add incoming solution to prevent its reversal var iteration = 0 while (iteration < N_TABU) { - bestCandidate = bestSolutionOfNeighbourhood(bestCandidate, tabuList) - tabuList.add(bestCandidate) - refreshTabuList(tabuList) + bestCandidate = bestSolutionOfNeighbourhoods(bestCandidate, tabuMap) + updateTabuMap(bestCandidate, tabuMap) + if (evaluateSolution(bestCandidate) < evaluateSolution(overallBestSolution)) { overallBestSolution = bestCandidate } iteration++ } - + log("Tabu search done") return overallBestSolution } - private fun bestSolutionOfNeighbourhood(solution: EVRPTWSolution, tabuList: List): EVRPTWSolution { - return solution + private fun bestSolutionOfNeighbourhoods( + solution: EVRPTWSolution, + tabuList: Map + ): EVRPTWSolution { + val solutionsOfAllNeighbourhoods = mutableListOf() + for (explorer in explorers) { + solutionsOfAllNeighbourhoods.addAll(explorer.exploreEverySolution(solution)) + } + return solutionsOfAllNeighbourhoods + .filter { !tabuList.contains(it) } // todo we should accept a "tabu" solution if it is feasible + .sortedBy { it.cost }[0] } - private fun refreshTabuList(list: MutableList) { + private fun updateTabuMap(solution: EVRPTWSolution, tabuMap: MutableMap) { // remove elements older than some constant + // don't forget to consider aspiration criteria + tabuMap + .filter { it.value >= 15 } + .keys.toList() + .forEach { tabuMap.remove(it) } + tabuMap[solution] = 0 + tabuMap.entries.forEach { tabuMap[it.key] = it.value + 1 } } private fun evaluateSolution(solution: EVRPTWSolution): Double { - return 42.0 + return EVRPTWRouteVerifier(solution.instance).calculateTotalCost(solution.routes, false) + } + + private fun log(message: String) { + if (logEnabled) { + log.info(message) + } } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java b/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java index b131847..f19327e 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java @@ -47,10 +47,10 @@ public boolean verify(List> routes, double cost, boolean detailedMode return Double.isFinite(c); } - public double calculateTotalCost(List> routes) { + public double calculateTotalCost(List> routes, boolean detailedMode) { double c = 0.0; for(int i = 0; i < routes.size(); i++) { - c = c + calculate(routes.get(i), i + 1, true); + c = c + calculate(routes.get(i), i + 1, detailedMode); } return c; } From d1846e02f2eb044e2ffd972b7850e0a7cbbf8843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Fri, 22 Jun 2018 18:10:49 +0200 Subject: [PATCH 08/36] implement 2opt arc exchange logic --- solutions/c204_21_sol.txt | 26 ++++----- solutions/r102_21_sol.txt | 18 +++--- solutions/r107_21_sol.txt | 4 +- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 4 +- .../TwoOptArcExchangeExplorer.kt | 55 +++++++++++++++++-- .../metaheuristic/tabusearch/TabuSearch.kt | 3 +- 6 files changed, 78 insertions(+), 32 deletions(-) diff --git a/solutions/c204_21_sol.txt b/solutions/c204_21_sol.txt index 57dc2b1..4f6a4d0 100644 --- a/solutions/c204_21_sol.txt +++ b/solutions/c204_21_sol.txt @@ -1,17 +1,17 @@ # solution for c204_21 -1553.1684430384391 +1572.2854629371848 D0, C93, C5, C75, C2, C1, C99, C100, C97, C95, C94, C98, C7, C3, C4, C89, C91, C90, D0 -D0, C20, C22, C24, C30, C25, C9, C11, C13, C17, C19, C23, D0 +D0, C20, C22, C24, C30, C25, C9, C11, C13, C17, C19, C18, C23, D0 D0, C48, C43, C42, C41, C45, C50, C51, C47, C46, C44, C57, C55, C54, C59, D0 -D0, C67, C63, C62, C66, C69, C68, C65, C64, C61, C74, C87, C77, C88, D0 -D0, C8, C10, C12, C26, C28, C29, C27, D0 -D0, C84, C78, C85, C86, D0 -D0, C49, C53, C56, C58, C60, C52, D0 -D0, C34, C36, C37, C35, C33, C39, C38, C31, C32, C6, D0 -D0, C76, C71, C79, C96, D0 -D0, C73, C70, C81, D0 -D0, C83, C82, C92, D0 -D0, C15, C14, C16, C18, D0 -D0, C40, C72, D0 -D0, C80, D0 +D0, C8, C10, C12, C14, C16, C26, C28, C29, C27, D0 +D0, C67, C63, C62, C66, C69, C64, C61, C74, C68, C65, C49, C40, D0 +D0, C87, C77, C78, C85, C76, C81, C96, D0 +D0, C52, C33, C34, C36, C37, C35, C38, C31, C32, C6, D0 +D0, C88, C84, C86, C83, C82, C72, D0 +D0, C15, C39, D0 D0, C21, D0 +D0, C56, C58, C60, C53, D0 +D0, C92, D0 +D0, C73, C70, C79, D0 +D0, C71, D0 +D0, C80, D0 diff --git a/solutions/r102_21_sol.txt b/solutions/r102_21_sol.txt index 52d9457..df8dee1 100644 --- a/solutions/r102_21_sol.txt +++ b/solutions/r102_21_sol.txt @@ -1,23 +1,23 @@ # solution for r102_21 -2649.679439695821 +2635.1075915695646 D0, C53, C28, C40, C21, C2, C13, D0 -D0, C94, C97, C98, C93, C100, C91, C59, D0 -D0, C89, C18, C60, C5, C99, C27, D0 -D0, C96, C6, C95, C87, D0 +D0, C94, C97, C98, C100, C93, C5, D0 +D0, C89, C18, C60, C84, C83, D0 +D0, C96, C59, C92, C95, C87, D0 D0, C1, C70, C10, C52, D0 -D0, C31, C62, C7, D0 +D0, C31, C62, C7, C27, D0 D0, C58, C22, C75, D0 +D0, C6, C99, C26, D0 D0, C3, C81, C33, C50, D0 D0, C74, C56, C72, D0 D0, C68, C24, C80, D0 -D0, C61, C16, C85, C92, D0 -D0, C8, C45, C83, D0 -D0, C69, C26, D0 +D0, C61, C16, C91, C85, D0 +D0, C8, C45, D0 +D0, C69, D0 D0, C55, C54, D0 D0, S4, C9, C34, C78, C29, D0 D0, S19, C25, C4, C73, D0 D0, S8, C47, C36, C48, C82, D0 -D0, C84, D0 D0, C12, D0 D0, C79, C77, D0 D0, C76, D0 diff --git a/solutions/r107_21_sol.txt b/solutions/r107_21_sol.txt index 7ebe9cb..43b36d1 100644 --- a/solutions/r107_21_sol.txt +++ b/solutions/r107_21_sol.txt @@ -15,8 +15,8 @@ D0, C45, C8, D0 D0, C7, C19, D0 D0, C55, C4, D0 D0, C14, C99, D0 -D0, S19, C25, C67, D0 D0, S7, C11, C63, C32, C90, D0 +D0, S19, C25, C67, D0 D0, S14, C43, C15, C42, C87, C57, D0 D0, S8, C47, C49, D0 D0, C12, D0 @@ -24,8 +24,8 @@ D0, C56, D0 D0, C79, C78, D0 D0, C20, D0 D0, C76, D0 -D0, S4, C34, C35, D0 D0, S17, C23, C39, C41, D0 +D0, S4, C34, C35, D0 D0, C17, D0 D0, C37, D0 D0, C29, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 4ccbaf7..2a4c43d 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -52,7 +52,9 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - runAlgorithmOnInstance(0, false) + for (i in 0 until 1) { + runAlgorithmOnInstance(i, false) + } } private fun runAlgorithmOnInstance(instanceId: Int, detailed: Boolean): Long { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt index 3d4f976..c524f46 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt @@ -1,7 +1,6 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution -import java.util.Random /** *

About this class

@@ -15,11 +14,57 @@ import java.util.Random class TwoOptArcExchangeExplorer : INeighbourhoodExplorer { - private val random = Random(java.lang.Double.doubleToLongBits(Math.random())) - override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { - // .... - return listOf(EVRPTWSolution(initialSolution)) + val result = mutableListOf() + val currentSolution = EVRPTWSolution(initialSolution) + for (routeIndex in 0 until currentSolution.routes.size) { + val route = currentSolution.routes[routeIndex] + + for (secondRouteIndex in (routeIndex + 1) until currentSolution.routes.size) { + val secondRoute = currentSolution.routes[secondRouteIndex] + for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot + for (nodeOfSecondRoute in 1 until secondRoute.size - 1) { + val neighbourSolution = performTwoOptExchange( + currentSolution, + routeIndex, + nodeOfFirstRoute, + secondRouteIndex, + nodeOfSecondRoute + ) + result.add(neighbourSolution) + } + } + } + } + return result + } + + private fun performTwoOptExchange( + solution: EVRPTWSolution, + firstRouteIndex: Int, + firstRouteNodeIndex: Int, + secondRouteIndex: Int, + secondRouteNoteIndex: Int + ): EVRPTWSolution { + val result = EVRPTWSolution(solution) + val firstRoute = result.routes[firstRouteIndex] + val secondRoute = result.routes[secondRouteIndex] + + val newFirstRoute = firstRoute.subList(0, firstRouteNodeIndex + 1).toList() + secondRoute.subList( + secondRouteNoteIndex, + secondRoute.size + ).toList() + val newSecondRoute = secondRoute.subList(0, secondRouteNoteIndex).toList() + firstRoute.subList( + firstRouteNodeIndex + 1, + firstRoute.size + ).toList() + + result.routes[firstRouteIndex].clear() + result.routes[firstRouteIndex].addAll(newFirstRoute) + result.routes[secondRouteIndex].clear() + result.routes[secondRouteIndex].addAll(newSecondRoute) + // TODO maybe recalculate costs, violations here? + return result } private fun calculateTotalCostBasedOnOtherSolution( diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index d9329be..2f0168d 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -2,7 +2,6 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU -import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.StationInReExplorer import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExplorer import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import java.util.logging.Logger @@ -18,7 +17,7 @@ import java.util.logging.Logger */ class TabuSearch(private val logEnabled: Boolean = true) { private val log: Logger = Logger.getLogger(this.javaClass.name) - private val explorers = listOf(TwoOptArcExchangeExplorer(), StationInReExplorer()) + private val explorers = listOf(TwoOptArcExchangeExplorer()) fun apply(solution: EVRPTWSolution): EVRPTWSolution { var overallBestSolution = EVRPTWSolution(solution) // create deep copy From bafb1049b7ac59e7273f5fd5c733934748e8e7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sat, 23 Jun 2018 19:32:43 +0200 Subject: [PATCH 09/36] implement naive cost function --- .../tuwien/otl/evrptw/dto/EVRPTWSolution.kt | 73 ++++++++++++- .../at/ac/tuwien/otl/evrptw/dto/Route.kt | 13 +++ .../otl/evrptw/metaheuristic/Constants.kt | 3 + .../ShakingNeighbourSolutionGenerator.kt | 11 +- .../metaheuristic/tabusearch/TabuSearch.kt | 2 +- .../evrptw/verifier/EVRPTWRouteVerifier.java | 102 ++++++++++++++++-- 6 files changed, 187 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt index a3a8391..bd099ba 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt @@ -1,5 +1,9 @@ package at.ac.tuwien.otl.evrptw.dto +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.ALPHA +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.BETA +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.GAMMA +import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import java.io.File import java.util.stream.Collectors @@ -15,8 +19,65 @@ import java.util.stream.Collectors data class EVRPTWSolution( val instance: EVRPTWInstance, val routes: MutableList>, - var cost: Double + val cost: Double ) { + + val fitnessValue: FitnessValue = calculateFitnessValue() + + private fun calculateFitnessValue(): FitnessValue { + return FitnessValue( + cost, + calculateTotalCapacityViolation(), + calculateTotalTimeWindowViolation(), + calculateTotalBatteryCapacityViolation() + ) + } + + private fun calculateTotalCapacityViolation(): Double { + var result = 0.0 + + for (route in routes) { + val demandSum = route.stream().filter { it is EVRPTWInstance.Customer } + .mapToDouble { (it as EVRPTWInstance.Customer).demand }.sum() + result += Math.max(demandSum - instance.vehicleCapacity, 0.0) + } + + return result + } + + private fun calculateTotalTimeWindowViolation(): Double { + return EVRPTWRouteVerifier.calculateTotalTimeWindowViolation(instance, routes) + } + + private fun calculateTotalBatteryCapacityViolation(): Double { + var result = 0.0 + + for (route in routes) { + var batteryViolation = 0.0 + var lastNotCustomerIndex = 0 + for (nodeIndex in 0 until route.size) { + if (route[nodeIndex] is EVRPTWInstance.Depot || route[nodeIndex] is EVRPTWInstance.RechargingStation) { + val currentViolation = batteryDemandTo(route, lastNotCustomerIndex, nodeIndex) + batteryViolation += Math.max(currentViolation - instance.vehicleEnergyCapacity, 0.0) + lastNotCustomerIndex = nodeIndex + } + } + result += batteryViolation + } + + return result + } + + private fun batteryDemandTo(route: List, startIndex: Int, nodeIndex: Int): Double { + if (startIndex == nodeIndex) { + return 0.0 + } + return batteryDemandTo(route, startIndex, nodeIndex - 1) + instance.getTravelDistance( + route[nodeIndex - 1], + route[nodeIndex] + ) * instance.vehicleEnergyConsumption + } + /** * Copy constructor. */ @@ -82,3 +143,13 @@ data class EVRPTWSolution( } } } + +data class FitnessValue( + val totalTravelDistance: Double, + val totalCapacityViolation: Double, + val totalTimeWindowViolation: Double, + val totalBatteryCapacityViolation: Double +) { + val fitness = + totalTravelDistance + (ALPHA * totalCapacityViolation) + (BETA * totalTimeWindowViolation) + (GAMMA * totalBatteryCapacityViolation) +} diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Route.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Route.kt index 0ab0187..1c7e695 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Route.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Route.kt @@ -180,4 +180,17 @@ class Route( log.info(message) } } + + companion object { + fun calculateTotalDistance(routes: List>, instance: EVRPTWInstance): Double { + var result = 0.0 + + for (route in routes) { + for (nodeIndex in 0 until route.size - 1) { + result += instance.getTravelDistance(route[nodeIndex], route[nodeIndex + 1]) + } + } + return result + } + } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 5f9258a..5989202 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -19,5 +19,8 @@ class Constants private constructor() { const val COOLING_FACTOR = 0.9 const val TABU_TENURE_MIN = 15 const val TABU_TENURE_MAX = 30 + const val ALPHA = 10 + const val BETA = 10 + const val GAMMA = 10 } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt index 9656a2c..f1d5455 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt @@ -1,9 +1,6 @@ package at.ac.tuwien.otl.evrptw.metaheuristic -import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance -import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution -import at.ac.tuwien.otl.evrptw.dto.ExchangeSequence -import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure +import at.ac.tuwien.otl.evrptw.dto.* import java.util.Random /** @@ -71,6 +68,10 @@ class ShakingNeighbourSolutionGenerator { result.routes.addAll(routes) - return result + return EVRPTWSolution( + result.instance, + result.routes, + Route.calculateTotalDistance(result.routes, result.instance) + ) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 2f0168d..fbd6d99 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -48,7 +48,7 @@ class TabuSearch(private val logEnabled: Boolean = true) { } return solutionsOfAllNeighbourhoods .filter { !tabuList.contains(it) } // todo we should accept a "tabu" solution if it is feasible - .sortedBy { it.cost }[0] + .sortedBy { it.fitnessValue.fitness }.first() } private fun updateTabuMap(solution: EVRPTWSolution, tabuMap: MutableMap) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java b/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java index f19327e..95d69b8 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java @@ -100,8 +100,8 @@ public double calculate(List route, int id, boolean detailedMode) { timeInNode = instance.getServiceTime(arriveAtNode); if(instance.isRechargingStation(arriveAtNode)) { double refuelTime = - (instance.getVehicleEnergyCapacity() - y) * // used fuel - instance.getRechargingRate(arriveAtNode); // refill rate + (instance.getVehicleEnergyCapacity() - y) * // used fuel + instance.getRechargingRate(arriveAtNode); // refill rate timeInNode = refuelTime; @@ -130,14 +130,14 @@ public double calculate(List route, int id, boolean detailedMode) { boolean infeasible = qInf > 0.0 || TW > 0.0 || yInfeasible > 0.0; if(detailedMode) { System.out.println( - String.format(Locale.ENGLISH, - "Route %d: %s" + "\n" + - "- load:\t\t%4.0f" + "\n" + - "- distance:\t%7.3f" + "\n" + - "- duration:\t%7.3f" + "\n" + - "- violations:\t %s", - id, route.toString(), q, distance, D, (infeasible) ? "YES" : "none" - )); + String.format(Locale.ENGLISH, + "Route %d: %s" + "\n" + + "- load:\t\t%4.0f" + "\n" + + "- distance:\t%7.3f" + "\n" + + "- duration:\t%7.3f" + "\n" + + "- violations:\t %s", + id, route.toString(), q, distance, D, (infeasible) ? "YES" : "none" + )); if(infeasible) { if(qInf > 0.0) System.out.println(String.format(Locale.ENGLISH, "-- load:\t%4.0f", qInf)); @@ -151,4 +151,86 @@ public double calculate(List route, int id, boolean detailedMode) { return Double.POSITIVE_INFINITY; else return distance; } + + public static double calculateTotalTimeWindowViolation(final EVRPTWInstance instance, final List> routes) { + double c = 0.0; + for (int i = 0; i < routes.size(); i++) { + c = c + calculateTimeWindowViolation(instance, routes.get(i), i + 1); + } + return c; + } + + private static double calculateTimeWindowViolation(EVRPTWInstance instance, List route, int id) { + int from = 1; + int to = route.size() - 1; + + Node prevNode = route.get(0); + + double q = 0.0; + double distance = 0.0; + + double y = instance.getVehicleEnergyCapacity(); + double yInfeasible = 0.0; + + double ST = instance.getServiceTime(prevNode); + double D = ST; + double TW = 0.0; + double E = instance.getTimewindow(prevNode).getStart(); + double L = instance.getTimewindow(prevNode).getEnd(); + double deltaWT = 0.0; + double deltaTW = 0.0; + + for (int i = from; i <= to; i++) { + Node arriveAtNode = route.get(i); + + double timeInNode = 0.0; + double travelTime = 0.0; + double delta = 0.0; + + travelTime = instance.getTravelTime(prevNode, arriveAtNode); + distance = distance + instance.getTravelDistance(prevNode, arriveAtNode); + + delta = D - TW + travelTime; + + double travelDistance = instance.getTravelDistance(prevNode, arriveAtNode); + y -= (travelDistance * instance.getVehicleEnergyConsumption()); + if (y < 0.0) { + yInfeasible -= y; + y = 0.0; + } + + deltaWT = Math.max(0, instance.getTimewindow(arriveAtNode).getStart() - delta - L); + deltaTW = Math.max(0, E + delta - instance.getTimewindow(arriveAtNode).getEnd()); + + timeInNode = instance.getServiceTime(arriveAtNode); + if (instance.isRechargingStation(arriveAtNode)) { + double refuelTime = + (instance.getVehicleEnergyCapacity() - y) * // used fuel + instance.getRechargingRate(arriveAtNode); // refill rate + + timeInNode = refuelTime; + + y = instance.getVehicleEnergyCapacity(); + } else { + // do nothing + } + + if (instance.isRechargingStation(arriveAtNode)) { + delta += timeInNode; + deltaWT = Math.max(0, instance.getTimewindow(arriveAtNode).getStart() - delta - L); + deltaTW = Math.max(0, E + delta - instance.getTimewindow(arriveAtNode).getEnd()); + } else + q = q + instance.getDemand(arriveAtNode); + + D = D + timeInNode + deltaWT + travelTime; + ST += timeInNode; + TW = TW + deltaTW; + E = Math.max(instance.getTimewindow(arriveAtNode).getStart() - delta, E) - deltaWT; + L = Math.min(instance.getTimewindow(arriveAtNode).getEnd() - delta, L) + deltaTW; + + prevNode = arriveAtNode; + } + + return TW; + } } From 437dea31761d4b6e25b9412b55e34873af3b4bf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sat, 23 Jun 2018 19:56:47 +0200 Subject: [PATCH 10/36] optimize copying of solutions --- .../tuwien/otl/evrptw/dto/EVRPTWSolution.kt | 44 +++++++++++++++++++ .../ShakingNeighbourSolutionGenerator.kt | 18 ++++---- .../TwoOptArcExchangeExplorer.kt | 30 +++++++------ .../metaheuristic/tabusearch/TabuSearch.kt | 3 +- 4 files changed, 71 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt index bd099ba..5535204 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt @@ -142,6 +142,50 @@ data class EVRPTWSolution( } } } + + fun copyOfRoutes(): MutableList> { + return routes + .stream() + .map { + it + .stream() + .map { node -> + when (node) { + is EVRPTWInstance.Depot -> EVRPTWInstance.Depot( + node.id, + node.name, + node.location.x, + node.location.y, + node.timeWindow.start, + node.timeWindow.end + ) + is EVRPTWInstance.Customer -> EVRPTWInstance.Customer( + node.id, + node.name, + node.location.x, + node.location.y, + node.timeWindow.start, + node.timeWindow.end, + node.demand, + node.serviceTime + ) + else -> EVRPTWInstance.RechargingStation( + (node as EVRPTWInstance.RechargingStation).id, + node.name, + node.location.x, + node.location.y, + node.timeWindow.start, + node.timeWindow.end, + node.rechargingRate + ) + } + } + .collect(Collectors.toList()) + .toMutableList() + } + .collect(Collectors.toList()) + .toMutableList() + } } data class FitnessValue( diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt index f1d5455..5297713 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt @@ -17,20 +17,20 @@ class ShakingNeighbourSolutionGenerator { private val random = Random(123456) fun generateRandomPoint(solution: EVRPTWSolution, neighbour: Int): EVRPTWSolution { - val result = EVRPTWSolution(solution) // we need a fresh very deep copy + val resultRoutes = solution.copyOfRoutes() val routes: MutableList> = mutableListOf() val neighbourhoodStructure = NeighbourhoodStructure.STRUCTURES[neighbour]!! - if (result.routes.size < neighbourhoodStructure.numberOfInvolvedRoutes) { + if (resultRoutes.size < neighbourhoodStructure.numberOfInvolvedRoutes) { throw RuntimeException("ERROR: fewer routes than involved routes") } for (i in 0 until neighbourhoodStructure.numberOfInvolvedRoutes) { - val index = random.nextInt(result.routes.size) + val index = random.nextInt(resultRoutes.size) - routes.add(result.routes[index]) - result.routes.removeAt(index) + routes.add(resultRoutes[index]) + resultRoutes.removeAt(index) } val exchangeSequences = mutableListOf() @@ -66,12 +66,12 @@ class ShakingNeighbourSolutionGenerator { routes[i].addAll(startIndex, sequence) } - result.routes.addAll(routes) + resultRoutes.addAll(routes) return EVRPTWSolution( - result.instance, - result.routes, - Route.calculateTotalDistance(result.routes, result.instance) + solution.instance, + resultRoutes, + Route.calculateTotalDistance(resultRoutes, solution.instance) ) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt index c524f46..fd92362 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt @@ -1,6 +1,8 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood +import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.Route /** *

About this class

@@ -16,16 +18,15 @@ class TwoOptArcExchangeExplorer : override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { val result = mutableListOf() - val currentSolution = EVRPTWSolution(initialSolution) - for (routeIndex in 0 until currentSolution.routes.size) { - val route = currentSolution.routes[routeIndex] + for (routeIndex in 0 until initialSolution.routes.size) { + val route = initialSolution.routes[routeIndex] - for (secondRouteIndex in (routeIndex + 1) until currentSolution.routes.size) { - val secondRoute = currentSolution.routes[secondRouteIndex] + for (secondRouteIndex in (routeIndex + 1) until initialSolution.routes.size) { + val secondRoute = initialSolution.routes[secondRouteIndex] for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot for (nodeOfSecondRoute in 1 until secondRoute.size - 1) { val neighbourSolution = performTwoOptExchange( - currentSolution, + initialSolution, routeIndex, nodeOfFirstRoute, secondRouteIndex, @@ -46,9 +47,10 @@ class TwoOptArcExchangeExplorer : secondRouteIndex: Int, secondRouteNoteIndex: Int ): EVRPTWSolution { - val result = EVRPTWSolution(solution) - val firstRoute = result.routes[firstRouteIndex] - val secondRoute = result.routes[secondRouteIndex] + val routes = solution.copyOfRoutes() + + val firstRoute = routes[firstRouteIndex] + val secondRoute = routes[secondRouteIndex] val newFirstRoute = firstRoute.subList(0, firstRouteNodeIndex + 1).toList() + secondRoute.subList( secondRouteNoteIndex, @@ -59,12 +61,12 @@ class TwoOptArcExchangeExplorer : firstRoute.size ).toList() - result.routes[firstRouteIndex].clear() - result.routes[firstRouteIndex].addAll(newFirstRoute) - result.routes[secondRouteIndex].clear() - result.routes[secondRouteIndex].addAll(newSecondRoute) + routes[firstRouteIndex].clear() + routes[firstRouteIndex].addAll(newFirstRoute) + routes[secondRouteIndex].clear() + routes[secondRouteIndex].addAll(newSecondRoute) // TODO maybe recalculate costs, violations here? - return result + return EVRPTWSolution(solution.instance, routes, Route.calculateTotalDistance(routes, solution.instance)) } private fun calculateTotalCostBasedOnOtherSolution( diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index fbd6d99..aa7ec0e 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -31,6 +31,7 @@ class TabuSearch(private val logEnabled: Boolean = true) { if (evaluateSolution(bestCandidate) < evaluateSolution(overallBestSolution)) { overallBestSolution = bestCandidate + log("New local optimum found") } iteration++ } @@ -63,7 +64,7 @@ class TabuSearch(private val logEnabled: Boolean = true) { } private fun evaluateSolution(solution: EVRPTWSolution): Double { - return EVRPTWRouteVerifier(solution.instance).calculateTotalCost(solution.routes, false) + return solution.fitnessValue.fitness } private fun log(message: String) { From 95bbfbc66abbc67367e686cb657a02f81a80989c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sat, 23 Jun 2018 21:53:14 +0200 Subject: [PATCH 11/36] fix 2opt issue where 2 routes of size 3 got changed incorrectly; temporarily accept only feasible solutions in TabuSearch component; --- solutions/c103_21_sol.txt | 47 +++++++------------ .../tuwien/otl/evrptw/dto/EVRPTWSolution.kt | 2 +- .../otl/evrptw/metaheuristic/Constants.kt | 2 +- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 6 ++- .../ShakingNeighbourSolutionGenerator.kt | 1 + .../TwoOptArcExchangeExplorer.kt | 14 ++++-- .../metaheuristic/tabusearch/TabuSearch.kt | 14 +++--- 7 files changed, 43 insertions(+), 43 deletions(-) diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index 14b1086..0665991 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,32 +1,19 @@ # solution for c103_21 -2249.872316017405 -D0, C20, C21, C22, C24, C29, C30, C26, C7, C3, D0 -D0, C67, C65, C63, C62, C69, C68, C64, C41, C42, C44, D0 -D0, C23, C25, C27, C28, C34, C36, D0 -D0, C75, C1, C2, C4, C6, C9, D0 -D0, C46, C45, C48, C50, C51, C52, C47, C43, D0 -D0, C90, C89, C88, C85, C84, C83, C86, C87, C91, D0 -D0, C74, C72, D0 -D0, C40, C49, D0 -D0, C10, C11, C5, D0 -D0, C66, D0 -D0, C8, D0 -D0, C98, C95, C96, C99, D0 -D0, C32, C31, C33, D0 -D0, C61, D0 -D0, C17, C18, C15, D0 -D0, C59, C55, D0 -D0, C14, D0 +1570.591621707613 +D0, C20, C32, C33, C34, C36, C29, C24, C22, C21, D0 +D0, C23, C25, C27, C28, C30, C26, C10, C11, C9, C8, D0 +D0, C90, C89, C88, C85, C84, C82, C83, C86, C87, C91, D0 +D0, C65, C63, C62, C69, C68, C66, D0 +D0, C47, C43, D0 +D0, C7, C3, C4, C6, C5, D0 +D0, C98, C96, C2, C1, C75, D0 +D0, C59, C55, C57, D0 D0, C12, D0 -D0, C82, D0 -D0, S12, C39, C38, C37, C35, D0 -D0, S4, C94, C93, C92, C97, C100, D0 -D0, C13, D0 -D0, C19, D0 -D0, S7, C16, D0 -D0, C57, D0 -D0, S16, C53, C56, C54, D0 -D0, S14, C60, C58, D0 -D0, S20, C78, C79, C77, C76, C81, D0 -D0, S18, C80, D0 -D0, S20, C73, C70, C71, D0 +D0, C14, S7, C16, C19, C17, C18, C15, C13, D0 +D0, C31, S12, C39, C38, C37, C35, D0 +D0, C95, S4, C94, C93, C92, C97, C100, C99, D0 +D0, S14, C60, C58, C56, C53, S16, C54, D0 +D0, S20, C78, C79, C77, D0 +D0, S20, C73, C70, C71, C76, C81, D0 +D0, C46, C80, C41, C42, C44, C45, C48, C50, C51, C52, C49, D0 +D0, C67, C74, S18, C40, C72, C61, C64, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt index 5535204..084d295 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt @@ -46,7 +46,7 @@ data class EVRPTWSolution( } private fun calculateTotalTimeWindowViolation(): Double { - return EVRPTWRouteVerifier.calculateTotalTimeWindowViolation(instance, routes) + return Math.max(EVRPTWRouteVerifier.calculateTotalTimeWindowViolation(instance, routes), 0.0) } private fun calculateTotalBatteryCapacityViolation(): Double { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 5989202..9d311c0 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,7 +12,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 200 + const val N_DIST = 1 const val N_FEAS = 500 const val N_TABU = 100 const val N_PENALTY = 2 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 78bb334..49204a2 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -5,6 +5,7 @@ import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS import at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch.TabuSearch +import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import java.util.logging.Logger /** @@ -52,7 +53,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe i++ } - return evrptwSolution + return bestSolution } private fun acceptSimulatedAnnealing(optimizedNewSolution: EVRPTWSolution, bestSolution: EVRPTWSolution): Boolean { @@ -60,7 +61,8 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe } private fun feasible(solution: EVRPTWSolution): Boolean { - return true + // return solution.fitnessValue.fitness == solution.cost + return EVRPTWRouteVerifier(solution.instance).verify(solution.routes, solution.cost, false) } private fun addVehicle(solution: EVRPTWSolution) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt index 5297713..c028c33 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt @@ -1,5 +1,6 @@ package at.ac.tuwien.otl.evrptw.metaheuristic +/* ktlint-disable no-wildcard-imports */ import at.ac.tuwien.otl.evrptw.dto.* import java.util.Random diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt index fd92362..08ae117 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt @@ -1,6 +1,5 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood -import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.dto.Route @@ -62,9 +61,18 @@ class TwoOptArcExchangeExplorer : ).toList() routes[firstRouteIndex].clear() - routes[firstRouteIndex].addAll(newFirstRoute) + if (newFirstRoute.size > 2) { + routes[firstRouteIndex].addAll(newFirstRoute) + } else { + routes.removeAt(firstRouteIndex) + } + routes[secondRouteIndex].clear() - routes[secondRouteIndex].addAll(newSecondRoute) + if (newSecondRoute.size > 2) { + routes[secondRouteIndex].addAll(newSecondRoute) + } else { + routes.removeAt(secondRouteIndex) + } // TODO maybe recalculate costs, violations here? return EVRPTWSolution(solution.instance, routes, Route.calculateTotalDistance(routes, solution.instance)) } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index aa7ec0e..cfaa87a 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -3,7 +3,6 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExplorer -import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import java.util.logging.Logger /** @@ -31,7 +30,7 @@ class TabuSearch(private val logEnabled: Boolean = true) { if (evaluateSolution(bestCandidate) < evaluateSolution(overallBestSolution)) { overallBestSolution = bestCandidate - log("New local optimum found") + log("New local optimum found. Cost: ${overallBestSolution.cost}") } iteration++ } @@ -41,15 +40,18 @@ class TabuSearch(private val logEnabled: Boolean = true) { private fun bestSolutionOfNeighbourhoods( solution: EVRPTWSolution, - tabuList: Map + tabuMap: Map ): EVRPTWSolution { val solutionsOfAllNeighbourhoods = mutableListOf() for (explorer in explorers) { solutionsOfAllNeighbourhoods.addAll(explorer.exploreEverySolution(solution)) } - return solutionsOfAllNeighbourhoods - .filter { !tabuList.contains(it) } // todo we should accept a "tabu" solution if it is feasible - .sortedBy { it.fitnessValue.fitness }.first() + val list = solutionsOfAllNeighbourhoods + .filter { !tabuMap.contains(it) && it.fitnessValue.fitness == it.cost } // todo we should accept a "tabu" solution if it is feasible + if (list.isEmpty()) { + return solution + } + return list.sortedBy { it.fitnessValue.fitness }.first() } private fun updateTabuMap(solution: EVRPTWSolution, tabuMap: MutableMap) { From 134cb161ecb55f8b30b07ead7ea72baf416bc996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 00:37:47 +0200 Subject: [PATCH 12/36] use route verifier to calculate violations --- solutions/c103_21_sol.txt | 6 +- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../tuwien/otl/evrptw/dto/EVRPTWSolution.kt | 20 +++- .../otl/evrptw/metaheuristic/Constants.kt | 4 +- .../TwoOptArcExchangeExplorer.kt | 16 ++-- .../evrptw/verifier/EVRPTWRouteVerifier.java | 91 ++++++++++++++++++- 6 files changed, 119 insertions(+), 20 deletions(-) diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index 0665991..f5aab76 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,7 +1,9 @@ # solution for c103_21 -1570.591621707613 +1459.9599355360504 D0, C20, C32, C33, C34, C36, C29, C24, C22, C21, D0 +D0, C67, C74, S18, C80, C72, C61, C64, D0 D0, C23, C25, C27, C28, C30, C26, C10, C11, C9, C8, D0 +D0, C46, C40, C41, C42, C44, C45, C48, C50, C51, C52, C49, D0 D0, C90, C89, C88, C85, C84, C82, C83, C86, C87, C91, D0 D0, C65, C63, C62, C69, C68, C66, D0 D0, C47, C43, D0 @@ -15,5 +17,3 @@ D0, C95, S4, C94, C93, C92, C97, C100, C99, D0 D0, S14, C60, C58, C56, C53, S16, C54, D0 D0, S20, C78, C79, C77, D0 D0, S20, C73, C70, C71, C76, C81, D0 -D0, C46, C80, C41, C42, C44, C45, C48, C50, C51, C52, C49, D0 -D0, C67, C74, S18, C40, C72, C61, C64, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 2a4c43d..db6a4fd 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -52,7 +52,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 0 until 1) { + for (i in 1 until 2) { runAlgorithmOnInstance(i, false) } } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt index 084d295..0d50b66 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt @@ -25,11 +25,12 @@ data class EVRPTWSolution( val fitnessValue: FitnessValue = calculateFitnessValue() private fun calculateFitnessValue(): FitnessValue { + val violations = EVRPTWRouteVerifier.calculateViolations(instance, routes) return FitnessValue( cost, - calculateTotalCapacityViolation(), - calculateTotalTimeWindowViolation(), - calculateTotalBatteryCapacityViolation() + violations.capacityViolation, + violations.timeWindowViolation, + violations.batteryCapacityViolation ) } @@ -37,8 +38,11 @@ data class EVRPTWSolution( var result = 0.0 for (route in routes) { - val demandSum = route.stream().filter { it is EVRPTWInstance.Customer } - .mapToDouble { (it as EVRPTWInstance.Customer).demand }.sum() + val demandSum = route + .stream() + .filter { it is EVRPTWInstance.Customer } + .mapToDouble { (it as EVRPTWInstance.Customer).demand } + .sum() result += Math.max(demandSum - instance.vehicleCapacity, 0.0) } @@ -197,3 +201,9 @@ data class FitnessValue( val fitness = totalTravelDistance + (ALPHA * totalCapacityViolation) + (BETA * totalTimeWindowViolation) + (GAMMA * totalBatteryCapacityViolation) } + +data class Violations( + val capacityViolation: Double, + val timeWindowViolation: Double, + val batteryCapacityViolation: Double +) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 9d311c0..0756b59 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,8 +12,8 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 1 - const val N_FEAS = 500 + const val N_DIST = 0 + const val N_FEAS = 5 const val N_TABU = 100 const val N_PENALTY = 2 const val COOLING_FACTOR = 0.9 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt index 08ae117..f309a00 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt @@ -61,18 +61,18 @@ class TwoOptArcExchangeExplorer : ).toList() routes[firstRouteIndex].clear() - if (newFirstRoute.size > 2) { - routes[firstRouteIndex].addAll(newFirstRoute) - } else { - routes.removeAt(firstRouteIndex) - } + routes[firstRouteIndex].addAll(newFirstRoute) routes[secondRouteIndex].clear() - if (newSecondRoute.size > 2) { - routes[secondRouteIndex].addAll(newSecondRoute) - } else { + routes[secondRouteIndex].addAll(newSecondRoute) + + if (newFirstRoute.size <= 2) { + routes.removeAt(firstRouteIndex) + } + if (newSecondRoute.size <= 2) { routes.removeAt(secondRouteIndex) } + // TODO maybe recalculate costs, violations here? return EVRPTWSolution(solution.instance, routes, Route.calculateTotalDistance(routes, solution.instance)) } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java b/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java index 95d69b8..5a667bd 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java @@ -18,6 +18,8 @@ import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance; import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance.Node; +import at.ac.tuwien.otl.evrptw.dto.Violations; + import java.util.List; import java.util.Locale; @@ -230,7 +232,94 @@ private static double calculateTimeWindowViolation(EVRPTWInstance instance, List prevNode = arriveAtNode; } - return TW; } + + public static Violations calculateViolations(final EVRPTWInstance instance, final List> routes) { + double capacityViolations = 0.0; + double timeWindowViolations = 0.0; + double batteryCapacityViolations = 0.0; + for (int i = 0; i < routes.size(); i++) { + final Violations violations = calculateViolationOfRoute(instance, routes.get(i)); + capacityViolations += violations.getCapacityViolation(); + timeWindowViolations += violations.getTimeWindowViolation(); + batteryCapacityViolations += violations.getBatteryCapacityViolation(); + } + return new Violations(capacityViolations, timeWindowViolations, batteryCapacityViolations); + } + + private static Violations calculateViolationOfRoute(EVRPTWInstance instance, List route) { + int from = 1; + int to = route.size() - 1; + + Node prevNode = route.get(0); + + double q = 0.0; + double distance = 0.0; + + double y = instance.getVehicleEnergyCapacity(); + double yInfeasible = 0.0; + + double ST = instance.getServiceTime(prevNode); + double D = ST; + double TW = 0.0; + double E = instance.getTimewindow(prevNode).getStart(); + double L = instance.getTimewindow(prevNode).getEnd(); + double deltaWT = 0.0; + double deltaTW = 0.0; + + for (int i = from; i <= to; i++) { + Node arriveAtNode = route.get(i); + + double timeInNode = 0.0; + double travelTime = 0.0; + double delta = 0.0; + + travelTime = instance.getTravelTime(prevNode, arriveAtNode); + distance = distance + instance.getTravelDistance(prevNode, arriveAtNode); + + delta = D - TW + travelTime; + + double travelDistance = instance.getTravelDistance(prevNode, arriveAtNode); + y -= (travelDistance * instance.getVehicleEnergyConsumption()); + if (y < 0.0) { + yInfeasible -= y; + y = 0.0; + } + + deltaWT = Math.max(0, instance.getTimewindow(arriveAtNode).getStart() - delta - L); + deltaTW = Math.max(0, E + delta - instance.getTimewindow(arriveAtNode).getEnd()); + + timeInNode = instance.getServiceTime(arriveAtNode); + if (instance.isRechargingStation(arriveAtNode)) { + double refuelTime = + (instance.getVehicleEnergyCapacity() - y) * // used fuel + instance.getRechargingRate(arriveAtNode); // refill rate + + timeInNode = refuelTime; + + y = instance.getVehicleEnergyCapacity(); + } else { + // do nothing + } + + if (instance.isRechargingStation(arriveAtNode)) { + delta += timeInNode; + deltaWT = Math.max(0, instance.getTimewindow(arriveAtNode).getStart() - delta - L); + deltaTW = Math.max(0, E + delta - instance.getTimewindow(arriveAtNode).getEnd()); + } else + q = q + instance.getDemand(arriveAtNode); + + D = D + timeInNode + deltaWT + travelTime; + ST += timeInNode; + TW = TW + deltaTW; + E = Math.max(instance.getTimewindow(arriveAtNode).getStart() - delta, E) - deltaWT; + L = Math.min(instance.getTimewindow(arriveAtNode).getEnd() - delta, L) + deltaTW; + + prevNode = arriveAtNode; + } + + double qInf = Math.max(0.0, q - instance.getVehicleCapacity()); + return new Violations(qInf, TW, yInfeasible); + } } From 3d265cdcb1c8a66f7ce38e7743b3772e9d412340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 01:12:08 +0200 Subject: [PATCH 13/36] temporary change TabuSearch s.t. feasible, non-tabu solutions are prioritized over infeasible but better ones --- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 1 + .../metaheuristic/tabusearch/TabuSearch.kt | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 49204a2..1b0990b 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -42,6 +42,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe if (feasibilityPhase) { if (!feasible(bestSolution)) { if (i == N_FEAS) { + // TODO this is very important, needs to be implemented addVehicle(bestSolution) i = -1 } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index cfaa87a..61191b5 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -30,7 +30,12 @@ class TabuSearch(private val logEnabled: Boolean = true) { if (evaluateSolution(bestCandidate) < evaluateSolution(overallBestSolution)) { overallBestSolution = bestCandidate - log("New local optimum found. Cost: ${overallBestSolution.cost}") + log( + "New local optimum found. Cost: ${overallBestSolution.cost}, " + + "Cap-Violation: ${overallBestSolution.fitnessValue.totalCapacityViolation}, " + + "TW-Violation: ${overallBestSolution.fitnessValue.totalTimeWindowViolation}, " + + "Bat-Violation: ${overallBestSolution.fitnessValue.totalBatteryCapacityViolation}" + ) } iteration++ } @@ -46,12 +51,17 @@ class TabuSearch(private val logEnabled: Boolean = true) { for (explorer in explorers) { solutionsOfAllNeighbourhoods.addAll(explorer.exploreEverySolution(solution)) } - val list = solutionsOfAllNeighbourhoods - .filter { !tabuMap.contains(it) && it.fitnessValue.fitness == it.cost } // todo we should accept a "tabu" solution if it is feasible - if (list.isEmpty()) { + val solutionsNotInTabu = solutionsOfAllNeighbourhoods.filter { !tabuMap.contains(it) } + + val feasibleSolutions = solutionsNotInTabu.filter { it.fitnessValue.fitness == it.cost } + + if (feasibleSolutions.isNotEmpty()) { + return feasibleSolutions.sortedBy { it.fitnessValue.fitness }.first() + } + if (solutionsNotInTabu.isEmpty()) { return solution } - return list.sortedBy { it.fitnessValue.fitness }.first() + return solutionsNotInTabu.sortedBy { it.fitnessValue.fitness }.first() } private fun updateTabuMap(solution: EVRPTWSolution, tabuMap: MutableMap) { From 50682c140668aef1839672827ed66c9785e43ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 11:56:38 +0200 Subject: [PATCH 14/36] introduce INeighbourhoodExplorerCallable and its implementations for parallel operator execution; update TabuSearch s.t. is uses ExecutorService to schedule explorers --- .../at/ac/tuwien/otl/evrptw/Executor.kt | 27 +++++++++++++++++ .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 3 +- .../INeighbourhoodExplorerCallable.kt | 29 +++++++++++++++++++ .../callable/StationInReExplorerCallable.kt | 16 ++++++++++ .../TwoOptArcExchangeExplorerCallable.kt | 16 ++++++++++ .../metaheuristic/tabusearch/TabuSearch.kt | 19 ++++++++---- 6 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt new file mode 100644 index 0000000..f2dbf77 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt @@ -0,0 +1,27 @@ +package at.ac.tuwien.otl.evrptw + +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class Executor private constructor() { + + companion object { + private var executorService: ExecutorService? = null + + fun getExecutorService(): ExecutorService { + if (executorService == null) { + executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) + } + return executorService!! + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index db6a4fd..6324407 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -52,9 +52,10 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 1 until 2) { + for (i in 0 until 1) { runAlgorithmOnInstance(i, false) } + Executor.getExecutorService().shutdown() } private fun runAlgorithmOnInstance(instanceId: Int, detailed: Boolean): Long { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt new file mode 100644 index 0000000..db40853 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt @@ -0,0 +1,29 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable + +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.INeighbourhoodExplorer +import java.util.concurrent.Callable + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +abstract class INeighbourhoodExplorerCallable( + private val initialSolution: T, + private val explorer: INeighbourhoodExplorer +) : Callable> { + + /** + * Computes a result, or throws an exception if unable to do so. + * + * @return computed result + * @throws Exception if unable to compute a result + */ + override fun call(): List { + return explorer.exploreEverySolution(initialSolution) + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt new file mode 100644 index 0000000..b07011d --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt @@ -0,0 +1,16 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.StationInReExplorer + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class StationInReExplorerCallable(initialSolution: EVRPTWSolution, explorer: StationInReExplorer) : + INeighbourhoodExplorerCallable(initialSolution, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt new file mode 100644 index 0000000..4fcbf2b --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt @@ -0,0 +1,16 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExplorer + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class TwoOptArcExchangeExplorerCallable(initialSolution: EVRPTWSolution, explorer: TwoOptArcExchangeExplorer) : + INeighbourhoodExplorerCallable(initialSolution, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 61191b5..51c38b6 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -1,9 +1,13 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch +import at.ac.tuwien.otl.evrptw.Executor import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExplorer +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.INeighbourhoodExplorerCallable +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.TwoOptArcExchangeExplorerCallable import java.util.logging.Logger +import java.util.stream.Collectors /** *

About this class

@@ -16,7 +20,6 @@ import java.util.logging.Logger */ class TabuSearch(private val logEnabled: Boolean = true) { private val log: Logger = Logger.getLogger(this.javaClass.name) - private val explorers = listOf(TwoOptArcExchangeExplorer()) fun apply(solution: EVRPTWSolution): EVRPTWSolution { var overallBestSolution = EVRPTWSolution(solution) // create deep copy @@ -47,10 +50,8 @@ class TabuSearch(private val logEnabled: Boolean = true) { solution: EVRPTWSolution, tabuMap: Map ): EVRPTWSolution { - val solutionsOfAllNeighbourhoods = mutableListOf() - for (explorer in explorers) { - solutionsOfAllNeighbourhoods.addAll(explorer.exploreEverySolution(solution)) - } + val solutionsOfAllNeighbourhoods = parallelExploreNeighbourhoods(solution) + val solutionsNotInTabu = solutionsOfAllNeighbourhoods.filter { !tabuMap.contains(it) } val feasibleSolutions = solutionsNotInTabu.filter { it.fitnessValue.fitness == it.cost } @@ -64,6 +65,14 @@ class TabuSearch(private val logEnabled: Boolean = true) { return solutionsNotInTabu.sortedBy { it.fitnessValue.fitness }.first() } + private fun parallelExploreNeighbourhoods(solution: EVRPTWSolution): List { + val callableList = mutableListOf>() + callableList.add(TwoOptArcExchangeExplorerCallable(solution, TwoOptArcExchangeExplorer())) + // callableList.add(StationInReExplorerCallable(solution, StationInReExplorer())) + val results = Executor.getExecutorService().invokeAll(callableList) + return results.stream().flatMap { it.get().stream() }.collect(Collectors.toList()).toList() + } + private fun updateTabuMap(solution: EVRPTWSolution, tabuMap: MutableMap) { // remove elements older than some constant // don't forget to consider aspiration criteria From 4d87277cff3cf2686d446166fbb7a762f6c4f721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 12:51:06 +0200 Subject: [PATCH 15/36] implement StationInReExplorer --- solutions/c103_21_sol.txt | 27 +++++---- .../otl/evrptw/metaheuristic/Constants.kt | 2 +- .../neighbourhood/StationInReExplorer.kt | 58 ++++++++++++++++++- .../metaheuristic/tabusearch/TabuSearch.kt | 5 +- 4 files changed, 74 insertions(+), 18 deletions(-) diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index f5aab76..57d3f10 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,19 +1,18 @@ # solution for c103_21 -1459.9599355360504 -D0, C20, C32, C33, C34, C36, C29, C24, C22, C21, D0 -D0, C67, C74, S18, C80, C72, C61, C64, D0 -D0, C23, C25, C27, C28, C30, C26, C10, C11, C9, C8, D0 -D0, C46, C40, C41, C42, C44, C45, C48, C50, C51, C52, C49, D0 +1358.8048641582088 +D0, S0, C20, C34, S12, C39, C38, C37, C35, C36, C29, C24, D0 +D0, C65, C67, C62, C69, C68, C66, D0 D0, C90, C89, C88, C85, C84, C82, C83, C86, C87, C91, D0 -D0, C65, C63, C62, C69, C68, C66, D0 -D0, C47, C43, D0 -D0, C7, C3, C4, C6, C5, D0 -D0, C98, C96, C2, C1, C75, D0 +D0, C31, C32, C33, S11, C50, C51, C52, C49, D0 +D0, C7, C3, C2, C4, C6, C10, C11, C9, C8, D0 +D0, C98, C95, C96, C75, C5, D0 D0, C59, C55, C57, D0 +D0, S0, C17, C14, S7, C16, C19, C18, C15, C13, D0 D0, C12, D0 -D0, C14, S7, C16, C19, C17, C18, C15, C13, D0 -D0, C31, S12, C39, C38, C37, C35, D0 -D0, C95, S4, C94, C93, C92, C97, C100, C99, D0 -D0, S14, C60, C58, C56, C53, S16, C54, D0 -D0, S20, C78, C79, C77, D0 +D0, S0, C46, C40, C41, C42, C44, C45, C48, C47, C43, D0 +D0, S3, C93, C92, C97, C100, C99, S3, C94, D0 D0, S20, C73, C70, C71, C76, C81, D0 +D0, C23, C25, C27, C28, C30, C26, C22, C21, C1, D0 +D0, C74, C78, C79, C77, C80, S18, C63, D0 +D0, S0, D0 +D0, S14, C60, C58, C56, C53, C54, C72, C61, C64, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 0756b59..11905c1 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,7 +12,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 0 + const val N_DIST = 5 const val N_FEAS = 5 const val N_TABU = 100 const val N_PENALTY = 2 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt index b2a995f..4ca3468 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt @@ -1,6 +1,8 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood +import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.Route /** *

About this class

@@ -14,7 +16,59 @@ import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution class StationInReExplorer : INeighbourhoodExplorer { override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { - // inserts or removes recharging stations between nodes - return listOf(EVRPTWSolution(initialSolution)) + val result = mutableListOf() + for (routeIndex in 0 until initialSolution.routes.size) { + val route = initialSolution.routes[routeIndex] + for (nodeIndex in 1 until route.size) { + if (route[nodeIndex] is EVRPTWInstance.RechargingStation) { + result.add(performStationRemoval(initialSolution, routeIndex, nodeIndex)) + } else { + for (station in initialSolution.instance.rechargingStations) { + val neighbourSolution = performStationInsertion(initialSolution, routeIndex, nodeIndex, station) + result.add(neighbourSolution) + } + } + } + } + return result + } + + private fun performStationRemoval( + initialSolution: EVRPTWSolution, + routeIndex: Int, + nodeIndex: Int + ): EVRPTWSolution { + val routes = initialSolution.copyOfRoutes() + routes[routeIndex].removeAt(nodeIndex) + return EVRPTWSolution( + initialSolution.instance, + routes, + Route.calculateTotalDistance(routes, initialSolution.instance) + ) + } + + private fun performStationInsertion( + initialSolution: EVRPTWSolution, + routeIndex: Int, + nodeIndex: Int, + station: EVRPTWInstance.RechargingStation + ): EVRPTWSolution { + val routes = initialSolution.copyOfRoutes() + val stationToInsert = EVRPTWInstance.RechargingStation( + station.id, + station.name, + station.location.x, + station.location.y, + station.timeWindow.start, + station.timeWindow.end, + station.rechargingRate + ) + + routes[routeIndex].add(nodeIndex, stationToInsert) + return EVRPTWSolution( + initialSolution.instance, + routes, + Route.calculateTotalDistance(routes, initialSolution.instance) + ) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 51c38b6..7c37412 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -3,8 +3,10 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch import at.ac.tuwien.otl.evrptw.Executor import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.StationInReExplorer import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExplorer import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.INeighbourhoodExplorerCallable +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.StationInReExplorerCallable import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.TwoOptArcExchangeExplorerCallable import java.util.logging.Logger import java.util.stream.Collectors @@ -60,6 +62,7 @@ class TabuSearch(private val logEnabled: Boolean = true) { return feasibleSolutions.sortedBy { it.fitnessValue.fitness }.first() } if (solutionsNotInTabu.isEmpty()) { + log("NO SOLUTIONS AVAILABLE THAT ARE EITHER FEASIBLE OR NOT IN TABU LIST") return solution } return solutionsNotInTabu.sortedBy { it.fitnessValue.fitness }.first() @@ -68,7 +71,7 @@ class TabuSearch(private val logEnabled: Boolean = true) { private fun parallelExploreNeighbourhoods(solution: EVRPTWSolution): List { val callableList = mutableListOf>() callableList.add(TwoOptArcExchangeExplorerCallable(solution, TwoOptArcExchangeExplorer())) - // callableList.add(StationInReExplorerCallable(solution, StationInReExplorer())) + callableList.add(StationInReExplorerCallable(solution, StationInReExplorer())) val results = Executor.getExecutorService().invokeAll(callableList) return results.stream().flatMap { it.get().stream() }.collect(Collectors.toList()).toList() } From 80cfce0a29bcdf79c506b79c630ce250f42e7516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 13:28:32 +0200 Subject: [PATCH 16/36] adjust simulated annealing accept criterion --- solutions/c105_21_sol.txt | 43 +++++++------------ solutions/r211_21_sol.txt | 12 +++--- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 2 +- 4 files changed, 23 insertions(+), 36 deletions(-) diff --git a/solutions/c105_21_sol.txt b/solutions/c105_21_sol.txt index f382e31..aad3ed0 100644 --- a/solutions/c105_21_sol.txt +++ b/solutions/c105_21_sol.txt @@ -1,30 +1,17 @@ # solution for c105_21 -2029.2143728128362 -D0, C20, C21, C25, C27, C30, C28, C26, C6, C7, D0 -D0, C67, C65, C62, C64, C68, C69, C48, C46, C47, C43, D0 -D0, C23, C22, C29, C32, C33, C31, D0 -D0, C63, C74, C40, C42, C44, C45, C51, C50, D0 -D0, C75, C1, C98, C95, D0 -D0, C24, C34, C36, D0 -D0, C41, C59, C55, D0 -D0, C90, C87, C86, C82, C83, C84, C85, C88, D0 -D0, C96, C2, C4, C3, D0 -D0, C12, C10, D0 -D0, S14, C60, C58, C56, C53, C54, C57, D0 -D0, C14, C11, D0 -D0, S7, C16, C19, C18, C17, C15, D0 -D0, S12, C39, C38, C37, C35, D0 -D0, S4, C94, C92, C93, C97, C100, C99, D0 +1376.958260332306 +D0, S0, S7, C14, C16, C19, C18, C17, C15, C13, D0 +D0, C59, S18, C80, C72, C61, C66, D0 +D0, C96, S4, C94, C92, C93, C97, C100, C99, D0 +D0, C75, C1, C12, S5, C6, C7, C10, C11, C9, C8, D0 +D0, C98, C95, C2, C4, C3, C5, D0 +D0, C20, C24, S12, C39, C38, C37, C35, C51, C50, C52, C49, D0 +D0, C32, C33, C31, D0 +D0, C23, C29, C34, C36, D0 D0, S20, C73, C70, C79, C77, C81, D0 -D0, S20, C71, C76, C78, C72, D0 -D0, C5, D0 -D0, S18, C80, D0 -D0, C9, D0 -D0, C89, D0 -D0, C61, D0 -D0, C91, D0 -D0, C49, D0 -D0, C8, D0 -D0, C66, D0 -D0, C52, D0 -D0, C13, D0 +D0, C63, C74, S20, C71, C76, C78, D0 +D0, C21, C22, C25, C27, C30, C28, C26, D0 +D0, S14, C60, C58, C56, C53, C54, C55, C57, D0 +D0, C67, C65, C62, C64, C68, C69, D0 +D0, C41, C40, C42, C44, C45, C48, C46, C47, C43, D0 +D0, C90, C87, C86, C82, C83, C84, C85, C88, C89, C91, D0 diff --git a/solutions/r211_21_sol.txt b/solutions/r211_21_sol.txt index 0dc26c8..14ab02b 100644 --- a/solutions/r211_21_sol.txt +++ b/solutions/r211_21_sol.txt @@ -1,7 +1,7 @@ # solution for r211_21 -1062.1554791026529 -D0, C27, C28, C26, C21, C72, C74, C75, C56, C23, C39, C25, C55, C4, C73, C22, C41, C57, C87, C97, C37, C98, C85, C61, C16, C91, C93, C96, C94, C6, C89, C60, C45, C17, C86, D0 -D0, C53, C58, C13, C95, C92, C59, C99, C5, C84, C83, C8, C46, C48, C19, C11, C63, C90, C32, C30, C20, C51, C9, C35, C71, C66, C10, C62, C31, C69, C3, C12, C40, D0 -D0, C52, C18, C7, C88, C70, C1, C50, C76, C77, C68, C80, C54, C24, C29, C78, C33, C82, C47, C36, C49, C64, D0 -D0, C2, C42, C100, C14, C44, C38, C43, C15, D0 -D0, C79, C81, C34, C65, C67, D0 +1149.74463287448 +D0, S0, S0, S0, C28, C76, C68, C26, C58, C42, C91, C100, C54, C39, C25, C55, C24, C29, C78, C33, C69, D0 +D0, C53, C13, C95, C92, C59, C99, C5, C84, C83, C8, C46, C14, C44, C38, C43, C15, C89, C60, C45, C17, C86, C61, C16, C85, C93, C96, C94, D0 +D0, S0, C52, C18, C7, C88, C63, C48, C19, C11, C27, C90, C32, C30, C20, C51, C9, C35, C71, C66, C10, C31, D0 +D0, C70, C1, C50, C81, C34, C65, C79, C3, C80, C77, C12, C40, D0 +D0, C6, C2, C21, C72, C74, C75, C56, C23, C67, C4, C73, C22, C41, C57, C87, C97, C37, C98, C82, C47, C36, C49, C64, C62, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 6324407..3237e77 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -52,7 +52,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 0 until 1) { + for (i in 1 until 2) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 1b0990b..3a2e842 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -58,7 +58,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe } private fun acceptSimulatedAnnealing(optimizedNewSolution: EVRPTWSolution, bestSolution: EVRPTWSolution): Boolean { - return true + return optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness } private fun feasible(solution: EVRPTWSolution): Boolean { From 7b33be5327dd9e402594e3c8698971b7ecfa57e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 13:34:25 +0200 Subject: [PATCH 17/36] fix JVM platform clash due to unambiguous naming --- src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt index f2dbf77..5d48a61 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt @@ -15,13 +15,13 @@ import java.util.concurrent.Executors class Executor private constructor() { companion object { - private var executorService: ExecutorService? = null + private var customExecutorService: ExecutorService? = null fun getExecutorService(): ExecutorService { - if (executorService == null) { - executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) + if (customExecutorService == null) { + customExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) } - return executorService!! + return customExecutorService!! } } } \ No newline at end of file From 73559534dbd8a365d574d7e716db05d07bf3bf7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 18:45:56 +0200 Subject: [PATCH 18/36] extend solution by violations for each route; adapt constants; implement making feasible solutions out of infeasible ones; add probability based accept criterion for infeasible but promising solutions --- solutions/c103_21_sol.txt | 29 +++++---- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../tuwien/otl/evrptw/dto/EVRPTWSolution.kt | 59 +++++++++++++++++-- .../otl/evrptw/metaheuristic/Constants.kt | 10 ++-- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 55 +++++++++++++++-- .../metaheuristic/tabusearch/TabuSearch.kt | 4 +- .../evrptw/verifier/EVRPTWRouteVerifier.java | 2 +- 7 files changed, 127 insertions(+), 34 deletions(-) diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index 57d3f10..d971f53 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,18 +1,17 @@ # solution for c103_21 -1358.8048641582088 -D0, S0, C20, C34, S12, C39, C38, C37, C35, C36, C29, C24, D0 -D0, C65, C67, C62, C69, C68, C66, D0 +1280.6320409175673 +D0, S0, C20, C24, C32, C33, C47, D0 +D0, C23, C29, S12, C39, C38, C37, C35, C34, C36, C31, D0 +D0, C75, C1, C2, C99, C5, D0 D0, C90, C89, C88, C85, C84, C82, C83, C86, C87, C91, D0 -D0, C31, C32, C33, S11, C50, C51, C52, C49, D0 -D0, C7, C3, C2, C4, C6, C10, C11, C9, C8, D0 -D0, C98, C95, C96, C75, C5, D0 -D0, C59, C55, C57, D0 -D0, S0, C17, C14, S7, C16, C19, C18, C15, C13, D0 -D0, C12, D0 -D0, S0, C46, C40, C41, C42, C44, C45, C48, C47, C43, D0 -D0, S3, C93, C92, C97, C100, C99, S3, C94, D0 +D0, C98, C95, C96, C94, C93, C92, C97, C100, S3, D0 +D0, S20, C78, C79, C77, C80, C72, C61, C66, D0 +D0, C14, C12, C16, C19, C15, S7, C17, C18, C13, D0 +D0, C67, C74, C62, C69, C68, C64, C41, C43, D0 D0, S20, C73, C70, C71, C76, C81, D0 -D0, C23, C25, C27, C28, C30, C26, C22, C21, C1, D0 -D0, C74, C78, C79, C77, C80, S18, C63, D0 -D0, S0, D0 -D0, S14, C60, C58, C56, C53, C54, C72, C61, C64, D0 +D0, S14, C60, C58, C56, C54, C53, C57, D0 +D0, C7, C3, C4, C6, C9, C8, D0 +D0, C59, C55, D0 +D0, C40, C42, C44, C46, C45, C48, C50, C51, C52, C49, D0 +D0, C65, C63, D0 +D0, C21, C22, C25, C27, C30, C28, C26, C10, C11, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 3237e77..6324407 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -52,7 +52,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 1 until 2) { + for (i in 0 until 1) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt index 0d50b66..fb532ca 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt @@ -25,12 +25,24 @@ data class EVRPTWSolution( val fitnessValue: FitnessValue = calculateFitnessValue() private fun calculateFitnessValue(): FitnessValue { - val violations = EVRPTWRouteVerifier.calculateViolations(instance, routes) + // val violations = EVRPTWRouteVerifier.calculateViolations(instance, routes) + var capacityViolations = 0.0 + var timeWindowViolations = 0.0 + var batteryCapacityViolations = 0.0 + val routeViolations = mutableListOf() + for (route in routes) { + val violations = EVRPTWRouteVerifier.calculateViolationOfRoute(instance, route) + capacityViolations += violations.capacityViolation + timeWindowViolations += violations.timeWindowViolation + batteryCapacityViolations += violations.batteryCapacityViolation + routeViolations.add(violations) + } return FitnessValue( cost, - violations.capacityViolation, - violations.timeWindowViolation, - violations.batteryCapacityViolation + capacityViolations, + timeWindowViolations, + batteryCapacityViolations, + routeViolations ) } @@ -190,13 +202,50 @@ data class EVRPTWSolution( .collect(Collectors.toList()) .toMutableList() } + + fun copyOfRoute(route: List): MutableList { + return route.stream().map { node -> + when (node) { + is EVRPTWInstance.Depot -> EVRPTWInstance.Depot( + node.id, + node.name, + node.location.x, + node.location.y, + node.timeWindow.start, + node.timeWindow.end + ) + is EVRPTWInstance.Customer -> EVRPTWInstance.Customer( + node.id, + node.name, + node.location.x, + node.location.y, + node.timeWindow.start, + node.timeWindow.end, + node.demand, + node.serviceTime + ) + else -> EVRPTWInstance.RechargingStation( + (node as EVRPTWInstance.RechargingStation).id, + node.name, + node.location.x, + node.location.y, + node.timeWindow.start, + node.timeWindow.end, + node.rechargingRate + ) + } + } + .collect(Collectors.toList()) + .toMutableList() + } } data class FitnessValue( val totalTravelDistance: Double, val totalCapacityViolation: Double, val totalTimeWindowViolation: Double, - val totalBatteryCapacityViolation: Double + val totalBatteryCapacityViolation: Double, + val routeViolations: List ) { val fitness = totalTravelDistance + (ALPHA * totalCapacityViolation) + (BETA * totalTimeWindowViolation) + (GAMMA * totalBatteryCapacityViolation) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 11905c1..2354558 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,15 +12,15 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 5 + const val N_DIST = 10 const val N_FEAS = 5 - const val N_TABU = 100 + const val N_TABU = 50 const val N_PENALTY = 2 const val COOLING_FACTOR = 0.9 const val TABU_TENURE_MIN = 15 const val TABU_TENURE_MAX = 30 - const val ALPHA = 10 - const val BETA = 10 - const val GAMMA = 10 + const val ALPHA = 1 + const val BETA = 1 + const val GAMMA = 1 } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 3a2e842..3ed022e 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -1,11 +1,15 @@ package at.ac.tuwien.otl.evrptw.metaheuristic +import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure +import at.ac.tuwien.otl.evrptw.dto.Route +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.COOLING_FACTOR import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS import at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch.TabuSearch import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier +import java.util.Random import java.util.logging.Logger /** @@ -21,6 +25,8 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe private val log: Logger = Logger.getLogger(this.javaClass.name) private val neighbourSolutionGenerator = ShakingNeighbourSolutionGenerator() private val tabuSearch = TabuSearch(logEnabled) + private val random = Random(java.lang.Double.doubleToLongBits(Math.random())) + private var temperature = 900.0 override fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution { var bestSolution = evrptwSolution @@ -42,8 +48,8 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe if (feasibilityPhase) { if (!feasible(bestSolution)) { if (i == N_FEAS) { - // TODO this is very important, needs to be implemented - addVehicle(bestSolution) + log("!!!!!!! Splitting routes !!!!!!") + bestSolution = addVehicle(bestSolution) i = -1 } } else { @@ -58,15 +64,54 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe } private fun acceptSimulatedAnnealing(optimizedNewSolution: EVRPTWSolution, bestSolution: EVRPTWSolution): Boolean { - return optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness + val accept: Boolean + accept = if (optimizedNewSolution.fitnessValue.fitness == optimizedNewSolution.cost) { + optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness + } else { + if (optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness) { + val exponent = + (-Math.abs(optimizedNewSolution.fitnessValue.fitness - bestSolution.fitnessValue.fitness)) / temperature + val probabilityAccept = Math.exp(exponent) + log("SA accept probability: $probabilityAccept") + random.nextDouble() < probabilityAccept + } else { + false + } + } + temperature *= COOLING_FACTOR + return accept } private fun feasible(solution: EVRPTWSolution): Boolean { - // return solution.fitnessValue.fitness == solution.cost return EVRPTWRouteVerifier(solution.instance).verify(solution.routes, solution.cost, false) } - private fun addVehicle(solution: EVRPTWSolution) { + private fun addVehicle(solution: EVRPTWSolution): EVRPTWSolution { + val newRoutes = mutableListOf>() + for (routeIndex in 0 until solution.fitnessValue.routeViolations.size) { + val routeViolation = solution.fitnessValue.routeViolations[routeIndex] + val route = solution.routes[routeIndex] + if (route.size > 3 && (routeViolation.batteryCapacityViolation > 0.0 || routeViolation.timeWindowViolation > 0.0 || routeViolation.capacityViolation > 0.0)) { + newRoutes.addAll(splitRoute(route, solution.instance)) + } else { + newRoutes.add(solution.copyOfRoute(solution.routes[routeIndex])) + } + } + return EVRPTWSolution(solution.instance, newRoutes, Route.calculateTotalDistance(newRoutes, solution.instance)) + } + + private fun splitRoute( + route: List, + instance: EVRPTWInstance + ): MutableList> { + val newRoutes = mutableListOf>() + val firstRoute = route.subList(0, route.size / 2).toMutableList() + firstRoute.add(instance.depot) + val secondRoute = route.subList(route.size / 2, route.size).toMutableList() + secondRoute.add(0, instance.depot) + newRoutes.add(firstRoute) + newRoutes.add(secondRoute) + return newRoutes } private fun log(message: String) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 7c37412..d1be371 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -56,11 +56,11 @@ class TabuSearch(private val logEnabled: Boolean = true) { val solutionsNotInTabu = solutionsOfAllNeighbourhoods.filter { !tabuMap.contains(it) } - val feasibleSolutions = solutionsNotInTabu.filter { it.fitnessValue.fitness == it.cost } + /*val feasibleSolutions = solutionsNotInTabu.filter { it.fitnessValue.fitness == it.cost } if (feasibleSolutions.isNotEmpty()) { return feasibleSolutions.sortedBy { it.fitnessValue.fitness }.first() - } + }*/ if (solutionsNotInTabu.isEmpty()) { log("NO SOLUTIONS AVAILABLE THAT ARE EITHER FEASIBLE OR NOT IN TABU LIST") return solution diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java b/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java index 5a667bd..c020d40 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/verifier/EVRPTWRouteVerifier.java @@ -248,7 +248,7 @@ public static Violations calculateViolations(final EVRPTWInstance instance, fina return new Violations(capacityViolations, timeWindowViolations, batteryCapacityViolations); } - private static Violations calculateViolationOfRoute(EVRPTWInstance instance, List route) { + public static Violations calculateViolationOfRoute(EVRPTWInstance instance, List route) { int from = 1; int to = route.size() - 1; From 0ff8c368f78131655ab3cbefdd7a3b586fb68a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 19:08:09 +0200 Subject: [PATCH 19/36] add unique initial temperatures for each instance --- solutions/r211_21_sol.txt | 11 +++++------ src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 14 +++++++++++++- .../tuwien/otl/evrptw/metaheuristic/Constants.kt | 6 +++--- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 9 ++++++--- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/solutions/r211_21_sol.txt b/solutions/r211_21_sol.txt index 14ab02b..8f87f35 100644 --- a/solutions/r211_21_sol.txt +++ b/solutions/r211_21_sol.txt @@ -1,7 +1,6 @@ # solution for r211_21 -1149.74463287448 -D0, S0, S0, S0, C28, C76, C68, C26, C58, C42, C91, C100, C54, C39, C25, C55, C24, C29, C78, C33, C69, D0 -D0, C53, C13, C95, C92, C59, C99, C5, C84, C83, C8, C46, C14, C44, C38, C43, C15, C89, C60, C45, C17, C86, C61, C16, C85, C93, C96, C94, D0 -D0, S0, C52, C18, C7, C88, C63, C48, C19, C11, C27, C90, C32, C30, C20, C51, C9, C35, C71, C66, C10, C31, D0 -D0, C70, C1, C50, C81, C34, C65, C79, C3, C80, C77, C12, C40, D0 -D0, C6, C2, C21, C72, C74, C75, C56, C23, C67, C4, C73, C22, C41, C57, C87, C97, C37, C98, C82, C47, C36, C49, C64, C62, D0 +921.7238364630402 +D0, S0, C27, C52, C18, C7, C88, C70, C1, C50, C79, C81, C34, C65, C35, C71, C66, C20, C51, C9, C24, C29, C78, C33, C3, C12, D0 +D0, C28, C76, C77, C68, C80, C54, C67, C75, C56, C23, C39, C25, C55, C4, C73, C22, C41, C40, D0 +D0, C53, C58, C13, C95, C92, C59, C99, C5, C84, C83, C8, C46, C48, C19, C11, C63, C90, C32, C30, C10, C62, C31, C69, D0 +D0, C26, C21, C72, C74, C2, C42, C100, C14, C44, C38, C43, C15, C57, C87, C97, C37, C98, C85, C61, C16, S11, C91, C93, C96, C94, C6, C89, C60, C45, C17, C86, C82, C47, C36, C49, C64, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 6324407..a44bbb7 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -27,6 +27,18 @@ class Main { private val solver = EVRPTWSolver() private const val rampUpRuns = 20 private const val measuredRuns = 30 + val instanceToInitTemperatureMap = mapOf( + instances[0] to 1237.55, + instances[1] to 995.23, + instances[2] to 841.04, + instances[3] to 1167.86, + instances[4] to 1354.36, + instances[5] to 399.49, + instances[6] to 296.37, + instances[7] to 1664.92, + instances[8] to 1638.65, + instances[9] to 675.69 + ) @JvmStatic fun main(args: Array) { @@ -52,7 +64,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 0 until 1) { + for (i in 6 until 7) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 2354558..a7c377d 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -19,8 +19,8 @@ class Constants private constructor() { const val COOLING_FACTOR = 0.9 const val TABU_TENURE_MIN = 15 const val TABU_TENURE_MAX = 30 - const val ALPHA = 1 - const val BETA = 1 - const val GAMMA = 1 + const val ALPHA = 0.1 + const val BETA = 0.1 + const val GAMMA = 0.1 } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 3ed022e..1b485c4 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -1,5 +1,6 @@ package at.ac.tuwien.otl.evrptw.metaheuristic +import at.ac.tuwien.otl.evrptw.Main import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure @@ -26,9 +27,10 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe private val neighbourSolutionGenerator = ShakingNeighbourSolutionGenerator() private val tabuSearch = TabuSearch(logEnabled) private val random = Random(java.lang.Double.doubleToLongBits(Math.random())) - private var temperature = 900.0 + private var temperature = 0.0 override fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution { + temperature = Main.instanceToInitTemperatureMap[evrptwSolution.instance.name]!! var bestSolution = evrptwSolution var k = 1 var i = 0 @@ -72,8 +74,9 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe val exponent = (-Math.abs(optimizedNewSolution.fitnessValue.fitness - bestSolution.fitnessValue.fitness)) / temperature val probabilityAccept = Math.exp(exponent) - log("SA accept probability: $probabilityAccept") - random.nextDouble() < probabilityAccept + val randomNumber = random.nextDouble() + log("SA accept probability: $randomNumber < $probabilityAccept") + randomNumber < probabilityAccept } else { false } From de49e2eced2739dc2f5475dd0be4c23a9715a673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 19:55:51 +0200 Subject: [PATCH 20/36] implement exchange operator both inter- and intra-route; update TabuSearch --- solutions/c103_21_sol.txt | 30 ++++--- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../otl/evrptw/metaheuristic/Constants.kt | 6 +- .../InterIntraRouteExchangeExplorer.kt | 82 +++++++++++++++++++ ...InterIntraRouteExchangeExplorerCallable.kt | 18 ++++ .../metaheuristic/tabusearch/TabuSearch.kt | 3 + 6 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index d971f53..9b99715 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,17 +1,15 @@ # solution for c103_21 -1280.6320409175673 -D0, S0, C20, C24, C32, C33, C47, D0 -D0, C23, C29, S12, C39, C38, C37, C35, C34, C36, C31, D0 -D0, C75, C1, C2, C99, C5, D0 -D0, C90, C89, C88, C85, C84, C82, C83, C86, C87, C91, D0 -D0, C98, C95, C96, C94, C93, C92, C97, C100, S3, D0 -D0, S20, C78, C79, C77, C80, C72, C61, C66, D0 -D0, C14, C12, C16, C19, C15, S7, C17, C18, C13, D0 -D0, C67, C74, C62, C69, C68, C64, C41, C43, D0 -D0, S20, C73, C70, C71, C76, C81, D0 -D0, S14, C60, C58, C56, C54, C53, C57, D0 -D0, C7, C3, C4, C6, C9, C8, D0 -D0, C59, C55, D0 -D0, C40, C42, C44, C46, C45, C48, C50, C51, C52, C49, D0 -D0, C65, C63, D0 -D0, C21, C22, C25, C27, C30, C28, C26, C10, C11, D0 +1240.2170764122088 +D0, S0, C23, C25, C27, C30, C28, C26, C10, C11, C9, C8, D0 +D0, C67, C65, C68, C64, S18, S18, C80, C72, C61, C69, D0 +D0, C32, C33, C34, C29, C24, C22, C21, D0 +D0, C98, C95, C94, S4, C92, C93, C97, C100, C99, C75, D0 +D0, S7, S7, C12, C14, C16, C19, C18, C17, C15, C13, D0 +D0, C86, S20, C73, C70, C71, C76, C81, D0 +D0, C59, C40, C41, C42, C43, D0 +D0, C91, C89, C88, C85, C82, C83, C84, C87, C90, D0 +D0, C63, C74, S20, C78, C77, C79, C62, C66, D0 +D0, S0, C44, C45, C48, C51, C50, C52, C46, C47, C49, D0 +D0, C20, C36, C39, S12, C38, C37, C35, C31, S0, D0 +D0, S14, C60, C58, C56, C53, C54, C55, C57, D0 +D0, C3, C1, C96, S3, C2, C4, C6, C7, C5, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index a44bbb7..422fc26 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -64,7 +64,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 6 until 7) { + for (i in 0 until 1) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index a7c377d..1efd26a 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -19,8 +19,8 @@ class Constants private constructor() { const val COOLING_FACTOR = 0.9 const val TABU_TENURE_MIN = 15 const val TABU_TENURE_MAX = 30 - const val ALPHA = 0.1 - const val BETA = 0.1 - const val GAMMA = 0.1 + const val ALPHA = 2 + const val BETA = 2 + const val GAMMA = 2 } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt new file mode 100644 index 0000000..ce76e9a --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt @@ -0,0 +1,82 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.Route + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class InterIntraRouteExchangeExplorer : INeighbourhoodExplorer { + + /** + * Explores (enumerates) every possible solution in the search space + * of this neighbourhood based on the initial solution and returns them + * in a list. + * + * @param initialSolution an initial solution for which the neighbours should + * be calculated + * @return a list of neighbour solutions, not necessarily sorted + */ + override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { + val result = mutableListOf() + for (routeIndex in 0 until initialSolution.routes.size) { + val route = initialSolution.routes[routeIndex] + + for (secondRouteIndex in routeIndex until initialSolution.routes.size) { + val secondRoute = initialSolution.routes[secondRouteIndex] + for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot + if (route[nodeOfFirstRoute] is EVRPTWInstance.RechargingStation) { + // skip exchange for Recharging stations + continue + } + for (nodeOfSecondRoute in 1 until secondRoute.size - 1) { + if (secondRoute[nodeOfSecondRoute] is EVRPTWInstance.RechargingStation) { + // skip exchange for Recharging stations + continue + } + if (secondRouteIndex != routeIndex || nodeOfFirstRoute != nodeOfSecondRoute) { + val neighbourSolution = performNodeSwap( + initialSolution, + routeIndex, + nodeOfFirstRoute, + secondRouteIndex, + nodeOfSecondRoute + ) + result.add(neighbourSolution) + } + } + } + } + } + return result + } + + private fun performNodeSwap( + initialSolution: EVRPTWSolution, + routeIndex: Int, + nodeOfFirstRoute: Int, + secondRouteIndex: Int, + nodeOfSecondRoute: Int + ): EVRPTWSolution { + val routes = initialSolution.copyOfRoutes() + + val node1 = routes[routeIndex][nodeOfFirstRoute] + val node2 = routes[secondRouteIndex][nodeOfSecondRoute] + + routes[routeIndex][nodeOfFirstRoute] = node2 + routes[secondRouteIndex][nodeOfSecondRoute] = node1 + + return EVRPTWSolution( + initialSolution.instance, + routes, + Route.calculateTotalDistance(routes, initialSolution.instance) + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt new file mode 100644 index 0000000..c2e8d69 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt @@ -0,0 +1,18 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteExchangeExplorer + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class InterIntraRouteExchangeExplorerCallable( + initialSolution: EVRPTWSolution, + explorer: InterIntraRouteExchangeExplorer +) : INeighbourhoodExplorerCallable(initialSolution, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index d1be371..bdcc9a3 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -3,9 +3,11 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch import at.ac.tuwien.otl.evrptw.Executor import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteExchangeExplorer import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.StationInReExplorer import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExplorer import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.INeighbourhoodExplorerCallable +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.InterIntraRouteExchangeExplorerCallable import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.StationInReExplorerCallable import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.TwoOptArcExchangeExplorerCallable import java.util.logging.Logger @@ -72,6 +74,7 @@ class TabuSearch(private val logEnabled: Boolean = true) { val callableList = mutableListOf>() callableList.add(TwoOptArcExchangeExplorerCallable(solution, TwoOptArcExchangeExplorer())) callableList.add(StationInReExplorerCallable(solution, StationInReExplorer())) + callableList.add(InterIntraRouteExchangeExplorerCallable(solution, InterIntraRouteExchangeExplorer())) val results = Executor.getExecutorService().invokeAll(callableList) return results.stream().flatMap { it.get().stream() }.collect(Collectors.toList()).toList() } From 6c9778bbc0f88017c86b0b1ab56da7da5035e626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 21:43:19 +0200 Subject: [PATCH 21/36] implement relocate operator both inter- and intra-route; update TabuSearch; update HybridVnsTsMetaHeuristic s.t. best feasible solution is stored as well --- solutions/c103_21_sol.txt | 29 ++++---- .../otl/evrptw/metaheuristic/Constants.kt | 2 +- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 6 +- .../InterIntraRouteRelocateExplorer.kt | 73 +++++++++++++++++++ .../TwoOptArcExchangeExplorer.kt | 9 --- ...InterIntraRouteRelocateExplorerCallable.kt | 18 +++++ .../metaheuristic/tabusearch/TabuSearch.kt | 13 +--- 7 files changed, 117 insertions(+), 33 deletions(-) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index 9b99715..2a0d64e 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,15 +1,18 @@ # solution for c103_21 -1240.2170764122088 -D0, S0, C23, C25, C27, C30, C28, C26, C10, C11, C9, C8, D0 -D0, C67, C65, C68, C64, S18, S18, C80, C72, C61, C69, D0 -D0, C32, C33, C34, C29, C24, C22, C21, D0 -D0, C98, C95, C94, S4, C92, C93, C97, C100, C99, C75, D0 -D0, S7, S7, C12, C14, C16, C19, C18, C17, C15, C13, D0 -D0, C86, S20, C73, C70, C71, C76, C81, D0 -D0, C59, C40, C41, C42, C43, D0 -D0, C91, C89, C88, C85, C82, C83, C84, C87, C90, D0 -D0, C63, C74, S20, C78, C77, C79, C62, C66, D0 -D0, S0, C44, C45, C48, C51, C50, C52, C46, C47, C49, D0 -D0, C20, C36, C39, S12, C38, C37, C35, C31, S0, D0 +1277.0315861559218 +D0, S0, S7, C12, C14, C16, C19, C18, C17, C15, C13, D0 +D0, C91, C89, C88, C85, C84, C82, C83, D0 +D0, C63, C74, C72, C61, C66, D0 +D0, C90, C87, C86, S20, C77, C80, C79, D0 +D0, C23, C25, C27, C30, C28, C26, C10, C11, D0 +D0, C59, C44, C42, C43, D0 +D0, C98, C95, C94, S4, C92, C93, C97, C100, C99, D0 +D0, C7, C3, C5, D0 +D0, C34, C36, C39, S12, C38, C37, C35, C31, D0 +D0, S20, C73, C70, C71, C76, C78, C81, D0 +D0, C41, C40, D0 +D0, C21, C22, C24, C29, C33, C32, C20, D0 +D0, C67, C65, C62, C64, C68, C69, D0 +D0, C75, C96, S3, C1, C2, C4, C6, C9, C8, D0 +D0, C46, C45, C48, C51, C50, C52, C47, C49, D0 D0, S14, C60, C58, C56, C53, C54, C55, C57, D0 -D0, C3, C1, C96, S3, C2, C4, C6, C7, C5, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 1efd26a..8545730 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,7 +12,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 10 + const val N_DIST = 5 const val N_FEAS = 5 const val N_TABU = 50 const val N_PENALTY = 2 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 1b485c4..75fb94a 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -31,6 +31,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe override fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution { temperature = Main.instanceToInitTemperatureMap[evrptwSolution.instance.name]!! + var bestFeasibleSolution = evrptwSolution var bestSolution = evrptwSolution var k = 1 var i = 0 @@ -42,6 +43,9 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe if (acceptSimulatedAnnealing(optimizedNewSolution, bestSolution)) { bestSolution = optimizedNewSolution + if (optimizedNewSolution.fitnessValue.fitness == optimizedNewSolution.cost) { + bestFeasibleSolution = optimizedNewSolution + } k = 1 } else { k = (k % NeighbourhoodStructure.STRUCTURES.size) + 1 @@ -62,7 +66,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe i++ } - return bestSolution + return bestFeasibleSolution } private fun acceptSimulatedAnnealing(optimizedNewSolution: EVRPTWSolution, bestSolution: EVRPTWSolution): Boolean { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt new file mode 100644 index 0000000..a9c2827 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt @@ -0,0 +1,73 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.Route + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class InterIntraRouteRelocateExplorer : INeighbourhoodExplorer { + /** + * Explores (enumerates) every possible solution in the search space + * of this neighbourhood based on the initial solution and returns them + * in a list. + * + * @param initialSolution an initial solution for which the neighbours should + * be calculated + * @return a list of neighbour solutions, not necessarily sorted + */ + override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { + val result = mutableListOf() + for (routeIndex in 0 until initialSolution.routes.size) { + val route = initialSolution.routes[routeIndex] + if (route.size > 3) { + for (secondRouteIndex in routeIndex until initialSolution.routes.size) { + val secondRoute = initialSolution.routes[secondRouteIndex] + for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot + for (nodeOfSecondRoute in 1 until secondRoute.size - 1) { + if (routeIndex == secondRouteIndex && nodeOfSecondRoute <= nodeOfFirstRoute) { + continue + } + val neighbourSolution = performRelocation( + initialSolution, + routeIndex, + nodeOfFirstRoute, + secondRouteIndex, + nodeOfSecondRoute + ) + result.add(neighbourSolution) + } + } + } + } + } + return result + } + + private fun performRelocation( + initialSolution: EVRPTWSolution, + routeIndex: Int, + nodeOfFirstRoute: Int, + secondRouteIndex: Int, + nodeOfSecondRoute: Int + ): EVRPTWSolution { + val routes = initialSolution.copyOfRoutes() + + val node = routes[routeIndex][nodeOfFirstRoute] + + routes[secondRouteIndex].add(nodeOfSecondRoute, node) + routes[routeIndex].removeAt(nodeOfFirstRoute) + + return EVRPTWSolution( + initialSolution.instance, + routes, + Route.calculateTotalDistance(routes, initialSolution.instance) + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt index f309a00..8e6ec87 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt @@ -73,15 +73,6 @@ class TwoOptArcExchangeExplorer : routes.removeAt(secondRouteIndex) } - // TODO maybe recalculate costs, violations here? return EVRPTWSolution(solution.instance, routes, Route.calculateTotalDistance(routes, solution.instance)) } - - private fun calculateTotalCostBasedOnOtherSolution( - baselineSolution: EVRPTWSolution, - newSolution: EVRPTWSolution - ): Double { - // ... - return baselineSolution.cost - } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt new file mode 100644 index 0000000..88edb60 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt @@ -0,0 +1,18 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteRelocateExplorer + +/** + *

About this class

+ * + *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 1.0.0 + */ +class InterIntraRouteRelocateExplorerCallable( + initialSolution: EVRPTWSolution, + explorer: InterIntraRouteRelocateExplorer +) : INeighbourhoodExplorerCallable(initialSolution, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index bdcc9a3..87f0c20 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -1,15 +1,14 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch +/* ktlint-disable no-wildcard-imports */ import at.ac.tuwien.otl.evrptw.Executor import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteExchangeExplorer +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteRelocateExplorer import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.StationInReExplorer import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExplorer -import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.INeighbourhoodExplorerCallable -import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.InterIntraRouteExchangeExplorerCallable -import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.StationInReExplorerCallable -import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.TwoOptArcExchangeExplorerCallable +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.* import java.util.logging.Logger import java.util.stream.Collectors @@ -58,11 +57,6 @@ class TabuSearch(private val logEnabled: Boolean = true) { val solutionsNotInTabu = solutionsOfAllNeighbourhoods.filter { !tabuMap.contains(it) } - /*val feasibleSolutions = solutionsNotInTabu.filter { it.fitnessValue.fitness == it.cost } - - if (feasibleSolutions.isNotEmpty()) { - return feasibleSolutions.sortedBy { it.fitnessValue.fitness }.first() - }*/ if (solutionsNotInTabu.isEmpty()) { log("NO SOLUTIONS AVAILABLE THAT ARE EITHER FEASIBLE OR NOT IN TABU LIST") return solution @@ -75,6 +69,7 @@ class TabuSearch(private val logEnabled: Boolean = true) { callableList.add(TwoOptArcExchangeExplorerCallable(solution, TwoOptArcExchangeExplorer())) callableList.add(StationInReExplorerCallable(solution, StationInReExplorer())) callableList.add(InterIntraRouteExchangeExplorerCallable(solution, InterIntraRouteExchangeExplorer())) + callableList.add(InterIntraRouteRelocateExplorerCallable(solution, InterIntraRouteRelocateExplorer())) val results = Executor.getExecutorService().invokeAll(callableList) return results.stream().flatMap { it.get().stream() }.collect(Collectors.toList()).toList() } From 3fe685b685e1e11d0816104359eeefab8f8064dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 24 Jun 2018 22:36:43 +0200 Subject: [PATCH 22/36] implemented adaptive penalty factors --- .../otl/evrptw/metaheuristic/Constants.kt | 13 ++++-- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 45 ++++++++++++++++++- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 8545730..bd0e8f0 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,15 +12,20 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 5 + const val N_DIST = 50 const val N_FEAS = 5 const val N_TABU = 50 const val N_PENALTY = 2 const val COOLING_FACTOR = 0.9 const val TABU_TENURE_MIN = 15 const val TABU_TENURE_MAX = 30 - const val ALPHA = 2 - const val BETA = 2 - const val GAMMA = 2 + const val NO_CHANGE_THRESHOLD = 3 + const val ALPHA_DEFAULT = 1.0 + const val BETA_DEFAULT = 1.0 + const val GAMMA_DEFAULT = 1.0 + const val PARAM_INCREASE_RATE = 0.3 + var ALPHA = ALPHA_DEFAULT + var BETA = BETA_DEFAULT + var GAMMA = GAMMA_DEFAULT } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 75fb94a..8cbe5aa 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -5,13 +5,22 @@ import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure import at.ac.tuwien.otl.evrptw.dto.Route +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.ALPHA +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.ALPHA_DEFAULT +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.BETA +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.BETA_DEFAULT import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.COOLING_FACTOR +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.GAMMA +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.GAMMA_DEFAULT +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.NO_CHANGE_THRESHOLD import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.PARAM_INCREASE_RATE import at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch.TabuSearch import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import java.util.Random import java.util.logging.Logger +import kotlin.streams.toList /** *

About this class

@@ -27,10 +36,14 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe private val neighbourSolutionGenerator = ShakingNeighbourSolutionGenerator() private val tabuSearch = TabuSearch(logEnabled) private val random = Random(java.lang.Double.doubleToLongBits(Math.random())) + private val lastSavedSolutions = mutableListOf() private var temperature = 0.0 + private var thresholdCounter = 0 override fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution { temperature = Main.instanceToInitTemperatureMap[evrptwSolution.instance.name]!! + lastSavedSolutions.clear() + thresholdCounter = 0 var bestFeasibleSolution = evrptwSolution var bestSolution = evrptwSolution var k = 1 @@ -40,6 +53,11 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe while (feasibilityPhase || (!feasibilityPhase && i < N_DIST)) { val newSolution = neighbourSolutionGenerator.generateRandomPoint(bestSolution, k) val optimizedNewSolution = tabuSearch.apply(newSolution) + lastSavedSolutions.add(optimizedNewSolution) + if (lastSavedSolutions.size > NO_CHANGE_THRESHOLD) { + lastSavedSolutions.removeAt(0) + } + adjustParameters() if (acceptSimulatedAnnealing(optimizedNewSolution, bestSolution)) { bestSolution = optimizedNewSolution @@ -65,10 +83,35 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe } i++ } - return bestFeasibleSolution } + private fun adjustParameters() { + val lastSolution = lastSavedSolutions.last() + if (lastSolution.fitnessValue.fitness == lastSolution.cost) { + thresholdCounter++ + } else { + thresholdCounter-- + } + if (thresholdCounter == NO_CHANGE_THRESHOLD) { + ALPHA = ALPHA_DEFAULT + BETA = BETA_DEFAULT + GAMMA = GAMMA_DEFAULT + log("PARAMETERS SET BACK TO DEFAULT = ($ALPHA, $BETA, $GAMMA)") + } else if (thresholdCounter == -NO_CHANGE_THRESHOLD) { + val numberOfCapacityViolations = + lastSavedSolutions.stream().filter { it.fitnessValue.totalCapacityViolation > 0.0 }.toList().size + val numberOfTimeWindowViolations = + lastSavedSolutions.stream().filter { it.fitnessValue.totalTimeWindowViolation > 0.0 }.toList().size + val numberOfBatteryCapacityViolations = + lastSavedSolutions.stream().filter { it.fitnessValue.totalBatteryCapacityViolation > 0.0 }.toList().size + ALPHA += PARAM_INCREASE_RATE * numberOfCapacityViolations + BETA += PARAM_INCREASE_RATE * numberOfTimeWindowViolations + GAMMA += PARAM_INCREASE_RATE * numberOfBatteryCapacityViolations + log("PARAMETERS INCREASED = ($ALPHA, $BETA, $GAMMA)") + } + } + private fun acceptSimulatedAnnealing(optimizedNewSolution: EVRPTWSolution, bestSolution: EVRPTWSolution): Boolean { val accept: Boolean accept = if (optimizedNewSolution.fitnessValue.fitness == optimizedNewSolution.cost) { From d9e5ee739580cc2776268cb19a99441774a7a837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Mon, 25 Jun 2018 01:44:09 +0200 Subject: [PATCH 23/36] StationInRe: insert station only, if node before current node is not a station; remove station only if route is larger than 3; HybridVnsTsMetaheuristic: reduce violation factors dynamically --- solutions/c103_21_sol.txt | 29 +++++++-------- solutions/c105_21_sol.txt | 28 +++++++-------- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../otl/evrptw/metaheuristic/Constants.kt | 9 ++--- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 23 +++++++----- .../ShakingNeighbourSolutionGenerator.kt | 3 ++ .../neighbourhood/StationInReExplorer.kt | 36 ++++++++++--------- 7 files changed, 68 insertions(+), 62 deletions(-) diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index 2a0d64e..35b2486 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,18 +1,15 @@ # solution for c103_21 -1277.0315861559218 -D0, S0, S7, C12, C14, C16, C19, C18, C17, C15, C13, D0 -D0, C91, C89, C88, C85, C84, C82, C83, D0 -D0, C63, C74, C72, C61, C66, D0 -D0, C90, C87, C86, S20, C77, C80, C79, D0 -D0, C23, C25, C27, C30, C28, C26, C10, C11, D0 -D0, C59, C44, C42, C43, D0 -D0, C98, C95, C94, S4, C92, C93, C97, C100, C99, D0 -D0, C7, C3, C5, D0 -D0, C34, C36, C39, S12, C38, C37, C35, C31, D0 -D0, S20, C73, C70, C71, C76, C78, C81, D0 -D0, C41, C40, D0 -D0, C21, C22, C24, C29, C33, C32, C20, D0 -D0, C67, C65, C62, C64, C68, C69, D0 -D0, C75, C96, S3, C1, C2, C4, C6, C9, C8, D0 -D0, C46, C45, C48, C51, C50, C52, C47, C49, D0 +1147.6646074163434 +D0, S0, C20, C32, C33, C35, C31, D0 +D0, C24, C29, C39, S12, C38, C37, C36, C34, C30, C22, C21, D0 +D0, C91, C89, C88, C85, C84, C82, C83, C86, C87, C90, D0 +D0, S15, C59, C44, C45, C48, C51, C50, C52, C49, D0 +D0, C23, C25, C27, C28, C26, C10, C11, C9, C8, D0 +D0, C12, C14, C16, C19, C15, S7, C18, C17, C13, D0 D0, S14, C60, C58, C56, C53, C54, C55, C57, D0 +D0, C75, C1, C2, C4, C6, C7, C3, C5, D0 +D0, C67, C65, S20, C77, C80, C79, C63, D0 +D0, C62, C74, C72, C61, C66, D0 +D0, S20, C73, C70, C71, C76, C78, C81, D0 +D0, C98, C96, C95, C94, C92, C93, C97, C100, C99, S3, D0 +D0, C41, C40, C42, C46, C47, C43, C68, C64, C69, D0 diff --git a/solutions/c105_21_sol.txt b/solutions/c105_21_sol.txt index aad3ed0..7f24569 100644 --- a/solutions/c105_21_sol.txt +++ b/solutions/c105_21_sol.txt @@ -1,17 +1,15 @@ # solution for c105_21 -1376.958260332306 -D0, S0, S7, C14, C16, C19, C18, C17, C15, C13, D0 -D0, C59, S18, C80, C72, C61, C66, D0 -D0, C96, S4, C94, C92, C93, C97, C100, C99, D0 -D0, C75, C1, C12, S5, C6, C7, C10, C11, C9, C8, D0 -D0, C98, C95, C2, C4, C3, C5, D0 -D0, C20, C24, S12, C39, C38, C37, C35, C51, C50, C52, C49, D0 -D0, C32, C33, C31, D0 -D0, C23, C29, C34, C36, D0 -D0, S20, C73, C70, C79, C77, C81, D0 -D0, C63, C74, S20, C71, C76, C78, D0 -D0, C21, C22, C25, C27, C30, C28, C26, D0 -D0, S14, C60, C58, C56, C53, C54, C55, C57, D0 -D0, C67, C65, C62, C64, C68, C69, D0 -D0, C41, C40, C42, C44, C45, C48, C46, C47, C43, D0 +1238.9366341686357 +D0, S0, C41, C40, C42, C44, C45, C48, C51, C50, C52, C49, D0 +D0, C12, C14, C16, C15, S7, C13, D0 +D0, C59, C60, C58, C56, C53, S14, C54, C55, C57, D0 +D0, C23, S7, C19, C18, C17, C6, C7, D0 +D0, C67, C65, C62, C64, C68, C69, C46, C47, C43, D0 +D0, C21, C22, C25, C27, C30, C28, C26, C10, C11, C9, C8, D0 +D0, C20, C34, S12, C39, C38, C37, C35, D0 +D0, C98, S4, C94, C92, C93, C97, C100, C99, D0 +D0, S20, C73, C70, C71, C76, C78, C81, D0 +D0, C63, C74, S20, C79, C77, C80, C72, C61, C66, D0 D0, C90, C87, C86, C82, C83, C84, C85, C88, C89, C91, D0 +D0, C24, C29, C36, C32, C33, C31, S13, D0 +D0, C75, C1, C96, C95, S3, C2, C4, C3, C5, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 422fc26..8d8d585 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -64,7 +64,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 0 until 1) { + for (i in 1 until 2) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index bd0e8f0..f6b11c9 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,18 +12,19 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 50 + const val N_DIST = 15 const val N_FEAS = 5 - const val N_TABU = 50 + const val N_TABU = 30 const val N_PENALTY = 2 const val COOLING_FACTOR = 0.9 const val TABU_TENURE_MIN = 15 const val TABU_TENURE_MAX = 30 - const val NO_CHANGE_THRESHOLD = 3 + const val NO_CHANGE_THRESHOLD = 2 const val ALPHA_DEFAULT = 1.0 const val BETA_DEFAULT = 1.0 const val GAMMA_DEFAULT = 1.0 - const val PARAM_INCREASE_RATE = 0.3 + const val VIOLATION_FACTOR_INCREASE_RATE = 0.5 + const val VIOLATION_FACTOR_DECREASE_RATE = 0.5 var ALPHA = ALPHA_DEFAULT var BETA = BETA_DEFAULT var GAMMA = GAMMA_DEFAULT diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 8cbe5aa..ddbb6f3 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -15,7 +15,8 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.GAMMA_DEFAULT import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.NO_CHANGE_THRESHOLD import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS -import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.PARAM_INCREASE_RATE +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_DECREASE_RATE +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_INCREASE_RATE import at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch.TabuSearch import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import java.util.Random @@ -51,6 +52,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe var feasibilityPhase = true while (feasibilityPhase || (!feasibilityPhase && i < N_DIST)) { + println("VNS Iteration: $i") val newSolution = neighbourSolutionGenerator.generateRandomPoint(bestSolution, k) val optimizedNewSolution = tabuSearch.apply(newSolution) lastSavedSolutions.add(optimizedNewSolution) @@ -62,6 +64,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe if (acceptSimulatedAnnealing(optimizedNewSolution, bestSolution)) { bestSolution = optimizedNewSolution if (optimizedNewSolution.fitnessValue.fitness == optimizedNewSolution.cost) { + log("New best feasible solution with cost ${optimizedNewSolution.cost}") bestFeasibleSolution = optimizedNewSolution } k = 1 @@ -94,10 +97,11 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe thresholdCounter-- } if (thresholdCounter == NO_CHANGE_THRESHOLD) { - ALPHA = ALPHA_DEFAULT - BETA = BETA_DEFAULT - GAMMA = GAMMA_DEFAULT - log("PARAMETERS SET BACK TO DEFAULT = ($ALPHA, $BETA, $GAMMA)") + ALPHA = if (ALPHA > ALPHA_DEFAULT) ALPHA_DEFAULT else ALPHA - VIOLATION_FACTOR_DECREASE_RATE + BETA = if (BETA > BETA_DEFAULT) BETA_DEFAULT else BETA - VIOLATION_FACTOR_DECREASE_RATE + GAMMA = if (GAMMA > GAMMA_DEFAULT) GAMMA_DEFAULT else GAMMA - VIOLATION_FACTOR_DECREASE_RATE + thresholdCounter = 0 + log("-- VIOLATION FACTORS DECREASED = ($ALPHA, $BETA, $GAMMA)") } else if (thresholdCounter == -NO_CHANGE_THRESHOLD) { val numberOfCapacityViolations = lastSavedSolutions.stream().filter { it.fitnessValue.totalCapacityViolation > 0.0 }.toList().size @@ -105,10 +109,11 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe lastSavedSolutions.stream().filter { it.fitnessValue.totalTimeWindowViolation > 0.0 }.toList().size val numberOfBatteryCapacityViolations = lastSavedSolutions.stream().filter { it.fitnessValue.totalBatteryCapacityViolation > 0.0 }.toList().size - ALPHA += PARAM_INCREASE_RATE * numberOfCapacityViolations - BETA += PARAM_INCREASE_RATE * numberOfTimeWindowViolations - GAMMA += PARAM_INCREASE_RATE * numberOfBatteryCapacityViolations - log("PARAMETERS INCREASED = ($ALPHA, $BETA, $GAMMA)") + ALPHA += VIOLATION_FACTOR_INCREASE_RATE * numberOfCapacityViolations + BETA += VIOLATION_FACTOR_INCREASE_RATE * numberOfTimeWindowViolations + GAMMA += VIOLATION_FACTOR_INCREASE_RATE * numberOfBatteryCapacityViolations + thresholdCounter = 0 + log("++ VIOLATION FACTORS INCREASED = ($ALPHA, $BETA, $GAMMA)") } } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt index c028c33..c9bf3db 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt @@ -38,6 +38,9 @@ class ShakingNeighbourSolutionGenerator { for (route in routes) { val upperBound = Math.min(neighbourhoodStructure.maxVertices, route.size - 2) + if (upperBound == 0) { + println("ERROR UPPER BOUND. Route: $route") + } val numberOfSuccessiveVertices = if (upperBound == 1) { 1 } else { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt index 4ca3468..633d31d 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt @@ -20,12 +20,14 @@ class StationInReExplorer : INeighbourhoodExplorer { for (routeIndex in 0 until initialSolution.routes.size) { val route = initialSolution.routes[routeIndex] for (nodeIndex in 1 until route.size) { - if (route[nodeIndex] is EVRPTWInstance.RechargingStation) { + if (route[nodeIndex] is EVRPTWInstance.RechargingStation && route.size > 3) { result.add(performStationRemoval(initialSolution, routeIndex, nodeIndex)) } else { - for (station in initialSolution.instance.rechargingStations) { - val neighbourSolution = performStationInsertion(initialSolution, routeIndex, nodeIndex, station) - result.add(neighbourSolution) + if (route[nodeIndex - 1] !is EVRPTWInstance.RechargingStation) { + for (station in initialSolution.instance.rechargingStations) { + val neighbourSolution = performStationInsertion(initialSolution, routeIndex, nodeIndex, station) + result.add(neighbourSolution) + } } } } @@ -41,9 +43,9 @@ class StationInReExplorer : INeighbourhoodExplorer { val routes = initialSolution.copyOfRoutes() routes[routeIndex].removeAt(nodeIndex) return EVRPTWSolution( - initialSolution.instance, - routes, - Route.calculateTotalDistance(routes, initialSolution.instance) + initialSolution.instance, + routes, + Route.calculateTotalDistance(routes, initialSolution.instance) ) } @@ -55,20 +57,20 @@ class StationInReExplorer : INeighbourhoodExplorer { ): EVRPTWSolution { val routes = initialSolution.copyOfRoutes() val stationToInsert = EVRPTWInstance.RechargingStation( - station.id, - station.name, - station.location.x, - station.location.y, - station.timeWindow.start, - station.timeWindow.end, - station.rechargingRate + station.id, + station.name, + station.location.x, + station.location.y, + station.timeWindow.start, + station.timeWindow.end, + station.rechargingRate ) routes[routeIndex].add(nodeIndex, stationToInsert) return EVRPTWSolution( - initialSolution.instance, - routes, - Route.calculateTotalDistance(routes, initialSolution.instance) + initialSolution.instance, + routes, + Route.calculateTotalDistance(routes, initialSolution.instance) ) } } \ No newline at end of file From d50cdb13ff5e5d50ca5ddb3fe85865e0ecd11773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Mon, 25 Jun 2018 11:37:42 +0200 Subject: [PATCH 24/36] fix adaptation of violation factor issue whereby in the decrease phase negative factors were also possible; Add fitness to log --- solutions/c204_21_sol.txt | 22 +++++-------------- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../otl/evrptw/metaheuristic/Constants.kt | 11 +++++----- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 11 +++++++--- .../metaheuristic/tabusearch/TabuSearch.kt | 3 ++- 5 files changed, 23 insertions(+), 26 deletions(-) diff --git a/solutions/c204_21_sol.txt b/solutions/c204_21_sol.txt index 4f6a4d0..532e540 100644 --- a/solutions/c204_21_sol.txt +++ b/solutions/c204_21_sol.txt @@ -1,17 +1,7 @@ # solution for c204_21 -1572.2854629371848 -D0, C93, C5, C75, C2, C1, C99, C100, C97, C95, C94, C98, C7, C3, C4, C89, C91, C90, D0 -D0, C20, C22, C24, C30, C25, C9, C11, C13, C17, C19, C18, C23, D0 -D0, C48, C43, C42, C41, C45, C50, C51, C47, C46, C44, C57, C55, C54, C59, D0 -D0, C8, C10, C12, C14, C16, C26, C28, C29, C27, D0 -D0, C67, C63, C62, C66, C69, C64, C61, C74, C68, C65, C49, C40, D0 -D0, C87, C77, C78, C85, C76, C81, C96, D0 -D0, C52, C33, C34, C36, C37, C35, C38, C31, C32, C6, D0 -D0, C88, C84, C86, C83, C82, C72, D0 -D0, C15, C39, D0 -D0, C21, D0 -D0, C56, C58, C60, C53, D0 -D0, C92, D0 -D0, C73, C70, C79, D0 -D0, C71, D0 -D0, C80, D0 +673.5281551536671 +D0, C8, C10, C11, C9, C13, C15, C12, C14, C16, C19, C18, C17, C25, S9, C23, C26, C28, C34, C36, C39, C38, C37, C35, C31, C33, C32, C6, C29, C30, C27, C24, C22, C21, D0 +D0, S0, C20, C52, C50, C51, C45, C46, C44, C40, C57, C54, C53, C56, C58, C60, C59, C55, C49, C65, C68, S15, C41, C42, C47, C43, C48, D0 +D0, C87, C86, C84, C83, C82, S1, C77, C85, C76, C71, C70, C73, C80, C79, C81, C78, S19, C96, C74, C62, C66, C69, C64, C61, C72, C63, C67, D0 +D0, S0, D0 +D0, C93, C5, C75, C2, C1, C95, C94, C92, C97, C100, C99, S3, C98, C7, C3, C4, C89, C91, C88, C90, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 8d8d585..131f5f6 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -64,7 +64,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 1 until 2) { + for (i in 2 until 3) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index f6b11c9..eb101ce 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,19 +12,20 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 15 + const val N_DIST = 30 const val N_FEAS = 5 - const val N_TABU = 30 + const val N_TABU = 50 const val N_PENALTY = 2 const val COOLING_FACTOR = 0.9 const val TABU_TENURE_MIN = 15 const val TABU_TENURE_MAX = 30 - const val NO_CHANGE_THRESHOLD = 2 + const val NO_CHANGE_THRESHOLD = 3 const val ALPHA_DEFAULT = 1.0 const val BETA_DEFAULT = 1.0 const val GAMMA_DEFAULT = 1.0 - const val VIOLATION_FACTOR_INCREASE_RATE = 0.5 - const val VIOLATION_FACTOR_DECREASE_RATE = 0.5 + const val VIOLATION_FACTOR_MIN = 0.3 + const val VIOLATION_FACTOR_INCREASE_RATE = 0.3 + const val VIOLATION_FACTOR_DECREASE_RATE = 0.1 var ALPHA = ALPHA_DEFAULT var BETA = BETA_DEFAULT var GAMMA = GAMMA_DEFAULT diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index ddbb6f3..0f69348 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -17,6 +17,7 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_DECREASE_RATE import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_INCREASE_RATE +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_MIN import at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch.TabuSearch import at.ac.tuwien.otl.evrptw.verifier.EVRPTWRouteVerifier import java.util.Random @@ -62,6 +63,10 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe adjustParameters() if (acceptSimulatedAnnealing(optimizedNewSolution, bestSolution)) { + log("$$$ New best solution $$$. Cost: ${optimizedNewSolution.cost}" + "Cap-Violation: ${optimizedNewSolution.fitnessValue.totalCapacityViolation}, " + + "TW-Violation: ${optimizedNewSolution.fitnessValue.totalTimeWindowViolation}, " + + "Bat-Violation: ${optimizedNewSolution.fitnessValue.totalBatteryCapacityViolation}, " + + "Fitness: ${optimizedNewSolution.fitnessValue.fitness}") bestSolution = optimizedNewSolution if (optimizedNewSolution.fitnessValue.fitness == optimizedNewSolution.cost) { log("New best feasible solution with cost ${optimizedNewSolution.cost}") @@ -97,9 +102,9 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe thresholdCounter-- } if (thresholdCounter == NO_CHANGE_THRESHOLD) { - ALPHA = if (ALPHA > ALPHA_DEFAULT) ALPHA_DEFAULT else ALPHA - VIOLATION_FACTOR_DECREASE_RATE - BETA = if (BETA > BETA_DEFAULT) BETA_DEFAULT else BETA - VIOLATION_FACTOR_DECREASE_RATE - GAMMA = if (GAMMA > GAMMA_DEFAULT) GAMMA_DEFAULT else GAMMA - VIOLATION_FACTOR_DECREASE_RATE + ALPHA = if (ALPHA > ALPHA_DEFAULT) ALPHA_DEFAULT else Math.max(ALPHA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) + BETA = if (BETA > BETA_DEFAULT) BETA_DEFAULT else Math.max(BETA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) + GAMMA = if (GAMMA > GAMMA_DEFAULT) GAMMA_DEFAULT else Math.max(GAMMA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) thresholdCounter = 0 log("-- VIOLATION FACTORS DECREASED = ($ALPHA, $BETA, $GAMMA)") } else if (thresholdCounter == -NO_CHANGE_THRESHOLD) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 87f0c20..6bf0aed 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -40,7 +40,8 @@ class TabuSearch(private val logEnabled: Boolean = true) { "New local optimum found. Cost: ${overallBestSolution.cost}, " + "Cap-Violation: ${overallBestSolution.fitnessValue.totalCapacityViolation}, " + "TW-Violation: ${overallBestSolution.fitnessValue.totalTimeWindowViolation}, " + - "Bat-Violation: ${overallBestSolution.fitnessValue.totalBatteryCapacityViolation}" + "Bat-Violation: ${overallBestSolution.fitnessValue.totalBatteryCapacityViolation}, " + + "Fitness: ${overallBestSolution.fitnessValue.fitness}" ) } iteration++ From bb20cf0edd86da48b247844cc27fd125f9a0cf35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Mon, 25 Jun 2018 12:10:44 +0200 Subject: [PATCH 25/36] adjust explorers s.t. start and end indices can be provided in order to better support multithreading --- solutions/c204_21_sol.txt | 12 ++++++------ .../neighbourhood/INeighbourhoodExplorer.kt | 2 +- .../InterIntraRouteExchangeExplorer.kt | 6 +++--- .../InterIntraRouteRelocateExplorer.kt | 6 +++--- .../neighbourhood/StationInReExplorer.kt | 4 ++-- .../neighbourhood/TwoOptArcExchangeExplorer.kt | 6 +++--- .../callable/INeighbourhoodExplorerCallable.kt | 8 +++++--- .../InterIntraRouteExchangeExplorerCallable.kt | 8 +++++--- .../InterIntraRouteRelocateExplorerCallable.kt | 8 +++++--- .../callable/StationInReExplorerCallable.kt | 9 +++++++-- .../callable/TwoOptArcExchangeExplorerCallable.kt | 9 +++++++-- .../evrptw/metaheuristic/tabusearch/TabuSearch.kt | 14 ++++++++++---- 12 files changed, 57 insertions(+), 35 deletions(-) diff --git a/solutions/c204_21_sol.txt b/solutions/c204_21_sol.txt index 532e540..6a33dc1 100644 --- a/solutions/c204_21_sol.txt +++ b/solutions/c204_21_sol.txt @@ -1,7 +1,7 @@ # solution for c204_21 -673.5281551536671 -D0, C8, C10, C11, C9, C13, C15, C12, C14, C16, C19, C18, C17, C25, S9, C23, C26, C28, C34, C36, C39, C38, C37, C35, C31, C33, C32, C6, C29, C30, C27, C24, C22, C21, D0 -D0, S0, C20, C52, C50, C51, C45, C46, C44, C40, C57, C54, C53, C56, C58, C60, C59, C55, C49, C65, C68, S15, C41, C42, C47, C43, C48, D0 -D0, C87, C86, C84, C83, C82, S1, C77, C85, C76, C71, C70, C73, C80, C79, C81, C78, S19, C96, C74, C62, C66, C69, C64, C61, C72, C63, C67, D0 -D0, S0, D0 -D0, C93, C5, C75, C2, C1, C95, C94, C92, C97, C100, C99, S3, C98, C7, C3, C4, C89, C91, C88, C90, D0 +745.044079844661 +D0, S0, S0, S0, C8, C9, C13, C15, C12, C14, C16, C19, C18, C17, C25, C30, S9, C23, C26, C28, C34, C36, C39, C38, C37, C35, C31, C33, C32, C6, C29, C27, C24, C20, C22, C21, D0 +D0, C90, C86, C83, C82, C85, C76, C71, C70, C73, C80, C79, C81, C78, S19, C96, C74, C72, C61, C64, C66, C62, C63, C67, D0 +D0, C93, C10, C11, C5, C75, C2, C1, C99, C100, C97, C98, C4, C91, S0, S0, D0 +D0, C89, C7, C95, C94, C92, C3, C88, C84, S1, C77, C87, D0 +D0, C48, C43, C41, C42, C47, S13, C52, C50, C51, C45, C46, C44, C40, C57, C59, C60, C58, C56, C53, C54, C55, C49, C65, C68, C69, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/INeighbourhoodExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/INeighbourhoodExplorer.kt index f743bc6..e8e1399 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/INeighbourhoodExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/INeighbourhoodExplorer.kt @@ -20,5 +20,5 @@ interface INeighbourhoodExplorer { * be calculated * @return a list of neighbour solutions, not necessarily sorted */ - fun exploreEverySolution(initialSolution: T): List + fun exploreEverySolution(initialSolution: T, startAtIncl: Int, endAtExcl: Int): List } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt index ce76e9a..990ce6b 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt @@ -24,12 +24,12 @@ class InterIntraRouteExchangeExplorer : INeighbourhoodExplorer { * be calculated * @return a list of neighbour solutions, not necessarily sorted */ - override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { + override fun exploreEverySolution(initialSolution: EVRPTWSolution, startAtIncl: Int, endAtExcl: Int): List { val result = mutableListOf() - for (routeIndex in 0 until initialSolution.routes.size) { + for (routeIndex in startAtIncl until endAtExcl) { val route = initialSolution.routes[routeIndex] - for (secondRouteIndex in routeIndex until initialSolution.routes.size) { + for (secondRouteIndex in routeIndex until endAtExcl) { val secondRoute = initialSolution.routes[secondRouteIndex] for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot if (route[nodeOfFirstRoute] is EVRPTWInstance.RechargingStation) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt index a9c2827..c05dea8 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt @@ -22,12 +22,12 @@ class InterIntraRouteRelocateExplorer : INeighbourhoodExplorer { * be calculated * @return a list of neighbour solutions, not necessarily sorted */ - override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { + override fun exploreEverySolution(initialSolution: EVRPTWSolution, startAtIncl: Int, endAtExcl: Int): List { val result = mutableListOf() - for (routeIndex in 0 until initialSolution.routes.size) { + for (routeIndex in startAtIncl until endAtExcl) { val route = initialSolution.routes[routeIndex] if (route.size > 3) { - for (secondRouteIndex in routeIndex until initialSolution.routes.size) { + for (secondRouteIndex in routeIndex until endAtExcl) { val secondRoute = initialSolution.routes[secondRouteIndex] for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot for (nodeOfSecondRoute in 1 until secondRoute.size - 1) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt index 633d31d..5ca4932 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt @@ -15,9 +15,9 @@ import at.ac.tuwien.otl.evrptw.dto.Route */ class StationInReExplorer : INeighbourhoodExplorer { - override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { + override fun exploreEverySolution(initialSolution: EVRPTWSolution, startAtIncl: Int, endAtExcl: Int): List { val result = mutableListOf() - for (routeIndex in 0 until initialSolution.routes.size) { + for (routeIndex in startAtIncl until endAtExcl) { val route = initialSolution.routes[routeIndex] for (nodeIndex in 1 until route.size) { if (route[nodeIndex] is EVRPTWInstance.RechargingStation && route.size > 3) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt index 8e6ec87..e8b252f 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt @@ -15,12 +15,12 @@ import at.ac.tuwien.otl.evrptw.dto.Route class TwoOptArcExchangeExplorer : INeighbourhoodExplorer { - override fun exploreEverySolution(initialSolution: EVRPTWSolution): List { + override fun exploreEverySolution(initialSolution: EVRPTWSolution, startAtIncl: Int, endAtExcl: Int): List { val result = mutableListOf() - for (routeIndex in 0 until initialSolution.routes.size) { + for (routeIndex in startAtIncl until endAtExcl) { val route = initialSolution.routes[routeIndex] - for (secondRouteIndex in (routeIndex + 1) until initialSolution.routes.size) { + for (secondRouteIndex in (routeIndex + 1) until endAtExcl) { val secondRoute = initialSolution.routes[secondRouteIndex] for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot for (nodeOfSecondRoute in 1 until secondRoute.size - 1) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt index db40853..92cfe99 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt @@ -13,8 +13,10 @@ import java.util.concurrent.Callable * @since 1.0.0 */ abstract class INeighbourhoodExplorerCallable( - private val initialSolution: T, - private val explorer: INeighbourhoodExplorer + private val initialSolution: T, + private val startAtIncl: Int, + private val endAtIncl: Int, + private val explorer: INeighbourhoodExplorer ) : Callable> { /** @@ -24,6 +26,6 @@ abstract class INeighbourhoodExplorerCallable( * @throws Exception if unable to compute a result */ override fun call(): List { - return explorer.exploreEverySolution(initialSolution) + return explorer.exploreEverySolution(initialSolution, startAtIncl, endAtIncl) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt index c2e8d69..1400524 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt @@ -13,6 +13,8 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteExchan * @since 1.0.0 */ class InterIntraRouteExchangeExplorerCallable( - initialSolution: EVRPTWSolution, - explorer: InterIntraRouteExchangeExplorer -) : INeighbourhoodExplorerCallable(initialSolution, explorer) \ No newline at end of file + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtIncl: Int, + explorer: InterIntraRouteExchangeExplorer +) : INeighbourhoodExplorerCallable(initialSolution, startAtIncl, endAtIncl, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt index 88edb60..8532bb4 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt @@ -13,6 +13,8 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteReloca * @since 1.0.0 */ class InterIntraRouteRelocateExplorerCallable( - initialSolution: EVRPTWSolution, - explorer: InterIntraRouteRelocateExplorer -) : INeighbourhoodExplorerCallable(initialSolution, explorer) \ No newline at end of file + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtIncl: Int, + explorer: InterIntraRouteRelocateExplorer +) : INeighbourhoodExplorerCallable(initialSolution, startAtIncl, endAtIncl, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt index b07011d..ea0311c 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt @@ -12,5 +12,10 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.StationInReExplorer * @version 1.0.0 * @since 1.0.0 */ -class StationInReExplorerCallable(initialSolution: EVRPTWSolution, explorer: StationInReExplorer) : - INeighbourhoodExplorerCallable(initialSolution, explorer) \ No newline at end of file +class StationInReExplorerCallable( + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtIncl: Int, + explorer: StationInReExplorer +) : + INeighbourhoodExplorerCallable(initialSolution, startAtIncl, endAtIncl, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt index 4fcbf2b..f7e9095 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt @@ -12,5 +12,10 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExpl * @version 1.0.0 * @since 1.0.0 */ -class TwoOptArcExchangeExplorerCallable(initialSolution: EVRPTWSolution, explorer: TwoOptArcExchangeExplorer) : - INeighbourhoodExplorerCallable(initialSolution, explorer) \ No newline at end of file +class TwoOptArcExchangeExplorerCallable( + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtIncl: Int, + explorer: TwoOptArcExchangeExplorer +) : + INeighbourhoodExplorerCallable(initialSolution, startAtIncl, endAtIncl, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 6bf0aed..53bbbae 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -67,10 +67,16 @@ class TabuSearch(private val logEnabled: Boolean = true) { private fun parallelExploreNeighbourhoods(solution: EVRPTWSolution): List { val callableList = mutableListOf>() - callableList.add(TwoOptArcExchangeExplorerCallable(solution, TwoOptArcExchangeExplorer())) - callableList.add(StationInReExplorerCallable(solution, StationInReExplorer())) - callableList.add(InterIntraRouteExchangeExplorerCallable(solution, InterIntraRouteExchangeExplorer())) - callableList.add(InterIntraRouteRelocateExplorerCallable(solution, InterIntraRouteRelocateExplorer())) + val numberOfRoutes = solution.routes.size + val middleOfRoutes = numberOfRoutes / 2 + callableList.add(TwoOptArcExchangeExplorerCallable(solution, 0, middleOfRoutes, TwoOptArcExchangeExplorer())) + callableList.add(TwoOptArcExchangeExplorerCallable(solution, middleOfRoutes, numberOfRoutes, TwoOptArcExchangeExplorer())) + callableList.add(StationInReExplorerCallable(solution, 0, middleOfRoutes, StationInReExplorer())) + callableList.add(StationInReExplorerCallable(solution, middleOfRoutes, numberOfRoutes, StationInReExplorer())) + callableList.add(InterIntraRouteExchangeExplorerCallable(solution, 0, middleOfRoutes, InterIntraRouteExchangeExplorer())) + callableList.add(InterIntraRouteExchangeExplorerCallable(solution, middleOfRoutes, numberOfRoutes, InterIntraRouteExchangeExplorer())) + callableList.add(InterIntraRouteRelocateExplorerCallable(solution, 0, middleOfRoutes, InterIntraRouteRelocateExplorer())) + callableList.add(InterIntraRouteRelocateExplorerCallable(solution, middleOfRoutes, numberOfRoutes, InterIntraRouteRelocateExplorer())) val results = Executor.getExecutorService().invokeAll(callableList) return results.stream().flatMap { it.get().stream() }.collect(Collectors.toList()).toList() } From b08642f046b689e69c2c35aabfaa6d1861de9d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Mon, 25 Jun 2018 12:49:52 +0200 Subject: [PATCH 26/36] add Operator enum to gather information about which operator induced the best local optima --- solutions/c204_21_sol.txt | 11 +++++------ .../ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt | 9 +++++---- .../at/ac/tuwien/otl/evrptw/dto/Operator.kt | 17 +++++++++++++++++ .../otl/evrptw/metaheuristic/Constants.kt | 6 +++--- .../InterIntraRouteExchangeExplorer.kt | 4 +++- .../InterIntraRouteRelocateExplorer.kt | 4 +++- .../neighbourhood/StationInReExplorer.kt | 4 +++- .../neighbourhood/TwoOptArcExchangeExplorer.kt | 3 ++- .../metaheuristic/tabusearch/TabuSearch.kt | 2 +- 9 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Operator.kt diff --git a/solutions/c204_21_sol.txt b/solutions/c204_21_sol.txt index 6a33dc1..995180d 100644 --- a/solutions/c204_21_sol.txt +++ b/solutions/c204_21_sol.txt @@ -1,7 +1,6 @@ # solution for c204_21 -745.044079844661 -D0, S0, S0, S0, C8, C9, C13, C15, C12, C14, C16, C19, C18, C17, C25, C30, S9, C23, C26, C28, C34, C36, C39, C38, C37, C35, C31, C33, C32, C6, C29, C27, C24, C20, C22, C21, D0 -D0, C90, C86, C83, C82, C85, C76, C71, C70, C73, C80, C79, C81, C78, S19, C96, C74, C72, C61, C64, C66, C62, C63, C67, D0 -D0, C93, C10, C11, C5, C75, C2, C1, C99, C100, C97, C98, C4, C91, S0, S0, D0 -D0, C89, C7, C95, C94, C92, C3, C88, C84, S1, C77, C87, D0 -D0, C48, C43, C41, C42, C47, S13, C52, C50, C51, C45, C46, C44, C40, C57, C59, C60, C58, C56, C53, C54, C55, C49, C65, C68, C69, D0 +716.8507104000043 +D0, S0, C25, C9, C13, C15, C12, C14, C16, C19, C18, C17, C23, C26, S10, C28, C34, C36, C39, C38, C37, C35, C31, C33, C32, C6, C29, C30, C27, C24, C20, C22, C21, D0 +D0, S0, C90, C86, C83, C82, C85, C76, C71, C70, C73, C80, C79, C81, C78, S19, C96, C72, C61, C64, C62, C74, C63, D0 +D0, C8, C10, C11, C99, C100, C97, C92, C94, C95, C3, C88, C84, S1, C77, C87, C91, C75, C5, C2, C1, C98, C7, C4, C89, C93, D0 +D0, C48, C43, C41, C42, C47, S13, C52, C50, C51, C45, C46, C44, C40, C57, C54, C53, C56, C58, C60, C59, C55, C49, C65, C68, C69, C66, C67, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt index fb532ca..9c4aeb9 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt @@ -17,9 +17,10 @@ import java.util.stream.Collectors * @since 0.1.0 */ data class EVRPTWSolution( - val instance: EVRPTWInstance, - val routes: MutableList>, - val cost: Double + val instance: EVRPTWInstance, + val routes: MutableList>, + val cost: Double, + val originOperator: Operator = Operator.NONE ) { val fitnessValue: FitnessValue = calculateFitnessValue() @@ -138,7 +139,7 @@ data class EVRPTWSolution( } .collect(Collectors.toList()) .toMutableList(), - solution.cost) + solution.cost, solution.originOperator) fun writeToFile() { File("solutions/" + instance.name + "_sol.txt").bufferedWriter().use { out -> diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Operator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Operator.kt new file mode 100644 index 0000000..b6ecaf9 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Operator.kt @@ -0,0 +1,17 @@ +package at.ac.tuwien.otl.evrptw.dto + +/** + *

About this class

+ *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 25.06.2018 + */ +enum class Operator { + NONE, + TWO_OPT, + EXCHANGE, + RELOCATE, + STATION_IN_RE +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index eb101ce..d95bfeb 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,7 +12,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 30 + const val N_DIST = 50 const val N_FEAS = 5 const val N_TABU = 50 const val N_PENALTY = 2 @@ -23,9 +23,9 @@ class Constants private constructor() { const val ALPHA_DEFAULT = 1.0 const val BETA_DEFAULT = 1.0 const val GAMMA_DEFAULT = 1.0 - const val VIOLATION_FACTOR_MIN = 0.3 + const val VIOLATION_FACTOR_MIN = 0.2 const val VIOLATION_FACTOR_INCREASE_RATE = 0.3 - const val VIOLATION_FACTOR_DECREASE_RATE = 0.1 + const val VIOLATION_FACTOR_DECREASE_RATE = 0.2 var ALPHA = ALPHA_DEFAULT var BETA = BETA_DEFAULT var GAMMA = GAMMA_DEFAULT diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt index 990ce6b..c2daeab 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt @@ -2,6 +2,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.Operator import at.ac.tuwien.otl.evrptw.dto.Route /** @@ -76,7 +77,8 @@ class InterIntraRouteExchangeExplorer : INeighbourhoodExplorer { return EVRPTWSolution( initialSolution.instance, routes, - Route.calculateTotalDistance(routes, initialSolution.instance) + Route.calculateTotalDistance(routes, initialSolution.instance), + Operator.EXCHANGE ) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt index c05dea8..f991f19 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt @@ -1,6 +1,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.Operator import at.ac.tuwien.otl.evrptw.dto.Route /** @@ -67,7 +68,8 @@ class InterIntraRouteRelocateExplorer : INeighbourhoodExplorer { return EVRPTWSolution( initialSolution.instance, routes, - Route.calculateTotalDistance(routes, initialSolution.instance) + Route.calculateTotalDistance(routes, initialSolution.instance), + Operator.RELOCATE ) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt index 5ca4932..6b30f82 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/StationInReExplorer.kt @@ -2,6 +2,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.Operator import at.ac.tuwien.otl.evrptw.dto.Route /** @@ -70,7 +71,8 @@ class StationInReExplorer : INeighbourhoodExplorer { return EVRPTWSolution( initialSolution.instance, routes, - Route.calculateTotalDistance(routes, initialSolution.instance) + Route.calculateTotalDistance(routes, initialSolution.instance), + Operator.STATION_IN_RE ) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt index e8b252f..9209b4f 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt @@ -1,6 +1,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.Operator import at.ac.tuwien.otl.evrptw.dto.Route /** @@ -73,6 +74,6 @@ class TwoOptArcExchangeExplorer : routes.removeAt(secondRouteIndex) } - return EVRPTWSolution(solution.instance, routes, Route.calculateTotalDistance(routes, solution.instance)) + return EVRPTWSolution(solution.instance, routes, Route.calculateTotalDistance(routes, solution.instance), Operator.TWO_OPT) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 53bbbae..74f64cb 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -41,7 +41,7 @@ class TabuSearch(private val logEnabled: Boolean = true) { "Cap-Violation: ${overallBestSolution.fitnessValue.totalCapacityViolation}, " + "TW-Violation: ${overallBestSolution.fitnessValue.totalTimeWindowViolation}, " + "Bat-Violation: ${overallBestSolution.fitnessValue.totalBatteryCapacityViolation}, " + - "Fitness: ${overallBestSolution.fitnessValue.fitness}" + "Induced by operator: ${overallBestSolution.originOperator}" ) } iteration++ From 8b0f7cecd1d39b52fc37a2c521b0d9167432889d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Mon, 25 Jun 2018 13:20:41 +0200 Subject: [PATCH 27/36] fix indexing issues introduced by the previous splitting up of neighbourhood searches that caused a lot of possible solutions not to be calculated --- .../kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt | 8 ++++---- .../neighbourhood/InterIntraRouteExchangeExplorer.kt | 2 +- .../neighbourhood/InterIntraRouteRelocateExplorer.kt | 2 +- .../neighbourhood/TwoOptArcExchangeExplorer.kt | 2 +- .../callable/INeighbourhoodExplorerCallable.kt | 8 ++++---- .../callable/InterIntraRouteExchangeExplorerCallable.kt | 8 ++++---- .../callable/InterIntraRouteRelocateExplorerCallable.kt | 8 ++++---- .../neighbourhood/callable/StationInReExplorerCallable.kt | 8 ++++---- .../callable/TwoOptArcExchangeExplorerCallable.kt | 8 ++++---- 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt index 9c4aeb9..dd7901c 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWSolution.kt @@ -17,10 +17,10 @@ import java.util.stream.Collectors * @since 0.1.0 */ data class EVRPTWSolution( - val instance: EVRPTWInstance, - val routes: MutableList>, - val cost: Double, - val originOperator: Operator = Operator.NONE + val instance: EVRPTWInstance, + val routes: MutableList>, + val cost: Double, + val originOperator: Operator = Operator.NONE ) { val fitnessValue: FitnessValue = calculateFitnessValue() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt index c2daeab..ff64895 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteExchangeExplorer.kt @@ -30,7 +30,7 @@ class InterIntraRouteExchangeExplorer : INeighbourhoodExplorer { for (routeIndex in startAtIncl until endAtExcl) { val route = initialSolution.routes[routeIndex] - for (secondRouteIndex in routeIndex until endAtExcl) { + for (secondRouteIndex in routeIndex until initialSolution.routes.size) { val secondRoute = initialSolution.routes[secondRouteIndex] for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot if (route[nodeOfFirstRoute] is EVRPTWInstance.RechargingStation) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt index f991f19..865f06f 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/InterIntraRouteRelocateExplorer.kt @@ -28,7 +28,7 @@ class InterIntraRouteRelocateExplorer : INeighbourhoodExplorer { for (routeIndex in startAtIncl until endAtExcl) { val route = initialSolution.routes[routeIndex] if (route.size > 3) { - for (secondRouteIndex in routeIndex until endAtExcl) { + for (secondRouteIndex in routeIndex until initialSolution.routes.size) { val secondRoute = initialSolution.routes[secondRouteIndex] for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot for (nodeOfSecondRoute in 1 until secondRoute.size - 1) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt index 9209b4f..305bb66 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOptArcExchangeExplorer.kt @@ -21,7 +21,7 @@ class TwoOptArcExchangeExplorer : for (routeIndex in startAtIncl until endAtExcl) { val route = initialSolution.routes[routeIndex] - for (secondRouteIndex in (routeIndex + 1) until endAtExcl) { + for (secondRouteIndex in (routeIndex + 1) until initialSolution.routes.size) { val secondRoute = initialSolution.routes[secondRouteIndex] for (nodeOfFirstRoute in 1 until route.size - 1) { // start at 1 and end -1 before due to depot for (nodeOfSecondRoute in 1 until secondRoute.size - 1) { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt index 92cfe99..9f4265e 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/INeighbourhoodExplorerCallable.kt @@ -13,10 +13,10 @@ import java.util.concurrent.Callable * @since 1.0.0 */ abstract class INeighbourhoodExplorerCallable( - private val initialSolution: T, - private val startAtIncl: Int, - private val endAtIncl: Int, - private val explorer: INeighbourhoodExplorer + private val initialSolution: T, + private val startAtIncl: Int, + private val endAtIncl: Int, + private val explorer: INeighbourhoodExplorer ) : Callable> { /** diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt index 1400524..6b4919c 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteExchangeExplorerCallable.kt @@ -13,8 +13,8 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteExchan * @since 1.0.0 */ class InterIntraRouteExchangeExplorerCallable( - initialSolution: EVRPTWSolution, - startAtIncl: Int, - endAtIncl: Int, - explorer: InterIntraRouteExchangeExplorer + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtIncl: Int, + explorer: InterIntraRouteExchangeExplorer ) : INeighbourhoodExplorerCallable(initialSolution, startAtIncl, endAtIncl, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt index 8532bb4..b1822f7 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/InterIntraRouteRelocateExplorerCallable.kt @@ -13,8 +13,8 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteReloca * @since 1.0.0 */ class InterIntraRouteRelocateExplorerCallable( - initialSolution: EVRPTWSolution, - startAtIncl: Int, - endAtIncl: Int, - explorer: InterIntraRouteRelocateExplorer + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtIncl: Int, + explorer: InterIntraRouteRelocateExplorer ) : INeighbourhoodExplorerCallable(initialSolution, startAtIncl, endAtIncl, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt index ea0311c..94d390b 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/StationInReExplorerCallable.kt @@ -13,9 +13,9 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.StationInReExplorer * @since 1.0.0 */ class StationInReExplorerCallable( - initialSolution: EVRPTWSolution, - startAtIncl: Int, - endAtIncl: Int, - explorer: StationInReExplorer + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtIncl: Int, + explorer: StationInReExplorer ) : INeighbourhoodExplorerCallable(initialSolution, startAtIncl, endAtIncl, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt index f7e9095..eeaa4ea 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOptArcExchangeExplorerCallable.kt @@ -13,9 +13,9 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExpl * @since 1.0.0 */ class TwoOptArcExchangeExplorerCallable( - initialSolution: EVRPTWSolution, - startAtIncl: Int, - endAtIncl: Int, - explorer: TwoOptArcExchangeExplorer + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtIncl: Int, + explorer: TwoOptArcExchangeExplorer ) : INeighbourhoodExplorerCallable(initialSolution, startAtIncl, endAtIncl, explorer) \ No newline at end of file From 552dff233101938ea398b98cfa56f5df0d7e8e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Mon, 25 Jun 2018 16:20:38 +0200 Subject: [PATCH 28/36] change accept criterion for new feasible solutions (experimental) --- solutions/c103_21_sol.txt | 27 ++++++------ solutions/c105_21_sol.txt | 43 +++++++++++++------ solutions/c204_21_sol.txt | 10 ++--- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../otl/evrptw/metaheuristic/Constants.kt | 4 +- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 15 ++++--- 6 files changed, 61 insertions(+), 40 deletions(-) diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index 35b2486..22d0106 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,15 +1,16 @@ # solution for c103_21 -1147.6646074163434 -D0, S0, C20, C32, C33, C35, C31, D0 -D0, C24, C29, C39, S12, C38, C37, C36, C34, C30, C22, C21, D0 -D0, C91, C89, C88, C85, C84, C82, C83, C86, C87, C90, D0 -D0, S15, C59, C44, C45, C48, C51, C50, C52, C49, D0 -D0, C23, C25, C27, C28, C26, C10, C11, C9, C8, D0 -D0, C12, C14, C16, C19, C15, S7, C18, C17, C13, D0 +1215.0610029344487 +D0, C98, C95, C94, S4, C92, C93, C97, C100, C99, C75, D0 +D0, S0, C21, C22, C25, C27, C30, S9, C18, C17, C10, C11, D0 +D0, C59, C40, C44, C45, C46, C47, S0, D0 +D0, C96, C2, C1, C3, C5, D0 +D0, S19, C73, C70, C71, C76, C78, C81, S19, D0 +D0, C63, C74, S19, C79, C77, C80, C72, C61, C66, D0 +D0, C67, C65, C62, C64, C68, C69, C41, C42, C43, D0 +D0, C7, C4, C6, C9, C8, D0 +D0, C12, C14, C16, C19, C15, S7, C13, D0 +D0, C20, C24, C29, C32, C33, C35, S11, C48, C49, D0 +D0, C90, C86, S1, C82, C83, C84, C85, C88, C87, C89, C91, D0 D0, S14, C60, C58, C56, C53, C54, C55, C57, D0 -D0, C75, C1, C2, C4, C6, C7, C3, C5, D0 -D0, C67, C65, S20, C77, C80, C79, C63, D0 -D0, C62, C74, C72, C61, C66, D0 -D0, S20, C73, C70, C71, C76, C78, C81, D0 -D0, C98, C96, C95, C94, C92, C93, C97, C100, C99, S3, D0 -D0, C41, C40, C42, C46, C47, C43, C68, C64, C69, D0 +D0, S13, C52, C50, C51, C31, C38, C37, C39, C36, C34, S13, D0 +D0, C23, C28, C26, D0 diff --git a/solutions/c105_21_sol.txt b/solutions/c105_21_sol.txt index 7f24569..f382e31 100644 --- a/solutions/c105_21_sol.txt +++ b/solutions/c105_21_sol.txt @@ -1,15 +1,30 @@ # solution for c105_21 -1238.9366341686357 -D0, S0, C41, C40, C42, C44, C45, C48, C51, C50, C52, C49, D0 -D0, C12, C14, C16, C15, S7, C13, D0 -D0, C59, C60, C58, C56, C53, S14, C54, C55, C57, D0 -D0, C23, S7, C19, C18, C17, C6, C7, D0 -D0, C67, C65, C62, C64, C68, C69, C46, C47, C43, D0 -D0, C21, C22, C25, C27, C30, C28, C26, C10, C11, C9, C8, D0 -D0, C20, C34, S12, C39, C38, C37, C35, D0 -D0, C98, S4, C94, C92, C93, C97, C100, C99, D0 -D0, S20, C73, C70, C71, C76, C78, C81, D0 -D0, C63, C74, S20, C79, C77, C80, C72, C61, C66, D0 -D0, C90, C87, C86, C82, C83, C84, C85, C88, C89, C91, D0 -D0, C24, C29, C36, C32, C33, C31, S13, D0 -D0, C75, C1, C96, C95, S3, C2, C4, C3, C5, D0 +2029.2143728128362 +D0, C20, C21, C25, C27, C30, C28, C26, C6, C7, D0 +D0, C67, C65, C62, C64, C68, C69, C48, C46, C47, C43, D0 +D0, C23, C22, C29, C32, C33, C31, D0 +D0, C63, C74, C40, C42, C44, C45, C51, C50, D0 +D0, C75, C1, C98, C95, D0 +D0, C24, C34, C36, D0 +D0, C41, C59, C55, D0 +D0, C90, C87, C86, C82, C83, C84, C85, C88, D0 +D0, C96, C2, C4, C3, D0 +D0, C12, C10, D0 +D0, S14, C60, C58, C56, C53, C54, C57, D0 +D0, C14, C11, D0 +D0, S7, C16, C19, C18, C17, C15, D0 +D0, S12, C39, C38, C37, C35, D0 +D0, S4, C94, C92, C93, C97, C100, C99, D0 +D0, S20, C73, C70, C79, C77, C81, D0 +D0, S20, C71, C76, C78, C72, D0 +D0, C5, D0 +D0, S18, C80, D0 +D0, C9, D0 +D0, C89, D0 +D0, C61, D0 +D0, C91, D0 +D0, C49, D0 +D0, C8, D0 +D0, C66, D0 +D0, C52, D0 +D0, C13, D0 diff --git a/solutions/c204_21_sol.txt b/solutions/c204_21_sol.txt index 995180d..33c78c5 100644 --- a/solutions/c204_21_sol.txt +++ b/solutions/c204_21_sol.txt @@ -1,6 +1,6 @@ # solution for c204_21 -716.8507104000043 -D0, S0, C25, C9, C13, C15, C12, C14, C16, C19, C18, C17, C23, C26, S10, C28, C34, C36, C39, C38, C37, C35, C31, C33, C32, C6, C29, C30, C27, C24, C20, C22, C21, D0 -D0, S0, C90, C86, C83, C82, C85, C76, C71, C70, C73, C80, C79, C81, C78, S19, C96, C72, C61, C64, C62, C74, C63, D0 -D0, C8, C10, C11, C99, C100, C97, C92, C94, C95, C3, C88, C84, S1, C77, C87, C91, C75, C5, C2, C1, C98, C7, C4, C89, C93, D0 -D0, C48, C43, C41, C42, C47, S13, C52, C50, C51, C45, C46, C44, C40, C57, C54, C53, C56, C58, C60, C59, C55, C49, C65, C68, C69, C66, C67, D0 +662.1163223356496 +D0, C93, C5, C75, C2, C1, C95, C94, C92, C97, C100, C99, C98, C7, C3, C4, C89, D0 +D0, S0, C8, C10, C11, C9, C13, C15, C12, C14, C16, C19, C18, C17, C25, C23, S9, C26, C28, C36, C39, C38, C37, C35, C31, C33, C32, C34, C6, C29, C30, C27, C24, C22, C21, D0 +D0, C90, C91, C88, C86, C84, C83, C82, C85, S20, C76, C71, C70, C73, C80, C79, C81, C78, C77, C87, C96, C74, C63, C67, D0 +D0, S0, C20, C47, C52, C50, C51, C45, C46, C44, C40, C57, C54, C53, C56, C58, C60, C59, C55, C49, C65, S15, C68, C64, C61, C72, C62, C66, C69, C41, C42, C43, C48, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 131f5f6..8d8d585 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -64,7 +64,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 2 until 3) { + for (i in 1 until 2) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index d95bfeb..5e91090 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,7 +12,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 50 + const val N_DIST = 30 const val N_FEAS = 5 const val N_TABU = 50 const val N_PENALTY = 2 @@ -23,7 +23,7 @@ class Constants private constructor() { const val ALPHA_DEFAULT = 1.0 const val BETA_DEFAULT = 1.0 const val GAMMA_DEFAULT = 1.0 - const val VIOLATION_FACTOR_MIN = 0.2 + const val VIOLATION_FACTOR_MIN = 0.3 const val VIOLATION_FACTOR_INCREASE_RATE = 0.3 const val VIOLATION_FACTOR_DECREASE_RATE = 0.2 var ALPHA = ALPHA_DEFAULT diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 0f69348..4872654 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -1,10 +1,8 @@ package at.ac.tuwien.otl.evrptw.metaheuristic +/* ktlint-disable no-wildcard-imports */ import at.ac.tuwien.otl.evrptw.Main -import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance -import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution -import at.ac.tuwien.otl.evrptw.dto.NeighbourhoodStructure -import at.ac.tuwien.otl.evrptw.dto.Route +import at.ac.tuwien.otl.evrptw.dto.* import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.ALPHA import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.ALPHA_DEFAULT import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.BETA @@ -125,7 +123,10 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe private fun acceptSimulatedAnnealing(optimizedNewSolution: EVRPTWSolution, bestSolution: EVRPTWSolution): Boolean { val accept: Boolean accept = if (optimizedNewSolution.fitnessValue.fitness == optimizedNewSolution.cost) { - optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness + // optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness + // bestSolution.fitnessValue.fitness != bestSolution.cost && optimizedNewSolution.fitnessValue.fitness < calculateFitnessOfSolutionWithDefaultFactors(bestSolution) + /* accept a new feasible solution, if the current best one is infeasible OR if both are feasible and the new is fitter than the current best one */ + bestSolution.fitnessValue.fitness != bestSolution.cost || optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness } else { if (optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness) { val exponent = @@ -142,6 +143,10 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe return accept } + private fun calculateFitnessOfSolutionWithDefaultFactors(solution: EVRPTWSolution): Double { + return solution.cost + (ALPHA_DEFAULT * solution.fitnessValue.totalCapacityViolation) + (BETA_DEFAULT * solution.fitnessValue.totalTimeWindowViolation) + (GAMMA_DEFAULT * solution.fitnessValue.totalBatteryCapacityViolation) + } + private fun feasible(solution: EVRPTWSolution): Boolean { return EVRPTWRouteVerifier(solution.instance).verify(solution.routes, solution.cost, false) } From 1cbca49b574eb448d806fd210694596958be2126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Tue, 26 Jun 2018 15:57:28 +0200 Subject: [PATCH 29/36] implement fibonacci based violation factor multiplication; fix issue whereby factors and counters weren't reset to default after an instance iteration; fix issue whereby a new feasible solution might overwrite the current best feasible one because their costs weren't compared; make ShakingGenerator full random; adjust constants --- solutions/c103_21_sol.txt | 28 +++--- solutions/c105_21_sol.txt | 42 +++------ solutions/c204_21_sol.txt | 11 +-- solutions/r102_21_sol.txt | 64 ++++++-------- solutions/r107_21_sol.txt | 52 ++++------- solutions/r205_21_sol.txt | 17 ++-- solutions/r211_21_sol.txt | 10 +-- solutions/rc101_21_sol.txt | 62 +++++-------- solutions/rc106_21_sol.txt | 54 ++++-------- solutions/rc203_21_sol.txt | 19 ++-- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../otl/evrptw/metaheuristic/Constants.kt | 17 ++-- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 86 +++++++++++++++---- .../ShakingNeighbourSolutionGenerator.kt | 2 +- 14 files changed, 210 insertions(+), 256 deletions(-) diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index 22d0106..5c438ef 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,16 +1,14 @@ # solution for c103_21 -1215.0610029344487 -D0, C98, C95, C94, S4, C92, C93, C97, C100, C99, C75, D0 -D0, S0, C21, C22, C25, C27, C30, S9, C18, C17, C10, C11, D0 -D0, C59, C40, C44, C45, C46, C47, S0, D0 -D0, C96, C2, C1, C3, C5, D0 -D0, S19, C73, C70, C71, C76, C78, C81, S19, D0 -D0, C63, C74, S19, C79, C77, C80, C72, C61, C66, D0 -D0, C67, C65, C62, C64, C68, C69, C41, C42, C43, D0 -D0, C7, C4, C6, C9, C8, D0 -D0, C12, C14, C16, C19, C15, S7, C13, D0 -D0, C20, C24, C29, C32, C33, C35, S11, C48, C49, D0 -D0, C90, C86, S1, C82, C83, C84, C85, C88, C87, C89, C91, D0 -D0, S14, C60, C58, C56, C53, C54, C55, C57, D0 -D0, S13, C52, C50, C51, C31, C38, C37, C39, C36, C34, S13, D0 -D0, C23, C28, C26, D0 +1077.5282280519511 +D0, S0, S0, C98, C96, C95, C94, C92, C93, C97, C100, C99, S3, D0 +D0, C91, C89, C88, C85, C84, C82, C83, C86, C87, C90, D0 +D0, C63, S19, C73, C70, C71, C76, C78, C81, S19, D0 +D0, C75, C1, C2, C4, C6, C7, C3, C5, D0 +D0, C23, C25, C27, C30, C28, C26, C10, C11, C9, C8, D0 +D0, C65, C40, C44, C42, C41, C68, C64, C66, D0 +D0, C20, C36, C39, S12, C38, C37, C35, C31, C52, C49, D0 +D0, C32, C33, C34, C29, C24, C22, C21, D0 +D0, S15, C59, C45, C48, C51, C50, C46, C47, C43, D0 +D0, S0, C67, C74, S19, C79, C77, C80, C72, C61, C62, D0 +D0, S7, C12, C14, C16, C19, C18, C17, C15, C13, D0 +D0, S14, C60, C58, C56, C53, C54, C55, C57, C69, D0 diff --git a/solutions/c105_21_sol.txt b/solutions/c105_21_sol.txt index f382e31..fd60368 100644 --- a/solutions/c105_21_sol.txt +++ b/solutions/c105_21_sol.txt @@ -1,30 +1,14 @@ # solution for c105_21 -2029.2143728128362 -D0, C20, C21, C25, C27, C30, C28, C26, C6, C7, D0 -D0, C67, C65, C62, C64, C68, C69, C48, C46, C47, C43, D0 -D0, C23, C22, C29, C32, C33, C31, D0 -D0, C63, C74, C40, C42, C44, C45, C51, C50, D0 -D0, C75, C1, C98, C95, D0 -D0, C24, C34, C36, D0 -D0, C41, C59, C55, D0 -D0, C90, C87, C86, C82, C83, C84, C85, C88, D0 -D0, C96, C2, C4, C3, D0 -D0, C12, C10, D0 -D0, S14, C60, C58, C56, C53, C54, C57, D0 -D0, C14, C11, D0 -D0, S7, C16, C19, C18, C17, C15, D0 -D0, S12, C39, C38, C37, C35, D0 -D0, S4, C94, C92, C93, C97, C100, C99, D0 -D0, S20, C73, C70, C79, C77, C81, D0 -D0, S20, C71, C76, C78, C72, D0 -D0, C5, D0 -D0, S18, C80, D0 -D0, C9, D0 -D0, C89, D0 -D0, C61, D0 -D0, C91, D0 -D0, C49, D0 -D0, C8, D0 -D0, C66, D0 -D0, C52, D0 -D0, C13, D0 +1046.6681306152846 +D0, S3, C98, C96, C95, C94, C92, C93, C97, C100, C99, D0 +D0, S0, C63, C74, C79, C77, C80, S19, D0 +D0, C41, C40, C42, C44, C45, C48, C46, C47, C43, D0 +D0, C75, C1, C2, C4, C6, C7, C3, C5, D0 +D0, S19, C73, C70, C71, C76, C78, C81, S19, D0 +D0, C20, C24, C32, C33, C31, S11, C51, C50, C52, C49, D0 +D0, C67, C65, C62, C64, C68, C69, C72, C61, C66, D0 +D0, C21, C22, C25, C27, C30, C28, C26, C10, C11, C9, C8, D0 +D0, C90, C87, C86, C82, C83, C84, C85, C88, C89, C91, D0 +D0, S5, C12, C14, C16, C19, C18, C17, C15, C13, D0 +D0, C59, C60, C58, C56, C53, S16, C54, C55, C57, D0 +D0, C23, C29, C34, C36, C39, C38, C37, C35, S13, D0 diff --git a/solutions/c204_21_sol.txt b/solutions/c204_21_sol.txt index 33c78c5..ca86433 100644 --- a/solutions/c204_21_sol.txt +++ b/solutions/c204_21_sol.txt @@ -1,6 +1,7 @@ # solution for c204_21 -662.1163223356496 -D0, C93, C5, C75, C2, C1, C95, C94, C92, C97, C100, C99, C98, C7, C3, C4, C89, D0 -D0, S0, C8, C10, C11, C9, C13, C15, C12, C14, C16, C19, C18, C17, C25, C23, S9, C26, C28, C36, C39, C38, C37, C35, C31, C33, C32, C34, C6, C29, C30, C27, C24, C22, C21, D0 -D0, C90, C91, C88, C86, C84, C83, C82, C85, S20, C76, C71, C70, C73, C80, C79, C81, C78, C77, C87, C96, C74, C63, C67, D0 -D0, S0, C20, C47, C52, C50, C51, C45, C46, C44, C40, C57, C54, C53, C56, C58, C60, C59, C55, C49, C65, S15, C68, C64, C61, C72, C62, C66, C69, C41, C42, C43, C48, D0 +673.3010608930191 +D0, S0, C22, C30, S9, C23, C26, C28, C34, C36, C39, C38, C37, C35, C31, C33, C32, C6, C29, C27, C24, C20, D0 +D0, S0, S0, C8, C10, C11, C9, C13, C15, C12, C14, C16, C19, C18, C17, C25, C21, D0 +D0, C90, C91, C88, C86, C84, C83, C82, C85, S20, C76, C71, C70, C73, C80, C79, C81, C78, C77, C87, C96, C63, C67, D0 +D0, C48, C43, C41, C42, C47, C52, C50, C51, C45, C46, C44, C40, C57, C55, C54, C59, C60, C58, C56, C53, S16, C49, C65, C68, C64, C72, C61, C74, C62, C66, C69, D0 +D0, C89, C4, C3, C95, C94, C92, C97, C100, C99, C98, C7, C1, C2, C75, C5, C93, D0 diff --git a/solutions/r102_21_sol.txt b/solutions/r102_21_sol.txt index df8dee1..0562427 100644 --- a/solutions/r102_21_sol.txt +++ b/solutions/r102_21_sol.txt @@ -1,41 +1,25 @@ # solution for r102_21 -2635.1075915695646 -D0, C53, C28, C40, C21, C2, C13, D0 -D0, C94, C97, C98, C100, C93, C5, D0 -D0, C89, C18, C60, C84, C83, D0 -D0, C96, C59, C92, C95, C87, D0 -D0, C1, C70, C10, C52, D0 -D0, C31, C62, C7, C27, D0 -D0, C58, C22, C75, D0 -D0, C6, C99, C26, D0 -D0, C3, C81, C33, C50, D0 -D0, C74, C56, C72, D0 -D0, C68, C24, C80, D0 -D0, C61, C16, C91, C85, D0 -D0, C8, C45, D0 -D0, C69, D0 -D0, C55, C54, D0 -D0, S4, C9, C34, C78, C29, D0 -D0, S19, C25, C4, C73, D0 -D0, S8, C47, C36, C48, C82, D0 -D0, C12, D0 -D0, C79, C77, D0 -D0, C76, D0 -D0, S7, C19, C11, C63, D0 -D0, C88, D0 -D0, C17, D0 -D0, S14, C43, C15, C42, D0 -D0, S18, C39, C67, C23, D0 -D0, S6, C90, C30, D0 -D0, S6, C32, C20, C51, D0 -D0, S10, C46, D0 -D0, C37, D0 -D0, C57, D0 -D0, S12, C86, C44, D0 -D0, S4, C66, C71, D0 -D0, S11, C38, C14, D0 -D0, C41, D0 -D0, S4, C35, D0 -D0, S4, C65, S5, D0 -D0, S8, C64, S9, D0 -D0, S8, C49, D0 +1697.4989612687232 +D0, S4, C66, C20, D0 +D0, C7, C48, C82, S0, D0 +D0, S0, S0, C31, C19, C11, S7, C62, C88, D0 +D0, C94, C96, C59, C98, C92, C95, D0 +D0, C74, C75, C56, C72, C21, D0 +D0, C61, C16, C91, C85, C93, C99, D0 +D0, C24, C55, C25, S19, C4, C73, C26, D0 +D0, S14, C43, C15, C42, C87, C57, C2, D0 +D0, C52, D0 +D0, C5, C17, C86, S12, C44, C37, D0 +D0, C53, C40, C58, D0 +D0, C81, C32, C90, S6, C30, D0 +D0, C89, C60, C84, S10, C45, C83, D0 +D0, S0, C47, S8, S8, C64, C63, C10, S5, D0 +D0, C6, D0 +D0, C18, C8, C46, C36, S8, C49, D0 +D0, C100, S11, C38, C14, C97, C13, D0 +D0, C1, C70, C50, C69, C27, D0 +D0, C68, C54, C80, C12, D0 +D0, C9, S4, C34, C78, C29, C28, D0 +D0, C3, C33, C79, C77, C76, D0 +D0, S18, C39, C67, C23, S17, C22, C41, D0 +D0, S4, C65, C71, C35, S4, C51, D0 diff --git a/solutions/r107_21_sol.txt b/solutions/r107_21_sol.txt index 43b36d1..0da3ff6 100644 --- a/solutions/r107_21_sol.txt +++ b/solutions/r107_21_sol.txt @@ -1,38 +1,16 @@ # solution for r107_21 -2534.1151825762677 -D0, C53, C58, C13, C94, C95, C97, C59, C93, C98, C100, C6, D0 -D0, C28, C26, C40, C21, C74, C22, C73, D0 -D0, C89, C18, C60, C83, C5, C84, D0 -D0, C50, C33, C81, C9, D0 -D0, C96, C61, C16, C91, C85, C92, D0 -D0, C31, C70, C10, C62, C88, C27, D0 -D0, C2, C75, C72, D0 -D0, C1, C51, C3, C77, D0 -D0, C80, C68, C24, C54, D0 -D0, C52, C48, C82, D0 -D0, C69, C30, D0 -D0, C45, C8, D0 -D0, C7, C19, D0 -D0, C55, C4, D0 -D0, C14, C99, D0 -D0, S7, C11, C63, C32, C90, D0 -D0, S19, C25, C67, D0 -D0, S14, C43, C15, C42, C87, C57, D0 -D0, S8, C47, C49, D0 -D0, C12, D0 -D0, C56, D0 -D0, C79, C78, D0 -D0, C20, D0 -D0, C76, D0 -D0, S17, C23, C39, C41, D0 -D0, S4, C34, C35, D0 -D0, C17, D0 -D0, C37, D0 -D0, C29, D0 -D0, C44, D0 -D0, S12, C86, D0 -D0, S8, C36, C46, D0 -D0, S4, C66, C71, D0 -D0, S11, C38, D0 -D0, S4, C65, D0 -D0, S8, C64, D0 +1259.7759332479818 +D0, S0, C53, C40, C74, C23, S17, C75, C72, C73, C22, C41, C57, D0 +D0, C58, C2, C15, S14, C43, C42, C87, C13, D0 +D0, C94, C59, C98, C100, C14, C38, C44, S11, D0 +D0, S0, C1, C70, C10, C62, S7, C63, C90, C32, C30, D0 +D0, C28, C68, C80, S1, C54, C24, C29, C3, C77, C76, C12, D0 +D0, C18, S9, C7, C47, C36, S8, C46, C82, D0 +D0, C26, C21, C56, C39, S18, C4, S19, C55, C25, C67, D0 +D0, C48, C19, S8, C49, C64, C11, S7, S7, C88, C31, D0 +D0, C96, C61, C16, C91, C85, C93, C92, C95, D0 +D0, C83, C8, C45, C17, S12, C86, C37, C97, D0 +D0, C89, C60, C84, C5, C99, C6, D0 +D0, S0, C52, C69, C27, D0 +D0, C50, C33, C81, C79, C78, C34, C35, S4, C71, D0 +D0, C20, S4, C65, C66, C9, C51, S3, D0 diff --git a/solutions/r205_21_sol.txt b/solutions/r205_21_sol.txt index 2a44227..49e1bf8 100644 --- a/solutions/r205_21_sol.txt +++ b/solutions/r205_21_sol.txt @@ -1,10 +1,9 @@ # solution for r205_21 -1364.418713755443 -D0, C27, C28, C53, C58, C40, C73, C22, C41, C56, C4, C12, C3, C29, C24, C51, D0 -D0, C13, C95, C59, C99, C92, C100, C98, C37, C91, C85, C61, C16, C44, C86, C38, C17, C45, C82, C62, C11, C63, D0 -D0, C26, C21, C72, C74, C75, C57, C87, C97, C94, C6, C89, C60, C83, C93, C96, C15, D0 -D0, C52, C7, C88, C31, C69, C50, C30, C20, C66, C71, C9, C35, C47, D0 -D0, C1, C70, C10, C90, C32, C64, C49, C36, D0 -D0, C76, C77, C79, C81, C33, C78, C34, C68, C55, C39, C43, C84, C18, D0 -D0, C2, C42, C14, C5, C8, C46, C48, C19, D0 -D0, C80, C54, C25, C67, C23, C65, D0 +1029.7584081333912 +D0, S0, C13, C42, C14, C100, C91, C61, C84, C83, C60, C18, C89, D0 +D0, C52, C7, C88, C69, C50, C28, C27, C53, C58, C40, C73, C56, C4, D0 +D0, S0, C76, C77, C79, C33, C81, C78, C34, C65, C66, C20, C71, C35, C9, C51, C3, C29, C24, C12, D0 +D0, C80, C68, C55, C39, C75, C74, C22, C41, C15, C38, C86, C17, C45, D0 +D0, C1, C70, C30, C32, C90, C63, C64, C49, C36, C47, C82, D0 +D0, C26, C54, C25, C67, C23, C72, C21, C2, C57, C87, C37, C98, C93, C85, C16, C44, C43, C97, C94, C96, C6, S0, D0 +D0, C95, C92, C59, C99, C5, C8, C46, C48, C19, C11, C62, C10, C31, D0 diff --git a/solutions/r211_21_sol.txt b/solutions/r211_21_sol.txt index 8f87f35..5d08e32 100644 --- a/solutions/r211_21_sol.txt +++ b/solutions/r211_21_sol.txt @@ -1,6 +1,6 @@ # solution for r211_21 -921.7238364630402 -D0, S0, C27, C52, C18, C7, C88, C70, C1, C50, C79, C81, C34, C65, C35, C71, C66, C20, C51, C9, C24, C29, C78, C33, C3, C12, D0 -D0, C28, C76, C77, C68, C80, C54, C67, C75, C56, C23, C39, C25, C55, C4, C73, C22, C41, C40, D0 -D0, C53, C58, C13, C95, C92, C59, C99, C5, C84, C83, C8, C46, C48, C19, C11, C63, C90, C32, C30, C10, C62, C31, C69, D0 -D0, C26, C21, C72, C74, C2, C42, C100, C14, C44, C38, C43, C15, C57, C87, C97, C37, C98, C85, C61, C16, S11, C91, C93, C96, C94, C6, C89, C60, C45, C17, C86, C82, C47, C36, C49, C64, D0 +822.5155349158299 +D0, C26, C21, C72, C2, C42, C100, C91, C44, C38, C14, C43, C15, C57, C87, C97, C92, C37, C98, C60, C83, C17, C86, C16, C61, C85, C93, C96, C94, C6, C89, D0 +D0, S0, C28, C76, C33, C81, C65, C34, C78, C79, C3, C77, C68, C80, C54, C55, C25, C39, C67, C23, C56, C4, C75, C41, C22, C74, C73, C40, D0 +D0, S0, C52, C18, C7, C88, C31, C70, C30, C50, C1, C69, C27, D0 +D0, S0, S0, C53, C58, C13, C95, C59, C99, C5, C84, C45, C8, C46, C19, C11, C10, C62, C82, C48, C47, C36, C49, C64, C63, C90, C32, C66, C20, C51, C9, S4, C71, C35, C29, C24, C12, D0 diff --git a/solutions/rc101_21_sol.txt b/solutions/rc101_21_sol.txt index ac24d31..be6062e 100644 --- a/solutions/rc101_21_sol.txt +++ b/solutions/rc101_21_sol.txt @@ -1,43 +1,21 @@ # solution for rc101_21 -3435.890895175693 -D0, C69, C55, C100, C70, C68, C81, C54, D0 -D0, C80, C91, C95, C56, C92, C96, D0 -D0, C66, C65, C99, C52, C86, D0 -D0, C90, C98, D0 -D0, C53, C88, C82, C83, D0 -D0, C93, C71, C72, C41, D0 -D0, C64, C84, C51, D0 -D0, C57, C74, D0 -D0, C62, C85, D0 -D0, C94, D0 -D0, C61, D0 -D0, C42, C44, C43, D0 -D0, C14, C47, C12, D0 -D0, C2, C6, C7, D0 -D0, C78, C73, D0 -D0, C10, C11, C15, D0 -D0, C4, C45, C46, D0 -D0, C13, C9, D0 -D0, C79, C60, D0 -D0, C87, D0 -D0, S16, C19, C49, C22, C24, C20, C48, D0 -D0, C67, D0 -D0, C63, D0 -D0, S4, C37, C38, C39, C40, D0 -D0, S11, C59, C58, D0 -D0, S17, C76, C89, D0 -D0, S4, C36, C35, D0 -D0, C8, D0 -D0, C3, D0 -D0, C50, D0 -D0, S16, C18, C23, C25, C21, D0 -D0, S7, C5, C1, D0 -D0, S19, C32, C30, C29, D0 -D0, S12, C17, D0 -D0, S12, C16, D0 -D0, S14, C77, D0 -D0, S20, C31, C26, C28, D0 -D0, S18, C33, C34, D0 -D0, S11, C97, D0 -D0, S14, C75, S15, D0 -D0, S20, C27, D0 +1870.531491249007 +D0, S17, C76, C89, C51, S0, D0 +D0, C100, C4, C45, C8, C7, C6, C46, S7, D0 +D0, S0, S3, C42, C44, C43, C39, C35, C40, C61, D0 +D0, S0, S3, C36, C37, C38, C41, C72, C54, C81, D0 +D0, S11, C87, C59, C74, C9, D0 +D0, C66, S16, C19, C49, C18, C23, C25, C21, C48, D0 +D0, C13, C10, C11, C15, S12, C16, C17, D0 +D0, C62, S19, C32, C31, C29, C34, C67, D0 +D0, C64, C91, C83, S15, C92, C94, D0 +D0, S14, C58, C75, C97, C86, S15, D0 +D0, C93, C71, S20, C26, C27, C96, D0 +D0, C70, C2, S7, C3, C5, C1, D0 +D0, C68, D0 +D0, C69, C53, C14, C47, C12, C82, C90, D0 +D0, C85, C63, C84, C56, D0 +D0, C55, C79, C78, C73, C60, S9, D0 +D0, C57, S14, S14, C77, C24, C22, C20, D0 +D0, C80, C95, S19, C33, C28, C30, C50, D0 +D0, C65, C52, C99, C88, C98, D0 diff --git a/solutions/rc106_21_sol.txt b/solutions/rc106_21_sol.txt index 6d00810..80055c6 100644 --- a/solutions/rc106_21_sol.txt +++ b/solutions/rc106_21_sol.txt @@ -1,38 +1,18 @@ # solution for rc106_21 -3078.24610349508 -D0, C69, C55, C100, C70, C68, C61, C81, D0 -D0, C80, C91, C95, C84, C56, C92, C94, C96, D0 -D0, C65, C66, C64, C57, C52, C82, C90, D0 -D0, C53, C88, C98, C60, D0 -D0, C93, C71, C72, C41, D0 -D0, C99, C87, C86, D0 -D0, C83, C24, C22, C20, D0 -D0, C62, C85, D0 -D0, C54, D0 -D0, C42, C44, C43, D0 -D0, C2, C4, C45, C46, C6, D0 -D0, C14, C47, C12, D0 -D0, C78, C73, D0 -D0, C10, C11, C15, D0 -D0, C67, C50, D0 -D0, C13, C9, D0 -D0, C79, D0 -D0, C63, D0 -D0, C7, C8, D0 -D0, S16, C19, C49, C18, C23, C25, C21, C48, D0 -D0, C38, C39, D0 -D0, S4, C37, C36, C35, C40, D0 -D0, C74, D0 -D0, S11, C59, C58, D0 -D0, C51, D0 -D0, S17, C76, C89, D0 -D0, C3, D0 -D0, C1, D0 -D0, S7, C5, D0 -D0, S12, C17, C16, D0 -D0, S14, C77, D0 -D0, S19, C32, C30, C31, C29, D0 -D0, S18, C33, C28, C34, D0 -D0, S11, C97, D0 -D0, S14, C75, S15, D0 -D0, S20, C26, C27, D0 +1621.9401325584856 +D0, C10, C13, C9, C82, C90, D0 +D0, C79, C7, C6, S7, C5, C3, C1, D0 +D0, C65, C99, C52, C87, C59, C74, C86, S13, D0 +D0, C55, C2, C4, C45, C8, C46, S7, C98, D0 +D0, C84, C85, C63, C76, C89, S17, C51, D0 +D0, C66, S16, C49, C19, C18, C23, C25, C21, C48, D0 +D0, C80, C62, S19, C32, C30, C31, C94, C96, D0 +D0, C91, C95, S18, C33, C50, D0 +D0, C93, C71, S20, C27, C26, C28, C29, C34, C67, D0 +D0, C57, S14, C77, C58, C75, C97, S11, D0 +D0, S0, C100, C70, C88, C78, C73, C60, S9, D0 +D0, S3, C37, C38, C39, C41, C72, C54, C81, D0 +D0, C64, C20, C22, C24, C83, D0 +D0, S0, C68, C92, C56, D0 +D0, C69, C53, C47, C14, C12, C11, C15, C16, S12, C17, D0 +D0, C42, C44, C43, C36, C35, C40, S3, C61, D0 diff --git a/solutions/rc203_21_sol.txt b/solutions/rc203_21_sol.txt index 27596a9..0c4b46f 100644 --- a/solutions/rc203_21_sol.txt +++ b/solutions/rc203_21_sol.txt @@ -1,11 +1,10 @@ # solution for rc203_21 -1639.8953568370507 -D0, C80, C94, C93, C67, C62, C50, C34, C31, C32, C28, C26, C33, C63, C51, C49, C19, C48, C18, C21, C23, C25, C24, C22, C83, C91, D0 -D0, C69, C98, C53, C60, C55, C100, C70, C2, C6, C45, C5, C3, C7, C14, C47, C11, C10, C9, C13, C87, C86, C52, D0 -D0, C81, C95, C65, C99, C74, C59, C97, C75, C58, C57, C20, C56, C66, D0 -D0, C90, C79, C73, C78, C43, C35, C36, C44, C42, C40, C54, C61, C68, D0 -D0, C85, C76, C77, C16, C17, C82, C88, D0 -D0, C96, C41, C37, C72, C1, C8, C4, C46, C12, C15, D0 -D0, C38, C39, C71, C84, C89, D0 -D0, C64, C92, C27, D0 -D0, C30, C29, D0 +1030.6866552746983 +D0, S0, C96, C72, C54, C61, D0 +D0, C81, C38, C41, C37, C35, C36, C40, C43, C44, C42, C39, C71, C93, S0, D0 +D0, S0, C55, C100, C2, C6, C7, C45, C1, C3, C5, C8, C46, C4, C70, C68, D0 +D0, C69, C60, C79, C73, C78, C88, C98, C82, D0 +D0, S0, C95, C85, C63, C76, C49, C19, C18, C48, C21, C23, C25, C24, C22, C20, C64, C66, D0 +D0, C94, C67, C62, C50, C34, C32, C30, C31, C29, C27, C26, C28, C33, C89, C51, C84, D0 +D0, C90, C14, C47, C17, C16, C11, C10, C9, C87, C74, C86, C52, C13, C15, C12, C53, D0 +D0, C65, C99, C59, C97, C75, C58, C77, C57, C83, C56, C91, C92, C80, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 8d8d585..ff97091 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -64,7 +64,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 1 until 2) { + for (i in 3 until 4) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 5e91090..007220d 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,7 +12,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 30 + const val N_DIST = 40 const val N_FEAS = 5 const val N_TABU = 50 const val N_PENALTY = 2 @@ -20,14 +20,17 @@ class Constants private constructor() { const val TABU_TENURE_MIN = 15 const val TABU_TENURE_MAX = 30 const val NO_CHANGE_THRESHOLD = 3 - const val ALPHA_DEFAULT = 1.0 - const val BETA_DEFAULT = 1.0 - const val GAMMA_DEFAULT = 1.0 - const val VIOLATION_FACTOR_MIN = 0.3 - const val VIOLATION_FACTOR_INCREASE_RATE = 0.3 - const val VIOLATION_FACTOR_DECREASE_RATE = 0.2 + const val ALPHA_DEFAULT = 10.0 + const val BETA_DEFAULT = 10.0 + const val GAMMA_DEFAULT = 10.0 + const val VIOLATION_FACTOR_MIN = 1.0 + const val VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE = 0.1 + const val VIOLATION_FACTOR_ABSOLUTE_MIN = 0.1 + const val VIOLATION_FACTOR_INCREASE_RATE = 2.0 + const val VIOLATION_FACTOR_DECREASE_RATE = 5.0 var ALPHA = ALPHA_DEFAULT var BETA = BETA_DEFAULT var GAMMA = GAMMA_DEFAULT + val FIBONACCI = arrayOf(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 4872654..4d7bf0b 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -8,11 +8,14 @@ import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.ALPHA_DEFAULT import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.BETA import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.BETA_DEFAULT import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.COOLING_FACTOR +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.FIBONACCI import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.GAMMA import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.GAMMA_DEFAULT import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.NO_CHANGE_THRESHOLD import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_ABSOLUTE_MIN +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_DECREASE_RATE import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_INCREASE_RATE import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.VIOLATION_FACTOR_MIN @@ -39,11 +42,13 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe private val lastSavedSolutions = mutableListOf() private var temperature = 0.0 private var thresholdCounter = 0 + private var infeasibleSequenceCounter = 0 + private var feasibleSequenceCounter = 0 override fun improveSolution(evrptwSolution: EVRPTWSolution): EVRPTWSolution { temperature = Main.instanceToInitTemperatureMap[evrptwSolution.instance.name]!! - lastSavedSolutions.clear() - thresholdCounter = 0 + resetParameters() + var bestFeasibleSolution = evrptwSolution var bestSolution = evrptwSolution var k = 1 @@ -66,7 +71,7 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe "Bat-Violation: ${optimizedNewSolution.fitnessValue.totalBatteryCapacityViolation}, " + "Fitness: ${optimizedNewSolution.fitnessValue.fitness}") bestSolution = optimizedNewSolution - if (optimizedNewSolution.fitnessValue.fitness == optimizedNewSolution.cost) { + if (optimizedNewSolution.fitnessValue.fitness == optimizedNewSolution.cost && optimizedNewSolution.cost < bestFeasibleSolution.cost) { log("New best feasible solution with cost ${optimizedNewSolution.cost}") bestFeasibleSolution = optimizedNewSolution } @@ -92,6 +97,16 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe return bestFeasibleSolution } + private fun resetParameters() { + ALPHA = ALPHA_DEFAULT + BETA = BETA_DEFAULT + GAMMA = GAMMA_DEFAULT + lastSavedSolutions.clear() + thresholdCounter = 0 + infeasibleSequenceCounter = 0 + feasibleSequenceCounter = 0 + } + private fun adjustParameters() { val lastSolution = lastSavedSolutions.last() if (lastSolution.fitnessValue.fitness == lastSolution.cost) { @@ -100,11 +115,16 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe thresholdCounter-- } if (thresholdCounter == NO_CHANGE_THRESHOLD) { - ALPHA = if (ALPHA > ALPHA_DEFAULT) ALPHA_DEFAULT else Math.max(ALPHA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) + decreaseRateAlpha() + decreaseRateBeta() + decreaseRateGamma() + /*ALPHA = if (ALPHA > ALPHA_DEFAULT) ALPHA_DEFAULT else Math.max(ALPHA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) BETA = if (BETA > BETA_DEFAULT) BETA_DEFAULT else Math.max(BETA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) - GAMMA = if (GAMMA > GAMMA_DEFAULT) GAMMA_DEFAULT else Math.max(GAMMA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) + GAMMA = if (GAMMA > GAMMA_DEFAULT) GAMMA_DEFAULT else Math.max(GAMMA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN)*/ thresholdCounter = 0 - log("-- VIOLATION FACTORS DECREASED = ($ALPHA, $BETA, $GAMMA)") + infeasibleSequenceCounter = 0 + feasibleSequenceCounter++ + log("-- VIOLATION FACTORS DECREASED = ($ALPHA, $BETA, $GAMMA). Fibonacci multiplier: ${FIBONACCI[feasibleSequenceCounter--]}") } else if (thresholdCounter == -NO_CHANGE_THRESHOLD) { val numberOfCapacityViolations = lastSavedSolutions.stream().filter { it.fitnessValue.totalCapacityViolation > 0.0 }.toList().size @@ -112,21 +132,55 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe lastSavedSolutions.stream().filter { it.fitnessValue.totalTimeWindowViolation > 0.0 }.toList().size val numberOfBatteryCapacityViolations = lastSavedSolutions.stream().filter { it.fitnessValue.totalBatteryCapacityViolation > 0.0 }.toList().size - ALPHA += VIOLATION_FACTOR_INCREASE_RATE * numberOfCapacityViolations - BETA += VIOLATION_FACTOR_INCREASE_RATE * numberOfTimeWindowViolations - GAMMA += VIOLATION_FACTOR_INCREASE_RATE * numberOfBatteryCapacityViolations + val indexOfMultiplier = infeasibleSequenceCounter % FIBONACCI.size + ALPHA += VIOLATION_FACTOR_INCREASE_RATE * numberOfCapacityViolations * FIBONACCI[indexOfMultiplier] + BETA += VIOLATION_FACTOR_INCREASE_RATE * numberOfTimeWindowViolations * FIBONACCI[indexOfMultiplier] + GAMMA += VIOLATION_FACTOR_INCREASE_RATE * numberOfBatteryCapacityViolations * FIBONACCI[indexOfMultiplier] thresholdCounter = 0 - log("++ VIOLATION FACTORS INCREASED = ($ALPHA, $BETA, $GAMMA)") + infeasibleSequenceCounter++ + feasibleSequenceCounter = 0 + log("++ VIOLATION FACTORS INCREASED = ($ALPHA, $BETA, $GAMMA). Fibonacci multiplier: ${FIBONACCI[infeasibleSequenceCounter--]}") + } + } + + private fun decreaseRateAlpha() { + ALPHA = when { + ALPHA > ALPHA_DEFAULT -> ALPHA_DEFAULT + ALPHA <= VIOLATION_FACTOR_MIN -> Math.max(ALPHA - VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE, VIOLATION_FACTOR_ABSOLUTE_MIN) + else -> Math.max(ALPHA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) + } + } + + private fun decreaseRateBeta() { + BETA = when { + BETA > BETA_DEFAULT -> BETA_DEFAULT + BETA <= VIOLATION_FACTOR_MIN -> Math.max(BETA - VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE, VIOLATION_FACTOR_ABSOLUTE_MIN) + else -> Math.max(BETA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) + } + } + + private fun decreaseRateGamma() { + GAMMA = when { + GAMMA > GAMMA_DEFAULT -> GAMMA_DEFAULT + GAMMA <= VIOLATION_FACTOR_MIN -> Math.max(GAMMA - VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE, VIOLATION_FACTOR_ABSOLUTE_MIN) + else -> Math.max(GAMMA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) } } private fun acceptSimulatedAnnealing(optimizedNewSolution: EVRPTWSolution, bestSolution: EVRPTWSolution): Boolean { val accept: Boolean accept = if (optimizedNewSolution.fitnessValue.fitness == optimizedNewSolution.cost) { - // optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness - // bestSolution.fitnessValue.fitness != bestSolution.cost && optimizedNewSolution.fitnessValue.fitness < calculateFitnessOfSolutionWithDefaultFactors(bestSolution) - /* accept a new feasible solution, if the current best one is infeasible OR if both are feasible and the new is fitter than the current best one */ - bestSolution.fitnessValue.fitness != bestSolution.cost || optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness + when { + bestSolution.fitnessValue.fitness != bestSolution.cost -> { + log("Accepting new feasible solution, because current best is infeasible") + true + } + optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness -> { + log("Accepting new feasible solution, because new is better than current best feasible") + true + } + else -> false + } } else { if (optimizedNewSolution.fitnessValue.fitness < bestSolution.fitnessValue.fitness) { val exponent = @@ -143,10 +197,6 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe return accept } - private fun calculateFitnessOfSolutionWithDefaultFactors(solution: EVRPTWSolution): Double { - return solution.cost + (ALPHA_DEFAULT * solution.fitnessValue.totalCapacityViolation) + (BETA_DEFAULT * solution.fitnessValue.totalTimeWindowViolation) + (GAMMA_DEFAULT * solution.fitnessValue.totalBatteryCapacityViolation) - } - private fun feasible(solution: EVRPTWSolution): Boolean { return EVRPTWRouteVerifier(solution.instance).verify(solution.routes, solution.cost, false) } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt index c9bf3db..24d9c18 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt @@ -15,7 +15,7 @@ import java.util.Random */ class ShakingNeighbourSolutionGenerator { - private val random = Random(123456) + private val random = Random(java.lang.Double.doubleToLongBits(Math.random())) fun generateRandomPoint(solution: EVRPTWSolution, neighbour: Int): EVRPTWSolution { val resultRoutes = solution.copyOfRoutes() From 5c59b73a2208fb879951380be8c5c1f101511e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Tue, 26 Jun 2018 18:30:41 +0200 Subject: [PATCH 30/36] add fibonacci decrease also when factors are between absolute minimum and lower bound; set factors back to default when they were previously under the lower bound (avoid huge jumps like from 0.2 to 5.0) --- solutions/r102_21_sol.txt | 40 +++++++++---------- solutions/rc203_21_sol.txt | 18 ++++----- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../otl/evrptw/metaheuristic/Constants.kt | 4 +- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 34 +++++++++++----- 5 files changed, 54 insertions(+), 44 deletions(-) diff --git a/solutions/r102_21_sol.txt b/solutions/r102_21_sol.txt index 0562427..0e883b9 100644 --- a/solutions/r102_21_sol.txt +++ b/solutions/r102_21_sol.txt @@ -1,25 +1,23 @@ # solution for r102_21 -1697.4989612687232 -D0, S4, C66, C20, D0 -D0, C7, C48, C82, S0, D0 -D0, S0, S0, C31, C19, C11, S7, C62, C88, D0 -D0, C94, C96, C59, C98, C92, C95, D0 -D0, C74, C75, C56, C72, C21, D0 -D0, C61, C16, C91, C85, C93, C99, D0 +1637.3378594584024 +D0, S4, C66, C20, S0, D0 +D0, S0, S0, S0, C31, C10, C32, C90, S6, C63, C30, D0 +D0, S14, C43, C15, C42, C57, C2, D0 +D0, C8, C46, C47, C36, S8, C49, D0 +D0, C89, C60, C84, C45, C83, S9, C18, D0 +D0, C96, C59, C92, C95, C97, C87, C13, D0 +D0, C6, C52, D0 D0, C24, C55, C25, S19, C4, C73, C26, D0 -D0, S14, C43, C15, C42, C87, C57, C2, D0 -D0, C52, D0 -D0, C5, C17, C86, S12, C44, C37, D0 -D0, C53, C40, C58, D0 -D0, C81, C32, C90, S6, C30, D0 -D0, C89, C60, C84, S10, C45, C83, D0 -D0, S0, C47, S8, S8, C64, C63, C10, S5, D0 -D0, C6, D0 -D0, C18, C8, C46, C36, S8, C49, D0 -D0, C100, S11, C38, C14, C97, C13, D0 -D0, C1, C70, C50, C69, C27, D0 -D0, C68, C54, C80, C12, D0 -D0, C9, S4, C34, C78, C29, C28, D0 -D0, C3, C33, C79, C77, C76, D0 +D0, C53, C74, C75, C56, C72, C21, D0 +D0, S7, C19, S8, S8, C7, C48, C82, D0 +D0, C28, C3, C68, C80, C12, D0 +D0, C61, C16, C91, C85, C93, C99, D0 D0, S18, C39, C67, C23, S17, C22, C41, D0 +D0, S0, C1, C70, C69, C27, D0 +D0, C62, S7, C11, C64, S7, C88, D0 +D0, C40, C54, C58, D0 +D0, C5, S12, C17, C86, C44, C98, D0 +D0, C94, S11, C38, C14, C100, C37, D0 +D0, C9, S4, C34, C78, C29, C77, C76, D0 +D0, C33, C81, C79, C50, D0 D0, S4, C65, C71, C35, S4, C51, D0 diff --git a/solutions/rc203_21_sol.txt b/solutions/rc203_21_sol.txt index 0c4b46f..17e9103 100644 --- a/solutions/rc203_21_sol.txt +++ b/solutions/rc203_21_sol.txt @@ -1,10 +1,10 @@ # solution for rc203_21 -1030.6866552746983 -D0, S0, C96, C72, C54, C61, D0 -D0, C81, C38, C41, C37, C35, C36, C40, C43, C44, C42, C39, C71, C93, S0, D0 -D0, S0, C55, C100, C2, C6, C7, C45, C1, C3, C5, C8, C46, C4, C70, C68, D0 -D0, C69, C60, C79, C73, C78, C88, C98, C82, D0 -D0, S0, C95, C85, C63, C76, C49, C19, C18, C48, C21, C23, C25, C24, C22, C20, C64, C66, D0 -D0, C94, C67, C62, C50, C34, C32, C30, C31, C29, C27, C26, C28, C33, C89, C51, C84, D0 -D0, C90, C14, C47, C17, C16, C11, C10, C9, C87, C74, C86, C52, C13, C15, C12, C53, D0 -D0, C65, C99, C59, C97, C75, C58, C77, C57, C83, C56, C91, C92, C80, D0 +1014.9435341059416 +D0, S0, C91, C92, C80, D0 +D0, S0, C69, C60, C79, C73, C78, C88, C98, C82, D0 +D0, C81, C38, C41, C37, C35, C36, C40, C43, C44, C42, C39, C71, C93, D0 +D0, C90, C65, C99, C57, C56, C95, C50, C34, C32, C30, C31, C29, C27, C26, C28, C33, C89, C51, C84, D0 +D0, C55, C100, C2, C6, C7, C45, C1, C3, C5, C8, C46, C4, C70, C68, D0 +D0, C94, C67, C62, C85, C63, C76, C20, C64, C66, D0 +D0, S0, C83, C24, C22, C49, C19, C18, C48, C21, C23, C25, C77, C58, C75, C97, C16, C17, C47, C11, C10, C9, C87, C59, C74, C86, C52, S11, C13, C15, C14, C12, C53, S0, D0 +D0, C96, C72, C54, C61, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index ff97091..8455e1c 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -64,7 +64,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 3 until 4) { + for (i in 9 until 10) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 007220d..48c40a0 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -12,7 +12,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic class Constants private constructor() { companion object { - const val N_DIST = 40 + const val N_DIST = 50 const val N_FEAS = 5 const val N_TABU = 50 const val N_PENALTY = 2 @@ -20,7 +20,7 @@ class Constants private constructor() { const val TABU_TENURE_MIN = 15 const val TABU_TENURE_MAX = 30 const val NO_CHANGE_THRESHOLD = 3 - const val ALPHA_DEFAULT = 10.0 + const val ALPHA_DEFAULT = 1.0 const val BETA_DEFAULT = 10.0 const val GAMMA_DEFAULT = 10.0 const val VIOLATION_FACTOR_MIN = 1.0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 4d7bf0b..256926e 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -118,13 +118,10 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe decreaseRateAlpha() decreaseRateBeta() decreaseRateGamma() - /*ALPHA = if (ALPHA > ALPHA_DEFAULT) ALPHA_DEFAULT else Math.max(ALPHA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) - BETA = if (BETA > BETA_DEFAULT) BETA_DEFAULT else Math.max(BETA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) - GAMMA = if (GAMMA > GAMMA_DEFAULT) GAMMA_DEFAULT else Math.max(GAMMA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN)*/ thresholdCounter = 0 infeasibleSequenceCounter = 0 feasibleSequenceCounter++ - log("-- VIOLATION FACTORS DECREASED = ($ALPHA, $BETA, $GAMMA). Fibonacci multiplier: ${FIBONACCI[feasibleSequenceCounter--]}") + log("-- VIOLATION FACTORS DECREASED = ($ALPHA, $BETA, $GAMMA). Fibonacci multiplier: ${FIBONACCI[feasibleSequenceCounter - 1]}") } else if (thresholdCounter == -NO_CHANGE_THRESHOLD) { val numberOfCapacityViolations = lastSavedSolutions.stream().filter { it.fitnessValue.totalCapacityViolation > 0.0 }.toList().size @@ -133,36 +130,51 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe val numberOfBatteryCapacityViolations = lastSavedSolutions.stream().filter { it.fitnessValue.totalBatteryCapacityViolation > 0.0 }.toList().size val indexOfMultiplier = infeasibleSequenceCounter % FIBONACCI.size - ALPHA += VIOLATION_FACTOR_INCREASE_RATE * numberOfCapacityViolations * FIBONACCI[indexOfMultiplier] - BETA += VIOLATION_FACTOR_INCREASE_RATE * numberOfTimeWindowViolations * FIBONACCI[indexOfMultiplier] - GAMMA += VIOLATION_FACTOR_INCREASE_RATE * numberOfBatteryCapacityViolations * FIBONACCI[indexOfMultiplier] + if (numberOfCapacityViolations > 0 && ALPHA <= VIOLATION_FACTOR_MIN) { + ALPHA = VIOLATION_FACTOR_MIN + } else { + ALPHA += VIOLATION_FACTOR_INCREASE_RATE * numberOfCapacityViolations * FIBONACCI[indexOfMultiplier] + } + if (numberOfTimeWindowViolations > 0 && BETA <= VIOLATION_FACTOR_MIN) { + BETA = VIOLATION_FACTOR_MIN + } else { + BETA += VIOLATION_FACTOR_INCREASE_RATE * numberOfTimeWindowViolations * FIBONACCI[indexOfMultiplier] + } + if (numberOfBatteryCapacityViolations > 0 && GAMMA <= VIOLATION_FACTOR_MIN) { + GAMMA = VIOLATION_FACTOR_MIN + } else { + GAMMA += VIOLATION_FACTOR_INCREASE_RATE * numberOfBatteryCapacityViolations * FIBONACCI[indexOfMultiplier] + } thresholdCounter = 0 infeasibleSequenceCounter++ feasibleSequenceCounter = 0 - log("++ VIOLATION FACTORS INCREASED = ($ALPHA, $BETA, $GAMMA). Fibonacci multiplier: ${FIBONACCI[infeasibleSequenceCounter--]}") + log("++ VIOLATION FACTORS INCREASED = ($ALPHA, $BETA, $GAMMA). Fibonacci multiplier: ${FIBONACCI[infeasibleSequenceCounter - 1]}") } } private fun decreaseRateAlpha() { + val indexOfMultiplier = feasibleSequenceCounter % FIBONACCI.size ALPHA = when { ALPHA > ALPHA_DEFAULT -> ALPHA_DEFAULT - ALPHA <= VIOLATION_FACTOR_MIN -> Math.max(ALPHA - VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE, VIOLATION_FACTOR_ABSOLUTE_MIN) + ALPHA <= VIOLATION_FACTOR_MIN -> Math.max(ALPHA - (VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE * FIBONACCI[indexOfMultiplier]), VIOLATION_FACTOR_ABSOLUTE_MIN) else -> Math.max(ALPHA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) } } private fun decreaseRateBeta() { + val indexOfMultiplier = feasibleSequenceCounter % FIBONACCI.size BETA = when { BETA > BETA_DEFAULT -> BETA_DEFAULT - BETA <= VIOLATION_FACTOR_MIN -> Math.max(BETA - VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE, VIOLATION_FACTOR_ABSOLUTE_MIN) + BETA <= VIOLATION_FACTOR_MIN -> Math.max(BETA - (VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE * FIBONACCI[indexOfMultiplier]), VIOLATION_FACTOR_ABSOLUTE_MIN) else -> Math.max(BETA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) } } private fun decreaseRateGamma() { + val indexOfMultiplier = feasibleSequenceCounter % FIBONACCI.size GAMMA = when { GAMMA > GAMMA_DEFAULT -> GAMMA_DEFAULT - GAMMA <= VIOLATION_FACTOR_MIN -> Math.max(GAMMA - VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE, VIOLATION_FACTOR_ABSOLUTE_MIN) + GAMMA <= VIOLATION_FACTOR_MIN -> Math.max(GAMMA - (VIOLATION_FACTOR_BELOW_MIN_DESCENT_RATE * FIBONACCI[indexOfMultiplier]), VIOLATION_FACTOR_ABSOLUTE_MIN) else -> Math.max(GAMMA - VIOLATION_FACTOR_DECREASE_RATE, VIOLATION_FACTOR_MIN) } } From c07f79d9268c9ae55c68d384fa5b67398665ff5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Tue, 26 Jun 2018 19:41:10 +0200 Subject: [PATCH 31/36] implement Or-Opt Operator and callable; extend TabuSearch; fix faulty reset of violation factors --- solutions/c103_21_sol.txt | 25 +++--- .../at/ac/tuwien/otl/evrptw/Executor.kt | 2 +- .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../at/ac/tuwien/otl/evrptw/dto/Operator.kt | 1 + .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 6 +- .../neighbourhood/TwoOrOptExplorer.kt | 87 +++++++++++++++++++ .../callable/TwoOrOptExplorerCallable.kt | 20 +++++ .../metaheuristic/tabusearch/TabuSearch.kt | 7 +- 8 files changed, 129 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOrOptExplorer.kt create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOrOptExplorerCallable.kt diff --git a/solutions/c103_21_sol.txt b/solutions/c103_21_sol.txt index 5c438ef..f03db88 100644 --- a/solutions/c103_21_sol.txt +++ b/solutions/c103_21_sol.txt @@ -1,14 +1,15 @@ # solution for c103_21 -1077.5282280519511 -D0, S0, S0, C98, C96, C95, C94, C92, C93, C97, C100, C99, S3, D0 -D0, C91, C89, C88, C85, C84, C82, C83, C86, C87, C90, D0 -D0, C63, S19, C73, C70, C71, C76, C78, C81, S19, D0 -D0, C75, C1, C2, C4, C6, C7, C3, C5, D0 +1103.5082377488513 +D0, S5, C12, C14, C16, C19, C18, C17, C15, C13, D0 +D0, S0, C91, C88, C85, C84, C82, C83, C86, C87, C89, C90, D0 +D0, S19, C73, C70, C71, C76, C78, C81, S19, S0, D0 D0, C23, C25, C27, C30, C28, C26, C10, C11, C9, C8, D0 -D0, C65, C40, C44, C42, C41, C68, C64, C66, D0 -D0, C20, C36, C39, S12, C38, C37, C35, C31, C52, C49, D0 -D0, C32, C33, C34, C29, C24, C22, C21, D0 -D0, S15, C59, C45, C48, C51, C50, C46, C47, C43, D0 -D0, S0, C67, C74, S19, C79, C77, C80, C72, C61, C62, D0 -D0, S7, C12, C14, C16, C19, C18, C17, C15, C13, D0 -D0, S14, C60, C58, C56, C53, C54, C55, C57, C69, D0 +D0, C32, C33, C29, C24, C22, C21, D0 +D0, S0, C75, C96, S3, C2, C4, C6, C7, C3, C5, D0 +D0, C59, C60, C58, C56, C53, S16, C54, C55, C57, D0 +D0, C98, C95, C94, S4, C92, C93, C97, C100, C99, C1, D0 +D0, C67, C65, C69, C68, C64, C66, D0 +D0, C42, C46, C47, C43, D0 +D0, C63, C74, S19, C79, C77, C80, C72, C61, C62, D0 +D0, C20, C34, C36, C39, C38, C37, C35, C31, C52, S13, D0 +D0, S0, C41, C40, C44, C45, C48, C51, C50, C49, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt index 5d48a61..7f844e9 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Executor.kt @@ -19,7 +19,7 @@ class Executor private constructor() { fun getExecutorService(): ExecutorService { if (customExecutorService == null) { - customExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) + customExecutorService = Executors.newFixedThreadPool(10) } return customExecutorService!! } diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 8455e1c..422fc26 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -64,7 +64,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 9 until 10) { + for (i in 0 until 1) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Operator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Operator.kt index b6ecaf9..698e346 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Operator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/Operator.kt @@ -11,6 +11,7 @@ package at.ac.tuwien.otl.evrptw.dto enum class Operator { NONE, TWO_OPT, + TWO_OR_OPT, EXCHANGE, RELOCATE, STATION_IN_RE diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index 256926e..b2acc31 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -130,17 +130,17 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe val numberOfBatteryCapacityViolations = lastSavedSolutions.stream().filter { it.fitnessValue.totalBatteryCapacityViolation > 0.0 }.toList().size val indexOfMultiplier = infeasibleSequenceCounter % FIBONACCI.size - if (numberOfCapacityViolations > 0 && ALPHA <= VIOLATION_FACTOR_MIN) { + if (numberOfCapacityViolations > 0 && ALPHA < VIOLATION_FACTOR_MIN) { ALPHA = VIOLATION_FACTOR_MIN } else { ALPHA += VIOLATION_FACTOR_INCREASE_RATE * numberOfCapacityViolations * FIBONACCI[indexOfMultiplier] } - if (numberOfTimeWindowViolations > 0 && BETA <= VIOLATION_FACTOR_MIN) { + if (numberOfTimeWindowViolations > 0 && BETA < VIOLATION_FACTOR_MIN) { BETA = VIOLATION_FACTOR_MIN } else { BETA += VIOLATION_FACTOR_INCREASE_RATE * numberOfTimeWindowViolations * FIBONACCI[indexOfMultiplier] } - if (numberOfBatteryCapacityViolations > 0 && GAMMA <= VIOLATION_FACTOR_MIN) { + if (numberOfBatteryCapacityViolations > 0 && GAMMA < VIOLATION_FACTOR_MIN) { GAMMA = VIOLATION_FACTOR_MIN } else { GAMMA += VIOLATION_FACTOR_INCREASE_RATE * numberOfBatteryCapacityViolations * FIBONACCI[indexOfMultiplier] diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOrOptExplorer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOrOptExplorer.kt new file mode 100644 index 0000000..4bc0a11 --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/TwoOrOptExplorer.kt @@ -0,0 +1,87 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWInstance +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.dto.Operator +import at.ac.tuwien.otl.evrptw.dto.Route + +/** + *

About this class

+ *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 26.06.2018 + */ +class TwoOrOptExplorer : INeighbourhoodExplorer { + /** + * Explores (enumerates) every possible solution in the search space + * of this neighbourhood based on the initial solution and returns them + * in a list. + * + * @param initialSolution an initial solution for which the neighbours should + * be calculated + * @return a list of neighbour solutions, not necessarily sorted + */ + override fun exploreEverySolution( + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtExcl: Int + ): List { + val result = mutableListOf() + for (routeIndex in startAtIncl until endAtExcl) { + val route = initialSolution.routes[routeIndex] + if (route.size >= 5) { + for (nodeIndex in 1 until route.size - 2) { + if (route[nodeIndex] is EVRPTWInstance.Customer && route[nodeIndex + 1] is EVRPTWInstance.Customer) { + if (nodeIndex > 1) { + for (nodeBefore in 1 until nodeIndex) { + result.add(performOrOptBackwards(initialSolution, routeIndex, nodeIndex, nodeBefore)) + } + } + if (nodeIndex + 1 != route.size - 2) { + for (nodeAfter in nodeIndex + 2 until route.size - 1) { + result.add(performOrOptForwards(initialSolution, routeIndex, nodeIndex, nodeAfter)) + } + } + } + } + } + } + return result + } + + private fun performOrOptBackwards(solution: EVRPTWSolution, routeIndex: Int, indexOfChainFirstNode: Int, insertBeforeIndex: Int): EVRPTWSolution { + val routes = solution.copyOfRoutes() + val route = routes[routeIndex] + val node1 = route[indexOfChainFirstNode] + val node2 = route[indexOfChainFirstNode + 1] + val chain = listOf(node1, node2) + route.removeAll(chain) + route.addAll(insertBeforeIndex, chain) + + return EVRPTWSolution( + solution.instance, + routes, + Route.calculateTotalDistance(routes, solution.instance), + Operator.TWO_OR_OPT + ) + } + + private fun performOrOptForwards(solution: EVRPTWSolution, routeIndex: Int, indexOfChainFirstNode: Int, insertAfterIndex: Int): EVRPTWSolution { + val routes = solution.copyOfRoutes() + val route = routes[routeIndex] + val node1 = route[indexOfChainFirstNode] + val node2 = route[indexOfChainFirstNode + 1] + val chain = listOf(node1, node2) + route.removeAll(chain) + route.addAll(insertAfterIndex - 1, chain) + + return EVRPTWSolution( + solution.instance, + routes, + Route.calculateTotalDistance(routes, solution.instance), + Operator.TWO_OR_OPT + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOrOptExplorerCallable.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOrOptExplorerCallable.kt new file mode 100644 index 0000000..236d6ff --- /dev/null +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/neighbourhood/callable/TwoOrOptExplorerCallable.kt @@ -0,0 +1,20 @@ +package at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable + +import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOrOptExplorer + +/** + *

About this class

+ *

Description

+ * + * @author Daniel Fuevesi + * @version 1.0.0 + * @since 26.06.2018 + */ +class TwoOrOptExplorerCallable( + initialSolution: EVRPTWSolution, + startAtIncl: Int, + endAtIncl: Int, + explorer: TwoOrOptExplorer +) : + INeighbourhoodExplorerCallable(initialSolution, startAtIncl, endAtIncl, explorer) \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt index 74f64cb..abbe021 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/tabusearch/TabuSearch.kt @@ -4,10 +4,7 @@ package at.ac.tuwien.otl.evrptw.metaheuristic.tabusearch import at.ac.tuwien.otl.evrptw.Executor import at.ac.tuwien.otl.evrptw.dto.EVRPTWSolution import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_TABU -import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteExchangeExplorer -import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.InterIntraRouteRelocateExplorer -import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.StationInReExplorer -import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.TwoOptArcExchangeExplorer +import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.* import at.ac.tuwien.otl.evrptw.metaheuristic.neighbourhood.callable.* import java.util.logging.Logger import java.util.stream.Collectors @@ -71,6 +68,8 @@ class TabuSearch(private val logEnabled: Boolean = true) { val middleOfRoutes = numberOfRoutes / 2 callableList.add(TwoOptArcExchangeExplorerCallable(solution, 0, middleOfRoutes, TwoOptArcExchangeExplorer())) callableList.add(TwoOptArcExchangeExplorerCallable(solution, middleOfRoutes, numberOfRoutes, TwoOptArcExchangeExplorer())) + callableList.add(TwoOrOptExplorerCallable(solution, 0, middleOfRoutes, TwoOrOptExplorer())) + callableList.add(TwoOrOptExplorerCallable(solution, middleOfRoutes, numberOfRoutes, TwoOrOptExplorer())) callableList.add(StationInReExplorerCallable(solution, 0, middleOfRoutes, StationInReExplorer())) callableList.add(StationInReExplorerCallable(solution, middleOfRoutes, numberOfRoutes, StationInReExplorer())) callableList.add(InterIntraRouteExchangeExplorerCallable(solution, 0, middleOfRoutes, InterIntraRouteExchangeExplorer())) From 03e3bc2220772cc15b1eff2ca851f5f824761b15 Mon Sep 17 00:00:00 2001 From: David Molnar Date: Tue, 26 Jun 2018 21:39:52 +0200 Subject: [PATCH 32/36] add graph visualizer --- build.gradle | 1 + plots/c103_21.png | Bin 0 -> 11546 bytes plots/c105_21.png | Bin 0 -> 11546 bytes plots/c204_21.png | Bin 0 -> 11400 bytes plots/r102_21.png | Bin 0 -> 17116 bytes plots/r107_21.png | Bin 0 -> 16256 bytes plots/r205_21.png | Bin 0 -> 15298 bytes plots/r211_21.png | Bin 0 -> 14152 bytes plots/rc101_21.png | Bin 0 -> 15637 bytes plots/rc106_21.png | Bin 0 -> 14922 bytes plots/rc203_21.png | Bin 0 -> 13067 bytes solutions/c103_21_sol.txt | 28 +++--- solutions/c105_21_sol.txt | 42 +++----- solutions/c204_21_sol.txt | 10 +- solutions/r102_21_sol.txt | 63 +++++------- solutions/r107_21_sol.txt | 52 +++------- solutions/r205_21_sol.txt | 17 ++-- solutions/r211_21_sol.txt | 10 +- solutions/rc101_21_sol.txt | 62 ++++-------- solutions/rc106_21_sol.txt | 53 +++------- solutions/rc203_21_sol.txt | 19 ++-- .../otl/evrptw/EVRPTWSolutionVisualizer.kt | 95 ++++++++++++++++++ .../kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 15 ++- .../tuwien/otl/evrptw/dto/EVRPTWInstance.java | 2 +- .../ShakingNeighbourSolutionGenerator.kt | 2 +- 25 files changed, 239 insertions(+), 232 deletions(-) create mode 100644 plots/c103_21.png create mode 100644 plots/c105_21.png create mode 100644 plots/c204_21.png create mode 100644 plots/r102_21.png create mode 100644 plots/r107_21.png create mode 100644 plots/r205_21.png create mode 100644 plots/r211_21.png create mode 100644 plots/rc101_21.png create mode 100644 plots/rc106_21.png create mode 100644 plots/rc203_21.png create mode 100644 src/main/kotlin/at/ac/tuwien/otl/evrptw/EVRPTWSolutionVisualizer.kt diff --git a/build.gradle b/build.gradle index 8b8d0de..244af3b 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ dependencies { ktlint "com.github.shyiko:ktlint:0.23.0" compile fileTree(dir: 'libs', include: '*.jar') compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7' + compile group: 'org.graphstream', name: 'gs-core', version: '1.3' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.0.3' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.0.3' diff --git a/plots/c103_21.png b/plots/c103_21.png new file mode 100644 index 0000000000000000000000000000000000000000..676da39b6251f26ad60aa7e768152c34cbe5122d GIT binary patch literal 11546 zcmbVycUV))w`g`ki3(hR<_U`0R>l@7r~u~KZHbQLK|7YIGbfy4rc1w1MscoaEQ z3q(3d6g*K7X-bPUK_ZC(Vgv-5FmcMxnF@_E9a5(z+-5mL>OmTurtsZt4-{RE^24tcxqy zFvC@wVFc56eek%oM^4~#Kjs$(g@UIWoWXi57KI{{!2pd0-vC}Z9ZV!7{%t@cf-eyC zZ?OMgi_mC#`uwkX-sGsOGGEPNVNrg@3~W!Q3=Wu(igE_Nicw%EIT@w}K5&gxR310)?1kj(&{C_5T zi9v+rrNMvU6p_mRES~?%B1DvkOuYt11+KC4$dITc7ls!OHKr^sI>{;H7Nvj~-KJ5796F?(J?O}_1ZXPZ^h^#l@bAV=!p6;d!qLWtB{8arCUh=#8~*nayh=iZMGlXC_8=cb4ai9*bw4BWeLJ zAH8U^D!YQLBX=C7GHzBv!{f#l^Cr(77HYfVx;?ge?ya4hYk!*gtY)Mr-Kf>APXdBe zj7>{_ZZ5CN<=tCLmDxNk$86trVp1k{c=9ZkSvjZsRPLmKB^iaziM%kp=OUEg+!^<& z>)kzjuY$XPvxXx-9Ix$>_ybDF{JT ze5vhYPvofStV{@BPSBQfct5KTcOHV_x;V^HzHU%Rcdz=mw{E$ztxCl8OenSTV1Gu< z^sUMgJ>>-_Aq8RNmANdefSe_Mep5uIos> zT2-awcQ-^HhRKEm>+T8K)sV=2M#H5o=FvuVCAd!Xq)I?ax^ zo=7y*A}&y=Z`5(J%Mw=^MjMs7pwSnH1_yMA{Ltin?s;2?lP6jU!zu(g3qPQJa7qp< zwYp%d-xea#?Qi;vZoSNf<$usnoWDZ=5SDpGEi3MJBw4q>tvX!1vvi@8Cy14XVXf70 zsR@~KEn6nk`M3r*%6V4eZQoaL*pFCA3_@DP9>QzO>ACb5bBlC3Jx}A}jzqNv$041f?ps2DI&SQo3OIkID10&U)#$pQ&C`(iMTVy+f$z*z5qi&+;>oxid?|B&sX$z)`Tnr|1BFu@szZlTzfnlLnPK+8=8&t5Et8)0Cv$1epubdZu56G zssJli*~X@(q)l!&rP^=@pBB~R4}JZfVv|0f*0e@pkATwYI+|(SHZE@ET!~7jd}Tb) zE(Rng4GnzlaTQ4Y(f;*7jo0{8ZC1_ZU%@|Rp4+~<3Za!po|_`L91g>2?Kv#{uMs<2V+7TH)1koM;i z_YK?HU1bd(LaA%Fks8Epj<@@h8DqZST=&|?!|?is*muU=gDVCRCJ69D%0B)mArnp7 zMtetqQ+el!J^mR7st3EqefJi2w%)T>)5#g0GeS?) z5f%4DhfIN1H?93<<LR`ux-nEORg>Mi9DN@3Z9~;i^yTv*N%Wbh@tYlW_8{6KUsCyv{@enUq{0U!|3` z*RlV>Kk1f7gLM+@Nyt6mRD$R{yOmWS4Q?-qKkAq9{uH6e{ z1aK_?Z}xJb+X-m^)OPn7)Nr}oL3WYwnJa~+T9lMPDL z3BAI&6Vh1$VX^l=5hu-lb;-=x#4d&tjJgVkqly=lk1uC>IdKb>%WDChdO6|ZMf$M<{1~@PvQ%l0g4hj$u zd|io$7sV9NT4(D?qmU6dZ%#=ckQE~C7?Y`<{55?AM-@?)M4I-}fGUx;W$AB2X13hY zfRa_8d?FpgQ3PN+m1kvWX%B9-r~y7?2+d%k{u|eQOWe|6R_0uy87Bz|8|aqb$KnLF ztkLmN{`OR^xXRBa_iJDu8v_tg|ES-J$7RKfJKPqg=kQ$<{B4m=tjs{faO5sARTFEE zn)b>L`^WDwIp5RtSJ z6UL*_W&N~uwq(qrk=l_m&>?mlcSz?4q3o$$|G>RMJmi?>?nbu$3%G*Fdm4ry9B{7d- z2FI(4{Oko9J9iJA{&8&;LMjX&k-J=;<;u+D_W6#lhK;f=Ik*F7g#5y{$z9e?K01BD zyc9~E%lOv91%}I@`2YJDGhnlJ;>dBy#p{{uFKKiTn~$e4A|24z=yg z(UK2D>aOQf*sF|E#!mlUcnTA(Kwy## zc1cMtYEofxOczF!_RPh~rZw&V`VKu|>weNFf`E}PE*-tA4BL$2%l)fn5o>vLiTMdb z+V(iz#pUZvngm-5Q%eI%MscXri7o5PF%gF(o->t&D4@WytIS(9mgQYx8`|aO3mdXU zr!^4f$ZeK^Y`Rf|qRRkf@IkZ9k~uykkw#1M^Kfg^V_p~q-T8pCYf*s*5zSsuS{o^Q zr}e=qGw1aDxCI@ZlhNlyZK8;Zm_Z0AS_KcANi{3DmW3Mp9CfkghmnVS)j%W%{wU$~ zrP#;z-jt%gvDvP2R(bXnVsZ|eq>~nH7N^Nh32d48-^4d0zUlOTl{ZsRe;!Jmm7z{V zZ(LGC5Y!rkeW&#PT*}M#sbF}0>-FVV@9{PwN3{-*Zynd()yg|=+@4;w?Za%xt=tEP zFCn(48%=#4VPegr?R93QaWQslHP>ee9!HBTkrPO_$K;&dq&<`KqO&W1H|i>u*(+c# zS%h0R^R}pPyS9sJp4svv#q>={>ydq~I8&k1I)K?Tv_07!1xB6 z!q=U9{eJGXSaUe$9Bau48`+d*+sIwy>6rfz_Q;U?7v^mvA{zsaoZ(J_#2<9v;DAkC zwvv1KEat{@8ZvEcq)huuP#1A01r=H@^QIX(lXI1GPK z57|aIA8?T^H}*#`;c=}~fTpuFQ9b>(!L@WLs?T1*YB;aaaP$}s0BEV<^mnr_L+-lY zOs;iFQ8`&3K0mn!O!uP>!Fn0C*!-E2gv{JaGep12L z1&|BWA-S2VT=CBzAtvjA)#)}8??zJpx+ki96~X0IJhYYujy%}3SCZ*2x{9vba4$2qI7(8sIBR+Yx-(nlm&RX!o+D8UUZ88Cl}#9 zS+Ls=D3Em|bKW-TnR9Mx@h&V=zd=3Bc58H6?S*Q8JY3FYf{?>4fLt)E#9Zx)_nsed zdjNz$yKR(t0G%WDB>JF9Vq+$V9O@20!s@nKWhpjgUUYW{F=DYYfVPo%m={?5Kvz`d zhh&%EgQx|@9}A<*9Ahd2^4mIOyenk`x2TrA=rA}~8`vka2)wzZ1|U=w)*f_Y*REiM zNr_Gr_cRNaVX+@~l8zI;hn6>MxF$SaMwcISXLvSvTD-R$_BsGe!O)zLSu`gHJ>k4h zYQ(_sL7fQ2*4_qO_Y9NKQ51Wnr(F2_f&xIu^EjaJk{ z*(WUTssE4;|~CDu`aG9%0d2IF#?Z;&``xWebqO z8u+crThnUeYtJynGnXK!(vLx zTwnr|pbDoxSic-AAwk)BxB%P@^EbZ@@2OF%4rKs;shzXS(19D;tuS2U`=g!qI6Pku zTtBA7J+jm=%4rV$_CQbCIgOm%9=!S=j%CVgyPiPgU@MU|_~QaZf{~og#-HxuloAynk{fK3|&c6Ys)*PLaHgvyr@)+|vj*%SG)9J&xl)Vb11T;fK;#t+Rj@8RkmEi~6rzAySQf{^` zJBdY!jkic717g2%cl%qemp1a_`=D)Y+kinBmTBM`eOu^z>LL;^+>o6%i4k_a#iz<+ z9}K#uLC(6s?YhOe$23|_yc00SR>NkvHJS(lA|Y9h$x0mFOT8&U;qfdu0~QcC+PfNh zJILvC>w{YT4GL#M`^G$+=NI^)_twJxRhk-#&G3x*#rCIeN+2;jBXjQ=yP-puU^LNJ zmnlzN{{(+2u@&R1Ng_e5#)SUavQy1T3;~l_bQc_2g{zD^_Hz4yLEd8K&gpNvC!RfT zxt)}mll`M2N%5bme~5&`7I_}!9g}iEeYh2p>^_vKCNZccZ|1A-Nqq_G6??Ua+Lt87 zCRwJy!}Ik73DjFmkFqImbQh14huM^2?n&9#Hh6q^M>bHa;g@>Ii-!oOa{Lf&w{V`$0y6k>h~vSS{QLZ-kmDbM)G!)N!?i}Ff2oZ z``$?zE*Eqahlf@VmnEgi!}h6~#1dIhIr%6I0ordfpP%oj9dUBub6MC23M}7Fx>xkN z%_Z7MZKbL~dA?u#NypzKwZ($&tBHuDh!dh{s4N&@X)fqN{nITBSOas_p|Wy> z;B6*t@k0ZfiA96;Ln*#n0g&8VYml^Mei`R#Nq>|ABXX$m+S&gnzQ z237HNw>wGSo3dFyCeJRA^HjS(3E`Q!54h7-=$G4=h^0^%Gr9FuBEh+>g0E3QEvySA z&otz5{!4tAV6l&`Fq$0K?T=Ag#{5vbwc4dn%F0An_1<^|YFF3y(nZ_WN#WH@f1ZZfC9I+U-T76xERa(XH-SSb zJEHH}?>OK_(In(h{<#a`t25F(sq7jc0!`}pT+04351{PqfLbOa0Zm#hRbHG(pZHk= zr9LZM38T?6|M2BOWX7Kn%VF_QIDz)jlt`7hrwZI#iMwi)+6y%{<(;wqHX_N+c5+6hq}b7f zL?*jE82_Q~Zt9c~)|?I}FK717HAK)S<}1{}m8X1^m2qjvJ&g|I-70~#Dj#9pl^k;K zr2m`}Jk07q^~A!k8^vez$;i-8quaH@!r@Zx&u$aq{-S3h%pCAUZWFh;1!`piK60&zrx5B|e}p5!~EUR2KsxJQWsHdIQG!OR>kcmdfMZ7D2+JY{N>~ zJ>>ofRE7(|`7lJI&l^sxVmG)WMzR2W@yxK@|CM6$I7i+qZP@sz2!TENVwOI@zg{G+26T z{k8MG8RO3nytBC<{LAl|5hSP!Ti~$V>e*^bIXr~ExEqu6O8>e|gL`vg`KYM6($A*h zYR0}r)oCrU@NY=(RG*J%9cBE9SYZQl#qX43UbiQLHb`gJ8X=Jwb0+%r-(B-^Bp|32 zAgtBgQ)M%k#N*5gODJS22#f5AQG8%t=!@^rIj_8f6Q@$USZ7K$8~Lo_y^g1kt$~xH zKqv*(Kf>EZbaxaLL%U6f<(6%cqz;!opH+z$$s%jxK{W7ui>qrx5-1nse&89OBj2P_ zo5LNrFrG;KXu2-QX707r^DnbMDpZ=8jAIy5#GgAD4)4qgm)4_|ZBt(<2m^x1QCkK6 zg}hFKZx~DRwXdKfwmAEyuRb}7EA&>jX?WK!{C=VDB)dTqT&D*9H6eI}MGc%v{HMH>eqm3*TE9@HG?W_V%#G42 z^hZL>L3J3%E#5U;mEg^mYJS}o5FDcRZ6(e`g6a*wU5oEDK(>KW@o=7Or=uGExo~@L zRr#-KJtx6Rcn2%`(TL+gWEU*6&ha*>p}XlpZa`x2opf%mTZF z>LoyqpcfMh2Mwp!ehT=4GuYDanQH&$tno>QiJ;10GPStnGG2WHBDQ2Xtn$0yi>DYK zJA$^Cv|6p$;_P>>m~%Bta`TMK<<8t^Me1PF_u;zFT35+$VK-!DDWxbJk2ql>O?_jq zNAO%0mj9?mB7WSq?!4|q)Y-KGK^Da~BA=HS`0*}5cEPTrRQCQJv|il2Iyh3>Ur|Rq zoaS#5^t8nBl`e6&!&E0ke<$3oTY!OY^RhPl{1;mN;k5R_Jo;@>le>0NqqV=g@n#ZR z;s*mAz+a1Zn zD)<9bfN%{S)_wJQGD-ZRHZc4FCR8&l=UVKSO{PpNzNGTPgQdB9@fd}UO9*K@0yvIs8_oOB7#As$`{T-ftrQb=!aVvQRB|be)v77#sswACip!TeN z-fqre*z`)jkI|K?4*qjGZLRqp?TxK-+Tu4-di6bXEIUw$J=5|yq%CMG(d{Sv$ygz$ z1r+GS;E3v_m=@V!g8B0=B0zjRzxio%sgq~$g5$xGIdi{9^WJZ2)rlp66O+rS6Nyos zkS%#yDdvFmr#p?Yxpc+o+Vq;0x8`V z(yf(qPsPuiA2#^B|U7Ehk*Tb7FZdWNO)g!c#Hr15fd= ziYe24VpDLkPb=@K1U2gcuibow%iO{KzM;x-yW1xjb$#Ak7wi3utUf7+*M8EG0`8u| zXN7~Cf_fzWpwo3Q@5a-^#!q*RKk=Gb8D7jP?C6d4|E3#^TlIcUk6(Q+$?4H6_NV&y zr&YaXK55Jyk&v*GdGJ7b(`ySq?aUJ#r4NpT&1X93P0_s(X0M<5sfAu$(Ni8Ks7&R( zAM-DSVLZOEt2^6WOw;nL$t(h1@pD$}OTjDOa(#Ii zed>b4+aOowrBVUoQw<83`1gP0Oe-ipGnS!1TI^RR^q#c!_@2FN2WncYOfs);6*&%c zfIBhgi}70B&P!MNPMs2qM_U5!7H~n^_*wFnV$_E39XT+RlW%x0d>^21%{5;IEZ_d}daGrnl56p=`)$HI7bJSM7p z{O1*56a_QL_dkcp*}f|%h98;})^KRp+`&E9VwyPOg>y;rlah4p3A!PJ(6re>LWo-RjNL*8zhW)aDBw@36%Cp&J*AGiQjL99j#6=at*2}#N>3LR)<#}X?Dzs;7>wtD;DLxM`n7>&V90nZ#~; zVuYz*Y1YO!^Fg(_BkO`pstXg8hH}oDH5t99e04Mq?)2q7qNBEvNELV|Gh$G!+JMjZ z36X4TDZ}mEveb6pce2du9=RRT|7frtjtqr_xR(7oT$|P^X~a9i^Yyvl7%E!V*e8he zYY6V#0Wi^rFfa9kz7DZQ`I{mosT1x_5BxP|t7Lbm)wqrXZi$XMfYIN0Z~ua9gjxLu z5`o{DksB8~T}JfmTLr@6kCHYQh+-BvHTUnjSt?6Y6>mGhXwT;yqsXIM!02dq+H zcQ;QVJh5;(UQ}t}XP@513*Hu%n{W#==sovg66>wax{+!hh==R4A-f2IhIJ3GY`4zo zA9Tg}7fBIXQ4~EBNop}iIfMY3nsUXTnsY)hPD?i*`qZ5;&hH=Q0J-MUT( z#u1V3&{0N$$3f8I2Av9`4jWW8z``juxIK{d^zo9Cx0ivB0wMaHfB$&TQGka*vC3=Z znlfpC2Zn7`6)KCAJHwY?r5Y1x08hXfQ^ig9eI4G`n4jP-!8@$`*B(b1yIVru+dT#i z5>Ua}q!W?pn$L|#$fz53Pc_!vH7+O`ygw{nBC^ENX;_Ng_k&DEra;wp_!voz)e1{L z0|Ll2$t7}85Pp_iN(c2Lqz>7e82><59ifdZ^hDaf;3Jj!o0%o)(9gy!EQo4<{{ru^ zxE5Sa6hHf5wInE(B29mll^~)_HCD;nYKws&w|FfgjAKE8r8-DFi9+uJ)GS=aE)|!^ z?kb9x4Yi4fIkomk0SknZarB>D8&F~fVI>MuC&F;7_Ei5%a3i6hIA&AC+{z`nF+uAH z`2?hm0_&C(0VoBU0XR^Q`wiLOKbC-zPfG#@@L<({fd^6n^YEKCI5utLg7Oe_Q z$;VbHZt9X{TBNu(JKi?!Um{&a0ly>}%T1*JV0um`463VyQm53%3AvI$1|@q9qZX;w z!6JiFkV?(>A#XxZx|XIZ$ zdewCEOQ)y6IZ7Y!hOFQcKOMSTBW#DSJR0axXzByxb<$UtJ$ari-4YCstC zgEM$ngCGzF2<2x;&vTmq9iToPj~;BfuM)kYP0RK22E=$#TR}(3YXOvhae>C;lS|r# z;Y%#thv-v@>L5S>I9#yI7R&PCO$UKS!LtEds#bZus$u&I#9D|Xg=A2r`gdypE95SL zC?_FWQRq`o=uPud!uF8Z*_C8~3RJx}5o;b0 z!Gj_+1dvFu2h(Tf|0es9|0*g*jxJd8e#FeIGFI$w6Dywgw;y$!LLOxT*q@%ivK_Nx z4#(5paMPvON19A`$Ah;gh)D_$lmD`gT%%;OzrO~YI}~VZ{WYk*ai#g9iMWtrla=rv zS{;_6Hixtr;rz9c=ZxnGaCz}|@D}2{5RzLC>WuvvR(#);fCe-jR9aTzD|-S(N8=FUI&;#bjZ;Zk+RGu zKV|T}jy_R_a0I5vn-?~H=#K`Zt z5LRQeh+GOVTt8qkDmV~(IQ>YFDv34lwFcP>%Up&ToU1A>qVOFbWCC2ZOOFXk>c9OQ zuP?A@om>Cg&hf1SpB6v3FMjnsRNghf7X-)PmXIdIN4-nE<&*OAXrwBi&u9`XnL^H> zw#5#$L_fX1#Kx|GGgSRRWmow0+R0bQaY7+np}=w!@(Ym_cy2qO^IxVA3gHP+C2DEK me-8fp(*KR)e_V880kh3k`AmIOo(sT*+PB-9TC~gU;{O5?%N|7l literal 0 HcmV?d00001 diff --git a/plots/c105_21.png b/plots/c105_21.png new file mode 100644 index 0000000000000000000000000000000000000000..f0db887d8d34be89644fb2eb3ef1d4dfc9122265 GIT binary patch literal 11546 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 V=ykc1E&vzi;C?H5?mm|*{{s$AAo2hJ literal 0 HcmV?d00001 diff --git a/plots/c204_21.png b/plots/c204_21.png new file mode 100644 index 0000000000000000000000000000000000000000..9e855a703e83b0b6a9fc6b929cf718bcd9d5c9a3 GIT binary patch literal 11400 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⋘i{vN_egEiiMSE$Xo34v;&F)OOGX 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 bZ)ArhMlm|Lq335Bw55)g0lrAnHt2r<;wO3K literal 0 HcmV?d00001 diff --git a/plots/r211_21.png b/plots/r211_21.png new file mode 100644 index 0000000000000000000000000000000000000000..209d8133716b112aa0e3aa6e5503d7030e7848f7 GIT binary patch literal 14152 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⪚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 literal 0 HcmV?d00001 diff --git a/plots/rc101_21.png b/plots/rc101_21.png new file mode 100644 index 0000000000000000000000000000000000000000..578973b8edf7c0575f1dcaf223adb9509bc5ec2d GIT binary patch literal 15637 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 NJ7Ph~IqZ1ne*s~%UR(eG literal 0 HcmV?d00001 diff --git a/plots/rc106_21.png b/plots/rc106_21.png new file mode 100644 index 0000000000000000000000000000000000000000..32f23b4610567b486ed7df5961e465847c0ded09 GIT binary patch literal 14922 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 Cb9o#9 literal 0 HcmV?d00001 diff --git a/plots/rc203_21.png b/plots/rc203_21.png new file mode 100644 index 0000000000000000000000000000000000000000..16efc77a9c2c403e790e6c1d135e59662c1939a0 GIT binary patch literal 13067 zcmbWeX&}_?+c!R&F_z&g*~T`O(jqCwz6>U`*pjkum0h;%+X$JG%GGsI2vLTtW#8AU z2qF8PD9dCi%Y^Wp(|!M+7ytYDJujYD=6kNkc^v1leU8&TV?#YGD~=TggJI9>lP<$x zNC*Z)oWY<$%VdP@7z}py(|MBCm7uZZNtTD>?QfZki`&P4WYsO~|7!BnIrXHgJyAIg zd3cKU_QDtF#ycLW=mD6D+AG0;KX3#L2I2pYKTx2bNCsX1^T&Uf!C34+%=qs$>=Bxg z2Q|O9-RJja-#2O|P9w=MxVL`LFAlD2FJ>0$Fgy_^#E;*Pq14*11#f5tG1gO3QwtM~ z{l&rhkjUuAK#zvC!_Z7%H#mYw)=zJd{I7PdQL3!^o(Q0q+=@Vfaj+QeKYw6g6By$g zFb)M{tnr_I{O^JP^T+>fDr4#YIpcq~h(_%1Z?1AOz9pku={=D+im2xPUbTm;$UYYE zl8i*OWk5fK*;n?hz#s%nNl9&jiBc;o22Kx$35nn}blT{y7xE;y|9s={MR!`9bMG#mjq{&@|8GY;GFUw{6ePLTHY$HA{3!$xz2z{o(l zhEb#+y{2|CHj!|!A(A)T^V=fN^X$)EDjki&e15FEbOL&58#+9px4*Sinm%Q8UAs8N zXz+)bP4ZB9XU_>NO4$yf7K@${L1Bmz{^OVC(B!{*LQ-rEShL+U%;L&7ZFek=z_TRV z^&UE@#U262=kUzA5aFhI^6ccAE}0@C=FU?i0k82V|B6^P&FoPA3xR>iuru;xmrEn% zQ_ctHJwuDk5O74VvA?U5uDo<_)>N^ZwC~Qx6B%YT>UtGE*N63V5hg_Tq$V-3l&a%rx2l2ARLyGhttPTnzBh%;3GLPGbK{H3H#P zBxklx8%xGbRTK)7c|y&az@waSK~iaMU;P2cA*t}B@gf8QWh^XmHQ38I|C<1W$9MKj zH_AjdFzfTt6TI-!Rvv8M_#nLfy|py~@d;b+Fr=80eW+|B5O8N~TrJE;(Obn|%bWaU zX}E$F17VWcl3hNF#CyHmm6H5%p#{`68<2H0&tt8z%k?SgNejJehu^Mn< zcXa;b&m-}kIIxxssaHWQERNFd98Ban85ft55~)h8e0my=fbdEuNX827dMOXvxjl;! zG4G;o;(+opQFvlg^URB* z#Fq`v2g6FNfJhUAvcFk%4fp>Zi-5xsF>znsNo*D_O-@?bzk(7?TjfxctLy*l#=-$- z;$q`mX5mycW}OL7B(FUl4Qg@~bNsqT_5>Uzi_Odw<=uIr-6pdOA3p;GHrb-nNsoF` zDbde~ftuvIwc_K&OpRW4R|oB^)Qk7b>l+-5Pe0X7KYhq%s-Qaj+%TQqnC$ULc|6pu zwqrXCTm}wy&cyLENfQ>we_C8S5DeB1nk$kijg4D{W)za=W7nxWb>l}ZQFqc*U%XmL zn|pWx5ZR(r%53uwnz9;sk_8IvSGNkHbVX-#I(ypnt&N1Jc7@Bt!+_pqHs?jC-1eOH z9SkF-toES8-btD1Q4em3$h*ulZRoG>UnP69a)V={oog~a!4k(~x(|sIOt4bjl)N` zIG|GveWKOEK+}l+5`AAK9QQZU8@$|%$KfwGXU_Qp69aJ|B=$9U z&SoY2`q2IAb4g&(uLgzrrQ3D+ISm1|UBRcD;Xqc`THklAGJByZeSwqr?=k~ckBF;H z5Y$SnC{<$-mJ@@=c<>(A!09{$@VfPybpm*e)ZL^&LWqi4hXNCjW(5#7{LY|GTdwE& zN+cYGu^(Ss+j7>XL1$EMvoUO~kbp=GrhVElh8djxAtw~6VW)$719PwfgFi2<$Ng6-H%8_9)%s z^h_nS++fgLqaZ%58CZONZ*uauj`C_aeZPD)crd8>{`mZ41wV^m3%KGio0KQ*=brUJ zK6DYIMz6i}6sZ6XPNVUEP216c9;X>G-_zS;^r}$L<=f|??;O29S=mE<{9+4`?lP50 zGK|l~LWR?EIFSps%=st^HT&?A2$;75cO1${dO?)?mj!OujYObi>$EzQ2aov{>KNo! zyWhPaC_$Bf^J#0l%<~-LttB%saTkHX_3W6Qu75wc>OL(;*FRS_dPe}+)?aai2!t+M zcYbk?o;`hyV*eiYA|34iG|NO_lP-}$9a|)iYR3@S|7hTcOWgrWQQi{C*sep81G3xYrIMS~|mD~2oAxg1zV zSpHp1w*UeKgeN@_q>PJN%VgYS`<=VzpWKBp{2%Vk_nv`*GgjTN&jY?ct@HU*0zPsX zALd}6G2nrLX#8?~u2cJ`?=d*a3k#_)R15q#^+IRE=2N8Y#2 zgn5aB(Zk7wQz@5XgeGKNL({lFyQWk(WE5AMdi|lyWl28hQhE9yKUfqHer6L~VHh=z z(zEz$eL6!kFp_((mP*$qGBH;3cw2;Mvf zP(D&O|DUm=-S}Vw@&- zfR}!nn|G0kJ@DT?_6lH-cdApJHjgqWmo?FuFH*v_^uZA`T8zs&a*X&A?E}}oZhqM> zmsyd2w5K zU_uf`BopP1kz{8=p4T^I1?JbJPzO$V=*btWrqx_2 z4H#AMONw+8CQS(fO1Y1b@Wk7mj*WKL1u8NgxdZ{TX8%IB8DA7&$j)U{T>5({+24?i z1g*_So9&At0yrUpLCL0NMVIb$IV)HJ>5^i8+h+ODV?Fe}!B(C4X$C*-v+eD#=^)xv zwH*KTEZ+FyyDLB>ld-9q$hw`;n<`d@dc~gC%a>1;3%QA~GJ}iKf~0Cqg>d2Dq3%83 zVoMBu;{}ZJNY!!0UMs15BljLx7cR-GtTBZxQG2Xl__yw==BXWX{}4S}n9Wa%lLeh= z!JW@UcX_}r-OQJw>;1)uWt@jpZP@L6^Q{>o7nR2T8ftax|NU*gC``Kg zIu0Y2)|UvNEIi-gmvr1lgrg9$;gM zcr_GyqgHF(wZwLFvGrBw5jx%N==}O!3GqbH@mKhp{x513`9GVOT7&jO^8ojZA z7ZB(*vXD7~O&q&_x>d0;Z&lq+Zcw);r}q8XIyS77?FI)1T62>->^d;jP(SO@`7akG zJlP@s8~s*;lWJ_nR5%l_%r&Xb=&r|IJ9bUVa zT}&Ee9VX6QigXhkKTg~zN?48jVp1KRf8OW5mv>`Z3xQ_|Yc&QEC0( zzxQaGnqG#T1vJl139kH)yE4?jyWdKKTZK^&o!I5z3>3>6{DUxB$Lt-s3 z94BXnm^eM^dOeb~YF}z4QDI45*p^(Nswlq*uH*!nh%6cCs}cR6*mb~4eVJZ3FI#X$ zPYO-Z$EN*aNAGF^b@_lkN2Z8IRk3mO(RkfN!W753D1#|)2GjR?cQI}4+o^@Rf1;M? z#S++$$&quodfo)b^Tzz!VUKy-7GA;S0(tQ|qMm%g#sM%O^mx+>=&AHUv032silS-99DC?mk z8U4O0gFV4;@y!R!GtCj8%YG>*H12W9m$z2+)uS_I|5&s{<4OvEUvF_se-Ngq|4m^i z(M!h^yJz|&LrygR`3IBT8*bP+3t%^8699SLtvVYW?SWX2MVsKPK%o!YX;(@{X0D`< z;l)U4-1r#geijVWdrFOz{^0kvSD{D{uj__sl-T^OvsNqeXQSI>IvxI{aCtrV%aX81_GLci+_6hh#g9#C!~e=w;7Ts zN-9JQXAVeA?1RY1s)s%&z_^s_7YDS|{pGcvDDQ(T!tdMsONorXAN>o`GLS2=lMB-? zrG>t}yt0X?qy4xT*9ksy{d8F~ywC2MGD&nmC1$Hd8f z%OBjdR_>Oh#l>lA%01_}2ZO(OKKdZ~^z}P2=-b^eF|`6dkqcg!7r|;0#IWJS%}2n< z9ZFTm@$QU z56V@}LlQC`ydwK7HbiT`H$Mae9S(_r&djXimdg4Y4OP`n&sKQ(FBe;VO@YsV}R|hu*<}GbU=Ad{^lm@ zk?D8E5Wk`OOD0A}((ZD2756SJ{+i$EtitUA>NMSh*B#mBVX$;QO3arzcU0!ft>Zh^ zcLvm0EJ4)R(t3S1a!~1IL`LZD$CRGeu5OmUyWJ|=)HgA$A1V`<1wlN9QSV@F-^P#(SM^~uMN>@tXBsyHAR@{Qmc96(ywj^vFd+TJ z7Vdbv^oZ==!(p!Er{NZQEv+jgw3-e*5}Wl=w|UtPCAhmck@Yv#7L4C8tx^=D6tzU_o{ z$hDNe*z!!V_BU`LcPaX0mr46&5q?YIBSE!O&fg9n1x&6{@Q@215;T%2s(xJd6}fYj zgIyij{GHI~$cQG%U+%ia_TbJ;=8=1U4m>nV86DMb?jkf^W<-=^r|zF0qH>3QUH$iW z*|8q0rf~zi6Ar*tkpC65!?olF!0X;T7hRryqXSk4moeY|Ykpl=F9I?QQ&st4h-yItkscL!sh-f|p z1`>xquMGr!+qb3g15-2Xf4iQ6j9M!^sm9*yo_uy8=Sln~@Ame5MpmusId=pjf{<0} znv!+9vWm2mR3iXHd+m;H+wJp$Xh}``sK<@`!{I z3851&6;*7mmyOW<_`JhS#*!IGsacDB%r>qs^YQv_n!7`QWkY!n;oj7X?`{^{Xd=>);zNY<8n>U=KGq18%(Js49d)>9j$%Y7qNRK zBaQ8iJL0jYDA@4(w;1)5*9(4pS<^0hD_^*=P~J41Q;tm|Efi$z^- zGPWatkzVyWC0FY-S#>)sPTmher|_;@hwimLB!DiMjbP_ZY~U?8l!q`;XIQcvnX zE~fn$j6H_oOnj$CS6kw(d?d8lTG#zmdf2i~eJ@*PN~65FLiE3eyqa!Ljxg-ZeC)<* zsX3Fp-+3$No&R0f4=0M!P1QH2!Ju51?|KIVcb|WHM{Jd~YbDH{a&)%T^@^i#uehP{ zM52SKZ3wGrJgj#|{$*{CeY%tT(6`&~#wU~1^bMbQ?gLilut7&6BMIioX^4LWdF3(d z@|JRu`RYFr3sb9$E-_GZ8UW?}xBV1lhJK5{5jC-tl#;}C3nSIql#(ZIXls(X-jni{ zw;2Rt*CFu$ud-#)pp$B0cPUhfJ>^C{GWixhXoUL4(si;B!aK2Y)heFYQmx|x$Uf<#NkDl# ztog7*E6ku<#p=|r!Y$c~XAe=!LO++YgH8e;>97(hL2Ob$QiUgKX3qK^rp<)_!Zy+y z+6g9Gu^4DEgG&6IBZh&b`(;1wr`K_dTpsS5*bASdpH!rr6l$gkh`Zg&k#(;h@j7#s zI58M9S43Gl%+nO0)$-7@aSf`jU-=%$VX>VeF^8yL~+siwI?DaAMKUqDA&0$q+!r7r;eKsrgZ#KTowkjbrn2 zExWr6d!;553iFWlP#Tx`)#n+)NYY2=N~Ot#yfagIwr;KdOZv+XhM9qyv5Q_(mLOp` z*O@2_^Yf^7rUqx}k9UEaLe$DGEyP%u^X!I%l;)0U88)t!Q z9FQLnit^@&v>}`SxZ?wY>YQlj{NX6r89Pc<$ns=pSDo|}EA*YH!fMu>@ z;^mR)kHtfuhis=~z5gLNd!W&>dx4QLG8q=6_SfC2Qb%4DK^;!BSPXRPbx)sIRLUiZU@zivg4v$_{+o27|40xcYlq>S4>;Kd_~%d zX{8urkUj10Ycu45d{ENVU*!QI#Tw%_Pd{>W1`-bEMLjQy()UEBxUK`J(WT(ESAvQo z(c0DWS$;!y`HOp(jNBhBYIvrWaWXD8_RuxtBzt$z$L*y%t)m1RE1i_~Mr!5o?ToU7 zF+OlZ(5n4CF%IT7MLc-JG27)QfP1$XRlJN0s?+iLqBBRZLm!ao&6eKA$?m6SCo3&> zB>{E%K~=1W`q(0}i>Lj2<;>YQmVE0Mc^Trx#l}%#J_MzI)chPOwR(uBlyW=$b=r!? zi}w!UiL%ma*0Gc{)2Ei~w)yF`37<0+hOL=ZZ?$e+1lQ3WE>6I2hCqM&b-$5e!gPN4 z4a6VML}@2r)kP8IOVK4RR{6CG1>a8A8+1eB)<`6t=<-1&y=7o_ihe6OPj~eWDPA>Z zSwtv{Q>S1%eARQQ5b6sRsIdX)YKa-8H&ZazxwxA*5Hr60WptpwWHrfdWmV79)GLNE z)aktuHo^eBbp#0pHBli$FrUB>o0o~Zx7a}@Ty1pCXfI+;fw1%OQZ-zhC&J16E+DQu zwVWqKqTfFtD7RW?WaUpKDCUhK?(;K5+v`pUT$-+yG;gB3ej_*jGVSwj>NNMz=t}o$ zZcm}b-D|5qh9X)Ufbp>Zq}7L4*C3Ksp)Dt5-DUFAWgLat!|${A?+RlTB1QI;UMwYF zceM!q;P-wR?9GRt?t6cSGWy7_Xuar-D&X~hd+^<)P8L*zHwuVqQP6tC&{#r(>V%&tm#k^rXS8e%00|uM7Qv#IY&aeMX_`GZQbx5%~x5d$IGs&L1)lQ!C5-<-QMY=$e>K;-eM+xhnVfaCiGp^p(X?>5C= z?HH)q9TP8|&&&6#Vn=t3i*m*weD2Cy5%`xvsZSEb%$N$Hr|f?bK{)r_~6=IoomL{sFy*zXxOPO<0y5@Z#+k9{yS>@ALZXE2Dog z{GnG1E#>`RH{GAD^5C6vqy7Q;%-h%X$cLV_2fmBJd^u^Z`Gw7C@dsQWk|CQfmKJDs zno-(|qqx?~m&zfj@7YGU+6#?pa)#o>rdC7ub4mJ}sV(eO_r zU$uN0g&SU4X7fgZ%R~F5nv`uea93c__cu$rHdwgQ-rBH&Ds0mbG8_IctnnhnxDx|a zaG@|wM8`=Cw3PC7$rji!n0&6eCYIW5fQ~$I_iw=#t_a7k@aD#-SA(`H9AP^AbRt{t2RHQdk3LIQ+4Uyzu+~P$o z&1U97JM_tl^KeFyJH~PE6fb@t;$Y324_`DC1#^hJ1LkNbhpAVjAJr2ZVut`?6vEp7 z0WPjmk3b<%nTu^Ths9BprgA8-^Yp4oviyoC$H*uFvppN+JAJtI#9SV0k@Ik10YyU~d1 z`*L6P?UW%4q}^r-&)Q&1GJB*^P**<&xM9Brdls7bxqlVQL5qp&LC>oa!<~J3>&48HR0!(J%DSbzC>2E+TJ;pvB9{mQq4mNN!M5$f0r_;h&7xyR zwD5GVgE|F&Y#Y6g($g6J`o&)~F(*qqZBzs0S7@kLz2{KF>W9DvZ8G@?vW@;xk*d5H zd|xk~{!o6*XkFCxqh3r@I(%-ayqYTekIZyBE#J(h@bF(V%co_0D|?1^7xdR$-<;MW zldtQvbwv;7YOO7;r#Gk{{GMEp?Nud~6(vvHG^uS659B_XS8Rp=(Wn(DC+vFsNLSwy z$e+Fs3ZEb#oov-v`FuI_b?<9zv(@IOw_4SqJ!!^O7b(39vhL(#duhC@T$3^(89A}w z%>eq|D05LIx}|-i;3urC{pZs+wwChTf!)Mq6{|%jI8MX~10~Kg4~AW(Ni?advm1k& zD6VWu2y-!~FY~P-IamGF_k?ktVe3n*Ud)tF%WpSbAN9Exf2T2Fh(WnH>kGvRpP$1_ ze3#lT`QvOjjEdubHz+){SM~6z*It<~V--{0^#$<(j04N5jCH*wr!elRYs3Z-y4Syh zOHh+qD;3qVh6?_OfsX8Uk9le+#>{>1T%Kmad~RBU1nkZ8gO%%T{S0SoLY_;wY_M}3 zS)F!Ft$YMv+nE>R^Ze#Rw`>h_$9V%^ZAWC(E>>WTFF!f8^(wFbhnhYIs5XkLH64e4 zSrPY`?mc^vAK-+Lh;bt~iekd|SM=T)1gc~i@7Uxx4Eb!1QRyY&cr}nSp}CN}acImq z@}#Gf8z*CmJu!g=8qo!6jJ&7C=D)WzKc>KMmf?m4sq^cOv)(@1h2=H=M@J6C0U(1E zy6mNmssPnd`}`Fifsbjz;JpS8|dbARK(N(YGdN=CH2qu^fyxXS9Sx@f#Y`}tw(#NZgx+0 zLqYa;L^Ne{s|lc{)7_?S`|Q0HkB0hR<$T0)1lpNf59+O~f_h&}96EY^k{n8>r>Kup zpyqLq-#85%Vi3j70Vy9Oc%CC?e%(Lri@Us3b_p1>nVDF)CAp*?!&Oibd07u24}JD; z+~ltK>W2@)ReWXMXjzaK)Wh8_k&lgPZf7AuQAR{^#ddw8GH{i?wV^jJ_H(xndAoZ9 z1mav998l?s9Z;BLG$kKBvrEs**;`(G)TOgQ&t`Ut&WD}Zm49t=9poJL_U#SGUQ>x5 z0pw%#nCfA?fb#w8-n<}xzKW9otcY$6(9A1g1Ayzt z!gz7)j|IOqD->mlDq9%gpaQh-g$Q84Yh;^rAnY{@R9_sm;IqVys~80^d#XTP`2$Aa zH*9pfA-$pC>E?LYt{}+d&y$~SauDlR9sgEsqPt<(7qr{H_m9mwq2;8Xi;L@B z4^0<$wFA3e;t>x~e3zXQIdGeQMA=nXbFwD*TdF`AhJok%rjGL?q}wXaYOx&=z$-j;v>!dC-=(IBjvSx94|hBQUY6vY z0rjtSU;D@AH+Kl|ie?_|whsa9oLdYtI}rWB4cm4f`$)GbK*^ml(jn8_tN{dX?qvL| z{>8`)g{9*Cvz%)NuTKK(KlAO)?1oOoS1E|`xd;9EGJ}V~6DZU)nrb&X8JjMF&qL!*AZx zJt&j9>5eOS|BPyC^BTZ9_EF8VyGi$}jbak&B5ps{pFDAvu$RfZNa zO1uF=K?Lr3H06r$1E5{w-iu++BUqm-3j?^Nu&)94;KF`HEX<_FL;i4`&8jku{x{6W zcXE6PXvm9%t?%=E!ym8RP6-qy6L{xQlr+zZ3Yn0!hAS#|R6C@DATT;LgyBHGBh&SE zb96a{D|xa$U+@LZBVM&dlTLcfl&N+9C7Y)n;{Zw7!l$VcSI3NB^eGXD1y_ttb|rh_ z0JVFex)q6u8((HlhuJSh+Rwc9xSZ>UxXN5kO4sp#G? z+3_90y~FO(a*PLJFjA43{$7I4o{KhwFR23&f99Gu{l@%4AsZI&qWkpbWchCof=h4| zEb5DN)$^tP&v;^{m_voX6mjG#IAt^Ziz{BA34_F>9717e;6<8}H1TDT14Y9^KwheD z(_XLKc^6z!TMj7E9nUWAa@nPP<-Za%MH*?@LID&viKysS)2H4W@NQQ1q47ZPDmE@< zyMTeIBZiX}GK~Ji={2Yn-jL!-D(r_8>E**1jpfb@q9ONgf~uT$%o_P!Y{!t*3ANwb z=WVh#H-Zr{o)AdX4@(k}t>d&)IQ+_%U7q{f3pCw&3tn*dnqlRgF|<=sMC*Dg%pnzA ze0J8%04B=Z@k@y?6VsDRUA_RAVrR{@)u`>%)ypn&sjzcF{j<( z+KhXKK8)Vk-W~qh6KJCrKD)mWIv?NfXVF+LbF~;w02Y-0iVIEzAZl~fOv*-<70-Kp zgV~@ZK2R7fkv$(&#RerlejKtDwH$M`)0%8t%V2TrJw%%`g_7QKyCu5MLkbbYDCXOH zbKw!g+7pBNbHOYX+?J>5io>n<37NNmpFpzfAV@D=Q-26;rjJv`s~Tz}Qyhc~Dpm$h zfQgSC%*nrQjrh`>Dnq~NJuD$54NsZ~?i5BZJb4#5e(jItBes@6S_M9P9%>SSg*7{N zi5Xe)VZ6p#V;83}vUQ2jt{aELp`Q}mVp#y}Z$=@<^Lt4>N#PDxmFf`>yU_epu_#&>2f-xGMRzI@SHV(i1D*ad2VHOiONaRqinCr!vf;-cpVMeg@HD$CdaC`X=633g@0? zxH2UlZ&{aY+oyTx0Uk!bHC~Co&g5j5G185daSoZoLhN61d8P{B<){(1OfgGYCH^iG zsX7zn5(eOj&?zAyr{}yLr@vbu-TpLqjuZ%fHWA%0KX3yznO=5JULp34Tr5LC}1pz2P;( z7)>Mx)sAbout this class + + *

Description of this class

+ * + * @author David Molnar + * @version 1.0.0 + * @since 1.0.0 + */ +class EVRPTWSolutionVisualizer { + private var edgeIdCounter = 0 + + fun visualizeSolution(instance: EVRPTWInstance) { + val solution = loadSolutionForInstance(instance) + plotSolution(instance, solution) + } + + private fun plotSolution(instance: EVRPTWInstance, solution: EVRPTWSolution) { + val graph = MultiGraph(instance.name) + graph.addAttribute("ui.screenshot", "plots/${instance.name}.png") + + // plot depot + graph.addNode(instance.depot.id.toString()) + val depotNode = graph.getNode(instance.depot.id.toString()) + depotNode.setAttribute("xy", instance.getLocation(instance.depot).x, instance.getLocation(instance.depot).y) + depotNode.setAttribute("ui.label", instance.depot.id) + depotNode.addAttribute("ui.style", "fill-color: red;") + + // plot recharge stations + for (station in instance.rechargingStations) { + graph.addNode(station.id.toString()) + val stationNode = graph.getNode(station.id.toString()) + stationNode.setAttribute("xy", station.location.x, station.location.y) + stationNode.setAttribute("ui.label", station.name) + stationNode.addAttribute("ui.style", "fill-color: #2E8B57;") + } + + // plot customers + for (customer in instance.customers) { + graph.addNode(customer.id.toString()) + val customerNode = graph.getNode(customer.id.toString()) + customerNode.setAttribute("xy", customer.location.x, customer.location.y) + customerNode.setAttribute("ui.label", customer.name) + } + + // plot routes + for (route in solution.routes) { + for (i in 0 until route.size - 1) { + graph.addEdge((++edgeIdCounter).toString(), route[i].id, route[i + 1].id) + } + } + + graph.display(false) + } + + private fun loadSolutionForInstance(instance: EVRPTWInstance): EVRPTWSolution { + File("solutions/" + instance.name + "_sol.txt").bufferedReader().use { reader -> + reader.readLine() // skip first line + + val cost = reader.readLine().toDouble() + + val routes = mutableListOf>() + + val iter = reader.lines().iterator() + while (iter.hasNext()) { + val line = iter.next() + + val nodeStrings = line.replace(" ", "").split(",") + + val route = mutableListOf() + + for (nodeString in nodeStrings) { + when { + nodeString.startsWith("D") -> route.add(instance.depot) + nodeString.startsWith("S") -> route.add(instance.rechargingStationMap[nodeString] as EVRPTWInstance.Node) + else -> route.add(instance.customerMap[nodeString] as EVRPTWInstance.Node) + } + } + + routes.add(route) + } + + return EVRPTWSolution(instance, routes, cost) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 8d8d585..5aa47c9 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -64,10 +64,23 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 1 until 2) { + for (i in 6 until 7) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() + +// plotSolutions() + } + + private fun plotSolutions() { + val visualizer = EVRPTWSolutionVisualizer() + val instanceLoader = InstanceLoader() + + for (i in 0 until 10) { + val instanceString = instances[i] + val instance = instanceLoader.load(instanceString) + visualizer.visualizeSolution(instance) + } } private fun runAlgorithmOnInstance(instanceId: Int, detailed: Boolean): Long { diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWInstance.java b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWInstance.java index 65b86e7..f4b6e51 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWInstance.java +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/dto/EVRPTWInstance.java @@ -163,7 +163,7 @@ public double getDemand(Node node) { return customers.get((int) node.id - (rechargingStations.size() + 1)).demand; } - private Node.Location getLocation(Node node) { + public Node.Location getLocation(Node node) { if(node.id == depot.id) return depot.location; else if(node.id > rechargingStations.size()) return customers .get((int) node.id - (rechargingStations.size() + 1)).location; diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt index c9bf3db..24d9c18 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/ShakingNeighbourSolutionGenerator.kt @@ -15,7 +15,7 @@ import java.util.Random */ class ShakingNeighbourSolutionGenerator { - private val random = Random(123456) + private val random = Random(java.lang.Double.doubleToLongBits(Math.random())) fun generateRandomPoint(solution: EVRPTWSolution, neighbour: Int): EVRPTWSolution { val resultRoutes = solution.copyOfRoutes() From f0249cfed33faa7ce7a0249504a3772102f90569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20F=C3=BCvesi?= Date: Tue, 26 Jun 2018 21:47:33 +0200 Subject: [PATCH 33/36] add starting values to violation factors --- solutions/c105_21_sol.txt | 16 ++++++++-------- solutions/c204_21_sol.txt | 11 +++++------ src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt | 2 +- .../tuwien/otl/evrptw/metaheuristic/Constants.kt | 9 ++++++--- .../metaheuristic/HybridVnsTsMetaHeuristic.kt | 9 ++++++--- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/solutions/c105_21_sol.txt b/solutions/c105_21_sol.txt index fd60368..778e0b2 100644 --- a/solutions/c105_21_sol.txt +++ b/solutions/c105_21_sol.txt @@ -1,14 +1,14 @@ # solution for c105_21 -1046.6681306152846 +1035.0671492266233 D0, S3, C98, C96, C95, C94, C92, C93, C97, C100, C99, D0 -D0, S0, C63, C74, C79, C77, C80, S19, D0 -D0, C41, C40, C42, C44, C45, C48, C46, C47, C43, D0 -D0, C75, C1, C2, C4, C6, C7, C3, C5, D0 +D0, C67, C65, C62, C64, C68, C69, D0 +D0, S0, C59, C60, C58, C56, C53, S16, C54, C55, C57, D0 D0, S19, C73, C70, C71, C76, C78, C81, S19, D0 D0, C20, C24, C32, C33, C31, S11, C51, C50, C52, C49, D0 -D0, C67, C65, C62, C64, C68, C69, C72, C61, C66, D0 D0, C21, C22, C25, C27, C30, C28, C26, C10, C11, C9, C8, D0 -D0, C90, C87, C86, C82, C83, C84, C85, C88, C89, C91, D0 -D0, S5, C12, C14, C16, C19, C18, C17, C15, C13, D0 -D0, C59, C60, C58, C56, C53, S16, C54, C55, C57, D0 +D0, S0, S0, C41, C40, C42, C44, C45, C48, C46, C47, C43, D0 +D0, C75, C1, C2, C4, C6, C7, C3, C5, D0 +D0, C63, C74, S19, C79, C77, C80, C72, C61, C66, D0 D0, C23, C29, C34, C36, C39, C38, C37, C35, S13, D0 +D0, S0, C12, C14, C16, C19, C18, C17, C15, S7, C13, D0 +D0, C90, C87, C86, C82, C83, C84, C85, C88, C89, C91, D0 diff --git a/solutions/c204_21_sol.txt b/solutions/c204_21_sol.txt index ca86433..3ebf5bc 100644 --- a/solutions/c204_21_sol.txt +++ b/solutions/c204_21_sol.txt @@ -1,7 +1,6 @@ # solution for c204_21 -673.3010608930191 -D0, S0, C22, C30, S9, C23, C26, C28, C34, C36, C39, C38, C37, C35, C31, C33, C32, C6, C29, C27, C24, C20, D0 -D0, S0, S0, C8, C10, C11, C9, C13, C15, C12, C14, C16, C19, C18, C17, C25, C21, D0 -D0, C90, C91, C88, C86, C84, C83, C82, C85, S20, C76, C71, C70, C73, C80, C79, C81, C78, C77, C87, C96, C63, C67, D0 -D0, C48, C43, C41, C42, C47, C52, C50, C51, C45, C46, C44, C40, C57, C55, C54, C59, C60, C58, C56, C53, S16, C49, C65, C68, C64, C72, C61, C74, C62, C66, C69, D0 -D0, C89, C4, C3, C95, C94, C92, C97, C100, C99, C98, C7, C1, C2, C75, C5, C93, D0 +677.0880504484361 +D0, S0, S0, C8, C10, C11, C9, C13, C15, C12, C14, C16, C19, C18, C17, C25, C23, C26, S10, C28, C34, C36, C39, C38, C37, C35, C31, C33, C32, C6, C29, C30, C27, C24, C22, C21, D0 +D0, S0, S0, S0, C93, C5, C75, C2, C1, C95, C94, C92, C97, C100, C99, S3, C98, C7, C3, C4, C89, C90, C96, C63, C67, D0 +D0, C91, C88, C86, C84, C83, C82, C85, S20, C80, C79, C81, C73, C70, C71, C76, C78, C77, C87, D0 +D0, C20, C47, C52, C50, C51, C45, C46, C44, C40, C57, C55, C54, C59, C60, C58, C56, C53, S16, C49, C65, C64, C61, C72, C74, C62, C66, C69, C68, C41, C42, C43, C48, D0 diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt index 422fc26..ff97091 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/Main.kt @@ -64,7 +64,7 @@ class Main { println("instanceId: $i, avg. runtime: ${TimeUnit.NANOSECONDS.toMillis(instanceRuntimeMap[i]!!.average().toLong())} ms") }*/ // println(Random().nextInt(1-1) + 1) - for (i in 0 until 1) { + for (i in 3 until 4) { runAlgorithmOnInstance(i, false) } Executor.getExecutorService().shutdown() diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt index 48c40a0..aba440f 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/Constants.kt @@ -28,9 +28,12 @@ class Constants private constructor() { const val VIOLATION_FACTOR_ABSOLUTE_MIN = 0.1 const val VIOLATION_FACTOR_INCREASE_RATE = 2.0 const val VIOLATION_FACTOR_DECREASE_RATE = 5.0 - var ALPHA = ALPHA_DEFAULT - var BETA = BETA_DEFAULT - var GAMMA = GAMMA_DEFAULT + const val ALPHA_STARTING = 1.0 + const val BETA_STARTING = 2.0 + const val GAMMA_STARTING = 2.0 + var ALPHA = ALPHA_STARTING + var BETA = BETA_STARTING + var GAMMA = GAMMA_STARTING val FIBONACCI = arrayOf(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610) } } \ No newline at end of file diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt index b2acc31..02c328c 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/metaheuristic/HybridVnsTsMetaHeuristic.kt @@ -5,12 +5,15 @@ import at.ac.tuwien.otl.evrptw.Main import at.ac.tuwien.otl.evrptw.dto.* import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.ALPHA import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.ALPHA_DEFAULT +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.ALPHA_STARTING import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.BETA import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.BETA_DEFAULT +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.BETA_STARTING import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.COOLING_FACTOR import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.FIBONACCI import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.GAMMA import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.GAMMA_DEFAULT +import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.GAMMA_STARTING import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.NO_CHANGE_THRESHOLD import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_DIST import at.ac.tuwien.otl.evrptw.metaheuristic.Constants.Companion.N_FEAS @@ -98,9 +101,9 @@ class HybridVnsTsMetaHeuristic(private val logEnabled: Boolean = true) : IMetaHe } private fun resetParameters() { - ALPHA = ALPHA_DEFAULT - BETA = BETA_DEFAULT - GAMMA = GAMMA_DEFAULT + ALPHA = ALPHA_STARTING + BETA = BETA_STARTING + GAMMA = GAMMA_STARTING lastSavedSolutions.clear() thresholdCounter = 0 infeasibleSequenceCounter = 0 From 69489ab186dd1b45ce7024da11663c7dfe45e65a Mon Sep 17 00:00:00 2001 From: David Molnar Date: Tue, 26 Jun 2018 22:25:10 +0200 Subject: [PATCH 34/36] improve graph visualization --- plots/c103_21.png | Bin 11546 -> 10780 bytes plots/c105_21.png | Bin 11546 -> 10696 bytes plots/c204_21.png | Bin 11400 -> 9648 bytes plots/r102_21.png | Bin 17116 -> 17308 bytes plots/r107_21.png | Bin 16256 -> 15903 bytes plots/r205_21.png | Bin 15298 -> 14408 bytes plots/r211_21.png | Bin 14152 -> 12658 bytes plots/rc101_21.png | Bin 15637 -> 15892 bytes plots/rc106_21.png | Bin 14922 -> 14637 bytes plots/rc203_21.png | Bin 13067 -> 11641 bytes .../otl/evrptw/EVRPTWSolutionVisualizer.kt | 11 ++++++++--- 11 files changed, 8 insertions(+), 3 deletions(-) diff --git a/plots/c103_21.png b/plots/c103_21.png index 676da39b6251f26ad60aa7e768152c34cbe5122d..7881c4317b3663ec9cd9cf79f9e482ed9ed1eca7 100644 GIT binary patch literal 10780 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 literal 11546 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 diff --git a/plots/c105_21.png b/plots/c105_21.png index f0db887d8d34be89644fb2eb3ef1d4dfc9122265..3254cb4a047b2bc75729262f0f505541e34f9999 100644 GIT binary patch literal 10696 zcmb_?c{r5o|L|-W!aPr<5aXGd7D=JBU@-AmQs0WQq>imkDxqYuj>0@jmX6BlltP_2 z2&GVlFw#njHd{sxk!3K*`rgktzwi4!@9({S*Y#f4+aH?eUOxN%xj)MtyK}pZl7hMd zfk05&Vv9Nu2xN#rAQ@0)!AMKE+ZO_1Wx^J8^KR~!z7~_(rCVKj?QN(<<+II|Ha)97 z&QiiBKk?2*eIof35cY*`h>RxED-X#0Nc+v$LDMH-XnvBodi_lso&A69KKNmJwl)6G zmw7h<Cb2inQ8VUaZcYmkAJnvtC$ux4Z3N>{`J)hM6P@YOf{JD=qTCwR0@4U$0MjJaMN?%(@i>}_+ zNnDqRDz4XeMPHZZU7!9X*8*4@M7yiC`lXs}|BoiW%5J#eWtqcR(1`tVpyH|R?2%^k zxNpOgGA&U5Dh_uWCW2K)oT?Ri9~GQKqiqMSD(XDOn@cDd@u0L z;{g*-kf*{1bzj%eAxK1oAh7KmkHaa>C6LaZ5pCvRa{9a5%5RW>a@cE`#qn}PB1sgs zPfiRdqiaHQpzFEgTD8kL>V4u{889eNze^NujVx!@fElpx52+iWrhj0mCc zGv|79epFZGMZAp$^Bi`Z+4T!@6tvvWFL4ux^Yp!YoYt(DiwT`M6F3xjJ^nMfvcNiZ z3A8%l-Sy$%CX+lr!1>_bs>;@^XC8F%&u*3Yh5g=LZYNfo_5AuEFJss>5G+VEGz_&y zv%@Ycwysn9x~f@rLLlq&qqnED60ph{`U)8}pg($j##22B?(G}$KlHBdEXAe3;{i!b zNv;Gd%9Z~C4gzxKDW?xG#G#Y4o7X+0X7L zY!XUTs6ZN9DMIhbp=CaYJ`5EkOwC^pR$b!r;jxF6gR2n=I)HWmGy_N8qXNt9lsw3{ zsrg-7O?Zf%c-;?z9+JHVI<~M%X+TFVXdlaXK=ZS)*Ug6sDN4B_##%&UTnU1nIuK#O z4q`>On`J041Te6c$Y4T{#H?&7u8wU>pu<}0{)8ds*?LiX*pPS`7$l|pH6@)Z=Xs3q zK3%}NJ>A(rqbbIS1F1rXvLlu+s4xqte%gbp><%ftx=e=hKnDHxRgVt#vY7~T?1akM zμ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 V=ykc1E&vzi;C?H5?mm|*{{s$AAo2hJ diff --git a/plots/c204_21.png b/plots/c204_21.png index 9e855a703e83b0b6a9fc6b929cf718bcd9d5c9a3..1b14e4f9667ab29732b7611854213438d13f865d 100644 GIT binary patch literal 9648 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 We|e~2-z#8D*=+v<|ADP*`2PWd_U2Fk literal 11400 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 p@cDndf`TA_gT?>oxNc;IP{{UK0$>IP2 literal 17116 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 literal 16256 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⋘i{vN_egEiiMSE$Xo34v;&F)OOGX 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þ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 bZ)ArhMlm|Lq335Bw55)g0lrAnHt2r<;wO3K diff --git a/plots/r211_21.png b/plots/r211_21.png index 209d8133716b112aa0e3aa6e5503d7030e7848f7..31754d4d05039498f5ab3edfe68cc50c2237ce07 100644 GIT binary patch literal 12658 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 Cgsmz7 literal 14152 zcmb`uXH?T$)Hj%fAb}`^3k0MFY$#o%Nek_QiWE^mdhrUt-& zmmm-h6a)fOK^y>I#^YQl5QqlfIb(y%5fs|E;{&Q@4X0Il_u$Wb9q-BF^rg}V=cWyG z_`W7}FNE8ky$Hhu)fgMBFoFd*Xd3KO#qtAs3o=_dR1%Y-c6_!Bm zNB!t8H8y6<^xLTQsRcD}FUT{h%<|$OVJjn`z6xtS50`+8Up-w6J)* zC;H1}tBU)u6L6v}LmGo^;fGFdd(q>%21y*K*a=3O90KL0QjLOf^+)3&P#yU~%j8Ao z0MwX|AN+Z=&1lgBiMp|;S`JQe6T$a(Pq8H_G(YkX9FZRiSqp%pZt$MTV~-u=Lpx(A z;@*lJP?$&^HcA5q-UbRwNIbTG2rn7>XbTBPxP56`7)OAIfg@0oHCs5S&)m^aGy>HE zLl)wWfY~SvHe!@z4`CEU@_^}g2%j4GTRt)hwz$EC?a%6gDR2a)4F;&VRcn3DhJzc+ zN~pTIoH+r`VC=ZwCgz7W*M9(4iHQ8nzkjhCN=Dy;|%Z+LS;T@Vgh1>v;_wz6p^`Uu-U*jcOg`n z%^hPg4|y*xg3X--_Wfni-d~?k9tzASJS4}KhpOQKpW5z(6JC&4PeL8QA230lo4&68 zz4sV~v%!;$v0h)QAb_lfe73Iu(9=dqM?s1@pph!gK}r~30s;Go9R}9`E+$2{%nn}wr5`-?($eY z0Pe|onK^ZCS5_xe&Q@$c7YR5j2w$)q_4Dh~`TNfzR7DtR$3Yh(z#Swwr3hBa zsJQaXX)`wa@E&-p*taCD$o_@cl_I@p_Upnm-%RS)o>mxNz4?P?{UZ0NmghJ|0k9Iv z12tsE_m4P#FW%ZaKVQ2X`Ap;Mp}*v+QX?e*&$^X$jdfC=7TIljrPbEXKaSohayGRd zs2z^vX73~efy(gnUMlY%_%J+Ai~J!@!by3!Ao(d|aXui#_M__9UQygznDJudbM5|v zf0b6JMoI#*#T8BkO|18@rvByGC#V@`wxQ=AuF%1jhf~cc>oq4PO3>=L?-tp5qxxc6*svZ-B?U5xx%^ zGIm2JdQdufN$Wy)jQS!AL}Il`PkNsXVsuy9xYj8J0$9eo{|X(Xk$CfxZmhC`uKIudm7ZTLX;32 z4n!3KN^nOTIa*gR{O+!CQ?24zgRj}*Q!zFjzVTiQc^@a`?Y#c9QSNobUzoz)%0FY{ zFfKXxy$9328w1%W$==G+G~HMP4BqJS-R(zP>2Q&Y7(K}{^o<@I3X>NR7m*1eGsnH2 z)baKnSuh1Ecl#DZ8k=eQDIM^k69z3@3BOR!eU;@UKCY*6ly`ey(WNP$ztBxLfJXjT{$;t* zD|%zqYXpNp$>uLqxfSVsUkSJ+>Fg)6Pxkgnc@no@#n!lxGDfcd@QXZW90r?@_xOH$ zw?0UJ!|_JJ^%d2qlw3*7C7{XJ6#4Gx&G$GdEFKH(EKII(%uLzOAs0h}rDV=e-f}9* z^Agtvih#eA$&DiK-JLm8R3X2`D1lD@d(k0|6-AFzm%0VH1xqsF=on}4vL?35|H53V z9Ydhv*|XH zQxAiww@3C^4vrYJx_%(z54Dk1B)yRXh`-7-fVgy&(KhPUvG;?T*t>`Mm(z2!!O3`0 z808XQT-Q`1|52w$AV}RM%7Vh0sIx&gF(obva zvHk(>xMo0H7lrv=@dt_1PF%vo(k3D-T!%nf^)^E}| zFqJr=q)TZR$6zxZ0g2b0?aY_u5#E!{mQ0NSze!mp3yGS;dwbq|tYt`qKyPyh|6AnI zvO5q<#U&bUyo6NRkk%0>yLlg8`_L4YpLmN<$2yG3c9Tr|nbm<;xAlaIN~`Egu{(5C zwQ54htaGLhb$a!dYiP@M+=A}^+><%odQ|VVCUsZG&X3vP+~k9tv+26AIQdHPOx4mu zGa3g1cE)qQjKO$+4wDYtY~zAQ6Wav# zpc~HlTREgWK0wT#NuQ`K)kz`w76<+JL`w*0L%nZu4{hzWh#WVnM%sCM$5yeI^8n)% z0<{*xU)|mLnDKGfncsyaeeErIkm{IeA&0_Egoj@PCrG&fq}iC>D2M!6RCo$EUy{2` z85wJyWsPr}q@cCMhf_Wes+%jT1H+<-tS2ahN0^PnE;F`dn)I&PMQDbK&kO#p&TG!` zQKegn^-yDjoFFz`GD&>d9t-=wJi@VM#0BFB|I=7#|`F7o4_DSc} zf8PoTk{QLy?kS>RY0FCj#RIL4eT4Q3ge7>iW#&p?LMQ{8YIRVlay8ya6Uux+4TTDMeGFQcXpqgxBYO_gT`H35qAx*#kR zZGCGEW1}x1%DQZ4)>m~>t!D(`h|wUGQFnt1Z)aieW3g|PRmom=#~NsN^9Ag>{O2byh4l@asaL=Q!rTjh3EDc5?b!(CAQe!m z!rVwfo_Cqh>Cm{7u61>LcuDqg;w19p2z9*Tskmx5ExvL>)z1xgHw;>&9iUnI;C!p=W(LM;lT06 zuXvoiqO2?QcN@nEF5(0IJ190WArkC#VljN*y{sWVXQABIac|o!k^~q|8 ze-)))3x>TS#ZisfmmxnR8r7#%GL4Tbd4YK3W1Ce&{; zUXPXC4+0^R78XZ}e@ z&(SivXRRA&+9y_T(7fK5>+({?0)WRf;grLzEC*SIOn*wPNx@?=@$(ZsW)kU;;LW-? z)z^PB=Z95lHI>++>FqGPavA;--CIQXxn8^CSVgzdo~sF54W8Z06(UCkgK7Xj{3jDsBudkRgl31K|6E4jB8*u)Zla>L+$4a$!cA8D00=foigv+Q=GX|^_^ z*K8*Vi?mWo%HjE@t__|7((Jfxtujztcb+HcS}EC=6SRfGJWgUav@IKfK%pw^E3LlP zj#$vdUgcd#`=+@{-LszO4V3}jwa(2`DTKSb6pfE^9k7nG{!(ER%2I%(ZS7O@FY%f< zza@PJ45%*Wa6R{-GOd}&H8m*s6Xw`@>;S!$|JTliiAb}YJB5pHDvL?g++ahKjh1i}CdFu^rUnG}6~#WubJEjGzxDUutip;gK0Cp8&LMm#V6I< z?))4%r87Ihs_~UxrLYNC_#D~w8au>;vg@j%7F7eA>gGOd!TMCU3~+1GdGw#LdUldP zRQRtISK@-Hrv*O9|IgOdS$o%f@Miv74ejp@z?VjO!DivzY-ii^aV{$Jb@mOej(+>g zQTASXB#@FJdChPFjA<~dbG%01%WjZMF6>vSG$nl zJo)XPAKKW6&hvQ8-=yYmd${39%u?7|xE?$3#;-zUHY^f2=hNG`cR$!?YO3kX*2J%B z_XC#?VqN|HQOxWx%#SwCwx z!%ijT%Y6~zB8#vlf3qzdHzB7D3%%E@5y%09qxI(JMLws?4>8H2AS~^e77jL`{KyvM zAd5!#KT~%FQh0C$i~JI>J?Gh2=^B)|tA!>@fSE(ElFZP?1*2cUpjlRGY=S#*X3W?^ z>TFS%yQyNH021v3S}%8N1+3@QS6sv|sFhO+z+q5Fc~4EtvtaOfIB{Zz909;x@I04% zCOT$>Q^l}jIAUSr3;PyRca2up}*8`}kzaWcSPw))qkKTZRc9H+JK+o0NpC zLg$DYQ-i-HR9#8mJ|2c6zK7kyVBZYesd3(IE;MarZQ8!bMj1pBWt*H;ekqgyo04yG zE_%oQUEl;fmALEkreE&z1@aFYvtsU#tXF+cYS}rAPk!zmsh#gl<0EVdpP@2WGMCyn zV;@k9f>sIWL7Y$8%{y8~(J?BQIFy@kDDGw?X zmJkQ*{0AZT&*;=VQv5F6x3lf$oLp=Jvl`|VR~fYtsueqXWp!^iWTaosaTXX09mTZ_ zblSjLknqv#eqME7WM~3Wkv4C5IDMG_yIbzV$N z8GHSDOMs>ygk1U21(zd!Gv#o|9^1T@QW0TE3x%7(3V`3YgtH&QI=pU$hDohusek)< z#h@z*BMpQfDyXDh#7S%jEpJkDV;bIg>S=d|CUJB<&lh-9h^kN7F-RMlD(Z8ROs%8J zY4h(;m`KQ6C)O-pta(#9g=xqxyA<+Cb3?Y7aP z2r*U))(!5I;)-l|Fv3byvZ1Q84P(1q( zXG0S{hV9jxpjg?3C?=I}l$-Wa!ArCuKb9b^&t*6;lv%5tpWe&_9L6Lh?v`8r87)li zxtDA`0IA&8MyaMZT)`7}TJPP4Q$WTxv-y66aN%m)_5##{Tv;2TXcB=s+Pe)IFTh0pKECJsL-n#^>c_^l6L|y=yY62yyYU3lNk2(%Y%&}hB{Dm{i#@s zm}3pQvn#yURHE$Ooc2noiimg~S~mXtd!$;;&Y{^00uJ#0F|X(6IT9h2cKAp4PlWzPy^N`kmM^5yY1KNRL-8KtHD9|Dzqxltr*ofnmR zi+C906YgV#$G<(%RD=_aS~>21`k&!oProl`u{wQO+BC`$?9h1I>@$sZ&4T02wvP9{ z`Z0W9{|w8JM_*nc2JPllKDiBPRS|Ga=2QKzzOzTBBfuo3@?0`%ilXhQzzMy5XBj~AL5k&6h5Q$y^^}V#5+htd&7`rj zJ8y8(qm}t8iYnnwQovNafMi6O7T#PoK=R9>)yT)lY0DhIM1p`{KkI$|qcN_rZ3BDi zkCPKJXfyIAW%slreVdW0jlxunUK}@gId4>F# z95cUiqj0pzBiPyXYIdZCK&9$UwoONV{XLZ{s#YSQAlpf#EQ#30zq;Sl6~9ci6ukMARJ_|)>QPk6?e?TWJ>@tRb7v1gTilM-qI&8rP3mWE!X zH5R7)cy1Zf?3|F_yJa4k5{856U=GfwZ>+8c^fj1cyqj=zF&5wkZ*+uv+(9-{o1Ro1KZEl|J0 z;v<~y+xov4yWQxe;=GMoe1my+L3;gX>b%m())%N@1AW5~lm+rD5KW)bM!($bG(71O zycQwZ+Gdns7(QaC7A38q!*+cuudCFa!<*?UkNz_F^le^JT{EU8etPQ0$cIWxPl@52i`$<98JF1f<u z2M{O(>fs&4?e>xx*IRPzWKV*tpQBO|`f96tt%u^5C(R9ci4YJ#Rs6nPGp#C!;Jr=% zHeb1QvBTt(p`7N5W-}bo73c!MF(L8X)YSI! zqE|>W$pI>-*&#n&Mh>9;-rWf-34S{|KJ&Xz7Y}I-hI-gW)SY@=mIm_GC3!O`-<4GX z{7g1@UP$%rEc^>C~EQ%DAjky10h5%p6XylP7V!H?X$qsvnY>4|;!b$VZ(BYt?b?HW{HNi$`M@!-4T z**Gs!=y9y~1yCoMV*dW<-EUiLNeVQEZmu&Y_OdMX2bU(tZ^X>zGCulOI^c<#>b8ge zK0$+#HTC27KkQJZo&)K4&DlRZZTk!3mCRjk^7G2Otc%ME8ZXjPZEMp_ltwLh_yTL~ zB-O6N5q}dB>8Q#XS`skWPMFun-+W2Tbd>N-pVaXAug`|u^^sQDb{$eQAc12LaTv{$ z*hgh=$-pK%3PS6VTRsC8>psa3Dyp_E)U2tV1g`^dVq>`0F=JUCPvB6atV1qTRLTH_ z4mOKMpkN93trroF-q70uu#PO>7JGh-8&w8#LViWFXM+;LsDf06M8B!>{Cp2Yriv*L zgLO#dFE!v~IVW{INPKmKi&(U&`4iLvD4x{{mbeL?jlR+1!xs$U{IHIt5m0S8a}r)f z*d%^IzthM{_J?71?GW#Ce9?N*I@np|GZS@(Ov>N`7vS%0Ed^K29N2 zQ2bw5=DfE(2&`d{ab&Vu;_?3YI@Y&_N%sH{@L45j4nVpbSoCP}?33oK*6pYx;@k{1NsE zKQ%%kws|{->2-1oj&Nd8eEDpqokAfTG4gT-RP@{yVfpIVAsvpN$FYx^2k%oAGdMbU zISV0EnNa9$gSfrDu&s^D;&bxirT(SooT2v_=VLA!kp5FNuQ&O8o?7!{ePnQQoaT_G zIf4MaBdzLFQ`*L}E4B~cFCU5)f>eV7LF4wPf9-RxtduE}Cs)kEaJ<^i|G^Swz=NTA zQMk_6okwk_er-a3JfPllkX*jkd1-m?FHH8sJ@E2L?d?~Hvh{=>85!&>ybm$s`4nRP zp0GMxP4dYz$?;79Fz!Z$WH<EBp~O42~;;6%Gx<-WN5$10pREHvwQ#3o`#UyyQ}m9Ux` zyMRN6#+$SY2vlIwei5mb72Z})Q(hJ^rJvupzCA|YpD0v-Bjy1fne+00dLh!Z-B22M z!s*x2UQ(ClS+}{G&f)#9H3Z0%6F}m7(y-j5&aa%twD+2po!-lBuZE3&=%)W%vNz@W z?@J8|!@dxN7a{N-W^56>bMu1r!0r0kscFeB&3Bs1)&IPK1b^M)26_ZBG#;jlROYNv zXC(PCQ3tAoIE>RRPYPqQ1;8K|oH&LG-k`sR8T%VJT*8dRdy!ATu`P`rU+KAz?fI(Ka ze!KBvrGArK@W^ZHz^8{}{S?S0(}R&bY?pe?%F4V5Jzxzj{9)Ip#pIEz-+uz@CB2Q$ zIl?xfKYvLODX2>!V_E~Yjk}-T@ee$cZ3H}85(jmn51d4bzrHCaQIgIxyVGw?6Wfp= z3iY=e4fPL4g0T6_LH-vwcc021bAyppJ2|gz6>Uy_^v7{Ng%sqCoE;&K$g};vqsdeu z4vX^8G6li&Z9Kl{C-2E0;`9P@6=EyDFFrX3x{nr!kZCGrRHzUQb^M+nB=y zx?(6~QlB>zgYydvKe<>rIxp_Jx(cASC_Q=52yhC|*5%a&Z^=6|S+v&|21@==6r4X9x&1`9kl>K3>Wy7X@6)}YE}9AO`z^0ovIL4+_w zoWHx?#9+5)S*amVFT#(M970)kS${BjQF#)(5Iql)ByJg}WIvV1ZLZ>GV@O`zJ-WP1 z-L8{`F{}@vBL~>$@%aq-xyjVq?Hy#AN=#;Xrih-0Bg)(8tMhc%r>^xQTUWFr*yS9} zxM0aE*`#mi@S⪚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 diff --git a/plots/rc101_21.png b/plots/rc101_21.png index 578973b8edf7c0575f1dcaf223adb9509bc5ec2d..916c4df8a419433910e24a43f2873e917454b42d 100644 GIT binary patch literal 15892 zcmb8Wc|c728$aGmqo!1IPAO@b&YaRkmIf)BYSbx7S47KAdmnVOw9ul>&@v~j?zQA1 zbVK(}i>?rD)FjvKN|d67CWO|ZO^bfd$>+QMzJGoHVLHoud7kI}Jg?{VJSSz_)=kQa zT8c6JEL9=bQ%?#wC6nSUU7`Cq&Fe|YJB`}D{^jou7u(($H{2pX)2zmrEYsSAJ8 zI5;IZ=Gq1c5d9#*nxsbX-%2Oka zO1{5~u&8&$vao#tO$z`1&nG0*%z`LNh{pSP-)H<^*Np#R9n-V=&GSy(z?JcJ)sN$J zhkBQ5WXujsAqc{MaU(QhY30GxcAKFC@u#Qm-Me%%YB-L`WL}R{6*C1)-PpMUb2E43srW$?zS?d4@x-WV^3qw`;1oQ(D8^mIt-EdL~iP4 z_w1qd+B*lU4mJ#A&dU9c`wOS;c^n~M4k*AAO;SWvcD0KdCYtQ6z_c`YrN=aA;E*oyjB;1^H~1$R8q8E8|k@<$pZ-^1xx$Tfsatt`3G_ zTr6Lk)jiy_TBm4GHu_=h3EA--b4G{Z45B59R3LlK<;EzD(~mdNN*>>ji*)@Nr7@Xh zDa_9`98)aXau0$uez%{sslwsedL`X@fu^+WN+hqra(}NlK>&u7wLqnQBQJ8EfAb*$ zik3L=Vy!G3C=<@tU^&fN_B`$O4YHFHJ4lBuj$EKDZ`%rXPR6k)t^7SvmQUB`#LPSR_OaS!j7oifS1!Er^Mw#<4jmTvY<7F&*!1Pmkg6aai0TcinwwB< za80hZ<*v?P(RtDPO??@POg+|gkziu~mrBsH%bTfeN+*K6jrwDG2baoky6$PZ`^~}d zuxf9#n~_vi?J@9KEyl;K)jU-mWKAfma?z8~Q_q}&@0y!OrPC@l@k`@X3Pxv4Bvm;@ zKT!Hupsc%>dgemkd!^S`Wur0!)_7RSznJ>=DbD9f53K9*7Jjp4R+#_0sE^#(`OF9v zGW(0!+(EOIt?x8G88+3{cSdY8AjsE6D|2LHVvpV`E6<gS($9yx?)i$8R26OcC) zANZqSstktlW`48Q#5~60s)X3Sg!i=}b8bk}Y&ew}q%>wR*z>7s&L!c=aU!Hal~qW` zP1rCZCbNM^O!#A|?qHoPtXSg&xu}{wlp@riq46^}&E`gVq%%=yZM+(b7T50JgOmm0 zD@Pv|*MF04GslV~oHT}ra(PR1CiWFu^Bb~OQR~5Qg3)9X|4(s@hQgeWJFRcce`j zLYQbCbxeJyP2Xqg*#y7-+;{ucX$5$)ck0bb4CZ{gLhH%O%7d@J^eHku9@=c?d)C!< zjvs5G-1Bp>P5%w==&3HB)@6(pvbhn7yg0o%$NhYs`1$ucclHoW>c#%?W7JVG_To<$ zTYWV@d-hrT+1O6iTe8*!lYAw8x*?ASlNqoF>6b^wuKL_@<+~^OwnTwK1TxpsNL@E9 zNO{(k!yvZu#q6jR)Y6tOL@xPNy&WrLyD7VJve&btf3VTqD=50RT>WZC)o9P3e=lR)d{_%+mjp@@EY7ndhU%Z=Sb;TYAgrfb6L~T& zgJFDPXB5w*MTrR}A*Qs6ayJvDu{y3C`;=p6C|AN@kfdGaRTutSLf}Gh0Gom1kp--_`D>W&Ah}pq<}Vk{I+Xe zq6-;WZ=)%{f{MK0gVBpaF%L=K0m0n5+RX4EHDoA57=V>#0KlNeKYRiVqEwM|m4;{_ zbFU=ex$9`sCs7l2)&n7CssH1-4D!YJSv6UeYu@G%9bmcM3(ys^xGC=b1KXqjufyta zh`=%u@S0_eLVB)-6qYYY(a+rt>O@#noRQHc#Qh?qKppw-@_cK^z>Nec)SBCYVG5Ze zLP|Db*b7DS#drA{hZ3dRRFPY_q@HEvFn6X1u`0?9QkSSH?_KyJWo~tX&HA~$%LvB> zr;4mS>k`Q9RbWhX@Ngu#QiM1`WO`5GDOk_n`u?|N|D2Z?MWL$sw#0l3SB^g*jl6oB z#-2GX2%d8?zv8*|w05|~5O+usIq}P*;GXVdcYKLb74o|ABa3VgUURy-dhUC3abr>(emR;r`YJ-d*4laWZ; zPySY@pZhSpHmYpW^r5)+r{!(o;63>fJSKTpKlik;Qqk>%>w+>+!bv@rs~(t)qb}N@ zj#k35?+8U?)CqYM)nj#6kDi#@AhsU+_U+QQmqRNX7NPpLQ?Coiba`5+E~{kLbq%{_ z7CejKt)EmA!Toc{z)HoFf2~$$J6=>}(eagtN3kW(b{?#ZE&s6C7)umiK)I{28q!|* z_#Jt|f#)6vMr?dhWRIXjDddHOzD8G$bA<`+FYzS+f5C?9z_+5-{`-kik0g6#vhmHO zLwi1%$`e7nW+qu~KWBu0%8R}1Z@3Y859g3W_h=CLU5l5a`JT3kS(<8Sp-O&+0y!-B z*g@J`3`d_0flbT_`wWXL&+^?*?3~%SFtL`a@+3K4goH~t74gO3FJR@r$%|bGW=a*> z)b}>bNv}nV?YIJa+;b&@pEs=y_kRhv{PwoBzb8?udMHPpCO!YU{a+&!c1?S8n$UFM z-r-joVJ-}@IJk(`Y)Wd2>n&`8hYF66`mlRZ&| zwNU=@06V_)5R;$`JPu+h{WPp3J(bSke=UETwJN zXS;gDJxWQsK!PRQ*vMz5+(u8{R6KcTrJM*kx`_6><&mW(Y&$vPpbjrCU6dsYPxS{(ajXT1dzCUNA`#UA{oIk|Pj9(#y5PXO={e{J z5X;330fm{EWI-0T3eWjgxt3kS&o5Cd@!(g4j$UzDPQ@GnnXd&BJV-Q?!fgAt8Q)LO zT7)lquDC5{Qg;P`8mf7=;+NH}X>c1rEJOviGu@vs=5< zy(36zgcJ_nyIEJW!)$JC0z_4fAT(Kj?9G3oK>j8|%*7B&d3Cj+6Dq~#TmHMm>~#i& zSkDfgE2K-7s&?rhw_NoG<%W&1JvvX5!29qC$3-@daMU?K7RRa zUg*+ty-~BDJ^%fVQfOQ+dy=$LNjo<%%Ui{EkA)=INbzi`?`IHb4wuh@Ww!x1-2(k`2a-1}l&q?xgbZbCQi_b9X;y z&aU8uhIia=5Ftw&f4&-(f6K@@h?l0q>;|WgApfvBgB4P)Cu9^_m(C}f%JF{@ma8sh z%<8BcM|vhIdodm{pVau_=k~>Hu%w1k+P4pP6pGgFudi5XUSyXS-|^9lU)pxN2;3a& zS`msYZtOE#a9wQkr2h7kioRzr_XW1VUW~tn#6}zegQ{9tP7s54RWrJu2DEFHD4zUM z;hg*CVBnMWL$zt;E4?a{YvA!4fxHotJSX4(tabNj3;9f~u(Qr);DBg+8PWg6p>y8S zb&ApTHmQQBXk`i*_;w2d7okS-M1S93{zUVJEr%Kpi2g&Jsg4=-nNP)^)=RV0kP^E> z%fw=i4E?yRtmISiM{}Pbm1?6xyXF(ZleExVid-AMIC0~CeqQqZXL))fs0s9wC$=i{U;k%=zw;$_nEvY zivR0YTkE^)KJn}^Ej{6_E@{Z_!vuK$%x7QJn?2(mr3)J)nqo$;yx+$J5tsPgJ^aYa z0vq%`>$LWQS9H}ww{kKqO~^(=Z%c0Y^WPR(zCF0G@;jRG)=>@4B!`c9*4a1`&C%(X zRCekvVpZx(24^qMBl`F4SUsKL5tlt6vxjnHQK3Y$nVj$0*7r-XFn#=;iO0L&SMRbV zGF`|sG_-s&>)uq;{`MpR*dH^cRbIR)U9coB-EFd91}0ZmH)0Tl-($+Pbn=vD+X~EI z!dZ-kdH1L{jNqf zu6&2-Cb8H4x+)d!oh=IFIqTfrM4AfuO2jJ3xPwxUoJk=?52ES?W~t<<)}=pG`Qwrx z_nZ|5L3YIBW#UJIz+00@*ZbwT-Q3f^$ah?n&2>F|mErzm-}-&_r6ZGp-R92X`swdT!+o`ugOzjX*tUwAO=(37 z)J(R1Hr-`kN|HBcy%%E<-$T0lTDF~?*r9i+JICOOYq&!M>-NWHMi31P zkl^p&)SbtUmM0tmGmUXh1{}6OWk(ctW;@8@yGQ-%=X7{n8PA=PTZLUqN%@Ua5=N9V z$rmD)=wa;>TKC?Ck=3ai`8R&#r%f(4V!uyG@%-I7*8mSHus+2QcYdnfCGoo>7(SHN z{JI*$+%54vQ&;;I7H4S!3X{cuV7h+IesPD9XqBAxCB}__C&q*?)2d_7=^MH#q;!x` z9!dG&zeKF^`r7mKnnsiAL$(Y^H`RVkA_hpanCTUC$x5XNz^BIqV2R8(^SInY_HPN| z?}Lu(!GTN;Ka85FO1{(`{Z25P5_SrR3V^P~B>AfMWcGU80l0zD?sVym_AFgBJI@G^7Fp7!M0kRim_97PEgb z4b_$}hqJ{mSb)}J_R35S2WL=gMHg|KJ{P=LO2zung$P%~3QiMx(Iv|mIXU$ZY*Hd^ z89)ozFL+3Zvhq>%maL=@_zvIbQ9-`R#C(c?D$XRc?`xu}CE#(e99O-FbYV~5p^5?> zWJPiu2Z)+CEgV!$i_6_^Zs369u{`iBe@)&9<9W`r3F22Q)^WDBb_8>8+kiXUNLCsK zB@>&8w{k?(UByHft7|6fv}#%duL?d`G|WwBoonsbteBEb2C5@|OA0hucOQgI&To8d zv)vKArEhzdeR{?V&Pg@2gmMKp9C&$*+Q)l;%~_$72uZvkJf7(?u_a_b5<6tH*CltY zK@N`ZQO6ep;o$coDr%Q;V*7N1O|+=g|%cQ?hLsTdyh);#avFrd7lHVmoH?MnXmba z1VFTHtz*lEG803rXwYc43;u(FzkaSAKaCXLk+01^5PvO;86a7T5g>CxuHUEP*)K=e z{QE{jTkQ`HK*>lE!liMSu^F|azLopxTv>*e@A^Yg^ELHy=v`?g=md|l-Dm3YnO z46^erK`|XoA<-#|^1@{C(JFt}1rAO-7pkd7qG;FPGR#9<`{}{c4NCswvqyLFE>y}( zAjRj0nd!JGI3)k^8;TZrzGc*LmpNGD>u_gRJrB9s{J`wRioYCvS+y3+0Kmr-I!#>d#02?Nw4XKSc{% znN}iA|92EY?iW}wRA*K1Ij=217IIN(jFEbET(z_0ii20OJ9!v?}v%6Q-EG-Vg-msKN3Bz?b@LWhiPVcHzaYdL3nE_rQyw1^`p}leWE& zI4H^8x{Wta9K6+4H)D2%Qk#jvej$}^%1Vw-_Z{tZ{`gEEBWS@VsG}fDF$~EwC84Mq z>$PCw+XCn{m{sHAb=bG*+3DKXUW0V}JzXSB%!d05>}(wP z+rDYJDF|OrE}Wey<~^+CYQ}lySFX^f8ST=Zh&9F7BwwX@T*)6qx$!1ZXm4!p^_Ai!L&HP$@dI4)MH`Mk&bIVzk znQR(2{Ymi{q#LkUZ_DoC%IWmkiAblOH|E)`WQysgWc9n1Hr9>+l$(Zs33RtW93A^Ym7r*{`1SOq zqa%I03C99tk)&u zt7tK5R!fIdj%ONUma`gw$*AERxJ}DeGZM29RY+-G(xz5w*(E3N^Lr<_g?ptTx2BvO zArTRC4I;>t=0~K(7W1KW(2gJwkq?Of@GXK_nJNtCUnsPkmR3)G$s@`2Ws^${{4>wB z`ITuP;>0;+?AT-jpqu$c`I?&)Xo^dHZ;R}BNra}FTjRdE(<#}4kSB^9jsc`*u1y&7 zLdPLfI;>0%zxmj#PPUN~Q70f9i;DIZ4Za^)fNYKb!H}}a530(q9~hfzi62zaLOWKx z4;t<6S&3QZ&?T+KClNT`=%pw$OsM}ch$bLCVO zB~PBpdZbKts@%5&IRRO%qrA5pLnzwOv(Qp3C*GuqbkCNjlGn4$04;oZHSqib!LWwY zzrfbU!IMlFH&YqAN|Tl!n?+Q(v?d(S;GF!DVryQPNMa_2u&XTz8!<#pc`4+bto1bX zVT~z3Ey+rQoSfF@_e*UY-P5rj48#yZ2w0rTowH7iQ-f89)sgO*OG(y_#0BLMZtdqC ze6h+FqxwXWZx4A6_8%|1OPt@n=5mw-r<}YM-SnL4xKff6|E(R-5JXH=6j|O{8sYB2 zH6$2UximUm+ok5E$Ck$a$-8lXztKB1)wGw~^KS@K_Km~fCmX=s1=eCh+;TrQ;v4x( zmOJ&DHJwIFNr-LOco=gSwSQbz^etaGIsZ)LL8TUH}0`x?Ih>BCwwl!>zd4#4yH*i-Vj6NruFI-LM zh<%UYy>tb1;{Y7^V**l1@XOTWxPsh`Yk}|knWrNw>3{RFT)=cPT&Rth=t~Y_ELq9H z-MM2o*6wm7nI2%9P}DgQHK8)PbZ_yByTz_|dcC?s7VC^`k#P2mUNOLG_-6CTS14*{ z-LP`&3c0K2{H+Ph070_2*}YWh%cCPF(yo@vt_iVE2kts_A<7jYEA;WP=f8O*iI;Qz zOpc%7hL#UdWg`~tK{V}TOy{$ypwO7EMJg5)+maXx$H7hZs~6u>x$&<2u3{ZE{J1wq zngaDCCKE90FMVga+)~@W^zXHW0W~XsY+gXc%{dI$ zZd?1jWa*0EyR}tr^>8}6sQ3n?_%b30bleWOMoZPLeTyiwY`;1Pr3(ccrg5;_8N#8f zyVs7S343NnR7hjR99K>d<)6ajK7x~Y0T8iwmsZ+KFHCFY*`{ zm7f36{tub8gfi0~D{mj6LiV*(@=iS^r37q`daf%G+f}b6Lf5zdlb`h{9hi%dK`jbS z(xkJ+=d~Dgi5IvOc2G{wl}-HETb@aQ035rXpAwL|*P?~QR>uwt9|{n|A`nzJcwwiv#fDIu4-7(4C`nh9&ch6N;@;ZWx1xvuvsOr?! zgqHlQvecCA_3|7Hh=VwOb6KUJZAUt(xrlXBj?kp90IDJRb%$($m0s`U$j?8TixUNx zBlsi}mTV0pr%1Z@w{Lh@yNSw>Ne>YS9-aDm^ZHBA7UZ}eY|XMiqRiP{E;nZZIL>8z z{Bd{ksXkVN*ej48=*chJi|eB%{?tWpCK3(%CXVDR);pUD!0dqrkTfPh1!u2dKe;XH zNVvXf9^?h&%TdGLyhD3wCHw5f;##S5^q_JVir{qGgLSl%pQ#thv@`aSLWp>Klt^QG z2CZoW9zS%~PNgbRdSjn`9z*;SK`K1C?MKNF7;wWBp-1_u(+sdeApi0J$}q%Al_fN) zPLU+`ioHrCW<$XQCOAx!ey!8cM)tN2EJ7|#Whyf-9sbLX0Qfl+u@f@)z5GNPP=aWD zx_KlTV%nc9?^Lgas#eOEULX-Q9KuJ>y1c|pjkLQ59d!Vr57a-_fn4|OHz+aOI?%6* zfT)Y;_+mUva%Gb}uTK03VxU)DA|;Kr(vWrg>%H%|@Jpi#(lW^|z2{mt51eB2EO4ww z28cgY8$H`-rFn3RY;HKwpsLDkbaI6K9TWJz5}2v9m$d4tBkMCymjIE|e|vkt=#{lt z7%n-e?tg}yNG4J_G1oy;2jNHn`5+RMDJHtoq@9^wd{PM6yV>;7p;acBKRCMV84R(yT+bKfP*?O^*V1?_JJ44F`lEKKYtQ zw|Scw2%f9x|dgkKq}FU#pX>IkImdaz~pt?6WW zAoF$ab`h(RF4_Mf7zkz!1*g!afWQN~D3{BfIuurvVnK(oA2Gwv@vfOkTHDu%qwGl`tZZoPTz*2~7{TOrjoi00{WOYG)O zZna(Q%iT$V?klh&+}b`ZRd{{;>R8NzoFma^LGs^>v1mJD@bW7o+*Ktdr_t82K^KLr zxc1%d_mE^@gL1Bu4{eHJcMU#3%bJTbnbg5wk8WRmdEL}c<=gE?V%h%I;8@*_u^imr z&@J=8i~1PT`>Iyt^&GDahFU?F^du{>pbP5dF8qeS6hp_ti=mk9`)XwGprE_l#c?Da z-Us=*uf_J<@!)qT-)c@3o;scUTQ5|0go|YHSY-#f8B~PaN`BS_M6P|>2`1PSf;j51 zoa9~RlebZh!RMYU{~6D7MWx9Crf{aGCr}-|DncS_!RyAb^+^TlaO9E*(qnVHy3O^H z!Bn>Bkl5{ndI>qcKSmbWYe;;UK5@{o6}mh40jhr0`04>j#J zsPKNZu8H`%@GSe%wlVDazr^v%!7Ge9W=r)Li|_vjQ|QjX zs!Gpo?Ar$M!A;Lqfn9A6;s5s?Fa|N$|v)otUV&dC6Dnys~+V@M$0~_I`^)}&sooB&^MaX2t zRhiIp_YgX=-AnNk@n07+h`#QymMnJ@c=6R3@-kHQ3uV~xXu!Goy zmk|`!0EAs2nX=sZAvU)gX9`TRU?!Q4=J>6#e zkLJ|(hF|>nm8aZlp^}rKOb)#pjOI4pbmgBau#Sb=GYJ*+jAMLZIX(6~ZRZ|(3?*WXe#uCYkd;i+OzcA0aP(Z#s{l{T* z_ucBbF54O=;udHl<~p7iC)vF@kp{hESI5=8GtlkkX;4$Op%a9eS^zM;SHHkuIqxF>oGkyo+u>QLmT@GY|bb(ean z{wZ~m%*tNMu96))Q5_OpY}t<>PT)IkCwSqw$r5L|(VW`oPd(B9Y)Tybiw@)$lbQWZ zIQ-{$%WB*s5sI_`8q#Wdqjkz6708!R+r@p~d7T6DWw-s%k%P(C>qi&y8`;kXd$pJ7 ze%Pc-%!G$8PXvDTMjcCXdPo#*55#@QQ}6XyhHRv$^Q=EzvK$X-1&VBonQ-)MHZeR{*&L7Yk^wwfhM zpH5lyt0OJK3|k7#it1z3QN8t?8&goUX{-@d`;KR%WDgCsk?t!Don2L(8|OQJ{`<$p zIbHL+fq+SUP;!{e0nYhhtqZ>(+VueKqy|!=*QfP3oiv4LtyH8RAld9dG-#;(A%DL& z=X9)Gd7%;qyZp}^_kGzpi{XTgdq{6Y^L2!SXUbQlE0WuxMFAAefJ#Jjcf^@&v+~C5 zETA2$7Hd%8c9}}X(%C8p`_eRkX092+awnfZ*GG*5pvQsQAEYu4?BIm1u3F@{DQ?(R zdXLI(j4{kLlf1dna@_Z$m#k#)NDI5_kgWf;&g>epEV`o*S}KTdAv$WRh5q&&{8pNIg-*+b0{X#E#5@f0na3pFY`B5~ZQW0;2483cKqow)o#UG1(9lvn|0C~4~)qWiay%u6;O1$t@#sKw8xj*N8#*@?G8 zk|FLqWPef{=J-=@!7YCY)P|m%AUj%;yqr8+*I*TU=ybD1>|Mh8e(AQ=Z>x8Yb_X$6 zEfPnnP{DR7ih4T{4Qgta0?o?BP~=mrP19qQhe6O3ygoIMQR>gXkmU67h<;b;lyfSn zsf&o_vXqvd%Lljd9*UvrjE0Vn2K8qcETQ!CC+oRIuvPn)e*yh)Vy!Tp9G1E00_PR znHH*tMm8?q1o`{GV*`9r-qry~qt&q^6J|eg(b-90H26C;ryG;Mh-4XiGlT#?(}gc( z@!mHdv;C&cvSufw(*fgZ^y+V;;XQ(EAl$`f+oi*^viQ4a0qz=V5I<-wJ>3(d{w(Bz z1$z+3m5$vRsye(b>u7FwzpDApbSBv(LMRgv`=ci{kvTl!`%yqz@Iy{VMJc9)+>ie< zxTSjBW(;T7kdIz^2gwtfXdy(oGR1jli8p^b`r>iH2la))DvC4;uO3z*bI>M9S(>PY z)Q#}j@O&9o1Xh|^xV;noo*-xlgLWe z6jU$8oblXd7s9j9nk1#@_K{m!wzgY6D;66P@CvO7YWJJ?Ba=jan^VxF(m!kDh<_n{ z?dd-Bcc-@pL(IkfRVEHXCm$~PC=;xq_{;TQTb0SwU$V`nEccIAp8FD>nOXg=Q3)!_ zdT<;|Yu4|j zCcW}-e9y;LukO7(sG~ve07yZlQqmohk|KN={~%3?Y#Uf?$o5}#HS-p%_5`zhb)}I( z(5B7lTQ)=QYw(%)PM7$H`k3!IL6nwaWQtHkGdINcOw4<}n(ZI|_qF}2fSl=HEt&ZQ zsMwKkPinKmJz~ZC{=NJND(XZ3J}WM-X^fidX4`Aym)YAiwY<&E=C+_Leue*?v(3N$ zJ+J`j{vxs^Nb*c?%98Zd4$$foJp9)0BV%BVtoa@F4pQUEvs)VWxf)^4ajX+tmB}9# z!zFj}Hx#-~x&u^EJhwIAak}sVz~*~K(`lO^SS`^!zKH>dtzr6G<_W+Qp;PnyY6Q$n z0S6*c>5(eje>N-opz%%mF@eBD;@2X8%=;>h8^c#5YYbvzvR(O1HxJ= zJC;_xbB}i%WDu`t+|M&wAf^7Nh8V=a`tY&1wofrPNBR_@3SOKlJRRYqb>zlKr@$iY z(ZSJg)$1|4MU{{YLi8m@VtK%T*3iOEPn}!J-m~b&uBufcIq`WsHG@=zbF20gL zKB}~W$oN!dy$!!Xi`6Zt4B$N(n`RKRF64#!C3u&bs>Pr@=zbkPqoR0GW+dd^{x8VD znUW&>xQGnozMT(_l}WyxQe_>gOAusRVKu#0wLJ?^+#0sa=N-OVtuE<4K;!;ePPPH; z!Tpn6pi>d2oE(E_o!Z4^NARLN_yvoQ?tRAp1gN7!iYY%;X)u{l8~Fu@%1u`5t*+E_ zvLF8ia=;yuap6%ZQ20_L?vhbmZ9Cn69UWhR?KwfUWm8=v`C*rg>T4gDuPW5C4`u;w z&3;#Mf@+Nf{vA6pbm&%8c3fGJw5u4gr`*63pZVwmMfd@?*$0OXoldwKsYtf4a-gy6 zCfU{kt+yAZHvzApfp6>B`Czz1-)UPXF zr9e)T*#q2ECMuozYA`o;uB?AclsgBY>#4?-=8RtiB#VhYNCU>@3sBw_Jj}J!hR2)v z#rxy>hiv=Ga`njF4k>zL^HVx5If34Np=*V7frGZUI?rvFABy~(Q0BsP{q_~aC?}s7 z)n?8PVu%$Pga^@3r3m8vE{(Q5tyf)=`-b-EeEH`me#I)RrghK5`jz-xb@o1hmH?79 zG&H5aH=3HeO^Z{49ynW%&YQfVsHe3nc*2{2rq z_@~}#i!tyhXw_B`Auc+4^&4d0Gk?Jfztq{J9vk`8x9eE8xn_hi8N8&z0!V)U7ri(7 z;y+VS8FHL!3S>^`=NAe2ZVJg>lJ4cuk{{1+&_v7QJsMr04GvDiZeaOqM6=H_k4ziM zB><&oWGgP}ogbyhqzF*&g)Fyvo8{xOoTzDYMuH$}FTdcP(XRriR4vy*mdbt(vKeXd ztqd^k*ML?9s+BDh7Amjeko1c51!d3ygrJs|&O_K)Znpv|ApF(hPfZXdqyv-6bLP z;*qAdVl@#0Ek0)%L}t`P>?!|`aZjQow;R8xB7O1@R2kyY`!jcW7oGg-KzKSR2OzWXd!>SJIvw&tD2xm;Cs;9@hAY;)$K( zHxF&tra<1(x8^s)u=B~DA2ut14MSz6x$9zJtIrdv9c1IjbyhaSg}4QURL{8q)J9c3 zbW?BitqK&e=+?wEP&;dO3cC?)6wG$3^%dx7Qf8N{>@-(yrvt;i|4TQYt&RR(+pTPM z32fj;y8r3SA5HdW##qvipW;}o!#wPW{Ztnm`O;nQNjWrYOTWGa7a6FB*1-2j#-VHb z!@kXvz0<3Yy~rRnL)>y1Q%NnffWSd=e-lcLw}bjxi_eehpu`!CQr{}Tt`LOS zBo@L?C_rmyiwCLK8reAeS_#fKH zYk?YAF)X0|l?9ej8!zf}w!$-L=r{BKUWIOgdTw*Y$~auo{~A_zBb?vB%Z8)=dP!iY z*CYXLp>SM%r^sQ=dRs`K!}TR);O0T~OlD@u5x!1i_Z#ffC>w-)AG!eJ*A1;0!+@P;RP@+`&pe-l92-q}qdP!4T`?ki; z^;rL7sFpzM{TO@>SdFtCaQMz+A-d_im_H!Od(WC~R#2dEeSpNIq~{;PBgsDS6Neay zUgxN_kqQter~T}i4oxzU03ScpDiCNhsvsH>p<5>Tl zd1qDT%~XLsOW}u;=ie;stBdhfpHp$Z4zvJ27}_N6{3+j7+!h7h%_8bE35AjHmXc>%*_dH1&9sH;WLtlbPfXjBvU{zb@&gk$Rf7k~lv=iYo8HhXpERV?;A_LjeJebv-Jgnil@b<%Cy{z9`3Erhn9J-`+^_ zsYL~vjU?5+CIc2S#PRJ-`GU*KDIp^k6LBklSBcO-Vh(It5f@>lI_I?Si)l>|(x5{+ zR0D0EKPMb4=);tpm`V399>@>pRg7XEawyw=Q&*Mn^+UDJd~%#1`hYy4oIbzj3J(&8 z=!ybuL;^B_L(S9i2S>L}A3h#n#Oc$iT6=Bx!HM%y=}m8<)PK}(|D0{yPp$R4+WTNS zS_!_FoU>0Ax;5W1Tu*FE8DBSsWk;9YnZ)J{TAo4^2~_JgrUOUTy?38JoF`fhG{0@< zyT>7vON5p@S}sOskog^nG|fY!Ok3^RXIZF$s9*{+oTwqA`!ZeE!EW zYKs7A)2#tItYU)DT9qg2S+JRFohrLJEXVF$l9}){HDpxz?BG@C;|_P+WUOr6!`#iw zVI|yuBHX9ORusjAThL1O@=qNshKM+je0#{&j7cuKUHjXe;Ly%z^^N7X-Jq`XqMDhB z9wRmH>b|%am)dv6GQR6pC47tsFgmw3LX({pTLG+LUAXe8+ipXq7&g3R*w56k-j+#r zwfk-6g@ZudzJ9uzEGe5qq+&po-#?M4s)9wdyM z$8GpBpj5){^P$gY9YLP=AD-O)PF@)I2Q)X!;QEHEV{_+IzV$zZPwX}7Qz5=De3k+C z!u9nH;p-N-BB-9e!ojF(&5Cokh)oO`aI0*6It(Zy112Cd_X8%90oBS-H)TNo|Jx53 vlN#Xv_bm1H|NRp6?A+V`*Gu=`f73g_$;(~8%V;G$BeU6dD^XkG+LkQOxfDx$`&Uslw!0cPF!DIoN4KVeQFGW1B4#N z#Pwa66sOsRDn0~Q)j#O(za7d*K%;?(X^rr~>!d(z`qOR=829CO+Op_&qc?b;Xax85 zabEm~3}XV9AQfRK<@TfQIvkEmtAghjLN%Z3J043M=jKv12pl;N2C9bXG8*bsf`vE!NvFAy8oX2-R>)ODR7gtIXccVFlg?n3Ro>~ za70mB=E*U!t7|`7L^fWMLu6JqdM?9xc}uV07L`$FLe?}(W7d1fEsIP3$bAbh*k|YG z=qn$*E6FRD(u^O?@2$S52~hzdq3uJCcpV<||g1Xc*z%EcISm?^eos{+h_NBc5 zF6?|ftR8~xC`w>eHC>La^YabJ`97js5P8wUn8<@X_amfdf+d-t=9L^mt;ncym@9Kl zmF4U|0VJa$ed?J&g)6^%-eF>-ZHcK-{0JJ2Cemx_TejjP{NG~B7g%)-(4mbo^PB=8 zFxqhSkXPQ^5+}31DZns6iZVPlo_PB=+g^eJIFqrm=tzg<{qGUmGX3-UYa7GeLWiP>iZy~JVhTUsdK6+fVaUTwMNCgiGf0dh?_12kq zTrYzDjTUC{>g%=8w~t|G6jm#_Mpe2W-ykV4j38-Z4Q1%w{Z5Z+zrgalogQby2dDBB z0KO$z<3Nu&O|%OJ-Fi94?GC@GP{~=?>GIwp`x3L?p?Sdlc~!&@qy;8+`MZ=!}kWbrfqZS-rbL&FdVC}NoQ&vjxk1#LVDbtz+gLx9G3_6glP zQC3z|_A- z%lzV-S9<7MYnEIuP6?ZBNRAeduM2z_(E@oqZ`6;=ZCD7txX&{_JJG)(>fTrZKMvPF z>#xWtyExr<_h{mJB$P;N5WJflmQ44DD)^}~%@->@N9#{FI!y;%;HsmV=ejNNt^46c zuU8w&xL~2`dq3~^Jv?HyML>yixHScYZ_U4GK zlLiNRCR@ZE3s?ouUR1`Kq)?PEX3pa(8A9_405(n`;H%e+0h%S`K^MACqV2{#Eva2gST3){RWp+ z5!Y7&S{`RGTy^Jdhk1EgM~-vjn~TIwxy_&{uMztXK55Xhz_#Ydh!&ISK6jd!_-`|I zr%MCfMhGY{&u9JjZgj5*S}y0qdC!B|068KYa(n~%OD?mM;j&l|ZStxeS+s|Ju(_5% zE3fl!#zr1AwSaYK%uNZ5!RYC~ffiOXI-lwfEVBA4#%cEBm-&+uXoY!I5McKS^a|8= zsbjyEgFOkFG!4|{;*}#@8i^#w%0`M%gbED&COTH`;^s{>CV|y$SP95L&}h}`qQN)) zULrxhM26{wX)xgchQKrl(b&9)h1FTr=jKRUDdE3RPC|?-&wU5JES@Grz+f=VhY>08Xu7e# zCO#QEG{wo5DO`N@x&NU!$kjJZ*_@17z#y^EAL^Sc8CCB?9?{A@PRu(?9R6g|{AXop z>*Ze{!cc<~rWn?`sxAj7U~0b1ecRtG7t0H%P=V!7ST^zGe(e9ubAq@)iL?(TZ}K4! zYVx>BK|R+c(YMi!IRSR9p|K3~dkNvzs{^-wJ|Lt7>;YKgY0K+9k3+T=#8|-&YYDp4 z{lD)ze$tFVx=$7C#Q`}8#6W#p!3#r65=3$6eniJsDO)KQ^nG)9S! zH1TrwC9+)3eRv-hIL>b2i8G-C{~o%`zgj+Xw_#hv(G$I*)f$6;w>68?Ph1{9*z{`?E64H9GPN!FGt%UHgmthPB zS3ocLnLYi}qgjJrr50iiMgF7b5V%%lIZqs(u-6fGtJ9plAv*EKvrtmt9ZFtzBGTxeKGXKHuLEn*GQ|6>qf-qOX_`+g1Iz zF{QdA%r-2tQ0_4CZ_%wO19eO0TadmAK;)1VO3>bz+ml5y0gEb)8UU|s*y*rneYwH$ zGB^8(x;U;;(T*bvmrTFLK(Xw@Z+{*kbg z%{xjPHjLecp)u()vmj9oi*_W4Y&_H{$5W)I2K7;xSg*8y>AM=uu%f=-JtcI9r*BA* zRCfXfUd_{LP5sur$2J1YGRAL#{B8S#&UvAi`}UgdhrKw@9I-gcR#}KJVAZcmX*JJ} zG;Dt>b0K`a4Vjx+SBY21pV%R^FcsCMig(Q^@m{ruzL)!kP^-uxovVo7Jxhk|$Z#J~9L_Pc=C0r$ zEavoD)NjEE^=Cr%DYdWth>3Z>kGzr96J&x)+wn;agMkUkXoS|x7sTEtOQ2Ii))vIH zQa)!=WzMkg7rj8n-q^tf1-lpZhk{XGlbm^icW$Q|?8SN-)}>HIUDIJe)FwnJl1 z+kT%t!|GsOZg*%h4wjX;f#B@myL@sSH#pqs} znS*K#287`vhEg$nps$TGY7dFdWL;^geTPmE39SVgCGRtj?qU?1o>f|5H?UU=)iscQ zZg0x*&oh>wK4)A1xp0JCej29k_FP<=moeH#Z&Cg8{B3#_4+s_~#N>@`eM>=#kn1aF ze9!*d-7?bSq^O(HxNYv6bKjTeWY^JrQO;?9ibJbOtfxx;hqbdhgw}dSzNJ~E3>Kxdn>bs4CEF5`RFZ@x^AH(5&Dc4)bNC;HvKa z^hG!EIq}i2ZrK(mRHWBCDpg^zRp96hv?EGpEBTzIZf3{w{IJG>)&vp1elA|Zb>?V8CkRS zuI2Kx76$uAFh!{SB4ZN-mBi9@I0_|J(Q0&qXKsq|w~eqOE-h~)GjlJ?C@LE2gSBd4 zt(KP@xVcT`A+)O*Hnvh0_^IwJsXbBgg$D7aPl0EQG5cNJkcA**<=5t}MP8!(0*i&j z)pkHzi@SwWFtH!bT^)A}8XYn9i-uxh`L=|&)Ay3R{;`7r7I2z43_9O`SsYX5S2!(= zE)J{zfd?t3Wl!4tr=CDOH@a&|3-n5%Q^G#ui>k9JT5iOc2?Hr3BGSmIoq z{WJEwt7@9x$0#8IOUZYn%c(zT)U=k|BVtVw?eT`4_vij5Psw_d;pPUTt&5GJl!fr- z-kRQVSnRG{*}RTvk6gqtZ+H(AK<5Tl6AO-Jk4nVe(LX3nw}lu@q0_q)4k^pq_VImK zutzE(nHKZq;1q(E0`>^cIu9RAH$a!cH{z@{Uu=|>Jb*A^YmaZ-UbXwh2Tl_GG@4jI zj`73tK~w%~I>P7rEYyr-{$wYT&0YJ)trXSY4)EGtT5M3 zDAOsSlw)kY`vW)3_j$E6tAwSCV$}6;wjb-7=a)b4(Sf8a#2BfGs)w6@AGr9cBxg|kkQ&Nlhlu&Kb28)uJJRc8yk)_`j7L;rq18<87%8z(N}8wyb*}wxlF5H zvj=@8-ft!_0}>B=pTV}g zd#C0VV;lbz6Z`@)&OFyI7Ox?dg-XFfZO z*TDl>-G?_?DM+%DFRJZ-YfU`?P7()n#Uh2oti|7gUfB(vqGdY$qse%g&>LQ7W`O=- zUQnpd$+mhqwhvYA-itZybtJ7L{@yQjG0($zkynMy$_YNs+{0~jB-HyzvFxjBBlS$= zb_hgtN^i*Yv-`d~Mt(N#0rpuUNs1HFGZRktluPv^{?;bG7hibDFAG|3v#1z5r_vk`Td)>9yZpS%BAkZ`9?;(Bs&(@t@$K_vYmDn z`JjwLZCDBV?DZqj=Awqp`)!c)&_KE)bP48K{G{~aJ@17|L?%bx9xysM;M1Qhp;#sO z$}6{WSJeFswyQX%m9bqngkD=c*aQR#F|eeCb6 zF1tW}TN8p$Rymw?Cz9g0$35h^^I;p^H2I-ii(gE zFAVm`z~hVfsVE7QDQA1p2e_xE{2AEdO320fx@|SzG*Fa^!{eV^)@`g8Ht;Z>~!(XFiWk zr6tA50`L!ou7wB@&azUg41^ck-tNO8V1I4-5pA}x*-{f{m@##>K|D}a9^mrc0gVlf ztS48Dw>k@lkJkz|g3|!)SS$L2#Hmj+K}*kKBqfhJ!4dogubnmxuDs1R25fTXB8Gn1q;AE2qY8$8o_O{NJV6@PQGN{&? zmRx3XM99`CXJ+`S6P2M5T9Y$*6coXNse+?R@SY5lN0oPL*)#aiLqi)o1gtKV02-~$ z>ar#4+mPv*^Lsp}d*-rgRxnv7>RWRy`~Sk9YxS7;G@DM&VOgB8IUNxi(h* zCIxgx@oKdFig?y5 zCd@)6Wzzf~-s^sJ=%0PAyl>=jy)LJZg5dDMw9Z^oHT0W;n7Pndsw6WVk6$eBeblg0 z{*o6jaXn5(7Q`tR2|<w9vmkt%$v&LDgShVx^;o0#7~+|MmBc*ob&_% zsiPH#?ud+N|J;&kfF`S--icvhC;w41Zx)AyimuB{JXy0hhc^`Xeex9Kk^!I!Xs55O-*b(s@RFq-i2ym1XNNy)YH@-q41A^ZUecJZN*i zIi=}8-0T4%NX=y!%dPSwq@phP-0tn=!98U0PwSze3z=$+`{w#eS~pUokx zIAXDYm>c{2Nngb%&MO?j%lm$;5E-hCev6#bwRSR>o?g0D{le?9w>;kUP1$*#-@A9l zg>aXtMBPh-Jz@xqzNC|X$jx`rYp!eJQgxz~7{bOUNeg0{Q+X8^_ITKeh0&_Ne5lcR zYqd_YfK;XZP5C!K=o}Nlq~z}P)F^A?ckB>OZ~!ff+e3ZM=u=cF-)XmxgkKbFfL`Q{ zyEJIC=`3PV=>D_IwX3|cp)}fqn!x5)TRl8)7udHvVs}aE)<5nsxy?3ZTzg^0F?@<3 zHA0FTdnxSA5f3Tsi(<|ztVul`-(WbHshSQuc9N$E^(YZ0$#zdP`&0+fK=G5=si!1=?NhihZd4XDS+Hnb z1?z_q4EE0pl7C`ht)RERc3qF|0Y5}F6x|2Z-6tEOiwYk2!D2HQIK^t}+tKR05}io< z_2-U2_MKNB_ug?L5eZGXv03;Rmg@X@Bw`WvN|#NFh;S=4e0V+h3SqdS5(87$!&BCK zdi}?=surMaa7~IC$V7;68aUv2BAe3UMWw}s(_k3!OB|b}Yrp(+hh5!M=8sSGM)vtV zQ+;0bK4lDp!Qs$o-owNz0^iS6%>)>JY@i)d$5%4?z8hGPsRgF*Wjs~&1(griR)o{~ z8)%9@ZMLIen51~$=!j>$soF~y`vfcGaedh9pFYS^@@XCF_ikx$X%LwxhGBGlgRBVe zv1mCtyk2R{w62R*6(LDck1F19!O+~W>L^JTPbA`S?@z>_Agi#qn6UJACv|-g%e+V# zZkj^Wuc}JFk_>ec%!j%^d|MJnAb4xPEObF*bA&hXcjLkq8>lu;s-$K|BhGA*{d}V< z>l1x>-MmHkFlfOzuF%HRY550#8R2a$vM$VhqrB=KZe?!D6yG6KG zTY?Mr!NRH_s{!WJXnb*o_U``6b{756z)H_hprK#ZlOk(@ZGScGI3kY|BQ%-HwDft- z)J1YVM~jN{3^p1xKT-$^+*(%^HJXnisKmEJiS=moX#N;0I%+X*Eegx@>Spm_f1q*FZO#(AV@5W(DQn?Gd&q=P^_hktEt zD_!i1;JTu3AF?_vmoa*(#Lfgyk%1bj>Q{Fm5cPbdh1-bmfLj^*q{;e^l8CR}L4C_RcMh7n>p-wY!3}!{vrvdt=R;;JWoC zxl-tCxzx@DhOE3f+YR)(Q0ao{XLN$T=<3eis7}^L-Po{|b84e_m{J}Yx&^B;4&2x; zYRbnH!sPcjx{ki8xYm7t8>qAerp>zr#$@^{>RXCg`R@M4)E?^1hujWrA zZA=|AFP>jz+Jv+N|__=$;Pc-!laYLw>eEJxYds)EiFa9Gv}Jh zFonFSi|e+M7`Y727;H20(O6j|*U8WlI66UnWt_~iH+!h0_N=1s+!$elEZJ>1_suiR z#?vR~ah7sgaMc;&*9TAsr`*Os-qfIT+YB5NN^_iZXQQ21G2`k)J!Rbc$R6RxwuvojZiaUM z;C!Q?K80H|s~?SX*@=wlojXe|pirVw5mh0n^;>30^+GH6tlZg~sL2eUrhsiq;qEiE zh9J;0b!M(!{iMEGo_pQOhWt}W7E~JtM1{#zSx>9|g}E0i8^1K0>uwc(5kEA1N%sbQ z#Vd`LFlTsC2sVpvGH#+v2$HeqkUgs3p$BB8DX`a^$+nwZ)Zy7qHWjZE-tA1rE zEMR?FP3A^*V>8qv-y(gQRoIE*JUf6+rR4_0MS7-{K3ueJ*Hu_HbAEkgv&#TyKKi^lT|T;jeQK7^**N;G z_a}$j!Bc&nE=E3m(K6d}`Dw7Iegw05Qc0bwDilg-`edJVi&FRxslg7<*&4#Ey@p6ejXsjZO z<=EMAvd^eF$6KI=4NgXIsM_V^ox4hmP$)E-UvQu8Et(ZWuj@Uz@3_6?Ny35<^te9d z_IKZ@>s{2mvhSWLyQ~y)BghixxGF;auo^wi=zAvT(UHIP#;6Vh|J7k!*qD}haXQK7 zmYvS3X_RxVHa`q@+#Pf~a?aNNqc>~kV{*yUM*(&k%cm9bx!3b@Azku!lTEbkrL>7p z3GX&8dCqixcW&u+?>UA*gj=196?F5)?KB8I_=jL%ot_mkZ+mdVHn2q5Xuj8YuAwg8 z7Qy@ty?f4YZXsnf(ik(gt?y0ToAxSjhDb_x^M2Vj;dRVT`2c4I?=U@Vf8~|8y_=7X zEVy{%Pst?zBNyV}nb9p8?{!nxM#kWl>5012DFMw+<{J3hY}z}!%H++hUmS}f+k4jz znC*?PJ9)BmY&9?<;oYy~qU{lM_7~}8y1zr(BC&v6`;V>P&LIF4XG>MKowi ziQj6i-;J&+%(yT`K#LQmDwEwX7~K@7DZ=-OeGz32sHB3a{fr6Qamy=qXUfgqF73Am zHA?kCSgh@ZVjK17zWydDwM8+<_dC%kF><&Gc-o&DAG)M?KZfaGogr~n{CpMn51t`ATp^n_D;o%k0#<_psO&##o7c`gK zcgR`fx8z>9`sGs&R2%a60+!)^1SH}I(fPL+1($Ax>Fl?mgy`$8NkKm)%cS9{S4mHs z;jsp$I`>G;f4&deizy4eE(;JQ9rNyN-jiwa+_ohQ+<{gfR2s~viVB9uQ@{80%zrf$ zr54Ytc=E^WO}W*g4>!|!&pX)pGyvyws4WU}x}c&x`S_W6!L=`wZKr#3ep)*{Fue9K zK#e^d0+~&iws8?i9IyS{NRe2m6RYiIyIxE%dv!&E zeyKh#Q3s;Y(yYJ8S#RnKowBuyeK*ns`F4MMt0JT4_F}4H&)}_f=nBrQF zxdzDKPGgtxqOrxY=u(|#_en2svmxm}Q+h}0w$ArmMs;-ZNj_2#sVLY^$CT8B^L_@S zE*wlR|JWngV}d|@b3zxbnZD}m_h!??=2rQ_p#*hQvVmjEij~~~2)M}2vee#<@Jh@K znN5R!7wkb_#-}b^v7g}ol4T!F8P@gSrY}tvl(dW`$3(p3P3_eDhp|a*tkUVfq4Q{S zsmfa7T}g%KZanDH@Z!B5{TNbdR^VeoyD}|N5H?nC6;0^l@0QCMYsUvNxpjB7r&7Z% zt9J&`l13}DX?-*7do(oWI`X4A+$x`&pYs;B|9J|HF*pNE+SpvO#Wu)({_u>KqS&5Y zA?u6mDx=~?ljf`EFc=gnq0*T!a4J1+<|><}7&Ilf)VHu5N^VQ5w}*Pw)cT*x>?RM) zt~`-gi0^IEU6jCpfRj+*Oc>upGo~eP5B5cL%qaVvs-O@THhW3TVB^^BPzZi-ym`cO zGV?{HUESeesvoR zR%Z>B3qMZLPS-&&-|H);V9{BmaN_|9JZ#QOmD%%Skb(&R8}-oWD0_#82BJC$($rd_ z2%3@6vY$#8KytQ0?Pa@Tj~ACDM>H7$PviMz1(D0gy>!f#RU^6wFZTV6SpI#mI!G^A z-^VAR-AO9e`20A{mBT={velM%-XsH%(8xiJiX>b?M-3j|@fTmG0elz=)Uk z09w#GTvq{Qs;yldp%%4j^>g6(EqLlj*%N!Iauz>#VC;eg?sPnWPEY=c_4{t9`*#zw z?k(*oBknu6j@{uvWDdQ)B22pCc&1!oq=bAHom~7r^12Mtz_zji#f+ZXd9-Vi-o$THiiwaH{TmkCY@=~nkYzN zz*9#(=%UuIGn4FNq5CITr{@cyykr?^_1KXDZM7)3>XJfxps~pZerTpXNr@dlrSQHG_ zNj_(oI}!r6ca^p$N*0_U2CflO2c8$!NUJ9tJ2}(fc5&+8Q~27|&Ea=8BN^VhMF+N{ zN_{VTe({O~DfHFYTv@j0HGL#$T31p7zcuc2nNf9|IE zd9cXd5gpEUQf7@!Vyug?#1%%Wtp zeSB)~fX43tlXOhVz?aYtIr-}rxzfC;3u-y^Eg4KJ9)C@FUMY771Nz$9krDK$uPSqe z$A=dxUbi>2I4xbZbAu1g-^M$ILJb#$s0Usu#|3~ZsA}Zp0rrI(A1mffog)~+Wx4a= zQA^XX(_VPpNrCQc2V)}5KQD9_I{joJP0?VE)&FhDn84lUJT?zS zsk@6xDAY}KTyV}a%Yq_~F)_gkNu}S?@U8r<117U>j$ESX5>j4+^;IPaGJ?xDjlLcS zvre#t)j*KGP|!wtd}zuoXa&gDYIAU)TCg{I4jo`e=MRVa!*AT$1Ps)Wm*%Gm=0HcG z)>{MhcB4pp25``?Gr7VHyCI8fPVhyBa&DcqV4MROZ4TjlyFI$ra5z$U4Q?uaUwxnnwYvj!OU1)81z3dKnv# zTWGWc7HcF;LS*H>?CUU2s{)0bBtLVT(tMJ;r*%PS@G3nkSd`4}=+*T1P~4Sqg!FKw zd;Knr2IsB4P7fxMsy<)b`2LD6U7I(5-d*4i)8q>IMziNM8Uz`sox;4%F@oL;FA1>x znf0sfHCk#oEIQ`r?^rcizB4iu)Q%)#RjKgfd-?ytPG zQLU_)O5RfolKDTax81i_9-ksj^meKOA=6&@5#Q$p^T9&>ZCr+Cr?f@_A8IOL@z(19 z29fvHoYE7t5UbWju112yh_CIJfx+V46TGS}JgDdpfu}AQJjJpu7C1Hi0t0UtZpjXA z=KTr7BxcX#iX#j%K0FkRSE!VDI+f@L)n*QU@lBvkI1Bs9sZ>)L0(MHQHi>@n-y_ z14xOYb7*N==gkGPAa`X}-9-ILvKYF&N)^*O`hV5z_p(T9wKZESCaQ!FE+1-w%&3c-RrI80Wb!U_~ z93I{~S-hz~k|Q!`(0cGQ8Z<3(zar;5ES@$tkL4q+1sJa0t#)-lkm9cTz2gTFV&E$L zr`yo_(sEXCx%FCQs;oQ?!8`^Ab2F^$qR+x&S6QP9plN=dSY+5*wnVs>IF#uJ?kij7 zN^e|H>wl^7%}@*HelZ%1BKaq1f3x3M9_;3b1U2QT?8W&CLy1>?GiV+{;1U*%PSPGO zlN__l@%TOwv<(HjeTLMoQmDkwbe~iNHvKKsX-2Xl+jk2&bDSVHTPz4kr!G(N3Mr^G z0)^Zflr0W5^db7J2I6rf(m26rzA&nfUK93^hDuppZBt{ zL+z%ADA%t7+0Fc_Ft~=@zmj;DJDyiVgA(CR@ZizvwWaa>xKR>QCIoz|Br^ag2a&&W zW2)`4m24YYEptJJD{o%yn{Wd4vbY2!=P%O zUdQ9auP<^zmtO;aCPtY%f{Zjp4%nBE49||>CD5_sgFD;6rJ6u3!xwgYxgd5IdT+j? z1PBOfD9#5y=3UrnPm|+q!eF2_w6xP;!D}|)JqFX7w~PC|4kEl$8K+`n>M88N%bmEL zeG$KYAP{?*O;1!(1$XgaI*!3x>-op1N`pJ!i$dkndmOi>#8$rR7qB;{=qw|KP@;e! zca9{VNC=etYPPx5vz4;i|9)YLIYkMLqc=3j++W`eUytQrwZJaG1-#FD{`RjjV zq%5|YSt9N{3nKz^)(T2I|%l53V+{Po@~iAVj*vp)u+rH?2oQAh2=LF z?fkoxd6=sSJV&{qtnJU2Hu*(ckaT&#Eo3n)_5s%$1%}UX47+@~QP5&~Y}P@bU^IFM zAF~C5u(jT`QLVm4@{=VaX53_e#ykWw#Xe>#4?heo+{fm8d;yXOmqF+re8>efY1|Zq z`_J5FRs<}M_hGq!Xfzi>*XqSwlLQN?>J6#4rb~Gw!`xo#sabtp!9HhI5n4J|yh?C9 zNAgc^UA(}hkgLB&R;NPDwvsDupbZv&7tQAZp%2 zDmaQvh^o{CGN-J5a0gV9vOza_mYi>Qi6#yo~eo$xWpB zuaddsRmZ_(#g=5&M+HoebBH7%-A03PhIge>ROsop{+{cJRR3K7NSt(}C-{zte|WDo z&;Bwm|B3Ly29b2DGP>Er8?(OtPp)DBq=K-Pj2h`RQxbV^eD5#W?WQNfda3cinH--x zszJIMc+-ZmIfrK16Z+W7vwv3fPk_{0tqwPbKR3wWxajI^9&d8c>4VqUb4|z&xf-j# zgKoG`hZ{{^8N2l@`9+N95~%T511%ur^9&d}3{U80KGD1=U@G9;oDh>be`GxHX(7j{ z{d@w6n<-fY1ER=wq6*jTUBIX6k@qw;G6eh%_VS*|6`s;= zo_hvzwSj-rkJL^fOL+Bpe%0S1n9&_DU=aP)9$hzP@7%bWjOJ=FJPh_`{E9uAJMtSI zX8eYxrkbc1b&?-o@BsZ_qol9=c&@Q|sCj56eT7R}UT@zH;bch#-zEwD-(N--jbaF)m^o07=qW%Eu~uz@cljt6^UHU7|3!kWREvf_Hj z#p`j+h3TLWKXlDx`PS^b_E^-qAU@TbxTG0!gnu=*s^xy{i>i%47I~{Duz*d`&}tc7 z2h)IgKkliqP0g1KQIPn&ViGFhIg=+rYH!8ajLE0bQj`V?Lh5tUgvn4;stG2 ztWm*%ZFBP-FLkL007KS@a99D&#Xu%`VOP{G_!o`Nmzg(IBtVNy1Wr#Bl;=(LHe6qN zK2^d$IF-l`yX{K^)h#Fqie=Z=*Lv%X!MIoidA{G=>C~Un*yR$bBu1Ym1MEI6S@$d1 zjFbq*E9b2n8%tkQW$T};bw)P z;e;?t@t2$WBssqik%xrE%Hntcq4>+qZj{xRbRbG94{(cJ_{$4kM^t9S7^4IpV45F! z<@8)~%R2ATxT`OTif4l%LLQ(S(tl4*$)Hmd&OGD?;gCl2uP8Vp4{rQ`%l6lTuW7-A zlOmqGj|YI_`R@lv99%p+zz%u9CLS)V|Lq6Z{6E+J-TnV%lhni(&ls6LnJQ2c2_W+v NJ7Ph~IqZ1ne*s~%UR(eG diff --git a/plots/rc106_21.png b/plots/rc106_21.png index 32f23b4610567b486ed7df5961e465847c0ded09..ecfe06d2b5c338b4be5b4dbca0558c4835be4e66 100644 GIT binary patch literal 14637 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 Cb9o#9 diff --git a/plots/rc203_21.png b/plots/rc203_21.png index 16efc77a9c2c403e790e6c1d135e59662c1939a0..ac3099d06d609c78dc3d08181c7d17a7ddb7377f 100644 GIT binary patch literal 11641 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 Tok_&N>7+Fd>(Og=zVZJ9v$^wH literal 13067 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 z7)>Mx)s(station.id.toString()) val stationNode = graph.getNode(station.id.toString()) stationNode.setAttribute("xy", station.location.x, station.location.y) - stationNode.setAttribute("ui.label", station.name) +// stationNode.setAttribute("ui.label", station.name) stationNode.addAttribute("ui.style", "fill-color: #2E8B57;") } @@ -49,13 +51,16 @@ class EVRPTWSolutionVisualizer { graph.addNode(customer.id.toString()) val customerNode = graph.getNode(customer.id.toString()) customerNode.setAttribute("xy", customer.location.x, customer.location.y) - customerNode.setAttribute("ui.label", customer.name) +// customerNode.setAttribute("ui.label", customer.name) } // plot routes for (route in solution.routes) { + val edgeColor = Color((Math.random() * 0x1000000).toInt()) for (i in 0 until route.size - 1) { graph.addEdge((++edgeIdCounter).toString(), route[i].id, route[i + 1].id) + val edge = graph.getEdge(edgeIdCounter.toString()) + edge.setAttribute("ui.style", "size: 3px; fill-color: rgb(${edgeColor.red}, ${edgeColor.green}, ${edgeColor.blue});") } } From e0088e3e4f238530e7da16c5c66771f156cc2c3d Mon Sep 17 00:00:00 2001 From: David Molnar Date: Wed, 27 Jun 2018 13:55:18 +0200 Subject: [PATCH 35/36] increase graph plot quality --- .../at/ac/tuwien/otl/evrptw/EVRPTWSolutionVisualizer.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/at/ac/tuwien/otl/evrptw/EVRPTWSolutionVisualizer.kt b/src/main/kotlin/at/ac/tuwien/otl/evrptw/EVRPTWSolutionVisualizer.kt index 546f398..2276a3e 100644 --- a/src/main/kotlin/at/ac/tuwien/otl/evrptw/EVRPTWSolutionVisualizer.kt +++ b/src/main/kotlin/at/ac/tuwien/otl/evrptw/EVRPTWSolutionVisualizer.kt @@ -29,12 +29,14 @@ class EVRPTWSolutionVisualizer { private fun plotSolution(instance: EVRPTWInstance, solution: EVRPTWSolution) { val graph = MultiGraph(instance.name) graph.addAttribute("ui.screenshot", "plots/${instance.name}.png") + graph.addAttribute("ui.quality") + graph.addAttribute("ui.antialias") // plot depot graph.addNode(instance.depot.id.toString()) val depotNode = graph.getNode(instance.depot.id.toString()) depotNode.setAttribute("xy", instance.getLocation(instance.depot).x, instance.getLocation(instance.depot).y) - depotNode.setAttribute("ui.label", instance.depot.id) +// depotNode.setAttribute("ui.label", instance.depot.id) depotNode.addAttribute("ui.style", "fill-color: red;") // plot recharge stations From f4d2647548f371e8387276b4b0649d0ba189e875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fu=CC=88vesi?= Date: Sun, 16 Sep 2018 13:53:45 +0200 Subject: [PATCH 36/36] prepare for release, update readme --- README.md | 29 +++++++++++++++++++++++------ build.gradle | 2 +- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 21e2fe4..ecc7929 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,10 @@ Solver for the Electric Vehicle Routing Problem with Time Windows.

- +
+ Image Source

-[Image Source](http://ls11-www.cs.tu-dortmund.de/people/chimani/VehicleRouting/) - ## Problem description In the E-VRPTW, a set of customers must be served by a fleet of battery electric vehicles (BEV). The E-VRPTW extends the well-know [VRPTW](https://en.wikipedia.org/wiki/Vehicle_routing_problem), @@ -20,7 +19,7 @@ More formal, the EVRPTW is defined on a complete directed graph G = (V,A) consisting of a set of depot, customer, and recharging station nodes and a set of edges. Each edge _(i, j)_ has a distance _dij_ and a travel time _tij_ associated and each traveled edge consumes the amount of _r_ x _dij_ of the remaining battery charge of the vehicle, -where _h_ denotes the constant charge consumption rate. Furthermore, a set of homogeneous +where _r_ denotes the constant charge consumption rate. Furthermore, a set of homogeneous vehicles is given, where each vehicle has a maximal capacity of _C_ and is located, full loaded at the depot. Each node has a positive demand _qi_, a service time _si_ and a time window [ _ei_, _li_ ] assigned. The service must start within the given time window (thus, @@ -34,13 +33,31 @@ served within their given time window. All routes must begin and end in the depo the vehicle capacity and battery capacity must be respected. The objective function is to minimize the total traveled distance. +## Implementation Details + +Our E-VRPTW Solver consists of two parts: +1. Construction heuristic: [Time-Oriented, Nearest-Neighbor Heuristic](https://pubsonline.informs.org/doi/abs/10.1287/opre.35.2.254?journalCode=opre) (Solomon 1987) +2. Metaheuristic: For this part, we tried to implement a Hybrid VNS/TS metaheuristic proposed in the paper [The Electric Vehicle-Routing Problem with Time Windows and Recharging Stations](https://pubsonline.informs.org/doi/10.1287/trsc.2013.0490), +however our solution is not the most efficient one and requires further optimizations (especially in route representation) + +Model objects like `EVRPTWInstance`, `Customer`, `Node` have been copied from the [E-VRPTW Solution Verifier (Java)](https://github.com/ghiermann/evrptw-verifier) implemented by @ghiermann. + +See our [presentation slides](https://docs.google.com/presentation/d/1WnySkapfZkM57kC_8XTIKsyNZOhBhiNAdNTUWY201tQ/edit?usp=sharing) for more details. + ## Usage -// to be written +See Main.kt for instance/plotting/performance-recording customizations. + ## Contributions Special thanks to my dear friend and colleague [David Molnar](https://github.com/dmolnar99) -for participating in this project. +for participating in this project: + + + +| [
David Molnar](https://github.com/dmolnar99)
[🤔](#ideas "Ideas and Planning") [💻](https://github.com/fuvidani/clickbait-defeater/commits?author=dmolnar99 "Code") [⚠️](https://github.com/fuvidani/clickbait-defeater/commits?author=dmolnar99 "Tests") | +| :---: | + ## License This project is licensed under the [MIT License](https://opensource.org/licenses/MIT). Feel free to diff --git a/build.gradle b/build.gradle index 244af3b..13b54cf 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ apply plugin: 'kotlin-allopen' apply plugin: 'application' group ='at.ac.tuwien.otl' -version = '1.0-SNAPSHOT' +version = '1.0.0' mainClassName = 'at.ac.tuwien.otl.Main' repositories {