1 //===- Graph.h - PBQP Graph -------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
11 //===----------------------------------------------------------------------===//
13 #ifndef LLVM_CODEGEN_PBQP_GRAPH_H
14 #define LLVM_CODEGEN_PBQP_GRAPH_H
16 #include "llvm/ADT/STLExtras.h"
28 using NodeId
= unsigned;
29 using EdgeId
= unsigned;
31 /// Returns a value representing an invalid (non-existent) node.
32 static NodeId
invalidNodeId() {
33 return std::numeric_limits
<NodeId
>::max();
36 /// Returns a value representing an invalid (non-existent) edge.
37 static EdgeId
invalidEdgeId() {
38 return std::numeric_limits
<EdgeId
>::max();
43 /// Instances of this class describe PBQP problems.
45 template <typename SolverT
>
46 class Graph
: public GraphBase
{
48 using CostAllocator
= typename
SolverT::CostAllocator
;
51 using RawVector
= typename
SolverT::RawVector
;
52 using RawMatrix
= typename
SolverT::RawMatrix
;
53 using Vector
= typename
SolverT::Vector
;
54 using Matrix
= typename
SolverT::Matrix
;
55 using VectorPtr
= typename
CostAllocator::VectorPtr
;
56 using MatrixPtr
= typename
CostAllocator::MatrixPtr
;
57 using NodeMetadata
= typename
SolverT::NodeMetadata
;
58 using EdgeMetadata
= typename
SolverT::EdgeMetadata
;
59 using GraphMetadata
= typename
SolverT::GraphMetadata
;
64 using AdjEdgeList
= std::vector
<EdgeId
>;
65 using AdjEdgeIdx
= AdjEdgeList::size_type
;
66 using AdjEdgeItr
= AdjEdgeList::const_iterator
;
68 NodeEntry(VectorPtr Costs
) : Costs(std::move(Costs
)) {}
70 static AdjEdgeIdx
getInvalidAdjEdgeIdx() {
71 return std::numeric_limits
<AdjEdgeIdx
>::max();
74 AdjEdgeIdx
addAdjEdgeId(EdgeId EId
) {
75 AdjEdgeIdx Idx
= AdjEdgeIds
.size();
76 AdjEdgeIds
.push_back(EId
);
80 void removeAdjEdgeId(Graph
&G
, NodeId ThisNId
, AdjEdgeIdx Idx
) {
81 // Swap-and-pop for fast removal.
82 // 1) Update the adj index of the edge currently at back().
83 // 2) Move last Edge down to Idx.
85 // If Idx == size() - 1 then the setAdjEdgeIdx and swap are
86 // redundant, but both operations are cheap.
87 G
.getEdge(AdjEdgeIds
.back()).setAdjEdgeIdx(ThisNId
, Idx
);
88 AdjEdgeIds
[Idx
] = AdjEdgeIds
.back();
89 AdjEdgeIds
.pop_back();
92 const AdjEdgeList
& getAdjEdgeIds() const { return AdjEdgeIds
; }
95 NodeMetadata Metadata
;
98 AdjEdgeList AdjEdgeIds
;
103 EdgeEntry(NodeId N1Id
, NodeId N2Id
, MatrixPtr Costs
)
104 : Costs(std::move(Costs
)) {
107 ThisEdgeAdjIdxs
[0] = NodeEntry::getInvalidAdjEdgeIdx();
108 ThisEdgeAdjIdxs
[1] = NodeEntry::getInvalidAdjEdgeIdx();
111 void connectToN(Graph
&G
, EdgeId ThisEdgeId
, unsigned NIdx
) {
112 assert(ThisEdgeAdjIdxs
[NIdx
] == NodeEntry::getInvalidAdjEdgeIdx() &&
113 "Edge already connected to NIds[NIdx].");
114 NodeEntry
&N
= G
.getNode(NIds
[NIdx
]);
115 ThisEdgeAdjIdxs
[NIdx
] = N
.addAdjEdgeId(ThisEdgeId
);
118 void connect(Graph
&G
, EdgeId ThisEdgeId
) {
119 connectToN(G
, ThisEdgeId
, 0);
120 connectToN(G
, ThisEdgeId
, 1);
123 void setAdjEdgeIdx(NodeId NId
, typename
NodeEntry::AdjEdgeIdx NewIdx
) {
125 ThisEdgeAdjIdxs
[0] = NewIdx
;
127 assert(NId
== NIds
[1] && "Edge not connected to NId");
128 ThisEdgeAdjIdxs
[1] = NewIdx
;
132 void disconnectFromN(Graph
&G
, unsigned NIdx
) {
133 assert(ThisEdgeAdjIdxs
[NIdx
] != NodeEntry::getInvalidAdjEdgeIdx() &&
134 "Edge not connected to NIds[NIdx].");
135 NodeEntry
&N
= G
.getNode(NIds
[NIdx
]);
136 N
.removeAdjEdgeId(G
, NIds
[NIdx
], ThisEdgeAdjIdxs
[NIdx
]);
137 ThisEdgeAdjIdxs
[NIdx
] = NodeEntry::getInvalidAdjEdgeIdx();
140 void disconnectFrom(Graph
&G
, NodeId NId
) {
142 disconnectFromN(G
, 0);
144 assert(NId
== NIds
[1] && "Edge does not connect NId");
145 disconnectFromN(G
, 1);
149 NodeId
getN1Id() const { return NIds
[0]; }
150 NodeId
getN2Id() const { return NIds
[1]; }
153 EdgeMetadata Metadata
;
157 typename
NodeEntry::AdjEdgeIdx ThisEdgeAdjIdxs
[2];
160 // ----- MEMBERS -----
162 GraphMetadata Metadata
;
163 CostAllocator CostAlloc
;
164 SolverT
*Solver
= nullptr;
166 using NodeVector
= std::vector
<NodeEntry
>;
167 using FreeNodeVector
= std::vector
<NodeId
>;
169 FreeNodeVector FreeNodeIds
;
171 using EdgeVector
= std::vector
<EdgeEntry
>;
172 using FreeEdgeVector
= std::vector
<EdgeId
>;
174 FreeEdgeVector FreeEdgeIds
;
176 Graph(const Graph
&Other
) {}
178 // ----- INTERNAL METHODS -----
180 NodeEntry
&getNode(NodeId NId
) {
181 assert(NId
< Nodes
.size() && "Out of bound NodeId");
184 const NodeEntry
&getNode(NodeId NId
) const {
185 assert(NId
< Nodes
.size() && "Out of bound NodeId");
189 EdgeEntry
& getEdge(EdgeId EId
) { return Edges
[EId
]; }
190 const EdgeEntry
& getEdge(EdgeId EId
) const { return Edges
[EId
]; }
192 NodeId
addConstructedNode(NodeEntry N
) {
194 if (!FreeNodeIds
.empty()) {
195 NId
= FreeNodeIds
.back();
196 FreeNodeIds
.pop_back();
197 Nodes
[NId
] = std::move(N
);
200 Nodes
.push_back(std::move(N
));
205 EdgeId
addConstructedEdge(EdgeEntry E
) {
206 assert(findEdge(E
.getN1Id(), E
.getN2Id()) == invalidEdgeId() &&
207 "Attempt to add duplicate edge.");
209 if (!FreeEdgeIds
.empty()) {
210 EId
= FreeEdgeIds
.back();
211 FreeEdgeIds
.pop_back();
212 Edges
[EId
] = std::move(E
);
215 Edges
.push_back(std::move(E
));
218 EdgeEntry
&NE
= getEdge(EId
);
220 // Add the edge to the adjacency sets of its nodes.
221 NE
.connect(*this, EId
);
225 void operator=(const Graph
&Other
) {}
228 using AdjEdgeItr
= typename
NodeEntry::AdjEdgeItr
;
232 using iterator_category
= std::forward_iterator_tag
;
233 using value_type
= NodeId
;
234 using difference_type
= int;
235 using pointer
= NodeId
*;
236 using reference
= NodeId
&;
238 NodeItr(NodeId CurNId
, const Graph
&G
)
239 : CurNId(CurNId
), EndNId(G
.Nodes
.size()), FreeNodeIds(G
.FreeNodeIds
) {
240 this->CurNId
= findNextInUse(CurNId
); // Move to first in-use node id
243 bool operator==(const NodeItr
&O
) const { return CurNId
== O
.CurNId
; }
244 bool operator!=(const NodeItr
&O
) const { return !(*this == O
); }
245 NodeItr
& operator++() { CurNId
= findNextInUse(++CurNId
); return *this; }
246 NodeId
operator*() const { return CurNId
; }
249 NodeId
findNextInUse(NodeId NId
) const {
250 while (NId
< EndNId
&& is_contained(FreeNodeIds
, NId
)) {
256 NodeId CurNId
, EndNId
;
257 const FreeNodeVector
&FreeNodeIds
;
262 EdgeItr(EdgeId CurEId
, const Graph
&G
)
263 : CurEId(CurEId
), EndEId(G
.Edges
.size()), FreeEdgeIds(G
.FreeEdgeIds
) {
264 this->CurEId
= findNextInUse(CurEId
); // Move to first in-use edge id
267 bool operator==(const EdgeItr
&O
) const { return CurEId
== O
.CurEId
; }
268 bool operator!=(const EdgeItr
&O
) const { return !(*this == O
); }
269 EdgeItr
& operator++() { CurEId
= findNextInUse(++CurEId
); return *this; }
270 EdgeId
operator*() const { return CurEId
; }
273 EdgeId
findNextInUse(EdgeId EId
) const {
274 while (EId
< EndEId
&& is_contained(FreeEdgeIds
, EId
)) {
280 EdgeId CurEId
, EndEId
;
281 const FreeEdgeVector
&FreeEdgeIds
;
286 NodeIdSet(const Graph
&G
) : G(G
) {}
288 NodeItr
begin() const { return NodeItr(0, G
); }
289 NodeItr
end() const { return NodeItr(G
.Nodes
.size(), G
); }
291 bool empty() const { return G
.Nodes
.empty(); }
293 typename
NodeVector::size_type
size() const {
294 return G
.Nodes
.size() - G
.FreeNodeIds
.size();
303 EdgeIdSet(const Graph
&G
) : G(G
) {}
305 EdgeItr
begin() const { return EdgeItr(0, G
); }
306 EdgeItr
end() const { return EdgeItr(G
.Edges
.size(), G
); }
308 bool empty() const { return G
.Edges
.empty(); }
310 typename
NodeVector::size_type
size() const {
311 return G
.Edges
.size() - G
.FreeEdgeIds
.size();
320 AdjEdgeIdSet(const NodeEntry
&NE
) : NE(NE
) {}
322 typename
NodeEntry::AdjEdgeItr
begin() const {
323 return NE
.getAdjEdgeIds().begin();
326 typename
NodeEntry::AdjEdgeItr
end() const {
327 return NE
.getAdjEdgeIds().end();
330 bool empty() const { return NE
.getAdjEdgeIds().empty(); }
332 typename
NodeEntry::AdjEdgeList::size_type
size() const {
333 return NE
.getAdjEdgeIds().size();
340 /// Construct an empty PBQP graph.
343 /// Construct an empty PBQP graph with the given graph metadata.
344 Graph(GraphMetadata Metadata
) : Metadata(std::move(Metadata
)) {}
346 /// Get a reference to the graph metadata.
347 GraphMetadata
& getMetadata() { return Metadata
; }
349 /// Get a const-reference to the graph metadata.
350 const GraphMetadata
& getMetadata() const { return Metadata
; }
352 /// Lock this graph to the given solver instance in preparation
353 /// for running the solver. This method will call solver.handleAddNode for
354 /// each node in the graph, and handleAddEdge for each edge, to give the
355 /// solver an opportunity to set up any requried metadata.
356 void setSolver(SolverT
&S
) {
357 assert(!Solver
&& "Solver already set. Call unsetSolver().");
359 for (auto NId
: nodeIds())
360 Solver
->handleAddNode(NId
);
361 for (auto EId
: edgeIds())
362 Solver
->handleAddEdge(EId
);
365 /// Release from solver instance.
367 assert(Solver
&& "Solver not set.");
371 /// Add a node with the given costs.
372 /// @param Costs Cost vector for the new node.
373 /// @return Node iterator for the added node.
374 template <typename OtherVectorT
>
375 NodeId
addNode(OtherVectorT Costs
) {
376 // Get cost vector from the problem domain
377 VectorPtr AllocatedCosts
= CostAlloc
.getVector(std::move(Costs
));
378 NodeId NId
= addConstructedNode(NodeEntry(AllocatedCosts
));
380 Solver
->handleAddNode(NId
);
384 /// Add a node bypassing the cost allocator.
385 /// @param Costs Cost vector ptr for the new node (must be convertible to
387 /// @return Node iterator for the added node.
389 /// This method allows for fast addition of a node whose costs don't need
390 /// to be passed through the cost allocator. The most common use case for
391 /// this is when duplicating costs from an existing node (when using a
392 /// pooling allocator). These have already been uniqued, so we can avoid
393 /// re-constructing and re-uniquing them by attaching them directly to the
395 template <typename OtherVectorPtrT
>
396 NodeId
addNodeBypassingCostAllocator(OtherVectorPtrT Costs
) {
397 NodeId NId
= addConstructedNode(NodeEntry(Costs
));
399 Solver
->handleAddNode(NId
);
403 /// Add an edge between the given nodes with the given costs.
404 /// @param N1Id First node.
405 /// @param N2Id Second node.
406 /// @param Costs Cost matrix for new edge.
407 /// @return Edge iterator for the added edge.
408 template <typename OtherVectorT
>
409 EdgeId
addEdge(NodeId N1Id
, NodeId N2Id
, OtherVectorT Costs
) {
410 assert(getNodeCosts(N1Id
).getLength() == Costs
.getRows() &&
411 getNodeCosts(N2Id
).getLength() == Costs
.getCols() &&
412 "Matrix dimensions mismatch.");
413 // Get cost matrix from the problem domain.
414 MatrixPtr AllocatedCosts
= CostAlloc
.getMatrix(std::move(Costs
));
415 EdgeId EId
= addConstructedEdge(EdgeEntry(N1Id
, N2Id
, AllocatedCosts
));
417 Solver
->handleAddEdge(EId
);
421 /// Add an edge bypassing the cost allocator.
422 /// @param N1Id First node.
423 /// @param N2Id Second node.
424 /// @param Costs Cost matrix for new edge.
425 /// @return Edge iterator for the added edge.
427 /// This method allows for fast addition of an edge whose costs don't need
428 /// to be passed through the cost allocator. The most common use case for
429 /// this is when duplicating costs from an existing edge (when using a
430 /// pooling allocator). These have already been uniqued, so we can avoid
431 /// re-constructing and re-uniquing them by attaching them directly to the
433 template <typename OtherMatrixPtrT
>
434 NodeId
addEdgeBypassingCostAllocator(NodeId N1Id
, NodeId N2Id
,
435 OtherMatrixPtrT Costs
) {
436 assert(getNodeCosts(N1Id
).getLength() == Costs
->getRows() &&
437 getNodeCosts(N2Id
).getLength() == Costs
->getCols() &&
438 "Matrix dimensions mismatch.");
439 // Get cost matrix from the problem domain.
440 EdgeId EId
= addConstructedEdge(EdgeEntry(N1Id
, N2Id
, Costs
));
442 Solver
->handleAddEdge(EId
);
446 /// Returns true if the graph is empty.
447 bool empty() const { return NodeIdSet(*this).empty(); }
449 NodeIdSet
nodeIds() const { return NodeIdSet(*this); }
450 EdgeIdSet
edgeIds() const { return EdgeIdSet(*this); }
452 AdjEdgeIdSet
adjEdgeIds(NodeId NId
) { return AdjEdgeIdSet(getNode(NId
)); }
454 /// Get the number of nodes in the graph.
455 /// @return Number of nodes in the graph.
456 unsigned getNumNodes() const { return NodeIdSet(*this).size(); }
458 /// Get the number of edges in the graph.
459 /// @return Number of edges in the graph.
460 unsigned getNumEdges() const { return EdgeIdSet(*this).size(); }
462 /// Set a node's cost vector.
463 /// @param NId Node to update.
464 /// @param Costs New costs to set.
465 template <typename OtherVectorT
>
466 void setNodeCosts(NodeId NId
, OtherVectorT Costs
) {
467 VectorPtr AllocatedCosts
= CostAlloc
.getVector(std::move(Costs
));
469 Solver
->handleSetNodeCosts(NId
, *AllocatedCosts
);
470 getNode(NId
).Costs
= AllocatedCosts
;
473 /// Get a VectorPtr to a node's cost vector. Rarely useful - use
474 /// getNodeCosts where possible.
475 /// @param NId Node id.
476 /// @return VectorPtr to node cost vector.
478 /// This method is primarily useful for duplicating costs quickly by
479 /// bypassing the cost allocator. See addNodeBypassingCostAllocator. Prefer
480 /// getNodeCosts when dealing with node cost values.
481 const VectorPtr
& getNodeCostsPtr(NodeId NId
) const {
482 return getNode(NId
).Costs
;
485 /// Get a node's cost vector.
486 /// @param NId Node id.
487 /// @return Node cost vector.
488 const Vector
& getNodeCosts(NodeId NId
) const {
489 return *getNodeCostsPtr(NId
);
492 NodeMetadata
& getNodeMetadata(NodeId NId
) {
493 return getNode(NId
).Metadata
;
496 const NodeMetadata
& getNodeMetadata(NodeId NId
) const {
497 return getNode(NId
).Metadata
;
500 typename
NodeEntry::AdjEdgeList::size_type
getNodeDegree(NodeId NId
) const {
501 return getNode(NId
).getAdjEdgeIds().size();
504 /// Update an edge's cost matrix.
505 /// @param EId Edge id.
506 /// @param Costs New cost matrix.
507 template <typename OtherMatrixT
>
508 void updateEdgeCosts(EdgeId EId
, OtherMatrixT Costs
) {
509 MatrixPtr AllocatedCosts
= CostAlloc
.getMatrix(std::move(Costs
));
511 Solver
->handleUpdateCosts(EId
, *AllocatedCosts
);
512 getEdge(EId
).Costs
= AllocatedCosts
;
515 /// Get a MatrixPtr to a node's cost matrix. Rarely useful - use
516 /// getEdgeCosts where possible.
517 /// @param EId Edge id.
518 /// @return MatrixPtr to edge cost matrix.
520 /// This method is primarily useful for duplicating costs quickly by
521 /// bypassing the cost allocator. See addNodeBypassingCostAllocator. Prefer
522 /// getEdgeCosts when dealing with edge cost values.
523 const MatrixPtr
& getEdgeCostsPtr(EdgeId EId
) const {
524 return getEdge(EId
).Costs
;
527 /// Get an edge's cost matrix.
528 /// @param EId Edge id.
529 /// @return Edge cost matrix.
530 const Matrix
& getEdgeCosts(EdgeId EId
) const {
531 return *getEdge(EId
).Costs
;
534 EdgeMetadata
& getEdgeMetadata(EdgeId EId
) {
535 return getEdge(EId
).Metadata
;
538 const EdgeMetadata
& getEdgeMetadata(EdgeId EId
) const {
539 return getEdge(EId
).Metadata
;
542 /// Get the first node connected to this edge.
543 /// @param EId Edge id.
544 /// @return The first node connected to the given edge.
545 NodeId
getEdgeNode1Id(EdgeId EId
) const {
546 return getEdge(EId
).getN1Id();
549 /// Get the second node connected to this edge.
550 /// @param EId Edge id.
551 /// @return The second node connected to the given edge.
552 NodeId
getEdgeNode2Id(EdgeId EId
) const {
553 return getEdge(EId
).getN2Id();
556 /// Get the "other" node connected to this edge.
557 /// @param EId Edge id.
558 /// @param NId Node id for the "given" node.
559 /// @return The iterator for the "other" node connected to this edge.
560 NodeId
getEdgeOtherNodeId(EdgeId EId
, NodeId NId
) {
561 EdgeEntry
&E
= getEdge(EId
);
562 if (E
.getN1Id() == NId
) {
568 /// Get the edge connecting two nodes.
569 /// @param N1Id First node id.
570 /// @param N2Id Second node id.
571 /// @return An id for edge (N1Id, N2Id) if such an edge exists,
572 /// otherwise returns an invalid edge id.
573 EdgeId
findEdge(NodeId N1Id
, NodeId N2Id
) {
574 for (auto AEId
: adjEdgeIds(N1Id
)) {
575 if ((getEdgeNode1Id(AEId
) == N2Id
) ||
576 (getEdgeNode2Id(AEId
) == N2Id
)) {
580 return invalidEdgeId();
583 /// Remove a node from the graph.
584 /// @param NId Node id.
585 void removeNode(NodeId NId
) {
587 Solver
->handleRemoveNode(NId
);
588 NodeEntry
&N
= getNode(NId
);
589 // TODO: Can this be for-each'd?
590 for (AdjEdgeItr AEItr
= N
.adjEdgesBegin(),
591 AEEnd
= N
.adjEdgesEnd();
597 FreeNodeIds
.push_back(NId
);
600 /// Disconnect an edge from the given node.
602 /// Removes the given edge from the adjacency list of the given node.
603 /// This operation leaves the edge in an 'asymmetric' state: It will no
604 /// longer appear in an iteration over the given node's (NId's) edges, but
605 /// will appear in an iteration over the 'other', unnamed node's edges.
607 /// This does not correspond to any normal graph operation, but exists to
608 /// support efficient PBQP graph-reduction based solvers. It is used to
609 /// 'effectively' remove the unnamed node from the graph while the solver
610 /// is performing the reduction. The solver will later call reconnectNode
611 /// to restore the edge in the named node's adjacency list.
613 /// Since the degree of a node is the number of connected edges,
614 /// disconnecting an edge from a node 'u' will cause the degree of 'u' to
617 /// A disconnected edge WILL still appear in an iteration over the graph
620 /// A disconnected edge should not be removed from the graph, it should be
621 /// reconnected first.
623 /// A disconnected edge can be reconnected by calling the reconnectEdge
625 void disconnectEdge(EdgeId EId
, NodeId NId
) {
627 Solver
->handleDisconnectEdge(EId
, NId
);
629 EdgeEntry
&E
= getEdge(EId
);
630 E
.disconnectFrom(*this, NId
);
633 /// Convenience method to disconnect all neighbours from the given
635 void disconnectAllNeighborsFromNode(NodeId NId
) {
636 for (auto AEId
: adjEdgeIds(NId
))
637 disconnectEdge(AEId
, getEdgeOtherNodeId(AEId
, NId
));
640 /// Re-attach an edge to its nodes.
642 /// Adds an edge that had been previously disconnected back into the
643 /// adjacency set of the nodes that the edge connects.
644 void reconnectEdge(EdgeId EId
, NodeId NId
) {
645 EdgeEntry
&E
= getEdge(EId
);
646 E
.connectTo(*this, EId
, NId
);
648 Solver
->handleReconnectEdge(EId
, NId
);
651 /// Remove an edge from the graph.
652 /// @param EId Edge id.
653 void removeEdge(EdgeId EId
) {
655 Solver
->handleRemoveEdge(EId
);
656 EdgeEntry
&E
= getEdge(EId
);
658 FreeEdgeIds
.push_back(EId
);
659 Edges
[EId
].invalidate();
662 /// Remove all nodes and edges from the graph.
671 } // end namespace PBQP
672 } // end namespace llvm
674 #endif // LLVM_CODEGEN_PBQP_GRAPH_HPP