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
0 commit comments