在基于OptaPlanner的应用程序中,我想使用ProblemFactChange,根据https://docs.optaplanner.org/7.13.0.Final/optaplanner-docs/html_single/index.html#problemFactChange,它将重新启动所有求解器阶段。
问题是我不想重新启动分区搜索阶段-求解器应进入下一个阶段(CH),然后进入本地搜索阶段。
是否可以使其以某种方式工作?
答案 0 :(得分:0)
有趣的用例。我首先想到的是使用自定义Termination,但这仍然会导致阶段启动开销-而且Termination接口不是公共api。
这真的是一个RFE-我们需要能够在阶段配置中插入一个条件。
答案 1 :(得分:0)
我认为我设法解决了这个问题。我创建了两个求解器行为:第一个行为完成后从求解器中删除分区搜索阶段,第二个行为恢复阶段开始时间以保留终止结束。
行为的基类,请注意,我使用非公共API。 我已将所有类放在原始OptaPlanner的程序包中,以便可以在无需反思的情况下访问受保护和受程序包保护的类。
package org.optaplanner.core.impl.phase.scope
import org.optaplanner.core.impl.solver.DefaultSolver
/**
* Base interface for all behaviors
*/
abstract class SolverBehavior<T>(protected val solver: DefaultSolver<T>) {
abstract fun apply()
abstract fun unapply()
}
以下行为在完成后从求解器阶段中删除了分区搜索阶段
package org.optaplanner.core.impl.solver
import org.optaplanner.core.impl.partitionedsearch.PartitionedSearchPhase
import org.optaplanner.core.impl.phase.event.PhaseLifecycleListener
import org.optaplanner.core.impl.phase.scope.AbstractPhaseScope
import org.optaplanner.core.impl.phase.scope.AbstractStepScope
import org.optaplanner.core.impl.phase.scope.SolverBehavior
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope
/**
* Ensures that [org.optaplanner.core.impl.partitionedsearch.PartitionedSearchPhase] run only once
*/
class RunPartitionedSearchOnceBehavior<T>(solver: DefaultSolver<T>) : SolverBehavior<T>(solver) {
private var solverStartCount: Int = 0
private val isFirstSolverCycle: Boolean get() = solverStartCount <= 1
override fun apply() {
registerForSolverEvents()
}
override fun unapply() {
unregisterFromSolverEvents()
}
private fun registerForSolverEvents() {
solver.addPhaseLifecycleListener(phaseLifecycleListener)
}
private fun unregisterFromSolverEvents() {
solver.removePhaseLifecycleListener(phaseLifecycleListener)
}
private fun handleSolverStart() {
incrementSolverStartCount()
removePartitionedSearchPhasesIfNecessary()
}
private fun removePartitionedSearchPhasesIfNecessary() {
if (!isFirstSolverCycle) {
removePartitionedSearchPhases()
}
}
private fun removePartitionedSearchPhases() {
val phaseListIterator = solver.phaseList.iterator()
for (phase in phaseListIterator) {
if (phase is PartitionedSearchPhase) {
phaseListIterator.remove()
}
}
}
private fun incrementSolverStartCount() {
solverStartCount++
}
private val phaseLifecycleListener = object : PhaseLifecycleListener<T> {
override fun solvingStarted(solverScope: DefaultSolverScope<T>) = handleSolverStart()
override fun phaseStarted(phaseScope: AbstractPhaseScope<T>) {}
override fun stepStarted(stepScope: AbstractStepScope<T>) {}
override fun solvingEnded(solverScope: DefaultSolverScope<T>) {}
override fun phaseEnded(phaseScope: AbstractPhaseScope<T>) {}
override fun stepEnded(stepScope: AbstractStepScope<T>) {}
}
}
以下行为(包含在Kotlin文件中)恢复所有阶段的开始时间,以保留其终止时间。
package org.optaplanner.core.impl.phase.scope
import org.optaplanner.core.impl.constructionheuristic.scope.ConstructionHeuristicPhaseScope
import org.optaplanner.core.impl.localsearch.scope.LocalSearchPhaseScope
import org.optaplanner.core.impl.phase.event.PhaseLifecycleListener
import org.optaplanner.core.impl.solver.DefaultSolver
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope
import java.lang.IllegalStateException
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
private var AbstractPhaseScope<*>.startDate
get() = Date(startingSystemTimeMillis)
set(value) {
startingSystemTimeMillis = value.time
}
private data class PhaseScopeId constructor(private val phaseClass: Class<*>, private val phaseIndex: Int)
/**
* When solver restarts it restores phase's start time information in order to preserve termination times.
* This helps smooth [org.optaplanner.core.impl.solver.ProblemFactChange] execution
*/
class PreservePhaseStartTimeBehavior<T>(solver: DefaultSolver<T>) : SolverBehavior<T>(solver) {
private var solverStartCount: Int = 0
private var startedPhasesCounters: MutableMap<Class<out AbstractPhaseScope<T>>, AtomicInteger> = mutableMapOf()
private val isFirstSolverCycle: Boolean get() = solverStartCount <= 1
private val phasesStartDates: MutableMap<PhaseScopeId, Date> = mutableMapOf()
private val startTimePreservationPhaseIds: MutableSet<PhaseScopeId> = mutableSetOf()
fun addLocalSearchPhaseStartTimePreservation(solverPhaseIndex: Int) {
startTimePreservationPhaseIds.add(PhaseScopeId(LocalSearchPhaseScope::class.java, solverPhaseIndex))
}
fun addConstructionHeuristicPhaseStartTimePreservation(solverPhaseIndex: Int) {
startTimePreservationPhaseIds.add(PhaseScopeId(ConstructionHeuristicPhaseScope::class.java, solverPhaseIndex))
}
override fun apply() {
registerForSolverEvents()
}
override fun unapply() {
unregisterFromSolverEvents()
}
private fun registerForSolverEvents() {
solver.addPhaseLifecycleListener(phaseLifecycleListener)
}
private fun unregisterFromSolverEvents() {
solver.removePhaseLifecycleListener(phaseLifecycleListener)
}
private fun handleSolverStart() {
incrementSolverStartCount()
resetPhaseStartCounters()
}
private fun handlePhaseStarted(phaseScope: AbstractPhaseScope<T>) {
incrementPhaseStartCount(phaseScope)
savePhaseStartTimeIfNecessary(phaseScope)
restorePhaseStartTimeIfNecessary(phaseScope)
}
private fun savePhaseStartTimeIfNecessary(phaseScope: AbstractPhaseScope<T>) {
if (isFirstSolverCycle) {
phasesStartDates[getPhaseIdForStartedPhase(phaseScope)] = phaseScope.startDate
}
}
private fun restorePhaseStartTimeIfNecessary(phaseScope: AbstractPhaseScope<T>) {
if (!isFirstSolverCycle) {
getPhaseIdForStartedPhase(phaseScope).let { phaseScopeId ->
if (phaseScopeId in startTimePreservationPhaseIds) {
restorePhaseStartTime(phaseScope, phaseScopeId)
}
}
}
}
private fun restorePhaseStartTime(phaseScope: AbstractPhaseScope<T>, phaseScopeId: PhaseScopeId) {
phaseScope.startDate = phasesStartDates[phaseScopeId]
?: throw IllegalStateException("No preserved start date for phase scope: $phaseScopeId")
}
private fun incrementSolverStartCount() {
solverStartCount++
}
private fun resetPhaseStartCounters() = startedPhasesCounters.clear()
private fun incrementPhaseStartCount(phaseScope: AbstractPhaseScope<T>) {
getStartedPhaseCounterForPhase(phaseScope).incrementAndGet()
}
private fun getStartedPhaseCounterForPhase(phaseScope: AbstractPhaseScope<T>): AtomicInteger {
return startedPhasesCounters.getOrPut(phaseScope::class.java) { AtomicInteger(-1) }
}
private fun getPhaseIdForStartedPhase(phaseScope: AbstractPhaseScope<T>): PhaseScopeId {
return PhaseScopeId(phaseScope::class.java, getStartedPhaseCounterForPhase(phaseScope).get())
}
private val phaseLifecycleListener = object : PhaseLifecycleListener<T> {
override fun solvingStarted(solverScope: DefaultSolverScope<T>) = handleSolverStart()
override fun phaseStarted(phaseScope: AbstractPhaseScope<T>) = handlePhaseStarted(phaseScope)
override fun stepStarted(stepScope: AbstractStepScope<T>) {}
override fun solvingEnded(solverScope: DefaultSolverScope<T>) {}
override fun phaseEnded(phaseScope: AbstractPhaseScope<T>) {}
override fun stepEnded(stepScope: AbstractStepScope<T>) {}
}
}