#include <config.h>
#include <sampler/Slicer.h>
#include <graph/StochasticNode.h>
#include <graph/NodeError.h>
#include <cmath>
#include <cfloat>
#include <Rmath.h>
using std::vector;
Slicer::Slicer(vector<StochasticNode *> const &nodes, Graph const &graph,
double width, long max)
: Sampler(nodes, graph), _width(width), _burnin(true), _max(max),
_sumdiff(0), _iter(0)
{
}
void Slicer::updateStep()
{
// Test current value
double g0 = logFullConditional();
if (!R_finite(g0)) {
if (g0 > 0) {
return;
}
else {
throw NodeError(nodes()[0], "Current value is inconsistent with data");
}
}
if (g0 == -DBL_MAX) {
throw NodeError(nodes()[0], "Current value is inconsistent with data");
}
// Generate auxiliary variable
double z = g0 - rexp(1);
// Generate random interval of width "_width" about current value
double xold = value();
double L = xold - unif_rand() * _width;
double R = L + _width;
double lower = lowerLimit();
double upper = upperLimit();
// Stepping out
// Randomly set number of steps in left and right directions,
// subject to the limit in the maximal size of the interval
int j = static_cast<int>(unif_rand() * _max);
int k = _max - 1 - j;
if (L < lower) {
L = lower;
}
else {
setValue(L);
while (j-- > 0 && logFullConditional() > z) {
L -= _width;
if (L < lower) {
L = lower;
break;
}
setValue(L);
}
}
if (R > upper) {
R = upper;
}
else {
setValue(R);
while (k-- > 0 && logFullConditional() > z) {
R += _width;
if (R > upper) {
R = upper;
break;
}
setValue(R);
}
}
// Keep sampling from the interval until acceptance (the loop is
// guaranteed to terminate).
double xnew;
for(;;) {
xnew = L + unif_rand() * (R - L);
setValue(xnew);
double g = logFullConditional();
if (g >= z - DBL_EPSILON) {
// Accept point
break;
}
else {
// shrink the interval
if (xnew < xold) {
L = xnew;
}
else {
R = xnew;
}
}
}
if (_burnin) {
_sumdiff += _iter * fabs(xnew - xold);
++_iter;
}
}
void Slicer::updateDouble()
{
// Test current value
double g0 = logFullConditional();
if (g0 < 0) {
if (g0 == -DBL_MAX || !R_finite(g0)) {
throw NodeError(nodes()[0],
"Current value is inconsistent with data");
}
}
else if (g0 == DBL_MAX || !R_finite(g0)) {
return;
}
// Generate auxiliary variable
double z = g0 - rexp(1);
// Generate random interval of width "_width" about current value
double xold = value();
double L = xold - unif_rand() * _width;
double R = L + _width;
double lower = lowerLimit();
double upper = upperLimit();
// Doubling
bool left_ok = false, right_ok = false;
for (int i = 0; i < _max; ++i) {
if (runif(0,1) < 0.5) {
if (L >= lower) {
L = 2*L - R;
setValue(L);
left_ok = logFullConditional() < z;
}
else {
left_ok = true;
}
}
else {
if (R <= upper) {
R = 2*R - L;
setValue(R);
right_ok = logFullConditional() < z;
}
else {
right_ok = true;
}
}
if (left_ok && right_ok)
break;
}
// Keep sampling from the interval until acceptance (the loop is
// guaranteed to terminate).
double Lbar = L, Rbar = R;
double xnew;
for(;;) {
xnew = Lbar + unif_rand() * (Rbar - Lbar);
setValue(xnew);
double g = logFullConditional();
if (g >= z && accept(xold, xnew, z, L, R)) {
// The accept function will alter the current value. So we must reset it.
setValue(xnew);
return;
}
// shrink the interval
if (xnew <= xold) {
Lbar = xnew;
}
else {
Rbar = xnew;
}
}
if (_burnin) {
_sumdiff += _iter * fabs(xnew - xold);
++_iter;
}
}
bool Slicer::accept(double xold, double xnew, double z,
double L, double R)
{
//Acceptance step for doubling update method
bool d = false;
while ((R - L) > 1.1 * _width) {
double M = (L + R)/2;
if ((xold < M && xnew >= M) || (xold >= M && xnew < M))
d = true;
if (xnew < M) {
R = M;
}
else {
L = M;
}
if (d) {
setValue(R); bool right_ok = logFullConditional() < z;
setValue(L); bool left_ok = logFullConditional() < z;
if (left_ok && right_ok)
return false;
}
}
return true;
}
void Slicer::burninOff()
{
// Reset slice width
if (_iter > 1) {
_width = 2 * _sumdiff / _iter / (_iter - 1);
}
}
syntax highlighted by Code2HTML, v. 0.9.1