mirror of
https://github.com/Z3Prover/z3
synced 2025-04-29 11:55:51 +00:00
Update Network Simplex implementation
This commit is contained in:
parent
d78d22deb6
commit
906bbb4eeb
4 changed files with 200 additions and 152 deletions
|
@ -25,170 +25,212 @@ Notes:
|
|||
namespace smt {
|
||||
|
||||
template<typename Ext>
|
||||
network_flow<Ext>::network_flow(graph & g, vector<fin_numeral> const& costs) :
|
||||
network_flow<Ext>::network_flow(graph & g, vector<fin_numeral> const& balances) :
|
||||
m_graph(g),
|
||||
m_costs(costs) {
|
||||
m_balances(balances) {
|
||||
unsigned num_nodes = m_balances.size() + 1;
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
|
||||
vector<edge> const & es = m_graph.get_all_edges();
|
||||
for (unsigned i = 0; i < num_edges; ++i) {
|
||||
fin_numeral cost(es[i].get_weight().get_rational());
|
||||
m_costs.push_back(cost);
|
||||
}
|
||||
|
||||
m_balances.resize(num_nodes);
|
||||
for (unsigned i = 0; i < balances.size(); ++i) {
|
||||
m_costs.push_back(balances[i]);
|
||||
}
|
||||
|
||||
m_potentials.resize(num_nodes);
|
||||
m_costs.resize(num_edges);
|
||||
m_flows.resize(num_edges);
|
||||
m_states.resize(num_edges);
|
||||
|
||||
m_upwards.resize(num_nodes);
|
||||
m_pred.resize(num_nodes);
|
||||
m_depth.resize(num_nodes);
|
||||
m_thread.resize(num_nodes);
|
||||
m_rev_thread.resize(num_nodes);
|
||||
m_final.resize(num_nodes);
|
||||
m_num_node.resize(num_nodes);
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::initialize() {
|
||||
// TODO: construct an initial spanning tree i.e. inializing m_pred, m_depth and m_thread.
|
||||
compute_potentials();
|
||||
compute_flows();
|
||||
}
|
||||
// Create an artificial root node to construct initial spanning tree
|
||||
unsigned num_nodes = m_balances.size();
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
node root = num_nodes;
|
||||
m_pred[root] = -1;
|
||||
m_thread[root] = 0;
|
||||
m_rev_thread[0] = root;
|
||||
m_num_node[root] = num_nodes + 1;
|
||||
m_final[root] = root - 1;
|
||||
m_potentials[root] = numeral::zero();
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::compute_potentials() {
|
||||
SASSERT(!m_potentials.empty());
|
||||
SASSERT(!m_thread.empty());
|
||||
SASSERT(m_thread.size() == m_pred.size());
|
||||
|
||||
m_potentials.set(0, numeral::zero());
|
||||
node target = m_thread[0];
|
||||
|
||||
while (target != 0) {
|
||||
node source = m_pred[target];
|
||||
edge_id e_id;
|
||||
if (m_graph.get_edge_id(source, target, e_id)) {
|
||||
m_potentials.set(target, m_potentials[source] - m_graph.get_weight(e_id));
|
||||
}
|
||||
if (m_graph.get_edge_id(target, source, e_id)) {
|
||||
m_potentials.set(target, m_potentials[source] + m_graph.get_weight(e_id));
|
||||
}
|
||||
target = m_thread[target];
|
||||
fin_numeral sum_supply = fin_numeral::zero();
|
||||
for (unsigned i = 0; i < m_balances.size(); ++i) {
|
||||
sum_supply += m_balances[i];
|
||||
}
|
||||
m_balances[root] = -sum_supply;
|
||||
|
||||
m_states.resize(num_nodes + num_edges);
|
||||
m_states.fill(NON_BASIS);
|
||||
|
||||
// Create artificial edges and initialize the spanning tree
|
||||
for (unsigned i = 0; i < num_nodes; ++i) {
|
||||
m_upwards[i] = m_balances[i] >= fin_numeral::zero();
|
||||
m_pred[i] = root;
|
||||
m_depth[i] = 1;
|
||||
m_thread[i] = i + 1;
|
||||
m_final[i] = i;
|
||||
m_rev_thread[i] = (i = 0) ? root : i - 1;
|
||||
m_num_node[i] = 1;
|
||||
m_states[num_edges + i] = BASIS;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::compute_flows() {
|
||||
vector<numeral> balances(m_balances);
|
||||
|
||||
// OPTIMIZE: Need a set data structure for efficiently removing elements
|
||||
vector<edge_id> basics;
|
||||
while (!basics.empty()) {
|
||||
// Find a leaf node of a spanning tree
|
||||
node target;
|
||||
for (unsigned int i = 0; i < m_thread.size(); ++i) {
|
||||
if (m_depth[i] <= m_depth[m_thread[i]]) {
|
||||
target = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
node source = m_pred[target];
|
||||
void network_flow<Ext>::update_potentials() {
|
||||
node src = m_graph.get_source(m_entering_edge);
|
||||
node tgt = m_graph.get_source(m_entering_edge);
|
||||
numeral cost = m_graph.get_weight(m_entering_edge);
|
||||
numeral change = m_upwards[src] ? (cost - m_potentials[src] + m_potentials[tgt]) :
|
||||
(-cost + m_potentials[src] - m_potentials[tgt]);
|
||||
node last = m_thread[m_final[src]];
|
||||
for (node u = src; u != last; u = m_thread[u]) {
|
||||
m_potentials[u] += change;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::update_flows() {
|
||||
numeral val = m_state[m_entering_edge] == NON_BASIS ? numeral::zero() : m_delta;
|
||||
m_flows[m_entering_edge] += val;
|
||||
for (unsigned u = m_graph.get_source(m_entering_edge); u != m_join_node; u = m_pred[u]) {
|
||||
edge_id e_id;
|
||||
if (m_graph.get_edge_id(source, target, e_id)) {
|
||||
m_flows.set(e_id, -m_graph.get_weight(basics[target]));
|
||||
basics[source] += basics[target];
|
||||
basics.erase(e_id);
|
||||
}
|
||||
else if (m_graph.get_edge_id(target, source, e_id)) {
|
||||
m_flows.set(e_id, m_graph.get_weight(basics[target]));
|
||||
basics[source] += basics[target];
|
||||
basics.erase(e_id);
|
||||
}
|
||||
m_graph.get_edge_id(u, m_pred[u], e_id);
|
||||
m_flows[e_id] += m_upwards[u] ? -val : val;
|
||||
}
|
||||
for (unsigned u = m_graph.get_target(m_entering_edge); u != m_join_node; u = m_pred[u]) {
|
||||
edge_id e_id;
|
||||
m_graph.get_edge_id(u, m_pred[u], e_id);
|
||||
m_flows[e_id] += m_upwards[u] ? val : -val;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::is_optimal(edge_id & violating_edge) {
|
||||
// TODO: how to get nonbasics vector?
|
||||
vector<edge> nonbasics;
|
||||
typename vector<edge>::iterator it = nonbasics.begin();
|
||||
typename vector<edge>::iterator end = nonbasics.end();
|
||||
bool found = false;
|
||||
for (unsigned int i = 0; i < nonbasics.size(); ++i) {
|
||||
edge & e = nonbasics[i];
|
||||
if (e.is_enabled()) {
|
||||
bool network_flow<Ext>::choose_entering_edge() {
|
||||
vector<edge> const & es = m_graph.get_all_edges();
|
||||
for (unsigned int i = 0; i < es.size(); ++i) {
|
||||
edge const & e = es[i];
|
||||
edge_id e_id;
|
||||
if (e.is_enabled() && m_graph.get_edge_id(e.get_source(), e.get_target(), e_id) && m_states[e_id] == BASIS) {
|
||||
node source = e.get_source();
|
||||
node target = e.get_target();
|
||||
numeral cost = e.get_weight() - m_potentials[source] + m_potentials[target];
|
||||
// Choose the first negative-cost edge to be the violating edge
|
||||
// TODO: add multiple pivoting strategies
|
||||
numeral zero(0);
|
||||
if (cost < zero) {
|
||||
edge_id e_id;
|
||||
m_graph.get_edge_id(source, target, e_id);
|
||||
violating_edge = e_id;
|
||||
found = true;
|
||||
break;
|
||||
if (cost < numeral::zero()) {
|
||||
m_entering_edge = e_id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !found;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
edge_id network_flow<Ext>::choose_leaving_edge(edge_id entering_edge) {
|
||||
node source = m_graph.get_source(entering_edge);
|
||||
node target = m_graph.get_target(entering_edge);
|
||||
while (source != target) {
|
||||
if (m_depth[source] > m_depth[target])
|
||||
source = m_pred[source];
|
||||
else if (m_depth[source] < m_depth[target])
|
||||
target = m_pred[target];
|
||||
bool network_flow<Ext>::choose_leaving_edge() {
|
||||
node source = m_graph.get_source(m_entering_edge);
|
||||
node target = m_graph.get_target(m_entering_edge);
|
||||
node u = source, v = target;
|
||||
while (u != v) {
|
||||
if (m_depth[u] > m_depth[v])
|
||||
u = m_pred[u];
|
||||
else if (m_depth[u] < m_depth[v])
|
||||
v = m_pred[v];
|
||||
else {
|
||||
source = m_pred[source];
|
||||
target = m_pred[target];
|
||||
u = m_pred[u];
|
||||
v = m_pred[v];
|
||||
}
|
||||
}
|
||||
edge_id e_id;
|
||||
m_graph.get_edge_id(source, target, e_id);
|
||||
return e_id;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::update_spanning_tree(edge_id entering_edge, edge_id leaving_edge) {
|
||||
// Need special handling in case two edges are identical
|
||||
SASSERT(entering_edge != leaving_edge);
|
||||
|
||||
// Update potentials
|
||||
node target = m_upwards[leaving_edge] ? m_graph.get_source(leaving_edge) : m_graph.get_target(leaving_edge);
|
||||
numeral src_pot = m_potentials[m_graph.get_source(entering_edge)];
|
||||
numeral tgt_pot = m_potentials[m_graph.get_target(entering_edge)];
|
||||
numeral weight = m_graph.get_weight(entering_edge);
|
||||
numeral change = m_upwards[entering_edge] ? (weight - src_pot + tgt_pot) : (-weight + src_pot - tgt_pot);
|
||||
m_potentials[target] += change;
|
||||
node start = m_thread[target];
|
||||
while (m_depth[start] > m_depth[target]) {
|
||||
m_potentials[start] += change;
|
||||
start = m_thread[start];
|
||||
// Found first common ancestor of source and target
|
||||
m_join_node = u;
|
||||
// FIXME: need to get truly finite value
|
||||
numeral infty = numeral(INT_MAX);
|
||||
m_delta = infty;
|
||||
node src, tgt;
|
||||
// Send flows along the path from source to the ancestor
|
||||
for (unsigned u = source; u != m_join_node; u = m_pred[u]) {
|
||||
edge_id e_id;
|
||||
m_graph.get_edge_id(u, m_pred[u], e_id);
|
||||
numeral d = m_upwards[u] ? m_flows[e_id] : infty;
|
||||
if (d < m_delta) {
|
||||
m_delta = d;
|
||||
src = u;
|
||||
tgt = m_pred[u];
|
||||
}
|
||||
}
|
||||
|
||||
// Send flows along the path from target to the ancestor
|
||||
for (unsigned u = target; u != m_join_node; u = m_pred[u]) {
|
||||
edge_id e_id;
|
||||
m_graph.get_edge_id(u, m_pred[u], e_id);
|
||||
numeral d = m_upwards[u] ? infty : m_flows[e_id];
|
||||
if (d <= m_delta) {
|
||||
m_delta = d;
|
||||
src = u;
|
||||
tgt = m_pred[u];
|
||||
}
|
||||
}
|
||||
|
||||
if (m_delta < infty) {
|
||||
m_graph.get_edge_id(src, tgt, m_leaving_edge);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::is_unbounded() {
|
||||
return false;
|
||||
void network_flow<Ext>::update_spanning_tree() {
|
||||
|
||||
}
|
||||
|
||||
// Get the optimal solution
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::get_optimal_solution(numeral & objective, vector<numeral> & flows) {
|
||||
SASSERT(m_is_optimal);
|
||||
flows.reset();
|
||||
flows.append(m_flows);
|
||||
objective = numeral::zero();
|
||||
for (unsigned int i = 0; i < m_flows.size(); ++i) {
|
||||
objective += m_costs[i] * m_flows[i];
|
||||
typename network_flow<Ext>::numeral network_flow<Ext>::get_optimal_solution(vector<numeral> & result, bool is_dual) {
|
||||
m_objective_value = numeral::zero();
|
||||
for (unsigned i = 0; i < m_flows.size(); ++i) {
|
||||
m_objective_value += m_costs[i] * m_flows[i];
|
||||
}
|
||||
result.reset();
|
||||
if (is_dual) {
|
||||
result.append(m_potentials);
|
||||
}
|
||||
else {
|
||||
result.append(m_flows);
|
||||
}
|
||||
return m_objective_value;
|
||||
}
|
||||
|
||||
// Minimize cost flows
|
||||
// Return true if found an optimal solution, and return false if unbounded
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::min_cost() {
|
||||
SASSERT(!m_graph.get_all_edges().empty());
|
||||
initialize();
|
||||
edge_id entering_edge;
|
||||
while (!is_optimal(entering_edge)) {
|
||||
edge_id leaving_edge = choose_leaving_edge(entering_edge);
|
||||
update_spanning_tree(entering_edge, leaving_edge);
|
||||
if (is_unbounded()) {
|
||||
m_is_optimal = false;
|
||||
return m_is_optimal;
|
||||
while (choose_entering_edge()) {
|
||||
bool bounded = choose_leaving_edge();
|
||||
if (!bounded) return false;
|
||||
if (m_entering_edge != m_leaving_edge) {
|
||||
m_states[m_entering_edge] = BASIS;
|
||||
m_states[m_leaving_edge] = NON_BASIS;
|
||||
update_spanning_tree();
|
||||
update_potentials();
|
||||
}
|
||||
}
|
||||
m_is_optimal = true;
|
||||
return m_is_optimal;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue