Optaplanner 多线程异常:"The externalObject ... has no known workingObject"



TLDR:在Optaplanner中启用多线程本应是一个线性操作,但它抛出了一个异常

我正在尝试在视频游戏中使用可配置的加载来优化损伤计算。对于上下文,玩家可以用";重折叠;这会增加力量或暴击伤害的统计数据。最终伤害计算必须最大化为强度和暴击伤害的组合。出于这个原因,我使用Optaplanner为项目分配重折叠。

但是,在XML解算器配置中通过<moveThreadCount>AUTO</moveThreadCount>启用多线程会引发一个异常(在单线程执行中不会发生):

Caused by: java.lang.IllegalStateException: The externalObject (ReforgeProblemFact(id=897f4bab-80e0-4eb9-a1d7-974f7cddfd9e, name=Fierce, rarity=COMMON, strength=4, critDamage=0)) with planningId ((class net.javaman.optaplanner_reproducible.ReforgeProblemFact,897f4bab-80e0-4eb9-a1d7-974f7cddfd9e)) has no known workingObject (null).
Maybe the workingObject was never added because the planning solution doesn't have a @ProblemFactCollectionProperty annotation on a member with instances of the externalObject's class (class net.javaman.optaplanner_reproducible.ReforgeProblemFact).

这个SO问题是类似的,但它的答案并没有解决本例中的异常。

我在下面的代码中删除了包和导入。完整的GitHub存储库链接

项目结构:

src/main/
kotlin/
net/javaman/optaplanner_reproducible/
Rarity.kt
ReforgeProblemFact.kt        
ItemPlanningEntity.kt
ReforgePlanningSolution.kt
MaximizeDamageConstraintProvider.kt
Main.kt
resources/
reforgeSolverConfig.xml

稀有.kt:

enum class Rarity {
COMMON,
RARE,
LEGENDARY
}

ReforgeProblemFact.kt:

data class ReforgeProblemFact(
@PlanningId
val id: UUID,
val name: String,
val rarity: Rarity,
val strength: Int,
val critDamage: Int
)

ItemPlanningEntity.kt:

@PlanningEntity
data class ItemPlanningEntity @JvmOverloads constructor(
@PlanningId
val id: UUID? = null,
val rarity: Rarity? = null,
@PlanningVariable(valueRangeProviderRefs = ["reforgeRange"])
var reforge: ReforgeProblemFact? = null,
@ValueRangeProvider(id = "reforgeRange")
@ProblemFactCollectionProperty
val availableReforges: List<ReforgeProblemFact>? = null
)

重组计划解决方案.kt:

@PlanningSolution
class ReforgePlanningSolution @JvmOverloads constructor(
@PlanningEntityCollectionProperty
val availableItems: List<ItemPlanningEntity>? = null,
@PlanningScore
val score: HardSoftScore? = null,
)

最大化损坏约束提供者.kt:

class MaximizeDamageConstraintProvider : ConstraintProvider {
override fun defineConstraints(factory: ConstraintFactory): Array<Constraint> = arrayOf(maximizeDamage(factory))
// This approach does not take full advantage of incremental solving,
// but it is necessary to compute strength and critDamage together in the same equation
private fun maximizeDamage(factory: ConstraintFactory) = factory.from(ItemPlanningEntity::class.java)
.map(ItemPlanningEntity::reforge) // Get each item's reforge
.groupBy({ 0 }, toList { reforge: ReforgeProblemFact? -> reforge }) // Compile into one List<ReforgeProblemFact>
.reward("damage", HardSoftScore.ONE_SOFT) { _, reforges: List<ReforgeProblemFact?> ->
val strengthSum = reforges.stream().collect(Collectors.summingInt { reforge -> reforge?.strength ?: 0 })
val critDamageSum = reforges.stream().collect(Collectors.summingInt { reforge -> reforge?.critDamage ?: 0 })
(100 + strengthSum) * (100 + critDamageSum)
}
}

Main.kt:

