Matrix<double> DeJongFunction::getInverseHessian(Vector<double> argument) { Matrix<double> inverseHessian(numberOfVariables, numberOfVariables, 0.0); for(int i = 0; i < numberOfVariables; i++) { for(int j = 0; j < numberOfVariables; j++) { if(i == j) { inverseHessian[i][j] = 0.5; } else { inverseHessian[i][j] = 0.0; } } } return(inverseHessian); }
void BFGS::optimize() { Printer::getInstance().printStatusBegin("Optimizing (BFGS)..."); const size_t d = f.getNumberOfParameters(); xOpt.resize(0); fOpt = NAN; xHist.resize(0, d); fHist.resize(0); base::DataVector x(x0); float_t fx = NAN; base::DataVector gradFx(d); base::DataVector xNew(d); float_t fxNew; base::DataVector gradFxNew(d); base::DataVector delta(d); base::DataVector y(d); base::DataMatrix inverseHessian(d, d); base::DataMatrix inverseHessianNew(d, d); base::DataMatrix M(d, d); for (size_t i = 0; i < d; i++) { for (size_t j = 0; j < d; j++) { inverseHessian(i, j) = (i == j ? 1.0 : 0.0); } } size_t k = 0; float_t alpha = 1.0; base::DataVector dir(d); bool inDomain; size_t breakIterationCounter = 0; const size_t BREAK_ITERATION_COUNTER_MAX = 10; while (k < N) { // calculate gradient fx = fGradient.eval(x, gradFx); k++; if (k == 1) { xHist.appendRow(x); fHist.append(fx); } const float_t gradFxNorm = gradFx.l2Norm(); if (gradFxNorm == 0.0) { break; } for (size_t i = 0; i < d; i++) { dir[i] = 0.0; for (size_t j = 0; j < d; j++) { dir[i] -= inverseHessian(i, j) * gradFx[j]; } } if (dir.dotProduct(gradFx) > 0.0) { for (size_t t = 0; t < d; t++) { dir[t] = -gradFx[t] / gradFxNorm; } } inDomain = true; for (size_t t = 0; t < d; t++) { xNew[t] = x[t] + alpha * dir[t]; if ((xNew[t] < 0.0) || (xNew[t] > 1.0)) { inDomain = false; break; } } // evaluate at new point fxNew = (inDomain ? f.eval(xNew) : INFINITY); k++; // inner product of gradient and search direction const float_t gradFxTimesDir = gradFx.dotProduct(dir); // line search while (fxNew > fx + rhoLs * alpha * gradFxTimesDir) { alpha *= rhoAlphaMinus; inDomain = true; // recalculate new point for (size_t t = 0; t < d; t++) { xNew[t] = x[t] + alpha * dir[t]; if ((xNew[t] < 0.0) || (xNew[t] > 1.0)) { inDomain = false; break; } } // evaluate at new point fxNew = (inDomain ? fGradient.eval(xNew, gradFxNew) : INFINITY); k++; } for (size_t t = 0; t < d; t++) { delta[t] = alpha * dir[t]; y[t] = gradFxNew[t] - gradFx[t]; } // save new point x = xNew; fx = fxNew; gradFx = gradFxNew; xHist.appendRow(x); fHist.append(fx); // increase step size alpha *= rhoAlphaPlus; const float_t deltaTimesY = delta.dotProduct(y); if (deltaTimesY != 0.0) { for (size_t i = 0; i < d; i++) { for (size_t j = 0; j < d; j++) { M(i, j) = (i == j ? 1.0 : 0.0) - y[i] * delta[j] / deltaTimesY; } } for (size_t i = 0; i < d; i++) { for (size_t j = 0; j < d; j++) { float_t entry = delta[i] * delta[j] / deltaTimesY; for (size_t p = 0; p < d; p++) { for (size_t q = 0; q < d; q++) { entry += M(p, i) * inverseHessian(p, q) * M(q, j); } } inverseHessianNew(i, j) = entry; } } inverseHessian = inverseHessianNew; } // status printing Printer::getInstance().printStatusUpdate(std::to_string(k) + " evaluations, x = " + x.toString() + ", f(x) = " + std::to_string(fx)); // stopping criterion: // stop if delta is smaller than tolerance theta // in BREAK_ITERATION_COUNTER_MAX consecutive iterations if (delta.l2Norm() < theta) { breakIterationCounter++; if (breakIterationCounter >= BREAK_ITERATION_COUNTER_MAX) { break; } } else { breakIterationCounter = 0; } } xOpt.resize(d); xOpt = x; fOpt = fx; Printer::getInstance().printStatusEnd(); }
SolutionInfo Alignment::align(bool n) { // create initial solution SolutionInfo si; si.volume = -1000.0; si.iterations = 0; si.center1 = _refCenter; si.center2 = _dbCenter; si.rotation1 = _refRotMat; si.rotation2 = _dbRotMat; // scaling of the exclusion spheres double scale(1.0); if (_nbrExcl != 0) { scale /= _nbrExcl; } // try 4 different start orientations for (unsigned int _call(0); _call < 4; ++_call ) { // create initial rotation quaternion SiMath::Vector rotor(4,0.0); rotor[_call] = 1.0; double volume(0.0), oldVolume(-999.99), v(0.0); SiMath::Vector dG(4,0.0); // gradient update SiMath::Matrix hessian(4,4,0.0), dH(4,4,0.0); // hessian and hessian update unsigned int ii(0); for ( ; ii < 100; ++ii) { // compute gradient of volume _grad = 0.0; volume = 0.0; hessian = 0.0; for (unsigned int i(0); i < _refMap.size(); ++i) { // compute the volume overlap of the two pharmacophore points SiMath::Vector Aq(4,0.0); SiMath::Matrix * AkA = _AkA[i]; Aq[0] = (*AkA)[0][0] * rotor[0] + (*AkA)[0][1] * rotor[1] + (*AkA)[0][2] * rotor[2] + (*AkA)[0][3] * rotor[3]; Aq[1] = (*AkA)[1][0] * rotor[0] + (*AkA)[1][1] * rotor[1] + (*AkA)[1][2] * rotor[2] + (*AkA)[1][3] * rotor[3]; Aq[2] = (*AkA)[2][0] * rotor[0] + (*AkA)[2][1] * rotor[1] + (*AkA)[2][2] * rotor[2] + (*AkA)[2][3] * rotor[3]; Aq[3] = (*AkA)[3][0] * rotor[0] + (*AkA)[3][1] * rotor[1] + (*AkA)[3][2] * rotor[2] + (*AkA)[3][3] * rotor[3]; double qAq = Aq[0] * rotor[0] + Aq[1] * rotor[1] + Aq[2] * rotor[2] +Aq[3] * rotor[3]; v = GCI2 * pow(PI/(_refMap[i].alpha+_dbMap[i].alpha),1.5) * exp(-qAq); double c(1.0); // add normal if AROM-AROM // in this case the absolute value of the angle is needed if (n && (_refMap[i].func == AROM) && (_dbMap[i].func == AROM) && (_refMap[i].hasNormal) && (_dbMap[i].hasNormal)) { // for aromatic rings only the planar directions count // therefore the absolute value of the cosine is taken c = _normalContribution(_refMap[i].normal, _dbMap[i].normal, rotor); // update based on the sign of the cosine if (c < 0) { c *= -1.0; _dCdq *= -1.0; _d2Cdq2 *= -1.0; } for (unsigned int hi(0); hi < 4; hi++) { _grad[hi] += v * ( _dCdq[hi] - 2.0 * c * Aq[hi] ); for (unsigned int hj(0); hj < 4; hj++) { hessian[hi][hj] += v * (_d2Cdq2[hi][hj] - 2.0 * _dCdq[hi]*Aq[hj] + 2.0 * c * (2.0*Aq[hi]*Aq[hj] - (*AkA)[hi][hj])); } } v *= c; } else if (n && ((_refMap[i].func == HACC) || (_refMap[i].func == HDON) || (_refMap[i].func == HYBH)) && ((_dbMap[i].func == HYBH) || (_dbMap[i].func == HACC) || (_dbMap[i].func == HDON)) && (_refMap[i].hasNormal) && (_dbMap[i].hasNormal)) { // hydrogen donors and acceptor also have a direction // in this case opposite directions have negative impact c = _normalContribution(_refMap[i].normal, _dbMap[i].normal, rotor); for (unsigned int hi(0); hi < 4; hi++) { _grad[hi] += v * ( _dCdq[hi] - 2.0 * c * Aq[hi] ); for (unsigned int hj(0); hj < 4; hj++) { hessian[hi][hj] += v * (_d2Cdq2[hi][hj] - 2.0 * _dCdq[hi]*Aq[hj] + 2.0 * c * (2.0*Aq[hi]*Aq[hj] - (*AkA)[hi][hj])); } } v *= c; } else if (_refMap[i].func == EXCL) { // scale volume overlap of exclusion sphere with a negative scaling factor // => exclusion spheres have a negative impact v *= -scale; // update gradient and hessian directions for (unsigned int hi=0; hi < 4; hi++) { _grad[hi] -= 2.0 * v * Aq[hi]; for (unsigned int hj(0); hj < 4; hj++) { hessian[hi][hj] += 2.0 * v * (2.0*Aq[hi]*Aq[hj] - (*AkA)[hi][hj]); } } } else { // update gradient and hessian directions for (unsigned int hi(0); hi < 4; hi++) { _grad[hi] -= 2.0 * v * Aq[hi]; for (unsigned int hj(0); hj < 4; hj++) { hessian[hi][hj] += 2.0 * v * (2.0*Aq[hi]*Aq[hj] - (*AkA)[hi][hj]); } } } volume += v; } // stop iterations if the increase in volume overlap is too small (gradient ascent) // or if the volume is not defined if (std::isnan(volume) || (volume - oldVolume < 1e-5)) { break; } // reset old volume oldVolume = volume; inverseHessian(hessian); // update gradient based on inverse hessian _grad = rowProduct(hessian,_grad); // small scaling of the gradient _grad *= 0.9; // update rotor based on gradient information rotor += _grad; // normalise rotor such that it has unit norm normalise(rotor); } // save result in info structure if (oldVolume > si.volume) { si.rotor = rotor; si.volume = oldVolume; si.iterations = ii; } } return si; }
Matrix<double> ObjectiveFunction::getInverseHessian(Vector<double> argument) { Matrix<double> inverseHessian(numberOfVariables, numberOfVariables, 0.0); Matrix<double> hessian = getHessian(argument); double hessianDeterminant = getDeterminant(hessian); if(hessianDeterminant == 0.0) { std::cout << "Error: ObjectiveFunction class. " << "Matrix<double> getInverseHessian(Vector<double>) method." << std::endl << "Hessian matrix is singular." << std::endl << std::endl; exit(1); } // Get cofactor matrix Matrix<double> cofactor(numberOfVariables, numberOfVariables, 0.0); Matrix<double> c(numberOfVariables-1, numberOfVariables-1, 0.0); for(int j = 0; j < numberOfVariables; j++) { for (int i = 0; i < numberOfVariables; i++) { // Form the adjoint a_ij int i1 = 0; for(int ii = 0; ii < numberOfVariables; ii++) { if(ii == i) { continue; } int j1 = 0; for(int jj = 0; jj < numberOfVariables; jj++) { if (jj == j) { continue; } c[i1][j1] = hessian[ii][jj]; j1++; } i1++; } double determinant = getDeterminant(c); cofactor[i][j] = pow(-1.0, i+j+2.0)*determinant; } } // Adjoint matrix is the transpose of cofactor matrix Matrix<double> adjoint(numberOfVariables, numberOfVariables, 0.0); double temp = 0.0; for(int i = 0; i < numberOfVariables; i++) { for (int j = 0; j < numberOfVariables; j++) { adjoint[i][j] = cofactor[j][i]; } } // Inverse matrix is adjoint matrix divided by matrix determinant for(int i = 0; i < numberOfVariables; i++) { for(int j = 0; j < numberOfVariables; j++) { inverseHessian[i][j] = adjoint[i][j]/hessianDeterminant; } } return(inverseHessian); }