Commit 8de22e32 authored by Matthew Bowles's avatar Matthew Bowles
Browse files

initial fit for single/multi ion/spectra Re #20358

parent c3d1dd66
......@@ -263,7 +263,7 @@ std::string UsageServiceImpl::generateFeatureUsageMessage() {
thisFeature["count"] = featureItem.second;
features.append(thisFeature);
}
if (features.size() > 0) {
if (!features.empty()) {
message["features"] = features;
return writer.write(message);
}
......
......@@ -341,7 +341,7 @@ void CompareMDWorkspaces::exec() {
this->doComparison();
if (m_result != "") {
if (!m_result.empty()) {
g_log.notice() << "The workspaces did not match: " << m_result << '\n';
this->setProperty("Equals", false);
} else {
......
......@@ -70,7 +70,7 @@ void ConvertToDiffractionMDWorkspace3::convertExtents(
minVal[d] = Extents[2 * d + 0];
maxVal[d] = Extents[2 * d + 1];
}
} else if (Extents.size() == 0) {
} else if (Extents.empty()) {
calculateExtentsFromData(minVal, maxVal);
} else
throw std::invalid_argument(
......
......@@ -98,9 +98,9 @@ Integrate3DEvents::integrateStrongPeak(const IntegrationParameters &params,
std::vector<V3D> eigen_vectors;
getEigenVectors(cov_matrix, eigen_vectors);
std::vector<double> sigmas;
std::vector<double> sigmas(3);
for (int i = 0; i < 3; i++) {
sigmas.push_back(stdDev(events, eigen_vectors[i], params.regionRadius));
sigmas[i] = stdDev(events, eigen_vectors[i], params.regionRadius);
}
bool invalid_peak =
......@@ -248,9 +248,9 @@ double Integrate3DEvents::estimateSignalToNoiseRatio(
std::vector<V3D> eigen_vectors;
getEigenVectors(cov_matrix, eigen_vectors);
std::vector<double> sigmas;
std::vector<double> sigmas(3);
for (int i = 0; i < 3; i++) {
sigmas.push_back(stdDev(events, eigen_vectors[i], params.regionRadius));
sigmas[i] = stdDev(events, eigen_vectors[i], params.regionRadius);
}
const auto max_sigma = *std::max_element(sigmas.begin(), sigmas.end());
......@@ -404,9 +404,9 @@ Integrate3DEvents::ellipseIntegrateEvents(
std::vector<V3D> eigen_vectors;
getEigenVectors(cov_matrix, eigen_vectors);
std::vector<double> sigmas;
std::vector<double> sigmas(3);
for (int i = 0; i < 3; i++) {
sigmas.push_back(stdDev(some_events, eigen_vectors[i], m_radius));
sigmas[i] = stdDev(some_events, eigen_vectors[i], m_radius);
}
bool invalid_peak =
......
......@@ -264,7 +264,7 @@ bool NexusFileIO::writeNxNote(const std::string &noteName,
m_filehandle->makeGroup(noteName, "NXnote", true);
std::vector<std::string> attributes, avalues;
if (date != "") {
if (!date.empty()) {
attributes.emplace_back("date");
avalues.push_back(date);
}
......
......@@ -15,6 +15,7 @@ set ( OUTPUT_DIR ${PYTHON_PKG_ROOT} )
set ( PY_FILES
__init__.py
simpleapi.py
fitfunctions.py
)
copy_files_to_dir ( "${PY_FILES}" ${CMAKE_CURRENT_SOURCE_DIR} ${OUTPUT_DIR}
......
......@@ -75,6 +75,7 @@ set ( EXPORT_FILES
src/Exports/Projection.cpp
src/Exports/FunctionProperty.cpp
src/Exports/AlgorithmProperty.cpp
src/Exports/MultiDomainFunction.cpp
)
set ( MODULE_DEFINITION ${CMAKE_CURRENT_BINARY_DIR}/api.cpp )
......
#include "MantidPythonInterface/kernel/GetPointer.h"
#include "MantidAPI/CompositeFunction.h"
#include <boost/python/class.hpp>
#include <boost/python/overloads.hpp>
#include <boost/python/register_ptr_to_python.hpp>
using Mantid::API::CompositeFunction;
using Mantid::API::IFunction;
using namespace boost::python;
GET_POINTER_SPECIALIZATION(CompositeFunction)
namespace {
typedef double (CompositeFunction::*getParameterType1)(size_t) const;
typedef double (CompositeFunction::*getParameterType2)(
const std::string &) const;
typedef void (CompositeFunction::*setParameterType2)(const std::string &,
const double &, bool);
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wunused-local-typedef"
#endif
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(setParameterType2_Overloads,
setParameter, 2, 3)
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace
void export_CompositeFunction() {
register_ptr_to_python<boost::shared_ptr<CompositeFunction>>();
class_<CompositeFunction, bases<IFunction>, boost::noncopyable>(
"CompositeFunction", "Composite Fit functions")
.def("nFunctions", &CompositeFunction::nFunctions, arg("self"),
......@@ -17,6 +44,23 @@ void export_CompositeFunction() {
(arg("self"), arg("i")), "Get the i-th function.")
.def("__getitem__", &CompositeFunction::getFunction,
(arg("self"), arg("i")), "Get the i-th function.")
.def("__setitem__", &CompositeFunction::replaceFunction,
(arg("self"), arg("i"), arg("f")),
"Put function in place of the i-th function.")
.def("add", &CompositeFunction::addFunction,
(arg("self"), arg("function")), "Add a member function.");
(arg("self"), arg("function")), "Add a member function.")
.def("getParameterValue",
(getParameterType1)&CompositeFunction::getParameter,
(arg("self"), arg("i")), "Get value of parameter of given index.")
.def("getParameterValue",
(getParameterType2)&CompositeFunction::getParameter,
(arg("self"), arg("name")), "Get value of parameter of given name.")
.def("__getitem__", (getParameterType2)&CompositeFunction::getParameter,
(arg("self"), arg("name")), "Get value of parameter of given name.")
.def("__setitem__", (setParameterType2)&CompositeFunction::setParameter,
setParameterType2_Overloads(
(arg("self"), arg("name"), arg("value"), arg("explicitlySet")),
"Get value of parameter of given name."))
.def("__delitem__", &CompositeFunction::removeFunction,
(arg("self"), arg("index")));
}
#include "MantidAPI/FunctionFactory.h"
#include "MantidAPI/IFunction.h"
#include "MantidAPI/CompositeFunction.h"
#include "MantidKernel/WarningSuppressions.h"
#include "MantidPythonInterface/kernel/GetPointer.h"
#include "MantidPythonInterface/kernel/PythonObjectInstantiator.h"
......@@ -47,6 +48,28 @@ PyObject *getFunctionNames(FunctionFactoryImpl &self) {
return registered;
}
//------------------------------------------------------------------------------------------------------
/**
* Something that makes Function Factory return to python a composite function
* for Product function, Convolution or
* any similar superclass of composite function.
* @param self :: Enables it to be called as a member function on the
* FunctionFactory class
* @param name :: Name of the superclass of composite function,
* e.g. "ProductFunction".
*/
Mantid::API::CompositeFunction_sptr
createCompositeFunction(FunctionFactoryImpl &self, const std::string &name) {
auto fun = self.createFunction(name);
auto composite =
boost::dynamic_pointer_cast<Mantid::API::CompositeFunction>(fun);
if (composite) {
return composite;
}
std::string error_message = name + " is not a composite function.";
throw std::invalid_argument(error_message);
}
//--------------------------------------------- Function registration
//------------------------------------------------
......@@ -96,6 +119,9 @@ void export_FunctionFactory() {
no_init)
.def("getFunctionNames", &getFunctionNames, arg("self"),
"Returns a list of the currently available functions")
.def("createCompositeFunction", &createCompositeFunction,
(arg("self"), arg("name")),
"Return a pointer to the requested function")
.def("createFunction", &FunctionFactoryImpl::createFunction,
(arg("self"), arg("type")),
"Return a pointer to the requested function")
......
......@@ -93,7 +93,7 @@ struct MandatoryFirst {
/// in the list
bool operator()(const Property *p1, const Property *p2) const {
// this is false, unless p1 is not valid and p2 is valid
return (p1->isValid() != "") && (p2->isValid() == "");
return (!p1->isValid().empty()) && (p2->isValid().empty());
}
};
......
#include "MantidKernel/WarningSuppressions.h"
#include "MantidPythonInterface/kernel/GetPointer.h"
#include "MantidPythonInterface/api/FitFunctions/IFunctionAdapter.h"
#include "MantidAPI/CompositeFunction.h"
#include <boost/python/class.hpp>
#include <boost/python/def.hpp>
......@@ -60,6 +61,8 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fixParameter_Overloads, fixParameter, 1,
2)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fix_Overloads, fix, 1, 2)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fixAll_Overloads, fixAll, 0, 1)
typedef void (IFunction::*removeTieByName)(const std::string &);
GCC_DIAG_ON(conversion)
#ifdef __clang__
#pragma clang diagnostic pop
......@@ -92,6 +95,12 @@ void export_IFunction() {
.def("attributeNames", &IFunction::getAttributeNames, arg("self"),
"The names of all the attributes")
.def("hasAttribute", &IFunction::hasAttribute, (arg("self"), arg("name")),
"Return whether there is an attribute of the given name")
.def("hasParameter", &IFunction::hasParameter, (arg("self"), arg("name")),
"Return whether there is an parameter of the given name")
.def("nParams", &IFunction::nParams, arg("self"),
"Return the number of parameters")
......@@ -114,6 +123,10 @@ void export_IFunction() {
IFunction::getParameter,
(arg("self"), arg("name")), "Get the value of the named parameter")
.def("__getitem__", (double (IFunction::*)(const std::string &) const) &
IFunction::getParameter,
(arg("self"), arg("name")), "Get the value of the named parameter")
.def("setParameter", (setParameterType1)&IFunction::setParameter,
setParameterType1_Overloads(
(arg("self"), arg("i"), arg("value"), arg("explicitlySet")),
......@@ -124,6 +137,11 @@ void export_IFunction() {
(arg("self"), arg("name"), arg("value"), arg("explicitlySet")),
"Sets the value of the named parameter"))
.def("__setitem__", (setParameterType2)&IFunction::setParameter,
setParameterType2_Overloads(
(arg("self"), arg("name"), arg("value"), arg("explicitlySet")),
"Sets the value of the named parameter"))
.def("declareAttribute", &IFunctionAdapter::declareAttribute,
(arg("self"), arg("name"), arg("default_value")),
"Declare an attribute with an initial value")
......@@ -204,6 +222,9 @@ void export_IFunction() {
"Split this function (if needed) into a list of "
"independent functions")
.def("nDomains", &IFunction::getNumberDomains, arg("self"),
"Get the number of domains.")
//-- Deprecated functions that have the wrong names --
.def("categories", &getCategories, arg("self"),
"Returns a list of the categories for an algorithm")
......@@ -219,6 +240,7 @@ void export_IFunction() {
.def("getParamValue",
(double (IFunction::*)(std::size_t) const) & IFunction::getParameter,
(arg("self"), arg("i")), "Get the value of the ith parameter")
//-- Python special methods --
.def("__repr__", &IFunction::asString, arg("self"),
"Return a string representation of the function");
......
#include "MantidAPI/MultiDomainFunction.h"
#include <boost/python/class.hpp>
using Mantid::API::MultiDomainFunction;
using Mantid::API::CompositeFunction;
using namespace boost::python;
void export_MultiDomainFunction() {
class_<MultiDomainFunction, bases<CompositeFunction>, boost::noncopyable>(
"MultiDomainFunction", "Multi-Domain Fit functions")
.def("nFunctions", &MultiDomainFunction::nFunctions, arg("self"),
"Get the number of member functions.")
.def("__len__", &MultiDomainFunction::nFunctions, arg("self"),
"Get the number of member functions.")
.def("getFunction", &MultiDomainFunction::getFunction,
(arg("self"), arg("i")), "Get the i-th function.")
.def("__getitem__", &MultiDomainFunction::getFunction,
(arg("self"), arg("i")), "Get the i-th function.")
.def("add", &MultiDomainFunction::addFunction,
(arg("self"), arg("function")), "Add a member function.")
.def("setDomainIndex", &MultiDomainFunction::setDomainIndex,
(arg("self"), arg("funIndex"), arg("domainIndex")),
"Associate a function and a domain.");
}
#include "MantidAPI/ProductFunction.h"
#include <boost/python/class.hpp>
#include <boost/python/overloads.hpp>
using Mantid::API::ProductFunction;
using Mantid::API::IFunction;
using namespace boost::python;
namespace {
typedef double (ProductFunction::*getParameterType1)(size_t) const;
typedef double (ProductFunction::*getParameterType2)(const std::string &) const;
typedef void (ProductFunction::*setParameterType2)(const std::string &,
const double &, bool);
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(setParameterType2_Overloads,
setParameter, 2, 3)
}
void export_ProductFunction() {
class_<ProductFunction, bases<IFunction>, boost::noncopyable>(
"ProductFunction", "Composite Fit functions")
.def("nFunctions", &ProductFunction::nFunctions, arg("self"),
"Get the number of member functions.")
.def("__len__", &ProductFunction::nFunctions, arg("self"),
"Get the number of member functions.")
.def("getFunction", &ProductFunction::getFunction,
(arg("self"), arg("i")), "Get the i-th function.")
.def("__getitem__", &ProductFunction::getFunction,
(arg("self"), arg("i")), "Get the i-th function.")
.def("add", &ProductFunction::addFunction, (arg("self"), arg("function")),
"Add a member function.")
.def("getParameterValue",
(getParameterType1)&ProductFunction::getParameter,
(arg("self"), arg("i")), "Get value of parameter of given index.")
.def("getParameterValue",
(getParameterType2)&ProductFunction::getParameter,
(arg("self"), arg("name")), "Get value of parameter of given name.")
.def("__getitem__", (getParameterType2)&ProductFunction::getParameter,
(arg("self"), arg("name")), "Get value of parameter of given name.")
.def("__setitem__", (setParameterType2)&ProductFunction::setParameter,
setParameterType2_Overloads(
(arg("self"), arg("name"), arg("value"), arg("explicitlySet")),
"Get value of parameter of given name."))
.def("__delitem__", &ProductFunction::removeFunction,
(arg("self"), arg("index")));
}
from mantid.api import FunctionFactory
class FunctionWrapper(object):
""" Wrapper class for Fitting Function
"""
def __init__ (self, name, **kwargs):
"""
Called when creating an instance
:param name: name of fitting function to create or
an Ifunction object to wrap.
:param **kwargs: standard argument for __init__ function
"""
if not isinstance(name, str):
self.fun = name
else:
self.fun = FunctionFactory.createFunction(name)
# Deal with attributes first
for key in kwargs:
if key == "attributes":
atts = kwargs[key]
for keya in atts:
self.fun.setAttributeValue(keya, atts[keya])
elif self.fun.hasAttribute(key):
self.fun.setAttributeValue(key, kwargs[key])
# Then deal with parameters
for key in kwargs:
if key != "attributes" and not self.fun.hasAttribute(key):
self.fun.setParameter(key, kwargs[key])
def __getattr__(self, item):
"""
__getattr__ invoked when attribute item not found in the instance,
nor in the class, nor its superclasses.
:param item: named attribute
:return: attribute of self.fun instance, or any of the fitting
parameters or function attributes
"""
if 'fun' in self.__dict__:
if hasattr(self.fun, item):
return getattr(self.fun, item)
else:
return self.__getitem__(item)
def __setattr__(self, key, value):
"""
__setattr__ invoked when attribute key not found in the instance,
nor in the class, nor its superclasses.
Enables direct access to the attributes of self.fun instance, and
also to its fitting parameters and function attributes
:param key: named attribute
:param value: new value for the named attribute
"""
if key == 'fun':
self.__dict__['fun'] = value # initialize self.fun
elif 'fun' in self.__dict__ and hasattr(self.fun, key):
setattr(self.fun, key, value) # key is attribute of instance self.fun
elif 'fun' in self.__dict__ and self.fun.hasAttribute(key):
self.fun.setAttributeValue(key, value) # key is function attribute
elif 'fun' in self.__dict__ and self.fun.hasParameter(key):
self.fun.setParameter(key, value) # key is fitting parameter
else:
self.__dict__[key] = value # initialize self.key
def __getitem__ (self, name):
""" Called from array-like access on RHS
It should not be called directly.
:param name: name that appears in the []
"""
if type(name) == type('string') and self.fun.hasAttribute(name):
return self.fun.getAttributeValue(name)
else:
return self.fun.getParameterValue(name)
def __setitem__ (self, name, value):
""" Called from array-like access on LHS
It should not be called directly.
:param name: name that appears in the []
:param value: new value of this item
"""
if type(name) == type('string') and self.fun.hasAttribute(name):
self.fun.setAttributeValue(name, value)
else:
self.fun.setParameter(name, value)
def __str__ (self):
""" Return string giving contents of function.
Used in unit tests.
"""
return self.fun.__str__()
def __add__ (self, other):
""" Implement + operator for composite function
:param other: functionWrapper to be added to self
"""
sum = CompositeFunctionWrapper(self, other)
if sum.pureAddition:
sum = sum.flatten()
return sum
def __mul__ (self, other):
""" Implement * operator for product function
:param other: functionWrapper to multiply self by
"""
prod = ProductFunctionWrapper(self, other)
if prod.pureMultiplication:
prod = prod.flatten()
return prod
def tie (self, *args, **kwargs):
""" Add ties.
:param *args: one or more dictionaries of ties
:param **kwargs: one or more ties
"""
for a in args:
if isinstance(a, dict):
for key in a:
self.fun.tie(key, str(a[key]))
for key in kwargs:
self.fun.tie(key, str(kwargs[key]))
def fix(self, name):
""" Fix a parameter.
:param name: name of parameter to be fixed
"""
self.fun.fixParameter(name)
def fixAllParameters(self):
""" Fix all parameters.
"""
for i in range(0, self.fun.numParams()):
self.fix(self.getParameterName(i))
def untie(self, name):
""" Remove tie from parameter.
:param name: name of parameter to be untied
"""
self.fun.removeTie(name)
def untieAllParameters(self):
""" Remove ties from all parameters.
"""
for i in range(0, self.fun.numParams()):
self.fun.removeTie(self.getParameterName(i))
def constrain(self, expressions):
""" Add constraints
:param expressions: string of tie expressions
"""
self.fun.addConstraints( expressions )
def unconstrain(self, name):
""" Remove constraints from a parameter
:param name: name of parameter to be unconstrained
"""
self.fun.removeConstraint(name)
def free(self, name):
""" Free a parameter from tie or constraint
:param name: name of parameter to be freed
"""
self.fun.removeTie(name)
self.fun.removeConstraint(name)
def getParameterName(self, index):
""" Get the name of the parameter of given index
:param index: index of parameter
"""
return self.fun.getParamName(index)
@property
def function(self):
""" Return the underlying IFunction object
"""
return self.fun
@property
def name(self):
""" Return the name of the function
"""
return self.fun.name()
class CompositeFunctionWrapper(FunctionWrapper):
""" Wrapper class for Composite Fitting Function
"""
def __init__ (self, *args):
""" Called when creating an instance
It should not be called directly
:param *args: names of functions in composite function
"""
self.pureAddition = True
self.pureMultiplication = False
return self.initByName("CompositeFunction", *args)
def initByName(self, name, *args):
""" intialise composite function of named type.
E.g. "ProductFunction"
This function would be protected in c++
and should not be called directly except
by __init__ functions of this class and
subclasses.
:param name: name of class calling this.
:param *args: names of functions in composite function
"""
if len(args) == 1 and not isinstance(args[0], FunctionWrapper):
# We have a composite function to wrap
self.fun = args[0]
else:
self.fun = FunctionFactory.createCompositeFunction(name)
#Add the functions, checking for Composite & Product functions
for a in args:
if not isinstance(a, int):