#----------------------------------------------------------------------- # # Minimise f # # subject to -0.5 <= x1 <= INFTY # -2 <= x2 <= INFTY # 0 <= x3 <= 2 # -2 <= x4 <= 2 # g1 == 1 # -INFTY <= g2 <= -1 # 2.5 <= g3 <= 5 # # where f (x1,x2,x3,x4) = x1^2 + 2 x2^2 - x3 # g1(x1,x2,x3,x4) = x1^2 + x3^2 + x1x3 # g2(x1,x2,x3,x4) = x3 - x4 # g3(x1,x2,x3,x4) = x2 + x4 # # Optimal solution # x* = (0, 0.5, 1, 2) # f(x*) = -0.5 # g(x*) = (1, -1, 2.5) # #----------------------------------------------------------------------- import worhp # Objective function def user_f(opt, wsp, par, cnt): opt.f = wsp.scale_obj * (opt.x[0] ** 2 + 2.0 * opt.x[1] ** 2 - opt.x[2]) # Function of constraints def user_g(opt, wsp, par, cnt): opt.g[0] = opt.x[0] ** 2 + opt.x[2] ** 2 + opt.x[0] * opt.x[2] opt.g[1] = opt.x[2] - opt.x[3] opt.g[2] = opt.x[1] + opt.x[3] # Gradient of objective function def user_df(opt, wsp, par, cnt): wsp.df.val[0] = wsp.scale_obj * 2.0 * opt.x[0] wsp.df.val[1] = wsp.scale_obj * 4.0 * opt.x[1] wsp.df.val[2] = wsp.scale_obj * -1.0 # Jacobian of constraints def user_dg(opt, wsp, par, cnt): wsp.dg.val[0] = 2.0 * opt.x[0] + opt.x[2] wsp.dg.val[1] = 1.0 wsp.dg.val[2] = opt.x[0] + 2.0 * opt.x[2] wsp.dg.val[3] = 1.0 wsp.dg.val[4] = -1.0 wsp.dg.val[5] = 1.0 # Hessian of Lagrangian def user_hm(opt, wsp, par, cnt): # Only scale the F part of HM wsp.hm.val[0] = opt.µ[0] wsp.hm.val[1] = wsp.scale_obj * 2.0 + 2.0 * opt.µ[0] wsp.hm.val[2] = wsp.scale_obj * 4.0 wsp.hm.val[3] = 2.0 * opt.µ[0] wsp.hm.val[4] = 0.0 # Check version of library and Python module if worhp.check_version(worhp.MAJOR, worhp.MINOR, worhp.PATCH): exit(1) # # WORHP data structures # # worhp.OptVar contains the variables, constraint values and multipliers. # worhp.Workspace encapsulates all workspace and working variables for WORHP. # worhp.Params contains all WORHP parameters. # worhp.Control contains things for reverse communication flow control. # opt = worhp.OptVar() wsp = worhp.Workspace() par = worhp.Params() cnt = worhp.Control() # Properly zeros everything, or else the following routines could get confused. worhp.pre_init(opt, wsp, par, cnt) # Uncomment this to get more info on data structures # worhp.diag(opt, wsp, par, cnt) # Parameter initialisation routine that must be called when using # read_params_no_init instead of read_params. worhp.init_params(par) # We can now set parameters that may be overruled by those in the parameter file. # This is useful for setting a non-default standard parameter value that may still # be overwritten. par.NLPprint = 1 # Let's prefer the slim output format unless the parameter file # says differently # Parameter XML import routine that does not reset all parameters to # default values (init_params does this) status = worhp.read_params_no_init("worhp.xml", par) if status == worhp.DATA_ERROR or status == worhp.INIT_ERROR: exit(1) # # WORHP data structure initialisation routine. # Calling this routine prior to WORHP is mandatory. # Before calling worhp.init, set the problem and matrix dimensions as # # opt.n = number of variables, # opt.m = number of constraints (lin + nonlin, excluding box con's), # wsp.df.nnz = nonzero entries of the objective function gradient, # wsp.dg.nnz = nonzero entries of the constraint Jacobian, # wsp.hm.nnz = nonzero entries of the Lagrange Hessian. # # Set nnz to 'worhp.MATRIX_INIT_DENSE' to have worhp.init allocate and # create a dense matrix structure appropriate for the matrix kind and # its dimensions. Setting it to its dense dimension achieves the same. # opt.n = 4 # This problem has 4 variables opt.m = 3 # and 3 constraints (excluding box constraints) # All derivatives for this problem have a sparse structure, so # set the amount of nonzeros here wsp.df.nnz = 3 wsp.dg.nnz = 6 wsp.hm.nnz = 1 + opt.n # 1 entry on strict lower triangle # plus full diagonal worhp.init(opt, wsp, par, cnt) if cnt.status != worhp.FIRST_CALL: print("Main: Initialisation failed.") exit(1) # # These pointers give access to the essential user data: # # opt.x[0:opt.n - 1] : Optimisation variables # opt.λ[0:opt.n - 1] : multipliers for the constraints # on x ("box constraints") # opt.g[0:opt.m - 1] : Linear and nonlinear constraints # opt.µ[0:opt.m - 1] : multipliers for the constraints on G # # Set initial values of x, λ and µ here. # G need not be initialised. # opt.x[0] = 2.0 opt.x[1] = 2.0 opt.x[2] = 1.0 opt.x[3] = 0.0 opt.λ[0] = 0.0 # alternatively you can use opt.Lambda opt.λ[1] = 0.0 opt.λ[2] = 0.0 opt.λ[3] = 0.0 opt.µ[0] = 0.0 # alternatively you can use opt.Mu opt.µ[1] = 0.0 opt.µ[2] = 0.0 # # Set lower and upper bounds on the variables and constraints. # Use +/-par.infty to signal "unbounded". # # xl and xu are lower and upper bounds ("box constraints") on x. # gl and gu are lower and upper bounds on g. # opt.xl[0] = -0.5 opt.xu[0] = par.infty opt.xl[1] = -2.0 opt.xu[1] = par.infty opt.xl[2] = 0.0 opt.xu[2] = 2.0 opt.xl[3] = -2.0 opt.xu[3] = 2.0 opt.gl[0] = 1.0 # set opt.gl[i] == opt.gu[i] opt.gu[0] = 1.0 # for equality constraints opt.gl[1] = -par.infty opt.gu[1] = -1.0 opt.gl[2] = 2.5 opt.gu[2] = 5.0 # # Specify matrix structures in CS format, using Fortran indexing, # i.e. 1...N instead of 0...N-1, to describe the matrix structure. # # # Define df as row vector, column index is ommited # if wsp.df.need_structure: # only set the nonzero entries, so omit the 4th entry, # which is a structural zero wsp.df.row[0] = 1 wsp.df.row[1] = 2 wsp.df.row[2] = 3 # # Define dg as CS-matrix # if wsp.dg.need_structure: # only set the nonzero entries in column-major order */ wsp.dg.row[0] = 1 wsp.dg.col[0] = 1 wsp.dg.row[1] = 3 wsp.dg.col[1] = 2 wsp.dg.row[2] = 1 wsp.dg.col[2] = 3 wsp.dg.row[3] = 2 wsp.dg.col[3] = 3 wsp.dg.row[4] = 2 wsp.dg.col[4] = 4 wsp.dg.row[5] = 3 wsp.dg.col[5] = 4 # # Define hm as a diagonal LT-CS-matrix, but only if needed by WORHP # if wsp.hm.need_structure: # only set the nonzero entries, with the strictly lower triangle first, # followed by ALL diagonal entries, so even the entry at (4, 4) # even though it is a structural zero # strict lower triangle wsp.hm.row[0] = 3 wsp.hm.col[0] = 1 # diagonal for i in range(0, opt.n): wsp.hm.row[wsp.hm.nnz - opt.n + i] = i + 1 wsp.hm.col[wsp.hm.nnz - opt.n + i] = i + 1 # # WORHP Reverse Communication loop. # In every iteration poll get_user_action for the requested action, i.e. one # of {CALL_WORHP, ITER_OUTPUT, EVAL_F, EVAL_G, EVAL_DF, EVAL_DG, EVAL_HM, FIDIF}. # # Make sure to reset the requested user action afterwards by calling # done_user_action, except for 'CALL_WORHP' and 'FIDIF'. # while cnt.status < worhp.TERMINATE_SUCCESS and cnt.status > worhp.TERMINATE_ERROR: # # WORHP's main routine. # Do not manually reset CALL_WORHP, this is only done by the FD routines. # if worhp.get_user_action(cnt, worhp.Action.CALL_WORHP): worhp.worhp(opt, wsp, par, cnt) # No done_user_action! # # Show iteration output. # The call to iteration_output may be replaced by user-defined code. # if worhp.get_user_action(cnt, worhp.Action.ITER_OUTPUT): worhp.iteration_output(opt, wsp, par, cnt) worhp.done_user_action(cnt, worhp.Action.ITER_OUTPUT) # # Evaluate the objective function. # The call to user_f may be replaced by user-defined code. # if worhp.get_user_action(cnt, worhp.Action.EVAL_F): user_f(opt, wsp, par, cnt) worhp.done_user_action(cnt, worhp.Action.EVAL_F) # # Evaluate the constraints. # The call to user_g may be replaced by user-defined code. # if worhp.get_user_action(cnt, worhp.Action.EVAL_G): user_g(opt, wsp, par, cnt) worhp.done_user_action(cnt, worhp.Action.EVAL_G) # # Evaluate the gradient of the objective function. # The call to user_df may be replaced by user-defined code. # if worhp.get_user_action(cnt, worhp.Action.EVAL_DF): user_df(opt, wsp, par, cnt) worhp.done_user_action(cnt, worhp.Action.EVAL_DF) # # Evaluate the Jacobian of the constraints. # The call to user_dg may be replaced by user-defined code. # if worhp.get_user_action(cnt, worhp.Action.EVAL_DG): user_dg(opt, wsp, par, cnt) worhp.done_user_action(cnt, worhp.Action.EVAL_DG) # # Evaluate the Hessian matrix of the Lagrange function (L = f + µ*g) # The call to user_hm may be replaced by user-defined code. # if worhp.get_user_action(cnt, worhp.Action.EVAL_HM): user_hm(opt, wsp, par, cnt) worhp.done_user_action(cnt, worhp.Action.EVAL_HM) # # Use finite differences with RC to determine derivatives # Do not reset fidif, this is done by the FD routine. # if worhp.get_user_action(cnt, worhp.Action.FIDIF): worhp.fidif(opt, wsp, par, cnt) # No done_user_action! # # Translate the WORHP status flag into a meaningful message. # worhp.status_msg(opt, wsp, par, cnt) # # Deallocate all data structures. # Data structures must not be accessed after this call. # worhp.free(opt, wsp, par, cnt)