Skip to content

Conversation

@Erol444
Copy link
Contributor

@Erol444 Erol444 commented Feb 9, 2026

What does this PR do?

Adds Heatmap block (uses supervision's heatmap annotator), which supports both:

  • detections, so heatmap based on where detections were
  • tracklets, which ignores stationary objects (default: on), so we heatmap the movements not the objects

Type of Change

  • New feature (non-breaking change that adds functionality)

Testing

  • I have tested this change locally
  • I have added/updated tests for this change

Tested locally, demo below

heatmap2.mp4

From this workflow (ignores stationary objects)
image

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code where necessary, particularly in hard-to-understand areas
  • My changes generate no new warnings or errors
  • I have updated the documentation accordingly (if applicable)

@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Erol444
Copy link
Contributor Author

Erol444 commented Feb 9, 2026

seems like installing dependencies is failing

        File "<string>", line 198, in _build_z3
      __main__.LibError: Unable to build Z3.
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for z3-solver
error: failed-wheel-build-for-install

× Failed to build installable wheels for some pyproject.toml based projects
╰─> z3-solver

Comment on lines 166 to 192
key = "_".join(
map(
str,
[
position,
opacity,
radius,
kernel_size,
top_hue,
low_hue,
],
)
)

if key not in self.annotatorCache:
position_enum = getattr(sv.Position, position)
self.annotatorCache[key] = sv.HeatMapAnnotator(
position=position_enum,
opacity=opacity,
radius=radius,
kernel_size=kernel_size,
top_hue=top_hue,
low_hue=low_hue,
)

return self.annotatorCache[key]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡️Codeflash found 41% (0.41x) speedup for HeatmapVisualizationBlockV1.getAnnotator in inference/core/workflows/core_steps/visualizations/heatmap/v1.py

⏱️ Runtime : 9.44 milliseconds 6.71 milliseconds (best of 143 runs)

📝 Explanation and details

The optimized code achieves a 40% speedup by applying two key optimizations to the cache key generation and Position enum lookup:

Key Optimizations

1. Tuple-based cache key instead of string concatenation (~25% of speedup)

The original code builds a cache key using "_".join(map(str, [...])), which:

  • Creates a list of 6 string representations
  • Calls map() to convert each parameter to a string
  • Allocates a new joined string via "_".join()

The optimized version uses a tuple of strings as the cache key:

key = (position, str(opacity), str(radius), str(kernel_size), str(top_hue), str(low_hue))

This is faster because:

  • Tuples are hashable and work directly as dictionary keys
  • Avoids the overhead of join() and intermediate list creation
  • Python's tuple hashing is highly optimized
  • Line profiler shows key creation drops from 25.2% to ~18% of total time

2. Cached Position enum lookups (~15% of speedup)

The original code calls getattr(sv.Position, position) on every cache miss. The optimized version adds a secondary cache (_position_enum_cache) to store Position enum lookups:

pos_cache = getattr(self, "_position_enum_cache", None)
if pos_cache is None:
    pos_cache = {}
    self._position_enum_cache = pos_cache

position_enum = pos_cache.get(position)
if position_enum is None:
    position_enum = getattr(sv.Position, position)
    pos_cache[position] = position_enum

This avoids repeated getattr() calls when the same position string is used multiple times.

3. Fast-path return for cache hits

The code now uses self.annotatorCache.get(key) with an early return when the annotator exists, avoiding the key not in self.annotatorCache membership test followed by dictionary lookup.

Test Results Analysis

The optimization performs best when:

  • High cache hit rates (53% faster in test_large_scale_repeated_calls_do_not_grow_cache with 1000 identical calls)
  • Repeated position strings (benefits from Position enum caching)
  • Sequential access patterns (36-43% faster in tests creating/reusing many annotators)

Cache misses still show 20-30% improvement due to the more efficient cache key construction, making this optimization effective across all usage patterns.

Impact Assessment

Since function_references are not available, we can't definitively assess the hot path impact. However, given that this is a visualization block that likely processes frames in video workflows, the caching optimizations would be especially valuable in scenarios where:

  • The same visualization parameters are reused across multiple frames
  • A limited set of positions are used repeatedly
  • High-throughput video processing requires fast annotator retrieval

Correctness verification report:

Test Status
⏪ Replay Tests 🔘 None Found
⚙️ Existing Unit Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
🌀 Generated Regression Tests 4396 Passed
📊 Tests Coverage 100.0%
🌀 Click to see Generated Regression Tests
import types
from typing import Any

# imports
import pytest  # used for our unit tests

# import the external supervision module used by the function under test
import supervision as sv
from inference.core.workflows.core_steps.visualizations.heatmap.v1 import (
    HeatmapVisualizationBlockV1,
)


# Helper factory to create an instance of HeatmapVisualizationBlockV1 without invoking
# its potentially complex __init__ chain. We use object.__new__ to get a real instance
# (so isinstance checks pass) and then we set the attributes the method relies on.
def _make_block_without_init() -> HeatmapVisualizationBlockV1:
    # Create an instance without calling __init__
    inst = object.__new__(HeatmapVisualizationBlockV1)
    # Initialize attributes used by getAnnotator
    inst.annotatorCache = {}
    inst._track_history = {}
    return inst


def test_getAnnotator_returns_heatmap_annotator_and_caches():
    # Create a bare instance and ensure a clean cache
    block = _make_block_without_init()

    # Use a standard position name that exists in the supervision.Position enum.
    # 'TOP_LEFT' is a commonly provided enum member in many visualization libs.
    position_name = "TOP_LEFT"
    opacity = 0.5
    radius = 10
    kernel_size = 5
    top_hue = 120
    low_hue = 60

    # First call should create and return a HeatMapAnnotator instance
    codeflash_output = block.getAnnotator(
        position_name, opacity, radius, kernel_size, top_hue, low_hue
    )
    annotator1 = codeflash_output  # 6.85μs -> 5.65μs (21.3% faster)

    # A cache key should have been added to the block.annotatorCache mapping
    # The method creates a string key by joining the str() of each parameter. Verify key presence.
    expected_key = "_".join(
        map(
            str,
            [position_name, opacity, radius, kernel_size, top_hue, low_hue],
        )
    )

    # Second call with the exact same parameters must return the cached object (same identity)
    codeflash_output = block.getAnnotator(
        position_name, opacity, radius, kernel_size, top_hue, low_hue
    )
    annotator2 = codeflash_output  # 2.13μs -> 1.85μs (15.1% faster)


def test_getAnnotator_different_params_create_distinct_annotators():
    # Ensure different parameter tuples produce different cached annotators
    block = _make_block_without_init()

    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 5, 3, 100, 50)
    a = codeflash_output  # 6.43μs -> 5.15μs (24.9% faster)
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.6, 5, 3, 100, 50)
    b = codeflash_output  # 3.33μs -> 2.71μs (23.0% faster)
    codeflash_output = block.getAnnotator("TOP_RIGHT", 0.5, 5, 3, 100, 50)
    c = codeflash_output  # 3.14μs -> 2.33μs (34.4% faster)


