You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
importpandasaspdimportlinopylinopy.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 CSRConstraint whose CSR has one appended entry per active row: the slack label with coefficient -1 (<=), +1 (>=), and both for ==; replace the registered constraint in model.constraints;
keep the current rules: positive penalty, objective must exist, no double softening, no mixed signs, max_violation as 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.
Note
The following content was generated by AI.
Describe the feature you'd like to see
Soften a frozen
CSRConstraintnatively, soadd_constraints(..., penalty=..., freeze=True)works without densifying. Follow-up of #970 and #974.Today a frozen constraint cannot be softened by any route:
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 throughupdate(lhs=lhs - slack), stores it asslackand addspenalty * slackto the objective. On the CSR store this is one extra term per active row:mask=con.mask, as today;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;max_violationas slack upper bound.Then
add_constraintscan drop thepenalty+freezeerror and soften the frozen constraint directly. Tests should compare against the densesoftencell by cell for<=,>=,==, with a mask and withmax_violation, and check that no densify notice is emitted.A cheaper intermediate step is possible: with
penalty=andfreeze=True, build the mutable constraint, soften it, then freeze it. That densifies each softened constraint once, with a notice.