OpenGM  2.3.x
Discrete Graphical Model Library
mplp.hxx
Go to the documentation of this file.
1 #ifndef OPENGM_EXTERNAL_MPLP_HXX_
2 #define OPENGM_EXTERNAL_MPLP_HXX_
3 
4 #include <algorithm>
5 #include <sstream>
6 
12 
13 // mplp logic
14 #include <cycle.h>
15 #undef eps
16 #undef Inf
17 
18 namespace opengm {
19 
20 namespace external {
21 
27 // MPLP
33 template<class GM>
34 class MPLP : public Inference<GM, opengm::Minimizer> {
35 public:
36  typedef GM GraphicalModelType;
42 
43  struct Parameter {
46  //const size_t maxTightIter = 1000000,
47  //const size_t numIter = 1000,
48  //const size_t numIterLater = 20,
49  const size_t maxIterLP = 1000,
50  const size_t maxIterTight = 100000,
51  const size_t maxIterLater = 20,
52  const double maxTime = 3600,
53  const double maxTimeLP = 1200,
54  const size_t numClusToAddMin = 5,
55  const size_t numClusToAddMax = 20,
56  const double objDelThr = 0.0002,
57  const double intGapThr = 0.0002,
58  const bool UAIsettings = false,
59  const bool addEdgeIntersections = true,
60  const bool doGlobalDecoding = false,
61  const bool useDecimation=false,
62  const bool lookForCSPs = false,
63  //const double infTime = 0.0,
64  const bool logMode = false,
65  const std::string& logFile = std::string(),
66  const int seed = 0,
67  const std::string& inputFile = std::string(),
68  const std::string& evidenceFile = std::string()
69  )
70  : //maxTightIter_(maxTightIter),
71  //numIter_(numIter),
72  //numIterLater_(numIterLater),
73  maxIterLP_(maxIterLP),
74  maxIterTight_(maxIterTight),
75  maxIterLater_(maxIterLater),
76  maxTime_(maxTime),
77  maxTimeLP_(maxTimeLP),
78  numClusToAddMin_(numClusToAddMin),
79  numClusToAddMax_(numClusToAddMax),
80  objDelThr_(objDelThr),
81  intGapThr_(intGapThr),
82  UAIsettings_(UAIsettings),
83  addEdgeIntersections_(addEdgeIntersections),
84  doGlobalDecoding_(doGlobalDecoding),
85  useDecimation_(useDecimation),
86  lookForCSPs_(lookForCSPs),
87  //infTime_(infTime),
88  logFile_(logFile),
89  seed_(seed),
90  inputFile_(inputFile),
91  evidenceFile_(evidenceFile)
92  { }
93 
94  // new parameters
95  size_t maxIterLP_; //maximum number of iterrations for the initial LP
96  size_t maxIterTight_; //maximum number of rounds for tightening
97  size_t maxIterLater_; //maximum number of iterrations after each tightening
98  double maxTime_; //overall time limit in seconds
99  double maxTimeLP_; //time limit for the initial LP in seconds
100 
101  //size_t maxTightIter_;
102  //size_t numIter_;
103  //size_t numIterLater_;
106  double objDelThr_;
107  double intGapThr_;
108 
109  // Settings for UAI inference competition override all others
111 
112  // defaults. UAIsettings modulates the value of many of these
117 
118  /* Note:
119  * Setting infTime_ to the total number of seconds allowed to run will
120  * result in global decoding being called once 1/3 through, and (if turned
121  * on) decimation being called 2/3 through (very helpful for CSP intances).
122  */
123  double infTime_;
124 
125  std::string logFile_;
126  int seed_;
127  std::string inputFile_;
128  std::string evidenceFile_;
129  };
130 
131  // construction
132  MPLP(const GraphicalModelType& gm, const Parameter& para = Parameter());
133  // destruction
134  ~MPLP();
135  // query
136  std::string name() const;
137  const GraphicalModelType& graphicalModel() const;
138  // inference
139  template<class VISITOR>
140  InferenceTermination infer(VISITOR & visitor);
142  InferenceTermination arg(std::vector<LabelType>&, const size_t& = 1) const;
143  typename GM::ValueType bound() const;
144  typename GM::ValueType value() const;
145 
146 protected:
147  const GraphicalModelType& gm_;
149 
151  //double mplpTimeLimit_;
152  clock_t mplpStart_;
153  MPLPAlg* mplp_;
154 
155  bool valueCheck() const;
156 };
157 
158 template<class GM>
159 inline MPLP<GM>::MPLP(const GraphicalModelType& gm, const Parameter& para)
160  : gm_(gm), parameter_(para), mplpLogFile_(NULL), mplp_(NULL) {
161 
165  parameter_.lookForCSPs_ = true;
166  }
167 
168  // Log file
169  if(!parameter_.logFile_.empty()) {
170  mplpLogFile_ = fopen(parameter_.logFile_.c_str(), "w");
171  }
172 
173  if (parameter_.maxTime_ <= 0.0) {
174  parameter_.maxTime_ = 3600*24*30; //30 days
175  }
176 
179  }
180 
181  if(MPLP_DEBUG_MODE) {
182  std::cout << "Time limit = " << parameter_.maxTime_ << std::endl;
183  }
184 
185  mplpStart_ = clock();
186 
187  // Load in the MRF and initialize GMPLP state
188  if(!parameter_.inputFile_.empty()) {
189  //mplp_ = new MPLPAlg(mplpStart_, mplpTimeLimit_, parameter_.inputFile_, parameter_.evidenceFile_, mplpLogFile_, parameter_.lookForCSPs_);
191  } else {
192  // fill vectors from opengm model
193  std::vector<int> var_sizes(gm_.numberOfVariables());
194  for(IndexType var = 0; var < gm_.numberOfVariables(); ++var){
195  var_sizes[var] = static_cast<int>(gm_.numberOfLabels(var));
196  }
197 
198  std::vector< std::vector<int> > all_factors(gm_.numberOfFactors());
199  for(IndexType f = 0; f < gm_.numberOfFactors(); ++f){
200  all_factors[f].resize(gm_[f].numberOfVariables());
201  for(IndexType i = 0; i < gm_[f].numberOfVariables(); ++i){
202  all_factors[f][i] = static_cast<int>(gm_[f].variableIndex(i));
203  }
204  }
205 
206  std::vector< std::vector<double> > all_lambdas(gm_.numberOfFactors());
207  for(IndexType f = 0; f < gm_.numberOfFactors(); ++f){
208  all_lambdas[f].resize(gm_[f].size());
209  //gm_[f].copyValues(all_lambdas[f].begin());
210 
211  gm_[f].copyValuesSwitchedOrder(all_lambdas[f].begin());
212 
213  // TODO check if value transform (log or exp) is needed
214  for(size_t i = 0; i < all_lambdas[f].size(); i++) {
215  all_lambdas[f][i] = -all_lambdas[f][i];
216  }
217  }
218 
219  //mplp_ = new MPLPAlg(mplpStart_, mplpTimeLimit_, var_sizes, all_factors, all_lambdas, mplpLogFile_, parameter_.lookForCSPs_);
220  mplp_ = new MPLPAlg(mplpStart_, parameter_.maxTime_, var_sizes, all_factors, all_lambdas, mplpLogFile_, parameter_.lookForCSPs_);
221  }
222 }
223 
224 template<class GM>
225 inline MPLP<GM>::~MPLP() {
226  if(mplp_) {
227  delete mplp_;
228  }
229 }
230 
231 template<class GM>
232 inline std::string MPLP<GM>::name() const {
233  return "MPLP";
234 }
235 
236 template<class GM>
238  return gm_;
239 }
240 
241 template<class GM>
243  EmptyVisitorType visitor;
244  return this->infer(visitor);
245 }
246 
247 template<class GM>
248 template<class VISITOR>
249 inline InferenceTermination MPLP<GM>::infer(VISITOR & visitor) {
250  visitor.begin(*this);
251 
252  bool decimation_has_started = false;
253  bool force_decimation = false;
254  bool prevGlobalDecodingWas1 = true;
255 
256  // Keep track of triplets added so far
257  std::map<std::vector<int>, bool> triplet_set;
258 
259  //if(MPLP_DEBUG_MODE) std::cout << "Random seed = " << parameter_.seed_ << std::endl;
260  srand(parameter_.seed_);
261 /*
262  if(!parameter_.logFile_.empty()) {
263  std::stringstream stream;
264  stream << "I niter=" << parameter_.numIter_ << ", niter_later=" << parameter_.numIterLater_ << ", nclus_to_add_min=" << parameter_.numClusToAddMin_ << ", nclus_to_add_max=" << parameter_.numClusToAddMax_ << ", obj_del_thr=" << parameter_.objDelThr_ << ", int_gap_thr=" << parameter_.intGapThr_ << "\n";
265  fprintf(mplpLogFile_, "%s", stream.str().c_str());
266  }
267 
268  if (MPLP_DEBUG_MODE) {
269  std::cout << "niter=" << parameter_.numIter_ << "\nniter_later=" << parameter_.numIterLater_ << "\nnclus_to_add=" << parameter_.numClusToAddMin_ << "\nobj_del_thr=" << parameter_.objDelThr_ << "\nint_gap_thr=" << parameter_.intGapThr_ << "\n";
270  std::cout << "Initially running MPLP for " << parameter_.numIter_ << " iterations\n";
271  }
272 */
273  double value_old;
274  for (size_t i=0; i<=parameter_.maxIterLP_;++i){
275  value_old = mplp_->last_obj;
276  mplp_->RunMPLP(1, parameter_.objDelThr_, parameter_.intGapThr_);
277  if( visitor(*this) != visitors::VisitorReturnFlag::ContinueInf ){
278  if(!parameter_.logFile_.empty()) fflush(mplpLogFile_);
279  if(!parameter_.logFile_.empty()) fclose(mplpLogFile_);
280  visitor.end(*this);
281  return NORMAL;
282  }
283  if(((double)(clock() - mplpStart_) / CLOCKS_PER_SEC) > parameter_.maxTimeLP_){
284  std::cout << "stop because of timelimit for LP switching to tightening" <<std::endl;
285  break;
286  }
287  if(((double)(clock() - mplpStart_) / CLOCKS_PER_SEC) > parameter_.maxTime_){
288  std::cout << "stop because of timelimit" <<std::endl;
289  break;
290  }
291  if (std::fabs(value_old- mplp_->last_obj)<parameter_.objDelThr_ && i > 16){
292  std::cout << "stop because small progress" <<std::endl;
293  break;
294  }
295  if(std::fabs(value()-bound())<parameter_.intGapThr_){
296  std::cout << "stop because small gap" <<std::endl;
297  break;
298  }
299  }
300 
301  for(size_t iter=1; iter<=parameter_.maxIterTight_; iter++){ // Break when problem is solved
302  // if(!parameter_.logFile_.empty()) fflush(mplpLogFile_);
303  // if (MPLP_DEBUG_MODE) std::cout << "\n\nOuter loop iteration " << iter << "\n----------------------\n";
304 
305  // Is problem solved? If so, break.
306  double int_gap = mplp_->last_obj - mplp_->m_best_val;
307  if(int_gap < parameter_.intGapThr_){
308  if (MPLP_DEBUG_MODE) std::cout << "Done! Integrality gap less than " << parameter_.intGapThr_ << "\n";
309  break;
310  }
311  double time_elapsed = (double)(clock() - mplpStart_)/ CLOCKS_PER_SEC;
312  if (time_elapsed > parameter_.maxTime_) {
313  break; // terminates if alreay running past time limit (this should be very conservative)
314  }
315 
316  // Heuristic: when the integrality gap is sufficiently small, allow the algorithm
317  // more time to run till convergence
318 
319  if(int_gap < 1){
320  parameter_.maxIterLater_ = std::max(parameter_.maxIterLater_, static_cast<size_t>(600)); // TODO opt: don't hard code
321  parameter_.objDelThr_ = std::min(parameter_.objDelThr_, 1e-5);
322  if (MPLP_DEBUG_MODE) std::cout << "Int gap small, so setting niter_later to " << parameter_.maxIterLater_ << " and obj_del_thr to " << parameter_.objDelThr_ << "\n";
323  }
324 
325  // Keep track of global decoding time and run this frequently, but at most 20% of total runtime
326  if(parameter_.doGlobalDecoding_ && (((double)clock() - mplp_->last_global_decoding_end_time)/CLOCKS_PER_SEC >= mplp_->last_global_decoding_total_time*4)) {
327  // Alternate between global decoding methods
328  if(prevGlobalDecodingWas1) {
329  mplp_->RunGlobalDecoding(false);
330  prevGlobalDecodingWas1 = false;
331  } else {
332  mplp_->RunGlobalDecoding2(false);
333  prevGlobalDecodingWas1 = true;
334  }
335  }
336 
337  // Tighten LP
338  if (MPLP_DEBUG_MODE) std::cout << "Now attempting to tighten LP relaxation..." << std::endl;
339 
340  clock_t tightening_start_time = clock();
341  double bound=0; double bound2 = 0;
342  int nClustersAdded = 0;
343 
344  nClustersAdded += TightenTriplet(*mplp_, parameter_.numClusToAddMin_, parameter_.numClusToAddMax_, triplet_set, bound);
345  nClustersAdded += TightenCycle(*mplp_, parameter_.numClusToAddMin_, triplet_set, bound2, 1);
346 
347  if(std::max(bound, bound2) < CLUSTER_THR) {
348  if(MPLP_DEBUG_MODE) std::cout << "TightenCycle did not find anything useful! Re-running with FindPartition." << std::endl;
349 
350  nClustersAdded += TightenCycle(*mplp_, parameter_.numClusToAddMin_, triplet_set, bound2, 2);
351  }
352 
353  // Check to see if guaranteed bound criterion was non-trivial.
354  // TODO: these bounds are not for the cycles actually added (since many of the top ones are skipped, already being in the relaxation). Modify it to be so.
355  bool noprogress = false;
356  if(std::max(bound, bound2) < CLUSTER_THR) noprogress = true;
357 
358  clock_t tightening_end_time = clock();
359  double tightening_total_time = (double)(tightening_end_time - tightening_start_time)/CLOCKS_PER_SEC;
360  if (MPLP_DEBUG_MODE) {
361  std::cout << " -- Added " << nClustersAdded << " clusters to relaxation. Took " << tightening_total_time << " seconds\n";
362  }
363  if(!parameter_.logFile_.empty()) {
364  std::stringstream stream;
365  stream << "I added " << nClustersAdded << " clusters. Took " << tightening_total_time << " seconds\n";
366  fprintf(mplpLogFile_, "%s", stream.str().c_str());
367  }
368 
369  // For CSP instances, 2/3 through run time, start decimation -- OR, when no progress being made
370  if((mplp_->CSP_instance || noprogress) && ((double)(clock() - mplpStart_) / CLOCKS_PER_SEC) > parameter_.maxTimeLP_ + (parameter_.maxTime_-parameter_.maxTimeLP_)/2){
371  force_decimation = true;
372  }
373  /*
374  We have done as much as we can with the existing edge intersection sets. Now
375  add in all new edge intersection sets for large clusters.
376  */
377  if(nClustersAdded == 0 && parameter_.addEdgeIntersections_) {
378  mplp_->AddAllEdgeIntersections();
379  parameter_.addEdgeIntersections_ = false; // only makes sense to run this code once
380  }
381 
382  // Not able to tighten relaxation further, so try to see if decoding is the problem
383  // Do not run this too often!
384  else if((!parameter_.addEdgeIntersections_ && nClustersAdded == 0) || force_decimation) {
385  // Do one last push to try to find the global assignment!
386  if(parameter_.doGlobalDecoding_ && (!parameter_.useDecimation_ || !decimation_has_started)) mplp_->RunGlobalDecoding3();
387 
388  // Do one step of decimation
389  if (parameter_.useDecimation_) {
390  decimation_has_started = true;
391 
392  bool fixed_node = mplp_->RunDecimation();
393  if(!fixed_node) {
394  if(MPLP_DEBUG_MODE) std::cout << "Decimation fixed all of the nodes it could... quiting." << std::endl;
395  break;
396  }
397  }
398  }
399 
400  if (MPLP_DEBUG_MODE) std::cout << "Running MPLP again for " << parameter_.maxIterLater_ << " more iterations\n";
401  mplp_->RunMPLP(parameter_.maxIterLater_, parameter_.objDelThr_, parameter_.intGapThr_);
402 
403  if(parameter_.UAIsettings_) {
404  // For UAI competition: time limit can be up to 1 hour, so kill process if still running.
405  //double time_elapsed, /*time,*/ time_limit;
406  double time_elapsed = (double)(clock() - mplpStart_)/ CLOCKS_PER_SEC;
407  if (time_elapsed > 4000 && time_elapsed > parameter_.maxTime_) {
408  break; // terminates if alreay running past time limit (this should be very conservative)
409  }
410  }
411 
412  if(!parameter_.logFile_.empty()) fflush(mplpLogFile_);
413  if( visitor(*this) != visitors::VisitorReturnFlag::ContinueInf ){
414  break;
415  }
416  }
417 
418  if(!parameter_.logFile_.empty()) fflush(mplpLogFile_);
419  if(!parameter_.logFile_.empty()) fclose(mplpLogFile_);
420 
421 
422  visitor.end(*this);
423  return NORMAL;
424 }
425 
426 template<class GM>
427 inline InferenceTermination MPLP<GM>::arg(std::vector<LabelType>& arg, const size_t& n) const {
428  if(n > 1) {
429  return UNKNOWN;
430  }
431  else {
432  if(parameter_.inputFile_.empty()) {
433  OPENGM_ASSERT(mplp_->m_decoded_res.size() == gm_.numberOfVariables());
434  }
435 
436  arg.resize(mplp_->m_decoded_res.size());
437  for(size_t i = 0; i < arg.size(); i++) {
438  arg[i] = static_cast<LabelType>(mplp_->m_decoded_res[i]);
439  }
440  return NORMAL;
441  }
442 }
443 
444 template<class GM>
445 inline typename GM::ValueType MPLP<GM>::bound() const {
446  return -mplp_->last_obj;
447  //return -mplp_->m_best_val;
448 }
449 
450 template<class GM>
451 inline typename GM::ValueType MPLP<GM>::value() const {
452  std::vector<LabelType> state;
453  arg(state);
454  return gm_.evaluate(state);
455  // -mplp_->m_best_val is the best value so far and not the value of the current configuration!
456 
457  //OPENGM_ASSERT(valueCheck());
458  //return -mplp_->m_best_val;
459 }
460 
461 template<class GM>
462 inline bool MPLP<GM>::valueCheck() const {
463  if(!parameter_.inputFile_.empty()) {
464  return true;
465  } else {
466  static bool visited = false;
467  if(visited) {
468  std::vector<LabelType> state;
469  arg(state);
470  if(fabs(-mplp_->m_best_val - gm_.evaluate(state)) < OPENGM_FLOAT_TOL) {
471  return true;
472  } else {
473  std::cout << "state: ";
474  for(size_t i = 0; i < state.size(); i++) {
475  std::cout << state[i] << "; ";
476  }
477  std::cout << std::endl;
478 
479  std::cout << "value: " << -mplp_->m_best_val << std::endl;
480  std::cout << "expected: " << gm_.evaluate(state) << std::endl;
481  return false;
482  }
483  } else {
484  visited = true;
485  return true;
486  }
487  }
488 }
489 
490 } // namespace external
491 
492 } // namespace opengm
493 
494 #endif /* OPENGM_EXTERNAL_MPLP_HXX_ */
#define OPENGM_FLOAT_TOL
The OpenGM namespace.
Definition: config.hxx:43
GM::ValueType value() const
return the solution (value)
Definition: mplp.hxx:451
std::string name() const
Definition: mplp.hxx:232
InferenceTermination arg(std::vector< LabelType > &, const size_t &=1) const
Definition: mplp.hxx:427
#define OPENGM_ASSERT(expression)
Definition: opengm.hxx:77
InferenceTermination infer()
Definition: mplp.hxx:242
GM::ValueType bound() const
return a bound on the solution
Definition: mplp.hxx:445
GraphicalModelType::IndexType IndexType
Definition: inference.hxx:40
bool valueCheck() const
Definition: mplp.hxx:462
visitors::TimingVisitor< MPLP< GM > > TimingVisitorType
Definition: mplp.hxx:41
MPLP MPLP inference algorithm class.
Definition: mplp.hxx:34
const GraphicalModelType & graphicalModel() const
Definition: mplp.hxx:237
visitors::VerboseVisitor< MPLP< GM > > VerboseVisitorType
Definition: mplp.hxx:39
Inference algorithm interface.
Definition: inference.hxx:34
opengm::Minimizer AccumulationType
Definition: mplp.hxx:37
visitors::EmptyVisitor< MPLP< GM > > EmptyVisitorType
Definition: mplp.hxx:40
Parameter parameter_
Definition: mplp.hxx:148
MPLP(const GraphicalModelType &gm, const Parameter &para=Parameter())
Definition: mplp.hxx:159
const GraphicalModelType & gm_
Definition: mplp.hxx:147
GraphicalModelType::LabelType LabelType
Definition: inference.hxx:39
Minimization as a unary accumulation.
Definition: minimizer.hxx:12
Parameter(const size_t maxIterLP=1000, const size_t maxIterTight=100000, const size_t maxIterLater=20, const double maxTime=3600, const double maxTimeLP=1200, const size_t numClusToAddMin=5, const size_t numClusToAddMax=20, const double objDelThr=0.0002, const double intGapThr=0.0002, const bool UAIsettings=false, const bool addEdgeIntersections=true, const bool doGlobalDecoding=false, const bool useDecimation=false, const bool lookForCSPs=false, const bool logMode=false, const std::string &logFile=std::string(), const int seed=0, const std::string &inputFile=std::string(), const std::string &evidenceFile=std::string())
Constructor.
Definition: mplp.hxx:45
InferenceTermination
Definition: inference.hxx:24