/** Replace a function with a new one. The old function is deleted. * @param i :: The index of the function to replace * @param f :: A pointer to the new function */ void CompositeFunction::replaceFunction(size_t i, IFunction_sptr f) { if (i >= nFunctions()) throw std::out_of_range("Function index out of range."); IFunction_sptr fun = getFunction(i); size_t np_old = fun->nParams(); size_t np_new = f->nParams(); // Modify function indeces: The new function may have different number of // parameters { auto itFun = std::find(m_IFunction.begin(), m_IFunction.end(), i); if (itFun != m_IFunction.end()) // functions must have at least 1 parameter { if (np_old > np_new) { m_IFunction.erase(itFun, itFun + np_old - np_new); } else if (np_old < np_new) { m_IFunction.insert(itFun, np_new - np_old, i); } } else if (np_new > 0) // it could happen if the old function is an empty // CompositeFunction { itFun = std::find_if(m_IFunction.begin(), m_IFunction.end(), std::bind2nd(std::greater<size_t>(), i)); m_IFunction.insert(itFun, np_new, i); } } size_t dnp = np_new - np_old; m_nParams += dnp; // Shift the parameter offsets down by the total number of i-th function's // params for (size_t j = i + 1; j < nFunctions(); j++) { m_paramOffsets[j] += dnp; } m_functions[i] = f; }
/// Get number of domains required by this function size_t CompositeFunction::getNumberDomains() const { auto n = nFunctions(); if (n == 0) { return 1; } size_t nd = getFunction(0)->getNumberDomains(); for (size_t iFun = 1; iFun < n; ++iFun) { if (getFunction(0)->getNumberDomains() != nd) { throw std::runtime_error("CompositeFunction has members with " "inconsistent domain numbers."); } } return nd; }
/** * Returns the index of parameter if the ref points to one of the member * function * @param ref :: A reference to a parameter * @return Parameter index or number of nParams() if parameter not found */ size_t CompositeFunction::getParameterIndex(const ParameterReference &ref) const { if (ref.getLocalFunction() == this && ref.getLocalIndex() < nParams()) { return ref.getLocalIndex(); } for (size_t iFun = 0; iFun < nFunctions(); iFun++) { IFunction_sptr fun = getFunction(iFun); size_t iLocalIndex = fun->getParameterIndex(ref); if (iLocalIndex < fun->nParams()) { return m_paramOffsets[iFun] + iLocalIndex; } } return nParams(); }
/// Function you want to fit to. /// @param domain :: The domain void FunctionComposition::function(const FunctionDomain& domain, FunctionValues& values)const { FunctionValues tmp(domain); values.setCalculated(1.0); for(size_t iFun = 0; iFun < nFunctions(); ++iFun) { FunctionDomain1DView view(tmp.getPointerToCalculated(0),domain.size()); if ( iFun == 0 ) { m_functions[ iFun ]->function(domain,tmp); } else { } } }
/** * Set a value to attribute attName */ void MultiDomainFunction::setLocalAttribute(size_t i, const std::string& attName,const IFunction::Attribute& att) { if (attName != "domains") { throw std::invalid_argument("MultiDomainFunction does not have attribute " + attName); } if (i >= nFunctions()) { throw std::out_of_range("Function index is out of range."); } std::string value = att.asString(); auto it = m_domains.find(i); if (value == "All") {// fit to all domains if (it != m_domains.end()) { m_domains.erase(it); } return; } else if (value == "i") {// fit to domain with the same index as the function setDomainIndex(i,i); return; } else if (value.empty()) {// do not fit to any domain setDomainIndices(i,std::vector<size_t>()); } // fit to a selection of domains std::vector<size_t> indx; Expression list; list.parse(value); list.toList(); for(size_t k = 0; k < list.size(); ++k) { indx.push_back(boost::lexical_cast<size_t>(list[k].name())); } setDomainIndices(i,indx); }
/// Function you want to fit to. /// @param domain :: The buffer for writing the calculated values. Must be big enough to accept dataSize() values void MultiDomainFunction::function(const FunctionDomain& domain, FunctionValues& values)const { // works only on CompositeDomain if (!dynamic_cast<const CompositeDomain*>(&domain)) { throw std::invalid_argument("Non-CompositeDomain passed to MultiDomainFunction."); } const CompositeDomain& cd = dynamic_cast<const CompositeDomain&>(domain); // domain must not have less parts than m_maxIndex if (cd.getNParts() <= m_maxIndex) { throw std::invalid_argument("CompositeDomain has too few parts (" + boost::lexical_cast<std::string>(cd.getNParts()) + ") for MultiDomainFunction (max index " + boost::lexical_cast<std::string>(m_maxIndex) + ")."); } // domain and values must be consistent if (cd.size() != values.size()) { throw std::invalid_argument("MultiDomainFunction: domain and values have different sizes."); } countValueOffsets(cd); // evaluate member functions values.zeroCalculated(); for(size_t iFun = 0; iFun < nFunctions(); ++iFun) { // find the domains member function must be applied to std::vector<size_t> domains; getFunctionDomains(iFun, cd, domains); for(auto i = domains.begin(); i != domains.end(); ++i) { const FunctionDomain& d = cd.getDomain(*i); FunctionValues tmp(d); getFunction(iFun)->function( d, tmp ); values.addToCalculated(m_valueOffsets[*i],tmp); } } }
/** * Initialize the function with the workspace(s). * @param function :: A function to initialize. */ void MultiDomainCreator::initFunction(API::IFunction_sptr function) { auto mdFunction = boost::dynamic_pointer_cast<API::MultiDomainFunction>(function); if ( mdFunction ) { // loop over member functions and init them for(size_t iFun = 0; iFun < mdFunction->nFunctions(); ++iFun) { std::vector<size_t> domainIndices; // get domain indices for this function mdFunction->getDomainIndices(iFun,m_creators.size(),domainIndices); if ( !domainIndices.empty() ) { if ( domainIndices.size() != 1 ) { g_log.warning() << "Function #" << iFun << " applies to multiple domains." << std::endl; g_log.warning() << "Only one of the domains is used to set workspace." << std::endl; } size_t index = domainIndices[0]; if ( index >= m_creators.size() ) { std::stringstream msg; msg << "Domain index is out of range. (Function #" << iFun << ")"; throw std::runtime_error(msg.str()); } m_creators[index]->initFunction( mdFunction->getFunction(iFun) ); } else { g_log.warning() << "Function #" << iFun << " doesn't apply to any domain" << std::endl; } } } else { IDomainCreator::initFunction(function); } }
/// Return a value of attribute attName IFunction::Attribute MultiDomainFunction::getLocalAttribute(size_t i, const std::string &attName) const { if (attName != "domains") { throw std::invalid_argument("MultiDomainFunction does not have attribute " + attName); } if (i >= nFunctions()) { throw std::out_of_range("Function index is out of range."); } auto it = m_domains.find(i); if (it == m_domains.end()) { return IFunction::Attribute("All"); } else if (it->second.size() == 1 && it->second.front() == i) { return IFunction::Attribute("i"); } else if (!it->second.empty()) { std::string out(boost::lexical_cast<std::string>(it->second.front())); for (auto i = it->second.begin() + 1; i != it->second.end(); ++it) { out += "," + boost::lexical_cast<std::string>(*i); } return IFunction::Attribute(out); } return IFunction::Attribute(""); }
/// Derivatives of function with respect to active parameters void MultiDomainFunction::functionDeriv(const FunctionDomain &domain, Jacobian &jacobian) { // works only on CompositeDomain if (!dynamic_cast<const CompositeDomain *>(&domain)) { throw std::invalid_argument( "Non-CompositeDomain passed to MultiDomainFunction."); } if (getAttribute("NumDeriv").asBool()) { calNumericalDeriv(domain, jacobian); } else { const CompositeDomain &cd = dynamic_cast<const CompositeDomain &>(domain); // domain must not have less parts than m_maxIndex if (cd.getNParts() < m_maxIndex) { throw std::invalid_argument( "CompositeDomain has too few parts (" + boost::lexical_cast<std::string>(cd.getNParts()) + ") for MultiDomainFunction (max index " + boost::lexical_cast<std::string>(m_maxIndex) + ")."); } countValueOffsets(cd); // evaluate member functions derivatives for (size_t iFun = 0; iFun < nFunctions(); ++iFun) { // find the domains member function must be applied to std::vector<size_t> domains; getDomainIndices(iFun, cd.getNParts(), domains); for (auto i = domains.begin(); i != domains.end(); ++i) { const FunctionDomain &d = cd.getDomain(*i); PartialJacobian J(&jacobian, m_valueOffsets[*i], paramOffset(iFun)); getFunction(iFun)->functionDeriv(d, J); } } } }
/** * Clear the ties. */ void CompositeFunction::clearTies() { IFunction::clearTies(); for (size_t i = 0; i < nFunctions(); i++) { getFunction(i)->clearTies(); } }
/** * Called at the end of an iteration. Call iterationFinished() of the members. */ void MultiDomainFunction::iterationFinished() { for (size_t iFun = 0; iFun < nFunctions(); ++iFun) { getFunction(iFun)->iterationFinished(); } }
/** * Called at the start of each iteration. Call iterationStarting() of the * members. */ void MultiDomainFunction::iterationStarting() { for (size_t iFun = 0; iFun < nFunctions(); ++iFun) { getFunction(iFun)->iterationStarting(); } }
/** * Apply the ties. */ void CompositeFunction::applyTies() { for (size_t i = 0; i < nFunctions(); i++) { getFunction(i)->applyTies(); } }
/** * @param i :: The index of the function * @return function at the requested index */ IFunction_sptr CompositeFunction::getFunction(std::size_t i) const { if (i >= nFunctions()) { throw std::out_of_range("Function index out of range."); } return m_functions[i]; }