Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
remote_http_solve.py
Go to the documentation of this file.
1# Copyright 2010-2024 Google LLC
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7#
8# Unless required by applicable law or agreed to in writing, software
9# distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
13
14"""Solve MathOpt models via HTTP request to the OR API."""
15
16import json
17from typing import List, Optional, Tuple
18from google.protobuf import json_format
19import requests
20from ortools.service.v1 import optimization_pb2
21from ortools.math_opt import rpc_pb2
22from ortools.math_opt.python import mathopt
23from ortools.math_opt.python.ipc import proto_converter
24
25_DEFAULT_DEADLINE_SEC = 10
26_DEFAULT_ENDPOINT = "https://optimization.googleapis.com/v1/mathopt:solveMathOptModel"
27_RELATIVE_TIME_BUFFER = 0.05
28
29
30class OptimizationServiceError(Exception):
31 """Error produced when solving a MathOpt model via HTTP request."""
32
33
34def remote_http_solve(
35 model: mathopt.Model,
36 solver_type: mathopt.SolverType,
37 params: Optional[mathopt.SolveParameters] = None,
38 model_params: Optional[mathopt.ModelSolveParameters] = None,
39 endpoint: Optional[str] = _DEFAULT_ENDPOINT,
40 api_key: Optional[str] = None,
41 deadline_sec: Optional[float] = _DEFAULT_DEADLINE_SEC,
42) -> Tuple[mathopt.SolveResult, List[str]]:
43 """Solves a MathOpt model via HTTP request to the OR API.
44
45 Args:
46 model: The optimization model.
47 solver_type: The underlying solver to use.
48 params: Optional configuration of the underlying solver.
49 model_params: Optional configuration of the solver that is model specific.
50 endpoint: An URI identifying the service for remote solves.
51 api_key: Key to the OR API.
52 deadline_sec: The number of seconds before the request times out.
53
54 Returns:
55 A SolveResult containing the termination reason, solution(s) and stats.
56 A list of messages with the logs (if specified in the `params`).
57
58 Raises:
59 OptimizationServiceError: if an HTTP error is returned while solving a
60 model.
61 """
62 if api_key is None:
63 # TODO(b/306709279): Relax this when unauthenticated solves are allowed.
64 raise ValueError("api_key can't be None when solving remotely")
65
66 payload = _build_json_payload(model, solver_type, params, model_params)
67
68 session = create_optimization_service_session(api_key, deadline_sec)
69 response = session.post(
70 url=endpoint,
71 json=payload,
72 timeout=deadline_sec,
73 )
74
75 if not response.ok:
76 http_error = json.loads(response.content)["error"]
77 raise OptimizationServiceError(
78 f'status code {http_error["code"]}: {http_error["message"]}'
79 ) from None
80
81 return _build_solve_result(response.content, model)
82
83
85 api_key: str,
86 deadline_sec: float,
87) -> requests.Session:
88 """Creates a session with the appropriate headers.
89
90 This function sets headers for authentication via an API key, and it sets
91 deadlines set for the server and the connection.
92
93 Args:
94 api_key: Key to the OR API.
95 deadline_sec: The number of seconds before the request times out.
96
97 Returns:
98 requests.Session a session with the necessary headers to call the
99 optimization service.
100 """
101 session = requests.Session()
102 server_timeout = deadline_sec * (1 - _RELATIVE_TIME_BUFFER)
103 session.headers = {
104 "Content-Type": "application/json",
105 "Connection": "keep-alive",
106 "Keep-Alive": f"timeout={deadline_sec}, max=1",
107 "X-Server-Timeout": f"{server_timeout}",
108 "X-Goog-Api-Key": api_key,
109 }
110 return session
111
112
114 model: mathopt.Model,
115 solver_type: mathopt.SolverType,
116 params: Optional[mathopt.SolveParameters],
117 model_params: Optional[mathopt.ModelSolveParameters],
118):
119 """Builds a JSON payload.
120
121 Args:
122 model: The optimization model.
123 solver_type: The underlying solver to use.
124 params: Optional configuration of the underlying solver.
125 model_params: Optional configuration of the solver that is model specific.
126
127 Returns:
128 A JSON object with a MathOpt model and corresponding parameters.
129
130 Raises:
131 SerializationError: If building the OR API proto is not successful or
132 deserializing to JSON fails.
133 """
134 params = params or mathopt.SolveParameters()
135 model_params = model_params or mathopt.ModelSolveParameters()
136 try:
137 request = rpc_pb2.SolveRequest(
138 model=model.export_model(),
139 solver_type=solver_type.value,
140 parameters=params.to_proto(),
141 model_parameters=model_params.to_proto(),
142 )
143 api_request = proto_converter.convert_request(request)
144 except ValueError as err:
145 raise ValueError from err
146
147 return json.loads(json_format.MessageToJson(api_request))
148
149
151 json_response: bytes, model: mathopt.Model
152) -> Tuple[mathopt.SolveResult, List[str]]:
153 """Parses a JSON representation of a response to a SolveResult object.
154
155 Args:
156 json_response: bytes representing the `SolveMathOptModelResponse` in JSON
157 format
158 model: The optimization model that was solved
159
160 Returns:
161 A SolveResult of the model.
162 A list of messages with the logs.
163
164 Raises:
165 SerializationError: If parsing the json response fails or if converting the
166 OR API response to the internal MathOpt response fails.
167 """
168 try:
169 api_response = json_format.Parse(
170 json_response, optimization_pb2.SolveMathOptModelResponse()
171 )
172 except json_format.ParseError as json_err:
173 raise ValueError(
174 "API response is not a valid SolveMathOptModelResponse JSON"
175 ) from json_err
176
177 response = proto_converter.convert_response(api_response)
178 return mathopt.parse_solve_result(response.result, model), list(response.messages)
Tuple[mathopt.SolveResult, List[str]] _build_solve_result(bytes json_response, mathopt.Model model)
_build_json_payload(mathopt.Model model, mathopt.SolverType solver_type, Optional[mathopt.SolveParameters] params, Optional[mathopt.ModelSolveParameters] model_params)
requests.Session create_optimization_service_session(str api_key, float deadline_sec)