Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
56 changes: 56 additions & 0 deletions cpp/memilio/mobility/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,62 @@ class Graph
});
}

/**
* @brief Add edges to a graph without checking for duplicates and without sorting.
*
* @param start_node_idx Id of start node
* @param end_node_idx Id of end node
* @param args Additional arguments for edge construction
* @return Edge<EdgePropertyT>& End of edge vector
*/
template <class... Args>
Edge<EdgePropertyT>& lazy_add_edge(size_t start_node_idx, size_t end_node_idx, Args&&... args)
{
assert(m_nodes.size() > start_node_idx && m_nodes.size() > end_node_idx);
m_edges.emplace_back(start_node_idx, end_node_idx, std::forward<Args>(args)...);
return m_edges.back();
}

/**
* @brief Sort the edge vector of a graph.
*
* @return Edge<EdgePropertyT>& End of edge vector
*/
Edge<EdgePropertyT>& sort_edges()
{
std::sort(m_edges.begin(), m_edges.end(), [](auto&& e1, auto&& e2) {
return e1.start_node_idx == e2.start_node_idx ? e1.end_node_idx < e2.end_node_idx
: e1.start_node_idx < e2.start_node_idx;
});
return m_edges.back();
}

/**
* @brief Make the edges of a graph unique.
*
* Copies all the unique edges to a new vector and replaces the edge vector of the graph with it. Unique means that
* the start and end node indices are unique. Other edge properties are not checked.
* @return Edge<EdgePropertyT>& End of edge vector
*/
Edge<EdgePropertyT>& make_edges_unique()
{
std::vector<Edge<EdgePropertyT>> unique_edges;
unique_edges.reserve(m_edges.size());
std::ranges::unique_copy(m_edges, std::back_inserter(unique_edges), [](auto&& e1, auto&& e2) {
return e1.start_node_idx == e2.start_node_idx && e1.end_node_idx == e2.end_node_idx;
});
m_edges = std::move(unique_edges);
return m_edges.back();
}

/**
* @brief reserve space for edges.
*/
void reserve_edges(size_t n)
{
m_edges.reserve(n);
}

/**
* @brief range of nodes
*/
Expand Down
35 changes: 35 additions & 0 deletions cpp/tests/test_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,41 @@ TEST(TestGraph, ot_edges)
EXPECT_THAT(g.out_edges(1), testing::ElementsAreArray(v1));
}

TEST(TestGraph, compare_add_edge_functions)
{
mio::Graph<int, int> g;
mio::Graph<int, int> g_lazy;
int num_nodes = 10;
for (int index = 0; index < num_nodes; ++index) {
g.add_node(index);
g_lazy.add_node(index);
}
for (int first_node = num_nodes - 1; first_node >= 0; --first_node) {
for (int second_node = 0; second_node < num_nodes; ++second_node) {
if (first_node != second_node) {
g.add_edge(first_node, second_node, int(first_node + second_node));
g_lazy.lazy_add_edge(first_node, second_node, int(first_node + second_node));
}
}
}
for (int first_node = num_nodes - 1; first_node >= 0; --first_node) {
for (int second_node = 0; second_node < num_nodes; ++second_node) {
if (first_node != second_node) {
g.add_edge(first_node, second_node, int(first_node + second_node));
g_lazy.lazy_add_edge(first_node, second_node, int(first_node + second_node));
}
}
}
g_lazy.sort_edges();
g_lazy.make_edges_unique();
EXPECT_EQ(g.edges().size(), g_lazy.edges().size());

for (size_t index = 0; index < g.edges().size(); index++) {
EXPECT_EQ(g.edges()[index].start_node_idx, g_lazy.edges()[index].start_node_idx);
EXPECT_EQ(g.edges()[index].end_node_idx, g_lazy.edges()[index].end_node_idx);
}
}

namespace
{

Expand Down
15 changes: 15 additions & 0 deletions docs/source/cpp/graph_metapop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,21 @@ The following steps detail how to configure and execute a graph simulation:
graph.add_edge(0, 1, std::move(transition_rates));
graph.add_edge(1, 0, std::move(transition_rates));

.. dropdown:: :fa:`gears` Working with large graphs

When working with very large graphs, i.e. starting from a few thousand edges, it will be faster to not use the standard ``add_edge`` function. This function always
keeps the list of edges inside the graph sorted and checks for duplicates. For large graphs, it is faster to first add all the edges to the graph
and then sort them and remove duplicates:

.. code-block:: cpp

graph.reserve_edges(2);
graph.lazy_add_edge(0, 1, std::move(transition_rates));
graph.lazy_add_edge(1, 0, std::move(transition_rates));
graph.sort_edges();
graph.make_edges_unique();


5. **Initialize and Advance the Mobility Simulation:**

With the graph constructed, initialize the simulation with the starting time and time step. Then, advance the simulation until the final time :math:`t_{max}`.
Expand Down