From d9ddc9013af89924c4d575fb007b0e380b62def6 Mon Sep 17 00:00:00 2001
From: Elias Pipping <elias.pipping@fu-berlin.de>
Date: Mon, 6 Aug 2012 11:21:29 +0200
Subject: [PATCH] Move minimisation code to a separate header

---
 dune/tectonic/ellipticenergy.hh    | 170 ---------------------------
 dune/tectonic/minimisation.hh      | 182 +++++++++++++++++++++++++++++
 dune/tectonic/myblockproblem.hh    |   1 +
 src/test-gradient-method-helper.hh |   1 +
 4 files changed, 184 insertions(+), 170 deletions(-)
 create mode 100644 dune/tectonic/minimisation.hh

diff --git a/dune/tectonic/ellipticenergy.hh b/dune/tectonic/ellipticenergy.hh
index c30626f7..274d8c4c 100644
--- a/dune/tectonic/ellipticenergy.hh
+++ b/dune/tectonic/ellipticenergy.hh
@@ -6,11 +6,8 @@
 #include <dune/common/stdstreams.hh>
 
 #include <dune/fufem/interval.hh>
-#include <dune/tnnmg/problem-classes/bisection.hh>
 
-#include "mydirectionalconvexfunction.hh"
 #include "localnonlinearity.hh"
-#include "circularconvexfunction.hh"
 
 namespace Dune {
 template <int dim> class EllipticEnergy {
@@ -146,172 +143,5 @@ template <int dim> class EllipticEnergy {
     y.axpy(-dx / xx, x);
   }
 };
-
-template <class Functional>
-void descentMinimisation(Functional const &J,
-                         typename Functional::SmallVector &x,
-                         typename Functional::SmallVector const &descDir,
-                         Bisection const &bisection) {
-  typedef typename Functional::SmallVector SmallVector;
-  typedef typename Functional::NonlinearityType LocalNonlinearityType;
-
-  // {{{ Construct a restriction of J to the line x + t * descDir
-
-  /* We have
-
-     1/2 <A(u+xv),u+xv>-<b,u+xv> = 1/2 <Av,v> x^2 - <b-Au,v> x + <1/2 Au-b,u>
-
-     since A is symmetric.
-  */
-  SmallVector tmp = J.b;               //  b
-  J.A.mmv(x, tmp);                     //  b-Au
-  double const JRestb = tmp * descDir; // <b-Au,v>
-
-  J.A.mv(descDir, tmp);                //  Av
-  double const JRestA = tmp * descDir; // <Av,v>
-
-  MyDirectionalConvexFunction<LocalNonlinearityType> const JRest(
-      JRestA, JRestb, *J.phi, x, descDir);
-  // }}}
-
-  { // Debug
-    Interval<double> D;
-    JRest.subDiff(0, D);
-
-    dverb
-        << "## Directional derivative (as per subdifferential of restriction): "
-        << D[1] << " (coordinates of the restriction)" << std::endl;
-    /*
-      It is possible that this differs quite a lot from the
-      directional derivative computed in the descentDirection()
-      method:
-
-      If phi is nonsmooth at x, so that the directional
-      derivatives jump, and |x| is computed to be too small or too
-      large globally or locally, the locally computed
-      subdifferential and the globally computed subdifferential
-      will no longer coincide!
-
-      The assertion D[1] <= 0 may thus fail.
-    */
-  }
-
-  int count;
-  double const stepsize = bisection.minimize(JRest, 0.0, 0.0, count);
-  dverb << "Number of iterations in the bisection method: " << count
-        << std::endl;
-  ;
-
-  x.axpy(stepsize, descDir);
-}
-
-template <class Functional>
-void tangentialMinimisation(Functional const &J,
-                            typename Functional::SmallVector &x,
-                            typename Functional::SmallVector const &descDir,
-                            Bisection const &bisection) {
-  typedef typename Functional::SmallMatrix SmallMatrix;
-  typedef typename Functional::SmallVector SmallVector;
-
-  // We completely ignore the nonlinearity here -- when restricted
-  // to a circle, it just enters as a constant!
-  CircularConvexFunction<SmallMatrix, SmallVector> const JRest(J.A, J.b, x,
-                                                               descDir);
-
-  int count;
-  double const stepsize = bisection.minimize(JRest, 0.0, 1.0, count);
-  dverb << "Number of iterations in the bisection method: " << count
-        << std::endl;
-  ;
-
-  // Since x is used in the computation of the rhs, do not write to it directly
-  SmallVector tmp;
-  JRest.cartesian(stepsize, tmp);
-  x = tmp;
-}
-
-template <class Functional>
-void minimise(Functional const &J, typename Functional::SmallVector &x,
-              size_t steps, Bisection const &bisection) {
-  typedef typename Functional::SmallVector SmallVector;
-
-  for (size_t step = 0; step < steps; ++step) {
-    SmallVector descDir;
-    bool const linesearchp = J.descentDirection(x, descDir);
-
-    if (descDir == SmallVector(0.0))
-      return;
-
-    if (linesearchp) {
-      descentMinimisation(J, x, descDir, bisection);
-    } else {
-      Bisection slowBisection(bisection);
-      slowBisection.setFastQuadratic(false);
-
-      tangentialMinimisation(J, x, descDir, slowBisection);
-    }
-  }
-}
-
-// NOTE: only works for the 2D case
-template <class Functional>
-void minimise2(Functional const &J, typename Functional::SmallVector &x,
-               size_t steps, Bisection const &bisection) {
-  typedef typename Functional::SmallVector SmallVector;
-  minimisationInitialiser(J, bisection, x);
-
-  { // Make a single step where we already know we're not differentiable
-    SmallVector descDir;
-    if (x == SmallVector(0))
-      J.descentAtZero(descDir);
-    else
-      J.descentDirectionNew(x, descDir);
-    descentMinimisation(J, x, descDir, bisection);
-  }
-
-  // From here on, we're in a C1 region and stay there.
-  for (size_t i = 1; i < steps; ++i) {
-    SmallVector descDir;
-    J.descentDirectionNew(x, descDir);
-    descentMinimisation(J, x, descDir, bisection);
-  }
-}
-
-template <class Functional>
-void minimisationInitialiser(Functional const &J, Bisection const &bisection,
-                             typename Functional::SmallVector &startingPoint) {
-  typedef typename Functional::SmallVector SmallVector;
-
-  std::vector<double> const kinks = { 5, 10,
-                                      15 }; // FIXME: We happen to know these
-
-  SmallVector x_old(0);
-  double Jx_old = J(x_old);
-
-  for (auto &kink : kinks) {
-    SmallVector x1 = { kink, 0 }; // Random vector that has the right norm
-    SmallVector const descDir1 = { x1[1], -x1[0] };
-    tangentialMinimisation(J, x1, descDir1, bisection);
-    double const Jx1 = J(x1);
-
-    SmallVector x2(0);
-    x2.axpy(-1, x1);
-    SmallVector const descDir2 = { x2[1], -x2[0] };
-    tangentialMinimisation(J, x2, descDir2, bisection);
-    double const Jx2 = J(x2);
-
-    double const Jx = (Jx2 < Jx1 ? Jx2 : Jx1);
-    SmallVector const x = (Jx2 < Jx1 ? x2 : x1);
-
-    if (Jx >= Jx_old) {
-      startingPoint = x_old;
-      return;
-    }
-
-    Jx_old = Jx;
-    x_old = x;
-  }
-  startingPoint = x_old;
-}
 }
 #endif
