mirror of
https://github.com/Z3Prover/z3
synced 2025-08-03 18:00:23 +00:00
Update interface of network flows
This commit is contained in:
parent
759d80dfe3
commit
ab4efe2da0
5 changed files with 360 additions and 407 deletions
|
@ -22,7 +22,280 @@ Notes:
|
|||
#include "spanning_tree.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
template<typename Ext>
|
||||
thread_spanning_tree<Ext>::thread_spanning_tree(graph & g) :
|
||||
m_graph(g) {
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void thread_spanning_tree<Ext>::initialize(svector<bool> const & upwards) {
|
||||
unsigned num_nodes = m_graph.get_num_nodes();
|
||||
m_pred.resize(num_nodes);
|
||||
m_depth.resize(num_nodes);
|
||||
m_thread.resize(num_nodes);
|
||||
m_upwards.resize(num_nodes);
|
||||
|
||||
node root = m_graph.get_num_nodes() - 1;
|
||||
m_pred[root] = -1;
|
||||
m_depth[root] = 0;
|
||||
m_thread[root] = 0;
|
||||
|
||||
// Create artificial edges from/to root node to/from other nodes and initialize the spanning tree
|
||||
for (int i = 0; i < root; ++i) {
|
||||
m_pred[i] = root;
|
||||
m_depth[i] = 1;
|
||||
m_thread[i] = i + 1;
|
||||
m_upwards[i] = upwards[i];
|
||||
}
|
||||
|
||||
TRACE("network_flow", {
|
||||
tout << pp_vector("Predecessors", m_pred, true) << pp_vector("Threads", m_thread);
|
||||
tout << pp_vector("Depths", m_depth) << pp_vector("Upwards", m_upwards);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
typename thread_spanning_tree<Ext>::node thread_spanning_tree<Ext>::get_common_ancestor(node u, node v) {
|
||||
while (u != v) {
|
||||
if (m_depth[u] > m_depth[v])
|
||||
u = m_pred[u];
|
||||
else
|
||||
v = m_pred[v];
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
edge_id thread_spanning_tree<Ext>::get_edge_to_parent(node start) const {
|
||||
SASSERT(m_pred[start] != -1);
|
||||
edge_id id;
|
||||
node end = m_pred[start];
|
||||
VERIFY(m_upwards[start] ? m_graph.get_edge_id(start, end, id) : m_graph.get_edge_id(end, start, id));
|
||||
return id;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void thread_spanning_tree<Ext>::get_path(node start, node end, svector<edge_id> & path, svector<bool> & against) {
|
||||
node join = get_common_ancestor(start, end);
|
||||
path.reset();
|
||||
while (start != join) {
|
||||
edge_id e_id = get_edge_to_parent(start);
|
||||
path.push_back(e_id);
|
||||
against.push_back(is_forward_edge(e_id));
|
||||
start = m_pred[start];
|
||||
}
|
||||
while (end != join) {
|
||||
edge_id e_id = get_edge_to_parent(end);
|
||||
path.push_back(e_id);
|
||||
against.push_back(!is_forward_edge(e_id));
|
||||
end = m_pred[end];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool thread_spanning_tree<Ext>::is_forward_edge(edge_id e_id) const {
|
||||
node start = m_graph.get_source(e_id);
|
||||
node end = m_graph.get_target(e_id);
|
||||
SASSERT(m_pred[start] == end || m_pred[end] == start);
|
||||
return m_pred[start] == end;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void thread_spanning_tree<Ext>::get_descendants(node start, svector<node> & descendants) {
|
||||
descendants.reset();
|
||||
node u = start;
|
||||
while (m_depth[m_thread[u]] > m_depth[start]) {
|
||||
descendants.push_back(u);
|
||||
u = m_thread[u];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool thread_spanning_tree<Ext>::is_ancestor_of(node ancestor, node child) {
|
||||
for (node n = child; n != -1; n = m_pred[n]) {
|
||||
if (n == ancestor) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief add entering_edge, remove leaving_edge from spanning tree.
|
||||
|
||||
Old tree: New tree:
|
||||
root root
|
||||
/ \ / \
|
||||
x y x y
|
||||
/ \ / \ / \ / \
|
||||
u s u s
|
||||
| / /
|
||||
v w v w
|
||||
/ \ \ / \ \
|
||||
z p z p
|
||||
\ \ /
|
||||
q q
|
||||
*/
|
||||
template<typename Ext>
|
||||
void thread_spanning_tree<Ext>::update(edge_id enter_id, edge_id leave_id, bool & is_swap_enter, bool & is_swap_leave) {
|
||||
node p = m_graph.get_source(enter_id);
|
||||
node q = m_graph.get_target(enter_id);
|
||||
node u = m_graph.get_source(leave_id);
|
||||
node v = m_graph.get_target(leave_id);
|
||||
|
||||
if (m_pred[u] == v) {
|
||||
std::swap(u, v);
|
||||
is_swap_leave = true;
|
||||
}
|
||||
else {
|
||||
is_swap_leave = false;
|
||||
}
|
||||
SASSERT(m_pred[v] == u);
|
||||
|
||||
bool prev_upwards = false;
|
||||
if (is_ancestor_of(v, p)) {
|
||||
std::swap(p, q);
|
||||
prev_upwards = true;
|
||||
}
|
||||
|
||||
is_swap_enter = prev_upwards;
|
||||
SASSERT(is_ancestor_of(v, q));
|
||||
|
||||
TRACE("network_flow", {
|
||||
tout << "update_spanning_tree: (" << p << ", " << q << ") enters, (";
|
||||
tout << u << ", " << v << ") leaves\n";
|
||||
});
|
||||
|
||||
// Update m_pred (for nodes in the stem from q to v)
|
||||
// Note: m_pred[v] == u
|
||||
// Initialize m_upwards[q] = q_upwards
|
||||
|
||||
node old_pred = m_pred[q];
|
||||
if (q != v) {
|
||||
for (node n = q; n != u; ) {
|
||||
SASSERT(old_pred != u || n == v); // the last processed node is v
|
||||
SASSERT(-1 != m_pred[old_pred]);
|
||||
int next_old_pred = m_pred[old_pred];
|
||||
swap_order(n, old_pred);
|
||||
std::swap(m_upwards[n], prev_upwards);
|
||||
prev_upwards = !prev_upwards; // flip previous version of upwards.
|
||||
n = old_pred;
|
||||
old_pred = next_old_pred;
|
||||
}
|
||||
}
|
||||
m_pred[q] = p;
|
||||
|
||||
// m_thread were updated.
|
||||
// update the depth.
|
||||
|
||||
fix_depth(q, get_final(q));
|
||||
|
||||
TRACE("network_flow", {
|
||||
tout << pp_vector("Predecessors", m_pred, true) << pp_vector("Threads", m_thread);
|
||||
tout << pp_vector("Depths", m_depth) << pp_vector("Upwards", m_upwards);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Check invariants of main data-structures.
|
||||
|
||||
Spanning tree of m_graph + root is represented using:
|
||||
|
||||
svector<edge_state> m_states; edge_id |-> edge_state
|
||||
svector<bool> m_upwards; node |-> bool
|
||||
svector<node> m_pred; node |-> node
|
||||
svector<int> m_depth; node |-> int
|
||||
svector<node> m_thread; node |-> node
|
||||
|
||||
Tree is determined by m_pred:
|
||||
- m_pred[root] == -1
|
||||
- m_pred[n] = m != n for each node n, acyclic until reaching root.
|
||||
- m_depth[m_pred[n]] + 1 == m_depth[n] for each n != root
|
||||
|
||||
m_thread is a linked list traversing all nodes.
|
||||
Furthermore, the nodes linked in m_thread follows a
|
||||
depth-first traversal order.
|
||||
|
||||
m_upwards direction of edge from i to m_pred[i] m_graph
|
||||
|
||||
*/
|
||||
template<typename Ext>
|
||||
bool thread_spanning_tree<Ext>::check_well_formed() {
|
||||
node root = m_pred.size()-1;
|
||||
|
||||
// Check that m_thread traverses each node.
|
||||
// This gets checked using union-find as well.
|
||||
svector<bool> found(m_thread.size(), false);
|
||||
found[root] = true;
|
||||
for (node x = m_thread[root]; x != root; x = m_thread[x]) {
|
||||
SASSERT(x != m_thread[x]);
|
||||
found[x] = true;
|
||||
}
|
||||
for (unsigned i = 0; i < found.size(); ++i) {
|
||||
SASSERT(found[i]);
|
||||
}
|
||||
|
||||
// m_pred is acyclic, and points to root.
|
||||
SASSERT(m_pred[root] == -1);
|
||||
SASSERT(m_depth[root] == 0);
|
||||
for (node i = 0; i < root; ++i) {
|
||||
SASSERT(m_depth[m_pred[i]] < m_depth[i]);
|
||||
}
|
||||
|
||||
// m_depth[x] denotes distance from x to the root node
|
||||
for (node x = m_thread[root]; x != root; x = m_thread[x]) {
|
||||
SASSERT(m_depth[x] > 0);
|
||||
SASSERT(m_depth[x] == m_depth[m_pred[x]] + 1);
|
||||
}
|
||||
|
||||
// m_thread forms a spanning tree over [0..root]
|
||||
// Union-find structure
|
||||
svector<int> roots(m_pred.size(), -1);
|
||||
|
||||
for (node x = m_thread[root]; x != root; x = m_thread[x]) {
|
||||
node y = m_pred[x];
|
||||
// We are now going to check the edge between x and y
|
||||
SASSERT(find(roots, x) != find(roots, y));
|
||||
merge(roots, x, y);
|
||||
}
|
||||
|
||||
// All nodes belong to the same spanning tree
|
||||
for (unsigned i = 0; i < roots.size(); ++i) {
|
||||
SASSERT(roots[i] + roots.size() == 0 || roots[i] >= 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned find(svector<int>& roots, unsigned x) {
|
||||
unsigned old_x = x;
|
||||
while (roots[x] >= 0) {
|
||||
x = roots[x];
|
||||
}
|
||||
SASSERT(roots[x] < 0);
|
||||
if (old_x != x) {
|
||||
roots[old_x] = x;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
static void merge(svector<int>& roots, unsigned x, unsigned y) {
|
||||
x = find(roots, x);
|
||||
y = find(roots, y);
|
||||
SASSERT(roots[x] < 0 && roots[y] < 0);
|
||||
if (x == y) {
|
||||
return;
|
||||
}
|
||||
if (roots[x] > roots[y]) {
|
||||
std::swap(x, y);
|
||||
}
|
||||
SASSERT(roots[x] <= roots[y]);
|
||||
roots[x] += roots[y];
|
||||
roots[y] = x;
|
||||
}
|
||||
|
||||
/**
|
||||
swap v and q in tree.
|
||||
- fixup m_thread
|
||||
- fixup m_pred
|
||||
|
@ -119,251 +392,6 @@ namespace smt {
|
|||
SASSERT(children.empty());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool thread_spanning_tree<Ext>::is_ancestor_of(node ancestor, node child) {
|
||||
for (node n = child; n != -1; n = m_pred[n]) {
|
||||
if (n == ancestor) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned find(svector<int>& roots, unsigned x) {
|
||||
unsigned old_x = x;
|
||||
while (roots[x] >= 0) {
|
||||
x = roots[x];
|
||||
}
|
||||
SASSERT(roots[x] < 0);
|
||||
if (old_x != x) {
|
||||
roots[old_x] = x;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
static void merge(svector<int>& roots, unsigned x, unsigned y) {
|
||||
x = find(roots, x);
|
||||
y = find(roots, y);
|
||||
SASSERT(roots[x] < 0 && roots[y] < 0);
|
||||
if (x == y) {
|
||||
return;
|
||||
}
|
||||
if (roots[x] > roots[y]) {
|
||||
std::swap(x, y);
|
||||
}
|
||||
SASSERT(roots[x] <= roots[y]);
|
||||
roots[x] += roots[y];
|
||||
roots[y] = x;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void thread_spanning_tree<Ext>::initialize(svector<bool> const & upwards, int num_nodes) {
|
||||
m_pred.resize(num_nodes + 1);
|
||||
m_depth.resize(num_nodes + 1);
|
||||
m_thread.resize(num_nodes + 1);
|
||||
m_upwards.resize(num_nodes + 1);
|
||||
|
||||
node root = num_nodes;
|
||||
m_pred[root] = -1;
|
||||
m_depth[root] = 0;
|
||||
m_thread[root] = 0;
|
||||
|
||||
// Create artificial edges from/to root node to/from other nodes and initialize the spanning tree
|
||||
for (int i = 0; i < num_nodes; ++i) {
|
||||
m_pred[i] = root;
|
||||
m_depth[i] = 1;
|
||||
m_thread[i] = i + 1;
|
||||
m_upwards[i] = upwards[i];
|
||||
}
|
||||
|
||||
TRACE("network_flow", {
|
||||
tout << pp_vector("Predecessors", m_pred, true) << pp_vector("Threads", m_thread);
|
||||
tout << pp_vector("Depths", m_depth) << pp_vector("Upwards", m_upwards);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
typename thread_spanning_tree<Ext>::node thread_spanning_tree<Ext>::get_common_ancestor(node u, node v) {
|
||||
while (u != v) {
|
||||
if (m_depth[u] > m_depth[v])
|
||||
u = m_pred[u];
|
||||
else
|
||||
v = m_pred[v];
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void thread_spanning_tree<Ext>::get_descendants(node start, svector<node>& descendants) {
|
||||
descendants.reset();
|
||||
node u = start;
|
||||
while (m_depth[m_thread[u]] > m_depth[start]) {
|
||||
descendants.push_back(u);
|
||||
u = m_thread[u];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void thread_spanning_tree<Ext>::get_ancestors(node start, svector<node>& ancestors) {
|
||||
ancestors.reset();
|
||||
while (m_pred[start] != -1) {
|
||||
ancestors.push_back(start);
|
||||
start = m_pred[start];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief add entering_edge, remove leaving_edge from spanning tree.
|
||||
|
||||
Old tree: New tree:
|
||||
root root
|
||||
/ \ / \
|
||||
x y x y
|
||||
/ \ / \ / \ / \
|
||||
u s u s
|
||||
| / /
|
||||
v w v w
|
||||
/ \ \ / \ \
|
||||
z p z p
|
||||
\ \ /
|
||||
q q
|
||||
*/
|
||||
template<typename Ext>
|
||||
void thread_spanning_tree<Ext>::update(node p, node q, node u, node v) {
|
||||
bool q_upwards = false;
|
||||
|
||||
// v is parent of u so T_u does not contain root node
|
||||
if (m_pred[u] == v) {
|
||||
std::swap(u, v);
|
||||
}
|
||||
SASSERT(m_pred[v] == u);
|
||||
|
||||
if (is_ancestor_of(v, p)) {
|
||||
std::swap(p, q);
|
||||
q_upwards = true;
|
||||
}
|
||||
SASSERT(is_ancestor_of(v, q));
|
||||
|
||||
TRACE("network_flow", {
|
||||
tout << "update_spanning_tree: (" << p << ", " << q << ") enters, (";
|
||||
tout << u << ", " << v << ") leaves\n";
|
||||
});
|
||||
|
||||
// Update m_pred (for nodes in the stem from q to v)
|
||||
// Note: m_pred[v] == u
|
||||
// Initialize m_upwards[q] = q_upwards
|
||||
|
||||
bool prev_upwards = q_upwards;
|
||||
node old_pred = m_pred[q];
|
||||
if (q != v) {
|
||||
for (node n = q; n != u; ) {
|
||||
SASSERT(old_pred != u || n == v); // the last processed node is v
|
||||
TRACE("network_flow", {
|
||||
tout << pp_vector("Predecessors", m_pred, true);
|
||||
});
|
||||
SASSERT(-1 != m_pred[old_pred]);
|
||||
int next_old_pred = m_pred[old_pred];
|
||||
swap_order(n, old_pred);
|
||||
std::swap(m_upwards[n], prev_upwards);
|
||||
prev_upwards = !prev_upwards; // flip previous version of upwards.
|
||||
n = old_pred;
|
||||
old_pred = next_old_pred;
|
||||
}
|
||||
}
|
||||
m_pred[q] = p;
|
||||
|
||||
// m_thread were updated.
|
||||
// update the depth.
|
||||
|
||||
fix_depth(q, get_final(q));
|
||||
|
||||
TRACE("network_flow", {
|
||||
tout << pp_vector("Predecessors", m_pred, true) << pp_vector("Threads", m_thread);
|
||||
tout << pp_vector("Depths", m_depth) << pp_vector("Upwards", m_upwards);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Check invariants of main data-structures.
|
||||
|
||||
Spanning tree of m_graph + root is represented using:
|
||||
|
||||
svector<edge_state> m_states; edge_id |-> edge_state
|
||||
svector<bool> m_upwards; node |-> bool
|
||||
svector<node> m_pred; node |-> node
|
||||
svector<int> m_depth; node |-> int
|
||||
svector<node> m_thread; node |-> node
|
||||
|
||||
Tree is determined by m_pred:
|
||||
- m_pred[root] == -1
|
||||
- m_pred[n] = m != n for each node n, acyclic until reaching root.
|
||||
- m_depth[m_pred[n]] + 1 == m_depth[n] for each n != root
|
||||
|
||||
m_thread is a linked list traversing all nodes.
|
||||
Furthermore, the nodes linked in m_thread follows a
|
||||
depth-first traversal order.
|
||||
|
||||
m_upwards direction of edge from i to m_pred[i] m_graph
|
||||
|
||||
*/
|
||||
template<typename Ext>
|
||||
bool thread_spanning_tree<Ext>::check_well_formed() {
|
||||
node root = m_pred.size()-1;
|
||||
|
||||
// Check that m_thread traverses each node.
|
||||
// This gets checked using union-find as well.
|
||||
svector<bool> found(m_thread.size(), false);
|
||||
found[root] = true;
|
||||
for (node x = m_thread[root]; x != root; x = m_thread[x]) {
|
||||
SASSERT(x != m_thread[x]);
|
||||
found[x] = true;
|
||||
}
|
||||
for (unsigned i = 0; i < found.size(); ++i) {
|
||||
SASSERT(found[i]);
|
||||
}
|
||||
|
||||
// m_pred is acyclic, and points to root.
|
||||
SASSERT(m_pred[root] == -1);
|
||||
SASSERT(m_depth[root] == 0);
|
||||
for (node i = 0; i < root; ++i) {
|
||||
SASSERT(m_depth[m_pred[i]] < m_depth[i]);
|
||||
}
|
||||
|
||||
// m_depth[x] denotes distance from x to the root node
|
||||
for (node x = m_thread[root]; x != root; x = m_thread[x]) {
|
||||
SASSERT(m_depth[x] > 0);
|
||||
SASSERT(m_depth[x] == m_depth[m_pred[x]] + 1);
|
||||
}
|
||||
|
||||
// m_thread forms a spanning tree over [0..root]
|
||||
// Union-find structure
|
||||
svector<int> roots(m_pred.size(), -1);
|
||||
|
||||
for (node x = m_thread[root]; x != root; x = m_thread[x]) {
|
||||
node y = m_pred[x];
|
||||
// We are now going to check the edge between x and y
|
||||
SASSERT(find(roots, x) != find(roots, y));
|
||||
merge(roots, x, y);
|
||||
}
|
||||
|
||||
// All nodes belong to the same spanning tree
|
||||
for (unsigned i = 0; i < roots.size(); ++i) {
|
||||
SASSERT(roots[i] + roots.size() == 0 || roots[i] >= 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool thread_spanning_tree<Ext>::get_arc_direction(node start) const {
|
||||
return m_upwards[start];
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
typename thread_spanning_tree<Ext>::node thread_spanning_tree<Ext>::get_parent(node start) {
|
||||
return m_pred[start];
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue