From 4dd7b29eba815116ad8d2ae074f3bdc6f3ccbba9 Mon Sep 17 00:00:00 2001 From: Elias Pipping <elias.pipping@fu-berlin.de> Date: Fri, 11 Nov 2011 15:53:15 +0100 Subject: [PATCH] Bisect along a sphere --- dune/tectonic/curvedfunction.hh | 70 +++++++++++++++ dune/tectonic/samplefunctional.hh | 137 ++++++++++++++++++------------ src/test-gradient-method.cc | 18 ++-- 3 files changed, 161 insertions(+), 64 deletions(-) create mode 100644 dune/tectonic/curvedfunction.hh diff --git a/dune/tectonic/curvedfunction.hh b/dune/tectonic/curvedfunction.hh new file mode 100644 index 00000000..9d5fb281 --- /dev/null +++ b/dune/tectonic/curvedfunction.hh @@ -0,0 +1,70 @@ +#ifndef CURVED_FUNCTION_HH +#define CURVED_FUNCTION_HH + +namespace Dune { +template <class NonlinearityType> class CurvedFunction { + typedef typename NonlinearityType::VectorType VectorType; + typedef typename NonlinearityType::MatrixType MatrixType; + +public: + CurvedFunction(MatrixType const &A, VectorType const &b, + NonlinearityType const &phi, VectorType const &x, + VectorType const &dir) + : A(A), b(b), phi(phi), x(x), dir(dir) {} + + double quadraticPart() const { return 0; } + + double linearPart() const { return 0; } + + void subDiff(double m, Interval<double> &D) const { + VectorType x; + evaluate(m, x); + VectorType tangent; + tangentialDirection(m, tangent); + + phi.directionalSubDiff(x, tangent, D); + + VectorType tmp; + A.mv(x, tmp); // Ax + tmp -= b; // Ax - b + double const dotp = tmp * tangent; // <Ax - b,t> + D[0] += dotp; + D[1] += dotp; + } + + void domain(Interval<double> &domain) const { + // TODO + domain[0] = 0; + domain[1] = 1; + } + + void evaluate(double m, VectorType &y) const { + y = 0; + y.axpy(1 - m, x); + y.axpy(m, dir); + y /= y.two_norm(); + } + +private: + MatrixType const &A; + VectorType const &b; + NonlinearityType const φ + VectorType const &x; + VectorType const &dir; + + /* + Tangential vector within the plane, with positive direction. + + < (1-m)x + m*y,-m*|y|^2*x+(1-m)*|x|^2*y> + = -m(1-m)|y|^2<x,x> + m(1-m)|x|^2<y,y> (because <x,y> = 0) + = 0 + */ + void tangentialDirection(double m, VectorType &y) const { + y = 0; + y.axpy(-m * dir.two_norm2(), x); + y.axpy((1 - m) * x.two_norm2(), dir); + } +}; +} + +#endif diff --git a/dune/tectonic/samplefunctional.hh b/dune/tectonic/samplefunctional.hh index a19d40a5..50d8f785 100644 --- a/dune/tectonic/samplefunctional.hh +++ b/dune/tectonic/samplefunctional.hh @@ -13,6 +13,7 @@ #include <dune/tnnmg/problem-classes/directionalconvexfunction.hh> #include "localnonlinearity.hh" +#include "curvedfunction.hh" namespace Dune { template <int dim> class SampleFunctional { @@ -34,7 +35,7 @@ template <int dim> class SampleFunctional { return y * v + phi(v); // <1/2 Av - b,v> + H(|v|) } - void descentDirection(SmallVector const x, SmallVector &ret) const { + bool descentDirection(SmallVector const x, SmallVector &ret) const { // Check the squared norm rather than each component because // complementaryProjection() divides by it if (x.two_norm2() == 0.0) { @@ -54,7 +55,7 @@ template <int dim> class SampleFunctional { } else { ret = 0; } - return; + return true; } SmallVector pg; @@ -68,11 +69,15 @@ template <int dim> class SampleFunctional { dverb << "## Directional derivative (as per scalar product w/ " "semigradient): " << -(ret * mg) << " (coordinates of the restriction)" << std::endl; + ret *= -1; + return true; } else if (pgx <= 0 && mgx <= 0) { ret = mg; dverb << "## Directional derivative (as per scalar product w/ " "semigradient): " << -(ret * pg) << " (coordinates of the restriction)" << std::endl; + ret *= -1; + return true; } else { // Includes the case that pg points in direction x and mg // points in direction -x. The projection will then be zero. @@ -82,8 +87,9 @@ template <int dim> class SampleFunctional { dverb << "## Directional derivative (as per scalar product w/ " "semigradient): " << -(ret * ret) << " (coordinates of the restriction)" << std::endl; + ret *= -1; + return false; } - ret *= -1; } SmallMatrix const &A; @@ -136,7 +142,7 @@ void minimise(Functional const J, typename Functional::SmallVector &x, // become smaller than this number 1.0, // acceptFactor: ? 1e-12, // requiredResidual: ? - true, // fastQuadratic + false, // fastQuadratic 0)) // safety: acceptance factor for inexact // minimization { @@ -144,68 +150,89 @@ void minimise(Functional const J, typename Functional::SmallVector &x, for (size_t step = 0; step < steps; ++step) { SmallVector descDir; - J.descentDirection(x, descDir); + double linesearchp = J.descentDirection(x, descDir); if (descDir == SmallVector(0.0)) return; - // {{{ 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; + if (linesearchp) { + // {{{ Construct a restriction of J to the line x + t * descDir - J.A.mv(descDir, tmp); // Av - double const JRestA = tmp * descDir; // <Av,v> + /* We have - tmp = J.b; // b - J.A.mmv(x, tmp); // b-Au - double const JRestb = tmp * descDir; // <b-Au,v> + 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> - typedef typename Functional::NonlinearityType LocalNonlinearityType; - LocalNonlinearityType phi = J.phi; - typedef DirectionalConvexFunction<LocalNonlinearityType> - MyDirectionalConvexFunctionType; - // FIXME: We cannot pass J.phi directly because the constructor - // does not allow for constant arguments - MyDirectionalConvexFunctionType JRest(JRestA, JRestb, phi, x, descDir); - // }}} - - { // Debug - Interval<double> D; - JRest.subDiff(0, D); + since A is symmetric. + */ + SmallVector tmp; + + J.A.mv(descDir, tmp); // Av + double const JRestA = tmp * descDir; // <Av,v> + + tmp = J.b; // b + J.A.mmv(x, tmp); // b-Au + double const JRestb = tmp * descDir; // <b-Au,v> + + typedef typename Functional::NonlinearityType LocalNonlinearityType; + LocalNonlinearityType phi = J.phi; + typedef DirectionalConvexFunction<LocalNonlinearityType> + MyDirectionalConvexFunctionType; + // FIXME: We cannot pass J.phi directly because the constructor + // does not allow for constant arguments + MyDirectionalConvexFunctionType JRest(JRestA, JRestb, 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. + */ + } - dverb << "## Directional derivative (as per subdifferential of " - "restriction): " << D[1] << " (coordinates of the restriction)" + int count; + // FIXME: The value of x_old should not matter if the factor is 1.0, + // correct? + double const stepsize = bisection.minimize(JRest, 0.0, 1.0, count); + dverb << "Number of iterations in the bisection method: " << count << 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; - // FIXME: The value of x_old should not matter if the factor is 1.0, - // correct? - double const stepsize = bisection.minimize(JRest, 0.0, 1.0, count); - dverb << "Number of iterations in the bisection method: " << count - << std::endl; - ; + x.axpy(stepsize, descDir); + } else { + typedef typename Functional::NonlinearityType LocalNonlinearityType; + LocalNonlinearityType phi = J.phi; + typedef Dune::CurvedFunction<LocalNonlinearityType> MyCurvedFunctionType; + MyCurvedFunctionType JRest(J.A, J.b, phi, 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; + ; - x.axpy(stepsize, descDir); + // Since x is used in the computation of the rhs, do not write to it + // directly + SmallVector tmp; + JRest.evaluate(stepsize, tmp); + x = tmp; + } + // std::cout << "Resulting vector: " << x << std::endl; } } } diff --git a/src/test-gradient-method.cc b/src/test-gradient-method.cc index fa3b5827..ffb7e9ca 100644 --- a/src/test-gradient-method.cc +++ b/src/test-gradient-method.cc @@ -379,19 +379,19 @@ void testSampleFunctionSteep1() { start[0] = 0; start[1] = 1; - double const ret1 = functionTester(J, start, 7); + double const ret1 = functionTester(J, start, 3); // Something random start[0] = 279; start[1] = -96; - double const ret2 = functionTester(J, start, 7); + double const ret2 = functionTester(J, start, 3); assert(std::abs(ret1 - ret2) < 1e-5); start[0] = 0; start[1] = 0; - double const ret3 = functionTester(J, start, 2); + double const ret3 = functionTester(J, start, 1); assert(std::abs(ret1 - ret3) < 1e-5); } @@ -416,19 +416,19 @@ void testSampleFunctionSteep2() { start[0] = 0; start[1] = 1; - double const ret1 = functionTester(J, start, 7); + double const ret1 = functionTester(J, start, 1); // Something random start[0] = 279; start[1] = -96; - double const ret2 = functionTester(J, start, 7); + double const ret2 = functionTester(J, start, 3); assert(std::abs(ret1 - ret2) < 1e-5); start[0] = 0; start[1] = 0; - double const ret3 = functionTester(J, start, 3); + double const ret3 = functionTester(J, start, 1); assert(std::abs(ret1 - ret3) < 1e-5); } @@ -453,19 +453,19 @@ void testSteepFunction() { start[0] = 0; start[1] = 1; - double const ret1 = functionTester(J, start, 1000); + double const ret1 = functionTester(J, start, 1); // Something random start[0] = 279; start[1] = -96; - double const ret2 = functionTester(J, start, 1000); + double const ret2 = functionTester(J, start, 4); assert(std::abs(ret1 - ret2) < 1e-8); start[0] = 0; start[1] = 0; - double const ret3 = functionTester(J, start, 3); + double const ret3 = functionTester(J, start, 1); assert(std::abs(ret1 - ret3) < 1e-5); } -- GitLab