Skip to content

Commit 785275e

Browse files
committed
CHG: Move GraphBuilder to own file
1 parent 3f3d421 commit 785275e

File tree

5 files changed

+193
-129
lines changed

5 files changed

+193
-129
lines changed

cpp/memilio/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ add_library(memilio
8181
mobility/graph_simulation.cpp
8282
mobility/graph.h
8383
mobility/graph.cpp
84+
mobility/graph_builder.h
85+
mobility/graph_builder.cpp
8486
timer/auto_timer.h
8587
timer/basic_timer.cpp
8688
timer/basic_timer.h

cpp/memilio/mobility/graph.h

Lines changed: 0 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -450,135 +450,6 @@ void print_graph(std::ostream& os, const Graph& g)
450450
}
451451
}
452452

453-
/**
454-
* @brief A builder class for constructing graphs.
455-
*
456-
* This class provides a interface for adding nodes and edges to a graph. It allows for efficient construction of large
457-
* graphs by reserving space for nodes and edges in advance. The build method finalizes the graph by sorting edges and
458-
* optionally removing duplicates.
459-
* The advantage over the :ref add_edge function of the Graph class is that edges are only sorted once during the build
460-
* process, improving performance when adding many edges.
461-
*
462-
* @tparam NodePropertyT Type of the node property.
463-
* @tparam EdgePropertyT Type of the edge property.
464-
*/
465-
template <class NodePropertyT, class EdgePropertyT>
466-
class GraphBuilder
467-
{
468-
public:
469-
using NodeProperty = NodePropertyT;
470-
using EdgeProperty = EdgePropertyT;
471-
472-
GraphBuilder() = default;
473-
GraphBuilder(const size_t num_nodes, const size_t num_edges)
474-
{
475-
m_nodes.reserve(num_nodes);
476-
m_edges.reserve(num_edges);
477-
}
478-
479-
/**
480-
* @brief Add a node to the GraphBuilder.
481-
*
482-
* The property of the node is constructed from arguments.
483-
* @param id Id for the node.
484-
* @tparam args Additional arguments for node construction.
485-
*/
486-
template <class... Args>
487-
void add_node(int id, Args&&... args)
488-
{
489-
m_nodes.emplace_back(id, std::forward<Args>(args)...);
490-
}
491-
492-
/**
493-
* @brief Add an edge to the GraphBuilder.
494-
*
495-
* @param start_node_idx Id of start node
496-
* @param end_node_idx Id of end node
497-
* @tparam args Additional arguments for edge construction
498-
*/
499-
template <class... Args>
500-
void add_edge(size_t start_node_idx, size_t end_node_idx, Args&&... args)
501-
{
502-
assert(m_nodes.size() > start_node_idx && m_nodes.size() > end_node_idx);
503-
m_edges.emplace_back(start_node_idx, end_node_idx, std::forward<Args>(args)...);
504-
}
505-
506-
/**
507-
* @brief Build the graph from the added nodes and edges.
508-
*
509-
* Sorts the edges and optionally removes duplicate edges (same start and end node indices).
510-
* Wihout dupplicate removal, multiple edges between the same nodes are allowed and the order of insertion is stable.
511-
* @param make_unique If true, duplicate edges are removed. The last added edge is kept!
512-
* @return Graph<NodePropertyT, EdgePropertyT> The constructed graph.
513-
*/
514-
Graph<NodeProperty, EdgeProperty> build(bool make_unique = false) &&
515-
{
516-
sort_edges();
517-
if (make_unique) {
518-
remove_duplicate_edges();
519-
}
520-
Graph<NodeProperty, EdgeProperty> graph(std::move(m_nodes), std::move(m_edges));
521-
return graph;
522-
}
523-
524-
private:
525-
/**
526-
* @brief Sort the edge vector of a graph.
527-
*
528-
* Sorts the edges first by start node index, then by end node index. We use stable_sort to keep the order of insertion
529-
* for edges with the same start and end node indices.
530-
*/
531-
void sort_edges()
532-
{
533-
std::stable_sort(m_edges.begin(), m_edges.end(), [](auto&& e1, auto&& e2) {
534-
return e1.start_node_idx == e2.start_node_idx ? e1.end_node_idx < e2.end_node_idx
535-
: e1.start_node_idx < e2.start_node_idx;
536-
});
537-
}
538-
539-
/**
540-
* @brief Remove duplicate edges from a sorted edge vector.
541-
*
542-
* Copies all the unique edges to a new vector and replaces the original edge vector with it. Unique means that
543-
* the start and end node indices are unique. Other edge properties are not checked and may get lost. Only the last
544-
* edge in the vector is kept.
545-
*/
546-
void remove_duplicate_edges()
547-
{
548-
std::vector<Edge<EdgePropertyT>> unique_edges;
549-
unique_edges.reserve(m_edges.size());
550-
bool duplicate_edges = false;
551-
auto curr_elem = m_edges.begin();
552-
auto next_elem = std::adjacent_find(curr_elem, m_edges.end(), [](auto&& e1, auto&& e2) {
553-
return e1.start_node_idx == e2.start_node_idx && e1.end_node_idx == e2.end_node_idx;
554-
});
555-
while (next_elem != m_edges.end()) {
556-
std::copy(curr_elem, next_elem, std::back_inserter(unique_edges));
557-
curr_elem = next_elem;
558-
while (next_elem != m_edges.end() && (*curr_elem).start_node_idx == (*next_elem).start_node_idx &&
559-
(*curr_elem).end_node_idx == (*next_elem).end_node_idx) {
560-
next_elem = next(next_elem);
561-
}
562-
curr_elem = prev(next_elem);
563-
std::copy(curr_elem, next(curr_elem), std::back_inserter(unique_edges));
564-
duplicate_edges = true;
565-
curr_elem = next(curr_elem);
566-
next_elem = std::adjacent_find(curr_elem, m_edges.end(), [](auto&& e1, auto&& e2) {
567-
return e1.start_node_idx == e2.start_node_idx && e1.end_node_idx == e2.end_node_idx;
568-
});
569-
}
570-
std::copy(curr_elem, next_elem, std::back_inserter(unique_edges));
571-
m_edges = std::move(unique_edges);
572-
if (duplicate_edges) {
573-
mio::log_warning("Removed duplicate edge(s)");
574-
}
575-
}
576-
577-
private:
578-
std::vector<Node<NodePropertyT>> m_nodes;
579-
std::vector<Edge<EdgePropertyT>> m_edges;
580-
};
581-
582453
} // namespace mio
583454

584455
#endif //GRAPH_H
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (C) 2020-2025 MEmilio
3+
*
4+
* Authors: Kilian Volmer
5+
*
6+
* Contact: Martin J. Kuehn <[email protected]>
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
#include "memilio/mobility/graph_builder.h"
21+
22+
namespace mio
23+
{
24+
25+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
* Copyright (C) 2020-2025 MEmilio
3+
*
4+
* Authors: Kilian Volmer
5+
*
6+
* Contact: Martin J. Kuehn <[email protected]>
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
#ifndef GRAPH_BUILDER_H
21+
#define GRAPH_BUILDER_H
22+
23+
#include "memilio/mobility/graph.h"
24+
#include "memilio/utils/logging.h"
25+
#include "memilio/utils/stl_util.h"
26+
#include <algorithm>
27+
#include <functional>
28+
#include <iostream>
29+
#include <ranges>
30+
31+
namespace mio
32+
{
33+
34+
/**
35+
* @brief A builder class for constructing graphs.
36+
*
37+
* This class provides a interface for adding nodes and edges to a graph. It allows for efficient construction of large
38+
* graphs by reserving space for nodes and edges in advance. The build method finalizes the graph by sorting edges and
39+
* optionally removing duplicates.
40+
* The advantage over the :ref add_edge function of the Graph class is that edges are only sorted once during the build
41+
* process, improving performance when adding many edges.
42+
*
43+
* @tparam NodePropertyT Type of the node property.
44+
* @tparam EdgePropertyT Type of the edge property.
45+
*/
46+
template <class NodePropertyT, class EdgePropertyT>
47+
class GraphBuilder
48+
{
49+
public:
50+
using NodeProperty = NodePropertyT;
51+
using EdgeProperty = EdgePropertyT;
52+
53+
GraphBuilder() = default;
54+
GraphBuilder(const size_t num_nodes, const size_t num_edges)
55+
{
56+
m_nodes.reserve(num_nodes);
57+
m_edges.reserve(num_edges);
58+
}
59+
60+
/**
61+
* @brief Add a node to the GraphBuilder.
62+
*
63+
* The property of the node is constructed from arguments.
64+
* @param id Id for the node.
65+
* @tparam args Additional arguments for node construction.
66+
*/
67+
template <class... Args>
68+
void add_node(int id, Args&&... args)
69+
{
70+
m_nodes.emplace_back(id, std::forward<Args>(args)...);
71+
}
72+
73+
/**
74+
* @brief Add an edge to the GraphBuilder.
75+
*
76+
* @param start_node_idx Id of start node
77+
* @param end_node_idx Id of end node
78+
* @tparam args Additional arguments for edge construction
79+
*/
80+
template <class... Args>
81+
void add_edge(size_t start_node_idx, size_t end_node_idx, Args&&... args)
82+
{
83+
assert(m_nodes.size() > start_node_idx && m_nodes.size() > end_node_idx);
84+
m_edges.emplace_back(start_node_idx, end_node_idx, std::forward<Args>(args)...);
85+
}
86+
87+
/**
88+
* @brief Build the graph from the added nodes and edges.
89+
*
90+
* Sorts the edges and optionally removes duplicate edges (same start and end node indices).
91+
* Wihout dupplicate removal, multiple edges between the same nodes are allowed and the order of insertion is stable.
92+
* @param make_unique If true, duplicate edges are removed. The last added edge is kept!
93+
* @return Graph<NodePropertyT, EdgePropertyT> The constructed graph.
94+
*/
95+
Graph<NodeProperty, EdgeProperty> build(bool make_unique = false) &&
96+
{
97+
sort_edges();
98+
if (make_unique) {
99+
remove_duplicate_edges();
100+
}
101+
Graph<NodeProperty, EdgeProperty> graph(std::move(m_nodes), std::move(m_edges));
102+
return graph;
103+
}
104+
105+
private:
106+
/**
107+
* @brief Sort the edge vector of a graph.
108+
*
109+
* Sorts the edges first by start node index, then by end node index. We use stable_sort to keep the order of insertion
110+
* for edges with the same start and end node indices.
111+
*/
112+
void sort_edges()
113+
{
114+
std::stable_sort(m_edges.begin(), m_edges.end(), [](auto&& e1, auto&& e2) {
115+
return e1.start_node_idx == e2.start_node_idx ? e1.end_node_idx < e2.end_node_idx
116+
: e1.start_node_idx < e2.start_node_idx;
117+
});
118+
}
119+
120+
/**
121+
* @brief Remove duplicate edges from a sorted edge vector.
122+
*
123+
* Copies all the unique edges to a new vector and replaces the original edge vector with it. Unique means that
124+
* the start and end node indices are unique. Other edge properties are not checked and may get lost. Only the last
125+
* edge in the vector is kept.
126+
*/
127+
void remove_duplicate_edges()
128+
{
129+
std::vector<Edge<EdgePropertyT>> unique_edges;
130+
unique_edges.reserve(m_edges.size());
131+
bool duplicate_edges = false;
132+
auto curr_elem = m_edges.begin();
133+
auto next_elem = std::adjacent_find(curr_elem, m_edges.end(), [](auto&& e1, auto&& e2) {
134+
return e1.start_node_idx == e2.start_node_idx && e1.end_node_idx == e2.end_node_idx;
135+
});
136+
while (next_elem != m_edges.end()) {
137+
std::copy(curr_elem, next_elem, std::back_inserter(unique_edges));
138+
curr_elem = next_elem;
139+
while (next_elem != m_edges.end() && (*curr_elem).start_node_idx == (*next_elem).start_node_idx &&
140+
(*curr_elem).end_node_idx == (*next_elem).end_node_idx) {
141+
next_elem = next(next_elem);
142+
}
143+
curr_elem = prev(next_elem);
144+
std::copy(curr_elem, next(curr_elem), std::back_inserter(unique_edges));
145+
duplicate_edges = true;
146+
curr_elem = next(curr_elem);
147+
next_elem = std::adjacent_find(curr_elem, m_edges.end(), [](auto&& e1, auto&& e2) {
148+
return e1.start_node_idx == e2.start_node_idx && e1.end_node_idx == e2.end_node_idx;
149+
});
150+
}
151+
std::copy(curr_elem, next_elem, std::back_inserter(unique_edges));
152+
m_edges = std::move(unique_edges);
153+
if (duplicate_edges) {
154+
mio::log_warning("Removed duplicate edge(s)");
155+
}
156+
}
157+
158+
private:
159+
std::vector<Node<NodePropertyT>> m_nodes;
160+
std::vector<Edge<EdgePropertyT>> m_edges;
161+
};
162+
163+
} // namespace mio
164+
165+
#endif // GRAPH_BUILDER_H

cpp/tests/test_graph.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* limitations under the License.
1919
*/
2020
#include "memilio/mobility/graph.h"
21+
#include "memilio/mobility/graph_builder.h"
2122
#include "memilio/epidemiology/age_group.h"
2223
#include "memilio/utils/compiler_diagnostics.h"
2324
#include "memilio/utils/date.h"

0 commit comments

Comments
 (0)