From f9d21d234598b35da02236ef6b10949c5d1078c3 Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Sat, 16 Mar 2024 17:24:34 +0100 Subject: [PATCH 01/33] Add `SpireStreet` class --- src/dsm/headers/Street.hpp | 123 +++++++++++++++++++++++++++++++++++-- test/Test_street.cpp | 64 +++++++++++++++++++ 2 files changed, 181 insertions(+), 6 deletions(-) diff --git a/src/dsm/headers/Street.hpp b/src/dsm/headers/Street.hpp index 88b0e8f5..37d24009 100644 --- a/src/dsm/headers/Street.hpp +++ b/src/dsm/headers/Street.hpp @@ -40,7 +40,6 @@ namespace dsm { Size m_capacity; Size m_transportCapacity; bool m_isSpire; - public: Street() = delete; /// @brief Construct a new Street object starting from an existing street @@ -70,6 +69,8 @@ namespace dsm { /// @param nodePair The street's node pair Street(Id id, Size capacity, double len, double maxSpeed, std::pair nodePair); + virtual ~Street() = default; + /// @brief Set the street's id /// @param id The street's id void setId(Id id); @@ -148,9 +149,10 @@ namespace dsm { double angle() const; /// @brief Add an agent to the street's queue /// @param agentId The id of the agent to add to the street's queue - void enqueue(Id agentId); + /// @throw std::runtime_error If the street's queue is full + virtual void enqueue(Id agentId); /// @brief Remove an agent from the street's queue - std::optional dequeue(); + virtual std::optional dequeue(); /// @brief Check if the street is a spire /// @return bool True if the street is a spire, false otherwise bool isSpire() const; @@ -334,9 +336,7 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral) void Street::enqueue(Id agentId) { if (m_queue.size() == m_capacity) { - std::string errorMsg{"Error at line " + std::to_string(__LINE__) + " in file " + - __FILE__ + ": " + "The street's queue is full."}; - throw std::runtime_error(errorMsg); + throw std::runtime_error(buildLog("The street's queue is full.")); } for (auto const& id : m_queue) { if (id == agentId) { @@ -361,6 +361,117 @@ namespace dsm { return m_isSpire; } + /// @brief The SpireStreet class represents a street which is able to count agent flows in both input and output. + /// @tparam Id The type of the street's id + /// @tparam Size The type of the street's capacity + template + requires(std::unsigned_integral && std::unsigned_integral) + class SpireStreet : public Street { + private: + Size m_agentCounterIn; + Size m_agentCounterOut; + public: + SpireStreet() = delete; + /// @brief Construct a new SpireStreet object starting from an existing street + /// @param id The street's id + /// @param street The existing street + SpireStreet(Id id, const Street& street); + /// @brief Construct a new SpireStreet object + /// @param id The street's id + /// @param capacity The street's capacity + /// @param len The street's length + /// @param nodePair The street's node pair + SpireStreet(Id id, Size capacity, double len, std::pair nodePair); + /// @brief Construct a new SpireStreet object + /// @param id The street's id + /// @param capacity The street's capacity + /// @param len The street's length + /// @param maxSpeed The street's speed limit + /// @param nodePair The street's node pair + SpireStreet(Id id, Size capacity, double len, double maxSpeed, std::pair nodePair); + ~SpireStreet() = default; + + /// @brief Add an agent to the street's queue + /// @param agentId The id of the agent to add to the street's queue + /// @throw std::runtime_error If the street's queue is full + void enqueue(Id agentId) override; + + /// @brief Get the input flow of the street + /// @return Size The input flow of the street + /// @details Once the input flow is retrieved, it is reset to 0 together with the output flow. + Size inputFlow(); + /// @brief Get the output flow of the street + /// @return Size The output flow of the street + /// @details Once the output flow is retrieved, it is reset to 0 together with the input flow. + Size outputFlow(); + /// @brief Get the mean flow of the street + /// @return int The flow of the street, i.e. the difference between input and output flows + /// @details Once the flow is retrieved, bothh the input and output flows are reset to 0. + /// Notice that this flow is positive iff the input flow is greater than the output flow. + int meanFlow(); + /// @brief Remove an agent from the street's queue + /// @return std::optional The id of the agent removed from the street's queue + std::optional dequeue() override; + }; + + template + requires(std::unsigned_integral && std::unsigned_integral) + SpireStreet::SpireStreet(Id id, const Street& street) + : Street(id, street), m_agentCounterIn{0}, m_agentCounterOut{0} {} + + template + requires(std::unsigned_integral && std::unsigned_integral) + SpireStreet::SpireStreet(Id id, Size capacity, double len, std::pair nodePair) + : Street(id, capacity, len, nodePair), m_agentCounterIn{0}, m_agentCounterOut{0} {} + + template + requires(std::unsigned_integral && std::unsigned_integral) + SpireStreet::SpireStreet( + Id id, Size capacity, double len, double maxSpeed, std::pair nodePair) + : Street(id, capacity, len, maxSpeed, nodePair), m_agentCounterIn{0}, m_agentCounterOut{0} {} + + template + requires(std::unsigned_integral && std::unsigned_integral) + void SpireStreet::enqueue(Id agentId) { + Street::enqueue(agentId); + ++m_agentCounterIn; + } + + template + requires(std::unsigned_integral && std::unsigned_integral) + Size SpireStreet::inputFlow() { + Size flow = m_agentCounterIn; + m_agentCounterIn = 0; + m_agentCounterOut = 0; + return flow; + } + template + requires(std::unsigned_integral && std::unsigned_integral) + Size SpireStreet::outputFlow() { + Size flow = m_agentCounterOut; + m_agentCounterIn = 0; + m_agentCounterOut = 0; + return flow; + } + template + requires(std::unsigned_integral && std::unsigned_integral) + int SpireStreet::meanFlow() { + int flow = static_cast(m_agentCounterIn) - static_cast(m_agentCounterOut); + m_agentCounterIn = 0; + m_agentCounterOut = 0; + return flow; + } + + template + requires(std::unsigned_integral && std::unsigned_integral) + std::optional SpireStreet::dequeue() { + std::optional id = Street::dequeue(); + if (id.has_value()) { + ++m_agentCounterOut; + } + return id; + } + }; // namespace dsm #endif diff --git a/test/Test_street.cpp b/test/Test_street.cpp index 9ea6ba6d..e60bd27e 100644 --- a/test/Test_street.cpp +++ b/test/Test_street.cpp @@ -11,6 +11,7 @@ using Agent = dsm::Agent; using Node = dsm::Node; using Street = dsm::Street; +using SpireStreet = dsm::SpireStreet; TEST_CASE("Street") { SUBCASE("Constructor_1") { @@ -162,3 +163,66 @@ TEST_CASE("Street") { CHECK_THROWS(street.setAngle(7.)); } } + +TEST_CASE("SpireStreet") { + SUBCASE("Input flow") { + GIVEN("A spire street") { + SpireStreet street{1, 4, 3.5, std::make_pair(0, 1)}; + WHEN("An agent is enqueued") { + street.enqueue(1); + THEN("The density is updated") { CHECK_EQ(street.density(), 0.25); } + THEN("Input flow is one") { CHECK_EQ(street.inputFlow(), 1); } + THEN("Output flow is zero") { CHECK_EQ(street.outputFlow(), 0); } + THEN("Mean flow is one") { CHECK_EQ(street.meanFlow(), 1); } + } + WHEN("Three agents are enqueued") { + street.enqueue(1); + street.enqueue(2); + street.enqueue(3); + THEN("The density is updated") { CHECK_EQ(street.density(), 0.75); } + THEN("Input flow is three") { CHECK_EQ(street.inputFlow(), 3); } + THEN("Output flow is zero") { CHECK_EQ(street.outputFlow(), 0); } + THEN("Mean flow is three") { CHECK_EQ(street.meanFlow(), 3); } + } + WHEN("An agent is dequeued") { + street.enqueue(1); + street.dequeue(); + THEN("The density is updated") { CHECK_EQ(street.density(), 0); } + THEN("Input flow is one") { CHECK_EQ(street.inputFlow(), 1); } + THEN("Output flow is one") { CHECK_EQ(street.outputFlow(), 1); } + THEN("Mean flow is zero") { CHECK_EQ(street.meanFlow(), 0); } + } + WHEN("Three agents are dequeued") { + street.enqueue(1); + street.enqueue(2); + street.enqueue(3); + street.dequeue(); + street.dequeue(); + street.dequeue(); + THEN("The density is updated") { CHECK_EQ(street.density(), 0); } + THEN("Input flow is three") { CHECK_EQ(street.inputFlow(), 3); } + THEN("Output flow is three") { CHECK_EQ(street.outputFlow(), 3); } + THEN("Mean flow is zero") { CHECK_EQ(street.meanFlow(), 0); } + } + WHEN("Input is greater than output") { + street.enqueue(1); + street.enqueue(2); + street.dequeue(); + street.dequeue(); + street.enqueue(3); + THEN("The density is updated") { CHECK_EQ(street.density(), 0.25); } + THEN("Mean flow is one") { CHECK_EQ(street.meanFlow(), 1); } + } + WHEN("Output is greater than input") { + street.enqueue(1); + street.enqueue(2); + street.meanFlow(); + street.enqueue(3); + street.dequeue(); + street.dequeue(); + THEN("The density is updated") { CHECK_EQ(street.density(), 0.25); } + THEN("Mean flow is minus one") { CHECK_EQ(street.meanFlow(), -1); } + } + } + } +} From 51991e3ea482e6b7660198c555b5625d4a7e6b5a Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Fri, 15 Mar 2024 19:11:05 +0100 Subject: [PATCH 02/33] Remove aliases for smart pointers --- src/dsm/headers/Graph.hpp | 5 ----- src/dsm/utility/TypeTraits/is_agent.hpp | 4 ---- src/dsm/utility/TypeTraits/is_itinerary.hpp | 4 ---- src/dsm/utility/TypeTraits/is_node.hpp | 4 ---- src/dsm/utility/TypeTraits/is_numeric.hpp | 3 --- src/dsm/utility/TypeTraits/is_street.hpp | 4 ---- 6 files changed, 24 deletions(-) diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index dfe546aa..014d0917 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -36,11 +36,6 @@ namespace dsm { - // Alias for shared pointers - template - using shared = std::shared_ptr; - using std::make_shared; - /// @brief The Graph class represents a graph in the network. /// @tparam Id, The type of the graph's id. It must be an unsigned integral type. /// @tparam Size, The type of the graph's capacity. It must be an unsigned integral type. diff --git a/src/dsm/utility/TypeTraits/is_agent.hpp b/src/dsm/utility/TypeTraits/is_agent.hpp index d648b2a6..ab140966 100644 --- a/src/dsm/utility/TypeTraits/is_agent.hpp +++ b/src/dsm/utility/TypeTraits/is_agent.hpp @@ -12,10 +12,6 @@ namespace dsm { is_numeric_v) class Agent; - // Alias for shared pointers - template - using shared = std::shared_ptr; - // define is_node type trait template struct is_agent : std::false_type {}; diff --git a/src/dsm/utility/TypeTraits/is_itinerary.hpp b/src/dsm/utility/TypeTraits/is_itinerary.hpp index bcb7e8e6..f0c5ac0a 100644 --- a/src/dsm/utility/TypeTraits/is_itinerary.hpp +++ b/src/dsm/utility/TypeTraits/is_itinerary.hpp @@ -10,10 +10,6 @@ namespace dsm { requires(std::unsigned_integral) class Itinerary; - // Alias for shared pointers - template - using shared = std::shared_ptr; - // define is_node type trait template struct is_itinerary : std::false_type {}; diff --git a/src/dsm/utility/TypeTraits/is_node.hpp b/src/dsm/utility/TypeTraits/is_node.hpp index f3173aac..49d4a5a5 100644 --- a/src/dsm/utility/TypeTraits/is_node.hpp +++ b/src/dsm/utility/TypeTraits/is_node.hpp @@ -10,10 +10,6 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral) class Node; - // Alias for shared pointers - template - using shared = std::shared_ptr; - // define is_node type trait template struct is_node : std::false_type {}; diff --git a/src/dsm/utility/TypeTraits/is_numeric.hpp b/src/dsm/utility/TypeTraits/is_numeric.hpp index 5fdc77b3..cd9d820a 100644 --- a/src/dsm/utility/TypeTraits/is_numeric.hpp +++ b/src/dsm/utility/TypeTraits/is_numeric.hpp @@ -7,9 +7,6 @@ #include namespace dsm { - // Alias for shared pointers - template - using shared = std::shared_ptr; // define is_numeric_v type trait template diff --git a/src/dsm/utility/TypeTraits/is_street.hpp b/src/dsm/utility/TypeTraits/is_street.hpp index b3400a3d..06fbf2d5 100644 --- a/src/dsm/utility/TypeTraits/is_street.hpp +++ b/src/dsm/utility/TypeTraits/is_street.hpp @@ -10,10 +10,6 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral) class Street; - // Alias for shared pointers - template - using shared = std::shared_ptr; - // define the is_street type trait template struct is_street : std::false_type {}; From ff33807edfb8cfaee4509b8e72bdf6f5cdcc046b Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Fri, 15 Mar 2024 23:34:42 +0100 Subject: [PATCH 03/33] Update nomenclature --- src/dsm/utility/TypeTraits/is_agent.hpp | 2 +- src/dsm/utility/TypeTraits/is_itinerary.hpp | 2 +- src/dsm/utility/TypeTraits/is_node.hpp | 2 +- src/dsm/utility/TypeTraits/is_street.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dsm/utility/TypeTraits/is_agent.hpp b/src/dsm/utility/TypeTraits/is_agent.hpp index ab140966..2e040fbd 100644 --- a/src/dsm/utility/TypeTraits/is_agent.hpp +++ b/src/dsm/utility/TypeTraits/is_agent.hpp @@ -20,7 +20,7 @@ namespace dsm { struct is_agent> : std::true_type {}; template - struct is_agent>> : std::true_type {}; + struct is_agent>> : std::true_type {}; template inline constexpr bool is_agent_v = is_agent::value; diff --git a/src/dsm/utility/TypeTraits/is_itinerary.hpp b/src/dsm/utility/TypeTraits/is_itinerary.hpp index f0c5ac0a..1dc74e75 100644 --- a/src/dsm/utility/TypeTraits/is_itinerary.hpp +++ b/src/dsm/utility/TypeTraits/is_itinerary.hpp @@ -18,7 +18,7 @@ namespace dsm { struct is_itinerary> : std::true_type {}; template - struct is_itinerary>> : std::true_type {}; + struct is_itinerary>> : std::true_type {}; template inline constexpr bool is_itinerary_v = is_itinerary::value; diff --git a/src/dsm/utility/TypeTraits/is_node.hpp b/src/dsm/utility/TypeTraits/is_node.hpp index 49d4a5a5..d3a0d8cf 100644 --- a/src/dsm/utility/TypeTraits/is_node.hpp +++ b/src/dsm/utility/TypeTraits/is_node.hpp @@ -24,7 +24,7 @@ namespace dsm { struct is_node&> : std::true_type {}; template - struct is_node>> : std::true_type {}; + struct is_node>> : std::true_type {}; template inline constexpr bool is_node_v = is_node::value; diff --git a/src/dsm/utility/TypeTraits/is_street.hpp b/src/dsm/utility/TypeTraits/is_street.hpp index 06fbf2d5..b1aae5d5 100644 --- a/src/dsm/utility/TypeTraits/is_street.hpp +++ b/src/dsm/utility/TypeTraits/is_street.hpp @@ -24,7 +24,7 @@ namespace dsm { struct is_street&> : std::true_type {}; template - struct is_street>> : std::true_type {}; + struct is_street>> : std::true_type {}; template inline constexpr bool is_street_v = is_street::value; From 823e7620c16a25775f135d24716de44755e569a8 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 11:32:28 +0100 Subject: [PATCH 04/33] Add virtual destructor for `Node` --- src/dsm/headers/Node.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dsm/headers/Node.hpp b/src/dsm/headers/Node.hpp index 7001ff7a..5659c4d9 100644 --- a/src/dsm/headers/Node.hpp +++ b/src/dsm/headers/Node.hpp @@ -48,6 +48,8 @@ namespace dsm { /// @param coords A std::pair containing the node's coordinates Node(Id id, std::pair coords); + virtual ~Node() = default; + /// @brief Set the node's coordinates /// @param coords A std::pair containing the node's coordinates void setCoords(std::pair coords); From d656f6e941ee10cffcf89c9cd40c9f7ec4fcb8c6 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 11:32:40 +0100 Subject: [PATCH 05/33] Add `isTrafficLight` method for nodes --- src/dsm/headers/Node.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dsm/headers/Node.hpp b/src/dsm/headers/Node.hpp index 5659c4d9..f685bbe4 100644 --- a/src/dsm/headers/Node.hpp +++ b/src/dsm/headers/Node.hpp @@ -84,6 +84,8 @@ namespace dsm { virtual bool isGreen(Id) const; virtual void increaseCounter(){}; + virtual bool isTrafficLight() const { return false; } + /// @brief Get the node's id /// @return Id The node's id /// @return Id The node's id @@ -302,6 +304,7 @@ namespace dsm { /// @return bool True if the traffic light is green bool isGreen() const override; bool isGreen(Id streetId) const override; + bool isTrafficLight() const override { return true; } }; template From ea918478caf0eeb97b01b2cfab7380ccbb7e6842 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 11:33:05 +0100 Subject: [PATCH 06/33] Substitute shared with unique and remove useless pointers --- src/dsm/headers/Dynamics.hpp | 95 ++++++++--------- src/dsm/headers/Graph.hpp | 191 +++++++++++++++++++++-------------- 2 files changed, 162 insertions(+), 124 deletions(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 1ca033f5..32c39314 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -73,7 +73,7 @@ namespace dsm { std::unordered_map>> m_itineraries; std::map>> m_agents; TimePoint m_time; - std::unique_ptr> m_graph; + Graph m_graph; double m_errorProbability; double m_minSpeedRateo; mutable std::mt19937_64 m_generator{std::random_device{}()}; @@ -255,7 +255,7 @@ namespace dsm { is_numeric_v) Dynamics::Dynamics(const Graph& graph) : m_time{0}, - m_graph{std::make_unique>(graph)}, + m_graph{(std::move(graph))}, m_errorProbability{0.}, m_minSpeedRateo{0.} {} @@ -263,8 +263,8 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) void Dynamics::m_evolveStreets() { - for (auto& streetPair : this->m_graph->streetSet()) { - auto street{streetPair.second}; + for (auto& streetPair : this->m_graph.streetSet()) { + const auto& street{streetPair.second}; if (street->queue().empty()) { continue; } @@ -273,11 +273,11 @@ namespace dsm { continue; } this->m_agents[agentId]->setSpeed(0.); - auto destinationNode{this->m_graph->nodeSet()[street->nodePair().second]}; + auto& destinationNode{this->m_graph.nodeSet()[street->nodePair().second]}; if (destinationNode->isFull()) { continue; } - if (std::dynamic_pointer_cast>(destinationNode) && + if (destinationNode->isTrafficLight() && !destinationNode->isGreen(street->id())) { continue; } @@ -293,8 +293,8 @@ namespace dsm { By doing the angle difference, if the destination street is the same we can basically compare these differences (mod(pi)!, i.e. delta % std::numbers::pi): the smaller goes first. Anyway, this is not trivial as it seems so I will leave it as a comment.*/ - for (auto& nodePair : this->m_graph->nodeSet()) { - auto node{nodePair.second}; + for (auto& nodePair : this->m_graph.nodeSet()) { + auto& node{nodePair.second}; for (const auto agent : node->agents()) { Size agentId{agent.second}; if (node->id() == @@ -318,7 +318,7 @@ namespace dsm { this->m_itineraries[this->m_agents[agentId]->itineraryId()]->path().getRow( node->id(), true)}; if (this->m_uniformDist(this->m_generator) < this->m_errorProbability) { - possibleMoves = this->m_graph->adjMatrix()->getRow(node->id(), true); + possibleMoves = this->m_graph.adjMatrix().getRow(node->id(), true); } if (static_cast(possibleMoves.size()) == 0) { continue; @@ -329,7 +329,7 @@ namespace dsm { auto iterator = possibleMoves.begin(); std::advance(iterator, p); const auto nextStreetId{iterator->first}; - auto nextStreet{this->m_graph->streetSet()[nextStreetId]}; + auto& nextStreet{this->m_graph.streetSet()[nextStreetId]}; if (nextStreet->density() < 1) { node->removeAgent(agentId); @@ -342,7 +342,7 @@ namespace dsm { break; } } - if (std::dynamic_pointer_cast>(node)) { + if (node->isTrafficLight()) { node->increaseCounter(); } } @@ -352,14 +352,14 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) void Dynamics::m_evolveAgents() { - for (auto const& [agentId, agent] : this->m_agents) { + for (const auto& [agentId, agent] : this->m_agents) { if (agent->time() > 0) { if (agent->delay() > 0) { if (agent->delay() > 1) { agent->incrementDistance(); } else if (agent->streetId().has_value()) { double distance{ - std::fmod(this->m_graph->streetSet()[agent->streetId().value()]->length(), + std::fmod(this->m_graph.streetSet()[agent->streetId().value()]->length(), agent->speed())}; if (distance < std::numeric_limits::epsilon()) { agent->incrementDistance(); @@ -371,7 +371,7 @@ namespace dsm { } } else if (!agent->streetId().has_value()) { assert(agent->srcNodeId().has_value()); - auto srcNode{this->m_graph->nodeSet()[agent->srcNodeId().value()]}; + auto& srcNode{this->m_graph.nodeSet()[agent->srcNodeId().value()]}; if (srcNode->isFull()) { continue; } @@ -428,7 +428,7 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) void Dynamics::updatePaths() { - const Size dimension = m_graph->adjMatrix()->getRowDim(); + const Size dimension = m_graph.adjMatrix().getRowDim(); for (const auto& [itineraryId, itinerary] : m_itineraries) { SparseMatrix path{dimension, dimension}; // cycle over the nodes @@ -436,24 +436,25 @@ namespace dsm { if (i == itinerary->destination()) { continue; } - auto result{m_graph->shortestPath(i, itinerary->destination())}; + auto result{m_graph.shortestPath(i, itinerary->destination())}; if (!result.has_value()) { continue; } // save the minimum distance between i and the destination auto minDistance{result.value().distance()}; - for (auto const& node : m_graph->adjMatrix()->getRow(i)) { + for (auto const& node : m_graph.adjMatrix().getRow(i)) { // init distance from a neighbor node to the destination to zero double distance{0.}; - auto streetResult = m_graph->street(i, node.first); - if (!streetResult.has_value()) { + // can't dereference because risk undefined behavior + auto streetResult = m_graph.street(i, node.first); + if (streetResult == nullptr) { continue; } - auto streetLength{streetResult.value()->length()}; + auto streetLength{(*streetResult)->length()}; // TimePoint expectedTravelTime{ // streetLength}; // / street->maxSpeed()}; // TODO: change into input velocity - result = m_graph->shortestPath(node.first, itinerary->destination()); + result = m_graph.shortestPath(node.first, itinerary->destination()); if (result.has_value()) { // if the shortest path exists, save the distance distance = result.value().distance(); @@ -490,7 +491,7 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) const Graph& Dynamics::graph() const { - return *m_graph; + return m_graph; } template @@ -599,7 +600,7 @@ namespace dsm { std::uniform_int_distribution itineraryDist{ 0, static_cast(this->m_itineraries.size() - 1)}; std::uniform_int_distribution streetDist{ - 0, static_cast(this->m_graph->streetSet().size() - 1)}; + 0, static_cast(this->m_graph.streetSet().size() - 1)}; for (Size i{0}; i < nAgents; ++i) { if (randomItinerary) { itineraryId = itineraryDist(this->m_generator); @@ -611,14 +612,14 @@ namespace dsm { Id streetId{0}; do { // I dunno why this works and the following doesn't - auto streetSet = this->m_graph->streetSet(); + const auto& streetSet = this->m_graph.streetSet(); auto streetIt = streetSet.begin(); // auto streetIt = this->m_graph->streetSet().begin(); Size step = streetDist(this->m_generator); std::advance(streetIt, step); streetId = streetIt->first; - } while (this->m_graph->streetSet()[streetId]->density() == 1); - auto street{this->m_graph->streetSet()[streetId]}; + } while (this->m_graph.streetSet()[streetId]->density() == 1); + auto& street{this->m_graph.streetSet()[streetId]}; Agent agent{ agentId, itineraryId.value(), street->nodePair().first}; agent.setStreetId(streetId); @@ -733,11 +734,11 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) Measurement Dynamics::streetMeanDensity() const { - if (m_graph->streetSet().size() == 0) { + if (m_graph.streetSet().size() == 0) { return Measurement(0., 0.); } - const double mean{std::accumulate(m_graph->streetSet().cbegin(), - m_graph->streetSet().cend(), + const double mean{std::accumulate(m_graph.streetSet().cbegin(), + m_graph.streetSet().cend(), 0., [](double sum, const auto& street) { if (!street.second->isSpire()) { @@ -745,10 +746,10 @@ namespace dsm { } return sum + street.second->density(); }) / - m_graph->streetSet().size()}; + m_graph.streetSet().size()}; const double variance{ - std::accumulate(m_graph->streetSet().cbegin(), - m_graph->streetSet().cend(), + std::accumulate(m_graph.streetSet().cbegin(), + m_graph.streetSet().cend(), 0., [mean](double sum, const auto& street) { if (!street.second->isSpire()) { @@ -756,7 +757,7 @@ namespace dsm { } return sum + std::pow(street.second->density() - mean, 2); }) / - (m_graph->streetSet().size() - 1)}; + (m_graph.streetSet().size() - 1)}; return Measurement(mean, std::sqrt(variance)); } template @@ -764,8 +765,8 @@ namespace dsm { is_numeric_v) Measurement Dynamics::streetMeanFlow() const { std::vector flows; - flows.reserve(m_graph->streetSet().size()); - for (const auto& [streetId, street] : m_graph->streetSet()) { + flows.reserve(m_graph.streetSet().size()); + for (const auto& [streetId, street] : m_graph.streetSet()) { if (!street->isSpire()) { continue; } @@ -785,8 +786,8 @@ namespace dsm { Measurement Dynamics::streetMeanFlow(double threshold, bool above) const { std::vector flows; - flows.reserve(m_graph->streetSet().size()); - for (const auto& [streetId, street] : m_graph->streetSet()) { + flows.reserve(m_graph.streetSet().size()); + for (const auto& [streetId, street] : m_graph.streetSet()) { if (!street->isSpire()) { continue; } @@ -840,7 +841,7 @@ namespace dsm { FirstOrderDynamics() = delete; /// @brief Construct a new First Order Dynamics object /// @param graph, The graph representing the network - FirstOrderDynamics(const Graph& graph); + FirstOrderDynamics(Graph graph); /// @brief Set the speed of an agent /// @param agentId The id of the agent /// @throw std::invalid_argument, If the agent is not found @@ -864,23 +865,24 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral && std::unsigned_integral) - FirstOrderDynamics::FirstOrderDynamics(const Graph& graph) - : Dynamics(graph) {} + FirstOrderDynamics::FirstOrderDynamics(Graph graph) + : Dynamics(std::move(graph)) {} template requires(std::unsigned_integral && std::unsigned_integral && std::unsigned_integral) void FirstOrderDynamics::setAgentSpeed(Size agentId) { - auto street{this->m_graph->streetSet()[this->m_agents[agentId]->streetId().value()]}; + const auto& street{this->m_graph.streetSet()[this->m_agents[agentId]->streetId().value()]}; double speed{street->maxSpeed() * (1. - this->m_minSpeedRateo * street->density())}; this->m_agents[agentId]->setSpeed(speed); } + template requires(std::unsigned_integral && std::unsigned_integral && std::unsigned_integral) std::optional FirstOrderDynamics::streetMeanSpeed( Id streetId) const { - auto street{this->m_graph->streetSet()[streetId]}; + auto& street{this->m_graph.streetSet().at(streetId)}; if (street->queue().empty() || !street->isSpire()) { return std::nullopt; } @@ -895,6 +897,7 @@ namespace dsm { } return meanSpeed / street->queue().size(); } + template requires(std::unsigned_integral && std::unsigned_integral && std::unsigned_integral) @@ -903,8 +906,8 @@ namespace dsm { return Measurement(0., 0.); } std::vector speeds; - speeds.reserve(this->m_graph->streetSet().size()); - for (const auto& [streetId, street] : this->m_graph->streetSet()) { + speeds.reserve(this->m_graph.streetSet().size()); + for (const auto& [streetId, street] : this->m_graph.streetSet()) { auto speedOpt{this->streetMeanSpeed(streetId)}; if (speedOpt.has_value()) { speeds.push_back(speedOpt.value()); @@ -921,8 +924,8 @@ namespace dsm { return Measurement(0., 0.); } std::vector speeds; - speeds.reserve(this->m_graph->streetSet().size()); - for (const auto& [streetId, street] : this->m_graph->streetSet()) { + speeds.reserve(this->m_graph.streetSet().size()); + for (const auto& [streetId, street] : this->m_graph.streetSet()) { if (!street->isSpire()) { continue; } diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index 014d0917..9a86211d 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -43,10 +43,10 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral) class Graph { private: - std::unordered_map>> m_nodes; - std::unordered_map>> m_streets; - shared> m_adjacency; + std::unordered_map>> m_nodes; + std::unordered_map>> m_streets; std::unordered_map m_nodeMapping; + SparseMatrix m_adjacency; /// @brief Reassign the street ids using the max node id /// @details The street ids are reassigned using the max node id, i.e. @@ -60,7 +60,7 @@ namespace dsm { Graph(const SparseMatrix& adj); /// @brief Construct a new Graph object /// @param streetSet A map of streets representing the graph's streets - Graph(const std::unordered_map>>& streetSet); + Graph(const std::unordered_map>>& streetSet); /// @brief Build the graph's adjacency matrix /// @details The adjacency matrix is built using the graph's streets and nodes. N.B.: The street ids @@ -101,7 +101,7 @@ namespace dsm { /// @brief Add a node to the graph /// @param node A std::shared_ptr to the node to add - void addNode(shared> node); + void addNode(std::unique_ptr> node); /// @brief Add a node to the graph /// @param node A reference to the node to add void addNode(const Node& node); @@ -125,7 +125,7 @@ namespace dsm { /// @brief Add a street to the graph /// @param street A std::shared_ptr to the street to add - void addStreet(shared> street); + void addStreet(std::shared_ptr> street); /// @brief Add a street to the graph /// @param street A reference to the street to add void addStreet(const Street& street); @@ -141,19 +141,25 @@ namespace dsm { /// @brief Get the graph's adjacency matrix /// @return A std::shared_ptr to the graph's adjacency matrix - shared> adjMatrix() const; + const SparseMatrix& adjMatrix() const; /// @brief Get the graph's node map /// @return A std::unordered_map containing the graph's nodes - std::unordered_map>> nodeSet() const; + const std::unordered_map>>& nodeSet() const; + /// @brief Get the graph's node map + /// @return A std::unordered_map containing the graph's nodes + std::unordered_map>>& nodeSet(); + /// @brief Get the graph's street map + /// @return A std::unordered_map containing the graph's streets + const std::unordered_map>>& streetSet() const; /// @brief Get the graph's street map /// @return A std::unordered_map containing the graph's streets - std::unordered_map>> streetSet() const; + std::unordered_map>>& streetSet(); /// @brief Get a street from the graph /// @param source The source node /// @param destination The destination node /// @return A std::optional containing a std::shared_ptr to the street if it exists, otherwise /// std::nullopt - std::optional>> street(Id source, Id destination) const; + const std::unique_ptr>* street(Id source, Id destination) const; /// @brief Get the shortest path between two nodes using dijkstra algorithm /// @param source The source node @@ -170,39 +176,39 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral) - Graph::Graph() : m_adjacency{make_shared>()} {} + Graph::Graph() : m_adjacency{SparseMatrix()} {} template requires(std::unsigned_integral && std::unsigned_integral) - Graph::Graph(const SparseMatrix& adj) - : m_adjacency{make_shared>(adj)} { + Graph::Graph(const SparseMatrix& adj) : m_adjacency{adj} { assert(adj.getRowDim() == adj.getColDim()); auto n{static_cast(adj.getRowDim())}; for (const auto& [id, value] : adj) { const auto srcId{static_cast(id / n)}; const auto dstId{static_cast(id % n)}; if (!m_nodes.contains(srcId)) { - m_nodes.emplace(srcId, make_shared>(srcId)); + m_nodes.emplace(srcId, std::make_unique>(srcId)); } if (!m_nodes.contains(dstId)) { - m_nodes.emplace(dstId, make_shared>(dstId)); + m_nodes.emplace(dstId, std::make_unique>(dstId)); } - m_streets.emplace(id, - make_shared>(id, std::make_pair(srcId, dstId))); + m_streets.emplace( + id, std::make_unique>(id, std::make_pair(srcId, dstId))); } } template requires(std::unsigned_integral && std::unsigned_integral) - Graph::Graph(const std::unordered_map>>& streetSet) - : m_adjacency{make_shared>()} { + Graph::Graph( + const std::unordered_map>>& streetSet) + : m_adjacency{std::make_unique>()} { for (const auto& street : streetSet) { m_streets.emplace(std::make_pair(street->id(), street)); Id node1 = street->nodePair().first; Id node2 = street->nodePair().second; - m_nodes.emplace(node1, make_shared>(node1)); - m_nodes.emplace(node2, make_shared>(node2)); + m_nodes.emplace(node1, std::make_unique>(node1)); + m_nodes.emplace(node2, std::make_unique>(node2)); } buildAdj(); @@ -211,7 +217,8 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral) void Graph::m_reassignIds() { - const auto oldStreetSet{m_streets}; + // not sure about this, might need a bit more work + const auto oldStreetSet{std::move(m_streets)}; m_streets.clear(); const auto n{static_cast(m_nodes.size())}; std::unordered_map newStreetIds; @@ -223,12 +230,12 @@ namespace dsm { throw std::invalid_argument(buildLog("Street with same id already exists.")); } auto newStreet = Street(newStreetId, *street); - m_streets.emplace(newStreetId, make_shared>(newStreet)); + m_streets.emplace(newStreetId, std::make_unique>(newStreet)); newStreetIds.emplace(streetId, newStreetId); } - for (auto [nodeId, node] : m_nodes) { + for (const auto& [nodeId, node] : m_nodes) { // This is probably not the best way to do this - if (std::dynamic_pointer_cast>(node)) { + if (node->isTrafficLight()) { const auto& oldStreetPriorities{node->streetPriorities()}; std::set newStreetPriorities; for (const auto streetId : oldStreetPriorities) { @@ -244,18 +251,19 @@ namespace dsm { void Graph::buildAdj() { // find max values in streets node pairs const auto maxNode{static_cast(m_nodes.size())}; - m_adjacency->reshape(maxNode, maxNode); + m_adjacency.reshape(maxNode, maxNode); for (const auto& [streetId, street] : m_streets) { - m_adjacency->insert(street->nodePair().first, street->nodePair().second, true); + m_adjacency.insert(street->nodePair().first, street->nodePair().second, true); } this->m_reassignIds(); } + template requires(std::unsigned_integral && std::unsigned_integral) void Graph::buildStreetAngles() { for (const auto& street : m_streets) { - const auto node1{m_nodes[street.second->nodePair().first]}; - const auto node2{m_nodes[street.second->nodePair().second]}; + const auto& node1{m_nodes[street.second->nodePair().first]}; + const auto& node2{m_nodes[street.second->nodePair().second]}; street.second->setAngle(node1->coordinates(), node2->coordinates()); } } @@ -276,23 +284,24 @@ namespace dsm { throw std::invalid_argument(buildLog("Adjacency matrix must be square")); } Size n{rows}; - m_adjacency = make_shared>(n, n); + m_adjacency = SparseMatrix(n, n); // each line has 2 elements while (!file.eof()) { Id index; double val; file >> index >> val; - m_adjacency->insert(index, val); + m_adjacency.insert(index, val); const auto srcId{static_cast(index / n)}; const auto dstId{static_cast(index % n)}; if (!m_nodes.contains(srcId)) { - m_nodes.emplace(srcId, make_shared>(srcId)); + m_nodes.emplace(srcId, std::make_unique>(srcId)); } if (!m_nodes.contains(dstId)) { - m_nodes.emplace(dstId, make_shared>(dstId)); + m_nodes.emplace(dstId, std::make_unique>(dstId)); } m_streets.emplace( - index, make_shared>(index, std::make_pair(srcId, dstId))); + index, + std::make_unique>(index, std::make_pair(srcId, dstId))); assert(index == srcId * n + dstId); if (!isAdj) { m_streets[index]->setLength(val); @@ -317,7 +326,7 @@ namespace dsm { throw std::invalid_argument( buildLog("Matrix size is too large for the current type of Id.")); } - m_adjacency = make_shared>(n, n); + m_adjacency = SparseMatrix(n, n); Id index{0}; while (!file.eof()) { double value; @@ -327,17 +336,18 @@ namespace dsm { buildLog("Adjacency matrix elements must be positive")); } if (value > 0) { - m_adjacency->insert(index, true); + m_adjacency.insert(index, true); const auto srcId{static_cast(index / n)}; const auto dstId{static_cast(index % n)}; if (!m_nodes.contains(srcId)) { - m_nodes.emplace(srcId, make_shared>(srcId)); + m_nodes.emplace(srcId, std::make_unique>(srcId)); } if (!m_nodes.contains(dstId)) { - m_nodes.emplace(dstId, make_shared>(dstId)); + m_nodes.emplace(dstId, std::make_unique>(dstId)); } m_streets.emplace( - index, make_shared>(index, std::make_pair(srcId, dstId))); + index, + std::make_unique>(index, std::make_pair(srcId, dstId))); assert(index == srcId * n + dstId); if (!isAdj) { m_streets[index]->setLength(value); @@ -402,7 +412,7 @@ namespace dsm { std::getline(iss, highway, ';'); Id nodeId{static_cast(std::stoul(id))}; m_nodes.emplace(nodeIndex, - make_shared>( + std::make_unique>( nodeIndex, std::make_pair(std::stod(lat), std::stod(lon)))); m_nodeMapping.emplace(std::make_pair(nodeId, nodeIndex)); ++nodeIndex; @@ -447,7 +457,7 @@ namespace dsm { } Id streetId = std::stoul(sourceId) + std::stoul(targetId) * m_nodes.size(); m_streets.emplace(streetId, - make_shared>( + std::make_unique>( streetId, 1, std::stod(maxspeed), @@ -470,12 +480,12 @@ namespace dsm { throw std::invalid_argument(buildLog("Cannot open file: " + path)); } if (isAdj) { - file << m_adjacency->getRowDim() << '\t' << m_adjacency->getColDim() << '\n'; - for (const auto& [id, value] : *m_adjacency) { + file << m_adjacency.getRowDim() << '\t' << m_adjacency.getColDim() << '\n'; + for (const auto& [id, value] : m_adjacency) { file << id << '\t' << value << '\n'; } } else { - file << m_adjacency->getRowDim() << " " << m_adjacency->getColDim() << '\n'; + file << m_adjacency.getRowDim() << " " << m_adjacency.getColDim() << '\n'; for (const auto& [id, street] : m_streets) { file << id << '\t' << street->length() << '\n'; } @@ -484,14 +494,14 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral) - void Graph::addNode(shared> node) { - m_nodes.emplace(std::make_pair(node->id(), node)); + void Graph::addNode(std::unique_ptr> node) { + m_nodes.emplace(std::make_pair(node->id(), std::move(node))); } template requires(std::unsigned_integral && std::unsigned_integral) void Graph::addNode(const Node& node) { - m_nodes.emplace(std::make_pair(node.id(), make_shared>(node))); + m_nodes.emplace(std::make_pair(node.id(), std::make_unique>(node))); } template @@ -527,9 +537,7 @@ namespace dsm { pNode = pTrafficLight; } - template - requires(std::unsigned_integral && std::unsigned_integral) - void Graph::addStreet(shared> street) { + void Graph::addStreet(std::shared_ptr> street) { if (m_streets.contains(street->id())) { throw std::invalid_argument(buildLog("Street with same id already exists.")); } @@ -537,10 +545,10 @@ namespace dsm { const auto srcId{street.nodePair().first}; const auto dstId{street.nodePair().second}; if (!m_nodes.contains(srcId)) { - m_nodes.emplace(srcId, make_shared>(srcId)); + m_nodes.emplace(srcId, std::make_unique>(srcId)); } if (!m_nodes.contains(dstId)) { - m_nodes.emplace(dstId, make_shared>(dstId)); + m_nodes.emplace(dstId, std::make_unique>(dstId)); } // emplace street m_streets.emplace(std::make_pair(street->id(), street)); @@ -556,13 +564,14 @@ namespace dsm { const auto srcId{street.nodePair().first}; const auto dstId{street.nodePair().second}; if (!m_nodes.contains(srcId)) { - m_nodes.emplace(srcId, make_shared>(srcId)); + m_nodes.emplace(srcId, std::make_unique>(srcId)); } if (!m_nodes.contains(dstId)) { - m_nodes.emplace(dstId, make_shared>(dstId)); + m_nodes.emplace(dstId, std::make_unique>(dstId)); } // emplace street - m_streets.emplace(std::make_pair(street.id(), make_shared>(street))); + m_streets.emplace( + std::make_pair(street.id(), std::make_unique>(street))); } template @@ -577,13 +586,14 @@ namespace dsm { const auto srcId{street.nodePair().first}; const auto dstId{street.nodePair().second}; if (!m_nodes.contains(srcId)) { - m_nodes.emplace(srcId, make_shared>(srcId)); + m_nodes.emplace(srcId, std::make_unique>(srcId)); } if (!m_nodes.contains(dstId)) { - m_nodes.emplace(dstId, make_shared>(dstId)); + m_nodes.emplace(dstId, std::make_unique>(dstId)); } // emplace street - m_streets.emplace(std::make_pair(street.id(), make_shared>(street))); + m_streets.emplace( + std::make_pair(street.id(), std::make_unique>(street))); } template @@ -598,26 +608,40 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral) - shared> Graph::adjMatrix() const { + const SparseMatrix& Graph::adjMatrix() const { return m_adjacency; } template requires(std::unsigned_integral && std::unsigned_integral) - std::unordered_map>> Graph::nodeSet() const { + const std::unordered_map>>& + Graph::nodeSet() const { + return m_nodes; + } + + template + requires(std::unsigned_integral && std::unsigned_integral) + std::unordered_map>>& Graph::nodeSet() { return m_nodes; } template requires(std::unsigned_integral && std::unsigned_integral) - std::unordered_map>> Graph::streetSet() const { + const std::unordered_map>>& + Graph::streetSet() const { return m_streets; } template requires(std::unsigned_integral && std::unsigned_integral) - std::optional>> Graph::street(Id source, - Id destination) const { + std::unordered_map>>& Graph::streetSet() { + return m_streets; + } + + template + requires(std::unsigned_integral && std::unsigned_integral) + const std::unique_ptr>* Graph::street(Id source, + Id destination) const { auto streetIt = std::find_if(m_streets.begin(), m_streets.end(), [source, destination](const auto& street) -> bool { @@ -625,7 +649,7 @@ namespace dsm { street.second->nodePair().second == destination; }); if (streetIt == m_streets.end()) { - return std::nullopt; + return nullptr; } Size n = m_nodes.size(); auto id1 = streetIt->first; @@ -636,7 +660,7 @@ namespace dsm { std::cout << "Street id: " << id1 << std::endl; std::cout << "Nodes: " << id2 << std::endl; } - return streetIt->second; + return &(streetIt->second); } template @@ -652,16 +676,27 @@ namespace dsm { Id destination) const { const Id sourceId{source}; - std::unordered_map>> unvisitedNodes{m_nodes}; - if (!unvisitedNodes.contains(source)) { - return std::nullopt; - } - if (!unvisitedNodes.contains(destination)) { + std::unordered_set unvisitedNodes; + bool source_found{false}; + bool dest_found{false}; + std::for_each(m_nodes.begin(), + m_nodes.end(), + [&unvisitedNodes, &source_found, &dest_found, source, destination]( + const auto& node) -> void { + if (!source_found && node.first == source) { + source_found = true; + } + if (!dest_found && node.first == destination) { + dest_found = true; + } + unvisitedNodes.emplace(node.first); + }); + if (!source_found || !dest_found) { return std::nullopt; } const size_t n_nodes{m_nodes.size()}; - auto adj{*m_adjacency}; + auto adj{m_adjacency}; std::unordered_set visitedNodes; std::vector> dist(n_nodes); @@ -680,12 +715,12 @@ namespace dsm { prev[source].second = 0.; while (unvisitedNodes.size() != 0) { - source = std::min_element(unvisitedNodes.begin(), - unvisitedNodes.end(), - [&dist](const auto& a, const auto& b) -> bool { - return dist[a.first].second < dist[b.first].second; - }) - ->first; + source = *std::min_element(unvisitedNodes.begin(), + unvisitedNodes.end(), + [&dist](const auto& a, const auto& b) -> bool { + return dist[a].second < dist[b].second; + }); + unvisitedNodes.erase(source); visitedNodes.emplace(source); @@ -695,7 +730,7 @@ namespace dsm { if (visitedNodes.find(neighbour.first) != visitedNodes.end()) { continue; } - double streetLength = this->street(source, neighbour.first).value()->length(); + double streetLength = (*(this->street(source, neighbour.first)))->length(); // if current path is shorter than the previous one, update the distance if (streetLength + dist[source].second < dist[neighbour.first].second) { dist[neighbour.first].second = streetLength + dist[source].second; From a05322889737c5c6efd88a4870055f8aee7660b7 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 11:33:34 +0100 Subject: [PATCH 07/33] Update tests --- test/Test_dynamics.cpp | 2 +- test/Test_graph.cpp | 108 ++++++++++++++++++++--------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index 2ba437ea..6b05723e 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -334,7 +334,7 @@ TEST_CASE("Dynamics") { tl.addStreetPriority(0); tl.addStreetPriority(1); Graph graph2; - graph2.addNode(std::make_shared(tl)); + graph2.addNode(std::make_unique(tl)); graph2.addStreets(s1, s2, s3, s4); graph2.buildAdj(); Dynamics dynamics{graph2}; diff --git a/test/Test_graph.cpp b/test/Test_graph.cpp index 1341f86d..10a8ce22 100644 --- a/test/Test_graph.cpp +++ b/test/Test_graph.cpp @@ -37,7 +37,7 @@ TEST_CASE("Graph") { graph.buildAdj(); CHECK(graph.streetSet().size() == 1); CHECK_EQ(graph.nodeSet().size(), 2); - CHECK(graph.adjMatrix()->size() == 1); + CHECK(graph.adjMatrix().size() == 1); } SUBCASE("Constructor_2") { @@ -50,11 +50,11 @@ TEST_CASE("Graph") { Graph graph{sm}; CHECK(graph.nodeSet().size() == 4); CHECK(graph.streetSet().size() == 5); - CHECK(graph.adjMatrix()->size() == 5); - CHECK(graph.adjMatrix()->contains(1, 2)); - CHECK(graph.adjMatrix()->contains(2, 3)); - CHECK(graph.adjMatrix()->contains(3, 2)); - CHECK_FALSE(graph.adjMatrix()->contains(2, 1)); + CHECK(graph.adjMatrix().size() == 5); + CHECK(graph.adjMatrix().contains(1, 2)); + CHECK(graph.adjMatrix().contains(2, 3)); + CHECK(graph.adjMatrix().contains(3, 2)); + CHECK_FALSE(graph.adjMatrix().contains(2, 1)); } SUBCASE("Construction with addStreet") { @@ -73,11 +73,11 @@ TEST_CASE("Graph") { CHECK_EQ(graph.streetSet().size(), 5); CHECK_EQ(graph.nodeSet().size(), 4); - CHECK_EQ(graph.adjMatrix()->size(), 5); - CHECK(graph.adjMatrix()->contains(0, 1)); - CHECK(graph.adjMatrix()->contains(1, 2)); - CHECK(graph.adjMatrix()->contains(0, 2)); - CHECK_FALSE(graph.adjMatrix()->contains(1, 3)); + CHECK_EQ(graph.adjMatrix().size(), 5); + CHECK(graph.adjMatrix().contains(0, 1)); + CHECK(graph.adjMatrix().contains(1, 2)); + CHECK(graph.adjMatrix().contains(0, 2)); + CHECK_FALSE(graph.adjMatrix().contains(1, 3)); } SUBCASE("Construction with addStreets") { @@ -92,11 +92,11 @@ TEST_CASE("Graph") { CHECK_EQ(graph.streetSet().size(), 5); CHECK_EQ(graph.nodeSet().size(), 4); - CHECK_EQ(graph.adjMatrix()->size(), 5); - CHECK(graph.adjMatrix()->contains(0, 1)); - CHECK(graph.adjMatrix()->contains(1, 2)); - CHECK(graph.adjMatrix()->contains(0, 2)); - CHECK_FALSE(graph.adjMatrix()->contains(1, 3)); + CHECK_EQ(graph.adjMatrix().size(), 5); + CHECK(graph.adjMatrix().contains(0, 1)); + CHECK(graph.adjMatrix().contains(1, 2)); + CHECK(graph.adjMatrix().contains(0, 2)); + CHECK_FALSE(graph.adjMatrix().contains(1, 3)); } SUBCASE("importMatrix - dsm") { @@ -106,25 +106,25 @@ TEST_CASE("Graph") { // THEN: the graph's adjacency matrix is the same as the one in the file Graph graph{}; graph.importMatrix("./data/matrix.dsm"); - CHECK_EQ(graph.adjMatrix()->max_size(), 9); - CHECK_EQ(graph.adjMatrix()->getRowDim(), 3); - CHECK_EQ(graph.adjMatrix()->getColDim(), 3); - CHECK(graph.adjMatrix()->operator()(8)); - CHECK(graph.adjMatrix()->operator()(6)); - CHECK(graph.adjMatrix()->operator()(3)); - CHECK(graph.adjMatrix()->operator()(1)); + CHECK_EQ(graph.adjMatrix().max_size(), 9); + CHECK_EQ(graph.adjMatrix().getRowDim(), 3); + CHECK_EQ(graph.adjMatrix().getColDim(), 3); + CHECK(graph.adjMatrix().operator()(8)); + CHECK(graph.adjMatrix().operator()(6)); + CHECK(graph.adjMatrix().operator()(3)); + CHECK(graph.adjMatrix().operator()(1)); CHECK(graph.nodeSet().size() == 3); CHECK(graph.streetSet().size() == 4); graph.exportMatrix("./data/temp.dsm", false); Graph graph2{}; graph2.importMatrix("./data/temp.dsm"); - CHECK_EQ(graph2.adjMatrix()->max_size(), 9); - CHECK_EQ(graph2.adjMatrix()->getRowDim(), 3); - CHECK_EQ(graph2.adjMatrix()->getColDim(), 3); - CHECK(graph2.adjMatrix()->operator()(8)); - CHECK(graph2.adjMatrix()->operator()(6)); - CHECK(graph2.adjMatrix()->operator()(3)); - CHECK(graph2.adjMatrix()->operator()(1)); + CHECK_EQ(graph2.adjMatrix().max_size(), 9); + CHECK_EQ(graph2.adjMatrix().getRowDim(), 3); + CHECK_EQ(graph2.adjMatrix().getColDim(), 3); + CHECK(graph2.adjMatrix().operator()(8)); + CHECK(graph2.adjMatrix().operator()(6)); + CHECK(graph2.adjMatrix().operator()(3)); + CHECK(graph2.adjMatrix().operator()(1)); CHECK(graph2.nodeSet().size() == 3); CHECK(graph2.streetSet().size() == 4); } @@ -141,13 +141,13 @@ TEST_CASE("Graph") { SUBCASE("importMatrix - raw matrix") { Graph graph{}; graph.importMatrix("./data/rawMatrix.txt", false); - CHECK_EQ(graph.adjMatrix()->max_size(), 9); - CHECK_EQ(graph.adjMatrix()->getRowDim(), 3); - CHECK_EQ(graph.adjMatrix()->getColDim(), 3); - CHECK(graph.adjMatrix()->operator()(0, 1)); - CHECK(graph.adjMatrix()->operator()(1, 0)); - CHECK(graph.adjMatrix()->operator()(1, 2)); - CHECK(graph.adjMatrix()->operator()(2, 1)); + CHECK_EQ(graph.adjMatrix().max_size(), 9); + CHECK_EQ(graph.adjMatrix().getRowDim(), 3); + CHECK_EQ(graph.adjMatrix().getColDim(), 3); + CHECK(graph.adjMatrix().operator()(0, 1)); + CHECK(graph.adjMatrix().operator()(1, 0)); + CHECK(graph.adjMatrix().operator()(1, 2)); + CHECK(graph.adjMatrix().operator()(2, 1)); CHECK(graph.nodeSet().size() == 3); CHECK(graph.streetSet().size() == 4); CHECK_EQ(graph.streetSet()[1]->length(), 500); @@ -171,7 +171,7 @@ TEST_CASE("Graph") { graph.importOSMEdges("./data/edges.csv"); CHECK_EQ(graph.streetSet().size(), 60); graph.buildAdj(); - CHECK_EQ(graph.adjMatrix()->size(), 60); + CHECK_EQ(graph.adjMatrix().size(), 60); } SUBCASE("street") { /// GIVEN: a graph @@ -181,12 +181,12 @@ TEST_CASE("Graph") { Street street{1, 1, 1., std::make_pair(0, 1)}; graph.addStreet(street); auto result = graph.street(0, 1); - CHECK(result.has_value()); - auto street2 = result.value(); + CHECK(result != nullptr); + const auto& street2 = *result; CHECK_EQ(street2->id(), 1); CHECK_EQ(street2->length(), 1.); CHECK_EQ(street2->capacity(), 1); - CHECK(!graph.street(1, 0).has_value()); + CHECK_EQ(graph.street(1, 0), nullptr); } SUBCASE("make trafficlight") { GIVEN("A graph object with two nodes and one street") { @@ -440,29 +440,29 @@ TEST_CASE("Dijkstra") { Street street{1, 1, 1., std::make_pair(0, 1)}; graph.addStreet(street); auto result = graph.street(0, 1); - CHECK(result.has_value()); - auto street2 = result.value(); + CHECK(result != nullptr); + const auto& street2 = *result; CHECK_EQ(street2->id(), 1); CHECK_EQ(street2->length(), 1.); CHECK_EQ(street2->capacity(), 1); - CHECK(!graph.street(1, 0).has_value()); + CHECK_EQ(graph.street(1, 0), nullptr); } SUBCASE("equal length") { Graph graph{}; graph.importMatrix("./data/matrix.dat", false); // check correct import - CHECK_EQ(graph.adjMatrix()->max_size(), 14400); - CHECK_EQ(graph.adjMatrix()->getRowDim(), 120); - CHECK_EQ(graph.adjMatrix()->getColDim(), 120); - CHECK_EQ(graph.adjMatrix()->size(), 436); + CHECK_EQ(graph.adjMatrix().max_size(), 14400); + CHECK_EQ(graph.adjMatrix().getRowDim(), 120); + CHECK_EQ(graph.adjMatrix().getColDim(), 120); + CHECK_EQ(graph.adjMatrix().size(), 436); // check that the path exists - CHECK(graph.adjMatrix()->operator()(46, 58)); - CHECK(graph.adjMatrix()->operator()(58, 70)); - CHECK(graph.adjMatrix()->operator()(70, 82)); - CHECK(graph.adjMatrix()->operator()(82, 94)); - CHECK(graph.adjMatrix()->operator()(94, 106)); - CHECK(graph.adjMatrix()->operator()(106, 118)); + CHECK(graph.adjMatrix().operator()(46, 58)); + CHECK(graph.adjMatrix().operator()(58, 70)); + CHECK(graph.adjMatrix().operator()(70, 82)); + CHECK(graph.adjMatrix().operator()(82, 94)); + CHECK(graph.adjMatrix().operator()(94, 106)); + CHECK(graph.adjMatrix().operator()(106, 118)); auto result = graph.shortestPath(46, 118); CHECK(result.has_value()); From c8aa014a982eacfd1f1dc4166382b632b1bd6871 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 11:48:15 +0100 Subject: [PATCH 08/33] Fix typo and specify special constructors for `Graph` --- src/dsm/headers/Graph.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index 9a86211d..29683f4b 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -60,7 +60,13 @@ namespace dsm { Graph(const SparseMatrix& adj); /// @brief Construct a new Graph object /// @param streetSet A map of streets representing the graph's streets - Graph(const std::unordered_map>>& streetSet); + Graph(const std::unordered_map>>& streetSet); + + Graph(const Graph&) = delete; + Graph& operator=(const Graph&) = delete; + + Graph(Graph&&) = default; + Graph& operator=(Graph&&) = default; /// @brief Build the graph's adjacency matrix /// @details The adjacency matrix is built using the graph's streets and nodes. N.B.: The street ids @@ -200,7 +206,7 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral) Graph::Graph( - const std::unordered_map>>& streetSet) + const std::unordered_map>>& streetSet) : m_adjacency{std::make_unique>()} { for (const auto& street : streetSet) { m_streets.emplace(std::make_pair(street->id(), street)); From 8697ca885ef63e9f64af519ae899a49534908bc6 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 15:30:18 +0100 Subject: [PATCH 09/33] Write copy constructor/assign for `Graph` --- src/dsm/headers/Dynamics.hpp | 8 ++++---- src/dsm/headers/Graph.hpp | 31 ++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 32c39314..a9916046 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -255,7 +255,7 @@ namespace dsm { is_numeric_v) Dynamics::Dynamics(const Graph& graph) : m_time{0}, - m_graph{(std::move(graph))}, + m_graph{graph}, m_errorProbability{0.}, m_minSpeedRateo{0.} {} @@ -841,7 +841,7 @@ namespace dsm { FirstOrderDynamics() = delete; /// @brief Construct a new First Order Dynamics object /// @param graph, The graph representing the network - FirstOrderDynamics(Graph graph); + FirstOrderDynamics(const Graph& graph); /// @brief Set the speed of an agent /// @param agentId The id of the agent /// @throw std::invalid_argument, If the agent is not found @@ -865,8 +865,8 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral && std::unsigned_integral) - FirstOrderDynamics::FirstOrderDynamics(Graph graph) - : Dynamics(std::move(graph)) {} + FirstOrderDynamics::FirstOrderDynamics(const Graph& graph) + : Dynamics(graph) {} template requires(std::unsigned_integral && std::unsigned_integral && diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index 29683f4b..a04aabfb 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -62,11 +62,32 @@ namespace dsm { /// @param streetSet A map of streets representing the graph's streets Graph(const std::unordered_map>>& streetSet); - Graph(const Graph&) = delete; - Graph& operator=(const Graph&) = delete; + Graph(const Graph& other) { + std::for_each(other.m_nodes.begin(), other.m_nodes.end(), [this](const auto& pair) { + this->m_nodes.emplace(pair.first, std::make_unique>(*pair.second)); + }); + std::for_each(other.m_streets.begin(), other.m_streets.end(), [this](const auto& pair) { + this->m_streets.emplace(pair.first, std::make_unique>(*pair.second)); + }); + m_nodeMapping = other.m_nodeMapping; + m_adjacency = other.m_adjacency; + } + + Graph& operator=(const Graph& other) { + std::for_each(other.m_nodes.begin(), other.m_nodes.end(), [this](const auto& pair) { + this->m_nodes.insert_or_assign(pair.first, std::make_unique>(*pair.second)); + }); + std::for_each(other.m_streets.begin(), other.m_streets.end(), [this](const auto& pair) { + this->m_streets.insert_or_assign(pair.first, std::make_unique>(*pair.second)); + }); + m_nodeMapping = other.m_nodeMapping; + m_adjacency = other.m_adjacency; + + return *this; + } - Graph(Graph&&) = default; - Graph& operator=(Graph&&) = default; + Graph(Graph&&) = default; + Graph& operator=(Graph&&) = default; /// @brief Build the graph's adjacency matrix /// @details The adjacency matrix is built using the graph's streets and nodes. N.B.: The street ids @@ -223,7 +244,7 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral) void Graph::m_reassignIds() { - // not sure about this, might need a bit more work + // not sure about this, might need a bit more work const auto oldStreetSet{std::move(m_streets)}; m_streets.clear(); const auto n{static_cast(m_nodes.size())}; From 054429876d79bcc42bb11b07fd164548f8dea565 Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Sat, 16 Mar 2024 17:44:11 +0100 Subject: [PATCH 10/33] Fix non-const methods in `SparseMatrix` class --- src/dsm/headers/SparseMatrix.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dsm/headers/SparseMatrix.hpp b/src/dsm/headers/SparseMatrix.hpp index 4f6d2c27..87caeec7 100644 --- a/src/dsm/headers/SparseMatrix.hpp +++ b/src/dsm/headers/SparseMatrix.hpp @@ -127,17 +127,17 @@ namespace dsm { /// @brief get the input degree of all nodes /// @return a SparseMatrix vector with the input degree of all nodes /// @throw std::runtime_error if the matrix is not square - SparseMatrix getDegreeVector(); + SparseMatrix getDegreeVector() const; /// @brief get the strength of all nodes /// @return a SparseMatrix vector with the strength of all nodes /// @throw std::runtime_error if the matrix is not square - SparseMatrix getStrengthVector(); + SparseMatrix getStrengthVector() const; /// @brief get the laplacian matrix /// @return the laplacian matrix /// @throw std::runtime_error if the matrix is not square - SparseMatrix getLaplacian(); + SparseMatrix getLaplacian() const; /// @brief get a row as a row vector /// @param index row index @@ -485,7 +485,7 @@ namespace dsm { template requires(std::unsigned_integral) - SparseMatrix SparseMatrix::getDegreeVector() { + SparseMatrix SparseMatrix::getDegreeVector() const { if (_rows != _cols) { throw std::runtime_error(buildLog("getDegreeVector only works on square matrices")); } @@ -499,7 +499,7 @@ namespace dsm { template requires(std::unsigned_integral) - SparseMatrix SparseMatrix::getStrengthVector() { + SparseMatrix SparseMatrix::getStrengthVector() const { if (_rows != _cols) { throw std::runtime_error( buildLog("getStrengthVector only works on square matrices")); @@ -514,7 +514,7 @@ namespace dsm { template requires(std::unsigned_integral) - SparseMatrix SparseMatrix::getLaplacian() { + SparseMatrix SparseMatrix::getLaplacian() const { if (_rows != _cols) { throw std::runtime_error(buildLog("getLaplacian only works on square matrices")); } From 542fce95c971041db64db1905d75f0a83a319a6b Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 17:49:46 +0100 Subject: [PATCH 11/33] Update profiling main --- profiling/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/profiling/main.cpp b/profiling/main.cpp index cb38d1d4..f7380059 100644 --- a/profiling/main.cpp +++ b/profiling/main.cpp @@ -19,12 +19,12 @@ int main() { std::cout << "Number of nodes: " << graph.nodeSet().size() << '\n' << "Number of streets: " << graph.streetSet().size() << '\n'; for (auto& streetPair : graph.streetSet()) { - auto street = streetPair.second; + auto& street = streetPair.second; street->setCapacity(100); street->setMaxSpeed(10.); } for (auto& nodePair : graph.nodeSet()) { - auto node = nodePair.second; + auto& node = nodePair.second; node->setCapacity(10); } std::cout << "Done.\n"; From 7245ebe196ef2d7dfe43dba3b8909d1071089576 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 18:05:16 +0100 Subject: [PATCH 12/33] Change `-O3` to '-Os' for profiling --- profiling/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profiling/CMakeLists.txt b/profiling/CMakeLists.txt index b38296b3..2b7e0bae 100644 --- a/profiling/CMakeLists.txt +++ b/profiling/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # Set the C++ flags -string(APPEND CMAKE_CXX_FLAGS "-Wall -Wextra -O3") +string(APPEND CMAKE_CXX_FLAGS "-Wall -Wextra -Os") # Set the folder for the executable set(EXECUTABLE_OUTPUT_PATH ../) From b762d8704ddefa715bb3b9b4f2952dd7ecec7f38 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 22:03:11 +0100 Subject: [PATCH 13/33] revrite type traits for `unique_ptr` --- src/dsm/utility/TypeTraits/is_agent.hpp | 2 +- src/dsm/utility/TypeTraits/is_itinerary.hpp | 2 +- src/dsm/utility/TypeTraits/is_node.hpp | 2 +- src/dsm/utility/TypeTraits/is_street.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dsm/utility/TypeTraits/is_agent.hpp b/src/dsm/utility/TypeTraits/is_agent.hpp index 2e040fbd..f1001d41 100644 --- a/src/dsm/utility/TypeTraits/is_agent.hpp +++ b/src/dsm/utility/TypeTraits/is_agent.hpp @@ -20,7 +20,7 @@ namespace dsm { struct is_agent> : std::true_type {}; template - struct is_agent>> : std::true_type {}; + struct is_agent>> : std::true_type {}; template inline constexpr bool is_agent_v = is_agent::value; diff --git a/src/dsm/utility/TypeTraits/is_itinerary.hpp b/src/dsm/utility/TypeTraits/is_itinerary.hpp index 1dc74e75..9d68c415 100644 --- a/src/dsm/utility/TypeTraits/is_itinerary.hpp +++ b/src/dsm/utility/TypeTraits/is_itinerary.hpp @@ -18,7 +18,7 @@ namespace dsm { struct is_itinerary> : std::true_type {}; template - struct is_itinerary>> : std::true_type {}; + struct is_itinerary>> : std::true_type {}; template inline constexpr bool is_itinerary_v = is_itinerary::value; diff --git a/src/dsm/utility/TypeTraits/is_node.hpp b/src/dsm/utility/TypeTraits/is_node.hpp index d3a0d8cf..e520ffb1 100644 --- a/src/dsm/utility/TypeTraits/is_node.hpp +++ b/src/dsm/utility/TypeTraits/is_node.hpp @@ -24,7 +24,7 @@ namespace dsm { struct is_node&> : std::true_type {}; template - struct is_node>> : std::true_type {}; + struct is_node>> : std::true_type {}; template inline constexpr bool is_node_v = is_node::value; diff --git a/src/dsm/utility/TypeTraits/is_street.hpp b/src/dsm/utility/TypeTraits/is_street.hpp index b1aae5d5..4ebd6b47 100644 --- a/src/dsm/utility/TypeTraits/is_street.hpp +++ b/src/dsm/utility/TypeTraits/is_street.hpp @@ -24,7 +24,7 @@ namespace dsm { struct is_street&> : std::true_type {}; template - struct is_street>> : std::true_type {}; + struct is_street>> : std::true_type {}; template inline constexpr bool is_street_v = is_street::value; From de4238ce2bc7d4b2337bea2082e6d43bd4d624a7 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 23:20:47 +0100 Subject: [PATCH 14/33] Fix `makeTrafficLight` --- src/dsm/headers/Graph.hpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index a04aabfb..3fd9f98f 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -555,15 +555,12 @@ namespace dsm { if (!m_nodes.contains(nodeId)) { throw std::invalid_argument(buildLog("Node does not exist.")); } - auto& pNode = m_nodes[nodeId]; - auto pTrafficLight = std::dynamic_pointer_cast>(pNode); - if (!pTrafficLight) { - pTrafficLight = std::make_shared>( - *static_cast*>(pNode.get())); - } - pNode = pTrafficLight; + auto& pNode = m_nodes[nodeId]; + pNode = std::make_unique>(pNode->id());; } + template + requires(std::unsigned_integral && std::unsigned_integral) void Graph::addStreet(std::shared_ptr> street) { if (m_streets.contains(street->id())) { throw std::invalid_argument(buildLog("Street with same id already exists.")); From e21a35160ed72969c544f03a43c3648c5d6d7c67 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Sat, 16 Mar 2024 23:20:58 +0100 Subject: [PATCH 15/33] Update type traits tests --- test/Test_graph.cpp | 3 +-- test/Test_is_node.cpp | 24 ++++++++++++------------ test/Test_is_street.cpp | 24 ++++++++++++------------ 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/test/Test_graph.cpp b/test/Test_graph.cpp index 10a8ce22..4d9dfae9 100644 --- a/test/Test_graph.cpp +++ b/test/Test_graph.cpp @@ -196,8 +196,7 @@ TEST_CASE("Graph") { WHEN("We make node 0 a traffic light") { graph.makeTrafficLight(0); THEN("The node 0 is a traffic light") { - CHECK(std::dynamic_pointer_cast>( - graph.nodeSet().at(0))); + CHECK(graph.nodeSet().at(0)->isTrafficLight()); } } } diff --git a/test/Test_is_node.cpp b/test/Test_is_node.cpp index d5f8ffe5..c5274185 100644 --- a/test/Test_is_node.cpp +++ b/test/Test_is_node.cpp @@ -15,12 +15,12 @@ static_assert(is_node>::value); static_assert(!is_node::value); static_assert(!is_node::value); static_assert(!is_node::value); -static_assert(is_node>>::value); -static_assert(is_node>>::value); -static_assert(is_node>>::value); -static_assert(!is_node>::value); -static_assert(!is_node>::value); -static_assert(!is_node>::value); +static_assert(is_node>>::value); +static_assert(is_node>>::value); +static_assert(is_node>>::value); +static_assert(!is_node>::value); +static_assert(!is_node>::value); +static_assert(!is_node>::value); // check the template variable static_assert(is_node_v>); @@ -29,9 +29,9 @@ static_assert(is_node_v>); static_assert(!is_node_v); static_assert(!is_node_v); static_assert(!is_node_v); -static_assert(is_node_v>>); -static_assert(is_node_v>>); -static_assert(is_node_v>>); -static_assert(!is_node_v>); -static_assert(!is_node_v>); -static_assert(!is_node_v>); +static_assert(is_node_v>>); +static_assert(is_node_v>>); +static_assert(is_node_v>>); +static_assert(!is_node_v>); +static_assert(!is_node_v>); +static_assert(!is_node_v>); diff --git a/test/Test_is_street.cpp b/test/Test_is_street.cpp index 8a0386bb..f659db8a 100644 --- a/test/Test_is_street.cpp +++ b/test/Test_is_street.cpp @@ -15,12 +15,12 @@ static_assert(is_street>::value); static_assert(!is_street::value); static_assert(!is_street::value); static_assert(!is_street::value); -static_assert(is_street>>::value); -static_assert(is_street>>::value); -static_assert(is_street>>::value); -static_assert(!is_street>::value); -static_assert(!is_street>::value); -static_assert(!is_street>::value); +static_assert(is_street>>::value); +static_assert(is_street>>::value); +static_assert(is_street>>::value); +static_assert(!is_street>::value); +static_assert(!is_street>::value); +static_assert(!is_street>::value); // check the template variable static_assert(is_street_v>); @@ -29,9 +29,9 @@ static_assert(is_street_v>); static_assert(!is_street_v); static_assert(!is_street_v); static_assert(!is_street_v); -static_assert(is_street_v>>); -static_assert(is_street_v>>); -static_assert(is_street_v>>); -static_assert(!is_street_v>); -static_assert(!is_street_v>); -static_assert(!is_street_v>); +static_assert(is_street_v>>); +static_assert(is_street_v>>); +static_assert(is_street_v>>); +static_assert(!is_street_v>); +static_assert(!is_street_v>); +static_assert(!is_street_v>); From 0557d56d3889d59ac27d18ccac1b8db402de84f5 Mon Sep 17 00:00:00 2001 From: Grufoony Date: Mon, 18 Mar 2024 10:23:32 +0100 Subject: [PATCH 16/33] Add TL constructor from Node to fix makeTrafficLight --- src/dsm/headers/Graph.hpp | 2 +- src/dsm/headers/Node.hpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index 3fd9f98f..9c84da02 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -556,7 +556,7 @@ namespace dsm { throw std::invalid_argument(buildLog("Node does not exist.")); } auto& pNode = m_nodes[nodeId]; - pNode = std::make_unique>(pNode->id());; + pNode = std::make_unique>(*pNode); } template diff --git a/src/dsm/headers/Node.hpp b/src/dsm/headers/Node.hpp index f685bbe4..fdbbbcda 100644 --- a/src/dsm/headers/Node.hpp +++ b/src/dsm/headers/Node.hpp @@ -266,6 +266,9 @@ namespace dsm { /// @brief Construct a new TrafficLight object /// @param id The node's id explicit TrafficLight(Id id); + /// @brief Construct a new TrafficLight object + /// @param node A Node object + TrafficLight(Node node); /// @brief Set the node's delay /// @details This function is used to set the node's delay. @@ -312,6 +315,15 @@ namespace dsm { std::unsigned_integral) TrafficLight::TrafficLight(Id id) : Node{id}, m_counter{0} {} + template + requires(std::unsigned_integral && std::unsigned_integral && + std::unsigned_integral) + TrafficLight::TrafficLight(Node node) + : Node{node.id()}, m_counter{0} { + this->setCoords(node.coords()); + this->setCapacity(node.capacity()); + } + template requires(std::unsigned_integral && std::unsigned_integral && std::unsigned_integral) From a0c39a2670ca85a990ce5798e8bfb2aba90c59c7 Mon Sep 17 00:00:00 2001 From: Grufoony Date: Mon, 18 Mar 2024 12:21:17 +0100 Subject: [PATCH 17/33] Fix bug in updatePaths --- src/dsm/headers/Dynamics.hpp | 12 +++++++----- test/Test_dynamics.cpp | 28 ++++++++++++---------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index a9916046..60b403b1 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -263,8 +263,7 @@ namespace dsm { requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) void Dynamics::m_evolveStreets() { - for (auto& streetPair : this->m_graph.streetSet()) { - const auto& street{streetPair.second}; + for (auto& [streetId, street] : this->m_graph.streetSet()) { if (street->queue().empty()) { continue; } @@ -278,7 +277,7 @@ namespace dsm { continue; } if (destinationNode->isTrafficLight() && - !destinationNode->isGreen(street->id())) { + !destinationNode->isGreen(streetId)) { continue; } destinationNode->addAgent(street->dequeue().value()); @@ -441,7 +440,7 @@ namespace dsm { continue; } // save the minimum distance between i and the destination - auto minDistance{result.value().distance()}; + const auto minDistance{result.value().distance()}; for (auto const& node : m_graph.adjMatrix().getRow(i)) { // init distance from a neighbor node to the destination to zero double distance{0.}; @@ -464,7 +463,10 @@ namespace dsm { } // if (!(distance > minDistance + expectedTravelTime)) { - if (!(distance > minDistance + streetLength)) { + if (minDistance == distance + streetLength) { + // std::cout << "minDistance: " << minDistance << " distance: " << distance + // << " streetLength: " << streetLength << '\n'; + // std::cout << "Inserting " << i << ';' << node.first << '\n'; path.insert(i, node.first, true); } } diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index 6b05723e..b9964a63 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -82,19 +82,19 @@ TEST_CASE("Dynamics") { ->destination(), Itinerary2.destination()); CHECK(dynamics.agents().at(0)->streetId().has_value()); - CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 3); + CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 6); CHECK_EQ(dynamics.itineraries() .at(dynamics.agents().at(1)->itineraryId()) ->destination(), Itinerary2.destination()); CHECK(dynamics.agents().at(1)->streetId().has_value()); - CHECK_EQ(dynamics.agents().at(1)->streetId().value(), 8); + CHECK_EQ(dynamics.agents().at(1)->streetId().value(), 1); CHECK_EQ(dynamics.itineraries() .at(dynamics.agents().at(2)->itineraryId()) ->destination(), Itinerary1.destination()); CHECK(dynamics.agents().at(2)->streetId().has_value()); - CHECK_EQ(dynamics.agents().at(2)->streetId().value(), 1); + CHECK_EQ(dynamics.agents().at(2)->streetId().value(), 8); } } } @@ -146,7 +146,6 @@ TEST_CASE("Dynamics") { CHECK_EQ(dynamics.itineraries().size(), 1); CHECK(dynamics.itineraries().at(0)->path()(0, 1)); CHECK(dynamics.itineraries().at(0)->path()(1, 2)); - CHECK(dynamics.itineraries().at(0)->path()(0, 2)); for (auto const& it : dynamics.itineraries()) { auto const& path = it.second->path(); for (uint16_t i{0}; i < path.getRowDim(); ++i) { @@ -234,23 +233,18 @@ TEST_CASE("Dynamics") { dynamics.addItinerary(itinerary); dynamics.updatePaths(); WHEN("We add an agent randomly and evolve the dynamics") { - dynamics.addAgentsUniformly(1); + dynamics.addAgent(Agent(0, 0, 0)); dynamics.evolve(false); - THEN("The agent evolves") { - CHECK_EQ(dynamics.agents().at(0)->time(), 1); - CHECK_EQ(dynamics.agents().at(0)->delay(), 1); - CHECK(dynamics.agents().at(0)->streetId().has_value()); - CHECK_EQ(dynamics.agents().at(0)->speed(), 13.8888888889); - } dynamics.evolve(false); - THEN("The agent evolves again") { + THEN("The agent evolves") { CHECK_EQ(dynamics.agents().at(0)->time(), 2); CHECK_EQ(dynamics.agents().at(0)->delay(), 0); + CHECK(dynamics.agents().at(0)->streetId().has_value()); CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 1); CHECK_EQ(dynamics.agents().at(0)->speed(), 13.8888888889); } dynamics.evolve(false); - THEN("And again, changing street") { + THEN("The agent evolves again, changing street") { CHECK_EQ(dynamics.agents().at(0)->time(), 3); CHECK_EQ(dynamics.agents().at(0)->delay(), 0); CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 5); @@ -344,16 +338,16 @@ TEST_CASE("Dynamics") { dynamics.updatePaths(); dynamics.addAgent(Agent(0, 0, 0)); WHEN("We evolve the dynamics") { - dynamics.evolve(false); + // dynamics.evolve(false); THEN( "The agent is ready to go through the traffic light at time 3 but the " "traffic light is red" " until time 4, so the agent waits until time 4") { for (uint8_t i{0}; i < 5; ++i) { dynamics.evolve(false); - if (i < 3) { + if (i > 0 && i < 3) { CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 1); - } else { + } else if (i > 2) { CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 7); } if (i == 2) { @@ -361,6 +355,8 @@ TEST_CASE("Dynamics") { } } CHECK_EQ(dynamics.agents().at(0)->distance(), 60.); + dynamics.evolve(false); + CHECK_EQ(dynamics.agents().size(), 0); } } } From eda256577cfe21175465d31c5c58b0981c97d55c Mon Sep 17 00:00:00 2001 From: Grufoony Date: Mon, 18 Mar 2024 14:21:59 +0100 Subject: [PATCH 18/33] Update --- src/dsm/headers/Dynamics.hpp | 39 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 60b403b1..49099498 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -292,19 +292,18 @@ namespace dsm { By doing the angle difference, if the destination street is the same we can basically compare these differences (mod(pi)!, i.e. delta % std::numbers::pi): the smaller goes first. Anyway, this is not trivial as it seems so I will leave it as a comment.*/ - for (auto& nodePair : this->m_graph.nodeSet()) { - auto& node{nodePair.second}; - for (const auto agent : node->agents()) { - Size agentId{agent.second}; - if (node->id() == - this->m_itineraries[this->m_agents[agentId]->itineraryId()]->destination()) { + for (auto& [nodeId, node] : this->m_graph.nodeSet()) { + for (const auto [priority, agentId] : node->agents()) { + auto& agent = m_agents[agentId]; + if (nodeId == + this->m_itineraries[agent->itineraryId()]->destination()) { node->removeAgent(agentId); - this->m_travelTimes.push_back(this->m_agents[agentId]->time()); + this->m_travelTimes.push_back(agent->time()); if (reinsert_agents) { - Agent newAgent{this->m_agents[agentId]->id(), - this->m_agents[agentId]->itineraryId()}; - if (this->m_agents[agentId]->srcNodeId().has_value()) { - newAgent.setSourceNodeId(this->m_agents[agentId]->srcNodeId().value()); + Agent newAgent{agent->id(), + agent->itineraryId()}; + if (agent->srcNodeId().has_value()) { + newAgent.setSourceNodeId(agent->srcNodeId().value()); } this->removeAgent(agentId); this->addAgent(newAgent); @@ -314,8 +313,8 @@ namespace dsm { continue; } auto possibleMoves{ - this->m_itineraries[this->m_agents[agentId]->itineraryId()]->path().getRow( - node->id(), true)}; + this->m_itineraries[agent->itineraryId()]->path().getRow( + nodeId, true)}; if (this->m_uniformDist(this->m_generator) < this->m_errorProbability) { possibleMoves = this->m_graph.adjMatrix().getRow(node->id(), true); } @@ -327,16 +326,16 @@ namespace dsm { const auto p{moveDist(this->m_generator)}; auto iterator = possibleMoves.begin(); std::advance(iterator, p); - const auto nextStreetId{iterator->first}; - auto& nextStreet{this->m_graph.streetSet()[nextStreetId]}; + + auto& nextStreet{this->m_graph.streetSet()[iterator->first]}; if (nextStreet->density() < 1) { node->removeAgent(agentId); - this->m_agents[agentId]->setStreetId(nextStreet->id()); - this->setAgentSpeed(this->m_agents[agentId]->id()); - this->m_agents[agentId]->incrementDelay( - std::ceil(nextStreet->length() / this->m_agents[agentId]->speed())); - nextStreet->enqueue(this->m_agents[agentId]->id()); + agent->setStreetId(nextStreet->id()); + this->setAgentSpeed(agentId); + agent->incrementDelay( + std::ceil(nextStreet->length() / agent->speed())); + nextStreet->enqueue(agentId); } else { break; } From 74e4f780708909b64e597a46b10a1952cb51334a Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:13:42 +0100 Subject: [PATCH 19/33] Code refactor --- src/dsm/headers/Dynamics.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 49099498..304d968b 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -440,7 +440,7 @@ namespace dsm { } // save the minimum distance between i and the destination const auto minDistance{result.value().distance()}; - for (auto const& node : m_graph.adjMatrix().getRow(i)) { + for (const auto& node : m_graph.adjMatrix().getRow(i)) { // init distance from a neighbor node to the destination to zero double distance{0.}; From cebdd9a504a1947139e3fc3a1aaf839981840d1f Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:14:10 +0100 Subject: [PATCH 20/33] New test. Should fix #151 --- test/Test_dynamics.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index b9964a63..9bd59499 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -146,6 +146,7 @@ TEST_CASE("Dynamics") { CHECK_EQ(dynamics.itineraries().size(), 1); CHECK(dynamics.itineraries().at(0)->path()(0, 1)); CHECK(dynamics.itineraries().at(0)->path()(1, 2)); + CHECK_FALSE(dynamics.itineraries().at(0)->path()(0, 2)); for (auto const& it : dynamics.itineraries()) { auto const& path = it.second->path(); for (uint16_t i{0}; i < path.getRowDim(); ++i) { From 5f3b4d5147885f1f8c2010a36f9d86a40aff0410 Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:17:09 +0100 Subject: [PATCH 21/33] I hate `nullptr` --- test/Test_graph.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Test_graph.cpp b/test/Test_graph.cpp index 4d9dfae9..a38b4bd6 100644 --- a/test/Test_graph.cpp +++ b/test/Test_graph.cpp @@ -181,12 +181,12 @@ TEST_CASE("Graph") { Street street{1, 1, 1., std::make_pair(0, 1)}; graph.addStreet(street); auto result = graph.street(0, 1); - CHECK(result != nullptr); + CHECK(result); const auto& street2 = *result; CHECK_EQ(street2->id(), 1); CHECK_EQ(street2->length(), 1.); CHECK_EQ(street2->capacity(), 1); - CHECK_EQ(graph.street(1, 0), nullptr); + CHECK_FALSE(graph.street(1, 0)); } SUBCASE("make trafficlight") { GIVEN("A graph object with two nodes and one street") { @@ -439,12 +439,12 @@ TEST_CASE("Dijkstra") { Street street{1, 1, 1., std::make_pair(0, 1)}; graph.addStreet(street); auto result = graph.street(0, 1); - CHECK(result != nullptr); + CHECK(result); const auto& street2 = *result; CHECK_EQ(street2->id(), 1); CHECK_EQ(street2->length(), 1.); CHECK_EQ(street2->capacity(), 1); - CHECK_EQ(graph.street(1, 0), nullptr); + CHECK_FALSE(graph.street(1, 0)); } SUBCASE("equal length") { From b9241cdd6fd6a779541d6ea1976da99c0a2df476 Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:20:26 +0100 Subject: [PATCH 22/33] Changed `auto&` to `const auto&` --- src/dsm/headers/Dynamics.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 304d968b..21ccc0f9 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -369,7 +369,7 @@ namespace dsm { } } else if (!agent->streetId().has_value()) { assert(agent->srcNodeId().has_value()); - auto& srcNode{this->m_graph.nodeSet()[agent->srcNodeId().value()]}; + const auto& srcNode{this->m_graph.nodeSet()[agent->srcNodeId().value()]}; if (srcNode->isFull()) { continue; } @@ -883,7 +883,7 @@ namespace dsm { std::unsigned_integral) std::optional FirstOrderDynamics::streetMeanSpeed( Id streetId) const { - auto& street{this->m_graph.streetSet().at(streetId)}; + const auto& street{this->m_graph.streetSet().at(streetId)}; if (street->queue().empty() || !street->isSpire()) { return std::nullopt; } From 6177c67bcfc2bc757a95193198b22f0c1deadf98 Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:31:32 +0100 Subject: [PATCH 23/33] Reworked old `isSpire` --- src/dsm/headers/Dynamics.hpp | 17 +---------------- src/dsm/headers/Street.hpp | 35 ++++++++--------------------------- test/Test_dynamics.cpp | 2 -- 3 files changed, 9 insertions(+), 45 deletions(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 21ccc0f9..88575b25 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -742,9 +742,6 @@ namespace dsm { m_graph.streetSet().cend(), 0., [](double sum, const auto& street) { - if (!street.second->isSpire()) { - return sum; - } return sum + street.second->density(); }) / m_graph.streetSet().size()}; @@ -753,9 +750,6 @@ namespace dsm { m_graph.streetSet().cend(), 0., [mean](double sum, const auto& street) { - if (!street.second->isSpire()) { - return sum; - } return sum + std::pow(street.second->density() - mean, 2); }) / (m_graph.streetSet().size() - 1)}; @@ -768,9 +762,6 @@ namespace dsm { std::vector flows; flows.reserve(m_graph.streetSet().size()); for (const auto& [streetId, street] : m_graph.streetSet()) { - if (!street->isSpire()) { - continue; - } auto speedOpt{this->streetMeanSpeed(streetId)}; if (speedOpt.has_value()) { double flow{street->density() * speedOpt.value()}; @@ -789,9 +780,6 @@ namespace dsm { std::vector flows; flows.reserve(m_graph.streetSet().size()); for (const auto& [streetId, street] : m_graph.streetSet()) { - if (!street->isSpire()) { - continue; - } if (above && street->density() > threshold) { auto speedOpt{this->streetMeanSpeed(streetId)}; if (speedOpt.has_value()) { @@ -884,7 +872,7 @@ namespace dsm { std::optional FirstOrderDynamics::streetMeanSpeed( Id streetId) const { const auto& street{this->m_graph.streetSet().at(streetId)}; - if (street->queue().empty() || !street->isSpire()) { + if (street->queue().empty()) { return std::nullopt; } if (this->m_agents.at(street->queue().front())->delay() > 0) { @@ -927,9 +915,6 @@ namespace dsm { std::vector speeds; speeds.reserve(this->m_graph.streetSet().size()); for (const auto& [streetId, street] : this->m_graph.streetSet()) { - if (!street->isSpire()) { - continue; - } if (above) { if (street->density() > threshold) { auto speedOpt{this->streetMeanSpeed(streetId)}; diff --git a/src/dsm/headers/Street.hpp b/src/dsm/headers/Street.hpp index 37d24009..e513fab0 100644 --- a/src/dsm/headers/Street.hpp +++ b/src/dsm/headers/Street.hpp @@ -39,7 +39,6 @@ namespace dsm { Id m_id; Size m_capacity; Size m_transportCapacity; - bool m_isSpire; public: Street() = delete; /// @brief Construct a new Street object starting from an existing street @@ -69,8 +68,6 @@ namespace dsm { /// @param nodePair The street's node pair Street(Id id, Size capacity, double len, double maxSpeed, std::pair nodePair); - virtual ~Street() = default; - /// @brief Set the street's id /// @param id The street's id void setId(Id id); @@ -112,11 +109,6 @@ namespace dsm { /// @param angle The street's angle /// @throw std::invalid_argument If the angle is negative or greater than 2 * pi void setAngle(double angle); - /// @brief Set the street's spire status - /// @param isSpire The street's spire status - /// @details A spire is a street from which you can extract data, e.g. density. However, this - /// parameter must be managed by the dynamics. - void setIsSpire(bool isSpire); /// @brief Get the street's id /// @return Id, The street's id @@ -155,7 +147,7 @@ namespace dsm { virtual std::optional dequeue(); /// @brief Check if the street is a spire /// @return bool True if the street is a spire, false otherwise - bool isSpire() const; + virtual bool isSpire() const { return false; }; }; template @@ -167,8 +159,7 @@ namespace dsm { m_angle{street.angle()}, m_id{id}, m_capacity{street.capacity()}, - m_transportCapacity{street.transportCapacity()}, - m_isSpire{true} {} + m_transportCapacity{street.transportCapacity()} {} template requires(std::unsigned_integral && std::unsigned_integral) @@ -179,8 +170,7 @@ namespace dsm { m_angle{0.}, m_id{index}, m_capacity{1}, - m_transportCapacity{std::numeric_limits::max()}, - m_isSpire{true} {} + m_transportCapacity{std::numeric_limits::max()} {} template requires(std::unsigned_integral && std::unsigned_integral) @@ -191,8 +181,7 @@ namespace dsm { m_angle{0.}, m_id{id}, m_capacity{capacity}, - m_transportCapacity{std::numeric_limits::max()}, - m_isSpire{true} {} + m_transportCapacity{std::numeric_limits::max()} {} template requires(std::unsigned_integral && std::unsigned_integral) @@ -203,8 +192,7 @@ namespace dsm { m_angle{0.}, m_id{id}, m_capacity{capacity}, - m_transportCapacity{std::numeric_limits::max()}, - m_isSpire{true} { + m_transportCapacity{std::numeric_limits::max()} { this->setMaxSpeed(maxSpeed); } @@ -281,11 +269,6 @@ namespace dsm { } m_angle = angle; } - template - requires(std::unsigned_integral && std::unsigned_integral) - void Street::setIsSpire(bool isSpire) { - m_isSpire = isSpire; - } template requires(std::unsigned_integral && std::unsigned_integral) @@ -355,11 +338,6 @@ namespace dsm { m_queue.pop(); return id; } - template - requires(std::unsigned_integral && std::unsigned_integral) - bool Street::isSpire() const { - return m_isSpire; - } /// @brief The SpireStreet class represents a street which is able to count agent flows in both input and output. /// @tparam Id The type of the street's id @@ -412,6 +390,9 @@ namespace dsm { /// @brief Remove an agent from the street's queue /// @return std::optional The id of the agent removed from the street's queue std::optional dequeue() override; + /// @brief Check if the street is a spire + /// @return bool True if the street is a spire, false otherwise + bool isSpire() const override { return true; }; }; template diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index 9bd59499..e867b2a0 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -439,7 +439,5 @@ TEST_CASE("Dynamics") { CHECK_EQ(dynamics.graph().streetSet().at(1)->queue().size(), 3); CHECK(dynamics.streetMeanSpeed(1).has_value()); CHECK_EQ(dynamics.streetMeanSpeed(1).value(), meanSpeed); - dynamics.graph().streetSet().at(1)->setIsSpire(false); - CHECK_FALSE(dynamics.streetMeanSpeed(1).has_value()); } } From b7e32591d60e20ce853d6b6efbe8ed20fd6be272 Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:43:14 +0100 Subject: [PATCH 24/33] Update version --- CMakeLists.txt | 2 +- test/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d1de5684..76198412 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.0) -project(dms VERSION 1.1.5 LANGUAGES CXX) +project(dms VERSION 1.2.0 LANGUAGES CXX) # set the C++ standard set(CMAKE_CXX_STANDARD 20) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b8fb5df7..b866058f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.0) -project(dsm_tests VERSION 1.1.5 LANGUAGES CXX) +project(dsm_tests VERSION 1.2.0 LANGUAGES CXX) # Set the C++ standard set(CMAKE_CXX_STANDARD 20) From 939c11a0a9ba806d71fe983ce1be969ec8bb528e Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:01:41 +0100 Subject: [PATCH 25/33] Add `makeSpireStreet` method to graph --- src/dsm/headers/Graph.hpp | 13 +++++++++++++ test/Test_graph.cpp | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index 9c84da02..a1e11ce7 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -149,6 +149,10 @@ namespace dsm { template requires(std::unsigned_integral) void makeTrafficLight(Id nodeId); + /// @brief Convert an existing street into a spire street + /// @param streetId The id of the street to convert to a spire street + /// @throws std::invalid_argument if the street does not exist + void makeSpireStreet(Id streetId); /// @brief Add a street to the graph /// @param street A std::shared_ptr to the street to add @@ -558,6 +562,15 @@ namespace dsm { auto& pNode = m_nodes[nodeId]; pNode = std::make_unique>(*pNode); } + template + requires(std::unsigned_integral && std::unsigned_integral) + void Graph::makeSpireStreet(Id streetId) { + if (!m_streets.contains(streetId)) { + throw std::invalid_argument(buildLog("Street does not exist.")); + } + auto& pStreet = m_streets[streetId]; + pStreet = std::make_unique>(pStreet->id(), *pStreet); + } template requires(std::unsigned_integral && std::unsigned_integral) diff --git a/test/Test_graph.cpp b/test/Test_graph.cpp index a38b4bd6..c9b2ed7c 100644 --- a/test/Test_graph.cpp +++ b/test/Test_graph.cpp @@ -201,6 +201,20 @@ TEST_CASE("Graph") { } } } + SUBCASE("make spire street") { + GIVEN("A graph object with two nodes and one street") { + Graph graph{}; + graph.addStreet(Street{0, 1, 1., std::make_pair(0, 1)}); + graph.buildAdj(); + WHEN("We make the street a spire street") { + graph.makeSpireStreet(1); + THEN("The street is a spire street") { + CHECK(graph.streetSet().at(1)->isSpire()); + } + } + } + + } } TEST_CASE("Dijkstra") { From ca487e7636d0cdbbea5a01c140ba5b620adb7455 Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:02:06 +0100 Subject: [PATCH 26/33] Update version --- CMakeLists.txt | 2 +- test/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d1de5684..5ec23f7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.0) -project(dms VERSION 1.1.5 LANGUAGES CXX) +project(dms VERSION 1.2.1 LANGUAGES CXX) # set the C++ standard set(CMAKE_CXX_STANDARD 20) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b8fb5df7..388eee5d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.0) -project(dsm_tests VERSION 1.1.5 LANGUAGES CXX) +project(dsm_tests VERSION 1.2.1 LANGUAGES CXX) # Set the C++ standard set(CMAKE_CXX_STANDARD 20) From a2d16eaa728e3e05d7d271dc64d1b651d3964a1f Mon Sep 17 00:00:00 2001 From: Grufoony <64806874+Grufoony@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:41:51 +0100 Subject: [PATCH 27/33] Rework graph getter --- src/dsm/headers/Dynamics.hpp | 6 +++--- src/dsm/headers/Street.hpp | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 88575b25..f68f8fda 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -134,7 +134,7 @@ namespace dsm { /// @brief Get the graph /// @return const Graph&, The graph - const Graph& graph() const; + Graph& graph(); /// @brief Get the itineraries /// @return const std::unordered_map>&, The itineraries const std::unordered_map>>& itineraries() const; @@ -255,7 +255,7 @@ namespace dsm { is_numeric_v) Dynamics::Dynamics(const Graph& graph) : m_time{0}, - m_graph{graph}, + m_graph{std::move(graph)}, m_errorProbability{0.}, m_minSpeedRateo{0.} {} @@ -491,7 +491,7 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) - const Graph& Dynamics::graph() const { + Graph& Dynamics::graph() { return m_graph; } diff --git a/src/dsm/headers/Street.hpp b/src/dsm/headers/Street.hpp index e513fab0..e65fb798 100644 --- a/src/dsm/headers/Street.hpp +++ b/src/dsm/headers/Street.hpp @@ -68,6 +68,8 @@ namespace dsm { /// @param nodePair The street's node pair Street(Id id, Size capacity, double len, double maxSpeed, std::pair nodePair); + virtual ~Street() = default; + /// @brief Set the street's id /// @param id The street's id void setId(Id id); From 0f0801c3da957072b5caae0c164a7f31e8a9872f Mon Sep 17 00:00:00 2001 From: Grufoony Date: Wed, 20 Mar 2024 09:25:44 +0100 Subject: [PATCH 28/33] New test --- test/Test_dynamics.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index 9bd59499..e03d4695 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -21,6 +21,7 @@ TEST_CASE("Dynamics") { GIVEN("A graph object") { auto graph = Graph{}; graph.importMatrix("./data/matrix.dsm"); + graph.buildAdj(); WHEN("A dynamics object is created") { Dynamics dynamics(graph); THEN("The node and the street sets are the same") { @@ -38,6 +39,13 @@ TEST_CASE("Dynamics") { CHECK_EQ(dynamics.meanTravelTime().error, 0.); } } + WHEN("We transform a node into a traffic light and create the dynamics") { + graph.makeTrafficLight(0); + Dynamics dynamics(graph); + THEN("The node is a traffic light") { + CHECK(dynamics.graph().nodeSet().at(0)->isTrafficLight()); + } + } } } SUBCASE("addAgentsUniformly") { From dbb8cbab6b2b360f722160c2a05693dfba451e46 Mon Sep 17 00:00:00 2001 From: Grufoony Date: Wed, 20 Mar 2024 09:41:38 +0100 Subject: [PATCH 29/33] Fix new test --- src/dsm/headers/Dynamics.hpp | 10 +++++----- test/Test_dynamics.cpp | 14 ++++++-------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 21ccc0f9..f2b92a7a 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -98,7 +98,7 @@ namespace dsm { Dynamics() = delete; /// @brief Construct a new Dynamics object /// @param graph The graph representing the network - Dynamics(const Graph& graph); + Dynamics(Graph& graph); /// @brief Set the itineraries /// @param itineraries The itineraries @@ -253,9 +253,9 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) - Dynamics::Dynamics(const Graph& graph) + Dynamics::Dynamics(Graph& graph) : m_time{0}, - m_graph{graph}, + m_graph{std::move(graph)}, m_errorProbability{0.}, m_minSpeedRateo{0.} {} @@ -842,7 +842,7 @@ namespace dsm { FirstOrderDynamics() = delete; /// @brief Construct a new First Order Dynamics object /// @param graph, The graph representing the network - FirstOrderDynamics(const Graph& graph); + FirstOrderDynamics(Graph& graph); /// @brief Set the speed of an agent /// @param agentId The id of the agent /// @throw std::invalid_argument, If the agent is not found @@ -866,7 +866,7 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral && std::unsigned_integral) - FirstOrderDynamics::FirstOrderDynamics(const Graph& graph) + FirstOrderDynamics::FirstOrderDynamics(Graph& graph) : Dynamics(graph) {} template diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index e03d4695..ef728840 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -90,19 +90,19 @@ TEST_CASE("Dynamics") { ->destination(), Itinerary2.destination()); CHECK(dynamics.agents().at(0)->streetId().has_value()); - CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 6); + CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 3); CHECK_EQ(dynamics.itineraries() .at(dynamics.agents().at(1)->itineraryId()) ->destination(), Itinerary2.destination()); CHECK(dynamics.agents().at(1)->streetId().has_value()); - CHECK_EQ(dynamics.agents().at(1)->streetId().value(), 1); + CHECK_EQ(dynamics.agents().at(1)->streetId().value(), 8); CHECK_EQ(dynamics.itineraries() .at(dynamics.agents().at(2)->itineraryId()) ->destination(), Itinerary1.destination()); CHECK(dynamics.agents().at(2)->streetId().has_value()); - CHECK_EQ(dynamics.agents().at(2)->streetId().value(), 8); + CHECK_EQ(dynamics.agents().at(2)->streetId().value(), 1); } } } @@ -347,16 +347,16 @@ TEST_CASE("Dynamics") { dynamics.updatePaths(); dynamics.addAgent(Agent(0, 0, 0)); WHEN("We evolve the dynamics") { - // dynamics.evolve(false); + dynamics.evolve(false); THEN( "The agent is ready to go through the traffic light at time 3 but the " "traffic light is red" " until time 4, so the agent waits until time 4") { for (uint8_t i{0}; i < 5; ++i) { dynamics.evolve(false); - if (i > 0 && i < 3) { + if (i < 3) { CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 1); - } else if (i > 2) { + } else { CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 7); } if (i == 2) { @@ -364,8 +364,6 @@ TEST_CASE("Dynamics") { } } CHECK_EQ(dynamics.agents().at(0)->distance(), 60.); - dynamics.evolve(false); - CHECK_EQ(dynamics.agents().size(), 0); } } } From 029c38db53b0c92c013c7f3da748b8bc74e5d81d Mon Sep 17 00:00:00 2001 From: Grufoony Date: Wed, 20 Mar 2024 09:47:38 +0100 Subject: [PATCH 30/33] Revert graph getter constness --- src/dsm/headers/Dynamics.hpp | 9 +-------- test/Test_dynamics.cpp | 5 +++++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 340c92f9..1c78008b 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -134,7 +134,7 @@ namespace dsm { /// @brief Get the graph /// @return const Graph&, The graph - Graph& graph(); + const Graph& graph() { return m_graph; }; /// @brief Get the itineraries /// @return const std::unordered_map>&, The itineraries const std::unordered_map>>& itineraries() const; @@ -488,13 +488,6 @@ namespace dsm { ++this->m_time; } - template - requires(std::unsigned_integral && std::unsigned_integral && - is_numeric_v) - Graph& Dynamics::graph() { - return m_graph; - } - template requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index 8c5a1c64..d58cfdfb 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -46,6 +46,11 @@ TEST_CASE("Dynamics") { CHECK(dynamics.graph().nodeSet().at(0)->isTrafficLight()); } } + WHEN("We transorm a street into a spire and create the dynamcis") { + graph.makeSpireStreet(8); + Dynamics dynamics(graph); + THEN("The street is a spire") { CHECK(dynamics.graph().streetSet().at(8)->isSpire()); } + } } } SUBCASE("addAgentsUniformly") { From 02d03319fd2e35d34fd02e914d247784fccad52f Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Wed, 27 Mar 2024 22:27:54 +0100 Subject: [PATCH 31/33] Formatting --- src/dsm/headers/Graph.hpp | 2 +- src/dsm/headers/Street.hpp | 20 +++++++++++++++----- test/Test_dynamics.cpp | 4 +++- test/Test_graph.cpp | 1 - 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index 11631583..ef199c50 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -564,7 +564,7 @@ namespace dsm { if (!m_nodes.contains(nodeId)) { throw std::invalid_argument(buildLog("Node does not exist.")); } - auto& pNode = m_nodes[nodeId]; + auto& pNode = m_nodes[nodeId]; pNode = std::make_unique>(*pNode); } template diff --git a/src/dsm/headers/Street.hpp b/src/dsm/headers/Street.hpp index e65fb798..533b6676 100644 --- a/src/dsm/headers/Street.hpp +++ b/src/dsm/headers/Street.hpp @@ -39,6 +39,7 @@ namespace dsm { Id m_id; Size m_capacity; Size m_transportCapacity; + public: Street() = delete; /// @brief Construct a new Street object starting from an existing street @@ -350,6 +351,7 @@ namespace dsm { private: Size m_agentCounterIn; Size m_agentCounterOut; + public: SpireStreet() = delete; /// @brief Construct a new SpireStreet object starting from an existing street @@ -368,7 +370,8 @@ namespace dsm { /// @param len The street's length /// @param maxSpeed The street's speed limit /// @param nodePair The street's node pair - SpireStreet(Id id, Size capacity, double len, double maxSpeed, std::pair nodePair); + SpireStreet( + Id id, Size capacity, double len, double maxSpeed, std::pair nodePair); ~SpireStreet() = default; /// @brief Add an agent to the street's queue @@ -404,14 +407,21 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral) - SpireStreet::SpireStreet(Id id, Size capacity, double len, std::pair nodePair) - : Street(id, capacity, len, nodePair), m_agentCounterIn{0}, m_agentCounterOut{0} {} + SpireStreet::SpireStreet(Id id, + Size capacity, + double len, + std::pair nodePair) + : Street(id, capacity, len, nodePair), + m_agentCounterIn{0}, + m_agentCounterOut{0} {} template requires(std::unsigned_integral && std::unsigned_integral) SpireStreet::SpireStreet( Id id, Size capacity, double len, double maxSpeed, std::pair nodePair) - : Street(id, capacity, len, maxSpeed, nodePair), m_agentCounterIn{0}, m_agentCounterOut{0} {} + : Street(id, capacity, len, maxSpeed, nodePair), + m_agentCounterIn{0}, + m_agentCounterOut{0} {} template requires(std::unsigned_integral && std::unsigned_integral) @@ -441,7 +451,7 @@ namespace dsm { int SpireStreet::meanFlow() { int flow = static_cast(m_agentCounterIn) - static_cast(m_agentCounterOut); m_agentCounterIn = 0; - m_agentCounterOut = 0; + m_agentCounterOut = 0; return flow; } diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index d58cfdfb..043af5cd 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -49,7 +49,9 @@ TEST_CASE("Dynamics") { WHEN("We transorm a street into a spire and create the dynamcis") { graph.makeSpireStreet(8); Dynamics dynamics(graph); - THEN("The street is a spire") { CHECK(dynamics.graph().streetSet().at(8)->isSpire()); } + THEN("The street is a spire") { + CHECK(dynamics.graph().streetSet().at(8)->isSpire()); + } } } } diff --git a/test/Test_graph.cpp b/test/Test_graph.cpp index c9b2ed7c..2dac2312 100644 --- a/test/Test_graph.cpp +++ b/test/Test_graph.cpp @@ -213,7 +213,6 @@ TEST_CASE("Graph") { } } } - } } From 6249f77485352b54623dccce2307a26534b3b2b6 Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Wed, 27 Mar 2024 22:29:32 +0100 Subject: [PATCH 32/33] Format --- src/dsm/headers/Node.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dsm/headers/Node.hpp b/src/dsm/headers/Node.hpp index 013c6855..3f898cf4 100644 --- a/src/dsm/headers/Node.hpp +++ b/src/dsm/headers/Node.hpp @@ -82,7 +82,7 @@ namespace dsm { virtual bool isGreen() const; virtual bool isGreen(Id) const; - virtual void increaseCounter() {}; + virtual void increaseCounter(){}; virtual bool isTrafficLight() const { return false; } From 8616ad3e79f1bcc394acba33d9d2ed03dad547ea Mon Sep 17 00:00:00 2001 From: Simone Balducci Date: Wed, 27 Mar 2024 22:31:58 +0100 Subject: [PATCH 33/33] Add `const` qualifier to getter --- src/dsm/headers/Dynamics.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 75c26a07..c89d42ea 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -134,7 +134,7 @@ namespace dsm { /// @brief Get the graph /// @return const Graph&, The graph - const Graph& graph() { return m_graph; }; + const Graph& graph() const { return m_graph; }; /// @brief Get the itineraries /// @return const std::unordered_map>&, The itineraries const std::unordered_map>>& itineraries() const;