cuOpt Self-Hosted Thin Client API Example#
Routing Example#
1from cuopt_sh_client import CuOptServiceSelfHostClient
2import json
3import time
4
5data = {"cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}},
6 "task_data": {"task_locations": [0,1]},
7 "fleet_data": {"vehicle_locations": [[0,0],[0,0]]}}
8
9# If cuOpt is not running on localhost:5000, edit ip and port parameters
10cuopt_service_client = CuOptServiceSelfHostClient(
11 ip="localhost",
12 port=5000,
13 polling_timeout=25,
14 timeout_exception=False
15)
16
17def repoll(solution, repoll_tries):
18 # If solver is still busy solving, the job will be assigned a request id and response is sent back in the
19 # following format {"reqId": <REQUEST-ID>}.
20 # Solver needs to be re-polled for response using this <REQUEST-ID>.
21
22 if "reqId" in solution and "response" not in solution:
23 req_id = solution["reqId"]
24 for i in range(repoll_tries):
25 solution = cuopt_service_client.repoll(req_id, response_type="dict")
26 if "reqId" in solution and "response" in solution:
27 break;
28
29 # Sleep for a second before requesting
30 time.sleep(1)
31
32 return solution
33
34solution = cuopt_service_client.get_optimized_routes(data)
35
36# Number of repoll requests to be carried out for a successful response
37repoll_tries = 500
38
39solution = repoll(solution, repoll_tries)
40
41print(json.dumps(solution, indent=4))
response would be as follows:
1{ 2"response": { 3 "solver_response": { 4 "status": 0, 5 "num_vehicles": 1, 6 "solution_cost": 2.0, 7 "objective_values": { 8 "cost": 2.0 9 }, 10 "vehicle_data": { 11 "0": { 12 "task_id": [ 13 "Depot", 14 "0", 15 "1", 16 "Depot" 17 ], 18 "arrival_stamp": [ 19 0.0, 20 0.0, 21 0.0, 22 0.0 23 ], 24 "type": [ 25 "Depot", 26 "Delivery", 27 "Delivery", 28 "Depot" 29 ], 30 "route": [ 31 0, 32 0, 33 1, 34 0 35 ] 36 } 37 }, 38 "initial_solutions": [], 39 "dropped_tasks": { 40 "task_id": [], 41 "task_index": [] 42 } 43 }, 44 "total_solve_time": 0.1120915412902832 45}, 46"reqId": "ebd378a3-c02a-47f3-b0a1-adec81be7cdd" 47}
Initial Solution#
Previously run solutions or uploaded solutions can be used as the initial solution for new requests using previously run reqIds as follows:
1from cuopt_sh_client import CuOptServiceSelfHostClient
2import json
3import time
4
5data = {"cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}},
6 "task_data": {"task_locations": [0,1]},
7 "fleet_data": {"vehicle_locations": [[0,0],[0,0]]}}
8
9# If cuOpt is not running on localhost:5000, edit ip and port parameters
10cuopt_service_client = CuOptServiceSelfHostClient(
11 ip="localhost",
12 port=5000,
13 timeout_exception=False
14)
15
16# Get initial solution
17# Set delete_solution to false so it can be used in next request
18initial_solution = cuopt_service_client.get_optimized_routes(
19 data, delete_solution=False
20)
21
22
23# Upload a solution returned/saved from previous request as initial solution
24initial_solution_3 = cuopt_service_client.upload_solution(initial_solution)
25
26# Use previous solution saved in server as initial solution to this request.
27# That solution is referenced with previous request id.
28solution = cuopt_service_client.get_optimized_routes(
29 data,
30 initial_ids=[
31 initial_solution["reqId"],
32 initial_solution_3["reqId"]
33 ]
34)
35
36print(json.dumps(solution, indent=4))
37
38# Delete saved solution if not required to save space
39cuopt_service_client.delete(initial_solution["reqId"])
40cuopt_service_client.delete(initial_solution_3["reqId"])
41
42# Another option is to add a solution that was generated
43# to data model option as follows
44initial_solution_2 = [
45 {
46 "0": {
47 "task_id": ["Depot", "0", "1", "Depot"],
48 "type": ["Depot", "Delivery", "Delivery", "Depot"]
49 }
50 }
51]
52
53data["initial_solution"] = initial_solution_2
54solution = cuopt_service_client.get_optimized_routes(data)
55
56print(json.dumps(solution, indent=4))
The initial solution in the response is not accepted
, because the problem is too small, and the optimal solution is found even before cuOpt could use an initial solution.
The response would be as follows:
1{ 2"response": { 3 "solver_response": { 4 "status": 0, 5 "num_vehicles": 1, 6 "solution_cost": 2.0, 7 "objective_values": { 8 "cost": 2.0 9 }, 10 "vehicle_data": { 11 "0": { 12 "task_id": [ 13 "Depot", 14 "0", 15 "1", 16 "Depot" 17 ], 18 "arrival_stamp": [ 19 0.0, 20 0.0, 21 0.0, 22 0.0 23 ], 24 "type": [ 25 "Depot", 26 "Delivery", 27 "Delivery", 28 "Depot" 29 ], 30 "route": [ 31 0, 32 0, 33 1, 34 0 35 ] 36 } 37 }, 38 "initial_solutions": [ 39 "not accepted", 40 ], 41 "dropped_tasks": { 42 "task_id": [], 43 "task_index": [] 44 } 45 }, 46 "total_solve_time": 0.06160402297973633 47}, 48"reqId": "ebd378a3-c02a-47f3-b0a1-adec81be7cdd" 49}
The data
argument to get_optimized_routes
may be a dictionary of the format shown in Get Routes Open-API spec.
It may also be the path of a file containing such a dictionary as JSON or written using the Python msgpack module (pickle is deprecated).
A JSON file may optionally be compressed with zlib. More details on the response can be found under responses schema in open-api spec or same can be found in redoc as well.
To enable HTTPS:
In the case of the server using public certificates, simply enable https.
1from cuopt_sh_client import CuOptServiceSelfHostClient 2 3data = {"cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}}, 4 "task_data": {"task_locations": [0,1]}, 5 "fleet_data": {"vehicle_locations": [[0,0],[0,0]]}} 6 7# If cuOpt is not running on localhost:5000, edit ip and port parameters 8cuopt_service_client = CuOptServiceSelfHostClient( 9 ip="localhost", 10 port=5000, 11 use_https=True 12)
In the case of a self-signed certificate, provide the complete path to the certificate.
1from cuopt_sh_client import CuOptServiceSelfHostClient 2 3data = {"cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}}, 4 "task_data": {"task_locations": [0,1]}, 5 "fleet_data": {"vehicle_locations": [[0,0],[0,0]]}} 6 7# If cuOpt is not running on localhost:5000, edit ip and port parameters 8cuopt_service_client = CuOptServiceSelfHostClient( 9 ip="localhost", 10 port=5000, 11 use_https=True, 12 self_signed_cert=/complete/path/to/certificate 13)
You can generate self signed certificate easily as follows,
openssl genrsa -out ca.key 2048 openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ca.crt openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=*.example.com" -out server.csr openssl x509 -req -extfile <(printf "subjectAltName=DNS:example.com,DNS:www.example.com") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
server.crt
andserver.key
are meant for server,ca.crt
is meant for client.
LP Example#
Note
Linear Programming (LP) and Mixed Integer Linear Programming (MILP) is an Early Access feature and is currently open to only select customers.
Note
LP: Solver returns as soon as optimality is achieved and does not wait for the time limit to expire. MILP: Solver keeps trying other paths to find a better solution since it is heuristic-based until the time limit expires.
1from cuopt_sh_client import CuOptServiceSelfHostClient
2import json
3import time
4
5data = {
6 "csr_constraint_matrix": {
7 "offsets": [0, 2, 4],
8 "indices": [0, 1, 0, 1],
9 "values": [3.0, 4.0, 2.7, 10.1]
10 },
11 "constraint_bounds": {
12 "upper_bounds": [5.4, 4.9],
13 "lower_bounds": ["ninf", "ninf"]
14 },
15 "objective_data": {
16 "coefficients": [0.2, 0.1],
17 "scalability_factor": 1.0,
18 "offset": 0.0
19 },
20 "variable_bounds": {
21 "upper_bounds": ["inf", "inf"],
22 "lower_bounds": [0.0, 0.0]
23 },
24 "maximize": False,
25 "solver_config": {
26 "tolerances": {
27 "optimality": 0.0001
28 }
29 }
30}
31
32# If cuOpt is not running on localhost:5000, edit ip and port parameters
33cuopt_service_client = CuOptServiceSelfHostClient(
34 ip="localhost",
35 port=5000,
36 polling_timeout=25,
37 timeout_exception=False
38)
39
40# Number of repoll requests to be carried out for a successful response
41repoll_tries = 500
42
43def repoll(solution, repoll_tries):
44 # If solver is still busy solving, the job will be assigned a request id and response is sent back in the
45 # following format {"reqId": <REQUEST-ID>}.
46 # Solver needs to be re-polled for response using this <REQUEST-ID>.
47
48 if "reqId" in solution and "response" not in solution:
49 req_id = solution["reqId"]
50 for i in range(repoll_tries):
51 solution = cuopt_service_client.repoll(req_id, response_type="dict")
52 if "reqId" in solution and "response" in solution:
53 break;
54
55 # Sleep for a second before requesting
56 time.sleep(1)
57
58 return solution
59
60# Logging callback
61def log_callback(log):
62 for i in log:
63 print("server-log: ", log)
64
65solution = cuopt_service_client.get_LP_solve(
66 data, response_type="dict", logging_callback=log_callback
67)
68
69solution = repoll(solution, repoll_tries)
70
71print("---------- Normal mode --------------- \n", json.dumps(solution, indent=4))
72
73# For batch mode send list of mps/dict/DataModel
74
75solution = cuopt_service_client.get_LP_solve(
76 [data, data], response_type="dict", logging_callback=log_callback
77)
78solution = repoll(solution, repoll_tries)
79
80print("---------- Batch mode ----------------- \n", json.dumps(solution, indent=4))
response would be as follows:
Normal mode response:
1{
2"response": {
3 "solver_response": {
4 "status": 1,
5 "solution": {
6 "problem_category": 0,
7 "primal_solution": [
8 0.0,
9 0.0
10 ],
11 "dual_solution": [
12 0.0,
13 0.0
14 ],
15 "primal_objective": 0.0,
16 "dual_objective": 0.0,
17 "solver_time": 15.0,
18 "vars": {},
19 "lp_statistics": {
20 "primal_residual": 0.0,
21 "dual_residual": 0.0,
22 "gap": 0.0,
23 "reduced_cost": [
24 0.2,
25 0.1
26 ]
27 },
28 "milp_statistics": {}
29 }
30 },
31 "total_solve_time": 0.00931859016418457
32},
33"reqId": "1ac3db65-c046-4118-96dd-386de5a4b9e6"
34}
Batch mode response:
1{
2"response": {
3 "solver_response": [
4 {
5 "status": 1,
6 "solution": {
7 "problem_category": 0,
8 "primal_solution": [
9 0.0,
10 0.0
11 ],
12 "dual_solution": [
13 0.0,
14 0.0
15 ],
16 "primal_objective": 0.0,
17 "dual_objective": 0.0,
18 "solver_time": 4.0,
19 "vars": {},
20 "lp_statistics": {
21 "primal_residual": 0.0,
22 "dual_residual": 0.0,
23 "gap": 0.0,
24 "reduced_cost": [
25 0.2,
26 0.1
27 ]
28 },
29 "milp_statistics": {}
30 }
31 },
32 {
33 "status": 1,
34 "solution": {
35 "problem_category": 0,
36 "primal_solution": [
37 0.0,
38 0.0
39 ],
40 "dual_solution": [
41 0.0,
42 0.0
43 ],
44 "primal_objective": 0.0,
45 "dual_objective": 0.0,
46 "solver_time": 6.0,
47 "vars": {},
48 "lp_statistics": {
49 "primal_residual": 0.0,
50 "dual_residual": 0.0,
51 "gap": 0.0,
52 "reduced_cost": [
53 0.2,
54 0.1
55 ]
56 },
57 "milp_statistics": {}
58 }
59 }
60 ],
61 "total_solve_time": 12.0
62},
63"reqId": "be428d9f-00f3-4001-b3b0-4850889a80e6"
64}
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
3import time
4
5data = {
6 "csr_constraint_matrix": {
7 "offsets": [0, 2, 4],
8 "indices": [0, 1, 0, 1],
9 "values": [3.0, 4.0, 2.7, 10.1]
10 },
11 "constraint_bounds": {
12 "upper_bounds": [5.4, 4.9],
13 "lower_bounds": ["ninf", "ninf"]
14 },
15 "objective_data": {
16 "coefficients": [0.2, 0.1],
17 "scalability_factor": 1.0,
18 "offset": 0.0
19 },
20 "variable_bounds": {
21 "upper_bounds": ["inf", "inf"],
22 "lower_bounds": [0.0, 0.0]
23 },
24 "maximize": False,
25 "solver_config": {
26 "tolerances": {
27 "optimality": 0.0001
28 }
29 }
30}
31
32# If cuOpt is not running on localhost:5000, edit ip and port parameters
33cuopt_service_client = CuOptServiceSelfHostClient(
34 ip="localhost",
35 port=5000,
36 timeout_exception=False
37)
38
39# Set delete_solution to false so it can be used in next request
40initial_solution = cuopt_service_client.get_LP_solve(
41 data, delete_solution=False, response_type="dict"
42)
43
44# Use previous solution saved in server as initial solution to this request.
45# That solution is referenced with previous request id.
46solution = cuopt_service_client.get_LP_solve(
47 data, warmstart_id=initial_solution["reqId"], response_type="dict"
48)
49
50print(json.dumps(solution, indent=4))
51
52# Delete saved solution if not required to save space
53cuopt_service_client.delete(initial_solution["reqId"])
The response would be as follows:
1{
2"response": {
3 "solver_response": {
4 "status": 1,
5 "solution": {
6 "problem_category": 0,
7 "primal_solution": [
8 0.0,
9 0.0
10 ],
11 "dual_solution": [
12 0.0,
13 0.0
14 ],
15 "primal_objective": 0.0,
16 "dual_objective": 0.0,
17 "solver_time": 15.0,
18 "vars": {},
19 "lp_statistics": {
20 "primal_residual": 0.0,
21 "dual_residual": 0.0,
22 "gap": 0.0,
23 "reduced_cost": [
24 0.2,
25 0.1
26 ]
27 },
28 "milp_statistics": {}
29 }
30 },
31 "total_solve_time": 0.009014129638671875
32},
33"reqId": "1ac3db65-c046-4118-96dd-386de5a4b9e6"
34}
Using MPS file directly#
An example on using .mps file as input shown below:
1from cuopt_sh_client import CuOptServiceSelfHostClient
2import json
3import time
4from solver_settings import SolverSettings
5
6data = "sample.mps"
7
8mps_data = """* optimize
9* cost = 0.2 * VAR1 + 0.1 * VAR2
10* subject to
11* 3 * VAR1 + 4 * VAR2 <= 5.4
12* 2.7 * VAR1 + 10.1 * VAR2 <= 4.9
13NAME good-1
14ROWS
15 N COST
16 L ROW1
17 L ROW2
18COLUMNS
19 VAR1 COST 0.2
20 VAR1 ROW1 3 ROW2 2.7
21 VAR2 COST 0.1
22 VAR2 ROW1 4 ROW2 10.1
23RHS
24 RHS1 ROW1 5.4 ROW2 4.9
25ENDATA
26"""
27
28with open(data, "w") as file:
29 file.write(mps_data)
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
38ss = SolverSettings()
39
40ss.set_time_limit(5)
41ss.set_optimality_tolerance(0.00001)
42
43solution = cuopt_service_client.get_LP_solve(data, solver_config=ss, response_type="dict")
44
45print(json.dumps(solution, indent=4))
The response is:
1{
2"response": {
3 "solver_response": {
4 "status": 1,
5 "solution": {
6 "problem_category": 0,
7 "primal_solution": [
8 0.0,
9 0.0
10 ],
11 "dual_solution": [
12 0.0,
13 0.0
14 ],
15 "primal_objective": 0.0,
16 "dual_objective": 0.0,
17 "solver_time": 4.0,
18 "vars": {
19 "VAR1": 0.0,
20 "VAR2": 0.0
21 },
22 "lp_statistics": {
23 "primal_residual": 0.0,
24 "dual_residual": 0.0,
25 "gap": 0.0,
26 "reduced_cost": [
27 0.2,
28 0.1
29 ]
30 },
31 "milp_statistics": {}
32 }
33 },
34 "total_solve_time": 0.009944677352905273
35},
36"reqId": "bb966e6b-162f-492d-b168-76fcdbb10e69"
37}
Generate datamodel from mps parser#
Use datamodel generated from mps file as input, this yields solution object in response.
1from cuopt_sh_client import CuOptServiceSelfHostClient
2import cuopt_mps_parser
3import json
4import time
5from solver_settings import SolverSettings, SolverMode
6
7# -- Parse the MPS file --
8
9data = "sample.mps"
10
11mps_data = """* optimize
12* cost = 0.2 * VAR1 + 0.1 * VAR2
13* subject to
14* 3 * VAR1 + 4 * VAR2 <= 5.4
15* 2.7 * VAR1 + 10.1 * VAR2 <= 4.9
16NAME good-1
17ROWS
18 N COST
19 L ROW1
20 L ROW2
21COLUMNS
22 VAR1 COST 0.2
23 VAR1 ROW1 3 ROW2 2.7
24 VAR2 COST 0.1
25 VAR2 ROW1 4 ROW2 10.1
26RHS
27 RHS1 ROW1 5.4 ROW2 4.9
28ENDATA
29"""
30
31with open(data, "w") as file:
32 file.write(mps_data)
33
34# Parse the MPS file and measure the time spent
35parse_start = time.time()
36data_model = cuopt_mps_parser.ParseMps(data)
37parse_time = time.time() - parse_start
38
39# -- Build the client object --
40
41# If cuOpt is not running on localhost:5000, edit `ip` and `port` parameters
42cuopt_service_client = CuOptServiceSelfHostClient(
43 ip="localhost",
44 port=5000,
45 timeout_exception=False
46)
47
48# -- Set the solver settings --
49
50ss = SolverSettings()
51
52# Set the solver mode to the same of the blogpost, Fast1.
53# Stable1 could also be used.
54# For more details on solver_mode checkout `SolverSettings.set_solver_mode()`
55ss.set_solver_mode(SolverMode.Fast1)
56
57# Disable the infeasiblity detection since we know the instance is feasible
58# Checking infeasiblity adds to the total solve time.
59ss.set_infeasibility_detection(False)
60
61# Set the general tolerance to 1e-4 which is already the default value.
62# For more detail on optimality checkout `SolverSettings.set_optimality_tolerance()`
63ss.set_optimality_tolerance(1e-4)
64
65# Here you could set an iteration limit to 1000 and time limit to 10 seconds
66# By default there is no iteration limit and the max time limit is 10 minutes
67# Any problem taking more than 10 minutes to solve will stop and the current solution will be returned
68# For this example, no limit is set
69# settings.set_iteration_limit(1000)
70# settings.set_time_limit(10)
71ss.set_time_limit(5)
72
73# -- Call solve --
74
75network_time = time.time()
76solution = cuopt_service_client.get_LP_solve(data_model, ss)
77network_time = time.time() - network_time
78
79# -- Retrieve the solution object and print the details --
80
81solution_obj = solution["response"]["solver_response"]["solution"]
82
83# Check Termination Reason
84# For more detail on termination reasons: checkout `Solution.get_termination_reason()`
85print("Termination Reason: (1 is Optimal)")
86print(solution_obj.get_termination_reason())
87
88# Check found objective value
89print("Objective Value:")
90print(solution_obj.get_primal_objective())
91
92# Check the MPS parse time
93print(f"Mps Parse time: {parse_time:.3f} sec")
94
95# Check network time (client call - solve time)
96network_time = network_time - (solution_obj.get_solve_time() / 1000) # / 1000 because the solve time is in milliseconds
97print(f"Network time: {network_time:.3f} sec")
98
99# Check solver time
100solve_time = solution_obj.get_solve_time() / 1000 # / 1000 because the solve time is in milliseconds
101print(f"Engine Solve time: {solve_time:.3f} sec")
102
103# Check the total end to end time (mps parsing + network + solve time)
104end_to_end_time = parse_time + network_time + solve_time
105print(f"Total end to end time: {end_to_end_time:.3f} sec")
106
107# You could print the found decision variables
108# Since the problem might be very large, in this example it's commented
109#print("Variables Values:")
110#print(solution_obj.get_vars())
The response would be as follows,
1Termination Reason: (1 is Optimal)
2 1
3Objective Value:
40.0
5Mps Parse time: 0.000 sec
6Network time: 1.006 sec
7Engine Solve time: 0.003 sec
8Total end to end time: 1.009 sec
Example with DataModel is available in the LP example notebook
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 responses schema in open-api spec or same can be found in redoc as well.
MILP Example#
Note
Linear Programming (LP) and Mixed Integer Linear Programming (MILP) are Early Access features and are currently open to only select customers.
Note
LP: Solver returns as soon as optimality is achieved and does not wait for the time limit to expire. MILP: Solver keeps trying other paths to find a better solution since it is heuristic-based until the time limit expires.
The major difference between this example and the prior LP example is that some of the variables are integers, so variable_types
need to be shared.
1from cuopt_sh_client import CuOptServiceSelfHostClient
2import json
3import time
4
5data = {
6 "csr_constraint_matrix": {
7 "offsets": [0, 2],
8 "indices": [0, 1],
9 "values": [1.0, 1.0]
10 },
11 "constraint_bounds": {
12 "upper_bounds": [5000.0],
13 "lower_bounds": [0.0]
14 },
15 "objective_data": {
16 "coefficients": [1.2, 1.7],
17 "scalability_factor": 1.0,
18 "offset": 0.0
19 },
20 "variable_bounds": {
21 "upper_bounds": [3000.0, 5000.0],
22 "lower_bounds": [0.0, 0.0]
23 },
24 "maximize": True,
25 "variable_names": ["x", "y"],
26 "variable_types": ["I", "I"],
27 "solver_config":{
28 "time_limit": 30
29 }
30}
31
32# If cuOpt is not running on localhost:5000, edit ip and port parameters
33cuopt_service_client = CuOptServiceSelfHostClient(
34 ip="localhost",
35 port=5000,
36 polling_timeout=25,
37 timeout_exception=False
38)
39
40def repoll(solution, repoll_tries):
41 # If solver is still busy solving, the job will be assigned a request id and response is sent back in the
42 # following format {"reqId": <REQUEST-ID>}.
43 # Solver needs to be re-polled for response using this <REQUEST-ID>.
44
45 if "reqId" in solution and "response" not in solution:
46 req_id = solution["reqId"]
47 for i in range(repoll_tries):
48 solution = cuopt_service_client.repoll(req_id, response_type="dict")
49 if "reqId" in solution and "response" in solution:
50 break;
51
52 # Sleep for a second before requesting
53 time.sleep(1)
54
55 return solution
56
57solution = cuopt_service_client.get_LP_solve(data, response_type="dict")
58
59# Number of repoll requests to be carried out for a successful response
60repoll_tries = 500
61
62solution = repoll(solution, repoll_tries)
63
64print(json.dumps(solution, indent=4))
response would be as follows:
1{
2"response": {
3 "solver_response": {
4 "status": 2,
5 "solution": {
6 "problem_category": 1,
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": 30.003654432,
15 "vars": {
16 "x": 0.0,
17 "y": 5000.0
18 },
19 "lp_statistics": {
20 "reduced_cost": null
21 },
22 "milp_statistics": {
23 "mip_gap": -6.93265581705675e-310,
24 "solution_bound": "-Infinity",
25 "presolve_time": 0.004038293,
26 "max_constraint_violation": 0.0,
27 "max_int_violation": 0.0,
28 "max_variable_bound_violation": 0.0
29 }
30 }
31 },
32 "total_solve_time": 30.054951906204224
33},
34"reqId": "b8b0ee19-672c-4120-b8f6-600eceb70d89"
35}
Incumbent Solution#
Incumbent solution can be retrieved using a callback function as follows:
Note
Incumbent solution callback is only applicable to MILP.
1from cuopt_sh_client import CuOptServiceSelfHostClient
2import json
3import time
4
5data = {
6 "csr_constraint_matrix": {
7 "offsets": [0, 2],
8 "indices": [0, 1],
9 "values": [1.0, 1.0]
10 },
11 "constraint_bounds": {
12 "upper_bounds": [5000.0],
13 "lower_bounds": [0.0]
14 },
15 "objective_data": {
16 "coefficients": [1.2, 1.7],
17 "scalability_factor": 1.0,
18 "offset": 0.0
19 },
20 "variable_bounds": {
21 "upper_bounds": [3000.0, 5000.0],
22 "lower_bounds": [0.0, 0.0]
23 },
24 "maximize": True,
25 "variable_names": ["x", "y"],
26 "variable_types": ["I", "I"],
27 "solver_config":{
28 "time_limit": 30
29 }
30}
31
32# If cuOpt is not running on localhost:5000, edit ip and port parameters
33cuopt_service_client = CuOptServiceSelfHostClient(
34 ip="localhost",
35 port=5000,
36 timeout_exception=False
37)
38
39# callback should accept 2 values, one is solution and another is cost
40def callback(solution, solution_cost):
41 print(f"Solution : {solution} cost : {solution_cost}\n")
42
43# Logging callback
44def log_callback(log):
45 for i in log:
46 print("server-log: ", i)
47
48solution = cuopt_service_client.get_LP_solve(
49 data, incumbent_callback=callback, response_type="dict", logging_callback=log_callback
50)
51
52print(json.dumps(solution, indent=4))
Log callback response:
1server-log: [2025-02-21 12:01:48.028] [CUOPT] [info] Running presolve!
2server-log: [2025-02-21 12:01:48.032] [CUOPT] [info] Solving LP root relaxation
3server-log: [2025-02-21 12:01:48.032] [CUOPT] [info] Scaling matrix. Maximum column norm 1.000000e+00
4.....
Incumbent callback response :
1 Solution : [0.0, 0.0] cost : 0.0
2
3 Solution : [940.0, 0.0] cost : 1128.0
4
5 Solution : [2472.0, 2527.0] cost : 7262.299999999999
1{
2"response": {
3 "solver_response": {
4 "status": 2,
5 "solution": {
6 "problem_category": 1,
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": 30.003654432,
15 "vars": {
16 "x": 0.0,
17 "y": 5000.0
18 },
19 "lp_statistics": {
20 "reduced_cost": null
21 },
22 "milp_statistics": {
23 "mip_gap": -6.93265581705675e-310,
24 "solution_bound": "-Infinity",
25 "presolve_time": 0.004038293,
26 "max_constraint_violation": 0.0,
27 "max_int_violation": 0.0,
28 "max_variable_bound_violation": 0.0
29 }
30 }
31 },
32 "total_solve_time": 30.03960680961609
33},
34"reqId": "b8b0ee19-672c-4120-b8f6-600eceb70d89"
35}
An example with DataModel is available in the MILP example notebook.
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 responses schema in open-api spec or same can be found in redoc as well.
The can be of different format as well, please check the documentation.
Aborting a Running Job In Thin Client#
Note
This is only supported in self-hosted
1from cuopt_sh_client import CuOptServiceSelfHostClient
2
3# This is an UUID that is returned by the solver while the solver is trying to find solution so users can come back and check the status or query for results.
4job_uuid = "<UUID_THAT_WE_GOT>"
5
6# If cuOpt is not running on localhost:5000, edit ip and port parameters
7 cuopt_service_client = CuOptServiceSelfHostClient(
8 ip="localhost",
9 port=5000
10 )
11
12 # Delete the job if it is still queued or running
13 response = cuopt_service_client.delete(uuid, running=True, queued=True, cached=False)
14
15 print(response.json())
cuOpt Self-Hosted Thin Client CLI Example#
Create a data.json
file containing this sample data:
Routing Example#
echo '{"cost_matrix_data": {"data": {"0": [[0, 1], [1, 0]]}},
"task_data": {"task_locations": [0, 1]},
"fleet_data": {"vehicle_locations": [[0, 0], [0, 0]]}}' > data.json
Invoke the CLI.
# client's default ip address for cuOpt is localhost:5000 if ip/port are not specified
cuopt_sh data.json -i ip -p port
Initial Solution in CLI#
To use a previous solution as an initial 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:
# client's default ip address for cuOpt is localhost:5000 if ip/port are not specified
reqId=$(cuopt_sh data.json -i ip -p port -k | sed "s/'/\"/g" | jq -r '.reqId')
cuopt_sh data.json -i ip -p port -id $reqId $reqID
# delete previous saved solutions using follwing command
cuopt_sh -d $reqId
Uploading a solution#
Users can also upload a solution which might have been saved for later runs.
# Save solution to a file
cuopt_sh data.json -i ip -p port | sed "s/'/\"/g" > solution.json
# Upload the solution and get request-id generated for that
reqId=$(cuopt_sh solution.json -us -i ip -p port | sed "s/'/\"/g" | jq -r '.reqId')
# Use this request id for initial solution
cuopt_sh data.json -i ip -p port -id $reqId
# delete previous saved solutions using follwing command
cuopt_sh -d $reqId
To enable HTTPS#
In the case of the server using public certificates, simply enable https.
cuopt_sh data.json -s -i ip -p port
In the case of a self-signed certificate, provide the complete path to the certificate.
cuopt_sh data.json -s -c /complete/path/to/certificate -i ip -p port
LP Example#
Note
Linear Programming (LP) and Mixed Integer Linear Programming (MILP) are Early Access features and are currently open to only select customers.
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.
cuopt_sh data.json -t LP -i ip -p port -sl
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.
# client's default ip address for cuOpt is localhost:5000 if ip/port are not specified
reqId=$(cuopt_sh -t LP data.json -i ip -p port -k | sed "s/'/\"/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:
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
cuopt_sh sample.mps sample.mps sample.mps -t LP -i ip -p port -ss '{"tolerances": {"optimality": 0.0001}, "time_limit": 5}'
Note
Please use solver settings while using .mps files.
MILP Example#
The only difference between this example and the prior LP example would be the variable types provided in data.
Note
Linear Programming (LP) and Mixed Integer Linear Programming (MILP) are Early Access features and are currently open to only select customers.
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:
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:
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#
Note
This is only supported in self-hosted
UUID that is returned by the solver while the solver is trying to find 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.
cuopt_sh -d -r -q <UUID>