OpenGM  2.3.x
Discrete Graphical Model Library
smooth_nesterov.hxx
Go to the documentation of this file.
1 /*
2  * smooth_nesterov.hxx
3  *
4  * Created on: Dec 23, 2013
5  * Author: bsavchyn
6  */
7 
8 #ifndef SMOOTH_NESTEROV_HXX_
9 #define SMOOTH_NESTEROV_HXX_
14 
15 namespace opengm{
16 
17 template<class GM>
19 
20 {
21  typedef typename GM::ValueType ValueType;
30 
31  static GradientStepType getGradientStepType(const std::string& name)
32  {
33  if (name.compare("WC_STEP")==0) return WC_STEP;
34  else if (name.compare("JOJIC_STEP")==0) return JOJIC_STEP;
35  else return ADAPTIVE_STEP;
36  }
37 
38  static std::string getString(GradientStepType steptype)
39  {
40  switch (steptype)
41  {
42  case ADAPTIVE_STEP: return std::string("ADAPTIVE_STEP");
43  case WC_STEP : return std::string("WC_STEP");
44  case JOJIC_STEP : return std::string("JOJIC_STEP");
45  default: return std::string("UNKNOWN");
46  }
47  }
48 
49  Nesterov_Parameter(size_t numOfExternalIterations=0,
50  ValueType precision=1.0,
51  bool absolutePrecision=true,
52  size_t numOfInternalIterations=3,
54  ValueType smoothingGapRatio=4,
55  ValueType startSmoothingValue=0.0,
56  ValueType primalBoundPrecision=std::numeric_limits<ValueType>::epsilon(),
58  size_t presolveMaxIterNumber=100,
59  bool canonicalNormalization=true,
62  ValueType smoothingDecayMultiplier=-1.0,
64  bool fastComputations=true,
65  bool verbose=false,
66  GradientStepType gradientStep=ADAPTIVE_STEP,
67  ValueType gamma0=1e6,
68  bool plainGradient=false
69  )
70  :parent(numOfExternalIterations,
71  precision,
72  absolutePrecision,
73  numOfInternalIterations,
77  primalBoundPrecision,
79  presolveMaxIterNumber,
86  verbose
87  ),
88  gradientStep_(gradientStep),
89  gamma0_(gamma0),
90  plainGradient_(plainGradient)
91  {};
92 
94  ValueType gamma0_;
96 
97 #ifdef TRWS_DEBUG_OUTPUT
98  void print(std::ostream& fout)const
99  {
100  parent::print(fout);
101  fout << "gradientStep_="<<getString(gradientStep_)<<std::endl;
102  fout << "gamma0_=" << gamma0_ <<std::endl;
103  fout << "plainGradient_=" << plainGradient_ <<std::endl;
104  }
105 #endif
106 
107 };
108 
109 
110 template<class GM, class ACC>
111 class NesterovAcceleratedGradient : public trws_base::SmoothingBasedInference<GM, ACC> //Inference<GM, ACC>
112 {
113 public:
115  typedef ACC AccumulationType;
116  typedef GM GraphicalModelType;
118 
119  //typedef std::vector<typename GM::ValueType> DDVectorType;
120 
121  typedef typename parent::Storage Storage;
126 
128 
129 // typedef visitors::ExplicitVerboseVisitor<NesterovAcceleratedGradient<GM, ACC> > VerboseVisitorType;
130 // typedef visitors::ExplicitTimingVisitor <NesterovAcceleratedGradient<GM, ACC> > TimingVisitorType;
131 // typedef visitors::ExplicitEmptyVisitor <NesterovAcceleratedGradient<GM, ACC> > EmptyVisitorType;
132 
136 
137 
138  NesterovAcceleratedGradient(const GraphicalModelType& gm,const Parameter& param
139 #ifdef TRWS_DEBUG_OUTPUT
140  ,std::ostream& fout=std::cout
141 #endif
142  )
143  :
144  parent(gm,param
145 #ifdef TRWS_DEBUG_OUTPUT
146  ,(param.verbose_ ? fout : *OUT::nullstream::Instance())
147 #endif
148  ),
149  _parameters(param),
150  _currentDualVector(_getDualVectorSize(),0.0)
151  {
152 #ifdef TRWS_DEBUG_OUTPUT
153  parent::_fout << "Parameters of the "<< name() <<" algorithm:"<<std::endl;
154  param.print(parent::_fout);
155 #endif
156 
157  if (param.numOfExternalIterations_==0) throw std::runtime_error("NEST: a strictly positive number of iterations must be provided!");
158  };
159 
160  template<class VISITOR>
161  InferenceTermination infer(VISITOR & visitor);
162 
163  std::string name() const{ return "NEST"; }
164  InferenceTermination infer(){EmptyVisitorType visitor; return infer(visitor);};
165 
166 // InferenceTermination marginal(const IndexType varID, IndependentFactorType& out) //const
167 // {
168 // parent::_marginalsTemp.resize(parent::_storage.numberOfLabels(varID));
169 // parent::_sumprodsolver.GetMarginals(varID, parent::_marginalsTemp.begin());
170 // OPENGM_ASSERT(parent::_marginalsTemp.size() == out.size());
171 // out.assign(parent::_storage.masterModel(), &varID, &varID+1, ACC::template neutral<ValueType>());
172 // for (LabelType i=0;i<out.size();++i)
173 // out(i)=parent::_marginalsTemp[i];
174 // }
175 
176 private:
177  ValueType _evaluateGradient(const DDVectorType& point,DDVectorType* pgradient);
178  ValueType _evaluateSmoothObjective(const DDVectorType& point);
179  size_t _getDualVectorSize()const{return parent::_storage.getDDVectorSize();}
180  void _SetDualVariables(const DDVectorType& lambda);
181  ValueType _estimateOmega0()const{return 1;};//TODO: exchange with a reasonable value
182  void _InitSmoothing();//TODO: refactor me
183  void _GradientStep(const DDVectorType& gradient, const DDVectorType& startPoint, DDVectorType& endPoint, ValueType stepsize);
184 
185  ValueType getLipschitzConstant()const;
186 
187  Parameter _parameters;
188  DDVectorType _currentDualVector;
189 };
190 
191 template<class GM, class ACC>
192 typename NesterovAcceleratedGradient<GM,ACC>::ValueType
193 NesterovAcceleratedGradient<GM,ACC>::getLipschitzConstant()const
194 {
195  ValueType result=0;
196  for (IndexType modelId=0;modelId<parent::_storage.numberOfModels();++modelId)
197  result+=(ValueType)parent::_storage.size(modelId);
198 
199  return result/parent::_sumprodsolver.GetSmoothing();
200 }
201 
202 template<class GM, class ACC>
203 typename NesterovAcceleratedGradient<GM,ACC>::ValueType
204 NesterovAcceleratedGradient<GM,ACC>::_evaluateGradient(const DDVectorType& point,DDVectorType* pgradient)
205 {
206  ValueType bound=_evaluateSmoothObjective(point);
207  parent::_sumprodsolver.GetMarginalsMove();
208  std::vector<ValueType> buffer1st;
209  std::vector<ValueType> buffer;
210  //transform marginals to dual vector
211  pgradient->resize(_currentDualVector.size());
212  typename DDVectorType::iterator gradientIt=pgradient->begin();
213  for (IndexType varId=0;varId<parent::_storage.masterModel().numberOfVariables();++varId)// all variables
214  {
215  const typename Storage::SubVariableListType& varList=parent::_storage.getSubVariableList(varId);
216 
217  if (varList.size()==1) continue;
218  typename Storage::SubVariableListType::const_iterator modelIt=varList.begin();
219  IndexType firstModelId=modelIt->subModelId_;
220  IndexType firstModelVariableId=modelIt->subVariableId_;
221  buffer1st.resize(parent::_storage.masterModel().numberOfLabels(varId));
222  buffer.resize(parent::_storage.masterModel().numberOfLabels(varId));
223  parent::_sumprodsolver.GetMarginalsForSubModel(firstModelId,firstModelVariableId,buffer1st.begin());
224  ++modelIt;
225  for(;modelIt!=varList.end();++modelIt) //all related models
226  {
227  parent::_sumprodsolver.GetMarginalsForSubModel(modelIt->subModelId_,modelIt->subVariableId_,buffer.begin());
228  gradientIt=std::transform(buffer.begin(),buffer.end(),buffer1st.begin(),gradientIt,std::minus<ValueType>());
229  }
230  }
231 
232  return bound;
233 }
234 
235 
236 template<class GM, class ACC>
237 typename NesterovAcceleratedGradient<GM,ACC>::ValueType
238 NesterovAcceleratedGradient<GM,ACC>::_evaluateSmoothObjective(const DDVectorType& point)
239 {
240  _SetDualVariables(point);
241  parent::_sumprodsolver.ForwardMove();
242  return parent::_sumprodsolver.bound();
243 }
244 
245 template<class GM, class ACC>
246 void NesterovAcceleratedGradient<GM,ACC>::_SetDualVariables(const DDVectorType& lambda)
247 {
248  DDVectorType delta(_currentDualVector.size());
249  std::transform(lambda.begin(),lambda.end(),_currentDualVector.begin(),delta.begin(),std::minus<ValueType>());
250  _currentDualVector=lambda;
251  parent::_storage.addDDvector(delta);
252 };
253 
254 template<class GM, class ACC>
255 void NesterovAcceleratedGradient<GM,ACC>::_InitSmoothing()
256 {
257  if (_parameters.smoothing_ > 0.0)
258  parent::_sumprodsolver.SetSmoothing(_parameters.smoothing_);
259  else
260  throw std::runtime_error("NesterovAcceleratedGradient::_InitSmoothing(): Error! Automatic smoothing selection is not implemented yet.");
261 };
262 
263 template<class GM, class ACC>
264 void NesterovAcceleratedGradient<GM,ACC>::_GradientStep(const DDVectorType& gradient, const DDVectorType& startPoint, DDVectorType& endPoint, ValueType stepsize)
265 {
267  std::transform(gradient.begin(),gradient.end(),endPoint.begin(),std::bind1st(std::multiplies<ValueType>(),stepsize));
268  std::transform(startPoint.begin(),startPoint.end(),endPoint.begin(),endPoint.begin(),std::plus<ValueType>());
269 }
270 
271 template<class GM, class ACC>
272 template<class VISITOR>
274 {
276 
277  size_t counter=0;
278  visitor.addLog("primalLPbound");
279  visitor.addLog("oracleCalls");
280  visitor.begin();
281 
282  if (parent::_sumprodsolver.GetSmoothing()<=0.0)
283  {
284 
285  parent::_maxsumsolver.ForwardMove();
286  parent::_maxsumsolver.EstimateIntegerLabelingAndBound();
287  parent::_SelectOptimalBoundsAndLabeling();
288  ++counter;
289 
290  if (parent::_sumprodsolver.CheckDualityGap(parent::value(),parent::bound()))
291  {
292  #ifdef TRWS_DEBUG_OUTPUT
293  parent::_fout << "NesterovAcceleratedGradient::_CheckStoppingCondition(): Precision attained! Problem solved!"<<std::endl;
294  #endif
295 
296  return NORMAL;
297  }
298 
299  counter+=parent::_EstimateStartingSmoothing(visitor);
300 
301  }else
302  {
303  parent::_sumprodsolver.SetSmoothing(_parameters.startSmoothingValue());
304  }
305 
306  DDVectorType gradient(_currentDualVector.size()),
307  lambda(_currentDualVector.size()),
308  y(_currentDualVector),
309  v(_currentDualVector);
310  DDVectorType w(_currentDualVector.size());//temp variable
311  ValueType alpha,
312 
313  gamma= _parameters.gamma0_,
314  omega=_estimateOmega0();
315 
316  omega=omega/2.0;
317 
318  for (size_t i=0;i<_parameters.maxNumberOfIterations();++i)
319  {
320 #ifdef TRWS_DEBUG_OUTPUT
321  parent::_fout <<"i="<<i<<std::endl;
322 #endif
323  //gradient step with approximate linear search:
324  ValueType doubledLipschitzConstant=2*getLipschitzConstant();//depends on a smoothing value
325 //===================== begin of internal loop ===========================================
326  for (size_t j=0;j<_parameters.numberOfInternalIterations();++j)
327  {
328  ValueType mul=1.0;
329 
330  counter+=2; ValueType oldObjVal=_evaluateGradient(y,&gradient);
331 #ifdef TRWS_DEBUG_OUTPUT
332  parent::_fout <<"Dual smooth objective ="<<oldObjVal<<std::endl;
333 #endif
334 
335  switch (_parameters.gradientStep_)
336  {
337  case Parameter::ADAPTIVE_STEP:
338  {
339  ValueType norm2=std::inner_product(gradient.begin(),gradient.end(),gradient.begin(),(ValueType)0);//squared L2 norm
340  ValueType newObjVal;
341 
342  omega/=4.0;
343 
344  do
345  {
346  omega*=2.0;
347  ACC::iop(-1.0,1.0,mul);
348  _GradientStep(gradient,y,lambda,mul/omega);
349 
350  //newObjVal=_evaluateSmoothObjective(lambda,((j+1)==_parameters.numberOfInternalIterations()));
351  ++counter; newObjVal=_evaluateSmoothObjective(lambda);
352  }
353  while ( ACC::bop(newObjVal,(ValueType)(oldObjVal+mul*norm2/2.0/omega)) && (omega < doubledLipschitzConstant));//TODO: +/- and >/< depending on ACC
354  ++counter; parent::_sumprodsolver.GetMarginalsAndDerivativeMove();
355 
356  if (omega >= doubledLipschitzConstant)
357  {
358 #ifdef TRWS_DEBUG_OUTPUT
359  parent::_fout << "Step size is smaller then the inverse Lipschitz constant. Passing to smoothing update." <<std::endl;
360 #endif
361  }
362  }
363  break;
364 
365  case Parameter::WC_STEP:
366  omega=doubledLipschitzConstant/2.0;
367  _GradientStep(gradient,y,lambda,mul/omega);
368  break;
369 
370  case Parameter::JOJIC_STEP:
371  omega=1.0/parent::_sumprodsolver.GetSmoothing();
372  _GradientStep(gradient,y,lambda,mul/omega);
373  break;
374  default:
375  std::runtime_error("NesterovAcceleratedGradient::infer():Error! Unknown value of the step size selector _parameters.gradientStep_.");
376  break;
377  }//switch
378 
379 
380  if (!_parameters.plainGradient_)
381  {
382  //updating parameters
383  alpha=(sqrt(gamma*gamma+4*omega*gamma)-gamma)/omega/2.0;
384  gamma*=(1-alpha);
385  //v+=(alpha/gamma)*gradient;
386  trws_base::transform_inplace(gradient.begin(),gradient.end(),std::bind1st(std::multiplies<ValueType>(),mul*alpha/gamma));
387  std::transform(v.begin(),v.end(),gradient.begin(),v.begin(),std::plus<ValueType>());
388 
389  //y=alpha*v+(1-alpha)*lambda;
390  trws_base::transform_inplace(lambda.begin(),lambda.end(),std::bind1st(std::multiplies<ValueType>(),(1-alpha)));
391  std::transform(v.begin(),v.end(),w.begin(),std::bind1st(std::multiplies<ValueType>(),alpha));
392  std::transform(w.begin(),w.end(),lambda.begin(),y.begin(),std::plus<ValueType>());
393  }else //plain gradient algorithm
394  {
395  std::copy(lambda.begin(),lambda.end(),y.begin());
396  }
397 }
398 
399 //=================================== end of internal loop ===============================================
400  parent::_maxsumsolver.ForwardMove();//initializes a move, makes a forward move and computes the dual bound, is used also in derivative computation in the next line
401  parent::_maxsumsolver.EstimateIntegerLabelingAndBound();
402  ++counter;
403 #ifdef TRWS_DEBUG_OUTPUT
404  parent::_fout << "_maxsumsolver.bound()=" <<parent::_maxsumsolver.bound()<<", _maxsumsolver.value()=" <<parent::_maxsumsolver.value() <<std::endl;
405 #endif
406 
407  ValueType derivative=parent::_EstimateRhoDerivative();
408 #ifdef TRWS_DEBUG_OUTPUT
409  parent::_fout << "derivative="<<derivative<<std::endl;
410 #endif
411 
412  InferenceTermination returncode;
413  if ( parent::_CheckStoppingCondition(&returncode))
414  {
415  visitor();
416  //std::cout << "counter=" << counter <<std::endl;
417  visitor.log("oracleCalls",(double)counter);
418  visitor.log("primalLPbound",(double)parent::_bestPrimalLPbound);
419  visitor.end();
420  return NORMAL;
421  }
422 
423  size_t flag=visitor();
424  //std::cout << "counter=" << counter <<std::endl;
425  visitor.log("oracleCalls",(double)counter);
426  visitor.log("primalLPbound",(double)parent::_bestPrimalLPbound);
428  break;
429  }
430 
431  parent::_UpdateSmoothing(parent::_bestPrimalBound,parent::_maxsumsolver.bound(),parent::_sumprodsolver.bound(),derivative,i+1);
432 
433 
434  }
435  //update smoothing
436  parent::_SelectOptimalBoundsAndLabeling();
437  visitor();
438  visitor.log("oracleCalls",(double)counter);
439  visitor.log("primalLPbound",(double)parent::_bestPrimalLPbound);
440  visitor.end();
441 
442  return NORMAL;
443 }
444 
445 //template<class GM>
446 //class NesterovAcceleratedGradient<GM, opengm::Integrator>
447 //{
448 //public:
449 // typedef GM GraphicalModelType;
450 // OPENGM_GM_TYPE_TYPEDEFS;
451 // typedef NesterovAcceleratedGradient <GM, opengm::Maximizer> parent;
452 // typedef typename parent::AccumulationType AccumulationType;
453 //
454 // typedef typename parent::Parameter Parameter;
455 // typedef typename parent::Storage Storage;
456 // typedef typename Storage::DDVectorType DDVectorType;
457 // typedef typename parent::SumProdSolver SumProdSolver;
458 // typedef typename parent::MaxSumSolver MaxSumSolver;
459 // typedef typename parent::PrimalBoundEstimator PrimalBoundEstimator;
460 //
461 // typedef typename parent::VerboseVisitorType VerboseVisitorType;
462 // typedef typename parent::TimingVisitorType TimingVisitorType;
463 // typedef typename parent::EmptyVisitorType EmptyVisitorType;
464 //
465 // NesterovAcceleratedGradient(const GraphicalModelType& gm, const Parameter& param
466 // #ifdef TRWS_DEBUG_OUTPUT
467 // ,std::ostream& fout=std::cout
468 // #endif
469 // )
470 // {
471 // Parameter param1=param;
472 // param1.smoothingStrategy() = Parameter::SmoothingParametersType::FIXED;
473 // param1.setStartSmoothingValue(1.0);
474 //
475 // _pparent = new parent(gm,param1
476 // #ifdef TRWS_DEBUG_OUTPUT
477 // ,fout
478 // #endif
479 // );
480 // }
481 // virtual ~NesterovAcceleratedGradient(){delete _pparent;}
482 //
483 // std::string name() const{ return "NEST"; }
484 //
485 // InferenceTermination marginal(const IndexType varID, IndependentFactorType& out) const
486 // {
487 // return _pparent->marginal(varID,out);
488 // }
489 //
490 // template<class VISITOR>
491 // InferenceTermination infer(VISITOR & visitor){return _pparent->infer(visitor);};
492 //
493 // InferenceTermination infer(){return _pparent->infer();}
494 //
495 //private:
496 // parent* _pparent;
497 //};
498 
499 
500 }//namespace opengm
501 
502 #endif /* SMOOTH_NESTEROV_HXX_ */
The OpenGM namespace.
Definition: config.hxx:43
parent::MaxSumSolverParametersType MaxSumSolverParametersType
GradientStepType gradientStep_
Nesterov_Parameter< GM > Parameter
NesterovAcceleratedGradient(const GraphicalModelType &gm, const Parameter &param)
static std::string getString(GradientStepType steptype)
visitors::VerboseVisitor< NesterovAcceleratedGradient< GM, ACC > > VerboseVisitorType
static GradientStepType getGradientStepType(const std::string &name)
void log(const std::string &logName, double value)
visitors::EmptyVisitor< NesterovAcceleratedGradient< GM, ACC > > EmptyVisitorType
parent::SumProdSolverParametersType SumProdSolverParametersType
trws_base::DecompositionStorage< GM > Storage
parent::PrimalBoundEstimator PrimalBoundEstimator
parent::PrimalLPEstimatorParametersType PrimalLPEstimatorParametersType
InputIterator transform_inplace(InputIterator first, InputIterator last, UnaryOperator op)
Definition: utilities2.hxx:79
parent::SmoothingStrategyType SmoothingStrategyType
parent::SmoothingParametersType SmoothingParametersType
Nesterov_Parameter(size_t numOfExternalIterations=0, ValueType precision=1.0, bool absolutePrecision=true, size_t numOfInternalIterations=3, typename Storage::StructureType decompositionType=Storage::GENERALSTRUCTURE, ValueType smoothingGapRatio=4, ValueType startSmoothingValue=0.0, ValueType primalBoundPrecision=std::numeric_limits< ValueType >::epsilon(), size_t maxPrimalBoundIterationNumber=100, size_t presolveMaxIterNumber=100, bool canonicalNormalization=true, ValueType presolveMinRelativeDualImprovement=0.01, bool lazyLPPrimalBoundComputation=true, ValueType smoothingDecayMultiplier=-1.0, SmoothingStrategyType smoothingStrategy=SmoothingParametersType::ADAPTIVE_DIMINISHING, bool fastComputations=true, bool verbose=false, GradientStepType gradientStep=ADAPTIVE_STEP, ValueType gamma0=1e6, bool plainGradient=false)
std::vector< typename GM::ValueType > DDVectorType
Definition: trws_base.hxx:51
parent::PrimalLPEstimatorParametersType PrimalLPEstimatorParametersType
parent::SumProdSolverParametersType SumProdSolverParametersType
visitors::TimingVisitor< NesterovAcceleratedGradient< GM, ACC > > TimingVisitorType
trws_base::SmoothingBasedInference_Parameter< GM > parent
void addLog(const std::string &logName)
trws_base::SmoothingBasedInference< GM, ACC > parent
parent::MaxSumSolverParametersType MaxSumSolverParametersType
InferenceTermination
Definition: inference.hxx:24