Implementation

17. Implementation#

We implement a function algebra, which allows us to write expressions like

  func = Id + 3 * Compose (f, g);

where f and g are functions, and Id is the identic function. Then the composed function func shall be able to compute the function value and the derivative at a given point:

  Matrix jacobi(func->DimF(), func->DimX());
  func->EvaluateDeriv(x, jacobi);

The base class for such functions is

class NonlinearFunction {
public:
  virtual ~NonlinearFunction() = default;
  virtual size_t DimX() const = 0;
  virtual size_t DimF() const = 0;
  virtual void Evaluate (VectorView<double> x, VectorView<double> f) const = 0;
  virtual void EvaluateDeriv (VectorView<double> x, MatrixView<double> df) const = 0;
};

DimX and DimF provide the vector space dimensions of the domain, and the image. The Evaluate and EvaluateDeriv take vector- and matrix-views, such that we can take sub-vectors and sub-matrices when calling the evaluations.

We build expression trees, similar as the expression templates for vectors and matrices. But now we use virtual inheritance instead of the Barton Neckman trick (i.e. dynamic polymorphism instead of static polymorphism). This is more expensive to create, but it allows to pass NonlinearFunction objects between C++ functions.

A SumFunction implements the sum \(f_A+f_B\). The two childs are provided by pointers. Shared pointers allow simple life-time management:

class SumFunction : public NonlinearFunction {
  shared_ptr<NonlinearFunction> fa, fb;
public:
  SumFunction (shared_ptr<NonlinearFunction> _fa,
               shared_ptr<NonlinearFunction> _fb)
    : fa(_fa), fb(_fb) { } 
    
  size_t DimX() const { return fa->DimX(); }
  size_t DimF() const { return fa->DimF(); }

  void Evaluate (VectorView<double> x, VectorView<double> f) const
  {
    fa->Evaluate(x, f);
    Vector<> tmp(DimF());
    fb->Evaluate(x, tmp);
    f += tmp;
  }

  void EvaluateDeriv (VectorView<double> x, MatrixView<double> df) const
  {
    fa->EvaluateDeriv(x, df);
    Matrix<> tmp(DimF(), DimX());
    fb->EvaluateDeriv(x, tmp);
    df += tmp;
  }
};

To generate such a SumFunction object, we overload the operator+ for two NonlinearFunction objects, represented as shared objectes:

auto operator+ (shared_ptr<NonlinearFunction> fa, shared_ptr<NonlinearFunction> fb)
{
  return make_shared<SumFunction>(fa, fb);
}