def test_getAnnotator_opacity_and_integer_stringification_affect_cache_key():
    # Demonstrates that values with different string representations produce different keys.
    block = _make_block_without_init()

    # opacity as integer 1 and float 1.0 produce different str() values: '1' vs '1.0'
    codeflash_output = block.getAnnotator("TOP_LEFT", 1, 1, 1, 10, 0)
    annot_int = codeflash_output  # 5.08μs -> 3.97μs (28.0% faster)
    codeflash_output = block.getAnnotator("TOP_LEFT", 1.0, 1, 1, 10, 0)
    annot_float = codeflash_output  # 3.75μs -> 2.98μs (25.5% faster)


def test_getAnnotator_handles_minimum_boundary_values():
    # Test with boundary-like values for integers and floats that are commonly accepted
    block = _make_block_without_init()

    # Use zeros and smallest reasonable kernel_size. This should succeed and return an annotator.
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.0, 0, 1, 0, 0)
    annot = codeflash_output  # 5.76μs -> 4.57μs (26.1% faster)


def test_getAnnotator_raises_for_invalid_position_string():
    # Lowercase / misspelled position names likely don't exist in the Position enum -> AttributeError
    block = _make_block_without_init()

    with pytest.raises(AttributeError):
        # 'top_left' (lowercase) should not be an attribute on sv.Position in typical enums
        block.getAnnotator(
            "top_left", 0.5, 5, 3, 100, 50
        )  # 6.90μs -> 5.37μs (28.5% faster)


def test_getAnnotator_raises_for_non_string_position_type():
    # Passing a non-string position should cause getattr to raise a TypeError
    block = _make_block_without_init()

    with pytest.raises(TypeError):
        # Non-string 123 is not a valid attribute name for getattr
        block.getAnnotator(123, 0.5, 5, 3, 100, 50)  # 5.98μs -> 4.67μs (28.1% faster)