class Main {
companion object {
private val allReforges = listOf(
ReforgeProblemFact(UUID.randomUUID(), "Clean", Rarity.COMMON, 0, 3),
ReforgeProblemFact(UUID.randomUUID(), "Fierce", Rarity.COMMON, 4, 0),
ReforgeProblemFact(UUID.randomUUID(), "Shiny", Rarity.COMMON, 2, 1),
ReforgeProblemFact(UUID.randomUUID(), "Clean", Rarity.RARE, 1, 3),
ReforgeProblemFact(UUID.randomUUID(), "Fierce", Rarity.RARE, 5, 0),
ReforgeProblemFact(UUID.randomUUID(), "Shiny", Rarity.RARE, 3, 2),
ReforgeProblemFact(UUID.randomUUID(), "Clean", Rarity.LEGENDARY, 1, 4),
ReforgeProblemFact(UUID.randomUUID(), "Fierce", Rarity.LEGENDARY, 6, 0),
ReforgeProblemFact(UUID.randomUUID(), "Shiny", Rarity.LEGENDARY, 4, 2),
)
private val solverManager: SolverManager<ReforgePlanningSolution, UUID> = SolverManager.create(
SolverConfig.createFromXmlResource("reforgeSolverConfig.xml")
)
@JvmStatic
fun main(args: Array<String>) {
val availableItems = generateAvailableItems(
mapOf(
Rarity.COMMON to 4,
Rarity.RARE to 3,
Rarity.LEGENDARY to 1
)
)
val solverJob = solverManager.solve(UUID.randomUUID(), ReforgePlanningSolution(availableItems))
val solution = solverJob.finalBestSolution
solution.availableItems!!
.map { it.reforge!! }
.forEach { println(it.rarity.name + " " + it.name) }
}
private fun generateAvailableItems(itemCounts: Map<Rarity, Int>): MutableList<ItemPlanningEntity> {
val availableItems = mutableListOf<ItemPlanningEntity>()
for (itemCount in itemCounts) {
for (count in 0 until itemCount.value) {
val rarity = itemCount.key
availableItems.add(
ItemPlanningEntity(
UUID.randomUUID(),
rarity,
null,
allReforges.filter { it.rarity == rarity }
)
)
}
}
return availableItems
}
}
}

我重新讨论了类似的SO问题。在尝试了几个不同版本的答案后,它终于奏效了。这里有一个比其他帖子更详细的解释:

每个PlanningEntityProblemFactCollectionProperty必须是PlanningSolution中主ProblemFactCollectionProperty的一部分。这意味着实体和解决方案都应该定义其问题事实。以下是为我修复的:

保持ItemPlanningEntity.kt不变。

在ReforgePlanningSolution.kt:中包含全局ProblemFactCollectionProperty

@PlanningSolution
class ReforgePlanningSolution @JvmOverloads constructor(
@PlanningEntityCollectionProperty
val availableItems: List<ItemPlanningEntity>? = null,
@ProblemFactCollectionProperty
val allReforges: List<ReforgeProblemFact>? = null,
@PlanningScore
val score: HardSoftScore? = null
)

在Main.kt:中实例化解决方案时定义全局集合

val solverJob = solverManager.solve(UUID.randomUUID(), ReforgePlanningSolution(availableItems, allReforges))

planningId为((class ReforgeProblemFact,897f…)的externalObject(ReforgePproblemFact(id=897f))没有已知的workingObject(null)。

planningId ((class ReforgeProblemFact没有任何意义,因为planningId类在您的模型中是UUID。查看PlanningIdLookUpStrategy第71行的代码,错误消息是正确的。在这行上放一个断点,看看planningId变量是什么类型的类。它应该是UUID。

升级OptaPlanner,例如到最近发布的8.12.0.Final,以获得如下有用的错误消息:

Caused by: java.lang.IllegalStateException: The externalObject (2018-10-01T10:15-12:15) with planningId ((class org.optaplanner.examples.conferencescheduling.domain.Timeslot, 0)) has no known workingObject (null).
Maybe the workingObject was never added because the planning solution doesn't have a @ProblemFactCollectionProperty annotation on a member with instances of the externalObject's class (class org.optaplanner.examples.conferencescheduling.domain.Timeslot).
at org.optaplanner.core.impl.domain.lookup.PlanningIdLookUpStrategy.lookUpWorkingObject(PlanningIdLookUpStrategy.java:76)

它有一个错误消息;也许"直接指向另一个答案中显示的解决方案的线。

相关内容

最新更新