#include <iostream>
#include <iomanip>
#include <cmath>
struct Point {
double x, y;
Point() : x(0.0), y(0.0) {}
Point( double xx, double yy ) : x(xx), y(yy) {}
friend Point operator+( const Point &a, const Point &b ) {
return Point(a.x + b.x, a.y + b.y);
}
// and so on...
};
struct Vector {
Vector() { x = y = 0.0; }
Vector( double xx, double yy ) : x(xx), y(yy) {}
Vector operator+( const Vector &v ) const {
return Vector(x + v.x, y + v.y);
}
Vector operator-() const {
return Vector(-x, -y);
}
Vector operator-( const Vector & v ) const {
return Vector(x - v.x, y - v.y);
}
Vector operator*( double s ) const {
return Vector(s*x, s*y);
}
// ... whatever...
double x, y;
};
Vector operator*( double s, const Vector &v ) {
return v * s;
}
Point operator+( const Point &a, const Vector &b ) {
return Point(a.x + b.x, a.y + b.y);
}
Point operator-( const Point &a, const Vector &b ) {
return Point(a.x - b.x, a.y - b.y);
}
// OP's function
struct MyFuncVec {
double operator()( const Point &p ) const {
return p.x * p.x * p.x + p.y * p.y * p.y - 2.0 * p.x * p.y;
}
};
struct MyFuncGradient {
Vector operator()( const Point &p ) {
return Vector(3.0 * p.x * p.x - 2.0 * p.y, 3.0 * p.y * p.y - 2.0 * p.x);
}
};
struct NumericGradient {
NumericGradient( double hh = 0.0001 ) : h(hh), inv_2h(1.0 / (2.0 * hh) ) {}
template < typename F >
Vector operator()( F func, Point &p ) {
double fx0 = func(p.x - h, p.y),
fx1 = func(p.x + h, p.y),
fy0 = func(p.x, p.y - h),
fy1 = func(p.x, p.y + h);
return Vector((fx1 - fx0) * inv_2h, (fy1 - fy0) * inv_2h);
}
private:
double h, inv_2h;
};
template < typename F >
double numeric_derivative( F func, double x ) {
static double h = 0.00001;
return (func(x + h) - func(x)) / h;
}
template < typename F >
void tabulate_function( F func, double a, double b, int steps ) {
// the functor ^^^^^^ is passed to the templated function
double step = (b - a) / (steps - 1);
std::cout << " x f(x)\n------------------------\n";
for ( int i = 0; i < steps; ++i ) {
double x = a + i * step,
fx = func(x);
// ^^^^^^^ call the operator() of the functor
std::cout << std::fixed << std::setw(8) << std::setprecision(3) << x
<< std::scientific << std::setw(16) << std::setprecision(5)
<< fx << '\n';
}
}
// monodimensional Optimization with golden ratio search
auto cmp_min = [] ( double f1, double f2 ) -> bool { return f1 < f2; };
auto cmp_max = [] ( double f1, double f2 ) -> bool { return f2 < f1; };
struct Optimum {
Optimum( double eps = 0.0, unsigned int max = 0 )
: epsilon(set_epsilon(eps)), max_iter(set_max_iter(max)) {}
double set_epsilon( double eps ) {
return epsilon = eps > 0.0 ? eps : default_epsilon;
}
int set_max_iter( int max ) {
return max_iter = max > 0 && max < max_acceptable_iter ?
max : default_max_iter;
}
template < typename F >
double find_min( F func, double a, double b ) {
return find_optimum(func, a, b, cmp_min);
}
template < typename F >
double find_max( F func, double a, double b ) {
return find_optimum(func, a, b, cmp_max);
}
template < typename F, typename P >
double find_optimum( F func, double a, double b, P cmp ) {
int k = 0;
double b_a = b - a,
x1 = a + one_tau * b_a, // computing x values
x2 = a + tau * b_a;
double f_x1 = func(x1), // computing values in x points
f_x2 = func(x2);
while ( fabs(b_a) > epsilon && k < max_iter ) {
++k;
if ( cmp(f_x1,f_x2) ) {
b = x2;
b_a = b - a;
x2 = x1;
x1 = a + one_tau * b_a;
f_x2 = f_x1;
f_x1 = func(x1);
}
else {
a = x1;
b_a = b - a;
x1 = x2;
x2 = a + tau * b_a;
f_x1 = f_x2;
f_x2 = func(x2);
}
}
// chooses minimum point
return cmp(f_x1,f_x2) ? x1 : x2;
}
private:
static constexpr double tau = (sqrt(5.0)-1.0)/2.0; // golden ratio
static constexpr double one_tau = 1.0 - tau;
static constexpr double default_epsilon = 0.00001;
static const int default_max_iter = 50;
static const int max_acceptable_iter = 500;
double epsilon; // accuracy value
int max_iter;
};
int main() {
MyFuncVec funcOP;
MyFuncGradient grad_funcOP;
Point p0(0.2, 0.8);
Vector g = grad_funcOP(p0);
// use a lambda to transform the OP function to 1D
auto sliced_func = [&funcOP, &p0, &g] ( double t ) -> double {
return funcOP(p0 - t * g);
};
std::cout << "Example of tabulated function:\n";
tabulate_function(sliced_func, 0, 0.5, 21);
std::cout << "\nLet's try to find a local minimum of OP's function:\n";
Optimum example;
double t;
Point p1;
std::cout << "\n p f(p) grad(p) t\n";
for ( int i = 0; i < 5; ++i ) {
t = example.find_min(sliced_func, 0.0, 1.0);
p1 = p0 - t * g;
std::cout << std::fixed << std::setw(8) << p1.x << std::setw(8)
<< p1.y << std::setw(12) << sliced_func(t) << std::setw(12)
<< g.x << std::setw(8) << g.y << std::setw(12) << t << '\n';
p0 = p1;
g = grad_funcOP(p1);
}
return 0;
}