slang-netlist  0.12.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
136 auto addEdge(NodeType &targetNode) -> EdgeType & {
137 return withEndpointsLocked(targetNode, [&] {
138 parallelOutEdges = true;
139 return appendEdge(targetNode);
140 });
141 }
142
161 auto getOrAddEdge(NodeType &targetNode) -> EdgeType & {
162 return withEndpointsLocked(targetNode, [&] {
163 if (auto *existing = lookupOutEdge(targetNode); existing != nullptr) {
164 return existing;
165 }
166 return appendEdge(targetNode);
167 });
168 }
169
172 auto removeEdge(NodeType &targetNode) -> bool {
173 auto edgeIt = findEdgeTo(targetNode);
174 if (edgeIt != outEdges.end()) {
175 auto success = targetNode.removeInEdge(getDerived());
176 assert(success && "No corresponding in edge reference");
177 outEdges.erase(edgeIt);
178 // Re-point or drop the index entry: a parallel edge to the same
179 // target may still survive in outEdges.
180 if (outEdgeIndex != nullptr) {
181 auto survivor = std::ranges::find_if(outEdges, [&](auto &e) {
182 return &e->getTargetNode() == &targetNode;
183 });
184 if (survivor == outEdges.end()) {
185 outEdgeIndex->erase(&targetNode);
186 } else {
187 (*outEdgeIndex)[&targetNode] = survivor->get();
188 }
189 }
190 return success;
191 }
192 return false;
193 }
194
198 auto mayHaveParallelOutEdges() const -> bool { return parallelOutEdges; }
199
207 template <typename Predicate> void removeOutEdgesIf(Predicate pred) {
208 std::vector<NodeType *> targets;
209 for (auto const &edge : outEdges) {
210 if (pred(*edge)) {
211 targets.push_back(&edge->getTargetNode());
212 }
213 }
214 if (targets.empty()) {
215 return;
216 }
217 // Visit each target once, however many of its in-edges are going.
218 std::ranges::sort(targets);
219 targets.erase(std::ranges::unique(targets).begin(), targets.end());
220 auto *self = &getDerived();
221 for (auto *target : targets) {
222 std::erase_if(target->inEdges, [&](EdgeType *edge) {
223 return &edge->getSourceNode() == self && pred(*edge);
224 });
225 }
226 std::erase_if(outEdges,
227 [&](OutEdgePtrType const &edge) { return pred(*edge); });
228 if (outEdgeIndex != nullptr) {
229 buildOutEdgeIndex();
230 }
231 }
232
235 // Remove outgoing edges.
236 for (auto &edge : outEdges) {
237 edge->getTargetNode().removeInEdge(getDerived());
238 }
239 outEdges.clear();
240 outEdgeIndex.reset();
241 // Remove incoming edges, creating a temporary list to avoid
242 // invalidating the iterator.
243 std::vector<NodeType *> sourceNodes;
244 for (auto &edge : inEdges) {
245 sourceNodes.push_back(&edge->getSourceNode());
246 }
247 for (auto *sourceNode : sourceNodes) {
248 sourceNode->removeEdge(getDerived());
249 }
250 assert(inEdges.empty());
251 }
252
255 auto getEdgesTo(const NodeType &targetNode, std::vector<EdgeType *> &result)
256 -> bool {
257 assert(result.empty() && "Expected the results parameter to be empty");
258 for (auto &edge : outEdges) {
259 if (edge->getTargetNode() == targetNode) {
260 result.push_back(edge.get());
261 }
262 }
263 return !result.empty();
264 }
265
267 auto getInEdges() const -> const InEdgeListType & { return inEdges; }
268 auto getOutEdges() const -> const OutEdgeListType & { return outEdges; }
269
271 auto inDegree() const -> size_t { return inEdges.size(); }
272
274 auto outDegree() const -> size_t { return outEdges.size(); }
275
276protected:
280 mutable std::mutex edgeMutex;
281
284
290 using OutEdgeIndex = flat_hash_map<NodeType const *, EdgeType *>;
291 std::unique_ptr<OutEdgeIndex> outEdgeIndex;
292
295 static constexpr size_t outEdgeIndexThreshold = 16;
296
301 bool parallelOutEdges{false};
302
303 // As the default implementation use address comparison for equality.
304 auto isEqualTo(const NodeType &node) const -> bool { return this == &node; }
305
306 // Cast the 'this' pointer to the derived type and return a reference.
307 auto getDerived() -> NodeType & { return *static_cast<NodeType *>(this); }
308 auto getDerived() const -> const NodeType & {
309 return *static_cast<const NodeType *>(this);
310 }
311
312private:
317 auto removeInEdge(NodeType &sourceNode) -> bool {
318 auto edgeIt = findEdgeFrom(sourceNode);
319 if (edgeIt != inEdges.end()) {
320 inEdges.erase(edgeIt);
321 return true;
322 }
323 return false;
324 }
325
332 template <typename Fn>
333 auto withEndpointsLocked(NodeType &targetNode, Fn fn) -> EdgeType & {
334 if (&getDerived() == &targetNode) {
335 std::lock_guard<std::mutex> lock(edgeMutex);
336 return *fn();
337 }
338 std::scoped_lock lock(edgeMutex, targetNode.edgeMutex);
339 return *fn();
340 }
341
345 auto appendEdge(NodeType &targetNode) -> EdgeType * {
346 auto edge = std::make_unique<EdgeType>(getDerived(), targetNode);
347 auto *edgePtr = edge.get();
348 outEdges.emplace_back(std::move(edge));
349 insertOutEdgeIndex(&targetNode, edgePtr);
350 targetNode.inEdges.push_back(edgePtr);
351 return edgePtr;
352 }
353
357 auto lookupOutEdge(NodeType const &targetNode) -> EdgeType * {
358 if (outEdgeIndex != nullptr) {
359 auto it = outEdgeIndex->find(&targetNode);
360 return it != outEdgeIndex->end() ? it->second : nullptr;
361 }
362 auto it = findEdgeTo(targetNode);
363 return it != outEdges.end() ? it->get() : nullptr;
364 }
365
368 void buildOutEdgeIndex() {
369 outEdgeIndex = std::make_unique<OutEdgeIndex>();
370 outEdgeIndex->reserve(outEdges.size());
371 for (auto &e : outEdges) {
372 outEdgeIndex->try_emplace(&e->getTargetNode(), e.get());
373 }
374 }
375
381 void insertOutEdgeIndex(NodeType const *targetNode, EdgeType *edgePtr) {
382 if (outEdgeIndex != nullptr) {
383 outEdgeIndex->try_emplace(targetNode, edgePtr);
384 } else if (outEdges.size() > outEdgeIndexThreshold) {
385 buildOutEdgeIndex();
386 }
387 }
388};
389
396template <class NodeType, class EdgeType> class DirectedGraph {
397public:
398 using NodePtrType = std::unique_ptr<NodeType>;
399 using NodeListType = std::vector<NodePtrType>;
400 using iterator = typename NodeListType::iterator;
401 using const_iterator = typename NodeListType::const_iterator;
402 using node_descriptor = size_t;
403 using edge_descriptor = EdgeType *;
405
406 static const size_t null_node = std::numeric_limits<size_t>::max();
407
408 DirectedGraph() = default;
409
410 auto begin() const -> const_iterator { return nodes.begin(); }
411 auto end() const -> const_iterator { return nodes.end(); }
412 auto begin() -> iterator { return nodes.begin(); }
413 auto end() -> iterator { return nodes.end(); }
414
415 auto findNode(const NodeType &nodeToFind) const -> node_descriptor {
416 auto it =
417 std::ranges::find_if(nodes, [&nodeToFind](const NodePtrType &node) {
418 return const_cast<const NodeType &>(*node) == nodeToFind;
419 });
420 if (it != nodes.end()) {
421 return it - nodes.begin();
422 }
423 return null_node;
424 }
425
427 auto getNode(node_descriptor node) const -> NodeType & {
428 assert(node < nodes.size() && "Node does not exist");
429 return *nodes[node];
430 }
431
435 auto addNode() -> NodeType & {
436 std::lock_guard<std::mutex> lock(nodesMutex);
437 nodes.push_back(std::make_unique<NodeType>());
438 return *(nodes.back().get());
439 }
440
444 auto addNode(std::unique_ptr<NodeType> node) -> NodeType & {
445 std::lock_guard<std::mutex> lock(nodesMutex);
446 nodes.push_back(std::move(node));
447 return *(nodes.back().get());
448 }
449
454 auto removeNode(NodeType &nodeToRemove) -> bool {
455 auto nodeToRemoveDesc = findNode(nodeToRemove);
456 if (nodeToRemoveDesc >= nodes.size()) {
457 // The node is not in the graph.
458 return false;
459 }
460 // Remove all edges to and from the node for removal.
461 nodeToRemove.clearAllEdges();
462 // Remove the node itself.
463 nodes.erase(std::ranges::next(nodes.begin(), nodeToRemoveDesc));
464 return true;
465 }
466
469 auto getOrAddEdge(NodeType &sourceNode, NodeType &targetNode) -> EdgeType & {
470 assert(findNode(sourceNode) < nodes.size() && "Source node does not exist");
471 assert(findNode(targetNode) < nodes.size() && "Target node does not exist");
472 return sourceNode.getOrAddEdge(targetNode);
473 }
474
477 auto addEdge(NodeType &sourceNode, NodeType &targetNode) -> EdgeType & {
478 assert(findNode(sourceNode) < nodes.size() && "Source node does not exist");
479 assert(findNode(targetNode) < nodes.size() && "Target node does not exist");
480 return sourceNode.addEdge(targetNode);
481 }
482
485 auto removeEdge(NodeType &sourceNode, NodeType &targetNode) -> bool {
486 assert(findNode(sourceNode) < nodes.size() && "Source node does not exist");
487 assert(findNode(targetNode) < nodes.size() && "Target node does not exist");
488 return sourceNode.removeEdge(targetNode);
489 }
490
492 auto outDegree(const NodeType &node) const -> size_t {
493 assert(findNode(node) < nodes.size() && "Node does not exist");
494 return node.outDegree();
495 }
496
498 auto inDegree(const NodeType &node) const -> size_t {
499 assert(findNode(node) < nodes.size() && "Node does not exist");
500 return node.inDegree();
501 }
502
504 auto numNodes() const -> size_t { return nodes.size(); }
505
507 auto numEdges() const -> size_t {
508 size_t count = 0;
509 for (auto &node : nodes) {
510 count += node->outDegree();
511 }
512 return count;
513 }
514
515protected:
518 mutable std::mutex nodesMutex;
519
521};
522
523} // 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
std::vector< NodePtrType > NodeListType
Definition DirectedGraph.hpp:399
auto numEdges() const -> size_t
Return the number of edges in the graph.
Definition DirectedGraph.hpp:507
auto getOrAddEdge(NodeType &sourceNode, NodeType &targetNode) -> EdgeType &
Definition DirectedGraph.hpp:469
auto addEdge(NodeType &sourceNode, NodeType &targetNode) -> EdgeType &
Definition DirectedGraph.hpp:477
auto inDegree(const NodeType &node) const -> size_t
Return the number of edges incident to the specified node.
Definition DirectedGraph.hpp:498
size_t node_descriptor
Definition DirectedGraph.hpp:402
auto removeNode(NodeType &nodeToRemove) -> bool
Definition DirectedGraph.hpp:454
auto addNode(std::unique_ptr< NodeType > node) -> NodeType &
Definition DirectedGraph.hpp:444
auto findNode(const NodeType &nodeToFind) const -> node_descriptor
Definition DirectedGraph.hpp:415
NodeListType nodes
Definition DirectedGraph.hpp:520
auto removeEdge(NodeType &sourceNode, NodeType &targetNode) -> bool
Definition DirectedGraph.hpp:485
auto begin() const -> const_iterator
Definition DirectedGraph.hpp:410
static const size_t null_node
Definition DirectedGraph.hpp:406
std::mutex nodesMutex
Definition DirectedGraph.hpp:518
auto outDegree(const NodeType &node) const -> size_t
Return the number of edges outgoing from the specified node.
Definition DirectedGraph.hpp:492
auto end() -> iterator
Definition DirectedGraph.hpp:413
auto addNode() -> NodeType &
Definition DirectedGraph.hpp:435
typename NodeListType::iterator iterator
Definition DirectedGraph.hpp:400
DirectedGraph< NodeType, EdgeType > DirectedGraphType
Definition DirectedGraph.hpp:404
auto begin() -> iterator
Definition DirectedGraph.hpp:412
std::unique_ptr< NodeType > NodePtrType
Definition DirectedGraph.hpp:398
auto getNode(node_descriptor node) const -> NodeType &
Given a node descriptor, return the node by reference.
Definition DirectedGraph.hpp:427
typename NodeListType::const_iterator const_iterator
Definition DirectedGraph.hpp:401
EdgeType * edge_descriptor
Definition DirectedGraph.hpp:403
auto end() const -> const_iterator
Definition DirectedGraph.hpp:411
auto numNodes() const -> size_t
Return the size of the graph.
Definition DirectedGraph.hpp:504
auto end() -> iterator
Definition DirectedGraph.hpp:83
virtual ~Node()=default
typename OutEdgeListType::const_iterator const_iterator
Definition DirectedGraph.hpp:65
void removeOutEdgesIf(Predicate pred)
Definition DirectedGraph.hpp:207
std::vector< OutEdgePtrType > OutEdgeListType
Definition DirectedGraph.hpp:62
auto getEdgesTo(const NodeType &targetNode, std::vector< EdgeType * > &result) -> bool
Definition DirectedGraph.hpp:255
auto getOrAddEdge(NodeType &targetNode) -> EdgeType &
Definition DirectedGraph.hpp:161
auto outDegree() const -> size_t
Return the total number of edges outgoing from this node.
Definition DirectedGraph.hpp:274
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
bool parallelOutEdges
Definition DirectedGraph.hpp:301
auto operator==(const NodeType &N) const -> bool
Definition DirectedGraph.hpp:99
auto getDerived() const -> const NodeType &
Definition DirectedGraph.hpp:308
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
auto mayHaveParallelOutEdges() const -> bool
Definition DirectedGraph.hpp:198
typename InEdgeListType::iterator in_iterator
Definition DirectedGraph.hpp:66
auto removeEdge(NodeType &targetNode) -> bool
Definition DirectedGraph.hpp:172
Node(const Node &)=delete
static constexpr size_t outEdgeIndexThreshold
Definition DirectedGraph.hpp:295
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 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:267
auto isEqualTo(const NodeType &node) const -> bool
Definition DirectedGraph.hpp:304
std::unique_ptr< OutEdgeIndex > outEdgeIndex
Definition DirectedGraph.hpp:291
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:290
auto getOutEdges() const -> const OutEdgeListType &
Definition DirectedGraph.hpp:268
auto getDerived() -> NodeType &
Definition DirectedGraph.hpp:307
void clearAllEdges()
Remove all edges to/from this node.
Definition DirectedGraph.hpp:234
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:280
auto addEdge(NodeType &targetNode) -> EdgeType &
Definition DirectedGraph.hpp:136
auto operator=(const Node &) -> Node &=delete
InEdgeListType inEdges
Definition DirectedGraph.hpp:282
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:283
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:271
Definition FormatBuffer.hpp:9