Skip to content

Commit

Permalink
Add IFunctionAdapter for access to IFunction's protected attrs.
Browse files Browse the repository at this point in the history
Refs #970
  • Loading branch information
martyngigg committed Apr 15, 2013
1 parent 5542c77 commit c7307e9
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#ifndef MANTID_PYTHONINTERFACE_IFunction1DAdapter_H_
#define MANTID_PYTHONINTERFACE_IFunction1DAdapter_H_
#ifndef MANTID_PYTHONINTERFACE_IFUNCTION1DADAPTER_H_
#define MANTID_PYTHONINTERFACE_IFUNCTION1DADAPTER_H_
/**
Copyright © 2011 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
Expand All @@ -26,7 +26,7 @@
//-----------------------------------------------------------------------------
#include "MantidAPI/IFunction1D.h"
#include "MantidAPI/ParamFunction.h"
#include "MantidKernel/ClassMacros.h"
#include "MantidPythonInterface/api/FitFunctions/IFunctionAdapter.h"

#include <boost/python/object.hpp>

Expand All @@ -41,16 +41,15 @@ namespace Mantid
*
* This is essentially a transparent layer that handles the function calls up into Python.
*/
class IFunction1DAdapter : public virtual API::ParamFunction, public virtual API::IFunction1D
class IFunction1DAdapter :
public virtual API::ParamFunction, public virtual API::IFunction1D, public virtual IFunctionAdapter
{
public:
/// A constructor that looks like a Python __init__ method
IFunction1DAdapter(PyObject* self);

/** @name Virtual methods */
///@{
/// Returns the name of the algorithm
std::string name() const;
/// Base-class method
void function1D(double* out, const double* xValues, const size_t nData) const;
/// Python-type signature
Expand All @@ -63,7 +62,7 @@ namespace Mantid
DISABLE_COPY_AND_ASSIGN(IFunction1DAdapter);

/**
* Returns the PyObject that owns this wrapper, i.e. self
* Returns the PyObject that owns this wrapper, i.e. self
* @returns A pointer to self
*/
inline PyObject * getSelf() const { return m_self; }
Expand All @@ -75,4 +74,4 @@ namespace Mantid
}


#endif /* MANTID_PYTHONINTERFACE_IFunction1DAdapter_H_ */
#endif /* MANTID_PYTHONINTERFACE_IFUNCTION1DADAPTER_H_ */
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#ifndef MANTID_PYTHONINTERFACE_IFUNCTIONADAPTER_H_
#define MANTID_PYTHONINTERFACE_IFUNCTIONADAPTER_H_
/**
Copyright &copy; 2011 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://github.com/mantidproject/mantid>.
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "MantidAPI/IFunction.h"
#include "MantidKernel/ClassMacros.h"

#include <boost/python/object.hpp>

namespace Mantid
{
namespace PythonInterface
{
/**
* Provides a layer to hook into the protected functions
* of IFunction
*/
class IFunctionAdapter : public virtual API::IFunction
{
public:
/// A constructor that looks like a Python __init__ method
IFunctionAdapter(PyObject* self);

/// Returns the name of the function
std::string name() const;

/// Declare an attribute with an initial value
void declareAttribute(const std::string &name, const boost::python::object &defaultValue);
/// Get a named attribute value
PyObject * getAttributeValue(const std::string & name);

private:
/// The PyObject must be supplied to construct the object
DISABLE_DEFAULT_CONSTRUCT(IFunctionAdapter);
DISABLE_COPY_AND_ASSIGN(IFunctionAdapter);

/// The name of the function
std::string m_name;
};
}
}


#endif /* MANTID_PYTHONINTERFACE_IFUNCTIONADAPTER_H_ */
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,16 @@ set ( EXPORT_FILES
)

