LP Python Examples#
The following example showcases how to use the CuOptServiceSelfHostClient
to solve a simple LP problem in normal mode and batch mode (where multiple problems are solved at once).
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 under “POST /cuopt/request” under schema section. LP and MILP share same spec.
If you want to run server locally, please run the following command in a terminal or tmux session so you can test examples in another terminal.
1export ip="localhost"
2export port=5000
3python -m cuopt_server.cuopt_service --ip $ip --port $port
Genric Example With Normal Mode and Batch Mode#
1from cuopt_sh_client import CuOptServiceSelfHostClient
2import json
3import time
4
5# Example data for LP problem
6# The data is structured as per the OpenAPI specification for the server, please refer /cuopt/request -> schema -> LPData
7data = {
8 "csr_constraint_matrix": {
9 "offsets": [0, 2, 4],
10 "indices": [0, 1, 0, 1],
11 "values": [3.0, 4.0, 2.7, 10.1]
12 },
13 "constraint_bounds": {
14 "upper_bounds": [5.4, 4.9],
15 "lower_bounds": ["ninf", "ninf"]
16 },
17 "objective_data": {
18 "coefficients": [-0.2, 0.1],
19 "scalability_factor": 1.0,
20 "offset": 0.0
21 },
22 "variable_bounds": {
23 "upper_bounds": ["inf", "inf"],
24 "lower_bounds": [0.0, 0.0]
25 },
26 "maximize": False,
27 "solver_config": {
28 "tolerances": {
29 "optimality": 0.0001
30 }
31 }
32}
33
34# If cuOpt is not running on localhost:5000, edit ip and port parameters
35cuopt_service_client = CuOptServiceSelfHostClient(
36 ip="localhost",
37 port=5000,
38 polling_timeout=25,
39 timeout_exception=False
40)
41
42# Number of repoll requests to be carried out for a successful response
43repoll_tries = 500
44
45def repoll(solution, repoll_tries):
46 # If solver is still busy solving, the job will be assigned a request id and response is sent back in the
47 # following format {"reqId": <REQUEST-ID>}.
48 # Solver needs to be re-polled for response using this <REQUEST-ID>.
49
50 if "reqId" in solution and "response" not in solution:
51 req_id = solution["reqId"]
52 for i in range(repoll_tries):
53 solution = cuopt_service_client.repoll(req_id, response_type="dict")
54 if "reqId" in solution and "response" in solution:
55 break;
56
57 # Sleep for a second before requesting
58 time.sleep(1)
59
60 return solution
61
62# Logging callback
63def log_callback(log):
64 for i in log:
65 print("server-log: ", log)
66
67solution = cuopt_service_client.get_LP_solve(
68 data, response_type="dict", logging_callback=log_callback
69)
70
71solution = repoll(solution, repoll_tries)
72
73print("---------- Normal mode --------------- \n", json.dumps(solution, indent=4))
74
75# For batch mode send list of mps/dict/DataModel
76
77solution = cuopt_service_client.get_LP_solve(
78 [data, data], response_type="dict", logging_callback=log_callback
79)
80solution = repoll(solution, repoll_tries)
81
82print("---------- Batch mode ----------------- \n", json.dumps(solution, indent=4))
The response would be as follows:
Normal mode response:
1{
2 "response": {
3 "solver_response": {
4 "status": "Optimal",
5 "solution": {
6 "problem_category": "LP",
7 "primal_solution": [
8 1.8,
9 0.0
10 ],
11 "dual_solution": [
12 -0.06666666666666668,
13 0.0
14 ],
15 "primal_objective": -0.36000000000000004,
16 "dual_objective": 6.92188481708744e-310,
17 "solver_time": 0.006462812423706055,
18 "vars": {},
19 "lp_statistics": {
20 "primal_residual": 6.92114652678267e-310,
21 "dual_residual": 6.9218848170975e-310,
22 "gap": 6.92114652686054e-310,
23 "nb_iterations": 1
24 },
25 "reduced_cost": [
26 0.0,
27 0.0031070813207920247
28 ],
29 "milp_statistics": {}
30 }
31 },
32 "total_solve_time": 0.013341188430786133
33 },
34 "reqId": "c7f2e5a1-d210-4e2e-9308-4257d0a86c4a"
35}
Batch mode response:
1{
2 "response": {
3 "solver_response": [
4 {
5 "status": "Optimal",
6 "solution": {
7 "problem_category": "LP",
8 "primal_solution": [
9 1.8,
10 0.0
11 ],
12 "dual_solution": [
13 -0.06666666666666668,
14 0.0
15 ],
16 "primal_objective": -0.36000000000000004,
17 "dual_objective": 6.92188481708744e-310,
18 "solver_time": 0.005717039108276367,
19 "vars": {},
20 "lp_statistics": {
21 "primal_residual": 6.92114652678267e-310,
22 "dual_residual": 6.9218848170975e-310,
23 "gap": 6.92114652686054e-310,
24 "nb_iterations": 1
25 },
26 "reduced_cost": [
27 0.0,
28 0.0031070813207920247
29 ],
30 "milp_statistics": {}
31 }
32 },
33 {
34 "status": "Optimal",
35 "solution": {
36 "problem_category": "LP",
37 "primal_solution": [
38 1.8,
39 0.0
40 ],
41 "dual_solution": [
42 -0.06666666666666668,
43 0.0
44 ],
45 "primal_objective": -0.36000000000000004,
46 "dual_objective": 6.92188481708744e-310,
47 "solver_time": 0.007481813430786133,
48 "vars": {},
49 "lp_statistics": {
50 "primal_residual": 6.921146112128e-310,
51 "dual_residual": 6.9218848170975e-310,
52 "gap": 6.92114611220587e-310,
53 "nb_iterations": 1
54 },
55 "reduced_cost": [
56 0.0,
57 0.0031070813207920247
58 ],
59 "milp_statistics": {}
60 }
61 }
62 ],
63 "total_solve_time": 0.013
64 },
65 "reqId": "69dc8f36-16c3-4e28-8fb9-3977eb92b480"
66}
Note
Warm start is only applicable to LP and not for MILP.
Warm Start#
Previously run solutions can be saved and be used as warm start for new requests using previously run reqIds as follows:
1from cuopt_sh_client import CuOptServiceSelfHostClient
2import json
3
4data = {
5 "csr_constraint_matrix": {
6 "offsets": [0, 2, 4],
7 "indices": [0, 1, 0, 1],
8 "values": [3.0, 4.0, 2.7, 10.1]
9 },
10 "constraint_bounds": {
11 "upper_bounds": [5.4, 4.9],
12 "lower_bounds": ["ninf", "ninf"]
13 },
14 "objective_data": {
15 "coefficients": [-0.2, 0.1],
16 "scalability_factor": 1.0,
17 "offset": 0.0
18 },
19 "variable_bounds": {
20 "upper_bounds": ["inf", "inf"],
21 "lower_bounds": [0.0, 0.0]
22 },
23 "maximize": False,
24 "solver_config": {
25 "tolerances": {
26 "optimality": 0.0001
27 }
28 }
29}
30
31# If cuOpt is not running on localhost:5000, edit ip and port parameters
32cuopt_service_client = CuOptServiceSelfHostClient(
33 ip="localhost",
34 port=5000,
35 timeout_exception=False
36)
37
38# Set delete_solution to false so it can be used in next request
39initial_solution = cuopt_service_client.get_LP_solve(
40 data, delete_solution=False, response_type="dict"
41)
42
43# Use previous solution saved in server as initial solution to this request.
44# That solution is referenced with previous request id.
45solution = cuopt_service_client.get_LP_solve(
46 data, warmstart_id=initial_solution["reqId"], response_type="dict"
47)
48
49print(json.dumps(solution, indent=4))
50
51# Delete saved solution if not required to save space
52cuopt_service_client.delete(initial_solution["reqId"])
The response would be as follows:
1{
2 "response": {
3 "solver_response": {
4 "status": "Optimal",
5 "solution": {
6 "problem_category": "LP",
7 "primal_solution": [
8 1.8,
9 0.0
10 ],
11 "dual_solution": [
12 -0.06666666666666668,
13 0.0
14 ],
15 "primal_objective": -0.36000000000000004,
16 "dual_objective": 6.92188481708744e-310,
17 "solver_time": 0.006613016128540039,
18 "vars": {},
19 "lp_statistics": {
20 "primal_residual": 6.921146112128e-310,
21 "dual_residual": 6.9218848170975e-310,
22 "gap": 6.92114611220587e-310,
23 "nb_iterations": 1
24 },
25 "reduced_cost": [
26 0.0,
27 0.0031070813207920247
28 ],
29 "milp_statistics": {}
30 }
31 },
32 "total_solve_time": 0.013310909271240234
33 },
34 "reqId": "6d1e278f-5505-4bcc-8a33-2f7f7d6f8a30"
35}
Using MPS file directly#
An example on using .mps files as input is shown below:
1from cuopt_sh_client import CuOptServiceSelfHostClient, ThinClientSolverSettings
2import json
3
4data = "sample.mps"
5
6mps_data = """* optimize
7* cost = -0.2 * VAR1 + 0.1 * VAR2
8* subject to
9* 3 * VAR1 + 4 * VAR2 <= 5.4
10* 2.7 * VAR1 + 10.1 * VAR2 <= 4.9
11NAME good-1
12ROWS
13 N COST
14 L ROW1
15 L ROW2
16COLUMNS
17 VAR1 COST -0.2
18 VAR1 ROW1 3 ROW2 2.7
19 VAR2 COST 0.1
20 VAR2 ROW1 4 ROW2 10.1
21RHS
22 RHS1 ROW1 5.4 ROW2 4.9
23ENDATA
24"""
25
26with open(data, "w") as file:
27 file.write(mps_data)
28
29# If cuOpt is not running on localhost:5000, edit `ip` and `port` parameters
30cuopt_service_client = CuOptServiceSelfHostClient(
31 ip="localhost",
32 port=5000,
33 timeout_exception=False
34)
35
36ss = ThinClientSolverSettings()
37
38ss.set_parameter("time_limit", 5)
39ss.set_optimality_tolerance(0.00001)
40
41solution = cuopt_service_client.get_LP_solve(data, solver_config=ss, response_type="dict")
42
43print(json.dumps(solution, indent=4))
The response is:
1{
2 "response": {
3 "solver_response": {
4 "status": "Optimal",
5 "solution": {
6 "problem_category": "LP",
7 "primal_solution": [
8 1.8,
9 0.0
10 ],
11 "dual_solution": [
12 -0.06666666666666668,
13 0.0
14 ],
15 "primal_objective": -0.36000000000000004,
16 "dual_objective": 6.92188481708744e-310,
17 "solver_time": 0.008397102355957031,
18 "vars": {
19 "VAR1": 1.8,
20 "VAR2": 0.0
21 },
22 "lp_statistics": {
23 "primal_residual": 6.921146112128e-310,
24 "dual_residual": 6.9218848170975e-310,
25 "gap": 6.92114611220587e-310,
26 "nb_iterations": 1
27 },
28 "reduced_cost": [
29 0.0,
30 0.0031070813207920247
31 ],
32 "milp_statistics": {}
33 }
34 },
35 "total_solve_time": 0.014980316162109375
36 },
37 "reqId": "3f36bad7-6135-4ffd-915b-858c449c7cbb"
38}
Generate Datamodel from MPS Parser#
Use a datamodel generated from mps file as input; this yields a solution object in response. For more details please refer to LP/MILP parameters.
1from cuopt_sh_client import (
2 CuOptServiceSelfHostClient,
3 ThinClientSolverSettings,
4 PDLPSolverMode
5)
6import cuopt_mps_parser
7import json
8import time
9
10# -- Parse the MPS file --
11
12data = "sample.mps"
13
14mps_data = """* optimize
15* cost = -0.2 * VAR1 + 0.1 * VAR2
16* subject to
17* 3 * VAR1 + 4 * VAR2 <= 5.4
18* 2.7 * VAR1 + 10.1 * VAR2 <= 4.9
19NAME good-1
20ROWS
21 N COST
22 L ROW1
23 L ROW2
24COLUMNS
25 VAR1 COST -0.2
26 VAR1 ROW1 3 ROW2 2.7
27 VAR2 COST 0.1
28 VAR2 ROW1 4 ROW2 10.1
29RHS
30 RHS1 ROW1 5.4 ROW2 4.9
31ENDATA
32"""
33
34with open(data, "w") as file:
35 file.write(mps_data)
36
37# Parse the MPS file and measure the time spent
38parse_start = time.time()
39data_model = cuopt_mps_parser.ParseMps(data)
40parse_time = time.time() - parse_start
41
42# -- Build the client object --
43
44# If cuOpt is not running on localhost:5000, edit `ip` and `port` parameters
45cuopt_service_client = CuOptServiceSelfHostClient(
46 ip="localhost",
47 port=5000,
48 timeout_exception=False
49)
50
51# -- Set the solver settings --
52
53ss = ThinClientSolverSettings()
54
55# Set the solver mode to the same of the blogpost, Fast1.
56# Stable1 could also be used.
57ss.set_parameter("pdlp_solver_mode", PDLPSolverMode.Fast1)
58
59# Set the general tolerance to 1e-4 which is already the default value.
60# For more detail on optimality checkout `SolverSettings.set_optimality_tolerance()`
61ss.set_optimality_tolerance(1e-4)
62
63# Here you could set an iteration limit to 1000 and time limit to 10 seconds
64# By default there is no iteration limit and the max time limit is 10 minutes
65# Any problem taking more than 10 minutes to solve will stop and the current solution will be returned
66# For this example, no limit is set
67# settings.set_iteration_limit(1000)
68# settings.set_time_limit(10)
69ss.set_parameter("time_limit", 5)
70
71# -- Call solve --
72
73network_time = time.time()
74solution = cuopt_service_client.get_LP_solve(data_model, ss)
75network_time = time.time() - network_time
76
77# -- Retrieve the solution object and print the details --
78
79solution_status = solution["response"]["solver_response"]["status"]
80solution_obj = solution["response"]["solver_response"]["solution"]
81
82# Check Termination Reason
83print("Termination Reason: ")
84print(solution_status)
85
86# Check found objective value
87print("Objective Value:")
88print(solution_obj.get_primal_objective())
89
90# Check the MPS parse time
91print(f"Mps Parse time: {parse_time:.3f} sec")
92
93# Check network time (client call - solve time)
94network_time = network_time - (solution_obj.get_solve_time())
95print(f"Network time: {network_time:.3f} sec")
96
97# Check solver time
98solve_time = solution_obj.get_solve_time()
99print(f"Engine Solve time: {solve_time:.3f} sec")
100
101# Check the total end to end time (mps parsing + network + solve time)
102end_to_end_time = parse_time + network_time + solve_time
103print(f"Total end to end time: {end_to_end_time:.3f} sec")
104
105# Print the found decision variables
106print("Variables Values:")
107print(solution_obj.get_vars())
The response would be as follows:
1 Termination Reason: (1 is Optimal)
2 1
3 Objective Value:
4 -0.36000000000000004
5 Mps Parse time: 0.000 sec
6 Network time: 1.062 sec
7 Engine Solve time: 0.004 sec
8 Total end to end time: 1.066 sec
9 Variables Values:
10 {'VAR1': 1.8, 'VAR2': 0.0}
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 LP Open-API spec. More details on the response can be found under the responses schema “get /cuopt/request” and “get /cuopt/solution” API spec.
Aborting a Running Job in Thin Client#
Please refer to the Aborting a Running Job in Thin Client in the MILP Example for more details.
LP CLI Examples#
Generic Example#
The following examples showcase how to use the cuopt_sh
CLI to solve a simple LP problem.
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]
},
"maximize": "False",
"solver_config": {
"tolerances": {
"optimality": 0.0001
}
}
}' > data.json
Invoke the CLI.
# Please update these values 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
Response is as follows:
1{
2 "response": {
3 "solver_response": {
4 "status": "Optimal",
5 "solution": {
6 "problem_category": "LP",
7 "primal_solution": [1.8, 0.0],
8 "dual_solution": [-0.06666666666666668, 0.0],
9 "primal_objective": -0.36000000000000004,
10 "dual_objective": 6.92188481708744e-310,
11 "solver_time": 0.007324934005737305,
12 "vars": {},
13 "lp_statistics": {
14 "primal_residual": 6.921146112128e-310,
15 "dual_residual": 6.9218848170975e-310,
16 "gap": 6.92114611220587e-310,
17 "nb_iterations": 1
18 },
19 "reduced_cost": [0.0, 0.0031070813207920247],
20 "milp_statistics": {}
21 }
22 },
23 "total_solve_time": 0.014164209365844727
24 },
25 "reqId": "4665e513-341e-483b-85eb-bced04ba598c"
26}
Warm Start in CLI#
To use a previous solution as the initial/warm start solution for a new request ID, you are required to save the previous solution, which can be accomplished use option -k
. Use the previous reqId in the next request as follows:
Note
Warm start is only applicable to LP and not for MILP.
# Please update these values if the server is running on a different IP address or port
export ip="localhost"
export port=5000
reqId=$(cuopt_sh -t LP data.json -i $ip -p $port -k | sed "s/'/\"/g" | sed 's/False/false/g' | jq -r '.reqId')
cuopt_sh data.json -t LP -i $ip -p $port -wid $reqId
In case the user needs to update solver settings through CLI, the option -ss
can be used as follows:
# Please update these values 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 '{"tolerances": {"optimality": 0.0001}, "time_limit": 5}'
In the case of batch mode, you can send a bunch of mps
files at once, and acquire results. The batch mode works only for mps
in the case of CLI:
Note
Batch mode is not available for MILP problems.
echo "* optimize
* cost = -0.2 * VAR1 + 0.1 * VAR2
* subject to
* 3 * VAR1 + 4 * VAR2 <= 5.4
* 2.7 * VAR1 + 10.1 * VAR2 <= 4.9
NAME good-1
ROWS
N COST
L ROW1
L ROW2
COLUMNS
VAR1 COST -0.2
VAR1 ROW1 3 ROW2 2.7
VAR2 COST 0.1
VAR2 ROW1 4 ROW2 10.1
RHS
RHS1 ROW1 5.4 ROW2 4.9
ENDATA" > sample.mps
# Please update these values if the server is running on a different IP address or port
export ip="localhost"
export port=5000
cuopt_sh sample.mps sample.mps sample.mps -t LP -i $ip -p $port -ss '{"tolerances": {"optimality": 0.0001}, "time_limit": 5}'
Aborting a Running Job In CLI#
Please refer to the Aborting a Running Job In CLI in the MILP Example for more details.
Note
Please use solver settings while using .mps files.