Changes by Tiago
This commit is contained in:
parent
33bf0bc0f5
commit
7df0fd90f5
@ -1,149 +0,0 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "BPNodeInfo.h"
|
||||
#include "BPSolver.h"
|
||||
|
||||
BPNodeInfo::BPNodeInfo (BayesNode* node)
|
||||
{
|
||||
node_ = node;
|
||||
ds_ = node->getDomainSize();
|
||||
piValsCalc_ = false;
|
||||
ldValsCalc_ = false;
|
||||
nPiMsgsRcv_ = 0;
|
||||
nLdMsgsRcv_ = 0;
|
||||
piVals_.resize (ds_, 1);
|
||||
ldVals_.resize (ds_, 1);
|
||||
const BnNodeSet& childs = node->getChilds();
|
||||
for (unsigned i = 0; i < childs.size(); i++) {
|
||||
cmsgs_.insert (make_pair (childs[i], false));
|
||||
}
|
||||
const BnNodeSet& parents = node->getParents();
|
||||
for (unsigned i = 0; i < parents.size(); i++) {
|
||||
pmsgs_.insert (make_pair (parents[i], false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
BPNodeInfo::getBeliefs (void) const
|
||||
{
|
||||
double sum = 0.0;
|
||||
ParamSet beliefs (ds_);
|
||||
for (unsigned xi = 0; xi < ds_; xi++) {
|
||||
double prod = piVals_[xi] * ldVals_[xi];
|
||||
beliefs[xi] = prod;
|
||||
sum += prod;
|
||||
}
|
||||
assert (sum);
|
||||
//normalize the beliefs
|
||||
for (unsigned xi = 0; xi < ds_; xi++) {
|
||||
beliefs[xi] /= sum;
|
||||
}
|
||||
return beliefs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
BPNodeInfo::readyToSendPiMsgTo (const BayesNode* child) const
|
||||
{
|
||||
for (unsigned i = 0; i < inChildLinks_.size(); i++) {
|
||||
if (inChildLinks_[i]->getSource() != child
|
||||
&& !inChildLinks_[i]->messageWasSended()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
BPNodeInfo::readyToSendLambdaMsgTo (const BayesNode* parent) const
|
||||
{
|
||||
for (unsigned i = 0; i < inParentLinks_.size(); i++) {
|
||||
if (inParentLinks_[i]->getSource() != parent
|
||||
&& !inParentLinks_[i]->messageWasSended()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double
|
||||
BPNodeInfo::getPiValue (unsigned idx) const
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
return piVals_[idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPNodeInfo::setPiValue (unsigned idx, Param value)
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
piVals_[idx] = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double
|
||||
BPNodeInfo::getLambdaValue (unsigned idx) const
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
return ldVals_[idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPNodeInfo::setLambdaValue (unsigned idx, Param value)
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
ldVals_[idx] = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double
|
||||
BPNodeInfo::getBeliefChange (void)
|
||||
{
|
||||
double change = 0.0;
|
||||
if (oldBeliefs_.size() == 0) {
|
||||
oldBeliefs_ = getBeliefs();
|
||||
change = 9999999999.0;
|
||||
} else {
|
||||
ParamSet currentBeliefs = getBeliefs();
|
||||
for (unsigned xi = 0; xi < ds_; xi++) {
|
||||
change += abs (currentBeliefs[xi] - oldBeliefs_[xi]);
|
||||
}
|
||||
oldBeliefs_ = currentBeliefs;
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
BPNodeInfo::receivedBottomInfluence (void) const
|
||||
{
|
||||
// if all lambda values are equal, then neither
|
||||
// this node neither its descendents have evidence,
|
||||
// we can use this to don't send lambda messages his parents
|
||||
bool childInfluenced = false;
|
||||
for (unsigned xi = 1; xi < ds_; xi++) {
|
||||
if (ldVals_[xi] != ldVals_[0]) {
|
||||
childInfluenced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return childInfluenced;
|
||||
}
|
||||
|
@ -1,82 +0,0 @@
|
||||
#ifndef BP_BP_NODE_H
|
||||
#define BP_BP_NODE_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "BPSolver.h"
|
||||
#include "BayesNode.h"
|
||||
#include "Shared.h"
|
||||
|
||||
//class Edge;
|
||||
|
||||
using namespace std;
|
||||
|
||||
class BPNodeInfo
|
||||
{
|
||||
public:
|
||||
BPNodeInfo (int);
|
||||
BPNodeInfo (BayesNode*);
|
||||
|
||||
ParamSet getBeliefs (void) const;
|
||||
double getPiValue (unsigned) const;
|
||||
void setPiValue (unsigned, Param);
|
||||
double getLambdaValue (unsigned) const;
|
||||
void setLambdaValue (unsigned, Param);
|
||||
double getBeliefChange (void);
|
||||
bool receivedBottomInfluence (void) const;
|
||||
|
||||
ParamSet& getPiValues (void) { return piVals_; }
|
||||
ParamSet& getLambdaValues (void) { return ldVals_; }
|
||||
bool arePiValuesCalculated (void) { return piValsCalc_; }
|
||||
bool areLambdaValuesCalculated (void) { return ldValsCalc_; }
|
||||
void markPiValuesAsCalculated (void) { piValsCalc_ = true; }
|
||||
void markLambdaValuesAsCalculated (void) { ldValsCalc_ = true; }
|
||||
void incNumPiMsgsRcv (void) { nPiMsgsRcv_ ++; }
|
||||
void incNumLambdaMsgsRcv (void) { nLdMsgsRcv_ ++; }
|
||||
|
||||
bool receivedAllPiMessages (void)
|
||||
{
|
||||
return node_->getParents().size() == nPiMsgsRcv_;
|
||||
}
|
||||
|
||||
bool receivedAllLambdaMessages (void)
|
||||
{
|
||||
return node_->getChilds().size() == nLdMsgsRcv_;
|
||||
}
|
||||
|
||||
bool readyToSendPiMsgTo (const BayesNode*) const ;
|
||||
bool readyToSendLambdaMsgTo (const BayesNode*) const;
|
||||
|
||||
CEdgeSet getIncomingParentLinks (void) { return inParentLinks_; }
|
||||
CEdgeSet getIncomingChildLinks (void) { return inChildLinks_; }
|
||||
CEdgeSet getOutcomingParentLinks (void) { return outParentLinks_; }
|
||||
CEdgeSet getOutcomingChildLinks (void) { return outChildLinks_; }
|
||||
|
||||
void addIncomingParentLink (Edge* l) { inParentLinks_.push_back (l); }
|
||||
void addIncomingChildLink (Edge* l) { inChildLinks_.push_back (l); }
|
||||
void addOutcomingParentLink (Edge* l) { outParentLinks_.push_back (l); }
|
||||
void addOutcomingChildLink (Edge* l) { outChildLinks_.push_back (l); }
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN (BPNodeInfo);
|
||||
|
||||
ParamSet piVals_; // pi values
|
||||
ParamSet ldVals_; // lambda values
|
||||
ParamSet oldBeliefs_;
|
||||
unsigned nPiMsgsRcv_;
|
||||
unsigned nLdMsgsRcv_;
|
||||
bool piValsCalc_;
|
||||
bool ldValsCalc_;
|
||||
EdgeSet inParentLinks_;
|
||||
EdgeSet inChildLinks_;
|
||||
EdgeSet outParentLinks_;
|
||||
EdgeSet outChildLinks_;
|
||||
unsigned ds_;
|
||||
const BayesNode* node_;
|
||||
map<const BayesNode*, bool> pmsgs_;
|
||||
map<const BayesNode*, bool> cmsgs_;
|
||||
};
|
||||
|
||||
#endif //BP_BP_NODE_H
|
||||
|
@ -1,905 +0,0 @@
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <time.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "BPSolver.h"
|
||||
|
||||
BPSolver::BPSolver (const BayesNet& bn) : Solver (&bn)
|
||||
{
|
||||
bn_ = &bn;
|
||||
useAlwaysLoopySolver_ = false;
|
||||
//jointCalcType_ = CHAIN_RULE;
|
||||
jointCalcType_ = JUNCTION_NODE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BPSolver::~BPSolver (void)
|
||||
{
|
||||
for (unsigned i = 0; i < nodesI_.size(); i++) {
|
||||
delete nodesI_[i];
|
||||
}
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
delete links_[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPSolver::runSolver (void)
|
||||
{
|
||||
clock_t start_ = clock();
|
||||
unsigned size = bn_->getNumberOfNodes();
|
||||
unsigned nIters = 0;
|
||||
initializeSolver();
|
||||
if (bn_->isSingleConnected() && !useAlwaysLoopySolver_) {
|
||||
runPolyTreeSolver();
|
||||
Statistics::numSolvedPolyTrees ++;
|
||||
} else {
|
||||
runLoopySolver();
|
||||
Statistics::numSolvedLoopyNets ++;
|
||||
if (nIter_ >= SolverOptions::maxIter) {
|
||||
Statistics::numUnconvergedRuns ++;
|
||||
} else {
|
||||
nIters = nIter_;
|
||||
}
|
||||
if (DL >= 2) {
|
||||
cout << endl;
|
||||
if (nIter_ < SolverOptions::maxIter) {
|
||||
cout << "Belief propagation converged in " ;
|
||||
cout << nIter_ << " iterations" << endl;
|
||||
} else {
|
||||
cout << "The maximum number of iterations was hit, terminating..." ;
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
double time = (double (clock() - start_)) / CLOCKS_PER_SEC;
|
||||
Statistics::updateStats (size, nIters, time);
|
||||
if (EXPORT_TO_DOT && size > EXPORT_MIN_SIZE) {
|
||||
stringstream ss;
|
||||
ss << size << "." ;
|
||||
ss << Statistics::getCounting (size) << ".dot" ;
|
||||
bn_->exportToDotFormat (ss.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
BPSolver::getPosterioriOf (Vid vid) const
|
||||
{
|
||||
BayesNode* node = bn_->getBayesNode (vid);
|
||||
assert (node);
|
||||
return nodesI_[node->getIndex()]->getBeliefs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
BPSolver::getJointDistributionOf (const VidSet& jointVids)
|
||||
{
|
||||
if (DL >= 2) {
|
||||
cout << "calculating joint distribution on: " ;
|
||||
for (unsigned i = 0; i < jointVids.size(); i++) {
|
||||
Variable* var = bn_->getBayesNode (jointVids[i]);
|
||||
cout << var->getLabel() << " " ;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
if (jointCalcType_ == JUNCTION_NODE) {
|
||||
return getJointByJunctionNode (jointVids);
|
||||
} else {
|
||||
return getJointByChainRule (jointVids);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPSolver::initializeSolver (void)
|
||||
{
|
||||
if (DL >= 2) {
|
||||
if (!useAlwaysLoopySolver_) {
|
||||
cout << "-> solver type = polytree solver" << endl;
|
||||
cout << "-> schedule = n/a";
|
||||
} else {
|
||||
cout << "-> solver = loopy solver" << endl;
|
||||
cout << "-> schedule = ";
|
||||
switch (SolverOptions::schedule) {
|
||||
case SolverOptions::S_SEQ_FIXED: cout << "sequential fixed" ; break;
|
||||
case SolverOptions::S_SEQ_RANDOM: cout << "sequential random" ; break;
|
||||
case SolverOptions::S_PARALLEL: cout << "parallel" ; break;
|
||||
case SolverOptions::S_MAX_RESIDUAL: cout << "max residual" ; break;
|
||||
}
|
||||
}
|
||||
cout << endl;
|
||||
cout << "-> joint method = " ;
|
||||
if (jointCalcType_ == JUNCTION_NODE) {
|
||||
cout << "junction node" << endl;
|
||||
} else {
|
||||
cout << "chain rule " << endl;
|
||||
}
|
||||
cout << "-> max iters = " << SolverOptions::maxIter << endl;
|
||||
cout << "-> accuracy = " << SolverOptions::accuracy << endl;
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
CBnNodeSet nodes = bn_->getBayesNodes();
|
||||
for (unsigned i = 0; i < nodesI_.size(); i++) {
|
||||
delete nodesI_[i];
|
||||
}
|
||||
nodesI_.clear();
|
||||
nodesI_.reserve (nodes.size());
|
||||
links_.clear();
|
||||
sortedOrder_.clear();
|
||||
edgeMap_.clear();
|
||||
|
||||
for (unsigned i = 0; i < nodes.size(); i++) {
|
||||
nodesI_.push_back (new BPNodeInfo (nodes[i]));
|
||||
}
|
||||
|
||||
BnNodeSet roots = bn_->getRootNodes();
|
||||
for (unsigned i = 0; i < roots.size(); i++) {
|
||||
const ParamSet& params = roots[i]->getParameters();
|
||||
ParamSet& piVals = M(roots[i])->getPiValues();
|
||||
for (unsigned ri = 0; ri < roots[i]->getDomainSize(); ri++) {
|
||||
piVals[ri] = params[ri];
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < nodes.size(); i++) {
|
||||
CBnNodeSet parents = nodes[i]->getParents();
|
||||
for (unsigned j = 0; j < parents.size(); j++) {
|
||||
Edge* newLink = new Edge (parents[j], nodes[i], PI_MSG);
|
||||
links_.push_back (newLink);
|
||||
M(nodes[i])->addIncomingParentLink (newLink);
|
||||
M(parents[j])->addOutcomingChildLink (newLink);
|
||||
}
|
||||
CBnNodeSet childs = nodes[i]->getChilds();
|
||||
for (unsigned j = 0; j < childs.size(); j++) {
|
||||
Edge* newLink = new Edge (childs[j], nodes[i], LAMBDA_MSG);
|
||||
links_.push_back (newLink);
|
||||
M(nodes[i])->addIncomingChildLink (newLink);
|
||||
M(childs[j])->addOutcomingParentLink (newLink);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < nodes.size(); i++) {
|
||||
if (nodes[i]->hasEvidence()) {
|
||||
ParamSet& piVals = M(nodes[i])->getPiValues();
|
||||
ParamSet& ldVals = M(nodes[i])->getLambdaValues();
|
||||
for (unsigned xi = 0; xi < nodes[i]->getDomainSize(); xi++) {
|
||||
piVals[xi] = 0.0;
|
||||
ldVals[xi] = 0.0;
|
||||
}
|
||||
piVals[nodes[i]->getEvidence()] = 1.0;
|
||||
ldVals[nodes[i]->getEvidence()] = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPSolver::runPolyTreeSolver (void)
|
||||
{
|
||||
CBnNodeSet nodes = bn_->getBayesNodes();
|
||||
for (unsigned i = 0; i < nodes.size(); i++) {
|
||||
if (nodes[i]->isRoot()) {
|
||||
M(nodes[i])->markPiValuesAsCalculated();
|
||||
}
|
||||
if (nodes[i]->isLeaf()) {
|
||||
M(nodes[i])->markLambdaValuesAsCalculated();
|
||||
}
|
||||
}
|
||||
|
||||
bool finish = false;
|
||||
while (!finish) {
|
||||
finish = true;
|
||||
for (unsigned i = 0; i < nodes.size(); i++) {
|
||||
if (M(nodes[i])->arePiValuesCalculated() == false
|
||||
&& M(nodes[i])->receivedAllPiMessages()) {
|
||||
if (!nodes[i]->hasEvidence()) {
|
||||
updatePiValues (nodes[i]);
|
||||
}
|
||||
M(nodes[i])->markPiValuesAsCalculated();
|
||||
finish = false;
|
||||
}
|
||||
|
||||
if (M(nodes[i])->areLambdaValuesCalculated() == false
|
||||
&& M(nodes[i])->receivedAllLambdaMessages()) {
|
||||
if (!nodes[i]->hasEvidence()) {
|
||||
updateLambdaValues (nodes[i]);
|
||||
}
|
||||
M(nodes[i])->markLambdaValuesAsCalculated();
|
||||
finish = false;
|
||||
}
|
||||
|
||||
if (M(nodes[i])->arePiValuesCalculated()) {
|
||||
CEdgeSet outChildLinks = M(nodes[i])->getOutcomingChildLinks();
|
||||
for (unsigned j = 0; j < outChildLinks.size(); j++) {
|
||||
BayesNode* child = outChildLinks[j]->getDestination();
|
||||
if (!outChildLinks[j]->messageWasSended()) {
|
||||
if (M(nodes[i])->readyToSendPiMsgTo (child)) {
|
||||
outChildLinks[j]->setNextMessage (getMessage (outChildLinks[j]));
|
||||
outChildLinks[j]->updateMessage();
|
||||
M(child)->incNumPiMsgsRcv();
|
||||
}
|
||||
finish = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (M(nodes[i])->areLambdaValuesCalculated()) {
|
||||
CEdgeSet outParentLinks = M(nodes[i])->getOutcomingParentLinks();
|
||||
for (unsigned j = 0; j < outParentLinks.size(); j++) {
|
||||
BayesNode* parent = outParentLinks[j]->getDestination();
|
||||
if (!outParentLinks[j]->messageWasSended()) {
|
||||
if (M(nodes[i])->readyToSendLambdaMsgTo (parent)) {
|
||||
outParentLinks[j]->setNextMessage (getMessage (outParentLinks[j]));
|
||||
outParentLinks[j]->updateMessage();
|
||||
M(parent)->incNumLambdaMsgsRcv();
|
||||
}
|
||||
finish = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPSolver::runLoopySolver()
|
||||
{
|
||||
nIter_ = 0;
|
||||
while (!converged() && nIter_ < SolverOptions::maxIter) {
|
||||
|
||||
nIter_++;
|
||||
if (DL >= 2) {
|
||||
cout << endl;
|
||||
cout << "****************************************" ;
|
||||
cout << "****************************************" ;
|
||||
cout << endl;
|
||||
cout << " Iteration " << nIter_ << endl;
|
||||
cout << "****************************************" ;
|
||||
cout << "****************************************" ;
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
switch (SolverOptions::schedule) {
|
||||
|
||||
case SolverOptions::S_SEQ_RANDOM:
|
||||
random_shuffle (links_.begin(), links_.end());
|
||||
// no break
|
||||
|
||||
case SolverOptions::S_SEQ_FIXED:
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
links_[i]->setNextMessage (getMessage (links_[i]));
|
||||
links_[i]->updateMessage();
|
||||
updateValues (links_[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case SolverOptions::S_PARALLEL:
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
//calculateNextMessage (links_[i]);
|
||||
}
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
//updateMessage (links_[i]);
|
||||
//updateValues (links_[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case SolverOptions::S_MAX_RESIDUAL:
|
||||
maxResidualSchedule();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
BPSolver::converged (void) const
|
||||
{
|
||||
// this can happen if the graph is fully disconnected
|
||||
if (links_.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
if (nIter_ == 0 || nIter_ == 1) {
|
||||
return false;
|
||||
}
|
||||
bool converged = true;
|
||||
if (SolverOptions::schedule == SolverOptions::S_MAX_RESIDUAL) {
|
||||
Param maxResidual = (*(sortedOrder_.begin()))->getResidual();
|
||||
if (maxResidual < SolverOptions::accuracy) {
|
||||
converged = true;
|
||||
} else {
|
||||
converged = false;
|
||||
}
|
||||
} else {
|
||||
CBnNodeSet nodes = bn_->getBayesNodes();
|
||||
for (unsigned i = 0; i < nodes.size(); i++) {
|
||||
if (!nodes[i]->hasEvidence()) {
|
||||
double change = M(nodes[i])->getBeliefChange();
|
||||
if (DL >= 2) {
|
||||
cout << nodes[i]->getLabel() + " belief change = " ;
|
||||
cout << change << endl;
|
||||
}
|
||||
if (change > SolverOptions::accuracy) {
|
||||
converged = false;
|
||||
if (DL == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return converged;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPSolver::maxResidualSchedule (void)
|
||||
{
|
||||
if (nIter_ == 1) {
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
links_[i]->setNextMessage (getMessage (links_[i]));
|
||||
links_[i]->updateResidual();
|
||||
SortedOrder::iterator it = sortedOrder_.insert (links_[i]);
|
||||
edgeMap_.insert (make_pair (links_[i], it));
|
||||
if (DL >= 2) {
|
||||
cout << "calculating " << links_[i]->toString() << endl;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned c = 0; c < sortedOrder_.size(); c++) {
|
||||
if (DL >= 2) {
|
||||
cout << endl << "current residuals:" << endl;
|
||||
for (SortedOrder::iterator it = sortedOrder_.begin();
|
||||
it != sortedOrder_.end(); it ++) {
|
||||
cout << " " << setw (30) << left << (*it)->toString();
|
||||
cout << "residual = " << (*it)->getResidual() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
SortedOrder::iterator it = sortedOrder_.begin();
|
||||
Edge* edge = *it;
|
||||
if (DL >= 2) {
|
||||
cout << "updating " << edge->toString() << endl;
|
||||
}
|
||||
if (edge->getResidual() < SolverOptions::accuracy) {
|
||||
return;
|
||||
}
|
||||
edge->updateMessage();
|
||||
updateValues (edge);
|
||||
edge->clearResidual();
|
||||
sortedOrder_.erase (it);
|
||||
edgeMap_.find (edge)->second = sortedOrder_.insert (edge);
|
||||
|
||||
// update the messages that depend on message source --> destin
|
||||
CEdgeSet outChildLinks =
|
||||
M(edge->getDestination())->getOutcomingChildLinks();
|
||||
for (unsigned i = 0; i < outChildLinks.size(); i++) {
|
||||
if (outChildLinks[i]->getDestination() != edge->getSource()) {
|
||||
if (DL >= 2) {
|
||||
cout << " calculating " << outChildLinks[i]->toString() << endl;
|
||||
}
|
||||
outChildLinks[i]->setNextMessage (getMessage (outChildLinks[i]));
|
||||
outChildLinks[i]->updateResidual();
|
||||
EdgeMap::iterator iter = edgeMap_.find (outChildLinks[i]);
|
||||
sortedOrder_.erase (iter->second);
|
||||
iter->second = sortedOrder_.insert (outChildLinks[i]);
|
||||
}
|
||||
}
|
||||
CEdgeSet outParentLinks =
|
||||
M(edge->getDestination())->getOutcomingParentLinks();
|
||||
for (unsigned i = 0; i < outParentLinks.size(); i++) {
|
||||
if (outParentLinks[i]->getDestination() != edge->getSource()) {
|
||||
//&& !outParentLinks[i]->getDestination()->hasEvidence()) FIXME{
|
||||
if (DL >= 2) {
|
||||
cout << " calculating " << outParentLinks[i]->toString() << endl;
|
||||
}
|
||||
outParentLinks[i]->setNextMessage (getMessage (outParentLinks[i]));
|
||||
outParentLinks[i]->updateResidual();
|
||||
EdgeMap::iterator iter = edgeMap_.find (outParentLinks[i]);
|
||||
sortedOrder_.erase (iter->second);
|
||||
iter->second = sortedOrder_.insert (outParentLinks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPSolver::updatePiValues (BayesNode* x)
|
||||
{
|
||||
// π(Xi)
|
||||
if (DL >= 3) {
|
||||
cout << "updating " << PI << " values for " << x->getLabel() << endl;
|
||||
}
|
||||
CEdgeSet parentLinks = M(x)->getIncomingParentLinks();
|
||||
assert (x->getParents() == parentLinks.size());
|
||||
const vector<CptEntry>& entries = x->getCptEntries();
|
||||
stringstream* calcs1 = 0;
|
||||
stringstream* calcs2 = 0;
|
||||
|
||||
ParamSet messageProducts (entries.size());
|
||||
for (unsigned k = 0; k < entries.size(); k++) {
|
||||
if (DL >= 5) {
|
||||
calcs1 = new stringstream;
|
||||
calcs2 = new stringstream;
|
||||
}
|
||||
double messageProduct = 1.0;
|
||||
const DConf& conf = entries[k].getDomainConfiguration();
|
||||
for (unsigned i = 0; i < parentLinks.size(); i++) {
|
||||
assert (parentLinks[i]->getSource() == parents[i]);
|
||||
assert (parentLinks[i]->getDestination() == x);
|
||||
messageProduct *= parentLinks[i]->getMessage()[conf[i]];
|
||||
if (DL >= 5) {
|
||||
if (i != 0) *calcs1 << "." ;
|
||||
if (i != 0) *calcs2 << "*" ;
|
||||
*calcs1 << PI << "(" << parentLinks[i]->getSource()->getLabel();
|
||||
*calcs1 << " --> " << x->getLabel() << ")" ;
|
||||
*calcs1 << "[" ;
|
||||
*calcs1 << parentLinks[i]->getSource()->getDomain()[conf[i]];
|
||||
*calcs1 << "]";
|
||||
*calcs2 << parentLinks[i]->getMessage()[conf[i]];
|
||||
}
|
||||
}
|
||||
messageProducts[k] = messageProduct;
|
||||
if (DL >= 5) {
|
||||
cout << " mp" << k;
|
||||
cout << " = " << (*calcs1).str();
|
||||
if (parentLinks.size() == 1) {
|
||||
cout << " = " << messageProduct << endl;
|
||||
} else {
|
||||
cout << " = " << (*calcs2).str();
|
||||
cout << " = " << messageProduct << endl;
|
||||
}
|
||||
delete calcs1;
|
||||
delete calcs2;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
double sum = 0.0;
|
||||
if (DL >= 5) {
|
||||
calcs1 = new stringstream;
|
||||
calcs2 = new stringstream;
|
||||
}
|
||||
for (unsigned k = 0; k < entries.size(); k++) {
|
||||
sum += x->getProbability (xi, entries[k]) * messageProducts[k];
|
||||
if (DL >= 5) {
|
||||
if (k != 0) *calcs1 << " + " ;
|
||||
if (k != 0) *calcs2 << " + " ;
|
||||
*calcs1 << x->cptEntryToString (xi, entries[k]);
|
||||
*calcs1 << ".mp" << k;
|
||||
*calcs2 << x->getProbability (xi, entries[k]);
|
||||
*calcs2 << "*" << messageProducts[k];
|
||||
}
|
||||
}
|
||||
M(x)->setPiValue (xi, sum);
|
||||
if (DL >= 5) {
|
||||
cout << " " << PI << "(" << x->getLabel() << ")" ;
|
||||
cout << "[" << x->getDomain()[xi] << "]" ;
|
||||
cout << " = " << (*calcs1).str();
|
||||
cout << " = " << (*calcs2).str();
|
||||
cout << " = " << sum << endl;
|
||||
delete calcs1;
|
||||
delete calcs2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPSolver::updateLambdaValues (BayesNode* x)
|
||||
{
|
||||
// λ(Xi)
|
||||
if (DL >= 3) {
|
||||
cout << "updating " << LD << " values for " << x->getLabel() << endl;
|
||||
}
|
||||
CEdgeSet childLinks = M(x)->getIncomingChildLinks();
|
||||
stringstream* calcs1 = 0;
|
||||
stringstream* calcs2 = 0;
|
||||
|
||||
for (unsigned xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
double product = 1.0;
|
||||
if (DL >= 5) {
|
||||
calcs1 = new stringstream;
|
||||
calcs2 = new stringstream;
|
||||
}
|
||||
for (unsigned i = 0; i < childLinks.size(); i++) {
|
||||
assert (childLinks[i]->getDestination() == x);
|
||||
product *= childLinks[i]->getMessage()[xi];
|
||||
if (DL >= 5) {
|
||||
if (i != 0) *calcs1 << "." ;
|
||||
if (i != 0) *calcs2 << "*" ;
|
||||
*calcs1 << LD << "(" << childLinks[i]->getSource()->getLabel();
|
||||
*calcs1 << "-->" << x->getLabel() << ")" ;
|
||||
*calcs1 << "[" << x->getDomain()[xi] << "]" ;
|
||||
*calcs2 << childLinks[i]->getMessage()[xi];
|
||||
}
|
||||
}
|
||||
M(x)->setLambdaValue (xi, product);
|
||||
if (DL >= 5) {
|
||||
cout << " " << LD << "(" << x->getLabel() << ")" ;
|
||||
cout << "[" << x->getDomain()[xi] << "]" ;
|
||||
cout << " = " << (*calcs1).str();
|
||||
if (childLinks.size() == 1) {
|
||||
cout << " = " << product << endl;
|
||||
} else {
|
||||
cout << " = " << (*calcs2).str();
|
||||
cout << " = " << product << endl;
|
||||
}
|
||||
delete calcs1;
|
||||
delete calcs2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
BPSolver::calculateNextPiMessage (Edge* edge)
|
||||
{
|
||||
// πX(Zi)
|
||||
BayesNode* z = edge->getSource();
|
||||
BayesNode* x = edge->getDestination();
|
||||
ParamSet zxPiNextMessage (z->getDomainSize());
|
||||
CEdgeSet zChildLinks = M(z)->getIncomingChildLinks();
|
||||
stringstream* calcs1 = 0;
|
||||
stringstream* calcs2 = 0;
|
||||
|
||||
|
||||
for (unsigned zi = 0; zi < z->getDomainSize(); zi++) {
|
||||
double product = M(z)->getPiValue (zi);
|
||||
if (DL >= 5) {
|
||||
calcs1 = new stringstream;
|
||||
calcs2 = new stringstream;
|
||||
*calcs1 << PI << "(" << z->getLabel() << ")";
|
||||
*calcs1 << "[" << z->getDomain()[zi] << "]" ;
|
||||
*calcs2 << product;
|
||||
}
|
||||
for (unsigned i = 0; i < zChildLinks.size(); i++) {
|
||||
assert (zChildLinks[i]->getDestination() == z);
|
||||
if (zChildLinks[i]->getSource() != x) {
|
||||
product *= zChildLinks[i]->getMessage()[zi];
|
||||
if (DL >= 5) {
|
||||
*calcs1 << "." << LD << "(" << zChildLinks[i]->getSource()->getLabel();
|
||||
*calcs1 << "-->" << z->getLabel() << ")";
|
||||
*calcs1 << "[" << z->getDomain()[zi] + "]" ;
|
||||
*calcs2 << " * " << zChildLinks[i]->getMessage()[zi];
|
||||
}
|
||||
}
|
||||
}
|
||||
zxPiNextMessage[zi] = product;
|
||||
if (DL >= 5) {
|
||||
cout << " " << PI << "(" << z->getLabel();
|
||||
cout << "-->" << x->getLabel() << ")" ;
|
||||
cout << "[" << z->getDomain()[zi] << "]" ;
|
||||
cout << " = " << (*calcs1).str();
|
||||
if (zChildLinks.size() == 1) {
|
||||
cout << " = " << product << endl;
|
||||
} else {
|
||||
cout << " = " << (*calcs2).str();
|
||||
cout << " = " << product << endl;
|
||||
}
|
||||
delete calcs1;
|
||||
delete calcs2;
|
||||
}
|
||||
}
|
||||
return zxPiNextMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
BPSolver::calculateNextLambdaMessage (Edge* edge)
|
||||
{
|
||||
// λY(Xi)
|
||||
BayesNode* y = edge->getSource();
|
||||
BayesNode* x = edge->getDestination();
|
||||
if (!M(y)->receivedBottomInfluence()) {
|
||||
//cout << "returning 1" << endl;
|
||||
//return edge->getMessage();
|
||||
}
|
||||
if (x->hasEvidence()) {
|
||||
//cout << "returning 2" << endl;
|
||||
//return edge->getMessage();
|
||||
}
|
||||
ParamSet yxLambdaNextMessage (x->getDomainSize());
|
||||
CEdgeSet yParentLinks = M(y)->getIncomingParentLinks();
|
||||
const vector<CptEntry>& allEntries = y->getCptEntries();
|
||||
int parentIndex = y->getIndexOfParent (x);
|
||||
stringstream* calcs1 = 0;
|
||||
stringstream* calcs2 = 0;
|
||||
|
||||
vector<CptEntry> entries;
|
||||
DConstraint constr = make_pair (parentIndex, 0);
|
||||
for (unsigned i = 0; i < allEntries.size(); i++) {
|
||||
if (allEntries[i].matchConstraints(constr)) {
|
||||
entries.push_back (allEntries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ParamSet messageProducts (entries.size());
|
||||
for (unsigned k = 0; k < entries.size(); k++) {
|
||||
if (DL >= 5) {
|
||||
calcs1 = new stringstream;
|
||||
calcs2 = new stringstream;
|
||||
}
|
||||
double messageProduct = 1.0;
|
||||
const DConf& conf = entries[k].getDomainConfiguration();
|
||||
for (unsigned i = 0; i < yParentLinks.size(); i++) {
|
||||
assert (yParentLinks[i]->getDestination() == y);
|
||||
if (yParentLinks[i]->getSource() != x) {
|
||||
if (DL >= 5) {
|
||||
if (messageProduct != 1.0) *calcs1 << "*" ;
|
||||
if (messageProduct != 1.0) *calcs2 << "*" ;
|
||||
*calcs1 << PI << "(" << yParentLinks[i]->getSource()->getLabel();
|
||||
*calcs1 << "-->" << y->getLabel() << ")" ;
|
||||
*calcs1 << "[" ;
|
||||
*calcs1 << yParentLinks[i]->getSource()->getDomain()[conf[i]];
|
||||
*calcs1 << "]" ;
|
||||
*calcs2 << yParentLinks[i]->getMessage()[conf[i]];
|
||||
}
|
||||
messageProduct *= yParentLinks[i]->getMessage()[conf[i]];
|
||||
}
|
||||
}
|
||||
messageProducts[k] = messageProduct;
|
||||
if (DL >= 5) {
|
||||
cout << " mp" << k;
|
||||
cout << " = " << (*calcs1).str();
|
||||
if (yParentLinks.size() == 1) {
|
||||
cout << 1 << endl;
|
||||
} else if (yParentLinks.size() == 2) {
|
||||
cout << " = " << messageProduct << endl;
|
||||
} else {
|
||||
cout << " = " << (*calcs2).str();
|
||||
cout << " = " << messageProduct << endl;
|
||||
}
|
||||
delete calcs1;
|
||||
delete calcs2;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
if (DL >= 5) {
|
||||
calcs1 = new stringstream;
|
||||
calcs2 = new stringstream;
|
||||
}
|
||||
vector<CptEntry> entries;
|
||||
DConstraint constr = make_pair (parentIndex, xi);
|
||||
for (unsigned i = 0; i < allEntries.size(); i++) {
|
||||
if (allEntries[i].matchConstraints(constr)) {
|
||||
entries.push_back (allEntries[i]);
|
||||
}
|
||||
}
|
||||
double outerSum = 0.0;
|
||||
for (unsigned yi = 0; yi < y->getDomainSize(); yi++) {
|
||||
if (DL >= 5) {
|
||||
(yi != 0) ? *calcs1 << " + {" : *calcs1 << "{" ;
|
||||
(yi != 0) ? *calcs2 << " + {" : *calcs2 << "{" ;
|
||||
}
|
||||
double innerSum = 0.0;
|
||||
for (unsigned k = 0; k < entries.size(); k++) {
|
||||
if (DL >= 5) {
|
||||
if (k != 0) *calcs1 << " + " ;
|
||||
if (k != 0) *calcs2 << " + " ;
|
||||
*calcs1 << y->cptEntryToString (yi, entries[k]);
|
||||
*calcs1 << ".mp" << k;
|
||||
*calcs2 << y->getProbability (yi, entries[k]);
|
||||
*calcs2 << "*" << messageProducts[k];
|
||||
}
|
||||
innerSum += y->getProbability (yi, entries[k]) * messageProducts[k];
|
||||
}
|
||||
outerSum += innerSum * M(y)->getLambdaValue (yi);
|
||||
if (DL >= 5) {
|
||||
*calcs1 << "}." << LD << "(" << y->getLabel() << ")" ;
|
||||
*calcs1 << "[" << y->getDomain()[yi] << "]";
|
||||
*calcs2 << "}*" << M(y)->getLambdaValue (yi);
|
||||
}
|
||||
}
|
||||
yxLambdaNextMessage[xi] = outerSum;
|
||||
if (DL >= 5) {
|
||||
cout << " " << LD << "(" << y->getLabel();
|
||||
cout << "-->" << x->getLabel() << ")" ;
|
||||
cout << "[" << x->getDomain()[xi] << "]" ;
|
||||
cout << " = " << (*calcs1).str();
|
||||
cout << " = " << (*calcs2).str();
|
||||
cout << " = " << outerSum << endl;
|
||||
delete calcs1;
|
||||
delete calcs2;
|
||||
}
|
||||
}
|
||||
return yxLambdaNextMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
BPSolver::getJointByJunctionNode (const VidSet& jointVids) const
|
||||
{
|
||||
BnNodeSet jointVars;
|
||||
for (unsigned i = 0; i < jointVids.size(); i++) {
|
||||
jointVars.push_back (bn_->getBayesNode (jointVids[i]));
|
||||
}
|
||||
|
||||
BayesNet* mrn = bn_->getMinimalRequesiteNetwork (jointVids);
|
||||
|
||||
BnNodeSet parents;
|
||||
unsigned dsize = 1;
|
||||
for (unsigned i = 0; i < jointVars.size(); i++) {
|
||||
parents.push_back (mrn->getBayesNode (jointVids[i]));
|
||||
dsize *= jointVars[i]->getDomainSize();
|
||||
}
|
||||
|
||||
unsigned nParams = dsize * dsize;
|
||||
ParamSet params (nParams);
|
||||
|
||||
for (unsigned i = 0; i < nParams; i++) {
|
||||
unsigned row = i / dsize;
|
||||
unsigned col = i % dsize;
|
||||
if (row == col) {
|
||||
params[i] = 1;
|
||||
} else {
|
||||
params[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned maxVid = std::numeric_limits<unsigned>::max();
|
||||
Distribution* dist = new Distribution (params);
|
||||
|
||||
mrn->addNode (maxVid, dsize, NO_EVIDENCE, parents, dist);
|
||||
mrn->setIndexes();
|
||||
|
||||
BPSolver solver (*mrn);
|
||||
solver.runSolver();
|
||||
|
||||
const ParamSet& results = solver.getPosterioriOf (maxVid);
|
||||
|
||||
delete mrn;
|
||||
delete dist;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
BPSolver::getJointByChainRule (const VidSet& jointVids) const
|
||||
{
|
||||
BnNodeSet jointVars;
|
||||
for (unsigned i = 0; i < jointVids.size(); i++) {
|
||||
jointVars.push_back (bn_->getBayesNode (jointVids[i]));
|
||||
}
|
||||
|
||||
BayesNet* mrn = bn_->getMinimalRequesiteNetwork (jointVids[0]);
|
||||
BPSolver solver (*mrn);
|
||||
solver.runSolver();
|
||||
ParamSet prevBeliefs = solver.getPosterioriOf (jointVids[0]);
|
||||
delete mrn;
|
||||
|
||||
VarSet observedVars = {jointVars[0]};
|
||||
|
||||
for (unsigned i = 1; i < jointVids.size(); i++) {
|
||||
mrn = bn_->getMinimalRequesiteNetwork (jointVids[i]);
|
||||
ParamSet newBeliefs;
|
||||
vector<DConf> confs =
|
||||
Util::getDomainConfigurations (observedVars);
|
||||
for (unsigned j = 0; j < confs.size(); j++) {
|
||||
for (unsigned k = 0; k < observedVars.size(); k++) {
|
||||
if (!observedVars[k]->hasEvidence()) {
|
||||
BayesNode* node = mrn->getBayesNode (observedVars[k]->getVarId());
|
||||
if (node) {
|
||||
node->setEvidence (confs[j][k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
BPSolver solver (*mrn);
|
||||
solver.runSolver();
|
||||
ParamSet beliefs = solver.getPosterioriOf (jointVids[i]);
|
||||
for (unsigned k = 0; k < beliefs.size(); k++) {
|
||||
newBeliefs.push_back (beliefs[k]);
|
||||
}
|
||||
}
|
||||
|
||||
int count = -1;
|
||||
for (unsigned j = 0; j < newBeliefs.size(); j++) {
|
||||
if (j % jointVars[i]->getDomainSize() == 0) {
|
||||
count ++;
|
||||
}
|
||||
newBeliefs[j] *= prevBeliefs[count];
|
||||
}
|
||||
prevBeliefs = newBeliefs;
|
||||
observedVars.push_back (jointVars[i]);
|
||||
delete mrn;
|
||||
}
|
||||
return prevBeliefs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPSolver::printMessageStatusOf (const BayesNode* var) const
|
||||
{
|
||||
cout << left;
|
||||
cout << setw (10) << "domain" ;
|
||||
cout << setw (20) << PI << "(" + var->getLabel() + ")" ;
|
||||
cout << setw (20) << LD << "(" + var->getLabel() + ")" ;
|
||||
cout << setw (16) << "belief" ;
|
||||
cout << endl;
|
||||
cout << "--------------------------------" ;
|
||||
cout << "--------------------------------" ;
|
||||
cout << endl;
|
||||
|
||||
BPNodeInfo* x = M(var);
|
||||
ParamSet& piVals = x->getPiValues();
|
||||
ParamSet& ldVals = x->getLambdaValues();
|
||||
ParamSet beliefs = x->getBeliefs();
|
||||
const Domain& domain = var->getDomain();
|
||||
CBnNodeSet& childs = var->getChilds();
|
||||
|
||||
for (unsigned xi = 0; xi < var->getDomainSize(); xi++) {
|
||||
cout << setw (10) << domain[xi];
|
||||
cout << setw (19) << piVals[xi];
|
||||
cout << setw (19) << ldVals[xi];
|
||||
cout.precision (PRECISION);
|
||||
cout << setw (16) << beliefs[xi];
|
||||
cout << endl;
|
||||
}
|
||||
cout << endl;
|
||||
if (childs.size() > 0) {
|
||||
string s = "(" + var->getLabel() + ")" ;
|
||||
for (unsigned j = 0; j < childs.size(); j++) {
|
||||
cout << setw (10) << "domain" ;
|
||||
cout << setw (28) << PI + childs[j]->getLabel() + s;
|
||||
cout << setw (28) << LD + childs[j]->getLabel() + s;
|
||||
cout << endl;
|
||||
cout << "--------------------------------" ;
|
||||
cout << "--------------------------------" ;
|
||||
cout << endl;
|
||||
/* FIXME
|
||||
const ParamSet& piMessage = x->getPiMessage (childs[j]);
|
||||
const ParamSet& lambdaMessage = x->getLambdaMessage (childs[j]);
|
||||
for (unsigned xi = 0; xi < var->getDomainSize(); xi++) {
|
||||
cout << setw (10) << domain[xi];
|
||||
cout.precision (PRECISION);
|
||||
cout << setw (27) << piMessage[xi];
|
||||
cout.precision (PRECISION);
|
||||
cout << setw (27) << lambdaMessage[xi];
|
||||
cout << endl;
|
||||
}
|
||||
cout << endl;
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BPSolver::printAllMessageStatus (void) const
|
||||
{
|
||||
CBnNodeSet nodes = bn_->getBayesNodes();
|
||||
for (unsigned i = 0; i < nodes.size(); i++) {
|
||||
printMessageStatusOf (nodes[i]);
|
||||
}
|
||||
}
|
||||
|
@ -1,192 +0,0 @@
|
||||
#ifndef BP_BP_SOLVER_H
|
||||
#define BP_BP_SOLVER_H
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "Solver.h"
|
||||
#include "BayesNet.h"
|
||||
#include "BPNodeInfo.h"
|
||||
#include "Shared.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class BPNodeInfo;
|
||||
|
||||
static const string PI = "pi" ;
|
||||
static const string LD = "ld" ;
|
||||
|
||||
|
||||
enum MessageType {PI_MSG, LAMBDA_MSG};
|
||||
enum JointCalcType {CHAIN_RULE, JUNCTION_NODE};
|
||||
|
||||
class Edge
|
||||
{
|
||||
public:
|
||||
Edge (BayesNode* s, BayesNode* d, MessageType t)
|
||||
{
|
||||
source_ = s;
|
||||
destin_ = d;
|
||||
type_ = t;
|
||||
if (type_ == PI_MSG) {
|
||||
currMsg_.resize (s->getDomainSize(), 1);
|
||||
nextMsg_.resize (s->getDomainSize(), 1);
|
||||
} else {
|
||||
currMsg_.resize (d->getDomainSize(), 1);
|
||||
nextMsg_.resize (d->getDomainSize(), 1);
|
||||
}
|
||||
msgSended_ = false;
|
||||
residual_ = 0.0;
|
||||
}
|
||||
|
||||
//void setMessage (ParamSet msg)
|
||||
//{
|
||||
// Util::normalize (msg);
|
||||
// residual_ = Util::getMaxNorm (currMsg_, msg);
|
||||
// currMsg_ = msg;
|
||||
//}
|
||||
|
||||
void setNextMessage (CParamSet msg)
|
||||
{
|
||||
nextMsg_ = msg;
|
||||
Util::normalize (nextMsg_);
|
||||
residual_ = Util::getMaxNorm (currMsg_, nextMsg_);
|
||||
}
|
||||
|
||||
void updateMessage (void)
|
||||
{
|
||||
currMsg_ = nextMsg_;
|
||||
if (DL >= 3) {
|
||||
cout << "updating " << toString() << endl;
|
||||
}
|
||||
msgSended_ = true;
|
||||
}
|
||||
|
||||
void updateResidual (void)
|
||||
{
|
||||
residual_ = Util::getMaxNorm (currMsg_, nextMsg_);
|
||||
}
|
||||
|
||||
string toString (void) const
|
||||
{
|
||||
stringstream ss;
|
||||
if (type_ == PI_MSG) {
|
||||
ss << PI;
|
||||
} else if (type_ == LAMBDA_MSG) {
|
||||
ss << LD;
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
ss << "(" << source_->getLabel();
|
||||
ss << " --> " << destin_->getLabel() << ")" ;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
BayesNode* getSource (void) const { return source_; }
|
||||
BayesNode* getDestination (void) const { return destin_; }
|
||||
MessageType getMessageType (void) const { return type_; }
|
||||
CParamSet getMessage (void) const { return currMsg_; }
|
||||
bool messageWasSended (void) const { return msgSended_; }
|
||||
double getResidual (void) const { return residual_; }
|
||||
void clearResidual (void) { residual_ = 0.0; }
|
||||
|
||||
private:
|
||||
BayesNode* source_;
|
||||
BayesNode* destin_;
|
||||
MessageType type_;
|
||||
ParamSet currMsg_;
|
||||
ParamSet nextMsg_;
|
||||
bool msgSended_;
|
||||
double residual_;
|
||||
};
|
||||
|
||||
|
||||
class BPSolver : public Solver
|
||||
{
|
||||
public:
|
||||
BPSolver (const BayesNet&);
|
||||
~BPSolver (void);
|
||||
|
||||
void runSolver (void);
|
||||
ParamSet getPosterioriOf (Vid) const;
|
||||
ParamSet getJointDistributionOf (const VidSet&);
|
||||
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN (BPSolver);
|
||||
|
||||
void initializeSolver (void);
|
||||
void runPolyTreeSolver (void);
|
||||
void runLoopySolver (void);
|
||||
void maxResidualSchedule (void);
|
||||
bool converged (void) const;
|
||||
void updatePiValues (BayesNode*);
|
||||
void updateLambdaValues (BayesNode*);
|
||||
ParamSet calculateNextLambdaMessage (Edge* edge);
|
||||
ParamSet calculateNextPiMessage (Edge* edge);
|
||||
ParamSet getJointByJunctionNode (const VidSet&) const;
|
||||
ParamSet getJointByChainRule (const VidSet&) const;
|
||||
void printMessageStatusOf (const BayesNode*) const;
|
||||
void printAllMessageStatus (void) const;
|
||||
|
||||
ParamSet getMessage (Edge* edge)
|
||||
{
|
||||
if (DL >= 3) {
|
||||
cout << " calculating " << edge->toString() << endl;
|
||||
}
|
||||
if (edge->getMessageType() == PI_MSG) {
|
||||
return calculateNextPiMessage (edge);
|
||||
} else if (edge->getMessageType() == LAMBDA_MSG) {
|
||||
return calculateNextLambdaMessage (edge);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
return ParamSet();
|
||||
}
|
||||
|
||||
void updateValues (Edge* edge)
|
||||
{
|
||||
if (!edge->getDestination()->hasEvidence()) {
|
||||
if (edge->getMessageType() == PI_MSG) {
|
||||
updatePiValues (edge->getDestination());
|
||||
} else if (edge->getMessageType() == LAMBDA_MSG) {
|
||||
updateLambdaValues (edge->getDestination());
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BPNodeInfo* M (const BayesNode* node) const
|
||||
{
|
||||
assert (node);
|
||||
assert (node == bn_->getBayesNode (node->getVarId()));
|
||||
assert (node->getIndex() < nodesI_.size());
|
||||
return nodesI_[node->getIndex()];
|
||||
}
|
||||
|
||||
const BayesNet* bn_;
|
||||
vector<BPNodeInfo*> nodesI_;
|
||||
unsigned nIter_;
|
||||
vector<Edge*> links_;
|
||||
bool useAlwaysLoopySolver_;
|
||||
JointCalcType jointCalcType_;
|
||||
|
||||
struct compare
|
||||
{
|
||||
inline bool operator() (const Edge* e1, const Edge* e2)
|
||||
{
|
||||
return e1->getResidual() > e2->getResidual();
|
||||
}
|
||||
};
|
||||
|
||||
typedef multiset<Edge*, compare> SortedOrder;
|
||||
SortedOrder sortedOrder_;
|
||||
|
||||
typedef map<Edge*, SortedOrder::iterator> EdgeMap;
|
||||
EdgeMap edgeMap_;
|
||||
|
||||
};
|
||||
|
||||
#endif //BP_BP_SOLVER_H
|
||||
|
@ -1,811 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#include "BpNetwork.h"
|
||||
#include "BpNode.h"
|
||||
#include "CptEntry.h"
|
||||
|
||||
BpNetwork::BpNetwork (void)
|
||||
{
|
||||
schedule_ = SEQUENTIAL_SCHEDULE;
|
||||
maxIter_ = 150;
|
||||
stableThreashold_ = 0.00000000000000000001;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BpNetwork::~BpNetwork (void)
|
||||
{
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
delete nodes_[i];
|
||||
}
|
||||
nodes_.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::setSolverParameters (Schedule schedule,
|
||||
int maxIter,
|
||||
double stableThreashold)
|
||||
{
|
||||
if (maxIter <= 0) {
|
||||
cerr << "error: maxIter must be greater or equal to 1" << endl;
|
||||
abort();
|
||||
}
|
||||
if (stableThreashold <= 0.0 || stableThreashold >= 1.0) {
|
||||
cerr << "error: stableThreashold must be greater than 0.0 " ;
|
||||
cerr << "and lesser than 1.0" << endl;
|
||||
abort();
|
||||
}
|
||||
schedule_ = schedule;
|
||||
maxIter_ = maxIter;
|
||||
stableThreashold_ = stableThreashold;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::runSolver (BayesianNode* queryVar)
|
||||
{
|
||||
vector<BayesianNode*> queryVars;
|
||||
queryVars.push_back (queryVar);
|
||||
runSolver (queryVars);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::runSolver (vector<BayesianNode*> queryVars)
|
||||
{
|
||||
if (queryVars.size() > 1) {
|
||||
addJunctionNode (queryVars);
|
||||
}
|
||||
else {
|
||||
string varName = queryVars[0]->getVariableName();
|
||||
queryNode_ = static_cast<BpNode*> (getNode (varName));
|
||||
}
|
||||
|
||||
if (!isPolyTree()) {
|
||||
if (DL_ >= 1) {
|
||||
cout << "The graph is not single connected. " ;
|
||||
cout << "Iterative belief propagation will be used." ;
|
||||
cout << endl << endl;
|
||||
}
|
||||
schedule_ = PARALLEL_SCHEDULE;
|
||||
}
|
||||
|
||||
if (schedule_ == SEQUENTIAL_SCHEDULE) {
|
||||
initializeSolver (queryVars);
|
||||
runNeapolitanSolver();
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
if (nodes_[i]->hasEvidence()) {
|
||||
BpNode* v = static_cast<BpNode*> (nodes_[i]);
|
||||
addEvidence (v);
|
||||
vector<BpNode*> parents = cast (v->getParents());
|
||||
for (unsigned int i = 0; i < parents.size(); i++) {
|
||||
if (!parents[i]->hasEvidence()) {
|
||||
sendLambdaMessage (v, parents[i]);
|
||||
}
|
||||
}
|
||||
vector<BpNode*> childs = cast (v->getChilds());
|
||||
for (unsigned int i = 0; i < childs.size(); i++) {
|
||||
sendPiMessage (v, childs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (schedule_ == PARALLEL_SCHEDULE) {
|
||||
BpNode::enableParallelSchedule();
|
||||
initializeSolver (queryVars);
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
if (nodes_[i]->hasEvidence()) {
|
||||
addEvidence (static_cast<BpNode*> (nodes_[i]));
|
||||
}
|
||||
}
|
||||
runIterativeBpSolver();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::printCurrentStatus (void)
|
||||
{
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
printCurrentStatusOf (static_cast<BpNode*> (nodes_[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::printCurrentStatusOf (BpNode* x)
|
||||
{
|
||||
vector<BpNode*> childs = cast (x->getChilds());
|
||||
vector<string> domain = x->getDomain();
|
||||
|
||||
cout << left;
|
||||
cout << setw (10) << "domain" ;
|
||||
cout << setw (20) << "π(" + x->getVariableName() + ")" ;
|
||||
cout << setw (20) << "λ(" + x->getVariableName() + ")" ;
|
||||
cout << setw (16) << "belief" ;
|
||||
cout << endl;
|
||||
|
||||
cout << "--------------------------------" ;
|
||||
cout << "--------------------------------" ;
|
||||
cout << endl;
|
||||
|
||||
double* piValues = x->getPiValues();
|
||||
double* lambdaValues = x->getLambdaValues();
|
||||
double* beliefs = x->getBeliefs();
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
cout << setw (10) << domain[xi];
|
||||
cout << setw (19) << piValues[xi];
|
||||
cout << setw (19) << lambdaValues[xi];
|
||||
cout.precision (PRECISION_);
|
||||
cout << setw (16) << beliefs[xi];
|
||||
cout << endl;
|
||||
}
|
||||
cout << endl;
|
||||
if (childs.size() > 0) {
|
||||
string s = "(" + x->getVariableName() + ")" ;
|
||||
for (unsigned int j = 0; j < childs.size(); j++) {
|
||||
cout << setw (10) << "domain" ;
|
||||
cout << setw (28) << "π" + childs[j]->getVariableName() + s;
|
||||
cout << setw (28) << "λ" + childs[j]->getVariableName() + s;
|
||||
cout << endl;
|
||||
cout << "--------------------------------" ;
|
||||
cout << "--------------------------------" ;
|
||||
cout << endl;
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
cout << setw (10) << domain[xi];
|
||||
cout.precision (PRECISION_);
|
||||
cout << setw (27) << x->getPiMessage(childs[j], xi);
|
||||
cout.precision (PRECISION_);
|
||||
cout << setw (27) << x->getLambdaMessage(childs[j], xi);
|
||||
cout << endl;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::printBeliefs (void)
|
||||
{
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
BpNode* x = static_cast<BpNode*> (nodes_[i]);
|
||||
vector<string> domain = x->getDomain();
|
||||
cout << setw (20) << left << x->getVariableName() ;
|
||||
cout << setw (26) << "belief" ;
|
||||
cout << endl;
|
||||
cout << "--------------------------------------" ;
|
||||
cout << endl;
|
||||
double* beliefs = x->getBeliefs();
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
cout << setw (20) << domain[xi];
|
||||
cout.precision (PRECISION_);
|
||||
cout << setw (26) << beliefs[xi];
|
||||
cout << endl;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
vector<double>
|
||||
BpNetwork::getBeliefs (void)
|
||||
{
|
||||
return getBeliefs (queryNode_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
vector<double>
|
||||
BpNetwork::getBeliefs (BpNode* x)
|
||||
{
|
||||
double* beliefs = x->getBeliefs();
|
||||
vector<double> beliefsVec;
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
beliefsVec.push_back (beliefs[xi]);
|
||||
}
|
||||
return beliefsVec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::initializeSolver (vector<BayesianNode*> queryVars)
|
||||
{
|
||||
if (DL_ >= 1) {
|
||||
cout << "Initializing solver" << endl;
|
||||
if (schedule_ == SEQUENTIAL_SCHEDULE) {
|
||||
cout << "-> schedule = sequential" << endl;
|
||||
} else {
|
||||
cout << "-> schedule = parallel" << endl;
|
||||
}
|
||||
cout << "-> max iters = " << maxIter_ << endl;
|
||||
cout << "-> stable threashold = " << stableThreashold_ << endl;
|
||||
cout << "-> query vars = " ;
|
||||
for (unsigned int i = 0; i < queryVars.size(); i++) {
|
||||
cout << queryVars[i]->getVariableName() << " " ;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
nIter_ = 0;
|
||||
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
BpNode* node = static_cast<BpNode*> (nodes_[i]);
|
||||
node->allocateMemory();
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
BpNode* x = static_cast<BpNode*> (nodes_[i]);
|
||||
|
||||
double* piValues = x->getPiValues();
|
||||
double* lambdaValues = x->getLambdaValues();
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
piValues[xi] = 1.0;
|
||||
lambdaValues[xi] = 1.0;
|
||||
}
|
||||
|
||||
vector<BpNode*> xChilds = cast (x->getChilds());
|
||||
for (unsigned int j = 0; j < xChilds.size(); j++) {
|
||||
double* piMessages = x->getPiMessages (xChilds[j]);
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
piMessages[xi] = 1.0;
|
||||
//x->setPiMessage (xChilds[j], xi, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
vector<BpNode*> xParents = cast (x->getParents());
|
||||
for (unsigned int j = 0; j < xParents.size(); j++) {
|
||||
double* lambdaMessages = xParents[j]->getLambdaMessages (x);
|
||||
for (int xi = 0; xi < xParents[j]->getDomainSize(); xi++) {
|
||||
lambdaMessages[xi] = 1.0;
|
||||
//xParents[j]->setLambdaMessage (x, xi, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
BpNode* x = static_cast<BpNode*> (nodes_[i]);
|
||||
x->normalizeMessages();
|
||||
}
|
||||
printCurrentStatus();
|
||||
|
||||
|
||||
vector<BpNode*> roots = cast (getRootNodes());
|
||||
for (unsigned int i = 0; i < roots.size(); i++) {
|
||||
double* params = roots[i]->getParameters();
|
||||
double* piValues = roots[i]->getPiValues();
|
||||
for (int ri = 0; ri < roots[i]->getDomainSize(); ri++) {
|
||||
piValues[ri] = params[ri];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::addJunctionNode (vector<BayesianNode*> queryVars)
|
||||
{
|
||||
const string VAR_NAME = "_Jn";
|
||||
int nStates = 1;
|
||||
vector<BayesianNode*> parents;
|
||||
vector<string> domain;
|
||||
for (unsigned int i = 0; i < queryVars.size(); i++) {
|
||||
parents.push_back (queryVars[i]);
|
||||
nStates *= queryVars[i]->getDomainSize();
|
||||
}
|
||||
|
||||
for (int i = 0; i < nStates; i++) {
|
||||
stringstream ss;
|
||||
ss << "_jn" << i;
|
||||
domain.push_back (ss.str()); // FIXME make domain optional
|
||||
}
|
||||
|
||||
int nParams = nStates * nStates;
|
||||
double* params = new double [nParams];
|
||||
for (int i = 0; i < nParams; i++) {
|
||||
int row = i / nStates;
|
||||
int col = i % nStates;
|
||||
if (row == col) {
|
||||
params[i] = 1;
|
||||
} else {
|
||||
params[i] = 0;
|
||||
}
|
||||
}
|
||||
addNode (VAR_NAME, parents, params, nParams, domain);
|
||||
queryNode_ = static_cast<BpNode*> (getNode (VAR_NAME));
|
||||
printNetwork();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::addEvidence (BpNode* v)
|
||||
{
|
||||
if (DL_ >= 1) {
|
||||
cout << "Adding evidence: node " ;
|
||||
cout << "`" << v->getVariableName() << "' was instantiated as " ;
|
||||
cout << "`" << v->getDomain()[v->getEvidence()] << "'" ;
|
||||
cout << endl;
|
||||
}
|
||||
double* piValues = v->getPiValues();
|
||||
double* lambdaValues = v->getLambdaValues();
|
||||
for (int vi = 0; vi < v->getDomainSize(); vi++) {
|
||||
if (vi == v->getEvidence()) {
|
||||
piValues[vi] = 1.0;
|
||||
lambdaValues[vi] = 1.0;
|
||||
} else {
|
||||
piValues[vi] = 0.0;
|
||||
lambdaValues[vi] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::runNeapolitanSolver (void)
|
||||
{
|
||||
vector<BpNode*> roots = cast (getRootNodes());
|
||||
for (unsigned int i = 0; i < roots.size(); i++) {
|
||||
vector<BpNode*> childs = cast (roots[i]->getChilds());
|
||||
for (unsigned int j = 0; j < childs.size(); j++) {
|
||||
sendPiMessage (roots[i], static_cast<BpNode*> (childs[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::sendPiMessage (BpNode* z, BpNode* x)
|
||||
{
|
||||
nIter_ ++;
|
||||
if (!(maxIter_ == -1 || nIter_ < maxIter_)) {
|
||||
cout << "the maximum number of iterations was achieved, terminating..." ;
|
||||
cout << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (DL_ >= 1) {
|
||||
cout << "π message " << z->getVariableName();
|
||||
cout << " --> " << x->getVariableName() << endl;
|
||||
}
|
||||
|
||||
updatePiMessages(z, x);
|
||||
|
||||
if (!x->hasEvidence()) {
|
||||
updatePiValues (x);
|
||||
vector<BpNode*> xChilds = cast (x->getChilds());
|
||||
for (unsigned int i = 0; i < xChilds.size(); i++) {
|
||||
sendPiMessage (x, xChilds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool isAllOnes = true;
|
||||
double* lambdaValues = x->getLambdaValues();
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
if (lambdaValues[xi] != 1.0) {
|
||||
isAllOnes = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAllOnes) {
|
||||
vector<BpNode*> xParents = cast (x->getParents());
|
||||
for (unsigned int i = 0; i < xParents.size(); i++) {
|
||||
if (xParents[i] != z && !xParents[i]->hasEvidence()) {
|
||||
sendLambdaMessage (x, xParents[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::sendLambdaMessage (BpNode* y, BpNode* x)
|
||||
{
|
||||
nIter_ ++;
|
||||
if (!(maxIter_ == -1 || nIter_ < maxIter_)) {
|
||||
cout << "the maximum number of iterations was achieved, terminating..." ;
|
||||
cout << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (DL_ >= 1) {
|
||||
cout << "λ message " << y->getVariableName();
|
||||
cout << " --> " << x->getVariableName() << endl;
|
||||
}
|
||||
|
||||
updateLambdaMessages (x, y);
|
||||
updateLambdaValues (x);
|
||||
|
||||
vector<BpNode*> xParents = cast (x->getParents());
|
||||
for (unsigned int i = 0; i < xParents.size(); i++) {
|
||||
if (!xParents[i]->hasEvidence()) {
|
||||
sendLambdaMessage (x, xParents[i]);
|
||||
}
|
||||
}
|
||||
|
||||
vector<BpNode*> xChilds = cast (x->getChilds());
|
||||
for (unsigned int i = 0; i < xChilds.size(); i++) {
|
||||
if (xChilds[i] != y) {
|
||||
sendPiMessage (x, xChilds[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::updatePiValues (BpNode* x)
|
||||
{
|
||||
// π(Xi)
|
||||
vector<BpNode*> parents = cast (x->getParents());
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
stringstream calcs1;
|
||||
stringstream calcs2;
|
||||
if (DL_ >= 2) {
|
||||
calcs1 << "π("<< x->getDomain()[xi] << ")" << endl << "= " ;
|
||||
}
|
||||
double sum = 0.0;
|
||||
vector<pair<int, int> > constraints;
|
||||
vector<CptEntry> entries = x->getCptEntriesOfRow (xi);
|
||||
for (unsigned int k = 0; k < entries.size(); k++) {
|
||||
double prod = x->getProbability (entries[k]);
|
||||
if (DL_ >= 2) {
|
||||
if (k != 0) calcs1 << endl << "+ " ;
|
||||
calcs1 << x->entryToString (entries[k]);
|
||||
if (DL_ >= 3) {
|
||||
(k == 0) ? calcs2 << "(" << prod : calcs2 << endl << "+ (" << prod;
|
||||
}
|
||||
}
|
||||
vector<int> insts = entries[k].getDomainInstantiations();
|
||||
for (unsigned int i = 0; i < parents.size(); i++) {
|
||||
double value = parents[i]->getPiMessage (x, insts[i + 1]);
|
||||
prod *= value;
|
||||
if (DL_ >= 2) {
|
||||
calcs1 << ".π" << x->getVariableName();
|
||||
calcs1 << "(" << parents[i]->getDomain()[insts[i + 1]] << ")";
|
||||
if (DL_ >= 3) calcs2 << "x" << value;
|
||||
}
|
||||
}
|
||||
sum += prod;
|
||||
if (DL_ >= 3) calcs2 << ")";
|
||||
}
|
||||
x->setPiValue (xi, sum);
|
||||
if (DL_ >= 2) {
|
||||
cout << calcs1.str();
|
||||
if (DL_ >= 3) cout << endl << "= " << calcs2.str();
|
||||
cout << " = " << sum << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::updatePiMessages (BpNode* z, BpNode* x)
|
||||
{
|
||||
// πX(Zi)
|
||||
vector<BpNode*> zChilds = cast (z->getChilds());
|
||||
for (int zi = 0; zi < z->getDomainSize(); zi++) {
|
||||
stringstream calcs1;
|
||||
stringstream calcs2;
|
||||
if (DL_ >= 2) {
|
||||
calcs1 << "π" << x->getVariableName();
|
||||
calcs1 << "(" << z->getDomain()[zi] << ") = " ;
|
||||
}
|
||||
double prod = z->getPiValue (zi);
|
||||
if (DL_ >= 2) {
|
||||
calcs1 << "π(" << z->getDomain()[zi] << ")" ;
|
||||
if (DL_ >= 3) calcs2 << prod;
|
||||
}
|
||||
for (unsigned int i = 0; i < zChilds.size(); i++) {
|
||||
if (zChilds[i] != x) {
|
||||
double value = z->getLambdaMessage (zChilds[i], zi);
|
||||
prod *= value;
|
||||
if (DL_ >= 2) {
|
||||
calcs1 << ".λ" << zChilds[i]->getVariableName();
|
||||
calcs1 << "(" << z->getDomain()[zi] + ")" ;
|
||||
if (DL_ >= 3) calcs2 << " x " << value;
|
||||
}
|
||||
}
|
||||
}
|
||||
z->setPiMessage (x, zi, prod);
|
||||
if (DL_ >= 2) {
|
||||
cout << calcs1.str();
|
||||
if (DL_ >= 3) cout << " = " << calcs2.str();
|
||||
cout << " = " << prod << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::updateLambdaValues (BpNode* x)
|
||||
{
|
||||
// λ(Xi)
|
||||
vector<BpNode*> childs = cast (x->getChilds());
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
stringstream calcs1;
|
||||
stringstream calcs2;
|
||||
if (DL_ >= 2) {
|
||||
calcs1 << "λ" << "(" << x->getDomain()[xi] << ") = " ;
|
||||
}
|
||||
double prod = 1.0;
|
||||
for (unsigned int i = 0; i < childs.size(); i++) {
|
||||
double val = x->getLambdaMessage (childs[i], xi);
|
||||
prod *= val;
|
||||
if (DL_ >= 2) {
|
||||
if (i != 0) calcs1 << "." ;
|
||||
calcs1 << "λ" << childs[i]->getVariableName();
|
||||
calcs1 << "(" << x->getDomain()[xi] + ")" ;
|
||||
if (DL_ >= 3) (i == 0) ? calcs2 << val : calcs2 << " x " << val;
|
||||
}
|
||||
}
|
||||
x->setLambdaValue (xi, prod);
|
||||
if (DL_ >= 2) {
|
||||
cout << calcs1.str();
|
||||
if (childs.size() == 0) {
|
||||
cout << 1 << endl;
|
||||
} else {
|
||||
if (DL_ >= 3) cout << " = " << calcs2.str();
|
||||
cout << " = " << prod << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::updateLambdaMessages (BpNode* x, BpNode* y)
|
||||
{
|
||||
// λY(Xi)
|
||||
int parentIndex = y->getIndexOfParent (x) + 1;
|
||||
vector<BpNode*> yParents = cast (y->getParents());
|
||||
for (int xi = 0; xi < x->getDomainSize(); xi++) {
|
||||
stringstream calcs1;
|
||||
stringstream calcs2;
|
||||
if (DL_ >= 2) {
|
||||
calcs1 << "λ" << y->getVariableName() ;
|
||||
calcs1 << "(" << x->getDomain()[xi] << ")" << endl << "= " ;
|
||||
}
|
||||
double outer_sum = 0.0;
|
||||
for (int yi = 0; yi < y->getDomainSize(); yi++) {
|
||||
if (DL_ >= 2) {
|
||||
(yi == 0) ? calcs1 << "[" : calcs1 << endl << "+ [" ;
|
||||
if (DL_ >= 3) {
|
||||
(yi == 0) ? calcs2 << "[" : calcs2 << endl << "+ [" ;
|
||||
}
|
||||
}
|
||||
double inner_sum = 0.0;
|
||||
vector<pair<int, int> > constraints;
|
||||
constraints.push_back (make_pair (0, yi));
|
||||
constraints.push_back (make_pair (parentIndex, xi));
|
||||
vector<CptEntry> entries = y->getCptEntries (constraints);
|
||||
for (unsigned int k = 0; k < entries.size(); k++) {
|
||||
double prod = y->getProbability (entries[k]);
|
||||
if (DL_ >= 2) {
|
||||
if (k != 0) calcs1 << " + " ;
|
||||
calcs1 << y->entryToString (entries[k]);
|
||||
if (DL_ >= 3) {
|
||||
(k == 0) ? calcs2 << "(" << prod : calcs2 << " + (" << prod;
|
||||
}
|
||||
}
|
||||
vector<int> insts = entries[k].getDomainInstantiations();
|
||||
for (unsigned int i = 0; i < yParents.size(); i++) {
|
||||
if (yParents[i] != x) {
|
||||
double val = yParents[i]->getPiMessage (y, insts[i + 1]);
|
||||
prod *= val;
|
||||
if (DL_ >= 2) {
|
||||
calcs1 << ".π" << y->getVariableName();
|
||||
calcs1 << "(" << yParents[i]->getDomain()[insts[i + 1]] << ")" ;
|
||||
if (DL_ >= 3) calcs2 << "x" << val;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
inner_sum += prod;
|
||||
if (DL_ >= 3) {
|
||||
calcs2 << ")" ;
|
||||
}
|
||||
}
|
||||
outer_sum += inner_sum * y->getLambdaValue (yi);
|
||||
if (DL_ >= 2) {
|
||||
calcs1 << "].λ(" << y->getDomain()[yi] << ")";
|
||||
if (DL_ >= 3) calcs2 << "]x" << y->getLambdaValue (yi);
|
||||
}
|
||||
}
|
||||
x->setLambdaMessage (y, xi, outer_sum);
|
||||
if (DL_ >= 2) {
|
||||
cout << calcs1.str();
|
||||
if (DL_ >= 3) cout << endl << "= " << calcs2.str();
|
||||
cout << " = " << outer_sum << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::runIterativeBpSolver()
|
||||
{
|
||||
int nIter = 0;
|
||||
maxIter_ = 100;
|
||||
bool converged = false;
|
||||
while (nIter < maxIter_ && !converged) {
|
||||
if (DL_ >= 1) {
|
||||
cout << endl << endl;
|
||||
cout << "****************************************" ;
|
||||
cout << "****************************************" ;
|
||||
cout << endl;
|
||||
cout << " Iteration " << nIter + 1 << endl;
|
||||
cout << "****************************************" ;
|
||||
cout << "****************************************" ;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
BpNode* x = static_cast<BpNode*>(nodes_[i]);
|
||||
vector<BpNode*> xParents = cast (x->getParents());
|
||||
for (unsigned int j = 0; j < xParents.size(); j++) {
|
||||
//if (!xParents[j]->hasEvidence()) {
|
||||
if (DL_ >= 1) {
|
||||
cout << endl << "λ message " << x->getVariableName();
|
||||
cout << " --> " << xParents[j]->getVariableName() << endl;
|
||||
}
|
||||
updateLambdaMessages (xParents[j], x);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
BpNode* x = static_cast<BpNode*>(nodes_[i]);
|
||||
vector<BpNode*> xChilds = cast (x->getChilds());
|
||||
for (unsigned int j = 0; j < xChilds.size(); j++) {
|
||||
if (DL_ >= 1) {
|
||||
cout << endl << "π message " << x->getVariableName();
|
||||
cout << " --> " << xChilds[j]->getVariableName() << endl;
|
||||
}
|
||||
updatePiMessages (x, xChilds[j]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
BpNode* x = static_cast<BpNode*>(nodes_[i]);
|
||||
vector<BpNode*> xChilds = cast (x->getChilds());
|
||||
for (unsigned int j = 0; j < xChilds.size(); j++) {
|
||||
if (DL_ >= 1) {
|
||||
cout << "π message " << x->getVariableName();
|
||||
cout << " --> " << xChilds[j]->getVariableName() << endl;
|
||||
}
|
||||
updatePiMessages (x, xChilds[j]);
|
||||
}
|
||||
vector<BpNode*> xParents = cast (x->getParents());
|
||||
for (unsigned int j = 0; j < xParents.size(); j++) {
|
||||
//if (!xParents[j]->hasEvidence()) {
|
||||
if (DL_ >= 1) {
|
||||
cout << "λ message " << x->getVariableName();
|
||||
cout << " --> " << xParents[j]->getVariableName() << endl;
|
||||
}
|
||||
updateLambdaMessages (xParents[j], x);
|
||||
//}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
BpNode* x = static_cast<BpNode*> (nodes_[i]);
|
||||
//cout << endl << "SWAPING MESSAGES FOR " << x->getVariableName() << ":" ;
|
||||
//cout << endl << endl;
|
||||
//printCurrentStatusOf (x);
|
||||
x->swapMessages();
|
||||
x->normalizeMessages();
|
||||
//cout << endl << "messages swaped " << endl;
|
||||
//printCurrentStatusOf (x);
|
||||
}
|
||||
|
||||
converged = true;
|
||||
for (unsigned int i = 0; i < nodes_.size(); i++) {
|
||||
BpNode* x = static_cast<BpNode*>(nodes_[i]);
|
||||
if (DL_ >= 1) {
|
||||
cout << endl << "var " << x->getVariableName() << ":" << endl;
|
||||
}
|
||||
//if (!x->hasEvidence()) {
|
||||
updatePiValues (x);
|
||||
updateLambdaValues (x);
|
||||
double change = x->getBeliefChange();
|
||||
if (DL_ >= 1) {
|
||||
cout << "belief change = " << change << endl;
|
||||
}
|
||||
if (change > stableThreashold_) {
|
||||
converged = false;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
if (converged) {
|
||||
// converged = false;
|
||||
}
|
||||
if (DL_ >= 2) {
|
||||
cout << endl;
|
||||
printCurrentStatus();
|
||||
}
|
||||
nIter++;
|
||||
}
|
||||
|
||||
if (DL_ >= 1) {
|
||||
cout << endl;
|
||||
if (converged) {
|
||||
cout << "Iterative belief propagation converged in " ;
|
||||
cout << nIter << " iterations" << endl;
|
||||
} else {
|
||||
cout << "Iterative belief propagation converged didn't converge" ;
|
||||
cout << endl;
|
||||
}
|
||||
if (DL_ == 1) {
|
||||
cout << endl;
|
||||
printBeliefs();
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::addNode (string varName,
|
||||
vector<BayesianNode*> parents,
|
||||
int evidence,
|
||||
int distId)
|
||||
{
|
||||
for (unsigned int i = 0; i < dists_.size(); i++) {
|
||||
if (dists_[i]->id == distId) {
|
||||
BpNode* node = new BpNode (varName, parents, dists_[i], evidence);
|
||||
nodes_.push_back (node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNetwork::addNode (string varName,
|
||||
vector<BayesianNode*> parents,
|
||||
double* params,
|
||||
int nParams,
|
||||
vector<string> domain)
|
||||
{
|
||||
Distribution* dist = new Distribution (params, nParams, domain);
|
||||
BpNode* node = new BpNode (varName, parents, dist);
|
||||
dists_.push_back (dist);
|
||||
nodes_.push_back (node);
|
||||
}
|
||||
|
||||
|
||||
|
||||
vector<BpNode*>
|
||||
BpNetwork::cast (vector<BayesianNode*> nodes)
|
||||
{
|
||||
vector<BpNode*> castedNodes (nodes.size());
|
||||
for (unsigned int i = 0; i < nodes.size(); i++) {
|
||||
castedNodes[i] = static_cast<BpNode*> (nodes[i]);
|
||||
}
|
||||
return castedNodes;
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
#ifndef BP_BP_NETWORK_H
|
||||
#define BP_BP_NETWORK_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "BayesianNetwork.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class BpNode;
|
||||
|
||||
enum Schedule
|
||||
{
|
||||
SEQUENTIAL_SCHEDULE,
|
||||
PARALLEL_SCHEDULE
|
||||
};
|
||||
|
||||
class BpNetwork : public BayesianNetwork
|
||||
{
|
||||
public:
|
||||
// constructs
|
||||
BpNetwork (void);
|
||||
// destruct
|
||||
~BpNetwork (void);
|
||||
// methods
|
||||
void setSolverParameters (Schedule, int, double);
|
||||
void runSolver (BayesianNode* queryVar);
|
||||
void runSolver (vector<BayesianNode*>);
|
||||
void printCurrentStatus (void);
|
||||
void printCurrentStatusOf (BpNode*);
|
||||
void printBeliefs (void);
|
||||
vector<double> getBeliefs (void);
|
||||
vector<double> getBeliefs (BpNode*);
|
||||
|
||||
private:
|
||||
BpNetwork (const BpNetwork&); // disallow copy
|
||||
void operator= (const BpNetwork&); // disallow assign
|
||||
// methods
|
||||
void initializeSolver (vector<BayesianNode*>);
|
||||
void addJunctionNode (vector<BayesianNode*>);
|
||||
void addEvidence (BpNode*);
|
||||
void runNeapolitanSolver (void);
|
||||
void sendLambdaMessage (BpNode*, BpNode*);
|
||||
void sendPiMessage (BpNode*, BpNode*);
|
||||
void updatePiValues (BpNode*);
|
||||
void updatePiMessages (BpNode*, BpNode*);
|
||||
void updateLambdaValues (BpNode*);
|
||||
void updateLambdaMessages (BpNode*, BpNode*);
|
||||
void runIterativeBpSolver (void);
|
||||
void addNode (string, vector<BayesianNode*>, int, int);
|
||||
void addNode (string, vector<BayesianNode*>,
|
||||
double*, int, vector<string>);
|
||||
vector<BpNode*> cast (vector<BayesianNode*>);
|
||||
// members
|
||||
Schedule schedule_;
|
||||
int nIter_;
|
||||
int maxIter_;
|
||||
double stableThreashold_;
|
||||
BpNode* queryNode_;
|
||||
static const int DL_ = 3;
|
||||
static const int PRECISION_ = 10;
|
||||
};
|
||||
|
||||
#endif // BP_BP_NETWORK_H
|
||||
|
@ -1,250 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#include "BpNode.h"
|
||||
|
||||
bool BpNode::calculateMessageResidual_ = true;
|
||||
|
||||
|
||||
BpNode::BpNode (BayesNode* node)
|
||||
{
|
||||
ds_ = node->getDomainSize();
|
||||
const NodeSet& childs = node->getChilds();
|
||||
piVals_.resize (ds_, 1);
|
||||
ldVals_.resize (ds_, 1);
|
||||
if (calculateMessageResidual_) {
|
||||
piResiduals_.resize (childs.size(), 0.0);
|
||||
ldResiduals_.resize (childs.size(), 0.0);
|
||||
}
|
||||
childs_ = &childs;
|
||||
for (unsigned i = 0; i < childs.size(); i++) {
|
||||
//indexMap_.insert (make_pair (childs[i]->getVarId(), i));
|
||||
currPiMsgs_.push_back (ParamSet (ds_, 1));
|
||||
currLdMsgs_.push_back (ParamSet (ds_, 1));
|
||||
nextPiMsgs_.push_back (ParamSet (ds_, 1));
|
||||
nextLdMsgs_.push_back (ParamSet (ds_, 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
BpNode::getBeliefs (void) const
|
||||
{
|
||||
double sum = 0.0;
|
||||
ParamSet beliefs (ds_);
|
||||
for (int xi = 0; xi < ds_; xi++) {
|
||||
double prod = piVals_[xi] * ldVals_[xi];
|
||||
beliefs[xi] = prod;
|
||||
sum += prod;
|
||||
}
|
||||
assert (sum);
|
||||
//normalize the beliefs
|
||||
for (int xi = 0; xi < ds_; xi++) {
|
||||
beliefs[xi] /= sum;
|
||||
}
|
||||
return beliefs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double
|
||||
BpNode::getPiValue (int idx) const
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
return piVals_[idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNode::setPiValue (int idx, double value)
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
piVals_[idx] = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double
|
||||
BpNode::getLambdaValue (int idx) const
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
return ldVals_[idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNode::setLambdaValue (int idx, double value)
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
ldVals_[idx] = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet&
|
||||
BpNode::getPiValues (void)
|
||||
{
|
||||
return piVals_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet&
|
||||
BpNode::getLambdaValues (void)
|
||||
{
|
||||
return ldVals_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double
|
||||
BpNode::getPiMessageValue (const BayesNode* destination, int idx) const
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
return currPiMsgs_[getIndex(destination)][idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
double
|
||||
BpNode::getLambdaMessageValue (const BayesNode* source, int idx) const
|
||||
{
|
||||
assert (idx >=0 && idx < ds_);
|
||||
return currLdMsgs_[getIndex(source)][idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ParamSet&
|
||||
BpNode::getPiMessage (const BayesNode* destination) const
|
||||
{
|
||||
return currPiMsgs_[getIndex(destination)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ParamSet&
|
||||
BpNode::getLambdaMessage (const BayesNode* source) const
|
||||
{
|
||||
return currLdMsgs_[getIndex(source)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet&
|
||||
BpNode::piNextMessageReference (const BayesNode* destination)
|
||||
{
|
||||
return nextPiMsgs_[getIndex(destination)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet&
|
||||
BpNode::lambdaNextMessageReference (const BayesNode* source)
|
||||
{
|
||||
return nextLdMsgs_[getIndex(source)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNode::updatePiMessage (const BayesNode* destination)
|
||||
{
|
||||
int idx = getIndex (destination);
|
||||
currPiMsgs_[idx] = nextPiMsgs_[idx];
|
||||
Util::normalize (currPiMsgs_[idx]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNode::updateLambdaMessage (const BayesNode* source)
|
||||
{
|
||||
int idx = getIndex (source);
|
||||
currLdMsgs_[idx] = nextLdMsgs_[idx];
|
||||
Util::normalize (currLdMsgs_[idx]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
double
|
||||
BpNode::getBeliefChange (void)
|
||||
{
|
||||
double change = 0.0;
|
||||
if (oldBeliefs_.size() == 0) {
|
||||
oldBeliefs_ = getBeliefs();
|
||||
change = 9999999999.0;
|
||||
} else {
|
||||
ParamSet currentBeliefs = getBeliefs();
|
||||
for (int xi = 0; xi < ds_; xi++) {
|
||||
change += abs (currentBeliefs[xi] - oldBeliefs_[xi]);
|
||||
}
|
||||
oldBeliefs_ = currentBeliefs;
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNode::updatePiResidual (const BayesNode* destination)
|
||||
{
|
||||
int idx = getIndex (destination);
|
||||
Util::normalize (nextPiMsgs_[idx]);
|
||||
//piResiduals_[idx] = Util::getL1dist (
|
||||
// currPiMsgs_[idx], nextPiMsgs_[idx]);
|
||||
piResiduals_[idx] = Util::getMaxNorm (
|
||||
currPiMsgs_[idx], nextPiMsgs_[idx]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNode::updateLambdaResidual (const BayesNode* source)
|
||||
{
|
||||
int idx = getIndex (source);
|
||||
Util::normalize (nextLdMsgs_[idx]);
|
||||
//ldResiduals_[idx] = Util::getL1dist (
|
||||
// currLdMsgs_[idx], nextLdMsgs_[idx]);
|
||||
ldResiduals_[idx] = Util::getMaxNorm (
|
||||
currLdMsgs_[idx], nextLdMsgs_[idx]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNode::clearPiResidual (const BayesNode* destination)
|
||||
{
|
||||
piResiduals_[getIndex(destination)] = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BpNode::clearLambdaResidual (const BayesNode* source)
|
||||
{
|
||||
ldResiduals_[getIndex(source)] = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
BpNode::hasReceivedChildInfluence (void) const
|
||||
{
|
||||
// if all lambda values are equal, then neither
|
||||
// this node neither its descendents have evidence,
|
||||
// we can use this to don't send lambda messages his parents
|
||||
bool childInfluenced = false;
|
||||
for (int xi = 1; xi < ds_; xi++) {
|
||||
if (ldVals_[xi] != ldVals_[0]) {
|
||||
childInfluenced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return childInfluenced;
|
||||
}
|
||||
|
@ -1,99 +0,0 @@
|
||||
#ifndef BP_BPNODE_H
|
||||
#define BP_BPNODE_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "BayesNode.h"
|
||||
#include "Shared.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class BpNode
|
||||
{
|
||||
public:
|
||||
BpNode (int);
|
||||
BpNode (BayesNode*);
|
||||
|
||||
ParamSet getBeliefs (void) const;
|
||||
double getPiValue (int) const;
|
||||
void setPiValue (int, double);
|
||||
double getLambdaValue (int) const;
|
||||
void setLambdaValue (int, double);
|
||||
ParamSet& getPiValues (void);
|
||||
ParamSet& getLambdaValues (void);
|
||||
double getPiMessageValue (const BayesNode*, int) const;
|
||||
double getLambdaMessageValue (const BayesNode*, int) const;
|
||||
const ParamSet& getPiMessage (const BayesNode*) const;
|
||||
const ParamSet& getLambdaMessage (const BayesNode*) const;
|
||||
ParamSet& piNextMessageReference (const BayesNode*);
|
||||
ParamSet& lambdaNextMessageReference (const BayesNode*);
|
||||
void updatePiMessage (const BayesNode*);
|
||||
void updateLambdaMessage (const BayesNode*);
|
||||
double getBeliefChange (void);
|
||||
void updatePiResidual (const BayesNode*);
|
||||
void updateLambdaResidual (const BayesNode*);
|
||||
void clearPiResidual (const BayesNode*);
|
||||
void clearLambdaResidual (const BayesNode*);
|
||||
bool hasReceivedChildInfluence (void) const;
|
||||
// inlines
|
||||
double getPiResidual (const BayesNode*);
|
||||
double getLambdaResidual (const BayesNode*);
|
||||
int getIndex (const BayesNode*) const;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN (BpNode);
|
||||
|
||||
IndexMap indexMap_;
|
||||
ParamSet piVals_; // pi values
|
||||
ParamSet ldVals_; // lambda values
|
||||
vector<ParamSet> currPiMsgs_; // current pi messages
|
||||
vector<ParamSet> currLdMsgs_; // current lambda messages
|
||||
vector<ParamSet> nextPiMsgs_;
|
||||
vector<ParamSet> nextLdMsgs_;
|
||||
ParamSet oldBeliefs_;
|
||||
ParamSet piResiduals_;
|
||||
ParamSet ldResiduals_;
|
||||
int ds_;
|
||||
const NodeSet* childs_;
|
||||
static bool calculateMessageResidual_;
|
||||
// static const double MAX_CHANGE_ = 10000000.0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline double
|
||||
BpNode::getPiResidual (const BayesNode* destination)
|
||||
{
|
||||
return piResiduals_[getIndex(destination)];
|
||||
}
|
||||
|
||||
|
||||
inline double
|
||||
BpNode::getLambdaResidual (const BayesNode* source)
|
||||
{
|
||||
return ldResiduals_[getIndex(source)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline int
|
||||
BpNode::getIndex (const BayesNode* node) const
|
||||
{
|
||||
assert (node);
|
||||
//assert (indexMap_.find(node->getVarId()) != indexMap_.end());
|
||||
//return indexMap_.find(node->getVarId())->second;
|
||||
for (unsigned i = 0; childs_->size(); i++) {
|
||||
if ((*childs_)[i]->getVarId() == node->getVarId()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert (false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -1,198 +0,0 @@
|
||||
#include "CountingBP.h"
|
||||
|
||||
|
||||
CountingBP::~CountingBP (void)
|
||||
{
|
||||
delete lfg_;
|
||||
delete fg_;
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
delete links_[i];
|
||||
}
|
||||
links_.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
CountingBP::getPosterioriOf (Vid vid) const
|
||||
{
|
||||
FgVarNode* var = lfg_->getEquivalentVariable (vid);
|
||||
ParamSet probs;
|
||||
|
||||
if (var->hasEvidence()) {
|
||||
probs.resize (var->getDomainSize(), 0.0);
|
||||
probs[var->getEvidence()] = 1.0;
|
||||
} else {
|
||||
probs.resize (var->getDomainSize(), 1.0);
|
||||
CLinkSet links = varsI_[var->getIndex()]->getLinks();
|
||||
for (unsigned i = 0; i < links.size(); i++) {
|
||||
ParamSet msg = links[i]->getMessage();
|
||||
CountingBPLink* l = static_cast<CountingBPLink*> (links[i]);
|
||||
Util::pow (msg, l->getNumberOfEdges());
|
||||
for (unsigned j = 0; j < msg.size(); j++) {
|
||||
probs[j] *= msg[j];
|
||||
}
|
||||
}
|
||||
Util::normalize (probs);
|
||||
}
|
||||
return probs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
CountingBP::initializeSolver (void)
|
||||
{
|
||||
lfg_ = new LiftedFG (*fg_);
|
||||
unsigned nUncVars = fg_->getFgVarNodes().size();
|
||||
unsigned nUncFactors = fg_->getFactors().size();
|
||||
CFgVarSet vars = fg_->getFgVarNodes();
|
||||
unsigned nNeighborLessVars = 0;
|
||||
for (unsigned i = 0; i < vars.size(); i++) {
|
||||
CFactorSet factors = vars[i]->getFactors();
|
||||
if (factors.size() == 1 && factors[0]->getFgVarNodes().size() == 1) {
|
||||
nNeighborLessVars ++;
|
||||
}
|
||||
}
|
||||
// cout << "UNCOMPRESSED FACTOR GRAPH" << endl;
|
||||
// fg_->printGraphicalModel();
|
||||
fg_->exportToDotFormat ("uncompress.dot");
|
||||
|
||||
FactorGraph *temp;
|
||||
temp = fg_;
|
||||
fg_ = lfg_->getCompressedFactorGraph();
|
||||
unsigned nCompVars = fg_->getFgVarNodes().size();
|
||||
unsigned nCompFactors = fg_->getFactors().size();
|
||||
|
||||
Statistics::updateCompressingStats (nUncVars,
|
||||
nUncFactors,
|
||||
nCompVars,
|
||||
nCompFactors,
|
||||
nNeighborLessVars);
|
||||
|
||||
cout << "COMPRESSED FACTOR GRAPH" << endl;
|
||||
fg_->printGraphicalModel();
|
||||
//fg_->exportToDotFormat ("compress.dot");
|
||||
|
||||
SPSolver::initializeSolver();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
CountingBP::createLinks (void)
|
||||
{
|
||||
const FactorClusterSet fcs = lfg_->getFactorClusters();
|
||||
for (unsigned i = 0; i < fcs.size(); i++) {
|
||||
const VarClusterSet vcs = fcs[i]->getVarClusters();
|
||||
for (unsigned j = 0; j < vcs.size(); j++) {
|
||||
unsigned c = lfg_->getGroundEdgeCount (fcs[i], vcs[j]);
|
||||
links_.push_back (
|
||||
new CountingBPLink (fcs[i]->getRepresentativeFactor(),
|
||||
vcs[j]->getRepresentativeVariable(), c));
|
||||
//cout << (links_.back())->toString() << " edge count =" << c << endl;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
CountingBP::deleteJunction (Factor* f, FgVarNode*)
|
||||
{
|
||||
f->freeDistribution();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
CountingBP::maxResidualSchedule (void)
|
||||
{
|
||||
if (nIter_ == 1) {
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
links_[i]->setNextMessage (getFactor2VarMsg (links_[i]));
|
||||
SortedOrder::iterator it = sortedOrder_.insert (links_[i]);
|
||||
linkMap_.insert (make_pair (links_[i], it));
|
||||
if (DL >= 2 && DL < 5) {
|
||||
cout << "calculating " << links_[i]->toString() << endl;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned c = 0; c < links_.size(); c++) {
|
||||
if (DL >= 2) {
|
||||
cout << endl << "current residuals:" << endl;
|
||||
for (SortedOrder::iterator it = sortedOrder_.begin();
|
||||
it != sortedOrder_.end(); it ++) {
|
||||
cout << " " << setw (30) << left << (*it)->toString();
|
||||
cout << "residual = " << (*it)->getResidual() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
SortedOrder::iterator it = sortedOrder_.begin();
|
||||
Link* link = *it;
|
||||
if (DL >= 2) {
|
||||
cout << "updating " << (*sortedOrder_.begin())->toString() << endl;
|
||||
}
|
||||
if (link->getResidual() < SolverOptions::accuracy) {
|
||||
return;
|
||||
}
|
||||
link->updateMessage();
|
||||
link->clearResidual();
|
||||
sortedOrder_.erase (it);
|
||||
linkMap_.find (link)->second = sortedOrder_.insert (link);
|
||||
|
||||
// update the messages that depend on message source --> destin
|
||||
CFactorSet factorNeighbors = link->getVariable()->getFactors();
|
||||
for (unsigned i = 0; i < factorNeighbors.size(); i++) {
|
||||
CLinkSet links = factorsI_[factorNeighbors[i]->getIndex()]->getLinks();
|
||||
for (unsigned j = 0; j < links.size(); j++) {
|
||||
if (links[j]->getVariable() != link->getVariable()) { //FIXMEFIXME
|
||||
if (DL >= 2 && DL < 5) {
|
||||
cout << " calculating " << links[j]->toString() << endl;
|
||||
}
|
||||
links[j]->setNextMessage (getFactor2VarMsg (links[j]));
|
||||
LinkMap::iterator iter = linkMap_.find (links[j]);
|
||||
sortedOrder_.erase (iter->second);
|
||||
iter->second = sortedOrder_.insert (links[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
CountingBP::getVar2FactorMsg (const Link* link) const
|
||||
{
|
||||
const FgVarNode* src = link->getVariable();
|
||||
const Factor* dest = link->getFactor();
|
||||
ParamSet msg;
|
||||
if (src->hasEvidence()) {
|
||||
cout << "has evidence" << endl;
|
||||
msg.resize (src->getDomainSize(), 0.0);
|
||||
msg[src->getEvidence()] = link->getMessage()[src->getEvidence()];
|
||||
cout << "-> " << link->getVariable()->getLabel() << " " << link->getFactor()->getLabel() << endl;
|
||||
cout << "-> p2s " << Util::parametersToString (msg) << endl;
|
||||
} else {
|
||||
msg = link->getMessage();
|
||||
}
|
||||
const CountingBPLink* l = static_cast<const CountingBPLink*> (link);
|
||||
Util::pow (msg, l->getNumberOfEdges() - 1);
|
||||
CLinkSet links = varsI_[src->getIndex()]->getLinks();
|
||||
for (unsigned i = 0; i < links.size(); i++) {
|
||||
if (links[i]->getFactor() != dest) {
|
||||
ParamSet msgFromFactor = links[i]->getMessage();
|
||||
CountingBPLink* l = static_cast<CountingBPLink*> (links[i]);
|
||||
Util::pow (msgFromFactor, l->getNumberOfEdges());
|
||||
for (unsigned j = 0; j < msgFromFactor.size(); j++) {
|
||||
msg[j] *= msgFromFactor[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
#ifndef BP_COUNTING_BP_H
|
||||
#define BP_COUNTING_BP_H
|
||||
|
||||
#include "SPSolver.h"
|
||||
#include "LiftedFG.h"
|
||||
|
||||
class Factor;
|
||||
class FgVarNode;
|
||||
|
||||
class CountingBPLink : public Link
|
||||
{
|
||||
public:
|
||||
CountingBPLink (Factor* f, FgVarNode* v, unsigned c) : Link (f, v)
|
||||
{
|
||||
edgeCount_ = c;
|
||||
}
|
||||
|
||||
unsigned getNumberOfEdges (void) const { return edgeCount_; }
|
||||
|
||||
private:
|
||||
unsigned edgeCount_;
|
||||
};
|
||||
|
||||
|
||||
class CountingBP : public SPSolver
|
||||
{
|
||||
public:
|
||||
CountingBP (FactorGraph& fg) : SPSolver (fg) { }
|
||||
~CountingBP (void);
|
||||
|
||||
ParamSet getPosterioriOf (Vid) const;
|
||||
|
||||
private:
|
||||
void initializeSolver (void);
|
||||
void createLinks (void);
|
||||
void deleteJunction (Factor*, FgVarNode*);
|
||||
|
||||
void maxResidualSchedule (void);
|
||||
ParamSet getVar2FactorMsg (const Link*) const;
|
||||
|
||||
LiftedFG* lfg_;
|
||||
};
|
||||
|
||||
#endif // BP_COUNTING_BP_H
|
||||
|
@ -1,40 +0,0 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <Distribution.h>
|
||||
|
||||
Distribution::Distribution (int id,
|
||||
double* params,
|
||||
int nParams,
|
||||
vector<string> domain)
|
||||
{
|
||||
this->id = id;
|
||||
this->params = params;
|
||||
this->nParams = nParams;
|
||||
this->domain = domain;
|
||||
}
|
||||
|
||||
|
||||
Distribution::Distribution (double* params,
|
||||
int nParams,
|
||||
vector<string> domain)
|
||||
{
|
||||
this->id = -1;
|
||||
this->params = params;
|
||||
this->nParams = nParams;
|
||||
this->domain = domain;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Distribution::~Distribution()
|
||||
{
|
||||
delete params;
|
||||
for (unsigned int i = 0; i < cptEntries.size(); i++) {
|
||||
delete cptEntries[i];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
#ifndef BP_FG_VAR_NODE_H
|
||||
#define BP_FG_VAR_NODE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Variable.h"
|
||||
#include "Shared.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Factor;
|
||||
|
||||
class FgVarNode : public Variable
|
||||
{
|
||||
public:
|
||||
FgVarNode (unsigned vid, unsigned dsize) : Variable (vid, dsize) { }
|
||||
FgVarNode (const Variable* v) : Variable (v) { }
|
||||
|
||||
void addFactor (Factor* f) { factors_.push_back (f); }
|
||||
CFactorSet getFactors (void) const { return factors_; }
|
||||
|
||||
void removeFactor (const Factor* f)
|
||||
{
|
||||
if (factors_[factors_.size() -1] == f) {
|
||||
factors_.pop_back();
|
||||
} else {
|
||||
for (unsigned i = 0; i < factors_.size(); i++) {
|
||||
if (factors_[i] == f) {
|
||||
factors_.erase (factors_.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert (false);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN (FgVarNode);
|
||||
// members
|
||||
FactorSet factors_;
|
||||
};
|
||||
|
||||
#endif // BP_FG_VAR_NODE_H
|
@ -1,278 +0,0 @@
|
||||
|
||||
#include "LiftedFG.h"
|
||||
#include "FgVarNode.h"
|
||||
#include "Factor.h"
|
||||
#include "Distribution.h"
|
||||
|
||||
LiftedFG::LiftedFG (const FactorGraph& fg)
|
||||
{
|
||||
groundFg_ = &fg;
|
||||
freeColor_ = 0;
|
||||
|
||||
const FgVarSet& varNodes = fg.getFgVarNodes();
|
||||
const FactorSet& factors = fg.getFactors();
|
||||
varColors_.resize (varNodes.size());
|
||||
factorColors_.resize (factors.size());
|
||||
for (unsigned i = 0; i < factors.size(); i++) {
|
||||
factors[i]->setIndex (i);
|
||||
}
|
||||
|
||||
// create the initial variable colors
|
||||
VarColorMap colorMap;
|
||||
for (unsigned i = 0; i < varNodes.size(); i++) {
|
||||
unsigned dsize = varNodes[i]->getDomainSize();
|
||||
VarColorMap::iterator it = colorMap.find (dsize);
|
||||
if (it == colorMap.end()) {
|
||||
it = colorMap.insert (make_pair (
|
||||
dsize, vector<Color> (dsize + 1,-1))).first;
|
||||
}
|
||||
unsigned idx;
|
||||
if (varNodes[i]->hasEvidence()) {
|
||||
idx = varNodes[i]->getEvidence();
|
||||
} else {
|
||||
idx = dsize;
|
||||
}
|
||||
vector<Color>& stateColors = it->second;
|
||||
if (stateColors[idx] == -1) {
|
||||
stateColors[idx] = getFreeColor();
|
||||
}
|
||||
setColor (varNodes[i], stateColors[idx]);
|
||||
}
|
||||
|
||||
// create the initial factor colors
|
||||
DistColorMap distColors;
|
||||
for (unsigned i = 0; i < factors.size(); i++) {
|
||||
Distribution* dist = factors[i]->getDistribution();
|
||||
DistColorMap::iterator it = distColors.find (dist);
|
||||
if (it == distColors.end()) {
|
||||
it = distColors.insert (make_pair (dist, getFreeColor())).first;
|
||||
}
|
||||
setColor (factors[i], it->second);
|
||||
}
|
||||
|
||||
VarSignMap varGroups;
|
||||
FactorSignMap factorGroups;
|
||||
bool groupsHaveChanged = true;
|
||||
unsigned nIter = 0;
|
||||
while (groupsHaveChanged || nIter == 1) {
|
||||
nIter ++;
|
||||
if (Statistics::numCreatedNets == 4) {
|
||||
cout << "--------------------------------------------" << endl;
|
||||
cout << "Iteration " << nIter << endl;
|
||||
cout << "--------------------------------------------" << endl;
|
||||
}
|
||||
|
||||
unsigned prevFactorGroupsSize = factorGroups.size();
|
||||
factorGroups.clear();
|
||||
// set a new color to the factors with the same signature
|
||||
for (unsigned i = 0; i < factors.size(); i++) {
|
||||
const string& signatureId = getSignatureId (factors[i]);
|
||||
// cout << factors[i]->getLabel() << " signature: " ;
|
||||
// cout<< signatureId << endl;
|
||||
FactorSignMap::iterator it = factorGroups.find (signatureId);
|
||||
if (it == factorGroups.end()) {
|
||||
it = factorGroups.insert (make_pair (signatureId, FactorSet())).first;
|
||||
}
|
||||
it->second.push_back (factors[i]);
|
||||
}
|
||||
if (nIter > 0)
|
||||
for (FactorSignMap::iterator it = factorGroups.begin();
|
||||
it != factorGroups.end(); it++) {
|
||||
Color newColor = getFreeColor();
|
||||
FactorSet& groupMembers = it->second;
|
||||
for (unsigned i = 0; i < groupMembers.size(); i++) {
|
||||
setColor (groupMembers[i], newColor);
|
||||
}
|
||||
}
|
||||
|
||||
// set a new color to the variables with the same signature
|
||||
unsigned prevVarGroupsSize = varGroups.size();
|
||||
varGroups.clear();
|
||||
for (unsigned i = 0; i < varNodes.size(); i++) {
|
||||
const string& signatureId = getSignatureId (varNodes[i]);
|
||||
VarSignMap::iterator it = varGroups.find (signatureId);
|
||||
// cout << varNodes[i]->getLabel() << " signature: " ;
|
||||
// cout << signatureId << endl;
|
||||
if (it == varGroups.end()) {
|
||||
it = varGroups.insert (make_pair (signatureId, FgVarSet())).first;
|
||||
}
|
||||
it->second.push_back (varNodes[i]);
|
||||
}
|
||||
if (nIter > 0)
|
||||
for (VarSignMap::iterator it = varGroups.begin();
|
||||
it != varGroups.end(); it++) {
|
||||
Color newColor = getFreeColor();
|
||||
FgVarSet& groupMembers = it->second;
|
||||
for (unsigned i = 0; i < groupMembers.size(); i++) {
|
||||
setColor (groupMembers[i], newColor);
|
||||
}
|
||||
}
|
||||
|
||||
//if (nIter >= 3) cout << "bigger than three: " << nIter << endl;
|
||||
groupsHaveChanged = prevVarGroupsSize != varGroups.size()
|
||||
|| prevFactorGroupsSize != factorGroups.size();
|
||||
}
|
||||
|
||||
printGroups (varGroups, factorGroups);
|
||||
for (VarSignMap::iterator it = varGroups.begin();
|
||||
it != varGroups.end(); it++) {
|
||||
CFgVarSet vars = it->second;
|
||||
VarCluster* vc = new VarCluster (vars);
|
||||
for (unsigned i = 0; i < vars.size(); i++) {
|
||||
vid2VarCluster_.insert (make_pair (vars[i]->getVarId(), vc));
|
||||
}
|
||||
varClusters_.push_back (vc);
|
||||
}
|
||||
|
||||
for (FactorSignMap::iterator it = factorGroups.begin();
|
||||
it != factorGroups.end(); it++) {
|
||||
VarClusterSet varClusters;
|
||||
Factor* groundFactor = it->second[0];
|
||||
FgVarSet groundVars = groundFactor->getFgVarNodes();
|
||||
for (unsigned i = 0; i < groundVars.size(); i++) {
|
||||
Vid vid = groundVars[i]->getVarId();
|
||||
varClusters.push_back (vid2VarCluster_.find (vid)->second);
|
||||
}
|
||||
factorClusters_.push_back (new FactorCluster (it->second, varClusters));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
LiftedFG::~LiftedFG (void)
|
||||
{
|
||||
for (unsigned i = 0; i < varClusters_.size(); i++) {
|
||||
delete varClusters_[i];
|
||||
}
|
||||
for (unsigned i = 0; i < factorClusters_.size(); i++) {
|
||||
delete factorClusters_[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
string
|
||||
LiftedFG::getSignatureId (FgVarNode* var) const
|
||||
{
|
||||
stringstream ss;
|
||||
CFactorSet myFactors = var->getFactors();
|
||||
ss << myFactors.size();
|
||||
for (unsigned i = 0; i < myFactors.size(); i++) {
|
||||
ss << "." << getColor (myFactors[i]);
|
||||
ss << "." << myFactors[i]->getIndexOf(var);
|
||||
}
|
||||
ss << "." << getColor (var);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
string
|
||||
LiftedFG::getSignatureId (Factor* factor) const
|
||||
{
|
||||
stringstream ss;
|
||||
CFgVarSet myVars = factor->getFgVarNodes();
|
||||
ss << myVars.size();
|
||||
for (unsigned i = 0; i < myVars.size(); i++) {
|
||||
ss << "." << getColor (myVars[i]);
|
||||
}
|
||||
ss << "." << getColor (factor);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
FactorGraph*
|
||||
LiftedFG::getCompressedFactorGraph (void)
|
||||
{
|
||||
FactorGraph* fg = new FactorGraph();
|
||||
for (unsigned i = 0; i < varClusters_.size(); i++) {
|
||||
FgVarNode* var = varClusters_[i]->getGroundFgVarNodes()[0];
|
||||
FgVarNode* newVar = new FgVarNode (var);
|
||||
newVar->setIndex (i);
|
||||
varClusters_[i]->setRepresentativeVariable (newVar);
|
||||
fg->addVariable (newVar);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < factorClusters_.size(); i++) {
|
||||
FgVarSet myGroundVars;
|
||||
const VarClusterSet& myVarClusters = factorClusters_[i]->getVarClusters();
|
||||
for (unsigned j = 0; j < myVarClusters.size(); j++) {
|
||||
myGroundVars.push_back (myVarClusters[j]->getRepresentativeVariable());
|
||||
}
|
||||
Factor* newFactor = new Factor (myGroundVars,
|
||||
factorClusters_[i]->getGroundFactors()[0]->getDistribution());
|
||||
factorClusters_[i]->setRepresentativeFactor (newFactor);
|
||||
fg->addFactor (newFactor);
|
||||
}
|
||||
return fg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned
|
||||
LiftedFG::getGroundEdgeCount (FactorCluster* fc, VarCluster* vc) const
|
||||
{
|
||||
CFactorSet clusterGroundFactors = fc->getGroundFactors();
|
||||
FgVarNode* var = vc->getGroundFgVarNodes()[0];
|
||||
unsigned count = 0;
|
||||
for (unsigned i = 0; i < clusterGroundFactors.size(); i++) {
|
||||
if (clusterGroundFactors[i]->getIndexOf (var) != -1) {
|
||||
count ++;
|
||||
}
|
||||
}
|
||||
/*
|
||||
CFgVarSet vars = vc->getGroundFgVarNodes();
|
||||
for (unsigned i = 1; i < vars.size(); i++) {
|
||||
FgVarNode* var = vc->getGroundFgVarNodes()[i];
|
||||
unsigned count2 = 0;
|
||||
for (unsigned i = 0; i < clusterGroundFactors.size(); i++) {
|
||||
if (clusterGroundFactors[i]->getIndexOf (var) != -1) {
|
||||
count2 ++;
|
||||
}
|
||||
}
|
||||
if (count != count2) { cout << "oops!" << endl; abort(); }
|
||||
}
|
||||
*/
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
LiftedFG::printGroups (const VarSignMap& varGroups,
|
||||
const FactorSignMap& factorGroups) const
|
||||
{
|
||||
cout << "variable groups:" << endl;
|
||||
unsigned count = 0;
|
||||
for (VarSignMap::const_iterator it = varGroups.begin();
|
||||
it != varGroups.end(); it++) {
|
||||
const FgVarSet& groupMembers = it->second;
|
||||
if (groupMembers.size() > 0) {
|
||||
cout << ++count << ": " ;
|
||||
//if (groupMembers.size() > 1) {
|
||||
for (unsigned i = 0; i < groupMembers.size(); i++) {
|
||||
cout << groupMembers[i]->getLabel() << " " ;
|
||||
}
|
||||
//}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
cout << endl;
|
||||
cout << "factor groups:" << endl;
|
||||
count = 0;
|
||||
for (FactorSignMap::const_iterator it = factorGroups.begin();
|
||||
it != factorGroups.end(); it++) {
|
||||
const FactorSet& groupMembers = it->second;
|
||||
if (groupMembers.size() > 0) {
|
||||
cout << ++count << ": " ;
|
||||
//if (groupMembers.size() > 1) {
|
||||
for (unsigned i = 0; i < groupMembers.size(); i++) {
|
||||
cout << groupMembers[i]->getLabel() << " " ;
|
||||
}
|
||||
//}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,152 +0,0 @@
|
||||
#ifndef BP_LIFTED_FG_H
|
||||
#define BP_LIFTED_FG_H
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "FactorGraph.h"
|
||||
#include "FgVarNode.h"
|
||||
#include "Factor.h"
|
||||
#include "Shared.h"
|
||||
|
||||
class VarCluster;
|
||||
class FactorCluster;
|
||||
class Distribution;
|
||||
|
||||
typedef long Color;
|
||||
typedef vector<Color> Signature;
|
||||
typedef vector<VarCluster*> VarClusterSet;
|
||||
typedef vector<FactorCluster*> FactorClusterSet;
|
||||
|
||||
typedef map<string, FgVarSet> VarSignMap;
|
||||
typedef map<string, FactorSet> FactorSignMap;
|
||||
|
||||
typedef map<unsigned, vector<Color> > VarColorMap;
|
||||
typedef map<Distribution*, Color> DistColorMap;
|
||||
|
||||
typedef map<Vid, VarCluster*> Vid2VarCluster;
|
||||
|
||||
|
||||
class VarCluster
|
||||
{
|
||||
public:
|
||||
VarCluster (CFgVarSet vs)
|
||||
{
|
||||
for (unsigned i = 0; i < vs.size(); i++) {
|
||||
groundVars_.push_back (vs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void addFactorCluster (FactorCluster* fc)
|
||||
{
|
||||
factorClusters_.push_back (fc);
|
||||
}
|
||||
|
||||
const FactorClusterSet& getFactorClusters (void) const
|
||||
{
|
||||
return factorClusters_;
|
||||
}
|
||||
|
||||
FgVarNode* getRepresentativeVariable (void) const { return representVar_; }
|
||||
void setRepresentativeVariable (FgVarNode* v) { representVar_ = v; }
|
||||
CFgVarSet getGroundFgVarNodes (void) const { return groundVars_; }
|
||||
|
||||
private:
|
||||
FgVarSet groundVars_;
|
||||
FactorClusterSet factorClusters_;
|
||||
FgVarNode* representVar_;
|
||||
};
|
||||
|
||||
|
||||
class FactorCluster
|
||||
{
|
||||
public:
|
||||
FactorCluster (CFactorSet groundFactors, const VarClusterSet& vcs)
|
||||
{
|
||||
groundFactors_ = groundFactors;
|
||||
varClusters_ = vcs;
|
||||
for (unsigned i = 0; i < varClusters_.size(); i++) {
|
||||
varClusters_[i]->addFactorCluster (this);
|
||||
}
|
||||
}
|
||||
|
||||
const VarClusterSet& getVarClusters (void) const
|
||||
{
|
||||
return varClusters_;
|
||||
}
|
||||
|
||||
bool containsGround (const Factor* f)
|
||||
{
|
||||
for (unsigned i = 0; i < groundFactors_.size(); i++) {
|
||||
if (groundFactors_[i] == f) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Factor* getRepresentativeFactor (void) const { return representFactor_; }
|
||||
void setRepresentativeFactor (Factor* f) { representFactor_ = f; }
|
||||
CFactorSet getGroundFactors (void) const { return groundFactors_; }
|
||||
|
||||
|
||||
private:
|
||||
FactorSet groundFactors_;
|
||||
VarClusterSet varClusters_;
|
||||
Factor* representFactor_;
|
||||
};
|
||||
|
||||
|
||||
class LiftedFG
|
||||
{
|
||||
public:
|
||||
LiftedFG (const FactorGraph&);
|
||||
~LiftedFG (void);
|
||||
|
||||
FactorGraph* getCompressedFactorGraph (void);
|
||||
unsigned getGroundEdgeCount (FactorCluster*, VarCluster*) const;
|
||||
void printGroups (const VarSignMap& varGroups,
|
||||
const FactorSignMap& factorGroups) const;
|
||||
|
||||
FgVarNode* getEquivalentVariable (Vid vid)
|
||||
{
|
||||
VarCluster* vc = vid2VarCluster_.find (vid)->second;
|
||||
return vc->getRepresentativeVariable();
|
||||
}
|
||||
|
||||
const VarClusterSet& getVariableClusters (void) { return varClusters_; }
|
||||
const FactorClusterSet& getFactorClusters (void) { return factorClusters_; }
|
||||
|
||||
private:
|
||||
string getSignatureId (FgVarNode*) const;
|
||||
string getSignatureId (Factor*) const;
|
||||
|
||||
Color getFreeColor (void) { return ++freeColor_ -1; }
|
||||
Color getColor (FgVarNode* v) const { return varColors_[v->getIndex()]; }
|
||||
Color getColor (Factor* f) const { return factorColors_[f->getIndex()]; }
|
||||
|
||||
void setColor (FgVarNode* v, Color c)
|
||||
{
|
||||
varColors_[v->getIndex()] = c;
|
||||
}
|
||||
|
||||
void setColor (Factor* f, Color c)
|
||||
{
|
||||
factorColors_[f->getIndex()] = c;
|
||||
}
|
||||
|
||||
VarCluster* getVariableCluster (Vid vid) const
|
||||
{
|
||||
return vid2VarCluster_.find (vid)->second;
|
||||
}
|
||||
|
||||
Color freeColor_;
|
||||
vector<Color> varColors_;
|
||||
vector<Color> factorColors_;
|
||||
VarClusterSet varClusters_;
|
||||
FactorClusterSet factorClusters_;
|
||||
Vid2VarCluster vid2VarCluster_;
|
||||
const FactorGraph* groundFg_;
|
||||
};
|
||||
|
||||
#endif // BP_LIFTED_FG_H
|
||||
|
@ -1,470 +0,0 @@
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "SPSolver.h"
|
||||
#include "FactorGraph.h"
|
||||
#include "FgVarNode.h"
|
||||
#include "Factor.h"
|
||||
#include "Shared.h"
|
||||
|
||||
|
||||
SPSolver::SPSolver (FactorGraph& fg) : Solver (&fg)
|
||||
{
|
||||
fg_ = &fg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SPSolver::~SPSolver (void)
|
||||
{
|
||||
for (unsigned i = 0; i < varsI_.size(); i++) {
|
||||
delete varsI_[i];
|
||||
}
|
||||
for (unsigned i = 0; i < factorsI_.size(); i++) {
|
||||
delete factorsI_[i];
|
||||
}
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
delete links_[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
SPSolver::runTreeSolver (void)
|
||||
{
|
||||
CFactorSet factors = fg_->getFactors();
|
||||
bool finish = false;
|
||||
while (!finish) {
|
||||
finish = true;
|
||||
for (unsigned i = 0; i < factors.size(); i++) {
|
||||
CLinkSet links = factorsI_[factors[i]->getIndex()]->getLinks();
|
||||
for (unsigned j = 0; j < links.size(); j++) {
|
||||
if (!links[j]->messageWasSended()) {
|
||||
if (readyToSendMessage(links[j])) {
|
||||
links[j]->setNextMessage (getFactor2VarMsg (links[j]));
|
||||
links[j]->updateMessage();
|
||||
}
|
||||
finish = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
SPSolver::readyToSendMessage (const Link* link) const
|
||||
{
|
||||
CFgVarSet factorVars = link->getFactor()->getFgVarNodes();
|
||||
for (unsigned i = 0; i < factorVars.size(); i++) {
|
||||
if (factorVars[i] != link->getVariable()) {
|
||||
CLinkSet links = varsI_[factorVars[i]->getIndex()]->getLinks();
|
||||
for (unsigned j = 0; j < links.size(); j++) {
|
||||
if (links[j]->getFactor() != link->getFactor() &&
|
||||
!links[j]->messageWasSended()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
SPSolver::runSolver (void)
|
||||
{
|
||||
initializeSolver();
|
||||
runTreeSolver();
|
||||
return;
|
||||
nIter_ = 0;
|
||||
while (!converged() && nIter_ < SolverOptions::maxIter) {
|
||||
|
||||
nIter_ ++;
|
||||
if (DL >= 2) {
|
||||
cout << endl;
|
||||
cout << "****************************************" ;
|
||||
cout << "****************************************" ;
|
||||
cout << endl;
|
||||
cout << " Iteration " << nIter_ << endl;
|
||||
cout << "****************************************" ;
|
||||
cout << "****************************************" ;
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
switch (SolverOptions::schedule) {
|
||||
case SolverOptions::S_SEQ_RANDOM:
|
||||
random_shuffle (links_.begin(), links_.end());
|
||||
// no break
|
||||
|
||||
case SolverOptions::S_SEQ_FIXED:
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
links_[i]->setNextMessage (getFactor2VarMsg (links_[i]));
|
||||
links_[i]->updateMessage();
|
||||
}
|
||||
break;
|
||||
|
||||
case SolverOptions::S_PARALLEL:
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
links_[i]->setNextMessage (getFactor2VarMsg (links_[i]));
|
||||
}
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
links_[i]->updateMessage();
|
||||
}
|
||||
break;
|
||||
|
||||
case SolverOptions::S_MAX_RESIDUAL:
|
||||
maxResidualSchedule();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (DL >= 2) {
|
||||
cout << endl;
|
||||
if (nIter_ < SolverOptions::maxIter) {
|
||||
cout << "Loopy Sum-Product converged in " ;
|
||||
cout << nIter_ << " iterations" << endl;
|
||||
} else {
|
||||
cout << "The maximum number of iterations was hit, terminating..." ;
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
SPSolver::getPosterioriOf (Vid vid) const
|
||||
{
|
||||
assert (fg_->getFgVarNode (vid));
|
||||
FgVarNode* var = fg_->getFgVarNode (vid);
|
||||
ParamSet probs;
|
||||
|
||||
if (var->hasEvidence()) {
|
||||
probs.resize (var->getDomainSize(), 0.0);
|
||||
probs[var->getEvidence()] = 1.0;
|
||||
} else {
|
||||
probs.resize (var->getDomainSize(), 1.0);
|
||||
CLinkSet links = varsI_[var->getIndex()]->getLinks();
|
||||
for (unsigned i = 0; i < links.size(); i++) {
|
||||
CParamSet msg = links[i]->getMessage();
|
||||
for (unsigned j = 0; j < msg.size(); j++) {
|
||||
probs[j] *= msg[j];
|
||||
}
|
||||
}
|
||||
Util::normalize (probs);
|
||||
}
|
||||
return probs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
SPSolver::getJointDistributionOf (const VidSet& jointVids)
|
||||
{
|
||||
FgVarSet jointVars;
|
||||
unsigned dsize = 1;
|
||||
for (unsigned i = 0; i < jointVids.size(); i++) {
|
||||
FgVarNode* varNode = fg_->getFgVarNode (jointVids[i]);
|
||||
dsize *= varNode->getDomainSize();
|
||||
jointVars.push_back (varNode);
|
||||
}
|
||||
|
||||
unsigned maxVid = std::numeric_limits<unsigned>::max();
|
||||
FgVarNode* junctionVar = new FgVarNode (maxVid, dsize);
|
||||
FgVarSet factorVars = { junctionVar };
|
||||
for (unsigned i = 0; i < jointVars.size(); i++) {
|
||||
factorVars.push_back (jointVars[i]);
|
||||
}
|
||||
|
||||
unsigned nParams = dsize * dsize;
|
||||
ParamSet params (nParams);
|
||||
for (unsigned i = 0; i < nParams; i++) {
|
||||
unsigned row = i / dsize;
|
||||
unsigned col = i % dsize;
|
||||
if (row == col) {
|
||||
params[i] = 1;
|
||||
} else {
|
||||
params[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Distribution* dist = new Distribution (params, maxVid);
|
||||
Factor* newFactor = new Factor (factorVars, dist);
|
||||
fg_->addVariable (junctionVar);
|
||||
fg_->addFactor (newFactor);
|
||||
|
||||
runSolver();
|
||||
ParamSet results = getPosterioriOf (maxVid);
|
||||
deleteJunction (newFactor, junctionVar);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
SPSolver::initializeSolver (void)
|
||||
{
|
||||
fg_->setIndexes();
|
||||
|
||||
CFgVarSet vars = fg_->getFgVarNodes();
|
||||
for (unsigned i = 0; i < varsI_.size(); i++) {
|
||||
delete varsI_[i];
|
||||
}
|
||||
varsI_.reserve (vars.size());
|
||||
for (unsigned i = 0; i < vars.size(); i++) {
|
||||
varsI_.push_back (new SPNodeInfo());
|
||||
}
|
||||
|
||||
CFactorSet factors = fg_->getFactors();
|
||||
for (unsigned i = 0; i < factorsI_.size(); i++) {
|
||||
delete factorsI_[i];
|
||||
}
|
||||
factorsI_.reserve (factors.size());
|
||||
for (unsigned i = 0; i < factors.size(); i++) {
|
||||
factorsI_.push_back (new SPNodeInfo());
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
delete links_[i];
|
||||
}
|
||||
createLinks();
|
||||
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
Factor* source = links_[i]->getFactor();
|
||||
FgVarNode* dest = links_[i]->getVariable();
|
||||
varsI_[dest->getIndex()]->addLink (links_[i]);
|
||||
factorsI_[source->getIndex()]->addLink (links_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
SPSolver::createLinks (void)
|
||||
{
|
||||
CFactorSet factors = fg_->getFactors();
|
||||
for (unsigned i = 0; i < factors.size(); i++) {
|
||||
CFgVarSet neighbors = factors[i]->getFgVarNodes();
|
||||
for (unsigned j = 0; j < neighbors.size(); j++) {
|
||||
links_.push_back (new Link (factors[i], neighbors[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
SPSolver::deleteJunction (Factor* f, FgVarNode* v)
|
||||
{
|
||||
fg_->removeFactor (f);
|
||||
f->freeDistribution();
|
||||
delete f;
|
||||
fg_->removeVariable (v);
|
||||
delete v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
SPSolver::converged (void)
|
||||
{
|
||||
// this can happen if the graph is fully disconnected
|
||||
if (links_.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
if (nIter_ == 0 || nIter_ == 1) {
|
||||
return false;
|
||||
}
|
||||
bool converged = true;
|
||||
if (SolverOptions::schedule == SolverOptions::S_MAX_RESIDUAL) {
|
||||
Param maxResidual = (*(sortedOrder_.begin()))->getResidual();
|
||||
if (maxResidual < SolverOptions::accuracy) {
|
||||
converged = true;
|
||||
} else {
|
||||
converged = false;
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
double residual = links_[i]->getResidual();
|
||||
if (DL >= 2) {
|
||||
cout << links_[i]->toString() + " residual = " << residual << endl;
|
||||
}
|
||||
if (residual > SolverOptions::accuracy) {
|
||||
converged = false;
|
||||
if (DL == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return converged;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
SPSolver::maxResidualSchedule (void)
|
||||
{
|
||||
if (nIter_ == 1) {
|
||||
for (unsigned i = 0; i < links_.size(); i++) {
|
||||
links_[i]->setNextMessage (getFactor2VarMsg (links_[i]));
|
||||
SortedOrder::iterator it = sortedOrder_.insert (links_[i]);
|
||||
linkMap_.insert (make_pair (links_[i], it));
|
||||
if (DL >= 2 && DL < 5) {
|
||||
cout << "calculating " << links_[i]->toString() << endl;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned c = 0; c < links_.size(); c++) {
|
||||
if (DL >= 2) {
|
||||
cout << endl << "current residuals:" << endl;
|
||||
for (SortedOrder::iterator it = sortedOrder_.begin();
|
||||
it != sortedOrder_.end(); it ++) {
|
||||
cout << " " << setw (30) << left << (*it)->toString();
|
||||
cout << "residual = " << (*it)->getResidual() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
SortedOrder::iterator it = sortedOrder_.begin();
|
||||
Link* link = *it;
|
||||
if (DL >= 2) {
|
||||
cout << "updating " << (*sortedOrder_.begin())->toString() << endl;
|
||||
}
|
||||
if (link->getResidual() < SolverOptions::accuracy) {
|
||||
return;
|
||||
}
|
||||
link->updateMessage();
|
||||
link->clearResidual();
|
||||
sortedOrder_.erase (it);
|
||||
linkMap_.find (link)->second = sortedOrder_.insert (link);
|
||||
|
||||
// update the messages that depend on message source --> destin
|
||||
CFactorSet factorNeighbors = link->getVariable()->getFactors();
|
||||
for (unsigned i = 0; i < factorNeighbors.size(); i++) {
|
||||
if (factorNeighbors[i] != link->getFactor()) {
|
||||
CLinkSet links = factorsI_[factorNeighbors[i]->getIndex()]->getLinks();
|
||||
for (unsigned j = 0; j < links.size(); j++) {
|
||||
if (links[j]->getVariable() != link->getVariable()) {
|
||||
if (DL >= 2 && DL < 5) {
|
||||
cout << " calculating " << links[j]->toString() << endl;
|
||||
}
|
||||
links[j]->setNextMessage (getFactor2VarMsg (links[j]));
|
||||
LinkMap::iterator iter = linkMap_.find (links[j]);
|
||||
sortedOrder_.erase (iter->second);
|
||||
iter->second = sortedOrder_.insert (links[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
SPSolver::getFactor2VarMsg (const Link* link) const
|
||||
{
|
||||
const Factor* src = link->getFactor();
|
||||
const FgVarNode* dest = link->getVariable();
|
||||
CFgVarSet neighbors = src->getFgVarNodes();
|
||||
CLinkSet links = factorsI_[src->getIndex()]->getLinks();
|
||||
// calculate the product of messages that were sent
|
||||
// to factor `src', except from var `dest'
|
||||
Factor result (*src);
|
||||
Factor temp;
|
||||
if (DL >= 5) {
|
||||
cout << "calculating " ;
|
||||
cout << src->getLabel() << " --> " << dest->getLabel();
|
||||
cout << endl;
|
||||
}
|
||||
for (unsigned i = 0; i < neighbors.size(); i++) {
|
||||
if (links[i]->getVariable() != dest) {
|
||||
if (DL >= 5) {
|
||||
cout << " message from " << links[i]->getVariable()->getLabel();
|
||||
cout << ": " ;
|
||||
ParamSet p = getVar2FactorMsg (links[i]);
|
||||
cout << endl;
|
||||
Factor temp2 (links[i]->getVariable(), p);
|
||||
temp.multiplyByFactor (temp2);
|
||||
temp2.freeDistribution();
|
||||
} else {
|
||||
Factor temp2 (links[i]->getVariable(), getVar2FactorMsg (links[i]));
|
||||
temp.multiplyByFactor (temp2);
|
||||
temp2.freeDistribution();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (links.size() >= 2) {
|
||||
result.multiplyByFactor (temp, &(src->getCptEntries()));
|
||||
if (DL >= 5) {
|
||||
cout << " message product: " ;
|
||||
cout << Util::parametersToString (temp.getParameters()) << endl;
|
||||
cout << " factor product: " ;
|
||||
cout << Util::parametersToString (src->getParameters());
|
||||
cout << " x " ;
|
||||
cout << Util::parametersToString (temp.getParameters());
|
||||
cout << " = " ;
|
||||
cout << Util::parametersToString (result.getParameters()) << endl;
|
||||
}
|
||||
temp.freeDistribution();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < links.size(); i++) {
|
||||
if (links[i]->getVariable() != dest) {
|
||||
result.removeVariable (links[i]->getVariable());
|
||||
}
|
||||
}
|
||||
if (DL >= 5) {
|
||||
cout << " final message: " ;
|
||||
cout << Util::parametersToString (result.getParameters()) << endl << endl;
|
||||
}
|
||||
ParamSet msg = result.getParameters();
|
||||
result.freeDistribution();
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParamSet
|
||||
SPSolver::getVar2FactorMsg (const Link* link) const
|
||||
{
|
||||
const FgVarNode* src = link->getVariable();
|
||||
const Factor* dest = link->getFactor();
|
||||
ParamSet msg;
|
||||
if (src->hasEvidence()) {
|
||||
msg.resize (src->getDomainSize(), 0.0);
|
||||
msg[src->getEvidence()] = 1.0;
|
||||
if (DL >= 5) {
|
||||
cout << Util::parametersToString (msg);
|
||||
}
|
||||
} else {
|
||||
msg.resize (src->getDomainSize(), 1.0);
|
||||
}
|
||||
if (DL >= 5) {
|
||||
cout << Util::parametersToString (msg);
|
||||
}
|
||||
CLinkSet links = varsI_[src->getIndex()]->getLinks();
|
||||
for (unsigned i = 0; i < links.size(); i++) {
|
||||
if (links[i]->getFactor() != dest) {
|
||||
CParamSet msgFromFactor = links[i]->getMessage();
|
||||
for (unsigned j = 0; j < msgFromFactor.size(); j++) {
|
||||
msg[j] *= msgFromFactor[j];
|
||||
}
|
||||
if (DL >= 5) {
|
||||
cout << " x " << Util::parametersToString (msgFromFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DL >= 5) {
|
||||
cout << " = " << Util::parametersToString (msg);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
@ -1,130 +0,0 @@
|
||||
#ifndef BP_SP_SOLVER_H
|
||||
#define BP_SP_SOLVER_H
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "Solver.h"
|
||||
#include "FgVarNode.h"
|
||||
#include "Factor.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class FactorGraph;
|
||||
class SPSolver;
|
||||
|
||||
|
||||
class Link
|
||||
{
|
||||
public:
|
||||
Link (Factor* f, FgVarNode* v)
|
||||
{
|
||||
factor_ = f;
|
||||
var_ = v;
|
||||
currMsg_.resize (v->getDomainSize(), 1);
|
||||
nextMsg_.resize (v->getDomainSize(), 1);
|
||||
msgSended_ = false;
|
||||
residual_ = 0.0;
|
||||
}
|
||||
|
||||
void setMessage (ParamSet msg)
|
||||
{
|
||||
Util::normalize (msg);
|
||||
residual_ = Util::getMaxNorm (currMsg_, msg);
|
||||
currMsg_ = msg;
|
||||
}
|
||||
|
||||
void setNextMessage (CParamSet msg)
|
||||
{
|
||||
nextMsg_ = msg;
|
||||
Util::normalize (nextMsg_);
|
||||
residual_ = Util::getMaxNorm (currMsg_, nextMsg_);
|
||||
}
|
||||
|
||||
void updateMessage (void)
|
||||
{
|
||||
currMsg_ = nextMsg_;
|
||||
msgSended_ = true;
|
||||
}
|
||||
|
||||
string toString (void) const
|
||||
{
|
||||
stringstream ss;
|
||||
ss << factor_->getLabel();
|
||||
ss << " -- " ;
|
||||
ss << var_->getLabel();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Factor* getFactor (void) const { return factor_; }
|
||||
FgVarNode* getVariable (void) const { return var_; }
|
||||
CParamSet getMessage (void) const { return currMsg_; }
|
||||
bool messageWasSended (void) const { return msgSended_; }
|
||||
double getResidual (void) const { return residual_; }
|
||||
void clearResidual (void) { residual_ = 0.0; }
|
||||
|
||||
private:
|
||||
Factor* factor_;
|
||||
FgVarNode* var_;
|
||||
ParamSet currMsg_;
|
||||
ParamSet nextMsg_;
|
||||
bool msgSended_;
|
||||
double residual_;
|
||||
};
|
||||
|
||||
|
||||
class SPNodeInfo
|
||||
{
|
||||
public:
|
||||
void addLink (Link* link) { links_.push_back (link); }
|
||||
CLinkSet getLinks (void) { return links_; }
|
||||
|
||||
private:
|
||||
LinkSet links_;
|
||||
};
|
||||
|
||||
|
||||
class SPSolver : public Solver
|
||||
{
|
||||
public:
|
||||
SPSolver (FactorGraph&);
|
||||
virtual ~SPSolver (void);
|
||||
|
||||
void runSolver (void);
|
||||
virtual ParamSet getPosterioriOf (Vid) const;
|
||||
ParamSet getJointDistributionOf (CVidSet);
|
||||
|
||||
protected:
|
||||
virtual void initializeSolver (void);
|
||||
void runTreeSolver (void);
|
||||
bool readyToSendMessage (const Link*) const;
|
||||
virtual void createLinks (void);
|
||||
virtual void deleteJunction (Factor*, FgVarNode*);
|
||||
bool converged (void);
|
||||
virtual void maxResidualSchedule (void);
|
||||
virtual ParamSet getFactor2VarMsg (const Link*) const;
|
||||
virtual ParamSet getVar2FactorMsg (const Link*) const;
|
||||
|
||||
struct CompareResidual {
|
||||
inline bool operator() (const Link* link1, const Link* link2)
|
||||
{
|
||||
return link1->getResidual() > link2->getResidual();
|
||||
}
|
||||
};
|
||||
|
||||
FactorGraph* fg_;
|
||||
LinkSet links_;
|
||||
vector<SPNodeInfo*> varsI_;
|
||||
vector<SPNodeInfo*> factorsI_;
|
||||
unsigned nIter_;
|
||||
|
||||
typedef multiset<Link*, CompareResidual> SortedOrder;
|
||||
SortedOrder sortedOrder_;
|
||||
|
||||
typedef map<Link*, SortedOrder::iterator> LinkMap;
|
||||
LinkMap linkMap_;
|
||||
|
||||
};
|
||||
|
||||
#endif // BP_SP_SOLVER_H
|
||||
|
@ -1,172 +0,0 @@
|
||||
#ifndef BP_VARIABLE_H
|
||||
#define BP_VARIABLE_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "Shared.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Variable
|
||||
{
|
||||
public:
|
||||
|
||||
Variable (const Variable* v)
|
||||
{
|
||||
vid_ = v->getVarId();
|
||||
dsize_ = v->getDomainSize();
|
||||
if (v->hasDomain()) {
|
||||
domain_ = v->getDomain();
|
||||
dsize_ = domain_.size();
|
||||
} else {
|
||||
dsize_ = v->getDomainSize();
|
||||
}
|
||||
evidence_ = v->getEvidence();
|
||||
if (v->hasLabel()) {
|
||||
label_ = new string (v->getLabel());
|
||||
} else {
|
||||
label_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Variable (Vid vid)
|
||||
{
|
||||
this->vid_ = vid;
|
||||
this->dsize_ = 0;
|
||||
this->evidence_ = NO_EVIDENCE;
|
||||
this->label_ = 0;
|
||||
}
|
||||
|
||||
Variable (Vid vid, unsigned dsize, int evidence = NO_EVIDENCE,
|
||||
const string& lbl = string())
|
||||
{
|
||||
assert (dsize != 0);
|
||||
assert (evidence < (int)dsize);
|
||||
this->vid_ = vid;
|
||||
this->dsize_ = dsize;
|
||||
this->evidence_ = evidence;
|
||||
if (!lbl.empty()) {
|
||||
this->label_ = new string (lbl);
|
||||
} else {
|
||||
this->label_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Variable (Vid vid, const Domain& domain, int evidence = NO_EVIDENCE,
|
||||
const string& lbl = string())
|
||||
{
|
||||
assert (!domain.empty());
|
||||
assert (evidence < (int)domain.size());
|
||||
this->vid_ = vid;
|
||||
this->dsize_ = domain.size();
|
||||
this->domain_ = domain;
|
||||
this->evidence_ = evidence;
|
||||
if (!lbl.empty()) {
|
||||
this->label_ = new string (lbl);
|
||||
} else {
|
||||
this->label_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
~Variable (void)
|
||||
{
|
||||
delete label_;
|
||||
}
|
||||
|
||||
unsigned getVarId (void) const { return vid_; }
|
||||
unsigned getIndex (void) const { return index_; }
|
||||
void setIndex (unsigned idx) { index_ = idx; }
|
||||
unsigned getDomainSize (void) const { return dsize_; }
|
||||
bool hasEvidence (void) const { return evidence_ != NO_EVIDENCE; }
|
||||
int getEvidence (void) const { return evidence_; }
|
||||
bool hasDomain (void) const { return !domain_.empty(); }
|
||||
bool hasLabel (void) const { return label_ != 0; }
|
||||
|
||||
bool isValidStateIndex (int index)
|
||||
{
|
||||
return index >= 0 && index < (int)dsize_;
|
||||
}
|
||||
|
||||
bool isValidState (const string& state)
|
||||
{
|
||||
return find (domain_.begin(), domain_.end(), state) != domain_.end();
|
||||
}
|
||||
|
||||
Domain getDomain (void) const
|
||||
{
|
||||
assert (dsize_ != 0);
|
||||
if (domain_.size() == 0) {
|
||||
Domain d;
|
||||
for (unsigned i = 0; i < dsize_; i++) {
|
||||
stringstream ss;
|
||||
ss << "x" << i ;
|
||||
d.push_back (ss.str());
|
||||
}
|
||||
return d;
|
||||
} else {
|
||||
return domain_;
|
||||
}
|
||||
}
|
||||
|
||||
void setDomainSize (unsigned dsize)
|
||||
{
|
||||
assert (dsize != 0);
|
||||
dsize_ = dsize;
|
||||
}
|
||||
|
||||
void setDomain (const Domain& domain)
|
||||
{
|
||||
assert (!domain.empty());
|
||||
domain_ = domain;
|
||||
dsize_ = domain.size();
|
||||
}
|
||||
|
||||
void setEvidence (int ev)
|
||||
{
|
||||
assert (ev < dsize_);
|
||||
evidence_ = ev;
|
||||
}
|
||||
|
||||
void setEvidence (const string& ev)
|
||||
{
|
||||
assert (isValidState (ev));
|
||||
for (unsigned i = 0; i < domain_.size(); i++) {
|
||||
if (domain_[i] == ev) {
|
||||
evidence_ = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setLabel (const string& label)
|
||||
{
|
||||
label_ = new string (label);
|
||||
}
|
||||
|
||||
string getLabel (void) const
|
||||
{
|
||||
if (label_ == 0) {
|
||||
stringstream ss;
|
||||
ss << "v" << vid_;
|
||||
return ss.str();
|
||||
} else {
|
||||
return *label_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN (Variable);
|
||||
|
||||
Vid vid_;
|
||||
unsigned dsize_;
|
||||
int evidence_;
|
||||
Domain domain_;
|
||||
string* label_;
|
||||
unsigned index_;
|
||||
|
||||
};
|
||||
|
||||
#endif // BP_VARIABLE_H
|
||||
|
@ -1,147 +0,0 @@
|
||||
|
||||
/*
|
||||
----------------------------------------------------------------
|
||||
|
||||
Notice that the following BSD-style license applies to this one
|
||||
file (callgrind.h) only. The rest of Valgrind is licensed under the
|
||||
terms of the GNU General Public License, version 2, unless
|
||||
otherwise indicated. See the COPYING file in the source
|
||||
distribution for details.
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
This file is part of callgrind, a valgrind tool for cache simulation
|
||||
and call tree tracing.
|
||||
|
||||
Copyright (C) 2003-2010 Josef Weidendorfer. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product
|
||||
documentation would be appreciated but is not required.
|
||||
|
||||
3. Altered source versions must be plainly marked as such, and must
|
||||
not be misrepresented as being the original software.
|
||||
|
||||
4. The name of the author may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
Notice that the above BSD-style license applies to this one file
|
||||
(callgrind.h) only. The entire rest of Valgrind is licensed under
|
||||
the terms of the GNU General Public License, version 2. See the
|
||||
COPYING file in the source distribution for details.
|
||||
|
||||
----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __CALLGRIND_H
|
||||
#define __CALLGRIND_H
|
||||
|
||||
#include "valgrind.h"
|
||||
|
||||
/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
|
||||
This enum comprises an ABI exported by Valgrind to programs
|
||||
which use client requests. DO NOT CHANGE THE ORDER OF THESE
|
||||
ENTRIES, NOR DELETE ANY -- add new ones at the end.
|
||||
|
||||
The identification ('C','T') for Callgrind has historical
|
||||
reasons: it was called "Calltree" before. Besides, ('C','G') would
|
||||
clash with cachegrind.
|
||||
*/
|
||||
|
||||
typedef
|
||||
enum {
|
||||
VG_USERREQ__DUMP_STATS = VG_USERREQ_TOOL_BASE('C','T'),
|
||||
VG_USERREQ__ZERO_STATS,
|
||||
VG_USERREQ__TOGGLE_COLLECT,
|
||||
VG_USERREQ__DUMP_STATS_AT,
|
||||
VG_USERREQ__START_INSTRUMENTATION,
|
||||
VG_USERREQ__STOP_INSTRUMENTATION
|
||||
} Vg_CallgrindClientRequest;
|
||||
|
||||
/* Dump current state of cost centers, and zero them afterwards */
|
||||
#define CALLGRIND_DUMP_STATS \
|
||||
{unsigned int _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__DUMP_STATS, \
|
||||
0, 0, 0, 0, 0); \
|
||||
}
|
||||
|
||||
/* Dump current state of cost centers, and zero them afterwards.
|
||||
The argument is appended to a string stating the reason which triggered
|
||||
the dump. This string is written as a description field into the
|
||||
profile data dump. */
|
||||
#define CALLGRIND_DUMP_STATS_AT(pos_str) \
|
||||
{unsigned int _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__DUMP_STATS_AT, \
|
||||
pos_str, 0, 0, 0, 0); \
|
||||
}
|
||||
|
||||
/* Zero cost centers */
|
||||
#define CALLGRIND_ZERO_STATS \
|
||||
{unsigned int _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__ZERO_STATS, \
|
||||
0, 0, 0, 0, 0); \
|
||||
}
|
||||
|
||||
/* Toggles collection state.
|
||||
The collection state specifies whether the happening of events
|
||||
should be noted or if they are to be ignored. Events are noted
|
||||
by increment of counters in a cost center */
|
||||
#define CALLGRIND_TOGGLE_COLLECT \
|
||||
{unsigned int _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__TOGGLE_COLLECT, \
|
||||
0, 0, 0, 0, 0); \
|
||||
}
|
||||
|
||||
/* Start full callgrind instrumentation if not already switched on.
|
||||
When cache simulation is done, it will flush the simulated cache;
|
||||
this will lead to an artifical cache warmup phase afterwards with
|
||||
cache misses which would not have happened in reality. */
|
||||
#define CALLGRIND_START_INSTRUMENTATION \
|
||||
{unsigned int _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__START_INSTRUMENTATION, \
|
||||
0, 0, 0, 0, 0); \
|
||||
}
|
||||
|
||||
/* Stop full callgrind instrumentation if not already switched off.
|
||||
This flushes Valgrinds translation cache, and does no additional
|
||||
instrumentation afterwards, which effectivly will run at the same
|
||||
speed as the "none" tool (ie. at minimal slowdown).
|
||||
Use this to bypass Callgrind aggregation for uninteresting code parts.
|
||||
To start Callgrind in this mode to ignore the setup phase, use
|
||||
the option "--instr-atstart=no". */
|
||||
#define CALLGRIND_STOP_INSTRUMENTATION \
|
||||
{unsigned int _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__STOP_INSTRUMENTATION, \
|
||||
0, 0, 0, 0, 0); \
|
||||
}
|
||||
|
||||
#endif /* __CALLGRIND_H */
|
14
packages/CLPBN/clpbn/bp/examples/cbp_example.uai
Normal file
14
packages/CLPBN/clpbn/bp/examples/cbp_example.uai
Normal file
@ -0,0 +1,14 @@
|
||||
MARKOV
|
||||
3
|
||||
2 2 2
|
||||
2
|
||||
2 0 1
|
||||
2 2 1
|
||||
|
||||
|
||||
4
|
||||
1.2 1.4 2.0 0.4
|
||||
|
||||
4
|
||||
1.2 1.4 2.0 0.4
|
||||
|
@ -1,53 +0,0 @@
|
||||
|
||||
:- use_module(library(clpbn)).
|
||||
|
||||
:- set_clpbn_flag(solver, bp).
|
||||
|
||||
%
|
||||
% A E
|
||||
% / \ /
|
||||
% / \ /
|
||||
% B C
|
||||
% \ /
|
||||
% \ /
|
||||
% D
|
||||
%
|
||||
|
||||
a(A) :-
|
||||
a_table(ADist),
|
||||
{ A = a with p([a1, a2], ADist) }.
|
||||
|
||||
b(B) :-
|
||||
a(A),
|
||||
b_table(BDist),
|
||||
{ B = b with p([b1, b2], BDist, [A]) }.
|
||||
|
||||
c(C) :-
|
||||
a(A),
|
||||
c_table(CDist),
|
||||
{ C = c with p([c1, c2], CDist, [A]) }.
|
||||
|
||||
d(D) :-
|
||||
b(B),
|
||||
c(C),
|
||||
d_table(DDist),
|
||||
{ D = d with p([d1, d2], DDist, [B, C]) }.
|
||||
|
||||
e(E) :-
|
||||
e_table(EDist),
|
||||
{ E = e with p([e1, e2], EDist) }.
|
||||
|
||||
|
||||
a_table([0.005, 0.995]).
|
||||
|
||||
b_table([0.02, 0.97,
|
||||
0.88, 0.03]).
|
||||
|
||||
c_table([0.55, 0.94,
|
||||
0.45, 0.06]).
|
||||
|
||||
d_table([0.192, 0.98, 0.33, 0.013,
|
||||
0.908, 0.02, 0.77, 0.987]).
|
||||
|
||||
e_table([0.055, 0.945]).
|
||||
|
@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
|
||||
cp ~/bin/yap ~/bin/town_comp
|
||||
YAP=~/bin/town_comp
|
||||
|
||||
#OUT_FILE_NAME=results`date "+ %H:%M:%S %d-%m-%Y"`.log
|
||||
OUT_FILE_NAME=bp_compress.log
|
||||
rm -f $OUT_FILE_NAME
|
||||
rm -f ignore.$OUT_FILE_NAME
|
||||
|
||||
|
||||
function run_solver
|
||||
{
|
||||
if [ $2 = bp ]
|
||||
then
|
||||
extra_flag1=clpbn_bp:set_solver_parameter\(run_mode,$4\)
|
||||
extra_flag2=clpbn_bp:set_solver_parameter\(schedule,$5\)
|
||||
extra_flag3=clpbn_bp:set_solver_parameter\(always_loopy_solver,$6\)
|
||||
else
|
||||
extra_flag1=true
|
||||
extra_flag2=true
|
||||
extra_flag3=true
|
||||
fi
|
||||
/usr/bin/time -o $OUT_FILE_NAME -a -f "real:%E\tuser:%U\tsys:%S" $YAP << EOF >> $OUT_FILE_NAME 2>> ignore.$OUT_FILE_NAME
|
||||
[$1].
|
||||
clpbn:set_clpbn_flag(solver,$2),
|
||||
clpbn_bp:use_log_space,
|
||||
$extra_flag1, $extra_flag2, $extra_flag3,
|
||||
run_query(_R),
|
||||
open("$OUT_FILE_NAME", 'append',S),
|
||||
format(S, '$3: ~15+ ',[]),
|
||||
close(S).
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
function run_all_graphs
|
||||
{
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
echo "results for solver $2" >> $OUT_FILE_NAME
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
run_solver town_1000 $1 town_1000 $3 $4 $5
|
||||
run_solver town_5000 $1 town_5000 $3 $4 $5
|
||||
run_solver town_10000 $1 town_10000 $3 $4 $5
|
||||
run_solver town_50000 $1 town_50000 $3 $4 $5
|
||||
run_solver town_100000 $1 town_100000 $3 $4 $5
|
||||
run_solver town_500000 $1 town_500000 $3 $4 $5
|
||||
run_solver town_1000000 $1 town_1000000 $3 $4 $5
|
||||
run_solver town_2500000 $1 town_2500000 $3 $4 $5
|
||||
run_solver town_5000000 $1 town_5000000 $3 $4 $5
|
||||
run_solver town_7500000 $1 town_7500000 $3 $4 $5
|
||||
run_solver town_10000000 $1 town_10000000 $3 $4 $5
|
||||
}
|
||||
|
||||
run_solver town_10000 "bp(compress,seq_fixed)" town_10000 compress seq_fixed true
|
||||
exit
|
||||
|
||||
##########
|
||||
run_all_graphs bp "bp(compress,seq_fixed) " compress seq_fixed true
|
||||
|
51
packages/CLPBN/clpbn/bp/examples/town/run_town_bp_convert.sh
Normal file
51
packages/CLPBN/clpbn/bp/examples/town/run_town_bp_convert.sh
Normal file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
YAP=~/bin/town_conv
|
||||
|
||||
#OUT_FILE_NAME=results`date "+ %H:%M:%S %d-%m-%Y"`.log
|
||||
OUT_FILE_NAME=bp_convert.log
|
||||
rm -f $OUT_FILE_NAME
|
||||
rm -f ignore.$OUT_FILE_NAME
|
||||
|
||||
|
||||
function run_solver
|
||||
{
|
||||
if [ $2 = bp ]
|
||||
then
|
||||
extra_flag1=clpbn_bp:set_solver_parameter\(run_mode,$4\)
|
||||
extra_flag2=clpbn_bp:set_solver_parameter\(schedule,$5\)
|
||||
extra_flag3=clpbn_bp:set_solver_parameter\(always_loopy_solver,$6\)
|
||||
else
|
||||
extra_flag1=true
|
||||
extra_flag2=true
|
||||
extra_flag3=true
|
||||
fi
|
||||
/usr/bin/time -o $OUT_FILE_NAME -a -f "real:%E\tuser:%U\tsys:%S" $YAP << EOF >> $OUT_FILE_NAME 2>> ignore.$OUT_FILE_NAME
|
||||
[$1].
|
||||
clpbn:set_clpbn_flag(solver,$2),
|
||||
clpbn_bp:use_log_space,
|
||||
$extra_flag1, $extra_flag2, $extra_flag3,
|
||||
run_query(_R),
|
||||
open("$OUT_FILE_NAME", 'append',S),
|
||||
format(S, '$3: ~15+ ',[]),
|
||||
close(S).
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
function run_all_graphs
|
||||
{
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
echo "results for solver $2" >> $OUT_FILE_NAME
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
run_solver town_1000 $1 town_1000 $3 $4 $5
|
||||
run_solver town_5000 $1 town_5000 $3 $4 $5
|
||||
run_solver town_10000 $1 town_10000 $3 $4 $5
|
||||
run_solver town_50000 $1 town_50000 $3 $4 $5
|
||||
run_solver town_100000 $1 town_100000 $3 $4 $5
|
||||
run_solver town_500000 $1 town_500000 $3 $4 $5
|
||||
run_solver town_1000000 $1 town_1000000 $3 $4 $5
|
||||
}
|
||||
|
||||
run_all_graphs bp "bp(convert,seq_fixed) " convert seq_fixed false
|
||||
|
50
packages/CLPBN/clpbn/bp/examples/town/run_town_bp_normal.sh
Normal file
50
packages/CLPBN/clpbn/bp/examples/town/run_town_bp_normal.sh
Normal file
@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
YAP=~/bin/town_norm
|
||||
|
||||
#OUT_FILE_NAME=results`date "+ %H:%M:%S %d-%m-%Y"`.log
|
||||
OUT_FILE_NAME=bp_normal.log
|
||||
rm -f $OUT_FILE_NAME
|
||||
rm -f ignore.$OUT_FILE_NAME
|
||||
|
||||
|
||||
function run_solver
|
||||
{
|
||||
if [ $2 = bp ]
|
||||
then
|
||||
extra_flag1=clpbn_bp:set_solver_parameter\(run_mode,$4\)
|
||||
extra_flag2=clpbn_bp:set_solver_parameter\(schedule,$5\)
|
||||
extra_flag3=clpbn_bp:set_solver_parameter\(always_loopy_solver,$6\)
|
||||
else
|
||||
extra_flag1=true
|
||||
extra_flag2=true
|
||||
extra_flag3=true
|
||||
fi
|
||||
/usr/bin/time -o $OUT_FILE_NAME -a -f "real:%E\tuser:%U\tsys:%S" $YAP << EOF >> $OUT_FILE_NAME 2>> ignore.$OUT_FILE_NAME
|
||||
[$1].
|
||||
clpbn:set_clpbn_flag(solver,$2),
|
||||
clpbn_bp:use_log_space,
|
||||
$extra_flag1, $extra_flag2, $extra_flag3,
|
||||
run_query(_R),
|
||||
open("$OUT_FILE_NAME", 'append',S),
|
||||
format(S, '$3: ~15+ ',[]),
|
||||
close(S).
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
function run_all_graphs
|
||||
{
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
echo "results for solver $2" >> $OUT_FILE_NAME
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
run_solver town_1000 $1 town_1000 $3 $4 $5
|
||||
run_solver town_5000 $1 town_5000 $3 $4 $5
|
||||
run_solver town_10000 $1 town_10000 $3 $4 $5
|
||||
run_solver town_50000 $1 town_50000 $3 $4 $5
|
||||
run_solver town_100000 $1 town_100000 $3 $4 $5
|
||||
run_solver town_500000 $1 town_500000 $3 $4 $5
|
||||
run_solver town_1000000 $1 town_1000000 $3 $4 $5
|
||||
}
|
||||
|
||||
run_all_graphs bp "bp(normal,seq_fixed) " normal seq_fixed false
|
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
YAP=~/bin/town_gibbs
|
||||
|
||||
#OUT_FILE_NAME=results`date "+ %H:%M:%S %d-%m-%Y"`.log
|
||||
OUT_FILE_NAME=gibbs.log
|
||||
rm -f $OUT_FILE_NAME
|
||||
rm -f ignore.$OUT_FILE_NAME
|
||||
|
||||
|
||||
function run_solver
|
||||
{
|
||||
if [ $2 = bp ]
|
||||
then
|
||||
extra_flag1=clpbn_bp:set_solver_parameter\(run_mode,$4\)
|
||||
extra_flag2=clpbn_bp:set_solver_parameter\(schedule,$5\)
|
||||
extra_flag3=clpbn_bp:set_solver_parameter\(always_loopy_solver,$6\)
|
||||
else
|
||||
extra_flag1=true
|
||||
extra_flag2=true
|
||||
extra_flag3=true
|
||||
fi
|
||||
/usr/bin/time -o $OUT_FILE_NAME -a -f "real:%E\tuser:%U\tsys:%S" $YAP << EOF >> $OUT_FILE_NAME 2>> ignore.$OUT_FILE_NAME
|
||||
[$1].
|
||||
clpbn:set_clpbn_flag(solver,$2),
|
||||
clpbn_bp:use_log_space,
|
||||
$extra_flag1, $extra_flag2, $extra_flag3,
|
||||
run_query(_R),
|
||||
open("$OUT_FILE_NAME", 'append',S),
|
||||
format(S, '$3: ~15+ ',[]),
|
||||
close(S).
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
function run_all_graphs
|
||||
{
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
echo "results for solver $2" >> $OUT_FILE_NAME
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
run_solver town_1000 $1 town_1000 $3 $4 $5
|
||||
run_solver town_5000 $1 town_5000 $3 $4 $5
|
||||
run_solver town_10000 $1 town_10000 $3 $4 $5
|
||||
run_solver town_50000 $1 town_50000 $3 $4 $5
|
||||
run_solver town_100000 $1 town_100000 $3 $4 $5
|
||||
run_solver town_500000 $1 town_500000 $3 $4 $5
|
||||
run_solver town_1000000 $1 town_1000000 $3 $4 $5
|
||||
}
|
||||
|
||||
run_all_graphs gibbs "gibbs "
|
||||
|
51
packages/CLPBN/clpbn/bp/examples/town/run_town_jt_tests.sh
Normal file
51
packages/CLPBN/clpbn/bp/examples/town/run_town_jt_tests.sh
Normal file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
YAP=~/bin/town_jt
|
||||
|
||||
#OUT_FILE_NAME=results`date "+ %H:%M:%S %d-%m-%Y"`.log
|
||||
OUT_FILE_NAME=jt.log
|
||||
rm -f $OUT_FILE_NAME
|
||||
rm -f ignore.$OUT_FILE_NAME
|
||||
|
||||
|
||||
function run_solver
|
||||
{
|
||||
if [ $2 = bp ]
|
||||
then
|
||||
extra_flag1=clpbn_bp:set_solver_parameter\(run_mode,$4\)
|
||||
extra_flag2=clpbn_bp:set_solver_parameter\(schedule,$5\)
|
||||
extra_flag3=clpbn_bp:set_solver_parameter\(always_loopy_solver,$6\)
|
||||
else
|
||||
extra_flag1=true
|
||||
extra_flag2=true
|
||||
extra_flag3=true
|
||||
fi
|
||||
/usr/bin/time -o $OUT_FILE_NAME -a -f "real:%E\tuser:%U\tsys:%S" $YAP << EOF >> $OUT_FILE_NAME 2>> ignore.$OUT_FILE_NAME
|
||||
[$1].
|
||||
clpbn:set_clpbn_flag(solver,$2),
|
||||
clpbn_bp:use_log_space,
|
||||
$extra_flag1, $extra_flag2, $extra_flag3,
|
||||
run_query(_R),
|
||||
open("$OUT_FILE_NAME", 'append',S),
|
||||
format(S, '$3: ~15+ ',[]),
|
||||
close(S).
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
function run_all_graphs
|
||||
{
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
echo "results for solver $2" >> $OUT_FILE_NAME
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
run_solver town_1000 $1 town_1000 $3 $4 $5
|
||||
run_solver town_5000 $1 town_5000 $3 $4 $5
|
||||
run_solver town_10000 $1 town_10000 $3 $4 $5
|
||||
run_solver town_50000 $1 town_50000 $3 $4 $5
|
||||
run_solver town_100000 $1 town_100000 $3 $4 $5
|
||||
run_solver town_500000 $1 town_500000 $3 $4 $5
|
||||
run_solver town_1000000 $1 town_1000000 $3 $4 $5
|
||||
}
|
||||
|
||||
run_all_graphs jt "jt "
|
||||
|
51
packages/CLPBN/clpbn/bp/examples/town/run_town_ve_tests.sh
Normal file
51
packages/CLPBN/clpbn/bp/examples/town/run_town_ve_tests.sh
Normal file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
YAP=~/bin/town_ve
|
||||
|
||||
#OUT_FILE_NAME=results`date "+ %H:%M:%S %d-%m-%Y"`.log
|
||||
OUT_FILE_NAME=ve.log
|
||||
rm -f $OUT_FILE_NAME
|
||||
rm -f ignore.$OUT_FILE_NAME
|
||||
|
||||
|
||||
function run_solver
|
||||
{
|
||||
if [ $2 = bp ]
|
||||
then
|
||||
extra_flag1=clpbn_bp:set_solver_parameter\(run_mode,$4\)
|
||||
extra_flag2=clpbn_bp:set_solver_parameter\(schedule,$5\)
|
||||
extra_flag3=clpbn_bp:set_solver_parameter\(always_loopy_solver,$6\)
|
||||
else
|
||||
extra_flag1=true
|
||||
extra_flag2=true
|
||||
extra_flag3=true
|
||||
fi
|
||||
/usr/bin/time -o $OUT_FILE_NAME -a -f "real:%E\tuser:%U\tsys:%S" $YAP << EOF >> $OUT_FILE_NAME 2>> ignore.$OUT_FILE_NAME
|
||||
[$1].
|
||||
clpbn:set_clpbn_flag(solver,$2),
|
||||
clpbn_bp:use_log_space,
|
||||
$extra_flag1, $extra_flag2, $extra_flag3,
|
||||
run_query(_R),
|
||||
open("$OUT_FILE_NAME", 'append',S),
|
||||
format(S, '$3: ~15+ ',[]),
|
||||
close(S).
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
function run_all_graphs
|
||||
{
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
echo "results for solver $2" >> $OUT_FILE_NAME
|
||||
echo "*******************************************************************" >> "$OUT_FILE_NAME"
|
||||
run_solver town_1000 $1 town_1000 $3 $4 $5
|
||||
run_solver town_5000 $1 town_5000 $3 $4 $5
|
||||
run_solver town_10000 $1 town_10000 $3 $4 $5
|
||||
run_solver town_50000 $1 town_50000 $3 $4 $5
|
||||
run_solver town_100000 $1 town_100000 $3 $4 $5
|
||||
run_solver town_500000 $1 town_500000 $3 $4 $5
|
||||
run_solver town_1000000 $1 town_1000000 $3 $4 $5
|
||||
}
|
||||
|
||||
run_all_graphs ve "ve "
|
||||
|
65
packages/CLPBN/clpbn/bp/examples/town/schema.yap
Normal file
65
packages/CLPBN/clpbn/bp/examples/town/schema.yap
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
conservative_city(City, Cons) :-
|
||||
cons_table(City, ConsDist),
|
||||
{ Cons = conservative_city(City) with p([y,n], ConsDist) }.
|
||||
|
||||
|
||||
gender(X, Gender) :-
|
||||
gender_table(X, GenderDist),
|
||||
{ Gender = gender(X) with p([m,f], GenderDist) }.
|
||||
|
||||
|
||||
hair_color(X, Color) :-
|
||||
lives(X, City),
|
||||
conservative_city(City, Cons),
|
||||
hair_color_table(X,ColorTable),
|
||||
{ Color = hair_color(X) with
|
||||
p([t,f], ColorTable,[Cons]) }.
|
||||
|
||||
|
||||
car_color(X, Color) :-
|
||||
hair_color(X, HColor),
|
||||
car_color_table(X,CColorTable),
|
||||
{ Color = car_color(X) with
|
||||
p([t,f], CColorTable,[HColor]) }.
|
||||
|
||||
|
||||
height(X, Height) :-
|
||||
gender(X, Gender),
|
||||
height_table(X,HeightTable),
|
||||
{ Height = height(X) with
|
||||
p([t,f], HeightTable,[Gender]) }.
|
||||
|
||||
|
||||
shoe_size(X, Shoesize) :-
|
||||
height(X, Height),
|
||||
shoe_size_table(X,ShoesizeTable),
|
||||
{ Shoesize = shoe_size(X) with
|
||||
p([t,f], ShoesizeTable,[Height]) }.
|
||||
|
||||
|
||||
guilty(X, Guilt) :-
|
||||
guilty_table(X, GuiltDist),
|
||||
{ Guilt = guilty(X) with p([y,n], GuiltDist) }.
|
||||
|
||||
|
||||
descn(X, Descn) :-
|
||||
car_color(X, Car),
|
||||
hair_color(X, Hair),
|
||||
height(X, Height),
|
||||
guilty(X, Guilt),
|
||||
descn_table(X, DescTable),
|
||||
{ Descn = descn(X) with
|
||||
p([t,f], DescTable,[Car,Hair,Height,Guilt]) }.
|
||||
|
||||
|
||||
witness(City, Witness) :-
|
||||
descn(joe, DescnJ),
|
||||
descn(p2, Descn2),
|
||||
wit_table(WitTable),
|
||||
{ Witness = witness(City) with
|
||||
p([t,f], WitTable,[DescnJ, Descn2]) }.
|
||||
|
||||
|
||||
:- ensure_loaded(tables).
|
||||
|
46
packages/CLPBN/clpbn/bp/examples/town/tables.yap
Normal file
46
packages/CLPBN/clpbn/bp/examples/town/tables.yap
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
cons_table(amsterdam, [0.2, 0.8]) :- !.
|
||||
cons_table(_, [0.8, 0.2]).
|
||||
|
||||
|
||||
gender_table(_, [0.55, 0.44]).
|
||||
|
||||
|
||||
hair_color_table(_,
|
||||
/* conservative_city */
|
||||
/* y n */
|
||||
[ 0.05, 0.1,
|
||||
0.95, 0.9 ]).
|
||||
|
||||
|
||||
car_color_table(_,
|
||||
/* t f */
|
||||
[ 0.9, 0.2,
|
||||
0.1, 0.8 ]).
|
||||
|
||||
|
||||
height_table(_,
|
||||
/* m f */
|
||||
[ 0.6, 0.4,
|
||||
0.4, 0.6 ]).
|
||||
|
||||
|
||||
shoe_size_table(_,
|
||||
/* t f */
|
||||
[ 0.9, 0.1,
|
||||
0.1, 0.9 ]).
|
||||
|
||||
|
||||
guilty_table(_, [0.23, 0.77]).
|
||||
|
||||
|
||||
descn_table(_,
|
||||
/* color, hair, height, guilt */
|
||||
/* ttttt tttf ttft ttff tfttt tftf tfft tfff ttttt fttf ftft ftff ffttt fftf ffft ffff */
|
||||
[ 0.99, 0.5, 0.23, 0.88, 0.41, 0.3, 0.76, 0.87, 0.44, 0.43, 0.29, 0.72, 0.33, 0.91, 0.95, 0.92,
|
||||
0.01, 0.5, 0.77, 0.12, 0.59, 0.7, 0.24, 0.13, 0.56, 0.57, 0.61, 0.28, 0.77, 0.09, 0.05, 0.08]).
|
||||
|
||||
|
||||
wit_table([0.2, 0.45, 0.24, 0.34,
|
||||
0.8, 0.55, 0.76, 0.66]).
|
||||
|
59
packages/CLPBN/clpbn/bp/examples/town/town_generator.sh
Normal file
59
packages/CLPBN/clpbn/bp/examples/town/town_generator.sh
Normal file
@ -0,0 +1,59 @@
|
||||
#!/home/tgomes/bin/yap -L --
|
||||
|
||||
/*
|
||||
Steps:
|
||||
1. generate N facts lives(I, nyc), 0 <= I < N.
|
||||
2. generate evidence on descn for N people, *** except for 1 ***
|
||||
3. Run query ?- guilty(joe, Guilty), witness(joe, t), descn(2,t), descn(3, f), descn(4, f) ...
|
||||
*/
|
||||
|
||||
:- initialization(main).
|
||||
|
||||
|
||||
main :-
|
||||
unix(argv([H])),
|
||||
generate_town(H).
|
||||
|
||||
|
||||
generate_town(N) :-
|
||||
atomic_concat(['town_', N, '.yap'], FileName),
|
||||
open(FileName, 'write', S),
|
||||
write(S, ':- source.\n'),
|
||||
write(S, ':- style_check(all).\n'),
|
||||
write(S, ':- yap_flag(unknown,error).\n'),
|
||||
write(S, ':- yap_flag(write_strings,on).\n'),
|
||||
write(S, ':- use_module(library(clpbn)).\n'),
|
||||
write(S, ':- set_clpbn_flag(solver, bp).\n'),
|
||||
write(S, ':- [-schema].\n\n'),
|
||||
write(S, 'lives(_joe, nyc).\n'),
|
||||
atom_number(N, N2),
|
||||
generate_people(S, N2, 2),
|
||||
write(S, '\nrun_query(Guilty) :- \n'),
|
||||
write(S, '\tguilty(joe, Guilty),\n'),
|
||||
write(S, '\twitness(nyc, t),\n'),
|
||||
write(S, '\trunall(X, ev(X)).\n\n\n'),
|
||||
write(S, 'runall(G, Wrapper) :-\n'),
|
||||
write(S, '\tfindall(G, Wrapper, L),\n'),
|
||||
write(S, '\texecute_all(L).\n\n\n'),
|
||||
write(S, 'execute_all([]).\n'),
|
||||
write(S, 'execute_all(G.L) :-\n'),
|
||||
write(S, '\tcall(G),\n'),
|
||||
write(S, '\texecute_all(L).\n\n\n'),
|
||||
generate_query(S, N2, 2),
|
||||
close(S).
|
||||
|
||||
|
||||
generate_people(_, N, Counting1) :- !.
|
||||
generate_people(S, N, Counting) :-
|
||||
format(S, 'lives(p~w, nyc).~n', [Counting]),
|
||||
Counting1 is Counting + 1,
|
||||
generate_people(S, N, Counting1).
|
||||
|
||||
|
||||
generate_query(S, N, Counting) :-
|
||||
Counting > N, !.
|
||||
generate_query(S, N, Counting) :- !,
|
||||
format(S, 'ev(descn(p~w, t)).~n', [Counting]),
|
||||
Counting1 is Counting + 1,
|
||||
generate_query(S, N, Counting1).
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user