[DEV] add v1.66.0

This commit is contained in:
2018-01-12 21:47:58 +01:00
parent 87059bb1af
commit a97e9ae7d4
49032 changed files with 7668950 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
# Copyright Stefan Seefeld 2016.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import python ;
if ! [ python.configured ]
{
ECHO "warning: no Python configured in user-config.jam" ;
ECHO "warning: will use default configuration" ;
using python ;
}
# Adjust the following if Boost.Python isn't installed in a default location
lib boost_python ;
project
: requirements
# <include>/path/to/boost/python
<library>boost_python
;
rule run-test ( test-name : sources + )
{
import testing ;
testing.make-test run-pyd : $(sources) : : $(test-name) ;
}
build-project quickstart ;
build-project tutorial ;
if [ python.numpy ]
{
build-project numpy ;
}

View File

@@ -0,0 +1,11 @@
![logo](https://raw.githubusercontent.com/boostorg/python/develop/doc/images/bpl.png)
# Examples
This directory contains various examples using Boost.Python.
You may compile these using the `bjam` command either in this directory
or in any of the subdirectories.
You may need to adjust the paths in the Jamroot file if Boost.Python
is not installed in a default location.
See http://boostorg.github.io/python/doc/html/building/no_install_quickstart.html
for details.

View File

@@ -0,0 +1,29 @@
# Copyright Stefan Seefeld 2016.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import python ;
# Adjust the following if Boost.Python isn't installed in a default location
lib boost_numpy
:
: <search>/usr/local/Boost/lib
<include>/usr/local/Boost/include
;
project numpy
: requirements
<include>/usr/local/Boost/include
<library>boost_numpy
<location>.
;
exe simple : simple.cpp boost_numpy /python//python ;
exe dtype : dtype.cpp boost_numpy /python//python ;
exe ndarray : ndarray.cpp /python//python ;
exe fromdata : fromdata.cpp /python//python ;
exe ufunc : ufunc.cpp /python//python ;
exe wrap : wrap.cpp /python//python ;
python-extension gaussian : gaussian.cpp ;

View File

@@ -0,0 +1,37 @@
# Copyright Jim Bosch 2010-2012.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import numpy
import gaussian
mu = numpy.zeros(2, dtype=float)
sigma = numpy.identity(2, dtype=float)
sigma[0, 1] = 0.15
sigma[1, 0] = 0.15
g = gaussian.bivariate_gaussian(mu, sigma)
r = numpy.linspace(-40, 40, 1001)
x, y = numpy.meshgrid(r, r)
z = g(x, y)
s = z.sum() * (r[1] - r[0])**2
print "sum (should be ~ 1):", s
xc = (z * x).sum() / z.sum()
print "x centroid (should be ~ %f): %f" % (mu[0], xc)
yc = (z * y).sum() / z.sum()
print "y centroid (should be ~ %f): %f" % (mu[1], yc)
xx = (z * (x - xc)**2).sum() / z.sum()
print "xx moment (should be ~ %f): %f" % (sigma[0,0], xx)
yy = (z * (y - yc)**2).sum() / z.sum()
print "yy moment (should be ~ %f): %f" % (sigma[1,1], yy)
xy = 0.5 * (z * (x - xc) * (y - yc)).sum() / z.sum()
print "xy moment (should be ~ %f): %f" % (sigma[0,1], xy)

View File

@@ -0,0 +1,49 @@
// Copyright Ankit Daftery 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
/**
* @brief An example to show how to create ndarrays with built-in python data types, and extract
* the types and values of member variables
*
* @todo Add an example to show type conversion.
* Add an example to show use of user-defined types
*
*/
#include <boost/python/numpy.hpp>
#include <iostream>
namespace p = boost::python;
namespace np = boost::python::numpy;
int main(int argc, char **argv)
{
// Initialize the Python runtime.
Py_Initialize();
// Initialize NumPy
np::initialize();
// Create a 3x3 shape...
p::tuple shape = p::make_tuple(3, 3);
// ...as well as a type for C++ double
np::dtype dtype = np::dtype::get_builtin<double>();
// Construct an array with the above shape and type
np::ndarray a = np::zeros(shape, dtype);
// Print the array
std::cout << "Original array:\n" << p::extract<char const *>(p::str(a)) << std::endl;
// Print the datatype of the elements
std::cout << "Datatype is:\n" << p::extract<char const *>(p::str(a.get_dtype())) << std::endl ;
// Using user defined dtypes to create dtype and an array of the custom dtype
// First create a tuple with a variable name and its dtype, double, to create a custom dtype
p::tuple for_custom_dtype = p::make_tuple("ha",dtype) ;
// The list needs to be created, because the constructor to create the custom dtype
// takes a list of (variable,variable_type) as an argument
p::list list_for_dtype ;
list_for_dtype.append(for_custom_dtype) ;
// Create the custom dtype
np::dtype custom_dtype = np::dtype(list_for_dtype) ;
// Create an ndarray with the custom dtype
np::ndarray new_array = np::zeros(shape,custom_dtype);
}

View File

@@ -0,0 +1,48 @@
// Copyright Ankit Daftery 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
/**
* @brief An example to show how to access data using raw pointers. This shows that you can use and
* manipulate data in either Python or C++ and have the changes reflected in both.
*/
#include <boost/python/numpy.hpp>
#include <iostream>
namespace p = boost::python;
namespace np = boost::python::numpy;
int main(int argc, char **argv)
{
// Initialize the Python runtime.
Py_Initialize();
// Initialize NumPy
np::initialize();
// Create an array in C++
int arr[] = {1,2,3,4} ;
// Create the ndarray in Python
np::ndarray py_array = np::from_data(arr, np::dtype::get_builtin<int>() , p::make_tuple(4), p::make_tuple(4), p::object());
// Print the ndarray that we just created, and the source C++ array
std::cout << "C++ array :" << std::endl ;
for (int j=0;j<4;j++)
{
std::cout << arr[j] << ' ' ;
}
std::cout << std::endl << "Python ndarray :" << p::extract<char const *>(p::str(py_array)) << std::endl;
// Change an element in the python ndarray
py_array[1] = 5 ;
// And see if the C++ container is changed or not
std::cout << "Is the change reflected in the C++ array used to create the ndarray ? " << std::endl ;
for (int j = 0;j<4 ; j++)
{
std::cout << arr[j] << ' ' ;
}
// Conversely, change it in C++
arr[2] = 8 ;
// And see if the changes are reflected in the Python ndarray
std::cout << std::endl << "Is the change reflected in the Python ndarray ?" << std::endl << p::extract<char const *>(p::str(py_array)) << std::endl;
}

View File

@@ -0,0 +1,315 @@
// Copyright Jim Bosch 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/python/numpy.hpp>
#include <cmath>
#include <memory>
#ifndef M_PI
#include <boost/math/constants/constants.hpp>
const double M_PI = boost::math::constants::pi<double>();
#endif
namespace bp = boost::python;
namespace bn = boost::python::numpy;
/**
* A 2x2 matrix class, purely for demonstration purposes.
*
* Instead of wrapping this class with Boost.Python, we'll convert it to/from numpy.ndarray.
*/
class matrix2 {
public:
double & operator()(int i, int j) {
return _data[i*2 + j];
}
double const & operator()(int i, int j) const {
return _data[i*2 + j];
}
double const * data() const { return _data; }
private:
double _data[4];
};
/**
* A 2-element vector class, purely for demonstration purposes.
*
* Instead of wrapping this class with Boost.Python, we'll convert it to/from numpy.ndarray.
*/
class vector2 {
public:
double & operator[](int i) {
return _data[i];
}
double const & operator[](int i) const {
return _data[i];
}
double const * data() const { return _data; }
vector2 operator+(vector2 const & other) const {
vector2 r;
r[0] = _data[0] + other[0];
r[1] = _data[1] + other[1];
return r;
}
vector2 operator-(vector2 const & other) const {
vector2 r;
r[0] = _data[0] - other[0];
r[1] = _data[1] - other[1];
return r;
}
private:
double _data[2];
};
/**
* Matrix-vector multiplication.
*/
vector2 operator*(matrix2 const & m, vector2 const & v) {
vector2 r;
r[0] = m(0, 0) * v[0] + m(0, 1) * v[1];
r[1] = m(1, 0) * v[0] + m(1, 1) * v[1];
return r;
}
/**
* Vector inner product.
*/
double dot(vector2 const & v1, vector2 const & v2) {
return v1[0] * v2[0] + v1[1] * v2[1];
}
/**
* This class represents a simple 2-d Gaussian (Normal) distribution, defined by a
* mean vector 'mu' and a covariance matrix 'sigma'.
*/
class bivariate_gaussian {
public:
vector2 const & get_mu() const { return _mu; }
matrix2 const & get_sigma() const { return _sigma; }
/**
* Evaluate the density of the distribution at a point defined by a two-element vector.
*/
double operator()(vector2 const & p) const {
vector2 u = _cholesky * (p - _mu);
return 0.5 * _cholesky(0, 0) * _cholesky(1, 1) * std::exp(-0.5 * dot(u, u)) / M_PI;
}
/**
* Evaluate the density of the distribution at an (x, y) point.
*/
double operator()(double x, double y) const {
vector2 p;
p[0] = x;
p[1] = y;
return operator()(p);
}
/**
* Construct from a mean vector and covariance matrix.
*/
bivariate_gaussian(vector2 const & mu, matrix2 const & sigma)
: _mu(mu), _sigma(sigma), _cholesky(compute_inverse_cholesky(sigma))
{}
private:
/**
* This evaluates the inverse of the Cholesky factorization of a 2x2 matrix;
* it's just a shortcut in evaluating the density.
*/
static matrix2 compute_inverse_cholesky(matrix2 const & m) {
matrix2 l;
// First do cholesky factorization: l l^t = m
l(0, 0) = std::sqrt(m(0, 0));
l(0, 1) = m(0, 1) / l(0, 0);
l(1, 1) = std::sqrt(m(1, 1) - l(0,1) * l(0,1));
// Now do forward-substitution (in-place) to invert:
l(0, 0) = 1.0 / l(0, 0);
l(1, 0) = l(0, 1) = -l(0, 1) / l(1, 1);
l(1, 1) = 1.0 / l(1, 1);
return l;
}
vector2 _mu;
matrix2 _sigma;
matrix2 _cholesky;
};
/*
* We have a two options for wrapping get_mu and get_sigma into NumPy-returning Python methods:
* - we could deep-copy the data, making totally new NumPy arrays;
* - we could make NumPy arrays that point into the existing memory.
* The latter is often preferable, especially if the arrays are large, but it's dangerous unless
* the reference counting is correct: the returned NumPy array needs to hold a reference that
* keeps the memory it points to from being deallocated as long as it is alive. This is what the
* "owner" argument to from_data does - the NumPy array holds a reference to the owner, keeping it
* from being destroyed.
*
* Note that this mechanism isn't completely safe for data members that can have their internal
* storage reallocated. A std::vector, for instance, can be invalidated when it is resized,
* so holding a Python reference to a C++ class that holds a std::vector may not be a guarantee
* that the memory in the std::vector will remain valid.
*/
/**
* These two functions are custom wrappers for get_mu and get_sigma, providing the shallow-copy
* conversion with reference counting described above.
*
* It's also worth noting that these return NumPy arrays that cannot be modified in Python;
* the const overloads of vector::data() and matrix::data() return const references,
* and passing a const pointer to from_data causes NumPy's 'writeable' flag to be set to false.
*/
static bn::ndarray py_get_mu(bp::object const & self) {
vector2 const & mu = bp::extract<bivariate_gaussian const &>(self)().get_mu();
return bn::from_data(
mu.data(),
bn::dtype::get_builtin<double>(),
bp::make_tuple(2),
bp::make_tuple(sizeof(double)),
self
);
}
static bn::ndarray py_get_sigma(bp::object const & self) {
matrix2 const & sigma = bp::extract<bivariate_gaussian const &>(self)().get_sigma();
return bn::from_data(
sigma.data(),
bn::dtype::get_builtin<double>(),
bp::make_tuple(2, 2),
bp::make_tuple(2 * sizeof(double), sizeof(double)),
self
);
}
/**
* To allow the constructor to work, we need to define some from-Python converters from NumPy arrays
* to the matrix/vector types. The rvalue-from-python functionality is not well-documented in Boost.Python
* itself; you can learn more from boost/python/converter/rvalue_from_python_data.hpp.
*/
/**
* We start with two functions that just copy a NumPy array into matrix/vector objects. These will be used
* in the templated converted below. The first just uses the operator[] overloads provided by
* bp::object.
*/
static void copy_ndarray_to_mv2(bn::ndarray const & array, vector2 & vec) {
vec[0] = bp::extract<double>(array[0]);
vec[1] = bp::extract<double>(array[1]);
}
/**
* Here, we'll take the alternate approach of using the strides to access the array's memory directly.
* This can be much faster for large arrays.
*/
static void copy_ndarray_to_mv2(bn::ndarray const & array, matrix2 & mat) {
// Unfortunately, get_strides() can't be inlined, so it's best to call it once up-front.
Py_intptr_t const * strides = array.get_strides();
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
mat(i, j) = *reinterpret_cast<double const *>(array.get_data() + i * strides[0] + j * strides[1]);
}
}
}
/**
* Here's the actual converter. Because we've separated the differences into the above functions,
* we can write a single template class that works for both matrix2 and vector2.
*/
template <typename T, int N>
struct mv2_from_python {
/**
* Register the converter.
*/
mv2_from_python() {
bp::converter::registry::push_back(
&convertible,
&construct,
bp::type_id< T >()
);
}
/**
* Test to see if we can convert this to the desired type; if not return zero.
* If we can convert, returned pointer can be used by construct().
*/
static void * convertible(PyObject * p) {
try {
bp::object obj(bp::handle<>(bp::borrowed(p)));
std::auto_ptr<bn::ndarray> array(
new bn::ndarray(
bn::from_object(obj, bn::dtype::get_builtin<double>(), N, N, bn::ndarray::V_CONTIGUOUS)
)
);
if (array->shape(0) != 2) return 0;
if (N == 2 && array->shape(1) != 2) return 0;
return array.release();
} catch (bp::error_already_set & err) {
bp::handle_exception();
return 0;
}
}
/**
* Finish the conversion by initializing the C++ object into memory prepared by Boost.Python.
*/
static void construct(PyObject * obj, bp::converter::rvalue_from_python_stage1_data * data) {
// Extract the array we passed out of the convertible() member function.
std::auto_ptr<bn::ndarray> array(reinterpret_cast<bn::ndarray*>(data->convertible));
// Find the memory block Boost.Python has prepared for the result.
typedef bp::converter::rvalue_from_python_storage<T> storage_t;
storage_t * storage = reinterpret_cast<storage_t*>(data);
// Use placement new to initialize the result.
T * m_or_v = new (storage->storage.bytes) T();
// Fill the result with the values from the NumPy array.
copy_ndarray_to_mv2(*array, *m_or_v);
// Finish up.
data->convertible = storage->storage.bytes;
}
};
BOOST_PYTHON_MODULE(gaussian) {
bn::initialize();
// Register the from-python converters
mv2_from_python< vector2, 1 >();
mv2_from_python< matrix2, 2 >();
typedef double (bivariate_gaussian::*call_vector)(vector2 const &) const;
bp::class_<bivariate_gaussian>("bivariate_gaussian", bp::init<bivariate_gaussian const &>())
// Declare the constructor (wouldn't work without the from-python converters).
.def(bp::init< vector2 const &, matrix2 const & >())
// Use our custom reference-counting getters
.add_property("mu", &py_get_mu)
.add_property("sigma", &py_get_sigma)
// First overload accepts a two-element array argument
.def("__call__", (call_vector)&bivariate_gaussian::operator())
// This overload works like a binary NumPy universal function: you can pass
// in scalars or arrays, and the C++ function will automatically be called
// on each element of an array argument.
.def("__call__", bn::binary_ufunc<bivariate_gaussian,double,double,double>::make())
;
}

