Description
The GAMS Embedded Code facility allows to execute foreign code (e.g. Python) while GAMS runs and to exchange data with GAMS without any disk access (e.g. GDX). In this example we combine the power of the embedded code facility with the GAMS Python OO-API class GAMSModelInstance. An instance of this class provides access to a model instance that can be modified and resolved without regenerating the model over and over. Here we generate the model instance once by using the libinclude pyEmbMI. The arguments to this call provide all necessary information to instantiate an instance of a GAMSModelInstance. In particular we provide the relevant part of the solve statement as well as a list of modifiers. These are the parameters in the model that are subject to change. In addition we can provide some options belonging to a GAMS/Python OO-API class GAMSOptions via the -key=value pairs. See more about the use of GAMSModelInstance and GAMSOptions in the GAMS/Python OO-API at https://www.gams.com/latest/docs/apis/python/annotated.html A traditional GAMS implemenation of such a scenario loop looks like this: loop(ScenariosToRun, a(i) = newsupply(ScenariosToRun,i); b(j) = newdemand(ScenariosToRun,j); solve transport using lp minimizing z; resultantx(ScenariosToRun,i,j) = x.l(i,j) ); With the embedded code/GAMSModelInstance solution this loop looks as follows: loop(ScenariosToRun, a(i) = newsupply(ScenariosToRun,i); b(j) = newdemand(ScenariosToRun,j); continueEmbeddedCode: gams.db['a'].copy_symbol(tMI.sync_db['a']) gams.db['b'].copy_symbol(tMI.sync_db['b']) tMI.solve() tMI.sync_db['x'].copy_symbol(gams.db['x']) pauseEmbeddedCode x resultantx(ScenariosToRun,i,j) = x.l(i,j); ); With a little helper function this code becomes even more similar: loop(ScenariosToRun, a(i) = newsupply(ScenariosToRun,i); b(j) = newdemand(ScenariosToRun,j); continueEmbeddedCode: solveMI(tMI,['a','b'],['x']) pauseEmbeddedCode x resultantx(ScenariosToRun,i,j) = x.l(i,j); ); In contrast to GUSS/Scenario Solver here we implement the loop logic in GAMS and execute in the loop body the solve method of the GAMSModelInstance class inside the embedded Python code. Rather than using the gams.get|set method of the embedded code facility we use GAMSDatabase.copy_symbol to move data between GAMS (gams.db) and the GAMSModelInstance.sync_db. Even though we don't exercise the ability in this example, the combination of GAMSModelInstance and embedded code provides a way of defining the scenario n+1 based on the result (primal and dual) of the nth scenario. This is not possible in GUSS/Scenario Solver. Keywords: linear programming, GAMS embedded code facility, Python, transportation problem, scheduling
Small Model of Type : LP
Category : GAMS Model library
Main file : embmiex1.gms
$title Simple Embedded Code ModelInstance Example (EMBMIEX1,SEQ=417)
$onText
The GAMS Embedded Code facility allows to execute foreign code (e.g. Python)
while GAMS runs and to exchange data with GAMS without any disk access (e.g. GDX).
In this example we combine the power of the embedded code facility with the
GAMS Python OO-API class GAMSModelInstance. An instance of this class provides
access to a model instance that can be modified and resolved without regenerating
the model over and over.
Here we generate the model instance once by using the libinclude pyEmbMI. The
arguments to this call provide all necessary information to instantiate an instance
of a GAMSModelInstance. In particular we provide the relevant part of the solve
statement as well as a list of modifiers. These are the parameters in the model
that are subject to change. In addition we can provide some options belonging to
a GAMS/Python OO-API class GAMSOptions via the -key=value pairs. See more about
the use of GAMSModelInstance and GAMSOptions in the GAMS/Python OO-API at
https://www.gams.com/latest/docs/apis/python/annotated.html
A traditional GAMS implemenation of such a scenario loop looks like this:
loop(ScenariosToRun,
a(i) = newsupply(ScenariosToRun,i);
b(j) = newdemand(ScenariosToRun,j);
solve transport using lp minimizing z;
resultantx(ScenariosToRun,i,j) = x.l(i,j)
);
With the embedded code/GAMSModelInstance solution this loop looks as follows:
$libInclude pyEmbMI tMI 'transport us lp min z' -all_model_types=cplex a.Zero b.Zero
loop(ScenariosToRun,
a(i) = newsupply(ScenariosToRun,i);
b(j) = newdemand(ScenariosToRun,j);
continueEmbeddedCode:
gams.db['a'].copy_symbol(tMI.sync_db['a'])
gams.db['b'].copy_symbol(tMI.sync_db['b'])
tMI.solve()
tMI.sync_db['x'].copy_symbol(gams.db['x'])
pauseEmbeddedCode x
resultantx(ScenariosToRun,i,j) = x.l(i,j);
);
With a little helper function this code becomes even more similar:
$libInclude pyEmbMI tMI 'transport us lp min z' -all_model_types=cplex a.Zero b.Zero
loop(ScenariosToRun,
a(i) = newsupply(ScenariosToRun,i);
b(j) = newdemand(ScenariosToRun,j);
continueEmbeddedCode:
solveMI(tMI,['a','b'],['x'])
pauseEmbeddedCode x
resultantx(ScenariosToRun,i,j) = x.l(i,j);
);
In contrast to GUSS/Scenario Solver here we implement the loop logic in GAMS
and execute in the loop body the solve method of the GAMSModelInstance class
inside the embedded Python code. Rather than using the gams.get|set method of
the embedded code facility we use GAMSDatabase.copy_symbol to move data between
GAMS (gams.db) and the GAMSModelInstance.sync_db.
Even though we don't exercise the ability in this example, the combination of
GAMSModelInstance and embedded code provides a way of defining the scenario n+1
based on the result (primal and dual) of the nth scenario. This is not possible
in GUSS/Scenario Solver.
Keywords: linear programming, GAMS embedded code facility, Python,
transportation problem, scheduling
$offText
$log --- Using Python library %sysEnv.GMSPYTHONLIB%
Set
i 'canning plants' / seattle, san-diego /
j 'markets' / new-york, chicago, topeka /;
Parameter
a(i) 'capacity of plant i in cases'
/ seattle 350
san-diego 600 /
b(j) 'demand at market j in cases'
/ new-york 325
chicago 300
topeka 275 /;
Table d(i,j) 'distance in thousands of miles'
new-york chicago topeka
seattle 2.5 1.7 1.8
san-diego 2.5 1.8 1.4;
Scalar f 'freight in dollars per case per thousand miles' / 90 /;
Parameter c(i,j) 'transport cost in thousands of dollars per case';
c(i,j) = f*d(i,j)/1000;
Variable
x(i,j) 'shipment quantities in cases'
z 'total transportation costs in thousands of dollars';
Positive Variable x;
Equation
cost 'define objective function'
supply(i) 'observe supply limit at plant i'
demand(j) 'satisfy demand at market j';
cost.. z =e= sum((i,j), c(i,j)*x(i,j));
supply(i).. sum(j, x(i,j)) =l= a(i);
demand(j).. sum(i, x(i,j)) =g= b(j);
Model transport / all /;
Set s 'scenarios to run' / base, run1, run2 /;
Table newsupply(s,i) 'updater for a (capacity)'
seattle san-diego
base 350 600
run1 300 650
run2 400 550;
Table newdemand(s,j) 'updater for b (demand)'
new-york chicago topeka
base 325 300 275
run1 325 300 275
run2 350 300 250;
$set solverlog
$if set useSolverLog $set solverlog output=sys.stdout
embeddedCode Python:
gams.wsWorkingDir = '.'
def solveMI(mi, symIn=[], symOut=[]):
for sym in symIn:
gams.db[sym].copy_symbol(mi.sync_db[sym])
mi.solve(%solverlog%)
for sym in symOut:
try:
gams.db[sym].clear() # Explicitly clear the symbol to ensure setting "writtenTo" flag for sym
mi.sync_db[sym].copy_symbol(gams.db[sym])
except:
pass
pauseEmbeddedCode
abort$execerror 'Python error. Check the log';
$set license
$if not "%gams.license%" $set license '"-license=%gams.license%"'
$libInclude pyEmbMI tMI 'transport us lp min z' -all_model_types=cplex %license% a.Zero b.Zero
Parameter repX(s,i,j) 'collector for level of x';
loop(s,
a(i) = newsupply(s,i);
b(j) = newdemand(s,j);
continueEmbeddedCode:
solveMI(tMI,['a','b'],['x'])
pauseEmbeddedCode x
repX(s,i,j) = x.l(i,j);
);
continueEmbeddedCode:
tMI.cleanup()
endEmbeddedCode
option repX:0:1:2;
display repX;
Set error(s) 'empty solution';
error(s) = sum((i,j), repX(s,i,j)) = 0;
abort$card(error) 'Missing solution for some scenarios', error;