Article du jour 10 du calendrier de l'avent C ++.
Boost 1.63 fusionne Boost.NumPy dans Boost.Python. Parallèlement à cela, il peut y avoir un problème avec la description suivante. J'écrirai un article révisé.
Python est vraiment pratique. Pour moi, qui est un universitaire (apprenti) dont l'objectif principal est la simulation et l'analyse des résultats. Un bloc-notes IPython qui vous permet de traiter, d'analyser et de visualiser les données de manière transparente de manière interactive est indispensable. Malheureusement, C ++ seul ne fournit pas (probablement) la fonctionnalité équivalente. L'accrochage de ROOT fait par le CERN semble pouvoir exécuter C ++ de manière interactive, mais je ne l'ai jamais utilisé car il y a peu d'informations. Veuillez créer un bloc-notes IC ++ que quelqu'un peut programmer en C ++ (de toute urgence).
Cependant, la simulation elle-même dure des jours, voire des semaines, La vitesse est essentielle et Python ne peut pas être écrit dans un langage lent. La simulation sera donc écrite en C ++ (qu'est-ce que Fortran est si délicieux).
D'un autre côté, des projets comme scikit-learn et PyMC sont des frontaux pour Python. Si vous regardez l'endroit où Pour le moment, je pense qu'il deviendra plus courant de l'implémenter dans un autre langage et de l'utiliser depuis Python. Donc, parmi les méthodes d'écriture de fonctions utilisables en Python en C ++ Voici comment utiliser Boost.NumPy.
Une fois, j'ai essayé d'utiliser Boost.Python et j'étais frustré parce que le convertisseur ne comprenait pas Boost.NumPy était très facile à utiliser (important).
Pour mentionner brièvement les autres options,
Boost.NumPy introduit cette fois prend simplement la méthode de construction du wrapping C ++ de numpy.ndarray
de Python.
Puisque C ++ a déjà des bibliothèques (innombrables) qui effectuent des opérations algébriques linéaires comme Eigen
Il existe également un Choice qui convertit numpy.ndarray
en vecteur multidimensionnel d'Eigen tel quel.
Plus simplement, en utilisant uniquement Boost.Python, convertissez C ++ vector
etc. en liste Python,
Vous avez également la possibilité de convertir ensuite en numpy.ndarray
.
Il en existe de nombreux autres tels que SWIG et Cython.
J'ai résumé comment installer et compiler dans Article précédent, donc cette fois je vais résumer comment l'utiliser réellement. Normalement, je veux utiliser uniquement la partie interface du code C ++ du côté Python, donc Je vais résumer comment l'utiliser le plus facilement possible tout en évitant les choses difficiles.
Je n'expliquerai pas Boost.Python, alors vérifiez-le vous-même. Vous trouverez ci-dessous une liste de sites de commentaires Boost.Python:
Boost.Python (traduction japonaise) Honke (traduction japonaise)
Tutorial for Boost.NumPy
Voici ce que j'ai écrit sur la base du tutoriel Boost.NumPy. Pour éliminer la complexité de la notation, l'espace de noms est abrégé comme suit.
namespace p = boost::python;
namespace np = boost::numpy;
np :: ndarray
(pour unidimensionnel)L'introduction a été longue, alors mettons dans un code qui fonctionne rapidement:
mymod1.cpp
#include "boost/numpy.hpp"
#include <stdexcept>
#include <algorithm>
namespace p = boost::python;
namespace np = boost::numpy;
/*Double*/
void mult_two(np::ndarray a) {
int nd = a.get_nd();
if (nd != 1)
throw std::runtime_error("a must be 1-dimensional");
size_t N = a.shape(0);
if (a.get_dtype() != np::dtype::get_builtin<double>())
throw std::runtime_error("a must be float64 array");
double *p = reinterpret_cast<double *>(a.get_data());
std::transform(p, p + N, p, [](doublex){return2*x;});
}
BOOST_PYTHON_MODULE(mymod1) {
Py_Initialize();
np::initialize();
p::def("mult_two", mult_two);
}
Cela peut être compilé tel quel. C'est une fonction simple qui double simplement un tableau unidimensionnel, mais elle contient des éléments importants.
--Utilisez get_nd ()
pour les dimensions du tableau
--Utilisez shape (n)
pour la taille du tableau
--Le type des éléments du tableau est déterminé dynamiquement et obtenu avec get_dtype ()
std :: runtime_error
est converti en RuntimeError
côté PythonLa mémoire est gérée du côté np :: ndarray
, vous n'avez donc pas besoin d'en être conscient.
Malheureusement, le type est déterminé dynamiquement, la fonction ne peut donc pas être surchargée.
Il doit être jugé par la déclaration if au moment de l'exécution.
C'est bien de convertir les exceptions.
Commençons par Python.
mymod1.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mymod1
import numpy as np
if __name__ == '__main__':
a = np.array([1,2,3], dtype=np.float64)
mymod1.mult_two(a)
print(a)
b = np.array([1,2,3], dtype=np.int64)
mymod1.mult_two(b) # raise error
print(b)
La partie b est une erreur car le type est long long
au lieu de double
comme décrit ci-dessus:
[ 2. 4. 6.]
Traceback (most recent call last):
File "/path/of/mymod1.py", line 13, in <module>
mymod1.mult_two(b)
RuntimeError: a must be float64 array
Lors de l'utilisation d'un entier sur numpy, ʻint64 (
long long` en C ++) est utilisé sauf indication contraire.
Veuillez noter que ce n'est pas «int».
Il y a des plaintes telles que l'impossibilité de surcharger, J'ai pu implémenter une fonction qui peut être facilement utilisée en Python en C ++.
np :: ndarray
(pour multidimensionnel)Les bases sont les mêmes pour le multidimensionnel, mais vous devez faire attention à l'ordre de la mémoire. L'explication de numpy.ndarray.stride est facile à comprendre, mais ci-dessous Je vais vous expliquer brièvement.
Parfois, vous souhaitez gérer un tableau bidimensionnel dans une zone de mémoire continue.
double *a_as1 = new double[N*M];
double **a_as2 = new double*[N];
for(int i=0;i<N;++i){
a_as2[i] = &a_as1[i*M];
}
De cette façon, la mémoire appelée par ʻa_as2 [i] [j] sera la même que ʻa_as1 [i * M + j]
.
La façon de faire quelque chose comme ceci ʻi * M + j est
ndarray.stride`.
Dans ce cas, il avance de 8 octets dans la mémoire pour avancer de 1 dans le sens j (le double fait 8 octets).
Par contre, pour avancer de 1 dans la direction i, il avance «8 * M» octets en mémoire («(i + 1) * M + j = i * M + j + M»).
Ces «8» et «8 * M» sont appelés stride (stride, stride).
Cette idée peut être utilisée même à des dimensions plus élevées.
Si vous faites attention à cela, le reste est facile.
#include "boost/numpy.hpp"
#include <iostream>
#include <stdexcept>
#include <algorithm>
namespace p = boost::python;
namespace np = boost::numpy;
void print(np::ndarray a) {
int nd = a.get_nd();
if (nd != 2)
throw std::runtime_error("a must be two-dimensional");
if (a.get_dtype() != np::dtype::get_builtin<double>())
throw std::runtime_error("a must be float64 array");
auto shape = a.get_shape();
auto strides = a.get_strides();
std::cout << "i j val\n";
for (int i = 0; i < shape[0]; ++i) {
for (int j = 0; j < shape[1]; ++j) {
std::cout << i << " " << j << " "
<< *reinterpret_cast<double *>(a.get_data() + i * strides[0] +
j * strides[1]) << std::endl;
}
}
}
BOOST_PYTHON_MODULE(mymod2) {
Py_Initialize();
np::initialize();
p::def("print", print);
}
Il est plus rapide de regarder la foulée et de la tourner par ordre croissant en termes d'accès mémoire, Je l'ai omis parce que c'est gênant. S'il vous plaît faites de votre mieux.
Python ou NumPy est vraiment pratique. Très probablement, SciPy fournit une interface de bibliothèque mathématique standard bien documentée.
Maintenant que nous avons résumé comment accéder aux données dans np :: ndarray
,
Je pense que l'algorithme implémenté en C ++ peut être utilisé depuis Python.
Ensuite, j'aimerais en savoir plus sur les publications sur PyPI et les publications sur scikits.
Recommended Posts