5.6.7. MISOCP Modeling and Optimization in C¶
In this chapter, we will use MindOpt C API to model and solve the problem in MIQCP Example II.
Include the header file:
26#include <stdio.h>
27#include <stdlib.h>
Create an optimization model model:
54 /*------------------------------------------------------------------*/
55 CHECK_RESULT(MDOemptyenv(&env));
56 CHECK_RESULT(MDOstartenv(env));
Next, we set the optimization sense to minimization via MDOsetintattr(). Then, we call MDOaddvar() to add five variables. Their lower bounds, upper bounds, types, names, and linear objective coefficients are passed directly as arguments.
Note
To mark a variable as integer, set its type to MDO_INTEGER in the call to MDOaddvar().
59 /*------------------------------------------------------------------*/
60 /* Step 2. Input model. */
61 /*------------------------------------------------------------------*/
62 /* Change to minimization problem. */
63 CHECK_RESULT(MDOsetintattr(model, MODEL_SENSE, MDO_MINIMIZE));
64
65 /* Add variables. Objective coefficients are passed directly when creating variables. */
66 CHECK_RESULT(MDOaddvar(model, 0, NULL, NULL, 1.0, 0.0, 10.0, MDO_INTEGER, "x0"));
67 CHECK_RESULT(MDOaddvar(model, 0, NULL, NULL, 2.0, 0.0, MDO_INFINITY, MDO_INTEGER, "x1"));
68 CHECK_RESULT(MDOaddvar(model, 0, NULL, NULL, 1.0, 0.0, MDO_INFINITY, MDO_INTEGER, "x2"));
69 CHECK_RESULT(MDOaddvar(model, 0, NULL, NULL, 1.0, 0.0, MDO_INFINITY, MDO_CONTINUOUS, "x3"));
Note
The linear objective coefficients (1, 2, 1, 1, 0.5) are passed directly during variable creation via the fourth argument of MDOaddvar(). Therefore, there is no separate call to set or modify the objective, and it remains purely linear.
We now add the linear constraints. For the C API, this involves preparing arrays for variable indices and their corresponding coefficients.
Constraint c0: \(x_0 + x_1 + 2x_2 + 3x_3 \geq 1\)
Constraint c1: \(x_0 - x_2 + 6x_3 = 1\)
First, prepare the data arrays for these constraints:
72 /* Prepare data for constraints. */
73 /* Linear constraint c0: 1 x0 + 1 x1 + 2 x2 + 3 x3 >= 1 */
74 int c0_nnz = 4;
75 int c0_idx[] = {0, 1, 2, 3};
76 double c0_val[] = {1.0, 1.0, 2.0, 3.0};
77
78 /* Linear constraint c1: 1 x0 - 1 x2 + 6 x3 = 1 */
79 int c1_nnz = 3;
80 int c1_idx[] = {0, 2, 3};
Then, add them to the model using MDOaddconstr():
89 /* Add constraints to the model. */
90 /* Add c0 */
91 CHECK_RESULT(MDOaddconstr(model, c0_nnz, c0_idx, c0_val, MDO_GREATER_EQUAL, 1.0, "c0"));
92 /* Add c1 */
Finally, we add the quadratic (second-order cone) constraint:
c2: \(x_1^2 + x_2^2 - x_4^2 \leq 0\)
Similar to linear constraints, we prepare arrays for the row indices, column indices, and values of the quadratic terms:
83 /* Second order cone constraint c2: x_1^2 + x_2^2 - x_4^2 <= 0 */
84 int qc2_nnz = 3;
85 int qc2_col1[] = {1, 2, 4};
86 int qc2_col2[] = {1, 2, 4};
Then, the quadratic constraint is added to the model using MDOaddqconstr(). Note that this constraint has no linear part, so the arguments for the linear component are set to 0 and NULL.
94 CHECK_RESULT(MDOaddconstr(model, c1_nnz, c1_idx, c1_val, MDO_EQUAL, 1.0, "c1"));
95 /* Add c2: The constraint has no linear part, so the first three arguments are 0, NULL, NULL. */
Once the model is fully constructed, we solve it by calling MDOoptimize():
100 /*------------------------------------------------------------------*/
101 /* Solve the problem. */
After solving, we check the solution status. For MISOCP problems, it’s important to also check SolCount in case the solver terminated early but found a feasible solution. We then retrieve the optimal objective value and variable values using attribute-getting functions.
110 /* exist, making it necessary to check the SolCount property. */
111 /*------------------------------------------------------------------*/
112 CHECK_RESULT(MDOgetintattr(model, STATUS, &status));
113 CHECK_RESULT(MDOgetintattr(model, SOL_COUNT, &solcount));
114 if (status == MDO_OPTIMAL || status == MDO_SUB_OPTIMAL || solcount > 0)
115 {
116 CHECK_RESULT(MDOgetdblattr(model, OBJ_VAL, &obj));
117 printf("Optimal objective value is: %f\n", obj);
118 printf("Decision variables:\n");
119 for (i = 0; i < num_vars; ++i)
120 {
121 CHECK_RESULT(MDOgetdblattrelement(model, X, i, &x));
122 printf("x[%d] = %f\n", i, x);
123 }
124 }
125 else
126 {
127 printf("No feasible solution.\n");
Finally, we free the memory allocated for the model and environment:
31/* Macro to check the return code. */
32#define RELEASE_MEMORY \
33 MDOfreemodel(model); \
129 /*------------------------------------------------------------------*/
130 /* Step 5. Free the model. */
131 /*------------------------------------------------------------------*/
The complete example code is shown in MdoMISOCPEx1.c:
1/**
2 * Description
3 * -----------
4 *
5 * Formulation
6 * -----------
7 *
8 Minimize
9 * obj: 1 x0 + 2 x1 + 1 x2 + 1 x3 + 0.5 x_4
10 *
11 *
12 * Subject To
13 * c0 : 1 x0 + 1 x1 + 2 x2 + 3 x3 >= 1
14 * c1 : 1 x0 - 1 x2 + 6 x3 = 1
15 * c2 : x_1^2 + x_2^2 - x_4^2 <= 0
16 * Bounds
17 * 0 <= x0 <= 10
18 * 0 <= x1
19 * 0 <= x2
20 * 0 <= x3
21 * 0 <= x4
22 * Integer
23 * x0, x1, x2
24 * End
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include "Mindopt.h"
30
31/* Macro to check the return code. */
32#define RELEASE_MEMORY \
33 MDOfreemodel(model); \
34 MDOfreeenv(env);
35#define CHECK_RESULT(code) { int res = code; if (res != 0) { fprintf(stderr, "Bad code: %d\n", res); RELEASE_MEMORY; return (res); } }
36#define MODEL_NAME "MISOCPEx1"
37#define MODEL_SENSE "ModelSense"
38#define SOL_COUNT "SolCount"
39#define STATUS "Status"
40#define OBJ_VAL "ObjVal"
41#define X "X"
42
43int main(void)
44{
45 /* Variables. */
46 MDOenv *env;
47 MDOmodel *model;
48 double obj, x;
49 int i, solcount, status;
50 int num_vars = 5;
51
52 /*------------------------------------------------------------------*/
53 /* Step 1. Create environment and model. */
54 /*------------------------------------------------------------------*/
55 CHECK_RESULT(MDOemptyenv(&env));
56 CHECK_RESULT(MDOstartenv(env));
57 CHECK_RESULT(MDOnewmodel(env, &model, MODEL_NAME, 0, NULL, NULL, NULL, NULL, NULL));
58
59
60 /*------------------------------------------------------------------*/
61 /* Step 2. Input model. */
62 /*------------------------------------------------------------------*/
63 /* Change to minimization problem. */
64 CHECK_RESULT(MDOsetintattr(model, MODEL_SENSE, MDO_MINIMIZE));
65
66 /* Add variables. Objective coefficients are passed directly when creating variables. */
67 CHECK_RESULT(MDOaddvar(model, 0, NULL, NULL, 1.0, 0.0, 10.0, MDO_INTEGER, "x0"));
68 CHECK_RESULT(MDOaddvar(model, 0, NULL, NULL, 2.0, 0.0, MDO_INFINITY, MDO_INTEGER, "x1"));
69 CHECK_RESULT(MDOaddvar(model, 0, NULL, NULL, 1.0, 0.0, MDO_INFINITY, MDO_INTEGER, "x2"));
70 CHECK_RESULT(MDOaddvar(model, 0, NULL, NULL, 1.0, 0.0, MDO_INFINITY, MDO_CONTINUOUS, "x3"));
71 CHECK_RESULT(MDOaddvar(model, 0, NULL, NULL, 0.5, 0.0, MDO_INFINITY, MDO_CONTINUOUS, "x4"));
72
73 /* Prepare data for constraints. */
74 /* Linear constraint c0: 1 x0 + 1 x1 + 2 x2 + 3 x3 >= 1 */
75 int c0_nnz = 4;
76 int c0_idx[] = {0, 1, 2, 3};
77 double c0_val[] = {1.0, 1.0, 2.0, 3.0};
78
79 /* Linear constraint c1: 1 x0 - 1 x2 + 6 x3 = 1 */
80 int c1_nnz = 3;
81 int c1_idx[] = {0, 2, 3};
82 double c1_val[] = {1.0, -1.0, 6.0};
83
84 /* Second order cone constraint c2: x_1^2 + x_2^2 - x_4^2 <= 0 */
85 int qc2_nnz = 3;
86 int qc2_col1[] = {1, 2, 4};
87 int qc2_col2[] = {1, 2, 4};
88 double qc2_values[] = {1.0, 1.0, -1.0};
89
90 /* Add constraints to the model. */
91 /* Add c0 */
92 CHECK_RESULT(MDOaddconstr(model, c0_nnz, c0_idx, c0_val, MDO_GREATER_EQUAL, 1.0, "c0"));
93 /* Add c1 */
94 CHECK_RESULT(MDOaddconstr(model, c1_nnz, c1_idx, c1_val, MDO_EQUAL, 1.0, "c1"));
95 /* Add c2: The constraint has no linear part, so the first three arguments are 0, NULL, NULL. */
96 CHECK_RESULT(MDOaddqconstr(model, 0, NULL, NULL, qc2_nnz, qc2_col1, qc2_col2, qc2_values, MDO_LESS_EQUAL, 0.0, "c2"));
97
98 /*------------------------------------------------------------------*/
99 /* Step 3. Solve the problem. */
100 /*------------------------------------------------------------------*/
101 /* Solve the problem. */
102 CHECK_RESULT(MDOoptimize(model));
103
104 /*------------------------------------------------------------------*/
105 /* Step 4. Retrive model status and objective. */
106 /* For MIP(MILP,MIQP, MIQCP) problems, if the solving process */
107 /* terminates early due to reasons such as timeout or interruption, */
108 /* the model status will indicate termination by timeout (or */
109 /* interruption, etc.). However, suboptimal solutions may still */
110 /* exist, making it necessary to check the SolCount property. */
111 /*------------------------------------------------------------------*/
112 CHECK_RESULT(MDOgetintattr(model, STATUS, &status));
113 CHECK_RESULT(MDOgetintattr(model, SOL_COUNT, &solcount));
114 if (status == MDO_OPTIMAL || status == MDO_SUB_OPTIMAL || solcount > 0)
115 {
116 CHECK_RESULT(MDOgetdblattr(model, OBJ_VAL, &obj));
117 printf("Optimal objective value is: %f\n", obj);
118 printf("Decision variables:\n");
119 for (i = 0; i < num_vars; ++i)
120 {
121 CHECK_RESULT(MDOgetdblattrelement(model, X, i, &x));
122 printf("x[%d] = %f\n", i, x);
123 }
124 }
125 else
126 {
127 printf("No feasible solution.\n");
128 }
129
130 /*------------------------------------------------------------------*/
131 /* Step 5. Free the model. */
132 /*------------------------------------------------------------------*/
133 RELEASE_MEMORY;
134
135 return 0;
136}