-
-
Notifications
You must be signed in to change notification settings - Fork 95
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
Fast pareto-front calculation for 2D #687
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #687 +/- ##
==========================================
+ Coverage 62.88% 68.22% +5.33%
==========================================
Files 35 35
Lines 2250 2329 +79
==========================================
+ Hits 1415 1589 +174
+ Misses 835 740 -95 ☔ View full report in Codecov by Sentry. |
@not522 @nabenabe0928 Could you review this PR? |
Sure! |
This is out of the scope of this PR, but the implementation for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the variable and function names sound unnatural. How about the followings?
dominatedTrials
-> dominationFlags
getIsDominatedTrial
-> evaluateTrialDomination
I think renaming |
I think starting a variable name with "is" may not be appropriate for an array. That is, "... is dominated trials" is grammatically incorrect. |
There was a problem hiding this 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 several comments. PTAL.
Co-authored-by: Hideaki Imamura <[email protected]>
Co-authored-by: Hideaki Imamura <[email protected]>
Co-authored-by: Hideaki Imamura <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
@not522 How about |
It sounds more natural. 👍 |
) | ||
|
||
sorted.forEach((values) => { | ||
if (values[1] <= minValue1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition is buggy when the values between trials are the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, both trials should be considered undominated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now given a sequence of 2D vectors
For example,
As mentioned above, let's assume that
However,
It means that there could exist
Btw, I guessed you are trying to get "weak dominance" from getIsDominatedTrialND
.
Maybe I am wrong, if so, correct me please!
const normalizedValues = [[0, 1], [1, 1], [2, 1]]
const getIsDominatedTrial2D = (normalizedValues: number[][]) => {
const sortedValues = normalizedValues
.map((values, i) => [values[0], values[1], i])
.sort((a, b) => a[0] !== b[0] ? a[0] - b[0] : a[1] - b[1])
let minValue1 = sortedValues[0][1]
const dominatedTrials: boolean[] = new Array(normalizedValues.length).fill(
true
)
sortedValues.forEach((values) => {
if (values[1] <= minValue1) {
dominatedTrials[values[2]] = false
minValue1 = values[1]
}
})
return dominatedTrials
}
console.log(getIsDominatedTrial2D(normalizedValues))
Output
[ false, false, false ]
I guess (if we need weakly dominated trials) what we want is:
[ false, true, true ]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick solution to this is the following:
const normalizedValues = [[0, 1], [1, 1], [2, 1]]
const getIsDominatedTrial2D = (normalizedValues: number[][]) => {
const sortedValues = normalizedValues
.map((values, i) => [values[0], values[1], i])
.sort((a, b) => a[0] !== b[0] ? a[0] - b[0] : a[1] - b[1])
let minValue1 = sortedValues[0][1]
let latestParetoSolution = sortedValues[0].slice(0, 2)
const dominatedTrials: boolean[] = new Array(normalizedValues.length).fill(
true
)
sortedValues.forEach((values) => {
if (values[1] < minValue1 || latestParetoSolution.every((v, i) => v === values[i])) {
dominatedTrials[values[2]] = false
minValue1 = values[1]
latestParetoSolution = values.slice(0, 2)
}
})
return dominatedTrials
}
console.log(getIsDominatedTrial2D(normalizedValues))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See https://github.com/optuna/optuna/blob/master/optuna/study/_multi_objective.py#L101. Optuna uses weakly dominates
for comparison. It seems that @nabenabe0928 is correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for pointing out. My statement is incorrect and @nabenabe0928's one is correct, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new modification fails with the following case.
const normalizedValues = [[0, 1], [1, 2], [2, 1], [3, 0], [3, 0]]
We get:
[ false, true, false, false, false ]
But we would like to have:
[ false, true, true, false, false ]
I think the suggestion here is sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, it is still necessary.
I will check.
// Fast pareto front algorithm (O(N log N) complexity). | ||
const sorted = normalizedValues | ||
.map((values, i) => [values[0], values[1], i]) | ||
.sort() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const values = [[2, 2], [3, 2], [10, 2]]
const sortedValues = values.map((v, i) => [v[0], v[1], i]).sort()
console.log(sortedValues)
Output
[ [ 10, 2, 2 ], [ 2, 2, 0 ], [ 3, 2, 1 ] ]
So you still need to change the comparator inside sort()
.
.sort() | |
.sort((a, b) => a[0] !== b[0] ? a[0] - b[0] : a[1] - b[1]) |
Output after the change:
[ [ 2, 2, 0 ], [ 3, 2, 1 ], [ 10, 2, 2 ] ]
|
||
const getIsDominatedTrial1D = (normalizedValues: number[][]) => { | ||
const best_value = Math.min(...normalizedValues.map((values) => values[0])) | ||
return normalizedValues.map((value) => value[0] !== best_value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return normalizedValues.map((value) => value[0] !== best_value) | |
return normalizedValues.map((values) => values[0] !== best_value) |
@nabenabe0928 @not522 Thank you for pointing them out. I fixed the bug. PTAL. |
I commented below to bundle the discussion in one place! |
minValue1 = values[1] | ||
} | ||
maxValue0 = values[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or probably, it should work as well.
minValue1 = values[1] | |
} | |
maxValue0 = values[0] | |
} | |
maxValue0 = values[0] | |
minValue1 = Math.min(...[minValue1, values[1]]) |
sorted.forEach((values) => { | ||
if ( | ||
values[1] > minValue1 || | ||
(values[1] >= minValue1 && values[0] > maxValue0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it is clearer as the inequality is always removed by the first condition.
(values[1] >= minValue1 && values[0] > maxValue0) | |
(values[1] === minValue1 && values[0] > maxValue0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Co-authored-by: Shuhei Watanabe <[email protected]>
@not522 Thank you for your review. I think I fixed the problems, PTAL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your update. I confirmed that this PR makes the Pareto-front calculation faster when many trials are best trials. LGTM.
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.
Reference Issues/PRs
Fixes #63.
What does this implement/fix? Explain your changes.