# Frozen Solve#

In rare cases, the GAMSPy model generation time dominates the solver solution time and GAMSPy itself becomes the bottleneck in an optimization application.
For a model instance which is a single mathematical model generated by a GAMSPy solve statement, *frozen solve* provides a controlled way of
modifying a model instance and solving the resulting problem repeatedly 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.

The

`unfreeze`

function unfreezes the model and releases the resources.

```
from gamspy import (
Container,
Set,
Parameter,
Variable,
Equation,
Sum,
Model,
Sense,
ModelStatus,
)
import numpy as np
m = Container()
i = Set(m, name="i")
j = Set(m, name="j")
a = Parameter(
m,
name="a",
domain=i,
domain_forwarding=True,
records=[["seattle", 350], ["san-diego", 600]],
)
b = Parameter(
m,
name="b",
domain=j,
domain_forwarding=True,
records=[["new-york", 325], ["chicago", 300], ["topeka", 275]],
)
d = Parameter(
m, name="d", domain=[i, j], records=np.array([[2.5, 1.7, 1.8], [2.5, 1.8, 1.4]])
)
c = Parameter(m, name="c", domain=[i, j])
c[i, j] = 90 * d[i, j] / 1000
x = Variable(m, name="x", domain=[i, j], type="Positive")
z = Variable(m, name="z")
supply = Equation(m, name="supply", domain=i)
demand = Equation(m, name="demand", domain=j)
bmult = Parameter(m, name="bmult", records=1)
cost = 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=[supply, demand],
problem="LP",
sense=Sense.MIN,
objective=cost,
)
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(
f'obj:{transport.objective_value if transport.status == ModelStatus.OptimalGlobal else "infeasible"}'
)
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 parameters cannot be used in `.where`

conditions. Variable and equation attributes used in equation
algebra are evaluated once at model generation. Changes in the attibutes will not percolate to the algebra.
For example, the algebra `x <= b * x.up`

will not change even if the modifiables include `x.up`

. One needs
a parameter `bigM`

and algebra `x <= b * bigM`

in order to modify this algebra in a frozen solve.