/** Fit GSL function wrapper * @param x :: Input function parameters * @param params :: Input data * @param f :: Output function values = (y_cal-y_data)/sigma for each data point * @return A GSL status information */ int gsl_f(const gsl_vector *x, void *params, gsl_vector *f) { struct GSL_FitData *p = (struct GSL_FitData *)params; // update function parameters if (x->data) { size_t ia = 0; for (size_t i = 0; i < p->function->nParams(); ++i) { if (p->function->isActive(i)) { p->function->setActiveParameter(i, x->data[ia]); ++ia; } } } p->function->applyTies(); auto values = boost::dynamic_pointer_cast<API::FunctionValues>( p->costFunction->getValues()); if (!values) { throw std::invalid_argument("FunctionValues expected"); } p->function->function(*p->costFunction->getDomain(), *values); // Add penalty double penalty = 0.; for (size_t i = 0; i < p->function->nParams(); ++i) { API::IConstraint *c = p->function->getConstraint(i); if (c) { penalty += c->checkDeriv(); } } size_t n = values->size() - 1; // add penalty to first and last point and every 10th point in between if (penalty != 0.0) { values->addToCalculated(0, penalty); values->addToCalculated(n, penalty); for (size_t i = 9; i < n; i += 10) { values->addToCalculated(i, penalty); } } // function() return calculated data values. Need to convert this values into // calculated-observed devided by error values used by GSL for (size_t i = 0; i < p->n; i++) { f->data[i] = (values->getCalculated(i) - values->getFitData(i)) * values->getFitWeight(i); // std::cerr << values.getCalculated(i) << ' ' << values.getFitData(i) << ' // ' << values.getFitWeight(i) << std::endl; } return GSL_SUCCESS; }
/** Calculate the value and the first and second derivatives of the cost function * @param evalFunction :: If false cost function isn't evaluated and returned value (0.0) should be ignored. * It is for efficiency reasons. * @param evalDeriv :: flag for evaluation of the first derivatives * @param evalHessian :: flag for evaluation of the second derivatives */ double CostFuncLeastSquares::valDerivHessian(bool evalFunction, bool evalDeriv, bool evalHessian) const { if (m_pushed || !evalDeriv) { return val(); } if (!m_dirtyVal && !m_dirtyDeriv && !m_dirtyHessian) return m_value; if (m_dirtyVal) evalFunction = true; checkValidity(); if (evalFunction) { m_value = 0.0; } if (evalDeriv) { m_der.resize(nParams()); m_der.zero(); } if (evalHessian) { m_hessian.resize(nParams(),nParams()); m_hessian.zero(); } auto seqDomain = boost::dynamic_pointer_cast<SeqDomain>(m_domain); if (seqDomain) { seqDomain->leastSquaresValDerivHessian(*this,evalFunction,evalDeriv,evalHessian); } else { auto simpleValues = boost::dynamic_pointer_cast<API::FunctionValues>(m_values); if (!simpleValues) { throw std::runtime_error("LeastSquares: unsupported IFunctionValues."); } addValDerivHessian(m_function,m_domain,simpleValues,evalFunction,evalDeriv,evalHessian); } // Add constraints penalty size_t np = m_function->nParams(); if (evalFunction) { for(size_t i = 0; i < np; ++i) { API::IConstraint* c = m_function->getConstraint(i); if (c) { m_value += c->check(); } } m_dirtyVal = false; } if (evalDeriv) { size_t i = 0; for(size_t ip = 0; ip < np; ++ip) { if ( !m_function->isActive(ip) ) continue; API::IConstraint* c = m_function->getConstraint(ip); if (c) { double d = m_der.get(i) + c->checkDeriv(); m_der.set(i,d); } ++i; } m_dirtyDeriv = false; } if (evalDeriv) { size_t i = 0; for(size_t ip = 0; ip < np; ++ip) { if ( !m_function->isActive(ip) ) continue; API::IConstraint* c = m_function->getConstraint(ip); if (c) { double d = m_hessian.get(i,i) + c->checkDeriv2(); m_hessian.set(i,i,d); } ++i; } // clear the dirty flag if hessian was actually calculated m_dirtyHessian = m_hessian.isEmpty(); } return m_value; }