Skip to content
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

Fix methods in SVD Toolbox #1288

Merged
merged 6 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 25 additions & 32 deletions idaes/core/util/model_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -1450,6 +1450,10 @@ def __init__(self, model: _BlockData, **kwargs):
self._model, scaled=False, equality_constraints_only=True
)

# Get list of equality constraint and variable names
self._eq_con_list = self.nlp.get_pyomo_equality_constraints()
self._var_list = self.nlp.get_pyomo_variables()

if self.jacobian.shape[0] < 2:
raise ValueError(
"Model needs at least 2 equality constraints to perform svd_analysis."
Expand Down Expand Up @@ -1562,10 +1566,6 @@ def display_underdetermined_variables_and_constraints(

tol = self.config.size_cutoff_in_singular_vector

# Get list of equality constraint and variable names
eq_con_list = self.nlp.get_pyomo_equality_constraints()
var_list = self.nlp.get_pyomo_variables()

if singular_values is None:
singular_values = range(1, len(self.s) + 1)

Expand All @@ -1588,11 +1588,11 @@ def display_underdetermined_variables_and_constraints(
stream.write(f"{TAB}Smallest Singular Value {e}:\n\n")
stream.write(f"{2 * TAB}Variables:\n\n")
for v in np.where(abs(self.v[:, e - 1]) > tol)[0]:
stream.write(f"{3 * TAB}{var_list[v].name}\n")
stream.write(f"{3 * TAB}{self._var_list[v].name}\n")

stream.write(f"\n{2 * TAB}Constraints:\n\n")
for c in np.where(abs(self.u[:, e - 1]) > tol)[0]:
stream.write(f"{3 * TAB}{eq_con_list[c].name}\n")
stream.write(f"{3 * TAB}{self._eq_con_list[c].name}\n")
stream.write("\n")

stream.write("=" * MAX_STR_LENGTH + "\n")
Expand Down Expand Up @@ -1620,23 +1620,19 @@ def display_constraints_including_variable(self, variable, stream=None):
f"object (got {variable})."
)

# Get list of equality constraint and variable names
eq_con_list = self.nlp.get_pyomo_equality_constraints()
var_list = self.nlp.get_pyomo_variables()

# Get index of variable in Jacobian
try:
var_idx = var_list.index(variable)
except (ValueError, PyomoException):
var_idx = self.nlp.get_primal_indices([variable])[0]
except (KeyError, PyomoException):
raise AttributeError(f"Could not find {variable.name} in model.")

nonzeroes = self.jacobian[:, var_idx].nonzero()
nonzeros = self.jacobian.getcol(var_idx).nonzero()
Comment on lines -1633 to +1629
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the type of self.jacobian here? I.e. dense, COO, CSC, etc. I guess for a single variable, performance isn't an issue here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's some sparse type, I forget whether it's row or column based. Performance probably isn't an issue, so this is probably unnecessary, but I'm familiar with the methods whereas it's unclear (at least to me) how indexing sparse matrices works.

Copy link
Member

@Robbybp Robbybp Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getcol and indexing do the same thing AFAIK. So one of row/col is slow and one is fast depending on whether the matrix CSC or CSR. If the matrix is COO, both are slow.


# Build a list of all constraints that include var
cons_w_var = []
for i, r in enumerate(nonzeroes[0]):
for r in nonzeros[0]:
cons_w_var.append(
f"{eq_con_list[r].name}: {self.jacobian[(r, nonzeroes[1][i])]}"
f"{self._eq_con_list[r].name}: {self.jacobian[(r, var_idx)]:.3e}"
)

# Write the output
Expand Down Expand Up @@ -1671,23 +1667,20 @@ def display_variables_in_constraint(self, constraint, stream=None):
f"object (got {constraint})."
)

# Get list of equality constraint and variable names
eq_con_list = self.nlp.get_pyomo_equality_constraints()
var_list = self.nlp.get_pyomo_variables()

# Get index of variable in Jacobian
try:
con_idx = eq_con_list.index(constraint)
except ValueError:
con_idx = self.nlp.get_constraint_indices([constraint])[0]
except KeyError:
raise AttributeError(f"Could not find {constraint.name} in model.")

nonzeroes = self.jacobian[con_idx, :].nonzero()
nonzeros = self.jacobian[con_idx, :].nonzero()

# Build a list of all vars in constraint
vars_in_cons = []
for i, r in enumerate(nonzeroes[0]):
c = nonzeroes[1][i]
vars_in_cons.append(f"{var_list[c].name}: {self.jacobian[(r, c)]}")
for c in nonzeros[1]:
vars_in_cons.append(
f"{self._var_list[c].name}: {self.jacobian[(con_idx, c)]:.3e}"
)

# Write the output
_write_report_section(
Expand Down Expand Up @@ -2281,8 +2274,8 @@ def __init__(self, block_or_jac, solver=None):
self.jac_eq = get_jacobian(self.block, equality_constraints_only=True)[0]

# Create a list of equality constraint names
self.eq_con_list = self.nlp.get_pyomo_equality_constraints()
self.var_list = self.nlp.get_pyomo_variables()
self._eq_con_list = self.nlp.get_pyomo_equality_constraints()
self._var_list = self.nlp.get_pyomo_variables()

self.candidate_eqns = None

Expand All @@ -2293,7 +2286,7 @@ def __init__(self, block_or_jac, solver=None):

# # TODO: Need to refactor, document, and test support for Jacobian
# self.jac_eq = block_or_jac
# self.eq_con_list = None
# self._eq_con_list = None

else:
raise TypeError("Check the type for 'block_or_jac'")
Expand Down Expand Up @@ -2813,11 +2806,11 @@ def underdetermined_variables_and_constraints(self, n_calc=1, tol=0.1, dense=Fal
)
print("Column: Variable")
for i in np.where(abs(self.v[:, n_calc - 1]) > tol)[0]:
print(str(i) + ": " + self.var_list[i].name)
print(str(i) + ": " + self._var_list[i].name)
print("")
print("Row: Constraint")
for i in np.where(abs(self.u[:, n_calc - 1]) > tol)[0]:
print(str(i) + ": " + self.eq_con_list[i].name)
print(str(i) + ": " + self._eq_con_list[i].name)

def find_candidate_equations(self, verbose=True, tee=False):
"""
Expand All @@ -2842,7 +2835,7 @@ def find_candidate_equations(self, verbose=True, tee=False):
if verbose:
print("Solving MILP model...")
ce, ds = self._find_candidate_eqs(
self.candidates_milp, self.solver, self.eq_con_list, tee
self.candidates_milp, self.solver, self._eq_con_list, tee
)

if ce is not None:
Expand Down Expand Up @@ -2883,7 +2876,7 @@ def find_irreducible_degenerate_sets(self, verbose=True, tee=False):

# Check if equation 'c' is a major element of an IDS
ids_ = self._check_candidate_ids(
self.dh_milp, self.solver, c, self.eq_con_list, tee
self.dh_milp, self.solver, c, self._eq_con_list, tee
)

if ids_ is not None:
Expand Down
8 changes: 4 additions & 4 deletions idaes/core/util/tests/test_model_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -1598,8 +1598,8 @@ def test_display_constraints_including_variable(self):
expected = """====================================================================================
The following constraints involve v[1]:

c1: 1.0
c4: 8.0
c1: 1.000e+00
c4: 8.000e+00

====================================================================================
"""
Expand Down Expand Up @@ -1663,8 +1663,8 @@ def test_display_variables_in_constraint(self):
expected = """====================================================================================
The following variables are involved in c1:

v[1]: 1.0
v[2]: 2.0
v[1]: 1.000e+00
v[2]: 2.000e+00

====================================================================================
"""
Expand Down
Loading