C ++ Adventskalender Tag 10 Artikel.
Boost.NumPy wird in Boost 1.63 mit Boost.Python zusammengeführt. Gleichzeitig kann ein Problem mit der folgenden Beschreibung auftreten. Ich werde einen überarbeiteten Artikel schreiben.
Python ist wirklich praktisch. Für mich ist das eine Akademie (Lehrling), deren Schwerpunkt auf der Simulation und Analyse der Ergebnisse liegt. Ein IPython-Notizbuch, mit dem Sie Daten nahtlos interaktiv verarbeiten, analysieren und visualisieren können, ist ein Muss. Leider bietet C ++ allein (wahrscheinlich) nicht die entsprechende Funktionalität. Das von CERN erstellte Festhalten an ROOT scheint in der Lage zu sein, C ++ interaktiv auszuführen, aber ich habe es nie verwendet, da es nur wenige Informationen gibt. Bitte erstellen Sie ein IC ++ - Notizbuch, das jemand (dringend) in C ++ programmieren kann.
Die Simulation selbst läuft jedoch Tage bis Wochen, Geschwindigkeit ist wichtig und Python kann nicht in einer langsamen Sprache geschrieben werden. Die Simulation wird also in C ++ geschrieben (was ist Fortran so lecker).
Auf der anderen Seite sind Projekte wie scikit-learn und PyMC Frontends für Python. Wenn Sie sich den Ort ansehen, an dem Ich bin der Meinung, dass es vorerst üblicher wird, es in einer anderen Sprache zu implementieren und von Python aus zu verwenden. Also unter den Methoden zum Schreiben von Funktionen, die in Python in C ++ verwendet werden können So verwenden Sie Boost.NumPy.
Ich habe einmal versucht, Boost.Python zu verwenden und war frustriert, weil der Konverter nicht verstanden hat Boost.NumPy war sehr einfach zu bedienen (wichtig).
Um die anderen Optionen kurz zu erwähnen:
Boost.NumPy, das dieses Mal eingeführt wurde, verwendet einfach die Methode zum Erstellen des C ++ - Wrappings von Pythons numpy.ndarray
.
Da C ++ bereits (unzählige) Bibliotheken hat, die lineare algebraische Operationen wie Eigen ausführen
Es gibt auch eine Auswahl, die numpy.ndarray
unverändert in den mehrdimensionalen Eigenvektor konvertiert.
Einfacher, wenn Sie nur Boost.Python verwenden, konvertieren Sie C ++ vector
etc. in eine Python-Liste.
Sie haben auch die Möglichkeit, dann in "numpy.ndarray" zu konvertieren.
Es gibt viele andere wie SWIG und Cython.
Ich habe in Vorheriger Artikel zusammengefasst, wie es installiert und kompiliert wird. Dieses Mal werde ich zusammenfassen, wie es tatsächlich verwendet wird. Normalerweise möchte ich also nur den Schnittstellenteil von C ++ - Code von der Python-Seite verwenden Ich werde zusammenfassen, wie man es so einfach wie möglich benutzt und dabei schwierige Dinge vermeidet.
Ich werde Boost.Python nicht erklären, also überprüfen Sie es bitte selbst. Unten finden Sie eine Liste der Boost.Python-Kommentarseiten:
Boost.Python (japanische Übersetzung) Honke (japanische Übersetzung)
Tutorial for Boost.NumPy
Folgendes habe ich basierend auf dem Boost.NumPy-Tutorial geschrieben. Um die Komplexität der Notation zu beseitigen, wird der Namespace wie folgt abgekürzt.
namespace p = boost::python;
namespace np = boost::numpy;
np :: ndarray
benutzt (für eindimensional)Die Einführung war lang, also fügen wir einen Code ein, der schnell funktioniert:
mymod1.cpp
#include "boost/numpy.hpp"
#include <stdexcept>
#include <algorithm>
namespace p = boost::python;
namespace np = boost::numpy;
/*Doppelt*/
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);
}
Dies kann so wie es ist kompiliert werden. Es ist eine einfache Funktion, die ein eindimensionales Array nur verdoppelt, aber wichtige Elemente enthält.
--Verwenden Sie get_nd () für Array-Dimensionen
shape (n)
für die Größe des Arraysget_dtype ()
erhaltenstd :: runtime_error
wird auf der Python-Seite in RuntimeError
konvertiertDer Speicher wird auf der Seite "np :: ndarray" verwaltet, sodass Sie sich dessen nicht bewusst sein müssen. Leider wird der Typ dynamisch bestimmt, sodass die Funktion nicht überladen werden kann. Es muss nach der if-Anweisung zum Zeitpunkt der Ausführung beurteilt werden. Es ist schön, Ausnahmen zu konvertieren.
Beginnen wir mit 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)
Der b-Teil ist ein Fehler, da der Typ "long long" anstelle von "double" ist, wie oben beschrieben:
[ 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
Wenn Sie eine Ganzzahl für numpy verwenden, wird "int64" ("long long" in C ++) verwendet, sofern nicht anders angegeben. Bitte beachten Sie, dass es nicht "int" ist.
Es gibt einige Beschwerden wie nicht überladen zu können, Ich konnte eine Funktion implementieren, die in Python in C ++ leicht verwendet werden kann.
np :: ndarray
(für mehrdimensionale)Die Grundlagen sind für mehrdimensionale Dateien gleich, Sie müssen jedoch auf die Reihenfolge des Speichers achten. Die Erklärung von numpy.ndarray.stride ist leicht zu verstehen, aber unten Ich werde es kurz erklären.
Manchmal möchten Sie ein zweidimensionales Array in einem kontinuierlichen Speicherbereich verwalten.
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];
}
Auf diese Weise ist der von "a_as2 [i] [j]" aufgerufene Speicher derselbe wie "a_as1 [i * M + j]". Die Art und Weise, so etwas zu tun, ist "ndarray.stride". In diesem Fall rückt es im Speicher um 8 Bytes vor, um 1 in j-Richtung vorzurücken (double ist 8 Bytes). Um andererseits 1 in die i-Richtung vorzurücken, wird "8 * M" Byte im Speicher vorgerückt ("(i + 1) * M + j = i * M + j + M"). Diese "8" und "8 * M" werden Schritt (Schritt) genannt. Diese Idee kann auch in höheren Dimensionen verwendet werden.
Wenn Sie darauf achten, ist der Rest einfach.
#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);
}
Es ist schneller, den Schritt zu betrachten und ihn in aufsteigender Reihenfolge in Bezug auf den Speicherzugriff zu drehen. Ich habe es weggelassen, weil es problematisch ist. Bitte gib dein bestes.
Python oder NumPy ist sehr praktisch. Höchstwahrscheinlich bietet SciPy eine Standardschnittstelle für Mathematikbibliotheken mit gut dokumentierten.
Nachdem wir nun zusammengefasst haben, wie auf die Daten in np :: ndarray
zugegriffen werden kann,
Ich denke, dass der in C ++ implementierte Algorithmus von Python aus verwendet werden kann.
Als nächstes möchte ich mich über Beiträge zu PyPI und Beiträge zu scikits informieren.
Recommended Posts