Calling optimize in Rcpp not producing expected result - r

I've been trying to port an optimization routine from R to Rcpp. The Rcpp version isn't producing what I expect and I'm stumped as to what the problem might be. For context, the problem is to compute the inverse cdf of a "gamma(shape, scale) + normal(0, sigma^2)" distribution. In particular, given a value c, find x such that P(W' <= x) = c, where W' has the distribution described. Notice that P(W' <= x) = \int_W P(W' <= x | W) * f_W(W) dW, where W ~ gamma(shape, scale) and W' | W ~ normal(W, sigma^2).
I'm using RcppNumerical (https://stackoverflow.com/a/39449199/2875572) for integration (this seems to be working fine, as the test results indicate). It's the call to optimize that is producing the mysterious results.
R test functions:
IntegrateRTest <- function(x, SIGMA, SHAPE, SCALE) {
sapply(x,
function(x) {
integrate(f = function(W) {
# P(W' <= x | W) * f_W(W)
pnorm(x, mean = W, sd = SIGMA) * dgamma(W, shape = SHAPE, scale = SCALE)
}, 0, Inf)$value
})
}
OptimizeRTest <- function(c, SIGMA, SHAPE, SCALE) {
optimize(f = function(x) {
rhs <- integrate(f = function(W) {
# P(W' <= x | W) * f_W(W)
pnorm(x, mean = W, sd = SIGMA) *
dgamma(W, shape = SHAPE, scale = SCALE)
}, 0, Inf)$value
(c - rhs)^2
},
lower = -10,
upper = 10)
}
The Rcpp script:
#include <Rcpp.h>
#include <RcppNumerical.h>
// [[Rcpp::depends(RcppEigen)]]
// [[Rcpp::depends(RcppNumerical)]]
using namespace Rcpp;
// utility function for vectorized exponentiation
NumericVector vecpow(const NumericVector base, const NumericVector exp) {
NumericVector out(base.size());
std::transform(base.begin(), base.end(),
exp.begin(), out.begin(), static_cast<double(*)(double, double)>(::pow));
return out;
}
class Mintegrand: public Numer::Func {
private:
const double x;
const double SIGMA;
const double SHAPE;
const double SCALE;
public:
Mintegrand(double x_, double sigma_, double shape_, double scale_) : x(x_), SIGMA(sigma_), SHAPE(shape_), SCALE(scale_) {}
double operator()(const double& W) const
{
// P(W' <= x | W) * f_W(W)
return R::pnorm5(x, W, SIGMA, true, false) * R::dgamma(W, SHAPE, SCALE, false);
}
};
NumericVector objective(NumericVector x,
double c,
double SIGMA,
double SHAPE,
double SCALE) {
// for loop is to "vectorize" this function (required by stats::optimize)
NumericVector rhs(x.length());
for (int i = 0; i < x.length(); ++i) {
Mintegrand f(x[i], SIGMA, SHAPE, SCALE);
double err_est;
int err_code;
// compute P(W' <= x) = \int_W P(W' <= x | W) * f_W(W) dW
rhs[i] = Numer::integrate(f, 0.0, R_PosInf, err_est, err_code);
}
return vecpow(c - rhs, 2.0);
}
// [[Rcpp::export]]
NumericVector IntegrateTest(NumericVector x,
double SIGMA,
double SHAPE,
double SCALE) {
NumericVector rhs(x.length());
for (int i = 0; i < x.length(); ++i) {
Mintegrand f(x[i], SIGMA, SHAPE, SCALE);
double err_est;
int err_code;
// compute P(W' <= x) = \int_W P(W' <= x | W) * f_W(W) dW
rhs[i] = Numer::integrate(f, 0.0, R_PosInf, err_est, err_code);
}
return rhs;
}
// [[Rcpp::export]]
List OptimizeTest(double c,
double SIGMA,
double SHAPE,
double SCALE) {
Environment stats("package:stats");
Function optimize = stats["optimize"];
return optimize(_["f"] = InternalFunction(&objective),
_["c"] = c,
_["SIGMA"] = SIGMA,
_["SHAPE"] = SHAPE,
_["SCALE"] = SCALE,
_["lower"] = -10.0,
_["upper"] = 10.0);
}
Test results:
all.equal(IntegrateTest(seq(0, 1, .01), SIGMA = .4, SHAPE = .9, SCALE = .5),
IntegrateRTest(seq(0, 1, .01), SIGMA = .4, SHAPE = .9, SCALE = .5))
# TRUE
OptimizeTest(.9, SIGMA = .4, SHAPE = 9, SCALE = .5)
OptimizeRTest(.9, SIGMA = .4, SHAPE = 9, SCALE = .5)
# gives very different results

Related

Rcpp (function with default null parameter): Not compatible with requested type: [type=NULL; target=double]

My Rcpp code is shown here, I have used sourceCpp to compile the cpp file. No error is reported, just some warnings there. When I tried to implement the function softImpute using softImpute(m1, NULL, mr1, mr2, 0, -1), the reported error is Not compatible with requested type: [type=NULL; target=double].
Note: m1 is a numeric matrix, mr1 and mr2 are two logical matrix. For example, we can generate m1, mr1, mr2 as below:
m1 <- matrix(rnorm(30),5,6)
mr1 <- matrix(runif(30) > 0.5,5,6)
mr2 <- !mr1
Do you have any idea to address this error? Thank you in advance!
Rcpp code:
#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
Rcpp::List dcSVD(arma::mat X) {
arma::mat u, v;
arma::vec d;
arma::svd(u, d, v, X, "dc");
return Rcpp::List::create(Rcpp::Named("u") = u,
Rcpp::Named("d") = d,
Rcpp::Named("v") = v);
}
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
Rcpp::List svd_st(arma::mat X, double lambda){
Rcpp::List mysvd = dcSVD(X);
arma::mat U = mysvd["u"];
arma::mat V = mysvd["v"];
arma::mat VT = V.t();
arma::vec d = mysvd["d"];
arma::mat D = arma::diagmat(d);
arma::uvec index_list = arma::find(d >= lambda);
arma::vec w;
arma::mat W;
arma::mat L;
if(index_list.n_elem > 1){
w = d.elem(index_list) - lambda;
W = arma::diagmat(w);
arma::mat U1 = U.cols(index_list);
arma::mat VT1 = VT.rows(index_list);
L = U1 * W * VT1;
}else if(index_list.n_elem == 1){
w = d.elem(index_list) - lambda;
W = arma::diagmat(w);
L = U.cols(index_list) * W * VT.rows(index_list);
}else if(index_list.n_elem == 0){
W = arma::zeros(1,1);
L = arma::zeros(X.n_rows, X.n_cols);
}
return Rcpp::List::create(Rcpp::Named("L") = L, Rcpp::Named("W") = W);
}
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
Rcpp::List soft(arma::mat X, Rcpp::Nullable<Rcpp::NumericMatrix> Z_ = R_NilValue, Rcpp::Nullable<Rcpp::LogicalMatrix> Ome_ = R_NilValue,
Rcpp::Nullable<Rcpp::LogicalMatrix> Ome1_ = R_NilValue, Rcpp::Nullable<Rcpp::LogicalMatrix> Ome2_ = R_NilValue,
Rcpp::Nullable<Rcpp::NumericVector> alpha0_ = R_NilValue, Rcpp::Nullable<Rcpp::NumericVector> maxRank_ = R_NilValue){
Rcpp::NumericMatrix ZH = R_NilValue;
double alpha = 0;
int maxRank = -1;
arma::mat Z;
if (Ome_.isNotNull() && Ome1_.isNotNull() && Ome2_.isNotNull()){
Rcpp::LogicalMatrix Ome(Ome_);
arma::umat Omega = Rcpp::as<arma::umat>(Ome);
Rcpp::LogicalMatrix Ome1(Ome1_);
arma::umat Omega1 = Rcpp::as<arma::umat>(Ome1);
Rcpp::LogicalMatrix Ome2(Ome2_);
arma::umat Omega2 = Rcpp::as<arma::umat>(Ome2);
arma::mat X_0 = X % Omega;
if (!Z_.isNotNull()){
Rcpp::NumericMatrix ZH = Rcpp::wrap(X_0);
}else{
Rcpp::NumericMatrix ZH = Rcpp::as<Rcpp::NumericMatrix>(Z_);
}
if (!alpha0_.isNotNull()){
Rcpp::List my_svd = dcSVD(X_0);
arma::vec d = my_svd["d"];
double alpha = arma::as_scalar(d(1));
}else{
Rcpp::NumericVector aa(alpha0_);
double alpha = arma::as_scalar(aa(0));
}
if (!maxRank_.isNotNull()){
int maxRank = -1;
}else{
Rcpp::NumericVector bb(maxRank_);
int maxRank = arma::as_scalar(bb(0));
}
Z = Rcpp::as<arma::mat>(ZH);
}
return Rcpp::List::create(Rcpp::Named("Z") = Z,
Rcpp::Named("alpha") = alpha,
Rcpp::Named("maxRank") = maxRank);
}

How can I optimize this Sierpinski carpet i made using recursion?

I followed the Shiffmans tutorial about recursion to end up with this:
As you can see its not perfect and I think the code can be optimized. How do I get rid of the thick lines that should´t be there? And if you get an idea of how I could optimize this code tell me please!
This was made with processing 3.3.6 and the code is the following:
void setup() {
size(800, 800);
}
void draw() {
background(255);
fill(0);
noStroke();
rectMode(CENTER);
Serpinski(width/2, height/2, width/3);
}
void Serpinski(int x, int y, int d) {
rect(x, y, d, d);
if (d > 1) {
Serpinski(int(x+ d), y, d*1/3);
Serpinski(int(x- d), y, d*1/3);
Serpinski(x, int(y+ d), d*1/3);
Serpinski(x, int(y- d), d*1/3);
Serpinski(int(x+ d), int(y+ d), d*1/3);
Serpinski(int(x- d), int(y- d), d*1/3);
Serpinski(int(x+ d), int(y- d), d*1/3);
Serpinski(int(x- d), int(y+ d), d*1/3);
}
}
As mentioned in the comments, changing the Sierpinski method so it deals with float values instead of int will help.
void setup() {
size(800, 800);
}
void draw() {
background(255);
fill(0);
noStroke();
rectMode(CENTER);
Serpinski(width/2, height/2, width/3);
}
void Serpinski(float x, float y, float d) {
rect(x, y, d, d);
if (d > 1) {
Serpinski( x+ d, y, d/3);
Serpinski( x- d, y, d/3);
Serpinski( x, y+ d, d/3);
Serpinski( x, y- d, d/3);
Serpinski( x+ d, y+ d, d/3);
Serpinski( x- d, y- d, d/3);
Serpinski( x+ d, y- d, d/3);
Serpinski( x- d, y+ d, d/3);
}
}
However, due to the way the pixel information is handled, you will find that the graphic representation is still not "exact" when you get down to the smaller rectangles. One way to achieve that is to change the size of the sketch to one that is a power of 3:
size(729, 729);
As for optimization, you could call the Sierpinski method in setup(), that way it only gets computed once rather than every time draw() is called.
Like this?
void setup() {
size(729, 729);
fill(0);
background(255);
centerRectangle(0, 0, width);
rectangles(width/3, height/3, width/3);
}
void centerRectangle(int x, int y, int s) {
float delta = s/3;
noStroke();
rect(x+delta, y+delta, delta, delta);
}
void rectangles(int x, int y, int s) {
if (s < 1) return;
int xc = x-s;
int yc = y-s;
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
if (!(row == 1 && col == 1)) {
int xx = xc+row*s;
int yy = yc+col*s;
centerRectangle(xx, yy, s);
rectangles(xx+s/3, yy+s/3, s/3);
}
}
}
}

Collision detection between two objects

The collision is not working
According to that post Collision detection between 2 "linearly" moving objects in WGS84,
I have the following data
EDIT:
I have updated the data for a collision that should occur in 10 seconds.
m_sPosAV = {North=48.276111971715515 East=17.921031349301817 Altitude=6000.0000000000000 }
Poi_Position = {North=48.806113707277042 East=17.977161602106488 Altitude=5656.0000000000000 }
velocity.x = -189.80000000000001 // m/s
velocity.y = -39.800000000000004 // m/s
velocity.z = 9 // m/s
m_sVelAV = {x=1.0000000000000000 y=1.0000000000000000 z=0.00000000000000000 } // m/s
void WGS84toXYZ(double &x, double &y, double &z, double lon, double lat, double alt)
{
const double _earth_a = 6378141.4; // [m] equator radius
const double _earth_b = 6356755.0; // [m] polar radius
double a, b, h, l, c, s;
a = lon;
b = lat;
h = alt;
c = cos(b);
s = sin(b);
h = h + sqrt((_earth_a*_earth_a*c*c) + (_earth_b*_earth_b*s*s));
z = h*s;
l = h*c;
x = l*cos(a);
y = l*sin(a);
}
bool CPoiFilterCollision::collisionDetection(const CPoiItem& poi)
{
const double _min_t = 10; // min_time
const double _max_d = 500; // max_distance
const double _max_t = 0.001; // max_time
double dt;
double d0, d1;
double xAv, yAv, zAv;
double xPoi, yPoi, zPoi;
double x, y, z;
double Ux, Uy, Uz; // [m]
double Vx, Vy, Vz; // [m]
double Wx, Wy, Wz; // [m]
double da = 1.567e-7; // [rad] angular step ~ 1.0 m in lon direction
double dl = 1.0;
const double deg = pi / 180.0;
// [m] altitide step 1.0 m
WGS84toXYZ(xAv, yAv, zAv, m_sPosAV.GetLongitude(), m_sPosAV.GetLatitude(), m_sPosAV.GetAltitude()); // actual position
WGS84toXYZ(xPoi, yPoi, zPoi, poi.Position().GetLongitude(), poi.Position().GetLatitude(), poi.Position().GetAltitude()); // actual position
WGS84toXYZ(Ux, Uy, Uz, m_sPosAV.GetLongitude() + da, m_sPosAV.GetLatitude(), m_sPosAV.GetAltitude()); // lon direction Nort
WGS84toXYZ(Vx, Vy, Vz, m_sPosAV.GetLongitude(), m_sPosAV.GetLatitude() + da, m_sPosAV.GetAltitude()); // lat direction East
WGS84toXYZ(Wx, Wy, Wz, m_sPosAV.GetLongitude(), m_sPosAV.GetLatitude(), m_sPosAV.GetAltitude() + dl); // alt direction High/Up
Ux -= xAv; Uy -= yAv; Uz -= zAv;
Vx -= xAv; Vy -= yAv; Vz -= zAv;
Wx -= xAv; Wy -= yAv; Wz -= zAv;
normalize(Ux, Uy, Uz);
normalize(Vx, Vy, Vz);
normalize(Wx, Wy, Wz);
double vx = m_sVelAV.x*Ux + m_sVelAV.y*Vx + m_sVelAV.z*Wx;
double vy = m_sVelAV.x*Uy + m_sVelAV.y*Vy + m_sVelAV.z*Wy;
double vz = m_sVelAV.x*Uz + m_sVelAV.y*Vz + m_sVelAV.z*Wz;
const QList<QVariant> velocity = poi.Property(QLatin1String("VELOCITY")).toList();
if (velocity.size() == 3)
{
dt = _max_t;
x = xAv - xPoi;
y = yAv - yPoi;
z = zAv - zPoi;
d0 = sqrt((x*x) + (y*y) + (z*z));
x = xAv - xPoi + (vx - velocity.at(0).toDouble())*dt;
y = yAv - yPoi + (vy - velocity.at(1).toDouble())*dt;
z = zAv - zPoi + (vz - velocity.at(2).toDouble())*dt;
d1 = sqrt((x*x) + (y*y) + (z*z));
if (d0 <= _max_d)
{
return true;
}
if (d0 <= d1)
{
return false;
}
double t = (_max_d - d0)*dt / (d1 - d0);
if (t < _min_t)
{
qDebug() << "Collision at time " << t;
return true;
}
}
return false;
}

