Bibliotecas C ++ para computação estatística

23

Eu tenho um algoritmo MCMC específico que gostaria de portar para C / C ++. Grande parte da computação dispendiosa já está em C via Cython, mas quero que todo o amostrador seja escrito em uma linguagem compilada, para que eu possa escrever wrappers para Python / R / Matlab / o que for.

Depois de bisbilhotar, estou inclinado para C ++. Conheço algumas bibliotecas relevantes: Armadillo (http://arma.sourceforge.net/) e Scythe (http://scythe.wustl.edu/). Ambos tentam emular alguns aspectos do R / Matlab para facilitar a curva de aprendizado, que eu gosto muito. Foice quadrada um pouco melhor com o que eu quero fazer, eu acho. Em particular, seu RNG inclui muitas distribuições nas quais o Tatu possui apenas uniforme / normal, o que é inconveniente. O tatu parece estar em desenvolvimento bastante ativo, enquanto o Scythe viu seu último lançamento em 2007.

Então, o que eu estou querendo saber é se alguém tem experiência com essas bibliotecas - ou outras que eu quase certamente perdi - e se sim, se há algo para recomendar uma sobre as outras para um estatístico muito familiarizado com Python / R / Matlab mas menos ainda com idiomas compilados (não completamente ignorantes, mas não exatamente proficientes ...).

JMS
fonte

Respostas:

18

Passamos algum tempo facilitando muito o empacotamento do C ++ para o R (e vice-versa) por meio do nosso pacote Rcpp .

E como a álgebra linear já é um campo tão bem compreendido e codificado, o Armadillo , uma biblioteca atual, moderna, agradável, bem documentada, pequena, modelada, ... foi um ajuste muito natural para o nosso primeiro invólucro estendido: RcppArmadillo .

Isso chamou a atenção de outros usuários do MCMC também. Trabalhei um dia na escola de administração da Universidade de Rochester no último verão e ajudei outro pesquisador no Centro-Oeste a fazer explorações semelhantes. Experimente o RcppArmadillo - funciona bem, é mantido ativamente (o novo Armadillo versão 1.1.4 hoje, farei um novo RcppArmadillo mais tarde) e com suporte.

E como eu acabei de Luuv muito esse exemplo, aqui está uma versão "rápida" rápida do lm()retorno de coeficiente e std.errors:

extern "C" SEXP fastLm(SEXP ys, SEXP Xs) {

  try {
    Rcpp::NumericVector yr(ys);                 // creates Rcpp vector 
    Rcpp::NumericMatrix Xr(Xs);                 // creates Rcpp matrix 
    int n = Xr.nrow(), k = Xr.ncol();

    arma::mat X(Xr.begin(), n, k, false);       // avoids extra copy
    arma::colvec y(yr.begin(), yr.size(), false);

    arma::colvec coef = arma::solve(X, y);      // fit model y ~ X
    arma::colvec res = y - X*coef;              // residuals

    double s2 = std::inner_product(res.begin(), res.end(), 
                                   res.begin(), double())/(n - k);
                                            // std.errors of coefficients
    arma::colvec std_err = 
           arma::sqrt(s2 * arma::diagvec( arma::pinv(arma::trans(X)*X) ));  

    return Rcpp::List::create(Rcpp::Named("coefficients") = coef,
                              Rcpp::Named("stderr")       = std_err,
                              Rcpp::Named("df")           = n - k);

  } catch( std::exception &ex ) {
      forward_exception_to_r( ex );
  } catch(...) { 
      ::Rf_error( "c++ exception (unknown reason)" ); 
  }
  return R_NilValue; // -Wall
}

Por fim, você também obtém prototipagem imediata via inline, o que pode tornar o tempo de codificação mais rápido.

Dirk Eddelbuettel
fonte
Obrigado Dirk - Tive a sensação de que você responderia mais cedo ou mais tarde :). Dado que eu quero código que eu possa chamar de outro software (principalmente o Python, mas também o Matlab), talvez um bom fluxo de trabalho seja o protótipo no Rcpp / RcppArmadillo e, em seguida, passe para o tatu "reto"? A sintaxe, etc, parece muito semelhante.
JMS
1
Espero que você tenha achado útil.
Dirk Eddelbuettel
Re sua segunda pergunta da edição: Claro. O Armadillo depende de pouco ou, no nosso caso, nada além de R. Rcpp / RcppArmadillo ajudará você a fazer interface e testar código de protótipo que pode ser reutilizado de forma independente ou com um wrapper Python e Matlab que você pode adicionar posteriormente. Conrad pode ter ponteiros para alguma coisa; Eu não tenho nenhum para Python ou Matlab.
Dirk Eddelbuettel
Desculpe puxar o tapete :) Quero que a tecla Enter dê um retorno de carro, mas ele envia meu comentário. De qualquer forma, obrigado por sua ajuda - eu tenho me divertido mexendo e vasculhando a lista de discussão do Rcpp o dia inteiro hoje.
JMS
8

