/************************************************************************* This function runs QPBLEIC solver; it returns after optimization process was completed. Following QP problem is solved: min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin)) subject to boundary constraints. INPUT PARAMETERS: AC - for dense problems (AKind=0), A-term of CQM object contains system matrix. Other terms are unspecified and should not be referenced. SparseAC - for sparse problems (AKind=1 AKind - sparse matrix format: * 0 for dense matrix * 1 for sparse matrix SparseUpper - which triangle of SparseAC stores matrix - upper or lower one (for dense matrices this parameter is not actual). AbsASum - SUM(|A[i,j]|) AbsASum2 - SUM(A[i,j]^2) BC - linear term, array[NC] BndLC - lower bound, array[NC] BndUC - upper bound, array[NC] SC - scale vector, array[NC]: * I-th element contains scale of I-th variable, * SC[I]>0 XOriginC - origin term, array[NC]. Can be zero. NC - number of variables in the original formulation (no slack variables). CLEICC - linear equality/inequality constraints. Present version of this function does NOT provide publicly available support for linear constraints. This feature will be introduced in the future versions of the function. NEC, NIC - number of equality/inequality constraints. MUST BE ZERO IN THE CURRENT VERSION!!! Settings - QPBLEICSettings object initialized by one of the initialization functions. SState - object which stores temporaries: * if uninitialized object was passed, FirstCall parameter MUST be set to True; object will be automatically initialized by the function, and FirstCall will be set to False. * if FirstCall=False, it is assumed that this parameter was already initialized by previous call to this function with same problem dimensions (variable count N). FirstCall - whether it is first call of this function for this specific instance of SState, with this number of variables N specified. XS - initial point, array[NC] OUTPUT PARAMETERS: XS - last point FirstCall - uncondtionally set to False TerminationType-termination type: * * * -- ALGLIB -- Copyright 14.05.2011 by Bochkanov Sergey *************************************************************************/ public static void qpbleicoptimize(cqmodels.convexquadraticmodel a, sparse.sparsematrix sparsea, int akind, bool sparseaupper, double absasum, double absasum2, double[] b, double[] bndl, double[] bndu, double[] s, double[] xorigin, int n, double[,] cleic, int nec, int nic, qpbleicsettings settings, qpbleicbuffers sstate, ref bool firstcall, ref double[] xs, ref int terminationtype) { int i = 0; double d2 = 0; double d1 = 0; double d0 = 0; double v = 0; double v0 = 0; double v1 = 0; double md = 0; double mx = 0; double mb = 0; int d1est = 0; int d2est = 0; int i_ = 0; terminationtype = 0; alglib.ap.assert(akind==0 || akind==1, "QPBLEICOptimize: unexpected AKind"); sstate.repinneriterationscount = 0; sstate.repouteriterationscount = 0; terminationtype = 0; // // Prepare solver object, if needed // if( firstcall ) { minbleic.minbleiccreate(n, xs, sstate.solver); firstcall = false; } // // Prepare max(|B|) // mb = 0.0; for(i=0; i<=n-1; i++) { mb = Math.Max(mb, Math.Abs(b[i])); } // // Temporaries // apserv.ivectorsetlengthatleast(ref sstate.tmpi, nec+nic); apserv.rvectorsetlengthatleast(ref sstate.tmp0, n); apserv.rvectorsetlengthatleast(ref sstate.tmp1, n); for(i=0; i<=nec-1; i++) { sstate.tmpi[i] = 0; } for(i=0; i<=nic-1; i++) { sstate.tmpi[nec+i] = -1; } minbleic.minbleicsetlc(sstate.solver, cleic, sstate.tmpi, nec+nic); minbleic.minbleicsetbc(sstate.solver, bndl, bndu); minbleic.minbleicsetdrep(sstate.solver, true); minbleic.minbleicsetcond(sstate.solver, math.minrealnumber, 0.0, 0.0, settings.maxits); minbleic.minbleicsetscale(sstate.solver, s); minbleic.minbleicsetprecscale(sstate.solver); minbleic.minbleicrestartfrom(sstate.solver, xs); while( minbleic.minbleiciteration(sstate.solver) ) { // // Line search started // if( sstate.solver.lsstart ) { // // Iteration counters: // * inner iterations count is increased on every line search // * outer iterations count is increased only at steepest descent line search // apserv.inc(ref sstate.repinneriterationscount); if( sstate.solver.steepestdescentstep ) { apserv.inc(ref sstate.repouteriterationscount); } // // Build quadratic model of F along descent direction: // // F(x+alpha*d) = D2*alpha^2 + D1*alpha + D0 // // Calculate estimates of linear and quadratic term // (term magnitude is compared with magnitude of numerical errors) // d0 = sstate.solver.f; d1 = 0.0; for(i_=0; i_<=n-1;i_++) { d1 += sstate.solver.d[i_]*sstate.solver.g[i_]; } d2 = 0; if( akind==0 ) { d2 = cqmodels.cqmxtadx2(a, sstate.solver.d); } if( akind==1 ) { sparse.sparsesmv(sparsea, sparseaupper, sstate.solver.d, ref sstate.tmp0); d2 = 0.0; for(i=0; i<=n-1; i++) { d2 = d2+sstate.solver.d[i]*sstate.tmp0[i]; } d2 = 0.5*d2; } mx = 0.0; md = 0.0; for(i=0; i<=n-1; i++) { mx = Math.Max(mx, Math.Abs(sstate.solver.x[i])); md = Math.Max(md, Math.Abs(sstate.solver.d[i])); } optserv.estimateparabolicmodel(absasum, absasum2, mx, mb, md, d1, d2, ref d1est, ref d2est); // // Tests for "normal" convergence. // // This line search may be started from steepest descent // stage (stage 2) or from L-BFGS stage (stage 3) of the // BLEIC algorithm. Depending on stage type, different // checks are performed. // // Say, L-BFGS stage is an equality-constrained refinement // stage of BLEIC. This stage refines current iterate // under "frozen" equality constraints. We can terminate // iterations at this stage only when we encounter // unconstrained direction of negative curvature. In all // other cases (say, when constrained gradient is zero) // we should not terminate algorithm because everything may // change after de-activating presently active constraints. // // Tests for convergence are performed only at "steepest descent" stage // of the BLEIC algorithm, and only when function is non-concave // (D2 is positive or approximately zero) along direction D. // // NOTE: we do not test iteration count (MaxIts) here, because // this stopping condition is tested by BLEIC itself. // if( sstate.solver.steepestdescentstep && d2est>=0 ) { if( d1est>=0 ) { // // "Emergency" stopping condition: D is non-descent direction. // Sometimes it is possible because of numerical noise in the // target function. // terminationtype = 4; for(i=0; i<=n-1; i++) { xs[i] = sstate.solver.x[i]; } break; } if( d2est>0 ) { // // Stopping condition #4 - gradient norm is small: // // 1. rescale State.Solver.D and State.Solver.G according to // current scaling, store results to Tmp0 and Tmp1. // 2. Normalize Tmp0 (scaled direction vector). // 3. compute directional derivative (in scaled variables), // which is equal to DOTPRODUCT(Tmp0,Tmp1). // v = 0; for(i=0; i<=n-1; i++) { sstate.tmp0[i] = sstate.solver.d[i]/s[i]; sstate.tmp1[i] = sstate.solver.g[i]*s[i]; v = v+math.sqr(sstate.tmp0[i]); } alglib.ap.assert((double)(v)>(double)(0), "QPBLEICOptimize: inernal errror (scaled direction is zero)"); v = 1/Math.Sqrt(v); for(i_=0; i_<=n-1;i_++) { sstate.tmp0[i_] = v*sstate.tmp0[i_]; } v = 0.0; for(i_=0; i_<=n-1;i_++) { v += sstate.tmp0[i_]*sstate.tmp1[i_]; } if( (double)(Math.Abs(v))<=(double)(settings.epsg) ) { terminationtype = 4; for(i=0; i<=n-1; i++) { xs[i] = sstate.solver.x[i]; } break; } // // Stopping condition #1 - relative function improvement is small: // // 1. calculate steepest descent step: V = -D1/(2*D2) // 2. calculate function change: V1= D2*V^2 + D1*V // 3. stop if function change is small enough // v = -(d1/(2*d2)); v1 = d2*v*v+d1*v; if( (double)(Math.Abs(v1))<=(double)(settings.epsf*Math.Max(d0, 1.0)) ) { terminationtype = 1; for(i=0; i<=n-1; i++) { xs[i] = sstate.solver.x[i]; } break; } // // Stopping condition #2 - scaled step is small: // // 1. calculate step multiplier V0 (step itself is D*V0) // 2. calculate scaled step length V // 3. stop if step is small enough // v0 = -(d1/(2*d2)); v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(v0*sstate.solver.d[i]/s[i]); } if( (double)(Math.Sqrt(v))<=(double)(settings.epsx) ) { terminationtype = 2; for(i=0; i<=n-1; i++) { xs[i] = sstate.solver.x[i]; } break; } } } // // Test for unconstrained direction of negative curvature // if( (d2est<0 || (d2est==0 && d1est<0)) && !sstate.solver.boundedstep ) { // // Function is unbounded from below: // * function will decrease along D, i.e. either: // * D2<0 // * D2=0 and D1<0 // * step is unconstrained // // If these conditions are true, we abnormally terminate QP // algorithm with return code -4 (we can do so at any stage // of BLEIC - whether it is L-BFGS or steepest descent one). // terminationtype = -4; for(i=0; i<=n-1; i++) { xs[i] = sstate.solver.x[i]; } break; } // // Suggest new step (only if D1 is negative far away from zero, // D2 is positive far away from zero). // if( d1est<0 && d2est>0 ) { sstate.solver.stp = apserv.safeminposrv(-d1, 2*d2, sstate.solver.curstpmax); } } // // Gradient evaluation // if( sstate.solver.needfg ) { for(i=0; i<=n-1; i++) { sstate.tmp0[i] = sstate.solver.x[i]-xorigin[i]; } if( akind==0 ) { cqmodels.cqmadx(a, sstate.tmp0, ref sstate.tmp1); } if( akind==1 ) { sparse.sparsesmv(sparsea, sparseaupper, sstate.tmp0, ref sstate.tmp1); } v0 = 0.0; for(i_=0; i_<=n-1;i_++) { v0 += sstate.tmp0[i_]*sstate.tmp1[i_]; } v1 = 0.0; for(i_=0; i_<=n-1;i_++) { v1 += sstate.tmp0[i_]*b[i_]; } sstate.solver.f = 0.5*v0+v1; for(i_=0; i_<=n-1;i_++) { sstate.solver.g[i_] = sstate.tmp1[i_]; } for(i_=0; i_<=n-1;i_++) { sstate.solver.g[i_] = sstate.solver.g[i_] + b[i_]; } } } if( terminationtype==0 ) { // // BLEIC optimizer was terminated by one of its inner stopping // conditions. Usually it is iteration counter (if such // stopping condition was specified by user). // minbleic.minbleicresultsbuf(sstate.solver, ref xs, sstate.solverrep); terminationtype = sstate.solverrep.terminationtype; } else { // // BLEIC optimizer was terminated in "emergency" mode by QP // solver. // // NOTE: such termination is "emergency" only when viewed from // BLEIC's position. QP solver sees such termination as // routine one, triggered by QP's stopping criteria. // minbleic.minbleicemergencytermination(sstate.solver); } }
public override alglib.apobject make_copy() { qpbleicbuffers _result = new qpbleicbuffers(); _result.solver = (minbleic.minbleicstate)solver.make_copy(); _result.solverrep = (minbleic.minbleicreport)solverrep.make_copy(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmp1 = (double[])tmp1.Clone(); _result.tmpi = (int[])tmpi.Clone(); _result.repinneriterationscount = repinneriterationscount; _result.repouteriterationscount = repouteriterationscount; return _result; }