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#

basic_milp_example.py

  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. The callback bound can be None when the solver has found an incumbent but no finite global bound is available yet.

incumbent_callback_example.py

 1# SPDX-FileCopyrightText: Copyright (c) 2025-2026, 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 host solutions
65    def callback(solution, solution_cost, solution_bound):
66        """Called when solver finds a new incumbent solution.
67
68        solution_bound can be None when no finite bound is available yet.
69        """
70        print(
71            f"Solution : {solution} cost : {solution_cost} "
72            f"bound : {solution_bound}\n"
73        )
74
75    # Logging callback - receives server log messages
76    def log_callback(log):
77        """Called when server sends log messages."""
78        for i in log:
79            print("server-log: ", i)
80
81    print("=== Solving MILP with Callbacks ===")
82    print("\n--- Logging Output ---")
83
84    solution = cuopt_service_client.get_LP_solve(
85        data,
86        incumbent_callback=callback,
87        response_type="dict",
88        logging_callback=log_callback,
89    )
90
91    print("\n--- Final Solution ---")
92    print(json.dumps(solution, indent=4))
93
94
95if __name__ == "__main__":
96    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#

abort_job_example.py

 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