Compare commits

...
38 Commits
Author SHA1 Message Date
Tucker Babcock 0639425ff2 add margin to WeightRevDiff test 2023-07-19 10:35:50 -06:00
Tucker Babcock 8f4d4300c2 make style 2023-07-19 10:29:17 -06:00
Tucker Babcock f881903103 add IsoparametricTransformation::InverseJacobianRevDiff and DenseMatrix::CalcInverseRevDiff, both with tests 2023-07-19 10:28:54 -06:00
Tucker Babcock bab667fe8c make style 2022-09-15 10:48:00 -04:00
Tucker Babcock cef328b0db added mesh coord differentiation to ProductCoefficient with tests 2022-09-15 10:47:23 -04:00
Tucker Babcock 0b81ece2f4 fix issue in FunctionCoefficient::EvalRevDiff where the position vectors used the wrong size (were hardcoded to size 3 instead of space dimension) 2022-09-13 20:45:36 -04:00
Tucker Babcock 3dfdc52328 fix merge conflix I missed 2022-02-18 13:44:26 -07:00
Tucker Babcock 2b8b1ebe58 merge master into mesh-sens-dev 2022-02-18 13:39:09 -07:00
Tucker Babcock 08d4b29232 add CalcPhysShapeRevDiff method to FiniteElement that implements the reverse mode implementation of FiniteElement::CalcPhysShape wrt the mesh coordinates 2022-02-18 13:36:11 -07:00
Tucker Babcock ed5e10466d add vshapedxt and vshapedxt_bar variables to vector finite elements 2021-10-14 16:18:23 -06:00
Tucker Babcock 0c811d8487 update fe reverse mode differentiated functions to new file structure 2021-10-14 15:56:02 -06:00
Tucker Babcock 53d8356f19 reverse accidental changes to ex10p 2021-09-03 18:42:11 -06:00
Tucker Babcock 6fbd833e2f reverse accidental changes to ex10p 2021-09-03 18:40:56 -06:00
Tucker Babcock 71b6300e81 removing ReorientTetMesh and making sure meshes are sufficiently complex 2021-09-03 18:08:39 -06:00
Tucker Babcock 692b904b23 remove broken GridFunction::ProjectCoefficientRevDiff 2021-09-02 18:49:44 -06:00
Tucker Babcock 0533b79ec4 make style 2021-09-02 18:47:35 -06:00
Tucker Babcock f613037904 update copyright 2021-09-02 18:41:46 -06:00
Tucker Babcock 7e07042bed merge master into mesh-sens-dev 2021-09-02 18:41:34 -06:00
Tucker Babcock 601d14c6c6 merge master into mesh-sens-dev 2021-09-02 16:31:21 -06:00
Tucker Babcock 4bc31d4ba8 improved performance in some of the IsoparametricTransformation::*RevDiff methods by stack allocating dFdx_bar inside. Added additional FiniteElement::*RevDiff methods for differentiating various methods. Added EvalRevDiff methods to additional coefficient classes, and added tests for the coefficient differentiation as well as the finite element differentiation 2021-09-02 14:03:57 -06:00
Tucker Babcock 852cd5b11e added GridFunction::ProjectRevDiff, but it fails the test with more than one element. Temporarily changed the test CMakeLists.txt to build my test as its own executable so its faster to run the test 2020-05-14 22:54:22 -07:00
Tucker Babcock 9b523e3cd9 added test for ProjectRevDiff with tet elements 2020-05-14 09:37:02 -07:00
Tucker Babcock a1f3518511 Implemented reverse mode differentiation of Project for RT and ND elements projecting a VectorCoefficient. Added new test for this, fixed bug in VectorFunctionCoefficient::EvalRevDiff. 2020-05-13 17:25:15 -07:00
Tucker Babcock 52b145d3da Added EvalRevDiff to VectorCoefficient with default implementation to abort if not implemented in a derived class. Made EvalRevDiff virtual. 2020-05-13 14:32:55 -07:00
Jason Hicken 966cfb70ce added inputs for FunctionRevDiff and TDFunctionRevDiff in VectorFucntionCoefficient 2020-05-13 16:52:31 -04:00
Jason Hicken 0683e8c754 Merge branch 'mesh-sens-dev' of github.com:mfem/mfem into mesh-sens-dev 2020-05-13 16:03:45 -04:00
Jason Hicken d19abbbcd4 added VectorCoefficient::EvalRevDiff...not tested! 2020-05-13 16:02:27 -04:00
Tucker Babcock 5a53f301d7 reverting new constructor 2020-05-13 11:29:50 -07:00
Tucker Babcock 8a87ccceae added new constructor for VectorFunctionCoefficient that constructs a coefficient with the function's Jacobian, needed for EvalRevDiff 2020-05-13 10:31:44 -07:00
Jason Hicken d6f828838a changed IsoparametricTransformation reverse-diff functions to accumulate derivatives 2020-04-21 22:09:55 -04:00
garobed1 2f9a63acab Merge pull request #1431 from garobed1/mesh-sens-dev-prgb1
minor compilation error fixes
2020-04-20 12:10:01 -04:00
Garo Bedonian 1d59a2667f minor compilation error fixes 2020-04-20 11:56:21 -04:00
Jason Hicken 13462698ab Added reverse-diff version of IsoparametricTransformation::Transform(ip,x) 2020-04-19 22:00:26 -04:00
Jason Hicken 826411f669 added reverse differentiation of CalcOrtho; added corresponding tests 2020-04-19 18:02:09 -04:00
Jason Hicken 52bb0871b3 added reverse-mode differentiated Jacobian, AdjugateJacobian, and Weight functions to IsoparametricTransformation; included unit test 2020-04-16 22:31:37 -04:00
Jason Hicken f9fb52a91c Generalized CalcAdjugateRevDiff to handle 2x1 and 3x1 matrices 2020-04-16 11:45:21 -04:00
Jason Hicken 674e5511c5 Added differentiated versions of DenseMatrix::Det() and DenseMatrix::Weight() 2020-04-14 22:51:13 -04:00
Jason Hicken f936173a7f Added reverse-diff of CalcAdjugate; included unit test 2020-04-14 19:31:29 -04:00
15 changed files with 2770 additions and 1 deletions
+124
View File
@@ -14,6 +14,7 @@
#include "fem.hpp"
#include <cmath>
#include <cstddef>
#include <limits>
namespace mfem
@@ -48,6 +49,15 @@ ElementTransformation *RefinedToCoarse(
return coarse_T;
}
void Coefficient::EvalRevDiff(const double Q_bar,
ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar)
{
MFEM_ABORT("Coefficient::EvalRevDiff\n"
"\tEvalRevDiff not implemented for this coefficient!\n");
}
double PWConstCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
@@ -119,6 +129,34 @@ double FunctionCoefficient::Eval(ElementTransformation & T,
}
}
void FunctionCoefficient::EvalRevDiff(const double Q_bar,
ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar)
{
int space_dim = T.GetSpaceDim();
double x[3] = {};
Vector transip(x, space_dim);
T.Transform(ip, transip);
double x_bar[3] = {};
Vector transip_bar(x_bar, space_dim);
if (Function)
{
MFEM_ASSERT(FunctionRevDiff, "EvalRevDiff: reverse-mode differentiated "
"version of Function must be provided");
FunctionRevDiff(transip, Q_bar, transip_bar);
}
else
{
MFEM_ASSERT(TDFunctionRevDiff, "EvalRevDiff: reverse-mode differentiated"
" version of TDFunction must be provided");
TDFunctionRevDiff(transip, GetTime(), Q_bar, transip_bar);
}
static_cast<IsoparametricTransformation &>(T).TransformRevDiff(
ip, transip_bar, PointMat_bar);
}
double GridFunctionCoefficient::Eval (ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -189,6 +227,15 @@ void RestrictedCoefficient::SetTime(double t)
this->Coefficient::SetTime(t);
}
void VectorCoefficient::EvalRevDiff(const Vector &V_bar,
ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar)
{
MFEM_ABORT("VectorCoefficient::EvalRevDiff\n"
"\tEvalRevDiff not implemented for this coefficient!\n");
}
void VectorCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir)
{
@@ -283,6 +330,35 @@ void VectorFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
}
}
void VectorFunctionCoefficient::EvalRevDiff(const Vector &V_bar,
ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar)
{
MFEM_ASSERT( Q == NULL, "EvalRevDiff: not implemented for use with Q.")
double x[3];
Vector transip(x, vdim);
double x_bar[3];
Vector transip_bar(x_bar, vdim);
T.Transform(ip, transip);
transip_bar = 0.0;
if (Function)
{
MFEM_ASSERT(FunctionRevDiff, "EvalRevDiff: reverse-mode differentiated "
"version of Function must be provided");
FunctionRevDiff(transip, V_bar, transip_bar);
}
else
{
MFEM_ASSERT(TDFunctionRevDiff, "EvalRevDiff: reverse-mode differentiated"
" version of TDFunction must be provided");
TDFunctionRevDiff(transip, GetTime(), V_bar, transip_bar);
}
static_cast<IsoparametricTransformation &>(T).TransformRevDiff(
ip, transip_bar, PointMat_bar);
}
VectorArrayCoefficient::VectorArrayCoefficient (int dim)
: VectorCoefficient(dim), Coeff(dim), ownCoeff(dim)
{
@@ -797,6 +873,25 @@ void ProductCoefficient::SetTime(double t)
this->Coefficient::SetTime(t);
}
void ProductCoefficient::EvalRevDiff(const double Q_bar,
ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar)
{
if (a == nullptr)
{
b->EvalRevDiff(Q_bar * aConst, T, ip, PointMat_bar);
}
else
{
double a_val = a->Eval(T, ip);
double b_val = b->Eval(T, ip);
a->EvalRevDiff(Q_bar * b_val, T, ip, PointMat_bar);
b->EvalRevDiff(Q_bar * a_val, T, ip, PointMat_bar);
}
}
void RatioCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
@@ -967,6 +1062,35 @@ void ScalarVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
V *= sa;
}
void ScalarVectorProductCoefficient::EvalRevDiff(
const Vector &V_bar,
ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar)
{
#ifdef MFEM_THREAD_SAFE
Vector W(V_bar.Size());
Vector W_bar(V_bar.Size());
#else
W.SetSize(V_bar.Size());
W_bar.SetSize(V_bar.Size());
#endif
double sa = (a == nullptr) ? aConst : a->Eval(T, ip);
b->Eval(W, T, ip);
W *= sa;
/// reverse pass
W_bar = 0.0;
add(W_bar, sa, V_bar, W_bar);
b->EvalRevDiff(W_bar, T, ip, PointMat_bar);
if (a != nullptr)
{
const double sa_bar = V_bar * W;
a->EvalRevDiff(sa_bar, T, ip, PointMat_bar);
}
}
NormalizedVectorCoefficient::NormalizedVectorCoefficient(VectorCoefficient &A,
double tol_)
: VectorCoefficient(A.GetVDim()), a(&A), tol(tol_)
+111
View File
@@ -58,6 +58,21 @@ public:
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip) = 0;
/** @brief Reverse-mode differentiation of Eval w.r.t. the mesh node
locations in the element described by @a T, accumulating the result in
@a PointMat_bar */
/** @param[in] Q_bar - derivative of some output w.r.t. result of Eval */
/** @param[in] T - an element transformation */
/** @param[in] ip - defines location in reference space */
/** @param[inout] PointMat_bar - derivative of output w.r.t. mesh nodes */
/** @note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
virtual void EvalRevDiff(const double Q_bar,
ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar);
/** @brief Evaluate the coefficient in the element described by @a T at the
point @a ip at time @a t. */
/** @note When this method is called, the caller must make sure that the
@@ -211,7 +226,15 @@ class FunctionCoefficient : public Coefficient
{
protected:
std::function<double(const Vector &)> Function;
std::function<void(const Vector &,
const double,
Vector &)> FunctionRevDiff;
std::function<double(const Vector &, double)> TDFunction;
std::function<void(const Vector &,
double,
const double,
Vector &)> TDFunctionRevDiff;
public:
/// Define a time-independent coefficient from a std function
@@ -226,6 +249,23 @@ public:
: TDFunction(std::move(TDF))
{ }
/// Construct time-independent coefficient that can be differentiated
FunctionCoefficient(std::function<double(const Vector &)> F,
std::function<void(const Vector &,
const double,
Vector &)> dF)
: Function(F), FunctionRevDiff(dF)
{ }
/// Construct time-dependent coefficient that can be differentiated
FunctionCoefficient(std::function<double(const Vector &, double)> TDF,
std::function<void(const Vector &,
double,
const double,
Vector &)> dTDF)
: TDFunction(TDF), TDFunctionRevDiff(dTDF)
{ }
/// (DEPRECATED) Define a time-independent coefficient from a C-function
/** @deprecated Use the method where the C-function, @a f, uses a const
Vector argument instead of Vector. */
@@ -247,6 +287,11 @@ public:
/// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
virtual void EvalRevDiff(const double Q_bar,
ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar);
};
class GridFunction;
@@ -456,6 +501,20 @@ public:
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) = 0;
/** @brief Reverse-mode differentiation of Eval w.r.t. the mesh node
locations in the element described by @a T, accumulating the result in
@a PointMat_bar */
/** @param[in] V_bar - derivative of some output with respect to `V` */
/** @param[in] T - an element transformation */
/** @param[in] ip - defines location in reference space */
/** @param[inout] PointMat_bar - derivative of output w.r.t. mesh nodes */
/** @note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
virtual void EvalRevDiff(const Vector &V_bar, ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar);
/** @brief Evaluate the vector coefficient in the element described by @a T
at all points of @a ir, storing the result in @a M. */
/** The dimensions of @a M are GetVDim() by ir.GetNPoints() and they must be
@@ -490,6 +549,10 @@ public:
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) { V = vec; }
virtual void EvalRevDiff(const Vector &V_bar, ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar) { }
/// Return a reference to the constant vector in this class.
const Vector& GetVec() { return vec; }
};
@@ -582,8 +645,17 @@ class VectorFunctionCoefficient : public VectorCoefficient
{
private:
std::function<void(const Vector &, Vector &)> Function;
std::function<void(const Vector &,
const Vector &,
Vector &)> FunctionRevDiff;
std::function<void(const Vector &, double, Vector &)> TDFunction;
std::function<void(const Vector &,
double,
const Vector &,
Vector &)> TDFunctionRevDiff;
Coefficient *Q;
// Coefficient *dQ;
public:
/// Define a time-independent vector coefficient from a std function
@@ -606,11 +678,37 @@ public:
: VectorCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
{ }
/// Construct time-independent vector coefficient that can be differentiated
VectorFunctionCoefficient(int dim,
std::function<void(const Vector &,
Vector &)> F,
std::function<void(const Vector &,
const Vector &,
Vector &)> dF)
: VectorCoefficient(dim), Function(std::move(F)),
FunctionRevDiff(std::move(dF)), Q(NULL)
{ }
/// Construct time-dependent vector coefficient that can be differentiated
VectorFunctionCoefficient(int dim,
std::function<void(const Vector &,
double,
Vector &)> TDF,
std::function<void(const Vector &,
double, const Vector &, Vector &)> dTDF)
: VectorCoefficient(dim), TDFunction(std::move(TDF)),
TDFunctionRevDiff(std::move(dTDF)), Q(NULL)
{ }
using VectorCoefficient::Eval;
/// Evaluate the vector coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
virtual void EvalRevDiff(const Vector &V_bar, ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar);
virtual ~VectorFunctionCoefficient() { }
};
@@ -1388,6 +1486,11 @@ public:
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{ return ((a == NULL ) ? aConst : a->Eval(T, ip) ) * b->Eval(T, ip); }
void EvalRevDiff(const double Q_bar,
ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar);
};
/** @brief Scalar coefficient defined as the ratio of two scalars where one or
@@ -1657,6 +1760,9 @@ private:
double aConst;
Coefficient * a;
VectorCoefficient * b;
#ifndef MFEM_THREAD_SAFE
Vector W, W_bar;
#endif
public:
/// Constructor with constant and vector coefficient. Result is A * B.
@@ -1687,6 +1793,11 @@ public:
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
virtual void EvalRevDiff(const Vector &V_bar, ElementTransformation &T,
const IntegrationPoint &ip,
DenseMatrix &PointMat_bar);
};
/// Vector coefficient defined as a normalized vector field (returns v/|v|)
+76
View File
@@ -532,6 +532,82 @@ void IsoparametricTransformation::Transform (const DenseMatrix &matrix,
}
}
void IsoparametricTransformation::TransformRevDiff(const IntegrationPoint &ip,
const Vector &x_bar,
DenseMatrix &PointMat_bar)
{
MFEM_ASSERT((PointMat_bar.Width() == PointMat.Width()) &&
(PointMat_bar.Height() == PointMat.Height()),
"PointMat_bar shape != PointMat shape");
shape.SetSize(FElem->GetDof());
FElem->CalcShape(ip, shape);
AddMultVWt(x_bar, shape, PointMat_bar);
}
void IsoparametricTransformation::JacobianRevDiff(const DenseMatrix &dFdx_bar,
DenseMatrix &PointMat_bar)
{
MFEM_ASSERT((PointMat_bar.Width() == PointMat.Width()) &&
(PointMat_bar.Height() == PointMat.Height()),
"PointMat_bar shape != PointMat shape");
dshape.SetSize(FElem->GetDof(), FElem->GetDim());
if (dshape.Width() > 0)
{
// The math here can be found in Giles' report "An extended collection of
// matrix derivative results for forward and reverse mode algorithmic
// differentiation"
FElem->CalcDShape(*IntPoint, dshape);
AddMultABt(dFdx_bar, dshape, PointMat_bar);
}
}
void IsoparametricTransformation::AdjugateJacobianRevDiff(
const DenseMatrix &adjJ_bar, DenseMatrix &PointMat_bar)
{
Jacobian(); // Recompute the Jacobian, if necessary
double dFdx_bar_buffer[9];
DenseMatrix dFdx_bar(dFdx_bar_buffer, dFdx.Height(), dFdx.Width());
if (dFdx.Width() > 0)
{
CalcAdjugateRevDiff(dFdx, adjJ_bar, dFdx_bar);
}
JacobianRevDiff(dFdx_bar, PointMat_bar);
}
void IsoparametricTransformation::InverseJacobianRevDiff(
const DenseMatrix &invJ_bar, DenseMatrix &PointMat_bar)
{
Jacobian(); // Recompute the Jacobian, if necessary
double dFdx_bar_buffer[9];
DenseMatrix dFdx_bar(dFdx_bar_buffer, dFdx.Height(), dFdx.Width());
if (dFdx.Width() > 0)
{
CalcInverseRevDiff(dFdx, invJ_bar, dFdx_bar);
}
JacobianRevDiff(dFdx_bar, PointMat_bar);
}
void IsoparametricTransformation::WeightRevDiff(DenseMatrix &PointMat_bar)
{
Jacobian(); // Recompute the Jacobian, if necessary
double dFdx_bar_buffer[9];
DenseMatrix dFdx_bar(dFdx_bar_buffer, dFdx.Height(), dFdx.Width());
dFdx.WeightRevDiff(dFdx_bar);
JacobianRevDiff(dFdx_bar, PointMat_bar);
}
void IsoparametricTransformation::WeightRevDiff(double weight_bar,
DenseMatrix &PointMat_bar)
{
Jacobian(); // Recompute the Jacobian, if necessary
double dFdx_bar_buffer[9];
DenseMatrix dFdx_bar(dFdx_bar_buffer, dFdx.Height(), dFdx.Width());
dFdx.WeightRevDiff(dFdx_bar);
dFdx_bar *= weight_bar;
JacobianRevDiff(dFdx_bar, PointMat_bar);
}
void IntegrationPointTransformation::Transform (const IntegrationPoint &ip1,
IntegrationPoint &ip2)
{
+52
View File
@@ -446,6 +446,58 @@ public:
return inv_tr.Transform(v, ip);
}
/// @brief Reverse-mode differentiation of Transform() w.r.t. PointMat
/// @param[in] ip - specifies the location in reference space
/// @param[in] x_bar - derivative of some output w.r.t. x
/// @param[out] PointMat_bar - derivative of output w.r.t. PointMat
/// @note PointMat_bar must have the same shape as PointMat
/// @warning This routine does not initialize PointMat_bar, and instead
/// accumulates (with += or -=) contributions to its derivative.
void TransformRevDiff(const IntegrationPoint &ip, const Vector &x_bar,
DenseMatrix &PointMat_bar);
/// @brief Reverse-mode differentiation of Jacobian() w.r.t. PointMat
/// @param[in] dFdx_bar - derivative of functional w.r.t. Jacobian
/// @param[out] PointMat_bar - derivative w.r.t. PointMat
/// @note PointMat_bar must have the same shape as PointMat
/// @warning This routine does not initialize PointMat_bar, and instead
/// accumulates (with += or -=) contributions to its derivative.
void JacobianRevDiff(const DenseMatrix &dFdx_bar,
DenseMatrix &PointMat_bar);
/// @brief Reverse-mode differentiation of AdjugateJacobian() w.r.t. PointMat
/// @param[in] adjJ_bar - derivative of functional w.r.t. Adjugate
/// @param[out] PointMat_bar - derivative w.r.t. PointMat
/// @note PointMat_bar must have the same shape as PointMat
/// @warning This routine does not initialize PointMat_bar, and instead
/// accumulates (with += or -=) contributions to its derivative.
void AdjugateJacobianRevDiff(const DenseMatrix &adjJ_bar,
DenseMatrix &PointMat_bar);
/// @brief Reverse-mode differentiation of InverseJacobian() w.r.t PointMat
/// @param[in] invJ_bar - derivative of functional w.r.t. Inverse
/// @param[out] PointMat_bar - derivative w.r.t. PointMat
/// @note PointMat_bar must have the same shape as PointMat
/// @warning This routine does not initialize PointMat_bar, and instead
/// accumulates (with += or -=) contributions to its derivative.
void InverseJacobianRevDiff(const DenseMatrix &adjJ_bar,
DenseMatrix &PointMat_bar);
/// @brief Reverse-mode differentiation of Weight()
/// @param[out] PointMat_bar - derivative of functional w.r.t. PointMat
/// @note PointMat_bar must have the same shape as PointMat
/// @warning This routine does not initialize PointMat_bar, and instead
/// accumulates (with += or -=) contributions to its derivative.
void WeightRevDiff(DenseMatrix &PointMat_bar);
/// @brief Reverse-mode differentiation of Weight()
/// @param[in] weight_bar - derivative of functional w.r.t Weight
/// @param[out] PointMat_bar - derivative of functional w.r.t. PointMat
/// @note PointMat_bar must have the same shape as PointMat
/// @warning This routine does not initialize PointMat_bar, and instead
/// accumulates (with += or -=) contributions to its derivative.
void WeightRevDiff(double weight_bar, DenseMatrix &PointMat_bar);
virtual ~IsoparametricTransformation() { }
MFEM_DEPRECATED void FinalizeTransformation() {}
+255
View File
@@ -30,6 +30,7 @@ FiniteElement::FiniteElement(int D, Geometry::Type G, int Do, int O, int F)
deriv_map_type = VALUE;
for (int i = 0; i < Geometry::MaxDim; i++) { orders[i] = -1; }
#ifndef MFEM_THREAD_SAFE
shape.SetSize(dof);
vshape.SetSize(dof, dim);
#endif
}
@@ -46,6 +47,13 @@ void FiniteElement::CalcVShape (
MFEM_ABORT("method is not implemented for this class");
}
void FiniteElement::CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{
MFEM_ABORT("method is not implemented for this class");
}
void FiniteElement::CalcDivShape (
const IntegrationPoint &ip, Vector &divshape) const
{
@@ -90,6 +98,60 @@ void FiniteElement::CalcPhysCurlShape(ElementTransformation &Trans,
}
}
void FiniteElement::CalcPhysCurlShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &curlshape_bar,
DenseMatrix &PointMat_bar) const
{
switch (dim)
{
case 3:
{
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
DenseMatrix vshapedxt(dof, dim);
DenseMatrix vshapedxt_bar(dof, dim);
CalcCurlShape(Trans.GetIntPoint(), vshape);
const auto &jac = Trans.Jacobian();
MultABt(vshape, jac, vshapedxt);
const double weight = Trans.Weight();
// curl_shape *= 1.0 / weight;
/// start reverse pass
auto &isotrans = dynamic_cast<IsoparametricTransformation&>(Trans);
/// curl_shape = vshapedxt / weight;
double weight_bar = 0.0;
for (int j = 0; j < curlshape_bar.Width(); ++j)
{
for (int i = 0; i < curlshape_bar.Height(); ++i)
{
weight_bar -= curlshape_bar(i,j) * vshapedxt(i,j) / pow(weight, 2);
}
}
vshapedxt_bar = curlshape_bar; vshapedxt_bar *= (1.0 / weight);
/// double weight = Trans.Weight();
isotrans.WeightRevDiff(weight_bar, PointMat_bar);
/// const auto &jac = Trans.Jacobian();
/// MultABt(vshape, jac, vshapedxt);
double jac_bar_buffer[9];
DenseMatrix jac_bar(jac_bar_buffer, jac.Width(), jac.Height());
MultAtB(vshapedxt_bar, vshape, jac_bar);
isotrans.JacobianRevDiff(jac_bar, PointMat_bar);
break;
}
case 2:
MFEM_ABORT("CalcPhysCurlShapeRevDiff not implemented!\n");
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
}
void FiniteElement::GetFaceDofs(int face, int **dofs, int *ndofs) const
{
MFEM_ABORT("method is not overloaded");
@@ -138,6 +200,16 @@ void FiniteElement::ProjectFromNodes(Vector &vc, ElementTransformation &Trans,
mfem_error ("FiniteElement::ProjectFromNodes() (vector) is not overloaded!");
}
void FiniteElement::ProjectRevDiff (
const Vector &P_bar,
VectorCoefficient &vc,
ElementTransformation &Trans,
DenseMatrix &PointMat_bar) const
{
mfem_error ("FiniteElement::ProjectRevDiff (...) (vector) is not "
"overloaded !");
}
void FiniteElement::ProjectMatrixCoefficient(
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
{
@@ -186,6 +258,26 @@ void FiniteElement::CalcPhysShape(ElementTransformation &Trans,
}
}
void FiniteElement::CalcPhysShapeRevDiff(ElementTransformation &Trans,
const Vector &shape_bar,
DenseMatrix &PointMat_bar) const
{
if (map_type == INTEGRAL)
{
#ifdef MFEM_THREAD_SAFE
Vector shape(dof);
#endif
CalcShape(Trans.GetIntPoint(), shape);
// shape /= Trans.Weight();
auto weight = Trans.Weight();
auto weight_bar = -(shape_bar * shape) / pow(weight, 2);
// cast the ElementTransformation
auto &isotrans = dynamic_cast<IsoparametricTransformation &>(Trans);
isotrans.WeightRevDiff(weight_bar, PointMat_bar);
}
}
void FiniteElement::CalcPhysDShape(ElementTransformation &Trans,
DenseMatrix &dshape) const
{
@@ -894,6 +986,46 @@ void VectorFiniteElement::CalcVShape_RT (
shape *= (1.0 / Trans.Weight());
}
void VectorFiniteElement::CalcVShape_RTRevDiff(ElementTransformation &Trans,
const DenseMatrix &vshape_bar,
DenseMatrix &PointMat_bar) const
{
MFEM_ASSERT(map_type == H_DIV, "");
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
DenseMatrix vshapedxt(dof, dim);
DenseMatrix vshapedxt_bar(dof, dim);
#else
vshapedxt.SetSize(dof, dim);
vshapedxt_bar.SetSize(dof, dim);
#endif
CalcVShape(Trans.GetIntPoint(), vshape);
const auto &jac = Trans.Jacobian();
MultABt(vshape, jac, vshapedxt);
const double weight = Trans.Weight();
// shape *= (1.0 / weight);
/// start reverse pass
auto &isotrans = dynamic_cast<IsoparametricTransformation&>(Trans);
double weight_bar = 0.0;
for (int j = 0; j < vshape_bar.Width(); ++j)
{
for (int i = 0; i < vshape_bar.Height(); ++i)
{
weight_bar -= vshape_bar(i,j) * vshapedxt(i,j) / pow(weight,2);
}
}
isotrans.WeightRevDiff(weight_bar, PointMat_bar);
vshapedxt_bar = vshape_bar; vshapedxt_bar *= (1.0 / weight);
double jac_bar_buffer[9];
DenseMatrix jac_bar(jac_bar_buffer, jac.Width(), jac.Height());
MultAtB(vshapedxt_bar, vshape, jac_bar);
isotrans.JacobianRevDiff(jac_bar, PointMat_bar);
}
void VectorFiniteElement::CalcVShape_ND (
ElementTransformation &Trans, DenseMatrix &shape) const
{
@@ -905,6 +1037,46 @@ void VectorFiniteElement::CalcVShape_ND (
Mult(vshape, Trans.InverseJacobian(), shape);
}
void VectorFiniteElement::CalcVShape_NDRevDiff(ElementTransformation &Trans,
const DenseMatrix &vshape_bar,
DenseMatrix &PointMat_bar) const
{
MFEM_ASSERT(map_type == H_CURL, "");
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
DenseMatrix vshapedxt(dof, dim);
DenseMatrix vshapedxt_bar(dof, dim);
#else
vshapedxt.SetSize(dof, dim);
vshapedxt_bar.SetSize(dof, dim);
#endif
CalcVShape(Trans.GetIntPoint(), vshape);
const auto &adjJ = Trans.AdjugateJacobian();
Mult(vshape, adjJ, vshapedxt);
const double weight = Trans.Weight();
// shape *= (1.0 / weight);
/// start reverse pass
auto &isotrans = dynamic_cast<IsoparametricTransformation&>(Trans);
double weight_bar = 0.0;
for (int j = 0; j < vshape_bar.Width(); ++j)
{
for (int i = 0; i < vshape_bar.Height(); ++i)
{
weight_bar -= vshape_bar(i,j) * vshapedxt(i,j) / pow(weight, 2);
}
}
isotrans.WeightRevDiff(weight_bar, PointMat_bar);
vshapedxt_bar = vshape_bar; vshapedxt_bar *= (1.0 / weight);
double adjJ_bar_buffer[9];
DenseMatrix adjJ_bar(adjJ_bar_buffer, adjJ.Width(), adjJ.Height());
MultAtB(vshape, vshapedxt_bar, adjJ_bar);
isotrans.AdjugateJacobianRevDiff(adjJ_bar, PointMat_bar);
}
void VectorFiniteElement::Project_RT(
const double *nk, const Array<int> &d2n,
VectorCoefficient &vc, ElementTransformation &Trans, Vector &dofs) const
@@ -942,6 +1114,47 @@ void VectorFiniteElement::Project_RT(
}
}
void VectorFiniteElement::Project_RTRevDiff(
const Vector &P_bar,
const double *nk, const Array<int> &d2n,
VectorCoefficient &vc, ElementTransformation &Trans,
DenseMatrix &PointMat_bar) const
{
double vk[Geometry::MaxDim];
const int sdim = Trans.GetSpaceDim();
MFEM_ASSERT(vc.GetVDim() == sdim, "");
Vector xk(vk, sdim);
MFEM_ASSERT(dim == sdim, "VectorFiniteElement::Project_RTRevDiff\n"
"\tOnly implemented if space dim == reference dim!\n");
DenseMatrix temp_bar(PointMat_bar.Height(), PointMat_bar.Width());
IsoparametricTransformation &isotrans =
dynamic_cast<IsoparametricTransformation&>(Trans);
for (int k = 0; k < dof; k++)
{
temp_bar = 0.0;
isotrans.SetIntPoint(&Nodes.IntPoint(k));
vc.Eval(xk, isotrans, Nodes.IntPoint(k));
// dof_k = nk^t adj(J) xk
const Vector nk_vec(const_cast<double*>(nk + d2n[k]*dim), sdim);
double adjJ_bar_buffer[Geometry::MaxDim*Geometry::MaxDim];
DenseMatrix adjJ_bar(adjJ_bar_buffer, dim, dim);
MultVWt(nk_vec, xk, adjJ_bar);
isotrans.AdjugateJacobianRevDiff(adjJ_bar, temp_bar);
double V_bar_buffer[Geometry::MaxDim];
Vector V_bar(V_bar_buffer, sdim);
isotrans.AdjugateJacobian().MultTranspose(nk_vec, V_bar);
vc.EvalRevDiff(V_bar, isotrans,
Nodes.IntPoint(k), temp_bar);
temp_bar *= P_bar(k);
PointMat_bar += temp_bar;
}
}
void VectorFiniteElement::ProjectMatrixCoefficient_RT(
const double *nk, const Array<int> &d2n,
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
@@ -1159,6 +1372,48 @@ void VectorFiniteElement::Project_ND(
}
}
void VectorFiniteElement::Project_NDRevDiff(
const Vector &P_bar,
const double *tk, const Array<int> &d2t,
VectorCoefficient &vc, ElementTransformation &Trans,
DenseMatrix &PointMat_bar) const
{
double vk[Geometry::MaxDim];
const int sdim = Trans.GetSpaceDim();
MFEM_ASSERT(vc.GetVDim() == sdim, "");
Vector xk(vk, sdim);
MFEM_ASSERT(dim == sdim, "VectorFiniteElement::Project_NDRevDiff\n"
"\tOnly implemented if space dim == reference dim!\n");
DenseMatrix temp_bar(PointMat_bar.Height(), PointMat_bar.Width());
IsoparametricTransformation &isotrans =
dynamic_cast<IsoparametricTransformation&>(Trans);
for (int k = 0; k < dof; k++)
{
temp_bar = 0.0;
isotrans.SetIntPoint(&Nodes.IntPoint(k));
vc.Eval(xk, isotrans, Nodes.IntPoint(k));
// dof_k = nk^t J xk
const Vector tk_vec(const_cast<double*>(tk + d2t[k]*dim), sdim);
double J_bar_buffer[Geometry::MaxDim*Geometry::MaxDim];
DenseMatrix J_bar(J_bar_buffer, dim, dim);
MultVWt(xk, tk_vec, J_bar);
isotrans.JacobianRevDiff(J_bar, temp_bar);
double V_bar_buffer[Geometry::MaxDim];
Vector V_bar(V_bar_buffer, sdim);
isotrans.Jacobian().Mult(tk_vec, V_bar);
vc.EvalRevDiff(V_bar, isotrans,
Nodes.IntPoint(k), temp_bar);
temp_bar *= P_bar(k);
PointMat_bar += temp_bar;
}
}
void VectorFiniteElement::ProjectMatrixCoefficient_ND(
const double *tk, const Array<int> &d2t,
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
+63
View File
@@ -245,6 +245,7 @@ protected:
mutable int orders[Geometry::MaxDim]; ///< Anisotropic orders
IntegrationRule Nodes;
#ifndef MFEM_THREAD_SAFE
mutable Vector shape;
mutable DenseMatrix vshape; // Dof x Dim
#endif
/// Container for all DofToQuad objects created by the FiniteElement.
@@ -362,6 +363,10 @@ public:
/** The size (#dof) of the result Vector @a shape must be set in advance. */
void CalcPhysShape(ElementTransformation &Trans, Vector &shape) const;
void CalcPhysShapeRevDiff(ElementTransformation &Trans,
const Vector &shape_bar,
DenseMatrix &PointMat_bar) const;
/** @brief Evaluate the gradients of all shape functions of a scalar finite
element in reference space at the given point @a ip. */
/** Each row of the result DenseMatrix @a dshape contains the derivatives of
@@ -400,6 +405,10 @@ public:
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const;
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const;
/// Equivalent to the CalcVShape() method with the same arguments.
void CalcPhysVShape(ElementTransformation &Trans, DenseMatrix &shape) const
{ CalcVShape(Trans, shape); }
@@ -435,6 +444,10 @@ public:
void CalcPhysCurlShape(ElementTransformation &Trans,
DenseMatrix &curl_shape) const;
void CalcPhysCurlShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &curlshape_bar,
DenseMatrix &PointMat_bar) const;
/** @brief Get the dofs associated with the given @a face.
@a *dofs is set to an internal array of the local dofc on the
face, while *ndofs is set to the number of dofs on that face.
@@ -519,6 +532,14 @@ public:
virtual void Project(VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const;
/** Given a vector coefficient and a transformation, compute the derivative of
its projection (approximation) in the local finite dimensional space
w.r.t. the mesh nodes (VectorFiniteElements) */
virtual void ProjectRevDiff(const Vector &P_bar,
VectorCoefficient &vc,
ElementTransformation &Trans,
DenseMatrix &PointMat_bar) const;
/** @brief Given a vector of values at the finite element nodes and a
transformation, compute its projection (approximation) in the local
finite dimensional space in terms of the degrees of freedom. Valid for
@@ -791,15 +812,24 @@ protected:
#ifndef MFEM_THREAD_SAFE
mutable DenseMatrix J, Jinv;
mutable DenseMatrix curlshape, curlshape_J;
mutable DenseMatrix vshapedxt, vshapedxt_bar;
#endif
void SetDerivMembers();
void CalcVShape_RT(ElementTransformation &Trans,
DenseMatrix &shape) const;
void CalcVShape_RTRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const;
void CalcVShape_ND(ElementTransformation &Trans,
DenseMatrix &shape) const;
void CalcVShape_NDRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const;
/** @brief Project a vector coefficient onto the RT basis functions
@param nk Face normal vectors for this element type
@param d2n Offset into nk for each degree of freedom
@@ -845,6 +875,23 @@ protected:
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const;
/** Reverse-mode differentiation of Project_ND w.r.t. the mesh node
locations in the element described by @a T
@param[in] P_bar - derivative of function with respect to the projection
@param[in] nk - Face normal vectors for this element type
@param[in] d2n - Offset into nk for each degree of freedom
@param[in] vc - VectorCoefficient being projected
@param[in] Trans - an element transformation
@param[out] PointMat_bar - derivative of projected degrees of freedom w.r.t.
mesh nodes
@warning - only implemented for the same space and reference dimension
*/
void Project_RTRevDiff(const Vector &P_bar,
const double *nk, const Array<int> &d2n,
VectorCoefficient &vc,
ElementTransformation &Trans,
DenseMatrix &PointMat_bar) const;
// rotated gradient in 2D
void ProjectGrad_RT(const double *nk, const Array<int> &d2n,
const FiniteElement &fe, ElementTransformation &Trans,
@@ -883,6 +930,22 @@ protected:
Vector &vc, ElementTransformation &Trans,
Vector &dofs) const;
/** Reverse-mode differentiation of Project_ND w.r.t. the mesh node
locations in the element described by @a T
@param[in] P_bar - derivative of output with respect to the projection
@param[in] tk - Edge tangent vectors for this element type
@param[in] d2t - Offset into tk for each degree of freedom
@param[in] vc - Vector coefficient being projected
@param[in] Trans - Transformation from reference to physical coordinates
@param[out] PointMat_bar - derivative of some output w.r.t. mesh nodes
@warning - only implemented for the same space and reference dimension
*/
void Project_NDRevDiff(const Vector &P_bar,
const double *tk, const Array<int> &d2t,
VectorCoefficient &vc,
ElementTransformation &Trans,
DenseMatrix &PointMat_bar) const;
/// Project the rows of the matrix coefficient in an ND space
void ProjectMatrixCoefficient_ND(
const double *tk, const Array<int> &d2t,
+31
View File
@@ -42,6 +42,11 @@ public:
DenseMatrix &shape) const
{ CalcVShape_ND(Trans, shape); }
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{ CalcVShape_NDRevDiff(Trans, shape_bar, PointMat_bar); }
virtual void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const;
@@ -120,6 +125,10 @@ public:
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_ND(Trans, shape); }
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{ CalcVShape_NDRevDiff(Trans, shape_bar, PointMat_bar); }
virtual void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
@@ -139,6 +148,11 @@ public:
if (obasis1d.IsIntegratedType()) { ProjectIntegrated(vc, Trans, dofs); }
else { Project_ND(tk, dof2tk, vc, Trans, dofs); }
}
virtual void ProjectRevDiff(const Vector &P_bar,
VectorCoefficient &vc,
ElementTransformation &Trans,
DenseMatrix &dofs_bar) const
{ Project_NDRevDiff(P_bar, tk, dof2tk, vc, Trans, dofs_bar); }
virtual void ProjectFromNodes(Vector &vc, ElementTransformation &Trans,
Vector &dofs) const
{ Project_ND(tk, dof2tk, vc, Trans, dofs); }
@@ -182,6 +196,10 @@ public:
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_ND(Trans, shape); }
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{ CalcVShape_NDRevDiff(Trans, shape_bar, PointMat_bar); }
virtual void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
@@ -198,6 +216,11 @@ public:
virtual void Project(VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const
{ Project_ND(tk, dof2tk, vc, Trans, dofs); }
virtual void ProjectRevDiff(const Vector &P_bar,
VectorCoefficient &vc,
ElementTransformation &Trans,
DenseMatrix &dofs_bar) const
{ Project_NDRevDiff(P_bar, tk, dof2tk, vc, Trans, dofs_bar); }
virtual void ProjectFromNodes(Vector &vc, ElementTransformation &Trans,
Vector &dofs) const
{ Project_ND(tk, dof2tk, vc, Trans, dofs); }
@@ -241,6 +264,10 @@ public:
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_ND(Trans, shape); }
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{ CalcVShape_NDRevDiff(Trans, shape_bar, PointMat_bar); }
virtual void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
@@ -292,6 +319,10 @@ public:
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_ND(Trans, shape); }
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{ CalcVShape_NDRevDiff(Trans, shape_bar, PointMat_bar); }
// virtual void CalcCurlShape(const IntegrationPoint &ip,
// DenseMatrix &curl_shape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
+26
View File
@@ -41,6 +41,10 @@ public:
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_RT(Trans, shape); }
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{ CalcVShape_RTRevDiff(Trans, shape_bar, PointMat_bar); }
virtual void CalcDivShape(const IntegrationPoint &ip,
Vector &divshape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
@@ -60,6 +64,11 @@ public:
if (obasis1d.IsIntegratedType()) { ProjectIntegrated(vc, Trans, dofs); }
else { Project_RT(nk, dof2nk, vc, Trans, dofs); }
}
virtual void ProjectRevDiff(const Vector &P_bar,
VectorCoefficient &vc,
ElementTransformation &Trans,
DenseMatrix &dofs_bar) const
{ Project_RTRevDiff(P_bar, nk, dof2nk, vc, Trans, dofs_bar); }
virtual void ProjectFromNodes(Vector &vc, ElementTransformation &Trans,
Vector &dofs) const
{ Project_RT(nk, dof2nk, vc, Trans, dofs); }
@@ -110,6 +119,10 @@ public:
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_RT(Trans, shape); }
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{ CalcVShape_RTRevDiff(Trans, shape_bar, PointMat_bar); }
virtual void CalcDivShape(const IntegrationPoint &ip,
Vector &divshape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
@@ -172,6 +185,10 @@ public:
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_RT(Trans, shape); }
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{ CalcVShape_RTRevDiff(Trans, shape_bar, PointMat_bar); }
virtual void CalcDivShape(const IntegrationPoint &ip,
Vector &divshape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
@@ -232,6 +249,10 @@ public:
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_RT(Trans, shape); }
virtual void CalcVShapeRevDiff(ElementTransformation &Trans,
const DenseMatrix &shape_bar,
DenseMatrix &PointMat_bar) const
{ CalcVShape_RTRevDiff(Trans, shape_bar, PointMat_bar); }
virtual void CalcDivShape(const IntegrationPoint &ip,
Vector &divshape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
@@ -248,6 +269,11 @@ public:
virtual void Project(VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const
{ Project_RT(nk, dof2nk, vc, Trans, dofs); }
virtual void ProjectRevDiff(const Vector &P_bar,
VectorCoefficient &vc,
ElementTransformation &Trans,
DenseMatrix &dofs_bar) const
{ Project_RTRevDiff(P_bar, nk, dof2nk, vc, Trans, dofs_bar); }
virtual void ProjectFromNodes(Vector &vc, ElementTransformation &Trans,
Vector &dofs) const
{ Project_RT(nk, dof2nk, vc, Trans, dofs); }
+485 -1
View File
@@ -513,6 +513,119 @@ double DenseMatrix::Weight() const
return 0.0;
}
void DenseMatrix::DetRevDiff(DenseMatrix &A_bar) const
{
MFEM_ASSERT(Height() == Width() && Height() > 0,
"The matrix must be square and "
<< "sized larger than zero to compute the determinant."
<< " Height() = " << Height()
<< ", Width() = " << Width());
switch (Height())
{
case 1:
// return data[0];
A_bar(0,0) = 1.0;
return;
case 2:
// return data[0] * data[3] - data[1] * data[2];
A_bar(0,0) = data[3]; // data[0]
A_bar(1,1) = data[0]; // data[3]
A_bar(1,0) = -data[2]; // data[1]
A_bar(0,1) = -data[1]; // data[2]
return;
case 3:
{
const double *d = data;
// return
// d[0] * (d[4] * d[8] - d[5] * d[7]) +
// d[3] * (d[2] * d[7] - d[1] * d[8]) +
// d[6] * (d[1] * d[5] - d[2] * d[4]);
A_bar(0,0) = d[4]*d[8] - d[5]*d[7]; // d[0]
A_bar(1,0) = d[6]*d[5] - d[3]*d[8]; // d[1]
A_bar(2,0) = d[3]*d[7] - d[6]*d[4]; // d[2]
A_bar(0,1) = d[2]*d[7] - d[1]*d[8]; // d[3]
A_bar(1,1) = d[0]*d[8] - d[6]*d[2]; // d[4]
A_bar(2,1) = d[6]*d[1] - d[0]*d[7]; // d[5]
A_bar(0,2) = d[1]*d[5] - d[2]*d[4]; // d[6]
A_bar(1,2) = d[3]*d[2] - d[0]*d[5]; // d[7]
A_bar(2,2) = d[0]*d[4] - d[3]*d[1]; // d[8]
return;
}
default:
{
// In the general case we compute the gradient of the determinant
// using the relation from Mike Giles document:
// "An extended collection of matrix derivative results for forward
// and reverse mode algorithmic differentiation"
DenseMatrixInverse lu_factors(*this);
lu_factors.GetInverseMatrix(A_bar);
A_bar.Transpose();
A_bar *= lu_factors.Det();
return;
}
}
// not reachable
}
void DenseMatrix::WeightRevDiff(DenseMatrix &A_bar) const
{
#ifdef MFEM_DEBUG
if (Height() != A_bar.Height() || Width() != A_bar.Width())
{
mfem_error("DenseMatrix::WeightRevDiff()");
}
#endif
if (Height() == Width())
{
// return Det();
DetRevDiff(A_bar);
return;
}
else if ((Height() == 2) && (Width() == 1))
{
// return sqrt(data[0] * data[0] + data[1] * data[1]);
double wgt = sqrt(data[0] * data[0] + data[1] * data[1]);
A_bar(0,0) = data[0]/wgt;
A_bar(1,0) = data[1]/wgt;
return;
}
else if ((Height() == 3) && (Width() == 1))
{
// return sqrt(data[0] * data[0] + data[1] * data[1] + data[2] * data[2]);
double wgt = sqrt(data[0] * data[0] + data[1] * data[1] + data[2] * data[2]);
for (int i = 0; i < 3; ++i)
{
A_bar(i,0) = data[i]/wgt;
}
return;
}
else if ((Height() == 3) && (Width() == 2))
{
const double *d = data;
double E = d[0] * d[0] + d[1] * d[1] + d[2] * d[2];
double G = d[3] * d[3] + d[4] * d[4] + d[5] * d[5];
double F = d[0] * d[3] + d[1] * d[4] + d[2] * d[5];
double wgt = sqrt(E * G - F * F);
// start reverse sweep
double E_bar = 0.5*G/wgt;
double G_bar = 0.5*E/wgt;
double F_bar = -F/wgt;
A_bar(0,0) = F_bar*d[3] + 2.0*E_bar*d[0]; // d[0]
A_bar(1,0) = F_bar*d[4] + 2.0*E_bar*d[1]; // d[1]
A_bar(2,0) = F_bar*d[5] + 2.0*E_bar*d[2]; // d[2]
A_bar(0,1) = F_bar*d[0] + 2.0*G_bar*d[3]; // d[3]
A_bar(1,1) = F_bar*d[1] + 2.0*G_bar*d[4]; // d[4]
A_bar(2,1) = F_bar*d[2] + 2.0*G_bar*d[5]; // d[5]
return;
}
mfem_error("DenseMatrix::WeightRevDiff()");
}
void DenseMatrix::Set(double alpha, const double *A)
{
const int s = Width()*Height();
@@ -2175,6 +2288,143 @@ void CalcAdjugateTranspose(const DenseMatrix &a, DenseMatrix &adjat)
}
}
void CalcAdjugateRevDiff(const DenseMatrix &a, const DenseMatrix &adja_bar,
DenseMatrix &a_bar)
{
#ifdef MFEM_DEBUG
if (a.Width() > a.Height() || a.Width() < 1 || a.Height() > 3)
{
mfem_error("CalcAdjugateRevDiff(...)");
}
if (a.Width() != a_bar.Width() ||
a.Height() != a_bar.Height() ||
a_bar.Width() != adja_bar.Height() ||
a_bar.Height() != adja_bar.Width())
{
mfem_error("CalcAdjugateRefDiff(...)");
}
#endif
if (a.Width() < a.Height())
{
const double *d = a.Data();
const double *ad_bar = adja_bar.Data();
double *d_bar = a_bar.Data();
if (a.Width() == 1)
{
// N x 1, N = 2,3
// ad[0] = d[0];
d_bar[0] = ad_bar[0];
// ad[1] = d[1];
d_bar[1] = ad_bar[1];
if (a.Height() == 3)
{
// ad[2] = d[2];
d_bar[2] = ad_bar[2];
}
}
else
{
// 3 x 2
// e, g, and f are needed during the reverse sweep
double e, g, f;
e = d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
g = d[3]*d[3] + d[4]*d[4] + d[5]*d[5];
f = d[0]*d[3] + d[1]*d[4] + d[2]*d[5];
// start reverse sweep
a_bar = 0.0; // this zeros out d_bar[]
double e_bar = 0.0;
double g_bar = 0.0;
double f_bar = 0.0;
// ad[0] = d[0]*g - d[3]*f;
d_bar[0] += g*ad_bar[0];
d_bar[3] -= f*ad_bar[0];
g_bar += d[0]*ad_bar[0];
f_bar -= d[3]*ad_bar[0];
// ad[1] = d[3]*e - d[0]*f;
d_bar[3] += e*ad_bar[1];
d_bar[0] -= f*ad_bar[1];
e_bar += d[3]*ad_bar[1];
f_bar -= d[0]*ad_bar[1];
// ad[2] = d[1]*g - d[4]*f;
d_bar[1] += g*ad_bar[2];
d_bar[4] -= f*ad_bar[2];
g_bar += d[1]*ad_bar[2];
f_bar -= d[4]*ad_bar[2];
// ad[3] = d[4]*e - d[1]*f;
d_bar[4] += e*ad_bar[3];
d_bar[1] -= f*ad_bar[3];
e_bar += d[4]*ad_bar[3];
f_bar -= d[1]*ad_bar[3];
// ad[4] = d[2]*g - d[5]*f;
d_bar[2] += g*ad_bar[4];
d_bar[5] -= f*ad_bar[4];
g_bar += d[2]*ad_bar[4];
f_bar -= d[5]*ad_bar[4];
// ad[5] = d[5]*e - d[2]*f;
d_bar[5] += e*ad_bar[5];
d_bar[2] -= f*ad_bar[5];
e_bar += d[5]*ad_bar[5];
f_bar -= d[2]*ad_bar[5];
// e = d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
d_bar[0] += 2.0*d[0]*e_bar;
d_bar[1] += 2.0*d[1]*e_bar;
d_bar[2] += 2.0*d[2]*e_bar;
// g = d[3]*d[3] + d[4]*d[4] + d[5]*d[5];
d_bar[3] += 2.0*d[3]*g_bar;
d_bar[4] += 2.0*d[4]*g_bar;
d_bar[5] += 2.0*d[5]*g_bar;
// f = d[0]*d[3] + d[1]*d[4] + d[2]*d[5];
d_bar[0] += d[3]*f_bar;
d_bar[3] += d[0]*f_bar;
d_bar[1] += d[4]*f_bar;
d_bar[4] += d[1]*f_bar;
d_bar[2] += d[5]*f_bar;
d_bar[5] += d[2]*f_bar;
}
return;
}
if (a.Width() == 1)
{
// adja(0,0) = 1.0;
a_bar(0,0) = 0.0;
}
else if (a.Width() == 2)
{
// adja(0,0) = a(1,1);
a_bar(1,1) = adja_bar(0,0);
// adja(0,1) = -a(0,1);
a_bar(0,1) = -adja_bar(0,1);
// adja(1,0) = -a(1,0);
a_bar(1,0) = -adja_bar(1,0);
// adja(1,1) = a(0,0);
a_bar(0,0) = adja_bar(1,1);
}
else
{
a_bar = 0.0;
for (int di1 = 0; di1 < 3; ++di1)
{
int it11 = (di1 + 1) % 3;
int it12 = (di1 + 2) % 3;
for (int di2 = 0; di2 < 3; ++di2)
{
int it21 = (di2 + 1) % 3;
int it22 = (di2 + 2) % 3;
// adja(di2,di1) = a(it11,it21)*a(it12,it22) - a(it11,it22)*a(it12,it21);
a_bar(it11,it21) += a(it12,it22)*adja_bar(di2,di1);
a_bar(it12,it22) += a(it11,it21)*adja_bar(di2,di1);
a_bar(it11,it22) -= a(it12,it21)*adja_bar(di2,di1);
a_bar(it12,it21) -= a(it11,it22)*adja_bar(di2,di1);
}
}
}
}
void CalcInverse(const DenseMatrix &a, DenseMatrix &inva)
{
MFEM_ASSERT(a.Width() <= a.Height() && a.Width() >= 1 && a.Height() <= 3, "");
@@ -2209,7 +2459,9 @@ void CalcInverse(const DenseMatrix &a, DenseMatrix &inva)
g = d[3]*d[3] + d[4]*d[4] + d[5]*d[5];
f = d[0]*d[3] + d[1]*d[4] + d[2]*d[5];
t = 1.0 / (e*g - f*f);
e *= t; g *= t; f *= t;
e *= t;
g *= t;
f *= t;
id[0] = d[0]*g - d[3]*f;
id[1] = d[3]*e - d[0]*f;
@@ -2281,6 +2533,194 @@ void CalcInverseTranspose(const DenseMatrix &a, DenseMatrix &inva)
}
}
void CalcInverseRevDiff(const DenseMatrix &a, const DenseMatrix &inva_bar,
DenseMatrix &a_bar)
{
#ifdef MFEM_DEBUG
if (a.Width() > a.Height() || a.Width() < 1 || a.Height() > 3)
{
mfem_error("CalcInverseRevDiff(...)");
}
if (a.Width() != a_bar.Width() ||
a.Height() != a_bar.Height() ||
a_bar.Width() != inva_bar.Height() ||
a_bar.Height() != inva_bar.Width())
{
mfem_error("CalcInverseRevDiff(...)");
}
#endif
if (a.Width() < a.Height())
{
const double *d = a.Data();
const double *id_bar = inva_bar.Data();
double *d_bar = a_bar.Data();
if (a.Height() == 2)
{
double t = 1.0 / (d[0]*d[0] + d[1]*d[1]);
/// id[0] = d[0] * t;
d_bar[0] += id_bar[0] * t;
double t_bar = id_bar[0] * d[0];
/// id[1] = d[1] * t;
d_bar[1] += id_bar[1] * t;
t_bar += id_bar[1] * d[1];
/// t = 1.0 / (d[0]*d[0] + d[1]*d[1]);
d_bar[0] -= t_bar * 2 * d[0] / pow(d[0]*d[0] + d[1]*d[1], 2);
d_bar[1] -= t_bar * 2 * d[1] / pow(d[0]*d[0] + d[1]*d[1], 2);
}
else
{
if (a.Width() == 1)
{
double t = 1.0 / (d[0]*d[0] + d[1]*d[1] + d[2]*d[2]);
/// id[0] = d[0] * t;
d_bar[0] += id_bar[0] * t;
double t_bar = id_bar[0] * d[0];
/// id[1] = d[1] * t;
d_bar[1] += id_bar[1] * t;
t_bar += id_bar[1] * d[1];
/// id[2] = d[2] * t;
d_bar[2] += id_bar[2] * t;
t_bar += id_bar[2] * d[2];
/// t = 1.0 / (d[0]*d[0] + d[1]*d[1] + d[2]*d[2]);
d_bar[0] -= t_bar * 2 * d[0] / pow(d[0]*d[0] + d[1]*d[1] + d[2]*d[2], 2);
d_bar[1] -= t_bar * 2 * d[1] / pow(d[0]*d[0] + d[1]*d[1] + d[2]*d[2], 2);
d_bar[2] -= t_bar * 2 * d[2] / pow(d[0]*d[0] + d[1]*d[1] + d[2]*d[2], 2);
}
else
{
double e = d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
double g = d[3]*d[3] + d[4]*d[4] + d[5]*d[5];
double f = d[0]*d[3] + d[1]*d[4] + d[2]*d[5];
double t = 1.0 / (e*g - f*f);
double ee = e * t;
double gg = g * t;
double ff = f * t;
/// id[0] = d[0]*g - d[3]*f;
d_bar[0] += id_bar[0]*gg;
double gg_bar = id_bar[0] * d[0];
d_bar[3] += -id_bar[0] * ff;
double ff_bar = -id_bar[0] * d[3];
/// id[1] = d[3]*e - d[0]*f;
d_bar[3] += id_bar[1] * ee;
double ee_bar = id_bar[1] * d[3];
d_bar[0] += -id_bar[1] * ff;
ff_bar -= id_bar[1] * d[0];
/// id[2] = d[1]*g - d[4]*f;
d_bar[1] += id_bar[2] * gg;
gg_bar += id_bar[2] * d[1];
d_bar[4] += -id_bar[2] * ff;
ff_bar -= id_bar[2] * d[4];
/// id[3] = d[4]*e - d[1]*f;
d_bar[4] += id_bar[3] * ee;
ee_bar += id_bar[3] * d[4];
d_bar[1] += -id_bar[3] * ff;
ff_bar -= id_bar[3] * d[1];
/// id[4] = d[2]*g - d[5]*f;
d_bar[2] += id_bar[4] * gg;
gg_bar += id_bar[4] * d[2];
d_bar[5] += -id_bar[4] * ff;
ff_bar -= id_bar[4] * d[5];
/// id[5] = d[5]*e - d[2]*f;
d_bar[5] += id_bar[5] * ee;
ee_bar += id_bar[5] * d[5];
d_bar[2] += -id_bar[5] * ff;
ff_bar -= id_bar[5] * d[2];
/// double ff = f * t;
double t_bar = ff_bar * f;
double f_bar = ff_bar * t;
/// double gg = g * t;
t_bar += gg_bar * g;
double g_bar = gg_bar * t;
/// double ee = e * t;
t_bar += ee_bar * e;
double e_bar = ee_bar * t;
// /// f *= t;
// double t_bar = f_bar * f / t;
// f_bar *= t;
// /// g *= t;
// t_bar += g_bar * g / t;
// g_bar *= t;
// /// e *= t;
// t_bar += e_bar * e / t;
// e_bar *= t;
/// double t = 1.0 / (e*g - f*f);
e_bar -= t_bar * g / pow(e*g - f*f, 2);
g_bar -= t_bar * e / pow(e*g - f*f, 2);
f_bar += t_bar * 2*f / pow(e*g - f*f, 2);
/// double f = d[0]*d[3] + d[1]*d[4] + d[2]*d[5];
d_bar[0] += f_bar * d[3];
d_bar[3] += f_bar * d[0];
d_bar[1] += f_bar * d[4];
d_bar[4] += f_bar * d[1];
d_bar[2] += f_bar * d[5];
d_bar[5] += f_bar * d[2];
/// double g = d[3]*d[3] + d[4]*d[4] + d[5]*d[5];
d_bar[3] += g_bar * 2 * d[3];
d_bar[4] += g_bar * 2 * d[4];
d_bar[5] += g_bar * 2 * d[5];
/// double e = d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
d_bar[0] += e_bar * 2 * d[0];
d_bar[1] += e_bar * 2 * d[1];
d_bar[2] += e_bar * 2 * d[2];
}
}
return;
}
#ifdef MFEM_DEBUG
double t = a.Det();
MFEM_ASSERT(std::abs(t) > 1.0e-14 * pow(a.FNorm()/a.Width(), a.Width()),
"singular matrix!");
#endif
double inva_buffer[9] = {};
DenseMatrix inva(inva_buffer, a.Height(), a.Width());
switch (a.Height())
{
case 1:
inva(0,0) = 1.0 / a.Det();
break;
case 2:
kernels::CalcInverse<2>(a.Data(), inva.Data());
break;
case 3:
kernels::CalcInverse<3>(a.Data(), inva.Data());
break;
}
double tmp_buffer[9] = {};
DenseMatrix tmp(tmp_buffer, a.Height(), a.Width());
MultAtB(inva, inva_bar, tmp);
AddMult_a_ABt(-1, tmp, inva, a_bar);
}
void CalcOrtho(const DenseMatrix &J, Vector &n)
{
MFEM_ASSERT( ((J.Height() == 2 && J.Width() == 1)
@@ -2307,6 +2747,50 @@ void CalcOrtho(const DenseMatrix &J, Vector &n)
}
}
void CalcOrthoRevDiff(const DenseMatrix &J, const Vector &n_bar,
DenseMatrix &J_bar)
{
MFEM_ASSERT(((J.Height() == 2 && J.Width() == 1) ||
(J.Height() == 3 && J.Width() == 2)) &&
(J.Height() == n_bar.Size()),
"Matrix must be 3x2 or 2x1, "
<< "and the Vector must be sized with the rows. "
<< " J.Height() = " << J.Height()
<< ", J.Width() = " << J.Width()
<< ", n_bar.Size() = " << n_bar.Size());
MFEM_ASSERT((J.Height() == J_bar.Height() && J.Width() == J_bar.Width()),
"Input matrix and derivative matrix must be the same size.");
const double *d = J.Data();
double *d_bar = J_bar.Data();
if (J.Height() == 2)
{
// n(0) = d[1];
d_bar[1] = n_bar(0);
// n(1) = -d[0];
d_bar[0] = -n_bar(1);
}
else
{
J_bar = 0.0;
// n(0) = d[1]*d[5] - d[2]*d[4];
d_bar[1] += d[5]*n_bar(0);
d_bar[5] += d[1]*n_bar(0);
d_bar[2] -= d[4]*n_bar(0);
d_bar[4] -= d[2]*n_bar(0);
// n(1) = d[2]*d[3] - d[0]*d[5];
d_bar[2] += d[3]*n_bar(1);
d_bar[3] += d[2]*n_bar(1);
d_bar[0] -= d[5]*n_bar(1);
d_bar[5] -= d[0]*n_bar(1);
// n(2) = d[0]*d[4] - d[1]*d[3];
d_bar[0] += d[4]*n_bar(2);
d_bar[4] += d[0]*n_bar(2);
d_bar[1] -= d[3]*n_bar(2);
d_bar[3] -= d[1]*n_bar(2);
}
}
void MultAAt(const DenseMatrix &a, DenseMatrix &aat)
{
const int height = a.Height();
+18
View File
@@ -197,6 +197,12 @@ public:
double Weight() const;
/// Evaluate the derivative of Det() w.r.t. the matrix entries
void DetRevDiff(DenseMatrix &A_bar) const;
/// Evaluate the derivative of Weight() w.r.t. the matrix entries
void WeightRevDiff(DenseMatrix &A_bar) const;
/** @brief Set the matrix to alpha * A, assuming that A has the same
dimensions as the matrix and uses column-major layout. */
void Set(double alpha, const double *A);
@@ -457,6 +463,10 @@ void CalcAdjugate(const DenseMatrix &a, DenseMatrix &adja);
/// Calculate the transposed adjugate of a matrix (for NxN matrices, N=1,2,3)
void CalcAdjugateTranspose(const DenseMatrix &a, DenseMatrix &adjat);
/// Reverse-mode sensitivities of adj(A) with respect to the entries in A
void CalcAdjugateRevDiff(const DenseMatrix &a, const DenseMatrix &adja_bar,
DenseMatrix &a_bar);
/** Calculate the inverse of a matrix (for NxN matrices, N=1,2,3) or the
left inverse (A^t.A)^{-1}.A^t (for 2x1, 3x1, or 3x2 matrices) */
void CalcInverse(const DenseMatrix &a, DenseMatrix &inva);
@@ -464,11 +474,19 @@ void CalcInverse(const DenseMatrix &a, DenseMatrix &inva);
/// Calculate the inverse transpose of a matrix (for NxN matrices, N=1,2,3)
void CalcInverseTranspose(const DenseMatrix &a, DenseMatrix &inva);
/// Reverse-mode sensitivities of inv(A) with respect to the entries in A
void CalcInverseRevDiff(const DenseMatrix &a, const DenseMatrix &inva_bar,
DenseMatrix &a_bar);
/** For a given Nx(N-1) (N=2,3) matrix J, compute a vector n such that
n_k = (-1)^{k+1} det(J_k), k=1,..,N, where J_k is the matrix J with the
k-th row removed. Note: J^t.n = 0, det([n|J])=|n|^2=det(J^t.J). */
void CalcOrtho(const DenseMatrix &J, Vector &n);
/// The reverse-mode differentiation of CalcOrtho()
void CalcOrthoRevDiff(const DenseMatrix &J, const Vector &n_bar,
DenseMatrix &J_bar);
/// Calculate the matrix A.At
void MultAAt(const DenseMatrix &a, DenseMatrix &aat);
+3
View File
@@ -56,17 +56,20 @@ set(UNIT_TESTS_SRCS
fem/test_blocknonlinearform.cpp
fem/test_calcshape.cpp
fem/test_coefficient.cpp
fem/test_coeff_revdiff.cpp
fem/test_datacollection.cpp
fem/test_derefine.cpp
fem/test_estimator.cpp
fem/test_face_elem_trans.cpp
fem/test_face_permutation.cpp
fem/test_fe.cpp
fem/test_fe_revdiff.cpp
fem/test_get_value.cpp
fem/test_getderivative.cpp
fem/test_intrules.cpp
fem/test_intruletypes.cpp
fem/test_inversetransform.cpp
fem/test_eltrans_revdiff.cpp
fem/test_lexicographic_ordering.cpp
fem/test_lin_interp.cpp
fem/test_linear_fes.cpp
+325
View File
@@ -0,0 +1,325 @@
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "mfem.hpp"
#include "catch.hpp"
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <random>
using namespace mfem;
namespace
{
// String used to define a single element mesh, with a c-shaped quad element
std::string mesh_str =
"MFEM mesh v1.0" "\n\n"
"dimension" "\n"
"2" "\n\n"
"elements" "\n"
"1" "\n"
"1 3 0 1 2 3" "\n\n"
"boundary" "\n"
"0" "\n\n"
"vertices" "\n"
"4" "\n\n"
"nodes" "\n"
"FiniteElementSpace" "\n"
"FiniteElementCollection: Quadratic" "\n"
"VDim: 2" "\n"
"Ordering: 1" "\n"
"0 0" "\n"
"0 2" "\n"
"0 6" "\n"
"0 8" "\n"
"0 1" "\n"
"-6 4" "\n"
"0 7" "\n"
"-8 4" "\n"
"-7 4" "\n";
double scalar_func(const Vector &x)
{
double q = 0;
for (int i = 0; i < x.Size(); ++i)
{
q += pow(x(i), 2);
}
return q;
}
void scalar_funcRevDiff(const mfem::Vector &x, const double q_bar,
mfem::Vector &x_bar)
{
for (int i = 0; i < x.Size(); ++i)
{
x_bar(i) += q_bar * 2 * x(i);
}
}
double scalar_func2(const Vector &x)
{
double q = 0;
for (int i = 0; i < x.Size(); ++i)
{
q += x(i);
}
return q;
}
void scalar_func2RevDiff(const mfem::Vector &x, const double q_bar,
mfem::Vector &x_bar)
{
for (int i = 0; i < x.Size(); ++i)
{
x_bar(i) += q_bar;
}
}
void func2D(const Vector &x, Vector &y)
{
y.SetSize(2);
y(0) = x(0)*x(0) - x(1);
y(1) = x(0) * exp(x(1));
}
void func2DRevDiff(const Vector &x, const Vector &v_bar, Vector &x_bar)
{
x_bar(0) = v_bar(0) * 2*x(0) + v_bar(1) * exp(x(1));
x_bar(1) = -v_bar(0) + v_bar(1) * x(0) * exp(x(1));
}
void func3D(const Vector &x, Vector &y)
{
y.SetSize(3);
y(0) = x(0)*x(0) - x(1);
y(1) = x(0) * exp(x(1));
y(2) = x(2)*x(0) - x(1);
}
void func3DRevDiff(const Vector &x, const Vector &v_bar, Vector &x_bar)
{
x_bar(0) = v_bar(0) * 2*x(0) + v_bar(1) * exp(x(1)) + v_bar(2)*x(2);
x_bar(1) = -v_bar(0) + v_bar(1) * x(0) * exp(x(1)) - v_bar(2);
x_bar(2) = v_bar(2) * x(0);
}
void runScalarTest(Mesh &mesh, Coefficient &q)
{
constexpr double eps_fd = 1e-5;
std::default_random_engine generator;
std::uniform_real_distribution<double> distribution(-1.0,1.0);
for (int p = 1; p <= 4; ++p)
{
const int dim = mesh.Dimension();
H1_FECollection fec(p, dim);
FiniteElementSpace fes(&mesh, &fec);
const FiniteElement &el = *fes.GetFE(0);
IsoparametricTransformation trans;
mesh.GetElementTransformation(0, &trans);
DenseMatrix &coords = trans.GetPointMat();
DenseMatrix coords_bar(coords.Height(), coords.Width());
double Q_bar = distribution(generator);
int order = trans.OrderW() + 2 * el.GetOrder();
const IntegrationRule *ir = &IntRules.Get(el.GetGeomType(), order);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint (&ip);
// reverse-mode differentiation of Eval
coords_bar = 0.0;
q.EvalRevDiff(Q_bar, trans, ip, coords_bar);
// get the weighted derivatives using finite difference method
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
double Q_fd = q.Eval(trans, ip);
coords(di, n) -= 2.0*eps_fd;
Q_fd -= q.Eval(trans, ip);
Q_fd /= (2.0 * eps_fd);
coords(di, n) += eps_fd;
double x_bar_fd = Q_bar * Q_fd;
REQUIRE(coords_bar(di, n) == Approx(x_bar_fd));
}
}
}
}
}
void runVectorTest(Mesh &mesh, VectorCoefficient &vc)
{
constexpr double eps_fd = 1e-5;
std::default_random_engine generator;
std::uniform_real_distribution<double> distribution(-1.0,1.0);
for (int p = 1; p <= 4; ++p)
{
const int dim = mesh.Dimension();
ND_FECollection fec(p, dim);
FiniteElementSpace fes(&mesh, &fec);
const FiniteElement &el = *fes.GetFE(0);
IsoparametricTransformation trans;
mesh.GetElementTransformation(0, &trans);
DenseMatrix &coords = trans.GetPointMat();
DenseMatrix coords_bar(coords.Height(), coords.Width());
Vector V_bar(dim), V_fd(dim), V_pert(dim);
for (int i = 0; i < V_bar.Size(); ++i)
{
V_bar(i) = distribution(generator);
}
int order = trans.OrderW() + 2 * el.GetOrder();
const IntegrationRule *ir = &IntRules.Get(el.GetGeomType(), order);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint (&ip);
// reverse-mode differentiation of Eval
coords_bar = 0.0;
vc.EvalRevDiff(V_bar, trans, ip, coords_bar);
// get the weighted derivatives using finite difference method
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
vc.Eval(V_fd, trans, ip);
coords(di, n) -= 2.0*eps_fd;
vc.Eval(V_pert, trans, ip);
V_fd -= V_pert;
V_fd /= (2.0 * eps_fd);
coords(di, n) += eps_fd;
double x_bar_fd = V_bar * V_fd;
REQUIRE(coords_bar(di, n) == Approx(x_bar_fd));
}
}
}
}
}
} // anonymous namespace
namespace coeff_revdiff
{
TEST_CASE("CoeffRevDiff::FunctionCoefficient::EvalRevDiff_2D")
{
// Create quadratic mesh with single C-shaped quadrilateral
std::stringstream meshStr;
meshStr << mesh_str;
Mesh mesh2D(meshStr);
REQUIRE( mesh2D.GetNE() == 1 );
REQUIRE( mesh2D.GetNodes() != NULL );
FunctionCoefficient c1(scalar_func, scalar_funcRevDiff);
runScalarTest(mesh2D, c1);
FunctionCoefficient c2(scalar_func2, scalar_func2RevDiff);
runScalarTest(mesh2D, c2);
}
TEST_CASE("CoeffRevDiff::ProductCoefficient::EvalRevDiff_2D 1 Coeff")
{
// Create quadratic mesh with single C-shaped quadrilateral
std::stringstream meshStr;
meshStr << mesh_str;
Mesh mesh2D(meshStr);
REQUIRE( mesh2D.GetNE() == 1 );
REQUIRE( mesh2D.GetNodes() != NULL );
FunctionCoefficient c1(scalar_func, scalar_funcRevDiff);
ProductCoefficient prod(2.0, c1);
runScalarTest(mesh2D, prod);
}
TEST_CASE("CoeffRevDiff::ProductCoefficient::EvalRevDiff_2D 2 Coeffs")
{
// Create quadratic mesh with single C-shaped quadrilateral
std::stringstream meshStr;
meshStr << mesh_str;
Mesh mesh2D(meshStr);
REQUIRE( mesh2D.GetNE() == 1 );
REQUIRE( mesh2D.GetNodes() != NULL );
FunctionCoefficient c1(scalar_func, scalar_funcRevDiff);
FunctionCoefficient c2(scalar_func2, scalar_func2RevDiff);
ProductCoefficient prod(c1, c2);
runScalarTest(mesh2D, prod);
}
TEST_CASE("CoeffRevDiff::VectorFunctionCoefficient::EvalRevDiff_2D")
{
// Create quadratic mesh with single C-shaped quadrilateral
std::stringstream meshStr;
meshStr << mesh_str;
Mesh mesh2D(meshStr);
REQUIRE( mesh2D.GetNE() == 1 );
REQUIRE( mesh2D.GetNodes() != NULL );
VectorFunctionCoefficient vc2D(2, func2D, func2DRevDiff);
runVectorTest(mesh2D, vc2D);
}
TEST_CASE("CoeffRevDiff::VectorFunctionCoefficient::EvalRevDiff_3D")
{
auto mesh3D = Mesh::MakeCartesian3D(2, 2, 2,
Element::TETRAHEDRON,
2.0, 1.0, 3.0, true);
mesh3D.EnsureNodes();
VectorFunctionCoefficient vc3D(3, func3D, func3DRevDiff);
runVectorTest(mesh3D, vc3D);
}
TEST_CASE("CoeffRevDiff::ScalarVectorProductCoefficient::EvalRevDiff_3D")
{
auto mesh3D = Mesh::MakeCartesian3D(2, 2, 2,
Element::TETRAHEDRON,
2.0, 1.0, 3.0, true);
mesh3D.EnsureNodes();
VectorFunctionCoefficient vfc(3, func3D, func3DRevDiff);
ScalarVectorProductCoefficient vc(2.0, vfc);
runVectorTest(mesh3D, vc);
}
} // namespace coeff_revdiff
+268
View File
@@ -0,0 +1,268 @@
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "mfem.hpp"
#include "catch.hpp"
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace mfem;
// String used to define a single element mesh, with a c-shaped quad element
std::string mesh_str =
"MFEM mesh v1.0" "\n\n"
"dimension" "\n"
"2" "\n\n"
"elements" "\n"
"1" "\n"
"1 3 0 1 2 3" "\n\n"
"boundary" "\n"
"0" "\n\n"
"vertices" "\n"
"4" "\n\n"
"nodes" "\n"
"FiniteElementSpace" "\n"
"FiniteElementCollection: Quadratic" "\n"
"VDim: 2" "\n"
"Ordering: 1" "\n"
"0 0" "\n"
"0 2" "\n"
"0 6" "\n"
"0 8" "\n"
"0 1" "\n"
"-6 4" "\n"
"0 7" "\n"
"-8 4" "\n"
"-7 4" "\n";
TEST_CASE("IsoparametricTransformation reverse-mode differentiation",
"[IsoparametricTransformation]")
{
constexpr double eps_fd = 1e-5;
// Create quadratic mesh with single C-shaped quadrilateral
std::stringstream meshStr;
meshStr << mesh_str;
Mesh mesh(meshStr);
REQUIRE( mesh.GetNE() == 1 );
REQUIRE( mesh.GetNodes() != NULL );
bool dumpMesh = false;
if (dumpMesh)
{
std::ofstream mesh_ostream("isoparametric-revdiff-mesh.vtk");
mesh_ostream.precision(14);
int refine = 10;
mesh.PrintVTK(mesh_ostream, refine);
}
// Create the transformation and get integration rule
IsoparametricTransformation trans;
mesh.GetElementTransformation(0, &trans);
const int intorder = 5;
const IntegrationRule *ir = &IntRules.Get(mesh.GetElementBaseGeometry(0),
intorder);
DenseMatrix &coords = trans.GetPointMat();
DenseMatrix coords_bar(coords.Height(), coords.Width());
SECTION("TransformRevDiff")
{
// x_bar(i) is the weight on the (i)th entry of the coordinate x;
// the values are not important for this test.
double x_bar_data[4] = {2.5, -3.2};
Vector x_bar(x_bar_data, 2);
Vector x_fd(2), x_pert(2);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
// reverse-mode differentiation of coordinate transformation
coords_bar = 0.0;
trans.TransformRevDiff(ip, x_bar, coords_bar);
// get the weighted derivatives using finite difference method
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
trans.Transform(ip, x_fd);
coords(di, n) -= 2.0*eps_fd;
trans.Transform(ip, x_pert);
x_fd -= x_pert;
x_fd *= 1.0/(2.0*eps_fd);
coords(di, n) += eps_fd;
double x_bar_fd = 0.0;
for (int j = 0; j < x_bar.Size(); ++j)
{
x_bar_fd += x_bar(j)*x_fd(j);
}
REQUIRE(coords_bar(di, n) == Approx(x_bar_fd));
}
}
}
}
SECTION("JacobianRevDiff")
{
// dFdx_bar(i,j) is the weight on the (i,j)th entry of the Jacobian;
// the values are not important for this test.
double dFdx_bar_data[4] = {2.0, -3.0, 4.0, -1.0};
DenseMatrix dFdx_bar(dFdx_bar_data, 2, 2);
DenseMatrix dFdx_fd(2,2);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
// reverse-mode differentiation of Jacobian of mapping
coords_bar = 0.0;
trans.JacobianRevDiff(dFdx_bar, coords_bar);
// get the weighted derivatives using finite difference method
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
trans.SetIntPoint(&ip); // force re-evaluation of Jacobian
dFdx_fd = trans.Jacobian();
coords(di, n) -= 2.0*eps_fd;
trans.SetIntPoint(&ip); // force re-evaluation of Jacobian
dFdx_fd -= trans.Jacobian();
dFdx_fd *= 1.0/(2.0*eps_fd);
coords(di, n) += eps_fd;
double dFdx_bar_fd = 0.0;
for (int j = 0; j < dFdx_bar.Height(); ++j)
{
for (int k = 0; k < dFdx_bar.Width(); ++k)
{
dFdx_bar_fd += dFdx_bar(j,k)*dFdx_fd(j,k);
}
}
REQUIRE(coords_bar(di, n) == Approx(dFdx_bar_fd));
}
}
}
}
SECTION("AdjugateJacobianRevDiff")
{
// adjJ_bar(i,j) is the weight on the (i,j)th entry of the Adjugate;
// the values are not important for this test.
double adjJ_bar_data[4] = {2.0, -3.0, 4.0, -1.0};
DenseMatrix adjJ_bar(adjJ_bar_data, 2, 2);
DenseMatrix adjJ_fd(2,2);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
// reverse-mode differentiation of Adjugate of mapping
coords_bar = 0.0;
trans.AdjugateJacobianRevDiff(adjJ_bar, coords_bar);
// get the weighted derivatives using finite difference method
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
trans.SetIntPoint(&ip); // force re-evaluation of Adjugate
adjJ_fd = trans.AdjugateJacobian();
coords(di, n) -= 2.0*eps_fd;
trans.SetIntPoint(&ip); // force re-evaluation of Adjugate
adjJ_fd -= trans.AdjugateJacobian();
adjJ_fd *= 1.0/(2.0*eps_fd);
coords(di, n) += eps_fd;
double adjJ_bar_fd = 0.0;
for (int j = 0; j < adjJ_bar.Height(); ++j)
{
for (int k = 0; k < adjJ_bar.Width(); ++k)
{
adjJ_bar_fd += adjJ_bar(j,k)*adjJ_fd(j,k);
}
}
REQUIRE(coords_bar(di, n) == Approx(adjJ_bar_fd));
}
}
}
}
SECTION("InverseJacobianRevDiff")
{
// invJ_bar(i,j) is the weight on the (i,j)th entry of the Inverse;
// the values are not important for this test.
double invJ_bar_data[4] = {2.0, -3.0, 4.0, -1.0};
DenseMatrix invJ_bar(invJ_bar_data, 2, 2);
DenseMatrix invJ_fd(2,2);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
// reverse-mode differentiation of Inverse of mapping
coords_bar = 0.0;
trans.InverseJacobianRevDiff(invJ_bar, coords_bar);
// get the weighted derivatives using finite difference method
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
trans.SetIntPoint(&ip); // force re-evaluation of Inverse
invJ_fd = trans.InverseJacobian();
coords(di, n) -= 2.0*eps_fd;
trans.SetIntPoint(&ip); // force re-evaluation of Inverse
invJ_fd -= trans.InverseJacobian();
invJ_fd *= 1.0/(2.0*eps_fd);
coords(di, n) += eps_fd;
double invJ_bar_fd = 0.0;
for (int j = 0; j < invJ_bar.Height(); ++j)
{
for (int k = 0; k < invJ_bar.Width(); ++k)
{
invJ_bar_fd += invJ_bar(j,k)*invJ_fd(j,k);
}
}
REQUIRE(coords_bar(di, n) == Approx(invJ_bar_fd));
}
}
}
}
SECTION("WeightRevDiff")
{
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
// get the gradient of the Weight() using reverse mode
coords_bar = 0.0;
trans.WeightRevDiff(coords_bar);
// get the gradient of the Weight() using finite difference method
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
trans.SetIntPoint(&ip); // force re-evaluation of Weight
double dWeight_fd = trans.Weight();
coords(di, n) -= 2.0*eps_fd;
trans.SetIntPoint(&ip); // force re-evaluation of Weight
dWeight_fd -= trans.Weight();
dWeight_fd /= (2.0*eps_fd);
coords(di, n) += eps_fd;
REQUIRE(coords_bar(di, n) == Approx(dWeight_fd).margin(1e-10));
}
}
}
}
}
+419
View File
@@ -0,0 +1,419 @@
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "mfem.hpp"
#include "catch.hpp"
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <random>
using namespace mfem;
namespace
{
// String used to define a single element mesh, with a c-shaped quad element
std::string mesh_str =
"MFEM mesh v1.0" "\n\n"
"dimension" "\n"
"2" "\n\n"
"elements" "\n"
"1" "\n"
"1 3 0 1 2 3" "\n\n"
"boundary" "\n"
"0" "\n\n"
"vertices" "\n"
"4" "\n\n"
"nodes" "\n"
"FiniteElementSpace" "\n"
"FiniteElementCollection: Quadratic" "\n"
"VDim: 2" "\n"
"Ordering: 1" "\n"
"0 0" "\n"
"0 2" "\n"
"0 6" "\n"
"0 8" "\n"
"0 1" "\n"
"-6 4" "\n"
"0 7" "\n"
"-8 4" "\n"
"-7 4" "\n";
void func2D(const Vector &x, Vector &y)
{
y.SetSize(2);
y(0) = x(0)*x(0) - x(1);
y(1) = x(0) * exp(x(1));
}
void func2DRevDiff(const Vector &x, const Vector &v_bar, Vector &x_bar)
{
x_bar(0) = v_bar(0) * 2*x(0) + v_bar(1) * exp(x(1));
x_bar(1) = -v_bar(0) + v_bar(1) * x(0) * exp(x(1));
}
void func3D(const Vector &x, Vector &y)
{
y.SetSize(3);
y(0) = x(0)*x(0) - x(1);
y(1) = x(0) * exp(x(1));
y(2) = x(2)*x(0) - x(1);
}
void func3DRevDiff(const Vector &x, const Vector &v_bar, Vector &x_bar)
{
x_bar(0) = v_bar(0) * 2*x(0) + v_bar(1) * exp(x(1)) + v_bar(2)*x(2);
x_bar(1) = -v_bar(0) + v_bar(1) * x(0) * exp(x(1)) - v_bar(2);
x_bar(2) = v_bar(2) * x(0);
}
} // anonymous namespace
namespace fe_revdiff
{
template<typename T>
void runProjectRevDiffTest(Mesh &mesh, VectorCoefficient &vc);
template<typename T>
void runCalcPhysShapeRevDiffTest(Mesh &mesh);
template<typename T>
void runCalcVShapeRevDiffTest(Mesh &mesh);
void runCalcPhysCurlShapeRevDiffTest(Mesh &mesh);
constexpr double eps_fd = 1e-5;
std::default_random_engine generator;
std::uniform_real_distribution<double> distribution(-1.0,1.0);
TEST_CASE("VectorFiniteElement::ProjectRevDiff - 2D")
{
// Create quadratic mesh with single C-shaped quadrilateral
std::stringstream meshStr;
meshStr << mesh_str;
Mesh mesh2D(meshStr);
REQUIRE(mesh2D.GetNE() == 1);
REQUIRE(mesh2D.GetNodes() != nullptr);
VectorFunctionCoefficient vc2D(2, func2D, func2DRevDiff);
runProjectRevDiffTest<RT_FECollection>(mesh2D, vc2D);
runProjectRevDiffTest<ND_FECollection>(mesh2D, vc2D);
}
TEST_CASE("VectorFiniteElement::ProjectRevDiff - 3D")
{
auto mesh3D = Mesh::MakeCartesian3D(2, 2, 2, Element::TETRAHEDRON,
2.0, 1.0, 3.0, true);
// mesh3D.ReorientTetMesh();
mesh3D.EnsureNodes();
VectorFunctionCoefficient vc3D(3, func3D, func3DRevDiff);
runProjectRevDiffTest<RT_FECollection>(mesh3D, vc3D);
runProjectRevDiffTest<ND_FECollection>(mesh3D, vc3D);
}
TEST_CASE("FiniteElement::CalcPhysShapeRevDiff")
{
// Create quadratic mesh with single C-shaped quadrilateral
std::stringstream meshStr;
meshStr << mesh_str;
Mesh mesh2D(meshStr);
REQUIRE(mesh2D.GetNE() == 1);
REQUIRE(mesh2D.GetNodes() != nullptr);
runCalcPhysShapeRevDiffTest<H1_FECollection>(mesh2D);
runCalcPhysShapeRevDiffTest<L2_FECollection>(mesh2D);
}
TEST_CASE("VectorFiniteElement::CalcVShape_RTRevDiff - 2D")
{
// Create quadratic mesh with single C-shaped quadrilateral
std::stringstream meshStr;
meshStr << mesh_str;
Mesh mesh2D(meshStr);
REQUIRE(mesh2D.GetNE() == 1);
REQUIRE(mesh2D.GetNodes() != nullptr);
runCalcVShapeRevDiffTest<RT_FECollection>(mesh2D);
}
TEST_CASE("VectorFiniteElement::CalcVShape_NDRevDiff - 2D")
{
// Create quadratic mesh with single C-shaped quadrilateral
std::stringstream meshStr;
meshStr << mesh_str;
Mesh mesh2D(meshStr);
REQUIRE(mesh2D.GetNE() == 1);
REQUIRE(mesh2D.GetNodes() != nullptr);
runCalcVShapeRevDiffTest<ND_FECollection>(mesh2D);
}
TEST_CASE("VectorFiniteElement::CalcVShape_RTRevDiff - 3D")
{
auto mesh3D = Mesh::MakeCartesian3D(2, 2, 2, Element::TETRAHEDRON,
2.0, 1.0, 3.0, true);
// mesh3D.ReorientTetMesh();
mesh3D.EnsureNodes();
runCalcVShapeRevDiffTest<RT_FECollection>(mesh3D);
}
TEST_CASE("VectorFiniteElement::CalcVShape_NDRevDiff - 3D")
{
auto mesh3D = Mesh::MakeCartesian3D(2, 2, 2, Element::TETRAHEDRON,
2.0, 1.0, 3.0, true);
// mesh3D.ReorientTetMesh();
mesh3D.EnsureNodes();
runCalcVShapeRevDiffTest<ND_FECollection>(mesh3D);
}
TEST_CASE("FiniteElement::CalcPhysCurlShapeRevDiff - 3D")
{
auto mesh3D = Mesh::MakeCartesian3D(2, 2, 2, Element::TETRAHEDRON,
2.0, 1.0, 3.0, true);
// mesh3D.ReorientTetMesh();
mesh3D.EnsureNodes();
runCalcPhysCurlShapeRevDiffTest(mesh3D);
}
template<typename T>
void runProjectRevDiffTest(Mesh &mesh, VectorCoefficient &vc)
{
for (int p = 1; p <= 4; ++p)
{
const int dim = mesh.Dimension();
T fec(p, dim);
FiniteElementSpace fes(&mesh, &fec);
const FiniteElement &el = *fes.GetFE(0);
IsoparametricTransformation trans;
mesh.GetElementTransformation(0, &trans);
// P_bar is the vector contracted with the derivative of the projection
// the values are not important for this test
const int dof = el.GetDof();
Vector P_bar(dof);
for (int i = 0; i < P_bar.Size(); ++i)
{
P_bar(i) = distribution(generator);
}
// reverse-mode differentiation of projection
DenseMatrix &coords = trans.GetPointMat();
DenseMatrix coords_bar(coords.Height(), coords.Width());
coords_bar = 0.0;
el.ProjectRevDiff(P_bar, vc, trans, coords_bar);
// get the weighted derivatives using finite difference method
Vector dofs_fd(dof), dofs_pert(dof);
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
trans.Reset();
el.Project(vc, trans, dofs_fd);
coords(di, n) -= 2.0 * eps_fd;
trans.Reset();
el.Project(vc, trans, dofs_pert);
dofs_fd -= dofs_pert;
dofs_fd *= 1.0 / (2.0 * eps_fd);
coords(di, n) += eps_fd;
double x_bar_fd = P_bar * dofs_fd;
REQUIRE(coords_bar(di, n) == Approx(x_bar_fd));
}
}
}
}
template<typename T>
void runCalcPhysShapeRevDiffTest(Mesh &mesh)
{
for (int p = 1; p <= 4; ++p)
{
const int dim = mesh.Dimension();
T fec(p, dim);
FiniteElementSpace fes(&mesh, &fec);
const FiniteElement &el = *fes.GetFE(0);
IsoparametricTransformation trans;
mesh.GetElementTransformation(0, &trans);
int order = trans.OrderW() + 2 * el.GetOrder();
const IntegrationRule *ir = &IntRules.Get(el.GetGeomType(), order);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
const int dof = el.GetDof();
Vector shape_bar(dof);
for (int k = 0; k < shape_bar.Size(); ++k)
{
shape_bar(k) = distribution(generator);
}
// reverse-mode differentiation CalcVShape
DenseMatrix &coords = trans.GetPointMat();
DenseMatrix coords_bar(coords.Height(), coords.Width());
coords_bar = 0.0;
el.CalcPhysShapeRevDiff(trans, shape_bar, coords_bar);
// get the weighted derivatives using finite difference method
Vector shape_fd(dof), shape_pert(dof);
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
trans.Reset();
el.CalcPhysShape(trans, shape_fd);
coords(di, n) -= 2.0 * eps_fd;
trans.Reset();
el.CalcPhysShape(trans, shape_pert);
shape_fd -= shape_pert;
shape_fd *= 1.0 / (2.0 * eps_fd);
coords(di, n) += eps_fd;
double x_bar_fd = shape_bar * shape_fd;
REQUIRE(coords_bar(di, n) == Approx(x_bar_fd));
}
}
}
}
}
template<typename T>
void runCalcVShapeRevDiffTest(Mesh &mesh)
{
for (int p = 1; p <= 4; ++p)
{
const int dim = mesh.Dimension();
T fec(p, dim);
FiniteElementSpace fes(&mesh, &fec);
const FiniteElement &el = *fes.GetFE(0);
IsoparametricTransformation trans;
mesh.GetElementTransformation(0, &trans);
int order = trans.OrderW() + 2 * el.GetOrder();
const IntegrationRule *ir = &IntRules.Get(el.GetGeomType(), order);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
const int dof = el.GetDof();
const int el_dim = el.GetDim();
DenseMatrix vshape_bar(dof, el_dim);
for (int k = 0; k < vshape_bar.Width(); ++k)
{
for (int j = 0; j < vshape_bar.Height(); ++j)
{
vshape_bar(j, k) = distribution(generator);
}
}
// reverse-mode differentiation CalcVShape
DenseMatrix &coords = trans.GetPointMat();
DenseMatrix coords_bar(coords.Height(), coords.Width());
coords_bar = 0.0;
el.CalcVShapeRevDiff(trans, vshape_bar, coords_bar);
// get the weighted derivatives using finite difference method
DenseMatrix vshape_fd(dof, el_dim), vshape_pert(dof, el_dim);
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
trans.Reset();
el.CalcVShape(trans, vshape_fd);
coords(di, n) -= 2.0 * eps_fd;
trans.Reset();
el.CalcVShape(trans, vshape_pert);
vshape_fd -= vshape_pert;
vshape_fd *= 1.0 / (2.0 * eps_fd);
coords(di, n) += eps_fd;
double x_bar_fd = vshape_bar * vshape_fd;
REQUIRE(coords_bar(di, n) == Approx(x_bar_fd));
}
}
}
}
}
void runCalcPhysCurlShapeRevDiffTest(Mesh &mesh)
{
for (int p = 1; p <= 4; ++p)
{
const int dim = mesh.Dimension();
ND_FECollection fec(p, dim);
FiniteElementSpace fes(&mesh, &fec);
const FiniteElement &el = *fes.GetFE(0);
IsoparametricTransformation trans;
mesh.GetElementTransformation(0, &trans);
int order = trans.OrderW() + 2 * el.GetOrder();
const IntegrationRule *ir = &IntRules.Get(el.GetGeomType(), order);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
const int dof = el.GetDof();
const int el_dim = el.GetDim();
DenseMatrix curlshape_bar(dof, el_dim);
for (int k = 0; k < curlshape_bar.Width(); ++k)
{
for (int j = 0; j < curlshape_bar.Height(); ++j)
{
curlshape_bar(j, k) = distribution(generator);
}
}
// reverse-mode differentiation CalcPhysCurlShape
DenseMatrix &coords = trans.GetPointMat();
DenseMatrix coords_bar(coords.Height(), coords.Width());
coords_bar = 0.0;
el.CalcPhysCurlShapeRevDiff(trans, curlshape_bar, coords_bar);
// get the weighted derivatives using finite difference method
DenseMatrix curlshape_fd(dof, el_dim), curlshape_pert(dof, el_dim);
for (int n = 0; n < coords.Width(); ++n)
{
for (int di = 0; di < coords.Height(); ++di)
{
coords(di, n) += eps_fd;
trans.Reset();
el.CalcPhysCurlShape(trans, curlshape_fd);
coords(di, n) -= 2.0 * eps_fd;
trans.Reset();
el.CalcPhysCurlShape(trans, curlshape_pert);
curlshape_fd -= curlshape_pert;
curlshape_fd *= 1.0 / (2.0 * eps_fd);
coords(di, n) += eps_fd;
double x_bar_fd = curlshape_bar * curlshape_fd;
REQUIRE(coords_bar(di, n) == Approx(x_bar_fd));
}
}
}
}
}
} // namespace fe_revdiff
+514
View File
@@ -341,3 +341,517 @@ TEST_CASE("DenseTensor copy", "[DenseMatrix][DenseTensor]")
REQUIRE(t3.Data()[i] == t1.Data()[i]);
}
}
TEST_CASE("DenseMatrix CalcAdjugateRevDiff", "[DenseMatrix]")
{
constexpr double eps_fd = 1e-5; // 2nd-order finite-difference step size
SECTION("1x1 matrix")
{
double A_data[1] = { 3.1415926};
double adjA_bar_data[1] = {-2.0};
DenseMatrix A(A_data, 1, 1);
DenseMatrix adjA_bar(adjA_bar_data, 1, 1);
DenseMatrix A_bar(1, 1), adjA_fd(1, 1);
DenseMatrix A_pert(1, 1), adjA_pert(1, 1);
// Compute the derivative using reverse mode
CalcAdjugateRevDiff(A, adjA_bar, A_bar);
// Compute the derivative using central finite-difference approximation
A_pert = A;
A_pert(0, 0) += eps_fd;
CalcAdjugate(A_pert, adjA_fd);
A_pert(0, 0) -= 2.0 * eps_fd;
CalcAdjugate(A_pert, adjA_pert);
adjA_fd -= adjA_pert;
adjA_fd *= 1/(2.0 * eps_fd);
// sum up derivative with weights
double A_bar_fd = adjA_fd(0, 0) * adjA_bar(0, 0);
REQUIRE(A_bar(0, 0) == Approx(A_bar_fd));
}
SECTION("2x1 matrix")
{
double A_data[2] = {2.0, -3.0};
double adjA_bar_data[2] = {-1.5, 4.0};
DenseMatrix A(A_data, 2, 1);
DenseMatrix adjA_bar(adjA_bar_data, 1, 2);
DenseMatrix A_bar(2,1), adjA_fd(1,2);
DenseMatrix A_pert(2,1), adjA_pert(1,2);
// Compute the derivatives using reverse mode
CalcAdjugateRevDiff(A, adjA_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 2; ++i)
{
// Pertrub A(i,0) and evaluate derivative of adjugate
A_pert = A;
A_pert(i, 0) += eps_fd;
CalcAdjugate(A_pert, adjA_fd);
A_pert(i, 0) -= 2.0 * eps_fd;
CalcAdjugate(A_pert, adjA_pert);
adjA_fd -= adjA_pert;
adjA_fd *= 1 / (2.0 * eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 2; ++k)
{
A_bar_fd += adjA_fd(0, k) * adjA_bar(0, k);
}
REQUIRE(A_bar(i, 0) == Approx(A_bar_fd));
}
}
SECTION("2x2 matrix")
{
double A_data[4] = {2.0, -3.0, 4.0, -1.0};
double adjA_bar_data[4] = {1.0, 4.0, 2.0, -3.0};
DenseMatrix A(A_data, 2, 2);
DenseMatrix adjA_bar(adjA_bar_data, 2, 2);
DenseMatrix A_bar(2,2), adjA_fd(2,2);
DenseMatrix A_pert(2,2), adjA_pert(2,2);
// Compute the derivatives using reverse mode
CalcAdjugateRevDiff(A, adjA_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < 2; ++j)
{
// Pertrub A(i,j) and evaluate derivative of adjugate
A_pert = A;
A_pert(i,j) += eps_fd;
CalcAdjugate(A_pert, adjA_fd);
A_pert(i,j) -= 2.0*eps_fd;
CalcAdjugate(A_pert, adjA_pert);
adjA_fd -= adjA_pert;
adjA_fd *= 1/(2.0*eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 2; ++k)
{
for (int l = 0; l < 2; ++l)
{
A_bar_fd += adjA_fd(k,l)*adjA_bar(k,l);
}
}
REQUIRE(A_bar(i,j) == Approx(A_bar_fd));
}
}
}
SECTION("3x1 matrix")
{
double A_data[3] = {2.0, -3.0, 3.1415926};
double adjA_bar_data[3] = {-1.5, 4.0, 2.71828};
DenseMatrix A(A_data, 3, 1);
DenseMatrix adjA_bar(adjA_bar_data, 1, 3);
DenseMatrix A_bar(3, 1), adjA_fd(1, 3);
DenseMatrix A_pert(3, 1), adjA_pert(1, 3);
// Compute the derivatives using reverse mode
CalcAdjugateRevDiff(A, adjA_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 3; ++i)
{
// Pertrub A(i,0) and evaluate derivative of adjugate
A_pert = A;
A_pert(i, 0) += eps_fd;
CalcAdjugate(A_pert, adjA_fd);
A_pert(i, 0) -= 2.0 * eps_fd;
CalcAdjugate(A_pert, adjA_pert);
adjA_fd -= adjA_pert;
adjA_fd *= 1 / (2.0 * eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 3; ++k)
{
A_bar_fd += adjA_fd(0, k) * adjA_bar(0, k);
}
REQUIRE(A_bar(i, 0) == Approx(A_bar_fd));
}
}
SECTION("3x3 matrix")
{
double A_data[9] = {1.0, 5.0, 3.0, -2.0, 6.0, -9.0, 4.0, -7.0, 8.0};
double adjA_bar_data[9] = {3.0, 6.0, -8.0, 1.0, -7.0, 5.0, 2.0, 4.0, -9.0};
DenseMatrix A(A_data, 3, 3);
DenseMatrix adjA_bar(adjA_bar_data, 3, 3);
DenseMatrix A_bar(3,3), adjA_fd(3,3);
DenseMatrix A_pert(3,3), adjA_pert(3,3);
// Compute the derivatives using reverse mode
CalcAdjugateRevDiff(A, adjA_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
// Pertrub A(i,j) and evaluate derivative of adjugate
A_pert = A;
A_pert(i,j) += eps_fd;
CalcAdjugate(A_pert, adjA_fd);
A_pert(i,j) -= 2.0*eps_fd;
CalcAdjugate(A_pert, adjA_pert);
adjA_fd -= adjA_pert;
adjA_fd *= 1/(2.0*eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 3; ++k)
{
for (int l = 0; l < 3; ++l)
{
A_bar_fd += adjA_fd(k,l)*adjA_bar(k,l);
}
}
REQUIRE(A_bar(i,j) == Approx(A_bar_fd));
}
}
}
}
TEST_CASE("DenseMatrix CalcInverseRevDiff", "[DenseMatrix]")
{
constexpr double eps_fd = 1e-5; // 2nd-order finite-difference step size
SECTION("1x1 matrix")
{
double A_data[1] = { 3.1415926};
double invA_bar_data[1] = {-2.0};
DenseMatrix A(A_data, 1, 1);
DenseMatrix invA_bar(invA_bar_data, 1, 1);
DenseMatrix A_bar(1, 1), invA_fd(1, 1);
DenseMatrix A_pert(1, 1), invA_pert(1, 1);
// Compute the derivative using reverse mode
A_bar = 0.0;
CalcInverseRevDiff(A, invA_bar, A_bar);
// Compute the derivative using central finite-difference approximation
A_pert = A;
A_pert(0, 0) += eps_fd;
CalcInverse(A_pert, invA_fd);
A_pert(0, 0) -= 2.0 * eps_fd;
CalcInverse(A_pert, invA_pert);
invA_fd -= invA_pert;
invA_fd *= 1/(2.0 * eps_fd);
// sum up derivative with weights
double A_bar_fd = invA_fd(0, 0) * invA_bar(0, 0);
REQUIRE(A_bar(0, 0) == Approx(A_bar_fd));
}
SECTION("2x1 matrix")
{
double A_data[2] = {2.0, -3.0};
double invA_bar_data[2] = {-1.5, 4.0};
DenseMatrix A(A_data, 2, 1);
DenseMatrix invA_bar(invA_bar_data, 1, 2);
DenseMatrix A_bar(2,1), invA_fd(1,2);
DenseMatrix A_pert(2,1), invA_pert(1,2);
// Compute the derivatives using reverse mode
A_bar = 0.0;
CalcInverseRevDiff(A, invA_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 2; ++i)
{
// Pertrub A(i,0) and evaluate derivative of inverse
A_pert = A;
A_pert(i, 0) += eps_fd;
CalcInverse(A_pert, invA_fd);
A_pert(i, 0) -= 2.0 * eps_fd;
CalcInverse(A_pert, invA_pert);
invA_fd -= invA_pert;
invA_fd *= 1 / (2.0 * eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 2; ++k)
{
A_bar_fd += invA_fd(0, k) * invA_bar(0, k);
}
REQUIRE(A_bar(i, 0) == Approx(A_bar_fd));
}
}
SECTION("2x2 matrix")
{
double A_data[4] = {2.0, -3.0, 4.0, -1.0};
double invA_bar_data[4] = {1.0, 4.0, 2.0, -3.0};
DenseMatrix A(A_data, 2, 2);
DenseMatrix invA_bar(invA_bar_data, 2, 2);
DenseMatrix A_bar(2,2), invA_fd(2,2);
DenseMatrix A_pert(2,2), invA_pert(2,2);
// Compute the derivatives using reverse mode
A_bar = 0.0;
CalcInverseRevDiff(A, invA_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < 2; ++j)
{
// Pertrub A(i,j) and evaluate derivative of inverse
A_pert = A;
A_pert(i,j) += eps_fd;
CalcInverse(A_pert, invA_fd);
A_pert(i,j) -= 2.0*eps_fd;
CalcInverse(A_pert, invA_pert);
invA_fd -= invA_pert;
invA_fd *= 1/(2.0*eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 2; ++k)
{
for (int l = 0; l < 2; ++l)
{
A_bar_fd += invA_fd(k,l)*invA_bar(k,l);
}
}
REQUIRE(A_bar(i,j) == Approx(A_bar_fd));
}
}
}
SECTION("3x1 matrix")
{
double A_data[3] = {2.0, -3.0, 3.1415926};
double invA_bar_data[3] = {-1.5, 4.0, 2.71828};
DenseMatrix A(A_data, 3, 1);
DenseMatrix invA_bar(invA_bar_data, 1, 3);
DenseMatrix A_bar(3, 1), invA_fd(1, 3);
DenseMatrix A_pert(3, 1), invA_pert(1, 3);
// Compute the derivatives using reverse mode
A_bar = 0.0;
CalcInverseRevDiff(A, invA_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 3; ++i)
{
// Pertrub A(i,0) and evaluate derivative of inverse
A_pert = A;
A_pert(i, 0) += eps_fd;
CalcInverse(A_pert, invA_fd);
A_pert(i, 0) -= 2.0 * eps_fd;
CalcInverse(A_pert, invA_pert);
invA_fd -= invA_pert;
invA_fd *= 1 / (2.0 * eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 3; ++k)
{
A_bar_fd += invA_fd(0, k) * invA_bar(0, k);
}
REQUIRE(A_bar(i, 0) == Approx(A_bar_fd));
}
}
SECTION("3x2 matrix")
{
double A_data[6] = {1.0, 5.0, 3.0, -2.0, 6.0, -9.0};
double invA_bar_data[6] = {3.0, 6.0, -8.0, 1.0, -7.0, 5.0};
// double invA_bar_data[6] = {1.0, 0.0, 0.0, 0.0, 0.0, 0.0};
DenseMatrix invA_bar(invA_bar_data, 2, 3);
DenseMatrix invA_pert(2, 3), invA_fd(2, 3);
DenseMatrix A(A_data, 3, 2);
DenseMatrix A_bar(3, 2), A_pert(3, 2);
// Compute the derivatives using reverse mode
A_bar = 0.0;
CalcInverseRevDiff(A, invA_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 2; ++j)
{
// Pertrub A(i,j) and evaluate derivative of inverse
A_pert = A;
A_pert(i,j) += eps_fd;
CalcInverse(A_pert, invA_fd);
A_pert(i,j) -= 2.0*eps_fd;
CalcInverse(A_pert, invA_pert);
invA_fd -= invA_pert;
invA_fd *= 1/(2.0*eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 3; ++k)
{
for (int l = 0; l < 2; ++l)
{
A_bar_fd += invA_fd(l, k)*invA_bar(l, k);
}
}
REQUIRE(A_bar(i,j) == Approx(A_bar_fd));
}
}
}
SECTION("3x3 matrix")
{
double A_data[9] = {1.0, 5.0, 3.0, -2.0, 6.0, -9.0, 4.0, -7.0, 8.0};
double invA_bar_data[9] = {3.0, 6.0, -8.0, 1.0, -7.0, 5.0, 2.0, 4.0, -9.0};
DenseMatrix A(A_data, 3, 3);
DenseMatrix invA_bar(invA_bar_data, 3, 3);
DenseMatrix A_bar(3,3), invA_fd(3,3);
DenseMatrix A_pert(3,3), invA_pert(3,3);
// Compute the derivatives using reverse mode
A_bar = 0.0;
CalcInverseRevDiff(A, invA_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
// Pertrub A(i,j) and evaluate derivative of inverse
A_pert = A;
A_pert(i,j) += eps_fd;
CalcInverse(A_pert, invA_fd);
A_pert(i,j) -= 2.0*eps_fd;
CalcInverse(A_pert, invA_pert);
invA_fd -= invA_pert;
invA_fd *= 1/(2.0*eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 3; ++k)
{
for (int l = 0; l < 3; ++l)
{
A_bar_fd += invA_fd(k,l)*invA_bar(k,l);
}
}
REQUIRE(A_bar(i,j) == Approx(A_bar_fd));
}
}
}
}
TEST_CASE("DenseMatrix WeightRevDiff", "[DenseMatrix]")
{
// This also tests DenseMatrix::DetRevDiff indirectly
constexpr double eps_fd = 1e-5; // 2nd-order finite-difference step size
double A_data[9] = {1.0, 5.0, 3.0, -2.0, 6.0, -9.0, 4.0, -7.0, 8.0};
for (int height = 1; height <= 3; ++height)
{
for (int width = 1; width <= height; ++width)
{
DenseMatrix A(A_data, height, width);
DenseMatrix weight_bar(height, width);
DenseMatrix A_pert(height, width);
// Compute the gradient of A.Weight() using reverse mode AD
A.WeightRevDiff(weight_bar);
// Compute the gradient of A.Weight using 2nd order finite-difference
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
// Perturb A(i,j) in + and - directions and evaluate Weight()
A_pert = A;
A_pert(i,j) += eps_fd;
double dweight = A_pert.Weight();
A_pert(i,j) -= 2.0*eps_fd;
dweight -= A_pert.Weight();
dweight /= (2.0*eps_fd);
REQUIRE(weight_bar(i,j) == Approx(dweight));
}
}
}
}
}
TEST_CASE("DenseMatrix CalcOrthoRevDiff", "[DenseMatrix]")
{
constexpr double eps_fd = 1e-5; // 2nd-order finite-difference step size
SECTION("2x1 matrix")
{
double A_data[2] = {2.0, -3.0};
double n_bar_data[2] = {-1.5, 4.0};
Vector n_bar(n_bar_data, 2);
Vector n_pert(2), n_fd(2);
DenseMatrix A(A_data, 2, 1);
DenseMatrix A_bar(2,1), A_pert(2,1);
// Compute the derivatives using reverse mode
CalcOrthoRevDiff(A, n_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 2; ++i)
{
// Pertrub A(i,0) and evaluate derivative of adjugate
A_pert = A;
A_pert(i, 0) += eps_fd;
CalcOrtho(A_pert, n_fd);
A_pert(i, 0) -= 2.0 * eps_fd;
CalcOrtho(A_pert, n_pert);
n_fd -= n_pert;
n_fd *= 1 / (2.0 * eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 2; ++k)
{
A_bar_fd += n_fd(k) * n_bar(k);
}
REQUIRE(A_bar(i, 0) == Approx(A_bar_fd));
}
}
SECTION("3x2 matrix")
{
double A_data[6] = {1.0, 5.0, 3.0, -2.0, 6.0, -9.0};
double n_bar_data[3] = {1.0, 4.0, -3.0};
Vector n_bar(n_bar_data, 3);
Vector n_pert(3), n_fd(3);
DenseMatrix A(A_data, 3, 2);
DenseMatrix A_bar(3,2), A_pert(3,2);
// Compute the derivatives using reverse mode
CalcOrthoRevDiff(A, n_bar, A_bar);
// Compute the derivatives using central finite-difference approximation
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 2; ++j)
{
// Pertrub A(i,j) and evaluate derivative of adjugate
A_pert = A;
A_pert(i,j) += eps_fd;
CalcOrtho(A_pert, n_fd);
A_pert(i,j) -= 2.0*eps_fd;
CalcOrtho(A_pert, n_pert);
n_fd -= n_pert;
n_fd *= 1/(2.0*eps_fd);
// sum up derivative with weights
double A_bar_fd = 0.0;
for (int k = 0; k < 3; ++k)
{
A_bar_fd += n_fd(k)*n_bar(k);
}
REQUIRE(A_bar(i,j) == Approx(A_bar_fd));
}
}
}
}