def test_large_scale_cache_population_unique_radius_up_to_1000():
    # Populate the cache with 1000 distinct annotators by varying radius.
    block = _make_block_without_init()

    position = "TOP_LEFT"
    opacity = 0.5
    kernel_size = 3
    top_hue = 180
    low_hue = 0

    # Insert 1000 distinct annotators (radius from 1..1000)
    for r in range(1, 1001):  # 1000 iterations
        codeflash_output = block.getAnnotator(
            position, opacity, r, kernel_size, top_hue, low_hue
        )
        annot = codeflash_output  # 2.43ms -> 1.75ms (39.1% faster)


def test_large_scale_repeated_calls_do_not_grow_cache():
    # Ensure that repeated calls with the same parameters do not increase cache size
    block = _make_block_without_init()

    position = "TOP_LEFT"
    opacity = 0.75
    radius = 7
    kernel_size = 3
    top_hue = 200
    low_hue = 50

    # Call the same parameters many times
    for _ in range(1000):  # 1000 iterations but identical args
        codeflash_output = block.getAnnotator(
            position, opacity, radius, kernel_size, top_hue, low_hue
        )
        annot = codeflash_output  # 1.66ms -> 1.08ms (53.0% faster)


# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
import pytest
import supervision as sv
from inference.core.workflows.core_steps.visualizations.heatmap.v1 import (
    HeatmapVisualizationBlockV1,
)


def test_getAnnotator_returns_base_annotator_instance():
    """Test that getAnnotator returns a BaseAnnotator instance."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.5,
        radius=50,
        kernel_size=15,
        top_hue=120,
        low_hue=60,
    )
    annotator = codeflash_output  # 7.73μs -> 7.18μs (7.67% faster)


def test_getAnnotator_returns_heatmap_annotator():
    """Test that getAnnotator specifically returns a HeatMapAnnotator."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_CENTER",
        opacity=0.7,
        radius=40,
        kernel_size=10,
        top_hue=100,
        low_hue=50,
    )
    annotator = codeflash_output  # 7.16μs -> 6.01μs (19.2% faster)


def test_getAnnotator_caches_results():
    """Test that getAnnotator caches annotators and returns the same instance."""
    block = HeatmapVisualizationBlockV1()
    params = {
        "position": "TOP_RIGHT",
        "opacity": 0.6,
        "radius": 45,
        "kernel_size": 12,
        "top_hue": 110,
        "low_hue": 55,
    }

    # Call getAnnotator twice with identical parameters
    codeflash_output = block.getAnnotator(**params)
    annotator1 = codeflash_output  # 6.77μs -> 6.31μs (7.29% faster)
    codeflash_output = block.getAnnotator(**params)
    annotator2 = codeflash_output  # 2.75μs -> 2.22μs (23.4% faster)


def test_getAnnotator_different_params_creates_different_instances():
    """Test that different parameters result in different cached annotators."""
    block = HeatmapVisualizationBlockV1()

    # Create annotator with one set of parameters
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.5,
        radius=50,
        kernel_size=15,
        top_hue=120,
        low_hue=60,
    )
    annotator1 = codeflash_output  # 6.55μs -> 5.48μs (19.5% faster)

    # Create annotator with different parameters
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.6,  # Different opacity
        radius=50,
        kernel_size=15,
        top_hue=120,
        low_hue=60,
    )
    annotator2 = codeflash_output  # 3.76μs -> 3.16μs (19.0% faster)


def test_getAnnotator_with_bottom_left_position():
    """Test getAnnotator with BOTTOM_LEFT position."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="BOTTOM_LEFT",
        opacity=0.4,
        radius=35,
        kernel_size=8,
        top_hue=90,
        low_hue=40,
    )
    annotator = codeflash_output  # 6.72μs -> 5.48μs (22.7% faster)


def test_getAnnotator_with_bottom_center_position():
    """Test getAnnotator with BOTTOM_CENTER position."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="BOTTOM_CENTER",
        opacity=0.3,
        radius=30,
        kernel_size=7,
        top_hue=80,
        low_hue=30,
    )
    annotator = codeflash_output  # 6.60μs -> 5.11μs (29.2% faster)


def test_getAnnotator_with_center_position():
    """Test getAnnotator with CENTER position."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="CENTER",
        opacity=0.5,
        radius=50,
        kernel_size=15,
        top_hue=120,
        low_hue=60,
    )
    annotator = codeflash_output  # 6.60μs -> 5.49μs (20.3% faster)


def test_getAnnotator_with_minimum_opacity():
    """Test getAnnotator with minimum opacity value (0.0)."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.0,
        radius=50,
        kernel_size=15,
        top_hue=120,
        low_hue=60,
    )
    annotator = codeflash_output  # 6.02μs -> 4.90μs (22.9% faster)


def test_getAnnotator_with_maximum_opacity():
    """Test getAnnotator with maximum opacity value (1.0)."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=1.0,
        radius=50,
        kernel_size=15,
        top_hue=120,
        low_hue=60,
    )
    annotator = codeflash_output  # 5.83μs -> 4.61μs (26.5% faster)


def test_getAnnotator_with_minimum_radius():
    """Test getAnnotator with minimum radius value (1)."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.5,
        radius=1,
        kernel_size=15,
        top_hue=120,
        low_hue=60,
    )
    annotator = codeflash_output  # 6.71μs -> 5.41μs (24.1% faster)


def test_getAnnotator_with_large_radius():
    """Test getAnnotator with large radius value (1000)."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.5,
        radius=1000,
        kernel_size=15,
        top_hue=120,
        low_hue=60,
    )
    annotator = codeflash_output  # 6.88μs -> 5.40μs (27.5% faster)


def test_getAnnotator_with_minimum_kernel_size():
    """Test getAnnotator with minimum kernel size value (1)."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.5,
        radius=50,
        kernel_size=1,
        top_hue=120,
        low_hue=60,
    )
    annotator = codeflash_output  # 6.52μs -> 5.30μs (23.1% faster)


def test_getAnnotator_with_large_kernel_size():
    """Test getAnnotator with large kernel size value (1000)."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.5,
        radius=50,
        kernel_size=1000,
        top_hue=120,
        low_hue=60,
    )
    annotator = codeflash_output  # 7.03μs -> 5.65μs (24.5% faster)


def test_getAnnotator_with_minimum_hue_values():
    """Test getAnnotator with minimum hue values (0)."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.5,
        radius=50,
        kernel_size=15,
        top_hue=0,
        low_hue=0,
    )
    annotator = codeflash_output  # 6.59μs -> 5.27μs (25.1% faster)


def test_getAnnotator_with_maximum_hue_values():
    """Test getAnnotator with maximum hue values (360)."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.5,
        radius=50,
        kernel_size=15,
        top_hue=360,
        low_hue=360,
    )
    annotator = codeflash_output  # 6.91μs -> 5.58μs (23.9% faster)


def test_getAnnotator_with_equal_hue_values():
    """Test getAnnotator when top_hue equals low_hue."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.5,
        radius=50,
        kernel_size=15,
        top_hue=120,
        low_hue=120,
    )
    annotator = codeflash_output  # 6.55μs -> 5.30μs (23.6% faster)


def test_getAnnotator_with_fractional_opacity():
    """Test getAnnotator with fractional opacity values."""
    block = HeatmapVisualizationBlockV1()
    codeflash_output = block.getAnnotator(
        position="TOP_LEFT",
        opacity=0.333333,
        radius=50,
        kernel_size=15,
        top_hue=120,
        low_hue=60,
    )
    annotator = codeflash_output  # 6.61μs -> 5.22μs (26.7% faster)


def test_getAnnotator_cache_key_uniqueness_position():
    """Test that cache keys are unique based on position parameter."""
    block = HeatmapVisualizationBlockV1()

    # Create annotators with different positions
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 50, 15, 120, 60)
    ann1 = codeflash_output  # 6.38μs -> 4.93μs (29.5% faster)
    codeflash_output = block.getAnnotator("TOP_RIGHT", 0.5, 50, 15, 120, 60)
    ann2 = codeflash_output  # 3.83μs -> 3.02μs (26.5% faster)


def test_getAnnotator_cache_key_uniqueness_opacity():
    """Test that cache keys are unique based on opacity parameter."""
    block = HeatmapVisualizationBlockV1()

    # Create annotators with different opacities
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 50, 15, 120, 60)
    ann1 = codeflash_output  # 6.43μs -> 4.93μs (30.5% faster)
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.6, 50, 15, 120, 60)
    ann2 = codeflash_output  # 3.51μs -> 2.77μs (26.4% faster)


def test_getAnnotator_cache_key_uniqueness_radius():
    """Test that cache keys are unique based on radius parameter."""
    block = HeatmapVisualizationBlockV1()

    # Create annotators with different radii
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 50, 15, 120, 60)
    ann1 = codeflash_output  # 6.22μs -> 4.79μs (29.9% faster)
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 51, 15, 120, 60)
    ann2 = codeflash_output  # 3.41μs -> 2.62μs (30.2% faster)


def test_getAnnotator_cache_key_uniqueness_kernel_size():
    """Test that cache keys are unique based on kernel_size parameter."""
    block = HeatmapVisualizationBlockV1()

    # Create annotators with different kernel sizes
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 50, 15, 120, 60)
    ann1 = codeflash_output  # 6.08μs -> 4.78μs (27.3% faster)
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 50, 16, 120, 60)
    ann2 = codeflash_output  # 3.30μs -> 2.54μs (29.5% faster)


def test_getAnnotator_cache_key_uniqueness_top_hue():
    """Test that cache keys are unique based on top_hue parameter."""
    block = HeatmapVisualizationBlockV1()

    # Create annotators with different top_hue values
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 50, 15, 120, 60)
    ann1 = codeflash_output  # 6.08μs -> 4.74μs (28.3% faster)
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 50, 15, 121, 60)
    ann2 = codeflash_output  # 3.41μs -> 2.50μs (36.0% faster)


def test_getAnnotator_cache_key_uniqueness_low_hue():
    """Test that cache keys are unique based on low_hue parameter."""
    block = HeatmapVisualizationBlockV1()

    # Create annotators with different low_hue values
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 50, 15, 120, 60)
    ann1 = codeflash_output  # 6.03μs -> 4.76μs (26.7% faster)
    codeflash_output = block.getAnnotator("TOP_LEFT", 0.5, 50, 15, 120, 61)
    ann2 = codeflash_output  # 3.28μs -> 2.50μs (30.8% faster)


def test_getAnnotator_with_all_positions():
    """Test getAnnotator with all valid Position enum values."""
    block = HeatmapVisualizationBlockV1()

    # Test all valid sv.Position values by trying common positions
    positions = [
        "TOP_LEFT",
        "TOP_CENTER",
        "TOP_RIGHT",
        "CENTER_LEFT",
        "CENTER",
        "CENTER_RIGHT",
        "BOTTOM_LEFT",
        "BOTTOM_CENTER",
        "BOTTOM_RIGHT",
    ]

    for position in positions:
        # Verify each position can create an annotator
        codeflash_output = block.getAnnotator(position, 0.5, 50, 15, 120, 60)
        annotator = codeflash_output  # 29.0μs -> 23.9μs (21.5% faster)


def test_getAnnotator_cache_size_after_multiple_calls():
    """Test that cache grows correctly with multiple different parameter sets."""
    block = HeatmapVisualizationBlockV1()

    # Create 5 different annotators
    for i in range(5):
        block.getAnnotator(
            "TOP_LEFT", 0.5, 50 + i, 15, 120, 60
        )  # 17.0μs -> 13.3μs (27.7% faster)


def test_getAnnotator_cache_no_growth_with_repeated_calls():
    """Test that cache does not grow when calling with same parameters."""
    block = HeatmapVisualizationBlockV1()

    params = {
        "position": "TOP_LEFT",
        "opacity": 0.5,
        "radius": 50,
        "kernel_size": 15,
        "top_hue": 120,
        "low_hue": 60,
    }

    # Call getAnnotator 10 times with identical parameters
    for _ in range(10):
        block.getAnnotator(**params)  # 25.0μs -> 18.8μs (33.3% faster)


def test_getAnnotator_caching_performance_with_many_combinations():
    """Test getAnnotator performance and caching with many parameter combinations."""
    block = HeatmapVisualizationBlockV1()

    # Create 100 different parameter combinations
    positions = ["TOP_LEFT", "TOP_CENTER", "TOP_RIGHT", "CENTER", "BOTTOM_LEFT"]
    opacities = [0.1, 0.3, 0.5, 0.7, 0.9]
    radii = [10, 30, 50, 70, 90]
    kernel_sizes = [5, 10, 15, 20]

    # Generate annotators with various combinations
    count = 0
    for position in positions:
        for opacity in opacities:
            for radius in radii:
                for kernel_size in kernel_sizes:
                    # Limit to a reasonable number to avoid excessive test time
                    if count >= 100:
                        break
                    codeflash_output = block.getAnnotator(
                        position, opacity, radius, kernel_size, 120, 60
                    )
                    annotator = codeflash_output
                    count += 1


