Gradient-Free-Optimizers
Gradient-Free-Optimizers copied to clipboard
⚡️ Speed up method `DiagonalGridSearchOptimizer.grid_move` by 108%
📄 108% (1.08x) speedup for DiagonalGridSearchOptimizer.grid_move in src/gradient_free_optimizers/optimizers/grid/diagonal_grid_search.py
⏱️ Runtime : 5.84 milliseconds → 2.81 milliseconds (best of 55 runs)
📝 Explanation and details
To optimize the given DiagonalGridSearchOptimizer implementation, we can focus on improving the performance of the grid_move method by avoiding repeated calls to np.prod which is computationally expensive if repeatedly used. Additionally, we can use Python's integer arithmetic to achieve our requirements more efficiently. Below is the optimized code.
Explanation of Optimizations.
- Precompute cumulative products: The cumulative product of dimensions (
cumprod_dims) is computed only once, and in reverse order. This helps in avoiding repeated computation vianp.prod, making the process faster. - Use of
divmod: Thedivmodfunction replaces the need to use division and remainder operations separately, thus making the operations more efficient. - Avoid recursive slicing of lists: By precomputing the cumulative products, we avoid multiple slicing operations within the loop, which can be expensive.
This optimization primarily focuses on improving the efficiency of the grid_move function which is the core logic for this optimizer.
✅ Correctness verification report:
| Test | Status |
|---|---|
| ⚙️ Existing Unit Tests | 🔘 None Found |
| 🌀 Generated Regression Tests | ✅ 1028 Passed |
| ⏪ Replay Tests | 🔘 None Found |
| 🔎 Concolic Coverage Tests | 🔘 None Found |
| 📊 Tests Coverage | 100.0% |
🌀 Generated Regression Tests Details
import numpy as np
# imports
import pytest # used for our unit tests
from gradient_free_optimizers.optimizers.base_optimizer import BaseOptimizer
from gradient_free_optimizers.optimizers.core_optimizer import CoreOptimizer
from gradient_free_optimizers.optimizers.grid.diagonal_grid_search import \
DiagonalGridSearchOptimizer
# Author: Simon Blanke
# Email: [email protected]
# License: MIT License
class BaseOptimizer(CoreOptimizer):
def __init__(
self,
search_space,
initialize={"grid": 4, "random": 2, "vertices": 4},
constraints=[],
random_state=None,
rand_rest_p=0,
nth_process=None,
):
super().__init__(
search_space=search_space,
initialize=initialize,
constraints=constraints,
random_state=random_state,
rand_rest_p=rand_rest_p,
nth_process=nth_process,
)
self.optimizers = [self]
# unit tests
# Mock the necessary parts of the optimizer
class MockConv:
def __init__(self, dim_sizes):
self.dim_sizes = dim_sizes
self.n_dimensions = len(dim_sizes)
class MockOptimizer(DiagonalGridSearchOptimizer):
def __init__(self, dim_sizes, high_dim_pointer):
self.conv = MockConv(dim_sizes)
self.high_dim_pointer = high_dim_pointer
# Basic Functionality
def test_basic_single_dimension():
optimizer = MockOptimizer([10], 3)
def test_basic_multiple_dimensions():
optimizer = MockOptimizer([3, 3], 4)
# Edge Cases
def test_high_dimensionality():
optimizer = MockOptimizer([10] * 100, 123456789)
codeflash_output = optimizer.grid_move(); result = codeflash_output
def test_random_pointers_fixed_dimensions():
dim_sizes = [10, 10, 10]
pointer = np.random.randint(0, 1000)
optimizer = MockOptimizer(dim_sizes, pointer)
codeflash_output = optimizer.grid_move(); result = codeflash_output
def test_random_dimension_sizes():
dim_sizes = [np.random.randint(1, 10) for _ in range(10)]
pointer = np.random.randint(0, np.prod(dim_sizes))
optimizer = MockOptimizer(dim_sizes, pointer)
codeflash_output = optimizer.grid_move(); result = codeflash_output
# Degenerate Cases
def test_single_element_each_dimension():
optimizer = MockOptimizer([1, 1, 1], 0)
def test_all_dimensions_one_except_one():
optimizer = MockOptimizer([1, 1, 1, 10], 5)
# Sequential Pointers
def test_sequential_pointers_fixed_dimensions():
dim_sizes = [4, 4]
for pointer in range(16):
optimizer = MockOptimizer(dim_sizes, pointer)
codeflash_output = optimizer.grid_move(); result = codeflash_output
def test_sequential_pointers_large_dimensions():
dim_sizes = [100, 100]
for pointer in range(1000):
optimizer = MockOptimizer(dim_sizes, pointer)
codeflash_output = optimizer.grid_move(); result = codeflash_output
# Performance and Scalability
def test_very_large_dimension_sizes():
optimizer = MockOptimizer([10000, 10000], 50000000)
codeflash_output = optimizer.grid_move(); result = codeflash_output
def test_high_dimensionality_large_sizes():
optimizer = MockOptimizer([100] * 20, 1234567890)
codeflash_output = optimizer.grid_move(); result = codeflash_output
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
import numpy as np
# imports
import pytest # used for our unit tests
from gradient_free_optimizers.optimizers.base_optimizer import BaseOptimizer
from gradient_free_optimizers.optimizers.core_optimizer import CoreOptimizer
from gradient_free_optimizers.optimizers.grid.diagonal_grid_search import \
DiagonalGridSearchOptimizer
# Author: Simon Blanke
# Email: [email protected]
# License: MIT License
class BaseOptimizer(CoreOptimizer):
def __init__(
self,
search_space,
initialize={"grid": 4, "random": 2, "vertices": 4},
constraints=[],
random_state=None,
rand_rest_p=0,
nth_process=None,
):
super().__init__(
search_space=search_space,
initialize=initialize,
constraints=constraints,
random_state=random_state,
rand_rest_p=rand_rest_p,
nth_process=nth_process,
)
self.optimizers = [self]
# unit tests
# Basic Functionality Tests
from gradient_free_optimizers.optimizers.grid.diagonal_grid_search import DiagonalGridSearchOptimizer
To edit these changes git checkout codeflash/optimize-DiagonalGridSearchOptimizer.grid_move-m8gnxbqn and push.
Hi, can I help in any way with moving forward with merging this optimization?
Could you provide measurements of the speedup in a realistic example? You could use a test-function for this.
HI @SimonBlanke , the performance was measured with the set of examples that are attached in the "Generated Regression Tests Details" in the PR description.