IDAES / idaes-pse

The IDAES Process Systems Engineering Framework
https://idaes-pse.readthedocs.io/
Other
217 stars 235 forks source link

Diagnostics toolbox: ``display_near_parallel_constraints()`` does not distinguish between equality and inequality constraints #1502

Open OOAmusat opened 3 weeks ago

OOAmusat commented 3 weeks ago

Running the diagnostics toolbox on a model with inequality constraints enforcing variable bounds is throwing up display_near_parallel_constraints() warnings:

MWE:

 @m.Constraint(m.periods)
    def eq_hw_tank_temperature_lb(m, key):
        return m.fs[key].hw_tank.T[0] >= (50 + 273.15) * pyunits.K

    @m.Constraint(m.periods)
    def eq_hw_tank_temperature_ub(m, key):
        return m.fs[key].hw_tank.T[0] <= (90 + 273.15) * pyunits.K

Diagnostics box output


====================================================================================
>>> dt.display_near_parallel_constraints()
WARNING: model contains export suffix 'scaling_factor' that contains 144
component keys that are not exported as part of the NL file.  Skipping.
====================================================================================
The following pairs of constraints are nearly parallel:

    eq_hw_tank_temperature_lb[0], eq_hw_tank_temperature_ub[0]
    eq_hw_tank_temperature_lb[1], eq_hw_tank_temperature_ub[1]
    eq_hw_tank_temperature_lb[2], eq_hw_tank_temperature_ub[2]
    eq_hw_tank_temperature_lb[3], eq_hw_tank_temperature_ub[3]
    eq_hw_tank_temperature_lb[4], eq_hw_tank_temperature_ub[4]
    eq_hw_tank_temperature_lb[5], eq_hw_tank_temperature_ub[5]
    eq_hw_tank_temperature_lb[6], eq_hw_tank_temperature_ub[6]
    eq_hw_tank_temperature_lb[7], eq_hw_tank_temperature_ub[7]
    eq_hw_tank_temperature_lb[8], eq_hw_tank_temperature_ub[8]
    eq_hw_tank_temperature_lb[9], eq_hw_tank_temperature_ub[9]
    eq_hw_tank_temperature_lb[10], eq_hw_tank_temperature_ub[10]
    eq_hw_tank_temperature_lb[11], eq_hw_tank_temperature_ub[11]
    eq_hw_tank_temperature_lb[12], eq_hw_tank_temperature_ub[12]
    eq_hw_tank_temperature_lb[13], eq_hw_tank_temperature_ub[13]
    eq_hw_tank_temperature_lb[14], eq_hw_tank_temperature_ub[14]
    eq_hw_tank_temperature_lb[15], eq_hw_tank_temperature_ub[15]
    eq_hw_tank_temperature_lb[16], eq_hw_tank_temperature_ub[16]
    eq_hw_tank_temperature_lb[17], eq_hw_tank_temperature_ub[17]
    eq_hw_tank_temperature_lb[18], eq_hw_tank_temperature_ub[18]
    eq_hw_tank_temperature_lb[19], eq_hw_tank_temperature_ub[19]
    eq_hw_tank_temperature_lb[20], eq_hw_tank_temperature_ub[20]
    eq_hw_tank_temperature_lb[21], eq_hw_tank_temperature_ub[21]
    eq_hw_tank_temperature_lb[22], eq_hw_tank_temperature_ub[22]
    eq_hw_tank_temperature_lb[23], eq_hw_tank_temperature_ub[23]

According to @andrewlee94, this issue is possibly because we do not distinguish between equality and inequality constraints in the check.

dallan-keylogic commented 3 weeks ago

The quick, easy fix is to just set the equalities_only option in get_jacobian to True. However, that misses out on potential issues. Equalities shouldn't be near parallel to each other, and equalities shouldn't be near parallel to any inequalities. The nuance comes in when comparing inequalities to other inequalities. I think what we want to do is get normal vectors that point out of the feasible region and check whether the inner product of those normal vectors is greater than zero. We can find a unique normal vector in 2D, but, off the top of my head, I'm not sure what to do in higher dimensions.

@Robbybp , what do you think?

dallan-keylogic commented 3 weeks ago

Also, incidentally, why are you using Constraints here? Setting variable bounds on m.fs[key].hw_tank.T[0] would generally be better form. The difference is that a solver like IPOPT allows transient violation of Constraints, but will truncate steps to keep variables within their bounds.

dallan-keylogic commented 3 weeks ago

Actually, taking the gradient gives the normal vector already (for some reason I thought it gave a tangent vector), so we should be golden. We just need to partition the Jacobian between equality and inequality constraints, make sure the inequality constraints have appropriate signs based on whether they have lower bounds, upper bounds, or both, then apply the same test without taking the absolute value of the inner product.

OOAmusat commented 3 weeks ago

Also, incidentally, why are you using Constraints here? Setting variable bounds on m.fs[key].hw_tank.T[0] would generally be better form. The difference is that a solver like IPOPT allows transient violation of Constraints, but will truncate steps to keep variables within their bounds.

I set the variable bounds. I came across when I started trying out different formulations for an infeasible model (I wanted to see if using constraints instead of bounds would help).

Robbybp commented 2 weeks ago

Yeah, it shouldn't be too hard to exclude inequality-inequality pairs. Although I don't think parallel inequality-equality pairs are too big of a problem, so I'd be happy with just omitting inequalities.