Skip to content

Commit

Permalink
Export ArrayProperty constructors to python. Refs #4399
Browse files Browse the repository at this point in the history
  • Loading branch information
martyngigg committed Apr 15, 2012
1 parent 906624c commit 7e55a90
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 20 deletions.
2 changes: 2 additions & 0 deletions Code/Mantid/Framework/PythonInterface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ set ( TEST_PY_FILES
test/python/AlgorithmFactoryTest.py
test/python/AlgorithmManagerTest.py
test/python/AnalysisDataServiceTest.py
test/python/ArrayPropertyTest.py
test/python/AxisTest.py
test/python/BoundedValidatorTest.py
test/python/ConfigServiceTest.py
Expand All @@ -96,6 +97,7 @@ set ( TEST_PY_FILES
test/python/LoggerTest.py
test/python/MatrixWorkspaceTest.py
test/python/MDHistoWorkspaceTest.py
test/python/NullValidatorTest.py
test/python/OrientedLatticeTest.py
test/python/PythonPluginsTest.py
test/python/PropertyWithValueTest.py
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,20 @@ set ( EXPORT_FILES
src/Exports/DataItem.cpp
src/Exports/IPropertyManager.cpp
src/Exports/Property.cpp
src/Exports/IValidator.cpp
src/Exports/PropertyWithValue.cpp
src/Exports/ArrayProperty.cpp
src/Exports/Quat.cpp
src/Exports/V3D.cpp
src/Exports/StlContainers.cpp
src/Exports/Logger.cpp
src/Exports/Unit.cpp
src/Exports/IValidator.cpp
src/Exports/BoundedValidator.cpp
src/Exports/TimeSeriesProperty.cpp
src/Exports/DateAndTime.cpp
src/Exports/InstrumentInfo.cpp
src/Exports/FacilityInfo.cpp
src/Exports/NullValidator.cpp
)

set ( SRC_FILES
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ namespace Mantid
DEFINE_TYPE_MAPPING(bool, NPY_BOOL);
DEFINE_TYPE_MAPPING(double, NPY_DOUBLE);
DEFINE_TYPE_MAPPING(float, NPY_FLOAT);
DEFINE_TYPE_MAPPING(std::string, NPY_STRING);

}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#include "MantidPythonInterface/kernel/Converters/VectorToNDArray.h"
#include "MantidPythonInterface/kernel/Converters/NDArrayTypeIndex.h"

#include <boost/python/detail/prefix.hpp> // Safe include of Python.h
#include <boost/python/list.hpp>
#define PY_ARRAY_UNIQUE_SYMBOL KERNEL_ARRAY_API
#define NO_IMPORT_ARRAY
#include <numpy/arrayobject.h>

#include <string>

namespace Mantid { namespace PythonInterface
{
namespace Converters
Expand Down Expand Up @@ -38,7 +40,8 @@ namespace Mantid { namespace PythonInterface
}

/**
* Returns a new numpy array with the a copy of the data from cvector
* Returns a new numpy array with the a copy of the data from cvector. A specialization
* exists for strings so that they simply create a standard python list.
* @param cvector :: A reference to a std::vector
* @return
*/
Expand All @@ -47,15 +50,37 @@ namespace Mantid { namespace PythonInterface
{
npy_intp dims[1] = { cvector.size() };
int datatype = NDArrayTypeIndex<typename ContainerType::value_type>::typenum;
PyArrayObject *nparray = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type,
PyArray_DescrFromType(datatype),
1, // rank 1
dims, // Length in each dimension
NULL, NULL,
0, NULL);
double *dest = (double*)PyArray_DATA(nparray); // HEAD of the contiguous numpy data array
std::copy(cvector.begin(), cvector.end(), dest);
return (PyObject *)nparray;
PyObject *nparray =
PyArray_NewFromDescr(&PyArray_Type,
PyArray_DescrFromType(datatype),
1, // rank 1
dims, // Length in each dimension
NULL, NULL,
0, NULL);

void *arrayData = PyArray_DATA(nparray);
const void *data = cvector.data();
std::memcpy(arrayData, data, PyArray_ITEMSIZE(nparray) * dims[0]);
return (PyObject*)nparray;
}

/**
* Returns a new python list of strings from the given vector
* exists for strings so that they simply create a standard python list.
* @param cvector :: A reference to a std::vector
* @return
*/
template<>
PyObject *cloneToNDArray(const std::vector<std::string> & cvector)
{
boost::python::list pystrs;
for(auto iter = cvector.begin(); iter != cvector.end(); ++iter)
{
pystrs.append(iter->c_str());
}
PyObject *rawptr = pystrs.ptr();
Py_INCREF(rawptr); // Make sure it survies after the wrapper decrefs the count
return rawptr;
}

//-----------------------------------------------------------------------
Expand All @@ -72,6 +97,7 @@ namespace Mantid { namespace PythonInterface
INSTANTIATE(unsigned long);
INSTANTIATE(unsigned long long);
INSTANTIATE(double);
INSTANTIATE(std::string);
}
}
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,98 @@
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/NullValidator.h"

#include "MantidPythonInterface/kernel/Policies/VectorToNumpy.h"
#include "MantidPythonInterface/kernel/Converters/NDArrayToVector.h"
#include "MantidPythonInterface/kernel/Converters/PySequenceToVector.h"

#include <boost/python/class.hpp>
#include <boost/python/list.hpp>
#include <boost/python/numeric.hpp>

#include <boost/python/make_constructor.hpp>
#include <boost/python/default_call_policies.hpp>

using Mantid::Kernel::ArrayProperty;
using Mantid::Kernel::PropertyWithValue;
using Mantid::Kernel::Direction;
using Mantid::Kernel::IValidator_sptr;
using Mantid::Kernel::NullValidator;
namespace Policies = Mantid::PythonInterface::Policies;
namespace Converters = Mantid::PythonInterface::Converters;
using namespace boost::python;

#define EXPORT_ARRAY_PROP(type, suffix) \
class_<ArrayProperty<type>, \
bases<PropertyWithValue<std::vector<type> > >, boost::noncopyable>("ArrayProperty_"#suffix, no_init);
namespace
{
/// return_value_policy for cloned numpy array
typedef return_value_policy<Policies::VectorToNumpy<Converters::Clone> > return_cloned_numpy;

#define EXPORT_ARRAY_PROP(type, prefix) \
class_<ArrayProperty<type>, \
bases<PropertyWithValue<std::vector<type> > >, \
boost::noncopyable \
>(#prefix"ArrayProperty", no_init) \
.def(init<const std::string &, const unsigned int>((arg("name"), arg("direction") = Direction::Input), \
"Construct an ArrayProperty of type"#type)) \
\
.def(init<const std::string &, IValidator_sptr, \
const unsigned int>((arg("name"), arg("validator"), arg("direction") = Direction::Input), \
"Construct an ArrayProperty of type"#type"with a validator")) \
\
.def(init<const std::string &, const std::string &, IValidator_sptr, \
const unsigned int>((arg("name"), arg("values"), arg("validator") = IValidator_sptr(new NullValidator), \
arg("direction") = Direction::Input), \
"Construct an ArrayProperty of type"#type"with a validator giving the values as a string")) \
.def("__init__", make_constructor(&createArrayPropertyFromList<type>, default_call_policies(), \
(arg("name"), arg("values"), arg("validator") = IValidator_sptr(new NullValidator), \
arg("direction") = Direction::Input) \
))\
.def("__init__", make_constructor(&createArrayPropertyFromNDArray<type>, default_call_policies(), \
(arg("name"), arg("values"), arg("validator") = IValidator_sptr(new NullValidator), \
arg("direction") = Direction::Input) \
))\
.add_property("value", make_function(&ArrayProperty<type>::operator(), return_cloned_numpy())) \
;


/**
* Factory function to allow the initial values to be specified as a python list
* @param name :: The name of the property
* @param vec :: A boost python list of initial values
* @param validator :: A validator object
* @param direction :: A direction
* @return
*/
template<typename T>
ArrayProperty<T> *
createArrayPropertyFromList(const std::string &name, const boost::python::list & values,
IValidator_sptr validator, const unsigned int direction)
{
return new ArrayProperty<T>(name, Converters::PySequenceToVectorConverter<T>(values)(), validator, direction);
}

/**
* Factory function to allow the initial values to be specified as a numpy array
* @param name :: The name of the property
* @param vec :: A boost python array of initial values
* @param validator :: A validator object
* @param direction :: A direction
* @return
*/
template<typename T>
ArrayProperty<T> *
createArrayPropertyFromNDArray(const std::string &name, const boost::python::numeric::array & values,
IValidator_sptr validator, const unsigned int direction)
{
return new ArrayProperty<T>(name, Converters::NDArrayToVectorConverter<T>(values)(), validator, direction);
}

}

void export_ArrayProperty()
{
EXPORT_ARRAY_PROP(double,dbl);
EXPORT_ARRAY_PROP(int,int);
EXPORT_ARRAY_PROP(size_t,size_t);
EXPORT_ARRAY_PROP(std::string, std_string);
EXPORT_ARRAY_PROP(double,Float);
EXPORT_ARRAY_PROP(int,Int);
EXPORT_ARRAY_PROP(size_t,UnsignedInt);
EXPORT_ARRAY_PROP(std::string, String);
}

Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#include "MantidKernel/IValidator.h"
#include "MantidPythonInterface/kernel/SharedPtrToPythonMacro.h"
#include <boost/python/class.hpp>

using Mantid::Kernel::IValidator;
using namespace boost::python;

void export_IValidator()
{
REGISTER_SHARED_PTR_TO_PYTHON(IValidator);

class_<IValidator, boost::noncopyable>("IValidator", no_init)
;
;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "MantidKernel/NullValidator.h"
#include "MantidPythonInterface/kernel/SharedPtrToPythonMacro.h"
#include <boost/python/class.hpp>

using Mantid::Kernel::NullValidator;
using Mantid::Kernel::IValidator;
using namespace boost::python;

void export_NullValidator()
{
REGISTER_SHARED_PTR_TO_PYTHON(NullValidator);

class_<NullValidator, bases<IValidator>, boost::noncopyable>("NullValidator")
;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Test the exposed ArrayProperty
"""
import unittest
from mantid import FloatArrayProperty, Direction, NullValidator
import numpy as np

class ArrayPropertyTest(unittest.TestCase):

def test_default_constructor_raises_an_exception(self):
"""
Test that the class cannot be default constructed
"""
self.assertRaises(Exception, FloatArrayProperty)

def test_name_only_constructor_gives_correct_object(self):
"""
Tests the simplest constructor that takes
only a name
"""
name = "numbers"
arr = FloatArrayProperty(name)
self.assertTrue(isinstance(arr, FloatArrayProperty))
self._check_object_attributes(arr, name, Direction.Input)

def test_name_direction_constructor_gives_correct_object(self):
"""
Tests the constructor that takes
only a name & direction
"""
name = "numbers"
direc = Direction.Output
arr = FloatArrayProperty(name, direc)
self._check_object_attributes(arr, name, direc)

def test_name_validator_direction_constructor_gives_correct_object(self):
"""
Test the constructor that takes a name, validator & direction
"""
name = "numbers"
direc = Direction.Output
validator = NullValidator()
arr = FloatArrayProperty(name, validator, direc)
self._check_object_attributes(arr, name, direc)
self.assertEquals(arr.isValid(), "")

def test_name_string_values_validator_direction_constructor_gives_correct_object(self):
"""
Test the constructor that takes a name, values as string, validator & direction
"""
name = "numbers"
direc = Direction.Output
validator = NullValidator()
values_str = "1.345,34.2,5345.3,4,5.3948"
arr = FloatArrayProperty(name, values_str, validator, direc)
self._check_object_attributes(arr, name, direc, length = 5)
self.assertEquals(arr.isValid(), "")
values = arr.value
self.assertTrue(isinstance(values, np.ndarray))


def test_name_values_from_list_validator_direction_constructor_gives_correct_object(self):
"""
Test the constructor that takes a name, values from python object,
validator & direction
"""
name = "numbers"
direc = Direction.Output
validator = NullValidator()
input_values =[1.1,2.5,5.6,4.6,9.0, 6.0]
arr = FloatArrayProperty(name, input_values, validator, direc)
self._check_object_attributes(arr, name, direc, length = len(input_values))

def test_name_values_from_array_validator_direction_constructor_gives_correct_object(self):
"""
Test the constructor that takes a name, values from python object,
validator & direction
"""
name = "numbers"
direc = Direction.Output
validator = NullValidator()
input_values = np.array([1.1,2.5,5.6,4.6,9.0, 6.0])
arr = FloatArrayProperty(name, input_values, validator, direc)
self._check_object_attributes(arr, name, direc, length = 6)

def _check_object_attributes(self, arrprop, name, direction, length = 0):
"""
Do attribute tests
"""
self.assertEquals(arrprop.name, name)
self.assertEquals(arrprop.direction, direction)
self.assertEquals(len(arrprop.value), length)

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import unittest
import testhelpers
from mantid import NullValidator

class NullValidatorTest(unittest.TestCase):

def test_NullValidator_can_be_default_constructed(self):
testhelpers.assertRaisesNothing(self, NullValidator)

if __name__ == '__main__':
unittest.main()

0 comments on commit 7e55a90

Please sign in to comment.