// gps - FMRIB's Tool for getting directions // // gps.cpp // // Jesper Andersson, FMRIB Image Analysis Group // // #include #include #include #include #include #include #include #include "newmat.h" #include "newmatio.h" #include "miscmaths/miscmaths.h" #include "miscmaths/nonlin.h" #include "gps.h" using namespace GPS; double Bvec::PI = 3.141592653589793; Bvec::Bvec() : _vr(3), _ar(2), _dvdt(3), _dvdp(3), _d2vdt2(3), _d2vdp2(3), _d2vdtdp(3), _dutd(false) { for (int i=1; i<=3; i++) _vr(i) = -1.0 + 2.0*(double(rand()) / double(RAND_MAX)); _vr /= _vr.NormFrobenius(); set_ar_from_vr(); } Bvec::Bvec(const NEWMAT::ColumnVector& vec) : _vr(3), _ar(2), _dvdt(3), _dvdp(3), _d2vdt2(3), _d2vdp2(3), _d2vdtdp(3), _dutd(false) { if (vec.Nrows() != 3) throw GpsException("Bvec::Bvec: vec must have 3 elements"); _vr = vec/vec.NormFrobenius(); set_ar_from_vr(); } void Bvec::SetAngles(const NEWMAT::ColumnVector& angl) { if (angl.Nrows() != 2) throw GpsException("Bvec::SetAngles: angl must have 2 elements"); if (_ar != angl) { _ar = angl; set_vr_from_ar(); _dutd = false; } } void Bvec::SetVector(const NEWMAT::ColumnVector& vec) { if (vec.Nrows() != 3) throw GpsException("Bvec::SetVector: vec must have 3 elements"); if (_vr != vec) { _vr = vec/vec.NormFrobenius(); set_ar_from_vr(); _dutd = false; } } void Bvec::SetRandomVector() { for (int i=1; i<=3; i++) _vr(i) = -1.0 + 2.0*(double(rand()) / double(RAND_MAX)); _vr /= _vr.NormFrobenius(); set_ar_from_vr(); _dutd = false; } void Bvec::SignSwap() { for (int i=1; i<=3; i++) _vr(i) = -_vr(i); set_ar_from_vr(); _dutd = false; } void Bvec::set_ar_from_vr() { double theta = asin(_vr(3)); double cost = cos(theta); std::vector phi1(2); phi1[0] = acos(_vr(1)/cost); phi1[1] = 2*PI-phi1[0]; std::vector phi2(2); double tmp = asin(_vr(2)/cost); phi2[1] = PI-tmp; if (tmp >= 0.0) phi2[0] = tmp; else phi2[0] = 2.0*PI+tmp; double phi = 0.0; if (fabs(phi1[0]-phi2[0])<1e-6 || fabs(phi1[0]-phi2[1])<1e-6) phi = phi1[0]; else phi = phi1[1]; _ar(1) = theta; _ar(2) = phi; } void Bvec::set_vr_from_ar() { double cost = cos(_ar(1)); _vr(1) = cost*cos(_ar(2)); _vr(2) = cost*sin(_ar(2)); _vr(3) = sin(_ar(1)); } void Bvec::update_derivatives() const { double sint = sin(_ar(1)); double cost = cos(_ar(1)); double sinp = sin(_ar(2)); double cosp = cos(_ar(2)); _dvdt(1) = -sint*cosp; _dvdt(2) = -sint*sinp; _dvdt(3) = cost; _dvdp(1) = -cost*sinp; _dvdp(2) = cost*cosp; _dvdp(3) = 0.0; _d2vdt2(1) = -cost*cosp; _d2vdt2(2) = -cost*sinp; _d2vdt2(3) = -sint; _d2vdp2(1) = -cost*cosp; _d2vdp2(2) = -cost*sinp; _d2vdp2(3) = 0.0; _d2vdtdp(1) = sint*sinp; _d2vdtdp(2) = -sint*cosp; _d2vdtdp(3) = 0.0; _dutd = true; } BvecCollection::BvecCollection(unsigned int n) : _bv(n) { NEWMAT::ColumnVector first(3); first << 1 << 0 << 0; _bv[0].SetVector(first); for (unsigned int i=1; i 5*mgrad || grad(2*j) > 5*mgrad) { double oldcf = CoulombForces(); NEWMAT::ColumnVector oldvec = _bv[j].Vector(); SetBvecToRandom(j); if (CoulombForces() > oldcf) SetBvec(j,oldvec); } } } } void BvecCollection::OptimiseOnWholeSphere(unsigned int niter, bool verbose) { double cf = SingleChargeCoulombForces(); for (unsigned int i=0; i 2048) throw GpsException(""); if (!_out.set()) { char ofname[128]; sprintf(ofname,"bvecs%d.txt",_ndir.value()); _out.set_value(std::string(ofname)); } if (_init.set()) { try { _init_mat = MISCMATHS::read_ascii_matrix(_init.value()); } catch(...) { throw GpsException("GpsCommandLineOptions::GpsCommandLineOptions: Failed to read --init file"); } if (_init_mat.Nrows() != 3 && _init_mat.Ncols() != 3) throw GpsException("GpsCommandLineOptions::GpsCommandLineOptions: --init matrix must be 3xN or Nx3"); if (_init_mat.Nrows() != 3) _init_mat = _init_mat.t(); } // Perform some global tasks srand(_ranseed.value()); } int main(int argc, char *argv[]) { BvecCollection *bvecp; // Parse command line GpsCommandLineOptions clo(argc,argv); if (clo.HasInitMatrix()) { NEWMAT::Matrix mat_bvecs = clo.InitMatrix(); bvecp = new BvecCollection(mat_bvecs); } else { // Get a random collection of b-vectors bvecp = new BvecCollection(clo.NDir()); // Do a little initial shaking bvecp->ShakeIt(50); } // If debug, write some debug info and exit if (clo.Debug()) { cout << "Coulomb Forces = " << bvecp->CoulombForces() << endl; cout << "Single Charge Coulomb Forces = " << bvecp->SingleChargeCoulombForces() << endl; NEWMAT::Matrix mat_bvecs = bvecp->GetAllBvecs(); MISCMATHS::write_ascii_matrix(mat_bvecs,clo.OutFname()+string("_debug_vectors")); NEWMAT::ColumnVector grad = bvecp->CoulombForcesGradient(); MISCMATHS::write_ascii_matrix(grad,clo.OutFname()+string("_debug_analytic_gradient")); grad = bvecp->CoulombForcesNumericalGradient(); MISCMATHS::write_ascii_matrix(grad,clo.OutFname()+string("_debug_numerical_gradient")); NEWMAT::Matrix hess = bvecp->CoulombForcesHessian(); MISCMATHS::write_ascii_matrix(hess,clo.OutFname()+string("_debug_analytic_hessian")); hess = bvecp->CoulombForcesNumericalHessian(); MISCMATHS::write_ascii_matrix(hess,clo.OutFname()+string("_debug_numerical_hessian")); exit(EXIT_SUCCESS); } // If report, report forces and exit if (clo.Report()) { cout << "Coulomb Forces = " << bvecp->CoulombForces() << endl; cout << "Single Charge Coulomb Forces = " << bvecp->SingleChargeCoulombForces() << endl; exit(EXIT_SUCCESS); } // Set up a cost-function object GpsCF cf(*bvecp,clo.Verbose()); // A nonlinear object to guide the optimisation MISCMATHS::NonlinParam nlpar(bvecp->NPar(),MISCMATHS::NL_LM,bvecp->GetPar()); nlpar.SetGaussNewtonType(MISCMATHS::LM_L); nlpar.SetMaxIter(2000*clo.NDir()); // Do the minimisation try { nonlin(nlpar,cf); } catch (const std::exception& error) { cerr << "Error occurred when attempting to optimise directions" << endl; cerr << "Exception thrown with message: " << error.what() << endl; exit(EXIT_FAILURE); } BvecCollection opt_bvecs = cf.GetBvecs(); // At this stage they should be optimised on a half sphere, and // distributed "randomly" on the whole sphere. If the --optws // flag is set we will sign swap directions and attempt do find // a more even configuration on the whole sphere. To do so we // use the coulomb forces resulting from putting charges only at // the positive ends of the gradients. if (clo.OptimiseOnWholeSphere()) opt_bvecs.OptimiseOnWholeSphere(50,clo.Verbose()); // Write the results NEWMAT::Matrix mat_bvecs = opt_bvecs.GetAllBvecs(); MISCMATHS::write_ascii_matrix(mat_bvecs,clo.OutFname()); exit(EXIT_SUCCESS); }