Skip to content
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

Travel time weight #268

Merged
merged 5 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
18 changes: 15 additions & 3 deletions src/dsm/headers/DijkstraWeights.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#pragma once

#include "../utility/Typedef.hpp"
Expand All @@ -7,7 +6,20 @@ namespace dsm {

class Graph;

double streetLength(const Graph* graph, Id node1, Id node2);
double streetTime(const Graph* graph, Id node1, Id node2);
namespace weight_functions {
/// @brief Returns the length of a street given its source and destination nodes
/// @param graph A pointer to the graph
/// @param node1 The source node id
/// @param node2 The destination node id
/// @return The length of the street
double streetLength(const Graph* graph, Id node1, Id node2);
/// @brief Returns the time to cross a street given its source and destination nodes
/// @param graph A pointer to the graph
/// @param node1 The source node id
/// @param node2 The destination node id
/// @return The time to cross the street
/// @details This time also takes into account the number of agents on the street
double streetTime(const Graph* graph, Id node1, Id node2);
} // namespace weight_functions

}; // namespace dsm
46 changes: 34 additions & 12 deletions src/dsm/headers/Dynamics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <exception>
#include <fstream>
#include <filesystem>
#include <functional>
#include <tbb/parallel_for_each.h>

#include "DijkstraWeights.hpp"
Expand Down Expand Up @@ -72,6 +73,8 @@
private:
std::map<Id, std::unique_ptr<agent_t>> m_agents;
std::unordered_map<Id, std::unique_ptr<Itinerary>> m_itineraries;
std::function<double(const Graph*, Id, Id)> m_weightFunction;
double m_weightTreshold;
bool m_bCacheEnabled;

protected:
Expand All @@ -97,7 +100,6 @@
}
}

auto const dimension = static_cast<Size>(m_graph.nNodes());
auto const destinationID = pItinerary->destination();
std::vector<double> shortestDistances(m_graph.nNodes());
tbb::parallel_for_each(
Expand All @@ -108,7 +110,7 @@
if (nodeId == destinationID) {
shortestDistances[nodeId] = -1.;
} else {
auto result = m_graph.shortestPath(nodeId, destinationID);
auto result = m_graph.shortestPath(nodeId, destinationID, m_weightFunction);
if (result.has_value()) {
shortestDistances[nodeId] = result.value().distance();
} else {
Expand All @@ -132,17 +134,17 @@
auto const& row{m_graph.adjMatrix().getRow(nodeId)};
for (const auto nextNodeId : row) {
if (nextNodeId == destinationID) {
if (std::abs(m_graph.street(nodeId * dimension + nextNodeId)->length() -
minDistance) < 1.) // 1 meter tolerance between shortest paths
if (std::abs(m_weightFunction(&m_graph, nodeId, nextNodeId) - minDistance) <

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
m_weightTreshold) // 1 meter tolerance between shortest paths
{
path.insert(nodeId, nextNodeId);
} else {
Logger::debug(
std::format("Found a path from {} to {} which differs for more than {} "
"meter(s) from the shortest one.",
"unit(s) from the shortest one.",
nodeId,
destinationID,
1.));
m_weightTreshold));
}
continue;
}
Expand All @@ -151,18 +153,18 @@
continue;
}
bool const bIsMinDistance{
std::abs(m_graph.street(nodeId * dimension + nextNodeId)->length() +
distance - minDistance) <
1.}; // 1 meter tolerance between shortest paths
std::abs(m_weightFunction(&m_graph, nodeId, nextNodeId) + distance -
minDistance) <

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
m_weightTreshold}; // 1 meter tolerance between shortest paths
if (bIsMinDistance) {
path.insert(nodeId, nextNodeId);
} else {
Logger::debug(
std::format("Found a path from {} to {} which differs for more than {} "
"meter(s) from the shortest one.",
"unit(s) from the shortest one.",
nodeId,
destinationID,
1.));
m_weightTreshold));
}
}
}
Expand Down Expand Up @@ -199,6 +201,18 @@
/// @brief Update the paths of the itineraries based on the actual travel times
virtual void updatePaths();

/// @brief Set the weight function for the Dijkstra's algorithm
/// @param weightFunction A std::function returning a double value and taking as arguments a
/// pointer to the graph, an id of a source node and an id of a target node (for the edge)
/// @details The weight function must return the weight of the edge between the source and the
/// target node. One can use the predefined weight functions in the DijkstraWeights.hpp file.
void setWeightFunction(std::function<double(const Graph*, Id, Id)> weightFunction);
/// @brief Set the weight treshold for updating the paths
/// @param weightTreshold The weight treshold
/// @details If two paths differs only for a weight smaller than the treshold, the two paths are
/// considered equivalent.
void setWeightTreshold(double weightTreshold) { m_weightTreshold = weightTreshold; }

/// @brief Set the destination nodes
/// @param destinationNodes The destination nodes (as an initializer list)
/// @param updatePaths If true, the paths are updated
Expand Down Expand Up @@ -343,7 +357,9 @@
Dynamics<agent_t>::Dynamics(Graph& graph,
bool useCache,
std::optional<unsigned int> seed)
: m_bCacheEnabled{useCache},
: m_weightFunction{weight_functions::streetLength},
m_weightTreshold{1.},
m_bCacheEnabled{useCache},
m_graph{std::move(graph)},
m_time{0},
m_previousSpireTime{0},
Expand Down Expand Up @@ -371,6 +387,12 @@
});
}

