| 1 | /*
 | 
| 2 |  * Souffle - A Datalog Compiler
 | 
| 3 |  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved
 | 
| 4 |  * Licensed under the Universal Permissive License v 1.0 as shown at:
 | 
| 5 |  * - https://opensource.org/licenses/UPL
 | 
| 6 |  * - <souffle root>/licenses/SOUFFLE-UPL.txt
 | 
| 7 |  */
 | 
| 8 | 
 | 
| 9 | /************************************************************************
 | 
| 10 |  *
 | 
| 11 |  * @file Graph.h
 | 
| 12 |  *
 | 
| 13 |  * A basic digraph with edge labels.
 | 
| 14 |  *
 | 
| 15 |  ***********************************************************************/
 | 
| 16 | 
 | 
| 17 | #pragma once
 | 
| 18 | 
 | 
| 19 | #include "souffle/utility/ContainerUtil.h"
 | 
| 20 | #include "souffle/utility/Types.h"
 | 
| 21 | #include <deque>
 | 
| 22 | #include <functional>
 | 
| 23 | #include <map>
 | 
| 24 | #include <ostream>
 | 
| 25 | #include <set>
 | 
| 26 | #include <vector>
 | 
| 27 | #ifndef NDEBUG
 | 
| 28 | #include <algorithm>
 | 
| 29 | #endif
 | 
| 30 | 
 | 
