#include <config.h>
#include <sampler/ConjugateNormal.h>
#include <sampler/ConjugateSampler.h>
#include <graph/AggNode.h>
#include <graph/MixtureNode.h>
#include <graph/LogicalNode.h>
#include <graph/StochasticNode.h>
#include <set>
#include <stdexcept>
#include <vector>
#include <cmath>
#include <Rmath.h>
using std::vector;
using std::set;
using std::sqrt;
using std::invalid_argument;
ConjugateNormal::ConjugateNormal(StochasticNode *snode, Graph const &graph)
: ConjugateSampler(snode, graph)
{
if (!canSample(snode, graph)) {
throw invalid_argument("Can't construct ConjugateNormal sampler");
}
}
ConjugateNormal::~ConjugateNormal()
{
}
bool ConjugateNormal::canSample(StochasticNode *snode, Graph const &graph)
{
/*
Normal Conjugate samplers are more complex than other samplers
because conjugacy is preserved by linear functions, and we want
to be able to exploit this.
1) Stochastic children of snode must be normal, and depend on
snode only via the mean parameter
2) The mean parameter must be a linear function of snode. This
means that any path from snode to the stochastic child must
consist of logical nodes representing functions +,-,*,/ and
the identity function.
*/
if (getDist(snode) != NORM)
return false;
vector<StochasticNode const*> stoch_nodes;
vector<Node*> dtrm_nodes;
classifyChildren(vector<StochasticNode*>(1,snode),
graph, stoch_nodes, dtrm_nodes);
/*
Create a set of nodes containing snode and its deterministic
descendants for the checks below.
*/
set<SArray const *> paramset;
paramset.insert(&snode->data);
for (unsigned int j = 0; j < dtrm_nodes.size(); ++j) {
paramset.insert(&dtrm_nodes[j]->data);
}
// Check stochastic children
for (unsigned int i = 0; i < stoch_nodes.size(); ++i) {
if (getDist(stoch_nodes[i]) != NORM) {
return false; //Not normal
}
if (stoch_nodes[i]->isBounded()) {
return false;
}
vector<SArray const *> const ¶m = stoch_nodes[i]->parameters();
if (paramset.count(param[1])) {
return false; //Precision depends on snode
}
}
// Check deterministic descendants
for (unsigned int j = 0; j < dtrm_nodes.size(); ++j) {
if (isLogical(dtrm_nodes[j])) {
LogicalNode const *lnode = asLogical(dtrm_nodes[j]);
vector<SArray const *> const ¶m = lnode->parameters();
unsigned int nfactor = 0;
switch(getOp(lnode)) {
case ADD: case SUBTRACT: case NEG:
break;
case DIVIDE:
if (paramset.count(param[1]))
return false; //reciprocal term
break;
case MULTIPLY:
for (unsigned int k = 0; k < param.size(); ++k) {
nfactor += paramset.count(param[k]);
}
if (nfactor != 1)
return false; //quadratic or higher term
break;
default:
return false;
}
}
else if (isMixture(dtrm_nodes[j])) {
// Check that indices do not depend on snode
vector<Node *> const &index = asMixture(dtrm_nodes[j])->index();
for (unsigned int i = 0; i < index.size(); ++i) {
if (paramset.count(&index[i]->data))
return false;
}
}
else {
return false;
}
}
return true; //We made it!
}
void ConjugateNormal::update()
{
vector<StochasticNode const*> const &stoch_children = stochasticChildren();
unsigned int nchildren = stoch_children.size();
/* For convenience in the following computations, we shift the
origin to xold, the previous value of the node */
const double xold = *node()->data.value();
const double priormean = *node()->parameters()[0]->value() - xold;
const double priorprec = *node()->parameters()[1]->value();
double A = priormean * priorprec; //Weighted sum of means
double B = priorprec; //Sum of weights
if (deterministicChildren().empty()) {
/* Stochastic children are direct children of snode */
for (unsigned int i = 0; i < nchildren; ++i) {
double Y = *stoch_children[i]->data.value();
double tau = *stoch_children[i]->parameters()[1]->value();
A += (Y - xold) * tau;
B += tau;
}
}
else {
/* The prior mean of any stochastic child depends on the value of
snode (x say) via a linear function. We can write this as y =
alpha + beta * (x - x.cur), where x.cur is the currrent value
of snode. In order to calculate the value of the coefficients
alpha and beta, we evaluate the prior mean twice: once at x.cur
and once at x.cur + 1.
*/
double *alpha = new double[nchildren];
for (unsigned int i = 0; i < nchildren; ++i) {
alpha[i] = *stoch_children[i]->parameters()[0]->value();
}
double xnew = xold + 1;
setValue(&xnew, 1);
for (unsigned int i = 0; i < nchildren; ++i) {
double Y = *stoch_children[i]->data.value();
double tau = *stoch_children[i]->parameters()[1]->value();
double beta = *(stoch_children[i]->parameters()[0]->value()) - alpha[i];
A += (Y - alpha[i]) * tau * beta;
B += tau * beta * beta;
}
// Tidy up
delete [] alpha;
}
// Draw the sample
double postmean = xold + A/B;
double postsd = sqrt(1/B);
double xnew;
if (node()->isBounded()) {
SArray const *lb = node()->lowerBound();
SArray const *ub = node()->upperBound();
double plower = lb ? pnorm(*lb->value(), postmean, postsd, 1, 0) : 0;
double pupper = ub ? pnorm(*ub->value(), postmean, postsd, 1, 0) : 1;
double p = runif(plower, pupper);
xnew = qnorm(p, postmean, postsd, 1, 0);
}
else {
xnew = rnorm(postmean, postsd);
}
setValue(&xnew, 1);
}
syntax highlighted by Code2HTML, v. 0.9.1