# Files containing additional helper code that are not related to exporting class/functions
set ( SRC_FILES
set ( SRC_FILES
src/FitFunctions/IFunctionAdapter.cpp
src/FitFunctions/IFunction1DAdapter.cpp
src/PythonAlgorithm/AlgorithmWrapper.cpp
src/PythonAlgorithm/PythonAlgorithm.cpp
src/CloneMatrixWorkspace.cpp
)

set ( INC_FILES
set ( INC_FILES
${HEADER_DIR}/api/FitFunctions/IFunctionAdapter.h
${HEADER_DIR}/api/FitFunctions/IFunction1DAdapter.h
${HEADER_DIR}/api/PythonAlgorithm/AlgorithmWrapper.h
${HEADER_DIR}/api/PythonAlgorithm/PythonAlgorithm.h
Expand Down Expand Up @@ -116,4 +118,4 @@ target_link_libraries ( PythonAPIModule PythonGeometryModule PythonKernelModule
install ( TARGETS PythonAPIModule ${SYSTEM_PACKAGE_TARGET} DESTINATION ${BIN_DIR}/mantid/api )

# Pure Python files
install ( FILES ${PY_FILES} DESTINATION ${BIN_DIR}/mantid/api )
install ( FILES ${PY_FILES} DESTINATION ${BIN_DIR}/mantid/api )
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
#include "MantidAPI/IFunction.h"
#include "MantidPythonInterface/kernel/PythonObjectInstantiator.h"
#include "MantidPythonInterface/api/FitFunctions/IFunctionAdapter.h"
#include "MantidPythonInterface/kernel/SharedPtrToPythonMacro.h"
#include "MantidPythonInterface/api/PythonAlgorithm/AlgorithmWrapper.h"

#include <boost/python/class.hpp>
#include <boost/python/def.hpp>

// Python frameobject. This is under the boost includes so that boost will have done the
// include of Python.h which it ensures is done correctly
#include <frameobject.h>

using Mantid::API::IFunction;
using Mantid::PythonInterface::IFunctionAdapter;
using namespace boost::python;

namespace
Expand Down Expand Up @@ -43,19 +38,43 @@ void export_IFunction()
{

REGISTER_SHARED_PTR_TO_PYTHON(IFunction);
class_<IFunction, boost::noncopyable>("IFunction", "Base-class for C IFunctions", no_init);

class_<IFunction,boost::noncopyable>("IFunction", no_init)
.def("name", &IFunction::name, "Return the name of the function")
.def("__repr__", &IFunction::asString, "Return a string representation of the function")
.def("categories", &getCategories, "Returns a list of the categories for an algorithm")
.def("numParams", &IFunction::nParams, "Return the number of parameters")
.def("getParamName", &IFunction::parameterName, "Return the name of the ith parameter")
.def("getParamDescr", &IFunction::parameterDescription, "Return a description of the ith parameter")
.def("getParamExplicit", &IFunction::isExplicitlySet,
"Return whether the ith parameter needs to be explicitely set")
.def("getParamValue", (double (IFunction::*)(std::size_t) const)&IFunction::getParameter,
"Get the value of the ith parameter")

class_<IFunction, IFunctionAdapter, boost::noncopyable>("IFunction", "Base class for all functions", no_init)
.def("name", &IFunction::name, "Return the name of the function")

.def("getCategories", &getCategories, "Returns a list of the categories for an algorithm")

.def("nParams", &IFunction::nParams, "Return the number of parameters")

.def("parameterName", &IFunction::parameterName, "Return the name of the ith parameter")

.def("paramDescription", &IFunction::parameterDescription, "Return a description of the ith parameter")

.def("isExplicitlySet", &IFunction::isExplicitlySet,
"Return whether the ith parameter needs to be explicitely set")

.def("getParameterValue", (double (IFunction::*)(size_t) const)&IFunction::getParameter,
"Get the value of the ith parameter")

.def("setParameter", (void (IFunction::*)(size_t,const double &,bool))&IFunction::setParameter,
"Set the value of the ith parameter")

.def("declareAttribute", &IFunctionAdapter::declareAttribute, "Declare an attribute with an initial value")

.def("getAttributeValue", &IFunctionAdapter::getAttribute, "Return the value of the ith attribute")


//-- Deprecated functions that have the wrong names --
.def("categories", &getCategories, "Returns a list of the categories for an algorithm")
.def("numParams", &IFunction::nParams, "Return the number of parameters")
.def("getParamName", &IFunction::parameterName, "Return the name of the ith parameter")
.def("getParamDescr", &IFunction::parameterDescription, "Return a description of the ith parameter")
.def("getParamExplicit", &IFunction::isExplicitlySet,
"Return whether the ith parameter needs to be explicitely set")
.def("getParamValue", (double (IFunction::*)(std::size_t) const)&IFunction::getParameter,
"Get the value of the ith parameter")
//-- Python special methods --
.def("__repr__", &IFunction::asString, "Return a string representation of the function")
;

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Mantid::API::IFunction1D;
using Mantid::API::IFunction;
using Mantid::PythonInterface::IFunction1DAdapter;
using Mantid::PythonInterface::IFunctionAdapter;
using namespace boost::python;

void export_IFunction1D()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,10 @@ namespace Mantid
* * @param self A reference to the calling Python object
*/
IFunction1DAdapter::IFunction1DAdapter(PyObject* self)
: IFunction1D(), m_self(self)
: API::ParamFunction(), API::IFunction1D(), IFunctionAdapter(self), m_self(self)
{
}

/**
* Returns the class name of the function. This cannot be overridden in Python.
*/
std::string IFunction1DAdapter::name() const
{
return std::string(getSelf()->ob_type->tp_name);
}

/**
* Translates between the C++ signature & the Python signature
* @param out The 1D data array of size nData that stores the output values
Expand All @@ -56,7 +48,5 @@ namespace Mantid
{
CallMethod2<void,object,object>::dispatchWithException(getSelf(), "function1D", xvals, out);
}


}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include "MantidPythonInterface/api/FitFunctions/IFunctionAdapter.h"

#include <boost/python/class.hpp>

namespace Mantid
{
namespace PythonInterface
{
using namespace boost::python;

/**
* Construct the wrapper and stores the reference to the PyObject
* * @param self A reference to the calling Python object
*/
IFunctionAdapter::IFunctionAdapter(PyObject* self)
: IFunction(), m_name(self->ob_type->tp_name)
{
}

/**
* Returns the class name of the function. This cannot be overridden in Python.
*/
std::string IFunctionAdapter::name() const
{
return m_name;
}

/**
* Declare an attribute on the given function from a python object
* @param name :: The name of the new attribute
* @param defaultValue :: The default value for the attribute
*/
void IFunctionAdapter::declareAttribute(const std::string &name,
const object &defaultValue)
{
PyObject *rawptr = defaultValue.ptr();
IFunction::Attribute attr;
if(PyInt_Check(rawptr) == 1) attr = IFunction::Attribute(extract<int>(rawptr)());
else if(PyFloat_Check(rawptr) == 1) attr = IFunction::Attribute(extract<double>(rawptr)());
else if(PyString_Check(rawptr) == 1) attr = IFunction::Attribute(extract<std::string>(rawptr)());
else if(PyBool_Check(rawptr) == 1) attr = IFunction::Attribute(extract<bool>(rawptr)());
else throw std::invalid_argument("Invalid attribute type. Allowed types=float,int,str,bool");

IFunction::declareAttribute(name, attr);
}

/**
* Get the value of the given attribute as a Python object
* @param name :: The name of the new attribute
* @param defaultValue :: The default value for the attribute
* @returns The value of the attribute
*/
PyObject * IFunctionAdapter::getAttributeValue(const std::string & name)
{
auto attr = IFunction::getAttribute(name);
std::string type = attr.type();
PyObject *result(NULL);
if(type=="int") result = to_python_value<const int&>()(attr.asInt());
else if(type=="double") result = to_python_value<const double&>()(attr.asDouble());
else if(type=="std::string") result = to_python_value<const std::string&>()(attr.asString());
else if(type=="bool") result = to_python_value<const bool&>()(attr.asBool());
else throw std::runtime_error("Unknown attribute type, cannot convert C++ type to Python. Contact developement team.");

return result;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class PyLinear(IFunction1D):

def function1D(self, out, xvals):
def function1D(self, xvals, out):
pass

class IFunction1DTest(unittest.TestCase):
Expand All @@ -19,6 +19,15 @@ def test_instance_can_be_created_from_factory(self):
self.assertTrue(isinstance(func, IFunction1D))
FunctionFactory.unsubscribe(func_name)

def test_declareAttribute_only_accepts_known_types(self):
func = PyLinear()
func.declareAttribute("IntAtt", 1)
func.declareAttribute("DoubleAtt", 3.4)
func.declareAttribute("StringAtt", "filename")
func.declareAttribute("BoolAtt", True)

self.assertRaises(ValueError, func.declareAttribute, "ListAtt", [1,2,3])


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

0 comments on commit c7307e9

Please sign in to comment.