Copyright (c) 2013 Microsoft Corporation
Module Name:
network_flow.h
Abstract:
Implements Network Simplex algorithm for min cost flow problem
Author:
Anh-Dung Phan (t-anphan) 2013-10-24
Notes:
This will be used to solve the dual of min cost flow problem
i.e. optimization of difference constraint.
We need a function to reduce DL constraints to min cost flow problem
and another function to convert from min cost flow solution to DL solution.
It remains unclear how to convert DL assignment to a basic feasible solution of Network Simplex.
A naive approach is to run an algorithm on max flow in order to get a spanning tree.
--*/
#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() {}
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);
};
}