[PYTHON] Étendre NumPy avec Rust

Avantages de Python

Problèmes avec Python

--Python lui-même est lent


Efforts pour accélérer


Extensions Python et extensions NumPy

--Python C-API (extension CPython) - http://docs.python.jp/3/c-api/ --Définir les structures et fonctions qui peuvent être utilisées en Python en C

Vous devez utiliser les deux C-API


Dans quelle langue étendez-vous NumPy?


Pourquoi Rust?

--Rust est un langage système moderne adapté aux calculs numériques (je pense) --Borrow Checker élimine les courses de données --C ++ - comme la sémantique de déplacement de modèle de mémoire (pas de GC)


rust-cpython

https://github.com/dgrunwald/rust-cpython


Appeler Python depuis Rust

extern crate cpython;

use cpython::{Python, PyDict, PyResult};

fn main() {
    let gil = Python::acquire_gil();
    hello(gil.python()).unwrap();
}

fn hello(py: Python) -> PyResult<()> {
    let sys = py.import("sys")?;
    let version: String = sys.get(py, "version")?.extract(py)?;

    let locals = PyDict::new(py);
    locals.set_item(py, "os", py.import("os")?)?;
    let user: String = py.eval("os.getenv('USER') or os.getenv('USERNAME')", None, Some(&locals))?.extract(py)?;

    println!("Hello {}, I'm Python {}", user, version);
    Ok(())
}

Appel de fonctions Rust depuis Python

#[macro_use] extern crate cpython;

use cpython::{PyResult, Python};

// add bindings to the generated python module
// N.B: names: "librust2py" must be the name of the `.so` or `.pyd` file
py_module_initializer!(librust2py, initlibrust2py, PyInit_librust2py, |py, m| {
    try!(m.add(py, "__doc__", "This module is implemented in Rust."));
    try!(m.add(py, "sum_as_string", py_fn!(py, sum_as_string_py(a: i64, b:i64))));
    Ok(())
});

// logic implemented as a normal rust function
fn sum_as_string(a:i64, b:i64) -> String {
    format!("{}", a + b).to_string()
}

// rust-cpython aware function. All of our python interface could be
// declared in a separate module.
// Note that the py_fn!() macro automatically converts the arguments from
// Python objects to Rust values; and the Rust return value back into a Python object.
fn sum_as_string_py(_: Python, a:i64, b:i64) -> PyResult<String> {
    let out = sum_as_string(a, b);
    Ok(out)
}

rust-numpy

https://github.com/termoshtt/rust-numpy

--Fabriqué en GW (/ ・ ω ・) / --NumPy C-API publié du côté de Rust basé sur le python rust-c --Introduire PyArray correspondant à numpy.ndarray --Convertir en ʻArray` de rust-ndarray


rust-ndarray

https://github.com/bluss/rust-ndarray

extern crate ndarray;
use ndarray::*;

// immutable example
fn axpy(a: f64, x: ArrayViewD<f64>, y: ArrayViewD<f64>) -> ArrayD<f64> {
    a * &x + &y
}

// mutable example (no return)
fn mult(a: f64, mut x: ArrayViewMutD<f64>) {
    x *= a;
}

rust-numpy

#[macro_use]
extern crate cpython;
extern crate numpy;

use numpy::*;
use cpython::{PyResult, Python, PyObject};

// wrapper of `axpy`
fn axpy_py(py: Python, a: f64, x: PyArray, y: PyArray) -> PyResult<PyArray> {
    let np = PyArrayModule::import(py)?;
    let x = x.as_array().into_pyresult(py, "x must be f64 array")?;
    let y = y.as_array().into_pyresult(py, "y must be f64 array")?;
    Ok(axpy(a, x, y).into_pyarray(py, &np))
}

// wrapper of `mult`
fn mult_py(py: Python, a: f64, x: PyArray) -> PyResult<PyObject> {
    let x = x.as_array_mut().into_pyresult(py, "x must be f64 array")?;
    mult(a, x);
    Ok(py.None()) // Python function must returns
}

/* Define module "_rust_ext" */
py_module_initializer!(_rust_ext, init_rust_ext, PyInit__rust_ext, |py, m| {
    m.add(py, "__doc__", "Rust extension for NumPy")?;
    m.add(py, "axpy", py_fn!(py, axpy_py(a: f64, x: PyArray, y: PyArray)))?;
    m.add(py, "mult", py_fn!(py, mult_py(a: f64, x: PyArray)))?;
    Ok(())
});

setuptools-rust

--rust-c Une bibliothèque pour gérer les extensions python avec setuptools

setup.py
extensions/Cargo.toml
           src/lib.rs
rust_ext/__init__.py

setup.py


from setuptools import setup
from setuptools_rust import RustExtension

setup(name='rust_ext',
      version='1.0',
      rust_extensions=[
          RustExtension('rust_ext._rust_ext', 'extensions/Cargo.toml')],
      packages=['rust_ext'],
      zip_safe=False)

rust_ext/__init__.py


from ._rust_ext import *

Appeler rust-numpy

python setup.py install
import rust_ext
import numpy as np

x = np.array([1.0, 2.0])
y = np.array([2.0, 3.0])
rust_ext.axpy(3, x, y)

(* '▽') Cela a fonctionné!


Enfin: quand la rouille est douloureuse

Recommended Posts

Étendre NumPy avec Rust
Moyenne mobile avec numpy
[Rust / Python] Gérer numpy avec PyO3 (version d'août 2020)
Premiers pas avec Numpy
Concaténation de matrices avec Numpy
Code de bourdonnement avec numpy
Effectuer une analyse de régression avec NumPy
Régression du noyau avec Numpy uniquement
J'ai écrit GP avec numpy
Implémentation CNN avec juste numpy
Génération artificielle de données avec numpy
Essayez l'opération matricielle avec NumPy
Animation de l'équation de diffusion avec NumPy
Simulation de remboursement de dette avec numpy
Implémentation de SMO avec Python + NumPy
Coller les chaînes avec Numpy
Gérez les tableaux numpy avec f2py
Utilisez OpenBLAS avec numpy, scipy
Implémentation de la régression logistique avec NumPy
Utiliser une plage de type Python avec Rust
Effectuez un ajustement carré minimum avec numpy.
Dessinez un beau cercle avec numpy
Implémenter Keras LSTM feed forward avec numpy
Extraire plusieurs éléments avec le tableau Numpy
Lire et écrire des fichiers csv avec numpy
Créez un bac avec NumPy, obtenez la correspondance entre les données et le bac
Graphiques de fonctions triangulaires avec numpy et matplotlib
J'ai fait un jeu de vie avec Numpy
Lire le fichier de données de caractères avec numpy
Gérer numpy avec Cython (méthode par memoryview)
Utilisez BLAS / LAPACK multi-thread avec numpy / scipy