Eu sugiro fortemente que você dê uma olhada RCppe RcppArmadillopacotes R. Basicamente, você não precisa se preocupar com os invólucros, pois eles já estão "incluídos". Além disso, o açúcar sintático é realmente doce (trocadilhos).

Como observação lateral, eu recomendaria que você desse uma olhada JAGS, o que faz o MCMC e seu código fonte estarem em C ++.

teucer
fonte
2
Eu gostaria de afirmar isso. Se você está procurando uma maneira rápida e fácil de interagir com código compilado com R, Rcppcom RcppArmadilloé o caminho a percorrer. Edit: Usando Rcpp, você também tem acesso a todas as RNG implmented no código C subjacente R.
fabianos
Obrigado pelo voto de confiança. Eu estava prestes a sugerir o mesmo ;-)
Dirk Eddelbuettel
7

O Boost Random das bibliotecas Boost C ++ pode ser uma boa opção para você. Além de muitos tipos de RNGs, oferece uma variedade de diferentes distribuições para extrair, como

  • Uniforme (real)
  • Uniforme (esfera unitária ou dimensão arbitrária)
  • Bernoulli
  • Binomial
  • Cauchy
  • Gama
  • Poisson
  • Geométrico
  • Triângulo
  • Exponencial
  • Normal
  • Lognormal

Além disso, o Boost Math complementa as distribuições acima, das quais você pode experimentar várias funções de densidade de muitas distribuições. Ele também possui várias funções de ajuda; Só para lhe dar uma ideia:

students_t dist(5);

cout << "CDF at t = 1 is " << cdf(dist, 1.0) << endl;
cout << "Complement of CDF at t = 1 is " << cdf(complement(dist, 1.0)) << endl;

for(double i = 10; i < 1e10; i *= 10)
{
   // Calculate the quantile for a 1 in i chance:
   double t = quantile(complement(dist, 1/i));
   // Print it out:
   cout << "Quantile of students-t with 5 degrees of freedom\n"
           "for a 1 in " << i << " chance is " << t << endl;
}

Se você decidiu usar o Boost, também pode usar sua biblioteca UBLAS, que apresenta uma variedade de tipos e operações de matriz diferentes.

mavam
fonte
Obrigado pela dica. Boost parece um grande martelo para a minha unha, mas maduro e mantido.
JMS
Não tenho certeza se boot :: math :: binomial_distribution tem a mesma função implementada em R binom.test () frente e verso. Eu olhei para a referência e não consegui encontrar essa função. Eu tentei implementar isso, e não é trivial!
Kemin Zhou
1

Existem inúmeras bibliotecas C / C ++ por aí, a maioria focada em um domínio de problemas específico de (por exemplo, solucionadores de PDE). Existem duas bibliotecas abrangentes que você pode achar especialmente úteis, porque elas são escritas em C, mas têm excelentes wrappers Python já escritos.

1) IMSL C e PyIMSL

2) trilinos e pirtrilinos

Eu nunca usei trilinos, pois a funcionalidade é principalmente em métodos de análise numérica, mas uso muito o PyIMSL para trabalhos estatísticos (e em uma vida profissional anterior, desenvolvi o software também).

Com relação aos RNGs, aqui estão os em C e Python no IMSL

DISCRETO

  • random_binomial: gera números binomiais pseudo-aleatórios a partir de uma distribuição binomial.
  • random_geometric: gera números pseudo-aleatórios a partir de uma distribuição geométrica.
  • random_hypergeometric: gera números pseudo-aleatórios a partir de uma distribuição hipergeométrica.
  • random_logarithmic: gera números pseudo-aleatórios a partir de uma distribuição logarítmica.
  • random_neg_binomial: gera números pseudo-aleatórios a partir de uma distribuição binomial negativa.
  • random_poisson: gera números pseudo-aleatórios a partir de uma distribuição Poisson.
  • random_uniform_discrete: gera números pseudo-aleatórios a partir de uma distribuição uniforme e discreta.
  • random_general_discrete: gera números pseudo-aleatórios a partir de uma distribuição discreta geral usando um método de alias ou, opcionalmente, um método de pesquisa de tabela.

