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.