#pragma once
#include "util/inf_rational.h"
#include "smt/diff_logic.h"
#include "smt/spanning_tree.h"
namespace smt {
enum min_flow_result {
INFEASIBLE,
OPTIMAL,
UNBOUNDED,
};
enum pivot_rule {
FIRST_ELIGIBLE,
BEST_ELIGIBLE,
CANDIDATE_LIST
};
template<typename Ext>
class network_flow : private Ext {
private:
enum edge_state {
LOWER = 1,
BASIS = 0,
};
typedef dl_var node;
typedef dl_edge<Ext> edge;
typedef dl_graph<Ext> graph;
typedef typename Ext::numeral numeral;
typedef typename Ext::fin_numeral fin_numeral;
class pivot_rule_impl {
protected:
graph & m_graph;
svector<edge_state> & m_states;
vector<numeral> & m_potentials;
edge_id & m_enter_id;
bool edge_in_tree(edge_id id) const { return m_states[id] == BASIS; }
public:
pivot_rule_impl(graph & g, vector<numeral> & potentials,
svector<edge_state> & states, edge_id & enter_id)
: m_graph(g),
m_potentials(potentials),
m_states(states),
m_enter_id(enter_id) {
}
virtual ~pivot_rule_impl() = default;
virtual bool choose_entering_edge() = 0;
virtual pivot_rule rule() const = 0;
};
class first_eligible_pivot : public pivot_rule_impl {
edge_id m_next_edge;
public:
first_eligible_pivot(graph & g, vector<numeral> & potentials,
svector<edge_state> & states, edge_id & enter_id) :
pivot_rule_impl(g, potentials, states, enter_id),
m_next_edge(0) {
}
virtual bool choose_entering_edge();
virtual pivot_rule rule() const { return FIRST_ELIGIBLE; }
};
class best_eligible_pivot : public pivot_rule_impl {
public:
best_eligible_pivot(graph & g, vector<numeral> & potentials,
svector<edge_state> & states, edge_id & enter_id) :
pivot_rule_impl(g, potentials, states, enter_id) {
}
virtual pivot_rule rule() const { return BEST_ELIGIBLE; }
virtual bool choose_entering_edge();
};
class candidate_list_pivot : public pivot_rule_impl {
private:
edge_id m_next_edge;
svector<edge_id> m_candidates;
unsigned m_num_candidates;
unsigned m_minor_step;
unsigned m_current_length;
static const unsigned NUM_CANDIDATES = 10;
static const unsigned MINOR_STEP_LIMIT = 5;
public:
candidate_list_pivot(graph & g, vector<numeral> & potentials,
svector<edge_state> & states, edge_id & enter_id) :
pivot_rule_impl(g, potentials, states, enter_id),
m_next_edge(0),
m_minor_step(0),
m_current_length(0),
m_num_candidates(NUM_CANDIDATES),
m_candidates(m_num_candidates) {
}
virtual pivot_rule rule() const { return CANDIDATE_LIST; }
virtual bool choose_entering_edge();
};
graph m_graph;
scoped_ptr<spanning_tree_base> m_tree;
scoped_ptr<pivot_rule_impl> m_pivot;
vector<fin_numeral> m_balances; vector<numeral> m_potentials; vector<numeral> m_flows; svector<edge_state> m_states;
unsigned m_step;
edge_id m_enter_id;
edge_id m_leave_id;
optional<numeral> m_delta;
void initialize();
void update_potentials();
void update_flows();
bool choose_entering_edge(pivot_rule pr);
bool choose_leaving_edge();
void update_spanning_tree();
numeral get_cost() const;
bool edge_in_tree(edge_id id) const;
bool is_infeasible();
bool check_well_formed();
bool check_optimal();
void display_primal(std::ofstream & os);
void display_dual(std::ofstream & os);
void display_spanning_tree(std::ofstream & os);
void display_system(std::ofstream & os);
public:
network_flow(graph & g, vector<fin_numeral> const & balances);
min_flow_result min_cost(pivot_rule pr = FIRST_ELIGIBLE);
numeral get_optimal_solution(vector<numeral> & result, bool is_dual);
};
}