File input/output of the model

You can load an LP or QPLIB file and create Model from it, and 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.

  • Variables in the Amplify SDK with the following names are not output correctly.

    • Variable names containing the symbols +, -, *, ^, :, /, [, ].

    • Variable names beginning with the symbols <, >, =, ,, (, ) in addition to the above

  • Constraint weights in the Amplify SDK are not described in the LP file.

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.

  • Variables and constraints in the Amplify SDK whose names contain newline characters (\n, \v, \f, \r) are not output correctly.

  • Constraint weights in the Amplify SDK are not output to the QPLIB file.

  • The descriptions of the default values of variables are ignored for loading QPLIB files.

Output to LP/QPLIB file

To output a model created with the Amplify SDK to a file, pass Model as the first argument and the file path as the second argument to save_lp() or save_qplib() for LP and QPLIB files, respectively.

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
1 2 4
3 4 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 the path to an LP/QPLIB files is given as an argument to load_lp() or load_qplib(), you will get a tuple of (Model, VariableGenerator) is returned. 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, FixstarsClient

client = FixstarsClient()
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, the variables contained in the model are converted into a list of Poly, and then create polynomials from the variables contained in the model 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 the 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 have just changed to a file and 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