Bibliothèques C ++ pour le calcul statistique

23

J'ai un algorithme MCMC particulier que je voudrais porter en C / C ++. Une grande partie du calcul coûteux est déjà en C via Cython, mais je veux que l'échantillonneur entier soit écrit dans un langage compilé afin que je puisse simplement écrire des wrappers pour Python / R / Matlab / peu importe.

Après avoir fouillé, je me penche vers C ++. Quelques bibliothèques pertinentes que je connais sont Armadillo (http://arma.sourceforge.net/) et Scythe (http://scythe.wustl.edu/). Les deux essaient d'émuler certains aspects de R / Matlab pour faciliter la courbe d'apprentissage, ce que j'aime beaucoup. Scythe est un peu mieux avec ce que je veux faire, je pense. En particulier, son RNG comprend de nombreuses distributions où Armadillo n'a qu'un uniforme / normal, ce qui n'est pas pratique. Armadillo semble être en développement assez actif tandis que Scythe a vu sa dernière version en 2007.

Donc, ce que je me demande, c'est si quelqu'un a de l'expérience avec ces bibliothèques - ou d'autres que j'ai presque sûrement manquées - et si oui, s'il y a quelque chose à recommander l'une par rapport aux autres pour un statisticien très familier avec Python / R / Matlab mais moins avec les langages compilés (pas complètement ignorants, mais pas exactement compétents ...).

JMS
la source

Réponses:

18

Nous avons passé un peu de temps à rendre l' emballage de C ++ en R (et vice-versa ) beaucoup plus facile via notre package Rcpp .

Et parce que l'algèbre linéaire est déjà un domaine si bien compris et codé, Armadillo , une bibliothèque actuelle, moderne, agréable, bien documentée, petite, basée sur des modèles, ... était une solution très naturelle pour notre premier wrapper étendu: RcppArmadillo .

Cela a également attiré l'attention d'autres utilisateurs de MCMC. J'ai donné un travail d'une journée à l'école de commerce de l'Université de Rochester l'été dernier et j'ai aidé un autre chercheur du MidWest avec des explorations similaires. Essayez RcppArmadillo - cela fonctionne bien, est activement maintenu (nouvelle version Armadillo 1.1.4 aujourd'hui, je ferai un nouveau RcppArmadillo plus tard) et pris en charge.

Et parce que je viens de lire tellement cet exemple, voici une version rapide et rapide du lm()coefficient de retour et des erreurs std:

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
}

Enfin, vous bénéficiez également d'un prototypage immédiat via inline, ce qui peut accélérer le «temps de codage».

Dirk Eddelbuettel
la source
Merci Dirk - j'avais l'impression que vous répondriez le plus tôt possible :). Étant donné que je veux du code que je peux appeler à partir d'autres logiciels (Python principalement, mais aussi Matlab), un bon flux de travail serait peut-être de prototyper dans Rcpp / RcppArmadillo, puis de passer à Armadillo "droit"? La syntaxe, etc. semble très similaire.
JMS
1
J'espère que vous l'avez trouvé utile.
Dirk Eddelbuettel
Re votre 2ème question de l'édition: Bien sûr. Armadillo dépend de peu, ou dans notre cas, rien à part R. Rcpp / RcppArmadillo ne vous aiderait à interfacer et à tester du code prototypé qui peut être réutilisé de manière autonome ou avec des wrappers Python et Matlab que vous pourrez ajouter plus tard. Conrad peut avoir des pointeurs pour quelque chose; Je n'en ai pas pour Python ou Matlab.
Dirk Eddelbuettel
Désolé de retirer le tapis :) Je veux que la touche Entrée donne un retour chariot, mais elle soumet mon commentaire à la place. Quoi qu'il en soit, merci pour votre aide - je me suis amusé à bricoler et à fouiller la liste de diffusion Rcpp toute la journée aujourd'hui.
JMS
8

Je suggérerais fortement que vous jetiez un oeil à RCppet des RcppArmadillopackages pour R. Fondamentalement, vous n'aurez pas à vous soucier des wrappers car ils sont déjà "inclus". De plus, le sucre syntaxique est vraiment sucré (jeu de mots).

En guise de remarque secondaire, je vous recommande de jeter un œil à JAGSce qui fait MCMC et son code source est en C ++.

