00001 #pragma once
00002 #ifndef OPENGM_ASTAR_HXX
00003 #define OPENGM_ASTAR_HXX
00004
00005 #include <cmath>
00006 #include <vector>
00007 #include <list>
00008 #include <set>
00009 #include <iostream>
00010 #include <algorithm>
00011 #include <iostream>
00012 #include <functional>
00013
00014 #include "opengm/opengm.hxx"
00015 #include "opengm/graphicalmodel/graphicalmodel.hxx"
00016 #include "opengm/inference/inference.hxx"
00017 #include "opengm/inference/messagepassing/messagepassing.hxx"
00018 #include "opengm/inference/visitors/visitor.hxx"
00019
00020 namespace opengm {
00021
00023
00024
00025 template<class FactorType> struct AStarNode {
00026 typename std::vector<typename FactorType::LabelType> conf;
00027 typename FactorType::ValueType value;
00028 };
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00043
00062 template<class GM,class ACC>
00063 class AStar : public Inference<GM,ACC>
00064 {
00065 public:
00067 typedef GM GraphicalModelType;
00068 typedef typename GraphicalModelType::template Rebind<true>::RebindType EditableGraphicalModelType;
00070 typedef ACC AccumulationType;
00071 OPENGM_GM_TYPE_TYPEDEFS;
00073 typedef typename std::vector<LabelType> ConfVec ;
00075 typedef typename ConfVec::iterator ConfVecIt;
00077 typedef VerboseVisitor<AStar<GM, ACC> > VerboseVisitorType;
00078 typedef TimingVisitor<AStar<GM, ACC> > TimingVisitorType;
00079 typedef EmptyVisitor<AStar<GM, ACC> > EmptyVisitorType;
00080
00081 enum Heuristic{
00082 DEFAULT_HEURISTIC = 0,
00083 FAST_HEURISTIC = 1,
00084 STANDARD_HEURISTIC = 2
00085 };
00086 struct Parameter {
00087 Parameter()
00088 {
00089 maxHeapSize_ = 3000000;
00090 numberOfOpt_ = 1;
00091 objectiveBound_ = AccumulationType::template neutral<ValueType>();
00092 heuristic_ = Parameter::DEFAULTHEURISTIC;
00093 };
00095
00098 void addTreeFactorId(size_t id)
00099 { treeFactorIds_.push_back(id); }
00101 static const size_t DEFAULTHEURISTIC = 0;
00103 static const size_t FASTHEURISTIC = 1;
00105 static const size_t STANDARDHEURISTIC = 2;
00107 size_t maxHeapSize_;
00109 size_t numberOfOpt_;
00111 ValueType objectiveBound_;
00117 size_t heuristic_;
00118 std::vector<IndexType> nodeOrder_;
00119 std::vector<size_t> treeFactorIds_;
00120
00121 };
00122 AStar(const GM& gm, Parameter para = Parameter());
00123 virtual std::string name() const {return "AStar";}
00124 const GraphicalModelType& graphicalModel() const;
00125 virtual InferenceTermination infer();
00126 virtual void reset();
00127 template<class VisitorType> InferenceTermination infer(VisitorType& vistitor);
00128 ValueType bound()const {return belowBound_;}
00129 virtual InferenceTermination marginal(const size_t,IndependentFactorType& out)const {return UNKNOWN;}
00130 virtual InferenceTermination factorMarginal(const size_t, IndependentFactorType& out)const {return UNKNOWN;}
00131 virtual InferenceTermination arg(std::vector<LabelType>& v, const size_t = 1)const;
00132 virtual InferenceTermination args(std::vector< std::vector<LabelType> >& v)const;
00133
00134 private:
00135 const GM& gm_;
00136 Parameter parameter_;
00137
00138
00139 Parameter parameterInitial_;
00140 std::vector<AStarNode<IndependentFactorType> > array_;
00141 std::vector<size_t> numStates_;
00142 size_t numNodes_;
00143 std::vector<IndependentFactorType> treeFactor_;
00144 std::vector<IndependentFactorType> optimizedFactor_;
00145 std::vector<ConfVec > optConf_;
00146 std::vector<bool> isTreeFactor_;
00147 ValueType aboveBound_;
00148 ValueType belowBound_;
00149 template<class VisitorType> void expand(VisitorType& vistitor);
00150 std::vector<ValueType> fastHeuristic(ConfVec conf);
00151 inline static bool comp1(const AStarNode<IndependentFactorType>& a, const AStarNode<IndependentFactorType>& b)
00152 {return AccumulationType::ibop(a.value,b.value);};
00153 inline static bool comp2(const AStarNode<IndependentFactorType>& a, const AStarNode<IndependentFactorType>& b)
00154 { return AccumulationType::bop(a.value,b.value);};
00155 inline static ValueType better(ValueType a, ValueType b) {return AccumulationType::op(a,b);};
00156 inline static ValueType wrose(ValueType a, ValueType b) {return AccumulationType::iop(a,b);};
00157 };
00158
00159
00160
00161
00162
00163
00167 template<class GM, class ACC >
00168 AStar<GM,ACC>
00169 ::AStar
00170 (
00171 const GM& gm,
00172 Parameter para
00173 ):gm_(gm)
00174 {
00175 parameterInitial_=para;
00176 parameter_ = para;
00177 if( parameter_.heuristic_ == Parameter::DEFAULTHEURISTIC) {
00178 if(gm_.factorOrder()<=2)
00179 parameter_.heuristic_ = Parameter::FASTHEURISTIC;
00180 else
00181 parameter_.heuristic_ = Parameter::STANDARDHEURISTIC;
00182 }
00183 OPENGM_ASSERT(parameter_.heuristic_ == Parameter::FASTHEURISTIC || parameter_.heuristic_ == Parameter::STANDARDHEURISTIC);
00184 ACC::ineutral(belowBound_);
00185 ACC::neutral(aboveBound_);
00186
00187 isTreeFactor_.resize(gm_.numberOfFactors());
00188 numStates_.resize(gm_.numberOfVariables());
00189 numNodes_ = gm_.numberOfVariables();
00190 for(size_t i=0; i<numNodes_;++i)
00191 numStates_[i] = gm_.numberOfLabels(i);
00192
00193 if(parameter_.nodeOrder_.size()==0) {
00194 parameter_.nodeOrder_.resize(numNodes_);
00195 for(size_t i=0; i<numNodes_; ++i)
00196 parameter_.nodeOrder_[i]=i;
00197 }
00198 if(parameter_.nodeOrder_.size()!=numNodes_)
00199 throw RuntimeError("The node order does not fit to the model.");
00200 OPENGM_ASSERT(std::set<size_t>(parameter_.nodeOrder_.begin(), parameter_.nodeOrder_.end()).size()==numNodes_);
00201 for(size_t i=0;i<numNodes_; ++i) {
00202 OPENGM_ASSERT(parameter_.nodeOrder_[i] < numNodes_);
00203 OPENGM_ASSERT(parameter_.nodeOrder_[i] >= 0);
00204 }
00205
00206 if(parameter_.treeFactorIds_.size()==0) {
00207
00208 for(size_t i=0; i<gm_.numberOfFactors(); ++i) {
00209 if((gm_[i].numberOfVariables()==2) &&
00210 (gm_[i].variableIndex(0)==parameter_.nodeOrder_.back() || gm_[i].variableIndex(1)==parameter_.nodeOrder_.back())
00211 )
00212 parameter_.addTreeFactorId(i);
00213 }
00214 }
00215 for(size_t i=0; i<parameter_.treeFactorIds_.size(); ++i)
00216 OPENGM_ASSERT(gm_.numberOfFactors() > parameter_.treeFactorIds_[i]);
00217
00218 optimizedFactor_.resize(gm_.numberOfFactors());
00219 for(size_t i=0; i<gm_.numberOfFactors(); ++i) {
00220 if(gm_[i].numberOfVariables()<=1) continue;
00221 std::vector<size_t> index(gm_[i].numberOfVariables());
00222 gm_[i].variableIndices(index.begin());
00223 optimizedFactor_[i].assign(gm_ ,index.end()-1, index.end());
00224 opengm::accumulate<ACC>(gm[i],index.begin()+1,index.end(),optimizedFactor_[i]);
00225
00226 OPENGM_ASSERT(optimizedFactor_[i].numberOfVariables() == 1);
00227 OPENGM_ASSERT(optimizedFactor_[i].variableIndex(0) == index[0]);
00228 }
00229
00230 AStarNode<IndependentFactorType> a;
00231 a.conf.resize(0);
00232 a.value = 0;
00233 array_.push_back(a);
00234 make_heap(array_.begin(), array_.end(), comp1);
00235
00236 if(parameter_.heuristic_ == parameter_.FASTHEURISTIC) {
00237 for(size_t i=0; i<parameter_.treeFactorIds_.size(); ++i) {
00238 if(gm_[parameter_.treeFactorIds_[i]].numberOfVariables() > 2) {
00239 throw RuntimeError("The heuristic includes factor of order > 2.");
00240 }
00241 }
00242 }
00243
00244 treeFactor_.clear();
00245 for(size_t i=0; i<gm_.numberOfFactors(); ++i)
00246 isTreeFactor_[i] = false;
00247 for(size_t i=0; i<parameter_.treeFactorIds_.size(); ++i) {
00248 int factorId = parameter_.treeFactorIds_[i];
00249 isTreeFactor_[factorId] = true;
00250 treeFactor_.push_back(gm_[factorId]);
00251 }
00252 }
00253
00260 template<class GM, class ACC >
00261 void
00262 AStar<GM,ACC>::reset()
00263 {
00265 }
00266
00267 template <class GM, class ACC>
00268 InferenceTermination
00269 AStar<GM,ACC>::infer()
00270 {
00271 EmptyVisitorType v;
00272 return infer(v);
00273 }
00274
00277 template<class GM, class ACC>
00278 template<class VisitorType>
00279 InferenceTermination AStar<GM,ACC>::infer(VisitorType& visitor)
00280 {
00281 visitor.begin(*this, ACC::template neutral<ValueType>(),ACC::template ineutral<ValueType>());
00282 optConf_.resize(0);
00283 while(array_.size()>0) {
00284 if(parameter_.numberOfOpt_ == optConf_.size()) {
00285 visitor.end(*this);
00286 return NORMAL;
00287 }
00288 while(array_.front().conf.size() < numNodes_) {
00289 expand(visitor);
00290 visitor(*this, ACC::template neutral<ValueType>(),array_.front().value);
00291
00292 }
00293 ValueType value = array_.front().value;
00294 belowBound_ = value;
00295 std::vector<LabelType> conf(numNodes_);
00296 for(size_t n=0; n<numNodes_; ++n) {
00297 conf[parameter_.nodeOrder_[n]] = array_.front().conf[n];
00298 }
00299 visitor(*this,gm_.evaluate(conf),this->bound());
00300 if(ACC::bop(parameter_.objectiveBound_, value)) {
00301 visitor.end(*this,value,this->bound());
00302 return NORMAL;
00303 }
00304 optConf_.push_back(conf);
00305 pop_heap(array_.begin(), array_.end(), comp1);
00306 array_.pop_back();
00307 }
00308 visitor.end(*this);
00309 return UNKNOWN;
00310 }
00311
00312 template<class GM, class ACC>
00313 InferenceTermination AStar<GM, ACC>
00314 ::arg(ConfVec& conf, const size_t n)const
00315 {
00316 if(n>optConf_.size()) {
00317 conf.resize(0);
00318 return UNKNOWN;
00319 }
00320
00321 conf=optConf_[n-1];
00322 return NORMAL;
00323 }
00324
00329 template<class GM, class ACC>
00330 InferenceTermination AStar<GM,ACC>
00331 ::args(std::vector<std::vector<typename AStar<GM,ACC>::LabelType> >& conf)const
00332 {
00333 conf=optConf_;
00334 return NORMAL;
00335 }
00336
00337 template<class GM, class ACC>
00338 template<class VisitorType>
00339 void AStar<GM, ACC>::expand(VisitorType& visitor)
00340 {
00341
00342 if(array_.size()>parameter_.maxHeapSize_*0.99) {
00343 partial_sort(array_.begin(), array_.begin()+(int)(parameter_.maxHeapSize_/2), array_.end(), comp2);
00344 array_.resize((int)(parameter_.maxHeapSize_/2));
00345 }
00346
00347 AStarNode<IndependentFactorType> a = array_.front();
00348 size_t subconfsize = a.conf.size();
00349
00350 OPENGM_ASSERT(array_.size() > 0);
00351 pop_heap(array_.begin(), array_.end(), comp1);
00352 array_.pop_back();
00353 if( parameter_.heuristic_ == parameter_.STANDARDHEURISTIC) {
00354
00355 EditableGraphicalModelType tgm = gm_;
00356 std::vector<size_t> variableIndices(subconfsize);
00357 std::vector<size_t> values(subconfsize);
00358 for(size_t i =0; i<subconfsize ; ++i) {
00359 variableIndices[i] = parameter_.nodeOrder_[i];
00360 values[i] = a.conf[i];
00361 }
00362 tgm.introduceEvidence(variableIndices.begin(), variableIndices.end(),values.begin());
00363
00364
00365
00366
00367
00368 std::vector<IndexType> varInd;
00369 for(size_t i=0; i<tgm.numberOfFactors(); ++i) {
00370
00371 if(isTreeFactor_[i]) {
00372 continue;
00373 }else{
00374 varInd.clear();
00375 size_t nvar = tgm[i].numberOfVariables();
00376
00377 if(nvar<=1) {
00378 continue;
00379 }
00380 else{
00381 typename GraphicalModelType::FunctionIdentifier id=tgm.addFunction(optimizedFactor_[i].function());
00382 std::vector<IndexType> variablesIndices(optimizedFactor_[i].numberOfVariables());
00383 optimizedFactor_[i].variableIndices(variablesIndices.begin());
00384 OPENGM_ASSERT(variablesIndices.size()==optimizedFactor_[i].numberOfVariables());
00385 tgm.replaceFactor(i,id.functionIndex,variablesIndices.begin(),variablesIndices.end());
00386 OPENGM_ASSERT(tgm[i].numberOfVariables()==1);
00387 OPENGM_ASSERT(tgm[i].variableIndex(0)==variablesIndices[0]);
00388 }
00389 }
00390 }
00391 typedef typename opengm::BeliefPropagationUpdateRules<EditableGraphicalModelType,ACC> UpdateRules;
00392 typename MessagePassing<EditableGraphicalModelType, ACC, UpdateRules, opengm::MaxDistance>::Parameter bpPara;
00393 bpPara.isAcyclic_ = opengm::Tribool::True;
00394
00395 OPENGM_ASSERT(tgm.isAcyclic());
00396
00397 MessagePassing<EditableGraphicalModelType, ACC, UpdateRules, opengm::MaxDistance> bp(tgm,bpPara);
00398
00399
00400
00401
00402 try{
00403 bp.infer();
00404 }
00405 catch(...) {
00406 throw RuntimeError("bp failed in astar");
00407 }
00408
00409 if(true) {
00410 std::vector<LabelType> conf(numNodes_);
00411 bp.arg(conf);
00412 for(size_t i =0; i<subconfsize ; ++i) {
00413 conf[i] = a.conf[i];
00414 }
00415
00416 ACC::op(gm_.evaluate(conf),aboveBound_,aboveBound_);
00417 }
00418 std::vector<LabelType> conf(numNodes_);
00419 a.conf.resize(subconfsize+1);
00420 for(size_t i=0; i<numStates_[subconfsize]; ++i) {
00421 a.conf[subconfsize] = i;
00422 bp.constrainedOptimum(parameter_.nodeOrder_,a.conf,conf);
00423 a.value = tgm.evaluate(conf);
00424 array_.push_back(a);
00425 push_heap(array_.begin(), array_.end(), comp1);
00426 }
00427 }
00428 if( parameter_.heuristic_ == parameter_.FASTHEURISTIC) {
00429 std::vector<LabelType> conf(subconfsize);
00430 for(size_t i=0;i<subconfsize;++i)
00431 conf[i] = a.conf[i];
00432 std::vector<ValueType> bound = fastHeuristic(conf);
00433 a.conf.resize(subconfsize+1);
00434 for(size_t i=0; i<numStates_[parameter_.nodeOrder_[subconfsize]]; ++i) {
00435 a.conf[subconfsize] = i;
00436 a.value = bound[i];
00437
00438 array_.push_back(a);
00439 push_heap(array_.begin(), array_.end(), comp1);
00440
00441 }
00442 }
00443 }
00444
00445 template<class GM, class ACC>
00446 std::vector<typename AStar<GM, ACC>::ValueType>
00447 AStar<GM, ACC>::fastHeuristic(typename AStar<GM, ACC>::ConfVec conf)
00448 {
00449 std::list<size_t> factorList;
00450 std::vector<size_t> nodeDegree(numNodes_,0);
00451 std::vector<int> nodeLabel(numNodes_,-1);
00452 std::vector<std::vector<ValueType > > nodeEnergy(numNodes_);
00453 size_t nextNode = parameter_.nodeOrder_[conf.size()];
00454 for(size_t i=0; i<numNodes_; ++i) {
00455 nodeEnergy[i].resize(numStates_[i]);
00456 for(size_t j=0;j<numStates_[i];++j)
00457 OperatorType::neutral(nodeEnergy[i][j]);
00458 }
00459 for(size_t i=0;i<conf.size();++i) {
00460 nodeLabel[parameter_.nodeOrder_[i]] = conf[i];
00461 }
00462
00463
00464
00465
00466 for(size_t i=0; i<gm_.numberOfFactors(); ++i) {
00467 const FactorType & f = gm_[i];
00468 size_t nvar = f.numberOfVariables();
00469
00470 if(nvar==1) {
00471 int index = f.variableIndex(0);
00472 if(nodeLabel[index]>=0) {
00473 nodeEnergy[index].resize(1);
00474
00475 LabelType coordinates[]={static_cast<LabelType>(nodeLabel[index])};
00476 OperatorType::op(f(coordinates), nodeEnergy[index][0]);
00477 }
00478 else{
00479 OPENGM_ASSERT(numStates_[index] == nodeEnergy[index].size());
00480 for(size_t j=0;j<numStates_[index];++j) {
00481
00482 LabelType coordinates[]={j};
00483 OperatorType::op(f(coordinates),nodeEnergy[index][j]);
00484 }
00485 }
00486 }
00487 if(nvar==2) {
00488 size_t index1 = f.variableIndex(0);
00489 size_t index2 = f.variableIndex(1);
00490 if(nodeLabel[index1]>=0) {
00491 if(nodeLabel[index2]>=0) {
00492 nodeEnergy[index1].resize(1);
00493
00494 LabelType coordinates[]={
00495 static_cast<LabelType>(nodeLabel[index1]),
00496 static_cast<LabelType>(nodeLabel[index2])
00497 };
00498 OperatorType::op(f(coordinates),nodeEnergy[index1][0]);
00499 }
00500 else{
00501 OPENGM_ASSERT(numStates_[index2] == nodeEnergy[index2].size());
00502 for(size_t j=0;j<numStates_[index2];++j) {
00503
00504 LabelType coordinates[]={
00505 static_cast<LabelType>(nodeLabel[index1]),
00506 static_cast<LabelType>(j)
00507 };
00508 OperatorType::op(f(coordinates), nodeEnergy[index2][j]);
00509 }
00510 }
00511 }
00512 else if(nodeLabel[index2]>=0) {
00513 OPENGM_ASSERT(numStates_[index1] == nodeEnergy[index1].size());
00514 for(size_t j=0;j<numStates_[index1];++j) {
00515
00516 LabelType coordinates[]={
00517 static_cast<LabelType>(j),
00518 static_cast<LabelType>(nodeLabel[index2])
00519 };
00520 OperatorType::op(f(coordinates),nodeEnergy[index1][j]);
00521 }
00522 }
00523 else if(isTreeFactor_[i]) {
00524 factorList.push_front(i);
00525 ++nodeDegree[index1];
00526 ++nodeDegree[index2];
00527 continue;
00528 }
00529 else{
00530 for(size_t j=0;j<numStates_[index1];++j) {
00531
00532 LabelType coordinates[]={j};
00533 OperatorType::op(optimizedFactor_[i](coordinates), nodeEnergy[index1][j]);
00534 }
00535 }
00536 }
00537 if(nvar>2) {
00538 bool covered = true;
00539 std::vector<size_t> state(nvar);
00540 for(size_t j=0; j<nvar; ++j) {
00541 if(nodeLabel[f.variableIndex(j)]<0) {
00542 state[j] = nodeLabel[f.variableIndex(j)];
00543 covered = false;
00544 }
00545 }
00546 if(covered)
00547 nodeEnergy[f.variableIndex(0)][0] = f(state.begin());
00548 else{
00549 for(size_t j=0;j<numStates_[f.variableIndex(0)];++j) {
00550
00551 LabelType coordinates[]={j};
00552 OperatorType::op(optimizedFactor_[i](coordinates), nodeEnergy[f.variableIndex(0)][j]);
00553 }
00554 }
00555 }
00556 }
00557 nodeDegree[nextNode] += numNodes_;
00558
00559 while(factorList.size()>0) {
00560 size_t id = factorList.front();
00561 factorList.pop_front();
00562 const FactorType & f = gm_[id];
00563 size_t index1 = f.variableIndex(0);
00564 size_t index2 = f.variableIndex(1);
00565 typename FactorType::ValueType temp;
00566 OPENGM_ASSERT(index1 < numNodes_);
00567 OPENGM_ASSERT(index2 < numNodes_);
00568 OPENGM_ASSERT(gm_.numberOfLabels(index1) == numStates_[index1]);
00569 OPENGM_ASSERT(gm_.numberOfLabels(index2) == numStates_[index2]);
00570 if(nodeDegree[index1]==1) {
00571 typename FactorType::ValueType min;
00572 OPENGM_ASSERT(numStates_[index2] == nodeEnergy[index2].size());
00573 for(size_t j2=0;j2<numStates_[index2];++j2) {
00574 ACC::neutral(min);
00575 OPENGM_ASSERT(numStates_[index1] == nodeEnergy[index1].size());
00576 for(size_t j1=0;j1<numStates_[index1];++j1) {
00577 LabelType coordinates[]={j1,j2};
00578 OperatorType::op(f(coordinates),nodeEnergy[index1][j1],temp);
00579 ACC::op(min,temp,min);
00580 }
00581
00582 OperatorType::op(min,nodeEnergy[index2][j2]);
00583 }
00584 --nodeDegree[index1];
00585 --nodeDegree[index2];
00586 nodeEnergy[index1].resize(1);
00587 OperatorType::neutral(nodeEnergy[index1][0]);
00588 }
00589 else if(nodeDegree[index2]==1) {
00590 typename FactorType::ValueType min;
00591 OPENGM_ASSERT(numStates_[index1] == nodeEnergy[index1].size());
00592 for(size_t j1=0;j1<numStates_[index1];++j1) {
00593 ACC::neutral(min);
00594 OPENGM_ASSERT(numStates_[index2] == nodeEnergy[index2].size());
00595 for(size_t j2=0;j2<numStates_[index2];++j2) {
00596 LabelType coordinates[]={j1,j2};
00597 OperatorType::op(f(coordinates),nodeEnergy[index2][j2],temp);
00598 ACC::op(min,temp,min);
00599
00600 }
00601
00602 OperatorType::op(min,nodeEnergy[index1][j1]);
00603 }
00604 --nodeDegree[index1];
00605 --nodeDegree[index2];
00606 nodeEnergy[index2].resize(1);
00607 OperatorType::neutral(nodeEnergy[index2][0]);
00608 }
00609 else{
00610 factorList.push_back(id);
00611 }
00612 }
00613
00614 ValueType result;
00615 ValueType min;
00616 OperatorType::neutral(result);
00617 std::vector<ValueType > bound;
00618 for(size_t i=0;i<numNodes_;++i) {
00619 if(i==nextNode) continue;
00620 ACC::neutral(min);
00621 for(size_t j=0; j<nodeEnergy[i].size();++j)
00622 ACC::op(min,nodeEnergy[i][j],min);
00623
00624 OperatorType::op(min,result);
00625 }
00626 bound.resize(nodeEnergy[nextNode].size());
00627 for(size_t j=0; j<nodeEnergy[nextNode].size();++j) {
00628
00629 OperatorType::op(nodeEnergy[nextNode][j],result,bound[j]);
00630 }
00631 return bound;
00632 }
00633
00634 template<class GM, class ACC>
00635 inline const typename AStar<GM, ACC>::GraphicalModelType&
00636 AStar<GM, ACC>::graphicalModel() const
00637 {
00638 return gm_;
00639 }
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669 }
00670
00671 #endif // #ifndef OPENGM_ASTAR_HXX
00672