ConstraintIDBase

ConstraintIDBase#

class ConstraintIDBase#

Common base of constraint IDs, including families introduced by future SDKs.

Consumers must handle the concrete ID types they support and reject all others. This base alone is not a usable constraint identity. Integers are not instances of this class and are never accepted by mixed-family lookups.

Typed identity and integer inputs

The currently supported concrete types are ConstraintID, IndicatorConstraintID, OneHotConstraintID, and Sos1ConstraintID. An ID carries its family and an unsigned 64-bit number. IDs from different families are unequal, even if their numbers coincide. Within one Instance, constraint numbers must still be unique across all active and removed families.

APIs whose argument already fixes a family promote integer inputs to that family’s ID: for example, constraints={1: constraint} in from_components(), or instance.constraints.of_kind("regular")[1]. They reject IDs of another family. Such mapping inputs reject 1 and ConstraintID(1) together with ValueError, because the keys normalize to the same identity. Mixed-family collections and constraint_violation(id) require typed IDs. DataFrame indexes also retain typed IDs. Constraint IDs do not support int() or integer-index conversion; variable and sample IDs remain integers.

IDs are immutable. Shallow and deep copies return the same ID object; copying containers or pickling preserves ID family and value, including IDs used in DataFrame indexes or columns.

>>> import copy
>>> import pickle
>>> from ommx import ConstraintID, OneHotConstraintID
>>> cid = ConstraintID(7)
>>> assert cid != 7 and cid != OneHotConstraintID(7)
>>> assert copy.copy(cid) is cid
>>> assert copy.deepcopy(cid) is cid
>>> assert copy.deepcopy({cid: [cid]}) == {cid: [cid]}
>>> restored = pickle.loads(pickle.dumps({cid: [cid]}))
>>> assert restored == {cid: [cid]}
>>> assert type(next(iter(restored))) is ConstraintID

Supporting additional families

New special-constraint families are additive API extensions. Collections, including removed and result collections, expose the open common interfaces Mapping[ConstraintIDBase, ConstraintView]; their known concrete ID overloads retain precise row types. The currently supported families are not an exhaustive list of future possibilities.

A solver adapter or transformation must reject families it cannot handle. Silently skipping their constraints can relax the optimization problem. Keep this check even if an earlier applicability check normally excludes unsupported kinds. For example, a consumer requiring regular rows can validate the complete collection before processing it:

>>> from ommx import ConstraintID, Instance, OneHotConstraint
>>> def regular_rows(model):
...     rows = []
...     for cid in model.constraints:
...         if not isinstance(cid, ConstraintID):
...             raise NotImplementedError("Unsupported constraint family")
...         rows.append(model.constraints[cid])
...     return rows
>>> model = Instance.minimize()
>>> x = model.new_binary("x")
>>> y = model.new_binary("y")
>>> rule = model.add_constraint(x + y <= 1)
>>> assert regular_rows(model) == [rule]
>>> _ = model.add_one_hot_constraint(OneHotConstraint(variables=[x, y]))
>>> regular_rows(model)
Traceback (most recent call last):
...
NotImplementedError: Unsupported constraint family

Handle additional concrete ID types explicitly when supported and retain the rejecting fallback. Receiving a new family in an older consumer should fail explicitly. Metadata-only inspection can use ConstraintView without interpreting each family’s mathematical meaning.

__new__() → ConstraintIDBase#