/** * Check if a {@linkplain TRSPTwoOptMove 2-opt move} is feasible for the given <code>tour</code> * * @param tour the base tour * @param move the {@link TRSPTwoOptMove} to be tested * @return <code>true</code> if applying <code>move</code> to the given <code>tour</code> will * result in a feasible tour */ @Override public boolean isTwoOptFeasible(ITRSPTour itour, TRSPTwoOptMove move) { if (!TRSPTour.class.isAssignableFrom(itour.getClass())) { TRSPLogging.getOptimizationLogger() .warn( String.format( "ToolsConstraint.isTwoOptFeasible: unsupported tour class (%s)", itour.getClass())); return true; } TRSPTour tour = (TRSPTour) itour; if (!tour.isMainDepotVisited() // The main depot is not visited, the tour is already feasible || tour.isMainDepotVisited( tour.getSucc(move.getFirst())) // The visit to the main depot is done before // the first // affected node || !tour.isMainDepotVisited(move.getSecond())) // The visit to the main depot is done // after the last affected node return true; // The main depot is visited between j and m TRSPTourIterator it = tour.iterator(move.getSecond()); // Start with node m and iterate backward int node = it.previous(); while (node != tour.getMainDepotId() && it.hasNext()) { if (!tour.getInstance().hasRequiredTools(tour.getTechnician().getID(), node)) // The technician does not have the required tools for this request return false; } // No incompatible request was found, the move is feasible return true; }
/** Initialize and setup the post-optimizer */ public void setupPostOp() { setPostOp( new SCGurobiSolver( getInstance(), getParams(), mTourPoolCB.getHasher(), !getALNSSol().getUnservedRequests().isEmpty())); getPostOp().addColumns(mTourPoolCB.getTourPool().getAllTours()); mTourPoolSize = getTourPool().size(); // Free some memory mTourPoolCB.getTourPool().clear(); // Set initial solution getPostOp().setIncumbent(getALNSSol()); if (getModelWriter() != null) try { getModelWriter() .write( getPostOp().getModel(), String.format("%s-%s", getInstance().getName(), getComment())); } catch (IOException e) { TRSPLogging.getOptimizationLogger() .exception(this.getClass().getSimpleName() + ".setupPostOp", e); } // Add stat writer if (getGRBStatCollector() != null) getGRBStatCollector().setModel(getPostOp().getModel()); }
/** * Check an insertion move feasibility * * @param tour * @param move * @return 1 if move is feasible, -1 if the technician does not have the required tools and no * visit to main depot is planned, -2 if the required tools are not available at this point */ private int checkInsFeasibility(ITRSPTour itour, InsertionMove move) { if (!TRSPTour.class.isAssignableFrom(itour.getClass())) { TRSPLogging.getOptimizationLogger() .warn( String.format( "ToolsConstraint.checkInsFeasibility: unsupported tour class (%s)", itour.getClass())); return 1; } TRSPTour tour = (TRSPTour) itour; InsertionMove m = move; if (m.isDepotTrip() || !tour.getInstance().isRequest(move.getNodeId())) return 1; if (!tour.isMainDepotVisited() && !m.isDepotTrip() && !tour.getInstance().hasRequiredTools(tour.getTechnician().getID(), m.getNodeId())) return -1; int pred = m.getInsertionPred(); if (pred == ITRSPTour.UNDEFINED) { if (tour.getInstance().hasRequiredTools(tour.getTechnician().getID(), m.getNodeId())) return 1; else return -2; } else { for (int tool : itour.getInstance().getRequest(m.getNodeId()).getToolSet()) if (!tour.isToolAvailable(pred, tool)) return -2; } return 1; }
public void setupALNS() { // ---------------------------------------- // Setup the ALNS IComponentHandler<IDestroy<TRSPSolution>> destroyComponents = null; IComponentHandler<IRepair<TRSPSolution>> repairComponents = null; getInitSol().setCostDelegate(getALNSCostDelegate(getInitSol())); Class<?> handlerClass = getParams().get(TRSPGlobalParameters.ALNS_COMP_HANDLER); if (handlerClass == ALNSComponentHandler.class) { destroyComponents = new ALNSComponentHandler<IDestroy<TRSPSolution>>( getParams().getALNSRndStream(), newDestroySet(getParams()), getParams().get(ALNS_A_SIGMA1), getParams().get(ALNS_A_SIGMA2), getParams().get(ALNS_A_SIGMA3), getParams().get(ALNS_A_R), getParams().get(ALNS_A_L)); repairComponents = new ALNSComponentHandler<IRepair<TRSPSolution>>( getParams().getALNSRndStream(), newRepairSet(getTourCtrHandler(), getParams()), getParams().get(ALNS_A_SIGMA1), getParams().get(ALNS_A_SIGMA2), getParams().get(ALNS_A_SIGMA3), getParams().get(ALNS_A_R), getParams().get(ALNS_A_L)); } else if (handlerClass == RndComponentHanlder.class) { destroyComponents = new RndComponentHanlder<IDestroy<TRSPSolution>>( getParams().getALNSRndStream(), newDestroySet(getParams()), false); repairComponents = new RndComponentHanlder<IRepair<TRSPSolution>>( getParams().getALNSRndStream(), newRepairSet(getTourCtrHandler(), getParams()), false); } else { TRSPLogging.getSetupLogger() .error( this.getClass().getSimpleName() + ".setupALNS: Unsupported ALNS component handler: %s", handlerClass); } getALNSGlobalParams() .set( ALNSGlobalParameters.DESTROY_SIZE_RANGE, new double[] { getParams().get(TRSPGlobalParameters.ALNS_XI_MIN), getParams().get(TRSPGlobalParameters.ALNS_XI_MAX) }); // Parallel ALNS if (getParams().get(TRSPGlobalParameters.ALNS_PARALLEL)) { // && getParams().get(TRSPGlobalParameters.THREAD_COUNT) > 1) { getALNSGlobalParams() .set( ALNSGlobalParameters.PALNS_IT_P, getParams().get(TRSPGlobalParameters.ALNS_PALNS_IT_P)); getALNSGlobalParams() .set( ALNSGlobalParameters.PALNS_POOL_SIZE, getParams().get(TRSPGlobalParameters.ALNS_PALNS_POOL_SIZE)); getALNSGlobalParams() .set( ALNSGlobalParameters.PALNS_POOL, getParams().get(TRSPGlobalParameters.ALNS_PALNS_POOL)); getALNSGlobalParams() .set( ALNSGlobalParameters.DIVERSITY_METRIC, getParams().get(TRSPGlobalParameters.ALNS_PALNS_DIV_METRIC)); getALNSGlobalParams() .set(ALNSGlobalParameters.PALNS_THREAD_COUNT, getParams().getThreadCount()); ParallelALNS<TRSPSolution> alns = new ParallelALNS<TRSPSolution>( OptimizationSense.MINIMIZATION, getParams().getALNSRndStream(), getALNSGlobalParams(), destroyComponents, repairComponents); setALNS(alns); // Setup the solution pool TRSPCostDelegate cd = getALNSCostDelegate(getInitSol()); for (TRSPSolution s : mInitPool) { // FIXME update mInitPool in dynamic setting? s.setCostDelegate(cd); alns.getSolPool().add(s, true); } } else { // Sequential ALNS setALNS( new AdaptiveLargeNeighborhoodSearch<TRSPSolution>( OptimizationSense.MINIMIZATION, getParams().getALNSRndStream(), getALNSGlobalParams(), destroyComponents, repairComponents)); } if (mLogger != null) mLogger.registerToALNS(getALNS()); // ---------------------------------------- mALNSParams = new SimpleParameters( LSStrategy.DET_BEST_IMPROVEMENT, getParams().get(ALNS_MAX_TIME), getParams().get(ALNS_MAX_IT), getParams().getALNSRndStream()); if (getParams().get(TRSPGlobalParameters.SC_ENABLED) || getParams().get(TRSPGlobalParameters.TOUR_POOL_ENABLED)) { mTourPoolCB = new TourPoolCallBack( getInstance(), getParams().get(ALNS_MAX_IT), new NodeSetSolutionHasher(getInstance(), getParams().getHashRndStream())); getALNS().registerCallback(mTourPoolCB, ALNSEventType.REPAIRED); } if (getParams().get(TRSPGlobalParameters.ALNS_ENABLE_LOGGING)) { ALNSLogger<TRSPSolution> logger = new ALNSSALogger<TRSPSolution>("results/alns"); logger.registerToALNS(getALNS()); } // Setup SA acceptance criterion mALNSParams.setAcceptanceCriterion( new SAAcceptanceCriterion( OptimizationSense.MINIMIZATION, getParams().getALNSRndStream(), getInitSol().getObjectiveValue(), getParams().get(ALNS_SA_W), getParams().get(ALNS_SA_P), mALNSParams.getMaxIterations(), getParams().get(ALNS_SA_ALPHA), true)); }
@Override public Object[] getStats(BestKnownSolutions bks, int runId, int runNum) { String group = getInstance().getName().substring(0, (getInstance().getName().contains("RC") ? 3 : 2)); String instanceName = getInstance().getName().replace(".txt", ""); TRSPCostDelegate wtDel; // sCVRPTW if (getParams().isCVRPTW()) wtDel = new TRSPDistance(); else wtDel = new TRSPWorkingTime(); TRSPTourBalance tbDel = new TRSPTourBalance( wtDel, getParams().get(TRSPGlobalParameters.BALANCE_COST_DELEGATE_MEASURE)); double init_wt = wtDel.evaluateSolution(getInitSol(), true, true); // double init_wt_dev = tbDel.evaluateSolution(getInitSol(), true, true); double alns_wt = wtDel.evaluateSolution(getALNSSol(), true, true); // double alns_wt_dev = tbDel.evaluateSolution(getALNSSol(), true, true); double postop_wt = wtDel.evaluateSolution(getFinalSolution(), true, true); // double postop_wt_dev = tbDel.evaluateSolution(getFinalSolution(), true, true); double postop_bbgap = Double.NaN; if (getPostOp() != null) { try { double mipObj = getPostOp().getModel().get(DoubleAttr.ObjVal); double mipLB = getPostOp().getModel().get(DoubleAttr.ObjBound); postop_bbgap = mipLB != 0 ? (mipObj - mipLB) / mipObj : mipObj / 100; } catch (GRBException e) { TRSPLogging.getProcedureLogger() .exception(this.getClass().getSimpleName() + ".collectStats", e); } } Object[] stats = new Object[] { runId, getInstance().getName(), // name group, // group getInstance().getRequestCount(), // size getInstance().getFleet().size(), // crew count runNum, getComment(), // comment mInitTimer.readTimeS(), // init_time getInit().getIterationCount(), // init_it // mInit.getAssignmentStrategy(),// init_strat getInit().getStatus(), // init_status getInitSol().getUnservedCount(), // init_unserved init_wt, // init_wt // init_wt_dev,// init_wt_dev getALNS().getTimer().readTimeS(), // alns_time getALNS().getStoppingCriterion().getIterationCount(), // alns_it getALNSSol().getUnservedCount(), // alns_unserved alns_wt, // alns_wt // alns_wt_dev,// alns_wt_dev getALNSSol().getActualTourCount(), // alns_K (init_wt - alns_wt) / init_wt, // wt imp // (init_wt_dev - alns_wt_dev) / init_wt_dev, // wt dev imp getChecker().checkSolution(getALNSSol()), // alns_feasible getPostOp() == null ? 0l : getPostOp().getTimer().readTimeS(), // postop_time getFinalSolution().getUnservedCount(), // postop_unserved postop_wt, // postop_wt // postop_wt_dev, // postop_wt_dev getFinalSolution().getActualTourCount(), // postop_K (alns_wt - postop_wt) / alns_wt, // postop_wt improvement // (alns_wt_dev - postop_wt_dev) / alns_wt_dev, // postop_wt_dev improvement getChecker().checkSolution(getFinalSolution()), // postop_checksol postop_bbgap, // postop_gap getPostOp() != null ? getPostOp().getStatus() : SolverStatus.UNKNOWN_STATUS, // postop_status getPostOp() != null ? getPostOp().getColumnCount() : 0, // postop_pool_size getHashPoolCollisionCount(), // postop_pool_collisions bks.getBKS(instanceName), // BKS bks.getGapToBKS(instanceName, alns_wt, OptimizationSense.MINIMIZATION), // ALNS GAP bks.getGapToBKS(instanceName, postop_wt, OptimizationSense.MINIMIZATION), // POSTOP GAP bks.getIntValue(instanceName, "K"), // BKS - K bks.isOptimal(instanceName) ? 1 : 0, // BKS is optimal? Utilities.toShortString(getParams().get(TRSPGlobalParameters.RUN_SEEDS)), // seeds getFinalSolution().toShortString() }; return stats; }
@Override public TRSPSolution call() { TRSPLogging.getProcedureLogger() .info( this.getClass().getSimpleName() + "[start ]: Solving instance %s (I=%s, SC=%s)", getInstance().getName(), getParams().get(TRSPGlobalParameters.ALNS_MAX_IT), getParams().get(TRSPGlobalParameters.SC_ENABLED)); if (getInitSol() == null) { initialization(); TRSPLogging.getProcedureLogger() .info( this.getClass().getSimpleName() + "[init ]: Initialization finished in %.1fs", mInitTimer.readTimeS()); TRSPLogging.getProcedureLogger() .info( this.getClass().getSimpleName() + "[init ]: Initial solution: %.3f (%s)", getInitSol().getObjectiveValue(), getInitSol().getUnservedCount()); String err = getChecker().checkSolution(getInitSol()); if (!err.isEmpty()) { TRSPLogging.getProcedureLogger() .warn(this.getClass().getSimpleName() + "[init ]: Infeasibility: %s", err); } } setupALNS(); if (ParallelALNS.class.isAssignableFrom(getALNS().getClass())) TRSPLogging.getProcedureLogger() .info( this.getClass().getSimpleName() + "[init ]: Penalty: %.3f - Initial pool: %s", getInitSol().getCostDelegate().getUnservedPenalty(), ((ParallelALNS<?>) getALNS()).getSolPool()); alns(); TRSPLogging.getProcedureLogger() .info( this.getClass().getSimpleName() + "[alns ]: ALNS finished in %.1fs %s", getALNS().getTimer().readTimeS(), getALNS().getStoppingCriterion()); if (ParallelALNS.class.isAssignableFrom(getALNS().getClass())) TRSPLogging.getProcedureLogger() .info( this.getClass().getSimpleName() + "[alns ]: Final pool: %s", ((ParallelALNS<?>) getALNS()).getSolPool()); if (getALNSSol() != null) TRSPLogging.getProcedureLogger() .info( this.getClass().getSimpleName() + "[alns ]: ALNS solution : %.3f (%s)", getALNSSol().getObjectiveValue(), getALNSSol().getUnservedCount()); String err = getChecker().checkSolution(getALNSSol()); if (!err.isEmpty()) { TRSPLogging.getProcedureLogger() .warn(this.getClass().getSimpleName() + "[alns ]: Infeasibility: %s", err); } if (getParams().get(TRSPGlobalParameters.SC_ENABLED) && getALNSSol() != null) { TRSPLogging.getProcedureLogger() .info(this.getClass().getSimpleName() + "[postop]: Post-optimization"); setupPostOp(); postOp(); TRSPLogging.getProcedureLogger() .info( this.getClass().getSimpleName() + "[postop]: Post-optimization finished in %.1fs", getPostOp().getTimer().readTimeS()); err = getChecker().checkSolution(getFinalSolution()); if (!err.isEmpty()) { TRSPLogging.getProcedureLogger() .warn(this.getClass().getSimpleName() + "[end ]: Infeasibility: %s", err); } } else { setFinalSolution(getALNSSol()); } TRSPLogging.getProcedureLogger() .info( this.getClass().getSimpleName() + "[end ]: Final solution : %.3f (%s)", getFinalSolution() != null ? getFinalSolution().getObjectiveValue() : Double.NaN, getFinalSolution() != null ? getFinalSolution().getUnservedCount() : "na"); return getFinalSolution(); }
/** * Check if a {@linkplain TRSPShiftMove shift move} is feasible for the given <code>solution * </code> * * @param solution the base solution * @param move the {@link TRSPShiftMove} to be tested * @return <code>true</code> if applying <code>move</code> to the given <code>solution</code> will * result in a feasible mSolution */ @Override public boolean isShiftFeasible(ITRSPTour itour, TRSPShiftMove move) { if (!TRSPTour.class.isAssignableFrom(itour.getClass())) { TRSPLogging.getOptimizationLogger() .warn( String.format( "ToolsConstraint.isShiftFeasible: unsupported tour class (%s)", itour.getClass())); return true; } TRSPTour tour = (TRSPTour) itour; if (!tour.isMainDepotVisited()) { // The main depot is not visited, we assume that the tour is and will remain feasible return true; } else { // The main depot is visited if (move.getNode() == tour.getMainDepotId()) { // The shifted node is the main depot if (!move.isForward()) { // A backward move of the main depot is always feasible return true; } else { // Check if the requests between the current depot position and its new one require a // visit to // the depot to be served List<Integer> seq = move.getChangedSequence(); for (int s : seq) { if (s == tour.getMainDepotId()) // The requests after the main depot will be feasible return true; else if (!tour.getInstance().hasRequiredTools(tour.getTechnician().getID(), s)) // This request will be before the depot visit but the technician does not have the // required // skills return false; } throw new IllegalStateException( "Illegal state when checking the feasibility of move " + move); } } else { if (tour.getInstance().hasRequiredTools(tour.getTechnician().getID(), move.getNode())) // The technician already has the tools to serve the shifted node, the move is feasible in // all cases return true; int pred = move.getNewSucc() != ITRSPTour.UNDEFINED ? tour.getPred(move.getNewSucc()) : tour.getLastNode(); if (tour.isMainDepotVisited(move.getNode()) == tour.isMainDepotVisited(pred)) { // Both the shifted node and its new predecessor are on the same "side" of the tour // (either before // or after the depot visit), the move is therefore feasible return true; } else { // The shifted node and its new predecessor are on different "side" of the tour (one is // before // while the other is after the depot visit) if (move.isForward()) { // The node will be after the depot visit, the move is therefore feasible return true; } else { // The node is shifted before the depot visit, while the technician does not have the // required // tools (tested before) return false; } } } } }