15"""An API for storing a MathOpt model and tracking model modifications."""
17from collections.abc
import Sequence
18from typing
import Optional, Protocol
23from typing_extensions
import Self
31 """An API for building, modifying, and tracking changes to a MathOpt model.
33 On functions that return protocol buffers: These functions can fail for two
35 (1) The data is too large for proto's in memory representation. Specifically,
36 any repeated field can have at most 2^31 entries (~2 billion). So if your
37 model has this many nonzeros in the constraint matrix, we cannot build a
38 proto for it (we can potentially export to a text format still).
39 (2) The particular combination of Elemental and Proto you are using must
40 serialize your message (typically to cross a Python/C++ language
41 boundary). Proto has a limit of 2GB for serialized messages, which is
42 generally hit much earlier than the repeated field limit.
44 Note that for users solving locally, they can avoid needing to serialize
46 - using the C++ implementation of Elemental
47 - using the upb or cpp implementations of proto for python and compile
48 correctly, see go/fastpythonproto and
49 https://github.com/protocolbuffers/protobuf/blob/main/python/README.md.
53 self, *, model_name: str =
"", primary_objective_name: str =
""
55 """Creates an empty optimization model.
58 model_name: The name of the model, used for logging and export only.
59 primary_objective_name: The name of the main objective of the problem.
60 Typically used only for multi-objective problems.
65 """Returns an Elemental equivalent to the input proto."""
67 def clone(self, *, new_model_name: Optional[str] =
None) -> Self:
68 """Returns a copy of this model with no associated diffs."""
72 """The name of the model."""
76 """The name of the primary objective of the model (rarely used)."""
78 def add_element(self, element_type: enums.ElementType, name: str) -> int:
79 """Adds an element of `element_type` to the model and returns its id."""
82 self, element_type: enums.ElementType, num: int
83 ) -> np.typing.NDArray[np.int64]:
84 """Adds `num` `element_type`s to the model and returns their ids.
86 All elements added will have the name ''.
89 element_type: The ElementType of elements to add to the model.
90 num: How many elements are added.
93 A numpy array with shape (num,) with the ids of the newly added elements.
98 element_type: enums.ElementType,
99 names: np.typing.NDArray,
100 ) -> np.typing.NDArray[np.int64]:
101 """Adds an element of `element_type` for each name in names and returns ids.
104 element_type: The ElementType of elements to add to the model.
105 names: The names the elements, must have shape (n,) and string values.
108 A numpy array with shape (n,) with the ids of the newly added elements.
111 def delete_element(self, element_type: enums.ElementType, element_id: int) -> bool:
112 """Deletes element `id` of `element_type` from model, returns success."""
116 element_type: enums.ElementType,
117 elements: np.typing.NDArray[np.int64],
118 ) -> np.typing.NDArray[np.bool_]:
119 """Removes `elements` from the model, returning true elementwise on success.
121 A value of false is returned when an element was deleted in a previous call
122 to delete elements or the element was never in the model. Note that
123 repeating an id in `elements` for a single call to this function results in
127 element_type: The ElementType of elements to delete from to the model.
128 elements: The ids of the elements to delete, must have shape (n,).
131 A numpy array with shape (n,) indicating if each element was successfully
132 deleted. (Entries are false when the element id was previously deleted or
133 was never in the model.)
136 ValueError: if elements contains any duplicates. No modifications to the
137 model will be applied when this exception is raised.
141 """Returns the name of the element `id` of ElementType `element_type`."""
145 element_type: enums.ElementType,
146 elements: np.typing.NDArray[np.int64],
147 ) -> np.typing.NDArray:
148 """Returns the name of each element in `elements`.
150 Note that elements have a default name of '' if no name is provided.
153 element_type: The ElementType of elements to get the names for.
154 elements: The ids of the elements, must have shape (n,).
157 A numpy array with shape (n,) containing the names.
160 ValueError: if any id from `elements` is not in the model.
163 def element_exists(self, element_type: enums.ElementType, element_id: int) -> bool:
164 """Returns if element `id` of ElementType `element_type` is in the model."""
168 element_type: enums.ElementType,
169 elements: np.typing.NDArray[np.int64],
170 ) -> np.typing.NDArray[np.bool_]:
171 """Returns if each id in `elements` is an element in the model.
174 element_type: The ElementType to check.
175 elements: The ids to look for, must have shape (n,).
178 A numpy array with shape (n,) containing true if each element is in the
179 model (the id has been created and not deleted).
183 """Returns the next available element id of type `element_type`."""
186 """Returns the number of elements of type `element_type` in the model."""
189 self, element_type: enums.ElementType
190 ) -> np.typing.NDArray[np.int64]:
191 """Returns all element ids for type `element_type` in unspecified order."""
194 self, element_type: enums.ElementType, element_id: int
196 """Increases next_element_id() to `element_id` if it is currently less."""
200 attr: enums.PyAttr[enums.AttrPyValueType],
202 values: enums.AttrPyValueType,
204 """Sets an attribute to a value for a key."""
208 attr: enums.Attr[enums.AttrValueType],
209 keys: np.typing.NDArray[np.int64],
210 values: np.typing.NDArray[enums.AttrValueType],
212 """Sets the value of an attribute for a list of keys.
215 attr: The attribute to modify, with k elements in each key.
216 keys: An (n, k) array of n keys to set this attribute for.
217 values: An array with shape (n,), the values to set for each key.
220 ValueError: if (1) any key is repeated (2) any key references an element
221 not in the model, (3) the shape of keys are values is invalid, or (4)
222 the shape of keys and values is inconsistent. No changes are applied for
223 any key if the operation fails.
227 self, attr: enums.PyAttr[enums.AttrPyValueType], key: Sequence[int]
228 ) -> enums.AttrPyValueType:
229 """Returns the attribute value for a key.
231 The type of the attribute determines the number of elements in the key and
232 return type. E.g. when attr=DoubleAttr1.VARIABLE_LOWER_BOUND, key should
233 have size one (the element id of the variable) and the return type is float.
236 attr: The attribute to query, which implies the key size and return type.
237 key: A sequence of k ints, the element ids of the key.
240 The value for the key, or the default value for the attribute if the key
244 ValueError: if key is of the wrong size or key refers to an element id
250 attr: enums.Attr[enums.AttrValueType],
251 keys: np.typing.NDArray[np.int64],
252 ) -> np.typing.NDArray[enums.AttrValueType]:
253 """Returns the values of an attribute for a list of keys.
255 Repeated keys are okay.
258 attr: The attribute to query, with k elements in each key.
259 keys: An (n, k) array of n keys to read.
262 An array with shape (n,) with the values for each key. The default value
263 of the attribute is returned if it was never set for the key.
266 ValueError: if (1) any key references an element not in the model or (2)
267 the shape of keys is invalid.
271 """Restores an attribute to its default value for every key."""
274 """Returns true if the attribute has a non-default value for key."""
277 self, attr: enums.AnyAttr, keys: np.typing.NDArray[np.int64]
278 ) -> np.typing.NDArray[np.bool_]:
279 """Returns which keys take a value different from the attribute's default.
281 Repeated keys are okay.
284 attr: The attribute to query, with k elements in each key.
285 keys: An (n, k) array to of n keys to query.
288 An array with shape (n,), for each key, if it had a non-default value.
291 ValueError: if (1) any key references an element not in the model or (2)
292 the shape of keys is invalid.
296 """Returns the number of keys with a non-default value for an attribute."""
299 """Returns the keys with a non-default value for an attribute.
302 attr: The attribute to query, with k elements in each key.
305 An array with shape (n, k) if there are n keys with a non-default value
310 self, attr: enums.AnyAttr, key_index: int, element_id: int
311 ) -> np.typing.NDArray[np.int64]:
312 """Returns the keys with a non-default value for an attribute along a slice.
315 attr: The attribute to query, with k elements in each key.
316 key_index: The index of the key to slice on, in [0..k).
317 element_id: The value of the key to slice on, must be the id of an element
318 with type given by the `key_index` key for `attr`.
321 An array with shape (n, k) if there are n keys along the slice with a
322 non-default value for this attribute.
325 ValueError: if (1) `key_index` is not in [0..k) or (2) if no element with
330 self, attr: enums.AnyAttr, key_index: int, element_id: int
332 """Returns the number of keys in slice_attr(attr, key_index, element_id)."""
334 def export_model(self, *, remove_names: bool =
False) -> model_pb2.ModelProto:
335 """Returns a ModelProto equivalent to this model.
338 remove_names: If True, exclude names (e.g. variable names, the model name)
339 from the returned proto.
342 The equivalent ModelProto.
345 ValueError: if the model is too big to fit into the proto, see class
346 description for details.
350 """Creates a new Diff to track changes to the model and returns its id."""
353 """Stop tracking changes to the model for the Diff with id `diff_id`."""
356 """Discards any previously tracked changes for this Diff.
358 The diff will now track changes from the point onward.
361 diff_id: The id of to the Diff to advance.
364 ValueError: if diff_id does not reference a Diff for this model (e.g.,
365 the Diff was already deleted).
369 self, diff_id: int, *, remove_names: bool =
False
370 ) -> Optional[model_update_pb2.ModelUpdateProto]:
371 """Returns a ModelUpdateProto with the changes for the Diff `diff_id`.
374 diff_id: The id of the Diff to get changes for.
375 remove_names: If True, exclude names (e.g. variable names, the model name)
376 from the returned proto.
379 All changes to the model since the most recent call to
380 `advance(diff_id)`, or since the Diff was created if it was never
381 advanced. Returns `None` instead of an empty proto when there are no
385 ValueError: if the update is too big to fit into the proto (see class
386 description for details) or if diff_id does not reference a Diff for
387 this model (e.g., the id was already deleted).
390 def apply_update(self, update_proto: model_update_pb2.ModelUpdateProto) ->
None:
391 """Modifies the model to apply the changes from `update_proto`.
394 update_proto: the changes to apply to the model.
397 ValueError: if the update proto is invalid for the current model, or if
398 the implementation must serialize the proto and it is too large (see