Arcane  4.1.12.0
User documentation
Loading...
Searching...
No Matches
DirectedAcyclicGraphT.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* DirectedAcyclicGraphT.h (C) 2000-2022 */
9/* */
10/* Implementation of a directed acyclic graph */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCANE_DIRECTEDACYCLICGRAPHT_H_
13#define ARCANE_DIRECTEDACYCLICGRAPHT_H_
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include <iterator>
18
20#include "arcane/utils/GraphBaseT.h"
21#include "arcane/utils/ITraceMng.h"
22#include "arcane/utils/Math.h"
23
24/*---------------------------------------------------------------------------*/
25/*---------------------------------------------------------------------------*/
26
27namespace Arcane
28{
29
30/*---------------------------------------------------------------------------*/
31/*---------------------------------------------------------------------------*/
32/*! Template class for DirectedAcyclicGraph.
33 * VertexType must implement a less comparison operator.
34 *
35 */
36
37template <class VertexType, class EdgeType>
39: public GraphBaseT<VertexType, EdgeType>
40{
41 typedef VertexType& VertexRef;
42 typedef EdgeType& EdgeRef;
43
44 public:
45
46 /** Constructor of the class */
48 : GraphBaseT<VertexType, EdgeType>(trace_mng)
49 , m_compute_vertex_levels(true)
50 {}
51
52 /** Destructor of the class */
54
55 public:
56
57 template <class ContainerT>
58 class SortedElementSet
59 {
60 public:
61
62 template <class T>
63 class ReverseOrderSet
64 {
65 public:
66
67 ReverseOrderSet(const T& elements)
68 : m_elements(elements)
69 {}
70
71 virtual ~ReverseOrderSet() {}
72
73 typedef typename std::reverse_iterator<typename T::iterator> iterator;
74 typedef typename std::reverse_iterator<typename T::const_iterator> const_iterator;
75
76 iterator begin() { return iterator(m_elements.end()); }
77 const_iterator begin() const
78 {
79 return iterator(m_elements.end());
80 ;
81 }
82
83 iterator end() { return iterator(m_elements.begin()); }
84 const_iterator end() const { return iterator(m_elements.begin()); }
85
86 Integer size() { return m_elements.size(); }
87
88 private:
89
90 T m_elements;
91 };
92
93 public:
94
95 SortedElementSet(const ContainerT& elements)
96 : m_elements(elements)
97 {}
98
99 SortedElementSet(ContainerT&& elements)
100 : m_elements(elements)
101 {}
102
103 SortedElementSet() {}
104
105 virtual ~SortedElementSet() {}
106
107 typedef typename ContainerT::iterator iterator;
108 typedef typename ContainerT::const_iterator const_iterator;
109
110 iterator begin() { return m_elements.begin(); }
111 const_iterator begin() const { return m_elements.begin(); }
112
113 iterator end() { return m_elements.end(); }
114 const_iterator end() const { return m_elements.end(); }
115
116 Integer size() { return m_elements.size(); }
117
118 ReverseOrderSet<ContainerT> reverseOrder() { return ReverseOrderSet<ContainerT>(m_elements); }
119
120 private:
121
122 ContainerT m_elements;
123 };
124
128 typedef std::map<typename Base::VertexTypeConstRef, Integer> VertexLevelMap;
129 typedef std::map<typename Base::EdgeTypeConstRef, Integer> EdgeLevelMap;
130 typedef std::set<std::pair<VertexType, Integer>, std::function<bool(std::pair<VertexType, Integer>, std::pair<VertexType, Integer>)>> VertexLevelSet;
131
132 void addEdge(const VertexType& source_vertex, const VertexType& target_vertex, const EdgeType& source_to_target_edge)
133 {
134 Base::addEdge(source_vertex, target_vertex, source_to_target_edge);
135 m_compute_vertex_levels = true;
136 }
137
138 void addEdge(VertexType&& source_vertex, VertexType&& target_vertex, EdgeType&& source_to_target_edge)
139 {
140 Base::addEdge(source_vertex, target_vertex, source_to_target_edge);
141 m_compute_vertex_levels = true;
142 }
143
144 SortedVertexSet topologicalSort()
145 {
146 if (m_compute_vertex_levels)
147 _computeVertexLevels();
148 typename Base::VertexTypeRefArray sorted_vertices;
149 for (auto& vertex : this->m_vertices) {
150 sorted_vertices.add(std::ref(vertex));
151 }
152 std::sort(sorted_vertices.begin(), sorted_vertices.end(), [&](const VertexType& a, const VertexType& b) { return m_vertex_level_map[a] < m_vertex_level_map[b]; });
153 return SortedVertexSet(std::move(sorted_vertices));
154 }
155
156 SortedEdgeSet spanningTree()
157 {
158 if (m_compute_vertex_levels)
159 _computeVertexLevels();
160 typename Base::EdgeTypeRefArray spaning_tree_edges;
161 for (auto& edge : this->m_edges) {
162 Integer level = _computeEdgeLevel(edge);
163 m_edge_level_map[edge] = level;
164 if (level < 0)
165 continue; // edge must not be accounted for in spanning tree
166 spaning_tree_edges.add(std::ref(edge));
167 }
168 std::sort(spaning_tree_edges.begin(), spaning_tree_edges.end(), [&](const EdgeType& a, const EdgeType& b) { return m_edge_level_map[a] < m_edge_level_map[b]; });
169 return SortedEdgeSet(spaning_tree_edges);
170 }
171
172 void print()
173 {
174 if (m_compute_vertex_levels)
175 _computeVertexLevels();
176 this->m_trace_mng->info() << "--- Directed Graph ---";
177 for (const auto& vertex_entry : this->m_adjacency_list)
178 _printGraphEntry(vertex_entry);
179
180 std::ostringstream oss;
181 this->m_trace_mng->info() << oss.str();
182
183 for (const auto& vertex_level_set_entry : m_vertex_level_map)
184 this->m_trace_mng->info() << "-- Graph has vertex " << vertex_level_set_entry.first << " with level " << vertex_level_set_entry.second;
185 }
186
187 /*! Cycle detection is done using a lazy pattern which is only triggered when calling topologicalSort() and print().
188 * This method allows knowing if the graph contains a cycle (which would cause topologicalSort() to fail).
189 */
190 bool hasCycle()
191 {
192 bool has_cycle = false;
193 try {
194 _computeVertexLevels();
195 }
196 catch (const FatalErrorException& e) {
197 has_cycle = true;
198 }
199 return has_cycle;
200 }
201
202 private:
203
204 std::set<typename Base::VertexTypeConstRef> m_colored_vertices;
205 VertexLevelMap m_vertex_level_map;
206 EdgeLevelMap m_edge_level_map;
207 bool m_compute_vertex_levels;
208
209 private:
210
211 void _computeVertexLevels()
212 {
213 // Current algo cannot update vertex level ; need to clear the map.
214 m_vertex_level_map.clear();
215 // compute vertex level
216 for (const auto& vertex_entry : this->m_adjacency_list)
217 _computeVertexLevel(vertex_entry.first, 0);
218 m_compute_vertex_levels = false;
219 }
220
221 void _computeVertexLevel(const VertexType& vertex, Integer level)
222 {
223 // Check for cycles
224 if (!m_colored_vertices.insert(std::cref(vertex)).second)
225 throw FatalErrorException("Cycle in graph. Exiting");
226
227 // Try to insert vertex at the given level
228 bool update_children = true;
229 auto vertex_level_set_entry = m_vertex_level_map.insert(std::make_pair(std::cref(vertex), level)); // use emplace when available (gcc >= 4.8.0)
230 if (!vertex_level_set_entry.second) // vertex already present
231 {
232 if (vertex_level_set_entry.first->second < level)
233 vertex_level_set_entry.first->second = level;
234 else
235 update_children = false;
236 }
237 if (update_children) {
238 auto vertex_adjacency_list = Base::m_adjacency_list.find(vertex);
239 if (vertex_adjacency_list != Base::m_adjacency_list.end()) {
240 ++level;
241 for (const auto& child_vertex : vertex_adjacency_list->second.first)
242 _computeVertexLevel(child_vertex, level);
243 }
244 }
245 // Remove vertex from cycle detection
246 m_colored_vertices.erase(vertex);
247 }
248
249 Integer _computeEdgeLevel(const EdgeType& edge)
250 {
251 auto edge_vertices_iterator = this->m_edge_to_vertex_map.find(edge);
252 if (edge_vertices_iterator == this->m_edge_to_vertex_map.end())
253 throw FatalErrorException("Not existing edge.");
254 typename Base::VertexPair edge_vertices = edge_vertices_iterator->second;
255 // edge level is set to source vertex level
256 Integer edge_level = m_vertex_level_map[std::cref(edge_vertices.first)];
257 // if the edge crosses graph levels (ie vertices level difference > 1), we put edge_level =-1, since we don't want to keep it in the spanning tree
258 if (math::abs(edge_level - m_vertex_level_map[std::cref(edge_vertices.second)]) > 1)
259 edge_level = -1;
260 return edge_level;
261 }
262
263 void _printGraphEntry(const typename Base::AdjacencyListType::value_type& vertex_entry)
264 {
265 this->m_trace_mng->info() << "-- Vertex " << vertex_entry.first << " depends on ";
266 for (const auto& connected_vertex : vertex_entry.second.first)
267 this->m_trace_mng->info() << " - " << connected_vertex;
268 }
269};
270
271/*---------------------------------------------------------------------------*/
272/*---------------------------------------------------------------------------*/
273
274} // End namespace Arcane
275
276/*---------------------------------------------------------------------------*/
277/*---------------------------------------------------------------------------*/
278
279#endif
Arcane configuration file.
DirectedAcyclicGraphT(ITraceMng *trace_mng)
void addEdge(const VertexType &source_vertex, const VertexType &target_vertex, const EdgeType &source_to_target_edge)
Multiple edges (consisting of the same source and target nodes) are not allowed (throws FatalErrorExc...
Definition GraphBaseT.h:140
GraphBaseT(ITraceMng *trace_mng)
Definition GraphBaseT.h:53
EdgeToVertexMap m_edge_to_vertex_map
target_vertex -> source_vertices
Definition GraphBaseT.h:266
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type representing an integer.