/// Calculate the dot product /// @param v :: The other vector. double GSLVector::dot(const GSLVector &v) const { if (size() != v.size()) { throw std::runtime_error("Vectors have different sizes in dot product."); } double res = 0.0; gsl_blas_ddot(gsl(), v.gsl(), &res); return res; }
/// Set the matrix to be diagonal. /// @param d :: Values on the diagonal. void GSLMatrix::diag(const GSLVector &d) { const auto n = d.size(); resize(n, n); zero(); for (size_t i = 0; i < n; ++i) { set(i, i, d.get(i)); } }
/** * Copy the parameter values to a GSLVector. * @param params :: A vector to copy the parameters to */ void CostFuncLeastSquares::getParameters(GSLVector ¶ms) const { if (params.size() != nParams()) { params.resize(nParams()); } for (size_t i = 0; i < nParams(); ++i) { params.set(i, getParameter(i)); } }
/** * Copy the parameter values from a GSLVector. * @param params :: A vector to copy the parameters from */ void CostFuncLeastSquares::setParameters(const GSLVector ¶ms) { if (nParams() != params.size()) { throw std::runtime_error( "Parameter vector has wrong size in CostFuncLeastSquares."); } for (size_t i = 0; i < nParams(); ++i) { setParameter(i, params.get(i)); } m_function->applyTies(); }
/// 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); }
/** * Improve the solution by using obtained functions as a basis for diagonalization. * * @param hamiltonian :: The Hamiltonian. * @param basis :: The basis functions. * @param n :: The size of the basis to use. * @param eigv :: Output eigenvalues. * @param eigf :: Output eigenvectors (functions). This method creates a new ChebfunVector instance and returns it. */ void Schrodinger1D::improve(ChebOperator *hamiltonian, ChebfunVector *basis, GSLVector &eigv, ChebfunVector **eigf) const { size_t n = basis->size(); GSLMatrix H(n,n); for(size_t i = 0; i < n; ++i) { const chebfun &f1 = basis->cfun(i).cfun(0).getUnscaled(); chebfun d1(f1); hamiltonian->apply(f1, d1); chebfun dd = d1; dd *= f1; double me = dd.integr(); H.set( i, i, me ); //std::cerr << "e=" << me << std::endl; for(size_t j = i+1; j < n; ++j) { const chebfun &f2 = basis->cfun(j).cfun(0).getUnscaled(); chebfun d2(f2); hamiltonian->apply(f2, d2); d2 *= f1; double me = d2.integr(); //std::cerr << "o=" << me << std::endl; H.set( i, j, me ); H.set( j, i, me ); } } //std::cerr << H << std::endl; GSLVector en; GSLMatrix v; H.diag(en,v); std::vector<size_t> indx; getSortedIndex( en, indx ); if ( indx.empty() ) { throw std::runtime_error("Schrodinger1D failed to find solution."); } eigv.resize( indx.size() ); *eigf = new ChebfunVector; ChebfunVector &funs = **eigf; funs.fromLinearCombinations( *basis, v ); funs.sort( indx ); for(size_t i = 0; i < indx.size(); ++i) { std::cerr << i << ' ' << en[indx[i]] << std::endl; eigv.set( i, en[indx[i]] ); } }
/// Matrix by vector multiplication /// @param v :: A vector to multiply by. Must have the same size as size2(). /// @returns A vector - the result of the multiplication. Size of the returned /// vector equals size1(). /// @throws std::invalid_argument if the input vector has a wrong size. /// @throws std::runtime_error if the underlying GSL routine fails. GSLVector GSLMatrix::operator*(const GSLVector &v) const { if (v.size() != size2()) { throw std::invalid_argument( "Matrix by vector multiplication: wrong size of vector."); } GSLVector res(size1()); auto status = gsl_blas_dgemv(CblasNoTrans, 1.0, gsl(), v.gsl(), 0.0, res.gsl()); if (status != GSL_SUCCESS) { std::string message = "Failed to multiply matrix by a vector.\n" "Error message returned by the GSL:\n" + std::string(gsl_strerror(status)); throw std::runtime_error(message); } return res; }
/// Solve system of linear equations M*x == rhs, M is this matrix /// This matrix is destroyed. /// @param rhs :: The right-hand-side vector /// @param x :: The solution vector void GSLMatrix::solve(const GSLVector &rhs, GSLVector &x) { if (size1() != size2()) { throw std::runtime_error( "System of linear equations: the matrix must be square."); } size_t n = size1(); if (rhs.size() != n) { throw std::runtime_error( "System of linear equations: right-hand side vector has wrong size."); } x.resize(n); int s; gsl_permutation *p = gsl_permutation_alloc(n); gsl_linalg_LU_decomp(gsl(), p, &s); // matrix is modified at this moment gsl_linalg_LU_solve(gsl(), p, rhs.gsl(), x.gsl()); gsl_permutation_free(p); }
/** * Recalculate the B-spline knots */ void BSpline::resetKnots() { bool isUniform = getAttribute("Uniform").asBool(); std::vector<double> breakPoints; if ( isUniform ) { // create uniform knots in the interval [StartX, EndX] double startX = getAttribute("StartX").asDouble(); double endX = getAttribute("EndX").asDouble(); gsl_bspline_knots_uniform( startX, endX, m_bsplineWorkspace.get() ); getGSLBreakPoints( breakPoints ); storeAttributeValue( "BreakPoints", Attribute(breakPoints) ); } else { // set the break points from BreakPoints vector attribute, update other attributes breakPoints = getAttribute( "BreakPoints" ).asVector(); // check that points are in ascending order double prev = breakPoints[0]; for(size_t i = 1; i < breakPoints.size(); ++i) { double next = breakPoints[i]; if ( next <= prev ) { throw std::invalid_argument("BreakPoints must be in ascending order."); } prev = next; } int nbreaks = getAttribute( "NBreak" ).asInt(); // if number of break points change do necessary updates if ( static_cast<size_t>(nbreaks) != breakPoints.size() ) { storeAttributeValue("NBreak", Attribute(static_cast<int>(breakPoints.size())) ); resetGSLObjects(); resetParameters(); } GSLVector bp = breakPoints; gsl_bspline_knots( bp.gsl(), m_bsplineWorkspace.get() ); storeAttributeValue( "StartX", Attribute(breakPoints.front()) ); storeAttributeValue( "EndX", Attribute(breakPoints.back()) ); } }
/// Solve system of linear equations M*x == rhs, M is this matrix /// This matrix is destroyed. /// @param rhs :: The right-hand-side vector /// @param x :: The solution vector /// @throws std::invalid_argument if the input vectors have wrong sizes. /// @throws std::runtime_error if the GSL fails to solve the equations. void GSLMatrix::solve(const GSLVector &rhs, GSLVector &x) { if (size1() != size2()) { throw std::invalid_argument( "System of linear equations: the matrix must be square."); } size_t n = size1(); if (rhs.size() != n) { throw std::invalid_argument( "System of linear equations: right-hand side vector has wrong size."); } x.resize(n); int s; gsl_permutation *p = gsl_permutation_alloc(n); gsl_linalg_LU_decomp(gsl(), p, &s); // matrix is modified at this moment int res = gsl_linalg_LU_solve(gsl(), p, rhs.gsl(), x.gsl()); gsl_permutation_free(p); if (res != GSL_SUCCESS) { std::string message = "Failed to solve system of linear equations.\n" "Error message returned by the GSL:\n" + std::string(gsl_strerror(res)); throw std::runtime_error(message); } }
/** * Sort a vector in ascending order of positive values. It means that a negative value is * always "greater" than any positive value. It's for moving negatives to the back. * * The vector isn't actually changed by this method. Instead a vector of indices is created * which records the sort order. * * @param v :: A vector to sort. * @param indx :: The output sorted indices. v[ indx[i] ] gives i-th element in the sorted vector. */ void Schrodinger1D::getSortedIndex(const GSLVector &v, std::vector<size_t> &indx) const { indx.resize(v.size()); { size_t i = 0; std::generate(indx.begin(), indx.end(), [&i]()->size_t{++i;return i-1;}); } std::sort(indx.begin(), indx.end(), [&v](size_t x1,size_t x2)->bool{ const double& d1 = v[x1]; const double& d2 = v[x2]; if ( d1 < 0 && d2 >= 0 ) return false; if ( d2 < 0 && d1 >= 0 ) return true; if ( d1 < 0 && d2 < 0 ) return d2 < d1; return d1 < d2; }); }
/// 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); }
GSLVector::GSLVector(const GSLVector& v) { m_vector = gsl_vector_alloc(v.size()); gsl_vector_memcpy(m_vector, v.gsl()); }