// $Id$ // // Copyright (C) 2004-2006 Rational Discovery LLC // // @@ All Rights Reserved @@ // This file is part of the RDKit. // The contents are covered by the terms of the BSD license // which is included in the file license.txt, found at the root // of the RDKit source tree. // #include #include #include #include #include "BFGSOpt.h" double circ_0_0(double *v) { double dx = v[0]; double dy = v[1]; return dx * dx + dy * dy; } double circ_0_0_grad(double *v, double *grad) { double dx = v[0]; double dy = v[1]; grad[0] = 2 * dx; grad[1] = 2 * dy; return 1.0; } double circ_1_0(double *v) { double dx = v[0] - 1; double dy = v[1]; return dx * dx + dy * dy; } double circ_1_0_grad(double *v, double *grad) { double dx = v[0] - 1; double dy = v[1]; grad[0] = 2 * dx; grad[1] = 2 * dy; return 1.0; } double func2(double *v) { double weight = .5; double dx = v[0] - 1; double dy = v[1]; double term1 = dx * dx - dy * dy; double term2 = dx * dx + dy * dy; return term1 * term1 + weight * term2; } double grad2(double *v, double *grad) { double weight = .5; double dx = v[0] - 1; double dy = v[1]; double term1 = dx * dx - dy * dy; grad[0] = 4 * dx * term1 + 2 * weight * dx; grad[1] = -4 * dy * term1 + 2 * weight * dy; return 1.0; } void test1() { std::cerr << "-------------------------------------" << std::endl; std::cerr << "Testing linear search." << std::endl; int dim = 2; double oLoc[2], oVal; double grad[2], dir[2]; double nLoc[2], nVal; int resCode; double (*func)(double *); double (*gradFunc)(double *, double *); func = circ_0_0; gradFunc = circ_0_0_grad; oLoc[0] = 0; oLoc[1] = 1.0; oVal = func(oLoc); TEST_ASSERT(fabs(oVal - 1.0) < 1e-4); gradFunc(oLoc, grad); dir[0] = 0; dir[1] = -.5; BFGSOpt::linearSearch(dim, oLoc, oVal, grad, dir, nLoc, nVal, func, 0.5, resCode); TEST_ASSERT(resCode == 0); TEST_ASSERT(fabs(nVal - 0.25) < 1e-4); TEST_ASSERT(fabs(nLoc[0]) < 1e-4); TEST_ASSERT(fabs(nLoc[1] - 0.5) < 1e-4); oLoc[0] = 1.0; oLoc[1] = 1.0; oVal = func(oLoc); TEST_ASSERT(fabs(oVal - 2.0) < 1e-4); gradFunc(oLoc, grad); dir[0] = -.5; dir[1] = -.5; BFGSOpt::linearSearch(dim, oLoc, oVal, grad, dir, nLoc, nVal, func, 1.0, resCode); TEST_ASSERT(resCode == 0); TEST_ASSERT(fabs(nVal - 0.5) < 1e-4); TEST_ASSERT(fabs(nLoc[0] - 0.5) < 1e-4); TEST_ASSERT(fabs(nLoc[1] - 0.5) < 1e-4); // we go hugely too far, but the dir gets cut in half, so we // immediately hit the minimum func = circ_0_0; oLoc[0] = 0; oLoc[1] = 1.0; oVal = func(oLoc); TEST_ASSERT(fabs(oVal - 1.0) < 1e-4); gradFunc(oLoc, grad); dir[0] = 0; dir[1] = -2; BFGSOpt::linearSearch(dim, oLoc, oVal, grad, dir, nLoc, nVal, func, 2.0, resCode); TEST_ASSERT(resCode == 0); TEST_ASSERT(fabs(nVal) < 1e-4); TEST_ASSERT(fabs(nLoc[0]) < 1e-4); TEST_ASSERT(fabs(nLoc[1]) < 1e-4); std::cerr << " done" << std::endl; } void test2() { std::cerr << "-------------------------------------" << std::endl; std::cerr << "Testing BFGS optimization." << std::endl; unsigned int dim = 2; double oLoc[2], oVal; double nVal; unsigned int nIters; double (*func)(double *); double (*gradFunc)(double *, double *); func = circ_0_0; gradFunc = circ_0_0_grad; oLoc[0] = 0; oLoc[1] = 1.0; oVal = func(oLoc); TEST_ASSERT(fabs(oVal - 1.0) < 1e-4); BFGSOpt::minimize(dim, oLoc, 1e-4, nIters, nVal, func, gradFunc); TEST_ASSERT(nIters = 1); TEST_ASSERT(fabs(nVal) < 1e-4); TEST_ASSERT(fabs(oLoc[0]) < 1e-4); TEST_ASSERT(fabs(oLoc[1]) < 1e-4); func = func2; gradFunc = grad2; oLoc[0] = 2.0; oLoc[1] = 0.5; BFGSOpt::minimize(dim, oLoc, 1e-4, nIters, nVal, func, gradFunc); // TEST_ASSERT(nIters=1); TEST_ASSERT(fabs(nVal) < 1e-4); TEST_ASSERT(fabs(oLoc[0] - 1) < 1e-3); TEST_ASSERT(fabs(oLoc[1]) < 1e-3); // up the tolerance: oLoc[0] = 2.0; oLoc[1] = 0.5; BFGSOpt::minimize(dim, oLoc, 1e-4, nIters, nVal, func, gradFunc, 1e-8); // TEST_ASSERT(nIters=1); TEST_ASSERT(fabs(nVal) < 1e-4); TEST_ASSERT(fabs(oLoc[0] - 1) < 1e-4); TEST_ASSERT(fabs(oLoc[1]) < 1e-4); std::cerr << " done" << std::endl; } int main() { test1(); test2(); }