teucer
la source
2
Je voudrais appuyer ceci. Si vous cherchez un moyen rapide et facile à l' interface code compilé avec R, Rcppavec RcppArmadillole chemin à parcourir. Edit: En utilisant Rcpp, vous avez également accès à tous les RNG implémentés dans le code C sous-jacent R.
fabians
Merci pour le vote de confiance. J'étais sur le point de suggérer la même chose ;-)
Dirk Eddelbuettel
7

Boost Random des bibliothèques Boost C ++ pourrait vous convenir. En plus de nombreux types de RNG, il offre une variété de distributions différentes à utiliser, telles que

  • Uniforme (réel)
  • Uniforme (sphère unitaire ou dimension arbitraire)
  • Bernoulli
  • Binomial
  • Cauchy
  • Gamma
  • Poisson
  • Géométrique
  • Triangle
  • Exponentiel
  • Ordinaire
  • Lognormal

De plus, Boost Math complète les distributions ci-dessus à partir desquelles vous pouvez échantillonner avec de nombreuses fonctions de densité de nombreuses distributions. Il a également plusieurs fonctions d'assistance soignées; Juste pour vous donner une idée:

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;
}

Si vous avez décidé d'utiliser Boost, vous pouvez également utiliser sa bibliothèque UBLAS qui propose une variété de types de matrice et d'opérations différents.

mavam
la source
Merci pour le conseil. Boost ressemble à un gros marteau pour mon petit ongle, mais mature et entretenu.
JMS
Je ne suis pas sûr que boot :: math :: binomial_distribution ait la même fonction que celle implémentée dans R binom.test () bilatéral. J'ai regardé la référence et n'ai pas pu trouver cette fonction. J'ai essayé de l'implémenter, et ce n'est pas anodin!
Kemin Zhou
1

Il existe de nombreuses bibliothèques C / C ++, la plupart se concentrant sur un domaine de problème particulier (par exemple les solveurs PDE). Il y a deux bibliothèques complètes auxquelles je peux penser que vous pouvez trouver particulièrement utiles car elles sont écrites en C mais ont d'excellents wrappers Python déjà écrits.

1) IMSL C et PyIMSL

2) trilinos et pytrilinos

Je n'ai jamais utilisé de trilinos car la fonctionnalité concerne principalement les méthodes d'analyse numérique, mais j'utilise beaucoup PyIMSL pour le travail statistique (et dans une vie professionnelle précédente, j'ai également développé le logiciel).

En ce qui concerne les RNG, voici ceux en C et Python en IMSL

DISCRET

  • random_binomial: génère des nombres binomiaux pseudo-aléatoires à partir d'une distribution binomiale.
  • random_geometric: génère des nombres pseudo-aléatoires à partir d'une distribution géométrique.
  • random_hypergeometric: génère des nombres pseudo-aléatoires à partir d'une distribution hypergéométrique.
  • random_logarithmic: génère des nombres pseudo-aléatoires à partir d'une distribution logarithmique.
  • random_neg_binomial: génère des nombres pseudo-aléatoires à partir d'une distribution binomiale négative.
  • random_poisson: Génère des nombres pseudo-aléatoires à partir d'une distribution de Poisson.
  • random_uniform_discrete: génère des nombres pseudo-aléatoires à partir d'une distribution uniforme discrète.
  • random_general_discrete: génère des nombres pseudo-aléatoires à partir d'une distribution discrète générale à l'aide d'une méthode d'alias ou éventuellement d'une méthode de recherche de table.

DISTRIBUTIONS CONTINUES UNIVARIES

  • random_beta: génère des nombres pseudo-aléatoires à partir d'une distribution bêta.
  • random_cauchy: génère des nombres pseudo-aléatoires à partir d'une distribution de Cauchy.
  • random_chi_squared: Génère des nombres pseudo-aléatoires à partir d'une distribution chi carré.
  • random_exponential: génère des nombres pseudo-aléatoires à partir d'une distribution exponentielle standard.
  • random_exponential_mix: Génère des nombres mixtes pseudo-aléatoires à partir d'une distribution exponentielle standard.
  • random_gamma: génère des nombres pseudo-aléatoires à partir d'une distribution gamma standard.
  • random_lognormal: génère des nombres pseudo-aléatoires à partir d'une distribution log-normale.
  • random_normal: génère des nombres pseudo-aléatoires à partir d'une distribution normale standard.
  • random_stable: configure une table pour générer des nombres pseudo-aléatoires à partir d'une distribution discrète générale.
  • random_student_t: génère des nombres pseudo-aléatoires à partir de la distribution t d'un étudiant.
  • random_triangular: génère des nombres pseudo-aléatoires à partir d'une distribution triangulaire.
  • random_uniform: Génère des nombres pseudo-aléatoires à partir d'une distribution uniforme (0, 1).
  • random_von_mises: génère des nombres pseudo-aléatoires à partir d'une distribution de von Mises.
  • random_weibull: Génère des nombres pseudo-aléatoires à partir d'une distribution de Weibull.
  • random_general_continuous: génère des nombres pseudo-aléatoires à partir d'une distribution continue générale.

