Skip to content

Commit

Permalink
Refs #9637. Adding logic for array property creation.
Browse files Browse the repository at this point in the history
Makes everything double vector, but that gets fixed next.
  • Loading branch information
Michael Reuter committed Jun 11, 2014
1 parent d390b3e commit 85378dc
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 9 deletions.
Expand Up @@ -56,7 +56,9 @@ namespace Mantid
const unsigned int direction);
private:
/// Return a handler that maps the python type to a C++ type
static const PropertyValueHandler & lookup(PyTypeObject * const pythonType);
static const PropertyValueHandler & lookup(PyObject * const object);
/// Return a string based on the python array type
static const std::string check_sequence(PyObject * const object);
};
}
}
Expand Down
Expand Up @@ -42,6 +42,9 @@ namespace Mantid
void set(Kernel::IPropertyManager* alg, const std::string &name,
const boost::python::object & value) const;

/// Call to create a name property where the value is some container type
Kernel::Property *create(const std::string &name, const boost::python::object &defaultValue,
const boost::python::object &validator, const unsigned int direction) const;
// /**
// * Return the PyTypeObject of the DerivedType
// * @returns A PyTypeObject for the given DerivedType
Expand Down
Expand Up @@ -3,8 +3,14 @@
//-----------------------------------------------------------------------------
#include "MantidPythonInterface/kernel/Registry/PropertyWithValueFactory.h"
#include "MantidPythonInterface/kernel/Registry/TypedPropertyValueHandler.h"
#include "MantidPythonInterface/kernel/Registry/SequenceTypeHandler.h"
#include "MantidKernel/PropertyWithValue.h"

// See http://docs.scipy.org/doc/numpy/reference/c-api.array.html#PY_ARRAY_UNIQUE_SYMBOL
#define PY_ARRAY_UNIQUE_SYMBOL KERNEL_ARRAY_API
#define NO_IMPORT_ARRAY
#include <numpy/arrayobject.h>

#include <boost/make_shared.hpp>

#include <cassert>
Expand Down Expand Up @@ -53,6 +59,45 @@ namespace Mantid
if( index.empty() ) initTypeLookup(index);
return index;
}

// Lookup map for arrays
typedef std::map<std::string,
boost::shared_ptr<PropertyValueHandler>> PyArrayIndex;

/**
* Initialize lookup map
*/
void initArrayLookup(PyArrayIndex & index)
{
assert(index.empty());

// Map the Python array types to the best match in C++
typedef SequenceTypeHandler<std::vector<double>> FloatArrayHandler;
index.insert(std::make_pair("FloatArray",
boost::make_shared<FloatArrayHandler>()));

typedef SequenceTypeHandler<std::vector<int>> IntArrayHandler;
index.insert(std::make_pair("IntArray",
boost::make_shared<IntArrayHandler>()));

typedef SequenceTypeHandler<std::vector<long>> LongIntArrayHandler;
index.insert(std::make_pair("LongIntArray",
boost::make_shared<LongIntArrayHandler>()));

typedef SequenceTypeHandler<std::vector<std::string>> StringArrayHandler;
index.insert(std::make_pair("StringArray",
boost::make_shared<StringArrayHandler>()));
}

/**
* Returns a reference to the static array lookup map
*/
const PyArrayIndex & getArrayIndex()
{
static PyArrayIndex index;
if( index.empty() ) initArrayLookup(index);
return index;
}
}

