Mixed Integer Linear Programming with Datamodel#
Consider the following example,
Given system constraints:
2x + 4y >= 230
3x + 2y <= 190
x >= 0, x - Integer
y >= 0, y - Continuous/Floating/non-Integer
Maximize objective function:
f(x) = 5x + 3y
You need to find x and y such a way that it satisfies constraints and maximizes the objective function.
import numpy as np
import cuopt_mps_parser
import solver_settings
from data_model import DataModel
from solver_settings import SolverSettings
problem_data = {}
dm = DataModel()
ss = SolverSettings()
Set Constraint Matrix#
If the constraints are:
2x + 4y >= 230
3x + 2y <= 190
Constraints are depicted in CSR format. The constraints can be transformed to the CSR matrix as follows:
offsets = np.array([0, 2, 4], dtype=np.int32)
indices = np.array([0, 1, 0, 1], dtype=np.int32)
coefficients = np.array([2.0, 4.0, 3.0, 2.0], dtype=np.float64)
dm.set_csr_constraint_matrix(coefficients, indices, offsets)
The offsets indicate the length of the constraint and indices indicate variables.
Set Constraint Bounds#
If the constraints are as follows:
2x + 4y >= 230
3x + 2y <= 190
You need to define upper_bounds
and lower_bounds
of all the
constraints, each value signifies the upper or lower bound of each
constraint respective to its index.
upper_bounds = np.array([np.inf, 190], dtype=np.float64)
lower_bounds = np.array([230, -np.inf], dtype=np.float64)
dm.set_constraint_lower_bounds(lower_bounds)
dm.set_constraint_upper_bounds(upper_bounds)
inf
- infinity and -inf
- negative infinity are used when there
is no explict upper or lower bound.
Set Variable Bounds#
Variables:
x >= 0
y >= 0
Define the variable bounds similar to constraint bounds.
var_upper_bounds = np.array([np.inf, np.inf], dtype=np.float64)
var_lower_bounds = np.array([0, 0], dtype=np.float64)
dm.set_variable_lower_bounds(var_lower_bounds)
dm.set_variable_upper_bounds(var_upper_bounds)
Set Objective Data#
Objective:
f(x) = 5x + 3y
Pass coefficents for objective data and also set whether it needs to be maximized or minimized.
objective_coefficients = np.array([5, 3], dtype=np.float64)
dm.set_objective_coefficients(objective_coefficients)
dm.set_maximize(True)
Set Variable Names#
This is optional, but it helps users to navigate the result.
dm.set_variable_names(np.array(["x", "y"]))
Set Variable Types#
Set variable types, “I” - Integer
“C” - Continuous
dm.set_variable_types(np.array(["I", "C"]))
Set Solver Configuration#
The solver configuration can be fine-tuned for optimization and runtimes.
ss.set_time_limit(1)
Solve the Problem#
For managed service, cuOpt endpoints can be triggered as shown in the thin client example for managed service.
For self-hosted, cuOpt endpoints can be triggered as shown in the thin client example for self-hosted.
Use this data and invoke the cuOpt endpoint, which would return values
for x
and y
.
The following example is using a locally hosted server:
data = cuopt_mps_parser.toDict(dm)
data["solver_config"] = solver_settings.toDict(ss)
import json
import time
from cuopt_sh_client import CuOptServiceSelfHostClient
# If cuOpt is not running on localhost:5000, edit ip and port parameters
cuopt_service_client = CuOptServiceSelfHostClient(
ip="localhost",
port=5000,
timeout_exception=False
)
data['variable_bounds']['upper_bounds'] = ["inf", "inf"]
repoll_tries = 50
def repoll(solution, repoll_tries):
# If solver is still busy solving, the job will be assigned a request id and response is sent back in the
# following format {"reqId": <REQUEST-ID>}. Solver needs to be rei-polled for response using this <REQUEST-ID>.
if "reqId" in solution and "response" not in solution:
req_id = solution["reqId"]
for i in range(repoll_tries):
solution = cuopt_service_client.repoll(req_id, response_type="dict")
if "reqId" in solution and "response" in solution:
break;
# Sleep for a second before requesting
time.sleep(1)
return solution
solution = cuopt_service_client.get_LP_solve(data, response_type="dict")
solution = repoll(solution, repoll_tries)
print(json.dumps(solution, indent=4))
Status - 2
corresponds to Feasible solution is available
.
{
"response": {
"solver_response": {
"status": 2,
"solution": {
"problem_category": 1,
"primal_solution": [
37.0,
39.49999999148369
],
"dual_solution": null,
"primal_objective": 303.49999997445104,
"dual_objective": null,
"solver_time": 1.005017378,
"vars": {
"x": 37.0,
"y": 39.49999999148369
},
"lp_statistics": {
"reduced_cost": null
},
"milp_statistics": {
"mip_gap": -303.49999997445104,
"solution_bound": -2.0,
"presolve_time": 0.042006453,
"max_constraint_violation": 0.0,
"max_int_violation": 0.0,
"max_variable_bound_violation": 0.0
}
}
},
"total_solve_time": 1.0515711307525635
},
"reqId": "fc2871c8-d3e8-410a-8a32-b540d572054c"
}