diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/ImmutableCompositeFunction.h b/Code/Mantid/Framework/API/inc/MantidAPI/ImmutableCompositeFunction.h index 7cbf7f4b2f6ea9e0590e4e04970add7c55cd7359..24495861b32bf950093c1898bba38c268a086b80 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/ImmutableCompositeFunction.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/ImmutableCompositeFunction.h @@ -6,6 +6,8 @@ //---------------------------------------------------------------------- #include "MantidAPI/CompositeFunction.h" +#include <map> + namespace Mantid { namespace API @@ -49,11 +51,38 @@ public: virtual std::string name()const {return "ImmutableCompositeFunction";} /// Writes itself into a string std::string asString()const; + /// Set i-th parameter + void setParameter(size_t i, const double& value, bool explicitlySet = true) + {CompositeFunction::setParameter(i, value, explicitlySet);} + /// Set i-th parameter description + void setParameterDescription(size_t i, const std::string& description) + {CompositeFunction::setParameterDescription(i, description);} + /// Set parameter by name. + void setParameter(const std::string& name, const double& value, bool explicitlySet = true); + /// Set description of parameter by name. + void setParameterDescription(const std::string& name, const std::string& description); + /// Get i-th parameter + double getParameter(size_t i)const { return CompositeFunction::getParameter( i ); } + /// Get parameter by name. + double getParameter(const std::string& name)const; + /// Returns the index of parameter name + size_t parameterIndex(const std::string& name)const; + /// Returns the name of parameter i + std::string parameterName(size_t i)const; protected: - // make it protected + /// Make it protected using CompositeFunction::addFunction; + /// Overload addFunction to take a bare pointer + void addFunction(IFunction* fun); + /// Define an alias for a parameter + void setAlias(const std::string& parName, const std::string& alias); + +private: + + /// Keep paramater aliases + std::map< std::string, size_t > m_alias; }; diff --git a/Code/Mantid/Framework/API/src/ImmutableCompositeFunction.cpp b/Code/Mantid/Framework/API/src/ImmutableCompositeFunction.cpp index 7f13f9add0b03fd2f2cf1afd057c4d34049b95d1..bc0cdf58ec5f5a410fa4a04d82a9647a094f0e1a 100644 --- a/Code/Mantid/Framework/API/src/ImmutableCompositeFunction.cpp +++ b/Code/Mantid/Framework/API/src/ImmutableCompositeFunction.cpp @@ -4,6 +4,8 @@ #include "MantidKernel/Exception.h" #include "MantidAPI/ImmutableCompositeFunction.h" +#include <algorithm> + namespace Mantid { namespace API @@ -11,13 +13,117 @@ namespace API using std::size_t; + //----------------------------------------------------------------------------------------------- /** * Overridden method creates an initialization string which makes it look like a * siple function. */ std::string ImmutableCompositeFunction::asString()const { - return ""; + return IFunction::asString(); + } + + /** + * A convenience method to add a new function. + * @param fun @@ A pointer to a newly created function. + */ + void ImmutableCompositeFunction::addFunction(IFunction* fun) + { + addFunction( IFunction_sptr( fun ) ); + } + + /** + * Set parameter by name. + * @param name :: An alias or a name in CompositeFunction's style: f#.name + * @param value :: A new parameter value. + * @param explicitlySet :: The explicitly set flag. + */ + void ImmutableCompositeFunction::setParameter(const std::string& name, const double& value, bool explicitlySet) + { + auto alias = m_alias.find( name ); + if ( alias != m_alias.end() ) + { + CompositeFunction::setParameter( alias->second, value, explicitlySet ); + } + else + { + CompositeFunction::setParameter( name, value, explicitlySet ); + } + } + + /** + * Set description of parameter by name. + * @param name :: An alias or a name in CompositeFunction's style: f#.name + * @param description :: A parameter description. + */ + void ImmutableCompositeFunction::setParameterDescription(const std::string& name, const std::string& description) + { + auto alias = m_alias.find( name ); + if ( alias != m_alias.end() ) + { + CompositeFunction::setParameterDescription( alias->second, description ); + } + else + { + CompositeFunction::setParameterDescription( name, description ); + } + } + + /** + * Get parameter by name. + * @param name :: An alias or a name in CompositeFunction's style: f#.name + * @return :: The parameter value. + */ + double ImmutableCompositeFunction::getParameter(const std::string& name)const + { + auto alias = m_alias.find( name ); + if ( alias != m_alias.end() ) + { + return CompositeFunction::getParameter( alias->second ); + } + return CompositeFunction::getParameter( name ); + } + + /** + * Returns the index of parameter name + * @param name :: An alias or a name in CompositeFunction's style: f#.name + */ + size_t ImmutableCompositeFunction::parameterIndex(const std::string& name)const + { + auto alias = m_alias.find( name ); + if ( alias != m_alias.end() ) + { + return alias->second; + } + return CompositeFunction::parameterIndex( name ); + } + + /** + * Returns the alias or name of parameter i + */ + std::string ImmutableCompositeFunction::parameterName(size_t i)const + { + for(auto alias = m_alias.begin(); alias != m_alias.end(); ++alias) + { + if ( alias->second == i ) return alias->first; + } + return CompositeFunction::parameterName( i ); + } + + //----------------------------------------------------------------------------------------------- + /** + * Define an alias for a parameter. Use this method only after all member functions have been added. + * @param parName :: Fully qualified parameter name. + * @param alias :: An alias for the parameter. + */ + void ImmutableCompositeFunction::setAlias(const std::string& parName, const std::string& alias) + { + // make sure the alias is unique + if ( m_alias.count(alias) > 0 ) + { + throw Kernel::Exception::ExistsError("ImmutableCompositeFunction",alias); + } + m_alias[alias] = CompositeFunction::parameterIndex( parName ); } } // namespace API diff --git a/Code/Mantid/Framework/API/test/ImmutableCompositeFunctionTest.h b/Code/Mantid/Framework/API/test/ImmutableCompositeFunctionTest.h index 142c6d9440f27ace3a53702954901d2b371c52b8..07d0e8bd465e178468e69a2603c5dff4e0336ee4 100644 --- a/Code/Mantid/Framework/API/test/ImmutableCompositeFunctionTest.h +++ b/Code/Mantid/Framework/API/test/ImmutableCompositeFunctionTest.h @@ -6,15 +6,16 @@ #include "MantidAPI/ImmutableCompositeFunction.h" #include "MantidAPI/ParamFunction.h" #include "MantidAPI/IFunction1D.h" +#include "MantidAPI/FunctionFactory.h" using namespace Mantid; using namespace Mantid::API; -class Linear: public ParamFunction, public IFunction1D +class ImmutableCompositeFunctionTest_Linear: public ParamFunction, public IFunction1D { public: - Linear() + ImmutableCompositeFunctionTest_Linear() { declareParameter("a"); declareParameter("b"); @@ -33,7 +34,6 @@ public: } void functionDeriv1D(Jacobian* out, const double* xValues, const size_t nData) { - //throw Mantid::Kernel::Exception::NotImplementedError(""); for(size_t i=0;i<nData;i++) { out->set(static_cast<int>(i),0,1.); @@ -48,6 +48,67 @@ class ImmutableCompositeFunctionTest_Function: public ImmutableCompositeFunction public: ImmutableCompositeFunctionTest_Function(): ImmutableCompositeFunction() { + IFunction* fun1 = new ImmutableCompositeFunctionTest_Linear; + fun1->setParameter( "a", 1.0 ); + fun1->setParameter( "b", 2.0 ); + addFunction( fun1 ); + + IFunction* fun2 = new ImmutableCompositeFunctionTest_Linear; + fun2->setParameter( "a", 3.0 ); + fun2->setParameter( "b", 4.0 ); + addFunction( fun2 ); + + setAlias("f0.a", "a1"); + setAlias("f0.b", "b1"); + setAlias("f1.a", "a2"); + setAlias("f1.b", "b2"); + } + std::string name()const {return "ImmutableCompositeFunctionTest_Function";} +}; + +DECLARE_FUNCTION(ImmutableCompositeFunctionTest_Function); + +class ImmutableCompositeFunctionTest_FunctionThrow: public ImmutableCompositeFunction +{ +public: + ImmutableCompositeFunctionTest_FunctionThrow(): ImmutableCompositeFunction() + { + IFunction* fun1 = new ImmutableCompositeFunctionTest_Linear; + fun1->setParameter( "a", 1.0 ); + fun1->setParameter( "b", 2.0 ); + addFunction( fun1 ); + + IFunction* fun2 = new ImmutableCompositeFunctionTest_Linear; + fun2->setParameter( "a", 3.0 ); + fun2->setParameter( "b", 4.0 ); + addFunction( fun2 ); + + setAlias("f0.a", "a1"); + setAlias("f0.b", "b1"); + setAlias("f1.a", "a1"); // repeated alias + setAlias("f1.b", "b2"); + } +}; + +class ImmutableCompositeFunctionTest_FunctionThrow1: public ImmutableCompositeFunction +{ +public: + ImmutableCompositeFunctionTest_FunctionThrow1(): ImmutableCompositeFunction() + { + IFunction* fun1 = new ImmutableCompositeFunctionTest_Linear; + fun1->setParameter( "a", 1.0 ); + fun1->setParameter( "b", 2.0 ); + addFunction( fun1 ); + + IFunction* fun2 = new ImmutableCompositeFunctionTest_Linear; + fun2->setParameter( "a", 3.0 ); + fun2->setParameter( "b", 4.0 ); + addFunction( fun2 ); + + setAlias("f0.a", "a1"); + setAlias("f0.b", "b1"); + setAlias("f1.a", "a2"); + setAlias("f1.c", "b2"); // name doesn't exist } }; @@ -57,7 +118,174 @@ public: void testAdd() { ImmutableCompositeFunctionTest_Function icf; + TS_ASSERT_EQUALS( icf.nFunctions(), 2 ); + TS_ASSERT_EQUALS( icf.getParameter(0), 1.0 ); + TS_ASSERT_EQUALS( icf.getParameter(1), 2.0 ); + TS_ASSERT_EQUALS( icf.getParameter(2), 3.0 ); + TS_ASSERT_EQUALS( icf.getParameter(3), 4.0 ); + } + + void testFactoryCreate() + { + auto fun = FunctionFactory::Instance().createInitialized("name=ImmutableCompositeFunctionTest_Function"); + TS_ASSERT( fun ); + TS_ASSERT_EQUALS( fun->nParams(), 4 ); + TS_ASSERT_EQUALS( fun->getParameter(0), 1.0 ); + TS_ASSERT_EQUALS( fun->getParameter(1), 2.0 ); + TS_ASSERT_EQUALS( fun->getParameter(2), 3.0 ); + TS_ASSERT_EQUALS( fun->getParameter(3), 4.0 ); } + + void testFactoryInitialize() + { + std::string ini = "name=ImmutableCompositeFunctionTest_Function,a1=7.0,b1=8.0,a2=9.0,b2=0"; + auto fun = FunctionFactory::Instance().createInitialized(ini); + TS_ASSERT( fun ); + TS_ASSERT_EQUALS( fun->nParams(), 4 ); + TS_ASSERT_EQUALS( fun->getParameter(0), 7.0 ); + TS_ASSERT_EQUALS( fun->getParameter(1), 8.0 ); + TS_ASSERT_EQUALS( fun->getParameter(2), 9.0 ); + TS_ASSERT_EQUALS( fun->getParameter(3), 0.0 ); + } + + void testParameterAlias() + { + ImmutableCompositeFunctionTest_Function icf; + + TS_ASSERT_EQUALS( icf.getParameter("a1"), 1.0 ); + TS_ASSERT_EQUALS( icf.getParameter("b1"), 2.0 ); + TS_ASSERT_EQUALS( icf.getParameter("a2"), 3.0 ); + TS_ASSERT_EQUALS( icf.getParameter("b2"), 4.0 ); + + TS_ASSERT_EQUALS( icf.getParameter("f0.a"), 1.0 ); + TS_ASSERT_EQUALS( icf.getParameter("f0.b"), 2.0 ); + TS_ASSERT_EQUALS( icf.getParameter("f1.a"), 3.0 ); + TS_ASSERT_EQUALS( icf.getParameter("f1.b"), 4.0 ); + } + + void testSetParameter() + { + ImmutableCompositeFunctionTest_Function icf; + + icf.setParameter("a1", 11.0); + icf.setParameter("b1", 12.0); + icf.setParameter("a2", 13.0); + icf.setParameter("b2", 14.0); + + TS_ASSERT_EQUALS( icf.getParameter(0), 11.0 ); + TS_ASSERT_EQUALS( icf.getParameter(1), 12.0 ); + TS_ASSERT_EQUALS( icf.getParameter(2), 13.0 ); + TS_ASSERT_EQUALS( icf.getParameter(3), 14.0 ); + + } + + void testSetParameterDescription() + { + ImmutableCompositeFunctionTest_Function icf; + + icf.setParameterDescription("a1", "First a parameter"); + icf.setParameterDescription("b1", "First b parameter"); + icf.setParameterDescription("a2", "Second a parameter"); + icf.setParameterDescription("f1.b", "Second b parameter"); + + TS_ASSERT_EQUALS( icf.parameterDescription(0), "First a parameter" ); + TS_ASSERT_EQUALS( icf.parameterDescription(1), "First b parameter" ); + TS_ASSERT_EQUALS( icf.parameterDescription(2), "Second a parameter" ); + TS_ASSERT_EQUALS( icf.parameterDescription(3), "Second b parameter" ); + } + + void testParameterIndex() + { + ImmutableCompositeFunctionTest_Function icf; + + TS_ASSERT_EQUALS( icf.parameterIndex("a1"), 0 ); + TS_ASSERT_EQUALS( icf.parameterIndex("b1"), 1 ); + TS_ASSERT_EQUALS( icf.parameterIndex("a2"), 2 ); + TS_ASSERT_EQUALS( icf.parameterIndex("b2"), 3 ); + + TS_ASSERT_EQUALS( icf.parameterIndex("f0.a"), 0 ); + TS_ASSERT_EQUALS( icf.parameterIndex("f0.b"), 1 ); + TS_ASSERT_EQUALS( icf.parameterIndex("f1.a"), 2 ); + TS_ASSERT_EQUALS( icf.parameterIndex("f1.b"), 3 ); + } + + void testParameterName() + { + ImmutableCompositeFunctionTest_Function icf; + + TS_ASSERT_EQUALS( icf.parameterName(0), "a1" ); + TS_ASSERT_EQUALS( icf.parameterName(1), "b1" ); + TS_ASSERT_EQUALS( icf.parameterName(2), "a2" ); + TS_ASSERT_EQUALS( icf.parameterName(3), "b2" ); + } + + void testParameterAliasUnique() + { + TS_ASSERT_THROWS(ImmutableCompositeFunctionTest_FunctionThrow icf, Mantid::Kernel::Exception::ExistsError); + } + + void testSetAliasThrowsIfNameDoesntExist() + { + TS_ASSERT_THROWS(ImmutableCompositeFunctionTest_FunctionThrow1 icf, std::invalid_argument); + } + + void testAddTies() + { + ImmutableCompositeFunctionTest_Function icf; + + icf.addTies("b2=b1,a2=a1/5"); + TS_ASSERT( !icf.getTie( 0 ) ); + TS_ASSERT( !icf.getTie( 1 ) ); + TS_ASSERT( icf.getTie( 2 ) ); + TS_ASSERT( icf.getTie( 3 ) ); + + icf.applyTies(); + + TS_ASSERT_EQUALS( icf.getParameter(0), 1.0 ); + TS_ASSERT_EQUALS( icf.getParameter(1), 2.0 ); + TS_ASSERT_EQUALS( icf.getParameter(2), 0.2 ); + TS_ASSERT_EQUALS( icf.getParameter(3), 2.0 ); + } + + // BoundaryConstraint isn't defined (it's in CurveFitting) so this test doesn't work + void xtestConstraints() + { + ImmutableCompositeFunctionTest_Function icf; + + icf.addConstraints("0 < b1 < 5"); + TS_ASSERT( ! icf.getConstraint( 0 ) ); + TS_ASSERT( ! icf.getConstraint( 1 ) ); + TS_ASSERT( icf.getConstraint( 2 ) ); + TS_ASSERT( ! icf.getConstraint( 3 ) ); + } + + void testAsString() + { + ImmutableCompositeFunctionTest_Function icf; + + icf.setParameter(0, 11.0 ); + icf.setParameter(1, 12.0 ); + icf.setParameter(2, 13.0 ); + icf.setParameter(3, 14.0 ); + + icf.addTies("b2=b1,a2=a1/5"); + icf.applyTies(); + + TS_ASSERT_EQUALS( icf.asString(), "name=ImmutableCompositeFunctionTest_Function,a1=11,b1=12,a2=2.2,b2=12,ties=(a2=a1/5,b2=b1)" ); + + auto fun = FunctionFactory::Instance().createInitialized( icf.asString() ); + TS_ASSERT( fun ); + TS_ASSERT_EQUALS( fun->nParams(), 4 ); + TS_ASSERT_EQUALS( fun->getParameter(0), 11.0 ); + TS_ASSERT_EQUALS( fun->getParameter(1), 12.0 ); + TS_ASSERT_EQUALS( fun->getParameter(2), 2.2 ); + TS_ASSERT_EQUALS( fun->getParameter(3), 12.0 ); + TS_ASSERT( ! fun->getTie( 0 ) ); + TS_ASSERT( ! fun->getTie( 1 ) ); + TS_ASSERT( fun->getTie( 2 ) ); + TS_ASSERT( fun->getTie( 3 ) ); + } + }; #endif /*IMMUTABLECOMPOSITEFUNCTIONTEST_H_*/