finite-difference-gradient.cc

Example shows finite differences gradient use.

// Copyright (C) 2009 by Thomas Moulard, AIST, CNRS, INRIA.
//
// This file is part of the roboptim.
//
// roboptim is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// roboptim is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with roboptim. If not, see <http://www.gnu.org/licenses/>.
#include "shared-tests/fixture.hh"
#include <iostream>
#include <roboptim/core/io.hh>
#include <roboptim/core/finite-difference-gradient.hh>
#include <roboptim/core/indent.hh>
#include <roboptim/core/visualization/gnuplot.hh>
#include <roboptim/core/visualization/gnuplot-commands.hh>
#include <roboptim/core/visualization/gnuplot-function.hh>
using namespace roboptim;
using namespace roboptim::visualization;
using namespace roboptim::visualization::gnuplot;
// Define a function with a correct gradient.
template <typename T>
struct FGood : public GenericDifferentiableFunction<T>
{
ROBOPTIM_DIFFERENTIABLE_FUNCTION_FWD_TYPEDEFS_
(GenericDifferentiableFunction<T>);
FGood () : GenericDifferentiableFunction<T> (1, 1, "x * x")
{}
void impl_compute (result_t& result,
const argument_t& argument) const throw ()
{
result (0) = argument[0] * argument[0];
}
void impl_gradient (gradient_t& gradient,
const argument_t& argument, size_type) const throw ();
};
template <>
void
FGood<EigenMatrixSparse>::impl_gradient
(gradient_t& gradient, const argument_t& argument, size_type) const throw ()
{
gradient.insert (0) = 2 * argument[0];
}
template <typename T>
void
FGood<T>::impl_gradient (gradient_t& gradient,
const argument_t& argument, size_type) const throw ()
{
gradient (0) = 2 * argument[0];
}
// Define a function with a bad gradient.
template <typename T>
struct FBad : public GenericDifferentiableFunction<T>
{
ROBOPTIM_DIFFERENTIABLE_FUNCTION_FWD_TYPEDEFS_
(GenericDifferentiableFunction<T>);
FBad () : GenericDifferentiableFunction<T> (1, 1, "x * x")
{}
void impl_compute (result_t& result,
const argument_t& argument) const throw ()
{
result (0) = argument[0] * argument[0];
}
void impl_gradient (gradient_t& gradient,
const argument_t& argument, size_type) const throw ();
};
template <>
void
FBad<EigenMatrixSparse>::impl_gradient
(gradient_t& gradient, const argument_t& argument, size_type) const throw ()
{
gradient.insert (0) = 5 * argument[0] + 42;
}
template <typename T>
void
FBad<T>::impl_gradient (gradient_t& gradient,
const argument_t& argument, size_type) const throw ()
{
gradient (0) = 5 * argument[0] + 42;
}
// Define a polynomial function.
template <typename T>
struct Polynomial : public GenericDifferentiableFunction<T>
{
ROBOPTIM_DIFFERENTIABLE_FUNCTION_FWD_TYPEDEFS_
(GenericDifferentiableFunction<T>);
Polynomial () : GenericDifferentiableFunction<T> (1, 1)
{}
void impl_compute (result_t& result,
const argument_t& argument) const throw ()
{
result (0) = -24 * argument[0] * argument[0] + 33 * argument[0] + 5;
}
void impl_gradient (gradient_t& gradient,
const argument_t& argument, size_type) const throw ();
};
template <>
void
Polynomial<EigenMatrixSparse>::impl_gradient
(gradient_t& gradient, const argument_t& argument, size_type) const throw ()
{
gradient.insert (0) = -42 * argument[0] + 33;
}
template <typename T>
void
Polynomial<T>::impl_gradient (gradient_t& gradient,
const argument_t& argument, size_type)
const throw ()
{
gradient (0) = -42 * argument[0] + 33;
}
// Define a function that draws a circle.
template <typename T>
struct CircleXY : public GenericDifferentiableFunction<T>
{
ROBOPTIM_DIFFERENTIABLE_FUNCTION_FWD_TYPEDEFS_
(GenericDifferentiableFunction<T>);
CircleXY () : GenericDifferentiableFunction<T> (1, 2)
{}
void impl_compute (result_t& result,
const argument_t& argument) const throw ()
{
result (0) = sin (argument[0]);
result (1) = cos (argument[0]);
}
void impl_gradient (gradient_t& result,
const argument_t& argument,
size_type idFunction) const throw ();
};
template <>
void
CircleXY<EigenMatrixSparse>::impl_gradient (gradient_t& gradient,
const argument_t& argument,
size_type idFunction) const throw ()
{
switch (idFunction)
{
case 0:
gradient.insert (0) = cos (argument[0]);
break;
case 1:
gradient.insert (0) = -sin (argument[0]);
break;
default:
assert (0);
}
}
template <typename T>
void
CircleXY<T>::impl_gradient (gradient_t& gradient,
const argument_t& argument,
size_type idFunction) const throw ()
{
switch (idFunction)
{
case 0:
gradient (0) = cos (argument[0]);
break;
case 1:
gradient (0) = -sin (argument[0]);
break;
default:
assert (0);
}
}
// Define ``f(x,y) = x * y'' function.
template <typename T>
struct Times : public GenericDifferentiableFunction<T>
{
ROBOPTIM_DIFFERENTIABLE_FUNCTION_FWD_TYPEDEFS_
(GenericDifferentiableFunction<T>);
Times () : GenericDifferentiableFunction<T> (2, 1)
{}
void impl_compute (result_t& result,
const vector_t& argument) const throw ()
{
result (0) = argument[0] * argument[1];
}
void impl_gradient (gradient_t& gradient,
const argument_t& argument,
size_type) const throw ();
};
template <>
void
Times<EigenMatrixSparse>::impl_gradient (gradient_t& gradient,
const argument_t& argument,
size_type) const throw ()
{
gradient.insert (0) = argument[1];
gradient.insert (1) = argument[0];
}
template <typename T>
void
Times<T>::impl_gradient (gradient_t& gradient,
const argument_t& argument,
size_type) const throw ()
{
gradient (0) = argument[1];
gradient (1) = argument[0];
}
template <typename T>
void displayGradient
(boost::shared_ptr<boost::test_tools::output_test_stream> output,
const GenericDifferentiableFunction<T>&,
const typename GenericDifferentiableFunction<T>::vector_t&,
typename GenericDifferentiableFunction<T>::size_type i = 0);
template <typename T>
void
displayGradient
(boost::shared_ptr<boost::test_tools::output_test_stream> output,
const GenericDifferentiableFunction<T>& function,
const typename GenericDifferentiableFunction<T>::vector_t& x,
typename GenericDifferentiableFunction<T>::size_type i)
{
GenericFiniteDifferenceGradient<T> fdfunction (function);
typename GenericFiniteDifferenceGradient<T>::gradient_t grad =
function.gradient (x, i);
typename GenericFiniteDifferenceGradient<T>::gradient_t fdgrad =
fdfunction.gradient (x, i);
(*output) << "#" << grad << std::endl
<< "#" << fdgrad << std::endl;
}
BOOST_FIXTURE_TEST_SUITE (core, TestSuiteConfiguration)
typedef boost::mpl::list< ::roboptim::EigenMatrixDense,
::roboptim::EigenMatrixSparse> functionTypes_t;
BOOST_AUTO_TEST_CASE_TEMPLATE (finite_difference_gradient, T, functionTypes_t)
{
boost::shared_ptr<boost::test_tools::output_test_stream>
output = retrievePattern ("finite-difference-gradient");
FGood<T> fg;
FBad<T> fb;
CircleXY<T> sq;
Times<T> times;
typename FGood<T>::vector_t x (1);
for (x[0] = -10.; x[0] < 10.; x[0] += 1.)
{
(*output) << "#Check gradient at x = " << x[0] << std::endl;
(*output) << "# Good" << std::endl;
displayGradient (output, fg, x);
(*output) << "# Bad" << std::endl;
displayGradient (output, fb, x);
(*output) << "# Circle" << std::endl;
displayGradient (output, sq, x);
displayGradient (output, sq, x, 1);
BOOST_CHECK (checkGradient (fg, 0, x));
BOOST_CHECK (! checkGradient (fb, 0, x));
BOOST_CHECK (checkGradient (sq, 0, x));
BOOST_CHECK (checkGradient (sq, 1, x));
}
x.resize (2);
for (x[1] = -10.; x[1] < 10.; x[1] += 1.)
for (x[0] = -10.; x[0] < 10.; x[0] += 1.)
{
(*output) << "# Times at x = " << x << std::endl;
displayGradient (output, times, x);
BOOST_CHECK (checkGradient (times, 0, x));
}
Gnuplot gnuplot = Gnuplot::make_interactive_gnuplot ();
GenericFiniteDifferenceGradient<
T,
finiteDifferenceGradientPolicies::Simple<T> > fg_fd (fg, 10.);
Polynomial<T> p;
GenericFiniteDifferenceGradient<
T,
finiteDifferenceGradientPolicies::Simple<T> >
p_fd (p, 10.);
Function::discreteInterval_t interval (-100., 100., 1.);
(*output)
<< (gnuplot
<< set ("multiplot layout 2,2")
<< plot (fg, interval)
<< plot (fg_fd, interval)
<< plot (p, interval)
<< plot (p_fd, interval)
<< unset ("multiplot")
);
std::cout << output->str () << std::endl;
}
BOOST_AUTO_TEST_SUITE_END ()