Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@mdhaber
Copy link
Contributor

@mdhaber mdhaber commented Apr 10, 2023

This adds support for the methods parameter of minimize_ipopt.

To do:

  • Figure out why method=None (ipopt) fails
  • Test hessp
  • Test bounds
  • Test callback
  • Test tol?
  • Test options?

Copy link
Contributor Author

@mdhaber mdhaber left a comment

Choose a reason for hiding this comment

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

Some self-review to aid the review.

Comment on lines +326 to +349
def _wrap_fun(fun, kwargs):
if callable(fun) and kwargs:
def new_fun(x, *args):
return fun(x, *args, **kwargs)
else:
new_fun = fun
return new_fun

def _wrap_funs(fun, jac, hess, hessp, constraints, kwargs):
wrapped_fun = _wrap_fun(fun, kwargs)
wrapped_jac = _wrap_fun(jac, kwargs)
wrapped_hess = _wrap_fun(hess, kwargs)
wrapped_hessp = _wrap_fun(hessp, kwargs)
if isinstance(constraints, dict):
constraints = (constraints,)
wrapped_constraints = []
for constraint in constraints:
constraint = constraint.copy()
ckwargs = constraint.pop('kwargs', {})
constraint['fun'] = _wrap_fun(constraint.get('fun', None), ckwargs)
constraint['jac'] = _wrap_fun(constraint.get('jac', None), ckwargs)
wrapped_constraints.append(constraint)
return (wrapped_fun, wrapped_jac, wrapped_hess, wrapped_hessp,
wrapped_constraints)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

minimize does not natively support kwargs like minimize_ipopt, so we have to do some wrapping. This is why the tests heavily emphasize the callable arguments (and I didn't test bounds, options, etc.).

There is one other callable, callback, but that doesn't need to be wrapped because it does not accept additional arguments.

Copy link
Collaborator

@moorepants moorepants Apr 10, 2023

Choose a reason for hiding this comment

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

The kwargs haven't ever worked before, I added them in the recent PR #197. If it makes more sense to disable the support for kwargs, we can.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, they work now, so no harm leaving them unless you want to deprecate them and remove the parameter altogether.

Copy link
Collaborator

@moorepants moorepants Apr 10, 2023

Choose a reason for hiding this comment

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

They don't need to be deprecated because we haven't made a new release yet. If the goal is to match the scipy api, then maybe we should stick to that. Otherwise, I'm open for whatever people need.

Copy link
Contributor Author

@mdhaber mdhaber Apr 10, 2023

Choose a reason for hiding this comment

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

I know it didn't work before, but it looks like it was already present as a positional/keyword argument in a previous release (e.g. 1.0.3). For SciPy, that would mean it needs to be deprecated before it can be removed because working user code may pass it as an argument even though it does nothing. But if you are comfortable with the (probably rare) immediate disruption of removing it (e.g. now be an error if it is passed), I understand.

I'd rather not weigh in on that decision, though. (If I had my way, minimize would have neither args nor kwargs, jac would be grad - see scipy/scipy#18249, etc...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@moorepants since it was already present as an argument in previous releases, do you agree that it would need to be deprecated? In light of that, would you like to just keep it?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm fine keeping it, but we should document how we deviate from scipy's minimize function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a bit at the top of the documentation.

Please don't keep kwargs on my account, though. As I mentioned, I am almost always against accepting args and kwargs just to be passed through to functions. I would prefer that standard Python features be used to accomplish the same thing (e.g. def, lambda, or functools.partial to wrap the callable). I've supported them here because whether they worked or not, they have been part of the signature for at least a few releases.

Comment on lines 17 to 20
# Hard-code rather than importing from scipy.optimize._minimize in a try/except
MINIMIZE_METHODS = ['nelder-mead', 'powell', 'cg', 'bfgs', 'newton-cg',
'l-bfgs-b', 'tnc', 'cobyla', 'slsqp', 'trust-constr',
'dogleg', 'trust-ncg', 'trust-exact', 'trust-krylov']
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would you prefer:

Suggested change
# Hard-code rather than importing from scipy.optimize._minimize in a try/except
MINIMIZE_METHODS = ['nelder-mead', 'powell', 'cg', 'bfgs', 'newton-cg',
'l-bfgs-b', 'tnc', 'cobyla', 'slsqp', 'trust-constr',
'dogleg', 'trust-ncg', 'trust-exact', 'trust-krylov']
try:
from scipy.optimize._minimize import MINIMIZE_METHODS
except ImportError:
pass

?

Copy link
Collaborator

Choose a reason for hiding this comment

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

We'll have to maintain this hard coded list if things change in scipy, so I think I'd prefer the try clause. Is there a reason you hardcoded?

Copy link
Contributor Author

@mdhaber mdhaber May 20, 2023

Choose a reason for hiding this comment

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

Only to avoid importing from a private module. But I don't think that's much of a concern to me anymore. I'll change it.

self.obj_hess = hess
if jac is None:
jac = lambda x0, *args, **kwargs: approx_fprime(
x0, fun, eps, *args, **kwargs)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

approx_fprime doesn't accept kwargs

@mdhaber
Copy link
Contributor Author

mdhaber commented May 18, 2023

OK, I finished the remaining tests and fixed a bug with use of approx_derivative with kwargs. I think this is ready for review. Thanks @moorepants!

@mdhaber mdhaber requested a review from moorepants May 18, 2023 23:23
if method in constr_methods:
np.testing.assert_allclose(res.x[2:], [c0, d0], rtol=1e-3)
else:
np.testing.assert_allclose(res.x[2:], 0, atol=1e-3)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm curious why the tolerances in these checks are quite large.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Think it was because of COBYLA. I'll see if they can be tighter for other methods.

Copy link
Contributor Author

@mdhaber mdhaber May 20, 2023

Choose a reason for hiding this comment

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

Yeah a few solvers have relative or absolute errors up to the order of 1e-5. I tightened the tolerances to 5e-5 across the board and made them variables so they can be adjusted more easily. I don't think it's worth it to have different tolerances for each part of the test / every method, and 5e-5 is about the tightest we can do and have everything pass.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok, that's fine.

@moorepants
Copy link
Collaborator

This looks good. One comment and I merged the other PR so there are some conflicts here now.

@mdhaber
Copy link
Contributor Author

mdhaber commented May 20, 2023

Thanks! Addressed the comments and merged master. Squash merge is fine with me; I don't think there's much worth preserving about the commits.

@moorepants
Copy link
Collaborator

Thanks!

@moorepants moorepants merged commit ade9319 into mechmotum:master May 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants