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