Skip to content

Add SpireStreet class #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 38 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f9d21d2
Add `SpireStreet` class
Grufoony Mar 16, 2024
51991e3
Remove aliases for smart pointers
sbaldu Mar 15, 2024
ff33807
Update nomenclature
sbaldu Mar 15, 2024
823e762
Add virtual destructor for `Node`
sbaldu Mar 16, 2024
d656f6e
Add `isTrafficLight` method for nodes
sbaldu Mar 16, 2024
ea91847
Substitute shared with unique and remove useless pointers
sbaldu Mar 16, 2024
a053228
Update tests
sbaldu Mar 16, 2024
c8aa014
Fix typo and specify special constructors for `Graph`
sbaldu Mar 16, 2024
8697ca8
Write copy constructor/assign for `Graph`
sbaldu Mar 16, 2024
0544298
Fix non-const methods in `SparseMatrix` class
Grufoony Mar 16, 2024
542fce9
Update profiling main
sbaldu Mar 16, 2024
7245ebe
Change `-O3` to '-Os' for profiling
sbaldu Mar 16, 2024
b762d87
revrite type traits for `unique_ptr`
sbaldu Mar 16, 2024
de4238c
Fix `makeTrafficLight`
sbaldu Mar 16, 2024
e21a351
Update type traits tests
sbaldu Mar 16, 2024
0557d56
Add TL constructor from Node to fix makeTrafficLight
Grufoony Mar 18, 2024
a0c39a2
Fix bug in updatePaths
Grufoony Mar 18, 2024
eda2565
Update
Grufoony Mar 18, 2024
74e4f78
Code refactor
Grufoony Mar 18, 2024
cebdd9a
New test. Should fix #151
Grufoony Mar 18, 2024
5f3b4d5
I hate `nullptr`
Grufoony Mar 18, 2024
b9241cd
Changed `auto&` to `const auto&`
Grufoony Mar 18, 2024
40529a5
Merge branch 'rework_shared_to_unique' into feature_add_SpireStreet
Grufoony Mar 18, 2024
6177c67
Reworked old `isSpire`
Grufoony Mar 18, 2024
b7e3259
Update version
Grufoony Mar 18, 2024
939c11a
Add `makeSpireStreet` method to graph
Grufoony Mar 18, 2024
ca487e7
Update version
Grufoony Mar 18, 2024
a2d16ea
Rework graph getter
Grufoony Mar 18, 2024
0f0801c
New test
Grufoony Mar 20, 2024
dbb8cba
Fix new test
Grufoony Mar 20, 2024
5b68a56
Merge branch 'rework_shared_to_unique' into feature_add_SpireStreet
Grufoony Mar 20, 2024
029c38d
Revert graph getter constness
Grufoony Mar 20, 2024
2f03d41
Merge branch 'main' into feature_add_SpireStreet
Grufoony Mar 20, 2024
7c3788e
Merge branch 'main' into feature_add_SpireStreet
Grufoony Mar 20, 2024
f174a11
Merge branch 'main' into feature_add_SpireStreet
Grufoony Mar 27, 2024
02d0331
Formatting
sbaldu Mar 27, 2024
6249f77
Format
sbaldu Mar 27, 2024
8616ad3
Add `const` qualifier to getter
sbaldu Mar 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16.0)

project(dms VERSION 1.2.0 LANGUAGES CXX)
project(dms VERSION 1.2.1 LANGUAGES CXX)

# set the C++ standard
set(CMAKE_CXX_STANDARD 20)
Expand Down
26 changes: 2 additions & 24 deletions src/dsm/headers/Dynamics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ namespace dsm {

/// @brief Get the graph
/// @return const Graph<Id, Size>&, The graph
const Graph<Id, Size>& graph() const;
const Graph<Id, Size>& graph() const { return m_graph; };
/// @brief Get the itineraries
/// @return const std::unordered_map<Id, Itinerary<Id>>&, The itineraries
const std::unordered_map<Id, std::unique_ptr<Itinerary<Id>>>& itineraries() const;
Expand Down Expand Up @@ -483,13 +483,6 @@ namespace dsm {
++this->m_time;
}

template <typename Id, typename Size, typename Delay>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size> &&
is_numeric_v<Delay>)
const Graph<Id, Size>& Dynamics<Id, Size, Delay>::graph() const {
return m_graph;
}

template <typename Id, typename Size, typename Delay>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size> &&
is_numeric_v<Delay>)
Expand Down Expand Up @@ -737,9 +730,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()};
Expand All @@ -748,9 +738,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)};
Expand All @@ -763,9 +750,6 @@ namespace dsm {
std::vector<double> 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()};
Expand All @@ -784,9 +768,6 @@ namespace dsm {
std::vector<double> 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()) {
Expand Down Expand Up @@ -880,7 +861,7 @@ namespace dsm {
std::optional<double> FirstOrderDynamics<Id, Size, Delay>::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) {
Expand Down Expand Up @@ -923,9 +904,6 @@ namespace dsm {
std::vector<double> 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)};
Expand Down
13 changes: 13 additions & 0 deletions src/dsm/headers/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ namespace dsm {
template <typename Delay>
requires(std::unsigned_integral<Delay>)
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
Expand Down Expand Up @@ -563,6 +567,15 @@ namespace dsm {
auto& pNode = m_nodes[nodeId];
pNode = std::make_unique<TrafficLight<Id, Size, Delay>>(*pNode);
}
template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
void Graph<Id, Size>::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<SpireStreet<Id, Size>>(pStreet->id(), *pStreet);
}

template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
Expand Down
2 changes: 1 addition & 1 deletion src/dsm/headers/Node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
158 changes: 131 additions & 27 deletions src/dsm/headers/Street.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ namespace dsm {
Id m_id;
Size m_capacity;
Size m_transportCapacity;
bool m_isSpire;

public:
Street() = delete;
Expand Down Expand Up @@ -70,6 +69,8 @@ namespace dsm {
/// @param nodePair The street's node pair
Street(Id id, Size capacity, double len, double maxSpeed, std::pair<Id, Id> nodePair);

virtual ~Street() = default;

/// @brief Set the street's id
/// @param id The street's id
void setId(Id id);
Expand Down Expand Up @@ -111,11 +112,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
Expand Down Expand Up @@ -148,12 +144,13 @@ 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<Id> dequeue();
virtual std::optional<Id> 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 <typename Id, typename Size>
Expand All @@ -165,8 +162,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 <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
Expand All @@ -177,8 +173,7 @@ namespace dsm {
m_angle{0.},
m_id{index},
m_capacity{1},
m_transportCapacity{std::numeric_limits<Size>::max()},
m_isSpire{true} {}
m_transportCapacity{std::numeric_limits<Size>::max()} {}

template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
Expand All @@ -189,8 +184,7 @@ namespace dsm {
m_angle{0.},
m_id{id},
m_capacity{capacity},
m_transportCapacity{std::numeric_limits<Size>::max()},
m_isSpire{true} {}
m_transportCapacity{std::numeric_limits<Size>::max()} {}

template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
Expand All @@ -201,8 +195,7 @@ namespace dsm {
m_angle{0.},
m_id{id},
m_capacity{capacity},
m_transportCapacity{std::numeric_limits<Size>::max()},
m_isSpire{true} {
m_transportCapacity{std::numeric_limits<Size>::max()} {
this->setMaxSpeed(maxSpeed);
}

Expand Down Expand Up @@ -279,11 +272,6 @@ namespace dsm {
}
m_angle = angle;
}
template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
void Street<Id, Size>::setIsSpire(bool isSpire) {
m_isSpire = isSpire;
}

template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
Expand Down Expand Up @@ -334,9 +322,7 @@ namespace dsm {
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
void Street<Id, Size>::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) {
Expand All @@ -355,10 +341,128 @@ namespace dsm {
m_queue.pop();
return id;
}

/// @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 <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
bool Street<Id, Size>::isSpire() const {
return m_isSpire;
class SpireStreet : public Street<Id, Size> {
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<Id, Size>& 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<Id, Id> 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<Id, Id> 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<Id> The id of the agent removed from the street's queue
std::optional<Id> 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 <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
SpireStreet<Id, Size>::SpireStreet(Id id, const Street<Id, Size>& street)
: Street<Id, Size>(id, street), m_agentCounterIn{0}, m_agentCounterOut{0} {}

template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
SpireStreet<Id, Size>::SpireStreet(Id id,
Size capacity,
double len,
std::pair<Id, Id> nodePair)
: Street<Id, Size>(id, capacity, len, nodePair),
m_agentCounterIn{0},
m_agentCounterOut{0} {}

template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
SpireStreet<Id, Size>::SpireStreet(
Id id, Size capacity, double len, double maxSpeed, std::pair<Id, Id> nodePair)
: Street<Id, Size>(id, capacity, len, maxSpeed, nodePair),
m_agentCounterIn{0},
m_agentCounterOut{0} {}

template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
void SpireStreet<Id, Size>::enqueue(Id agentId) {
Street<Id, Size>::enqueue(agentId);
++m_agentCounterIn;
}

template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
Size SpireStreet<Id, Size>::inputFlow() {
Size flow = m_agentCounterIn;
m_agentCounterIn = 0;
m_agentCounterOut = 0;
return flow;
}
template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
Size SpireStreet<Id, Size>::outputFlow() {
Size flow = m_agentCounterOut;
m_agentCounterIn = 0;
m_agentCounterOut = 0;
return flow;
}
template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
int SpireStreet<Id, Size>::meanFlow() {
int flow = static_cast<int>(m_agentCounterIn) - static_cast<int>(m_agentCounterOut);
m_agentCounterIn = 0;
m_agentCounterOut = 0;
return flow;
}

template <typename Id, typename Size>
requires(std::unsigned_integral<Id> && std::unsigned_integral<Size>)
std::optional<Id> SpireStreet<Id, Size>::dequeue() {
std::optional<Id> id = Street<Id, Size>::dequeue();
if (id.has_value()) {
++m_agentCounterOut;
}
return id;
}

}; // namespace dsm
Expand Down
2 changes: 1 addition & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16.0)

project(dsm_tests VERSION 1.2.0 LANGUAGES CXX)
project(dsm_tests VERSION 1.2.1 LANGUAGES CXX)

# Set the C++ standard
set(CMAKE_CXX_STANDARD 20)
Expand Down
9 changes: 7 additions & 2 deletions test/Test_dynamics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ 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") {
Expand Down Expand Up @@ -445,7 +452,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());
}
}
Loading