Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Dec 22, 2025

📄 24,040% (240.40x) speedup for find_last_node in src/algorithms/graph.py

⏱️ Runtime : 74.0 milliseconds 306 microseconds (best of 250 runs)

📝 Explanation and details

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 36 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
from __future__ import annotations

# imports
import pytest  # used for our unit tests
from src.algorithms.graph import find_last_node

# unit tests

# --- Basic Test Cases ---


def test_single_node_no_edges():
    # One node, no edges. Should return the node itself.
    nodes = [{"id": 1}]
    edges = []
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.25μs -> 959ns (30.3% faster)


def test_two_nodes_one_edge():
    # Two nodes, one edge from node 1 to node 2. Last node is node 2.
    nodes = [{"id": 1}, {"id": 2}]
    edges = [{"source": 1, "target": 2}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.92μs -> 1.17μs (64.3% faster)


def test_three_nodes_linear_chain():
    # 1 -> 2 -> 3, last node is 3
    nodes = [{"id": 1}, {"id": 2}, {"id": 3}]
    edges = [{"source": 1, "target": 2}, {"source": 2, "target": 3}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 2.25μs -> 1.21μs (86.3% faster)


def test_three_nodes_branching():
    # 1 -> 2, 1 -> 3, both 2 and 3 are "last" nodes, returns first found (2)
    nodes = [{"id": 1}, {"id": 2}, {"id": 3}]
    edges = [{"source": 1, "target": 2}, {"source": 1, "target": 3}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.79μs -> 1.17μs (53.6% faster)


def test_no_last_node_all_have_outgoing():
    # 1 -> 2, 2 -> 1 (cycle), no last node
    nodes = [{"id": 1}, {"id": 2}]
    edges = [{"source": 1, "target": 2}, {"source": 2, "target": 1}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.83μs -> 1.21μs (51.8% faster)


# --- Edge Test Cases ---


def test_empty_nodes_and_edges():
    # No nodes, no edges. Should return None.
    nodes = []
    edges = []
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 833ns -> 875ns (4.80% slower)


def test_nodes_with_no_edges():
    # Multiple unconnected nodes, all are last nodes, should return the first one.
    nodes = [{"id": "a"}, {"id": "b"}, {"id": "c"}]
    edges = []
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.29μs -> 1.00μs (29.1% faster)


def test_edges_with_nonexistent_nodes():
    # Edges refer to node ids not in nodes list. Should return first node as last.
    nodes = [{"id": 10}]
    edges = [{"source": 99, "target": 10}, {"source": 100, "target": 10}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.58μs -> 1.12μs (40.7% faster)


def test_duplicate_node_ids():
    # Duplicate node ids, both are last nodes if not source in any edge.
    nodes = [{"id": 1}, {"id": 1}]
    edges = []
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.21μs -> 1.00μs (20.9% faster)


def test_node_with_self_loop():
    # Node with edge to itself, should not be last node.
    nodes = [{"id": 1}]
    edges = [{"source": 1, "target": 1}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.38μs -> 1.17μs (17.9% faster)


def test_multiple_last_nodes():
    # 1 -> 2, 1 -> 3, both 2 and 3 are last nodes, returns first found
    nodes = [{"id": 1}, {"id": 2}, {"id": 3}]
    edges = [{"source": 1, "target": 2}, {"source": 1, "target": 3}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.92μs -> 1.21μs (58.7% faster)


def test_node_with_multiple_incoming_edges():
    # 1 -> 3, 2 -> 3, only 3 is last node
    nodes = [{"id": 1}, {"id": 2}, {"id": 3}]
    edges = [{"source": 1, "target": 3}, {"source": 2, "target": 3}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 2.17μs -> 1.21μs (79.4% faster)


def test_edge_with_extra_keys():
    # Edge has extra keys, function should ignore them
    nodes = [{"id": 1}, {"id": 2}]
    edges = [{"source": 1, "target": 2, "weight": 5}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.92μs -> 1.17μs (64.4% faster)


def test_nodes_with_non_hashable_ids():
    # Node ids are tuples
    nodes = [{"id": (1, 2)}, {"id": (2, 3)}]
    edges = [{"source": (1, 2), "target": (2, 3)}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 2.12μs -> 1.29μs (64.5% faster)


# --- Large Scale Test Cases ---


def test_large_linear_chain():
    # 0 -> 1 -> 2 -> ... -> 999, last node is 999
    n = 1000
    nodes = [{"id": i} for i in range(n)]
    edges = [{"source": i, "target": i + 1} for i in range(n - 1)]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 18.6ms -> 58.2μs (31767% faster)


def test_large_star_graph():
    # 0 -> 1, 0 -> 2, ..., 0 -> 999, all nodes except 0 are last nodes
    n = 1000
    nodes = [{"id": i} for i in range(n)]
    edges = [{"source": 0, "target": i} for i in range(1, n)]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 37.8μs -> 20.4μs (85.1% faster)


def test_large_graph_all_cycles():
    # Every node points to next, last points to first, no last node
    n = 1000
    nodes = [{"id": i} for i in range(n)]
    edges = [{"source": i, "target": (i + 1) % n} for i in range(n)]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 18.4ms -> 57.0μs (32162% faster)


def test_large_graph_multiple_disconnected_components():
    # Two chains: 0->1->2 and 100->101->102, should return 2 (first last node found)
    nodes = [{"id": 0}, {"id": 1}, {"id": 2}, {"id": 100}, {"id": 101}, {"id": 102}]
    edges = [
        {"source": 0, "target": 1},
        {"source": 1, "target": 2},
        {"source": 100, "target": 101},
        {"source": 101, "target": 102},
    ]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 2.38μs -> 1.38μs (72.7% faster)


def test_large_graph_with_isolated_nodes():
    # 0->1->2, 3 is isolated, should return 2 or 3 (first found)
    nodes = [{"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}]
    edges = [{"source": 0, "target": 1}, {"source": 1, "target": 2}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 2.25μs -> 1.21μs (86.3% faster)


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

# imports
import pytest  # used for our unit tests
from src.algorithms.graph import find_last_node

# unit tests

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


def test_single_node_no_edges():
    # One node, no edges; should return the node itself as last node
    nodes = [{"id": 1}]
    edges = []
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.29μs -> 1.00μs (29.2% faster)


def test_two_nodes_one_edge():
    # Two nodes, one edge from 1->2; last node is node 2 (no outgoing edges)
    nodes = [{"id": 1}, {"id": 2}]
    edges = [{"source": 1, "target": 2}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.83μs -> 1.12μs (62.9% faster)


def test_three_nodes_linear_chain():
    # Three nodes, edges 1->2, 2->3; last node is node 3
    nodes = [{"id": 1}, {"id": 2}, {"id": 3}]
    edges = [{"source": 1, "target": 2}, {"source": 2, "target": 3}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 2.17μs -> 1.21μs (79.3% faster)


def test_multiple_last_nodes_returns_first():
    # Two nodes with no outgoing edges (2 and 3); should return the first found (2)
    nodes = [{"id": 1}, {"id": 2}, {"id": 3}]
    edges = [{"source": 1, "target": 2}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.79μs -> 1.08μs (65.5% faster)


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


def test_no_nodes():
    # No nodes at all: should return None
    nodes = []
    edges = []
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 792ns -> 875ns (9.49% slower)


def test_no_edges_multiple_nodes():
    # Multiple nodes, no edges; should return the first node
    nodes = [{"id": 10}, {"id": 20}, {"id": 30}]
    edges = []
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.29μs -> 958ns (34.9% faster)


def test_cycle_graph():
    # Cycle: 1->2, 2->3, 3->1; all nodes have outgoing edges, so should return None
    nodes = [{"id": 1}, {"id": 2}, {"id": 3}]
    edges = [
        {"source": 1, "target": 2},
        {"source": 2, "target": 3},
        {"source": 3, "target": 1},
    ]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 2.25μs -> 1.29μs (74.1% faster)


def test_disconnected_graph():
    # Disconnected nodes: 1->2, 3 (no edges), 4 (no edges)
    nodes = [{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}]
    edges = [{"source": 1, "target": 2}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.79μs -> 1.12μs (59.3% faster)


def test_non_integer_node_ids():
    # Node IDs as strings
    nodes = [{"id": "a"}, {"id": "b"}, {"id": "c"}]
    edges = [{"source": "a", "target": "b"}, {"source": "b", "target": "c"}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 2.50μs -> 1.29μs (93.5% faster)


def test_edge_with_nonexistent_source():
    # Edge with source not in nodes; should still find last node correctly
    nodes = [{"id": 1}, {"id": 2}]
    edges = [{"source": 3, "target": 1}]
    # node 1 has no outgoing edges; node 2 also has no outgoing edges; should return node 1
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.42μs -> 1.12μs (26.0% faster)


def test_edge_with_nonexistent_target():
    # Edge with target not in nodes; should still find last node correctly
    nodes = [{"id": 1}, {"id": 2}]
    edges = [{"source": 1, "target": 3}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.79μs -> 1.12μs (59.3% faster)


def test_duplicate_edges():
    # Multiple edges with same source and target
    nodes = [{"id": 1}, {"id": 2}]
    edges = [{"source": 1, "target": 2}, {"source": 1, "target": 2}]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.88μs -> 1.17μs (60.7% faster)


def test_empty_edges_dicts():
    # Edges with missing keys (should raise KeyError)
    nodes = [{"id": 1}, {"id": 2}]
    edges = [{}]
    with pytest.raises(KeyError):
        find_last_node(nodes, edges)  # 2.04μs -> 792ns (158% faster)


def test_large_linear_chain():
    # 1000 nodes in a chain; last node should be the last in the list
    nodes = [{"id": i} for i in range(1000)]
    edges = [{"source": i, "target": i + 1} for i in range(999)]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 18.5ms -> 58.9μs (31285% faster)


def test_large_star_graph():
    # One central node (0) pointing to 999 leaves; leaves are last nodes (no outgoing edges)
    nodes = [{"id": 0}] + [{"id": i} for i in range(1, 1000)]
    edges = [{"source": 0, "target": i} for i in range(1, 1000)]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 38.2μs -> 20.1μs (90.2% faster)


def test_large_disconnected_nodes():
    # 1000 nodes, no edges; should return the first node
    nodes = [{"id": i} for i in range(1000)]
    edges = []
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 1.42μs -> 1.08μs (30.8% faster)


def test_large_graph_all_have_outgoing():
    # 1000 nodes, each has an outgoing edge to the next (wrap around, i->(i+1)%1000); all have outgoing, so None
    nodes = [{"id": i} for i in range(1000)]
    edges = [{"source": i, "target": (i + 1) % 1000} for i in range(1000)]
    codeflash_output = find_last_node(nodes, edges)
    result = codeflash_output  # 18.4ms -> 58.2μs (31526% faster)


# 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-find_last_node-mjgwzqyt and push.

Codeflash

@codeflash-ai codeflash-ai bot requested a review from KRRT7 December 22, 2025 08:49
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Dec 22, 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