Commit 6d631bcd authored by Roman Tolchenov's avatar Roman Tolchenov
Browse files

Added method asString() and std::string operator to IFunction which enables...

Added method asString() and std::string operator to IFunction which enables setting Function property of Fit algorithm with a function object or pointer. Renamed InputParameters property to Function in Fit. re #1003
parent 1c331003
......@@ -47,8 +47,12 @@ public:
///Destructor
virtual ~CompositeFunction();
/// Initialize the function providing it the workspace
virtual void initialize(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int spec,int xMin,int xMax);
/// Set the workspace
void setWorkspace(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int spec,int xMin,int xMax);
/// Returns the function's name
std::string name()const{return "CompositeFunction";}
/// Writes itself into a string
std::string asString()const;
/// Function you want to fit to.
void function(double* out, const double* xValues, const int& nData);
......
......@@ -60,10 +60,7 @@ namespace API
/**Creates an instance of a function
* @param type The function's type
*/
IFunction* createFunction(const std::string& type) const
{
return createUnwrapped(type);
}
IFunction* createFunction(const std::string& type) const;
private:
friend struct Mantid::Kernel::CreateUsingNew<FunctionFactoryImpl>;
......
......@@ -69,9 +69,17 @@ public:
/// Virtual destructor
virtual ~IFunction();
/// Initialize the function providing it the workspace
virtual void initialize(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int wi,int xMin,int xMax);
/// Writes itself into a string
virtual std::string asString()const;
/// The string operator
virtual operator std::string()const{return asString();}
/// Set the workspace
virtual void setWorkspace(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int wi,int xMin,int xMax);
/// Iinialize the function
virtual void initialize(){this->init();}
/// Returns the function's name
virtual std::string name()const = 0;
/// Function you want to fit to.
virtual void function(double* out, const double* xValues, const int& nData) = 0;
/// Derivatives of function with respect to active parameters
......@@ -89,7 +97,7 @@ public:
/// Get parameter by name.
virtual double getParameter(const std::string& name)const;
/// Total number of parameters
virtual int nParams()const{return m_parameters.size();};
virtual int nParams()const{return m_parameters.size();}
/// Returns the index of parameter name
virtual int parameterIndex(const std::string& name)const;
/// Returns the name of parameter i
......@@ -163,6 +171,9 @@ public:
protected:
};
/// Overload operator <<
DLLExport std::ostream& operator<<(std::ostream& ostr,const IFunction& f);
} // namespace API
} // namespace Mantid
......
......@@ -49,6 +49,17 @@ void CompositeFunction::init()
{
}
/// Writes itself into a string
std::string CompositeFunction::asString()const
{
std::ostringstream ostr;
for(int i=0;i<nFunctions();i++)
{
ostr << getFunction(i)->asString() << ';';
}
return ostr.str();
}
/// Function you want to fit to.
void CompositeFunction::function(double* out, const double* xValues, const int& nData)
{
......@@ -361,11 +372,11 @@ void CompositeFunction::parseName(const std::string& varName,int& index, std::st
* @param xMin The minimum bin index of spectrum spec that will be used in fitting
* @param xMax The maximum bin index of spectrum spec that will be used in fitting
*/
void CompositeFunction::initialize(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int spec,int xMin,int xMax)
void CompositeFunction::setWorkspace(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int spec,int xMin,int xMax)
{
IFunction::initialize(workspace,spec,xMin,xMax);
IFunction::setWorkspace(workspace,spec,xMin,xMax);
for(int i=0;i<nFunctions();i++)
getFunction(i)->initialize(workspace,spec,xMin,xMax);
getFunction(i)->setWorkspace(workspace,spec,xMin,xMax);
}
......
......@@ -14,5 +14,12 @@ namespace Mantid
{
}
} // namespace DataObjects
IFunction* FunctionFactoryImpl::createFunction(const std::string& type) const
{
IFunction* fun = createUnwrapped(type);
fun->initialize();
return fun;
}
} // namespace API
} // namespace Mantid
......@@ -54,13 +54,12 @@ void IFunction::functionDeriv(Jacobian* out, const double* xValues, const int& n
}
/// Initialize the function providing it the workspace
void IFunction::initialize(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int wi,int xMin,int xMax)
void IFunction::setWorkspace(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int wi,int xMin,int xMax)
{
m_workspace = workspace;
m_workspaceIndex = wi;
m_xMinIndex = xMin;
m_xMaxIndex = xMax;
this->init();
}
......@@ -308,5 +307,28 @@ void IFunction::calJacobianForCovariance(Jacobian* out, const double* xValues, c
this->functionDeriv(out,xValues,nData);
}
/**
* Writes a string that can be used in Fit.Function to create a copy of this function
*/
std::string IFunction::asString()const
{
std::ostringstream ostr;
ostr << "name="<<this->name();
for(int i=0;i<nParams();i++)
ostr<<','<<parameterName(i)<<'='<<parameter(i);
return ostr.str();
}
/**
* Operator <<
* @param ostr The output stream
* @param f The function
*/
std::ostream& operator<<(std::ostream& ostr,const IFunction& f)
{
ostr << f.asString();
return ostr;
}
} // namespace API
} // namespace Mantid
......@@ -10,15 +10,16 @@
using namespace Mantid;
using namespace Mantid::API;
class Gauss: public IPeakFunction
class ParameterTieTest_Gauss: public IPeakFunction
{
public:
void init()
ParameterTieTest_Gauss()
{
declareParameter("c");
declareParameter("h",1.);
declareParameter("s",1.);
}
std::string name()const{return "ParameterTieTest_Gauss";}
void function(double* out, const double* xValues, const int& nData)
{
double c = getParameter("c");
......@@ -77,14 +78,15 @@ public:
};
class Linear: public IFunction
class ParameterTieTest_Linear: public IFunction
{
public:
void init()
ParameterTieTest_Linear()
{
declareParameter("a");
declareParameter("b");
}
std::string name()const{return "ParameterTieTest_Linear";}
void function(double* out, const double* xValues, const int& nData)
{
double a = getParameter("a");
......@@ -112,12 +114,8 @@ public:
void testComposite()
{
CompositeFunction mfun;
Gauss *g1 = new Gauss(),*g2 = new Gauss();
Linear *bk = new Linear();
g1->init();
g2->init();
bk->init();
ParameterTieTest_Gauss *g1 = new ParameterTieTest_Gauss(),*g2 = new ParameterTieTest_Gauss();
ParameterTieTest_Linear *bk = new ParameterTieTest_Linear();
mfun.addFunction(bk);
mfun.addFunction(g1);
......@@ -148,8 +146,7 @@ public:
void testSimple()
{
Linear bk;
bk.init();
ParameterTieTest_Linear bk;
bk.getParameter("a") = 0.8;
bk.getParameter("b") = 0.;
......
......@@ -276,6 +276,10 @@
RelativePath=".\test\BackToBackExponential1DTest.h"
>
</File>
<File
RelativePath=".\test\CompositeFunctionTest.h"
>
</File>
<File
RelativePath=".\inc\MantidCurveFitting\Fit.h"
>
......@@ -284,6 +288,14 @@
RelativePath=".\inc\MantidCurveFitting\Fit1D.h"
>
</File>
<File
RelativePath=".\test\FitTest.h"
>
</File>
<File
RelativePath=".\test\FunctionTest.h"
>
</File>
<File
RelativePath=".\inc\MantidCurveFitting\Gaussian.h"
>
......
......@@ -9,10 +9,6 @@
namespace Mantid
{
namespace DtaaObjects
{
class Workspace2D;
}
namespace CurveFitting
{
/**
......@@ -26,7 +22,7 @@ namespace Mantid
<LI> StartX - Lowest value of x data array </LI>
<LI> EndX - Highest value of x data array </LI>
<LI> InputParameters -
<LI> Function -
<LI> MaxIterations - The spectrum to fit (default 500)</LI>
<LI> Output Status - whether the fit was successful. Direction::Output</LI>
......@@ -98,7 +94,7 @@ namespace Mantid
virtual void afterDataRangedDetermined(const int& m_minX, const int& m_maxX){};
// Process input parameters and create the fitting function.
void processParameters(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int spec,int xMin,int xMax);
void processParameters();
/// Number of parameters.
size_t nParams()const{return m_function->nActive();}
......
......@@ -65,6 +65,7 @@ namespace Mantid
/// overwrite IFunction base class methods
std::string name()const{return "Gaussian";}
virtual void init();
virtual void calJacobianForCovariance(API::Jacobian* out, const double* xValues, const int& nData);
virtual void setActiveParameter(int i,double value);
......
......@@ -58,6 +58,7 @@ namespace Mantid
virtual void setWidth(const double w);
/// overwrite IFunction base class methods
std::string name()const{return "IkedaCarpenterPV";}
virtual void init();
virtual void function(double* out, const double* xValues, const int& nData);
......
......@@ -50,6 +50,7 @@ namespace Mantid
virtual ~LinearBackground() {};
/// overwrite IFunction base class methods
std::string name()const{return "LinearBackground";}
virtual void init();
virtual void function(double* out, const double* xValues, const int& nData);
virtual void functionDeriv(API::Jacobian* out, const double* xValues, const int& nData);
......
......@@ -62,6 +62,7 @@ namespace Mantid
/// overwrite IFunction base class methods
std::string name()const{return "Lorentzian";}
virtual void init();
virtual void function(double* out, const double* xValues, const int& nData);
virtual void functionDeriv(API::Jacobian* out, const double* xValues, const int& nData);
......
......@@ -51,6 +51,7 @@ namespace Mantid
virtual ~Quadratic() {};
/// overwrite IFunction base class methods
std::string name()const{return "Quadratic";}
virtual void init();
virtual void function(double* out, const double* xValues, const int& nData);
virtual void functionDeriv(API::Jacobian* out, const double* xValues, const int& nData);
......
......@@ -195,8 +195,8 @@ namespace CurveFitting
"A value in, or on the high x boundary of, the last bin the fitting range\n"
"(default the highest value of x)" );
declareProperty("InputParameters","","Parameters defining the fitting function and its initial values" );
declareProperty("Ties","","Math expressions that tie paraemters to other parameters or to constants" );
declareProperty("Function","","Parameters defining the fitting function and its initial values" );
declareProperty("Ties","","Math expressions that tie parameters to other parameters or to constants" );
declareProperty("MaxIterations", 500, mustBePositive->clone(),
"Stop after this number of iterations if a good fit is not found" );
......@@ -279,10 +279,13 @@ namespace CurveFitting
afterDataRangedDetermined(m_minX, m_maxX);
processParameters(localworkspace,histNumber,m_minX, m_maxX);
// Process the Function property and create the function using FunctionFactory
processParameters();
if (m_function == NULL)
throw std::runtime_error("Function has not been set.");
throw std::runtime_error("Function was not set.");
m_function->setWorkspace(localworkspace,histNumber,m_minX, m_maxX);
// check if derivative defined in derived class
bool isDerivDefined = true;
......@@ -611,14 +614,14 @@ namespace CurveFitting
/**
* Process input parameters and create the fitting function.
*/
void Fit::processParameters(boost::shared_ptr<const DataObjects::Workspace2D> workspace,int spec,int xMin,int xMax)
void Fit::processParameters()
{
// Parameters of different functions are separated by ';'. Parameters of the same function
// are separated by ','. parameterName=value pairs are used to set a parameter value. For each function
// "function" parameter must be set to a function name. E.g.
// InputParameters = "function=LinearBackground,A0=0,A1=1; function = Gaussian, PeakCentre=10.,Sigma=1"
std::string input = getProperty("InputParameters");
// Function = "name=LinearBackground,A0=0,A1=1; function = Gaussian, PeakCentre=10.,Sigma=1"
std::string input = getProperty("Function");
if (input.empty()) return;
typedef Poco::StringTokenizer tokenizer;
......@@ -648,12 +651,11 @@ namespace CurveFitting
}
}
std::string functionName = param["function"];
std::string functionName = param["name"];
if (functionName.empty())
throw std::runtime_error("Function is not defined");
API::IFunction* fun = API::FunctionFactory::Instance().createFunction(functionName);
fun->initialize(workspace,spec,xMin,xMax);
if (isComposite)
static_cast<API::CompositeFunction*>(function)->addFunction(fun);
......@@ -663,7 +665,7 @@ namespace CurveFitting
std::map<std::string,std::string>::const_iterator par = param.begin();
for(;par!=param.end();++par)
{
if (par->first != "function")
if (par->first != "name")
{
//fun->getParameter(par->first) = boost::lexical_cast<double>(par->second);
fun->getParameter(par->first) = atof(par->second.c_str());
......
......@@ -33,6 +33,9 @@ public:
declareParameter("h",1.);
declareParameter("s",1.);
}
std::string name()const{return "Gauss";}
void function(double* out, const double* xValues, const int& nData)
{
double c = getParameter("c");
......@@ -107,6 +110,9 @@ public:
declareParameter("a");
declareParameter("b");
}
std::string name()const{return "Linear";}
void function(double* out, const double* xValues, const int& nData)
{
double a = getParameter("a");
......@@ -196,7 +202,7 @@ public:
addNoise(ws,0.1);
storeWS("mfun",ws);
mfun->initialize(ws,7,12,3);
mfun->setWorkspace(ws,7,12,3);
mfun->testInit(ws,7,12,3);
Fit alg;
......@@ -206,7 +212,8 @@ public:
alg.setPropertyValue("WorkspaceIndex","0");
alg.setPropertyValue("Output","out");
alg.setFunction(mfun);
alg.execute();
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());
WS_type outWS = getWS("out_Workspace");
const Mantid::MantidVec& Y00 = ws->readY(0);
......
......@@ -45,14 +45,16 @@ public:
alg.setPropertyValue("WorkspaceIndex","0");
alg.setPropertyValue("Output","out");
std::string params = "";
params += "function=LinearBackground,A0=1,A1=0;";
params += "function=Gaussian, PeakCentre=4.1,Height=1.1,Sigma=0.5;";
params += "function=Gaussian, PeakCentre=6.1,Height=3.1,Sigma=0.5;";
params += "name=LinearBackground,A0=1,A1=0;";
params += "name=Gaussian, PeakCentre=4.1,Height=1.1,Sigma=0.5;";
params += "name=Gaussian, PeakCentre=6.1,Height=3.1,Sigma=0.5;";
alg.setPropertyValue("InputParameters",params);
alg.setPropertyValue("Function",params);
//alg.setPropertyValue("Ties","f1.Sigma=f2.Sigma/3");
alg.execute();
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());
WS_type outWS = getWS("out_Workspace");
const Mantid::MantidVec& Y00 = ws->readY(0);
......@@ -122,14 +124,16 @@ public:
alg.setPropertyValue("WorkspaceIndex","0");
alg.setPropertyValue("Output","out");
std::string params = "";
params += "function=LinearBackground,A0=1,A1=0;";
params += "function=Gaussian, PeakCentre=4.1,Height=1.1,Sigma=0.5;";
params += "function=Gaussian, PeakCentre=6.1,Height=3.1,Sigma=0.5;";
params += "name=LinearBackground,A0=1,A1=0;";
params += "name=Gaussian, PeakCentre=4.1,Height=1.1,Sigma=0.5;";
params += "name=Gaussian, PeakCentre=6.1,Height=3.1,Sigma=0.5;";
alg.setPropertyValue("InputParameters",params);
alg.setPropertyValue("Function",params);
alg.setPropertyValue("Ties","f1.Sigma=f2.Sigma/3");
alg.execute();
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());
WS_type outWS = getWS("out_Workspace");
TWS_type outParams = getTWS("out_Parameters");
......
......@@ -26,6 +26,7 @@ public:
declareParameter("h",1.);
declareParameter("s",1.);
}
std::string name()const{return "FunctionTestGauss";}
void function(double* out, const double* xValues, const int& nData)
{
double c = getParameter("c");
......@@ -91,6 +92,7 @@ public:
}
};
DECLARE_FUNCTION(FunctionTestGauss);
class Exp
{
......@@ -124,7 +126,7 @@ public:
WS_type ws = mkWS(Exp(),1,0,10,0.1);
storeWS("Exp",ws);
g->initialize(ws,12,7,9);
g->setWorkspace(ws,12,7,9);
g->testInit(ws,12,7,9);
Fit alg;
......@@ -134,7 +136,8 @@ public:
alg.setPropertyValue("WorkspaceIndex","0");
alg.setPropertyValue("Output","out");
alg.setFunction(g);
alg.execute();
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());
WS_type outWS = getWS("out_Workspace");
const Mantid::MantidVec& Y00 = ws->readY(0);
......@@ -208,7 +211,8 @@ public:
alg.setPropertyValue("InputWorkspace","Exp");
alg.setPropertyValue("WorkspaceIndex","0");
alg.setFunction(g);
alg.execute();
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());
TS_ASSERT_DELTA(g->parameter(0),5,0.0001);
TS_ASSERT_DELTA(g->parameter(1),0.8944,0.0001);
......@@ -217,6 +221,80 @@ public:
removeWS("Exp");
}
void testFitString()
{
FunctionTestGauss g;
g.getParameter("c") = 5.5;
g.getParameter("h") = 1.2;
g.getParameter("s") = 1.;
TS_ASSERT_EQUALS(g.nParams(),3);
TS_ASSERT_EQUALS(g.nActive(),3);
TS_ASSERT_EQUALS(g.parameter(0),5.5);
TS_ASSERT_EQUALS(g.parameter(1),1.2);
TS_ASSERT_EQUALS(g.parameter(2),1.);
WS_type ws = mkWS(Exp(),1,0,10,0.1);
storeWS("Exp",ws);
Fit alg;
alg.initialize();
alg.setPropertyValue("InputWorkspace","Exp");
alg.setPropertyValue("WorkspaceIndex","0");
alg.setPropertyValue("Output","out");
alg.setPropertyValue("Function",g);
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());
WS_type outWS = getWS("out_Workspace");
const Mantid::MantidVec& Y00 = ws->readY(0);
const Mantid::MantidVec& Y0 = outWS->readY(0);
const Mantid::MantidVec& Y = outWS->readY(1);
const Mantid::MantidVec& R = outWS->readY(2);
for(int i=0;i<Y.size();i++)
{
TS_ASSERT_EQUALS(Y00[i],Y0[i]);
TS_ASSERT_DELTA(Y0[i],Y[i],0.001);
TS_ASSERT_DIFFERS(R[i],0);
}
TS_ASSERT_EQUALS(g.parameterName(0),"c");
TS_ASSERT_DELTA(g.parameter(0),5.5,0.00001);
TS_ASSERT_EQUALS(g.parameterName(1),"h");
TS_ASSERT_DELTA(g.parameter(1),1.2,0.00001);
TS_ASSERT_EQUALS(g.parameterName(2),"s");
TS_ASSERT_DELTA(g.parameter(2),1.,0.00001);
TWS_type outParams = getTWS("out_Parameters");
TS_ASSERT(outParams);
TS_ASSERT_EQUALS(outParams->rowCount(),3);
TS_ASSERT_EQUALS(outParams->columnCount(),2);
TableRow row = outParams->getFirstRow();
TS_ASSERT_EQUALS(row.String(0),"c");
TS_ASSERT_DELTA(row.Double(1),5,0.00001);
row = outParams->getRow(1);
TS_ASSERT_EQUALS(row.String(0),"h");
TS_ASSERT_DELTA(row.Double(1),1,0.000001);
row = outParams->getRow(2);
TS_ASSERT_EQUALS(row.String(0),"s");
TS_ASSERT_DELTA(row.Double(1),3,0.00001);
removeWS("Exp");
removeWS("out_Workspace");
removeWS("out_Parameters");
}
private:
template<class Funct>
......
......@@ -122,7 +122,8 @@ public:
// set up fitting function and pass to Fit