slang-netlist  0.9.0
Loading...
Searching...
No Matches
DirectedGraph.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cassert>
5#include <limits>
6#include <memory>
7#include <mutex>
8#include <vector>
9
10#include "slang/util/FlatMap.h"
11
12namespace slang::netlist {
13
15template <class NodeType, class EdgeType> class DirectedEdge {
16public:
19
22
26 friend auto operator==(const EdgeType &A, const EdgeType &B) noexcept
27 -> bool {
28 return A.getDerived().isEqualTo(B);
29 }
30 auto operator==(const EdgeType &E) const -> bool {
31 return getDerived().isEqualTo(E);
32 }
33
35 auto getSourceNode() const -> NodeType & { return sourceNode; }
36
38 auto getTargetNode() const -> NodeType & { return targetNode; }
39
40protected:
41 // As the default implementation use address comparison for equality.
42 auto isEqualTo(const EdgeType &edge) const -> bool { return this == &edge; }
43
44 // Cast the 'this' pointer to the derived type and return a reference.
45 auto getDerived() -> EdgeType & { return *static_cast<EdgeType *>(this); }
46 auto getDerived() const -> const EdgeType & {
47 return *static_cast<const EdgeType *>(this);
48 }
49
50 NodeType &sourceNode;
51 NodeType &targetNode;
52};
53
59template <class NodeType, class EdgeType> class Node {
60public:
61 using OutEdgePtrType = std::unique_ptr<EdgeType>;
62 using OutEdgeListType = std::vector<OutEdgePtrType>;
63 using InEdgeListType = std::vector<EdgeType *>;
64 using iterator = typename OutEdgeListType::iterator;
65 using const_iterator = typename OutEdgeListType::const_iterator;
66 using in_iterator = typename InEdgeListType::iterator;
67 using const_in_iterator = typename InEdgeListType::const_iterator;
68 using edge_descriptor = EdgeType *;
69
70 Node() = default;
71 virtual ~Node() = default;
72
73 // Non-copyable/non-movable: edgeMutex is not movable.
74 Node(const Node &) = delete;
75 Node(Node &&) = delete;
76 auto operator=(const Node &) -> Node & = delete;
77 auto operator=(Node &&) -> Node & = delete;
78
79 // Iterator methods for outgoing edges.
80 auto begin() const -> const_iterator { return outEdges.begin(); }
81 auto end() const -> const_iterator { return outEdges.end(); }
82 auto begin() -> iterator { return outEdges.begin(); }
83 auto end() -> iterator { return outEdges.end(); }
84
85 // Iterator methods for incoming edges.
86 auto inBegin() -> in_iterator { return inEdges.begin(); }
87 auto inEnd() -> in_iterator { return inEdges.end(); }
88 auto inBegin() const -> const_in_iterator { return inEdges.begin(); }
89 auto inEnd() const -> const_in_iterator { return inEdges.end(); }
90
94 friend auto operator==(NodeType const &A, NodeType const &B) noexcept
95 -> bool {
96 return A.getDerived().isEqualTo(B);
97 }
98
99 auto operator==(const NodeType &N) const -> bool {
100 return getDerived().isEqualTo(N);
101 }
102
104 auto findEdgeFrom(const NodeType &sourceNode) -> in_iterator {
105 return std::ranges::find_if(inEdges, [&](EdgeType *e) {
106 return &e->getSourceNode() == &sourceNode;
107 });
108 }
109
111 auto findEdgeFrom(const NodeType &sourceNode) const -> const_in_iterator {
112 return std::ranges::find_if(inEdges, [&](EdgeType const *e) {
113 return &e->getSourceNode() == &sourceNode;
114 });
115 }
116
118 auto findEdgeTo(const NodeType &targetNode) -> iterator {
119 return std::ranges::find_if(outEdges, [&](OutEdgePtrType const &e) {
120 return &e->getTargetNode() == &targetNode;
121 });
122 }
123
125 auto findEdgeTo(const NodeType &targetNode) const -> const_iterator {
126 return std::ranges::find_if(outEdges, [&](OutEdgePtrType const &e) {
127 return &e->getTargetNode() == &targetNode;
128 });
129 }
130
144 auto addEdge(NodeType &targetNode) -> EdgeType & {
145 bool isSelfEdge = (&getDerived() == &targetNode);
146 std::lock_guard<std::mutex> lock(edgeMutex);
147 if (auto *existing = lookupOutEdge(targetNode); existing != nullptr) {
148 return *existing;
149 }
150 auto edge = std::make_unique<EdgeType>(getDerived(), targetNode);
151 auto *edgePtr = edge.get();
152 outEdges.emplace_back(std::move(edge));
153 insertOutEdgeIndex(&targetNode, edgePtr);
154 if (isSelfEdge) {
155 inEdges.push_back(edgePtr);
156 } else {
157 std::lock_guard<std::mutex> lock2(targetNode.edgeMutex);
158 targetNode.inEdges.push_back(edgePtr);
159 }
160 return *edgePtr;
161 }
162
170 auto addNewEdge(NodeType &targetNode) -> EdgeType & {
171 bool isSelfEdge = (&getDerived() == &targetNode);
172 auto edge = std::make_unique<EdgeType>(getDerived(), targetNode);
173 auto *edgePtr = edge.get();
174 if (isSelfEdge) {
175 std::lock_guard<std::mutex> lock(edgeMutex);
176 outEdges.emplace_back(std::move(edge));
177 // If no entry exists yet (caller went straight to addNewEdge), seed
178 // it so a later addEdge dedupes against this edge instead of
179 // creating a third parallel one.
180 tryInsertOutEdgeIndex(&targetNode, edgePtr);
181 inEdges.push_back(edgePtr);
182 } else {
183 {
184 std::lock_guard<std::mutex> lock(edgeMutex);
185 outEdges.emplace_back(std::move(edge));
186 tryInsertOutEdgeIndex(&targetNode, edgePtr);
187 }
188 {
189 std::lock_guard<std::mutex> lock(targetNode.edgeMutex);
190 targetNode.inEdges.push_back(edgePtr);
191 }
192 }
193 return *edgePtr;
194 }
195
198 auto removeEdge(NodeType &targetNode) -> bool {
199 auto edgeIt = findEdgeTo(targetNode);
200 if (edgeIt != outEdges.end()) {
201 auto success = targetNode.removeInEdge(getDerived());
202 assert(success && "No corresponding in edge reference");
203 outEdges.erase(edgeIt);
204 // Re-point or drop the index entry: a parallel edge to the same
205 // target may still survive in outEdges.
206 if (outEdgeIndex != nullptr) {
207 auto survivor = std::ranges::find_if(outEdges, [&](auto &e) {
208 return &e->getTargetNode() == &targetNode;
209 });
210 if (survivor == outEdges.end()) {
211 outEdgeIndex->erase(&targetNode);
212 } else {
213 (*outEdgeIndex)[&targetNode] = survivor->get();
214 }
215 }
216 return success;
217 }
218 return false;
219 }
220
223 // Remove outgoing edges.
224 for (auto &edge : outEdges) {
225 edge->getTargetNode().removeInEdge(getDerived());
226 }
227 outEdges.clear();
228 outEdgeIndex.reset();
229 // Remove incoming edges, creating a temporary list to avoid
230 // invalidating the iterator.
231 std::vector<NodeType *> sourceNodes;
232 for (auto &edge : inEdges) {
233 sourceNodes.push_back(&edge->getSourceNode());
234 }
235 for (auto *sourceNode : sourceNodes) {
236 sourceNode->removeEdge(getDerived());
237 }
238 assert(inEdges.empty());
239 }
240
243 auto getEdgesTo(const NodeType &targetNode, std::vector<EdgeType *> &result)
244 -> bool {
245 assert(result.empty() && "Expected the results parameter to be empty");
246 for (auto &edge : outEdges) {
247 if (edge->getTargetNode() == targetNode) {
248 result.push_back(edge.get());
249 }
250 }
251 return !result.empty();
252 }
253
255 auto getInEdges() const -> const InEdgeListType & { return inEdges; }
256 auto getOutEdges() const -> const OutEdgeListType & { return outEdges; }
257
259 auto inDegree() const -> size_t { return inEdges.size(); }
260
262 auto outDegree() const -> size_t { return outEdges.size(); }
263
264protected:
268 mutable std::mutex edgeMutex;
269
272
278 using OutEdgeIndex = flat_hash_map<NodeType const *, EdgeType *>;
279 std::unique_ptr<OutEdgeIndex> outEdgeIndex;
280
283 static constexpr size_t outEdgeIndexThreshold = 16;
284
285 // As the default implementation use address comparison for equality.
286 auto isEqualTo(const NodeType &node) const -> bool { return this == &node; }
287
288 // Cast the 'this' pointer to the derived type and return a reference.
289 auto getDerived() -> NodeType & { return *static_cast<NodeType *>(this); }
290 auto getDerived() const -> const NodeType & {
291 return *static_cast<const NodeType *>(this);
292 }
293
294private:
299 auto removeInEdge(NodeType &sourceNode) -> bool {
300 auto edgeIt = findEdgeFrom(sourceNode);
301 if (edgeIt != inEdges.end()) {
302 inEdges.erase(edgeIt);
303 return true;
304 }
305 return false;
306 }
307
311 auto lookupOutEdge(NodeType const &targetNode) -> EdgeType * {
312 if (outEdgeIndex != nullptr) {
313 auto it = outEdgeIndex->find(&targetNode);
314 return it != outEdgeIndex->end() ? it->second : nullptr;
315 }
316 auto it = std::ranges::find_if(outEdges, [&](OutEdgePtrType const &e) {
317 return &e->getTargetNode() == &targetNode;
318 });
319 return it != outEdges.end() ? it->get() : nullptr;
320 }
321
324 void buildOutEdgeIndex() {
325 outEdgeIndex = std::make_unique<OutEdgeIndex>();
326 outEdgeIndex->reserve(outEdges.size());
327 for (auto &e : outEdges) {
328 outEdgeIndex->try_emplace(&e->getTargetNode(), e.get());
329 }
330 }
331
335 void insertOutEdgeIndex(NodeType const *targetNode, EdgeType *edgePtr) {
336 if (outEdgeIndex != nullptr) {
337 outEdgeIndex->emplace(targetNode, edgePtr);
338 } else if (outEdges.size() > outEdgeIndexThreshold) {
339 buildOutEdgeIndex();
340 }
341 }
342
345 void tryInsertOutEdgeIndex(NodeType const *targetNode, EdgeType *edgePtr) {
346 if (outEdgeIndex != nullptr) {
347 outEdgeIndex->try_emplace(targetNode, edgePtr);
348 } else if (outEdges.size() > outEdgeIndexThreshold) {
349 buildOutEdgeIndex();
350 }
351 }
352};
353
358template <class NodeType, class EdgeType> class DirectedGraph {
359public:
360 using NodePtrType = std::unique_ptr<NodeType>;
361 using NodeListType = std::vector<NodePtrType>;
362 using iterator = typename NodeListType::iterator;
363 using const_iterator = typename NodeListType::const_iterator;
364 using node_descriptor = size_t;
365 using edge_descriptor = EdgeType *;
367
368 static const size_t null_node = std::numeric_limits<size_t>::max();
369
370 DirectedGraph() = default;
371
372 auto begin() const -> const_iterator { return nodes.begin(); }
373 auto end() const -> const_iterator { return nodes.end(); }
374 auto begin() -> iterator { return nodes.begin(); }
375 auto end() -> iterator { return nodes.end(); }
376
377 auto findNode(const NodeType &nodeToFind) const -> node_descriptor {
378 auto it =
379 std::ranges::find_if(nodes, [&nodeToFind](const NodePtrType &node) {
380 return const_cast<const NodeType &>(*node) == nodeToFind;
381 });
382 if (it != nodes.end()) {
383 return it - nodes.begin();
384 }
385 return null_node;
386 }
387
389 auto getNode(node_descriptor node) const -> NodeType & {
390 assert(node < nodes.size() && "Node does not exist");
391 return *nodes[node];
392 }
393
397 auto addNode() -> NodeType & {
398 std::lock_guard<std::mutex> lock(nodesMutex);
399 nodes.push_back(std::make_unique<NodeType>());
400 return *(nodes.back().get());
401 }
402
406 auto addNode(std::unique_ptr<NodeType> node) -> NodeType & {
407 std::lock_guard<std::mutex> lock(nodesMutex);
408 nodes.push_back(std::move(node));
409 return *(nodes.back().get());
410 }
411
416 auto removeNode(NodeType &nodeToRemove) -> bool {
417 auto nodeToRemoveDesc = findNode(nodeToRemove);
418 if (nodeToRemoveDesc >= nodes.size()) {
419 // The node is not in the graph.
420 return false;
421 }
422 // Remove all edges to and from the node for removal.
423 nodeToRemove.clearAllEdges();
424 // Remove the node itself.
425 nodes.erase(std::ranges::next(nodes.begin(), nodeToRemoveDesc));
426 return true;
427 }
428
430 auto addEdge(NodeType &sourceNode, NodeType &targetNode) -> EdgeType & {
431 assert(findNode(sourceNode) < nodes.size() && "Source node does not exist");
432 assert(findNode(targetNode) < nodes.size() && "Target node does not exist");
433 return sourceNode.addEdge(targetNode);
434 }
435
438 auto addNewEdge(NodeType &sourceNode, NodeType &targetNode) -> EdgeType & {
439 assert(findNode(sourceNode) < nodes.size() && "Source node does not exist");
440 assert(findNode(targetNode) < nodes.size() && "Target node does not exist");
441 return sourceNode.addNewEdge(targetNode);
442 }
443
446 auto removeEdge(NodeType &sourceNode, NodeType &targetNode) -> bool {
447 assert(findNode(sourceNode) < nodes.size() && "Source node does not exist");
448 assert(findNode(targetNode) < nodes.size() && "Target node does not exist");
449 return sourceNode.removeEdge(targetNode);
450 }
451
453 auto outDegree(const NodeType &node) const -> size_t {
454 assert(findNode(node) < nodes.size() && "Node does not exist");
455 return node.outDegree();
456 }
457
459 auto inDegree(const NodeType &node) const -> size_t {
460 assert(findNode(node) < nodes.size() && "Node does not exist");
461 return node.inDegree();
462 }
463
465 auto numNodes() const -> size_t { return nodes.size(); }
466
468 auto numEdges() const -> size_t {
469 size_t count = 0;
470 for (auto &node : nodes) {
471 count += node->outDegree();
472 }
473 return count;
474 }
475
476protected:
479 mutable std::mutex nodesMutex;
480
482};
483
484} // namespace slang::netlist
auto operator=(const DirectedEdge< NodeType, EdgeType > &edge) -> DirectedEdge< NodeType, EdgeType > &=default
NodeType & sourceNode
Definition DirectedGraph.hpp:50
auto operator==(const EdgeType &E) const -> bool
Definition DirectedGraph.hpp:30
NodeType & targetNode
Definition DirectedGraph.hpp:51
auto getDerived() -> EdgeType &
Definition DirectedGraph.hpp:45
auto getDerived() const -> const EdgeType &
Definition DirectedGraph.hpp:46
auto getTargetNode() const -> NodeType &
Return the target node of this edge.
Definition DirectedGraph.hpp:38
auto isEqualTo(const EdgeType &edge) const -> bool
Definition DirectedGraph.hpp:42
DirectedEdge(NodeType &sourceNode, NodeType &targetNode)
Definition DirectedGraph.hpp:17
auto getSourceNode() const -> NodeType &
Return the source node of this edge.
Definition DirectedGraph.hpp:35
friend auto operator==(const EdgeType &A, const EdgeType &B) noexcept -> bool
Definition DirectedGraph.hpp:26
auto addNewEdge(NodeType &sourceNode, NodeType &targetNode) -> EdgeType &
Definition DirectedGraph.hpp:438
std::vector< NodePtrType > NodeListType
Definition DirectedGraph.hpp:361
auto numEdges() const -> size_t
Return the number of edges in the graph.
Definition DirectedGraph.hpp:468
auto addEdge(NodeType &sourceNode, NodeType &targetNode) -> EdgeType &
Add an edge between two existing nodes in the graph.
Definition DirectedGraph.hpp:430
auto inDegree(const NodeType &node) const -> size_t
Return the number of edges incident to the specified node.
Definition DirectedGraph.hpp:459
size_t node_descriptor
Definition DirectedGraph.hpp:364
auto removeNode(NodeType &nodeToRemove) -> bool
Definition DirectedGraph.hpp:416
auto addNode(std::unique_ptr< NodeType > node) -> NodeType &
Definition DirectedGraph.hpp:406
auto findNode(const NodeType &nodeToFind) const -> node_descriptor
Definition DirectedGraph.hpp:377
NodeListType nodes
Definition DirectedGraph.hpp:481
auto removeEdge(NodeType &sourceNode, NodeType &targetNode) -> bool
Definition DirectedGraph.hpp:446
auto begin() const -> const_iterator
Definition DirectedGraph.hpp:372
static const size_t null_node
Definition DirectedGraph.hpp:368
std::mutex nodesMutex
Definition DirectedGraph.hpp:479
auto outDegree(const NodeType &node) const -> size_t
Return the number of edges outgoing from the specified node.
Definition DirectedGraph.hpp:453
auto end() -> iterator
Definition DirectedGraph.hpp:375
auto addNode() -> NodeType &
Definition DirectedGraph.hpp:397
typename NodeListType::iterator iterator
Definition DirectedGraph.hpp:362
DirectedGraph< NodeType, EdgeType > DirectedGraphType
Definition DirectedGraph.hpp:366
auto begin() -> iterator
Definition DirectedGraph.hpp:374
std::unique_ptr< NodeType > NodePtrType
Definition DirectedGraph.hpp:360
auto getNode(node_descriptor node) const -> NodeType &
Given a node descriptor, return the node by reference.
Definition DirectedGraph.hpp:389
typename NodeListType::const_iterator const_iterator
Definition DirectedGraph.hpp:363
EdgeType * edge_descriptor
Definition DirectedGraph.hpp:365
auto end() const -> const_iterator
Definition DirectedGraph.hpp:373
auto numNodes() const -> size_t
Return the size of the graph.
Definition DirectedGraph.hpp:465
auto end() -> iterator
Definition DirectedGraph.hpp:83
virtual ~Node()=default
typename OutEdgeListType::const_iterator const_iterator
Definition DirectedGraph.hpp:65
std::vector< OutEdgePtrType > OutEdgeListType
Definition DirectedGraph.hpp:62
auto getEdgesTo(const NodeType &targetNode, std::vector< EdgeType * > &result) -> bool
Definition DirectedGraph.hpp:243
auto outDegree() const -> size_t
Return the total number of edges outgoing from this node.
Definition DirectedGraph.hpp:262
typename InEdgeListType::const_iterator const_in_iterator
Definition DirectedGraph.hpp:67
typename OutEdgeListType::iterator iterator
Definition DirectedGraph.hpp:64
auto inEnd() const -> const_in_iterator
Definition DirectedGraph.hpp:89
auto operator==(const NodeType &N) const -> bool
Definition DirectedGraph.hpp:99
auto getDerived() const -> const NodeType &
Definition DirectedGraph.hpp:290
auto inBegin() -> in_iterator
Definition DirectedGraph.hpp:86
EdgeType * edge_descriptor
Definition DirectedGraph.hpp:68
friend auto operator==(NodeType const &A, NodeType const &B) noexcept -> bool
Definition DirectedGraph.hpp:94
typename InEdgeListType::iterator in_iterator
Definition DirectedGraph.hpp:66
auto removeEdge(NodeType &targetNode) -> bool
Definition DirectedGraph.hpp:198
Node(const Node &)=delete
static constexpr size_t outEdgeIndexThreshold
Definition DirectedGraph.hpp:283
auto findEdgeFrom(const NodeType &sourceNode) const -> const_in_iterator
Return an iterator to the edge connecting the source node.
Definition DirectedGraph.hpp:111
auto begin() -> iterator
Definition DirectedGraph.hpp:82
auto end() const -> const_iterator
Definition DirectedGraph.hpp:81
auto addNewEdge(NodeType &targetNode) -> EdgeType &
Definition DirectedGraph.hpp:170
auto inBegin() const -> const_in_iterator
Definition DirectedGraph.hpp:88
auto getInEdges() const -> const InEdgeListType &
Return the list of outgoing edges from this node.
Definition DirectedGraph.hpp:255
auto isEqualTo(const NodeType &node) const -> bool
Definition DirectedGraph.hpp:286
std::unique_ptr< OutEdgeIndex > outEdgeIndex
Definition DirectedGraph.hpp:279
auto inEnd() -> in_iterator
Definition DirectedGraph.hpp:87
auto begin() const -> const_iterator
Definition DirectedGraph.hpp:80
std::vector< EdgeType * > InEdgeListType
Definition DirectedGraph.hpp:63
flat_hash_map< NodeType const *, EdgeType * > OutEdgeIndex
Definition DirectedGraph.hpp:278
auto getOutEdges() const -> const OutEdgeListType &
Definition DirectedGraph.hpp:256
auto getDerived() -> NodeType &
Definition DirectedGraph.hpp:289
void clearAllEdges()
Remove all edges to/from this node.
Definition DirectedGraph.hpp:222
auto operator=(Node &&) -> Node &=delete
auto findEdgeFrom(const NodeType &sourceNode) -> in_iterator
Return an iterator to the edge connecting the source node.
Definition DirectedGraph.hpp:104
std::mutex edgeMutex
Definition DirectedGraph.hpp:268
auto addEdge(NodeType &targetNode) -> EdgeType &
Definition DirectedGraph.hpp:144
auto operator=(const Node &) -> Node &=delete
InEdgeListType inEdges
Definition DirectedGraph.hpp:270
auto findEdgeTo(const NodeType &targetNode) const -> const_iterator
Return an iterator to the edge connecting the target node.
Definition DirectedGraph.hpp:125
auto findEdgeTo(const NodeType &targetNode) -> iterator
Return an iterator to the edge connecting the target node.
Definition DirectedGraph.hpp:118
OutEdgeListType outEdges
Definition DirectedGraph.hpp:271
std::unique_ptr< EdgeType > OutEdgePtrType
Definition DirectedGraph.hpp:61
Node(Node &&)=delete
auto inDegree() const -> size_t
Return the total number of edges incoming to this node.
Definition DirectedGraph.hpp:259
Definition Utilities.hpp:16