orchestrata_ultimate.refactor.codemod._csr_index

Reference › orchestrata_ultimate › refactor › codemod › _csr_index

_CSRIndex stores a directed graph as compressed sparse row arrays so that the neighbours of a node can be read quickly. It imports nothing from the project and is used by _cig and _cig_builder_ast. It is built from a node count and a list of (source, target) pairs, with a forward and a reverse array. dependencies and dependents return the successors and predecessors of a node, out_degree and in_degree count them, and transitive_closure returns all nodes reachable forward or backward with an optional depth limit.

At a glance

Source src/orchestrata_ultimate/refactor/codemod/_csr_index.py
Lines of code 94
Classes 1
Functions 0
Imports (internal) none
Imported by orchestrata_ultimate.refactor.codemod._cig, orchestrata_ultimate.refactor.codemod._cig_builder_ast
Tests that import it tests/refactor/codemod/analysis/test_protocol.py, tests/refactor/codemod/test_analysis_passes.py, tests/refactor/codemod/test_csr_index.py, tests/scripts/test_codemod_cli.py

Class diagram

Class diagram orchestrata_ultimate.refactor.codemod._csr_index

Dependency diagram

Dependency diagram orchestrata_ultimate.refactor.codemod._csr_index

API

Classes

_CSRIndex

Line 18

Compressed Sparse Row adjacency index for directed graphs.

Method Signature Summary
__init__ __init__(node_count: int, edges: list[tuple[int, int]]) -> None
_build_csr staticmethod _build_csr(node_count: int, edges: list[tuple[int, int]]) -> tuple[array[int], array[int]] Build CSR arrays from edge list.
node_count property node_count() -> int Number of nodes in the graph.
dependencies dependencies(node_id: int) -> list[int] Forward neighbors (what this node depends on).
dependents dependents(node_id: int) -> list[int] Reverse neighbors (what depends on this node).
out_degree out_degree(node_id: int) -> int Number of outgoing edges for a node.
in_degree in_degree(node_id: int) -> int Number of incoming edges for a node.
transitive_closure transitive_closure(node_id: int, direction: str = 'forward', max_depth: int = -1) -> frozenset[int] BFS transitive closure with optional depth limit.

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/src/orchestrata_ultimate/refactor/codemod/_csr_index.py

"""Compressed Sparse Row (CSR) adjacency arrays for O(1) graph queries.

Stores a directed graph as two CSR matrices (forward and transpose) for
O(1) neighbor lookups in both directions. Memory layout:

    _offsets[i]   : start index in _neighbors for node i
    _offsets[i+1] : end index (exclusive)
    _neighbors[_offsets[i]:_offsets[i+1]] : neighbor node_ids
"""

from __future__ import annotations

from array import array

import codemod_fast


class _CSRIndex:
    """Compressed Sparse Row adjacency index for directed graphs."""

    __slots__ = (
        "_node_count",
        "_offsets",
        "_neighbors",
        "_transpose_offsets",
        "_transpose_neighbors",
    )

    def __init__(self, node_count: int, edges: list[tuple[int, int]]) -> None:
        self._node_count = node_count
        self._offsets, self._neighbors = self._build_csr(node_count, edges)
        self._transpose_offsets, self._transpose_neighbors = self._build_csr(
            node_count, [(to, frm) for frm, to in edges]
        )

    @staticmethod
    def _build_csr(
        node_count: int, edges: list[tuple[int, int]]
    ) -> tuple[array[int], array[int]]:
        """Build CSR arrays from edge list.

        Delegates to codemod_fast (C extension; pure-Python fallback inside
        the package if the native module is unavailable).
        """
        # codemod_fast is an untyped in-repo native extension (no .pyi stub).
        built: tuple[array[int], array[int]] = codemod_fast.csr_build(node_count, edges)
        return built

    @property
    def node_count(self) -> int:
        """Number of nodes in the graph."""
        return self._node_count

    def dependencies(self, node_id: int) -> list[int]:
        """Forward neighbors (what this node depends on)."""
        start = self._offsets[node_id]
        end = self._offsets[node_id + 1]
        return list(self._neighbors[start:end])

    def dependents(self, node_id: int) -> list[int]:
        """Reverse neighbors (what depends on this node)."""
        start = self._transpose_offsets[node_id]
        end = self._transpose_offsets[node_id + 1]
        return list(self._transpose_neighbors[start:end])

    def out_degree(self, node_id: int) -> int:
        """Number of outgoing edges for a node."""
        return self._offsets[node_id + 1] - self._offsets[node_id]

    def in_degree(self, node_id: int) -> int:
        """Number of incoming edges for a node."""
        return self._transpose_offsets[node_id + 1] - self._transpose_offsets[node_id]

    def transitive_closure(
        self, node_id: int, direction: str = "forward", max_depth: int = -1
    ) -> frozenset[int]:
        """BFS transitive closure with optional depth limit.

        Args:
            node_id: Starting node.
            direction: "forward" for dependencies, "reverse" for dependents.
            max_depth: Maximum BFS depth (-1 for unlimited).

        Returns:
            Set of all reachable node IDs (including start node).
        """
        if direction == "forward":
            offsets, neighbors = self._offsets, self._neighbors
        else:
            offsets, neighbors = (
                self._transpose_offsets,
                self._transpose_neighbors,
            )
        return frozenset(codemod_fast.csr_bfs(offsets, neighbors, node_id, max_depth))