Git Product home page Git Product logo

Comments (24)

zoq avatar zoq commented on July 26, 2024 1

I see what you mean; there are two main problems I have with putting the bounds into the problem:

  1. The current function/problem interface is super simple, in the simplest form, you just have to implement Evaluate or EvaluateConstraints, putting the bounds in the function adds two more functions to the interface.
  2. Refactoring, currently all the parameters are attached to the optimizer, so le'ts say we put the bounds into the function class, at the same time we want to keep the same interface across the codebase this means we would have to refactor all optimizers that currently take the bounds as parameter.

I guess we could solve the problem for the multi-objective case if we pass a vector or something similar to the optimizer. Not sure this is the best idea, but interested to hear what others think about the issue.

from ensmallen.

rcurtin avatar rcurtin commented on July 26, 2024 1

I think that the bounds to be searched for the solution should be a parameter to the optimizer. The reason I say this is because if I have a function f(x) = 2 * x and I want to find the minimum... whether or not I want to bound my search is not related to the function itself; it is related to how I choose to use the optimizer.

Now, in #249 I refactored many tests to move various aspects of the problem (including the solution) into each of the problem classes. I didn't do this with any of the multi-objective objective functions, but I think that could be an improvement. In that setting you could add the lower and upper bounds to use to the problems in problems/, but then in the tests you would extract the bounds from the function and provide them as input to the optimizer. If that were done, we could use the same simple FunctionTest<>()-style interface to implement a test with multiple trials, like many of the other single-objective objective functions.

From a user perspective (i.e. outside of our test suite), though, we shouldn't require users to encode their bounds into the function itself---they can set them as optimizer parameters. 👍

from ensmallen.

rcurtin avatar rcurtin commented on July 26, 2024 1

(a) Yes, agreed.
(b) If you are referring only to functions in problems/, then yes, but I don't expect users to be using anything in there. Those are primarily for our own internal testing, and if we encode the bounds of the problem there, writing the test suite is a little bit easier.
(c) Sure, this could be useful, and if you want to add this, we should consider where in the documentation we can introduce those functions so that users know about them.

from ensmallen.

zoq avatar zoq commented on July 26, 2024 1

About b and c) I think a lot of users treat the tests as examples, so everything we show there could be interpreted as how a certain function can or should be used, with that in mind I'm not a fan of the idea to add the bounds or provide some an extra utility.

from ensmallen.

rcurtin avatar rcurtin commented on July 26, 2024 1

@zoq good point---in this case, should we go through what's in problems/ and add a comment above GetInitialPoint() and GetFinalPoint() to point out that those functions are not required for general ensmallen usage and are only used in our test suite?

from ensmallen.

rcurtin avatar rcurtin commented on July 26, 2024 1

Not all multiobjective optimizers will necessarily require bounds. So it would be more appropriate just to point out in the optimizer documentation that setting the bounds correctly is important, if it is an optimizer that requires bounds inside of which to search. 👍

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024

An added advantage to bounds being inside the problem is the optimizer doesn't require lowerBound and upperBound argument anymore. It can simply extract it from the problem itself.

from ensmallen.

zoq avatar zoq commented on July 26, 2024

NSGA2 Test: NSGA2Test sets the bounds customly to [-1000, 1000] , which again brings me back to the main point, users shouldn't be bothered to fill the bounds of a problem.

All the problems we have in ensmallen are there for testing, so I'm not sure putting the bounds in the problem definition saves a user from specifying bounds for his problem, the same applies for the optimizer parameters, which have to be tailored down to the specific problem. Maybe I missed the point here?

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024

Sorry, I wasn't clear. I meant, Schaffer N1 Problem which is used inside NSGA2 Test. In NSGA2Test we manually put lowerBound as -1000 and upperBound as 1000 whereas Schaffer N1 Problem's domain is [-10, 10].

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024

Can we try declaring static constexpr lowerBoundand upperBound so that users can extract from the problem set and feed it to optimizer?

One step further, can we create a util function which takes in two or more Problem classes, gets the intersection of lowerBound and upperBound. The user can then feed it to the optimizer?

This is just a stand-in solution which doesn't mess up the interface 😃.

from ensmallen.

zoq avatar zoq commented on July 26, 2024

Adding on the point, I guess the main question is how do we pass multiple constraints to the optimizer, because our current interface Optimizer(lowerBound, upperBound) applies the same bound to the function, which is all we need if we optimize a single function but for multi-function optimization there are cases where we need to pass different constraints for different functions. @jonpsy correct me if I'm wrong. So what I could see as a solution is to use a matrix for the lower and upper bound; one column for each function. Let me know what you think.

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024

@jonpsy correct me if I'm wrong. So what I could see as a solution is to use a matrix for the lower and upper bound; one column for each function. Let me know what you think.

Remember that we're solving for all the multiobjective problems simulataneously. This means our lowerBound and upperBound should be an intersection of all the (lowerBound, upperBound) of the Problems provided. As I said earlier, perhaps we can create an external util function for this?

Basically,
say ProblemA has domain [-3, 3] and ProblemB has domain [-2, 4] then our util function should return [-2, 3] as the intersection of bounds. Which we can then feed to optimizer :)

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024

f(x) = 2 * x and I want to find the minimum... whether or not I want to bound my search is not related to the function itself; it is related to how I choose to use the optimizer.

Consider f(x) = log(x),

image

If we put our bounds as (-1000, 1000), it will cause UIB because log(x) is not defined for negative numbers. So yes, it depends on the problem too.

. In that setting you could add the lower and upper bounds to use to the problems in problems/, but then in the tests you would extract the bounds from the function and provide them as input to the optimizer.

+1, imo an external function should handle this, because ProblemA doesn't know the bounds of ProblemB. We can store the bounds inside the Problems as you've suggested, then our external util function can extract those, and return an intersection as I've explained in my previous comment. This can then be fed to the optimizer, easy peasy.

from ensmallen.

zoq avatar zoq commented on July 26, 2024

I'm still not sure I see the benefit for something that is only meant for testing. I see this as another complexity added on top of something that a user has to specify anyway and should now be hidden behind some functionality that is not directly visible to the user.

So at least for me something like:

Optimizer opt(-1000, 500);

is easier to grasp as something like:

Optimizer opt(min(functionA, functionB), max(functionA, functionB));

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024
arma::vec lowerBound = util::IntersectionLowerBound(functionA, functionB);
arma::vec upperBound = util::IntersectionUpperBound(functionA, functionB);

Optimizer opt(...., lowerBound, upperBound);

Not very hard to grasp, and it lets the user know that we're taking the intersection lowerBound and upperBound of the functions.

I'm still not sure I see the benefit for something that is only meant for testing. I see this as another complexity added on top of something that a user has to specify anyway and should now be hidden behind some functionality that is not directly visible to the user.

I don't expect the user to calculate the intersecting lower and upper bound of all the arbitrary functions he puts in. Isn't it better if we do it for them? And if the user is feeling adventurous, there's nothing stopping him from doing

Optimizer opt(-1000, 500);

A win-win, no?

from ensmallen.

rcurtin avatar rcurtin commented on July 26, 2024

I'm leaving aside the question of whether or not we put bounds in the test problems in problems/ (because regardless of the outcome of this discussion I think we should do that anyway to simplify our test infrastructure; it's internal code).

I appreciate the discussion---I could write quite a lot about each of these reasons why it is better to keep the bounded regions that the optimizer should search in the optimizer, not in the function, but I will try to spare you from an essay and keep to just a few short reasons here...

  • A user might want to use different search regions for different optimizers or different optimizer configurations. So, e.g., maybe they want to run a fast multi-objective minimization in a very large region with large step sizes, then call it again and change the search space to be a small space around the coarse solution they just found, and then have a much more granular search. The simplest way to do this is to make the search region a parameter of the optimizer.

  • There could exist a multi-objective optimizer that does not need to constrain its search area, and there can also exist objective functions for which the search space is the entire float64 space. In that situation, it's awkward to ask the user to specify their search space.

  • There could exist multi-objective optimizers that did not want a bounding box for range limits but instead a different shape, like a ball or any arbitrary thing, like, I dunno, a polymatroid or something. Limiting ourselves to taking a bounding box means that if we ever implement a multi-objective optimizer like that, we're forced into an awkward design to support that particular case.

  • Keeping the constraints in the optimizer mean that you can take some ArbitraryFunctions that you have and you don't need to modify them at all and you can wrap them in a std::tuple<> and pass them off to a multi-objective optimizer. If we need to specify constraints as a member function of each function, and a user doesn't control the implementation of that ArbitraryFunction (which they might reasonably not in a large and complex application), they have to write a weird shim wrapper class before using the multi-objective optimizer.

To me, the first of those is the most relevant---ease of use for complicated or nonstandard use cases. 👍 (For what it's worth, I can see a few valid choices on how we could specify the bounding box of a function in the optimizer, but here I'm just focusing on where the right place is to specify that information.)

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024

Thanks for the detailed explanation, I've summarised my conclusions:

a) It's best to keep the bounds as parameters to optimizers to allow flexibility in search space for the user.
b) For convenience's sake, it will be good if users could extract problem search space from the Problem class.
c) A util function could help us get the intersection of bounds of multiple objective functions.

from ensmallen.

zoq avatar zoq commented on July 26, 2024

@zoq good point---in this case, should we go through what's in problems/ and add a comment above GetInitialPoint() and GetFinalPoint() to point out that those functions are not required for general ensmallen usage and are only used in our test suite?

Yes, I guess a top level comment in some of the test suites as well.

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024

@rcurtin Do you think it will be a good idea to warn the users if the bounds they're using isn't in the feasible set (maybe a runtime warning or in docs) just to let them know "You're on your own now! "

from ensmallen.

rcurtin avatar rcurtin commented on July 26, 2024

How would we know what the feasible set is? Multi-objective functions have no API requirement to specify that.

@zoq no problem, I can open a PR for that.

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024

True, maybe we can put it in FunctionTypes.md something like "It is advisable to use the util function to know the bounds of multiobjective functions. However, a user may choose to ignore it but is responsible for undefined behaviors".

from ensmallen.

rcurtin avatar rcurtin commented on July 26, 2024

PR opened for comments in #276. 👍

from ensmallen.

jonpsy avatar jonpsy commented on July 26, 2024

Opened PR for comments #278 ^_^

from ensmallen.

mlpack-bot avatar mlpack-bot commented on July 26, 2024

This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! 👍

from ensmallen.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.