mirror of
https://github.com/Z3Prover/z3
synced 2025-04-23 17:15:31 +00:00
move min_cut, fix #1321
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
8acc924c21
commit
371f0b193c
9 changed files with 328 additions and 385 deletions
|
@ -28,6 +28,7 @@ z3_add_component(util
|
|||
lbool.cpp
|
||||
luby.cpp
|
||||
memory_manager.cpp
|
||||
min_cut.cpp
|
||||
mpbq.cpp
|
||||
mpf.cpp
|
||||
mpff.cpp
|
||||
|
|
229
src/util/min_cut.cpp
Normal file
229
src/util/min_cut.cpp
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
min_cut.cpp
|
||||
|
||||
Abstract:
|
||||
min cut solver
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
#include "util/min_cut.h"
|
||||
|
||||
min_cut::min_cut() {
|
||||
// push back two empty vectors for source and sink
|
||||
m_edges.push_back(edge_vector());
|
||||
m_edges.push_back(edge_vector());
|
||||
}
|
||||
|
||||
unsigned min_cut::new_node() {
|
||||
m_edges.push_back(edge_vector());
|
||||
return m_edges.size() - 1;
|
||||
}
|
||||
|
||||
void min_cut::add_edge(unsigned int i, unsigned int j) {
|
||||
m_edges.reserve(i + 1);
|
||||
m_edges[i].push_back(edge(j, 1));
|
||||
TRACE("spacer.mincut", tout << "adding edge (" << i << "," << j << ")\n";);
|
||||
}
|
||||
|
||||
void min_cut::compute_min_cut(unsigned_vector& cut_nodes) {
|
||||
if (m_edges.size() == 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_d.resize(m_edges.size());
|
||||
m_pred.resize(m_edges.size());
|
||||
|
||||
// compute initial distances and number of nodes
|
||||
compute_initial_distances();
|
||||
|
||||
unsigned i = 0;
|
||||
|
||||
while (m_d[0] < m_edges.size()) {
|
||||
unsigned j = get_admissible_edge(i);
|
||||
|
||||
if (j < m_edges.size()) {
|
||||
// advance(i)
|
||||
m_pred[j] = i;
|
||||
i = j;
|
||||
|
||||
// if i is the sink, augment path
|
||||
if (i == 1) {
|
||||
augment_path();
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// retreat
|
||||
compute_distance(i);
|
||||
if (i != 0) {
|
||||
i = m_pred[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// split nodes into reachable and unreachable ones
|
||||
bool_vector reachable(m_edges.size());
|
||||
compute_reachable_nodes(reachable);
|
||||
|
||||
// find all edges between reachable and unreachable nodes and for each such edge, add corresponding lemma to unsat-core
|
||||
compute_cut_and_add_lemmas(reachable, cut_nodes);
|
||||
}
|
||||
|
||||
void min_cut::compute_initial_distances() {
|
||||
unsigned_vector todo;
|
||||
bool_vector visited(m_edges.size());
|
||||
|
||||
todo.push_back(0); // start at the source, since we do postorder traversel
|
||||
|
||||
while (!todo.empty()) {
|
||||
unsigned current = todo.back();
|
||||
|
||||
// if we haven't already visited current
|
||||
if (!visited[current]) {
|
||||
bool exists_unvisited_parent = false;
|
||||
|
||||
// add unprocessed parents to stack for DFS. If there is at least one unprocessed parent, don't compute the result
|
||||
// for current now, but wait until those unprocessed parents are processed
|
||||
for (auto const& edge : m_edges[current]) {
|
||||
unsigned parent = edge.node;
|
||||
|
||||
// if we haven't visited the current parent yet
|
||||
if (!visited[parent]) {
|
||||
// add it to the stack
|
||||
todo.push_back(parent);
|
||||
exists_unvisited_parent = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if we already visited all parents, we can visit current too
|
||||
if (!exists_unvisited_parent) {
|
||||
visited[current] = true;
|
||||
todo.pop_back();
|
||||
|
||||
compute_distance(current); // I.H. all parent distances are already computed
|
||||
}
|
||||
}
|
||||
else {
|
||||
todo.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned min_cut::get_admissible_edge(unsigned i) {
|
||||
for (const auto& edge : m_edges[i]) {
|
||||
if (edge.weight > 0 && m_d[i] == m_d[edge.node] + 1) {
|
||||
return edge.node;
|
||||
}
|
||||
}
|
||||
return m_edges.size(); // no element found
|
||||
}
|
||||
|
||||
void min_cut::augment_path() {
|
||||
// find bottleneck capacity
|
||||
unsigned max = std::numeric_limits<unsigned int>::max();
|
||||
unsigned k = 1;
|
||||
while (k != 0) {
|
||||
unsigned l = m_pred[k];
|
||||
for (const auto& edge : m_edges[l]) {
|
||||
if (edge.node == k) {
|
||||
max = std::min(max, edge.weight);
|
||||
}
|
||||
}
|
||||
k = l;
|
||||
}
|
||||
|
||||
k = 1;
|
||||
while (k != 0) {
|
||||
unsigned l = m_pred[k];
|
||||
|
||||
// decrease capacity
|
||||
for (auto& edge : m_edges[l]) {
|
||||
if (edge.node == k) {
|
||||
edge.weight -= max;
|
||||
}
|
||||
}
|
||||
// increase reverse flow
|
||||
bool already_exists = false;
|
||||
for (auto& edge : m_edges[k]) {
|
||||
if (edge.node == l) {
|
||||
already_exists = true;
|
||||
edge.weight += max;
|
||||
}
|
||||
}
|
||||
if (!already_exists) {
|
||||
m_edges[k].push_back(edge(1, max));
|
||||
}
|
||||
k = l;
|
||||
}
|
||||
}
|
||||
|
||||
void min_cut::compute_distance(unsigned i) {
|
||||
if (i == 1) { // sink node
|
||||
m_d[1] = 0;
|
||||
}
|
||||
else {
|
||||
unsigned min = std::numeric_limits<unsigned int>::max();
|
||||
|
||||
// find edge (i,j) with positive residual capacity and smallest distance
|
||||
for (const auto& edge : m_edges[i]) {
|
||||
if (edge.weight > 0) {
|
||||
min = std::min(min, m_d[edge.node] + 1);
|
||||
}
|
||||
}
|
||||
m_d[i] = min;
|
||||
}
|
||||
}
|
||||
|
||||
void min_cut::compute_reachable_nodes(bool_vector& reachable) {
|
||||
unsigned_vector todo;
|
||||
|
||||
todo.push_back(0);
|
||||
while (!todo.empty()) {
|
||||
unsigned current = todo.back();
|
||||
todo.pop_back();
|
||||
|
||||
if (!reachable[current]) {
|
||||
reachable[current] = true;
|
||||
|
||||
for (const auto& edge : m_edges[current]) {
|
||||
if (edge.weight > 0) {
|
||||
todo.push_back(edge.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void min_cut::compute_cut_and_add_lemmas(bool_vector& reachable, unsigned_vector& cut_nodes) {
|
||||
unsigned_vector todo;
|
||||
bool_vector visited(m_edges.size());
|
||||
|
||||
todo.push_back(0);
|
||||
while (!todo.empty()) {
|
||||
unsigned current = todo.back();
|
||||
todo.pop_back();
|
||||
|
||||
if (!visited[current]) {
|
||||
visited[current] = true;
|
||||
|
||||
for (const auto& edge : m_edges[current]) {
|
||||
unsigned successor = edge.node;
|
||||
if (reachable[successor]) {
|
||||
todo.push_back(successor);
|
||||
}
|
||||
else {
|
||||
cut_nodes.push_back(successor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
src/util/min_cut.h
Normal file
56
src/util/min_cut.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
min_cut.h
|
||||
|
||||
Abstract:
|
||||
min cut solver
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef MIN_CUT_H_
|
||||
#define MIN_CUT_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
|
||||
class min_cut {
|
||||
public:
|
||||
min_cut();
|
||||
|
||||
unsigned new_node();
|
||||
/*
|
||||
\brief add an edge (with unit capacity)
|
||||
*/
|
||||
void add_edge(unsigned i, unsigned j);
|
||||
|
||||
void compute_min_cut(unsigned_vector& cut_nodes);
|
||||
|
||||
private:
|
||||
|
||||
typedef svector<bool> bool_vector;
|
||||
struct edge { unsigned node; unsigned weight; edge(unsigned n, unsigned w): node(n), weight(w) {} edge(): node(0), weight(0) {} };
|
||||
typedef svector<edge> edge_vector;
|
||||
|
||||
vector<edge_vector> m_edges; // map from node to all outgoing edges together with their weights (also contains "reverse edges")
|
||||
unsigned_vector m_d; // approximation of distance from node to sink in residual graph
|
||||
unsigned_vector m_pred; // predecessor-information for reconstruction of augmenting path
|
||||
|
||||
void compute_initial_distances();
|
||||
unsigned get_admissible_edge(unsigned i);
|
||||
void augment_path();
|
||||
void compute_distance(unsigned i);
|
||||
void compute_reachable_nodes(bool_vector& reachable);
|
||||
void compute_cut_and_add_lemmas(bool_vector& reachable, unsigned_vector& cut_nodes);
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue