Setting parameters for gamma_distribution using boost - c++

I am trying to use the Gamma distribution from Boost 1.5.
Now I want the value of k and theta to be 4 and .5 respectively.
But I get a compile error whenever I set the value of theta < 1.
/usr/local/include/boost/random/gamma_distribution.hpp:118: boost::random::gamma_distribution<RealType>::gamma_distribution(const RealType&, const RealType&) [with RealType = double]: Assertion `_beta > result_type(0)' failed.
Is there any way to get around the same?

It looks like you do not pass the parameters correctly to the distribution function. Here is the C++11 version (Boost works equivalently):
#include <random>
#include <iostream>
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
double alpha = 4.0;
double theta = 0.5;
std::gamma_distribution<> gamma(alpha, 1.0 / theta);
auto value = gamma(gen);
// May print: 6.94045.
std::cout << value << std::endl;
return 0;
}
Note the parametrization:
alpha is the same as k
beta the inverse scale parameter and is the same as 1 / theta.

Related

C++ boost library to generate negative binomial random variables

I'm new to C++ and I'm using the boost library to generate random variables. I want to generate random variables from a negative binomial distribution.
The first parameter of boost::random::negative_binomial_distribution<int> freq_nb(r, p); has to be an integer. I want to expand that to a real value. Therefore I would like to use a poisson-gamma mixture, but I fail to.
Here's an excerpt from my code:
int nr_sim = 1000000;
double mean = 2.0;
double variance = 15.0;
double r = mean * mean / (variance - mean);
double p = mean / variance;
double beta = (1 - p) / p;
typedef boost::mt19937 RNGType;
RNGType rng(5);
boost::random::gamma_distribution<double> my_gamma(r, beta);
boost::random::poisson_distribution<int> my_poi(my_gamma(rng));
int simulated_mean = 0;
for (int i = 0; i < nr_sim; i++) {
simulated_mean += my_poi(rng);
}
double my_result = (double)simulated_mean / (double)nr_sim;
With my_result == 0.5 there is definitly something wrong. Is it my_poi(my_gamma(rng))? If so, what is the correct way to solve that problem?

Calculate p value of a t - statistic using the student_t_distribution

I wanted to calculate p-values of a t-statistic for a two tailed test with 5% level of significance. And I wanted to do this with the standard library. I was wondering if this was possible using the student_t_distribution from the < random > module.
My code currently is as following
#include <iostream>
int main(){
double t_stat = 0.0267; // t-statistic
double alpha_los = 0.05; // level of significance
double dof = 30; // degrees of freedom
// calculate P > |t| and compare with alpha_los
return 0;
}
Thank you
The <random> header just provides you with the ability to get random numbers from different distributions.
If you are able to use boost you can do the following:
#include <boost/math/distributions/students_t.hpp>
int main() {
double t_stat = 0.0267; // t-statistic
double alpha_los = 0.05; // level of significance
double dof = 30; // degrees of freedom
boost::math::students_t dist(dof);
double P_x_greater_t = 1.0 - boost::math::cdf(dist, t_stat);
double P_x_smaller_negative_t = boost::math::cdf(dist, -t_stat);
if(P_x_greater_t + P_x_smaller_negative_t < alpha_los) {
} else {
}
}

Eigen LLT (Cholesky) fails, while SVD works

I'm trying to reproduce some numpy code on Gaussian Processes (from here) using Eigen. Basically, I need to sample from a multivariate normal distribution:
samples = np.random.multivariate_normal(mu.ravel(), cov, 1)
The mean vector is currently zero, while the covariance matrix is a square matrix generated via the isotropic squared exponential kernel:
sqdist = np.sum(X1**2, 1).reshape(-1, 1) + np.sum(X2**2, 1) - 2 * np.dot(X1, X2.T)
return sigma_f**2 * np.exp(-0.5 / l**2 * sqdist)
I can generate the covariance matrix just fine for now (it can probably be cleaned but for now it's a POC):
Matrix2D kernel(const Matrix2D & x1, const Matrix2D & x2, double l = 1.0, double sigma = 1.0) {
auto dists = ((- 2.0 * (x1 * x2.transpose())).colwise()
+ x1.rowwise().squaredNorm()).rowwise() +
+ x2.rowwise().squaredNorm().transpose();
return std::pow(sigma, 2) * ((-0.5 / std::pow(l, 2)) * dists).array().exp();
}
However, my problems start when I need to sample the multivariate normal.
I've tried using the solution proposed in this accepted answer; however, the decomposition only works with covariance matrices of size up to 30x30; more than that and LLT fails to decompose the matrix. The alternative version provided in the answer also does not work, and creates NaNs. I tried LDLT as well but it also breaks (D contains negative values, so sqrt gives NaN).
Then, I got curious, and I looked into how numpy does this. Turns out the numpy implementation uses SVD decomposition (with LAPACK), rather than Cholesky. So I tried copying their implementation:
// SVD on the covariance matrix generated via kernel function
Eigen::BDCSVD<Matrix2D> solver(covs, Eigen::ComputeFullV);
normTransform = (-solver.matrixV().transpose()).array().colwise() * solver.singularValues().array().sqrt();
// Generate gaussian samples, "randN" is from the multivariate StackOverflow answer
Matrix2D gaussianSamples = Eigen::MatrixXd::NullaryExpr(1, means.size(), randN);
Eigen::MatrixXd samples = (gaussianSamples * normTransform).rowwise() + means.transpose();
The various minuses are me trying to exactly reproduce numpy's results.
In any case, this works perfectly fine, even with large dimensions. I was wondering why Eigen is not able to do LLT, but SVD works. The covariance matrix I use is the same. Is there something I can do to simply use LLT?
EDIT: Here is my full example:
#include <iostream>
#include <random>
#include <Eigen/Cholesky>
#include <Eigen/SVD>
#include <Eigen/Eigenvalues>
using Matrix2D = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::AutoAlign>;
using Vector = Eigen::Matrix<double, Eigen::Dynamic, 1>;
/*
We need a functor that can pretend it's const,
but to be a good random number generator
it needs mutable state.
*/
namespace Eigen {
namespace internal {
template<typename Scalar>
struct scalar_normal_dist_op
{
static std::mt19937 rng; // The uniform pseudo-random algorithm
mutable std::normal_distribution<Scalar> norm; // The gaussian combinator
EIGEN_EMPTY_STRUCT_CTOR(scalar_normal_dist_op)
template<typename Index>
inline const Scalar operator() (Index, Index = 0) const { return norm(rng); }
};
template<typename Scalar> std::mt19937 scalar_normal_dist_op<Scalar>::rng;
template<typename Scalar>
struct functor_traits<scalar_normal_dist_op<Scalar> >
{ enum { Cost = 50 * NumTraits<Scalar>::MulCost, PacketAccess = false, IsRepeatable = false }; };
} // end namespace internal
} // end namespace Eigen
Matrix2D kernel(const Matrix2D & x1, const Matrix2D & x2, double l = 1.0, double sigma = 1.0) {
auto dists = ((- 2.0 * (x1 * x2.transpose())).colwise() + x1.rowwise().squaredNorm()).rowwise() + x2.rowwise().squaredNorm().transpose();
return std::pow(sigma, 2) * ((-0.5 / std::pow(l, 2)) * dists).array().exp();
}
int main() {
unsigned size = 50;
unsigned seed = 1;
Matrix2D X = Vector::LinSpaced(size, -5.0, 4.8);
Eigen::internal::scalar_normal_dist_op<double> randN; // Gaussian functor
Eigen::internal::scalar_normal_dist_op<double>::rng.seed(seed); // Seed the rng
Vector means = Vector::Zero(X.rows());
auto covs = kernel(X, X);
Eigen::LLT<Matrix2D> cholSolver(covs);
// We can only use the cholesky decomposition if
// the covariance matrix is symmetric, pos-definite.
// But a covariance matrix might be pos-semi-definite.
// In that case, we'll go to an EigenSolver
Eigen::MatrixXd normTransform;
if (cholSolver.info()==Eigen::Success) {
std::cout << "Used LLT\n";
// Use cholesky solver
normTransform = cholSolver.matrixL();
} else {
std::cout << "Broken\n";
Eigen::BDCSVD<Matrix2D> solver(covs, Eigen::ComputeFullV);
normTransform = (-solver.matrixV().transpose()).array().colwise() * solver.singularValues().array().sqrt();
}
Matrix2D gaussianSamples = Eigen::MatrixXd::NullaryExpr(1, means.size(), randN);
Eigen::MatrixXd samples = (gaussianSamples * normTransform).rowwise() + means.transpose();
return 0;
}

Getting "-nan(ind)" when trying to generate random variates

I am trying generate random variates by trying to generate two standard normal variates r1, r2, by using polar coordinates along with a mean and sigma value. However when I run my code, I keep getting a "-nan(ind)" as my output.
What am I doing wrong here? The code is as follows:
static double saveNormal;
static int NumNormals = 0;
static double PI = 3.1415927;
double fRand(double fMin, double fMax)
{
double f = (double)rand() / RAND_MAX;
return fMin + f * (fMax - fMin);
}
static double normal(double r, double mean, double sigma) {
double returnNormal;
if (NumNormals == 0) {
//to get next double value
double r1 = fRand(0, 20);
double r2 = fRand(0, 20);
returnNormal = sqrt(-2 * log(r1)) * cos(2 * PI*r2);
saveNormal = sqrt(-2 * log(r1)) * sin(2 * PI*r2);
}
else {
NumNormals = 0;
returnNormal = saveNormal;
}
return returnNormal*sigma + mean;
}
So, you're using the Box–Muller method to pseudo randomly sample a normal random variate. For this transform to work, r1 and r2 must be uniformly distributed independent variates in [0,1].
Instead, your r1/r2 are [0,20] supported, resulting in a negative sqrt argument when >1, this will give you nans. Replace with
double r1 = fRand(0, 1);
double r2 = fRand(0, 1);
Moreover, you should use C++11 <random> for better pseudorandom number generation; as of now, your fRand has poor quality due to rand()-to-double conversion and possible spurious correlations between adjacent calls. Moreover, your function lacks some basic error checking and badly depends on global variables and is inherently thread unsafe.
FYI, this is what a C++11 version might look like
#include <random>
#include <iostream>
int main()
{
auto engine = std::default_random_engine{ std::random_device{}() };
auto variate = std::normal_distribution<>{ /*mean*/0., /*stddev*/ 1. };
while(true) // a lot of normal samples ...
std::cout << variate(engine) << std::endl;
}
r1 can be zero, making log(r1) undefined.
furthermore, don't use rand() except when you need your numbers to look random to a human in a hurry. Use <random> instead

RVIZ: Display own point cloud

I try to build my own point cloud with a gaussian distribution. The visualization with rviz doesn't work.
Here is how I create the pointcloud
int sizeOfCloud = 1000;
keypoints.points.resize(sizeOfCloud);
getRandomPointCloud(keypoints, 100, 100, sizeOfCloud);
keypoints.header.frame_id = "base_link";
keypoints.header.stamp = ros::Time::now();
keypoints_publisher.publish(keypoints);
and here is the function getRandomPointCloud:
void getRandomPointCloud(sensor_msgs::PointCloud& pc, int centerX, int centerY, int& sizeOfCloud) {
std::random_device rd;
std::mt19937 gen(rd());
std::normal_distribution<> distX(centerX, 10);
std::normal_distribution<> distY(centerY, 10);
for (int i = 0; i < pc.points.size(); i++) {
double xValue = distX(gen);
double yValue = distY(gen);
std::cout << std::round(xValue) << std::endl;
pc.points[i].x = std::round(xValue);
pc.points[i].y = std::round(yValue);
}
std::cout << "done" << std::endl;
}
As I said, it can't be displayed in rviz. I do select by topic, select the proper topic and then there is nothing on the screen. Topic is correct and if I set the grid to base_link then everything with the topic is okay. Maybe I have to set a special attribute in rviz or I don't build my pointcloud correctly.
Edit:
Here is a screenshot from rviz
Now I think the problem is more about the "base_link" tf topic which can't get resolved. If I try to map my tf tree then there is no entry. How do I set the base_link in my tf tree. Or is there another possibility for my purpose?
The message sensor_msgs::PointCloud pc has an array of Point32 which in turn has x, y and z values. You are setting the x and y values of each point but you are missing the z value.
I'm not sure if the rviz visualizer also requires channel information. If the point cloud is still not visible despite the z value, then set the channel information. The channel is an array in sensor_msgs::PointCloud called channels which is of type ChannelFloat32. If you have depth information you can use a single channel:
sensor_msgs::ChannelFloat32 depth_channel;
depth_channel.name = "distance";
for (int i = 0; i < pc.points.size(); i++) {
depth_channel.values.push_back(0.43242); // or set to a random value if you like
}
// add channel to point cloud
pc.channels.push_back(depth_channel);
It is also important to publish the message more than once in order to see it in rviz and often when dealing with TF you need to update the time stamp in the header.
Btw you are spreading the points around the point 100meter/10meter thats way out!
Here is my example.
Here is the code that works for me
#include <ros/ros.h>
#include <sensor_msgs/PointCloud.h>
#include <string>
#include <random>
void getRandomPointCloud(sensor_msgs::PointCloud& pc,
double centerX,
double centerY,
int& sizeOfCloud) {
std::random_device rd;
std::mt19937 gen(rd());
std::normal_distribution<> distX(centerX, 2.);
std::normal_distribution<> distY(centerY, 2.);
for (int i = 0; i < pc.points.size(); i++) {
double xValue = distX(gen);
double yValue = distY(gen);
pc.points[i].x = xValue;
pc.points[i].y = yValue;
pc.points[i].z =
std::exp(-((xValue * xValue) + (yValue * yValue)) / 4.);
}
sensor_msgs::ChannelFloat32 depth_channel;
depth_channel.name = "distance";
for (int i = 0; i < pc.points.size(); i++) {
depth_channel.values.push_back(pc.points[i].z); // or set to a random value if you like
}
// add channel to point cloud
pc.channels.push_back(depth_channel);
}
int main(int argc, char** argv) {
ros::init(argc, argv, "point_cloud_test");
auto nh = ros::NodeHandle();
int sizeOfCloud = 100000;
sensor_msgs::PointCloud keypoints;
keypoints.points.resize(sizeOfCloud);
getRandomPointCloud(keypoints, 0.5, 0.5, sizeOfCloud);
keypoints.header.frame_id = "base_link";
keypoints.header.stamp = ros::Time::now();
auto keypoints_publisher =
nh.advertise<sensor_msgs::PointCloud>("point_cloud", 10);
ros::Rate rate(30);
while (ros::ok()) {
keypoints.header.stamp = ros::Time::now();
keypoints_publisher.publish(keypoints);
ros::spinOnce();
rate.sleep();
}
return 0;
}
You might try zooming out a bit...
and of course ensure the Fixed Frame matches the frame in your message. You can see I also made the points larger (1.0 meter) and used a flat colour to ensure visibility over your enormous scale