Vector2<double> GaussianDistribution2D::rand() const { Matrix2x2<double> L; choleskyDecomposition(covariance, L, 1E-9*(covariance.trace())); Vector2<double> xRaw(randomGauss(), randomGauss()); return L * xRaw; }
/* ******************************************************************************** */ int getSearchDir(double *p, double *Grad, double **Hes, double delta, int numSS) { /* Computes the search direction using the dogleg method (Nocedal and Wright, page 71). Notation is consistent with that in this reference. Due to the construction of the problem, the minimization routine to find tau can be solved exactly by solving a quadratic. There can be precision issues with this, so this is checked. If the argument of the square root in the quadratic formula is negative, there must be a precision error, as such a situation is not possible in the construction of the problem. Returns: 1 if step was a pure Newton step (didn't hit trust region boundary) 2 if the step was purely Cauchy in nature (hit trust region boundary) 3 if the step was a dogleg step (part Newton and part Cauchy) 4 if Cholesky decomposition failed and we had to take a Cauchy step 5 if Cholesky decompostion failed but we would've taken Cauchy step anyways 6 if the dogleg calculation failed (should never happen) */ int i,j; // counters double a,b,c,sgnb; // Constants used in quadratic formula double q,x1,x2; // results from quadratic formula double tau; // Multipler in Newtonstep double *pB; // Unconstrained minimizer (the regular Newton step) double *pU; // Minimizer along steepest descent direction double pB2; // pj^2 double pU2; // pu^2 double pBpU; // pj . pc double *CholDiag; // Diagonal from Cholesky decomposition double **HesCopy; // Copy of the upper triangle of the Hessian (don't want to mess // with actual Hessian). int CholSuccess; // Whether of not Cholesky decomposition is successful double *HGrad; // Hessian dotted with the gradient double pUcoeff; // The coefficient on the Cauchy step double delta2; // delta^2 double mag1; // temporary variables for vector magnitudes double mag2; // Initialize pB2 just so compiler doesn't give a warning when optimization is on pB2 = 0.0; delta2 = pow(delta,2); /* ********** Compute the Newton step ************** */ // Memory allocation for computation of Newton step pB = (double *) malloc(numSS * sizeof(double)); CholDiag = (double *) malloc(numSS * sizeof(double)); HesCopy = (double **) malloc(numSS * sizeof(double *)); for (j = 0; j < numSS; j++) { HesCopy[j] = (double *) malloc(numSS * sizeof(double)); } // Make a copy of the Hessian because the Cholesky decomposition messes // with its entries. We only have to copy the upper diagonal. for (j = 0; j < numSS; j++) { for (i = j; i < numSS; i++) { HesCopy[i][j] = Hes[i][j]; } } CholSuccess = choleskyDecomposition(HesCopy,numSS); if (CholSuccess == 1) { choleskySolve(HesCopy,numSS,Grad,pB); // Free memory from Cholesky computation free(CholDiag); for (i = 0; i < numSS; i++) { free(HesCopy[i]); } free(HesCopy); // Newton step is -H^{-1} Grad for (i = 0; i < numSS; i++) { pB[i] *= -1.0; } // If Newton is in trust region, take it pB2 = dot(pB,pB,numSS); if (pB2 <= delta2) { for (i = 0; i < numSS; i++) { p[i] = pB[i]; } free(pB); return 1; // Signifies we took a pure Newton step } } else { // Free memory from failed Newton step computation free(CholDiag); for (i = 0; i < numSS; i++) { free(HesCopy[i]); } free(HesCopy); } /* ************************************************* */ /* ********** Compute the Cauchy step ************** */ // Allocate necessary arrays HGrad = (double *) malloc(numSS * sizeof(double)); pU = (double *) malloc(numSS * sizeof(double)); // The direction of the Cauchy step for (i = 0; i < numSS; i++) { pU[i] = -Grad[i]; } // prefactor for the Cauchy step MatrixVectorMult(HGrad,Hes,Grad,numSS); // Should this be sqrt too? mag1 = dot(Grad,Grad,numSS); mag2 = dot(Grad,HGrad,numSS); pUcoeff = mag1 / mag2; for (i = 0; i < numSS; i++) { pU[i] = pUcoeff * pU[i]; } free(HGrad); // Don't need this any more pU2 = dot(pU,pU,numSS); if (pU2 >= delta2) { // In this case we just take the Cauchy step, 0 < tau <= 1 tau = sqrt(delta2/pU2); for (i = 0; i < numSS; i++) { p[i] = tau*pU[i]; } free(pU); free(pB); if (CholSuccess != 1) { return 5; // Signifies Cholesky failure, but doesn't matter, would take Cauchy } // regardless else { return 2; // Signifies that we just took the Cauchy step } } if (CholSuccess != 1) { // We failed computing Newton step and have to take Cauchy for (i = 0; i < numSS; i++) { p[i] = pU[i]; } free(pU); free(pB); return 4; // Signifies Cholesky failure and we just took the Cauchy step } /* ************************************************* */ /* ************ Take the dogleg step *************** */ pBpU = dot(pB,pU,numSS); // Need this for dogleg calculation // Constants for quadratic formula for solving ||pU + (alpha)(pB-pU)||^2 = delta2 a = pB2 + pU2 - 2.0*pBpU; b = 2*(pBpU - pU2); c = pU2 - delta2; sgnb = 1; if(b < 0) { sgnb = -1; } q = -0.5 * (b + sgnb * sqrt(b*b - 4*a*c)); x1 = q / a; x2 = c / q; // x2 should be the positive root, x1 should be the negative root. if(x2 >= 0 && x2 <= 1.0) { for(i = 0; i < numSS; i++) { p[i] = pU[i] + x2 * (pB[i] - pU[i]); } free(pU); free(pB); return 3; // Signifies we took a dogleg step } else if(x1 >= 0 && x1 <= 1.0) { for(i = 0; i < numSS; i++) { p[i] = pU[i] + x1 * (pB[i] - pU[i]); } free(pU); free(pB); return 3; } else { for (i = 0; i < numSS; i++) { p[i] = pU[i]; } free(pU); free(pB); return 6; // Signifies no root satisfies the dogleg step and we took a Cauchy step } }
// Compute L (lower triangular) such that A = L L^T void choleskyDecomposition(BlockDiagonalMatrix &A, BlockDiagonalMatrix &L) { #pragma omp parallel for schedule(dynamic) for (unsigned int b = 0; b < A.blocks.size(); b++) choleskyDecomposition(A.blocks[b], L.blocks[b]); }