/************************************************************************* ALGLIB 3.15.0 (source code generated 2019-02-20) Copyright (c) Sergey Bochkanov (ALGLIB project). >>> SOURCE LICENSE >>> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation (www.fsf.org); either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GNU General Public License is available at http://www.fsf.org/licensing/licenses >>> END OF LICENSE >>> *************************************************************************/ #pragma warning disable 162 #pragma warning disable 164 #pragma warning disable 219 using System; public partial class alglib { } public partial class alglib { /************************************************************************* This structure is used to store OptGuard report, i.e. report on the properties of the nonlinear function being optimized with ALGLIB. After you tell your optimizer to activate OptGuard this technology starts to silently monitor function values and gradients/Jacobians being passed all around during your optimization session. Depending on specific set of checks enabled OptGuard may perform additional function evaluations (say, about 3*N evaluations if you want to check analytic gradient for errors). Upon discovering that something strange happens (function values and/or gradient components change too sharply and/or unexpectedly) OptGuard sets one of the "suspicion flags" (without interrupting optimization session). After optimization is done, you can examine OptGuard report. Following report fields can be set: * nonc0suspected * nonc1suspected * badgradsuspected === WHAT CAN BE DETECTED WITH OptGuard INTEGRITY CHECKER ================= Following types of errors in your target function (constraints) can be caught: a) discontinuous functions ("non-C0" part of the report) b) functions with discontinuous derivative ("non-C1" part of the report) c) errors in the analytic gradient provided by user These types of errors result in optimizer stopping well before reaching solution (most often - right after encountering discontinuity). Type A errors are usually coding errors during implementation of the target function. Most "normal" problems involve continuous functions, and anyway you can't reliably optimize discontinuous function. Type B errors are either coding errors or (in case code itself is correct) evidence of the fact that your problem is an "incorrect" one. Most optimizers (except for ones provided by MINNS subpackage) do not support nonsmooth problems. Type C errors are coding errors which often prevent optimizer from making even one step or result in optimizing stopping too early, as soon as actual descent direction becomes too different from one suggested by user- supplied gradient. === WHAT IS REPORTED ===================================================== Following set of report fields deals with discontinuous target functions, ones not belonging to C0 continuity class: * nonc0suspected - is a flag which is set upon discovering some indication of the discontinuity. If this flag is false, the rest of "non-C0" fields should be ignored * nonc0fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C0" * nonc0lipshitzc - a Lipchitz constant for a function which was suspected of being non-continuous. * nonc0test0positive - set to indicate specific test which detected continuity violation (test #0) Following set of report fields deals with discontinuous gradient/Jacobian, i.e. with functions violating C1 continuity: * nonc1suspected - is a flag which is set upon discovering some indication of the discontinuity. If this flag is false, the rest of "non-C1" fields should be ignored * nonc1fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * nonc1lipshitzc - a Lipchitz constant for a function gradient which was suspected of being non-smooth. * nonc1test0positive - set to indicate specific test which detected continuity violation (test #0) * nonc1test1positive - set to indicate specific test which detected continuity violation (test #1) Following set of report fields deals with errors in the gradient: * badgradsuspected - is a flad which is set upon discovering an error in the analytic gradient supplied by user * badgradfidx - index of the function with bad gradient (0 for target function, 1 or higher for nonlinear constraints) * badgradvidx - index of the variable * badgradxbase - location where Jacobian is tested * following matrices store user-supplied Jacobian and its numerical differentiation version (which is assumed to be free from the coding errors), both of them computed near the initial point: * badgraduser, an array[K,N], analytic Jacobian supplied by user * badgradnum, an array[K,N], numeric Jacobian computed by ALGLIB Here K is a total number of nonlinear functions (target + nonlinear constraints), N is a variable number. The element of badgraduser[] with index [badgradfidx,badgradvidx] is assumed to be wrong. More detailed error log can be obtained from optimizer by explicitly requesting reports for tests C0.0, C1.0, C1.1. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public class optguardreport : alglibobject { // // Public declarations // public bool nonc0suspected { get { return _innerobj.nonc0suspected; } set { _innerobj.nonc0suspected = value; } } public bool nonc0test0positive { get { return _innerobj.nonc0test0positive; } set { _innerobj.nonc0test0positive = value; } } public int nonc0fidx { get { return _innerobj.nonc0fidx; } set { _innerobj.nonc0fidx = value; } } public double nonc0lipschitzc { get { return _innerobj.nonc0lipschitzc; } set { _innerobj.nonc0lipschitzc = value; } } public bool nonc1suspected { get { return _innerobj.nonc1suspected; } set { _innerobj.nonc1suspected = value; } } public bool nonc1test0positive { get { return _innerobj.nonc1test0positive; } set { _innerobj.nonc1test0positive = value; } } public bool nonc1test1positive { get { return _innerobj.nonc1test1positive; } set { _innerobj.nonc1test1positive = value; } } public int nonc1fidx { get { return _innerobj.nonc1fidx; } set { _innerobj.nonc1fidx = value; } } public double nonc1lipschitzc { get { return _innerobj.nonc1lipschitzc; } set { _innerobj.nonc1lipschitzc = value; } } public bool badgradsuspected { get { return _innerobj.badgradsuspected; } set { _innerobj.badgradsuspected = value; } } public int badgradfidx { get { return _innerobj.badgradfidx; } set { _innerobj.badgradfidx = value; } } public int badgradvidx { get { return _innerobj.badgradvidx; } set { _innerobj.badgradvidx = value; } } public double[] badgradxbase { get { return _innerobj.badgradxbase; } set { _innerobj.badgradxbase = value; } } public double[,] badgraduser { get { return _innerobj.badgraduser; } set { _innerobj.badgraduser = value; } } public double[,] badgradnum { get { return _innerobj.badgradnum; } set { _innerobj.badgradnum = value; } } public optguardreport() { _innerobj = new optguardapi.optguardreport(); } public override alglib.alglibobject make_copy() { return new optguardreport((optguardapi.optguardreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private optguardapi.optguardreport _innerobj; public optguardapi.optguardreport innerobj { get { return _innerobj; } } public optguardreport(optguardapi.optguardreport obj) { _innerobj = obj; } } /************************************************************************* This structure is used for detailed reporting about suspected C1 continuity violation as flagged by C1 test #0 (OptGuard has several tests for C1 continuity, this report is used by #0). === WHAT IS TESTED ======================================================= C1 test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of directional derivative estimate. This test is less powerful than test #1, but it does not depend on gradient values and thus it is more robust against artifacts introduced by numerical differentiation. === WHAT IS REPORTED ===================================================== Actually, report retrieval function returns TWO report structures: * one for most suspicious point found so far (one with highest change in the directional derivative), so called "strongest" report * another one for most detailed line search (more function evaluations = easier to understand what's going on) which triggered test #0 criteria, so called "longest" report In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. You can plot function values stored in stp[] and f[] arrays and study behavior of your function by your own eyes, just to be sure that test correctly reported C1 violation. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public class optguardnonc1test0report : alglibobject { // // Public declarations // public bool positive { get { return _innerobj.positive; } set { _innerobj.positive = value; } } public int fidx { get { return _innerobj.fidx; } set { _innerobj.fidx = value; } } public double[] x0 { get { return _innerobj.x0; } set { _innerobj.x0 = value; } } public double[] d { get { return _innerobj.d; } set { _innerobj.d = value; } } public int n { get { return _innerobj.n; } set { _innerobj.n = value; } } public double[] stp { get { return _innerobj.stp; } set { _innerobj.stp = value; } } public double[] f { get { return _innerobj.f; } set { _innerobj.f = value; } } public int cnt { get { return _innerobj.cnt; } set { _innerobj.cnt = value; } } public int stpidxa { get { return _innerobj.stpidxa; } set { _innerobj.stpidxa = value; } } public int stpidxb { get { return _innerobj.stpidxb; } set { _innerobj.stpidxb = value; } } public optguardnonc1test0report() { _innerobj = new optguardapi.optguardnonc1test0report(); } public override alglib.alglibobject make_copy() { return new optguardnonc1test0report((optguardapi.optguardnonc1test0report)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private optguardapi.optguardnonc1test0report _innerobj; public optguardapi.optguardnonc1test0report innerobj { get { return _innerobj; } } public optguardnonc1test0report(optguardapi.optguardnonc1test0report obj) { _innerobj = obj; } } /************************************************************************* This structure is used for detailed reporting about suspected C1 continuity violation as flagged by C1 test #1 (OptGuard has several tests for C1 continuity, this report is used by #1). === WHAT IS TESTED ======================================================= C1 test #1 studies individual components of the gradient as recorded during line searches. Upon discovering discontinuity in the gradient this test records specific component which was suspected (or one with highest indication of discontinuity if multiple components are suspected). When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. === WHAT IS REPORTED ===================================================== Actually, report retrieval function returns TWO report structures: * one for most suspicious point found so far (one with highest change in the directional derivative), so called "strongest" report * another one for most detailed line search (more function evaluations = easier to understand what's going on) which triggered test #1 criteria, so called "longest" report In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. You can plot function values stored in stp[] and g[] arrays and study behavior of your function by your own eyes, just to be sure that test correctly reported C1 violation. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public class optguardnonc1test1report : alglibobject { // // Public declarations // public bool positive { get { return _innerobj.positive; } set { _innerobj.positive = value; } } public int fidx { get { return _innerobj.fidx; } set { _innerobj.fidx = value; } } public int vidx { get { return _innerobj.vidx; } set { _innerobj.vidx = value; } } public double[] x0 { get { return _innerobj.x0; } set { _innerobj.x0 = value; } } public double[] d { get { return _innerobj.d; } set { _innerobj.d = value; } } public int n { get { return _innerobj.n; } set { _innerobj.n = value; } } public double[] stp { get { return _innerobj.stp; } set { _innerobj.stp = value; } } public double[] g { get { return _innerobj.g; } set { _innerobj.g = value; } } public int cnt { get { return _innerobj.cnt; } set { _innerobj.cnt = value; } } public int stpidxa { get { return _innerobj.stpidxa; } set { _innerobj.stpidxa = value; } } public int stpidxb { get { return _innerobj.stpidxb; } set { _innerobj.stpidxb = value; } } public optguardnonc1test1report() { _innerobj = new optguardapi.optguardnonc1test1report(); } public override alglib.alglibobject make_copy() { return new optguardnonc1test1report((optguardapi.optguardnonc1test1report)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private optguardapi.optguardnonc1test1report _innerobj; public optguardapi.optguardnonc1test1report innerobj { get { return _innerobj; } } public optguardnonc1test1report(optguardapi.optguardnonc1test1report obj) { _innerobj = obj; } } } public partial class alglib { } public partial class alglib { } public partial class alglib { } public partial class alglib { } public partial class alglib { /************************************************************************* *************************************************************************/ public class minlbfgsstate : alglibobject { // // Public declarations // public bool needf { get { return _innerobj.needf; } set { _innerobj.needf = value; } } public bool needfg { get { return _innerobj.needfg; } set { _innerobj.needfg = value; } } public bool xupdated { get { return _innerobj.xupdated; } set { _innerobj.xupdated = value; } } public double f { get { return _innerobj.f; } set { _innerobj.f = value; } } public double[] g { get { return _innerobj.g; } } public double[] x { get { return _innerobj.x; } } public minlbfgsstate() { _innerobj = new minlbfgs.minlbfgsstate(); } public override alglib.alglibobject make_copy() { return new minlbfgsstate((minlbfgs.minlbfgsstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minlbfgs.minlbfgsstate _innerobj; public minlbfgs.minlbfgsstate innerobj { get { return _innerobj; } } public minlbfgsstate(minlbfgs.minlbfgsstate obj) { _innerobj = obj; } } /************************************************************************* This structure stores optimization report: * IterationsCount total number of inner iterations * NFEV number of gradient evaluations * TerminationType termination type (see below) TERMINATION CODES TerminationType field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. 1 relative function improvement is no more than EpsF. 2 relative step is no more than EpsX. 4 gradient norm is no more than EpsG 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 terminated by user who called minlbfgsrequesttermination(). X contains point which was "current accepted" when termination request was submitted. Other fields of this structure are not documented and should not be used! *************************************************************************/ public class minlbfgsreport : alglibobject { // // Public declarations // public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } public int nfev { get { return _innerobj.nfev; } set { _innerobj.nfev = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public minlbfgsreport() { _innerobj = new minlbfgs.minlbfgsreport(); } public override alglib.alglibobject make_copy() { return new minlbfgsreport((minlbfgs.minlbfgsreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minlbfgs.minlbfgsreport _innerobj; public minlbfgs.minlbfgsreport innerobj { get { return _innerobj; } } public minlbfgsreport(minlbfgs.minlbfgsreport obj) { _innerobj = obj; } } /************************************************************************* LIMITED MEMORY BFGS METHOD FOR LARGE SCALE OPTIMIZATION DESCRIPTION: The subroutine minimizes function F(x) of N arguments by using a quasi- Newton method (LBFGS scheme) which is optimized to use a minimum amount of memory. The subroutine generates the approximation of an inverse Hessian matrix by using information about the last M steps of the algorithm (instead of N). It lessens a required amount of memory from a value of order N^2 to a value of order 2*N*M. REQUIREMENTS: Algorithm will request following information during its operation: * function value F and its gradient G (simultaneously) at given point X USAGE: 1. User initializes algorithm state with MinLBFGSCreate() call 2. User tunes solver parameters with MinLBFGSSetCond() MinLBFGSSetStpMax() and other functions 3. User calls MinLBFGSOptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G. 4. User calls MinLBFGSResults() to get solution 5. Optionally user may call MinLBFGSRestartFrom() to solve another problem with same N/M but another starting point and/or another function. MinLBFGSRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension. N>0 M - number of corrections in the BFGS scheme of Hessian approximation update. Recommended value: 3<=M<=7. The smaller value causes worse convergence, the bigger will not cause a considerably better convergence, but will cause a fall in the performance. M<=N. X - initial solution approximation, array[0..N-1]. OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. you may tune stopping conditions with MinLBFGSSetCond() function 2. if target function contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow, use MinLBFGSSetStpMax() function to bound algorithm's steps. However, L-BFGS rarely needs such a tuning. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgscreate(int n, int m, double[] x, out minlbfgsstate state) { state = new minlbfgsstate(); minlbfgs.minlbfgscreate(n, m, x, state.innerobj, null); } public static void minlbfgscreate(int n, int m, double[] x, out minlbfgsstate state, alglib.xparams _params) { state = new minlbfgsstate(); minlbfgs.minlbfgscreate(n, m, x, state.innerobj, _params); } public static void minlbfgscreate(int m, double[] x, out minlbfgsstate state) { int n; state = new minlbfgsstate(); n = ap.len(x); minlbfgs.minlbfgscreate(n, m, x, state.innerobj, null); return; } public static void minlbfgscreate(int m, double[] x, out minlbfgsstate state, alglib.xparams _params) { int n; state = new minlbfgsstate(); n = ap.len(x); minlbfgs.minlbfgscreate(n, m, x, state.innerobj, _params); return; } /************************************************************************* The subroutine is finite difference variant of MinLBFGSCreate(). It uses finite differences in order to differentiate target function. Description below contains information which is specific to this function only. We recommend to read comments on MinLBFGSCreate() in order to get more information about creation of LBFGS optimizer. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X M - number of corrections in the BFGS scheme of Hessian approximation update. Recommended value: 3<=M<=7. The smaller value causes worse convergence, the bigger will not cause a considerably better convergence, but will cause a fall in the performance. M<=N. X - starting point, array[0..N-1]. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinLBFGSSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large truncation errors, while too small step will result in too large numerical errors. 1.0E-6 can be good value to start with. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. LBFGS needs exact gradient values. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 16.05.2011 by Bochkanov Sergey *************************************************************************/ public static void minlbfgscreatef(int n, int m, double[] x, double diffstep, out minlbfgsstate state) { state = new minlbfgsstate(); minlbfgs.minlbfgscreatef(n, m, x, diffstep, state.innerobj, null); } public static void minlbfgscreatef(int n, int m, double[] x, double diffstep, out minlbfgsstate state, alglib.xparams _params) { state = new minlbfgsstate(); minlbfgs.minlbfgscreatef(n, m, x, diffstep, state.innerobj, _params); } public static void minlbfgscreatef(int m, double[] x, double diffstep, out minlbfgsstate state) { int n; state = new minlbfgsstate(); n = ap.len(x); minlbfgs.minlbfgscreatef(n, m, x, diffstep, state.innerobj, null); return; } public static void minlbfgscreatef(int m, double[] x, double diffstep, out minlbfgsstate state, alglib.xparams _params) { int n; state = new minlbfgsstate(); n = ap.len(x); minlbfgs.minlbfgscreatef(n, m, x, diffstep, state.innerobj, _params); return; } /************************************************************************* This function sets stopping conditions for L-BFGS optimization algorithm. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if on k+1-th iteration the condition |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - ste pvector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinLBFGSSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (small EpsX). -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetcond(minlbfgsstate state, double epsg, double epsf, double epsx, int maxits) { minlbfgs.minlbfgssetcond(state.innerobj, epsg, epsf, epsx, maxits, null); } public static void minlbfgssetcond(minlbfgsstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { minlbfgs.minlbfgssetcond(state.innerobj, epsg, epsf, epsx, maxits, _params); } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinLBFGSOptimize(). -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetxrep(minlbfgsstate state, bool needxrep) { minlbfgs.minlbfgssetxrep(state.innerobj, needxrep, null); } public static void minlbfgssetxrep(minlbfgsstate state, bool needxrep, alglib.xparams _params) { minlbfgs.minlbfgssetxrep(state.innerobj, needxrep, _params); } /************************************************************************* This function sets maximum step length INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0 (default), if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetstpmax(minlbfgsstate state, double stpmax) { minlbfgs.minlbfgssetstpmax(state.innerobj, stpmax, null); } public static void minlbfgssetstpmax(minlbfgsstate state, double stpmax, alglib.xparams _params) { minlbfgs.minlbfgssetstpmax(state.innerobj, stpmax, _params); } /************************************************************************* This function sets scaling coefficients for LBFGS optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. In most optimizers (and in the LBFGS too) scaling is NOT a form of preconditioning. It just affects stopping conditions. You should set preconditioner by separate call to one of the MinLBFGSSetPrec...() functions. There is special preconditioning mode, however, which uses scaling coefficients to form diagonal preconditioning matrix. You can turn this mode on, if you want. But you should understand that scaling is not the same thing as preconditioning - these are two different, although related forms of tuning solver. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetscale(minlbfgsstate state, double[] s) { minlbfgs.minlbfgssetscale(state.innerobj, s, null); } public static void minlbfgssetscale(minlbfgsstate state, double[] s, alglib.xparams _params) { minlbfgs.minlbfgssetscale(state.innerobj, s, _params); } /************************************************************************* Modification of the preconditioner: default preconditioner (simple scaling, same for all elements of X) is used. INPUT PARAMETERS: State - structure which stores algorithm state NOTE: you can change preconditioner "on the fly", during algorithm iterations. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetprecdefault(minlbfgsstate state) { minlbfgs.minlbfgssetprecdefault(state.innerobj, null); } public static void minlbfgssetprecdefault(minlbfgsstate state, alglib.xparams _params) { minlbfgs.minlbfgssetprecdefault(state.innerobj, _params); } /************************************************************************* Modification of the preconditioner: Cholesky factorization of approximate Hessian is used. INPUT PARAMETERS: State - structure which stores algorithm state P - triangular preconditioner, Cholesky factorization of the approximate Hessian. array[0..N-1,0..N-1], (if larger, only leading N elements are used). IsUpper - whether upper or lower triangle of P is given (other triangle is not referenced) After call to this function preconditioner is changed to P (P is copied into the internal buffer). NOTE: you can change preconditioner "on the fly", during algorithm iterations. NOTE 2: P should be nonsingular. Exception will be thrown otherwise. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetpreccholesky(minlbfgsstate state, double[,] p, bool isupper) { minlbfgs.minlbfgssetpreccholesky(state.innerobj, p, isupper, null); } public static void minlbfgssetpreccholesky(minlbfgsstate state, double[,] p, bool isupper, alglib.xparams _params) { minlbfgs.minlbfgssetpreccholesky(state.innerobj, p, isupper, _params); } /************************************************************************* Modification of the preconditioner: diagonal of approximate Hessian is used. INPUT PARAMETERS: State - structure which stores algorithm state D - diagonal of the approximate Hessian, array[0..N-1], (if larger, only leading N elements are used). NOTE: you can change preconditioner "on the fly", during algorithm iterations. NOTE 2: D[i] should be positive. Exception will be thrown otherwise. NOTE 3: you should pass diagonal of approximate Hessian - NOT ITS INVERSE. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetprecdiag(minlbfgsstate state, double[] d) { minlbfgs.minlbfgssetprecdiag(state.innerobj, d, null); } public static void minlbfgssetprecdiag(minlbfgsstate state, double[] d, alglib.xparams _params) { minlbfgs.minlbfgssetprecdiag(state.innerobj, d, _params); } /************************************************************************* Modification of the preconditioner: scale-based diagonal preconditioning. This preconditioning mode can be useful when you don't have approximate diagonal of Hessian, but you know that your variables are badly scaled (for example, one variable is in [1,10], and another in [1000,100000]), and most part of the ill-conditioning comes from different scales of vars. In this case simple scale-based preconditioner, with H[i] = 1/(s[i]^2), can greatly improve convergence. IMPRTANT: you should set scale of your variables with MinLBFGSSetScale() call (before or after MinLBFGSSetPrecScale() call). Without knowledge of the scale of your variables scale-based preconditioner will be just unit matrix. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetprecscale(minlbfgsstate state) { minlbfgs.minlbfgssetprecscale(state.innerobj, null); } public static void minlbfgssetprecscale(minlbfgsstate state, alglib.xparams _params) { minlbfgs.minlbfgssetprecscale(state.innerobj, _params); } /************************************************************************* This function provides reverse communication interface Reverse communication interface is not documented or recommended to use. See below for functions which provide better documented API *************************************************************************/ public static bool minlbfgsiteration(minlbfgsstate state) { return minlbfgs.minlbfgsiteration(state.innerobj, null); } public static bool minlbfgsiteration(minlbfgsstate state, alglib.xparams _params) { return minlbfgs.minlbfgsiteration(state.innerobj, _params); } /************************************************************************* This family of functions is used to launcn iterations of nonlinear optimizer These functions accept following parameters: func - callback which calculates function (or merit function) value func at given point x grad - callback which calculates function (or merit function) value func and gradient grad at given point x rep - optional callback which is called after each iteration can be null obj - optional object which is passed to func/grad/hess/jac/rep can be null NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied gradient, and one which uses function value only and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object (either MinLBFGSCreate() for analytical gradient or MinLBFGSCreateF() for numerical differentiation) you should choose appropriate variant of MinLBFGSOptimize() - one which accepts function AND gradient or one which accepts function ONLY. Be careful to choose variant of MinLBFGSOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinLBFGSOptimize() and specific function used to create optimizer. | USER PASSED TO MinLBFGSOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinLBFGSCreateF() | work FAIL MinLBFGSCreate() | FAIL work Here "FAIL" denotes inappropriate combinations of optimizer creation function and MinLBFGSOptimize() version. Attemps to use such combination (for example, to create optimizer with MinLBFGSCreateF() and to pass gradient information to MinCGOptimize()) will lead to exception being thrown. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptimize(minlbfgsstate state, ndimensional_func func, ndimensional_rep rep, object obj) { minlbfgsoptimize(state, func, rep, obj, null); } public static void minlbfgsoptimize(minlbfgsstate state, ndimensional_func func, ndimensional_rep rep, object obj, alglib.xparams _params) { if( func==null ) throw new alglibexception("ALGLIB: error in 'minlbfgsoptimize()' (func is null)"); while( alglib.minlbfgsiteration(state, _params) ) { if( state.needf ) { func(state.x, ref state.innerobj.f, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minlbfgsoptimize' (some derivatives were not provided?)"); } } public static void minlbfgsoptimize(minlbfgsstate state, ndimensional_grad grad, ndimensional_rep rep, object obj) { minlbfgsoptimize(state, grad, rep, obj, null); } public static void minlbfgsoptimize(minlbfgsstate state, ndimensional_grad grad, ndimensional_rep rep, object obj, alglib.xparams _params) { if( grad==null ) throw new alglibexception("ALGLIB: error in 'minlbfgsoptimize()' (grad is null)"); while( alglib.minlbfgsiteration(state, _params) ) { if( state.needfg ) { grad(state.x, ref state.innerobj.f, state.innerobj.g, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minlbfgsoptimize' (some derivatives were not provided?)"); } } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with minlbfgsoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minlbfgssetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardgradient(minlbfgsstate state, double teststep) { minlbfgs.minlbfgsoptguardgradient(state.innerobj, teststep, null); } public static void minlbfgsoptguardgradient(minlbfgsstate state, double teststep, alglib.xparams _params) { minlbfgs.minlbfgsoptguardgradient(state.innerobj, teststep, _params); } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) b) nonsmooth target function (non-C1) Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardsmoothness(minlbfgsstate state, int level) { minlbfgs.minlbfgsoptguardsmoothness(state.innerobj, level, null); } public static void minlbfgsoptguardsmoothness(minlbfgsstate state, int level, alglib.xparams _params) { minlbfgs.minlbfgsoptguardsmoothness(state.innerobj, level, _params); } public static void minlbfgsoptguardsmoothness(minlbfgsstate state) { int level; level = 1; minlbfgs.minlbfgsoptguardsmoothness(state.innerobj, level, null); return; } public static void minlbfgsoptguardsmoothness(minlbfgsstate state, alglib.xparams _params) { int level; level = 1; minlbfgs.minlbfgsoptguardsmoothness(state.innerobj, level, _params); return; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * minlbfgsoptguardgradient() for gradient verification * minlbfgsoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradvidx for specific variable (gradient element) suspected * rep.badgradxbase, a point where gradient is tested * rep.badgraduser, user-provided gradient (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.badgradnum, reference gradient obtained via numerical differentiation (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.nonc0suspected * rep.nonc1suspected === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * minlbfgsoptguardnonc1test0results() * minlbfgsoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardresults(minlbfgsstate state, out optguardreport rep) { rep = new optguardreport(); minlbfgs.minlbfgsoptguardresults(state.innerobj, rep.innerobj, null); } public static void minlbfgsoptguardresults(minlbfgsstate state, out optguardreport rep, alglib.xparams _params) { rep = new optguardreport(); minlbfgs.minlbfgsoptguardresults(state.innerobj, rep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardnonc1test0results(minlbfgsstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); minlbfgs.minlbfgsoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void minlbfgsoptguardnonc1test0results(minlbfgsstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); minlbfgs.minlbfgsoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardnonc1test1results(minlbfgsstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); minlbfgs.minlbfgsoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void minlbfgsoptguardnonc1test1results(minlbfgsstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); minlbfgs.minlbfgsoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* L-BFGS algorithm results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report: * Rep.TerminationType completetion code: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -2 rounding errors prevent further improvement. X contains best point found. * -1 incorrect parameters were specified * 1 relative function improvement is no more than EpsF. * 2 relative step is no more than EpsX. * 4 gradient norm is no more than EpsG * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible * 8 terminated by user who called minlbfgsrequesttermination(). X contains point which was "current accepted" when termination request was submitted. * Rep.IterationsCount contains iterations count * NFEV countains number of function calculations -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsresults(minlbfgsstate state, out double[] x, out minlbfgsreport rep) { x = new double[0]; rep = new minlbfgsreport(); minlbfgs.minlbfgsresults(state.innerobj, ref x, rep.innerobj, null); } public static void minlbfgsresults(minlbfgsstate state, out double[] x, out minlbfgsreport rep, alglib.xparams _params) { x = new double[0]; rep = new minlbfgsreport(); minlbfgs.minlbfgsresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* L-BFGS algorithm results Buffered implementation of MinLBFGSResults which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 20.08.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsresultsbuf(minlbfgsstate state, ref double[] x, minlbfgsreport rep) { minlbfgs.minlbfgsresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void minlbfgsresultsbuf(minlbfgsstate state, ref double[] x, minlbfgsreport rep, alglib.xparams _params) { minlbfgs.minlbfgsresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* This subroutine restarts LBFGS algorithm from new point. All optimization parameters are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure used to store algorithm state X - new starting point. -- ALGLIB -- Copyright 30.07.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsrestartfrom(minlbfgsstate state, double[] x) { minlbfgs.minlbfgsrestartfrom(state.innerobj, x, null); } public static void minlbfgsrestartfrom(minlbfgsstate state, double[] x, alglib.xparams _params) { minlbfgs.minlbfgsrestartfrom(state.innerobj, x, _params); } /************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 08.10.2014 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsrequesttermination(minlbfgsstate state) { minlbfgs.minlbfgsrequesttermination(state.innerobj, null); } public static void minlbfgsrequesttermination(minlbfgsstate state, alglib.xparams _params) { minlbfgs.minlbfgsrequesttermination(state.innerobj, _params); } } public partial class alglib { } public partial class alglib { /************************************************************************* This object stores nonlinear optimizer state. You should use functions provided by MinBLEIC subpackage to work with this object *************************************************************************/ public class minbleicstate : alglibobject { // // Public declarations // public bool needf { get { return _innerobj.needf; } set { _innerobj.needf = value; } } public bool needfg { get { return _innerobj.needfg; } set { _innerobj.needfg = value; } } public bool xupdated { get { return _innerobj.xupdated; } set { _innerobj.xupdated = value; } } public double f { get { return _innerobj.f; } set { _innerobj.f = value; } } public double[] g { get { return _innerobj.g; } } public double[] x { get { return _innerobj.x; } } public minbleicstate() { _innerobj = new minbleic.minbleicstate(); } public override alglib.alglibobject make_copy() { return new minbleicstate((minbleic.minbleicstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minbleic.minbleicstate _innerobj; public minbleic.minbleicstate innerobj { get { return _innerobj; } } public minbleicstate(minbleic.minbleicstate obj) { _innerobj = obj; } } /************************************************************************* This structure stores optimization report: * IterationsCount number of iterations * NFEV number of gradient evaluations * TerminationType termination type (see below) TERMINATION CODES TerminationType field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. -3 inconsistent constraints. Feasible point is either nonexistent or too hard to find. Try to restart optimizer with better initial approximation 1 relative function improvement is no more than EpsF. 2 relative step is no more than EpsX. 4 gradient norm is no more than EpsG 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 terminated by user who called minbleicrequesttermination(). X contains point which was "current accepted" when termination request was submitted. ADDITIONAL FIELDS There are additional fields which can be used for debugging: * DebugEqErr error in the equality constraints (2-norm) * DebugFS f, calculated at projection of initial point to the feasible set * DebugFF f, calculated at the final point * DebugDX |X_start-X_final| *************************************************************************/ public class minbleicreport : alglibobject { // // Public declarations // public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } public int nfev { get { return _innerobj.nfev; } set { _innerobj.nfev = value; } } public int varidx { get { return _innerobj.varidx; } set { _innerobj.varidx = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public double debugeqerr { get { return _innerobj.debugeqerr; } set { _innerobj.debugeqerr = value; } } public double debugfs { get { return _innerobj.debugfs; } set { _innerobj.debugfs = value; } } public double debugff { get { return _innerobj.debugff; } set { _innerobj.debugff = value; } } public double debugdx { get { return _innerobj.debugdx; } set { _innerobj.debugdx = value; } } public int debugfeasqpits { get { return _innerobj.debugfeasqpits; } set { _innerobj.debugfeasqpits = value; } } public int debugfeasgpaits { get { return _innerobj.debugfeasgpaits; } set { _innerobj.debugfeasgpaits = value; } } public int inneriterationscount { get { return _innerobj.inneriterationscount; } set { _innerobj.inneriterationscount = value; } } public int outeriterationscount { get { return _innerobj.outeriterationscount; } set { _innerobj.outeriterationscount = value; } } public minbleicreport() { _innerobj = new minbleic.minbleicreport(); } public override alglib.alglibobject make_copy() { return new minbleicreport((minbleic.minbleicreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minbleic.minbleicreport _innerobj; public minbleic.minbleicreport innerobj { get { return _innerobj; } } public minbleicreport(minbleic.minbleicreport obj) { _innerobj = obj; } } /************************************************************************* BOUND CONSTRAINED OPTIMIZATION WITH ADDITIONAL LINEAR EQUALITY AND INEQUALITY CONSTRAINTS DESCRIPTION: The subroutine minimizes function F(x) of N arguments subject to any combination of: * bound constraints * linear inequality constraints * linear equality constraints REQUIREMENTS: * user must provide function value and gradient * starting point X0 must be feasible or not too far away from the feasible set * grad(f) must be Lipschitz continuous on a level set: L = { x : f(x)<=f(x0) } * function must be defined everywhere on the feasible set F USAGE: Constrained optimization if far more complex than the unconstrained one. Here we give very brief outline of the BLEIC optimizer. We strongly recommend you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide on optimization, which is available at http://www.alglib.net/optimization/ 1. User initializes algorithm state with MinBLEICCreate() call 2. USer adds boundary and/or linear constraints by calling MinBLEICSetBC() and MinBLEICSetLC() functions. 3. User sets stopping conditions with MinBLEICSetCond(). 4. User calls MinBLEICOptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G. 5. User calls MinBLEICResults() to get solution 6. Optionally user may call MinBLEICRestartFrom() to solve another problem with same N but another starting point. MinBLEICRestartFrom() allows to reuse already initialized structure. NOTE: if you have box-only constraints (no general linear constraints), then MinBC optimizer can be better option. It uses special, faster constraint activation method, which performs better on problems with multiple constraints active at the solution. On small-scale problems performance of MinBC is similar to that of MinBLEIC, but on large-scale ones (hundreds and thousands of active constraints) it can be several times faster than MinBLEIC. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size ofX X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. OUTPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleiccreate(int n, double[] x, out minbleicstate state) { state = new minbleicstate(); minbleic.minbleiccreate(n, x, state.innerobj, null); } public static void minbleiccreate(int n, double[] x, out minbleicstate state, alglib.xparams _params) { state = new minbleicstate(); minbleic.minbleiccreate(n, x, state.innerobj, _params); } public static void minbleiccreate(double[] x, out minbleicstate state) { int n; state = new minbleicstate(); n = ap.len(x); minbleic.minbleiccreate(n, x, state.innerobj, null); return; } public static void minbleiccreate(double[] x, out minbleicstate state, alglib.xparams _params) { int n; state = new minbleicstate(); n = ap.len(x); minbleic.minbleiccreate(n, x, state.innerobj, _params); return; } /************************************************************************* The subroutine is finite difference variant of MinBLEICCreate(). It uses finite differences in order to differentiate target function. Description below contains information which is specific to this function only. We recommend to read comments on MinBLEICCreate() in order to get more information about creation of BLEIC optimizer. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[0..N-1]. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinBLEICSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large truncation errors, while too small step will result in too large numerical errors. 1.0E-6 can be good value to start with. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. CG needs exact gradient values. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 16.05.2011 by Bochkanov Sergey *************************************************************************/ public static void minbleiccreatef(int n, double[] x, double diffstep, out minbleicstate state) { state = new minbleicstate(); minbleic.minbleiccreatef(n, x, diffstep, state.innerobj, null); } public static void minbleiccreatef(int n, double[] x, double diffstep, out minbleicstate state, alglib.xparams _params) { state = new minbleicstate(); minbleic.minbleiccreatef(n, x, diffstep, state.innerobj, _params); } public static void minbleiccreatef(double[] x, double diffstep, out minbleicstate state) { int n; state = new minbleicstate(); n = ap.len(x); minbleic.minbleiccreatef(n, x, diffstep, state.innerobj, null); return; } public static void minbleiccreatef(double[] x, double diffstep, out minbleicstate state, alglib.xparams _params) { int n; state = new minbleicstate(); n = ap.len(x); minbleic.minbleiccreatef(n, x, diffstep, state.innerobj, _params); return; } /************************************************************************* This function sets boundary constraints for BLEIC optimizer. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinBLEICRestartFrom(). NOTE: if you have box-only constraints (no general linear constraints), then MinBC optimizer can be better option. It uses special, faster constraint activation method, which performs better on problems with multiple constraints active at the solution. On small-scale problems performance of MinBC is similar to that of MinBLEIC, but on large-scale ones (hundreds and thousands of active constraints) it can be several times faster than MinBLEIC. INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: this solver has following useful properties: * bound constraints are always satisfied exactly * function is evaluated only INSIDE area specified by bound constraints, even when numerical differentiation is used (algorithm adjusts nodes according to boundary constraints) -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetbc(minbleicstate state, double[] bndl, double[] bndu) { minbleic.minbleicsetbc(state.innerobj, bndl, bndu, null); } public static void minbleicsetbc(minbleicstate state, double[] bndl, double[] bndu, alglib.xparams _params) { minbleic.minbleicsetbc(state.innerobj, bndl, bndu, _params); } /************************************************************************* This function sets linear constraints for BLEIC optimizer. Linear constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinBLEICRestartFrom(). INPUT PARAMETERS: State - structure previously allocated with MinBLEICCreate call. C - linear constraints, array[K,N+1]. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. CT - type of constraints, array[K]: * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT NOTE 1: linear (non-bound) constraints are satisfied only approximately: * there always exists some minor violation (about Epsilon in magnitude) due to rounding errors * numerical differentiation, if used, may lead to function evaluations outside of the feasible area, because algorithm does NOT change numerical differentiation formula according to linear constraints. If you want constraints to be satisfied exactly, try to reformulate your problem in such manner that all constraints will become boundary ones (this kind of constraints is always satisfied exactly, both in the final solution and in all intermediate points). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetlc(minbleicstate state, double[,] c, int[] ct, int k) { minbleic.minbleicsetlc(state.innerobj, c, ct, k, null); } public static void minbleicsetlc(minbleicstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { minbleic.minbleicsetlc(state.innerobj, c, ct, k, _params); } public static void minbleicsetlc(minbleicstate state, double[,] c, int[] ct) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minbleicsetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minbleic.minbleicsetlc(state.innerobj, c, ct, k, null); return; } public static void minbleicsetlc(minbleicstate state, double[,] c, int[] ct, alglib.xparams _params) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minbleicsetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minbleic.minbleicsetlc(state.innerobj, c, ct, k, _params); return; } /************************************************************************* This function sets stopping conditions for the optimizer. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if on k+1-th iteration the condition |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinBLEICSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection. NOTE: when SetCond() called with non-zero MaxIts, BLEIC solver may perform slightly more than MaxIts iterations. I.e., MaxIts sets non-strict limit on iterations count. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetcond(minbleicstate state, double epsg, double epsf, double epsx, int maxits) { minbleic.minbleicsetcond(state.innerobj, epsg, epsf, epsx, maxits, null); } public static void minbleicsetcond(minbleicstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { minbleic.minbleicsetcond(state.innerobj, epsg, epsf, epsx, maxits, _params); } /************************************************************************* This function sets scaling coefficients for BLEIC optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. In most optimizers (and in the BLEIC too) scaling is NOT a form of preconditioning. It just affects stopping conditions. You should set preconditioner by separate call to one of the MinBLEICSetPrec...() functions. There is a special preconditioning mode, however, which uses scaling coefficients to form diagonal preconditioning matrix. You can turn this mode on, if you want. But you should understand that scaling is not the same thing as preconditioning - these are two different, although related forms of tuning solver. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetscale(minbleicstate state, double[] s) { minbleic.minbleicsetscale(state.innerobj, s, null); } public static void minbleicsetscale(minbleicstate state, double[] s, alglib.xparams _params) { minbleic.minbleicsetscale(state.innerobj, s, _params); } /************************************************************************* Modification of the preconditioner: preconditioning is turned off. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetprecdefault(minbleicstate state) { minbleic.minbleicsetprecdefault(state.innerobj, null); } public static void minbleicsetprecdefault(minbleicstate state, alglib.xparams _params) { minbleic.minbleicsetprecdefault(state.innerobj, _params); } /************************************************************************* Modification of the preconditioner: diagonal of approximate Hessian is used. INPUT PARAMETERS: State - structure which stores algorithm state D - diagonal of the approximate Hessian, array[0..N-1], (if larger, only leading N elements are used). NOTE 1: D[i] should be positive. Exception will be thrown otherwise. NOTE 2: you should pass diagonal of approximate Hessian - NOT ITS INVERSE. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetprecdiag(minbleicstate state, double[] d) { minbleic.minbleicsetprecdiag(state.innerobj, d, null); } public static void minbleicsetprecdiag(minbleicstate state, double[] d, alglib.xparams _params) { minbleic.minbleicsetprecdiag(state.innerobj, d, _params); } /************************************************************************* Modification of the preconditioner: scale-based diagonal preconditioning. This preconditioning mode can be useful when you don't have approximate diagonal of Hessian, but you know that your variables are badly scaled (for example, one variable is in [1,10], and another in [1000,100000]), and most part of the ill-conditioning comes from different scales of vars. In this case simple scale-based preconditioner, with H[i] = 1/(s[i]^2), can greatly improve convergence. IMPRTANT: you should set scale of your variables with MinBLEICSetScale() call (before or after MinBLEICSetPrecScale() call). Without knowledge of the scale of your variables scale-based preconditioner will be just unit matrix. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetprecscale(minbleicstate state) { minbleic.minbleicsetprecscale(state.innerobj, null); } public static void minbleicsetprecscale(minbleicstate state, alglib.xparams _params) { minbleic.minbleicsetprecscale(state.innerobj, _params); } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinBLEICOptimize(). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetxrep(minbleicstate state, bool needxrep) { minbleic.minbleicsetxrep(state.innerobj, needxrep, null); } public static void minbleicsetxrep(minbleicstate state, bool needxrep, alglib.xparams _params) { minbleic.minbleicsetxrep(state.innerobj, needxrep, _params); } /************************************************************************* This function sets maximum step length IMPORTANT: this feature is hard to combine with preconditioning. You can't set upper limit on step length, when you solve optimization problem with linear (non-boundary) constraints AND preconditioner turned on. When non-boundary constraints are present, you have to either a) use preconditioner, or b) use upper limit on step length. YOU CAN'T USE BOTH! In this case algorithm will terminate with appropriate error code. INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which lead to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetstpmax(minbleicstate state, double stpmax) { minbleic.minbleicsetstpmax(state.innerobj, stpmax, null); } public static void minbleicsetstpmax(minbleicstate state, double stpmax, alglib.xparams _params) { minbleic.minbleicsetstpmax(state.innerobj, stpmax, _params); } /************************************************************************* This function provides reverse communication interface Reverse communication interface is not documented or recommended to use. See below for functions which provide better documented API *************************************************************************/ public static bool minbleiciteration(minbleicstate state) { return minbleic.minbleiciteration(state.innerobj, null); } public static bool minbleiciteration(minbleicstate state, alglib.xparams _params) { return minbleic.minbleiciteration(state.innerobj, _params); } /************************************************************************* This family of functions is used to launcn iterations of nonlinear optimizer These functions accept following parameters: func - callback which calculates function (or merit function) value func at given point x grad - callback which calculates function (or merit function) value func and gradient grad at given point x rep - optional callback which is called after each iteration can be null obj - optional object which is passed to func/grad/hess/jac/rep can be null NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied gradient, and one which uses function value only and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object (either MinBLEICCreate() for analytical gradient or MinBLEICCreateF() for numerical differentiation) you should choose appropriate variant of MinBLEICOptimize() - one which accepts function AND gradient or one which accepts function ONLY. Be careful to choose variant of MinBLEICOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinBLEICOptimize() and specific function used to create optimizer. | USER PASSED TO MinBLEICOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinBLEICCreateF() | work FAIL MinBLEICCreate() | FAIL work Here "FAIL" denotes inappropriate combinations of optimizer creation function and MinBLEICOptimize() version. Attemps to use such combination (for example, to create optimizer with MinBLEICCreateF() and to pass gradient information to MinBLEICOptimize()) will lead to exception being thrown. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptimize(minbleicstate state, ndimensional_func func, ndimensional_rep rep, object obj) { minbleicoptimize(state, func, rep, obj, null); } public static void minbleicoptimize(minbleicstate state, ndimensional_func func, ndimensional_rep rep, object obj, alglib.xparams _params) { if( func==null ) throw new alglibexception("ALGLIB: error in 'minbleicoptimize()' (func is null)"); while( alglib.minbleiciteration(state, _params) ) { if( state.needf ) { func(state.x, ref state.innerobj.f, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minbleicoptimize' (some derivatives were not provided?)"); } } public static void minbleicoptimize(minbleicstate state, ndimensional_grad grad, ndimensional_rep rep, object obj) { minbleicoptimize(state, grad, rep, obj, null); } public static void minbleicoptimize(minbleicstate state, ndimensional_grad grad, ndimensional_rep rep, object obj, alglib.xparams _params) { if( grad==null ) throw new alglibexception("ALGLIB: error in 'minbleicoptimize()' (grad is null)"); while( alglib.minbleiciteration(state, _params) ) { if( state.needfg ) { grad(state.x, ref state.innerobj.f, state.innerobj.g, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minbleicoptimize' (some derivatives were not provided?)"); } } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with minbleicoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minbleicsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardgradient(minbleicstate state, double teststep) { minbleic.minbleicoptguardgradient(state.innerobj, teststep, null); } public static void minbleicoptguardgradient(minbleicstate state, double teststep, alglib.xparams _params) { minbleic.minbleicoptguardgradient(state.innerobj, teststep, _params); } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) b) nonsmooth target function (non-C1) Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardsmoothness(minbleicstate state, int level) { minbleic.minbleicoptguardsmoothness(state.innerobj, level, null); } public static void minbleicoptguardsmoothness(minbleicstate state, int level, alglib.xparams _params) { minbleic.minbleicoptguardsmoothness(state.innerobj, level, _params); } public static void minbleicoptguardsmoothness(minbleicstate state) { int level; level = 1; minbleic.minbleicoptguardsmoothness(state.innerobj, level, null); return; } public static void minbleicoptguardsmoothness(minbleicstate state, alglib.xparams _params) { int level; level = 1; minbleic.minbleicoptguardsmoothness(state.innerobj, level, _params); return; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * minbleicoptguardgradient() for gradient verification * minbleicoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradvidx for specific variable (gradient element) suspected * rep.badgradxbase, a point where gradient is tested * rep.badgraduser, user-provided gradient (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.badgradnum, reference gradient obtained via numerical differentiation (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.nonc0suspected * rep.nonc1suspected === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * minbleicoptguardnonc1test0results() * minbleicoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardresults(minbleicstate state, out optguardreport rep) { rep = new optguardreport(); minbleic.minbleicoptguardresults(state.innerobj, rep.innerobj, null); } public static void minbleicoptguardresults(minbleicstate state, out optguardreport rep, alglib.xparams _params) { rep = new optguardreport(); minbleic.minbleicoptguardresults(state.innerobj, rep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardnonc1test0results(minbleicstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); minbleic.minbleicoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void minbleicoptguardnonc1test0results(minbleicstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); minbleic.minbleicoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardnonc1test1results(minbleicstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); minbleic.minbleicoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void minbleicoptguardnonc1test1results(minbleicstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); minbleic.minbleicoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* BLEIC results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report. You should check Rep.TerminationType in order to distinguish successful termination from unsuccessful one: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -3 inconsistent constraints. Feasible point is either nonexistent or too hard to find. Try to restart optimizer with better initial approximation * 1 relative function improvement is no more than EpsF. * 2 scaled step is no more than EpsX. * 4 scaled gradient norm is no more than EpsG. * 5 MaxIts steps was taken * 8 terminated by user who called minbleicrequesttermination(). X contains point which was "current accepted" when termination request was submitted. More information about fields of this structure can be found in the comments on MinBLEICReport datatype. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicresults(minbleicstate state, out double[] x, out minbleicreport rep) { x = new double[0]; rep = new minbleicreport(); minbleic.minbleicresults(state.innerobj, ref x, rep.innerobj, null); } public static void minbleicresults(minbleicstate state, out double[] x, out minbleicreport rep, alglib.xparams _params) { x = new double[0]; rep = new minbleicreport(); minbleic.minbleicresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* BLEIC results Buffered implementation of MinBLEICResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicresultsbuf(minbleicstate state, ref double[] x, minbleicreport rep) { minbleic.minbleicresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void minbleicresultsbuf(minbleicstate state, ref double[] x, minbleicreport rep, alglib.xparams _params) { minbleic.minbleicresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* This subroutine restarts algorithm from new point. All optimization parameters (including constraints) are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure previously allocated with MinBLEICCreate call. X - new starting point. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicrestartfrom(minbleicstate state, double[] x) { minbleic.minbleicrestartfrom(state.innerobj, x, null); } public static void minbleicrestartfrom(minbleicstate state, double[] x, alglib.xparams _params) { minbleic.minbleicrestartfrom(state.innerobj, x, _params); } /************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 08.10.2014 by Bochkanov Sergey *************************************************************************/ public static void minbleicrequesttermination(minbleicstate state) { minbleic.minbleicrequesttermination(state.innerobj, null); } public static void minbleicrequesttermination(minbleicstate state, alglib.xparams _params) { minbleic.minbleicrequesttermination(state.innerobj, _params); } } public partial class alglib { } public partial class alglib { /************************************************************************* This object stores nonlinear optimizer state. You should use functions provided by MinQP subpackage to work with this object *************************************************************************/ public class minqpstate : alglibobject { // // Public declarations // public minqpstate() { _innerobj = new minqp.minqpstate(); } public override alglib.alglibobject make_copy() { return new minqpstate((minqp.minqpstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minqp.minqpstate _innerobj; public minqp.minqpstate innerobj { get { return _innerobj; } } public minqpstate(minqp.minqpstate obj) { _innerobj = obj; } } /************************************************************************* This structure stores optimization report: * InnerIterationsCount number of inner iterations * OuterIterationsCount number of outer iterations * NCholesky number of Cholesky decomposition * NMV number of matrix-vector products (only products calculated as part of iterative process are counted) * TerminationType completion code (see below) Completion codes: * -9 failure of the automatic scale evaluation: one of the diagonal elements of the quadratic term is non-positive. Specify variable scales manually! * -5 inappropriate solver was used: * QuickQP solver for problem with general linear constraints (dense/sparse) * -4 BLEIC-QP or QuickQP solver found unconstrained direction of negative curvature (function is unbounded from below even under constraints), no meaningful minimum can be found. * -3 inconsistent constraints (or, maybe, feasible point is too hard to find). If you are sure that constraints are feasible, try to restart optimizer with better initial approximation. * -1 solver error * 1..4 successful completion * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. *************************************************************************/ public class minqpreport : alglibobject { // // Public declarations // public int inneriterationscount { get { return _innerobj.inneriterationscount; } set { _innerobj.inneriterationscount = value; } } public int outeriterationscount { get { return _innerobj.outeriterationscount; } set { _innerobj.outeriterationscount = value; } } public int nmv { get { return _innerobj.nmv; } set { _innerobj.nmv = value; } } public int ncholesky { get { return _innerobj.ncholesky; } set { _innerobj.ncholesky = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public minqpreport() { _innerobj = new minqp.minqpreport(); } public override alglib.alglibobject make_copy() { return new minqpreport((minqp.minqpreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minqp.minqpreport _innerobj; public minqp.minqpreport innerobj { get { return _innerobj; } } public minqpreport(minqp.minqpreport obj) { _innerobj = obj; } } /************************************************************************* CONSTRAINED QUADRATIC PROGRAMMING The subroutine creates QP optimizer. After initial creation, it contains default optimization problem with zero quadratic and linear terms and no constraints. You should set quadratic/linear terms with calls to functions provided by MinQP subpackage. You should also choose appropriate QP solver and set it and its stopping criteria by means of MinQPSetAlgo??????() function. Then, you should start solution process by means of MinQPOptimize() call. Solution itself can be obtained with MinQPResults() function. Following solvers are recommended: * QuickQP for dense problems with box-only constraints (or no constraints at all) * QP-BLEIC for dense/sparse problems with moderate (up to 50) number of general linear constraints * DENSE-AUL-QP for dense problems with any (small or large) number of general linear constraints INPUT PARAMETERS: N - problem size OUTPUT PARAMETERS: State - optimizer with zero quadratic/linear terms and no constraints -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpcreate(int n, out minqpstate state) { state = new minqpstate(); minqp.minqpcreate(n, state.innerobj, null); } public static void minqpcreate(int n, out minqpstate state, alglib.xparams _params) { state = new minqpstate(); minqp.minqpcreate(n, state.innerobj, _params); } /************************************************************************* This function sets linear term for QP solver. By default, linear term is zero. INPUT PARAMETERS: State - structure which stores algorithm state B - linear term, array[N]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlinearterm(minqpstate state, double[] b) { minqp.minqpsetlinearterm(state.innerobj, b, null); } public static void minqpsetlinearterm(minqpstate state, double[] b, alglib.xparams _params) { minqp.minqpsetlinearterm(state.innerobj, b, _params); } /************************************************************************* This function sets dense quadratic term for QP solver. By default, quadratic term is zero. SUPPORT BY QP SOLVERS: Dense quadratic term can be handled by following QP solvers: * QuickQP * BLEIC-QP * Dense-AUL-QP IMPORTANT: This solver minimizes following function: f(x) = 0.5*x'*A*x + b'*x. Note that quadratic term has 0.5 before it. So if you want to minimize f(x) = x^2 + x you should rewrite your problem as follows: f(x) = 0.5*(2*x^2) + x and your matrix A will be equal to [[2.0]], not to [[1.0]] INPUT PARAMETERS: State - structure which stores algorithm state A - matrix, array[N,N] IsUpper - (optional) storage type: * if True, symmetric matrix A is given by its upper triangle, and the lower triangle isn't used * if False, symmetric matrix A is given by its lower triangle, and the upper triangle isn't used * if not given, both lower and upper triangles must be filled. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetquadraticterm(minqpstate state, double[,] a, bool isupper) { minqp.minqpsetquadraticterm(state.innerobj, a, isupper, null); } public static void minqpsetquadraticterm(minqpstate state, double[,] a, bool isupper, alglib.xparams _params) { minqp.minqpsetquadraticterm(state.innerobj, a, isupper, _params); } public static void minqpsetquadraticterm(minqpstate state, double[,] a) { bool isupper; if( !alglib.ap.issymmetric(a) ) throw new alglibexception("'a' parameter is not symmetric matrix"); isupper = false; minqp.minqpsetquadraticterm(state.innerobj, a, isupper, null); return; } public static void minqpsetquadraticterm(minqpstate state, double[,] a, alglib.xparams _params) { bool isupper; if( !alglib.ap.issymmetric(a) ) throw new alglibexception("'a' parameter is not symmetric matrix"); isupper = false; minqp.minqpsetquadraticterm(state.innerobj, a, isupper, _params); return; } /************************************************************************* This function sets sparse quadratic term for QP solver. By default, quadratic term is zero. This function overrides previous calls to minqpsetquadraticterm() or minqpsetquadratictermsparse(). SUPPORT BY QP SOLVERS: Sparse quadratic term can be handled by following QP solvers: * QuickQP * BLEIC-QP * Dense-AUL-QP (internally converts sparse matrix to dense format) IMPORTANT: This solver minimizes following function: f(x) = 0.5*x'*A*x + b'*x. Note that quadratic term has 0.5 before it. So if you want to minimize f(x) = x^2 + x you should rewrite your problem as follows: f(x) = 0.5*(2*x^2) + x and your matrix A will be equal to [[2.0]], not to [[1.0]] INPUT PARAMETERS: State - structure which stores algorithm state A - matrix, array[N,N] IsUpper - (optional) storage type: * if True, symmetric matrix A is given by its upper triangle, and the lower triangle isn't used * if False, symmetric matrix A is given by its lower triangle, and the upper triangle isn't used * if not given, both lower and upper triangles must be filled. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetquadratictermsparse(minqpstate state, sparsematrix a, bool isupper) { minqp.minqpsetquadratictermsparse(state.innerobj, a.innerobj, isupper, null); } public static void minqpsetquadratictermsparse(minqpstate state, sparsematrix a, bool isupper, alglib.xparams _params) { minqp.minqpsetquadratictermsparse(state.innerobj, a.innerobj, isupper, _params); } /************************************************************************* This function sets starting point for QP solver. It is useful to have good initial approximation to the solution, because it will increase speed of convergence and identification of active constraints. INPUT PARAMETERS: State - structure which stores algorithm state X - starting point, array[N]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetstartingpoint(minqpstate state, double[] x) { minqp.minqpsetstartingpoint(state.innerobj, x, null); } public static void minqpsetstartingpoint(minqpstate state, double[] x, alglib.xparams _params) { minqp.minqpsetstartingpoint(state.innerobj, x, _params); } /************************************************************************* This function sets origin for QP solver. By default, following QP program is solved: min(0.5*x'*A*x+b'*x) This function allows to solve different problem: min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin)) Specification of non-zero origin affects function being minimized, but not constraints. Box and linear constraints are still calculated without origin. INPUT PARAMETERS: State - structure which stores algorithm state XOrigin - origin, array[N]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetorigin(minqpstate state, double[] xorigin) { minqp.minqpsetorigin(state.innerobj, xorigin, null); } public static void minqpsetorigin(minqpstate state, double[] xorigin, alglib.xparams _params) { minqp.minqpsetorigin(state.innerobj, xorigin, _params); } /************************************************************************* This function sets scaling coefficients. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances) and as preconditioner. Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function If you do not know how to choose scales of your variables, you can: * read www.alglib.net/optimization/scaling.php article * use minqpsetscaleautodiag(), which calculates scale using diagonal of the quadratic term: S is set to 1/sqrt(diag(A)), which works well sometimes. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetscale(minqpstate state, double[] s) { minqp.minqpsetscale(state.innerobj, s, null); } public static void minqpsetscale(minqpstate state, double[] s, alglib.xparams _params) { minqp.minqpsetscale(state.innerobj, s, _params); } /************************************************************************* This function sets automatic evaluation of variable scaling. IMPORTANT: this function works only for matrices with positive diagonal elements! Zero or negative elements will result in -9 error code being returned. Specify scale vector manually with minqpsetscale() in such cases. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances) and as preconditioner. The best way to set scaling is to manually specify variable scales. However, sometimes you just need quick-and-dirty solution - either when you perform fast prototyping, or when you know your problem well and you are 100% sure that this quick solution is robust enough in your case. One such solution is to evaluate scale of I-th variable as 1/Sqrt(A[i,i]), where A[i,i] is an I-th diagonal element of the quadratic term. Such approach works well sometimes, but you have to be careful here. INPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 26.12.2017 by Bochkanov Sergey *************************************************************************/ public static void minqpsetscaleautodiag(minqpstate state) { minqp.minqpsetscaleautodiag(state.innerobj, null); } public static void minqpsetscaleautodiag(minqpstate state, alglib.xparams _params) { minqp.minqpsetscaleautodiag(state.innerobj, _params); } /************************************************************************* This function tells solver to use BLEIC-based algorithm and sets stopping criteria for the algorithm. This algorithm is fast enough for large-scale problems with following properties: a) feasible initial point, moderate amount of general linear constraints b) arbitrary (can be infeasible) initial point, small amount of general linear constraints (say, hundred or less) If you solve large-scale QP problem with many inequality constraints and without initial feasibility guarantees, consider using DENSE-AUL solver instead. Initial feasibility detection stage by BLEIC may take too long on such problems. ALGORITHM FEATURES: * supports dense and sparse QP problems * supports box and general linear equality/inequality constraints * can solve all types of problems (convex, semidefinite, nonconvex) as long as they are bounded from below under constraints. Say, it is possible to solve "min{-x^2} subject to -1<=x<=+1". Of course, global minimum is found only for positive definite and semidefinite problems. As for indefinite ones - only local minimum is found. ALGORITHM OUTLINE: * BLEIC-QP solver is just a driver function for MinBLEIC solver; it solves quadratic programming problem as general linearly constrained optimization problem, which is solved by means of BLEIC solver (part of ALGLIB, active set method). ALGORITHM LIMITATIONS: * This algorithm is inefficient on problems with hundreds and thousands of general inequality constraints and infeasible initial point. Initial feasibility detection stage may take too long on such constraint sets. Consider using DENSE-AUL instead. * unlike QuickQP solver, this algorithm does not perform Newton steps and does not use Level 3 BLAS. Being general-purpose active set method, it can activate constraints only one-by-one. Thus, its performance is lower than that of QuickQP. * its precision is also a bit inferior to that of QuickQP. BLEIC-QP performs only LBFGS steps (no Newton steps), which are good at detecting neighborhood of the solution, buy needs many iterations to find solution with more than 6 digits of precision. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} EpsX - >=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinQPSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. NOTE: this algorithm uses LBFGS iterations, which are relatively cheap, but improve function value only a bit. So you will need many iterations to converge - from 0.1*N to 10*N, depending on problem's condition number. IT IS VERY IMPORTANT TO CALL MinQPSetScale() WHEN YOU USE THIS ALGORITHM BECAUSE ITS STOPPING CRITERIA ARE SCALE-DEPENDENT! Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (presently it is small step length, but it may change in the future versions of ALGLIB). -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetalgobleic(minqpstate state, double epsg, double epsf, double epsx, int maxits) { minqp.minqpsetalgobleic(state.innerobj, epsg, epsf, epsx, maxits, null); } public static void minqpsetalgobleic(minqpstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { minqp.minqpsetalgobleic(state.innerobj, epsg, epsf, epsx, maxits, _params); } /************************************************************************* This function tells QP solver to use Dense-AUL algorithm and sets stopping criteria for the algorithm. ALGORITHM FEATURES: * supports box and dense/sparse general linear equality/inequality constraints * convergence is theoretically proved for positive-definite (convex) QP problems. Semidefinite and non-convex problems can be solved as long as they are bounded from below under constraints, although without theoretical guarantees. * this solver is better than QP-BLEIC on problems with large number of general linear constraints. It better handles infeasible initial points. ALGORITHM OUTLINE: * this algorithm is an augmented Lagrangian method with dense preconditioner (hence its name). It is similar to barrier/penalty methods, but much more precise and faster. * it performs several outer iterations in order to refine values of the Lagrange multipliers. Single outer iteration is a solution of some unconstrained optimization problem: first it performs dense Cholesky factorization of the Hessian in order to build preconditioner (adaptive regularization is applied to enforce positive definiteness), and then it uses L-BFGS optimizer to solve optimization problem. * typically you need about 5-10 outer iterations to converge to solution ALGORITHM LIMITATIONS: * because dense Cholesky driver is used, this algorithm has O(N^2) memory requirements and O(OuterIterations*N^3) minimum running time. From the practical point of view, it limits its applicability by several thousands of variables. From the other side, variables count is the most limiting factor, and dependence on constraint count is much more lower. Assuming that constraint matrix is sparse, it may handle tens of thousands of general linear constraints. INPUT PARAMETERS: State - structure which stores algorithm state EpsX - >=0, stopping criteria for inner optimizer. Inner iterations are stopped when step length (with variable scaling being applied) is less than EpsX. See minqpsetscale() for more information on variable scaling. Rho - penalty coefficient, Rho>0: * large enough that algorithm converges with desired precision. * not TOO large to prevent ill-conditioning * recommended values are 100, 1000 or 10000 ItsCnt - number of outer iterations: * recommended values: 10-15 (although in most cases it converges within 5 iterations, you may need a few more to be sure). * ItsCnt=0 means that small number of outer iterations is automatically chosen (10 iterations in current version). * ItsCnt=1 means that AUL algorithm performs just as usual penalty method. * ItsCnt>1 means that AUL algorithm performs specified number of outer iterations IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS ALGORITHM BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT! NOTE: Passing EpsX=0 will lead to automatic step length selection (specific step length chosen may change in the future versions of ALGLIB, so it is better to specify step length explicitly). -- ALGLIB -- Copyright 20.08.2016 by Bochkanov Sergey *************************************************************************/ public static void minqpsetalgodenseaul(minqpstate state, double epsx, double rho, int itscnt) { minqp.minqpsetalgodenseaul(state.innerobj, epsx, rho, itscnt, null); } public static void minqpsetalgodenseaul(minqpstate state, double epsx, double rho, int itscnt, alglib.xparams _params) { minqp.minqpsetalgodenseaul(state.innerobj, epsx, rho, itscnt, _params); } /************************************************************************* This function tells solver to use QuickQP algorithm: special extra-fast algorithm for problems with box-only constrants. It may solve non-convex problems as long as they are bounded from below under constraints. ALGORITHM FEATURES: * many times (from 5x to 50x!) faster than BLEIC-based QP solver; utilizes accelerated methods for activation of constraints. * supports dense and sparse QP problems * supports ONLY box constraints; general linear constraints are NOT supported by this solver * can solve all types of problems (convex, semidefinite, nonconvex) as long as they are bounded from below under constraints. Say, it is possible to solve "min{-x^2} subject to -1<=x<=+1". In convex/semidefinite case global minimum is returned, in nonconvex case - algorithm returns one of the local minimums. ALGORITHM OUTLINE: * algorithm performs two kinds of iterations: constrained CG iterations and constrained Newton iterations * initially it performs small number of constrained CG iterations, which can efficiently activate/deactivate multiple constraints * after CG phase algorithm tries to calculate Cholesky decomposition and to perform several constrained Newton steps. If Cholesky decomposition failed (matrix is indefinite even under constraints), we perform more CG iterations until we converge to such set of constraints that system matrix becomes positive definite. Constrained Newton steps greatly increase convergence speed and precision. * algorithm interleaves CG and Newton iterations which allows to handle indefinite matrices (CG phase) and quickly converge after final set of constraints is found (Newton phase). Combination of CG and Newton phases is called "outer iteration". * it is possible to turn off Newton phase (beneficial for semidefinite problems - Cholesky decomposition will fail too often) ALGORITHM LIMITATIONS: * algorithm does not support general linear constraints; only box ones are supported * Cholesky decomposition for sparse problems is performed with Skyline Cholesky solver, which is intended for low-profile matrices. No profile- reducing reordering of variables is performed in this version of ALGLIB. * problems with near-zero negative eigenvalues (or exacty zero ones) may experience about 2-3x performance penalty. The reason is that Cholesky decomposition can not be performed until we identify directions of zero and negative curvature and activate corresponding boundary constraints - but we need a lot of trial and errors because these directions are hard to notice in the matrix spectrum. In this case you may turn off Newton phase of algorithm. Large negative eigenvalues are not an issue, so highly non-convex problems can be solved very efficiently. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} EpsX - >=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinQPSetScale() MaxOuterIts-maximum number of OUTER iterations. One outer iteration includes some amount of CG iterations (from 5 to ~N) and one or several (usually small amount) Newton steps. Thus, one outer iteration has high cost, but can greatly reduce funcation value. Use 0 if you do not want to limit number of outer iterations. UseNewton- use Newton phase or not: * Newton phase improves performance of positive definite dense problems (about 2 times improvement can be observed) * can result in some performance penalty on semidefinite or slightly negative definite problems - each Newton phase will bring no improvement (Cholesky failure), but still will require computational time. * if you doubt, you can turn off this phase - optimizer will retain its most of its high speed. IT IS VERY IMPORTANT TO CALL MinQPSetScale() WHEN YOU USE THIS ALGORITHM BECAUSE ITS STOPPING CRITERIA ARE SCALE-DEPENDENT! Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (presently it is small step length, but it may change in the future versions of ALGLIB). -- ALGLIB -- Copyright 22.05.2014 by Bochkanov Sergey *************************************************************************/ public static void minqpsetalgoquickqp(minqpstate state, double epsg, double epsf, double epsx, int maxouterits, bool usenewton) { minqp.minqpsetalgoquickqp(state.innerobj, epsg, epsf, epsx, maxouterits, usenewton, null); } public static void minqpsetalgoquickqp(minqpstate state, double epsg, double epsf, double epsx, int maxouterits, bool usenewton, alglib.xparams _params) { minqp.minqpsetalgoquickqp(state.innerobj, epsg, epsf, epsx, maxouterits, usenewton, _params); } /************************************************************************* This function sets box constraints for QP solver Box constraints are inactive by default (after initial creation). After being set, they are preserved until explicitly turned off with another SetBC() call. All QP solvers may handle box constraints. INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF (latter is recommended because it will allow solver to use better algorithm). BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF (latter is recommended because it will allow solver to use better algorithm). NOTE: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetbc(minqpstate state, double[] bndl, double[] bndu) { minqp.minqpsetbc(state.innerobj, bndl, bndu, null); } public static void minqpsetbc(minqpstate state, double[] bndl, double[] bndu, alglib.xparams _params) { minqp.minqpsetbc(state.innerobj, bndl, bndu, _params); } /************************************************************************* This function sets dense linear constraints for QP optimizer. This function overrides results of previous calls to minqpsetlc(), minqpsetlcsparse() and minqpsetlcmixed(). After call to this function sparse constraints are dropped, and you have only those constraints which were specified in the present call. If you want to specify mixed (with dense and sparse terms) linear constraints, you should call minqpsetlcmixed(). SUPPORT BY QP SOLVERS: Following QP solvers can handle dense linear constraints: * BLEIC-QP - handles them with high precision, but may be inefficient for problems with hundreds of constraints * Dense-AUL-QP - handles them with moderate precision (approx. 10^-6), may efficiently handle thousands of constraints. Following QP solvers can NOT handle dense linear constraints: * QuickQP - can not handle general linear constraints INPUT PARAMETERS: State - structure previously allocated with MinQPCreate call. C - linear constraints, array[K,N+1]. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. CT - type of constraints, array[K]: * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT NOTE 1: linear (non-bound) constraints are satisfied only approximately - there always exists some violation due to numerical errors and algorithmic limitations (BLEIC-QP solver is most precise, AUL-QP solver is less precise). -- ALGLIB -- Copyright 19.06.2012 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlc(minqpstate state, double[,] c, int[] ct, int k) { minqp.minqpsetlc(state.innerobj, c, ct, k, null); } public static void minqpsetlc(minqpstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { minqp.minqpsetlc(state.innerobj, c, ct, k, _params); } public static void minqpsetlc(minqpstate state, double[,] c, int[] ct) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minqpsetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minqp.minqpsetlc(state.innerobj, c, ct, k, null); return; } public static void minqpsetlc(minqpstate state, double[,] c, int[] ct, alglib.xparams _params) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minqpsetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minqp.minqpsetlc(state.innerobj, c, ct, k, _params); return; } /************************************************************************* This function sets sparse linear constraints for QP optimizer. This function overrides results of previous calls to minqpsetlc(), minqpsetlcsparse() and minqpsetlcmixed(). After call to this function dense constraints are dropped, and you have only those constraints which were specified in the present call. If you want to specify mixed (with dense and sparse terms) linear constraints, you should call minqpsetlcmixed(). SUPPORT BY QP SOLVERS: Following QP solvers can handle sparse linear constraints: * BLEIC-QP - handles them with high precision, but can not utilize their sparsity - sparse constraint matrix is silently converted to dense format. Thus, it may be inefficient for problems with hundreds of constraints. * Dense-AUL-QP - although this solver uses dense linear algebra to calculate Cholesky preconditioner, it may efficiently handle sparse constraints. It may solve problems with hundreds and thousands of constraints. The only drawback is that precision of constraint handling is typically within 1E-4... ..1E-6 range. Following QP solvers can NOT handle sparse linear constraints: * QuickQP - can not handle general linear constraints INPUT PARAMETERS: State - structure previously allocated with MinQPCreate call. C - linear constraints, sparse matrix with dimensions at least [K,N+1]. If matrix has larger size, only leading Kx(N+1) rectangle is used. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. CT - type of constraints, array[K]: * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0 NOTE 1: linear (non-bound) constraints are satisfied only approximately - there always exists some violation due to numerical errors and algorithmic limitations (BLEIC-QP solver is most precise, AUL-QP solver is less precise). -- ALGLIB -- Copyright 22.08.2016 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlcsparse(minqpstate state, sparsematrix c, int[] ct, int k) { minqp.minqpsetlcsparse(state.innerobj, c.innerobj, ct, k, null); } public static void minqpsetlcsparse(minqpstate state, sparsematrix c, int[] ct, int k, alglib.xparams _params) { minqp.minqpsetlcsparse(state.innerobj, c.innerobj, ct, k, _params); } /************************************************************************* This function sets mixed linear constraints, which include a set of dense rows, and a set of sparse rows. This function overrides results of previous calls to minqpsetlc(), minqpsetlcsparse() and minqpsetlcmixed(). This function may be useful if constraint matrix includes large number of both types of rows - dense and sparse. If you have just a few sparse rows, you may represent them in dense format without loosing performance. Similarly, if you have just a few dense rows, you may store them in sparse format with almost same performance. SUPPORT BY QP SOLVERS: Following QP solvers can handle mixed dense/sparse linear constraints: * BLEIC-QP - handles them with high precision, but can not utilize their sparsity - sparse constraint matrix is silently converted to dense format. Thus, it may be inefficient for problems with hundreds of constraints. * Dense-AUL-QP - although this solver uses dense linear algebra to calculate Cholesky preconditioner, it may efficiently handle sparse constraints. It may solve problems with hundreds and thousands of constraints. The only drawback is that precision of constraint handling is typically within 1E-4... ..1E-6 range. Following QP solvers can NOT handle mixed linear constraints: * QuickQP - can not handle general linear constraints at all INPUT PARAMETERS: State - structure previously allocated with MinQPCreate call. DenseC - dense linear constraints, array[K,N+1]. Each row of DenseC represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of DenseC (including right part) must be finite. DenseCT - type of constraints, array[K]: * if DenseCT[i]>0, then I-th constraint is DenseC[i,*]*x >= DenseC[i,n+1] * if DenseCT[i]=0, then I-th constraint is DenseC[i,*]*x = DenseC[i,n+1] * if DenseCT[i]<0, then I-th constraint is DenseC[i,*]*x <= DenseC[i,n+1] DenseK - number of equality/inequality constraints, DenseK>=0 SparseC - linear constraints, sparse matrix with dimensions at least [SparseK,N+1]. If matrix has larger size, only leading SPARSEKx(N+1) rectangle is used. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. SparseCT- type of sparse constraints, array[K]: * if SparseCT[i]>0, then I-th constraint is SparseC[i,*]*x >= SparseC[i,n+1] * if SparseCT[i]=0, then I-th constraint is SparseC[i,*]*x = SparseC[i,n+1] * if SparseCT[i]<0, then I-th constraint is SparseC[i,*]*x <= SparseC[i,n+1] SparseK - number of sparse equality/inequality constraints, K>=0 NOTE 1: linear (non-bound) constraints are satisfied only approximately - there always exists some violation due to numerical errors and algorithmic limitations (BLEIC-QP solver is most precise, AUL-QP solver is less precise). -- ALGLIB -- Copyright 22.08.2016 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlcmixed(minqpstate state, double[,] densec, int[] densect, int densek, sparsematrix sparsec, int[] sparsect, int sparsek) { minqp.minqpsetlcmixed(state.innerobj, densec, densect, densek, sparsec.innerobj, sparsect, sparsek, null); } public static void minqpsetlcmixed(minqpstate state, double[,] densec, int[] densect, int densek, sparsematrix sparsec, int[] sparsect, int sparsek, alglib.xparams _params) { minqp.minqpsetlcmixed(state.innerobj, densec, densect, densek, sparsec.innerobj, sparsect, sparsek, _params); } /************************************************************************* This function solves quadratic programming problem. Prior to calling this function you should choose solver by means of one of the following functions: * minqpsetalgoquickqp() - for QuickQP solver * minqpsetalgobleic() - for BLEIC-QP solver * minqpsetalgodenseaul() - for Dense-AUL-QP solver These functions also allow you to control stopping criteria of the solver. If you did not set solver, MinQP subpackage will automatically select solver for your problem and will run it with default stopping criteria. However, it is better to set explicitly solver and its stopping criteria. INPUT PARAMETERS: State - algorithm state You should use MinQPResults() function to access results after calls to this function. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey. Special thanks to Elvira Illarionova for important suggestions on the linearly constrained QP algorithm. *************************************************************************/ public static void minqpoptimize(minqpstate state) { minqp.minqpoptimize(state.innerobj, null); } public static void minqpoptimize(minqpstate state, alglib.xparams _params) { minqp.minqpoptimize(state.innerobj, _params); } /************************************************************************* QP solver results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution. This array is allocated and initialized only when Rep.TerminationType parameter is positive (success). Rep - optimization report. You should check Rep.TerminationType, which contains completion code, and you may check another fields which contain another information about algorithm functioning. Failure codes returned by algorithm are: * -9 failure of the automatic scale evaluation: one of the diagonal elements of the quadratic term is non-positive. Specify variable scales manually! * -5 inappropriate solver was used: * QuickQP solver for problem with general linear constraints * -4 BLEIC-QP/QuickQP solver found unconstrained direction of negative curvature (function is unbounded from below even under constraints), no meaningful minimum can be found. * -3 inconsistent constraints (or maybe feasible point is too hard to find). If you are sure that constraints are feasible, try to restart optimizer with better initial approximation. Completion codes specific for Cholesky algorithm: * 4 successful completion Completion codes specific for BLEIC/QuickQP algorithms: * 1 relative function improvement is no more than EpsF. * 2 scaled step is no more than EpsX. * 4 scaled gradient norm is no more than EpsG. * 5 MaxIts steps was taken -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpresults(minqpstate state, out double[] x, out minqpreport rep) { x = new double[0]; rep = new minqpreport(); minqp.minqpresults(state.innerobj, ref x, rep.innerobj, null); } public static void minqpresults(minqpstate state, out double[] x, out minqpreport rep, alglib.xparams _params) { x = new double[0]; rep = new minqpreport(); minqp.minqpresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* QP results Buffered implementation of MinQPResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpresultsbuf(minqpstate state, ref double[] x, minqpreport rep) { minqp.minqpresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void minqpresultsbuf(minqpstate state, ref double[] x, minqpreport rep, alglib.xparams _params) { minqp.minqpresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } } public partial class alglib { } public partial class alglib { /************************************************************************* This object stores linear solver state. You should use functions provided by MinLP subpackage to work with this object *************************************************************************/ public class minlpstate : alglibobject { // // Public declarations // public minlpstate() { _innerobj = new minlp.minlpstate(); } public override alglib.alglibobject make_copy() { return new minlpstate((minlp.minlpstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minlp.minlpstate _innerobj; public minlp.minlpstate innerobj { get { return _innerobj; } } public minlpstate(minlp.minlpstate obj) { _innerobj = obj; } } /************************************************************************* This structure stores optimization report: * f target function value * y dual variables * stats array[N+M], statuses of box (N) and linear (M) constraints: * stats[i]>0 => constraint at upper bound (also used for free non-basic variables set to zero) * stats[i]<0 => constraint at lower bound * stats[i]=0 => constraint is inactive, basic variable * primalerror primal feasibility error * dualerror dual feasibility error * iterationscount iteration count * terminationtype completion code (see below) Completion codes: * -4 LP problem is primal unbounded (dual infeasible) * -3 LP problem is primal infeasible (dual unbounded) * 1..4 successful completion * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. *************************************************************************/ public class minlpreport : alglibobject { // // Public declarations // public double f { get { return _innerobj.f; } set { _innerobj.f = value; } } public double[] y { get { return _innerobj.y; } set { _innerobj.y = value; } } public int[] stats { get { return _innerobj.stats; } set { _innerobj.stats = value; } } public double primalerror { get { return _innerobj.primalerror; } set { _innerobj.primalerror = value; } } public double dualerror { get { return _innerobj.dualerror; } set { _innerobj.dualerror = value; } } public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public minlpreport() { _innerobj = new minlp.minlpreport(); } public override alglib.alglibobject make_copy() { return new minlpreport((minlp.minlpreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minlp.minlpreport _innerobj; public minlp.minlpreport innerobj { get { return _innerobj; } } public minlpreport(minlp.minlpreport obj) { _innerobj = obj; } } /************************************************************************* LINEAR PROGRAMMING The subroutine creates LP solver. After initial creation it contains default optimization problem with zero cost vector and all variables being fixed to zero values and no constraints. In order to actually solve something you should: * set cost vector with minlpsetcost() * set variable bounds with minlpsetbc() or minlpsetbcall() * specify constraint matrix with one of the following functions: [*] minlpsetlc() for dense one-sided constraints [*] minlpsetlc2dense() for dense two-sided constraints [*] minlpsetlc2() for sparse two-sided constraints [*] minlpaddlc2dense() to add one dense row to constraint matrix [*] minlpaddlc2() to add one row to constraint matrix (compressed format) * call minlpoptimize() to run the solver and minlpresults() to get the solution vector and additional information. Presently this optimizer supports only revised simplex method as underlying solver. DSE pricing and bounds flipping ratio test (aka long dual step) are supported. Large-scale sparse LU solver with Forest-Tomlin is used internally as linear algebra driver. Future releases of ALGLIB may introduce other solvers. INPUT PARAMETERS: N - problem size OUTPUT PARAMETERS: State - optimizer in the default state -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpcreate(int n, out minlpstate state) { state = new minlpstate(); minlp.minlpcreate(n, state.innerobj, null); } public static void minlpcreate(int n, out minlpstate state, alglib.xparams _params) { state = new minlpstate(); minlp.minlpcreate(n, state.innerobj, _params); } /************************************************************************* This function sets cost term for LP solver. By default, cost term is zero. INPUT PARAMETERS: State - structure which stores algorithm state C - cost term, array[N]. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetcost(minlpstate state, double[] c) { minlp.minlpsetcost(state.innerobj, c, null); } public static void minlpsetcost(minlpstate state, double[] c, alglib.xparams _params) { minlp.minlpsetcost(state.innerobj, c, _params); } /************************************************************************* This function sets scaling coefficients. ALGLIB optimizers use scaling matrices to test stopping conditions and as preconditioner. Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetscale(minlpstate state, double[] s) { minlp.minlpsetscale(state.innerobj, s, null); } public static void minlpsetscale(minlpstate state, double[] s, alglib.xparams _params) { minlp.minlpsetscale(state.innerobj, s, _params); } /************************************************************************* This function sets box constraints for LP solver (all variables at once, different constraints for different variables). The default state of constraints is to have all variables fixed at zero. You have to overwrite it by your own constraint vector. Constraint status is preserved until constraints are explicitly overwritten with another minlpsetbc() call, overwritten with minlpsetbcall(), or partially overwritten with minlmsetbci() call. Following types of constraints are supported: DESCRIPTION CONSTRAINT HOW TO SPECIFY fixed variable x[i]=Bnd[i] BndL[i]=BndU[i] lower bound BndL[i]<=x[i] BndU[i]=+INF upper bound x[i]<=BndU[i] BndL[i]=-INF range BndL[i]<=x[i]<=BndU[i] ... free variable - BndL[I]=-INF, BndU[I]+INF INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. BndU - upper bounds, array[N]. NOTE: infinite values can be specified by means of Double.PositiveInfinity and Double.NegativeInfinity (in C#) and alglib::fp_posinf and alglib::fp_neginf (in C++). NOTE: you may replace infinities by very small/very large values, but it is not recommended because large numbers may introduce large numerical errors in the algorithm. NOTE: if constraints for all variables are same you may use minlpsetbcall() which allows to specify constraints without using arrays. NOTE: BndL>BndU will result in LP problem being recognized as infeasible. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetbc(minlpstate state, double[] bndl, double[] bndu) { minlp.minlpsetbc(state.innerobj, bndl, bndu, null); } public static void minlpsetbc(minlpstate state, double[] bndl, double[] bndu, alglib.xparams _params) { minlp.minlpsetbc(state.innerobj, bndl, bndu, _params); } /************************************************************************* This function sets box constraints for LP solver (all variables at once, same constraints for all variables) The default state of constraints is to have all variables fixed at zero. You have to overwrite it by your own constraint vector. Constraint status is preserved until constraints are explicitly overwritten with another minlpsetbc() call or partially overwritten with minlpsetbcall(). Following types of constraints are supported: DESCRIPTION CONSTRAINT HOW TO SPECIFY fixed variable x[i]=Bnd[i] BndL[i]=BndU[i] lower bound BndL[i]<=x[i] BndU[i]=+INF upper bound x[i]<=BndU[i] BndL[i]=-INF range BndL[i]<=x[i]<=BndU[i] ... free variable - BndL[I]=-INF, BndU[I]+INF INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bound, same for all variables BndU - upper bound, same for all variables NOTE: infinite values can be specified by means of Double.PositiveInfinity and Double.NegativeInfinity (in C#) and alglib::fp_posinf and alglib::fp_neginf (in C++). NOTE: you may replace infinities by very small/very large values, but it is not recommended because large numbers may introduce large numerical errors in the algorithm. NOTE: minlpsetbc() can be used to specify different constraints for different variables. NOTE: BndL>BndU will result in LP problem being recognized as infeasible. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetbcall(minlpstate state, double bndl, double bndu) { minlp.minlpsetbcall(state.innerobj, bndl, bndu, null); } public static void minlpsetbcall(minlpstate state, double bndl, double bndu, alglib.xparams _params) { minlp.minlpsetbcall(state.innerobj, bndl, bndu, _params); } /************************************************************************* This function sets box constraints for I-th variable (other variables are not modified). The default state of constraints is to have all variables fixed at zero. You have to overwrite it by your own constraint vector. Following types of constraints are supported: DESCRIPTION CONSTRAINT HOW TO SPECIFY fixed variable x[i]=Bnd[i] BndL[i]=BndU[i] lower bound BndL[i]<=x[i] BndU[i]=+INF upper bound x[i]<=BndU[i] BndL[i]=-INF range BndL[i]<=x[i]<=BndU[i] ... free variable - BndL[I]=-INF, BndU[I]+INF INPUT PARAMETERS: State - structure stores algorithm state I - variable index, in [0,N) BndL - lower bound for I-th variable BndU - upper bound for I-th variable NOTE: infinite values can be specified by means of Double.PositiveInfinity and Double.NegativeInfinity (in C#) and alglib::fp_posinf and alglib::fp_neginf (in C++). NOTE: you may replace infinities by very small/very large values, but it is not recommended because large numbers may introduce large numerical errors in the algorithm. NOTE: minlpsetbc() can be used to specify different constraints for different variables. NOTE: BndL>BndU will result in LP problem being recognized as infeasible. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetbci(minlpstate state, int i, double bndl, double bndu) { minlp.minlpsetbci(state.innerobj, i, bndl, bndu, null); } public static void minlpsetbci(minlpstate state, int i, double bndl, double bndu, alglib.xparams _params) { minlp.minlpsetbci(state.innerobj, i, bndl, bndu, _params); } /************************************************************************* This function sets one-sided linear constraints A*x ~ AU, where "~" can be a mix of "<=", "=" and ">=". IMPORTANT: this function is provided here for compatibility with the rest of ALGLIB optimizers which accept constraints in format like this one. Many real-life problems feature two-sided constraints like a0 <= a*x <= a1. It is really inefficient to add them as a pair of one-sided constraints. Use minlpsetlc2dense(), minlpsetlc2(), minlpaddlc2() (or its sparse version) wherever possible. INPUT PARAMETERS: State - structure previously allocated with minlpcreate() call. A - linear constraints, array[K,N+1]. Each row of A represents one constraint, with first N elements being linear coefficients, and last element being right side. CT - constraint types, array[K]: * if CT[i]>0, then I-th constraint is A[i,*]*x >= A[i,n] * if CT[i]=0, then I-th constraint is A[i,*]*x = A[i,n] * if CT[i]<0, then I-th constraint is A[i,*]*x <= A[i,n] K - number of equality/inequality constraints, K>=0; if not given, inferred from sizes of A and CT. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetlc(minlpstate state, double[,] a, int[] ct, int k) { minlp.minlpsetlc(state.innerobj, a, ct, k, null); } public static void minlpsetlc(minlpstate state, double[,] a, int[] ct, int k, alglib.xparams _params) { minlp.minlpsetlc(state.innerobj, a, ct, k, _params); } public static void minlpsetlc(minlpstate state, double[,] a, int[] ct) { int k; if( (ap.rows(a)!=ap.len(ct))) throw new alglibexception("Error while calling 'minlpsetlc': looks like one of arguments has wrong size"); k = ap.rows(a); minlp.minlpsetlc(state.innerobj, a, ct, k, null); return; } public static void minlpsetlc(minlpstate state, double[,] a, int[] ct, alglib.xparams _params) { int k; if( (ap.rows(a)!=ap.len(ct))) throw new alglibexception("Error while calling 'minlpsetlc': looks like one of arguments has wrong size"); k = ap.rows(a); minlp.minlpsetlc(state.innerobj, a, ct, k, _params); return; } /************************************************************************* This function sets two-sided linear constraints AL <= A*x <= AU. This version accepts dense matrix as input; internally LP solver uses sparse storage anyway (most LP problems are sparse), but for your convenience it may accept dense inputs. This function overwrites linear constraints set by previous calls (if such calls were made). We recommend you to use sparse version of this function unless you solve small-scale LP problem (less than few hundreds of variables). NOTE: there also exist several versions of this function: * one-sided dense version which accepts constraints in the same format as one used by QP and NLP solvers * two-sided sparse version which accepts sparse matrix * two-sided dense version which allows you to add constraints row by row * two-sided sparse version which allows you to add constraints row by row INPUT PARAMETERS: State - structure previously allocated with minlpcreate() call. A - linear constraints, array[K,N]. Each row of A represents one constraint. One-sided inequality constraints, two- sided inequality constraints, equality constraints are supported (see below) AL, AU - lower and upper bounds, array[K]; * AL[i]=AU[i] => equality constraint Ai*x * AL[i] two-sided constraint AL[i]<=Ai*x<=AU[i] * AL[i]=-INF => one-sided constraint Ai*x<=AU[i] * AU[i]=+INF => one-sided constraint AL[i]<=Ai*x * AL[i]=-INF, AU[i]=+INF => constraint is ignored K - number of equality/inequality constraints, K>=0; if not given, inferred from sizes of A, AL, AU. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetlc2dense(minlpstate state, double[,] a, double[] al, double[] au, int k) { minlp.minlpsetlc2dense(state.innerobj, a, al, au, k, null); } public static void minlpsetlc2dense(minlpstate state, double[,] a, double[] al, double[] au, int k, alglib.xparams _params) { minlp.minlpsetlc2dense(state.innerobj, a, al, au, k, _params); } public static void minlpsetlc2dense(minlpstate state, double[,] a, double[] al, double[] au) { int k; if( (ap.rows(a)!=ap.len(al)) || (ap.rows(a)!=ap.len(au))) throw new alglibexception("Error while calling 'minlpsetlc2dense': looks like one of arguments has wrong size"); k = ap.rows(a); minlp.minlpsetlc2dense(state.innerobj, a, al, au, k, null); return; } public static void minlpsetlc2dense(minlpstate state, double[,] a, double[] al, double[] au, alglib.xparams _params) { int k; if( (ap.rows(a)!=ap.len(al)) || (ap.rows(a)!=ap.len(au))) throw new alglibexception("Error while calling 'minlpsetlc2dense': looks like one of arguments has wrong size"); k = ap.rows(a); minlp.minlpsetlc2dense(state.innerobj, a, al, au, k, _params); return; } /************************************************************************* This function sets two-sided linear constraints AL <= A*x <= AU with sparse constraining matrix A. Recommended for large-scale problems. This function overwrites linear (non-box) constraints set by previous calls (if such calls were made). INPUT PARAMETERS: State - structure previously allocated with minlpcreate() call. A - sparse matrix with size [K,N] (exactly!). Each row of A represents one general linear constraint. A can be stored in any sparse storage format. AL, AU - lower and upper bounds, array[K]; * AL[i]=AU[i] => equality constraint Ai*x * AL[i] two-sided constraint AL[i]<=Ai*x<=AU[i] * AL[i]=-INF => one-sided constraint Ai*x<=AU[i] * AU[i]=+INF => one-sided constraint AL[i]<=Ai*x * AL[i]=-INF, AU[i]=+INF => constraint is ignored K - number of equality/inequality constraints, K>=0. If K=0 is specified, A, AL, AU are ignored. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetlc2(minlpstate state, sparsematrix a, double[] al, double[] au, int k) { minlp.minlpsetlc2(state.innerobj, a.innerobj, al, au, k, null); } public static void minlpsetlc2(minlpstate state, sparsematrix a, double[] al, double[] au, int k, alglib.xparams _params) { minlp.minlpsetlc2(state.innerobj, a.innerobj, al, au, k, _params); } /************************************************************************* This function appends two-sided linear constraint AL <= A*x <= AU to the list of currently present constraints. This version accepts dense constraint vector as input, but sparsifies it for internal storage and processing. Thus, time to add one constraint in is O(N) - we have to scan entire array of length N. Sparse version of this function is order of magnitude faster for constraints with just a few nonzeros per row. INPUT PARAMETERS: State - structure previously allocated with minlpcreate() call. A - linear constraint coefficient, array[N], right side is NOT included. AL, AU - lower and upper bounds; * AL=AU => equality constraint Ai*x * AL two-sided constraint AL<=A*x<=AU * AL=-INF => one-sided constraint Ai*x<=AU * AU=+INF => one-sided constraint AL<=Ai*x * AL=-INF, AU=+INF => constraint is ignored -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpaddlc2dense(minlpstate state, double[] a, double al, double au) { minlp.minlpaddlc2dense(state.innerobj, a, al, au, null); } public static void minlpaddlc2dense(minlpstate state, double[] a, double al, double au, alglib.xparams _params) { minlp.minlpaddlc2dense(state.innerobj, a, al, au, _params); } /************************************************************************* This function appends two-sided linear constraint AL <= A*x <= AU to the list of currently present constraints. Constraint is passed in compressed format: as list of non-zero entries of coefficient vector A. Such approach is more efficient than dense storage for highly sparse constraint vectors. INPUT PARAMETERS: State - structure previously allocated with minlpcreate() call. IdxA - array[NNZ], indexes of non-zero elements of A: * can be unsorted * can include duplicate indexes (corresponding entries of ValA[] will be summed) ValA - array[NNZ], values of non-zero elements of A NNZ - number of non-zero coefficients in A AL, AU - lower and upper bounds; * AL=AU => equality constraint A*x * AL two-sided constraint AL<=A*x<=AU * AL=-INF => one-sided constraint A*x<=AU * AU=+INF => one-sided constraint AL<=A*x * AL=-INF, AU=+INF => constraint is ignored -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpaddlc2(minlpstate state, int[] idxa, double[] vala, int nnz, double al, double au) { minlp.minlpaddlc2(state.innerobj, idxa, vala, nnz, al, au, null); } public static void minlpaddlc2(minlpstate state, int[] idxa, double[] vala, int nnz, double al, double au, alglib.xparams _params) { minlp.minlpaddlc2(state.innerobj, idxa, vala, nnz, al, au, _params); } /************************************************************************* This function solves LP problem. INPUT PARAMETERS: State - algorithm state You should use minlpresults() function to access results after calls to this function. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey. *************************************************************************/ public static void minlpoptimize(minlpstate state) { minlp.minlpoptimize(state.innerobj, null); } public static void minlpoptimize(minlpstate state, alglib.xparams _params) { minlp.minlpoptimize(state.innerobj, _params); } /************************************************************************* LP solver results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[N], solution. Filled by zeros on failure. Rep - optimization report. You should check Rep.TerminationType, which contains completion code, and you may check another fields which contain another information about algorithm functioning. Failure codes returned by algorithm are: * -4 LP problem is primal unbounded (dual infeasible) * -3 LP problem is primal infeasible (dual unbounded) Success codes: * 1..4 successful completion * 5 MaxIts steps was taken -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlpresults(minlpstate state, out double[] x, out minlpreport rep) { x = new double[0]; rep = new minlpreport(); minlp.minlpresults(state.innerobj, ref x, rep.innerobj, null); } public static void minlpresults(minlpstate state, out double[] x, out minlpreport rep, alglib.xparams _params) { x = new double[0]; rep = new minlpreport(); minlp.minlpresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* LP results Buffered implementation of MinLPResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlpresultsbuf(minlpstate state, ref double[] x, minlpreport rep) { minlp.minlpresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void minlpresultsbuf(minlpstate state, ref double[] x, minlpreport rep, alglib.xparams _params) { minlp.minlpresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } } public partial class alglib { } public partial class alglib { /************************************************************************* This object stores nonlinear optimizer state. You should use functions provided by MinNLC subpackage to work with this object *************************************************************************/ public class minnlcstate : alglibobject { // // Public declarations // public bool needfi { get { return _innerobj.needfi; } set { _innerobj.needfi = value; } } public bool needfij { get { return _innerobj.needfij; } set { _innerobj.needfij = value; } } public bool xupdated { get { return _innerobj.xupdated; } set { _innerobj.xupdated = value; } } public double f { get { return _innerobj.f; } set { _innerobj.f = value; } } public double[] fi { get { return _innerobj.fi; } } public double[,] j { get { return _innerobj.j; } } public double[] x { get { return _innerobj.x; } } public minnlcstate() { _innerobj = new minnlc.minnlcstate(); } public override alglib.alglibobject make_copy() { return new minnlcstate((minnlc.minnlcstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minnlc.minnlcstate _innerobj; public minnlc.minnlcstate innerobj { get { return _innerobj; } } public minnlcstate(minnlc.minnlcstate obj) { _innerobj = obj; } } /************************************************************************* These fields store optimization report: * iterationscount total number of inner iterations * nfev number of gradient evaluations * terminationtype termination type (see below) Scaled constraint violations are reported: * bcerr maximum violation of the box constraints * bcidx index of the most violated box constraint (or -1, if all box constraints are satisfied or there is no box constraint) * lcerr maximum violation of the linear constraints, computed as maximum scaled distance between final point and constraint boundary. * lcidx index of the most violated linear constraint (or -1, if all constraints are satisfied or there is no general linear constraints) * nlcerr maximum violation of the nonlinear constraints * nlcidx index of the most violated nonlinear constraint (or -1, if all constraints are satisfied or there is no nonlinear constraints) Violations of box constraints are scaled on per-component basis according to the scale vector s[] as specified by minnlcsetscale(). Violations of the general linear constraints are also computed using user-supplied variable scaling. Violations of nonlinear constraints are computed "as is" TERMINATION CODES TerminationType field contains completion code, which can be either: === FAILURE CODE === -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signaled. -3 box constraints are infeasible. Note: infeasibility of non-box constraints does NOT trigger emergency completion; you have to examine bcerr/lcerr/nlcerr to detect possibly inconsistent constraints. === SUCCESS CODE === 2 relative step is no more than EpsX. 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 user requested algorithm termination via minnlcrequesttermination(), last accepted point is returned Other fields of this structure are not documented and should not be used! *************************************************************************/ public class minnlcreport : alglibobject { // // Public declarations // public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } public int nfev { get { return _innerobj.nfev; } set { _innerobj.nfev = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public double bcerr { get { return _innerobj.bcerr; } set { _innerobj.bcerr = value; } } public int bcidx { get { return _innerobj.bcidx; } set { _innerobj.bcidx = value; } } public double lcerr { get { return _innerobj.lcerr; } set { _innerobj.lcerr = value; } } public int lcidx { get { return _innerobj.lcidx; } set { _innerobj.lcidx = value; } } public double nlcerr { get { return _innerobj.nlcerr; } set { _innerobj.nlcerr = value; } } public int nlcidx { get { return _innerobj.nlcidx; } set { _innerobj.nlcidx = value; } } public int dbgphase0its { get { return _innerobj.dbgphase0its; } set { _innerobj.dbgphase0its = value; } } public minnlcreport() { _innerobj = new minnlc.minnlcreport(); } public override alglib.alglibobject make_copy() { return new minnlcreport((minnlc.minnlcreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minnlc.minnlcreport _innerobj; public minnlc.minnlcreport innerobj { get { return _innerobj; } } public minnlcreport(minnlc.minnlcreport obj) { _innerobj = obj; } } /************************************************************************* NONLINEARLY CONSTRAINED OPTIMIZATION WITH PRECONDITIONED AUGMENTED LAGRANGIAN ALGORITHM DESCRIPTION: The subroutine minimizes function F(x) of N arguments subject to any combination of: * bound constraints * linear inequality constraints * linear equality constraints * nonlinear equality constraints Gi(x)=0 * nonlinear inequality constraints Hi(x)<=0 REQUIREMENTS: * user must provide function value and gradient for F(), H(), G() * starting point X0 must be feasible or not too far away from the feasible set * F(), G(), H() are twice continuously differentiable on the feasible set and its neighborhood * nonlinear constraints G() and H() must have non-zero gradient at G(x)=0 and at H(x)=0. Say, constraint like x^2>=1 is supported, but x^2>=0 is NOT supported. USAGE: Constrained optimization if far more complex than the unconstrained one. Nonlinearly constrained optimization is one of the most esoteric numerical procedures. Here we give very brief outline of the MinNLC optimizer. We strongly recommend you to study examples in the ALGLIB Reference Manual and to read ALGLIB User Guide on optimization, which is available at http://www.alglib.net/optimization/ 1. User initializes algorithm state with MinNLCCreate() call and chooses what NLC solver to use. There is some solver which is used by default, with default settings, but you should NOT rely on default choice. It may change in future releases of ALGLIB without notice, and no one can guarantee that new solver will be able to solve your problem with default settings. From the other side, if you choose solver explicitly, you can be pretty sure that it will work with new ALGLIB releases. In the current release following solvers can be used: * SLP solver (activated with minnlcsetalgoslp() function) - successive linear programming, recommended as the first step. * AUL solver (activated with minnlcsetalgoaul() function) - augmented Lagrangian method with dense preconditioner. SLP solver is the most robust one in ALGLIB and converges in less iterations than AUL; however, each iteration has higher overhead - we have to solve an LP problem. From the other side, AUL has cheaper iterations - although it typically needs more of them, and also it is less robust in nonconvex setting. 2. [optional] user activates OptGuard integrity checker which tries to detect possible errors in the user-supplied callbacks: * discontinuity/nonsmoothness of the target/nonlinear constraints * errors in the analytic gradient provided by user This feature is essential for early prototyping stages because it helps to catch common coding and problem statement errors. OptGuard can be activated with following functions (one per each check performed): * minnlcoptguardsmoothness() * minnlcoptguardgradient() 3. User adds boundary and/or linear and/or nonlinear constraints by means of calling one of the following functions: a) minnlcsetbc() for boundary constraints b) minnlcsetlc() for linear constraints c) minnlcsetnlc() for nonlinear constraints You may combine (a), (b) and (c) in one optimization problem. 4. User sets scale of the variables with minnlcsetscale() function. It is VERY important to set scale of the variables, because nonlinearly constrained problems are hard to solve when variables are badly scaled. 5. User sets stopping conditions with minnlcsetcond(). If NLC solver uses inner/outer iteration layout, this function sets stopping conditions for INNER iterations. 6. Finally, user calls minnlcoptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G/H. 7. User calls minnlcresults() to get solution; additionally you can retrieve OptGuard report with minnlcoptguardresults(), and get detailed report about purported errors in the target function with: * minnlcoptguardnonc1test0results() * minnlcoptguardnonc1test1results() 8. Optionally user may call minnlcrestartfrom() to solve another problem with same N but another starting point. minnlcrestartfrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size ofX X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. OUTPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlccreate(int n, double[] x, out minnlcstate state) { state = new minnlcstate(); minnlc.minnlccreate(n, x, state.innerobj, null); } public static void minnlccreate(int n, double[] x, out minnlcstate state, alglib.xparams _params) { state = new minnlcstate(); minnlc.minnlccreate(n, x, state.innerobj, _params); } public static void minnlccreate(double[] x, out minnlcstate state) { int n; state = new minnlcstate(); n = ap.len(x); minnlc.minnlccreate(n, x, state.innerobj, null); return; } public static void minnlccreate(double[] x, out minnlcstate state, alglib.xparams _params) { int n; state = new minnlcstate(); n = ap.len(x); minnlc.minnlccreate(n, x, state.innerobj, _params); return; } /************************************************************************* This subroutine is a finite difference variant of MinNLCCreate(). It uses finite differences in order to differentiate target function. Description below contains information which is specific to this function only. We recommend to read comments on MinNLCCreate() in order to get more information about creation of NLC optimizer. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size ofX X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinNLCSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large TRUNCATION errors, while too small step will result in too large NUMERICAL errors. 1.0E-4 can be good value to start from. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlccreatef(int n, double[] x, double diffstep, out minnlcstate state) { state = new minnlcstate(); minnlc.minnlccreatef(n, x, diffstep, state.innerobj, null); } public static void minnlccreatef(int n, double[] x, double diffstep, out minnlcstate state, alglib.xparams _params) { state = new minnlcstate(); minnlc.minnlccreatef(n, x, diffstep, state.innerobj, _params); } public static void minnlccreatef(double[] x, double diffstep, out minnlcstate state) { int n; state = new minnlcstate(); n = ap.len(x); minnlc.minnlccreatef(n, x, diffstep, state.innerobj, null); return; } public static void minnlccreatef(double[] x, double diffstep, out minnlcstate state, alglib.xparams _params) { int n; state = new minnlcstate(); n = ap.len(x); minnlc.minnlccreatef(n, x, diffstep, state.innerobj, _params); return; } /************************************************************************* This function sets boundary constraints for NLC optimizer. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinNLCRestartFrom(). You may combine boundary constraints with general linear ones - and with nonlinear ones! Boundary constraints are handled more efficiently than other types. Thus, if your problem has mixed constraints, you may explicitly specify some of them as boundary and save some time/space. INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: when you solve your problem with augmented Lagrangian solver, boundary constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of feasible area! -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetbc(minnlcstate state, double[] bndl, double[] bndu) { minnlc.minnlcsetbc(state.innerobj, bndl, bndu, null); } public static void minnlcsetbc(minnlcstate state, double[] bndl, double[] bndu, alglib.xparams _params) { minnlc.minnlcsetbc(state.innerobj, bndl, bndu, _params); } /************************************************************************* This function sets linear constraints for MinNLC optimizer. Linear constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinNLCRestartFrom(). You may combine linear constraints with boundary ones - and with nonlinear ones! If your problem has mixed constraints, you may explicitly specify some of them as linear. It may help optimizer to handle them more efficiently. INPUT PARAMETERS: State - structure previously allocated with MinNLCCreate call. C - linear constraints, array[K,N+1]. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. CT - type of constraints, array[K]: * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT NOTE 1: when you solve your problem with augmented Lagrangian solver, linear constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of feasible area! -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetlc(minnlcstate state, double[,] c, int[] ct, int k) { minnlc.minnlcsetlc(state.innerobj, c, ct, k, null); } public static void minnlcsetlc(minnlcstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { minnlc.minnlcsetlc(state.innerobj, c, ct, k, _params); } public static void minnlcsetlc(minnlcstate state, double[,] c, int[] ct) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minnlcsetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minnlc.minnlcsetlc(state.innerobj, c, ct, k, null); return; } public static void minnlcsetlc(minnlcstate state, double[,] c, int[] ct, alglib.xparams _params) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minnlcsetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minnlc.minnlcsetlc(state.innerobj, c, ct, k, _params); return; } /************************************************************************* This function sets nonlinear constraints for MinNLC optimizer. In fact, this function sets NUMBER of nonlinear constraints. Constraints itself (constraint functions) are passed to MinNLCOptimize() method. This method requires user-defined vector function F[] and its Jacobian J[], where: * first component of F[] and first row of Jacobian J[] corresponds to function being minimized * next NLEC components of F[] (and rows of J) correspond to nonlinear equality constraints G_i(x)=0 * next NLIC components of F[] (and rows of J) correspond to nonlinear inequality constraints H_i(x)<=0 NOTE: you may combine nonlinear constraints with linear/boundary ones. If your problem has mixed constraints, you may explicitly specify some of them as linear ones. It may help optimizer to handle them more efficiently. INPUT PARAMETERS: State - structure previously allocated with MinNLCCreate call. NLEC - number of Non-Linear Equality Constraints (NLEC), >=0 NLIC - number of Non-Linear Inquality Constraints (NLIC), >=0 NOTE 1: when you solve your problem with augmented Lagrangian solver, nonlinear constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of feasible area! NOTE 2: algorithm scales variables according to scale specified by MinNLCSetScale() function, so it can handle problems with badly scaled variables (as long as we KNOW their scales). However, there is no way to automatically scale nonlinear constraints Gi(x) and Hi(x). Inappropriate scaling of Gi/Hi may ruin convergence. Solving problem with constraint "1000*G0(x)=0" is NOT same as solving it with constraint "0.001*G0(x)=0". It means that YOU are the one who is responsible for correct scaling of nonlinear constraints Gi(x) and Hi(x). We recommend you to scale nonlinear constraints in such way that I-th component of dG/dX (or dH/dx) has approximately unit magnitude (for problems with unit scale) or has magnitude approximately equal to 1/S[i] (where S is a scale set by MinNLCSetScale() function). -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetnlc(minnlcstate state, int nlec, int nlic) { minnlc.minnlcsetnlc(state.innerobj, nlec, nlic, null); } public static void minnlcsetnlc(minnlcstate state, int nlec, int nlic, alglib.xparams _params) { minnlc.minnlcsetnlc(state.innerobj, nlec, nlic, _params); } /************************************************************************* This function sets stopping conditions for inner iterations of optimizer. INPUT PARAMETERS: State - structure which stores algorithm state EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinNLCSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic selection of the stopping condition. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetcond(minnlcstate state, double epsx, int maxits) { minnlc.minnlcsetcond(state.innerobj, epsx, maxits, null); } public static void minnlcsetcond(minnlcstate state, double epsx, int maxits, alglib.xparams _params) { minnlc.minnlcsetcond(state.innerobj, epsx, maxits, _params); } /************************************************************************* This function sets scaling coefficients for NLC optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetscale(minnlcstate state, double[] s) { minnlc.minnlcsetscale(state.innerobj, s, null); } public static void minnlcsetscale(minnlcstate state, double[] s, alglib.xparams _params) { minnlc.minnlcsetscale(state.innerobj, s, _params); } /************************************************************************* This function sets preconditioner to "inexact LBFGS-based" mode. Preconditioning is very important for convergence of Augmented Lagrangian algorithm because presence of penalty term makes problem ill-conditioned. Difference between performance of preconditioned and unpreconditioned methods can be as large as 100x! MinNLC optimizer may use following preconditioners, each with its own benefits and drawbacks: a) inexact LBFGS-based, with O(N*K) evaluation time b) exact low rank one, with O(N*K^2) evaluation time c) exact robust one, with O(N^3+K*N^2) evaluation time where K is a total number of general linear and nonlinear constraints (box ones are not counted). Inexact LBFGS-based preconditioner uses L-BFGS formula combined with orthogonality assumption to perform very fast updates. For a N-dimensional problem with K general linear or nonlinear constraints (boundary ones are not counted) it has O(N*K) cost per iteration. This preconditioner has best quality (less iterations) when general linear and nonlinear constraints are orthogonal to each other (orthogonality with respect to boundary constraints is not required). Number of iterations increases when constraints are non-orthogonal, because algorithm assumes orthogonality, but still it is better than no preconditioner at all. INPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 26.09.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetprecinexact(minnlcstate state) { minnlc.minnlcsetprecinexact(state.innerobj, null); } public static void minnlcsetprecinexact(minnlcstate state, alglib.xparams _params) { minnlc.minnlcsetprecinexact(state.innerobj, _params); } /************************************************************************* This function sets preconditioner to "exact low rank" mode. Preconditioning is very important for convergence of Augmented Lagrangian algorithm because presence of penalty term makes problem ill-conditioned. Difference between performance of preconditioned and unpreconditioned methods can be as large as 100x! MinNLC optimizer may use following preconditioners, each with its own benefits and drawbacks: a) inexact LBFGS-based, with O(N*K) evaluation time b) exact low rank one, with O(N*K^2) evaluation time c) exact robust one, with O(N^3+K*N^2) evaluation time where K is a total number of general linear and nonlinear constraints (box ones are not counted). It also provides special unpreconditioned mode of operation which can be used for test purposes. Comments below discuss low rank preconditioner. Exact low-rank preconditioner uses Woodbury matrix identity to build quadratic model of the penalized function. It has following features: * no special assumptions about orthogonality of constraints * preconditioner evaluation is optimized for K<=N. * finally, stability of the process is guaranteed only for K<=N due to degeneracy of intermediate matrices. That's why we recommend to use "exact robust" preconditioner for such cases. RECOMMENDATIONS We recommend to choose between "exact low rank" and "exact robust" preconditioners, with "low rank" version being chosen when you know in advance that total count of non-box constraints won't exceed N, and "robust" version being chosen when you need bulletproof solution. INPUT PARAMETERS: State - structure stores algorithm state UpdateFreq- update frequency. Preconditioner is rebuilt after every UpdateFreq iterations. Recommended value: 10 or higher. Zero value means that good default value will be used. -- ALGLIB -- Copyright 26.09.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetprecexactlowrank(minnlcstate state, int updatefreq) { minnlc.minnlcsetprecexactlowrank(state.innerobj, updatefreq, null); } public static void minnlcsetprecexactlowrank(minnlcstate state, int updatefreq, alglib.xparams _params) { minnlc.minnlcsetprecexactlowrank(state.innerobj, updatefreq, _params); } /************************************************************************* This function sets preconditioner to "exact robust" mode. Preconditioning is very important for convergence of Augmented Lagrangian algorithm because presence of penalty term makes problem ill-conditioned. Difference between performance of preconditioned and unpreconditioned methods can be as large as 100x! MinNLC optimizer may use following preconditioners, each with its own benefits and drawbacks: a) inexact LBFGS-based, with O(N*K) evaluation time b) exact low rank one, with O(N*K^2) evaluation time c) exact robust one, with O(N^3+K*N^2) evaluation time where K is a total number of general linear and nonlinear constraints (box ones are not counted). It also provides special unpreconditioned mode of operation which can be used for test purposes. Comments below discuss robust preconditioner. Exact robust preconditioner uses Cholesky decomposition to invert approximate Hessian matrix H=D+W'*C*W (where D stands for diagonal terms of Hessian, combined result of initial scaling matrix and penalty from box constraints; W stands for general linear constraints and linearization of nonlinear ones; C stands for diagonal matrix of penalty coefficients). This preconditioner has following features: * no special assumptions about constraint structure * preconditioner is optimized for stability; unlike "exact low rank" version which fails for K>=N, this one works well for any value of K. * the only drawback is that is takes O(N^3+K*N^2) time to build it. No economical Woodbury update is applied even when it makes sense, thus there are exist situations (K<=0. Set StpMax to 0.0 (default), if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. NOTE: different solvers employed by MinNLC optimizer use different norms for step; AUL solver uses 2-norm, whilst SLP solver uses INF-norm. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetstpmax(minnlcstate state, double stpmax) { minnlc.minnlcsetstpmax(state.innerobj, stpmax, null); } public static void minnlcsetstpmax(minnlcstate state, double stpmax, alglib.xparams _params) { minnlc.minnlcsetstpmax(state.innerobj, stpmax, _params); } /************************************************************************* This function tells MinNLC unit to use Augmented Lagrangian algorithm for nonlinearly constrained optimization. This algorithm is a slight modification of one described in "A Modified Barrier-Augmented Lagrangian Method for Constrained Minimization (1999)" by D.GOLDFARB, R.POLYAK, K. SCHEINBERG, I.YUZEFOVICH. AUL solver can be significantly faster than SLP on easy problems, although it is less robust than SLP (the "gold standard" of robust optimization). Augmented Lagrangian algorithm works by converting problem of minimizing F(x) subject to equality/inequality constraints to unconstrained problem of the form min[ f(x) + + Rho*PENALTY_EQ(x) + SHIFT_EQ(x,Nu1) + + Rho*PENALTY_INEQ(x) + SHIFT_INEQ(x,Nu2) ] where: * Rho is a fixed penalization coefficient * PENALTY_EQ(x) is a penalty term, which is used to APPROXIMATELY enforce equality constraints * SHIFT_EQ(x) is a special "shift" term which is used to "fine-tune" equality constraints, greatly increasing precision * PENALTY_INEQ(x) is a penalty term which is used to approximately enforce inequality constraints * SHIFT_INEQ(x) is a special "shift" term which is used to "fine-tune" inequality constraints, greatly increasing precision * Nu1/Nu2 are vectors of Lagrange coefficients which are fine-tuned during outer iterations of algorithm This version of AUL algorithm uses preconditioner, which greatly accelerates convergence. Because this algorithm is similar to penalty methods, it may perform steps into infeasible area. All kinds of constraints (boundary, linear and nonlinear ones) may be violated in intermediate points - and in the solution. However, properly configured AUL method is significantly better at handling constraints than barrier and/or penalty methods. The very basic outline of algorithm is given below: 1) first outer iteration is performed with "default" values of Lagrange multipliers Nu1/Nu2. Solution quality is low (candidate point can be too far away from true solution; large violation of constraints is possible) and is comparable with that of penalty methods. 2) subsequent outer iterations refine Lagrange multipliers and improve quality of the solution. INPUT PARAMETERS: State - structure which stores algorithm state Rho - penalty coefficient, Rho>0: * large enough that algorithm converges with desired precision. Minimum value is 10*max(S'*diag(H)*S), where S is a scale matrix (set by MinNLCSetScale) and H is a Hessian of the function being minimized. If you can not easily estimate Hessian norm, see our recommendations below. * not TOO large to prevent ill-conditioning * for unit-scale problems (variables and Hessian have unit magnitude), Rho=100 or Rho=1000 can be used. * it is important to note that Rho is internally multiplied by scaling matrix, i.e. optimum value of Rho depends on scale of variables specified by MinNLCSetScale(). ItsCnt - number of outer iterations: * ItsCnt=0 means that small number of outer iterations is automatically chosen (10 iterations in current version). * ItsCnt=1 means that AUL algorithm performs just as usual barrier method. * ItsCnt>1 means that AUL algorithm performs specified number of outer iterations HOW TO CHOOSE PARAMETERS Nonlinear optimization is a tricky area and Augmented Lagrangian algorithm is sometimes hard to tune. Good values of Rho and ItsCnt are problem- specific. In order to help you we prepared following set of recommendations: * for unit-scale problems (variables and Hessian have unit magnitude), Rho=100 or Rho=1000 can be used. * start from some small value of Rho and solve problem with just one outer iteration (ItcCnt=1). In this case algorithm behaves like penalty method. Increase Rho in 2x or 10x steps until you see that one outer iteration returns point which is "rough approximation to solution". It is very important to have Rho so large that penalty term becomes constraining i.e. modified function becomes highly convex in constrained directions. From the other side, too large Rho may prevent you from converging to the solution. You can diagnose it by studying number of inner iterations performed by algorithm: too few (5-10 on 1000-dimensional problem) or too many (orders of magnitude more than dimensionality) usually means that Rho is too large. * with just one outer iteration you usually have low-quality solution. Some constraints can be violated with very large margin, while other ones (which are NOT violated in the true solution) can push final point too far in the inner area of the feasible set. For example, if you have constraint x0>=0 and true solution x0=1, then merely a presence of "x0>=0" will introduce a bias towards larger values of x0. Say, algorithm may stop at x0=1.5 instead of 1.0. * after you found good Rho, you may increase number of outer iterations. ItsCnt=10 is a good value. Subsequent outer iteration will refine values of Lagrange multipliers. Constraints which were violated will be enforced, inactive constraints will be dropped (corresponding multipliers will be decreased). Ideally, you should see 10-1000x improvement in constraint handling (constraint violation is reduced). * if you see that algorithm converges to vicinity of solution, but additional outer iterations do not refine solution, it may mean that algorithm is unstable - it wanders around true solution, but can not approach it. Sometimes algorithm may be stabilized by increasing Rho one more time, making it 5x or 10x larger. SCALING OF CONSTRAINTS [IMPORTANT] AUL optimizer scales variables according to scale specified by MinNLCSetScale() function, so it can handle problems with badly scaled variables (as long as we KNOW their scales). However, because function being optimized is a mix of original function and constraint-dependent penalty functions, it is important to rescale both variables AND constraints. Say, if you minimize f(x)=x^2 subject to 1000000*x>=0, then you have constraint whose scale is different from that of target function (another example is 0.000001*x>=0). It is also possible to have constraints whose scales are misaligned: 1000000*x0>=0, 0.000001*x1<=0. Inappropriate scaling may ruin convergence because minimizing x^2 subject to x>=0 is NOT same as minimizing it subject to 1000000*x>=0. Because we know coefficients of boundary/linear constraints, we can automatically rescale and normalize them. However, there is no way to automatically rescale nonlinear constraints Gi(x) and Hi(x) - they are black boxes. It means that YOU are the one who is responsible for correct scaling of nonlinear constraints Gi(x) and Hi(x). We recommend you to rescale nonlinear constraints in such way that I-th component of dG/dX (or dH/dx) has magnitude approximately equal to 1/S[i] (where S is a scale set by MinNLCSetScale() function). WHAT IF IT DOES NOT CONVERGE? It is possible that AUL algorithm fails to converge to precise values of Lagrange multipliers. It stops somewhere around true solution, but candidate point is still too far from solution, and some constraints are violated. Such kind of failure is specific for Lagrangian algorithms - technically, they stop at some point, but this point is not constrained solution. There are exist several reasons why algorithm may fail to converge: a) too loose stopping criteria for inner iteration b) degenerate, redundant constraints c) target function has unconstrained extremum exactly at the boundary of some constraint d) numerical noise in the target function In all these cases algorithm is unstable - each outer iteration results in large and almost random step which improves handling of some constraints, but violates other ones (ideally outer iterations should form a sequence of progressively decreasing steps towards solution). First reason possible is that too loose stopping criteria for inner iteration were specified. Augmented Lagrangian algorithm solves a sequence of intermediate problems, and requries each of them to be solved with high precision. Insufficient precision results in incorrect update of Lagrange multipliers. Another reason is that you may have specified degenerate constraints: say, some constraint was repeated twice. In most cases AUL algorithm gracefully handles such situations, but sometimes it may spend too much time figuring out subtle degeneracies in constraint matrix. Third reason is tricky and hard to diagnose. Consider situation when you minimize f=x^2 subject to constraint x>=0. Unconstrained extremum is located exactly at the boundary of constrained area. In this case algorithm will tend to oscillate between negative and positive x. Each time it stops at x<0 it "reinforces" constraint x>=0, and each time it is bounced to x>0 it "relaxes" constraint (and is attracted to x<0). Such situation sometimes happens in problems with hidden symetries. Algorithm is got caught in a loop with Lagrange multipliers being continuously increased/decreased. Luckily, such loop forms after at least three iterations, so this problem can be solved by DECREASING number of outer iterations down to 1-2 and increasing penalty coefficient Rho as much as possible. Final reason is numerical noise. AUL algorithm is robust against moderate noise (more robust than, say, active set methods), but large noise may destabilize algorithm. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetalgoaul(minnlcstate state, double rho, int itscnt) { minnlc.minnlcsetalgoaul(state.innerobj, rho, itscnt, null); } public static void minnlcsetalgoaul(minnlcstate state, double rho, int itscnt, alglib.xparams _params) { minnlc.minnlcsetalgoaul(state.innerobj, rho, itscnt, _params); } /************************************************************************* This function tells MinNLC optimizer to use SLP (Successive Linear Programming) algorithm for nonlinearly constrained optimization. This algorithm is a slight modification of one described in "A Linear programming-based optimization algorithm for solving nonlinear programming problems" (2010) by Claus Still and Tapio Westerlund. Despite its name ("linear" = "first order method") this algorithm performs steps similar to that of conjugate gradients method; internally it uses orthogonality/conjugacy requirement for subsequent steps which makes it closer to second order methods in terms of convergence speed. Convergence is proved for the following case: * function and constraints are continuously differentiable (C1 class) * extended Mangasarian–Fromovitz constraint qualification (EMFCQ) holds; in the context of this algorithm EMFCQ means that one can, for any infeasible point, find a search direction such that the constraint infeasibilities are reduced. This algorithm has following nice properties: * no parameters to tune * no convexity requirements for target function or constraints * initial point can be infeasible * algorithm respects box constraints in all intermediate points (it does not even evaluate function outside of box constrained area) * once linear constraints are enforced, algorithm will not violate them * no such guarantees can be provided for nonlinear constraints, but once nonlinear constraints are enforced, algorithm will try to respect them as much as possible * numerical differentiation does not violate box constraints (although general linear and nonlinear ones can be violated during differentiation) However, following drawbacks can be noted: * algorithm performance decreased on problems with dense constraints * it has higher iteration cost than AUL - we have to solve an LP problem at each step. We recommend this algorithm as a first step; as soon as you make sure that it converges, you can try switching to AUL which is sometimes much faster. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 02.04.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetalgoslp(minnlcstate state) { minnlc.minnlcsetalgoslp(state.innerobj, null); } public static void minnlcsetalgoslp(minnlcstate state, alglib.xparams _params) { minnlc.minnlcsetalgoslp(state.innerobj, _params); } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinNLCOptimize(). NOTE: algorithm passes two parameters to rep() callback - current point and penalized function value at current point. Important - function value which is returned is NOT function being minimized. It is sum of the value of the function being minimized - and penalty term. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetxrep(minnlcstate state, bool needxrep) { minnlc.minnlcsetxrep(state.innerobj, needxrep, null); } public static void minnlcsetxrep(minnlcstate state, bool needxrep, alglib.xparams _params) { minnlc.minnlcsetxrep(state.innerobj, needxrep, _params); } /************************************************************************* This function provides reverse communication interface Reverse communication interface is not documented or recommended to use. See below for functions which provide better documented API *************************************************************************/ public static bool minnlciteration(minnlcstate state) { return minnlc.minnlciteration(state.innerobj, null); } public static bool minnlciteration(minnlcstate state, alglib.xparams _params) { return minnlc.minnlciteration(state.innerobj, _params); } /************************************************************************* This family of functions is used to launcn iterations of nonlinear optimizer These functions accept following parameters: fvec - callback which calculates function vector fi[] at given point x jac - callback which calculates function vector fi[] and Jacobian jac at given point x rep - optional callback which is called after each iteration can be null obj - optional object which is passed to func/grad/hess/jac/rep can be null NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied Jacobian, and one which uses only function vector and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object you should choose appropriate variant of MinNLCOptimize() - one which accepts function AND Jacobian or one which accepts ONLY function. Be careful to choose variant of MinNLCOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinNLCOptimize() and specific function used to create optimizer. | USER PASSED TO MinNLCOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinNLCCreateF() | works FAILS MinNLCCreate() | FAILS works Here "FAILS" denotes inappropriate combinations of optimizer creation function and MinNLCOptimize() version. Attemps to use such combination will lead to exception. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptimize(minnlcstate state, ndimensional_fvec fvec, ndimensional_rep rep, object obj) { minnlcoptimize(state, fvec, rep, obj, null); } public static void minnlcoptimize(minnlcstate state, ndimensional_fvec fvec, ndimensional_rep rep, object obj, alglib.xparams _params) { if( fvec==null ) throw new alglibexception("ALGLIB: error in 'minnlcoptimize()' (fvec is null)"); while( alglib.minnlciteration(state, _params) ) { if( state.needfi ) { fvec(state.x, state.innerobj.fi, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minnlcoptimize' (some derivatives were not provided?)"); } } public static void minnlcoptimize(minnlcstate state, ndimensional_jac jac, ndimensional_rep rep, object obj) { minnlcoptimize(state, jac, rep, obj, null); } public static void minnlcoptimize(minnlcstate state, ndimensional_jac jac, ndimensional_rep rep, object obj, alglib.xparams _params) { if( jac==null ) throw new alglibexception("ALGLIB: error in 'minnlcoptimize()' (jac is null)"); while( alglib.minnlciteration(state, _params) ) { if( state.needfij ) { jac(state.x, state.innerobj.fi, state.innerobj.j, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minnlcoptimize' (some derivatives were not provided?)"); } } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient/Jacobian. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function (constraints) at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient/Jacobian with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients/Jacobians, and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with minnlcoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minnlcsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardgradient(minnlcstate state, double teststep) { minnlc.minnlcoptguardgradient(state.innerobj, teststep, null); } public static void minnlcoptguardgradient(minnlcstate state, double teststep, alglib.xparams _params) { minnlc.minnlcoptguardgradient(state.innerobj, teststep, _params); } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) and/or constraints b) nonsmooth target function (non-C1) and/or constraints Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardsmoothness(minnlcstate state, int level) { minnlc.minnlcoptguardsmoothness(state.innerobj, level, null); } public static void minnlcoptguardsmoothness(minnlcstate state, int level, alglib.xparams _params) { minnlc.minnlcoptguardsmoothness(state.innerobj, level, _params); } public static void minnlcoptguardsmoothness(minnlcstate state) { int level; level = 1; minnlc.minnlcoptguardsmoothness(state.innerobj, level, null); return; } public static void minnlcoptguardsmoothness(minnlcstate state, alglib.xparams _params) { int level; level = 1; minnlc.minnlcoptguardsmoothness(state.innerobj, level, _params); return; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * minnlcoptguardgradient() for gradient verification * minnlcoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradfidx for specific function (Jacobian row) suspected * rep.badgradvidx for specific variable (Jacobian column) suspected * rep.badgradxbase, a point where gradient/Jacobian is tested * rep.badgraduser, user-provided gradient/Jacobian * rep.badgradnum, reference gradient/Jacobian obtained via numerical differentiation * rep.nonc0suspected, and additionally: * rep.nonc0fidx - an index of specific function violating C0 continuity * rep.nonc1suspected, and additionally * rep.nonc1fidx - an index of specific function violating C1 continuity Here function index 0 means target function, index 1 or higher denotes nonlinear constraints. === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * minnlcoptguardnonc1test0results() * minnlcoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardresults(minnlcstate state, out optguardreport rep) { rep = new optguardreport(); minnlc.minnlcoptguardresults(state.innerobj, rep.innerobj, null); } public static void minnlcoptguardresults(minnlcstate state, out optguardreport rep, alglib.xparams _params) { rep = new optguardreport(); minnlc.minnlcoptguardresults(state.innerobj, rep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardnonc1test0results(minnlcstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); minnlc.minnlcoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void minnlcoptguardnonc1test0results(minnlcstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); minnlc.minnlcoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardnonc1test1results(minnlcstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); minnlc.minnlcoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void minnlcoptguardnonc1test1results(minnlcstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); minnlc.minnlcoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* MinNLC results: the solution found, completion codes and additional information. If you activated OptGuard integrity checking functionality and want to get OptGuard report, it can be retrieved with: * minnlcoptguardresults() - for a primary report about (a) suspected C0/C1 continuity violations and (b) errors in the analytic gradient. * minnlcoptguardnonc1test0results() - for C1 continuity violation test #0, detailed line search log * minnlcoptguardnonc1test1results() - for C1 continuity violation test #1, detailed line search log INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report, contains information about completion code, constraint violation at the solution and so on. You should check rep.terminationtype in order to distinguish successful termination from unsuccessful one: === FAILURE CODES === * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -3 box constraints are infeasible. Note: infeasibility of non-box constraints does NOT trigger emergency completion; you have to examine rep.bcerr/rep.lcerr/rep.nlcerr to detect possibly inconsistent constraints. === SUCCESS CODES === * 2 scaled step is no more than EpsX. * 5 MaxIts steps were taken. * 8 user requested algorithm termination via minnlcrequesttermination(), last accepted point is returned. More information about fields of this structure can be found in the comments on minnlcreport datatype. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcresults(minnlcstate state, out double[] x, out minnlcreport rep) { x = new double[0]; rep = new minnlcreport(); minnlc.minnlcresults(state.innerobj, ref x, rep.innerobj, null); } public static void minnlcresults(minnlcstate state, out double[] x, out minnlcreport rep, alglib.xparams _params) { x = new double[0]; rep = new minnlcreport(); minnlc.minnlcresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* NLC results Buffered implementation of MinNLCResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcresultsbuf(minnlcstate state, ref double[] x, minnlcreport rep) { minnlc.minnlcresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void minnlcresultsbuf(minnlcstate state, ref double[] x, minnlcreport rep, alglib.xparams _params) { minnlc.minnlcresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 08.10.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcrequesttermination(minnlcstate state) { minnlc.minnlcrequesttermination(state.innerobj, null); } public static void minnlcrequesttermination(minnlcstate state, alglib.xparams _params) { minnlc.minnlcrequesttermination(state.innerobj, _params); } /************************************************************************* This subroutine restarts algorithm from new point. All optimization parameters (including constraints) are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure previously allocated with MinNLCCreate call. X - new starting point. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcrestartfrom(minnlcstate state, double[] x) { minnlc.minnlcrestartfrom(state.innerobj, x, null); } public static void minnlcrestartfrom(minnlcstate state, double[] x, alglib.xparams _params) { minnlc.minnlcrestartfrom(state.innerobj, x, _params); } } public partial class alglib { /************************************************************************* This object stores nonlinear optimizer state. You should use functions provided by MinBC subpackage to work with this object *************************************************************************/ public class minbcstate : alglibobject { // // Public declarations // public bool needf { get { return _innerobj.needf; } set { _innerobj.needf = value; } } public bool needfg { get { return _innerobj.needfg; } set { _innerobj.needfg = value; } } public bool xupdated { get { return _innerobj.xupdated; } set { _innerobj.xupdated = value; } } public double f { get { return _innerobj.f; } set { _innerobj.f = value; } } public double[] g { get { return _innerobj.g; } } public double[] x { get { return _innerobj.x; } } public minbcstate() { _innerobj = new minbc.minbcstate(); } public override alglib.alglibobject make_copy() { return new minbcstate((minbc.minbcstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minbc.minbcstate _innerobj; public minbc.minbcstate innerobj { get { return _innerobj; } } public minbcstate(minbc.minbcstate obj) { _innerobj = obj; } } /************************************************************************* This structure stores optimization report: * iterationscount number of iterations * nfev number of gradient evaluations * terminationtype termination type (see below) TERMINATION CODES terminationtype field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. -3 inconsistent constraints. 1 relative function improvement is no more than EpsF. 2 relative step is no more than EpsX. 4 gradient norm is no more than EpsG 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 terminated by user who called minbcrequesttermination(). X contains point which was "current accepted" when termination request was submitted. *************************************************************************/ public class minbcreport : alglibobject { // // Public declarations // public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } public int nfev { get { return _innerobj.nfev; } set { _innerobj.nfev = value; } } public int varidx { get { return _innerobj.varidx; } set { _innerobj.varidx = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public minbcreport() { _innerobj = new minbc.minbcreport(); } public override alglib.alglibobject make_copy() { return new minbcreport((minbc.minbcreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minbc.minbcreport _innerobj; public minbc.minbcreport innerobj { get { return _innerobj; } } public minbcreport(minbc.minbcreport obj) { _innerobj = obj; } } /************************************************************************* BOX CONSTRAINED OPTIMIZATION WITH FAST ACTIVATION OF MULTIPLE BOX CONSTRAINTS DESCRIPTION: The subroutine minimizes function F(x) of N arguments subject to box constraints (with some of box constraints actually being equality ones). This optimizer uses algorithm similar to that of MinBLEIC (optimizer with general linear constraints), but presence of box-only constraints allows us to use faster constraint activation strategies. On large-scale problems, with multiple constraints active at the solution, this optimizer can be several times faster than BLEIC. REQUIREMENTS: * user must provide function value and gradient * starting point X0 must be feasible or not too far away from the feasible set * grad(f) must be Lipschitz continuous on a level set: L = { x : f(x)<=f(x0) } * function must be defined everywhere on the feasible set F USAGE: Constrained optimization if far more complex than the unconstrained one. Here we give very brief outline of the BC optimizer. We strongly recommend you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide on optimization, which is available at http://www.alglib.net/optimization/ 1. User initializes algorithm state with MinBCCreate() call 2. USer adds box constraints by calling MinBCSetBC() function. 3. User sets stopping conditions with MinBCSetCond(). 4. User calls MinBCOptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G. 5. User calls MinBCResults() to get solution 6. Optionally user may call MinBCRestartFrom() to solve another problem with same N but another starting point. MinBCRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size ofX X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. OUTPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbccreate(int n, double[] x, out minbcstate state) { state = new minbcstate(); minbc.minbccreate(n, x, state.innerobj, null); } public static void minbccreate(int n, double[] x, out minbcstate state, alglib.xparams _params) { state = new minbcstate(); minbc.minbccreate(n, x, state.innerobj, _params); } public static void minbccreate(double[] x, out minbcstate state) { int n; state = new minbcstate(); n = ap.len(x); minbc.minbccreate(n, x, state.innerobj, null); return; } public static void minbccreate(double[] x, out minbcstate state, alglib.xparams _params) { int n; state = new minbcstate(); n = ap.len(x); minbc.minbccreate(n, x, state.innerobj, _params); return; } /************************************************************************* The subroutine is finite difference variant of MinBCCreate(). It uses finite differences in order to differentiate target function. Description below contains information which is specific to this function only. We recommend to read comments on MinBCCreate() in order to get more information about creation of BC optimizer. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[0..N-1]. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinBCSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large truncation errors, while too small step will result in too large numerical errors. 1.0E-6 can be good value to start with. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. CG needs exact gradient values. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 16.05.2011 by Bochkanov Sergey *************************************************************************/ public static void minbccreatef(int n, double[] x, double diffstep, out minbcstate state) { state = new minbcstate(); minbc.minbccreatef(n, x, diffstep, state.innerobj, null); } public static void minbccreatef(int n, double[] x, double diffstep, out minbcstate state, alglib.xparams _params) { state = new minbcstate(); minbc.minbccreatef(n, x, diffstep, state.innerobj, _params); } public static void minbccreatef(double[] x, double diffstep, out minbcstate state) { int n; state = new minbcstate(); n = ap.len(x); minbc.minbccreatef(n, x, diffstep, state.innerobj, null); return; } public static void minbccreatef(double[] x, double diffstep, out minbcstate state, alglib.xparams _params) { int n; state = new minbcstate(); n = ap.len(x); minbc.minbccreatef(n, x, diffstep, state.innerobj, _params); return; } /************************************************************************* This function sets boundary constraints for BC optimizer. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinBCRestartFrom(). INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: this solver has following useful properties: * bound constraints are always satisfied exactly * function is evaluated only INSIDE area specified by bound constraints, even when numerical differentiation is used (algorithm adjusts nodes according to boundary constraints) -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetbc(minbcstate state, double[] bndl, double[] bndu) { minbc.minbcsetbc(state.innerobj, bndl, bndu, null); } public static void minbcsetbc(minbcstate state, double[] bndl, double[] bndu, alglib.xparams _params) { minbc.minbcsetbc(state.innerobj, bndl, bndu, _params); } /************************************************************************* This function sets stopping conditions for the optimizer. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if on k+1-th iteration the condition |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinBCSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection. NOTE: when SetCond() called with non-zero MaxIts, BC solver may perform slightly more than MaxIts iterations. I.e., MaxIts sets non-strict limit on iterations count. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetcond(minbcstate state, double epsg, double epsf, double epsx, int maxits) { minbc.minbcsetcond(state.innerobj, epsg, epsf, epsx, maxits, null); } public static void minbcsetcond(minbcstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { minbc.minbcsetcond(state.innerobj, epsg, epsf, epsx, maxits, _params); } /************************************************************************* This function sets scaling coefficients for BC optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. In most optimizers (and in the BC too) scaling is NOT a form of preconditioning. It just affects stopping conditions. You should set preconditioner by separate call to one of the MinBCSetPrec...() functions. There is a special preconditioning mode, however, which uses scaling coefficients to form diagonal preconditioning matrix. You can turn this mode on, if you want. But you should understand that scaling is not the same thing as preconditioning - these are two different, although related forms of tuning solver. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minbcsetscale(minbcstate state, double[] s) { minbc.minbcsetscale(state.innerobj, s, null); } public static void minbcsetscale(minbcstate state, double[] s, alglib.xparams _params) { minbc.minbcsetscale(state.innerobj, s, _params); } /************************************************************************* Modification of the preconditioner: preconditioning is turned off. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetprecdefault(minbcstate state) { minbc.minbcsetprecdefault(state.innerobj, null); } public static void minbcsetprecdefault(minbcstate state, alglib.xparams _params) { minbc.minbcsetprecdefault(state.innerobj, _params); } /************************************************************************* Modification of the preconditioner: diagonal of approximate Hessian is used. INPUT PARAMETERS: State - structure which stores algorithm state D - diagonal of the approximate Hessian, array[0..N-1], (if larger, only leading N elements are used). NOTE 1: D[i] should be positive. Exception will be thrown otherwise. NOTE 2: you should pass diagonal of approximate Hessian - NOT ITS INVERSE. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetprecdiag(minbcstate state, double[] d) { minbc.minbcsetprecdiag(state.innerobj, d, null); } public static void minbcsetprecdiag(minbcstate state, double[] d, alglib.xparams _params) { minbc.minbcsetprecdiag(state.innerobj, d, _params); } /************************************************************************* Modification of the preconditioner: scale-based diagonal preconditioning. This preconditioning mode can be useful when you don't have approximate diagonal of Hessian, but you know that your variables are badly scaled (for example, one variable is in [1,10], and another in [1000,100000]), and most part of the ill-conditioning comes from different scales of vars. In this case simple scale-based preconditioner, with H[i] = 1/(s[i]^2), can greatly improve convergence. IMPRTANT: you should set scale of your variables with MinBCSetScale() call (before or after MinBCSetPrecScale() call). Without knowledge of the scale of your variables scale-based preconditioner will be just unit matrix. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetprecscale(minbcstate state) { minbc.minbcsetprecscale(state.innerobj, null); } public static void minbcsetprecscale(minbcstate state, alglib.xparams _params) { minbc.minbcsetprecscale(state.innerobj, _params); } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinBCOptimize(). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetxrep(minbcstate state, bool needxrep) { minbc.minbcsetxrep(state.innerobj, needxrep, null); } public static void minbcsetxrep(minbcstate state, bool needxrep, alglib.xparams _params) { minbc.minbcsetxrep(state.innerobj, needxrep, _params); } /************************************************************************* This function sets maximum step length INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which lead to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetstpmax(minbcstate state, double stpmax) { minbc.minbcsetstpmax(state.innerobj, stpmax, null); } public static void minbcsetstpmax(minbcstate state, double stpmax, alglib.xparams _params) { minbc.minbcsetstpmax(state.innerobj, stpmax, _params); } /************************************************************************* This function provides reverse communication interface Reverse communication interface is not documented or recommended to use. See below for functions which provide better documented API *************************************************************************/ public static bool minbciteration(minbcstate state) { return minbc.minbciteration(state.innerobj, null); } public static bool minbciteration(minbcstate state, alglib.xparams _params) { return minbc.minbciteration(state.innerobj, _params); } /************************************************************************* This family of functions is used to launcn iterations of nonlinear optimizer These functions accept following parameters: func - callback which calculates function (or merit function) value func at given point x grad - callback which calculates function (or merit function) value func and gradient grad at given point x rep - optional callback which is called after each iteration can be null obj - optional object which is passed to func/grad/hess/jac/rep can be null NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied gradient, and one which uses function value only and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object (either MinBCCreate() for analytical gradient or MinBCCreateF() for numerical differentiation) you should choose appropriate variant of MinBCOptimize() - one which accepts function AND gradient or one which accepts function ONLY. Be careful to choose variant of MinBCOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinBCOptimize() and specific function used to create optimizer. | USER PASSED TO MinBCOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinBCCreateF() | works FAILS MinBCCreate() | FAILS works Here "FAIL" denotes inappropriate combinations of optimizer creation function and MinBCOptimize() version. Attemps to use such combination (for example, to create optimizer with MinBCCreateF() and to pass gradient information to MinCGOptimize()) will lead to exception being thrown. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcoptimize(minbcstate state, ndimensional_func func, ndimensional_rep rep, object obj) { minbcoptimize(state, func, rep, obj, null); } public static void minbcoptimize(minbcstate state, ndimensional_func func, ndimensional_rep rep, object obj, alglib.xparams _params) { if( func==null ) throw new alglibexception("ALGLIB: error in 'minbcoptimize()' (func is null)"); while( alglib.minbciteration(state, _params) ) { if( state.needf ) { func(state.x, ref state.innerobj.f, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minbcoptimize' (some derivatives were not provided?)"); } } public static void minbcoptimize(minbcstate state, ndimensional_grad grad, ndimensional_rep rep, object obj) { minbcoptimize(state, grad, rep, obj, null); } public static void minbcoptimize(minbcstate state, ndimensional_grad grad, ndimensional_rep rep, object obj, alglib.xparams _params) { if( grad==null ) throw new alglibexception("ALGLIB: error in 'minbcoptimize()' (grad is null)"); while( alglib.minbciteration(state, _params) ) { if( state.needfg ) { grad(state.x, ref state.innerobj.f, state.innerobj.g, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minbcoptimize' (some derivatives were not provided?)"); } } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with minbcoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minbcsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardgradient(minbcstate state, double teststep) { minbc.minbcoptguardgradient(state.innerobj, teststep, null); } public static void minbcoptguardgradient(minbcstate state, double teststep, alglib.xparams _params) { minbc.minbcoptguardgradient(state.innerobj, teststep, _params); } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) b) nonsmooth target function (non-C1) Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardsmoothness(minbcstate state, int level) { minbc.minbcoptguardsmoothness(state.innerobj, level, null); } public static void minbcoptguardsmoothness(minbcstate state, int level, alglib.xparams _params) { minbc.minbcoptguardsmoothness(state.innerobj, level, _params); } public static void minbcoptguardsmoothness(minbcstate state) { int level; level = 1; minbc.minbcoptguardsmoothness(state.innerobj, level, null); return; } public static void minbcoptguardsmoothness(minbcstate state, alglib.xparams _params) { int level; level = 1; minbc.minbcoptguardsmoothness(state.innerobj, level, _params); return; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * minbcoptguardgradient() for gradient verification * minbcoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradvidx for specific variable (gradient element) suspected * rep.badgradxbase, a point where gradient is tested * rep.badgraduser, user-provided gradient (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.badgradnum, reference gradient obtained via numerical differentiation (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.nonc0suspected * rep.nonc1suspected === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * minbcoptguardnonc1test0results() * minbcoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardresults(minbcstate state, out optguardreport rep) { rep = new optguardreport(); minbc.minbcoptguardresults(state.innerobj, rep.innerobj, null); } public static void minbcoptguardresults(minbcstate state, out optguardreport rep, alglib.xparams _params) { rep = new optguardreport(); minbc.minbcoptguardresults(state.innerobj, rep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardnonc1test0results(minbcstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); minbc.minbcoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void minbcoptguardnonc1test0results(minbcstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); minbc.minbcoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardnonc1test1results(minbcstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); minbc.minbcoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void minbcoptguardnonc1test1results(minbcstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); minbc.minbcoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* BC results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report. You should check Rep.TerminationType in order to distinguish successful termination from unsuccessful one: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -3 inconsistent constraints. * 1 relative function improvement is no more than EpsF. * 2 scaled step is no more than EpsX. * 4 scaled gradient norm is no more than EpsG. * 5 MaxIts steps was taken * 8 terminated by user who called minbcrequesttermination(). X contains point which was "current accepted" when termination request was submitted. More information about fields of this structure can be found in the comments on MinBCReport datatype. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcresults(minbcstate state, out double[] x, out minbcreport rep) { x = new double[0]; rep = new minbcreport(); minbc.minbcresults(state.innerobj, ref x, rep.innerobj, null); } public static void minbcresults(minbcstate state, out double[] x, out minbcreport rep, alglib.xparams _params) { x = new double[0]; rep = new minbcreport(); minbc.minbcresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* BC results Buffered implementation of MinBCResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcresultsbuf(minbcstate state, ref double[] x, minbcreport rep) { minbc.minbcresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void minbcresultsbuf(minbcstate state, ref double[] x, minbcreport rep, alglib.xparams _params) { minbc.minbcresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* This subroutine restarts algorithm from new point. All optimization parameters (including constraints) are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure previously allocated with MinBCCreate call. X - new starting point. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcrestartfrom(minbcstate state, double[] x) { minbc.minbcrestartfrom(state.innerobj, x, null); } public static void minbcrestartfrom(minbcstate state, double[] x, alglib.xparams _params) { minbc.minbcrestartfrom(state.innerobj, x, _params); } /************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 08.10.2014 by Bochkanov Sergey *************************************************************************/ public static void minbcrequesttermination(minbcstate state) { minbc.minbcrequesttermination(state.innerobj, null); } public static void minbcrequesttermination(minbcstate state, alglib.xparams _params) { minbc.minbcrequesttermination(state.innerobj, _params); } } public partial class alglib { /************************************************************************* This object stores nonlinear optimizer state. You should use functions provided by MinNS subpackage to work with this object *************************************************************************/ public class minnsstate : alglibobject { // // Public declarations // public bool needfi { get { return _innerobj.needfi; } set { _innerobj.needfi = value; } } public bool needfij { get { return _innerobj.needfij; } set { _innerobj.needfij = value; } } public bool xupdated { get { return _innerobj.xupdated; } set { _innerobj.xupdated = value; } } public double f { get { return _innerobj.f; } set { _innerobj.f = value; } } public double[] fi { get { return _innerobj.fi; } } public double[,] j { get { return _innerobj.j; } } public double[] x { get { return _innerobj.x; } } public minnsstate() { _innerobj = new minns.minnsstate(); } public override alglib.alglibobject make_copy() { return new minnsstate((minns.minnsstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minns.minnsstate _innerobj; public minns.minnsstate innerobj { get { return _innerobj; } } public minnsstate(minns.minnsstate obj) { _innerobj = obj; } } /************************************************************************* This structure stores optimization report: * IterationsCount total number of inner iterations * NFEV number of gradient evaluations * TerminationType termination type (see below) * CErr maximum violation of all types of constraints * LCErr maximum violation of linear constraints * NLCErr maximum violation of nonlinear constraints TERMINATION CODES TerminationType field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. -3 box constraints are inconsistent -1 inconsistent parameters were passed: * penalty parameter for minnssetalgoags() is zero, but we have nonlinear constraints set by minnssetnlc() 2 sampling radius decreased below epsx 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 User requested termination via MinNSRequestTermination() Other fields of this structure are not documented and should not be used! *************************************************************************/ public class minnsreport : alglibobject { // // Public declarations // public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } public int nfev { get { return _innerobj.nfev; } set { _innerobj.nfev = value; } } public double cerr { get { return _innerobj.cerr; } set { _innerobj.cerr = value; } } public double lcerr { get { return _innerobj.lcerr; } set { _innerobj.lcerr = value; } } public double nlcerr { get { return _innerobj.nlcerr; } set { _innerobj.nlcerr = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public int varidx { get { return _innerobj.varidx; } set { _innerobj.varidx = value; } } public int funcidx { get { return _innerobj.funcidx; } set { _innerobj.funcidx = value; } } public minnsreport() { _innerobj = new minns.minnsreport(); } public override alglib.alglibobject make_copy() { return new minnsreport((minns.minnsreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minns.minnsreport _innerobj; public minns.minnsreport innerobj { get { return _innerobj; } } public minnsreport(minns.minnsreport obj) { _innerobj = obj; } } /************************************************************************* NONSMOOTH NONCONVEX OPTIMIZATION SUBJECT TO BOX/LINEAR/NONLINEAR-NONSMOOTH CONSTRAINTS DESCRIPTION: The subroutine minimizes function F(x) of N arguments subject to any combination of: * bound constraints * linear inequality constraints * linear equality constraints * nonlinear equality constraints Gi(x)=0 * nonlinear inequality constraints Hi(x)<=0 IMPORTANT: see MinNSSetAlgoAGS for important information on performance restrictions of AGS solver. REQUIREMENTS: * starting point X0 must be feasible or not too far away from the feasible set * F(), G(), H() are continuous, locally Lipschitz and continuously (but not necessarily twice) differentiable in an open dense subset of R^N. Functions F(), G() and H() may be nonsmooth and non-convex. Informally speaking, it means that functions are composed of large differentiable "patches" with nonsmoothness having place only at the boundaries between these "patches". Most real-life nonsmooth functions satisfy these requirements. Say, anything which involves finite number of abs(), min() and max() is very likely to pass the test. Say, it is possible to optimize anything of the following: * f=abs(x0)+2*abs(x1) * f=max(x0,x1) * f=sin(max(x0,x1)+abs(x2)) * for nonlinearly constrained problems: F() must be bounded from below without nonlinear constraints (this requirement is due to the fact that, contrary to box and linear constraints, nonlinear ones require special handling). * user must provide function value and gradient for F(), H(), G() at all points where function/gradient can be calculated. If optimizer requires value exactly at the boundary between "patches" (say, at x=0 for f=abs(x)), where gradient is not defined, user may resolve tie arbitrarily (in our case - return +1 or -1 at its discretion). * NS solver supports numerical differentiation, i.e. it may differentiate your function for you, but it results in 2N increase of function evaluations. Not recommended unless you solve really small problems. See minnscreatef() for more information on this functionality. USAGE: 1. User initializes algorithm state with MinNSCreate() call and chooses what NLC solver to use. There is some solver which is used by default, with default settings, but you should NOT rely on default choice. It may change in future releases of ALGLIB without notice, and no one can guarantee that new solver will be able to solve your problem with default settings. From the other side, if you choose solver explicitly, you can be pretty sure that it will work with new ALGLIB releases. In the current release following solvers can be used: * AGS solver (activated with MinNSSetAlgoAGS() function) 2. User adds boundary and/or linear and/or nonlinear constraints by means of calling one of the following functions: a) MinNSSetBC() for boundary constraints b) MinNSSetLC() for linear constraints c) MinNSSetNLC() for nonlinear constraints You may combine (a), (b) and (c) in one optimization problem. 3. User sets scale of the variables with MinNSSetScale() function. It is VERY important to set scale of the variables, because nonlinearly constrained problems are hard to solve when variables are badly scaled. 4. User sets stopping conditions with MinNSSetCond(). 5. Finally, user calls MinNSOptimize() function which takes algorithm state and pointer (delegate, etc) to callback function which calculates F/G/H. 7. User calls MinNSResults() to get solution 8. Optionally user may call MinNSRestartFrom() to solve another problem with same N but another starting point. MinNSRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. OUTPUT PARAMETERS: State - structure stores algorithm state NOTE: minnscreatef() function may be used if you do not have analytic gradient. This function creates solver which uses numerical differentiation with user-specified step. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnscreate(int n, double[] x, out minnsstate state) { state = new minnsstate(); minns.minnscreate(n, x, state.innerobj, null); } public static void minnscreate(int n, double[] x, out minnsstate state, alglib.xparams _params) { state = new minnsstate(); minns.minnscreate(n, x, state.innerobj, _params); } public static void minnscreate(double[] x, out minnsstate state) { int n; state = new minnsstate(); n = ap.len(x); minns.minnscreate(n, x, state.innerobj, null); return; } public static void minnscreate(double[] x, out minnsstate state, alglib.xparams _params) { int n; state = new minnsstate(); n = ap.len(x); minns.minnscreate(n, x, state.innerobj, _params); return; } /************************************************************************* Version of minnscreatef() which uses numerical differentiation. I.e., you do not have to calculate derivatives yourself. However, this version needs 2N times more function evaluations. 2-point differentiation formula is used, because more precise 4-point formula is unstable when used on non-smooth functions. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. DiffStep- differentiation step, DiffStep>0. Algorithm performs numerical differentiation with step for I-th variable being equal to DiffStep*S[I] (here S[] is a scale vector, set by minnssetscale() function). Do not use too small steps, because it may lead to catastrophic cancellation during intermediate calculations. OUTPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnscreatef(int n, double[] x, double diffstep, out minnsstate state) { state = new minnsstate(); minns.minnscreatef(n, x, diffstep, state.innerobj, null); } public static void minnscreatef(int n, double[] x, double diffstep, out minnsstate state, alglib.xparams _params) { state = new minnsstate(); minns.minnscreatef(n, x, diffstep, state.innerobj, _params); } public static void minnscreatef(double[] x, double diffstep, out minnsstate state) { int n; state = new minnsstate(); n = ap.len(x); minns.minnscreatef(n, x, diffstep, state.innerobj, null); return; } public static void minnscreatef(double[] x, double diffstep, out minnsstate state, alglib.xparams _params) { int n; state = new minnsstate(); n = ap.len(x); minns.minnscreatef(n, x, diffstep, state.innerobj, _params); return; } /************************************************************************* This function sets boundary constraints. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with minnsrestartfrom(). INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: AGS solver has following useful properties: * bound constraints are always satisfied exactly * function is evaluated only INSIDE area specified by bound constraints, even when numerical differentiation is used (algorithm adjusts nodes according to boundary constraints) -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetbc(minnsstate state, double[] bndl, double[] bndu) { minns.minnssetbc(state.innerobj, bndl, bndu, null); } public static void minnssetbc(minnsstate state, double[] bndl, double[] bndu, alglib.xparams _params) { minns.minnssetbc(state.innerobj, bndl, bndu, _params); } /************************************************************************* This function sets linear constraints. Linear constraints are inactive by default (after initial creation). They are preserved after algorithm restart with minnsrestartfrom(). INPUT PARAMETERS: State - structure previously allocated with minnscreate() call. C - linear constraints, array[K,N+1]. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. CT - type of constraints, array[K]: * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT NOTE: linear (non-bound) constraints are satisfied only approximately: * there always exists some minor violation (about current sampling radius in magnitude during optimization, about EpsX in the solution) due to use of penalty method to handle constraints. * numerical differentiation, if used, may lead to function evaluations outside of the feasible area, because algorithm does NOT change numerical differentiation formula according to linear constraints. If you want constraints to be satisfied exactly, try to reformulate your problem in such manner that all constraints will become boundary ones (this kind of constraints is always satisfied exactly, both in the final solution and in all intermediate points). -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetlc(minnsstate state, double[,] c, int[] ct, int k) { minns.minnssetlc(state.innerobj, c, ct, k, null); } public static void minnssetlc(minnsstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { minns.minnssetlc(state.innerobj, c, ct, k, _params); } public static void minnssetlc(minnsstate state, double[,] c, int[] ct) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minnssetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minns.minnssetlc(state.innerobj, c, ct, k, null); return; } public static void minnssetlc(minnsstate state, double[,] c, int[] ct, alglib.xparams _params) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minnssetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minns.minnssetlc(state.innerobj, c, ct, k, _params); return; } /************************************************************************* This function sets nonlinear constraints. In fact, this function sets NUMBER of nonlinear constraints. Constraints itself (constraint functions) are passed to minnsoptimize() method. This method requires user-defined vector function F[] and its Jacobian J[], where: * first component of F[] and first row of Jacobian J[] correspond to function being minimized * next NLEC components of F[] (and rows of J) correspond to nonlinear equality constraints G_i(x)=0 * next NLIC components of F[] (and rows of J) correspond to nonlinear inequality constraints H_i(x)<=0 NOTE: you may combine nonlinear constraints with linear/boundary ones. If your problem has mixed constraints, you may explicitly specify some of them as linear ones. It may help optimizer to handle them more efficiently. INPUT PARAMETERS: State - structure previously allocated with minnscreate() call. NLEC - number of Non-Linear Equality Constraints (NLEC), >=0 NLIC - number of Non-Linear Inquality Constraints (NLIC), >=0 NOTE 1: nonlinear constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of the feasible area! NOTE 2: algorithm scales variables according to scale specified by minnssetscale() function, so it can handle problems with badly scaled variables (as long as we KNOW their scales). However, there is no way to automatically scale nonlinear constraints Gi(x) and Hi(x). Inappropriate scaling of Gi/Hi may ruin convergence. Solving problem with constraint "1000*G0(x)=0" is NOT same as solving it with constraint "0.001*G0(x)=0". It means that YOU are the one who is responsible for correct scaling of nonlinear constraints Gi(x) and Hi(x). We recommend you to scale nonlinear constraints in such way that I-th component of dG/dX (or dH/dx) has approximately unit magnitude (for problems with unit scale) or has magnitude approximately equal to 1/S[i] (where S is a scale set by minnssetscale() function). NOTE 3: nonlinear constraints are always hard to handle, no matter what algorithm you try to use. Even basic box/linear constraints modify function curvature by adding valleys and ridges. However, nonlinear constraints add valleys which are very hard to follow due to their "curved" nature. It means that optimization with single nonlinear constraint may be significantly slower than optimization with multiple linear ones. It is normal situation, and we recommend you to carefully choose Rho parameter of minnssetalgoags(), because too large value may slow down convergence. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetnlc(minnsstate state, int nlec, int nlic) { minns.minnssetnlc(state.innerobj, nlec, nlic, null); } public static void minnssetnlc(minnsstate state, int nlec, int nlic, alglib.xparams _params) { minns.minnssetnlc(state.innerobj, nlec, nlic, _params); } /************************************************************************* This function sets stopping conditions for iterations of optimizer. INPUT PARAMETERS: State - structure which stores algorithm state EpsX - >=0 The AGS solver finishes its work if on k+1-th iteration sampling radius decreases below EpsX. MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection. We do not recommend you to rely on default choice in production code. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetcond(minnsstate state, double epsx, int maxits) { minns.minnssetcond(state.innerobj, epsx, maxits, null); } public static void minnssetcond(minnsstate state, double epsx, int maxits, alglib.xparams _params) { minns.minnssetcond(state.innerobj, epsx, maxits, _params); } /************************************************************************* This function sets scaling coefficients for NLC optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetscale(minnsstate state, double[] s) { minns.minnssetscale(state.innerobj, s, null); } public static void minnssetscale(minnsstate state, double[] s, alglib.xparams _params) { minns.minnssetscale(state.innerobj, s, _params); } /************************************************************************* This function tells MinNS unit to use AGS (adaptive gradient sampling) algorithm for nonsmooth constrained optimization. This algorithm is a slight modification of one described in "An Adaptive Gradient Sampling Algorithm for Nonsmooth Optimization" by Frank E. Curtisy and Xiaocun Quez. This optimizer has following benefits and drawbacks: + robustness; it can be used with nonsmooth and nonconvex functions. + relatively easy tuning; most of the metaparameters are easy to select. - it has convergence of steepest descent, slower than CG/LBFGS. - each iteration involves evaluation of ~2N gradient values and solution of 2Nx2N quadratic programming problem, which limits applicability of algorithm by small-scale problems (up to 50-100). IMPORTANT: this algorithm has convergence guarantees, i.e. it will steadily move towards some stationary point of the function. However, "stationary point" does not always mean "solution". Nonsmooth problems often have "flat spots", i.e. areas where function do not change at all. Such "flat spots" are stationary points by definition, and algorithm may be caught here. Nonsmooth CONVEX tasks are not prone to this problem. Say, if your function has form f()=MAX(f0,f1,...), and f_i are convex, then f() is convex too and you have guaranteed convergence to solution. INPUT PARAMETERS: State - structure which stores algorithm state Radius - initial sampling radius, >=0. Internally multiplied by vector of per-variable scales specified by minnssetscale()). You should select relatively large sampling radius, roughly proportional to scaled length of the first steps of the algorithm. Something close to 0.1 in magnitude should be good for most problems. AGS solver can automatically decrease radius, so too large radius is not a problem (assuming that you won't choose so large radius that algorithm will sample function in too far away points, where gradient value is irrelevant). Too small radius won't cause algorithm to fail, but it may slow down algorithm (it may have to perform too short steps). Penalty - penalty coefficient for nonlinear constraints: * for problem with nonlinear constraints should be some problem-specific positive value, large enough that penalty term changes shape of the function. Starting from some problem-specific value penalty coefficient becomes large enough to exactly enforce nonlinear constraints; larger values do not improve precision. Increasing it too much may slow down convergence, so you should choose it carefully. * can be zero for problems WITHOUT nonlinear constraints (i.e. for unconstrained ones or ones with just box or linear constraints) * if you specify zero value for problem with at least one nonlinear constraint, algorithm will terminate with error code -1. ALGORITHM OUTLINE The very basic outline of unconstrained AGS algorithm is given below: 0. If sampling radius is below EpsX or we performed more then MaxIts iterations - STOP. 1. sample O(N) gradient values at random locations around current point; informally speaking, this sample is an implicit piecewise linear model of the function, although algorithm formulation does not mention that explicitly 2. solve quadratic programming problem in order to find descent direction 3. if QP solver tells us that we are near solution, decrease sampling radius and move to (0) 4. perform backtracking line search 5. after moving to new point, goto (0) As for the constraints: * box constraints are handled exactly by modification of the function being minimized * linear/nonlinear constraints are handled by adding L1 penalty. Because our solver can handle nonsmoothness, we can use L1 penalty function, which is an exact one (i.e. exact solution is returned under such penalty). * penalty coefficient for linear constraints is chosen automatically; however, penalty coefficient for nonlinear constraints must be specified by user. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetalgoags(minnsstate state, double radius, double penalty) { minns.minnssetalgoags(state.innerobj, radius, penalty, null); } public static void minnssetalgoags(minnsstate state, double radius, double penalty, alglib.xparams _params) { minns.minnssetalgoags(state.innerobj, radius, penalty, _params); } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to minnsoptimize(). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnssetxrep(minnsstate state, bool needxrep) { minns.minnssetxrep(state.innerobj, needxrep, null); } public static void minnssetxrep(minnsstate state, bool needxrep, alglib.xparams _params) { minns.minnssetxrep(state.innerobj, needxrep, _params); } /************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsrequesttermination(minnsstate state) { minns.minnsrequesttermination(state.innerobj, null); } public static void minnsrequesttermination(minnsstate state, alglib.xparams _params) { minns.minnsrequesttermination(state.innerobj, _params); } /************************************************************************* This function provides reverse communication interface Reverse communication interface is not documented or recommended to use. See below for functions which provide better documented API *************************************************************************/ public static bool minnsiteration(minnsstate state) { return minns.minnsiteration(state.innerobj, null); } public static bool minnsiteration(minnsstate state, alglib.xparams _params) { return minns.minnsiteration(state.innerobj, _params); } /************************************************************************* This family of functions is used to launcn iterations of nonlinear optimizer These functions accept following parameters: fvec - callback which calculates function vector fi[] at given point x jac - callback which calculates function vector fi[] and Jacobian jac at given point x rep - optional callback which is called after each iteration can be null obj - optional object which is passed to func/grad/hess/jac/rep can be null NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied Jacobian, and one which uses only function vector and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object you should choose appropriate variant of minnsoptimize() - one which accepts function AND Jacobian or one which accepts ONLY function. Be careful to choose variant of minnsoptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to minnsoptimize() and specific function used to create optimizer. | USER PASSED TO minnsoptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ minnscreatef() | works FAILS minnscreate() | FAILS works Here "FAILS" denotes inappropriate combinations of optimizer creation function and minnsoptimize() version. Attemps to use such combination will lead to exception. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsoptimize(minnsstate state, ndimensional_fvec fvec, ndimensional_rep rep, object obj) { minnsoptimize(state, fvec, rep, obj, null); } public static void minnsoptimize(minnsstate state, ndimensional_fvec fvec, ndimensional_rep rep, object obj, alglib.xparams _params) { if( fvec==null ) throw new alglibexception("ALGLIB: error in 'minnsoptimize()' (fvec is null)"); while( alglib.minnsiteration(state, _params) ) { if( state.needfi ) { fvec(state.x, state.innerobj.fi, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minnsoptimize' (some derivatives were not provided?)"); } } public static void minnsoptimize(minnsstate state, ndimensional_jac jac, ndimensional_rep rep, object obj) { minnsoptimize(state, jac, rep, obj, null); } public static void minnsoptimize(minnsstate state, ndimensional_jac jac, ndimensional_rep rep, object obj, alglib.xparams _params) { if( jac==null ) throw new alglibexception("ALGLIB: error in 'minnsoptimize()' (jac is null)"); while( alglib.minnsiteration(state, _params) ) { if( state.needfij ) { jac(state.x, state.innerobj.fi, state.innerobj.j, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minnsoptimize' (some derivatives were not provided?)"); } } /************************************************************************* MinNS results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report. You should check Rep.TerminationType in order to distinguish successful termination from unsuccessful one: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -3 box constraints are inconsistent * -1 inconsistent parameters were passed: * penalty parameter for minnssetalgoags() is zero, but we have nonlinear constraints set by minnssetnlc() * 2 sampling radius decreased below epsx * 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. * 8 User requested termination via minnsrequesttermination() -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsresults(minnsstate state, out double[] x, out minnsreport rep) { x = new double[0]; rep = new minnsreport(); minns.minnsresults(state.innerobj, ref x, rep.innerobj, null); } public static void minnsresults(minnsstate state, out double[] x, out minnsreport rep, alglib.xparams _params) { x = new double[0]; rep = new minnsreport(); minns.minnsresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* Buffered implementation of minnsresults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsresultsbuf(minnsstate state, ref double[] x, minnsreport rep) { minns.minnsresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void minnsresultsbuf(minnsstate state, ref double[] x, minnsreport rep, alglib.xparams _params) { minns.minnsresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* This subroutine restarts algorithm from new point. All optimization parameters (including constraints) are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure previously allocated with minnscreate() call. X - new starting point. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsrestartfrom(minnsstate state, double[] x) { minns.minnsrestartfrom(state.innerobj, x, null); } public static void minnsrestartfrom(minnsstate state, double[] x, alglib.xparams _params) { minns.minnsrestartfrom(state.innerobj, x, _params); } } public partial class alglib { /************************************************************************* *************************************************************************/ public class minasastate : alglibobject { // // Public declarations // public bool needfg { get { return _innerobj.needfg; } set { _innerobj.needfg = value; } } public bool xupdated { get { return _innerobj.xupdated; } set { _innerobj.xupdated = value; } } public double f { get { return _innerobj.f; } set { _innerobj.f = value; } } public double[] g { get { return _innerobj.g; } } public double[] x { get { return _innerobj.x; } } public minasastate() { _innerobj = new mincomp.minasastate(); } public override alglib.alglibobject make_copy() { return new minasastate((mincomp.minasastate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private mincomp.minasastate _innerobj; public mincomp.minasastate innerobj { get { return _innerobj; } } public minasastate(mincomp.minasastate obj) { _innerobj = obj; } } /************************************************************************* *************************************************************************/ public class minasareport : alglibobject { // // Public declarations // public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } public int nfev { get { return _innerobj.nfev; } set { _innerobj.nfev = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public int activeconstraints { get { return _innerobj.activeconstraints; } set { _innerobj.activeconstraints = value; } } public minasareport() { _innerobj = new mincomp.minasareport(); } public override alglib.alglibobject make_copy() { return new minasareport((mincomp.minasareport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private mincomp.minasareport _innerobj; public mincomp.minasareport innerobj { get { return _innerobj; } } public minasareport(mincomp.minasareport obj) { _innerobj = obj; } } /************************************************************************* Obsolete function, use MinLBFGSSetPrecDefault() instead. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetdefaultpreconditioner(minlbfgsstate state) { mincomp.minlbfgssetdefaultpreconditioner(state.innerobj, null); } public static void minlbfgssetdefaultpreconditioner(minlbfgsstate state, alglib.xparams _params) { mincomp.minlbfgssetdefaultpreconditioner(state.innerobj, _params); } /************************************************************************* Obsolete function, use MinLBFGSSetCholeskyPreconditioner() instead. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetcholeskypreconditioner(minlbfgsstate state, double[,] p, bool isupper) { mincomp.minlbfgssetcholeskypreconditioner(state.innerobj, p, isupper, null); } public static void minlbfgssetcholeskypreconditioner(minlbfgsstate state, double[,] p, bool isupper, alglib.xparams _params) { mincomp.minlbfgssetcholeskypreconditioner(state.innerobj, p, isupper, _params); } /************************************************************************* This is obsolete function which was used by previous version of the BLEIC optimizer. It does nothing in the current version of BLEIC. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetbarrierwidth(minbleicstate state, double mu) { mincomp.minbleicsetbarrierwidth(state.innerobj, mu, null); } public static void minbleicsetbarrierwidth(minbleicstate state, double mu, alglib.xparams _params) { mincomp.minbleicsetbarrierwidth(state.innerobj, mu, _params); } /************************************************************************* This is obsolete function which was used by previous version of the BLEIC optimizer. It does nothing in the current version of BLEIC. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetbarrierdecay(minbleicstate state, double mudecay) { mincomp.minbleicsetbarrierdecay(state.innerobj, mudecay, null); } public static void minbleicsetbarrierdecay(minbleicstate state, double mudecay, alglib.xparams _params) { mincomp.minbleicsetbarrierdecay(state.innerobj, mudecay, _params); } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 25.03.2010 by Bochkanov Sergey *************************************************************************/ public static void minasacreate(int n, double[] x, double[] bndl, double[] bndu, out minasastate state) { state = new minasastate(); mincomp.minasacreate(n, x, bndl, bndu, state.innerobj, null); } public static void minasacreate(int n, double[] x, double[] bndl, double[] bndu, out minasastate state, alglib.xparams _params) { state = new minasastate(); mincomp.minasacreate(n, x, bndl, bndu, state.innerobj, _params); } public static void minasacreate(double[] x, double[] bndl, double[] bndu, out minasastate state) { int n; if( (ap.len(x)!=ap.len(bndl)) || (ap.len(x)!=ap.len(bndu))) throw new alglibexception("Error while calling 'minasacreate': looks like one of arguments has wrong size"); state = new minasastate(); n = ap.len(x); mincomp.minasacreate(n, x, bndl, bndu, state.innerobj, null); return; } public static void minasacreate(double[] x, double[] bndl, double[] bndu, out minasastate state, alglib.xparams _params) { int n; if( (ap.len(x)!=ap.len(bndl)) || (ap.len(x)!=ap.len(bndu))) throw new alglibexception("Error while calling 'minasacreate': looks like one of arguments has wrong size"); state = new minasastate(); n = ap.len(x); mincomp.minasacreate(n, x, bndl, bndu, state.innerobj, _params); return; } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minasasetcond(minasastate state, double epsg, double epsf, double epsx, int maxits) { mincomp.minasasetcond(state.innerobj, epsg, epsf, epsx, maxits, null); } public static void minasasetcond(minasastate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { mincomp.minasasetcond(state.innerobj, epsg, epsf, epsx, maxits, _params); } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minasasetxrep(minasastate state, bool needxrep) { mincomp.minasasetxrep(state.innerobj, needxrep, null); } public static void minasasetxrep(minasastate state, bool needxrep, alglib.xparams _params) { mincomp.minasasetxrep(state.innerobj, needxrep, _params); } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minasasetalgorithm(minasastate state, int algotype) { mincomp.minasasetalgorithm(state.innerobj, algotype, null); } public static void minasasetalgorithm(minasastate state, int algotype, alglib.xparams _params) { mincomp.minasasetalgorithm(state.innerobj, algotype, _params); } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minasasetstpmax(minasastate state, double stpmax) { mincomp.minasasetstpmax(state.innerobj, stpmax, null); } public static void minasasetstpmax(minasastate state, double stpmax, alglib.xparams _params) { mincomp.minasasetstpmax(state.innerobj, stpmax, _params); } /************************************************************************* This function provides reverse communication interface Reverse communication interface is not documented or recommended to use. See below for functions which provide better documented API *************************************************************************/ public static bool minasaiteration(minasastate state) { return mincomp.minasaiteration(state.innerobj, null); } public static bool minasaiteration(minasastate state, alglib.xparams _params) { return mincomp.minasaiteration(state.innerobj, _params); } /************************************************************************* This family of functions is used to launcn iterations of nonlinear optimizer These functions accept following parameters: grad - callback which calculates function (or merit function) value func and gradient grad at given point x rep - optional callback which is called after each iteration can be null obj - optional object which is passed to func/grad/hess/jac/rep can be null -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minasaoptimize(minasastate state, ndimensional_grad grad, ndimensional_rep rep, object obj) { minasaoptimize(state, grad, rep, obj, null); } public static void minasaoptimize(minasastate state, ndimensional_grad grad, ndimensional_rep rep, object obj, alglib.xparams _params) { if( grad==null ) throw new alglibexception("ALGLIB: error in 'minasaoptimize()' (grad is null)"); while( alglib.minasaiteration(state, _params) ) { if( state.needfg ) { grad(state.x, ref state.innerobj.f, state.innerobj.g, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minasaoptimize' (some derivatives were not provided?)"); } } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minasaresults(minasastate state, out double[] x, out minasareport rep) { x = new double[0]; rep = new minasareport(); mincomp.minasaresults(state.innerobj, ref x, rep.innerobj, null); } public static void minasaresults(minasastate state, out double[] x, out minasareport rep, alglib.xparams _params) { x = new double[0]; rep = new minasareport(); mincomp.minasaresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minasaresultsbuf(minasastate state, ref double[] x, minasareport rep) { mincomp.minasaresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void minasaresultsbuf(minasastate state, ref double[] x, minasareport rep, alglib.xparams _params) { mincomp.minasaresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 30.07.2010 by Bochkanov Sergey *************************************************************************/ public static void minasarestartfrom(minasastate state, double[] x, double[] bndl, double[] bndu) { mincomp.minasarestartfrom(state.innerobj, x, bndl, bndu, null); } public static void minasarestartfrom(minasastate state, double[] x, double[] bndl, double[] bndu, alglib.xparams _params) { mincomp.minasarestartfrom(state.innerobj, x, bndl, bndu, _params); } } public partial class alglib { /************************************************************************* This object stores state of the nonlinear CG optimizer. You should use ALGLIB functions to work with this object. *************************************************************************/ public class mincgstate : alglibobject { // // Public declarations // public bool needf { get { return _innerobj.needf; } set { _innerobj.needf = value; } } public bool needfg { get { return _innerobj.needfg; } set { _innerobj.needfg = value; } } public bool xupdated { get { return _innerobj.xupdated; } set { _innerobj.xupdated = value; } } public double f { get { return _innerobj.f; } set { _innerobj.f = value; } } public double[] g { get { return _innerobj.g; } } public double[] x { get { return _innerobj.x; } } public mincgstate() { _innerobj = new mincg.mincgstate(); } public override alglib.alglibobject make_copy() { return new mincgstate((mincg.mincgstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private mincg.mincgstate _innerobj; public mincg.mincgstate innerobj { get { return _innerobj; } } public mincgstate(mincg.mincgstate obj) { _innerobj = obj; } } /************************************************************************* This structure stores optimization report: * IterationsCount total number of inner iterations * NFEV number of gradient evaluations * TerminationType termination type (see below) TERMINATION CODES TerminationType field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. 1 relative function improvement is no more than EpsF. 2 relative step is no more than EpsX. 4 gradient norm is no more than EpsG 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 terminated by user who called mincgrequesttermination(). X contains point which was "current accepted" when termination request was submitted. Other fields of this structure are not documented and should not be used! *************************************************************************/ public class mincgreport : alglibobject { // // Public declarations // public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } public int nfev { get { return _innerobj.nfev; } set { _innerobj.nfev = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public mincgreport() { _innerobj = new mincg.mincgreport(); } public override alglib.alglibobject make_copy() { return new mincgreport((mincg.mincgreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private mincg.mincgreport _innerobj; public mincg.mincgreport innerobj { get { return _innerobj; } } public mincgreport(mincg.mincgreport obj) { _innerobj = obj; } } /************************************************************************* NONLINEAR CONJUGATE GRADIENT METHOD DESCRIPTION: The subroutine minimizes function F(x) of N arguments by using one of the nonlinear conjugate gradient methods. These CG methods are globally convergent (even on non-convex functions) as long as grad(f) is Lipschitz continuous in a some neighborhood of the L = { x : f(x)<=f(x0) }. REQUIREMENTS: Algorithm will request following information during its operation: * function value F and its gradient G (simultaneously) at given point X USAGE: 1. User initializes algorithm state with MinCGCreate() call 2. User tunes solver parameters with MinCGSetCond(), MinCGSetStpMax() and other functions 3. User calls MinCGOptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G. 4. User calls MinCGResults() to get solution 5. Optionally, user may call MinCGRestartFrom() to solve another problem with same N but another starting point and/or another function. MinCGRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[0..N-1]. OUTPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 25.03.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgcreate(int n, double[] x, out mincgstate state) { state = new mincgstate(); mincg.mincgcreate(n, x, state.innerobj, null); } public static void mincgcreate(int n, double[] x, out mincgstate state, alglib.xparams _params) { state = new mincgstate(); mincg.mincgcreate(n, x, state.innerobj, _params); } public static void mincgcreate(double[] x, out mincgstate state) { int n; state = new mincgstate(); n = ap.len(x); mincg.mincgcreate(n, x, state.innerobj, null); return; } public static void mincgcreate(double[] x, out mincgstate state, alglib.xparams _params) { int n; state = new mincgstate(); n = ap.len(x); mincg.mincgcreate(n, x, state.innerobj, _params); return; } /************************************************************************* The subroutine is finite difference variant of MinCGCreate(). It uses finite differences in order to differentiate target function. Description below contains information which is specific to this function only. We recommend to read comments on MinCGCreate() in order to get more information about creation of CG optimizer. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[0..N-1]. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinCGSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large truncation errors, while too small step will result in too large numerical errors. 1.0E-6 can be good value to start with. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. L-BFGS needs exact gradient values. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 16.05.2011 by Bochkanov Sergey *************************************************************************/ public static void mincgcreatef(int n, double[] x, double diffstep, out mincgstate state) { state = new mincgstate(); mincg.mincgcreatef(n, x, diffstep, state.innerobj, null); } public static void mincgcreatef(int n, double[] x, double diffstep, out mincgstate state, alglib.xparams _params) { state = new mincgstate(); mincg.mincgcreatef(n, x, diffstep, state.innerobj, _params); } public static void mincgcreatef(double[] x, double diffstep, out mincgstate state) { int n; state = new mincgstate(); n = ap.len(x); mincg.mincgcreatef(n, x, diffstep, state.innerobj, null); return; } public static void mincgcreatef(double[] x, double diffstep, out mincgstate state, alglib.xparams _params) { int n; state = new mincgstate(); n = ap.len(x); mincg.mincgcreatef(n, x, diffstep, state.innerobj, _params); return; } /************************************************************************* This function sets stopping conditions for CG optimization algorithm. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if on k+1-th iteration the condition |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - ste pvector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinCGSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (small EpsX). -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetcond(mincgstate state, double epsg, double epsf, double epsx, int maxits) { mincg.mincgsetcond(state.innerobj, epsg, epsf, epsx, maxits, null); } public static void mincgsetcond(mincgstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { mincg.mincgsetcond(state.innerobj, epsg, epsf, epsx, maxits, _params); } /************************************************************************* This function sets scaling coefficients for CG optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of CG optimizer - step along I-th axis is equal to DiffStep*S[I]. In most optimizers (and in the CG too) scaling is NOT a form of preconditioning. It just affects stopping conditions. You should set preconditioner by separate call to one of the MinCGSetPrec...() functions. There is special preconditioning mode, however, which uses scaling coefficients to form diagonal preconditioning matrix. You can turn this mode on, if you want. But you should understand that scaling is not the same thing as preconditioning - these are two different, although related forms of tuning solver. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void mincgsetscale(mincgstate state, double[] s) { mincg.mincgsetscale(state.innerobj, s, null); } public static void mincgsetscale(mincgstate state, double[] s, alglib.xparams _params) { mincg.mincgsetscale(state.innerobj, s, _params); } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinCGOptimize(). -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetxrep(mincgstate state, bool needxrep) { mincg.mincgsetxrep(state.innerobj, needxrep, null); } public static void mincgsetxrep(mincgstate state, bool needxrep, alglib.xparams _params) { mincg.mincgsetxrep(state.innerobj, needxrep, _params); } /************************************************************************* This function sets CG algorithm. INPUT PARAMETERS: State - structure which stores algorithm state CGType - algorithm type: * -1 automatic selection of the best algorithm * 0 DY (Dai and Yuan) algorithm * 1 Hybrid DY-HS algorithm -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetcgtype(mincgstate state, int cgtype) { mincg.mincgsetcgtype(state.innerobj, cgtype, null); } public static void mincgsetcgtype(mincgstate state, int cgtype, alglib.xparams _params) { mincg.mincgsetcgtype(state.innerobj, cgtype, _params); } /************************************************************************* This function sets maximum step length INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetstpmax(mincgstate state, double stpmax) { mincg.mincgsetstpmax(state.innerobj, stpmax, null); } public static void mincgsetstpmax(mincgstate state, double stpmax, alglib.xparams _params) { mincg.mincgsetstpmax(state.innerobj, stpmax, _params); } /************************************************************************* This function allows to suggest initial step length to the CG algorithm. Suggested step length is used as starting point for the line search. It can be useful when you have badly scaled problem, i.e. when ||grad|| (which is used as initial estimate for the first step) is many orders of magnitude different from the desired step. Line search may fail on such problems without good estimate of initial step length. Imagine, for example, problem with ||grad||=10^50 and desired step equal to 0.1 Line search function will use 10^50 as initial step, then it will decrease step length by 2 (up to 20 attempts) and will get 10^44, which is still too large. This function allows us to tell than line search should be started from some moderate step length, like 1.0, so algorithm will be able to detect desired step length in a several searches. Default behavior (when no step is suggested) is to use preconditioner, if it is available, to generate initial estimate of step length. This function influences only first iteration of algorithm. It should be called between MinCGCreate/MinCGRestartFrom() call and MinCGOptimize call. Suggested step is ignored if you have preconditioner. INPUT PARAMETERS: State - structure used to store algorithm state. Stp - initial estimate of the step length. Can be zero (no estimate). -- ALGLIB -- Copyright 30.07.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsuggeststep(mincgstate state, double stp) { mincg.mincgsuggeststep(state.innerobj, stp, null); } public static void mincgsuggeststep(mincgstate state, double stp, alglib.xparams _params) { mincg.mincgsuggeststep(state.innerobj, stp, _params); } /************************************************************************* Modification of the preconditioner: preconditioning is turned off. INPUT PARAMETERS: State - structure which stores algorithm state NOTE: you can change preconditioner "on the fly", during algorithm iterations. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetprecdefault(mincgstate state) { mincg.mincgsetprecdefault(state.innerobj, null); } public static void mincgsetprecdefault(mincgstate state, alglib.xparams _params) { mincg.mincgsetprecdefault(state.innerobj, _params); } /************************************************************************* Modification of the preconditioner: diagonal of approximate Hessian is used. INPUT PARAMETERS: State - structure which stores algorithm state D - diagonal of the approximate Hessian, array[0..N-1], (if larger, only leading N elements are used). NOTE: you can change preconditioner "on the fly", during algorithm iterations. NOTE 2: D[i] should be positive. Exception will be thrown otherwise. NOTE 3: you should pass diagonal of approximate Hessian - NOT ITS INVERSE. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetprecdiag(mincgstate state, double[] d) { mincg.mincgsetprecdiag(state.innerobj, d, null); } public static void mincgsetprecdiag(mincgstate state, double[] d, alglib.xparams _params) { mincg.mincgsetprecdiag(state.innerobj, d, _params); } /************************************************************************* Modification of the preconditioner: scale-based diagonal preconditioning. This preconditioning mode can be useful when you don't have approximate diagonal of Hessian, but you know that your variables are badly scaled (for example, one variable is in [1,10], and another in [1000,100000]), and most part of the ill-conditioning comes from different scales of vars. In this case simple scale-based preconditioner, with H[i] = 1/(s[i]^2), can greatly improve convergence. IMPRTANT: you should set scale of your variables with MinCGSetScale() call (before or after MinCGSetPrecScale() call). Without knowledge of the scale of your variables scale-based preconditioner will be just unit matrix. INPUT PARAMETERS: State - structure which stores algorithm state NOTE: you can change preconditioner "on the fly", during algorithm iterations. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetprecscale(mincgstate state) { mincg.mincgsetprecscale(state.innerobj, null); } public static void mincgsetprecscale(mincgstate state, alglib.xparams _params) { mincg.mincgsetprecscale(state.innerobj, _params); } /************************************************************************* This function provides reverse communication interface Reverse communication interface is not documented or recommended to use. See below for functions which provide better documented API *************************************************************************/ public static bool mincgiteration(mincgstate state) { return mincg.mincgiteration(state.innerobj, null); } public static bool mincgiteration(mincgstate state, alglib.xparams _params) { return mincg.mincgiteration(state.innerobj, _params); } /************************************************************************* This family of functions is used to launcn iterations of nonlinear optimizer These functions accept following parameters: func - callback which calculates function (or merit function) value func at given point x grad - callback which calculates function (or merit function) value func and gradient grad at given point x rep - optional callback which is called after each iteration can be null obj - optional object which is passed to func/grad/hess/jac/rep can be null NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied gradient, and one which uses function value only and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object (either MinCGCreate() for analytical gradient or MinCGCreateF() for numerical differentiation) you should choose appropriate variant of MinCGOptimize() - one which accepts function AND gradient or one which accepts function ONLY. Be careful to choose variant of MinCGOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinCGOptimize() and specific function used to create optimizer. | USER PASSED TO MinCGOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinCGCreateF() | work FAIL MinCGCreate() | FAIL work Here "FAIL" denotes inappropriate combinations of optimizer creation function and MinCGOptimize() version. Attemps to use such combination (for example, to create optimizer with MinCGCreateF() and to pass gradient information to MinCGOptimize()) will lead to exception being thrown. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 20.04.2009 by Bochkanov Sergey *************************************************************************/ public static void mincgoptimize(mincgstate state, ndimensional_func func, ndimensional_rep rep, object obj) { mincgoptimize(state, func, rep, obj, null); } public static void mincgoptimize(mincgstate state, ndimensional_func func, ndimensional_rep rep, object obj, alglib.xparams _params) { if( func==null ) throw new alglibexception("ALGLIB: error in 'mincgoptimize()' (func is null)"); while( alglib.mincgiteration(state, _params) ) { if( state.needf ) { func(state.x, ref state.innerobj.f, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'mincgoptimize' (some derivatives were not provided?)"); } } public static void mincgoptimize(mincgstate state, ndimensional_grad grad, ndimensional_rep rep, object obj) { mincgoptimize(state, grad, rep, obj, null); } public static void mincgoptimize(mincgstate state, ndimensional_grad grad, ndimensional_rep rep, object obj, alglib.xparams _params) { if( grad==null ) throw new alglibexception("ALGLIB: error in 'mincgoptimize()' (grad is null)"); while( alglib.mincgiteration(state, _params) ) { if( state.needfg ) { grad(state.x, ref state.innerobj.f, state.innerobj.g, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'mincgoptimize' (some derivatives were not provided?)"); } } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with mincgoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with mincgsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardgradient(mincgstate state, double teststep) { mincg.mincgoptguardgradient(state.innerobj, teststep, null); } public static void mincgoptguardgradient(mincgstate state, double teststep, alglib.xparams _params) { mincg.mincgoptguardgradient(state.innerobj, teststep, _params); } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) b) nonsmooth target function (non-C1) Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardsmoothness(mincgstate state, int level) { mincg.mincgoptguardsmoothness(state.innerobj, level, null); } public static void mincgoptguardsmoothness(mincgstate state, int level, alglib.xparams _params) { mincg.mincgoptguardsmoothness(state.innerobj, level, _params); } public static void mincgoptguardsmoothness(mincgstate state) { int level; level = 1; mincg.mincgoptguardsmoothness(state.innerobj, level, null); return; } public static void mincgoptguardsmoothness(mincgstate state, alglib.xparams _params) { int level; level = 1; mincg.mincgoptguardsmoothness(state.innerobj, level, _params); return; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * mincgoptguardgradient() for gradient verification * mincgoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradvidx for specific variable (gradient element) suspected * rep.badgradxbase, a point where gradient is tested * rep.badgraduser, user-provided gradient (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.badgradnum, reference gradient obtained via numerical differentiation (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.nonc0suspected * rep.nonc1suspected === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * mincgoptguardnonc1test0results() * mincgoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardresults(mincgstate state, out optguardreport rep) { rep = new optguardreport(); mincg.mincgoptguardresults(state.innerobj, rep.innerobj, null); } public static void mincgoptguardresults(mincgstate state, out optguardreport rep, alglib.xparams _params) { rep = new optguardreport(); mincg.mincgoptguardresults(state.innerobj, rep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardnonc1test0results(mincgstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); mincg.mincgoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void mincgoptguardnonc1test0results(mincgstate state, out optguardnonc1test0report strrep, out optguardnonc1test0report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test0report(); lngrep = new optguardnonc1test0report(); mincg.mincgoptguardnonc1test0results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardnonc1test1results(mincgstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); mincg.mincgoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, null); } public static void mincgoptguardnonc1test1results(mincgstate state, out optguardnonc1test1report strrep, out optguardnonc1test1report lngrep, alglib.xparams _params) { strrep = new optguardnonc1test1report(); lngrep = new optguardnonc1test1report(); mincg.mincgoptguardnonc1test1results(state.innerobj, strrep.innerobj, lngrep.innerobj, _params); } /************************************************************************* Conjugate gradient results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report: * Rep.TerminationType completetion code: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -7 gradient verification failed. See MinCGSetGradientCheck() for more information. * 1 relative function improvement is no more than EpsF. * 2 relative step is no more than EpsX. * 4 gradient norm is no more than EpsG * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible, we return best X found so far * 8 terminated by user * Rep.IterationsCount contains iterations count * NFEV countains number of function calculations -- ALGLIB -- Copyright 20.04.2009 by Bochkanov Sergey *************************************************************************/ public static void mincgresults(mincgstate state, out double[] x, out mincgreport rep) { x = new double[0]; rep = new mincgreport(); mincg.mincgresults(state.innerobj, ref x, rep.innerobj, null); } public static void mincgresults(mincgstate state, out double[] x, out mincgreport rep, alglib.xparams _params) { x = new double[0]; rep = new mincgreport(); mincg.mincgresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* Conjugate gradient results Buffered implementation of MinCGResults(), which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 20.04.2009 by Bochkanov Sergey *************************************************************************/ public static void mincgresultsbuf(mincgstate state, ref double[] x, mincgreport rep) { mincg.mincgresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void mincgresultsbuf(mincgstate state, ref double[] x, mincgreport rep, alglib.xparams _params) { mincg.mincgresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* This subroutine restarts CG algorithm from new point. All optimization parameters are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure used to store algorithm state. X - new starting point. -- ALGLIB -- Copyright 30.07.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgrestartfrom(mincgstate state, double[] x) { mincg.mincgrestartfrom(state.innerobj, x, null); } public static void mincgrestartfrom(mincgstate state, double[] x, alglib.xparams _params) { mincg.mincgrestartfrom(state.innerobj, x, _params); } /************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 08.10.2014 by Bochkanov Sergey *************************************************************************/ public static void mincgrequesttermination(mincgstate state) { mincg.mincgrequesttermination(state.innerobj, null); } public static void mincgrequesttermination(mincgstate state, alglib.xparams _params) { mincg.mincgrequesttermination(state.innerobj, _params); } } public partial class alglib { /************************************************************************* Levenberg-Marquardt optimizer. This structure should be created using one of the MinLMCreate???() functions. You should not access its fields directly; use ALGLIB functions to work with it. *************************************************************************/ public class minlmstate : alglibobject { // // Public declarations // public bool needf { get { return _innerobj.needf; } set { _innerobj.needf = value; } } public bool needfg { get { return _innerobj.needfg; } set { _innerobj.needfg = value; } } public bool needfgh { get { return _innerobj.needfgh; } set { _innerobj.needfgh = value; } } public bool needfi { get { return _innerobj.needfi; } set { _innerobj.needfi = value; } } public bool needfij { get { return _innerobj.needfij; } set { _innerobj.needfij = value; } } public bool xupdated { get { return _innerobj.xupdated; } set { _innerobj.xupdated = value; } } public double f { get { return _innerobj.f; } set { _innerobj.f = value; } } public double[] fi { get { return _innerobj.fi; } } public double[] g { get { return _innerobj.g; } } public double[,] h { get { return _innerobj.h; } } public double[,] j { get { return _innerobj.j; } } public double[] x { get { return _innerobj.x; } } public minlmstate() { _innerobj = new minlm.minlmstate(); } public override alglib.alglibobject make_copy() { return new minlmstate((minlm.minlmstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minlm.minlmstate _innerobj; public minlm.minlmstate innerobj { get { return _innerobj; } } public minlmstate(minlm.minlmstate obj) { _innerobj = obj; } } /************************************************************************* Optimization report, filled by MinLMResults() function FIELDS: * TerminationType, completetion code: * -8 optimizer detected NAN/INF values either in the function itself, or in its Jacobian * -5 inappropriate solver was used: * solver created with minlmcreatefgh() used on problem with general linear constraints (set with minlmsetlc() call). * -3 constraints are inconsistent * 2 relative step is no more than EpsX. * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible * 8 terminated by user who called MinLMRequestTermination(). X contains point which was "current accepted" when termination request was submitted. * IterationsCount, contains iterations count * NFunc, number of function calculations * NJac, number of Jacobi matrix calculations * NGrad, number of gradient calculations * NHess, number of Hessian calculations * NCholesky, number of Cholesky decomposition calculations *************************************************************************/ public class minlmreport : alglibobject { // // Public declarations // public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } public int nfunc { get { return _innerobj.nfunc; } set { _innerobj.nfunc = value; } } public int njac { get { return _innerobj.njac; } set { _innerobj.njac = value; } } public int ngrad { get { return _innerobj.ngrad; } set { _innerobj.ngrad = value; } } public int nhess { get { return _innerobj.nhess; } set { _innerobj.nhess = value; } } public int ncholesky { get { return _innerobj.ncholesky; } set { _innerobj.ncholesky = value; } } public minlmreport() { _innerobj = new minlm.minlmreport(); } public override alglib.alglibobject make_copy() { return new minlmreport((minlm.minlmreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // private minlm.minlmreport _innerobj; public minlm.minlmreport innerobj { get { return _innerobj; } } public minlmreport(minlm.minlmreport obj) { _innerobj = obj; } } /************************************************************************* IMPROVED LEVENBERG-MARQUARDT METHOD FOR NON-LINEAR LEAST SQUARES OPTIMIZATION DESCRIPTION: This function is used to find minimum of function which is represented as sum of squares: F(x) = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1]) using value of function vector f[] and Jacobian of f[]. REQUIREMENTS: This algorithm will request following information during its operation: * function vector f[] at given point X * function vector f[] and Jacobian of f[] (simultaneously) at given point There are several overloaded versions of MinLMOptimize() function which correspond to different LM-like optimization algorithms provided by this unit. You should choose version which accepts fvec() and jac() callbacks. First one is used to calculate f[] at given point, second one calculates f[] and Jacobian df[i]/dx[j]. You can try to initialize MinLMState structure with VJ function and then use incorrect version of MinLMOptimize() (for example, version which works with general form function and does not provide Jacobian), but it will lead to exception being thrown after first attempt to calculate Jacobian. USAGE: 1. User initializes algorithm state with MinLMCreateVJ() call 2. User tunes solver parameters with MinLMSetCond(), MinLMSetStpMax() and other functions 3. User calls MinLMOptimize() function which takes algorithm state and callback functions. 4. User calls MinLMResults() to get solution 5. Optionally, user may call MinLMRestartFrom() to solve another problem with same N/M but another starting point and/or another function. MinLMRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - dimension, N>1 * if given, only leading N elements of X are used * if not given, automatically determined from size of X M - number of functions f[i] X - initial solution, array[0..N-1] OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. you may tune stopping conditions with MinLMSetCond() function 2. if target function contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow, use MinLMSetStpMax() function to bound algorithm's steps. -- ALGLIB -- Copyright 30.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmcreatevj(int n, int m, double[] x, out minlmstate state) { state = new minlmstate(); minlm.minlmcreatevj(n, m, x, state.innerobj, null); } public static void minlmcreatevj(int n, int m, double[] x, out minlmstate state, alglib.xparams _params) { state = new minlmstate(); minlm.minlmcreatevj(n, m, x, state.innerobj, _params); } public static void minlmcreatevj(int m, double[] x, out minlmstate state) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatevj(n, m, x, state.innerobj, null); return; } public static void minlmcreatevj(int m, double[] x, out minlmstate state, alglib.xparams _params) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatevj(n, m, x, state.innerobj, _params); return; } /************************************************************************* IMPROVED LEVENBERG-MARQUARDT METHOD FOR NON-LINEAR LEAST SQUARES OPTIMIZATION DESCRIPTION: This function is used to find minimum of function which is represented as sum of squares: F(x) = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1]) using value of function vector f[] only. Finite differences are used to calculate Jacobian. REQUIREMENTS: This algorithm will request following information during its operation: * function vector f[] at given point X There are several overloaded versions of MinLMOptimize() function which correspond to different LM-like optimization algorithms provided by this unit. You should choose version which accepts fvec() callback. You can try to initialize MinLMState structure with VJ function and then use incorrect version of MinLMOptimize() (for example, version which works with general form function and does not accept function vector), but it will lead to exception being thrown after first attempt to calculate Jacobian. USAGE: 1. User initializes algorithm state with MinLMCreateV() call 2. User tunes solver parameters with MinLMSetCond(), MinLMSetStpMax() and other functions 3. User calls MinLMOptimize() function which takes algorithm state and callback functions. 4. User calls MinLMResults() to get solution 5. Optionally, user may call MinLMRestartFrom() to solve another problem with same N/M but another starting point and/or another function. MinLMRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - dimension, N>1 * if given, only leading N elements of X are used * if not given, automatically determined from size of X M - number of functions f[i] X - initial solution, array[0..N-1] DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state See also MinLMIteration, MinLMResults. NOTES: 1. you may tune stopping conditions with MinLMSetCond() function 2. if target function contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow, use MinLMSetStpMax() function to bound algorithm's steps. -- ALGLIB -- Copyright 30.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmcreatev(int n, int m, double[] x, double diffstep, out minlmstate state) { state = new minlmstate(); minlm.minlmcreatev(n, m, x, diffstep, state.innerobj, null); } public static void minlmcreatev(int n, int m, double[] x, double diffstep, out minlmstate state, alglib.xparams _params) { state = new minlmstate(); minlm.minlmcreatev(n, m, x, diffstep, state.innerobj, _params); } public static void minlmcreatev(int m, double[] x, double diffstep, out minlmstate state) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatev(n, m, x, diffstep, state.innerobj, null); return; } public static void minlmcreatev(int m, double[] x, double diffstep, out minlmstate state, alglib.xparams _params) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatev(n, m, x, diffstep, state.innerobj, _params); return; } /************************************************************************* LEVENBERG-MARQUARDT-LIKE METHOD FOR NON-LINEAR OPTIMIZATION DESCRIPTION: This function is used to find minimum of general form (not "sum-of- -squares") function F = F(x[0], ..., x[n-1]) using its gradient and Hessian. Levenberg-Marquardt modification with L-BFGS pre-optimization and internal pre-conditioned L-BFGS optimization after each Levenberg-Marquardt step is used. REQUIREMENTS: This algorithm will request following information during its operation: * function value F at given point X * F and gradient G (simultaneously) at given point X * F, G and Hessian H (simultaneously) at given point X There are several overloaded versions of MinLMOptimize() function which correspond to different LM-like optimization algorithms provided by this unit. You should choose version which accepts func(), grad() and hess() function pointers. First pointer is used to calculate F at given point, second one calculates F(x) and grad F(x), third one calculates F(x), grad F(x), hess F(x). You can try to initialize MinLMState structure with FGH-function and then use incorrect version of MinLMOptimize() (for example, version which does not provide Hessian matrix), but it will lead to exception being thrown after first attempt to calculate Hessian. USAGE: 1. User initializes algorithm state with MinLMCreateFGH() call 2. User tunes solver parameters with MinLMSetCond(), MinLMSetStpMax() and other functions 3. User calls MinLMOptimize() function which takes algorithm state and pointers (delegates, etc.) to callback functions. 4. User calls MinLMResults() to get solution 5. Optionally, user may call MinLMRestartFrom() to solve another problem with same N but another starting point and/or another function. MinLMRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - dimension, N>1 * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - initial solution, array[0..N-1] OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. you may tune stopping conditions with MinLMSetCond() function 2. if target function contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow, use MinLMSetStpMax() function to bound algorithm's steps. -- ALGLIB -- Copyright 30.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmcreatefgh(int n, double[] x, out minlmstate state) { state = new minlmstate(); minlm.minlmcreatefgh(n, x, state.innerobj, null); } public static void minlmcreatefgh(int n, double[] x, out minlmstate state, alglib.xparams _params) { state = new minlmstate(); minlm.minlmcreatefgh(n, x, state.innerobj, _params); } public static void minlmcreatefgh(double[] x, out minlmstate state) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatefgh(n, x, state.innerobj, null); return; } public static void minlmcreatefgh(double[] x, out minlmstate state, alglib.xparams _params) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatefgh(n, x, state.innerobj, _params); return; } /************************************************************************* This function sets stopping conditions for Levenberg-Marquardt optimization algorithm. INPUT PARAMETERS: State - structure which stores algorithm state EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - ste pvector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinLMSetScale() Recommended values: 1E-9 ... 1E-12. MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Only Levenberg-Marquardt iterations are counted (L-BFGS/CG iterations are NOT counted because their cost is very low compared to that of LM). Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (small EpsX). NOTE: it is not recommended to set large EpsX (say, 0.001). Because LM is a second-order method, it performs very precise steps anyway. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlmsetcond(minlmstate state, double epsx, int maxits) { minlm.minlmsetcond(state.innerobj, epsx, maxits, null); } public static void minlmsetcond(minlmstate state, double epsx, int maxits, alglib.xparams _params) { minlm.minlmsetcond(state.innerobj, epsx, maxits, _params); } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinLMOptimize(). Both Levenberg-Marquardt and internal L-BFGS iterations are reported. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlmsetxrep(minlmstate state, bool needxrep) { minlm.minlmsetxrep(state.innerobj, needxrep, null); } public static void minlmsetxrep(minlmstate state, bool needxrep, alglib.xparams _params) { minlm.minlmsetxrep(state.innerobj, needxrep, _params); } /************************************************************************* This function sets maximum step length INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. NOTE: non-zero StpMax leads to moderate performance degradation because intermediate step of preconditioned L-BFGS optimization is incompatible with limits on step size. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlmsetstpmax(minlmstate state, double stpmax) { minlm.minlmsetstpmax(state.innerobj, stpmax, null); } public static void minlmsetstpmax(minlmstate state, double stpmax, alglib.xparams _params) { minlm.minlmsetstpmax(state.innerobj, stpmax, _params); } /************************************************************************* This function sets scaling coefficients for LM optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Generally, scale is NOT considered to be a form of preconditioner. But LM optimizer is unique in that it uses scaling matrix both in the stopping condition tests and as Marquardt damping factor. Proper scaling is very important for the algorithm performance. It is less important for the quality of results, but still has some influence (it is easier to converge when variables are properly scaled, so premature stopping is possible when very badly scalled variables are combined with relaxed stopping conditions). INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlmsetscale(minlmstate state, double[] s) { minlm.minlmsetscale(state.innerobj, s, null); } public static void minlmsetscale(minlmstate state, double[] s, alglib.xparams _params) { minlm.minlmsetscale(state.innerobj, s, _params); } /************************************************************************* This function sets boundary constraints for LM optimizer Boundary constraints are inactive by default (after initial creation). They are preserved until explicitly turned off with another SetBC() call. INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF (latter is recommended because it will allow solver to use better algorithm). BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF (latter is recommended because it will allow solver to use better algorithm). NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: this solver has following useful properties: * bound constraints are always satisfied exactly * function is evaluated only INSIDE area specified by bound constraints or at its boundary -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlmsetbc(minlmstate state, double[] bndl, double[] bndu) { minlm.minlmsetbc(state.innerobj, bndl, bndu, null); } public static void minlmsetbc(minlmstate state, double[] bndl, double[] bndu, alglib.xparams _params) { minlm.minlmsetbc(state.innerobj, bndl, bndu, _params); } /************************************************************************* This function sets general linear constraints for LM optimizer Linear constraints are inactive by default (after initial creation). They are preserved until explicitly turned off with another minlmsetlc() call. INPUT PARAMETERS: State - structure stores algorithm state C - linear constraints, array[K,N+1]. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. CT - type of constraints, array[K]: * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT IMPORTANT: if you have linear constraints, it is strongly recommended to set scale of variables with minlmsetscale(). QP solver which is used to calculate linearly constrained steps heavily relies on good scaling of input problems. IMPORTANT: solvers created with minlmcreatefgh() do not support linear constraints. NOTE: linear (non-bound) constraints are satisfied only approximately - there always exists some violation due to numerical errors and algorithmic limitations. NOTE: general linear constraints add significant overhead to solution process. Although solver performs roughly same amount of iterations (when compared with similar box-only constrained problem), each iteration now involves solution of linearly constrained QP subproblem, which requires ~3-5 times more Cholesky decompositions. Thus, if you can reformulate your problem in such way this it has only box constraints, it may be beneficial to do so. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlmsetlc(minlmstate state, double[,] c, int[] ct, int k) { minlm.minlmsetlc(state.innerobj, c, ct, k, null); } public static void minlmsetlc(minlmstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { minlm.minlmsetlc(state.innerobj, c, ct, k, _params); } public static void minlmsetlc(minlmstate state, double[,] c, int[] ct) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minlmsetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minlm.minlmsetlc(state.innerobj, c, ct, k, null); return; } public static void minlmsetlc(minlmstate state, double[,] c, int[] ct, alglib.xparams _params) { int k; if( (ap.rows(c)!=ap.len(ct))) throw new alglibexception("Error while calling 'minlmsetlc': looks like one of arguments has wrong size"); k = ap.rows(c); minlm.minlmsetlc(state.innerobj, c, ct, k, _params); return; } /************************************************************************* This function is used to change acceleration settings You can choose between three acceleration strategies: * AccType=0, no acceleration. * AccType=1, secant updates are used to update quadratic model after each iteration. After fixed number of iterations (or after model breakdown) we recalculate quadratic model using analytic Jacobian or finite differences. Number of secant-based iterations depends on optimization settings: about 3 iterations - when we have analytic Jacobian, up to 2*N iterations - when we use finite differences to calculate Jacobian. AccType=1 is recommended when Jacobian calculation cost is prohibitively high (several Mx1 function vector calculations followed by several NxN Cholesky factorizations are faster than calculation of one M*N Jacobian). It should also be used when we have no Jacobian, because finite difference approximation takes too much time to compute. Table below list optimization protocols (XYZ protocol corresponds to MinLMCreateXYZ) and acceleration types they support (and use by default). ACCELERATION TYPES SUPPORTED BY OPTIMIZATION PROTOCOLS: protocol 0 1 comment V + + VJ + + FGH + DEFAULT VALUES: protocol 0 1 comment V x without acceleration it is so slooooooooow VJ x FGH x NOTE: this function should be called before optimization. Attempt to call it during algorithm iterations may result in unexpected behavior. NOTE: attempt to call this function with unsupported protocol/acceleration combination will result in exception being thrown. -- ALGLIB -- Copyright 14.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlmsetacctype(minlmstate state, int acctype) { minlm.minlmsetacctype(state.innerobj, acctype, null); } public static void minlmsetacctype(minlmstate state, int acctype, alglib.xparams _params) { minlm.minlmsetacctype(state.innerobj, acctype, _params); } /************************************************************************* This function provides reverse communication interface Reverse communication interface is not documented or recommended to use. See below for functions which provide better documented API *************************************************************************/ public static bool minlmiteration(minlmstate state) { return minlm.minlmiteration(state.innerobj, null); } public static bool minlmiteration(minlmstate state, alglib.xparams _params) { return minlm.minlmiteration(state.innerobj, _params); } /************************************************************************* This family of functions is used to launcn iterations of nonlinear optimizer These functions accept following parameters: func - callback which calculates function (or merit function) value func at given point x grad - callback which calculates function (or merit function) value func and gradient grad at given point x hess - callback which calculates function (or merit function) value func, gradient grad and Hessian hess at given point x fvec - callback which calculates function vector fi[] at given point x jac - callback which calculates function vector fi[] and Jacobian jac at given point x rep - optional callback which is called after each iteration can be null obj - optional object which is passed to func/grad/hess/jac/rep can be null NOTES: 1. Depending on function used to create state structure, this algorithm may accept Jacobian and/or Hessian and/or gradient. According to the said above, there ase several versions of this function, which accept different sets of callbacks. This flexibility opens way to subtle errors - you may create state with MinLMCreateFGH() (optimization using Hessian), but call function which does not accept Hessian. So when algorithm will request Hessian, there will be no callback to call. In this case exception will be thrown. Be careful to avoid such errors because there is no way to find them at compile time - you can see them at runtime only. -- ALGLIB -- Copyright 10.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmoptimize(minlmstate state, ndimensional_fvec fvec, ndimensional_rep rep, object obj) { minlmoptimize(state, fvec, rep, obj, null); } public static void minlmoptimize(minlmstate state, ndimensional_fvec fvec, ndimensional_rep rep, object obj, alglib.xparams _params) { if( fvec==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (fvec is null)"); while( alglib.minlmiteration(state, _params) ) { if( state.needfi ) { fvec(state.x, state.innerobj.fi, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minlmoptimize' (some derivatives were not provided?)"); } } public static void minlmoptimize(minlmstate state, ndimensional_fvec fvec, ndimensional_jac jac, ndimensional_rep rep, object obj) { minlmoptimize(state, fvec, jac, rep, obj, null); } public static void minlmoptimize(minlmstate state, ndimensional_fvec fvec, ndimensional_jac jac, ndimensional_rep rep, object obj, alglib.xparams _params) { if( fvec==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (fvec is null)"); if( jac==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (jac is null)"); while( alglib.minlmiteration(state, _params) ) { if( state.needfi ) { fvec(state.x, state.innerobj.fi, obj); continue; } if( state.needfij ) { jac(state.x, state.innerobj.fi, state.innerobj.j, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minlmoptimize' (some derivatives were not provided?)"); } } public static void minlmoptimize(minlmstate state, ndimensional_func func, ndimensional_grad grad, ndimensional_hess hess, ndimensional_rep rep, object obj) { minlmoptimize(state, func, grad, hess, rep, obj, null); } public static void minlmoptimize(minlmstate state, ndimensional_func func, ndimensional_grad grad, ndimensional_hess hess, ndimensional_rep rep, object obj, alglib.xparams _params) { if( func==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (func is null)"); if( grad==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (grad is null)"); if( hess==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (hess is null)"); while( alglib.minlmiteration(state, _params) ) { if( state.needf ) { func(state.x, ref state.innerobj.f, obj); continue; } if( state.needfg ) { grad(state.x, ref state.innerobj.f, state.innerobj.g, obj); continue; } if( state.needfgh ) { hess(state.x, ref state.innerobj.f, state.innerobj.g, state.innerobj.h, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minlmoptimize' (some derivatives were not provided?)"); } } public static void minlmoptimize(minlmstate state, ndimensional_func func, ndimensional_jac jac, ndimensional_rep rep, object obj) { minlmoptimize(state, func, jac, rep, obj, null); } public static void minlmoptimize(minlmstate state, ndimensional_func func, ndimensional_jac jac, ndimensional_rep rep, object obj, alglib.xparams _params) { if( func==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (func is null)"); if( jac==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (jac is null)"); while( alglib.minlmiteration(state, _params) ) { if( state.needf ) { func(state.x, ref state.innerobj.f, obj); continue; } if( state.needfij ) { jac(state.x, state.innerobj.fi, state.innerobj.j, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minlmoptimize' (some derivatives were not provided?)"); } } public static void minlmoptimize(minlmstate state, ndimensional_func func, ndimensional_grad grad, ndimensional_jac jac, ndimensional_rep rep, object obj) { minlmoptimize(state, func, grad, jac, rep, obj, null); } public static void minlmoptimize(minlmstate state, ndimensional_func func, ndimensional_grad grad, ndimensional_jac jac, ndimensional_rep rep, object obj, alglib.xparams _params) { if( func==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (func is null)"); if( grad==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (grad is null)"); if( jac==null ) throw new alglibexception("ALGLIB: error in 'minlmoptimize()' (jac is null)"); while( alglib.minlmiteration(state, _params) ) { if( state.needf ) { func(state.x, ref state.innerobj.f, obj); continue; } if( state.needfg ) { grad(state.x, ref state.innerobj.f, state.innerobj.g, obj); continue; } if( state.needfij ) { jac(state.x, state.innerobj.fi, state.innerobj.j, obj); continue; } if( state.innerobj.xupdated ) { if( rep!=null ) rep(state.innerobj.x, state.innerobj.f, obj); continue; } throw new alglibexception("ALGLIB: error in 'minlmoptimize' (some derivatives were not provided?)"); } } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic Jacobian. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function vector at the initial point (note: future versions may also perform check at the final point) and compares numerical Jacobian with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both Jacobians, and specific components highlighted as suspicious by the OptGuard. The OptGuard report can be retrieved with minlmoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minlmsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minlmoptguardgradient(minlmstate state, double teststep) { minlm.minlmoptguardgradient(state.innerobj, teststep, null); } public static void minlmoptguardgradient(minlmstate state, double teststep, alglib.xparams _params) { minlm.minlmoptguardgradient(state.innerobj, teststep, _params); } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. OptGuard checks analytic Jacobian against reference value obtained by numerical differentiation with user-specified step. NOTE: other optimizers perform additional OptGuard checks for things like C0/C1-continuity violations. However, LM optimizer can check only for incorrect Jacobian. The reason is that unlike line search methods LM optimizer does not perform extensive evaluations along the line. Thus, we simply do not have enough data to catch C0/C1-violations. This check is activated with minlmoptguardgradient() function. Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradfidx for specific function (Jacobian row) suspected * rep.badgradvidx for specific variable (Jacobian column) suspected * rep.badgradxbase, a point where gradient/Jacobian is tested * rep.badgraduser, user-provided gradient/Jacobian * rep.badgradnum, reference gradient/Jacobian obtained via numerical differentiation INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - OptGuard report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlmoptguardresults(minlmstate state, out optguardreport rep) { rep = new optguardreport(); minlm.minlmoptguardresults(state.innerobj, rep.innerobj, null); } public static void minlmoptguardresults(minlmstate state, out optguardreport rep, alglib.xparams _params) { rep = new optguardreport(); minlm.minlmoptguardresults(state.innerobj, rep.innerobj, _params); } /************************************************************************* Levenberg-Marquardt algorithm results NOTE: if you activated OptGuard integrity checking functionality and want to get OptGuard report, it can be retrieved with the help of minlmoptguardresults() function. INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report; includes termination codes and additional information. Termination codes are listed below, see comments for this structure for more info. Termination code is stored in rep.terminationtype field: * -8 optimizer detected NAN/INF values either in the function itself, or in its Jacobian * -3 constraints are inconsistent * 2 relative step is no more than EpsX. * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible * 8 terminated by user who called minlmrequesttermination(). X contains point which was "current accepted" when termination request was submitted. -- ALGLIB -- Copyright 10.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmresults(minlmstate state, out double[] x, out minlmreport rep) { x = new double[0]; rep = new minlmreport(); minlm.minlmresults(state.innerobj, ref x, rep.innerobj, null); } public static void minlmresults(minlmstate state, out double[] x, out minlmreport rep, alglib.xparams _params) { x = new double[0]; rep = new minlmreport(); minlm.minlmresults(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* Levenberg-Marquardt algorithm results Buffered implementation of MinLMResults(), which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 10.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmresultsbuf(minlmstate state, ref double[] x, minlmreport rep) { minlm.minlmresultsbuf(state.innerobj, ref x, rep.innerobj, null); } public static void minlmresultsbuf(minlmstate state, ref double[] x, minlmreport rep, alglib.xparams _params) { minlm.minlmresultsbuf(state.innerobj, ref x, rep.innerobj, _params); } /************************************************************************* This subroutine restarts LM algorithm from new point. All optimization parameters are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure used for reverse communication previously allocated with MinLMCreateXXX call. X - new starting point. -- ALGLIB -- Copyright 30.07.2010 by Bochkanov Sergey *************************************************************************/ public static void minlmrestartfrom(minlmstate state, double[] x) { minlm.minlmrestartfrom(state.innerobj, x, null); } public static void minlmrestartfrom(minlmstate state, double[] x, alglib.xparams _params) { minlm.minlmrestartfrom(state.innerobj, x, _params); } /************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 08.10.2014 by Bochkanov Sergey *************************************************************************/ public static void minlmrequesttermination(minlmstate state) { minlm.minlmrequesttermination(state.innerobj, null); } public static void minlmrequesttermination(minlmstate state, alglib.xparams _params) { minlm.minlmrequesttermination(state.innerobj, _params); } /************************************************************************* This is obsolete function. Since ALGLIB 3.3 it is equivalent to MinLMCreateVJ(). -- ALGLIB -- Copyright 30.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmcreatevgj(int n, int m, double[] x, out minlmstate state) { state = new minlmstate(); minlm.minlmcreatevgj(n, m, x, state.innerobj, null); } public static void minlmcreatevgj(int n, int m, double[] x, out minlmstate state, alglib.xparams _params) { state = new minlmstate(); minlm.minlmcreatevgj(n, m, x, state.innerobj, _params); } public static void minlmcreatevgj(int m, double[] x, out minlmstate state) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatevgj(n, m, x, state.innerobj, null); return; } public static void minlmcreatevgj(int m, double[] x, out minlmstate state, alglib.xparams _params) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatevgj(n, m, x, state.innerobj, _params); return; } /************************************************************************* This is obsolete function. Since ALGLIB 3.3 it is equivalent to MinLMCreateFJ(). -- ALGLIB -- Copyright 30.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmcreatefgj(int n, int m, double[] x, out minlmstate state) { state = new minlmstate(); minlm.minlmcreatefgj(n, m, x, state.innerobj, null); } public static void minlmcreatefgj(int n, int m, double[] x, out minlmstate state, alglib.xparams _params) { state = new minlmstate(); minlm.minlmcreatefgj(n, m, x, state.innerobj, _params); } public static void minlmcreatefgj(int m, double[] x, out minlmstate state) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatefgj(n, m, x, state.innerobj, null); return; } public static void minlmcreatefgj(int m, double[] x, out minlmstate state, alglib.xparams _params) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatefgj(n, m, x, state.innerobj, _params); return; } /************************************************************************* This function is considered obsolete since ALGLIB 3.1.0 and is present for backward compatibility only. We recommend to use MinLMCreateVJ, which provides similar, but more consistent and feature-rich interface. -- ALGLIB -- Copyright 30.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmcreatefj(int n, int m, double[] x, out minlmstate state) { state = new minlmstate(); minlm.minlmcreatefj(n, m, x, state.innerobj, null); } public static void minlmcreatefj(int n, int m, double[] x, out minlmstate state, alglib.xparams _params) { state = new minlmstate(); minlm.minlmcreatefj(n, m, x, state.innerobj, _params); } public static void minlmcreatefj(int m, double[] x, out minlmstate state) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatefj(n, m, x, state.innerobj, null); return; } public static void minlmcreatefj(int m, double[] x, out minlmstate state, alglib.xparams _params) { int n; state = new minlmstate(); n = ap.len(x); minlm.minlmcreatefj(n, m, x, state.innerobj, _params); return; } } public partial class alglib { public class cqmodels { /************************************************************************* This structure describes convex quadratic model of the form: f(x) = 0.5*(Alpha*x'*A*x + Tau*x'*D*x) + 0.5*Theta*(Q*x-r)'*(Q*x-r) + b'*x where: * Alpha>=0, Tau>=0, Theta>=0, Alpha+Tau>0. * A is NxN matrix, Q is NxK matrix (N>=1, K>=0), b is Nx1 vector, D is NxN diagonal matrix. * "main" quadratic term Alpha*A+Lambda*D is symmetric positive definite Structure may contain optional equality constraints of the form x[i]=x0[i], in this case functions provided by this unit calculate Newton step subject to these equality constraints. *************************************************************************/ public class convexquadraticmodel : apobject { public int n; public int k; public double alpha; public double tau; public double theta; public double[,] a; public double[,] q; public double[] b; public double[] r; public double[] xc; public double[] d; public bool[] activeset; public double[,] tq2dense; public double[,] tk2; public double[] tq2diag; public double[] tq1; public double[] tk1; public double tq0; public double tk0; public double[] txc; public double[] tb; public int nfree; public int ecakind; public double[,] ecadense; public double[,] eq; public double[,] eccm; public double[] ecadiag; public double[] eb; public double ec; public double[] tmp0; public double[] tmp1; public double[] tmpg; public double[,] tmp2; public bool ismaintermchanged; public bool issecondarytermchanged; public bool islineartermchanged; public bool isactivesetchanged; public convexquadraticmodel() { init(); } public override void init() { a = new double[0,0]; q = new double[0,0]; b = new double[0]; r = new double[0]; xc = new double[0]; d = new double[0]; activeset = new bool[0]; tq2dense = new double[0,0]; tk2 = new double[0,0]; tq2diag = new double[0]; tq1 = new double[0]; tk1 = new double[0]; txc = new double[0]; tb = new double[0]; ecadense = new double[0,0]; eq = new double[0,0]; eccm = new double[0,0]; ecadiag = new double[0]; eb = new double[0]; tmp0 = new double[0]; tmp1 = new double[0]; tmpg = new double[0]; tmp2 = new double[0,0]; } public override alglib.apobject make_copy() { convexquadraticmodel _result = new convexquadraticmodel(); _result.n = n; _result.k = k; _result.alpha = alpha; _result.tau = tau; _result.theta = theta; _result.a = (double[,])a.Clone(); _result.q = (double[,])q.Clone(); _result.b = (double[])b.Clone(); _result.r = (double[])r.Clone(); _result.xc = (double[])xc.Clone(); _result.d = (double[])d.Clone(); _result.activeset = (bool[])activeset.Clone(); _result.tq2dense = (double[,])tq2dense.Clone(); _result.tk2 = (double[,])tk2.Clone(); _result.tq2diag = (double[])tq2diag.Clone(); _result.tq1 = (double[])tq1.Clone(); _result.tk1 = (double[])tk1.Clone(); _result.tq0 = tq0; _result.tk0 = tk0; _result.txc = (double[])txc.Clone(); _result.tb = (double[])tb.Clone(); _result.nfree = nfree; _result.ecakind = ecakind; _result.ecadense = (double[,])ecadense.Clone(); _result.eq = (double[,])eq.Clone(); _result.eccm = (double[,])eccm.Clone(); _result.ecadiag = (double[])ecadiag.Clone(); _result.eb = (double[])eb.Clone(); _result.ec = ec; _result.tmp0 = (double[])tmp0.Clone(); _result.tmp1 = (double[])tmp1.Clone(); _result.tmpg = (double[])tmpg.Clone(); _result.tmp2 = (double[,])tmp2.Clone(); _result.ismaintermchanged = ismaintermchanged; _result.issecondarytermchanged = issecondarytermchanged; _result.islineartermchanged = islineartermchanged; _result.isactivesetchanged = isactivesetchanged; return _result; } }; public const int newtonrefinementits = 3; /************************************************************************* This subroutine is used to initialize CQM. By default, empty NxN model is generated, with Alpha=Lambda=Theta=0.0 and zero b. Previously allocated buffer variables are reused as much as possible. -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqminit(int n, convexquadraticmodel s, alglib.xparams _params) { int i = 0; s.n = n; s.k = 0; s.nfree = n; s.ecakind = -1; s.alpha = 0.0; s.tau = 0.0; s.theta = 0.0; s.ismaintermchanged = true; s.issecondarytermchanged = true; s.islineartermchanged = true; s.isactivesetchanged = true; apserv.bvectorsetlengthatleast(ref s.activeset, n, _params); apserv.rvectorsetlengthatleast(ref s.xc, n, _params); apserv.rvectorsetlengthatleast(ref s.eb, n, _params); apserv.rvectorsetlengthatleast(ref s.tq1, n, _params); apserv.rvectorsetlengthatleast(ref s.txc, n, _params); apserv.rvectorsetlengthatleast(ref s.tb, n, _params); apserv.rvectorsetlengthatleast(ref s.b, s.n, _params); apserv.rvectorsetlengthatleast(ref s.tk1, s.n, _params); for(i=0; i<=n-1; i++) { s.activeset[i] = false; s.xc[i] = 0.0; s.b[i] = 0.0; } } /************************************************************************* This subroutine changes main quadratic term of the model. INPUT PARAMETERS: S - model A - NxN matrix, only upper or lower triangle is referenced IsUpper - True, when matrix is stored in upper triangle Alpha - multiplier; when Alpha=0, A is not referenced at all -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmseta(convexquadraticmodel s, double[,] a, bool isupper, double alpha, alglib.xparams _params) { int i = 0; int j = 0; double v = 0; alglib.ap.assert(math.isfinite(alpha) && (double)(alpha)>=(double)(0), "CQMSetA: Alpha<0 or is not finite number"); alglib.ap.assert((double)(alpha)==(double)(0) || apserv.isfinitertrmatrix(a, s.n, isupper, _params), "CQMSetA: A is not finite NxN matrix"); s.alpha = alpha; if( (double)(alpha)>(double)(0) ) { apserv.rmatrixsetlengthatleast(ref s.a, s.n, s.n, _params); apserv.rmatrixsetlengthatleast(ref s.ecadense, s.n, s.n, _params); apserv.rmatrixsetlengthatleast(ref s.tq2dense, s.n, s.n, _params); for(i=0; i<=s.n-1; i++) { for(j=i; j<=s.n-1; j++) { if( isupper ) { v = a[i,j]; } else { v = a[j,i]; } s.a[i,j] = v; s.a[j,i] = v; } } } s.ismaintermchanged = true; } /************************************************************************* This subroutine changes main quadratic term of the model. INPUT PARAMETERS: S - model A - possibly preallocated buffer OUTPUT PARAMETERS: A - NxN matrix, full matrix is returned. Zero matrix is returned if model is empty. -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmgeta(convexquadraticmodel s, ref double[,] a, alglib.xparams _params) { int i = 0; int j = 0; double v = 0; int n = 0; n = s.n; apserv.rmatrixsetlengthatleast(ref a, n, n, _params); if( (double)(s.alpha)>(double)(0) ) { v = s.alpha; for(i=0; i<=n-1; i++) { for(j=0; j<=n-1; j++) { a[i,j] = v*s.a[i,j]; } } } else { for(i=0; i<=n-1; i++) { for(j=0; j<=n-1; j++) { a[i,j] = 0.0; } } } } /************************************************************************* This subroutine rewrites diagonal of the main quadratic term of the model (dense A) by vector Z/Alpha (current value of the Alpha coefficient is used). IMPORTANT: in case model has no dense quadratic term, this function allocates N*N dense matrix of zeros, and fills its diagonal by non-zero values. INPUT PARAMETERS: S - model Z - new diagonal, array[N] -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmrewritedensediagonal(convexquadraticmodel s, double[] z, alglib.xparams _params) { int n = 0; int i = 0; int j = 0; n = s.n; if( (double)(s.alpha)==(double)(0) ) { apserv.rmatrixsetlengthatleast(ref s.a, s.n, s.n, _params); apserv.rmatrixsetlengthatleast(ref s.ecadense, s.n, s.n, _params); apserv.rmatrixsetlengthatleast(ref s.tq2dense, s.n, s.n, _params); for(i=0; i<=n-1; i++) { for(j=0; j<=n-1; j++) { s.a[i,j] = 0.0; } } s.alpha = 1.0; } for(i=0; i<=s.n-1; i++) { s.a[i,i] = z[i]/s.alpha; } s.ismaintermchanged = true; } /************************************************************************* This subroutine changes diagonal quadratic term of the model. INPUT PARAMETERS: S - model D - array[N], semidefinite diagonal matrix Tau - multiplier; when Tau=0, D is not referenced at all -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmsetd(convexquadraticmodel s, double[] d, double tau, alglib.xparams _params) { int i = 0; alglib.ap.assert(math.isfinite(tau) && (double)(tau)>=(double)(0), "CQMSetD: Tau<0 or is not finite number"); alglib.ap.assert((double)(tau)==(double)(0) || apserv.isfinitevector(d, s.n, _params), "CQMSetD: D is not finite Nx1 vector"); s.tau = tau; if( (double)(tau)>(double)(0) ) { apserv.rvectorsetlengthatleast(ref s.d, s.n, _params); apserv.rvectorsetlengthatleast(ref s.ecadiag, s.n, _params); apserv.rvectorsetlengthatleast(ref s.tq2diag, s.n, _params); for(i=0; i<=s.n-1; i++) { alglib.ap.assert((double)(d[i])>=(double)(0), "CQMSetD: D[i]<0"); s.d[i] = d[i]; } } s.ismaintermchanged = true; } /************************************************************************* This subroutine drops main quadratic term A from the model. It is same as call to CQMSetA() with zero A, but gives better performance because algorithm knows that matrix is zero and can optimize subsequent calculations. INPUT PARAMETERS: S - model -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmdropa(convexquadraticmodel s, alglib.xparams _params) { s.alpha = 0.0; s.ismaintermchanged = true; } /************************************************************************* This subroutine changes linear term of the model -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmsetb(convexquadraticmodel s, double[] b, alglib.xparams _params) { int i = 0; alglib.ap.assert(apserv.isfinitevector(b, s.n, _params), "CQMSetB: B is not finite vector"); apserv.rvectorsetlengthatleast(ref s.b, s.n, _params); for(i=0; i<=s.n-1; i++) { s.b[i] = b[i]; } s.islineartermchanged = true; } /************************************************************************* This subroutine changes linear term of the model -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmsetq(convexquadraticmodel s, double[,] q, double[] r, int k, double theta, alglib.xparams _params) { int i = 0; int j = 0; alglib.ap.assert(k>=0, "CQMSetQ: K<0"); alglib.ap.assert((k==0 || (double)(theta)==(double)(0)) || apserv.apservisfinitematrix(q, k, s.n, _params), "CQMSetQ: Q is not finite matrix"); alglib.ap.assert((k==0 || (double)(theta)==(double)(0)) || apserv.isfinitevector(r, k, _params), "CQMSetQ: R is not finite vector"); alglib.ap.assert(math.isfinite(theta) && (double)(theta)>=(double)(0), "CQMSetQ: Theta<0 or is not finite number"); // // degenerate case: K=0 or Theta=0 // if( k==0 || (double)(theta)==(double)(0) ) { s.k = 0; s.theta = 0; s.issecondarytermchanged = true; return; } // // General case: both Theta>0 and K>0 // s.k = k; s.theta = theta; apserv.rmatrixsetlengthatleast(ref s.q, s.k, s.n, _params); apserv.rvectorsetlengthatleast(ref s.r, s.k, _params); apserv.rmatrixsetlengthatleast(ref s.eq, s.k, s.n, _params); apserv.rmatrixsetlengthatleast(ref s.eccm, s.k, s.k, _params); apserv.rmatrixsetlengthatleast(ref s.tk2, s.k, s.n, _params); for(i=0; i<=s.k-1; i++) { for(j=0; j<=s.n-1; j++) { s.q[i,j] = q[i,j]; } s.r[i] = r[i]; } s.issecondarytermchanged = true; } /************************************************************************* This subroutine changes active set INPUT PARAMETERS S - model X - array[N], constraint values ActiveSet- array[N], active set. If ActiveSet[I]=True, then I-th variables is constrained to X[I]. -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmsetactiveset(convexquadraticmodel s, double[] x, bool[] activeset, alglib.xparams _params) { int i = 0; alglib.ap.assert(alglib.ap.len(x)>=s.n, "CQMSetActiveSet: Length(X)=s.n, "CQMSetActiveSet: Length(ActiveSet)(double)(0) ) { for(i=0; i<=n-1; i++) { for(j=0; j<=n-1; j++) { result = result+s.alpha*0.5*x[i]*s.a[i,j]*x[j]; } } } if( (double)(s.tau)>(double)(0) ) { for(i=0; i<=n-1; i++) { result = result+0.5*math.sqr(x[i])*s.tau*s.d[i]; } } // // secondary quadratic term // if( (double)(s.theta)>(double)(0) ) { for(i=0; i<=s.k-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += s.q[i,i_]*x[i_]; } result = result+0.5*s.theta*math.sqr(v-s.r[i]); } } // // linear term // for(i=0; i<=s.n-1; i++) { result = result+x[i]*s.b[i]; } return result; } /************************************************************************* This subroutine evaluates model at X. Active constraints are ignored. It returns: R - model value Noise- estimate of the numerical noise in data -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmevalx(convexquadraticmodel s, double[] x, ref double r, ref double noise, alglib.xparams _params) { int n = 0; int i = 0; int j = 0; double v = 0; double v2 = 0; double mxq = 0; double eps = 0; r = 0; noise = 0; n = s.n; alglib.ap.assert(apserv.isfinitevector(x, n, _params), "CQMEval: X is not finite vector"); r = 0.0; noise = 0.0; eps = 2*math.machineepsilon; mxq = 0.0; // // Main quadratic term. // // Noise from the main quadratic term is equal to the // maximum summand in the term. // if( (double)(s.alpha)>(double)(0) ) { for(i=0; i<=n-1; i++) { for(j=0; j<=n-1; j++) { v = s.alpha*0.5*x[i]*s.a[i,j]*x[j]; r = r+v; noise = Math.Max(noise, eps*Math.Abs(v)); } } } if( (double)(s.tau)>(double)(0) ) { for(i=0; i<=n-1; i++) { v = 0.5*math.sqr(x[i])*s.tau*s.d[i]; r = r+v; noise = Math.Max(noise, eps*Math.Abs(v)); } } // // secondary quadratic term // // Noise from the secondary quadratic term is estimated as follows: // * noise in qi*x-r[i] is estimated as // Eps*MXQ = Eps*max(|r[i]|, |q[i,j]*x[j]|) // * noise in (qi*x-r[i])^2 is estimated as // NOISE = (|qi*x-r[i]|+Eps*MXQ)^2-(|qi*x-r[i]|)^2 // = Eps*MXQ*(2*|qi*x-r[i]|+Eps*MXQ) // if( (double)(s.theta)>(double)(0) ) { for(i=0; i<=s.k-1; i++) { v = 0.0; mxq = Math.Abs(s.r[i]); for(j=0; j<=n-1; j++) { v2 = s.q[i,j]*x[j]; v = v+v2; mxq = Math.Max(mxq, Math.Abs(v2)); } r = r+0.5*s.theta*math.sqr(v-s.r[i]); noise = Math.Max(noise, eps*mxq*(2*Math.Abs(v-s.r[i])+eps*mxq)); } } // // linear term // for(i=0; i<=s.n-1; i++) { r = r+x[i]*s.b[i]; noise = Math.Max(noise, eps*Math.Abs(x[i]*s.b[i])); } // // Final update of the noise // noise = n*noise; } /************************************************************************* This subroutine evaluates gradient of the model; active constraints are ignored. INPUT PARAMETERS: S - convex model X - point, array[N] G - possibly preallocated buffer; resized, if too small -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmgradunconstrained(convexquadraticmodel s, double[] x, ref double[] g, alglib.xparams _params) { int n = 0; int i = 0; int j = 0; double v = 0; int i_ = 0; n = s.n; alglib.ap.assert(apserv.isfinitevector(x, n, _params), "CQMEvalGradUnconstrained: X is not finite vector"); apserv.rvectorsetlengthatleast(ref g, n, _params); for(i=0; i<=n-1; i++) { g[i] = 0; } // // main quadratic term // if( (double)(s.alpha)>(double)(0) ) { for(i=0; i<=n-1; i++) { v = 0.0; for(j=0; j<=n-1; j++) { v = v+s.alpha*s.a[i,j]*x[j]; } g[i] = g[i]+v; } } if( (double)(s.tau)>(double)(0) ) { for(i=0; i<=n-1; i++) { g[i] = g[i]+x[i]*s.tau*s.d[i]; } } // // secondary quadratic term // if( (double)(s.theta)>(double)(0) ) { for(i=0; i<=s.k-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += s.q[i,i_]*x[i_]; } v = s.theta*(v-s.r[i]); for(i_=0; i_<=n-1;i_++) { g[i_] = g[i_] + v*s.q[i,i_]; } } } // // linear term // for(i=0; i<=n-1; i++) { g[i] = g[i]+s.b[i]; } } /************************************************************************* This subroutine evaluates x'*(0.5*alpha*A+tau*D)*x NOTE: Tmp[] must be preallocated array whose length is at least N -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static double cqmxtadx2(convexquadraticmodel s, double[] x, double[] tmp, alglib.xparams _params) { double result = 0; int n = 0; int i = 0; n = s.n; alglib.ap.assert(apserv.isfinitevector(x, n, _params), "CQMXTADX2: X is not finite vector"); alglib.ap.assert(alglib.ap.len(tmp)>=n, "CQMXTADX2: Length(Tmp)(double)(0) ) { result = result+s.alpha*0.5*ablas.rmatrixsyvmv(n, s.a, 0, 0, true, x, 0, tmp, _params); } if( (double)(s.tau)>(double)(0) ) { for(i=0; i<=n-1; i++) { result = result+0.5*math.sqr(x[i])*s.tau*s.d[i]; } } return result; } /************************************************************************* This subroutine evaluates (0.5*alpha*A+tau*D)*x Y is automatically resized if needed -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmadx(convexquadraticmodel s, double[] x, ref double[] y, alglib.xparams _params) { int n = 0; int i = 0; n = s.n; alglib.ap.assert(apserv.isfinitevector(x, n, _params), "CQMEval: X is not finite vector"); apserv.rvectorsetlengthatleast(ref y, n, _params); // // main quadratic term // for(i=0; i<=n-1; i++) { y[i] = 0; } if( (double)(s.alpha)>(double)(0) ) { ablas.rmatrixsymv(n, s.alpha, s.a, 0, 0, true, x, 0, 1.0, y, 0, _params); } if( (double)(s.tau)>(double)(0) ) { for(i=0; i<=n-1; i++) { y[i] = y[i]+x[i]*s.tau*s.d[i]; } } } /************************************************************************* This subroutine finds optimum of the model. It returns False on failure (indefinite/semidefinite matrix). Optimum is found subject to active constraints. INPUT PARAMETERS S - model X - possibly preallocated buffer; automatically resized, if too small enough. -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static bool cqmconstrainedoptimum(convexquadraticmodel s, ref double[] x, alglib.xparams _params) { bool result = new bool(); int n = 0; int nfree = 0; int k = 0; int i = 0; double v = 0; int cidx0 = 0; int itidx = 0; int i_ = 0; // // Rebuild internal structures // if( !cqmrebuild(s, _params) ) { result = false; return result; } n = s.n; k = s.k; nfree = s.nfree; result = true; // // Calculate initial point for the iterative refinement: // * free components are set to zero // * constrained components are set to their constrained values // apserv.rvectorsetlengthatleast(ref x, n, _params); for(i=0; i<=n-1; i++) { if( s.activeset[i] ) { x[i] = s.xc[i]; } else { x[i] = 0; } } // // Iterative refinement. // // In an ideal world without numerical errors it would be enough // to make just one Newton step from initial point: // x_new = -H^(-1)*grad(x=0) // However, roundoff errors can significantly deteriorate quality // of the solution. So we have to recalculate gradient and to // perform Newton steps several times. // // Below we perform fixed number of Newton iterations. // for(itidx=0; itidx<=newtonrefinementits-1; itidx++) { // // Calculate gradient at the current point. // Move free components of the gradient in the beginning. // cqmgradunconstrained(s, x, ref s.tmpg, _params); cidx0 = 0; for(i=0; i<=n-1; i++) { if( !s.activeset[i] ) { s.tmpg[cidx0] = s.tmpg[i]; cidx0 = cidx0+1; } } // // Free components of the extrema are calculated in the first NFree elements of TXC. // // First, we have to calculate original Newton step, without rank-K perturbations // for(i_=0; i_<=nfree-1;i_++) { s.txc[i_] = -s.tmpg[i_]; } cqmsolveea(s, ref s.txc, ref s.tmp0, _params); // // Then, we account for rank-K correction. // Woodbury matrix identity is used. // if( s.k>0 && (double)(s.theta)>(double)(0) ) { apserv.rvectorsetlengthatleast(ref s.tmp0, Math.Max(nfree, k), _params); apserv.rvectorsetlengthatleast(ref s.tmp1, Math.Max(nfree, k), _params); for(i_=0; i_<=nfree-1;i_++) { s.tmp1[i_] = -s.tmpg[i_]; } cqmsolveea(s, ref s.tmp1, ref s.tmp0, _params); for(i=0; i<=k-1; i++) { v = 0.0; for(i_=0; i_<=nfree-1;i_++) { v += s.eq[i,i_]*s.tmp1[i_]; } s.tmp0[i] = v; } fbls.fblscholeskysolve(s.eccm, 1.0, k, true, s.tmp0, ref s.tmp1, _params); for(i=0; i<=nfree-1; i++) { s.tmp1[i] = 0.0; } for(i=0; i<=k-1; i++) { v = s.tmp0[i]; for(i_=0; i_<=nfree-1;i_++) { s.tmp1[i_] = s.tmp1[i_] + v*s.eq[i,i_]; } } cqmsolveea(s, ref s.tmp1, ref s.tmp0, _params); for(i_=0; i_<=nfree-1;i_++) { s.txc[i_] = s.txc[i_] - s.tmp1[i_]; } } // // Unpack components from TXC into X. We pass through all // free components of X and add our step. // cidx0 = 0; for(i=0; i<=n-1; i++) { if( !s.activeset[i] ) { x[i] = x[i]+s.txc[cidx0]; cidx0 = cidx0+1; } } } return result; } /************************************************************************* This function scales vector by multiplying it by inverse of the diagonal of the Hessian matrix. It should be used to accelerate steepest descent phase of the QP solver. Although it is called "scale-grad", it can be called for any vector, whether it is gradient, anti-gradient, or just some vector. This function does NOT takes into account current set of constraints, it just performs matrix-vector multiplication without taking into account constraints. INPUT PARAMETERS: S - model X - vector to scale OUTPUT PARAMETERS: X - scaled vector NOTE: when called for non-SPD matrices, it silently skips components of X which correspond to zero or negative diagonal elements. NOTE: this function uses diagonals of A and D; it ignores Q - rank-K term of the quadratic model. -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static void cqmscalevector(convexquadraticmodel s, ref double[] x, alglib.xparams _params) { int n = 0; int i = 0; double v = 0; n = s.n; for(i=0; i<=n-1; i++) { v = 0.0; if( (double)(s.alpha)>(double)(0) ) { v = v+s.a[i,i]; } if( (double)(s.tau)>(double)(0) ) { v = v+s.d[i]; } if( (double)(v)>(double)(0) ) { x[i] = x[i]/v; } } } /************************************************************************* This function returns diagonal of the A-term. INPUT PARAMETERS: S - model OUTPUT PARAMETERS: D - diagonal of the A (or zero) -- ALGLIB -- Copyright 26.12.2017 by Bochkanov Sergey *************************************************************************/ public static void cqmgetdiaga(convexquadraticmodel s, ref double[] x, alglib.xparams _params) { int n = 0; int i = 0; n = s.n; apserv.rvectorsetlengthatleast(ref x, n, _params); for(i=0; i<=n-1; i++) { if( (double)(s.alpha)>(double)(0) ) { x[i] = s.a[i,i]; } else { x[i] = 0; } } } /************************************************************************* This subroutine calls CQMRebuild() and evaluates model at X subject to active constraints. It is intended for debug purposes only, because it evaluates model by means of temporaries, which were calculated by CQMRebuild(). The only purpose of this function is to check correctness of CQMRebuild() by comparing results of this function with ones obtained by CQMEval(), which is used as reference point. The idea is that significant deviation in results of these two functions is evidence of some error in the CQMRebuild(). NOTE: suffix T denotes that temporaries marked by T-prefix are used. There is one more variant of this function, which uses "effective" model built by CQMRebuild(). NOTE2: in case CQMRebuild() fails (due to model non-convexity), this function returns NAN. -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static double cqmdebugconstrainedevalt(convexquadraticmodel s, double[] x, alglib.xparams _params) { double result = 0; int n = 0; int nfree = 0; int i = 0; int j = 0; double v = 0; n = s.n; alglib.ap.assert(apserv.isfinitevector(x, n, _params), "CQMDebugConstrainedEvalT: X is not finite vector"); if( !cqmrebuild(s, _params) ) { result = Double.NaN; return result; } result = 0.0; nfree = s.nfree; // // Reorder variables // j = 0; for(i=0; i<=n-1; i++) { if( !s.activeset[i] ) { alglib.ap.assert(j(double)(0) ) { // // Dense TQ2 // for(i=0; i<=nfree-1; i++) { for(j=0; j<=nfree-1; j++) { result = result+0.5*s.txc[i]*s.tq2dense[i,j]*s.txc[j]; } } } else { // // Diagonal TQ2 // for(i=0; i<=nfree-1; i++) { result = result+0.5*s.tq2diag[i]*math.sqr(s.txc[i]); } } for(i=0; i<=nfree-1; i++) { result = result+s.tq1[i]*s.txc[i]; } result = result+s.tq0; // // TK2, TK1, TK0 // if( s.k>0 && (double)(s.theta)>(double)(0) ) { for(i=0; i<=s.k-1; i++) { v = 0; for(j=0; j<=nfree-1; j++) { v = v+s.tk2[i,j]*s.txc[j]; } result = result+0.5*math.sqr(v); } for(i=0; i<=nfree-1; i++) { result = result+s.tk1[i]*s.txc[i]; } result = result+s.tk0; } // // TB (Bf and Bc parts) // for(i=0; i<=n-1; i++) { result = result+s.tb[i]*s.txc[i]; } return result; } /************************************************************************* This subroutine calls CQMRebuild() and evaluates model at X subject to active constraints. It is intended for debug purposes only, because it evaluates model by means of "effective" matrices built by CQMRebuild(). The only purpose of this function is to check correctness of CQMRebuild() by comparing results of this function with ones obtained by CQMEval(), which is used as reference point. The idea is that significant deviation in results of these two functions is evidence of some error in the CQMRebuild(). NOTE: suffix E denotes that effective matrices. There is one more variant of this function, which uses temporary matrices built by CQMRebuild(). NOTE2: in case CQMRebuild() fails (due to model non-convexity), this function returns NAN. -- ALGLIB -- Copyright 12.06.2012 by Bochkanov Sergey *************************************************************************/ public static double cqmdebugconstrainedevale(convexquadraticmodel s, double[] x, alglib.xparams _params) { double result = 0; int n = 0; int nfree = 0; int i = 0; int j = 0; double v = 0; n = s.n; alglib.ap.assert(apserv.isfinitevector(x, n, _params), "CQMDebugConstrainedEvalE: X is not finite vector"); if( !cqmrebuild(s, _params) ) { result = Double.NaN; return result; } result = 0.0; nfree = s.nfree; // // Reorder variables // j = 0; for(i=0; i<=n-1; i++) { if( !s.activeset[i] ) { alglib.ap.assert(j0 and Alpha=0 separately: // * in the first case we have dense matrix // * in the second one we have diagonal matrix, which can be // handled more efficiently // if( (double)(s.alpha)>(double)(0) ) { // // Alpha>0, dense QP // // Split variables into two groups - free (F) and constrained (C). Reorder // variables in such way that free vars come first, constrained are last: // x = [xf, xc]. // // Main quadratic term x'*(alpha*A+tau*D)*x now splits into quadratic part, // linear part and constant part: // ( alpha*Aff+tau*Df alpha*Afc ) ( xf ) // 0.5*( xf' xc' )*( )*( ) = // ( alpha*Acf alpha*Acc+tau*Dc ) ( xc ) // // = 0.5*xf'*(alpha*Aff+tau*Df)*xf + (alpha*Afc*xc)'*xf + 0.5*xc'(alpha*Acc+tau*Dc)*xc // // We store these parts into temporary variables: // * alpha*Aff+tau*Df, alpha*Afc, alpha*Acc+tau*Dc are stored into upper // triangle of TQ2 // * alpha*Afc*xc is stored into TQ1 // * 0.5*xc'(alpha*Acc+tau*Dc)*xc is stored into TQ0 // // Below comes first part of the work - generation of TQ2: // * we pass through rows of A and copy I-th row into upper block (Aff/Afc) or // lower one (Acf/Acc) of TQ2, depending on presence of X[i] in the active set. // RIdx0 variable contains current position for insertion into upper block, // RIdx1 contains current position for insertion into lower one. // * within each row, we copy J-th element into left half (Aff/Acf) or right // one (Afc/Acc), depending on presence of X[j] in the active set. CIdx0 // contains current position for insertion into left block, CIdx1 contains // position for insertion into right one. // * during copying, we multiply elements by alpha and add diagonal matrix D. // ridx0 = 0; ridx1 = s.nfree; for(i=0; i<=n-1; i++) { cidx0 = 0; cidx1 = s.nfree; for(j=0; j<=n-1; j++) { if( !s.activeset[i] && !s.activeset[j] ) { // // Element belongs to Aff // v = s.alpha*s.a[i,j]; if( i==j && (double)(s.tau)>(double)(0) ) { v = v+s.tau*s.d[i]; } s.tq2dense[ridx0,cidx0] = v; } if( !s.activeset[i] && s.activeset[j] ) { // // Element belongs to Afc // s.tq2dense[ridx0,cidx1] = s.alpha*s.a[i,j]; } if( s.activeset[i] && !s.activeset[j] ) { // // Element belongs to Acf // s.tq2dense[ridx1,cidx0] = s.alpha*s.a[i,j]; } if( s.activeset[i] && s.activeset[j] ) { // // Element belongs to Acc // v = s.alpha*s.a[i,j]; if( i==j && (double)(s.tau)>(double)(0) ) { v = v+s.tau*s.d[i]; } s.tq2dense[ridx1,cidx1] = v; } if( s.activeset[j] ) { cidx1 = cidx1+1; } else { cidx0 = cidx0+1; } } if( s.activeset[i] ) { ridx1 = ridx1+1; } else { ridx0 = ridx0+1; } } // // Now we have TQ2, and we can evaluate TQ1. // In the special case when we have Alpha=0, NFree=0 or NFree=N, // TQ1 is filled by zeros. // for(i=0; i<=n-1; i++) { s.tq1[i] = 0.0; } if( s.nfree>0 && s.nfree0 and Theta>0 // * we pass through columns of Q and copy I-th column into left block (Qf) or // right one (Qc) of TK2, depending on presence of X[i] in the active set. // CIdx0 variable contains current position for insertion into upper block, // CIdx1 contains current position for insertion into lower one. // * we calculate Qc*xc-r and store it into Tmp0 // * we calculate TK0 and TK1 // * we multiply leading part of TK2 which stores Qf by sqrt(theta) // it is important to perform this step AFTER calculation of TK0 and TK1, // because we need original (non-modified) Qf to calculate TK0 and TK1. // for(j=0; j<=n-1; j++) { for(i=0; i<=k-1; i++) { s.tk2[i,j] = 0.0; } s.tk1[j] = 0.0; } s.tk0 = 0.0; if( s.k>0 && (double)(s.theta)>(double)(0) ) { // // Split Q into Qf and Qc // Calculate Qc*xc-r, store in Tmp0 // apserv.rvectorsetlengthatleast(ref s.tmp0, k, _params); apserv.rvectorsetlengthatleast(ref s.tmp1, k, _params); cidx0 = 0; cidx1 = nfree; for(i=0; i<=k-1; i++) { s.tmp1[i] = 0.0; } for(j=0; j<=n-1; j++) { if( s.activeset[j] ) { for(i=0; i<=k-1; i++) { s.tk2[i,cidx1] = s.q[i,j]; s.tmp1[i] = s.tmp1[i]+s.q[i,j]*s.txc[cidx1]; } cidx1 = cidx1+1; } else { for(i=0; i<=k-1; i++) { s.tk2[i,cidx0] = s.q[i,j]; } cidx0 = cidx0+1; } } for(i=0; i<=k-1; i++) { s.tmp0[i] = s.tmp1[i]-s.r[i]; } // // Calculate TK0 // v = 0.0; for(i=0; i<=k-1; i++) { v = v+s.theta*(0.5*math.sqr(s.tmp1[i])-s.r[i]*s.tmp0[i]-0.5*math.sqr(s.r[i])); } s.tk0 = v; // // Calculate TK1 // if( nfree>0 ) { for(i=0; i<=k-1; i++) { v = s.theta*s.tmp0[i]; for(i_=0; i_<=nfree-1;i_++) { s.tk1[i_] = s.tk1[i_] + v*s.tk2[i,i_]; } } } // // Calculate TK2 // if( nfree>0 ) { v = Math.Sqrt(s.theta); for(i=0; i<=k-1; i++) { for(i_=0; i_<=nfree-1;i_++) { s.tk2[i,i_] = v*s.tk2[i,i_]; } } } } } // // Re-evaluate TB // if( s.isactivesetchanged || s.islineartermchanged ) { ridx0 = 0; ridx1 = nfree; for(i=0; i<=n-1; i++) { if( s.activeset[i] ) { s.tb[ridx1] = s.b[i]; ridx1 = ridx1+1; } else { s.tb[ridx0] = s.b[i]; ridx0 = ridx0+1; } } } // // Compose ECA: either dense ECA or diagonal ECA // if( (s.isactivesetchanged || s.ismaintermchanged) && nfree>0 ) { if( (double)(s.alpha)>(double)(0) ) { // // Dense ECA // s.ecakind = 0; for(i=0; i<=nfree-1; i++) { for(j=i; j<=nfree-1; j++) { s.ecadense[i,j] = s.tq2dense[i,j]; } } if( !trfac.spdmatrixcholeskyrec(ref s.ecadense, 0, nfree, true, ref s.tmp0, _params) ) { result = false; return result; } } else { // // Diagonal ECA // s.ecakind = 1; for(i=0; i<=nfree-1; i++) { if( (double)(s.tq2diag[i])<(double)(0) ) { result = false; return result; } s.ecadiag[i] = Math.Sqrt(s.tq2diag[i]); } } } // // Compose EQ // if( s.isactivesetchanged || s.issecondarytermchanged ) { for(i=0; i<=k-1; i++) { for(j=0; j<=nfree-1; j++) { s.eq[i,j] = s.tk2[i,j]; } } } // // Calculate ECCM // if( ((((s.isactivesetchanged || s.ismaintermchanged) || s.issecondarytermchanged) && s.k>0) && (double)(s.theta)>(double)(0)) && nfree>0 ) { // // Calculate ECCM - Cholesky factor of the "effective" capacitance // matrix CM = I + EQ*inv(EffectiveA)*EQ'. // // We calculate CM as follows: // CM = I + EQ*inv(EffectiveA)*EQ' // = I + EQ*ECA^(-1)*ECA^(-T)*EQ' // = I + (EQ*ECA^(-1))*(EQ*ECA^(-1))' // // Then we perform Cholesky decomposition of CM. // apserv.rmatrixsetlengthatleast(ref s.tmp2, k, n, _params); ablas.rmatrixcopy(k, nfree, s.eq, 0, 0, s.tmp2, 0, 0, _params); alglib.ap.assert(s.ecakind==0 || s.ecakind==1, "CQMRebuild: unexpected ECAKind"); if( s.ecakind==0 ) { ablas.rmatrixrighttrsm(k, nfree, s.ecadense, 0, 0, true, false, 0, s.tmp2, 0, 0, _params); } if( s.ecakind==1 ) { for(i=0; i<=k-1; i++) { for(j=0; j<=nfree-1; j++) { s.tmp2[i,j] = s.tmp2[i,j]/s.ecadiag[j]; } } } for(i=0; i<=k-1; i++) { for(j=0; j<=k-1; j++) { s.eccm[i,j] = 0.0; } s.eccm[i,i] = 1.0; } ablas.rmatrixsyrk(k, nfree, 1.0, s.tmp2, 0, 0, 0, 1.0, s.eccm, 0, 0, true, _params); if( !trfac.spdmatrixcholeskyrec(ref s.eccm, 0, k, true, ref s.tmp0, _params) ) { result = false; return result; } } // // Compose EB and EC // // NOTE: because these quantities are cheap to compute, we do not // use caching here. // for(i=0; i<=nfree-1; i++) { s.eb[i] = s.tq1[i]+s.tk1[i]+s.tb[i]; } s.ec = s.tq0+s.tk0; for(i=nfree; i<=n-1; i++) { s.ec = s.ec+s.tb[i]*s.txc[i]; } // // Change cache status - everything is cached // s.ismaintermchanged = false; s.issecondarytermchanged = false; s.islineartermchanged = false; s.isactivesetchanged = false; return result; } /************************************************************************* Internal function, solves system Effective_A*x = b. It should be called after successful completion of CQMRebuild(). INPUT PARAMETERS: S - quadratic model, after call to CQMRebuild() X - right part B, array[S.NFree] Tmp - temporary array, automatically reallocated if needed OUTPUT PARAMETERS: X - solution, array[S.NFree] NOTE: when called with zero S.NFree, returns silently NOTE: this function assumes that EA is non-degenerate -- ALGLIB -- Copyright 10.05.2011 by Bochkanov Sergey *************************************************************************/ private static void cqmsolveea(convexquadraticmodel s, ref double[] x, ref double[] tmp, alglib.xparams _params) { int i = 0; alglib.ap.assert((s.ecakind==0 || s.ecakind==1) || (s.ecakind==-1 && s.nfree==0), "CQMSolveEA: unexpected ECAKind"); if( s.ecakind==0 ) { // // Dense ECA, use FBLSCholeskySolve() dense solver. // fbls.fblscholeskysolve(s.ecadense, 1.0, s.nfree, true, x, ref tmp, _params); } if( s.ecakind==1 ) { // // Diagonal ECA // for(i=0; i<=s.nfree-1; i++) { x[i] = x[i]/math.sqr(s.ecadiag[i]); } } } } public class optguardapi { /************************************************************************* This structure is used to store OptGuard report, i.e. report on the properties of the nonlinear function being optimized with ALGLIB. After you tell your optimizer to activate OptGuard this technology starts to silently monitor function values and gradients/Jacobians being passed all around during your optimization session. Depending on specific set of checks enabled OptGuard may perform additional function evaluations (say, about 3*N evaluations if you want to check analytic gradient for errors). Upon discovering that something strange happens (function values and/or gradient components change too sharply and/or unexpectedly) OptGuard sets one of the "suspicion flags" (without interrupting optimization session). After optimization is done, you can examine OptGuard report. Following report fields can be set: * nonc0suspected * nonc1suspected * badgradsuspected === WHAT CAN BE DETECTED WITH OptGuard INTEGRITY CHECKER ================= Following types of errors in your target function (constraints) can be caught: a) discontinuous functions ("non-C0" part of the report) b) functions with discontinuous derivative ("non-C1" part of the report) c) errors in the analytic gradient provided by user These types of errors result in optimizer stopping well before reaching solution (most often - right after encountering discontinuity). Type A errors are usually coding errors during implementation of the target function. Most "normal" problems involve continuous functions, and anyway you can't reliably optimize discontinuous function. Type B errors are either coding errors or (in case code itself is correct) evidence of the fact that your problem is an "incorrect" one. Most optimizers (except for ones provided by MINNS subpackage) do not support nonsmooth problems. Type C errors are coding errors which often prevent optimizer from making even one step or result in optimizing stopping too early, as soon as actual descent direction becomes too different from one suggested by user- supplied gradient. === WHAT IS REPORTED ===================================================== Following set of report fields deals with discontinuous target functions, ones not belonging to C0 continuity class: * nonc0suspected - is a flag which is set upon discovering some indication of the discontinuity. If this flag is false, the rest of "non-C0" fields should be ignored * nonc0fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C0" * nonc0lipshitzc - a Lipchitz constant for a function which was suspected of being non-continuous. * nonc0test0positive - set to indicate specific test which detected continuity violation (test #0) Following set of report fields deals with discontinuous gradient/Jacobian, i.e. with functions violating C1 continuity: * nonc1suspected - is a flag which is set upon discovering some indication of the discontinuity. If this flag is false, the rest of "non-C1" fields should be ignored * nonc1fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * nonc1lipshitzc - a Lipchitz constant for a function gradient which was suspected of being non-smooth. * nonc1test0positive - set to indicate specific test which detected continuity violation (test #0) * nonc1test1positive - set to indicate specific test which detected continuity violation (test #1) Following set of report fields deals with errors in the gradient: * badgradsuspected - is a flad which is set upon discovering an error in the analytic gradient supplied by user * badgradfidx - index of the function with bad gradient (0 for target function, 1 or higher for nonlinear constraints) * badgradvidx - index of the variable * badgradxbase - location where Jacobian is tested * following matrices store user-supplied Jacobian and its numerical differentiation version (which is assumed to be free from the coding errors), both of them computed near the initial point: * badgraduser, an array[K,N], analytic Jacobian supplied by user * badgradnum, an array[K,N], numeric Jacobian computed by ALGLIB Here K is a total number of nonlinear functions (target + nonlinear constraints), N is a variable number. The element of badgraduser[] with index [badgradfidx,badgradvidx] is assumed to be wrong. More detailed error log can be obtained from optimizer by explicitly requesting reports for tests C0.0, C1.0, C1.1. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public class optguardreport : apobject { public bool nonc0suspected; public bool nonc0test0positive; public int nonc0fidx; public double nonc0lipschitzc; public bool nonc1suspected; public bool nonc1test0positive; public bool nonc1test1positive; public int nonc1fidx; public double nonc1lipschitzc; public bool badgradsuspected; public int badgradfidx; public int badgradvidx; public double[] badgradxbase; public double[,] badgraduser; public double[,] badgradnum; public optguardreport() { init(); } public override void init() { badgradxbase = new double[0]; badgraduser = new double[0,0]; badgradnum = new double[0,0]; } public override alglib.apobject make_copy() { optguardreport _result = new optguardreport(); _result.nonc0suspected = nonc0suspected; _result.nonc0test0positive = nonc0test0positive; _result.nonc0fidx = nonc0fidx; _result.nonc0lipschitzc = nonc0lipschitzc; _result.nonc1suspected = nonc1suspected; _result.nonc1test0positive = nonc1test0positive; _result.nonc1test1positive = nonc1test1positive; _result.nonc1fidx = nonc1fidx; _result.nonc1lipschitzc = nonc1lipschitzc; _result.badgradsuspected = badgradsuspected; _result.badgradfidx = badgradfidx; _result.badgradvidx = badgradvidx; _result.badgradxbase = (double[])badgradxbase.Clone(); _result.badgraduser = (double[,])badgraduser.Clone(); _result.badgradnum = (double[,])badgradnum.Clone(); return _result; } }; /************************************************************************* This structure is used for detailed reporting about suspected C1 continuity violation as flagged by C1 test #0 (OptGuard has several tests for C1 continuity, this report is used by #0). === WHAT IS TESTED ======================================================= C1 test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of directional derivative estimate. This test is less powerful than test #1, but it does not depend on gradient values and thus it is more robust against artifacts introduced by numerical differentiation. === WHAT IS REPORTED ===================================================== Actually, report retrieval function returns TWO report structures: * one for most suspicious point found so far (one with highest change in the directional derivative), so called "strongest" report * another one for most detailed line search (more function evaluations = easier to understand what's going on) which triggered test #0 criteria, so called "longest" report In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. You can plot function values stored in stp[] and f[] arrays and study behavior of your function by your own eyes, just to be sure that test correctly reported C1 violation. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public class optguardnonc1test0report : apobject { public bool positive; public int fidx; public double[] x0; public double[] d; public int n; public double[] stp; public double[] f; public int cnt; public int stpidxa; public int stpidxb; public optguardnonc1test0report() { init(); } public override void init() { x0 = new double[0]; d = new double[0]; stp = new double[0]; f = new double[0]; } public override alglib.apobject make_copy() { optguardnonc1test0report _result = new optguardnonc1test0report(); _result.positive = positive; _result.fidx = fidx; _result.x0 = (double[])x0.Clone(); _result.d = (double[])d.Clone(); _result.n = n; _result.stp = (double[])stp.Clone(); _result.f = (double[])f.Clone(); _result.cnt = cnt; _result.stpidxa = stpidxa; _result.stpidxb = stpidxb; return _result; } }; /************************************************************************* This structure is used for detailed reporting about suspected C1 continuity violation as flagged by C1 test #1 (OptGuard has several tests for C1 continuity, this report is used by #1). === WHAT IS TESTED ======================================================= C1 test #1 studies individual components of the gradient as recorded during line searches. Upon discovering discontinuity in the gradient this test records specific component which was suspected (or one with highest indication of discontinuity if multiple components are suspected). When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. === WHAT IS REPORTED ===================================================== Actually, report retrieval function returns TWO report structures: * one for most suspicious point found so far (one with highest change in the directional derivative), so called "strongest" report * another one for most detailed line search (more function evaluations = easier to understand what's going on) which triggered test #1 criteria, so called "longest" report In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. You can plot function values stored in stp[] and g[] arrays and study behavior of your function by your own eyes, just to be sure that test correctly reported C1 violation. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public class optguardnonc1test1report : apobject { public bool positive; public int fidx; public int vidx; public double[] x0; public double[] d; public int n; public double[] stp; public double[] g; public int cnt; public int stpidxa; public int stpidxb; public optguardnonc1test1report() { init(); } public override void init() { x0 = new double[0]; d = new double[0]; stp = new double[0]; g = new double[0]; } public override alglib.apobject make_copy() { optguardnonc1test1report _result = new optguardnonc1test1report(); _result.positive = positive; _result.fidx = fidx; _result.vidx = vidx; _result.x0 = (double[])x0.Clone(); _result.d = (double[])d.Clone(); _result.n = n; _result.stp = (double[])stp.Clone(); _result.g = (double[])g.Clone(); _result.cnt = cnt; _result.stpidxa = stpidxa; _result.stpidxb = stpidxb; return _result; } }; /************************************************************************* This subroutine initializes "internal" OptGuard report, i.e. one intended for internal use by optimizers. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void optguardinitinternal(optguardreport rep, int n, int k, alglib.xparams _params) { rep.nonc0suspected = false; rep.nonc0test0positive = false; rep.nonc0lipschitzc = 0; rep.nonc0fidx = -1; rep.nonc1suspected = false; rep.nonc1test0positive = false; rep.nonc1test1positive = false; rep.nonc1lipschitzc = 0; rep.nonc1fidx = -1; rep.badgradsuspected = false; rep.badgradfidx = -1; rep.badgradvidx = -1; } /************************************************************************* This subroutine exports report to user-readable representation (all arrays are forced to have exactly same size as needed; unused arrays are set to zero length). -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void optguardexportreport(optguardreport srcrep, int n, int k, bool badgradhasxj, optguardreport dstrep, alglib.xparams _params) { int i = 0; int j = 0; dstrep.nonc0suspected = srcrep.nonc0suspected; dstrep.nonc0test0positive = srcrep.nonc0test0positive; if( srcrep.nonc0suspected ) { dstrep.nonc0lipschitzc = srcrep.nonc0lipschitzc; dstrep.nonc0fidx = srcrep.nonc0fidx; } else { dstrep.nonc0lipschitzc = 0; dstrep.nonc0fidx = -1; } dstrep.nonc1suspected = srcrep.nonc1suspected; dstrep.nonc1test0positive = srcrep.nonc1test0positive; dstrep.nonc1test1positive = srcrep.nonc1test1positive; if( srcrep.nonc1suspected ) { dstrep.nonc1lipschitzc = srcrep.nonc1lipschitzc; dstrep.nonc1fidx = srcrep.nonc1fidx; } else { dstrep.nonc1lipschitzc = 0; dstrep.nonc1fidx = -1; } dstrep.badgradsuspected = srcrep.badgradsuspected; if( srcrep.badgradsuspected ) { dstrep.badgradfidx = srcrep.badgradfidx; dstrep.badgradvidx = srcrep.badgradvidx; } else { dstrep.badgradfidx = -1; dstrep.badgradvidx = -1; } if( badgradhasxj ) { dstrep.badgradxbase = new double[n]; for(j=0; j<=n-1; j++) { dstrep.badgradxbase[j] = srcrep.badgradxbase[j]; } dstrep.badgraduser = new double[k, n]; dstrep.badgradnum = new double[k, n]; for(i=0; i<=k-1; i++) { for(j=0; j<=n-1; j++) { dstrep.badgraduser[i,j] = srcrep.badgraduser[i,j]; dstrep.badgradnum[i,j] = srcrep.badgradnum[i,j]; } } } else { dstrep.badgradxbase = new double[0]; dstrep.badgraduser = new double[0, 0]; dstrep.badgradnum = new double[0, 0]; } } /************************************************************************* This subroutine exports report to user-readable representation (all arrays are forced to have exactly same size as needed; unused arrays are set to zero length). NOTE: we assume that SrcRep contains scaled X0[] and D[], i.e. explicit variable scaling was applied. We need to rescale them during export, that's why we need S[] parameter. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void smoothnessmonitorexportc1test0report(optguardnonc1test0report srcrep, double[] s, optguardnonc1test0report dstrep, alglib.xparams _params) { int i = 0; dstrep.positive = srcrep.positive; if( srcrep.positive ) { dstrep.stpidxa = srcrep.stpidxa; dstrep.stpidxb = srcrep.stpidxb; dstrep.fidx = srcrep.fidx; dstrep.cnt = srcrep.cnt; dstrep.n = srcrep.n; dstrep.x0 = new double[srcrep.n]; dstrep.d = new double[srcrep.n]; for(i=0; i<=srcrep.n-1; i++) { dstrep.x0[i] = srcrep.x0[i]*s[i]; dstrep.d[i] = srcrep.d[i]*s[i]; } dstrep.stp = new double[srcrep.cnt]; dstrep.f = new double[srcrep.cnt]; for(i=0; i<=srcrep.cnt-1; i++) { dstrep.stp[i] = srcrep.stp[i]; dstrep.f[i] = srcrep.f[i]; } } else { dstrep.stpidxa = -1; dstrep.stpidxb = -1; dstrep.fidx = -1; dstrep.cnt = 0; dstrep.n = 0; dstrep.x0 = new double[0]; dstrep.d = new double[0]; dstrep.stp = new double[0]; dstrep.f = new double[0]; } } /************************************************************************* This subroutine exports report to user-readable representation (all arrays are forced to have exactly same size as needed; unused arrays are set to zero length). NOTE: we assume that SrcRep contains scaled X0[], D[] and G[], i.e. explicit variable scaling was applied. We need to rescale them during export, that's why we need S[] parameter. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void smoothnessmonitorexportc1test1report(optguardnonc1test1report srcrep, double[] s, optguardnonc1test1report dstrep, alglib.xparams _params) { int i = 0; dstrep.positive = srcrep.positive; if( srcrep.positive ) { alglib.ap.assert(srcrep.vidx>=0 && srcrep.vidxbcerr ) { bcerr = ve; bcidx = i; } } // // Check upper bound // if( hasbndu[i] && x[i]>bndu[i] ) { ve = (x[i]-bndu[i])*vs; if( ve>bcerr ) { bcerr = ve; bcidx = i; } } } } /************************************************************************* This subroutine checks violation of the general linear constraints. Constraints are assumed to be un-normalized and stored in the format "NEC equality ones followed by NIC inequality ones". On output it sets lcerr to the maximum scaled violation, lcidx to the source index of the most violating constraint (row indexes of CLEIC are mapped to the indexes of the "original" constraints via LCSrcIdx[] array. if lcerr=0 (say, if no constraints are violated) then lcidx=-1. If nonunits=false then s[] is not referenced at all (assumed unit). -- ALGLIB -- Copyright 7.11.2018 by Bochkanov Sergey *************************************************************************/ public static void checklcviolation(double[,] cleic, int[] lcsrcidx, int nec, int nic, double[] x, int n, ref double lcerr, ref int lcidx, alglib.xparams _params) { int i = 0; int j = 0; double cx = 0; double cnrm = 0; double v = 0; lcerr = 0; lcidx = 0; lcerr = 0; lcidx = -1; for(i=0; i<=nec+nic-1; i++) { cx = -cleic[i,n]; cnrm = 0; for(j=0; j<=n-1; j++) { v = cleic[i,j]; cx = cx+v*x[j]; cnrm = cnrm+v*v; } cnrm = Math.Sqrt(cnrm); cx = cx/apserv.coalesce(cnrm, 1, _params); if( ilcerr ) { lcerr = cx; lcidx = lcsrcidx[i]; } } } /************************************************************************* This subroutine checks violation of the nonlinear constraints. Fi[0] is the target value (ignored), Fi[1:NG+NH] are values of nonlinear constraints. On output it sets nlcerr to the scaled violation, nlcidx to the index of the most violating constraint. if nlcerr=0 (say, if no constraints are violated) then nlcidx=-1. If nonunits=false then s[] is not referenced at all (assumed unit). -- ALGLIB -- Copyright 7.11.2018 by Bochkanov Sergey *************************************************************************/ public static void checknlcviolation(double[] fi, int ng, int nh, ref double nlcerr, ref int nlcidx, alglib.xparams _params) { int i = 0; double v = 0; nlcerr = 0; nlcidx = 0; nlcerr = 0; nlcidx = -1; for(i=0; i<=ng+nh-1; i++) { v = fi[i+1]; if( inlcerr ) { nlcerr = v; nlcidx = i; } } } /************************************************************************* This subroutine is used to prepare threshold value which will be used for trimming of the target function (see comments on TrimFunction() for more information). This function accepts only one parameter: function value at the starting point. It returns threshold which will be used for trimming. -- ALGLIB -- Copyright 10.05.2011 by Bochkanov Sergey *************************************************************************/ public static void trimprepare(double f, ref double threshold, alglib.xparams _params) { threshold = 0; threshold = 10*(Math.Abs(f)+1); } /************************************************************************* This subroutine is used to "trim" target function, i.e. to do following transformation: { {F,G} if F=Threshold Such transformation allows us to solve problems with singularities by redefining function in such way that it becomes bounded from above. -- ALGLIB -- Copyright 10.05.2011 by Bochkanov Sergey *************************************************************************/ public static void trimfunction(ref double f, ref double[] g, int n, double threshold, alglib.xparams _params) { int i = 0; if( (double)(f)>=(double)(threshold) ) { f = threshold; for(i=0; i<=n-1; i++) { g[i] = 0.0; } } } /************************************************************************* This function enforces boundary constraints in the X. This function correctly (although a bit inefficient) handles BL[i] which are -INF and BU[i] which are +INF. We have NMain+NSlack dimensional X, with first NMain components bounded by BL/BU, and next NSlack ones bounded by non-negativity constraints. INPUT PARAMETERS X - array[NMain+NSlack], point BL - array[NMain], lower bounds (may contain -INF, when bound is not present) HaveBL - array[NMain], if HaveBL[i] is False, then i-th bound is not present BU - array[NMain], upper bounds (may contain +INF, when bound is not present) HaveBU - array[NMain], if HaveBU[i] is False, then i-th bound is not present OUTPUT PARAMETERS X - X with all constraints being enforced It returns True when constraints are consistent, False - when constraints are inconsistent. -- ALGLIB -- Copyright 10.01.2012 by Bochkanov Sergey *************************************************************************/ public static bool enforceboundaryconstraints(double[] x, double[] bl, bool[] havebl, double[] bu, bool[] havebu, int nmain, int nslack, alglib.xparams _params) { bool result = new bool(); int i = 0; result = false; for(i=0; i<=nmain-1; i++) { if( (havebl[i] && havebu[i]) && (double)(bl[i])>(double)(bu[i]) ) { return result; } if( havebl[i] && (double)(x[i])<(double)(bl[i]) ) { x[i] = bl[i]; } if( havebu[i] && (double)(x[i])>(double)(bu[i]) ) { x[i] = bu[i]; } } for(i=0; i<=nslack-1; i++) { if( (double)(x[nmain+i])<(double)(0) ) { x[nmain+i] = 0; } } result = true; return result; } /************************************************************************* This function projects gradient into feasible area of boundary constrained optimization problem. X can be infeasible with respect to boundary constraints. We have NMain+NSlack dimensional X, with first NMain components bounded by BL/BU, and next NSlack ones bounded by non-negativity constraints. INPUT PARAMETERS X - array[NMain+NSlack], point G - array[NMain+NSlack], gradient BL - lower bounds (may contain -INF, when bound is not present) HaveBL - if HaveBL[i] is False, then i-th bound is not present BU - upper bounds (may contain +INF, when bound is not present) HaveBU - if HaveBU[i] is False, then i-th bound is not present OUTPUT PARAMETERS G - projection of G. Components of G which satisfy one of the following (1) (X[I]<=BndL[I]) and (G[I]>0), OR (2) (X[I]>=BndU[I]) and (G[I]<0) are replaced by zeros. NOTE 1: this function assumes that constraints are feasible. It throws exception otherwise. NOTE 2: in fact, projection of ANTI-gradient is calculated, because this function trims components of -G which points outside of the feasible area. However, working with -G is considered confusing, because all optimization source work with G. -- ALGLIB -- Copyright 10.01.2012 by Bochkanov Sergey *************************************************************************/ public static void projectgradientintobc(double[] x, ref double[] g, double[] bl, bool[] havebl, double[] bu, bool[] havebu, int nmain, int nslack, alglib.xparams _params) { int i = 0; for(i=0; i<=nmain-1; i++) { alglib.ap.assert((!havebl[i] || !havebu[i]) || (double)(bl[i])<=(double)(bu[i]), "ProjectGradientIntoBC: internal error (infeasible constraints)"); if( (havebl[i] && (double)(x[i])<=(double)(bl[i])) && (double)(g[i])>(double)(0) ) { g[i] = 0; } if( (havebu[i] && (double)(x[i])>=(double)(bu[i])) && (double)(g[i])<(double)(0) ) { g[i] = 0; } } for(i=0; i<=nslack-1; i++) { if( (double)(x[nmain+i])<=(double)(0) && (double)(g[nmain+i])>(double)(0) ) { g[nmain+i] = 0; } } } /************************************************************************* Given a) initial point X0[NMain+NSlack] (feasible with respect to bound constraints) b) step vector alpha*D[NMain+NSlack] c) boundary constraints BndL[NMain], BndU[NMain] d) implicit non-negativity constraints for slack variables this function calculates bound on the step length subject to boundary constraints. It returns: * MaxStepLen - such step length that X0+MaxStepLen*alpha*D is exactly at the boundary given by constraints * VariableToFreeze - index of the constraint to be activated, 0 <= VariableToFreeze < NMain+NSlack * ValueToFreeze - value of the corresponding constraint. Notes: * it is possible that several constraints can be activated by the step at once. In such cases only one constraint is returned. It is caller responsibility to check other constraints. This function makes sure that we activate at least one constraint, and everything else is the responsibility of the caller. * steps smaller than MaxStepLen still can activate constraints due to numerical errors. Thus purpose of this function is not to guard against accidental activation of the constraints - quite the reverse, its purpose is to activate at least constraint upon performing step which is too long. * in case there is no constraints to activate, we return negative VariableToFreeze and zero MaxStepLen and ValueToFreeze. * this function assumes that constraints are consistent; it throws exception otherwise. INPUT PARAMETERS X - array[NMain+NSlack], point. Must be feasible with respect to bound constraints (exception will be thrown otherwise) D - array[NMain+NSlack], step direction alpha - scalar multiplier before D, alpha<>0 BndL - lower bounds, array[NMain] (may contain -INF, when bound is not present) HaveBndL - array[NMain], if HaveBndL[i] is False, then i-th bound is not present BndU - array[NMain], upper bounds (may contain +INF, when bound is not present) HaveBndU - array[NMain], if HaveBndU[i] is False, then i-th bound is not present NMain - number of main variables NSlack - number of slack variables OUTPUT PARAMETERS VariableToFreeze: * negative value = step is unbounded, ValueToFreeze=0, MaxStepLen=0. * non-negative value = at least one constraint, given by this parameter, will be activated upon performing maximum step. ValueToFreeze- value of the variable which will be constrained MaxStepLen - maximum length of the step. Can be zero when step vector looks outside of the feasible area. -- ALGLIB -- Copyright 10.01.2012 by Bochkanov Sergey *************************************************************************/ public static void calculatestepbound(double[] x, double[] d, double alpha, double[] bndl, bool[] havebndl, double[] bndu, bool[] havebndu, int nmain, int nslack, ref int variabletofreeze, ref double valuetofreeze, ref double maxsteplen, alglib.xparams _params) { int i = 0; double prevmax = 0; double initval = 0; variabletofreeze = 0; valuetofreeze = 0; maxsteplen = 0; alglib.ap.assert((double)(alpha)!=(double)(0), "CalculateStepBound: zero alpha"); variabletofreeze = -1; initval = math.maxrealnumber; maxsteplen = initval; for(i=0; i<=nmain-1; i++) { if( havebndl[i] && (double)(alpha*d[i])<(double)(0) ) { alglib.ap.assert((double)(x[i])>=(double)(bndl[i]), "CalculateStepBound: infeasible X"); prevmax = maxsteplen; maxsteplen = apserv.safeminposrv(x[i]-bndl[i], -(alpha*d[i]), maxsteplen, _params); if( (double)(maxsteplen)<(double)(prevmax) ) { variabletofreeze = i; valuetofreeze = bndl[i]; } } if( havebndu[i] && (double)(alpha*d[i])>(double)(0) ) { alglib.ap.assert((double)(x[i])<=(double)(bndu[i]), "CalculateStepBound: infeasible X"); prevmax = maxsteplen; maxsteplen = apserv.safeminposrv(bndu[i]-x[i], alpha*d[i], maxsteplen, _params); if( (double)(maxsteplen)<(double)(prevmax) ) { variabletofreeze = i; valuetofreeze = bndu[i]; } } } for(i=0; i<=nslack-1; i++) { if( (double)(alpha*d[nmain+i])<(double)(0) ) { alglib.ap.assert((double)(x[nmain+i])>=(double)(0), "CalculateStepBound: infeasible X"); prevmax = maxsteplen; maxsteplen = apserv.safeminposrv(x[nmain+i], -(alpha*d[nmain+i]), maxsteplen, _params); if( (double)(maxsteplen)<(double)(prevmax) ) { variabletofreeze = nmain+i; valuetofreeze = 0; } } } if( (double)(maxsteplen)==(double)(initval) ) { valuetofreeze = 0; maxsteplen = 0; } } /************************************************************************* This function postprocesses bounded step by: * analysing step length (whether it is equal to MaxStepLen) and activating constraint given by VariableToFreeze if needed * checking for additional bound constraints to activate This function uses final point of the step, quantities calculated by the CalculateStepBound() function. As result, it returns point which is exactly feasible with respect to boundary constraints. NOTE 1: this function does NOT handle and check linear equality constraints NOTE 2: when StepTaken=MaxStepLen we always activate at least one constraint INPUT PARAMETERS X - array[NMain+NSlack], final point to postprocess XPrev - array[NMain+NSlack], initial point BndL - lower bounds, array[NMain] (may contain -INF, when bound is not present) HaveBndL - array[NMain], if HaveBndL[i] is False, then i-th bound is not present BndU - array[NMain], upper bounds (may contain +INF, when bound is not present) HaveBndU - array[NMain], if HaveBndU[i] is False, then i-th bound is not present NMain - number of main variables NSlack - number of slack variables VariableToFreeze-result of CalculateStepBound() ValueToFreeze- result of CalculateStepBound() StepTaken - actual step length (actual step is equal to the possibly non-unit step direction vector times this parameter). StepTaken<=MaxStepLen. MaxStepLen - result of CalculateStepBound() OUTPUT PARAMETERS X - point bounded with respect to constraints. components corresponding to active constraints are exactly equal to the boundary values. RESULT: number of constraints activated in addition to previously active ones. Constraints which were DEACTIVATED are ignored (do not influence function value). -- ALGLIB -- Copyright 10.01.2012 by Bochkanov Sergey *************************************************************************/ public static int postprocessboundedstep(ref double[] x, double[] xprev, double[] bndl, bool[] havebndl, double[] bndu, bool[] havebndu, int nmain, int nslack, int variabletofreeze, double valuetofreeze, double steptaken, double maxsteplen, alglib.xparams _params) { int result = 0; int i = 0; bool wasactivated = new bool(); alglib.ap.assert(variabletofreeze<0 || (double)(steptaken)<=(double)(maxsteplen)); // // Activate constraints // if( variabletofreeze>=0 && (double)(steptaken)==(double)(maxsteplen) ) { x[variabletofreeze] = valuetofreeze; } for(i=0; i<=nmain-1; i++) { if( havebndl[i] && (double)(x[i])<(double)(bndl[i]) ) { x[i] = bndl[i]; } if( havebndu[i] && (double)(x[i])>(double)(bndu[i]) ) { x[i] = bndu[i]; } } for(i=0; i<=nslack-1; i++) { if( (double)(x[nmain+i])<=(double)(0) ) { x[nmain+i] = 0; } } // // Calculate number of constraints being activated // result = 0; for(i=0; i<=nmain-1; i++) { wasactivated = (double)(x[i])!=(double)(xprev[i]) && ((havebndl[i] && (double)(x[i])==(double)(bndl[i])) || (havebndu[i] && (double)(x[i])==(double)(bndu[i]))); wasactivated = wasactivated || variabletofreeze==i; if( wasactivated ) { result = result+1; } } for(i=0; i<=nslack-1; i++) { wasactivated = (double)(x[nmain+i])!=(double)(xprev[nmain+i]) && (double)(x[nmain+i])==(double)(0.0); wasactivated = wasactivated || variabletofreeze==nmain+i; if( wasactivated ) { result = result+1; } } return result; } /************************************************************************* The purpose of this function is to prevent algorithm from "unsticking" from the active bound constraints because of numerical noise in the gradient or Hessian. It is done by zeroing some components of the search direction D. D[i] is zeroed when both (a) and (b) are true: a) corresponding X[i] is exactly at the boundary b) |D[i]*S[i]| <= DropTol*Sqrt(SUM(D[i]^2*S[I]^2)) D can be step direction , antigradient, gradient, or anything similar. Sign of D does not matter, nor matters step length. NOTE 1: boundary constraints are expected to be consistent, as well as X is expected to be feasible. Exception will be thrown otherwise. INPUT PARAMETERS D - array[NMain+NSlack], direction X - array[NMain+NSlack], current point BndL - lower bounds, array[NMain] (may contain -INF, when bound is not present) HaveBndL - array[NMain], if HaveBndL[i] is False, then i-th bound is not present BndU - array[NMain], upper bounds (may contain +INF, when bound is not present) HaveBndU - array[NMain], if HaveBndU[i] is False, then i-th bound is not present S - array[NMain+NSlack], scaling of the variables NMain - number of main variables NSlack - number of slack variables DropTol - drop tolerance, >=0 OUTPUT PARAMETERS X - point bounded with respect to constraints. components corresponding to active constraints are exactly equal to the boundary values. -- ALGLIB -- Copyright 10.01.2012 by Bochkanov Sergey *************************************************************************/ public static void filterdirection(ref double[] d, double[] x, double[] bndl, bool[] havebndl, double[] bndu, bool[] havebndu, double[] s, int nmain, int nslack, double droptol, alglib.xparams _params) { int i = 0; double scalednorm = 0; bool isactive = new bool(); scalednorm = 0.0; for(i=0; i<=nmain+nslack-1; i++) { scalednorm = scalednorm+math.sqr(d[i]*s[i]); } scalednorm = Math.Sqrt(scalednorm); for(i=0; i<=nmain-1; i++) { alglib.ap.assert(!havebndl[i] || (double)(x[i])>=(double)(bndl[i]), "FilterDirection: infeasible point"); alglib.ap.assert(!havebndu[i] || (double)(x[i])<=(double)(bndu[i]), "FilterDirection: infeasible point"); isactive = (havebndl[i] && (double)(x[i])==(double)(bndl[i])) || (havebndu[i] && (double)(x[i])==(double)(bndu[i])); if( isactive && (double)(Math.Abs(d[i]*s[i]))<=(double)(droptol*scalednorm) ) { d[i] = 0.0; } } for(i=0; i<=nslack-1; i++) { alglib.ap.assert((double)(x[nmain+i])>=(double)(0), "FilterDirection: infeasible point"); if( (double)(x[nmain+i])==(double)(0) && (double)(Math.Abs(d[nmain+i]*s[nmain+i]))<=(double)(droptol*scalednorm) ) { d[nmain+i] = 0.0; } } } /************************************************************************* This function returns number of bound constraints whose state was changed (either activated or deactivated) when making step from XPrev to X. Constraints are considered: * active - when we are exactly at the boundary * inactive - when we are not at the boundary You should note that antigradient direction is NOT taken into account when we make decions on the constraint status. INPUT PARAMETERS X - array[NMain+NSlack], final point. Must be feasible with respect to bound constraints. XPrev - array[NMain+NSlack], initial point. Must be feasible with respect to bound constraints. BndL - lower bounds, array[NMain] (may contain -INF, when bound is not present) HaveBndL - array[NMain], if HaveBndL[i] is False, then i-th bound is not present BndU - array[NMain], upper bounds (may contain +INF, when bound is not present) HaveBndU - array[NMain], if HaveBndU[i] is False, then i-th bound is not present NMain - number of main variables NSlack - number of slack variables RESULT: number of constraints whose state was changed. -- ALGLIB -- Copyright 10.01.2012 by Bochkanov Sergey *************************************************************************/ public static int numberofchangedconstraints(double[] x, double[] xprev, double[] bndl, bool[] havebndl, double[] bndu, bool[] havebndu, int nmain, int nslack, alglib.xparams _params) { int result = 0; int i = 0; bool statuschanged = new bool(); result = 0; for(i=0; i<=nmain-1; i++) { if( (double)(x[i])!=(double)(xprev[i]) ) { statuschanged = false; if( havebndl[i] && ((double)(x[i])==(double)(bndl[i]) || (double)(xprev[i])==(double)(bndl[i])) ) { statuschanged = true; } if( havebndu[i] && ((double)(x[i])==(double)(bndu[i]) || (double)(xprev[i])==(double)(bndu[i])) ) { statuschanged = true; } if( statuschanged ) { result = result+1; } } } for(i=0; i<=nslack-1; i++) { if( (double)(x[nmain+i])!=(double)(xprev[nmain+i]) && ((double)(x[nmain+i])==(double)(0) || (double)(xprev[nmain+i])==(double)(0)) ) { result = result+1; } } return result; } /************************************************************************* This function finds feasible point of (NMain+NSlack)-dimensional problem subject to NMain explicit boundary constraints (some constraints can be omitted), NSlack implicit non-negativity constraints, K linear equality constraints. INPUT PARAMETERS X - array[NMain+NSlack], initial point. BndL - lower bounds, array[NMain] (may contain -INF, when bound is not present) HaveBndL - array[NMain], if HaveBndL[i] is False, then i-th bound is not present BndU - array[NMain], upper bounds (may contain +INF, when bound is not present) HaveBndU - array[NMain], if HaveBndU[i] is False, then i-th bound is not present NMain - number of main variables NSlack - number of slack variables CE - array[K,NMain+NSlack+1], equality constraints CE*x=b. Rows contain constraints, first NMain+NSlack columns contain coefficients before X[], last column contain right part. K - number of linear constraints EpsI - infeasibility (error in the right part) allowed in the solution OUTPUT PARAMETERS: X - feasible point or best infeasible point found before algorithm termination QPIts - number of QP iterations (for debug purposes) GPAIts - number of GPA iterations (for debug purposes) RESULT: True in case X is feasible, False - if it is infeasible. -- ALGLIB -- Copyright 20.01.2012 by Bochkanov Sergey *************************************************************************/ public static bool findfeasiblepoint(ref double[] x, double[] bndl, bool[] havebndl, double[] bndu, bool[] havebndu, int nmain, int nslack, double[,] ce, int k, double epsi, ref int qpits, ref int gpaits, alglib.xparams _params) { bool result = new bool(); int i = 0; int j = 0; int idx0 = 0; int idx1 = 0; double[] permx = new double[0]; double[] xn = new double[0]; double[] xa = new double[0]; double[] newtonstep = new double[0]; double[] g = new double[0]; double[] pg = new double[0]; double[] tau = new double[0]; double[] s = new double[0]; double armijostep = 0; double armijobeststep = 0; double armijobestfeas = 0; double v = 0; double vv = 0; double mx = 0; double feaserr = 0; double feaserr0 = 0; double feaserr1 = 0; double feasold = 0; double feasnew = 0; double pgnorm = 0; double vn = 0; double vd = 0; double stp = 0; int vartofreeze = 0; double valtofreeze = 0; double maxsteplen = 0; bool werechangesinconstraints = new bool(); bool stage1isover = new bool(); bool converged = new bool(); double[] activeconstraints = new double[0]; double[] tmpk = new double[0]; double[] colnorms = new double[0]; int nactive = 0; int nfree = 0; int[] p1 = new int[0]; int[] p2 = new int[0]; apserv.apbuffers buf = new apserv.apbuffers(); int itscount = 0; int itswithintolerance = 0; int maxitswithintolerance = 0; int badits = 0; int maxbadits = 0; int gparuns = 0; int maxarmijoruns = 0; double[,] permce = new double[0,0]; double[,] q = new double[0,0]; int i_ = 0; ce = (double[,])ce.Clone(); qpits = 0; gpaits = 0; maxitswithintolerance = 3; maxbadits = 3; maxarmijoruns = 5; qpits = 0; gpaits = 0; // // Initial enforcement of the feasibility with respect to boundary constraints // NOTE: after this block we assume that boundary constraints are consistent. // if( !enforceboundaryconstraints(x, bndl, havebndl, bndu, havebndu, nmain, nslack, _params) ) { result = false; return result; } if( k==0 ) { // // No linear constraints, we can exit right now // result = true; return result; } // // Scale rows of CE in such way that max(CE[i,0..nmain+nslack-1])=1 for any i=0..k-1 // for(i=0; i<=k-1; i++) { v = 0.0; for(j=0; j<=nmain+nslack-1; j++) { v = Math.Max(v, Math.Abs(ce[i,j])); } if( (double)(v)!=(double)(0) ) { v = 1/v; for(i_=0; i_<=nmain+nslack;i_++) { ce[i,i_] = v*ce[i,i_]; } } } // // Allocate temporaries // xn = new double[nmain+nslack]; xa = new double[nmain+nslack]; permx = new double[nmain+nslack]; g = new double[nmain+nslack]; pg = new double[nmain+nslack]; tmpk = new double[k]; permce = new double[k, nmain+nslack]; activeconstraints = new double[nmain+nslack]; newtonstep = new double[nmain+nslack]; s = new double[nmain+nslack]; colnorms = new double[nmain+nslack]; for(i=0; i<=nmain+nslack-1; i++) { s[i] = 1.0; colnorms[i] = 0.0; for(j=0; j<=k-1; j++) { colnorms[i] = colnorms[i]+math.sqr(ce[j,i]); } } // // K>0, we have linear equality constraints combined with bound constraints. // // Try to find feasible point as minimizer of the quadratic function // F(x) = 0.5*||CE*x-b||^2 = 0.5*x'*(CE'*CE)*x - (b'*CE)*x + 0.5*b'*b // subject to boundary constraints given by BL, BU and non-negativity of // the slack variables. BTW, we drop constant term because it does not // actually influences on the solution. // // Below we will assume that K>0. // itswithintolerance = 0; badits = 0; itscount = 0; while( true ) { // // Stage 0: check for exact convergence // converged = true; feaserr = feasibilityerror(ce, x, nmain, nslack, k, ref tmpk, _params); for(i=0; i<=k-1; i++) { // // Calculate MX - maximum term in the left part // // Terminate if error in the right part is not greater than 100*Eps*MX. // // IMPORTANT: we must perform check for non-strict inequality, i.e. to use <= instead of <. // it will allow us to easily handle situations with zero rows of CE. // // NOTE: it is important to calculate feasibility error with dedicated // function. Once we had a situation when usage of "inline" code // resulted in different numerical values calculated at different // parts of program for exactly same X. However, this value is // essential for algorithm's ability to terminate before entering // infinite loop, so reproducibility of numerical results is very // important. // mx = 0; v = -ce[i,nmain+nslack]; for(j=0; j<=nmain+nslack-1; j++) { mx = Math.Max(mx, Math.Abs(ce[i,j]*x[j])); v = v+ce[i,j]*x[j]; } converged = converged && (double)(Math.Abs(v))<=(double)(100*math.machineepsilon*mx); } feaserr0 = feaserr; if( converged ) { result = (double)(feaserr)<=(double)(epsi); return result; } // // Stage 1: equality constrained quadratic programming // // * treat active bound constraints as equality ones (constraint is considered // active when we are at the boundary, independently of the antigradient direction) // * calculate unrestricted Newton step to point XM (which may be infeasible) // calculate MaxStepLen = largest step in direction of XM which retains feasibility. // * perform bounded step from X to XN: // a) XN=XM (if XM is feasible) // b) XN=X-MaxStepLen*(XM-X) (otherwise) // * X := XN // * if XM (Newton step subject to currently active constraints) was feasible, goto Stage 2 // * repeat Stage 1 // // NOTE 1: in order to solve constrained qudratic subproblem we will have to reorder // variables in such way that ones corresponding to inactive constraints will // be first, and active ones will be last in the list. CE and X are now // [ xi ] // separated into two parts: CE = [CEi CEa], x = [ ], where CEi/Xi correspond // [ xa ] // to INACTIVE constraints, and CEa/Xa correspond to the ACTIVE ones. // // Now, instead of F=0.5*x'*(CE'*CE)*x - (b'*CE)*x + 0.5*b'*b, we have // F(xi) = 0.5*(CEi*xi,CEi*xi) + (CEa*xa-b,CEi*xi) + (0.5*CEa*xa-b,CEa*xa). // Here xa is considered constant, i.e. we optimize with respect to xi, leaving xa fixed. // // We can solve it by performing SVD of CEi and calculating pseudoinverse of the // Hessian matrix. Of course, we do NOT calculate pseudoinverse explicitly - we // just use singular vectors to perform implicit multiplication by it. // // while( true ) { // // Calculate G - gradient subject to equality constraints, // multiply it by inverse of the Hessian diagonal to obtain initial // step vector. // // Bound step subject to constraints which can be activated, // run Armijo search with increasing step size. // Search is terminated when feasibility error stops to decrease. // // NOTE: it is important to test for "stops to decrease" instead // of "starts to increase" in order to correctly handle cases with // zero CE. // armijobeststep = 0.0; feasibilityerrorgrad(ce, x, nmain, nslack, k, ref armijobestfeas, g, ref tmpk, _params); for(i=0; i<=nmain-1; i++) { if( havebndl[i] && (double)(x[i])==(double)(bndl[i]) ) { g[i] = 0.0; } if( havebndu[i] && (double)(x[i])==(double)(bndu[i]) ) { g[i] = 0.0; } } for(i=0; i<=nslack-1; i++) { if( (double)(x[nmain+i])==(double)(0.0) ) { g[nmain+i] = 0.0; } } v = 0.0; for(i=0; i<=nmain+nslack-1; i++) { if( (double)(math.sqr(colnorms[i]))!=(double)(0) ) { newtonstep[i] = -(g[i]/math.sqr(colnorms[i])); } else { newtonstep[i] = 0.0; } v = v+math.sqr(newtonstep[i]); } if( (double)(v)==(double)(0) ) { // // Constrained gradient is zero, QP iterations are over // break; } calculatestepbound(x, newtonstep, 1.0, bndl, havebndl, bndu, havebndu, nmain, nslack, ref vartofreeze, ref valtofreeze, ref maxsteplen, _params); if( vartofreeze>=0 && (double)(maxsteplen)==(double)(0) ) { // // Can not perform step, QP iterations are over // break; } if( vartofreeze>=0 ) { armijostep = Math.Min(1.0, maxsteplen); } else { armijostep = 1; } while( true ) { for(i_=0; i_<=nmain+nslack-1;i_++) { xa[i_] = x[i_]; } for(i_=0; i_<=nmain+nslack-1;i_++) { xa[i_] = xa[i_] + armijostep*newtonstep[i_]; } enforceboundaryconstraints(xa, bndl, havebndl, bndu, havebndu, nmain, nslack, _params); feaserr = feasibilityerror(ce, xa, nmain, nslack, k, ref tmpk, _params); if( (double)(feaserr)>=(double)(armijobestfeas) ) { break; } armijobestfeas = feaserr; armijobeststep = armijostep; armijostep = 2.0*armijostep; } for(i_=0; i_<=nmain+nslack-1;i_++) { x[i_] = x[i_] + armijobeststep*newtonstep[i_]; } enforceboundaryconstraints(x, bndl, havebndl, bndu, havebndu, nmain, nslack, _params); // // Determine number of active and free constraints // nactive = 0; for(i=0; i<=nmain-1; i++) { activeconstraints[i] = 0; if( havebndl[i] && (double)(x[i])==(double)(bndl[i]) ) { activeconstraints[i] = 1; } if( havebndu[i] && (double)(x[i])==(double)(bndu[i]) ) { activeconstraints[i] = 1; } if( (double)(activeconstraints[i])>(double)(0) ) { nactive = nactive+1; } } for(i=0; i<=nslack-1; i++) { activeconstraints[nmain+i] = 0; if( (double)(x[nmain+i])==(double)(0.0) ) { activeconstraints[nmain+i] = 1; } if( (double)(activeconstraints[nmain+i])>(double)(0) ) { nactive = nactive+1; } } nfree = nmain+nslack-nactive; if( nfree==0 ) { break; } qpits = qpits+1; // // Reorder variables: CE is reordered to PermCE, X is reordered to PermX // tsort.tagsortbuf(ref activeconstraints, nmain+nslack, ref p1, ref p2, buf, _params); for(i=0; i<=k-1; i++) { for(j=0; j<=nmain+nslack-1; j++) { permce[i,j] = ce[i,j]; } } for(j=0; j<=nmain+nslack-1; j++) { permx[j] = x[j]; } for(j=0; j<=nmain+nslack-1; j++) { if( p2[j]!=j ) { idx0 = p2[j]; idx1 = j; for(i=0; i<=k-1; i++) { v = permce[i,idx0]; permce[i,idx0] = permce[i,idx1]; permce[i,idx1] = v; } v = permx[idx0]; permx[idx0] = permx[idx1]; permx[idx1] = v; } } // // Calculate (unprojected) gradient: // G(xi) = CEi'*(CEi*xi + CEa*xa - b) // for(i=0; i<=nfree-1; i++) { g[i] = 0; } for(i=0; i<=k-1; i++) { v = 0.0; for(i_=0; i_<=nmain+nslack-1;i_++) { v += permce[i,i_]*permx[i_]; } tmpk[i] = v-ce[i,nmain+nslack]; } for(i=0; i<=k-1; i++) { v = tmpk[i]; for(i_=0; i_<=nfree-1;i_++) { g[i_] = g[i_] + v*permce[i,i_]; } } // // Calculate Newton step using pseudoinverse PermCE: // F(xi) = 0.5*xi'*H*xi + g'*xi (Taylor decomposition) // XN = -H^(-1)*g (new point, solution of the QP subproblem) // H = CEi'*CEi // H^(-1) can be calculated via QR or LQ decomposition (see below) // step = -H^(-1)*g // // NOTE: PermCE is destroyed after this block // for(i=0; i<=nmain+nslack-1; i++) { newtonstep[i] = 0; } if( k<=nfree ) { // // CEi = L*Q // H = Q'*L'*L*Q // inv(H) = Q'*inv(L)*inv(L')*Q // // NOTE: we apply minor regularizing perturbation to diagonal of L, // which is equal to 10*K*Eps // ortfac.rmatrixlq(ref permce, k, nfree, ref tau, _params); ortfac.rmatrixlqunpackq(permce, k, nfree, tau, k, ref q, _params); v = 0; for(i=0; i<=k-1; i++) { v = Math.Max(v, Math.Abs(permce[i,i])); } v = apserv.coalesce(v, 1, _params); for(i=0; i<=k-1; i++) { permce[i,i] = permce[i,i]+10*k*math.machineepsilon*v; } ablas.rmatrixgemv(k, nfree, 1.0, q, 0, 0, 0, g, 0, 0.0, tmpk, 0, _params); ablas.rmatrixtrsv(k, permce, 0, 0, false, false, 1, tmpk, 0, _params); ablas.rmatrixtrsv(k, permce, 0, 0, false, false, 0, tmpk, 0, _params); ablas.rmatrixgemv(nfree, k, -1.0, q, 0, 0, 1, tmpk, 0, 0.0, newtonstep, 0, _params); } else { // // CEi = Q*R // H = R'*R // inv(H) = inv(R)*inv(R') // // NOTE: we apply minor regularizing perturbation to diagonal of R, // which is equal to 10*K*Eps // ortfac.rmatrixqr(ref permce, k, nfree, ref tau, _params); v = 0; for(i=0; i<=nfree-1; i++) { v = Math.Max(v, Math.Abs(permce[i,i])); } v = apserv.coalesce(v, 1, _params); for(i=0; i<=nfree-1; i++) { vv = 10*nfree*math.machineepsilon*v; if( (double)(permce[i,i])<(double)(0) ) { vv = -vv; } permce[i,i] = permce[i,i]+vv; } for(i_=0; i_<=nfree-1;i_++) { newtonstep[i_] = -g[i_]; } ablas.rmatrixtrsv(nfree, permce, 0, 0, true, false, 1, newtonstep, 0, _params); ablas.rmatrixtrsv(nfree, permce, 0, 0, true, false, 0, newtonstep, 0, _params); } // // Post-reordering of Newton step // for(j=nmain+nslack-1; j>=0; j--) { if( p2[j]!=j ) { idx0 = p2[j]; idx1 = j; v = newtonstep[idx0]; newtonstep[idx0] = newtonstep[idx1]; newtonstep[idx1] = v; } } // // NewtonStep contains Newton step subject to active bound constraints. // // Such step leads us to the minimizer of the equality constrained F, // but such minimizer may be infeasible because some constraints which // are inactive at the initial point can be violated at the solution. // // Thus, we perform optimization in two stages: // a) perform bounded Newton step, i.e. step in the Newton direction // until activation of the first constraint // b) in case (MaxStepLen>0)and(MaxStepLen<1), perform additional iteration // of the Armijo line search in the rest of the Newton direction. // calculatestepbound(x, newtonstep, 1.0, bndl, havebndl, bndu, havebndu, nmain, nslack, ref vartofreeze, ref valtofreeze, ref maxsteplen, _params); if( vartofreeze>=0 && (double)(maxsteplen)==(double)(0) ) { // // Activation of the constraints prevent us from performing step, // QP iterations are over // break; } if( vartofreeze>=0 ) { v = Math.Min(1.0, maxsteplen); } else { v = 1.0; } for(i_=0; i_<=nmain+nslack-1;i_++) { xn[i_] = v*newtonstep[i_]; } for(i_=0; i_<=nmain+nslack-1;i_++) { xn[i_] = xn[i_] + x[i_]; } postprocessboundedstep(ref xn, x, bndl, havebndl, bndu, havebndu, nmain, nslack, vartofreeze, valtofreeze, v, maxsteplen, _params); if( (double)(maxsteplen)>(double)(0) && (double)(maxsteplen)<(double)(1) ) { // // Newton step was restricted by activation of the constraints, // perform Armijo iteration. // // Initial estimate for best step is zero step. We try different // step sizes, from the 1-MaxStepLen (residual of the full Newton // step) to progressively smaller and smaller steps. // armijobeststep = 0.0; armijobestfeas = feasibilityerror(ce, xn, nmain, nslack, k, ref tmpk, _params); armijostep = 1-maxsteplen; for(j=0; j<=maxarmijoruns-1; j++) { for(i_=0; i_<=nmain+nslack-1;i_++) { xa[i_] = xn[i_]; } for(i_=0; i_<=nmain+nslack-1;i_++) { xa[i_] = xa[i_] + armijostep*newtonstep[i_]; } enforceboundaryconstraints(xa, bndl, havebndl, bndu, havebndu, nmain, nslack, _params); feaserr = feasibilityerror(ce, xa, nmain, nslack, k, ref tmpk, _params); if( (double)(feaserr)<(double)(armijobestfeas) ) { armijobestfeas = feaserr; armijobeststep = armijostep; } armijostep = 0.5*armijostep; } for(i_=0; i_<=nmain+nslack-1;i_++) { xa[i_] = xn[i_]; } for(i_=0; i_<=nmain+nslack-1;i_++) { xa[i_] = xa[i_] + armijobeststep*newtonstep[i_]; } enforceboundaryconstraints(xa, bndl, havebndl, bndu, havebndu, nmain, nslack, _params); } else { // // Armijo iteration is not performed // for(i_=0; i_<=nmain+nslack-1;i_++) { xa[i_] = xn[i_]; } } stage1isover = (double)(maxsteplen)>=(double)(1) || (double)(maxsteplen)==(double)(0); // // Calculate feasibility errors for old and new X. // These quantinies are used for debugging purposes only. // However, we can leave them in release code because performance impact is insignificant. // // Update X. Exit if needed. // feasold = feasibilityerror(ce, x, nmain, nslack, k, ref tmpk, _params); feasnew = feasibilityerror(ce, xa, nmain, nslack, k, ref tmpk, _params); if( (double)(feasnew)>=(double)(feasold) ) { break; } for(i_=0; i_<=nmain+nslack-1;i_++) { x[i_] = xa[i_]; } if( stage1isover ) { break; } } // // Stage 2: gradient projection algorithm (GPA) // // * calculate feasibility error (with respect to linear equality constraints) // * calculate gradient G of F, project it into feasible area (G => PG) // * exit if norm(PG) is exactly zero or feasibility error is smaller than EpsC // * let XM be exact minimum of F along -PG (XM may be infeasible). // calculate MaxStepLen = largest step in direction of -PG which retains feasibility. // * perform bounded step from X to XN: // a) XN=XM (if XM is feasible) // b) XN=X-MaxStepLen*PG (otherwise) // * X := XN // * stop after specified number of iterations or when no new constraints was activated // // NOTES: // * grad(F) = (CE'*CE)*x - (b'*CE)^T // * CE[i] denotes I-th row of CE // * XM = X+stp*(-PG) where stp=(grad(F(X)),PG)/(CE*PG,CE*PG). // Here PG is a projected gradient, but in fact it can be arbitrary non-zero // direction vector - formula for minimum of F along PG still will be correct. // werechangesinconstraints = false; for(gparuns=1; gparuns<=k; gparuns++) { // // calculate feasibility error and G // feasibilityerrorgrad(ce, x, nmain, nslack, k, ref feaserr, g, ref tmpk, _params); // // project G, filter it (strip numerical noise) // for(i_=0; i_<=nmain+nslack-1;i_++) { pg[i_] = g[i_]; } projectgradientintobc(x, ref pg, bndl, havebndl, bndu, havebndu, nmain, nslack, _params); filterdirection(ref pg, x, bndl, havebndl, bndu, havebndu, s, nmain, nslack, 1.0E-9, _params); for(i=0; i<=nmain+nslack-1; i++) { if( (double)(math.sqr(colnorms[i]))!=(double)(0) ) { pg[i] = pg[i]/math.sqr(colnorms[i]); } else { pg[i] = 0.0; } } // // Check GNorm and feasibility. // Exit when GNorm is exactly zero. // pgnorm = 0.0; for(i_=0; i_<=nmain+nslack-1;i_++) { pgnorm += pg[i_]*pg[i_]; } pgnorm = Math.Sqrt(pgnorm); if( (double)(pgnorm)==(double)(0) ) { result = (double)(feaserr)<=(double)(epsi); return result; } // // calculate planned step length // vn = 0.0; for(i_=0; i_<=nmain+nslack-1;i_++) { vn += g[i_]*pg[i_]; } vd = 0; ablas.rmatrixgemv(k, nmain+nslack, 1.0, ce, 0, 0, 0, pg, 0, 0.0, tmpk, 0, _params); for(i=0; i<=k-1; i++) { vd = vd+math.sqr(tmpk[i]); } stp = vn/vd; // // Calculate step bound. // Perform bounded step and post-process it // calculatestepbound(x, pg, -1.0, bndl, havebndl, bndu, havebndu, nmain, nslack, ref vartofreeze, ref valtofreeze, ref maxsteplen, _params); if( vartofreeze>=0 && (double)(maxsteplen)==(double)(0) ) { result = false; return result; } if( vartofreeze>=0 ) { v = Math.Min(stp, maxsteplen); } else { v = stp; } for(i_=0; i_<=nmain+nslack-1;i_++) { xn[i_] = x[i_]; } for(i_=0; i_<=nmain+nslack-1;i_++) { xn[i_] = xn[i_] - v*pg[i_]; } postprocessboundedstep(ref xn, x, bndl, havebndl, bndu, havebndu, nmain, nslack, vartofreeze, valtofreeze, v, maxsteplen, _params); // // update X // check stopping criteria // werechangesinconstraints = werechangesinconstraints || numberofchangedconstraints(xn, x, bndl, havebndl, bndu, havebndu, nmain, nslack, _params)>0; for(i_=0; i_<=nmain+nslack-1;i_++) { x[i_] = xn[i_]; } gpaits = gpaits+1; if( !werechangesinconstraints ) { break; } } // // Stage 3: decide to stop algorithm or not to stop // // 1. we can stop when last GPA run did NOT changed constraints status. // It means that we've found final set of the active constraints even // before GPA made its run. And it means that Newton step moved us to // the minimum subject to the present constraints. // Depending on feasibility error, True or False is returned. // feaserr = feasibilityerror(ce, x, nmain, nslack, k, ref tmpk, _params); feaserr1 = feaserr; if( (double)(feaserr1)>=(double)(feaserr0*(1-1000*math.machineepsilon)) ) { apserv.inc(ref badits, _params); } else { badits = 0; } if( (double)(feaserr)<=(double)(epsi) ) { apserv.inc(ref itswithintolerance, _params); } else { itswithintolerance = 0; } if( (!werechangesinconstraints || itswithintolerance>=maxitswithintolerance) || badits>=maxbadits ) { result = (double)(feaserr)<=(double)(epsi); return result; } itscount = itscount+1; // // Block below is never executed; it is necessary just to avoid // "unreachable code" warning about automatically generated code. // // We just need a way to transfer control to the end of the function, // even a fake way which is never actually traversed. // if( apserv.alwaysfalse(_params) ) { result = false; alglib.ap.assert(false); break; } } return result; } /************************************************************************* This function checks that input derivatives are right. First it scales parameters DF0 and DF1 from segment [A;B] to [0;1]. Then it builds Hermite spline and derivative of it in 0.5. Search scale as Max(DF0,DF1, |F0-F1|). Right derivative has to satisfy condition: |H-F|/S<=0,001, |H'-F'|/S<=0,001. INPUT PARAMETERS: F0 - function's value in X-TestStep point; DF0 - derivative's value in X-TestStep point; F1 - function's value in X+TestStep point; DF1 - derivative's value in X+TestStep point; F - testing function's value; DF - testing derivative's value; Width- width of verification segment. RESULT: If input derivatives is right then function returns true, else function returns false. -- ALGLIB -- Copyright 29.05.2012 by Bochkanov Sergey *************************************************************************/ public static bool derivativecheck(double f0, double df0, double f1, double df1, double f, double df, double width, alglib.xparams _params) { bool result = new bool(); double s = 0; double h = 0; double dh = 0; // // Rescale input data to [0,1] // df = width*df; df0 = width*df0; df1 = width*df1; // // Compute error scale, two sources are used: // * magnitudes of derivatives and secants // * magnitudes of input data times sqrt(machine_epsilon) // s = 0.0; s = Math.Max(s, Math.Abs(df0)); s = Math.Max(s, Math.Abs(df1)); s = Math.Max(s, Math.Abs(f1-f0)); s = Math.Max(s, Math.Sqrt(math.machineepsilon)*Math.Abs(f0)); s = Math.Max(s, Math.Sqrt(math.machineepsilon)*Math.Abs(f1)); // // Compute H and dH/dX at the middle of interval // h = 0.5*(f0+f1)+0.125*(df0-df1); dh = 1.5*(f1-f0)-0.250*(df0+df1); // // Check // if( (double)(s)!=(double)(0) ) { if( (double)(Math.Abs(h-f)/s)>(double)(0.001) || (double)(Math.Abs(dh-df)/s)>(double)(0.001) ) { result = false; return result; } } else { if( (double)(h-f)!=(double)(0.0) || (double)(dh-df)!=(double)(0.0) ) { result = false; return result; } } result = true; return result; } /************************************************************************* Having quadratic target function f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b) and its parabolic model along direction D F(x0+alpha*D) = D2*alpha^2 + D1*alpha this function estimates numerical errors in the coefficients of the model. It is important that this function does NOT calculate D1/D2 - it only estimates numerical errors introduced during evaluation and compares their magnitudes against magnitudes of numerical errors. As result, one of three outcomes is returned for each coefficient: * "true" coefficient is almost surely positive * "true" coefficient is almost surely negative * numerical errors in coefficient are so large that it can not be reliably distinguished from zero INPUT PARAMETERS: AbsASum - SUM(|A[i,j]|) AbsASum2- SUM(A[i,j]^2) MB - max(|B|) MX - max(|X|) MD - max(|D|) D1 - linear coefficient D2 - quadratic coefficient OUTPUT PARAMETERS: D1Est - estimate of D1 sign, accounting for possible numerical errors: * >0 means "almost surely positive" (D1>0 and large) * <0 means "almost surely negative" (D1<0 and large) * =0 means "pessimistic estimate of numerical errors in D1 is larger than magnitude of D1 itself; it is impossible to reliably distinguish D1 from zero". D2Est - estimate of D2 sign, accounting for possible numerical errors: * >0 means "almost surely positive" (D2>0 and large) * <0 means "almost surely negative" (D2<0 and large) * =0 means "pessimistic estimate of numerical errors in D2 is larger than magnitude of D2 itself; it is impossible to reliably distinguish D2 from zero". -- ALGLIB -- Copyright 14.05.2014 by Bochkanov Sergey *************************************************************************/ public static void estimateparabolicmodel(double absasum, double absasum2, double mx, double mb, double md, double d1, double d2, ref int d1est, ref int d2est, alglib.xparams _params) { double d1esterror = 0; double d2esterror = 0; double eps = 0; double e1 = 0; double e2 = 0; d1est = 0; d2est = 0; // // Error estimates: // // * error in D1=d'*(A*x+b) is estimated as // ED1 = eps*MAX_ABS(D)*(MAX_ABS(X)*ENORM(A)+MAX_ABS(B)) // * error in D2=0.5*d'*A*d is estimated as // ED2 = eps*MAX_ABS(D)^2*ENORM(A) // // Here ENORM(A) is some pseudo-norm which reflects the way numerical // error accumulates during addition. Two ways of accumulation are // possible - worst case (errors always increase) and mean-case (errors // may cancel each other). We calculate geometrical average of both: // * ENORM_WORST(A) = SUM(|A[i,j]|) error in N-term sum grows as O(N) // * ENORM_MEAN(A) = SQRT(SUM(A[i,j]^2)) error in N-term sum grows as O(sqrt(N)) // * ENORM(A) = SQRT(ENORM_WORST(A),ENORM_MEAN(A)) // eps = 4*math.machineepsilon; e1 = eps*md*(mx*absasum+mb); e2 = eps*md*(mx*Math.Sqrt(absasum2)+mb); d1esterror = Math.Sqrt(e1*e2); if( (double)(Math.Abs(d1))<=(double)(d1esterror) ) { d1est = 0; } else { d1est = Math.Sign(d1); } e1 = eps*md*md*absasum; e2 = eps*md*md*Math.Sqrt(absasum2); d2esterror = Math.Sqrt(e1*e2); if( (double)(Math.Abs(d2))<=(double)(d2esterror) ) { d2est = 0; } else { d2est = Math.Sign(d2); } } /************************************************************************* This function calculates inexact rank-K preconditioner for Hessian matrix H=D+W'*C*W, where: * H is a Hessian matrix, which is approximated by D/W/C * D is a diagonal matrix with positive entries * W is a rank-K correction * C is a diagonal factor of rank-K correction This preconditioner is inexact but fast - it requires O(N*K) time to be applied. Its main purpose - to be used in barrier/penalty/AUL methods, where ill-conditioning is created by combination of two factors: * simple bounds on variables => ill-conditioned D * general barrier/penalty => correction W with large coefficient C (makes problem ill-conditioned) but W itself is well conditioned. Preconditioner P is calculated by artificially constructing a set of BFGS updates which tries to reproduce behavior of H: * Sk = Wk (k-th row of W) * Yk = (D+Wk'*Ck*Wk)*Sk * Yk/Sk are reordered by ascending of C[k]*norm(Wk)^2 Here we assume that rows of Wk are orthogonal or nearly orthogonal, which allows us to have O(N*K+K^2) update instead of O(N*K^2) one. Reordering of updates is essential for having good performance on non-orthogonal problems (updates which do not add much of curvature are added first, and updates which add very large eigenvalues are added last and override effect of the first updates). On input this function takes direction S and components of H. On output it returns inv(H)*S -- ALGLIB -- Copyright 30.06.2014 by Bochkanov Sergey *************************************************************************/ public static void inexactlbfgspreconditioner(double[] s, int n, double[] d, double[] c, double[,] w, int k, precbuflbfgs buf, alglib.xparams _params) { int idx = 0; int i = 0; int j = 0; double v = 0; double v0 = 0; double v1 = 0; double vx = 0; double vy = 0; int i_ = 0; apserv.rvectorsetlengthatleast(ref buf.norms, k, _params); apserv.rvectorsetlengthatleast(ref buf.alpha, k, _params); apserv.rvectorsetlengthatleast(ref buf.rho, k, _params); apserv.rmatrixsetlengthatleast(ref buf.yk, k, n, _params); apserv.ivectorsetlengthatleast(ref buf.idx, k, _params); // // Check inputs // for(i=0; i<=n-1; i++) { alglib.ap.assert((double)(d[i])>(double)(0), "InexactLBFGSPreconditioner: D[]<=0"); } for(i=0; i<=k-1; i++) { alglib.ap.assert((double)(c[i])>=(double)(0), "InexactLBFGSPreconditioner: C[]<0"); } // // Reorder linear terms according to increase of second derivative. // Fill Norms[] array. // for(idx=0; idx<=k-1; idx++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += w[idx,i_]*w[idx,i_]; } buf.norms[idx] = v*c[idx]; buf.idx[idx] = idx; } tsort.tagsortfasti(ref buf.norms, ref buf.idx, ref buf.bufa, ref buf.bufb, k, _params); // // Apply updates // for(idx=0; idx<=k-1; idx++) { // // Select update to perform (ordered by ascending of second derivative) // i = buf.idx[idx]; // // Calculate YK and Rho // v = 0.0; for(i_=0; i_<=n-1;i_++) { v += w[i,i_]*w[i,i_]; } v = v*c[i]; for(j=0; j<=n-1; j++) { buf.yk[i,j] = (d[j]+v)*w[i,j]; } v = 0.0; v0 = 0.0; v1 = 0.0; for(j=0; j<=n-1; j++) { vx = w[i,j]; vy = buf.yk[i,j]; v = v+vx*vy; v0 = v0+vx*vx; v1 = v1+vy*vy; } if( ((double)(v)>(double)(0) && (double)(v0*v1)>(double)(0)) && (double)(v/Math.Sqrt(v0*v1))>(double)(n*10*math.machineepsilon) ) { buf.rho[i] = 1/v; } else { buf.rho[i] = 0.0; } } for(idx=k-1; idx>=0; idx--) { // // Select update to perform (ordered by ascending of second derivative) // i = buf.idx[idx]; // // Calculate Alpha[] according to L-BFGS algorithm // and update S[] // v = 0.0; for(i_=0; i_<=n-1;i_++) { v += w[i,i_]*s[i_]; } v = buf.rho[i]*v; buf.alpha[i] = v; for(i_=0; i_<=n-1;i_++) { s[i_] = s[i_] - v*buf.yk[i,i_]; } } for(j=0; j<=n-1; j++) { s[j] = s[j]/d[j]; } for(idx=0; idx<=k-1; idx++) { // // Select update to perform (ordered by ascending of second derivative) // i = buf.idx[idx]; // // Calculate Beta according to L-BFGS algorithm // and update S[] // v = 0.0; for(i_=0; i_<=n-1;i_++) { v += buf.yk[i,i_]*s[i_]; } v = buf.alpha[i]-buf.rho[i]*v; for(i_=0; i_<=n-1;i_++) { s[i_] = s[i_] + v*w[i,i_]; } } } /************************************************************************* This function prepares exact low-rank preconditioner for Hessian matrix H=D+W'*C*W, where: * H is a Hessian matrix, which is approximated by D/W/C * D is a diagonal matrix with positive entries * W is a rank-K correction * C is a diagonal factor of rank-K correction, positive semidefinite This preconditioner is exact but relatively slow - it requires O(N*K^2) time to be prepared and O(N*K) time to be applied. It is calculated with the help of Woodbury matrix identity. It should be used as follows: * PrepareLowRankPreconditioner() call PREPARES data structure * subsequent calls to ApplyLowRankPreconditioner() APPLY preconditioner to user-specified search direction. -- ALGLIB -- Copyright 30.06.2014 by Bochkanov Sergey *************************************************************************/ public static void preparelowrankpreconditioner(double[] d, double[] c, double[,] w, int n, int k, precbuflowrank buf, alglib.xparams _params) { int i = 0; int j = 0; double v = 0; bool b = new bool(); // // Check inputs // alglib.ap.assert(n>0, "PrepareLowRankPreconditioner: N<=0"); alglib.ap.assert(k>=0, "PrepareLowRankPreconditioner: N<=0"); for(i=0; i<=n-1; i++) { alglib.ap.assert((double)(d[i])>(double)(0), "PrepareLowRankPreconditioner: D[]<=0"); } for(i=0; i<=k-1; i++) { alglib.ap.assert((double)(c[i])>=(double)(0), "PrepareLowRankPreconditioner: C[]<0"); } // // Prepare buffer structure; skip zero entries of update. // apserv.rvectorsetlengthatleast(ref buf.d, n, _params); apserv.rmatrixsetlengthatleast(ref buf.v, k, n, _params); apserv.rvectorsetlengthatleast(ref buf.bufc, k, _params); apserv.rmatrixsetlengthatleast(ref buf.bufw, k+1, n, _params); buf.n = n; buf.k = 0; for(i=0; i<=k-1; i++) { // // Estimate magnitude of update row; skip zero rows (either W or C are zero) // v = 0.0; for(j=0; j<=n-1; j++) { v = v+w[i,j]*w[i,j]; } v = v*c[i]; if( (double)(v)==(double)(0) ) { continue; } alglib.ap.assert((double)(v)>(double)(0), "PrepareLowRankPreconditioner: internal error"); // // Copy non-zero update to buffer // buf.bufc[buf.k] = c[i]; for(j=0; j<=n-1; j++) { buf.v[buf.k,j] = w[i,j]; buf.bufw[buf.k,j] = w[i,j]; } apserv.inc(ref buf.k, _params); } // // Reset K (for convenience) // k = buf.k; // // Prepare diagonal factor; quick exit for K=0 // for(i=0; i<=n-1; i++) { buf.d[i] = 1/d[i]; } if( k==0 ) { return; } // // Use Woodbury matrix identity // apserv.rmatrixsetlengthatleast(ref buf.bufz, k, k, _params); for(i=0; i<=k-1; i++) { for(j=0; j<=k-1; j++) { buf.bufz[i,j] = 0.0; } } for(i=0; i<=k-1; i++) { buf.bufz[i,i] = 1/buf.bufc[i]; } for(j=0; j<=n-1; j++) { buf.bufw[k,j] = 1/Math.Sqrt(d[j]); } for(i=0; i<=k-1; i++) { for(j=0; j<=n-1; j++) { buf.bufw[i,j] = buf.bufw[i,j]*buf.bufw[k,j]; } } ablas.rmatrixgemm(k, k, n, 1.0, buf.bufw, 0, 0, 0, buf.bufw, 0, 0, 1, 1.0, buf.bufz, 0, 0, _params); b = trfac.spdmatrixcholeskyrec(ref buf.bufz, 0, k, true, ref buf.tmp, _params); alglib.ap.assert(b, "PrepareLowRankPreconditioner: internal error (Cholesky failure)"); ablas.rmatrixlefttrsm(k, n, buf.bufz, 0, 0, true, false, 1, buf.v, 0, 0, _params); for(i=0; i<=k-1; i++) { for(j=0; j<=n-1; j++) { buf.v[i,j] = buf.v[i,j]*buf.d[j]; } } } /************************************************************************* This function apply exact low-rank preconditioner prepared by PrepareLowRankPreconditioner function (see its comments for more information). -- ALGLIB -- Copyright 30.06.2014 by Bochkanov Sergey *************************************************************************/ public static void applylowrankpreconditioner(double[] s, precbuflowrank buf, alglib.xparams _params) { int n = 0; int k = 0; int i = 0; int j = 0; double v = 0; n = buf.n; k = buf.k; apserv.rvectorsetlengthatleast(ref buf.tmp, n, _params); for(j=0; j<=n-1; j++) { buf.tmp[j] = buf.d[j]*s[j]; } for(i=0; i<=k-1; i++) { v = 0.0; for(j=0; j<=n-1; j++) { v = v+buf.v[i,j]*s[j]; } for(j=0; j<=n-1; j++) { buf.tmp[j] = buf.tmp[j]-v*buf.v[i,j]; } } for(i=0; i<=n-1; i++) { s[i] = buf.tmp[i]; } } /************************************************************************* This subroutine initializes smoothness monitor at the beginning of the optimization session. It is possible to perform "dummy" initialization with N=K=0. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void smoothnessmonitorinit(smoothnessmonitor monitor, int n, int k, bool checksmoothness, alglib.xparams _params) { monitor.n = n; monitor.k = k; monitor.checksmoothness = checksmoothness; monitor.linesearchspoiled = false; monitor.linesearchstarted = false; monitor.enqueuedcnt = 0; monitor.sortedcnt = 0; monitor.nonc0currentrating = 0.0; monitor.nonc1currentrating = 0.0; optguardapi.optguardinitinternal(monitor.rep, n, k, _params); monitor.nonc1test0strrating = 0.0; monitor.nonc1test0lngrating = 0.0; monitor.nonc1test0strrep.positive = false; monitor.nonc1test0lngrep.positive = false; monitor.nonc1test1strrating = 0.0; monitor.nonc1test1lngrating = 0.0; monitor.nonc1test1strrep.positive = false; monitor.nonc1test1lngrep.positive = false; monitor.badgradhasxj = false; monitor.rstateg0.ia = new int[4+1]; monitor.rstateg0.ra = new double[3+1]; monitor.rstateg0.stage = -1; } /************************************************************************* This subroutine starts line search -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void smoothnessmonitorstartlinesearch(smoothnessmonitor monitor, double[] x, double[] fi, double[,] jac, alglib.xparams _params) { int n = 0; int k = 0; int i = 0; int j = 0; double v = 0; n = monitor.n; k = monitor.k; // // Skip if inactive or spoiled by NAN // if( !monitor.checksmoothness ) { return; } v = 0; for(i=0; i<=n-1; i++) { v = 0.5*v+x[i]; } for(i=0; i<=k-1; i++) { v = 0.5*v+fi[i]; } for(i=0; i<=k-1; i++) { for(j=0; j<=n-1; j++) { v = 0.5*v+jac[i,j]; } } if( !math.isfinite(v) ) { monitor.linesearchspoiled = true; return; } // // Finalize previous line search // if( monitor.enqueuedcnt>0 ) { smoothnessmonitorfinalizelinesearch(monitor, _params); } // // Store initial point // monitor.linesearchstarted = true; monitor.enqueuedcnt = 1; apserv.rvectorgrowto(ref monitor.enqueuedstp, monitor.enqueuedcnt, _params); apserv.rvectorgrowto(ref monitor.enqueuedx, monitor.enqueuedcnt*n, _params); apserv.rvectorgrowto(ref monitor.enqueuedfunc, monitor.enqueuedcnt*k, _params); apserv.rmatrixgrowrowsto(ref monitor.enqueuedjac, monitor.enqueuedcnt*k, n, _params); monitor.enqueuedstp[0] = 0.0; for(j=0; j<=n-1; j++) { monitor.enqueuedx[j] = x[j]; } for(i=0; i<=k-1; i++) { monitor.enqueuedfunc[i] = fi[i]; } for(i=0; i<=k-1; i++) { for(j=0; j<=n-1; j++) { monitor.enqueuedjac[i,j] = jac[i,j]; } } // // Initialize sorted representation // apserv.rvectorgrowto(ref monitor.sortedstp, 1, _params); apserv.ivectorgrowto(ref monitor.sortedidx, 1, _params); monitor.sortedstp[0] = 0.0; monitor.sortedidx[0] = 0; monitor.sortedcnt = 1; } /************************************************************************* This subroutine starts line search for a scalar function - convenience wrapper for ....StartLineSearch() with unscaled variables. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void smoothnessmonitorstartlinesearch1u(smoothnessmonitor monitor, double[] s, double[] invs, double[] x, double f0, double[] j0, alglib.xparams _params) { int n = 0; int k = 0; int i = 0; n = monitor.n; k = monitor.k; if( !monitor.checksmoothness ) { return; } alglib.ap.assert(k==1, "SmoothnessMonitorStartLineSearch1: K<>1"); apserv.rvectorsetlengthatleast(ref monitor.xu, n, _params); apserv.rvectorsetlengthatleast(ref monitor.f0, 1, _params); apserv.rmatrixsetlengthatleast(ref monitor.j0, 1, n, _params); monitor.f0[0] = f0; for(i=0; i<=n-1; i++) { monitor.xu[i] = x[i]*invs[i]; monitor.j0[0,i] = j0[i]*s[i]; } smoothnessmonitorstartlinesearch(monitor, monitor.xu, monitor.f0, monitor.j0, _params); } /************************************************************************* This subroutine enqueues one more trial point -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void smoothnessmonitorenqueuepoint(smoothnessmonitor monitor, double[] d, double stp, double[] x, double[] fi, double[,] jac, alglib.xparams _params) { int n = 0; int k = 0; int i = 0; int j = 0; double v = 0; int enqueuedcnt = 0; int sortedcnt = 0; bool hasduplicates = new bool(); int funcidx = 0; int stpidx = 0; double f0 = 0; double f1 = 0; double f2 = 0; double f3 = 0; double f4 = 0; double noise0 = 0; double noise1 = 0; double noise2 = 0; double noise3 = 0; double rating = 0; double lipschitz = 0; n = monitor.n; k = monitor.k; // // Skip if inactive or spoiled by NAN // if( (!monitor.checksmoothness || monitor.linesearchspoiled) || !monitor.linesearchstarted ) { return; } v = stp; for(i=0; i<=n-1; i++) { v = 0.5*v+x[i]; } for(i=0; i<=n-1; i++) { v = 0.5*v+d[i]; } for(i=0; i<=k-1; i++) { v = 0.5*v+fi[i]; } for(i=0; i<=k-1; i++) { for(j=0; j<=n-1; j++) { v = 0.5*v+jac[i,j]; } } if( !math.isfinite(v) ) { monitor.linesearchspoiled = true; return; } // // Enqueue // apserv.inc(ref monitor.enqueuedcnt, _params); enqueuedcnt = monitor.enqueuedcnt; apserv.rvectorgrowto(ref monitor.dcur, n, _params); apserv.rvectorgrowto(ref monitor.enqueuedstp, enqueuedcnt, _params); apserv.rvectorgrowto(ref monitor.enqueuedx, enqueuedcnt*n, _params); apserv.rvectorgrowto(ref monitor.enqueuedfunc, enqueuedcnt*k, _params); apserv.rmatrixgrowrowsto(ref monitor.enqueuedjac, enqueuedcnt*k, n, _params); monitor.enqueuedstp[enqueuedcnt-1] = stp; for(j=0; j<=n-1; j++) { monitor.dcur[j] = d[j]; } for(j=0; j<=n-1; j++) { monitor.enqueuedx[(enqueuedcnt-1)*n+j] = x[j]; } for(i=0; i<=k-1; i++) { monitor.enqueuedfunc[(enqueuedcnt-1)*k+i] = fi[i]; } for(i=0; i<=k-1; i++) { for(j=0; j<=n-1; j++) { monitor.enqueuedjac[(enqueuedcnt-1)*k+i,j] = jac[i,j]; } } // // Update sorted representation: insert to the end, reorder // sortedcnt = monitor.sortedcnt; hasduplicates = false; for(i=0; i<=sortedcnt-1; i++) { hasduplicates = hasduplicates || monitor.sortedstp[i]==stp; } if( !hasduplicates ) { apserv.inc(ref monitor.sortedcnt, _params); sortedcnt = monitor.sortedcnt; apserv.rvectorgrowto(ref monitor.sortedstp, sortedcnt, _params); apserv.ivectorgrowto(ref monitor.sortedidx, sortedcnt, _params); monitor.sortedstp[sortedcnt-1] = stp; monitor.sortedidx[sortedcnt-1] = enqueuedcnt-1; for(i=sortedcnt-2; i>=0; i--) { if( monitor.sortedstp[i]<=monitor.sortedstp[i+1] ) { break; } v = monitor.sortedstp[i]; monitor.sortedstp[i] = monitor.sortedstp[i+1]; monitor.sortedstp[i+1] = v; j = monitor.sortedidx[i]; monitor.sortedidx[i] = monitor.sortedidx[i+1]; monitor.sortedidx[i+1] = j; } } // // Scan sorted representation, check for C0 and C1 continuity // violations. // apserv.rvectorsetlengthatleast(ref monitor.f, sortedcnt, _params); apserv.rvectorsetlengthatleast(ref monitor.g, sortedcnt*n, _params); for(funcidx=0; funcidx<=k-1; funcidx++) { // // Fetch current function and its gradient to the contiguous storage // for(i=0; i<=sortedcnt-1; i++) { monitor.f[i] = monitor.enqueuedfunc[monitor.sortedidx[i]*k+funcidx]; for(j=0; j<=n-1; j++) { monitor.g[i*n+j] = monitor.enqueuedjac[monitor.sortedidx[i]*k+funcidx,j]; } } // // Check C0 continuity. // // The basis approach is that we find appropriate candidate point // (either a local minimum along the line - for target; or an interval // where function sign is changed - for constraints), calculate left // and right estimates of the Lipschitz constant (slopes between points // #0 and #1, #2 and #3), and then calculate slope between points #1 and // #2 and compare it with left/right estimates. // // The actual approach is a bit more complex to account for different // sources of numerical noise and different false positive scenarios. // if( funcidx==0 ) { for(stpidx=0; stpidx<=sortedcnt-4; stpidx++) { f0 = monitor.f[stpidx+0]; f1 = monitor.f[stpidx+1]; f2 = monitor.f[stpidx+2]; f3 = monitor.f[stpidx+3]; noise0 = ognoiselevelf*Math.Max(Math.Abs(f0), 1.0); noise1 = ognoiselevelf*Math.Max(Math.Abs(f1), 1.0); noise2 = ognoiselevelf*Math.Max(Math.Abs(f2), 1.0); noise3 = ognoiselevelf*Math.Max(Math.Abs(f3), 1.0); if( !(f1ogminrating0 && rating>monitor.nonc0currentrating ) { monitor.nonc0currentrating = rating; monitor.rep.nonc0suspected = true; monitor.rep.nonc0test0positive = true; monitor.rep.nonc0lipschitzc = lipschitz; monitor.rep.nonc0fidx = funcidx; } } } // // C1 continuity test #0 // for(stpidx=0; stpidx<=sortedcnt-7; stpidx++) { // // Fetch function values // f2 = monitor.f[stpidx+2]; f3 = monitor.f[stpidx+3]; f4 = monitor.f[stpidx+4]; noise2 = ognoiselevelf*Math.Max(Math.Abs(f2), 1.0); noise3 = ognoiselevelf*Math.Max(Math.Abs(f3), 1.0); // // Decide whether we want to test this interval or not; for target // function we test intervals around minimum, for constraints we // test intervals of sign change. // if( funcidx==0 ) { // // Skip if not minimum // if( !(f30 ) { continue; } } c1continuitytest0(monitor, funcidx, stpidx+0, sortedcnt, _params); c1continuitytest0(monitor, funcidx, stpidx+1, sortedcnt, _params); } // // C1 continuity test #1 // for(stpidx=0; stpidx<=sortedcnt-4; stpidx++) { // // Fetch function values from the interval being tested // f0 = monitor.f[stpidx+0]; f1 = monitor.f[stpidx+1]; f2 = monitor.f[stpidx+2]; f3 = monitor.f[stpidx+3]; noise0 = ognoiselevelf*Math.Max(Math.Abs(f0), 1.0); noise1 = ognoiselevelf*Math.Max(Math.Abs(f1), 1.0); noise2 = ognoiselevelf*Math.Max(Math.Abs(f2), 1.0); noise3 = ognoiselevelf*Math.Max(Math.Abs(f3), 1.0); // // Decide whether we want to test this interval or not; for target // function we test intervals around minimum, for constraints we // test intervals of sign change. // if( funcidx==0 ) { // // Skip if not minimum // if( !(f10 ) { continue; } } c1continuitytest1(monitor, funcidx, stpidx, sortedcnt, _params); } } } /************************************************************************* This subroutine enqueues one more trial point for a task with scalar function with unscaled variables - a convenience wrapper for more general EnqueuePoint() -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void smoothnessmonitorenqueuepoint1u(smoothnessmonitor monitor, double[] s, double[] invs, double[] d, double stp, double[] x, double f0, double[] j0, alglib.xparams _params) { int n = 0; int k = 0; int i = 0; n = monitor.n; k = monitor.k; if( !monitor.checksmoothness ) { return; } alglib.ap.assert(k==1, "SmoothnessMonitorEnqueuePoint1: K<>1"); apserv.rvectorsetlengthatleast(ref monitor.xu, n, _params); apserv.rvectorsetlengthatleast(ref monitor.du, n, _params); apserv.rvectorsetlengthatleast(ref monitor.f0, 1, _params); apserv.rmatrixsetlengthatleast(ref monitor.j0, 1, n, _params); monitor.f0[0] = f0; for(i=0; i<=n-1; i++) { monitor.xu[i] = x[i]*invs[i]; monitor.du[i] = d[i]*invs[i]; monitor.j0[0,i] = j0[i]*s[i]; } smoothnessmonitorenqueuepoint(monitor, monitor.du, stp, monitor.xu, monitor.f0, monitor.j0, _params); } /************************************************************************* This subroutine finalizes line search -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void smoothnessmonitorfinalizelinesearch(smoothnessmonitor monitor, alglib.xparams _params) { // // As for now - nothing to be done. // } /************************************************************************* This subroutine exports report to user-readable representation (all arrays are forced to have exactly same size as needed; unused arrays are set to zero length). -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ public static void smoothnessmonitorexportreport(smoothnessmonitor monitor, optguardapi.optguardreport rep, alglib.xparams _params) { // // Finalize last line search, just to be sure // if( monitor.enqueuedcnt>0 ) { smoothnessmonitorfinalizelinesearch(monitor, _params); } // // Export report // optguardapi.optguardexportreport(monitor.rep, monitor.n, monitor.k, monitor.badgradhasxj, rep, _params); } /************************************************************************* Check numerical gradient at point X0 (unscaled variables!), with optional box constraints [BndL,BndU] (if HasBoxConstraints=True) and with scale vector S[]. Step S[i]*TestStep is performed along I-th variable. NeedFiJ rcomm protocol is used to request derivative information. Box constraints BndL/BndU are expected to be feasible. It is possible to have BndL=BndU. -- ALGLIB -- Copyright 06.12.2018 by Bochkanov Sergey *************************************************************************/ public static bool smoothnessmonitorcheckgradientatx0(smoothnessmonitor monitor, double[] unscaledx0, double[] s, double[] bndl, double[] bndu, bool hasboxconstraints, double teststep, alglib.xparams _params) { bool result = new bool(); int n = 0; int k = 0; int i = 0; int j = 0; int varidx = 0; double v = 0; double vp = 0; double vm = 0; double vc = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( monitor.rstateg0.stage>=0 ) { n = monitor.rstateg0.ia[0]; k = monitor.rstateg0.ia[1]; i = monitor.rstateg0.ia[2]; j = monitor.rstateg0.ia[3]; varidx = monitor.rstateg0.ia[4]; v = monitor.rstateg0.ra[0]; vp = monitor.rstateg0.ra[1]; vm = monitor.rstateg0.ra[2]; vc = monitor.rstateg0.ra[3]; } else { n = 359; k = -58; i = -919; j = -909; varidx = 81; v = 255; vp = 74; vm = -788; vc = 809; } if( monitor.rstateg0.stage==0 ) { goto lbl_0; } if( monitor.rstateg0.stage==1 ) { goto lbl_1; } if( monitor.rstateg0.stage==2 ) { goto lbl_2; } if( monitor.rstateg0.stage==3 ) { goto lbl_3; } // // Routine body // n = monitor.n; k = monitor.k; monitor.needfij = false; // // Quick exit // if( ((n<=0 || k<=0) || !math.isfinite(teststep)) || (double)(teststep)==(double)(0) ) { result = false; return result; } teststep = Math.Abs(teststep); // // Allocate storage // apserv.rvectorsetlengthatleast(ref monitor.x, n, _params); apserv.rvectorsetlengthatleast(ref monitor.fi, k, _params); apserv.rmatrixsetlengthatleast(ref monitor.j, k, n, _params); apserv.rvectorsetlengthatleast(ref monitor.xbase, n, _params); apserv.rvectorsetlengthatleast(ref monitor.fbase, k, _params); apserv.rvectorsetlengthatleast(ref monitor.fm, k, _params); apserv.rvectorsetlengthatleast(ref monitor.fc, k, _params); apserv.rvectorsetlengthatleast(ref monitor.fp, k, _params); apserv.rvectorsetlengthatleast(ref monitor.jm, k, _params); apserv.rvectorsetlengthatleast(ref monitor.jc, k, _params); apserv.rvectorsetlengthatleast(ref monitor.jp, k, _params); apserv.rmatrixsetlengthatleast(ref monitor.jbaseusr, k, n, _params); apserv.rmatrixsetlengthatleast(ref monitor.jbasenum, k, n, _params); apserv.rvectorsetlengthatleast(ref monitor.rep.badgradxbase, n, _params); apserv.rmatrixsetlengthatleast(ref monitor.rep.badgraduser, k, n, _params); apserv.rmatrixsetlengthatleast(ref monitor.rep.badgradnum, k, n, _params); // // Set XBase/Jacobian presence flag // monitor.badgradhasxj = true; // // Determine reference point, compute function vector and user-supplied Jacobian // for(i=0; i<=n-1; i++) { v = unscaledx0[i]; if( (hasboxconstraints && math.isfinite(bndl[i])) && (double)(v)<(double)(bndl[i]) ) { v = bndl[i]; } if( (hasboxconstraints && math.isfinite(bndu[i])) && (double)(v)>(double)(bndu[i]) ) { v = bndu[i]; } monitor.xbase[i] = v; monitor.rep.badgradxbase[i] = v; monitor.x[i] = v; } monitor.needfij = true; monitor.rstateg0.stage = 0; goto lbl_rcomm; lbl_0: monitor.needfij = false; for(i=0; i<=k-1; i++) { monitor.fbase[i] = monitor.fi[i]; for(j=0; j<=n-1; j++) { monitor.jbaseusr[i,j] = monitor.j[i,j]; monitor.rep.badgraduser[i,j] = monitor.j[i,j]; } } // // Check Jacobian column by column // varidx = 0; lbl_4: if( varidx>n-1 ) { goto lbl_6; } // // Determine test location. // v = monitor.xbase[varidx]; vm = v-s[varidx]*teststep; vp = v+s[varidx]*teststep; if( (hasboxconstraints && math.isfinite(bndl[varidx])) && (double)(vm)<(double)(bndl[varidx]) ) { vm = bndl[varidx]; } if( (hasboxconstraints && math.isfinite(bndu[varidx])) && (double)(vp)>(double)(bndu[varidx]) ) { vp = bndu[varidx]; } vc = vm+(vp-vm)/2; // // Quickly skip fixed variables // if( ((double)(vm)==(double)(vp) || (double)(vc)==(double)(vm)) || (double)(vc)==(double)(vp) ) { for(i=0; i<=k-1; i++) { monitor.rep.badgradnum[i,varidx] = 0; } goto lbl_5; } // // Compute F/J at three trial points // for(i=0; i<=n-1; i++) { monitor.x[i] = monitor.xbase[i]; } monitor.x[varidx] = vm; monitor.needfij = true; monitor.rstateg0.stage = 1; goto lbl_rcomm; lbl_1: monitor.needfij = false; for(i=0; i<=k-1; i++) { monitor.fm[i] = monitor.fi[i]; monitor.jm[i] = monitor.j[i,varidx]; } for(i=0; i<=n-1; i++) { monitor.x[i] = monitor.xbase[i]; } monitor.x[varidx] = vc; monitor.needfij = true; monitor.rstateg0.stage = 2; goto lbl_rcomm; lbl_2: monitor.needfij = false; for(i=0; i<=k-1; i++) { monitor.fc[i] = monitor.fi[i]; monitor.jc[i] = monitor.j[i,varidx]; } for(i=0; i<=n-1; i++) { monitor.x[i] = monitor.xbase[i]; } monitor.x[varidx] = vp; monitor.needfij = true; monitor.rstateg0.stage = 3; goto lbl_rcomm; lbl_3: monitor.needfij = false; for(i=0; i<=k-1; i++) { monitor.fp[i] = monitor.fi[i]; monitor.jp[i] = monitor.j[i,varidx]; } // // Check derivative // for(i=0; i<=k-1; i++) { monitor.rep.badgradnum[i,varidx] = (monitor.fp[i]-monitor.fm[i])/(vp-vm); if( !derivativecheck(monitor.fm[i], monitor.jm[i]*s[varidx], monitor.fp[i], monitor.jp[i]*s[varidx], monitor.fc[i], monitor.jc[i]*s[varidx], (vp-vm)/s[varidx], _params) ) { monitor.rep.badgradsuspected = true; monitor.rep.badgradfidx = i; monitor.rep.badgradvidx = varidx; } } lbl_5: varidx = varidx+1; goto lbl_4; lbl_6: result = false; return result; // // Saving state // lbl_rcomm: result = true; monitor.rstateg0.ia[0] = n; monitor.rstateg0.ia[1] = k; monitor.rstateg0.ia[2] = i; monitor.rstateg0.ia[3] = j; monitor.rstateg0.ia[4] = varidx; monitor.rstateg0.ra[0] = v; monitor.rstateg0.ra[1] = vp; monitor.rstateg0.ra[2] = vm; monitor.rstateg0.ra[3] = vc; return result; } /************************************************************************* This function calculates feasibility error (square root of sum of squared errors) for a Kx(NMain+NSlack) system of linear equalities. INPUT PARAMETERS: CE - set of K equality constraints, array[K,NMain+NSlack+1] X - candidate point, array [NMain+NSlack] NMain - number of primary variables NSlack - number of slack variables K - number of constraints Tmp0 - possible preallocated buffer, automatically resized RESULT: Sqrt(SUM(Err^2)) -- ALGLIB -- Copyright 17.09.2015 by Bochkanov Sergey *************************************************************************/ private static double feasibilityerror(double[,] ce, double[] x, int nmain, int nslack, int k, ref double[] tmp0, alglib.xparams _params) { double result = 0; int i = 0; apserv.rvectorsetlengthatleast(ref tmp0, k, _params); for(i=0; i<=k-1; i++) { tmp0[i] = -ce[i,nmain+nslack]; } ablas.rmatrixgemv(k, nmain+nslack, 1.0, ce, 0, 0, 0, x, 0, 1.0, tmp0, 0, _params); result = 0.0; for(i=0; i<=k-1; i++) { result = result+tmp0[i]*tmp0[i]; } result = Math.Sqrt(result); return result; } /************************************************************************* This function calculates feasibility error (square root of sum of squared errors) for a Kx(NMain+NSlack) system of linear equalities and error gradient (with respect to x) INPUT PARAMETERS: CE - set of K equality constraints, array[K,NMain+NSlack+1] X - candidate point, array [NMain+NSlack] NMain - number of primary variables NSlack - number of slack variables K - number of constraints Grad - preallocated array[NMain+NSlack] Tmp0 - possible preallocated buffer, automatically resized RESULT: Err - Sqrt(SUM(Err^2)) Grad - error gradient with respect to X, array[NMain+NSlack] -- ALGLIB -- Copyright 17.09.2015 by Bochkanov Sergey *************************************************************************/ private static void feasibilityerrorgrad(double[,] ce, double[] x, int nmain, int nslack, int k, ref double err, double[] grad, ref double[] tmp0, alglib.xparams _params) { int i = 0; double v = 0; err = 0; alglib.ap.assert(alglib.ap.len(grad)>=nmain+nslack, "FeasibilityErrorGrad: integrity check failed"); apserv.rvectorsetlengthatleast(ref tmp0, k, _params); ablas.rmatrixgemv(k, nmain+nslack, 1.0, ce, 0, 0, 0, x, 0, 0.0, tmp0, 0, _params); err = 0.0; for(i=0; i<=k-1; i++) { v = tmp0[i]-ce[i,nmain+nslack]; tmp0[i] = v; err = err+v*v; } err = Math.Sqrt(err); ablas.rmatrixgemv(nmain+nslack, k, 1.0, ce, 0, 0, 1, tmp0, 0, 0.0, grad, 0, _params); } /************************************************************************* This subroutine checks C0 continuity and returns continuity rating (normalized value, with values above 50-500 being good indication of the discontinuity) and Lipschitz constant. An interval between F1 and F2 is tested for (dis)continuity. Per-point noise estimates are provided. Delta[i] is a step from F[i] to F[i+1]. ApplySpecialCorrection parameter should be set to True if you use this function to estimate continuity of the model around minimum; it adds special correction which helps to detect "max(0,1/x)"-like discontinuities. Without this correction algorithm will still work, but will be a bit less powerful. Do not use this correction for situations when you want to estimate continuity around some non-extremal point - it may result in spurious discontinuities being reported. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ private static void testc0continuity(double f0, double f1, double f2, double f3, double noise0, double noise1, double noise2, double noise3, double delta0, double delta1, double delta2, bool applyspecialcorrection, ref double rating, ref double lipschitz, alglib.xparams _params) { double lipschitz01 = 0; double lipschitz12 = 0; double lipschitz23 = 0; rating = 0; lipschitz = 0; // // Compute Lipschitz constant for the interval [0,1], // add noise correction in order to get increased estimate (makes // comparison below more conservative). // lipschitz01 = (Math.Abs(f1-f0)+(noise0+noise1))/delta0; // // Compute Lipschitz constant for the interval [StpIdx+1,StpIdx+2], // SUBTRACT noise correction in order to get decreased estimate (makes // comparison below more conservative). // lipschitz12 = Math.Max(Math.Abs(f2-f1)-(noise1+noise2), 0.0)/delta1; // // Compute Lipschitz constant for the interval [StpIdx+2,StpIdx+3] // using special algorithm: // a) if F30, "OptGuard: integrity check failed"); rating = lipschitz12/Math.Max(lipschitz01, lipschitz23); lipschitz = lipschitz12; } /************************************************************************* This subroutine checks C1 continuity using test #0 (function values from the line search log are studied, gradient is not used). An interval between F[StpIdx+0] and F[StpIdx+5]is tested for continuity. An normalized error metric (Lipschitz constant growth for the derivative) for the interval in question is calculated. Values above 50 are a good indication of the discontinuity. A six-point algorithm is used for testing, so we expect that Monitor.F and Monitor.Stp have enough points for this test. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ private static void c1continuitytest0(smoothnessmonitor monitor, int funcidx, int stpidx, int sortedcnt, alglib.xparams _params) { double f0 = 0; double f1 = 0; double f2 = 0; double f3 = 0; double f4 = 0; double f5 = 0; double noise0 = 0; double noise1 = 0; double noise2 = 0; double noise3 = 0; double noise4 = 0; double noise5 = 0; double delta0 = 0; double delta1 = 0; double delta2 = 0; double delta3 = 0; double delta4 = 0; double d0 = 0; double d1 = 0; double d2 = 0; double d3 = 0; double newnoise0 = 0; double newnoise1 = 0; double newnoise2 = 0; double newnoise3 = 0; double newdelta0 = 0; double newdelta1 = 0; double newdelta2 = 0; double rating = 0; double lipschitz = 0; double lengthrating = 0; int i = 0; int n = 0; double nrm = 0; n = monitor.n; alglib.ap.assert(stpidx+5(double)(0), "C1ContinuityTest0: integrity check failed"); // // Fetch F, noise, Delta's // f0 = monitor.f[stpidx+0]; f1 = monitor.f[stpidx+1]; f2 = monitor.f[stpidx+2]; f3 = monitor.f[stpidx+3]; f4 = monitor.f[stpidx+4]; f5 = monitor.f[stpidx+5]; noise0 = ognoiselevelf*Math.Max(Math.Abs(f0), 1.0); noise1 = ognoiselevelf*Math.Max(Math.Abs(f1), 1.0); noise2 = ognoiselevelf*Math.Max(Math.Abs(f2), 1.0); noise3 = ognoiselevelf*Math.Max(Math.Abs(f3), 1.0); noise4 = ognoiselevelf*Math.Max(Math.Abs(f4), 1.0); noise5 = ognoiselevelf*Math.Max(Math.Abs(f5), 1.0); delta0 = monitor.sortedstp[stpidx+1]-monitor.sortedstp[stpidx+0]; delta1 = monitor.sortedstp[stpidx+2]-monitor.sortedstp[stpidx+1]; delta2 = monitor.sortedstp[stpidx+3]-monitor.sortedstp[stpidx+2]; delta3 = monitor.sortedstp[stpidx+4]-monitor.sortedstp[stpidx+3]; delta4 = monitor.sortedstp[stpidx+5]-monitor.sortedstp[stpidx+4]; // // Differentiate functions, get derivative values and noise // estimates at points (0+1)/2, (1+2)/2, (3+4)/2, (3+4)/2, // (4+5)/2. Compute new step values NewDelta[i] and new // noise estimates. // d0 = (f1-f0)/delta0; d1 = (f2-f1)/delta1; d2 = (f4-f3)/delta3; d3 = (f5-f4)/delta4; newnoise0 = (noise0+noise1)/delta0; newnoise1 = (noise1+noise2)/delta1; newnoise2 = (noise3+noise4)/delta3; newnoise3 = (noise4+noise5)/delta4; newdelta0 = 0.5*(delta0+delta1); newdelta1 = 0.5*delta1+delta2+0.5*delta3; newdelta2 = 0.5*(delta3+delta4); // // Test with C0 continuity tester. "Special correction" is // turned off for this test. // testc0continuity(d0, d1, d2, d3, newnoise0, newnoise1, newnoise2, newnoise3, newdelta0, newdelta1, newdelta2, false, ref rating, ref lipschitz, _params); // // Store results // if( rating>ogminrating1 ) { // // Store to total report // monitor.rep.nonc1test0positive = true; if( rating>monitor.nonc1currentrating ) { monitor.nonc1currentrating = rating; monitor.rep.nonc1suspected = true; monitor.rep.nonc1lipschitzc = lipschitz; monitor.rep.nonc1fidx = funcidx; } // // Store to "strongest" report // if( rating>monitor.nonc1test0strrating ) { monitor.nonc1test0strrating = rating; monitor.nonc1test0strrep.positive = true; monitor.nonc1test0strrep.fidx = funcidx; monitor.nonc1test0strrep.n = n; monitor.nonc1test0strrep.cnt = sortedcnt; monitor.nonc1test0strrep.stpidxa = stpidx+1; monitor.nonc1test0strrep.stpidxb = stpidx+4; apserv.rvectorsetlengthatleast(ref monitor.nonc1test0strrep.x0, n, _params); apserv.rvectorsetlengthatleast(ref monitor.nonc1test0strrep.d, n, _params); for(i=0; i<=n-1; i++) { monitor.nonc1test0strrep.x0[i] = monitor.enqueuedx[monitor.sortedidx[0]*n+i]; monitor.nonc1test0strrep.d[i] = monitor.dcur[i]; } apserv.rvectorsetlengthatleast(ref monitor.nonc1test0strrep.stp, sortedcnt, _params); apserv.rvectorsetlengthatleast(ref monitor.nonc1test0strrep.f, sortedcnt, _params); for(i=0; i<=sortedcnt-1; i++) { monitor.nonc1test0strrep.stp[i] = monitor.sortedstp[i]; monitor.nonc1test0strrep.f[i] = monitor.f[i]; } } // // Store to "longest" report // nrm = 0; for(i=0; i<=n-1; i++) { nrm = nrm+math.sqr(monitor.enqueuedx[monitor.sortedidx[0]*n+i]-monitor.enqueuedx[monitor.sortedidx[sortedcnt-1]*n+i]); } nrm = Math.Sqrt(nrm); nrm = Math.Min(nrm, 1.0); nrm = apserv.coalesce(nrm, math.machineepsilon, _params); lengthrating = sortedcnt+Math.Log(nrm)/Math.Log(100); if( lengthrating>monitor.nonc1test0lngrating ) { monitor.nonc1test0lngrating = lengthrating; monitor.nonc1test0lngrep.positive = true; monitor.nonc1test0lngrep.fidx = funcidx; monitor.nonc1test0lngrep.n = n; monitor.nonc1test0lngrep.cnt = sortedcnt; monitor.nonc1test0lngrep.stpidxa = stpidx+1; monitor.nonc1test0lngrep.stpidxb = stpidx+4; apserv.rvectorsetlengthatleast(ref monitor.nonc1test0lngrep.x0, n, _params); apserv.rvectorsetlengthatleast(ref monitor.nonc1test0lngrep.d, n, _params); for(i=0; i<=n-1; i++) { monitor.nonc1test0lngrep.x0[i] = monitor.enqueuedx[monitor.sortedidx[0]*n+i]; monitor.nonc1test0lngrep.d[i] = monitor.dcur[i]; } apserv.rvectorsetlengthatleast(ref monitor.nonc1test0lngrep.stp, sortedcnt, _params); apserv.rvectorsetlengthatleast(ref monitor.nonc1test0lngrep.f, sortedcnt, _params); for(i=0; i<=sortedcnt-1; i++) { monitor.nonc1test0lngrep.stp[i] = monitor.sortedstp[i]; monitor.nonc1test0lngrep.f[i] = monitor.f[i]; } } } } /************************************************************************* This subroutine checks C1 continuity using test #1 (individual gradient components from the line search log are studied for continuity). An interval between F[StpIdx+0] and F[StpIdx+3]is tested for continuity. An normalized error metric (Lipschitz constant growth for the derivative) for the interval in question is calculated. Values above 50 are a good indication of the discontinuity. -- ALGLIB -- Copyright 19.11.2018 by Bochkanov Sergey *************************************************************************/ private static void c1continuitytest1(smoothnessmonitor monitor, int funcidx, int stpidx, int sortedcnt, alglib.xparams _params) { int i = 0; int varidx = 0; int n = 0; double f0 = 0; double f1 = 0; double f2 = 0; double f3 = 0; double noise0 = 0; double noise1 = 0; double noise2 = 0; double noise3 = 0; double nrm = 0; double rating = 0; double lengthrating = 0; double lipschitz = 0; n = monitor.n; alglib.ap.assert(stpidx+3(double)(0), "C1ContinuityTest1: integrity check failed"); // // Study each component of the gradient in the interval in question // for(varidx=0; varidx<=n-1; varidx++) { f0 = monitor.g[(stpidx+0)*n+varidx]; f1 = monitor.g[(stpidx+1)*n+varidx]; f2 = monitor.g[(stpidx+2)*n+varidx]; f3 = monitor.g[(stpidx+3)*n+varidx]; noise0 = ognoiselevelg*Math.Max(Math.Abs(f0), 1.0); noise1 = ognoiselevelg*Math.Max(Math.Abs(f1), 1.0); noise2 = ognoiselevelg*Math.Max(Math.Abs(f2), 1.0); noise3 = ognoiselevelg*Math.Max(Math.Abs(f3), 1.0); testc0continuity(f0, f1, f2, f3, noise0, noise1, noise2, noise3, monitor.sortedstp[stpidx+1]-monitor.sortedstp[stpidx+0], monitor.sortedstp[stpidx+2]-monitor.sortedstp[stpidx+1], monitor.sortedstp[stpidx+3]-monitor.sortedstp[stpidx+2], false, ref rating, ref lipschitz, _params); // // Store results // if( rating>ogminrating1 ) { // // Store to total report // monitor.rep.nonc1test1positive = true; if( rating>monitor.nonc1currentrating ) { monitor.nonc1currentrating = rating; monitor.rep.nonc1suspected = true; monitor.rep.nonc1lipschitzc = lipschitz; monitor.rep.nonc1fidx = funcidx; } // // Store to "strongest" report // if( rating>monitor.nonc1test1strrating ) { monitor.nonc1test1strrating = rating; monitor.nonc1test1strrep.positive = true; monitor.nonc1test1strrep.fidx = funcidx; monitor.nonc1test1strrep.vidx = varidx; monitor.nonc1test1strrep.n = n; monitor.nonc1test1strrep.cnt = sortedcnt; monitor.nonc1test1strrep.stpidxa = stpidx+0; monitor.nonc1test1strrep.stpidxb = stpidx+3; apserv.rvectorsetlengthatleast(ref monitor.nonc1test1strrep.x0, n, _params); apserv.rvectorsetlengthatleast(ref monitor.nonc1test1strrep.d, n, _params); for(i=0; i<=n-1; i++) { monitor.nonc1test1strrep.x0[i] = monitor.enqueuedx[monitor.sortedidx[0]*n+i]; monitor.nonc1test1strrep.d[i] = monitor.dcur[i]; } apserv.rvectorsetlengthatleast(ref monitor.nonc1test1strrep.stp, sortedcnt, _params); apserv.rvectorsetlengthatleast(ref monitor.nonc1test1strrep.g, sortedcnt, _params); for(i=0; i<=sortedcnt-1; i++) { monitor.nonc1test1strrep.stp[i] = monitor.sortedstp[i]; monitor.nonc1test1strrep.g[i] = monitor.g[i*n+varidx]; } } // // Store to "longest" report // nrm = 0; for(i=0; i<=n-1; i++) { nrm = nrm+math.sqr(monitor.enqueuedx[monitor.sortedidx[0]*n+i]-monitor.enqueuedx[monitor.sortedidx[sortedcnt-1]*n+i]); } nrm = Math.Sqrt(nrm); nrm = Math.Min(nrm, 1.0); nrm = apserv.coalesce(nrm, math.machineepsilon, _params); lengthrating = sortedcnt+Math.Log(nrm)/Math.Log(100); if( lengthrating>monitor.nonc1test1lngrating ) { monitor.nonc1test1lngrating = lengthrating; monitor.nonc1test1lngrep.positive = true; monitor.nonc1test1lngrep.fidx = funcidx; monitor.nonc1test1lngrep.vidx = varidx; monitor.nonc1test1lngrep.n = n; monitor.nonc1test1lngrep.cnt = sortedcnt; monitor.nonc1test1lngrep.stpidxa = stpidx+0; monitor.nonc1test1lngrep.stpidxb = stpidx+3; apserv.rvectorsetlengthatleast(ref monitor.nonc1test1lngrep.x0, n, _params); apserv.rvectorsetlengthatleast(ref monitor.nonc1test1lngrep.d, n, _params); for(i=0; i<=n-1; i++) { monitor.nonc1test1lngrep.x0[i] = monitor.enqueuedx[monitor.sortedidx[0]*n+i]; monitor.nonc1test1lngrep.d[i] = monitor.dcur[i]; } apserv.rvectorsetlengthatleast(ref monitor.nonc1test1lngrep.stp, sortedcnt, _params); apserv.rvectorsetlengthatleast(ref monitor.nonc1test1lngrep.g, sortedcnt, _params); for(i=0; i<=sortedcnt-1; i++) { monitor.nonc1test1lngrep.stp[i] = monitor.sortedstp[i]; monitor.nonc1test1lngrep.g[i] = monitor.g[i*n+varidx]; } } } } } } public class snnls { /************************************************************************* This structure is a SNNLS (Specialized Non-Negative Least Squares) solver. It solves problems of the form |A*x-b|^2 => min subject to non-negativity constraints on SOME components of x, with structured A (first NS columns are just unit matrix, next ND columns store dense part). This solver is suited for solution of many sequential NNLS subproblems - it keeps track of previously allocated memory and reuses it as much as possible. *************************************************************************/ public class snnlssolver : apobject { public int ns; public int nd; public int nr; public double[,] densea; public double[] b; public bool[] nnc; public double debugflops; public int debugmaxinnerits; public double[] xn; public double[] xp; public double[,] tmpca; public double[,] tmplq; public double[,] trda; public double[] trdd; public double[] crb; public double[] g; public double[] d; public double[] dx; public double[] diagaa; public double[] cb; public double[] cx; public double[] cborg; public double[] tmpcholesky; public double[] r; public double[] regdiag; public double[] tmp0; public double[] tmp1; public double[] tmp2; public int[] rdtmprowmap; public snnlssolver() { init(); } public override void init() { densea = new double[0,0]; b = new double[0]; nnc = new bool[0]; xn = new double[0]; xp = new double[0]; tmpca = new double[0,0]; tmplq = new double[0,0]; trda = new double[0,0]; trdd = new double[0]; crb = new double[0]; g = new double[0]; d = new double[0]; dx = new double[0]; diagaa = new double[0]; cb = new double[0]; cx = new double[0]; cborg = new double[0]; tmpcholesky = new double[0]; r = new double[0]; regdiag = new double[0]; tmp0 = new double[0]; tmp1 = new double[0]; tmp2 = new double[0]; rdtmprowmap = new int[0]; } public override alglib.apobject make_copy() { snnlssolver _result = new snnlssolver(); _result.ns = ns; _result.nd = nd; _result.nr = nr; _result.densea = (double[,])densea.Clone(); _result.b = (double[])b.Clone(); _result.nnc = (bool[])nnc.Clone(); _result.debugflops = debugflops; _result.debugmaxinnerits = debugmaxinnerits; _result.xn = (double[])xn.Clone(); _result.xp = (double[])xp.Clone(); _result.tmpca = (double[,])tmpca.Clone(); _result.tmplq = (double[,])tmplq.Clone(); _result.trda = (double[,])trda.Clone(); _result.trdd = (double[])trdd.Clone(); _result.crb = (double[])crb.Clone(); _result.g = (double[])g.Clone(); _result.d = (double[])d.Clone(); _result.dx = (double[])dx.Clone(); _result.diagaa = (double[])diagaa.Clone(); _result.cb = (double[])cb.Clone(); _result.cx = (double[])cx.Clone(); _result.cborg = (double[])cborg.Clone(); _result.tmpcholesky = (double[])tmpcholesky.Clone(); _result.r = (double[])r.Clone(); _result.regdiag = (double[])regdiag.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmp1 = (double[])tmp1.Clone(); _result.tmp2 = (double[])tmp2.Clone(); _result.rdtmprowmap = (int[])rdtmprowmap.Clone(); return _result; } }; /************************************************************************* This subroutine is used to initialize SNNLS solver. By default, empty NNLS problem is produced, but we allocated enough space to store problems with NSMax+NDMax columns and NRMax rows. It is good place to provide algorithm with initial estimate of the space requirements, although you may underestimate problem size or even pass zero estimates - in this case buffer variables will be resized automatically when you set NNLS problem. Previously allocated buffer variables are reused as much as possible. This function does not clear structure completely, it tries to preserve as much dynamically allocated memory as possible. -- ALGLIB -- Copyright 10.10.2012 by Bochkanov Sergey *************************************************************************/ public static void snnlsinit(int nsmax, int ndmax, int nrmax, snnlssolver s, alglib.xparams _params) { s.ns = 0; s.nd = 0; s.nr = 0; apserv.rmatrixsetlengthatleast(ref s.densea, nrmax, ndmax, _params); apserv.rmatrixsetlengthatleast(ref s.tmpca, nrmax, ndmax, _params); apserv.rvectorsetlengthatleast(ref s.b, nrmax, _params); apserv.bvectorsetlengthatleast(ref s.nnc, nsmax+ndmax, _params); s.debugflops = 0.0; s.debugmaxinnerits = 0; } /************************************************************************* This subroutine is used to set NNLS problem: ( [ 1 | ] [ ] [ ] )^2 ( [ 1 | ] [ ] [ ] ) min ( [ 1 | Ad ] * [ x ] - [ b ] ) s.t. x>=0 ( [ | ] [ ] [ ] ) ( [ | ] [ ] [ ] ) where: * identity matrix has NS*NS size (NS<=NR, NS can be zero) * dense matrix Ad has NR*ND size * b is NR*1 vector * x is (NS+ND)*1 vector * all elements of x are non-negative (this constraint can be removed later by calling SNNLSDropNNC() function) Previously allocated buffer variables are reused as much as possible. After you set problem, you can solve it with SNNLSSolve(). INPUT PARAMETERS: S - SNNLS solver, must be initialized with SNNLSInit() call A - array[NR,ND], dense part of the system B - array[NR], right part NS - size of the sparse part of the system, 0<=NS<=NR ND - size of the dense part of the system, ND>=0 NR - rows count, NR>0 NOTE: 1. You can have NS+ND=0, solver will correctly accept such combination and return empty array as problem solution. -- ALGLIB -- Copyright 10.10.2012 by Bochkanov Sergey *************************************************************************/ public static void snnlssetproblem(snnlssolver s, double[,] a, double[] b, int ns, int nd, int nr, alglib.xparams _params) { int i = 0; int i_ = 0; alglib.ap.assert(nd>=0, "SNNLSSetProblem: ND<0"); alglib.ap.assert(ns>=0, "SNNLSSetProblem: NS<0"); alglib.ap.assert(nr>0, "SNNLSSetProblem: NR<=0"); alglib.ap.assert(ns<=nr, "SNNLSSetProblem: NS>NR"); alglib.ap.assert(alglib.ap.rows(a)>=nr || nd==0, "SNNLSSetProblem: rows(A)=nd, "SNNLSSetProblem: cols(A)=nr, "SNNLSSetProblem: length(B)0 ) { apserv.rmatrixsetlengthatleast(ref s.densea, nr, nd, _params); for(i=0; i<=nr-1; i++) { for(i_=0; i_<=nd-1;i_++) { s.densea[i,i_] = a[i,i_]; } } } apserv.rvectorsetlengthatleast(ref s.b, nr, _params); for(i_=0; i_<=nr-1;i_++) { s.b[i_] = b[i_]; } apserv.bvectorsetlengthatleast(ref s.nnc, ns+nd, _params); for(i=0; i<=ns+nd-1; i++) { s.nnc[i] = true; } } /************************************************************************* This subroutine drops non-negativity constraint from the problem set by SNNLSSetProblem() call. This function must be called AFTER problem is set, because each SetProblem() call resets constraints to their default state (all constraints are present). INPUT PARAMETERS: S - SNNLS solver, must be initialized with SNNLSInit() call, problem must be set with SNNLSSetProblem() call. Idx - constraint index, 0<=IDX=0, "SNNLSDropNNC: Idx<0"); alglib.ap.assert(idx=NS+ND"); s.nnc[idx] = false; } /************************************************************************* This subroutine is used to solve NNLS problem. INPUT PARAMETERS: S - SNNLS solver, must be initialized with SNNLSInit() call and problem must be set up with SNNLSSetProblem() call. X - possibly preallocated buffer, automatically resized if needed OUTPUT PARAMETERS: X - array[NS+ND], solution NOTE: 1. You can have NS+ND=0, solver will correctly accept such combination and return empty array as problem solution. 2. Internal field S.DebugFLOPS contains rough estimate of FLOPs used to solve problem. It can be used for debugging purposes. This field is real-valued. -- ALGLIB -- Copyright 10.10.2012 by Bochkanov Sergey *************************************************************************/ public static void snnlssolve(snnlssolver s, ref double[] x, alglib.xparams _params) { int i = 0; int ns = 0; int nd = 0; int nr = 0; bool wasactivation = new bool(); double lambdav = 0; double v0 = 0; double v1 = 0; double v = 0; int outerits = 0; int innerits = 0; int maxouterits = 0; double xtol = 0; double kicklength = 0; bool kickneeded = new bool(); double f0 = 0; double f1 = 0; double dnrm = 0; int actidx = 0; double stp = 0; double stpmax = 0; // // Prepare // ns = s.ns; nd = s.nd; nr = s.nr; s.debugflops = 0.0; // // Handle special cases: // * NS+ND=0 // * ND=0 // if( ns+nd==0 ) { return; } if( nd==0 ) { apserv.rvectorsetlengthatleast(ref x, ns, _params); for(i=0; i<=ns-1; i++) { x[i] = s.b[i]; if( s.nnc[i] ) { x[i] = Math.Max(x[i], 0.0); } } return; } // // Main cycle of BLEIC-SNNLS algorithm. // Below we assume that ND>0. // apserv.rvectorsetlengthatleast(ref x, ns+nd, _params); apserv.rvectorsetlengthatleast(ref s.xn, ns+nd, _params); apserv.rvectorsetlengthatleast(ref s.xp, ns+nd, _params); apserv.rvectorsetlengthatleast(ref s.g, ns+nd, _params); apserv.rvectorsetlengthatleast(ref s.d, ns+nd, _params); apserv.rvectorsetlengthatleast(ref s.r, nr, _params); apserv.rvectorsetlengthatleast(ref s.diagaa, nd, _params); apserv.rvectorsetlengthatleast(ref s.regdiag, ns+nd, _params); apserv.rvectorsetlengthatleast(ref s.dx, ns+nd, _params); for(i=0; i<=ns+nd-1; i++) { x[i] = 0.0; s.regdiag[i] = 1.0; } lambdav = 1.0E6*math.machineepsilon; maxouterits = 10; outerits = 0; innerits = 0; xtol = 1.0E3*math.machineepsilon; kicklength = Math.Sqrt(math.minrealnumber); while( true ) { // // Initial check for correctness of X // for(i=0; i<=ns+nd-1; i++) { alglib.ap.assert(!s.nnc[i] || (double)(x[i])>=(double)(0), "SNNLS: integrity check failed"); } // // Calculate gradient G and constrained descent direction D // funcgradu(s, x, s.r, s.g, ref f0, _params); for(i=0; i<=ns+nd-1; i++) { if( (s.nnc[i] && (double)(x[i])==(double)(0)) && (double)(s.g[i])>(double)(0) ) { s.d[i] = 0.0; } else { s.d[i] = -s.g[i]; } } // // Decide whether we need "kick" stage: special stage // that moves us away from boundary constraints which are // not strictly active (i.e. such constraints that x[i]=0.0 and d[i]>0). // // If we need kick stage, we make a kick - and restart iteration. // If not, after this block we can rely on the fact that // for all x[i]=0.0 we have d[i]=0.0 // // NOTE: we do not increase outer iterations counter here // kickneeded = false; for(i=0; i<=ns+nd-1; i++) { if( (s.nnc[i] && (double)(x[i])==(double)(0.0)) && (double)(s.d[i])>(double)(0.0) ) { kickneeded = true; } } if( kickneeded ) { // // Perform kick. // Restart. // Do not increase iterations counter. // for(i=0; i<=ns+nd-1; i++) { if( (double)(x[i])==(double)(0.0) && (double)(s.d[i])>(double)(0.0) ) { x[i] = x[i]+kicklength; } } continue; } // // Newton phase // Reduce problem to constrained triangular form and perform Newton // steps with quick activation of constrants (triangular form is // updated in order to handle changed constraints). // for(i=0; i<=ns+nd-1; i++) { s.xp[i] = x[i]; } trdprepare(s, x, s.regdiag, lambdav, ref s.trdd, ref s.trda, ref s.tmp0, ref s.tmp1, ref s.tmp2, ref s.tmplq, _params); while( true ) { // // Skip if debug limit on inner iterations count is turned on. // if( s.debugmaxinnerits>0 && innerits>=s.debugmaxinnerits ) { break; } // // Prepare step vector. // funcgradu(s, x, s.r, s.g, ref f0, _params); for(i=0; i<=ns+nd-1; i++) { s.d[i] = -s.g[i]; if( s.nnc[i] && (double)(x[i])==(double)(0.0) ) { s.d[i] = 0.0; } } trdsolve(s.trdd, s.trda, ns, nd, s.d, _params); // // Perform unconstrained trial step and compare function values. // for(i=0; i<=ns+nd-1; i++) { s.xn[i] = x[i]+s.d[i]; } func(s, s.xn, ref f1, _params); if( (double)(f1)>=(double)(f0) ) { break; } // // Calculate length of D, maximum step and component which is // activated by this step. Break if D is exactly zero. // dnrm = 0.0; for(i=0; i<=ns+nd-1; i++) { dnrm = dnrm+math.sqr(s.d[i]); } dnrm = Math.Sqrt(dnrm); actidx = -1; stpmax = 1.0E50; for(i=0; i<=ns+nd-1; i++) { if( s.nnc[i] && (double)(s.d[i])<(double)(0.0) ) { v = stpmax; stpmax = apserv.safeminposrv(x[i], -s.d[i], stpmax, _params); if( (double)(stpmax)<(double)(v) ) { actidx = i; } } } if( (double)(dnrm)==(double)(0.0) ) { break; } // // Perform constrained step and update X // and triangular model. // stp = Math.Min(1.0, stpmax); for(i=0; i<=ns+nd-1; i++) { v = x[i]+stp*s.d[i]; if( s.nnc[i] ) { v = Math.Max(v, 0.0); } s.xn[i] = v; } if( (double)(stp)==(double)(stpmax) && actidx>=0 ) { s.xn[actidx] = 0.0; } wasactivation = false; for(i=0; i<=ns+nd-1; i++) { if( (double)(s.xn[i])==(double)(0.0) && (double)(x[i])!=(double)(0.0) ) { wasactivation = true; trdfixvariable(s.trdd, s.trda, ns, nd, i, ref s.tmpcholesky, _params); } } for(i=0; i<=ns+nd-1; i++) { x[i] = s.xn[i]; } // // Increment iterations counter. // Terminate if no constraint was activated. // apserv.inc(ref innerits, _params); if( !wasactivation ) { break; } } // // Update outer iterations counter. // // Break if necessary: // * maximum number of outer iterations performed // * relative change in X is small enough // apserv.inc(ref outerits, _params); if( outerits>=maxouterits ) { break; } v = 0; for(i=0; i<=ns+nd-1; i++) { v0 = Math.Abs(s.xp[i]); v1 = Math.Abs(x[i]); if( (double)(v0)!=(double)(0) || (double)(v1)!=(double)(0) ) { v = Math.Max(v, Math.Abs(x[i]-s.xp[i])/Math.Max(v0, v1)); } } if( (double)(v)<=(double)(xtol) ) { break; } } } /************************************************************************* This function calculates: * residual vector R = A*x-b * unconstrained gradient vector G * function value F = 0.5*|R|^2 R and G must have at least N elements. -- ALGLIB -- Copyright 15.07.2015 by Bochkanov Sergey *************************************************************************/ private static void funcgradu(snnlssolver s, double[] x, double[] r, double[] g, ref double f, alglib.xparams _params) { int i = 0; int nr = 0; int nd = 0; int ns = 0; double v = 0; int i_ = 0; int i1_ = 0; f = 0; nr = s.nr; nd = s.nd; ns = s.ns; f = 0.0; for(i=0; i<=nr-1; i++) { i1_ = (ns)-(0); v = 0.0; for(i_=0; i_<=nd-1;i_++) { v += s.densea[i,i_]*x[i_+i1_]; } if( i=0; i--) { v = 0.0; for(j=i+1; j<=nd-1; j++) { v = v+trda[ns+i,j]*d[ns+j]; } d[ns+i] = (d[ns+i]-v)/trda[ns+i,i]; } for(i=ns-1; i>=0; i--) { v = 0.0; for(j=0; j<=nd-1; j++) { v = v+trda[i,j]*d[ns+j]; } d[i] = (d[i]-v)/trdd[i]; } } private static void trdfixvariable(double[] trdd, double[,] trda, int ns, int nd, int idx, ref double[] tmp, alglib.xparams _params) { int i = 0; int j = 0; int k = 0; double cs = 0; double sn = 0; double r = 0; double v = 0; double vv = 0; alglib.ap.assert(ns>=0, "TRDFixVariable: integrity error"); alglib.ap.assert(nd>=0, "TRDFixVariable: integrity error"); alglib.ap.assert(ns+nd>0, "TRDFixVariable: integrity error"); alglib.ap.assert(idx>=0, "TRDFixVariable: integrity error"); alglib.ap.assert(idx0 I-th constraint is in the active set * CStatus[I]=0 I-th constraint is at the boundary, but inactive * CStatus[I]<0 I-th constraint is far from the boundary (and inactive) * elements from 0 to N-1 correspond to boundary constraints * elements from N to N+NEC+NIC-1 correspond to linear constraints * elements from N to N+NEC-1 are always +1 FeasInitPt - SASStartOptimization() sets this flag to True if, after enforcement of box constraints, initial point was feasible w.r.t. general linear constraints. This field can be used by unit test to validate some details of initial point calculation algorithm. SparseBatch - indices of box constraints which are NOT included into dense batch (were activated at the beginning of the orthogonalization); SparseBatchSize elements are stored. SparseBatchSize-size of sparse batcj PDenseBatch, IDenseBatch, SDenseBatch - after call to SASRebuildBasis() these matrices store active constraints (except for ones included into sparse batch), reorthogonalized with respect to some inner product: a) for "P" batch - one given by preconditioner matrix (inverse Hessian) b) for "S" one - one given by square of the scale matrix c) for "I" one - traditional dot product array[DenseBatchSize,N+1] All three matrices are linearly equivalent to each other. IMPORTANT: you have to call SASRebuildBasis() before accessing these arrays in order to make sure that they are up to date. DenseBatchSize - dense batch size (PBasis/SBasis/IBasis) *************************************************************************/ public class sactiveset : apobject { public int n; public int algostate; public double[] xc; public bool hasxc; public double[] s; public double[] h; public int[] cstatus; public bool basisisready; public double[,] sdensebatch; public double[,] pdensebatch; public double[,] idensebatch; public int densebatchsize; public int[] sparsebatch; public int sparsebatchsize; public int basisage; public bool feasinitpt; public bool constraintschanged; public bool[] hasbndl; public bool[] hasbndu; public double[] bndl; public double[] bndu; public double[,] cleic; public int nec; public int nic; public bool[] mtnew; public double[] mtx; public int[] mtas; public double[] cdtmp; public double[] corrtmp; public double[] unitdiagonal; public snnls.snnlssolver solver; public double[] scntmp; public double[] tmp0; public double[] tmpfeas; public double[,] tmpm0; public double[] rctmps; public double[] rctmpg; public double[] rctmprightpart; public double[,] rctmpdense0; public double[,] rctmpdense1; public bool[] rctmpisequality; public int[] rctmpconstraintidx; public double[] rctmplambdas; public double[,] tmpbasis; public double[] tmpnormestimates; public double[] tmpreciph; public double[] tmpprodp; public double[] tmpprods; public double[] tmpcp; public double[] tmpcs; public double[] tmpci; public sactiveset() { init(); } public override void init() { xc = new double[0]; s = new double[0]; h = new double[0]; cstatus = new int[0]; sdensebatch = new double[0,0]; pdensebatch = new double[0,0]; idensebatch = new double[0,0]; sparsebatch = new int[0]; hasbndl = new bool[0]; hasbndu = new bool[0]; bndl = new double[0]; bndu = new double[0]; cleic = new double[0,0]; mtnew = new bool[0]; mtx = new double[0]; mtas = new int[0]; cdtmp = new double[0]; corrtmp = new double[0]; unitdiagonal = new double[0]; solver = new snnls.snnlssolver(); scntmp = new double[0]; tmp0 = new double[0]; tmpfeas = new double[0]; tmpm0 = new double[0,0]; rctmps = new double[0]; rctmpg = new double[0]; rctmprightpart = new double[0]; rctmpdense0 = new double[0,0]; rctmpdense1 = new double[0,0]; rctmpisequality = new bool[0]; rctmpconstraintidx = new int[0]; rctmplambdas = new double[0]; tmpbasis = new double[0,0]; tmpnormestimates = new double[0]; tmpreciph = new double[0]; tmpprodp = new double[0]; tmpprods = new double[0]; tmpcp = new double[0]; tmpcs = new double[0]; tmpci = new double[0]; } public override alglib.apobject make_copy() { sactiveset _result = new sactiveset(); _result.n = n; _result.algostate = algostate; _result.xc = (double[])xc.Clone(); _result.hasxc = hasxc; _result.s = (double[])s.Clone(); _result.h = (double[])h.Clone(); _result.cstatus = (int[])cstatus.Clone(); _result.basisisready = basisisready; _result.sdensebatch = (double[,])sdensebatch.Clone(); _result.pdensebatch = (double[,])pdensebatch.Clone(); _result.idensebatch = (double[,])idensebatch.Clone(); _result.densebatchsize = densebatchsize; _result.sparsebatch = (int[])sparsebatch.Clone(); _result.sparsebatchsize = sparsebatchsize; _result.basisage = basisage; _result.feasinitpt = feasinitpt; _result.constraintschanged = constraintschanged; _result.hasbndl = (bool[])hasbndl.Clone(); _result.hasbndu = (bool[])hasbndu.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.cleic = (double[,])cleic.Clone(); _result.nec = nec; _result.nic = nic; _result.mtnew = (bool[])mtnew.Clone(); _result.mtx = (double[])mtx.Clone(); _result.mtas = (int[])mtas.Clone(); _result.cdtmp = (double[])cdtmp.Clone(); _result.corrtmp = (double[])corrtmp.Clone(); _result.unitdiagonal = (double[])unitdiagonal.Clone(); _result.solver = (snnls.snnlssolver)solver.make_copy(); _result.scntmp = (double[])scntmp.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmpfeas = (double[])tmpfeas.Clone(); _result.tmpm0 = (double[,])tmpm0.Clone(); _result.rctmps = (double[])rctmps.Clone(); _result.rctmpg = (double[])rctmpg.Clone(); _result.rctmprightpart = (double[])rctmprightpart.Clone(); _result.rctmpdense0 = (double[,])rctmpdense0.Clone(); _result.rctmpdense1 = (double[,])rctmpdense1.Clone(); _result.rctmpisequality = (bool[])rctmpisequality.Clone(); _result.rctmpconstraintidx = (int[])rctmpconstraintidx.Clone(); _result.rctmplambdas = (double[])rctmplambdas.Clone(); _result.tmpbasis = (double[,])tmpbasis.Clone(); _result.tmpnormestimates = (double[])tmpnormestimates.Clone(); _result.tmpreciph = (double[])tmpreciph.Clone(); _result.tmpprodp = (double[])tmpprodp.Clone(); _result.tmpprods = (double[])tmpprods.Clone(); _result.tmpcp = (double[])tmpcp.Clone(); _result.tmpcs = (double[])tmpcs.Clone(); _result.tmpci = (double[])tmpci.Clone(); return _result; } }; public const int maxbasisage = 5; public const double maxbasisdecay = 0.01; public const double minnormseparation = 0.25; /************************************************************************* This subroutine is used to initialize active set. By default, empty N-variable model with no constraints is generated. Previously allocated buffer variables are reused as much as possible. Two use cases for this object are described below. CASE 1 - STEEPEST DESCENT: SASInit() repeat: SASReactivateConstraints() SASDescentDirection() SASExploreDirection() SASMoveTo() until convergence CASE 1 - PRECONDITIONED STEEPEST DESCENT: SASInit() repeat: SASReactivateConstraintsPrec() SASDescentDirectionPrec() SASExploreDirection() SASMoveTo() until convergence -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ public static void sasinit(int n, sactiveset s, alglib.xparams _params) { int i = 0; s.n = n; s.algostate = 0; // // Constraints // s.constraintschanged = true; s.nec = 0; s.nic = 0; apserv.rvectorsetlengthatleast(ref s.bndl, n, _params); apserv.bvectorsetlengthatleast(ref s.hasbndl, n, _params); apserv.rvectorsetlengthatleast(ref s.bndu, n, _params); apserv.bvectorsetlengthatleast(ref s.hasbndu, n, _params); for(i=0; i<=n-1; i++) { s.bndl[i] = Double.NegativeInfinity; s.bndu[i] = Double.PositiveInfinity; s.hasbndl[i] = false; s.hasbndu[i] = false; } // // current point, scale // s.hasxc = false; apserv.rvectorsetlengthatleast(ref s.xc, n, _params); apserv.rvectorsetlengthatleast(ref s.s, n, _params); apserv.rvectorsetlengthatleast(ref s.h, n, _params); for(i=0; i<=n-1; i++) { s.xc[i] = 0.0; s.s[i] = 1.0; s.h[i] = 1.0; } // // Other // apserv.rvectorsetlengthatleast(ref s.unitdiagonal, n, _params); for(i=0; i<=n-1; i++) { s.unitdiagonal[i] = 1.0; } } /************************************************************************* This function sets scaling coefficients for SAS object. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function During orthogonalization phase, scale is used to calculate drop tolerances (whether vector is significantly non-zero or not). INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ public static void sassetscale(sactiveset state, double[] s, alglib.xparams _params) { int i = 0; alglib.ap.assert(state.algostate==0, "SASSetScale: you may change scale only in modification mode"); alglib.ap.assert(alglib.ap.len(s)>=state.n, "SASSetScale: Length(S)=state.n, "SASSetPrecDiag: D is too short"); for(i=0; i<=state.n-1; i++) { alglib.ap.assert(math.isfinite(d[i]), "SASSetPrecDiag: D contains infinite or NAN elements"); alglib.ap.assert((double)(d[i])>(double)(0), "SASSetPrecDiag: D contains non-positive elements"); } for(i=0; i<=state.n-1; i++) { state.h[i] = d[i]; } } /************************************************************************* This function sets/changes boundary constraints. INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ public static void sassetbc(sactiveset state, double[] bndl, double[] bndu, alglib.xparams _params) { int i = 0; int n = 0; alglib.ap.assert(state.algostate==0, "SASSetBC: you may change constraints only in modification mode"); n = state.n; alglib.ap.assert(alglib.ap.len(bndl)>=n, "SASSetBC: Length(BndL)=n, "SASSetBC: Length(BndU)0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0 NOTE 1: linear (non-bound) constraints are satisfied only approximately: * there always exists some minor violation (about Epsilon in magnitude) due to rounding errors * numerical differentiation, if used, may lead to function evaluations outside of the feasible area, because algorithm does NOT change numerical differentiation formula according to linear constraints. If you want constraints to be satisfied exactly, try to reformulate your problem in such manner that all constraints will become boundary ones (this kind of constraints is always satisfied exactly, both in the final solution and in all intermediate points). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void sassetlc(sactiveset state, double[,] c, int[] ct, int k, alglib.xparams _params) { int n = 0; int i = 0; int i_ = 0; alglib.ap.assert(state.algostate==0, "SASSetLC: you may change constraints only in modification mode"); n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(k>=0, "SASSetLC: K<0"); alglib.ap.assert(alglib.ap.cols(c)>=n+1 || k==0, "SASSetLC: Cols(C)=k, "SASSetLC: Rows(C)=k, "SASSetLC: Length(CT)0 ) { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = -c[i,i_]; } } else { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = c[i,i_]; } } state.nic = state.nic+1; } } // // Mark state as changed // state.constraintschanged = true; } /************************************************************************* Another variation of SASSetLC(), which accepts linear constraints using another representation. Linear constraints are inactive by default (after initial creation). INPUT PARAMETERS: State - SAS structure CLEIC - linear constraints, array[NEC+NIC,N+1]. Each row of C represents one constraint: * first N elements correspond to coefficients, * last element corresponds to the right part. First NEC rows store equality constraints, next NIC - are inequality ones. All elements of C (including right part) must be finite. NEC - number of equality constraints, NEC>=0 NIC - number of inequality constraints, NIC>=0 NOTE 1: linear (non-bound) constraints are satisfied only approximately: * there always exists some minor violation (about Epsilon in magnitude) due to rounding errors * numerical differentiation, if used, may lead to function evaluations outside of the feasible area, because algorithm does NOT change numerical differentiation formula according to linear constraints. If you want constraints to be satisfied exactly, try to reformulate your problem in such manner that all constraints will become boundary ones (this kind of constraints is always satisfied exactly, both in the final solution and in all intermediate points). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void sassetlcx(sactiveset state, double[,] cleic, int nec, int nic, alglib.xparams _params) { int n = 0; int i = 0; int j = 0; alglib.ap.assert(state.algostate==0, "SASSetLCX: you may change constraints only in modification mode"); n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(nec>=0, "SASSetLCX: NEC<0"); alglib.ap.assert(nic>=0, "SASSetLCX: NIC<0"); alglib.ap.assert(alglib.ap.cols(cleic)>=n+1 || nec+nic==0, "SASSetLCX: Cols(CLEIC)=nec+nic, "SASSetLCX: Rows(CLEIC)(double)(state.bndu[i]) ) { return result; } } } for(i_=0; i_<=n-1;i_++) { state.xc[i_] = x[i_]; } if( state.nec+state.nic>0 ) { // // General linear constraints are present. // Try to use fast code for feasible initial point with modest // memory requirements. // apserv.rvectorsetlengthatleast(ref state.tmp0, n, _params); state.feasinitpt = true; for(i=0; i<=n-1; i++) { state.tmp0[i] = x[i]; state.cstatus[i] = -1; if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { state.tmp0[i] = state.bndl[i]; state.cstatus[i] = 1; continue; } if( state.hasbndl[i] && (double)(state.tmp0[i])<=(double)(state.bndl[i]) ) { state.cstatus[i] = 0; state.tmp0[i] = state.bndl[i]; } if( state.hasbndu[i] && (double)(state.tmp0[i])>=(double)(state.bndu[i]) ) { state.cstatus[i] = 0; state.tmp0[i] = state.bndu[i]; } } for(i=0; i<=state.nec+state.nic-1; i++) { v = -state.cleic[i,n]; v0 = 0; v1 = 0; for(j=0; j<=n-1; j++) { vx = state.tmp0[j]/state.s[j]; vc = state.cleic[i,j]*state.s[j]; v = v+vx*vc; v0 = v0+math.sqr(vx); v1 = v1+math.sqr(vc); } vv = Math.Sqrt(v0)*Math.Sqrt(v1)*1000*math.machineepsilon; if( i=state.nec ) { state.tmpm0[i,n+i-state.nec] = 1.0; } state.tmpm0[i,n+state.nic] = state.cleic[i,n]; } for(i_=0; i_<=n-1;i_++) { state.tmpfeas[i_] = state.xc[i_]; } for(i=0; i<=state.nic-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.cleic[i+state.nec,i_]*state.xc[i_]; } state.tmpfeas[i+n] = Math.Max(state.cleic[i+state.nec,n]-v, 0.0); } if( !optserv.findfeasiblepoint(ref state.tmpfeas, state.bndl, state.hasbndl, state.bndu, state.hasbndu, n, state.nic, state.tmpm0, state.nec+state.nic, 1.0E-6, ref i, ref j, _params) ) { return result; } for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.tmpfeas[i_]; } for(i=0; i<=n-1; i++) { if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { state.cstatus[i] = 1; continue; } if( (state.hasbndl[i] && (double)(state.xc[i])==(double)(state.bndl[i])) || (state.hasbndu[i] && (double)(state.xc[i])==(double)(state.bndu[i])) ) { state.cstatus[i] = 0; continue; } state.cstatus[i] = -1; } for(i=0; i<=state.nec-1; i++) { state.cstatus[n+i] = 1; } for(i=0; i<=state.nic-1; i++) { if( (double)(state.tmpfeas[n+i])==(double)(0) ) { state.cstatus[n+state.nec+i] = 0; } else { state.cstatus[n+state.nec+i] = -1; } } } } else { // // Only box constraints are present, quick code can be used // for(i=0; i<=n-1; i++) { state.cstatus[i] = -1; if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { state.cstatus[i] = 1; state.xc[i] = state.bndl[i]; continue; } if( state.hasbndl[i] && (double)(state.xc[i])<=(double)(state.bndl[i]) ) { state.xc[i] = state.bndl[i]; state.cstatus[i] = 0; continue; } if( state.hasbndu[i] && (double)(state.xc[i])>=(double)(state.bndu[i]) ) { state.xc[i] = state.bndu[i]; state.cstatus[i] = 0; continue; } } state.feasinitpt = true; } // // Change state, allocate temporaries // result = true; state.algostate = 1; state.basisisready = false; state.hasxc = true; return result; } /************************************************************************* This function explores search direction and calculates bound for step as well as information for activation of constraints. INPUT PARAMETERS: State - SAS structure which stores current point and all other active set related information D - descent direction to explore OUTPUT PARAMETERS: StpMax - upper limit on step length imposed by yet inactive constraints. Can be zero in case some constraints can be activated by zero step. Equal to some large value in case step is unlimited. CIdx - -1 for unlimited step, in [0,N+NEC+NIC) in case of limited step. VVal - value which is assigned to X[CIdx] during activation. For CIdx<0 or CIdx>=N some dummy value is assigned to this parameter. *************************************************************************/ public static void sasexploredirection(sactiveset state, double[] d, ref double stpmax, ref int cidx, ref double vval, alglib.xparams _params) { int n = 0; int nec = 0; int nic = 0; int i = 0; double prevmax = 0; double vc = 0; double vd = 0; int i_ = 0; stpmax = 0; cidx = 0; vval = 0; alglib.ap.assert(state.algostate==1, "SASExploreDirection: is not in optimization mode"); n = state.n; nec = state.nec; nic = state.nic; cidx = -1; vval = 0; stpmax = 1.0E50; for(i=0; i<=n-1; i++) { if( state.cstatus[i]<=0 ) { alglib.ap.assert(!state.hasbndl[i] || (double)(state.xc[i])>=(double)(state.bndl[i]), "SASExploreDirection: internal error - infeasible X"); alglib.ap.assert(!state.hasbndu[i] || (double)(state.xc[i])<=(double)(state.bndu[i]), "SASExploreDirection: internal error - infeasible X"); if( state.hasbndl[i] && (double)(d[i])<(double)(0) ) { prevmax = stpmax; stpmax = apserv.safeminposrv(state.xc[i]-state.bndl[i], -d[i], stpmax, _params); if( (double)(stpmax)<(double)(prevmax) ) { cidx = i; vval = state.bndl[i]; } } if( state.hasbndu[i] && (double)(d[i])>(double)(0) ) { prevmax = stpmax; stpmax = apserv.safeminposrv(state.bndu[i]-state.xc[i], d[i], stpmax, _params); if( (double)(stpmax)<(double)(prevmax) ) { cidx = i; vval = state.bndu[i]; } } } } for(i=nec; i<=nec+nic-1; i++) { if( state.cstatus[n+i]<=0 ) { vc = 0.0; for(i_=0; i_<=n-1;i_++) { vc += state.cleic[i,i_]*state.xc[i_]; } vc = vc-state.cleic[i,n]; vd = 0.0; for(i_=0; i_<=n-1;i_++) { vd += state.cleic[i,i_]*d[i_]; } if( (double)(vd)<=(double)(0) ) { continue; } if( (double)(vc)<(double)(0) ) { // // XC is strictly feasible with respect to I-th constraint, // we can perform non-zero step because there is non-zero distance // between XC and bound. // prevmax = stpmax; stpmax = apserv.safeminposrv(-vc, vd, stpmax, _params); if( (double)(stpmax)<(double)(prevmax) ) { cidx = n+i; } } else { // // XC is at the boundary (or slightly beyond it), and step vector // points beyond the boundary. // // The only thing we can do is to perform zero step and activate // I-th constraint. // stpmax = 0; cidx = n+i; } } } } /************************************************************************* This subroutine moves current point to XN, which can be: a) point in the direction previously explored with SASExploreDirection() function (in this case NeedAct/CIdx/CVal are used) b) point in arbitrary direction, not necessarily previously checked with SASExploreDirection() function. Step may activate one constraint. It is assumed than XN is approximately feasible (small error as large as several ulps is possible). Strict feasibility with respect to bound constraints is enforced during activation, feasibility with respect to general linear constraints is not enforced. This function activates boundary constraints, such that both is True: 1) XC[I] is not at the boundary 2) XN[I] is at the boundary or beyond it INPUT PARAMETERS: S - active set object XN - new point. NeedAct - True in case one constraint needs activation CIdx - index of constraint, in [0,N+NEC+NIC). Ignored if NeedAct is false. This value is calculated by SASExploreDirection(). CVal - for CIdx in [0,N) this field stores value which is assigned to XC[CIdx] during activation. CVal is ignored in other cases. This value is calculated by SASExploreDirection(). OUTPUT PARAMETERS: S - current point and list of active constraints are changed. RESULT: >0, in case at least one inactive non-candidate constraint was activated =0, in case only "candidate" constraints were activated <0, in case no constraints were activated by the step NOTE: in general case State.XC<>XN because activation of constraints may slightly change current point (to enforce feasibility). -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ public static int sasmoveto(sactiveset state, double[] xn, bool needact, int cidx, double cval, alglib.xparams _params) { int result = 0; int n = 0; int nec = 0; int nic = 0; int i = 0; bool wasactivation = new bool(); alglib.ap.assert(state.algostate==1, "SASMoveTo: is not in optimization mode"); n = state.n; nec = state.nec; nic = state.nic; // // Save previous state, update current point // apserv.rvectorsetlengthatleast(ref state.mtx, n, _params); apserv.ivectorsetlengthatleast(ref state.mtas, n+nec+nic, _params); for(i=0; i<=n-1; i++) { state.mtx[i] = state.xc[i]; state.xc[i] = xn[i]; } for(i=0; i<=n+nec+nic-1; i++) { state.mtas[i] = state.cstatus[i]; } // // Activate constraints // apserv.bvectorsetlengthatleast(ref state.mtnew, n+nec+nic, _params); wasactivation = false; for(i=0; i<=n+nec+nic-1; i++) { state.mtnew[i] = false; } if( needact ) { // // Activation // alglib.ap.assert(cidx>=0 && cidx=(double)(state.bndu[i])) && (double)(state.xc[i])!=(double)(state.mtx[i]) ) { state.xc[i] = state.bndu[i]; state.cstatus[i] = 1; state.mtnew[i] = true; wasactivation = true; } } // // Determine return status: // * -1 in case no constraints were activated // * 0 in case only "candidate" constraints were activated // * +1 in case at least one "non-candidate" constraint was activated // if( wasactivation ) { // // Step activated one/several constraints, but sometimes it is spurious // activation - RecalculateConstraints() tells us that constraint is // inactive (negative Largrange multiplier), but step activates it // because of numerical noise. // // This block of code checks whether step activated truly new constraints // (ones which were not in the active set at the solution): // // * for non-boundary constraint it is enough to check that previous value // of CStatus[i] is negative (=far from boundary), and new one is // positive (=we are at the boundary, constraint is activated). // // * for boundary constraints previous criterion won't work. Each variable // has two constraints, and simply checking their status is not enough - // we have to correctly identify cases when we leave one boundary // (PrevActiveSet[i]=0) and move to another boundary (CStatus[i]>0). // Such cases can be identified if we compare previous X with new X. // // In case only "candidate" constraints were activated, result variable // is set to 0. In case at least one new constraint was activated, result // is set to 1. // result = 0; for(i=0; i<=n-1; i++) { if( state.cstatus[i]>0 && (double)(state.xc[i])!=(double)(state.mtx[i]) ) { result = 1; } } for(i=n; i<=n+state.nec+state.nic-1; i++) { if( state.mtas[i]<0 && state.cstatus[i]>0 ) { result = 1; } } } else { // // No activation, return -1 // result = -1; } // // Update basis // sasappendtobasis(state, state.mtnew, _params); return result; } /************************************************************************* This subroutine performs immediate activation of one constraint: * "immediate" means that we do not have to move to activate it * in case boundary constraint is activated, we enforce current point to be exactly at the boundary INPUT PARAMETERS: S - active set object CIdx - index of constraint, in [0,N+NEC+NIC). This value is calculated by SASExploreDirection(). CVal - for CIdx in [0,N) this field stores value which is assigned to XC[CIdx] during activation. CVal is ignored in other cases. This value is calculated by SASExploreDirection(). -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ public static void sasimmediateactivation(sactiveset state, int cidx, double cval, alglib.xparams _params) { int i = 0; alglib.ap.assert(state.algostate==1, "SASMoveTo: is not in optimization mode"); if( cidx X1. 2) next, we perform projection with respect to ALL boundary constraints which are violated at X1: X1 -> X2. 3) X is replaced by X2. The idea is that this function can preserve and enforce feasibility during optimization, and additional penalty parameter can be used to prevent algo from leaving feasible set because of rounding errors. INPUT PARAMETERS: S - active set object X - array[N], candidate point OUTPUT PARAMETERS: X - "improved" candidate point: a) feasible with respect to all boundary constraints b) feasibility with respect to active set is retained at good level. Penalty - penalty term, which can be added to function value if user wants to penalize violation of constraints (recommended). NOTE: this function is not intended to find exact projection (i.e. best approximation) of X into feasible set. It just improves situation a bit. Regular use of this function will help you to retain feasibility - if you already have something to start with and constrain your steps is such way that the only source of infeasibility are roundoff errors. -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ public static void sascorrection(sactiveset state, double[] x, ref double penalty, alglib.xparams _params) { int i = 0; int j = 0; int n = 0; double v = 0; int i_ = 0; penalty = 0; alglib.ap.assert(state.algostate==1, "SASCorrection: is not in optimization mode"); sasrebuildbasis(state, _params); n = state.n; apserv.rvectorsetlengthatleast(ref state.corrtmp, n, _params); // // Calculate penalty term. // penalty = sasactivelcpenalty1(state, x, _params); // // Perform projection 1. // // This projecton is given by: // // x_proj = x - S*S*As'*(As*x-b) // // where x is original x before projection, S is a scale matrix, // As is a matrix of equality constraints (active set) which were // orthogonalized with respect to inner product given by S (i.e. we // have As*S*S'*As'=I), b is a right part of the orthogonalized // constraints. // // NOTE: you can verify that x_proj is strictly feasible w.r.t. // active set by multiplying it by As - you will get // As*x_proj = As*x - As*x + b = b. // // This formula for projection can be obtained by solving // following minimization problem. // // min ||inv(S)*(x_proj-x)||^2 s.t. As*x_proj=b // // NOTE: we apply sparse batch by examining CStatus[]; it is guaranteed // to contain sparse batch, but avoids roundoff errors associated // with the fact that some box constraints were moved to sparse // storage // // for(i_=0; i_<=n-1;i_++) { state.corrtmp[i_] = x[i_]; } for(i=0; i<=state.densebatchsize-1; i++) { v = -state.sdensebatch[i,n]; for(j=0; j<=n-1; j++) { v = v+state.sdensebatch[i,j]*state.corrtmp[j]; } for(j=0; j<=n-1; j++) { state.corrtmp[j] = state.corrtmp[j]-v*state.sdensebatch[i,j]*math.sqr(state.s[j]); } } for(i=0; i<=n-1; i++) { if( state.cstatus[i]>0 ) { state.corrtmp[i] = state.xc[i]; } } // // Perform projection 2 // for(i=0; i<=n-1; i++) { x[i] = state.corrtmp[i]; if( state.hasbndl[i] && (double)(x[i])<(double)(state.bndl[i]) ) { x[i] = state.bndl[i]; } if( state.hasbndu[i] && (double)(x[i])>(double)(state.bndu[i]) ) { x[i] = state.bndu[i]; } } } /************************************************************************* This subroutine returns L1 penalty for violation of active general linear constraints (violation of boundary or inactive linear constraints is not added to penalty). Penalty term is equal to: Penalty = SUM( Abs((C_i*x-R_i)/Alpha_i) ) Here: * summation is performed for I=0...NEC+NIC-1, CStatus[N+I]>0 (only for rows of CLEIC which are in active set) * C_i is I-th row of CLEIC * R_i is corresponding right part * S is a scale matrix * Alpha_i = ||S*C_i|| - is a scaling coefficient which "normalizes" I-th summation term according to its scale. INPUT PARAMETERS: S - active set object X - array[N], candidate point -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ public static double sasactivelcpenalty1(sactiveset state, double[] x, alglib.xparams _params) { double result = 0; int i = 0; int j = 0; int n = 0; int nec = 0; int nic = 0; double v = 0; double alpha = 0; double p = 0; alglib.ap.assert(state.algostate==1, "SASActiveLCPenalty1: is not in optimization mode"); sasrebuildbasis(state, _params); n = state.n; nec = state.nec; nic = state.nic; // // Calculate penalty term. // result = 0; for(i=0; i<=nec+nic-1; i++) { if( state.cstatus[n+i]>0 ) { alpha = 0; p = -state.cleic[i,n]; for(j=0; j<=n-1; j++) { v = state.cleic[i,j]; p = p+v*x[j]; alpha = alpha+math.sqr(v*state.s[j]); } alpha = Math.Sqrt(alpha); if( (double)(alpha)!=(double)(0) ) { result = result+Math.Abs(p/alpha); } } } return result; } /************************************************************************* This subroutine calculates scaled norm of vector after projection onto subspace of active constraints. Most often this function is used to test stopping conditions. INPUT PARAMETERS: S - active set object D - vector whose norm is calculated RESULT: Vector norm (after projection and scaling) NOTE: projection is performed first, scaling is performed after projection NOTE: if we have N active constraints, zero value (exact zero) is returned -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ public static double sasscaledconstrainednorm(sactiveset state, double[] d, alglib.xparams _params) { double result = 0; int i = 0; int n = 0; double v = 0; int i_ = 0; alglib.ap.assert(state.algostate==1, "SASMoveTo: is not in optimization mode"); n = state.n; apserv.rvectorsetlengthatleast(ref state.scntmp, n, _params); // // Prepare basis (if needed) // sasrebuildbasis(state, _params); // // Calculate descent direction // if( state.sparsebatchsize+state.densebatchsize>=n ) { // // Quick exit if number of active constraints is N or larger // result = 0.0; return result; } for(i=0; i<=n-1; i++) { state.scntmp[i] = d[i]; } for(i=0; i<=state.densebatchsize-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.idensebatch[i,i_]*state.scntmp[i_]; } for(i_=0; i_<=n-1;i_++) { state.scntmp[i_] = state.scntmp[i_] - v*state.idensebatch[i,i_]; } } for(i=0; i<=n-1; i++) { if( state.cstatus[i]>0 ) { state.scntmp[i] = 0; } } v = 0.0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.s[i]*state.scntmp[i]); } result = Math.Sqrt(v); return result; } /************************************************************************* This subroutine turns off optimization mode. INPUT PARAMETERS: S - active set object OUTPUT PARAMETERS: S - state is changed NOTE: this function can be called many times for optimizer which was already stopped. -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ public static void sasstopoptimization(sactiveset state, alglib.xparams _params) { state.algostate = 0; } /************************************************************************* This function recalculates constraints - activates and deactivates them according to gradient value at current point. Algorithm assumes that we want to make steepest descent step from current point; constraints are activated and deactivated in such way that we won't violate any constraint by steepest descent step. After call to this function active set is ready to try steepest descent step (SASDescentDirection-SASExploreDirection-SASMoveTo). Only already "active" and "candidate" elements of ActiveSet are examined; constraints which are not active are not examined. INPUT PARAMETERS: State - active set object GC - array[N], gradient at XC OUTPUT PARAMETERS: State - active set object, with new set of constraint -- ALGLIB -- Copyright 26.09.2012 by Bochkanov Sergey *************************************************************************/ public static void sasreactivateconstraints(sactiveset state, double[] gc, alglib.xparams _params) { alglib.ap.assert(state.algostate==1, "SASReactivateConstraints: must be in optimization mode"); reactivateconstraints(state, gc, state.unitdiagonal, _params); } /************************************************************************* This function recalculates constraints - activates and deactivates them according to gradient value at current point. Algorithm assumes that we want to make Quasi-Newton step from current point with diagonal Quasi-Newton matrix H. Constraints are activated and deactivated in such way that we won't violate any constraint by step. After call to this function active set is ready to try preconditioned steepest descent step (SASDescentDirection-SASExploreDirection-SASMoveTo). Only already "active" and "candidate" elements of ActiveSet are examined; constraints which are not active are not examined. INPUT PARAMETERS: State - active set object GC - array[N], gradient at XC OUTPUT PARAMETERS: State - active set object, with new set of constraint -- ALGLIB -- Copyright 26.09.2012 by Bochkanov Sergey *************************************************************************/ public static void sasreactivateconstraintsprec(sactiveset state, double[] gc, alglib.xparams _params) { alglib.ap.assert(state.algostate==1, "SASReactivateConstraintsPrec: must be in optimization mode"); reactivateconstraints(state, gc, state.h, _params); } /************************************************************************* This function builds three orthonormal basises for current active set: * P-orthogonal one, which is orthogonalized with inner product (x,y) = x'*P*y, where P=inv(H) is current preconditioner * S-orthogonal one, which is orthogonalized with inner product (x,y) = x'*S'*S*y, where S is diagonal scaling matrix * I-orthogonal one, which is orthogonalized with standard dot product NOTE: all sets of orthogonal vectors are guaranteed to have same size. P-orthogonal basis is built first, I/S-orthogonal basises are forced to have same number of vectors as P-orthogonal one (padded by zero vectors if needed). NOTE: this function tracks changes in active set; first call will result in reorthogonalization INPUT PARAMETERS: State - active set object H - diagonal preconditioner, H[i]>0 OUTPUT PARAMETERS: State - active set object with new basis -- ALGLIB -- Copyright 20.06.2012 by Bochkanov Sergey *************************************************************************/ public static void sasrebuildbasis(sactiveset state, alglib.xparams _params) { int n = 0; int nec = 0; int nic = 0; int i = 0; int j = 0; bool hasactivelin = new bool(); int candidatescnt = 0; double v = 0; double vv = 0; double vmax = 0; int kmax = 0; int i_ = 0; if( state.basisisready ) { return; } n = state.n; nec = state.nec; nic = state.nic; apserv.rvectorsetlengthatleast(ref state.tmp0, n, _params); apserv.rvectorsetlengthatleast(ref state.tmpprodp, n, _params); apserv.rvectorsetlengthatleast(ref state.tmpprods, n, _params); apserv.rvectorsetlengthatleast(ref state.tmpcp, n+1, _params); apserv.rvectorsetlengthatleast(ref state.tmpcs, n+1, _params); apserv.rvectorsetlengthatleast(ref state.tmpci, n+1, _params); apserv.rmatrixsetlengthatleast(ref state.tmpbasis, nec+nic, n+1, _params); apserv.rmatrixsetlengthatleast(ref state.pdensebatch, nec+nic, n+1, _params); apserv.rmatrixsetlengthatleast(ref state.idensebatch, nec+nic, n+1, _params); apserv.rmatrixsetlengthatleast(ref state.sdensebatch, nec+nic, n+1, _params); apserv.ivectorsetlengthatleast(ref state.sparsebatch, n, _params); state.sparsebatchsize = 0; state.densebatchsize = 0; state.basisage = 0; state.basisisready = true; // // Determine number of active boundary and non-boundary // constraints, move them to TmpBasis. Quick exit if no // non-boundary constraints were detected. // hasactivelin = false; for(i=0; i<=nec+nic-1; i++) { if( state.cstatus[n+i]>0 ) { hasactivelin = true; } } for(j=0; j<=n-1; j++) { if( state.cstatus[j]>0 ) { state.sparsebatch[state.sparsebatchsize] = j; state.sparsebatchsize = state.sparsebatchsize+1; } } if( !hasactivelin ) { return; } // // Prepare precomputed values // apserv.rvectorsetlengthatleast(ref state.tmpreciph, n, _params); for(i=0; i<=n-1; i++) { state.tmpreciph[i] = 1/state.h[i]; } // // Prepare initial candidate set: // * select active constraints // * normalize (inner product is given by preconditioner) // * orthogonalize with respect to active box constraints // * copy normalized/orthogonalized candidates to PBasis/SBasis/IBasis // candidatescnt = 0; for(i=0; i<=nec+nic-1; i++) { if( state.cstatus[n+i]>0 ) { for(i_=0; i_<=n;i_++) { state.tmpbasis[candidatescnt,i_] = state.cleic[i,i_]; } apserv.inc(ref candidatescnt, _params); } } for(i=0; i<=candidatescnt-1; i++) { v = 0.0; for(j=0; j<=n-1; j++) { v = v+math.sqr(state.tmpbasis[i,j])*state.tmpreciph[j]; } if( (double)(v)>(double)(0) ) { v = 1/Math.Sqrt(v); for(j=0; j<=n; j++) { state.tmpbasis[i,j] = state.tmpbasis[i,j]*v; } } } for(j=0; j<=n-1; j++) { if( state.cstatus[j]>0 ) { for(i=0; i<=candidatescnt-1; i++) { state.tmpbasis[i,n] = state.tmpbasis[i,n]-state.tmpbasis[i,j]*state.xc[j]; state.tmpbasis[i,j] = 0.0; } } } for(i=0; i<=candidatescnt-1; i++) { for(j=0; j<=n; j++) { state.pdensebatch[i,j] = state.tmpbasis[i,j]; state.sdensebatch[i,j] = state.tmpbasis[i,j]; state.idensebatch[i,j] = state.tmpbasis[i,j]; } } // // Perform orthogonalization of general linear constraints with respect // to each other (constraints in P/S/IBasis are already normalized w.r.t. // box constraints). During this process we select strictly active constraints // from the candidate set, and drop ones which were detected as redundant // during orthogonalization. // // Orthogonalization is performed with the help of Gram-Schmidt process. // Due to accumulation of round-off errors it is beneficial to perform // pivoting, i.e. to select candidate vector with largest norm at each // step. // // First (basic) version of the algorithm is: // 0. split all constraints into two sets: basis ones (initially empty) // and candidate ones (all constraints) // 1. fill PBasis with H-normalized candidate constraints, fill // corresponding entries of S/IBasis with corresponding // (non-normalized) constraints // 2. select row of PBasis with largest norm, move it (and its S/IBasis // counterparts) to the beginning of the candidate set, H-normalize // this row (rows of S/IBasis are normalized using corresponding norms). // Stop if largest row is nearly (or exactly) zero. // 3. orthogonalize remaining rows of P/S/IBasis with respect to // one chosen at step (2). It can be done efficiently using // combination of DGEMV/DGER BLAS calls. // 4. increase basis size by one, decrease candidate set size by one, // goto (2) // // However, naive implementation of the algorithm above spends significant // amount of time in step (2) - selection of row with largest H-norm. Step // (3) can be efficiently implemented with optimized BLAS, but we have no // optimized BLAS kernels for step(2). And because step (3) changes row norms, // step (2) have to be re-calculated every time, which is quite slow. // // We can save significant amount of calculations by noticing that: // * step (3) DECREASES row norms, but never increases it // * we can maintain upper bounds for row H-norms is a separate array, // use them for initial evaluation of best candidates, and update them // after we find some promising row (all bounds are invalidated after // step 3, but their old values still carry some information) // * it is beneficial re-evaluate bounds only for rows which are // significantly (at least few percents) larger than best one found so far // * because rows are initially normalized, initial values for upper bounds // can be set to 1.0 // alglib.ap.assert(state.densebatchsize==0, "SAS: integrity check failed"); alglib.ap.assert((double)(minnormseparation)>(double)(0), "SAS: integrity check failed"); apserv.rvectorsetlengthatleast(ref state.tmpnormestimates, candidatescnt, _params); for(i=0; i<=candidatescnt-1; i++) { state.tmpnormestimates[i] = 1.0; } while( state.sparsebatchsize+state.densebatchsize(double)(vmax) ) { vmax = v; kmax = i; } } if( (double)(vmax)<(double)(1.0E4*math.machineepsilon) || kmax<0 ) { // // All candidates are either zero or too small (after orthogonalization) // candidatescnt = 0; break; } // // Candidate is selected for inclusion into basis set. // // Move candidate row to the beginning of candidate array (which is // right past the end of the approved basis). Normalize (for P-basis // we perform preconditioner-based normalization, for S-basis - scale // based, for I-basis - identity based). // apserv.swaprows(state.pdensebatch, state.densebatchsize, kmax, n+1, _params); apserv.swaprows(state.sdensebatch, state.densebatchsize, kmax, n+1, _params); apserv.swaprows(state.idensebatch, state.densebatchsize, kmax, n+1, _params); apserv.swapelements(state.tmpnormestimates, state.densebatchsize, kmax, _params); v = 1/vmax; for(i_=0; i_<=n;i_++) { state.pdensebatch[state.densebatchsize,i_] = v*state.pdensebatch[state.densebatchsize,i_]; } v = 0; for(j=0; j<=n-1; j++) { vv = state.sdensebatch[state.densebatchsize,j]*state.s[j]; v = v+vv*vv; } alglib.ap.assert((double)(v)>(double)(0), "SActiveSet.RebuildBasis(): integrity check failed, SNorm=0"); v = 1/Math.Sqrt(v); for(i_=0; i_<=n;i_++) { state.sdensebatch[state.densebatchsize,i_] = v*state.sdensebatch[state.densebatchsize,i_]; } v = 0; for(j=0; j<=n-1; j++) { vv = state.idensebatch[state.densebatchsize,j]; v = v+vv*vv; } alglib.ap.assert((double)(v)>(double)(0), "SActiveSet.RebuildBasis(): integrity check failed, INorm=0"); v = 1/Math.Sqrt(v); for(i_=0; i_<=n;i_++) { state.idensebatch[state.densebatchsize,i_] = v*state.idensebatch[state.densebatchsize,i_]; } // // Reorthogonalize other candidates with respect to candidate #0: // * calculate projections en masse with GEMV() // * subtract projections with GER() // apserv.rvectorsetlengthatleast(ref state.tmp0, candidatescnt-1, _params); for(j=0; j<=n-1; j++) { state.tmpprodp[j] = state.pdensebatch[state.densebatchsize,j]/state.h[j]; state.tmpprods[j] = state.sdensebatch[state.densebatchsize,j]*math.sqr(state.s[j]); } for(j=0; j<=n; j++) { state.tmpcp[j] = state.pdensebatch[state.densebatchsize,j]; state.tmpcs[j] = state.sdensebatch[state.densebatchsize,j]; state.tmpci[j] = state.idensebatch[state.densebatchsize,j]; } ablas.rmatrixgemv(candidatescnt-1, n, 1.0, state.pdensebatch, state.densebatchsize+1, 0, 0, state.tmpprodp, 0, 0.0, state.tmp0, 0, _params); ablas.rmatrixger(candidatescnt-1, n+1, state.pdensebatch, state.densebatchsize+1, 0, -1.0, state.tmp0, 0, state.tmpcp, 0, _params); ablas.rmatrixgemv(candidatescnt-1, n, 1.0, state.sdensebatch, state.densebatchsize+1, 0, 0, state.tmpprods, 0, 0.0, state.tmp0, 0, _params); ablas.rmatrixger(candidatescnt-1, n+1, state.sdensebatch, state.densebatchsize+1, 0, -1.0, state.tmp0, 0, state.tmpcs, 0, _params); ablas.rmatrixgemv(candidatescnt-1, n, 1.0, state.idensebatch, state.densebatchsize+1, 0, 0, state.tmpci, 0, 0.0, state.tmp0, 0, _params); ablas.rmatrixger(candidatescnt-1, n+1, state.idensebatch, state.densebatchsize+1, 0, -1.0, state.tmp0, 0, state.tmpci, 0, _params); // // Increase basis, decrease candidates count // apserv.inc(ref state.densebatchsize, _params); apserv.dec(ref candidatescnt, _params); } } /************************************************************************* This function appends new constraints (if possible; sometimes it isn't!) to three orthonormal basises for current active set: * P-orthogonal one, which is orthogonalized with inner product (x,y) = x'*P*y, where P=inv(H) is current preconditioner * S-orthogonal one, which is orthogonalized with inner product (x,y) = x'*S'*S*y, where S is diagonal scaling matrix * I-orthogonal one, which is orthogonalized with standard dot product NOTE: all sets of orthogonal vectors are guaranteed to have same size. P-orthogonal basis is built first, I/S-orthogonal basises are forced to have same number of vectors as P-orthogonal one (padded by zero vectors if needed). NOTE: this function may fail to update basis without full recalculation; in such case it will set BasisIsReady to False and silently return; if it succeeds, it will increase BasisSize. INPUT PARAMETERS: State - active set object NewEntries - array[N+NEC+NIC], indexes of constraints being added are marked as True; it is responsibility of the caller to specify only those constraints which were previously inactive; when some constraint is already in the active set, algorithm behavior is undefined. OUTPUT PARAMETERS: State - active set object with new basis -- ALGLIB -- Copyright 03.10.2017 by Bochkanov Sergey *************************************************************************/ public static void sasappendtobasis(sactiveset state, bool[] newentries, alglib.xparams _params) { int n = 0; int nec = 0; int nic = 0; int i = 0; int j = 0; int t = 0; int nact = 0; double v = 0; double vp = 0; double vs = 0; double vi = 0; double initnormp = 0; double projnormp = 0; double projnorms = 0; double projnormi = 0; int i_ = 0; if( !state.basisisready ) { return; } n = state.n; nec = state.nec; nic = state.nic; // // Count number of constraints to activate; // perform integrity check. // nact = 0; for(i=0; i<=n-1; i++) { if( newentries[i] ) { nact = nact+1; } } for(i=n; i<=n+nec-1; i++) { alglib.ap.assert(!newentries[i], "SAS: integrity check failed (appendtobasis.0)"); } for(i=n+nec; i<=n+nec+nic-1; i++) { if( newentries[i] ) { nact = nact+1; } } if( nact+state.basisage>maxbasisage ) { state.basisisready = false; return; } // // Resize basis matrices if needed // apserv.rmatrixgrowrowsto(ref state.pdensebatch, state.densebatchsize+nact, n+1, _params); apserv.rmatrixgrowrowsto(ref state.sdensebatch, state.densebatchsize+nact, n+1, _params); apserv.rmatrixgrowrowsto(ref state.idensebatch, state.densebatchsize+nact, n+1, _params); // // Try adding recommended entries to basis. // If reorthogonalization removes too much of candidate constraint, // we will invalidate basis and try to rebuild it from scratch. // apserv.rvectorsetlengthatleast(ref state.tmp0, n+1, _params); apserv.rvectorsetlengthatleast(ref state.tmpcp, n+1, _params); apserv.rvectorsetlengthatleast(ref state.tmpcs, n+1, _params); apserv.rvectorsetlengthatleast(ref state.tmpci, n+1, _params); apserv.rvectorsetlengthatleast(ref state.tmpprodp, n, _params); apserv.rvectorsetlengthatleast(ref state.tmpprods, n, _params); for(t=0; t<=n+nec+nic-1; t++) { if( newentries[t] ) { // // Basis is full? Quick skip! // if( state.sparsebatchsize+state.densebatchsize>=n ) { alglib.ap.assert(state.sparsebatchsize+state.densebatchsize==n, "SAS: integrity check failed (sasappendtobasis)"); break; } // // Copy constraint to temporary storage. // if( t(double)(0), "SAS: integrity check failed, ProjNormP=0"); alglib.ap.assert((double)(projnorms)>(double)(0), "SAS: integrity check failed, ProjNormS=0"); alglib.ap.assert((double)(projnormi)>(double)(0), "SAS: integrity check failed, ProjNormI=0"); v = 1/projnormp; for(i_=0; i_<=n;i_++) { state.pdensebatch[state.densebatchsize,i_] = v*state.tmpcp[i_]; } v = 1/projnorms; for(i_=0; i_<=n;i_++) { state.sdensebatch[state.densebatchsize,i_] = v*state.tmpcs[i_]; } v = 1/projnormi; for(i_=0; i_<=n;i_++) { state.idensebatch[state.densebatchsize,i_] = v*state.tmpci[i_]; } // // Increase set size // apserv.inc(ref state.densebatchsize, _params); apserv.inc(ref state.basisage, _params); } } } /************************************************************************* This subroutine calculates preconditioned descent direction subject to current active set. INPUT PARAMETERS: State - active set object G - array[N], gradient H - array[N], Hessian matrix HA - active constraints orthogonalized in such way that HA*inv(H)*HA'= I. Normalize- whether we need normalized descent or not D - possibly preallocated buffer; automatically resized. OUTPUT PARAMETERS: D - descent direction projected onto current active set. Components of D which correspond to active boundary constraints are forced to be exactly zero. In case D is non-zero and Normalize is True, it is normalized to have unit norm. NOTE: if we have N active constraints, D is explicitly set to zero. -- ALGLIB -- Copyright 21.12.2012 by Bochkanov Sergey *************************************************************************/ private static void constraineddescent(sactiveset state, double[] g, double[] h, double[,] ha, bool normalize, ref double[] d, alglib.xparams _params) { int i = 0; int j = 0; int n = 0; double v = 0; int i_ = 0; alglib.ap.assert(state.algostate==1, "SAS: internal error in ConstrainedDescent() - not in optimization mode"); alglib.ap.assert(state.basisisready, "SAS: internal error in ConstrainedDescent() - no basis"); n = state.n; apserv.rvectorsetlengthatleast(ref d, n, _params); // // Calculate preconditioned constrained descent direction: // // d := -inv(H)*( g - HA'*(HA*inv(H)*g) ) // // Formula above always gives direction which is orthogonal to rows of HA. // You can verify it by multiplication of both sides by HA[i] (I-th row), // taking into account that HA*inv(H)*HA'= I (by definition of HA - it is // orthogonal basis with inner product given by inv(H)). // for(i=0; i<=n-1; i++) { d[i] = g[i]; } for(i=0; i<=state.densebatchsize-1; i++) { v = 0.0; for(j=0; j<=n-1; j++) { v = v+ha[i,j]*d[j]/h[j]; } for(i_=0; i_<=n-1;i_++) { d[i_] = d[i_] - v*ha[i,i_]; } } for(i=0; i<=n-1; i++) { if( state.cstatus[i]>0 ) { d[i] = 0; } } v = 0.0; for(i=0; i<=n-1; i++) { d[i] = -(d[i]/h[i]); v = v+math.sqr(d[i]); } v = Math.Sqrt(v); if( state.sparsebatchsize+state.densebatchsize>=n ) { v = 0; for(i=0; i<=n-1; i++) { d[i] = 0; } } if( normalize && (double)(v)>(double)(0) ) { for(i=0; i<=n-1; i++) { d[i] = d[i]/v; } } } /************************************************************************* This function recalculates constraints - activates and deactivates them according to gradient value at current point. Algorithm assumes that we want to make Quasi-Newton step from current point with diagonal Quasi-Newton matrix H. Constraints are activated and deactivated in such way that we won't violate any constraint by step. Only already "active" and "candidate" elements of ActiveSet are examined; constraints which are not active are not examined. INPUT PARAMETERS: State - active set object GC - array[N], gradient at XC H - array[N], Hessian matrix OUTPUT PARAMETERS: State - active set object, with new set of constraint -- ALGLIB -- Copyright 26.09.2012 by Bochkanov Sergey *************************************************************************/ private static void reactivateconstraints(sactiveset state, double[] gc, double[] h, alglib.xparams _params) { int n = 0; int nec = 0; int nic = 0; int i = 0; int j = 0; int idx0 = 0; int idx1 = 0; double v = 0; int nactivebnd = 0; int nactivelin = 0; int nactiveconstraints = 0; double rowscale = 0; int i_ = 0; alglib.ap.assert(state.algostate==1, "SASReactivateConstraintsPrec: must be in optimization mode"); // // Prepare // n = state.n; nec = state.nec; nic = state.nic; state.basisisready = false; // // Handle important special case - no linear constraints, // only boundary constraints are present // if( nec+nic==0 ) { for(i=0; i<=n-1; i++) { if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { state.cstatus[i] = 1; continue; } if( (state.hasbndl[i] && (double)(state.xc[i])==(double)(state.bndl[i])) && (double)(gc[i])>=(double)(0) ) { state.cstatus[i] = 1; continue; } if( (state.hasbndu[i] && (double)(state.xc[i])==(double)(state.bndu[i])) && (double)(gc[i])<=(double)(0) ) { state.cstatus[i] = 1; continue; } state.cstatus[i] = -1; } return; } // // General case. // Allocate temporaries. // apserv.rvectorsetlengthatleast(ref state.rctmpg, n, _params); apserv.rvectorsetlengthatleast(ref state.rctmprightpart, n, _params); apserv.rvectorsetlengthatleast(ref state.rctmps, n, _params); apserv.rmatrixsetlengthatleast(ref state.rctmpdense0, n, nec+nic, _params); apserv.rmatrixsetlengthatleast(ref state.rctmpdense1, n, nec+nic, _params); apserv.bvectorsetlengthatleast(ref state.rctmpisequality, n+nec+nic, _params); apserv.ivectorsetlengthatleast(ref state.rctmpconstraintidx, n+nec+nic, _params); // // Calculate descent direction // for(i_=0; i_<=n-1;i_++) { state.rctmpg[i_] = -gc[i_]; } // // Determine candidates to the active set. // // After this block constraints become either "inactive" (CStatus[i]<0) // or "candidates" (CStatus[i]=0). Previously active constraints always // become "candidates". // for(i=0; i<=n-1; i++) { state.cstatus[i] = -1; } for(i=n; i<=n+nec+nic-1; i++) { if( state.cstatus[i]>0 ) { state.cstatus[i] = 0; } else { state.cstatus[i] = -1; } } nactiveconstraints = 0; nactivebnd = 0; nactivelin = 0; for(i=0; i<=n-1; i++) { // // Activate boundary constraints: // * copy constraint index to RCTmpConstraintIdx // * set corresponding element of CStatus[] to "candidate" // * fill RCTmpS by either +1 (lower bound) or -1 (upper bound) // * set RCTmpIsEquality to False (BndL=nec && state.cstatus[n+i]<0 ) { // // Inequality constraints are skipped if both (a) constraint was // not active, and (b) we are too far away from the boundary. // rowscale = 0.0; v = -state.cleic[i,n]; for(j=0; j<=n-1; j++) { v = v+state.cleic[i,j]*state.xc[j]; rowscale = Math.Max(rowscale, Math.Abs(state.cleic[i,j]*state.s[j])); } if( (double)(v)<=(double)(-(1.0E5*math.machineepsilon*rowscale)) ) { // // NOTE: it is important to check for non-strict inequality // because we have to correctly handle zero constraint // 0*x<=0 // continue; } } for(i_=0; i_<=n-1;i_++) { state.rctmpdense0[i_,nactivelin] = state.cleic[i,i_]; } state.rctmpconstraintidx[nactiveconstraints] = n+i; state.cstatus[n+i] = 0; state.rctmpisequality[nactiveconstraints] = i=(double)(0) ) { state.cstatus[i] = 1; continue; } if( (state.hasbndu[i] && (double)(state.xc[i])==(double)(state.bndu[i])) && (double)(gc[i])<=(double)(0) ) { state.cstatus[i] = 1; continue; } } return; } // // General case. // // APPROACH TO CONSTRAINTS ACTIVATION/DEACTIVATION // // We have NActiveConstraints "candidates": NActiveBnd boundary candidates, // NActiveLin linear candidates. Indexes of boundary constraints are stored // in RCTmpConstraintIdx[0:NActiveBnd-1], indexes of linear ones are stored // in RCTmpConstraintIdx[NActiveBnd:NActiveBnd+NActiveLin-1]. Some of the // constraints are equality ones, some are inequality - as specified by // RCTmpIsEquality[i]. // // Now we have to determine active subset of "candidates" set. In order to // do so we solve following constrained minimization problem: // ( )^2 // min ( SUM(lambda[i]*A[i]) + G ) // ( ) // Here: // * G is a gradient (column vector) // * A[i] is a column vector, linear (left) part of I-th constraint. // I=0..NActiveConstraints-1, first NActiveBnd elements of A are just // subset of identity matrix (boundary constraints), next NActiveLin // elements are subset of rows of the matrix of general linear constraints. // * lambda[i] is a Lagrange multiplier corresponding to I-th constraint // // NOTE: for preconditioned setting A is replaced by A*H^(-0.5), G is // replaced by G*H^(-0.5). We apply this scaling at the last stage, // before passing data to NNLS solver. // // Minimization is performed subject to non-negativity constraints on // lambda[i] corresponding to inequality constraints. Inequality constraints // which correspond to non-zero lambda are activated, equality constraints // are always considered active. // // Informally speaking, we "decompose" descent direction -G and represent // it as sum of constraint vectors and "residual" part (which is equal to // the actual descent direction subject to constraints). // // SOLUTION OF THE NNLS PROBLEM // // We solve this optimization problem with Non-Negative Least Squares solver, // which can efficiently solve least squares problems of the form // // ( [ I | AU ] )^2 // min ( [ | ]*x-b ) s.t. non-negativity constraints on some x[i] // ( [ 0 | AL ] ) // // In order to use this solver we have to rearrange rows of A[] and G in // such way that first NActiveBnd columns of A store identity matrix (before // sorting non-zero elements are randomly distributed in the first NActiveBnd // columns of A, during sorting we move them to first NActiveBnd rows). // // Then we create instance of NNLS solver (we reuse instance left from the // previous run of the optimization problem) and solve NNLS problem. // idx0 = 0; idx1 = nactivebnd; for(i=0; i<=n-1; i++) { if( state.cstatus[i]>=0 ) { v = 1/Math.Sqrt(h[i]); for(j=0; j<=nactivelin-1; j++) { state.rctmpdense1[idx0,j] = state.rctmpdense0[i,j]/state.rctmps[i]*v; } state.rctmprightpart[idx0] = state.rctmpg[i]/state.rctmps[i]*v; idx0 = idx0+1; } else { v = 1/Math.Sqrt(h[i]); for(j=0; j<=nactivelin-1; j++) { state.rctmpdense1[idx1,j] = state.rctmpdense0[i,j]*v; } state.rctmprightpart[idx1] = state.rctmpg[i]*v; idx1 = idx1+1; } } snnls.snnlsinit(n, Math.Min(nec+nic, n), n, state.solver, _params); snnls.snnlssetproblem(state.solver, state.rctmpdense1, state.rctmprightpart, nactivebnd, nactiveconstraints-nactivebnd, n, _params); for(i=0; i<=nactiveconstraints-1; i++) { if( state.rctmpisequality[i] ) { snnls.snnlsdropnnc(state.solver, i, _params); } } snnls.snnlssolve(state.solver, ref state.rctmplambdas, _params); // // After solution of the problem we activate equality constraints (always active) // and inequality constraints with non-zero Lagrange multipliers. Then we reorthogonalize // active constraints. // for(i=0; i<=n+nec+nic-1; i++) { state.cstatus[i] = -1; } for(i=0; i<=nactiveconstraints-1; i++) { if( state.rctmpisequality[i] || (double)(state.rctmplambdas[i])>(double)(0) ) { state.cstatus[state.rctmpconstraintidx[i]] = 1; } else { state.cstatus[state.rctmpconstraintidx[i]] = 0; } } sasrebuildbasis(state, _params); } } public class qqpsolver { /************************************************************************* This object stores settings for QQP solver. It must be initialized with QQPLoadDefaults(). After initialization you may change settings. *************************************************************************/ public class qqpsettings : apobject { public double epsg; public double epsf; public double epsx; public int maxouterits; public bool cgphase; public bool cnphase; public int cgminits; public int cgmaxits; public int cnmaxupdates; public int sparsesolver; public qqpsettings() { init(); } public override void init() { } public override alglib.apobject make_copy() { qqpsettings _result = new qqpsettings(); _result.epsg = epsg; _result.epsf = epsf; _result.epsx = epsx; _result.maxouterits = maxouterits; _result.cgphase = cgphase; _result.cnphase = cnphase; _result.cgminits = cgminits; _result.cgmaxits = cgmaxits; _result.cnmaxupdates = cnmaxupdates; _result.sparsesolver = sparsesolver; return _result; } }; /************************************************************************* This object stores temporaries used by QuickQP solver. *************************************************************************/ public class qqpbuffers : apobject { public int n; public int akind; public double[,] densea; public sparse.sparsematrix sparsea; public bool sparseupper; public double absamax; public double absasum; public double absasum2; public double[] b; public double[] bndl; public double[] bndu; public bool[] havebndl; public bool[] havebndu; public double[] xs; public double[] xf; public double[] gc; public double[] xp; public double[] dc; public double[] dp; public double[] cgc; public double[] cgp; public sactivesets.sactiveset sas; public bool[] activated; public int nfree; public int cnmodelage; public double[,] densez; public sparse.sparsematrix sparsecca; public int[] yidx; public double[] regdiag; public double[] regx0; public double[] tmpcn; public int[] tmpcni; public bool[] tmpcnb; public double[] tmp0; public double[] tmp1; public double[] stpbuf; public sparse.sparsebuffers sbuf; public int repinneriterationscount; public int repouteriterationscount; public int repncholesky; public int repncupdates; public qqpbuffers() { init(); } public override void init() { densea = new double[0,0]; sparsea = new sparse.sparsematrix(); b = new double[0]; bndl = new double[0]; bndu = new double[0]; havebndl = new bool[0]; havebndu = new bool[0]; xs = new double[0]; xf = new double[0]; gc = new double[0]; xp = new double[0]; dc = new double[0]; dp = new double[0]; cgc = new double[0]; cgp = new double[0]; sas = new sactivesets.sactiveset(); activated = new bool[0]; densez = new double[0,0]; sparsecca = new sparse.sparsematrix(); yidx = new int[0]; regdiag = new double[0]; regx0 = new double[0]; tmpcn = new double[0]; tmpcni = new int[0]; tmpcnb = new bool[0]; tmp0 = new double[0]; tmp1 = new double[0]; stpbuf = new double[0]; sbuf = new sparse.sparsebuffers(); } public override alglib.apobject make_copy() { qqpbuffers _result = new qqpbuffers(); _result.n = n; _result.akind = akind; _result.densea = (double[,])densea.Clone(); _result.sparsea = (sparse.sparsematrix)sparsea.make_copy(); _result.sparseupper = sparseupper; _result.absamax = absamax; _result.absasum = absasum; _result.absasum2 = absasum2; _result.b = (double[])b.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.havebndl = (bool[])havebndl.Clone(); _result.havebndu = (bool[])havebndu.Clone(); _result.xs = (double[])xs.Clone(); _result.xf = (double[])xf.Clone(); _result.gc = (double[])gc.Clone(); _result.xp = (double[])xp.Clone(); _result.dc = (double[])dc.Clone(); _result.dp = (double[])dp.Clone(); _result.cgc = (double[])cgc.Clone(); _result.cgp = (double[])cgp.Clone(); _result.sas = (sactivesets.sactiveset)sas.make_copy(); _result.activated = (bool[])activated.Clone(); _result.nfree = nfree; _result.cnmodelage = cnmodelage; _result.densez = (double[,])densez.Clone(); _result.sparsecca = (sparse.sparsematrix)sparsecca.make_copy(); _result.yidx = (int[])yidx.Clone(); _result.regdiag = (double[])regdiag.Clone(); _result.regx0 = (double[])regx0.Clone(); _result.tmpcn = (double[])tmpcn.Clone(); _result.tmpcni = (int[])tmpcni.Clone(); _result.tmpcnb = (bool[])tmpcnb.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmp1 = (double[])tmp1.Clone(); _result.stpbuf = (double[])stpbuf.Clone(); _result.sbuf = (sparse.sparsebuffers)sbuf.make_copy(); _result.repinneriterationscount = repinneriterationscount; _result.repouteriterationscount = repouteriterationscount; _result.repncholesky = repncholesky; _result.repncupdates = repncupdates; return _result; } }; public const int quickqprestartcg = 50; public const double regz = 1.0E-9; /************************************************************************* This function initializes QQPSettings structure with default settings. Newly created structure MUST be initialized by default settings - or by copy of the already initialized structure. -- ALGLIB -- Copyright 14.05.2011 by Bochkanov Sergey *************************************************************************/ public static void qqploaddefaults(int n, qqpsettings s, alglib.xparams _params) { s.epsg = 0.0; s.epsf = 0.0; s.epsx = 1.0E-6; s.maxouterits = 0; s.cgphase = true; s.cnphase = true; s.cgminits = 5; s.cgmaxits = Math.Max(s.cgminits, (int)Math.Round(1+0.33*n)); s.sparsesolver = 0; s.cnmaxupdates = (int)Math.Round(1+0.1*n); } /************************************************************************* This function initializes QQPSettings structure with copy of another, already initialized structure. -- ALGLIB -- Copyright 14.05.2011 by Bochkanov Sergey *************************************************************************/ public static void qqpcopysettings(qqpsettings src, qqpsettings dst, alglib.xparams _params) { dst.epsg = src.epsg; dst.epsf = src.epsf; dst.epsx = src.epsx; dst.maxouterits = src.maxouterits; dst.cgphase = src.cgphase; dst.cnphase = src.cnphase; dst.cgminits = src.cgminits; dst.cgmaxits = src.cgmaxits; dst.sparsesolver = src.sparsesolver; dst.cnmaxupdates = src.cnmaxupdates; } /************************************************************************* This function performs preallocation of internal 2D matrices. If matrix size is less than expected, we grow to some larger value (specified by user). It can be useful in cases when we solve many subsequent QP problems with increasing sizes - helps to avoid multiple allocations. INPUT PARAMETERS: SState - object which stores temporaries: * uninitialized object is automatically initialized * previously allocated memory is reused as much as possible NExpected - if internal buffers have size enough for NExpected, no preallocation happens. If size is less than NExpected, buffers are preallocated up to NGrowTo*NGrowTo NGrowTo - new size OUTPUT PARAMETERS: SState - temporary buffers, some of them are preallocated -- ALGLIB -- Copyright 09.10.2017 by Bochkanov Sergey *************************************************************************/ public static void qqppreallocategrowdense(qqpbuffers sstate, int nexpected, int ngrowto, alglib.xparams _params) { if( alglib.ap.rows(sstate.densea)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 - QQPSettings object initialized by one of the initialization functions. SState - object which stores temporaries: * uninitialized object is automatically initialized * previously allocated memory is reused as much as possible XS - initial point, array[NC] OUTPUT PARAMETERS: XS - last point TerminationType-termination type: * * * -- ALGLIB -- Copyright 14.05.2011 by Bochkanov Sergey *************************************************************************/ public static void qqpoptimize(cqmodels.convexquadraticmodel cqmac, sparse.sparsematrix sparseac, double[,] denseac, int akind, bool isupper, double[] bc, double[] bndlc, double[] bnduc, double[] sc, double[] xoriginc, int nc, qqpsettings settings, qqpbuffers sstate, double[] xs, ref int terminationtype, alglib.xparams _params) { int n = 0; int i = 0; int j = 0; int k = 0; double v = 0; double vv = 0; double d2 = 0; double d1 = 0; int d1est = 0; int d2est = 0; bool needact = new bool(); double reststp = 0; double fullstp = 0; double stpmax = 0; double stp = 0; int stpcnt = 0; int cidx = 0; double cval = 0; int cgcnt = 0; int cgmax = 0; int newtcnt = 0; int sparsesolver = 0; double beta = 0; bool b = new bool(); double fprev = 0; double fcur = 0; bool problemsolved = new bool(); bool isconstrained = new bool(); double f0 = 0; double f1 = 0; int i_ = 0; terminationtype = 0; // // Primary checks // alglib.ap.assert((akind==0 || akind==1) || akind==2, "QQPOptimize: incorrect AKind"); sstate.n = nc; n = sstate.n; terminationtype = 0; sstate.repinneriterationscount = 0; sstate.repouteriterationscount = 0; sstate.repncholesky = 0; sstate.repncupdates = 0; // // Several checks // * matrix size // * scale vector // * consistency of bound constraints // * consistency of settings // if( akind==1 ) { alglib.ap.assert(sparse.sparsegetnrows(sparseac, _params)==n, "QQPOptimize: rows(SparseAC)<>N"); alglib.ap.assert(sparse.sparsegetncols(sparseac, _params)==n, "QQPOptimize: cols(SparseAC)<>N"); } for(i=0; i<=n-1; i++) { alglib.ap.assert(math.isfinite(sc[i]) && (double)(sc[i])>(double)(0), "QQPOptimize: incorrect scale"); } for(i=0; i<=n-1; i++) { if( math.isfinite(bndlc[i]) && math.isfinite(bnduc[i]) ) { if( (double)(bndlc[i])>(double)(bnduc[i]) ) { terminationtype = -3; return; } } } alglib.ap.assert(settings.cgphase || settings.cnphase, "QQPOptimize: both phases (CG and Newton) are inactive"); // // Allocate data structures // apserv.rvectorsetlengthatleast(ref sstate.bndl, n, _params); apserv.rvectorsetlengthatleast(ref sstate.bndu, n, _params); apserv.bvectorsetlengthatleast(ref sstate.havebndl, n, _params); apserv.bvectorsetlengthatleast(ref sstate.havebndu, n, _params); apserv.rvectorsetlengthatleast(ref sstate.xs, n, _params); apserv.rvectorsetlengthatleast(ref sstate.xf, n, _params); apserv.rvectorsetlengthatleast(ref sstate.xp, n, _params); apserv.rvectorsetlengthatleast(ref sstate.gc, n, _params); apserv.rvectorsetlengthatleast(ref sstate.cgc, n, _params); apserv.rvectorsetlengthatleast(ref sstate.cgp, n, _params); apserv.rvectorsetlengthatleast(ref sstate.dc, n, _params); apserv.rvectorsetlengthatleast(ref sstate.dp, n, _params); apserv.rvectorsetlengthatleast(ref sstate.tmp0, n, _params); apserv.rvectorsetlengthatleast(ref sstate.tmp1, n, _params); apserv.rvectorsetlengthatleast(ref sstate.stpbuf, 15, _params); sactivesets.sasinit(n, sstate.sas, _params); // // Scale/shift problem coefficients: // // min { 0.5*(x-x0)'*A*(x-x0) + b'*(x-x0) } // // becomes (after transformation "x = S*y+x0") // // min { 0.5*y'*(S*A*S)*y + (S*b)'*y // // Modified A_mod=S*A*S and b_mod=S*(b+A*x0) are // stored into SState.DenseA and SState.B. // // apserv.rvectorsetlengthatleast(ref sstate.b, n, _params); for(i=0; i<=n-1; i++) { sstate.b[i] = sc[i]*bc[i]; } sstate.akind = -99; if( akind==0 ) { // // Dense QP problem - just copy and scale. // apserv.rmatrixsetlengthatleast(ref sstate.densea, n, n, _params); cqmodels.cqmgeta(cqmac, ref sstate.densea, _params); sstate.akind = 0; sstate.absamax = 0; sstate.absasum = 0; sstate.absasum2 = 0; for(i=0; i<=n-1; i++) { for(j=0; j<=n-1; j++) { v = sc[i]*sstate.densea[i,j]*sc[j]; vv = Math.Abs(v); sstate.densea[i,j] = v; sstate.absamax = Math.Max(sstate.absamax, vv); sstate.absasum = sstate.absasum+vv; sstate.absasum2 = sstate.absasum2+vv*vv; } } } if( akind==1 ) { // // Sparse QP problem - a bit tricky. Depending on format of the // input we use different strategies for copying matrix: // * SKS matrices are copied to SKS format // * anything else is copied to CRS format // sparse.sparsecopytosksbuf(sparseac, sstate.sparsea, _params); if( isupper ) { sparse.sparsetransposesks(sstate.sparsea, _params); } sstate.akind = 1; sstate.sparseupper = false; sstate.absamax = 0; sstate.absasum = 0; sstate.absasum2 = 0; for(i=0; i<=n-1; i++) { k = sstate.sparsea.ridx[i]; for(j=i-sstate.sparsea.didx[i]; j<=i; j++) { v = sc[i]*sstate.sparsea.vals[k]*sc[j]; vv = Math.Abs(v); sstate.sparsea.vals[k] = v; if( i==j ) { // // Diagonal terms are counted only once // sstate.absamax = Math.Max(sstate.absamax, vv); sstate.absasum = sstate.absasum+vv; sstate.absasum2 = sstate.absasum2+vv*vv; } else { // // Offdiagonal terms are counted twice // sstate.absamax = Math.Max(sstate.absamax, vv); sstate.absasum = sstate.absasum+2*vv; sstate.absasum2 = sstate.absasum2+2*vv*vv; } k = k+1; } } } if( akind==2 ) { // // Dense QP problem - just copy and scale. // apserv.rmatrixsetlengthatleast(ref sstate.densea, n, n, _params); sstate.akind = 0; sstate.absamax = 0; sstate.absasum = 0; sstate.absasum2 = 0; if( isupper ) { for(i=0; i<=n-1; i++) { for(j=i; j<=n-1; j++) { v = sc[i]*denseac[i,j]*sc[j]; vv = Math.Abs(v); sstate.densea[i,j] = v; sstate.densea[j,i] = v; if( (double)(i)==(double)(v) ) { k = 1; } else { k = 2; } sstate.absamax = Math.Max(sstate.absamax, vv); sstate.absasum = sstate.absasum+vv*k; sstate.absasum2 = sstate.absasum2+vv*vv*k; } } } else { for(i=0; i<=n-1; i++) { for(j=0; j<=i; j++) { v = sc[i]*denseac[i,j]*sc[j]; vv = Math.Abs(v); sstate.densea[i,j] = v; sstate.densea[j,i] = v; if( (double)(i)==(double)(v) ) { k = 1; } else { k = 2; } sstate.absamax = Math.Max(sstate.absamax, vv); sstate.absasum = sstate.absasum+vv*k; sstate.absasum2 = sstate.absasum2+vv*vv*k; } } } } alglib.ap.assert(sstate.akind>=0, "QQP: integrity check failed"); // // Load box constraints into State structure. // // We apply transformation to variables: y=(x-x_origin)/s, // each of the constraints is appropriately shifted/scaled. // for(i=0; i<=n-1; i++) { sstate.havebndl[i] = math.isfinite(bndlc[i]); if( sstate.havebndl[i] ) { sstate.bndl[i] = (bndlc[i]-xoriginc[i])/sc[i]; } else { alglib.ap.assert(Double.IsNegativeInfinity(bndlc[i]), "QQPOptimize: incorrect lower bound"); sstate.bndl[i] = Double.NegativeInfinity; } sstate.havebndu[i] = math.isfinite(bnduc[i]); if( sstate.havebndu[i] ) { sstate.bndu[i] = (bnduc[i]-xoriginc[i])/sc[i]; } else { alglib.ap.assert(Double.IsPositiveInfinity(bnduc[i]), "QQPOptimize: incorrect upper bound"); sstate.bndu[i] = Double.PositiveInfinity; } } // // Process initial point: // * set it to XS-XOriginC // * make sure that boundary constraints are preserved by transformation // for(i=0; i<=n-1; i++) { sstate.xs[i] = (xs[i]-xoriginc[i])/sc[i]; if( sstate.havebndl[i] && (double)(sstate.xs[i])<(double)(sstate.bndl[i]) ) { sstate.xs[i] = sstate.bndl[i]; } if( sstate.havebndu[i] && (double)(sstate.xs[i])>(double)(sstate.bndu[i]) ) { sstate.xs[i] = sstate.bndu[i]; } if( sstate.havebndl[i] && (double)(xs[i])==(double)(bndlc[i]) ) { sstate.xs[i] = sstate.bndl[i]; } if( sstate.havebndu[i] && (double)(xs[i])==(double)(bnduc[i]) ) { sstate.xs[i] = sstate.bndu[i]; } } // // Select sparse direct solver // if( akind==1 ) { sparsesolver = settings.sparsesolver; if( sparsesolver==0 ) { sparsesolver = 1; } if( sparse.sparseissks(sstate.sparsea, _params) ) { sparsesolver = 2; } sparsesolver = 2; alglib.ap.assert(sparsesolver==1 || sparsesolver==2, "QQPOptimize: incorrect SparseSolver"); } else { sparsesolver = 0; } // // For unconstrained problems - try to use fast approach which requires // just one unregularized Cholesky decomposition for solution. If it fails, // switch to general QQP code. // problemsolved = false; isconstrained = false; for(i=0; i<=n-1; i++) { isconstrained = (isconstrained || sstate.havebndl[i]) || sstate.havebndu[i]; } if( (!isconstrained && settings.cnphase) && akind==0 ) { apserv.rmatrixsetlengthatleast(ref sstate.densez, n, n, _params); apserv.rvectorsetlengthatleast(ref sstate.tmpcn, n, _params); for(i=0; i<=n-1; i++) { for(j=i; j<=n-1; j++) { sstate.densez[i,j] = sstate.densea[i,j]; } } apserv.inc(ref sstate.repncholesky, _params); if( trfac.spdmatrixcholeskyrec(ref sstate.densez, 0, n, true, ref sstate.tmpcn, _params) ) { for(i_=0; i_<=n-1;i_++) { sstate.xf[i_] = sstate.xs[i_]; } for(i=0; i<=n-1; i++) { sstate.dc[i] = 0; } f0 = projectedtargetfunction(sstate, sstate.xf, sstate.dc, 0.0, ref sstate.tmpcn, ref sstate.tmp1, _params); for(k=0; k<=3; k++) { ablas.rmatrixmv(n, n, sstate.densea, 0, 0, 0, sstate.xf, 0, sstate.gc, 0, _params); for(i_=0; i_<=n-1;i_++) { sstate.gc[i_] = sstate.gc[i_] + sstate.b[i_]; } for(i=0; i<=n-1; i++) { sstate.dc[i] = -sstate.gc[i]; } fbls.fblscholeskysolve(sstate.densez, 1.0, n, true, sstate.dc, ref sstate.tmpcn, _params); f1 = projectedtargetfunction(sstate, sstate.xf, sstate.dc, 1.0, ref sstate.tmpcn, ref sstate.tmp1, _params); if( (double)(f1)>=(double)(f0) ) { break; } for(i_=0; i_<=n-1;i_++) { sstate.xf[i_] = sstate.xf[i_] + sstate.dc[i_]; } f0 = f1; } terminationtype = 2; problemsolved = true; } } // // Attempt to solve problem with fast approach failed, use generic QQP // if( !problemsolved ) { // // Prepare "active set" structure // sactivesets.sassetbc(sstate.sas, sstate.bndl, sstate.bndu, _params); if( !sactivesets.sasstartoptimization(sstate.sas, sstate.xs, _params) ) { terminationtype = -3; return; } // // Main loop. // // Following variables are used: // * GC stores current gradient (unconstrained) // * CGC stores current gradient (constrained) // * DC stores current search direction // * CGP stores constrained gradient at previous point // (zero on initial entry) // * DP stores previous search direction // (zero on initial entry) // cgmax = settings.cgminits; sstate.repinneriterationscount = 0; sstate.repouteriterationscount = 0; while( true ) { if( settings.maxouterits>0 && sstate.repouteriterationscount>=settings.maxouterits ) { terminationtype = 5; break; } if( sstate.repouteriterationscount>0 ) { // // Check EpsF- and EpsX-based stopping criteria. // Because problem was already scaled, we do not scale step before checking its length. // NOTE: these checks are performed only after at least one outer iteration was made. // if( (double)(settings.epsf)>(double)(0) ) { // // NOTE 1: here we rely on the fact that ProjectedTargetFunction() ignore D when Stp=0 // NOTE 2: code below handles situation when update increases function value instead // of decreasing it. // fprev = projectedtargetfunction(sstate, sstate.xp, sstate.dc, 0.0, ref sstate.tmp0, ref sstate.tmp1, _params); fcur = projectedtargetfunction(sstate, sstate.sas.xc, sstate.dc, 0.0, ref sstate.tmp0, ref sstate.tmp1, _params); if( (double)(fprev-fcur)<=(double)(settings.epsf*Math.Max(Math.Abs(fprev), Math.Max(Math.Abs(fcur), 1.0))) ) { terminationtype = 1; break; } } if( (double)(settings.epsx)>(double)(0) ) { v = 0.0; for(i=0; i<=n-1; i++) { v = v+math.sqr(sstate.xp[i]-sstate.sas.xc[i]); } if( (double)(Math.Sqrt(v))<=(double)(settings.epsx) ) { terminationtype = 2; break; } } } apserv.inc(ref sstate.repouteriterationscount, _params); for(i_=0; i_<=n-1;i_++) { sstate.xp[i_] = sstate.sas.xc[i_]; } if( !settings.cgphase ) { cgmax = 0; } for(i=0; i<=n-1; i++) { sstate.cgp[i] = 0.0; sstate.dp[i] = 0.0; } for(cgcnt=0; cgcnt<=cgmax-1; cgcnt++) { // // Calculate unconstrained gradient GC for "extended" QP problem // Determine active set, current constrained gradient CGC. // Check gradient-based stopping condition. // // NOTE: because problem was scaled, we do not have to apply scaling // to gradient before checking stopping condition. // targetgradient(sstate, sstate.sas.xc, ref sstate.gc, _params); sactivesets.sasreactivateconstraints(sstate.sas, sstate.gc, _params); for(i_=0; i_<=n-1;i_++) { sstate.cgc[i_] = sstate.gc[i_]; } sactivesets.sasconstraineddirection(sstate.sas, ref sstate.cgc, _params); v = 0.0; for(i_=0; i_<=n-1;i_++) { v += sstate.cgc[i_]*sstate.cgc[i_]; } if( (double)(Math.Sqrt(v))<=(double)(settings.epsg) ) { terminationtype = 4; break; } // // Prepare search direction DC and explore it. // // We try to use CGP/DP to prepare conjugate gradient step, // but we resort to steepest descent step (Beta=0) in case // we are at I-th boundary, but DP[I]<>0. // // Such approach allows us to ALWAYS have feasible DC, with // guaranteed compatibility with both feasible area and current // active set. // // Automatic CG reset performed every time DP is incompatible // with current active set and/or feasible area. We also // perform reset every QuickQPRestartCG iterations. // for(i_=0; i_<=n-1;i_++) { sstate.dc[i_] = -sstate.cgc[i_]; } v = 0.0; vv = 0.0; b = false; for(i=0; i<=n-1; i++) { v = v+sstate.cgc[i]*sstate.cgc[i]; vv = vv+sstate.cgp[i]*sstate.cgp[i]; b = b || ((sstate.havebndl[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndl[i])) && (double)(sstate.dp[i])!=(double)(0)); b = b || ((sstate.havebndu[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndu[i])) && (double)(sstate.dp[i])!=(double)(0)); } b = b || (double)(vv)==(double)(0); b = b || cgcnt%quickqprestartcg==0; if( !b ) { beta = v/vv; } else { beta = 0.0; } for(i_=0; i_<=n-1;i_++) { sstate.dc[i_] = sstate.dc[i_] + beta*sstate.dp[i_]; } sactivesets.sasconstraineddirection(sstate.sas, ref sstate.dc, _params); sactivesets.sasexploredirection(sstate.sas, sstate.dc, ref stpmax, ref cidx, ref cval, _params); // // Build quadratic model of F along descent direction: // // F(xc+alpha*D) = D2*alpha^2 + D1*alpha // // Terminate algorithm if needed. // // NOTE: we do not maintain constant term D0 // quadraticmodel(sstate, sstate.sas.xc, sstate.dc, sstate.gc, ref d1, ref d1est, ref d2, ref d2est, sstate.tmp0, _params); if( (double)(d1)==(double)(0) && (double)(d2)==(double)(0) ) { // // D1 and D2 are exactly zero, success. // After this if-then we assume that D is non-zero. // terminationtype = 4; break; } if( d1est>=0 ) { // // Numerical noise is too large, it means that we are close // to minimum - and that further improvement is impossible. // // After this if-then we assume that D1 is definitely negative // (even under presence of numerical errors). // terminationtype = 7; break; } if( d2est<=0 && cidx<0 ) { // // Function is unbounded from below: // * D1<0 (verified by previous block) // * D2Est<=0, which means that either D2<0 - or it can not // be reliably distinguished from zero. // * step is unconstrained // // If these conditions are true, we abnormally terminate QP // algorithm with return code -4 // terminationtype = -4; break; } // // Perform step along DC. // // In this block of code we maintain two step length: // * RestStp - restricted step, maximum step length along DC which does // not violate constraints // * FullStp - step length along DC which minimizes quadratic function // without taking constraints into account. If problem is // unbounded from below without constraints, FullStp is // forced to be RestStp. // // So, if function is convex (D2>0): // * FullStp = -D1/(2*D2) // * RestStp = restricted FullStp // * 0<=RestStp<=FullStp // // If function is non-convex, but bounded from below under constraints: // * RestStp = step length subject to constraints // * FullStp = RestStp // // After RestStp and FullStp are initialized, we generate several trial // steps which are different multiples of RestStp and FullStp. // if( d2est>0 ) { alglib.ap.assert((double)(d1)<(double)(0), "QQPOptimize: internal error"); fullstp = -(d1/(2*d2)); needact = (double)(fullstp)>=(double)(stpmax); if( needact ) { alglib.ap.assert(alglib.ap.len(sstate.stpbuf)>=3, "QQPOptimize: StpBuf overflow"); reststp = stpmax; stp = reststp; sstate.stpbuf[0] = reststp*4; sstate.stpbuf[1] = fullstp; sstate.stpbuf[2] = fullstp/4; stpcnt = 3; } else { reststp = fullstp; stp = fullstp; stpcnt = 0; } } else { alglib.ap.assert(cidx>=0, "QQPOptimize: internal error"); alglib.ap.assert(alglib.ap.len(sstate.stpbuf)>=2, "QQPOptimize: StpBuf overflow"); reststp = stpmax; fullstp = stpmax; stp = reststp; needact = true; sstate.stpbuf[0] = 4*reststp; stpcnt = 1; } findbeststepandmove(sstate, sstate.sas, sstate.dc, stp, needact, cidx, cval, sstate.stpbuf, stpcnt, ref sstate.activated, ref sstate.tmp0, ref sstate.tmp1, _params); // // Update CG information. // for(i_=0; i_<=n-1;i_++) { sstate.dp[i_] = sstate.dc[i_]; } for(i_=0; i_<=n-1;i_++) { sstate.cgp[i_] = sstate.cgc[i_]; } // // Update iterations counter // sstate.repinneriterationscount = sstate.repinneriterationscount+1; } if( terminationtype!=0 ) { break; } cgmax = settings.cgmaxits; // // Generate YIdx - reordering of variables for constrained Newton phase. // Free variables come first, fixed are last ones. // newtcnt = 0; while( true ) { // // Skip iteration if constrained Newton is turned off. // if( !settings.cnphase ) { break; } // // At the first iteration - build Cholesky decomposition of Hessian. // At subsequent iterations - refine Hessian by adding new constraints. // // Loop is terminated in following cases: // * Hessian is not positive definite subject to current constraints // (termination during initial decomposition) // * there were no new constraints being activated // (termination during update) // * all constraints were activated during last step // (termination during update) // * CNMaxUpdates were performed on matrix // (termination during update) // if( newtcnt==0 ) { // // Perform initial Newton step. If Cholesky decomposition fails, // increase number of CG iterations to CGMaxIts - it should help // us to find set of constraints which will make matrix positive // definite. // b = cnewtonbuild(sstate, sparsesolver, ref sstate.repncholesky, _params); if( b ) { cgmax = settings.cgminits; } } else { b = cnewtonupdate(sstate, settings, ref sstate.repncupdates, _params); } if( !b ) { break; } apserv.inc(ref newtcnt, _params); // // Calculate gradient GC. // targetgradient(sstate, sstate.sas.xc, ref sstate.gc, _params); // // Bound-constrained Newton step // for(i=0; i<=n-1; i++) { sstate.dc[i] = sstate.gc[i]; } if( !cnewtonstep(sstate, settings, sstate.dc, _params) ) { break; } quadraticmodel(sstate, sstate.sas.xc, sstate.dc, sstate.gc, ref d1, ref d1est, ref d2, ref d2est, sstate.tmp0, _params); if( d1est>=0 ) { // // We are close to minimum, derivative is nearly zero, break Newton iteration // break; } if( d2est>0 ) { // // Positive definite matrix, we can perform Newton step // alglib.ap.assert((double)(d1)<(double)(0), "QQPOptimize: internal error"); fullstp = -(d1/(2*d2)); sactivesets.sasexploredirection(sstate.sas, sstate.dc, ref stpmax, ref cidx, ref cval, _params); needact = (double)(fullstp)>=(double)(stpmax); if( needact ) { alglib.ap.assert(alglib.ap.len(sstate.stpbuf)>=3, "QQPOptimize: StpBuf overflow"); reststp = stpmax; stp = reststp; sstate.stpbuf[0] = reststp*4; sstate.stpbuf[1] = fullstp; sstate.stpbuf[2] = fullstp/4; stpcnt = 3; } else { reststp = fullstp; stp = fullstp; stpcnt = 0; } findbeststepandmove(sstate, sstate.sas, sstate.dc, stp, needact, cidx, cval, sstate.stpbuf, stpcnt, ref sstate.activated, ref sstate.tmp0, ref sstate.tmp1, _params); } else { // // Matrix is semi-definite or indefinite, but regularized // Cholesky succeeded and gave us descent direction in DC. // // We will investigate it and try to perform descent step: // * first, we explore direction: // * if it is unbounded, we stop algorithm with // appropriate termination code -4. // * if StpMax=0, we break Newton phase and return to // CG phase - constraint geometry is complicated near // current point, so it is better to use simpler algo. // * second, we check that bounded step decreases function; // if not, we again skip to CG phase // * finally, we use FindBestStep...() function to choose // between bounded step and projection of full-length step // (latter may give additional decrease in // sactivesets.sasexploredirection(sstate.sas, sstate.dc, ref stpmax, ref cidx, ref cval, _params); if( cidx<0 ) { // // Function is unbounded from below: // * D1<0 (verified by previous block) // * D2Est<=0, which means that either D2<0 - or it can not // be reliably distinguished from zero. // * step is unconstrained // // If these conditions are true, we abnormally terminate QP // algorithm with return code -4 // terminationtype = -4; break; } if( (double)(stpmax)==(double)(0) ) { // // Resort to CG phase. // Increase number of CG iterations. // cgmax = settings.cgmaxits; break; } alglib.ap.assert((double)(stpmax)>(double)(0), "QQPOptimize: internal error"); f0 = projectedtargetfunction(sstate, sstate.sas.xc, sstate.dc, 0.0, ref sstate.tmp0, ref sstate.tmp1, _params); f1 = projectedtargetfunction(sstate, sstate.sas.xc, sstate.dc, stpmax, ref sstate.tmp0, ref sstate.tmp1, _params); if( (double)(f1)>=(double)(f0) ) { // // Descent direction does not actually decrease function value. // Resort to CG phase // Increase number of CG iterations. // cgmax = settings.cgmaxits; break; } alglib.ap.assert(alglib.ap.len(sstate.stpbuf)>=3, "QQPOptimize: StpBuf overflow"); reststp = stpmax; stp = reststp; sstate.stpbuf[0] = reststp*4; sstate.stpbuf[1] = 1.00; sstate.stpbuf[2] = 0.25; stpcnt = 3; findbeststepandmove(sstate, sstate.sas, sstate.dc, stp, true, cidx, cval, sstate.stpbuf, stpcnt, ref sstate.activated, ref sstate.tmp0, ref sstate.tmp1, _params); } } if( terminationtype!=0 ) { break; } } sactivesets.sasstopoptimization(sstate.sas, _params); for(i_=0; i_<=n-1;i_++) { sstate.xf[i_] = sstate.sas.xc[i_]; } } // // Stop optimization and unpack results. // // Add XOriginC to XS and make sure that boundary constraints are // both (a) satisfied, (b) preserved. Former means that "shifted" // point is feasible, while latter means that point which was exactly // at the boundary before shift will be exactly at the boundary // after shift. // for(i=0; i<=n-1; i++) { xs[i] = sc[i]*sstate.xf[i]+xoriginc[i]; if( sstate.havebndl[i] && (double)(xs[i])<(double)(bndlc[i]) ) { xs[i] = bndlc[i]; } if( sstate.havebndu[i] && (double)(xs[i])>(double)(bnduc[i]) ) { xs[i] = bnduc[i]; } if( sstate.havebndl[i] && (double)(sstate.xf[i])==(double)(sstate.bndl[i]) ) { xs[i] = bndlc[i]; } if( sstate.havebndu[i] && (double)(sstate.xf[i])==(double)(sstate.bndu[i]) ) { xs[i] = bnduc[i]; } } } /************************************************************************* Target function at point PROJ(X+Stp*D), where PROJ(.) is a projection into feasible set. NOTE: if Stp=0, D is not referenced at all. Thus, there is no need to fill it by some meaningful values for Stp=0. This subroutine uses temporary buffers Tmp0/1, which are automatically resized if needed. -- ALGLIB -- Copyright 21.12.2013 by Bochkanov Sergey *************************************************************************/ private static double projectedtargetfunction(qqpbuffers sstate, double[] x, double[] d, double stp, ref double[] tmp0, ref double[] tmp1, alglib.xparams _params) { double result = 0; int n = 0; int i = 0; double v = 0; n = sstate.n; apserv.rvectorsetlengthatleast(ref tmp0, n, _params); apserv.rvectorsetlengthatleast(ref tmp1, n, _params); // // Calculate projected point // for(i=0; i<=n-1; i++) { if( (double)(stp)!=(double)(0) ) { v = x[i]+stp*d[i]; } else { v = x[i]; } if( sstate.havebndl[i] && (double)(v)<(double)(sstate.bndl[i]) ) { v = sstate.bndl[i]; } if( sstate.havebndu[i] && (double)(v)>(double)(sstate.bndu[i]) ) { v = sstate.bndu[i]; } tmp0[i] = v; } // // Function value at the Tmp0: // // f(x) = 0.5*x'*A*x + b'*x // result = 0.0; for(i=0; i<=n-1; i++) { result = result+sstate.b[i]*tmp0[i]; } if( sstate.akind==0 ) { // // Dense matrix A // result = result+0.5*ablas.rmatrixsyvmv(n, sstate.densea, 0, 0, true, tmp0, 0, tmp1, _params); } else { // // sparse matrix A // alglib.ap.assert(sstate.akind==1, "QQPOptimize: unexpected AKind in ProjectedTargetFunction"); result = result+0.5*sparse.sparsevsmv(sstate.sparsea, sstate.sparseupper, tmp0, _params); } return result; } /************************************************************************* Gradient of the target function: f(x) = 0.5*x'*A*x + b'*x which is equal to grad = A*x + b Here: * x is array[N] * A is array[N,N] * b is array[N] INPUT PARAMETERS: SState - structure which stores function terms (not modified) X - location G - possibly preallocated buffer OUTPUT PARAMETERS: G - array[N], gradient -- ALGLIB -- Copyright 21.12.2013 by Bochkanov Sergey *************************************************************************/ private static void targetgradient(qqpbuffers sstate, double[] x, ref double[] g, alglib.xparams _params) { int n = 0; int i_ = 0; n = sstate.n; apserv.rvectorsetlengthatleast(ref g, n, _params); if( sstate.akind==0 ) { // // Dense matrix A // ablas.rmatrixsymv(n, 1.0, sstate.densea, 0, 0, true, x, 0, 0.0, g, 0, _params); } else { // // Sparse matrix A // alglib.ap.assert(sstate.akind==1, "QQPOptimize: unexpected AKind in TargetGradient"); sparse.sparsesmv(sstate.sparsea, sstate.sparseupper, x, ref g, _params); } for(i_=0; i_<=n-1;i_++) { g[i_] = g[i_] + sstate.b[i_]; } } /************************************************************************* First and second derivatives of the "extended" target function along specified direction. Target function is called "extended" because of additional slack variables and has form: f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b) with gradient grad = A*x + b + penaltyfactor*C'*(C*x-b) Quadratic model has form F(x0+alpha*D) = D2*alpha^2 + D1*alpha INPUT PARAMETERS: SState - structure which is used to obtain quadratic term of the model X - current point, array[N] D - direction across which derivatives are calculated, array[N] G - gradient at current point (pre-calculated by caller), array[N] OUTPUT PARAMETERS: D1 - linear coefficient D1Est - estimate of D1 sign, accounting for possible numerical errors: * >0 means "almost surely positive" * <0 means "almost surely negative" * =0 means "pessimistic estimate of numerical errors in D1 is larger than magnitude of D1 itself; it is impossible to reliably distinguish D1 from zero". D2 - quadratic coefficient D2Est - estimate of D2 sign, accounting for possible numerical errors: * >0 means "almost surely positive" * <0 means "almost surely negative" * =0 means "pessimistic estimate of numerical errors in D2 is larger than magnitude of D2 itself; it is impossible to reliably distinguish D2 from zero". -- ALGLIB -- Copyright 14.05.2014 by Bochkanov Sergey *************************************************************************/ private static void quadraticmodel(qqpbuffers sstate, double[] x, double[] d, double[] g, ref double d1, ref int d1est, ref double d2, ref int d2est, double[] tmp0, alglib.xparams _params) { int n = 0; int i = 0; double v = 0; double mx = 0; double mb = 0; double md = 0; int i_ = 0; d1 = 0; d1est = 0; d2 = 0; d2est = 0; n = sstate.n; // // Maximums // mx = 0.0; md = 0.0; mb = 0.0; for(i=0; i<=n-1; i++) { mx = Math.Max(mx, Math.Abs(x[i])); md = Math.Max(md, Math.Abs(d[i])); } for(i=0; i<=n-1; i++) { mb = Math.Max(mb, Math.Abs(sstate.b[i])); } // // D2 // if( sstate.akind==0 ) { // // Dense matrix A // d2 = 0.5*ablas.rmatrixsyvmv(n, sstate.densea, 0, 0, true, d, 0, tmp0, _params); } else { // // Sparse matrix A // alglib.ap.assert(sstate.akind==1, "QQPOptimize: unexpected AKind in TargetGradient"); d2 = 0.5*sparse.sparsevsmv(sstate.sparsea, sstate.sparseupper, d, _params); } v = 0.0; for(i_=0; i_<=n-1;i_++) { v += d[i_]*g[i_]; } d1 = v; // // Error estimates // optserv.estimateparabolicmodel(sstate.absasum, sstate.absasum2, mx, mb, md, d1, d2, ref d1est, ref d2est, _params); } /************************************************************************* This function accepts quadratic model of the form f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b) and list of possible steps along direction D. It chooses best step (one which achieves minimum value of the target function) and moves current point (given by SAS object) to the new location. Step is bounded subject to boundary constraints. Candidate steps are divided into two groups: * "default" step, which is always performed when no candidate steps LONGER THAN THE DEFAULT ONE is given. This candidate MUST reduce target function value; it is responsibility of caller to provide default candidate which reduces target function. * "additional candidates", which may be shorter or longer than the default step. Candidates which are shorter that the default step are ignored; candidates which are longer than the "default" step are tested. The idea is that we ALWAYS try "default" step, and it is responsibility of the caller to provide us with something which is worth trying. This step may activate some constraint - that's why we stopped at "default" step size. However, we may also try longer steps which may activate additional constraints and further reduce function value. INPUT PARAMETERS: SState - structure which stores model SAS - active set structure which stores current point in SAS.XC D - direction for step Stp - step length for "default" candidate NeedAct - whether default candidate activates some constraint; if NeedAct is True, constraint given by CIdc/CVal is GUARANTEED to be activated in the final point. CIdx - if NeedAct is True, stores index of the constraint to activate CVal - if NeedAct is True, stores constrained value; SAS.XC[CIdx] is forced to be equal to CVal. AddSteps- array[AddStepsCnt] of additional steps: * AddSteps[]<=Stp are ignored * AddSteps[]>Stp are tried Activated- possibly preallocated buffer; previously allocated memory will be reused. Tmp0/1 - possibly preallocated buffers; previously allocated memory will be reused. OUTPUT PARAMETERS: SAS - SAS.XC is set to new point; if there was a constraint specified by NeedAct/CIdx/CVal, it will be activated (other constraints may be activated too, but this one is guaranteed to be active in the final point). Activated- elements of this array are set to True, if I-th constraint as inactive at previous point, but become active in the new one. Situations when we deactivate xi>=0 and activate xi<=1 are considered as activation of previously inactive constraint -- ALGLIB -- Copyright 14.05.2014 by Bochkanov Sergey *************************************************************************/ private static void findbeststepandmove(qqpbuffers sstate, sactivesets.sactiveset sas, double[] d, double stp, bool needact, int cidx, double cval, double[] addsteps, int addstepscnt, ref bool[] activated, ref double[] tmp0, ref double[] tmp1, alglib.xparams _params) { int n = 0; int i = 0; int k = 0; double v = 0; double stpbest = 0; double fbest = 0; double fcand = 0; n = sstate.n; apserv.rvectorsetlengthatleast(ref tmp0, n, _params); apserv.bvectorsetlengthatleast(ref activated, n, _params); // // Calculate initial step, store to Tmp0 // // NOTE: Tmp0 is guaranteed to be feasible w.r.t. boundary constraints // for(i=0; i<=n-1; i++) { v = sas.xc[i]+stp*d[i]; if( sstate.havebndl[i] && (double)(v)<(double)(sstate.bndl[i]) ) { v = sstate.bndl[i]; } if( sstate.havebndu[i] && (double)(v)>(double)(sstate.bndu[i]) ) { v = sstate.bndu[i]; } tmp0[i] = v; } if( needact ) { tmp0[cidx] = cval; } // // Try additional steps, if AddStepsCnt>0 // if( addstepscnt>0 ) { // // Find best step // stpbest = stp; fbest = projectedtargetfunction(sstate, sas.xc, d, stpbest, ref tmp0, ref tmp1, _params); for(k=0; k<=addstepscnt-1; k++) { if( (double)(addsteps[k])>(double)(stp) ) { fcand = projectedtargetfunction(sstate, sas.xc, d, addsteps[k], ref tmp0, ref tmp1, _params); if( (double)(fcand)<(double)(fbest) ) { fbest = fcand; stpbest = addsteps[k]; } } } // // Prepare best step // // NOTE: because only AddSteps[]>Stp were checked, // this step will activate constraint CIdx. // for(i=0; i<=n-1; i++) { v = sas.xc[i]+stpbest*d[i]; if( sstate.havebndl[i] && (double)(v)<(double)(sstate.bndl[i]) ) { v = sstate.bndl[i]; } if( sstate.havebndu[i] && (double)(v)>(double)(sstate.bndu[i]) ) { v = sstate.bndu[i]; } tmp0[i] = v; } if( needact ) { tmp0[cidx] = cval; } } // // Fill Activated array by information about activated constraints. // Perform step // for(i=0; i<=n-1; i++) { activated[i] = false; v = tmp0[i]; if( (double)(v)==(double)(sas.xc[i]) ) { continue; } if( sstate.havebndl[i] && (double)(v)==(double)(sstate.bndl[i]) ) { activated[i] = true; } if( sstate.havebndu[i] && (double)(v)==(double)(sstate.bndu[i]) ) { activated[i] = true; } } sactivesets.sasmoveto(sas, tmp0, needact, cidx, cval, _params); } /************************************************************************* This function prepares data for constrained Newton step for penalized quadratic model of the form f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b) where A can be dense or sparse, and model is considered subject to equality constraints specified by SState.SAS.XC object. Constraint is considered active if XC[i] is exactly BndL[i] or BndU[i], i.e. we ignore internal list of constraints monitored by SAS object. Our own set of constraints includes all constraints stored by SAS, but also may include some constraints which are inactive in SAS. "Preparation" means that Cholesky decomposition of the effective system matrix is performed, and we can perform constrained Newton step. This function works as black box. It uses fields of SState which are marked as "Variables for constrained Newton phase", and only this function and its friends know about these variables. Everyone else should use: * CNewtonBuild() to prepare initial Cholesky decomposition for step * CNewtonStep() to perform constrained Newton step * CNewtonUpdate() to update Cholesky matrix after point was moved and constraints were updated. In some cases it is possible to efficiently re-calculate Cholesky decomposition if you know which constraints were activated. If efficient re-calculation is impossible, this function returns False. INPUT PARAMETERS: SState - structure which stores model and temporaries for CN phase; in particular, SAS.XC stores current point. SparseSolver-which sparse solver to use for sparse model; ignored for dense QP. Can be: * 2 - SKS-based Cholesky NCholesky- counter which is incremented after Cholesky (successful or failed one) OUTPUT PARAMETERS: NCholesky- possibly updated counter RESULT: True, if Cholesky decomposition was successfully performed. False, if a) matrix was semi-definite or indefinite, or b) particular combination of matrix type (sparse) and constraints (general linear) is not supported. NOTE: this function may routinely return False, for indefinite matrices or for sparse problems with general linear constraints. You should be able to handle such situations. -- ALGLIB -- Copyright 14.05.2014 by Bochkanov Sergey *************************************************************************/ private static bool cnewtonbuild(qqpbuffers sstate, int sparsesolver, ref int ncholesky, alglib.xparams _params) { bool result = new bool(); int n = 0; int i = 0; int j = 0; int k = 0; double v = 0; bool b = new bool(); int ridx0 = 0; int ridx1 = 0; int nfree = 0; int i_ = 0; result = false; // // Fetch often used fields // n = sstate.n; // // 1. Set CNModelAge to zero // 2. Generate YIdx - reordering of variables such that free variables // come first and are ordered by ascending, fixed are last ones and // have no particular ordering. // // This step is same for dense and sparse problems. // sstate.cnmodelage = 0; apserv.ivectorsetlengthatleast(ref sstate.yidx, n, _params); ridx0 = 0; ridx1 = n-1; for(i=0; i<=n-1; i++) { sstate.yidx[i] = -1; } for(i=0; i<=n-1; i++) { alglib.ap.assert(!sstate.havebndl[i] || (double)(sstate.sas.xc[i])>=(double)(sstate.bndl[i]), "CNewtonBuild: internal error"); alglib.ap.assert(!sstate.havebndu[i] || (double)(sstate.sas.xc[i])<=(double)(sstate.bndu[i]), "CNewtonBuild: internal error"); b = false; b = b || (sstate.havebndl[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndl[i])); b = b || (sstate.havebndu[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndu[i])); if( b ) { sstate.yidx[ridx1] = i; ridx1 = ridx1-1; } else { sstate.yidx[ridx0] = i; ridx0 = ridx0+1; } } alglib.ap.assert(ridx0==ridx1+1, "CNewtonBuild: internal error"); nfree = ridx0; sstate.nfree = nfree; if( nfree==0 ) { return result; } // // Constrained Newton matrix: dense version // if( sstate.akind==0 ) { apserv.rmatrixsetlengthatleast(ref sstate.densez, n, n, _params); apserv.rvectorsetlengthatleast(ref sstate.tmpcn, n, _params); for(i=0; i<=n-1; i++) { for(j=i; j<=n-1; j++) { sstate.densez[i,j] = sstate.densea[i,j]; } } for(i=1; i<=nfree-1; i++) { alglib.ap.assert(sstate.yidx[i]>sstate.yidx[i-1], "CNewtonBuild: integrity check failed"); } for(i=0; i<=nfree-1; i++) { k = sstate.yidx[i]; for(j=i; j<=nfree-1; j++) { sstate.densez[i,j] = sstate.densez[k,sstate.yidx[j]]; } } apserv.rvectorsetlengthatleast(ref sstate.regdiag, n, _params); for(i=0; i<=nfree-1; i++) { v = 0.0; for(j=0; j<=i-1; j++) { v = v+Math.Abs(sstate.densez[j,i]); } for(j=i; j<=nfree-1; j++) { v = v+Math.Abs(sstate.densez[i,j]); } if( (double)(v)==(double)(0) ) { v = 1.0; } sstate.regdiag[i] = regz*v; } for(i=0; i<=nfree-1; i++) { sstate.densez[i,i] = sstate.densez[i,i]+sstate.regdiag[i]; } apserv.inc(ref ncholesky, _params); if( !trfac.spdmatrixcholeskyrec(ref sstate.densez, 0, nfree, true, ref sstate.tmpcn, _params) ) { return result; } for(i=nfree-1; i>=0; i--) { for(i_=i; i_<=nfree-1;i_++) { sstate.tmpcn[i_] = sstate.densez[i,i_]; } k = sstate.yidx[i]; for(j=k; j<=n-1; j++) { sstate.densez[k,j] = 0; } for(j=i; j<=nfree-1; j++) { sstate.densez[k,sstate.yidx[j]] = sstate.tmpcn[j]; } } for(i=nfree; i<=n-1; i++) { k = sstate.yidx[i]; sstate.densez[k,k] = 1.0; for(j=k+1; j<=n-1; j++) { sstate.densez[k,j] = 0; } } result = true; return result; } // // Constrained Newton matrix: sparse version // if( sstate.akind==1 ) { alglib.ap.assert(sparsesolver==2, "CNewtonBuild: internal error"); // // Copy sparse A to Z and fill rows/columns corresponding to active // constraints by zeros. Diagonal elements corresponding to active // constraints are filled by unit values. // sparse.sparsecopytosksbuf(sstate.sparsea, sstate.sparsecca, _params); apserv.rvectorsetlengthatleast(ref sstate.tmpcn, n, _params); for(i=0; i<=n-1; i++) { sstate.tmpcn[i] = 0; } for(i=nfree; i<=n-1; i++) { sstate.tmpcn[sstate.yidx[i]] = 1; } for(i=0; i<=n-1; i++) { k = sstate.sparsecca.ridx[i]; for(j=i-sstate.sparsecca.didx[i]; j<=i; j++) { if( (double)(sstate.tmpcn[i])!=(double)(0) || (double)(sstate.tmpcn[j])!=(double)(0) ) { // // I-th or J-th variable is in active set (constrained) // if( i==j ) { sstate.sparsecca.vals[k] = 1.0; } else { sstate.sparsecca.vals[k] = 0.0; } } k = k+1; } } // // Perform sparse Cholesky // apserv.inc(ref ncholesky, _params); if( !trfac.sparsecholeskyskyline(sstate.sparsecca, n, sstate.sparseupper, _params) ) { return result; } result = true; return result; } // // Unexpected :) // alglib.ap.assert(false, "CNewtonBuild: internal error"); return result; } /************************************************************************* This function updates equality-constrained Cholesky matrix after activation of the new equality constraints. Matrix being updated is quadratic term of the function below f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b) where A can be dense or sparse. This function uses YIdx[] array (set by CNewtonBuild() function) to distinguish between active and inactive constraints. This function works as black box. It uses fields of SState which are marked as "Variables for constrained Newton phase", and only this function and its friends know about these variables. Everyone else should use: * CNewtonBuild() to prepare initial Cholesky decomposition for step * CNewtonStep() to perform constrained Newton step * CNewtonUpdate() to update Cholesky matrix after point was moved and constraints were updated. In some cases it is possible to efficiently re-calculate Cholesky decomposition if you know which constraints were activated. If efficient re-calculation is impossible, this function returns False. INPUT PARAMETERS: SState - structure which stores model and temporaries for CN phase; in particular, SAS.XC stores current point. Settings - QQPSettings object which was initialized by appropriate construction function. NCUpdates- counter which is incremented after each update (one update means one variable being fixed) OUTPUT PARAMETERS: NCUpdates- possibly updated counter RESULT: True, if Cholesky decomposition was successfully performed. False, if a) model age was too high, or b) particular combination of matrix type (sparse) and constraints (general linear) is not supported NOTE: this function may routinely return False. You should be able to handle such situations. -- ALGLIB -- Copyright 14.05.2014 by Bochkanov Sergey *************************************************************************/ private static bool cnewtonupdate(qqpbuffers sstate, qqpsettings settings, ref int ncupdates, alglib.xparams _params) { bool result = new bool(); int n = 0; int nfree = 0; int ntofix = 0; bool b = new bool(); int ridx0 = 0; int ridx1 = 0; int i = 0; int k = 0; result = false; // // Cholesky updates for sparse problems are not supported // if( sstate.akind==1 ) { return result; } // // Fetch often used fields // n = sstate.n; nfree = sstate.nfree; // // Determine variables to fix and move them to YIdx[NFree-NToFix:NFree-1] // Exit if CNModelAge increased too much. // apserv.ivectorsetlengthatleast(ref sstate.tmpcni, n, _params); ridx0 = 0; ridx1 = nfree-1; for(i=0; i<=nfree-1; i++) { sstate.tmpcni[i] = -1; } for(k=0; k<=nfree-1; k++) { i = sstate.yidx[k]; alglib.ap.assert(!sstate.havebndl[i] || (double)(sstate.sas.xc[i])>=(double)(sstate.bndl[i]), "CNewtonUpdate: internal error"); alglib.ap.assert(!sstate.havebndu[i] || (double)(sstate.sas.xc[i])<=(double)(sstate.bndu[i]), "CNewtonUpdate: internal error"); b = false; b = b || (sstate.havebndl[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndl[i])); b = b || (sstate.havebndu[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndu[i])); if( b ) { sstate.tmpcni[ridx1] = i; ridx1 = ridx1-1; } else { sstate.tmpcni[ridx0] = i; ridx0 = ridx0+1; } } alglib.ap.assert(ridx0==ridx1+1, "CNewtonUpdate: internal error"); ntofix = nfree-ridx0; if( ntofix==0 || ntofix==nfree ) { return result; } if( sstate.cnmodelage+ntofix>settings.cnmaxupdates ) { return result; } for(i=0; i<=nfree-1; i++) { sstate.yidx[i] = sstate.tmpcni[i]; } // // Constrained Newton matrix: dense version. // if( sstate.akind==0 ) { // // Update Cholesky matrix with SPDMatrixCholeskyUpdateFixBuf() // apserv.bvectorsetlengthatleast(ref sstate.tmpcnb, n, _params); for(i=0; i<=n-1; i++) { sstate.tmpcnb[i] = false; } for(i=nfree-ntofix; i<=nfree-1; i++) { sstate.tmpcnb[sstate.yidx[i]] = true; } trfac.spdmatrixcholeskyupdatefixbuf(sstate.densez, n, true, sstate.tmpcnb, ref sstate.tmpcn, _params); // // Update information stored in State and exit // sstate.nfree = nfree-ntofix; sstate.cnmodelage = sstate.cnmodelage+ntofix; ncupdates = ncupdates+ntofix; result = true; return result; } // // Unexpected :) // alglib.ap.assert(false, "CNewtonUpdate: internal error"); return result; } /************************************************************************* This function prepares equality-constrained Newton step using previously calculated constrained Cholesky matrix of the problem f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b) where A can be dense or sparse. As input, this function accepts gradient at the current location. As output, it returns step vector (replaces gradient). This function works as black box. It uses fields of SState which are marked as "Variables for constrained Newton phase", and only this function and its friends know about these variables. Everyone else should use: * CNewtonBuild() to prepare initial Cholesky decomposition for step * CNewtonStep() to perform constrained Newton step * CNewtonUpdate() to update Cholesky matrix after point was moved and constraints were updated. In some cases it is possible to efficiently re-calculate Cholesky decomposition if you know which constraints were activated. If efficient re-calculation is impossible, this function returns False. INPUT PARAMETERS: SState - structure which stores model and temporaries for CN phase; in particular, SAS.XC stores current point. Settings - QQPSettings object which was initialized by appropriate construction function. GC - array[N], gradient of the target function OUTPUT PARAMETERS: GC - array[N], step vector (on success) RESULT: True, if step was successfully calculated. False, if step calculation failed: a) gradient was exactly zero, b) gradient norm was smaller than EpsG (stopping condition) c) all variables were equality-constrained NOTE: this function may routinely return False. You should be able to handle such situations. -- ALGLIB -- Copyright 14.05.2014 by Bochkanov Sergey *************************************************************************/ private static bool cnewtonstep(qqpbuffers sstate, qqpsettings settings, double[] gc, alglib.xparams _params) { bool result = new bool(); int i = 0; int n = 0; int nfree = 0; double v = 0; int i_ = 0; result = false; n = sstate.n; nfree = sstate.nfree; for(i=nfree; i<=n-1; i++) { gc[sstate.yidx[i]] = 0.0; } v = 0.0; for(i_=0; i_<=n-1;i_++) { v += gc[i_]*gc[i_]; } if( (double)(Math.Sqrt(v))<=(double)(settings.epsg) ) { return result; } for(i=0; i<=n-1; i++) { gc[i] = -gc[i]; } if( sstate.akind==0 ) { // // Dense Newton step. // Use straightforward Cholesky solver. // fbls.fblscholeskysolve(sstate.densez, 1.0, n, true, gc, ref sstate.tmpcn, _params); result = true; return result; } if( sstate.akind==1 ) { // // Sparse Newton step. // // We have T*T' = L*L' = U'*U (depending on specific triangle stored in SparseCCA). // if( sstate.sparseupper ) { sparse.sparsetrsv(sstate.sparsecca, sstate.sparseupper, false, 1, gc, _params); sparse.sparsetrsv(sstate.sparsecca, sstate.sparseupper, false, 0, gc, _params); } else { sparse.sparsetrsv(sstate.sparsecca, sstate.sparseupper, false, 0, gc, _params); sparse.sparsetrsv(sstate.sparsecca, sstate.sparseupper, false, 1, gc, _params); } result = true; return result; } alglib.ap.assert(false, "CNewtonStep: internal error"); return result; } } public class minlbfgs { public class minlbfgsstate : apobject { public int n; public int m; public double epsg; public double epsf; public double epsx; public int maxits; public bool xrep; public double stpmax; public double[] s; public double diffstep; public int nfev; public int mcstage; public int k; public int q; public int p; public double[] rho; public double[,] yk; public double[,] sk; public double[] xp; public double[] theta; public double[] d; public double stp; public double[] work; public double fold; public double trimthreshold; public double[] xbase; public int prectype; public double gammak; public double[,] denseh; public double[] diagh; public double[] precc; public double[] precd; public double[,] precw; public int preck; public optserv.precbuflbfgs precbuf; public optserv.precbuflowrank lowrankbuf; public double fbase; public double fm2; public double fm1; public double fp1; public double fp2; public double[] autobuf; public double[] invs; public double[] x; public double f; public double[] g; public bool needf; public bool needfg; public bool xupdated; public bool userterminationneeded; public double teststep; public rcommstate rstate; public int repiterationscount; public int repnfev; public int repterminationtype; public linmin.linminstate lstate; public int smoothnessguardlevel; public optserv.smoothnessmonitor smonitor; public double[] lastscaleused; public minlbfgsstate() { init(); } public override void init() { s = new double[0]; rho = new double[0]; yk = new double[0,0]; sk = new double[0,0]; xp = new double[0]; theta = new double[0]; d = new double[0]; work = new double[0]; xbase = new double[0]; denseh = new double[0,0]; diagh = new double[0]; precc = new double[0]; precd = new double[0]; precw = new double[0,0]; precbuf = new optserv.precbuflbfgs(); lowrankbuf = new optserv.precbuflowrank(); autobuf = new double[0]; invs = new double[0]; x = new double[0]; g = new double[0]; rstate = new rcommstate(); lstate = new linmin.linminstate(); smonitor = new optserv.smoothnessmonitor(); lastscaleused = new double[0]; } public override alglib.apobject make_copy() { minlbfgsstate _result = new minlbfgsstate(); _result.n = n; _result.m = m; _result.epsg = epsg; _result.epsf = epsf; _result.epsx = epsx; _result.maxits = maxits; _result.xrep = xrep; _result.stpmax = stpmax; _result.s = (double[])s.Clone(); _result.diffstep = diffstep; _result.nfev = nfev; _result.mcstage = mcstage; _result.k = k; _result.q = q; _result.p = p; _result.rho = (double[])rho.Clone(); _result.yk = (double[,])yk.Clone(); _result.sk = (double[,])sk.Clone(); _result.xp = (double[])xp.Clone(); _result.theta = (double[])theta.Clone(); _result.d = (double[])d.Clone(); _result.stp = stp; _result.work = (double[])work.Clone(); _result.fold = fold; _result.trimthreshold = trimthreshold; _result.xbase = (double[])xbase.Clone(); _result.prectype = prectype; _result.gammak = gammak; _result.denseh = (double[,])denseh.Clone(); _result.diagh = (double[])diagh.Clone(); _result.precc = (double[])precc.Clone(); _result.precd = (double[])precd.Clone(); _result.precw = (double[,])precw.Clone(); _result.preck = preck; _result.precbuf = (optserv.precbuflbfgs)precbuf.make_copy(); _result.lowrankbuf = (optserv.precbuflowrank)lowrankbuf.make_copy(); _result.fbase = fbase; _result.fm2 = fm2; _result.fm1 = fm1; _result.fp1 = fp1; _result.fp2 = fp2; _result.autobuf = (double[])autobuf.Clone(); _result.invs = (double[])invs.Clone(); _result.x = (double[])x.Clone(); _result.f = f; _result.g = (double[])g.Clone(); _result.needf = needf; _result.needfg = needfg; _result.xupdated = xupdated; _result.userterminationneeded = userterminationneeded; _result.teststep = teststep; _result.rstate = (rcommstate)rstate.make_copy(); _result.repiterationscount = repiterationscount; _result.repnfev = repnfev; _result.repterminationtype = repterminationtype; _result.lstate = (linmin.linminstate)lstate.make_copy(); _result.smoothnessguardlevel = smoothnessguardlevel; _result.smonitor = (optserv.smoothnessmonitor)smonitor.make_copy(); _result.lastscaleused = (double[])lastscaleused.Clone(); return _result; } }; /************************************************************************* This structure stores optimization report: * IterationsCount total number of inner iterations * NFEV number of gradient evaluations * TerminationType termination type (see below) TERMINATION CODES TerminationType field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. 1 relative function improvement is no more than EpsF. 2 relative step is no more than EpsX. 4 gradient norm is no more than EpsG 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 terminated by user who called minlbfgsrequesttermination(). X contains point which was "current accepted" when termination request was submitted. Other fields of this structure are not documented and should not be used! *************************************************************************/ public class minlbfgsreport : apobject { public int iterationscount; public int nfev; public int terminationtype; public minlbfgsreport() { init(); } public override void init() { } public override alglib.apobject make_copy() { minlbfgsreport _result = new minlbfgsreport(); _result.iterationscount = iterationscount; _result.nfev = nfev; _result.terminationtype = terminationtype; return _result; } }; public const double gtol = 0.4; /************************************************************************* LIMITED MEMORY BFGS METHOD FOR LARGE SCALE OPTIMIZATION DESCRIPTION: The subroutine minimizes function F(x) of N arguments by using a quasi- Newton method (LBFGS scheme) which is optimized to use a minimum amount of memory. The subroutine generates the approximation of an inverse Hessian matrix by using information about the last M steps of the algorithm (instead of N). It lessens a required amount of memory from a value of order N^2 to a value of order 2*N*M. REQUIREMENTS: Algorithm will request following information during its operation: * function value F and its gradient G (simultaneously) at given point X USAGE: 1. User initializes algorithm state with MinLBFGSCreate() call 2. User tunes solver parameters with MinLBFGSSetCond() MinLBFGSSetStpMax() and other functions 3. User calls MinLBFGSOptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G. 4. User calls MinLBFGSResults() to get solution 5. Optionally user may call MinLBFGSRestartFrom() to solve another problem with same N/M but another starting point and/or another function. MinLBFGSRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension. N>0 M - number of corrections in the BFGS scheme of Hessian approximation update. Recommended value: 3<=M<=7. The smaller value causes worse convergence, the bigger will not cause a considerably better convergence, but will cause a fall in the performance. M<=N. X - initial solution approximation, array[0..N-1]. OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. you may tune stopping conditions with MinLBFGSSetCond() function 2. if target function contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow, use MinLBFGSSetStpMax() function to bound algorithm's steps. However, L-BFGS rarely needs such a tuning. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgscreate(int n, int m, double[] x, minlbfgsstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinLBFGSCreate: N<1!"); alglib.ap.assert(m>=1, "MinLBFGSCreate: M<1"); alglib.ap.assert(m<=n, "MinLBFGSCreate: M>N"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinLBFGSCreate: Length(X)0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X M - number of corrections in the BFGS scheme of Hessian approximation update. Recommended value: 3<=M<=7. The smaller value causes worse convergence, the bigger will not cause a considerably better convergence, but will cause a fall in the performance. M<=N. X - starting point, array[0..N-1]. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinLBFGSSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large truncation errors, while too small step will result in too large numerical errors. 1.0E-6 can be good value to start with. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. LBFGS needs exact gradient values. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 16.05.2011 by Bochkanov Sergey *************************************************************************/ public static void minlbfgscreatef(int n, int m, double[] x, double diffstep, minlbfgsstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinLBFGSCreateF: N too small!"); alglib.ap.assert(m>=1, "MinLBFGSCreateF: M<1"); alglib.ap.assert(m<=n, "MinLBFGSCreateF: M>N"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinLBFGSCreateF: Length(X)(double)(0), "MinLBFGSCreateF: DiffStep is non-positive!"); minlbfgscreatex(n, m, x, 0, diffstep, state, _params); } /************************************************************************* This function sets stopping conditions for L-BFGS optimization algorithm. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if on k+1-th iteration the condition |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - ste pvector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinLBFGSSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (small EpsX). -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetcond(minlbfgsstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsg), "MinLBFGSSetCond: EpsG is not finite number!"); alglib.ap.assert((double)(epsg)>=(double)(0), "MinLBFGSSetCond: negative EpsG!"); alglib.ap.assert(math.isfinite(epsf), "MinLBFGSSetCond: EpsF is not finite number!"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinLBFGSSetCond: negative EpsF!"); alglib.ap.assert(math.isfinite(epsx), "MinLBFGSSetCond: EpsX is not finite number!"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinLBFGSSetCond: negative EpsX!"); alglib.ap.assert(maxits>=0, "MinLBFGSSetCond: negative MaxIts!"); if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxits==0 ) { epsx = 1.0E-6; } state.epsg = epsg; state.epsf = epsf; state.epsx = epsx; state.maxits = maxits; } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinLBFGSOptimize(). -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetxrep(minlbfgsstate state, bool needxrep, alglib.xparams _params) { state.xrep = needxrep; } /************************************************************************* This function sets maximum step length INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0 (default), if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetstpmax(minlbfgsstate state, double stpmax, alglib.xparams _params) { alglib.ap.assert(math.isfinite(stpmax), "MinLBFGSSetStpMax: StpMax is not finite!"); alglib.ap.assert((double)(stpmax)>=(double)(0), "MinLBFGSSetStpMax: StpMax<0!"); state.stpmax = stpmax; } /************************************************************************* This function sets scaling coefficients for LBFGS optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. In most optimizers (and in the LBFGS too) scaling is NOT a form of preconditioning. It just affects stopping conditions. You should set preconditioner by separate call to one of the MinLBFGSSetPrec...() functions. There is special preconditioning mode, however, which uses scaling coefficients to form diagonal preconditioning matrix. You can turn this mode on, if you want. But you should understand that scaling is not the same thing as preconditioning - these are two different, although related forms of tuning solver. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetscale(minlbfgsstate state, double[] s, alglib.xparams _params) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.n, "MinLBFGSSetScale: Length(S)=1, "MinLBFGS: N too small!"); alglib.ap.assert(m>=1, "MinLBFGS: M too small!"); alglib.ap.assert(m<=n, "MinLBFGS: M too large!"); // // Initialize // state.teststep = 0; state.smoothnessguardlevel = 0; optserv.smoothnessmonitorinit(state.smonitor, 0, 0, false, _params); state.diffstep = diffstep; state.n = n; state.m = m; allocatemem = flags%2==0; flags = flags/2; if( allocatemem ) { apserv.rvectorsetlengthatleast(ref state.rho, m, _params); apserv.rvectorsetlengthatleast(ref state.theta, m, _params); apserv.rmatrixsetlengthatleast(ref state.yk, m, n, _params); apserv.rmatrixsetlengthatleast(ref state.sk, m, n, _params); apserv.rvectorsetlengthatleast(ref state.d, n, _params); apserv.rvectorsetlengthatleast(ref state.xp, n, _params); apserv.rvectorsetlengthatleast(ref state.x, n, _params); apserv.rvectorsetlengthatleast(ref state.xbase, n, _params); apserv.rvectorsetlengthatleast(ref state.s, n, _params); apserv.rvectorsetlengthatleast(ref state.invs, n, _params); apserv.rvectorsetlengthatleast(ref state.lastscaleused, n, _params); apserv.rvectorsetlengthatleast(ref state.g, n, _params); apserv.rvectorsetlengthatleast(ref state.work, n, _params); } for(i=0; i<=n-1; i++) { state.s[i] = 1.0; state.invs[i] = 1.0; state.lastscaleused[i] = 1.0; } state.prectype = 0; minlbfgssetcond(state, 0, 0, 0, 0, _params); minlbfgssetxrep(state, false, _params); minlbfgssetstpmax(state, 0, _params); minlbfgsrestartfrom(state, x, _params); } /************************************************************************* Modification of the preconditioner: default preconditioner (simple scaling, same for all elements of X) is used. INPUT PARAMETERS: State - structure which stores algorithm state NOTE: you can change preconditioner "on the fly", during algorithm iterations. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetprecdefault(minlbfgsstate state, alglib.xparams _params) { state.prectype = 0; } /************************************************************************* Modification of the preconditioner: Cholesky factorization of approximate Hessian is used. INPUT PARAMETERS: State - structure which stores algorithm state P - triangular preconditioner, Cholesky factorization of the approximate Hessian. array[0..N-1,0..N-1], (if larger, only leading N elements are used). IsUpper - whether upper or lower triangle of P is given (other triangle is not referenced) After call to this function preconditioner is changed to P (P is copied into the internal buffer). NOTE: you can change preconditioner "on the fly", during algorithm iterations. NOTE 2: P should be nonsingular. Exception will be thrown otherwise. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetpreccholesky(minlbfgsstate state, double[,] p, bool isupper, alglib.xparams _params) { int i = 0; double mx = 0; alglib.ap.assert(apserv.isfinitertrmatrix(p, state.n, isupper, _params), "MinLBFGSSetPrecCholesky: P contains infinite or NAN values!"); mx = 0; for(i=0; i<=state.n-1; i++) { mx = Math.Max(mx, Math.Abs(p[i,i])); } alglib.ap.assert((double)(mx)>(double)(0), "MinLBFGSSetPrecCholesky: P is strictly singular!"); if( alglib.ap.rows(state.denseh)=state.n, "MinLBFGSSetPrecDiag: D is too short"); for(i=0; i<=state.n-1; i++) { alglib.ap.assert(math.isfinite(d[i]), "MinLBFGSSetPrecDiag: D contains infinite or NAN elements"); alglib.ap.assert((double)(d[i])>(double)(0), "MinLBFGSSetPrecDiag: D contains non-positive elements"); } apserv.rvectorsetlengthatleast(ref state.diagh, state.n, _params); state.prectype = 2; for(i=0; i<=state.n-1; i++) { state.diagh[i] = d[i]; } } /************************************************************************* Modification of the preconditioner: scale-based diagonal preconditioning. This preconditioning mode can be useful when you don't have approximate diagonal of Hessian, but you know that your variables are badly scaled (for example, one variable is in [1,10], and another in [1000,100000]), and most part of the ill-conditioning comes from different scales of vars. In this case simple scale-based preconditioner, with H[i] = 1/(s[i]^2), can greatly improve convergence. IMPRTANT: you should set scale of your variables with MinLBFGSSetScale() call (before or after MinLBFGSSetPrecScale() call). Without knowledge of the scale of your variables scale-based preconditioner will be just unit matrix. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetprecscale(minlbfgsstate state, alglib.xparams _params) { state.prectype = 3; } /************************************************************************* This function sets low-rank preconditioner for Hessian matrix H=D+W'*C*W, where: * H is a Hessian matrix, which is approximated by D/W/C * D is a NxN diagonal positive definite matrix * W is a KxN low-rank correction * C is a KxK positive definite diagonal factor of low-rank correction This preconditioner is inexact but fast - it requires O(N*K) time to be applied. Preconditioner P is calculated by artificially constructing a set of BFGS updates which tries to reproduce behavior of H: * Sk = Wk (k-th row of W) * Yk = (D+Wk'*Ck*Wk)*Sk * Yk/Sk are reordered by ascending of C[k]*norm(Wk)^2 Here we assume that rows of Wk are orthogonal or nearly orthogonal, which allows us to have O(N*K+K^2) update instead of O(N*K^2) one. Reordering of updates is essential for having good performance on non-orthogonal problems (updates which do not add much of curvature are added first, and updates which add very large eigenvalues are added last and override effect of the first updates). In practice, this preconditioner is perfect when ortogonal correction is applied; on non-orthogonal problems sometimes it allows to achieve 5x speedup (when compared to non-preconditioned solver). -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetprecrankklbfgsfast(minlbfgsstate state, double[] d, double[] c, double[,] w, int cnt, alglib.xparams _params) { int i = 0; int j = 0; int n = 0; n = state.n; state.prectype = 4; state.preck = cnt; apserv.rvectorsetlengthatleast(ref state.precc, cnt, _params); apserv.rvectorsetlengthatleast(ref state.precd, n, _params); apserv.rmatrixsetlengthatleast(ref state.precw, cnt, n, _params); for(i=0; i<=n-1; i++) { state.precd[i] = d[i]; } for(i=0; i<=cnt-1; i++) { state.precc[i] = c[i]; for(j=0; j<=n-1; j++) { state.precw[i,j] = w[i,j]; } } } /************************************************************************* This function sets exact low-rank preconditioner for Hessian matrix H=D+W'*C*W, where: * H is a Hessian matrix, which is approximated by D/W/C * D is a NxN diagonal positive definite matrix * W is a KxN low-rank correction * C is a KxK semidefinite diagonal factor of low-rank correction This preconditioner is exact but slow - it requires O(N*K^2) time to be built and O(N*K) time to be applied. Woodbury matrix identity is used to build inverse matrix. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetpreclowrankexact(minlbfgsstate state, double[] d, double[] c, double[,] w, int cnt, alglib.xparams _params) { state.prectype = 5; optserv.preparelowrankpreconditioner(d, c, w, state.n, cnt, state.lowrankbuf, _params); } /************************************************************************* NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied gradient, and one which uses function value only and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object (either MinLBFGSCreate() for analytical gradient or MinLBFGSCreateF() for numerical differentiation) you should choose appropriate variant of MinLBFGSOptimize() - one which accepts function AND gradient or one which accepts function ONLY. Be careful to choose variant of MinLBFGSOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinLBFGSOptimize() and specific function used to create optimizer. | USER PASSED TO MinLBFGSOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinLBFGSCreateF() | work FAIL MinLBFGSCreate() | FAIL work Here "FAIL" denotes inappropriate combinations of optimizer creation function and MinLBFGSOptimize() version. Attemps to use such combination (for example, to create optimizer with MinLBFGSCreateF() and to pass gradient information to MinCGOptimize()) will lead to exception being thrown. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ public static bool minlbfgsiteration(minlbfgsstate state, alglib.xparams _params) { bool result = new bool(); int n = 0; int m = 0; int i = 0; int j = 0; int ic = 0; int mcinfo = 0; double v = 0; double vv = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { n = state.rstate.ia[0]; m = state.rstate.ia[1]; i = state.rstate.ia[2]; j = state.rstate.ia[3]; ic = state.rstate.ia[4]; mcinfo = state.rstate.ia[5]; v = state.rstate.ra[0]; vv = state.rstate.ra[1]; } else { n = 359; m = -58; i = -919; j = -909; ic = 81; mcinfo = 255; v = 74; vv = -788; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } if( state.rstate.stage==4 ) { goto lbl_4; } if( state.rstate.stage==5 ) { goto lbl_5; } if( state.rstate.stage==6 ) { goto lbl_6; } if( state.rstate.stage==7 ) { goto lbl_7; } if( state.rstate.stage==8 ) { goto lbl_8; } if( state.rstate.stage==9 ) { goto lbl_9; } if( state.rstate.stage==10 ) { goto lbl_10; } if( state.rstate.stage==11 ) { goto lbl_11; } if( state.rstate.stage==12 ) { goto lbl_12; } if( state.rstate.stage==13 ) { goto lbl_13; } if( state.rstate.stage==14 ) { goto lbl_14; } // // Routine body // // // Unload frequently used variables from State structure // (just for typing convinience) // n = state.n; m = state.m; // // Init // state.userterminationneeded = false; state.repterminationtype = 0; state.repiterationscount = 0; state.repnfev = 0; optserv.smoothnessmonitorinit(state.smonitor, n, 1, state.smoothnessguardlevel>0, _params); apserv.rvectorsetlengthatleast(ref state.invs, n, _params); for(i=0; i<=n-1; i++) { state.lastscaleused[i] = state.s[i]; state.invs[i] = 1/state.s[i]; } // // Check, that transferred derivative value is right // state.stp = 0; clearrequestfields(state, _params); if( !((double)(state.diffstep)==(double)(0) && (double)(state.teststep)>(double)(0)) ) { goto lbl_15; } lbl_17: if( !optserv.smoothnessmonitorcheckgradientatx0(state.smonitor, state.xbase, state.s, state.s, state.s, false, state.teststep, _params) ) { goto lbl_18; } for(i=0; i<=n-1; i++) { state.x[i] = state.smonitor.x[i]; } state.needfg = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needfg = false; state.smonitor.fi[0] = state.f; for(i=0; i<=n-1; i++) { state.smonitor.j[0,i] = state.g[i]; } goto lbl_17; lbl_18: lbl_15: // // Calculate F/G at the initial point // for(i=0; i<=n-1; i++) { state.x[i] = state.xbase[i]; } state.stp = 0; clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_19; } state.needfg = true; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: state.needfg = false; goto lbl_20; lbl_19: state.needf = true; state.rstate.stage = 2; goto lbl_rcomm; lbl_2: state.fbase = state.f; i = 0; lbl_21: if( i>n-1 ) { goto lbl_23; } v = state.x[i]; state.x[i] = v-state.diffstep*state.s[i]; state.rstate.stage = 3; goto lbl_rcomm; lbl_3: state.fm2 = state.f; state.x[i] = v-0.5*state.diffstep*state.s[i]; state.rstate.stage = 4; goto lbl_rcomm; lbl_4: state.fm1 = state.f; state.x[i] = v+0.5*state.diffstep*state.s[i]; state.rstate.stage = 5; goto lbl_rcomm; lbl_5: state.fp1 = state.f; state.x[i] = v+state.diffstep*state.s[i]; state.rstate.stage = 6; goto lbl_rcomm; lbl_6: state.fp2 = state.f; state.x[i] = v; state.g[i] = (8*(state.fp1-state.fm1)-(state.fp2-state.fm2))/(6*state.diffstep*state.s[i]); i = i+1; goto lbl_21; lbl_23: state.f = state.fbase; state.needf = false; lbl_20: optserv.trimprepare(state.f, ref state.trimthreshold, _params); if( !state.xrep ) { goto lbl_24; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 7; goto lbl_rcomm; lbl_7: state.xupdated = false; lbl_24: if( state.userterminationneeded ) { // // User requested termination // state.repterminationtype = 8; result = false; return result; } state.repnfev = 1; state.fold = state.f; v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.g[i]*state.s[i]); } if( (double)(Math.Sqrt(v))<=(double)(state.epsg) ) { state.repterminationtype = 4; result = false; return result; } // // Choose initial step and direction. // Apply preconditioner, if we have something other than default. // for(i_=0; i_<=n-1;i_++) { state.d[i_] = -state.g[i_]; } if( state.prectype==0 ) { // // Default preconditioner is used, but we can't use it before iterations will start // v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.g[i_]*state.g[i_]; } v = Math.Sqrt(v); if( (double)(state.stpmax)==(double)(0) ) { state.stp = Math.Min(1.0/v, 1); } else { state.stp = Math.Min(1.0/v, state.stpmax); } } if( state.prectype==1 ) { // // Cholesky preconditioner is used // fbls.fblscholeskysolve(state.denseh, 1.0, n, true, state.d, ref state.autobuf, _params); state.stp = 1; } if( state.prectype==2 ) { // // diagonal approximation is used // for(i=0; i<=n-1; i++) { state.d[i] = state.d[i]/state.diagh[i]; } state.stp = 1; } if( state.prectype==3 ) { // // scale-based preconditioner is used // for(i=0; i<=n-1; i++) { state.d[i] = state.d[i]*state.s[i]*state.s[i]; } state.stp = 1; } if( state.prectype==4 ) { // // rank-k BFGS-based preconditioner is used // optserv.inexactlbfgspreconditioner(state.d, n, state.precd, state.precc, state.precw, state.preck, state.precbuf, _params); state.stp = 1; } if( state.prectype==5 ) { // // exact low-rank preconditioner is used // optserv.applylowrankpreconditioner(state.d, state.lowrankbuf, _params); state.stp = 1; } // // Main cycle // state.k = 0; lbl_26: if( false ) { goto lbl_27; } // // Main cycle: prepare to 1-D line search // state.p = state.k%m; state.q = Math.Min(state.k, m-1); // // Store X[k], G[k] // for(i_=0; i_<=n-1;i_++) { state.xp[i_] = state.x[i_]; } for(i_=0; i_<=n-1;i_++) { state.sk[state.p,i_] = -state.x[i_]; } for(i_=0; i_<=n-1;i_++) { state.yk[state.p,i_] = -state.g[i_]; } // // Minimize F(x+alpha*d) // Calculate S[k], Y[k] // state.mcstage = 0; if( state.k!=0 ) { state.stp = 1.0; } linmin.linminnormalized(ref state.d, ref state.stp, n, _params); optserv.smoothnessmonitorstartlinesearch1u(state.smonitor, state.s, state.invs, state.x, state.f, state.g, _params); linmin.mcsrch(n, ref state.x, ref state.f, ref state.g, state.d, ref state.stp, state.stpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); lbl_28: if( state.mcstage==0 ) { goto lbl_29; } clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_30; } state.needfg = true; state.rstate.stage = 8; goto lbl_rcomm; lbl_8: state.needfg = false; goto lbl_31; lbl_30: state.needf = true; state.rstate.stage = 9; goto lbl_rcomm; lbl_9: state.fbase = state.f; i = 0; lbl_32: if( i>n-1 ) { goto lbl_34; } v = state.x[i]; state.x[i] = v-state.diffstep*state.s[i]; state.rstate.stage = 10; goto lbl_rcomm; lbl_10: state.fm2 = state.f; state.x[i] = v-0.5*state.diffstep*state.s[i]; state.rstate.stage = 11; goto lbl_rcomm; lbl_11: state.fm1 = state.f; state.x[i] = v+0.5*state.diffstep*state.s[i]; state.rstate.stage = 12; goto lbl_rcomm; lbl_12: state.fp1 = state.f; state.x[i] = v+state.diffstep*state.s[i]; state.rstate.stage = 13; goto lbl_rcomm; lbl_13: state.fp2 = state.f; state.x[i] = v; state.g[i] = (8*(state.fp1-state.fm1)-(state.fp2-state.fm2))/(6*state.diffstep*state.s[i]); i = i+1; goto lbl_32; lbl_34: state.f = state.fbase; state.needf = false; lbl_31: optserv.smoothnessmonitorenqueuepoint1u(state.smonitor, state.s, state.invs, state.d, state.stp, state.x, state.f, state.g, _params); optserv.trimfunction(ref state.f, ref state.g, n, state.trimthreshold, _params); linmin.mcsrch(n, ref state.x, ref state.f, ref state.g, state.d, ref state.stp, state.stpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); goto lbl_28; lbl_29: optserv.smoothnessmonitorfinalizelinesearch(state.smonitor, _params); if( state.userterminationneeded ) { // // User requested termination. // Restore previous point and return. // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xp[i_]; } state.repterminationtype = 8; result = false; return result; } if( !state.xrep ) { goto lbl_35; } // // report // clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 14; goto lbl_rcomm; lbl_14: state.xupdated = false; lbl_35: state.repnfev = state.repnfev+state.nfev; state.repiterationscount = state.repiterationscount+1; for(i_=0; i_<=n-1;i_++) { state.sk[state.p,i_] = state.sk[state.p,i_] + state.x[i_]; } for(i_=0; i_<=n-1;i_++) { state.yk[state.p,i_] = state.yk[state.p,i_] + state.g[i_]; } // // Stopping conditions // v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.g[i]*state.s[i]); } if( !math.isfinite(v) || !math.isfinite(state.f) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; result = false; return result; } if( state.repiterationscount>=state.maxits && state.maxits>0 ) { // // Too many iterations // state.repterminationtype = 5; result = false; return result; } if( (double)(Math.Sqrt(v))<=(double)(state.epsg) ) { // // Gradient is small enough // state.repterminationtype = 4; result = false; return result; } if( (double)(state.fold-state.f)<=(double)(state.epsf*Math.Max(Math.Abs(state.fold), Math.Max(Math.Abs(state.f), 1.0))) ) { // // F(k+1)-F(k) is small enough // state.repterminationtype = 1; result = false; return result; } v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.sk[state.p,i]/state.s[i]); } if( (double)(Math.Sqrt(v))<=(double)(state.epsx) ) { // // X(k+1)-X(k) is small enough // state.repterminationtype = 2; result = false; return result; } // // If Wolfe conditions are satisfied, we can update // limited memory model. // // However, if conditions are not satisfied (NFEV limit is met, // function is too wild, ...), we'll skip L-BFGS update // if( mcinfo!=1 ) { // // Skip update. // // In such cases we'll initialize search direction by // antigradient vector, because it leads to more // transparent code with less number of special cases // state.fold = state.f; for(i_=0; i_<=n-1;i_++) { state.d[i_] = -state.g[i_]; } } else { // // Calculate Rho[k], GammaK // v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.yk[state.p,i_]*state.sk[state.p,i_]; } vv = 0.0; for(i_=0; i_<=n-1;i_++) { vv += state.yk[state.p,i_]*state.yk[state.p,i_]; } if( (double)(v)==(double)(0) || (double)(vv)==(double)(0) ) { // // Rounding errors make further iterations impossible. // state.repterminationtype = -2; result = false; return result; } state.rho[state.p] = 1/v; state.gammak = v/vv; // // Calculate d(k+1) = -H(k+1)*g(k+1) // // for I:=K downto K-Q do // V = s(i)^T * work(iteration:I) // theta(i) = V // work(iteration:I+1) = work(iteration:I) - V*Rho(i)*y(i) // work(last iteration) = H0*work(last iteration) - preconditioner // for I:=K-Q to K do // V = y(i)^T*work(iteration:I) // work(iteration:I+1) = work(iteration:I) +(-V+theta(i))*Rho(i)*s(i) // // NOW WORK CONTAINS d(k+1) // for(i_=0; i_<=n-1;i_++) { state.work[i_] = state.g[i_]; } for(i=state.k; i>=state.k-state.q; i--) { ic = i%m; v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.sk[ic,i_]*state.work[i_]; } state.theta[ic] = v; vv = v*state.rho[ic]; for(i_=0; i_<=n-1;i_++) { state.work[i_] = state.work[i_] - vv*state.yk[ic,i_]; } } if( state.prectype==0 ) { // // Simple preconditioner is used // v = state.gammak; for(i_=0; i_<=n-1;i_++) { state.work[i_] = v*state.work[i_]; } } if( state.prectype==1 ) { // // Cholesky preconditioner is used // fbls.fblscholeskysolve(state.denseh, 1, n, true, state.work, ref state.autobuf, _params); } if( state.prectype==2 ) { // // diagonal approximation is used // for(i=0; i<=n-1; i++) { state.work[i] = state.work[i]/state.diagh[i]; } } if( state.prectype==3 ) { // // scale-based preconditioner is used // for(i=0; i<=n-1; i++) { state.work[i] = state.work[i]*state.s[i]*state.s[i]; } } if( state.prectype==4 ) { // // Rank-K BFGS-based preconditioner is used // optserv.inexactlbfgspreconditioner(state.work, n, state.precd, state.precc, state.precw, state.preck, state.precbuf, _params); } if( state.prectype==5 ) { // // Exact low-rank preconditioner is used // optserv.applylowrankpreconditioner(state.work, state.lowrankbuf, _params); } for(i=state.k-state.q; i<=state.k; i++) { ic = i%m; v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.yk[ic,i_]*state.work[i_]; } vv = state.rho[ic]*(-v+state.theta[ic]); for(i_=0; i_<=n-1;i_++) { state.work[i_] = state.work[i_] + vv*state.sk[ic,i_]; } } for(i_=0; i_<=n-1;i_++) { state.d[i_] = -state.work[i_]; } // // Next step // state.fold = state.f; state.k = state.k+1; } goto lbl_26; lbl_27: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = n; state.rstate.ia[1] = m; state.rstate.ia[2] = i; state.rstate.ia[3] = j; state.rstate.ia[4] = ic; state.rstate.ia[5] = mcinfo; state.rstate.ra[0] = v; state.rstate.ra[1] = vv; return result; } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with minlbfgsoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minlbfgssetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardgradient(minlbfgsstate state, double teststep, alglib.xparams _params) { alglib.ap.assert(math.isfinite(teststep), "MinLBFGSOptGuardGradient: TestStep contains NaN or INF"); alglib.ap.assert((double)(teststep)>=(double)(0), "MinLBFGSOptGuardGradient: invalid argument TestStep(TestStep<0)"); state.teststep = teststep; } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) b) nonsmooth target function (non-C1) Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardsmoothness(minlbfgsstate state, int level, alglib.xparams _params) { alglib.ap.assert(level==0 || level==1, "MinLBFGSOptGuardSmoothness: unexpected value of level parameter"); state.smoothnessguardlevel = level; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * minlbfgsoptguardgradient() for gradient verification * minlbfgsoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradvidx for specific variable (gradient element) suspected * rep.badgradxbase, a point where gradient is tested * rep.badgraduser, user-provided gradient (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.badgradnum, reference gradient obtained via numerical differentiation (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.nonc0suspected * rep.nonc1suspected === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * minlbfgsoptguardnonc1test0results() * minlbfgsoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardresults(minlbfgsstate state, optguardapi.optguardreport rep, alglib.xparams _params) { optserv.smoothnessmonitorexportreport(state.smonitor, rep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardnonc1test0results(minlbfgsstate state, optguardapi.optguardnonc1test0report strrep, optguardapi.optguardnonc1test0report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsoptguardnonc1test1results(minlbfgsstate state, optguardapi.optguardnonc1test1report strrep, optguardapi.optguardnonc1test1report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* L-BFGS algorithm results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report: * Rep.TerminationType completetion code: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -2 rounding errors prevent further improvement. X contains best point found. * -1 incorrect parameters were specified * 1 relative function improvement is no more than EpsF. * 2 relative step is no more than EpsX. * 4 gradient norm is no more than EpsG * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible * 8 terminated by user who called minlbfgsrequesttermination(). X contains point which was "current accepted" when termination request was submitted. * Rep.IterationsCount contains iterations count * NFEV countains number of function calculations -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsresults(minlbfgsstate state, ref double[] x, minlbfgsreport rep, alglib.xparams _params) { x = new double[0]; minlbfgsresultsbuf(state, ref x, rep, _params); } /************************************************************************* L-BFGS algorithm results Buffered implementation of MinLBFGSResults which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 20.08.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgsresultsbuf(minlbfgsstate state, ref double[] x, minlbfgsreport rep, alglib.xparams _params) { int i_ = 0; if( alglib.ap.len(x)=state.n, "MinLBFGSRestartFrom: Length(X)0 XOrigin - origin term, array[NC]. Can be zero. N - number of variables in the original formulation (no slack variables). CLEIC - dense linear equality/inequality constraints. Equality constraints come first. NEC, NIC - number of dense equality/inequality constraints. SCLEIC - sparse linear equality/inequality constraints. Equality constraints come first. SNEC, SNIC - number of sparse equality/inequality constraints. RenormLC - whether constraints should be renormalized (recommended) or used "as is". Settings - QPDENSEAULSettings object initialized by one of the initialization functions. State - object which stores temporaries XS - initial point, array[NC] OUTPUT PARAMETERS: XS - last point TerminationType-termination type: * * * -- ALGLIB -- Copyright 2017 by Bochkanov Sergey *************************************************************************/ public static void qpdenseauloptimize(cqmodels.convexquadraticmodel a, sparse.sparsematrix sparsea, int akind, bool sparseaupper, double[] b, double[] bndl, double[] bndu, double[] s, double[] xorigin, int nn, double[,] cleic, int dnec, int dnic, sparse.sparsematrix scleic, int snec, int snic, bool renormlc, qpdenseaulsettings settings, qpdenseaulbuffers state, ref double[] xs, ref int terminationtype, alglib.xparams _params) { int i = 0; int j = 0; int k = 0; double v = 0; double vv = 0; double rho = 0; double epsx = 0; int outeridx = 0; int nmain = 0; int nslack = 0; int ntotal = 0; int ktotal = 0; double maxrho = 0; double feaserr = 0; double feaserrprev = 0; double requestedfeasdecrease = 0; int goodcounter = 0; int stagnationcounter = 0; int nectotal = 0; int nictotal = 0; int nicwork = 0; int kwork = 0; int nwork = 0; bool allowwseviction = new bool(); bool workingsetextended = new bool(); int i_ = 0; terminationtype = 0; nmain = nn; nslack = dnic+snic; ntotal = nmain+nslack; nectotal = dnec+snec; nictotal = dnic+snic; ktotal = dnec+dnic+snec+snic; rho = settings.rho; epsx = settings.epsx; requestedfeasdecrease = 0.33; maxrho = 1.0E12; if( (double)(epsx)<=(double)(0) ) { epsx = 1.0E-9; } // // Integrity checks // if( snec+snic>0 ) { alglib.ap.assert(scleic.matrixtype==1, "QPDENSEAULOptimize: unexpected sparse matrix format"); alglib.ap.assert(scleic.m==snec+snic, "QPDENSEAULOptimize: unexpected sparse matrix size"); alglib.ap.assert(scleic.n==nmain+1, "QPDENSEAULOptimize: unexpected sparse matrix size"); } // // Prepare // state.repinneriterationscount = 0; state.repouteriterationscount = 0; state.repncholesky = 0; state.repnmv = 0; state.repnwrkchanges = 0; state.repnwrk0 = 0; state.repnwrk1 = 0; state.repnwrkf = 0; terminationtype = 0; apserv.rvectorsetlengthatleast(ref state.nulc, ktotal, _params); apserv.rvectorsetlengthatleast(ref state.nulcest, ktotal, _params); apserv.rvectorsetlengthatleast(ref state.exb, ntotal, _params); apserv.rvectorsetlengthatleast(ref state.exxc, ntotal, _params); apserv.rvectorsetlengthatleast(ref state.exxorigin, ntotal, _params); apserv.rvectorsetlengthatleast(ref state.exbndl, ntotal, _params); apserv.rvectorsetlengthatleast(ref state.exbndu, ntotal, _params); apserv.rvectorsetlengthatleast(ref state.exscale, ntotal, _params); apserv.rvectorsetlengthatleast(ref state.tmp0, ntotal, _params); apserv.rvectorsetlengthatleast(ref state.nicerr, nictotal, _params); apserv.ivectorsetlengthatleast(ref state.nicnact, nictotal, _params); // // Prepare scaled/shifted model in dense format - input parameters // are converted and stored in State.SclSftA/B/HasBndL/HasBndU/BndL/BndU/CLEIC/XC // scaleshiftoriginalproblem(a, sparsea, akind, sparseaupper, b, bndl, bndu, s, xorigin, nmain, cleic, dnec, dnic, scleic, snec, snic, renormlc, state, xs, _params); // // Normalize model in such way that norm(A)~1 (very roughly) // // We have two lower bounds for sigma_max(A): // * first estimate is provided by Frobenius norm, it is equal to ANorm/NMain // * second estimate is provided by max(CAC) // // We select largest one of these estimates, because using just one // of them is prone to different failure modes. Then, we divide A and B // by this estimate. // normalizequadraticterm(state.sclsfta, state.sclsftb, nmain, state.sclsftcleic, nectotal, nictotal, renormlc, ref state.tmp2, _params); // // Select working set of inequality constraints. // // Although it is possible to process all inequality constraints // at once, in one large batch, some QP problems have NIC>>N constraints, // but only minor fraction of them is inactive in the solution. // // Because algorithm running time is O((N+NEC+NIC)^3), we can // save a lot of time if we process only those inequality constraints // which need activation. Generally, NEC(double)(state.nicerr[k]) ) { k = j; } } // // If violation is positive, add it // if( (double)(state.nicerr[k])>(double)(0) ) { apserv.swaprows(state.sclsftcleic, nectotal+nicwork, nectotal+k, -1, _params); apserv.swapelements(state.nicerr, nicwork, k, _params); apserv.swapelementsi(state.nicnact, nicwork, k, _params); state.exxc[nmain+nicwork] = 0.0; state.nulc[nectotal+nicwork] = 0.0; state.nicnact[nicwork] = state.nicnact[nicwork]+1; apserv.inc(ref nicwork, _params); apserv.inc(ref nwork, _params); apserv.inc(ref kwork, _params); apserv.inc(ref i, _params); workingsetextended = true; } else { break; } } // // Working set eviction: // * select constraints which are (1) far away from the // boundary, AND (2) has less than two activation attempts // (if constraint is regularly activated/deactivated, we keep // it in the working set no matter what) // * remove such constraints from the working set one by one // if( allowwseviction ) { for(k=nicwork-1; k>=0; k--) { if( (double)(state.nicerr[k])<(double)(evictionlevel) && state.nicnact[k]<=1 ) { apserv.swaprows(state.sclsftcleic, nectotal+nicwork-1, nectotal+k, -1, _params); apserv.swapelements(state.nicerr, nicwork-1, k, _params); apserv.swapelementsi(state.nicnact, nicwork-1, k, _params); apserv.swapelements(state.exxc, nmain+nicwork-1, nmain+k, _params); apserv.swapelements(state.nulc, nectotal+nicwork-1, nectotal+k, _params); apserv.dec(ref nicwork, _params); apserv.dec(ref nwork, _params); apserv.dec(ref kwork, _params); } } } // // Report working set statistics // if( state.repnwrk1==0 ) { state.repnwrk1 = nicwork; } state.repnwrkf = nicwork; if( workingsetextended ) { apserv.inc(ref state.repnwrkchanges, _params); } } while( workingsetextended ); // // Estimate Lagrange multipliers using alternative algorithm // for(i_=0; i_<=kwork-1;i_++) { state.nulcest[i_] = state.nulc[i_]; } updatelagrangemultipliers(state.sclsfta, state.sclsftb, nmain, state.sclsftbndl, state.sclsfthasbndl, state.sclsftbndu, state.sclsfthasbndu, state.sclsftcleic, nectotal, nicwork, state.exxc, state.nulcest, state, _params); // // Update XC and Lagrange multipliers // feaserrprev = feaserr; feaserr = 0; for(i=0; i<=kwork-1; i++) { // // Calculate I-th feasibility error in V using formula for distance // between point and line (here we calculate actual distance between // XN and hyperplane Ci'*XN=Bi, which is different from error Ci'*XN-Bi). // v = 0; vv = 0; for(j=0; j<=nmain-1; j++) { v = v+state.sclsftcleic[i,j]*state.exxc[j]; vv = vv+math.sqr(state.sclsftcleic[i,j]); } if( i>=nectotal ) { v = v+state.exxc[nmain+(i-nectotal)]; vv = vv+math.sqr(1); } v = v-state.sclsftcleic[i,nmain]; vv = apserv.coalesce(vv, 1, _params); v = v/Math.Sqrt(vv); // // Calculate magnitude of Lagrangian update (and Lagrangian parameters themselves) // feaserr = feaserr+math.sqr(v); state.nulc[i] = state.nulcest[i]; } feaserr = Math.Sqrt(feaserr); if( (double)(feaserr)<(double)(epsx) ) { apserv.inc(ref goodcounter, _params); } else { goodcounter = 0; } if( (double)(feaserr)>(double)(feaserrprev*requestedfeasdecrease) ) { apserv.inc(ref stagnationcounter, _params); } else { stagnationcounter = 0; } if( goodcounter>=2 ) { break; } if( stagnationcounter>=2 ) { rho = Math.Min(rho*10.0, maxrho); } else { rho = Math.Min(rho*1.41, maxrho); } } // // Unpack results. // // Add XOrigin to XC and make sure that boundary constraints are // satisfied. // for(i=0; i<=nmain-1; i++) { // // Unscale/unshift // xs[i] = s[i]*state.exxc[i]+xorigin[i]; // // Make sure that point is feasible w.r.t. box constraints. // Enforce box constraints which were active in the scaled/shifted solution. // if( state.sclsfthasbndl[i] ) { if( (double)(xs[i])<(double)(bndl[i]) ) { xs[i] = bndl[i]; } if( (double)(state.exxc[i])==(double)(state.sclsftbndl[i]) ) { xs[i] = bndl[i]; } } if( state.sclsfthasbndu[i] ) { if( (double)(xs[i])>(double)(bndu[i]) ) { xs[i] = bndu[i]; } if( (double)(state.exxc[i])==(double)(state.sclsftbndu[i]) ) { xs[i] = bndu[i]; } } } terminationtype = 2; } /************************************************************************* This function generates box-constrained QP problem, which is penalized and augmented formulation of original linearly constrained problem -- ALGLIB -- Copyright 23.02.2017 by Bochkanov Sergey *************************************************************************/ private static void generateexmodel(double[,] sclsfta, double[] sclsftb, int nmain, double[] sclsftbndl, bool[] sclsfthasbndl, double[] sclsftbndu, bool[] sclsfthasbndu, double[,] sclsftcleic, int sclsftnec, int sclsftnic, double[] nulc, double rho, double[,] exa, double[] exb, double[] exbndl, double[] exbndu, ref double[,] tmp2, alglib.xparams _params) { int nslack = 0; int ntotal = 0; int i = 0; int j = 0; double v = 0; int i_ = 0; nslack = sclsftnic; ntotal = nmain+nslack; // // Integrity check for properly preallocated storage // alglib.ap.assert(alglib.ap.rows(exa)>=ntotal && alglib.ap.cols(exa)>=ntotal, "QPDenseAUL.GenerateExModel - integrity check failed"); alglib.ap.assert((alglib.ap.len(exb)>=ntotal && alglib.ap.len(exbndl)>=ntotal) && alglib.ap.len(exbndu)>=ntotal, "QPDenseAUL.GenerateExModel - integrity check failed"); // // Primary quadratic term // for(i=0; i<=ntotal-1; i++) { for(j=i; j<=ntotal-1; j++) { exa[i,j] = 0; } } for(i=0; i<=nmain-1; i++) { for(j=i; j<=nmain-1; j++) { exa[i,j] = sclsfta[i,j]; } } // // Primary linear term // for(i=0; i<=ntotal-1; i++) { exb[i] = 0; } for(i=0; i<=nmain-1; i++) { exb[i] = sclsftb[i]; } // // Box constraints - move primary, add slack // for(i=0; i<=nmain-1; i++) { if( sclsfthasbndl[i] ) { exbndl[i] = sclsftbndl[i]; } else { exbndl[i] = Double.NegativeInfinity; } if( sclsfthasbndu[i] ) { exbndu[i] = sclsftbndu[i]; } else { exbndu[i] = Double.PositiveInfinity; } } for(i=nmain; i<=ntotal-1; i++) { exbndl[i] = 0; exbndu[i] = Double.PositiveInfinity; } // // Handle equality constraints: // * modify quadratic term // * modify linear term // * add Lagrangian term // apserv.rmatrixsetlengthatleast(ref tmp2, sclsftnec+sclsftnic, ntotal, _params); for(i=0; i<=sclsftnec+sclsftnic-1; i++) { // // Given constraint row ci and right hand side ri, // I-th quadratic constraint adds penalty term // // 0.5*Rho*(ci'*x-ri)^2 = // = 0.5*Rho*(ci'*x-ri)^T*(ci'*x-ri) = // = 0.5*Rho*(x'*ci-ri')*(ci'*x-ri) = // = 0.5*Rho*(x'*ci*ci'*x - ri'*ci'*x - x'*ci*ri + ri'*ri ) // = 0.5*Rho*(x'*(ci*ci')*x - 2*ri*(ci'*x) + ri^2 ) // // Thus, quadratic term is updated by // // 0.5*Rho*(ci*ci') // // (with actual update to ExA being performed without 0.5 // multiplier because entire matrix is post-multipliead by 0.5) // and linear term receives update // // -Rho*ri*ci // // Similaryly, lagrangian term is -NUi*(ci'*x-ri), // so linear term is updated by // // -NUi*ci // // Because our model does not take into account constant term, // we calculate just quadratic and linear terms. // for(i_=0; i_<=nmain-1;i_++) { tmp2[i,i_] = sclsftcleic[i,i_]; } for(j=nmain; j<=ntotal-1; j++) { tmp2[i,j] = 0; } if( i>=sclsftnec ) { tmp2[i,nmain+i-sclsftnec] = 1.0; } v = -(rho*sclsftcleic[i,nmain]); for(i_=0; i_<=ntotal-1;i_++) { exb[i_] = exb[i_] + v*tmp2[i,i_]; } v = -nulc[i]; for(i_=0; i_<=ntotal-1;i_++) { exb[i_] = exb[i_] + v*tmp2[i,i_]; } } ablas.rmatrixsyrk(ntotal, sclsftnec+sclsftnic, rho, tmp2, 0, 0, 2, 1.0, exa, 0, 0, true, _params); } /************************************************************************* This function generates initial point for "extended" box-constrained QP problem. -- ALGLIB -- Copyright 23.02.2017 by Bochkanov Sergey *************************************************************************/ private static void generateexinitialpoint(double[] sclsftxc, int nmain, int nslack, double[] exxc, alglib.xparams _params) { int ntotal = 0; int i = 0; ntotal = nmain+nslack; for(i=0; i<=ntotal-1; i++) { exxc[i] = 0; } for(i=0; i<=nmain-1; i++) { exxc[i] = sclsftxc[i]; } } /************************************************************************* This function estimates Lagrange multipliers for scaled-shifted QP problem (here "scaled-shifted" means that we performed variable scaling and subtracted origin) given by quadratic term A, linear term B, box constraints and linear constraint matrix. It is assumed that all linear constraints are equality ones, with first NEC ones being constraints without slack variables, and next NIC ones having slack variables. The only inequality constraints we have are box ones, with first NMain ones being "general" box constraints, and next NIC ones being non-negativity constraints (not specified explicitly). We also make use of the current point XC, which is used to determine active box constraints. Actual QP problem size is NMain+NIC, but some parameters have lower dimensionality. Parameters sizes are: * A is assumed to be array[NMain,NMain] * B is assumed to be array[NMain] * BndL, BndU are array[NMain] * CLEIC is array[NEC+NIC,NMain+1] (last item in a row containts right part) * ExXC is array[NMain+NIC], holds current point * NuLCEst is array[NEC+NIC], holds initial values of Lagrange coeffs On exit NuLCEst is updated with new estimate of Lagrange multipliers. -- ALGLIB -- Copyright 23.02.2017 by Bochkanov Sergey *************************************************************************/ private static void updatelagrangemultipliers(double[,] sclsfta, double[] sclsftb, int nmain, double[] sclsftbndl, bool[] sclsfthasbndl, double[] sclsftbndu, bool[] sclsfthasbndu, double[,] sclsftcleic, int sclsftnec, int sclsftnic, double[] exxc, double[] nulcest, qpdenseaulbuffers buffers, alglib.xparams _params) { int nslack = 0; int ntotal = 0; int ktotal = 0; int nqrrows = 0; int nqrcols = 0; int i = 0; int j = 0; double lambdareg = 0; double mxdiag = 0; double v = 0; bool isactive = new bool(); int i_ = 0; nslack = sclsftnic; ntotal = nmain+nslack; ktotal = sclsftnec+sclsftnic; // // Given current point ExXC, we can determine active and inactive // constraints. After we drop inactive inequality constraints, we // have equality-only constrained QP problem, with mix of general // linear equality constraints and "simple" constraints Xi=Ci. // // Problem min(0.5*x'*A*x + b'*x) s.t. C*x=d (general linear // constraints) can be solved by explicitly writing out Lagrange // equations: // // [ A C' ] [ X ] [ -b] // [ ] [ ] = [ ] // [ C ] [ L ] [ d ] // // or // // [ X ] // A1* [ ] = b1 // [ L ] // // where X stands for solution itself, and L stands for Lagrange // multipliers. It can be easily solved with direct linear solver. // However, such formulation does not account for "simple" equality // constraints on variables. It is possible to include "simple" // constraints into "general" ones (i.e. append (0 ... 0 -1 0 ... 0)' // to the constraint matrix), but it will increase problem // size. // // Another approach is to use initial values of X and L (X0 and L0) // as starting point, and to solve for "offset" from (X0, L0): // // [ X0+X1 ] // A1*[ ] = b1 // [ L0+L1 ] // // or // // [ X1 ] [ X0 ] // A1*[ ] = b1 - A1*[ ] // [ L1 ] [ L0 ] // // In such formulation components of X1 which correspond to active // constraints on variables are "frozen" at value 0 (because we have // equality constraint, offset from constrained value have to be zero). // // Thus, we can rewrite corresponding columns of A1 with zeros - and // use this space to store (0 ... 0 -1 0 ... 0)', which is used to // account for Lagrange multipliers for "simple" constraints. // nqrcols = ntotal+ktotal; nqrrows = nqrcols; apserv.rvectorsetlengthatleast(ref buffers.qrsv0, nqrcols, _params); apserv.rvectorsetlengthatleast(ref buffers.qrsvx1, nqrcols, _params); for(i=0; i<=ntotal-1; i++) { buffers.qrsv0[i] = exxc[i]; } for(i=0; i<=ktotal-1; i++) { buffers.qrsv0[ntotal+i] = nulcest[i]; } apserv.rmatrixsetlengthatleast(ref buffers.qrkkt, nqrcols+nqrcols, nqrcols+1, _params); apserv.rvectorsetlengthatleast(ref buffers.qrrightpart, nqrcols+nqrcols, _params); lambdareg = 1.0E-8; while( true ) { // // Initialize matrix A1 and right part b1 with zeros // for(i=0; i<=alglib.ap.rows(buffers.qrkkt)-1; i++) { for(j=0; j<=alglib.ap.cols(buffers.qrkkt)-1; j++) { buffers.qrkkt[i,j] = 0; } buffers.qrrightpart[i] = 0; } // // Append quadratic term (note: we implicitly add NSlack zeros to // A and b). // mxdiag = 0; for(i=0; i<=nmain-1; i++) { for(j=0; j<=nmain-1; j++) { buffers.qrkkt[i,j] = sclsfta[i,j]; } buffers.qrrightpart[i] = -sclsftb[i]; mxdiag = Math.Max(mxdiag, Math.Abs(sclsfta[i,i])); } mxdiag = apserv.coalesce(mxdiag, 1, _params); // // Append general linear constraints // for(i=0; i<=ktotal-1; i++) { for(j=0; j<=nmain-1; j++) { buffers.qrkkt[ntotal+i,j] = -sclsftcleic[i,j]; buffers.qrkkt[j,ntotal+i] = -sclsftcleic[i,j]; } if( i>=sclsftnec ) { buffers.qrkkt[ntotal+i,nmain+(i-sclsftnec)] = -1; buffers.qrkkt[nmain+(i-sclsftnec),ntotal+i] = -1; } buffers.qrrightpart[ntotal+i] = -sclsftcleic[i,nmain]; } // // Append regularizer to the bottom of the matrix // (it will be factored in during QR decomposition) // if( (double)(lambdareg)>(double)(0) ) { nqrrows = nqrcols+nqrcols; for(i=0; i<=nqrcols-1; i++) { buffers.qrkkt[nqrcols+i,i] = lambdareg*mxdiag; } } // // Subtract reference point (X0,L0) from the system // for(i=0; i<=nqrcols-1; i++) { v = 0.0; for(i_=0; i_<=nqrcols-1;i_++) { v += buffers.qrkkt[i,i_]*buffers.qrsv0[i_]; } buffers.qrrightpart[i] = buffers.qrrightpart[i]-v; } // // Handle active "simple" equality constraints // for(i=0; i<=ntotal-1; i++) { isactive = false; if( i=nmain && (double)(exxc[i])==(double)(0.0) ) { isactive = true; } if( !isactive ) { continue; } for(j=0; j<=nqrrows-1; j++) { buffers.qrkkt[j,i] = 0; } buffers.qrkkt[i,i] = -1; } // // Solve via QR decomposition: // * append right part to the system matrix // * perform QR decomposition of the extended matrix (right part is implicitly // multiplied by Q during decomposition; believe me, it works!) // * check condition number, increase regularization value if necessary and retry // * solve triangular system, break iteration // for(i=0; i<=nqrrows-1; i++) { buffers.qrkkt[i,nqrcols] = buffers.qrrightpart[i]; } ortfac.rmatrixqr(ref buffers.qrkkt, nqrrows, nqrcols+1, ref buffers.qrtau, _params); if( (double)(rcond.rmatrixtrrcond1(buffers.qrkkt, nqrcols, true, false, _params))<=(double)(1000*math.machineepsilon) ) { lambdareg = apserv.coalesce(10*lambdareg, 1.0E-13, _params); continue; } for(i=nqrcols-1; i>=0; i--) { v = buffers.qrkkt[i,nqrcols]; for(j=i+1; j<=nqrcols-1; j++) { v = v-buffers.qrkkt[i,j]*buffers.qrsvx1[j]; } buffers.qrsvx1[i] = v/buffers.qrkkt[i,i]; } break; } // // Update Lagrange coefficients // for(i=0; i<=ktotal-1; i++) { nulcest[i] = buffers.qrsv0[ntotal+i]+buffers.qrsvx1[ntotal+i]; } } /************************************************************************* This function generates scaled (by S) and shifted (by XC) reformulation of the original problem. INPUT PARAMETERS: DenseA - for dense problems (AKind=0), A-term of CQM object contains system matrix. Other terms are unspecified and should not be referenced. SparseA - for sparse problems (AKind=1), CRS format 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). B - linear term, array[N] BndL - lower bound, array[N] BndU - upper bound, array[N] S - scale vector, array[NC]: * I-th element contains scale of I-th variable, * SC[I]>0 XOrigin - origin term, array[NC]. Can be zero. N - number of variables in the original formulation (no slack variables). CLEIC - dense linear equality/inequality constraints. Equality constraints come first. NEC, NIC - number of dense equality/inequality constraints. SCLEIC - sparse linear equality/inequality constraints. Equality constraints come first. SNEC, SNIC - number of sparse equality/inequality constraints. RenormLC - whether constraints should be renormalized (recommended) or used "as is". Settings - QPDENSEAULSettings object initialized by one of the initialization functions. State - object which stores temporaries XS - initial point, array[NC] On output, following fields of the State structure are modified: * SclSftA - array[NMain,NMain], quadratic term, both triangles * SclSftB - array[NMain], linear term * SclSftXC - array[NMain], initial point * SclSftHasBndL, SclSftHasBndU, SclSftBndL, SclSftBndU - array[NMain], lower/upper bounds * SclSftCLEIC - array[KTotal,NMain+1], general linear constraints NOTE: State.Tmp2 is used to store temporary array[NMain,NMain] -- ALGLIB -- Copyright 01.10.2017 by Bochkanov Sergey *************************************************************************/ private static void scaleshiftoriginalproblem(cqmodels.convexquadraticmodel a, sparse.sparsematrix sparsea, int akind, bool sparseaupper, double[] b, double[] bndl, double[] bndu, double[] s, double[] xorigin, int nmain, double[,] cleic, int dnec, int dnic, sparse.sparsematrix scleic, int snec, int snic, bool renormlc, qpdenseaulbuffers state, double[] xs, alglib.xparams _params) { int i = 0; int j = 0; int k = 0; int j0 = 0; int j1 = 0; double v = 0; double vv = 0; int ktotal = 0; alglib.ap.assert(akind==0 || akind==1, "QPDENSEAULOptimize: unexpected AKind"); ktotal = dnec+dnic+snec+snic; apserv.rmatrixsetlengthatleast(ref state.sclsfta, nmain, nmain, _params); apserv.rvectorsetlengthatleast(ref state.sclsftb, nmain, _params); apserv.rvectorsetlengthatleast(ref state.sclsftxc, nmain, _params); apserv.rvectorsetlengthatleast(ref state.sclsftbndl, nmain, _params); apserv.rvectorsetlengthatleast(ref state.sclsftbndu, nmain, _params); apserv.bvectorsetlengthatleast(ref state.sclsfthasbndl, nmain, _params); apserv.bvectorsetlengthatleast(ref state.sclsfthasbndu, nmain, _params); apserv.rmatrixsetlengthatleast(ref state.sclsftcleic, ktotal, nmain+1, _params); if( akind==0 ) { // // Extract dense A and scale // cqmodels.cqmgeta(a, ref state.tmp2, _params); for(i=0; i<=nmain-1; i++) { for(j=0; j<=nmain-1; j++) { state.sclsfta[i,j] = 0; } } for(i=0; i<=nmain-1; i++) { for(j=i; j<=nmain-1; j++) { v = state.tmp2[i,j]*s[i]*s[j]; state.sclsfta[i,j] = v; state.sclsfta[j,i] = v; } } } if( akind==1 ) { // // Extract sparse A and scale // alglib.ap.assert(sparsea.matrixtype==1, "QPDENSEAULOptimize: unexpected sparse matrix format"); alglib.ap.assert(sparsea.m==nmain, "QPDENSEAULOptimize: unexpected sparse matrix size"); alglib.ap.assert(sparsea.n==nmain, "QPDENSEAULOptimize: unexpected sparse matrix size"); for(i=0; i<=nmain-1; i++) { for(j=0; j<=nmain-1; j++) { state.sclsfta[i,j] = 0; } } if( sparseaupper ) { for(i=0; i<=nmain-1; i++) { if( sparsea.didx[i]!=sparsea.uidx[i] ) { state.sclsfta[i,i] = sparsea.vals[sparsea.didx[i]]*s[i]*s[i]; } j0 = sparsea.uidx[i]; j1 = sparsea.ridx[i+1]-1; for(j=j0; j<=j1; j++) { k = sparsea.idx[j]; v = sparsea.vals[j]*s[i]*s[k]; state.sclsfta[i,k] = v; state.sclsfta[k,i] = v; } } } else { for(i=0; i<=nmain-1; i++) { if( sparsea.didx[i]!=sparsea.uidx[i] ) { state.sclsfta[i,i] = sparsea.vals[sparsea.didx[i]]*s[i]*s[i]; } j0 = sparsea.ridx[i]; j1 = sparsea.didx[i]-1; for(j=j0; j<=j1; j++) { k = sparsea.idx[j]; v = sparsea.vals[j]*s[i]*s[k]; state.sclsfta[i,k] = v; state.sclsfta[k,i] = v; } } } } for(i=0; i<=nmain-1; i++) { state.sclsftb[i] = b[i]*s[i]; } for(i=0; i<=nmain-1; i++) { alglib.ap.assert(math.isfinite(bndl[i]) || Double.IsNegativeInfinity(bndl[i]), "QPDENSEAULOptimize: integrity check failure (7)"); alglib.ap.assert(math.isfinite(bndu[i]) || Double.IsPositiveInfinity(bndu[i]), "QPDENSEAULOptimize: integrity check failure (8)"); state.sclsftxc[i] = (xs[i]-xorigin[i])/s[i]; state.sclsfthasbndl[i] = math.isfinite(bndl[i]); state.sclsfthasbndu[i] = math.isfinite(bndu[i]); if( (state.sclsfthasbndl[i] && state.sclsfthasbndu[i]) && (double)(bndl[i])==(double)(bndu[i]) ) { // // Make sure that SclSftBndL[I]=SclSftBndU[I] bit-to-bit // even with CRAZY optimizing compiler. // state.sclsftbndu[i] = (bndu[i]-xorigin[i])/s[i]; state.sclsftbndl[i] = state.sclsftbndu[i]; continue; } if( state.sclsfthasbndl[i] ) { state.sclsftbndl[i] = (bndl[i]-xorigin[i])/s[i]; } if( state.sclsfthasbndu[i] ) { state.sclsftbndu[i] = (bndu[i]-xorigin[i])/s[i]; } } for(i=0; i<=ktotal-1; i++) { for(j=0; j<=nmain; j++) { state.sclsftcleic[i,j] = 0; } } for(i=0; i<=dnec-1; i++) { for(j=0; j<=nmain-1; j++) { v = cleic[i,j]*s[j]; state.sclsftcleic[i,j] = v; } state.sclsftcleic[i,nmain] = cleic[i,nmain]; } for(i=0; i<=dnic-1; i++) { for(j=0; j<=nmain-1; j++) { v = cleic[dnec+i,j]*s[j]; state.sclsftcleic[dnec+snec+i,j] = v; } state.sclsftcleic[dnec+snec+i,nmain] = cleic[dnec+i,nmain]; } for(i=0; i<=snec-1; i++) { // // Because constraints are sparse, everything is a bit tricky - // it is possible that N-th element of the row is zero and not // stored; it is also possible that entire row is empty. // j0 = scleic.ridx[i]; j1 = scleic.ridx[i+1]-1; if( j1>=j0 && scleic.idx[j1]==nmain ) { state.sclsftcleic[dnec+i,nmain] = scleic.vals[j1]; j1 = j1-1; } for(j=j0; j<=j1; j++) { k = scleic.idx[j]; v = scleic.vals[j]*s[k]; state.sclsftcleic[dnec+i,k] = v; } } for(i=0; i<=snic-1; i++) { // // Because constraints are sparse, everything is a bit tricky - // it is possible that N-th element of the row is zero and not // stored; it is also possible that entire row is empty. // j0 = scleic.ridx[snec+i]; j1 = scleic.ridx[snec+i+1]-1; if( j1>=j0 && scleic.idx[j1]==nmain ) { state.sclsftcleic[dnec+snec+dnic+i,nmain] = scleic.vals[j1]; j1 = j1-1; } for(j=j0; j<=j1; j++) { k = scleic.idx[j]; v = scleic.vals[j]*s[k]; state.sclsftcleic[dnec+snec+dnic+i,k] = v; } } if( renormlc && ktotal>0 ) { // // Normalize linear constraints in such way that they have unit norm // (after variable scaling) // for(i=0; i<=ktotal-1; i++) { vv = 0.0; for(j=0; j<=nmain-1; j++) { v = state.sclsftcleic[i,j]; vv = vv+v*v; } vv = Math.Sqrt(vv); if( (double)(vv)>(double)(0) ) { vv = 1/vv; for(j=0; j<=nmain; j++) { state.sclsftcleic[i,j] = state.sclsftcleic[i,j]*vv; } } } } for(i=0; i<=ktotal-1; i++) { // // Apply XOrigin // v = 0.0; for(j=0; j<=nmain-1; j++) { v = v+state.sclsftcleic[i,j]*(xorigin[j]/s[j]); } state.sclsftcleic[i,nmain] = state.sclsftcleic[i,nmain]-v; } } /************************************************************************* Normalize model in such way that norm(A)~1 (very roughly) We have two lower bounds for sigma_max(A): * first estimate is provided by Frobenius norm, it is equal to ANorm/NMain * second estimate is provided by max(CAC) We select largest one of these estimates, because using just one of them is prone to different failure modes. Then, we divide A and B by this estimate. INPUT PARAMETERS: A - array[N,N], quadratic term, full triangle is given B - array[N], linear term N - problem size CLEIC- array[NEC+NIC,N+1], linear equality/inequality constraints NEC - number of equality constraints NIC - number of inequality constraints UseCLEIC- additional normalization of A in such way that CLEIC*A*CLEIC'~1: * if False, CLEIC is ignored * if True, CLEIC rows MUST have unit norm (we check it) Tmp2- additional buffer, possibly preallocated OUTPUT PARAMETERS: A, B - appropriately rescaled -- ALGLIB -- Copyright 01.10.2017 by Bochkanov Sergey *************************************************************************/ private static void normalizequadraticterm(double[,] a, double[] b, int n, double[,] cleic, int nec, int nic, bool usecleic, ref double[,] tmp2, alglib.xparams _params) { int i = 0; int j = 0; double anorm = 0; double maxcac = 0; double v = 0; double vv = 0; int ktotal = 0; int nmain = 0; nmain = n; ktotal = nec+nic; anorm = 0; for(i=0; i<=nmain-1; i++) { for(j=0; j<=nmain-1; j++) { anorm = anorm+math.sqr(a[i,j]); } } anorm = Math.Sqrt(anorm); if( usecleic && ktotal>0 ) { // // Calculate max(|diag(C*A*C')|), where C is constraint matrix // apserv.rmatrixsetlengthatleast(ref tmp2, ktotal, nmain, _params); ablas.rmatrixgemm(ktotal, nmain, nmain, 1.0, cleic, 0, 0, 0, a, 0, 0, 0, 0.0, tmp2, 0, 0, _params); maxcac = 0.0; for(i=0; i<=ktotal-1; i++) { v = 0; vv = 0; for(j=0; j<=nmain-1; j++) { v = v+tmp2[i,j]*cleic[i,j]; vv = vv+math.sqr(cleic[i,j]); } alglib.ap.assert((double)(Math.Abs(vv-1))<(double)(1.0E-9) || (double)(vv)==(double)(0), "DENSE-AUL: integrity check failed"); maxcac = Math.Max(maxcac, Math.Abs(v)); } } else { maxcac = 0; } v = 1/apserv.coalesce(Math.Max(maxcac, anorm/nmain), 1, _params); for(i=0; i<=nmain-1; i++) { for(j=0; j<=nmain-1; j++) { a[i,j] = a[i,j]*v; } } for(i=0; i<=nmain-1; i++) { b[i] = b[i]*v; } } /************************************************************************* This function selects initial working set of general inequality constraints for QP problem: * for non-convex QP problems - NICWork=NIC is returned * otherwise - NICWork=0 is returned (we have to determine working set iteratively) INPUT PARAMETERS: A - array[NMain], quadratic term, full matrix is stored NMain - number of variables in the "original" QP problem CLEIC - array[NEC+NIC,NMain+1], constraint matrix NEC - number of equality constraints NIC - number of inequality constraints OUTPUT PARAMETERS: NICWork - recommended size of working set; in current version either all (NICWork=NIC) or none (NICWork=0) constraints are included. AllowWSEviction-whether problem properties allow eviction of constraints from working set or not. Non-convex problems do not allow eviction, convex ones do. -- ALGLIB -- Copyright 02.10.2017 by Bochkanov Sergey *************************************************************************/ private static void selectinitialworkingset(double[,] a, int nmain, double[,] cleic, int nec, int nic, ref double[] tmp0, ref double[,] tmp2, ref int nicwork, ref bool allowwseviction, alglib.xparams _params) { int i = 0; int j = 0; nicwork = 0; allowwseviction = new bool(); apserv.rmatrixsetlengthatleast(ref tmp2, nmain, nmain, _params); apserv.rvectorsetlengthatleast(ref tmp0, nmain, _params); for(i=0; i<=nmain-1; i++) { for(j=i; j<=nmain-1; j++) { tmp2[i,j] = a[i,j]; } } if( !trfac.spdmatrixcholeskyrec(ref tmp2, 0, nmain, true, ref tmp0, _params) ) { // // Matrix is indefinite. // // We have to select full working set, otherwise algorithm may fail // because problem with reduced working set can be unbounded from below. // nicwork = nic; allowwseviction = false; } else { // // Positive definite matrix. // // We can select zero initial working set and expand it later. // nicwork = 0; allowwseviction = true; } } } public class minbleic { /************************************************************************* This object stores nonlinear optimizer state. You should use functions provided by MinBLEIC subpackage to work with this object *************************************************************************/ public class minbleicstate : apobject { public int nmain; public int nslack; public double epsg; public double epsf; public double epsx; public int maxits; public bool xrep; public bool drep; public double stpmax; public double diffstep; public sactivesets.sactiveset sas; public double[] s; public int prectype; public double[] diagh; public double[] x; public double f; public double[] g; public bool needf; public bool needfg; public bool xupdated; public bool lsstart; public bool steepestdescentstep; public bool boundedstep; public bool userterminationneeded; public rcommstate rstate; public double[] ugc; public double[] cgc; public double[] xn; public double[] ugn; public double[] cgn; public double[] xp; public double fc; public double fn; public double fp; public double[] d; public double[,] cleic; public int nec; public int nic; public double lastgoodstep; public double lastscaledgoodstep; public double maxscaledgrad; public bool[] hasbndl; public bool[] hasbndu; public double[] bndl; public double[] bndu; public int repinneriterationscount; public int repouteriterationscount; public int repnfev; public int repvaridx; public int repterminationtype; public double repdebugeqerr; public double repdebugfs; public double repdebugff; public double repdebugdx; public int repdebugfeasqpits; public int repdebugfeasgpaits; public double[] xstart; public snnls.snnlssolver solver; public double fbase; public double fm2; public double fm1; public double fp1; public double fp2; public double xm1; public double xp1; public double gm1; public double gp1; public int cidx; public double cval; public double[] tmpprec; public double[] tmp0; public int nfev; public int mcstage; public double stp; public double curstpmax; public double activationstep; public double[] work; public linmin.linminstate lstate; public double trimthreshold; public int nonmonotoniccnt; public double[,] bufyk; public double[,] bufsk; public double[] bufrho; public double[] buftheta; public int bufsize; public double teststep; public int smoothnessguardlevel; public optserv.smoothnessmonitor smonitor; public double[] lastscaleused; public double[] invs; public minbleicstate() { init(); } public override void init() { sas = new sactivesets.sactiveset(); s = new double[0]; diagh = new double[0]; x = new double[0]; g = new double[0]; rstate = new rcommstate(); ugc = new double[0]; cgc = new double[0]; xn = new double[0]; ugn = new double[0]; cgn = new double[0]; xp = new double[0]; d = new double[0]; cleic = new double[0,0]; hasbndl = new bool[0]; hasbndu = new bool[0]; bndl = new double[0]; bndu = new double[0]; xstart = new double[0]; solver = new snnls.snnlssolver(); tmpprec = new double[0]; tmp0 = new double[0]; work = new double[0]; lstate = new linmin.linminstate(); bufyk = new double[0,0]; bufsk = new double[0,0]; bufrho = new double[0]; buftheta = new double[0]; smonitor = new optserv.smoothnessmonitor(); lastscaleused = new double[0]; invs = new double[0]; } public override alglib.apobject make_copy() { minbleicstate _result = new minbleicstate(); _result.nmain = nmain; _result.nslack = nslack; _result.epsg = epsg; _result.epsf = epsf; _result.epsx = epsx; _result.maxits = maxits; _result.xrep = xrep; _result.drep = drep; _result.stpmax = stpmax; _result.diffstep = diffstep; _result.sas = (sactivesets.sactiveset)sas.make_copy(); _result.s = (double[])s.Clone(); _result.prectype = prectype; _result.diagh = (double[])diagh.Clone(); _result.x = (double[])x.Clone(); _result.f = f; _result.g = (double[])g.Clone(); _result.needf = needf; _result.needfg = needfg; _result.xupdated = xupdated; _result.lsstart = lsstart; _result.steepestdescentstep = steepestdescentstep; _result.boundedstep = boundedstep; _result.userterminationneeded = userterminationneeded; _result.rstate = (rcommstate)rstate.make_copy(); _result.ugc = (double[])ugc.Clone(); _result.cgc = (double[])cgc.Clone(); _result.xn = (double[])xn.Clone(); _result.ugn = (double[])ugn.Clone(); _result.cgn = (double[])cgn.Clone(); _result.xp = (double[])xp.Clone(); _result.fc = fc; _result.fn = fn; _result.fp = fp; _result.d = (double[])d.Clone(); _result.cleic = (double[,])cleic.Clone(); _result.nec = nec; _result.nic = nic; _result.lastgoodstep = lastgoodstep; _result.lastscaledgoodstep = lastscaledgoodstep; _result.maxscaledgrad = maxscaledgrad; _result.hasbndl = (bool[])hasbndl.Clone(); _result.hasbndu = (bool[])hasbndu.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.repinneriterationscount = repinneriterationscount; _result.repouteriterationscount = repouteriterationscount; _result.repnfev = repnfev; _result.repvaridx = repvaridx; _result.repterminationtype = repterminationtype; _result.repdebugeqerr = repdebugeqerr; _result.repdebugfs = repdebugfs; _result.repdebugff = repdebugff; _result.repdebugdx = repdebugdx; _result.repdebugfeasqpits = repdebugfeasqpits; _result.repdebugfeasgpaits = repdebugfeasgpaits; _result.xstart = (double[])xstart.Clone(); _result.solver = (snnls.snnlssolver)solver.make_copy(); _result.fbase = fbase; _result.fm2 = fm2; _result.fm1 = fm1; _result.fp1 = fp1; _result.fp2 = fp2; _result.xm1 = xm1; _result.xp1 = xp1; _result.gm1 = gm1; _result.gp1 = gp1; _result.cidx = cidx; _result.cval = cval; _result.tmpprec = (double[])tmpprec.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.nfev = nfev; _result.mcstage = mcstage; _result.stp = stp; _result.curstpmax = curstpmax; _result.activationstep = activationstep; _result.work = (double[])work.Clone(); _result.lstate = (linmin.linminstate)lstate.make_copy(); _result.trimthreshold = trimthreshold; _result.nonmonotoniccnt = nonmonotoniccnt; _result.bufyk = (double[,])bufyk.Clone(); _result.bufsk = (double[,])bufsk.Clone(); _result.bufrho = (double[])bufrho.Clone(); _result.buftheta = (double[])buftheta.Clone(); _result.bufsize = bufsize; _result.teststep = teststep; _result.smoothnessguardlevel = smoothnessguardlevel; _result.smonitor = (optserv.smoothnessmonitor)smonitor.make_copy(); _result.lastscaleused = (double[])lastscaleused.Clone(); _result.invs = (double[])invs.Clone(); return _result; } }; /************************************************************************* This structure stores optimization report: * IterationsCount number of iterations * NFEV number of gradient evaluations * TerminationType termination type (see below) TERMINATION CODES TerminationType field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. -3 inconsistent constraints. Feasible point is either nonexistent or too hard to find. Try to restart optimizer with better initial approximation 1 relative function improvement is no more than EpsF. 2 relative step is no more than EpsX. 4 gradient norm is no more than EpsG 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 terminated by user who called minbleicrequesttermination(). X contains point which was "current accepted" when termination request was submitted. ADDITIONAL FIELDS There are additional fields which can be used for debugging: * DebugEqErr error in the equality constraints (2-norm) * DebugFS f, calculated at projection of initial point to the feasible set * DebugFF f, calculated at the final point * DebugDX |X_start-X_final| *************************************************************************/ public class minbleicreport : apobject { public int iterationscount; public int nfev; public int varidx; public int terminationtype; public double debugeqerr; public double debugfs; public double debugff; public double debugdx; public int debugfeasqpits; public int debugfeasgpaits; public int inneriterationscount; public int outeriterationscount; public minbleicreport() { init(); } public override void init() { } public override alglib.apobject make_copy() { minbleicreport _result = new minbleicreport(); _result.iterationscount = iterationscount; _result.nfev = nfev; _result.varidx = varidx; _result.terminationtype = terminationtype; _result.debugeqerr = debugeqerr; _result.debugfs = debugfs; _result.debugff = debugff; _result.debugdx = debugdx; _result.debugfeasqpits = debugfeasqpits; _result.debugfeasgpaits = debugfeasgpaits; _result.inneriterationscount = inneriterationscount; _result.outeriterationscount = outeriterationscount; return _result; } }; public const double gtol = 0.4; public const double maxnonmonotoniclen = 1.0E-7; public const double nmstol = 1.0E2; public const double initialdecay = 0.5; public const double mindecay = 0.1; public const double decaycorrection = 0.8; public const double penaltyfactor = 100; /************************************************************************* BOUND CONSTRAINED OPTIMIZATION WITH ADDITIONAL LINEAR EQUALITY AND INEQUALITY CONSTRAINTS DESCRIPTION: The subroutine minimizes function F(x) of N arguments subject to any combination of: * bound constraints * linear inequality constraints * linear equality constraints REQUIREMENTS: * user must provide function value and gradient * starting point X0 must be feasible or not too far away from the feasible set * grad(f) must be Lipschitz continuous on a level set: L = { x : f(x)<=f(x0) } * function must be defined everywhere on the feasible set F USAGE: Constrained optimization if far more complex than the unconstrained one. Here we give very brief outline of the BLEIC optimizer. We strongly recommend you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide on optimization, which is available at http://www.alglib.net/optimization/ 1. User initializes algorithm state with MinBLEICCreate() call 2. USer adds boundary and/or linear constraints by calling MinBLEICSetBC() and MinBLEICSetLC() functions. 3. User sets stopping conditions with MinBLEICSetCond(). 4. User calls MinBLEICOptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G. 5. User calls MinBLEICResults() to get solution 6. Optionally user may call MinBLEICRestartFrom() to solve another problem with same N but another starting point. MinBLEICRestartFrom() allows to reuse already initialized structure. NOTE: if you have box-only constraints (no general linear constraints), then MinBC optimizer can be better option. It uses special, faster constraint activation method, which performs better on problems with multiple constraints active at the solution. On small-scale problems performance of MinBC is similar to that of MinBLEIC, but on large-scale ones (hundreds and thousands of active constraints) it can be several times faster than MinBLEIC. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size ofX X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. OUTPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleiccreate(int n, double[] x, minbleicstate state, alglib.xparams _params) { double[,] c = new double[0,0]; int[] ct = new int[0]; alglib.ap.assert(n>=1, "MinBLEICCreate: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinBLEICCreate: Length(X)0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[0..N-1]. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinBLEICSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large truncation errors, while too small step will result in too large numerical errors. 1.0E-6 can be good value to start with. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. CG needs exact gradient values. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 16.05.2011 by Bochkanov Sergey *************************************************************************/ public static void minbleiccreatef(int n, double[] x, double diffstep, minbleicstate state, alglib.xparams _params) { double[,] c = new double[0,0]; int[] ct = new int[0]; alglib.ap.assert(n>=1, "MinBLEICCreateF: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinBLEICCreateF: Length(X)(double)(0), "MinBLEICCreateF: DiffStep is non-positive!"); minbleicinitinternal(n, x, diffstep, state, _params); } /************************************************************************* This function sets boundary constraints for BLEIC optimizer. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinBLEICRestartFrom(). NOTE: if you have box-only constraints (no general linear constraints), then MinBC optimizer can be better option. It uses special, faster constraint activation method, which performs better on problems with multiple constraints active at the solution. On small-scale problems performance of MinBC is similar to that of MinBLEIC, but on large-scale ones (hundreds and thousands of active constraints) it can be several times faster than MinBLEIC. INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: this solver has following useful properties: * bound constraints are always satisfied exactly * function is evaluated only INSIDE area specified by bound constraints, even when numerical differentiation is used (algorithm adjusts nodes according to boundary constraints) -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetbc(minbleicstate state, double[] bndl, double[] bndu, alglib.xparams _params) { int i = 0; int n = 0; n = state.nmain; alglib.ap.assert(alglib.ap.len(bndl)>=n, "MinBLEICSetBC: Length(BndL)=n, "MinBLEICSetBC: Length(BndU)0, then I-th constraint is C[i,*]*x >= C[i,n] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT NOTE 1: linear (non-bound) constraints are satisfied only approximately: * there always exists some minor violation (about Epsilon in magnitude) due to rounding errors * numerical differentiation, if used, may lead to function evaluations outside of the feasible area, because algorithm does NOT change numerical differentiation formula according to linear constraints. If you want constraints to be satisfied exactly, try to reformulate your problem in such manner that all constraints will become boundary ones (this kind of constraints is always satisfied exactly, both in the final solution and in all intermediate points). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetlc(minbleicstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { int n = 0; int i = 0; int j = 0; double v = 0; int i_ = 0; n = state.nmain; // // First, check for errors in the inputs // alglib.ap.assert(k>=0, "MinBLEICSetLC: K<0"); alglib.ap.assert(alglib.ap.cols(c)>=n+1 || k==0, "MinBLEICSetLC: Cols(C)=k, "MinBLEICSetLC: Rows(C)=k, "MinBLEICSetLC: Length(CT)0 ) { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = -c[i,i_]; } } else { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = c[i,i_]; } } state.nic = state.nic+1; } } // // Normalize rows of State.CLEIC: each row must have unit norm. // Norm is calculated using first N elements (i.e. right part is // not counted when we calculate norm). // for(i=0; i<=k-1; i++) { v = 0; for(j=0; j<=n-1; j++) { v = v+math.sqr(state.cleic[i,j]); } if( (double)(v)==(double)(0) ) { continue; } v = 1/Math.Sqrt(v); for(i_=0; i_<=n;i_++) { state.cleic[i,i_] = v*state.cleic[i,i_]; } } sactivesets.sassetlc(state.sas, c, ct, k, _params); } /************************************************************************* This function sets stopping conditions for the optimizer. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if on k+1-th iteration the condition |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinBLEICSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection. NOTE: when SetCond() called with non-zero MaxIts, BLEIC solver may perform slightly more than MaxIts iterations. I.e., MaxIts sets non-strict limit on iterations count. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetcond(minbleicstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsg), "MinBLEICSetCond: EpsG is not finite number"); alglib.ap.assert((double)(epsg)>=(double)(0), "MinBLEICSetCond: negative EpsG"); alglib.ap.assert(math.isfinite(epsf), "MinBLEICSetCond: EpsF is not finite number"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinBLEICSetCond: negative EpsF"); alglib.ap.assert(math.isfinite(epsx), "MinBLEICSetCond: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinBLEICSetCond: negative EpsX"); alglib.ap.assert(maxits>=0, "MinBLEICSetCond: negative MaxIts!"); if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxits==0 ) { epsx = 1.0E-6; } state.epsg = epsg; state.epsf = epsf; state.epsx = epsx; state.maxits = maxits; } /************************************************************************* This function sets scaling coefficients for BLEIC optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. In most optimizers (and in the BLEIC too) scaling is NOT a form of preconditioning. It just affects stopping conditions. You should set preconditioner by separate call to one of the MinBLEICSetPrec...() functions. There is a special preconditioning mode, however, which uses scaling coefficients to form diagonal preconditioning matrix. You can turn this mode on, if you want. But you should understand that scaling is not the same thing as preconditioning - these are two different, although related forms of tuning solver. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetscale(minbleicstate state, double[] s, alglib.xparams _params) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.nmain, "MinBLEICSetScale: Length(S)=state.nmain, "MinBLEICSetPrecDiag: D is too short"); for(i=0; i<=state.nmain-1; i++) { alglib.ap.assert(math.isfinite(d[i]), "MinBLEICSetPrecDiag: D contains infinite or NAN elements"); alglib.ap.assert((double)(d[i])>(double)(0), "MinBLEICSetPrecDiag: D contains non-positive elements"); } apserv.rvectorsetlengthatleast(ref state.diagh, state.nmain, _params); state.prectype = 2; for(i=0; i<=state.nmain-1; i++) { state.diagh[i] = d[i]; } } /************************************************************************* Modification of the preconditioner: scale-based diagonal preconditioning. This preconditioning mode can be useful when you don't have approximate diagonal of Hessian, but you know that your variables are badly scaled (for example, one variable is in [1,10], and another in [1000,100000]), and most part of the ill-conditioning comes from different scales of vars. In this case simple scale-based preconditioner, with H[i] = 1/(s[i]^2), can greatly improve convergence. IMPRTANT: you should set scale of your variables with MinBLEICSetScale() call (before or after MinBLEICSetPrecScale() call). Without knowledge of the scale of your variables scale-based preconditioner will be just unit matrix. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetprecscale(minbleicstate state, alglib.xparams _params) { state.prectype = 3; } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinBLEICOptimize(). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetxrep(minbleicstate state, bool needxrep, alglib.xparams _params) { state.xrep = needxrep; } /************************************************************************* This function turns on/off line search reports. These reports are described in more details in developer-only comments on MinBLEICState object. INPUT PARAMETERS: State - structure which stores algorithm state NeedDRep- whether line search reports are needed or not This function is intended for private use only. Turning it on artificially may cause program failure. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetdrep(minbleicstate state, bool needdrep, alglib.xparams _params) { state.drep = needdrep; } /************************************************************************* This function sets maximum step length IMPORTANT: this feature is hard to combine with preconditioning. You can't set upper limit on step length, when you solve optimization problem with linear (non-boundary) constraints AND preconditioner turned on. When non-boundary constraints are present, you have to either a) use preconditioner, or b) use upper limit on step length. YOU CAN'T USE BOTH! In this case algorithm will terminate with appropriate error code. INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which lead to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetstpmax(minbleicstate state, double stpmax, alglib.xparams _params) { alglib.ap.assert(math.isfinite(stpmax), "MinBLEICSetStpMax: StpMax is not finite!"); alglib.ap.assert((double)(stpmax)>=(double)(0), "MinBLEICSetStpMax: StpMax<0!"); state.stpmax = stpmax; } /************************************************************************* NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied gradient, and one which uses function value only and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object (either MinBLEICCreate() for analytical gradient or MinBLEICCreateF() for numerical differentiation) you should choose appropriate variant of MinBLEICOptimize() - one which accepts function AND gradient or one which accepts function ONLY. Be careful to choose variant of MinBLEICOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinBLEICOptimize() and specific function used to create optimizer. | USER PASSED TO MinBLEICOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinBLEICCreateF() | work FAIL MinBLEICCreate() | FAIL work Here "FAIL" denotes inappropriate combinations of optimizer creation function and MinBLEICOptimize() version. Attemps to use such combination (for example, to create optimizer with MinBLEICCreateF() and to pass gradient information to MinBLEICOptimize()) will lead to exception being thrown. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static bool minbleiciteration(minbleicstate state, alglib.xparams _params) { bool result = new bool(); int n = 0; int m = 0; int i = 0; int j = 0; double v = 0; double vv = 0; double v0 = 0; bool b = new bool(); int mcinfo = 0; int actstatus = 0; int itidx = 0; double penalty = 0; double ginit = 0; double gdecay = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { n = state.rstate.ia[0]; m = state.rstate.ia[1]; i = state.rstate.ia[2]; j = state.rstate.ia[3]; mcinfo = state.rstate.ia[4]; actstatus = state.rstate.ia[5]; itidx = state.rstate.ia[6]; b = state.rstate.ba[0]; v = state.rstate.ra[0]; vv = state.rstate.ra[1]; v0 = state.rstate.ra[2]; penalty = state.rstate.ra[3]; ginit = state.rstate.ra[4]; gdecay = state.rstate.ra[5]; } else { n = 359; m = -58; i = -919; j = -909; mcinfo = 81; actstatus = 255; itidx = 74; b = false; v = 809; vv = 205; v0 = -838; penalty = 939; ginit = -526; gdecay = 763; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } if( state.rstate.stage==4 ) { goto lbl_4; } if( state.rstate.stage==5 ) { goto lbl_5; } if( state.rstate.stage==6 ) { goto lbl_6; } if( state.rstate.stage==7 ) { goto lbl_7; } if( state.rstate.stage==8 ) { goto lbl_8; } if( state.rstate.stage==9 ) { goto lbl_9; } if( state.rstate.stage==10 ) { goto lbl_10; } if( state.rstate.stage==11 ) { goto lbl_11; } if( state.rstate.stage==12 ) { goto lbl_12; } if( state.rstate.stage==13 ) { goto lbl_13; } if( state.rstate.stage==14 ) { goto lbl_14; } if( state.rstate.stage==15 ) { goto lbl_15; } if( state.rstate.stage==16 ) { goto lbl_16; } if( state.rstate.stage==17 ) { goto lbl_17; } if( state.rstate.stage==18 ) { goto lbl_18; } if( state.rstate.stage==19 ) { goto lbl_19; } if( state.rstate.stage==20 ) { goto lbl_20; } if( state.rstate.stage==21 ) { goto lbl_21; } // // Routine body // // // Algorithm parameters: // * M number of L-BFGS corrections. // This coefficient remains fixed during iterations. // * GDecay desired decrease of constrained gradient during L-BFGS iterations. // This coefficient is decreased after each L-BFGS round until // it reaches minimum decay. // m = Math.Min(5, state.nmain); gdecay = initialdecay; // // Init // n = state.nmain; state.steepestdescentstep = false; state.userterminationneeded = false; state.repterminationtype = 0; state.repinneriterationscount = 0; state.repouteriterationscount = 0; state.repnfev = 0; state.repvaridx = -1; state.repdebugeqerr = 0.0; state.repdebugfs = Double.NaN; state.repdebugff = Double.NaN; state.repdebugdx = Double.NaN; if( (double)(state.stpmax)!=(double)(0) && state.prectype!=0 ) { state.repterminationtype = -10; result = false; return result; } apserv.rmatrixsetlengthatleast(ref state.bufyk, m+1, n, _params); apserv.rmatrixsetlengthatleast(ref state.bufsk, m+1, n, _params); apserv.rvectorsetlengthatleast(ref state.bufrho, m, _params); apserv.rvectorsetlengthatleast(ref state.buftheta, m, _params); apserv.rvectorsetlengthatleast(ref state.tmp0, n, _params); optserv.smoothnessmonitorinit(state.smonitor, n, 1, state.smoothnessguardlevel>0, _params); for(i=0; i<=n-1; i++) { state.lastscaleused[i] = state.s[i]; state.invs[i] = 1/state.s[i]; } // // Check analytic derivative // clearrequestfields(state, _params); if( !((double)(state.diffstep)==(double)(0) && (double)(state.teststep)>(double)(0)) ) { goto lbl_22; } lbl_24: if( !optserv.smoothnessmonitorcheckgradientatx0(state.smonitor, state.xstart, state.s, state.bndl, state.bndu, true, state.teststep, _params) ) { goto lbl_25; } for(i=0; i<=n-1; i++) { state.x[i] = state.smonitor.x[i]; } state.needfg = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needfg = false; state.smonitor.fi[0] = state.f; for(i=0; i<=n-1; i++) { state.smonitor.j[0,i] = state.g[i]; } goto lbl_24; lbl_25: lbl_22: // // Fill TmpPrec with current preconditioner // apserv.rvectorsetlengthatleast(ref state.tmpprec, n, _params); for(i=0; i<=n-1; i++) { if( state.prectype==2 ) { state.tmpprec[i] = state.diagh[i]; continue; } if( state.prectype==3 ) { state.tmpprec[i] = 1/math.sqr(state.s[i]); continue; } state.tmpprec[i] = 1; } sactivesets.sassetprecdiag(state.sas, state.tmpprec, _params); // // Start optimization // if( !sactivesets.sasstartoptimization(state.sas, state.xstart, _params) ) { state.repterminationtype = -3; result = false; return result; } // // Main cycle of BLEIC-PG algorithm // state.repterminationtype = 0; state.lastgoodstep = 0; state.lastscaledgoodstep = 0; state.maxscaledgrad = 0; state.nonmonotoniccnt = (int)Math.Round(1.5*(n+state.nic))+5; for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.sas.xc[i_]; } clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_26; } state.needfg = true; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: state.needfg = false; goto lbl_27; lbl_26: state.needf = true; state.rstate.stage = 2; goto lbl_rcomm; lbl_2: state.needf = false; lbl_27: state.fc = state.f; optserv.trimprepare(state.f, ref state.trimthreshold, _params); state.repnfev = state.repnfev+1; if( !state.xrep ) { goto lbl_28; } // // Report current point // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.sas.xc[i_]; } state.f = state.fc; state.xupdated = true; state.rstate.stage = 3; goto lbl_rcomm; lbl_3: state.xupdated = false; lbl_28: if( state.userterminationneeded ) { // // User requested termination // sactivesets.sasstopoptimization(state.sas, _params); state.repterminationtype = 8; result = false; return result; } lbl_30: if( false ) { goto lbl_31; } // // Preparations // // (a) calculate unconstrained gradient // (b) determine initial active set // (c) update MaxScaledGrad // (d) check F/G for NAN/INF, abnormally terminate algorithm if needed // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.sas.xc[i_]; } clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_32; } // // Analytic gradient // state.needfg = true; state.rstate.stage = 4; goto lbl_rcomm; lbl_4: state.needfg = false; goto lbl_33; lbl_32: // // Numerical differentiation // state.needf = true; state.rstate.stage = 5; goto lbl_rcomm; lbl_5: state.fbase = state.f; i = 0; lbl_34: if( i>n-1 ) { goto lbl_36; } v = state.x[i]; b = false; if( state.hasbndl[i] ) { b = b || (double)(v-state.diffstep*state.s[i])<(double)(state.bndl[i]); } if( state.hasbndu[i] ) { b = b || (double)(v+state.diffstep*state.s[i])>(double)(state.bndu[i]); } if( b ) { goto lbl_37; } state.x[i] = v-state.diffstep*state.s[i]; state.rstate.stage = 6; goto lbl_rcomm; lbl_6: state.fm2 = state.f; state.x[i] = v-0.5*state.diffstep*state.s[i]; state.rstate.stage = 7; goto lbl_rcomm; lbl_7: state.fm1 = state.f; state.x[i] = v+0.5*state.diffstep*state.s[i]; state.rstate.stage = 8; goto lbl_rcomm; lbl_8: state.fp1 = state.f; state.x[i] = v+state.diffstep*state.s[i]; state.rstate.stage = 9; goto lbl_rcomm; lbl_9: state.fp2 = state.f; state.g[i] = (8*(state.fp1-state.fm1)-(state.fp2-state.fm2))/(6*state.diffstep*state.s[i]); goto lbl_38; lbl_37: state.xm1 = v-state.diffstep*state.s[i]; state.xp1 = v+state.diffstep*state.s[i]; if( state.hasbndl[i] && (double)(state.xm1)<(double)(state.bndl[i]) ) { state.xm1 = state.bndl[i]; } if( state.hasbndu[i] && (double)(state.xp1)>(double)(state.bndu[i]) ) { state.xp1 = state.bndu[i]; } state.x[i] = state.xm1; state.rstate.stage = 10; goto lbl_rcomm; lbl_10: state.fm1 = state.f; state.x[i] = state.xp1; state.rstate.stage = 11; goto lbl_rcomm; lbl_11: state.fp1 = state.f; if( (double)(state.xm1)!=(double)(state.xp1) ) { state.g[i] = (state.fp1-state.fm1)/(state.xp1-state.xm1); } else { state.g[i] = 0; } lbl_38: state.x[i] = v; i = i+1; goto lbl_34; lbl_36: state.f = state.fbase; state.needf = false; lbl_33: state.fc = state.f; for(i_=0; i_<=n-1;i_++) { state.ugc[i_] = state.g[i_]; } for(i_=0; i_<=n-1;i_++) { state.cgc[i_] = state.g[i_]; } sactivesets.sasreactivateconstraintsprec(state.sas, state.ugc, _params); sactivesets.sasconstraineddirection(state.sas, ref state.cgc, _params); ginit = 0.0; for(i=0; i<=n-1; i++) { ginit = ginit+math.sqr(state.cgc[i]*state.s[i]); } ginit = Math.Sqrt(ginit); state.maxscaledgrad = Math.Max(state.maxscaledgrad, ginit); if( !math.isfinite(ginit) || !math.isfinite(state.fc) ) { // // Abnormal termination - infinities in function/gradient // sactivesets.sasstopoptimization(state.sas, _params); state.repterminationtype = -8; result = false; return result; } if( state.userterminationneeded ) { // // User requested termination // sactivesets.sasstopoptimization(state.sas, _params); state.repterminationtype = 8; result = false; return result; } // // LBFGS stage: // * during LBFGS iterations we activate new constraints, but never // deactivate already active ones. // * we perform at most N iterations of LBFGS before re-evaluating // active set and restarting LBFGS. // * first iteration of LBFGS is a special - it is performed with // minimum set of active constraints, algorithm termination can // be performed only at this state. We call this iteration // "steepest descent step". // // About termination: // * LBFGS iterations can be terminated because of two reasons: // * "termination" - non-zero termination code in RepTerminationType, // which means that optimization is done // * "restart" - zero RepTerminationType, which means that we // have to re-evaluate active set and resume LBFGS stage. // * one more option is "refresh" - to continue LBFGS iterations, // but with all BFGS updates (Sk/Yk pairs) being dropped; // it happens after changes in active set // state.bufsize = 0; state.steepestdescentstep = true; itidx = -1; lbl_39: if( itidx>=n-1 ) { goto lbl_40; } // // Increment iterations counter // // NOTE: we have strong reasons to use such complex scheme // instead of just for() loop - this counter may be // decreased at some occasions to perform "restart" // of an iteration. // itidx = itidx+1; // // At the beginning of each iteration: // * SAS.XC stores current point // * FC stores current function value // * UGC stores current unconstrained gradient // * CGC stores current constrained gradient // * D stores constrained step direction (calculated at this block) // // // Check gradient-based stopping criteria // // This stopping condition is tested only for step which is the // first step of LBFGS (subsequent steps may accumulate active // constraints thus they should NOT be used for stopping - gradient // may be small when constrained, but these constraints may be // deactivated by the subsequent steps) // if( state.steepestdescentstep && (double)(sactivesets.sasscaledconstrainednorm(state.sas, state.ugc, _params))<=(double)(state.epsg) ) { // // Gradient is small enough. // Optimization is terminated // state.repterminationtype = 4; goto lbl_40; } // // 1. Calculate search direction D according to L-BFGS algorithm // using constrained preconditioner to perform inner multiplication. // 2. Evaluate scaled length of direction D; restart LBFGS if D is zero // (it may be possible that we found minimum, but it is also possible // that some constraints need deactivation) // 3. If D is non-zero, try to use previous scaled step length as initial estimate for new step. // for(i_=0; i_<=n-1;i_++) { state.work[i_] = state.cgc[i_]; } for(i=state.bufsize-1; i>=0; i--) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.bufsk[i,i_]*state.work[i_]; } state.buftheta[i] = v; vv = v*state.bufrho[i]; for(i_=0; i_<=n-1;i_++) { state.work[i_] = state.work[i_] - vv*state.bufyk[i,i_]; } } sactivesets.sasconstraineddirectionprec(state.sas, ref state.work, _params); for(i=0; i<=state.bufsize-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.bufyk[i,i_]*state.work[i_]; } vv = state.bufrho[i]*(-v+state.buftheta[i]); for(i_=0; i_<=n-1;i_++) { state.work[i_] = state.work[i_] + vv*state.bufsk[i,i_]; } } sactivesets.sasconstraineddirection(state.sas, ref state.work, _params); for(i_=0; i_<=n-1;i_++) { state.d[i_] = -state.work[i_]; } v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.d[i]/state.s[i]); } v = Math.Sqrt(v); if( (double)(v)==(double)(0) ) { // // Search direction is zero. // If we perform "steepest descent step", algorithm is terminated. // Otherwise we just restart LBFGS. // if( state.steepestdescentstep ) { state.repterminationtype = 4; } goto lbl_40; } alglib.ap.assert((double)(v)>(double)(0), "MinBLEIC: internal error"); if( (double)(state.lastscaledgoodstep)>(double)(0) && (double)(v)>(double)(0) ) { state.stp = state.lastscaledgoodstep/v; } else { state.stp = 1.0/v; } // // Calculate bound on step length. // Step direction is stored // sactivesets.sasexploredirection(state.sas, state.d, ref state.curstpmax, ref state.cidx, ref state.cval, _params); state.activationstep = state.curstpmax; if( state.cidx>=0 && (double)(state.activationstep)==(double)(0) ) { // // We are exactly at the boundary, immediate activation // of constraint is required. LBFGS stage is continued // with "refreshed" model. // // ! IMPORTANT: we do not clear SteepestDescent flag here, // ! it is very important for correct stopping // ! of algorithm. // // ! IMPORTANT: we decrease iteration counter in order to // preserve computational budget for iterations. // sactivesets.sasimmediateactivation(state.sas, state.cidx, state.cval, _params); state.bufsize = 0; itidx = itidx-1; goto lbl_39; } if( (double)(state.stpmax)>(double)(0) ) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.d[i_]*state.d[i_]; } v = Math.Sqrt(v); if( (double)(v)>(double)(0) ) { state.curstpmax = Math.Min(state.curstpmax, state.stpmax/v); } } // // Report beginning of line search (if requested by caller). // See description of the MinBLEICState for more information // about fields accessible to caller. // // Caller may do following: // * change State.Stp and load better initial estimate of // the step length. // Caller may not terminate algorithm. // if( !state.drep ) { goto lbl_41; } clearrequestfields(state, _params); state.lsstart = true; state.boundedstep = state.cidx>=0; for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.sas.xc[i_]; } state.rstate.stage = 12; goto lbl_rcomm; lbl_12: state.lsstart = false; lbl_41: // // Minimize F(x+alpha*d) // for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.sas.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.cgn[i_] = state.cgc[i_]; } for(i_=0; i_<=n-1;i_++) { state.ugn[i_] = state.ugc[i_]; } state.fn = state.fc; state.mcstage = 0; optserv.smoothnessmonitorstartlinesearch1u(state.smonitor, state.s, state.invs, state.xn, state.fn, state.ugn, _params); linmin.mcsrch(n, ref state.xn, ref state.fn, ref state.ugn, state.d, ref state.stp, state.curstpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); lbl_43: if( state.mcstage==0 ) { goto lbl_44; } // // Perform correction (constraints are enforced) // Copy XN to X // sactivesets.sascorrection(state.sas, state.xn, ref penalty, _params); for(i=0; i<=n-1; i++) { state.x[i] = state.xn[i]; } // // Gradient, either user-provided or numerical differentiation // clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_45; } // // Analytic gradient // state.needfg = true; state.rstate.stage = 13; goto lbl_rcomm; lbl_13: state.needfg = false; state.repnfev = state.repnfev+1; goto lbl_46; lbl_45: // // Numerical differentiation // state.needf = true; state.rstate.stage = 14; goto lbl_rcomm; lbl_14: state.fbase = state.f; i = 0; lbl_47: if( i>n-1 ) { goto lbl_49; } v = state.x[i]; b = false; if( state.hasbndl[i] ) { b = b || (double)(v-state.diffstep*state.s[i])<(double)(state.bndl[i]); } if( state.hasbndu[i] ) { b = b || (double)(v+state.diffstep*state.s[i])>(double)(state.bndu[i]); } if( b ) { goto lbl_50; } state.x[i] = v-state.diffstep*state.s[i]; state.rstate.stage = 15; goto lbl_rcomm; lbl_15: state.fm2 = state.f; state.x[i] = v-0.5*state.diffstep*state.s[i]; state.rstate.stage = 16; goto lbl_rcomm; lbl_16: state.fm1 = state.f; state.x[i] = v+0.5*state.diffstep*state.s[i]; state.rstate.stage = 17; goto lbl_rcomm; lbl_17: state.fp1 = state.f; state.x[i] = v+state.diffstep*state.s[i]; state.rstate.stage = 18; goto lbl_rcomm; lbl_18: state.fp2 = state.f; state.g[i] = (8*(state.fp1-state.fm1)-(state.fp2-state.fm2))/(6*state.diffstep*state.s[i]); state.repnfev = state.repnfev+4; goto lbl_51; lbl_50: state.xm1 = v-state.diffstep*state.s[i]; state.xp1 = v+state.diffstep*state.s[i]; if( state.hasbndl[i] && (double)(state.xm1)<(double)(state.bndl[i]) ) { state.xm1 = state.bndl[i]; } if( state.hasbndu[i] && (double)(state.xp1)>(double)(state.bndu[i]) ) { state.xp1 = state.bndu[i]; } state.x[i] = state.xm1; state.rstate.stage = 19; goto lbl_rcomm; lbl_19: state.fm1 = state.f; state.x[i] = state.xp1; state.rstate.stage = 20; goto lbl_rcomm; lbl_20: state.fp1 = state.f; if( (double)(state.xm1)!=(double)(state.xp1) ) { state.g[i] = (state.fp1-state.fm1)/(state.xp1-state.xm1); } else { state.g[i] = 0; } state.repnfev = state.repnfev+2; lbl_51: state.x[i] = v; i = i+1; goto lbl_47; lbl_49: state.f = state.fbase; state.needf = false; lbl_46: // // Back to MCSRCH // // NOTE: penalty term from correction is added to FN in order // to penalize increase in infeasibility. // optserv.smoothnessmonitorenqueuepoint1u(state.smonitor, state.s, state.invs, state.d, state.stp, state.x, state.f, state.g, _params); state.fn = state.f+penaltyfactor*state.maxscaledgrad*penalty; for(i_=0; i_<=n-1;i_++) { state.cgn[i_] = state.g[i_]; } for(i_=0; i_<=n-1;i_++) { state.ugn[i_] = state.g[i_]; } sactivesets.sasconstraineddirection(state.sas, ref state.cgn, _params); optserv.trimfunction(ref state.fn, ref state.cgn, n, state.trimthreshold, _params); linmin.mcsrch(n, ref state.xn, ref state.fn, ref state.ugn, state.d, ref state.stp, state.curstpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); goto lbl_43; lbl_44: for(i_=0; i_<=n-1;i_++) { state.bufsk[state.bufsize,i_] = -state.sas.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.bufyk[state.bufsize,i_] = -state.cgc[i_]; } for(i_=0; i_<=n-1;i_++) { state.bufsk[state.bufsize,i_] = state.bufsk[state.bufsize,i_] + state.xn[i_]; } for(i_=0; i_<=n-1;i_++) { state.bufyk[state.bufsize,i_] = state.bufyk[state.bufsize,i_] + state.cgn[i_]; } optserv.smoothnessmonitorfinalizelinesearch(state.smonitor, _params); // // Check for presence of NAN/INF in function/gradient // v = state.fn; for(i=0; i<=n-1; i++) { v = 0.1*v+state.ugn[i]; } if( !math.isfinite(v) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; goto lbl_40; } // // Handle possible failure of the line search or request for termination // if( mcinfo!=1 && mcinfo!=5 ) { // // We can not find step which decreases function value. We have // two possibilities: // (a) numerical properties of the function do not allow us to // find good step. // (b) we are close to activation of some constraint, and it is // so close that step which activates it leads to change in // target function which is smaller than numerical noise. // // Optimization algorithm must be able to handle case (b), because // inability to handle it will cause failure when algorithm // started very close to boundary of the feasible area. // // In order to correctly handle such cases we allow limited amount // of small steps which increase function value. // v = 0.0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.d[i]*state.curstpmax/state.s[i]); } v = Math.Sqrt(v); b = false; if( (state.cidx>=0 && (double)(v)<=(double)(maxnonmonotoniclen)) && state.nonmonotoniccnt>0 ) { // // We try to enforce non-monotonic step: // * Stp := CurStpMax // * MCINFO := 5 // * XN := XC+CurStpMax*D // * non-monotonic counter is decreased // // NOTE: UGN/CGN are not updated because step is so short that we assume that // GN is approximately equal to GC. // // NOTE: prior to enforcing such step we check that it does not increase infeasibility // of constraints beyond tolerable level // v = state.curstpmax; for(i_=0; i_<=n-1;i_++) { state.tmp0[i_] = state.sas.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.tmp0[i_] = state.tmp0[i_] + v*state.d[i_]; } if( (double)(feasibilityerror(state.tmp0, state.s, n, state.cleic, state.nec, state.nic, _params))<=(double)(nmstol*Math.Sqrt(n)*math.machineepsilon) ) { state.stp = state.curstpmax; mcinfo = 5; for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.tmp0[i_]; } state.nonmonotoniccnt = state.nonmonotoniccnt-1; b = true; } } if( !b ) { // // Numerical properties of the function do not allow // us to solve problem. Here we have two possibilities: // * if it is "steepest descent" step, we can terminate // algorithm because we are close to minimum // * if it is NOT "steepest descent" step, we should restart // LBFGS iterations. // if( state.steepestdescentstep ) { // // Algorithm is terminated // state.repterminationtype = 7; goto lbl_40; } else { // // Re-evaluate active set and restart LBFGS // goto lbl_40; } } } if( state.userterminationneeded ) { goto lbl_40; } // // Current point is updated: // * move XC/FC/GC to XP/FP/GP // * change current point remembered by SAS structure // * move XN/FN/GN to XC/FC/GC // * report current point and update iterations counter // * if MCINFO=1, push new pair SK/YK to LBFGS buffer // state.fp = state.fc; for(i_=0; i_<=n-1;i_++) { state.xp[i_] = state.sas.xc[i_]; } state.fc = state.fn; for(i_=0; i_<=n-1;i_++) { state.cgc[i_] = state.cgn[i_]; } for(i_=0; i_<=n-1;i_++) { state.ugc[i_] = state.ugn[i_]; } actstatus = sactivesets.sasmoveto(state.sas, state.xn, state.cidx>=0 && (double)(state.stp)>=(double)(state.activationstep), state.cidx, state.cval, _params); if( !state.xrep ) { goto lbl_52; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.sas.xc[i_]; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 21; goto lbl_rcomm; lbl_21: state.xupdated = false; lbl_52: state.repinneriterationscount = state.repinneriterationscount+1; if( mcinfo==1 ) { // // Accept new LBFGS update given by Sk,Yk // if( state.bufsize==m ) { // // Buffer is full, shift contents by one row // for(i=0; i<=state.bufsize-1; i++) { for(i_=0; i_<=n-1;i_++) { state.bufsk[i,i_] = state.bufsk[i+1,i_]; } for(i_=0; i_<=n-1;i_++) { state.bufyk[i,i_] = state.bufyk[i+1,i_]; } } for(i=0; i<=state.bufsize-2; i++) { state.bufrho[i] = state.bufrho[i+1]; state.buftheta[i] = state.buftheta[i+1]; } } else { // // Buffer is not full, increase buffer size by 1 // state.bufsize = state.bufsize+1; } v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.bufyk[state.bufsize-1,i_]*state.bufsk[state.bufsize-1,i_]; } vv = 0.0; for(i_=0; i_<=n-1;i_++) { vv += state.bufyk[state.bufsize-1,i_]*state.bufyk[state.bufsize-1,i_]; } if( (double)(v)==(double)(0) || (double)(vv)==(double)(0) ) { // // Strange internal error in LBFGS - either YK=0 // (which should not have been) or (SK,YK)=0 (again, // unexpected). It should not take place because // MCINFO=1, which signals "good" step. But just // to be sure we have special branch of code which // restarts LBFGS // goto lbl_40; } state.bufrho[state.bufsize-1] = 1/v; alglib.ap.assert(state.bufsize<=m, "MinBLEIC: internal error"); // // Update length of the good step // v = 0; vv = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr((state.sas.xc[i]-state.xp[i])/state.s[i]); vv = vv+math.sqr(state.sas.xc[i]-state.xp[i]); } state.lastgoodstep = Math.Sqrt(vv); updateestimateofgoodstep(ref state.lastscaledgoodstep, Math.Sqrt(v), _params); } // // Check stopping criteria // // Step size and function-based stopping criteria are tested only // for step which satisfies Wolfe conditions and is the first step of // LBFGS (subsequent steps may accumulate active constraints thus // they should NOT be used for stopping; step size or function change // may be small when constrained, but these constraints may be // deactivated by the subsequent steps). // // MaxIts-based stopping condition is checked for all kinds of steps. // if( mcinfo==1 && state.steepestdescentstep ) { // // Step is small enough // v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr((state.sas.xc[i]-state.xp[i])/state.s[i]); } v = Math.Sqrt(v); if( (double)(v)<=(double)(state.epsx) ) { state.repterminationtype = 2; goto lbl_40; } // // Function change is small enough // if( (double)(Math.Abs(state.fp-state.fc))<=(double)(state.epsf*Math.Max(Math.Abs(state.fc), Math.Max(Math.Abs(state.fp), 1.0))) ) { state.repterminationtype = 1; goto lbl_40; } } if( state.maxits>0 && state.repinneriterationscount>=state.maxits ) { state.repterminationtype = 5; goto lbl_40; } // // Clear "steepest descent" flag. // state.steepestdescentstep = false; // // Smooth reset (LBFGS memory model is refreshed) or hard restart: // * LBFGS model is refreshed, if line search was performed with activation of constraints // * algorithm is restarted if scaled gradient decreased below GDecay // if( actstatus>=0 ) { state.bufsize = 0; goto lbl_39; } v = 0.0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.cgc[i]*state.s[i]); } if( (double)(Math.Sqrt(v))<(double)(gdecay*ginit) ) { goto lbl_40; } goto lbl_39; lbl_40: if( state.userterminationneeded ) { // // User requested termination // state.repterminationtype = 8; goto lbl_31; } if( state.repterminationtype!=0 ) { // // Algorithm terminated // goto lbl_31; } // // Decrease decay coefficient. Subsequent L-BFGS stages will // have more stringent stopping criteria. // gdecay = Math.Max(gdecay*decaycorrection, mindecay); goto lbl_30; lbl_31: sactivesets.sasstopoptimization(state.sas, _params); state.repouteriterationscount = 1; result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = n; state.rstate.ia[1] = m; state.rstate.ia[2] = i; state.rstate.ia[3] = j; state.rstate.ia[4] = mcinfo; state.rstate.ia[5] = actstatus; state.rstate.ia[6] = itidx; state.rstate.ba[0] = b; state.rstate.ra[0] = v; state.rstate.ra[1] = vv; state.rstate.ra[2] = v0; state.rstate.ra[3] = penalty; state.rstate.ra[4] = ginit; state.rstate.ra[5] = gdecay; return result; } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with minbleicoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minbleicsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardgradient(minbleicstate state, double teststep, alglib.xparams _params) { alglib.ap.assert(math.isfinite(teststep), "MinBLEICOptGuardGradient: TestStep contains NaN or INF"); alglib.ap.assert((double)(teststep)>=(double)(0), "MinBLEICOptGuardGradient: invalid argument TestStep(TestStep<0)"); state.teststep = teststep; } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) b) nonsmooth target function (non-C1) Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardsmoothness(minbleicstate state, int level, alglib.xparams _params) { alglib.ap.assert(level==0 || level==1, "MinBLEICOptGuardSmoothness: unexpected value of level parameter"); state.smoothnessguardlevel = level; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * minbleicoptguardgradient() for gradient verification * minbleicoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradvidx for specific variable (gradient element) suspected * rep.badgradxbase, a point where gradient is tested * rep.badgraduser, user-provided gradient (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.badgradnum, reference gradient obtained via numerical differentiation (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.nonc0suspected * rep.nonc1suspected === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * minbleicoptguardnonc1test0results() * minbleicoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardresults(minbleicstate state, optguardapi.optguardreport rep, alglib.xparams _params) { optserv.smoothnessmonitorexportreport(state.smonitor, rep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardnonc1test0results(minbleicstate state, optguardapi.optguardnonc1test0report strrep, optguardapi.optguardnonc1test0report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbleicoptguardnonc1test1results(minbleicstate state, optguardapi.optguardnonc1test1report strrep, optguardapi.optguardnonc1test1report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* BLEIC results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report. You should check Rep.TerminationType in order to distinguish successful termination from unsuccessful one: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -3 inconsistent constraints. Feasible point is either nonexistent or too hard to find. Try to restart optimizer with better initial approximation * 1 relative function improvement is no more than EpsF. * 2 scaled step is no more than EpsX. * 4 scaled gradient norm is no more than EpsG. * 5 MaxIts steps was taken * 8 terminated by user who called minbleicrequesttermination(). X contains point which was "current accepted" when termination request was submitted. More information about fields of this structure can be found in the comments on MinBLEICReport datatype. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicresults(minbleicstate state, ref double[] x, minbleicreport rep, alglib.xparams _params) { x = new double[0]; minbleicresultsbuf(state, ref x, rep, _params); } /************************************************************************* BLEIC results Buffered implementation of MinBLEICResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicresultsbuf(minbleicstate state, ref double[] x, minbleicreport rep, alglib.xparams _params) { int i = 0; int i_ = 0; if( alglib.ap.len(x)0 ) { for(i_=0; i_<=state.nmain-1;i_++) { x[i_] = state.sas.xc[i_]; } } else { for(i=0; i<=state.nmain-1; i++) { x[i] = Double.NaN; } } rep.debugeqerr = state.repdebugeqerr; rep.debugfs = state.repdebugfs; rep.debugff = state.repdebugff; rep.debugdx = state.repdebugdx; rep.debugfeasqpits = state.repdebugfeasqpits; rep.debugfeasgpaits = state.repdebugfeasgpaits; } /************************************************************************* This subroutine restarts algorithm from new point. All optimization parameters (including constraints) are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure previously allocated with MinBLEICCreate call. X - new starting point. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicrestartfrom(minbleicstate state, double[] x, alglib.xparams _params) { int n = 0; int i_ = 0; n = state.nmain; // // First, check for errors in the inputs // alglib.ap.assert(alglib.ap.len(x)>=n, "MinBLEICRestartFrom: Length(X)(double)(estimate*100) ) { estimate = estimate*100; return; } estimate = newstep; } /************************************************************************* This subroutine estimates relative feasibility error of the point. INPUT PARAMETERS: X - current point (unscaled) S - scale vector N - dimensionality CLEIC - array[NEC+NIC,N+1], constraint matrix, may be unnormalized NEC - number of equality constraints (come first) NIC - number of inequality constraints (come last) RESULT feasibility error, good value is ~1E-16...1E-14 -- ALGLIB -- Copyright 16.01.2013 by Bochkanov Sergey *************************************************************************/ private static double feasibilityerror(double[] x, double[] s, int n, double[,] cleic, int nec, int nic, alglib.xparams _params) { double result = 0; int i = 0; int j = 0; double v = 0; double v0 = 0; double v1 = 0; double vc = 0; double vx = 0; result = 0; for(i=0; i<=nec+nic-1; i++) { v = -cleic[i,n]; v0 = 0; v1 = 0; for(j=0; j<=n-1; j++) { vc = cleic[i,j]*s[j]; vx = x[j]/s[j]; v = v+vc*vx; v0 = v0+math.sqr(vc); v1 = v1+math.sqr(vx); } v0 = apserv.coalesce(Math.Sqrt(v0), 1, _params); v1 = Math.Max(Math.Sqrt(v1), 1); if( i0 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, alglib.xparams _params) { 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, _params); 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, _params); apserv.rvectorsetlengthatleast(ref sstate.tmp0, n, _params); apserv.rvectorsetlengthatleast(ref sstate.tmp1, n, _params); 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, _params); minbleic.minbleicsetbc(sstate.solver, bndl, bndu, _params); minbleic.minbleicsetdrep(sstate.solver, true, _params); minbleic.minbleicsetcond(sstate.solver, math.minrealnumber, 0.0, 0.0, settings.maxits, _params); minbleic.minbleicsetscale(sstate.solver, s, _params); minbleic.minbleicsetprecscale(sstate.solver, _params); minbleic.minbleicrestartfrom(sstate.solver, xs, _params); while( minbleic.minbleiciteration(sstate.solver, _params) ) { // // 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, _params); if( sstate.solver.steepestdescentstep ) { apserv.inc(ref sstate.repouteriterationscount, _params); } // // 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, sstate.tmp0, _params); } if( akind==1 ) { sparse.sparsesmv(sparsea, sparseaupper, sstate.solver.d, ref sstate.tmp0, _params); 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, _params); // // 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, _params); } } // // 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, _params); } if( akind==1 ) { sparse.sparsesmv(sparsea, sparseaupper, sstate.tmp0, ref sstate.tmp1, _params); } 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, _params); 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, _params); } } } public class minqp { /************************************************************************* This object stores nonlinear optimizer state. You should use functions provided by MinQP subpackage to work with this object *************************************************************************/ public class minqpstate : apobject { public int n; public qqpsolver.qqpsettings qqpsettingsuser; public qpbleicsolver.qpbleicsettings qpbleicsettingsuser; public qpdenseaulsolver.qpdenseaulsettings qpdenseaulsettingsuser; public bool dbgskipconstraintnormalization; public int algokind; public int akind; public cqmodels.convexquadraticmodel a; public sparse.sparsematrix sparsea; public bool sparseaupper; public double absamax; public double absasum; public double absasum2; public double[] b; public double[] bndl; public double[] bndu; public int stype; public double[] s; public bool[] havebndl; public bool[] havebndu; public double[] xorigin; public double[] startx; public bool havex; public double[,] cleic; public int nec; public int nic; public sparse.sparsematrix scleic; public int snec; public int snic; public double[] xs; public int repinneriterationscount; public int repouteriterationscount; public int repncholesky; public int repnmv; public int repterminationtype; public double[] effectives; public double[] tmp0; public double[,] ecleic; public double[,] dummyr2; public bool qpbleicfirstcall; public qpbleicsolver.qpbleicbuffers qpbleicbuf; public qqpsolver.qqpbuffers qqpbuf; public qpdenseaulsolver.qpdenseaulbuffers qpdenseaulbuf; public minqpstate() { init(); } public override void init() { qqpsettingsuser = new qqpsolver.qqpsettings(); qpbleicsettingsuser = new qpbleicsolver.qpbleicsettings(); qpdenseaulsettingsuser = new qpdenseaulsolver.qpdenseaulsettings(); a = new cqmodels.convexquadraticmodel(); sparsea = new sparse.sparsematrix(); b = new double[0]; bndl = new double[0]; bndu = new double[0]; s = new double[0]; havebndl = new bool[0]; havebndu = new bool[0]; xorigin = new double[0]; startx = new double[0]; cleic = new double[0,0]; scleic = new sparse.sparsematrix(); xs = new double[0]; effectives = new double[0]; tmp0 = new double[0]; ecleic = new double[0,0]; dummyr2 = new double[0,0]; qpbleicbuf = new qpbleicsolver.qpbleicbuffers(); qqpbuf = new qqpsolver.qqpbuffers(); qpdenseaulbuf = new qpdenseaulsolver.qpdenseaulbuffers(); } public override alglib.apobject make_copy() { minqpstate _result = new minqpstate(); _result.n = n; _result.qqpsettingsuser = (qqpsolver.qqpsettings)qqpsettingsuser.make_copy(); _result.qpbleicsettingsuser = (qpbleicsolver.qpbleicsettings)qpbleicsettingsuser.make_copy(); _result.qpdenseaulsettingsuser = (qpdenseaulsolver.qpdenseaulsettings)qpdenseaulsettingsuser.make_copy(); _result.dbgskipconstraintnormalization = dbgskipconstraintnormalization; _result.algokind = algokind; _result.akind = akind; _result.a = (cqmodels.convexquadraticmodel)a.make_copy(); _result.sparsea = (sparse.sparsematrix)sparsea.make_copy(); _result.sparseaupper = sparseaupper; _result.absamax = absamax; _result.absasum = absasum; _result.absasum2 = absasum2; _result.b = (double[])b.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.stype = stype; _result.s = (double[])s.Clone(); _result.havebndl = (bool[])havebndl.Clone(); _result.havebndu = (bool[])havebndu.Clone(); _result.xorigin = (double[])xorigin.Clone(); _result.startx = (double[])startx.Clone(); _result.havex = havex; _result.cleic = (double[,])cleic.Clone(); _result.nec = nec; _result.nic = nic; _result.scleic = (sparse.sparsematrix)scleic.make_copy(); _result.snec = snec; _result.snic = snic; _result.xs = (double[])xs.Clone(); _result.repinneriterationscount = repinneriterationscount; _result.repouteriterationscount = repouteriterationscount; _result.repncholesky = repncholesky; _result.repnmv = repnmv; _result.repterminationtype = repterminationtype; _result.effectives = (double[])effectives.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.ecleic = (double[,])ecleic.Clone(); _result.dummyr2 = (double[,])dummyr2.Clone(); _result.qpbleicfirstcall = qpbleicfirstcall; _result.qpbleicbuf = (qpbleicsolver.qpbleicbuffers)qpbleicbuf.make_copy(); _result.qqpbuf = (qqpsolver.qqpbuffers)qqpbuf.make_copy(); _result.qpdenseaulbuf = (qpdenseaulsolver.qpdenseaulbuffers)qpdenseaulbuf.make_copy(); return _result; } }; /************************************************************************* This structure stores optimization report: * InnerIterationsCount number of inner iterations * OuterIterationsCount number of outer iterations * NCholesky number of Cholesky decomposition * NMV number of matrix-vector products (only products calculated as part of iterative process are counted) * TerminationType completion code (see below) Completion codes: * -9 failure of the automatic scale evaluation: one of the diagonal elements of the quadratic term is non-positive. Specify variable scales manually! * -5 inappropriate solver was used: * QuickQP solver for problem with general linear constraints (dense/sparse) * -4 BLEIC-QP or QuickQP solver found unconstrained direction of negative curvature (function is unbounded from below even under constraints), no meaningful minimum can be found. * -3 inconsistent constraints (or, maybe, feasible point is too hard to find). If you are sure that constraints are feasible, try to restart optimizer with better initial approximation. * -1 solver error * 1..4 successful completion * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. *************************************************************************/ public class minqpreport : apobject { public int inneriterationscount; public int outeriterationscount; public int nmv; public int ncholesky; public int terminationtype; public minqpreport() { init(); } public override void init() { } public override alglib.apobject make_copy() { minqpreport _result = new minqpreport(); _result.inneriterationscount = inneriterationscount; _result.outeriterationscount = outeriterationscount; _result.nmv = nmv; _result.ncholesky = ncholesky; _result.terminationtype = terminationtype; return _result; } }; /************************************************************************* CONSTRAINED QUADRATIC PROGRAMMING The subroutine creates QP optimizer. After initial creation, it contains default optimization problem with zero quadratic and linear terms and no constraints. You should set quadratic/linear terms with calls to functions provided by MinQP subpackage. You should also choose appropriate QP solver and set it and its stopping criteria by means of MinQPSetAlgo??????() function. Then, you should start solution process by means of MinQPOptimize() call. Solution itself can be obtained with MinQPResults() function. Following solvers are recommended: * QuickQP for dense problems with box-only constraints (or no constraints at all) * QP-BLEIC for dense/sparse problems with moderate (up to 50) number of general linear constraints * DENSE-AUL-QP for dense problems with any (small or large) number of general linear constraints INPUT PARAMETERS: N - problem size OUTPUT PARAMETERS: State - optimizer with zero quadratic/linear terms and no constraints -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpcreate(int n, minqpstate state, alglib.xparams _params) { int i = 0; alglib.ap.assert(n>=1, "MinQPCreate: N<1"); // // initialize QP solver // state.n = n; state.nec = 0; state.nic = 0; state.snec = 0; state.snic = 0; state.repterminationtype = 0; state.absamax = 1; state.absasum = 1; state.absasum2 = 1; state.akind = 0; state.sparseaupper = false; cqmodels.cqminit(n, state.a, _params); state.b = new double[n]; state.bndl = new double[n]; state.bndu = new double[n]; state.havebndl = new bool[n]; state.havebndu = new bool[n]; state.s = new double[n]; state.startx = new double[n]; state.xorigin = new double[n]; state.xs = new double[n]; for(i=0; i<=n-1; i++) { state.bndl[i] = Double.NegativeInfinity; state.bndu[i] = Double.PositiveInfinity; state.havebndl[i] = false; state.havebndu[i] = false; state.b[i] = 0.0; state.startx[i] = 0.0; state.xorigin[i] = 0.0; state.s[i] = 1.0; } state.stype = 0; state.havex = false; minqpsetalgobleic(state, 0.0, 0.0, 0.0, 0, _params); qqpsolver.qqploaddefaults(n, state.qqpsettingsuser, _params); qpbleicsolver.qpbleicloaddefaults(n, state.qpbleicsettingsuser, _params); qpdenseaulsolver.qpdenseaulloaddefaults(n, state.qpdenseaulsettingsuser, _params); state.qpbleicfirstcall = true; state.dbgskipconstraintnormalization = false; } /************************************************************************* This function sets linear term for QP solver. By default, linear term is zero. INPUT PARAMETERS: State - structure which stores algorithm state B - linear term, array[N]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlinearterm(minqpstate state, double[] b, alglib.xparams _params) { int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(b)>=n, "MinQPSetLinearTerm: Length(B)=n, "MinQPSetQuadraticTerm: Rows(A)=n, "MinQPSetQuadraticTerm: Cols(A)N"); alglib.ap.assert(sparse.sparsegetncols(a, _params)==n, "MinQPSetQuadraticTermSparse: Cols(A)<>N"); sparse.sparsecopytocrsbuf(a, state.sparsea, _params); state.sparseaupper = isupper; state.akind = 1; // // Estimate norm of A // (it will be used later in the quadratic penalty function) // state.absamax = 0; state.absasum = 0; state.absasum2 = 0; t0 = 0; t1 = 0; while( sparse.sparseenumerate(a, ref t0, ref t1, ref i, ref j, ref v, _params) ) { if( i==j ) { // // Diagonal terms are counted only once // state.absamax = Math.Max(state.absamax, v); state.absasum = state.absasum+v; state.absasum2 = state.absasum2+v*v; } if( (j>i && isupper) || (j=n, "MinQPSetStartingPoint: Length(B)=n, "MinQPSetOrigin: Length(B)=state.n, "MinQPSetScale: Length(S)=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} EpsX - >=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinQPSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. NOTE: this algorithm uses LBFGS iterations, which are relatively cheap, but improve function value only a bit. So you will need many iterations to converge - from 0.1*N to 10*N, depending on problem's condition number. IT IS VERY IMPORTANT TO CALL MinQPSetScale() WHEN YOU USE THIS ALGORITHM BECAUSE ITS STOPPING CRITERIA ARE SCALE-DEPENDENT! Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (presently it is small step length, but it may change in the future versions of ALGLIB). -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetalgobleic(minqpstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsg), "MinQPSetAlgoBLEIC: EpsG is not finite number"); alglib.ap.assert((double)(epsg)>=(double)(0), "MinQPSetAlgoBLEIC: negative EpsG"); alglib.ap.assert(math.isfinite(epsf), "MinQPSetAlgoBLEIC: EpsF is not finite number"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinQPSetAlgoBLEIC: negative EpsF"); alglib.ap.assert(math.isfinite(epsx), "MinQPSetAlgoBLEIC: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinQPSetAlgoBLEIC: negative EpsX"); alglib.ap.assert(maxits>=0, "MinQPSetAlgoBLEIC: negative MaxIts!"); state.algokind = 2; if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxits==0 ) { epsx = 1.0E-6; } state.qpbleicsettingsuser.epsg = epsg; state.qpbleicsettingsuser.epsf = epsf; state.qpbleicsettingsuser.epsx = epsx; state.qpbleicsettingsuser.maxits = maxits; } /************************************************************************* This function tells QP solver to use Dense-AUL algorithm and sets stopping criteria for the algorithm. ALGORITHM FEATURES: * supports box and dense/sparse general linear equality/inequality constraints * convergence is theoretically proved for positive-definite (convex) QP problems. Semidefinite and non-convex problems can be solved as long as they are bounded from below under constraints, although without theoretical guarantees. * this solver is better than QP-BLEIC on problems with large number of general linear constraints. It better handles infeasible initial points. ALGORITHM OUTLINE: * this algorithm is an augmented Lagrangian method with dense preconditioner (hence its name). It is similar to barrier/penalty methods, but much more precise and faster. * it performs several outer iterations in order to refine values of the Lagrange multipliers. Single outer iteration is a solution of some unconstrained optimization problem: first it performs dense Cholesky factorization of the Hessian in order to build preconditioner (adaptive regularization is applied to enforce positive definiteness), and then it uses L-BFGS optimizer to solve optimization problem. * typically you need about 5-10 outer iterations to converge to solution ALGORITHM LIMITATIONS: * because dense Cholesky driver is used, this algorithm has O(N^2) memory requirements and O(OuterIterations*N^3) minimum running time. From the practical point of view, it limits its applicability by several thousands of variables. From the other side, variables count is the most limiting factor, and dependence on constraint count is much more lower. Assuming that constraint matrix is sparse, it may handle tens of thousands of general linear constraints. INPUT PARAMETERS: State - structure which stores algorithm state EpsX - >=0, stopping criteria for inner optimizer. Inner iterations are stopped when step length (with variable scaling being applied) is less than EpsX. See minqpsetscale() for more information on variable scaling. Rho - penalty coefficient, Rho>0: * large enough that algorithm converges with desired precision. * not TOO large to prevent ill-conditioning * recommended values are 100, 1000 or 10000 ItsCnt - number of outer iterations: * recommended values: 10-15 (although in most cases it converges within 5 iterations, you may need a few more to be sure). * ItsCnt=0 means that small number of outer iterations is automatically chosen (10 iterations in current version). * ItsCnt=1 means that AUL algorithm performs just as usual penalty method. * ItsCnt>1 means that AUL algorithm performs specified number of outer iterations IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS ALGORITHM BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT! NOTE: Passing EpsX=0 will lead to automatic step length selection (specific step length chosen may change in the future versions of ALGLIB, so it is better to specify step length explicitly). -- ALGLIB -- Copyright 20.08.2016 by Bochkanov Sergey *************************************************************************/ public static void minqpsetalgodenseaul(minqpstate state, double epsx, double rho, int itscnt, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsx), "MinQPSetAlgoDenseAUL: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinQPSetAlgoDenseAUL: negative EpsX"); alglib.ap.assert(math.isfinite(rho), "MinQPSetAlgoDenseAUL: Rho is not finite number"); alglib.ap.assert((double)(rho)>(double)(0), "MinQPSetAlgoDenseAUL: non-positive Rho"); alglib.ap.assert(itscnt>=0, "MinQPSetAlgoDenseAUL: negative ItsCnt!"); state.algokind = 4; if( (double)(epsx)==(double)(0) ) { epsx = 1.0E-8; } if( itscnt==0 ) { itscnt = 10; } state.qpdenseaulsettingsuser.epsx = epsx; state.qpdenseaulsettingsuser.outerits = itscnt; state.qpdenseaulsettingsuser.rho = rho; } /************************************************************************* This function tells solver to use QuickQP algorithm: special extra-fast algorithm for problems with box-only constrants. It may solve non-convex problems as long as they are bounded from below under constraints. ALGORITHM FEATURES: * many times (from 5x to 50x!) faster than BLEIC-based QP solver; utilizes accelerated methods for activation of constraints. * supports dense and sparse QP problems * supports ONLY box constraints; general linear constraints are NOT supported by this solver * can solve all types of problems (convex, semidefinite, nonconvex) as long as they are bounded from below under constraints. Say, it is possible to solve "min{-x^2} subject to -1<=x<=+1". In convex/semidefinite case global minimum is returned, in nonconvex case - algorithm returns one of the local minimums. ALGORITHM OUTLINE: * algorithm performs two kinds of iterations: constrained CG iterations and constrained Newton iterations * initially it performs small number of constrained CG iterations, which can efficiently activate/deactivate multiple constraints * after CG phase algorithm tries to calculate Cholesky decomposition and to perform several constrained Newton steps. If Cholesky decomposition failed (matrix is indefinite even under constraints), we perform more CG iterations until we converge to such set of constraints that system matrix becomes positive definite. Constrained Newton steps greatly increase convergence speed and precision. * algorithm interleaves CG and Newton iterations which allows to handle indefinite matrices (CG phase) and quickly converge after final set of constraints is found (Newton phase). Combination of CG and Newton phases is called "outer iteration". * it is possible to turn off Newton phase (beneficial for semidefinite problems - Cholesky decomposition will fail too often) ALGORITHM LIMITATIONS: * algorithm does not support general linear constraints; only box ones are supported * Cholesky decomposition for sparse problems is performed with Skyline Cholesky solver, which is intended for low-profile matrices. No profile- reducing reordering of variables is performed in this version of ALGLIB. * problems with near-zero negative eigenvalues (or exacty zero ones) may experience about 2-3x performance penalty. The reason is that Cholesky decomposition can not be performed until we identify directions of zero and negative curvature and activate corresponding boundary constraints - but we need a lot of trial and errors because these directions are hard to notice in the matrix spectrum. In this case you may turn off Newton phase of algorithm. Large negative eigenvalues are not an issue, so highly non-convex problems can be solved very efficiently. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} EpsX - >=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinQPSetScale() MaxOuterIts-maximum number of OUTER iterations. One outer iteration includes some amount of CG iterations (from 5 to ~N) and one or several (usually small amount) Newton steps. Thus, one outer iteration has high cost, but can greatly reduce funcation value. Use 0 if you do not want to limit number of outer iterations. UseNewton- use Newton phase or not: * Newton phase improves performance of positive definite dense problems (about 2 times improvement can be observed) * can result in some performance penalty on semidefinite or slightly negative definite problems - each Newton phase will bring no improvement (Cholesky failure), but still will require computational time. * if you doubt, you can turn off this phase - optimizer will retain its most of its high speed. IT IS VERY IMPORTANT TO CALL MinQPSetScale() WHEN YOU USE THIS ALGORITHM BECAUSE ITS STOPPING CRITERIA ARE SCALE-DEPENDENT! Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (presently it is small step length, but it may change in the future versions of ALGLIB). -- ALGLIB -- Copyright 22.05.2014 by Bochkanov Sergey *************************************************************************/ public static void minqpsetalgoquickqp(minqpstate state, double epsg, double epsf, double epsx, int maxouterits, bool usenewton, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsg), "MinQPSetAlgoQuickQP: EpsG is not finite number"); alglib.ap.assert((double)(epsg)>=(double)(0), "MinQPSetAlgoQuickQP: negative EpsG"); alglib.ap.assert(math.isfinite(epsf), "MinQPSetAlgoQuickQP: EpsF is not finite number"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinQPSetAlgoQuickQP: negative EpsF"); alglib.ap.assert(math.isfinite(epsx), "MinQPSetAlgoQuickQP: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinQPSetAlgoQuickQP: negative EpsX"); alglib.ap.assert(maxouterits>=0, "MinQPSetAlgoQuickQP: negative MaxOuterIts!"); state.algokind = 3; if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxouterits==0 ) { epsx = 1.0E-6; } state.qqpsettingsuser.maxouterits = maxouterits; state.qqpsettingsuser.epsg = epsg; state.qqpsettingsuser.epsf = epsf; state.qqpsettingsuser.epsx = epsx; state.qqpsettingsuser.cnphase = usenewton; } /************************************************************************* This function sets box constraints for QP solver Box constraints are inactive by default (after initial creation). After being set, they are preserved until explicitly turned off with another SetBC() call. All QP solvers may handle box constraints. INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF (latter is recommended because it will allow solver to use better algorithm). BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF (latter is recommended because it will allow solver to use better algorithm). NOTE: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetbc(minqpstate state, double[] bndl, double[] bndu, alglib.xparams _params) { int i = 0; int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(bndl)>=n, "MinQPSetBC: Length(BndL)=n, "MinQPSetBC: Length(BndU)0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT NOTE 1: linear (non-bound) constraints are satisfied only approximately - there always exists some violation due to numerical errors and algorithmic limitations (BLEIC-QP solver is most precise, AUL-QP solver is less precise). -- ALGLIB -- Copyright 19.06.2012 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlc(minqpstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { sparse.sparsematrix dummyc = new sparse.sparsematrix(); int[] dummyct = new int[0]; minqpsetlcmixed(state, c, ct, k, dummyc, dummyct, 0, _params); } /************************************************************************* This function sets sparse linear constraints for QP optimizer. This function overrides results of previous calls to minqpsetlc(), minqpsetlcsparse() and minqpsetlcmixed(). After call to this function dense constraints are dropped, and you have only those constraints which were specified in the present call. If you want to specify mixed (with dense and sparse terms) linear constraints, you should call minqpsetlcmixed(). SUPPORT BY QP SOLVERS: Following QP solvers can handle sparse linear constraints: * BLEIC-QP - handles them with high precision, but can not utilize their sparsity - sparse constraint matrix is silently converted to dense format. Thus, it may be inefficient for problems with hundreds of constraints. * Dense-AUL-QP - although this solver uses dense linear algebra to calculate Cholesky preconditioner, it may efficiently handle sparse constraints. It may solve problems with hundreds and thousands of constraints. The only drawback is that precision of constraint handling is typically within 1E-4... ..1E-6 range. Following QP solvers can NOT handle sparse linear constraints: * QuickQP - can not handle general linear constraints INPUT PARAMETERS: State - structure previously allocated with MinQPCreate call. C - linear constraints, sparse matrix with dimensions at least [K,N+1]. If matrix has larger size, only leading Kx(N+1) rectangle is used. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. CT - type of constraints, array[K]: * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0 NOTE 1: linear (non-bound) constraints are satisfied only approximately - there always exists some violation due to numerical errors and algorithmic limitations (BLEIC-QP solver is most precise, AUL-QP solver is less precise). -- ALGLIB -- Copyright 22.08.2016 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlcsparse(minqpstate state, sparse.sparsematrix c, int[] ct, int k, alglib.xparams _params) { double[,] dummyc = new double[0,0]; int[] dummyct = new int[0]; minqpsetlcmixed(state, dummyc, dummyct, 0, c, ct, k, _params); } /************************************************************************* This function sets mixed linear constraints, which include a set of dense rows, and a set of sparse rows. This function overrides results of previous calls to minqpsetlc(), minqpsetlcsparse() and minqpsetlcmixed(). This function may be useful if constraint matrix includes large number of both types of rows - dense and sparse. If you have just a few sparse rows, you may represent them in dense format without loosing performance. Similarly, if you have just a few dense rows, you may store them in sparse format with almost same performance. SUPPORT BY QP SOLVERS: Following QP solvers can handle mixed dense/sparse linear constraints: * BLEIC-QP - handles them with high precision, but can not utilize their sparsity - sparse constraint matrix is silently converted to dense format. Thus, it may be inefficient for problems with hundreds of constraints. * Dense-AUL-QP - although this solver uses dense linear algebra to calculate Cholesky preconditioner, it may efficiently handle sparse constraints. It may solve problems with hundreds and thousands of constraints. The only drawback is that precision of constraint handling is typically within 1E-4... ..1E-6 range. Following QP solvers can NOT handle mixed linear constraints: * QuickQP - can not handle general linear constraints at all INPUT PARAMETERS: State - structure previously allocated with MinQPCreate call. DenseC - dense linear constraints, array[K,N+1]. Each row of DenseC represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of DenseC (including right part) must be finite. DenseCT - type of constraints, array[K]: * if DenseCT[i]>0, then I-th constraint is DenseC[i,*]*x >= DenseC[i,n+1] * if DenseCT[i]=0, then I-th constraint is DenseC[i,*]*x = DenseC[i,n+1] * if DenseCT[i]<0, then I-th constraint is DenseC[i,*]*x <= DenseC[i,n+1] DenseK - number of equality/inequality constraints, DenseK>=0 SparseC - linear constraints, sparse matrix with dimensions at least [SparseK,N+1]. If matrix has larger size, only leading SPARSEKx(N+1) rectangle is used. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. SparseCT- type of sparse constraints, array[K]: * if SparseCT[i]>0, then I-th constraint is SparseC[i,*]*x >= SparseC[i,n+1] * if SparseCT[i]=0, then I-th constraint is SparseC[i,*]*x = SparseC[i,n+1] * if SparseCT[i]<0, then I-th constraint is SparseC[i,*]*x <= SparseC[i,n+1] SparseK - number of sparse equality/inequality constraints, K>=0 NOTE 1: linear (non-bound) constraints are satisfied only approximately - there always exists some violation due to numerical errors and algorithmic limitations (BLEIC-QP solver is most precise, AUL-QP solver is less precise). -- ALGLIB -- Copyright 22.08.2016 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlcmixed(minqpstate state, double[,] densec, int[] densect, int densek, sparse.sparsematrix sparsec, int[] sparsect, int sparsek, alglib.xparams _params) { int n = 0; int i = 0; int j = 0; int j0 = 0; double v = 0; int[] srcidx = new int[0]; int[] dstidx = new int[0]; double[] s = new double[0]; int[] rs = new int[0]; int[] eoffs = new int[0]; int[] roffs = new int[0]; double[] v2 = new double[0]; int[] eidx = new int[0]; double[] eval = new double[0]; int t0 = 0; int t1 = 0; int nnz = 0; int i_ = 0; n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(densek>=0, "MinQPSetLCMixed: K<0"); alglib.ap.assert(densek==0 || alglib.ap.cols(densec)>=n+1, "MinQPSetLCMixed: Cols(C)=densek, "MinQPSetLCMixed: Rows(DenseC)=densek, "MinQPSetLCMixed: Length(DenseCT)=0, "MinQPSetLCMixed: SparseK<0"); alglib.ap.assert(sparsek==0 || sparse.sparsegetncols(sparsec, _params)>=n+1, "MinQPSetLCMixed: Cols(SparseC)=sparsek, "MinQPSetLCMixed: Rows(SparseC)=sparsek, "MinQPSetLCMixed: Length(SparseCT)0 ) { // // Equality constraints are stored first, in the upper // NEC rows of State.CLEIC matrix. Inequality constraints // are stored in the next NIC rows. // // NOTE: we convert inequality constraints to the form // A*x<=b before copying them. // apserv.rmatrixsetlengthatleast(ref state.cleic, densek, n+1, _params); for(i=0; i<=densek-1; i++) { if( densect[i]==0 ) { for(i_=0; i_<=n;i_++) { state.cleic[state.nec,i_] = densec[i,i_]; } state.nec = state.nec+1; } } for(i=0; i<=densek-1; i++) { if( densect[i]!=0 ) { if( densect[i]>0 ) { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = -densec[i,i_]; } } else { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = densec[i,i_]; } } state.nic = state.nic+1; } } // // Normalize rows of State.CLEIC: each row must have unit norm. // Norm is calculated using first N elements (i.e. right part is // not counted when we calculate norm). // if( !state.dbgskipconstraintnormalization ) { for(i=0; i<=densek-1; i++) { v = 0; for(j=0; j<=n-1; j++) { v = v+math.sqr(state.cleic[i,j]); } if( (double)(v)==(double)(0) ) { continue; } v = 1/Math.Sqrt(v); for(i_=0; i_<=n;i_++) { state.cleic[i,i_] = v*state.cleic[i,i_]; } } } } // // If we have sparse constraints // // NOTE: we have to write our own conversion code // because standard implementation converts whole // matrix, and we need just leading rectangle. // if( sparsek>0 ) { // // Calculate metrics: // * row sizes for CRS storage. // * row norms for scaling // * number of non-zero elements // * in addition, we check for finiteness of matrix elements // rs = new int[sparsek]; v2 = new double[sparsek]; for(i=0; i<=sparsek-1; i++) { rs[i] = 0; v2[i] = 0; } t0 = 0; t1 = 0; nnz = 0; while( sparse.sparseenumerate(sparsec, ref t0, ref t1, ref i, ref j, ref v, _params) ) { if( i>sparsek-1 || j>n ) { continue; } alglib.ap.assert(math.isfinite(v), "MinQPSetLCSparse: C contains infinite or NAN values"); nnz = nnz+1; rs[i] = rs[i]+1; if( j0 ) { // // Determine actual order in which constraints are stored. // // Equality constraints are stored first, in the upper // SNEC rows of State.CLEIC matrix. Inequality constraints // are stored in the next NIC rows. // // NOTE: we convert inequality constraints to the form // A*x<=b before copying them. // srcidx = new int[sparsek]; dstidx = new int[sparsek]; s = new double[sparsek]; for(i=0; i<=sparsek-1; i++) { srcidx[i] = -1; } for(i=0; i<=sparsek-1; i++) { if( sparsect[i]==0 ) { s[i] = 1; srcidx[state.snec] = i; state.snec = state.snec+1; } else { if( sparsect[i]>0 ) { s[i] = -1; } else { s[i] = 1; } srcidx[sparsek-1-state.snic] = i; state.snic = state.snic+1; } } for(i=0; i<=sparsek-1; i++) { alglib.ap.assert(srcidx[i]>=0, "MinQPSetLCSparse: integrity check failed"); } for(i=0; i<=sparsek-1; i++) { dstidx[srcidx[i]] = i; } // // Prepare CRS conversion. // state.scleic.m = sparsek; state.scleic.n = n+1; apserv.ivectorsetlengthatleast(ref state.scleic.ridx, sparsek+1, _params); eoffs = new int[sparsek+1]; state.scleic.ridx[0] = 0; eoffs[0] = 0; for(i=1; i<=sparsek; i++) { state.scleic.ridx[i] = state.scleic.ridx[i-1]+rs[srcidx[i-1]]; eoffs[i] = state.scleic.ridx[i]; } for(i=0; i<=sparsek-1; i++) { if( (double)(v2[i])>(double)(0) && !state.dbgskipconstraintnormalization ) { v2[i] = s[i]/Math.Sqrt(v2[i]); } else { v2[i] = s[i]; } } apserv.ivectorsetlengthatleast(ref state.scleic.idx, nnz, _params); apserv.rvectorsetlengthatleast(ref state.scleic.vals, nnz, _params); t0 = 0; t1 = 0; while( sparse.sparseenumerate(sparsec, ref t0, ref t1, ref i, ref j, ref v, _params) ) { if( i>sparsek-1 || j>n ) { continue; } j0 = eoffs[dstidx[i]]; state.scleic.idx[j0] = j; state.scleic.vals[j0] = v*v2[i]; eoffs[dstidx[i]] = j0+1; } for(i=0; i<=sparsek-1; i++) { alglib.ap.assert(eoffs[i]==state.scleic.ridx[i+1], "MinQPSetLCSparse: integrity check failed"); } sparse.sparsecreatecrsinplace(state.scleic, _params); } } } /************************************************************************* This function solves quadratic programming problem. Prior to calling this function you should choose solver by means of one of the following functions: * minqpsetalgoquickqp() - for QuickQP solver * minqpsetalgobleic() - for BLEIC-QP solver * minqpsetalgodenseaul() - for Dense-AUL-QP solver These functions also allow you to control stopping criteria of the solver. If you did not set solver, MinQP subpackage will automatically select solver for your problem and will run it with default stopping criteria. However, it is better to set explicitly solver and its stopping criteria. INPUT PARAMETERS: State - algorithm state You should use MinQPResults() function to access results after calls to this function. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey. Special thanks to Elvira Illarionova for important suggestions on the linearly constrained QP algorithm. *************************************************************************/ public static void minqpoptimize(minqpstate state, alglib.xparams _params) { int n = 0; int i = 0; int j = 0; int idst = 0; int isrc = 0; int j0 = 0; int j1 = 0; int nbc = 0; int i_ = 0; n = state.n; state.repterminationtype = -5; state.repinneriterationscount = 0; state.repouteriterationscount = 0; state.repncholesky = 0; state.repnmv = 0; // // check correctness of constraints // for(i=0; i<=n-1; i++) { if( state.havebndl[i] && state.havebndu[i] ) { if( (double)(state.bndl[i])>(double)(state.bndu[i]) ) { state.repterminationtype = -3; return; } } } // // count number of bound and linear constraints // nbc = 0; for(i=0; i<=n-1; i++) { if( state.havebndl[i] ) { nbc = nbc+1; } if( state.havebndu[i] ) { nbc = nbc+1; } } // // Initial point: // * if we have starting point in StartX, we just have to bound it // * if we do not have StartX, deduce initial point from boundary constraints // if( state.havex ) { for(i=0; i<=n-1; i++) { state.xs[i] = state.startx[i]; if( state.havebndl[i] && (double)(state.xs[i])<(double)(state.bndl[i]) ) { state.xs[i] = state.bndl[i]; } if( state.havebndu[i] && (double)(state.xs[i])>(double)(state.bndu[i]) ) { state.xs[i] = state.bndu[i]; } } } else { for(i=0; i<=n-1; i++) { if( state.havebndl[i] && state.havebndu[i] ) { state.xs[i] = 0.5*(state.bndl[i]+state.bndu[i]); continue; } if( state.havebndl[i] ) { state.xs[i] = state.bndl[i]; continue; } if( state.havebndu[i] ) { state.xs[i] = state.bndu[i]; continue; } state.xs[i] = 0; } } // // Effective scale // apserv.rvectorsetlengthatleast(ref state.effectives, n, _params); if( state.stype==0 ) { // // User scale (or default one) // for(i=0; i<=n-1; i++) { state.effectives[i] = state.s[i]; } } else { if( state.stype==1 ) { // // Diagonal is used for scaling: // * unpack // * convert to scale, return error on failure // if( state.akind==0 ) { // // Unpack CQM structure // cqmodels.cqmgetdiaga(state.a, ref state.effectives, _params); } else { if( state.akind==1 ) { for(i=0; i<=n-1; i++) { state.effectives[i] = sparse.sparseget(state.sparsea, i, i, _params); } } else { alglib.ap.assert(false, "MinQPOptimize: integrity check failed"); } } for(i=0; i<=n-1; i++) { if( (double)(state.effectives[i])<=(double)(0) ) { state.repterminationtype = -9; return; } state.effectives[i] = 1/Math.Sqrt(state.effectives[i]); } } else { alglib.ap.assert(false, "MinQPOptimize: integrity check failed"); } } // // QP-BLEIC solver // if( state.algokind==2 ) { // // Combine dense and sparse constraints in temporary dense storage // apserv.rmatrixsetlengthatleast(ref state.ecleic, state.nec+state.nic+state.snec+state.snic, n+1, _params); for(i=0; i<=state.nec-1; i++) { for(i_=0; i_<=n;i_++) { state.ecleic[i,i_] = state.cleic[i,i_]; } } for(i=0; i<=state.snec-1; i++) { alglib.ap.assert(state.scleic.matrixtype==1, "MinQPOptimize: integrity check failed"); idst = i+state.nec; for(j=0; j<=n; j++) { state.ecleic[idst,j] = 0; } j0 = state.scleic.ridx[i]; j1 = state.scleic.ridx[i+1]; for(j=j0; j<=j1-1; j++) { state.ecleic[idst,state.scleic.idx[j]] = state.scleic.vals[j]; } } for(i=0; i<=state.nic-1; i++) { idst = i+state.nec+state.snec; isrc = i+state.nec; for(i_=0; i_<=n;i_++) { state.ecleic[idst,i_] = state.cleic[isrc,i_]; } } for(i=0; i<=state.snic-1; i++) { alglib.ap.assert(state.scleic.matrixtype==1, "MinQPOptimize: integrity check failed"); idst = i+state.nec+state.snec+state.nic; isrc = i+state.snec; for(j=0; j<=n; j++) { state.ecleic[idst,j] = 0; } j0 = state.scleic.ridx[isrc]; j1 = state.scleic.ridx[isrc+1]; for(j=j0; j<=j1-1; j++) { state.ecleic[idst,state.scleic.idx[j]] = state.scleic.vals[j]; } } // // Solve // qpbleicsolver.qpbleicoptimize(state.a, state.sparsea, state.akind, state.sparseaupper, state.absasum, state.absasum2, state.b, state.bndl, state.bndu, state.effectives, state.xorigin, n, state.ecleic, state.nec+state.snec, state.nic+state.snic, state.qpbleicsettingsuser, state.qpbleicbuf, ref state.qpbleicfirstcall, ref state.xs, ref state.repterminationtype, _params); state.repinneriterationscount = state.qpbleicbuf.repinneriterationscount; state.repouteriterationscount = state.qpbleicbuf.repouteriterationscount; return; } // // QuickQP solver // if( state.algokind==3 ) { if( state.nec+state.nic>0 ) { state.repterminationtype = -5; return; } if( state.snec+state.snic>0 ) { state.repterminationtype = -5; return; } qqpsolver.qqpoptimize(state.a, state.sparsea, state.dummyr2, state.akind, state.sparseaupper, state.b, state.bndl, state.bndu, state.effectives, state.xorigin, n, state.qqpsettingsuser, state.qqpbuf, state.xs, ref state.repterminationtype, _params); state.repinneriterationscount = state.qqpbuf.repinneriterationscount; state.repouteriterationscount = state.qqpbuf.repouteriterationscount; state.repncholesky = state.qqpbuf.repncholesky; return; } // // QP-DenseAUL solver // if( state.algokind==4 ) { // // Solve // qpdenseaulsolver.qpdenseauloptimize(state.a, state.sparsea, state.akind, state.sparseaupper, state.b, state.bndl, state.bndu, state.effectives, state.xorigin, n, state.cleic, state.nec, state.nic, state.scleic, state.snec, state.snic, !state.dbgskipconstraintnormalization, state.qpdenseaulsettingsuser, state.qpdenseaulbuf, ref state.xs, ref state.repterminationtype, _params); state.repinneriterationscount = state.qpdenseaulbuf.repinneriterationscount; state.repouteriterationscount = state.qpdenseaulbuf.repouteriterationscount; state.repncholesky = state.qpdenseaulbuf.repncholesky; return; } // // Integrity check failed - unknown solver // alglib.ap.assert(false, "MinQPOptimize: integrity check failed - unknown solver"); } /************************************************************************* QP solver results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution. This array is allocated and initialized only when Rep.TerminationType parameter is positive (success). Rep - optimization report. You should check Rep.TerminationType, which contains completion code, and you may check another fields which contain another information about algorithm functioning. Failure codes returned by algorithm are: * -9 failure of the automatic scale evaluation: one of the diagonal elements of the quadratic term is non-positive. Specify variable scales manually! * -5 inappropriate solver was used: * QuickQP solver for problem with general linear constraints * -4 BLEIC-QP/QuickQP solver found unconstrained direction of negative curvature (function is unbounded from below even under constraints), no meaningful minimum can be found. * -3 inconsistent constraints (or maybe feasible point is too hard to find). If you are sure that constraints are feasible, try to restart optimizer with better initial approximation. Completion codes specific for Cholesky algorithm: * 4 successful completion Completion codes specific for BLEIC/QuickQP algorithms: * 1 relative function improvement is no more than EpsF. * 2 scaled step is no more than EpsX. * 4 scaled gradient norm is no more than EpsG. * 5 MaxIts steps was taken -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpresults(minqpstate state, ref double[] x, minqpreport rep, alglib.xparams _params) { x = new double[0]; minqpresultsbuf(state, ref x, rep, _params); } /************************************************************************* QP results Buffered implementation of MinQPResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpresultsbuf(minqpstate state, ref double[] x, minqpreport rep, alglib.xparams _params) { int i_ = 0; if( alglib.ap.len(x)(double)(0) ) { apserv.rvectorsetlengthatleast(ref state.tmp0, n, _params); for(i=0; i<=n-1; i++) { state.tmp0[i] = a[i,i]+s; } cqmodels.cqmrewritedensediagonal(state.a, state.tmp0, _params); } // // Estimate norm of A // (it will be used later in the quadratic penalty function) // state.absamax = 0; state.absasum = 0; state.absasum2 = 0; for(i=0; i<=n-1; i++) { if( isupper ) { j0 = i; j1 = n-1; } else { j0 = 0; j1 = i; } for(j=j0; j<=j1; j++) { v = Math.Abs(a[i,j]); state.absamax = Math.Max(state.absamax, v); state.absasum = state.absasum+v; state.absasum2 = state.absasum2+v*v; } } } /************************************************************************* Internal function which allows to rewrite diagonal of quadratic term. For internal use only. This function can be used only when you have dense A and already made MinQPSetQuadraticTerm(Fast) call. -- ALGLIB -- Copyright 16.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqprewritediagonal(minqpstate state, double[] s, alglib.xparams _params) { cqmodels.cqmrewritedensediagonal(state.a, s, _params); } /************************************************************************* Fast version of MinQPSetStartingPoint(), which doesn't check its arguments. For internal use only. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetstartingpointfast(minqpstate state, double[] x, alglib.xparams _params) { int n = 0; int i_ = 0; n = state.n; for(i_=0; i_<=n-1;i_++) { state.startx[i_] = x[i_]; } state.havex = true; } /************************************************************************* Fast version of MinQPSetOrigin(), which doesn't check its arguments. For internal use only. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetoriginfast(minqpstate state, double[] xorigin, alglib.xparams _params) { int n = 0; int i_ = 0; n = state.n; for(i_=0; i_<=n-1;i_++) { state.xorigin[i_] = xorigin[i_]; } } } public class reviseddualsimplex { /************************************************************************* This object stores settings for dual simplex solver *************************************************************************/ public class dualsimplexsettings : apobject { public double pivottol; public double perturbmag; public int maxtrfage; public int trftype; public int ratiotest; public int pricing; public int shifting; public dualsimplexsettings() { init(); } public override void init() { } public override alglib.apobject make_copy() { dualsimplexsettings _result = new dualsimplexsettings(); _result.pivottol = pivottol; _result.perturbmag = perturbmag; _result.maxtrfage = maxtrfage; _result.trftype = trftype; _result.ratiotest = ratiotest; _result.pricing = pricing; _result.shifting = shifting; return _result; } }; /************************************************************************* This object stores basis and its triangular factorization for DualSimplexState *************************************************************************/ public class dualsimplexbasis : apobject { public int ns; public int m; public int[] idx; public int[] nidx; public bool[] isbasic; public int trftype; public bool isvalidtrf; public int trfage; public double[,] denselu; public sparse.sparsematrix sparsel; public sparse.sparsematrix sparseu; public sparse.sparsematrix sparseut; public int[] rowpermbwd; public int[] colpermbwd; public double[] densepfieta; public double[] densemu; public int[] rk; public int[] dk; public double[] dseweights; public bool dsevalid; public double eminu; public double[] wtmp0; public double[] wtmp1; public double[] wtmp2; public int[] nrs; public int[] tcinvidx; public double[,] denselu2; public int[] densep2; public int[] densep2c; public sparse.sparsematrix sparselu1; public sparse.sparsematrix sparselu2; public sptrf.sluv2buffer lubuf2; public int[] tmpi; public double[] utmp0; public int[] utmpi; public sparse.sparsematrix sparseludbg; public dualsimplexbasis() { init(); } public override void init() { idx = new int[0]; nidx = new int[0]; isbasic = new bool[0]; denselu = new double[0,0]; sparsel = new sparse.sparsematrix(); sparseu = new sparse.sparsematrix(); sparseut = new sparse.sparsematrix(); rowpermbwd = new int[0]; colpermbwd = new int[0]; densepfieta = new double[0]; densemu = new double[0]; rk = new int[0]; dk = new int[0]; dseweights = new double[0]; wtmp0 = new double[0]; wtmp1 = new double[0]; wtmp2 = new double[0]; nrs = new int[0]; tcinvidx = new int[0]; denselu2 = new double[0,0]; densep2 = new int[0]; densep2c = new int[0]; sparselu1 = new sparse.sparsematrix(); sparselu2 = new sparse.sparsematrix(); lubuf2 = new sptrf.sluv2buffer(); tmpi = new int[0]; utmp0 = new double[0]; utmpi = new int[0]; sparseludbg = new sparse.sparsematrix(); } public override alglib.apobject make_copy() { dualsimplexbasis _result = new dualsimplexbasis(); _result.ns = ns; _result.m = m; _result.idx = (int[])idx.Clone(); _result.nidx = (int[])nidx.Clone(); _result.isbasic = (bool[])isbasic.Clone(); _result.trftype = trftype; _result.isvalidtrf = isvalidtrf; _result.trfage = trfage; _result.denselu = (double[,])denselu.Clone(); _result.sparsel = (sparse.sparsematrix)sparsel.make_copy(); _result.sparseu = (sparse.sparsematrix)sparseu.make_copy(); _result.sparseut = (sparse.sparsematrix)sparseut.make_copy(); _result.rowpermbwd = (int[])rowpermbwd.Clone(); _result.colpermbwd = (int[])colpermbwd.Clone(); _result.densepfieta = (double[])densepfieta.Clone(); _result.densemu = (double[])densemu.Clone(); _result.rk = (int[])rk.Clone(); _result.dk = (int[])dk.Clone(); _result.dseweights = (double[])dseweights.Clone(); _result.dsevalid = dsevalid; _result.eminu = eminu; _result.wtmp0 = (double[])wtmp0.Clone(); _result.wtmp1 = (double[])wtmp1.Clone(); _result.wtmp2 = (double[])wtmp2.Clone(); _result.nrs = (int[])nrs.Clone(); _result.tcinvidx = (int[])tcinvidx.Clone(); _result.denselu2 = (double[,])denselu2.Clone(); _result.densep2 = (int[])densep2.Clone(); _result.densep2c = (int[])densep2c.Clone(); _result.sparselu1 = (sparse.sparsematrix)sparselu1.make_copy(); _result.sparselu2 = (sparse.sparsematrix)sparselu2.make_copy(); _result.lubuf2 = (sptrf.sluv2buffer)lubuf2.make_copy(); _result.tmpi = (int[])tmpi.Clone(); _result.utmp0 = (double[])utmp0.Clone(); _result.utmpi = (int[])utmpi.Clone(); _result.sparseludbg = (sparse.sparsematrix)sparseludbg.make_copy(); return _result; } }; /************************************************************************* This object stores subproblem for DualSimplexState object *************************************************************************/ public class dualsimplexsubproblem : apobject { public int ns; public int m; public double[] rawc; public double[] bndl; public double[] bndu; public int[] bndt; public double[] xa; public double[] d; public int state; public double[] xb; public double[] bndlb; public double[] bndub; public int[] bndtb; public double[] effc; public double[] colscales; public dualsimplexsubproblem() { init(); } public override void init() { rawc = new double[0]; bndl = new double[0]; bndu = new double[0]; bndt = new int[0]; xa = new double[0]; d = new double[0]; xb = new double[0]; bndlb = new double[0]; bndub = new double[0]; bndtb = new int[0]; effc = new double[0]; colscales = new double[0]; } public override alglib.apobject make_copy() { dualsimplexsubproblem _result = new dualsimplexsubproblem(); _result.ns = ns; _result.m = m; _result.rawc = (double[])rawc.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.bndt = (int[])bndt.Clone(); _result.xa = (double[])xa.Clone(); _result.d = (double[])d.Clone(); _result.state = state; _result.xb = (double[])xb.Clone(); _result.bndlb = (double[])bndlb.Clone(); _result.bndub = (double[])bndub.Clone(); _result.bndtb = (int[])bndtb.Clone(); _result.effc = (double[])effc.Clone(); _result.colscales = (double[])colscales.Clone(); return _result; } }; /************************************************************************* This object stores state of the DSS solver. MUST be initialized with DSSInit(). *************************************************************************/ public class dualsimplexstate : apobject { public double[] varscales; public double[] rowscales; public double[] rawbndl; public double[] rawbndu; public int ns; public int m; public sparse.sparsematrix a; public sparse.sparsematrix at; public dualsimplexbasis basis; public dualsimplexsubproblem primary; public dualsimplexsubproblem phase1; public dualsimplexsubproblem phase3; public double[] repx; public double[] repy; public double[] repdx; public int[] repstats; public double repf; public double repprimalerror; public double repdualerror; public int repterminationtype; public int repiterationscount; public int repiterationscount1; public int repiterationscount2; public int repiterationscount3; public int[] possibleflips; public int possibleflipscnt; public double[] dfctmp0; public double[] dfctmp1; public double[] dfctmp2; public int[] ustmpi; public double[] tmp0; public double[] tmp1; public double[] tmp2; public double[] alphar; public double[] rhor; public double[] tau; public double[] alphaq; public double[] alphaqim; public int[] eligibleset; public int[] harrisset; public dualsimplexstate() { init(); } public override void init() { varscales = new double[0]; rowscales = new double[0]; rawbndl = new double[0]; rawbndu = new double[0]; a = new sparse.sparsematrix(); at = new sparse.sparsematrix(); basis = new dualsimplexbasis(); primary = new dualsimplexsubproblem(); phase1 = new dualsimplexsubproblem(); phase3 = new dualsimplexsubproblem(); repx = new double[0]; repy = new double[0]; repdx = new double[0]; repstats = new int[0]; possibleflips = new int[0]; dfctmp0 = new double[0]; dfctmp1 = new double[0]; dfctmp2 = new double[0]; ustmpi = new int[0]; tmp0 = new double[0]; tmp1 = new double[0]; tmp2 = new double[0]; alphar = new double[0]; rhor = new double[0]; tau = new double[0]; alphaq = new double[0]; alphaqim = new double[0]; eligibleset = new int[0]; harrisset = new int[0]; } public override alglib.apobject make_copy() { dualsimplexstate _result = new dualsimplexstate(); _result.varscales = (double[])varscales.Clone(); _result.rowscales = (double[])rowscales.Clone(); _result.rawbndl = (double[])rawbndl.Clone(); _result.rawbndu = (double[])rawbndu.Clone(); _result.ns = ns; _result.m = m; _result.a = (sparse.sparsematrix)a.make_copy(); _result.at = (sparse.sparsematrix)at.make_copy(); _result.basis = (dualsimplexbasis)basis.make_copy(); _result.primary = (dualsimplexsubproblem)primary.make_copy(); _result.phase1 = (dualsimplexsubproblem)phase1.make_copy(); _result.phase3 = (dualsimplexsubproblem)phase3.make_copy(); _result.repx = (double[])repx.Clone(); _result.repy = (double[])repy.Clone(); _result.repdx = (double[])repdx.Clone(); _result.repstats = (int[])repstats.Clone(); _result.repf = repf; _result.repprimalerror = repprimalerror; _result.repdualerror = repdualerror; _result.repterminationtype = repterminationtype; _result.repiterationscount = repiterationscount; _result.repiterationscount1 = repiterationscount1; _result.repiterationscount2 = repiterationscount2; _result.repiterationscount3 = repiterationscount3; _result.possibleflips = (int[])possibleflips.Clone(); _result.possibleflipscnt = possibleflipscnt; _result.dfctmp0 = (double[])dfctmp0.Clone(); _result.dfctmp1 = (double[])dfctmp1.Clone(); _result.dfctmp2 = (double[])dfctmp2.Clone(); _result.ustmpi = (int[])ustmpi.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmp1 = (double[])tmp1.Clone(); _result.tmp2 = (double[])tmp2.Clone(); _result.alphar = (double[])alphar.Clone(); _result.rhor = (double[])rhor.Clone(); _result.tau = (double[])tau.Clone(); _result.alphaq = (double[])alphaq.Clone(); _result.alphaqim = (double[])alphaqim.Clone(); _result.eligibleset = (int[])eligibleset.Clone(); _result.harrisset = (int[])harrisset.Clone(); return _result; } }; public const int maxforcedrestarts = 1; public const int safetrfage = 5; public const int defaultmaxtrfage = 50; public const double minbeta = 1.0E-4; public const double maxudecay = 0.001; public const double shiftlen = 1.0E-12; public const double dtol = 1.0E-6; public const double xtol = 1.0E-9; public const double alphatrigger = 1.0E8*math.machineepsilon; public const double alphatrigger2 = 0.001; public const int ssinvalid = 0; public const int ssvalidxn = 1; public const int ssvalid = 2; public const int ccfixed = 0; public const int cclower = 1; public const int ccupper = 2; public const int ccrange = 3; public const int ccfree = 4; public const int ccinfeasible = 5; public static void dsssettingsinit(dualsimplexsettings settings, alglib.xparams _params) { settings.pivottol = 10*Math.Sqrt(math.machineepsilon); settings.perturbmag = 10*settings.pivottol; settings.maxtrfage = defaultmaxtrfage; settings.trftype = 3; settings.ratiotest = 1; settings.pricing = 1; settings.shifting = 2; } /************************************************************************* This function initializes DSS structure. Previously allocated memory is reused as much as possible. Default state of the problem is zero cost vector, all variables are fixed at zero. -- ALGLIB -- Copyright 01.07.2018 by Bochkanov Sergey *************************************************************************/ public static void dssinit(int n, dualsimplexstate s, alglib.xparams _params) { int i = 0; alglib.ap.assert(n>0, "DSSInit: N<=0"); s.ns = n; s.m = 0; apserv.rvectorsetlengthatleast(ref s.varscales, n, _params); apserv.rvectorsetlengthatleast(ref s.rawbndl, n, _params); apserv.rvectorsetlengthatleast(ref s.rawbndu, n, _params); for(i=0; i<=n-1; i++) { s.varscales[i] = 1.0; s.rawbndl[i] = 0.0; s.rawbndu[i] = 0.0; } subprobleminit(n, s.primary, _params); basisinit(n, 0, s.basis, _params); apserv.rvectorsetlengthatleast(ref s.repx, n, _params); apserv.rvectorsetlengthatleast(ref s.repdx, 0, _params); apserv.ivectorsetlengthatleast(ref s.repstats, n, _params); for(i=0; i<=n-1; i++) { s.repx[i] = 0.0; s.repstats[i] = 1; } } /************************************************************************* This function specifies LP problem INPUT PARAMETERS: State - structure previously allocated with minlpcreate() call. BndL - lower bounds, array[N]. BndU - upper bounds, array[N]. SV - scales, array[N]. DenseA - dense array[K,N], dense linear constraints SparseA - sparse linear constraints, sparsematrix[K,N] in CRS format AKind - type of A: 0 for dense, 1 for sparse AL, AU - lower and upper bounds, array[K] K - number of equality/inequality constraints, K>=0. ProposedBasis- basis to import from (if BasisType=2) BasisInitType- what to do with basis: * 0 - set new basis to all-logicals * 1 - try to reuse previous basis as much as possible * 2 - try to import basis from ProposedBasis Settings- algorithm settings -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void dsssetproblem(dualsimplexstate state, double[] c, double[] bndl, double[] bndu, double[] sv, double[,] densea, sparse.sparsematrix sparsea, int akind, double[] al, double[] au, int k, dualsimplexbasis proposedbasis, int basisinittype, dualsimplexsettings settings, alglib.xparams _params) { int i = 0; int j = 0; int jj = 0; int offs = 0; int ns = 0; int j0 = 0; int j1 = 0; bool processed = new bool(); int oldm = 0; bool basisinitialized = new bool(); double v = 0; double vv = 0; ns = state.primary.ns; oldm = state.primary.m; // // Integrity checks // alglib.ap.assert(alglib.ap.len(bndl)>=ns, "DSSSetProblem: Length(BndL)=ns, "DSSSetProblem: Length(BndU)=ns, "DSSSetProblem: Length(SV)=ns, "SubproblemSetCost: Length(C)=0, "DSSSetProblem: K<0"); if( k>0 && akind==1 ) { alglib.ap.assert(sparsea.m==k, "DSSSetProblem: rows(A)<>K"); alglib.ap.assert(sparsea.n==ns, "DSSSetProblem: cols(A)<>N"); } // // Downgrade state // downgradestate(state.primary, ssinvalid, _params); // // Reallocate storage // apserv.rvectorgrowto(ref state.primary.bndl, ns+k, _params); apserv.rvectorgrowto(ref state.primary.bndu, ns+k, _params); apserv.ivectorgrowto(ref state.primary.bndt, ns+k, _params); apserv.rvectorgrowto(ref state.primary.rawc, ns+k, _params); apserv.rvectorgrowto(ref state.primary.effc, ns+k, _params); apserv.rvectorgrowto(ref state.primary.xa, ns+k, _params); apserv.rvectorgrowto(ref state.primary.d, ns+k, _params); apserv.rvectorgrowto(ref state.primary.xb, k, _params); apserv.rvectorgrowto(ref state.primary.bndlb, k, _params); apserv.rvectorgrowto(ref state.primary.bndub, k, _params); apserv.ivectorgrowto(ref state.primary.bndtb, k, _params); // // Save original problem formulation // state.ns = ns; state.m = k; for(i=0; i<=ns-1; i++) { state.varscales[i] = sv[i]; state.rawbndl[i] = bndl[i]; state.rawbndu[i] = bndu[i]; } // // Setup cost, scale and box constraints // for(i=0; i<=ns-1; i++) { alglib.ap.assert(sv[i]>0, "DSSSetProblem: SV<=0"); state.primary.rawc[i] = c[i]*sv[i]; state.primary.effc[i] = c[i]*sv[i]; } for(i=0; i<=ns-1; i++) { alglib.ap.assert(math.isfinite(bndl[i]) || Double.IsNegativeInfinity(bndl[i]), "DSSSetProblem: BndL contains NAN or +INF"); alglib.ap.assert(math.isfinite(bndu[i]) || Double.IsPositiveInfinity(bndu[i]), "DSSSetProblem: BndU contains NAN or -INF"); state.primary.bndl[i] = bndl[i]/sv[i]; state.primary.bndu[i] = bndu[i]/sv[i]; // // Set bound type // if( math.isfinite(bndl[i]) && math.isfinite(bndu[i]) ) { if( (double)(bndl[i])>(double)(bndu[i]) ) { state.primary.bndt[i] = ccinfeasible; } if( (double)(bndl[i])<(double)(bndu[i]) ) { state.primary.bndt[i] = ccrange; } if( (double)(bndl[i])==(double)(bndu[i]) ) { state.primary.bndt[i] = ccfixed; } continue; } if( math.isfinite(bndl[i]) && !math.isfinite(bndu[i]) ) { state.primary.bndt[i] = cclower; continue; } if( !math.isfinite(bndl[i]) && math.isfinite(bndu[i]) ) { state.primary.bndt[i] = ccupper; continue; } alglib.ap.assert(Double.IsNegativeInfinity(bndl[i]) && Double.IsPositiveInfinity(bndu[i]), "DSSSetProblem: integrity check failed"); state.primary.bndt[i] = ccfree; } // // Quick exit if no linear constraints is present // if( k==0 ) { state.primary.m = 0; basisinit(state.primary.ns, state.primary.m, state.basis, _params); return; } // // Extend A with structural terms and transpose it: // * allocate place for A^T extended with logical part. // * copy with transposition // * perform integrity check for array sizes // * manually append new items // * update DIdx/UIdx // processed = false; state.primary.m = k; if( akind==0 ) { alglib.ap.assert(false, "DSSSetProblem: does not support dense inputs yet"); } if( akind==1 ) { // // Transpose constraints matrix, apply column and row scaling. // Extend it with identity submatrix. // // NOTE: in order to improve stability of LU factorization we // normalize rows using 2-norm, not INF-norm. Having rows // normalized with 2-norm makes every element less than // 1.0 in magnitude, which allows us later to move logical // columns to the beginning of LU factors without loosing // stability. // apserv.rvectorsetlengthatleast(ref state.at.vals, sparsea.ridx[k]+k, _params); apserv.ivectorsetlengthatleast(ref state.at.idx, sparsea.ridx[k]+k, _params); apserv.ivectorsetlengthatleast(ref state.at.ridx, ns+k+1, _params); apserv.ivectorsetlengthatleast(ref state.at.didx, ns+k, _params); apserv.ivectorsetlengthatleast(ref state.at.uidx, ns+k, _params); sparse.sparsecopytransposecrsbuf(sparsea, state.at, _params); apserv.rvectorsetlengthatleast(ref state.rowscales, k, _params); for(i=0; i<=k-1; i++) { state.rowscales[i] = 0; } for(i=0; i<=ns-1; i++) { j0 = state.at.ridx[i]; j1 = state.at.ridx[i+1]-1; v = sv[i]; for(j=j0; j<=j1; j++) { vv = v*state.at.vals[j]; jj = state.at.idx[j]; state.at.vals[j] = vv; state.rowscales[jj] = state.rowscales[jj]+vv*vv; } } apserv.rvectorsetlengthatleast(ref state.tmp0, k, _params); for(i=0; i<=k-1; i++) { state.rowscales[i] = apserv.coalesce(Math.Sqrt(state.rowscales[i]), 1, _params); state.tmp0[i] = 1/state.rowscales[i]; } for(i=0; i<=ns-1; i++) { j0 = state.at.ridx[i]; j1 = state.at.ridx[i+1]-1; for(j=j0; j<=j1; j++) { state.at.vals[j] = state.at.vals[j]*state.tmp0[state.at.idx[j]]; } } alglib.ap.assert(alglib.ap.len(state.at.vals)>=sparsea.ridx[k]+k, "DSSSetProblem: integrity check failed"); alglib.ap.assert(alglib.ap.len(state.at.idx)>=sparsea.ridx[k]+k, "DSSSetProblem: integrity check failed"); alglib.ap.assert(alglib.ap.len(state.at.ridx)>=ns+k+1, "DSSSetProblem: integrity check failed"); alglib.ap.assert(alglib.ap.len(state.at.didx)>=ns+k, "DSSSetProblem: integrity check failed"); alglib.ap.assert(alglib.ap.len(state.at.uidx)>=ns+k, "DSSSetProblem: integrity check failed"); offs = state.at.ridx[ns]; for(i=0; i<=k-1; i++) { state.at.vals[offs+i] = -1.0; state.at.idx[offs+i] = i; state.at.ridx[ns+i+1] = state.at.ridx[ns+i]+1; state.at.ninitialized = state.at.ninitialized+1; } state.at.m = state.at.m+k; sparse.sparseinitduidx(state.at, _params); processed = true; } alglib.ap.assert(processed, "DSSSetProblem: integrity check failed (akind)"); // // Copy AL, AU to BndL/BndT // for(i=0; i<=k-1; i++) { alglib.ap.assert(math.isfinite(al[i]) || Double.IsNegativeInfinity(al[i]), "DSSSetProblem: AL contains NAN or +INF"); alglib.ap.assert(math.isfinite(au[i]) || Double.IsPositiveInfinity(au[i]), "DSSSetProblem: AU contains NAN or -INF"); state.primary.bndl[ns+i] = al[i]/state.rowscales[i]; state.primary.bndu[ns+i] = au[i]/state.rowscales[i]; // // Set bound type // if( math.isfinite(al[i]) && math.isfinite(au[i]) ) { if( (double)(al[i])>(double)(au[i]) ) { state.primary.bndt[ns+i] = ccinfeasible; } if( (double)(al[i])<(double)(au[i]) ) { state.primary.bndt[ns+i] = ccrange; } if( (double)(al[i])==(double)(au[i]) ) { state.primary.bndt[ns+i] = ccfixed; } continue; } if( math.isfinite(al[i]) && !math.isfinite(au[i]) ) { state.primary.bndt[ns+i] = cclower; continue; } if( !math.isfinite(al[i]) && math.isfinite(au[i]) ) { state.primary.bndt[ns+i] = ccupper; continue; } alglib.ap.assert(Double.IsNegativeInfinity(al[i]) && Double.IsPositiveInfinity(au[i]), "DSSSetProblem: integrity check faoled"); state.primary.bndt[ns+i] = ccfree; } // // Depending on BasisInitType either start from all-logical basis // or try to reuse already existing basis. // // NOTE: current version does not support basis shrinkage, only // growing basis can be reused. // basisinitialized = false; if( basisinittype==2 ) { // // Import basis from one proposed by caller // alglib.ap.assert(proposedbasis.ns==state.primary.ns, "DSSSetProblemX: unable to import basis, sizes do not match"); alglib.ap.assert(proposedbasis.m==state.primary.m, "DSSSetProblemX: unable to import basis, sizes do not match"); basisinitialized = basistryimportfrom(state.basis, proposedbasis, state.at, settings, _params); } if( basisinittype==1 && state.primary.m>=oldm ) { // // New rows were added, try to reuse previous basis // for(i=oldm; i<=state.primary.m-1; i++) { state.primary.rawc[ns+i] = 0.0; state.primary.effc[ns+i] = 0.0; state.primary.xa[ns+i] = 0.0; state.primary.d[ns+i] = 0.0; } basisinitialized = basistryresize(state.basis, state.primary.m, state.at, settings, _params); } if( !basisinitialized ) { // // Straightforward code for all-logicals basis // for(i=0; i<=k-1; i++) { state.primary.rawc[ns+i] = 0.0; state.primary.effc[ns+i] = 0.0; state.primary.xa[ns+i] = 0.0; state.primary.d[ns+i] = 0.0; } basisinit(state.primary.ns, state.primary.m, state.basis, _params); basisfreshtrf(state.basis, state.at, settings, _params); } apserv.rvectorgrowto(ref state.repy, state.primary.m, _params); apserv.rvectorgrowto(ref state.repdx, state.primary.m, _params); apserv.ivectorgrowto(ref state.repstats, state.primary.ns+state.primary.m, _params); } /************************************************************************* This function exports basis from the primary (phase II) subproblem. INPUT PARAMETERS: State - structure OUTPUT PARAMETERS Basis - current basis exported (no factorization, only set of basis/nonbasic variables) -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void dssexportbasis(dualsimplexstate state, dualsimplexbasis basis, alglib.xparams _params) { basisexportto(state.basis, basis, _params); } /************************************************************************* This function performs actual solution. INPUT PARAMETERS: State - state Solution results can be found in fields of State which are explicitly declared as accessible by external code. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void dssoptimize(dualsimplexstate state, dualsimplexsettings settings, alglib.xparams _params) { int nx = 0; int m = 0; int i = 0; int j = 0; int j0 = 0; int j1 = 0; double v = 0; hqrnd.hqrndstate rs = new hqrnd.hqrndstate(); nx = state.primary.ns+state.primary.m; m = state.primary.m; // // Init report fields // state.repf = 0; state.repiterationscount = 0; state.repiterationscount1 = 0; state.repiterationscount2 = 0; state.repiterationscount3 = 0; state.repterminationtype = 1; state.repprimalerror = 0; state.repdualerror = 0; // // Handle case when M=0; after this block we assume that M>0. // if( m==0 ) { solveboxonly(state, _params); state.repf = 0; for(i=0; i<=state.primary.ns-1; i++) { state.repf = state.repf+state.repx[i]*state.primary.rawc[i]; } unscaleandenforce(state.repx, state.repy, state.repdx, state, _params); return; } // // Most basic check for correctness of box constraints // for(i=0; i<=nx-1; i++) { if( state.primary.bndt[i]==ccinfeasible ) { state.repterminationtype = -3; setzeroxystats(state, _params); return; } } // // Initialization: // * column scales, initial perturbed C[] // hqrnd.hqrndseed(7456, 2355, rs, _params); apserv.rvectorsetlengthatleast(ref state.primary.colscales, nx, _params); for(i=0; i<=nx-1; i++) { // // compute column scales // v = Math.Abs(state.primary.rawc[i]); j0 = state.at.ridx[i]; j1 = state.at.ridx[i+1]-1; for(j=j0; j<=j1; j++) { v = Math.Max(v, Math.Abs(state.at.vals[j])); } state.primary.colscales[i] = apserv.coalesce(v, 1.0, _params); // // apply perturbation // if( !isfree(state.primary, i, _params) ) { v = settings.perturbmag*state.primary.colscales[i]*(1+hqrnd.hqrnduniformr(rs, _params)); if( hasbndu(state.primary, i, _params) ) { v = -v; } state.primary.effc[i] = state.primary.rawc[i]+v; } } // // Solve phase 1 subproblem, then perturbed subproblem // basisfreshtrf(state.basis, state.at, settings, _params); if( state.primary.state==ssinvalid ) { subprobleminferinitialxn(state, state.primary, _params); } if( state.primary.state==ssvalidxn ) { subproblemhandlexnupdate(state, state.primary, _params); } alglib.ap.assert(state.primary.state==ssvalid, "DSS: integrity check failed (init)"); invokephase1(state, settings, _params); if( state.repterminationtype<=0 ) { // // Primal unbounded, dual infeasible // alglib.ap.assert(state.repterminationtype==-4, "DSS: integrity check for InvokePhase1() result failed"); state.repf = 0; state.repprimalerror = 0; state.repdualerror = 0; setzeroxystats(state, _params); return; } solvesubproblemdual(state, state.primary, false, settings, ref state.repterminationtype, _params); if( state.repterminationtype<=0 ) { // // Primal infeasible // alglib.ap.assert(state.repterminationtype==-3, "DSS: integrity check for SolveSubproblemDual() result failed"); state.repf = 0; state.repprimalerror = 0; state.repdualerror = 0; setzeroxystats(state, _params); return; } // // Remove perturbation from the cost vector, // then use primal simplex to enforce dual feasibility // after removal of the perturbation (if necessary). // subprobleminitphase3(state.primary, state.phase3, _params); for(i=0; i<=nx-1; i++) { state.phase3.effc[i] = state.primary.rawc[i]; } alglib.ap.assert(state.phase3.state>=ssvalidxn, "DSS: integrity check failed (remove perturbation)"); subproblemhandlexnupdate(state, state.phase3, _params); solvesubproblemprimal(state, state.phase3, settings, ref state.repterminationtype, _params); if( state.repterminationtype<=0 ) { // // Dual infeasible, primal unbounded // alglib.ap.assert(state.repterminationtype==-4, "DSS: integrity check for SolveSubproblemPrimal() result failed"); state.repf = 0; state.repprimalerror = 0; state.repdualerror = 0; setzeroxystats(state, _params); return; } for(i=0; i<=nx-1; i++) { state.primary.xa[i] = state.phase3.xa[i]; if( hasbndl(state.primary, i, _params) ) { state.primary.xa[i] = Math.Max(state.primary.xa[i], state.primary.bndl[i]); } if( hasbndu(state.primary, i, _params) ) { state.primary.xa[i] = Math.Min(state.primary.xa[i], state.primary.bndu[i]); } } // // Primal and dual feasible, problem solved // apserv.rvectorsetlengthatleast(ref state.tmp0, m, _params); apserv.rvectorsetlengthatleast(ref state.tmp1, m, _params); state.repf = 0; for(i=0; i<=state.primary.ns-1; i++) { state.repf = state.repf+state.primary.xa[i]*state.primary.rawc[i]; } state.repprimalerror = 0; state.repdualerror = 0; state.repterminationtype = 1; for(i=0; i<=m-1; i++) { state.tmp0[i] = state.primary.rawc[state.basis.idx[i]]; } basissolvet(state.basis, state.tmp0, ref state.tmp1, ref state.tmp2, _params); computeantv(state, state.tmp1, ref state.primary.d, _params); for(i=0; i<=state.ns-1; i++) { j = state.basis.nidx[i]; state.primary.d[j] = state.primary.rawc[j]-state.primary.d[j]; } for(i=0; i<=m-1; i++) { state.repy[i] = state.tmp1[i]; state.repdx[i] = state.primary.d[state.ns+i]; } for(i=0; i<=nx-1; i++) { if( state.basis.isbasic[i] ) { state.repstats[i] = 0; continue; } if( hasbndl(state.primary, i, _params) && (double)(state.primary.xa[i])==(double)(state.primary.bndl[i]) ) { state.repstats[i] = -1; continue; } if( hasbndu(state.primary, i, _params) && (double)(state.primary.xa[i])==(double)(state.primary.bndu[i]) ) { state.repstats[i] = 1; continue; } alglib.ap.assert(!hasbndl(state.primary, i, _params) && !hasbndu(state.primary, i, _params), "DSSOptimize: integrity check failed (zetta5)"); state.repstats[i] = 0; } for(i=0; i<=state.primary.ns-1; i++) { state.repx[i] = state.primary.xa[i]; } unscaleandenforce(state.repx, state.repy, state.repdx, state, _params); } /************************************************************************* This function initializes subproblem structure. Previously allocated memory is reused as much as possible. Default state of the problem is zero cost vector, all variables are fixed at zero, linear constraint matrix is zero. -- ALGLIB -- Copyright 01.07.2018 by Bochkanov Sergey *************************************************************************/ private static void subprobleminit(int n, dualsimplexsubproblem s, alglib.xparams _params) { int i = 0; alglib.ap.assert(n>0, "SubproblemInit: N<=0"); s.ns = n; s.m = 0; s.state = ssinvalid; apserv.rvectorsetlengthatleast(ref s.xa, n, _params); apserv.rvectorsetlengthatleast(ref s.xb, 0, _params); apserv.rvectorsetlengthatleast(ref s.d, n, _params); apserv.rvectorsetlengthatleast(ref s.rawc, n, _params); apserv.rvectorsetlengthatleast(ref s.effc, n, _params); apserv.rvectorsetlengthatleast(ref s.bndl, n, _params); apserv.rvectorsetlengthatleast(ref s.bndu, n, _params); apserv.ivectorsetlengthatleast(ref s.bndt, n, _params); for(i=0; i<=n-1; i++) { s.rawc[i] = 0; s.effc[i] = 0; s.bndl[i] = 0; s.bndu[i] = 0; s.bndt[i] = ccfixed; s.xa[i] = 0.0; s.d[i] = 0.0; } } /************************************************************************* This function initializes phase #1 subproblem which minimizes sum of dual infeasibilities. It is required that total count of non-boxed non-fixed variables is at least M. It splits out basic components of XA[] to XB[] -- ALGLIB -- Copyright 01.07.2018 by Bochkanov Sergey *************************************************************************/ private static void subprobleminitphase1(dualsimplexsubproblem s0, dualsimplexbasis basis, dualsimplexsubproblem s1, alglib.xparams _params) { int i = 0; s1.ns = s0.ns; s1.m = s0.m; apserv.copyrealarray(s0.rawc, ref s1.rawc, _params); apserv.copyrealarray(s0.effc, ref s1.effc, _params); apserv.copyrealarray(s0.colscales, ref s1.colscales, _params); apserv.copyrealarray(s0.bndl, ref s1.bndl, _params); apserv.copyrealarray(s0.bndu, ref s1.bndu, _params); apserv.copyintegerarray(s0.bndt, ref s1.bndt, _params); apserv.copyrealarray(s0.xa, ref s1.xa, _params); apserv.copyrealarray(s0.xb, ref s1.xb, _params); apserv.copyrealarray(s0.bndlb, ref s1.bndlb, _params); apserv.copyrealarray(s0.bndub, ref s1.bndub, _params); apserv.copyintegerarray(s0.bndtb, ref s1.bndtb, _params); apserv.copyrealarray(s0.d, ref s1.d, _params); for(i=0; i<=s1.ns+s1.m-1; i++) { if( s1.bndt[i]==cclower ) { s1.bndt[i] = ccrange; s1.bndl[i] = 0; s1.bndu[i] = 1; s1.xa[i] = 0; continue; } if( s1.bndt[i]==ccupper ) { s1.bndt[i] = ccrange; s1.bndl[i] = -1; s1.bndu[i] = 0; s1.xa[i] = 0; continue; } if( s1.bndt[i]==ccfree ) { s1.bndt[i] = ccrange; s1.bndl[i] = -1; s1.bndu[i] = 1; if( (double)(s1.effc[i])>=(double)(0) ) { s1.xa[i] = -1; } else { s1.xa[i] = 1; } continue; } s1.bndt[i] = ccfixed; s1.bndl[i] = 0; s1.bndu[i] = 0; s1.xa[i] = 0; } s1.state = ssvalidxn; } /************************************************************************* This function initializes phase #3 subproblem which applies primal simplex method to the result of the phase #2. It also performs modification of the subproblem in order to ensure that initial point is primal feasible. NOTE: this function expects that all components (basic and nonbasic ones) are stored in XA[] -- ALGLIB -- Copyright 01.07.2018 by Bochkanov Sergey *************************************************************************/ private static void subprobleminitphase3(dualsimplexsubproblem s0, dualsimplexsubproblem s1, alglib.xparams _params) { s1.ns = s0.ns; s1.m = s0.m; apserv.copyrealarray(s0.rawc, ref s1.rawc, _params); apserv.copyrealarray(s0.effc, ref s1.effc, _params); apserv.copyrealarray(s0.colscales, ref s1.colscales, _params); apserv.copyrealarray(s0.bndl, ref s1.bndl, _params); apserv.copyrealarray(s0.bndu, ref s1.bndu, _params); apserv.copyintegerarray(s0.bndt, ref s1.bndt, _params); apserv.copyrealarray(s0.xa, ref s1.xa, _params); apserv.copyrealarray(s0.xb, ref s1.xb, _params); apserv.copyrealarray(s0.bndlb, ref s1.bndlb, _params); apserv.copyrealarray(s0.bndub, ref s1.bndub, _params); apserv.copyintegerarray(s0.bndtb, ref s1.bndtb, _params); apserv.copyrealarray(s0.d, ref s1.d, _params); s1.state = ssvalidxn; } /************************************************************************* This function infers nonbasic variables of X using sign of effective C[]. Only non-basic components of XN are changed; everything else is NOT updated. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void subprobleminferinitialxn(dualsimplexstate state, dualsimplexsubproblem s, alglib.xparams _params) { int i = 0; int ii = 0; int bndt = 0; for(ii=0; ii<=s.ns-1; ii++) { i = state.basis.nidx[ii]; bndt = s.bndt[i]; if( bndt==ccfixed || bndt==ccrange ) { if( s.effc[i]>=0 ) { s.xa[i] = s.bndl[i]; } else { s.xa[i] = s.bndu[i]; } continue; } if( bndt==cclower ) { s.xa[i] = s.bndl[i]; continue; } if( bndt==ccupper ) { s.xa[i] = s.bndu[i]; continue; } if( bndt==ccfree ) { s.xa[i] = 0.0; continue; } alglib.ap.assert(false, "SubproblemInferInitialXN: integrity check failed (infeasible constraint)"); } s.state = ssvalidxn; } /************************************************************************* This function infers basic variables of X using values of non-basic vars and updates reduced cost vector D and target function Z. Sets state age to zero. D[] is allocated during computations. Temporary vectors Tmp0 and Tmp1 are used (reallocated as needed). NOTE: this function expects that both nonbasic and basic components are stored in XA[]. XB[] array is not referenced. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void subproblemhandlexnupdate(dualsimplexstate state, dualsimplexsubproblem s, alglib.xparams _params) { int i = 0; int j = 0; int m = 0; int nn = 0; alglib.ap.assert(s.state>=ssvalidxn, "SubproblemHandleXNUpdate: integrity check failed (XN is not valid)"); nn = s.ns; m = s.m; // // Compute nonbasic components // computeanxn(state, s, s.xa, ref state.tmp0, _params); basissolve(state.basis, state.tmp0, ref state.tmp1, ref state.tmp2, _params); for(i=0; i<=m-1; i++) { s.xa[state.basis.idx[i]] = -state.tmp1[i]; } // // Compute D // for(i=0; i<=m-1; i++) { state.tmp0[i] = s.effc[state.basis.idx[i]]; } basissolvet(state.basis, state.tmp0, ref state.tmp1, ref state.tmp2, _params); computeantv(state, state.tmp1, ref s.d, _params); for(i=0; i<=nn-1; i++) { j = state.basis.nidx[i]; s.d[j] = s.effc[j]-s.d[j]; } // // Update state validity/age // s.state = ssvalid; } /************************************************************************* This function performs initial dual feasibility correction on the subproblem. It assumes that problem state is at least ssValidXN. After call to this function the problem state is set to ssValid. This function returns dual feasibility error after dual feasibility correction. NOTE: this function expects that both nonbasic and basic components are stored in XA[]. XB[] array is not referenced. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static double initialdualfeasibilitycorrection(dualsimplexstate state, dualsimplexsubproblem s, dualsimplexsettings settings, alglib.xparams _params) { double result = 0; double[] dummy = new double[0]; int nn = 0; int m = 0; int ii = 0; int i = 0; int j = 0; bool flipped = new bool(); double v = 0; double dj = 0; double xj = 0; int bndt = 0; nn = s.ns; m = s.m; alglib.ap.assert(s.state>=ssvalidxn, "InitialDualFeasibilityCorrection: XN is invalid"); // // Prepare // apserv.rvectorsetlengthatleast(ref state.dfctmp0, m, _params); apserv.rvectorsetlengthatleast(ref state.dfctmp1, m, _params); // // Recompute D[] using fresh factorization // basisfreshtrf(state.basis, state.at, settings, _params); for(i=0; i<=m-1; i++) { state.dfctmp0[i] = s.effc[state.basis.idx[i]]; } basissolvet(state.basis, state.dfctmp0, ref state.dfctmp1, ref state.dfctmp2, _params); computeantv(state, state.dfctmp1, ref s.d, _params); for(i=0; i<=nn-1; i++) { j = state.basis.nidx[i]; s.d[j] = s.effc[j]-s.d[j]; } // // Perform flips for dual-infeasible boxed variables // result = 0; flipped = false; for(ii=0; ii<=nn-1; ii++) { j = state.basis.nidx[ii]; bndt = s.bndt[j]; // // Boxed variables, perform DFC // if( bndt==ccrange ) { dj = s.d[j]; xj = s.xa[j]; if( xj==s.bndl[j] && dj<0 ) { s.xa[j] = s.bndu[j]; flipped = true; continue; } if( xj==s.bndu[j] && dj>0 ) { s.xa[j] = s.bndl[j]; flipped = true; continue; } continue; } // // Non-boxed variables, compute dual feasibility error // if( bndt==ccfixed ) { continue; } if( bndt==cclower ) { v = -s.d[j]; if( v>result ) { result = v; } continue; } if( bndt==ccupper ) { v = s.d[j]; if( v>result ) { result = v; } continue; } if( bndt==ccfree ) { result = Math.Max(result, Math.Abs(s.d[j])); continue; } } // // Recompute basic components of X[] // if( flipped || s.state=(double)(0) ) { return; } s.effc[q] = s.effc[q]-s.d[q]; s.d[q] = 0; thetad = 0; return; } // // EXPAND with ThetaD=ShiftLen // if( settings.shifting==2 ) { dir = Math.Sign(delta); if( (double)(thetad*dir)>(double)(0) ) { return; } // // Ensure that non-zero step is performed // thetad = dir*shiftlen; // // Shift Q-th coefficient // sft = thetad*(dir*alphar[q])-s.d[q]; s.effc[q] = s.effc[q]+sft; s.d[q] = s.d[q]+sft; // // Shift other coefficients // for(ii=0; ii<=s.ns-1; ii++) { j = state.basis.nidx[ii]; bndt = s.bndt[j]; if( (j==q || bndt==ccfixed) || bndt==ccfree ) { continue; } sft = thetad*(dir*alphar[j])-s.d[j]; // // Handle variables at lower bound // if( bndt==cclower || (bndt==ccrange && s.xa[j]==s.bndl[j]) ) { sft = sft-dtol; if( sft>0 ) { s.effc[j] = s.effc[j]+sft; s.d[j] = s.d[j]+sft; } continue; } if( bndt==ccupper || (bndt==ccrange && s.xa[j]==s.bndu[j]) ) { sft = sft+dtol; if( sft<0 ) { s.effc[j] = s.effc[j]+sft; s.d[j] = s.d[j]+sft; } continue; } } // // Done // return; } alglib.ap.assert(false, "Shifting: unexpected shifting type"); } /************************************************************************* This function performs pricing step Additional parameters: * Phase1Pricing - if True, then special Phase #1 restriction is applied to leaving variables: only those are eligible which will move to zero bound after basis change. This trick allows to accelerate and stabilize phase #1. See Robert Fourer, 'Notes on the dual simplex method', draft report, 1994, for more info. Returns: * leaving variable index P * its index R in the basis, in [0,M) range * Delta - difference between variable value and corresponding bound NOTE: this function expects that basic components are stored in XB[]; corresponding entries of XA[] are ignored. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void pricingstep(dualsimplexstate state, dualsimplexsubproblem s, bool phase1pricing, ref int p, ref int r, ref double delta, dualsimplexsettings settings, alglib.xparams _params) { int m = 0; int i = 0; int bi = 0; double v = 0; double vtarget = 0; double xbi = 0; double bndl = 0; double bndu = 0; double vdiff = 0; double vtest = 0; double invw = 0; double tol = 0; int bndt = 0; bool hasboth = new bool(); bool hasl = new bool(); bool hasu = new bool(); p = 0; r = 0; delta = 0; m = s.m; // // Integrity checks // alglib.ap.assert(s.state==ssvalid, "PricingStep: invalid X"); alglib.ap.assert(m>0, "PricingStep: M<=0"); // // Pricing // if( settings.pricing==0 ) { // // "Most infeasible" pricing // // NOTE: VTarget is initialized by XTol because we want to skip // violations below XTol // p = -1; r = -1; delta = 0; vtarget = xtol; for(i=0; i<=m-1; i++) { bndt = s.bndtb[i]; hasboth = bndt==3 || bndt==0; hasl = hasboth || bndt==1; hasu = hasboth || bndt==2; xbi = s.xb[i]; if( hasl ) { bndl = s.bndlb[i]; vdiff = xbi-bndl; v = -vdiff; if( v>vtarget ) { // // Special phase 1 pricing: do not choose variables which move to non-zero bound // if( phase1pricing && !(bndl==0.0) ) { continue; } // // Proceed as usual // p = state.basis.idx[i]; r = i; delta = vdiff; vtarget = v; continue; } } if( hasu ) { bndu = s.bndub[i]; vdiff = xbi-bndu; v = vdiff; if( v>vtarget ) { // // Special phase 1 pricing: do not choose variables which move to non-zero bound // if( phase1pricing && !(bndu==0.0) ) { continue; } // // Proceed as usual // p = state.basis.idx[i]; r = i; delta = vdiff; vtarget = v; continue; } } } return; } if( settings.pricing==-1 || settings.pricing==1 ) { // // Dual steepest edge pricing // basisrequestweights(state.basis, settings, _params); p = -1; r = -1; delta = 0; vtarget = 0; for(i=0; i<=m-1; i++) { bi = state.basis.idx[i]; bndt = s.bndtb[i]; hasboth = bndt==3 || bndt==0; hasl = hasboth || bndt==1; hasu = hasboth || bndt==2; xbi = s.xb[i]; tol = xtol; invw = 1/state.basis.dseweights[i]; if( hasl ) { bndl = s.bndlb[i]; vdiff = xbi-bndl; vtest = vdiff*vdiff*invw; if( vdiff<-tol && (p<0 || vtest>vtarget) ) { // // Special phase 1 pricing: do not choose variables which move to non-zero bound // if( phase1pricing && !(bndl==0.0) ) { continue; } // // Proceed as usual // p = bi; r = i; delta = vdiff; vtarget = vtest; continue; } } if( hasu ) { bndu = s.bndub[i]; vdiff = xbi-bndu; vtest = vdiff*vdiff*invw; if( vdiff>tol && (p<0 || vtest>vtarget) ) { // // Special phase 1 pricing: do not choose variables which move to non-zero bound // if( phase1pricing && !(bndu==0.0) ) { continue; } // // Proceed as usual // p = bi; r = i; delta = vdiff; vtarget = vtest; continue; } } } return; } alglib.ap.assert(false, "PricingStep: unknown pricing type"); } /************************************************************************* This function performs ratio test, either simple one or BFRT. It accepts following parameters: * AlphaR - pivot row * Delta - delta from pricing step * P - index of leaving variable from pricing step It returns following results: * Q - non-negative value for success, negative for primal infeasible problem * ThetaD - dual step length * PossibleFlips[PossibleFlipsCnt] - for possible flip indexes (for BFRT this set coincides with actual flips, but stabilizing BFRT is a bit more complex - some variables in PossibleFlips[] may need flipping and some not) Internally it uses following fields of State for temporaries: * EligibleSet -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void ratiotest(dualsimplexstate state, dualsimplexsubproblem s, double[] alphar, double delta, int p, ref int q, ref double thetad, ref int[] possibleflips, ref int possibleflipscnt, dualsimplexsettings settings, alglib.xparams _params) { int nx = 0; int nn = 0; int i = 0; int j = 0; int nj = 0; int jj = 0; int dir = 0; double sinv = 0; double vx = 0; double vp = 0; double vtarget = 0; double vtest = 0; int eligiblecnt = 0; int bndt = 0; double alphawaver = 0; double adelta = 0; int idx = 0; double vtheta = 0; double thetamax = 0; int harrissetsize = 0; bool hasnonboxedvars = new bool(); q = 0; thetad = 0; nx = s.ns+s.m; nn = s.ns; alglib.ap.assert((double)(delta)!=(double)(0), "RatioTest: zero delta"); alglib.ap.assert(s.state==ssvalid, "RatioTest: invalid X"); // // Prepare temporaries // // Scaled tolerances are used to test AlphaWaveR for positivity/negativity, // scale of I-th tolerance is calculated as ratio of ColScale[I] and ColScale[P]. // dir = Math.Sign(delta); sinv = 1.0/s.colscales[p]; apserv.ivectorsetlengthatleast(ref possibleflips, nx, _params); // // Prepare set of eligible variables // // NOTE: free variables are immediately chosen at this stage // apserv.ivectorsetlengthatleast(ref state.eligibleset, nn, _params); eligiblecnt = 0; for(j=0; j<=nn-1; j++) { nj = state.basis.nidx[j]; bndt = s.bndt[nj]; // // Handle fixed and free variables: fixed ones are not eligible, // free non-basic variables are always and immediately eligible // if( bndt==ccfixed ) { continue; } if( bndt==ccfree ) { q = nj; thetad = 0; return; } // // Handle lower/upper/range constraints // vx = s.xa[nj]; vp = settings.pivottol*(s.colscales[nj]*sinv); alphawaver = dir*alphar[nj]; if( bndt==cclower || (bndt==ccrange && vx==s.bndl[nj]) ) { if( alphawaver>vp ) { state.eligibleset[eligiblecnt] = nj; eligiblecnt = eligiblecnt+1; continue; } } if( bndt==ccupper || (bndt==ccrange && vx==s.bndu[nj]) ) { if( alphawaver<-vp ) { state.eligibleset[eligiblecnt] = nj; eligiblecnt = eligiblecnt+1; continue; } } } // // Simple ratio test. // if( settings.ratiotest==0 ) { q = -1; thetad = 0; vtarget = 0; possibleflipscnt = 0; for(j=0; j<=eligiblecnt-1; j++) { nj = state.eligibleset[j]; // // More general case // alphawaver = dir*alphar[nj]; vtest = s.d[nj]/alphawaver; if( q<0 || vtest0 ) { // // Find Q satisfying BFRT criteria // idx = -1; q = -1; vtarget = 0; for(j=0; j<=eligiblecnt-1; j++) { nj = state.eligibleset[j]; vtheta = s.d[nj]/alphar[nj]; vtest = dir*vtheta; if( q<0 || vtest=0, "RatioTest: integrity check failed (BFRT)"); // // BFRT mini-iterations will be terminated upon discovery // of non-boxed variable or upon exhausting of eligible set. // if( s.bndt[q]!=ccrange ) { break; } if( eligiblecnt==1 ) { break; } // // Update and test ADelta. Break BFRT mini-iterations once // we get negative slope. // adelta = adelta-(s.bndu[q]-s.bndl[q])*Math.Abs(alphar[q]); if( (double)(adelta)<=(double)(0) ) { break; } // // Update eligible set, record flip // alglib.ap.assert(state.eligibleset[idx]==q, "RatioTest: unexpected failure"); possibleflips[possibleflipscnt] = q; possibleflipscnt = possibleflipscnt+1; state.eligibleset[idx] = state.eligibleset[eligiblecnt-1]; eligiblecnt = eligiblecnt-1; } alglib.ap.assert(q>=0, "RatioTest: unexpected failure"); thetad = s.d[q]/alphar[q]; shifting(state, s, alphar, delta, q, ref thetad, settings, _params); return; } // // Stabilizing bounds flipping ratio test // if( settings.ratiotest==2 ) { q = -1; thetad = 0; possibleflipscnt = 0; adelta = Math.Abs(delta); apserv.ivectorgrowto(ref state.harrisset, eligiblecnt, _params); // // Quick exit // if( eligiblecnt==0 ) { return; } // // BFRT // while( eligiblecnt>0 ) { // // Determine ThetaMax according to stabilizing BFRT // thetamax = math.maxrealnumber; for(j=0; j<=eligiblecnt-1; j++) { nj = state.eligibleset[j]; alphawaver = dir*alphar[nj]; if( alphawaver>0 ) { vtest = (s.d[nj]+dtol)/alphawaver; } else { vtest = (s.d[nj]-dtol)/alphawaver; } if( vtestthetamax ) { continue; } state.harrisset[harrissetsize] = nj; harrissetsize = harrissetsize+1; hasnonboxedvars = hasnonboxedvars || s.bndt[nj]!=ccrange; vtest = Math.Abs(alphawaver); if( vtest>vtarget ) { q = nj; vtarget = vtest; } } alglib.ap.assert(q>=0, "RatioTest: integrity check failed (Harris set selection)"); if( harrissetsize==eligiblecnt ) { break; } // // Remove Harris set from the eligible set // j = 0; jj = 0; for(i=0; i<=eligiblecnt-1; i++) { if( j==harrissetsize || state.eligibleset[i]!=state.harrisset[j] ) { // // I-th element not present in Harris set, leave it in the eligible set // state.eligibleset[jj] = state.eligibleset[i]; jj = jj+1; } else { // // I-th element is present in Harris set, skip it // j = j+1; } } eligiblecnt = eligiblecnt-harrissetsize; alglib.ap.assert(j==harrissetsize, "RatioTest: integrity check failed"); alglib.ap.assert(jj==eligiblecnt, "RatioTest: integrity check failed"); // // Update and test |delta|. // // Break BFRT mini-iterations once we get negative slope. // for(j=0; j<=harrissetsize-1; j++) { nj = state.harrisset[j]; if( !hasnonboxedvars || s.bndt[nj]==ccrange ) { adelta = adelta-(s.bndu[nj]-s.bndl[nj])*Math.Abs(alphar[nj]); } else { adelta = -1; } } if( (double)(adelta)<=(double)(0) ) { break; } alglib.ap.assert(!hasnonboxedvars, "RatioTest: integrity check failed"); for(j=0; j<=harrissetsize-1; j++) { possibleflips[possibleflipscnt] = state.harrisset[j]; possibleflipscnt = possibleflipscnt+1; } } alglib.ap.assert(q>=0, "RatioTest: unexpected failure"); if( eligiblecnt==0 && adelta<0 ) { // // Eligible set exhausted, declare dual unboundedness // q = -1; thetad = 0; return; } thetad = s.d[q]/alphar[q]; shifting(state, s, alphar, delta, q, ref thetad, settings, _params); return; } // // Unknown test type // alglib.ap.assert(false, "RatioTest: integrity check failed, unknown test type"); } /************************************************************************* This function performs update of XB, XN, D and Z during final step of revised dual simplex method. It also updates basis cache of the subproblem (s.bcache field). Depending on Settings.RatioTest, following operations are performed: * Settings.RatioTest=0 -> simple update is performed * Settings.RatioTest=1 -> bounds flipping ratio test update is performed * Settings.RatioTest=2 -> stabilizing bounds flipping ratio test update is performed It accepts following parameters: * P - index of leaving variable from pricing step * Q - index of entering variable. * R - index of leaving variable in AlphaQ * Delta - delta from pricing step * ThetaP - primal step length * ThetaD - dual step length * AlphaQ - pivot column * AlphaQim - intermediate result from Ftran for AlphaQ, used for Forest-Tomlin update, not referenced when other update scheme is set * AlphaR - pivot row * Tau - tau-vector for DSE pricing (ignored if simple pricing is used) * PossibleFlips, PossibleFlipsCnt - outputs of the RatioTest(), information about possible variable flips (however, we have to check residual costs before actually flipping variables - it is possible that some variables in this set actually do not need flipping) It performs following operations: * basis update * update of XB/BndTB/BndLB/BndUB[] and XA[] (basic and nonbasic components), D * update of pricing weights -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void updatestep(dualsimplexstate state, dualsimplexsubproblem s, int p, int q, int r, double delta, double thetap, double thetad, double[] alphaq, double[] alphaqim, double[] alphar, double[] tau, int[] possibleflips, int possibleflipscnt, dualsimplexsettings settings, alglib.xparams _params) { int nn = 0; int nx = 0; int m = 0; int ii = 0; int j = 0; int k = 0; int k0 = 0; int k1 = 0; double bndl = 0; double bndu = 0; bool flipped = new bool(); double flip = 0; double dj = 0; int dir = 0; int idx = 0; int actualflipscnt = 0; nn = s.ns; nx = s.ns+s.m; m = s.m; // // Integrity checks // alglib.ap.assert((settings.ratiotest==0 || settings.ratiotest==1) || settings.ratiotest==2, "UpdateStep: invalid X"); alglib.ap.assert(s.state==ssvalid, "UpdateStep: invalid X"); alglib.ap.assert(p>=0 && q>=0, "UpdateStep: invalid P/Q"); alglib.ap.assert((double)(delta)!=(double)(0), "UpdateStep: Delta=0"); alglib.ap.assert((double)(alphaq[r])!=(double)(0), "UpdateStep: AlphaQ[R]=0"); // // Prepare // dir = Math.Sign(delta); flip = 0; apserv.rvectorsetlengthatleast(ref state.tmp0, m, _params); for(k=0; k<=m-1; k++) { state.tmp0[k] = 0; } apserv.ivectorsetlengthatleast(ref state.ustmpi, nx, _params); actualflipscnt = 0; // // Evaluate and update non-basic elements of D // for(ii=0; ii<=nn-1; ii++) { j = state.basis.nidx[ii]; s.d[j] = s.d[j]-thetad*state.alphar[j]; } for(ii=0; ii<=possibleflipscnt-1; ii++) { j = possibleflips[ii]; dj = s.d[j]; bndl = s.bndl[j]; bndu = s.bndu[j]; flipped = false; if( s.xa[j]==bndl && dj<0 ) { flip = bndu-bndl; flipped = true; } else { if( s.xa[j]==bndu && dj>0 ) { flip = bndl-bndu; flipped = true; } } if( flipped ) { delta = delta-dir*(bndu-bndl)*Math.Abs(alphar[j]); state.ustmpi[actualflipscnt] = j; actualflipscnt = actualflipscnt+1; k0 = state.at.ridx[j]; k1 = state.at.ridx[j+1]-1; for(k=k0; k<=k1; k++) { idx = state.at.idx[k]; state.tmp0[idx] = state.tmp0[idx]+flip*state.at.vals[k]; } } } s.d[p] = -thetad; s.d[q] = 0.0; // // Apply BFRT update (aka long dual step) or simple ratio update // if( actualflipscnt>0 ) { thetap = delta/state.alphaq[r]; k0 = state.at.ridx[q]; k1 = state.at.ridx[q+1]-1; for(k=k0; k<=k1; k++) { idx = state.at.idx[k]; state.tmp0[idx] = state.tmp0[idx]+thetap*state.at.vals[k]; } basissolve(state.basis, state.tmp0, ref state.tmp1, ref state.tmp2, _params); for(j=0; j<=m-1; j++) { s.xb[j] = s.xb[j]-state.tmp1[j]; } for(ii=0; ii<=actualflipscnt-1; ii++) { j = state.ustmpi[ii]; if( s.xa[j]==s.bndl[j] ) { s.xa[j] = s.bndu[j]; } else { s.xa[j] = s.bndl[j]; } } s.xb[r] = s.xa[q]+thetap; if( dir<0 ) { s.xa[p] = s.bndl[p]; } else { s.xa[p] = s.bndu[p]; } } else { for(j=0; j<=m-1; j++) { s.xb[j] = s.xb[j]-thetap*state.alphaq[j]; } s.xb[r] = s.xa[q]+thetap; if( dir<0 ) { s.xa[p] = s.bndl[p]; } else { s.xa[p] = s.bndu[p]; } } // // Update basis // basisupdatetrf(state.basis, state.at, p, q, state.alphaq, state.alphaqim, r, tau, settings, _params); // // Update cached variables // s.bndlb[r] = s.bndl[q]; s.bndub[r] = s.bndu[q]; s.bndtb[r] = s.bndt[q]; } /************************************************************************* This function performs several checks for accumulation of errors during factorization update. It returns True if refactorization is advised. -- ALGLIB -- Copyright 24.01.2019 by Bochkanov Sergey *************************************************************************/ private static bool refactorizationrequired(dualsimplexstate state, dualsimplexsubproblem s, int q, int r, alglib.xparams _params) { bool result = new bool(); int m = 0; int i = 0; double mx = 0; double v = 0; m = s.m; result = false; // // Quick exit // if( state.basis.trfage<=safetrfage ) { return result; } // // Compare Q-th entry of the pivot row AlphaR with R-th entry of the AlphaQ; // ideally, both should match exactly. The difference is a rough estimate // of the magnitude of the numerical errors. // mx = 0.0; for(i=0; i<=m-1; i++) { v = state.alphaq[i]; v = v*v; if( v>mx ) { mx = v; } } mx = Math.Sqrt(mx); result = result || (double)(Math.Abs(state.alphaq[r]-state.alphar[q]))>(double)(alphatrigger*(1.0+mx)); result = result || (double)(Math.Abs(state.alphaq[r]-state.alphar[q]))>(double)(alphatrigger2*Math.Abs(state.alphar[q])); return result; } /************************************************************************* This function performs actual solution of dual simplex subproblem (either primary one or phase 1 one). A problem with following properties is expected: * M>0 * feasible box constraints * dual feasible initial basis * actual initial point XC and target value Z * actual reduced cost vector D * pricing weights being set to 1.0 or copied from previous problem Returns: * Info = +1 for success, -3 for infeasible * IterationsCount is increased by amount of iterations performed NOTE: this function internally uses separate storage of basic and nonbasic components; however, all inputs and outputs use single array S.XA[] to store both basic and nonbasic variables. It transparently splits variables on input and recombines them on output. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void solvesubproblemdual(dualsimplexstate state, dualsimplexsubproblem s, bool isphase1, dualsimplexsettings settings, ref int info, alglib.xparams _params) { int nx = 0; int m = 0; int i = 0; int j = 0; int p = 0; int r = 0; int q = 0; double thetad = 0; double thetap = 0; double delta = 0; int forcedrestarts = 0; int j0 = 0; int j1 = 0; info = 0; nx = s.ns+s.m; m = s.m; forcedrestarts = 0; // // Integrity checks // alglib.ap.assert(s.state==ssvalid, "SolveSubproblemDual: X is not valid"); alglib.ap.assert(m>0, "SolveSubproblemDual: M<=0"); for(i=0; i<=nx-1; i++) { alglib.ap.assert(s.bndt[i]!=ccinfeasible, "SolveSubproblemDual: infeasible box constraints"); } alglib.ap.assert(isdualfeasible(state, s, _params), "SolveSubproblemDual: dual infeasible initial basis"); // // Actual processing // offloadbasiccomponents(s, state.basis, _params); info = 0; apserv.rvectorsetlengthatleast(ref state.tmp0, m, _params); while( true ) { // // Pricing // pricingstep(state, s, isphase1, ref p, ref r, ref delta, settings, _params); if( (double)(delta)==(double)(0) ) { // // Solved! Feasible and bounded! // recombinebasicnonbasicx(s, state.basis, _params); info = 1; return; } // // BTran // for(i=0; i<=m-1; i++) { state.tmp0[i] = 0; } state.tmp0[r] = 1; basissolvet(state.basis, state.tmp0, ref state.rhor, ref state.tmp2, _params); // // Pivot row // computeantv(state, state.rhor, ref state.alphar, _params); // // Ratio test // ratiotest(state, s, state.alphar, delta, p, ref q, ref thetad, ref state.possibleflips, ref state.possibleflipscnt, settings, _params); if( q<0 ) { // // Do we have fresh factorization and state? If not, // refresh them prior to declaring that we have no solution. // if( state.basis.trfage>0 && forcedrestarts0 * feasible box constraints * primal feasible initial basis * actual initial point XC and target value Z * actual reduced cost vector D * pricing weights being set to 1.0 or copied from previous problem Returns: * Info = +1 for success, -3 for infeasible * IterationsCount is increased by amount of iterations performed -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void solvesubproblemprimal(dualsimplexstate state, dualsimplexsubproblem s, dualsimplexsettings settings, ref int info, alglib.xparams _params) { int nn = 0; int nx = 0; int m = 0; int i = 0; int j = 0; double v = 0; double vmax = 0; int bi = 0; double dj = 0; int bndt = 0; int q = 0; int p = 0; int r = 0; int dir = 0; double lim = 0; bool haslim = new bool(); double thetap = 0; double xbnd = 0; double flip = 0; int canddir = 0; double candlim = 0; double candflip = 0; int j0 = 0; int j1 = 0; double alphawave = 0; double vp = 0; double vb = 0; double vx = 0; double vtest = 0; double vv = 0; info = 0; nn = s.ns; nx = s.ns+s.m; m = s.m; // // Integrity checks // alglib.ap.assert(s.state==ssvalid, "SolveSubproblemPrimal: X is not valid"); alglib.ap.assert(m>0, "SolveSubproblemPrimal: M<=0"); for(i=0; i<=nx-1; i++) { alglib.ap.assert(s.bndt[i]!=ccinfeasible, "SolveSubproblemPrimal: infeasible box constraints"); } // // Actual processing // info = 1; apserv.rvectorsetlengthatleast(ref state.tmp0, m, _params); while( true ) { // // Primal simplex pricing step: we implement the very basic version // of the pricing step because it is expected that primal simplex method // is used just to apply quick correction after removal of the perturbation. // q = -1; vmax = 0; dir = 0; lim = math.maxrealnumber; haslim = false; flip = 0; canddir = 0; for(i=0; i<=nn-1; i++) { j = state.basis.nidx[i]; dj = s.d[j]; bndt = s.bndt[j]; if( bndt==ccfixed ) { continue; } if( bndt==ccrange ) { v = 0; candlim = s.bndu[j]-s.bndl[j]; candflip = 0; if( s.xa[j]==s.bndl[j] ) { v = -dj; canddir = 1; candflip = s.bndu[j]; } if( s.xa[j]==s.bndu[j] ) { v = dj; canddir = -1; candflip = s.bndl[j]; } if( v>vmax ) { vmax = v; dir = canddir; lim = candlim; haslim = true; flip = candflip; q = j; } continue; } v = 0; canddir = 0; if( bndt==cclower ) { v = -dj; canddir = 1; } if( bndt==ccupper ) { v = dj; canddir = -1; } if( bndt==ccfree ) { v = Math.Abs(dj); canddir = -Math.Sign(dj); } if( v>vmax ) { vmax = v; dir = canddir; lim = math.maxrealnumber; haslim = false; q = j; } continue; } if( vmax<=dtol ) { // // Solved: primal and dual feasible! // return; } alglib.ap.assert(q>=0, "SolveSubproblemPrimal: integrity check failed"); // // FTran and textbook ratio test (again, we expect primal phase to terminate quickly) // // NOTE: AlphaQim is filled by intermediate FTran result which is useful // for Forest-Tomlin update scheme. If not Forest-Tomlin update is // used, then it is not set. // for(i=0; i<=m-1; i++) { state.tmp0[i] = 0; } j0 = state.at.ridx[q]; j1 = state.at.ridx[q+1]-1; for(j=j0; j<=j1; j++) { state.tmp0[state.at.idx[j]] = state.at.vals[j]; } basissolvex(state.basis, state.tmp0, ref state.alphaq, ref state.alphaqim, true, ref state.tmp2, _params); vp = settings.pivottol; p = -1; r = -1; thetap = 0; xbnd = 0; for(i=0; i<=m-1; i++) { bi = state.basis.idx[i]; alphawave = -(dir*state.alphaq[i]); vx = s.xa[bi]; if( alphawave<-vp && hasbndl(s, bi, _params) ) { vb = s.bndl[bi]; if( vx<=vb ) { // // X[Bi] is already out of bounds due to rounding errors, perform shifting // vb = vx-shiftlen; s.bndl[bi] = vx; } vtest = (vb-vx)/alphawave; if( p<0 || vtestvp && hasbndu(s, bi, _params) ) { vb = s.bndu[bi]; if( vx>=vb ) { // // X[Bi] is already out of bounds due to rounding errors, perform shifting // vb = vx+shiftlen; s.bndu[bi] = vb; } vtest = (vb-vx)/alphawave; if( p<0 || vtest=0 && (!haslim || thetap0 * feasible box constraints * some initial basis (can be dual infeasible) with actual factorization * actual initial point XC and target value Z * actual reduced cost vector D It returns: * +1 if dual feasible basis was found * -4 if problem is dual infeasible -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void invokephase1(dualsimplexstate state, dualsimplexsettings settings, alglib.xparams _params) { int m = 0; m = state.primary.m; state.repterminationtype = 0; // // Integrity checks // alglib.ap.assert(state.primary.state==ssvalid, "InvokePhase1: invalid primary X"); alglib.ap.assert(m>0, "InvokePhase1: M<=0"); // // Is it dual feasible from the very beginning (or maybe after initial DFC)? // if( (double)(initialdualfeasibilitycorrection(state, state.primary, settings, _params))<=(double)(dtol) ) { state.repterminationtype = 1; return; } // // Solve phase #1 subproblem // subprobleminitphase1(state.primary, state.basis, state.phase1, _params); initialdualfeasibilitycorrection(state, state.phase1, settings, _params); solvesubproblemdual(state, state.phase1, true, settings, ref state.repterminationtype, _params); alglib.ap.assert(state.repterminationtype>0, "DualSimplexSolver: unexpected failure of phase #1"); state.repterminationtype = 1; // // Setup initial basis for phase #2 using solution of phase #1 // subprobleminferinitialxn(state, state.primary, _params); if( (double)(initialdualfeasibilitycorrection(state, state.primary, settings, _params))>(double)(dtol) ) { state.repterminationtype = -4; return; } state.repterminationtype = 1; } /************************************************************************* Box-constrained solver; sets State.RepX, State.RepStats and State.RepTerminationType, does not change other fields. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void solveboxonly(dualsimplexstate state, alglib.xparams _params) { int i = 0; int ns = 0; ns = state.primary.ns; alglib.ap.assert(state.primary.m==0, "SolveBoxOnly: integrity check failed"); for(i=0; i<=ns-1; i++) { // // Handle infeasible variable // if( state.primary.bndt[i]==ccinfeasible ) { state.repterminationtype = -3; setzeroxystats(state, _params); return; } // // Handle fixed variable // if( state.primary.bndt[i]==ccfixed ) { state.repx[i] = state.primary.bndl[i]; state.repstats[i] = -1; continue; } // // Handle non-zero cost component // if( (double)(state.primary.rawc[i])>(double)(0) ) { if( state.primary.bndt[i]!=ccrange && state.primary.bndt[i]!=cclower ) { state.repterminationtype = -4; setzeroxystats(state, _params); return; } state.repx[i] = state.primary.bndl[i]; state.repstats[i] = -1; continue; } if( (double)(state.primary.rawc[i])<(double)(0) ) { if( state.primary.bndt[i]!=ccrange && state.primary.bndt[i]!=ccupper ) { state.repterminationtype = -4; setzeroxystats(state, _params); return; } state.repx[i] = state.primary.bndu[i]; state.repstats[i] = 1; continue; } // // Handle non-free variable with zero cost component // if( state.primary.bndt[i]==ccupper || state.primary.bndt[i]==ccrange ) { state.repx[i] = state.primary.bndu[i]; state.repstats[i] = 1; continue; } if( state.primary.bndt[i]==cclower ) { state.repx[i] = state.primary.bndl[i]; state.repstats[i] = -1; continue; } // // Free variable, zero cost component // alglib.ap.assert(state.primary.bndt[i]==ccfree, "DSSOptimize: integrity check failed"); state.repx[i] = 0; state.repstats[i] = 0; } } /************************************************************************* Zero-fill RepX, RepY, RepStats. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void setzeroxystats(dualsimplexstate state, alglib.xparams _params) { int i = 0; for(i=0; i<=state.primary.ns-1; i++) { state.repx[i] = 0; } for(i=0; i<=state.primary.m-1; i++) { state.repy[i] = 0; state.repdx[i] = 0; } for(i=0; i<=state.primary.ns+state.primary.m-1; i++) { state.repstats[i] = 0; } } /************************************************************************* This function initializes basis structure; no triangular factorization is prepared yet. Previously allocated memory is reused. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void basisinit(int ns, int m, dualsimplexbasis s, alglib.xparams _params) { int i = 0; s.ns = ns; s.m = m; apserv.ivectorgrowto(ref s.idx, m, _params); apserv.ivectorgrowto(ref s.nidx, ns, _params); apserv.bvectorgrowto(ref s.isbasic, ns+m, _params); for(i=0; i<=ns-1; i++) { s.nidx[i] = i; s.isbasic[i] = false; } for(i=0; i<=m-1; i++) { s.idx[i] = ns+i; s.isbasic[ns+i] = true; } s.trftype = 3; s.trfage = 0; s.isvalidtrf = false; apserv.rvectorsetlengthatleast(ref s.dseweights, m, _params); for(i=0; i<=m-1; i++) { s.dseweights[i] = 1.0; } s.dsevalid = false; } /************************************************************************* This function resizes basis. It is assumed that constraint matrix is completely overwritten by new one, but both matrices are similar enough so we can reuse previous basis. Dual steepest edge weights are invalidated by this function. This function: * tries to resize basis * if possible, returns True and valid basis with valid factorization * if resize is impossible (or abandoned due to stability reasons), it returns False and basis object is left in the invalid state (you have to reinitialize it by all-logicals basis) Following types of resize are supported: * new basis size is larger than previous one => logical elements are added to the new basis * basis sizes match => no operation is performed * new basis size is zero => basis is set to zero This function: * requires valid triangular factorization at S on entry * replaces it by another, valid factorization * checks that new factorization deviates from the previous one not too much by comparing magnitudes of min[abs(u_ii)] in both factorization (sharp decrease results in attempt to resize being abandoned IMPORTANT: if smooth resize is not possible, this function throws an exception! It is responsibility of the caller to check that smooth resize is possible -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static bool basistryresize(dualsimplexbasis s, int newm, sparse.sparsematrix at, dualsimplexsettings settings, alglib.xparams _params) { bool result = new bool(); int ns = 0; int oldm = 0; int i = 0; double oldminu = 0; double newminu = 0; ns = s.ns; oldm = s.m; result = false; // // Quick exit strategies // if( newm==0 ) { basisinit(ns, 0, s, _params); result = true; return result; } // // Same size or larger // if( newm>=oldm ) { alglib.ap.assert(s.isvalidtrf || oldm==0, "BasisTryResize: needs valid TRF in S"); // // Save information about matrix conditioning // oldminu = basisminimumdiagonalelement(s, _params); // // Growth if needed // s.m = newm; apserv.ivectorgrowto(ref s.idx, newm, _params); apserv.bvectorgrowto(ref s.isbasic, ns+newm, _params); for(i=oldm; i<=newm-1; i++) { s.idx[i] = ns+i; s.isbasic[ns+i] = true; } // // DSE weights are invalid and filled by 1.0 // apserv.rvectorgrowto(ref s.dseweights, newm, _params); for(i=0; i<=newm-1; i++) { s.dseweights[i] = 1.0; } s.dsevalid = false; // // Invalidate TRF. // Try to refactorize. // s.isvalidtrf = false; newminu = basisfreshtrfunsafe(s, at, settings, _params); result = (double)(newminu)>=(double)(maxudecay*oldminu); return result; } alglib.ap.assert(false, "BasisTryResize: unexpected branch"); return result; } /************************************************************************* This function returns minimum diagonal element of S. Result=1 is returned for M=0. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static double basisminimumdiagonalelement(dualsimplexbasis s, alglib.xparams _params) { double result = 0; double v = 0; double vv = 0; int i = 0; int m = 0; m = s.m; if( m==0 ) { result = 1; return result; } alglib.ap.assert(((s.trftype==0 || s.trftype==1) || s.trftype==2) || s.trftype==3, "BasisMinimumDiagonalElement: unexpected TRF type"); alglib.ap.assert(s.isvalidtrf, "BasisMinimumDiagonalElement: TRF is invalid"); v = math.maxrealnumber; for(i=0; i<=m-1; i++) { vv = 0; if( s.trftype==0 || s.trftype==1 ) { vv = s.denselu[i,i]; } if( s.trftype==2 || s.trftype==3 ) { vv = sparse.sparsegetdiagonal(s.sparseu, i, _params); } if( vv<0 ) { vv = -vv; } if( vv0 ) { alglib.ap.assert(s0.isvalidtrf, "BasisExport: valid factorization is required for source basis"); s1.eminu = basisminimumdiagonalelement(s0, _params); } else { s1.eminu = 1; } } /************************************************************************* This function imports from S1 to S0 a division of variables into basic/nonbasic ones; only basic/nonbasic sets are imported. Triangular factorization is not imported; however, this function checks that new factorization deviates from the previous one not too much by comparing magnitudes of min[abs(u_ii)] in both factorization (basis being imported stores statistics about U). Sharp decrease of diagonal elements means that we have too unstable situation which results in import being abandoned. In this case False is returned, and the basis S0 is left in the indeterminate invalid state (you have to reinitialize it by all-logicals). IMPORTANT: if metrics of S0 and S1 do not match, an exception will be generated. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static bool basistryimportfrom(dualsimplexbasis s0, dualsimplexbasis s1, sparse.sparsematrix at, dualsimplexsettings settings, alglib.xparams _params) { bool result = new bool(); int i = 0; double newminu = 0; alglib.ap.assert(s0.ns==s1.ns, "BasisImportFrom: structural variable counts do not match"); s0.m = s1.m; for(i=0; i<=s0.m-1; i++) { s0.idx[i] = s1.idx[i]; } for(i=0; i<=s0.ns-1; i++) { s0.nidx[i] = s1.nidx[i]; } for(i=0; i<=s0.m+s0.ns-1; i++) { s0.isbasic[i] = s1.isbasic[i]; } s0.isvalidtrf = false; apserv.rvectorsetlengthatleast(ref s0.dseweights, s1.m, _params); for(i=0; i<=s1.m-1; i++) { s0.dseweights[i] = 1.0; } s0.dsevalid = false; newminu = basisfreshtrfunsafe(s0, at, settings, _params); result = (double)(newminu)>=(double)(maxudecay*s1.eminu); if( !result ) { s0.isvalidtrf = false; s0.trftype = -1; } return result; } /************************************************************************* This function computes fresh triangular factorization. If TRF of age 0 (fresh) is already present, no new factorization is calculated. If factorization has exactly zero element along diagonal, this function generates exception. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void basisfreshtrf(dualsimplexbasis s, sparse.sparsematrix at, dualsimplexsettings settings, alglib.xparams _params) { double v = 0; v = basisfreshtrfunsafe(s, at, settings, _params); alglib.ap.assert((double)(v)>(double)(0), "BasisFreshTrf: degeneracy of B is detected"); } /************************************************************************* This function computes fresh triangular factorization. If TRF of age 0 (fresh) is already present, no new factorization is calculated. It returns min[abs(u[i,i])] which can be used to determine whether factorization is degenerate or not (it will factorize anything, the question is whether it is possible to use factorization) -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static double basisfreshtrfunsafe(dualsimplexbasis s, sparse.sparsematrix at, dualsimplexsettings settings, alglib.xparams _params) { double result = 0; int m = 0; int ns = 0; int i = 0; int j = 0; int k = 0; int j0 = 0; int j1 = 0; int k1 = 0; int nzl = 0; int nzu = 0; int nlogical = 0; int nstructural = 0; int offs = 0; int offs1 = 0; int offs2 = 0; m = s.m; ns = s.ns; result = 0; // // Compare TRF type with one required by settings, invalidation and refresh otherwise // if( s.trftype!=settings.trftype ) { s.trftype = settings.trftype; s.isvalidtrf = false; result = basisfreshtrfunsafe(s, at, settings, _params); return result; } // // Is it valid and fresh? // if( s.isvalidtrf && s.trfage==0 ) { result = basisminimumdiagonalelement(s, _params); return result; } // // Dense TRF // if( s.trftype==0 || s.trftype==1 ) { apserv.ivectorsetlengthatleast(ref s.colpermbwd, m, _params); for(i=0; i<=m-1; i++) { s.colpermbwd[i] = i; } apserv.rmatrixsetlengthatleast(ref s.denselu, m, m, _params); for(i=0; i<=m-1; i++) { for(j=0; j<=m-1; j++) { s.denselu[i,j] = 0; } } for(i=0; i<=m-1; i++) { j0 = at.ridx[s.idx[i]]; j1 = at.ridx[s.idx[i]+1]-1; for(j=j0; j<=j1; j++) { s.denselu[i,at.idx[j]] = at.vals[j]; } } trfac.rmatrixlu(ref s.denselu, m, m, ref s.tmpi, _params); pivottobwd(s.tmpi, m, ref s.rowpermbwd, _params); s.isvalidtrf = true; s.trfage = 0; result = basisminimumdiagonalelement(s, _params); return result; } // // Sparse TRF (with either PFI or Forest-Tomlin) // if( s.trftype==2 || s.trftype==3 ) { // // Determine permutation which moves logical variables // to the beginning. // // NOTE: this reordering results in stable factorization // because we prenormalized constraints with 2-norm, // all elements in the logical columns are less than // 1.0 in magnitude. // // After this block is done we have following arrays: // * tCInvIdx[j], which is an inverse of ColPermBwf[] // apserv.ivectorsetlengthatleast(ref s.tcinvidx, m, _params); apserv.ivectorsetlengthatleast(ref s.rowpermbwd, m, _params); apserv.ivectorsetlengthatleast(ref s.colpermbwd, m, _params); for(i=0; i<=m-1; i++) { s.tcinvidx[i] = i; s.rowpermbwd[i] = i; s.colpermbwd[i] = i; } nlogical = 0; for(i=0; i<=m-1; i++) { if( s.idx[i]>=ns ) { j = s.rowpermbwd[nlogical]; s.rowpermbwd[nlogical] = s.rowpermbwd[i]; s.rowpermbwd[i] = j; j1 = s.tcinvidx[s.idx[i]-ns]; j = s.colpermbwd[j1]; s.colpermbwd[j1] = s.colpermbwd[nlogical]; s.colpermbwd[nlogical] = j; s.tcinvidx[s.colpermbwd[nlogical]] = nlogical; s.tcinvidx[s.colpermbwd[j1]] = j1; nlogical = nlogical+1; } } tsort.sortmiddlei(s.colpermbwd, nlogical, m-nlogical, _params); for(i=0; i<=m-1; i++) { s.tcinvidx[s.colpermbwd[i]] = i; } nstructural = m-nlogical; // // Prepare SparseLU1 to receive factored out logical part of the matrix // and SparseLU2 to receive structural part of the matrix. // apserv.ivectorsetlengthatleast(ref s.sparselu1.ridx, nstructural+1, _params); apserv.ivectorsetlengthatleast(ref s.sparselu1.didx, nstructural, _params); apserv.ivectorsetlengthatleast(ref s.sparselu1.uidx, nstructural, _params); s.sparselu1.matrixtype = 1; s.sparselu1.m = nstructural; s.sparselu1.n = nlogical; s.sparselu1.ridx[0] = 0; apserv.ivectorsetlengthatleast(ref s.sparselu2.ridx, nstructural+1, _params); apserv.ivectorsetlengthatleast(ref s.sparselu2.didx, nstructural, _params); apserv.ivectorsetlengthatleast(ref s.sparselu2.uidx, nstructural, _params); s.sparselu2.matrixtype = 1; s.sparselu2.m = nstructural; s.sparselu2.n = nstructural; s.sparselu2.ridx[0] = 0; // // Reorder array, perform LU factorization // for(k=0; k<=nstructural-1; k++) { // // Make sure SparseLU1 and SparseLU2 have enough place. // offs1 = s.sparselu1.ridx[k]; offs2 = s.sparselu2.ridx[k]; apserv.ivectorgrowto(ref s.sparselu1.idx, offs1+m, _params); apserv.rvectorgrowto(ref s.sparselu1.vals, offs1+m, _params); apserv.ivectorgrowto(ref s.sparselu2.idx, offs2+m, _params); apserv.rvectorgrowto(ref s.sparselu2.vals, offs2+m, _params); // // Extract K-th row of the SparseLU1/2 (I-th row of the original matrix) // i = s.rowpermbwd[k+nlogical]; j0 = at.ridx[s.idx[i]]; j1 = at.ridx[s.idx[i]+1]-1; for(j=j0; j<=j1; j++) { k1 = s.tcinvidx[at.idx[j]]; if( k10 ) { sptrf.sptrflu(s.sparselu2, 2, ref s.densep2, ref s.densep2c, s.lubuf2, _params); for(i=0; i<=nstructural-1; i++) { j = s.rowpermbwd[i+nlogical]; s.rowpermbwd[i+nlogical] = s.rowpermbwd[s.densep2[i]+nlogical]; s.rowpermbwd[s.densep2[i]+nlogical] = j; j = s.colpermbwd[i+nlogical]; s.colpermbwd[i+nlogical] = s.colpermbwd[s.densep2c[i]+nlogical]; s.colpermbwd[s.densep2c[i]+nlogical] = j; } // // Process L factor: // // 1. count number of non-zeros in the L factor, // 2. fill NLogical*NLogical leading block // 3. NStructural*M bottom block // nzl = nlogical; for(i=0; i<=nstructural-1; i++) { k = s.lubuf2.rowpermrawidx[i]; nzl = nzl+(s.sparselu1.ridx[k+1]-s.sparselu1.ridx[k]); nzl = nzl+1+(s.sparselu2.didx[i]-s.sparselu2.ridx[i]); } apserv.rvectorsetlengthatleast(ref s.sparsel.vals, nzl, _params); apserv.ivectorsetlengthatleast(ref s.sparsel.idx, nzl, _params); apserv.ivectorsetlengthatleast(ref s.sparsel.ridx, m+1, _params); apserv.ivectorsetlengthatleast(ref s.sparsel.didx, m, _params); apserv.ivectorsetlengthatleast(ref s.sparsel.uidx, m, _params); s.sparsel.matrixtype = 1; s.sparsel.m = m; s.sparsel.n = m; s.sparsel.ninitialized = nzl; s.sparsel.ridx[0] = 0; for(i=0; i<=nlogical-1; i++) { s.sparsel.idx[i] = i; s.sparsel.vals[i] = 1.0; s.sparsel.ridx[i+1] = i+1; } for(i=0; i<=nstructural-1; i++) { offs = s.sparsel.ridx[nlogical+i]; k = s.lubuf2.rowpermrawidx[i]; j0 = s.sparselu1.ridx[k]; j1 = s.sparselu1.ridx[k+1]-1; for(j=j0; j<=j1; j++) { s.sparsel.idx[offs] = s.sparselu1.idx[j]; s.sparsel.vals[offs] = -s.sparselu1.vals[j]; offs = offs+1; } j0 = s.sparselu2.ridx[i]; j1 = s.sparselu2.didx[i]-1; for(j=j0; j<=j1; j++) { s.sparsel.idx[offs] = nlogical+s.sparselu2.idx[j]; s.sparsel.vals[offs] = s.sparselu2.vals[j]; offs = offs+1; } s.sparsel.idx[offs] = nlogical+i; s.sparsel.vals[offs] = 1.0; offs = offs+1; s.sparsel.ridx[nlogical+i+1] = offs; } alglib.ap.assert(s.sparsel.ninitialized==s.sparsel.ridx[m], "BasisFreshTrf: integrity check failed"); sparse.sparseinitduidx(s.sparsel, _params); // // Process U factor: // // 1. count number of non-zeros in the U factor, // 2. fill NLogical*NLogical leading block // 3. NStructural*NStructural bottom block // nzu = nlogical; for(i=0; i<=nstructural-1; i++) { nzu = nzu+1+(s.sparselu2.ridx[i+1]-s.sparselu2.uidx[i]); } apserv.rvectorsetlengthatleast(ref s.sparseu.vals, nzu, _params); apserv.ivectorsetlengthatleast(ref s.sparseu.idx, nzu, _params); apserv.ivectorsetlengthatleast(ref s.sparseu.ridx, m+1, _params); apserv.ivectorsetlengthatleast(ref s.sparseu.didx, m, _params); apserv.ivectorsetlengthatleast(ref s.sparseu.uidx, m, _params); s.sparseu.matrixtype = 1; s.sparseu.m = m; s.sparseu.n = m; s.sparseu.ninitialized = nzu; s.sparseu.ridx[0] = 0; for(i=0; i<=nlogical-1; i++) { s.sparseu.idx[i] = i; s.sparseu.vals[i] = -1.0; s.sparseu.ridx[i+1] = i+1; } for(i=0; i<=nstructural-1; i++) { offs = s.sparseu.ridx[nlogical+i]; s.sparseu.idx[offs] = nlogical+i; j = s.sparselu2.didx[i]; if( j=settings.maxtrfage ) { // // Complete refresh is needed for factorization // s.isvalidtrf = false; basisfreshtrf(s, at, settings, _params); } else { processed = false; if( (s.trftype==0 || s.trftype==1) || s.trftype==2 ) { // // Dense/sparse factorizations with dense PFI // alglib.ap.assert((double)(alphaq[r])!=(double)(0), "BasisUpdateTrf: integrity check failed, AlphaQ[R]=0"); apserv.rvectorgrowto(ref s.densepfieta, (s.trfage+1)*m, _params); apserv.ivectorgrowto(ref s.rk, s.trfage+1, _params); s.rk[s.trfage] = r; invaq = 1.0/alphaq[r]; for(i=0; i<=m-1; i++) { if( i!=r ) { s.densepfieta[s.trfage*m+i] = -(alphaq[i]*invaq); } else { s.densepfieta[s.trfage*m+i] = invaq; } } apserv.inc(ref s.trfage, _params); processed = true; } if( s.trftype==3 ) { // // Sparse factorization with Forest-Tomlin update // alglib.ap.assert((double)(alphaq[r])!=(double)(0), "BasisUpdateTrf: integrity check failed, AlphaQ[R]=0"); apserv.rvectorgrowto(ref s.densemu, (s.trfage+1)*m, _params); apserv.ivectorgrowto(ref s.rk, s.trfage+1, _params); apserv.ivectorgrowto(ref s.dk, s.trfage+1, _params); apserv.rvectorsetlengthatleast(ref s.utmp0, m, _params); // // Determine D - index of row being overwritten by Forest-Tomlin update // idxd = -1; for(i=0; i<=m-1; i++) { if( s.rowpermbwd[i]==r ) { idxd = i; break; } } alglib.ap.assert(idxd>=0, "BasisUpdateTrf: unexpected integrity check failure"); s.rk[s.trfage] = r; s.dk[s.trfage] = idxd; // // Modify L with permutation which moves D-th row/column to the end: // * rows 0...D-1 are left intact // * rows D+1...M-1 are moved one position up, with columns 0..D-1 // retained as is, and columns D+1...M-1 being moved one position left. // * last row is filled by permutation/modification of AlphaQim // Determine FT update coefficients in the process. // apserv.ivectorgrowto(ref s.sparsel.idx, s.sparsel.ridx[m]+m, _params); apserv.rvectorgrowto(ref s.sparsel.vals, s.sparsel.ridx[m]+m, _params); for(i=0; i<=m-1; i++) { s.utmp0[i] = 0; } for(i=idxd+1; i<=m-1; i++) { j = s.sparsel.ridx[i+1]-1; if( s.sparsel.idx[j]!=i || s.sparsel.vals[j]!=1 ) { alglib.ap.assert(false, "UpdateTrf: integrity check failed for sparse L"); } dstoffs = s.sparsel.ridx[i-1]; srcoffs = s.sparsel.ridx[i]; // // Read first element in the row (it has at least one - unit diagonal) // srcidx = s.sparsel.idx[srcoffs]; srcval = s.sparsel.vals[srcoffs]; // // Read/write columns 0...D-1 // while( srcidx=0; k--) { v = 0; for(i=0; i<=m-1; i++) { v = v+s.densepfieta[k*m+i]*x[i]; } x[s.rk[k]] = v; } for(i=0; i<=m-1; i++) { tx[i] = x[s.rowpermbwd[i]]; } for(i=0; i<=m-1; i++) { x[i] = tx[i]; } if( s.trftype==0 || s.trftype==1 ) { // // Dense TRF // ablas.rmatrixtrsv(m, s.denselu, 0, 0, false, true, 0, x, 0, _params); ablas.rmatrixtrsv(m, s.denselu, 0, 0, true, false, 0, x, 0, _params); } else { // // Sparse TRF // sparse.sparsetrsv(s.sparsel, false, false, 0, x, _params); sparse.sparsetrsv(s.sparseu, true, false, 0, x, _params); } for(i=0; i<=m-1; i++) { tx[s.colpermbwd[i]] = x[i]; } for(i=0; i<=m-1; i++) { x[i] = tx[i]; } processed = true; } // // Sparse factorization with Forest-Tomlin update // if( s.trftype==3 ) { apserv.rvectorsetlengthatleast(ref x, m, _params); for(i=0; i<=m-1; i++) { x[i] = r[i]; } for(i=0; i<=m-1; i++) { tx[i] = x[s.rowpermbwd[i]]; } for(i=0; i<=m-1; i++) { x[i] = tx[i]; } sparse.sparsetrsv(s.sparsel, false, false, 0, x, _params); for(k=s.trfage-1; k>=0; k--) { // // The code below is an amalgamation of two parts: // // triangular factor // V:=X[M-1]; // for I:=D to M-2 do // X[I]:=X[I]+S.DenseMu[K*M+I]*V; // X[M-1]:=S.DenseMu[K*M+(M-1)]*V; // // inverse of cyclic permutation // V:=X[M-1]; // for I:=M-1 downto D+1 do // X[I]:=X[I-1]; // X[D]:=V; // d = s.dk[k]; vm = x[m-1]; v = s.densemu[k*m+(m-1)]*vm; if( vm!=0 ) { // // X[M-1] is non-zero, apply update // for(i=m-2; i>=d; i--) { x[i+1] = x[i]+s.densemu[k*m+i]*vm; } } else { // // X[M-1] is zero, just cyclic permutation // for(i=m-2; i>=d; i--) { x[i+1] = x[i]; } } x[d] = v; } sparse.sparsetrsv(s.sparseut, false, false, 1, x, _params); for(i=0; i<=m-1; i++) { tx[s.colpermbwd[i]] = x[i]; } for(i=0; i<=m-1; i++) { x[i] = tx[i]; } processed = true; } // // Integrity check // alglib.ap.assert(processed, "BasisSolveT: unsupported TRF type"); v = 0; for(i=0; i<=m-1; i++) { v = v+x[i]; } alglib.ap.assert(math.isfinite(v), "BasisSolveT: integrity check failed (degeneracy in B?)"); } /************************************************************************* This function computes product AN*XN, where AN is a non-basic subset of columns of A, and XN is a non-basic subset of columns of X. Output array is reallocated if its size is too small. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void computeanxn(dualsimplexstate state, dualsimplexsubproblem subproblem, double[] x, ref double[] y, alglib.xparams _params) { int nn = 0; int nx = 0; int m = 0; int i = 0; int j = 0; int k = 0; int j0 = 0; int j1 = 0; double v = 0; nx = subproblem.ns+subproblem.m; m = subproblem.m; nn = nx-m; // // Integrity check // alglib.ap.assert(subproblem.state>=ssvalidxn, "ComputeANXN: XN is invalid"); // // Compute // apserv.rvectorsetlengthatleast(ref y, m, _params); for(i=0; i<=m-1; i++) { y[i] = 0; } for(i=0; i<=nn-1; i++) { j0 = state.at.ridx[state.basis.nidx[i]]; j1 = state.at.ridx[state.basis.nidx[i]+1]-1; v = x[state.basis.nidx[i]]; for(j=j0; j<=j1; j++) { k = state.at.idx[j]; y[k] = y[k]+v*state.at.vals[j]; } } } /************************************************************************* This function computes product (AN^T)*y, where AN is a non-basic subset of columns of A, and y is some vector. Output array is set to full NX-sized length, with basic components of the output being set to zeros. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void computeantv(dualsimplexstate state, double[] y, ref double[] r, alglib.xparams _params) { int nn = 0; int nx = 0; int m = 0; int i = 0; int j = 0; int j0 = 0; int j1 = 0; double v = 0; nx = state.ns+state.m; m = state.m; nn = nx-m; // // Allocate output, set to zero // apserv.rvectorsetlengthatleast(ref r, nx, _params); for(i=0; i<=nx-1; i++) { r[i] = 0; } for(i=0; i<=nn-1; i++) { j0 = state.at.ridx[state.basis.nidx[i]]; j1 = state.at.ridx[state.basis.nidx[i]+1]-1; v = 0; for(j=j0; j<=j1; j++) { v = v+state.at.vals[j]*y[state.at.idx[j]]; } r[state.basis.nidx[i]] = v; } } /************************************************************************* Returns True if I-th lower bound is present -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static bool hasbndl(dualsimplexsubproblem subproblem, int i, alglib.xparams _params) { bool result = new bool(); int k = 0; k = subproblem.bndt[i]; result = false; if( (k==0 || k==1) || k==3 ) { result = true; return result; } if( k==2 || k==4 ) { result = false; return result; } alglib.ap.assert(false, "HasBndL: integrity check failed"); return result; } /************************************************************************* Returns True if I-th upper bound is present -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static bool hasbndu(dualsimplexsubproblem subproblem, int i, alglib.xparams _params) { bool result = new bool(); int k = 0; k = subproblem.bndt[i]; result = false; if( (k==0 || k==2) || k==3 ) { result = true; return result; } if( k==1 || k==4 ) { result = false; return result; } alglib.ap.assert(false, "HasBndL: integrity check failed"); return result; } /************************************************************************* Returns True if I-th variable if free -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static bool isfree(dualsimplexsubproblem subproblem, int i, alglib.xparams _params) { bool result = new bool(); int k = 0; k = subproblem.bndt[i]; result = false; if( ((k==0 || k==1) || k==2) || k==3 ) { result = false; return result; } if( k==4 ) { result = true; return result; } alglib.ap.assert(false, "IsFree: integrity check failed"); return result; } /************************************************************************* Downgrades problem state to the specified one (if status is lower than one specified by user, nothing is changed) -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static void downgradestate(dualsimplexsubproblem subproblem, int s, alglib.xparams _params) { subproblem.state = Math.Min(subproblem.state, s); } /************************************************************************* Returns maximum dual infeasibility (only non-basic variables are checked, we assume that basic variables are good enough). -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static double dualfeasibilityerror(dualsimplexstate state, dualsimplexsubproblem s, alglib.xparams _params) { double result = 0; int i = 0; int j = 0; int nn = 0; int bndt = 0; nn = s.ns; alglib.ap.assert(s.state==ssvalid, "DualFeasibilityError: invalid X"); result = 0; for(i=0; i<=nn-1; i++) { j = state.basis.nidx[i]; bndt = s.bndt[j]; if( bndt==ccfixed ) { continue; } if( bndt==ccrange ) { if( s.xa[j]==s.bndl[j] ) { result = Math.Max(result, -s.d[j]); continue; } if( s.xa[j]==s.bndu[j] ) { result = Math.Max(result, s.d[j]); continue; } alglib.ap.assert(false, "DualFeasibilityError: integrity check failed"); } if( bndt==cclower ) { alglib.ap.assert(s.xa[j]==s.bndl[j], "DualFeasibilityError: integrity check failed"); result = Math.Max(result, -s.d[j]); continue; } if( bndt==ccupper ) { alglib.ap.assert(s.xa[j]==s.bndu[j], "DualFeasibilityError: integrity check failed"); result = Math.Max(result, s.d[j]); continue; } if( bndt==ccfree ) { result = Math.Max(result, Math.Abs(s.d[j])); continue; } alglib.ap.assert(false, "DSSOptimize: integrity check failed (infeasible constraint)"); } return result; } /************************************************************************* Returns True for dual feasible basis (some minor dual feasibility error is allowed), False otherwise -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ private static bool isdualfeasible(dualsimplexstate state, dualsimplexsubproblem s, alglib.xparams _params) { bool result = new bool(); result = (double)(dualfeasibilityerror(state, s, _params))<=(double)(dtol); return result; } /************************************************************************* Transforms sequence of pivot permutations P0*P1*...*Pm to forward/backward permutation representation. -- ALGLIB -- Copyright 12.09.2018 by Bochkanov Sergey *************************************************************************/ private static void pivottobwd(int[] p, int m, ref int[] bwd, alglib.xparams _params) { int i = 0; int k = 0; int t = 0; apserv.ivectorsetlengthatleast(ref bwd, m, _params); for(i=0; i<=m-1; i++) { bwd[i] = i; } for(i=0; i<=m-1; i++) { k = p[i]; if( k!=i ) { t = bwd[i]; bwd[i] = bwd[k]; bwd[k] = t; } } } /************************************************************************* Applies inverse cyclic permutation of [D,M-1) (element D is moved to the end, the rest of elements is shifted one position backward) to the already existing permutation. -- ALGLIB -- Copyright 12.09.2018 by Bochkanov Sergey *************************************************************************/ private static void inversecyclicpermutation(int[] bwd, int m, int d, ref int[] tmpi, alglib.xparams _params) { int i = 0; int k = 0; // // update Bwd[] // k = bwd[d]; for(i=d; i<=m-2; i++) { bwd[i] = bwd[i+1]; } bwd[m-1] = k; } /************************************************************************* Offloads basic components of X[], BndT[], BndL[], BndU[] to XB/BndTB/BndLB/BndUB. -- ALGLIB -- Copyright 24.01.2019 by Bochkanov Sergey *************************************************************************/ private static void offloadbasiccomponents(dualsimplexsubproblem s, dualsimplexbasis basis, alglib.xparams _params) { int i = 0; int m = 0; m = basis.m; for(i=0; i<=m-1; i++) { s.xb[i] = s.xa[basis.idx[i]]; s.bndlb[i] = s.bndl[basis.idx[i]]; s.bndub[i] = s.bndu[basis.idx[i]]; s.bndtb[i] = s.bndt[basis.idx[i]]; } } /************************************************************************* Recombines basic and non-basic components in X[] -- ALGLIB -- Copyright 24.01.2019 by Bochkanov Sergey *************************************************************************/ private static void recombinebasicnonbasicx(dualsimplexsubproblem s, dualsimplexbasis basis, alglib.xparams _params) { int m = 0; int i = 0; m = basis.m; for(i=0; i<=m-1; i++) { s.xa[basis.idx[i]] = s.xb[i]; } } /************************************************************************* Restores original scale of the variables, enforces box constraints (just to be sure) -- ALGLIB -- Copyright 24.01.2019 by Bochkanov Sergey *************************************************************************/ private static void unscaleandenforce(double[] repx, double[] repy, double[] repdx, dualsimplexstate s, alglib.xparams _params) { int i = 0; int ns = 0; int m = 0; ns = s.ns; m = s.m; for(i=0; i<=ns-1; i++) { if( s.repstats[i]<0 ) { repx[i] = s.rawbndl[i]; continue; } if( s.repstats[i]>0 ) { repx[i] = s.rawbndu[i]; continue; } repx[i] = repx[i]*s.varscales[i]; if( math.isfinite(s.rawbndl[i]) ) { repx[i] = Math.Max(repx[i], s.rawbndl[i]); } if( math.isfinite(s.rawbndu[i]) ) { repx[i] = Math.Min(repx[i], s.rawbndu[i]); } } for(i=0; i<=m-1; i++) { repy[i] = repy[i]/s.rowscales[i]; repdx[i] = repdx[i]/s.rowscales[i]; } } } public class minlp { /************************************************************************* This object stores linear solver state. You should use functions provided by MinLP subpackage to work with this object *************************************************************************/ public class minlpstate : apobject { public int n; public int algokind; public double[] s; public double[] c; public double[] bndl; public double[] bndu; public int m; public sparse.sparsematrix a; public double[] al; public double[] au; public double[] xs; public double[] ys; public int[] cs; public double repf; public double repprimalerror; public double repdualerror; public int repiterationscount; public int repterminationtype; public reviseddualsimplex.dualsimplexstate dss; public int[] adddtmpi; public double[] adddtmpr; public minlpstate() { init(); } public override void init() { s = new double[0]; c = new double[0]; bndl = new double[0]; bndu = new double[0]; a = new sparse.sparsematrix(); al = new double[0]; au = new double[0]; xs = new double[0]; ys = new double[0]; cs = new int[0]; dss = new reviseddualsimplex.dualsimplexstate(); adddtmpi = new int[0]; adddtmpr = new double[0]; } public override alglib.apobject make_copy() { minlpstate _result = new minlpstate(); _result.n = n; _result.algokind = algokind; _result.s = (double[])s.Clone(); _result.c = (double[])c.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.m = m; _result.a = (sparse.sparsematrix)a.make_copy(); _result.al = (double[])al.Clone(); _result.au = (double[])au.Clone(); _result.xs = (double[])xs.Clone(); _result.ys = (double[])ys.Clone(); _result.cs = (int[])cs.Clone(); _result.repf = repf; _result.repprimalerror = repprimalerror; _result.repdualerror = repdualerror; _result.repiterationscount = repiterationscount; _result.repterminationtype = repterminationtype; _result.dss = (reviseddualsimplex.dualsimplexstate)dss.make_copy(); _result.adddtmpi = (int[])adddtmpi.Clone(); _result.adddtmpr = (double[])adddtmpr.Clone(); return _result; } }; /************************************************************************* This structure stores optimization report: * f target function value * y dual variables * stats array[N+M], statuses of box (N) and linear (M) constraints: * stats[i]>0 => constraint at upper bound (also used for free non-basic variables set to zero) * stats[i]<0 => constraint at lower bound * stats[i]=0 => constraint is inactive, basic variable * primalerror primal feasibility error * dualerror dual feasibility error * iterationscount iteration count * terminationtype completion code (see below) Completion codes: * -4 LP problem is primal unbounded (dual infeasible) * -3 LP problem is primal infeasible (dual unbounded) * 1..4 successful completion * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. *************************************************************************/ public class minlpreport : apobject { public double f; public double[] y; public int[] stats; public double primalerror; public double dualerror; public int iterationscount; public int terminationtype; public minlpreport() { init(); } public override void init() { y = new double[0]; stats = new int[0]; } public override alglib.apobject make_copy() { minlpreport _result = new minlpreport(); _result.f = f; _result.y = (double[])y.Clone(); _result.stats = (int[])stats.Clone(); _result.primalerror = primalerror; _result.dualerror = dualerror; _result.iterationscount = iterationscount; _result.terminationtype = terminationtype; return _result; } }; public const int alllogicalsbasis = 0; /************************************************************************* LINEAR PROGRAMMING The subroutine creates LP solver. After initial creation it contains default optimization problem with zero cost vector and all variables being fixed to zero values and no constraints. In order to actually solve something you should: * set cost vector with minlpsetcost() * set variable bounds with minlpsetbc() or minlpsetbcall() * specify constraint matrix with one of the following functions: [*] minlpsetlc() for dense one-sided constraints [*] minlpsetlc2dense() for dense two-sided constraints [*] minlpsetlc2() for sparse two-sided constraints [*] minlpaddlc2dense() to add one dense row to constraint matrix [*] minlpaddlc2() to add one row to constraint matrix (compressed format) * call minlpoptimize() to run the solver and minlpresults() to get the solution vector and additional information. Presently this optimizer supports only revised simplex method as underlying solver. DSE pricing and bounds flipping ratio test (aka long dual step) are supported. Large-scale sparse LU solver with Forest-Tomlin is used internally as linear algebra driver. Future releases of ALGLIB may introduce other solvers. INPUT PARAMETERS: N - problem size OUTPUT PARAMETERS: State - optimizer in the default state -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpcreate(int n, minlpstate state, alglib.xparams _params) { int i = 0; alglib.ap.assert(n>=1, "MinLPCreate: N<1"); // // Initialize // state.n = n; state.m = 0; state.algokind = 1; state.c = new double[n]; state.s = new double[n]; state.bndl = new double[n]; state.bndu = new double[n]; state.xs = new double[n]; for(i=0; i<=n-1; i++) { state.bndl[i] = 0; state.bndu[i] = 0; state.c[i] = 0.0; state.s[i] = 1.0; state.xs[i] = 1.0; } clearreportfields(state, _params); } /************************************************************************* This function sets cost term for LP solver. By default, cost term is zero. INPUT PARAMETERS: State - structure which stores algorithm state C - cost term, array[N]. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetcost(minlpstate state, double[] c, alglib.xparams _params) { int n = 0; int i = 0; n = state.n; alglib.ap.assert(alglib.ap.len(c)>=n, "MinLPSetCost: Length(C)=state.n, "MinLPSetScale: Length(S)BndU will result in LP problem being recognized as infeasible. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetbc(minlpstate state, double[] bndl, double[] bndu, alglib.xparams _params) { int i = 0; int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(bndl)>=n, "MinLPSetBC: Length(BndL)=n, "MinLPSetBC: Length(BndU)BndU will result in LP problem being recognized as infeasible. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetbcall(minlpstate state, double bndl, double bndu, alglib.xparams _params) { int i = 0; int n = 0; n = state.n; alglib.ap.assert(math.isfinite(bndl) || Double.IsNegativeInfinity(bndl), "MinLPSetBC: BndL is NAN or +INF"); alglib.ap.assert(math.isfinite(bndu) || Double.IsPositiveInfinity(bndu), "MinLPSetBC: BndU is NAN or -INF"); for(i=0; i<=n-1; i++) { state.bndl[i] = bndl; state.bndu[i] = bndu; } } /************************************************************************* This function sets box constraints for I-th variable (other variables are not modified). The default state of constraints is to have all variables fixed at zero. You have to overwrite it by your own constraint vector. Following types of constraints are supported: DESCRIPTION CONSTRAINT HOW TO SPECIFY fixed variable x[i]=Bnd[i] BndL[i]=BndU[i] lower bound BndL[i]<=x[i] BndU[i]=+INF upper bound x[i]<=BndU[i] BndL[i]=-INF range BndL[i]<=x[i]<=BndU[i] ... free variable - BndL[I]=-INF, BndU[I]+INF INPUT PARAMETERS: State - structure stores algorithm state I - variable index, in [0,N) BndL - lower bound for I-th variable BndU - upper bound for I-th variable NOTE: infinite values can be specified by means of Double.PositiveInfinity and Double.NegativeInfinity (in C#) and alglib::fp_posinf and alglib::fp_neginf (in C++). NOTE: you may replace infinities by very small/very large values, but it is not recommended because large numbers may introduce large numerical errors in the algorithm. NOTE: minlpsetbc() can be used to specify different constraints for different variables. NOTE: BndL>BndU will result in LP problem being recognized as infeasible. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetbci(minlpstate state, int i, double bndl, double bndu, alglib.xparams _params) { int n = 0; n = state.n; alglib.ap.assert(i>=0 && i=". IMPORTANT: this function is provided here for compatibility with the rest of ALGLIB optimizers which accept constraints in format like this one. Many real-life problems feature two-sided constraints like a0 <= a*x <= a1. It is really inefficient to add them as a pair of one-sided constraints. Use minlpsetlc2dense(), minlpsetlc2(), minlpaddlc2() (or its sparse version) wherever possible. INPUT PARAMETERS: State - structure previously allocated with minlpcreate() call. A - linear constraints, array[K,N+1]. Each row of A represents one constraint, with first N elements being linear coefficients, and last element being right side. CT - constraint types, array[K]: * if CT[i]>0, then I-th constraint is A[i,*]*x >= A[i,n] * if CT[i]=0, then I-th constraint is A[i,*]*x = A[i,n] * if CT[i]<0, then I-th constraint is A[i,*]*x <= A[i,n] K - number of equality/inequality constraints, K>=0; if not given, inferred from sizes of A and CT. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetlc(minlpstate state, double[,] a, int[] ct, int k, alglib.xparams _params) { double[] al = new double[0]; double[] au = new double[0]; int n = 0; int i = 0; n = state.n; alglib.ap.assert(k>=0, "MinLPSetLC: K<0"); alglib.ap.assert(k==0 || alglib.ap.cols(a)>=n+1, "MinLPSetLC: Cols(A)=k, "MinLPSetLC: Rows(A)=k, "MinLPSetLC: Length(CT)0 ) { al[i] = a[i,n]; au[i] = Double.PositiveInfinity; continue; } if( ct[i]<0 ) { al[i] = Double.NegativeInfinity; au[i] = a[i,n]; continue; } al[i] = a[i,n]; au[i] = a[i,n]; } minlpsetlc2dense(state, a, al, au, k, _params); } /************************************************************************* This function sets two-sided linear constraints AL <= A*x <= AU. This version accepts dense matrix as input; internally LP solver uses sparse storage anyway (most LP problems are sparse), but for your convenience it may accept dense inputs. This function overwrites linear constraints set by previous calls (if such calls were made). We recommend you to use sparse version of this function unless you solve small-scale LP problem (less than few hundreds of variables). NOTE: there also exist several versions of this function: * one-sided dense version which accepts constraints in the same format as one used by QP and NLP solvers * two-sided sparse version which accepts sparse matrix * two-sided dense version which allows you to add constraints row by row * two-sided sparse version which allows you to add constraints row by row INPUT PARAMETERS: State - structure previously allocated with minlpcreate() call. A - linear constraints, array[K,N]. Each row of A represents one constraint. One-sided inequality constraints, two- sided inequality constraints, equality constraints are supported (see below) AL, AU - lower and upper bounds, array[K]; * AL[i]=AU[i] => equality constraint Ai*x * AL[i] two-sided constraint AL[i]<=Ai*x<=AU[i] * AL[i]=-INF => one-sided constraint Ai*x<=AU[i] * AU[i]=+INF => one-sided constraint AL[i]<=Ai*x * AL[i]=-INF, AU[i]=+INF => constraint is ignored K - number of equality/inequality constraints, K>=0; if not given, inferred from sizes of A, AL, AU. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetlc2dense(minlpstate state, double[,] a, double[] al, double[] au, int k, alglib.xparams _params) { int i = 0; int j = 0; int n = 0; int nz = 0; int[] nrs = new int[0]; n = state.n; alglib.ap.assert(k>=0, "MinLPSetLC2Dense: K<0"); alglib.ap.assert(k==0 || alglib.ap.cols(a)>=n, "MinLPSetLC2Dense: Cols(A)=k, "MinLPSetLC2Dense: Rows(A)=k, "MinLPSetLC2Dense: Length(AL)=k, "MinLPSetLC2Dense: Length(AU) equality constraint Ai*x * AL[i] two-sided constraint AL[i]<=Ai*x<=AU[i] * AL[i]=-INF => one-sided constraint Ai*x<=AU[i] * AU[i]=+INF => one-sided constraint AL[i]<=Ai*x * AL[i]=-INF, AU[i]=+INF => constraint is ignored K - number of equality/inequality constraints, K>=0. If K=0 is specified, A, AL, AU are ignored. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpsetlc2(minlpstate state, sparse.sparsematrix a, double[] al, double[] au, int k, alglib.xparams _params) { int n = 0; int i = 0; n = state.n; // // Quick exit // if( k==0 ) { state.m = 0; return; } // // Integrity checks // alglib.ap.assert(k>0, "MinLPSetLC2: K<0"); alglib.ap.assert(sparse.sparsegetncols(a, _params)==n, "MinLPSetLC2: Cols(A)<>N"); alglib.ap.assert(sparse.sparsegetnrows(a, _params)==k, "MinLPSetLC2: Rows(A)<>K"); alglib.ap.assert(alglib.ap.len(al)>=k, "MinLPSetLC2: Length(AL)=k, "MinLPSetLC2: Length(AU) equality constraint Ai*x * AL two-sided constraint AL<=A*x<=AU * AL=-INF => one-sided constraint Ai*x<=AU * AU=+INF => one-sided constraint AL<=Ai*x * AL=-INF, AU=+INF => constraint is ignored -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpaddlc2dense(minlpstate state, double[] a, double al, double au, alglib.xparams _params) { int i = 0; int n = 0; int nnz = 0; n = state.n; alglib.ap.assert(alglib.ap.len(a)>=n, "MinLPAddLC2Dense: Length(A) equality constraint A*x * AL two-sided constraint AL<=A*x<=AU * AL=-INF => one-sided constraint A*x<=AU * AU=+INF => one-sided constraint AL<=A*x * AL=-INF, AU=+INF => constraint is ignored -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey *************************************************************************/ public static void minlpaddlc2(minlpstate state, int[] idxa, double[] vala, int nnz, double al, double au, alglib.xparams _params) { int i = 0; int j = 0; int k = 0; int offs = 0; int offsdst = 0; int m = 0; int n = 0; int didx = 0; int uidx = 0; m = state.m; n = state.n; // // Check inputs // alglib.ap.assert(nnz>=0, "MinLPAddLC2: NNZ<0"); alglib.ap.assert(alglib.ap.len(idxa)>=nnz, "MinLPAddLC2: Length(IdxA)=nnz, "MinLPAddLC2: Length(ValA)=0 && idxa[i]0 // (no need to care about degenerate cases). // // Append rows to A: // * append data // * sort in place // * merge duplicate indexes // * compute DIdx and UIdx // // for(i=0; i<=nnz-1; i++) { state.a.idx[offs+i] = idxa[i]; state.a.vals[offs+i] = vala[i]; } tsort.tagsortmiddleir(ref state.a.idx, ref state.a.vals, offs, nnz, _params); offsdst = offs; for(i=1; i<=nnz-1; i++) { if( state.a.idx[offsdst]!=state.a.idx[offs+i] ) { offsdst = offsdst+1; state.a.idx[offsdst] = state.a.idx[offs+i]; state.a.vals[offsdst] = state.a.vals[offs+i]; } else { state.a.vals[offsdst] = state.a.vals[offsdst]+state.a.vals[offs+i]; } } nnz = offsdst-offs+1; uidx = -1; didx = -1; for(j=offs; j<=offsdst; j++) { k = state.a.idx[j]; if( k==m ) { didx = j; } else { if( k>m && uidx==-1 ) { uidx = j; break; } } } if( uidx==-1 ) { uidx = offsdst+1; } if( didx==-1 ) { didx = uidx; } state.a.didx[m] = didx; state.a.uidx[m] = uidx; state.a.ridx[m+1] = offsdst+1; state.a.m = m+1; state.al[m] = al; state.au[m] = au; state.m = m+1; } /************************************************************************* This function solves LP problem. INPUT PARAMETERS: State - algorithm state You should use minlpresults() function to access results after calls to this function. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey. *************************************************************************/ public static void minlpoptimize(minlpstate state, alglib.xparams _params) { int n = 0; int m = 0; int i = 0; reviseddualsimplex.dualsimplexsettings settings = new reviseddualsimplex.dualsimplexsettings(); double[,] dummy = new double[0,0]; reviseddualsimplex.dualsimplexbasis dummybasis = new reviseddualsimplex.dualsimplexbasis(); n = state.n; m = state.m; clearreportfields(state, _params); // // Most basic check for correctness of constraints // for(i=0; i<=n-1; i++) { if( (double)(state.bndl[i])>(double)(state.bndu[i]) ) { state.repterminationtype = -3; return; } } for(i=0; i<=m-1; i++) { if( (double)(state.al[i])>(double)(state.au[i]) ) { state.repterminationtype = -3; return; } } // // Call current solver // if( state.algokind==1 ) { // // Dual simplex method // reviseddualsimplex.dsssettingsinit(settings, _params); reviseddualsimplex.dssinit(n, state.dss, _params); reviseddualsimplex.dsssetproblem(state.dss, state.c, state.bndl, state.bndu, state.s, dummy, state.a, 1, state.al, state.au, m, dummybasis, alllogicalsbasis, settings, _params); reviseddualsimplex.dssoptimize(state.dss, settings, _params); // // Export results // apserv.rvectorsetlengthatleast(ref state.xs, n, _params); apserv.rvectorsetlengthatleast(ref state.ys, m, _params); apserv.ivectorsetlengthatleast(ref state.cs, n+m, _params); for(i=0; i<=n-1; i++) { state.xs[i] = state.dss.repx[i]; } for(i=0; i<=m-1; i++) { state.ys[i] = state.dss.repy[i]; } for(i=0; i<=n+m-1; i++) { state.cs[i] = state.dss.repstats[i]; } state.repf = state.dss.repf; state.repprimalerror = state.dss.repprimalerror; state.repdualerror = state.dss.repdualerror; state.repiterationscount = state.dss.repiterationscount; state.repterminationtype = state.dss.repterminationtype; return; } // // Integrity check failed - unknown solver // alglib.ap.assert(false, "MinQPOptimize: integrity check failed - unknown solver"); } /************************************************************************* LP solver results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[N], solution. Filled by zeros on failure. Rep - optimization report. You should check Rep.TerminationType, which contains completion code, and you may check another fields which contain another information about algorithm functioning. Failure codes returned by algorithm are: * -4 LP problem is primal unbounded (dual infeasible) * -3 LP problem is primal infeasible (dual unbounded) Success codes: * 1..4 successful completion * 5 MaxIts steps was taken -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlpresults(minlpstate state, ref double[] x, minlpreport rep, alglib.xparams _params) { x = new double[0]; minlpresultsbuf(state, ref x, rep, _params); } /************************************************************************* LP results Buffered implementation of MinLPResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlpresultsbuf(minlpstate state, ref double[] x, minlpreport rep, alglib.xparams _params) { int i = 0; if( alglib.ap.len(x)0 ) { for(i=0; i<=state.n-1; i++) { x[i] = state.xs[i]; } for(i=0; i<=state.m-1; i++) { rep.y[i] = state.ys[i]; } for(i=0; i<=state.n+state.m-1; i++) { rep.stats[i] = state.cs[i]; } } else { for(i=0; i<=state.n-1; i++) { x[i] = 0; } for(i=0; i<=state.m-1; i++) { rep.y[i] = 0; } for(i=0; i<=state.n+state.m-1; i++) { rep.stats[i] = 0; } } } /************************************************************************* Clear report fields prior to the optimization. -- ALGLIB -- Copyright 19.07.2018 by Bochkanov Sergey. *************************************************************************/ private static void clearreportfields(minlpstate state, alglib.xparams _params) { state.repf = 0.0; state.repprimalerror = 0.0; state.repdualerror = 0.0; state.repiterationscount = 0; state.repterminationtype = 0; } } public class nlcslp { /************************************************************************* This object stores temporaries of SLP subsolver. *************************************************************************/ public class minslpsubsolver : apobject { public reviseddualsimplex.dualsimplexstate dss; public reviseddualsimplex.dualsimplexsettings dsssettings; public reviseddualsimplex.dualsimplexbasis lastbasis; public bool basispresent; public double[,] curd; public int curdcnt; public double[] curb; public double[] curbndl; public double[] curbndu; public double[] cural; public double[] curau; public sparse.sparsematrix sparserawlc; public sparse.sparsematrix sparseefflc; public int hessiantype; public double[,] h; public double[,] curhd; public double[,] densedummy; public sparse.sparsematrix sparsedummy; public double[] tmp0; public double[] tmp1; public double[] sk; public double[] yk; public minslpsubsolver() { init(); } public override void init() { dss = new reviseddualsimplex.dualsimplexstate(); dsssettings = new reviseddualsimplex.dualsimplexsettings(); lastbasis = new reviseddualsimplex.dualsimplexbasis(); curd = new double[0,0]; curb = new double[0]; curbndl = new double[0]; curbndu = new double[0]; cural = new double[0]; curau = new double[0]; sparserawlc = new sparse.sparsematrix(); sparseefflc = new sparse.sparsematrix(); h = new double[0,0]; curhd = new double[0,0]; densedummy = new double[0,0]; sparsedummy = new sparse.sparsematrix(); tmp0 = new double[0]; tmp1 = new double[0]; sk = new double[0]; yk = new double[0]; } public override alglib.apobject make_copy() { minslpsubsolver _result = new minslpsubsolver(); _result.dss = (reviseddualsimplex.dualsimplexstate)dss.make_copy(); _result.dsssettings = (reviseddualsimplex.dualsimplexsettings)dsssettings.make_copy(); _result.lastbasis = (reviseddualsimplex.dualsimplexbasis)lastbasis.make_copy(); _result.basispresent = basispresent; _result.curd = (double[,])curd.Clone(); _result.curdcnt = curdcnt; _result.curb = (double[])curb.Clone(); _result.curbndl = (double[])curbndl.Clone(); _result.curbndu = (double[])curbndu.Clone(); _result.cural = (double[])cural.Clone(); _result.curau = (double[])curau.Clone(); _result.sparserawlc = (sparse.sparsematrix)sparserawlc.make_copy(); _result.sparseefflc = (sparse.sparsematrix)sparseefflc.make_copy(); _result.hessiantype = hessiantype; _result.h = (double[,])h.Clone(); _result.curhd = (double[,])curhd.Clone(); _result.densedummy = (double[,])densedummy.Clone(); _result.sparsedummy = (sparse.sparsematrix)sparsedummy.make_copy(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmp1 = (double[])tmp1.Clone(); _result.sk = (double[])sk.Clone(); _result.yk = (double[])yk.Clone(); return _result; } }; /************************************************************************* This object stores temporaries of SLP solver. *************************************************************************/ public class minslpstate : apobject { public int n; public int nec; public int nic; public int nlec; public int nlic; public double[,] scaledcleic; public int[] lcsrcidx; public bool[] hasbndl; public bool[] hasbndu; public double[] scaledbndl; public double[] scaledbndu; public double epsx; public int maxits; public int hessiantype; public double[] x; public double[] fi; public double[,] j; public double f; public bool needfij; public bool xupdated; public double trustrad; public double deltamax; public minslpsubsolver subsolver; public double[] d; public double[] d0; public double[] d1; public linmin.linminstate mcstate; public int xstagnationcnt; public int fstagnationcnt; public double[] prevx; public double[] step0x; public double[] stepkx; public double[] stepkxc; public double[] stepkxn; public double[] step0fi; public double[] stepkfi; public double[] stepkfic; public double[] stepkfin; public double[,] step0j; public double[,] stepkj; public double[,] stepkjc; public double[,] stepkjn; public double stepklagval; public double stepkclagval; public double stepknlagval; public double[] stepklaggrad; public double[] stepknlaggrad; public double[] stepklagmult; public double[] stepknlagmult; public double[] rho; public double[] tmp0; public double[] sclagtmp0; public double[] sclagtmp1; public double lastlcerr; public int lastlcidx; public double lastnlcerr; public int lastnlcidx; public double[] mftmp0; public int repsimplexiterations; public int repsimplexiterations1; public int repsimplexiterations2; public int repsimplexiterations3; public int repinneriterationscount; public int repouteriterationscount; public int repterminationtype; public double repbcerr; public int repbcidx; public double replcerr; public int replcidx; public double repnlcerr; public int repnlcidx; public rcommstate rstate; public rcommstate rphase13state; public rcommstate rphase2state; public minslpstate() { init(); } public override void init() { scaledcleic = new double[0,0]; lcsrcidx = new int[0]; hasbndl = new bool[0]; hasbndu = new bool[0]; scaledbndl = new double[0]; scaledbndu = new double[0]; x = new double[0]; fi = new double[0]; j = new double[0,0]; subsolver = new minslpsubsolver(); d = new double[0]; d0 = new double[0]; d1 = new double[0]; mcstate = new linmin.linminstate(); prevx = new double[0]; step0x = new double[0]; stepkx = new double[0]; stepkxc = new double[0]; stepkxn = new double[0]; step0fi = new double[0]; stepkfi = new double[0]; stepkfic = new double[0]; stepkfin = new double[0]; step0j = new double[0,0]; stepkj = new double[0,0]; stepkjc = new double[0,0]; stepkjn = new double[0,0]; stepklaggrad = new double[0]; stepknlaggrad = new double[0]; stepklagmult = new double[0]; stepknlagmult = new double[0]; rho = new double[0]; tmp0 = new double[0]; sclagtmp0 = new double[0]; sclagtmp1 = new double[0]; mftmp0 = new double[0]; rstate = new rcommstate(); rphase13state = new rcommstate(); rphase2state = new rcommstate(); } public override alglib.apobject make_copy() { minslpstate _result = new minslpstate(); _result.n = n; _result.nec = nec; _result.nic = nic; _result.nlec = nlec; _result.nlic = nlic; _result.scaledcleic = (double[,])scaledcleic.Clone(); _result.lcsrcidx = (int[])lcsrcidx.Clone(); _result.hasbndl = (bool[])hasbndl.Clone(); _result.hasbndu = (bool[])hasbndu.Clone(); _result.scaledbndl = (double[])scaledbndl.Clone(); _result.scaledbndu = (double[])scaledbndu.Clone(); _result.epsx = epsx; _result.maxits = maxits; _result.hessiantype = hessiantype; _result.x = (double[])x.Clone(); _result.fi = (double[])fi.Clone(); _result.j = (double[,])j.Clone(); _result.f = f; _result.needfij = needfij; _result.xupdated = xupdated; _result.trustrad = trustrad; _result.deltamax = deltamax; _result.subsolver = (minslpsubsolver)subsolver.make_copy(); _result.d = (double[])d.Clone(); _result.d0 = (double[])d0.Clone(); _result.d1 = (double[])d1.Clone(); _result.mcstate = (linmin.linminstate)mcstate.make_copy(); _result.xstagnationcnt = xstagnationcnt; _result.fstagnationcnt = fstagnationcnt; _result.prevx = (double[])prevx.Clone(); _result.step0x = (double[])step0x.Clone(); _result.stepkx = (double[])stepkx.Clone(); _result.stepkxc = (double[])stepkxc.Clone(); _result.stepkxn = (double[])stepkxn.Clone(); _result.step0fi = (double[])step0fi.Clone(); _result.stepkfi = (double[])stepkfi.Clone(); _result.stepkfic = (double[])stepkfic.Clone(); _result.stepkfin = (double[])stepkfin.Clone(); _result.step0j = (double[,])step0j.Clone(); _result.stepkj = (double[,])stepkj.Clone(); _result.stepkjc = (double[,])stepkjc.Clone(); _result.stepkjn = (double[,])stepkjn.Clone(); _result.stepklagval = stepklagval; _result.stepkclagval = stepkclagval; _result.stepknlagval = stepknlagval; _result.stepklaggrad = (double[])stepklaggrad.Clone(); _result.stepknlaggrad = (double[])stepknlaggrad.Clone(); _result.stepklagmult = (double[])stepklagmult.Clone(); _result.stepknlagmult = (double[])stepknlagmult.Clone(); _result.rho = (double[])rho.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.sclagtmp0 = (double[])sclagtmp0.Clone(); _result.sclagtmp1 = (double[])sclagtmp1.Clone(); _result.lastlcerr = lastlcerr; _result.lastlcidx = lastlcidx; _result.lastnlcerr = lastnlcerr; _result.lastnlcidx = lastnlcidx; _result.mftmp0 = (double[])mftmp0.Clone(); _result.repsimplexiterations = repsimplexiterations; _result.repsimplexiterations1 = repsimplexiterations1; _result.repsimplexiterations2 = repsimplexiterations2; _result.repsimplexiterations3 = repsimplexiterations3; _result.repinneriterationscount = repinneriterationscount; _result.repouteriterationscount = repouteriterationscount; _result.repterminationtype = repterminationtype; _result.repbcerr = repbcerr; _result.repbcidx = repbcidx; _result.replcerr = replcerr; _result.replcidx = replcidx; _result.repnlcerr = repnlcerr; _result.repnlcidx = repnlcidx; _result.rstate = (rcommstate)rstate.make_copy(); _result.rphase13state = (rcommstate)rphase13state.make_copy(); _result.rphase2state = (rcommstate)rphase2state.make_copy(); return _result; } }; public const double slpstpclosetozero = 0.001; public const double slpdeltadecrease = 0.20; public const double slpdeltaincrease = 0.80; public const double slpstpclosetoone = 0.95; public const double slpgtol = 0.4; public const double bigc = 500.0; public const double bfgstol = 1.0E-5; public const double meritfunctiongain = 2.0; public const double inequalitydampingfactor = 10.0; public const double augmentationfactor = 10.0; public const double inittrustrad = 0.1; public const double stagnationepsf = 1.0E-12; public const double stagnationepsx = 1.0E-12; public const int xstagnationlimit = 3; public const int fstagnationlimit = 5; public const int nondescentlimit = 5; public static void minslpinitbuf(double[] bndl, double[] bndu, double[] s, double[] x0, int n, double[,] cleic, int[] lcsrcidx, int nec, int nic, int nlec, int nlic, double epsx, int maxits, minslpstate state, alglib.xparams _params) { int nslack = 0; int i = 0; int j = 0; double v = 0; double vv = 0; nslack = n+2*(nec+nlec)+(nic+nlic); state.n = n; state.nec = nec; state.nic = nic; state.nlec = nlec; state.nlic = nlic; // // Settings // state.hessiantype = 2; // // Prepare RCOMM state // state.rstate.ia = new int[9+1]; state.rstate.ba = new bool[0+1]; state.rstate.ra = new double[7+1]; state.rstate.stage = -1; state.needfij = false; state.xupdated = false; state.x = new double[n]; state.fi = new double[1+nlec+nlic]; state.j = new double[1+nlec+nlic, n]; // // Allocate memory. // apserv.rvectorsetlengthatleast(ref state.prevx, n, _params); apserv.rvectorsetlengthatleast(ref state.step0x, n, _params); apserv.rvectorsetlengthatleast(ref state.stepkx, n, _params); apserv.rvectorsetlengthatleast(ref state.stepkxc, n, _params); apserv.rvectorsetlengthatleast(ref state.stepkxn, n, _params); apserv.rvectorsetlengthatleast(ref state.step0fi, 1+nlec+nlic, _params); apserv.rvectorsetlengthatleast(ref state.stepkfi, 1+nlec+nlic, _params); apserv.rvectorsetlengthatleast(ref state.stepkfic, 1+nlec+nlic, _params); apserv.rvectorsetlengthatleast(ref state.stepkfin, 1+nlec+nlic, _params); apserv.rmatrixsetlengthatleast(ref state.step0j, 1+nlec+nlic, n, _params); apserv.rmatrixsetlengthatleast(ref state.stepkj, 1+nlec+nlic, n, _params); apserv.rmatrixsetlengthatleast(ref state.stepkjc, 1+nlec+nlic, n, _params); apserv.rmatrixsetlengthatleast(ref state.stepkjn, 1+nlec+nlic, n, _params); apserv.rvectorsetlengthatleast(ref state.stepklaggrad, n, _params); apserv.rvectorsetlengthatleast(ref state.stepknlaggrad, n, _params); apserv.rvectorsetlengthatleast(ref state.rho, nec+nic+nlec+nlic, _params); apserv.rvectorsetlengthatleast(ref state.stepklagmult, nec+nic+nlec+nlic, _params); apserv.rvectorsetlengthatleast(ref state.stepknlagmult, nec+nic+nlec+nlic, _params); apserv.bvectorsetlengthatleast(ref state.hasbndl, n, _params); apserv.bvectorsetlengthatleast(ref state.hasbndu, n, _params); apserv.rvectorsetlengthatleast(ref state.scaledbndl, n, _params); apserv.rvectorsetlengthatleast(ref state.scaledbndu, n, _params); apserv.rmatrixsetlengthatleast(ref state.scaledcleic, nec+nic, n+1, _params); apserv.rvectorsetlengthatleast(ref state.d, nslack, _params); apserv.rvectorsetlengthatleast(ref state.d0, nslack, _params); apserv.rvectorsetlengthatleast(ref state.d1, nslack, _params); apserv.rvectorsetlengthatleast(ref state.tmp0, nslack, _params); apserv.ivectorsetlengthatleast(ref state.lcsrcidx, nec+nic, _params); // // Prepare scaled problem // for(i=0; i<=n-1; i++) { state.hasbndl[i] = math.isfinite(bndl[i]); state.hasbndu[i] = math.isfinite(bndu[i]); if( state.hasbndl[i] ) { state.scaledbndl[i] = bndl[i]/s[i]; } if( state.hasbndu[i] ) { state.scaledbndu[i] = bndu[i]/s[i]; } if( state.hasbndl[i] && state.hasbndu[i] ) { alglib.ap.assert((double)(bndl[i])<=(double)(bndu[i]), "SLP: integrity check failed, box constraints are inconsistent"); } state.step0x[i] = x0[i]/s[i]; } for(i=0; i<=nec+nic-1; i++) { // // Permutation // state.lcsrcidx[i] = lcsrcidx[i]; // // Scale and normalize linear constraints // vv = 0.0; for(j=0; j<=n-1; j++) { v = cleic[i,j]*s[j]; state.scaledcleic[i,j] = v; vv = vv+v*v; } vv = Math.Sqrt(vv); state.scaledcleic[i,n] = cleic[i,n]; if( (double)(vv)>(double)(0) ) { for(j=0; j<=n; j++) { state.scaledcleic[i,j] = state.scaledcleic[i,j]/vv; } } } // // Initial enforcement of box constraints // for(i=0; i<=n-1; i++) { if( state.hasbndl[i] ) { state.step0x[i] = Math.Max(state.step0x[i], state.scaledbndl[i]); } if( state.hasbndu[i] ) { state.step0x[i] = Math.Min(state.step0x[i], state.scaledbndu[i]); } } // // Stopping criteria // state.epsx = epsx; state.maxits = maxits; // // Report fields // state.repsimplexiterations = 0; state.repsimplexiterations1 = 0; state.repsimplexiterations2 = 0; state.repsimplexiterations3 = 0; state.repterminationtype = 0; state.repbcerr = 0; state.repbcidx = -1; state.replcerr = 0; state.replcidx = -1; state.repnlcerr = 0; state.repnlcidx = -1; state.repinneriterationscount = 0; state.repouteriterationscount = 0; // // Integrity checks: // * it is important that significant step length is large enough that // we do not decrease trust regiod radius; it should also be small, // so we won't treat large steps as insignificant // alglib.ap.assert((double)(slpstpclosetozero)<(double)(slpdeltadecrease), "MinSLP: integrity check failed"); alglib.ap.assert((double)(slpdeltadecrease)<(double)(slpdeltaincrease), "MinSLP: integrity check failed"); alglib.ap.assert((double)(slpdeltaincrease)<(double)(slpstpclosetoone), "MinSLP: integrity check failed"); } /************************************************************************* This function performs actual processing for SLP algorithm. It expects that caller redirects its reverse communication requests NeedFiJ/XUpdated to external user who will provide analytic derivative (or handle reports about progress). In case external user does not have analytic derivative, it is responsibility of caller to intercept NeedFiJ request and replace it with appropriate numerical differentiation scheme. Results are stored: * point - in State.StepKX IMPORTANT: this function works with scaled problem formulation; it is responsibility of the caller to unscale request and scale Jacobian. NOTE: SMonitor is expected to be correctly initialized smoothness monitor. -- ALGLIB -- Copyright 05.03.2018 by Bochkanov Sergey *************************************************************************/ public static bool minslpiteration(minslpstate state, optserv.smoothnessmonitor smonitor, bool userterminationneeded, alglib.xparams _params) { bool result = new bool(); int n = 0; int nslack = 0; int nec = 0; int nic = 0; int nlec = 0; int nlic = 0; int i = 0; int j = 0; int innerk = 0; double v = 0; double vv = 0; double mx = 0; bool lpstagesuccess = new bool(); double gammamax = 0; double fscale = 0; double f0 = 0; double f1 = 0; int status = 0; double stp = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { n = state.rstate.ia[0]; nslack = state.rstate.ia[1]; nec = state.rstate.ia[2]; nic = state.rstate.ia[3]; nlec = state.rstate.ia[4]; nlic = state.rstate.ia[5]; i = state.rstate.ia[6]; j = state.rstate.ia[7]; innerk = state.rstate.ia[8]; status = state.rstate.ia[9]; lpstagesuccess = state.rstate.ba[0]; v = state.rstate.ra[0]; vv = state.rstate.ra[1]; mx = state.rstate.ra[2]; gammamax = state.rstate.ra[3]; fscale = state.rstate.ra[4]; f0 = state.rstate.ra[5]; f1 = state.rstate.ra[6]; stp = state.rstate.ra[7]; } else { n = 359; nslack = -58; nec = -919; nic = -909; nlec = 81; nlic = 255; i = 74; j = -788; innerk = 809; status = 205; lpstagesuccess = false; v = 939; vv = -526; mx = 763; gammamax = -541; fscale = -698; f0 = -900; f1 = -318; stp = -940; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } if( state.rstate.stage==4 ) { goto lbl_4; } // // Routine body // n = state.n; nec = state.nec; nic = state.nic; nlec = state.nlec; nlic = state.nlic; nslack = n+2*(nec+nlec)+(nic+nlic); // // Prepare rcomm interface // state.needfij = false; state.xupdated = false; // // Initialize algorithm data: // * Lagrangian and "Big C" estimates // * trust region // * vector of penalty coefficients Rho[], initialized by zeros // * current approximation of the Hessian matrix H // * initial linearized constraints // * initial violation of linear/nonlinear constraints // state.xstagnationcnt = 0; state.fstagnationcnt = 0; state.trustrad = inittrustrad; for(i=0; i<=nec+nic+nlec+nlic-1; i++) { state.rho[i] = 0.0; } gammamax = 0.0; // // Avoid spurious warnings about possibly uninitialized vars // status = 0; stp = 0; // // Evaluate function vector and Jacobian at Step0X, send first location report. // Compute initial violation of constraints. // slpsendx(state, state.step0x, _params); state.needfij = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needfij = false; if( !slpretrievefij(state, state.step0fi, state.step0j, _params) ) { // // Failed to retrieve function/Jaconian, infinities detected! // for(i=0; i<=n-1; i++) { state.stepkx[i] = state.step0x[i]; } state.repterminationtype = -8; result = false; return result; } slpcopystate(state, state.step0x, state.step0fi, state.step0j, state.stepkx, state.stepkfi, state.stepkj, _params); slpsendx(state, state.stepkx, _params); state.f = state.stepkfi[0]; state.xupdated = true; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: state.xupdated = false; optserv.checklcviolation(state.scaledcleic, state.lcsrcidx, nec, nic, state.stepkx, n, ref state.replcerr, ref state.replcidx, _params); optserv.checknlcviolation(state.stepkfi, nlec, nlic, ref state.repnlcerr, ref state.repnlcidx, _params); // // Perform outer (NLC) iterations // tracebeginning(_params); initlpsubsolver(state, state.subsolver, state.hessiantype, _params); lbl_5: if( false ) { goto lbl_6; } // // Update vector Rho[] of quadratic penalty coefficients: // * for Rho[i] corresponding to linear constraints we use gradient // norm at current point times augmentation factor (in 100-1000 range). // Everything is simple because linear constraints are normalized. // * for Rho[i] corresponding to nonlinear constraints we use // gradient norm times augmentation factor divided by norm of // constraint gradient (or 1.0, if constraint gradient is less // than 1 in magnitude). // // Having large enough penalty coefficient is essential for the // convergence of the algorithm from the infeasible initial points: // it may fail to converge for very small Rho's because of inability // to perform sufficiently large steps towards feasible area and against // target function growth. // // Rho[] may never decrease, only increase. Values set at this point // will retain through the entire NLP iteration, but will be updated // before next NLP iteration will start. // v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.stepkj[0,i_]*state.stepkj[0,i_]; } fscale = Math.Max(Math.Sqrt(v), gammamax); for(i=0; i<=nec+nic-1; i++) { state.rho[i] = Math.Max(state.rho[i], augmentationfactor*fscale); } if( (double)(fscale)>(double)(0) ) { for(i=0; i<=nlec+nlic-1; i++) { vv = 0.0; for(i_=0; i_<=n-1;i_++) { vv += state.stepkj[1+i,i_]*state.stepkj[1+i,i_]; } vv = Math.Max(Math.Sqrt(vv), fscale); state.rho[nec+nic+i] = Math.Max(state.rho[nec+nic+i], augmentationfactor*(fscale/vv)); } } // // Save initial point for the outer iteration // slpcopystate(state, state.stepkx, state.stepkfi, state.stepkj, state.step0x, state.step0fi, state.step0j, _params); // // Try to perform step using linear model with second order correction // // A second order correction helps to overcome Maratos effect - a tendency // of L1 penalized merit function to reject nonzero steps along steepest // descent direction. // // The idea (explained in more details in the Phase13Iteration() body) // is to perform one look-ahead step and use updated constraint values // back at the initial point. // state.rphase13state.ia = new int[8+1]; state.rphase13state.ra = new double[4+1]; state.rphase13state.stage = -1; lbl_7: if( !phase13iteration(state, smonitor, true, userterminationneeded, ref status, ref stp, _params) ) { goto lbl_8; } state.rstate.stage = 2; goto lbl_rcomm; lbl_2: goto lbl_7; lbl_8: if( status<0 ) { goto lbl_5; } if( status==0 ) { goto lbl_6; } // // If step with second order correction is shorter than 1.0, it means // that target is sufficiently nonlinear to use advanced iterations. // * perform inner LP subiterations with additional conjugacy constraints // * perform polishing step // if( (double)(stp)>=(double)(slpstpclosetoone) ) { goto lbl_9; } // // LP subiterations // state.rphase2state.ia = new int[11+1]; state.rphase2state.ra = new double[3+1]; state.rphase2state.stage = -1; lbl_11: if( !phase2iteration(state, smonitor, userterminationneeded, ref gammamax, ref status, _params) ) { goto lbl_12; } state.rstate.stage = 3; goto lbl_rcomm; lbl_3: goto lbl_11; lbl_12: if( status<0 ) { goto lbl_5; } if( status==0 ) { goto lbl_6; } // // Polishing step // // This step is essential because previous step (which minimizes // Lagrangian) may fail to produce descent direction for L1-penalized merit function. // f0 = meritfunction(state, state.step0x, state.trustrad, state.step0fi, state.step0j, state.stepklagmult, _params); f1 = meritfunction(state, state.stepkx, state.trustrad, state.stepkfi, state.stepkj, state.stepklagmult, _params); if( (double)(f1)<(double)(f0) ) { goto lbl_13; } // // Merit function does not decrease, perform "polishing" of results with // merit function minimization // // We use same Phase13Iteration() function as in the beginning of the outer // iteration, but this time without second order correction. Having // two slightly different approaches helps us to perform good step // due to diversity of the methods being utilized. // state.rphase13state.ia = new int[8+1]; state.rphase13state.ra = new double[4+1]; state.rphase13state.stage = -1; lbl_15: if( !phase13iteration(state, smonitor, false, userterminationneeded, ref status, ref stp, _params) ) { goto lbl_16; } state.rstate.stage = 4; goto lbl_rcomm; lbl_4: goto lbl_15; lbl_16: if( status<0 ) { goto lbl_5; } if( status==0 ) { goto lbl_6; } lbl_13: lbl_9: // // Multistep is done // tracemultistep(state, _params); // // Update trust region // state.deltamax = 0; for(i=0; i<=n-1; i++) { state.deltamax = Math.Max(state.deltamax, Math.Abs(state.step0x[i]-state.stepkx[i])/state.trustrad); } if( (double)(state.deltamax)<=(double)(slpdeltadecrease) ) { state.trustrad = state.trustrad*Math.Max(state.deltamax/slpdeltadecrease, 0.1); } if( (double)(state.deltamax)>=(double)(slpdeltaincrease) ) { state.trustrad = state.trustrad*2.0; } // // Advance outer iteration counter, test stopping criteria // apserv.inc(ref state.repouteriterationscount, _params); v = 0; vv = 0; for(i=0; i<=n-1; i++) { state.prevx[i] = state.step0x[i]; v = v+math.sqr(state.step0x[i]-state.stepkx[i]); vv = vv+math.sqr(state.stepkx[i]); } v = Math.Sqrt(v); vv = Math.Sqrt(vv); if( (double)(v)<=(double)(state.epsx) || (double)(v)<=(double)(stagnationepsx*vv) ) { apserv.inc(ref state.xstagnationcnt, _params); } else { state.xstagnationcnt = 0; } if( (double)(Math.Abs(state.stepkfi[0]-state.step0fi[0]))<=(double)(stagnationepsf*Math.Max(Math.Abs(state.step0fi[0]), 1)) ) { apserv.inc(ref state.fstagnationcnt, _params); } else { state.fstagnationcnt = 0; } tracenlpstep(state, _params); if( state.xstagnationcnt>=xstagnationlimit ) { state.repterminationtype = 2; goto lbl_6; } if( state.maxits>0 && state.repinneriterationscount>=state.maxits ) { state.repterminationtype = 5; goto lbl_6; } if( state.fstagnationcnt>=fstagnationlimit ) { state.repterminationtype = 7; goto lbl_6; } goto lbl_5; lbl_6: tracefinalpoint(state, _params); result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = n; state.rstate.ia[1] = nslack; state.rstate.ia[2] = nec; state.rstate.ia[3] = nic; state.rstate.ia[4] = nlec; state.rstate.ia[5] = nlic; state.rstate.ia[6] = i; state.rstate.ia[7] = j; state.rstate.ia[8] = innerk; state.rstate.ia[9] = status; state.rstate.ba[0] = lpstagesuccess; state.rstate.ra[0] = v; state.rstate.ra[1] = vv; state.rstate.ra[2] = mx; state.rstate.ra[3] = gammamax; state.rstate.ra[4] = fscale; state.rstate.ra[5] = f0; state.rstate.ra[6] = f1; state.rstate.ra[7] = stp; return result; } /************************************************************************* This function initializes SLP subproblem. Should be called once in the beginning of the optimization. INPUT PARAMETERS: SState - solver state Subsolver - SLP subproblem to initialize HessianType - 0 for identity Hessian, 1 for BFGS update RETURN VALUE: True on success False on failure of the LP solver (unexpected... but possible due to numerical errors) -- ALGLIB -- Copyright 05.03.2018 by Bochkanov Sergey *************************************************************************/ private static void initlpsubsolver(minslpstate sstate, minslpsubsolver subsolver, int hessiantype, alglib.xparams _params) { int n = 0; int nslack = 0; int nec = 0; int nic = 0; int nlec = 0; int nlic = 0; int lccnt = 0; int nnz = 0; int offs = 0; int i = 0; int j = 0; n = sstate.n; nec = sstate.nec; nic = sstate.nic; nlec = sstate.nlec; nlic = sstate.nlic; nslack = n+2*(nec+nlec)+(nic+nlic); lccnt = nec+nic+nlec+nlic; // // Create simplex solver. // // NOTE: we disable DSE pricing because it interferes with our // warm-start strategy. // reviseddualsimplex.dsssettingsinit(subsolver.dsssettings, _params); subsolver.dsssettings.pricing = 0; reviseddualsimplex.dssinit(nslack, subsolver.dss, _params); // // Allocate temporaries // apserv.rvectorsetlengthatleast(ref subsolver.cural, lccnt+n, _params); apserv.rvectorsetlengthatleast(ref subsolver.curau, lccnt+n, _params); apserv.rmatrixsetlengthatleast(ref subsolver.curd, n, n, _params); apserv.rmatrixsetlengthatleast(ref subsolver.curhd, n, n, _params); apserv.rvectorsetlengthatleast(ref subsolver.curbndl, nslack, _params); apserv.rvectorsetlengthatleast(ref subsolver.curbndu, nslack, _params); apserv.rvectorsetlengthatleast(ref subsolver.curb, nslack, _params); apserv.rvectorsetlengthatleast(ref subsolver.sk, n, _params); apserv.rvectorsetlengthatleast(ref subsolver.yk, n, _params); // // Initial state // subsolver.basispresent = false; subsolver.curdcnt = 0; subsolver.hessiantype = hessiantype; if( hessiantype==1 || hessiantype==2 ) { // // Prepare Hessian matrix // apserv.rmatrixsetlengthatleast(ref subsolver.h, n, n, _params); for(i=0; i<=n-1; i++) { for(j=0; j<=n-1; j++) { subsolver.h[i,j] = 0; } subsolver.h[i,i] = 1; } } // // Linear constraints do not change across subiterations, that's // why we allocate storage for them at the start of the program. // // A full set of "raw" constraints is stored; later we will filter // out inequality ones which are inactive anywhere in the current // trust region. // // NOTE: because sparserawlc object stores only linear constraint // (linearizations of nonlinear ones are not stored) we // allocate only minimum necessary space. // nnz = 0; for(i=0; i<=nec+nic-1; i++) { for(j=0; j<=n-1; j++) { if( sstate.scaledcleic[i,j]!=0.0 ) { nnz = nnz+1; } } } apserv.ivectorsetlengthatleast(ref subsolver.sparserawlc.ridx, nec+nic+1, _params); apserv.rvectorsetlengthatleast(ref subsolver.sparserawlc.vals, nnz, _params); apserv.ivectorsetlengthatleast(ref subsolver.sparserawlc.idx, nnz, _params); apserv.ivectorsetlengthatleast(ref subsolver.sparserawlc.didx, nec+nic, _params); apserv.ivectorsetlengthatleast(ref subsolver.sparserawlc.uidx, nec+nic, _params); offs = 0; subsolver.sparserawlc.ridx[0] = 0; for(i=0; i<=nec+nic-1; i++) { for(j=0; j<=n-1; j++) { if( sstate.scaledcleic[i,j]!=0.0 ) { // // Primary part of the matrix // subsolver.sparserawlc.vals[offs] = sstate.scaledcleic[i,j]; subsolver.sparserawlc.idx[offs] = j; offs = offs+1; } } subsolver.sparserawlc.ridx[i+1] = offs; } subsolver.sparserawlc.matrixtype = 1; subsolver.sparserawlc.ninitialized = subsolver.sparserawlc.ridx[nec+nic]; subsolver.sparserawlc.m = nec+nic; subsolver.sparserawlc.n = n; sparse.sparseinitduidx(subsolver.sparserawlc, _params); } /************************************************************************* Restarts LP subproblem (cleans the matrix of internally stored directions) INPUT PARAMETERS: SState - solver state Subsolver - SLP subproblem to initialize -- ALGLIB -- Copyright 05.03.2018 by Bochkanov Sergey *************************************************************************/ private static void lpsubproblemrestart(minslpstate sstate, minslpsubsolver subsolver, alglib.xparams _params) { subsolver.curdcnt = 0; } /************************************************************************* Updates Hessian estimate INPUT PARAMETERS: SState - solver state Subsolver - SLP subproblem to initialize -- ALGLIB -- Copyright 05.03.2018 by Bochkanov Sergey *************************************************************************/ private static void lpsubproblemupdatehessian(minslpstate sstate, minslpsubsolver subsolver, double[] x0, double[] g0, double[] x1, double[] g1, alglib.xparams _params) { int i = 0; int n = 0; double vv = 0; double v = 0; double v0 = 0; double v1 = 0; double v2 = 0; double gk = 0; double sk = 0; double yk = 0; n = sstate.n; if( subsolver.hessiantype==1 || subsolver.hessiantype==2 ) { apserv.rvectorsetlengthatleast(ref subsolver.tmp0, n, _params); v = 0; v0 = 0; v1 = 0; v2 = 0; for(i=0; i<=n-1; i++) { sk = x1[i]-x0[i]; yk = g1[i]-g0[i]; gk = g0[i]; v = v+sk*yk; v0 = v0+sk*sk; v1 = v1+yk*yk; v2 = v2+gk*gk; subsolver.sk[i] = sk; subsolver.yk[i] = yk; } if( ((double)(Math.Sqrt(v0))>(double)(Math.Max(sstate.epsx, bfgstol)) && (double)(Math.Sqrt(v1))>(double)(bfgstol*Math.Sqrt(v2))) && (double)(v)>(double)(bfgstol*Math.Sqrt(v0)*Math.Sqrt(v1)) ) { // // Update Hessian if following criteria hold: // * MCINFO=1 (good step) // * step length is large enough // * |Yk| is large enough when compared with |G| // * (Sk,Yk) is large enough when compared with |S| and |G| // vv = ablas.rmatrixsyvmv(n, subsolver.h, 0, 0, true, subsolver.sk, 0, subsolver.tmp0, _params); ablas.rmatrixgemv(n, n, 1.0, subsolver.h, 0, 0, 0, subsolver.sk, 0, 0.0, subsolver.tmp0, 0, _params); ablas.rmatrixger(n, n, subsolver.h, 0, 0, 1/v, subsolver.yk, 0, subsolver.yk, 0, _params); ablas.rmatrixger(n, n, subsolver.h, 0, 0, -(1/vv), subsolver.tmp0, 0, subsolver.tmp0, 0, _params); } } } /************************************************************************* This function solves LP subproblem given by initial point X, function vector Fi and Jacobian Jac, and returns estimates of Lagrangian multipliers and search direction D[]. -- ALGLIB -- Copyright 05.03.2018 by Bochkanov Sergey *************************************************************************/ private static bool lpsubproblemsolve(minslpstate state, minslpsubsolver subsolver, double[] x, double[] fi, double[,] jac, int innerk, double[] d, double[] lagmult, alglib.xparams _params) { bool result = new bool(); int n = 0; int nslack = 0; int nec = 0; int nic = 0; int nlec = 0; int nlic = 0; int i = 0; int j = 0; int k = 0; double v = 0; double vv = 0; double vright = 0; double vmax = 0; int basisinittype = 0; int lccnt = 0; int offsslackec = 0; int offsslacknlec = 0; int offsslackic = 0; int offsslacknlic = 0; int offs = 0; int nnz = 0; int j0 = 0; int j1 = 0; n = state.n; nec = state.nec; nic = state.nic; nlec = state.nlec; nlic = state.nlic; nslack = n+2*(nec+nlec)+(nic+nlic); lccnt = nec+nic+nlec+nlic; // // Locations of slack variables // offsslackec = n; offsslacknlec = n+2*nec; offsslackic = n+2*nec+2*nlec; offsslacknlic = n+2*(nec+nlec)+nic; // // Prepare temporary structures // apserv.rvectorgrowto(ref subsolver.cural, lccnt+subsolver.curdcnt, _params); apserv.rvectorgrowto(ref subsolver.curau, lccnt+subsolver.curdcnt, _params); // // Prepare default solution: all zeros // result = true; for(i=0; i<=nslack-1; i++) { d[i] = 0.0; } for(i=0; i<=lccnt-1; i++) { lagmult[i] = 0; } // // Linear term B // // NOTE: elements [N,NSlack) are equal to bigC + perturbation to improve numeric properties of LP problem // for(i=0; i<=n-1; i++) { subsolver.curb[i] = jac[0,i]; } v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(jac[0,i]); } v = apserv.coalesce(Math.Sqrt(v), 1.0, _params); for(i=n; i<=nslack-1; i++) { subsolver.curb[i] = (bigc+1.0/(1+i))*v; } // // Trust radius constraints for primary variables // for(i=0; i<=n-1; i++) { subsolver.curbndl[i] = -state.trustrad; subsolver.curbndu[i] = state.trustrad; if( state.hasbndl[i] ) { subsolver.curbndl[i] = Math.Max(subsolver.curbndl[i], state.scaledbndl[i]-x[i]); } if( state.hasbndu[i] ) { subsolver.curbndu[i] = Math.Min(subsolver.curbndu[i], state.scaledbndu[i]-x[i]); } } // // Prepare storage for "effective" constraining matrix // nnz = subsolver.sparserawlc.ridx[nec+nic]; for(i=0; i<=nlec+nlic-1; i++) { for(j=0; j<=n-1; j++) { if( jac[1+i,j]!=0.0 ) { nnz = nnz+1; } } } nnz = nnz+2*nec+nic; nnz = nnz+2*nlec+nlic; nnz = nnz+subsolver.curdcnt*n; apserv.ivectorgrowto(ref subsolver.sparseefflc.ridx, lccnt+n+1, _params); apserv.rvectorgrowto(ref subsolver.sparseefflc.vals, nnz, _params); apserv.ivectorgrowto(ref subsolver.sparseefflc.idx, nnz, _params); apserv.ivectorsetlengthatleast(ref subsolver.sparseefflc.didx, lccnt+n, _params); apserv.ivectorsetlengthatleast(ref subsolver.sparseefflc.uidx, lccnt+n, _params); subsolver.sparseefflc.m = 0; subsolver.sparseefflc.n = nslack; subsolver.sparseefflc.matrixtype = 1; // // Append linear equality/inequality constraints // // Scan sparsified linear constraints stored in sparserawlc[], skip ones // which are inactive anywhere in the trust region. // apserv.rvectorsetlengthatleast(ref subsolver.tmp0, nslack, _params); for(i=0; i<=n-1; i++) { subsolver.tmp0[i] = x[i]; } for(i=n; i<=nslack-1; i++) { subsolver.tmp0[i] = 0; } for(i=0; i<=nec+nic-1; i++) { // // Calculate: // * VRight - product of X[] (extended with zeros up to NSlack elements) // and AR[i] - Ith row of sparserawlc matrix. // * VMax - maximum value of X*ARi computed over trust region // vright = 0; vmax = 0; j0 = subsolver.sparserawlc.ridx[i]; j1 = subsolver.sparserawlc.ridx[i+1]-1; for(k=j0; k<=j1; k++) { j = subsolver.sparserawlc.idx[k]; v = subsolver.tmp0[j]; vv = subsolver.sparserawlc.vals[k]; vright = vright+vv*v; if( vv>=0 ) { vmax = vmax+vv*(v+subsolver.curbndu[j]); } else { vmax = vmax+vv*(v+subsolver.curbndl[j]); } } // // If constraint is an inequality one and guaranteed to be inactive // within trust region, it is skipped (row itself is retained but // filled by zeros). // if( i>=nec && vmax<=state.scaledcleic[i,n] ) { offs = subsolver.sparseefflc.ridx[i]; subsolver.sparseefflc.vals[offs] = -1; subsolver.sparseefflc.idx[offs] = offsslackic+(i-nec); subsolver.sparseefflc.ridx[i+1] = offs+1; subsolver.cural[i] = 0.0; subsolver.curau[i] = 0.0; subsolver.curbndl[offsslackic+(i-nec)] = 0; subsolver.curbndu[offsslackic+(i-nec)] = 0; continue; } // // Start working on row I // offs = subsolver.sparseefflc.ridx[i]; // // Copy constraint from sparserawlc[] to sparseefflc[] // j0 = subsolver.sparserawlc.ridx[i]; j1 = subsolver.sparserawlc.ridx[i+1]-1; for(k=j0; k<=j1; k++) { subsolver.sparseefflc.idx[offs] = subsolver.sparserawlc.idx[k]; subsolver.sparseefflc.vals[offs] = subsolver.sparserawlc.vals[k]; offs = offs+1; } // // Set up slack variables // if( i=0 ) { n = state.rphase13state.ia[0]; nslack = state.rphase13state.ia[1]; nec = state.rphase13state.ia[2]; nic = state.rphase13state.ia[3]; nlec = state.rphase13state.ia[4]; nlic = state.rphase13state.ia[5]; innerk = state.rphase13state.ia[6]; i = state.rphase13state.ia[7]; j = state.rphase13state.ia[8]; v = state.rphase13state.ra[0]; f0 = state.rphase13state.ra[1]; f1 = state.rphase13state.ra[2]; nu = state.rphase13state.ra[3]; localstp = state.rphase13state.ra[4]; } else { n = 1016; nslack = -229; nec = -536; nic = 487; nlec = -115; nlic = 886; innerk = 346; i = -722; j = -413; v = -461; f0 = 927; f1 = 201; nu = 922; localstp = -154; } if( state.rphase13state.stage==0 ) { goto lbl_0; } if( state.rphase13state.stage==1 ) { goto lbl_1; } if( state.rphase13state.stage==2 ) { goto lbl_2; } // // Routine body // n = state.n; nec = state.nec; nic = state.nic; nlec = state.nlec; nlic = state.nlic; nslack = n+2*(nec+nlec)+(nic+nlic); innerk = 1; // // Default decision is to continue algorithm // status = 1; stp = 0; // // Determine step direction using linearized model with no conjugacy terms // lpsubproblemrestart(state, state.subsolver, _params); if( !lpsubproblemsolve(state, state.subsolver, state.stepkx, state.stepkfi, state.stepkj, innerk, state.d0, state.stepklagmult, _params) ) { // // Can not find descent direction for Lagrangian // state.trustrad = 0.5*state.trustrad; if( (double)(state.trustrad)<(double)(state.epsx) ) { state.repterminationtype = 2; status = 0; } else { status = -1; } result = false; return result; } // // Compute second order correction if required. The issue we address here // is a tendency of L1 penalized function to reject steps built using simple // linearized model when nonlinear constraints change faster than the target. // // The idea is that we perform trial step (stp=1) using simple linearized model, // compute constraint vector at the new trial point - and use these updated // constraint linearizations back at the initial point. // // As result, we have: // * D0 - "simple" step without correction // * D1 - a correction to D0, such that D0+D1 points exactly to the solution // of the "corrected" linear model. // if( !usecorrection ) { goto lbl_3; } // // Perform trial step using vector D0 to StepKXC // for(i=0; i<=n-1; i++) { state.stepkxc[i] = state.stepkx[i]+state.d0[i]; } slpsendx(state, state.stepkxc, _params); state.needfij = true; state.rphase13state.stage = 0; goto lbl_rcomm; lbl_0: state.needfij = false; if( !slpretrievefij(state, state.stepkfic, state.stepkjc, _params) ) { // // Failed to retrieve func/Jac, infinities detected // state.repterminationtype = -8; status = 0; result = false; return result; } // // Move back to point StepKX, restore original linearization of the target // state.stepkfic[0] = state.stepkfi[0]; for(j=0; j<=n-1; j++) { state.stepkxc[j] = state.stepkx[j]; state.stepkjc[0,j] = state.stepkj[0,j]; } // // Extrapolate linearization of nonlinear constraints back to origin // for(i=1; i<=nlec+nlic; i++) { v = 0; for(j=0; j<=n-1; j++) { v = v+state.d0[j]*state.stepkjc[i,j]; } state.stepkfic[i] = state.stepkfic[i]-v; } // // Solve linearized problem one more time, now with new linearization of constraints // (but still old linearization of the target), obtain D1 // lpsubproblemrestart(state, state.subsolver, _params); if( !lpsubproblemsolve(state, state.subsolver, state.stepkxc, state.stepkfic, state.stepkjc, innerk, state.d1, state.stepknlagmult, _params) ) { // // Can not find descent direction for Lagrangian // state.trustrad = 0.5*state.trustrad; if( (double)(state.trustrad)<(double)(state.epsx) ) { state.repterminationtype = 2; status = 0; } else { status = -1; } result = false; return result; } for(i=0; i<=n-1; i++) { state.d1[i] = state.d1[i]-state.d0[i]; } goto lbl_4; lbl_3: // // No second order correction, D1=0 // for(i=0; i<=n-1; i++) { state.d1[i] = 0; } lbl_4: // // Perform merit function backtracking line search, with trial point being // computed as XN = XK + Stp*D0 + Stp^2*D1, with Stp in [0,1] // // NOTE: we use StepKLagMult - Lagrange multipliers computed for initial, // uncorrected task - for the merit function model. // Using StepKNLagMult can destabilize algorithm. // localstp = 1.0; nu = 0.5; f0 = meritfunction(state, state.stepkx, state.trustrad, state.stepkfi, state.stepkj, state.stepklagmult, _params); optserv.smoothnessmonitorstartlinesearch(smonitor, state.stepkx, state.stepkfi, state.stepkj, _params); lbl_5: if( false ) { goto lbl_6; } for(i=0; i<=n-1; i++) { state.stepkxn[i] = state.stepkx[i]+localstp*state.d0[i]+localstp*localstp*state.d1[i]; } slpsendx(state, state.stepkxn, _params); state.needfij = true; state.rphase13state.stage = 1; goto lbl_rcomm; lbl_1: state.needfij = false; if( !slpretrievefij(state, state.stepkfin, state.stepkjn, _params) ) { // // Failed to retrieve func/Jac, infinities detected // state.repterminationtype = -8; status = 0; result = false; return result; } optserv.smoothnessmonitorenqueuepoint(smonitor, state.d0, localstp, state.stepkxn, state.stepkfin, state.stepkjn, _params); f1 = meritfunction(state, state.stepkxn, state.trustrad, state.stepkfin, state.stepkjn, state.stepklagmult, _params); if( (double)(f1)<(double)(f0) ) { // // Step is found! // goto lbl_6; } if( (double)(localstp)<(double)(0.001) ) { // // Step is shorter than 0.001 times current search direction, // it means that no good step can be found. // localstp = 0; slpcopystate(state, state.stepkx, state.stepkfi, state.stepkj, state.stepkxn, state.stepkfin, state.stepkjn, _params); goto lbl_6; } localstp = nu*localstp; nu = Math.Max(0.1, 0.5*nu); goto lbl_5; lbl_6: optserv.smoothnessmonitorfinalizelinesearch(smonitor, _params); for(i=0; i<=n-1; i++) { if( state.hasbndl[i] ) { state.stepkxn[i] = Math.Max(state.stepkxn[i], state.scaledbndl[i]); } if( state.hasbndu[i] ) { state.stepkxn[i] = Math.Min(state.stepkxn[i], state.scaledbndu[i]); } } if( userterminationneeded ) { // // User requested termination, break before we move to new point // state.repterminationtype = 8; status = 0; result = false; return result; } // // Move to new point // stp = localstp; slpcopystate(state, state.stepkxn, state.stepkfin, state.stepkjn, state.stepkx, state.stepkfi, state.stepkj, _params); if( (double)(localstp)<=(double)(0) ) { goto lbl_7; } // // Report one more inner iteration // apserv.inc(ref state.repinneriterationscount, _params); slpsendx(state, state.stepkx, _params); state.f = state.stepkfi[0]; state.xupdated = true; state.rphase13state.stage = 2; goto lbl_rcomm; lbl_2: state.xupdated = false; // // Update constraint violations // optserv.checklcviolation(state.scaledcleic, state.lcsrcidx, nec, nic, state.stepkx, n, ref state.replcerr, ref state.replcidx, _params); optserv.checknlcviolation(state.stepkfi, nlec, nlic, ref state.repnlcerr, ref state.repnlcidx, _params); lbl_7: tracemeritsearch(state, _params); result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rphase13state.ia[0] = n; state.rphase13state.ia[1] = nslack; state.rphase13state.ia[2] = nec; state.rphase13state.ia[3] = nic; state.rphase13state.ia[4] = nlec; state.rphase13state.ia[5] = nlic; state.rphase13state.ia[6] = innerk; state.rphase13state.ia[7] = i; state.rphase13state.ia[8] = j; state.rphase13state.ra[0] = v; state.rphase13state.ra[1] = f0; state.rphase13state.ra[2] = f1; state.rphase13state.ra[3] = nu; state.rphase13state.ra[4] = localstp; return result; } /************************************************************************* This function tries to perform phase #2 iterations. Phase #2 is a sequence of linearized steps minimizing L2-penalized Lagrangian performed with successively increasing set of conjugacy constraints (which make algorithm behavior similar to that of CG). INPUT PARAMETERS: State - SLP solver state, StepKX/Fi/J is used as current point, StepK...N/C fields are modified. SMonitor - smoothness monitor UserTerminationNeeded-True if user requested termination GammaMax - current estimate of the Hessian norm OUTPUT PARAMETERS: State - StepKX/Fi/J advanced to new point, StepKLagMult contain current Lagrange multipliers, RepTerminationType is set to current termination code (if Status=0). GammaMax - updated estimate of the Hessian norm Status - when reverse communication is done, Status is set to: * negative value, if we have to restart outer iteration * positive value, if we can proceed to the next stage of the outer iteration * zero, if algorithm is terminated (RepTerminationType is set to appropriate value) -- ALGLIB -- Copyright 05.02.2019 by Bochkanov Sergey *************************************************************************/ private static bool phase2iteration(minslpstate state, optserv.smoothnessmonitor smonitor, bool userterminationneeded, ref double gammamax, ref int status, alglib.xparams _params) { bool result = new bool(); int n = 0; int nslack = 0; int nec = 0; int nic = 0; int nlec = 0; int nlic = 0; double stp = 0; int mcinfo = 0; int mcnfev = 0; int mcstage = 0; int i = 0; int innerk = 0; double v = 0; double vv = 0; double mx = 0; int nondescentcnt = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rphase2state.stage>=0 ) { n = state.rphase2state.ia[0]; nslack = state.rphase2state.ia[1]; nec = state.rphase2state.ia[2]; nic = state.rphase2state.ia[3]; nlec = state.rphase2state.ia[4]; nlic = state.rphase2state.ia[5]; mcinfo = state.rphase2state.ia[6]; mcnfev = state.rphase2state.ia[7]; mcstage = state.rphase2state.ia[8]; i = state.rphase2state.ia[9]; innerk = state.rphase2state.ia[10]; nondescentcnt = state.rphase2state.ia[11]; stp = state.rphase2state.ra[0]; v = state.rphase2state.ra[1]; vv = state.rphase2state.ra[2]; mx = state.rphase2state.ra[3]; } else { n = 306; nslack = -1011; nec = 951; nic = -463; nlec = 88; nlic = -861; mcinfo = -678; mcnfev = -731; mcstage = -675; i = -763; innerk = -233; nondescentcnt = -936; stp = -279; v = 94; vv = -812; mx = 427; } if( state.rphase2state.stage==0 ) { goto lbl_0; } if( state.rphase2state.stage==1 ) { goto lbl_1; } // // Routine body // n = state.n; nec = state.nec; nic = state.nic; nlec = state.nlec; nlic = state.nlic; nslack = n+2*(nec+nlec)+(nic+nlic); // // The default decision is to continue iterations // status = 1; // // Perform inner LP subiterations. // // During this process we maintain information about several points: // * point #0, initial one, with "step0" prefix // * point #K, last one of current LP session, with "stepk" prefix // * additionally we have point #KN, current candidate during line search at step K. // // For each point we store: // * location X (scaled coordinates) // * function vector Fi (target function + nonlinear constraints) // * scaled Jacobian J // nondescentcnt = 0; lpsubproblemrestart(state, state.subsolver, _params); innerk = 1; lbl_2: if( innerk>n ) { goto lbl_4; } // // Formulate LP subproblem and solve it // tracelpstart(_params); if( !lpsubproblemsolve(state, state.subsolver, state.stepkx, state.stepkfi, state.stepkj, innerk, state.d, state.stepklagmult, _params) ) { // // LP solver failed due to numerical errors; exit. // It may happen when we solve problem with LOTS of conjugacy constraints. // tracelpfailure(_params); status = -1; if( innerk==1 ) { // // The very first iteration failed, really strange. // Let's decrease trust radius and try one more time. // state.trustrad = 0.5*state.trustrad; if( (double)(state.trustrad)<(double)(state.epsx) ) { state.repterminationtype = 2; status = 0; } } result = false; return result; } v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.d[i]); } if( (double)(v)==(double)(0) ) { // // Zero step is suggested (maybe we arrived exactly to the solution), stop iterations // status = 1; slpcopystate(state, state.stepkx, state.stepkfi, state.stepkj, state.stepkxn, state.stepkfin, state.stepkjn, _params); tracelpstep(state, 0, 0, 0, _params); result = false; return result; } // // Perform line search to minimize Lagrangian along D. // Post-normalize StepKXN with respect to box constraints. // // MCSRCH can fail in the following cases: // * rounding errors prevent optimization // * non-descent direction is specified (MCINFO=0 is returned) // In the latter case we proceed to minimization of merit function. // // NOTE: constraint violation reports are updated during Lagrangian computation // state.lastlcerr = 0; state.lastlcidx = -1; state.lastnlcerr = 0; state.lastnlcidx = -1; apserv.rvectorsetlengthatleast(ref state.tmp0, n, _params); lagrangianfg(state, state.stepkx, state.trustrad, state.stepkfi, state.stepkj, state.stepklagmult, ref state.stepklagval, state.stepklaggrad, ref state.lastlcerr, ref state.lastlcidx, ref state.lastnlcerr, ref state.lastnlcidx, _params); slpcopystate(state, state.stepkx, state.stepkfi, state.stepkj, state.stepkxn, state.stepkfin, state.stepkjn, _params); v = 0; for(i=0; i<=n-1; i++) { state.stepknlaggrad[i] = state.stepklaggrad[i]; v = v+state.d[i]*state.stepklaggrad[i]; } if( (double)(v)>=(double)(0) ) { // // Non-descent direction D was specified; it may happen because LP subproblem favors // directions which decrease L1 penalty and default augmentation of Lagrangian involves // only L2 term. // // We make several retries with conjugate directions before giving up. // tracelpnondescent(_params); apserv.inc(ref nondescentcnt, _params); if( nondescentcnt>nondescentlimit ) { status = 1; result = false; return result; } goto lbl_3; } optserv.smoothnessmonitorstartlinesearch(smonitor, state.stepkx, state.stepkfi, state.stepkj, _params); state.stepknlagval = state.stepklagval; mcnfev = 0; mcstage = 0; stp = 1.0; linmin.mcsrch(n, ref state.stepkxn, ref state.stepknlagval, ref state.stepknlaggrad, state.d, ref stp, 1.0, slpgtol, ref mcinfo, ref mcnfev, ref state.tmp0, state.mcstate, ref mcstage, _params); lbl_5: if( mcstage==0 ) { goto lbl_6; } slpsendx(state, state.stepkxn, _params); state.needfij = true; state.rphase2state.stage = 0; goto lbl_rcomm; lbl_0: state.needfij = false; if( !slpretrievefij(state, state.stepkfin, state.stepkjn, _params) ) { // // Failed to retrieve func/Jac, infinities detected // status = 0; state.repterminationtype = -8; result = false; return result; } optserv.smoothnessmonitorenqueuepoint(smonitor, state.d, stp, state.stepkxn, state.stepkfin, state.stepkjn, _params); lagrangianfg(state, state.stepkxn, state.trustrad, state.stepkfin, state.stepkjn, state.stepklagmult, ref state.stepknlagval, state.stepknlaggrad, ref state.lastlcerr, ref state.lastlcidx, ref state.lastnlcerr, ref state.lastnlcidx, _params); linmin.mcsrch(n, ref state.stepkxn, ref state.stepknlagval, ref state.stepknlaggrad, state.d, ref stp, 1.0, slpgtol, ref mcinfo, ref mcnfev, ref state.tmp0, state.mcstate, ref mcstage, _params); goto lbl_5; lbl_6: optserv.smoothnessmonitorfinalizelinesearch(smonitor, _params); for(i=0; i<=n-1; i++) { if( state.hasbndl[i] ) { state.stepkxn[i] = Math.Max(state.stepkxn[i], state.scaledbndl[i]); } if( state.hasbndu[i] ) { state.stepkxn[i] = Math.Min(state.stepkxn[i], state.scaledbndu[i]); } } tracelpstep(state, stp, mcinfo, mcnfev, _params); if( mcinfo<=0 ) { // // Line search failed miserably, terminate // status = 1; if( innerk==1 ) { // // The very first iteration failed, really strange. // Let's decrease trust radius and try one more time. // state.trustrad = 0.5*state.trustrad; if( (double)(state.trustrad)<(double)(state.epsx) ) { state.repterminationtype = 2; status = 0; } } result = false; return result; } if( mcinfo==1 ) { lpsubproblemupdatehessian(state, state.subsolver, state.stepkx, state.stepklaggrad, state.stepkxn, state.stepknlaggrad, _params); } // // Update GammaMax - estimate of the function Hessian norm // v = 0; vv = 0; mx = 0; for(i=0; i<=n-1; i++) { mx = Math.Max(mx, Math.Abs(state.stepkxn[i]-state.stepkx[i])); v = v+math.sqr(state.stepkxn[i]-state.stepkx[i]); vv = vv+(state.stepkjn[0,i]-state.stepkj[0,i])*(state.stepkxn[i]-state.stepkx[i]); } if( (double)(mx)>(double)(bfgstol) ) { gammamax = Math.Max(gammamax, Math.Abs(vv/v)); } // // Check status of the termination request // Update current point // Update constraint status. // Report iteration. // if( userterminationneeded ) { // // User requested termination, break before we move to new point // status = 0; state.repterminationtype = 8; tracenlpstep(state, _params); tracefinalpoint(state, _params); result = false; return result; } slpcopystate(state, state.stepkxn, state.stepkfin, state.stepkjn, state.stepkx, state.stepkfi, state.stepkj, _params); state.replcerr = state.lastlcerr; state.replcidx = state.lastlcidx; state.repnlcerr = state.lastnlcerr; state.repnlcidx = state.lastnlcidx; apserv.inc(ref state.repinneriterationscount, _params); slpsendx(state, state.stepkx, _params); state.f = state.stepkfi[0]; state.xupdated = true; state.rphase2state.stage = 1; goto lbl_rcomm; lbl_1: state.xupdated = false; // // Terminate inner LP subiterations // if( state.maxits>0 && state.repinneriterationscount>=state.maxits ) { // // Iteration limit exhausted // status = 1; result = false; return result; } if( (double)(stp)<=(double)(slpstpclosetozero) ) { // // Step is too close to zero, terminate iterations. // status = 1; result = false; return result; } if( (double)(stp)>=(double)(slpstpclosetoone) ) { // // Step is close to 1.0, either of two is likely: // * we move through nearly linear region of F() // * we try to enforce some strongly violated constraint // // In any case, authors of the original algorithm recommend to break inner LP // iteration and proceed to test of sufficient decrease of merit function. // status = 1; result = false; return result; } if( (mcinfo!=1 && mcinfo!=3) && mcinfo!=5 ) { // // Line search ended with "bad" MCINFO // (neither sufficient decrease, neither maximum step); // terminate. // status = 1; result = false; return result; } for(i=n; i<=nslack-1; i++) { if( state.d[i]>0 ) { // // Some relaxation variable is non-zero; // break - it means that we are far away from the feasible area. // status = 1; result = false; return result; } } lbl_3: innerk = innerk+1; goto lbl_2; lbl_4: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rphase2state.ia[0] = n; state.rphase2state.ia[1] = nslack; state.rphase2state.ia[2] = nec; state.rphase2state.ia[3] = nic; state.rphase2state.ia[4] = nlec; state.rphase2state.ia[5] = nlic; state.rphase2state.ia[6] = mcinfo; state.rphase2state.ia[7] = mcnfev; state.rphase2state.ia[8] = mcstage; state.rphase2state.ia[9] = i; state.rphase2state.ia[10] = innerk; state.rphase2state.ia[11] = nondescentcnt; state.rphase2state.ra[0] = stp; state.rphase2state.ra[1] = v; state.rphase2state.ra[2] = vv; state.rphase2state.ra[3] = mx; return result; } /************************************************************************* Copies X to State.X *************************************************************************/ private static void slpsendx(minslpstate state, double[] xs, alglib.xparams _params) { int i = 0; int n = 0; n = state.n; for(i=0; i<=n-1; i++) { if( state.hasbndl[i] && xs[i]<=state.scaledbndl[i] ) { state.x[i] = state.scaledbndl[i]; continue; } if( state.hasbndu[i] && xs[i]>=state.scaledbndu[i] ) { state.x[i] = state.scaledbndu[i]; continue; } state.x[i] = xs[i]; } } /************************************************************************* Retrieves F-vector and scaled Jacobian, copies them to FiS and JS. Returns True on success, False on failure (when F or J are not finite numbers). *************************************************************************/ private static bool slpretrievefij(minslpstate state, double[] fis, double[,] js, alglib.xparams _params) { bool result = new bool(); int nlec = 0; int nlic = 0; int n = 0; int i = 0; int j = 0; double v = 0; n = state.n; nlec = state.nlec; nlic = state.nlic; v = 0; for(i=0; i<=nlec+nlic; i++) { fis[i] = state.fi[i]; v = 0.1*v+state.fi[i]; for(j=0; j<=n-1; j++) { js[i,j] = state.j[i,j]; v = 0.1*v+state.j[i,j]; } } result = math.isfinite(v); return result; } /************************************************************************* Copies state (X point, Fi vector, J jacobian) to preallocated storage. *************************************************************************/ private static void slpcopystate(minslpstate state, double[] x0, double[] fi0, double[,] j0, double[] x1, double[] fi1, double[,] j1, alglib.xparams _params) { int nlec = 0; int nlic = 0; int n = 0; int i = 0; int j = 0; n = state.n; nlec = state.nlec; nlic = state.nlic; for(i=0; i<=n-1; i++) { x1[i] = x0[i]; } for(i=0; i<=nlec+nlic; i++) { fi1[i] = fi0[i]; for(j=0; j<=n-1; j++) { j1[i,j] = j0[i,j]; } } } /************************************************************************* This function calculates Lagrangian of the problem (in scaled variables): its value and gradient. Additionally it also estimates violation of linear constraints at the point as well as index of the most violated constraint *************************************************************************/ private static void lagrangianfg(minslpstate state, double[] x, double trustrad, double[] fi, double[,] j, double[] lagmult, ref double f, double[] g, ref double lcerr, ref int lcidx, ref double nlcerr, ref int nlcidx, alglib.xparams _params) { int i = 0; int n = 0; int nec = 0; int nic = 0; int nlec = 0; int nlic = 0; double v = 0; double vlag = 0; double vact = 0; double vd = 0; double vviolate = 0; double rhoi = 0; bool usesparsegemv = new bool(); double dampingfactor = 0; f = 0; lcerr = 0; lcidx = 0; nlcerr = 0; nlcidx = 0; n = state.n; nec = state.nec; nic = state.nic; nlec = state.nlec; nlic = state.nlic; dampingfactor = inequalitydampingfactor/trustrad; // // Prepare constraint violation report // lcerr = 0; lcidx = -1; nlcerr = 0; nlcidx = -1; // // Target function // f = fi[0]; for(i=0; i<=n-1; i++) { g[i] = j[0,i]; } // // Lagrangian terms for linear constraints, constraint violations // if( nec+nic>0 ) { usesparsegemv = state.subsolver.sparserawlc.ridx[nec+nic]0 ) { // // Either equality constraint or violated inequality one. // Update violation report. // vviolate = Math.Abs(v); if( vviolate>lcerr ) { lcerr = vviolate; lcidx = state.lcsrcidx[i]; } } // // Prepare // vlag = lagmult[i]; state.sclagtmp1[i] = 0; // // Primary Lagrangian term // if( i0 ) { vact = v; vd = 1; } else { vd = 1/(1-dampingfactor*v); vact = v*vd; vd = vd*vd; } f = f+vlag*vact; state.sclagtmp1[i] = state.sclagtmp1[i]+vlag*vd; // // Quadratic augmentation term // if( i0 ) { vact = v; } else { vact = 0; } rhoi = apserv.coalesce(state.rho[i], 1.0, _params); f = f+0.5*rhoi*vact*vact; state.sclagtmp1[i] = state.sclagtmp1[i]+rhoi*vact; } if( usesparsegemv ) { sparse.sparsemtv(state.subsolver.sparserawlc, state.sclagtmp1, ref state.sclagtmp0, _params); for(i=0; i<=n-1; i++) { g[i] = g[i]+state.sclagtmp0[i]; } } else { ablas.rmatrixgemv(n, nec+nic, 1.0, state.scaledcleic, 0, 0, 1, state.sclagtmp1, 0, 1.0, g, 0, _params); } } // // Lagrangian terms for nonlinear constraints // apserv.rvectorsetlengthatleast(ref state.sclagtmp1, nlec+nlic, _params); for(i=0; i<=nlec+nlic-1; i++) { v = fi[1+i]; if( i0 ) { // // Either equality constraint or violated inequality one. // Update violation report. // vviolate = Math.Abs(v); if( vviolate>nlcerr ) { nlcerr = vviolate; nlcidx = i; } } vlag = lagmult[nec+nic+i]; state.sclagtmp1[i] = 0; // // Lagrangian term // if( i0 ) { vact = v; vd = 1; } else { vd = 1/(1-dampingfactor*v); vact = v*vd; vd = vd*vd; } f = f+vlag*vact; state.sclagtmp1[i] = state.sclagtmp1[i]+vlag*vd; // // Augmentation term // if( i0 ) { vact = v; } else { vact = 0; } rhoi = apserv.coalesce(state.rho[nec+nic+i], 1.0, _params); f = f+0.5*rhoi*vact*vact; state.sclagtmp1[i] = state.sclagtmp1[i]+rhoi*vact; } ablas.rmatrixgemv(n, nlec+nlic, 1.0, j, 1, 0, 1, state.sclagtmp1, 0, 1.0, g, 0, _params); } /************************************************************************* This function calculates L1-penalized merit function. *************************************************************************/ private static double meritfunction(minslpstate state, double[] x, double trustrad, double[] fi, double[,] j, double[] lagmult, alglib.xparams _params) { double result = 0; int i = 0; int n = 0; int nec = 0; int nic = 0; int nlec = 0; int nlic = 0; double v = 0; n = state.n; nec = state.nec; nic = state.nic; nlec = state.nlec; nlic = state.nlic; // // Merit function: primary term // result = fi[0]; // // Merit function: augmentation and penalty for linear constraints // apserv.rvectorsetlengthatleast(ref state.mftmp0, nec+nic, _params); ablas.rmatrixgemv(nec+nic, n, 1.0, state.scaledcleic, 0, 0, 0, x, 0, 0.0, state.mftmp0, 0, _params); for(i=0; i<=nec+nic-1; i++) { v = state.mftmp0[i]-state.scaledcleic[i,n]; if( i=1 is supported, but x^2>=0 is NOT supported. USAGE: Constrained optimization if far more complex than the unconstrained one. Nonlinearly constrained optimization is one of the most esoteric numerical procedures. Here we give very brief outline of the MinNLC optimizer. We strongly recommend you to study examples in the ALGLIB Reference Manual and to read ALGLIB User Guide on optimization, which is available at http://www.alglib.net/optimization/ 1. User initializes algorithm state with MinNLCCreate() call and chooses what NLC solver to use. There is some solver which is used by default, with default settings, but you should NOT rely on default choice. It may change in future releases of ALGLIB without notice, and no one can guarantee that new solver will be able to solve your problem with default settings. From the other side, if you choose solver explicitly, you can be pretty sure that it will work with new ALGLIB releases. In the current release following solvers can be used: * SLP solver (activated with minnlcsetalgoslp() function) - successive linear programming, recommended as the first step. * AUL solver (activated with minnlcsetalgoaul() function) - augmented Lagrangian method with dense preconditioner. SLP solver is the most robust one in ALGLIB and converges in less iterations than AUL; however, each iteration has higher overhead - we have to solve an LP problem. From the other side, AUL has cheaper iterations - although it typically needs more of them, and also it is less robust in nonconvex setting. 2. [optional] user activates OptGuard integrity checker which tries to detect possible errors in the user-supplied callbacks: * discontinuity/nonsmoothness of the target/nonlinear constraints * errors in the analytic gradient provided by user This feature is essential for early prototyping stages because it helps to catch common coding and problem statement errors. OptGuard can be activated with following functions (one per each check performed): * minnlcoptguardsmoothness() * minnlcoptguardgradient() 3. User adds boundary and/or linear and/or nonlinear constraints by means of calling one of the following functions: a) minnlcsetbc() for boundary constraints b) minnlcsetlc() for linear constraints c) minnlcsetnlc() for nonlinear constraints You may combine (a), (b) and (c) in one optimization problem. 4. User sets scale of the variables with minnlcsetscale() function. It is VERY important to set scale of the variables, because nonlinearly constrained problems are hard to solve when variables are badly scaled. 5. User sets stopping conditions with minnlcsetcond(). If NLC solver uses inner/outer iteration layout, this function sets stopping conditions for INNER iterations. 6. Finally, user calls minnlcoptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G/H. 7. User calls minnlcresults() to get solution; additionally you can retrieve OptGuard report with minnlcoptguardresults(), and get detailed report about purported errors in the target function with: * minnlcoptguardnonc1test0results() * minnlcoptguardnonc1test1results() 8. Optionally user may call minnlcrestartfrom() to solve another problem with same N but another starting point. minnlcrestartfrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size ofX X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. OUTPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlccreate(int n, double[] x, minnlcstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinNLCCreate: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinNLCCreate: Length(X)0: * if given, only leading N elements of X are used * if not given, automatically determined from size ofX X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinNLCSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large TRUNCATION errors, while too small step will result in too large NUMERICAL errors. 1.0E-4 can be good value to start from. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlccreatef(int n, double[] x, double diffstep, minnlcstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinNLCCreateF: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinNLCCreateF: Length(X)(double)(0), "MinNLCCreateF: DiffStep is non-positive!"); minnlcinitinternal(n, x, diffstep, state, _params); } /************************************************************************* This function sets boundary constraints for NLC optimizer. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinNLCRestartFrom(). You may combine boundary constraints with general linear ones - and with nonlinear ones! Boundary constraints are handled more efficiently than other types. Thus, if your problem has mixed constraints, you may explicitly specify some of them as boundary and save some time/space. INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: when you solve your problem with augmented Lagrangian solver, boundary constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of feasible area! -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetbc(minnlcstate state, double[] bndl, double[] bndu, alglib.xparams _params) { int i = 0; int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(bndl)>=n, "MinNLCSetBC: Length(BndL)=n, "MinNLCSetBC: Length(BndU)0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT NOTE 1: when you solve your problem with augmented Lagrangian solver, linear constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of feasible area! -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetlc(minnlcstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { int n = 0; int i = 0; int i_ = 0; n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(k>=0, "MinNLCSetLC: K<0"); alglib.ap.assert(alglib.ap.cols(c)>=n+1 || k==0, "MinNLCSetLC: Cols(C)=k, "MinNLCSetLC: Rows(C)=k, "MinNLCSetLC: Length(CT)0 ) { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = -c[i,i_]; } } else { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = c[i,i_]; } } state.lcsrcidx[state.nec+state.nic] = i; state.nic = state.nic+1; } } } /************************************************************************* This function sets nonlinear constraints for MinNLC optimizer. In fact, this function sets NUMBER of nonlinear constraints. Constraints itself (constraint functions) are passed to MinNLCOptimize() method. This method requires user-defined vector function F[] and its Jacobian J[], where: * first component of F[] and first row of Jacobian J[] corresponds to function being minimized * next NLEC components of F[] (and rows of J) correspond to nonlinear equality constraints G_i(x)=0 * next NLIC components of F[] (and rows of J) correspond to nonlinear inequality constraints H_i(x)<=0 NOTE: you may combine nonlinear constraints with linear/boundary ones. If your problem has mixed constraints, you may explicitly specify some of them as linear ones. It may help optimizer to handle them more efficiently. INPUT PARAMETERS: State - structure previously allocated with MinNLCCreate call. NLEC - number of Non-Linear Equality Constraints (NLEC), >=0 NLIC - number of Non-Linear Inquality Constraints (NLIC), >=0 NOTE 1: when you solve your problem with augmented Lagrangian solver, nonlinear constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of feasible area! NOTE 2: algorithm scales variables according to scale specified by MinNLCSetScale() function, so it can handle problems with badly scaled variables (as long as we KNOW their scales). However, there is no way to automatically scale nonlinear constraints Gi(x) and Hi(x). Inappropriate scaling of Gi/Hi may ruin convergence. Solving problem with constraint "1000*G0(x)=0" is NOT same as solving it with constraint "0.001*G0(x)=0". It means that YOU are the one who is responsible for correct scaling of nonlinear constraints Gi(x) and Hi(x). We recommend you to scale nonlinear constraints in such way that I-th component of dG/dX (or dH/dx) has approximately unit magnitude (for problems with unit scale) or has magnitude approximately equal to 1/S[i] (where S is a scale set by MinNLCSetScale() function). -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetnlc(minnlcstate state, int nlec, int nlic, alglib.xparams _params) { alglib.ap.assert(nlec>=0, "MinNLCSetNLC: NLEC<0"); alglib.ap.assert(nlic>=0, "MinNLCSetNLC: NLIC<0"); state.ng = nlec; state.nh = nlic; state.fi = new double[1+state.ng+state.nh]; state.j = new double[1+state.ng+state.nh, state.n]; } /************************************************************************* This function sets stopping conditions for inner iterations of optimizer. INPUT PARAMETERS: State - structure which stores algorithm state EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinNLCSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic selection of the stopping condition. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetcond(minnlcstate state, double epsx, int maxits, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsx), "MinNLCSetCond: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinNLCSetCond: negative EpsX"); alglib.ap.assert(maxits>=0, "MinNLCSetCond: negative MaxIts!"); if( (double)(epsx)==(double)(0) && maxits==0 ) { epsx = 1.0E-8; } state.epsx = epsx; state.maxits = maxits; } /************************************************************************* This function sets scaling coefficients for NLC optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetscale(minnlcstate state, double[] s, alglib.xparams _params) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.n, "MinNLCSetScale: Length(S)=N. * finally, stability of the process is guaranteed only for K<=N due to degeneracy of intermediate matrices. That's why we recommend to use "exact robust" preconditioner for such cases. RECOMMENDATIONS We recommend to choose between "exact low rank" and "exact robust" preconditioners, with "low rank" version being chosen when you know in advance that total count of non-box constraints won't exceed N, and "robust" version being chosen when you need bulletproof solution. INPUT PARAMETERS: State - structure stores algorithm state UpdateFreq- update frequency. Preconditioner is rebuilt after every UpdateFreq iterations. Recommended value: 10 or higher. Zero value means that good default value will be used. -- ALGLIB -- Copyright 26.09.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetprecexactlowrank(minnlcstate state, int updatefreq, alglib.xparams _params) { alglib.ap.assert(updatefreq>=0, "MinNLCSetPrecExactLowRank: UpdateFreq<0"); if( updatefreq==0 ) { updatefreq = 10; } state.prectype = 2; state.updatefreq = updatefreq; } /************************************************************************* This function sets preconditioner to "exact robust" mode. Preconditioning is very important for convergence of Augmented Lagrangian algorithm because presence of penalty term makes problem ill-conditioned. Difference between performance of preconditioned and unpreconditioned methods can be as large as 100x! MinNLC optimizer may use following preconditioners, each with its own benefits and drawbacks: a) inexact LBFGS-based, with O(N*K) evaluation time b) exact low rank one, with O(N*K^2) evaluation time c) exact robust one, with O(N^3+K*N^2) evaluation time where K is a total number of general linear and nonlinear constraints (box ones are not counted). It also provides special unpreconditioned mode of operation which can be used for test purposes. Comments below discuss robust preconditioner. Exact robust preconditioner uses Cholesky decomposition to invert approximate Hessian matrix H=D+W'*C*W (where D stands for diagonal terms of Hessian, combined result of initial scaling matrix and penalty from box constraints; W stands for general linear constraints and linearization of nonlinear ones; C stands for diagonal matrix of penalty coefficients). This preconditioner has following features: * no special assumptions about constraint structure * preconditioner is optimized for stability; unlike "exact low rank" version which fails for K>=N, this one works well for any value of K. * the only drawback is that is takes O(N^3+K*N^2) time to build it. No economical Woodbury update is applied even when it makes sense, thus there are exist situations (K<=0, "MinNLCSetPrecExactLowRank: UpdateFreq<0"); if( updatefreq==0 ) { updatefreq = 10; } state.prectype = 3; state.updatefreq = updatefreq; } /************************************************************************* This function sets preconditioner to "turned off" mode. Preconditioning is very important for convergence of Augmented Lagrangian algorithm because presence of penalty term makes problem ill-conditioned. Difference between performance of preconditioned and unpreconditioned methods can be as large as 100x! MinNLC optimizer may utilize two preconditioners, each with its own benefits and drawbacks: a) inexact LBFGS-based, and b) exact low rank one. It also provides special unpreconditioned mode of operation which can be used for test purposes. This function activates this test mode. Do not use it in production code to solve real-life problems. INPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 26.09.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetprecnone(minnlcstate state, alglib.xparams _params) { state.updatefreq = 0; state.prectype = 0; } /************************************************************************* This function sets maximum step length (after scaling of step vector with respect to variable scales specified by minnlcsetscale() call). INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0 (default), if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. NOTE: different solvers employed by MinNLC optimizer use different norms for step; AUL solver uses 2-norm, whilst SLP solver uses INF-norm. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetstpmax(minnlcstate state, double stpmax, alglib.xparams _params) { alglib.ap.assert(math.isfinite(stpmax), "MinNLCSetStpMax: StpMax is not finite!"); alglib.ap.assert((double)(stpmax)>=(double)(0), "MinNLCSetStpMax: StpMax<0!"); state.stpmax = stpmax; } /************************************************************************* This function tells MinNLC unit to use Augmented Lagrangian algorithm for nonlinearly constrained optimization. This algorithm is a slight modification of one described in "A Modified Barrier-Augmented Lagrangian Method for Constrained Minimization (1999)" by D.GOLDFARB, R.POLYAK, K. SCHEINBERG, I.YUZEFOVICH. AUL solver can be significantly faster than SLP on easy problems, although it is less robust than SLP (the "gold standard" of robust optimization). Augmented Lagrangian algorithm works by converting problem of minimizing F(x) subject to equality/inequality constraints to unconstrained problem of the form min[ f(x) + + Rho*PENALTY_EQ(x) + SHIFT_EQ(x,Nu1) + + Rho*PENALTY_INEQ(x) + SHIFT_INEQ(x,Nu2) ] where: * Rho is a fixed penalization coefficient * PENALTY_EQ(x) is a penalty term, which is used to APPROXIMATELY enforce equality constraints * SHIFT_EQ(x) is a special "shift" term which is used to "fine-tune" equality constraints, greatly increasing precision * PENALTY_INEQ(x) is a penalty term which is used to approximately enforce inequality constraints * SHIFT_INEQ(x) is a special "shift" term which is used to "fine-tune" inequality constraints, greatly increasing precision * Nu1/Nu2 are vectors of Lagrange coefficients which are fine-tuned during outer iterations of algorithm This version of AUL algorithm uses preconditioner, which greatly accelerates convergence. Because this algorithm is similar to penalty methods, it may perform steps into infeasible area. All kinds of constraints (boundary, linear and nonlinear ones) may be violated in intermediate points - and in the solution. However, properly configured AUL method is significantly better at handling constraints than barrier and/or penalty methods. The very basic outline of algorithm is given below: 1) first outer iteration is performed with "default" values of Lagrange multipliers Nu1/Nu2. Solution quality is low (candidate point can be too far away from true solution; large violation of constraints is possible) and is comparable with that of penalty methods. 2) subsequent outer iterations refine Lagrange multipliers and improve quality of the solution. INPUT PARAMETERS: State - structure which stores algorithm state Rho - penalty coefficient, Rho>0: * large enough that algorithm converges with desired precision. Minimum value is 10*max(S'*diag(H)*S), where S is a scale matrix (set by MinNLCSetScale) and H is a Hessian of the function being minimized. If you can not easily estimate Hessian norm, see our recommendations below. * not TOO large to prevent ill-conditioning * for unit-scale problems (variables and Hessian have unit magnitude), Rho=100 or Rho=1000 can be used. * it is important to note that Rho is internally multiplied by scaling matrix, i.e. optimum value of Rho depends on scale of variables specified by MinNLCSetScale(). ItsCnt - number of outer iterations: * ItsCnt=0 means that small number of outer iterations is automatically chosen (10 iterations in current version). * ItsCnt=1 means that AUL algorithm performs just as usual barrier method. * ItsCnt>1 means that AUL algorithm performs specified number of outer iterations HOW TO CHOOSE PARAMETERS Nonlinear optimization is a tricky area and Augmented Lagrangian algorithm is sometimes hard to tune. Good values of Rho and ItsCnt are problem- specific. In order to help you we prepared following set of recommendations: * for unit-scale problems (variables and Hessian have unit magnitude), Rho=100 or Rho=1000 can be used. * start from some small value of Rho and solve problem with just one outer iteration (ItcCnt=1). In this case algorithm behaves like penalty method. Increase Rho in 2x or 10x steps until you see that one outer iteration returns point which is "rough approximation to solution". It is very important to have Rho so large that penalty term becomes constraining i.e. modified function becomes highly convex in constrained directions. From the other side, too large Rho may prevent you from converging to the solution. You can diagnose it by studying number of inner iterations performed by algorithm: too few (5-10 on 1000-dimensional problem) or too many (orders of magnitude more than dimensionality) usually means that Rho is too large. * with just one outer iteration you usually have low-quality solution. Some constraints can be violated with very large margin, while other ones (which are NOT violated in the true solution) can push final point too far in the inner area of the feasible set. For example, if you have constraint x0>=0 and true solution x0=1, then merely a presence of "x0>=0" will introduce a bias towards larger values of x0. Say, algorithm may stop at x0=1.5 instead of 1.0. * after you found good Rho, you may increase number of outer iterations. ItsCnt=10 is a good value. Subsequent outer iteration will refine values of Lagrange multipliers. Constraints which were violated will be enforced, inactive constraints will be dropped (corresponding multipliers will be decreased). Ideally, you should see 10-1000x improvement in constraint handling (constraint violation is reduced). * if you see that algorithm converges to vicinity of solution, but additional outer iterations do not refine solution, it may mean that algorithm is unstable - it wanders around true solution, but can not approach it. Sometimes algorithm may be stabilized by increasing Rho one more time, making it 5x or 10x larger. SCALING OF CONSTRAINTS [IMPORTANT] AUL optimizer scales variables according to scale specified by MinNLCSetScale() function, so it can handle problems with badly scaled variables (as long as we KNOW their scales). However, because function being optimized is a mix of original function and constraint-dependent penalty functions, it is important to rescale both variables AND constraints. Say, if you minimize f(x)=x^2 subject to 1000000*x>=0, then you have constraint whose scale is different from that of target function (another example is 0.000001*x>=0). It is also possible to have constraints whose scales are misaligned: 1000000*x0>=0, 0.000001*x1<=0. Inappropriate scaling may ruin convergence because minimizing x^2 subject to x>=0 is NOT same as minimizing it subject to 1000000*x>=0. Because we know coefficients of boundary/linear constraints, we can automatically rescale and normalize them. However, there is no way to automatically rescale nonlinear constraints Gi(x) and Hi(x) - they are black boxes. It means that YOU are the one who is responsible for correct scaling of nonlinear constraints Gi(x) and Hi(x). We recommend you to rescale nonlinear constraints in such way that I-th component of dG/dX (or dH/dx) has magnitude approximately equal to 1/S[i] (where S is a scale set by MinNLCSetScale() function). WHAT IF IT DOES NOT CONVERGE? It is possible that AUL algorithm fails to converge to precise values of Lagrange multipliers. It stops somewhere around true solution, but candidate point is still too far from solution, and some constraints are violated. Such kind of failure is specific for Lagrangian algorithms - technically, they stop at some point, but this point is not constrained solution. There are exist several reasons why algorithm may fail to converge: a) too loose stopping criteria for inner iteration b) degenerate, redundant constraints c) target function has unconstrained extremum exactly at the boundary of some constraint d) numerical noise in the target function In all these cases algorithm is unstable - each outer iteration results in large and almost random step which improves handling of some constraints, but violates other ones (ideally outer iterations should form a sequence of progressively decreasing steps towards solution). First reason possible is that too loose stopping criteria for inner iteration were specified. Augmented Lagrangian algorithm solves a sequence of intermediate problems, and requries each of them to be solved with high precision. Insufficient precision results in incorrect update of Lagrange multipliers. Another reason is that you may have specified degenerate constraints: say, some constraint was repeated twice. In most cases AUL algorithm gracefully handles such situations, but sometimes it may spend too much time figuring out subtle degeneracies in constraint matrix. Third reason is tricky and hard to diagnose. Consider situation when you minimize f=x^2 subject to constraint x>=0. Unconstrained extremum is located exactly at the boundary of constrained area. In this case algorithm will tend to oscillate between negative and positive x. Each time it stops at x<0 it "reinforces" constraint x>=0, and each time it is bounced to x>0 it "relaxes" constraint (and is attracted to x<0). Such situation sometimes happens in problems with hidden symetries. Algorithm is got caught in a loop with Lagrange multipliers being continuously increased/decreased. Luckily, such loop forms after at least three iterations, so this problem can be solved by DECREASING number of outer iterations down to 1-2 and increasing penalty coefficient Rho as much as possible. Final reason is numerical noise. AUL algorithm is robust against moderate noise (more robust than, say, active set methods), but large noise may destabilize algorithm. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetalgoaul(minnlcstate state, double rho, int itscnt, alglib.xparams _params) { alglib.ap.assert(itscnt>=0, "MinNLCSetAlgoAUL: negative ItsCnt"); alglib.ap.assert(math.isfinite(rho), "MinNLCSetAlgoAUL: Rho is not finite"); alglib.ap.assert((double)(rho)>(double)(0), "MinNLCSetAlgoAUL: Rho<=0"); if( itscnt==0 ) { itscnt = 10; } state.aulitscnt = itscnt; state.rho = rho; state.solvertype = 0; } /************************************************************************* This function tells MinNLC optimizer to use SLP (Successive Linear Programming) algorithm for nonlinearly constrained optimization. This algorithm is a slight modification of one described in "A Linear programming-based optimization algorithm for solving nonlinear programming problems" (2010) by Claus Still and Tapio Westerlund. Despite its name ("linear" = "first order method") this algorithm performs steps similar to that of conjugate gradients method; internally it uses orthogonality/conjugacy requirement for subsequent steps which makes it closer to second order methods in terms of convergence speed. Convergence is proved for the following case: * function and constraints are continuously differentiable (C1 class) * extended Mangasarian–Fromovitz constraint qualification (EMFCQ) holds; in the context of this algorithm EMFCQ means that one can, for any infeasible point, find a search direction such that the constraint infeasibilities are reduced. This algorithm has following nice properties: * no parameters to tune * no convexity requirements for target function or constraints * initial point can be infeasible * algorithm respects box constraints in all intermediate points (it does not even evaluate function outside of box constrained area) * once linear constraints are enforced, algorithm will not violate them * no such guarantees can be provided for nonlinear constraints, but once nonlinear constraints are enforced, algorithm will try to respect them as much as possible * numerical differentiation does not violate box constraints (although general linear and nonlinear ones can be violated during differentiation) However, following drawbacks can be noted: * algorithm performance decreased on problems with dense constraints * it has higher iteration cost than AUL - we have to solve an LP problem at each step. We recommend this algorithm as a first step; as soon as you make sure that it converges, you can try switching to AUL which is sometimes much faster. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 02.04.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetalgoslp(minnlcstate state, alglib.xparams _params) { state.solvertype = 1; } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinNLCOptimize(). NOTE: algorithm passes two parameters to rep() callback - current point and penalized function value at current point. Important - function value which is returned is NOT function being minimized. It is sum of the value of the function being minimized - and penalty term. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetxrep(minnlcstate state, bool needxrep, alglib.xparams _params) { state.xrep = needxrep; } /************************************************************************* NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied Jacobian, and one which uses only function vector and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object you should choose appropriate variant of MinNLCOptimize() - one which accepts function AND Jacobian or one which accepts ONLY function. Be careful to choose variant of MinNLCOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinNLCOptimize() and specific function used to create optimizer. | USER PASSED TO MinNLCOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinNLCCreateF() | works FAILS MinNLCCreate() | FAILS works Here "FAILS" denotes inappropriate combinations of optimizer creation function and MinNLCOptimize() version. Attemps to use such combination will lead to exception. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static bool minnlciteration(minnlcstate state, alglib.xparams _params) { bool result = new bool(); int i = 0; int k = 0; int n = 0; int ng = 0; int nh = 0; double vleft = 0; double vright = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { i = state.rstate.ia[0]; k = state.rstate.ia[1]; n = state.rstate.ia[2]; ng = state.rstate.ia[3]; nh = state.rstate.ia[4]; vleft = state.rstate.ra[0]; vright = state.rstate.ra[1]; } else { i = 359; k = -58; n = -919; ng = -909; nh = 81; vleft = 255; vright = 74; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } if( state.rstate.stage==4 ) { goto lbl_4; } if( state.rstate.stage==5 ) { goto lbl_5; } if( state.rstate.stage==6 ) { goto lbl_6; } if( state.rstate.stage==7 ) { goto lbl_7; } if( state.rstate.stage==8 ) { goto lbl_8; } if( state.rstate.stage==9 ) { goto lbl_9; } if( state.rstate.stage==10 ) { goto lbl_10; } if( state.rstate.stage==11 ) { goto lbl_11; } if( state.rstate.stage==12 ) { goto lbl_12; } if( state.rstate.stage==13 ) { goto lbl_13; } if( state.rstate.stage==14 ) { goto lbl_14; } if( state.rstate.stage==15 ) { goto lbl_15; } // // Routine body // // // Init // state.userterminationneeded = false; state.repterminationtype = 0; state.repinneriterationscount = 0; state.repouteriterationscount = 0; state.repnfev = 0; state.repdbgphase0its = 0; state.repbcerr = 0; state.repbcidx = -1; state.replcerr = 0; state.replcidx = -1; state.repnlcerr = 0; state.repnlcidx = -1; n = state.n; ng = state.ng; nh = state.nh; clearrequestfields(state, _params); alglib.ap.assert(state.smoothnessguardlevel==0 || state.smoothnessguardlevel==1, "MinNLCIteration: integrity check failed"); optserv.smoothnessmonitorinit(state.smonitor, n, 1+ng+nh, state.smoothnessguardlevel>0, _params); for(i=0; i<=n-1; i++) { state.lastscaleused[i] = state.s[i]; } // // Check correctness of box constraints // for(i=0; i<=n-1; i++) { if( state.hasbndl[i] && state.hasbndu[i] ) { if( (double)(state.bndl[i])>(double)(state.bndu[i]) ) { state.repterminationtype = -3; state.repbcerr = state.bndl[i]-state.bndu[i]; state.repbcidx = i; result = false; return result; } } } // // Test gradient // if( !((double)(state.diffstep)==(double)(0) && (double)(state.teststep)>(double)(0)) ) { goto lbl_16; } lbl_18: if( !optserv.smoothnessmonitorcheckgradientatx0(state.smonitor, state.xstart, state.s, state.bndl, state.bndu, true, state.teststep, _params) ) { goto lbl_19; } for(i=0; i<=n-1; i++) { state.x[i] = state.smonitor.x[i]; } state.needfij = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needfij = false; for(i=0; i<=ng+nh; i++) { state.smonitor.fi[i] = state.fi[i]; for(k=0; k<=n-1; k++) { state.smonitor.j[i,k] = state.j[i,k]; } } goto lbl_18; lbl_19: lbl_16: // // AUL solver // if( state.solvertype!=0 ) { goto lbl_20; } if( (double)(state.diffstep)!=(double)(0) ) { apserv.rvectorsetlengthatleast(ref state.xbase, n, _params); apserv.rvectorsetlengthatleast(ref state.fbase, 1+ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.fm2, 1+ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.fm1, 1+ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.fp1, 1+ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.fp2, 1+ng+nh, _params); } state.rstateaul.ia = new int[8+1]; state.rstateaul.ra = new double[7+1]; state.rstateaul.stage = -1; lbl_22: if( !auliteration(state, state.smonitor, _params) ) { goto lbl_23; } // // Numerical differentiation (if needed) - intercept NeedFiJ // request and replace it by sequence of NeedFi requests // if( !((double)(state.diffstep)!=(double)(0) && state.needfij) ) { goto lbl_24; } state.needfij = false; state.needfi = true; for(i_=0; i_<=n-1;i_++) { state.xbase[i_] = state.x[i_]; } k = 0; lbl_26: if( k>n-1 ) { goto lbl_28; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]-state.s[k]*state.diffstep; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: for(i_=0; i_<=ng+nh;i_++) { state.fm2[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]-0.5*state.s[k]*state.diffstep; state.rstate.stage = 2; goto lbl_rcomm; lbl_2: for(i_=0; i_<=ng+nh;i_++) { state.fm1[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]+0.5*state.s[k]*state.diffstep; state.rstate.stage = 3; goto lbl_rcomm; lbl_3: for(i_=0; i_<=ng+nh;i_++) { state.fp1[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]+state.s[k]*state.diffstep; state.rstate.stage = 4; goto lbl_rcomm; lbl_4: for(i_=0; i_<=ng+nh;i_++) { state.fp2[i_] = state.fi[i_]; } for(i=0; i<=ng+nh; i++) { state.j[i,k] = (8*(state.fp1[i]-state.fm1[i])-(state.fp2[i]-state.fm2[i]))/(6*state.diffstep*state.s[i]); } k = k+1; goto lbl_26; lbl_28: for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.rstate.stage = 5; goto lbl_rcomm; lbl_5: // // Restore previous values of fields and continue // state.needfi = false; state.needfij = true; goto lbl_22; lbl_24: // // Forward request to caller // state.rstate.stage = 6; goto lbl_rcomm; lbl_6: goto lbl_22; lbl_23: result = false; return result; lbl_20: // // SLP solver // if( state.solvertype!=1 ) { goto lbl_29; } if( (double)(state.diffstep)!=(double)(0) ) { apserv.rvectorsetlengthatleast(ref state.xbase, n, _params); apserv.rvectorsetlengthatleast(ref state.fbase, 1+ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.fm2, 1+ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.fm1, 1+ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.fp1, 1+ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.fp2, 1+ng+nh, _params); } nlcslp.minslpinitbuf(state.bndl, state.bndu, state.s, state.xstart, n, state.cleic, state.lcsrcidx, state.nec, state.nic, state.ng, state.nh, state.epsx, state.maxits, state.slpsolverstate, _params); lbl_31: if( !nlcslp.minslpiteration(state.slpsolverstate, state.smonitor, state.userterminationneeded, _params) ) { goto lbl_32; } // // Forward request to caller // if( !state.slpsolverstate.needfij ) { goto lbl_33; } // // Evaluate target function/Jacobian // if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_35; } // // Analytic Jacobian is provided // unscale(state, state.slpsolverstate.x, state.slpsolverstate.scaledbndl, state.slpsolverstate.scaledbndu, state.x, _params); state.needfij = true; state.rstate.stage = 7; goto lbl_rcomm; lbl_7: state.needfij = false; for(i=0; i<=ng+nh; i++) { state.slpsolverstate.fi[i] = state.fi[i]; for(k=0; k<=n-1; k++) { state.slpsolverstate.j[i,k] = state.j[i,k]*state.s[k]; } } goto lbl_36; lbl_35: // // Numerical differentiation // state.needfij = false; state.needfi = true; unscale(state, state.slpsolverstate.x, state.slpsolverstate.scaledbndl, state.slpsolverstate.scaledbndu, state.xbase, _params); k = 0; lbl_37: if( k>n-1 ) { goto lbl_39; } vleft = state.xbase[k]-state.s[k]*state.diffstep; vright = state.xbase[k]+state.s[k]*state.diffstep; if( !((state.hasbndl[k] && (double)(vleft)<(double)(state.bndl[k])) || (state.hasbndu[k] && (double)(vright)>(double)(state.bndu[k]))) ) { goto lbl_40; } // // Box constraint is violated by 4-point centered formula, use 2-point uncentered one // if( state.hasbndl[k] && (double)(vleft)<(double)(state.bndl[k]) ) { vleft = state.bndl[k]; } if( state.hasbndu[k] && (double)(vright)>(double)(state.bndu[k]) ) { vright = state.bndu[k]; } alglib.ap.assert((double)(vleft)<=(double)(vright), "MinNLC: integrity check failed"); if( (double)(vleft)==(double)(vright) ) { // // Fixed variable // for(i=0; i<=ng+nh; i++) { state.j[i,k] = 0; } goto lbl_38; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = vleft; state.rstate.stage = 8; goto lbl_rcomm; lbl_8: for(i_=0; i_<=ng+nh;i_++) { state.fm1[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = vright; state.rstate.stage = 9; goto lbl_rcomm; lbl_9: for(i_=0; i_<=ng+nh;i_++) { state.fp1[i_] = state.fi[i_]; } for(i=0; i<=ng+nh; i++) { state.j[i,k] = (state.fp1[i]-state.fm1[i])/(vright-vleft); } goto lbl_41; lbl_40: // // 4-point centered formula does not violate box constraints // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]-state.s[k]*state.diffstep; state.rstate.stage = 10; goto lbl_rcomm; lbl_10: for(i_=0; i_<=ng+nh;i_++) { state.fm2[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]-0.5*state.s[k]*state.diffstep; state.rstate.stage = 11; goto lbl_rcomm; lbl_11: for(i_=0; i_<=ng+nh;i_++) { state.fm1[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]+0.5*state.s[k]*state.diffstep; state.rstate.stage = 12; goto lbl_rcomm; lbl_12: for(i_=0; i_<=ng+nh;i_++) { state.fp1[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]+state.s[k]*state.diffstep; state.rstate.stage = 13; goto lbl_rcomm; lbl_13: for(i_=0; i_<=ng+nh;i_++) { state.fp2[i_] = state.fi[i_]; } for(i=0; i<=ng+nh; i++) { state.j[i,k] = (8*(state.fp1[i]-state.fm1[i])-(state.fp2[i]-state.fm2[i]))/(6*state.diffstep*state.s[k]); } lbl_41: lbl_38: k = k+1; goto lbl_37; lbl_39: for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.rstate.stage = 14; goto lbl_rcomm; lbl_14: state.needfi = false; state.needfij = true; for(i=0; i<=ng+nh; i++) { state.slpsolverstate.fi[i] = state.fi[i]; for(k=0; k<=n-1; k++) { state.slpsolverstate.j[i,k] = state.j[i,k]*state.s[k]; } } lbl_36: goto lbl_31; lbl_33: if( !state.slpsolverstate.xupdated ) { goto lbl_42; } // // Report current point // if( !state.xrep ) { goto lbl_44; } unscale(state, state.slpsolverstate.x, state.slpsolverstate.scaledbndl, state.slpsolverstate.scaledbndu, state.x, _params); state.f = state.slpsolverstate.f; state.xupdated = true; state.rstate.stage = 15; goto lbl_rcomm; lbl_15: state.xupdated = false; lbl_44: goto lbl_31; lbl_42: alglib.ap.assert(state.slpsolverstate.needfij, "NLC:SLP:request"); goto lbl_31; lbl_32: state.repterminationtype = state.slpsolverstate.repterminationtype; state.repouteriterationscount = state.slpsolverstate.repouteriterationscount; state.repinneriterationscount = state.slpsolverstate.repinneriterationscount; state.repbcerr = state.slpsolverstate.repbcerr; state.repbcidx = state.slpsolverstate.repbcidx; state.replcerr = state.slpsolverstate.replcerr; state.replcidx = state.slpsolverstate.replcidx; state.repnlcerr = state.slpsolverstate.repnlcerr; state.repnlcidx = state.slpsolverstate.repnlcidx; unscale(state, state.slpsolverstate.stepkx, state.slpsolverstate.scaledbndl, state.slpsolverstate.scaledbndu, state.xc, _params); result = false; return result; lbl_29: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = i; state.rstate.ia[1] = k; state.rstate.ia[2] = n; state.rstate.ia[3] = ng; state.rstate.ia[4] = nh; state.rstate.ra[0] = vleft; state.rstate.ra[1] = vright; return result; } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient/Jacobian. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function (constraints) at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient/Jacobian with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients/Jacobians, and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with minnlcoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minnlcsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardgradient(minnlcstate state, double teststep, alglib.xparams _params) { alglib.ap.assert(math.isfinite(teststep), "MinNLCOptGuardGradient: TestStep contains NaN or INF"); alglib.ap.assert((double)(teststep)>=(double)(0), "MinNLCOptGuardGradient: invalid argument TestStep(TestStep<0)"); state.teststep = teststep; } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) and/or constraints b) nonsmooth target function (non-C1) and/or constraints Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardsmoothness(minnlcstate state, int level, alglib.xparams _params) { alglib.ap.assert(level==0 || level==1, "MinNLCOptGuardSmoothness: unexpected value of level parameter"); state.smoothnessguardlevel = level; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * minnlcoptguardgradient() for gradient verification * minnlcoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradfidx for specific function (Jacobian row) suspected * rep.badgradvidx for specific variable (Jacobian column) suspected * rep.badgradxbase, a point where gradient/Jacobian is tested * rep.badgraduser, user-provided gradient/Jacobian * rep.badgradnum, reference gradient/Jacobian obtained via numerical differentiation * rep.nonc0suspected, and additionally: * rep.nonc0fidx - an index of specific function violating C0 continuity * rep.nonc1suspected, and additionally * rep.nonc1fidx - an index of specific function violating C1 continuity Here function index 0 means target function, index 1 or higher denotes nonlinear constraints. === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * minnlcoptguardnonc1test0results() * minnlcoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardresults(minnlcstate state, optguardapi.optguardreport rep, alglib.xparams _params) { optserv.smoothnessmonitorexportreport(state.smonitor, rep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardnonc1test0results(minnlcstate state, optguardapi.optguardnonc1test0report strrep, optguardapi.optguardnonc1test0report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * fidx - is an index of the function (0 for target function, 1 or higher for nonlinear constraints) which is suspected of being "non-C1" * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minnlcoptguardnonc1test1results(minnlcstate state, optguardapi.optguardnonc1test1report strrep, optguardapi.optguardnonc1test1report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* MinNLC results: the solution found, completion codes and additional information. If you activated OptGuard integrity checking functionality and want to get OptGuard report, it can be retrieved with: * minnlcoptguardresults() - for a primary report about (a) suspected C0/C1 continuity violations and (b) errors in the analytic gradient. * minnlcoptguardnonc1test0results() - for C1 continuity violation test #0, detailed line search log * minnlcoptguardnonc1test1results() - for C1 continuity violation test #1, detailed line search log INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report, contains information about completion code, constraint violation at the solution and so on. You should check rep.terminationtype in order to distinguish successful termination from unsuccessful one: === FAILURE CODES === * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -3 box constraints are infeasible. Note: infeasibility of non-box constraints does NOT trigger emergency completion; you have to examine rep.bcerr/rep.lcerr/rep.nlcerr to detect possibly inconsistent constraints. === SUCCESS CODES === * 2 scaled step is no more than EpsX. * 5 MaxIts steps were taken. * 8 user requested algorithm termination via minnlcrequesttermination(), last accepted point is returned. More information about fields of this structure can be found in the comments on minnlcreport datatype. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcresults(minnlcstate state, ref double[] x, minnlcreport rep, alglib.xparams _params) { x = new double[0]; minnlcresultsbuf(state, ref x, rep, _params); } /************************************************************************* NLC results Buffered implementation of MinNLCResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcresultsbuf(minnlcstate state, ref double[] x, minnlcreport rep, alglib.xparams _params) { int i = 0; int i_ = 0; if( alglib.ap.len(x)0 ) { for(i_=0; i_<=state.n-1;i_++) { x[i_] = state.xc[i_]; } } else { for(i=0; i<=state.n-1; i++) { x[i] = Double.NaN; } } } /************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 08.10.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcrequesttermination(minnlcstate state, alglib.xparams _params) { state.userterminationneeded = true; } /************************************************************************* This subroutine restarts algorithm from new point. All optimization parameters (including constraints) are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure previously allocated with MinNLCCreate call. X - new starting point. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcrestartfrom(minnlcstate state, double[] x, alglib.xparams _params) { int n = 0; int i_ = 0; n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(alglib.ap.len(x)>=n, "MinNLCRestartFrom: Length(X)=+1 - in this case infinite value is returned. OUTPUT PARAMETERS: F - depending on Alpha: * for Alpha in (-1+eps,+1-eps), F=F(Alpha) * for Alpha outside of interval, F is some very large number DF - depending on Alpha: * for Alpha in (-1+eps,+1-eps), DF=dF(Alpha)/dAlpha, exact numerical derivative. * otherwise, it is zero D2F - second derivative -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcequalitypenaltyfunction(double alpha, ref double f, ref double df, ref double d2f, alglib.xparams _params) { f = 0; df = 0; d2f = 0; f = 0.5*alpha*alpha; df = alpha; d2f = 1.0; } /************************************************************************* "Penalty" function for inequality constraints, which is multiplied by penalty coefficient Rho. "Penalty" function plays only supplementary role - it helps to stabilize algorithm when solving non-convex problems. Because it is multiplied by fixed and large Rho - not Lagrange multiplier Nu which may become arbitrarily small! - it enforces convexity of the problem behind the boundary of the feasible area. This function is zero at the feasible area and in the close neighborhood, it becomes non-zero only at some distance (scaling is essential!) and grows quadratically. Penalty function must enter augmented Lagrangian as Rho*PENALTY(x-lowerbound) with corresponding changes being made for upper bound or other kinds of constraints. INPUT PARAMETERS: Alpha - function argument. Typically, if we have active constraint with precise Lagrange multiplier, we have Alpha around 1. Large positive Alpha's correspond to inner area of the feasible set. Alpha<1 corresponds to outer area of the feasible set. StabilizingPoint- point where F becomes non-zero. Must be negative value, at least -1, large values (hundreds) are possible. OUTPUT PARAMETERS: F - F(Alpha) DF - DF=dF(Alpha)/dAlpha, exact derivative D2F - second derivative NOTE: it is important to have significantly non-zero StabilizingPoint, because when it is large, shift term does not interfere with Lagrange multipliers converging to their final values. Thus, convergence of such modified AUL algorithm is still guaranteed by same set of theorems. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcinequalitypenaltyfunction(double alpha, double stabilizingpoint, ref double f, ref double df, ref double d2f, alglib.xparams _params) { f = 0; df = 0; d2f = 0; if( (double)(alpha)>=(double)(stabilizingpoint) ) { f = 0.0; df = 0.0; d2f = 0.0; } else { alpha = alpha-stabilizingpoint; f = 0.5*alpha*alpha; df = alpha; d2f = 1.0; } } /************************************************************************* "Shift" function for inequality constraints, which is multiplied by corresponding Lagrange multiplier. "Shift" function is a main factor which enforces inequality constraints. Inequality penalty function plays only supplementary role - it prevents accidental step deep into infeasible area when working with non-convex problems (read comments on corresponding function for more information). Shift function must enter augmented Lagrangian as Nu/Rho*SHIFT((x-lowerbound)*Rho+1) with corresponding changes being made for upper bound or other kinds of constraints. INPUT PARAMETERS: Alpha - function argument. Typically, if we have active constraint with precise Lagrange multiplier, we have Alpha around 1. Large positive Alpha's correspond to inner area of the feasible set. Alpha<1 corresponds to outer area of the feasible set. OUTPUT PARAMETERS: F - F(Alpha) DF - DF=dF(Alpha)/dAlpha, exact derivative D2F - second derivative -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcinequalityshiftfunction(double alpha, ref double f, ref double df, ref double d2f, alglib.xparams _params) { f = 0; df = 0; d2f = 0; if( (double)(alpha)>=(double)(0.5) ) { f = -Math.Log(alpha); df = -(1/alpha); d2f = 1/(alpha*alpha); } else { f = 2*alpha*alpha-4*alpha+(Math.Log(2)+1.5); df = 4*alpha-4; d2f = 4; } } /************************************************************************* Clears request fileds (to be sure that we don't forget to clear something) *************************************************************************/ private static void clearrequestfields(minnlcstate state, alglib.xparams _params) { state.needfi = false; state.needfij = false; state.xupdated = false; } /************************************************************************* Internal initialization subroutine. Sets default NLC solver with default criteria. *************************************************************************/ private static void minnlcinitinternal(int n, double[] x, double diffstep, minnlcstate state, alglib.xparams _params) { int i = 0; double[,] c = new double[0,0]; int[] ct = new int[0]; // // Default params // state.stabilizingpoint = -2.0; state.initialinequalitymultiplier = 1.0; // // Smoothness monitor, default init // state.teststep = 0; state.smoothnessguardlevel = 0; optserv.smoothnessmonitorinit(state.smonitor, 0, 0, false, _params); // // Initialize other params // state.n = n; state.diffstep = diffstep; state.userterminationneeded = false; state.bndl = new double[n]; state.hasbndl = new bool[n]; state.bndu = new double[n]; state.hasbndu = new bool[n]; state.s = new double[n]; state.lastscaleused = new double[n]; state.xstart = new double[n]; state.xc = new double[n]; state.x = new double[n]; for(i=0; i<=n-1; i++) { state.bndl[i] = Double.NegativeInfinity; state.hasbndl[i] = false; state.bndu[i] = Double.PositiveInfinity; state.hasbndu[i] = false; state.s[i] = 1.0; state.lastscaleused[i] = 1.0; state.xstart[i] = x[i]; state.xc[i] = x[i]; } minnlcsetlc(state, c, ct, 0, _params); minnlcsetnlc(state, 0, 0, _params); minnlcsetcond(state, 0.0, 0, _params); minnlcsetxrep(state, false, _params); minnlcsetalgoslp(state, _params); minnlcsetprecexactrobust(state, 0, _params); minnlcsetstpmax(state, 0.0, _params); minlbfgs.minlbfgscreate(n, Math.Min(lbfgsfactor, n), x, state.auloptimizer, _params); minnlcrestartfrom(state, x, _params); } /************************************************************************* This function clears preconditioner for L-BFGS optimizer (sets it do default state); Parameters: AULOptimizer - optimizer to tune -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ private static void clearpreconditioner(minlbfgs.minlbfgsstate auloptimizer, alglib.xparams _params) { minlbfgs.minlbfgssetprecdefault(auloptimizer, _params); } /************************************************************************* This function updates preconditioner for L-BFGS optimizer. Parameters: PrecType - preconditioner type: * 0 for unpreconditioned iterations * 1 for inexact LBFGS * 2 for exact low rank preconditioner update after each UpdateFreq its * 3 for exact robust preconditioner update after each UpdateFreq its UpdateFreq - update frequency PrecCounter - iterations counter, must be zero on the first call, automatically increased by this function. This counter is used to implement "update-once-in-X-iterations" scheme. AULOptimizer - optimizer to tune X - current point Rho - penalty term GammaK - current estimate of Hessian norm (used for initialization of preconditioner). Can be zero, in which case Hessian is assumed to be unit. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ private static void updatepreconditioner(int prectype, int updatefreq, ref int preccounter, minlbfgs.minlbfgsstate auloptimizer, double[] x, double rho, double gammak, double[] bndl, bool[] hasbndl, double[] bndu, bool[] hasbndu, double[] nubc, double[,] cleic, double[] nulc, double[] fi, double[,] jac, double[] nunlc, ref double[] bufd, ref double[] bufc, ref double[,] bufw, ref double[,] bufz, ref double[] tmp0, int n, int nec, int nic, int ng, int nh, alglib.xparams _params) { int i = 0; int j = 0; int k = 0; double v = 0; double p = 0; double dp = 0; double d2p = 0; bool bflag = new bool(); int i_ = 0; alglib.ap.assert((double)(rho)>(double)(0), "MinNLC: integrity check failed"); apserv.rvectorsetlengthatleast(ref bufd, n, _params); apserv.rvectorsetlengthatleast(ref bufc, nec+nic+ng+nh, _params); apserv.rmatrixsetlengthatleast(ref bufw, nec+nic+ng+nh, n, _params); apserv.rvectorsetlengthatleast(ref tmp0, n, _params); // // Preconditioner before update from barrier/penalty functions // if( (double)(gammak)==(double)(0) ) { gammak = 1; } for(i=0; i<=n-1; i++) { bufd[i] = gammak; } // // Update diagonal Hessian using nonlinearity from boundary constraints: // * penalty term from equality constraints // * shift term from inequality constraints // // NOTE: penalty term for inequality constraints is ignored because it // is large only in exceptional cases. // for(i=0; i<=n-1; i++) { if( (hasbndl[i] && hasbndu[i]) && (double)(bndl[i])==(double)(bndu[i]) ) { minnlcequalitypenaltyfunction((x[i]-bndl[i])*rho, ref p, ref dp, ref d2p, _params); bufd[i] = bufd[i]+d2p*rho; continue; } if( hasbndl[i] ) { minnlcinequalityshiftfunction((x[i]-bndl[i])*rho+1, ref p, ref dp, ref d2p, _params); bufd[i] = bufd[i]+nubc[2*i+0]*d2p*rho; } if( hasbndu[i] ) { minnlcinequalityshiftfunction((bndu[i]-x[i])*rho+1, ref p, ref dp, ref d2p, _params); bufd[i] = bufd[i]+nubc[2*i+1]*d2p*rho; } } // // Process linear constraints // for(i=0; i<=nec+nic-1; i++) { for(i_=0; i_<=n-1;i_++) { bufw[i,i_] = cleic[i,i_]; } v = 0.0; for(i_=0; i_<=n-1;i_++) { v += cleic[i,i_]*x[i_]; } v = v-cleic[i,n]; if( i0 ) { for(i=0; i<=nec+nic+ng+nh-1; i++) { alglib.ap.assert((double)(bufc[i])>=(double)(0), "MinNLC: updatepreconditioner() integrity failure"); v = Math.Sqrt(bufc[i]); for(j=0; j<=n-1; j++) { bufw[i,j] = bufw[i,j]*v; } } ablas.rmatrixsyrk(n, nec+nic+ng+nh, 1.0, bufw, 0, 0, 2, 1.0, bufz, 0, 0, true, _params); } // // Evaluate Cholesky decomposition, set preconditioner // bflag = trfac.spdmatrixcholeskyrec(ref bufz, 0, n, true, ref bufd, _params); alglib.ap.assert(bflag, "MinNLC: updatepreconditioner() failure, Cholesky failed"); minlbfgs.minlbfgssetpreccholesky(auloptimizer, bufz, true, _params); } apserv.inc(ref preccounter, _params); } /************************************************************************* This subroutine adds penalty from boundary constraints to target function and its gradient. Penalty function is one which is used for main AUL cycle - with Lagrange multipliers and infinite at the barrier and beyond. Parameters: X[] - current point BndL[], BndU[] - boundary constraints HasBndL[], HasBndU[] - I-th element is True if corresponding constraint is present NuBC[] - Lagrange multipliers corresponding to constraints Rho - penalty term StabilizingPoint - branch point for inequality stabilizing term F - function value to modify G - gradient to modify -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ private static void penaltybc(double[] x, double[] bndl, bool[] hasbndl, double[] bndu, bool[] hasbndu, double[] nubc, int n, double rho, double stabilizingpoint, ref double f, double[] g, alglib.xparams _params) { int i = 0; double p = 0; double dp = 0; double d2p = 0; for(i=0; i<=n-1; i++) { if( (hasbndl[i] && hasbndu[i]) && (double)(bndl[i])==(double)(bndu[i]) ) { // // I-th boundary constraint is of equality-type // minnlcequalitypenaltyfunction((x[i]-bndl[i])*rho, ref p, ref dp, ref d2p, _params); f = f+p/rho-nubc[2*i+0]*(x[i]-bndl[i]); g[i] = g[i]+dp-nubc[2*i+0]; continue; } if( hasbndl[i] ) { // // Handle lower bound // minnlcinequalitypenaltyfunction(x[i]-bndl[i], stabilizingpoint, ref p, ref dp, ref d2p, _params); f = f+rho*p; g[i] = g[i]+rho*dp; minnlcinequalityshiftfunction((x[i]-bndl[i])*rho+1, ref p, ref dp, ref d2p, _params); f = f+p/rho*nubc[2*i+0]; g[i] = g[i]+dp*nubc[2*i+0]; } if( hasbndu[i] ) { // // Handle upper bound // minnlcinequalitypenaltyfunction(bndu[i]-x[i], stabilizingpoint, ref p, ref dp, ref d2p, _params); f = f+rho*p; g[i] = g[i]-rho*dp; minnlcinequalityshiftfunction((bndu[i]-x[i])*rho+1, ref p, ref dp, ref d2p, _params); f = f+p/rho*nubc[2*i+1]; g[i] = g[i]-dp*nubc[2*i+1]; } } } /************************************************************************* This subroutine adds penalty from linear constraints to target function and its gradient. Penalty function is one which is used for main AUL cycle - with Lagrange multipliers and infinite at the barrier and beyond. Parameters: X[] - current point CLEIC[] - constraints matrix, first NEC rows are equality ones, next NIC rows are inequality ones. array[NEC+NIC,N+1] NuLC[] - Lagrange multipliers corresponding to constraints, array[NEC+NIC] N - dimensionalty NEC - number of equality constraints NIC - number of inequality constraints. Rho - penalty term StabilizingPoint - branch point for inequality stabilizing term F - function value to modify G - gradient to modify -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ private static void penaltylc(double[] x, double[,] cleic, double[] nulc, int n, int nec, int nic, double rho, double stabilizingpoint, ref double f, double[] g, alglib.xparams _params) { int i = 0; double v = 0; double p = 0; double dp = 0; double d2p = 0; double fupd = 0; double gupd = 0; int i_ = 0; for(i=0; i<=nec+nic-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += cleic[i,i_]*x[i_]; } v = v-cleic[i,n]; fupd = 0; gupd = 0; if( i=0 ) { n = state.rstateaul.ia[0]; nec = state.rstateaul.ia[1]; nic = state.rstateaul.ia[2]; ng = state.rstateaul.ia[3]; nh = state.rstateaul.ia[4]; i = state.rstateaul.ia[5]; j = state.rstateaul.ia[6]; outerit = state.rstateaul.ia[7]; preccounter = state.rstateaul.ia[8]; v = state.rstateaul.ra[0]; vv = state.rstateaul.ra[1]; p = state.rstateaul.ra[2]; dp = state.rstateaul.ra[3]; d2p = state.rstateaul.ra[4]; v0 = state.rstateaul.ra[5]; v1 = state.rstateaul.ra[6]; v2 = state.rstateaul.ra[7]; } else { n = -788; nec = 809; nic = 205; ng = -838; nh = 939; i = -526; j = 763; outerit = -541; preccounter = -698; v = -900; vv = -318; p = -940; dp = 1016; d2p = -229; v0 = -536; v1 = 487; v2 = -115; } if( state.rstateaul.stage==0 ) { goto lbl_0; } if( state.rstateaul.stage==1 ) { goto lbl_1; } if( state.rstateaul.stage==2 ) { goto lbl_2; } // // Routine body // alglib.ap.assert(state.solvertype==0, "MinNLC: internal error"); n = state.n; nec = state.nec; nic = state.nic; ng = state.ng; nh = state.nh; // // Prepare scaled problem // apserv.rvectorsetlengthatleast(ref state.scaledbndl, n, _params); apserv.rvectorsetlengthatleast(ref state.scaledbndu, n, _params); apserv.rmatrixsetlengthatleast(ref state.scaledcleic, nec+nic, n+1, _params); for(i=0; i<=n-1; i++) { if( state.hasbndl[i] ) { state.scaledbndl[i] = state.bndl[i]/state.s[i]; } if( state.hasbndu[i] ) { state.scaledbndu[i] = state.bndu[i]/state.s[i]; } state.xc[i] = state.xstart[i]/state.s[i]; } for(i=0; i<=nec+nic-1; i++) { // // Scale and normalize linear constraints // vv = 0.0; for(j=0; j<=n-1; j++) { v = state.cleic[i,j]*state.s[j]; state.scaledcleic[i,j] = v; vv = vv+v*v; } vv = Math.Sqrt(vv); state.scaledcleic[i,n] = state.cleic[i,n]; if( (double)(vv)>(double)(0) ) { for(j=0; j<=n; j++) { state.scaledcleic[i,j] = state.scaledcleic[i,j]/vv; } } } // // Prepare stopping criteria // minlbfgs.minlbfgssetcond(state.auloptimizer, 0, 0, state.epsx, state.maxits, _params); minlbfgs.minlbfgssetstpmax(state.auloptimizer, state.stpmax, _params); // // Main AUL cycle: // * prepare Lagrange multipliers NuNB/NuLC // * set GammaK (current estimate of Hessian norm) to InitGamma and XKPresent to False // apserv.rvectorsetlengthatleast(ref state.nubc, 2*n, _params); apserv.rvectorsetlengthatleast(ref state.nulc, nec+nic, _params); apserv.rvectorsetlengthatleast(ref state.nunlc, ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.xk, n, _params); apserv.rvectorsetlengthatleast(ref state.gk, n, _params); apserv.rvectorsetlengthatleast(ref state.xk1, n, _params); apserv.rvectorsetlengthatleast(ref state.gk1, n, _params); for(i=0; i<=n-1; i++) { state.nubc[2*i+0] = 0.0; state.nubc[2*i+1] = 0.0; if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { continue; } if( state.hasbndl[i] ) { state.nubc[2*i+0] = state.initialinequalitymultiplier; } if( state.hasbndu[i] ) { state.nubc[2*i+1] = state.initialinequalitymultiplier; } } for(i=0; i<=nec-1; i++) { state.nulc[i] = 0.0; } for(i=0; i<=nic-1; i++) { state.nulc[nec+i] = state.initialinequalitymultiplier; } for(i=0; i<=ng-1; i++) { state.nunlc[i] = 0.0; } for(i=0; i<=nh-1; i++) { state.nunlc[ng+i] = state.initialinequalitymultiplier; } state.gammak = initgamma; state.xkpresent = false; alglib.ap.assert(state.aulitscnt>0, "MinNLC: integrity check failed"); clearpreconditioner(state.auloptimizer, _params); outerit = 0; lbl_3: if( outerit>state.aulitscnt-1 ) { goto lbl_5; } // // Optimize with current Lagrange multipliers // // NOTE: this code expects and checks that line search ends in the // point which is used as beginning for the next search. Such // guarantee is given by MCSRCH function. L-BFGS optimizer // does not formally guarantee it, but it follows same rule. // Below we a) rely on such property of the optimizer, and b) // assert that it is true, in order to fail loudly if it is // not true. // // NOTE: security check for NAN/INF in F/G is responsibility of // LBFGS optimizer. AUL optimizer checks for NAN/INF only // when we update Lagrange multipliers. // preccounter = 0; minlbfgs.minlbfgssetxrep(state.auloptimizer, true, _params); minlbfgs.minlbfgsrestartfrom(state.auloptimizer, state.xc, _params); lbl_6: if( !minlbfgs.minlbfgsiteration(state.auloptimizer, _params) ) { goto lbl_7; } if( !state.auloptimizer.needfg ) { goto lbl_8; } // // Un-scale X, evaluate F/G/H, re-scale Jacobian // for(i=0; i<=n-1; i++) { state.x[i] = state.auloptimizer.x[i]*state.s[i]; } state.needfij = true; state.rstateaul.stage = 0; goto lbl_rcomm; lbl_0: state.needfij = false; for(i=0; i<=ng+nh; i++) { for(j=0; j<=n-1; j++) { state.j[i,j] = state.j[i,j]*state.s[j]; } } // // Store data for estimation of Hessian norm: // * current point (re-scaled) // * gradient of the target function (re-scaled, unmodified) // for(i_=0; i_<=n-1;i_++) { state.xk1[i_] = state.auloptimizer.x[i_]; } for(i_=0; i_<=n-1;i_++) { state.gk1[i_] = state.j[0,i_]; } // // Function being optimized // state.auloptimizer.f = state.fi[0]; for(i=0; i<=n-1; i++) { state.auloptimizer.g[i] = state.j[0,i]; } // // Send information to OptGuard monitor // optserv.smoothnessmonitorenqueuepoint(smonitor, state.auloptimizer.d, state.auloptimizer.stp, state.auloptimizer.x, state.fi, state.j, _params); // // Penalty for violation of boundary/linear/nonlinear constraints // penaltybc(state.auloptimizer.x, state.scaledbndl, state.hasbndl, state.scaledbndu, state.hasbndu, state.nubc, n, state.rho, state.stabilizingpoint, ref state.auloptimizer.f, state.auloptimizer.g, _params); penaltylc(state.auloptimizer.x, state.scaledcleic, state.nulc, n, nec, nic, state.rho, state.stabilizingpoint, ref state.auloptimizer.f, state.auloptimizer.g, _params); penaltynlc(state.fi, state.j, state.nunlc, n, ng, nh, state.rho, state.stabilizingpoint, ref state.auloptimizer.f, state.auloptimizer.g, _params); // // Forward termination request if needed // if( state.userterminationneeded ) { minlbfgs.minlbfgsrequesttermination(state.auloptimizer, _params); } // // To optimizer // goto lbl_6; lbl_8: if( !state.auloptimizer.xupdated ) { goto lbl_10; } // // Report current point (if needed) // if( !state.xrep ) { goto lbl_12; } for(i=0; i<=n-1; i++) { state.x[i] = state.auloptimizer.x[i]*state.s[i]; } state.f = state.auloptimizer.f; state.xupdated = true; state.rstateaul.stage = 1; goto lbl_rcomm; lbl_1: state.xupdated = false; lbl_12: // // Send information to OptGuard monitor // optserv.smoothnessmonitorfinalizelinesearch(smonitor, _params); optserv.smoothnessmonitorstartlinesearch(smonitor, state.auloptimizer.x, state.fi, state.j, _params); // // Forward termination request if needed // if( state.userterminationneeded ) { minlbfgs.minlbfgsrequesttermination(state.auloptimizer, _params); } // // Update constraints violation // optserv.checkbcviolation(state.hasbndl, state.scaledbndl, state.hasbndu, state.scaledbndu, state.auloptimizer.x, n, state.s, false, ref state.repbcerr, ref state.repbcidx, _params); optserv.checklcviolation(state.scaledcleic, state.lcsrcidx, nec, nic, state.auloptimizer.x, n, ref state.replcerr, ref state.replcidx, _params); optserv.checknlcviolation(state.fi, ng, nh, ref state.repnlcerr, ref state.repnlcidx, _params); // // Update GammaK // if( state.xkpresent ) { // // XK/GK store beginning of current line search, and XK1/GK1 // store data for the end of the line search: // * first, we Assert() that XK1 (last point where function // was evaluated) is same as AULOptimizer.X (what is // reported by RComm interface // * calculate step length V2. // // If V2>HessEstTol, then: // * calculate V0 - directional derivative at XK, // and V1 - directional derivative at XK1 // * set GammaK to Max(GammaK, |V1-V0|/V2) // for(i=0; i<=n-1; i++) { alglib.ap.assert((double)(Math.Abs(state.auloptimizer.x[i]-state.xk1[i]))<=(double)(100*math.machineepsilon) || !(math.isfinite(state.auloptimizer.x[i]) && math.isfinite(state.xk1[i])), "MinNLC: integrity check failed, unexpected behavior of LBFGS optimizer"); } v2 = 0.0; for(i=0; i<=n-1; i++) { v2 = v2+math.sqr(state.xk[i]-state.xk1[i]); } v2 = Math.Sqrt(v2); if( (double)(v2)>(double)(hessesttol) ) { v0 = 0.0; v1 = 0.0; for(i=0; i<=n-1; i++) { v = (state.xk[i]-state.xk1[i])/v2; v0 = v0+state.gk[i]*v; v1 = v1+state.gk1[i]*v; } state.gammak = Math.Max(state.gammak, Math.Abs(v1-v0)/v2); } } else { // // Beginning of the first line search, XK is not yet initialized. // for(i_=0; i_<=n-1;i_++) { state.xk[i_] = state.xk1[i_]; } for(i_=0; i_<=n-1;i_++) { state.gk[i_] = state.gk1[i_]; } state.xkpresent = true; } // // Update preconsitioner using current GammaK // updatepreconditioner(state.prectype, state.updatefreq, ref preccounter, state.auloptimizer, state.auloptimizer.x, state.rho, state.gammak, state.scaledbndl, state.hasbndl, state.scaledbndu, state.hasbndu, state.nubc, state.scaledcleic, state.nulc, state.fi, state.j, state.nunlc, ref state.bufd, ref state.bufc, ref state.bufw, ref state.bufz, ref state.tmp0, n, nec, nic, ng, nh, _params); goto lbl_6; lbl_10: alglib.ap.assert(false, "MinNLC: integrity check failed"); goto lbl_6; lbl_7: minlbfgs.minlbfgsresultsbuf(state.auloptimizer, ref state.xc, state.aulreport, _params); state.repinneriterationscount = state.repinneriterationscount+state.aulreport.iterationscount; state.repnfev = state.repnfev+state.aulreport.nfev; state.repterminationtype = state.aulreport.terminationtype; apserv.inc(ref state.repouteriterationscount, _params); if( state.repterminationtype<=0 || state.repterminationtype==8 ) { goto lbl_5; } // // 1. Evaluate F/J // 2. Check for NAN/INF in F/J: we just calculate sum of their // components, it should be enough to reduce vector/matrix to // just one value which either "normal" (all summands were "normal") // or NAN/INF (at least one summand was NAN/INF). // 3. Update Lagrange multipliers // for(i=0; i<=n-1; i++) { state.x[i] = state.xc[i]*state.s[i]; } state.needfij = true; state.rstateaul.stage = 2; goto lbl_rcomm; lbl_2: state.needfij = false; v = 0.0; for(i=0; i<=ng+nh; i++) { v = 0.1*v+state.fi[i]; for(j=0; j<=n-1; j++) { v = 0.1*v+state.j[i,j]; } } if( !math.isfinite(v) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; result = false; return result; } for(i=0; i<=ng+nh; i++) { for(j=0; j<=n-1; j++) { state.j[i,j] = state.j[i,j]*state.s[j]; } } for(i=0; i<=n-1; i++) { // // Process coefficients corresponding to equality-type // constraints. // if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { minnlcequalitypenaltyfunction((state.xc[i]-state.scaledbndl[i])*state.rho, ref p, ref dp, ref d2p, _params); state.nubc[2*i+0] = apserv.boundval(state.nubc[2*i+0]-dp, -maxlagmult, maxlagmult, _params); continue; } // // Process coefficients corresponding to inequality-type // constraints. These coefficients have limited growth/decay // per iteration which helps to stabilize algorithm. // alglib.ap.assert((double)(aulmaxgrowth)>(double)(1.0), "MinNLC: integrity error"); if( state.hasbndl[i] ) { minnlcinequalityshiftfunction((state.xc[i]-state.scaledbndl[i])*state.rho+1, ref p, ref dp, ref d2p, _params); v = Math.Abs(dp); v = Math.Min(v, aulmaxgrowth); v = Math.Max(v, 1/aulmaxgrowth); state.nubc[2*i+0] = apserv.boundval(state.nubc[2*i+0]*v, -maxlagmult, maxlagmult, _params); } if( state.hasbndu[i] ) { minnlcinequalityshiftfunction((state.scaledbndu[i]-state.xc[i])*state.rho+1, ref p, ref dp, ref d2p, _params); v = Math.Abs(dp); v = Math.Min(v, aulmaxgrowth); v = Math.Max(v, 1/aulmaxgrowth); state.nubc[2*i+1] = apserv.boundval(state.nubc[2*i+1]*v, -maxlagmult, maxlagmult, _params); } } for(i=0; i<=nec+nic-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.scaledcleic[i,i_]*state.xc[i_]; } v = v-state.scaledcleic[i,n]; if( i=scaledbndu[i] ) { xu[i] = state.bndu[i]; continue; } xu[i] = xs[i]*state.s[i]; if( state.hasbndl[i] && xu[i]state.bndu[i] ) { xu[i] = state.bndu[i]; } } } } public class minbc { /************************************************************************* This object stores nonlinear optimizer state. You should use functions provided by MinBC subpackage to work with this object *************************************************************************/ public class minbcstate : apobject { public int nmain; public double epsg; public double epsf; public double epsx; public int maxits; public bool xrep; public double stpmax; public double diffstep; public double[] s; public int prectype; public double[] diagh; public double[] x; public double f; public double[] g; public bool needf; public bool needfg; public bool xupdated; public bool userterminationneeded; public rcommstate rstate; public double[] xc; public double[] ugc; public double[] cgc; public double[] xn; public double[] ugn; public double[] cgn; public double[] xp; public double fc; public double fn; public double fp; public double[] d; public double lastscaledgoodstep; public bool[] hasbndl; public bool[] hasbndu; public double[] bndl; public double[] bndu; public int repiterationscount; public int repnfev; public int repvaridx; public int repterminationtype; public double[] xstart; public double fbase; public double fm2; public double fm1; public double fp1; public double fp2; public double xm1; public double xp1; public double gm1; public double gp1; public double[] tmpprec; public double[] tmp0; public int nfev; public int mcstage; public double stp; public double curstpmax; public double[] work; public linmin.linminstate lstate; public double trimthreshold; public int nonmonotoniccnt; public double[,] bufyk; public double[,] bufsk; public double[] bufrho; public double[] buftheta; public int bufsize; public double teststep; public int smoothnessguardlevel; public optserv.smoothnessmonitor smonitor; public double[] lastscaleused; public double[] invs; public minbcstate() { init(); } public override void init() { s = new double[0]; diagh = new double[0]; x = new double[0]; g = new double[0]; rstate = new rcommstate(); xc = new double[0]; ugc = new double[0]; cgc = new double[0]; xn = new double[0]; ugn = new double[0]; cgn = new double[0]; xp = new double[0]; d = new double[0]; hasbndl = new bool[0]; hasbndu = new bool[0]; bndl = new double[0]; bndu = new double[0]; xstart = new double[0]; tmpprec = new double[0]; tmp0 = new double[0]; work = new double[0]; lstate = new linmin.linminstate(); bufyk = new double[0,0]; bufsk = new double[0,0]; bufrho = new double[0]; buftheta = new double[0]; smonitor = new optserv.smoothnessmonitor(); lastscaleused = new double[0]; invs = new double[0]; } public override alglib.apobject make_copy() { minbcstate _result = new minbcstate(); _result.nmain = nmain; _result.epsg = epsg; _result.epsf = epsf; _result.epsx = epsx; _result.maxits = maxits; _result.xrep = xrep; _result.stpmax = stpmax; _result.diffstep = diffstep; _result.s = (double[])s.Clone(); _result.prectype = prectype; _result.diagh = (double[])diagh.Clone(); _result.x = (double[])x.Clone(); _result.f = f; _result.g = (double[])g.Clone(); _result.needf = needf; _result.needfg = needfg; _result.xupdated = xupdated; _result.userterminationneeded = userterminationneeded; _result.rstate = (rcommstate)rstate.make_copy(); _result.xc = (double[])xc.Clone(); _result.ugc = (double[])ugc.Clone(); _result.cgc = (double[])cgc.Clone(); _result.xn = (double[])xn.Clone(); _result.ugn = (double[])ugn.Clone(); _result.cgn = (double[])cgn.Clone(); _result.xp = (double[])xp.Clone(); _result.fc = fc; _result.fn = fn; _result.fp = fp; _result.d = (double[])d.Clone(); _result.lastscaledgoodstep = lastscaledgoodstep; _result.hasbndl = (bool[])hasbndl.Clone(); _result.hasbndu = (bool[])hasbndu.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.repiterationscount = repiterationscount; _result.repnfev = repnfev; _result.repvaridx = repvaridx; _result.repterminationtype = repterminationtype; _result.xstart = (double[])xstart.Clone(); _result.fbase = fbase; _result.fm2 = fm2; _result.fm1 = fm1; _result.fp1 = fp1; _result.fp2 = fp2; _result.xm1 = xm1; _result.xp1 = xp1; _result.gm1 = gm1; _result.gp1 = gp1; _result.tmpprec = (double[])tmpprec.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.nfev = nfev; _result.mcstage = mcstage; _result.stp = stp; _result.curstpmax = curstpmax; _result.work = (double[])work.Clone(); _result.lstate = (linmin.linminstate)lstate.make_copy(); _result.trimthreshold = trimthreshold; _result.nonmonotoniccnt = nonmonotoniccnt; _result.bufyk = (double[,])bufyk.Clone(); _result.bufsk = (double[,])bufsk.Clone(); _result.bufrho = (double[])bufrho.Clone(); _result.buftheta = (double[])buftheta.Clone(); _result.bufsize = bufsize; _result.teststep = teststep; _result.smoothnessguardlevel = smoothnessguardlevel; _result.smonitor = (optserv.smoothnessmonitor)smonitor.make_copy(); _result.lastscaleused = (double[])lastscaleused.Clone(); _result.invs = (double[])invs.Clone(); return _result; } }; /************************************************************************* This structure stores optimization report: * iterationscount number of iterations * nfev number of gradient evaluations * terminationtype termination type (see below) TERMINATION CODES terminationtype field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. -3 inconsistent constraints. 1 relative function improvement is no more than EpsF. 2 relative step is no more than EpsX. 4 gradient norm is no more than EpsG 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 terminated by user who called minbcrequesttermination(). X contains point which was "current accepted" when termination request was submitted. *************************************************************************/ public class minbcreport : apobject { public int iterationscount; public int nfev; public int varidx; public int terminationtype; public minbcreport() { init(); } public override void init() { } public override alglib.apobject make_copy() { minbcreport _result = new minbcreport(); _result.iterationscount = iterationscount; _result.nfev = nfev; _result.varidx = varidx; _result.terminationtype = terminationtype; return _result; } }; public const double gtol = 0.4; public const double maxnonmonotoniclen = 1.0E-5; public const double initialdecay = 0.5; public const double mindecay = 0.1; public const double decaycorrection = 0.8; /************************************************************************* BOX CONSTRAINED OPTIMIZATION WITH FAST ACTIVATION OF MULTIPLE BOX CONSTRAINTS DESCRIPTION: The subroutine minimizes function F(x) of N arguments subject to box constraints (with some of box constraints actually being equality ones). This optimizer uses algorithm similar to that of MinBLEIC (optimizer with general linear constraints), but presence of box-only constraints allows us to use faster constraint activation strategies. On large-scale problems, with multiple constraints active at the solution, this optimizer can be several times faster than BLEIC. REQUIREMENTS: * user must provide function value and gradient * starting point X0 must be feasible or not too far away from the feasible set * grad(f) must be Lipschitz continuous on a level set: L = { x : f(x)<=f(x0) } * function must be defined everywhere on the feasible set F USAGE: Constrained optimization if far more complex than the unconstrained one. Here we give very brief outline of the BC optimizer. We strongly recommend you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide on optimization, which is available at http://www.alglib.net/optimization/ 1. User initializes algorithm state with MinBCCreate() call 2. USer adds box constraints by calling MinBCSetBC() function. 3. User sets stopping conditions with MinBCSetCond(). 4. User calls MinBCOptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G. 5. User calls MinBCResults() to get solution 6. Optionally user may call MinBCRestartFrom() to solve another problem with same N but another starting point. MinBCRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size ofX X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. OUTPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbccreate(int n, double[] x, minbcstate state, alglib.xparams _params) { double[,] c = new double[0,0]; int[] ct = new int[0]; alglib.ap.assert(n>=1, "MinBCCreate: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinBCCreate: Length(X)0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[0..N-1]. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinBCSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large truncation errors, while too small step will result in too large numerical errors. 1.0E-6 can be good value to start with. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. CG needs exact gradient values. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 16.05.2011 by Bochkanov Sergey *************************************************************************/ public static void minbccreatef(int n, double[] x, double diffstep, minbcstate state, alglib.xparams _params) { double[,] c = new double[0,0]; int[] ct = new int[0]; alglib.ap.assert(n>=1, "MinBCCreateF: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinBCCreateF: Length(X)(double)(0), "MinBCCreateF: DiffStep is non-positive!"); minbcinitinternal(n, x, diffstep, state, _params); } /************************************************************************* This function sets boundary constraints for BC optimizer. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinBCRestartFrom(). INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: this solver has following useful properties: * bound constraints are always satisfied exactly * function is evaluated only INSIDE area specified by bound constraints, even when numerical differentiation is used (algorithm adjusts nodes according to boundary constraints) -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetbc(minbcstate state, double[] bndl, double[] bndu, alglib.xparams _params) { int i = 0; int n = 0; n = state.nmain; alglib.ap.assert(alglib.ap.len(bndl)>=n, "MinBCSetBC: Length(BndL)=n, "MinBCSetBC: Length(BndU)=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if on k+1-th iteration the condition |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinBCSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection. NOTE: when SetCond() called with non-zero MaxIts, BC solver may perform slightly more than MaxIts iterations. I.e., MaxIts sets non-strict limit on iterations count. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetcond(minbcstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsg), "MinBCSetCond: EpsG is not finite number"); alglib.ap.assert((double)(epsg)>=(double)(0), "MinBCSetCond: negative EpsG"); alglib.ap.assert(math.isfinite(epsf), "MinBCSetCond: EpsF is not finite number"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinBCSetCond: negative EpsF"); alglib.ap.assert(math.isfinite(epsx), "MinBCSetCond: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinBCSetCond: negative EpsX"); alglib.ap.assert(maxits>=0, "MinBCSetCond: negative MaxIts!"); if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxits==0 ) { epsx = 1.0E-6; } state.epsg = epsg; state.epsf = epsf; state.epsx = epsx; state.maxits = maxits; } /************************************************************************* This function sets scaling coefficients for BC optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. In most optimizers (and in the BC too) scaling is NOT a form of preconditioning. It just affects stopping conditions. You should set preconditioner by separate call to one of the MinBCSetPrec...() functions. There is a special preconditioning mode, however, which uses scaling coefficients to form diagonal preconditioning matrix. You can turn this mode on, if you want. But you should understand that scaling is not the same thing as preconditioning - these are two different, although related forms of tuning solver. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minbcsetscale(minbcstate state, double[] s, alglib.xparams _params) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.nmain, "MinBCSetScale: Length(S)=state.nmain, "MinBCSetPrecDiag: D is too short"); for(i=0; i<=state.nmain-1; i++) { alglib.ap.assert(math.isfinite(d[i]), "MinBCSetPrecDiag: D contains infinite or NAN elements"); alglib.ap.assert((double)(d[i])>(double)(0), "MinBCSetPrecDiag: D contains non-positive elements"); } apserv.rvectorsetlengthatleast(ref state.diagh, state.nmain, _params); state.prectype = 2; for(i=0; i<=state.nmain-1; i++) { state.diagh[i] = d[i]; } } /************************************************************************* Modification of the preconditioner: scale-based diagonal preconditioning. This preconditioning mode can be useful when you don't have approximate diagonal of Hessian, but you know that your variables are badly scaled (for example, one variable is in [1,10], and another in [1000,100000]), and most part of the ill-conditioning comes from different scales of vars. In this case simple scale-based preconditioner, with H[i] = 1/(s[i]^2), can greatly improve convergence. IMPRTANT: you should set scale of your variables with MinBCSetScale() call (before or after MinBCSetPrecScale() call). Without knowledge of the scale of your variables scale-based preconditioner will be just unit matrix. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetprecscale(minbcstate state, alglib.xparams _params) { state.prectype = 3; } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinBCOptimize(). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetxrep(minbcstate state, bool needxrep, alglib.xparams _params) { state.xrep = needxrep; } /************************************************************************* This function sets maximum step length INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which lead to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcsetstpmax(minbcstate state, double stpmax, alglib.xparams _params) { alglib.ap.assert(math.isfinite(stpmax), "MinBCSetStpMax: StpMax is not finite!"); alglib.ap.assert((double)(stpmax)>=(double)(0), "MinBCSetStpMax: StpMax<0!"); state.stpmax = stpmax; } /************************************************************************* NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied gradient, and one which uses function value only and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object (either MinBCCreate() for analytical gradient or MinBCCreateF() for numerical differentiation) you should choose appropriate variant of MinBCOptimize() - one which accepts function AND gradient or one which accepts function ONLY. Be careful to choose variant of MinBCOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinBCOptimize() and specific function used to create optimizer. | USER PASSED TO MinBCOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinBCCreateF() | works FAILS MinBCCreate() | FAILS works Here "FAIL" denotes inappropriate combinations of optimizer creation function and MinBCOptimize() version. Attemps to use such combination (for example, to create optimizer with MinBCCreateF() and to pass gradient information to MinCGOptimize()) will lead to exception being thrown. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static bool minbciteration(minbcstate state, alglib.xparams _params) { bool result = new bool(); int freezeidx = 0; double freezeval = 0; double scaleddnorm = 0; int n = 0; int m = 0; int i = 0; int j = 0; double v = 0; double vv = 0; double v0 = 0; bool b = new bool(); int mcinfo = 0; int itidx = 0; double ginit = 0; double gdecay = 0; bool activationstatus = new bool(); double activationstep = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { freezeidx = state.rstate.ia[0]; n = state.rstate.ia[1]; m = state.rstate.ia[2]; i = state.rstate.ia[3]; j = state.rstate.ia[4]; mcinfo = state.rstate.ia[5]; itidx = state.rstate.ia[6]; b = state.rstate.ba[0]; activationstatus = state.rstate.ba[1]; freezeval = state.rstate.ra[0]; scaleddnorm = state.rstate.ra[1]; v = state.rstate.ra[2]; vv = state.rstate.ra[3]; v0 = state.rstate.ra[4]; ginit = state.rstate.ra[5]; gdecay = state.rstate.ra[6]; activationstep = state.rstate.ra[7]; } else { freezeidx = 359; n = -58; m = -919; i = -909; j = 81; mcinfo = 255; itidx = 74; b = false; activationstatus = true; freezeval = 205; scaleddnorm = -838; v = 939; vv = -526; v0 = 763; ginit = -541; gdecay = -698; activationstep = -900; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } if( state.rstate.stage==4 ) { goto lbl_4; } if( state.rstate.stage==5 ) { goto lbl_5; } if( state.rstate.stage==6 ) { goto lbl_6; } if( state.rstate.stage==7 ) { goto lbl_7; } if( state.rstate.stage==8 ) { goto lbl_8; } if( state.rstate.stage==9 ) { goto lbl_9; } if( state.rstate.stage==10 ) { goto lbl_10; } if( state.rstate.stage==11 ) { goto lbl_11; } if( state.rstate.stage==12 ) { goto lbl_12; } if( state.rstate.stage==13 ) { goto lbl_13; } if( state.rstate.stage==14 ) { goto lbl_14; } if( state.rstate.stage==15 ) { goto lbl_15; } if( state.rstate.stage==16 ) { goto lbl_16; } if( state.rstate.stage==17 ) { goto lbl_17; } if( state.rstate.stage==18 ) { goto lbl_18; } if( state.rstate.stage==19 ) { goto lbl_19; } if( state.rstate.stage==20 ) { goto lbl_20; } if( state.rstate.stage==21 ) { goto lbl_21; } if( state.rstate.stage==22 ) { goto lbl_22; } if( state.rstate.stage==23 ) { goto lbl_23; } if( state.rstate.stage==24 ) { goto lbl_24; } if( state.rstate.stage==25 ) { goto lbl_25; } if( state.rstate.stage==26 ) { goto lbl_26; } if( state.rstate.stage==27 ) { goto lbl_27; } if( state.rstate.stage==28 ) { goto lbl_28; } if( state.rstate.stage==29 ) { goto lbl_29; } // // Routine body // // // Algorithm parameters: // * M number of L-BFGS corrections. // This coefficient remains fixed during iterations. // * GDecay desired decrease of constrained gradient during L-BFGS iterations. // This coefficient is decreased after each L-BFGS round until // it reaches minimum decay. // m = Math.Min(5, state.nmain); gdecay = initialdecay; // // Init // n = state.nmain; for(i=0; i<=n-1; i++) { state.xc[i] = state.xstart[i]; } if( !optserv.enforceboundaryconstraints(state.xc, state.bndl, state.hasbndl, state.bndu, state.hasbndu, n, 0, _params) ) { // // Inconsistent constraints // state.repterminationtype = -3; result = false; return result; } state.userterminationneeded = false; state.repterminationtype = 0; state.repiterationscount = 0; state.repnfev = 0; state.repvaridx = -1; apserv.rmatrixsetlengthatleast(ref state.bufyk, m+1, n, _params); apserv.rmatrixsetlengthatleast(ref state.bufsk, m+1, n, _params); apserv.rvectorsetlengthatleast(ref state.bufrho, m, _params); apserv.rvectorsetlengthatleast(ref state.buftheta, m, _params); apserv.rvectorsetlengthatleast(ref state.tmp0, n, _params); optserv.smoothnessmonitorinit(state.smonitor, n, 1, state.smoothnessguardlevel>0, _params); for(i=0; i<=n-1; i++) { state.lastscaleused[i] = state.s[i]; state.invs[i] = 1/state.s[i]; } // // Fill TmpPrec with current preconditioner // apserv.rvectorsetlengthatleast(ref state.tmpprec, n, _params); for(i=0; i<=n-1; i++) { if( state.prectype==2 ) { state.tmpprec[i] = 1/state.diagh[i]; continue; } if( state.prectype==3 ) { state.tmpprec[i] = math.sqr(state.s[i]); continue; } state.tmpprec[i] = 1; } // // Check correctness of user-supplied gradient // clearrequestfields(state, _params); if( !((double)(state.diffstep)==(double)(0) && (double)(state.teststep)>(double)(0)) ) { goto lbl_30; } lbl_32: if( !optserv.smoothnessmonitorcheckgradientatx0(state.smonitor, state.xc, state.s, state.bndl, state.bndu, true, state.teststep, _params) ) { goto lbl_33; } for(i=0; i<=n-1; i++) { state.x[i] = state.smonitor.x[i]; } state.needfg = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needfg = false; state.smonitor.fi[0] = state.f; for(i=0; i<=n-1; i++) { state.smonitor.j[0,i] = state.g[i]; } goto lbl_32; lbl_33: lbl_30: // // Main cycle of BC-PG algorithm // state.repterminationtype = 0; state.lastscaledgoodstep = 0; state.nonmonotoniccnt = (int)Math.Round(1.5*n)+5; for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xc[i_]; } clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_34; } state.needfg = true; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: state.needfg = false; goto lbl_35; lbl_34: state.needf = true; state.rstate.stage = 2; goto lbl_rcomm; lbl_2: state.needf = false; lbl_35: state.fc = state.f; optserv.trimprepare(state.f, ref state.trimthreshold, _params); state.repnfev = state.repnfev+1; if( !state.xrep ) { goto lbl_36; } // // Report current point // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xc[i_]; } state.f = state.fc; state.xupdated = true; state.rstate.stage = 3; goto lbl_rcomm; lbl_3: state.xupdated = false; lbl_36: if( state.userterminationneeded ) { // // User requested termination // state.repterminationtype = 8; result = false; return result; } lbl_38: if( false ) { goto lbl_39; } // // Steepest descent phase // // (a) calculate unconstrained gradient // (b) check F/G for NAN/INF, abnormally terminate algorithm if needed // (c) perform one steepest descent step, activating only those constraints // which prevent us from moving outside of box-constrained area // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xc[i_]; } clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_40; } // // Analytic gradient // state.needfg = true; state.rstate.stage = 4; goto lbl_rcomm; lbl_4: state.needfg = false; goto lbl_41; lbl_40: // // Numerical differentiation // state.needf = true; state.rstate.stage = 5; goto lbl_rcomm; lbl_5: state.fbase = state.f; i = 0; lbl_42: if( i>n-1 ) { goto lbl_44; } v = state.x[i]; b = false; if( state.hasbndl[i] ) { b = b || (double)(v-state.diffstep*state.s[i])<(double)(state.bndl[i]); } if( state.hasbndu[i] ) { b = b || (double)(v+state.diffstep*state.s[i])>(double)(state.bndu[i]); } if( b ) { goto lbl_45; } state.x[i] = v-state.diffstep*state.s[i]; state.rstate.stage = 6; goto lbl_rcomm; lbl_6: state.fm2 = state.f; state.x[i] = v-0.5*state.diffstep*state.s[i]; state.rstate.stage = 7; goto lbl_rcomm; lbl_7: state.fm1 = state.f; state.x[i] = v+0.5*state.diffstep*state.s[i]; state.rstate.stage = 8; goto lbl_rcomm; lbl_8: state.fp1 = state.f; state.x[i] = v+state.diffstep*state.s[i]; state.rstate.stage = 9; goto lbl_rcomm; lbl_9: state.fp2 = state.f; state.g[i] = (8*(state.fp1-state.fm1)-(state.fp2-state.fm2))/(6*state.diffstep*state.s[i]); goto lbl_46; lbl_45: state.xm1 = v-state.diffstep*state.s[i]; state.xp1 = v+state.diffstep*state.s[i]; if( state.hasbndl[i] && (double)(state.xm1)<(double)(state.bndl[i]) ) { state.xm1 = state.bndl[i]; } if( state.hasbndu[i] && (double)(state.xp1)>(double)(state.bndu[i]) ) { state.xp1 = state.bndu[i]; } state.x[i] = state.xm1; state.rstate.stage = 10; goto lbl_rcomm; lbl_10: state.fm1 = state.f; state.x[i] = state.xp1; state.rstate.stage = 11; goto lbl_rcomm; lbl_11: state.fp1 = state.f; if( (double)(state.xm1)!=(double)(state.xp1) ) { state.g[i] = (state.fp1-state.fm1)/(state.xp1-state.xm1); } else { state.g[i] = 0; } lbl_46: state.x[i] = v; i = i+1; goto lbl_42; lbl_44: state.f = state.fbase; state.needf = false; lbl_41: state.fc = state.f; for(i_=0; i_<=n-1;i_++) { state.ugc[i_] = state.g[i_]; } for(i_=0; i_<=n-1;i_++) { state.cgc[i_] = state.g[i_]; } optserv.projectgradientintobc(state.xc, ref state.cgc, state.bndl, state.hasbndl, state.bndu, state.hasbndu, n, 0, _params); ginit = 0.0; for(i=0; i<=n-1; i++) { ginit = ginit+math.sqr(state.cgc[i]*state.s[i]); } ginit = Math.Sqrt(ginit); if( !math.isfinite(ginit) || !math.isfinite(state.fc) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; result = false; return result; } if( state.userterminationneeded ) { // // User requested termination // state.repterminationtype = 8; result = false; return result; } if( (double)(ginit)<=(double)(state.epsg) ) { // // Gradient is small enough. // Optimization is terminated // state.repterminationtype = 4; result = false; return result; } for(i=0; i<=n-1; i++) { state.d[i] = -(state.tmpprec[i]*state.cgc[i]); } scaleddnorm = 0; for(i=0; i<=n-1; i++) { scaleddnorm = scaleddnorm+math.sqr(state.d[i]/state.s[i]); } scaleddnorm = Math.Sqrt(scaleddnorm); alglib.ap.assert((double)(scaleddnorm)>(double)(0), "MinBC: integrity check failed"); if( (double)(state.lastscaledgoodstep)>(double)(0) ) { state.stp = state.lastscaledgoodstep/scaleddnorm; } else { state.stp = 1.0/scaleddnorm; } optserv.calculatestepbound(state.xc, state.d, 1.0, state.bndl, state.hasbndl, state.bndu, state.hasbndu, n, 0, ref freezeidx, ref freezeval, ref state.curstpmax, _params); activationstep = state.curstpmax; if( freezeidx<0 || (double)(state.curstpmax)>(double)(1.0E50) ) { state.curstpmax = 1.0E50; } if( (double)(state.stpmax)>(double)(0) ) { state.curstpmax = Math.Min(state.curstpmax, state.stpmax/scaleddnorm); } for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.cgn[i_] = state.cgc[i_]; } for(i_=0; i_<=n-1;i_++) { state.ugn[i_] = state.ugc[i_]; } state.fn = state.fc; state.mcstage = 0; optserv.smoothnessmonitorstartlinesearch1u(state.smonitor, state.s, state.invs, state.xn, state.fn, state.ugn, _params); linmin.mcsrch(n, ref state.xn, ref state.fn, ref state.cgn, state.d, ref state.stp, state.curstpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); lbl_47: if( state.mcstage==0 ) { goto lbl_48; } // // Copy XN to X, perform on-the-fly correction w.r.t box // constraints (projection onto feasible set). // for(i=0; i<=n-1; i++) { state.x[i] = state.xn[i]; if( state.hasbndl[i] && (double)(state.xn[i])<(double)(state.bndl[i]) ) { state.x[i] = state.bndl[i]; } if( state.hasbndu[i] && (double)(state.xn[i])>(double)(state.bndu[i]) ) { state.x[i] = state.bndu[i]; } } // // Gradient, either user-provided or numerical differentiation // clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_49; } // // Analytic gradient // state.needfg = true; state.rstate.stage = 12; goto lbl_rcomm; lbl_12: state.needfg = false; state.repnfev = state.repnfev+1; goto lbl_50; lbl_49: // // Numerical differentiation // state.needf = true; state.rstate.stage = 13; goto lbl_rcomm; lbl_13: state.fbase = state.f; i = 0; lbl_51: if( i>n-1 ) { goto lbl_53; } v = state.x[i]; b = false; if( state.hasbndl[i] ) { b = b || (double)(v-state.diffstep*state.s[i])<(double)(state.bndl[i]); } if( state.hasbndu[i] ) { b = b || (double)(v+state.diffstep*state.s[i])>(double)(state.bndu[i]); } if( b ) { goto lbl_54; } state.x[i] = v-state.diffstep*state.s[i]; state.rstate.stage = 14; goto lbl_rcomm; lbl_14: state.fm2 = state.f; state.x[i] = v-0.5*state.diffstep*state.s[i]; state.rstate.stage = 15; goto lbl_rcomm; lbl_15: state.fm1 = state.f; state.x[i] = v+0.5*state.diffstep*state.s[i]; state.rstate.stage = 16; goto lbl_rcomm; lbl_16: state.fp1 = state.f; state.x[i] = v+state.diffstep*state.s[i]; state.rstate.stage = 17; goto lbl_rcomm; lbl_17: state.fp2 = state.f; state.g[i] = (8*(state.fp1-state.fm1)-(state.fp2-state.fm2))/(6*state.diffstep*state.s[i]); state.repnfev = state.repnfev+4; goto lbl_55; lbl_54: state.xm1 = v-state.diffstep*state.s[i]; state.xp1 = v+state.diffstep*state.s[i]; if( state.hasbndl[i] && (double)(state.xm1)<(double)(state.bndl[i]) ) { state.xm1 = state.bndl[i]; } if( state.hasbndu[i] && (double)(state.xp1)>(double)(state.bndu[i]) ) { state.xp1 = state.bndu[i]; } state.x[i] = state.xm1; state.rstate.stage = 18; goto lbl_rcomm; lbl_18: state.fm1 = state.f; state.x[i] = state.xp1; state.rstate.stage = 19; goto lbl_rcomm; lbl_19: state.fp1 = state.f; if( (double)(state.xm1)!=(double)(state.xp1) ) { state.g[i] = (state.fp1-state.fm1)/(state.xp1-state.xm1); } else { state.g[i] = 0; } state.repnfev = state.repnfev+2; lbl_55: state.x[i] = v; i = i+1; goto lbl_51; lbl_53: state.f = state.fbase; state.needf = false; lbl_50: // // Back to MCSRCH // optserv.smoothnessmonitorenqueuepoint1u(state.smonitor, state.s, state.invs, state.d, state.stp, state.x, state.f, state.g, _params); optserv.trimfunction(ref state.f, ref state.g, n, state.trimthreshold, _params); state.fn = state.f; for(i_=0; i_<=n-1;i_++) { state.cgn[i_] = state.g[i_]; } for(i_=0; i_<=n-1;i_++) { state.ugn[i_] = state.g[i_]; } for(i=0; i<=n-1; i++) { if( (double)(state.d[i])==(double)(0) ) { state.cgn[i] = 0; } } linmin.mcsrch(n, ref state.xn, ref state.fn, ref state.cgn, state.d, ref state.stp, state.curstpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); goto lbl_47; lbl_48: optserv.smoothnessmonitorfinalizelinesearch(state.smonitor, _params); v = state.fn; for(i=0; i<=n-1; i++) { v = 0.1*v+state.ugn[i]; } if( !math.isfinite(v) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; result = false; return result; } if( mcinfo!=1 && mcinfo!=5 ) { // // We can not find step which decreases function value. We have // two possibilities: // (a) numerical properties of the function do not allow us to // find good step. // (b) we are close to activation of some constraint, and it is // so close that step which activates it leads to change in // target function which is smaller than numerical noise. // // Optimization algorithm must be able to handle case (b), because // inability to handle it will cause failure when algorithm // started very close to boundary of the feasible area. // // In order to correctly handle such cases we allow limited amount // of small steps which increase function value. // if( (freezeidx>=0 && (double)(scaleddnorm*state.curstpmax)<=(double)(maxnonmonotoniclen)) && state.nonmonotoniccnt>0 ) { // // We enforce non-monotonic step: // * Stp := CurStpMax // * MCINFO := 5 // * XN := XC+CurStpMax*D // * non-monotonic counter is decreased // // NOTE: UGN/CGN are not updated because step is so short that we assume that // GN is approximately equal to GC. // state.stp = state.curstpmax; mcinfo = 5; v = state.curstpmax; for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xn[i_] + v*state.d[i_]; } state.nonmonotoniccnt = state.nonmonotoniccnt-1; } else { // // Numerical properties of the function does not allow // us to solve problem. Algorithm is terminated // state.repterminationtype = 7; result = false; return result; } } if( state.userterminationneeded ) { // // User requested termination // state.repterminationtype = 8; result = false; return result; } alglib.ap.assert(mcinfo!=5 || (double)(state.stp)==(double)(state.curstpmax), "MinBC: integrity check failed"); optserv.postprocessboundedstep(ref state.xn, state.xc, state.bndl, state.hasbndl, state.bndu, state.hasbndu, n, 0, freezeidx, freezeval, state.stp, activationstep, _params); state.fp = state.fc; state.fc = state.fn; for(i_=0; i_<=n-1;i_++) { state.xp[i_] = state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.xn[i_]; } for(i_=0; i_<=n-1;i_++) { state.cgc[i_] = state.cgn[i_]; } for(i_=0; i_<=n-1;i_++) { state.ugc[i_] = state.ugn[i_]; } if( !state.xrep ) { goto lbl_56; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xc[i_]; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 20; goto lbl_rcomm; lbl_20: state.xupdated = false; lbl_56: state.repiterationscount = state.repiterationscount+1; if( mcinfo==1 ) { v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr((state.xc[i]-state.xp[i])/state.s[i]); } v = Math.Sqrt(v); if( (double)(v)<=(double)(state.epsx) ) { // // Step is small enough // state.repterminationtype = 2; result = false; return result; } if( (double)(Math.Abs(state.fp-state.fc))<=(double)(state.epsf*Math.Max(Math.Abs(state.fc), Math.Max(Math.Abs(state.fp), 1.0))) ) { // // Function change is small enough // state.repterminationtype = 1; result = false; return result; } } if( state.maxits>0 && state.repiterationscount>=state.maxits ) { // // Iteration counter exceeded limit // state.repterminationtype = 5; result = false; return result; } // // LBFGS stage: // * during LBFGS iterations we activate new constraints, but never // deactivate already active ones. // * we perform at most N iterations of LBFGS before re-evaluating // active set and restarting LBFGS. // // About termination: // * LBFGS iterations can be terminated because of two reasons: // * "termination" - non-zero termination code in RepTerminationType, // which means that optimization is done // * "restart" - zero RepTerminationType, which means that we // have to re-evaluate active set and resume LBFGS stage. // * one more option is "refresh" - to continue LBFGS iterations, // but with all BFGS updates (Sk/Yk pairs) being dropped; // it happens after changes in active set // ginit = 0.0; for(i=0; i<=n-1; i++) { state.cgc[i] = state.ugc[i]; if( state.hasbndl[i] && (double)(state.xc[i])==(double)(state.bndl[i]) ) { state.cgc[i] = 0; } if( state.hasbndu[i] && (double)(state.xc[i])==(double)(state.bndu[i]) ) { state.cgc[i] = 0; } ginit = ginit+math.sqr(state.cgc[i]*state.s[i]); } ginit = Math.Sqrt(ginit); state.bufsize = 0; itidx = 0; lbl_58: if( itidx>n-1 ) { goto lbl_60; } // // At the beginning of each iteration: // * XC stores current point // * FC stores current function value // * UGC stores current unconstrained gradient // * CGC stores current constrained gradient // * D stores constrained step direction (calculated at this block) // // 1. Calculate search direction D according to L-BFGS algorithm // using constrained preconditioner to perform inner multiplication. // 2. Evaluate scaled length of direction D; restart LBFGS if D is zero // (it may be possible that we found minimum, but it is also possible // that some constraints need deactivation) // 3. If D is non-zero, try to use previous scaled step length as initial estimate for new step. // 4. Calculate bound on step length. // for(i_=0; i_<=n-1;i_++) { state.work[i_] = state.cgc[i_]; } for(i=state.bufsize-1; i>=0; i--) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.bufsk[i,i_]*state.work[i_]; } state.buftheta[i] = v; vv = v*state.bufrho[i]; for(i_=0; i_<=n-1;i_++) { state.work[i_] = state.work[i_] - vv*state.bufyk[i,i_]; } } for(i=0; i<=n-1; i++) { state.work[i] = state.tmpprec[i]*state.work[i]; } for(i=0; i<=state.bufsize-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.bufyk[i,i_]*state.work[i_]; } vv = state.bufrho[i]*(-v+state.buftheta[i]); for(i_=0; i_<=n-1;i_++) { state.work[i_] = state.work[i_] + vv*state.bufsk[i,i_]; } } for(i_=0; i_<=n-1;i_++) { state.d[i_] = -state.work[i_]; } b = false; for(i=0; i<=n-1; i++) { b = b || ((state.hasbndl[i] && (double)(state.xc[i])==(double)(state.bndl[i])) && (double)(state.d[i])!=(double)(0)); b = b || ((state.hasbndu[i] && (double)(state.xc[i])==(double)(state.bndu[i])) && (double)(state.d[i])!=(double)(0)); } alglib.ap.assert(!b, "MinBC: integrity check failed (q)"); scaleddnorm = 0; for(i=0; i<=n-1; i++) { scaleddnorm = scaleddnorm+math.sqr(state.d[i]/state.s[i]); } scaleddnorm = Math.Sqrt(scaleddnorm); if( (double)(scaleddnorm)==(double)(0) ) { // // Search direction is zero. // Skip back to steepest descent phase. // goto lbl_60; } if( (double)(state.lastscaledgoodstep)>(double)(0) ) { state.stp = state.lastscaledgoodstep/scaleddnorm; } else { state.stp = 1.0/scaleddnorm; } state.curstpmax = 1.0E50; if( (double)(state.stpmax)>(double)(0) ) { state.curstpmax = Math.Min(state.curstpmax, state.stpmax/scaleddnorm); } // // Minimize G(t) = F(CONSTRAIN(XC + t*D)), with t being scalar, XC and D being vectors. // for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.cgn[i_] = state.cgc[i_]; } for(i_=0; i_<=n-1;i_++) { state.ugn[i_] = state.ugc[i_]; } state.fn = state.fc; state.mcstage = 0; optserv.smoothnessmonitorstartlinesearch1u(state.smonitor, state.s, state.invs, state.xn, state.fn, state.ugn, _params); linmin.mcsrch(n, ref state.xn, ref state.fn, ref state.cgn, state.d, ref state.stp, state.curstpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); lbl_61: if( state.mcstage==0 ) { goto lbl_62; } // // Copy XN to X, perform on-the-fly correction w.r.t box // constraints (projection onto feasible set). // for(i=0; i<=n-1; i++) { state.x[i] = state.xn[i]; if( state.hasbndl[i] && (double)(state.xn[i])<=(double)(state.bndl[i]) ) { state.x[i] = state.bndl[i]; } if( state.hasbndu[i] && (double)(state.xn[i])>=(double)(state.bndu[i]) ) { state.x[i] = state.bndu[i]; } } // // Gradient, either user-provided or numerical differentiation // clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_63; } // // Analytic gradient // state.needfg = true; state.rstate.stage = 21; goto lbl_rcomm; lbl_21: state.needfg = false; state.repnfev = state.repnfev+1; goto lbl_64; lbl_63: // // Numerical differentiation // state.needf = true; state.rstate.stage = 22; goto lbl_rcomm; lbl_22: state.fbase = state.f; i = 0; lbl_65: if( i>n-1 ) { goto lbl_67; } v = state.x[i]; b = false; if( state.hasbndl[i] ) { b = b || (double)(v-state.diffstep*state.s[i])<(double)(state.bndl[i]); } if( state.hasbndu[i] ) { b = b || (double)(v+state.diffstep*state.s[i])>(double)(state.bndu[i]); } if( b ) { goto lbl_68; } state.x[i] = v-state.diffstep*state.s[i]; state.rstate.stage = 23; goto lbl_rcomm; lbl_23: state.fm2 = state.f; state.x[i] = v-0.5*state.diffstep*state.s[i]; state.rstate.stage = 24; goto lbl_rcomm; lbl_24: state.fm1 = state.f; state.x[i] = v+0.5*state.diffstep*state.s[i]; state.rstate.stage = 25; goto lbl_rcomm; lbl_25: state.fp1 = state.f; state.x[i] = v+state.diffstep*state.s[i]; state.rstate.stage = 26; goto lbl_rcomm; lbl_26: state.fp2 = state.f; state.g[i] = (8*(state.fp1-state.fm1)-(state.fp2-state.fm2))/(6*state.diffstep*state.s[i]); state.repnfev = state.repnfev+4; goto lbl_69; lbl_68: state.xm1 = v-state.diffstep*state.s[i]; state.xp1 = v+state.diffstep*state.s[i]; if( state.hasbndl[i] && (double)(state.xm1)<(double)(state.bndl[i]) ) { state.xm1 = state.bndl[i]; } if( state.hasbndu[i] && (double)(state.xp1)>(double)(state.bndu[i]) ) { state.xp1 = state.bndu[i]; } state.x[i] = state.xm1; state.rstate.stage = 27; goto lbl_rcomm; lbl_27: state.fm1 = state.f; state.x[i] = state.xp1; state.rstate.stage = 28; goto lbl_rcomm; lbl_28: state.fp1 = state.f; if( (double)(state.xm1)!=(double)(state.xp1) ) { state.g[i] = (state.fp1-state.fm1)/(state.xp1-state.xm1); } else { state.g[i] = 0; } state.repnfev = state.repnfev+2; lbl_69: state.x[i] = v; i = i+1; goto lbl_65; lbl_67: state.f = state.fbase; state.needf = false; lbl_64: // // Back to MCSRCH // optserv.smoothnessmonitorenqueuepoint1u(state.smonitor, state.s, state.invs, state.d, state.stp, state.x, state.f, state.g, _params); optserv.trimfunction(ref state.f, ref state.g, n, state.trimthreshold, _params); state.fn = state.f; for(i=0; i<=n-1; i++) { state.ugn[i] = state.g[i]; state.cgn[i] = state.g[i]; if( state.hasbndl[i] && (double)(state.xn[i])<=(double)(state.bndl[i]) ) { state.cgn[i] = 0; } if( state.hasbndu[i] && (double)(state.xn[i])>=(double)(state.bndu[i]) ) { state.cgn[i] = 0; } } linmin.mcsrch(n, ref state.xn, ref state.fn, ref state.cgn, state.d, ref state.stp, state.curstpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); goto lbl_61; lbl_62: optserv.smoothnessmonitorfinalizelinesearch(state.smonitor, _params); for(i=0; i<=n-1; i++) { if( state.hasbndl[i] && (double)(state.xn[i])<=(double)(state.bndl[i]) ) { state.xn[i] = state.bndl[i]; } if( state.hasbndu[i] && (double)(state.xn[i])>=(double)(state.bndu[i]) ) { state.xn[i] = state.bndu[i]; } } for(i_=0; i_<=n-1;i_++) { state.bufsk[state.bufsize,i_] = -state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.bufyk[state.bufsize,i_] = -state.cgc[i_]; } for(i_=0; i_<=n-1;i_++) { state.bufsk[state.bufsize,i_] = state.bufsk[state.bufsize,i_] + state.xn[i_]; } for(i_=0; i_<=n-1;i_++) { state.bufyk[state.bufsize,i_] = state.bufyk[state.bufsize,i_] + state.cgn[i_]; } // // Handle special situations: // * check for presence of NAN/INF in function/gradient // * handle failure of line search // v = state.fn; for(i=0; i<=n-1; i++) { v = 0.1*v+state.ugn[i]; } if( !math.isfinite(v) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; result = false; return result; } if( state.userterminationneeded ) { // // User requested termination // state.repterminationtype = 8; result = false; return result; } if( mcinfo!=1 ) { // // Terminate LBFGS phase // goto lbl_60; } // // Current point is updated: // * move XC/FC/GC to XP/FP/GP // * move XN/FN/GN to XC/FC/GC // * report current point and update iterations counter // * push new pair SK/YK to LBFGS buffer // * update length of the good step // activationstatus = false; for(i=0; i<=n-1; i++) { if( (state.hasbndl[i] && (double)(state.xn[i])==(double)(state.bndl[i])) && (double)(state.xn[i])!=(double)(state.xc[i]) ) { activationstatus = true; } if( (state.hasbndu[i] && (double)(state.xn[i])==(double)(state.bndu[i])) && (double)(state.xn[i])!=(double)(state.xc[i]) ) { activationstatus = true; } } state.fp = state.fc; state.fc = state.fn; for(i_=0; i_<=n-1;i_++) { state.xp[i_] = state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.xn[i_]; } for(i_=0; i_<=n-1;i_++) { state.cgc[i_] = state.cgn[i_]; } for(i_=0; i_<=n-1;i_++) { state.ugc[i_] = state.ugn[i_]; } if( !state.xrep ) { goto lbl_70; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xc[i_]; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 29; goto lbl_rcomm; lbl_29: state.xupdated = false; lbl_70: state.repiterationscount = state.repiterationscount+1; if( state.bufsize==m ) { // // Buffer is full, shift contents by one row // for(i=0; i<=state.bufsize-1; i++) { for(i_=0; i_<=n-1;i_++) { state.bufsk[i,i_] = state.bufsk[i+1,i_]; } for(i_=0; i_<=n-1;i_++) { state.bufyk[i,i_] = state.bufyk[i+1,i_]; } } for(i=0; i<=state.bufsize-2; i++) { state.bufrho[i] = state.bufrho[i+1]; state.buftheta[i] = state.buftheta[i+1]; } } else { // // Buffer is not full, increase buffer size by 1 // state.bufsize = state.bufsize+1; } v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.bufyk[state.bufsize-1,i_]*state.bufsk[state.bufsize-1,i_]; } vv = 0.0; for(i_=0; i_<=n-1;i_++) { vv += state.bufyk[state.bufsize-1,i_]*state.bufyk[state.bufsize-1,i_]; } if( (double)(v)==(double)(0) || (double)(vv)==(double)(0) ) { // // Strange internal error in LBFGS - either YK=0 // (which should not have been) or (SK,YK)=0 (again, // unexpected). It should not take place because // MCINFO=1, which signals "good" step. But just // to be sure we have special branch of code which // restarts LBFGS // goto lbl_60; } state.bufrho[state.bufsize-1] = 1/v; alglib.ap.assert(state.bufsize<=m, "MinBC: internal error"); v = 0; vv = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr((state.xc[i]-state.xp[i])/state.s[i]); vv = vv+math.sqr(state.xc[i]-state.xp[i]); } updateestimateofgoodstep(ref state.lastscaledgoodstep, Math.Sqrt(v), _params); // // Check MaxIts-based stopping condition. // if( state.maxits>0 && state.repiterationscount>=state.maxits ) { state.repterminationtype = 5; result = false; return result; } // // Smooth reset (LBFGS memory model is refreshed) or hard restart: // * LBFGS model is refreshed, if line search was performed with activation of constraints // * algorithm is restarted if scaled gradient decreased below GDecay // if( activationstatus ) { state.bufsize = 0; goto lbl_59; } v = 0.0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.cgc[i]*state.s[i]); } if( (double)(Math.Sqrt(v))<(double)(gdecay*ginit) ) { goto lbl_60; } lbl_59: itidx = itidx+1; goto lbl_58; lbl_60: // // Decrease decay coefficient. Subsequent L-BFGS stages will // have more stringent stopping criteria. // gdecay = Math.Max(gdecay*decaycorrection, mindecay); goto lbl_38; lbl_39: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = freezeidx; state.rstate.ia[1] = n; state.rstate.ia[2] = m; state.rstate.ia[3] = i; state.rstate.ia[4] = j; state.rstate.ia[5] = mcinfo; state.rstate.ia[6] = itidx; state.rstate.ba[0] = b; state.rstate.ba[1] = activationstatus; state.rstate.ra[0] = freezeval; state.rstate.ra[1] = scaleddnorm; state.rstate.ra[2] = v; state.rstate.ra[3] = vv; state.rstate.ra[4] = v0; state.rstate.ra[5] = ginit; state.rstate.ra[6] = gdecay; state.rstate.ra[7] = activationstep; return result; } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with minbcoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minbcsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardgradient(minbcstate state, double teststep, alglib.xparams _params) { alglib.ap.assert(math.isfinite(teststep), "MinBCOptGuardGradient: TestStep contains NaN or INF"); alglib.ap.assert((double)(teststep)>=(double)(0), "MinBCOptGuardGradient: invalid argument TestStep(TestStep<0)"); state.teststep = teststep; } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) b) nonsmooth target function (non-C1) Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardsmoothness(minbcstate state, int level, alglib.xparams _params) { alglib.ap.assert(level==0 || level==1, "MinBCOptGuardSmoothness: unexpected value of level parameter"); state.smoothnessguardlevel = level; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * minbcoptguardgradient() for gradient verification * minbcoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradvidx for specific variable (gradient element) suspected * rep.badgradxbase, a point where gradient is tested * rep.badgraduser, user-provided gradient (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.badgradnum, reference gradient obtained via numerical differentiation (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.nonc0suspected * rep.nonc1suspected === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * minbcoptguardnonc1test0results() * minbcoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardresults(minbcstate state, optguardapi.optguardreport rep, alglib.xparams _params) { optserv.smoothnessmonitorexportreport(state.smonitor, rep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardnonc1test0results(minbcstate state, optguardapi.optguardnonc1test0report strrep, optguardapi.optguardnonc1test0report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minbcoptguardnonc1test1results(minbcstate state, optguardapi.optguardnonc1test1report strrep, optguardapi.optguardnonc1test1report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* BC results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report. You should check Rep.TerminationType in order to distinguish successful termination from unsuccessful one: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -3 inconsistent constraints. * 1 relative function improvement is no more than EpsF. * 2 scaled step is no more than EpsX. * 4 scaled gradient norm is no more than EpsG. * 5 MaxIts steps was taken * 8 terminated by user who called minbcrequesttermination(). X contains point which was "current accepted" when termination request was submitted. More information about fields of this structure can be found in the comments on MinBCReport datatype. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcresults(minbcstate state, ref double[] x, minbcreport rep, alglib.xparams _params) { x = new double[0]; minbcresultsbuf(state, ref x, rep, _params); } /************************************************************************* BC results Buffered implementation of MinBCResults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcresultsbuf(minbcstate state, ref double[] x, minbcreport rep, alglib.xparams _params) { int i = 0; int i_ = 0; if( alglib.ap.len(x)0 ) { for(i_=0; i_<=state.nmain-1;i_++) { x[i_] = state.xc[i_]; } } else { for(i=0; i<=state.nmain-1; i++) { x[i] = Double.NaN; } } } /************************************************************************* This subroutine restarts algorithm from new point. All optimization parameters (including constraints) are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure previously allocated with MinBCCreate call. X - new starting point. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbcrestartfrom(minbcstate state, double[] x, alglib.xparams _params) { int n = 0; int i_ = 0; n = state.nmain; // // First, check for errors in the inputs // alglib.ap.assert(alglib.ap.len(x)>=n, "MinBCRestartFrom: Length(X)(double)(estimate*100) ) { estimate = estimate*100; return; } estimate = newstep; } } public class minns { /************************************************************************* This object stores temporaries for internal QP solver. *************************************************************************/ public class minnsqp : apobject { public double fc; public double fn; public double[] xc; public double[] xn; public double[] x0; public double[] gc; public double[] d; public double[,] uh; public double[,] ch; public double[,] rk; public double[] invutc; public double[] tmp0; public int[] tmpidx; public double[] tmpd; public double[] tmpc; public double[] tmplambdas; public double[,] tmpc2; public bool[] tmpb; public snnls.snnlssolver nnls; public minnsqp() { init(); } public override void init() { xc = new double[0]; xn = new double[0]; x0 = new double[0]; gc = new double[0]; d = new double[0]; uh = new double[0,0]; ch = new double[0,0]; rk = new double[0,0]; invutc = new double[0]; tmp0 = new double[0]; tmpidx = new int[0]; tmpd = new double[0]; tmpc = new double[0]; tmplambdas = new double[0]; tmpc2 = new double[0,0]; tmpb = new bool[0]; nnls = new snnls.snnlssolver(); } public override alglib.apobject make_copy() { minnsqp _result = new minnsqp(); _result.fc = fc; _result.fn = fn; _result.xc = (double[])xc.Clone(); _result.xn = (double[])xn.Clone(); _result.x0 = (double[])x0.Clone(); _result.gc = (double[])gc.Clone(); _result.d = (double[])d.Clone(); _result.uh = (double[,])uh.Clone(); _result.ch = (double[,])ch.Clone(); _result.rk = (double[,])rk.Clone(); _result.invutc = (double[])invutc.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmpidx = (int[])tmpidx.Clone(); _result.tmpd = (double[])tmpd.Clone(); _result.tmpc = (double[])tmpc.Clone(); _result.tmplambdas = (double[])tmplambdas.Clone(); _result.tmpc2 = (double[,])tmpc2.Clone(); _result.tmpb = (bool[])tmpb.Clone(); _result.nnls = (snnls.snnlssolver)nnls.make_copy(); return _result; } }; /************************************************************************* This object stores nonlinear optimizer state. You should use functions provided by MinNS subpackage to work with this object *************************************************************************/ public class minnsstate : apobject { public int solvertype; public int n; public double epsx; public int maxits; public bool xrep; public double diffstep; public double[] s; public double[] bndl; public double[] bndu; public bool[] hasbndl; public bool[] hasbndu; public int nec; public int nic; public double[,] cleic; public int ng; public int nh; public double[] x; public double f; public double[] fi; public double[,] j; public bool needfij; public bool needfi; public bool xupdated; public rcommstate rstate; public rcommstate rstateags; public hqrnd.hqrndstate agsrs; public double agsradius; public int agssamplesize; public double agsraddecay; public double agsalphadecay; public double agsdecrease; public double agsinitstp; public double agsstattold; public double agsshortstpabs; public double agsshortstprel; public double agsshortf; public int agsshortlimit; public double agsrhononlinear; public int agsminupdate; public int agsmaxraddecays; public int agsmaxbacktrack; public int agsmaxbacktracknonfull; public double agspenaltylevel; public double agspenaltyincrease; public double[] xstart; public double[] xc; public double[] xn; public double[] grs; public double[] d; public double[] colmax; public double[] diagh; public double[] signmin; public double[] signmax; public bool userterminationneeded; public double[] scaledbndl; public double[] scaledbndu; public double[,] scaledcleic; public double[] rholinear; public double[,] samplex; public double[,] samplegm; public double[,] samplegmbc; public double[] samplef; public double[] samplef0; public minnsqp nsqp; public double[] tmp0; public double[] tmp1; public double[,] tmp2; public int[] tmp3; public double[] xbase; public double[] fp; public double[] fm; public int repinneriterationscount; public int repouteriterationscount; public int repnfev; public int repvaridx; public int repfuncidx; public int repterminationtype; public double replcerr; public double repnlcerr; public int dbgncholesky; public minnsstate() { init(); } public override void init() { s = new double[0]; bndl = new double[0]; bndu = new double[0]; hasbndl = new bool[0]; hasbndu = new bool[0]; cleic = new double[0,0]; x = new double[0]; fi = new double[0]; j = new double[0,0]; rstate = new rcommstate(); rstateags = new rcommstate(); agsrs = new hqrnd.hqrndstate(); xstart = new double[0]; xc = new double[0]; xn = new double[0]; grs = new double[0]; d = new double[0]; colmax = new double[0]; diagh = new double[0]; signmin = new double[0]; signmax = new double[0]; scaledbndl = new double[0]; scaledbndu = new double[0]; scaledcleic = new double[0,0]; rholinear = new double[0]; samplex = new double[0,0]; samplegm = new double[0,0]; samplegmbc = new double[0,0]; samplef = new double[0]; samplef0 = new double[0]; nsqp = new minnsqp(); tmp0 = new double[0]; tmp1 = new double[0]; tmp2 = new double[0,0]; tmp3 = new int[0]; xbase = new double[0]; fp = new double[0]; fm = new double[0]; } public override alglib.apobject make_copy() { minnsstate _result = new minnsstate(); _result.solvertype = solvertype; _result.n = n; _result.epsx = epsx; _result.maxits = maxits; _result.xrep = xrep; _result.diffstep = diffstep; _result.s = (double[])s.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.hasbndl = (bool[])hasbndl.Clone(); _result.hasbndu = (bool[])hasbndu.Clone(); _result.nec = nec; _result.nic = nic; _result.cleic = (double[,])cleic.Clone(); _result.ng = ng; _result.nh = nh; _result.x = (double[])x.Clone(); _result.f = f; _result.fi = (double[])fi.Clone(); _result.j = (double[,])j.Clone(); _result.needfij = needfij; _result.needfi = needfi; _result.xupdated = xupdated; _result.rstate = (rcommstate)rstate.make_copy(); _result.rstateags = (rcommstate)rstateags.make_copy(); _result.agsrs = (hqrnd.hqrndstate)agsrs.make_copy(); _result.agsradius = agsradius; _result.agssamplesize = agssamplesize; _result.agsraddecay = agsraddecay; _result.agsalphadecay = agsalphadecay; _result.agsdecrease = agsdecrease; _result.agsinitstp = agsinitstp; _result.agsstattold = agsstattold; _result.agsshortstpabs = agsshortstpabs; _result.agsshortstprel = agsshortstprel; _result.agsshortf = agsshortf; _result.agsshortlimit = agsshortlimit; _result.agsrhononlinear = agsrhononlinear; _result.agsminupdate = agsminupdate; _result.agsmaxraddecays = agsmaxraddecays; _result.agsmaxbacktrack = agsmaxbacktrack; _result.agsmaxbacktracknonfull = agsmaxbacktracknonfull; _result.agspenaltylevel = agspenaltylevel; _result.agspenaltyincrease = agspenaltyincrease; _result.xstart = (double[])xstart.Clone(); _result.xc = (double[])xc.Clone(); _result.xn = (double[])xn.Clone(); _result.grs = (double[])grs.Clone(); _result.d = (double[])d.Clone(); _result.colmax = (double[])colmax.Clone(); _result.diagh = (double[])diagh.Clone(); _result.signmin = (double[])signmin.Clone(); _result.signmax = (double[])signmax.Clone(); _result.userterminationneeded = userterminationneeded; _result.scaledbndl = (double[])scaledbndl.Clone(); _result.scaledbndu = (double[])scaledbndu.Clone(); _result.scaledcleic = (double[,])scaledcleic.Clone(); _result.rholinear = (double[])rholinear.Clone(); _result.samplex = (double[,])samplex.Clone(); _result.samplegm = (double[,])samplegm.Clone(); _result.samplegmbc = (double[,])samplegmbc.Clone(); _result.samplef = (double[])samplef.Clone(); _result.samplef0 = (double[])samplef0.Clone(); _result.nsqp = (minnsqp)nsqp.make_copy(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmp1 = (double[])tmp1.Clone(); _result.tmp2 = (double[,])tmp2.Clone(); _result.tmp3 = (int[])tmp3.Clone(); _result.xbase = (double[])xbase.Clone(); _result.fp = (double[])fp.Clone(); _result.fm = (double[])fm.Clone(); _result.repinneriterationscount = repinneriterationscount; _result.repouteriterationscount = repouteriterationscount; _result.repnfev = repnfev; _result.repvaridx = repvaridx; _result.repfuncidx = repfuncidx; _result.repterminationtype = repterminationtype; _result.replcerr = replcerr; _result.repnlcerr = repnlcerr; _result.dbgncholesky = dbgncholesky; return _result; } }; /************************************************************************* This structure stores optimization report: * IterationsCount total number of inner iterations * NFEV number of gradient evaluations * TerminationType termination type (see below) * CErr maximum violation of all types of constraints * LCErr maximum violation of linear constraints * NLCErr maximum violation of nonlinear constraints TERMINATION CODES TerminationType field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. -3 box constraints are inconsistent -1 inconsistent parameters were passed: * penalty parameter for minnssetalgoags() is zero, but we have nonlinear constraints set by minnssetnlc() 2 sampling radius decreased below epsx 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 User requested termination via MinNSRequestTermination() Other fields of this structure are not documented and should not be used! *************************************************************************/ public class minnsreport : apobject { public int iterationscount; public int nfev; public double cerr; public double lcerr; public double nlcerr; public int terminationtype; public int varidx; public int funcidx; public minnsreport() { init(); } public override void init() { } public override alglib.apobject make_copy() { minnsreport _result = new minnsreport(); _result.iterationscount = iterationscount; _result.nfev = nfev; _result.cerr = cerr; _result.lcerr = lcerr; _result.nlcerr = nlcerr; _result.terminationtype = terminationtype; _result.varidx = varidx; _result.funcidx = funcidx; return _result; } }; /************************************************************************* NONSMOOTH NONCONVEX OPTIMIZATION SUBJECT TO BOX/LINEAR/NONLINEAR-NONSMOOTH CONSTRAINTS DESCRIPTION: The subroutine minimizes function F(x) of N arguments subject to any combination of: * bound constraints * linear inequality constraints * linear equality constraints * nonlinear equality constraints Gi(x)=0 * nonlinear inequality constraints Hi(x)<=0 IMPORTANT: see MinNSSetAlgoAGS for important information on performance restrictions of AGS solver. REQUIREMENTS: * starting point X0 must be feasible or not too far away from the feasible set * F(), G(), H() are continuous, locally Lipschitz and continuously (but not necessarily twice) differentiable in an open dense subset of R^N. Functions F(), G() and H() may be nonsmooth and non-convex. Informally speaking, it means that functions are composed of large differentiable "patches" with nonsmoothness having place only at the boundaries between these "patches". Most real-life nonsmooth functions satisfy these requirements. Say, anything which involves finite number of abs(), min() and max() is very likely to pass the test. Say, it is possible to optimize anything of the following: * f=abs(x0)+2*abs(x1) * f=max(x0,x1) * f=sin(max(x0,x1)+abs(x2)) * for nonlinearly constrained problems: F() must be bounded from below without nonlinear constraints (this requirement is due to the fact that, contrary to box and linear constraints, nonlinear ones require special handling). * user must provide function value and gradient for F(), H(), G() at all points where function/gradient can be calculated. If optimizer requires value exactly at the boundary between "patches" (say, at x=0 for f=abs(x)), where gradient is not defined, user may resolve tie arbitrarily (in our case - return +1 or -1 at its discretion). * NS solver supports numerical differentiation, i.e. it may differentiate your function for you, but it results in 2N increase of function evaluations. Not recommended unless you solve really small problems. See minnscreatef() for more information on this functionality. USAGE: 1. User initializes algorithm state with MinNSCreate() call and chooses what NLC solver to use. There is some solver which is used by default, with default settings, but you should NOT rely on default choice. It may change in future releases of ALGLIB without notice, and no one can guarantee that new solver will be able to solve your problem with default settings. From the other side, if you choose solver explicitly, you can be pretty sure that it will work with new ALGLIB releases. In the current release following solvers can be used: * AGS solver (activated with MinNSSetAlgoAGS() function) 2. User adds boundary and/or linear and/or nonlinear constraints by means of calling one of the following functions: a) MinNSSetBC() for boundary constraints b) MinNSSetLC() for linear constraints c) MinNSSetNLC() for nonlinear constraints You may combine (a), (b) and (c) in one optimization problem. 3. User sets scale of the variables with MinNSSetScale() function. It is VERY important to set scale of the variables, because nonlinearly constrained problems are hard to solve when variables are badly scaled. 4. User sets stopping conditions with MinNSSetCond(). 5. Finally, user calls MinNSOptimize() function which takes algorithm state and pointer (delegate, etc) to callback function which calculates F/G/H. 7. User calls MinNSResults() to get solution 8. Optionally user may call MinNSRestartFrom() to solve another problem with same N but another starting point. MinNSRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. OUTPUT PARAMETERS: State - structure stores algorithm state NOTE: minnscreatef() function may be used if you do not have analytic gradient. This function creates solver which uses numerical differentiation with user-specified step. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnscreate(int n, double[] x, minnsstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinNSCreate: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinNSCreate: Length(X)0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. DiffStep- differentiation step, DiffStep>0. Algorithm performs numerical differentiation with step for I-th variable being equal to DiffStep*S[I] (here S[] is a scale vector, set by minnssetscale() function). Do not use too small steps, because it may lead to catastrophic cancellation during intermediate calculations. OUTPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnscreatef(int n, double[] x, double diffstep, minnsstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinNSCreateF: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinNSCreateF: Length(X)(double)(0), "MinNSCreateF: DiffStep is non-positive!"); minnsinitinternal(n, x, diffstep, state, _params); } /************************************************************************* This function sets boundary constraints. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with minnsrestartfrom(). INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: AGS solver has following useful properties: * bound constraints are always satisfied exactly * function is evaluated only INSIDE area specified by bound constraints, even when numerical differentiation is used (algorithm adjusts nodes according to boundary constraints) -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetbc(minnsstate state, double[] bndl, double[] bndu, alglib.xparams _params) { int i = 0; int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(bndl)>=n, "MinNSSetBC: Length(BndL)=n, "MinNSSetBC: Length(BndU)0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT NOTE: linear (non-bound) constraints are satisfied only approximately: * there always exists some minor violation (about current sampling radius in magnitude during optimization, about EpsX in the solution) due to use of penalty method to handle constraints. * numerical differentiation, if used, may lead to function evaluations outside of the feasible area, because algorithm does NOT change numerical differentiation formula according to linear constraints. If you want constraints to be satisfied exactly, try to reformulate your problem in such manner that all constraints will become boundary ones (this kind of constraints is always satisfied exactly, both in the final solution and in all intermediate points). -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetlc(minnsstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { int n = 0; int i = 0; int i_ = 0; n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(k>=0, "MinNSSetLC: K<0"); alglib.ap.assert(alglib.ap.cols(c)>=n+1 || k==0, "MinNSSetLC: Cols(C)=k, "MinNSSetLC: Rows(C)=k, "MinNSSetLC: Length(CT)0 ) { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = -c[i,i_]; } } else { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = c[i,i_]; } } state.nic = state.nic+1; } } } /************************************************************************* This function sets nonlinear constraints. In fact, this function sets NUMBER of nonlinear constraints. Constraints itself (constraint functions) are passed to minnsoptimize() method. This method requires user-defined vector function F[] and its Jacobian J[], where: * first component of F[] and first row of Jacobian J[] correspond to function being minimized * next NLEC components of F[] (and rows of J) correspond to nonlinear equality constraints G_i(x)=0 * next NLIC components of F[] (and rows of J) correspond to nonlinear inequality constraints H_i(x)<=0 NOTE: you may combine nonlinear constraints with linear/boundary ones. If your problem has mixed constraints, you may explicitly specify some of them as linear ones. It may help optimizer to handle them more efficiently. INPUT PARAMETERS: State - structure previously allocated with minnscreate() call. NLEC - number of Non-Linear Equality Constraints (NLEC), >=0 NLIC - number of Non-Linear Inquality Constraints (NLIC), >=0 NOTE 1: nonlinear constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of the feasible area! NOTE 2: algorithm scales variables according to scale specified by minnssetscale() function, so it can handle problems with badly scaled variables (as long as we KNOW their scales). However, there is no way to automatically scale nonlinear constraints Gi(x) and Hi(x). Inappropriate scaling of Gi/Hi may ruin convergence. Solving problem with constraint "1000*G0(x)=0" is NOT same as solving it with constraint "0.001*G0(x)=0". It means that YOU are the one who is responsible for correct scaling of nonlinear constraints Gi(x) and Hi(x). We recommend you to scale nonlinear constraints in such way that I-th component of dG/dX (or dH/dx) has approximately unit magnitude (for problems with unit scale) or has magnitude approximately equal to 1/S[i] (where S is a scale set by minnssetscale() function). NOTE 3: nonlinear constraints are always hard to handle, no matter what algorithm you try to use. Even basic box/linear constraints modify function curvature by adding valleys and ridges. However, nonlinear constraints add valleys which are very hard to follow due to their "curved" nature. It means that optimization with single nonlinear constraint may be significantly slower than optimization with multiple linear ones. It is normal situation, and we recommend you to carefully choose Rho parameter of minnssetalgoags(), because too large value may slow down convergence. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetnlc(minnsstate state, int nlec, int nlic, alglib.xparams _params) { alglib.ap.assert(nlec>=0, "MinNSSetNLC: NLEC<0"); alglib.ap.assert(nlic>=0, "MinNSSetNLC: NLIC<0"); state.ng = nlec; state.nh = nlic; state.fi = new double[1+state.ng+state.nh]; state.j = new double[1+state.ng+state.nh, state.n]; } /************************************************************************* This function sets stopping conditions for iterations of optimizer. INPUT PARAMETERS: State - structure which stores algorithm state EpsX - >=0 The AGS solver finishes its work if on k+1-th iteration sampling radius decreases below EpsX. MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection. We do not recommend you to rely on default choice in production code. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetcond(minnsstate state, double epsx, int maxits, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsx), "MinNSSetCond: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinNSSetCond: negative EpsX"); alglib.ap.assert(maxits>=0, "MinNSSetCond: negative MaxIts!"); if( (double)(epsx)==(double)(0) && maxits==0 ) { epsx = 1.0E-6; } state.epsx = epsx; state.maxits = maxits; } /************************************************************************* This function sets scaling coefficients for NLC optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetscale(minnsstate state, double[] s, alglib.xparams _params) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.n, "MinNSSetScale: Length(S)=0. Internally multiplied by vector of per-variable scales specified by minnssetscale()). You should select relatively large sampling radius, roughly proportional to scaled length of the first steps of the algorithm. Something close to 0.1 in magnitude should be good for most problems. AGS solver can automatically decrease radius, so too large radius is not a problem (assuming that you won't choose so large radius that algorithm will sample function in too far away points, where gradient value is irrelevant). Too small radius won't cause algorithm to fail, but it may slow down algorithm (it may have to perform too short steps). Penalty - penalty coefficient for nonlinear constraints: * for problem with nonlinear constraints should be some problem-specific positive value, large enough that penalty term changes shape of the function. Starting from some problem-specific value penalty coefficient becomes large enough to exactly enforce nonlinear constraints; larger values do not improve precision. Increasing it too much may slow down convergence, so you should choose it carefully. * can be zero for problems WITHOUT nonlinear constraints (i.e. for unconstrained ones or ones with just box or linear constraints) * if you specify zero value for problem with at least one nonlinear constraint, algorithm will terminate with error code -1. ALGORITHM OUTLINE The very basic outline of unconstrained AGS algorithm is given below: 0. If sampling radius is below EpsX or we performed more then MaxIts iterations - STOP. 1. sample O(N) gradient values at random locations around current point; informally speaking, this sample is an implicit piecewise linear model of the function, although algorithm formulation does not mention that explicitly 2. solve quadratic programming problem in order to find descent direction 3. if QP solver tells us that we are near solution, decrease sampling radius and move to (0) 4. perform backtracking line search 5. after moving to new point, goto (0) As for the constraints: * box constraints are handled exactly by modification of the function being minimized * linear/nonlinear constraints are handled by adding L1 penalty. Because our solver can handle nonsmoothness, we can use L1 penalty function, which is an exact one (i.e. exact solution is returned under such penalty). * penalty coefficient for linear constraints is chosen automatically; however, penalty coefficient for nonlinear constraints must be specified by user. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetalgoags(minnsstate state, double radius, double penalty, alglib.xparams _params) { alglib.ap.assert(math.isfinite(radius), "MinNSSetAlgoAGS: Radius is not finite"); alglib.ap.assert((double)(radius)>(double)(0), "MinNSSetAlgoAGS: Radius<=0"); alglib.ap.assert(math.isfinite(penalty), "MinNSSetAlgoAGS: Penalty is not finite"); alglib.ap.assert((double)(penalty)>=(double)(0.0), "MinNSSetAlgoAGS: Penalty<0"); state.agsrhononlinear = penalty; state.agsradius = radius; state.solvertype = 0; } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to minnsoptimize(). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnssetxrep(minnsstate state, bool needxrep, alglib.xparams _params) { state.xrep = needxrep; } /************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsrequesttermination(minnsstate state, alglib.xparams _params) { state.userterminationneeded = true; } /************************************************************************* NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied Jacobian, and one which uses only function vector and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object you should choose appropriate variant of minnsoptimize() - one which accepts function AND Jacobian or one which accepts ONLY function. Be careful to choose variant of minnsoptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to minnsoptimize() and specific function used to create optimizer. | USER PASSED TO minnsoptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ minnscreatef() | works FAILS minnscreate() | FAILS works Here "FAILS" denotes inappropriate combinations of optimizer creation function and minnsoptimize() version. Attemps to use such combination will lead to exception. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static bool minnsiteration(minnsstate state, alglib.xparams _params) { bool result = new bool(); int i = 0; int k = 0; int n = 0; int ng = 0; int nh = 0; double v = 0; double xp = 0; double xm = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { i = state.rstate.ia[0]; k = state.rstate.ia[1]; n = state.rstate.ia[2]; ng = state.rstate.ia[3]; nh = state.rstate.ia[4]; v = state.rstate.ra[0]; xp = state.rstate.ra[1]; xm = state.rstate.ra[2]; } else { i = 359; k = -58; n = -919; ng = -909; nh = 81; v = 255; xp = 74; xm = -788; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } // // Routine body // // // Init // state.replcerr = 0.0; state.repnlcerr = 0.0; state.repterminationtype = 0; state.repinneriterationscount = 0; state.repouteriterationscount = 0; state.repnfev = 0; state.repvaridx = 0; state.repfuncidx = 0; state.userterminationneeded = false; state.dbgncholesky = 0; n = state.n; ng = state.ng; nh = state.nh; clearrequestfields(state, _params); // // AGS solver // if( state.solvertype!=0 ) { goto lbl_4; } if( (double)(state.diffstep)!=(double)(0) ) { apserv.rvectorsetlengthatleast(ref state.xbase, n, _params); apserv.rvectorsetlengthatleast(ref state.fm, 1+ng+nh, _params); apserv.rvectorsetlengthatleast(ref state.fp, 1+ng+nh, _params); } state.rstateags.ia = new int[13+1]; state.rstateags.ba = new bool[3+1]; state.rstateags.ra = new double[9+1]; state.rstateags.stage = -1; lbl_6: if( !agsiteration(state, _params) ) { goto lbl_7; } // // Numerical differentiation (if needed) - intercept NeedFiJ // request and replace it by sequence of NeedFi requests // if( !((double)(state.diffstep)!=(double)(0) && state.needfij) ) { goto lbl_8; } state.needfij = false; state.needfi = true; for(i_=0; i_<=n-1;i_++) { state.xbase[i_] = state.x[i_]; } k = 0; lbl_10: if( k>n-1 ) { goto lbl_12; } v = state.xbase[k]; xm = v-state.diffstep*state.s[k]; xp = v+state.diffstep*state.s[k]; if( state.hasbndl[k] && (double)(xm)<(double)(state.bndl[k]) ) { xm = state.bndl[k]; } if( state.hasbndu[k] && (double)(xp)>(double)(state.bndu[k]) ) { xp = state.bndu[k]; } alglib.ap.assert((double)(xm)<=(double)(xp), "MinNS: integrity check failed"); if( (double)(xm)==(double)(xp) ) { goto lbl_13; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = xm; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: for(i_=0; i_<=ng+nh;i_++) { state.fm[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = xp; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: for(i_=0; i_<=ng+nh;i_++) { state.fp[i_] = state.fi[i_]; } for(i_=0; i_<=ng+nh;i_++) { state.j[i_,k] = state.fp[i_]; } for(i_=0; i_<=ng+nh;i_++) { state.j[i_,k] = state.j[i_,k] - state.fm[i_]; } v = 1/(xp-xm); for(i_=0; i_<=ng+nh;i_++) { state.j[i_,k] = v*state.j[i_,k]; } state.repnfev = state.repnfev+2; goto lbl_14; lbl_13: for(i=0; i<=ng+nh; i++) { state.j[i,k] = 0.0; } lbl_14: k = k+1; goto lbl_10; lbl_12: for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.rstate.stage = 2; goto lbl_rcomm; lbl_2: // // Restore previous values of fields and continue // state.needfi = false; state.needfij = true; goto lbl_6; lbl_8: // // Forward request to caller // state.rstate.stage = 3; goto lbl_rcomm; lbl_3: apserv.inc(ref state.repnfev, _params); goto lbl_6; lbl_7: result = false; return result; lbl_4: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = i; state.rstate.ia[1] = k; state.rstate.ia[2] = n; state.rstate.ia[3] = ng; state.rstate.ia[4] = nh; state.rstate.ra[0] = v; state.rstate.ra[1] = xp; state.rstate.ra[2] = xm; return result; } /************************************************************************* MinNS results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report. You should check Rep.TerminationType in order to distinguish successful termination from unsuccessful one: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -3 box constraints are inconsistent * -1 inconsistent parameters were passed: * penalty parameter for minnssetalgoags() is zero, but we have nonlinear constraints set by minnssetnlc() * 2 sampling radius decreased below epsx * 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. * 8 User requested termination via minnsrequesttermination() -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsresults(minnsstate state, ref double[] x, minnsreport rep, alglib.xparams _params) { x = new double[0]; minnsresultsbuf(state, ref x, rep, _params); } /************************************************************************* Buffered implementation of minnsresults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsresultsbuf(minnsstate state, ref double[] x, minnsreport rep, alglib.xparams _params) { int i = 0; int i_ = 0; if( alglib.ap.len(x)0 ) { for(i_=0; i_<=state.n-1;i_++) { x[i_] = state.xc[i_]; } } else { for(i=0; i<=state.n-1; i++) { x[i] = Double.NaN; } } } /************************************************************************* This subroutine restarts algorithm from new point. All optimization parameters (including constraints) are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure previously allocated with minnscreate() call. X - new starting point. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsrestartfrom(minnsstate state, double[] x, alglib.xparams _params) { int n = 0; int i_ = 0; n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(alglib.ap.len(x)>=n, "MinNSRestartFrom: Length(X)=0 ) { n = state.rstateags.ia[0]; nec = state.rstateags.ia[1]; nic = state.rstateags.ia[2]; ng = state.rstateags.ia[3]; nh = state.rstateags.ia[4]; i = state.rstateags.ia[5]; j = state.rstateags.ia[6]; k = state.rstateags.ia[7]; radiusdecays = state.rstateags.ia[8]; maxsamplesize = state.rstateags.ia[9]; cursamplesize = state.rstateags.ia[10]; shortstepscnt = state.rstateags.ia[11]; backtrackits = state.rstateags.ia[12]; maxbacktrackits = state.rstateags.ia[13]; restartneeded = state.rstateags.ba[0]; b = state.rstateags.ba[1]; alphadecreased = state.rstateags.ba[2]; fullsample = state.rstateags.ba[3]; radius0 = state.rstateags.ra[0]; radius = state.rstateags.ra[1]; alpha = state.rstateags.ra[2]; recommendedstep = state.rstateags.ra[3]; dnrm = state.rstateags.ra[4]; dg = state.rstateags.ra[5]; v = state.rstateags.ra[6]; vv = state.rstateags.ra[7]; v0 = state.rstateags.ra[8]; v1 = state.rstateags.ra[9]; } else { n = 809; nec = 205; nic = -838; ng = 939; nh = -526; i = 763; j = -541; k = -698; radiusdecays = -900; maxsamplesize = -318; cursamplesize = -940; shortstepscnt = 1016; backtrackits = -229; maxbacktrackits = -536; restartneeded = true; b = true; alphadecreased = false; fullsample = false; radius0 = -722; radius = -413; alpha = -461; recommendedstep = 927; dnrm = 201; dg = 922; v = -154; vv = 306; v0 = -1011; v1 = 951; } if( state.rstateags.stage==0 ) { goto lbl_0; } if( state.rstateags.stage==1 ) { goto lbl_1; } if( state.rstateags.stage==2 ) { goto lbl_2; } if( state.rstateags.stage==3 ) { goto lbl_3; } // // Routine body // alglib.ap.assert(state.solvertype==0, "MinNS: internal error"); n = state.n; nec = state.nec; nic = state.nic; ng = state.ng; nh = state.nh; // // Check consistency of parameters // if( ng+nh>0 && (double)(state.agsrhononlinear)==(double)(0) ) { state.repterminationtype = -1; result = false; return result; } // // Allocate arrays. // apserv.rvectorsetlengthatleast(ref state.colmax, n, _params); apserv.rvectorsetlengthatleast(ref state.diagh, n, _params); apserv.rvectorsetlengthatleast(ref state.signmin, n, _params); apserv.rvectorsetlengthatleast(ref state.signmax, n, _params); maxsamplesize = state.agssamplesize; apserv.rmatrixsetlengthatleast(ref state.samplex, maxsamplesize+1, n, _params); apserv.rmatrixsetlengthatleast(ref state.samplegm, maxsamplesize+1, n, _params); apserv.rmatrixsetlengthatleast(ref state.samplegmbc, maxsamplesize+1, n, _params); apserv.rvectorsetlengthatleast(ref state.samplef, maxsamplesize+1, _params); apserv.rvectorsetlengthatleast(ref state.samplef0, maxsamplesize+1, _params); apserv.rvectorsetlengthatleast(ref state.grs, n, _params); // // Prepare optimizer // apserv.rvectorsetlengthatleast(ref state.tmp0, maxsamplesize, _params); apserv.rvectorsetlengthatleast(ref state.tmp1, maxsamplesize, _params); apserv.ivectorsetlengthatleast(ref state.tmp3, 1, _params); apserv.rmatrixsetlengthatleast(ref state.tmp2, 1, maxsamplesize+1, _params); for(i=0; i<=maxsamplesize-1; i++) { state.tmp0[i] = 0.0; state.tmp1[i] = Double.PositiveInfinity; } // // Prepare RNG, seed it with fixed values so // that each run on same problem yeilds same results // hqrnd.hqrndseed(7235, 98532, state.agsrs, _params); // // Prepare initial point subject to current bound constraints and // perform scaling of bound constraints, linear constraints, point itself // apserv.rvectorsetlengthatleast(ref state.scaledbndl, n, _params); apserv.rvectorsetlengthatleast(ref state.scaledbndu, n, _params); for(i=0; i<=n-1; i++) { // // Check and scale constraints // if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndu[i])<(double)(state.bndl[i]) ) { state.repterminationtype = -3; result = false; return result; } if( state.hasbndl[i] ) { state.scaledbndl[i] = state.bndl[i]/state.s[i]; } else { state.scaledbndl[i] = Double.NegativeInfinity; } if( state.hasbndu[i] ) { state.scaledbndu[i] = state.bndu[i]/state.s[i]; } else { state.scaledbndu[i] = Double.PositiveInfinity; } if( state.hasbndl[i] && state.hasbndu[i] ) { alglib.ap.assert((double)(state.scaledbndl[i])<=(double)(state.scaledbndu[i]), "MinNS: integrity check failed"); } if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { alglib.ap.assert((double)(state.scaledbndl[i])==(double)(state.scaledbndu[i]), "MinNS: integrity check failed"); } // // Scale and constrain point // state.xc[i] = state.xstart[i]; if( state.hasbndl[i] && (double)(state.xc[i])<=(double)(state.bndl[i]) ) { state.xc[i] = state.scaledbndl[i]; continue; } if( state.hasbndu[i] && (double)(state.xc[i])>=(double)(state.bndu[i]) ) { state.xc[i] = state.scaledbndu[i]; continue; } state.xc[i] = state.xc[i]/state.s[i]; if( state.hasbndl[i] && (double)(state.xc[i])<=(double)(state.scaledbndl[i]) ) { state.xc[i] = state.scaledbndl[i]; } if( state.hasbndu[i] && (double)(state.xc[i])>=(double)(state.scaledbndu[i]) ) { state.xc[i] = state.scaledbndu[i]; } } apserv.rmatrixsetlengthatleast(ref state.scaledcleic, nec+nic, n+1, _params); apserv.rvectorsetlengthatleast(ref state.rholinear, nec+nic, _params); for(i=0; i<=nec+nic-1; i++) { // // Initial value of penalty coefficient is zero // state.rholinear[i] = 0.0; // // Scale and normalize linear constraints // vv = 0.0; for(j=0; j<=n-1; j++) { v = state.cleic[i,j]*state.s[j]; state.scaledcleic[i,j] = v; vv = vv+v*v; } vv = Math.Sqrt(vv); state.scaledcleic[i,n] = state.cleic[i,n]; if( (double)(vv)>(double)(0) ) { for(j=0; j<=n; j++) { state.scaledcleic[i,j] = state.scaledcleic[i,j]/vv; } } } // // Main cycle // // We maintain several variables during iteration: // * RecommendedStep- current estimate of recommended step length; // must be Radius0 on first entry // * Radius - current sampling radius // * CurSampleSize - current sample size (may change in future versions) // * FullSample - whether we have full sample, or only partial one // * RadiusDecays - total number of decreases performed for sampling radius // radius = state.agsradius; radius0 = radius; recommendedstep = Math.Min(radius0, state.agsinitstp); cursamplesize = 1; radiusdecays = 0; shortstepscnt = 0; fullsample = false; lbl_4: if( false ) { goto lbl_5; } // // First phase of iteration - central point: // // 1. evaluate function at central point - first entry in sample. // Its status is ignored, it is always recalculated. // 2. report point and check gradient/function value for NAN/INF // 3. check penalty coefficients for linear terms; increase them // if directional derivative of function being optimized (not // merit function!) is larger than derivative of penalty. // 4. update report on constraint violation // cursamplesize = Math.Max(cursamplesize, 1); for(i_=0; i_<=n-1;i_++) { state.samplex[0,i_] = state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xc[i_]; } unscalepointbc(state, state.x, _params); clearrequestfields(state, _params); state.needfij = true; state.rstateags.stage = 0; goto lbl_rcomm; lbl_0: state.needfij = false; state.replcerr = 0.0; for(i=0; i<=nec+nic-1; i++) { v = -state.scaledcleic[i,n]; for(j=0; j<=n-1; j++) { v = v+state.scaledcleic[i,j]*state.xc[j]; } if( i>=nec && (double)(v)<=(double)(0) ) { continue; } state.replcerr = Math.Max(state.replcerr, Math.Abs(v)); } state.repnlcerr = 0.0; for(i=1; i<=ng+nh; i++) { v = state.fi[i]; if( i>ng && (double)(v)<=(double)(0) ) { continue; } state.repnlcerr = Math.Max(state.repnlcerr, Math.Abs(v)); } for(j=0; j<=n-1; j++) { state.grs[j] = state.j[0,j]*state.s[j]; } generatemeritfunction(state, 0, _params); if( !state.xrep ) { goto lbl_6; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xc[i_]; } state.f = state.samplef0[0]; unscalepointbc(state, state.x, _params); clearrequestfields(state, _params); state.xupdated = true; state.rstateags.stage = 1; goto lbl_rcomm; lbl_1: state.xupdated = false; lbl_6: if( state.userterminationneeded ) { // // User requested termination // state.repterminationtype = 8; goto lbl_5; } v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.samplegm[0,i]); } if( !math.isfinite(v) || !math.isfinite(state.samplef[0]) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; goto lbl_5; } restartneeded = false; for(i=0; i<=nec+nic-1; i++) { // // Evaluate penalty function. // // Skip update if penalty is satisfied exactly (this check // also covers situations when I-th row is exactly zero). // v = -state.scaledcleic[i,n]; for(j=0; j<=n-1; j++) { v = v+state.scaledcleic[i,j]*state.xc[j]; } if( i=nec && (double)(v)<=(double)(0) ) { continue; } // // Calculate directional derivative, compare it with threshold. // // NOTE: we rely on the fact that ScaledCLEIC is normalized // alglib.ap.assert((double)(state.agspenaltylevel)>(double)(1.0), "MinNS: integrity error"); alglib.ap.assert((double)(state.agspenaltyincrease)>(double)(state.agspenaltylevel), "MinNS: integrity error"); v = 0.0; for(j=0; j<=n-1; j++) { v = v+state.grs[j]*state.scaledcleic[i,j]; } v = Math.Abs(v); if( (double)(v*state.agspenaltylevel)>(double)(state.rholinear[i]) ) { state.rholinear[i] = v*state.agspenaltyincrease; restartneeded = true; } } if( restartneeded ) { cursamplesize = 0; goto lbl_4; } // // Check stopping conditions. // if( radiusdecays>=state.agsmaxraddecays ) { // // Too many attempts to decrease radius // state.repterminationtype = 7; goto lbl_5; } if( state.repinneriterationscount>=state.maxits && state.maxits>0 ) { // // Too many iterations // state.repterminationtype = 5; goto lbl_5; } if( (double)(radius)<=(double)(state.epsx*state.agsraddecay) ) { // // Radius is smaller than required step tolerance multiplied by radius decay. // // Additional decay is required in order to make sure that optimization session // with radius equal to EpsX was successfully done. // state.repterminationtype = 2; goto lbl_5; } // // Update sample: // // 1. invalidate entries which are too far away from XC // and move all valid entries to beginning of the sample. // 2. add new entries until we have AGSSampleSize // items in our sample. We remove oldest entries from // sample until we have enough place to add at least // AGSMinUpdate items. // 3. prepare "modified" gradient sample with respect to // boundary constraints. // alglib.ap.assert(cursamplesize>=1, "MinNS: integrity check failed"); k = 1; for(i=1; i<=cursamplesize-1; i++) { // // If entry is outside of Radius-ball around XC, discard it. // v = 0.0; for(j=0; j<=n-1; j++) { v = Math.Max(v, Math.Abs(state.samplex[i,j]-state.xc[j])); } if( (double)(v)>(double)(radius) ) { continue; } // // If central point is exactly at boundary, and corresponding // component of entry is OUT of boundary, entry is discarded. // b = false; for(j=0; j<=n-1; j++) { b = b || ((state.hasbndl[j] && (double)(state.xc[j])==(double)(state.scaledbndl[j])) && (double)(state.samplex[i,j])!=(double)(state.scaledbndl[j])); b = b || ((state.hasbndu[j] && (double)(state.xc[j])==(double)(state.scaledbndu[j])) && (double)(state.samplex[i,j])!=(double)(state.scaledbndu[j])); } if( b ) { continue; } // // Move to the beginning // for(i_=0; i_<=n-1;i_++) { state.samplex[k,i_] = state.samplex[i,i_]; } for(i_=0; i_<=n-1;i_++) { state.samplegm[k,i_] = state.samplegm[i,i_]; } state.samplef[k] = state.samplef[i]; state.samplef0[k] = state.samplef0[i]; k = k+1; } cursamplesize = k; if( state.agssamplesize-cursamplesizeMath.Min(cursamplesize+state.agsminupdate, state.agssamplesize)-1 ) { goto lbl_10; } for(j=0; j<=n-1; j++) { // // Undistorted position // state.samplex[i,j] = state.xc[j]; // // Do not apply distortion, if we are exactly at boundary constraint. // if( (state.hasbndl[j] && state.hasbndu[j]) && (double)(state.scaledbndl[j])==(double)(state.scaledbndu[j]) ) { continue; } if( state.hasbndl[j] && (double)(state.samplex[i,j])==(double)(state.scaledbndl[j]) ) { continue; } if( state.hasbndu[j] && (double)(state.samplex[i,j])==(double)(state.scaledbndu[j]) ) { continue; } // // Apply distortion // if( (double)(hqrnd.hqrnduniformr(state.agsrs, _params))>=(double)(0.5) ) { // // Sample at the left side with 50% probability // v0 = state.samplex[i,j]-radius; v1 = state.samplex[i,j]; if( state.hasbndl[j] ) { v0 = Math.Max(state.scaledbndl[j], v0); } } else { // // Sample at the right side with 50% probability // v0 = state.samplex[i,j]; v1 = state.samplex[i,j]+radius; if( state.hasbndu[j] ) { v1 = Math.Min(state.scaledbndu[j], v1); } } alglib.ap.assert((double)(v1)>=(double)(v0), "MinNS: integrity check failed"); state.samplex[i,j] = apserv.boundval(v0+(v1-v0)*hqrnd.hqrnduniformr(state.agsrs, _params), v0, v1, _params); } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.samplex[i,i_]; } unscalepointbc(state, state.x, _params); clearrequestfields(state, _params); state.needfij = true; state.rstateags.stage = 2; goto lbl_rcomm; lbl_2: state.needfij = false; generatemeritfunction(state, i, _params); k = k+1; i = i+1; goto lbl_8; lbl_10: cursamplesize = cursamplesize+k; fullsample = cursamplesize==state.agssamplesize; for(j=0; j<=cursamplesize-1; j++) { // // For J-th element in gradient sample, process all of its components // and modify them according to status of box constraints // for(i=0; i<=n-1; i++) { alglib.ap.assert(!state.hasbndl[i] || (double)(state.xc[i])>=(double)(state.scaledbndl[i]), "MinNS: integrity error"); alglib.ap.assert(!state.hasbndu[i] || (double)(state.xc[i])<=(double)(state.scaledbndu[i]), "MinNS: integrity error"); state.samplegmbc[j,i] = state.samplegm[j,i]; if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.scaledbndl[i])==(double)(state.scaledbndu[i]) ) { // // I-th box constraint is of equality type (lower bound matches upper one). // Simplest case, always active. // state.samplegmbc[j,i] = 0.0; continue; } if( state.hasbndl[i] && (double)(state.xc[i])==(double)(state.scaledbndl[i]) ) { // // We are at lower bound. // // A bit more complex: // * first, we have to activate/deactivate constraint depending on gradient at XC // * second, in any case, I-th column of gradient sample must be non-positive // if( (double)(state.samplegm[0,i])>=(double)(0.0) ) { state.samplegmbc[j,i] = 0.0; } state.samplegmbc[j,i] = Math.Min(state.samplegmbc[j,i], 0.0); continue; } if( state.hasbndu[i] && (double)(state.xc[i])==(double)(state.scaledbndu[i]) ) { // // We are at upper bound. // // A bit more complex: // * first, we have to activate/deactivate constraint depending on gradient at XC // * second, in any case, I-th column of gradient sample must be non-negative // if( (double)(state.samplegm[0,i])<=(double)(0.0) ) { state.samplegmbc[j,i] = 0.0; } state.samplegmbc[j,i] = Math.Max(state.samplegmbc[j,i], 0.0); continue; } } } // // Calculate diagonal Hessian. // // This Hessian serves two purposes: // * first, it improves performance of gradient descent step // * second, it improves condition number of QP subproblem // solved to determine step // // The idea is that for each variable we check whether sample // includes entries with alternating sign of gradient: // * if gradients with different signs are present, Hessian // component is set to M/R, where M is a maximum magnitude // of corresponding gradient component, R is a sampling radius. // Note that sign=0 and sign=1 are treated as different ones // * if all gradients have same sign, Hessian component is // set to M/R0, where R0 is initial sampling radius. // for(j=0; j<=n-1; j++) { state.colmax[j] = 0.0; state.signmin[j] = 1; state.signmax[j] = -1; } for(i=0; i<=cursamplesize-1; i++) { for(j=0; j<=n-1; j++) { v = state.samplegmbc[i,j]; state.colmax[j] = Math.Max(state.colmax[j], Math.Abs(v)); state.signmin[j] = Math.Min(state.signmin[j], Math.Sign(v)); state.signmax[j] = Math.Max(state.signmax[j], Math.Sign(v)); } } for(j=0; j<=n-1; j++) { if( (double)(state.signmin[j])!=(double)(state.signmax[j]) ) { // // Alternating signs of gradient - step is proportional to current sampling radius // alglib.ap.assert((double)(state.colmax[j])!=(double)(0), "MinNS: integrity check failed"); alglib.ap.assert((double)(radius)!=(double)(0), "MinNS: integrity check failed"); state.diagh[j] = state.colmax[j]/radius; continue; } if( (double)(state.colmax[j])!=(double)(0) ) { // // Non-alternating sign of gradient, but non-zero. // Step is proportional to initial sampling radius // alglib.ap.assert((double)(radius0)!=(double)(0), "MinNS: integrity check failed"); state.diagh[j] = state.colmax[j]/radius0; continue; } state.diagh[j] = 1; } // // PROJECTION PHASE // // We project zero vector on convex hull of gradient sample. // If projection is small enough, we decrease radius and restart. // Otherwise, this phase returns search direction in State.D. // // NOTE: because we use iterative solver, it may have trouble // dealing with ill-conditioned problems. So we also employ // second, backup test for stationarity - when too many // subsequent backtracking searches resulted in short steps. // solveqp(state.samplegmbc, state.diagh, cursamplesize, n, ref state.tmp0, ref state.dbgncholesky, state.nsqp, _params); for(j=0; j<=n-1; j++) { state.d[j] = 0.0; } for(i=0; i<=cursamplesize-1; i++) { v = state.tmp0[i]; for(i_=0; i_<=n-1;i_++) { state.d[i_] = state.d[i_] + v*state.samplegmbc[i,i_]; } } v = 0.0; for(j=0; j<=n-1; j++) { v = Math.Max(v, Math.Abs(state.d[j]/apserv.coalesce(state.colmax[j], 1.0, _params))); } if( (double)(v)<=(double)(state.agsstattold) ) { // // Stationarity test succeded. // Decrease radius and restart. // // NOTE: we also clear ShortStepsCnt on restart // radius = radius*state.agsraddecay; shortstepscnt = 0; apserv.inc(ref radiusdecays, _params); apserv.inc(ref state.repinneriterationscount, _params); goto lbl_4; } for(i=0; i<=n-1; i++) { state.d[i] = -(state.d[i]/state.diagh[i]); } // // Perform backtracking line search. // Update initial step length depending on search results. // Here we assume that D is non-zero. // // NOTE: if AGSShortLimit subsequent line searches resulted // in steps shorter than AGSStatTolStp, we decrease radius. // dnrm = 0.0; dg = 0.0; for(i=0; i<=n-1; i++) { dnrm = dnrm+math.sqr(state.d[i]); dg = dg+state.d[i]*state.samplegmbc[0,i]; } dnrm = Math.Sqrt(dnrm); alglib.ap.assert((double)(dnrm)>(double)(0), "MinNS: integrity error"); alpha = recommendedstep/dnrm; alphadecreased = false; backtrackits = 0; if( fullsample ) { maxbacktrackits = state.agsmaxbacktrack; } else { maxbacktrackits = state.agsmaxbacktracknonfull; } lbl_11: if( false ) { goto lbl_12; } // // Prepare XN and evaluate merit function at XN // for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xn[i_] + alpha*state.d[i_]; } optserv.enforceboundaryconstraints(state.xn, state.scaledbndl, state.hasbndl, state.scaledbndu, state.hasbndu, n, 0, _params); for(i_=0; i_<=n-1;i_++) { state.samplex[maxsamplesize,i_] = state.xn[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xn[i_]; } unscalepointbc(state, state.x, _params); clearrequestfields(state, _params); state.needfij = true; state.rstateags.stage = 3; goto lbl_rcomm; lbl_3: state.needfij = false; generatemeritfunction(state, maxsamplesize, _params); // // Check sufficient decrease condition // alglib.ap.assert((double)(dnrm)>(double)(0), "MinNS: integrity error"); if( (double)(state.samplef[maxsamplesize])<=(double)(state.samplef[0]+alpha*state.agsdecrease*dg) ) { goto lbl_12; } // // Decrease Alpha // alpha = alpha*state.agsalphadecay; alphadecreased = true; // // Update and check iterations counter. // apserv.inc(ref backtrackits, _params); if( backtrackits>=maxbacktrackits ) { // // Too many backtracking searches performed without success. // Terminate iterations. // alpha = 0.0; alphadecreased = true; for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xc[i_]; } goto lbl_12; } goto lbl_11; lbl_12: if( ((double)(alpha*dnrm)<=(double)(state.agsshortstpabs) || (double)(alpha*dnrm)<=(double)(state.agsshortstprel*radius)) || (double)(Math.Abs(state.samplef[0]-state.samplef[maxsamplesize]))<=(double)(state.agsshortf) ) { apserv.inc(ref shortstepscnt, _params); } else { shortstepscnt = 0; } if( shortstepscnt>=state.agsshortlimit ) { // // Too many subsequent short steps. // // It may be possible that optimizer is unable to find out // that we have to decrease radius because of ill-conditioned // gradients. // // Decrease radius and restart. // radius = radius*state.agsraddecay; shortstepscnt = 0; apserv.inc(ref radiusdecays, _params); apserv.inc(ref state.repinneriterationscount, _params); goto lbl_4; } if( !alphadecreased ) { recommendedstep = recommendedstep*2.0; } if( alphadecreased && fullsample ) { recommendedstep = recommendedstep*0.5; } // // Next iteration // for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.xn[i_]; } apserv.inc(ref state.repinneriterationscount, _params); goto lbl_4; lbl_5: // // Convert back from scaled to unscaled representation // unscalepointbc(state, state.xc, _params); result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstateags.ia[0] = n; state.rstateags.ia[1] = nec; state.rstateags.ia[2] = nic; state.rstateags.ia[3] = ng; state.rstateags.ia[4] = nh; state.rstateags.ia[5] = i; state.rstateags.ia[6] = j; state.rstateags.ia[7] = k; state.rstateags.ia[8] = radiusdecays; state.rstateags.ia[9] = maxsamplesize; state.rstateags.ia[10] = cursamplesize; state.rstateags.ia[11] = shortstepscnt; state.rstateags.ia[12] = backtrackits; state.rstateags.ia[13] = maxbacktrackits; state.rstateags.ba[0] = restartneeded; state.rstateags.ba[1] = b; state.rstateags.ba[2] = alphadecreased; state.rstateags.ba[3] = fullsample; state.rstateags.ra[0] = radius0; state.rstateags.ra[1] = radius; state.rstateags.ra[2] = alpha; state.rstateags.ra[3] = recommendedstep; state.rstateags.ra[4] = dnrm; state.rstateags.ra[5] = dg; state.rstateags.ra[6] = v; state.rstateags.ra[7] = vv; state.rstateags.ra[8] = v0; state.rstateags.ra[9] = v1; return result; } /************************************************************************* This function calculates merit function (target function + penalties for violation of non-box constraints), using State.X (unscaled), State.Fi, State.J (unscaled) and State.SampleX (scaled) as inputs. Results are loaded: * target function value - to State.SampleF0[SampleIdx] * merit function value - to State.SampleF[SampleIdx] * gradient of merit function - to State.SampleGM[SampleIdx] -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void generatemeritfunction(minnsstate state, int sampleidx, alglib.xparams _params) { int n = 0; int i = 0; int j = 0; int nec = 0; int nic = 0; int ng = 0; int nh = 0; double v = 0; double s = 0; n = state.n; nec = state.nec; nic = state.nic; ng = state.ng; nh = state.nh; // // Integrity check // for(i=0; i<=n-1; i++) { alglib.ap.assert(!state.hasbndl[i] || (double)(state.x[i])>=(double)(state.bndl[i]), "MinNS: integrity error"); alglib.ap.assert(!state.hasbndu[i] || (double)(state.x[i])<=(double)(state.bndu[i]), "MinNS: integrity error"); } // // Prepare "raw" function // state.samplef[sampleidx] = state.fi[0]; state.samplef0[sampleidx] = state.fi[0]; for(j=0; j<=n-1; j++) { state.samplegm[sampleidx,j] = state.j[0,j]*state.s[j]; } // // Modify merit function with linear constraints // for(i=0; i<=nec+nic-1; i++) { v = -state.scaledcleic[i,n]; for(j=0; j<=n-1; j++) { v = v+state.scaledcleic[i,j]*state.samplex[sampleidx,j]; } if( i>=nec && (double)(v)<(double)(0) ) { continue; } state.samplef[sampleidx] = state.samplef[sampleidx]+state.rholinear[i]*Math.Abs(v); s = Math.Sign(v); for(j=0; j<=n-1; j++) { state.samplegm[sampleidx,j] = state.samplegm[sampleidx,j]+state.rholinear[i]*s*state.scaledcleic[i,j]; } } // // Modify merit function with nonlinear constraints // for(i=1; i<=ng+nh; i++) { v = state.fi[i]; if( i<=ng && (double)(v)==(double)(0) ) { continue; } if( i>ng && (double)(v)<=(double)(0) ) { continue; } state.samplef[sampleidx] = state.samplef[sampleidx]+state.agsrhononlinear*Math.Abs(v); s = Math.Sign(v); for(j=0; j<=n-1; j++) { state.samplegm[sampleidx,j] = state.samplegm[sampleidx,j]+state.agsrhononlinear*s*state.j[i,j]*state.s[j]; } } } /************************************************************************* This function performs transformation of X from scaled coordinates to unscaled ones, paying special attention to box constraints: * points which were exactly at the boundary before scaling will be mapped to corresponding boundary after scaling * in any case, unscaled box constraints will be satisfied -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void unscalepointbc(minnsstate state, double[] x, alglib.xparams _params) { int i = 0; for(i=0; i<=state.n-1; i++) { if( state.hasbndl[i] && (double)(x[i])<=(double)(state.scaledbndl[i]) ) { x[i] = state.bndl[i]; continue; } if( state.hasbndu[i] && (double)(x[i])>=(double)(state.scaledbndu[i]) ) { x[i] = state.bndu[i]; continue; } x[i] = x[i]*state.s[i]; if( state.hasbndl[i] && (double)(x[i])<=(double)(state.bndl[i]) ) { x[i] = state.bndl[i]; } if( state.hasbndu[i] && (double)(x[i])>=(double)(state.bndu[i]) ) { x[i] = state.bndu[i]; } } } /************************************************************************* This function solves QP problem of the form [ ] min [ 0.5*c'*(G*inv(H)*G')*c ] s.t. c[i]>=0, SUM(c[i])=1.0 [ ] where G is stored in SampleG[] array, diagonal H is stored in DiagH[]. DbgNCholesky is incremented every time we perform Cholesky decomposition. -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void solveqp(double[,] sampleg, double[] diagh, int nsample, int nvars, ref double[] coeffs, ref int dbgncholesky, minnsqp state, alglib.xparams _params) { int i = 0; int j = 0; int k = 0; double v = 0; double vv = 0; int n = 0; int idx0 = 0; int idx1 = 0; int ncandbnd = 0; int innerits = 0; int outerits = 0; double dnrm = 0; double stp = 0; double stpmax = 0; int actidx = 0; double dtol = 0; bool kickneeded = new bool(); double kicklength = 0; double lambdav = 0; double maxdiag = 0; bool wasactivation = new bool(); bool werechanges = new bool(); int termcnt = 0; int i_ = 0; n = nsample; // // Allocate arrays, prepare data // apserv.rvectorsetlengthatleast(ref coeffs, n, _params); apserv.rvectorsetlengthatleast(ref state.xc, n, _params); apserv.rvectorsetlengthatleast(ref state.xn, n, _params); apserv.rvectorsetlengthatleast(ref state.x0, n, _params); apserv.rvectorsetlengthatleast(ref state.gc, n, _params); apserv.rvectorsetlengthatleast(ref state.d, n, _params); apserv.rmatrixsetlengthatleast(ref state.uh, n, n, _params); apserv.rmatrixsetlengthatleast(ref state.ch, n, n, _params); apserv.rmatrixsetlengthatleast(ref state.rk, nsample, nvars, _params); apserv.rvectorsetlengthatleast(ref state.invutc, n, _params); apserv.rvectorsetlengthatleast(ref state.tmp0, n, _params); apserv.bvectorsetlengthatleast(ref state.tmpb, n, _params); for(i=0; i<=n-1; i++) { state.xc[i] = 1.0/n; coeffs[i] = 1.0/n; } for(i=0; i<=nsample-1; i++) { for(j=0; j<=nvars-1; j++) { state.rk[i,j] = sampleg[i,j]/Math.Sqrt(diagh[j]); } } ablas.rmatrixsyrk(nsample, nvars, 1.0, state.rk, 0, 0, 0, 0.0, state.uh, 0, 0, true, _params); maxdiag = 0.0; for(i=0; i<=nsample-1; i++) { maxdiag = Math.Max(maxdiag, state.uh[i,i]); } maxdiag = apserv.coalesce(maxdiag, 1.0, _params); // // Main cycle: // innerits = 0; outerits = 0; dtol = 1.0E5*math.machineepsilon; kicklength = math.machineepsilon; lambdav = 1.0E5*math.machineepsilon; termcnt = 0; while( true ) { // // Save current point to X0 // for(i_=0; i_<=n-1;i_++) { state.x0[i_] = state.xc[i_]; } // // Calculate gradient at initial point, solve NNLS problem // to determine descent direction D subject to constraints. // // In order to do so we solve following constrained // minimization problem: // ( )^2 // min ( SUM(lambda[i]*A[i]) + G ) // ( ) // Here: // * G is a gradient (column vector) // * A[i] is a column vector of I-th constraint // * lambda[i] is a Lagrange multiplier corresponding to I-th constraint // // NOTE: all A[i] except for last one have only one element being set, // so we rely on sparse capabilities of NNLS solver. However, // in order to use these capabilities we have to reorder variables // in such way that sparse ones come first. // // After finding lambda[] coefficients, we can find constrained descent // direction by subtracting lambda[i]*A[i] from D=-G. We make use of the // fact that first NCandBnd columns are just columns of identity matrix, // so we can perform exact projection by explicitly setting elements of D // to zeros. // qpcalculategradfunc(sampleg, diagh, nsample, nvars, state.xc, ref state.gc, ref state.fc, ref state.tmp0, _params); apserv.ivectorsetlengthatleast(ref state.tmpidx, n, _params); apserv.rvectorsetlengthatleast(ref state.tmpd, n, _params); apserv.rmatrixsetlengthatleast(ref state.tmpc2, n, 1, _params); idx0 = 0; ncandbnd = 0; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) ) { ncandbnd = ncandbnd+1; } } idx1 = ncandbnd; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) ) { // // Candidate for activation of boundary constraint, // comes first. // // NOTE: multiplication by -1 is due to the fact that // it is lower bound, and has specific direction // of constraint gradient. // state.tmpidx[idx0] = i; state.tmpd[idx0] = -state.gc[i]*-1; state.tmpc2[idx0,0] = 1.0*-1; idx0 = idx0+1; } else { // // We are far away from boundary. // state.tmpidx[idx1] = i; state.tmpd[idx1] = -state.gc[i]; state.tmpc2[idx1,0] = 1.0; idx1 = idx1+1; } } alglib.ap.assert(idx0==ncandbnd, "MinNSQP: integrity check failed"); alglib.ap.assert(idx1==n, "MinNSQP: integrity check failed"); snnls.snnlsinit(n, 1, n, state.nnls, _params); snnls.snnlssetproblem(state.nnls, state.tmpc2, state.tmpd, ncandbnd, 1, n, _params); snnls.snnlsdropnnc(state.nnls, ncandbnd, _params); snnls.snnlssolve(state.nnls, ref state.tmplambdas, _params); for(i=0; i<=n-1; i++) { state.d[i] = -state.gc[i]-state.tmplambdas[ncandbnd]; } for(i=0; i<=ncandbnd-1; i++) { if( (double)(state.tmplambdas[i])>(double)(0) ) { state.d[state.tmpidx[i]] = 0.0; } } // // Additional stage to "polish" D (improve situation // with sum-to-one constraint and boundary constraints) // and to perform additional integrity check. // // After this stage we are pretty sure that: // * if x[i]=0.0, then d[i]>=0.0 // * if d[i]<0.0, then x[i]>0.0 // v = 0.0; vv = 0.0; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) && (double)(state.d[i])<(double)(0.0) ) { state.d[i] = 0.0; } v = v+state.d[i]; vv = Math.Max(vv, Math.Abs(state.gc[i])); } alglib.ap.assert((double)(Math.Abs(v))<(double)(1.0E5*Math.Sqrt(n)*math.machineepsilon*Math.Max(vv, 1.0)), "MinNSQP: integrity check failed"); // // Decide whether we need "kick" stage: special stage // that moves us away from boundary constraints which are // not strictly active (i.e. such constraints that x[i]=0.0 and d[i]>0). // // If we need kick stage, we make a kick - and restart iteration. // If not, after this block we can rely on the fact that // for all x[i]=0.0 we have d[i]=0.0 // kickneeded = false; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) && (double)(state.d[i])>(double)(0.0) ) { kickneeded = true; } } if( kickneeded ) { // // Perform kick. // Restart. // Do not increase outer iterations counter. // v = 0.0; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) && (double)(state.d[i])>(double)(0.0) ) { state.xc[i] = state.xc[i]+kicklength; } v = v+state.xc[i]; } alglib.ap.assert((double)(v)>(double)(0.0), "MinNSQP: integrity check failed"); for(i=0; i<=n-1; i++) { state.xc[i] = state.xc[i]/v; } apserv.inc(ref innerits, _params); continue; } // // Calculate Cholesky decomposition of constrained Hessian // for Newton phase. // while( true ) { for(i=0; i<=n-1; i++) { // // Diagonal element // if( (double)(state.xc[i])>(double)(0.0) ) { state.ch[i,i] = state.uh[i,i]+lambdav*maxdiag; } else { state.ch[i,i] = 1.0; } // // Offdiagonal elements // for(j=i+1; j<=n-1; j++) { if( (double)(state.xc[i])>(double)(0.0) && (double)(state.xc[j])>(double)(0.0) ) { state.ch[i,j] = state.uh[i,j]; } else { state.ch[i,j] = 0.0; } } } apserv.inc(ref dbgncholesky, _params); if( !trfac.spdmatrixcholeskyrec(ref state.ch, 0, n, true, ref state.tmp0, _params) ) { // // Cholesky decomposition failed. // Increase LambdaV and repeat iteration. // Do not increase outer iterations counter. // lambdav = lambdav*10; continue; } break; } // // Newton phase // while( true ) { // // Calculate constrained (equality and sum-to-one) descent direction D. // // Here we use Sherman-Morrison update to calculate direction subject to // sum-to-one constraint. // qpcalculategradfunc(sampleg, diagh, nsample, nvars, state.xc, ref state.gc, ref state.fc, ref state.tmp0, _params); for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])>(double)(0.0) ) { state.invutc[i] = 1.0; state.d[i] = -state.gc[i]; } else { state.invutc[i] = 0.0; state.d[i] = 0.0; } } qpsolveut(state.ch, n, state.invutc, _params); qpsolveut(state.ch, n, state.d, _params); v = 0.0; vv = 0.0; for(i=0; i<=n-1; i++) { vv = vv+math.sqr(state.invutc[i]); v = v+state.invutc[i]*state.d[i]; } for(i=0; i<=n-1; i++) { state.d[i] = state.d[i]-v/vv*state.invutc[i]; } qpsolveu(state.ch, n, state.d, _params); v = 0.0; k = 0; for(i=0; i<=n-1; i++) { v = v+state.d[i]; if( (double)(state.d[i])!=(double)(0.0) ) { k = k+1; } } if( k>0 && (double)(v)>(double)(0.0) ) { vv = v/k; for(i=0; i<=n-1; i++) { if( (double)(state.d[i])!=(double)(0.0) ) { state.d[i] = state.d[i]-vv; } } } // // Calculate length of D, maximum step and component which is // activated by this step. // // Break if D is exactly zero. We do not break here if DNrm is // small - this check is performed later. It is important to // perform last step with nearly-zero D, it allows us to have // extra-precision in solution which is often needed for convergence // of AGS algorithm. // dnrm = 0.0; for(i=0; i<=n-1; i++) { dnrm = dnrm+math.sqr(state.d[i]); } dnrm = Math.Sqrt(dnrm); actidx = -1; stpmax = 1.0E50; for(i=0; i<=n-1; i++) { if( (double)(state.d[i])<(double)(0.0) ) { v = stpmax; stpmax = apserv.safeminposrv(state.xc[i], -state.d[i], stpmax, _params); if( (double)(stpmax)<(double)(v) ) { actidx = i; } } } if( (double)(dnrm)==(double)(0.0) ) { break; } // // Calculate trial function value at unconstrained full step. // If trial value is greater or equal to FC, terminate iterations. // for(i=0; i<=n-1; i++) { state.xn[i] = state.xc[i]+1.0*state.d[i]; } qpcalculatefunc(sampleg, diagh, nsample, nvars, state.xn, ref state.fn, ref state.tmp0, _params); if( (double)(state.fn)>=(double)(state.fc) ) { break; } // // Perform step // Update Hessian // Update XC // // Break if: // a) no constraint was activated // b) norm of D is small enough // stp = Math.Min(1.0, stpmax); for(i=0; i<=n-1; i++) { state.xn[i] = Math.Max(state.xc[i]+stp*state.d[i], 0.0); } if( (double)(stp)==(double)(stpmax) && actidx>=0 ) { state.xn[actidx] = 0.0; } wasactivation = false; for(i=0; i<=n-1; i++) { state.tmpb[i] = (double)(state.xn[i])==(double)(0.0) && (double)(state.xc[i])!=(double)(0.0); wasactivation = wasactivation || state.tmpb[i]; } for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.xn[i_]; } if( !wasactivation ) { break; } if( (double)(dnrm)<=(double)(dtol) ) { break; } trfac.spdmatrixcholeskyupdatefixbuf(state.ch, n, true, state.tmpb, ref state.tmp0, _params); } // // Compare status of boundary constraints - if nothing changed during // last outer iteration, TermCnt is increased. Otherwise it is reset // to zero. // // When TermCnt is large enough, we terminate algorithm. // werechanges = false; for(i=0; i<=n-1; i++) { werechanges = werechanges || Math.Sign(state.x0[i])!=Math.Sign(state.xc[i]); } if( !werechanges ) { apserv.inc(ref termcnt, _params); } else { termcnt = 0; } if( termcnt>=2 ) { break; } // // Increase number of outer iterations. // Break if we performed too many. // apserv.inc(ref outerits, _params); if( outerits==10 ) { break; } } // // Store result // for(i=0; i<=n-1; i++) { coeffs[i] = state.xc[i]; } } /************************************************************************* Function/gradient calculation for QP solver. -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void qpcalculategradfunc(double[,] sampleg, double[] diagh, int nsample, int nvars, double[] coeffs, ref double[] g, ref double f, ref double[] tmp, alglib.xparams _params) { int i = 0; int j = 0; double v = 0; int i_ = 0; f = 0; apserv.rvectorsetlengthatleast(ref g, nsample, _params); apserv.rvectorsetlengthatleast(ref tmp, nvars, _params); // // Calculate GS*p // for(j=0; j<=nvars-1; j++) { tmp[j] = 0.0; } for(i=0; i<=nsample-1; i++) { v = coeffs[i]; for(i_=0; i_<=nvars-1;i_++) { tmp[i_] = tmp[i_] + v*sampleg[i,i_]; } } // // Calculate F // f = 0.0; for(i=0; i<=nvars-1; i++) { f = f+0.5*math.sqr(tmp[i])/diagh[i]; } // // Multiply by inverse Hessian // for(i=0; i<=nvars-1; i++) { tmp[i] = tmp[i]/diagh[i]; } // // Function gradient // for(i=0; i<=nsample-1; i++) { v = 0.0; for(i_=0; i_<=nvars-1;i_++) { v += sampleg[i,i_]*tmp[i_]; } g[i] = v; } } /************************************************************************* Function calculation for QP solver. -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void qpcalculatefunc(double[,] sampleg, double[] diagh, int nsample, int nvars, double[] coeffs, ref double f, ref double[] tmp, alglib.xparams _params) { int i = 0; int j = 0; double v = 0; int i_ = 0; f = 0; apserv.rvectorsetlengthatleast(ref tmp, nvars, _params); // // Calculate GS*p // for(j=0; j<=nvars-1; j++) { tmp[j] = 0.0; } for(i=0; i<=nsample-1; i++) { v = coeffs[i]; for(i_=0; i_<=nvars-1;i_++) { tmp[i_] = tmp[i_] + v*sampleg[i,i_]; } } // // Calculate F // f = 0.0; for(i=0; i<=nvars-1; i++) { f = f+0.5*math.sqr(tmp[i])/diagh[i]; } } /************************************************************************* Triangular solver for QP solver. -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void qpsolveu(double[,] a, int n, double[] x, alglib.xparams _params) { int i = 0; int j = 0; double v = 0; // // A^(-1)*X // for(i=n-1; i>=0; i--) { v = x[i]; for(j=i+1; j<=n-1; j++) { v = v-a[i,j]*x[j]; } x[i] = v/a[i,i]; } } /************************************************************************* Triangular solver for QP solver. -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void qpsolveut(double[,] a, int n, double[] x, alglib.xparams _params) { int i = 0; int j = 0; double v = 0; // // A^(-T)*X // for(i=0; i<=n-1; i++) { x[i] = x[i]/a[i,i]; v = x[i]; for(j=i+1; j<=n-1; j++) { x[j] = x[j]-a[i,j]*v; } } } } public class mincomp { public class minasastate : apobject { public int n; public double epsg; public double epsf; public double epsx; public int maxits; public bool xrep; public double stpmax; public int cgtype; public int k; public int nfev; public int mcstage; public double[] bndl; public double[] bndu; public int curalgo; public int acount; public double mu; public double finit; public double dginit; public double[] ak; public double[] xk; public double[] dk; public double[] an; public double[] xn; public double[] dn; public double[] d; public double fold; public double stp; public double[] work; public double[] yk; public double[] gc; public double laststep; public double[] x; public double f; public double[] g; public bool needfg; public bool xupdated; public rcommstate rstate; public int repiterationscount; public int repnfev; public int repterminationtype; public int debugrestartscount; public linmin.linminstate lstate; public double betahs; public double betady; public minasastate() { init(); } public override void init() { bndl = new double[0]; bndu = new double[0]; ak = new double[0]; xk = new double[0]; dk = new double[0]; an = new double[0]; xn = new double[0]; dn = new double[0]; d = new double[0]; work = new double[0]; yk = new double[0]; gc = new double[0]; x = new double[0]; g = new double[0]; rstate = new rcommstate(); lstate = new linmin.linminstate(); } public override alglib.apobject make_copy() { minasastate _result = new minasastate(); _result.n = n; _result.epsg = epsg; _result.epsf = epsf; _result.epsx = epsx; _result.maxits = maxits; _result.xrep = xrep; _result.stpmax = stpmax; _result.cgtype = cgtype; _result.k = k; _result.nfev = nfev; _result.mcstage = mcstage; _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.curalgo = curalgo; _result.acount = acount; _result.mu = mu; _result.finit = finit; _result.dginit = dginit; _result.ak = (double[])ak.Clone(); _result.xk = (double[])xk.Clone(); _result.dk = (double[])dk.Clone(); _result.an = (double[])an.Clone(); _result.xn = (double[])xn.Clone(); _result.dn = (double[])dn.Clone(); _result.d = (double[])d.Clone(); _result.fold = fold; _result.stp = stp; _result.work = (double[])work.Clone(); _result.yk = (double[])yk.Clone(); _result.gc = (double[])gc.Clone(); _result.laststep = laststep; _result.x = (double[])x.Clone(); _result.f = f; _result.g = (double[])g.Clone(); _result.needfg = needfg; _result.xupdated = xupdated; _result.rstate = (rcommstate)rstate.make_copy(); _result.repiterationscount = repiterationscount; _result.repnfev = repnfev; _result.repterminationtype = repterminationtype; _result.debugrestartscount = debugrestartscount; _result.lstate = (linmin.linminstate)lstate.make_copy(); _result.betahs = betahs; _result.betady = betady; return _result; } }; public class minasareport : apobject { public int iterationscount; public int nfev; public int terminationtype; public int activeconstraints; public minasareport() { init(); } public override void init() { } public override alglib.apobject make_copy() { minasareport _result = new minasareport(); _result.iterationscount = iterationscount; _result.nfev = nfev; _result.terminationtype = terminationtype; _result.activeconstraints = activeconstraints; return _result; } }; public const int n1 = 2; public const int n2 = 2; public const double stpmin = 1.0E-300; public const double gtol = 0.3; public const double gpaftol = 0.0001; public const double gpadecay = 0.5; public const double asarho = 0.5; /************************************************************************* Obsolete function, use MinLBFGSSetPrecDefault() instead. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetdefaultpreconditioner(minlbfgs.minlbfgsstate state, alglib.xparams _params) { minlbfgs.minlbfgssetprecdefault(state, _params); } /************************************************************************* Obsolete function, use MinLBFGSSetCholeskyPreconditioner() instead. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlbfgssetcholeskypreconditioner(minlbfgs.minlbfgsstate state, double[,] p, bool isupper, alglib.xparams _params) { minlbfgs.minlbfgssetpreccholesky(state, p, isupper, _params); } /************************************************************************* This is obsolete function which was used by previous version of the BLEIC optimizer. It does nothing in the current version of BLEIC. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetbarrierwidth(minbleic.minbleicstate state, double mu, alglib.xparams _params) { } /************************************************************************* This is obsolete function which was used by previous version of the BLEIC optimizer. It does nothing in the current version of BLEIC. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minbleicsetbarrierdecay(minbleic.minbleicstate state, double mudecay, alglib.xparams _params) { } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 25.03.2010 by Bochkanov Sergey *************************************************************************/ public static void minasacreate(int n, double[] x, double[] bndl, double[] bndu, minasastate state, alglib.xparams _params) { int i = 0; alglib.ap.assert(n>=1, "MinASA: N too small!"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinCGCreate: Length(X)=n, "MinCGCreate: Length(BndL)=n, "MinCGCreate: Length(BndU)=(double)(0), "MinASASetCond: negative EpsG!"); alglib.ap.assert(math.isfinite(epsf), "MinASASetCond: EpsF is not finite number!"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinASASetCond: negative EpsF!"); alglib.ap.assert(math.isfinite(epsx), "MinASASetCond: EpsX is not finite number!"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinASASetCond: negative EpsX!"); alglib.ap.assert(maxits>=0, "MinASASetCond: negative MaxIts!"); if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxits==0 ) { epsx = 1.0E-6; } state.epsg = epsg; state.epsf = epsf; state.epsx = epsx; state.maxits = maxits; } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minasasetxrep(minasastate state, bool needxrep, alglib.xparams _params) { state.xrep = needxrep; } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minasasetalgorithm(minasastate state, int algotype, alglib.xparams _params) { alglib.ap.assert(algotype>=-1 && algotype<=1, "MinASASetAlgorithm: incorrect AlgoType!"); if( algotype==-1 ) { algotype = 1; } state.cgtype = algotype; } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minasasetstpmax(minasastate state, double stpmax, alglib.xparams _params) { alglib.ap.assert(math.isfinite(stpmax), "MinASASetStpMax: StpMax is not finite!"); alglib.ap.assert((double)(stpmax)>=(double)(0), "MinASASetStpMax: StpMax<0!"); state.stpmax = stpmax; } /************************************************************************* -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ public static bool minasaiteration(minasastate state, alglib.xparams _params) { bool result = new bool(); int n = 0; int i = 0; double betak = 0; double v = 0; double vv = 0; int mcinfo = 0; bool b = new bool(); bool stepfound = new bool(); int diffcnt = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { n = state.rstate.ia[0]; i = state.rstate.ia[1]; mcinfo = state.rstate.ia[2]; diffcnt = state.rstate.ia[3]; b = state.rstate.ba[0]; stepfound = state.rstate.ba[1]; betak = state.rstate.ra[0]; v = state.rstate.ra[1]; vv = state.rstate.ra[2]; } else { n = 359; i = -58; mcinfo = -919; diffcnt = -909; b = true; stepfound = true; betak = 74; v = -788; vv = 809; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } if( state.rstate.stage==4 ) { goto lbl_4; } if( state.rstate.stage==5 ) { goto lbl_5; } if( state.rstate.stage==6 ) { goto lbl_6; } if( state.rstate.stage==7 ) { goto lbl_7; } if( state.rstate.stage==8 ) { goto lbl_8; } if( state.rstate.stage==9 ) { goto lbl_9; } if( state.rstate.stage==10 ) { goto lbl_10; } if( state.rstate.stage==11 ) { goto lbl_11; } if( state.rstate.stage==12 ) { goto lbl_12; } if( state.rstate.stage==13 ) { goto lbl_13; } if( state.rstate.stage==14 ) { goto lbl_14; } // // Routine body // // // Prepare // n = state.n; state.repterminationtype = 0; state.repiterationscount = 0; state.repnfev = 0; state.debugrestartscount = 0; state.cgtype = 1; for(i_=0; i_<=n-1;i_++) { state.xk[i_] = state.x[i_]; } for(i=0; i<=n-1; i++) { if( (double)(state.xk[i])==(double)(state.bndl[i]) || (double)(state.xk[i])==(double)(state.bndu[i]) ) { state.ak[i] = 0; } else { state.ak[i] = 1; } } state.mu = 0.1; state.curalgo = 0; // // Calculate F/G, initialize algorithm // clearrequestfields(state, _params); state.needfg = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needfg = false; if( !state.xrep ) { goto lbl_15; } // // progress report // clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: state.xupdated = false; lbl_15: if( (double)(asaboundedantigradnorm(state, _params))<=(double)(state.epsg) ) { state.repterminationtype = 4; result = false; return result; } state.repnfev = state.repnfev+1; // // Main cycle // // At the beginning of new iteration: // * CurAlgo stores current algorithm selector // * State.XK, State.F and State.G store current X/F/G // * State.AK stores current set of active constraints // lbl_17: if( false ) { goto lbl_18; } // // GPA algorithm // if( state.curalgo!=0 ) { goto lbl_19; } state.k = 0; state.acount = 0; lbl_21: if( false ) { goto lbl_22; } // // Determine Dk = proj(xk - gk)-xk // for(i=0; i<=n-1; i++) { state.d[i] = apserv.boundval(state.xk[i]-state.g[i], state.bndl[i], state.bndu[i], _params)-state.xk[i]; } // // Armijo line search. // * exact search with alpha=1 is tried first, // 'exact' means that we evaluate f() EXACTLY at // bound(x-g,bndl,bndu), without intermediate floating // point operations. // * alpha<1 are tried if explicit search wasn't successful // Result is placed into XN. // // Two types of search are needed because we can't // just use second type with alpha=1 because in finite // precision arithmetics (x1-x0)+x0 may differ from x1. // So while x1 is correctly bounded (it lie EXACTLY on // boundary, if it is active), (x1-x0)+x0 may be // not bounded. // v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.d[i_]*state.g[i_]; } state.dginit = v; state.finit = state.f; if( !((double)(asad1norm(state, _params))<=(double)(state.stpmax) || (double)(state.stpmax)==(double)(0)) ) { goto lbl_23; } // // Try alpha=1 step first // for(i=0; i<=n-1; i++) { state.x[i] = apserv.boundval(state.xk[i]-state.g[i], state.bndl[i], state.bndu[i], _params); } clearrequestfields(state, _params); state.needfg = true; state.rstate.stage = 2; goto lbl_rcomm; lbl_2: state.needfg = false; state.repnfev = state.repnfev+1; stepfound = (double)(state.f)<=(double)(state.finit+gpaftol*state.dginit); goto lbl_24; lbl_23: stepfound = false; lbl_24: if( !stepfound ) { goto lbl_25; } // // we are at the boundary(ies) // for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.x[i_]; } state.stp = 1; goto lbl_26; lbl_25: // // alpha=1 is too large, try smaller values // state.stp = 1; linmin.linminnormalized(ref state.d, ref state.stp, n, _params); state.dginit = state.dginit/state.stp; state.stp = gpadecay*state.stp; if( (double)(state.stpmax)>(double)(0) ) { state.stp = Math.Min(state.stp, state.stpmax); } lbl_27: if( false ) { goto lbl_28; } v = state.stp; for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xk[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.x[i_] + v*state.d[i_]; } clearrequestfields(state, _params); state.needfg = true; state.rstate.stage = 3; goto lbl_rcomm; lbl_3: state.needfg = false; state.repnfev = state.repnfev+1; if( (double)(state.stp)<=(double)(stpmin) ) { goto lbl_28; } if( (double)(state.f)<=(double)(state.finit+state.stp*gpaftol*state.dginit) ) { goto lbl_28; } state.stp = state.stp*gpadecay; goto lbl_27; lbl_28: for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.x[i_]; } lbl_26: state.repiterationscount = state.repiterationscount+1; if( !state.xrep ) { goto lbl_29; } // // progress report // clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 4; goto lbl_rcomm; lbl_4: state.xupdated = false; lbl_29: // // Calculate new set of active constraints. // Reset counter if active set was changed. // Prepare for the new iteration // for(i=0; i<=n-1; i++) { if( (double)(state.xn[i])==(double)(state.bndl[i]) || (double)(state.xn[i])==(double)(state.bndu[i]) ) { state.an[i] = 0; } else { state.an[i] = 1; } } for(i=0; i<=n-1; i++) { if( (double)(state.ak[i])!=(double)(state.an[i]) ) { state.acount = -1; break; } } state.acount = state.acount+1; for(i_=0; i_<=n-1;i_++) { state.xk[i_] = state.xn[i_]; } for(i_=0; i_<=n-1;i_++) { state.ak[i_] = state.an[i_]; } // // Stopping conditions // if( !(state.repiterationscount>=state.maxits && state.maxits>0) ) { goto lbl_31; } // // Too many iterations // state.repterminationtype = 5; if( !state.xrep ) { goto lbl_33; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 5; goto lbl_rcomm; lbl_5: state.xupdated = false; lbl_33: result = false; return result; lbl_31: if( (double)(asaboundedantigradnorm(state, _params))>(double)(state.epsg) ) { goto lbl_35; } // // Gradient is small enough // state.repterminationtype = 4; if( !state.xrep ) { goto lbl_37; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 6; goto lbl_rcomm; lbl_6: state.xupdated = false; lbl_37: result = false; return result; lbl_35: v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.d[i_]*state.d[i_]; } if( (double)(Math.Sqrt(v)*state.stp)>(double)(state.epsx) ) { goto lbl_39; } // // Step size is too small, no further improvement is // possible // state.repterminationtype = 2; if( !state.xrep ) { goto lbl_41; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 7; goto lbl_rcomm; lbl_7: state.xupdated = false; lbl_41: result = false; return result; lbl_39: if( (double)(state.finit-state.f)>(double)(state.epsf*Math.Max(Math.Abs(state.finit), Math.Max(Math.Abs(state.f), 1.0))) ) { goto lbl_43; } // // F(k+1)-F(k) is small enough // state.repterminationtype = 1; if( !state.xrep ) { goto lbl_45; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 8; goto lbl_rcomm; lbl_8: state.xupdated = false; lbl_45: result = false; return result; lbl_43: // // Decide - should we switch algorithm or not // if( asauisempty(state, _params) ) { if( (double)(asaginorm(state, _params))>=(double)(state.mu*asad1norm(state, _params)) ) { state.curalgo = 1; goto lbl_22; } else { state.mu = state.mu*asarho; } } else { if( state.acount==n1 ) { if( (double)(asaginorm(state, _params))>=(double)(state.mu*asad1norm(state, _params)) ) { state.curalgo = 1; goto lbl_22; } } } // // Next iteration // state.k = state.k+1; goto lbl_21; lbl_22: lbl_19: // // CG algorithm // if( state.curalgo!=1 ) { goto lbl_47; } // // first, check that there are non-active constraints. // move to GPA algorithm, if all constraints are active // b = true; for(i=0; i<=n-1; i++) { if( (double)(state.ak[i])!=(double)(0) ) { b = false; break; } } if( b ) { state.curalgo = 0; goto lbl_17; } // // CG iterations // state.fold = state.f; for(i_=0; i_<=n-1;i_++) { state.xk[i_] = state.x[i_]; } for(i=0; i<=n-1; i++) { state.dk[i] = -(state.g[i]*state.ak[i]); state.gc[i] = state.g[i]*state.ak[i]; } lbl_49: if( false ) { goto lbl_50; } // // Store G[k] for later calculation of Y[k] // for(i=0; i<=n-1; i++) { state.yk[i] = -state.gc[i]; } // // Make a CG step in direction given by DK[]: // * calculate step. Step projection into feasible set // is used. It has several benefits: a) step may be // found with usual line search, b) multiple constraints // may be activated with one step, c) activated constraints // are detected in a natural way - just compare x[i] with // bounds // * update active set, set B to True, if there // were changes in the set. // for(i_=0; i_<=n-1;i_++) { state.d[i_] = state.dk[i_]; } for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xk[i_]; } state.mcstage = 0; state.stp = 1; linmin.linminnormalized(ref state.d, ref state.stp, n, _params); if( (double)(state.laststep)!=(double)(0) ) { state.stp = state.laststep; } linmin.mcsrch(n, ref state.xn, ref state.f, ref state.gc, state.d, ref state.stp, state.stpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); lbl_51: if( state.mcstage==0 ) { goto lbl_52; } // // preprocess data: bound State.XN so it belongs to the // feasible set and store it in the State.X // for(i=0; i<=n-1; i++) { state.x[i] = apserv.boundval(state.xn[i], state.bndl[i], state.bndu[i], _params); } // // RComm // clearrequestfields(state, _params); state.needfg = true; state.rstate.stage = 9; goto lbl_rcomm; lbl_9: state.needfg = false; // // postprocess data: zero components of G corresponding to // the active constraints // for(i=0; i<=n-1; i++) { if( (double)(state.x[i])==(double)(state.bndl[i]) || (double)(state.x[i])==(double)(state.bndu[i]) ) { state.gc[i] = 0; } else { state.gc[i] = state.g[i]; } } linmin.mcsrch(n, ref state.xn, ref state.f, ref state.gc, state.d, ref state.stp, state.stpmax, gtol, ref mcinfo, ref state.nfev, ref state.work, state.lstate, ref state.mcstage, _params); goto lbl_51; lbl_52: diffcnt = 0; for(i=0; i<=n-1; i++) { // // XN contains unprojected result, project it, // save copy to X (will be used for progress reporting) // state.xn[i] = apserv.boundval(state.xn[i], state.bndl[i], state.bndu[i], _params); // // update active set // if( (double)(state.xn[i])==(double)(state.bndl[i]) || (double)(state.xn[i])==(double)(state.bndu[i]) ) { state.an[i] = 0; } else { state.an[i] = 1; } if( (double)(state.an[i])!=(double)(state.ak[i]) ) { diffcnt = diffcnt+1; } state.ak[i] = state.an[i]; } for(i_=0; i_<=n-1;i_++) { state.xk[i_] = state.xn[i_]; } state.repnfev = state.repnfev+state.nfev; state.repiterationscount = state.repiterationscount+1; if( !state.xrep ) { goto lbl_53; } // // progress report // clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 10; goto lbl_rcomm; lbl_10: state.xupdated = false; lbl_53: // // Update info about step length // v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.d[i_]*state.d[i_]; } state.laststep = Math.Sqrt(v)*state.stp; // // Check stopping conditions. // if( (double)(asaboundedantigradnorm(state, _params))>(double)(state.epsg) ) { goto lbl_55; } // // Gradient is small enough // state.repterminationtype = 4; if( !state.xrep ) { goto lbl_57; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 11; goto lbl_rcomm; lbl_11: state.xupdated = false; lbl_57: result = false; return result; lbl_55: if( !(state.repiterationscount>=state.maxits && state.maxits>0) ) { goto lbl_59; } // // Too many iterations // state.repterminationtype = 5; if( !state.xrep ) { goto lbl_61; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 12; goto lbl_rcomm; lbl_12: state.xupdated = false; lbl_61: result = false; return result; lbl_59: if( !((double)(asaginorm(state, _params))>=(double)(state.mu*asad1norm(state, _params)) && diffcnt==0) ) { goto lbl_63; } // // These conditions (EpsF/EpsX) are explicitly or implicitly // related to the current step size and influenced // by changes in the active constraints. // // For these reasons they are checked only when we don't // want to 'unstick' at the end of the iteration and there // were no changes in the active set. // // NOTE: consition |G|>=Mu*|D1| must be exactly opposite // to the condition used to switch back to GPA. At least // one inequality must be strict, otherwise infinite cycle // may occur when |G|=Mu*|D1| (we DON'T test stopping // conditions and we DON'T switch to GPA, so we cycle // indefinitely). // if( (double)(state.fold-state.f)>(double)(state.epsf*Math.Max(Math.Abs(state.fold), Math.Max(Math.Abs(state.f), 1.0))) ) { goto lbl_65; } // // F(k+1)-F(k) is small enough // state.repterminationtype = 1; if( !state.xrep ) { goto lbl_67; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 13; goto lbl_rcomm; lbl_13: state.xupdated = false; lbl_67: result = false; return result; lbl_65: if( (double)(state.laststep)>(double)(state.epsx) ) { goto lbl_69; } // // X(k+1)-X(k) is small enough // state.repterminationtype = 2; if( !state.xrep ) { goto lbl_71; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 14; goto lbl_rcomm; lbl_14: state.xupdated = false; lbl_71: result = false; return result; lbl_69: lbl_63: // // Check conditions for switching // if( (double)(asaginorm(state, _params))<(double)(state.mu*asad1norm(state, _params)) ) { state.curalgo = 0; goto lbl_50; } if( diffcnt>0 ) { if( asauisempty(state, _params) || diffcnt>=n2 ) { state.curalgo = 1; } else { state.curalgo = 0; } goto lbl_50; } // // Calculate D(k+1) // // Line search may result in: // * maximum feasible step being taken (already processed) // * point satisfying Wolfe conditions // * some kind of error (CG is restarted by assigning 0.0 to Beta) // if( mcinfo==1 ) { // // Standard Wolfe conditions are satisfied: // * calculate Y[K] and BetaK // for(i_=0; i_<=n-1;i_++) { state.yk[i_] = state.yk[i_] + state.gc[i_]; } vv = 0.0; for(i_=0; i_<=n-1;i_++) { vv += state.yk[i_]*state.dk[i_]; } v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.gc[i_]*state.gc[i_]; } state.betady = v/vv; v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.gc[i_]*state.yk[i_]; } state.betahs = v/vv; if( state.cgtype==0 ) { betak = state.betady; } if( state.cgtype==1 ) { betak = Math.Max(0, Math.Min(state.betady, state.betahs)); } } else { // // Something is wrong (may be function is too wild or too flat). // // We'll set BetaK=0, which will restart CG algorithm. // We can stop later (during normal checks) if stopping conditions are met. // betak = 0; state.debugrestartscount = state.debugrestartscount+1; } for(i_=0; i_<=n-1;i_++) { state.dn[i_] = -state.gc[i_]; } for(i_=0; i_<=n-1;i_++) { state.dn[i_] = state.dn[i_] + betak*state.dk[i_]; } for(i_=0; i_<=n-1;i_++) { state.dk[i_] = state.dn[i_]; } // // update other information // state.fold = state.f; state.k = state.k+1; goto lbl_49; lbl_50: lbl_47: goto lbl_17; lbl_18: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = n; state.rstate.ia[1] = i; state.rstate.ia[2] = mcinfo; state.rstate.ia[3] = diffcnt; state.rstate.ba[0] = b; state.rstate.ba[1] = stepfound; state.rstate.ra[0] = betak; state.rstate.ra[1] = v; state.rstate.ra[2] = vv; return result; } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minasaresults(minasastate state, ref double[] x, minasareport rep, alglib.xparams _params) { x = new double[0]; minasaresultsbuf(state, ref x, rep, _params); } /************************************************************************* Obsolete optimization algorithm. Was replaced by MinBLEIC subpackage. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minasaresultsbuf(minasastate state, ref double[] x, minasareport rep, alglib.xparams _params) { int i = 0; int i_ = 0; if( alglib.ap.len(x)=state.n, "MinASARestartFrom: Length(X)=state.n, "MinASARestartFrom: Length(BndL)=state.n, "MinASARestartFrom: Length(BndU)0)and(x[i]=bndu[i])) v[i]=-g[i] otherwise This function may be used to check a stopping criterion. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ private static double asaboundedantigradnorm(minasastate state, alglib.xparams _params) { double result = 0; int i = 0; double v = 0; result = 0; for(i=0; i<=state.n-1; i++) { v = -state.g[i]; if( (double)(state.x[i])==(double)(state.bndl[i]) && (double)(-state.g[i])<(double)(0) ) { v = 0; } if( (double)(state.x[i])==(double)(state.bndu[i]) && (double)(-state.g[i])>(double)(0) ) { v = 0; } result = result+math.sqr(v); } result = Math.Sqrt(result); return result; } /************************************************************************* Returns norm of GI(x). GI(x) is a gradient vector whose components associated with active constraints are zeroed. It differs from bounded anti-gradient because components of GI(x) are zeroed independently of sign(g[i]), and anti-gradient's components are zeroed with respect to both constraint and sign. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ private static double asaginorm(minasastate state, alglib.xparams _params) { double result = 0; int i = 0; result = 0; for(i=0; i<=state.n-1; i++) { if( (double)(state.x[i])!=(double)(state.bndl[i]) && (double)(state.x[i])!=(double)(state.bndu[i]) ) { result = result+math.sqr(state.g[i]); } } result = Math.Sqrt(result); return result; } /************************************************************************* Returns norm(D1(State.X)) For a meaning of D1 see 'NEW ACTIVE SET ALGORITHM FOR BOX CONSTRAINED OPTIMIZATION' by WILLIAM W. HAGER AND HONGCHAO ZHANG. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ private static double asad1norm(minasastate state, alglib.xparams _params) { double result = 0; int i = 0; result = 0; for(i=0; i<=state.n-1; i++) { result = result+math.sqr(apserv.boundval(state.x[i]-state.g[i], state.bndl[i], state.bndu[i], _params)-state.x[i]); } result = Math.Sqrt(result); return result; } /************************************************************************* Returns True, if U set is empty. * State.X is used as point, * State.G - as gradient, * D is calculated within function (because State.D may have different meaning depending on current optimization algorithm) For a meaning of U see 'NEW ACTIVE SET ALGORITHM FOR BOX CONSTRAINED OPTIMIZATION' by WILLIAM W. HAGER AND HONGCHAO ZHANG. -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ private static bool asauisempty(minasastate state, alglib.xparams _params) { bool result = new bool(); int i = 0; double d = 0; double d2 = 0; double d32 = 0; d = asad1norm(state, _params); d2 = Math.Sqrt(d); d32 = d*d2; result = true; for(i=0; i<=state.n-1; i++) { if( (double)(Math.Abs(state.g[i]))>=(double)(d2) && (double)(Math.Min(state.x[i]-state.bndl[i], state.bndu[i]-state.x[i]))>=(double)(d32) ) { result = false; return result; } } return result; } /************************************************************************* Clears request fileds (to be sure that we don't forgot to clear something) *************************************************************************/ private static void clearrequestfields(minasastate state, alglib.xparams _params) { state.needfg = false; state.xupdated = false; } } public class mincg { /************************************************************************* This object stores state of the nonlinear CG optimizer. You should use ALGLIB functions to work with this object. *************************************************************************/ public class mincgstate : apobject { public int n; public double epsg; public double epsf; public double epsx; public int maxits; public double stpmax; public double suggestedstep; public bool xrep; public bool drep; public int cgtype; public int prectype; public double[] diagh; public double[] diaghl2; public double[,] vcorr; public int vcnt; public double[] s; public double diffstep; public int nfev; public int mcstage; public int k; public double[] xk; public double[] dk; public double[] xn; public double[] dn; public double[] d; public double fold; public double stp; public double curstpmax; public double[] yk; public double lastgoodstep; public double lastscaledstep; public int mcinfo; public bool innerresetneeded; public bool terminationneeded; public double trimthreshold; public double[] xbase; public int rstimer; public double[] x; public double f; public double[] g; public bool needf; public bool needfg; public bool xupdated; public bool algpowerup; public bool lsstart; public bool lsend; public bool userterminationneeded; public rcommstate rstate; public int repiterationscount; public int repnfev; public int repterminationtype; public int debugrestartscount; public linmin.linminstate lstate; public double fbase; public double fm2; public double fm1; public double fp1; public double fp2; public double betahs; public double betady; public double[] work0; public double[] work1; public double[] invs; public double teststep; public int smoothnessguardlevel; public optserv.smoothnessmonitor smonitor; public double[] lastscaleused; public mincgstate() { init(); } public override void init() { diagh = new double[0]; diaghl2 = new double[0]; vcorr = new double[0,0]; s = new double[0]; xk = new double[0]; dk = new double[0]; xn = new double[0]; dn = new double[0]; d = new double[0]; yk = new double[0]; xbase = new double[0]; x = new double[0]; g = new double[0]; rstate = new rcommstate(); lstate = new linmin.linminstate(); work0 = new double[0]; work1 = new double[0]; invs = new double[0]; smonitor = new optserv.smoothnessmonitor(); lastscaleused = new double[0]; } public override alglib.apobject make_copy() { mincgstate _result = new mincgstate(); _result.n = n; _result.epsg = epsg; _result.epsf = epsf; _result.epsx = epsx; _result.maxits = maxits; _result.stpmax = stpmax; _result.suggestedstep = suggestedstep; _result.xrep = xrep; _result.drep = drep; _result.cgtype = cgtype; _result.prectype = prectype; _result.diagh = (double[])diagh.Clone(); _result.diaghl2 = (double[])diaghl2.Clone(); _result.vcorr = (double[,])vcorr.Clone(); _result.vcnt = vcnt; _result.s = (double[])s.Clone(); _result.diffstep = diffstep; _result.nfev = nfev; _result.mcstage = mcstage; _result.k = k; _result.xk = (double[])xk.Clone(); _result.dk = (double[])dk.Clone(); _result.xn = (double[])xn.Clone(); _result.dn = (double[])dn.Clone(); _result.d = (double[])d.Clone(); _result.fold = fold; _result.stp = stp; _result.curstpmax = curstpmax; _result.yk = (double[])yk.Clone(); _result.lastgoodstep = lastgoodstep; _result.lastscaledstep = lastscaledstep; _result.mcinfo = mcinfo; _result.innerresetneeded = innerresetneeded; _result.terminationneeded = terminationneeded; _result.trimthreshold = trimthreshold; _result.xbase = (double[])xbase.Clone(); _result.rstimer = rstimer; _result.x = (double[])x.Clone(); _result.f = f; _result.g = (double[])g.Clone(); _result.needf = needf; _result.needfg = needfg; _result.xupdated = xupdated; _result.algpowerup = algpowerup; _result.lsstart = lsstart; _result.lsend = lsend; _result.userterminationneeded = userterminationneeded; _result.rstate = (rcommstate)rstate.make_copy(); _result.repiterationscount = repiterationscount; _result.repnfev = repnfev; _result.repterminationtype = repterminationtype; _result.debugrestartscount = debugrestartscount; _result.lstate = (linmin.linminstate)lstate.make_copy(); _result.fbase = fbase; _result.fm2 = fm2; _result.fm1 = fm1; _result.fp1 = fp1; _result.fp2 = fp2; _result.betahs = betahs; _result.betady = betady; _result.work0 = (double[])work0.Clone(); _result.work1 = (double[])work1.Clone(); _result.invs = (double[])invs.Clone(); _result.teststep = teststep; _result.smoothnessguardlevel = smoothnessguardlevel; _result.smonitor = (optserv.smoothnessmonitor)smonitor.make_copy(); _result.lastscaleused = (double[])lastscaleused.Clone(); return _result; } }; /************************************************************************* This structure stores optimization report: * IterationsCount total number of inner iterations * NFEV number of gradient evaluations * TerminationType termination type (see below) TERMINATION CODES TerminationType field contains completion code, which can be: -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. 1 relative function improvement is no more than EpsF. 2 relative step is no more than EpsX. 4 gradient norm is no more than EpsG 5 MaxIts steps was taken 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. 8 terminated by user who called mincgrequesttermination(). X contains point which was "current accepted" when termination request was submitted. Other fields of this structure are not documented and should not be used! *************************************************************************/ public class mincgreport : apobject { public int iterationscount; public int nfev; public int terminationtype; public mincgreport() { init(); } public override void init() { } public override alglib.apobject make_copy() { mincgreport _result = new mincgreport(); _result.iterationscount = iterationscount; _result.nfev = nfev; _result.terminationtype = terminationtype; return _result; } }; public const int rscountdownlen = 10; public const double gtol = 0.3; /************************************************************************* NONLINEAR CONJUGATE GRADIENT METHOD DESCRIPTION: The subroutine minimizes function F(x) of N arguments by using one of the nonlinear conjugate gradient methods. These CG methods are globally convergent (even on non-convex functions) as long as grad(f) is Lipschitz continuous in a some neighborhood of the L = { x : f(x)<=f(x0) }. REQUIREMENTS: Algorithm will request following information during its operation: * function value F and its gradient G (simultaneously) at given point X USAGE: 1. User initializes algorithm state with MinCGCreate() call 2. User tunes solver parameters with MinCGSetCond(), MinCGSetStpMax() and other functions 3. User calls MinCGOptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G. 4. User calls MinCGResults() to get solution 5. Optionally, user may call MinCGRestartFrom() to solve another problem with same N but another starting point and/or another function. MinCGRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[0..N-1]. OUTPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 25.03.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgcreate(int n, double[] x, mincgstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinCGCreate: N too small!"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinCGCreate: Length(X)0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[0..N-1]. DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinCGSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large truncation errors, while too small step will result in too large numerical errors. 1.0E-6 can be good value to start with. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. L-BFGS needs exact gradient values. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 16.05.2011 by Bochkanov Sergey *************************************************************************/ public static void mincgcreatef(int n, double[] x, double diffstep, mincgstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinCGCreateF: N too small!"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinCGCreateF: Length(X)(double)(0), "MinCGCreateF: DiffStep is non-positive!"); mincginitinternal(n, diffstep, state, _params); mincgrestartfrom(state, x, _params); } /************************************************************************* This function sets stopping conditions for CG optimization algorithm. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|=0 The subroutine finishes its work if on k+1-th iteration the condition |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - ste pvector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinCGSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (small EpsX). -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetcond(mincgstate state, double epsg, double epsf, double epsx, int maxits, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsg), "MinCGSetCond: EpsG is not finite number!"); alglib.ap.assert((double)(epsg)>=(double)(0), "MinCGSetCond: negative EpsG!"); alglib.ap.assert(math.isfinite(epsf), "MinCGSetCond: EpsF is not finite number!"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinCGSetCond: negative EpsF!"); alglib.ap.assert(math.isfinite(epsx), "MinCGSetCond: EpsX is not finite number!"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinCGSetCond: negative EpsX!"); alglib.ap.assert(maxits>=0, "MinCGSetCond: negative MaxIts!"); if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxits==0 ) { epsx = 1.0E-6; } state.epsg = epsg; state.epsf = epsf; state.epsx = epsx; state.maxits = maxits; } /************************************************************************* This function sets scaling coefficients for CG optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of CG optimizer - step along I-th axis is equal to DiffStep*S[I]. In most optimizers (and in the CG too) scaling is NOT a form of preconditioning. It just affects stopping conditions. You should set preconditioner by separate call to one of the MinCGSetPrec...() functions. There is special preconditioning mode, however, which uses scaling coefficients to form diagonal preconditioning matrix. You can turn this mode on, if you want. But you should understand that scaling is not the same thing as preconditioning - these are two different, although related forms of tuning solver. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void mincgsetscale(mincgstate state, double[] s, alglib.xparams _params) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.n, "MinCGSetScale: Length(S)=-1 && cgtype<=1, "MinCGSetCGType: incorrect CGType!"); if( cgtype==-1 ) { cgtype = 1; } state.cgtype = cgtype; } /************************************************************************* This function sets maximum step length INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetstpmax(mincgstate state, double stpmax, alglib.xparams _params) { alglib.ap.assert(math.isfinite(stpmax), "MinCGSetStpMax: StpMax is not finite!"); alglib.ap.assert((double)(stpmax)>=(double)(0), "MinCGSetStpMax: StpMax<0!"); state.stpmax = stpmax; } /************************************************************************* This function allows to suggest initial step length to the CG algorithm. Suggested step length is used as starting point for the line search. It can be useful when you have badly scaled problem, i.e. when ||grad|| (which is used as initial estimate for the first step) is many orders of magnitude different from the desired step. Line search may fail on such problems without good estimate of initial step length. Imagine, for example, problem with ||grad||=10^50 and desired step equal to 0.1 Line search function will use 10^50 as initial step, then it will decrease step length by 2 (up to 20 attempts) and will get 10^44, which is still too large. This function allows us to tell than line search should be started from some moderate step length, like 1.0, so algorithm will be able to detect desired step length in a several searches. Default behavior (when no step is suggested) is to use preconditioner, if it is available, to generate initial estimate of step length. This function influences only first iteration of algorithm. It should be called between MinCGCreate/MinCGRestartFrom() call and MinCGOptimize call. Suggested step is ignored if you have preconditioner. INPUT PARAMETERS: State - structure used to store algorithm state. Stp - initial estimate of the step length. Can be zero (no estimate). -- ALGLIB -- Copyright 30.07.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsuggeststep(mincgstate state, double stp, alglib.xparams _params) { alglib.ap.assert(math.isfinite(stp), "MinCGSuggestStep: Stp is infinite or NAN"); alglib.ap.assert((double)(stp)>=(double)(0), "MinCGSuggestStep: Stp<0"); state.suggestedstep = stp; } /************************************************************************* This developer-only function allows to retrieve unscaled (!) length of last good step (i.e. step which resulted in sufficient decrease of target function). It can be used in for solution of sequential optimization subproblems, where MinCGSuggestStep() is called with length of previous step as parameter. INPUT PARAMETERS: State - structure used to store algorithm state. RESULT: length of last good step being accepted NOTE: result of this function is undefined if you called it before -- ALGLIB -- Copyright 30.07.2010 by Bochkanov Sergey *************************************************************************/ public static double mincglastgoodstep(mincgstate state, alglib.xparams _params) { double result = 0; result = state.lastgoodstep; return result; } /************************************************************************* Modification of the preconditioner: preconditioning is turned off. INPUT PARAMETERS: State - structure which stores algorithm state NOTE: you can change preconditioner "on the fly", during algorithm iterations. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetprecdefault(mincgstate state, alglib.xparams _params) { state.prectype = 0; state.innerresetneeded = true; } /************************************************************************* Modification of the preconditioner: diagonal of approximate Hessian is used. INPUT PARAMETERS: State - structure which stores algorithm state D - diagonal of the approximate Hessian, array[0..N-1], (if larger, only leading N elements are used). NOTE: you can change preconditioner "on the fly", during algorithm iterations. NOTE 2: D[i] should be positive. Exception will be thrown otherwise. NOTE 3: you should pass diagonal of approximate Hessian - NOT ITS INVERSE. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetprecdiag(mincgstate state, double[] d, alglib.xparams _params) { int i = 0; alglib.ap.assert(alglib.ap.len(d)>=state.n, "MinCGSetPrecDiag: D is too short"); for(i=0; i<=state.n-1; i++) { alglib.ap.assert(math.isfinite(d[i]), "MinCGSetPrecDiag: D contains infinite or NAN elements"); alglib.ap.assert((double)(d[i])>(double)(0), "MinCGSetPrecDiag: D contains non-positive elements"); } mincgsetprecdiagfast(state, d, _params); } /************************************************************************* Modification of the preconditioner: scale-based diagonal preconditioning. This preconditioning mode can be useful when you don't have approximate diagonal of Hessian, but you know that your variables are badly scaled (for example, one variable is in [1,10], and another in [1000,100000]), and most part of the ill-conditioning comes from different scales of vars. In this case simple scale-based preconditioner, with H[i] = 1/(s[i]^2), can greatly improve convergence. IMPRTANT: you should set scale of your variables with MinCGSetScale() call (before or after MinCGSetPrecScale() call). Without knowledge of the scale of your variables scale-based preconditioner will be just unit matrix. INPUT PARAMETERS: State - structure which stores algorithm state NOTE: you can change preconditioner "on the fly", during algorithm iterations. -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ public static void mincgsetprecscale(mincgstate state, alglib.xparams _params) { state.prectype = 3; state.innerresetneeded = true; } /************************************************************************* NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied gradient, and one which uses function value only and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object (either MinCGCreate() for analytical gradient or MinCGCreateF() for numerical differentiation) you should choose appropriate variant of MinCGOptimize() - one which accepts function AND gradient or one which accepts function ONLY. Be careful to choose variant of MinCGOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinCGOptimize() and specific function used to create optimizer. | USER PASSED TO MinCGOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinCGCreateF() | work FAIL MinCGCreate() | FAIL work Here "FAIL" denotes inappropriate combinations of optimizer creation function and MinCGOptimize() version. Attemps to use such combination (for example, to create optimizer with MinCGCreateF() and to pass gradient information to MinCGOptimize()) will lead to exception being thrown. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 20.04.2009 by Bochkanov Sergey *************************************************************************/ public static bool mincgiteration(mincgstate state, alglib.xparams _params) { bool result = new bool(); int n = 0; int i = 0; double betak = 0; double v = 0; double vv = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { n = state.rstate.ia[0]; i = state.rstate.ia[1]; betak = state.rstate.ra[0]; v = state.rstate.ra[1]; vv = state.rstate.ra[2]; } else { n = 359; i = -58; betak = -919; v = -909; vv = 81; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } if( state.rstate.stage==4 ) { goto lbl_4; } if( state.rstate.stage==5 ) { goto lbl_5; } if( state.rstate.stage==6 ) { goto lbl_6; } if( state.rstate.stage==7 ) { goto lbl_7; } if( state.rstate.stage==8 ) { goto lbl_8; } if( state.rstate.stage==9 ) { goto lbl_9; } if( state.rstate.stage==10 ) { goto lbl_10; } if( state.rstate.stage==11 ) { goto lbl_11; } if( state.rstate.stage==12 ) { goto lbl_12; } if( state.rstate.stage==13 ) { goto lbl_13; } if( state.rstate.stage==14 ) { goto lbl_14; } if( state.rstate.stage==15 ) { goto lbl_15; } if( state.rstate.stage==16 ) { goto lbl_16; } if( state.rstate.stage==17 ) { goto lbl_17; } // // Routine body // // // Prepare // n = state.n; state.terminationneeded = false; state.userterminationneeded = false; state.repterminationtype = 0; state.repiterationscount = 0; state.repnfev = 0; state.debugrestartscount = 0; optserv.smoothnessmonitorinit(state.smonitor, n, 1, state.smoothnessguardlevel>0, _params); apserv.rvectorsetlengthatleast(ref state.invs, n, _params); for(i=0; i<=n-1; i++) { state.lastscaleused[i] = state.s[i]; state.invs[i] = 1/state.s[i]; } // // Check, that transferred derivative value is right // clearrequestfields(state, _params); if( !((double)(state.diffstep)==(double)(0) && (double)(state.teststep)>(double)(0)) ) { goto lbl_18; } lbl_20: if( !optserv.smoothnessmonitorcheckgradientatx0(state.smonitor, state.xbase, state.s, state.s, state.s, false, state.teststep, _params) ) { goto lbl_21; } for(i=0; i<=n-1; i++) { state.x[i] = state.smonitor.x[i]; } state.needfg = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needfg = false; state.smonitor.fi[0] = state.f; for(i=0; i<=n-1; i++) { state.smonitor.j[0,i] = state.g[i]; } goto lbl_20; lbl_21: lbl_18: // // Preparations continue: // * set XK // * calculate F/G // * set DK to -G // * powerup algo (it may change preconditioner) // * apply preconditioner to DK // * report update of X // * check stopping conditions for G // for(i=0; i<=n-1; i++) { state.x[i] = state.xbase[i]; } for(i_=0; i_<=n-1;i_++) { state.xk[i_] = state.x[i_]; } clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_22; } state.needfg = true; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: state.needfg = false; goto lbl_23; lbl_22: state.needf = true; state.rstate.stage = 2; goto lbl_rcomm; lbl_2: state.fbase = state.f; i = 0; lbl_24: if( i>n-1 ) { goto lbl_26; } v = state.x[i]; state.x[i] = v-state.diffstep*state.s[i]; state.rstate.stage = 3; goto lbl_rcomm; lbl_3: state.fm2 = state.f; state.x[i] = v-0.5*state.diffstep*state.s[i]; state.rstate.stage = 4; goto lbl_rcomm; lbl_4: state.fm1 = state.f; state.x[i] = v+0.5*state.diffstep*state.s[i]; state.rstate.stage = 5; goto lbl_rcomm; lbl_5: state.fp1 = state.f; state.x[i] = v+state.diffstep*state.s[i]; state.rstate.stage = 6; goto lbl_rcomm; lbl_6: state.fp2 = state.f; state.x[i] = v; state.g[i] = (8*(state.fp1-state.fm1)-(state.fp2-state.fm2))/(6*state.diffstep*state.s[i]); i = i+1; goto lbl_24; lbl_26: state.f = state.fbase; state.needf = false; lbl_23: if( !state.drep ) { goto lbl_27; } // // Report algorithm powerup (if needed) // clearrequestfields(state, _params); state.algpowerup = true; state.rstate.stage = 7; goto lbl_rcomm; lbl_7: state.algpowerup = false; lbl_27: optserv.trimprepare(state.f, ref state.trimthreshold, _params); for(i_=0; i_<=n-1;i_++) { state.dk[i_] = -state.g[i_]; } preconditionedmultiply(state, ref state.dk, ref state.work0, ref state.work1, _params); if( !state.xrep ) { goto lbl_29; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 8; goto lbl_rcomm; lbl_8: state.xupdated = false; lbl_29: if( state.terminationneeded || state.userterminationneeded ) { // // Combined termination point for "internal" termination by TerminationNeeded flag // and for "user" termination by MinCGRequestTermination() (UserTerminationNeeded flag). // In this location rules for both of methods are same, thus only one exit point is needed. // for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xk[i_]; } state.repterminationtype = 8; result = false; return result; } v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.g[i]*state.s[i]); } if( (double)(Math.Sqrt(v))<=(double)(state.epsg) ) { for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xk[i_]; } state.repterminationtype = 4; result = false; return result; } state.repnfev = 1; state.k = 0; state.fold = state.f; // // Choose initial step. // Apply preconditioner, if we have something other than default. // if( state.prectype==2 || state.prectype==3 ) { // // because we use preconditioner, step length must be equal // to the norm of DK // v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.dk[i_]*state.dk[i_]; } state.lastgoodstep = Math.Sqrt(v); } else { // // No preconditioner is used, we try to use suggested step // if( (double)(state.suggestedstep)>(double)(0) ) { state.lastgoodstep = state.suggestedstep; } else { state.lastgoodstep = 1.0; } } // // Main cycle // state.rstimer = rscountdownlen; lbl_31: if( false ) { goto lbl_32; } // // * clear reset flag // * clear termination flag // * store G[k] for later calculation of Y[k] // * prepare starting point and direction and step length for line search // state.innerresetneeded = false; state.terminationneeded = false; for(i_=0; i_<=n-1;i_++) { state.yk[i_] = -state.g[i_]; } for(i_=0; i_<=n-1;i_++) { state.d[i_] = state.dk[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xk[i_]; } state.mcstage = 0; state.stp = 1.0; linmin.linminnormalized(ref state.d, ref state.stp, n, _params); if( (double)(state.lastgoodstep)!=(double)(0) ) { state.stp = state.lastgoodstep; } state.curstpmax = state.stpmax; // // Report beginning of line search (if needed) // Terminate algorithm, if user request was detected // if( !state.drep ) { goto lbl_33; } clearrequestfields(state, _params); state.lsstart = true; state.rstate.stage = 9; goto lbl_rcomm; lbl_9: state.lsstart = false; lbl_33: if( state.terminationneeded ) { for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.x[i_]; } state.repterminationtype = 8; result = false; return result; } // // Minimization along D // optserv.smoothnessmonitorstartlinesearch1u(state.smonitor, state.s, state.invs, state.x, state.f, state.g, _params); linmin.mcsrch(n, ref state.x, ref state.f, ref state.g, state.d, ref state.stp, state.curstpmax, gtol, ref state.mcinfo, ref state.nfev, ref state.work0, state.lstate, ref state.mcstage, _params); lbl_35: if( state.mcstage==0 ) { goto lbl_36; } // // Calculate function/gradient using either // analytical gradient supplied by user // or finite difference approximation. // // "Trim" function in order to handle near-singularity points. // clearrequestfields(state, _params); if( (double)(state.diffstep)!=(double)(0) ) { goto lbl_37; } state.needfg = true; state.rstate.stage = 10; goto lbl_rcomm; lbl_10: state.needfg = false; goto lbl_38; lbl_37: state.needf = true; state.rstate.stage = 11; goto lbl_rcomm; lbl_11: state.fbase = state.f; i = 0; lbl_39: if( i>n-1 ) { goto lbl_41; } v = state.x[i]; state.x[i] = v-state.diffstep*state.s[i]; state.rstate.stage = 12; goto lbl_rcomm; lbl_12: state.fm2 = state.f; state.x[i] = v-0.5*state.diffstep*state.s[i]; state.rstate.stage = 13; goto lbl_rcomm; lbl_13: state.fm1 = state.f; state.x[i] = v+0.5*state.diffstep*state.s[i]; state.rstate.stage = 14; goto lbl_rcomm; lbl_14: state.fp1 = state.f; state.x[i] = v+state.diffstep*state.s[i]; state.rstate.stage = 15; goto lbl_rcomm; lbl_15: state.fp2 = state.f; state.x[i] = v; state.g[i] = (8*(state.fp1-state.fm1)-(state.fp2-state.fm2))/(6*state.diffstep*state.s[i]); i = i+1; goto lbl_39; lbl_41: state.f = state.fbase; state.needf = false; lbl_38: optserv.smoothnessmonitorenqueuepoint1u(state.smonitor, state.s, state.invs, state.d, state.stp, state.x, state.f, state.g, _params); optserv.trimfunction(ref state.f, ref state.g, n, state.trimthreshold, _params); // // Call MCSRCH again // linmin.mcsrch(n, ref state.x, ref state.f, ref state.g, state.d, ref state.stp, state.curstpmax, gtol, ref state.mcinfo, ref state.nfev, ref state.work0, state.lstate, ref state.mcstage, _params); goto lbl_35; lbl_36: optserv.smoothnessmonitorfinalizelinesearch(state.smonitor, _params); // // * terminate algorithm if "user" request for detected // * report end of line search // * store current point to XN // * report iteration // * terminate algorithm if "internal" request was detected // if( state.userterminationneeded ) { for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xk[i_]; } state.repterminationtype = 8; result = false; return result; } if( !state.drep ) { goto lbl_42; } // // Report end of line search (if needed) // clearrequestfields(state, _params); state.lsend = true; state.rstate.stage = 16; goto lbl_rcomm; lbl_16: state.lsend = false; lbl_42: for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.x[i_]; } if( !state.xrep ) { goto lbl_44; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 17; goto lbl_rcomm; lbl_17: state.xupdated = false; lbl_44: if( state.terminationneeded ) { for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.x[i_]; } state.repterminationtype = 8; result = false; return result; } // // Line search is finished. // * calculate BetaK // * calculate DN // * update timers // * calculate step length: // * LastScaledStep is ALWAYS calculated because it is used in the stopping criteria // * LastGoodStep is updated only when MCINFO is equal to 1 (Wolfe conditions hold). // See below for more explanation. // if( state.mcinfo==1 && !state.innerresetneeded ) { // // Standard Wolfe conditions hold // Calculate Y[K] and D[K]'*Y[K] // for(i_=0; i_<=n-1;i_++) { state.yk[i_] = state.yk[i_] + state.g[i_]; } vv = 0.0; for(i_=0; i_<=n-1;i_++) { vv += state.yk[i_]*state.dk[i_]; } // // Calculate BetaK according to DY formula // v = preconditionedmultiply2(state, ref state.g, ref state.g, ref state.work0, ref state.work1, _params); state.betady = v/vv; // // Calculate BetaK according to HS formula // v = preconditionedmultiply2(state, ref state.g, ref state.yk, ref state.work0, ref state.work1, _params); state.betahs = v/vv; // // Choose BetaK // if( state.cgtype==0 ) { betak = state.betady; } if( state.cgtype==1 ) { betak = Math.Max(0, Math.Min(state.betady, state.betahs)); } } else { // // Something is wrong (may be function is too wild or too flat) // or we just have to restart algo. // // We'll set BetaK=0, which will restart CG algorithm. // We can stop later (during normal checks) if stopping conditions are met. // betak = 0; state.debugrestartscount = state.debugrestartscount+1; } if( state.repiterationscount>0 && state.repiterationscount%(3+n)==0 ) { // // clear Beta every N iterations // betak = 0; } if( state.mcinfo==1 || state.mcinfo==5 ) { state.rstimer = rscountdownlen; } else { state.rstimer = state.rstimer-1; } for(i_=0; i_<=n-1;i_++) { state.dn[i_] = -state.g[i_]; } preconditionedmultiply(state, ref state.dn, ref state.work0, ref state.work1, _params); for(i_=0; i_<=n-1;i_++) { state.dn[i_] = state.dn[i_] + betak*state.dk[i_]; } state.lastscaledstep = 0.0; for(i=0; i<=n-1; i++) { state.lastscaledstep = state.lastscaledstep+math.sqr(state.d[i]/state.s[i]); } state.lastscaledstep = state.stp*Math.Sqrt(state.lastscaledstep); if( state.mcinfo==1 ) { // // Step is good (Wolfe conditions hold), update LastGoodStep. // // This check for MCINFO=1 is essential because sometimes in the // constrained optimization setting we may take very short steps // (like 1E-15) because we were very close to boundary of the // feasible area. Such short step does not mean that we've converged // to the solution - it was so short because we were close to the // boundary and there was a limit on step length. // // So having such short step is quite normal situation. However, we // should NOT start next iteration from step whose initial length is // estimated as 1E-15 because it may lead to the failure of the // linear minimizer (step is too short, function does not changes, // line search stagnates). // state.lastgoodstep = 0; for(i=0; i<=n-1; i++) { state.lastgoodstep = state.lastgoodstep+math.sqr(state.d[i]); } state.lastgoodstep = state.stp*Math.Sqrt(state.lastgoodstep); } // // Update information. // Check stopping conditions. // v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.g[i]*state.s[i]); } if( !math.isfinite(v) || !math.isfinite(state.f) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; result = false; return result; } state.repnfev = state.repnfev+state.nfev; state.repiterationscount = state.repiterationscount+1; if( state.repiterationscount>=state.maxits && state.maxits>0 ) { // // Too many iterations // state.repterminationtype = 5; result = false; return result; } if( (double)(Math.Sqrt(v))<=(double)(state.epsg) ) { // // Gradient is small enough // state.repterminationtype = 4; result = false; return result; } if( !state.innerresetneeded ) { // // These conditions are checked only when no inner reset was requested by user // if( (double)(state.fold-state.f)<=(double)(state.epsf*Math.Max(Math.Abs(state.fold), Math.Max(Math.Abs(state.f), 1.0))) ) { // // F(k+1)-F(k) is small enough // state.repterminationtype = 1; result = false; return result; } if( (double)(state.lastscaledstep)<=(double)(state.epsx) ) { // // X(k+1)-X(k) is small enough // state.repterminationtype = 2; result = false; return result; } } if( state.rstimer<=0 ) { // // Too many subsequent restarts // state.repterminationtype = 7; result = false; return result; } // // Shift Xk/Dk, update other information // for(i_=0; i_<=n-1;i_++) { state.xk[i_] = state.xn[i_]; } for(i_=0; i_<=n-1;i_++) { state.dk[i_] = state.dn[i_]; } state.fold = state.f; state.k = state.k+1; goto lbl_31; lbl_32: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = n; state.rstate.ia[1] = i; state.rstate.ra[0] = betak; state.rstate.ra[1] = v; state.rstate.ra[2] = vv; return result; } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic gradient. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function at the initial point (note: future versions may also perform check at the final point) and compares numerical gradient with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both gradients and specific components highlighted as suspicious by the OptGuard. The primary OptGuard report can be retrieved with mincgoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with mincgsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardgradient(mincgstate state, double teststep, alglib.xparams _params) { alglib.ap.assert(math.isfinite(teststep), "MinCGOptGuardGradient: TestStep contains NaN or INF"); alglib.ap.assert((double)(teststep)>=(double)(0), "MinCGOptGuardGradient: invalid argument TestStep(TestStep<0)"); state.teststep = teststep; } /************************************************************************* This function activates/deactivates nonsmoothness monitoring option of the OptGuard integrity checker. Smoothness monitor silently observes solution process and tries to detect ill-posed problems, i.e. ones with: a) discontinuous target function (non-C0) b) nonsmooth target function (non-C1) Smoothness monitoring does NOT interrupt optimization even if it suspects that your problem is nonsmooth. It just sets corresponding flags in the OptGuard report which can be retrieved after optimization is over. Smoothness monitoring is a moderate overhead option which often adds less than 1% to the optimizer running time. Thus, you can use it even for large scale problems. NOTE: OptGuard does NOT guarantee that it will always detect C0/C1 continuity violations. First, minor errors are hard to catch - say, a 0.0001 difference in the model values at two sides of the gap may be due to discontinuity of the model - or simply because the model has changed. Second, C1-violations are especially difficult to detect in a noninvasive way. The optimizer usually performs very short steps near the nonsmoothness, and differentiation usually introduces a lot of numerical noise. It is hard to tell whether some tiny discontinuity in the slope is due to real nonsmoothness or just due to numerical noise alone. Our top priority was to avoid false positives, so in some rare cases minor errors may went unnoticed (however, in most cases they can be spotted with restart from different initial point). INPUT PARAMETERS: state - algorithm state level - monitoring level: * 0 - monitoring is disabled * 1 - noninvasive low-overhead monitoring; function values and/or gradients are recorded, but OptGuard does not try to perform additional evaluations in order to get more information about suspicious locations. === EXPLANATION ========================================================== One major source of headache during optimization is the possibility of the coding errors in the target function/constraints (or their gradients). Such errors most often manifest themselves as discontinuity or nonsmoothness of the target/constraints. Another frequent situation is when you try to optimize something involving lots of min() and max() operations, i.e. nonsmooth target. Although not a coding error, it is nonsmoothness anyway - and smooth optimizers usually stop right after encountering nonsmoothness, well before reaching solution. OptGuard integrity checker helps you to catch such situations: it monitors function values/gradients being passed to the optimizer and tries to errors. Upon discovering suspicious pair of points it raises appropriate flag (and allows you to continue optimization). When optimization is done, you can study OptGuard result. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardsmoothness(mincgstate state, int level, alglib.xparams _params) { alglib.ap.assert(level==0 || level==1, "MinCGOptGuardSmoothness: unexpected value of level parameter"); state.smoothnessguardlevel = level; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. === PRIMARY REPORT ======================================================= OptGuard performs several checks which are intended to catch common errors in the implementation of nonlinear function/gradient: * incorrect analytic gradient * discontinuous (non-C0) target functions (constraints) * nonsmooth (non-C1) target functions (constraints) Each of these checks is activated with appropriate function: * mincgoptguardgradient() for gradient verification * mincgoptguardsmoothness() for C0/C1 checks Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradvidx for specific variable (gradient element) suspected * rep.badgradxbase, a point where gradient is tested * rep.badgraduser, user-provided gradient (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.badgradnum, reference gradient obtained via numerical differentiation (stored as 2D matrix with single row in order to make report structure compatible with more complex optimizers like MinNLC or MinLM) * rep.nonc0suspected * rep.nonc1suspected === ADDITIONAL REPORTS/LOGS ============================================== Several different tests are performed to catch C0/C1 errors, you can find out specific test signaled error by looking to: * rep.nonc0test0positive, for non-C0 test #0 * rep.nonc1test0positive, for non-C1 test #0 * rep.nonc1test1positive, for non-C1 test #1 Additional information (including line search logs) can be obtained by means of: * mincgoptguardnonc1test0results() * mincgoptguardnonc1test1results() which return detailed error reports, specific points where discontinuities were found, and so on. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - generic OptGuard report; more detailed reports can be retrieved with other functions. NOTE: false negatives (nonsmooth problems are not identified as nonsmooth ones) are possible although unlikely. The reason is that you need to make several evaluations around nonsmoothness in order to accumulate enough information about function curvature. Say, if you start right from the nonsmooth point, optimizer simply won't get enough data to understand what is going wrong before it terminates due to abrupt changes in the derivative. It is also possible that "unlucky" step will move us to the termination too quickly. Our current approach is to have less than 0.1% false negatives in our test examples (measured with multiple restarts from random points), and to have exactly 0% false positives. -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardresults(mincgstate state, optguardapi.optguardreport rep, alglib.xparams _params) { optserv.smoothnessmonitorexportreport(state.smonitor, rep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #0 Nonsmoothness (non-C1) test #0 studies function values (not gradient!) obtained during line searches and monitors behavior of the directional derivative estimate. This test is less powerful than test #1, but it does not depend on the gradient values and thus it is more robust against artifacts introduced by numerical differentiation. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], f[] - arrays of length CNT which store step lengths and function values at these points; f[i] is evaluated in x0+stp[i]*d. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #0 "strong" report lngrep - C1 test #0 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardnonc1test0results(mincgstate state, optguardapi.optguardnonc1test0report strrep, optguardapi.optguardnonc1test0report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test0report(state.smonitor.nonc1test0lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* Detailed results of the OptGuard integrity check for nonsmoothness test #1 Nonsmoothness (non-C1) test #1 studies individual components of the gradient computed during line search. When precise analytic gradient is provided this test is more powerful than test #0 which works with function values and ignores user-provided gradient. However, test #0 becomes more powerful when numerical differentiation is employed (in such cases test #1 detects higher levels of numerical noise and becomes too conservative). This test also tells specific components of the gradient which violate C1 continuity, which makes it more informative than #0, which just tells that continuity is violated. Two reports are returned: * a "strongest" one, corresponding to line search which had highest value of the nonsmoothness indicator * a "longest" one, corresponding to line search which had more function evaluations, and thus is more detailed In both cases following fields are returned: * positive - is TRUE when test flagged suspicious point; FALSE if test did not notice anything (in the latter cases fields below are empty). * vidx - is an index of the variable in [0,N) with nonsmooth derivative * x0[], d[] - arrays of length N which store initial point and direction for line search (d[] can be normalized, but does not have to) * stp[], g[] - arrays of length CNT which store step lengths and gradient values at these points; g[i] is evaluated in x0+stp[i]*d and contains vidx-th component of the gradient. * stpidxa, stpidxb - we suspect that function violates C1 continuity between steps #stpidxa and #stpidxb (usually we have stpidxb=stpidxa+3, with most likely position of the violation between stpidxa+1 and stpidxa+2. ========================================================================== = SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it - you will = see where C1 continuity is violated. ========================================================================== INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: strrep - C1 test #1 "strong" report lngrep - C1 test #1 "long" report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void mincgoptguardnonc1test1results(mincgstate state, optguardapi.optguardnonc1test1report strrep, optguardapi.optguardnonc1test1report lngrep, alglib.xparams _params) { optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1strrep, state.lastscaleused, strrep, _params); optguardapi.smoothnessmonitorexportc1test1report(state.smonitor.nonc1test1lngrep, state.lastscaleused, lngrep, _params); } /************************************************************************* Conjugate gradient results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report: * Rep.TerminationType completetion code: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -7 gradient verification failed. See MinCGSetGradientCheck() for more information. * 1 relative function improvement is no more than EpsF. * 2 relative step is no more than EpsX. * 4 gradient norm is no more than EpsG * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible, we return best X found so far * 8 terminated by user * Rep.IterationsCount contains iterations count * NFEV countains number of function calculations -- ALGLIB -- Copyright 20.04.2009 by Bochkanov Sergey *************************************************************************/ public static void mincgresults(mincgstate state, ref double[] x, mincgreport rep, alglib.xparams _params) { x = new double[0]; mincgresultsbuf(state, ref x, rep, _params); } /************************************************************************* Conjugate gradient results Buffered implementation of MinCGResults(), which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 20.04.2009 by Bochkanov Sergey *************************************************************************/ public static void mincgresultsbuf(mincgstate state, ref double[] x, mincgreport rep, alglib.xparams _params) { int i_ = 0; if( alglib.ap.len(x)=state.n, "MinCGRestartFrom: Length(X)0 // for(i=0; i<=n-1; i++) { x[i] = x[i]/(state.diagh[i]+state.diaghl2[i]); } // // if VCnt>0 // if( vcnt>0 ) { for(i=0; i<=vcnt-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.vcorr[i,i_]*x[i_]; } work0[i] = v; } for(i=0; i<=n-1; i++) { work1[i] = 0; } for(i=0; i<=vcnt-1; i++) { v = work0[i]; for(i_=0; i_<=n-1;i_++) { state.work1[i_] = state.work1[i_] + v*state.vcorr[i,i_]; } } for(i=0; i<=n-1; i++) { x[i] = x[i]-state.work1[i]/(state.diagh[i]+state.diaghl2[i]); } } } /************************************************************************* This function calculates preconditioned product x'*H^(-1)*y. Work0[] and Work1[] are used as temporaries (size must be at least N; this function doesn't allocate arrays). -- ALGLIB -- Copyright 13.10.2010 by Bochkanov Sergey *************************************************************************/ private static double preconditionedmultiply2(mincgstate state, ref double[] x, ref double[] y, ref double[] work0, ref double[] work1, alglib.xparams _params) { double result = 0; int i = 0; int n = 0; int vcnt = 0; double v0 = 0; double v1 = 0; int i_ = 0; n = state.n; vcnt = state.vcnt; // // no preconditioning // if( state.prectype==0 ) { v0 = 0.0; for(i_=0; i_<=n-1;i_++) { v0 += x[i_]*y[i_]; } result = v0; return result; } if( state.prectype==3 ) { result = 0; for(i=0; i<=n-1; i++) { result = result+x[i]*state.s[i]*state.s[i]*y[i]; } return result; } alglib.ap.assert(state.prectype==2, "MinCG: internal error (unexpected PrecType)"); // // low rank preconditioning // result = 0.0; for(i=0; i<=n-1; i++) { result = result+x[i]*y[i]/(state.diagh[i]+state.diaghl2[i]); } if( vcnt>0 ) { for(i=0; i<=n-1; i++) { work0[i] = x[i]/(state.diagh[i]+state.diaghl2[i]); work1[i] = y[i]/(state.diagh[i]+state.diaghl2[i]); } for(i=0; i<=vcnt-1; i++) { v0 = 0.0; for(i_=0; i_<=n-1;i_++) { v0 += work0[i_]*state.vcorr[i,i_]; } v1 = 0.0; for(i_=0; i_<=n-1;i_++) { v1 += work1[i_]*state.vcorr[i,i_]; } result = result-v0*v1; } } return result; } /************************************************************************* Internal initialization subroutine -- ALGLIB -- Copyright 16.05.2011 by Bochkanov Sergey *************************************************************************/ private static void mincginitinternal(int n, double diffstep, mincgstate state, alglib.xparams _params) { int i = 0; // // Initialize // state.teststep = 0; state.smoothnessguardlevel = 0; optserv.smoothnessmonitorinit(state.smonitor, 0, 0, false, _params); state.n = n; state.diffstep = diffstep; state.lastgoodstep = 0; mincgsetcond(state, 0, 0, 0, 0, _params); mincgsetxrep(state, false, _params); mincgsetdrep(state, false, _params); mincgsetstpmax(state, 0, _params); mincgsetcgtype(state, -1, _params); mincgsetprecdefault(state, _params); state.xk = new double[n]; state.dk = new double[n]; state.xn = new double[n]; state.dn = new double[n]; state.x = new double[n]; state.d = new double[n]; state.g = new double[n]; state.work0 = new double[n]; state.work1 = new double[n]; state.yk = new double[n]; state.s = new double[n]; state.invs = new double[n]; state.lastscaleused = new double[n]; apserv.rvectorsetlengthatleast(ref state.xbase, n, _params); for(i=0; i<=n-1; i++) { state.s[i] = 1.0; state.invs[i] = 1.0; state.lastscaleused[i] = 1.0; } } } public class minlm { /************************************************************************* Step finder for Levenberg-Marquardt optimizer. Internal object used by MinLM unit. This structure should be initialized with MinLMStepFinderInit(). Each step search session should start with MinLMStepFinderStart() call, followed by a sequence of MinLMStepFinderIteration() calls. *************************************************************************/ public class minlmstepfinder : apobject { public int n; public int m; public double stpmax; public int modelage; public int maxmodelage; public bool hasfi; public double epsx; public double[] x; public double f; public double[] fi; public bool needf; public bool needfi; public double fbase; public double[] modeldiag; public double[] xbase; public double[] fibase; public double[] bndl; public double[] bndu; public bool[] havebndl; public bool[] havebndu; public double[] s; public rcommstate rstate; public double[] xdir; public double[] choleskybuf; public double[] tmp0; public int[] tmpct; public double actualdecrease; public double predicteddecrease; public minqp.minqpstate qpstate; public minqp.minqpreport qprep; public sparse.sparsematrix tmpsp; public minlmstepfinder() { init(); } public override void init() { x = new double[0]; fi = new double[0]; modeldiag = new double[0]; xbase = new double[0]; fibase = new double[0]; bndl = new double[0]; bndu = new double[0]; havebndl = new bool[0]; havebndu = new bool[0]; s = new double[0]; rstate = new rcommstate(); xdir = new double[0]; choleskybuf = new double[0]; tmp0 = new double[0]; tmpct = new int[0]; qpstate = new minqp.minqpstate(); qprep = new minqp.minqpreport(); tmpsp = new sparse.sparsematrix(); } public override alglib.apobject make_copy() { minlmstepfinder _result = new minlmstepfinder(); _result.n = n; _result.m = m; _result.stpmax = stpmax; _result.modelage = modelage; _result.maxmodelage = maxmodelage; _result.hasfi = hasfi; _result.epsx = epsx; _result.x = (double[])x.Clone(); _result.f = f; _result.fi = (double[])fi.Clone(); _result.needf = needf; _result.needfi = needfi; _result.fbase = fbase; _result.modeldiag = (double[])modeldiag.Clone(); _result.xbase = (double[])xbase.Clone(); _result.fibase = (double[])fibase.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.havebndl = (bool[])havebndl.Clone(); _result.havebndu = (bool[])havebndu.Clone(); _result.s = (double[])s.Clone(); _result.rstate = (rcommstate)rstate.make_copy(); _result.xdir = (double[])xdir.Clone(); _result.choleskybuf = (double[])choleskybuf.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmpct = (int[])tmpct.Clone(); _result.actualdecrease = actualdecrease; _result.predicteddecrease = predicteddecrease; _result.qpstate = (minqp.minqpstate)qpstate.make_copy(); _result.qprep = (minqp.minqpreport)qprep.make_copy(); _result.tmpsp = (sparse.sparsematrix)tmpsp.make_copy(); return _result; } }; /************************************************************************* Levenberg-Marquardt optimizer. This structure should be created using one of the MinLMCreate???() functions. You should not access its fields directly; use ALGLIB functions to work with it. *************************************************************************/ public class minlmstate : apobject { public int n; public int m; public double diffstep; public double epsx; public int maxits; public bool xrep; public double stpmax; public int maxmodelage; public bool makeadditers; public double[] x; public double f; public double[] fi; public double[,] j; public double[,] h; public double[] g; public bool needf; public bool needfg; public bool needfgh; public bool needfij; public bool needfi; public bool xupdated; public bool userterminationneeded; public int algomode; public bool hasf; public bool hasfi; public bool hasg; public double[] xbase; public double fbase; public double[] fibase; public double[] gbase; public double[,] quadraticmodel; public double[] bndl; public double[] bndu; public bool[] havebndl; public bool[] havebndu; public double[] s; public double[,] cleic; public int nec; public int nic; public double lambdav; public double nu; public int modelage; public double[] xnew; public double[] xdir; public double[] deltax; public double[] deltaf; public bool deltaxready; public bool deltafready; public optserv.smoothnessmonitor smonitor; public double teststep; public double[] lastscaleused; public int repiterationscount; public int repterminationtype; public int repnfunc; public int repnjac; public int repngrad; public int repnhess; public int repncholesky; public rcommstate rstate; public double[] choleskybuf; public double[] tmp0; public double actualdecrease; public double predicteddecrease; public double xm1; public double xp1; public double[] fm1; public double[] fp1; public double[] fc1; public double[] gm1; public double[] gp1; public double[] gc1; public minlbfgs.minlbfgsstate internalstate; public minlbfgs.minlbfgsreport internalrep; public minqp.minqpstate qpstate; public minqp.minqpreport qprep; public minlmstepfinder finderstate; public minlmstate() { init(); } public override void init() { x = new double[0]; fi = new double[0]; j = new double[0,0]; h = new double[0,0]; g = new double[0]; xbase = new double[0]; fibase = new double[0]; gbase = new double[0]; quadraticmodel = new double[0,0]; bndl = new double[0]; bndu = new double[0]; havebndl = new bool[0]; havebndu = new bool[0]; s = new double[0]; cleic = new double[0,0]; xnew = new double[0]; xdir = new double[0]; deltax = new double[0]; deltaf = new double[0]; smonitor = new optserv.smoothnessmonitor(); lastscaleused = new double[0]; rstate = new rcommstate(); choleskybuf = new double[0]; tmp0 = new double[0]; fm1 = new double[0]; fp1 = new double[0]; fc1 = new double[0]; gm1 = new double[0]; gp1 = new double[0]; gc1 = new double[0]; internalstate = new minlbfgs.minlbfgsstate(); internalrep = new minlbfgs.minlbfgsreport(); qpstate = new minqp.minqpstate(); qprep = new minqp.minqpreport(); finderstate = new minlmstepfinder(); } public override alglib.apobject make_copy() { minlmstate _result = new minlmstate(); _result.n = n; _result.m = m; _result.diffstep = diffstep; _result.epsx = epsx; _result.maxits = maxits; _result.xrep = xrep; _result.stpmax = stpmax; _result.maxmodelage = maxmodelage; _result.makeadditers = makeadditers; _result.x = (double[])x.Clone(); _result.f = f; _result.fi = (double[])fi.Clone(); _result.j = (double[,])j.Clone(); _result.h = (double[,])h.Clone(); _result.g = (double[])g.Clone(); _result.needf = needf; _result.needfg = needfg; _result.needfgh = needfgh; _result.needfij = needfij; _result.needfi = needfi; _result.xupdated = xupdated; _result.userterminationneeded = userterminationneeded; _result.algomode = algomode; _result.hasf = hasf; _result.hasfi = hasfi; _result.hasg = hasg; _result.xbase = (double[])xbase.Clone(); _result.fbase = fbase; _result.fibase = (double[])fibase.Clone(); _result.gbase = (double[])gbase.Clone(); _result.quadraticmodel = (double[,])quadraticmodel.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.havebndl = (bool[])havebndl.Clone(); _result.havebndu = (bool[])havebndu.Clone(); _result.s = (double[])s.Clone(); _result.cleic = (double[,])cleic.Clone(); _result.nec = nec; _result.nic = nic; _result.lambdav = lambdav; _result.nu = nu; _result.modelage = modelage; _result.xnew = (double[])xnew.Clone(); _result.xdir = (double[])xdir.Clone(); _result.deltax = (double[])deltax.Clone(); _result.deltaf = (double[])deltaf.Clone(); _result.deltaxready = deltaxready; _result.deltafready = deltafready; _result.smonitor = (optserv.smoothnessmonitor)smonitor.make_copy(); _result.teststep = teststep; _result.lastscaleused = (double[])lastscaleused.Clone(); _result.repiterationscount = repiterationscount; _result.repterminationtype = repterminationtype; _result.repnfunc = repnfunc; _result.repnjac = repnjac; _result.repngrad = repngrad; _result.repnhess = repnhess; _result.repncholesky = repncholesky; _result.rstate = (rcommstate)rstate.make_copy(); _result.choleskybuf = (double[])choleskybuf.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.actualdecrease = actualdecrease; _result.predicteddecrease = predicteddecrease; _result.xm1 = xm1; _result.xp1 = xp1; _result.fm1 = (double[])fm1.Clone(); _result.fp1 = (double[])fp1.Clone(); _result.fc1 = (double[])fc1.Clone(); _result.gm1 = (double[])gm1.Clone(); _result.gp1 = (double[])gp1.Clone(); _result.gc1 = (double[])gc1.Clone(); _result.internalstate = (minlbfgs.minlbfgsstate)internalstate.make_copy(); _result.internalrep = (minlbfgs.minlbfgsreport)internalrep.make_copy(); _result.qpstate = (minqp.minqpstate)qpstate.make_copy(); _result.qprep = (minqp.minqpreport)qprep.make_copy(); _result.finderstate = (minlmstepfinder)finderstate.make_copy(); return _result; } }; /************************************************************************* Optimization report, filled by MinLMResults() function FIELDS: * TerminationType, completetion code: * -8 optimizer detected NAN/INF values either in the function itself, or in its Jacobian * -5 inappropriate solver was used: * solver created with minlmcreatefgh() used on problem with general linear constraints (set with minlmsetlc() call). * -3 constraints are inconsistent * 2 relative step is no more than EpsX. * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible * 8 terminated by user who called MinLMRequestTermination(). X contains point which was "current accepted" when termination request was submitted. * IterationsCount, contains iterations count * NFunc, number of function calculations * NJac, number of Jacobi matrix calculations * NGrad, number of gradient calculations * NHess, number of Hessian calculations * NCholesky, number of Cholesky decomposition calculations *************************************************************************/ public class minlmreport : apobject { public int iterationscount; public int terminationtype; public int nfunc; public int njac; public int ngrad; public int nhess; public int ncholesky; public minlmreport() { init(); } public override void init() { } public override alglib.apobject make_copy() { minlmreport _result = new minlmreport(); _result.iterationscount = iterationscount; _result.terminationtype = terminationtype; _result.nfunc = nfunc; _result.njac = njac; _result.ngrad = ngrad; _result.nhess = nhess; _result.ncholesky = ncholesky; return _result; } }; public const double lambdaup = 2.0; public const double lambdadown = 0.33; public const double suspiciousnu = 16; public const int smallmodelage = 3; public const int additers = 5; /************************************************************************* IMPROVED LEVENBERG-MARQUARDT METHOD FOR NON-LINEAR LEAST SQUARES OPTIMIZATION DESCRIPTION: This function is used to find minimum of function which is represented as sum of squares: F(x) = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1]) using value of function vector f[] and Jacobian of f[]. REQUIREMENTS: This algorithm will request following information during its operation: * function vector f[] at given point X * function vector f[] and Jacobian of f[] (simultaneously) at given point There are several overloaded versions of MinLMOptimize() function which correspond to different LM-like optimization algorithms provided by this unit. You should choose version which accepts fvec() and jac() callbacks. First one is used to calculate f[] at given point, second one calculates f[] and Jacobian df[i]/dx[j]. You can try to initialize MinLMState structure with VJ function and then use incorrect version of MinLMOptimize() (for example, version which works with general form function and does not provide Jacobian), but it will lead to exception being thrown after first attempt to calculate Jacobian. USAGE: 1. User initializes algorithm state with MinLMCreateVJ() call 2. User tunes solver parameters with MinLMSetCond(), MinLMSetStpMax() and other functions 3. User calls MinLMOptimize() function which takes algorithm state and callback functions. 4. User calls MinLMResults() to get solution 5. Optionally, user may call MinLMRestartFrom() to solve another problem with same N/M but another starting point and/or another function. MinLMRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - dimension, N>1 * if given, only leading N elements of X are used * if not given, automatically determined from size of X M - number of functions f[i] X - initial solution, array[0..N-1] OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. you may tune stopping conditions with MinLMSetCond() function 2. if target function contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow, use MinLMSetStpMax() function to bound algorithm's steps. -- ALGLIB -- Copyright 30.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmcreatevj(int n, int m, double[] x, minlmstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinLMCreateVJ: N<1!"); alglib.ap.assert(m>=1, "MinLMCreateVJ: M<1!"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinLMCreateVJ: Length(X)1 * if given, only leading N elements of X are used * if not given, automatically determined from size of X M - number of functions f[i] X - initial solution, array[0..N-1] DiffStep- differentiation step, >0 OUTPUT PARAMETERS: State - structure which stores algorithm state See also MinLMIteration, MinLMResults. NOTES: 1. you may tune stopping conditions with MinLMSetCond() function 2. if target function contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow, use MinLMSetStpMax() function to bound algorithm's steps. -- ALGLIB -- Copyright 30.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmcreatev(int n, int m, double[] x, double diffstep, minlmstate state, alglib.xparams _params) { alglib.ap.assert(math.isfinite(diffstep), "MinLMCreateV: DiffStep is not finite!"); alglib.ap.assert((double)(diffstep)>(double)(0), "MinLMCreateV: DiffStep<=0!"); alglib.ap.assert(n>=1, "MinLMCreateV: N<1!"); alglib.ap.assert(m>=1, "MinLMCreateV: M<1!"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinLMCreateV: Length(X)1 * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - initial solution, array[0..N-1] OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. you may tune stopping conditions with MinLMSetCond() function 2. if target function contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow, use MinLMSetStpMax() function to bound algorithm's steps. -- ALGLIB -- Copyright 30.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmcreatefgh(int n, double[] x, minlmstate state, alglib.xparams _params) { alglib.ap.assert(n>=1, "MinLMCreateFGH: N<1!"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinLMCreateFGH: Length(X)=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - ste pvector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinLMSetScale() Recommended values: 1E-9 ... 1E-12. MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Only Levenberg-Marquardt iterations are counted (L-BFGS/CG iterations are NOT counted because their cost is very low compared to that of LM). Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (small EpsX). NOTE: it is not recommended to set large EpsX (say, 0.001). Because LM is a second-order method, it performs very precise steps anyway. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlmsetcond(minlmstate state, double epsx, int maxits, alglib.xparams _params) { alglib.ap.assert(math.isfinite(epsx), "MinLMSetCond: EpsX is not finite number!"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinLMSetCond: negative EpsX!"); alglib.ap.assert(maxits>=0, "MinLMSetCond: negative MaxIts!"); if( (double)(epsx)==(double)(0) && maxits==0 ) { epsx = 1.0E-9; } state.epsx = epsx; state.maxits = maxits; } /************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to MinLMOptimize(). Both Levenberg-Marquardt and internal L-BFGS iterations are reported. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlmsetxrep(minlmstate state, bool needxrep, alglib.xparams _params) { state.xrep = needxrep; } /************************************************************************* This function sets maximum step length INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't want to limit step length. Use this subroutine when you optimize target function which contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. NOTE: non-zero StpMax leads to moderate performance degradation because intermediate step of preconditioned L-BFGS optimization is incompatible with limits on step size. -- ALGLIB -- Copyright 02.04.2010 by Bochkanov Sergey *************************************************************************/ public static void minlmsetstpmax(minlmstate state, double stpmax, alglib.xparams _params) { alglib.ap.assert(math.isfinite(stpmax), "MinLMSetStpMax: StpMax is not finite!"); alglib.ap.assert((double)(stpmax)>=(double)(0), "MinLMSetStpMax: StpMax<0!"); state.stpmax = stpmax; } /************************************************************************* This function sets scaling coefficients for LM optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Generally, scale is NOT considered to be a form of preconditioner. But LM optimizer is unique in that it uses scaling matrix both in the stopping condition tests and as Marquardt damping factor. Proper scaling is very important for the algorithm performance. It is less important for the quality of results, but still has some influence (it is easier to converge when variables are properly scaled, so premature stopping is possible when very badly scalled variables are combined with relaxed stopping conditions). INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlmsetscale(minlmstate state, double[] s, alglib.xparams _params) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.n, "MinLMSetScale: Length(S)=n, "MinLMSetBC: Length(BndL)=n, "MinLMSetBC: Length(BndU)0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT IMPORTANT: if you have linear constraints, it is strongly recommended to set scale of variables with minlmsetscale(). QP solver which is used to calculate linearly constrained steps heavily relies on good scaling of input problems. IMPORTANT: solvers created with minlmcreatefgh() do not support linear constraints. NOTE: linear (non-bound) constraints are satisfied only approximately - there always exists some violation due to numerical errors and algorithmic limitations. NOTE: general linear constraints add significant overhead to solution process. Although solver performs roughly same amount of iterations (when compared with similar box-only constrained problem), each iteration now involves solution of linearly constrained QP subproblem, which requires ~3-5 times more Cholesky decompositions. Thus, if you can reformulate your problem in such way this it has only box constraints, it may be beneficial to do so. -- ALGLIB -- Copyright 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minlmsetlc(minlmstate state, double[,] c, int[] ct, int k, alglib.xparams _params) { int i = 0; int n = 0; int i_ = 0; n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(k>=0, "MinLMSetLC: K<0"); alglib.ap.assert(alglib.ap.cols(c)>=n+1 || k==0, "MinLMSetLC: Cols(C)=k, "MinLMSetLC: Rows(C)=k, "MinLMSetLC: Length(CT)0 ) { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = -c[i,i_]; } } else { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = c[i,i_]; } } state.nic = state.nic+1; } } } /************************************************************************* This function is used to change acceleration settings You can choose between three acceleration strategies: * AccType=0, no acceleration. * AccType=1, secant updates are used to update quadratic model after each iteration. After fixed number of iterations (or after model breakdown) we recalculate quadratic model using analytic Jacobian or finite differences. Number of secant-based iterations depends on optimization settings: about 3 iterations - when we have analytic Jacobian, up to 2*N iterations - when we use finite differences to calculate Jacobian. AccType=1 is recommended when Jacobian calculation cost is prohibitively high (several Mx1 function vector calculations followed by several NxN Cholesky factorizations are faster than calculation of one M*N Jacobian). It should also be used when we have no Jacobian, because finite difference approximation takes too much time to compute. Table below list optimization protocols (XYZ protocol corresponds to MinLMCreateXYZ) and acceleration types they support (and use by default). ACCELERATION TYPES SUPPORTED BY OPTIMIZATION PROTOCOLS: protocol 0 1 comment V + + VJ + + FGH + DEFAULT VALUES: protocol 0 1 comment V x without acceleration it is so slooooooooow VJ x FGH x NOTE: this function should be called before optimization. Attempt to call it during algorithm iterations may result in unexpected behavior. NOTE: attempt to call this function with unsupported protocol/acceleration combination will result in exception being thrown. -- ALGLIB -- Copyright 14.10.2010 by Bochkanov Sergey *************************************************************************/ public static void minlmsetacctype(minlmstate state, int acctype, alglib.xparams _params) { alglib.ap.assert((acctype==0 || acctype==1) || acctype==2, "MinLMSetAccType: incorrect AccType!"); if( acctype==2 ) { acctype = 0; } if( acctype==0 ) { state.maxmodelage = 0; state.makeadditers = false; return; } if( acctype==1 ) { alglib.ap.assert(state.hasfi, "MinLMSetAccType: AccType=1 is incompatible with current protocol!"); if( state.algomode==0 ) { state.maxmodelage = 2*state.n; } else { state.maxmodelage = smallmodelage; } state.makeadditers = false; return; } } /************************************************************************* NOTES: 1. Depending on function used to create state structure, this algorithm may accept Jacobian and/or Hessian and/or gradient. According to the said above, there ase several versions of this function, which accept different sets of callbacks. This flexibility opens way to subtle errors - you may create state with MinLMCreateFGH() (optimization using Hessian), but call function which does not accept Hessian. So when algorithm will request Hessian, there will be no callback to call. In this case exception will be thrown. Be careful to avoid such errors because there is no way to find them at compile time - you can see them at runtime only. -- ALGLIB -- Copyright 10.03.2009 by Bochkanov Sergey *************************************************************************/ public static bool minlmiteration(minlmstate state, alglib.xparams _params) { bool result = new bool(); int n = 0; int m = 0; bool bflag = new bool(); int iflag = 0; double v = 0; double s = 0; double t = 0; double fnew = 0; int i = 0; int k = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { n = state.rstate.ia[0]; m = state.rstate.ia[1]; iflag = state.rstate.ia[2]; i = state.rstate.ia[3]; k = state.rstate.ia[4]; bflag = state.rstate.ba[0]; v = state.rstate.ra[0]; s = state.rstate.ra[1]; t = state.rstate.ra[2]; fnew = state.rstate.ra[3]; } else { n = 359; m = -58; iflag = -919; i = -909; k = 81; bflag = true; v = 74; s = -788; t = 809; fnew = 205; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } if( state.rstate.stage==4 ) { goto lbl_4; } if( state.rstate.stage==5 ) { goto lbl_5; } if( state.rstate.stage==6 ) { goto lbl_6; } if( state.rstate.stage==7 ) { goto lbl_7; } if( state.rstate.stage==8 ) { goto lbl_8; } if( state.rstate.stage==9 ) { goto lbl_9; } if( state.rstate.stage==10 ) { goto lbl_10; } if( state.rstate.stage==11 ) { goto lbl_11; } if( state.rstate.stage==12 ) { goto lbl_12; } if( state.rstate.stage==13 ) { goto lbl_13; } if( state.rstate.stage==14 ) { goto lbl_14; } if( state.rstate.stage==15 ) { goto lbl_15; } if( state.rstate.stage==16 ) { goto lbl_16; } if( state.rstate.stage==17 ) { goto lbl_17; } if( state.rstate.stage==18 ) { goto lbl_18; } if( state.rstate.stage==19 ) { goto lbl_19; } if( state.rstate.stage==20 ) { goto lbl_20; } if( state.rstate.stage==21 ) { goto lbl_21; } if( state.rstate.stage==22 ) { goto lbl_22; } if( state.rstate.stage==23 ) { goto lbl_23; } if( state.rstate.stage==24 ) { goto lbl_24; } if( state.rstate.stage==25 ) { goto lbl_25; } if( state.rstate.stage==26 ) { goto lbl_26; } if( state.rstate.stage==27 ) { goto lbl_27; } // // Routine body // // // prepare // n = state.n; m = state.m; state.repiterationscount = 0; state.repterminationtype = 0; state.repnfunc = 0; state.repnjac = 0; state.repngrad = 0; state.repnhess = 0; state.repncholesky = 0; state.userterminationneeded = false; if( m>0 ) { optserv.smoothnessmonitorinit(state.smonitor, n, m, false, _params); } for(i=0; i<=n-1; i++) { state.lastscaleused[i] = state.s[i]; } // // Prepare LM step finder and enforce/check feasibility of constraints // if( !minlmstepfinderinit(state.finderstate, n, m, state.maxmodelage, state.hasfi, ref state.xbase, state.bndl, state.bndu, state.cleic, state.nec, state.nic, state.s, state.stpmax, state.epsx, _params) ) { state.repterminationtype = -3; result = false; return result; } // // set constraints for obsolete QP solver // minqp.minqpsetbc(state.qpstate, state.bndl, state.bndu, _params); // // Check correctness of the analytic Jacobian // clearrequestfields(state, _params); if( !(state.algomode==1 && (double)(state.teststep)>(double)(0)) ) { goto lbl_28; } alglib.ap.assert(m>0, "MinLM: integrity check failed"); lbl_30: if( !optserv.smoothnessmonitorcheckgradientatx0(state.smonitor, state.xbase, state.s, state.bndl, state.bndu, true, state.teststep, _params) ) { goto lbl_31; } for(i=0; i<=n-1; i++) { state.x[i] = state.smonitor.x[i]; } state.needfij = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needfij = false; for(i=0; i<=m-1; i++) { state.smonitor.fi[i] = state.fi[i]; for(k=0; k<=n-1; k++) { state.smonitor.j[i,k] = state.j[i,k]; } } goto lbl_30; lbl_31: lbl_28: // // Initial report of current point // // Note 1: we rewrite State.X twice because // user may accidentally change it after first call. // // Note 2: we set NeedF or NeedFI depending on what // information about function we have. // if( !state.xrep ) { goto lbl_32; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); if( !state.hasf ) { goto lbl_34; } state.needf = true; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: state.needf = false; goto lbl_35; lbl_34: alglib.ap.assert(state.hasfi, "MinLM: internal error 2!"); state.needfi = true; state.rstate.stage = 2; goto lbl_rcomm; lbl_2: state.needfi = false; v = 0.0; for(i_=0; i_<=m-1;i_++) { v += state.fi[i_]*state.fi[i_]; } state.f = v; lbl_35: state.repnfunc = state.repnfunc+1; for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 3; goto lbl_rcomm; lbl_3: state.xupdated = false; lbl_32: if( state.userterminationneeded ) { // // User requested termination // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.repterminationtype = 8; result = false; return result; } // // Prepare control variables // state.nu = 1; state.lambdav = -math.maxrealnumber; state.modelage = state.maxmodelage+1; state.deltaxready = false; state.deltafready = false; if( state.algomode==2 ) { goto lbl_36; } // // Jacobian-based optimization mode // // Main cycle. // // We move through it until either: // * one of the stopping conditions is met // * we decide that stopping conditions are too stringent // and break from cycle // lbl_38: if( false ) { goto lbl_39; } // // First, we have to prepare quadratic model for our function. // We use BFlag to ensure that model is prepared; // if it is false at the end of this block, something went wrong. // // We may either calculate brand new model or update old one. // // Before this block we have: // * State.XBase - current position. // * State.DeltaX - if DeltaXReady is True // * State.DeltaF - if DeltaFReady is True // // After this block is over, we will have: // * State.XBase - base point (unchanged) // * State.FBase - F(XBase) // * State.GBase - linear term // * State.QuadraticModel - quadratic term // * State.LambdaV - current estimate for lambda // // We also clear DeltaXReady/DeltaFReady flags // after initialization is done. // alglib.ap.assert(state.algomode==0 || state.algomode==1, "MinLM: integrity check failed"); if( !(state.modelage>state.maxmodelage || !(state.deltaxready && state.deltafready)) ) { goto lbl_40; } // // Refresh model (using either finite differences or analytic Jacobian) // if( state.algomode!=0 ) { goto lbl_42; } // // Optimization using F values only. // Use finite differences to estimate Jacobian. // alglib.ap.assert(state.hasfi, "MinLMIteration: internal error when estimating Jacobian (no f[])"); k = 0; lbl_44: if( k>n-1 ) { goto lbl_46; } // // We guard X[k] from leaving [BndL,BndU]. // In case BndL=BndU, we assume that derivative in this direction is zero. // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]-state.s[k]*state.diffstep; if( state.havebndl[k] ) { state.x[k] = Math.Max(state.x[k], state.bndl[k]); } if( state.havebndu[k] ) { state.x[k] = Math.Min(state.x[k], state.bndu[k]); } state.xm1 = state.x[k]; clearrequestfields(state, _params); state.needfi = true; state.rstate.stage = 4; goto lbl_rcomm; lbl_4: state.repnfunc = state.repnfunc+1; for(i_=0; i_<=m-1;i_++) { state.fm1[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]+state.s[k]*state.diffstep; if( state.havebndl[k] ) { state.x[k] = Math.Max(state.x[k], state.bndl[k]); } if( state.havebndu[k] ) { state.x[k] = Math.Min(state.x[k], state.bndu[k]); } state.xp1 = state.x[k]; clearrequestfields(state, _params); state.needfi = true; state.rstate.stage = 5; goto lbl_rcomm; lbl_5: state.repnfunc = state.repnfunc+1; for(i_=0; i_<=m-1;i_++) { state.fp1[i_] = state.fi[i_]; } v = state.xp1-state.xm1; if( (double)(v)!=(double)(0) ) { v = 1/v; for(i_=0; i_<=m-1;i_++) { state.j[i_,k] = v*state.fp1[i_]; } for(i_=0; i_<=m-1;i_++) { state.j[i_,k] = state.j[i_,k] - v*state.fm1[i_]; } } else { for(i=0; i<=m-1; i++) { state.j[i,k] = 0; } } k = k+1; goto lbl_44; lbl_46: // // Calculate F(XBase) // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.needfi = true; state.rstate.stage = 6; goto lbl_rcomm; lbl_6: state.needfi = false; state.repnfunc = state.repnfunc+1; state.repnjac = state.repnjac+1; // // New model // state.modelage = 0; goto lbl_43; lbl_42: // // Obtain f[] and Jacobian // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.needfij = true; state.rstate.stage = 7; goto lbl_rcomm; lbl_7: state.needfij = false; state.repnfunc = state.repnfunc+1; state.repnjac = state.repnjac+1; // // New model // state.modelage = 0; lbl_43: goto lbl_41; lbl_40: // // State.J contains Jacobian or its current approximation; // refresh it using secant updates: // // f(x0+dx) = f(x0) + J*dx, // J_new = J_old + u*h' // h = x_new-x_old // u = (f_new - f_old - J_old*h)/(h'h) // // We can explicitly generate h and u, but it is // preferential to do in-place calculations. Only // I-th row of J_old is needed to calculate u[I], // so we can update J row by row in one pass. // // NOTE: we expect that State.XBase contains new point, // State.FBase contains old point, State.DeltaX and // State.DeltaY contain updates from last step. // alglib.ap.assert(state.deltaxready && state.deltafready, "MinLMIteration: uninitialized DeltaX/DeltaF"); t = 0.0; for(i_=0; i_<=n-1;i_++) { t += state.deltax[i_]*state.deltax[i_]; } alglib.ap.assert((double)(t)!=(double)(0), "MinLM: internal error (T=0)"); for(i=0; i<=m-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.j[i,i_]*state.deltax[i_]; } v = (state.deltaf[i]-v)/t; for(i_=0; i_<=n-1;i_++) { state.j[i,i_] = state.j[i,i_] + v*state.deltax[i_]; } } for(i_=0; i_<=m-1;i_++) { state.fi[i_] = state.fibase[i_]; } for(i_=0; i_<=m-1;i_++) { state.fi[i_] = state.fi[i_] + state.deltaf[i_]; } // // Increase model age // state.modelage = state.modelage+1; lbl_41: ablas.rmatrixgemm(n, n, m, 2.0, state.j, 0, 0, 1, state.j, 0, 0, 0, 0.0, state.quadraticmodel, 0, 0, _params); ablas.rmatrixmv(n, m, state.j, 0, 0, 1, state.fi, 0, state.gbase, 0, _params); for(i_=0; i_<=n-1;i_++) { state.gbase[i_] = 2*state.gbase[i_]; } v = 0.0; for(i_=0; i_<=m-1;i_++) { v += state.fi[i_]*state.fi[i_]; } state.fbase = v; for(i_=0; i_<=m-1;i_++) { state.fibase[i_] = state.fi[i_]; } state.deltaxready = false; state.deltafready = false; // // Perform integrity check (presense of NAN/INF) // v = state.fbase; for(i=0; i<=n-1; i++) { v = 0.1*v+state.gbase[i]; } if( !math.isfinite(v) ) { // // Break! // state.repterminationtype = -8; result = false; return result; } // // If Lambda is not initialized, initialize it using quadratic model // if( (double)(state.lambdav)<(double)(0) ) { state.lambdav = 0; for(i=0; i<=n-1; i++) { state.lambdav = Math.Max(state.lambdav, Math.Abs(state.quadraticmodel[i,i])*math.sqr(state.s[i])); } state.lambdav = 0.001*state.lambdav; if( (double)(state.lambdav)==(double)(0) ) { state.lambdav = 1; } } // // Find value of Levenberg-Marquardt damping parameter which: // * leads to positive definite damped model // * within bounds specified by StpMax // * generates step which decreases function value // // After this block IFlag is set to: // * -8, if internal integrity control detected NAN/INF in function values // * -3, if constraints are infeasible // * -2, if model update is needed (either Lambda growth is too large // or step is too short, but we can't rely on model and stop iterations) // * -1, if model is fresh, Lambda have grown too large, termination is needed // * 0, if everything is OK, continue iterations // * >0, successful termination, step is less than EpsX // // State.Nu can have any value on enter, but after exit it is set to 1.0 // iflag = -99; minlmstepfinderstart(state.finderstate, state.quadraticmodel, state.gbase, state.fbase, state.xbase, state.fibase, state.modelage, _params); lbl_47: if( !minlmstepfinderiteration(state.finderstate, ref state.lambdav, ref state.nu, ref state.xnew, state.deltax, ref state.deltaxready, state.deltaf, ref state.deltafready, ref iflag, ref fnew, ref state.repncholesky, _params) ) { goto lbl_48; } alglib.ap.assert(state.hasfi || state.hasf, "MinLM: internal error 2!"); state.repnfunc = state.repnfunc+1; clearrequestfields(state, _params); if( !state.finderstate.needfi ) { goto lbl_49; } alglib.ap.assert(state.hasfi, "MinLM: internal error 2!"); for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.finderstate.x[i_]; } state.needfi = true; state.rstate.stage = 8; goto lbl_rcomm; lbl_8: state.needfi = false; for(i_=0; i_<=m-1;i_++) { state.finderstate.fi[i_] = state.fi[i_]; } goto lbl_47; lbl_49: if( !state.finderstate.needf ) { goto lbl_51; } alglib.ap.assert(state.hasf, "MinLM: internal error 2!"); for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.finderstate.x[i_]; } state.needf = true; state.rstate.stage = 9; goto lbl_rcomm; lbl_9: state.needf = false; state.finderstate.f = state.f; goto lbl_47; lbl_51: alglib.ap.assert(false, "MinLM: internal error 2!"); goto lbl_47; lbl_48: if( state.userterminationneeded ) { // // User requested termination // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.repterminationtype = 8; result = false; return result; } state.nu = 1; alglib.ap.assert(((iflag>=-3 && iflag<=0) || iflag==-8) || iflag>0, "MinLM: internal integrity check failed!"); if( iflag==-3 ) { state.repterminationtype = -3; result = false; return result; } if( iflag==-2 ) { state.modelage = state.maxmodelage+1; goto lbl_38; } if( iflag!=-1 ) { goto lbl_53; } // // Stopping conditions are too stringent // state.repterminationtype = 7; if( !state.xrep ) { goto lbl_55; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.f = state.fbase; clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 10; goto lbl_rcomm; lbl_10: state.xupdated = false; lbl_55: result = false; return result; lbl_53: if( !(iflag==-8 || iflag>0) ) { goto lbl_57; } // // Either: // * Integrity check failed - infinities or NANs // * successful termination (step size is small enough) // state.repterminationtype = iflag; if( !state.xrep ) { goto lbl_59; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.f = state.fbase; clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 11; goto lbl_rcomm; lbl_11: state.xupdated = false; lbl_59: result = false; return result; lbl_57: state.f = fnew; // // Levenberg-Marquardt step is ready. // Compare predicted vs. actual decrease and decide what to do with lambda. // // NOTE: we expect that State.DeltaX contains direction of step, // State.F contains function value at new point. // alglib.ap.assert(state.deltaxready, "MinLM: deltaX is not ready"); iflag = checkdecrease(state.quadraticmodel, state.gbase, state.fbase, n, state.deltax, state.f, ref state.lambdav, ref state.nu, _params); if( iflag==0 ) { goto lbl_61; } state.repterminationtype = iflag; if( !state.xrep ) { goto lbl_63; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.f = state.fbase; clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 12; goto lbl_rcomm; lbl_12: state.xupdated = false; lbl_63: result = false; return result; lbl_61: // // Accept step, report it and // test stopping conditions on iterations count and function decrease. // // NOTE: we expect that State.DeltaX contains direction of step, // State.F contains function value at new point. // // NOTE2: we should update XBase ONLY. In the beginning of the next // iteration we expect that State.FIBase is NOT updated and // contains old value of a function vector. // for(i_=0; i_<=n-1;i_++) { state.xbase[i_] = state.xnew[i_]; } if( !state.xrep ) { goto lbl_65; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 13; goto lbl_rcomm; lbl_13: state.xupdated = false; lbl_65: state.repiterationscount = state.repiterationscount+1; if( state.repiterationscount>=state.maxits && state.maxits>0 ) { state.repterminationtype = 5; } if( state.repterminationtype<=0 ) { goto lbl_67; } if( !state.xrep ) { goto lbl_69; } // // Report: XBase contains new point, F contains function value at new point // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 14; goto lbl_rcomm; lbl_14: state.xupdated = false; lbl_69: result = false; return result; lbl_67: state.modelage = state.modelage+1; goto lbl_38; lbl_39: // // Lambda is too large, we have to break iterations. // state.repterminationtype = 7; if( !state.xrep ) { goto lbl_71; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.f = state.fbase; clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 15; goto lbl_rcomm; lbl_15: state.xupdated = false; lbl_71: goto lbl_37; lbl_36: // // Legacy Hessian-based mode // // Main cycle. // // We move through it until either: // * one of the stopping conditions is met // * we decide that stopping conditions are too stringent // and break from cycle // // if( state.nec+state.nic>0 ) { // // FGH solver does not support general linear constraints // state.repterminationtype = -5; result = false; return result; } lbl_73: if( false ) { goto lbl_74; } // // First, we have to prepare quadratic model for our function. // We use BFlag to ensure that model is prepared; // if it is false at the end of this block, something went wrong. // // We may either calculate brand new model or update old one. // // Before this block we have: // * State.XBase - current position. // * State.DeltaX - if DeltaXReady is True // * State.DeltaF - if DeltaFReady is True // // After this block is over, we will have: // * State.XBase - base point (unchanged) // * State.FBase - F(XBase) // * State.GBase - linear term // * State.QuadraticModel - quadratic term // * State.LambdaV - current estimate for lambda // // We also clear DeltaXReady/DeltaFReady flags // after initialization is done. // bflag = false; if( !(state.algomode==0 || state.algomode==1) ) { goto lbl_75; } // // Calculate f[] and Jacobian // if( !(state.modelage>state.maxmodelage || !(state.deltaxready && state.deltafready)) ) { goto lbl_77; } // // Refresh model (using either finite differences or analytic Jacobian) // if( state.algomode!=0 ) { goto lbl_79; } // // Optimization using F values only. // Use finite differences to estimate Jacobian. // alglib.ap.assert(state.hasfi, "MinLMIteration: internal error when estimating Jacobian (no f[])"); k = 0; lbl_81: if( k>n-1 ) { goto lbl_83; } // // We guard X[k] from leaving [BndL,BndU]. // In case BndL=BndU, we assume that derivative in this direction is zero. // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]-state.s[k]*state.diffstep; if( state.havebndl[k] ) { state.x[k] = Math.Max(state.x[k], state.bndl[k]); } if( state.havebndu[k] ) { state.x[k] = Math.Min(state.x[k], state.bndu[k]); } state.xm1 = state.x[k]; clearrequestfields(state, _params); state.needfi = true; state.rstate.stage = 16; goto lbl_rcomm; lbl_16: state.repnfunc = state.repnfunc+1; for(i_=0; i_<=m-1;i_++) { state.fm1[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]+state.s[k]*state.diffstep; if( state.havebndl[k] ) { state.x[k] = Math.Max(state.x[k], state.bndl[k]); } if( state.havebndu[k] ) { state.x[k] = Math.Min(state.x[k], state.bndu[k]); } state.xp1 = state.x[k]; clearrequestfields(state, _params); state.needfi = true; state.rstate.stage = 17; goto lbl_rcomm; lbl_17: state.repnfunc = state.repnfunc+1; for(i_=0; i_<=m-1;i_++) { state.fp1[i_] = state.fi[i_]; } v = state.xp1-state.xm1; if( (double)(v)!=(double)(0) ) { v = 1/v; for(i_=0; i_<=m-1;i_++) { state.j[i_,k] = v*state.fp1[i_]; } for(i_=0; i_<=m-1;i_++) { state.j[i_,k] = state.j[i_,k] - v*state.fm1[i_]; } } else { for(i=0; i<=m-1; i++) { state.j[i,k] = 0; } } k = k+1; goto lbl_81; lbl_83: // // Calculate F(XBase) // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.needfi = true; state.rstate.stage = 18; goto lbl_rcomm; lbl_18: state.needfi = false; state.repnfunc = state.repnfunc+1; state.repnjac = state.repnjac+1; // // New model // state.modelage = 0; goto lbl_80; lbl_79: // // Obtain f[] and Jacobian // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.needfij = true; state.rstate.stage = 19; goto lbl_rcomm; lbl_19: state.needfij = false; state.repnfunc = state.repnfunc+1; state.repnjac = state.repnjac+1; // // New model // state.modelage = 0; lbl_80: goto lbl_78; lbl_77: // // State.J contains Jacobian or its current approximation; // refresh it using secant updates: // // f(x0+dx) = f(x0) + J*dx, // J_new = J_old + u*h' // h = x_new-x_old // u = (f_new - f_old - J_old*h)/(h'h) // // We can explicitly generate h and u, but it is // preferential to do in-place calculations. Only // I-th row of J_old is needed to calculate u[I], // so we can update J row by row in one pass. // // NOTE: we expect that State.XBase contains new point, // State.FBase contains old point, State.DeltaX and // State.DeltaY contain updates from last step. // alglib.ap.assert(state.deltaxready && state.deltafready, "MinLMIteration: uninitialized DeltaX/DeltaF"); t = 0.0; for(i_=0; i_<=n-1;i_++) { t += state.deltax[i_]*state.deltax[i_]; } alglib.ap.assert((double)(t)!=(double)(0), "MinLM: internal error (T=0)"); for(i=0; i<=m-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.j[i,i_]*state.deltax[i_]; } v = (state.deltaf[i]-v)/t; for(i_=0; i_<=n-1;i_++) { state.j[i,i_] = state.j[i,i_] + v*state.deltax[i_]; } } for(i_=0; i_<=m-1;i_++) { state.fi[i_] = state.fibase[i_]; } for(i_=0; i_<=m-1;i_++) { state.fi[i_] = state.fi[i_] + state.deltaf[i_]; } // // Increase model age // state.modelage = state.modelage+1; lbl_78: // // Generate quadratic model: // f(xbase+dx) = // = (f0 + J*dx)'(f0 + J*dx) // = f0^2 + dx'J'f0 + f0*J*dx + dx'J'J*dx // = f0^2 + 2*f0*J*dx + dx'J'J*dx // // Note that we calculate 2*(J'J) instead of J'J because // our quadratic model is based on Tailor decomposition, // i.e. it has 0.5 before quadratic term. // ablas.rmatrixgemm(n, n, m, 2.0, state.j, 0, 0, 1, state.j, 0, 0, 0, 0.0, state.quadraticmodel, 0, 0, _params); ablas.rmatrixmv(n, m, state.j, 0, 0, 1, state.fi, 0, state.gbase, 0, _params); for(i_=0; i_<=n-1;i_++) { state.gbase[i_] = 2*state.gbase[i_]; } v = 0.0; for(i_=0; i_<=m-1;i_++) { v += state.fi[i_]*state.fi[i_]; } state.fbase = v; for(i_=0; i_<=m-1;i_++) { state.fibase[i_] = state.fi[i_]; } // // set control variables // bflag = true; lbl_75: if( state.algomode!=2 ) { goto lbl_84; } alglib.ap.assert(!state.hasfi, "MinLMIteration: internal error (HasFI is True in Hessian-based mode)"); // // Obtain F, G, H // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.needfgh = true; state.rstate.stage = 20; goto lbl_rcomm; lbl_20: state.needfgh = false; state.repnfunc = state.repnfunc+1; state.repngrad = state.repngrad+1; state.repnhess = state.repnhess+1; ablas.rmatrixcopy(n, n, state.h, 0, 0, state.quadraticmodel, 0, 0, _params); for(i_=0; i_<=n-1;i_++) { state.gbase[i_] = state.g[i_]; } state.fbase = state.f; // // set control variables // bflag = true; state.modelage = 0; lbl_84: alglib.ap.assert(bflag, "MinLM: internal integrity check failed!"); state.deltaxready = false; state.deltafready = false; // // Perform integrity check (presense of NAN/INF) // v = state.fbase; for(i=0; i<=n-1; i++) { v = 0.1*v+state.gbase[i]; } if( !math.isfinite(v) ) { // // Break! // state.repterminationtype = -8; result = false; return result; } // // If Lambda is not initialized, initialize it using quadratic model // if( (double)(state.lambdav)<(double)(0) ) { state.lambdav = 0; for(i=0; i<=n-1; i++) { state.lambdav = Math.Max(state.lambdav, Math.Abs(state.quadraticmodel[i,i])*math.sqr(state.s[i])); } state.lambdav = 0.001*state.lambdav; if( (double)(state.lambdav)==(double)(0) ) { state.lambdav = 1; } } // // Find value of Levenberg-Marquardt damping parameter which: // * leads to positive definite damped model // * within bounds specified by StpMax // * generates step which decreases function value // // After this block IFlag is set to: // * -3, if constraints are infeasible // * -2, if model update is needed (either Lambda growth is too large // or step is too short, but we can't rely on model and stop iterations) // * -1, if model is fresh, Lambda have grown too large, termination is needed // * 0, if everything is OK, continue iterations // // State.Nu can have any value on enter, but after exit it is set to 1.0 // iflag = -99; lbl_86: if( false ) { goto lbl_87; } // // Do we need model update? // if( state.modelage>0 && (double)(state.nu)>=(double)(suspiciousnu) ) { iflag = -2; goto lbl_87; } // // Setup quadratic solver and solve quadratic programming problem. // After problem is solved we'll try to bound step by StpMax // (Lambda will be increased if step size is too large). // // We use BFlag variable to indicate that we have to increase Lambda. // If it is False, we will try to increase Lambda and move to new iteration. // bflag = true; minqp.minqpsetstartingpointfast(state.qpstate, state.xbase, _params); minqp.minqpsetoriginfast(state.qpstate, state.xbase, _params); minqp.minqpsetlineartermfast(state.qpstate, state.gbase, _params); minqp.minqpsetquadratictermfast(state.qpstate, state.quadraticmodel, true, 0.0, _params); for(i=0; i<=n-1; i++) { state.tmp0[i] = state.quadraticmodel[i,i]+state.lambdav/math.sqr(state.s[i]); } minqp.minqprewritediagonal(state.qpstate, state.tmp0, _params); minqp.minqpoptimize(state.qpstate, _params); minqp.minqpresultsbuf(state.qpstate, ref state.xdir, state.qprep, _params); if( state.qprep.terminationtype>0 ) { // // successful solution of QP problem // for(i_=0; i_<=n-1;i_++) { state.xdir[i_] = state.xdir[i_] - state.xbase[i_]; } v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.xdir[i_]*state.xdir[i_]; } if( math.isfinite(v) ) { v = Math.Sqrt(v); if( (double)(state.stpmax)>(double)(0) && (double)(v)>(double)(state.stpmax) ) { bflag = false; } } else { bflag = false; } } else { // // Either problem is non-convex (increase LambdaV) or constraints are inconsistent // alglib.ap.assert((state.qprep.terminationtype==-3 || state.qprep.terminationtype==-4) || state.qprep.terminationtype==-5, "MinLM: unexpected completion code from QP solver"); if( state.qprep.terminationtype==-3 ) { iflag = -3; goto lbl_87; } bflag = false; } if( !bflag ) { // // Solution failed: // try to increase lambda to make matrix positive definite and continue. // if( !increaselambda(ref state.lambdav, ref state.nu, _params) ) { iflag = -1; goto lbl_87; } goto lbl_86; } // // Step in State.XDir and it is bounded by StpMax. // // We should check stopping conditions on step size here. // DeltaX, which is used for secant updates, is initialized here. // // This code is a bit tricky because sometimes XDir<>0, but // it is so small that XDir+XBase==XBase (in finite precision // arithmetics). So we set DeltaX to XBase, then // add XDir, and then subtract XBase to get exact value of // DeltaX. // // Step length is estimated using DeltaX. // // NOTE: stopping conditions are tested // for fresh models only (ModelAge=0) // for(i_=0; i_<=n-1;i_++) { state.deltax[i_] = state.xbase[i_]; } for(i_=0; i_<=n-1;i_++) { state.deltax[i_] = state.deltax[i_] + state.xdir[i_]; } for(i_=0; i_<=n-1;i_++) { state.deltax[i_] = state.deltax[i_] - state.xbase[i_]; } state.deltaxready = true; v = 0.0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.deltax[i]/state.s[i]); } v = Math.Sqrt(v); if( (double)(v)>(double)(state.epsx) ) { goto lbl_88; } if( state.modelage!=0 ) { goto lbl_90; } // // Step is too short, model is fresh and we can rely on it. // Terminating. // state.repterminationtype = 2; if( !state.xrep ) { goto lbl_92; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.f = state.fbase; clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 21; goto lbl_rcomm; lbl_21: state.xupdated = false; lbl_92: result = false; return result; goto lbl_91; lbl_90: // // Step is suspiciously short, but model is not fresh // and we can't rely on it. // iflag = -2; goto lbl_87; lbl_91: lbl_88: // // Let's evaluate new step: // a) if we have Fi vector, we evaluate it using rcomm, and // then we manually calculate State.F as sum of squares of Fi[] // b) if we have F value, we just evaluate it through rcomm interface // // We prefer (a) because we may need Fi vector for additional // iterations // alglib.ap.assert(state.hasfi || state.hasf, "MinLM: internal error 2!"); for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.x[i_] + state.xdir[i_]; } clearrequestfields(state, _params); if( !state.hasfi ) { goto lbl_94; } state.needfi = true; state.rstate.stage = 22; goto lbl_rcomm; lbl_22: state.needfi = false; v = 0.0; for(i_=0; i_<=m-1;i_++) { v += state.fi[i_]*state.fi[i_]; } state.f = v; for(i_=0; i_<=m-1;i_++) { state.deltaf[i_] = state.fi[i_]; } for(i_=0; i_<=m-1;i_++) { state.deltaf[i_] = state.deltaf[i_] - state.fibase[i_]; } state.deltafready = true; goto lbl_95; lbl_94: state.needf = true; state.rstate.stage = 23; goto lbl_rcomm; lbl_23: state.needf = false; lbl_95: state.repnfunc = state.repnfunc+1; if( !math.isfinite(state.f) ) { // // Integrity check failed, break! // state.repterminationtype = -8; result = false; return result; } if( (double)(state.f)>=(double)(state.fbase) ) { // // Increase lambda and continue // if( !increaselambda(ref state.lambdav, ref state.nu, _params) ) { iflag = -1; goto lbl_87; } goto lbl_86; } // // We've found our step! // iflag = 0; goto lbl_87; goto lbl_86; lbl_87: if( state.userterminationneeded ) { // // User requested termination // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.repterminationtype = 8; result = false; return result; } state.nu = 1; alglib.ap.assert(iflag>=-3 && iflag<=0, "MinLM: internal integrity check failed!"); if( iflag==-3 ) { state.repterminationtype = -3; result = false; return result; } if( iflag==-2 ) { state.modelage = state.maxmodelage+1; goto lbl_73; } if( iflag==-1 ) { goto lbl_74; } // // Levenberg-Marquardt step is ready. // Compare predicted vs. actual decrease and decide what to do with lambda. // // NOTE: we expect that State.DeltaX contains direction of step, // State.F contains function value at new point. // alglib.ap.assert(state.deltaxready, "MinLM: deltaX is not ready"); t = 0; for(i=0; i<=n-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.quadraticmodel[i,i_]*state.deltax[i_]; } t = t+state.deltax[i]*state.gbase[i]+0.5*state.deltax[i]*v; } state.predicteddecrease = -t; state.actualdecrease = -(state.f-state.fbase); if( (double)(state.predicteddecrease)<=(double)(0) ) { goto lbl_74; } v = state.actualdecrease/state.predicteddecrease; if( (double)(v)>=(double)(0.1) ) { goto lbl_96; } if( increaselambda(ref state.lambdav, ref state.nu, _params) ) { goto lbl_98; } // // Lambda is too large, we have to break iterations. // state.repterminationtype = 7; if( !state.xrep ) { goto lbl_100; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.f = state.fbase; clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 24; goto lbl_rcomm; lbl_24: state.xupdated = false; lbl_100: result = false; return result; lbl_98: lbl_96: if( (double)(v)>(double)(0.5) ) { decreaselambda(ref state.lambdav, ref state.nu, _params); } // // Accept step, report it and // test stopping conditions on iterations count and function decrease. // // NOTE: we expect that State.DeltaX contains direction of step, // State.F contains function value at new point. // // NOTE2: we should update XBase ONLY. In the beginning of the next // iteration we expect that State.FIBase is NOT updated and // contains old value of a function vector. // for(i_=0; i_<=n-1;i_++) { state.xbase[i_] = state.xbase[i_] + state.deltax[i_]; } if( !state.xrep ) { goto lbl_102; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 25; goto lbl_rcomm; lbl_25: state.xupdated = false; lbl_102: state.repiterationscount = state.repiterationscount+1; if( state.repiterationscount>=state.maxits && state.maxits>0 ) { state.repterminationtype = 5; } if( state.repterminationtype<=0 ) { goto lbl_104; } if( !state.xrep ) { goto lbl_106; } // // Report: XBase contains new point, F contains function value at new point // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 26; goto lbl_rcomm; lbl_26: state.xupdated = false; lbl_106: result = false; return result; lbl_104: state.modelage = state.modelage+1; goto lbl_73; lbl_74: // // Lambda is too large, we have to break iterations. // state.repterminationtype = 7; if( !state.xrep ) { goto lbl_108; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.f = state.fbase; clearrequestfields(state, _params); state.xupdated = true; state.rstate.stage = 27; goto lbl_rcomm; lbl_27: state.xupdated = false; lbl_108: lbl_37: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = n; state.rstate.ia[1] = m; state.rstate.ia[2] = iflag; state.rstate.ia[3] = i; state.rstate.ia[4] = k; state.rstate.ba[0] = bflag; state.rstate.ra[0] = v; state.rstate.ra[1] = s; state.rstate.ra[2] = t; state.rstate.ra[3] = fnew; return result; } /************************************************************************* This function activates/deactivates verification of the user-supplied analytic Jacobian. Upon activation of this option OptGuard integrity checker performs numerical differentiation of your target function vector at the initial point (note: future versions may also perform check at the final point) and compares numerical Jacobian with analytic one provided by you. If difference is too large, an error flag is set and optimization session continues. After optimization session is over, you can retrieve the report which stores both Jacobians, and specific components highlighted as suspicious by the OptGuard. The OptGuard report can be retrieved with minlmoptguardresults(). IMPORTANT: gradient check is a high-overhead option which will cost you about 3*N additional function evaluations. In many cases it may cost as much as the rest of the optimization session. YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY. NOTE: unlike previous incarnation of the gradient checking code, OptGuard does NOT interrupt optimization even if it discovers bad gradient. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step used for numerical differentiation: * TestStep=0 turns verification off * TestStep>0 activates verification You should carefully choose TestStep. Value which is too large (so large that function behavior is non- cubic at this scale) will lead to false alarms. Too short step will result in rounding errors dominating numerical derivative. You may use different step for different parameters by means of setting scale with minlmsetscale(). === EXPLANATION ========================================================== In order to verify gradient algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minlmoptguardgradient(minlmstate state, double teststep, alglib.xparams _params) { alglib.ap.assert(math.isfinite(teststep), "MinLMOptGuardGradient: TestStep contains NaN or INF"); alglib.ap.assert((double)(teststep)>=(double)(0), "MinLMOptGuardGradient: invalid argument TestStep(TestStep<0)"); state.teststep = teststep; } /************************************************************************* Results of OptGuard integrity check, should be called after optimization session is over. OptGuard checks analytic Jacobian against reference value obtained by numerical differentiation with user-specified step. NOTE: other optimizers perform additional OptGuard checks for things like C0/C1-continuity violations. However, LM optimizer can check only for incorrect Jacobian. The reason is that unlike line search methods LM optimizer does not perform extensive evaluations along the line. Thus, we simply do not have enough data to catch C0/C1-violations. This check is activated with minlmoptguardgradient() function. Following flags are set when these errors are suspected: * rep.badgradsuspected, and additionally: * rep.badgradfidx for specific function (Jacobian row) suspected * rep.badgradvidx for specific variable (Jacobian column) suspected * rep.badgradxbase, a point where gradient/Jacobian is tested * rep.badgraduser, user-provided gradient/Jacobian * rep.badgradnum, reference gradient/Jacobian obtained via numerical differentiation INPUT PARAMETERS: state - algorithm state OUTPUT PARAMETERS: rep - OptGuard report -- ALGLIB -- Copyright 21.11.2018 by Bochkanov Sergey *************************************************************************/ public static void minlmoptguardresults(minlmstate state, optguardapi.optguardreport rep, alglib.xparams _params) { optserv.smoothnessmonitorexportreport(state.smonitor, rep, _params); } /************************************************************************* Levenberg-Marquardt algorithm results NOTE: if you activated OptGuard integrity checking functionality and want to get OptGuard report, it can be retrieved with the help of minlmoptguardresults() function. INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report; includes termination codes and additional information. Termination codes are listed below, see comments for this structure for more info. Termination code is stored in rep.terminationtype field: * -8 optimizer detected NAN/INF values either in the function itself, or in its Jacobian * -3 constraints are inconsistent * 2 relative step is no more than EpsX. * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible * 8 terminated by user who called minlmrequesttermination(). X contains point which was "current accepted" when termination request was submitted. -- ALGLIB -- Copyright 10.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmresults(minlmstate state, ref double[] x, minlmreport rep, alglib.xparams _params) { x = new double[0]; minlmresultsbuf(state, ref x, rep, _params); } /************************************************************************* Levenberg-Marquardt algorithm results Buffered implementation of MinLMResults(), which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 10.03.2009 by Bochkanov Sergey *************************************************************************/ public static void minlmresultsbuf(minlmstate state, ref double[] x, minlmreport rep, alglib.xparams _params) { int i_ = 0; if( alglib.ap.len(x)=state.n, "MinLMRestartFrom: Length(X)=1, "MinLMCreateFJ: N<1!"); alglib.ap.assert(m>=1, "MinLMCreateFJ: M<1!"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinLMCreateFJ: Length(X)(double)(0.25*lnmax) ) { return result; } if( (double)(lnnu+Math.Log(2))>(double)(lnmax) ) { return result; } lambdav = lambdav*lambdaup*nu; nu = nu*2; result = true; return result; } /************************************************************************* Decreases lambda, but leaves it unchanged when there is danger of underflow. *************************************************************************/ private static void decreaselambda(ref double lambdav, ref double nu, alglib.xparams _params) { nu = 1; if( (double)(Math.Log(lambdav)+Math.Log(lambdadown))<(double)(Math.Log(math.minrealnumber)) ) { lambdav = math.minrealnumber; } else { lambdav = lambdav*lambdadown; } } /************************************************************************* This function compares actual decrease vs predicted decrease and updates LambdaV/Nu accordingly. INPUT PARAMETERS: QuadraticModel - array[N,N], full Hessian matrix of quadratic model at deltaX=0 GBase - array[N], gradient at deltaX=0 FBase - F(deltaX=0) N - size DeltaX - step vector FNew - new function value LambdaV - lambda-value, updated on exit Nu - Nu-multiplier, updated on exit On exit it returns: * Result=0 - if we have to continue iterations * Result<>0 - if termination with completion code Result is requested -- ALGLIB -- Copyright 17.02.2017 by Bochkanov Sergey *************************************************************************/ private static int checkdecrease(double[,] quadraticmodel, double[] gbase, double fbase, int n, double[] deltax, double fnew, ref double lambdav, ref double nu, alglib.xparams _params) { int result = 0; int i = 0; double v = 0; double t = 0; double predicteddecrease = 0; double actualdecrease = 0; int i_ = 0; result = 0; t = 0; for(i=0; i<=n-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += quadraticmodel[i,i_]*deltax[i_]; } t = t+deltax[i]*gbase[i]+0.5*deltax[i]*v; } predicteddecrease = -t; actualdecrease = -(fnew-fbase); if( (double)(predicteddecrease)<=(double)(0) ) { result = 7; return result; } v = actualdecrease/predicteddecrease; if( (double)(v)<(double)(0.1) ) { if( !increaselambda(ref lambdav, ref nu, _params) ) { // // Lambda is too large, we have to break iterations. // result = 7; return result; } } if( (double)(v)>(double)(0.5) ) { decreaselambda(ref lambdav, ref nu, _params); } return result; } /************************************************************************* This function initializes step finder object with problem statement; model parameters specified during this call should not (and can not) change during object lifetime (although it is possible to re-initialize object with different settings). This function reuses internally allocated objects as much as possible. In addition to initializing step finder, this function enforces feasibility in initial point X passed to this function. It is important that LM iteration starts from feasible point and performs feasible steps; RETURN VALUE: True for successful initialization False for inconsistent constraints; you should not use step finder if it returned False. *************************************************************************/ private static bool minlmstepfinderinit(minlmstepfinder state, int n, int m, int maxmodelage, bool hasfi, ref double[] xbase, double[] bndl, double[] bndu, double[,] cleic, int nec, int nic, double[] s, double stpmax, double epsx, alglib.xparams _params) { bool result = new bool(); int i = 0; state.n = n; state.m = m; state.maxmodelage = maxmodelage; state.hasfi = hasfi; state.stpmax = stpmax; state.epsx = epsx; // // Allocate temporaries, create QP solver, select QP algorithm // apserv.rvectorsetlengthatleast(ref state.bndl, n, _params); apserv.rvectorsetlengthatleast(ref state.bndu, n, _params); apserv.rvectorsetlengthatleast(ref state.s, n, _params); apserv.bvectorsetlengthatleast(ref state.havebndl, n, _params); apserv.bvectorsetlengthatleast(ref state.havebndu, n, _params); apserv.rvectorsetlengthatleast(ref state.x, n, _params); apserv.rvectorsetlengthatleast(ref state.xbase, n, _params); apserv.rvectorsetlengthatleast(ref state.tmp0, n, _params); apserv.rvectorsetlengthatleast(ref state.modeldiag, n, _params); apserv.ivectorsetlengthatleast(ref state.tmpct, nec+nic, _params); apserv.rvectorsetlengthatleast(ref state.xdir, n, _params); if( hasfi ) { apserv.rvectorsetlengthatleast(ref state.fi, m, _params); apserv.rvectorsetlengthatleast(ref state.fibase, m, _params); } for(i=0; i<=n-1; i++) { alglib.ap.assert(math.isfinite(bndl[i]) || Double.IsNegativeInfinity(bndl[i]), "MinLM: integrity check failed"); alglib.ap.assert(math.isfinite(bndu[i]) || Double.IsPositiveInfinity(bndu[i]), "MinLM: integrity check failed"); state.bndl[i] = bndl[i]; state.havebndl[i] = math.isfinite(bndl[i]); state.bndu[i] = bndu[i]; state.havebndu[i] = math.isfinite(bndu[i]); state.s[i] = s[i]; } for(i=0; i<=nec-1; i++) { state.tmpct[i] = 0; } for(i=0; i<=nic-1; i++) { state.tmpct[nec+i] = -1; } minqp.minqpcreate(n, state.qpstate, _params); if( nec+nic==0 ) { minqp.minqpsetalgoquickqp(state.qpstate, 0.0, 0.0, apserv.coalesce(0.01*epsx, 1.0E-12, _params), 10, true, _params); } else { minqp.minqpsetalgodenseaul(state.qpstate, apserv.coalesce(0.01*epsx, 1.0E-12, _params), 100, 10, _params); } minqp.minqpsetbc(state.qpstate, bndl, bndu, _params); minqp.minqpsetlc(state.qpstate, cleic, state.tmpct, nec+nic, _params); minqp.minqpsetscale(state.qpstate, s, _params); // // Check feasibility of constraints: // * check/enforce box constraints (straightforward) // * prepare QP subproblem which return us a feasible point // result = true; for(i=0; i<=n-1; i++) { if( (state.havebndl[i] && state.havebndu[i]) && (double)(state.bndl[i])>(double)(state.bndu[i]) ) { result = false; return result; } if( state.havebndl[i] && (double)(xbase[i])<(double)(state.bndl[i]) ) { xbase[i] = state.bndl[i]; } if( state.havebndu[i] && (double)(xbase[i])>(double)(state.bndu[i]) ) { xbase[i] = state.bndu[i]; } } if( nec+nic>0 ) { // // Well, we have linear constraints... let's use heavy machinery. // // We will modify QP solver state below, but everything will be // restored in MinLMStepFinderStart(). // sparse.sparsecreate(n, n, n, state.tmpsp, _params); for(i=0; i<=n-1; i++) { sparse.sparseset(state.tmpsp, i, i, 0.5, _params); state.tmp0[i] = 0; } minqp.minqpsetstartingpointfast(state.qpstate, xbase, _params); minqp.minqpsetoriginfast(state.qpstate, xbase, _params); minqp.minqpsetlineartermfast(state.qpstate, state.tmp0, _params); minqp.minqpsetquadratictermsparse(state.qpstate, state.tmpsp, true, _params); minqp.minqpoptimize(state.qpstate, _params); minqp.minqpresultsbuf(state.qpstate, ref xbase, state.qprep, _params); } return result; } /************************************************************************* This function prepares LM step search session. *************************************************************************/ private static void minlmstepfinderstart(minlmstepfinder state, double[,] quadraticmodel, double[] gbase, double fbase, double[] xbase, double[] fibase, int modelage, alglib.xparams _params) { int i = 0; int n = 0; n = state.n; state.rstate.ia = new int[2+1]; state.rstate.ba = new bool[0+1]; state.rstate.ra = new double[0+1]; state.rstate.stage = -1; state.modelage = modelage; state.fbase = fbase; if( state.hasfi ) { for(i=0; i<=state.m-1; i++) { state.fibase[i] = fibase[i]; } } for(i=0; i<=n-1; i++) { state.xbase[i] = xbase[i]; state.modeldiag[i] = quadraticmodel[i,i]; } minqp.minqpsetstartingpointfast(state.qpstate, xbase, _params); minqp.minqpsetoriginfast(state.qpstate, xbase, _params); minqp.minqpsetlineartermfast(state.qpstate, gbase, _params); minqp.minqpsetquadratictermfast(state.qpstate, quadraticmodel, true, 0.0, _params); } /************************************************************************* This function runs LM step search session. // // Find value of Levenberg-Marquardt damping parameter which: // * leads to positive definite damped model // * within bounds specified by StpMax // * generates step which decreases function value // // After this block IFlag is set to: // * -8, if infinities/NANs were detected in function values/gradient // * -3, if constraints are infeasible // * -2, if model update is needed (either Lambda growth is too large // or step is too short, but we can't rely on model and stop iterations) // * -1, if model is fresh, Lambda have grown too large, termination is needed // * 0, if everything is OK, continue iterations // * >0 - successful completion (step size is small enough) // // State.Nu can have any value on enter, but after exit it is set to 1.0 // *************************************************************************/ private static bool minlmstepfinderiteration(minlmstepfinder state, ref double lambdav, ref double nu, ref double[] xnew, double[] deltax, ref bool deltaxready, double[] deltaf, ref bool deltafready, ref int iflag, ref double fnew, ref int ncholesky, alglib.xparams _params) { bool result = new bool(); int i = 0; bool bflag = new bool(); double v = 0; int n = 0; int m = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { i = state.rstate.ia[0]; n = state.rstate.ia[1]; m = state.rstate.ia[2]; bflag = state.rstate.ba[0]; v = state.rstate.ra[0]; } else { i = -838; n = 939; m = -526; bflag = true; v = -541; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } // // Routine body // iflag = -99; n = state.n; m = state.m; lbl_2: if( false ) { goto lbl_3; } deltaxready = false; deltafready = false; // // Do we need model update? // if( state.modelage>0 && (double)(nu)>=(double)(suspiciousnu) ) { iflag = -2; goto lbl_3; } // // Setup quadratic solver and solve quadratic programming problem. // After problem is solved we'll try to bound step by StpMax // (Lambda will be increased if step size is too large). // // We use BFlag variable to indicate that we have to increase Lambda. // If it is False, we will try to increase Lambda and move to new iteration. // bflag = true; for(i=0; i<=n-1; i++) { state.tmp0[i] = state.modeldiag[i]+lambdav/math.sqr(state.s[i]); } minqp.minqprewritediagonal(state.qpstate, state.tmp0, _params); minqp.minqpoptimize(state.qpstate, _params); minqp.minqpresultsbuf(state.qpstate, ref xnew, state.qprep, _params); ncholesky = ncholesky+state.qprep.ncholesky; if( state.qprep.terminationtype==-3 ) { // // Infeasible constraints // iflag = -3; goto lbl_3; } if( state.qprep.terminationtype==-4 || state.qprep.terminationtype==-5 ) { // // Unconstrained direction of negative curvature was detected // if( !increaselambda(ref lambdav, ref nu, _params) ) { iflag = -1; goto lbl_3; } goto lbl_2; } alglib.ap.assert(state.qprep.terminationtype>0, "MinLM: unexpected completion code from QP solver"); for(i_=0; i_<=n-1;i_++) { state.xdir[i_] = xnew[i_]; } for(i_=0; i_<=n-1;i_++) { state.xdir[i_] = state.xdir[i_] - state.xbase[i_]; } v = 0.0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.xdir[i]/state.s[i]); } if( math.isfinite(v) ) { v = Math.Sqrt(v); if( (double)(state.stpmax)>(double)(0) && (double)(v)>(double)(state.stpmax) ) { bflag = false; } } else { bflag = false; } if( !bflag ) { // // Solution failed: // try to increase lambda to make matrix positive definite and continue. // if( !increaselambda(ref lambdav, ref nu, _params) ) { iflag = -1; goto lbl_3; } goto lbl_2; } // // Step in State.XDir and it is bounded by StpMax. // // We should check stopping conditions on step size here. // DeltaX, which is used for secant updates, is initialized here. // // This code is a bit tricky because sometimes XDir<>0, but // it is so small that XDir+XBase==XBase (in finite precision // arithmetics). So we set DeltaX to XBase, then // add XDir, and then subtract XBase to get exact value of // DeltaX. // // Step length is estimated using DeltaX. // // NOTE: stopping conditions are tested // for fresh models only (ModelAge=0) // for(i_=0; i_<=n-1;i_++) { deltax[i_] = xnew[i_]; } for(i_=0; i_<=n-1;i_++) { deltax[i_] = deltax[i_] - state.xbase[i_]; } deltaxready = true; v = 0.0; for(i=0; i<=n-1; i++) { v = v+math.sqr(deltax[i]/state.s[i]); } v = Math.Sqrt(v); if( (double)(v)<=(double)(state.epsx) ) { if( state.modelage==0 ) { // // Step is too short, model is fresh and we can rely on it. // Terminating. // iflag = 2; goto lbl_3; } else { // // Step is suspiciously short, but model is not fresh // and we can't rely on it. // iflag = -2; goto lbl_3; } } // // Let's evaluate new step: // a) if we have Fi vector, we evaluate it using rcomm, and // then we manually calculate State.F as sum of squares of Fi[] // b) if we have F value, we just evaluate it through rcomm interface // // We prefer (a) because we may need Fi vector for additional // iterations // for(i_=0; i_<=n-1;i_++) { state.x[i_] = xnew[i_]; } state.needf = false; state.needfi = false; if( !state.hasfi ) { goto lbl_4; } state.needfi = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needfi = false; v = 0.0; for(i_=0; i_<=m-1;i_++) { v += state.fi[i_]*state.fi[i_]; } fnew = v; for(i_=0; i_<=m-1;i_++) { deltaf[i_] = state.fi[i_]; } for(i_=0; i_<=m-1;i_++) { deltaf[i_] = deltaf[i_] - state.fibase[i_]; } deltafready = true; goto lbl_5; lbl_4: state.needf = true; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: state.needf = false; fnew = state.f; lbl_5: if( !math.isfinite(fnew) ) { // // Integrity check failed, break! // iflag = -8; goto lbl_3; } if( (double)(fnew)>=(double)(state.fbase) ) { // // Increase lambda and continue // if( !increaselambda(ref lambdav, ref nu, _params) ) { iflag = -1; goto lbl_3; } goto lbl_2; } // // We've found our step! // iflag = 0; goto lbl_3; goto lbl_2; lbl_3: nu = 1; alglib.ap.assert(((iflag>=-3 && iflag<=0) || iflag==-8) || iflag>0, "MinLM: internal integrity check failed!"); result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = i; state.rstate.ia[1] = n; state.rstate.ia[2] = m; state.rstate.ba[0] = bflag; state.rstate.ra[0] = v; return result; } } }