LCOV - code coverage report
Current view: top level - src/microsim - MSStopOptimizer.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 98.2 % 109 107
Test Date: 2025-12-06 15:35:27 Functions: 100.0 % 3 3

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    MSStopOptimizer.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @date    Nov 2025
      17              : ///
      18              : // Optimizes prioritized stops
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : #include <queue>
      22              : #include "MSLane.h"
      23              : #include "MSEdge.h"
      24              : #include "MSStoppingPlace.h"
      25              : #include "MSStopOptimizer.h"
      26              : 
      27              : //#define DEBUG_OPTIMIZE_SKIPPED
      28              : 
      29              : // ===========================================================================
      30              : // method definitions
      31              : // ===========================================================================
      32              : 
      33              : ConstMSEdgeVector
      34           45 : MSStopOptimizer::optimizeSkipped(const MSEdge* source, double sourcePos, std::vector<StopEdgeInfo>& stops, ConstMSEdgeVector edges) const {
      35              :     // intial solution is an upper bound on the acceptle total skipped priority
      36              :     double minSkipped = 0;
      37              :     double totalPrio = 0;
      38           45 :     std::vector<StopEdgeInfo> bestStops = stops;
      39              :     std::shared_ptr<StopPathNode> bestNode = nullptr;
      40           45 :     double bestCost = myRouter.recomputeCostsPos(edges, myVehicle, sourcePos, 0, myT);
      41              :     std::vector<int> skipped;
      42              : 
      43              :     // each upcoming stop is part of a graph where we can start to search for a
      44              :     // better solution. We evaluate followers lazily and prune based on the best
      45              :     // solution already found
      46              :     std::priority_queue<std::shared_ptr<StopPathNode>, std::vector<std::shared_ptr<StopPathNode> >, spnCompare> queue;
      47              : 
      48           90 :     auto prev = std::make_shared<StopPathNode>(*this, StopEdgeInfo(source, -1, SIMSTEP, sourcePos));
      49           45 :     prev->stopIndex = -1;
      50           45 :     prev->routeIndex = 0;
      51           45 :     prev->cost = bestCost;
      52           45 :     prev->checked = true;
      53              :     //std::cout << " i=-1 e=" << prev->edge->getID() << " sp=" << prev->skippedPrio << " rp=" << prev->reachedPrio << "\n";
      54           45 :     queue.push(prev);
      55              : 
      56          240 :     for (int i = 0; i < (int)stops.size(); i++) {
      57          195 :         const StopEdgeInfo& stop = stops[i];
      58          195 :         if (stop.skipped) {
      59           52 :             skipped.push_back(i);
      60           52 :             minSkipped += stops[i].priority;
      61              :         } else {
      62              :             auto ptr = std::make_shared<StopPathNode>(*this, stop);
      63          143 :             ptr->skippedPrio = minSkipped;
      64          143 :             ptr->reachedPrio = prev->reachedPrio;
      65          143 :             ptr->reachedMandatory = prev->reachedMandatory;
      66          143 :             if (stop.priority >= 0) {
      67          122 :                 ptr->reachedPrio += stop.priority;
      68              :             } else {
      69           21 :                 ptr->reachedMandatory += 1;
      70              :             }
      71          143 :             ptr->checked = true;
      72              :             ptr->prev = prev;
      73          143 :             ptr->stopIndex = i;
      74          143 :             ConstMSEdgeVector subEdges(edges.begin(), edges.begin() + stop.routeIndex);
      75          143 :             ptr->cost = myRouter.recomputeCostsPos(subEdges, myVehicle, sourcePos, 0, myT);
      76          143 :             ptr->edges.insert(ptr->edges.begin(), edges.begin() + prev->routeIndex, edges.begin() + stop.routeIndex + 1);
      77              :             prev = ptr;
      78              :             bestNode = ptr;
      79          143 :             queue.push(ptr);
      80          143 :         }
      81          369 :         totalPrio += MAX2(0.0, stop.priority);
      82              :         //std::cout << " i=" << i << " e=" << ptr->edge->getID() << " sp=" << ptr->skippedPrio << " rp=" << ptr->reachedPrio << "\n";
      83              :     }
      84              : #ifdef DEBUG_OPTIMIZE_SKIPPED
      85              :     std::cout << SIMTIME << " optimizeSkipped veh=" << myVehicle->getID()
      86              :         << " source=" << source->getID() << " sourcePos=" << sourcePos
      87              :         << " nStops=" << stops.size() << " skipped=" << toString(skipped) << " qSize=" << queue.size()
      88              :         << " minSkipped=" << minSkipped << " totalPrio=" << totalPrio << "\n";
      89              :     for (const StopEdgeInfo& stop : stops) {
      90              :         std::cout << "   edge=" << stop.edge->getID() << " name=" << stop.nameTag.first << " wasSkipped=" << stop.skipped << " prio=" << stop.priority << "\n";
      91              :     }
      92              :     std::cout << " bestNode edge=" << bestNode->edge->getID() << " rPrio=" << bestNode->reachedPrio << " cost=" << bestNode->cost << "\n";
      93              : #endif
      94              : 
      95              :     std::map<const MSEdge*, std::shared_ptr<StopPathNode> > bestIntermediate;
      96         2527 :     while (!queue.empty()) {
      97              :         auto ptr = queue.top();
      98         2482 :         queue.pop();
      99              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     100              :         std::cout << "  qSize=" << queue.size() << " topNode edge=" << ptr->edge->getID()
     101              :             << " name='" << ptr->nameTag.first << "' skippedPrio=" << ptr->skippedPrio
     102              :             << " trackChanges=" << ptr->trackChanges
     103              :             << " cost=" << ptr->cost
     104              :             << " reachedPrio=" << ptr->reachedPrio
     105              :             << " mandatory=" << ptr->reachedMandatory
     106              :             << " stopIndex=" << ptr->stopIndex << "\n";
     107              : #endif
     108         2482 :         auto succ = ptr->getSuccessor(stops, minSkipped);
     109              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     110              :         std::cout << "      cost2=" << ptr->cost << " reachedPrio2=" << ptr->reachedPrio << " mandatory2=" << ptr->reachedMandatory << " succ=" << (succ == nullptr ? "NULL" : succ->edge->getID()) << "\n";
     111              : #endif
     112         2482 :         if (*bestNode < *ptr) {
     113           37 :             minSkipped = MIN2(minSkipped, totalPrio - ptr->reachedPrio);
     114              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     115              :             std::cout << "   newBestNode edge=" << bestNode->edge->getID() << " minSkipped=" << minSkipped
     116              :                         << " orm=" << bestNode->reachedMandatory << " rm=" << ptr->reachedMandatory
     117              :                         << " orp=" << bestNode->reachedPrio << " rp=" << ptr->reachedPrio
     118              :                         << " otc=" << bestNode->trackChanges << " tc=" << ptr->trackChanges
     119              :                         << " oc=" << bestNode->cost << " c=" << ptr->cost
     120              :                         << "\n";
     121              : #endif
     122              :             bestNode = ptr;
     123              :         }
     124         2482 :         if (succ != nullptr) {
     125         2035 :             if (minSkipped == 0 && ptr->trackChanges > bestNode->trackChanges) {
     126              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     127              :                 std::cout << "pruned by minTC=" << bestNode->trackChanges << "\n";
     128              : #endif
     129          728 :                 continue;
     130              :             }
     131              :             auto it = bestIntermediate.find(ptr->edge);
     132         1307 :             if (it == bestIntermediate.end()) {
     133          246 :                 bestIntermediate[ptr->edge] = ptr;
     134              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     135              :                 std::cout << "firstBest " << ptr->edge->getID() << "\n";
     136              : #endif
     137              :             } else {
     138              :                 auto best = it->second;
     139              :                 assert(best->checked);
     140              :                 assert(ptr->checked);
     141         1061 :                 if (best == ptr) {
     142              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     143              :                     std::cout << "alreadyBest " << ptr->edge->getID() << " rp=" << ptr->reachedPrio << " tc=" << ptr->trackChanges << " c=" << ptr->cost << "\n";
     144              : #endif
     145          209 :                 } else if (!(*best < *ptr)) {
     146              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     147              :                     std::cout << "skip " << ptr->edge->getID()
     148              :                         << " orp=" << best->reachedPrio << " rp=" << ptr->reachedPrio
     149              :                         << " otc=" << best->trackChanges << " tc=" << ptr->trackChanges
     150              :                         << " oc=" << best->cost << " c=" << ptr->cost
     151              :                         << "\n";
     152              : #endif
     153              :                     continue;
     154              :                 } else {
     155           49 :                     bestIntermediate[ptr->edge] = ptr;
     156              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     157              :                     std::cout << "newBest " << ptr->edge->getID()
     158              :                         << " orp=" << best->reachedPrio << " rp=" << ptr->reachedPrio
     159              :                         << " otc=" << best->trackChanges << " tc=" << ptr->trackChanges
     160              :                         << " oc=" << best->cost << " c=" << ptr->cost
     161              :                         << "\n";
     162              : #endif
     163              :                 }
     164              :             }
     165         1147 :             queue.push(ptr);
     166         1147 :             queue.push(succ);
     167              :         }
     168              :     }
     169          240 :     for (auto& stop : stops) {
     170              :         // init true and later set false for all used stops
     171          195 :         if (!stop.skipped) {
     172          143 :             stop.backtracked = true;
     173          143 :             stop.skipped = true;
     174              :         }
     175              :     }
     176              :     ConstMSEdgeVector bestEdges;
     177          222 :     while (bestNode != nullptr && bestNode->stopIndex >= 0) {
     178              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     179              :         std::cout << "  revBestNode index=" << bestNode->stopIndex << " edge=" << bestNode->edge->getID() << " name=" << bestNode->nameTag.first << " tc=" << bestNode->trackChanges << " c=" << bestNode->cost  << "\n";
     180              : #endif
     181          177 :         stops[bestNode->stopIndex].skipped = false;
     182          177 :         stops[bestNode->stopIndex].backtracked = false;
     183          177 :         stops[bestNode->stopIndex].origEdge = bestNode->origEdge;
     184          177 :         if (!bestEdges.empty() && !bestNode->edges.empty()) {
     185              :             bestNode->edges.pop_back();
     186              :         }
     187          177 :         bestEdges.insert(bestEdges.begin(), bestNode->edges.begin(), bestNode->edges.end());
     188              :         bestNode = bestNode->prev;
     189              :     }
     190              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     191              :     std::cout << "oldEdges=" << toString(edges) << "\nnewEdges=" << toString(bestEdges) << "\n";
     192              : #endif
     193           45 :     return bestEdges;
     194           90 : }
     195              : 
     196              : 
     197              : std::shared_ptr<MSStopOptimizer::StopPathNode>
     198         2482 : MSStopOptimizer::StopPathNode::getSuccessor(const std::vector<StopEdgeInfo>& stops, double minSkipped) {
     199         2482 :     if (!checked) {
     200         1147 :         checked = true;
     201              :         // @todo caching, bulk-routing
     202         1147 :         double newCost = 0;
     203         1147 :         if (!so.reachableInTime(prev->edge, prev->pos, edge, pos, arrival, edges, newCost)) {
     204              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     205              :             std::cout << "    prevIndex=" << prev->stopIndex << " prevEdge=" << prev->edge->getID() << " i=" << stopIndex << " edge=" << edge->getID() << " unreachable\n";
     206              : #endif
     207              :             // indicate failure
     208           76 :             reachedMandatory = -1;
     209           76 :             return nullptr;
     210              :         } else {
     211              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     212              :             std::cout << "    prevIndex=" << prev->stopIndex << " prevEdge=" << prev->edge->getID() << " i=" << stopIndex << " edge=" << edge->getID() << " reached with edges=" << toString(edges) << " cost=" << cost << " newCost=" << newCost << "\n";
     213              : #endif
     214              :         }
     215         1071 :         reachedPrio += MAX2(0.0, priority);
     216         1071 :         cost += newCost;
     217         1071 :         if (priority < 0) {
     218           28 :             reachedMandatory++;
     219              :         }
     220              :     }
     221         2406 :     int nextIndex = stopIndex + numSkipped + 1;
     222         2412 :     while (nextIndex < (int)stops.size()) {
     223         2336 :         const StopEdgeInfo& next = stops[nextIndex];
     224              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     225              :         std::cout << "    i=" << stopIndex << " next=" << nextIndex << " numSkipped=" << numSkipped << " altIndex=" << altIndex << "\n";
     226              : #endif
     227              :         // always try the default track first
     228         2336 :         if (altIndex == -1) {
     229         1189 :             altIndex++;
     230         1189 :             auto succ = std::make_shared<StopPathNode>(so, next);
     231         1189 :             succ->skippedPrio = skippedPrio;
     232         1189 :             succ->reachedPrio = reachedPrio;
     233         1189 :             succ->reachedMandatory = reachedMandatory;
     234         1189 :             succ->trackChanges = trackChanges;
     235         1189 :             succ->cost = cost;
     236            0 :             succ->prev = shared_from_this();
     237         1189 :             succ->stopIndex = nextIndex;
     238              :             return succ;
     239              :         }
     240         1147 :         const std::vector<MSStoppingPlace*>& alternatives = MSNet::getInstance()->getStoppingPlaceAlternatives(next.nameTag.first, next.nameTag.second);
     241         1147 :         if (next.priority < 0) {
     242              :             // next stop can neither be skipped nor changed
     243              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     244              :             std::cout << "    i=" << stopIndex << " next=" << nextIndex << " isMandatory\n";
     245              : #endif
     246              :             return nullptr;
     247              :         }
     248         1446 :         while (altIndex < (int)alternatives.size()) {
     249         1173 :             MSStoppingPlace* alt = alternatives[altIndex];
     250         1173 :             const MSEdge* altEdge = &alt->getLane().getEdge();
     251         1173 :             altIndex++;
     252         1173 :             if (altEdge == next.edge || !alt->getLane().allowsVehicleClass(so.myVehicle->getVClass())) {
     253          327 :                 continue;
     254              :             }
     255          846 :             auto succ = std::make_shared<StopPathNode>(so, next);
     256          846 :             succ->trackChanges = trackChanges;
     257          846 :             succ->pos = alt->getEndLanePosition();
     258          846 :             succ->skippedPrio = skippedPrio;
     259          846 :             succ->reachedPrio = reachedPrio;
     260          846 :             succ->reachedMandatory = reachedMandatory;
     261          846 :             succ->cost = cost;
     262            0 :             succ->prev = shared_from_this();
     263          846 :             succ->stopIndex = nextIndex;
     264          846 :             succ->origEdge = succ->edge;
     265          846 :             succ->edge = altEdge;
     266          846 :             succ->trackChanges += 1;
     267              :             return succ;
     268              :         }
     269          273 :         skippedPrio =+ next.priority;
     270          273 :         if (skippedPrio >= minSkipped) {
     271              :             // cannot improve on current best solution
     272              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     273              :             std::cout << "    i=" << stopIndex << " next=" << nextIndex << " skippedPrio=" << skippedPrio << " minSkipped=" << minSkipped << " pruned\n";
     274              : #endif
     275              :             return nullptr;
     276              :         }
     277            6 :         nextIndex++;
     278            6 :         altIndex = -1;
     279            6 :         numSkipped++;
     280              :     }
     281              : #ifdef DEBUG_OPTIMIZE_SKIPPED
     282              :     std::cout << "    i=" << stopIndex << " noSuccessors\n";
     283              : #endif
     284              :     return nullptr;
     285              : }
     286              : 
     287              : 
     288              : bool
     289         1147 : MSStopOptimizer::reachableInTime(const MSEdge* from, double fromPos,
     290              :         const MSEdge* to, double toPos,
     291              :         SUMOTime arrival,
     292              :         ConstMSEdgeVector& into, double &cost) const {
     293         1147 :     myRouter.compute(from, fromPos, to, toPos, myVehicle, myT, into, true);
     294         1147 :     if (into.size() > 0) {
     295         1080 :         cost = myRouter.recomputeCostsPos(into, myVehicle, fromPos, toPos, myT);
     296              :         //std::cout << " from=" << from->getID() << " fromPos=" << fromPos << " to=" << to->getID() << " toPos=" << toPos << " t=" << myT << " cost=" << cost << " arrival=" << arrival << " maxDelay=" << myMaxDelay << "\n";
     297         1080 :         if (myT + TIME2STEPS(cost) <= arrival + myMaxDelay) {
     298              :             return true;
     299              :         }
     300              :     }
     301              :     return false;
     302              : }
     303              : 
     304              : 
     305              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1