Are there any description about using "fit image" function (DM FitTools) in script?

I would like to integrate the functions provided by the "Fit Image" plattlet (especially 2D polynomial fit to a given input image and subtract it) in DM to script to automate the whole flow of image processing.
However, I could not find any description of how to do it.
It's appreciated if anyone know it, or have certain documentaion on this.
The script functionality for fitting is not yet officially supported/documented.
However, you can use the following examples to see how the commands work:
Commands
Boolean FitGaussian(Image* data, Image* errors, double* N, double* mu, double* sigma, double* chiSqr, double conv_cond)
ImageRef PlotGaussian(Image* data, double N, double mu, double sigma)
Boolean FitLorentzian(Image* data, Image* errors, double* I, double* x0, double* gamma, double* chiSqr, double conv_cond)
ImageRef PlotLorentzian(Image* data, double I, double x0, double gamma)
Boolean FitPolynomial(Image* data, Image* errors, Image* pars, Image* parsToFit, double* chiSqr, double conv_cond)
ImageRef PlotPolynomial(Image* data, Image* pars)
Boolean FitGaussian2D(Image* data, Image* errors, Image* pars, Image* parsToFit, double* chiSqr, double conv_cond)
ImageRef PlotGaussian2D(Image* data, Image* pars)
Boolean FitPolynomial2D(Image* data, Image* errors, Image* pars, Image* parsToFit, double* chiSqr, double conv_cond)
ImageRef PlotPolynomial2D(Image* data, Image* pars)
Boolean FitFormula(dm_string formulaStr, Image* data, Image* errors, Image* pars, Image* parsToFit, double* chiSqr, double conv_cond)
ImageRef PlotFormula(dm_string formulaStr, Image* data, Image* pars)
example 1, 1D formula fit
// create the input image:
Image input := NewImage("formula test", 2, 100)
input = 500.5 - icol*11.1 + icol*icol*0.11
// add some random noise:
input += (random()-0.5)*sqrt(abs(input))
// create image with error data (not required)
Image errors := input.ImageClone()
errors = tert(input > 1, sqrt(input), 1)
// setup fit:
Image pars := NewImage("pars", 2, 3)
Image parsToFit := NewImage("pars to fit", 2, 3)
pars = 10; // starting values
parsToFit = 1;
Number chiSqr = 1e6
Number conv_cond = 0.00001
Result("\n starting pars = {")
Number xSize = pars.ImageGetDimensionSize(0)
Number i = 0
for (i = 0; i < xSize; i++)
{
Result(GetPixel(pars, i, 0))
if (i < (xSize-1)) Result(", ")
}
Result("}")
// fit:
String formulaStr = "p0 + p1*x + p2*x**2"
Number ok = FitFormula(formulaStr, input, errors, pars, parsToFit, chiSqr, conv_cond)
Result("\n results pars = {")
for (i = 0; i < xSize; i++)
{
Result(GetPixel(pars, i, 0))
if (i < (xSize-1)) Result(", ")
}
Result("}")
Result(", chiSqr ="+ chiSqr)
// plot results of fit:
Image plot := PlotFormula(formulaStr, input, pars)
// compare the plot and original data:
Image compare := NewImage("Compare Fit", 2, 100, 3)
compare[icol, 0] = input // original data
compare[icol, 1] = plot // fit function
compare[icol, 2] = input - plot // residuals
ImageDocument linePlotDoc = CreateImageDocument("Test Fitting")
ImageDisplay linePlotDsp = linePlotDoc.ImageDocumentAddImageDisplay(compare, 3)
linePlotDoc.ImageDocumentShow()
example 2, 2D Gaussian fit
// $BACKGROUND$
// create data image
Image img := NewImage("Gaussian2D", 2, 200, 200)
Image true_pars := NewImage("Gaussian2D Pars", 2, 6)
// true parameters
true_pars[0,0] = 1000 // height of gaussian
true_pars[1,0] = 60 // center in x
true_pars[2,0] = 50 // width in x
true_pars[3,0] = 40 // center in y
true_pars[4,0] = 80 // width in y
true_pars[5,0] = 0.7 // rotation in radians
Image data := PlotGaussian2D(img, true_pars)
data += (gaussianrandom())*sqrt(abs(data)) //add noise
ShowImage(data)
Image errors := data.ImageClone()
errors = tert(abs(data) > 1, sqrt(abs(data)), 1)
// starting parameters of fit
Image pars := NewImage("Gaussian2D Pars", 2, 6)
pars = 100
pars[0,0] = max(data) // estimate normalization from peak of data
pars[5,0] = 0 // 100 radians doesn't make sense
Image parsToFit := NewImage("tmp", 2, 6)
parsToFit = 1
Number chiSqr = 1e6
Number conv_cond = 0.00001
Result("\n starting pars = {")
Number xSize = pars.ImageGetDimensionSize(0)
Number i = 0
for (i = 0; i < xSize; i++)
{
Result(GetPixel(pars, i, 0))
if (i < (xSize-1)) Result(", ")
}
Result("}")
// fit
Number ok = FitGaussian2D(data, errors, pars, parsToFit, chiSqr, conv_cond)
if (chiSqr > 2)
ok = FitGaussian2D(data, errors, pars, parsToFit, chiSqr, conv_cond)
Image parDif = 100.0*(pars - true_pars)/true_pars
Result("\n results pars (% dif from true)= {")
for (i = 0; i < xSize; i++)
{
Result(GetPixel(parDif, i, 0))
if (i < (xSize-1)) Result(", ")
}
Result("}")
Result(", chiSqr ="+ chiSqr)
// show residuals
Image residuals := PlotGaussian2D(img, pars)
residuals = data - residuals
ShowImage(residuals)
example 3, 2D Polynomial fit
// $BACKGROUND$
// The number of parameters are defined by the order,
// nPar = (order+1)*(order+2)/2. For example, a
// 3rd order poly will have (3+1)*(3+2)/2 = 10 parameters:
//
// x^0 x^1 x^2 x^3
// --------------------------------
// y^0 | p0 p1 p2 p3
// y^1 | p4 p5 p6 -- (the -- terms are higher)
// y^2 | p7 p8 -- -- (order so are ignored )
// y^3 | p9 -- -- --
//
//
// i.e. f(x,y|p) = p0 + p1*x + p2*x^2 + p3*x^3 + p4*y + p5*x*y
// + p6*x^2*y + p7*y^2 + p8*x*y^2 + p9*y^3
Number xImgSize = 512
Number yImgSize = 512 // create data image
Image img := NewImage("Poly2D", 2, xImgSize, yImgSize)
Image pars_true := NewImage("Poly2D Pars", 2, 3, 3)
// true parameters
pars_true[0,0] = 100
pars_true[1,0] = 60
pars_true[2,0] = -0.05
pars_true[0,1] = 70
pars_true[1,1] = 0.01
pars_true[0,2] = -0.1
Image data := PlotPolynomial2D(img, pars_true)
data += (gaussianrandom())*sqrt(abs(data)) //add noise
ShowImage(data)
Image errors := data.ImageClone()
errors = tert(abs(data) > 1, sqrt(abs(data)), 1)
// starting parameters of fit
Image pars := NewImage("Poly2D Pars", 2, 3, 3)
pars = 10
Image parsToFit := NewImage("tmp", 2, 3, 3)
parsToFit = 1
Number chiSqr = 1e6
Number conv_cond = 0.00001
Result("\n starting pars = {")
Number xSize = pars.ImageGetDimensionSize(0)
Number ySize = pars.ImageGetDimensionSize(1)
Number i, j
for (j = 0; j < ySize; j++)
{
if (j > 0) Result(", ")
Result("{")
for (i = 0; i < xSize; i++)
{
if (i > 0) Result(", ")
if ((i+j) > 2)
Result("-")
else
Result(GetPixel(pars, i, j))
}
Result("}")
}
Result("}")
// fit
Number startTicks = GetHighResTickCount()
Number ok = FitPolynomial2D(data, errors, pars, parsToFit, chiSqr, conv_cond)
Number endTicks = GetHighResTickCount()
Number secs = CalcHighResSecondsBetween(startTicks, endTicks)
Image parDif = 100*(pars - pars_true)/pars_true
Result("\n results pars (% diff from true) = {")
for (j = 0; j < ySize; j++)
{
if (j > 0) Result(", ")
Result("{")
for (i = 0; i < xSize; i++)
{
if (i > 0) Result(", ")
if ((i+j) > 2)
Result("-")
else
Result(GetPixel(parDif, i, j))
}
Result("}")
}
Result("}")
Result(", chiSqr = "+ chiSqr)
Result(", Fit Time (s) = " + secs)
// show residuals
Image residuals := PlotPolynomial2D(img, pars)
residuals = data - residuals
ShowImage(residuals)

