Skip to content

⚡️ Speed up function validate_float_arg by 20%#2

Open
codeflash-ai[bot] wants to merge 1 commit into
masterfrom
codeflash/optimize-validate_float_arg-max3zw1h
Open

⚡️ Speed up function validate_float_arg by 20%#2
codeflash-ai[bot] wants to merge 1 commit into
masterfrom
codeflash/optimize-validate_float_arg-max3zw1h

Conversation

@codeflash-ai
Copy link
Copy Markdown

@codeflash-ai codeflash-ai Bot commented May 20, 2025

📄 20% (0.20x) speedup for validate_float_arg in keras/src/regularizers/regularizers.py

⏱️ Runtime : 1.74 millisecond 1.44 millisecond (best of 164 runs)

📝 Explanation and details

Here’s an optimized version of your program, focusing on removing redundant computations, minimizing branching, and cutting function call overhead. The main improvements.

  1. Short-circuit the most common valid cases: Fast path for numeric, non-negative, non-NaN, non-inf.
  2. Single isinstance check: Use the C-API speed of type(x) in (float, int) when possible, rather than isinstance (although in some versions, type(x) in ... can be slightly faster).
  3. Minimize math module calls: Only call math.isinf/math.isnan if the type is float (skip for int, since isinf(int)/isnan(int) are always False, so avoid the calls).
  4. Reduce string formatting on error: Only format error strings on the (rare) error path.

Key optimization points:

  • Avoid math.isinf/math.isnan on integers (can't ever be true).
  • Avoid unnecessary formatting except when raising an exception.
  • Use the fast type check path for the biggest classes (int, float).
  • Return directly when possible to reduce overhead.

This should lead to a measurable speedup, especially for the typical case where the value is a valid int or float.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 5178 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests Details
import math

# imports
import pytest  # used for our unit tests
from keras.src.regularizers.regularizers import validate_float_arg

# unit tests

# --------------------------
# 1. Basic Test Cases
# --------------------------

def test_accepts_positive_float():
    # Should accept a typical positive float
    codeflash_output = validate_float_arg(1.5, "alpha")

def test_accepts_zero():
    # Should accept zero (edge of non-negativity)
    codeflash_output = validate_float_arg(0.0, "beta")

def test_accepts_positive_integer():
    # Should accept a positive integer and return as float
    codeflash_output = validate_float_arg(8, "gamma")

def test_accepts_large_positive_float():
    # Should accept a large positive float
    val = 1e100
    codeflash_output = validate_float_arg(val, "delta")

def test_accepts_small_positive_float():
    # Should accept a small positive float
    val = 1e-12
    codeflash_output = validate_float_arg(val, "epsilon")

# --------------------------
# 2. Edge Test Cases
# --------------------------

@pytest.mark.parametrize(
    "bad_value",
    [
        -1,                # negative integer
        -0.0001,           # negative float close to zero
        float('-inf'),     # negative infinity
        float('inf'),      # positive infinity
        float('nan'),      # NaN
    ]
)
def test_rejects_invalid_numbers(bad_value):
    # Should raise ValueError for invalid numbers
    with pytest.raises(ValueError):
        validate_float_arg(bad_value, "zeta")

@pytest.mark.parametrize(
    "bad_type",
    [
        None,              # NoneType
        "1.1",             # string representation of a number
        [1.0],             # list
        {"value": 1.0},    # dict
        (1.0,),            # tuple
        object(),          # generic object
        True,              # boolean (should be rejected, as bool is subclass of int but semantically wrong)
        b"2.0",            # bytes
    ]
)
def test_rejects_non_numeric_types(bad_type):
    # Should raise ValueError for non-numeric types
    with pytest.raises(ValueError):
        validate_float_arg(bad_type, "eta")

def test_error_message_contains_argument_name_and_value():
    # Should include argument name and value in the error message
    name = "theta"
    value = -5
    with pytest.raises(ValueError) as excinfo:
        validate_float_arg(value, name)
    msg = str(excinfo.value)

def test_rejects_negative_zero():
    # Negative zero is a special float value in IEEE 754
    neg_zero = -0.0
    # Should accept negative zero as it's mathematically equal to zero
    codeflash_output = validate_float_arg(neg_zero, "iota"); result = codeflash_output

# --------------------------
# 3. Large Scale Test Cases
# --------------------------

def test_accepts_many_valid_values():
    # Should accept a large list of valid values
    for i in range(1000):
        # All positive integers and floats
        codeflash_output = validate_float_arg(float(i), f"param_{i}")
        codeflash_output = validate_float_arg(i, f"param_{i}")

def test_rejects_many_invalid_values():
    # Should reject a large list of invalid values efficiently
    # All negative numbers
    for i in range(1, 1000):
        with pytest.raises(ValueError):
            validate_float_arg(-i, f"param_{i}")
        with pytest.raises(ValueError):
            validate_float_arg(-float(i), f"param_{i}")

def test_performance_with_large_input_set():
    # Should perform efficiently with a large batch of values
    # (not a performance benchmark, but ensures no pathological slowness)
    valid = [float(i) for i in range(1000)]
    results = [validate_float_arg(v, f"param_{v}") for v in valid]

def test_handles_mixed_types_in_large_batch():
    # Should handle mixed valid and invalid inputs robustly
    values = [0, 1.0, -1, "2", float('inf'), 3, None, 4.5, float('nan'), 5]
    expected = [0.0, 1.0, None, None, None, 3.0, None, 4.5, None, 5.0]
    results = []
    for v in values:
        try:
            codeflash_output = validate_float_arg(v, "mixed"); res = codeflash_output
        except ValueError:
            res = None
        results.append(res)

# --------------------------
# 4. Additional Edge Cases
# --------------------------

def test_accepts_float_subclass():
    class MyFloat(float):
        pass
    mf = MyFloat(2.5)
    codeflash_output = validate_float_arg(mf, "custom"); result = codeflash_output


def test_argument_name_can_be_any_string():
    # The function should not fail on unusual but valid argument names
    codeflash_output = validate_float_arg(5.0, "")
    codeflash_output = validate_float_arg(6.0, " ")
    codeflash_output = validate_float_arg(7.0, "特殊字符")
    codeflash_output = validate_float_arg(8.0, "a" * 100)

def test_return_type_is_always_float():
    # Should always return a float, even if input is int
    codeflash_output = validate_float_arg(10, "num"); result = codeflash_output
    codeflash_output = validate_float_arg(10.0, "num"); 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 math

# imports
import pytest  # used for our unit tests
from keras.src.regularizers.regularizers import validate_float_arg

# unit tests

# ---------------------------
# 1. Basic Test Cases
# ---------------------------

def test_accepts_positive_float():
    # Should accept a standard positive float
    codeflash_output = validate_float_arg(1.23, "myarg")

def test_accepts_zero_float():
    # Should accept zero as a float
    codeflash_output = validate_float_arg(0.0, "zero")

def test_accepts_positive_integer():
    # Should accept a positive integer and convert to float
    codeflash_output = validate_float_arg(42, "intarg")

def test_accepts_zero_integer():
    # Should accept zero as integer and convert to float
    codeflash_output = validate_float_arg(0, "zero_int")

def test_returns_float_type():
    # Should always return a float, even if input is int
    codeflash_output = validate_float_arg(7, "int_as_float"); result = codeflash_output

# ---------------------------
# 2. Edge Test Cases
# ---------------------------

def test_rejects_negative_float():
    # Should reject negative float values
    with pytest.raises(ValueError):
        validate_float_arg(-0.01, "negfloat")

def test_rejects_negative_integer():
    # Should reject negative integer values
    with pytest.raises(ValueError):
        validate_float_arg(-5, "negint")

def test_rejects_nan():
    # Should reject NaN (not a number)
    with pytest.raises(ValueError):
        validate_float_arg(float('nan'), "nan_value")

def test_rejects_positive_infinity():
    # Should reject positive infinity
    with pytest.raises(ValueError):
        validate_float_arg(float('inf'), "pos_inf")

def test_rejects_negative_infinity():
    # Should reject negative infinity
    with pytest.raises(ValueError):
        validate_float_arg(float('-inf'), "neg_inf")

def test_rejects_string_input():
    # Should reject string input
    with pytest.raises(ValueError):
        validate_float_arg("3.14", "str_input")

def test_rejects_list_input():
    # Should reject list input
    with pytest.raises(ValueError):
        validate_float_arg([1.0], "list_input")

def test_rejects_none_input():
    # Should reject None as input
    with pytest.raises(ValueError):
        validate_float_arg(None, "none_input")


def test_error_message_contains_name_and_value():
    # Should include argument name and value in the error message
    with pytest.raises(ValueError) as excinfo:
        validate_float_arg(-10, "penalty")

def test_accepts_large_float():
    # Should accept a very large but finite float
    large_value = 1e308
    codeflash_output = validate_float_arg(large_value, "large")

def test_accepts_smallest_positive_float():
    # Should accept the smallest positive float
    codeflash_output = validate_float_arg(float.fromhex('0x0.0000000000001p-1022'), "smallest")

def test_accepts_float_with_fractional_part():
    # Should accept a float with a fractional part
    codeflash_output = validate_float_arg(0.123456789, "fractional")

def test_rejects_complex_number():
    # Should reject complex numbers
    with pytest.raises(ValueError):
        validate_float_arg(1 + 2j, "complex_input")

def test_rejects_dict_input():
    # Should reject dictionaries
    with pytest.raises(ValueError):
        validate_float_arg({'a': 1.0}, "dict_input")

def test_rejects_tuple_input():
    # Should reject tuples
    with pytest.raises(ValueError):
        validate_float_arg((1.0,), "tuple_input")

def test_rejects_object_input():
    # Should reject arbitrary objects
    class Dummy: pass
    with pytest.raises(ValueError):
        validate_float_arg(Dummy(), "object_input")

# ---------------------------
# 3. Large Scale Test Cases
# ---------------------------

def test_many_valid_inputs():
    # Should accept a large list of valid values
    for i in range(1000):
        val = i / 10.0  # 0.0, 0.1, ..., 99.9
        codeflash_output = validate_float_arg(val, f"arg{i}"); result = codeflash_output

def test_many_invalid_inputs():
    # Should reject a large list of invalid values
    invalids = [float('nan'), float('inf'), float('-inf'), -1, -1.0, -1e100]
    for idx, val in enumerate(invalids):
        with pytest.raises(ValueError):
            validate_float_arg(val, f"invalid{idx}")

def test_performance_with_large_numbers():
    # Should handle very large but finite floats
    for exp in range(300, 308):
        val = 10.0 ** exp
        codeflash_output = validate_float_arg(val, f"large{exp}"); result = codeflash_output

def test_performance_with_small_numbers():
    # Should handle very small positive floats
    for exp in range(-308, -300):
        val = 10.0 ** exp
        codeflash_output = validate_float_arg(val, f"small{exp}"); result = codeflash_output

def test_error_message_large_scale():
    # Should have correct error message for many failing cases
    for i in range(100):
        with pytest.raises(ValueError) as excinfo:
            validate_float_arg(-i - 1, f"neg{i}")
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-validate_float_arg-max3zw1h and push.

Codeflash

Here’s an optimized version of your program, focusing on removing redundant computations, minimizing branching, and cutting function call overhead. The main improvements.

1. **Short-circuit the most common valid cases**: Fast path for numeric, non-negative, non-NaN, non-inf.
2. **Single isinstance check**: Use the C-API speed of `type(x) in (float, int)` when possible, rather than `isinstance` (although in some versions, `type(x) in ...` can be slightly faster).
3. **Minimize math module calls**: Only call `math.isinf`/`math.isnan` if the type is `float` (skip for `int`, since `isinf(int)`/`isnan(int)` are always False, so avoid the calls).
4. **Reduce string formatting on error**: Only format error strings on the (rare) error path.




**Key optimization points:**  
- Avoid `math.isinf`/`math.isnan` on integers (can't ever be true).
- Avoid unnecessary formatting except when raising an exception.
- Use the fast type check path for the biggest classes (`int`, `float`).
- Return directly when possible to reduce overhead.

This should lead to a measurable speedup, especially for the typical case where the value is a valid `int` or `float`.
@codeflash-ai codeflash-ai Bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label May 20, 2025
@codeflash-ai codeflash-ai Bot requested a review from HeshamHM28 May 20, 2025 22:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants