19#include "nlua_linopt.h"
25#define LINOPT_MAX_TM 1000
30typedef struct LuaLinOpt_s {
105 luaL_typerror(L, ind, LINOPT_METATABLE);
120 luaL_getmetatable(L, LINOPT_METATABLE);
121 lua_setmetatable(L, -2);
136 if (lua_getmetatable(L,ind)==0)
138 lua_getfield(L, LUA_REGISTRYINDEX, LINOPT_METATABLE);
141 if (lua_rawequal(L, -1, -2))
157 glp_delete_prob(lp->
prob);
174 lua_pushboolean( L, (memcmp( lp1, lp2,
sizeof(
LuaLinOpt_t))==0) );
195 name = luaL_optstring(L,1,NULL);
196 lp.
ncols = luaL_checkinteger(L,2);
197 lp.
nrows = luaL_checkinteger(L,3);
198 max = lua_toboolean(L,4);
202 NLUA_ERROR( L, _(
"Number of columns in a linear optimization problem must be greater than 0!") );
206 lp.
prob = glp_create_prob();
207 glp_set_prob_name( lp.
prob, name );
211 glp_set_obj_dir( lp.
prob, GLP_MAX );
228 lua_pushinteger( L, glp_get_num_cols( lp->
prob ) );
229 lua_pushinteger( L, glp_get_num_rows( lp->
prob ) );
243 int toadd = luaL_checkinteger(L,2);
244 glp_add_cols( lp->
prob, toadd );
259 int toadd = luaL_checkinteger(L,2);
260 glp_add_rows( lp->
prob, toadd );
280 int idx = luaL_checkinteger(L,2);
281 const char *name = luaL_checkstring(L,3);
282 double coef = luaL_checknumber(L,4);
283 const char *skind = luaL_optstring(L,5,
"real");
284 int haslb = !lua_isnoneornil(L,6);
285 int hasub = !lua_isnoneornil(L,7);
286 double lb = luaL_optnumber(L,6,0.0);
287 double ub = luaL_optnumber(L,7,0.0);
288 int type = GLP_FR, kind = GLP_CV;
291 glp_set_col_name( lp->
prob, idx, name );
292 glp_set_obj_coef( lp->
prob, idx, coef );
295 if (haslb && hasub) {
296 if (fabs(lb-ub) < 1e-5)
307 glp_set_col_bnds( lp->
prob, idx, type, lb, ub );
310 if (strcmp(skind,
"real")==0)
312 else if (strcmp(skind,
"integer")==0)
314 else if (strcmp(skind,
"binary")==0)
317 NLUA_ERROR(L,_(
"Unknown column kind '%s'!"), skind);
318 glp_set_col_kind( lp->
prob, idx, kind );
336 int idx = luaL_checkinteger(L,2);
337 const char *name = luaL_checkstring(L,3);
338 int haslb, hasub, type;
342 glp_set_row_name( lp->
prob, idx, name );
345 haslb = !lua_isnoneornil(L,4);
346 hasub = !lua_isnoneornil(L,5);
347 lb = luaL_optnumber(L,4,0.0);
348 ub = luaL_optnumber(L,5,0.0);
349 if (haslb && hasub) {
350 if (fabs(lb-ub) < 1e-5)
361 glp_set_row_bnds( lp->
prob, idx, type, lb, ub );
381 luaL_checktype(L, 2, LUA_TTABLE);
382 luaL_checktype(L, 3, LUA_TTABLE);
383 luaL_checktype(L, 4, LUA_TTABLE);
388 if ((n != lua_objlen(L,3)) || (n != lua_objlen(L,4)))
389 NLUA_ERROR(L, _(
"Table lengths don't match!"));
393 ia = calloc( n+1,
sizeof(
int) );
394 ja = calloc( n+1,
sizeof(
int) );
395 ar = calloc( n+1,
sizeof(
double) );
396 for (
size_t i=1; i<=n; i++) {
397 lua_rawgeti(L, 2, i);
398 lua_rawgeti(L, 3, i);
399 lua_rawgeti(L, 4, i);
401 ia[i] = luaL_checkinteger(L,-3);
402 ja[i] = luaL_checkinteger(L,-2);
403 ar[i] = luaL_checknumber(L,-1);
405 ia[i] = lua_tointeger(L,-3);
406 ja[i] = lua_tointeger(L,-2);
407 ar[i] = lua_tonumber(L,-1);
413 glp_load_matrix( lp->
prob, n, ia, ja, ar );
422static const char* linopt_status(
int retval )
426 return "solution is optimal";
428 return "solution is feasible";
430 return "solution is infeasible";
432 return "problem has no feasible solution";
434 return "problem has unbounded solution";
436 return "solution is undefined";
438 return "unknown GLPK status";
441static const char* linopt_error(
int retval )
449 return "The search was prematurely terminated due to the solver failure.";
451 return "The search was prematurely terminated, because the time limit has been exceeded.";
455 return "Unable to start the search, because the initial basis specified in the problem object is invalid—the number of basic (auxiliary and structural) variables is not the same as the number of rows in the problem object.";
457 return "Unable to start the search, because the basis matrix corresponding to the initial basis is singular within the working precision.";
459 return "Unable to start the search, because the basis matrix corresponding to the initial basis is ill-conditioned, i.e. its condition number is too large.";
461 return "Unable to start the search, because some double-bounded (auxiliary or structural) variables have incorrect bounds.";
463 return "The search was prematurely terminated, because the objective function being maximized has reached its lower limit and continues decreasing (the dual simplex only).";
465 return "The search was prematurely terminated, because the objective function being minimized has reached its upper limit and continues increasing (the dual simplex only).";
467 return "The search was prematurely terminated, because the simplex iteration limit has been exceeded.";
471 return "Unable to start the search, because optimal basis for initial LP relaxation is not provided. (This code may appear only if the presolver is disabled.)";
473 return "Unable to start the search, because LP relaxation of the MIP problem instance has no primal feasible solution. (This code may appear only if the presolver is enabled.)";
475 return "Unable to start the search, because LP relaxation of the MIP problem instance has no dual feasible solution. In other word, this code means that if the LP relaxation has at least one primal feasible solution, its optimal solution is unbounded, so if the MIP problem has at least one integer feasible solution, its (integer) optimal solution is also unbounded. (This code may appear only if the presolver is enabled.)";
477 return "The search was prematurely terminated, because the relative mip gap tolerance has been reached.";
479 return "The search was prematurely terminated by application. (This code may appear only if the advanced solver interface is used.)";
482 return "Unknown error.";
487#define METH_DEF GLP_PRIMAL
488#define PRICING_DEF GLP_PT_PSE
489#define R_TEST_DEF GLP_RT_HAR
490#define PRESOLVE_DEF GLP_OFF
492#define BR_TECH_DEF GLP_BR_DTH
493#define BT_TECH_DEF GLP_BT_BLB
494#define PP_TECH_DEF GLP_PP_ALL
496#define FP_HEUR_DEF GLP_OFF
498#define GMI_CUTS_DEF GLP_OFF
499#define MIR_CUTS_DEF GLP_OFF
500#define COV_CUTS_DEF GLP_OFF
501#define CLQ_CUTS_DEF GLP_OFF
504#define BR_TECH_DEF GLP_BR_PCH
505#define BT_TECH_DEF GLP_BT_DFS
506#define PP_TECH_DEF GLP_PP_ALL
508#define FP_HEUR_DEF GLP_OFF
510#define GMI_CUTS_DEF GLP_ON
511#define MIR_CUTS_DEF GLP_OFF
512#define COV_CUTS_DEF GLP_ON
513#define CLQ_CUTS_DEF GLP_ON
515#define STRCHK( val, ret ) if (strcmp(str,(val))==0) return (ret);
516static int opt_meth(
const char *str,
int def )
518 if (str==NULL)
return def;
519 STRCHK(
"primal", GLP_PRIMAL );
520 STRCHK(
"dual", GLP_DUAL );
521 STRCHK(
"dualp", GLP_DUALP );
522 WARN(
"Unknown meth value '%s'", str);
525static int opt_pricing(
const char *str,
int def )
527 if (str==NULL)
return def;
528 STRCHK(
"std", GLP_PT_STD );
529 STRCHK(
"pse", GLP_PT_PSE );
530 WARN(
"Unknown pricing value '%s'", str);
533static int opt_r_test(
const char *str,
int def )
535 if (str==NULL)
return def;
536 STRCHK(
"std", GLP_RT_STD );
537 STRCHK(
"har", GLP_RT_HAR );
538 WARN(
"Unknown r_test value '%s'", str);
541static int opt_br_tech(
const char *str,
int def )
543 if (str==NULL)
return def;
544 STRCHK(
"ffv", GLP_BR_FFV );
545 STRCHK(
"lfv", GLP_BR_LFV );
546 STRCHK(
"mfv", GLP_BR_MFV );
547 STRCHK(
"dth", GLP_BR_DTH );
548 STRCHK(
"pch", GLP_BR_PCH );
549 WARN(
"Unknown br_tech value '%s'", str);
552static int opt_bt_tech(
const char *str,
int def )
554 if (str==NULL)
return def;
555 STRCHK(
"dfs", GLP_BT_DFS );
556 STRCHK(
"bfs", GLP_BT_BFS );
557 STRCHK(
"blb", GLP_BT_BLB );
558 STRCHK(
"bph", GLP_BT_BPH );
559 WARN(
"Unknown bt_tech value '%s'", str);
562static int opt_pp_tech(
const char *str,
int def )
564 if (str==NULL)
return def;
565 STRCHK(
"none", GLP_PP_NONE );
566 STRCHK(
"root", GLP_PP_ROOT );
567 STRCHK(
"all", GLP_PP_ALL );
568 WARN(
"Unknown pp_tech value '%s'", str);
571static int opt_onoff(
const char *str,
int def )
573 if (str==NULL)
return def;
574 STRCHK(
"on", GLP_ON );
575 STRCHK(
"off", GLP_OFF );
576 WARN(
"Unknown onoff value '%s'", str);
581#define GETOPT_IOCP( name, func, def ) do {lua_getfield(L,2,#name); parm_iocp.name = func( luaL_optstring(L,-1,NULL), def ); lua_pop(L,1); } while (0)
582#define GETOPT_SMCP( name, func, def ) do {lua_getfield(L,2,#name); parm_smcp.name = func( luaL_optstring(L,-1,NULL), def ); lua_pop(L,1); } while (0)
599 Uint32 starttime = SDL_GetTicks();
603 ismip = (glp_get_num_int( lp->
prob ) > 0);
604 glp_init_smcp(&parm_smcp);
605 parm_smcp.msg_lev = GLP_MSG_ERR;
608 glp_init_iocp(&parm_iocp);
609 parm_iocp.msg_lev = GLP_MSG_ERR;
614 if (!lua_isnoneornil(L,2)) {
615 GETOPT_SMCP( meth, opt_meth, METH_DEF );
616 GETOPT_SMCP( pricing, opt_pricing, PRICING_DEF );
617 GETOPT_SMCP( r_test, opt_r_test, R_TEST_DEF );
618 GETOPT_SMCP( presolve,opt_onoff, PRESOLVE_DEF );
620 GETOPT_IOCP( br_tech, opt_br_tech, BR_TECH_DEF );
621 GETOPT_IOCP( bt_tech, opt_bt_tech, BT_TECH_DEF );
622 GETOPT_IOCP( pp_tech, opt_pp_tech, PP_TECH_DEF );
624 GETOPT_IOCP( fp_heur, opt_onoff, FP_HEUR_DEF );
626 GETOPT_IOCP( gmi_cuts, opt_onoff, GMI_CUTS_DEF );
627 GETOPT_IOCP( mir_cuts, opt_onoff, MIR_CUTS_DEF );
628 GETOPT_IOCP( cov_cuts, opt_onoff, COV_CUTS_DEF );
629 GETOPT_IOCP( clq_cuts, opt_onoff, CLQ_CUTS_DEF );
634 parm_smcp.meth = METH_DEF;
635 parm_smcp.pricing = PRICING_DEF;
636 parm_smcp.r_test = R_TEST_DEF;
637 parm_smcp.presolve= PRESOLVE_DEF;
639 parm_iocp.br_tech = BR_TECH_DEF;
640 parm_iocp.bt_tech = BT_TECH_DEF;
641 parm_iocp.pp_tech = PP_TECH_DEF;
642 parm_iocp.sr_heur = SR_HEUR_DEF;
643 parm_iocp.fp_heur = FP_HEUR_DEF;
644 parm_iocp.ps_heur = PS_HEUR_DEF;
645 parm_iocp.gmi_cuts = GMI_CUTS_DEF;
646 parm_iocp.mir_cuts = MIR_CUTS_DEF;
647 parm_iocp.cov_cuts = COV_CUTS_DEF;
648 parm_iocp.clq_cuts = CLQ_CUTS_DEF;
654 if (!ismip || !parm_iocp.presolve) {
655 ret = glp_simplex( lp->
prob, &parm_smcp );
656 if ((ret != 0) && (ret != GLP_ETMLIM)) {
658 lua_pushstring(L, linopt_error(ret));
662 ret = glp_get_status(lp->
prob);
663 if ((ret != GLP_OPT) && (ret != GLP_FEAS)) {
665 lua_pushstring(L, linopt_status(ret));
670 ret = glp_intopt( lp->
prob, &parm_iocp );
671 if ((ret != 0) && (ret != GLP_ETMLIM)) {
673 lua_pushstring(L, linopt_error(ret));
677 ret = glp_mip_status(lp->
prob);
678 if ((ret != GLP_OPT) && (ret != GLP_FEAS)) {
680 lua_pushstring(L, linopt_status(ret));
684 z = glp_get_obj_val( lp->
prob );
691 for (
int i=1; i<=lp->
ncols; i++) {
693 z = glp_mip_col_val( lp->
prob, i );
695 z = glp_get_col_prim( lp->
prob, i );
696 lua_pushnumber( L, z );
697 lua_rawseti( L, -2, i );
702 for (
int i=1; i<=lp->
nrows; i++) {
704 z = glp_mip_row_val( lp->
prob, i );
706 z = glp_get_row_prim( lp->
prob, i );
707 lua_pushnumber( L, z );
708 lua_rawseti( L, -2, i );
714 WARN(_(
"glpk: too over 1 second to optimize!"));
732 const char *fname = luaL_checkstring(L,1);
733 int glpk_format = lua_toboolean(L,2);
734 int maximize = lua_toboolean(L,3);
735 const char *dirname = PHYSFS_getRealDir( fname );
740 NLUA_ERROR( L, _(
"Failed to read LP problem \"%s\"!"), fname );
741 asprintf( &fpath,
"%s/%s", dirname, fname );
742 lp.
prob = glp_create_prob();
743 ret = glpk_format ? glp_read_prob( lp.
prob, 0, fpath ) : glp_read_mps( lp.
prob, GLP_MPS_FILE, NULL, fpath );
746 glp_delete_prob( lp.
prob );
747 NLUA_ERROR( L, _(
"Failed to read LP problem \"%s\"!"), fname );
752 glp_set_obj_dir( lp.
prob, GLP_MAX );
768 const char *fname = luaL_checkstring(L,2);
769 int glpk_format = lua_toboolean(L,3);
770 const char *dirname = PHYSFS_getWriteDir();
773 asprintf( &fpath,
"%s/%s", dirname, fname );
774 ret = glpk_format ? glp_write_prob( lp->
prob, 0, fpath ) : glp_write_mps( lp->
prob, GLP_MPS_FILE, NULL, fpath );
776 lua_pushboolean( L, ret==0 );
Header file with generic functions and naev-specifics.
LuaLinOpt_t * luaL_checklinopt(lua_State *L, int ind)
Gets linopt at index or raises error if there is no linopt at index.
static int linoptL_loadmatrix(lua_State *L)
Loads the entire matrix for the linear program.
static const luaL_Reg linoptL_methods[]
static int linoptL_setcol(lua_State *L)
Adds an optimization column.
static int linoptL_solve(lua_State *L)
Solves the linear optimization problem.
static int linoptL_gc(lua_State *L)
Frees a linopt.
LuaLinOpt_t * lua_pushlinopt(lua_State *L, LuaLinOpt_t linopt)
Pushes a linopt on the stack.
LuaLinOpt_t * lua_tolinopt(lua_State *L, int ind)
Lua bindings to interact with linopts.
static int linoptL_readProblem(lua_State *L)
Reads an optimization problem from a file for debugging purposes.
static int linoptL_size(lua_State *L)
Adds columns to the linear program.
static int linoptL_addrows(lua_State *L)
Adds rows to the linear program.
static int linoptL_new(lua_State *L)
Opens a new linopt.
int lua_islinopt(lua_State *L, int ind)
Checks to see if ind is a linopt.
static int linoptL_addcols(lua_State *L)
Adds columns to the linear program.
static int linoptL_setrow(lua_State *L)
Adds an optimization row.
static int linoptL_eq(lua_State *L)
Compares two linopts to see if they are the same.
int nlua_loadLinOpt(nlua_env env)
Loads the linopt library.
static int linoptL_writeProblem(lua_State *L)
Writes an optimization problem to a file for debugging purposes.
int asprintf(char **strp, const char *fmt,...)
Like sprintf(), but it allocates a large-enough string and returns the pointer in the first argument....
Our cute little linear program wrapper.