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

Bounded numerical value with shifted prior #361

Closed
Corwinpro opened this issue Dec 9, 2019 · 8 comments
Closed

Bounded numerical value with shifted prior #361

Corwinpro opened this issue Dec 9, 2019 · 8 comments

Comments

@Corwinpro
Copy link
Contributor

Corwinpro commented Dec 9, 2019

I am exploring a possibility to implement a simple instrumentation with the following functionality. We create a bounded Scalar variable, which default is within the bounds but not centralised between the bounds. Would that be possible to do?

Steps to reproduce

import nevergrad as ng
var = ng.var.Scalar().affined(1, -0.5).bounded(-1, 2)
args, kwargs = var.data_to_arguments([0] * var.dimension)
print(args, kwargs)

Observed Results

The resulting args, kwargs are ((0.057249147048700155,), {}), which is not directly related to the affined transformation.

Expected Results

The expected result would be ((-0.5,), {}), although the current implementation is definitely doing what it is suppose to be doing.

Question

What is the preferred way to have a "shifted" bounded Scalar variable in nevergrad? It has been mentioned in the doc file but without an example code.

@jrapin
Copy link
Contributor

jrapin commented Dec 10, 2019

Hi,
Thanks for raising the issue ;)
For now shifting (through affined) and bounds do not play well together (at all), because the default implementation of bounds actually recenters everything indeed. You could use the clipping method for bounds (bounded(-1, 2, transform="clipping")) which should work better, or even use a constraint

def set_cheap_constraint_checker(self, func: Callable[..., bool]) -> None:

I think constraints will be the way to go in the future, but for now it's still experimental and barely documented...

In practice I do find that the current implementation completely misses the point of being easy to use, I'm currently working on a huge refactor of the instrumentation #323 which will hopefully come with a simpler API (and a lot more of abilities). If you have ideas of how you would like to be able to specify your prior, let me know, I'm looking for ideas ;)

@Corwinpro
Copy link
Contributor Author

Corwinpro commented Dec 11, 2019

Thank you @jrapin , I looked at bounded, clipping and set_cheap_constraint methods, and they are really great and easy to use.

After I had a chance to think about it one more time, it seems that doing

var = ng.var.Scalar().affine(a, b).bounded(c, d)

should do what I am aiming at. The values of c and d are obviously just the constraints I impose on the parameter space. The values of a and b (if chosen properly) will provide a correct mapping between R->R, and then tanh transformation will be effectively shifted on the interval [c, d].

For instance, if I want to prioritise value e \in [c, d], then

b = tanh^{-1} ( (e - (c+d)/2) * 2/(b-a) )

@jrapin
Copy link
Contributor

jrapin commented Dec 13, 2019

I still find that having to come up with such an equation is cumbersome :D
In the next version (coming from #323, but still a lot of work to do before it's actually plugged into the optimizers), I'm currently aiming for something like:
Scalar(initial_value).set_mutation(sigma=a).set_bounds(c, d)
would that work, for you?
And I'll probably change to clipping as default, I'm a bit concerned with ease of use of arctan and tanh transforms (they make interpretation of mutation quite hard, and they don't mix that well with logarithmic scaling)

@Corwinpro
Copy link
Contributor Author

Thank you, I like the idea of setting the initial value and sigma. It sounds like a robust approach of mapping between the internal space and user argument.

I don't know exactly why this happens, but this is why I decided not to use the clipping approach: the search values are very much shifted towards the clipping boundaries. I assume this is because the sampling distribution in the internal space doesn't know the location of the clipping region and therefore almost always overshoots. For instance, it I use .bounded(100, 101, transform="clipping"), most of the points the algorithm explores are either 100 or 101.

@jrapin
Copy link
Contributor

jrapin commented Dec 13, 2019

most of the points the algorithm explores are either 100 or 101.

I would expect this since the width of your space is smaller than let's say 5 sigma (by default the standard deviation of the mutation is basically 1, if you don't use "affined")
Also, the initial value is 0, so this will be clipped directly to 100. I would even expect that all points in your initial population if you're using a population based algorithm (aka DE, CMA, TBPSA etc for instance) would be clipped to 100, which is a big issue.
I think

ng.var.Scalar().affined(0.1, 100.5).bounded(100, 101, transform="clipping")

would do what you want:

s.data_to_arguments([-3])
>>> ((100.2,), {})

As you can see, it's not so intuitive, happy that you like the new approach idea. In this future approach, you would have to write:

ng.var.Scalar(100.5).set_mutation(sigma=0.1).set_bounds(100, 101)

It's the same exact same numbers in the end, but probably more explicit. And that will come with a whole bunch of powerful features :D

@Corwinpro
Copy link
Contributor Author

Corwinpro commented Dec 13, 2019

Sounds great, thank you @jrapin . Just one more thing to mention: how would you expect this to work? Should it still be alright, considering the bias is not centralised relatively to the bounds?

ng.var.Scalar(100.01).set_mutation(sigma=0.1).set_bounds(100, 101)

@jrapin
Copy link
Contributor

jrapin commented Dec 13, 2019

it should work somehow, but many points will be similar at the bounds from the start, so it may be dangerous depending on what you want.
If having strictly identical values is a problem, then the following should work (at least in dimension 1, at very high dimension it may become very inefficient due to numereous try and errors to find mutations which satisfy the constraint):

ng.var.Scalar(100.01).set_mutation(sigma=0.1).set_bounds(100, 101, method="constraint")

@jrapin
Copy link
Contributor

jrapin commented Jan 27, 2020

From #391 the following should now work:

ng.p.Scalar(100.01).set_mutation(sigma=0.1).set_bounds(100, 101, method="constraint")

(notice this is the new scalar class in ng.p, not the one in ng.var)
I'm closing this issue then, please don't hesitate to reopen if need be!

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

No branches or pull requests

2 participants