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

check that initial guesses are within bounds. Fixes #355 #358

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

Peter230655
Copy link
Contributor

A method, called from solve to ensure that the initial guesses are within their respective bounds. As such a conflict is not fatal for opty, a UserWarning is raised. Should take care of issue #355

@Peter230655
Copy link
Contributor Author

Adjusted as per the suggestions given.

@Peter230655
Copy link
Contributor Author

Peter230655 commented Feb 13, 2025

If I read it correctly, about 10 examples seem to give problems.
What I could do is this:

  • set the kwarg to False
  • over the next so many days, I could look at the offending examples, and set their kwarg to False?

Or what would you suggest?

@moorepants
Copy link
Member

My suggestion was made above:

We'll have to update any examples or tests to accommodate this. Either by changing initial guess (probably best idea) or setting respect_bounds=False.

@moorepants
Copy link
Member

Good looking error message!

ValueError: The initial guesses for [c, k] are in conflict with their bounds.

@Peter230655
Copy link
Contributor Author

Good looking error message!

ValueError: The initial guesses for [c, k] are in conflict with their bounds.

Thanks!
I wasn't sure whether these brackets [....] would be o.k. - but frankly I could not remove them.

@Peter230655
Copy link
Contributor Author

My suggestion was made above:

We'll have to update any examples or tests to accommodate this. Either by changing initial guess (probably best idea) or setting respect_bounds=False.

I will go over the examples in the coming days, try to fix them and push them if fixed.

@@ -249,9 +254,58 @@ def solve(self, free, lagrange=[], zl=[], zu=[]):
gives the status of the algorithm as a message

"""

if respect_bounds == True:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if respect_bounds == True:
if respect_bounds:

@Peter230655
Copy link
Contributor Author

Peter230655 commented Feb 15, 2025

I proceeded as follows:

  • Where possible, I adjusted the initial guesses to meet the bounds.
  • where solutions of previous runs were used as initial guesses, I opened the bounds slightly to allow for numerical errors in the solutions.
  • plot_park2004.py: Here the initial guesses for the gain factors k00.... k13 were very far from their bounds. When I set them to meet the bounds, the results seemed to be a bit worse. So here I set recpect_bounds=False
  • plot_human_trait.py. Here the initial guess for the earth problem was some previous solution. Its solution is used as the initial guess for the moon problem. The earth solution seemed far outside the bounds. So, I used the initial guess for the earth problem also for the moon problem

@Peter230655
Copy link
Contributor Author

This test has been running for nearly 2 1/2 hours.
Is this normal, or did something happen?

@Peter230655
Copy link
Contributor Author

seems to run too long, will start again.

@Peter230655 Peter230655 reopened this Feb 15, 2025
@moorepants
Copy link
Member

it seems that conda cannot resolve the environment on windows with python 3.10.

yeadon                    1.5.0              pyhd8ed1ab_2    conda-forge
zstandard                 0.23.0          py310he5e10e1_1    conda-forge
zstd                      1.5.6                h0ea2cb4_0    conda-forge
CI detected...
Channels:
 - conda-forge
 - defaults
Platform: win-64
Collecting package metadata (repodata.json): ...working... done
Solving environment: ...working... 
Error: The operation was canceled.

I don't know why that would be, as it has worked before.

@moorepants
Copy link
Member

Peter, since you are on windows can you make this edit to your conda env file and see if it works locally when creating the environment:

name: opty-dev
channels:
  - conda-forge
dependencies:
  # build
  - setuptools
  # run (includes optional run deps)
  - cyipopt >=1.1.0
  - cython >=0.29.19
  - matplotlib >=3.2.0
  - numpy >=1.19.0
  - python =3.10
  - scipy >=1.5.0
  - sympy >=1.13  # version required for the docs
  # dev
  - coverage
  - ipython
  - pytest
  - pytest-cov
  # docs/examples
  - joblib
  - numpydoc
  - pip <24  # pin required for the gait2d pip install below
  - pydy >=0.5.0  # gait2d
  - pyyaml  # gait2d
  - sphinx
  - sphinx-gallery
  - sphinx-reredirects
  - symmeplot
  - yeadon
  - pip:
    - -e git+https://github.com/csu-hmc/gait2d#egg=gait2d

@Peter230655
Copy link
Contributor Author

Peter, since you are on windows can you make this edit to your conda env file and see if it works locally when creating the environment:

name: opty-dev
channels:
  - conda-forge
dependencies:
  # build
  - setuptools
  # run (includes optional run deps)
  - cyipopt >=1.1.0
  - cython >=0.29.19
  - matplotlib >=3.2.0
  - numpy >=1.19.0
  - python =3.10
  - scipy >=1.5.0
  - sympy >=1.13  # version required for the docs
  # dev
  - coverage
  - ipython
  - pytest
  - pytest-cov
  # docs/examples
  - joblib
  - numpydoc
  - pip <24  # pin required for the gait2d pip install below
  - pydy >=0.5.0  # gait2d
  - pyyaml  # gait2d
  - sphinx
  - sphinx-gallery
  - sphinx-reredirects
  - symmeplot
  - yeadon
  - pip:
    - -e git+https://github.com/csu-hmc/gait2d#egg=gait2d

So I understand correctly:

  • the above is a .yml file.
  • I store it somewhere on my PC
  • then I create a new environment, called opty-dev, using this .yml file like it is described in opty,
  • OR would I use this .yml file to update my existing opty-dev environment? This I do not know how to do.

Thanks!

@moorepants
Copy link
Member

You can do either, but I only changed this line - python =3.10 so it would install python 3.10.

@Peter230655
Copy link
Contributor Author

Peter230655 commented Feb 16, 2025

You can do either, but I only changed this line - python =3.10 so it would install python 3.10.

So to update I proceed as follows?

  1. I take the yml file above and store it on my PC as, say, update.yml
  2. I open the anaconda promt and put activate opty-dev
  3. then I go to the folder ``cd path/to/folder
  4. then I put conda env update -f update.yml

@moorepants
Copy link
Member

That will work but only if you first remove the existing opty-dev conda environment.

@moorepants
Copy link
Member

moorepants commented Feb 16, 2025

Sorry, nevermind, if you use "update" then it should be fine. You can also simple add python =3.10 to the yml file you already have in the repo.

@Peter230655
Copy link
Contributor Author

Peter230655 commented Feb 16, 2025

Sorry, nevermind, if you use "update" then it should be fine. You can also simple add python =3.10 to the yml file you already have in the repo.

This looks easiest for me! Then, when I push, this changed .yml file will also be pushed. Is this o.k.? OIr is this actually what we want to test?

@Peter230655
Copy link
Contributor Author

Can you create the environment from scratch, not simply update. It is failing to create the environment from scratch in CI.

NO problem! I will use the .yml file in opty, right?

@Peter230655
Copy link
Contributor Author

Peter230655 commented Feb 16, 2025

I built from scratch as follows:

  • I deleted the environment opty-dev
  • I created a new one as explained in the opty documentation. I used the original .yml file there, which is python 3.13.

This took about 90 sec.
Should I do the same thing but change line 12 there from python to python =3.10 ?

@moorepants
Copy link
Member

Should I do the same thing but change line 12 there from python to python =3.10 ?

Yes, you have to set python = 3.10 to do the same thing in the failing CI build.

@Peter230655
Copy link
Contributor Author

Should I do the same thing but change line 12 there from python to python =3.10 ?

Yes, you have to set python = 3.10 to do the same thing in the failing CI build.

Clear, will do so. Right now I managed once again to mess up everything :-((
Hopefully I'll fix it tomorrow morning, and report about the timing.

@moorepants
Copy link
Member

If you merge master the tests should run correctly again. I fixed it in #363

@Peter230655
Copy link
Contributor Author

Worked !! :-)

----------
free : array-like, shape(n*N + q*N + r + s, )
Initial guess given to solve.

Copy link
Member

Choose a reason for hiding this comment

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

You can add a "Raises" section to describe the ValueError. See the numpydoc documentation.

Copy link
Contributor Author

@Peter230655 Peter230655 Feb 18, 2025

Choose a reason for hiding this comment

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

You can add a "Raises" section to describe the ValueError. See the numpydoc documentation.

I assume you meant this section:
image

violating_variables.append(local_ts)

symbole = self.collocator.state_symbols + \
self.collocator.unknown_input_trajectories
Copy link
Member

Choose a reason for hiding this comment

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

bit better to do:

a = (longgggggggggggggggggggggg +
     longgggggggggggggggggggggggggg)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

bit better to do:

a = (longgggggggggggggggggggggg +
     longgggggggggggggggggggggggggg)

So, parenthesis are always preferred over \ ?

Copy link
Member

Choose a reason for hiding this comment

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

according to pep 8 it is

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!
I would have thought the other way around, I will remember in future.


if len(violating_variables) > 0:
msg = f'The initial guesses for {violating_variables} are in '+\
f'conflict with their bounds.'
Copy link
Member

Choose a reason for hiding this comment

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

strings don't need \ if you do this:

msg = (f'sstringgggggggggggggggggggggggg'
       f'anotherstringgggggg')

par_map = {b1: 1.0,
b2: 2.0,
b3: 3.0
}
Copy link
Member

Choose a reason for hiding this comment

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

preferred formating for dictionaries is:

a = {
    'b': 1,
    'c': 2,
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean 'b1' instead of b1 or you mena the location of } ?

Copy link
Member

Choose a reason for hiding this comment

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

I mean the location of the parentheses and the indentation. Some examples here: https://peps.python.org/pep-0008/#code-lay-out

uy: (-1.0, 1.0),
z: (-1.0, 1.0),
uz: (-1.0, 1.0),
}
Copy link
Member

Choose a reason for hiding this comment

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

This case seems to have all unknown variables with bounds. Do you tests cases where bounds are not given for some (or all)?

Copy link
Contributor Author

@Peter230655 Peter230655 Feb 18, 2025

Choose a reason for hiding this comment

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

This case seems to have all unknown variables with bounds. Do you tests cases where bounds are not given for some (or all)?

I will check. I will also check, what happens with 'reversed' bounds

)

bounds= {
a1: (-1.0, 1.0),
Copy link
Member

@moorepants moorepants Feb 18, 2025

Choose a reason for hiding this comment

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

What would happen if this was? a1: (1.0, -1.0)? I guess IPOPT may throw an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My method would not care, about IPOPT do not know, never tried.
Would this not be a completely nonstandard way to give an interval?

Copy link
Member

Choose a reason for hiding this comment

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

I'm just trying to imagine what inputs may break your function (and thus should be written as tests).

Copy link
Contributor Author

@Peter230655 Peter230655 Feb 18, 2025

Choose a reason for hiding this comment

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

I'm just trying to imagine what inputs may break your function (and thus should be written as tests).

Sorry, I mixed this up my PR for create_linear_initial_guess
With 'reversed' bounds it would not work, but easy to fix. I never thought of this.
(Maybe subconsciously I assumed your guide: garbage in - garbage out... :-) )

with raises(ValueError):
_, _ = prob.solve(initial_guess, respect_bounds=True)

# D: respect_bounds=False by default. Bounds are ignored.
Copy link
Member

Choose a reason for hiding this comment

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

you could set max iterations to 1 or 2 to save computation time when solve actually runs.

@moorepants
Copy link
Member

This looks very good. I left a few minor comments, but they aren't necessary to fix for merging.

The only major comment is that you probably need a few more unit tests to check some variation on what type of bounds were set.

@Peter230655
Copy link
Contributor Author

This looks very good. I left a few minor comments, but they aren't necessary to fix for merging.

The only major comment is that you probably need a few more unit tests to check some variation on what type of bounds were set.

Thanks!

I just checked an example (your drone) with 'reversed' bounds: {F: (100, -100)}
It did not break, but did not get a reasonable solution at all.
So, if I detect a reversed bound, should I raise an error? If YES, what error? ValueError?

@moorepants
Copy link
Member

I would have thought that IPOPT would raise an error. That makes me wonder why reverse bounds is allowed for IPOPT. Maybe there is a reason you'd want that.

For this PR, we only need to ensure that your new functionality is well tested. Do we want to support reverse bounds with this functionality? If not, we should probably raise an error if reverse bounds are detected. A ValueError sounds correct.

@Peter230655
Copy link
Contributor Author

I would have thought that IPOPT would raise an error. That makes me wonder why reverse bounds is allowed for IPOPT. Maybe there is a reason you'd want that.

For this PR, we only need to ensure that your new functionality is well tested. Do we want to support reverse bounds with this functionality? If not, we should probably raise an error if reverse bounds are detected. A ValueError sounds correct.

My method will not work for reversed bounds. (I must admit I did not think of this possibility)
So, I will have to check for this possibility.
At least with opty, I have never come across a situation where I would have needed reversed bounds.
So, maybe better for the opty user to get a ValueError, as it likely will not work, even if IPOPT allows it?

@moorepants
Copy link
Member

So, maybe better for the opty user to get a ValueError, as it likely will not work, even if IPOPT allows it?

I'm fine with opty telling you no for this.

@Peter230655
Copy link
Contributor Author

So, maybe better for the opty user to get a ValueError, as it likely will not work, even if IPOPT allows it?

I'm fine with opty telling you no for this.

I will do it like this. Maybe here the numpydocs raises could be used to explain why the ValueError is raised?

@moorepants
Copy link
Member

Yes, that is the purpose of that documentation section.

@Peter230655
Copy link
Contributor Author

Peter230655 commented Feb 19, 2025

I now check for reversed bounds before I check whether the initial guess meets the bounds. If there is at least one reversed bound a ValueError will be raised.
I test separately for reversed bounds and for violation of the bounds by the initial guess anf for the working of the kwarg in solve.

Can you somehow ignore all these changed simulations? I changed them, when we decided to set the kwarg of solve to True by default, so I tried to adjust the examples accordingly. When we decided to set it to False, I thought I changed them all back, but apparently this did not work.....

@moorepants
Copy link
Member

What is the status of this PR? Is it ready for a review?

Looks like merge conflicts need to be resolved.

@Peter230655
Copy link
Contributor Author

What is the status of this PR? Is it ready for a review?

Looks like merge conflicts need to be resolved.

Yes, ready.
Let me try and fix the conflicts and I will push again.

@Peter230655
Copy link
Contributor Author

Tried to fix the conflict.

@moorepants
Copy link
Member

There are still outstanding comments from my first review that haven't been addressed.

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