Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1374757
Normal Linear GLM working
VMatthijs Oct 19, 2017
fd65182
Added Test, which passes
VMatthijs Oct 19, 2017
de35b3b
Removed poissonglm from mat.hpp, to help merging
VMatthijs Oct 19, 2017
19b0b41
Removed poisson regression files to help merging
VMatthijs Oct 19, 2017
d9acb80
Satisfied cpplint + did performance tests - 4.5 performance gain over…
VMatthijs Oct 19, 2017
5d155f2
commented out performance test
VMatthijs Oct 19, 2017
7e4e5fd
initial stab at neg_binomial_2_log_glm
VMatthijs Oct 24, 2017
95f1a74
neg_binomial_2_log_glm finished and working
VMatthijs Oct 24, 2017
6f71ca9
minor speedup
VMatthijs Oct 25, 2017
6aba311
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
VMatthijs Oct 25, 2017
3906562
done for review
VMatthijs Nov 1, 2017
1d0c285
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
VMatthijs Nov 1, 2017
625ace1
satisfied cpplint for test
VMatthijs Nov 1, 2017
dde66fd
minor
VMatthijs Nov 10, 2017
d276308
Merge branch 'develop' into feature/neg_binomial_2_log_glm
seantalts Nov 13, 2017
178adb8
fixed passing by reference in lambdas
VMatthijs Nov 13, 2017
4385fec
Merge branch 'feature/neg_binomial_2_log_glm' of https://github.com/V…
VMatthijs Nov 13, 2017
94ce8f1
Merge branch 'develop' into feature/neg_binomial_2_log_glm
VMatthijs Nov 14, 2017
0866b23
Merge branch 'develop' into feature/neg_binomial_2_log_glm
VMatthijs Nov 14, 2017
f168bfd
minor
VMatthijs Nov 14, 2017
d6135c8
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
VMatthijs Nov 14, 2017
e3147a7
Merge branch 'feature/neg_binomial_2_log_glm' of https://github.com/V…
VMatthijs Nov 14, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions stan/math/prim/mat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@
#include <stan/math/prim/mat/prob/multinomial_log.hpp>
#include <stan/math/prim/mat/prob/multinomial_lpmf.hpp>
#include <stan/math/prim/mat/prob/multinomial_rng.hpp>
#include <stan/math/prim/mat/prob/neg_binomial_2_log_glm_lpmf.hpp>
#include <stan/math/prim/mat/prob/normal_id_glm_lpdf.hpp>
#include <stan/math/prim/mat/prob/ordered_logistic_log.hpp>
#include <stan/math/prim/mat/prob/ordered_logistic_lpmf.hpp>
Expand Down
19 changes: 9 additions & 10 deletions stan/math/prim/mat/prob/bernoulli_logit_glm_lpmf.hpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
#ifndef STAN_MATH_PRIM_MAT_PROB_BERNOULLI_LOGIT_GLM_LPMF_HPP
#define STAN_MATH_PRIM_MAT_PROB_BERNOULLI_LOGIT_GLM_LPMF_HPP


#include <stan/math/prim/scal/meta/is_constant_struct.hpp>
#include <stan/math/prim/scal/meta/partials_return_type.hpp>
#include <stan/math/prim/scal/meta/operands_and_partials.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_bounded.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/log1m.hpp>
#include <stan/math/prim/scal/fun/value_of.hpp>
#include <stan/math/prim/mat/fun/value_of.hpp>
#include <stan/math/prim/scal/meta/include_summand.hpp>
#include <stan/math/prim/scal/meta/scalar_seq_view.hpp>
#include <stan/math/prim/scal/fun/size_zero.hpp>
#include <boost/random/bernoulli_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include <cmath>

