577 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			577 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include <cassert>
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <iostream>
 | |
| #include <iomanip>
 | |
| #include <sstream>
 | |
| 
 | |
| #include "BeliefProp.h"
 | |
| #include "Indexer.h"
 | |
| #include "Horus.h"
 | |
| 
 | |
| 
 | |
| namespace Horus {
 | |
| 
 | |
| double    BeliefProp::accuracy_ = 0.0001;
 | |
| unsigned  BeliefProp::maxIter_  = 1000;
 | |
| 
 | |
| BeliefProp::MsgSchedule BeliefProp::schedule_ =
 | |
|     MsgSchedule::seqFixedSch;
 | |
| 
 | |
| 
 | |
| 
 | |
| BeliefProp::BeliefProp (const FactorGraph& fg)
 | |
|     : GroundSolver (fg), nIters_(0), runned_(false)
 | |
| {
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| BeliefProp::~BeliefProp()
 | |
| {
 | |
|   for (size_t i = 0; i < links_.size(); i++) {
 | |
|     delete links_[i];
 | |
|   }
 | |
|   links_.clear();
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| Params
 | |
| BeliefProp::solveQuery (VarIds queryVids)
 | |
| {
 | |
|   assert (queryVids.empty() == false);
 | |
|   return queryVids.size() == 1
 | |
|       ? getPosterioriOf (queryVids[0])
 | |
|       : getJointDistributionOf (queryVids);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::printSolverFlags() const
 | |
| {
 | |
|   std::stringstream ss;
 | |
|   ss << "belief propagation [" ;
 | |
|   ss << "bp_msg_schedule=" ;
 | |
|   switch (schedule_) {
 | |
|     case MsgSchedule::seqFixedSch:    ss << "seq_fixed";    break;
 | |
|     case MsgSchedule::seqRandomSch:   ss << "seq_random";   break;
 | |
|     case MsgSchedule::parallelSch:    ss << "parallel";     break;
 | |
|     case MsgSchedule::maxResidualSch: ss << "max_residual"; break;
 | |
|   }
 | |
|   ss << ",bp_max_iter=" << Util::toString (maxIter_);
 | |
|   ss << ",bp_accuracy=" << Util::toString (accuracy_);
 | |
|   ss << ",log_domain="  << Util::toString (Globals::logDomain);
 | |
|   ss << "]" ;
 | |
|   std::cout << ss.str() << std::endl;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| Params
 | |
| BeliefProp::getPosterioriOf (VarId vid)
 | |
| {
 | |
|   if (runned_ == false) {
 | |
|     runSolver();
 | |
|   }
 | |
|   assert (fg.getVarNode (vid));
 | |
|   VarNode* var = fg.getVarNode (vid);
 | |
|   Params probs;
 | |
|   if (var->hasEvidence()) {
 | |
|     probs.resize (var->range(), LogAware::noEvidence());
 | |
|     probs[var->getEvidence()] = LogAware::withEvidence();
 | |
|   } else {
 | |
|     probs.resize (var->range(), LogAware::multIdenty());
 | |
|     const BpLinks& links = getLinks (var);
 | |
|     if (Globals::logDomain) {
 | |
|       for (size_t i = 0; i < links.size(); i++) {
 | |
|         probs += links[i]->message();
 | |
|       }
 | |
|       LogAware::normalize (probs);
 | |
|       Util::exp (probs);
 | |
|     } else {
 | |
|       for (size_t i = 0; i < links.size(); i++) {
 | |
|         probs *= links[i]->message();
 | |
|       }
 | |
|       LogAware::normalize (probs);
 | |
|     }
 | |
|   }
 | |
|   return probs;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| Params
 | |
| BeliefProp::getJointDistributionOf (const VarIds& jointVarIds)
 | |
| {
 | |
|   if (runned_ == false) {
 | |
|     runSolver();
 | |
|   }
 | |
|   VarNode* vn = fg.getVarNode (jointVarIds[0]);
 | |
|   const FacNodes& facNodes = vn->neighbors();
 | |
|   size_t idx = facNodes.size();
 | |
|   for (size_t i = 0; i < facNodes.size(); i++) {
 | |
|     if (facNodes[i]->factor().contains (jointVarIds)) {
 | |
|       idx = i;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   if (idx == facNodes.size()) {
 | |
|     return getJointByConditioning (jointVarIds);
 | |
|   }
 | |
|   return getFactorJoint (facNodes[idx], jointVarIds);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| Params
 | |
| BeliefProp::getFactorJoint (
 | |
|     FacNode* fn,
 | |
|     const VarIds& jointVarIds)
 | |
| {
 | |
|   if (runned_ == false) {
 | |
|     runSolver();
 | |
|   }
 | |
|   Factor res (fn->factor());
 | |
|   const BpLinks& links = getLinks( fn);
 | |
|   for (size_t i = 0; i < links.size(); i++) {
 | |
|     Factor msg ({links[i]->varNode()->varId()},
 | |
|                 {links[i]->varNode()->range()},
 | |
|                 getVarToFactorMsg (links[i]));
 | |
|     res.multiply (msg);
 | |
|   }
 | |
|   res.sumOutAllExcept (jointVarIds);
 | |
|   res.reorderArguments (jointVarIds);
 | |
|   res.normalize();
 | |
|   Params jointDist = res.params();
 | |
|   if (Globals::logDomain) {
 | |
|     Util::exp (jointDist);
 | |
|   }
 | |
|   return jointDist;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| BeliefProp::BpLink::BpLink (FacNode* fn, VarNode* vn)
 | |
| {
 | |
|   fac_ = fn;
 | |
|   var_ = vn;
 | |
|   v1_.resize (vn->range(), LogAware::log (1.0 / vn->range()));
 | |
|   v2_.resize (vn->range(), LogAware::log (1.0 / vn->range()));
 | |
|   currMsg_   = &v1_;
 | |
|   nextMsg_   = &v2_;
 | |
|   residual_  = 0.0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::BpLink::clearResidual()
 | |
| {
 | |
|   residual_ = 0.0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::BpLink::updateResidual()
 | |
| {
 | |
|   residual_ = LogAware::getMaxNorm (v1_, v2_);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::BpLink::updateMessage()
 | |
| {
 | |
|   swap (currMsg_, nextMsg_);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| std::string
 | |
| BeliefProp::BpLink::toString() const
 | |
| {
 | |
|   std::stringstream ss;
 | |
|   ss << fac_->getLabel();
 | |
|   ss << " -- " ;
 | |
|   ss << var_->label();
 | |
|   return ss.str();
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::calculateAndUpdateMessage (BpLink* link, bool calcResidual)
 | |
| {
 | |
|   if (Globals::verbosity > 2) {
 | |
|     std::cout << "calculating & updating " << link->toString();
 | |
|     std::cout << std::endl;
 | |
|   }
 | |
|   calcFactorToVarMsg (link);
 | |
|   if (calcResidual) {
 | |
|     link->updateResidual();
 | |
|   }
 | |
|   link->updateMessage();
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::calculateMessage (BpLink* link, bool calcResidual)
 | |
| {
 | |
|   if (Globals::verbosity > 2) {
 | |
|     std::cout << "calculating " << link->toString();
 | |
|     std::cout << std::endl;
 | |
|   }
 | |
|   calcFactorToVarMsg (link);
 | |
|   if (calcResidual) {
 | |
|     link->updateResidual();
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::updateMessage (BpLink* link)
 | |
| {
 | |
|   link->updateMessage();
 | |
|   if (Globals::verbosity > 2) {
 | |
|     std::cout << "updating " << link->toString();
 | |
|     std::cout << std::endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::runSolver()
 | |
| {
 | |
|   initializeSolver();
 | |
|   nIters_ = 0;
 | |
|   while (!converged() && nIters_ < maxIter_) {
 | |
|     nIters_ ++;
 | |
|     if (Globals::verbosity > 1) {
 | |
|       Util::printHeader (std::string ("Iteration ")
 | |
|           + Util::toString (nIters_));
 | |
|     }
 | |
|     switch (schedule_) {
 | |
|      case MsgSchedule::seqRandomSch:
 | |
|        std::random_shuffle (links_.begin(), links_.end());
 | |
|        // no break
 | |
|       case MsgSchedule::seqFixedSch:
 | |
|         for (size_t i = 0; i < links_.size(); i++) {
 | |
|           calculateAndUpdateMessage (links_[i]);
 | |
|         }
 | |
|         break;
 | |
|       case MsgSchedule::parallelSch:
 | |
|         for (size_t i = 0; i < links_.size(); i++) {
 | |
|           calculateMessage (links_[i]);
 | |
|         }
 | |
|         for (size_t i = 0; i < links_.size(); i++) {
 | |
|           updateMessage(links_[i]);
 | |
|         }
 | |
|         break;
 | |
|       case MsgSchedule::maxResidualSch:
 | |
|         maxResidualSchedule();
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
|   if (Globals::verbosity > 0) {
 | |
|     if (nIters_ < maxIter_) {
 | |
|       std::cout << "Belief propagation converged in " ;
 | |
|       std::cout << nIters_ << " iterations" << std::endl;
 | |
|     } else {
 | |
|       std::cout << "The maximum number of iterations was hit," ;
 | |
|       std::cout << " terminating..." ;
 | |
|       std::cout << std::endl;
 | |
|     }
 | |
|     std::cout << std::endl;
 | |
|   }
 | |
|   runned_ = true;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::createLinks()
 | |
| {
 | |
|   const FacNodes& facNodes = fg.facNodes();
 | |
|   for (size_t i = 0; i < facNodes.size(); i++) {
 | |
|     const VarNodes& neighbors = facNodes[i]->neighbors();
 | |
|     for (size_t j = 0; j < neighbors.size(); j++) {
 | |
|       links_.push_back (new BpLink (facNodes[i], neighbors[j]));
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::maxResidualSchedule()
 | |
| {
 | |
|   if (nIters_ == 1) {
 | |
|     for (size_t i = 0; i < links_.size(); i++) {
 | |
|       calculateMessage (links_[i]);
 | |
|       SortedOrder::iterator it = sortedOrder_.insert (links_[i]);
 | |
|       linkMap_.insert (make_pair (links_[i], it));
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   for (size_t c = 0; c < links_.size(); c++) {
 | |
|     if (Globals::verbosity > 1) {
 | |
|       std::cout << "current residuals:" << std::endl;
 | |
|       for (SortedOrder::iterator it = sortedOrder_.begin();
 | |
|           it != sortedOrder_.end(); ++it) {
 | |
|         std::cout << "    " << std::setw (30) << std::left;
 | |
|         std::cout << (*it)->toString();
 | |
|         std::cout << "residual = " << (*it)->residual();
 | |
|         std::cout << std::endl;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     SortedOrder::iterator it = sortedOrder_.begin();
 | |
|     BpLink* link = *it;
 | |
|     if (link->residual() < accuracy_) {
 | |
|       return;
 | |
|     }
 | |
|     updateMessage (link);
 | |
|     link->clearResidual();
 | |
|     sortedOrder_.erase (it);
 | |
|     linkMap_.find (link)->second = sortedOrder_.insert (link);
 | |
| 
 | |
|     // update the messages that depend on message source --> destin
 | |
|     const FacNodes& factorNeighbors = link->varNode()->neighbors();
 | |
|     for (size_t i = 0; i < factorNeighbors.size(); i++) {
 | |
|       if (factorNeighbors[i] != link->facNode()) {
 | |
|         const BpLinks& links = getLinks (factorNeighbors[i]);
 | |
|         for (size_t j = 0; j < links.size(); j++) {
 | |
|           if (links[j]->varNode() != link->varNode()) {
 | |
|             calculateMessage (links[j]);
 | |
|             BpLinkMap::iterator iter = linkMap_.find (links[j]);
 | |
|             sortedOrder_.erase (iter->second);
 | |
|             iter->second = sortedOrder_.insert (links[j]);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if (Globals::verbosity > 1) {
 | |
|       Util::printDashedLine();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::calcFactorToVarMsg (BpLink* link)
 | |
| {
 | |
|   FacNode* src = link->facNode();
 | |
|   const VarNode* dst = link->varNode();
 | |
|   const BpLinks& links = getLinks (src);
 | |
|   // calculate the product of messages that were sent
 | |
|   // to factor `src', except from var `dst'
 | |
|   unsigned reps    = 1;
 | |
|   unsigned msgSize = Util::sizeExpected (src->factor().ranges());
 | |
|   Params msgProduct (msgSize, LogAware::multIdenty());
 | |
|   if (Globals::logDomain) {
 | |
|     for (size_t i = links.size(); i-- > 0; ) {
 | |
|       if (links[i]->varNode() != dst) {
 | |
|         if (Constants::showBpCalcs) {
 | |
|           std::cout << "    message from " << links[i]->varNode()->label();
 | |
|           std::cout << ": " ;
 | |
|         }
 | |
|         Util::apply_n_times (msgProduct, getVarToFactorMsg (links[i]),
 | |
|             reps, std::plus<double>());
 | |
|         if (Constants::showBpCalcs) {
 | |
|           std::cout << std::endl;
 | |
|         }
 | |
|       }
 | |
|       reps *= links[i]->varNode()->range();
 | |
|     }
 | |
|   } else {
 | |
|     for (size_t i = links.size(); i-- > 0; ) {
 | |
|       if (links[i]->varNode() != dst) {
 | |
|         if (Constants::showBpCalcs) {
 | |
|           std::cout << "    message from " << links[i]->varNode()->label();
 | |
|           std::cout << ": " ;
 | |
|         }
 | |
|         Util::apply_n_times (msgProduct, getVarToFactorMsg (links[i]),
 | |
|             reps, std::multiplies<double>());
 | |
|         if (Constants::showBpCalcs) {
 | |
|           std::cout << std::endl;
 | |
|         }
 | |
|       }
 | |
|       reps *= links[i]->varNode()->range();
 | |
|     }
 | |
|   }
 | |
|   Factor result (src->factor().arguments(),
 | |
|       src->factor().ranges(), msgProduct);
 | |
|   result.multiply (src->factor());
 | |
|   if (Constants::showBpCalcs) {
 | |
|     std::cout << "    message product:  " << msgProduct << std::endl;
 | |
|     std::cout << "    original factor:  " << src->factor().params();
 | |
|     std::cout << std::endl;
 | |
|     std::cout << "    factor product:   " << result.params() << std::endl;
 | |
|   }
 | |
|   result.sumOutAllExcept (dst->varId());
 | |
|   if (Constants::showBpCalcs) {
 | |
|     std::cout << "    marginalized:     " << result.params() << std::endl;
 | |
|   }
 | |
|   link->nextMessage() = result.params();
 | |
|   LogAware::normalize (link->nextMessage());
 | |
|   if (Constants::showBpCalcs) {
 | |
|     std::cout << "    curr msg:         " << link->message() << std::endl;
 | |
|     std::cout << "    next msg:         " << link->nextMessage() << std::endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| Params
 | |
| BeliefProp::getVarToFactorMsg (const BpLink* link)
 | |
| {
 | |
|   const VarNode* src = link->varNode();
 | |
|   Params msg;
 | |
|   if (src->hasEvidence()) {
 | |
|     msg.resize (src->range(), LogAware::noEvidence());
 | |
|     msg[src->getEvidence()] = LogAware::withEvidence();
 | |
|   } else {
 | |
|     msg.resize (src->range(), LogAware::one());
 | |
|   }
 | |
|   if (Constants::showBpCalcs) {
 | |
|     std::cout << msg;
 | |
|   }
 | |
|   BpLinks::const_iterator it;
 | |
|   const BpLinks& links = getLinks (src);
 | |
|   if (Globals::logDomain) {
 | |
|     for (it = links.begin(); it != links.end(); ++it) {
 | |
|       if (*it != link) {
 | |
|         msg += (*it)->message();
 | |
|       }
 | |
|       if (Constants::showBpCalcs) {
 | |
|         std::cout << " x " << (*it)->message();
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     for (it = links.begin(); it != links.end(); ++it) {
 | |
|       if (*it != link) {
 | |
|         msg *= (*it)->message();
 | |
|       }
 | |
|       if (Constants::showBpCalcs) {
 | |
|         std::cout << " x " << (*it)->message();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (Constants::showBpCalcs) {
 | |
|     std::cout << " = " << msg;
 | |
|   }
 | |
|   return msg;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| Params
 | |
| BeliefProp::getJointByConditioning (const VarIds& jointVarIds) const
 | |
| {
 | |
|   return GroundSolver::getJointByConditioning (
 | |
|       GroundSolverType::bpSolver, fg, jointVarIds);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::initializeSolver()
 | |
| {
 | |
|   const VarNodes& varNodes = fg.varNodes();
 | |
|   varsLinks_.reserve (varNodes.size());
 | |
|   for (size_t i = 0; i < varNodes.size(); i++) {
 | |
|     varsLinks_.push_back (BpLinks());
 | |
|   }
 | |
|   const FacNodes& facNodes = fg.facNodes();
 | |
|   facsLinks_.reserve (facNodes.size());
 | |
|   for (size_t i = 0; i < facNodes.size(); i++) {
 | |
|     facsLinks_.push_back (BpLinks());
 | |
|   }
 | |
|   createLinks();
 | |
|   for (size_t i = 0; i < links_.size(); i++) {
 | |
|     FacNode* src = links_[i]->facNode();
 | |
|     VarNode* dst = links_[i]->varNode();
 | |
|     getLinks (dst).push_back (links_[i]);
 | |
|     getLinks (src).push_back (links_[i]);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| bool
 | |
| BeliefProp::converged()
 | |
| {
 | |
|   if (links_.empty()) {
 | |
|     return true;
 | |
|   }
 | |
|   if (nIters_ == 0) {
 | |
|     return false;
 | |
|   }
 | |
|   if (Globals::verbosity > 2) {
 | |
|     std::cout << std::endl;
 | |
|   }
 | |
|   if (nIters_ == 1) {
 | |
|     if (Globals::verbosity > 1) {
 | |
|       std::cout << "no residuals" << std::endl << std::endl;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
|   bool converged = true;
 | |
|   if (schedule_ == MsgSchedule::maxResidualSch) {
 | |
|     double maxResidual = (*(sortedOrder_.begin()))->residual();
 | |
|     if (maxResidual > accuracy_) {
 | |
|       converged = false;
 | |
|     } else {
 | |
|       converged = true;
 | |
|     }
 | |
|   } else {
 | |
|     for (size_t i = 0; i < links_.size(); i++) {
 | |
|       double residual = links_[i]->residual();
 | |
|       if (Globals::verbosity > 1) {
 | |
|         std::cout << links_[i]->toString() + " residual = " << residual;
 | |
|         std::cout << std::endl;
 | |
|       }
 | |
|       if (residual > accuracy_) {
 | |
|         converged = false;
 | |
|         if (Globals::verbosity < 2) {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if (Globals::verbosity > 1) {
 | |
|       std::cout << std::endl;
 | |
|     }
 | |
|   }
 | |
|   return converged;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| BeliefProp::printLinkInformation() const
 | |
| {
 | |
|   using std::cout;
 | |
|   using std::endl;
 | |
|   for (size_t i = 0; i < links_.size(); i++) {
 | |
|     BpLink* l = links_[i];
 | |
|     cout << l->toString() << ":" << endl;
 | |
|     cout << "    curr msg = " ;
 | |
|     cout << l->message() << endl;
 | |
|     cout << "    next msg = " ;
 | |
|     cout << l->nextMessage() << endl;
 | |
|     cout << "    residual = " << l->residual() << endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace Horus
 | |
| 
 |