/**
Expand All @@ -68,7 +113,7 @@ namespace Mantid
PropertyWithValueFactory::create(const std::string & name , const boost::python::object & defaultValue,
const boost::python::object & validator, const unsigned int direction)
{
const auto &propHandle = lookup(defaultValue.ptr()->ob_type);
const auto &propHandle = lookup(defaultValue.ptr());
return propHandle.create(name, defaultValue, validator, direction);
}

Expand Down Expand Up @@ -97,18 +142,46 @@ namespace Mantid
* @param pythonType :: A pointer to a PyTypeObject that represents the type
* @returns A pointer to handler that can be used to instantiate a property
*/
const PropertyValueHandler & PropertyWithValueFactory::lookup(PyTypeObject * const pythonType)
const PropertyValueHandler & PropertyWithValueFactory::lookup(PyObject * const object)
{
const PyTypeIndex & typeIndex = getTypeIndex();
auto cit = typeIndex.find(pythonType);
if( cit == typeIndex.end() )
const auto ptype = check_sequence(object);
if (!ptype.empty())
{
const PyArrayIndex &arrayIndex = getArrayIndex();
auto ait = arrayIndex.find(ptype);
if (ait != arrayIndex.end())
{
std::ostringstream os;
os << "Cannot create PropertyWithValue from Python type " << pythonType->tp_name << ". No converter registered in PropertyWithValueFactory.";
throw std::invalid_argument(os.str());
return *(ait->second);
}
}
// Object is not array, so check primitive types
const PyTypeIndex & typeIndex = getTypeIndex();
auto cit = typeIndex.find(object->ob_type);
if( cit == typeIndex.end() )
{
std::ostringstream os;
os << "Cannot create PropertyWithValue from Python type " << object->ob_type->tp_name << ". No converter registered in PropertyWithValueFactory.";
throw std::invalid_argument(os.str());
}
return *(cit->second);
}

/**
* Return a string for the array type to check the map for.
* @param object :: Python object to check if it's an array
* @return :: A string as the array type.
*/
const std::string PropertyWithValueFactory::check_sequence(PyObject * const object)
{
if (PyArray_Check(object) || PyList_Check(object) || PyTuple_Check(object))
{
return std::string("FloatArray");
}
else
{
return std::string("");
}
}
}
}
}
Expand Up @@ -75,6 +75,57 @@ namespace Mantid
}
}

/**
* Create a PropertyWithValue from the given python object value
* @param name :: The name of the property
* @param defaultValue :: The defaultValue of the property. The object attempts to extract
* a value of type ContainerType from the python object
* @param validator :: A python object pointing to a validator instance, which can be None.
* @param direction :: The direction of the property
* @returns A pointer to a newly constructed property instance
*/
template<typename ContainerType>
Kernel::Property *SequenceTypeHandler<ContainerType>::create(const std::string &name,
const boost::python::object &defaultValue,
const boost::python::object &validator,
const unsigned int direction) const
{
typedef typename ContainerType::value_type DestElementType;

ContainerType valueInC;
// Current workaround for things that still pass back wrapped vectors...
if(boost::starts_with(defaultValue.ptr()->ob_type->tp_name, "std_vector"))
{
valueInC = StdVectorExtractor<DestElementType>::extract(defaultValue);
}
// numpy arrays requires special handling to extract their types. Hand-off to a more appropriate handler
else if( PyArray_Check(defaultValue.ptr()) )
{
valueInC = Converters::NDArrayToVector<DestElementType>(defaultValue)();
}
else if( PySequence_Check(defaultValue.ptr()) )
{
valueInC = Converters::PySequenceToVector<DestElementType>(defaultValue)();
}
else // assume it is a scalar and try to convert into a vector of length one
{
DestElementType scalar = boost::python::extract<DestElementType>(defaultValue.ptr());
valueInC = std::vector<DestElementType>(1, scalar);
}

Kernel::Property *valueProp(NULL);
if (isNone(validator))
{
valueProp = new Kernel::PropertyWithValue<ContainerType>(name, valueInC, direction);
}
else
{
const Kernel::IValidator *propValidator = boost::python::extract<Kernel::IValidator*>(validator);
valueProp = new Kernel::PropertyWithValue<ContainerType>(name, valueInC, propValidator->clone(), direction);
}
return valueProp;
}

//-----------------------------------------------------------------------
// Concrete instantiations
//-----------------------------------------------------------------------
Expand Down

0 comments on commit 85378dc

Please sign in to comment.