diff --git a/dune/tectonic/minimisation.hh b/dune/tectonic/minimisation.hh
new file mode 100644
index 00000000..113c101a
--- /dev/null
+++ b/dune/tectonic/minimisation.hh
@@ -0,0 +1,182 @@
+#ifndef MINIMISATION_HH
+#define MINIMISATION_HH
+
+#include <dune/common/fmatrix.hh>
+#include <dune/common/fvector.hh>
+#include <dune/common/stdstreams.hh>
+
+#include <dune/fufem/interval.hh>
+#include <dune/tnnmg/problem-classes/bisection.hh>
+
+#include "circularconvexfunction.hh"
+#include "mydirectionalconvexfunction.hh"
+
+namespace Dune {
+template <class Functional>
+void descentMinimisation(Functional const &J,
+                         typename Functional::SmallVector &x,
+                         typename Functional::SmallVector const &descDir,
+                         Bisection const &bisection) {
+  typedef typename Functional::SmallVector SmallVector;
+  typedef typename Functional::NonlinearityType LocalNonlinearityType;
+
+  // {{{ Construct a restriction of J to the line x + t * descDir
+
+  /* We have
+
+     1/2 <A(u+xv),u+xv>-<b,u+xv> = 1/2 <Av,v> x^2 - <b-Au,v> x + <1/2 Au-b,u>
+
+     since A is symmetric.
+  */
+  SmallVector tmp = J.b;               //  b
+  J.A.mmv(x, tmp);                     //  b-Au
+  double const JRestb = tmp * descDir; // <b-Au,v>
+
+  J.A.mv(descDir, tmp);                //  Av
+  double const JRestA = tmp * descDir; // <Av,v>
+
+  MyDirectionalConvexFunction<LocalNonlinearityType> const JRest(
+      JRestA, JRestb, *J.phi, x, descDir);
+  // }}}
+
+  { // Debug
+    Interval<double> D;
+    JRest.subDiff(0, D);
+
+    dverb
+        << "## Directional derivative (as per subdifferential of restriction): "
+        << D[1] << " (coordinates of the restriction)" << std::endl;
+    /*
+      It is possible that this differs quite a lot from the
+      directional derivative computed in the descentDirection()
+      method:
+
+      If phi is nonsmooth at x, so that the directional
+      derivatives jump, and |x| is computed to be too small or too
+      large globally or locally, the locally computed
+      subdifferential and the globally computed subdifferential
+      will no longer coincide!
+
+      The assertion D[1] <= 0 may thus fail.
+    */
+  }
+
+  int count;
+  double const stepsize = bisection.minimize(JRest, 0.0, 0.0, count);
+  dverb << "Number of iterations in the bisection method: " << count
+        << std::endl;
+  ;
+
+  x.axpy(stepsize, descDir);
+}
+
+template <class Functional>
+void tangentialMinimisation(Functional const &J,
+                            typename Functional::SmallVector &x,
+                            typename Functional::SmallVector const &descDir,
+                            Bisection const &bisection) {
+  typedef typename Functional::SmallMatrix SmallMatrix;
+  typedef typename Functional::SmallVector SmallVector;
+
+  // We completely ignore the nonlinearity here -- when restricted
+  // to a circle, it just enters as a constant!
+  CircularConvexFunction<SmallMatrix, SmallVector> const JRest(J.A, J.b, x,
+                                                               descDir);
+
+  int count;
+  double const stepsize = bisection.minimize(JRest, 0.0, 1.0, count);
+  dverb << "Number of iterations in the bisection method: " << count
+        << std::endl;
+  ;
+
+  // Since x is used in the computation of the rhs, do not write to it directly
+  SmallVector tmp;
+  JRest.cartesian(stepsize, tmp);
+  x = tmp;
+}
+
+template <class Functional>
+void minimise(Functional const &J, typename Functional::SmallVector &x,
+              size_t steps, Bisection const &bisection) {
+  typedef typename Functional::SmallVector SmallVector;
+
+  for (size_t step = 0; step < steps; ++step) {
+    SmallVector descDir;
+    bool const linesearchp = J.descentDirection(x, descDir);
+
+    if (descDir == SmallVector(0.0))
+      return;
+
+    if (linesearchp) {
+      descentMinimisation(J, x, descDir, bisection);
+    } else {
+      Bisection slowBisection(bisection);
+      slowBisection.setFastQuadratic(false);
+
+      tangentialMinimisation(J, x, descDir, slowBisection);
+    }
+  }
+}
+
+// NOTE: only works for the 2D case
+template <class Functional>
+void minimise2(Functional const &J, typename Functional::SmallVector &x,
+               size_t steps, Bisection const &bisection) {
+  typedef typename Functional::SmallVector SmallVector;
+  minimisationInitialiser(J, bisection, x);
+
+  { // Make a single step where we already know we're not differentiable
+    SmallVector descDir;
+    if (x == SmallVector(0))
+      J.descentAtZero(descDir);
+    else
+      J.descentDirectionNew(x, descDir);
+    descentMinimisation(J, x, descDir, bisection);
+  }
+
+  // From here on, we're in a C1 region and stay there.
+  for (size_t i = 1; i < steps; ++i) {
+    SmallVector descDir;
+    J.descentDirectionNew(x, descDir);
+    descentMinimisation(J, x, descDir, bisection);
+  }
+}
+
+template <class Functional>
+void minimisationInitialiser(Functional const &J, Bisection const &bisection,
+                             typename Functional::SmallVector &startingPoint) {
+  typedef typename Functional::SmallVector SmallVector;
+
+  std::vector<double> const kinks = { 5, 10,
+                                      15 }; // FIXME: We happen to know these
+
+  SmallVector x_old(0);
+  double Jx_old = J(x_old);
+
+  for (auto &kink : kinks) {
+    SmallVector x1 = { kink, 0 }; // Random vector that has the right norm
+    SmallVector const descDir1 = { x1[1], -x1[0] };
+    tangentialMinimisation(J, x1, descDir1, bisection);
+    double const Jx1 = J(x1);
+
+    SmallVector x2(0);
+    x2.axpy(-1, x1);
+    SmallVector const descDir2 = { x2[1], -x2[0] };
+    tangentialMinimisation(J, x2, descDir2, bisection);
+    double const Jx2 = J(x2);
+
+    double const Jx = (Jx2 < Jx1 ? Jx2 : Jx1);
+    SmallVector const x = (Jx2 < Jx1 ? x2 : x1);
+
+    if (Jx >= Jx_old) {
+      startingPoint = x_old;
+      return;
+    }
+
+    Jx_old = Jx;
+    x_old = x;
+  }
+  startingPoint = x_old;
+}
+}
+#endif
diff --git a/dune/tectonic/myblockproblem.hh b/dune/tectonic/myblockproblem.hh
index dc447c98..fb070099 100644
--- a/dune/tectonic/myblockproblem.hh
+++ b/dune/tectonic/myblockproblem.hh
@@ -12,6 +12,7 @@
 
 #include "globalnonlinearity.hh"
 #include "localnonlinearity.hh"
+#include "minimisation.hh"
 #include "mydirectionalconvexfunction.hh"
 #include "ellipticenergy.hh"
 
diff --git a/src/test-gradient-method-helper.hh b/src/test-gradient-method-helper.hh
index f3016d5a..a9479ebb 100644
--- a/src/test-gradient-method-helper.hh
+++ b/src/test-gradient-method-helper.hh
@@ -6,6 +6,7 @@
 #include <dune/tnnmg/problem-classes/bisection.hh>
 
 #include <dune/tectonic/ellipticenergy.hh>
+#include <dune/tectonic/minimisation.hh>
 
 template <int dim>
 double functionTester(Dune::EllipticEnergy<dim> J,
-- 
GitLab