public void testIntArray() { DynamicIntArray dia = new DynamicIntArray(10); for (int i = 0; i < 10000; ++i) { dia.add(2 * i); } Assert.Equal(10000, dia.size()); for (int i = 0; i < 10000; ++i) { Assert.Equal(2 * i, dia.get(i)); } dia.clear(); Assert.Equal(0, dia.size()); dia.add(3); dia.add(12); dia.add(65); Assert.Equal("{3,12,65}", dia.ToString()); for (int i = 0; i < 5; ++i) { dia.increment(i, 3); } Assert.Equal("{6,15,68,3,3}", dia.ToString()); }
/// <summary> Main algorithm. Descriptions see "Practical Optimization" /// /// </summary> /// <param name="initX">initial point of x, assuming no value's on the bound! /// </param> /// <param name="constraints">the bound constraints of each variable /// constraints[0] is the lower bounds and /// constraints[1] is the upper bounds /// </param> /// <returns> the solution of x, null if number of iterations not enough /// </returns> /// <exception cref="Exception">if an error occurs /// </exception> public virtual 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][]; for (int i = 0; i < 2; i++) { nwsBounds[i] = new double[l]; } // Record indice of fixed variables, simply for efficiency DynamicIntArray wsBdsIndx = new DynamicIntArray(this, 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); if (System.Double.IsNaN(m_f)) throw new System.Exception("Objective function value is NaN!"); 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.setXmlElement(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 * System.Math.Max(System.Math.Sqrt(sum), l); iterates: for (int step = 0; step < m_MAXITS; step++) { if (m_Debug) System.Console.Error.WriteLine("\nIteration # " + step + ":"); // Try at most one feasible newton step, i.e. 0<lamda<=alpha oldX = x; oldGrad = grad; // Also update grad if (m_Debug) System.Console.Error.WriteLine("Line search ... "); m_IsZeroStep = false; x = lnsrch(x, grad, direct, stpmax, isFixed, nwsBounds, wsBdsIndx); if (m_Debug) System.Console.Error.WriteLine("Line search finished."); if (m_IsZeroStep) { // Zero step, simply delete rows/cols of D and L for (int f = 0; f < wsBdsIndx.size(); 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 = System.Math.Abs(deltaX[h]) / System.Math.Max(System.Math.Abs(x[h]), 1.0); if (tmp > test) test = tmp; } if (test < m_Zero) { if (m_Debug) System.Console.Error.WriteLine("\nDeltaX converge: " + test); 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]; } // Only newly bounded variables will be non-zero else newlyBounded += deltaX[g] * (grad[g] - oldGrad[g]); // Note: CANNOT use projected gradient for testing // convergence because of newly bounded variables double tmp = System.Math.Abs(grad[g]) * System.Math.Max(System.Math.Abs(direct[g]), 1.0) / System.Math.Max(System.Math.Abs(m_f), 1.0); if (tmp > test) test = tmp; } if (test < m_Zero) { if (m_Debug) System.Console.Error.WriteLine("Gradient converge: " + test); finish = true; } // dg'*dx could be < 0 using inexact lnsrch if (m_Debug) System.Console.Error.WriteLine("dg'*dx=" + (denom + newlyBounded)); // dg'*dx = 0 if (System.Math.Abs(denom + newlyBounded) < m_Zero) finish = true; int size = wsBdsIndx.size(); bool isUpdate = true; // Whether to update BFGS formula // Converge: check whether release any current constraints if (finish) { if (m_Debug) System.Console.Error.WriteLine("Test any release possible ..."); if (toFree != null) oldToFree = (DynamicIntArray) toFree.copy(); toFree = new DynamicIntArray(this, wsBdsIndx.size()); 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, L2; if (x[index] >= constraints[1][index]) // Upper bound L1 = - grad[index]; else if (x[index] <= constraints[0][index]) // Lower bound L1 = grad[index]; else throw new System.Exception("x[" + index + "] not fixed on the" + " bounds where it should have been!"); // L2 = L1 + deltaL L2 = L1 + deltaL; if (m_Debug) System.Console.Error.WriteLine("Variable " + index + ": Lagrangian=" + L1 + "|" + L2); //Check validity of Lagrangian multiplier estimate bool isConverge = (2.0 * System.Math.Abs(deltaL)) < System.Math.Min(System.Math.Abs(L1), System.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 if (m_Debug) System.Console.Error.WriteLine("Minimum found."); m_f = objectiveFunction(x); if (System.Double.IsNaN(m_f)) throw new System.Exception("Objective function value is NaN!"); return x; } // Free some variables for (int mmm = 0; mmm < toFree.size(); 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]; if (m_Debug) System.Console.Error.WriteLine("Free variable " + freeIndx + " from bound " + nwsBounds[0][freeIndx]); } else { // Upper bound nwsBounds[1][freeIndx] = constraints[1][freeIndx]; if (m_Debug) System.Console.Error.WriteLine("Free variable " + freeIndx + " from bound " + nwsBounds[1][freeIndx]); } L.setXmlElement(freeIndx, freeIndx, 1.0); D[freeIndx] = 1.0; isUpdate = false; } } if (denom < System.Math.Max(m_Zero * System.Math.Sqrt(dxSq) * System.Math.Sqrt(dgSq), m_Zero)) { if (m_Debug) System.Console.Error.WriteLine("dg'*dx negative!"); 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.setXmlElement(j, k, L.getXmlElement(j, k) * D[k]); } } // Solve (LD)*y = -g, where y=L'*direct double[] LDIR = solveTriangle(LD, b, true, isFixed); LD = null; for (int m = 0; m < LDIR.Length; m++) { if (System.Double.IsNaN(LDIR[m])) throw new System.Exception("L*direct[" + m + "] is NaN!" + "|-g=" + b[m] + "|" + isFixed[m] + "|diag=" + D[m]); } // Solve L'*direct = y direct = solveTriangle(L, LDIR, false, isFixed); for (int m = 0; m < direct.Length; m++) { if (System.Double.IsNaN(direct[m])) throw new System.Exception("direct is NaN!"); } //System.gc(); } if (m_Debug) System.Console.Error.WriteLine("Cannot find minimum" + " -- too many interations!"); m_X = x; return null; }
/// <summary> 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 /// /// </summary> /// <param name="a">one integer vector /// </param> /// <param name="b">another integer vector /// </param> /// <returns> whether they are equal /// </returns> public bool equal(DynamicIntArray b) { if ((b == null) || (size() != b.size())) return false; int mysize = size(); // Only values matter, order does not matter int[] sorta = Utils.sort(m_Objects), sortb = Utils.sort(b.m_Objects); for (int j = 0; j < mysize; j++) if (m_Objects[sorta[j]] != b.m_Objects[sortb[j]]) return false; return true; }