Robert Kern wrote:
> On Thu, Mar 14, 2013 at 11:00 AM, Neal Becker <[email protected]> wrote:
>> Robert Kern wrote:
>>
>>> On Wed, Mar 13, 2013 at 12:16 AM, Neal Becker <[email protected]> wrote:
>>>> I guess I talked to you about 100 years ago about sharing state between
>>>> numpy
>>>> rng and code I have in c++ that wraps boost::random. So is there a C-api
>>>> for
>>>> this RandomState object I could use to call from c++? Maybe I could do
>>>> something with that.
>>>
>>> There is not one currently. Cython has provisions for sharing such
>>> low-level access to other Cython extensions, but I'm not sure how well
>>> it works for exporting data pointers and function pointers to general
>>> C/++ code. We could probably package the necessities into a struct and
>>> export a pointer to it via a PyCapsule.
>>>
>>
>> One thing this code doesn't do: it requires construction of the wrapper class
>> passing in a RandomState object. It doesn't verify you actually gave it a
>> RandomState object. It's hard to do that. The problem as I see it is to
>> perform this check, I need the RandomStateType object, which unfortunately
>> mtrand.so does not export.
>>
>> The only way to do it is in c++ code:
>>
>> 1. import numpy.random
>> 2. get RandomState class
>> 3. call it to create RandomState instance
>> 4. get the ob_type pointer.
>>
>> Pretty ugly:
>>
>> object mod = object (handle<>
>> (borrowed((PyImport_ImportModule("numpy.random")))));
>> object rs_obj = mod.attr("RandomState");
>> object rs_inst = call<object> (rs_obj.ptr(), 0);
>> RandomStateTypeObj = rs_inst.ptr()->ob_type;
>
> PyObject_IsInstance() should be sufficient.
>
> http://docs.python.org/2/c-api/object.html#PyObject_IsInstance
>
> --
> Robert Kern
Thanks!
For the record, an updated version attached.
#include <boost/python.hpp>
// Include this AFTER Python.h; in projects with multiple source files, you may have to
// #define some things first (see the numpy C-API documentation).
#include <numpy/arrayobject.h>
#include <boost/numpy/dtype.hpp>
#include <boost/python/class.hpp>
#include <boost/python/init.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/args.hpp>
#include <boost/python/str.hpp>
#include <boost/python/call.hpp>
#include <boost/numpy.hpp>
#include <ndarray/Array.h>
#include <ndarray/bp/auto.h>
#include <inttypes.h>
#include <stdexcept>
using namespace boost::python;
namespace bp=boost::python;
namespace nd=ndarray;
#define RK_STATE_LEN 624
typedef struct rk_state_
{
unsigned long key[RK_STATE_LEN];
int pos;
int has_gauss; /* !=0: gauss contains a gaussian deviate */
double gauss;
/* The rk_state structure has been extended to store the following
* information for the binomial generator. If the input values of n or p
* are different than nsave and psave, then the other parameters will be
* recomputed. RTK 2005-09-02 */
int has_binomial; /* !=0: following parameters initialized for
binomial */
double psave;
long nsave;
double r;
double q;
double fm;
long m;
double p1;
double xm;
double xl;
double xr;
double c;
double laml;
double lamr;
double p2;
double p3;
double p4;
}
rk_state;
struct RandomState {
PyObject_HEAD
rk_state *internal_state;
};
extern "C" void rk_fill(void *buffer, size_t size, rk_state *state);
extern "C" unsigned long rk_ulong(rk_state *state);
extern "C" unsigned long rk_random(rk_state *state);
inline NPY_TYPES dtype_to_type (bp::object const& dtype, NPY_TYPES _default) {
if (dtype == bp::object())
return _default;
else {
boost::numpy::dtype dt (dtype);
return NPY_TYPES (reinterpret_cast<PyArray_Descr*>(dt.ptr())->type_num);
// PyArray_Descr *tp_descr;
// if (PyArray_DescrConverter(dtype.ptr(), &tp_descr) != NPY_SUCCEED)
// throw bp::error_already_set();
// return NPY_TYPES(tp_descr->type_num);
}
}
template<int> struct type_imap;
template<>
struct type_imap<NPY_INT8> {
typedef npy_int8 type;
};
template<>
struct type_imap<NPY_INT16> {
typedef npy_int16 type;
};
template<>
struct type_imap<NPY_INT32> {
typedef npy_int32 type;
};
template<>
struct type_imap<NPY_INT64> {
typedef npy_int64 type;
};
template<>
struct type_imap<NPY_FLOAT32> {
typedef float type;
};
template<>
struct type_imap<NPY_FLOAT64> {
typedef double type;
};
template<>
struct type_imap<NPY_COMPLEX64> {
typedef std::complex<float> type;
};
template<>
struct type_imap<NPY_COMPLEX128> {
typedef std::complex<double> type;
};
template <class IntType=uint32_t>
class pnseq_generator {
public:
typedef IntType result_type;
typedef IntType input_type;
explicit pnseq_generator(int _width) : width (_width), count (0),
mask (
_width == std::numeric_limits<result_type>::digits ? ~(result_type(0)) : ~(result_type(-1LL) << _width))
{
if (std::numeric_limits<result_type>::digits < width or width <= 0)
throw std::runtime_error ("pnseq_generator::invalid width");
}
explicit pnseq_generator (int _width, int _count, result_type _cache):
width (_width), count (_count), cache (_cache), mask (~(result_type(-1LL) << _width)) {}
template<class Engine>
result_type operator() (Engine& eng) {
// BOOST_STATIC_ASSERT (sizeof (typename Engine::result_type) >= width);
if (count <= width-1) {
cache = eng();
count = std::min (std::numeric_limits<typename Engine::result_type>::digits, std::numeric_limits<result_type>::digits);
}
result_type bits = cache & mask;
cache >>= width;
count -= width;
return bits;
}
// private:
const int width;
int count;
result_type cache;
const result_type mask;
};
static object rs_obj;
template<typename out_type=uint32_t>
struct engine {
typedef out_type result_type;
bp::object rs; // RandomState object
void verify (object const& o) {
if (!PyObject_IsInstance(o.ptr(), rs_obj.ptr()))
throw std::runtime_error ("engine: expected RandomState");
}
engine (bp::object const& _rs) : rs (_rs) { verify (_rs); }
result_type operator() () {
RandomState* r = (RandomState*)(rs.ptr());
// result_type buffer;
// rk_fill ((void*)&buffer, sizeof(buffer), r->internal_state);
if (sizeof(result_type) == sizeof (uint64_t))
return rk_ulong (r->internal_state);
else if (sizeof(result_type) == sizeof (uint32_t))
return rk_random (r->internal_state);
}
};
template<typename result_type=uint32_t>
struct wrapped_pn {
engine<result_type> eng;
pnseq_generator<result_type> pn;
wrapped_pn (bp::object const& _rs, int _width) :
eng (_rs), pn (_width) {}
// for pickle
wrapped_pn (bp::object const& _rs, int width, int count, typename pnseq_generator<result_type>::result_type cache) :
eng (_rs),
pn (width, count, cache) {}
typename pnseq_generator<result_type>::result_type generate1() { return pn (eng); }
template<typename T>
object generate_impl (npy_intp n) {
nd::Array<T,1,1> out (n);
auto i = boost::begin(out);
auto e = boost::end(out);
for (; i < e; ++i)
*i = generate1();
return object (out);
}
object generate (npy_intp size, object const& dtype) {
NPY_TYPES type = dtype_to_type (dtype, NPY_INT);
if (type == NPY_BYTE)
return generate_impl<type_imap<NPY_BYTE>::type> (size);
else if (type == NPY_SHORT)
return generate_impl<type_imap<NPY_SHORT>::type> (size);
else if (type == NPY_INT)
return generate_impl<type_imap<NPY_INT>::type> (size);
else if (type == NPY_LONG)
return generate_impl<type_imap<NPY_LONG>::type> (size);
else
throw std::runtime_error ("pn::generate bad dtype");
}
object get_rs() const { return eng.rs; }
void set_rs(bp::object const& rs) { eng.rs = rs; }
int get_width() const { return pn.width; }
};
struct pn_pickle_suite : bp::pickle_suite {
static bp::tuple getinitargs (wrapped_pn<uint32_t> const& gen) {
return bp::make_tuple (gen.eng.rs, gen.pn.width, gen.pn.count, gen.pn.cache);
}
};
template<typename int_t>
static void export_pn (const char* name) {
class_<wrapped_pn<int_t> > (name,
bp::init<bp::object, int>((bp::arg("rng"),
bp::arg("bits")
),
"__init__(r,bits)\n"
"Construct a pn-generator\n\n"
":Parameters:\n"
" `r` : rng\n"
" Mersenne Twister\n"
" `bits` : int\n"
" #bits\n"
)
)
.def (bp::init<bp::object,int,int,typename pnseq_generator<int_t>::result_type>()) // for pickle
.def ("__call__", &wrapped_pn<int_t>::generate,
bp::default_call_policies(),
(bp::arg ("size"),
bp::arg ("dtype")=object()),
"__call__(cnt[,dtype])\n"
"Without cnt, generate a single r.v.\n"
"With cnt generate a vector of r.v.'s.\n\n"
":Returns: a single r.v. or a vector of r.v.'s\n"
" The returned values will be placed in the lowest bits of an integer.\n"
)
.def ("__call__", &wrapped_pn<int_t>::generate1)
.add_property ("width", &wrapped_pn<int_t>::get_width)
// .def_readwrite ("eng", &wrapped_pn<int_t>::eng)
.add_property ("rs", &wrapped_pn<int_t>::get_rs, &wrapped_pn<int_t>::set_rs)
.def_pickle (pn_pickle_suite())
;
}
BOOST_PYTHON_MODULE (pn) {
boost::numpy::initialize();
import_array();
export_pn<uint64_t> ("pn64");
export_pn<uint32_t> ("pn32");
object mod = object (handle<> ((PyImport_ImportModule("numpy.random"))));
rs_obj = mod.attr("RandomState");
}
_______________________________________________
NumPy-Discussion mailing list
[email protected]
http://mail.scipy.org/mailman/listinfo/numpy-discussion