Description of large scale optimization package, Version 2.1.0 ############################################################## Patrick Heimbach, MIT/EAPS, 02-Mar-2000 reference: ######### J.C. Gilbert & C. Lemarechal Some numerical experiments with variable-storage quasi-Newton algorithms Mathematical Programming 45 (1989), pp. 407-435 flow chart ########## lsopt_top | |---- check arguments |---- CALL INSTORE | | | |---- determine whether OPWARMI available: | * if no: cold start: create OPWARMI | * if yes: warm start: read from OPWARMI | create or open OPWARMD | |---- check consistency between OPWARMI and model parameters | |---- >>> if COLD start: <<< | | first simulation with f.g. xx_0; output: first ff_0, gg_0 | | set first preconditioner value xdiff_0 to 1 | | store xx(0), gg(0), xdiff(0) to OPWARMD (first 3 entries) | | | >>> else: WARM start: <<< | read xx(i), gg(i) from OPWARMD (first 2 entries) | for first warm start after cold start, i=0 | | | |---- /// if ITMAX > 0: perform optimization (increment loop index i) | ( | )---- save current values of gg(i-1) -> gold(i-1), ff -> fold(i-1) | (---- CALL LSUPDXX | ) | | ( |---- >>> if jmax=0 <<< | ) | | first optimization after cold start: | ( | | preconditioner estimated via ff_0 - ff_(first guess) | ) | | dd(i-1) = -gg(i-1)*preco | ( | | | ) | >>> if jmax > 0 <<< | ( | dd(i-1) = -gg(i-1) | ) | CALL HESSUPD | ( | | | ) | |---- dd(i-1) modified via Hessian approx. | ( | | ) |---- >>> if >= 0 <<< | ( | ifail = 4 | ) | | ( |---- compute step size: tact(i-1) | ) |---- compute update: xdiff(i) = xx(i-1) + tact(i-1)*dd(i-1) | ( | )---- >>> if ifail = 4 <<< | ( goto 1000 | ) | (---- CALL OPTLINE / LSLINE | ) | | ( | | ) | | ( |---- /// loop over simulations | ) ( | ( )---- CALL SIMUL | ) ( | | ( ) |---- input: xdiff(i) | ) ( |---- output: ff(i), gg(i) | ( ) |---- >>> if ONLINE <<< | ) ( runs model and adjoint | ( ) >>> if OFFLINE <<< | ) ( reads those values from file | ( ) | ) (---- 1st Wolfe test: | ( ) ff(i) <= tact*xpara1* | ) ( | ( )---- 2nd Wolfe test: | ) ( >= xpara2* | ( ) | ) (---- >>> if 1st and 2nd Wolfe tests ok <<< | ( ) | 320: update xx: xx(i) = xdiff(i) | ) ( | | ( ) >>> else if 1st Wolfe test not ok <<< | ) ( | 500: INTERpolate new tact: | ( ) | barr*tact < tact < (1-barr)*tact | ) ( | CALL CUBIC | ( ) | | ) ( >>> else if 2nd Wolfe test not ok <<< | ( ) 350: EXTRApolate new tact: | ) ( (1+barmin)*tact < tact < 10*tact | ( ) CALL CUBIC | ) ( | ( )---- >>> if new tact > tmax <<< | ) ( | ifail = 7 | ( ) | | ) (---- >>> if new tact < tmin OR tact*dd < machine precision <<< | ( ) | ifail = 8 | ) ( | | ( )---- >>> else <<< | ) ( update xdiff for new simulation | ( ) | ) \\\ if nfunc > 1: use inter-/extrapolated tact and xdiff | ( for new simulation | ) N.B.: new xx is thus not based on new gg, but | ( rather on new step size tact | ) | ( | ) | (---- store new values xx(i), gg(i) to OPWARMD (first 2 entries) | )---- >>> if ifail = 7,8,9 <<< | ( goto 1000 | ) | (---- compute new pointers jmin, jmax to include latest values | ) gg(i)-gg(i-1), xx(i)-xx(i-1) to Hessian matrix estimate | (---- store gg(i)-gg(i-1), xx(i)-xx(i-1) to OPWARMD | ) (entries 2*jmax+2, 2*jmax+3) | ( | )---- CALL DGSCALE | ( | | ) |---- call dostore | ( | | | ) | |---- read preconditioner of previous iteration diag(i-1) | ( | from OPWARMD (3rd entry) | ) | | ( |---- compute new preconditioner diag(i), based upon diag(i-1), | ) | gg(i)-gg(i-1), xx(i)-xx(i-1) | ( | | ) |---- call dostore | ( | | ) |---- write new preconditioner diag(i) to OPWARMD (3rd entry) | ( |---- \\\ end of optimization iteration loop | | | |---- CALL OUTSTORE | | | |---- store gnorm0, ff(i), current pointers jmin, jmax, iterabs to OPWARMI | |---- >>> if OFFLINE version <<< | xx(i+1) needs to be computed as input for offline optimization | | | |---- CALL LSUPDXX | | | | | |---- compute dd(i), tact(i) -> xdiff(i+1) = x(i) + tact(i)*dd(i) | | | |---- CALL WRITE_CONTROL | | | | | |---- write xdiff(i+1) to special file for offline optim. | |---- print final information | O Remarks: ####### 1. Difference between offline/online version -------------------------------------------- - Offline version: Every call to simul refers to a read procedure which reads the result of an offline forward and adjoint run Therefore, only one call to simul is allowed, itmax = 0, for cold start itmax = 1, for warm start Also, at the end, x(i+1) needs to be computed and saved to be available for the offline model and adjoint run - Online version: Every call to simul refers to an execution of the forward and adjoint model. Several iterations of optimization may thus be performed within a single run of the main program (main_lsopt). The following cases may occur: - cold start only (no optimization) - cold start & one or several iterations of optimization - warm start from previous cold start with one or several iterations - warm start from previous warm start with one or several iterations In order to achieve minimum difference between the online and offline code xdiff(i+1) is stored to file at the end of an (offline) iteration, but recomputed identically at the beginning of the next iteration. 2. Number of iterations vs. number of simulations ------------------------------------------------- - itmax: controls the max. number of iterations - nfunc: controls the max. number of simulations within one iteration Summary: From one iteration to the next the descent direction changes. Within one iteration more than one forward and adjoint run may be performed. The updated control used as input for these simulations uses the same descent direction, but different step sizes. In detail: From one iteration to the next the descent direction dd changes using the result for the adjoint vector gg of the previous iteration. In lsline the updated control xdiff(i,1) = xx(i-1) + tact(i-1,1)*dd(i-1) serves as input for a forward and adjoint model run yielding a new gg(i,1). In general, the new solution passes the 1st and 2nd Wolfe tests so xdiff(i,1) represents the solution sought: xx(i) = xdiff(i,1). If one of the two tests fails, an inter- or extrapolation is invoked to determine a new step size tact(i-1,2). If more than one function call is permitted, the new step size is used together with the "old" descent direction dd(i-1) (i.e. dd is not updated using the new gg(i)), to compute a new xdiff(i,2) = xx(i-1) + tact(i-1,2)*dd(i-1) that serves as input in a new forward and adjoint run, yielding gg(i,2). If now, both Wolfe tests are successfull, the updated solution is given by xx(i) = xdiff(i,2) = xx(i-1) + tact(i-1,2)*dd(i-1). 3. Double-usage of fields dd and xdiff -------------------------------------- In order to save memory both the fields dd and xdiff have a double usage. - xdiff: in lsopt_top: used as x(i) - x(i-1) for Hessian update in lsline: intermediate result for control update x = x + tact*dd - dd : in lsopt_top, lsline: descent vector, dd = -gg & hessupd in dgscale: intermediate result to compute new preconditioner 4. Notice for user of old code ------------------------------ Three relevant changes needed to switch to new version: (i): OPWARMI file: two variables added: gnorm0 : norm of first (cold start) gradient iabsiter: total number of iterations with respect to cold start (ii): routine names that are referenced by main_lsopt.f lsoptv1 -> lsopt_top lsline1 -> lsline (iii): parameter list of lsopt_top logical loffline included parameter file lsopt.par ######################## The optimization is controlled by a set of parameters provided through the standard input file lsopt.par, which is generated within the job script. NUPDATE : max. no. of update pairs (gg(i)-gg(i-1), xx(i)-xx(i-1)) to be stored in OPWARMD to estimate Hessian [pair of current iter. is stored in (2*jmax+2, 2*jmax+3) jmax must be > 0 to access these entries] Presently NUPDATE must be > 0 (i.e. iteration without reference to previous iterations through OPWARMD has not been tested) EPSX : relative precision on xx bellow which xx should not be improved EPSG : relative precision on gg below which optimization is considered successful IPRINT : controls verbose (>=1) or non-verbose output NUMITER : max. number of iterations of optimisation NUMTER = 0: cold start only, no optimization ITER_NUM : index of new restart file to be created (not necessarily = NUMITER!) NFUNC : max. no. of simulations per iteration (must be > 0) is used if step size tact is inter-/extrapolated; in this case, if NFUNC > 1, a new simulation is performed with same gradient but "improved" step size FMIN : first guess cost function value (only used as long as first iteration not completed, i.e. for jmax <= 0) OPWARMI, OPWARMD files ###################### Two files retain values of previous iterations which are used in latest iteration to update Hessian. OPWARMI: contains index settings and scalar variables OPWARMD: contains vectors Structure of OPWARMI file: ------------------------- n, fc, isize, m, jmin, jmax, gnorm0, iabsiter n = nn : no. of control variables fc = ff : cost value of last iteration isize : no. of bytes per record in OPWARMD m = nupdate : max. no. of updates for Hessian jmin, jmax : pointer indices for OPWARMD file (cf. below) gnorm0 : norm of first (cold start) gradient gg iabsiter : total number of iterations with respect to cold start Structure of OPWARMD file: ------------------------- entry 1 : xx(i) : control vector of latest iteration 2 : gg(i) : gradient of latest iteration 3 : xdiff(i), diag: preconditioning vector; (1,...,1) for cold start --- 2*jmax+2 : gold = g(i) - g(i-1) for last update (jmax) 2*jmax+3 : xdiff = tact * d = xx(i) - xx(i-1) for last update (jmax) if jmax = 0: cold start; no Hessian update used to compute dd if jmax > nupdate, old positions are overwritten, starting with position pair (4,5) Example 1: jmin = 1, jmax = 3, mupd = 5 1 2 3 | 4 5 6 7 8 9 empty empty |___|___|___| | |___|___| |___|___| |___|___| |___|___| |___|___| 0 | 1 2 3 Example 2: jmin = 3, jmax = 7, mupd = 5 ---> jmax = 2 1 2 3 | |___|___|___| | |___|___| |___|___| |___|___| |___|___| |___|___| | 6 7 3 4 5 Error handling ############## ifail | description --------+---------------------------------------------------------- < 0 | should not appear (flag indic in simul.F not used) 0 | normal mode during execution 1 | an input argument is wrong 2 | warm start file is corrupted 3 | the initial gradient is too small 4 | the search direction is not a descent one 5 | maximal number of iterations reached 6 | maximal number of simulations reached (handled passively) 7 | the linesearch failed 8 | the function could not be improved 9 | optline parameters wrong 10 | cold start, no optimization done 11 | convergence achieved within precision