14"""An API for storing a MathOpt model and tracking model modifications."""
16from typing
import Optional, Protocol, Sequence
21from typing_extensions
import Self
29 """An API for building, modifying, and tracking changes to a MathOpt model.
31 On functions that return protocol buffers: These functions can fail for two
33 (1) The data is too large for proto's in memory representation. Specifically,
34 any repeated field can have at most 2^31 entries (~2 billion). So if your
35 model has this many nonzeros in the constraint matrix, we cannot build a
36 proto for it (we can potentially export to a text format still).
37 (2) The particular combination of Elemental and Proto you are using must
38 serialize your message (typically to cross a Python/C++ language
39 boundary). Proto has a limit of 2GB for serialized messages, which is
40 generally hit much earlier than the repeated field limit.
42 Note that for users solving locally, they can avoid needing to serialize
44 - using the C++ implementation of Elemental
45 - using the upb or cpp implementations of proto for python and compile
46 correctly, see go/fastpythonproto and
47 https://github.com/protocolbuffers/protobuf/blob/main/python/README.md.
51 self, *, model_name: str =
"", primary_objective_name: str =
""
53 """Creates an empty optimization model.
56 model_name: The name of the model, used for logging and export only.
57 primary_objective_name: The name of the main objective of the problem.
58 Typically used only for multi-objective problems.
63 """Returns an Elemental equivalent to the input proto."""
65 def clone(self, *, new_model_name: Optional[str] =
None) -> Self:
66 """Returns a copy of this model with no associated diffs."""
70 """The name of the model."""
74 """The name of the primary objective of the model (rarely used)."""
76 def add_element(self, element_type: enums.ElementType, name: str) -> int:
77 """Adds an element of `element_type` to the model and returns its id."""
80 self, element_type: enums.ElementType, num: int
81 ) -> np.typing.NDArray[np.int64]:
82 """Adds `num` `element_type`s to the model and returns their ids.
84 All elements added will have the name ''.
87 element_type: The ElementType of elements to add to the model.
88 num: How many elements are added.
91 A numpy array with shape (num,) with the ids of the newly added elements.
96 element_type: enums.ElementType,
97 names: np.typing.NDArray,
98 ) -> np.typing.NDArray[np.int64]:
99 """Adds an element of `element_type` for each name in names and returns ids.
102 element_type: The ElementType of elements to add to the model.
103 names: The names the elements, must have shape (n,) and string values.
106 A numpy array with shape (n,) with the ids of the newly added elements.
109 def delete_element(self, element_type: enums.ElementType, element_id: int) -> bool:
110 """Deletes element `id` of `element_type` from model, returns success."""
114 element_type: enums.ElementType,
115 elements: np.typing.NDArray[np.int64],
116 ) -> np.typing.NDArray[np.bool_]:
117 """Removes `elements` from the model, returning true elementwise on success.
119 A value of false is returned when an element was deleted in a previous call
120 to delete elements or the element was never in the model. Note that
121 repeating an id in `elements` for a single call to this function results in
125 element_type: The ElementType of elements to delete from to the model.
126 elements: The ids of the elements to delete, must have shape (n,).
129 A numpy array with shape (n,) indicating if each element was successfully
130 deleted. (Entries are false when the element id was previously deleted or
131 was never in the model.)
134 ValueError: if elements contains any duplicates. No modifications to the
135 model will be applied when this exception is raised.
139 """Returns the name of the element `id` of ElementType `element_type`."""
143 element_type: enums.ElementType,
144 elements: np.typing.NDArray[np.int64],
145 ) -> np.typing.NDArray:
146 """Returns the name of each element in `elements`.
148 Note that elements have a default name of '' if no name is provided.
151 element_type: The ElementType of elements to get the names for.
152 elements: The ids of the elements, must have shape (n,).
155 A numpy array with shape (n,) containing the names.
158 ValueError: if any id from `elements` is not in the model.
161 def element_exists(self, element_type: enums.ElementType, element_id: int) -> bool:
162 """Returns if element `id` of ElementType `element_type` is in the model."""
166 element_type: enums.ElementType,
167 elements: np.typing.NDArray[np.int64],
168 ) -> np.typing.NDArray[np.bool_]:
169 """Returns if each id in `elements` is an element in the model.
172 element_type: The ElementType to check.
173 elements: The ids to look for, must have shape (n,).
176 A numpy array with shape (n,) containing true if each element is in the
177 model (the id has been created and not deleted).
181 """Returns the next available element id of type `element_type`."""
184 """Returns the number of elements of type `element_type` in the model."""
187 self, element_type: enums.ElementType
188 ) -> np.typing.NDArray[np.int64]:
189 """Returns all element ids for type `element_type` in unspecified order."""
192 self, element_type: enums.ElementType, element_id: int
194 """Increases next_element_id() to `element_id` if it is currently less."""
198 attr: enums.PyAttr[enums.AttrPyValueType],
200 values: enums.AttrPyValueType,
202 """Sets an attribute to a value for a key."""
206 attr: enums.Attr[enums.AttrValueType],
207 keys: np.typing.NDArray[np.int64],
208 values: np.typing.NDArray[enums.AttrValueType],
210 """Sets the value of an attribute for a list of keys.
213 attr: The attribute to modify, with k elements in each key.
214 keys: An (n, k) array of n keys to set this attribute for.
215 values: An array with shape (n,), the values to set for each key.
218 ValueError: if (1) any key is repeated (2) any key references an element
219 not in the model, (3) the shape of keys are values is invalid, or (4)
220 the shape of keys and values is inconsistent. No changes are applied for
221 any key if the operation fails.
225 self, attr: enums.PyAttr[enums.AttrPyValueType], key: Sequence[int]
226 ) -> enums.AttrPyValueType:
227 """Returns the attribute value for a key.
229 The type of the attribute determines the number of elements in the key and
230 return type. E.g. when attr=DoubleAttr1.VARIABLE_LOWER_BOUND, key should
231 have size one (the element id of the variable) and the return type is float.
234 attr: The attribute to query, which implies the key size and return type.
235 key: A sequence of k ints, the element ids of the key.
238 The value for the key, or the default value for the attribute if the key
242 ValueError: if key is of the wrong size or key refers to an element id
248 attr: enums.Attr[enums.AttrValueType],
249 keys: np.typing.NDArray[np.int64],
250 ) -> np.typing.NDArray[enums.AttrValueType]:
251 """Returns the values of an attribute for a list of keys.
253 Repeated keys are okay.
256 attr: The attribute to query, with k elements in each key.
257 keys: An (n, k) array of n keys to read.
260 An array with shape (n,) with the values for each key. The default value
261 of the attribute is returned if it was never set for the key.
264 ValueError: if (1) any key references an element not in the model or (2)
265 the shape of keys is invalid.
269 """Restores an attribute to its default value for every key."""
272 """Returns true if the attribute has a non-default value for key."""
275 self, attr: enums.AnyAttr, keys: np.typing.NDArray[np.int64]
276 ) -> np.typing.NDArray[np.bool_]:
277 """Returns which keys take a value different from the attribute's default.
279 Repeated keys are okay.
282 attr: The attribute to query, with k elements in each key.
283 keys: An (n, k) array to of n keys to query.
286 An array with shape (n,), for each key, if it had a non-default value.
289 ValueError: if (1) any key references an element not in the model or (2)
290 the shape of keys is invalid.
294 """Returns the number of keys with a non-default value for an attribute."""
297 """Returns the keys with a non-default value for an attribute.
300 attr: The attribute to query, with k elements in each key.
303 An array with shape (n, k) if there are n keys with a non-default value
308 self, attr: enums.AnyAttr, key_index: int, element_id: int
309 ) -> np.typing.NDArray[np.int64]:
310 """Returns the keys with a non-default value for an attribute along a slice.
313 attr: The attribute to query, with k elements in each key.
314 key_index: The index of the key to slice on, in [0..k).
315 element_id: The value of the key to slice on, must be the id of an element
316 with type given by the `key_index` key for `attr`.
319 An array with shape (n, k) if there are n keys along the slice with a
320 non-default value for this attribute.
323 ValueError: if (1) `key_index` is not in [0..k) or (2) if no element with
328 self, attr: enums.AnyAttr, key_index: int, element_id: int
330 """Returns the number of keys in slice_attr(attr, key_index, element_id)."""
332 def export_model(self, *, remove_names: bool =
False) -> model_pb2.ModelProto:
333 """Returns a ModelProto equivalent to this model.
336 remove_names: If True, exclude names (e.g. variable names, the model name)
337 from the returned proto.
340 The equivalent ModelProto.
343 ValueError: if the model is too big to fit into the proto, see class
344 description for details.
348 """Creates a new Diff to track changes to the model and returns its id."""
351 """Stop tracking changes to the model for the Diff with id `diff_id`."""
354 """Discards any previously tracked changes for this Diff.
356 The diff will now track changes from the point onward.
359 diff_id: The id of to the Diff to advance.
362 ValueError: if diff_id does not reference a Diff for this model (e.g.,
363 the Diff was already deleted).
367 self, diff_id: int, *, remove_names: bool =
False
368 ) -> Optional[model_update_pb2.ModelUpdateProto]:
369 """Returns a ModelUpdateProto with the changes for the Diff `diff_id`.
372 diff_id: The id of the Diff to get changes for.
373 remove_names: If True, exclude names (e.g. variable names, the model name)
374 from the returned proto.
377 All changes to the model since the most recent call to
378 `advance(diff_id)`, or since the Diff was created if it was never
379 advanced. Returns `None` instead of an empty proto when there are no
383 ValueError: if the update is too big to fit into the proto (see class
384 description for details) or if diff_id does not reference a Diff for
385 this model (e.g., the id was already deleted).
388 def apply_update(self, update_proto: model_update_pb2.ModelUpdateProto) ->
None:
389 """Modifies the model to apply the changes from `update_proto`.
392 update_proto: the changes to apply to the model.
395 ValueError: if the update proto is invalid for the current model, or if
396 the implementation must serialize the proto and it is too large (see