View File

@@ -0,0 +1,71 @@
// Copyright Ankit Daftery 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
/**
* @brief An example to show how to create ndarrays using arbitrary Python sequences.
*
* The Python sequence could be any object whose __array__ method returns an array, or any
* (nested) sequence. This example also shows how to create arrays using both unit and
* non-unit strides.
*/
#include <boost/python/numpy.hpp>
#include <iostream>
namespace p = boost::python;
namespace np = boost::python::numpy;
#if _MSC_VER
using boost::uint8_t;
#endif
int main(int argc, char **argv)
{
// Initialize the Python runtime.
Py_Initialize();
// Initialize NumPy
np::initialize();
// Create an ndarray from a simple tuple
p::object tu = p::make_tuple('a','b','c') ;
np::ndarray example_tuple = np::array (tu) ;
// and from a list
p::list l ;
np::ndarray example_list = np::array (l) ;
// Optionally, you can also specify a dtype
np::dtype dt = np::dtype::get_builtin<int>();
np::ndarray example_list1 = np::array (l,dt);
// You can also create an array by supplying data.First,create an integer array
int data[] = {1,2,3,4} ;
// Create a shape, and strides, needed by the function
p::tuple shape = p::make_tuple(4) ;
p::tuple stride = p::make_tuple(4) ;
// The function also needs an owner, to keep track of the data array passed. Passing none is dangerous
p::object own ;
// The from_data function takes the data array, datatype,shape,stride and owner as arguments
// and returns an ndarray
np::ndarray data_ex = np::from_data(data,dt,shape,stride,own);
// Print the ndarray we created
std::cout << "Single dimensional array ::" << std::endl << p::extract < char const * > (p::str(data_ex)) << std::endl ;
// Now lets make an 3x2 ndarray from a multi-dimensional array using non-unit strides
// First lets create a 3x4 array of 8-bit integers
uint8_t mul_data[][4] = {{1,2,3,4},{5,6,7,8},{1,3,5,7}};
// Now let's create an array of 3x2 elements, picking the first and third elements from each row
// For that, the shape will be 3x2
shape = p::make_tuple(3,2) ;
// The strides will be 4x2 i.e. 4 bytes to go to the next desired row, and 2 bytes to go to the next desired column
stride = p::make_tuple(4,2) ;
// Get the numpy dtype for the built-in 8-bit integer data type
np::dtype dt1 = np::dtype::get_builtin<uint8_t>();
// First lets create and print out the ndarray as is
np::ndarray mul_data_ex = np::from_data(mul_data,dt1, p::make_tuple(3,4),p::make_tuple(4,1),p::object());
std::cout << "Original multi dimensional array :: " << std::endl << p::extract < char const * > (p::str(mul_data_ex)) << std::endl ;
// Now create the new ndarray using the shape and strides
mul_data_ex = np::from_data(mul_data,dt1, shape,stride,p::object());
// Print out the array we created using non-unit strides
std::cout << "Selective multidimensional array :: "<<std::endl << p::extract < char const * > (p::str(mul_data_ex)) << std::endl ;
}

