我正在为“更高级别”的 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。
答案 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()))
...
}