/// Create a submatrix. A submatrix is a view into the parent matrix. /// Lifetime of a submatrix cannot exceed the lifetime of the parent. /// @param M :: The parent matrix. /// @param row :: The first row in the submatrix. /// @param col :: The first column in the submatrix. /// @param nRows :: The number of rows in the submatrix. /// @param nCols :: The number of columns in the submatrix. GSLMatrix::GSLMatrix(const GSLMatrix &M, size_t row, size_t col, size_t nRows, size_t nCols) { if (row + nRows > M.size1() || col + nCols > M.size2()) { throw std::runtime_error("Submatrix exceeds matrix size."); } auto view = gsl_matrix_const_submatrix(M.gsl(), row, col, nRows, nCols); m_matrix = gsl_matrix_alloc(nRows, nCols); gsl_matrix_memcpy(m_matrix, &view.matrix); }
/// Calculate the eigensystem of a symmetric matrix /// @param eigenValues :: Output variable that receives the eigenvalues of this /// matrix. /// @param eigenVectors :: Output variable that receives the eigenvectors of /// this matrix. void GSLMatrix::eigenSystem(GSLVector &eigenValues, GSLMatrix &eigenVectors) { size_t n = size1(); if (n != size2()) { throw std::runtime_error("Matrix eigenSystem: the matrix must be square."); } eigenValues.resize(n); eigenVectors.resize(n, n); auto workspace = gsl_eigen_symmv_alloc(n); gsl_eigen_symmv(gsl(), eigenValues.gsl(), eigenVectors.gsl(), workspace); gsl_eigen_symmv_free(workspace); }
/** * Calculates covariance matrix for fitting function's active parameters. */ void CostFuncFitting::calActiveCovarianceMatrix(GSLMatrix &covar, double epsrel) { // construct the jacobian GSLJacobian J(m_function, m_values->size()); size_t na = this->nParams(); // number of active parameters assert(J.getJ()->size2 == na); covar.resize(na, na); // calculate the derivatives m_function->functionDeriv(*m_domain, J); // let the GSL to compute the covariance matrix gsl_multifit_covar(J.getJ(), epsrel, covar.gsl()); }
/** * Calculates covariance matrix for fitting function's active parameters. * @param covar :: Output cavariance matrix. * @param epsrel :: Tolerance. */ void CostFuncLeastSquares::calActiveCovarianceMatrix(GSLMatrix& covar, double epsrel) { UNUSED_ARG(epsrel); if (m_hessian.isEmpty()) { valDerivHessian(); } covar = m_hessian; covar.invert(); }
/** * Calculate the transformation matrix T by numeric differentiation * @param tm :: The output transformation matrix. */ void CostFuncFitting::calTransformationMatrixNumerically(GSLMatrix &tm) { const double epsilon = std::numeric_limits<double>::epsilon() * 100; size_t np = m_function->nParams(); size_t na = nParams(); tm.resize(na, na); size_t ia = 0; for (size_t i = 0; i < np; ++i) { if (m_function->isFixed(i)) continue; double p0 = m_function->getParameter(i); for (size_t j = 0; j < na; ++j) { double ap = getParameter(j); double step = ap == 0.0 ? epsilon : ap * epsilon; setParameter(j, ap + step); tm.set(ia, j, (m_function->getParameter(i) - p0) / step); setParameter(j, ap); } ++ia; } }
/** * Calculates covariance matrix for fitting function's active parameters. * @param covar :: Output cavariance matrix. * @param epsrel :: Tolerance. */ void CostFuncLeastSquares::calActiveCovarianceMatrix(GSLMatrix &covar, double epsrel) { UNUSED_ARG(epsrel); if (m_hessian.isEmpty()) { valDerivHessian(); } if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { std::ostringstream osHess; osHess << "== Hessian (H) ==\n"; osHess << std::left << std::fixed; for (size_t i = 0; i < m_hessian.size1(); ++i) { for (size_t j = 0; j < m_hessian.size2(); ++j) { osHess << std::setw(10); osHess << m_hessian.get(i, j) << " "; } osHess << "\n"; } g_log.debug() << osHess.str(); } covar = m_hessian; covar.invert(); if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { std::ostringstream osCovar; osCovar << "== Covariance matrix (H^-1) ==\n"; osCovar << std::left << std::fixed; for (size_t i = 0; i < covar.size1(); ++i) { for (size_t j = 0; j < covar.size2(); ++j) { osCovar << std::setw(10); osCovar << covar.get(i, j) << " "; } osCovar << "\n"; } g_log.debug() << osCovar.str(); } }
/** * Calculate the fitting errors and assign them to the fitting function. * @param covar :: A covariance matrix to use for error calculations. * It can be calculated with calCovarianceMatrix(). * @param chi2 :: The final chi-squared of the fit. */ void CostFuncFitting::calFittingErrors(const GSLMatrix &covar, double chi2) { size_t np = m_function->nParams(); auto covarMatrix = boost::shared_ptr<Kernel::Matrix<double>>( new Kernel::Matrix<double>(np, np)); size_t ia = 0; for (size_t i = 0; i < np; ++i) { if (m_function->isFixed(i)) { m_function->setError(i, 0); } else { size_t ja = 0; for (size_t j = 0; j < np; ++j) { if (!m_function->isFixed(j)) { (*covarMatrix)[i][j] = covar.get(ia, ja); ++ja; } } double err = sqrt(covar.get(ia, ia)); m_function->setError(i, err); ++ia; } } m_function->setCovarianceMatrix(covarMatrix); m_function->setChiSquared(chi2); }
/** * Calculate the fitting errors and assign them to the fitting function. * @param covar :: A covariance matrix to use for error calculations. * It can be calculated with calCovarianceMatrix(). */ void CostFuncFitting::calFittingErrors(const GSLMatrix& covar) { size_t np = m_function->nParams(); size_t ia = 0; for(size_t i = 0; i < np; ++i) { if (m_function->isFixed(i)) { m_function->setError(i,0); } else { double err = sqrt(covar.get(ia,ia)); m_function->setError(i,err); ++ia; } } }
/** 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); }
/// Copy constructor /// @param M :: The other matrix. GSLMatrix::GSLMatrix(const GSLMatrix &M) : m_data(M.m_data), m_view(gsl_matrix_view_array(m_data.data(), M.size1(), M.size2())) {}
/// Execute algorithm. void Schrodinger1D::exec() { double startX = get("StartX"); double endX = get("EndX"); if (endX <= startX) { throw std::invalid_argument("StartX must be <= EndX"); } IFunction_sptr VPot = getClass("VPot"); chebfun vpot( 0, startX, endX ); vpot.bestFit( *VPot ); size_t nBasis = vpot.n() + 1; std::cerr << "n=" << nBasis << std::endl; //if (n < 3) { nBasis = 200; vpot.resize( nBasis ); } const double beta = get("Beta"); auto kinet = new ChebCompositeOperator; kinet->addRight( new ChebTimes(-beta) ); kinet->addRight( new ChebDiff2 ); auto hamiltonian = new ChebPlus; hamiltonian->add('+', kinet ); hamiltonian->add('+', new ChebTimes(VPot) ); GSLMatrix L; hamiltonian->createMatrix( vpot.getBase(), L ); GSLVector d; GSLMatrix v; L.diag( d, v ); std::vector<double> norms = vpot.baseNorm(); assert(norms.size() == L.size1()); assert(norms.size() == L.size2()); for(size_t i = 0; i < norms.size(); ++i) { double factor = 1.0 / norms[i]; for(size_t j = i; j < norms.size(); ++j) { v.multiplyBy(i,j,factor); } } // eigenvectors orthogonality check // GSLMatrix v1 = v; // GSLMatrix tst; // tst = Tr(v1) * v; // std::cerr << tst << std::endl; std::vector<size_t> indx(L.size1()); getSortedIndex( d, indx ); auto eigenvalues = API::TableWorkspace_ptr(dynamic_cast<API::TableWorkspace*>( API::WorkspaceFactory::instance().create("TableWorkspace")) ); eigenvalues->setRowCount(nBasis); setProperty("Eigenvalues", eigenvalues); eigenvalues->addColumn("double","N"); auto nColumn = static_cast<API::TableColumn<double>*>(eigenvalues->getColumn("N").get()); nColumn->asNumeric()->setPlotRole(API::NumericColumn::X); auto& nc = nColumn->data(); eigenvalues->addDoubleColumn("Energy"); auto eColumn = static_cast<API::TableColumn<double>*>(eigenvalues->getColumn("Energy").get()); eColumn->asNumeric()->setPlotRole(API::NumericColumn::Y); auto& ec = eigenvalues->getDoubleData("Energy"); boost::scoped_ptr<ChebfunVector> eigenvectors(new ChebfunVector); chebfun fun0(nBasis,startX,endX); ChebFunction_sptr theSum(new ChebFunction(fun0)); // collect indices of spurious eigenvalues to move them to the back std::vector<size_t> spurious; // index for good eigenvalues size_t n = 0; for(size_t j = 0; j < nBasis; ++j) { size_t i = indx[j]; chebfun fun(fun0); fun.setP(v,i); // check eigenvalues for spurious ones chebfun dfun(fun); dfun.square(); double norm = dfun.integr(); // I am not sure that it's a solid condition if ( norm < 0.999999 ) { // bad eigenvalue spurious.push_back(j); } else { nc[n] = double(n); ec[n] = d[i]; eigenvectors->add(ChebFunction_sptr(new ChebFunction(fun))); // test sum of functions squares *theSum += dfun; // chebfun dfun(fun); // hamiltonian->apply(fun,dfun); // dfun *= fun; // std::cerr << "ener["<<n<<"]=" << ec[n] << ' ' << norm << ' ' << dfun.integr() << std::endl; ++n; } } GSLVector eigv; ChebfunVector *eigf = NULL; improve(hamiltonian, eigenvectors.get(), eigv, &eigf); eigenvalues->setRowCount( eigv.size() ); for(size_t i = 0; i < eigv.size(); ++i) { nc[i] = double(i); ec[i] = eigv[i]; } eigf->add(theSum); setProperty("Eigenvectors",ChebfunVector_sptr(eigf)); //makeQuadrature(eigf); }
//---------------------------------------------------------------------------------------------- /// Examine the chi squared as a function of fitting parameters and estimate /// errors for each parameter. void CalculateChiSquared::estimateErrors() { // Number of fiting parameters auto nParams = m_function->nParams(); // Create an output table for displaying slices of the chi squared and // the probabilitydensity function auto pdfTable = API::WorkspaceFactory::Instance().createTable(); std::string baseName = getProperty("Output"); if (baseName.empty()) { baseName = "CalculateChiSquared"; } declareProperty(new API::WorkspaceProperty<API::ITableWorkspace>( "PDFs", "", Kernel::Direction::Output), "The name of the TableWorkspace in which to store the " "pdfs of fit parameters"); setPropertyValue("PDFs", baseName + "_pdf"); setProperty("PDFs", pdfTable); // Create an output table for displaying the parameter errors. auto errorsTable = API::WorkspaceFactory::Instance().createTable(); auto nameColumn = errorsTable->addColumn("str", "Parameter"); auto valueColumn = errorsTable->addColumn("double", "Value"); auto minValueColumn = errorsTable->addColumn("double", "Value at Min"); auto leftErrColumn = errorsTable->addColumn("double", "Left Error"); auto rightErrColumn = errorsTable->addColumn("double", "Right Error"); auto quadraticErrColumn = errorsTable->addColumn("double", "Quadratic Error"); auto chiMinColumn = errorsTable->addColumn("double", "Chi2 Min"); errorsTable->setRowCount(nParams); declareProperty(new API::WorkspaceProperty<API::ITableWorkspace>( "Errors", "", Kernel::Direction::Output), "The name of the TableWorkspace in which to store the " "values and errors of fit parameters"); setPropertyValue("Errors", baseName + "_errors"); setProperty("Errors", errorsTable); // Calculate initial values double chiSquared = 0.0; double chiSquaredWeighted = 0.0; double dof = 0; API::FunctionDomain_sptr domain; API::FunctionValues_sptr values; m_domainCreator->createDomain(domain, values); calcChiSquared(*m_function, nParams, *domain, *values, chiSquared, chiSquaredWeighted, dof); // Value of chi squared for current parameters in m_function double chi0 = chiSquared; // Fit data variance double sigma2 = chiSquared / dof; bool useWeighted = getProperty("Weighted"); if (useWeighted) { chi0 = chiSquaredWeighted; sigma2 = 0.0; } if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << "chi0=" << chi0 << std::endl; g_log.debug() << "sigma2=" << sigma2 << std::endl; g_log.debug() << "dof=" << dof << std::endl; } // Parameter bounds that define a volume in the parameter // space within which the chi squared is being examined. GSLVector lBounds(nParams); GSLVector rBounds(nParams); // Number of points in lines for plotting size_t n = 100; pdfTable->setRowCount(n); const double fac = 1e-4; // Loop over each parameter for (size_t ip = 0; ip < nParams; ++ip) { // Add columns for the parameter to the pdf table. auto parName = m_function->parameterName(ip); nameColumn->read(ip, parName); // Parameter values auto col1 = pdfTable->addColumn("double", parName); col1->setPlotType(1); // Chi squared values auto col2 = pdfTable->addColumn("double", parName + "_chi2"); col2->setPlotType(2); // PDF values auto col3 = pdfTable->addColumn("double", parName + "_pdf"); col3->setPlotType(2); double par0 = m_function->getParameter(ip); double shift = fabs(par0 * fac); if (shift == 0.0) { shift = fac; } // Make a slice along this parameter GSLVector dir(nParams); dir.zero(); dir[ip] = 1.0; ChiSlice slice(*m_function, dir, *domain, *values, chi0, sigma2); // Find the bounds withn which the PDF is significantly above zero. // The bounds are defined relative to par0: // par0 + lBound is the lowest value of the parameter (lBound <= 0) // par0 + rBound is the highest value of the parameter (rBound >= 0) double lBound = slice.findBound(-shift); double rBound = slice.findBound(shift); lBounds[ip] = lBound; rBounds[ip] = rBound; // Approximate the slice with a polynomial. // P is a vector of values of the polynomial at special points. // A is a vector of Chebyshev expansion coefficients. // The polynomial is defined on interval [lBound, rBound] // The value of the polynomial at 0 == chi squared at par0 std::vector<double> P, A; bool ok = true; auto base = slice.makeApprox(lBound, rBound, P, A, ok); if (!ok) { g_log.warning() << "Approximation failed for parameter " << ip << std::endl; } if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << "Parameter " << ip << std::endl; g_log.debug() << "Slice approximated by polynomial of order " << base->size() - 1; g_log.debug() << " between " << lBound << " and " << rBound << std::endl; } // Write n slice points into the output table. double dp = (rBound - lBound) / static_cast<double>(n); for (size_t i = 0; i < n; ++i) { double par = lBound + dp * static_cast<double>(i); double chi = base->eval(par, P); col1->fromDouble(i, par0 + par); col2->fromDouble(i, chi); } // Check if par0 is a minimum point of the chi squared std::vector<double> AD; // Calculate the derivative polynomial. // AD are the Chebyshev expansion of the derivative. base->derivative(A, AD); // Find the roots of the derivative polynomial std::vector<double> minima = base->roots(AD); if (minima.empty()) { minima.push_back(par0); } if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << "Minima: "; } // If only 1 extremum is found assume (without checking) that it's a // minimum. // If there are more than 1, find the one with the smallest chi^2. double chiMin = std::numeric_limits<double>::max(); double parMin = par0; for (size_t i = 0; i < minima.size(); ++i) { double value = base->eval(minima[i], P); if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << minima[i] << " (" << value << ") "; } if (value < chiMin) { chiMin = value; parMin = minima[i]; } } if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << std::endl; g_log.debug() << "Smallest minimum at " << parMin << " is " << chiMin << std::endl; } // Points of intersections with line chi^2 = 1/2 give an estimate of // the standard deviation of this parameter if it's uncorrelated with the // others. A[0] -= 0.5; // Now A are the coefficients of the original polynomial // shifted down by 1/2. std::vector<double> roots = base->roots(A); std::sort(roots.begin(), roots.end()); if (roots.empty()) { // Something went wrong; use the whole interval. roots.resize(2); roots[0] = lBound; roots[1] = rBound; } else if (roots.size() == 1) { // Only one root found; use a bound for the other root. if (roots.front() < 0) { roots.push_back(rBound); } else { roots.insert(roots.begin(), lBound); } } else if (roots.size() > 2) { // More than 2 roots; use the smallest and the biggest auto smallest = roots.front(); auto biggest = roots.back(); roots.resize(2); roots[0] = smallest; roots[1] = biggest; } if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << "Roots: "; for (size_t i = 0; i < roots.size(); ++i) { g_log.debug() << roots[i] << ' '; } g_log.debug() << std::endl; } // Output parameter info to the table. valueColumn->fromDouble(ip, par0); minValueColumn->fromDouble(ip, par0 + parMin); leftErrColumn->fromDouble(ip, roots[0] - parMin); rightErrColumn->fromDouble(ip, roots[1] - parMin); chiMinColumn->fromDouble(ip, chiMin); // Output the PDF for (size_t i = 0; i < n; ++i) { double chi = col2->toDouble(i); col3->fromDouble(i, exp(-chi + chiMin)); } // make sure function parameters don't change. m_function->setParameter(ip, par0); } // Improve estimates for standard deviations. // If parameters are correlated the found deviations // most likely underestimate the true values. unfixParameters(); GSLJacobian J(m_function, values->size()); m_function->functionDeriv(*domain, J); refixParameters(); // Calculate the hessian at the current point. GSLMatrix H; if (useWeighted) { H.resize(nParams, nParams); for (size_t i = 0; i < nParams; ++i) { for (size_t j = i; j < nParams; ++j) { double h = 0.0; for (size_t k = 0; k < values->size(); ++k) { double w = values->getFitWeight(k); h += J.get(k, i) * J.get(k, j) * w * w; } H.set(i, j, h); if (i != j) { H.set(j, i, h); } } } } else { H = Tr(J.matrix()) * J.matrix(); } // Square roots of the diagonals of the covariance matrix give // the standard deviations in the quadratic approximation of the chi^2. GSLMatrix V(H); if (!useWeighted) { V *= 1. / sigma2; } V.invert(); // In a non-quadratic asymmetric case the following procedure can give a // better result: // Find the direction in which the chi^2 changes slowest and the positive and // negative deviations in that direction. The change in a parameter at those // points can be a better estimate for the standard deviation. GSLVector v(nParams); GSLMatrix Q(nParams, nParams); // One of the eigenvectors of the hessian is the direction of the slowest // change. H.eigenSystem(v, Q); // Loop over the eigenvectors for (size_t i = 0; i < nParams; ++i) { auto dir = Q.copyColumn(i); if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << "Direction " << i << std::endl; g_log.debug() << dir << std::endl; } // Make a slice in that direction ChiSlice slice(*m_function, dir, *domain, *values, chi0, sigma2); double rBound0 = dir.dot(rBounds); double lBound0 = dir.dot(lBounds); if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << "lBound " << lBound0 << std::endl; g_log.debug() << "rBound " << rBound0 << std::endl; } double lBound = slice.findBound(lBound0); double rBound = slice.findBound(rBound0); std::vector<double> P, A; // Use a polynomial approximation bool ok = true; auto base = slice.makeApprox(lBound, rBound, P, A, ok); if (!ok) { g_log.warning() << "Approximation failed in direction " << i << std::endl; } // Find the deviation points where the chi^2 = 1/2 A[0] -= 0.5; std::vector<double> roots = base->roots(A); std::sort(roots.begin(), roots.end()); // Sort out the roots auto nRoots = roots.size(); if (nRoots == 0) { roots.resize(2, 0.0); } else if (nRoots == 1) { if (roots.front() > 0.0) { roots.insert(roots.begin(), 0.0); } else { roots.push_back(0.0); } } else if (nRoots > 2) { roots[1] = roots.back(); roots.resize(2); } if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << "Roots " << roots[0] << " (" << slice(roots[0]) << ") " << roots[1] << " (" << slice(roots[1]) << ") " << std::endl; } // Loop over the parameters and see if there deviations along // this direction is greater than any previous value. for (size_t ip = 0; ip < nParams; ++ip) { auto lError = roots.front() * dir[ip]; auto rError = roots.back() * dir[ip]; if (lError > rError) { std::swap(lError, rError); } if (lError < leftErrColumn->toDouble(ip)) { if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << " left for " << ip << ' ' << lError << ' ' << leftErrColumn->toDouble(ip) << std::endl; } leftErrColumn->fromDouble(ip, lError); } if (rError > rightErrColumn->toDouble(ip)) { if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) { g_log.debug() << " right for " << ip << ' ' << rError << ' ' << rightErrColumn->toDouble(ip) << std::endl; } rightErrColumn->fromDouble(ip, rError); } } // Output the quadratic estimate for comparrison. quadraticErrColumn->fromDouble(i, sqrt(V.get(i, i))); } }
/// Copy constructor /// @param M :: The other matrix. GSLMatrix::GSLMatrix(const GSLMatrix &M) { m_matrix = gsl_matrix_alloc(M.size1(), M.size2()); gsl_matrix_memcpy(m_matrix, M.gsl()); }
/** Returns the number of rows in TNT-based matrix */ size_t GSLUtility::Rows(const GSLMatrix &m) const { return (m.dim2()); }
/** Returns the number of columns in TNT-based matrix */ size_t GSLUtility::Columns(const GSLMatrix &m) const { return (m.dim1()); }