当前位置: 首页 > 知识库问答 >
问题:

ChainedSwapMove需要将额外的规划变量链接起来(当它来自CountableValueRange)

班高明
2023-03-14

我正在使用Optaplanner 7.38.0.Final

我实现了一个相对简单的模型,遇到了一些问题。

问题是,我想要不时地中断链以返回仓库,我认为没有必要创建另一个类来进行这种中断,并且在需要重新启动路由时,我包含了一个带有epoch second时间戳的规划变量(时间戳介于一个相对较小的范围之间,下面代码中的startoftrip)。

Optaplanner能够使用给定的xml配置创建求解器,如果我在score calculator上创建一个断点,我可以检查VAR,并看到构造启发式能够创建一个有效的访问链,甚至设置时间戳变量的值。

但几毫秒后,当在第43行调用org.optaplanner.core.impl.heuristic.selector.move.generic.chained.chainedswapmove构造函数(在上面给定版本的原始源代码上)时,求解器遇到NullPointerException

    public ChainedSwapMove(List<GenuineVariableDescriptor<Solution_>> variableDescriptorList,
            List<SingletonInverseVariableSupply> inverseVariableSupplyList, Object leftEntity, Object rightEntity) {
        super(variableDescriptorList, leftEntity, rightEntity);
        oldLeftTrailingEntityList = new ArrayList<>(inverseVariableSupplyList.size());
        oldRightTrailingEntityList = new ArrayList<>(inverseVariableSupplyList.size());
        for (SingletonInverseVariableSupply inverseVariableSupply : inverseVariableSupplyList) {
            oldLeftTrailingEntityList.add(inverseVariableSupply.getInverseSingleton(leftEntity));
            oldRightTrailingEntityList.add(inverseVariableSupply.getInverseSingleton(rightEntity));
        }
    }

变量inverseVariableSupplyList似乎包含一个空引用(它在分析包含常规非链接PlanningVariable的variableDescriptorList时创建此空引用)

package X;

import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.variable.AnchorShadowVariable;
import org.optaplanner.core.api.domain.variable.InverseRelationShadowVariable;
import org.optaplanner.core.api.domain.variable.PlanningVariable;
import org.optaplanner.core.api.domain.variable.PlanningVariableGraphType;

import javax.persistence.Transient;
import java.io.Serializable;
import java.time.LocalDateTime;

@PlanningEntity
public class OptimizingVisit implements OptimizingStandstill , Serializable {
    private static final long serialVersionUID = 9163651541108883957L;
    private ContinuousBranchTripSolution solution;
    private Order order;

    private Long startOfTrip;
    private Long start;
    private Long arrivalTime;
    private Long end;
    private Long travelDuration;
    private Long travelDistance;

    private OptimizingStandstill previousStandstill;

    private OptimizingVisit nextStandstill; //shadow variable
    private OptimizingDriver optimizingDriver;

    public OptimizingVisit() {
    }

    public OptimizingVisit(Order order, ContinuousBranchTripSolution solution) {
        this.order = order;
        this.solution = solution;
    }

    public Order getOrder() {
        return order;
    }

    public void setOrder(Order order) {
        this.order = order;
    }

    @AnchorShadowVariable(sourceVariableName = "previousStandstill")
    public OptimizingDriver getOptimizingDriver() {
        return optimizingDriver;
    }

    public void setOptimizingDriver(OptimizingDriver optimizingDriver) {
        this.optimizingDriver = optimizingDriver;
    }

    public Employee getDriver(){
        return this.getOptimizingDriver().getDriver();
    }

    @PlanningVariable( valueRangeProviderRefs = "startTimeCandidates" )
    public Long getStartOfTrip() {
        return startOfTrip;
    }

    public void setStartOfTrip(Long startOfTrip) {
        this.startOfTrip = startOfTrip;
    }

    public Long getTravelDuration() {
        return travelDuration;
    }

    public void setTravelDuration(Long travelDuration) {
        this.travelDuration = travelDuration;
    }

    public Long getTravelDistance() {
        return travelDistance;
    }

    public void setTravelDistance(Long travelDistance) {
        this.travelDistance = travelDistance;
    }

    @PlanningVariable( graphType = PlanningVariableGraphType.CHAINED , valueRangeProviderRefs = { "visitsRange" , "driversRange" } )
    public OptimizingStandstill getPreviousStandstill() {
        return previousStandstill;
    }

    public void setPreviousStandstill(OptimizingStandstill previousStandstill) {
        this.previousStandstill = previousStandstill;
    }

    @Override
    public OptimizingVisit getNextStandstill() {
        return nextStandstill;
    }

    @Override
    public void setNextStandstill(OptimizingVisit nextStandstill) {
        this.nextStandstill = nextStandstill;
    }

    @Override
    public Hexagon getHexagon() {
        return this.getOrder().getShippingAddress().getHexagon();
    }

    public TimeRange getTimeRange() {
        return new TimeRange( this.start , this.end );
    }

    /*Helper Methods*/
    public long getRecursiveStart(){
        if( this.getStartOfTrip() != null ) return this.getStartOfTrip() + 5*60;
        if( this.start != null ) return this.start;
        this.start = this.getPreviousStandstill().getRecursiveEnd();
        return this.start;
    }

    public long getRecursiveArrivalTime(){
        if( this.arrivalTime != null ) return this.arrivalTime;
        this.arrivalTime = this.getRecursiveStart() + solution.getDistanceBetweenHexagons( this.getPreviousStandstill().getHexagon() , this.getHexagon() ).getDuration();
        return this.arrivalTime;
    }

    @Override
    public long getRecursiveEnd(){
        if( this.end != null ) return this.end;
        this.end = this.getRecursiveArrivalTime() + TripsOptimizer.standByDuration;
        return this.end;
    }

    public boolean isEndOfTrip(){
        return this.getNextStandstill() == null || ( ( OptimizingVisit ) this.getNextStandstill()).getStartOfTrip() != null;
    }

    public long endOfTrip(){
        return this.getRecursiveEnd() + TripsOptimizer.standByDuration + solution.getDistanceBetweenHexagons( this.getHexagon() , this.getOptimizingDriver().getHexagon() ).getDuration();
    }

    @Override
    public void cleanTimes() {
        this.start = null;
        this.arrivalTime = null;
        this.end = null;
    }

    public long overlapWith( OptimizingVisit optimizingVisit ){
        if( this.getRecursiveStart() > optimizingVisit.getRecursiveEnd() ) return 0;
        if( this.getRecursiveEnd() < optimizingVisit.getRecursiveStart() ) return 0;

        OptimizingVisit firstEvent;
        OptimizingVisit lastEvent;
        if( this.getRecursiveStart() < optimizingVisit.getRecursiveStart() ){
            firstEvent = this;
            lastEvent = optimizingVisit;
        }else{
            firstEvent = optimizingVisit;
            lastEvent = this;
        }

        if( lastEvent.getRecursiveEnd() < firstEvent.getRecursiveEnd() ) return lastEvent.getRecursiveEnd() - lastEvent.getRecursiveStart();

        return  firstEvent.getRecursiveEnd() - lastEvent.getRecursiveStart();
    }

    public long getTimePenalization(){
        if( this.order == null ) return 0;

        long estimatedArrivalTime = this.getRecursiveArrivalTime();

        TimeRange orderTimeRange = this.getOrder().getTimeRange();

        if( estimatedArrivalTime > orderTimeRange.getEnd() ){
            double secondsOfDifference = estimatedArrivalTime - orderTimeRange.getEnd();

            return (long) Math.pow( secondsOfDifference , this.getOrder().isExpress() ? 2 : 1.5 );
        }

        if( estimatedArrivalTime > orderTimeRange.getStart() ) return 0;

        return (long) Math.pow( orderTimeRange.getStart() - estimatedArrivalTime , 2 );
    }

    @Transient
    public double getCarryOnCash() {
        if( this.order == null ) return 0;
        double r = 0;
        if( this.order.isOnAdvanceMode() ){
            for ( TransactionMatrix tm : this.order.getTransactionMatrix() ) {
                if( !PaymentMethodType.CASH.equals( tm.getPaymentMethodType() ) ) continue;
                r += tm.getAdvance();
            }
        }else{
            for ( TransactionMatrix tm : this.order.getTransactionMatrix() ) {
                if( !PaymentMethodType.CASH.equals( tm.getPaymentMethodType() ) ) continue;
                r += tm.getAmount();
            }
        }

        return r;
    }

    public long getEarlyOrLateSeconds(){
        TimeRange orderTimeRange = this.getOrder().getTimeRange();
        long arrivalTime = this.getRecursiveArrivalTime();
        long r = 0;
        if( arrivalTime < orderTimeRange.getStart() ) r += orderTimeRange.getStart() - arrivalTime;
        if( arrivalTime > orderTimeRange.getEnd() ) r += arrivalTime - orderTimeRange.getEnd();
        return r;
    }

