Frozen Solve#

In rare cases, the GAMS model generation time dominates the solver solution time and GAMS itself becomes the bottleneck in an optimization application. For a model instance which is a single mathematical model generated by a GAMS solve statement, frozen solve provides a controlled way of modifying a model instance and solving the resulting problem in the most efficient way, by communicating only the changes of the model to the solver and doing a hot start (in case of a continuous model like LP) without the use of disk IO.

Frozen solves are defined in two steps:

1. The freeze call will enable the frozen solve mode and query the symbol information of the modifiable symbols. The modifiables can be consisted of Parameter and Variable or Equation attributes such as Variable.lo, Variable.up and Variable.fx.

2. The solve method uses this data to update the model instance. After the model instance has been updated, the model is passed to the selected solver. After the completion of the solve method, the container will contain the primal and dual solution of the model just solved. Moreover, the parameters that are modifiable are also accessible in database with the name of the Parameter plus “_var”. The marginal of this Variable can provide sensitivity information about the parameter setting. The status of the solve is accessible through the Model.model_status, Model.solve_status and Model.objective_value attributes.

  1. The freeze function unfreezes the model and releases the resources.

m = Container()

distances = [
    ["seattle", "new-york", 2.5],
    ["seattle", "chicago", 1.7],
    ["seattle", "topeka", 1.8],
    ["san-diego", "new-york", 2.5],
    ["san-diego", "chicago", 1.8],
    ["san-diego", "topeka", 1.4],
]

capacities = [["seattle", 350], ["san-diego", 600]]
demands = [["new-york", 325], ["chicago", 300], ["topeka", 275]]

i = Set(m, name="i", records=["seattle", "san-diego"])
j = Set(m, name="j", records=["new-york", "chicago", "topeka"])

a = Parameter(m, name="a", domain=[i], records=capacities)
b = Parameter(m, name="b", domain=[j], records=demands)
d = Parameter(m, name="d", domain=[i, j], records=distances)
c = Parameter(m, name="c", domain=[i, j])
bmult = Parameter(m, name="bmult", records=1)
c[i, j] = 90 * d[i, j] / 1000

x = Variable(m, name="x", domain=[i, j], type="Positive")
z = Variable(m, name="z")

cost = Equation(m, name="cost")
supply = Equation(m, name="supply", domain=[i])
demand = Equation(m, name="demand", domain=[j])

cost[...] = z == Sum((i, j), c[i, j] * x[i, j])
supply[i] = Sum(j, x[i, j]) <= a[i]
demand[j] = Sum(i, x[i, j]) >= bmult * b[j]

transport = Model(
    m,
    name="transport",
    equations=m.getEquations(),
    problem="LP",
    sense=Sense.MIN,
    objective=z,
)

bmult_list = [0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3]

transport.freeze(modifiables=[bmult])

for b_value in bmult_list:
    bmult.setRecords(b_value)
    transport.solve(solver="conopt")
    print(transport.objective_value)

transport.unfreeze()

The solver used can be switched in between solves, for example the following script uses conopt for even numbers and cplex for odd numbers:

for index, b_value in enumerate(bmult_list):
    bmult.setRecords(b_value)

    if index % 2 == 0:
        transport.solve(solver="conopt")
    else:
        transport.solve(solver="cplex")

Note

Modifiable symbols cannot be used in .where conditions.