Simplify PBQP graph removeAdjEdgeId implementation.
[oota-llvm.git] / include / llvm / CodeGen / PBQP / Graph.h
1 //===-------------------- Graph.h - PBQP Graph ------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // PBQP Graph class.
11 //
12 //===----------------------------------------------------------------------===//
13
14
15 #ifndef LLVM_CODEGEN_PBQP_GRAPH_H
16 #define LLVM_CODEGEN_PBQP_GRAPH_H
17
18 #include "llvm/ADT/ilist.h"
19 #include "llvm/ADT/ilist_node.h"
20 #include "llvm/Support/Compiler.h"
21 #include <list>
22 #include <map>
23 #include <set>
24
25 namespace PBQP {
26
27   class GraphBase {
28   public:
29     typedef unsigned NodeId;
30     typedef unsigned EdgeId;
31   };
32
33   /// PBQP Graph class.
34   /// Instances of this class describe PBQP problems.
35   ///
36   template <typename SolverT>
37   class Graph : public GraphBase {
38   private:
39     typedef typename SolverT::CostAllocator CostAllocator;
40   public:
41     typedef typename SolverT::RawVector RawVector;
42     typedef typename SolverT::RawMatrix RawMatrix;
43     typedef typename SolverT::Vector Vector;
44     typedef typename SolverT::Matrix Matrix;
45     typedef typename CostAllocator::VectorPtr VectorPtr;
46     typedef typename CostAllocator::MatrixPtr MatrixPtr;
47     typedef typename SolverT::NodeMetadata NodeMetadata;
48     typedef typename SolverT::EdgeMetadata EdgeMetadata;
49
50   private:
51
52     class NodeEntry {
53     public:
54       typedef std::vector<EdgeId> AdjEdgeList;
55       typedef AdjEdgeList::size_type AdjEdgeIdx;
56       typedef AdjEdgeList::const_iterator AdjEdgeItr;
57
58       static AdjEdgeIdx getInvalidAdjEdgeIdx() {
59         return std::numeric_limits<AdjEdgeIdx>::max();
60       }
61
62       NodeEntry(VectorPtr Costs) : Costs(Costs) {}
63
64       AdjEdgeIdx addAdjEdgeId(EdgeId EId) {
65         AdjEdgeIdx Idx = AdjEdgeIds.size();
66         AdjEdgeIds.push_back(EId);
67         return Idx;
68       }
69
70       void removeAdjEdgeId(Graph &G, NodeId ThisNId, AdjEdgeIdx Idx) {
71         // Swap-and-pop for fast removal.
72         //   1) Update the adj index of the edge currently at back().
73         //   2) Swap Edge at Idx with back().
74         //   3) pop_back()
75         // If Idx == size() - 1 then the updateAdjEdgeIdx and swap are
76         // redundant, but both operations are cheap.
77         G.getEdge(AdjEdgeIds.back()).updateAdjEdgeIdx(ThisNId, Idx);
78         std::swap(AdjEdgeIds[Idx], AdjEdgeIds.back());
79         AdjEdgeIds.pop_back();
80       }
81
82       const AdjEdgeList& getAdjEdgeIds() const { return AdjEdgeIds; }
83
84       VectorPtr Costs;
85       NodeMetadata Metadata;
86     private:
87       AdjEdgeList AdjEdgeIds;
88     };
89
90     class EdgeEntry {
91     public:
92       EdgeEntry(NodeId N1Id, NodeId N2Id, MatrixPtr Costs)
93         : Costs(Costs) {
94         NIds[0] = N1Id;
95         NIds[1] = N2Id;
96         ThisEdgeAdjIdxs[0] = NodeEntry::getInvalidAdjEdgeIdx();
97         ThisEdgeAdjIdxs[1] = NodeEntry::getInvalidAdjEdgeIdx();
98       }
99
100       void invalidate() {
101         NIds[0] = NIds[1] = Graph::invalidNodeId();
102         ThisEdgeAdjIdxs[0] = ThisEdgeAdjIdxs[1] =
103           NodeEntry::getInvalidAdjEdgeIdx();
104         Costs = nullptr;
105       }
106
107       void connectToN(Graph &G, EdgeId ThisEdgeId, unsigned NIdx) {
108         assert(ThisEdgeAdjIdxs[NIdx] == NodeEntry::getInvalidAdjEdgeIdx() &&
109                "Edge already connected to NIds[NIdx].");
110         NodeEntry &N = G.getNode(NIds[NIdx]);
111         ThisEdgeAdjIdxs[NIdx] = N.addAdjEdgeId(ThisEdgeId);
112       }
113
114       void connectTo(Graph &G, EdgeId ThisEdgeId, NodeId NId) {
115         if (NId == NIds[0])
116           connectToN(G, ThisEdgeId, 0);
117         else {
118           assert(NId == NIds[1] && "Edge does not connect NId.");
119           connectToN(G, ThisEdgeId, 1);
120         }
121       }
122
123       void connect(Graph &G, EdgeId ThisEdgeId) {
124         connectToN(G, ThisEdgeId, 0);
125         connectToN(G, ThisEdgeId, 1);
126       }
127
128       void updateAdjEdgeIdx(NodeId NId, typename NodeEntry::AdjEdgeIdx NewIdx) {
129         if (NId == NIds[0])
130           ThisEdgeAdjIdxs[0] = NewIdx;
131         else {
132           assert(NId == NIds[1] && "Edge not connected to NId");
133           ThisEdgeAdjIdxs[1] = NewIdx;
134         }
135       }
136
137       void disconnectFromN(Graph &G, unsigned NIdx) {
138         assert(ThisEdgeAdjIdxs[NIdx] != NodeEntry::getInvalidAdjEdgeIdx() &&
139                "Edge not connected to NIds[NIdx].");
140         NodeEntry &N = G.getNode(NIds[NIdx]);
141         N.removeAdjEdgeId(G, NIds[NIdx], ThisEdgeAdjIdxs[NIdx]);
142         ThisEdgeAdjIdxs[NIdx] = NodeEntry::getInvalidAdjEdgeIdx();
143       }
144
145       void disconnectFrom(Graph &G, NodeId NId) {
146         if (NId == NIds[0])
147           disconnectFromN(G, 0);
148         else {
149           assert(NId == NIds[1] && "Edge does not connect NId");
150           disconnectFromN(G, 1);
151         }
152       }
153
154       NodeId getN1Id() const { return NIds[0]; }
155       NodeId getN2Id() const { return NIds[1]; }
156       MatrixPtr Costs;
157       EdgeMetadata Metadata;
158     private:
159       NodeId NIds[2];
160       typename NodeEntry::AdjEdgeIdx ThisEdgeAdjIdxs[2];
161     };
162
163     // ----- MEMBERS -----
164
165     CostAllocator CostAlloc;
166     SolverT *Solver;
167
168     typedef std::vector<NodeEntry> NodeVector;
169     typedef std::vector<NodeId> FreeNodeVector;
170     NodeVector Nodes;
171     FreeNodeVector FreeNodeIds;
172
173     typedef std::vector<EdgeEntry> EdgeVector;
174     typedef std::vector<EdgeId> FreeEdgeVector;
175     EdgeVector Edges;
176     FreeEdgeVector FreeEdgeIds;
177
178     // ----- INTERNAL METHODS -----
179
180     NodeEntry& getNode(NodeId NId) { return Nodes[NId]; }
181     const NodeEntry& getNode(NodeId NId) const { return Nodes[NId]; }
182
183     EdgeEntry& getEdge(EdgeId EId) { return Edges[EId]; }
184     const EdgeEntry& getEdge(EdgeId EId) const { return Edges[EId]; }
185
186     NodeId addConstructedNode(const NodeEntry &N) {
187       NodeId NId = 0;
188       if (!FreeNodeIds.empty()) {
189         NId = FreeNodeIds.back();
190         FreeNodeIds.pop_back();
191         Nodes[NId] = std::move(N);
192       } else {
193         NId = Nodes.size();
194         Nodes.push_back(std::move(N));
195       }
196       return NId;
197     }
198
199     EdgeId addConstructedEdge(const EdgeEntry &E) {
200       assert(findEdge(E.getN1Id(), E.getN2Id()) == invalidEdgeId() &&
201              "Attempt to add duplicate edge.");
202       EdgeId EId = 0;
203       if (!FreeEdgeIds.empty()) {
204         EId = FreeEdgeIds.back();
205         FreeEdgeIds.pop_back();
206         Edges[EId] = std::move(E);
207       } else {
208         EId = Edges.size();
209         Edges.push_back(std::move(E));
210       }
211
212       EdgeEntry &NE = getEdge(EId);
213
214       // Add the edge to the adjacency sets of its nodes.
215       NE.connect(*this, EId);
216       return EId;
217     }
218
219     Graph(const Graph &Other) {}
220     void operator=(const Graph &Other) {}
221
222   public:
223
224     typedef typename NodeEntry::AdjEdgeItr AdjEdgeItr;
225
226     class NodeItr {
227     public:
228       NodeItr(NodeId CurNId, const Graph &G)
229         : CurNId(CurNId), EndNId(G.Nodes.size()), FreeNodeIds(G.FreeNodeIds) {
230         this->CurNId = findNextInUse(CurNId); // Move to first in-use node id
231       }
232
233       bool operator==(const NodeItr &O) const { return CurNId == O.CurNId; }
234       bool operator!=(const NodeItr &O) const { return !(*this == O); }
235       NodeItr& operator++() { CurNId = findNextInUse(++CurNId); return *this; }
236       NodeId operator*() const { return CurNId; }
237
238     private:
239       NodeId findNextInUse(NodeId NId) const {
240         while (NId < EndNId &&
241                std::find(FreeNodeIds.begin(), FreeNodeIds.end(), NId) !=
242                  FreeNodeIds.end()) {
243           ++NId;
244         }
245         return NId;
246       }
247
248       NodeId CurNId, EndNId;
249       const FreeNodeVector &FreeNodeIds;
250     };
251
252     class EdgeItr {
253     public:
254       EdgeItr(EdgeId CurEId, const Graph &G)
255         : CurEId(CurEId), EndEId(G.Edges.size()), FreeEdgeIds(G.FreeEdgeIds) {
256         this->CurEId = findNextInUse(CurEId); // Move to first in-use edge id
257       }
258
259       bool operator==(const EdgeItr &O) const { return CurEId == O.CurEId; }
260       bool operator!=(const EdgeItr &O) const { return !(*this == O); }
261       EdgeItr& operator++() { CurEId = findNextInUse(++CurEId); return *this; }
262       EdgeId operator*() const { return CurEId; }
263
264     private:
265       EdgeId findNextInUse(EdgeId EId) const {
266         while (EId < EndEId &&
267                std::find(FreeEdgeIds.begin(), FreeEdgeIds.end(), EId) !=
268                FreeEdgeIds.end()) {
269           ++EId;
270         }
271         return EId;
272       }
273
274       EdgeId CurEId, EndEId;
275       const FreeEdgeVector &FreeEdgeIds;
276     };
277
278     class NodeIdSet {
279     public:
280       NodeIdSet(const Graph &G) : G(G) { }
281       NodeItr begin() const { return NodeItr(0, G); }
282       NodeItr end() const { return NodeItr(G.Nodes.size(), G); }
283       bool empty() const { return G.Nodes.empty(); }
284       typename NodeVector::size_type size() const {
285         return G.Nodes.size() - G.FreeNodeIds.size();
286       }
287     private:
288       const Graph& G;
289     };
290
291     class EdgeIdSet {
292     public:
293       EdgeIdSet(const Graph &G) : G(G) { }
294       EdgeItr begin() const { return EdgeItr(0, G); }
295       EdgeItr end() const { return EdgeItr(G.Edges.size(), G); }
296       bool empty() const { return G.Edges.empty(); }
297       typename NodeVector::size_type size() const {
298         return G.Edges.size() - G.FreeEdgeIds.size();
299       }
300     private:
301       const Graph& G;
302     };
303
304     class AdjEdgeIdSet {
305     public:
306       AdjEdgeIdSet(const NodeEntry &NE) : NE(NE) { }
307       typename NodeEntry::AdjEdgeItr begin() const {
308         return NE.getAdjEdgeIds().begin();
309       }
310       typename NodeEntry::AdjEdgeItr end() const {
311         return NE.getAdjEdgeIds().end();
312       }
313       bool empty() const { return NE.getAdjEdgeIds().empty(); }
314       typename NodeEntry::AdjEdgeList::size_type size() const {
315         return NE.getAdjEdgeIds().size();
316       }
317     private:
318       const NodeEntry &NE;
319     };
320
321     /// \brief Construct an empty PBQP graph.
322     Graph() : Solver(nullptr) { }
323
324     /// \brief Lock this graph to the given solver instance in preparation
325     /// for running the solver. This method will call solver.handleAddNode for
326     /// each node in the graph, and handleAddEdge for each edge, to give the
327     /// solver an opportunity to set up any requried metadata.
328     void setSolver(SolverT &S) {
329       assert(Solver == nullptr && "Solver already set. Call unsetSolver().");
330       Solver = &S;
331       for (auto NId : nodeIds())
332         Solver->handleAddNode(NId);
333       for (auto EId : edgeIds())
334         Solver->handleAddEdge(EId);
335     }
336
337     /// \brief Release from solver instance.
338     void unsetSolver() {
339       assert(Solver != nullptr && "Solver not set.");
340       Solver = nullptr;
341     }
342
343     /// \brief Add a node with the given costs.
344     /// @param Costs Cost vector for the new node.
345     /// @return Node iterator for the added node.
346     template <typename OtherVectorT>
347     NodeId addNode(OtherVectorT Costs) {
348       // Get cost vector from the problem domain
349       VectorPtr AllocatedCosts = CostAlloc.getVector(std::move(Costs));
350       NodeId NId = addConstructedNode(NodeEntry(AllocatedCosts));
351       if (Solver)
352         Solver->handleAddNode(NId);
353       return NId;
354     }
355
356     /// \brief Add an edge between the given nodes with the given costs.
357     /// @param N1Id First node.
358     /// @param N2Id Second node.
359     /// @return Edge iterator for the added edge.
360     template <typename OtherVectorT>
361     EdgeId addEdge(NodeId N1Id, NodeId N2Id, OtherVectorT Costs) {
362       assert(getNodeCosts(N1Id).getLength() == Costs.getRows() &&
363              getNodeCosts(N2Id).getLength() == Costs.getCols() &&
364              "Matrix dimensions mismatch.");
365       // Get cost matrix from the problem domain.
366       MatrixPtr AllocatedCosts = CostAlloc.getMatrix(std::move(Costs));
367       EdgeId EId = addConstructedEdge(EdgeEntry(N1Id, N2Id, AllocatedCosts));
368       if (Solver)
369         Solver->handleAddEdge(EId);
370       return EId;
371     }
372
373     /// \brief Returns true if the graph is empty.
374     bool empty() const { return NodeIdSet(*this).empty(); }
375
376     NodeIdSet nodeIds() const { return NodeIdSet(*this); }
377     EdgeIdSet edgeIds() const { return EdgeIdSet(*this); }
378
379     AdjEdgeIdSet adjEdgeIds(NodeId NId) { return AdjEdgeIdSet(getNode(NId)); }
380
381     /// \brief Get the number of nodes in the graph.
382     /// @return Number of nodes in the graph.
383     unsigned getNumNodes() const { return NodeIdSet(*this).size(); }
384
385     /// \brief Get the number of edges in the graph.
386     /// @return Number of edges in the graph.
387     unsigned getNumEdges() const { return EdgeIdSet(*this).size(); }
388
389     /// \brief Set a node's cost vector.
390     /// @param NId Node to update.
391     /// @param Costs New costs to set.
392     template <typename OtherVectorT>
393     void setNodeCosts(NodeId NId, OtherVectorT Costs) {
394       VectorPtr AllocatedCosts = CostAlloc.getVector(std::move(Costs));
395       if (Solver)
396         Solver->handleSetNodeCosts(NId, *AllocatedCosts);
397       getNode(NId).Costs = AllocatedCosts;
398     }
399
400     /// \brief Get a node's cost vector (const version).
401     /// @param NId Node id.
402     /// @return Node cost vector.
403     const Vector& getNodeCosts(NodeId NId) const {
404       return *getNode(NId).Costs;
405     }
406
407     NodeMetadata& getNodeMetadata(NodeId NId) {
408       return getNode(NId).Metadata;
409     }
410
411     const NodeMetadata& getNodeMetadata(NodeId NId) const {
412       return getNode(NId).Metadata;
413     }
414
415     typename NodeEntry::AdjEdgeList::size_type getNodeDegree(NodeId NId) const {
416       return getNode(NId).getAdjEdgeIds().size();
417     }
418
419     /// \brief Set an edge's cost matrix.
420     /// @param EId Edge id.
421     /// @param Costs New cost matrix.
422     template <typename OtherMatrixT>
423     void setEdgeCosts(EdgeId EId, OtherMatrixT Costs) {
424       MatrixPtr AllocatedCosts = CostAlloc.getMatrix(std::move(Costs));
425       if (Solver)
426         Solver->handleSetEdgeCosts(EId, *AllocatedCosts);
427       getEdge(EId).Costs = AllocatedCosts;
428     }
429
430     /// \brief Get an edge's cost matrix (const version).
431     /// @param EId Edge id.
432     /// @return Edge cost matrix.
433     const Matrix& getEdgeCosts(EdgeId EId) const { return *getEdge(EId).Costs; }
434
435     EdgeMetadata& getEdgeMetadata(EdgeId NId) {
436       return getEdge(NId).Metadata;
437     }
438
439     const EdgeMetadata& getEdgeMetadata(EdgeId NId) const {
440       return getEdge(NId).Metadata;
441     }
442
443     /// \brief Get the first node connected to this edge.
444     /// @param EId Edge id.
445     /// @return The first node connected to the given edge.
446     NodeId getEdgeNode1Id(EdgeId EId) {
447       return getEdge(EId).getN1Id();
448     }
449
450     /// \brief Get the second node connected to this edge.
451     /// @param EId Edge id.
452     /// @return The second node connected to the given edge.
453     NodeId getEdgeNode2Id(EdgeId EId) {
454       return getEdge(EId).getN2Id();
455     }
456
457     /// \brief Get the "other" node connected to this edge.
458     /// @param EId Edge id.
459     /// @param NId Node id for the "given" node.
460     /// @return The iterator for the "other" node connected to this edge.
461     NodeId getEdgeOtherNodeId(EdgeId EId, NodeId NId) {
462       EdgeEntry &E = getEdge(EId);
463       if (E.getN1Id() == NId) {
464         return E.getN2Id();
465       } // else
466       return E.getN1Id();
467     }
468
469     /// \brief Returns a value representing an invalid (non-existant) node.
470     static NodeId invalidNodeId() {
471       return std::numeric_limits<NodeId>::max();
472     }
473
474     /// \brief Returns a value representing an invalid (non-existant) edge.
475     static EdgeId invalidEdgeId() {
476       return std::numeric_limits<EdgeId>::max();
477     }
478
479     /// \brief Get the edge connecting two nodes.
480     /// @param N1Id First node id.
481     /// @param N2Id Second node id.
482     /// @return An id for edge (N1Id, N2Id) if such an edge exists,
483     ///         otherwise returns an invalid edge id.
484     EdgeId findEdge(NodeId N1Id, NodeId N2Id) {
485       for (auto AEId : adjEdgeIds(N1Id)) {
486         if ((getEdgeNode1Id(AEId) == N2Id) ||
487             (getEdgeNode2Id(AEId) == N2Id)) {
488           return AEId;
489         }
490       }
491       return invalidEdgeId();
492     }
493
494     /// \brief Remove a node from the graph.
495     /// @param NId Node id.
496     void removeNode(NodeId NId) {
497       if (Solver)
498         Solver->handleRemoveNode(NId);
499       NodeEntry &N = getNode(NId);
500       // TODO: Can this be for-each'd?
501       for (AdjEdgeItr AEItr = N.adjEdgesBegin(),
502                       AEEnd = N.adjEdgesEnd();
503            AEItr != AEEnd;) {
504         EdgeId EId = *AEItr;
505         ++AEItr;
506         removeEdge(EId);
507       }
508       FreeNodeIds.push_back(NId);
509     }
510
511     /// \brief Disconnect an edge from the given node.
512     ///
513     /// Removes the given edge from the adjacency list of the given node.
514     /// This operation leaves the edge in an 'asymmetric' state: It will no
515     /// longer appear in an iteration over the given node's (NId's) edges, but
516     /// will appear in an iteration over the 'other', unnamed node's edges.
517     ///
518     /// This does not correspond to any normal graph operation, but exists to
519     /// support efficient PBQP graph-reduction based solvers. It is used to
520     /// 'effectively' remove the unnamed node from the graph while the solver
521     /// is performing the reduction. The solver will later call reconnectNode
522     /// to restore the edge in the named node's adjacency list.
523     ///
524     /// Since the degree of a node is the number of connected edges,
525     /// disconnecting an edge from a node 'u' will cause the degree of 'u' to
526     /// drop by 1.
527     ///
528     /// A disconnected edge WILL still appear in an iteration over the graph
529     /// edges.
530     ///
531     /// A disconnected edge should not be removed from the graph, it should be
532     /// reconnected first.
533     ///
534     /// A disconnected edge can be reconnected by calling the reconnectEdge
535     /// method.
536     void disconnectEdge(EdgeId EId, NodeId NId) {
537       if (Solver)
538         Solver->handleDisconnectEdge(EId, NId);
539
540       EdgeEntry &E = getEdge(EId);
541       E.disconnectFrom(*this, NId);
542     }
543
544     /// \brief Convenience method to disconnect all neighbours from the given
545     ///        node.
546     void disconnectAllNeighborsFromNode(NodeId NId) {
547       for (auto AEId : adjEdgeIds(NId))
548         disconnectEdge(AEId, getEdgeOtherNodeId(AEId, NId));
549     }
550
551     /// \brief Re-attach an edge to its nodes.
552     ///
553     /// Adds an edge that had been previously disconnected back into the
554     /// adjacency set of the nodes that the edge connects.
555     void reconnectEdge(EdgeId EId, NodeId NId) {
556       EdgeEntry &E = getEdge(EId);
557       E.connectTo(*this, EId, NId);
558       if (Solver)
559         Solver->handleReconnectEdge(EId, NId);
560     }
561
562     /// \brief Remove an edge from the graph.
563     /// @param EId Edge id.
564     void removeEdge(EdgeId EId) {
565       if (Solver)
566         Solver->handleRemoveEdge(EId);
567       EdgeEntry &E = getEdge(EId);
568       E.disconnect();
569       FreeEdgeIds.push_back(EId);
570       Edges[EId].invalidate();
571     }
572
573     /// \brief Remove all nodes and edges from the graph.
574     void clear() {
575       Nodes.clear();
576       FreeNodeIds.clear();
577       Edges.clear();
578       FreeEdgeIds.clear();
579     }
580
581     /// \brief Dump a graph to an output stream.
582     template <typename OStream>
583     void dump(OStream &OS) {
584       OS << nodeIds().size() << " " << edgeIds().size() << "\n";
585
586       for (auto NId : nodeIds()) {
587         const Vector& V = getNodeCosts(NId);
588         OS << "\n" << V.getLength() << "\n";
589         assert(V.getLength() != 0 && "Empty vector in graph.");
590         OS << V[0];
591         for (unsigned i = 1; i < V.getLength(); ++i) {
592           OS << " " << V[i];
593         }
594         OS << "\n";
595       }
596
597       for (auto EId : edgeIds()) {
598         NodeId N1Id = getEdgeNode1Id(EId);
599         NodeId N2Id = getEdgeNode2Id(EId);
600         assert(N1Id != N2Id && "PBQP graphs shound not have self-edges.");
601         const Matrix& M = getEdgeCosts(EId);
602         OS << "\n" << N1Id << " " << N2Id << "\n"
603            << M.getRows() << " " << M.getCols() << "\n";
604         assert(M.getRows() != 0 && "No rows in matrix.");
605         assert(M.getCols() != 0 && "No cols in matrix.");
606         for (unsigned i = 0; i < M.getRows(); ++i) {
607           OS << M[i][0];
608           for (unsigned j = 1; j < M.getCols(); ++j) {
609             OS << " " << M[i][j];
610           }
611           OS << "\n";
612         }
613       }
614     }
615
616     /// \brief Print a representation of this graph in DOT format.
617     /// @param OS Output stream to print on.
618     template <typename OStream>
619     void printDot(OStream &OS) {
620       OS << "graph {\n";
621       for (auto NId : nodeIds()) {
622         OS << "  node" << NId << " [ label=\""
623            << NId << ": " << getNodeCosts(NId) << "\" ]\n";
624       }
625       OS << "  edge [ len=" << nodeIds().size() << " ]\n";
626       for (auto EId : edgeIds()) {
627         OS << "  node" << getEdgeNode1Id(EId)
628            << " -- node" << getEdgeNode2Id(EId)
629            << " [ label=\"";
630         const Matrix &EdgeCosts = getEdgeCosts(EId);
631         for (unsigned i = 0; i < EdgeCosts.getRows(); ++i) {
632           OS << EdgeCosts.getRowAsVector(i) << "\\n";
633         }
634         OS << "\" ]\n";
635       }
636       OS << "}\n";
637     }
638   };
639
640 }
641
642 #endif // LLVM_CODEGEN_PBQP_GRAPH_HPP