DISTRIBUTIONS CONTINUES MULTIVARIES

  • random_normal_multivariate: génère des nombres pseudo-aléatoires à partir d'une distribution normale multivariée.
  • random_orthogonal_matrix: génère une matrice orthogonale pseudo-aléatoire ou une matrice de corrélation.
  • random_mvar_from_data: génère des nombres pseudo-aléatoires à partir d'une distribution multivariée déterminée à partir d'un échantillon donné.
  • random_multinomial: génère des nombres pseudo-aléatoires à partir d'une distribution multinomiale.
  • random_sphere: génère des points pseudo-aléatoires sur un cercle unitaire ou une sphère K-dimensionnelle.
  • random_table_twoway: génère une table bidirectionnelle pseudo-aléatoire.

STATISTIQUES DE COMMANDE

  • random_order_normal: génère des statistiques d'ordre pseudo-aléatoire à partir d'une distribution normale standard.
  • random_order_uniform: génère des statistiques d'ordre pseudo-aléatoire à partir d'une distribution uniforme (0, 1).

PROCESSUS STOCHASTIQUES

  • random_arma: génère des numéros de processus ARMA pseudo-aléatoires.
  • random_npp: génère des nombres pseudo-aléatoires à partir d'un processus de Poisson non homogène.

ÉCHANTILLONS ET AUTORISATIONS

  • random_permutation: génère une permutation pseudo-aléatoire.
  • random_sample_indices: génère un échantillon pseudo-aléatoire simple d'indices.
  • random_sample: génère un échantillon pseudo-aléatoire simple à partir d'une population finie.

FONCTIONS UTILITAIRES

  • random_option: sélectionne le générateur de nombres pseudo-aléatoires multiplicatifs congruents uniformes (0, 1).
  • random_option_get: récupère le générateur de nombres pseudo-aléatoires multiplicatifs congruents uniformes (0, 1).
  • random_seed_get: récupère la valeur actuelle de la graine utilisée dans les générateurs de nombres aléatoires IMSL.
  • random_substream_seed_get: récupère une graine pour les générateurs congruentiels qui ne font pas de brassage qui généreront des nombres aléatoires commençant 100 000 nombres plus loin.
  • random_seed_set: Initialise une graine aléatoire à utiliser dans les générateurs de nombres aléatoires IMSL.
  • random_table_set: définit la table actuelle utilisée dans le générateur mélangé.
  • random_table_get: récupère la table actuelle utilisée dans le générateur mélangé.
  • random_GFSR_table_set: définit la table actuelle utilisée dans le générateur GFSR.
  • random_GFSR_table_get: récupère la table actuelle utilisée dans le générateur GFSR.
  • random_MT32_init: initialise le générateur Mersenne Twister 32 bits à l'aide d'un tableau.
  • random_MT32_table_get: récupère la table actuelle utilisée dans le générateur Mersenne Twister 32 bits.
  • random_MT32_table_set: définit la table actuelle utilisée dans le générateur Mersenne Twister 32 bits.
  • random_MT64_init: initialise le générateur Mersenne Twister 64 bits à l'aide d'un tableau.
  • random_MT64_table_get: récupère la table actuelle utilisée dans le générateur Mersenne Twister 64 bits.
  • random_MT64_table_set: définit la table actuelle utilisée dans le générateur Mersenne Twister 64 bits.

SÉQUENCE DE FAIBLE DISCRÉANCE

  • faure_next_point: calcule une séquence Faure mélangée.
Josh Hemann
la source