View File

@@ -0,0 +1,32 @@
// Copyright 2011 Stefan Seefeld.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/python/numpy.hpp>
#include <iostream>
namespace p = boost::python;
namespace np = boost::python::numpy;
int main(int argc, char **argv)
{
// Initialize the Python runtime.
Py_Initialize();
// Initialize NumPy
np::initialize();
// Create a 3x3 shape...
p::tuple shape = p::make_tuple(3, 3);
// ...as well as a type for C++ float
np::dtype dtype = np::dtype::get_builtin<float>();
// Construct an array with the above shape and type
np::ndarray a = np::zeros(shape, dtype);
// Construct an empty array with the above shape and dtype as well
np::ndarray b = np::empty(shape,dtype);
// Print the array
std::cout << "Original array:\n" << p::extract<char const *>(p::str(a)) << std::endl;
// Reshape the array into a 1D array
a = a.reshape(p::make_tuple(9));
// Print it again.
std::cout << "Reshaped array:\n" << p::extract<char const *>(p::str(a)) << std::endl;
}

View File

@@ -0,0 +1,86 @@
// Copyright Ankit Daftery 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
/**
* @brief An example to demonstrate use of universal functions or ufuncs
*
*
* @todo Calling the overloaded () operator is in a roundabout manner, find a simpler way
* None of the methods like np::add, np::multiply etc are supported as yet
*/
#include <boost/python/numpy.hpp>
#include <iostream>
namespace p = boost::python;
namespace np = boost::python::numpy;
// Create the structs necessary to implement the ufuncs
// The typedefs *must* be made
struct UnarySquare
{
typedef double argument_type;
typedef double result_type;
double operator()(double r) const { return r * r;}
};
struct BinarySquare
{
typedef double first_argument_type;
typedef double second_argument_type;
typedef double result_type;
double operator()(double a,double b) const { return (a*a + b*b) ; }
};
int main(int argc, char **argv)
{
// Initialize the Python runtime.
Py_Initialize();
// Initialize NumPy
np::initialize();
// Expose the struct UnarySquare to Python as a class, and let ud be the class object
p::object ud = p::class_<UnarySquare, boost::shared_ptr<UnarySquare> >("UnarySquare")
.def("__call__", np::unary_ufunc<UnarySquare>::make());
// Let inst be an instance of the class ud
p::object inst = ud();
// Use the "__call__" method to call the overloaded () operator and print the value
std::cout << "Square of unary scalar 1.0 is " << p::extract <char const * > (p::str(inst.attr("__call__")(1.0))) << std::endl ;
// Create an array in C++
int arr[] = {1,2,3,4} ;
// ..and use it to create the ndarray in Python
np::ndarray demo_array = np::from_data(arr, np::dtype::get_builtin<int>() , p::make_tuple(4), p::make_tuple(4), p::object());
// Print out the demo array
std::cout << "Demo array is " << p::extract <char const * > (p::str(demo_array)) << std::endl ;
// Call the "__call__" method to perform the operation and assign the value to result_array
p::object result_array = inst.attr("__call__")(demo_array) ;
// Print the resultant array
std::cout << "Square of demo array is " << p::extract <char const * > (p::str(result_array)) << std::endl ;
// Lets try the same with a list
p::list li ;
li.append(3);
li.append(7);
// Print out the demo list
std::cout << "Demo list is " << p::extract <char const * > (p::str(li)) << std::endl ;
// Call the ufunc for the list
result_array = inst.attr("__call__")(li) ;
// And print the list out
std::cout << "Square of demo list is " << p::extract <char const * > (p::str(result_array)) << std::endl ;
// Now lets try Binary ufuncs
// Expose the struct BinarySquare to Python as a class, and let ud be the class object
ud = p::class_<BinarySquare, boost::shared_ptr<BinarySquare> >("BinarySquare")
.def("__call__", np::binary_ufunc<BinarySquare>::make());
// Again initialise inst as an instance of the class ud
inst = ud();
// Print the two input listsPrint the two input lists
std::cout << "The two input list for binary ufunc are " << std::endl << p::extract <char const * > (p::str(demo_array)) << std::endl << p::extract <char const * > (p::str(demo_array)) << std::endl ;
// Call the binary ufunc taking demo_array as both inputs
result_array = inst.attr("__call__")(demo_array,demo_array) ;
std::cout << "Square of list with binary ufunc is " << p::extract <char const * > (p::str(result_array)) << std::endl ;
}

View File

@@ -0,0 +1,135 @@
// Copyright Jim Bosch 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
/**
* A simple example showing how to wrap a couple of C++ functions that
* operate on 2-d arrays into Python functions that take NumPy arrays
* as arguments.
*
* If you find have a lot of such functions to wrap, you may want to
* create a C++ array type (or use one of the many existing C++ array
* libraries) that maps well to NumPy arrays and create Boost.Python
* converters. There's more work up front than the approach here,
* but much less boilerplate per function. See the "Gaussian" example
* included with Boost.NumPy for an example of custom converters, or
* take a look at the "ndarray" project on GitHub for a more complete,
* high-level solution.
*
* Note that we're using embedded Python here only to make a convenient
* self-contained example; you could just as easily put the wrappers
* in a regular C++-compiled module and imported them in regular
* Python. Again, see the Gaussian demo for an example.
*/
#include <boost/python/numpy.hpp>
#include <boost/scoped_array.hpp>
#include <iostream>
namespace p = boost::python;
namespace np = boost::python::numpy;
// This is roughly the most efficient way to write a C/C++ function that operates
// on a 2-d NumPy array - operate directly on the array by incrementing a pointer
// with the strides.
void fill1(double * array, int rows, int cols, int row_stride, int col_stride) {
double * row_iter = array;
double n = 0.0; // just a counter we'll fill the array with.
for (int i = 0; i < rows; ++i, row_iter += row_stride) {
double * col_iter = row_iter;
for (int j = 0; j < cols; ++j, col_iter += col_stride) {
*col_iter = ++n;
}
}
}
// Here's a simple wrapper function for fill1. It requires that the passed
// NumPy array be exactly what we're looking for - no conversion from nested
// sequences or arrays with other data types, because we want to modify it
// in-place.
void wrap_fill1(np::ndarray const & array) {
if (array.get_dtype() != np::dtype::get_builtin<double>()) {
PyErr_SetString(PyExc_TypeError, "Incorrect array data type");
p::throw_error_already_set();
}
if (array.get_nd() != 2) {
PyErr_SetString(PyExc_TypeError, "Incorrect number of dimensions");
p::throw_error_already_set();
}
fill1(reinterpret_cast<double*>(array.get_data()),
array.shape(0), array.shape(1),
array.strides(0) / sizeof(double), array.strides(1) / sizeof(double));
}
// Another fill function that takes a double**. This is less efficient, because
// it's not the native NumPy data layout, but it's common enough in C/C++ that
// it's worth its own example. This time we don't pass the strides, and instead
// in wrap_fill2 we'll require the C_CONTIGUOUS bitflag, which guarantees that
// the column stride is 1 and the row stride is the number of columns. That
// restricts the arrays that can be passed to fill2 (it won't work on most
// subarray views or transposes, for instance).
void fill2(double ** array, int rows, int cols) {
double n = 0.0; // just a counter we'll fill the array with.
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
array[i][j] = ++n;
}
}
}
// Here's the wrapper for fill2; it's a little more complicated because we need
// to check the flags and create the array of pointers.
void wrap_fill2(np::ndarray const & array) {
if (array.get_dtype() != np::dtype::get_builtin<double>()) {
PyErr_SetString(PyExc_TypeError, "Incorrect array data type");
p::throw_error_already_set();
}
if (array.get_nd() != 2) {
PyErr_SetString(PyExc_TypeError, "Incorrect number of dimensions");
p::throw_error_already_set();
}
if (!(array.get_flags() & np::ndarray::C_CONTIGUOUS)) {
PyErr_SetString(PyExc_TypeError, "Array must be row-major contiguous");
p::throw_error_already_set();
}
double * iter = reinterpret_cast<double*>(array.get_data());
int rows = array.shape(0);
int cols = array.shape(1);
boost::scoped_array<double*> ptrs(new double*[rows]);
for (int i = 0; i < rows; ++i, iter += cols) {
ptrs[i] = iter;
}
fill2(ptrs.get(), array.shape(0), array.shape(1));
}
BOOST_PYTHON_MODULE(example) {
np::initialize(); // have to put this in any module that uses Boost.NumPy
p::def("fill1", wrap_fill1);
p::def("fill2", wrap_fill2);
}
int main(int argc, char **argv)
{
// This line makes our module available to the embedded Python intepreter.
# if PY_VERSION_HEX >= 0x03000000
PyImport_AppendInittab("example", &PyInit_example);
# else
PyImport_AppendInittab("example", &initexample);
# endif
// Initialize the Python runtime.
Py_Initialize();
PyRun_SimpleString(
"import example\n"
"import numpy\n"
"z1 = numpy.zeros((5,6), dtype=float)\n"
"z2 = numpy.zeros((4,3), dtype=float)\n"
"example.fill1(z1)\n"
"example.fill2(z2)\n"
"print z1\n"
"print z2\n"
);
Py_Finalize();
}

View File

@@ -0,0 +1,35 @@
# Copyright Stefan Seefeld 2016.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import python ;
import testing ;
project quickstart
: requirements
<location>.
;
# Declare a Python extension called hello.
python-extension extending : extending.cpp ;
# Declare an executable called embedding that embeds Python
exe embedding : embedding.cpp /python//python ;
# Declare a test of the extension module
testing.make-test run-pyd : extending test_extending.py : : test_ext ;
# Declare a test of the embedding application
testing.run embedding embedding.cpp
: # any ordinary arguments
: script.py # any arguments that should be treated as relative paths
: # requirements
: test_embed ; # name of test
# Create a "test" target that runs all the tests
alias test : test_ext test_embed ;
# make sure the tests don't run by default
explicit test_ext test_embed test ;

View File

@@ -0,0 +1,154 @@
// Copyright Stefan Seefeld 2005.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/python.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <iostream>
namespace python = boost::python;
// An abstract base class
class Base : public boost::noncopyable
{
public:
virtual ~Base() {};
virtual std::string hello() = 0;
};
// C++ derived class
class CppDerived : public Base
{
public:
virtual ~CppDerived() {}
virtual std::string hello() { return "Hello from C++!";}
};
// Familiar Boost.Python wrapper class for Base
struct BaseWrap : Base, python::wrapper<Base>
{
virtual std::string hello()
{
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
// workaround for VC++ 6.x or 7.0, see
// http://boost.org/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions
return python::call<std::string>(this->get_override("hello").ptr());
#else
return this->get_override("hello")();
#endif
}
};
// Pack the Base class wrapper into a module
BOOST_PYTHON_MODULE(embedded_hello)
{
python::class_<BaseWrap, boost::noncopyable> base("Base");
}
void exec_test()
{
std::cout << "registering extension module embedded_hello..." << std::endl;
// Register the module with the interpreter
if (PyImport_AppendInittab("embedded_hello", initembedded_hello) == -1)
throw std::runtime_error("Failed to add embedded_hello to the interpreter's "
"builtin modules");
std::cout << "defining Python class derived from Base..." << std::endl;
// Retrieve the main module
python::object main = python::import("__main__");
// Retrieve the main module's namespace
python::object global(main.attr("__dict__"));
// Define the derived class in Python.
python::object result = python::exec(
"from embedded_hello import * \n"
"class PythonDerived(Base): \n"
" def hello(self): \n"
" return 'Hello from Python!' \n",
global, global);
python::object PythonDerived = global["PythonDerived"];
// Creating and using instances of the C++ class is as easy as always.
CppDerived cpp;
BOOST_TEST(cpp.hello() == "Hello from C++!");
std::cout << "testing derived class from C++..." << std::endl;
// But now creating and using instances of the Python class is almost
// as easy!
python::object py_base = PythonDerived();
Base& py = python::extract<Base&>(py_base) BOOST_EXTRACT_WORKAROUND;
// Make sure the right 'hello' method is called.
BOOST_TEST(py.hello() == "Hello from Python!");
std::cout << "success!" << std::endl;
}
void exec_file_test(std::string const &script)
{
std::cout << "running file " << script << "..." << std::endl;
// Run a python script in an empty environment.
python::dict global;
python::object result = python::exec_file(script.c_str(), global, global);
// Extract an object the script stored in the global dictionary.
BOOST_TEST(python::extract<int>(global["number"]) == 42);
std::cout << "success!" << std::endl;
}
void exec_test_error()
{
std::cout << "intentionally causing a python exception..." << std::endl;
// Execute a statement that raises a python exception.
python::dict global;
python::object result = python::exec("print unknown \n", global, global);
std::cout << "Oops! This statement should be skipped due to an exception" << std::endl;
}
int main(int argc, char **argv)
{
BOOST_TEST(argc == 2);
std::string script = argv[1];
// Initialize the interpreter
Py_Initialize();
bool error_expected = false;
if (
python::handle_exception(exec_test)
|| python::handle_exception(boost::bind(exec_file_test, script))
|| (
(error_expected = true)
&& python::handle_exception(exec_test_error)
)
)
{
if (PyErr_Occurred())
{
if (!error_expected)
BOOST_ERROR("Python Error detected");
PyErr_Print();
}
else
{
BOOST_ERROR("A C++ exception was thrown for which "
"there was no exception translator registered.");
}
}
// Boost.Python doesn't support Py_Finalize yet, so don't call it!
return boost::report_errors();
}

