Skip to content

capturegraph.data.containers.utilities.vectorize #

vectorize - Decorator for broadcasting Lists over function parameters.#

A decorator that allows ordinary functions to accept Lists as arguments, automatically broadcasting and reshaping the output.

Example
from capturegraph.data import vectorize, List

@vectorize
def distance(a: float, b: float) -> float:
    return abs(a - b)

# Call with scalars (unchanged behavior)
distance(10, 3)  # → 7

# Call with Lists (broadcasted)
distance(List([1, 2, 3]), List([10, 20, 30]))  # → List([9, 18, 27])

# Mixed scalars and Lists (broadcast scalars)
distance(List([1, 2, 3]), 0)  # → List([1, 2, 3])
Broadcasting Rules
  • Same length: All Lists must have the same length
  • Single-element: Lists with one element are repeated to match
  • Scalars: Non-list values are repeated for every row
Notes
  • Works with both positional and keyword arguments
  • Preserves the original function when all arguments are scalars
  • Returns a List when any argument is a List

vectorize(fn) #

Decorator to broadcast List arguments over a scalar function.

Wraps a function that takes scalar arguments so it can also accept Lists. When called with Lists, the function is applied element-wise and results are collected into a List.

Parameters:

Name Type Description Default
fn Callable

A callable that operates on scalar values.

required

Returns:

Type Description
Callable

A wrapped function that accepts both scalars and Lists.

Example
@vectorize
def add(a, b):
    return a + b

add(1, 2)  # 3
add(List([1, 2, 3]), 10)  # List([11, 12, 13])
add(List([1, 2]), List([10, 20]))  # List([11, 22])
Source code in capturegraph-lib/capturegraph/data/containers/utilities/vectorize.py
def vectorize(fn: Callable) -> Callable:
    """Decorator to broadcast List arguments over a scalar function.

    Wraps a function that takes scalar arguments so it can also accept
    Lists. When called with Lists, the function is applied element-wise
    and results are collected into a List.

    Args:
        fn: A callable that operates on scalar values.

    Returns:
        A wrapped function that accepts both scalars and Lists.

    Example:
        ```python
        @vectorize
        def add(a, b):
            return a + b

        add(1, 2)  # 3
        add(List([1, 2, 3]), 10)  # List([11, 12, 13])
        add(List([1, 2]), List([10, 20]))  # List([11, 22])
        ```
    """

    @functools.wraps(fn)
    def wrapper(*args, **kwargs) -> Any:
        # Collect all arguments for broadcast length calculation
        all_values = list(args) + list(kwargs.values())
        length = _broadcast_length(*all_values)

        # If no Lists, call the function directly (scalar path)
        if length == 0:
            return fn(*args, **kwargs)

        # Broadcast all arguments
        broadcasted_args = [_broadcast_value(arg, length) for arg in args]
        broadcasted_kwargs = {
            key: _broadcast_value(val, length) for key, val in kwargs.items()
        }

        # Call the function for each row
        results = []
        for i in range(length):
            row_args = [broadcasted_args[j][i] for j in range(len(args))]
            row_kwargs = {key: broadcasted_kwargs[key][i] for key in kwargs}
            results.append(fn(*row_args, **row_kwargs))

        return List(results)

    return wrapper