template <typename agent_t>
void Dynamics<agent_t>::setWeightFunction(
std::function<double(const Graph*, Id, Id)> weightFunction) {
m_weightFunction = weightFunction;
}

template <typename agent_t>
void Dynamics<agent_t>::setDestinationNodes(std::initializer_list<Id> destinationNodes,
bool updatePaths) {
Expand Down
12 changes: 6 additions & 6 deletions src/dsm/headers/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,19 +276,19 @@ namespace dsm {
/// @return A DijkstraResult object containing the path and the distance
template <typename Func = std::function<double(const Graph*, Id, Id)>>
requires(std::is_same_v<std::invoke_result_t<Func, const Graph*, Id, Id>, double>)
std::optional<DijkstraResult> shortestPath(const Node& source,
const Node& destination,
Func f = streetLength) const;
std::optional<DijkstraResult> shortestPath(
const Node& source,
const Node& destination,
Func f = weight_functions::streetLength) const;

/// @brief Get the shortest path between two nodes using dijkstra algorithm
/// @param source The source node id
/// @param destination The destination node id
/// @return A DijkstraResult object containing the path and the distance
template <typename Func = std::function<double(const Graph*, Id, Id)>>
requires(std::is_same_v<std::invoke_result_t<Func, const Graph*, Id, Id>, double>)
std::optional<DijkstraResult> shortestPath(Id source,
Id destination,
Func f = streetLength) const;
std::optional<DijkstraResult> shortestPath(
Id source, Id destination, Func f = weight_functions::streetLength) const;
};

template <typename node_t, typename... TArgs>
Expand Down
25 changes: 13 additions & 12 deletions src/dsm/sources/DijkstraWeights.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@

namespace dsm {

double streetLength(const Graph* graph, Id node1, Id node2) {
const auto street{graph->street(node1, node2)};
return (*street)->length();
}
namespace weight_functions {
double streetLength(const Graph* graph, Id node1, Id node2) {
const auto street{graph->street(node1, node2)};
return (*street)->length();
}

double streetTime(const Graph* graph, Id node1, Id node2) {
const auto street{graph->street(node1, node2)};
const auto length{(*street)->length()};
const auto speed{(*street)->maxSpeed() *
(1. - (*street)->nAgents() / (*street)->capacity())};

return length / speed;
}
double streetTime(const Graph* graph, Id node1, Id node2) {
const auto street{graph->street(node1, node2)};
const auto length{(*street)->length()};

Check warning

Code scanning / Cppcheck (reported by Codacy)

Local variable 'speed' shadows outer function Warning

Local variable 'speed' shadows outer function
const auto speed{(*street)->maxSpeed() *
(1. - (*street)->nAgents() / (*street)->capacity())};
return length / speed;
}
} // namespace weight_functions

}; // namespace dsm
38 changes: 37 additions & 1 deletion test/Test_dynamics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,43 @@
graph.addStreets(s1, s2, s3, s4);
graph.buildAdj();
Dynamics dynamics{graph, false, 69};
dynamics.addItinerary(std::unique_ptr<Itinerary>(new Itinerary(0, 2)));
dynamics.addItinerary(0, 2);
WHEN("We update the paths") {
dynamics.updatePaths();
THEN("The path is updated and correctly formed") {
CHECK_EQ(dynamics.itineraries().size(), 1);
CHECK_EQ(dynamics.itineraries().at(0)->path()->size(), 4);
CHECK_EQ(dynamics.itineraries().at(0)->path()->n(), 4);
CHECK(dynamics.itineraries().at(0)->path()->operator()(0, 1));
CHECK(dynamics.itineraries().at(0)->path()->operator()(1, 2));
CHECK(dynamics.itineraries().at(0)->path()->operator()(0, 3));
CHECK(dynamics.itineraries().at(0)->path()->operator()(3, 2));
for (auto const& it : dynamics.itineraries()) {
auto const& path = it.second->path();
for (uint16_t i{0}; i < path->n(); ++i) {
if (i == it.second->destination()) {
CHECK_FALSE(path->getRow(i).size());
} else {
CHECK(path->getRow(i).size());
}
}
}
}
}
}
GIVEN(
"A dynamics objects, many streets and an itinerary with bifurcations (TIME "
"WEIGHTED)") {
Street s1{0, std::make_pair(0, 1), 5., 50.};

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
Street s2{1, std::make_pair(1, 2), 7., 70.};

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
Street s3{2, std::make_pair(0, 3), 9., 90.};

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
Street s4{3, std::make_pair(3, 2), 10., 100.};

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
Graph graph;
graph.addStreets(s1, s2, s3, s4);
graph.buildAdj();
Dynamics dynamics{graph, false, 69};
dynamics.setWeightFunction(dsm::weight_functions::streetTime);
dynamics.addItinerary(0, 2);
WHEN("We update the paths") {
dynamics.updatePaths();
THEN("The path is updated and correctly formed") {
Expand Down
Loading