Newer
Older

Oliver Sander
committed
#include <config.h>
#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>

Oliver Sander
committed
#include "valuefactory.hh"
/** \file
\brief Unit tests for classes that implement value manifolds for geodesic FE functions

Oliver Sander
committed

Oliver Sander
committed
/** \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;

Oliver Sander
committed
}
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);
}

Oliver Sander
committed
template <class TargetSpace>
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.

Oliver Sander
committed
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);

Oliver Sander
committed
/** \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;
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
///////////////////////////////////////////////////////////////////
// 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)
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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;
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/////////////////////////////////////////////////////////////////////////////////////////////
// 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;
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/////////////////////////////////////////////////////////////////////////////////////////////
// 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)
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
///////////////////////////////////////////////////////////////////
// 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);

Oliver Sander
committed
// 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()
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
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
committed
// 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> > >();
//
// test<HyperbolicHalfspacePoint<double,2> >();

Oliver Sander
committed
}
catch (Exception& e) {

Oliver Sander
committed
std::cout << e.what() << std::endl;
}