Optaplanner 约束流

时间:2021-04-26 18:10:26

标签: java optaplanner

我正在为“更高级别”的 ConstraintStreams (bi, tri ..) 苦苦挣扎。在 Optaplanner 用户指南中显示了最简单的情况,我理解它们。还有我能够分组的例子 - 总结一个 BiConstraintStream。 现在我有一个 TriConstraintStream 并面临问题。有没有人知道更好地理解 ConstraintStreams 的良好信息来源?

我的案例:问题结构与运输问题非常相似。我有节点和边,想计算边的数量。

我的主要问题是:

class Node{
   UUID id;
   int itemsInInventory;
   ...
}

和规划实体:

@PlanningEntity
class Edge{
   Node from;
   Node to; 
   @PlanningVariable
   int itemsTransported;

   getFromId(){return from.getId()}
   getToId(){return to.getId()}

}

我想将 Edges 两次加入节点,然后 groupBy 他们确实有一个像 (Node, Sum(to), Sum(from)) 的对象

约束的当前代码为:

public Constraint minimizeShortage(ConstraintFactory constraintFactory) {
    return constraintFactory.fromUnfiltered(Node.class)
            .join(Edge.class,
                    equal(Node::getId, Edge::getFromId))
            .join(Edge.class,
                    equal((node, edge) -> node.getId(), Edge::getToId))
            groupBy(...)
}

对于 BiConstraintStream(仅使用 fromNode 或 toNode),groupBy 将是 .groupBy((node, edge) -> node, sum((node, edge) -> edge.getItemsTransported())),但在第二次加入后我没有得到 TriConstraintStream。

2 个答案:

答案 0 :(得分:2)

我将以上一个答案中的代码作为我的起点:

public Constraint minimizeShortage(ConstraintFactory constraintFactory) {
    return constraintFactory.fromUnfiltered(Node.class)
        .join(Edge.class,
                equal(Node::getId, Edge::getFromId))
        .groupBy((node, edgeFrom) -> node,
                sum((node, tsaO) -> edgeFrom.transportedItems()))
        .join(Edge.class,
                equal((node, edge) -> node.getId(), Edge::getToId))
        .groupBy((node, edgeFrom, edgeTo) -> node,
                (node, edgeFrom, edgeTo) -> qtO,
                sum((node, qtO, edgeTo) -> edgeTo.transportedItems()))
        ...
}

此代码不太可能表现良好,因为 groupBy() 的开销很大,并且将它们链接起来会使问题逐渐恶化。让我们看看我们是否可以只用一个 groupBy() 来做同样的事情:

public Constraint minimizeShortage(ConstraintFactory constraintFactory) {
    return constraintFactory.fromUnfiltered(Node.class)
        .join(Edge.class,
                equal(Node::getId, Edge::getFromId))
        .join(Edge.class,
                equal((node, edge) -> node.getId(), Edge::getToId))
        .groupBy((node, edgeFrom, edgeTo) -> node,
                sum((node, edgeFrom, edgeTo) -> edgeFrom.transportedItems()),
                sum((node, edgeFrom, edgeTo) -> edgeTo.transportedItems())
        ...
}

让我们再讨论一下。这个特殊的 groupBy() 重载由两部分组成:键映射和收集器。

键映射将三元组分成组 - 在这种情况下 ((node, edgeFrom, edgeTo) -> node) 具有相同 node 的所有三元组将落入同一组。

然后每个收集器在每个这样的三元组上应用给定的操作(在这种情况下为 sum)产生一个结果。我们在这里使用两个收集器,一个汇总 edgeFrom 中传输的项目,另一个汇总 edgeTo 中传输的项目。并且这两个收集器仅适用于共享相同 node 的边 - 正如前面提到的键映射所保证的那样。

附带说明一下,在开发此类非平凡约束时,我绝对建议使用 Constraint Verifier 对一些典型结果进行建模,并查看约束是否按预期运行。

答案 1 :(得分:0)

我找到了解决问题的方法。解决方案是使用下面的语法,特别是不要在一个 groupBy 中总结。我不得不将总和分成两个 groupBy 并按以下顺序计算:Join1 - groupBy1 - join2 - groupBy2。

public Constraint minimizeShortage(ConstraintFactory constraintFactory) {
    return     constraintFactory.fromUnfiltered(Node.class)
            .join(Edge.class,
                    equal(Node::getId, Edge::getFromId))
            .groupBy((node, edgeFrom) -> node,
                    sum((node, tsaO) -> edgeFrom.transportedItems()))
            .join(Edge.class,
                    equal((node, edge) -> node.getId(), Edge::getToId))
            .groupBy((node, edgeFrom, edgeTo) -> node,
                    (node, edgeFrom, edgeTo) -> qtO,
                    sum((node, qtO, edgeTo) -> edgeTo.transportedItems()))
            ...
}
相关问题