andres::graph
lp.hxx
1 #pragma once
2 #ifndef ANDRES_GRAPH_MULTICUT_LP_HXX
3 #define ANDRES_GRAPH_MULTICUT_LP_HXX
4 
5 #include <cassert>
6 #include <cstddef>
7 #include <stdexcept>
8 #include <vector>
9 #include <deque>
10 #include <algorithm>
11 
12 #include "andres/graph/complete-graph.hxx"
13 #include "andres/graph/shortest-paths.hxx"
14 
15 
16 namespace andres {
17 namespace graph {
18 namespace multicut {
19 
20 template<typename RELAX, typename GRAPH, typename ECA>
21 inline
22 std::vector<double> lp(const GRAPH& graph, const ECA& edgeCosts, std::size_t numberOfIterations = std::numeric_limits<std::size_t>::max())
23 {
24  struct Visitor
25  {
26  bool operator()() const
27  {
28  return true;
29  }
30  } visitor;
31 
32  return lp<RELAX>(graph, edgeCosts, visitor, numberOfIterations);
33 }
34 
35 template<typename RELAX, typename GRAPH, typename ECA, typename VIS>
36 inline
37 std::vector<double> lp(const GRAPH& graph, const ECA& edgeCosts, VIS& visitor, std::size_t numberOfIterations = std::numeric_limits<std::size_t>::max())
38 {
39  const double tolerance = std::numeric_limits<float>::epsilon();
40 
41  RELAX lp;
42 
43  std::deque<std::size_t> path;
44  std::vector<double> vars(graph.numberOfEdges());
45  std::vector<double> variables(graph.numberOfEdges());
46  std::vector<double> coefficients(graph.numberOfEdges());
47  std::vector<double> distances(graph.numberOfVertices());
48  std::vector<std::size_t> parents(graph.numberOfVertices());
49 
50  auto addCycleInequalities = [&] ()
51  {
52  for (std::size_t i = 0; i < vars.size(); ++i)
53  vars[i] = std::max(.0, lp.variableValue(i));
54  // although in Gurobi we constrain the variables to be in the [0,1] range,
55  // sometimes Gurobi finds sligthly negative solutions of the order of 1e-13.
56  // The latter totally screws up Dijkstra's shortest path algorithm
57 
58  std::size_t counter = 0;
59 
60  for (ptrdiff_t edge = 0; edge < graph.numberOfEdges(); ++edge)
61  {
62  auto v0 = graph.vertexOfEdge(edge, 0);
63  auto v1 = graph.vertexOfEdge(edge, 1);
64 
65  // search for shortest path
66  double distance;
67  spsp(graph, DefaultSubgraphMask<>(), v0, v1, vars.begin(), path, distance, distances.begin(), parents.begin());
68 
69  if (vars[edge] > distance + tolerance)
70  {
71  // add inequality
72  auto sz = path.size();
73 
74  for (std::size_t j = 0; j < sz - 1; ++j)
75  {
76  variables[j] = static_cast<double>(graph.findEdge(path[j], path[j + 1]).second);
77  coefficients[j] = 1.0;
78  }
79 
80  variables[sz-1] = static_cast<double>(edge);
81  coefficients[sz-1] = -1.0;
82 
83  lp.addConstraint(variables.begin(), variables.begin() + sz, coefficients.begin(), .0, std::numeric_limits<double>::infinity());
84 
85  ++counter;
86  }
87  }
88 
89  return counter;
90  };
91 
92  lp.initModel(graph.numberOfEdges(), edgeCosts.data());
93 
94  for (std::size_t i = 0; numberOfIterations == 0 || i < numberOfIterations; ++i)
95  {
96  if (!visitor())
97  break;
98 
99  lp.optimize();
100 
101  if (addCycleInequalities() == 0)
102  break;
103  }
104 
105  std::vector<double> edge_values(graph.numberOfEdges());
106 
107  for (size_t i = 0; i < graph.numberOfEdges(); ++i)
108  edge_values[i] = lp.variableValue(i);
109 
110  return edge_values;
111 }
112 
113 }
114 } // namespace graph
115 } // namespace andres
116 
117 #endif // #ifndef ANDRES_GRAPH_MULTICUT_LP_HXX