|
| 1 | +==================== |
| 2 | +LP and MILP Examples |
| 3 | +==================== |
| 4 | + |
| 5 | +This section contains examples of how to use the cuOpt linear programming and mixed integer linear programming Python API. |
| 6 | + |
| 7 | +.. note:: |
| 8 | + |
| 9 | + The examples in this section are not exhaustive. They are provided to help you get started with the cuOpt linear programming and mixed integer linear programming Python API. For more examples, please refer to the `cuopt-examples GitHub repository <https://github.com/NVIDIA/cuopt-examples>`_. |
| 10 | + |
| 11 | + |
| 12 | +Simple Linear Programming Example |
| 13 | +--------------------------------- |
| 14 | + |
| 15 | +.. code-block:: python |
| 16 | +
|
| 17 | + from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE |
| 18 | + from cuopt.linear_programming.solver_settings import SolverSettings |
| 19 | +
|
| 20 | + # Create a new problem |
| 21 | + problem = Problem("Simple LP") |
| 22 | + |
| 23 | + # Add variables |
| 24 | + x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") |
| 25 | + y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") |
| 26 | +
|
| 27 | + # Add constraints |
| 28 | + problem.addConstraint(x + y <= 10, name="c1") |
| 29 | + problem.addConstraint(x - y >= 0, name="c2") |
| 30 | +
|
| 31 | + # Set objective function |
| 32 | + problem.setObjective(x + y, sense=MAXIMIZE) |
| 33 | + |
| 34 | + # Configure solver settings |
| 35 | + settings = SolverSettings() |
| 36 | + settings.set_parameter("time_limit", 60) |
| 37 | + |
| 38 | + # Solve the problem |
| 39 | + problem.solve(settings) |
| 40 | + |
| 41 | + # Check solution status |
| 42 | + if problem.Status.name == "Optimal": |
| 43 | + print(f"Optimal solution found in {problem.SolveTime:.2f} seconds") |
| 44 | + print(f"x = {x.getValue()}") |
| 45 | + print(f"y = {y.getValue()}") |
| 46 | + print(f"Objective value = {problem.ObjValue}") |
| 47 | +
|
| 48 | +The response is as follows: |
| 49 | + |
| 50 | +.. code-block:: text |
| 51 | +
|
| 52 | + Optimal solution found in 0.01 seconds |
| 53 | + x = 10.0 |
| 54 | + y = 0.0 |
| 55 | + Objective value = 10.0 |
| 56 | +
|
| 57 | +Mixed Integer Linear Programming Example |
| 58 | +---------------------------------------- |
| 59 | + |
| 60 | +.. code-block:: python |
| 61 | +
|
| 62 | + from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE |
| 63 | + from cuopt.linear_programming.solver_settings import SolverSettings |
| 64 | +
|
| 65 | + # Create a new MIP problem |
| 66 | + problem = Problem("Simple MIP") |
| 67 | + |
| 68 | + # Add integer variables with bounds |
| 69 | + x = problem.addVariable(vtype=INTEGER, name="V_x") |
| 70 | + y = problem.addVariable(lb=10, ub=50, vtype=INTEGER, name="V_y") |
| 71 | +
|
| 72 | + # Add constraints |
| 73 | + problem.addConstraint(2 * x + 4 * y >= 230, name="C1") |
| 74 | + problem.addConstraint(3 * x + 2 * y <= 190, name="C2") |
| 75 | +
|
| 76 | + # Set objective function |
| 77 | + problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE) |
| 78 | +
|
| 79 | + # Configure solver settings |
| 80 | + settings = SolverSettings() |
| 81 | + settings.set_parameter("time_limit", 60) |
| 82 | +
|
| 83 | + # Solve the problem |
| 84 | + problem.solve(settings) |
| 85 | + |
| 86 | + # Check solution status and results |
| 87 | + if problem.Status.name == "Optimal": |
| 88 | + print(f"Optimal solution found in {problem.SolveTime:.2f} seconds") |
| 89 | + print(f"x = {x.getValue()}") |
| 90 | + print(f"y = {y.getValue()}") |
| 91 | + print(f"Objective value = {problem.ObjValue}") |
| 92 | + else: |
| 93 | + print(f"Problem status: {problem.Status.name}") |
| 94 | +
|
| 95 | +The response is as follows: |
| 96 | + |
| 97 | +.. code-block:: text |
| 98 | +
|
| 99 | + Optimal solution found in 0.00 seconds |
| 100 | + x = 36.0 |
| 101 | + y = 40.99999999999999 |
| 102 | + Objective value = 303.0 |
| 103 | +
|
| 104 | +
|
| 105 | +Advanced Example: Production Planning |
| 106 | +------------------------------------- |
| 107 | + |
| 108 | +.. code-block:: python |
| 109 | +
|
| 110 | + from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE |
| 111 | + from cuopt.linear_programming.solver_settings import SolverSettings |
| 112 | +
|
| 113 | + # Production planning problem |
| 114 | + problem = Problem("Production Planning") |
| 115 | + |
| 116 | + # Decision variables: production quantities |
| 117 | + # x1 = units of product A |
| 118 | + # x2 = units of product B |
| 119 | + x1 = problem.addVariable(lb=10, vtype=INTEGER, name="Product_A") |
| 120 | + x2 = problem.addVariable(lb=15, vtype=INTEGER, name="Product_B") |
| 121 | + |
| 122 | + # Resource constraints |
| 123 | + # Machine time: 2 hours per unit of A, 1 hour per unit of B, max 100 hours |
| 124 | + problem.addConstraint(2 * x1 + x2 <= 100, name="Machine_Time") |
| 125 | + |
| 126 | + # Labor: 1 hour per unit of A, 3 hours per unit of B, max 120 hours |
| 127 | + problem.addConstraint(x1 + 3 * x2 <= 120, name="Labor_Hours") |
| 128 | + |
| 129 | + # Material: 4 units per unit of A, 2 units per unit of B, max 200 units |
| 130 | + problem.addConstraint(4 * x1 + 2 * x2 <= 200, name="Material") |
| 131 | + |
| 132 | + # Objective: maximize profit |
| 133 | + # Profit: $50 per unit of A, $30 per unit of B |
| 134 | + problem.setObjective(50 * x1 + 30 * x2, sense=MAXIMIZE) |
| 135 | + |
| 136 | + # Solve with time limit |
| 137 | + settings = SolverSettings() |
| 138 | + settings.set_parameter("time_limit", 30) |
| 139 | + problem.solve(settings) |
| 140 | + |
| 141 | + # Display results |
| 142 | + if problem.Status.name == "Optimal": |
| 143 | + print("=== Production Planning Solution ===") |
| 144 | + print(f"Status: {problem.Status.name}") |
| 145 | + print(f"Solve time: {problem.SolveTime:.2f} seconds") |
| 146 | + print(f"Product A production: {x1.getValue()} units") |
| 147 | + print(f"Product B production: {x2.getValue()} units") |
| 148 | + print(f"Total profit: ${problem.ObjValue:.2f}") |
| 149 | + |
| 150 | + else: |
| 151 | + print(f"Problem not solved optimally. Status: {problem.Status.name}") |
| 152 | +
|
| 153 | +The response is as follows: |
| 154 | + |
| 155 | +.. code-block:: text |
| 156 | +
|
| 157 | + === Production Planning Solution === |
| 158 | +
|
| 159 | + Status: Optimal |
| 160 | + Solve time: 0.09 seconds |
| 161 | + Product A production: 36.0 units |
| 162 | + Product B production: 28.000000000000004 units |
| 163 | + Total profit: $2640.00 |
| 164 | +
|
| 165 | +Working with Expressions and Constraints |
| 166 | +---------------------------------------- |
| 167 | + |
| 168 | +.. code-block:: python |
| 169 | +
|
| 170 | + from cuopt.linear_programming.problem import Problem, MAXIMIZE |
| 171 | + from cuopt.linear_programming.solver_settings import SolverSettings |
| 172 | +
|
| 173 | + problem = Problem("Expression Example") |
| 174 | + |
| 175 | + # Create variables |
| 176 | + x = problem.addVariable(lb=0, name="x") |
| 177 | + y = problem.addVariable(lb=0, name="y") |
| 178 | + z = problem.addVariable(lb=0, name="z") |
| 179 | + |
| 180 | + # Create complex expressions |
| 181 | + expr1 = 2 * x + 3 * y - z |
| 182 | + expr2 = x + y + z |
| 183 | + |
| 184 | + # Add constraints using expressions |
| 185 | + problem.addConstraint(expr1 <= 100, name="Complex_Constraint_1") |
| 186 | + problem.addConstraint(expr2 >= 20, name="Complex_Constraint_2") |
| 187 | + |
| 188 | + # Add constraint with different senses |
| 189 | + problem.addConstraint(x + y == 50, name="Equality_Constraint") |
| 190 | + problem.addConstraint(1 * x <= 30, name="Upper_Bound_X") |
| 191 | + problem.addConstraint(1 * y >= 10, name="Lower_Bound_Y") |
| 192 | + problem.addConstraint(1 * z <= 100, name="Upper_Bound_Z") |
| 193 | + |
| 194 | + # Set objective |
| 195 | + problem.setObjective(x + 2 * y + 3 * z, sense=MAXIMIZE) |
| 196 | +
|
| 197 | + settings = SolverSettings() |
| 198 | + settings.set_parameter("time_limit", 20) |
| 199 | +
|
| 200 | + problem.solve(settings) |
| 201 | + |
| 202 | + |
| 203 | + if problem.Status.name == "Optimal": |
| 204 | + print("=== Expression Example Results ===") |
| 205 | + print(f"x = {x.getValue()}") |
| 206 | + print(f"y = {y.getValue()}") |
| 207 | + print(f"z = {z.getValue()}") |
| 208 | + print(f"Objective value = {problem.ObjValue}") |
| 209 | + |
| 210 | +The response is as follows: |
| 211 | + |
| 212 | +.. code-block:: text |
| 213 | +
|
| 214 | + === Expression Example Results === |
| 215 | + x = 0.0 |
| 216 | + y = 50.0 |
| 217 | + z = 99.99999999999999 |
| 218 | + Objective value = 399.99999999999994 |
| 219 | +
|
| 220 | +Working with Incumbent Solutions |
| 221 | +-------------------------------- |
| 222 | + |
| 223 | +Incumbent solutions are intermediate feasible solutions found during the MIP solving process. They represent the best integer-feasible solution discovered so far and can be accessed through callback functions. |
| 224 | + |
| 225 | +.. note:: |
| 226 | + Incumbent solutions are only available for Mixed Integer Programming (MIP) problems, not for pure Linear Programming (LP) problems. |
| 227 | + |
| 228 | +.. code-block:: python |
| 229 | +
|
| 230 | + from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE |
| 231 | + from cuopt.linear_programming.solver_settings import SolverSettings |
| 232 | + from cuopt.linear_programming.solver.solver_parameters import CUOPT_TIME_LIMIT |
| 233 | + from cuopt.linear_programming.internals import GetSolutionCallback, SetSolutionCallback |
| 234 | +
|
| 235 | + # Create a callback class to receive incumbent solutions |
| 236 | + class IncumbentCallback(GetSolutionCallback): |
| 237 | + def __init__(self): |
| 238 | + super().__init__() |
| 239 | + self.solutions = [] |
| 240 | + self.n_callbacks = 0 |
| 241 | +
|
| 242 | + def get_solution(self, solution, solution_cost): |
| 243 | + """ |
| 244 | + Called whenever the solver finds a new incumbent solution. |
| 245 | +
|
| 246 | + Parameters |
| 247 | + ---------- |
| 248 | + solution : array-like |
| 249 | + The variable values of the incumbent solution |
| 250 | + solution_cost : array-like |
| 251 | + The objective value of the incumbent solution |
| 252 | + """ |
| 253 | + self.n_callbacks += 1 |
| 254 | +
|
| 255 | + # Store the incumbent solution |
| 256 | + incumbent = { |
| 257 | + "solution": solution.copy_to_host(), |
| 258 | + "cost": solution_cost.copy_to_host()[0], |
| 259 | + "iteration": self.n_callbacks |
| 260 | + } |
| 261 | + self.solutions.append(incumbent) |
| 262 | +
|
| 263 | + print(f"Incumbent {self.n_callbacks}: {incumbent['solution']}, cost: {incumbent['cost']:.2f}") |
| 264 | +
|
| 265 | + # Create a more complex MIP problem that will generate multiple incumbents |
| 266 | + problem = Problem("Incumbent Example") |
| 267 | +
|
| 268 | + # Add integer variables |
| 269 | + x = problem.addVariable(vtype=INTEGER) |
| 270 | + y = problem.addVariable(vtype=INTEGER) |
| 271 | +
|
| 272 | + # Add constraints to create a problem that will generate multiple incumbents |
| 273 | + problem.addConstraint(2 * x + 4 * y >= 230) |
| 274 | + problem.addConstraint(3 * x + 2 * y <= 190) |
| 275 | +
|
| 276 | + # Set objective to maximize |
| 277 | + problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE) |
| 278 | +
|
| 279 | + # Configure solver settings with callback |
| 280 | + settings = SolverSettings() |
| 281 | + # Set the incumbent callback |
| 282 | + incumbent_callback = IncumbentCallback() |
| 283 | + settings.set_mip_callback(incumbent_callback) |
| 284 | + settings.set_parameter(CUOPT_TIME_LIMIT, 30) # Allow enough time to find multiple incumbents |
| 285 | +
|
| 286 | + # Solve the problem |
| 287 | + problem.solve(settings) |
| 288 | +
|
| 289 | + # Display final results |
| 290 | + print(f"\n=== Final Results ===") |
| 291 | + print(f"Problem status: {problem.Status.name}") |
| 292 | + print(f"Solve time: {problem.SolveTime:.2f} seconds") |
| 293 | + print(f"Final solution: x={x.getValue()}, y={y.getValue()}") |
| 294 | + print(f"Final objective value: {problem.ObjValue:.2f}") |
| 295 | + |
| 296 | +The response is as follows: |
| 297 | + |
| 298 | +.. code-block:: text |
| 299 | +
|
| 300 | + Optimal solution found. |
| 301 | + Incumbent 1: [ 0. 58.], cost: 174.00 |
| 302 | + Incumbent 2: [36. 41.], cost: 303.00 |
| 303 | + Generated fast solution in 0.158467 seconds with objective 303.000000 |
| 304 | + Consuming B&B solutions, solution queue size 2 |
| 305 | + Solution objective: 303.000000 , relative_mip_gap 0.000000 solution_bound 303.000000 presolve_time 0.043211 total_solve_time 0.160270 max constraint violation 0.000000 max int violation 0.000000 max var bounds violation 0.000000 nodes 4 simplex_iterations 3 |
| 306 | +
|
| 307 | + === Final Results === |
| 308 | + Problem status: Optimal |
| 309 | + Solve time: 0.16 seconds |
| 310 | + Final solution: x=36.0, y=40.99999999999999 |
| 311 | + Final objective value: 303.00 |
| 312 | +
|
| 313 | +
|
0 commit comments