/** * Main algorithm. Descriptions see "Practical Optimization" * * @param initX initial point of x, assuMing no value's on the bound! * @param constraints the bound constraints of each variable * constraints[0] is the lower bounds and * constraints[1] is the upper bounds * @return the solution of x, null if number of iterations not enough * @throws Exception if an error occurs */ public double[] findArgMin(double[] initX, double[,] constraints) { int l = initX.Length; // Initially all variables are free, all bounds are constraints of // non-working-set constraints bool[] isFixed = new bool[l]; double[,] nwsBounds = new double[2, l]; // Record indice of fixed variables, simply for efficiency DynamicIntArray wsBdsIndx = new DynamicIntArray(constraints.Length); // Vectors used to record the variable indices to be freed DynamicIntArray toFree = null, oldToFree = null; // Initial value of obj. function, gradient and inverse of the Hessian m_f = objectiveFunction(initX); double sum = 0; double[] grad = evaluateGradient(initX), oldGrad, oldX, deltaGrad = new double[l], deltaX = new double[l], direct = new double[l], x = new double[l]; Matrix L = new Matrix(l, l); // Lower triangle of Cholesky factor double[] D = new double[l]; // Diagonal of Cholesky factor for (int i = 0; i < l; i++) { L.setRow(i, new double[l]); L.setElement(i, i, 1.0); D[i] = 1.0; direct[i] = -grad[i]; sum += grad[i] * grad[i]; x[i] = initX[i]; nwsBounds[0, i] = constraints[0, i]; nwsBounds[1, i] = constraints[1, i]; isFixed[i] = false; } double stpMax = m_STPMX * Math.Max(Math.Sqrt(sum), l); iterates: for (int step = 0; step < m_MaxITS; step++) { // Try at most one feasible newton step, i.e. 0<lamda<=alpha oldX = x; oldGrad = grad; // Also update grad m_IsZeroStep = false; x = lnsrch(x, grad, direct, stpMax, isFixed, nwsBounds, wsBdsIndx); if (m_IsZeroStep) { // Zero step, simply delete rows/cols of D and L for (int f = 0; f < wsBdsIndx.msize(); f++) { int idx = wsBdsIndx.elementAt(f); L.setRow(idx, new double[l]); L.setColumn(idx, new double[l]); D[idx] = 0.0; } grad = evaluateGradient(x); step--; } else { // Check converge on x bool finish = false; double test = 0.0; for (int h = 0; h < l; h++) { deltaX[h] = x[h] - oldX[h]; double tmp = Math.Abs(deltaX[h]) / Math.Max(Math.Abs(x[h]), 1.0); if (tmp > test) test = tmp; } if (test < m_Zero) { finish = true; } // Check zero gradient grad = evaluateGradient(x); test = 0.0; double denom = 0.0, dxSq = 0.0, dgSq = 0.0, newlyBounded = 0.0; for (int g = 0; g < l; g++) { if (!isFixed[g]) { deltaGrad[g] = grad[g] - oldGrad[g]; // Calculate the denoMinators denom += deltaX[g] * deltaGrad[g]; dxSq += deltaX[g] * deltaX[g]; dgSq += deltaGrad[g] * deltaGrad[g]; } else // Only newly bounded variables will be non-zero newlyBounded += deltaX[g] * (grad[g] - oldGrad[g]); // Note: CANNOT use projected gradient for testing // convergence because of newly bounded variables double tmp = Math.Abs(grad[g]) * Math.Max(Math.Abs(direct[g]), 1.0) / Math.Max(Math.Abs(m_f), 1.0); if (tmp > test) test = tmp; } if (test < m_Zero) { finish = true; } // dg'*dx could be < 0 using inexact lnsrch // dg'*dx = 0 if (Math.Abs(denom + newlyBounded) < m_Zero) finish = true; int size = wsBdsIndx.msize(); bool isUpdate = true; // Whether to update BFGS formula // Converge: check whether release any current constraints if (finish) { if (toFree != null) oldToFree = (DynamicIntArray)toFree.copy(); toFree = new DynamicIntArray(wsBdsIndx.msize()); for (int m = size - 1; m >= 0; m--) { int index = wsBdsIndx.elementAt(m); double[] hessian = evaluateHessian(x, index); double deltaL = 0.0; if (hessian != null) { for (int mm = 0; mm < hessian.Length; mm++) if (!isFixed[mm]) // Free variable deltaL += hessian[mm] * direct[mm]; } // First and second order Lagrangian multiplier estimate // If user didn't provide Hessian, use first-order only double L1 = 0, L2 = 0; if (x[index] >= constraints[1, index]) // Upper bound { L1 = -grad[index]; } else { if (x[index] <= constraints[0, index])// Lower bound { L1 = grad[index]; } } // L2 = L1 + deltaL L2 = L1 + deltaL; //Check validity of Lagrangian multiplier estimate bool isConverge = (2.0 * Math.Abs(deltaL)) < Math.Min(Math.Abs(L1), Math.Abs(L2)); if ((L1 * L2 > 0.0) && isConverge) { //Same sign and converge: valid if (L2 < 0.0) {// Negative Lagrangian: feasible toFree.addElement(index); wsBdsIndx.removeElementAt(m); finish = false; // Not optimal, cannot finish } } // Although hardly happen, better check it // If the first-order Lagrangian multiplier estimate is wrong, // avoid zigzagging if ((hessian == null) && (toFree != null) && toFree.Equal(oldToFree)) finish = true; } if (finish) {// Min. found m_f = objectiveFunction(x); return x; } // Free some variables for (int mmm = 0; mmm < toFree.msize(); mmm++) { int freeIndx = toFree.elementAt(mmm); isFixed[freeIndx] = false; // Free this variable if (x[freeIndx] <= constraints[0, freeIndx]) {// Lower bound nwsBounds[0, freeIndx] = constraints[0, freeIndx]; } else { // Upper bound nwsBounds[1, freeIndx] = constraints[1, freeIndx]; } L.setElement(freeIndx, freeIndx, 1.0); D[freeIndx] = 1.0; isUpdate = false; } } if (denom < Math.Max(m_Zero * Math.Sqrt(dxSq) * Math.Sqrt(dgSq), m_Zero)) { isUpdate = false; // Do not update } // If Hessian will be positive definite, update it if (isUpdate) { // modify once: dg*dg'/(dg'*dx) double coeff = 1.0 / denom; // 1/(dg'*dx) updateCholeskyFactor(L, D, deltaGrad, coeff, isFixed); // modify twice: g*g'/(g'*p) coeff = 1.0 / m_Slope; // 1/(g'*p) updateCholeskyFactor(L, D, oldGrad, coeff, isFixed); } } // Find new direction Matrix LD = new Matrix(l, l); // L*D double[] b = new double[l]; for (int k = 0; k < l; k++) { if (!isFixed[k]) b[k] = -grad[k]; else b[k] = 0.0; for (int j = k; j < l; j++) { // Lower triangle if (!isFixed[j] && !isFixed[k]) LD.setElement(j, k, L.getElement(j, k) * D[k]); } } // Solve (LD)*y = -g, where y=L'*direct double[] LDIR = solveTriangle(LD, b, true, isFixed); LD = null; // Solve L'*direct = y direct = solveTriangle(L, LDIR, false, isFixed); //System.gc(); } m_X = x; return null; }
/** * Check whether the two integer vectors equal to each other * Two integer vectors are equal if all the elements are the * same, regardless of the order of the elements * * @param b another integer vector * @return whether they are equal */ public bool Equal(DynamicIntArray b) { if ((b == null) || (msize() != b.msize())) return false; int size = msize(); // Only values matter, order does not matter int[] sorta = m_Objects, sortb = b.m_Objects; for (int j = 0; j < size; j++) if (m_Objects[sorta[j]] != b.m_Objects[sortb[j]]) return false; return true; }