Refactor and speed up HV3D#6124
Merged
nabenabe0928 merged 12 commits intooptuna:masterfrom Jun 6, 2025
Merged
Conversation
Member
|
@not522 @kAIto47802 Could you review this PR? |
nabenabe0928
commented
Jun 4, 2025
optuna/_hypervolume/wfg.py
Outdated
| x_delta = np.concatenate([x_vals[1:], reference_point[:1]]) - x_vals | ||
| y_delta = np.concatenate([y_vals[1:], reference_point[1:2]]) - y_vals | ||
| # NOTE(nabenabe): `np.vdot(A, B)` is a faster calculation of `np.sum(A * B)`. | ||
| return np.vdot(x_delta[:, np.newaxis] * y_delta, z_delta) |
Contributor
Author
There was a problem hiding this comment.
It seems sorted_pareto_sols.shape[0] is < 25 due to the default_gamma.
In [1]: import numpy as np
...:
...: A = np.random.random((30, 30))
...: %timeit np.sum(A * A)
...: %timeit np.vdot(A, A)
2.08 μs ± 32.8 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
479 ns ± 6.95 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
nabenabe0928
commented
Jun 4, 2025
| y_indices, y_vals = _compress_coordinate(sorted_pareto_sols[:, 1]) | ||
| z_delta = np.zeros((n, n), dtype=float) | ||
| z_delta[np.arange(n), y_indices] = reference_point[2] - sorted_pareto_sols[:, 2] | ||
| z_delta = np.maximum.accumulate(np.maximum.accumulate(z_delta, axis=0), axis=1) |
Contributor
Author
There was a problem hiding this comment.
In [2]: import numpy as np
...:
...:
...: A = np.random.random((30, 30))
...: %timeit 1.0 - np.minimum.accumulate(np.minimum.accumulate(A, axis=0), axis=1
...: )
...: %timeit np.maximum.accumulate(np.maximum.accumulate(A, axis=0), axis=1)
7.16 μs ± 52.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
6.61 μs ± 54.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
nabenabe0928
commented
Jun 4, 2025
optuna/_hypervolume/wfg.py
Outdated
Comment on lines
+51
to
+52
| z_delta = np.zeros((n, n), dtype=float) | ||
| z_delta[np.arange(n), y_indices] = reference_point[2] - sorted_pareto_sols[:, 2] |
Contributor
Author
There was a problem hiding this comment.
In [4]: import numpy as np
...:
...:
...: n = 30
...: a = np.random.random(n)
...:
...: def original():
...: A = np.full((n, n), 1.0)
...: for i in range(n):
...: A[i, i] = a[i]
...: A = 1.0 - A
...:
...: def this_pr():
...: inds = np.arange(n)
...: A = np.zeros((n, n), dtype=float)
...: A[inds, inds] = 1.0 - a
...:
...: %timeit original()
...: %timeit this_pr()
4.26 μs ± 41 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
1.63 μs ± 17.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
nabenabe0928
commented
Jun 4, 2025
| z_delta = np.maximum.accumulate(np.maximum.accumulate(z_delta, axis=0), axis=1) | ||
| # The x axis is already sorted, so no need to compress this coordinate. | ||
| x_vals = sorted_pareto_sols[:, 0] | ||
| x_delta = np.concatenate([x_vals[1:], reference_point[:1]]) - x_vals |
Contributor
Author
There was a problem hiding this comment.
This change makes only a slight speedup (2%).
In [5]: import numpy as np
...:
...:
...: a = np.random.random(30)
...:
...: def original():
...: b = np.concatenate([a, [1.0]])
...: b[1:] - b[:-1]
...:
...: def this_pr():
...: np.concatenate([a[1:], [1.0]]) - a
...:
...: %timeit original()
...: %timeit this_pr()
972 ns ± 13.2 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
952 ns ± 6.55 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
not522
reviewed
Jun 4, 2025
Member
not522
left a comment
There was a problem hiding this comment.
Could you check my comments? They will improve the speed performance.
Co-authored-by: Naoto Mizuno <naotomizuno@preferred.jp>
Co-authored-by: Naoto Mizuno <naotomizuno@preferred.jp>
nabenabe0928
commented
Jun 4, 2025
| x_delta = np.concatenate([x_vals[1:], reference_point[:1]]) - x_vals | ||
| y_delta = np.concatenate([y_vals[1:], reference_point[1:2]]) - y_vals | ||
| # NOTE(nabenabe): Below is a faster calculation of `np.sum(A * B)`. | ||
| return np.dot(np.dot(z_delta, y_delta), x_delta) |
Contributor
Author
There was a problem hiding this comment.
In [3]: import numpy as np
...:
...:
...: a = np.random.random(30)
...: b = np.random.random(30)
...: C = np.random.random((30, 30))
...: %timeit np.dot(np.dot(C, b), a)
...: %timeit np.vdot(a[:, None] * b, C)
...: %timeit np.sum(C * a[:, None] * b[None, :], axis=(0, 1))
761 ns ± 13.4 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
1.99 μs ± 28.2 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
4.31 μs ± 4.69 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
Collaborator
|
Thank you for your PR! indeed improves the benchmarking time. I used the same objective function as you described in the initial comment. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Note
This branch completed the script below in 31.02 seconds while the latest HEAD completed it in 36.84 seconds, resulting in 15.8% speedup.
Motivation
This PR is a followup of:
_compute_3dfor hypervolume computation #6112Description of the changes
#with"""In essence, here:
In essence, here:
and here:
np.vdot(A, B)instead ofnp.sum(A * B)andnp.concatenateinstead ofnp.append.Benchmarking Code