File input/output of the model¶
You can load an LP or QPLIB file and create Model from it. Vice versa, you can save Model and output to an LP or QPLIB file.
This is useful for interfacing with other mathematical optimization solvers and for saving and reusing Model.
LP file format¶
The format of LP files that the Amplify SDK can handle follows the Gurobi LP Format. The Amplify SDK supports the input and output of the following models.
Problems involving binary, integer, and real variables, as well as problems combining these variables
Problems consisting only of quadratic or less objective functions and constraints
Problems with objective functions and constraints expressible in LP format
Restrictions¶
Note the following restrictions about the input and output of LP files.
The following formats and sections are not supported for loading LP files.
Multi-Objective Case
Indicator Constraints
Lazy Constraints Section
User Cuts Section
SOS Section
PWLObj Section
General Constraints Section
Scenario Section
Semi-continuous variables are not supported for loading LP files.
The Amplify SDK raises an error and does not output variables or constraints whose names:
Contain whitespace, or control characters such as newlines or tabs
Contain the symbols
+, -, *, ^, :, /, \, [, ]Begin with the symbols
<, >, =, ,, (, )or a number (e.g.,1x)Begin with some of the reserved words listed below
s.t., st., stMatches the reserved words listed below
free, min, minimum, minimize, max, maximum, maximize, bin, binary, binaries, gen, general, generals, semi, semis, bounds, end256 characters or more
Constraint weights in the Amplify SDK are not described in the LP file.
When saving a model whose objective function is
Matrix, it is saved in the same format asPoly.Note
When loading a model from an LP file, the objective function will always be
Poly.
QPLIB file format¶
The format of QPLIB files that the Amplify SDK can handle follows the QPLIB: a library of quadratic programming instances and its Supplementary material 1. The Amplify SDK supports the input and output of the following models.
Problems involving binary, integer, and real variables, as well as problems combining these variables
Problems consisting only of quadratic or less objective functions and constraints
Problems with objective functions and constraints expressible in QPLIB format
Restrictions¶
Note the following restrictions about the input and output of QPLIB files.
The Amplify SDK raises an error and does not output variables or constraints whose names:
contain whitespace or control characters such as newlines or tabs
start with the symbols
!, %, #
Constraint weights in the Amplify SDK are not output to the QPLIB file.
QPLIB writes a binary variable as an integer variable with the bounds 0 and 1. An integer variable with these bounds therefore becomes a binary variable when you save a model and load it back. The set of the values that the variable takes does not change.
The descriptions of the default values of variables are ignored for loading QPLIB files.
When saving a model whose objective function is
Matrix, it is saved in the same format asPoly.Note
When loading a model from a QPLIB file, the objective function will always be
Poly.
Output to LP/QPLIB file¶
To output a model created with the Amplify SDK to a file, use save_lp() for LP files or save_qplib() for QPLIB files. Pass Model as the first argument and the file path as the second argument.
from amplify import VariableGenerator, Model, one_hot, save_lp
gen = VariableGenerator()
q = gen.array("Binary", 4)
f = 2 * q[0] * q[1] + q[2] * q[3] + q[0] + q[1] + q[2] + q[3] - 1
c = one_hot(q[:3]) # q_0 + q_1 + q_2 == 1
model = Model(f, c)
save_lp(model, "model.lp")
Then, model.lp is saved in the current directory.
model.lp
Minimize
1 q_0 + 1 q_1 + 1 q_2 + 1 q_3 + [ 4 q_0 * q_1 + 2 q_2 * q_3 ] / 2 - 1
Subject To
1 q_0 + 1 q_1 + 1 q_2 = 1
Bounds
q_0 free
q_1 free
q_2 free
q_3 free
Binaries
q_0 q_1 q_2 q_3
Generals
End
To save to a QPLIB file:
from amplify import save_qplib
save_qplib(model, "model.qplib")
Then, model.qplib is saved in the current directory.
model.qplib
! -------------------
! problem information
! -------------------
# problem name
Amplify
# problem type
QBL
# problem sense
minimize
# number of variables
4
# number of constraints
1
! ------------------
! objective function
! ------------------
# quadratic terms
2
2 1 4
4 3 2
# linear terms
0
4
1 1
2 1
3 1
4 1
# constant
-1
! -----------
! constraints
! -----------
# linear terms
3
1 1 1
1 2 1
1 3 1
# infinity value
1.0e+20
# lower bounds
-2.0e+20
1
1 1
# upper bounds
2.0e+20
1
1 1
! ---------------
! starting points
! ---------------
# starting point for variables
0
0
# starting point for Lagrange multipliers
0
0
# starting point for dual variables
0
0
! -----------------------------
! variable and constraint names
! -----------------------------
# variable names
4
1 q_0
2 q_1
3 q_2
4 q_3
# constraint names
0
Note
LP and QPLIB files identify variables by their names. Therefore, when formulating in the Amplify SDK, make sure that the names of variables in the model are not duplicated.
Read from LP/QPLIB file¶
When you give the path to an LP/QPLIB file as an argument to load_lp() or load_qplib(), you get a tuple. The tuple is (Model, VariableGenerator).
They respectively represent the model described in the input file and the variable generator used to make the model.
For example, the model.lp created above is loaded and solved as follows.
from amplify import load_lp
model, gen = load_lp("model.lp")
>>> print(model)
minimize:
2 q_0 q_1 + q_2 q_3 + q_0 + q_1 + q_2 + q_3 - 1
subject to:
q_0 + q_1 + q_2 == 1 (weight: 1)
The decision variables defined in the file can be found as follows.
>>> model.variables
[Variable({name: q_0, id: 0, type: Binary}), Variable({name: q_1, id: 1, type: Binary}),
Variable({name: q_2, id: 2, type: Binary}), Variable({name: q_3, id: 3, type: Binary})]
The loaded model can be solved by passing it to solve().
from amplify import solve, AmplifyAEClient
client = AmplifyAEClient()
client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
result = solve(model, client)
The mapping between the names and the values of the variables of the best solution is obtained as a dictionary as follows.
>>> {k.name: v for k,v in result.best.values.items()}
{'q_0': 0.0, 'q_1': 1.0, 'q_2': 0.0, 'q_3': 0.0}
Change the model after loading¶
You can change the objective and constraints of the loaded model from a file.
First, convert the variables contained in the model into a list of Poly as follows.
from amplify import Poly
# Load model from file
model, gen = load_lp("model.lp")
# Convert variables in the model to a list of Poly
vars = [Poly(v) for v in model.variables]
# Change the model's objective function and add constraints
model += vars[1] * vars[2]
model += one_hot(sum(vars[1:]))
You can also add new variables to the model using the variable generators obtained from load_lp() or load_qplib().
New variable names created here should not duplicate those of variables in the existing model.
# Create a new integer variable `n` with the variable generator.
add_vars = gen.array("Integer", 2, bounds=(1, 4), name="n")
# Change the model's objective function
model += 2 * add_vars[0] - add_vars[1]
>>> print(model)
minimize:
2 q_0 q_1 + q_1 q_2 + q_2 q_3 + q_0 + q_1 + q_2 + q_3 + 2 n_0 - n_1 - 1
subject to:
q_0 + q_1 + q_2 == 1 (weight: 1),
q_1 + q_2 + q_3 == 1 (weight: 1)
Set constraint weights¶
Constraint weights are not described in the LP and QPLIB files. Therefore, you must set the weights for constraints after loading the model if you need. (See Constructing Constraints/Setting constraint weights)
Here, we output the model we changed to a file, then re-load and use it as an example.
# Output model to file
save_lp(model, "model.lp")
# Re-Load model from file
model, gen = load_lp("model.lp")
# Get the ConstraintList in the model.
constraints = model.constraints
>>> print(constraints)
[q_0 + q_1 + q_2 == 1 (weight: 1),
q_1 + q_2 + q_3 == 1 (weight: 1)]
>>> constraints *= 2
>>> print(constraints)
[q_0 + q_1 + q_2 == 1 (weight: 2),
q_1 + q_2 + q_3 == 1 (weight: 2)]
>>> constraints[0].weight = 5
>>> print(constraints)
[q_0 + q_1 + q_2 == 1 (weight: 5),
q_1 + q_2 + q_3 == 1 (weight: 2)]
Specify the algorithm for generating the penalty function¶
You can set PenaltyFormulation as the second argument of load_lp() and load_qplib().
This parameter represents the algorithm used to generate the penalty for inequality constraints. The default value is Default. (See Constraints and Penalty Functions/Inequality constraints)
from amplify import VariableGenerator, Model, save_lp, load_lp, less_equal
gen = VariableGenerator()
q = gen.array("Binary", 3)
c = less_equal(q[0] + q[1] + q[2], 2)
model = Model(c)
save_lp(model, "model.lp")
>>> model, gen = load_lp("model.lp", "Default")
>>> print(model.constraints[0].penalty)
2 q_0 q_1 + 2 q_0 q_2 - 2 q_0 n_0 + 2 q_1 q_2 - 2 q_1 n_0 - 2 q_2 n_0 + n_0^2 + q_0 + q_1 + q_2
>>> model, gen = load_lp("model.lp", "LinearRelaxation")
>>> print(model.constraints[0].penalty)
0.5 q_0 + 0.5 q_1 + 0.5 q_2