5.6.4. MIQCP Modeling and Optimization in Python

In this section, we will utilize MindOpt Python API to model and solve the quadratically constrained optimization problem in Example of Mixed Integer Quadratically Constrained Programming.

Import MindOpt Python package:

26from mindoptpy import *

Create an optimization model model with name “MIQCP_01”:

31    # Step 1. Create model.
32    model = Model("MIQCP_01")

Next, we call Model.addVar() to add four variables and define their lower bounds, upper bounds, names and types (please refer to Python API for more details) :

37        # Add variables.
38        x = []
39        x.append(model.addVar(0.0,         10.0, 0.0, 'I', "x0")) 
40        x.append(model.addVar(0.0, float('inf'), 0.0, 'C', "x1"))
41        x.append(model.addVar(0.0, float('inf'), 0.0, 'C', "x2"))
42        x.append(model.addVar(0.0, float('inf'), 0.0, 'C', "x3"))

Then, we set the objective function. We first create a quadratic expression using the class QuadExpr. There are two ways to construct it: The first way is to use the method QuadExpr.addTerms() in QuadExpr to input the linear part and quadratic part separately. The second way is to directly input a quadratic expression.

Subsequently, we use Model.setObjective() to set the objective function and set the problem to minimization.

50        # Add objective: 1 x0 + 1 x1 + 1 x2 + 1 x3 + 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1]
51        obj = QuadExpr()
52
53        #option-I
54        obj.addTerms([1.0, 1.0, 1.0, 1.0], [x[0], x[1], x[2], x[3]])
55        obj.addTerms([0.5, 0.5, 0.5, 0.5, 0.5], [x[0], x[1], x[2], x[3], x[0]], [x[0], x[1], x[2], x[3], x[1]])
56        
57        #option II
58        # obj = 1*x[0] + 1*x[1] + 1*x[2] + 1*x[3] + 0.5 * x[0]*x[0] + 0.5 * x[1]*x[1] + 0.5 * x[2]*x[2] + 0.5 * x[3]*x[3] + 0.5*x[0]*x[1]
59        
60        # Set objective and change to minimization problem.
61        model.setObjective(obj, MDO.MINIMIZE)

Next, we add qudratic constraints to the model. The way to construct the quadratic expression is the same as in the objective.

44        # Add constraints.
45        # Note that the nonzero elements are inputted in a row-wise order here.
46        model.addConstr(1.0 * x[0] + 1.0 * x[1] + 2.0 * x[2] + 3.0 * x[3] - \
47                        0.5 * x[0] * x[0] - 0.5 * x[1] * x[1] - 0.5 * x[2] * x[2] - 0.5 * x[3] * x[3] - 0.5 * x[0] * x[1] >= 1, "c0")
48        model.addConstr(1.0 * x[0] - 1.0 * x[2] + 6.0 * x[3] + 0.5 * x[1] * x[1] <= 1, "c1")

Once the model is constructed, we call Model.optimize() to solve the problem:

64        model.optimize()

We can check the solution status via the attribute Status, and retrieve the optimal objective value and solutions via attributes ObjVal and X. For other attribute information, please refer to Attributes.

66        if model.status == MDO.OPTIMAL:
67            print(f"Optimal objective value is: {model.objval}")
68            print("Decision variables:")
69            for v in x:
70                print(f"x[{v.VarName}] = {v.X}")
71        else:
72            print("No feasible solution.")

Finally, we free the model by calling Model.dispose().

82        model.dispose()

The complete python code is shown in mdo_miqcp_ex1.py:

 1"""
 2/**
 3 *  Description
 4 *  -----------
 5 *
 6 *  Mixed Integer Quadratically constrained quadratic optimization (row-wise input).
 7 *
 8 *  Formulation
 9 *  -----------
10 *  Minimize
11 *    obj: 1 x0 + 1 x1 + 1 x2 + 1 x3
12 *         + 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1]
13 *  Subject To
14 *   c0 : 1 x0 + 1 x1 + 2 x2 + 3 x3 - 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] >= 1
15 *   c1 : 1 x0 - 1 x2 + 6 x3 + 1/2 [x1^2] <= 1
16 *  Bounds
17 *    0 <= x0 <= 10
18 *    0 <= x1
19 *    0 <= x2
20 *    0 <= x3
21 *  Integer
22 *  x0
23 *  End
24 */
25"""
26from mindoptpy import *
27
28
29if __name__ == "__main__":
30
31    # Step 1. Create model.
32    model = Model("MIQCP_01")
33
34    try:
35        # Step 2. Input model.
36
37        # Add variables.
38        x = []
39        x.append(model.addVar(0.0,         10.0, 0.0, 'I', "x0")) 
40        x.append(model.addVar(0.0, float('inf'), 0.0, 'C', "x1"))
41        x.append(model.addVar(0.0, float('inf'), 0.0, 'C', "x2"))
42        x.append(model.addVar(0.0, float('inf'), 0.0, 'C', "x3"))
43
44        # Add constraints.
45        # Note that the nonzero elements are inputted in a row-wise order here.
46        model.addConstr(1.0 * x[0] + 1.0 * x[1] + 2.0 * x[2] + 3.0 * x[3] - \
47                        0.5 * x[0] * x[0] - 0.5 * x[1] * x[1] - 0.5 * x[2] * x[2] - 0.5 * x[3] * x[3] - 0.5 * x[0] * x[1] >= 1, "c0")
48        model.addConstr(1.0 * x[0] - 1.0 * x[2] + 6.0 * x[3] + 0.5 * x[1] * x[1] <= 1, "c1")
49
50        # Add objective: 1 x0 + 1 x1 + 1 x2 + 1 x3 + 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1]
51        obj = QuadExpr()
52
53        #option-I
54        obj.addTerms([1.0, 1.0, 1.0, 1.0], [x[0], x[1], x[2], x[3]])
55        obj.addTerms([0.5, 0.5, 0.5, 0.5, 0.5], [x[0], x[1], x[2], x[3], x[0]], [x[0], x[1], x[2], x[3], x[1]])
56        
57        #option II
58        # obj = 1*x[0] + 1*x[1] + 1*x[2] + 1*x[3] + 0.5 * x[0]*x[0] + 0.5 * x[1]*x[1] + 0.5 * x[2]*x[2] + 0.5 * x[3]*x[3] + 0.5*x[0]*x[1]
59        
60        # Set objective and change to minimization problem.
61        model.setObjective(obj, MDO.MINIMIZE)
62
63        # Step 3. Solve the problem and populate optimization result.
64        model.optimize()
65
66        if model.status == MDO.OPTIMAL:
67            print(f"Optimal objective value is: {model.objval}")
68            print("Decision variables:")
69            for v in x:
70                print(f"x[{v.VarName}] = {v.X}")
71        else:
72            print("No feasible solution.")
73    except MindoptError as e:
74        print("Received Mindopt exception.")
75        print(" - Code          : {}".format(e.errno))
76        print(" - Reason        : {}".format(e.message))
77    except Exception as e:
78        print("Received other exception.")
79        print(" - Reason        : {}".format(e))
80    finally:
81        # Step 4. Free the model.
82        model.dispose()