Expand All @@ -32,15 +30,16 @@ namespace stan {
* should be an Eigen::Matrix type whose number of rows should match the
* length of n and whose number of columns should match the length of beta
* @tparam T_beta type of the weight vector;
* this can also be a single double value;
* this can also be a single value;
* @tparam T_alpha type of the intercept;
* this can either be a vector of doubles of a single double value;
* this has to be single value;
* @param n binary vector parameter
* @param x design matrix
* @param beta weight vector
* @param alpha intercept (in log odds)
* @return log probability or log sum of probabilities
* @throw std::domain_error if theta is infinite.
* @throw std::domain_error if x, beta or alpha is infinite.
* @throw std::domain_error if n is not binary.
* @throw std::invalid_argument if container sizes mismatch.
*/
template <bool propto, typename T_n, typename T_x, typename T_beta,
Expand All @@ -63,9 +62,9 @@ namespace stan {
T_partials_return logp(0.0);

check_bounded(function, "Vector of dependent variables", n, 0, 1);
check_not_nan(function, "Matrix of independent variables", x);
check_not_nan(function, "Weight vector", beta);
check_not_nan(function, "Intercept", alpha);
check_finite(function, "Matrix of independent variables", x);
check_finite(function, "Weight vector", beta);
check_finite(function, "Intercept", alpha);
check_consistent_sizes(function,
"Rows in matrix of independent variables",
x.col(0), "Vector of dependent variables", n);
Expand All @@ -79,7 +78,7 @@ namespace stan {
const size_t N = x.col(0).size();
const size_t M = x.row(0).size();

Matrix<double, Dynamic, 1> signs(N, 1);
Matrix<T_partials_return, Dynamic, 1> signs(N, 1);
{
scalar_seq_view<T_n> n_vec(n);
for (size_t n = 0; n < N; ++n) {
Expand Down
196 changes: 196 additions & 0 deletions stan/math/prim/mat/prob/neg_binomial_2_log_glm_lpmf.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
#ifndef STAN_MATH_PRIM_MAT_PROB_NEG_BINOMIAL_2_LOG_GLM_LPMF_HPP
#define STAN_MATH_PRIM_MAT_PROB_NEG_BINOMIAL_2_LOG_GLM_LPMF_HPP

#include <stan/math/prim/scal/meta/is_constant_struct.hpp>
#include <stan/math/prim/scal/meta/partials_return_type.hpp>
#include <stan/math/prim/scal/meta/operands_and_partials.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_positive_finite.hpp>
#include <stan/math/prim/scal/err/check_nonnegative.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/multiply_log.hpp>
#include <stan/math/prim/scal/fun/digamma.hpp>
#include <stan/math/prim/scal/fun/lgamma.hpp>
#include <stan/math/prim/scal/fun/log_sum_exp.hpp>
#include <stan/math/prim/scal/fun/value_of.hpp>
#include <stan/math/prim/mat/fun/value_of.hpp>
#include <stan/math/prim/scal/meta/include_summand.hpp>
#include <stan/math/prim/scal/meta/scalar_seq_view.hpp>
#include <cmath>

namespace stan {
namespace math {

/**
* Returns the log PMF of the Generalized Linear Model (GLM)
* with Negative-Binomial-2 distribution and log link function.
* If containers are supplied, returns the log sum of the probabilities.
* @tparam T_n type of positive int vector of variates (labels);
* this can also be a single positive integer value;
* @tparam T_x type of the matrix of covariates (features); this
* should be an Eigen::Matrix type whose number of rows should match the
* length of n and whose number of columns should match the length of beta
* @tparam T_beta type of the weight vector;
* this can also be a scalar;
* @tparam T_alpha type of the intercept;
* this should be a scalar;
* @tparam T_precision type of the (positive) precision vector phi;
* this can also be a scalar;
* @param n failures count vector parameter
* @param x design matrix
* @param beta weight vector
* @param alpha intercept (in log odds)
* @param phi (vector of) precision parameters
* @return log probability or log sum of probabilities
* @throw std::invalid_argument if container sizes mismatch.
* @throw std::domain_error if x, beta or alpha is infinite.
* @throw std::domain_error if phi is infinite or non-positive.
* @throw std::domain_error if n is negative.
*/
template <bool propto, typename T_n, typename T_x, typename T_beta,
typename T_alpha, typename T_precision>
typename return_type<T_x, T_beta, T_alpha, T_precision>::type
neg_binomial_2_log_glm_lpmf(const T_n &n, const T_x &x, const T_beta &beta,
const T_alpha &alpha, const T_precision &phi) {
static const char* function = "neg_binomial_2_log_glm_lpmf";
typedef typename stan::partials_return_type<T_n, T_x, T_beta,
T_alpha, T_precision>::type
T_partials_return;

using std::exp;
using Eigen::Dynamic;
using Eigen::Matrix;
using Eigen::Array;

if (!(stan::length(n) && stan::length(x) && stan::length(beta)
&& stan::length(phi)))
return 0.0;

T_partials_return logp(0.0);

check_nonnegative(function, "Failures variables", n);
check_finite(function, "Matrix of independent variables", x);
check_finite(function, "Weight vector", beta);
check_finite(function, "Intercept", alpha);
check_positive_finite(function, "Precision parameter", phi);
check_consistent_sizes(function,
"Rows in matrix of independent variables",
x.col(0), "Vector of dependent variables", n);
check_consistent_sizes(function,
"Rows in matrix of independent variables",
x.col(0), "Vector of failure counts", phi);
check_consistent_sizes(function,
"Columns in matrix of independent variables",
x.row(0), "Weight vector", beta);

if (!include_summand<propto, T_x, T_beta, T_alpha, T_precision>::value)
return 0.0;

const size_t N = x.col(0).size();
const size_t M = x.row(0).size();

Array<T_partials_return, Dynamic, 1> n_arr(N, 1);
Array<T_partials_return, Dynamic, 1> phi_arr(N, 1);
{
scalar_seq_view<T_n> n_vec(n);
scalar_seq_view<T_precision> phi_vec(phi);
for (size_t n = 0; n < N; ++n) {
n_arr[n] = n_vec[n];
phi_arr[n] = value_of(phi_vec[n]);
}
}

Matrix<T_partials_return, Dynamic, 1> beta_dbl(M, 1);
{
scalar_seq_view<T_beta> beta_vec(beta);
for (size_t m = 0; m < M; ++m) {
beta_dbl[m] = value_of(beta_vec[m]);
}
}
Matrix<T_partials_return, Dynamic, Dynamic> x_dbl = value_of(x);

Array<T_partials_return, Dynamic, 1> theta_dbl = (x_dbl * beta_dbl
+ Matrix<double, Dynamic, 1>::Ones(N, 1) * value_of(alpha)).array();
Array<T_partials_return, Dynamic, 1> log_phi = phi_arr.log();
Array<T_partials_return, Dynamic, 1> logsumexp_eta_logphi =
theta_dbl.binaryExpr(log_phi,
[](const T_partials_return& xx,
const T_partials_return& yy) {
return log_sum_exp(xx, yy);
});
Array<T_partials_return, Dynamic, 1> n_plus_phi = n_arr + phi_arr;

// Compute the log-density.
if (include_summand<propto>::value) {
logp -= (n_arr
+ Array<double, Dynamic, 1>::Ones(N, 1))
.unaryExpr([](const T_partials_return& xx) {
return lgamma(xx);
}).sum();
}
if (include_summand<propto, T_precision>::value) {
logp += (phi_arr.binaryExpr(phi_arr, [](const T_partials_return& xx,
const T_partials_return& yy) {
return multiply_log(xx, yy);
})
- phi_arr.unaryExpr([](const T_partials_return& xx) {
return lgamma(xx);
})).sum();
}
if (include_summand<propto, T_x, T_beta, T_alpha, T_precision>::value)
logp -= (n_plus_phi * logsumexp_eta_logphi).sum();
if (include_summand<propto, T_x, T_beta, T_alpha>::value)
logp += (n_arr * theta_dbl).sum();
if (include_summand<propto, T_precision>::value) {
logp += n_plus_phi.unaryExpr([](const T_partials_return& xx) {
return lgamma(xx);
}).sum();
}

// Compute the necessary derivatives.
operands_and_partials<T_x, T_beta, T_alpha,
T_precision> ops_partials(x, beta, alpha, phi);

if (!(is_constant_struct<T_x>::value && is_constant_struct<T_beta>::value
&& is_constant_struct<T_alpha>::value)) {
Matrix<T_partials_return, Dynamic, 1> theta_derivative(N, 1);
theta_derivative = (n_arr - (n_plus_phi / (phi_arr / (theta_dbl.exp())
+ Array<double, Dynamic, 1>::Ones(N, 1)))).matrix();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have wisdom / rules of thumb to share on when .matrix() or .eval() are required?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, a little bit. .matrix() and .array() convert back and forth between Eigen::Matrix and Eigen::Array types. There is no runtime cost. The two types just provide a different interface: matrix operations vs coefficient-wise operations. Sometimes you need one, sometimes the other.
.eval() forces eager evaluation, as far as I understand it. By default, Eigen has lazy evaluation. I guess there are some cases where you want eager evaluation for efficiency or for correctness purposes, which you can force with .eval(). I haven't used it though.

if (!is_constant_struct<T_beta>::value) {
ops_partials.edge2_.partials_ = x_dbl.transpose() * theta_derivative;
}
if (!is_constant_struct<T_x>::value) {
ops_partials.edge1_.partials_ = theta_derivative
* beta_dbl.transpose();
}
if (!is_constant_struct<T_alpha>::value) {
ops_partials.edge3_.partials_[0] = theta_derivative.trace();
}
}
if (!is_constant_struct<T_precision>::value) {
ops_partials.edge4_.partials_ = (Array<double, Dynamic, 1>::Ones(N, 1)
- n_plus_phi / (theta_dbl.exp() + phi_arr) + log_phi
- logsumexp_eta_logphi
- phi_arr.unaryExpr([](const T_partials_return& xx) {
return digamma(xx);
})
+ n_plus_phi.unaryExpr([](const T_partials_return& xx) {
return digamma(xx);
})).matrix();
}
return ops_partials.build(logp);
}

template <typename T_n, typename T_x, typename T_beta, typename T_alpha,
typename T_precision>
inline
typename return_type<T_x, T_beta, T_alpha, T_precision>::type
neg_binomial_2_log_glm_lpmf(const T_n &n, const T_x &x,
const T_beta &beta, const T_alpha &alpha,
const T_precision& phi) {
return neg_binomial_2_log_glm_lpmf<false>(n, x, beta, alpha, phi);
}
} // namespace math
} // namespace stan
#endif
Loading