| 31 | namespace souffle {
 | 
| 32 | 
 | 
| 33 | namespace detail {
 | 
| 34 | 
 | 
| 35 | struct GraphUnitEdgeSet {};
 | 
| 36 | 
 | 
| 37 | }  // namespace detail
 | 
| 38 | 
 | 
| 39 | /**
 | 
| 40 |  * A simple graph structure for graph-based operations.
 | 
| 41 |  */
 | 
| 42 | template <typename Vertex, typename EdgeLabel = Unit, typename CompareVertex = std::less<Vertex>,
 | 
| 43 |         typename CompareLabel = std::less<EdgeLabel>, bool UnitEdgeLabels = std::is_same_v<Unit, EdgeLabel>>
 | 
| 44 | class GraphLabeled {
 | 
| 45 |     // HACK: helper to avoid pulling in `Util.h` for `souffle::contains`
 | 
| 46 |     template <typename C, typename E = typename C::element_type>
 | 
| 47 |     static bool containsValue(const C& xs, const E& x) {
 | 
| 48 |         return xs.find(x) != xs.end();
 | 
| 49 |     }
 | 
| 50 | 
 | 
| 51 | public:
 | 
| 52 |     using self_t = GraphLabeled<Vertex, EdgeLabel, CompareVertex, CompareLabel>;
 | 
| 53 |     using VertexSet = std::set<Vertex, CompareVertex>;
 | 
| 54 |     using EdgeLabelSet = std::set<EdgeLabel, CompareLabel>;
 | 
| 55 |     enum class Visit { pre, post };
 | 
| 56 | 
 | 
| 57 |     /**
 | 
| 58 |      * Adds a new edge from the given vertex to the target vertex.
 | 
| 59 |      */
 | 
| 60 |     template <typename = std::enable_if_t<UnitEdgeLabels>>
 | 
| 61 |     bool insert(const Vertex& from, const Vertex& to) {
 | 
| 62 |         return insert(from, to, EdgeLabelSet{});
 | 
| 63 |     }
 | 
| 64 | 
 | 
| 65 |     bool insert(const Vertex& from, const Vertex& to, EdgeLabel label) {
 | 
| 66 |         return insert(from, to, EdgeLabelSet{std::move(label)});
 | 
| 67 |     }
 | 
| 68 | 
 | 
| 69 |     bool insert(const Vertex& from, const Vertex& to, EdgeLabelSet labels) {
 | 
| 70 |         insert(from);
 | 
| 71 |         insert(to);
 | 
| 72 |         bool inserted = _successors[from].insert(to).second;
 | 
| 73 |         _predecessors[to].insert(from);
 | 
| 74 |         if constexpr (!UnitEdgeLabels) {
 | 
| 75 |             auto& labels_store = _labels[{from, to}];  // force creation of entry
 | 
| 76 |             if (labels_store.empty() && !labels.empty()) {
 | 
| 77 |                 labels_store = std::move(labels);
 | 
| 78 |                 inserted = true;
 | 
| 79 |             } else {
 | 
| 80 |                 for (auto&& l : labels)
 | 
| 81 |                     inserted |= labels_store.insert(l).second;
 | 
| 82 |             }
 | 
| 83 |         }
 | 
| 84 |         return inserted;
 | 
| 85 |     }
 | 
| 86 | 
 | 
| 87 |     /**
 | 
| 88 |      * Adds a vertex.
 | 
| 89 |      */
 | 
| 90 |     bool insert(const Vertex& vertex) {
 | 
| 91 |         return _vertices.insert(vertex).second;
 | 
| 92 |     }
 | 
| 93 | 
 | 
| 94 |     bool insert(const self_t& g) {
 | 
| 95 |         bool changed = false;
 | 
| 96 |         auto combineSet = [&](auto& dst, auto&& src) {
 | 
| 97 |             for (auto&& x : src)
 | 
| 98 |                 changed |= dst.insert(x).second;
 | 
| 99 |         };
 | 
| 100 |         auto combineMap = [&](auto& dst, auto&& src) {
 | 
| 101 |             for (auto&& [v, xs] : src) {
 | 
| 102 |                 auto [it, added] = dst.insert({v, {}});
 | 
| 103 |                 changed |= added;
 | 
| 104 |                 combineSet(it->second, xs);
 | 
| 105 |             }
 | 
| 106 |         };
 | 
| 107 | 
 | 
| 108 |         combineSet(_vertices, g._vertices);
 | 
| 109 |         combineMap(_successors, g._successors);
 | 
| 110 |         combineMap(_predecessors, g._predecessors);
 | 
| 111 |         if constexpr (!UnitEdgeLabels) {
 | 
| 112 |             combineMap(_labels, g._labels);
 | 
| 113 |         }
 | 
| 114 | 
 | 
| 115 |         return changed;
 | 
| 116 |     }
 | 
| 117 | 
 | 
| 118 |     bool remove(const Vertex& vertex) {
 | 
| 119 |         if (_vertices.erase(vertex) == 0) return false;
 | 
| 120 | 
 | 
| 121 |         if constexpr (!UnitEdgeLabels) {
 | 
| 122 |             for (auto&& m : successors(vertex))
 | 
| 123 |                 _labels.erase({vertex, m});
 | 
| 124 |         }
 | 
| 125 | 
 | 
| 126 |         _successors.erase(vertex);
 | 
| 127 |         _predecessors.erase(vertex);
 | 
| 128 |         return true;
 | 
| 129 |     }
 | 
| 130 | 
 | 
| 131 |     size_t remove(const Vertex& src, const Vertex& dst) {
 | 
| 132 |         auto it = _successors.find(src);
 | 
| 133 |         if (it == _successors.end()) return 0;
 | 
| 134 |         if (it->second.erase(dst) == 0) return 0;
 | 
| 135 | 
 | 
| 136 |         if (it->second.empty()) _successors.erase(it);
 | 
| 137 |         removeExistingAdjEdge(_predecessors, dst, src);
 | 
| 138 | 
 | 
| 139 |         if constexpr (UnitEdgeLabels) {
 | 
| 140 |             return 1;
 | 
| 141 |         } else {
 | 
| 142 |             auto it = _labels.find({src, dst});
 | 
| 143 |             assert(it != _labels.end());
 | 
| 144 |             assert(!it->second.empty());
 | 
| 145 |             auto n = it->second.size();
 | 
| 146 |             _labels.erase(it);
 | 
| 147 |             return n;
 | 
| 148 |         }
 | 
| 149 |     }
 | 
| 150 | 
 | 
| 151 |     bool remove(const Vertex& src, const Vertex& dst, const EdgeLabel& label) {
 | 
| 152 |         if constexpr (UnitEdgeLabels) {
 | 
| 153 |             return 0 < remove(src, dst);
 | 
| 154 |         } else {
 | 
| 155 |             auto it = _labels.find({src, dst});
 | 
| 156 |             if (it == _labels.end()) return false;
 | 
| 157 |             if (it->second.erase(label) == 0) return false;
 | 
| 158 | 
 | 
| 159 |             // all edges erased -> remove from succ/pred
 | 
| 160 |             if (it->second.empty()) {
 | 
| 161 |                 _labels.erase(it);
 | 
| 162 |                 removeExistingAdjEdge(_predecessors, dst, src);
 | 
| 163 |                 removeExistingAdjEdge(_successors, src, dst);
 | 
| 164 |             }
 | 
| 165 | 
 | 
| 166 |             return true;
 | 
| 167 |         }
 | 
| 168 |     }
 | 
| 169 | 
 | 
| 170 |     size_t removeEdgesPred(const Vertex& node) {
 | 
| 171 |         return removeEdgesIncidentTo(_predecessors, _successors, node,
 | 
| 172 |                 [](auto node, auto adj) { return std::make_pair(adj, node); });
 | 
| 173 |     }
 | 
| 174 | 
 | 
| 175 |     size_t removeEdgesSucc(const Vertex& node) {
 | 
| 176 |         return removeEdgesIncidentTo(_successors, _predecessors, node,
 | 
| 177 |                 [](auto node, auto adj) { return std::make_pair(node, adj); });
 | 
| 178 |     }
 | 
| 179 | 
 | 
| 180 |     /** Obtains a reference to the set of all vertices */
 | 
| 181 |     const VertexSet& vertices() const {
 | 
| 182 |         return _vertices;
 | 
| 183 |     }
 | 
| 184 | 
 | 
| 185 |     // set of vertices which appear as the source of an edge
 | 
| 186 |     // i.e. { a | (a, b) \in E }
 | 
| 187 |     VertexSet edgeSources() const {
 | 
| 188 |         return keysOfAdjacency(_successors);
 | 
| 189 |     }
 | 
| 190 | 
 | 
| 191 |     // set of vertices which appear as the target of an edge
 | 
| 192 |     // i.e. { b | (a, b) \in E }
 | 
| 193 |     VertexSet edgeTargets() const {
 | 
| 194 |         return keysOfAdjacency(_predecessors);
 | 
| 195 |     }
 | 
| 196 | 
 | 
| 197 |     /** Returns the set of vertices the given vertex has edges to */
 | 
| 198 |     const VertexSet& successors(const Vertex& from) const {
 | 
| 199 |         auto it = _successors.find(from);
 | 
| 200 |         return it == _successors.end() ? null : it->second;
 | 
| 201 |     }
 | 
| 202 | 
 | 
| 203 |     /** Returns the set of vertices the given vertex has edges from */
 | 
| 204 |     const VertexSet& predecessors(const Vertex& to) const {
 | 
| 205 |         auto it = _predecessors.find(to);
 | 
| 206 |         return it == _predecessors.end() ? null : it->second;
 | 
| 207 |     }
 | 
| 208 | 
 | 
| 209 |     /** Obtains the union of sources and nodes transitively reachable via the sources' successors */
 | 
| 210 |     template <typename C>
 | 
| 211 |     VertexSet reachableFromSucc(C&& sources) const {
 | 
| 212 |         return reachableFrom(std::forward<C>(sources), std::mem_fn(&GraphLabeled::successors));
 | 
| 213 |     }
 | 
| 214 | 
 | 
| 215 |     /** Obtains the union of sources and nodes transitively reachable via the sources' predecessors */
 | 
| 216 |     template <typename C>
 | 
| 217 |     VertexSet reachableFromPred(C&& sources) const {
 | 
| 218 |         return reachableFrom(std::forward<C>(sources), std::mem_fn(&GraphLabeled::predecessors));
 | 
| 219 |     }
 | 
| 220 | 
 | 
| 221 |     VertexSet reachableFromSucc(Vertex src) const {
 | 
| 222 |         return reachableFromSucc(VertexSet{std::move(src)});
 | 
| 223 |     }
 | 
| 224 | 
 | 
| 225 |     VertexSet reachableFromPred(Vertex src) const {
 | 
| 226 |         return reachableFromPred(VertexSet{std::move(src)});
 | 
| 227 |     }
 | 
| 228 | 
 | 
| 229 |     template <typename Edges>
 | 
| 230 |     VertexSet reachableFrom(Vertex src, Edges&& edges) const {
 | 
| 231 |         return reachableFrom(VertexSet{std::move(src)}, std::forward<Edges>(edges));
 | 
| 232 |     }
 | 
| 233 | 
 | 
| 234 |     template <typename Edges>
 | 
| 235 |     VertexSet reachableFrom(VertexSet&& sources, Edges&& edges) const {
 | 
| 236 |         std::vector<Vertex> pending{sources.begin(), sources.end()};
 | 
| 237 |         return reachableFrom(std::move(sources), std::move(pending), std::forward<Edges>(edges));
 | 
| 238 |     }
 | 
| 239 | 
 | 
| 240 |     template <typename C, typename Edges>
 | 
| 241 |     VertexSet reachableFrom(C&& srcs, Edges&& edges) const {
 | 
| 242 |         // nitpick: build `pending` from `srcs` to preserve whatever iteration order is imposed by `srcs`
 | 
| 243 |         std::vector<Vertex> pending{srcs.begin(), srcs.end()};
 | 
| 244 |         return reachableFrom({srcs.begin(), srcs.end()}, std::move(pending), std::forward<Edges>(edges));
 | 
| 245 |     }
 | 
| 246 | 
 | 
| 247 |     const EdgeLabelSet& labels(const Vertex& from, const Vertex& to) const {
 | 
| 248 |         if constexpr (UnitEdgeLabels) {
 | 
| 249 |             if (contains(from, to)) return _labels.unit;
 | 
| 250 |         } else {
 | 
| 251 |             auto it = _labels.find({from, to});
 | 
| 252 |             if (it != _labels.end()) return it->second;
 | 
| 253 |         }
 | 
| 254 | 
 | 
| 255 |         return nullLabel;
 | 
| 256 |     }
 | 
| 257 | 
 | 
| 258 |     /** Determines whether the given vertex is present */
 | 
| 259 |     bool contains(const Vertex& vertex) const {
 | 
| 260 |         return _vertices.find(vertex) != _vertices.end();
 | 
| 261 |     }
 | 
| 262 | 
 | 
| 263 |     /** Determines whether the given edge is present */
 | 
| 264 |     bool contains(const Vertex& from, const Vertex& to) const {
 | 
| 265 |         auto pos = _successors.find(from);
 | 
| 266 |         if (pos == _successors.end()) {
 | 
| 267 |             return false;
 | 
| 268 |         }
 | 
| 269 |         auto p2 = pos->second.find(to);
 | 
| 270 |         return p2 != pos->second.end();
 | 
| 271 |     }
 | 
| 272 | 
 | 
| 273 |     bool contains(const Vertex& from, const Vertex& to, const EdgeLabel& label) const {
 | 
| 274 |         if constexpr (UnitEdgeLabels) {
 | 
| 275 |             return contains(from, to);
 | 
| 276 |         } else {
 | 
| 277 |             auto it = _labels.find({from, to});
 | 
| 278 |             return it != _labels.end() && containsValue(it->second, label);
 | 
| 279 |         }
 | 
| 280 |     }
 | 
| 281 | 
 | 
| 282 |     /** Determines whether there is a directed path between the two vertices */
 | 
| 283 |     bool reaches(const Vertex& from, const Vertex& to) const {
 | 
| 284 |         // quick check
 | 
| 285 |         if (!contains(from) || !contains(to)) {
 | 
| 286 |             return false;
 | 
| 287 |         }
 | 
| 288 | 
 | 
| 289 |         // conduct a depth-first search starting at from
 | 
| 290 |         bool found = false;
 | 
| 291 |         bool first = true;
 | 
| 292 |         visit(from, [&](const Vertex& cur) {
 | 
| 293 |             found = !first && (found || cur == to);
 | 
| 294 |             first = false;
 | 
| 295 |         });
 | 
| 296 |         return found;
 | 
| 297 |     }
 | 
| 298 | 
 | 
| 299 |     /** Obtains the set of all vertices in the same clique than the given vertex */
 | 
| 300 |     VertexSet clique(const Vertex& vertex) const {
 | 
| 301 |         VertexSet res;
 | 
| 302 |         res.insert(vertex);
 | 
| 303 |         for (const auto& cur : vertices()) {
 | 
| 304 |             if (reaches(vertex, cur) && reaches(cur, vertex)) {
 | 
| 305 |                 res.insert(cur);
 | 
| 306 |             }
 | 
| 307 |         }
 | 
| 308 |         return res;
 | 
| 309 |     }
 | 
| 310 | 
 | 
| 311 |     template <typename C>
 | 
| 312 |     self_t induced(const C& nodes) const {
 | 
| 313 |         VertexSet xs;
 | 
| 314 |         for (auto&& n : nodes)
 | 
| 315 |             if (containsValue(_vertices, n)) xs.insert(n);
 | 
| 316 | 
 | 
| 317 |         return inducedImpl(std::move(xs));
 | 
| 318 |     }
 | 
| 319 | 
 | 
| 320 |     self_t inducedReachableDijkstra(const Vertex& source, const std::set<Vertex>& sinks) const {
 | 
| 321 |         // `sink_depths` seperate from `depths` to handle case where `source \in sinks`,
 | 
| 322 |         // otherwise we'd see a sink as having depth 0 on the backwards pass.
 | 
| 323 |         std::map<Vertex, size_t, CompareVertex> sink_depths;
 | 
| 324 |         std::map<Vertex, size_t, CompareVertex> depths;
 | 
| 325 |         std::deque<Vertex> pending;
 | 
| 326 | 
 | 
| 327 |         auto update = [&](const Vertex& x, size_t depth) {
 | 
| 328 |             auto it = depths.find(x);
 | 
| 329 |             if (it != depths.end() && it->second <= depth) return false;
 | 
| 330 | 
 | 
| 331 |             depths[x] = depth;
 | 
| 332 |             pending.push_back(x);
 | 
| 333 |             return true;
 | 
| 334 |         };
 | 
| 335 | 
 | 
| 336 |         update(source, 0);
 | 
| 337 | 
 | 
| 338 |         while (!pending.empty() && sink_depths.size() < sinks.size()) {
 | 
| 339 |             auto x = pending.front();
 | 
| 340 |             pending.pop_front();
 | 
| 341 | 
 | 
| 342 |             auto depth = depths.at(x);
 | 
| 343 |             for (auto&& succ : successors(x)) {
 | 
| 344 |                 if (souffle::contains(sinks, succ)) {
 | 
| 345 |                     sink_depths.insert({succ, depth + 1});
 | 
| 346 |                     // Keep walking.
 | 
| 347 |                     // 1) The shortest path 'tween a src-sink pair could pass through a different sink.
 | 
| 348 |                     // 2) We want all shortest paths, not just one shortest path.
 | 
| 349 |                     // e.g. ```
 | 
| 350 |                     //      a --> b --> d -> e
 | 
| 351 |                     //        \-> c -/
 | 
| 352 |                     //      ```
 | 
| 353 |                     //      Source  : `a`
 | 
| 354 |                     //      Sinks   : `{d, e}`
 | 
| 355 |                 }
 | 
| 356 | 
 | 
| 357 |                 update(succ, depth + 1);
 | 
| 358 |             }
 | 
| 359 |         }
 | 
| 360 | 
 | 
| 361 |         self_t induced;
 | 
| 362 |         std::vector<std::pair<Vertex, size_t>> rev_pending(sink_depths.begin(), sink_depths.end());
 | 
| 363 |         while (!rev_pending.empty()) {
 | 
| 364 |             auto [x, depth] = rev_pending.back();
 | 
| 365 |             rev_pending.pop_back();
 | 
| 366 |             assert(1 <= depth && "rev-walk should terminate at source");
 | 
| 367 | 
 | 
| 368 |             for (auto&& pred : predecessors(x)) {
 | 
| 369 |                 auto it = depths.find(pred);
 | 
| 370 |                 if (it == depths.end() || depth <= it->second) continue;  // edge not in shortest-paths
 | 
| 371 |                 assert(it->second == depth - 1 && "shorter path found on reverse walk?");
 | 
| 372 |                 assert(((1 == depth) == (pred == source)) && "depth-1 vertices should only link to `source`");
 | 
| 373 | 
 | 
| 374 |                 // 1) not already in `rev_pending` (implied by `x \in induced`)
 | 
| 375 |                 // 2) not the source
 | 
| 376 |                 if (!induced.contains(pred) && 1 < depth) {
 | 
| 377 |                     rev_pending.push_back({pred, depth - 1});
 | 
| 378 |                 }
 | 
| 379 | 
 | 
| 380 |                 induced.insert(pred, x, labels(pred, x));
 | 
| 381 |             }
 | 
| 382 |         }
 | 
| 383 | 
 | 
| 384 |         return induced;
 | 
| 385 |     }
 | 
| 386 | 
 | 
| 387 |     /** A generic utility for depth-first pre-order visits */
 | 
| 388 |     template <typename Lambda>
 | 
| 389 |     void visit(const Vertex& vertex, const Lambda& lambda) const {
 | 
| 390 |         visitDepthFirst(vertex, [&](Visit e, auto&& v) {
 | 
| 391 |             if (e == Visit::pre) lambda(v);
 | 
| 392 |         });
 | 
| 393 |     }
 | 
| 394 | 
 | 
| 395 |     /** A generic utility for depth-first visits */
 | 
| 396 |     template <typename Lambda>
 | 
| 397 |     void visitDepthFirst(const Vertex& vertex, const Lambda& lambda) const {
 | 
| 398 |         VertexSet visited;
 | 
| 399 |         visitDepthFirst(vertex, lambda, std::mem_fn(&GraphLabeled::successors), visited);
 | 
| 400 |     }
 | 
| 401 | 
 | 
| 402 |     // if there are multiple sources then pretend there is a pseudo-source node with edge to each
 | 
| 403 |     template <typename Lambda>
 | 
| 404 |     void visitDepthFirstSources(const Lambda& lambda) const {
 | 
| 405 |         // pseudo node can be impl by iterating over each source w/ a shared `visited`
 | 
| 406 |         VertexSet visited;
 | 
| 407 |         for (auto&& v : vertices())
 | 
| 408 |             if (predecessors(v).empty()) {
 | 
| 409 |                 visitDepthFirst(v, lambda, std::mem_fn(&GraphLabeled::successors), visited);
 | 
| 410 |             }
 | 
| 411 |     }
 | 
| 412 | 
 | 
| 413 |     /** Enables graphs to be printed (e.g. for debugging) */
 | 
| 414 |     void print(std::ostream& os) const {
 | 
| 415 |         bool first = true;
 | 
| 416 |         os << "{";
 | 
| 417 |         for (auto&& curr : vertices()) {
 | 
| 418 |             auto&& succs = successors(curr);
 | 
| 419 |             for (auto&& succ : succs) {
 | 
| 420 |                 if (!first) os << "\n,";
 | 
| 421 |                 first = false;
 | 
| 422 | 
 | 
| 423 |                 os << curr << "->" << succ;
 | 
| 424 |                 if constexpr (!UnitEdgeLabels) {
 | 
| 425 |                     os << " [" << join(labels(curr, succ)) << "]";
 | 
| 426 |                 }
 | 
| 427 |             }
 | 
| 428 | 
 | 
| 429 |             // handle case where node is disconnected
 | 
| 430 |             if (succs.empty() && predecessors(curr).empty()) {
 | 
| 431 |                 if (!first) os << "\n,";
 | 
| 432 |                 first = false;
 | 
| 433 |                 os << curr;
 | 
| 434 |             }
 | 
| 435 |         }
 | 
| 436 |         os << "}";
 | 
| 437 |     }
 | 
| 438 | 
 | 
| 439 |     friend std::ostream& operator<<(std::ostream& out, const self_t& g) {
 | 
| 440 |         g.print(out);
 | 
| 441 |         return out;
 | 
| 442 |     }
 | 
| 443 | 
 | 
| 444 |     void DBG_sanCheck() const {
 | 
| 445 | #ifndef NDEBUG
 | 
| 446 |         auto checkPeer = [&](auto&& src, auto&& peer) {
 | 
| 447 |             for (auto&& [n, ms] : src) {
 | 
| 448 |                 assert(!ms.empty() && "shouldn't store empty sets");
 | 
| 449 |                 assert(containsValue(_vertices, n));
 | 
| 450 |                 for (auto&& m : ms) {
 | 
| 451 |                     assert(containsValue(_vertices, m));
 | 
| 452 |                     assert(containsValue(peer, m));
 | 
| 453 |                     assert(containsValue(peer.at(m), n));
 | 
| 454 |                 }
 | 
| 455 |             }
 | 
| 456 |         };
 | 
| 457 | 
 | 
| 458 |         checkPeer(_successors, _predecessors);
 | 
| 459 |         checkPeer(_predecessors, _successors);
 | 
| 460 | 
 | 
| 461 |         if constexpr (!UnitEdgeLabels) {
 | 
| 462 |             for (auto&& [kv, labels] : _labels) {
 | 
| 463 |                 auto&& [n, m] = kv;
 | 
| 464 |                 assert(!labels.empty() && "shouldn't store an empty label set");
 | 
| 465 |                 assert(containsValue(_successors.at(n), m));
 | 
| 466 |             }
 | 
| 467 |         }
 | 
| 468 | #endif
 | 
| 469 |     }
 | 
| 470 | 
 | 
| 471 | private:
 | 
| 472 |     using Vertex2Peer = std::map<Vertex, VertexSet, CompareVertex>;
 | 
| 473 |     using EdgeVerts = std::pair<Vertex, Vertex>;
 | 
| 474 |     using Edge2Labels =
 | 
| 475 |             std::conditional_t<UnitEdgeLabels, detail::GraphUnitEdgeSet, std::map<EdgeVerts, EdgeLabelSet>>;
 | 
| 476 | 
 | 
| 477 |     // not a very efficient but simple graph representation
 | 
| 478 |     VertexSet null;             // the empty set
 | 
| 479 |     EdgeLabelSet nullLabel;     // the empty set
 | 
| 480 |     VertexSet _vertices;        // all the vertices in the graph
 | 
| 481 |     Vertex2Peer _successors;    // all edges forward directed
 | 
| 482 |     Vertex2Peer _predecessors;  // all edges backward
 | 
| 483 |     Edge2Labels _labels;
 | 
| 484 | 
 | 
| 485 |     /** The internal implementation of depth-first visits */
 | 
| 486 |     template <typename Fn, typename Edges>
 | 
| 487 |     void visitDepthFirst(const Vertex& v, const Fn& lambda, const Edges& edges, VertexSet& visited) const {
 | 
| 488 |         lambda(Visit::pre, v);
 | 
| 489 | 
 | 
| 490 |         for (auto&& next : edges(*this, v))
 | 
| 491 |             if (visited.insert(next).second) {
 | 
| 492 |                 visitDepthFirst(next, lambda, edges, visited);
 | 
| 493 |             }
 | 
| 494 | 
 | 
| 495 |         lambda(Visit::post, v);
 | 
| 496 |     }
 | 
| 497 | 
 | 
| 498 |     template <typename Edges>
 | 
| 499 |     VertexSet reachableFrom(VertexSet visited, std::vector<Vertex> pending, Edges&& edges) const {
 | 
| 500 |         assert(visited.size() == pending.size());
 | 
| 501 |         assert(all_of(pending, [&](auto&& x) { return souffle::contains(visited, x); }));
 | 
| 502 | 
 | 
| 503 |         while (!pending.empty()) {
 | 
| 504 |             auto curr = pending.back();
 | 
| 505 |             pending.pop_back();
 | 
| 506 |             for (auto&& next : edges(*this, curr))
 | 
| 507 |                 if (visited.insert(next).second) {
 | 
| 508 |                     pending.push_back(next);
 | 
| 509 |                 }
 | 
| 510 |         }
 | 
| 511 | 
 | 
| 512 |         return visited;
 | 
| 513 |     }
 | 
| 514 | 
 | 
| 515 |     template <typename EdgePair>
 | 
| 516 |     size_t removeEdgesIncidentTo(Vertex2Peer& fwd, Vertex2Peer& rev, const Vertex& node, EdgePair&& edge) {
 | 
| 517 |         auto it = fwd.find(node);
 | 
| 518 |         if (it == fwd.end()) return 0;
 | 
| 519 | 
 | 
| 520 |         size_t n_edges = 0;
 | 
| 521 |         if constexpr (UnitEdgeLabels) {
 | 
| 522 |             n_edges = it->second.size();
 | 
| 523 |         }
 | 
| 524 | 
 | 
| 525 |         for (auto&& adj : it->second) {
 | 
| 526 |             removeExistingAdjEdge(rev, adj, node);
 | 
| 527 | 
 | 
| 528 |             if constexpr (!UnitEdgeLabels) {
 | 
| 529 |                 auto it_label = _labels.find(edge(node, adj));
 | 
| 530 |                 n_edges += it_label->second.size();
 | 
| 531 |                 _labels.erase(it_label);
 | 
| 532 |             }
 | 
| 533 |         }
 | 
| 534 | 
 | 
| 535 |         fwd.erase(it);
 | 
| 536 |         return n_edges;
 | 
| 537 |     }
 | 
| 538 | 
 | 
| 539 |     self_t inducedImpl(VertexSet nodes) const {
 | 
| 540 |         DBG_sanCheck();
 | 
| 541 |         assert(std::includes(_vertices.begin(), _vertices.end(), nodes.begin(), nodes.end()) &&
 | 
| 542 |                 "`nodes` must be a subset of `_vertices`");
 | 
| 543 | 
 | 
| 544 |         self_t induced;
 | 
| 545 |         induced._vertices = std::move(nodes);
 | 
| 546 | 
 | 
| 547 |         for (auto&& n : induced._vertices) {
 | 
| 548 |             auto addEdges = [&](auto&& dst_map, auto&& src) {
 | 
| 549 |                 if (src.empty()) return;
 | 
| 550 | 
 | 
| 551 |                 auto&& [it, _] = dst_map.insert({n, {}});
 | 
| 552 |                 auto& dst = it->second;
 | 
| 553 |                 bool added_anything = false;
 | 
| 554 |                 for (auto&& m : src)
 | 
| 555 |                     if (containsValue(induced._vertices, m)) {
 | 
| 556 |                         added_anything = true;
 | 
| 557 |                         dst.insert(m);
 | 
| 558 |                     }
 | 
| 559 | 
 | 
| 560 |                 if (!added_anything) dst_map.erase(it);
 | 
| 561 |             };
 | 
| 562 | 
 | 
| 563 |             addEdges(induced._successors, successors(n));
 | 
| 564 |             addEdges(induced._predecessors, predecessors(n));
 | 
| 565 | 
 | 
| 566 |             if constexpr (!UnitEdgeLabels) {
 | 
| 567 |                 for (auto&& m : induced.successors(n))
 | 
| 568 |                     induced._labels[{n, m}] = labels(n, m);
 | 
| 569 |             }
 | 
| 570 |         }
 | 
| 571 | 
 | 
| 572 |         induced.DBG_sanCheck();
 | 
| 573 |         return induced;
 | 
| 574 |     }
 | 
| 575 | 
 | 
| 576 |     static void removeExistingAdjEdge(Vertex2Peer& adj, const Vertex& a, const Vertex& b) {
 | 
| 577 |         auto it = adj.find(a);
 | 
| 578 |         assert(it != adj.end());
 | 
| 579 |         [[maybe_unused]] auto n = it->second.erase(b);
 | 
| 580 |         assert(n == 1);
 | 
| 581 |         if (it->second.empty()) adj.erase(it);
 | 
| 582 |     }
 | 
| 583 | 
 | 
| 584 |     static VertexSet keysOfAdjacency(const Vertex2Peer& adj) {
 | 
| 585 |         VertexSet xs;
 | 
| 586 |         for (auto&& [k, _] : adj)
 | 
| 587 |             xs.insert(k);
 | 
| 588 |         return xs;
 | 
| 589 |     }
 | 
| 590 | };
 | 
| 591 | 
 | 
| 592 | template <typename Vertex, typename Compare = std::less<Vertex>>
 | 
| 593 | using Graph = GraphLabeled<Vertex, Unit, Compare>;
 | 
| 594 | 
 | 
| 595 | }  // end of namespace souffle
 |