666 lines
16 KiB
C++
666 lines
16 KiB
C++
#include <cassert>
|
|
|
|
#include <queue>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#include "ParfactorList.h"
|
|
|
|
|
|
namespace Horus {
|
|
|
|
ParfactorList::ParfactorList (const ParfactorList& pfList)
|
|
{
|
|
ParfactorList::const_iterator it = pfList.begin();
|
|
while (it != pfList.end()) {
|
|
addShattered (new Parfactor (**it));
|
|
++ it;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ParfactorList::ParfactorList (const Parfactors& pfs)
|
|
{
|
|
add (pfs);
|
|
}
|
|
|
|
|
|
|
|
ParfactorList::~ParfactorList()
|
|
{
|
|
ParfactorList::const_iterator it = pfList_.begin();
|
|
while (it != pfList_.end()) {
|
|
delete *it;
|
|
++ it;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ParfactorList::add (Parfactor* pf)
|
|
{
|
|
pf->setNewGroups();
|
|
addToShatteredList (pf);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ParfactorList::add (const Parfactors& pfs)
|
|
{
|
|
for (size_t i = 0; i < pfs.size(); i++) {
|
|
pfs[i]->setNewGroups();
|
|
addToShatteredList (pfs[i]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ParfactorList::addShattered (Parfactor* pf)
|
|
{
|
|
assert (isAllShattered());
|
|
pfList_.push_back (pf);
|
|
assert (isAllShattered());
|
|
}
|
|
|
|
|
|
|
|
std::list<Parfactor*>::iterator
|
|
ParfactorList::insertShattered (
|
|
std::list<Parfactor*>::iterator it,
|
|
Parfactor* pf)
|
|
{
|
|
assert (isAllShattered());
|
|
return pfList_.insert (it, pf);
|
|
}
|
|
|
|
|
|
|
|
std::list<Parfactor*>::iterator
|
|
ParfactorList::remove (std::list<Parfactor*>::iterator it)
|
|
{
|
|
return pfList_.erase (it);
|
|
}
|
|
|
|
|
|
|
|
std::list<Parfactor*>::iterator
|
|
ParfactorList::removeAndDelete (std::list<Parfactor*>::iterator it)
|
|
{
|
|
delete *it;
|
|
return pfList_.erase (it);
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ParfactorList::isAllShattered() const
|
|
{
|
|
if (pfList_.size() <= 1) {
|
|
return true;
|
|
}
|
|
Parfactors pfs (pfList_.begin(), pfList_.end());
|
|
for (size_t i = 0; i < pfs.size(); i++) {
|
|
assert (isShattered (pfs[i]));
|
|
}
|
|
for (size_t i = 0; i < pfs.size() - 1; i++) {
|
|
for (size_t j = i + 1; j < pfs.size(); j++) {
|
|
if (isShattered (pfs[i], pfs[j]) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
struct sortByParams {
|
|
bool operator() (const Parfactor* pf1, const Parfactor* pf2) const
|
|
{
|
|
if (pf1->params().size() < pf2->params().size()) {
|
|
return true;
|
|
} else if (pf1->params().size() == pf2->params().size() &&
|
|
pf1->params() < pf2->params()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
void
|
|
ParfactorList::print() const
|
|
{
|
|
Parfactors pfVec (pfList_.begin(), pfList_.end());
|
|
std::sort (pfVec.begin(), pfVec.end(), sortByParams());
|
|
for (size_t i = 0; i < pfVec.size(); i++) {
|
|
pfVec[i]->print();
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ParfactorList&
|
|
ParfactorList::operator= (const ParfactorList& pfList)
|
|
{
|
|
if (this != &pfList) {
|
|
ParfactorList::const_iterator it0 = pfList_.begin();
|
|
while (it0 != pfList_.end()) {
|
|
delete *it0;
|
|
++ it0;
|
|
}
|
|
pfList_.clear();
|
|
ParfactorList::const_iterator it = pfList.begin();
|
|
while (it != pfList.end()) {
|
|
addShattered (new Parfactor (**it));
|
|
++ it;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ParfactorList::isShattered (const Parfactor* g) const
|
|
{
|
|
const ProbFormulas& formulas = g->arguments();
|
|
if (formulas.size() < 2) {
|
|
return true;
|
|
}
|
|
ConstraintTree ct (*g->constr());
|
|
for (size_t i = 0; i < formulas.size() - 1; i++) {
|
|
for (size_t j = i + 1; j < formulas.size(); j++) {
|
|
if (formulas[i].group() == formulas[j].group()) {
|
|
if (identical (
|
|
formulas[i], *(g->constr()),
|
|
formulas[j], *(g->constr())) == false) {
|
|
g->print();
|
|
std::cout << "-> not identical on positions " ;
|
|
std::cout << i << " and " << j << std::endl;
|
|
return false;
|
|
}
|
|
} else {
|
|
if (disjoint (
|
|
formulas[i], *(g->constr()),
|
|
formulas[j], *(g->constr())) == false) {
|
|
g->print();
|
|
std::cout << "-> not disjoint on positions " ;
|
|
std::cout << i << " and " << j << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ParfactorList::isShattered (
|
|
const Parfactor* g1,
|
|
const Parfactor* g2) const
|
|
{
|
|
assert (g1 != g2);
|
|
const ProbFormulas& fms1 = g1->arguments();
|
|
const ProbFormulas& fms2 = g2->arguments();
|
|
|
|
for (size_t i = 0; i < fms1.size(); i++) {
|
|
for (size_t j = 0; j < fms2.size(); j++) {
|
|
if (fms1[i].group() == fms2[j].group()) {
|
|
if (identical (
|
|
fms1[i], *(g1->constr()),
|
|
fms2[j], *(g2->constr())) == false) {
|
|
g1->print();
|
|
std::cout << "^" << std::endl;
|
|
g2->print();
|
|
std::cout << "-> not identical on group " ;
|
|
std::cout << fms1[i].group() << std::endl;
|
|
return false;
|
|
}
|
|
} else {
|
|
if (disjoint (
|
|
fms1[i], *(g1->constr()),
|
|
fms2[j], *(g2->constr())) == false) {
|
|
g1->print();
|
|
std::cout << "^" << std::endl;
|
|
g2->print();
|
|
std::cout << "-> not disjoint on groups " << fms1[i].group();
|
|
std::cout << " and " << fms2[j].group() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ParfactorList::addToShatteredList (Parfactor* g)
|
|
{
|
|
std::queue<Parfactor*> residuals;
|
|
residuals.push (g);
|
|
while (residuals.empty() == false) {
|
|
Parfactor* pf = residuals.front();
|
|
bool pfSplitted = false;
|
|
std::list<Parfactor*>::iterator pfIter;
|
|
pfIter = pfList_.begin();
|
|
while (pfIter != pfList_.end()) {
|
|
std::pair<Parfactors, Parfactors> shattRes;
|
|
shattRes = shatter (*pfIter, pf);
|
|
if (shattRes.first.empty() == false) {
|
|
pfIter = removeAndDelete (pfIter);
|
|
Util::addToQueue (residuals, shattRes.first);
|
|
} else {
|
|
++ pfIter;
|
|
}
|
|
if (shattRes.second.empty() == false) {
|
|
delete pf;
|
|
Util::addToQueue (residuals, shattRes.second);
|
|
pfSplitted = true;
|
|
break;
|
|
}
|
|
}
|
|
residuals.pop();
|
|
if (pfSplitted == false) {
|
|
Parfactors res = shatterAgainstMySelf (pf);
|
|
if (res.empty()) {
|
|
addShattered (pf);
|
|
} else {
|
|
Util::addToQueue (residuals, res);
|
|
}
|
|
}
|
|
}
|
|
assert (isAllShattered());
|
|
}
|
|
|
|
|
|
|
|
Parfactors
|
|
ParfactorList::shatterAgainstMySelf (Parfactor* g)
|
|
{
|
|
Parfactors pfs;
|
|
std::queue<Parfactor*> residuals;
|
|
residuals.push (g);
|
|
bool shattered = true;
|
|
while (residuals.empty() == false) {
|
|
Parfactor* pf = residuals.front();
|
|
Parfactors res = shatterAgainstMySelf2 (pf);
|
|
if (res.empty()) {
|
|
assert (isShattered (pf));
|
|
if (shattered) {
|
|
return { };
|
|
}
|
|
pfs.push_back (pf);
|
|
} else {
|
|
shattered = false;
|
|
for (size_t i = 0; i < res.size(); i++) {
|
|
assert (res[i]->constr()->empty() == false);
|
|
residuals.push (res[i]);
|
|
}
|
|
delete pf;
|
|
}
|
|
residuals.pop();
|
|
}
|
|
return pfs;
|
|
}
|
|
|
|
|
|
|
|
Parfactors
|
|
ParfactorList::shatterAgainstMySelf2 (Parfactor* g)
|
|
{
|
|
// slip a parfactor with overlapping formulas:
|
|
// e.g. {s(X),s(Y)}, with (X,Y) in {(p1,p2),(p1,p3),(p4,p1)}
|
|
const ProbFormulas& formulas = g->arguments();
|
|
for (size_t i = 0; i < formulas.size() - 1; i++) {
|
|
for (size_t j = i + 1; j < formulas.size(); j++) {
|
|
if (formulas[i].sameSkeletonAs (formulas[j])) {
|
|
Parfactors res = shatterAgainstMySelf (g, i, j);
|
|
if (res.empty() == false) {
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Parfactors();
|
|
}
|
|
|
|
|
|
|
|
Parfactors
|
|
ParfactorList::shatterAgainstMySelf (
|
|
Parfactor* g,
|
|
size_t fIdx1,
|
|
size_t fIdx2)
|
|
{
|
|
/*
|
|
Util::printDashedLine();
|
|
std::cout << "-> SHATTERING" << std::endl;
|
|
g->print();
|
|
std::cout << "-> ON: " << g->argument (fIdx1) << "|" ;
|
|
std::cout << g->constr()->tupleSet (g->argument (fIdx1).logVars());
|
|
std::cout << std::endl;
|
|
std::cout << "-> ON: " << g->argument (fIdx2) << "|" ;
|
|
std::cout << g->constr()->tupleSet (g->argument (fIdx2).logVars())
|
|
std::cout << std::endl;
|
|
Util::printDashedLine();
|
|
*/
|
|
ProbFormula& f1 = g->argument (fIdx1);
|
|
ProbFormula& f2 = g->argument (fIdx2);
|
|
if (f1.isAtom()) {
|
|
std::cerr << "Error: a ground occurs twice in the same parfactor." ;
|
|
std::cerr << std::endl;
|
|
std::cerr << std::endl;
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
assert (g->constr()->empty() == false);
|
|
ConstraintTree ctCopy (*g->constr());
|
|
if (f1.group() == f2.group()) {
|
|
assert (identical (f1, *(g->constr()), f2, ctCopy));
|
|
return { };
|
|
}
|
|
|
|
g->constr()->moveToTop (f1.logVars());
|
|
ctCopy.moveToTop (f2.logVars());
|
|
|
|
std::pair<ConstraintTree*,ConstraintTree*> split1 =
|
|
g->constr()->split (f1.logVars(), &ctCopy, f2.logVars());
|
|
ConstraintTree* commCt1 = split1.first;
|
|
ConstraintTree* exclCt1 = split1.second;
|
|
|
|
if (commCt1->empty()) {
|
|
// disjoint
|
|
delete commCt1;
|
|
delete exclCt1;
|
|
return { };
|
|
}
|
|
|
|
PrvGroup newGroup = ProbFormula::getNewGroup();
|
|
Parfactors res1 = shatter (g, fIdx1, commCt1, exclCt1, newGroup);
|
|
if (res1.empty()) {
|
|
res1.push_back (g);
|
|
}
|
|
|
|
Parfactors res;
|
|
ctCopy.moveToTop (f1.logVars());
|
|
for (size_t i = 0; i < res1.size(); i++) {
|
|
res1[i]->constr()->moveToTop (f2.logVars());
|
|
std::pair<ConstraintTree*, ConstraintTree*> split2;
|
|
split2 = res1[i]->constr()->split (f2.logVars(), &ctCopy, f1.logVars());
|
|
ConstraintTree* commCt2 = split2.first;
|
|
ConstraintTree* exclCt2 = split2.second;
|
|
if (commCt2->empty()) {
|
|
if (res1[i] != g) {
|
|
res.push_back (res1[i]);
|
|
}
|
|
delete commCt2;
|
|
delete exclCt2;
|
|
continue;
|
|
}
|
|
newGroup = ProbFormula::getNewGroup();
|
|
Parfactors res2 = shatter (res1[i], fIdx2, commCt2, exclCt2, newGroup);
|
|
if (res2.empty()) {
|
|
if (res1[i] != g) {
|
|
res.push_back (res1[i]);
|
|
}
|
|
} else {
|
|
Util::addToVector (res, res2);
|
|
for (size_t j = 0; j < res2.size(); j++) {
|
|
}
|
|
if (res1[i] != g) {
|
|
delete res1[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (res.empty()) {
|
|
g->argument (fIdx2).setGroup (g->argument (fIdx1).group());
|
|
updateGroups (f2.group(), f1.group());
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
std::pair<Parfactors, Parfactors>
|
|
ParfactorList::shatter (Parfactor* g1, Parfactor* g2)
|
|
{
|
|
ProbFormulas& formulas1 = g1->arguments();
|
|
ProbFormulas& formulas2 = g2->arguments();
|
|
assert (g1 && g2 && g1 != g2);
|
|
for (size_t i = 0; i < formulas1.size(); i++) {
|
|
for (size_t j = 0; j < formulas2.size(); j++) {
|
|
if (formulas1[i].sameSkeletonAs (formulas2[j])) {
|
|
std::pair<Parfactors, Parfactors> res;
|
|
res = shatter (i, g1, j, g2);
|
|
if (res.first.empty() == false ||
|
|
res.second.empty() == false) {
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return make_pair (Parfactors(), Parfactors());
|
|
}
|
|
|
|
|
|
|
|
std::pair<Parfactors, Parfactors>
|
|
ParfactorList::shatter (
|
|
size_t fIdx1, Parfactor* g1,
|
|
size_t fIdx2, Parfactor* g2)
|
|
{
|
|
ProbFormula& f1 = g1->argument (fIdx1);
|
|
ProbFormula& f2 = g2->argument (fIdx2);
|
|
/*
|
|
Util::printDashedLine();
|
|
std::cout << "-> SHATTERING" << std::endl;
|
|
g1->print();
|
|
std::cout << "-> WITH" << std::endl;
|
|
g2->print();
|
|
std::cout << "-> ON: " << f1 << "|" ;
|
|
std::cout << g1->constr()->tupleSet (f1.logVars()) << std::endl;
|
|
std::cout << "-> ON: " << f2 << "|" ;
|
|
std::cout << g2->constr()->tupleSet (f2.logVars()) << std::endl;
|
|
Util::printDashedLine();
|
|
*/
|
|
if (f1.isAtom()) {
|
|
f2.setGroup (f1.group());
|
|
updateGroups (f2.group(), f1.group());
|
|
return { };
|
|
}
|
|
assert (g1->constr()->empty() == false);
|
|
assert (g2->constr()->empty() == false);
|
|
if (f1.group() == f2.group()) {
|
|
assert (identical (f1, *(g1->constr()), f2, *(g2->constr())));
|
|
return { };
|
|
}
|
|
|
|
g1->constr()->moveToTop (f1.logVars());
|
|
g2->constr()->moveToTop (f2.logVars());
|
|
|
|
std::pair<ConstraintTree*,ConstraintTree*> split1 =
|
|
g1->constr()->split (f1.logVars(), g2->constr(), f2.logVars());
|
|
ConstraintTree* commCt1 = split1.first;
|
|
ConstraintTree* exclCt1 = split1.second;
|
|
|
|
if (commCt1->empty()) {
|
|
// disjoint
|
|
delete commCt1;
|
|
delete exclCt1;
|
|
return { };
|
|
}
|
|
|
|
std::pair<ConstraintTree*,ConstraintTree*> split2 =
|
|
g2->constr()->split (f2.logVars(), g1->constr(), f1.logVars());
|
|
ConstraintTree* commCt2 = split2.first;
|
|
ConstraintTree* exclCt2 = split2.second;
|
|
|
|
assert (commCt1->tupleSet (f1.logVars()) ==
|
|
commCt2->tupleSet (f2.logVars()));
|
|
|
|
// std::stringstream ss1; ss1 << "" << count << "_A.dot" ;
|
|
// std::stringstream ss2; ss2 << "" << count << "_B.dot" ;
|
|
// std::stringstream ss3; ss3 << "" << count << "_A_comm.dot" ;
|
|
// std::stringstream ss4; ss4 << "" << count << "_A_excl.dot" ;
|
|
// std::stringstream ss5; ss5 << "" << count << "_B_comm.dot" ;
|
|
// std::stringstream ss6; ss6 << "" << count << "_B_excl.dot" ;
|
|
// g1->constr()->exportToGraphViz (ss1.str().c_str(), true);
|
|
// g2->constr()->exportToGraphViz (ss2.str().c_str(), true);
|
|
// commCt1->exportToGraphViz (ss3.str().c_str(), true);
|
|
// exclCt1->exportToGraphViz (ss4.str().c_str(), true);
|
|
// commCt2->exportToGraphViz (ss5.str().c_str(), true);
|
|
// exclCt2->exportToGraphViz (ss6.str().c_str(), true);
|
|
|
|
if (exclCt1->empty() && exclCt2->empty()) {
|
|
// identical
|
|
f2.setGroup (f1.group());
|
|
updateGroups (f2.group(), f1.group());
|
|
delete commCt1;
|
|
delete exclCt1;
|
|
delete commCt2;
|
|
delete exclCt2;
|
|
return { };
|
|
}
|
|
|
|
PrvGroup group;
|
|
if (exclCt1->empty()) {
|
|
group = f1.group();
|
|
} else if (exclCt2->empty()) {
|
|
group = f2.group();
|
|
} else {
|
|
group = ProbFormula::getNewGroup();
|
|
}
|
|
Parfactors res1 = shatter (g1, fIdx1, commCt1, exclCt1, group);
|
|
Parfactors res2 = shatter (g2, fIdx2, commCt2, exclCt2, group);
|
|
return make_pair (res1, res2);
|
|
}
|
|
|
|
|
|
|
|
Parfactors
|
|
ParfactorList::shatter (
|
|
Parfactor* g,
|
|
size_t fIdx,
|
|
ConstraintTree* commCt,
|
|
ConstraintTree* exclCt,
|
|
PrvGroup commGroup)
|
|
{
|
|
ProbFormula& f = g->argument (fIdx);
|
|
if (exclCt->empty()) {
|
|
delete commCt;
|
|
delete exclCt;
|
|
f.setGroup (commGroup);
|
|
return { };
|
|
}
|
|
|
|
Parfactors result;
|
|
if (f.isCounting()) {
|
|
LogVar X_new1 = g->constr()->logVarSet().back() + 1;
|
|
LogVar X_new2 = g->constr()->logVarSet().back() + 2;
|
|
ConstraintTrees cts = g->constr()->jointCountNormalize (
|
|
commCt, exclCt, f.countedLogVar(), X_new1, X_new2);
|
|
for (size_t i = 0; i < cts.size(); i++) {
|
|
Parfactor* newPf = new Parfactor (g, cts[i]);
|
|
if (cts[i]->nrLogVars() == g->constr()->nrLogVars() + 1) {
|
|
newPf->expand (f.countedLogVar(), X_new1, X_new2);
|
|
assert (g->constr()->getConditionalCount (f.countedLogVar()) ==
|
|
cts[i]->getConditionalCount (X_new1) +
|
|
cts[i]->getConditionalCount (X_new2));
|
|
} else {
|
|
assert (g->constr()->getConditionalCount (f.countedLogVar()) ==
|
|
cts[i]->getConditionalCount (f.countedLogVar()));
|
|
}
|
|
newPf->setNewGroups();
|
|
result.push_back (newPf);
|
|
}
|
|
delete commCt;
|
|
delete exclCt;
|
|
} else {
|
|
Parfactor* newPf = new Parfactor (g, commCt);
|
|
newPf->setNewGroups();
|
|
newPf->argument (fIdx).setGroup (commGroup);
|
|
result.push_back (newPf);
|
|
newPf = new Parfactor (g, exclCt);
|
|
newPf->setNewGroups();
|
|
result.push_back (newPf);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ParfactorList::updateGroups (PrvGroup oldGroup, PrvGroup newGroup)
|
|
{
|
|
for (ParfactorList::iterator it = pfList_.begin();
|
|
it != pfList_.end(); ++it) {
|
|
ProbFormulas& formulas = (*it)->arguments();
|
|
for (size_t i = 0; i < formulas.size(); i++) {
|
|
if (formulas[i].group() == oldGroup) {
|
|
formulas[i].setGroup (newGroup);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ParfactorList::proper (
|
|
const ProbFormula& f1, ConstraintTree ct1,
|
|
const ProbFormula& f2, ConstraintTree ct2) const
|
|
{
|
|
return disjoint (f1, ct1, f2, ct2)
|
|
|| identical (f1, ct1, f2, ct2);
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ParfactorList::identical (
|
|
const ProbFormula& f1, ConstraintTree ct1,
|
|
const ProbFormula& f2, ConstraintTree ct2) const
|
|
{
|
|
if (f1.sameSkeletonAs (f2) == false) {
|
|
return false;
|
|
}
|
|
if (f1.isAtom()) {
|
|
return true;
|
|
}
|
|
TupleSet ts1 = ct1.tupleSet (f1.logVars());
|
|
TupleSet ts2 = ct2.tupleSet (f2.logVars());
|
|
return ts1 == ts2;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ParfactorList::disjoint (
|
|
const ProbFormula& f1, ConstraintTree ct1,
|
|
const ProbFormula& f2, ConstraintTree ct2) const
|
|
{
|
|
if (f1.sameSkeletonAs (f2) == false) {
|
|
return true;
|
|
}
|
|
if (f1.isAtom()) {
|
|
return false;
|
|
}
|
|
TupleSet ts1 = ct1.tupleSet (f1.logVars());
|
|
TupleSet ts2 = ct2.tupleSet (f2.logVars());
|
|
return (ts1 & ts2).empty();
|
|
}
|
|
|
|
} // namespace Horus
|
|
|