Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
model_parameters.py
Go to the documentation of this file.
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"""Model specific solver configuration (e.g. starting basis)."""
16import dataclasses
17import datetime
18from typing import Dict, List, Optional, Set
19
20from ortools.math_opt import model_parameters_pb2
21from ortools.math_opt.python import linear_constraints
22from ortools.math_opt.python import model
23from ortools.math_opt.python import objectives
24from ortools.math_opt.python import solution
25from ortools.math_opt.python import sparse_containers
26from ortools.math_opt.python import variables
27
28
29@dataclasses.dataclass
31 """A suggested starting solution for the solver.
32
33 MIP solvers generally only want primal information (`variable_values`),
34 while LP solvers want both primal and dual information (`dual_values`).
35
36 Many MIP solvers can work with: (1) partial solutions that do not specify all
37 variables or (2) infeasible solutions. In these cases, solvers typically solve
38 a sub-MIP to complete/correct the hint.
39
40 How the hint is used by the solver, if at all, is highly dependent on the
41 solver, the problem type, and the algorithm used. The most reliable way to
42 ensure your hint has an effect is to read the underlying solvers logs with
43 and without the hint.
44
45 Simplex-based LP solvers typically prefer an initial basis to a solution
46 hint (they need to crossover to convert the hint to a basic feasible
47 solution otherwise).
48
49 Floating point values should be finite and not NaN, they are validated by
50 MathOpt at Solve() time (resulting in an exception).
51
52 Attributes:
53 variable_values: a potentially partial assignment from the model's primal
54 variables to finite (and not NaN) double values.
55 dual_values: a potentially partial assignment from the model's linear
56 constraints to finite (and not NaN) double values.
57 """
58
59 variable_values: Dict[variables.Variable, float] = dataclasses.field(
60 default_factory=dict
61 )
62 dual_values: Dict[linear_constraints.LinearConstraint, float] = dataclasses.field(
63 default_factory=dict
64 )
65
66 def to_proto(self) -> model_parameters_pb2.SolutionHintProto:
67 """Returns an equivalent protocol buffer to this."""
68 return model_parameters_pb2.SolutionHintProto(
69 variable_values=sparse_containers.to_sparse_double_vector_proto(
71 ),
72 dual_values=sparse_containers.to_sparse_double_vector_proto(
73 self.dual_values
74 ),
75 )
76
77
79 hint_proto: model_parameters_pb2.SolutionHintProto, mod: model.Model
80) -> SolutionHint:
81 """Returns an equivalent SolutionHint to `hint_proto`.
82
83 Args:
84 hint_proto: The solution, as encoded by the ids of the variables and
85 constraints.
86 mod: A MathOpt Model that must contain variables and linear constraints with
87 the ids from hint_proto.
88
89 Returns:
90 A SolutionHint equivalent.
91
92 Raises:
93 ValueError if hint_proto is invalid or refers to variables or constraints
94 not in mod.
95 """
96 return SolutionHint(
97 variable_values=sparse_containers.parse_variable_map(
98 hint_proto.variable_values, mod
99 ),
100 dual_values=sparse_containers.parse_linear_constraint_map(
101 hint_proto.dual_values, mod
102 ),
103 )
104
105
106@dataclasses.dataclass
108 """Parameters for an individual objective in a multi-objective model.
109
110 This class mirrors (and can generate) the related proto
111 model_parameters_pb2.ObjectiveParametersProto.
112
113 Attributes:
114 objective_degradation_absolute_tolerance: Optional objective degradation
115 absolute tolerance. For a hierarchical multi-objective solver, each
116 objective fⁱ is processed in priority order: the solver determines the
117 optimal objective value Γⁱ, if it exists, subject to all constraints in
118 the model and the additional constraints that fᵏ(x) = Γᵏ (within
119 tolerances) for each k < i. If set, a solution is considered to be "within
120 tolerances" for this objective fᵏ if |fᵏ(x) - Γᵏ| ≤
121 `objective_degradation_absolute_tolerance`. See also
122 `objective_degradation_relative_tolerance`; if both parameters are set for
123 a given objective, the solver need only satisfy one to be considered
124 "within tolerances". If not None, must be nonnegative.
125 objective_degradation_relative_tolerance: Optional objective degradation
126 relative tolerance. For a hierarchical multi-objective solver, each
127 objective fⁱ is processed in priority order: the solver determines the
128 optimal objective value Γⁱ, if it exists, subject to all constraints in
129 the model and the additional constraints that fᵏ(x) = Γᵏ (within
130 tolerances) for each k < i. If set, a solution is considered to be "within
131 tolerances" for this objective fᵏ if |fᵏ(x) - Γᵏ| ≤
132 `objective_degradation_relative_tolerance` * |Γᵏ|. See also
133 `objective_degradation_absolute_tolerance`; if both parameters are set for
134 a given objective, the solver need only satisfy one to be considered
135 "within tolerances". If not None, must be nonnegative.
136 time_limit: The maximum time a solver should spend on optimizing this
137 particular objective (or infinite if not set). Note that this does not
138 supersede the global time limit in SolveParameters.time_limit; both will
139 be enforced when set. This value is not a hard limit, solve time may
140 slightly exceed this value.
141 """
142
143 objective_degradation_absolute_tolerance: Optional[float] = None
144 objective_degradation_relative_tolerance: Optional[float] = None
145 time_limit: Optional[datetime.timedelta] = None
146
147 def to_proto(self) -> model_parameters_pb2.ObjectiveParametersProto:
148 """Returns an equivalent protocol buffer."""
149 result = model_parameters_pb2.ObjectiveParametersProto()
151 result.objective_degradation_absolute_tolerance = (
153 )
155 result.objective_degradation_relative_tolerance = (
157 )
158 if self.time_limit is not None:
159 result.time_limit.FromTimedelta(self.time_limit)
160 return result
161
162
164 proto: model_parameters_pb2.ObjectiveParametersProto,
165) -> ObjectiveParameters:
166 """Returns an equivalent ObjectiveParameters to the input proto."""
167 result = ObjectiveParameters()
168 if proto.HasField("objective_degradation_absolute_tolerance"):
169 result.objective_degradation_absolute_tolerance = (
170 proto.objective_degradation_absolute_tolerance
171 )
172 if proto.HasField("objective_degradation_relative_tolerance"):
173 result.objective_degradation_relative_tolerance = (
174 proto.objective_degradation_relative_tolerance
175 )
176 if proto.HasField("time_limit"):
177 result.time_limit = proto.time_limit.ToTimedelta()
178 return result
179
180
181@dataclasses.dataclass
183 """Model specific solver configuration, for example, an initial basis.
184
185 This class mirrors (and can generate) the related proto
186 model_parameters_pb2.ModelSolveParametersProto.
187
188 Attributes:
189 variable_values_filter: Only return solution and primal ray values for
190 variables accepted by this filter (default accepts all variables).
191 dual_values_filter: Only return dual variable values and dual ray values for
192 linear constraints accepted by this filter (default accepts all linear
193 constraints).
194 quadratic_dual_values_filter: Only return quadratic constraint dual values
195 accepted by this filter (default accepts all quadratic constraints).
196 reduced_costs_filter: Only return reduced cost and dual ray values for
197 variables accepted by this filter (default accepts all variables).
198 initial_basis: If set, provides a warm start for simplex based solvers.
199 solution_hints: Optional solution hints. If the underlying solver only
200 accepts a single hint, the first hint is used.
201 branching_priorities: Optional branching priorities. Variables with higher
202 values will be branched on first. Variables for which priorities are not
203 set get the solver's default priority (usually zero).
204 objective_parameters: Optional per objective parameters used only only for
205 multi-objective models.
206 lazy_linear_constraints: Optional lazy constraint annotations. Included
207 linear constraints will be marked as "lazy" with supporting solvers,
208 meaning that they will only be added to the working model as-needed as the
209 solver runs. Note that this an algorithmic hint that does not affect the
210 model's feasible region; solvers not supporting these annotations will
211 simply ignore it.
212 """
213
214 variable_values_filter: sparse_containers.VariableFilter = (
215 sparse_containers.VariableFilter()
216 )
217 dual_values_filter: sparse_containers.LinearConstraintFilter = (
218 sparse_containers.LinearConstraintFilter()
219 )
220 quadratic_dual_values_filter: sparse_containers.QuadraticConstraintFilter = (
221 sparse_containers.QuadraticConstraintFilter()
222 )
223 reduced_costs_filter: sparse_containers.VariableFilter = (
224 sparse_containers.VariableFilter()
225 )
226 initial_basis: Optional[solution.Basis] = None
227 solution_hints: List[SolutionHint] = dataclasses.field(default_factory=list)
228 branching_priorities: Dict[variables.Variable, int] = dataclasses.field(
229 default_factory=dict
230 )
231 objective_parameters: Dict[objectives.Objective, ObjectiveParameters] = (
232 dataclasses.field(default_factory=dict)
233 )
234 lazy_linear_constraints: Set[linear_constraints.LinearConstraint] = (
235 dataclasses.field(default_factory=set)
236 )
237
238 def to_proto(self) -> model_parameters_pb2.ModelSolveParametersProto:
239 """Returns an equivalent protocol buffer."""
240 # TODO(b/236289022): these methods should check that the variables are from
241 # the correct model.
242 result = model_parameters_pb2.ModelSolveParametersProto(
243 variable_values_filter=self.variable_values_filter.to_proto(),
244 dual_values_filter=self.dual_values_filter.to_proto(),
245 quadratic_dual_values_filter=self.quadratic_dual_values_filter.to_proto(),
246 reduced_costs_filter=self.reduced_costs_filter.to_proto(),
247 branching_priorities=sparse_containers.to_sparse_int32_vector_proto(
249 ),
250 )
251 if self.initial_basis:
252 result.initial_basis.CopyFrom(self.initial_basis.to_proto())
253 for hint in self.solution_hints:
254 result.solution_hints.append(hint.to_proto())
255 for obj, params in self.objective_parameters.items():
256 if isinstance(obj, objectives.AuxiliaryObjective):
257 result.auxiliary_objective_parameters[obj.id].CopyFrom(
258 params.to_proto()
259 )
260 else:
261 result.primary_objective_parameters.CopyFrom(params.to_proto())
262 result.lazy_linear_constraint_ids[:] = sorted(
263 con.id for con in self.lazy_linear_constraints
264 )
265 return result
model_parameters_pb2.ModelSolveParametersProto to_proto(self)
model_parameters_pb2.ObjectiveParametersProto to_proto(self)
model_parameters_pb2.SolutionHintProto to_proto(self)
ObjectiveParameters parse_objective_parameters(model_parameters_pb2.ObjectiveParametersProto proto)
SolutionHint parse_solution_hint(model_parameters_pb2.SolutionHintProto hint_proto, model.Model mod)