From bc4f5e1c050c4aa2cdf34448c61f9222a05eefa4 Mon Sep 17 00:00:00 2001
From: Romain BERNARD <romain.bernard@uca.fr>
Date: Wed, 18 Sep 2024 18:17:48 +0200
Subject: [PATCH] update PT Line parser format to control travel time +
 implement fixed schedule parsing and add parser docs

---
 src/instance/graph/Graph.cpp | 63 +++++++++++++++++++++++++++++-------
 src/instance/graph/Graph.h   | 43 ++++++++++++++++++++----
 2 files changed, 89 insertions(+), 17 deletions(-)

diff --git a/src/instance/graph/Graph.cpp b/src/instance/graph/Graph.cpp
index 6596630..9d6b9ec 100644
--- a/src/instance/graph/Graph.cpp
+++ b/src/instance/graph/Graph.cpp
@@ -108,11 +108,16 @@ Graph::Graph(const std::string& datFilePath) {
 
     //-- Read Public transit line
     std::cout << currentRow.toString() << std::endl;
-    std::uniform_int_distribution<uint32_t> uint_dist10(1,5);
-    std::uniform_int_distribution<uint32_t> uint_dist60(1,10);
-    while(infile >> currentRow && !currentRow[0].starts_with('#')) {
-        this->parseLineRandomizedSchedule(currentRow, rng, uint_dist10, uint_dist60);
+    if(currentRow[0].starts_with("#PT Line fixed")) {
+        while(infile >> currentRow && !currentRow[0].starts_with('#')) {
+            this->parseLineFixedSchedule(infile, currentRow);
+        }
+    } else {
+        while(infile >> currentRow && !currentRow[0].starts_with('#')) {
+            this->parseLineRandomizedSchedule(currentRow, rng);
+        }
     }
+
     //Make links once all lines are created to prevent stale refs
     this->linkAllPTNodes();
 }
@@ -249,11 +254,9 @@ void Graph::parseEdgeRow(const DATRow& row)
     }
 }
 