Modified Bessel functions of order (n)

I'm using Incanter and Parallel Colt for a project, and need to have a function that returns the modified Bessel function of an order n for a value v.
The Colt library has two methods for order 0 and order 1, but beyond that, only a method that return the Bessel function of order n for a value v (cern.jet.math.tdouble.Bessel/jn).
I'm trying to build the R function, dskellam(x,lambda1, lambda2) for the Skellam distribution, in Clojure/Java
Is there something I can do with the return value of the Bessel method to convert it to a modified Bessel?
No, the difference isn't a simple transformation, as these links make clear:
http://mathworld.wolfram.com/BesselFunctionoftheFirstKind.html
http://mathworld.wolfram.com/ModifiedBesselFunctionoftheFirstKind.html
I'd have a look at "Numerical Recipes" or Abramowitz & Stegun. It wouldn't be hard to implement your own in a short period of time.
Here's a Java implementation of the modified Bessel functions:
package math;
/**
* Functions that are not part of standard libraries
* User: Michael
* Date: 1/9/12
* Time: 9:22 PM
*/
public class Functions {
public static final double ACC = 4.0;
public static final double BIGNO = 1.0e10;
public static final double BIGNI = 1.0e-10;
public static void main(String[] args) {
double xmin = ((args.length > 0) ? Double.valueOf(args[0]) : 0.0);
double xmax = ((args.length > 1) ? Double.valueOf(args[1]) : 4.0);
double dx = ((args.length > 2) ? Double.valueOf(args[2]) : 0.1);
System.out.printf("%10s %10s %10s %10s\n", "x", "bessi0(x)", "bessi1(x)", "bessi2(x)");
for (double x = xmin; x < xmax; x += dx) {
System.out.printf("%10.6f %10.6f %10.6f %10.6f\n", x, bessi0(x), bessi1(x), bessi(2, x));
}
}
public static final double bessi0(double x) {
double answer;
double ax = Math.abs(x);
if (ax < 3.75) { // polynomial fit
double y = x / 3.75;
y *= y;
answer = 1.0 + y * (3.5156229 + y * (3.0899424 + y * (1.2067492 + y * (0.2659732 + y * (0.360768e-1 + y * 0.45813e-2)))));
} else {
double y = 3.75 / ax;
answer = 0.39894228 + y * (0.1328592e-1 + y * (0.225319e-2 + y * (-0.157565e-2 + y * (0.916281e-2 + y * (-0.2057706e-1 + y * (0.2635537e-1 + y * (-0.1647633e-1 + y * 0.392377e-2)))))));
answer *= (Math.exp(ax) / Math.sqrt(ax));
}
return answer;
}
public static final double bessi1(double x) {
double answer;
double ax = Math.abs(x);
if (ax < 3.75) { // polynomial fit
double y = x / 3.75;
y *= y;
answer = ax * (0.5 + y * (0.87890594 + y * (0.51498869 + y * (0.15084934 + y * (0.2658733e-1 + y * (0.301532e-2 + y * 0.32411e-3))))));
} else {
double y = 3.75 / ax;
answer = 0.2282967e-1 + y * (-0.2895312e-1 + y * (0.1787654e-1 - y * 0.420059e-2));
answer = 0.39894228 + y * (-0.3988024e-1 + y * (-0.362018e-2 + y * (0.163801e-2 + y * (-0.1031555e-1 + y * answer))));
answer *= (Math.exp(ax) / Math.sqrt(ax));
}
return answer;
}
public static final double bessi(int n, double x) {
if (n < 2)
throw new IllegalArgumentException("Function order must be greater than 1");
if (x == 0.0) {
return 0.0;
} else {
double tox = 2.0/Math.abs(x);
double ans = 0.0;
double bip = 0.0;
double bi = 1.0;
for (int j = 2*(n + (int)Math.sqrt(ACC*n)); j > 0; --j) {
double bim = bip + j*tox*bi;
bip = bi;
bi = bim;
if (Math.abs(bi) > BIGNO) {
ans *= BIGNI;
bi *= BIGNI;
bip *= BIGNI;
}
if (j == n) {
ans = bip;
}
}
ans *= bessi0(x)/bi;
return (((x < 0.0) && ((n % 2) == 0)) ? -ans : ans);
}
}
}

Resources