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