-void Graph::parseLineRandomizedSchedule(const DATRow& row, std::mt19937 rng,
-                                        std::uniform_int_distribution<uint32_t> travelTimeDistribution,
-                                        std::uniform_int_distribution<uint32_t> startTimeDistribution)
+void Graph::parseLineRandomizedSchedule(const DATRow& row, std::mt19937 rng)
 {
-    int startTime, endTime, frequency, currentNodeIdx;
+    int startTime, endTime, frequency, minTravelTime, maxTravelTime, currentNodeIdx;
     //add nodes for the line
     Line newLine = Line();
     //Give it an ID
@@ -265,7 +268,11 @@ void Graph::parseLineRandomizedSchedule(const DATRow& row, std::mt19937 rng,
     std::from_chars(row[0].data(), row[0].data() + row[0].size(), frequency);
     std::from_chars(row[1].data(), row[1].data() + row[1].size(), startTime);
     std::from_chars(row[2].data(), row[2].data() + row[2].size(), endTime);
-    int currentTime = startTime + startTimeDistribution(rng); //random startTime time with 60min max offset
+    std::from_chars(row[3].data(), row[3].data() + row[3].size(), minTravelTime);
+    std::from_chars(row[4].data(), row[4].data() + row[4].size(), maxTravelTime);
+    std::uniform_int_distribution<uint32_t> travelTimeDistribution(minTravelTime,maxTravelTime);
+
+    int currentTime = startTime;
     while(currentTime + frequency < endTime)
     {
         timeTable.push_back(currentTime + frequency);
@@ -282,7 +289,7 @@ void Graph::parseLineRandomizedSchedule(const DATRow& row, std::mt19937 rng,
     //Create subsequent timetables according to preceding timetable and travel time
     for(size_t i = 1; i < newLine.getNodes().size(); ++i)
     {
-        int travelTime = travelTimeDistribution(rng); //FIXME travel time is randomized for now, we should get edge length if it exists I guess
+        uint travelTime = travelTimeDistribution(rng);
         std::vector<int> precedingTimeTable = newLine.getTimetable(i - 1);
         std::vector<int> newTimetable;
         newTimetable.reserve(precedingTimeTable.size()); //Reserve to improve foreach efficiency
@@ -292,9 +299,43 @@ void Graph::parseLineRandomizedSchedule(const DATRow& row, std::mt19937 rng,
         newLine.addTimetable(newTimetable);
         newTimetable.clear();
     }
+    //Check for errors
+    if(newLine.check()) {
+        this->addLine(newLine);
+        DEBUG_MSG("Created new line with randomized schedule");
+    }
+}
+
+void Graph::parseLineFixedSchedule(std::ifstream& infile, DATRow& row) {
+    Line newLine = Line();
+
+    // First line consists of the list of node indexes, add them all to the new line
+    uint currentNodeIdx;
+    for(uint i = 0; i < row.size(); ++i)
+    {
+        std::from_chars(row[i].data(), row[i].data() + row[i].size(), currentNodeIdx);
+        newLine.addNode(currentNodeIdx);
+    }
+
+    // Read schedule until hitting an empty line, used to separate individual lines to parse
+    std::vector<std::vector<int>> newSchedules;
+    while(infile >> row && !row[0].empty()) {
+        assertm(row.size() == newLine.size(), "Schedule size doesn't correspond to the number of nodes");
+        // Add the time to the appropriate station's schedule vector
+        for(uint stationIdx = 0; stationIdx < row.size(); ++stationIdx) {
+            newSchedules[stationIdx].emplace_back(std::stoi(row[stationIdx].data()));
+        }
+    }
+
+    //Add properly formatted timetables to the new line in station order
+    for(const std::vector<int>& stationSchedule : newSchedules) {
+        newLine.addTimetable(stationSchedule);
+    }
+
+    //Check for errors
     if(newLine.check()) {
         this->addLine(newLine);
-        DEBUG_MSG("Created new line with nodes");
+        DEBUG_MSG("Created new line with fixed schedule");
     }
 }
 
diff --git a/src/instance/graph/Graph.h b/src/instance/graph/Graph.h
index 050ee93..f1baeea 100644
--- a/src/instance/graph/Graph.h
+++ b/src/instance/graph/Graph.h
@@ -13,10 +13,11 @@
 
 #include "Node.h"
 #include "Edge.h"
-#include "../../ShortestPath/Vehicle/VehicleShortestPathCalculation.h"
+#include "../../utils/Globals.h"
 #include "../../utils/Constants.h"
-#include "../../ShortestPath/Transit/TransitShortestPathContainer.h"
-#include "../../ShortestPath/Transit/TransitShortestPathPrecompute.h"
+#include "../../algorithm/ShortestPath/Vehicle/VehicleShortestPathCalculation.h"
+#include "../../algorithm/ShortestPath/Transit/TransitShortestPathContainer.h"
+#include "../../algorithm/ShortestPath/Transit/TransitShortestPathPrecompute.h"
 
 class DATRow;
 class Graph {
@@ -34,11 +35,41 @@ private:
      * @return True if @this is properly referenced in the Line object at the expected index
      */
     bool checkLineToNodeLinks();
+    /**
+     * Parses and adds a node from the given row object to the graph.<br>
+     * Format : status,x_coordinate,y_coordinate
+     * @param row row extracted from the input file
+     */
     void parseNodeRow(const DATRow& row);
+    /**
+     * Parses and adds an edge from the given row object to the graph.<br>
+     * Format : start_node_idx,end_node_idx,length
+     * @param row row extracted from the input file
+     */
     void parseEdgeRow(const DATRow& row);
-    void parseLineRandomizedSchedule(const DATRow &row, std::mt19937 rng,
-                                     std::uniform_int_distribution<uint32_t> travelTimeDistribution,
-                                     std::uniform_int_distribution<uint32_t> startTimeDistribution);
+
+    /**
+     * Parses and adds a line to the graph from a given row object and reusing the given seeded rng engine.
+     * Uses the format : <br>
+     * frequency,start_time,end_time,min_travel_time,max_travel_time,node_0, ..., node_n
+     * @param row row containing the necessary info to generate a random line
+     * @param rng seeded random number generator
+     */
+    void parseLineRandomizedSchedule(const DATRow &row, std::mt19937 rng);
+
+    /**
+     * Parses and adds a line to the graph from the given row in given the following format : <br>
+     * node_0_idx, ... node_n_idx <br>
+     * node_0_pass_0_time, ... node_n_pass_0_time <br>
+     * ... <br>
+     * node_0_pass_m_time, ... node_n_pass_m_time <br>
+     * <br>
+     * /!\ each transit line to parse ends with an empty line
+     *
+     * @param infile input file stream, necessary to iterate over multiple lines in the function
+     * @param row the current DATrow object we reuse from other parsing functions
+     */
+    void parseLineFixedSchedule(std::ifstream& infile, DATRow& row);
 
 public:
     [[nodiscard]] const std::vector<Node> &getNodesVector() const {
-- 
GitLab