/// Create a domain from the input workspace void MultiDomainCreator::createDomain( boost::shared_ptr<API::FunctionDomain>& domain, boost::shared_ptr<API::IFunctionValues>& ivalues, size_t i0) { if (m_workspacePropertyNames.size() != m_creators.size()) { throw std::runtime_error("Cannot create JointDomain: number of workspaces does not match " "the number of creators"); } auto jointDomain = new API::JointDomain; API::IFunctionValues_sptr values; i0 = 0; for(auto c = m_creators.begin(); c != m_creators.end(); ++c) { if (!(*c)) { throw std::runtime_error("Missing domain creator"); } API::FunctionDomain_sptr domain; (**c).createDomain(domain,values,i0); jointDomain->addDomain(domain); i0 += domain->size(); } domain.reset(jointDomain); ivalues = values; }
/** Set fitting function, domain it will operate on, and container for values. * @param function :: The fitting function. * @param domain :: The domain for the function. * @param values :: The FunctionValues object which receives the calculated * values and * also contains the data to fit to and the fitting weights (reciprocal * errors). */ void CostFuncFitting::setFittingFunction(API::IFunction_sptr function, API::FunctionDomain_sptr domain, API::FunctionValues_sptr values) { if (domain->size() != values->size()) { throw std::runtime_error( "Function domain and values objects are incompatible."); } m_function = function; m_domain = domain; m_values = values; m_indexMap.clear(); for (size_t i = 0; i < m_function->nParams(); ++i) { if (m_function->isActive(i)) { m_indexMap.push_back(i); } API::IConstraint *c = m_function->getConstraint(i); if (c) { c->setParamToSatisfyConstraint(); } } }
/** Executes the algorithm * * @throw runtime_error Thrown if algorithm cannot execute */ void Fit::execConcrete() { std::string ties = getPropertyValue("Ties"); if (!ties.empty()) { m_function->addTies(ties); } std::string contstraints = getPropertyValue("Constraints"); if (!contstraints.empty()) { m_function->addConstraints(contstraints); } // prepare the function for a fit m_function->setUpForFit(); API::FunctionDomain_sptr domain; API::FunctionValues_sptr values; m_domainCreator->createDomain(domain, values); // do something with the function which may depend on workspace m_domainCreator->initFunction(m_function); // get the minimizer std::string minimizerName = getPropertyValue("Minimizer"); API::IFuncMinimizer_sptr minimizer = API::FuncMinimizerFactory::Instance().createMinimizer(minimizerName); // Try to retrieve optional properties int intMaxIterations = getProperty("MaxIterations"); const size_t maxIterations = static_cast<size_t>(intMaxIterations); // get the cost function which must be a CostFuncFitting boost::shared_ptr<CostFuncFitting> costFunc = boost::dynamic_pointer_cast<CostFuncFitting>( API::CostFunctionFactory::Instance().create( getPropertyValue("CostFunction"))); costFunc->setFittingFunction(m_function, domain, values); minimizer->initialize(costFunc, maxIterations); const int64_t nsteps = maxIterations * m_function->estimateNoProgressCalls(); API::Progress prog(this, 0.0, 1.0, nsteps); m_function->setProgressReporter(&prog); // do the fitting until success or iteration limit is reached size_t iter = 0; bool success = false; std::string errorString; g_log.debug("Starting minimizer iteration\n"); while (iter < maxIterations) { g_log.debug() << "Starting iteration " << iter << "\n"; m_function->iterationStarting(); if (!minimizer->iterate(iter)) { errorString = minimizer->getError(); g_log.debug() << "Iteration stopped. Minimizer status string=" << errorString << "\n"; success = errorString.empty() || errorString == "success"; if (success) { errorString = "success"; } break; } prog.report(); m_function->iterationFinished(); ++iter; } g_log.debug() << "Number of minimizer iterations=" << iter << "\n"; minimizer->finalize(); if (iter >= maxIterations) { if (!errorString.empty()) { errorString += '\n'; } errorString += "Failed to converge after " + boost::lexical_cast<std::string>(maxIterations) + " iterations."; } // return the status flag setPropertyValue("OutputStatus", errorString); // degrees of freedom size_t dof = domain->size() - costFunc->nParams(); if (dof == 0) dof = 1; double rawcostfuncval = minimizer->costFunctionVal(); double finalCostFuncVal = rawcostfuncval / double(dof); setProperty("OutputChi2overDoF", finalCostFuncVal); // fit ended, creating output // get the workspace API::Workspace_const_sptr ws = getProperty("InputWorkspace"); bool doCreateOutput = getProperty("CreateOutput"); std::string baseName = getPropertyValue("Output"); if (!baseName.empty()) { doCreateOutput = true; } bool doCalcErrors = getProperty("CalcErrors"); if (doCreateOutput) { doCalcErrors = true; } if (costFunc->nParams() == 0) { doCalcErrors = false; } GSLMatrix covar; if (doCalcErrors) { // Calculate the covariance matrix and the errors. costFunc->calCovarianceMatrix(covar); costFunc->calFittingErrors(covar, rawcostfuncval); } if (doCreateOutput) { copyMinimizerOutput(*minimizer); if (baseName.empty()) { baseName = ws->name(); if (baseName.empty()) { baseName = "Output"; } } baseName += "_"; declareProperty( new API::WorkspaceProperty<API::ITableWorkspace>( "OutputNormalisedCovarianceMatrix", "", Kernel::Direction::Output), "The name of the TableWorkspace in which to store the final covariance " "matrix"); setPropertyValue("OutputNormalisedCovarianceMatrix", baseName + "NormalisedCovarianceMatrix"); Mantid::API::ITableWorkspace_sptr covariance = Mantid::API::WorkspaceFactory::Instance().createTable("TableWorkspace"); covariance->addColumn("str", "Name"); // set plot type to Label = 6 covariance->getColumn(covariance->columnCount() - 1)->setPlotType(6); // std::vector<std::string> paramThatAreFitted; // used for populating 1st // "name" column for (size_t i = 0; i < m_function->nParams(); i++) { if (m_function->isActive(i)) { covariance->addColumn("double", m_function->parameterName(i)); // paramThatAreFitted.push_back(m_function->parameterName(i)); } } size_t np = m_function->nParams(); size_t ia = 0; for (size_t i = 0; i < np; i++) { if (m_function->isFixed(i)) continue; Mantid::API::TableRow row = covariance->appendRow(); row << m_function->parameterName(i); size_t ja = 0; for (size_t j = 0; j < np; j++) { if (m_function->isFixed(j)) continue; if (j == i) row << 100.0; else { if (!covar.gsl()) { throw std::runtime_error( "There was an error while allocating the (GSL) covariance " "matrix " "which is needed to produce fitting error results."); } row << 100.0 * covar.get(ia, ja) / sqrt(covar.get(ia, ia) * covar.get(ja, ja)); } ++ja; } ++ia; } setProperty("OutputNormalisedCovarianceMatrix", covariance); // create output parameter table workspace to store final fit parameters // including error estimates if derivative of fitting function defined declareProperty(new API::WorkspaceProperty<API::ITableWorkspace>( "OutputParameters", "", Kernel::Direction::Output), "The name of the TableWorkspace in which to store the " "final fit parameters"); setPropertyValue("OutputParameters", baseName + "Parameters"); Mantid::API::ITableWorkspace_sptr result = Mantid::API::WorkspaceFactory::Instance().createTable("TableWorkspace"); result->addColumn("str", "Name"); // set plot type to Label = 6 result->getColumn(result->columnCount() - 1)->setPlotType(6); result->addColumn("double", "Value"); result->addColumn("double", "Error"); // yErr = 5 result->getColumn(result->columnCount() - 1)->setPlotType(5); for (size_t i = 0; i < m_function->nParams(); i++) { Mantid::API::TableRow row = result->appendRow(); row << m_function->parameterName(i) << m_function->getParameter(i) << m_function->getError(i); } // Add chi-squared value at the end of parameter table Mantid::API::TableRow row = result->appendRow(); #if 1 std::string costfuncname = getPropertyValue("CostFunction"); if (costfuncname == "Rwp") row << "Cost function value" << rawcostfuncval; else row << "Cost function value" << finalCostFuncVal; setProperty("OutputParameters", result); #else row << "Cost function value" << finalCostFuncVal; Mantid::API::TableRow row2 = result->appendRow(); std::string name(getPropertyValue("CostFunction")); name += " value"; row2 << name << rawcostfuncval; #endif setProperty("OutputParameters", result); bool outputParametersOnly = getProperty("OutputParametersOnly"); if (!outputParametersOnly) { const bool unrollComposites = getProperty("OutputCompositeMembers"); bool convolveMembers = existsProperty("ConvolveMembers"); if (convolveMembers) { convolveMembers = getProperty("ConvolveMembers"); } m_domainCreator->separateCompositeMembersInOutput(unrollComposites, convolveMembers); m_domainCreator->createOutputWorkspace(baseName, m_function, domain, values); } } progress(1.0); }
/** * Update the cost function, derivatives and hessian by adding values calculated * on a domain. * @param function :: Function to use to calculate the value and the derivatives * @param domain :: The domain. * @param values :: The fit function values * @param evalFunction :: Flag to evaluate the function * @param evalDeriv :: Flag to evaluate the derivatives * @param evalHessian :: Flag to evaluate the Hessian */ void CostFuncLeastSquares::addValDerivHessian( API::IFunction_sptr function, API::FunctionDomain_sptr domain, API::FunctionValues_sptr values, bool evalFunction , bool evalDeriv, bool evalHessian) const { UNUSED_ARG(evalDeriv); size_t np = function->nParams(); // number of parameters size_t ny = domain->size(); // number of data points Jacobian jacobian(ny,np); if (evalFunction) { function->function(*domain,*values); } function->functionDeriv(*domain,jacobian); size_t iActiveP = 0; double fVal = 0.0; if (debug) { std::cerr << "Jacobian:\n"; for(size_t i = 0; i < ny; ++i) { for(size_t ip = 0; ip < np; ++ip) { if ( !m_function->isActive(ip) ) continue; std::cerr << jacobian.get(i,ip) << ' '; } std::cerr << std::endl; } } for(size_t ip = 0; ip < np; ++ip) { if ( !function->isActive(ip) ) continue; double d = 0.0; for(size_t i = 0; i < ny; ++i) { double calc = values->getCalculated(i); double obs = values->getFitData(i); double w = values->getFitWeight(i); double y = ( calc - obs ) * w; d += y * jacobian.get(i,ip) * w; if (iActiveP == 0 && evalFunction) { fVal += y * y; } } PARALLEL_CRITICAL(der_set) { double der = m_der.get(iActiveP); m_der.set(iActiveP, der + d); } //std::cerr << "der " << ip << ' ' << der[iActiveP] << std::endl; ++iActiveP; } if (evalFunction) { PARALLEL_ATOMIC m_value += 0.5 * fVal; } if (!evalHessian) return; //size_t na = m_der.size(); // number of active parameters size_t i1 = 0; // active parameter index for(size_t i = 0; i < np; ++i) // over parameters { if ( !function->isActive(i) ) continue; size_t i2 = 0; // active parameter index for(size_t j = 0; j <= i; ++j) // over ~ half of parameters { if ( !function->isActive(j) ) continue; double d = 0.0; for(size_t k = 0; k < ny; ++k) // over fitting data { double w = values->getFitWeight(k); d += jacobian.get(k,i) * jacobian.get(k,j) * w * w; } PARALLEL_CRITICAL(hessian_set) { double h = m_hessian.get(i1,i2); m_hessian.set(i1,i2, h + d); //std::cerr << "hess " << i1 << ' ' << i2 << std::endl; if (i1 != i2) { m_hessian.set(i2,i1,h + d); } } ++i2; } ++i1; } }