NDOSolver / FiOracle
Interfaces and Solvers for NonDifferentiable Optimization
|
Public Member Functions | |
TestFi (cIndex NV, cLMNum L0) | |
virtual HpNum | Fi (cIndex wFi=Inf< Index >()) override final |
virtual Index | GetGi (SgRow SubG, cIndex_Set &SGBse, cIndex Name=Inf< Index >(), cIndex strt=0, Index stp=Inf< Index >()) override final |
virtual HpNum | GetLowerBound (cIndex wFi=Inf< Index >()) override final |
virtual | ~TestFi () |
destructor of the class: it must be virtual | |
![]() | |
virtual NDOSolver * | GetNDOSolver (void) |
FiOracle (void) | |
virtual void | SetNDOSolver (NDOSolver *NwSlvr=0) |
virtual void | SetFiLog (ostream *outs=0, const char lvl=0) |
virtual void | SetFiTime (const bool TimeIt=true) |
virtual void | SetMaxName (cIndex MxNme=0) |
virtual Index | GetNumVar (void) const |
virtual Index | GetMaxNumVar (void) const |
virtual Index | GetNrFi (void) const |
virtual Index | MaxNConst (void) |
virtual bool | IsFiContinuous (cIndex NrFi=Inf< Index >()) |
virtual bool | IsFiConvex (cIndex NrFi=Inf< Index >()) |
virtual bool | HasGi (cIndex NrFi=Inf< Index >()) |
virtual bool | IsGiContinuous (cIndex NrFi) |
virtual bool | HasH (cIndex NrFi) |
virtual bool | IsHContinuous (cIndex NrFi) |
virtual Index | GetMaxName (void) const |
virtual HpNum | GetMinusInfinity (void) |
virtual Index | GetMaxNZ (cIndex wFi=Inf< Index >()) const |
virtual Index | GetMaxCNZ (cIndex wFi=Inf< Index >()) const |
virtual bool | GetUC (cIndex i) |
virtual LMNum | GetUB (cIndex i) |
virtual LMNum | GetBndEps (void) |
virtual HpNum | GetGlobalLipschitz (cIndex wFi=Inf< Index >()) |
virtual Index | GetBNC (cIndex wFi) |
virtual Index | GetBNR (cIndex wFi) |
virtual Index | GetBNZ (cIndex wFi) |
virtual void | GetBDesc (cIndex wFi, int *Bbeg, int *Bind, double *Bval, double *lhs, double *rhs, double *cst, double *lbd, double *ubd) |
virtual Index | GetANZ (cIndex wFi, cIndex strt=0, Index stp=Inf< Index >()) |
virtual void | GetADesc (cIndex wFi, int *Abeg, int *Aind, double *Aval, cIndex strt=0, Index stp=Inf< Index >()) |
virtual void | SetLambda (cLMRow Lmbd=0) |
virtual void | SetLamBase (cIndex_Set LmbdB=0, cIndex LmbdBD=0) |
virtual bool | SetPrecision (HpNum Eps) |
virtual bool | NewGi (cIndex wFi=Inf< Index >()) |
virtual HpNum | GetVal (cIndex Name=Inf< Index >()) |
virtual void | SetGiName (cIndex Name) |
virtual FiStatus | GetFiStatus (Index wFi=Inf< Index >()) |
void | FiTime (double &t_us, double &t_ss) |
double | FiTime (void) |
virtual void | Deleted (cIndex i=Inf< Index >()) |
virtual void | Aggregate (cHpRow Mlt, cIndex_Set NmSt, cIndex Dm, cIndex NwNm) |
virtual | ~FiOracle () |
Protected Attributes | |
LMNum | Cntr |
![]() | |
NDOSolver * | Slvr |
Index | NumVar |
(current) number of variables if Fi() | |
Index | MaxName |
maximum name to be used in SetGiName() | |
cLMRow | Lambda |
cIndex_Set | LamBase |
Index | LamBDim |
length of LamBase[] | |
bool | LHasChgd |
ostream * | FiLog |
the output stream object for log purposes | |
char | FiLLvl |
the "level of verbosity" of the log | |
OPTtimers * | Fit |
OPTtimer for timing purposes. | |
Additional Inherited Members | |
![]() | |
enum | FiStatus { kFiNorm = 0 , kFiError , kFiChgd , kFiStop , kFiCont } |
NV is the number of variables, and L0 the "center" of the function; these parameters are only needed for the sake of! this example: remove them and replace with your own.
This method must return the value of the function Fi to be minimized in the point Lmbd set by SetLambda() and SetLamBase() [see above]. Usually, Fi() has to be called after SetLambda(), the only exception being if Fi( < all-zero vector > ) is required, since Lambda == all-zero is the default assumed if SetLambda() is not called.
wFi tells the value of which of the components of Fi() [see GetNrFi() above] has to be returned. Note that all functions, even those that are declared non-separable (GetNrFi() == 1), are treated as the sum of a generic function Fi[ 1 ]( Lambda ) plus a linear function b * Lambda; this is always possible (b can be == 0). The meaning of wFi is:
In case an error occurs in the calculation of Fi(), this can be signalled immediately by returning - INF (a proper convex function never evaluates to - Infinity); if a "decent" value is available, an alternative is to use GetFiStatus() [see below]. Note that an important special case exists when it may be reasonable for a FiOracle to return - INF; this is when (one component of) Fi() is not a proper convex function, that is, it is identically equal to - Infinity*. In the Lagrangian case, this corresponds to the fact that (one of) the Lagrangian problem(s) actually is empty. It is for this reason that if Fi() returns - INF, then the NDO solver has the right to declare Fi() unbounded below.
Fi() may also return + INF, meaning that the point set by SetLambda() is outside the feasible region (the domain of the function to be minimized). In this case, the information returned by GetGi() [see below] has a different meaning. However, note that, when Fi is separable, only some of its components may be undefined, while other may still give valid subgradients; in other words, the point set by SetLambda() may belong to Dom( Fi[ h ] ) for some h, and not for others. Thus, as for subgradients, when a constraint is generated the value of wFi in the call to Fi() which returned INF allows the NDOSolver to distinguish to which component the constraint belongs to.
Usually, there will be only one call to Fi() (for each component) for each call to SetLambda(); however, multiple calls are allowed if the "precision" changes [see SetPrecision() above].
Implements FiOracle.
|
finaloverridevirtual |
GetGi() [and GetVal(), see below] can be used to query information about the items. The method allows to access both the newly produced item corresponding to the last call to NewGi() [see above], if it has not been "named" yet [see SetGiName() below], and all "named" items recorded in the FiOracle memory, if any [see SetMaxName() above]. This is specified by the value of Name. If Name < n, where n is the max name set by the latest call to SetMaxName(), then the required information is about the item Name. Note that Name must be a valid item name, i.e., a name that has been previously used in some call to SetGiName(), and not used in any call to Deleted() after the last call of SetGiName() where it was used. If Name == n, then the required information is about the constant subgradient of the linear 0-th component of Fi. Finally, if Name > n then the required information is about the newly produced "unnamed" item. Note that, given Name, it is immediately known to which component of Fi the associated item belongs.
GetGi() can be used to retrieve (part of) the linear GetNumVar()-vector which characterizes the item. SubG is a pointer to a vector of SgNum of appropriate length (see below) where the item have to be written. The indices strt and stp tell that what is required is the part of the vector corresponding to variables with "names" comprised between strt (included) and min{ stp , GetNumVar() } (excluded); the name of a variable is just its position in the vector Lmbd. Upon return, the required information has to be written in SubG; the "format" of SubG depends on what is returned in the read-only pointer SGBse. If SGBse == 0, then SubG[] is in "dense" format, i.e., SubG[ i ] is the entry of the subgradient relative to variable strt + i for i in 0 .. min( GetNumVar() , stp ) - strt. If SGBse != 0, then it must point to the vector of indices of nonzero entries of the subgradient (ordered in increasing sense and Inf< Index >()-terminated). That is, for i = strt ... stp - 1, the "real" SubG is
/ SubG[ j ] if exists j < k s.t. SGBse[ j ] = i
SubG[ i ] = | \ 0 otherwise.
where k, the number of nonzeroes of SubG[], is returned by GetGi(). If SGBse == 0, then GetGi() should return min{ stp , GetNumVar() } - strt, i.e., the length of the "dense" SubG[]. Note that, if SGBse != 0, the Indices in SGBse[] are in the range [strt, min{ stp , GetNumVar() }).
According to whether the item is a subgradient or a constraint (which is revealed by the return value of Fi()), GetMaxNZ( wFi ) and GetMaxCNZ( wFi ) [see above] provide an upper bound on the maximum number of nonzeroes in the item; let us denote that by MxNZ. Then, the length of the vector SubG is required to be at least
min( MxNZ , min( GetNumVar() , stp ) - strt ) ,
i.e., the minimum possible amount that is guaranteed to contain all the possible nonzeroes. Hence, a non-0 SGBDim must always be returned if MxNZ < min( GetNumVar() , stp ) - strt ), as in this case the vector SubG is not long enough to accommodate (the required part of) an item in "dense" format. Also, note that the memory for SGBase has to be provided by the FiOracle, but the NDO solver is not allowed to keep the pointer and must copy the vector, if it so requires, because the content may change at the subsequent call to any method of the FiOracle.
Allowing a NDO solver to query information about items "stored" in the FiOracle [see SetGiName() below] has different uses. For instance, the NDO solver may use variables generation techniques which try to solve a NDO problem by restricting it to a (small) "active set" of the variables, minimizing the function in that subspace and possibly revising the active set. Thus, at some point the NDO solver may not need some of the information related to an item, but this information may still be required later. A possibly more important use, however, is tied to the handling of changes in the function, which may occur in applications as those alluded to in the general notes. The implementation of the methods AddVariables() and ChgSbG() of NDOSolver [see NDOSlver.h] typically require calls to GetGi() to get information about some components of the items "stored" in the FiOracle. Note that these methods of class NDOSolver, in turn, can be called by the FiOracle itself, e.g. in the method GetFiStatus() [see below], so that the FiOracle may well be "prepared" when a call occurs simply because the caller was, ultimately, itself.
Implements FiOracle.
In some cases, a Lower Bound on the minimal value of Fi is known; if Fi is a Lagrangian function, for instance, the objective function value c( x ) of any dual feasible solution x (s.t. A( x ) [<]= b and x \in X) gives a valid lower bound. This value can be useful for NDO solver, especially if it is tight; for one thing, it ensures that the function is not unbounded below.
GetLowerBound() allows to retrieve this value from the FiOracle, which may return - INF to signal that no Lower Bound is known. Note that the value may change—typically, increase—over time, e.g. as better feasible solutions of the dual problem are generated, so that it may be worth to call GetLowerBound() after each call to Fi() to check if a better bound has become available. Of course, the value returned by GetLowerBound() must be no smaller than that returned by GetMinusInfinity() [see above], although the two can be equal.
Furthermore, changes in Fi() (number of variables etc.) may possibly render any previously obtained Lower Bound invalid, so that this method should be called at least each time Fi() changes; failure to do that could lead to early and incorrect termination of the optimization due to the use of an incorrect Lower Bound.
Lower Bounds may be available separately for the wFi-th component of Fi, 1 <= wFi <= GetNrFi(); these are returned by GetLowerBound( wFi ). Note that if wFi is an "easy" component then it makes no sense to provide an explicit lower bound, since everything is known for that component and the Lower Bound, if any, is already implicit in the description; thus, GetLowerBound( wFi ) should always return - INF if wFi is "easy". GetLowerBound( 0 ) also makes no sense (i.e., - INF is again a good return value) while GetLowerBound( wFi ) for wFi > NrFi must return a "global" Lower Bound for the whole Fi.
Reimplemented from FiOracle.
|
protected |
the minimum of the function (L0): this protected data structure is only needed for the sake of this example, remove it and replace with your own.