dlinear  0.0.1
Delta-complete SMT solver for linear programming
Loading...
Searching...
No Matches
Graph.hpp
1
9#pragma once
10
11#include <functional>
12#include <iostream>
13#include <numeric>
14#include <queue>
15#include <stack>
16#include <unordered_map>
17#include <unordered_set>
18#include <utility>
19#include <vector>
20
21namespace dlinear {
22
31enum class VisitResult {
32 CONTINUE,
33 SKIP,
34 STOP,
35};
36
37template <class T, class W>
38struct EdgeHash_ {
39 size_t operator()(const std::pair<T, W>& lhs) const { return std::hash<T>{}(lhs.first); }
40};
41
42template <class T, class W>
43struct EdgeEqual_ {
44 bool operator()(const std::pair<T, W>& lhs, const std::pair<T, W>& rhs) const {
45 return std::equal_to<T>{}(lhs.first, rhs.first);
46 }
47};
48
49// using T = int;
50// using W = double;
51// using EdgeHash = EdgeHash_<T, W>;
52// using EdgeEqual = EdgeEqual_<T, W>;
53
65template <class T, std::default_initializable W, class EdgeHash = EdgeHash_<T, W>, class EdgeEqual = EdgeEqual_<T, W>>
66class Graph {
67 public:
68 using Edge = std::pair<T, W>;
69 using AdjSet = std::unordered_set<Edge, EdgeHash, EdgeEqual>;
70 using VisitFunc = std::function<VisitResult(const T&, const T&, const W&)>;
84 using PathsFunc = std::function<VisitResult(
85 std::vector<T>&)>;
95
104 void AddEdge(const T& u, const T& v, bool bidirectional = true) {
105 adj_list_[u].emplace(v, 1);
106 adj_list_[v]; // Ensure the ending vertex exists
107 if (bidirectional) adj_list_[v].emplace(u, 1);
108 }
109
121 bool AddEdge(const T& u, const T& v, const W& weight, bool bidirectional = true) {
122 bool updated = false;
123 const auto [it, inserted] = adj_list_[u].emplace(v, weight);
124 adj_list_[v]; // Ensure the ending vertex exists
125 if (!inserted && it->second != weight) {
126 adj_list_.at(u).erase(it);
127 adj_list_.at(u).emplace(v, weight);
128 updated = true;
129 }
130 if (bidirectional) {
131 const auto [b_it, b_inserted] = adj_list_[v].emplace(u, 1 / weight);
132 if (!b_inserted && b_it->second != 1 / weight) {
133 adj_list_.at(v).erase(b_it);
134 adj_list_.at(v).emplace(u, 1 / weight);
135 updated = true;
136 }
137 }
138 return updated;
139 }
140
147 void AddVertex(const T& v) {
148 if (adj_list_.find(v) == adj_list_.end()) adj_list_.try_emplace(v);
149 }
150
158 bool HasEdge(const T& u, const T& v) const {
159 return adj_list_.find(u) != adj_list_.cend() && adj_list_.at(u).find({v, W{}}) != adj_list_.at(u).cend();
160 }
167 bool HasEdges(const T& u) const { return adj_list_.find(u) != adj_list_.cend() && !adj_list_.at(u).empty(); }
168
176 const W* GetEdgeWeight(const T& u, const T& v) const {
177 if (auto it = adj_list_.find(u); it != adj_list_.cend()) {
178 if (auto it2 = adj_list_.at(u).find({v, W{}}); it2 != adj_list_.at(u).cend()) {
179 return &it2->second;
180 }
181 }
182 return nullptr;
183 }
184
189 const std::unordered_map<T, AdjSet>& adj_list() const { return adj_list_; }
190
197 void RemoveEdge(const T& u, const T& v, bool bidirectional = true) {
198 adj_list_[u].erase({v, W{}});
199 if (bidirectional) adj_list_[v].erase({u, W{}});
200 }
201
206 void RemoveVertex(const T& v) {
207 adj_list_.erase(v);
208 for (auto& [node, edges] : adj_list_) edges.erase({v, W{}});
209 }
210
212 void Clear() { adj_list_.clear(); }
214 void ClearEdges() {
215 for (auto& [node, edges] : adj_list_) edges.clear();
216 }
217
222 [[nodiscard]] size_t Size() const {
223 return std::accumulate(adj_list_.begin(), adj_list_.end(), 0,
224 [](size_t acc, const auto& pair) { return acc + pair.second.size(); });
225 }
226
232 [[nodiscard]] bool IsEmpty() const { return adj_list_.empty(); }
233
247 void DFS(const T& start, const VisitFunc& func) {
248 std::unordered_set<T> visited{};
249 DFS(start, func, visited);
250 }
268 void DFS(const T& start, const VisitFunc& func, std::unordered_set<T>& visited) {
269 // If the starting vertex is not in the graph, return
270 if (adj_list_.find(start) == adj_list_.end()) return;
271
272 visited.reserve(adj_list_.size());
273 std::stack<T> stack;
274 std::unordered_map<T, std::pair<T, const W*>> edges;
275
276 edges.emplace(start, std::make_pair(start, &zero_));
277 stack.push(start);
278 while (!stack.empty()) {
279 const T current = std::move(stack.top());
280 stack.pop();
281 if (visited.find(current) != visited.end()) continue;
282 visited.insert(current);
283 const VisitResult res = func(edges.at(current).first, current, *edges.at(current).second);
284 if (res == VisitResult::STOP) return;
285 if (res == VisitResult::SKIP || adj_list_.find(current) == adj_list_.end()) continue;
286 for (auto adj_it = adj_list_.at(current).begin(); adj_it != adj_list_.at(current).end(); ++adj_it) {
287 const auto& [adj_vertex, weight] = *adj_it;
288 if (visited.find(adj_vertex) != visited.end()) continue;
289 stack.push(adj_vertex);
290 edges.insert_or_assign(adj_vertex, std::make_pair(current, &weight));
291 }
292 }
293 }
294
308 void BFS(const T& start, const VisitFunc& func) {
309 std::unordered_set<T> visited{};
310 BFS(start, func, visited);
311 }
328 void BFS(const T& start, const VisitFunc& func, std::unordered_set<T>& visited) {
329 // If the starting vertex is not in the graph, return
330 if (adj_list_.find(start) == adj_list_.end()) return;
331
332 visited.reserve(adj_list_.size());
333 std::queue<T> queue;
334 std::unordered_map<T, std::pair<T, const W*>> edges;
335
336 visited.insert(start);
337 edges.emplace(start, std::make_pair(start, &zero_));
338 queue.push(start);
339 while (!queue.empty()) {
340 const VisitResult res = func(edges.at(queue.front()).first, queue.front(), *edges.at(queue.front()).second);
341 if (res == VisitResult::STOP) return;
342 if (res == VisitResult::SKIP || adj_list_.find(queue.front()) == adj_list_.end()) {
343 queue.pop();
344 continue;
345 }
346 for (auto adj_it = adj_list_.at(queue.front()).begin(); adj_it != adj_list_.at(queue.front()).end(); ++adj_it) {
347 const auto& [adj_vertex, weight] = *adj_it;
348 if (visited.find(adj_vertex) != visited.end()) continue;
349 visited.insert(adj_vertex);
350 queue.push(adj_vertex);
351 edges.insert_or_assign(adj_vertex, std::make_pair(queue.front(), &weight));
352 }
353 queue.pop();
354 }
355 }
356
373 void AllPaths(const T& start, const T& end, const PathsFunc& func) {
374 std::unordered_set<T> visited;
375 AllPaths(start, end, func, visited);
376 }
396 void AllPaths(const T& start, const T& end, const PathsFunc& func, std::unordered_set<T>& visited) {
397 if (adj_list_.find(start) == adj_list_.end() || adj_list_.find(end) == adj_list_.end() ||
398 adj_list_.find(start) == adj_list_.find(end))
399 return;
400 std::stack<T> stack;
401 std::unordered_map<T, typename std::unordered_set<Edge>::iterator> iterators;
402 std::vector<T> path;
403
404 iterators.reserve(adj_list_.size());
405 visited.reserve(adj_list_.size());
406 path.reserve(adj_list_.size());
407
408 stack.push(start);
409 visited.insert(start);
410 path.push_back(start);
411 iterators[start] = adj_list_[start].begin();
412
413 while (!stack.empty()) {
414 const T current = stack.top();
415
416 if (current == end) {
417 if (func(path) != VisitResult::CONTINUE) return;
418 stack.pop();
419 visited.erase(current);
420 path.pop_back();
421 continue;
422 }
423
424 auto& it = iterators.at(current);
425 if (it == adj_list_.at(current).end()) {
426 stack.pop();
427 visited.erase(current);
428 path.pop_back();
429 continue;
430 }
431
432 const T next = it->first;
433 ++it;
434 if (visited.find(next) == visited.end()) {
435 stack.push(next);
436 visited.insert(next);
437 path.push_back(next);
438 iterators.insert_or_assign(next, adj_list_.at(next).begin());
439 }
440 }
441 }
442
443 private:
444 const W zero_{};
445 std::unordered_map<T, AdjSet> adj_list_;
446};
447
448template <class T, class E>
449std::ostream& operator<<(std::ostream& os, const Graph<T, E>& s) {
450 os << "Graph{";
451 for (const auto& [vertex, edges] : s.adj_list()) {
452 os << "| " << vertex << " | -> ";
453 for (const auto& [adj_vertex, weight] : edges) os << adj_vertex << "(" << weight << "), ";
454 os << " - ";
455 }
456 return os << "}";
457}
458
459} // namespace dlinear
Graph class.
Definition Graph.hpp:66
std::unordered_map< T, AdjSet > adj_list_
Adjacency list of the graph.
Definition Graph.hpp:445
void AllPaths(const T &start, const T &end, const PathsFunc &func)
Find all paths from vertex start to vertex end.
Definition Graph.hpp:373
const W * GetEdgeWeight(const T &u, const T &v) const
Get a pointer to the weight of the edge from vertex u to vertex v.
Definition Graph.hpp:176
void Clear()
Clear the graph, removing all vertices and edges.
Definition Graph.hpp:212
void ClearEdges()
Clear the graph, removing only the edges and leaving the vertices.
Definition Graph.hpp:214
void AddVertex(const T &v)
Add a vertex v to the graph.
Definition Graph.hpp:147
bool HasEdges(const T &u) const
Check if there are any positive number of vertexes starting from vertex u.
Definition Graph.hpp:167
void AddEdge(const T &u, const T &v, bool bidirectional=true)
Add an edge to the graph from vertex u to vertex v.
Definition Graph.hpp:104
bool AddEdge(const T &u, const T &v, const W &weight, bool bidirectional=true)
Add an edge to the graph from vertex u to vertex v.
Definition Graph.hpp:121
size_t Size() const
Return the number of vertices in the graph.
Definition Graph.hpp:222
void BFS(const T &start, const VisitFunc &func, std::unordered_set< T > &visited)
Explore the graph using breadth-first search starting from vertex start.
Definition Graph.hpp:328
std::function< VisitResult(const T &, const T &, const W &)> VisitFunc
Visit function for DFS and BFS.
Definition Graph.hpp:70
void DFS(const T &start, const VisitFunc &func, std::unordered_set< T > &visited)
Explore the graph using depth-first search starting from vertex start.
Definition Graph.hpp:268
std::function< VisitResult( std::vector< T > &)> PathsFunc
Function to call on each path found.
Definition Graph.hpp:84
void AllPaths(const T &start, const T &end, const PathsFunc &func, std::unordered_set< T > &visited)
Find all paths from vertex start to vertex end.
Definition Graph.hpp:396
void RemoveVertex(const T &v)
Remove a vertex v from the graph.
Definition Graph.hpp:206
void RemoveEdge(const T &u, const T &v, bool bidirectional=true)
Remove an edge from vertex u to vertex v.
Definition Graph.hpp:197
bool HasEdge(const T &u, const T &v) const
Check if the graph contains an edge from vertex u to vertex v.
Definition Graph.hpp:158
void BFS(const T &start, const VisitFunc &func)
Explore the graph using breadth-first search starting from vertex start.
Definition Graph.hpp:308
const std::unordered_map< T, AdjSet > & adj_list() const
Return the adjacency list of the graph.
Definition Graph.hpp:189
bool IsEmpty() const
Check if the graph is empty.
Definition Graph.hpp:232
void DFS(const T &start, const VisitFunc &func)
Explore the graph using depth-first search starting from vertex start.
Definition Graph.hpp:247
Global namespace for the dlinear library.
VisitResult
Result returned by the visit function.
Definition Graph.hpp:31
@ CONTINUE
Continue the search as usual and add the adjacent vertices to the stack/queue.
@ STOP
Stop the search altogether.
@ SKIP
Skip adding the adjacent vertices to the stack/queue, but continue the search.