Tutoriel Boost.NumPy pour l'extension de Python en C ++ (pratique)

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é.

Motivation

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.

Pourquoi 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.

Objectif

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:

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;

Comment utiliser 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 ()

La 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 ++.

Comment utiliser 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.

finalement

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

Tutoriel Boost.NumPy pour l'extension de Python en C ++ (pratique)
Étendre python en C ++ (Boost.NumPy)
Next Python en langage C
API C en Python 3
Techniques de tri en Python
Recherche binaire en Python / C ++
Présentation de Python en pratique (PiP)
À propos de "for _ in range ():" de python
Méthode de Newton en C ++, Python, Go (pour comprendre les objets de fonction)
Analyse des ondes cérébrales avec Python: tutoriel Python MNE
Rechercher les fuites de mémoire dans Python
Rechercher des commandes externes avec python
ABC166 en Python A ~ C problème
Aide-mémoire Python (pour les expérimentés C ++)
Résoudre ABC036 A ~ C avec Python
Conseils pour appeler Python à partir de C
Comment envelopper C en Python
Algorithme (arborescence de segments) en Python (s'entraîner)
Exécutez unittest en Python (pour les débutants)
Résoudre ABC037 A ~ C avec Python
Ecrire un test unitaire de langage C en Python
Historique d'apprentissage pour participer au développement d'applications d'équipe avec Python ~ Tutoriel Django 5 ~
Historique d'apprentissage pour participer au développement d'applications d'équipe avec Python ~ Tutoriel Django 4 ~
Historique d'apprentissage pour participer au développement d'applications d'équipe avec Python ~ Tutoriel Django 1, 2, 3 ~
Bonnes pratiques pour la journalisation au format JSON sur AWS Lambda / Python
Historique d'apprentissage pour participer au développement d'applications d'équipe avec Python ~ Tutoriel Django 6 ~
Historique d'apprentissage pour participer au développement d'applications d'équipe en Python ~ Tutoriel Django 7 ~
Résoudre ABC175 A, B, C avec Python
Note de nfc.ContactlessFrontend () de nfcpy de python
Algorithme en Python (ABC 146 C Dichotomy
Implémenter le filtre FIR en langage Python et C
Conseils pour gérer les binaires en Python
Résumé de diverses instructions for en Python
Tapez les annotations pour Python2 dans les fichiers stub!
Tutoriel Python
Ecrire le fichier O_SYNC en C et Python
Modèle pour l'écriture de scripts batch en python
Traiter plusieurs listes avec for en Python
MongoDB avec Python pour la première fois
Obtenez un jeton pour conoha avec python
Exemple de gestion des fichiers eml en Python
Fiche de triche AtCoder en python (pour moi-même)
J'ai cherché un nombre premier avec python
Remarques sur l'utilisation de python (pydev) avec eclipse
Générer un langage C à partir d'une expression S avec Python
Conseils pour créer de petits outils avec python
Utilisez pathlib dans Maya (Python2.7) en préparation du prochain Python3.7
Exécutez Python en C ++ sur Visual Studio 2017
Une introduction à Python pour les programmeurs en langage C
Rendre la bibliothèque créée par Eigen of C ++ disponible à partir de Python avec Boost.Numpy.
Tapez des notes sur les scripts Python pour exécuter le modèle PyTorch en C ++ avec libtorch
Modèle pour créer des applications de ligne de commande en Python
[Python, Scala] Faites un tutoriel pour Apache Spark
Comment utiliser la bibliothèque C en Python
CERTIFICATE_VERIFY_FAILED dans Python 3.6, le programme d'installation officiel de macOS
Utilisez Python pour une sortie formatée telle que C / C ++ printf
++ et-ne peuvent pas être utilisés pour incrémenter / décrémenter en python
Comment générer une séquence en Python et C ++
Exécutez Python YOLOv3 en C ++ sur Visual Studio 2017