DISTRIBUIÇÕES CONTÍNUAS UNIVARIADAS

  • random_beta: gera números pseudo-aleatórios a partir de uma distribuição beta.
  • random_cauchy: gera números pseudo-aleatórios a partir de uma distribuição Cauchy.
  • random_chi_squared: gera números pseudo-aleatórios a partir de uma distribuição qui-quadrado.
  • random_exponential: gera números pseudo-aleatórios a partir de uma distribuição exponencial padrão.
  • random_exponential_mix: gera números mistos pseudo-aleatórios a partir de uma distribuição exponencial padrão.
  • random_gamma: gera números pseudo-aleatórios a partir de uma distribuição gama padrão.
  • random_lognormal: gera números pseudo-aleatórios a partir de uma distribuição lognormal.
  • random_normal: gera números pseudo-aleatórios a partir de uma distribuição normal padrão.
  • random_stable: configura uma tabela para gerar números pseudo-aleatórios a partir de uma distribuição discreta geral.
  • random_student_t: gera números pseudo-aleatórios a partir da distribuição t de um aluno.
  • random_triangular: gera números pseudo-aleatórios a partir de uma distribuição triangular.
  • random_uniform: gera números pseudo-aleatórios a partir de uma distribuição uniforme (0, 1).
  • random_von_mises: gera números pseudo-aleatórios a partir de uma distribuição von Mises.
  • random_weibull: gera números pseudo-aleatórios a partir de uma distribuição Weibull.
  • random_general_continuous: gera números pseudo-aleatórios a partir de uma distribuição contínua geral.

DISTRIBUIÇÕES CONTÍNUAS MULTIVARIADAS

  • random_normal_multivariate: gera números pseudo-aleatórios a partir de uma distribuição normal multivariada.
  • random_orthogonal_matrix: gera uma matriz ortogonal pseudo-aleatória ou uma matriz de correlação.
  • random_mvar_from_data: gera números pseudo-aleatórios a partir de uma distribuição multivariada determinada a partir de uma determinada amostra.
  • random_multinomial: gera números pseudo-aleatórios a partir de uma distribuição multinomial.
  • random_sphere: gera pontos pseudo-aleatórios em um círculo unitário ou esfera K-dimensional.
  • random_table_twoway: gera uma tabela bidirecional pseudo-aleatória.

ESTATÍSTICAS DO PEDIDO

  • random_order_normal: gera estatísticas de pedidos pseudo-aleatórios a partir de uma distribuição normal padrão.
  • random_order_uniform: gera estatísticas de ordem pseudo-aleatória a partir de uma distribuição uniforme (0, 1).

PROCESSOS ESTOCÁSTICOS

  • random_arma: gera números de processo ARMA pseudoaleatórios.
  • random_npp: gera números pseudo-aleatórios a partir de um processo não-homogêneo de Poisson.

AMOSTRAS E PERMUTAÇÕES

  • random_permutation: gera uma permutação pseudo-aleatória.
  • random_sample_indices: gera uma amostra pseudo-aleatória simples de índices.
  • random_sample: gera uma amostra pseudo-aleatória simples de uma população finita.

FUNÇÕES DE UTILIDADE

  • random_option: Seleciona o gerador de número pseudo-aleatório multiplicativo uniforme (0, 1).
  • random_option_get: Recupera o gerador de números pseudo-aleatórios multiplicativos uniformes (0, 1).
  • random_seed_get: recupera o valor atual da semente usada nos geradores de números aleatórios IMSL.
  • random_substream_seed_get: Recupera uma semente para os geradores congruenciais que não fazem shuffling que gerará números aleatórios começando 100.000 números adiante.
  • random_seed_set: inicializa uma semente aleatória para uso nos geradores de números aleatórios IMSL.
  • random_table_set: define a tabela atual usada no gerador aleatório.
  • random_table_get: recupera a tabela atual usada no gerador aleatório.
  • random_GFSR_table_set: define a tabela atual usada no gerador GFSR.
  • random_GFSR_table_get: recupera a tabela atual usada no gerador GFSR.
  • random_MT32_init: inicializa o gerador Mersenne Twister de 32 bits usando uma matriz.
  • random_MT32_table_get: recupera a tabela atual usada no gerador Mersenne Twister de 32 bits.
  • random_MT32_table_set: define a tabela atual usada no gerador Mersenne Twister de 32 bits.
  • random_MT64_init: inicializa o gerador Mersenne Twister de 64 bits usando uma matriz.
  • random_MT64_table_get: recupera a tabela atual usada no gerador Mersenne Twister de 64 bits.
  • random_MT64_table_set: define a tabela atual usada no gerador Mersenne Twister de 64 bits.

SEQUÊNCIA DE BAIXA DISCREPÂNCIA

  • faure_next_point: Calcula uma sequência Faure embaralhada.
Josh Hemann
fonte