Chained Variables / Anchor Variable and Error: Only a chained variable supports a singleton inverse

Chained Variables / Anchor Variable and Error: Only a chained variable supports a singleton inverse

前言:Google+SO+docu搜索未出现相关信息

领域模型:

我的域模型试图描绘一个包含机器列表的生产计划。 每个机器都有一个链式作业列表,因此一个作业有一个 getNextEntry():Job 方法,创建一个作业列表。

我试图用链式 PlanningVariables 解决这个问题,但显然不理解 chainedVariables/shadowVariables/anchorVariables 的概念。 据我了解,所有作业都被链接起来,并且 anchorShadowVariable 指向列表的开头,因此指向机器。

为了实现链接,Job 和 Machine 需要实现接口或扩展超类,因此我创建了 ChainSuperClass。此外,我不知道是否必须覆盖 getter/setter 才能在 Machine-Class 中设置注释,我的猜测是因为 Machine 扩展了 ChainSuperClass,这些注释会继续存在。

编辑:Kotlin 的特定改进也很受欢迎。

我的代码执行的完整错误日志现在是:

Exception in thread "main" java.lang.IllegalArgumentException: The entityClass (class optaplanner.productionPlan.domain.ChainSuperClass) has a InverseRelationShadowVariable annotated property (nextEntry) which does not return a Collection with sourceVariableName (machine) which is not chained. Only a chained variable supports a singleton inverse.

超级链Class:

@PlanningEntity
abstract class ChainSuperClass {

@PlanningId
open val id = Random.nextInt().toString()

@InverseRelationShadowVariable(sourceVariableName = "machine")
abstract fun getNextEntry(): Job?

abstract fun setNextEntry(job: Job)
}

工作:

@PlanningEntity
class Job(
    val jobType: JobType,
    val itemNumber: String,
    val orderNumber: String,
    val setupTime: Int,
    val productionTime: Int
) : ChainSuperClass() {

     @AnchorShadowVariable(sourceVariableName = "machine")
     var machine: Machine? = null

     private var nextEntry: Job? = null

     @PlanningVariable(
          valueRangeProviderRefs = ["jobList"],
          graphType = PlanningVariableGraphType.CHAINED
     )
     override fun getNextEntry(): Job? {
          return nextEntry
     }

     override fun setNextEntry(job: Job) {
          this.nextEntry = nextEntry
     }
}

机器:

class Machine(override val id: String, val jobTypes: List<JobType>) : ChainSuperClass() {

    private var nextEntry: Job? = null

    override fun setNextEntry(job: Job) {
        this.nextEntry = job
    }

    override fun getNextEntry(): Job? {
        return nextEntry!!
    }
}

我认为使用链式变量实现的最重要的事情是:当你有一个实体时,说 Job A 并且求解器分配一个值 (job/machine)到它的变量,这不像是从 Job A 开始向前构建的链。恰恰相反。通过为 Job A 的计划变量赋值,Job A 连接到现有链的末端。

请查看 documentation 以查找有关链和有效链示例的更多详细信息。

通过理解这一点,应该清楚 Job 的计划变量名称应该类似于 previousJobOrMachine(您可能想要更简单的名称,例如 previousStep)而 nextJob 属性 是从中派生的反向关系影子变量(因此当 Job X 连接到以 Job 结尾的现有链C赋值Job X.previousStep=Job C,自动建立反关系:Job C .nextJob=工作 X).

根据该信息,您的模型应该更像这样:

@PlanningEntity
abstract class ChainSuperClass {

@PlanningId
open val id = Random.nextInt().toString()

// changed sourceVariableName to point to the planning variable
@InverseRelationShadowVariable(sourceVariableName = "previousStep")
abstract fun getNextEntry(): Job?

abstract fun setNextEntry(job: Job)
}
@PlanningEntity
class Job(
    val jobType: JobType,
    val itemNumber: String,
    val orderNumber: String,
    val setupTime: Int,
    val productionTime: Int
) : ChainSuperClass() {

     // changed sourceVariableName to point to the planning variable
     @AnchorShadowVariable(sourceVariableName = "previousStep")
     var machine: Machine? = null

     // added planning variable
     private var previousStep: ChainSuperClass? = null
     private var nextEntry: Job? = null

     @PlanningVariable(
          // added machineList value range provider
          valueRangeProviderRefs = ["jobList", "machineList"],
          graphType = PlanningVariableGraphType.CHAINED
     )
     // getter for the new planning variable
     fun getPreviousStep(): ChainSuperClass {
          return previousStep
     }

     override fun getNextEntry(): Job? {
          return nextEntry
     }

     override fun setNextEntry(job: Job) {
          this.nextEntry = nextEntry
     }
}

请注意,我添加了 machineList 作为 previousStep 计划变量可能值的来源,因为上一步可能是非空链末尾的 Job或表示空链的 Machine

您的域与 vehicle routing domain 非常相似,您可以在其中看到链接的工作示例。充分理解链接原则后,您应该能够在您的域中复制它。