    public long getContinuousOptimizationScore( ContinuousBranchTripSolution solution ) {
        return 0;
        /*if( !( this.getPreviousStandstill() instanceof OptimizingTrip ) ){
            return this.getTimePenalization();
        }
        double r = 0;
        OptimizingTrip trip = (OptimizingTrip) this.getPreviousStandstill();

        for ( DriverShift shift : solution.getDriverShifts() ){
            if( this.getOptimizingDriver().getDriver().computedIdHashCode() != shift.getDriver().computedIdHashCode() ) continue;
            long seconds = Math.max( 0 , trip.getEnd() - shift.getAvailableUntilAsEpochSeconds() );
            r += Math.pow( seconds * 2 , 2 );
        }

        r += 0.25d * Math.max( this.getCarryOnCash() - this.getOptimizingDriver().getDriver().getTrustLevel() , 0 );

        if ( trip.getStart() > solution.getStart() ) {
            r -= 0.5d * ( trip.getEnd() - solution.getStart() );
        }

        r += this.getTimePenalization();

        return (long) r;*/
    }

    @Override
    public String toString() {
        return String.format("OptimizingVisit{  %s  ,  %s  ,  %s  ,  %s , %s min early , %s min late  }",
                this.order.getNumber(),
                this.getOrder().getLowerBoundDelivery(),
                this.getPreviousStandstill() == null ? "" : LocalDateTime.ofEpochSecond( this.getRecursiveArrivalTime() , 0 , BitemporalModel.ZONE_OFFSET ),
                this.getOrder().getUpperBoundDelivery(),
                this.getPreviousStandstill() == null ? "" : Math.max( 0 , ( this.getOrder().getTimeRange().getStart() - this.getRecursiveArrivalTime() ) ) / 60,
                this.getPreviousStandstill() == null ? "" : Math.max( 0 , ( this.getRecursiveArrivalTime() - this.getOrder().getTimeRange().getEnd() ) ) / 60
        );
    }
}

共有1个答案

范轶
2023-03-14

我们最近修复了1961年的计划,其症状与你们在这里的问题有显著的相似之处。请查看OptaPlanner 7.39.0.final(当它退出时)或更晚,您的问题可能会消失。

 类似资料:
  • 首先,感谢Angel Chang编写了像TokensRegex这样伟大的工具! 我的用例如下: 我的测试规则集中有两个提取规则。它们都有指定为结果的“action”字段,并且在action列表中都有“annotate”。当要匹配的第二个规则的表达式与第一个规则的结果无关时,它们就可以正常工作。但是当第二个规则的执行依赖于第一个规则的结果时,事情就崩溃了。 一个具体的例子: 我有以下句子:“共识估计

  • 在制定时间表时,你必须遵守法律(硬性要求),该法律规定,作为一名员工,你每七天只能工作一定的时间。它没有说日历周的原因是在创建时间表时为雇主提供一些回旋空间(通常是提前4-16周)。一旦设置了此计划期的开始日期,在您想要进行完整的重新规划之前,该日期无法更改,但您需要通知员工。所以它可以被视为一个变量,直到你接受了时间表,然后它就变成了一个常数。当然,这是每个员工的个人情况。该开始日期将由您的第一

  • 我是Optaplanner的新手,我一直在考虑将VRP扩展到一个不同的问题空间。我用的是6.1.0-final。这是一个很难计算的问题,但下面是: 规划实体-装运(扩展Standstill),由运输商(即货船)锚定规划变量-终端(货物目的地),当然隐含运输商(作为VRP示例中的Vehicle这样的影子变量) 在这个路径问题中,货物直接运输到一个终端,然后再运回工厂。即一个运输机服务于一个终点站,然

  • 问题内容: 在Linux上的“ C”上, 我需要静态库来静态链接,还是需要足够的共享库?如果没有,为什么不呢?(它们不包含相同的数据吗?) 问题答案: 是的,您需要静态库来构建静态链接的可执行文件。 静态库是编译对象的捆绑包。静态链接到库时,实际上与获取该库的编译结果,将它们解压缩到当前项目中以及将它们当作自己的对象使用一样。 动态库已链接。这意味着一些信息,例如重定位,已经被修复并丢弃。 此外,

  • Berat有资格获得驾驶执照! Berat不能买酒! Berat的有效年龄:真 虽然,我优先考虑每个规则与突出关键字,“可以由酒精”规则仍然被解雇。它不应该被激发,因为在第一个规则中执行setValid(true),而在第二个规则中执行isValid()==false控件应该返回false,因此应该遗漏部分。

  • 我们已经将我们的车辆路径问题建模为一个链式规划问题,即取货和送货问题。最重要的是,我们有一个定制的移动实现,它从车队中解开一对收货-送货对,并将其插入其他地方。 我们已经实现了一个早期退出:如果在链更新期间没有更新特定的阴影变量,我们将跳过链的其余部分。然而,只有当我们偶然地首先处理来自第一个实体的链时,这才起作用。 我们如何确保每次移动只更新链一次,从第一个更改的实体开始?