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 MindtPy bugs #3034

Merged
merged 73 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
6d442b2
disable ipopt warmstart for feasibility subproblem solver
ZedongPeng Sep 21, 2023
a6e92c5
create new copy_var_list_values function
ZedongPeng Sep 21, 2023
f766c0a
update log format
ZedongPeng Sep 21, 2023
8306925
add update_solver_timelimit
ZedongPeng Sep 23, 2023
816e3d6
handle appsi solver unbounded situation
ZedongPeng Oct 9, 2023
5c47040
add skip_validation when ignore integrality
ZedongPeng Oct 10, 2023
a767f28
add special handle for rnlp infeasible
ZedongPeng Oct 10, 2023
a30cf82
fix bug
ZedongPeng Oct 10, 2023
2a6f1d7
add comments
ZedongPeng Oct 12, 2023
6bcdadb
fix bug
Oct 27, 2023
a66f955
improve copy_var_list_values function
ZedongPeng Oct 29, 2023
cefd4a6
fix FP bug
ZedongPeng Oct 29, 2023
dec70eb
Merge branch 'benchmark' of github.com:ZedongPeng/pyomo into benchmark
ZedongPeng Oct 29, 2023
905503b
fix gurobi single tree termination check bug
ZedongPeng Nov 1, 2023
3b01104
fix Gurobi single tree cycle handling
ZedongPeng Nov 2, 2023
906fff7
black format
ZedongPeng Nov 13, 2023
f57c938
Merge branch 'add_grey_box' into benchmark
ZedongPeng Nov 21, 2023
f261fdc
fix load_solutions bug
ZedongPeng Nov 21, 2023
892021f
Merge branch 'main' into benchmark
ZedongPeng Nov 21, 2023
1f2ab73
Merge branch 'main' into benchmark
emma58 Nov 27, 2023
015ebaf
fix typo: change try to trying
ZedongPeng Nov 27, 2023
a6079d5
add more details of the error in copy_var_list_values
ZedongPeng Nov 27, 2023
e8b3b72
create copy_var_value function
ZedongPeng Nov 27, 2023
dc41b8e
add exc_info for the error message
ZedongPeng Nov 28, 2023
dbe9f49
black format
ZedongPeng Nov 28, 2023
655692a
Merge branch 'main' into benchmark
ZedongPeng Nov 28, 2023
4ac390e
change dir() to locals()
ZedongPeng Nov 28, 2023
a755067
improve int_sol_2_cuts_ind
ZedongPeng Nov 28, 2023
51be801
Merge branch 'main' into benchmark
mrmundt Nov 28, 2023
d9d29bf
rename copy_var_value to set_var_value
ZedongPeng Nov 28, 2023
f04424e
add unit test for mindtpy
ZedongPeng Nov 28, 2023
ef66660
improve var_val description
ZedongPeng Nov 29, 2023
04ea15e
rename set_var_value to set_var_valid_value
ZedongPeng Nov 29, 2023
f84ff8d
change v_to to var
ZedongPeng Nov 29, 2023
83b28cb
move NOTE from docstring to comment
ZedongPeng Nov 29, 2023
6ee3f58
Merge branch 'main' into benchmark
ZedongPeng Nov 30, 2023
355df8b
remove redundant test
ZedongPeng Nov 30, 2023
a7a01c2
add test_add_var_bound
ZedongPeng Nov 30, 2023
875269f
delete redundant set_up_logger function
ZedongPeng Nov 30, 2023
65e58f5
add test_FP_L1_norm
ZedongPeng Dec 1, 2023
c8eead9
improve mindtpy logging
ZedongPeng Dec 1, 2023
baa816c
Merge branch 'main' into benchmark
ZedongPeng Dec 5, 2023
d229461
fix greybox cuts bug
ZedongPeng Dec 7, 2023
3d1db13
redesign calc_jacobians function
ZedongPeng Dec 7, 2023
7e69413
redesign initialize_feas_subproblem function
ZedongPeng Dec 7, 2023
c9f7888
redesign calc_jacobians function
ZedongPeng Dec 7, 2023
934b153
Merge branch 'main' into benchmark
ZedongPeng Dec 8, 2023
973fb23
Merge branch 'main' into benchmark
blnicho Dec 19, 2023
b4e9f47
Merge branch 'main' into benchmark
blnicho Jan 9, 2024
a93d793
change all ''' to """
ZedongPeng Jan 10, 2024
666cbaa
rename int_sol_2_cuts_ind to integer_solution_to_cuts_index
ZedongPeng Jan 10, 2024
14ebdcd
add one more comment to CPLEX lazy constraint callback
ZedongPeng Jan 10, 2024
b143e87
remove the finished TODO
ZedongPeng Jan 10, 2024
09bda47
add TODO for self.abort()
ZedongPeng Jan 10, 2024
767da46
Merge branch 'main' into benchmark
bernalde Jan 11, 2024
317dae8
update the version of MindtPy
ZedongPeng Jan 11, 2024
601ec30
Merge branch 'main' into benchmark
mrmundt Jan 15, 2024
76acc80
Merge branch 'main' into benchmark
bernalde Jan 24, 2024
f3414da
Merge branch 'main' into benchmark
blnicho Jan 24, 2024
43f0d24
Merge branch 'main' into benchmark
ZedongPeng Jan 24, 2024
70fe040
Merge branch 'main' into benchmark
ZedongPeng Jan 29, 2024
194e328
Merge branch 'main' into benchmark
ZedongPeng Jan 30, 2024
ff4c031
Merge branch 'main' into benchmark
emma58 Jan 31, 2024
1b73570
correct typos
ZedongPeng Jan 31, 2024
4ec0e8c
remove unused log
ZedongPeng Jan 31, 2024
acb10de
add one condition for fix dual bound
ZedongPeng Feb 1, 2024
de73340
remove fix_dual_bound for ECP method
ZedongPeng Feb 1, 2024
92d9477
black format
ZedongPeng Feb 1, 2024
8d051b0
black format
ZedongPeng Feb 1, 2024
66f5025
Merge branch 'main' into benchmark
ZedongPeng Feb 1, 2024
a77a19b
Update pyomo/contrib/mindtpy/util.py
ZedongPeng Feb 13, 2024
a0997d8
Update pyomo/contrib/mindtpy/util.py
ZedongPeng Feb 13, 2024
6c9fa3e
update the differentiate.Modes
ZedongPeng Feb 13, 2024
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
8 changes: 8 additions & 0 deletions pyomo/contrib/mindtpy/MindtPy.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@
- Add single-tree implementation.
- Add support for cplex_persistent solver.
- Fix bug in OA cut expression in cut_generation.py.

24.1.11 changes:
- fix gurobi single tree termination check bug
- fix Gurobi single tree cycle handling
- fix bug in feasibility pump method
- add special handling for infeasible relaxed NLP
- update the log format of infeasible fixed NLP subproblems
- create a new copy_var_list_values function
"""

from pyomo.contrib.mindtpy import __version__
Expand Down
2 changes: 1 addition & 1 deletion pyomo/contrib/mindtpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = (0, 1, 0)
__version__ = (1, 0, 0)
128 changes: 100 additions & 28 deletions pyomo/contrib/mindtpy/algorithm_base_class.py

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions pyomo/contrib/mindtpy/cut_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ def add_oa_cuts_for_grey_box(
target_model_grey_box.inputs.values()
)
)
- (output - value(output))
)
- (output - value(output))
- (slack_var if config.add_slack else 0)
<= 0
)
Expand Down Expand Up @@ -271,8 +271,9 @@ def add_ecp_cuts(
try:
upper_slack = constr.uslack()
except (ValueError, OverflowError) as e:
config.logger.error(e, exc_info=True)
config.logger.error(
str(e) + '\nConstraint {} has caused either a '
'Constraint {} has caused either a '
'ValueError or OverflowError.'
'\n'.format(constr)
)
Expand Down Expand Up @@ -300,8 +301,9 @@ def add_ecp_cuts(
try:
lower_slack = constr.lslack()
except (ValueError, OverflowError) as e:
config.logger.error(e, exc_info=True)
config.logger.error(
str(e) + '\nConstraint {} has caused either a '
'Constraint {} has caused either a '
'ValueError or OverflowError.'
'\n'.format(constr)
)
Expand Down Expand Up @@ -424,9 +426,9 @@ def add_affine_cuts(target_model, config, timing):
try:
mc_eqn = mc(constr.body)
except MCPP_Error as e:
config.logger.error(e, exc_info=True)
config.logger.error(
'\nSkipping constraint %s due to MCPP error %s'
% (constr.name, str(e))
'Skipping constraint %s due to MCPP error' % (constr.name)
)
continue # skip to the next constraint

Expand Down
11 changes: 7 additions & 4 deletions pyomo/contrib/mindtpy/extended_cutting_plane.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ def check_config(self):
super().check_config()

def initialize_mip_problem(self):
'''Deactivate the nonlinear constraints to create the MIP problem.'''
"""Deactivate the nonlinear constraints to create the MIP problem."""
super().initialize_mip_problem()
self.jacobians = calc_jacobians(self.mip, self.config) # preload jacobians
self.jacobians = calc_jacobians(
self.mip.MindtPy_utils.nonlinear_constraint_list,
self.config.differentiate_mode,
) # preload jacobians
self.mip.MindtPy_utils.cuts.ecp_cuts = ConstraintList(
doc='Extended Cutting Planes'
)
Expand Down Expand Up @@ -140,7 +143,7 @@ def all_nonlinear_constraint_satisfied(self):
lower_slack = nlc.lslack()
except (ValueError, OverflowError) as e:
# Set lower_slack (upper_slack below) less than -config.ecp_tolerance in this case.
config.logger.error(e)
config.logger.error(e, exc_info=True)
lower_slack = -10 * config.ecp_tolerance
if lower_slack < -config.ecp_tolerance:
config.logger.debug(
Expand All @@ -153,7 +156,7 @@ def all_nonlinear_constraint_satisfied(self):
try:
upper_slack = nlc.uslack()
except (ValueError, OverflowError) as e:
config.logger.error(e)
config.logger.error(e, exc_info=True)
upper_slack = -10 * config.ecp_tolerance
if upper_slack < -config.ecp_tolerance:
config.logger.debug(
Expand Down
7 changes: 5 additions & 2 deletions pyomo/contrib/mindtpy/feasibility_pump.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ def check_config(self):
super().check_config()

def initialize_mip_problem(self):
'''Deactivate the nonlinear constraints to create the MIP problem.'''
"""Deactivate the nonlinear constraints to create the MIP problem."""
super().initialize_mip_problem()
self.jacobians = calc_jacobians(self.mip, self.config) # preload jacobians
self.jacobians = calc_jacobians(
self.mip.MindtPy_utils.nonlinear_constraint_list,
self.config.differentiate_mode,
) # preload jacobians
self.mip.MindtPy_utils.cuts.oa_cuts = ConstraintList(
doc='Outer approximation cuts'
)
Expand Down
5 changes: 3 additions & 2 deletions pyomo/contrib/mindtpy/global_outer_approximation.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def check_config(self):
super().check_config()

def initialize_mip_problem(self):
'''Deactivate the nonlinear constraints to create the MIP problem.'''
"""Deactivate the nonlinear constraints to create the MIP problem."""
super().initialize_mip_problem()
self.mip.MindtPy_utils.cuts.aff_cuts = ConstraintList(doc='Affine cuts')

Expand Down Expand Up @@ -108,4 +108,5 @@ def deactivate_no_good_cuts_when_fixing_bound(self, no_good_cuts):
if self.config.use_tabu_list:
self.integer_list = self.integer_list[:valid_no_good_cuts_num]
except KeyError as e:
self.config.logger.error(str(e) + '\nDeactivating no-good cuts failed.')
self.config.logger.error(e, exc_info=True)
self.config.logger.error('Deactivating no-good cuts failed.')
Comment on lines 110 to +112
Copy link
Contributor

Choose a reason for hiding this comment

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

How can this fail? Wouldn't this be a DeveloperError (if you need an error here at all) since mindtpy can keep track of what cuts it has added?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. I checked the log of the benchmark tests and found this error came out when timelimit was reached. Let me try to fix it before merging this PR. Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The KeyError happens when no feasible solution has been found within the timelimit. I added one more condition for the fix_dual_bound function in the OA, GOA, LP/NLP B&B and GLP/NLP B&B methods.
I also removed fix_dual_bound for the ECP method since we will never add no-good cuts in the ECP method.
I would appreciate it if @bernalde can give it a double-check.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, okay. I think a different error message might be clearer then--it's not deactivating the cuts that failed, it's the solve after you deactivated them (if I'm understanding correctly).

7 changes: 5 additions & 2 deletions pyomo/contrib/mindtpy/outer_approximation.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,12 @@ def check_config(self):
_MindtPyAlgorithm.check_config(self)

def initialize_mip_problem(self):
'''Deactivate the nonlinear constraints to create the MIP problem.'''
"""Deactivate the nonlinear constraints to create the MIP problem."""
super().initialize_mip_problem()
self.jacobians = calc_jacobians(self.mip, self.config) # preload jacobians
self.jacobians = calc_jacobians(
self.mip.MindtPy_utils.nonlinear_constraint_list,
self.config.differentiate_mode,
) # preload jacobians
self.mip.MindtPy_utils.cuts.oa_cuts = ConstraintList(
doc='Outer approximation cuts'
)
Expand Down
115 changes: 41 additions & 74 deletions pyomo/contrib/mindtpy/single_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
from pyomo.repn import generate_standard_repn
import pyomo.core.expr as EXPR
from math import copysign
from pyomo.contrib.mindtpy.util import get_integer_solution
from pyomo.contrib.gdpopt.util import (
from pyomo.contrib.mindtpy.util import (
get_integer_solution,
copy_var_list_values,
get_main_elapsed_time,
time_code,
set_var_valid_value,
)
from pyomo.contrib.gdpopt.util import get_main_elapsed_time, time_code
from pyomo.opt import TerminationCondition as tc
from pyomo.core import minimize, value
from pyomo.core.expr import identify_variables
Expand All @@ -35,17 +35,9 @@ class LazyOACallback_cplex(
"""Inherent class in CPLEX to call Lazy callback."""

def copy_lazy_var_list_values(
self,
opt,
from_list,
to_list,
config,
skip_stale=False,
skip_fixed=True,
ignore_integrality=False,
self, opt, from_list, to_list, config, skip_stale=False, skip_fixed=True
):
"""This function copies variable values from one list to another.

Rounds to Binary/Integer if necessary.
Sets to zero for NonNegativeReals if necessary.

Expand All @@ -54,61 +46,29 @@ def copy_lazy_var_list_values(
opt : SolverFactory
The cplex_persistent solver.
from_list : list
The variables that provides the values to copy from.
The variable list that provides the values to copy from.
to_list : list
The variables that need to set value.
The variable list that needs to set value.
config : ConfigBlock
The specific configurations for MindtPy.
skip_stale : bool, optional
Whether to skip the stale variables, by default False.
skip_fixed : bool, optional
Whether to skip the fixed variables, by default True.
ignore_integrality : bool, optional
Whether to ignore the integrality of integer variables, by default False.
"""
for v_from, v_to in zip(from_list, to_list):
if skip_stale and v_from.stale:
continue # Skip stale variable values.
if skip_fixed and v_to.is_fixed():
continue # Skip fixed variables.
v_val = self.get_values(opt._pyomo_var_to_solver_var_map[v_from])
try:
# We don't want to trigger the reset of the global stale
# indicator, so we will set this variable to be "stale",
# knowing that set_value will switch it back to "not
# stale"
v_to.stale = True
# NOTE: PEP 2180 changes the var behavior so that domain
# / bounds violations no longer generate exceptions (and
# instead log warnings). This means that the following
# will always succeed and the ValueError should never be
# raised.
v_to.set_value(v_val, skip_validation=True)
except ValueError as e:
# Snap the value to the bounds
config.logger.error(e)
if (
v_to.has_lb()
and v_val < v_to.lb
and v_to.lb - v_val <= config.variable_tolerance
):
v_to.set_value(v_to.lb, skip_validation=True)
elif (
v_to.has_ub()
and v_val > v_to.ub
and v_val - v_to.ub <= config.variable_tolerance
):
v_to.set_value(v_to.ub, skip_validation=True)
# ... or the nearest integer
elif v_to.is_integer():
rounded_val = int(round(v_val))
if (
ignore_integrality
or abs(v_val - rounded_val) <= config.integer_tolerance
) and rounded_val in v_to.domain:
v_to.set_value(rounded_val, skip_validation=True)
else:
raise
set_var_valid_value(
v_to,
v_val,
config.integer_tolerance,
config.zero_tolerance,
ignore_integrality=False,
)

def add_lazy_oa_cuts(
self,
Expand Down Expand Up @@ -309,12 +269,11 @@ def add_lazy_affine_cuts(self, mindtpy_solver, config, opt):
try:
mc_eqn = mc(constr.body)
except MCPP_Error as e:
config.logger.error(e, exc_info=True)
config.logger.debug(
'Skipping constraint %s due to MCPP error %s'
% (constr.name, str(e))
'Skipping constraint %s due to MCPP error' % (constr.name)
)
continue # skip to the next constraint
# TODO: check if the value of ccSlope and cvSlope is not Nan or inf. If so, we skip this.
ccSlope = mc_eqn.subcc()
cvSlope = mc_eqn.subcv()
ccStart = mc_eqn.concave()
Expand Down Expand Up @@ -706,10 +665,11 @@ def __call__(self):
main_mip = self.main_mip
mindtpy_solver = self.mindtpy_solver

# The lazy constraint callback may be invoked during MIP start processing. In that case get_solution_source returns mip_start_solution.
# Reference: https://www.ibm.com/docs/en/icos/22.1.1?topic=SSSA5P_22.1.1/ilog.odms.cplex.help/refpythoncplex/html/cplex.callbacks.SolutionSource-class.htm
# Another solution source is user_solution = 118, but it will not be encountered in LazyConstraintCallback.
config.logger.debug(
"Solution source: %s (111 node_solution, 117 heuristic_solution, 119 mipstart_solution)".format(
config.logger.info(
"Solution source: {} (111 node_solution, 117 heuristic_solution, 119 mipstart_solution)".format(
self.get_solution_source()
)
)
Expand All @@ -718,6 +678,7 @@ def __call__(self):
# Lazy constraints separated when processing a MIP start will be discarded after that MIP start has been processed.
# This means that the callback may have to separate the same constraint again for the next MIP start or for a solution that is found later in the solution process.
# https://www.ibm.com/docs/en/icos/22.1.1?topic=SSSA5P_22.1.1/ilog.odms.cplex.help/refpythoncplex/html/cplex.callbacks.LazyConstraintCallback-class.htm
# For the MINLP3_simple example, all the solutions are obtained from mip_start (solution source). Therefore, it will not go to a branch and bound process.Cause an error output.
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment is to mention some reasons why we trigger tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be more precise, the algorithm will terminate during the MIP start process as soon as the bounds converge. In such scenarios, we utilize self.abort() to terminate CPLEX. As a consequence, the results.solver.status will be set to error, accompanied by an error message. Our initial choice to use self.abort() was driven by the need for immediate termination upon meeting the convergence criteria. However, I am currently uncertain about the extent of the difference it would make if we allowed CPLEX to terminate on its own.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the error will be captured by the try except command. We don't need to worry about it and it is still the most efficient way to terminate CPLEX. I add a TODO here. We can decide based on the benchmark results in the future.

if (
self.get_solution_source()
!= cplex.callbacks.SolutionSource.mipstart_solution
Expand All @@ -728,6 +689,7 @@ def __call__(self):
mindtpy_solver.mip_start_lazy_oa_cuts = []

if mindtpy_solver.should_terminate:
# TODO: check the performance difference if we don't use self.abort() and let cplex terminate by itself.
self.abort()
return
self.handle_lazy_main_feasible_solution(main_mip, mindtpy_solver, config, opt)
Expand All @@ -745,9 +707,9 @@ def __call__(self):
mindtpy_solver.mip, None, mindtpy_solver, config, opt
)
except ValueError as e:
config.logger.error(e, exc_info=True)
config.logger.error(
str(e)
+ "\nUsually this error is caused by the MIP start solution causing a math domain error. "
"Usually this error is caused by the MIP start solution causing a math domain error. "
"We will skip it."
)
return
Expand Down Expand Up @@ -783,6 +745,7 @@ def __call__(self):
)
)
mindtpy_solver.results.solver.termination_condition = tc.optimal
# TODO: check the performance difference if we don't use self.abort() and let cplex terminate by itself.
self.abort()
return

Expand Down Expand Up @@ -910,19 +873,7 @@ def LazyOACallback_gurobi(cb_m, cb_opt, cb_where, mindtpy_solver, config):
if mindtpy_solver.dual_bound != mindtpy_solver.dual_bound_progress[0]:
mindtpy_solver.add_regularization()

if (
abs(mindtpy_solver.primal_bound - mindtpy_solver.dual_bound)
<= config.absolute_bound_tolerance
):
config.logger.info(
'MindtPy exiting on bound convergence. '
'|Primal Bound: {} - Dual Bound: {}| <= (absolute tolerance {}) \n'.format(
mindtpy_solver.primal_bound,
mindtpy_solver.dual_bound,
config.absolute_bound_tolerance,
)
)
mindtpy_solver.results.solver.termination_condition = tc.optimal
if mindtpy_solver.bounds_converged() or mindtpy_solver.reached_time_limit():
cb_opt._solver_model.terminate()
return

Expand Down Expand Up @@ -953,15 +904,31 @@ def LazyOACallback_gurobi(cb_m, cb_opt, cb_where, mindtpy_solver, config):
)
return
elif config.strategy == 'OA':
# Refer to the official document of GUROBI.
# Your callback should be prepared to cut off solutions that violate any of your lazy constraints, including those that have already been added. Node solutions will usually respect previously added lazy constraints, but not always.
# https://www.gurobi.com/documentation/current/refman/cs_cb_addlazy.html
# If this happens, MindtPy will look for the index of corresponding cuts, instead of solving the fixed-NLP again.
begin_index, end_index = mindtpy_solver.integer_solution_to_cuts_index[
mindtpy_solver.curr_int_sol
]
for ind in range(begin_index, end_index + 1):
cb_opt.cbLazy(mindtpy_solver.mip.MindtPy_utils.cuts.oa_cuts[ind])
return
else:
mindtpy_solver.integer_list.append(mindtpy_solver.curr_int_sol)
if config.strategy == 'OA':
cut_ind = len(mindtpy_solver.mip.MindtPy_utils.cuts.oa_cuts)

# solve subproblem
# The constraint linearization happens in the handlers
fixed_nlp, fixed_nlp_result = mindtpy_solver.solve_subproblem()

mindtpy_solver.handle_nlp_subproblem_tc(fixed_nlp, fixed_nlp_result, cb_opt)
if config.strategy == 'OA':
# store the cut index corresponding to current integer solution.
mindtpy_solver.integer_solution_to_cuts_index[
mindtpy_solver.curr_int_sol
] = [cut_ind + 1, len(mindtpy_solver.mip.MindtPy_utils.cuts.oa_cuts)]


def handle_lazy_main_feasible_solution_gurobi(cb_m, cb_opt, mindtpy_solver, config):
Expand Down
4 changes: 2 additions & 2 deletions pyomo/contrib/mindtpy/tests/MINLP_simple_grey_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ def evaluate_jacobian_equality_constraints(self):
"""Evaluate the Jacobian of the equality constraints."""
return None

'''
"""
def _extract_and_assemble_fim(self):
M = np.zeros((self.n_parameters, self.n_parameters))
for i in range(self.n_parameters):
for k in range(self.n_parameters):
M[i,k] = self._input_values[self.ele_to_order[(i,k)]]

return M
'''
"""

def evaluate_jacobian_outputs(self):
"""Evaluate the Jacobian of the outputs."""
Expand Down
1 change: 1 addition & 0 deletions pyomo/contrib/mindtpy/tests/test_mindtpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ def test_OA_APPSI_ipopt(self):
value(model.objective.expr), model.optimal_value, places=1
)

# CYIPOPT will raise WARNING (W1002) during loading solution.
@unittest.skipUnless(
SolverFactory('cyipopt').available(exception_flag=False),
"APPSI_IPOPT not available.",
Expand Down
Loading
Loading