-
Notifications
You must be signed in to change notification settings - Fork 238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Infeasibility diagnostic tool #1409
Changes from 3 commits
6099416
199e2b7
a931a77
34668b0
40670e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
from math import log, isclose, inf, isfinite | ||
import json | ||
from typing import List | ||
import logging | ||
|
||
import numpy as np | ||
from scipy.linalg import svd | ||
|
@@ -73,6 +74,7 @@ | |
from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP | ||
from pyomo.contrib.pynumero.asl import AmplInterface | ||
from pyomo.contrib.fbbt.fbbt import compute_bounds_on_expr | ||
from pyomo.contrib.iis import mis | ||
from pyomo.common.deprecation import deprecation_warning | ||
from pyomo.common.errors import PyomoException | ||
from pyomo.common.tempfiles import TempfileManager | ||
|
@@ -291,6 +293,14 @@ | |
description="Tolerance for identifying near-parallel Jacobian rows/columns", | ||
), | ||
) | ||
CONFIG.declare( | ||
"absolute_feasibility_tolerance", | ||
ConfigValue( | ||
default=1e-6, | ||
domain=float, | ||
description="Feasiblity tolerance for idenifying infeasible constraint and bounds", | ||
), | ||
) | ||
|
||
|
||
SVDCONFIG = ConfigDict() | ||
|
@@ -725,6 +735,36 @@ | |
footer="=", | ||
) | ||
|
||
def compute_infeasibility_explanation(self, solver, stream=None, tee=False): | ||
""" | ||
This function attempts to determine why a given model is infeasible. It deploys | ||
two main algorithms: | ||
|
||
1. Relaxes the constraints of the problem, and reports to the user | ||
some sets of constraints and variable bounds, which when relaxed, creates a | ||
feasible model. | ||
2. Uses the information collected from (1) to attempt to compute a Minimal | ||
Infeasible System (MIS), which is a set of constraints and variable bounds | ||
which appear to be in conflict with each other. It is minimal in the sense | ||
that removing any single constraint or variable bound would result in a | ||
feasible subsystem. | ||
|
||
Args: | ||
solver: A pyomo solver object or a string for SolverFactory | ||
stream: I/O object to write report to (default = stdout) | ||
tee: Display intermediate solves conducted (False) | ||
|
||
Returns: | ||
None | ||
|
||
""" | ||
compute_infeasibility_explanation( | ||
self._model, | ||
solver, | ||
tolerance=self.config.absolute_feasibility_tolerance, | ||
stream=stream, | ||
) | ||
|
||
def get_dulmage_mendelsohn_partition(self): | ||
""" | ||
Performs a Dulmage-Mendelsohn partitioning on the model and returns | ||
|
@@ -1123,6 +1163,9 @@ | |
next_steps.append( | ||
self.display_constraints_with_large_residuals.__name__ + "()" | ||
) | ||
next_steps.append( | ||
self.compute_infeasibility_explanation.__name__ + "(solver=)" | ||
) | ||
|
||
# Variables outside bounds | ||
violated_bounds = _vars_violating_bounds( | ||
|
@@ -3887,6 +3930,51 @@ | |
return ill_cond | ||
|
||
|
||
def compute_infeasibility_explanation( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be done as a separate method, or could it just be written directly into the toolbox class? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree -- I couldn't decide at what level things should be integrated. Implemented this suggestion in 40670e4. |
||
model, solver, tee=False, tolerance=1e-6, stream=None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should have
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 40670e4 |
||
): | ||
""" | ||
This function attempts to determine why a given model is infeasible. It deploys | ||
two main algorithms: | ||
|
||
1. Relaxes the constraints of the problem, and reports to the user | ||
some sets of constraints and variable bounds, which when relaxed, creates a | ||
feasible model. | ||
2. Uses the information collected from (1) to attempt to compute a Minimal | ||
Infeasible System (MIS), which is a set of constraints and variable bounds | ||
which appear to be in conflict with each other. It is minimal in the sense | ||
that removing any single constraint or variable bound would result in a | ||
feasible subsystem. | ||
|
||
This function is a wrapper for the same capability in pyomo.contrib.iis.mis. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You will need to add a few exclusions to the spell-checker file as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 34668b0 |
||
|
||
Args: | ||
model: A pyomo block | ||
solver: A pyomo solver object or a string for SolverFactory | ||
tee: Display intermediate solves conducted (False) | ||
tolerance: The feasibility tolerance to use when declaring a | ||
constraint feasible (1e-08) | ||
stream: I/O object to write report to (default = stdout) | ||
|
||
Returns: | ||
None | ||
|
||
""" | ||
if stream is None: | ||
stream = sys.stdout | ||
|
||
h = logging.StreamHandler(stream) | ||
h.setLevel(logging.INFO) | ||
|
||
l = logging.Logger(name=__name__ + ".compute_infeasibility_explanation") | ||
l.setLevel(logging.INFO) | ||
l.addHandler(h) | ||
|
||
mis.compute_infeasibility_explanation( | ||
model, solver, tee=tee, tolerance=tolerance, logger=l | ||
) | ||
|
||
|
||
# ------------------------------------------------------------------------------------------- | ||
# Private module functions | ||
def _var_in_block(var, block): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: "idenifying"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also "feasiblty" I believe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should probably also be constraints (plural).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 34668b0 and 40670e4