MILP Python Examples#
The major difference between this example and the LP example is that some of the variables are integers, so variable_types need to be shared.
The OpenAPI specification for the server is available in open-api spec. The example data is structured as per the OpenAPI specification for the server, please refer LPData data under “POST /cuopt/request” under schema section. LP and MILP share same spec.
Generic Example#
1# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2# SPDX-License-Identifier: Apache-2.0
3"""
4Basic MILP Server Example
5
6This example demonstrates how to use the cuOpt Self-Hosted Service Client
7to solve MILP (Mixed Integer Linear Programming) problems via the cuOpt server.
8
9The major difference between this example and the LP example is that some of
10the variables are integers, so 'variable_types' need to be specified.
11
12Requirements:
13 - cuOpt server running (default: localhost:5000)
14 - cuopt_sh_client package installed
15
16Problem:
17 Maximize: 1.2*x + 1.7*y
18 Subject to:
19 x + y <= 5000
20 x, y are integers
21 0 <= x <= 3000
22 0 <= y <= 5000
23
24Expected Response:
25 {
26 "response": {
27 "solver_response": {
28 "status": "Optimal",
29 "solution": {
30 "primal_objective": 8500.0,
31 "vars": {
32 "x": 0.0,
33 "y": 5000.0
34 }
35 }
36 }
37 }
38 }
39"""
40
41from cuopt_sh_client import CuOptServiceSelfHostClient
42import json
43import time
44
45
46def repoll(cuopt_service_client, solution, repoll_tries):
47 """
48 Repoll the server for solution if it's still processing.
49
50 If solver is still busy solving, the job will be assigned a request id
51 and response is sent back in the format {"reqId": <REQUEST-ID>}.
52 """
53 if "reqId" in solution and "response" not in solution:
54 req_id = solution["reqId"]
55 for i in range(repoll_tries):
56 solution = cuopt_service_client.repoll(
57 req_id, response_type="dict"
58 )
59 if "reqId" in solution and "response" in solution:
60 break
61
62 # Sleep for a second before requesting
63 time.sleep(1)
64
65 return solution
66
67
68def main():
69 """Run the basic MILP example."""
70 # Example data for MILP problem
71 # The data is structured as per the OpenAPI specification for the server
72 data = {
73 "csr_constraint_matrix": {
74 "offsets": [0, 2],
75 "indices": [0, 1],
76 "values": [1.0, 1.0],
77 },
78 "constraint_bounds": {"upper_bounds": [5000.0], "lower_bounds": [0.0]},
79 "objective_data": {
80 "coefficients": [1.2, 1.7],
81 "scalability_factor": 1.0,
82 "offset": 0.0,
83 },
84 "variable_bounds": {
85 "upper_bounds": [3000.0, 5000.0],
86 "lower_bounds": [0.0, 0.0],
87 },
88 "maximize": True,
89 "variable_names": ["x", "y"],
90 "variable_types": ["I", "I"], # Both variables are integers
91 "solver_config": {"time_limit": 30},
92 }
93
94 # If cuOpt is not running on localhost:5000, edit ip and port parameters
95 cuopt_service_client = CuOptServiceSelfHostClient(
96 ip="localhost", port=5000, polling_timeout=25, timeout_exception=False
97 )
98
99 print("=== Solving MILP Problem ===")
100 solution = cuopt_service_client.get_LP_solve(data, response_type="dict")
101
102 # Number of repoll requests to be carried out for a successful response
103 repoll_tries = 500
104
105 solution = repoll(cuopt_service_client, solution, repoll_tries)
106
107 print(json.dumps(solution, indent=4))
108
109
110if __name__ == "__main__":
111 main()
The response would be as follows:
1{
2 "response": {
3 "solver_response": {
4 "status": "Optimal",
5 "solution": {
6 "problem_category": "MIP",
7 "primal_solution": [
8 0.0,
9 5000.0
10 ],
11 "dual_solution": null,
12 "primal_objective": 8500.0,
13 "dual_objective": null,
14 "solver_time": 0.0,
15 "vars": {
16 "x": 0.0,
17 "y": 5000.0
18 },
19 "lp_statistics": {},
20 "reduced_cost": null,
21 "milp_statistics": {
22 "mip_gap": 0.0,
23 "solution_bound": 8500.0,
24 "presolve_time": 0.007354775,
25 "max_constraint_violation": 0.0,
26 "max_int_violation": 0.0,
27 "max_variable_bound_violation": 0.0,
28 "num_nodes": 1999468624,
29 "num_simplex_iterations": 21951
30 }
31 }
32 },
33 "total_solve_time": 0.08600544929504395
34 },
35 "reqId": "524e2e37-3494-4c16-bd06-2a9bfd768f76"
36}
Incumbent and Logging Callback#
The incumbent solution can be retrieved using a callback function as follows:
Note
Incumbent solution callback is only applicable to MILP.
1# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2# SPDX-License-Identifier: Apache-2.0
3"""
4MILP Incumbent and Logging Callback Example
5
6This example demonstrates how to use callbacks with the cuOpt server:
7- Incumbent solution callback: Receives intermediate solutions as they're found
8- Logging callback: Receives solver log messages in real-time
9
10Note:
11 Incumbent solution callback is only applicable to MILP, not for LP.
12
13Requirements:
14 - cuOpt server running (default: localhost:5000)
15 - cuopt_sh_client package installed
16
17Problem:
18 Maximize: 1.2*x + 1.7*y
19 Subject to:
20 x + y <= 5000
21 x, y are integers
22 0 <= x <= 3000
23 0 <= y <= 5000
24
25Expected Output:
26 server-log: Solving a problem with 1 constraints 2 variables (2 integers) and 2 nonzeros
27 ...
28 Solution : [0.0, 5000.0] cost : 8500.0
29"""
30
31from cuopt_sh_client import CuOptServiceSelfHostClient
32import json
33
34
35def main():
36 """Run the incumbent and logging callback example."""
37 data = {
38 "csr_constraint_matrix": {
39 "offsets": [0, 2],
40 "indices": [0, 1],
41 "values": [1.0, 1.0],
42 },
43 "constraint_bounds": {"upper_bounds": [5000.0], "lower_bounds": [0.0]},
44 "objective_data": {
45 "coefficients": [1.2, 1.7],
46 "scalability_factor": 1.0,
47 "offset": 0.0,
48 },
49 "variable_bounds": {
50 "upper_bounds": [3000.0, 5000.0],
51 "lower_bounds": [0.0, 0.0],
52 },
53 "maximize": True,
54 "variable_names": ["x", "y"],
55 "variable_types": ["I", "I"],
56 "solver_config": {"time_limit": 30},
57 }
58
59 # If cuOpt is not running on localhost:5000, edit ip and port parameters
60 cuopt_service_client = CuOptServiceSelfHostClient(
61 ip="localhost", port=5000, timeout_exception=False
62 )
63
64 # Incumbent callback - receives intermediate solutions
65 def callback(solution, solution_cost):
66 """Called when solver finds a new incumbent solution."""
67 print(f"Solution : {solution} cost : {solution_cost}\n")
68
69 # Logging callback - receives server log messages
70 def log_callback(log):
71 """Called when server sends log messages."""
72 for i in log:
73 print("server-log: ", i)
74
75 print("=== Solving MILP with Callbacks ===")
76 print("\n--- Logging Output ---")
77
78 solution = cuopt_service_client.get_LP_solve(
79 data,
80 incumbent_callback=callback,
81 response_type="dict",
82 logging_callback=log_callback,
83 )
84
85 print("\n--- Final Solution ---")
86 print(json.dumps(solution, indent=4))
87
88
89if __name__ == "__main__":
90 main()
Log the callback response:
1server-log: Solving a problem with 1 constraints 2 variables (2 integers) and 2 nonzeros
2server-log: Objective offset 0.000000 scaling_factor -1.000000
3server-log: After trivial presolve updated 1 constraints 2 variables
4server-log: Running presolve!
5server-log: Solving LP root relaxation
6.....
Incumbent callback response:
1 Solution : [0.0, 5000.0] cost : 8500.0
1{
2 "response": {
3 "solver_response": {
4 "status": "Optimal",
5 "solution": {
6 "problem_category": "MIP",
7 "primal_solution": [
8 0.0,
9 5000.0
10 ],
11 "dual_solution": null,
12 "primal_objective": 8500.0,
13 "dual_objective": null,
14 "solver_time": 0.0,
15 "vars": {
16 "x": 0.0,
17 "y": 5000.0
18 },
19 "lp_statistics": {},
20 "reduced_cost": null,
21 "milp_statistics": {
22 "mip_gap": 0.0,
23 "solution_bound": 8500.0,
24 "presolve_time": 0.001391178,
25 "max_constraint_violation": 0.0,
26 "max_int_violation": 0.0,
27 "max_variable_bound_violation": 0.0,
28 "num_nodes": 1999468624,
29 "num_simplex_iterations": 21951
30 }
31 }
32 },
33 "total_solve_time": 0.025009632110595703
34 },
35 "reqId": "eb753ac0-c6a2-4fda-9ad4-ee595cddf0ec"
36}
An example with DataModel is available in the Examples Notebooks Repository.
The data argument to get_LP_solve may be a dictionary of the format shown in MILP Open-API spec. More details on the response can be found under responses schema in “/cuopt/request” and “/cuopt/solution” API spec.
They can be of different format as well, please check the documentation.
Aborting a Running Job in Thin Client#
1# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2# SPDX-License-Identifier: Apache-2.0
3"""
4Aborting a Running MILP Job Example
5
6This example demonstrates how to abort a running or queued job on the cuOpt server.
7This is useful when:
8- A solve is taking too long
9- You want to cancel a queued job
10- You need to free up server resources
11
12Requirements:
13 - cuOpt server running (default: localhost:5000)
14 - cuopt_sh_client package installed
15
16Usage:
17 Replace <UUID_THAT_WE_GOT> with the actual UUID returned by the solver
18 when you submitted a job.
19"""
20
21from cuopt_sh_client import CuOptServiceSelfHostClient
22
23
24def main():
25 """Run the abort job example."""
26 # This is a UUID that is returned by the solver while the solver is trying
27 # to find solution so users can come back and check the status or query for results.
28 job_uuid = "<UUID_THAT_WE_GOT>"
29
30 print(f"Attempting to abort job: {job_uuid}")
31
32 # If cuOpt is not running on localhost:5000, edit ip and port parameters
33 cuopt_service_client = CuOptServiceSelfHostClient(
34 ip="localhost", port=5000
35 )
36
37 # Delete the job if it is still queued or running
38 # Parameters:
39 # - job_uuid: The UUID of the job to abort
40 # - running=True: Abort if the job is currently running
41 # - queued=True: Abort if the job is in the queue
42 # - cached=False: Don't delete from cache (only abort active/queued jobs)
43 response = cuopt_service_client.delete(
44 job_uuid, running=True, queued=True, cached=False
45 )
46
47 print(f"Response: {response}")
48
49
50if __name__ == "__main__":
51 # Example usage - in practice, you would get the UUID from a previous job submission
52 print("This is a template example.")
53 print("To use this:")
54 print("1. Submit a MILP job and get its UUID")
55 print("2. Replace '<UUID_THAT_WE_GOT>' in this file with that UUID")
56 print("3. Run this script to abort the job")
57
58 # Uncomment the line below and add your actual UUID to run
59 # main()
MILP CLI Examples#
Generic MILP Example#
The only difference between this example and the prior LP example would be the variable types provided in data.
echo '{
"csr_constraint_matrix": {
"offsets": [0, 2, 4],
"indices": [0, 1, 0, 1],
"values": [3.0, 4.0, 2.7, 10.1]
},
"constraint_bounds": {
"upper_bounds": [5.4, 4.9],
"lower_bounds": ["ninf", "ninf"]
},
"objective_data": {
"coefficients": [0.2, 0.1],
"scalability_factor": 1.0,
"offset": 0.0
},
"variable_bounds": {
"upper_bounds": ["inf", "inf"],
"lower_bounds": [0.0, 0.0]
},
"variable_names": ["x", "y"],
"variable_types": ["I", "I"],
"maximize": "False",
"solver_config": {
"time_limit": 30
}
}' > data.json
Invoke the CLI:
# Please update ip and port if the server is running on a different IP address or port
export ip="localhost"
export port=5000
cuopt_sh data.json -t LP -i $ip -p $port -sl -il
In case the user needs to update solver settings through CLI, the option -ss can be used as follows:
# Please update ip and port if the server is running on a different IP address or port
export ip="localhost"
export port=5000
cuopt_sh data.json -t LP -i $ip -p $port -ss '{"time_limit": 5}'
Note
Batch mode is not supported for MILP.
Aborting a Running Job In CLI#
UUID that is returned by the solver while the solver is trying to find a solution so users can come back and check the status or query for results.
This aborts a job with UUID if it’s in running state.
# Please update ip and port if the server is running on a different IP address or port
export ip="localhost"
export port=5000
cuopt_sh -d -r -q <UUID> -i $ip -p $port