Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Oct 1, 2025

📄 5% (0.05x) speedup for bbox_to_polygon in doctr/utils/geometry.py

⏱️ Runtime : 419 microseconds 398 microseconds (best of 176 runs)

📝 Explanation and details

The optimization reduces redundant tuple indexing operations by caching bbox[0] and bbox[1] in local variables bbox0 and bbox1.

In the original code, bbox[0] is accessed 3 times and bbox[1] is accessed 3 times within the single return statement. Each tuple indexing operation has overhead in Python. The optimized version performs each indexing only once, storing the results in local variables that are then reused.

This micro-optimization is particularly effective because:

  • Tuple indexing reduction: Eliminates 4 redundant indexing operations (from 6 total to 2)
  • Local variable access: Reading from local variables is faster than tuple indexing in Python
  • Hot path optimization: This function is likely called frequently in document processing workflows

The test results show the optimization performs best on large-scale operations (5-7% speedup on batch processing tests like test_bbox_to_polygon_many_boxes and test_bbox_to_polygon_stress_randomized) where the indexing overhead compounds across many function calls. Individual function calls show mixed but generally positive results, with the overall 5% speedup representing meaningful performance gains for high-frequency geometric operations.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 13 Passed
🌀 Generated Regression Tests 2137 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
⚙️ Existing Unit Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
common/test_utils_geometry.py::test_bbox_to_polygon 491ns 496ns -1.01%⚠️
🌀 Generated Regression Tests and Runtime
from typing import Tuple

# imports
import pytest  # used for our unit tests
from doctr.utils.geometry import bbox_to_polygon

# function to test
# Copyright (C) 2021-2025, Mindee.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.

BoundingBox = Tuple[Tuple[float, float], Tuple[float, float]]
Polygon4P = Tuple[Tuple[float, float], Tuple[float, float], Tuple[float, float], Tuple[float, float]]
from doctr.utils.geometry import bbox_to_polygon

# unit tests

# 1. Basic Test Cases

def test_bbox_to_polygon_square():
    # Test with a simple square bounding box
    bbox = ((0.0, 0.0), (1.0, 1.0))
    expected = ((0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (1.0, 1.0))
    codeflash_output = bbox_to_polygon(bbox) # 514ns -> 495ns (3.84% faster)

def test_bbox_to_polygon_rectangle():
    # Test with a rectangle bounding box
    bbox = ((2.0, 3.0), (5.0, 7.0))
    expected = ((2.0, 3.0), (5.0, 3.0), (2.0, 7.0), (5.0, 7.0))
    codeflash_output = bbox_to_polygon(bbox) # 474ns -> 496ns (4.44% slower)

def test_bbox_to_polygon_negative_coords():
    # Test with negative coordinates
    bbox = ((-1.0, -2.0), (3.0, 4.0))
    expected = ((-1.0, -2.0), (3.0, -2.0), (-1.0, 4.0), (3.0, 4.0))
    codeflash_output = bbox_to_polygon(bbox) # 488ns -> 493ns (1.01% slower)

def test_bbox_to_polygon_float_coords():
    # Test with floating point coordinates
    bbox = ((0.5, 1.5), (2.5, 3.5))
    expected = ((0.5, 1.5), (2.5, 1.5), (0.5, 3.5), (2.5, 3.5))
    codeflash_output = bbox_to_polygon(bbox) # 484ns -> 461ns (4.99% faster)

def test_bbox_to_polygon_zero_area():
    # Test with a zero area bounding box (both points the same)
    bbox = ((1.0, 1.0), (1.0, 1.0))
    expected = ((1.0, 1.0), (1.0, 1.0), (1.0, 1.0), (1.0, 1.0))
    codeflash_output = bbox_to_polygon(bbox) # 475ns -> 467ns (1.71% faster)

# 2. Edge Test Cases

def test_bbox_to_polygon_inverted_bbox():
    # Test with inverted bbox: top-left > bottom-right
    bbox = ((5.0, 7.0), (2.0, 3.0))
    expected = ((5.0, 7.0), (2.0, 7.0), (5.0, 3.0), (2.0, 3.0))
    codeflash_output = bbox_to_polygon(bbox) # 462ns -> 484ns (4.55% slower)

def test_bbox_to_polygon_vertical_line():
    # Test with a vertical line (x1 == x2)
    bbox = ((2.0, 3.0), (2.0, 7.0))
    expected = ((2.0, 3.0), (2.0, 3.0), (2.0, 7.0), (2.0, 7.0))
    codeflash_output = bbox_to_polygon(bbox) # 464ns -> 475ns (2.32% slower)

def test_bbox_to_polygon_horizontal_line():
    # Test with a horizontal line (y1 == y2)
    bbox = ((2.0, 7.0), (5.0, 7.0))
    expected = ((2.0, 7.0), (5.0, 7.0), (2.0, 7.0), (5.0, 7.0))
    codeflash_output = bbox_to_polygon(bbox) # 470ns -> 471ns (0.212% slower)

def test_bbox_to_polygon_large_negative_coords():
    # Test with large negative coordinates
    bbox = ((-1000.0, -2000.0), (-1500.0, -2500.0))
    expected = ((-1000.0, -2000.0), (-1500.0, -2000.0), (-1000.0, -2500.0), (-1500.0, -2500.0))
    codeflash_output = bbox_to_polygon(bbox) # 479ns -> 492ns (2.64% slower)

def test_bbox_to_polygon_extreme_float_precision():
    # Test with extreme float precision
    bbox = ((1e-10, 1e-10), (1e10, 1e10))
    expected = ((1e-10, 1e-10), (1e10, 1e-10), (1e-10, 1e10), (1e10, 1e10))
    codeflash_output = bbox_to_polygon(bbox) # 460ns -> 478ns (3.77% slower)

def test_bbox_to_polygon_inf_nan():
    # Test with inf and nan coordinates
    import math
    bbox = ((float('inf'), float('-inf')), (float('nan'), 0.0))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 472ns -> 471ns (0.212% faster)

def test_bbox_to_polygon_tuple_length():
    # Test that output always has 4 points, each with 2 coordinates
    bbox = ((1, 2), (3, 4))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 502ns -> 515ns (2.52% slower)
    for pt in result:
        pass



def test_bbox_to_polygon_none_input():
    # Test with None as input (should raise TypeError)
    bbox = None
    with pytest.raises(TypeError):
        codeflash_output = bbox_to_polygon(bbox); _ = codeflash_output # 1.27μs -> 1.29μs (1.62% slower)

# 3. Large Scale Test Cases

def test_bbox_to_polygon_many_boxes():
    # Test with a large number of bounding boxes
    bboxes = [((i, i+1), (i+2, i+3)) for i in range(1000)]
    expected_polygons = [
        ((i, i+1), (i+2, i+1), (i, i+3), (i+2, i+3))
        for i in range(1000)
    ]
    for bbox, expected in zip(bboxes, expected_polygons):
        codeflash_output = bbox_to_polygon(bbox) # 192μs -> 181μs (5.72% faster)

def test_bbox_to_polygon_large_values():
    # Test with bounding boxes with very large values
    large_val = 1e12
    bbox = ((large_val, large_val), (large_val+1, large_val+2))
    expected = ((large_val, large_val), (large_val+1, large_val), (large_val, large_val+2), (large_val+1, large_val+2))
    codeflash_output = bbox_to_polygon(bbox) # 467ns -> 522ns (10.5% slower)

def test_bbox_to_polygon_stress_randomized():
    # Stress test with randomized bounding boxes
    import random
    random.seed(42)
    for _ in range(100):
        x1 = random.uniform(-1e6, 1e6)
        y1 = random.uniform(-1e6, 1e6)
        x2 = random.uniform(-1e6, 1e6)
        y2 = random.uniform(-1e6, 1e6)
        bbox = ((x1, y1), (x2, y2))
        codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 19.6μs -> 18.4μs (6.26% faster)

def test_bbox_to_polygon_performance():
    # Performance test: ensure function runs quickly for 1000 boxes
    import time
    bboxes = [((i, i+1), (i+2, i+3)) for i in range(1000)]
    start = time.time()
    for bbox in bboxes:
        codeflash_output = bbox_to_polygon(bbox); _ = codeflash_output # 189μs -> 179μs (5.29% faster)
    duration = time.time() - start
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from typing import Tuple

# imports
import pytest  # used for our unit tests
from doctr.utils.geometry import bbox_to_polygon

# function to test
# Copyright (C) 2021-2025, Mindee.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.


BoundingBox = Tuple[Tuple[float, float], Tuple[float, float]]
Polygon4P = Tuple[Tuple[float, float], Tuple[float, float], Tuple[float, float], Tuple[float, float]]
from doctr.utils.geometry import bbox_to_polygon

# unit tests

# ------------------- Basic Test Cases -------------------

def test_basic_square_bbox():
    # Test with a simple square bounding box
    bbox = ((0.0, 0.0), (1.0, 1.0))
    expected = ((0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (1.0, 1.0))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 483ns -> 464ns (4.09% faster)

def test_basic_rectangle_bbox():
    # Test with a rectangle bounding box
    bbox = ((2.0, 3.0), (5.0, 7.0))
    expected = ((2.0, 3.0), (5.0, 3.0), (2.0, 7.0), (5.0, 7.0))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 463ns -> 452ns (2.43% faster)

def test_basic_negative_coordinates():
    # Test with negative coordinates
    bbox = ((-2.0, -3.0), (-1.0, -1.0))
    expected = ((-2.0, -3.0), (-1.0, -3.0), (-2.0, -1.0), (-1.0, -1.0))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 472ns -> 481ns (1.87% slower)

def test_basic_float_precision():
    # Test with floating point coordinates
    bbox = ((0.1, 0.2), (0.3, 0.4))
    expected = ((0.1, 0.2), (0.3, 0.2), (0.1, 0.4), (0.3, 0.4))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 458ns -> 434ns (5.53% faster)

def test_basic_zero_area():
    # Test with zero area (both corners are the same)
    bbox = ((1.0, 1.0), (1.0, 1.0))
    expected = ((1.0, 1.0), (1.0, 1.0), (1.0, 1.0), (1.0, 1.0))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 466ns -> 486ns (4.12% slower)

# ------------------- Edge Test Cases -------------------

def test_edge_inverted_bbox():
    # Test with inverted bbox (min > max)
    bbox = ((5.0, 7.0), (2.0, 3.0))
    expected = ((5.0, 7.0), (2.0, 7.0), (5.0, 3.0), (2.0, 3.0))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 463ns -> 447ns (3.58% faster)

def test_edge_large_numbers():
    # Test with very large numbers
    bbox = ((1e10, 1e10), (1e10 + 1000, 1e10 + 2000))
    expected = ((1e10, 1e10), (1e10 + 1000, 1e10), (1e10, 1e10 + 2000), (1e10 + 1000, 1e10 + 2000))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 477ns -> 488ns (2.25% slower)

def test_edge_small_numbers():
    # Test with very small (close to zero) numbers
    bbox = ((1e-10, 1e-10), (2e-10, 3e-10))
    expected = ((1e-10, 1e-10), (2e-10, 1e-10), (1e-10, 3e-10), (2e-10, 3e-10))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 473ns -> 477ns (0.839% slower)

def test_edge_identical_x():
    # Test where x coordinates are the same (vertical line)
    bbox = ((4.0, 2.0), (4.0, 5.0))
    expected = ((4.0, 2.0), (4.0, 2.0), (4.0, 5.0), (4.0, 5.0))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 452ns -> 454ns (0.441% slower)

def test_edge_identical_y():
    # Test where y coordinates are the same (horizontal line)
    bbox = ((2.0, 4.0), (5.0, 4.0))
    expected = ((2.0, 4.0), (5.0, 4.0), (2.0, 4.0), (5.0, 4.0))
    codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 474ns -> 469ns (1.07% faster)


def test_edge_wrong_length_bbox():
    # Test with wrong length bbox (not two points)
    bbox = ((0.0, 0.0),)
    with pytest.raises(IndexError):
        bbox_to_polygon(bbox) # 1.14μs -> 1.18μs (3.40% slower)

def test_edge_wrong_length_point():
    # Test with wrong length point (not two coordinates)
    bbox = ((0.0,), (1.0, 1.0))
    with pytest.raises(IndexError):
        bbox_to_polygon(bbox) # 1.11μs -> 1.06μs (4.34% faster)



def test_large_scale_extreme_coordinates():
    # Test with bounding boxes with extreme float values
    import sys
    max_float = sys.float_info.max
    min_float = -sys.float_info.max
    bboxes = [((min_float + i, min_float + i), (max_float - i, max_float - i)) for i in range(10)]
    for i, bbox in enumerate(bboxes):
        expected = (
            (min_float + i, min_float + i),
            (max_float - i, min_float + i),
            (min_float + i, max_float - i),
            (max_float - i, max_float - i)
        )
        codeflash_output = bbox_to_polygon(bbox); result = codeflash_output # 2.36μs -> 2.20μs (7.42% faster)


def test_large_scale_randomized():
    # Test with randomized bounding boxes
    import random
    random.seed(42)
    bboxes = []
    expected_polygons = []
    for _ in range(500):
        x0 = random.uniform(-1000, 1000)
        y0 = random.uniform(-1000, 1000)
        x1 = random.uniform(-1000, 1000)
        y1 = random.uniform(-1000, 1000)
        bbox = ((x0, y0), (x1, y1))
        bboxes.append(bbox)
        expected_polygons.append((
            (x0, y0),
            (x1, y0),
            (x0, y1),
            (x1, y1)
        ))
    results = [bbox_to_polygon(bbox) for bbox in bboxes] # 554ns -> 591ns (6.26% slower)
# 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-bbox_to_polygon-mg7ryhci and push.

Codeflash

The optimization reduces redundant tuple indexing operations by caching `bbox[0]` and `bbox[1]` in local variables `bbox0` and `bbox1`. 

In the original code, `bbox[0]` is accessed 3 times and `bbox[1]` is accessed 3 times within the single return statement. Each tuple indexing operation has overhead in Python. The optimized version performs each indexing only once, storing the results in local variables that are then reused.

This micro-optimization is particularly effective because:
- **Tuple indexing reduction**: Eliminates 4 redundant indexing operations (from 6 total to 2)
- **Local variable access**: Reading from local variables is faster than tuple indexing in Python
- **Hot path optimization**: This function is likely called frequently in document processing workflows

The test results show the optimization performs best on large-scale operations (5-7% speedup on batch processing tests like `test_bbox_to_polygon_many_boxes` and `test_bbox_to_polygon_stress_randomized`) where the indexing overhead compounds across many function calls. Individual function calls show mixed but generally positive results, with the overall 5% speedup representing meaningful performance gains for high-frequency geometric operations.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 1, 2025 09:19
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Oct 1, 2025
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.

1 participant