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

Refactor NSGA-II crossover operations into classes #3221

Merged
merged 42 commits into from
Feb 14, 2022

Conversation

xadrianzetx
Copy link
Collaborator

@xadrianzetx xadrianzetx commented Jan 9, 2022

Motivation

This PR aims to create common API for all NSGA-II crossover operations and refactor functions introduced in #2903 into classes following this API. This decouples crossover implementations from sampler implementation, making them modular, easier to maintain/test/extend. Also, some numpy operations are vectorized (straightforward ones at least). This is a part of #3133.

Description of the changes

  • Introduce common crossover API
  • Refactor crossover functions into classes following this API
  • Make NSGAIISampler support new crossover format
  • Docs
  • Tests

@github-actions github-actions bot added the optuna.samplers Related to the `optuna.samplers` submodule. This is automatically labeled by github-actions. label Jan 9, 2022
@xadrianzetx
Copy link
Collaborator Author

This is still early work-in-progress, but general design should be clearly visible by now, so any comments on it would be greatly appreciated.

@codecov-commenter
Copy link

codecov-commenter commented Jan 15, 2022

Codecov Report

Merging #3221 (1b958cf) into master (6584ff5) will decrease coverage by 0.02%.
The diff coverage is 96.11%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3221      +/-   ##
==========================================
- Coverage   91.72%   91.70%   -0.03%     
==========================================
  Files         146      154       +8     
  Lines       12065    12132      +67     
==========================================
+ Hits        11067    11126      +59     
- Misses        998     1006       +8     
Impacted Files Coverage Δ
optuna/samplers/_base.py 82.14% <ø> (ø)
optuna/samplers/nsgaii/_crossovers/_base.py 84.61% <84.61%> (ø)
optuna/samplers/nsgaii/_crossover.py 91.78% <91.78%> (ø)
optuna/samplers/nsgaii/_crossovers/_vsbx.py 96.96% <96.96%> (ø)
optuna/samplers/nsgaii/_crossovers/_sbx.py 97.67% <97.67%> (ø)
optuna/samplers/nsgaii/_crossovers/_undx.py 98.07% <98.07%> (ø)
optuna/samplers/__init__.py 100.00% <100.00%> (ø)
optuna/samplers/nsgaii/__init__.py 100.00% <100.00%> (ø)
optuna/samplers/nsgaii/_crossovers/_blxalpha.py 100.00% <100.00%> (ø)
optuna/samplers/nsgaii/_crossovers/_spx.py 100.00% <100.00%> (ø)
... and 11 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6584ff5...1b958cf. Read the comment docs.

@xadrianzetx xadrianzetx marked this pull request as ready for review January 20, 2022 19:10
@toshihikoyanase toshihikoyanase added the feature Change that does not break compatibility, but affects the public interfaces. label Jan 21, 2022
@toshihikoyanase
Copy link
Member

@HideakiImamura @sile Could you review this PR, please? If you don't have enough time, please remove the assignment.

@sile
Copy link
Member

sile commented Jan 21, 2022

Yes. I'll review this PR this or next weekend.

Copy link
Member

@HideakiImamura HideakiImamura left a comment

Choose a reason for hiding this comment

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

Thank you for the update. I checked all of your changes. I have several minor comments. PTAL.

optuna/samplers/nsga2/_crossovers/_base.py Outdated Show resolved Hide resolved
optuna/samplers/nsga2/_crossovers/_base.py Outdated Show resolved Hide resolved
tests/samplers_tests/test_nsga2.py Show resolved Hide resolved
@xadrianzetx
Copy link
Collaborator Author

Seems like workflows are stuck. Could you guys re-trigger them?

docs/source/reference/samplers/nsga2.rst Outdated Show resolved Hide resolved
parents_params: np.ndarray,
rng: np.random.RandomState,
study: Study,
search_space: Dict[str, BaseDistribution],
Copy link
Member

Choose a reason for hiding this comment

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

It seems, in numerical distributions case, parents_params contains parameters transformed by _SearchSpaceTransform.transform method. In such a case, the upper and lower bounds of each transformed parameter are not guaranteed to be the same as the original distribution (especially if suggest_float(..., log=True) or suggest_float(..., step=...) is used to sample the parameter).

So passing the (non-transformed) distributions here could cause an unexpected calculation if a crossover implementation uses the low or high fields of the distributions (e.g., _sbx.py#72). I think that we need to use an instance of _SearchSpaceTransform (or __SearchSpaceTransform.bounds) in the crossover calculation instead of the Dict[str, BaseDistribution] instance (for numerical parameters).

A difficult point is that, in categorical distributions case, we should keep using Dict[str, BaseDistrivution] because we don't apply the transformation on categorical parameters.

Copy link
Member

Choose a reason for hiding this comment

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

Just an idea, but we may be able to define a function converting __SearchSpaceTransform.bounds to List[BaseDistribution] and apply the function to search_space before passing to the crossover method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since _SearchSpaceTransform already has an information about search space and bounds, maybe it should be able to do this conversion? I could implement this method in separate PR and merge it back here.

Copy link
Collaborator Author

@xadrianzetx xadrianzetx Jan 29, 2022

Choose a reason for hiding this comment

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

Or not, since it might not be possible to instantiate a distribution with transformed bounds e.g. FloatDistribution(log(1), log(10), log=True). I think we might be forced to pass instance of _SearchSpaceTransform after all. Maybe we could make OHE optional (_SearchSpaceTransform with ohe=False would be just identity function for categorical params)? We could then use it regardless of distribution, and pass to crossover instead of search space dict.

Copy link
Member

Choose a reason for hiding this comment

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

@hvy As the implementor of _SearchSpaceTransform, do you have any preference for how to deal with this issue?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

along with making _SearchSpaceTransform public

I think I could work on this right now in separate PR. This would be really usefull as now public SearchSpaceTransform would have essentially the functionality of bounds wrapper I was talking about previously. I think in the process of making it public we could think about some convenience methods such as __len__ to return search space length. What do you think about discussing and implementing it in separate PR? It would be a drop in replacement for search_space dict.

we could make it more Optuna-like by passing arguments that more resemble those of _try_crossover

Passing some object with params instead of an array in parents_params? +1 to that in future revisions, as it would probably come in handy if other crossovers were to support OHE.

Copy link
Member

@hvy hvy Feb 4, 2022

Choose a reason for hiding this comment

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

What do you think about discussing and implementing it in separate PR?

Sounds good. I think it'll be important to hear what others are thinking about this, considering the fact that this'll likely be an API targeting only a small portion of the users. __len__ by the way sounds interesting. I'd personally prefer named methods, as it's not entirely clear from __len__ whether it will return the length after or prior to the transformation, but this is not a strong opinion.

Given the discussion so far, how do you suggest we proceed with this PR. Should we aim to merge it as is, revisit it at a later time? If so, should we perhaps make APIs in this PR experimental to reserve ourselves for changes? Alternatively, we'll keep this PR open until everything discussed so far is fixed.

Copy link
Member

Choose a reason for hiding this comment

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

+1 to make _SearchSpaceTransform public. I think it would be beneficial for users to make it public, since it could be used by users who want to implement a new sampler, for example.

On the other hand, I don't think merging this PR will have to wait for that. Frankly speaking, @xadrianzetx's contribution to Optuna V3 is very big and we are relying on your contribution a lot. Making _SearchSpaceTransform public is out of scope for Optuna V3, but perhaps PR is expected to be somewhat bigger, including the interface. Therefore, in this PR, why not perform an in-line uniform crossover on categorical parameters and pass _SearchSpaceTransform.bounds to try_crossover? I suggest that making _SearchSpaceTransform public is a separate issue to be made into future work.

Copy link
Collaborator Author

@xadrianzetx xadrianzetx Feb 4, 2022

Choose a reason for hiding this comment

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

Fully agree with @HideakiImamura.

  • Uniform crossover for categorical params will be inlined, and BaseCrossover implementations will be reserved for transformed numerical params (for now).
  • We will re-design search_space arg to accept numpy array of _SearchSpaceTransform.bounds instead of distribution dict to solve issue reported by @sile.
  • Making _SearchSpaceTransform a public API and passing it instead of just bounds will be left for future discussion and work (which I'm happy to be assigned to).
  • Making crossovers accept one-hot-encoded params will be left for future discussion.

Copy link
Member

Choose a reason for hiding this comment

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

Perfect! Thank you very much for this useful discussion! > @xadrianzetx @hvy @HideakiImamura

@sile
Copy link
Member

sile commented Jan 29, 2022

Seems like workflows are stuck. Could you guys re-trigger them?

Sorry, I don't know how to resolve that.
(I could re-run the workflows for a bit old commit 2520dcf. However, I could not find workflows to be re-trigger for the latest commit via the GitHub web UI.)

@HideakiImamura Could you address this problem?

@xadrianzetx
Copy link
Collaborator Author

xadrianzetx commented Jan 29, 2022

I think i may have to close and re-open this PR to resolve CI hangup, since pushing new commits does not seem to resolve the issue. I think I'm unable to fix the status of those jobs on my side. @HideakiImamura, @sile merging master to resolve conflicts also re-triggered the jobs.

@xadrianzetx xadrianzetx closed this Feb 1, 2022
@xadrianzetx xadrianzetx reopened this Feb 1, 2022
@xadrianzetx
Copy link
Collaborator Author

@sile, @HideakiImamura, changes discussed in #3221 (comment) are now implemented. Sorry for small delay and PTAL.

Copy link
Member

@sile sile left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@xadrianzetx xadrianzetx changed the title [RFC] Refactor NSGA-II crossover operations into classes Refactor NSGA-II crossover operations into classes Feb 11, 2022
Copy link
Member

@HideakiImamura HideakiImamura left a comment

Choose a reason for hiding this comment

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

Thanks for the long running PR! LGTM!

@HideakiImamura HideakiImamura merged commit 444695d into optuna:master Feb 14, 2022
@HideakiImamura HideakiImamura added this to the v3.0.0-b0 milestone Feb 14, 2022
@xadrianzetx xadrianzetx deleted the feat-crossover-classes branch February 14, 2022 07:45
@xadrianzetx
Copy link
Collaborator Author

Thank you for reviews and discussions @sile, @HideakiImamura, @hvy!

@hrntsm hrntsm mentioned this pull request Aug 31, 2022
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Change that does not break compatibility, but affects the public interfaces. optuna.samplers Related to the `optuna.samplers` submodule. This is automatically labeled by github-actions.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants