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

Support constrained optimization for timeline plot #678

Merged
merged 5 commits into from
Nov 12, 2023

Conversation

not522
Copy link
Member

@not522 not522 commented Nov 2, 2023

Contributor License Agreement

This repository (optuna-dashboard) and Goptuna share common code.
This pull request may therefore be ported to Goptuna.
Make sure that you understand the consequences concerning licenses and check the box below if you accept the term before creating this pull request.

  • I agree this patch may be ported to Goptuna by other Goptuna contributors.

Reference Issues/PRs

This feature corresponds to optuna/optuna#4877.

What does this implement/fix? Explain your changes.

This PR supports constrained optimization for timeline plot. Same as Optuna, it applies only to COMPLETE trials.

newplot (2)

Copy link

codecov bot commented Nov 2, 2023

Codecov Report

Merging #678 (3628c31) into main (0e1feb3) will not change coverage.
Report is 22 commits behind head on main.
The diff coverage is n/a.

@@           Coverage Diff           @@
##             main     #678   +/-   ##
=======================================
  Coverage   62.88%   62.88%           
=======================================
  Files          35       35           
  Lines        2250     2250           
=======================================
  Hits         1415     1415           
  Misses        835      835           

📣 Codecov offers a browser extension for seamless coverage viewing on GitHub. Try it in Chrome or Firefox today!

@not522
Copy link
Member Author

not522 commented Nov 2, 2023

@HideakiImamura @nabenabe0928 Could you review this PR?

Copy link
Collaborator

@nabenabe0928 nabenabe0928 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 PR!
I left some comments")

Comment on lines 120 to 135
if (s == "Complete") {
const feasibleTrials = bars.filter((t) =>
t.constraints.every((c) => c <= 0)
)
const infeasibleTrials = bars.filter((t) =>
t.constraints.some((c) => c > 0)
)
if (feasibleTrials.length > 0) {
traces.push(makeTrace(feasibleTrials, "Complete", cm[s]))
}
if (infeasibleTrials.length > 0) {
traces.push(makeTrace(infeasibleTrials, "Infeasible", "#cccccc"))
}
} else {
traces.push(makeTrace(bars, s, cm[s]))
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about early return?

Suggested change
if (s == "Complete") {
const feasibleTrials = bars.filter((t) =>
t.constraints.every((c) => c <= 0)
)
const infeasibleTrials = bars.filter((t) =>
t.constraints.some((c) => c > 0)
)
if (feasibleTrials.length > 0) {
traces.push(makeTrace(feasibleTrials, "Complete", cm[s]))
}
if (infeasibleTrials.length > 0) {
traces.push(makeTrace(infeasibleTrials, "Infeasible", "#cccccc"))
}
} else {
traces.push(makeTrace(bars, s, cm[s]))
}
if (s !== "Complete") {
traces.push(makeTrace(bars, s, cm[s]))
continue
}
const feasibleTrials = bars.filter((t) =>
t.constraints.every((c) => c <= 0)
)
const infeasibleTrials = bars.filter((t) =>
t.constraints.some((c) => c > 0)
)
if (feasibleTrials.length > 0) {
traces.push(makeTrace(feasibleTrials, "Complete", cm[s]))
}
if (infeasibleTrials.length > 0) {
traces.push(makeTrace(infeasibleTrials, "Infeasible", "#cccccc"))
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think early return is appropriate here since the codes inside the brackets are at the same level. For example, if we add another if statement for "Waiting", it should be like this.

if (s === "Complete") {
  ...
} else if (s === "Waiting") {
  ...
} else {
  ...
}

not like this.

if (s !== "Complete" && s !== "Waiting") {
  ...
  continue
}
if (s === "Complete") {
  ...
  continue
}
...

if (bars.length === 0) {
continue
}
const makeTrace = (bars: Trial[], name: string, color: string) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It is a very minor comment, but what about replacing name with state?

Suggested change
const makeTrace = (bars: Trial[], name: string, color: string) => {
const makeTrace = (bars: Trial[], state: string, color: string) => {

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 PR. I have a minor comment. PTAL.

optuna_dashboard/ts/components/GraphTimeline.tsx Outdated Show resolved Hide resolved
@HideakiImamura
Copy link
Member

I confirmed that the change of this PR generate the following plot for the following script.

    # Study with trials having certain duration and constriants
    def objective_with_duration(trial: optuna.Trial) -> float:
        trial.set_user_attr("constraint", [trial.number % 2])
    
        time.sleep(0.01)
        if trial.number % 3 == 0:
            raise optuna.TrialPruned()
        if trial.number % 7 == 0:
            return float("nan")
        return 0.0

    def constraints(trial: optuna.Trial) -> list[float]:
        return trial.user_attrs["constraint"]

    study = optuna.create_study(
        study_name="trials-with-duration", 
        storage=storage,
        sampler=optuna.samplers.NSGAIISampler(constraints_func=constraints),
    )

    trial = study.ask({"x": FloatDistribution(0, 10), "y": CategoricalDistribution(["Foo", "Bar"])})  # To create a running trial
    trial.set_user_attr("constraint", [1])
    time.sleep(0.01)

    study.optimize(objective_with_duration, n_trials=10)

    study.enqueue_trial({"x": 0, "y": 10})  # To create a waiting trial
    time.sleep(0.01)
Screenshot 2023-11-02 at 20 27 42

@HideakiImamura
Copy link
Member

I noticed that the running and waiting trials are not appropriately plotted. Also, it seems that the waiting trials are not plotted properly even in Optuna. As neither of these issues are due to the changes in this PR, I believe it would be best to address these in a separate PR.

Optuna Dashboard Optuna
Screenshot 2023-11-02 at 20 27 42 timeline

@not522
Copy link
Member Author

not522 commented Nov 8, 2023

Thank you for your reviews. I have updated this PR responding to your comments. PTAL.

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 update. LGTM.

Copy link
Collaborator

@nabenabe0928 nabenabe0928 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 update! LGTM

@nabenabe0928 nabenabe0928 removed their assignment Nov 10, 2023
@keisuke-umezawa keisuke-umezawa merged commit 285e5e0 into optuna:main Nov 12, 2023
10 checks passed
@not522 not522 deleted the timeline-constraint branch November 13, 2023 03:32
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.

4 participants