Repository navigation
Description
Note
The following content was generated by AI.
Describe the feature you'd like to see
Soften a frozen CSRConstraint natively, so add_constraints(..., penalty=..., freeze=True) works without densifying. Follow-up of #970 and #974.
Today a frozen constraint cannot be softened by any route:
import pandas as pd
import linopy
linopy.options["semantics"] = "v1"
m = linopy.Model()
x = m.add_variables(lower=0, coords=[pd.RangeIndex(4, name="i")], name="x")
m.add_objective(x.sum())
m.add_constraints(x >= 1, name="c", freeze=True, penalty=10)
# ValueError: `penalty` cannot be combined with `freeze=True` ...
c = m.add_constraints(x >= 1, name="d", freeze=True)
c.soften(penalty=10)
# AttributeError: CSRConstraint.soften is not supported on a frozen constraint; add the constraint with freeze=False to soften it.
c.mutable().soften(penalty=10)
# ValueError: Constraint 'd' is not the constraint registered in the model ...The only route is to keep the constraint unfrozen for its whole life, which loses the sparse path for large models.
Implementation ideas
Constraint.soften (linopy/constraints.py) adds a slack variable (two for ==), rewrites the lhs through update(lhs=lhs - slack), stores it as slack and adds penalty * slack to the objective. On the CSR store this is one extra term per active row:
- add the slack variable(s) over the constraint grid with
mask=con.mask, as today; - return a new
CSRConstraintwhose CSR has one appended entry per active row: the slack label with coefficient-1(<=),+1(>=), and both for==; replace the registered constraint inmodel.constraints; - add the penalty term to the objective, which stays sparse since Sparse path for the objective #967;
- keep the current rules: positive penalty, objective must exist, no double softening, no mixed signs,
max_violationas slack upper bound.
Then add_constraints can drop the penalty + freeze error and soften the frozen constraint directly. Tests should compare against the dense soften cell by cell for <=, >=, ==, with a mask and with max_violation, and check that no densify notice is emitted.
A cheaper intermediate step is possible: with penalty= and freeze=True, build the mutable constraint, soften it, then freeze it. That densifies each softened constraint once, with a notice.
Activity
feat(csr): native soften for frozen constraints (#975)
coroa commentedon Sep 24, 2026
Isn't this what is typically called relaxing a constraint (rather than soften).
feat(csr): native soften for frozen constraints (#975)
feat(csr): native soften for frozen constraints (#979)