View File

@@ -0,0 +1,41 @@
// Copyright Ralf W. Grosse-Kunstleve 2002-2004. Distributed under the Boost
// Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/python/class.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <iostream>
#include <string>
namespace { // Avoid cluttering the global namespace.
// A friendly class.
class hello
{
public:
hello(const std::string& country) { this->country = country; }
std::string greet() const { return "Hello from " + country; }
private:
std::string country;
};
// A function taking a hello object as an argument.
std::string invite(const hello& w) {
return w.greet() + "! Please come soon!";
}
}
BOOST_PYTHON_MODULE(extending)
{
using namespace boost::python;
class_<hello>("hello", init<std::string>())
// Add a regular member function.
.def("greet", &hello::greet)
// Add invite() as a member of hello!
.def("invite", invite)
;
// Also add invite() as a regular function to the module.
def("invite", invite);
}

View File

@@ -0,0 +1,7 @@
#! /usr/bin/env python
# Copyright Stefan Seefeld 2006. Distributed under the Boost
# Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
print('Hello World !')
number = 42

View File

@@ -0,0 +1,37 @@
#! /usr/bin/env python
# Copyright Ralf W. Grosse-Kunstleve 2006. Distributed under the Boost
# Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
# Using the doctest module here to ensure that the results are as expected.
r'''>>> from extending import *
>>> hi = hello('California')
>>> hi.greet()
'Hello from California'
>>> invite(hi)
'Hello from California! Please come soon!'
>>> hi.invite()
'Hello from California! Please come soon!'
>>> class wordy(hello):
... def greet(self):
... return hello.greet(self) + ', where the weather is fine'
...
>>> hi2 = wordy('Florida')
>>> hi2.greet()
'Hello from Florida, where the weather is fine'
>>> invite(hi2)
'Hello from Florida! Please come soon!'
'''
def run(args = None):
if args is not None:
import sys
sys.argv = args
import doctest, test_extending
return doctest.testmod(test_extending, verbose=True)
if __name__ == '__main__':
import sys
sys.exit(run()[0])

View File

@@ -0,0 +1,19 @@
# Copyright Stefan Seefeld 2016.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import python ;
project tutorial
: requirements
<location>.
;
python-extension hello_ext : hello.cpp ;
run-test hello : hello_ext hello.py ;
alias test : hello ;
explicit test ;

View File

@@ -0,0 +1,20 @@
// Copyright Joel de Guzman 2002-2004. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
// Hello World Example from the tutorial
// [Joel de Guzman 10/9/2002]
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
char const* greet()
{
return "hello, world";
}
BOOST_PYTHON_MODULE(hello_ext)
{
using namespace boost::python;
def("greet", greet);
}

View File

@@ -0,0 +1,8 @@
#! /usr/bin/env python
# Copyright Joel de Guzman 2002-2007. Distributed under the Boost
# Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt
# or copy at http://www.boost.org/LICENSE_1_0.txt)
# Hello World Example from the tutorial
import hello_ext
print(hello_ext.greet())