Skip to content
Snippets Groups Projects
targetspacetest.cc 14.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include <dune/gfe/spaces/hyperbolichalfspacepoint.hh>
    #include <dune/gfe/spaces/productmanifold.hh>
    #include <dune/gfe/spaces/unitvector.hh>
    #include <dune/gfe/spaces/realtuple.hh>
    #include <dune/gfe/spaces/rotation.hh>
    
    #include "valuefactory.hh"
    
    
    using namespace Dune;
    
        \brief Unit tests for classes that implement value manifolds for geodesic FE functions
    
    /** \brief Computes the diameter of a set */
    template <class TargetSpace>
    double diameter(const std::vector<TargetSpace>& v)
    {
    
      double d = 0;
      for (size_t i=0; i<v.size(); i++)
        for (size_t j=0; j<v.size(); j++)
          d = std::max(d, TargetSpace::distance(v[i],v[j]));
      return d;
    
    const double eps = 1e-4;
    
    template <class TargetSpace>
    void testExpLog(const TargetSpace& a, const TargetSpace& b)
    {
    
      // Check whether exp and log are mutually inverse
      typename TargetSpace::EmbeddedTangentVector logarithm = TargetSpace::log(a,b);
      TargetSpace exponential = TargetSpace::exp(a, logarithm);
    
      if (TargetSpace::distance(b, exponential) > eps)
      {
        std::cout << className(a) << ": Exp and log are not mutually inverse." << std::endl;
        std::cout << "exp(a,log(a,b)): " << exponential << std::endl;
        std::cout << "b              : " << b << std::endl;
        assert(false);
      }
    
    double distanceSquared(const TargetSpace& a, const TargetSpace& b)
    
      return Dune::power(TargetSpace::distance(a,b), 2);
    
    // Squared distance between two points slightly off the manifold.
    // This is required for finite difference approximations.
    
    template <class TargetSpace, int dim>
    
    double distanceSquared(const FieldVector<double,dim>& a, const FieldVector<double,dim>& b)
    
      return Dune::power(TargetSpace::distance(TargetSpace(a),TargetSpace(b)), 2);
    
    /** \brief Compute the Riemannian Hessian of the squared distance function in global coordinates
    
        The formula for the Riemannian Hessian has been taken from Absil, Mahony, Sepulchre:
        'Optimization algorithms on matrix manifolds', page 107
    
    template <class TargetSpace, int worldDim>
    FieldMatrix<double,worldDim,worldDim> getSecondDerivativeOfSecondArgumentFD(const TargetSpace& a, const TargetSpace& b)
    
    
      const size_t spaceDim = TargetSpace::dim;
    
      // finite-difference approximation
      FieldMatrix<double,spaceDim,spaceDim> d2d2_fd(0);
    
      FieldMatrix<double,spaceDim,worldDim> B = b.orthonormalFrame();
    
      for (size_t i=0; i<spaceDim; i++) {
        for (size_t j=0; j<spaceDim; j++) {
    
          FieldVector<double,worldDim> epsXi  = eps * B[i];
          FieldVector<double,worldDim> epsEta = eps * B[j];
    
          FieldVector<double,worldDim> minusEpsXi  = -1 * epsXi;
          FieldVector<double,worldDim> minusEpsEta = -1 * epsEta;
    
          double forwardValue  = distanceSquared(a,TargetSpace::exp(b,epsXi+epsEta)) - distanceSquared(a, TargetSpace::exp(b,epsXi)) - distanceSquared(a,TargetSpace::exp(b,epsEta));
          double centerValue   = distanceSquared(a,b) - distanceSquared(a,b) - distanceSquared(a,b);
          double backwardValue = distanceSquared(a,TargetSpace::exp(b,minusEpsXi + minusEpsEta)) - distanceSquared(a, TargetSpace::exp(b,minusEpsXi)) - distanceSquared(a,TargetSpace::exp(b,minusEpsEta));
    
          d2d2_fd[i][j] = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*eps);
    
    
      }
    
      //B.invert();
      FieldMatrix<double,worldDim,spaceDim> BT;
      for (int i=0; i<worldDim; i++)
        for (size_t j=0; j<spaceDim; j++)
          BT[i][j] = B[j][i];
    
    
      FieldMatrix<double,spaceDim,worldDim> ret1;
      FMatrixHelp::multMatrix(d2d2_fd,B,ret1);
    
      FieldMatrix<double,worldDim,worldDim> ret2;
      FMatrixHelp::multMatrix(BT,ret1,ret2);
      return ret2;
    
    template <class TargetSpace>
    
    void testOrthonormalFrame(const TargetSpace& a)
    {
    
      const size_t spaceDim = TargetSpace::dim;
      const size_t embeddedDim = TargetSpace::embeddedDim;
    
      FieldMatrix<double,spaceDim,embeddedDim> B = a.orthonormalFrame();
    
      for (size_t i=0; i<spaceDim; i++)
        for (size_t j=0; j<spaceDim; j++)
          assert( std::fabs(a.metric(B[i],B[j]) - (i==j)) < 1e-10 );
    
    template <class TargetSpace>
    
    void testDerivativeOfDistanceSquared(const TargetSpace& a, const TargetSpace& b)
    
      static const size_t embeddedDim = TargetSpace::embeddedDim;
    
      ///////////////////////////////////////////////////////////////////
      //  Test derivative with respect to second argument
      ///////////////////////////////////////////////////////////////////
      typename TargetSpace::EmbeddedTangentVector d2 =  TargetSpace::derivativeOfDistanceSquaredWRTSecondArgument(a, b);
    
      // finite-difference approximation
      Dune::FieldMatrix<double,TargetSpace::TangentVector::dimension,embeddedDim> B = b.orthonormalFrame();
    
      typename TargetSpace::TangentVector d2_fd;
      for (size_t i=0; i<TargetSpace::TangentVector::dimension; i++) {
    
        typename TargetSpace::EmbeddedTangentVector fwVariation =  eps * B[i];
        typename TargetSpace::EmbeddedTangentVector bwVariation = -eps * B[i];
        TargetSpace bPlus  = TargetSpace::exp(b,fwVariation);
        TargetSpace bMinus = TargetSpace::exp(b,bwVariation);
    
        d2_fd[i] = (distanceSquared(a,bPlus) - distanceSquared(a,bMinus)) / (2*eps);
      }
    
      // transform into embedded coordinates
      typename TargetSpace::EmbeddedTangentVector d2_fd_embedded;
      B.mtv(d2_fd,d2_fd_embedded);
    
      if ( (d2 - d2_fd_embedded).infinity_norm() > 200*eps ) {
        std::cout << className(a) << ": Analytical gradient does not match fd approximation." << std::endl;
        std::cout << "d2 Analytical: " << d2 << std::endl;
        std::cout << "d2 FD        : " << d2_fd << std::endl;
        assert(false);
      }
    
    template <class TargetSpace>
    
    void testHessianOfDistanceSquared(const TargetSpace& a, const TargetSpace& b)
    
      static const int embeddedDim = TargetSpace::embeddedDim;
    
      ///////////////////////////////////////////////////////////////////
      //  Test second derivative with respect to second argument
      ///////////////////////////////////////////////////////////////////
      FieldMatrix<double,embeddedDim,embeddedDim> d2d2 = (TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(a, b)).matrix();
    
      // finite-difference approximation
      FieldMatrix<double,embeddedDim,embeddedDim> d2d2_fd = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,b);
    
      if ( (d2d2 - d2d2_fd).infinity_norm() > 200*eps) {
        std::cout << className(a) << ": Analytical second derivative does not match fd approximation." << std::endl;
        std::cout << "d2d2 Analytical:" << std::endl << d2d2 << std::endl;
        std::cout << "d2d2 FD        :" << std::endl << d2d2_fd << std::endl;
        assert(false);
      }
    
    
    template <class TargetSpace>
    
    void testMixedDerivativesOfDistanceSquared(const TargetSpace& a, const TargetSpace& b)
    
      static const size_t embeddedDim = TargetSpace::embeddedDim;
    
      //////////////////////////////////////////////////////////////////////////////
      //  Test mixed second derivative with respect to first and second argument
      //////////////////////////////////////////////////////////////////////////////
    
      FieldMatrix<double,embeddedDim,embeddedDim> d1d2 = TargetSpace::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(a, b);
    
      // finite-difference approximation
      FieldMatrix<double,embeddedDim,embeddedDim> d1d2_fd;
    
      for (size_t i=0; i<embeddedDim; i++) {
        for (size_t j=0; j<embeddedDim; j++) {
    
          FieldVector<double,embeddedDim> aPlus  = a.globalCoordinates();
          FieldVector<double,embeddedDim> aMinus = a.globalCoordinates();
          aPlus[i]  += eps;
          aMinus[i] -= eps;
    
          FieldVector<double,embeddedDim> bPlus  = b.globalCoordinates();
          FieldVector<double,embeddedDim> bMinus = b.globalCoordinates();
          bPlus[j]  += eps;
          bMinus[j] -= eps;
    
          d1d2_fd[i][j] = (distanceSquared<TargetSpace>(aPlus,bPlus) + distanceSquared<TargetSpace>(aMinus,bMinus)
                           - distanceSquared<TargetSpace>(aPlus,bMinus) - distanceSquared<TargetSpace>(aMinus,bPlus)) / (4*eps*eps);
    
    
      }
    
      if ( (d1d2 - d1d2_fd).infinity_norm() > 200*eps ) {
        std::cout << className(a) << ": Analytical mixed second derivative does not match fd approximation." << std::endl;
        std::cout << "d1d2 Analytical:" << std::endl << d1d2 << std::endl;
        std::cout << "d1d2 FD        :" << std::endl << d1d2_fd << std::endl;
        assert(false);
      }
    
    template <class TargetSpace>
    
    void testDerivativeOfHessianOfDistanceSquared(const TargetSpace& a, const TargetSpace& b)
    
      static const size_t embeddedDim = TargetSpace::embeddedDim;
    
      /////////////////////////////////////////////////////////////////////////////////////////////
      //  Test mixed third derivative with respect to first (once) and second (twice) argument
      /////////////////////////////////////////////////////////////////////////////////////////////
    
      Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d2d2d2 = TargetSpace::thirdDerivativeOfDistanceSquaredWRTSecondArgument(a, b);
    
      Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d2d2d2_fd;
    
      for (size_t i=0; i<embeddedDim; i++) {
    
        FieldVector<double,embeddedDim> bPlus  = b.globalCoordinates();
        FieldVector<double,embeddedDim> bMinus = b.globalCoordinates();
        bPlus[i]  += eps;
        bMinus[i] -= eps;
    
        FieldMatrix<double,embeddedDim,embeddedDim> hPlus  = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,TargetSpace(bPlus));
        FieldMatrix<double,embeddedDim,embeddedDim> hMinus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,TargetSpace(bMinus));
    
        d2d2d2_fd[i] = hPlus;
        d2d2d2_fd[i] -= hMinus;
        d2d2d2_fd[i] /= 2*eps;
    
      }
    
      if ( (d2d2d2 - d2d2d2_fd).infinity_norm() > 200*eps) {
        std::cout << className(a) << ": Analytical third derivative does not match fd approximation." << std::endl;
        std::cout << "d2d2d2 Analytical:" << std::endl << d2d2d2 << std::endl;
        std::cout << "d2d2d2 FD        :" << std::endl << d2d2d2_fd << std::endl;
        assert(false);
      }
    
    template <class TargetSpace>
    
    void testMixedDerivativeOfHessianOfDistanceSquared(const TargetSpace& a, const TargetSpace& b)
    
      static const size_t embeddedDim = TargetSpace::embeddedDim;
    
      /////////////////////////////////////////////////////////////////////////////////////////////
      //  Test mixed third derivative with respect to first (once) and second (twice) argument
      /////////////////////////////////////////////////////////////////////////////////////////////
    
      Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d1d2d2 = TargetSpace::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(a, b);
    
      Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d1d2d2_fd;
    
      for (size_t i=0; i<embeddedDim; i++) {
    
        FieldVector<double,embeddedDim> aPlus  = a.globalCoordinates();
        FieldVector<double,embeddedDim> aMinus = a.globalCoordinates();
        aPlus[i]  += eps;
        aMinus[i] -= eps;
    
        FieldMatrix<double,embeddedDim,embeddedDim> hPlus  = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(TargetSpace(aPlus),b);
        FieldMatrix<double,embeddedDim,embeddedDim> hMinus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(TargetSpace(aMinus),b);
    
        d1d2d2_fd[i] = hPlus;
        d1d2d2_fd[i] -= hMinus;
        d1d2d2_fd[i] /= 2*eps;
    
      }
    
      if ( (d1d2d2 - d1d2d2_fd).infinity_norm() > 200*eps ) {
        std::cout << className(a) << ": Analytical mixed third derivative does not match fd approximation." << std::endl;
        std::cout << "d1d2d2 Analytical:" << std::endl << d1d2d2 << std::endl;
        std::cout << "d1d2d2 FD        :" << std::endl << d1d2d2_fd << std::endl;
        assert(false);
      }
    
    template <class TargetSpace>
    
    void testDerivativesOfDistanceSquared(const TargetSpace& a, const TargetSpace& b)
    
    
      ///////////////////////////////////////////////////////////////////
      //  Test derivative with respect to second argument
      ///////////////////////////////////////////////////////////////////
    
      testDerivativeOfDistanceSquared<TargetSpace>(a,b);
    
      ///////////////////////////////////////////////////////////////////
      //  Test second derivative with respect to second argument
      ///////////////////////////////////////////////////////////////////
    
      testHessianOfDistanceSquared<TargetSpace>(a,b);
    
      //////////////////////////////////////////////////////////////////////////////
      //  Test mixed second derivative with respect to first and second argument
      //////////////////////////////////////////////////////////////////////////////
    
      testMixedDerivativesOfDistanceSquared<TargetSpace>(a,b);
    
      /////////////////////////////////////////////////////////////////////////////////////////////
      //  Test third derivative with respect to second argument
      /////////////////////////////////////////////////////////////////////////////////////////////
    
      testDerivativeOfHessianOfDistanceSquared<TargetSpace>(a,b);
    
      /////////////////////////////////////////////////////////////////////////////////////////////
      //  Test mixed third derivative with respect to first (once) and second (twice) argument
      /////////////////////////////////////////////////////////////////////////////////////////////
    
      testMixedDerivativeOfHessianOfDistanceSquared<TargetSpace>(a,b);
    
    // The class ProductManifold had a bug that lead to a build failure
    // when using std::cout in the namespace Dune::GFE.
    //
    // See https://gitlab.mn.tu-dresden.de/osander/dune-gfe/-/merge_requests/114
    //
    // The following method is here to make sure that this bug does not
    // come back.
    namespace Dune::GFE
    {
    
      void testUsingIOStream()
      {
        std::cout << "dummy text" << std::endl;
      }
    
    template <class TargetSpace>
    void test()
    
      std::cout << "Testing class " << className<TargetSpace>() << std::endl;
    
      std::vector<TargetSpace> testPoints;
      ValueFactory<TargetSpace>::get(testPoints);
    
      int nTestPoints = testPoints.size();
    
      // Test each element in the list
      for (int i=0; i<nTestPoints; i++) {
    
        //testOrthonormalFrame<TargetSpace>(testPoints[i]);
    
        for (int j=0; j<nTestPoints; j++) {
    
          std::vector<TargetSpace> testPointPair(2);
          testPointPair[0] = testPoints[i];
          testPointPair[1] = testPoints[j];
          if (diameter(testPointPair) > TargetSpace::convexityRadius)
            continue;
    
          // Test the exponential map and the logarithm
          testExpLog(testPoints[i], testPoints[j]);
    
          // Test the various derivatives of the squared distance
          testDerivativesOfDistanceSquared<TargetSpace>(testPoints[i], testPoints[j]);
    
    
      }
    
      // Test whether we can rebind to another number type
      using FTargetSpace = typename TargetSpace::template rebind<float>::other;
    
      // Can we construct an object of that rebound type?
      FTargetSpace fTargetSpace;
    
    Oliver Sander's avatar
    Oliver Sander committed
    int main() try
    {
    
      // Test the RealTuple class
      test<RealTuple<double,1> >();
      test<RealTuple<double,3> >();
    
      // Test the UnitVector class
      test<UnitVector<double,2> >();
      test<UnitVector<double,3> >();
      test<UnitVector<double,4> >();
    
      // Test the rotation class
      test<Rotation<double,3> >();
    
      // Test the ProductManifold class
      test<Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2> > >();
      test<Dune::GFE::ProductManifold<Rotation<double,3>,UnitVector<double,5> > >();
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      //
      //     test<HyperbolicHalfspacePoint<double,2> >();
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      std::cout << e.what() << std::endl;
    
    }