def test_getAnnotator_repeated_access_same_key():
    """Test that repeated access with the same key returns cached instance 1000 times."""
    block = HeatmapVisualizationBlockV1()

    params = {
        "position": "TOP_LEFT",
        "opacity": 0.5,
        "radius": 50,
        "kernel_size": 15,
        "top_hue": 120,
        "low_hue": 60,
    }

    # Access the same annotator 1000 times
    codeflash_output = block.getAnnotator(**params)
    first_annotator = codeflash_output  # 6.47μs -> 5.36μs (20.7% faster)
    for _ in range(1000):
        codeflash_output = block.getAnnotator(**params)
        annotator = codeflash_output  # 1.86ms -> 1.30ms (43.5% faster)


def test_getAnnotator_sequential_different_parameters_1000_times():
    """Test creating 1000 different annotators with sequential parameter variations."""
    block = HeatmapVisualizationBlockV1()

    # Create 1000 annotators with incrementally different parameters
    for i in range(1000):
        radius = 10 + (i % 100)  # Vary radius from 10 to 109
        kernel_size = 5 + (i % 50)  # Vary kernel size from 5 to 54
        opacity = 0.1 + ((i % 90) / 100.0)  # Vary opacity from 0.1 to 0.99

        codeflash_output = block.getAnnotator(
            "TOP_LEFT",
            opacity,
            radius,
            kernel_size,
            120,
            60,
        )
        annotator = codeflash_output  # 2.45ms -> 1.80ms (36.7% faster)


def test_getAnnotator_cache_consistency_with_large_dataset():
    """Test that cache consistency is maintained over many operations."""
    block = HeatmapVisualizationBlockV1()

    # Create a set of test parameter combinations
    test_cases = []
    for i in range(100):
        test_cases.append(
            {
                "position": ["TOP_LEFT", "TOP_RIGHT", "CENTER"][i % 3],
                "opacity": 0.1 + ((i % 10) * 0.1),
                "radius": 10 + (i % 50),
                "kernel_size": 5 + (i % 30),
                "top_hue": 60 + (i % 360),
                "low_hue": 30 + (i % 360),
            }
        )

    # Access each test case twice
    first_access_results = {}
    for idx, params in enumerate(test_cases):
        first_access_results[idx] = block.getAnnotator(
            **params
        )  # 284μs -> 214μs (32.3% faster)

    # Verify second access returns the same instances
    for idx, params in enumerate(test_cases):
        codeflash_output = block.getAnnotator(**params)
        second_access = codeflash_output  # 199μs -> 138μs (43.3% faster)


# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To test or edit this optimization locally git merge codeflash/optimize-pr1986-2026-02-09T21.57.22

Click to see suggested changes
Suggested change
key = "_".join(
map(
str,
[
position,
opacity,
radius,
kernel_size,
top_hue,
low_hue,
],
)
)
if key not in self.annotatorCache:
position_enum = getattr(sv.Position, position)
self.annotatorCache[key] = sv.HeatMapAnnotator(
position=position_enum,
opacity=opacity,
radius=radius,
kernel_size=kernel_size,
top_hue=top_hue,
low_hue=low_hue,
)
return self.annotatorCache[key]
# Build a tuple key of stringified components to avoid creating a joined string
key = (
position,
str(opacity),
str(radius),
str(kernel_size),
str(top_hue),
str(low_hue),
)
# Fast-path return if cached
annotator = self.annotatorCache.get(key)
if annotator is not None:
return annotator
# Cache Position enum lookups to avoid repeated getattr calls
pos_cache = getattr(self, "_position_enum_cache", None)
if pos_cache is None:
pos_cache = {}
self._position_enum_cache = pos_cache
position_enum = pos_cache.get(position)
if position_enum is None:
# Preserve original behavior: if attribute missing, let AttributeError propagate
position_enum = getattr(sv.Position, position)
pos_cache[position] = position_enum
annotator = sv.HeatMapAnnotator(
position=position_enum,
opacity=opacity,
radius=radius,
kernel_size=kernel_size,
top_hue=top_hue,
low_hue=low_hue,
)
self.annotatorCache[key] = annotator
return annotator

Static Badge

@grzegorz-roboflow grzegorz-roboflow merged commit cb0be0f into main Feb 12, 2026
51 checks passed
@grzegorz-roboflow grzegorz-roboflow deleted the heatmap_block branch February 12, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants