Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Nov 13, 2025

📄 170% (1.70x) speedup for _hungarian_algorithm in marimo/_utils/cell_matching.py

⏱️ Runtime : 181 milliseconds 67.0 milliseconds (best of 80 runs)

📝 Explanation and details

The optimized code achieves a 170% speedup by eliminating redundant matrix traversals and reducing cache misses through more efficient memory access patterns.

Key Optimizations:

1. Precomputed Index Sets

  • The original code repeatedly checks row_assignment[i] == -1 and col_assignment[j] == -1 across nested O(n²) loops
  • The optimized version precomputes uncovered_rows and uncovered_cols once per iteration, then operates only on these smaller sets
  • This reduces the inner loop complexity from O(n²) to O(uncovered × uncovered), which is typically much smaller

2. Row Reference Caching

  • Added row = score_matrix[i] before inner loops to avoid repeated score_matrix[i] lookups
  • This eliminates Python's array indexing overhead on every matrix access
  • Particularly effective in the tight loops that dominate runtime (Steps 1, 2, and 4)

3. Batch Matrix Updates

  • Instead of checking cover conditions for every (i,j) pair, the optimization separates covered/uncovered positions
  • Matrix updates are now batched by category: subtract from uncovered positions, add to covered positions
  • This eliminates redundant conditional checks within the nested loops

Performance Impact:

The line profiler shows the most dramatic improvements in Step 4's nested loops:

  • Original: 14.8% + 14.7% = 29.5% of total time in conditional checks
  • Optimized: 29.2% + 40.5% = 69.7% of total time, but with much faster per-iteration execution

The optimization is particularly effective for:

  • Large matrices (100x100+): 195-200% speedup as shown in test cases
  • Sparse assignment scenarios: When many rows/columns remain unassigned, the uncovered sets are much smaller than n
  • Cell matching workloads: Based on the function reference, this is called from _match_cell_ids_by_similarity which processes code similarity matrices, making the performance gains directly beneficial to cell matching operations in the Marimo notebook environment

The improvements maintain identical algorithmic behavior while significantly reducing the constant factors that dominate the Hungarian algorithm's runtime.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 42 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 2 Passed
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest  # used for our unit tests
from marimo._utils.cell_matching import _hungarian_algorithm

# unit tests

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

def test_single_element_matrix():
    # 1x1 matrix, trivial assignment
    scores = [[42.0]]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 5.51μs -> 5.51μs (0.109% slower)

def test_two_by_two_distinct_minimum():
    # 2x2 matrix with clear best assignment
    scores = [
        [1, 100],
        [100, 1]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 7.55μs -> 7.61μs (0.736% slower)

def test_two_by_two_swapped_minimum():
    # 2x2 matrix with swapped minimum
    scores = [
        [100, 1],
        [1, 100]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 7.51μs -> 7.58μs (0.989% slower)

def test_three_by_three_diagonal_minimum():
    # 3x3 matrix with minimum on diagonal
    scores = [
        [1, 2, 3],
        [2, 1, 3],
        [3, 2, 1]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 9.46μs -> 9.12μs (3.79% faster)

def test_three_by_three_off_diagonal_minimum():
    # 3x3 matrix with minimum off-diagonal
    scores = [
        [10, 2, 3],
        [2, 10, 3],
        [3, 2, 10]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 13.4μs -> 13.7μs (2.39% slower)

def test_three_by_three_all_equal():
    # 3x3 matrix, all values equal
    scores = [
        [5, 5, 5],
        [5, 5, 5],
        [5, 5, 5]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 9.36μs -> 8.96μs (4.38% faster)

def test_four_by_four_unique_minimums():
    # 4x4 matrix, each row has a unique minimum in a different column
    scores = [
        [1, 100, 100, 100],
        [100, 1, 100, 100],
        [100, 100, 1, 100],
        [100, 100, 100, 1]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 11.1μs -> 11.1μs (0.615% faster)

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

def test_empty_matrix():
    # Empty matrix should return empty assignment
    scores = []
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 2.65μs -> 2.84μs (6.55% slower)

def test_one_row_multiple_columns():
    # 1xN matrix, should assign row 0 to column with minimum value
    scores = [[10, 5, 7, 2, 8]]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 5.68μs -> 5.79μs (1.76% slower)

def test_large_values_and_negatives():
    # Matrix with large values and negatives
    scores = [
        [1000, -1000, 0],
        [0, 1000, -1000],
        [-1000, 0, 1000]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 10.1μs -> 9.81μs (3.11% faster)

def test_matrix_with_zero_rows():
    # Matrix with zero rows (empty)
    scores = []
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 2.70μs -> 2.69μs (0.223% faster)

def test_matrix_with_zero_columns():
    # Matrix with zero columns (empty rows)
    scores = [[], [], []]
    with pytest.raises(ValueError):
        # min([]) will raise ValueError
        _hungarian_algorithm(scores) # 2.90μs -> 2.90μs (0.069% faster)

def test_matrix_with_duplicate_minima():
    # Multiple zeros in same row/column
    scores = [
        [0, 0, 1],
        [1, 0, 0],
        [0, 1, 0]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 10.1μs -> 9.70μs (4.26% faster)


def test_matrix_with_inf_and_nan():
    # Matrix with inf and nan values
    import math
    scores = [
        [math.inf, 1, 2],
        [3, math.nan, 1],
        [2, 3, 1]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 11.8μs -> 11.5μs (2.62% faster)

def test_matrix_with_all_inf():
    # All values are inf, assignment is arbitrary
    import math
    scores = [
        [math.inf, math.inf],
        [math.inf, math.inf]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 9.54μs -> 10.2μs (6.62% slower)

def test_matrix_with_all_negative_inf():
    # All values are -inf, assignment is arbitrary
    import math
    scores = [
        [-math.inf, -math.inf],
        [-math.inf, -math.inf]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 8.89μs -> 9.14μs (2.76% slower)

# ---------------------------
# Large Scale Test Cases
# ---------------------------

def test_large_identity_matrix():
    # 100x100 identity matrix, minimum on diagonal
    size = 100
    scores = [[0 if i == j else 1000 for j in range(size)] for i in range(size)]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 1.61ms -> 1.45ms (10.7% faster)

def test_large_uniform_matrix():
    # 100x100 matrix, all values equal
    size = 100
    scores = [[5 for _ in range(size)] for _ in range(size)]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 1.68ms -> 1.38ms (21.8% faster)

def test_large_random_matrix():
    # 100x100 matrix with random values, deterministic seed
    import random
    size = 100
    random.seed(42)
    scores = [[random.randint(1, 1000) for _ in range(size)] for _ in range(size)]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 23.7ms -> 8.04ms (195% faster)

def test_large_sparse_matrix():
    # 100x100 matrix, mostly high values, some zeros
    size = 100
    scores = [[1000 for _ in range(size)] for _ in range(size)]
    for i in range(size):
        scores[i][i] = 0  # minimum on diagonal
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 1.62ms -> 1.45ms (11.6% faster)

def test_large_matrix_with_duplicate_minima():
    # 100x100 matrix, two zeros per row in different columns
    size = 100
    scores = [[1000 for _ in range(size)] for _ in range(size)]
    for i in range(size):
        scores[i][i] = 0
        scores[i][(i+1)%size] = 0
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 1.63ms -> 1.46ms (11.6% faster)

def test_large_matrix_performance():
    # 200x200 matrix, all values random, test for performance
    import random
    size = 200
    random.seed(123)
    scores = [[random.randint(1, 1000) for _ in range(size)] for _ in range(size)]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 142ms -> 47.4ms (200% 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
from marimo._utils.cell_matching import _hungarian_algorithm

# unit tests

# --- Basic Test Cases ---
def test_2x2_unique_assignment():
    # 2x2 matrix, easy assignment
    scores = [
        [1, 2],
        [2, 1]
    ]
    # Best assignment: row 0 to col 0, row 1 to col 1
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 7.94μs -> 8.30μs (4.37% slower)
    # Check that assignment is optimal (sum of assigned scores is minimal)
    total = sum(scores[result[i]][i] for i in range(2))

def test_3x3_distinct_minima():
    # 3x3 matrix, each row has a distinct minimum in a different column
    scores = [
        [1, 2, 3],
        [2, 1, 3],
        [3, 2, 1]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 9.76μs -> 9.51μs (2.68% faster)
    total = sum(scores[result[i]][i] for i in range(3))

def test_3x3_multiple_optimal_assignments():
    # 3x3 matrix with multiple zeros, multiple optimal solutions
    scores = [
        [0, 0, 1],
        [0, 1, 0],
        [1, 0, 0]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 9.09μs -> 9.17μs (0.862% slower)
    # All assignments should yield optimal cost 0
    total = sum(scores[result[i]][i] for i in range(3))

def test_4x4_diagonal_minimum():
    # 4x4 matrix with diagonal minima
    scores = [
        [1, 2, 3, 4],
        [2, 1, 4, 3],
        [3, 4, 1, 2],
        [4, 3, 2, 1]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 11.5μs -> 10.9μs (5.42% faster)
    # Should assign each row to its diagonal minimum
    total = sum(scores[result[i]][i] for i in range(4))

# --- Edge Test Cases ---
def test_empty_matrix():
    # Empty matrix should return empty assignment
    scores = []
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 2.59μs -> 2.73μs (5.06% slower)

def test_single_element_matrix():
    # Single element matrix
    scores = [[42]]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 5.59μs -> 5.76μs (3.00% slower)

def test_all_equal_elements():
    # All elements are equal
    scores = [
        [5, 5, 5],
        [5, 5, 5],
        [5, 5, 5]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 9.34μs -> 9.14μs (2.16% faster)
    total = sum(scores[result[i]][i] for i in range(3))

def test_negative_values():
    # Matrix with negative values
    scores = [
        [-1, -2, -3],
        [-2, -1, -3],
        [-3, -2, -1]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 9.49μs -> 9.31μs (1.94% faster)
    total = sum(scores[result[i]][i] for i in range(3))

def test_large_positive_and_negative_values():
    # Large positive/negative values, check for overflow/precision
    scores = [
        [1e9, -1e9, 0],
        [0, 1e9, -1e9],
        [-1e9, 0, 1e9]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 9.98μs -> 10.0μs (0.359% slower)
    # The optimal assignment should be minimal sum
    total = sum(scores[result[i]][i] for i in range(3))


def test_duplicate_minima():
    # Multiple zeros in a row, function should pick any valid assignment
    scores = [
        [0, 0, 1],
        [1, 0, 0],
        [0, 1, 0]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 10.8μs -> 10.6μs (1.78% faster)
    total = sum(scores[result[i]][i] for i in range(3))

def test_large_floats():
    # Matrix with very large floats, check for precision
    scores = [
        [1e100, 1e101, 1e102],
        [1e102, 1e100, 1e101],
        [1e101, 1e102, 1e100]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 10.1μs -> 9.66μs (4.18% faster)
    total = sum(scores[result[i]][i] for i in range(3))

# --- Large Scale Test Cases ---
def test_10x10_random_pattern():
    # 10x10 matrix with random pattern, known optimal assignment
    scores = [
        [i + j for j in range(10)] for i in range(10)
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 29.3μs -> 26.5μs (10.8% faster)
    total = sum(scores[result[i]][i] for i in range(10))

def test_100x100_identity_matrix():
    # 100x100 identity matrix, optimal assignment is diagonal
    n = 100
    scores = [[0 if i == j else 1 for j in range(n)] for i in range(n)]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 1.49ms -> 1.31ms (14.2% faster)
    total = sum(scores[result[i]][i] for i in range(n))

def test_100x100_all_ones():
    # All elements are ones
    n = 100
    scores = [[1 for _ in range(n)] for _ in range(n)]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 1.67ms -> 1.37ms (22.0% faster)
    total = sum(scores[result[i]][i] for i in range(n))

def test_100x100_diagonal_minimum():
    # Diagonal elements are zero, others are large
    n = 100
    scores = [[0 if i == j else 1e6 for j in range(n)] for i in range(n)]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 1.69ms -> 1.50ms (12.8% faster)
    total = sum(scores[result[i]][i] for i in range(n))

def test_50x50_random_large_values():
    # 50x50 matrix with random large values, test performance
    import random
    random.seed(42)
    n = 50
    scores = [[random.randint(0, 1000) for _ in range(n)] for _ in range(n)]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 3.52ms -> 1.37ms (158% faster)

# --- Determinism and Robustness ---
def test_determinism():
    # Repeated calls with same input should yield same output
    scores = [
        [1, 2, 3],
        [2, 1, 3],
        [3, 2, 1]
    ]
    codeflash_output = _hungarian_algorithm(scores); result1 = codeflash_output # 9.46μs -> 9.58μs (1.22% slower)
    codeflash_output = _hungarian_algorithm(scores); result2 = codeflash_output # 5.71μs -> 5.49μs (4.02% faster)

def test_assignment_is_permutation():
    # Check that assignment is a permutation of row indices
    scores = [
        [4, 1, 3],
        [2, 0, 5],
        [3, 2, 2]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 13.4μs -> 13.6μs (1.11% slower)

def test_assignment_minimizes_cost():
    # Check that assignment minimizes total cost
    scores = [
        [10, 19, 8],
        [10, 18, 7],
        [13, 16, 9]
    ]
    codeflash_output = _hungarian_algorithm(scores); result = codeflash_output # 8.95μs -> 8.71μs (2.73% faster)
    total = sum(scores[result[i]][i] for i in range(3))
    # Brute force for small n
    import itertools
    min_cost = min(sum(scores[p[i]][i] for i in range(3)) for p in itertools.permutations(range(3)))
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
from marimo._utils.cell_matching import _hungarian_algorithm

def test__hungarian_algorithm():
    _hungarian_algorithm([[-1.39067116156734e-309, float('nan')], [-2.7238612773409314, -2.786361544634701, 0.0]])

def test__hungarian_algorithm_2():
    _hungarian_algorithm([])
🔎 Concolic Coverage Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
codeflash_concolic_a_rncq49/tmpfmcrb4_l/test_concolic_coverage.py::test__hungarian_algorithm 9.79μs 10.3μs -5.24%⚠️
codeflash_concolic_a_rncq49/tmpfmcrb4_l/test_concolic_coverage.py::test__hungarian_algorithm_2 2.77μs 2.82μs -1.81%⚠️

To edit these changes git checkout codeflash/optimize-_hungarian_algorithm-mhwqu21y and push.

Codeflash Static Badge

The optimized code achieves a **170% speedup** by eliminating redundant matrix traversals and reducing cache misses through more efficient memory access patterns.

## Key Optimizations:

**1. Precomputed Index Sets**
- The original code repeatedly checks `row_assignment[i] == -1` and `col_assignment[j] == -1` across nested O(n²) loops
- The optimized version precomputes `uncovered_rows` and `uncovered_cols` once per iteration, then operates only on these smaller sets
- This reduces the inner loop complexity from O(n²) to O(uncovered × uncovered), which is typically much smaller

**2. Row Reference Caching**
- Added `row = score_matrix[i]` before inner loops to avoid repeated `score_matrix[i]` lookups
- This eliminates Python's array indexing overhead on every matrix access
- Particularly effective in the tight loops that dominate runtime (Steps 1, 2, and 4)

**3. Batch Matrix Updates**
- Instead of checking cover conditions for every (i,j) pair, the optimization separates covered/uncovered positions
- Matrix updates are now batched by category: subtract from uncovered positions, add to covered positions
- This eliminates redundant conditional checks within the nested loops

## Performance Impact:

The line profiler shows the most dramatic improvements in Step 4's nested loops:
- Original: 14.8% + 14.7% = 29.5% of total time in conditional checks
- Optimized: 29.2% + 40.5% = 69.7% of total time, but with much faster per-iteration execution

The optimization is particularly effective for:
- **Large matrices** (100x100+): 195-200% speedup as shown in test cases
- **Sparse assignment scenarios**: When many rows/columns remain unassigned, the uncovered sets are much smaller than n
- **Cell matching workloads**: Based on the function reference, this is called from `_match_cell_ids_by_similarity` which processes code similarity matrices, making the performance gains directly beneficial to cell matching operations in the Marimo notebook environment

The improvements maintain identical algorithmic behavior while significantly reducing the constant factors that dominate the Hungarian algorithm's runtime.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 13, 2025 01:21
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Nov 13, 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 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant