Skip to content

Tensor Access Patterns (taplib)

taplib provides abstractions for describing how data is tiled and streamed between memory and AIE compute tiles. A Tensor Access Pattern (TAP) describes a multi-dimensional iteration over a buffer, generating the DMA descriptor sequences that the NPU hardware executes.

TensorAccessPattern

TensorAccessPattern(
    tensor_dims: Sequence[int],
    offset: int,
    sizes: Sequence[int],
    strides: Sequence[int],
)

A TensorAccessPattern represents a data access pattern applied to a tensor of a specific dimension. This is a base class meant to generically represent such as transformation using sizes, strides, and an offset.

An object representing an access pattern applied to a tensor.

Parameters:

Name Type Description Default
tensor_dims Sequence[int]

Dimensions of the tensor

required
offset int

Offset into the tensor to begin the transformation

required
sizes Sequence[int]

Transformation sizes

required
strides Sequence[int]

Transformation strides

required
Source code in python/helpers/taplib/tap.py
def __init__(
    self,
    tensor_dims: Sequence[int],
    offset: int,
    sizes: Sequence[int],
    strides: Sequence[int],
):
    """
    An object representing an access pattern applied to a tensor.

    Args:
        tensor_dims (Sequence[int]): Dimensions of the tensor
        offset (int): Offset into the tensor to begin the transformation
        sizes (Sequence[int]): Transformation sizes
        strides (Sequence[int]): Transformation strides
    """
    self._tensor_dims = validate_tensor_dims(tensor_dims)
    self._offset = validate_offset(offset, tensor_dims)
    cleaned_sizes, cleaned_strides = validate_and_clean_sizes_strides(
        sizes, strides
    )
    assert cleaned_sizes is not None and cleaned_strides is not None
    self._sizes: Sequence[int] = cleaned_sizes
    self._strides: Sequence[int] = cleaned_strides

tensor_dims property

tensor_dims: Sequence[int]

A copy of the dimensions of the tensor

Returns:

Type Description
Sequence[int]

Sequence[int]: Tensor dimensions

offset property

offset: int

Returns the offset into the tensor

Returns:

Name Type Description
int int

offset

sizes property

sizes: Sequence[int]

A copy of the access pattern sizes

Returns:

Type Description
Sequence[int]

Sequence[int]: Transformation sizes

strides property

strides: Sequence[int]

A copy of the access pattern strides

Returns:

Type Description
Sequence[int]

Sequence[int]: Trsnformation strides

transformation_dims property

transformation_dims: Sequence[tuple[int, int]]

The access pattern represented as a sequence of (size, stride) tuples

Returns:

Type Description
Sequence[tuple[int, int]]

Sequence[tuple[int, int]]: Transformation dimensions

accesses

accesses() -> tuple[ndarray, ndarray]

Returns the access_order and access_count arrays.

The access_order ndarray sequentially counts access to elements in the tensor. If an element is accessed more than once, only the last count is reflected.

The access_count ndarray contains the number of times each element is accessed by the tensor access pattern.

Returns:

Type Description
tuple[ndarray, ndarray]

tuple[np.ndarray, np.ndarray]: access_order, access_count

Source code in python/helpers/taplib/tap.py
def accesses(self) -> tuple[np.ndarray, np.ndarray]:
    """
    Returns the access_order and access_count arrays.

    The access_order ndarray sequentially counts access to elements in the
    tensor. If an element is accessed more than once, only the last count is reflected.

    The access_count ndarray contains the number of times each element is
    accessed by the tensor access pattern.

    Returns:
        tuple[np.ndarray, np.ndarray]: access_order, access_count
    """
    return self._calculate_accesses(calc_order=True, calc_count=True)

access_order

access_order() -> ndarray

The access_order ndarray sequentially counts access to elements in the tensor. If an element is accessed more than once, only the last count is reflected.

Returns:

Type Description
ndarray

np.ndarray: access_order

Source code in python/helpers/taplib/tap.py
def access_order(self) -> np.ndarray:
    """
    The access_order ndarray sequentially counts access to elements in the
    tensor. If an element is accessed more than once, only the last count is reflected.

    Returns:
        np.ndarray: access_order
    """
    access_order_tensor, _ = self._calculate_accesses(
        calc_order=True, calc_count=False
    )
    return access_order_tensor

access_count

access_count() -> ndarray

The access_count ndarray contains the number of times each element is accessed by the tensor access pattern.

Returns:

Type Description
ndarray

np.ndarray: access_count

Source code in python/helpers/taplib/tap.py
def access_count(self) -> np.ndarray:
    """
    The access_count ndarray contains the number of times each element is
    accessed by the tensor access pattern.

    Returns:
        np.ndarray: access_count
    """
    _, access_count_tensor = self._calculate_accesses(
        calc_order=False, calc_count=True
    )
    return access_count_tensor

access_generator

access_generator() -> Generator[int, None, None]

This function returns an iterator that returns the access index into the flattened tensor that this access pattern represents. This can be used to calculate the access count or to enumerate accesses.

Yields:

Name Type Description
int int

The next access index

Source code in python/helpers/taplib/tap.py
def access_generator(self) -> Generator[int, None, None]:
    """This function returns an iterator that returns the access index
    into the flattened tensor that this access pattern represents. This can
    be used to calculate the access count or to enumerate accesses.

    Yields:
        int: The next access index
    """
    total_elems = np.prod(self._tensor_dims)

    # Use itertools.product to collapse len(sizes) nested forloop into one forloop
    for dims in itertools.product(*[range(0, n) for n in self._sizes]):
        yield (
            self._offset + np.sum(np.multiply(dims, self._strides))
        ) % total_elems

compare_access_orders

compare_access_orders(other: TensorAccessPattern) -> bool

This function creates an alternative way to compare access patterns. Sometimes access patterns with different sizes/strides are functionally equivalent; to detect functional equivalency, this function uses iterators produced by access_generator() to compare the access patterns. This is more performant than comparing the numpy array access_order or access_count tensors.

Parameters:

Name Type Description Default
other TensorAccessPattern

The TensorAccessPattern to compare to

required

Raises:

Type Description
ValueError

other must be of type TensorAccessPattern

Returns:

Name Type Description
bool bool

True if the TensorAccessPatterns are functionally equivalent; false otherwise.

Source code in python/helpers/taplib/tap.py
def compare_access_orders(self, other: TensorAccessPattern) -> bool:
    """
    This function creates an alternative way to compare access patterns.
    Sometimes access patterns with different sizes/strides are functionally equivalent;
    to detect functional equivalency, this function uses iterators produced by
    access_generator() to compare the access patterns. This is more performant than
    comparing the numpy array access_order or access_count tensors.

    Args:
        other (TensorAccessPattern): The TensorAccessPattern to compare to

    Raises:
        ValueError: other must be of type TensorAccessPattern

    Returns:
        bool: True if the TensorAccessPatterns are functionally equivalent; false otherwise.
    """
    # This function compares using access generators, which is more performant
    # than actually generating the access order or access count tensors.
    if not isinstance(other, TensorAccessPattern):
        raise ValueError(
            "Can only compare access order against another TensorAccessPattern"
        )
    my_generator = self.access_generator()
    other_generator = other.access_generator()
    return all(
        my_idx == other_idx
        for my_idx, other_idx in itertools.zip_longest(
            my_generator, other_generator
        )
    )

visualize

visualize(
    show_arrows: bool | None = None,
    title: str | None = None,
    file_path: str | None = None,
    show_plot: bool = True,
    plot_access_count: bool = False,
) -> None

Visualize the TensorAccessPattern using a graph.

Parameters:

Name Type Description Default
show_arrows bool | None

Display arrows between sequentially accessed elements. Defaults to None.

None
title str | None

Title of the produced graph. Defaults to None.

None
file_path str | None

Path to save the graph at; if none, it is not saved. Defaults to None.

None
show_plot bool

Show the plot (this is useful for Jupyter notebooks). Defaults to True.

True
plot_access_count bool

Plot the access count in addition to the access order. Defaults to False.

False

Raises:

Type Description
NotImplementedError

This function is not implemented for all dimensions.

Source code in python/helpers/taplib/tap.py
def visualize(
    self,
    show_arrows: bool | None = None,
    title: str | None = None,
    file_path: str | None = None,
    show_plot: bool = True,
    plot_access_count: bool = False,
) -> None:
    """Visualize the TensorAccessPattern using a graph.

    Args:
        show_arrows (bool | None, optional): Display arrows between sequentially accessed elements. Defaults to None.
        title (str | None, optional): Title of the produced graph. Defaults to None.
        file_path (str | None, optional): Path to save the graph at; if none, it is not saved. Defaults to None.
        show_plot (bool, optional): Show the plot (this is useful for Jupyter notebooks). Defaults to True.
        plot_access_count (bool, optional): Plot the access count in addition to the access order. Defaults to False.

    Raises:
        NotImplementedError: This function is not implemented for all dimensions.
    """
    from .visualization2d import visualize_from_accesses

    if len(self._tensor_dims) != 2:
        raise NotImplementedError(
            "Visualization is only currently supported for 1- or 2-dimensional tensors"
        )
    if plot_access_count:
        access_order, access_count = self.accesses()
    else:
        access_count = None
        access_order = self.access_order()
    if title is None:
        title = str(self)
    visualize_from_accesses(
        access_order,
        access_count,
        title=title,
        show_arrows=show_arrows,
        file_path=file_path,
        show_plot=show_plot,
    )

TensorAccessSequence

TensorAccessSequence(
    tensor_dims: Sequence[int],
    num_steps: int,
    offset: int | None = None,
    sizes: Sequence[int] | None = None,
    strides: Sequence[int] | None = None,
    offset_fn: Callable[[int, int], int] | None = None,
    sizes_fn: (
        Callable[[int, Sequence[int]], Sequence[int]] | None
    ) = None,
    strides_fn: (
        Callable[[int, Sequence[int]], Sequence[int]] | None
    ) = None,
)

Bases: MutableSequence, Iterable

TensorAccessSequence is a MutableSequence and an Iterable. Generally, it is a thin wrapper around a list[TensorAccessPattern].

The TensorAccessSequence is useful as a container of TensorAccessPatterns so that a grouping of patterns may be accessed in a particular order, or visualized or animated in sequence.

A TensorAccessSequence is a sequence of TensorAccessPatterns modelled after a list.

The constructor can used given functions to generate a sequence of n steps. Allowed functions are the: * offset_fn(step: int, current_offset: int) -> new_offset: int * sizes_fn(step: int, current_sizes: Sequence[int]) -> new_sizes: Sequence[int] * strides_fn(step: int, currrent_strides: Sequence[int]) -> new_strides: Sequence[int]

In lieu or in addition to a function, a default value for sizes/strides/offsets may also be set.

Parameters:

Name Type Description Default
tensor_dims Sequence[int]

Dimensions of the tensor. All TensorAccessPatterns in the sequence must share the tensor dimension.

required
num_steps int

Number of steps (elements) in the sequence.

required
offset int | None

Offset into the sequence. Defaults to None.

None
sizes Sequence[int] | None

Sizes for the TensorAccessPatterns Defaults to None.

None
strides Sequence[int] | None

Strides for the TensorAccessPatterns in the sequence. Defaults to None.

None
offset_fn Callable[[int, int], int] | None

A function to calculate the offset at each step. Defaults to None.

None
sizes_fn Callable[[int, Sequence[int]], Sequence[int]] | None

A function to calculate the sizes at each step. Defaults to None.

None
strides_fn Callable[[int, Sequence[int]], Sequence[int]] | None

A function to calculate the strides at teach step. Defaults to None.

None

Raises:

Type Description
ValueError

Parameters are validated

Source code in python/helpers/taplib/tas.py
def __init__(
    self,
    tensor_dims: Sequence[int],
    num_steps: int,
    offset: int | None = None,
    sizes: Sequence[int] | None = None,
    strides: Sequence[int] | None = None,
    offset_fn: Callable[[int, int], int] | None = None,
    sizes_fn: Callable[[int, Sequence[int]], Sequence[int]] | None = None,
    strides_fn: Callable[[int, Sequence[int]], Sequence[int]] | None = None,
):
    """A TensorAccessSequence is a sequence of TensorAccessPatterns modelled after a list.

    The constructor can used given functions to generate a sequence of n steps. Allowed functions are the:
    * offset_fn(step: int, current_offset: int) -> new_offset: int
    * sizes_fn(step: int, current_sizes: Sequence[int]) -> new_sizes: Sequence[int]
    * strides_fn(step: int, currrent_strides: Sequence[int]) -> new_strides: Sequence[int]

    In lieu or in addition to a function, a default value for sizes/strides/offsets may also be set.

    Args:
        tensor_dims (Sequence[int]): Dimensions of the tensor. All TensorAccessPatterns in the sequence must share the tensor dimension.
        num_steps (int): Number of steps (elements) in the sequence.
        offset (int | None, optional): Offset into the sequence. Defaults to None.
        sizes (Sequence[int] | None, optional): Sizes for the TensorAccessPatterns Defaults to None.
        strides (Sequence[int] | None, optional): Strides for the TensorAccessPatterns in the sequence. Defaults to None.
        offset_fn (Callable[[int, int], int] | None, optional): A function to calculate the offset at each step. Defaults to None.
        sizes_fn (Callable[[int, Sequence[int]], Sequence[int]] | None, optional): A function to calculate the sizes at each step. Defaults to None.
        strides_fn (Callable[[int, Sequence[int]], Sequence[int]] | None, optional): A function to calculate the strides at teach step. Defaults to None.

    Raises:
        ValueError: Parameters are validated
    """
    self._current_step = 0

    # Check tensor dims, offset, sizes, strides
    self._tensor_dims = validate_tensor_dims(tensor_dims)
    if not (offset is None):
        offset = validate_offset(offset, self._tensor_dims)
    sizes, strides = validate_and_clean_sizes_strides(
        sizes, strides, allow_none=True
    )

    # Validate and set num steps
    if num_steps < 0:
        raise ValueError(f"Number of steps must be positive (but is {num_steps})")

    if num_steps == 0:
        if (
            offset != None
            or sizes != None
            or strides != None
            or offset_fn != None
            or sizes_fn != None
            or strides_fn != None
        ):
            raise ValueError(
                f"If num_steps=0, no sizes/strides/offset information may be specified"
            )
        self._taps = []
    else:
        # Make sure values or not None if iteration functions are None; also set default iter fn
        if offset_fn is None:
            if offset is None:
                raise ValueError("Offset must be provided if offset_fn is None")
            const_offset = offset
            offset_fn = lambda _step, _prev_offset: const_offset
        if sizes_fn is None:
            if sizes is None:
                raise ValueError("Sizes must be provided if size_fn is None")
            const_sizes = sizes
            sizes_fn = lambda _step, _prev_sizes: const_sizes
        if strides_fn is None:
            if strides is None:
                raise ValueError("Strides must be provided if stride_fn is None")
            const_strides = strides
            strides_fn = lambda _step, _prev_strides: const_strides

        # Pre-calculate taps, because better for error handling up-front (and for visualizing full iter)
        # This is somewhat against the mentality behind iterations, but should be okay at the scale this
        # class will be used for (e.g., no scalability concerns with keeping all taps in mem)
        self._taps = []
        cur_offset: Any = offset
        cur_sizes: Any = sizes
        cur_strides: Any = strides
        for step in range(num_steps):
            cur_offset = offset_fn(step, cur_offset)
            cur_sizes = sizes_fn(step, cur_sizes)
            cur_strides = strides_fn(step, cur_strides)

            self._taps.append(
                TensorAccessPattern(
                    self._tensor_dims,
                    cur_offset,
                    cur_sizes,
                    cur_strides,
                )
            )

from_taps classmethod

from_taps(
    taps: Sequence[TensorAccessPattern],
) -> TensorAccessSequence

This alternative constructor creates a TensorAccessSequence from a sequence of TensorAccessPatterns. This is an alternative to the traditional constructor, and is useful for patterns that are difficult to express using the sizes/strides/offset functions.

Parameters:

Name Type Description Default
taps Sequence[TensorAccessPattern]

The sequence of tensor access patterns

required

Raises:

Type Description
ValueError

At least one TensorAccessPattern must be specified

ValueError

All TensorAccessPatterns in a sequence must share tensor dimensions

Returns:

Name Type Description
TensorAccessSequence TensorAccessSequence

A newly constructor TensorAccessSequence object

Source code in python/helpers/taplib/tas.py
@classmethod
def from_taps(cls, taps: Sequence[TensorAccessPattern]) -> TensorAccessSequence:
    """
    This alternative constructor creates a TensorAccessSequence from a sequence of TensorAccessPatterns.
    This is an alternative to the traditional constructor, and is useful for patterns that are difficult
    to express using the sizes/strides/offset functions.

    Args:
        taps (Sequence[TensorAccessPattern]): The sequence of tensor access patterns

    Raises:
        ValueError: At least one TensorAccessPattern must be specified
        ValueError: All TensorAccessPatterns in a sequence must share tensor dimensions

    Returns:
        TensorAccessSequence: A newly constructor TensorAccessSequence object
    """
    if len(taps) < 1:
        raise ValueError(
            "Received no TensorAccessPatterns; must have at least one TensorAccessPatterns to create a TensorAccessSequence."
        )
    tensor_dims = taps[0].tensor_dims
    for t in taps:
        if t.tensor_dims != tensor_dims:
            raise ValueError(
                f"TensorAccessPatterns have multiple tensor dimensions (found {tensor_dims} and {t.tensor_dims})"
            )
    tas = cls(
        tensor_dims,
        num_steps=1,
        offset=taps[0].offset,
        sizes=taps[0].sizes,
        strides=taps[0].strides,
    )
    for t in taps[1:]:
        tas.append(t)
    return tas

accesses

accesses() -> tuple[ndarray, ndarray]

Returns the access_order and access_count arrays of the TensorAccessPatterns in the sequence applied sequentially to the tensor.

The access_order ndarray sequentially counts access to elements in the tensor. If an element is accessed more than once, only the last count is reflected.

The access_count ndarray contains the number of times each element is accessed by the tensor access pattern.

Returns:

Type Description
tuple[ndarray, ndarray]

tuple[np.ndarray, np.ndarray]: access_order, access_count

Source code in python/helpers/taplib/tas.py
def accesses(self) -> tuple[np.ndarray, np.ndarray]:
    """
    Returns the access_order and access_count arrays of the TensorAccessPatterns in
    the sequence applied sequentially to the tensor.

    The access_order ndarray sequentially counts access to elements in the
    tensor. If an element is accessed more than once, only the last count is reflected.

    The access_count ndarray contains the number of times each element is
    accessed by the tensor access pattern.

    Returns:
        tuple[np.ndarray, np.ndarray]: access_order, access_count
    """
    return self._calc_accesses(True, True)

access_order

access_order() -> ndarray

The access_order ndarray sequentially counts access to elements in the tensor. If an element is accessed more than once, only the last count is reflected.

The TensorAccessPatterns in the sequence are applied sequentially.

Returns:

Type Description
ndarray

np.ndarray: access_order

Source code in python/helpers/taplib/tas.py
def access_order(self) -> np.ndarray:
    """
    The access_order ndarray sequentially counts access to elements in the
    tensor. If an element is accessed more than once, only the last count is reflected.

    The TensorAccessPatterns in the sequence are applied sequentially.

    Returns:
        np.ndarray: access_order
    """
    access_order, _ = self._calc_accesses(calc_order=True, calc_count=False)
    return access_order

access_count

access_count() -> ndarray

The access_count ndarray contains the number of times each element is accessed by the tensor access pattern.

The TensorAccessPatterns in the sequence are applied sequentially.

Returns:

Type Description
ndarray

np.ndarray: access_count

Source code in python/helpers/taplib/tas.py
def access_count(self) -> np.ndarray:
    """
    The access_count ndarray contains the number of times each element is
    accessed by the tensor access pattern.

    The TensorAccessPatterns in the sequence are applied sequentially.

    Returns:
        np.ndarray: access_count
    """
    _, access_count = self._calc_accesses(calc_order=False, calc_count=True)
    return access_count

animate

animate(
    title: str | None = None,
    animate_access_count: bool = False,
) -> "FuncAnimation"

Creates and returns a handle to a TensorAccessSequence animation. Each frame in the animation represents one TensorAccessPattern in the sequence.

Parameters:

Name Type Description Default
title str | None

The title of the animation. Defaults to None.

None
animate_access_count bool

Create an animation for the tensor access count, in addition to the tensor access order. Defaults to False.

False

Raises:

Type Description
NotImplementedError

Not all dimensions of tensor may be visualized by animation at this time.

Returns:

Type Description
'FuncAnimation'

animation.FuncAnimation: A handle to the animation, produced by the matplotlib.animation module.

Source code in python/helpers/taplib/tas.py
def animate(
    self, title: str | None = None, animate_access_count: bool = False
) -> "FuncAnimation":
    """
    Creates and returns a handle to a TensorAccessSequence animation. Each frame
    in the animation represents one TensorAccessPattern in the sequence.

    Args:
        title (str | None, optional): The title of the animation. Defaults to None.
        animate_access_count (bool, optional): Create an animation for the tensor access count, in addition to the tensor access order. Defaults to False.

    Raises:
        NotImplementedError: Not all dimensions of tensor may be visualized by animation at this time.

    Returns:
        animation.FuncAnimation: A handle to the animation, produced by the matplotlib.animation module.
    """
    from .visualization2d import animate_from_accesses

    if len(self._tensor_dims) != 2:
        raise NotImplementedError(
            "Visualization is only currently supported for 1- or 2-dimensional tensors"
        )

    if title is None:
        title = "TensorAccessSequence Animation"
    total_elems = np.prod(self._tensor_dims)

    animate_order_frames = [
        np.full(total_elems, -1, TensorAccessPattern._DTYPE).reshape(
            self._tensor_dims
        )
    ]

    animate_count_frames: list[np.ndarray] | None = None
    if animate_access_count:
        animate_count_frames = [
            np.full(total_elems, 0, TensorAccessPattern._DTYPE).reshape(
                self._tensor_dims
            )
        ]

    for t in self._taps:
        if animate_count_frames is not None:
            t_access_order, t_access_count = t.accesses()
            animate_count_frames.append(t_access_count)
        else:
            t_access_order = t.access_order()
        animate_order_frames.append(t_access_order)

    return animate_from_accesses(
        animate_order_frames,
        animate_count_frames,
        title=title,
    )

visualize

visualize(
    title: str | None = None,
    file_path: str | None = None,
    show_plot: bool = True,
    plot_access_count: bool = False,
) -> None

Provides a visual of the TensorAccessSequence using a graph.

Parameters:

Name Type Description Default
title str | None

The title of the graph. Defaults to None.

None
file_path str | None

The path to save the graph at. If None, it is not saved. Defaults to None.

None
show_plot bool

Show the plot; this is useful when running in a Jupyter notebook. Defaults to True.

True
plot_access_count bool

Plot the access count in addition to the access order. Defaults to False.

False

Raises:

Type Description
NotImplementedError

Not all dimensions of tensor may be visualized.

Source code in python/helpers/taplib/tas.py
def visualize(
    self,
    title: str | None = None,
    file_path: str | None = None,
    show_plot: bool = True,
    plot_access_count: bool = False,
) -> None:
    """Provides a visual of the TensorAccessSequence using a graph.

    Args:
        title (str | None, optional): The title of the graph. Defaults to None.
        file_path (str | None, optional): The path to save the graph at. If None, it is not saved. Defaults to None.
        show_plot (bool, optional): Show the plot; this is useful when running in a Jupyter notebook. Defaults to True.
        plot_access_count (bool, optional): Plot the access count in addition to the access order. Defaults to False.

    Raises:
        NotImplementedError: Not all dimensions of tensor may be visualized.
    """
    from .visualization2d import visualize_from_accesses

    if len(self._tensor_dims) != 2:
        raise NotImplementedError(
            "Visualization is only currently supported for 1- or 2-dimensional tensors"
        )

    if title is None:
        title = "TensorAccessSequence"
    if plot_access_count:
        access_order_tensor, access_count_tensor = self.accesses()
    else:
        access_order_tensor = self.access_order()
        access_count_tensor = None

    visualize_from_accesses(
        access_order_tensor,
        access_count_tensor,
        title=title,
        show_arrows=False,
        file_path=file_path,
        show_plot=show_plot,
    )

compare_access_orders

compare_access_orders(other: TensorAccessSequence) -> bool

This function creates an alternative way to compare access pattern sequences. Sometimes access patterns with different sizes/strides are functionally equivalent; to detect functional equivalency, this function uses iterators produced by access_generator() to compare the access patterns. This is more performant than comparing the numpy array access_order or access_count tensors, particularly when comparing sequences containing multiple tensor access patterns.

Parameters:

Name Type Description Default
other TensorAccessSequence

The TensorAccessSequence to compare to

required

Returns:

Name Type Description
bool bool

True is functionally equivalent; False otherwise.

Source code in python/helpers/taplib/tas.py
def compare_access_orders(self, other: TensorAccessSequence) -> bool:
    """
    This function creates an alternative way to compare access pattern sequences.
    Sometimes access patterns with different sizes/strides are functionally equivalent;
    to detect functional equivalency, this function uses iterators produced by
    access_generator() to compare the access patterns. This is more performant than
    comparing the numpy array access_order or access_count tensors, particularly
    when comparing sequences containing multiple tensor access patterns.

    Args:
        other (TensorAccessSequence): The TensorAccessSequence to compare to

    Returns:
        bool: True is functionally equivalent; False otherwise.
    """
    if len(self._taps) != len(other._taps):
        return False
    for my_tap, other_tap in zip(self._taps, other._taps):
        if not my_tap.compare_access_orders(other_tap):
            return False
    return True

TensorTiler2D

TensorTiler2D()

This is a generator (similar to factory pattern) class which produces TensorAccessSequences for common 2-dimensional tiling patterns.

Source code in python/helpers/taplib/tensortiler2d.py
def __init__(self):
    raise Exception(
        f"{self.__class__} cannot be instantiated. Use it as a factory/generator of TensorAccessSequences."
    )

simple_tiler classmethod

simple_tiler(
    tensor_dims: Sequence[int],
    tile_dims: Sequence[int] | None = None,
    tile_col_major: bool = False,
    iter_col_major: bool = False,
    pattern_repeat: int = 1,
    prune_step: bool = True,
) -> TensorAccessSequence

The simple_tiler is a special case of the group_tiler. The simple_tiler produces a TensorAccessSequence with one TensorAccessPattern per tile.

Parameters:

Name Type Description Default
tensor_dims Sequence[int]

The dimensions of the tensor to tile.

required
tile_dims Sequence[int] | None

The dimension of the tile. If None, the tile_dims is set equal to the tensor_dims. Defaults to None.

None
tile_col_major bool

Iterate column major within each tile. Defaults to False.

False
iter_col_major bool

Iterate column major over tiles within the TensorAccessSequence. Defaults to False.

False
pattern_repeat int

Access a tile n times per TensorAccessPattern. Defaults to 1.

1
prune_step bool

Prune the iteration steps in the tiling process. Defaults to True.

True

Returns:

Name Type Description
TensorAccessSequence TensorAccessSequence

A TensorAccessSequence with one TensorAccessPattern per tile

Source code in python/helpers/taplib/tensortiler2d.py
@classmethod
def simple_tiler(
    cls,
    tensor_dims: Sequence[int],
    tile_dims: Sequence[int] | None = None,
    tile_col_major: bool = False,
    iter_col_major: bool = False,
    pattern_repeat: int = 1,
    prune_step: bool = True,
) -> TensorAccessSequence:
    """The simple_tiler is a special case of the group_tiler. The simple_tiler produces a TensorAccessSequence
    with one TensorAccessPattern per tile.

    Args:
        tensor_dims (Sequence[int]): The dimensions of the tensor to tile.
        tile_dims (Sequence[int] | None, optional): The dimension of the tile. If None, the tile_dims is set equal to the tensor_dims. Defaults to None.
        tile_col_major (bool, optional): Iterate column major within each tile. Defaults to False.
        iter_col_major (bool, optional): Iterate column major over tiles within the TensorAccessSequence. Defaults to False.
        pattern_repeat (int, optional): Access a tile n times per TensorAccessPattern. Defaults to 1.
        prune_step (bool, optional): Prune the iteration steps in the tiling process. Defaults to True.

    Returns:
        TensorAccessSequence: A TensorAccessSequence with one TensorAccessPattern per tile
    """
    if tile_dims is None:
        tile_dims = deepcopy(tensor_dims)
    # Special case of group_tiler
    return cls.group_tiler(
        tensor_dims=tensor_dims,
        tile_dims=tile_dims,
        tile_col_major=tile_col_major,
        iter_col_major=iter_col_major,
        pattern_repeat=pattern_repeat,
        prune_step=prune_step,
    )

group_tiler classmethod

group_tiler(
    tensor_dims: Sequence[int],
    tile_dims: Sequence[int],
    tile_group_dims: Sequence[int] | None = None,
    tile_col_major: bool = False,
    tile_group_col_major: bool = False,
    iter_col_major: bool = False,
    pattern_repeat: int = 1,
    allow_partial: bool = False,
    prune_step: bool = True,
) -> TensorAccessSequence

The group_tiler is a special case of the step_tiler. The group_tiler produces a TensorAccessSequence with a group of tiles per TensorAccesspattern in the sequence.

Parameters:

Name Type Description Default
tensor_dims Sequence[int]

The dimensions of the tensor to tile.

required
tile_dims Sequence[int]

The dimension of the tile (a contiguous group of elements)

required
tile_group_dims Sequence[int] | None

Dimensions of the grouping of tiles, specified by number of tiles (not elements). If None, assumed to be (1, 1). Defaults to None.

None
tile_col_major bool

Iterate column major within each tile. Defaults to False.

False
tile_group_col_major bool

Iterate column major between tiles in a group within a TensorAccessSequence. Defaults to False.

False
iter_col_major bool

Iterate column major over tiles within the TensorAccessSequence. Defaults to False.

False
pattern_repeat int

Apply a pattern n times within a single TensorAccessPattern. Defaults to 1.

1
allow_partial bool

While a tensor must decompose into tiles easily, a tensor may not decompose into tile groups evenly. If True, uneven groups are allowed. If false, an exception will be thrown. Defaults to False.

False
prune_step bool

Prune the iteration steps in the tiling process. Defaults to True.

True

Returns:

Name Type Description
TensorAccessSequence TensorAccessSequence

A TensorAccessSequence with one tile grouping per TensorAccessPattern

Source code in python/helpers/taplib/tensortiler2d.py
@classmethod
def group_tiler(
    cls,
    tensor_dims: Sequence[int],
    tile_dims: Sequence[int],
    tile_group_dims: Sequence[int] | None = None,
    tile_col_major: bool = False,
    tile_group_col_major: bool = False,
    iter_col_major: bool = False,
    pattern_repeat: int = 1,
    allow_partial: bool = False,
    prune_step: bool = True,
) -> TensorAccessSequence:
    """The group_tiler is a special case of the step_tiler. The group_tiler produces a TensorAccessSequence
    with a group of tiles per TensorAccesspattern in the sequence.

    Args:
        tensor_dims (Sequence[int]): The dimensions of the tensor to tile.
        tile_dims (Sequence[int]): The dimension of the tile (a contiguous group of elements)
        tile_group_dims (Sequence[int] | None, optional): Dimensions of the grouping of tiles, specified by number of tiles (not elements).
            If None, assumed to be (1, 1). Defaults to None.
        tile_col_major (bool, optional): Iterate column major within each tile. Defaults to False.
        tile_group_col_major (bool, optional): Iterate column major between tiles in a group within a TensorAccessSequence. Defaults to False.
        iter_col_major (bool, optional): Iterate column major over tiles within the TensorAccessSequence. Defaults to False.
        pattern_repeat (int, optional): Apply a pattern n times within a single TensorAccessPattern. Defaults to 1.
        allow_partial (bool, optional): While a tensor must decompose into tiles easily, a tensor may not decompose into tile groups evenly.
            If True, uneven groups are allowed. If false, an exception will be thrown. Defaults to False.
        prune_step (bool, optional): Prune the iteration steps in the tiling process. Defaults to True.

    Returns:
        TensorAccessSequence: A TensorAccessSequence with one tile grouping per TensorAccessPattern
    """
    if tile_group_dims is None:
        tile_group_dims = (1,) * cls._NUM_DIMS
    # Special case of step_tiler
    return cls.step_tiler(
        tensor_dims=tensor_dims,
        tile_dims=tile_dims,
        tile_group_repeats=tile_group_dims,
        tile_col_major=tile_col_major,
        tile_group_col_major=tile_group_col_major,
        iter_col_major=iter_col_major,
        pattern_repeat=pattern_repeat,
        allow_partial=allow_partial,
        prune_step=prune_step,
    )

step_tiler classmethod

step_tiler(
    tensor_dims: Sequence[int],
    tile_dims: Sequence[int],
    tile_group_repeats: Sequence[int],
    tile_group_steps: Sequence[int] | None = None,
    tile_col_major: bool = False,
    tile_group_col_major: bool = False,
    iter_col_major: bool = False,
    allow_partial: bool = False,
    pattern_repeat: int = 1,
    prune_step: bool = True,
) -> TensorAccessSequence

Build a TensorAccessSequence by tiling a tensor with explicit per-dimension steps.

This is the general-purpose tiler that the simpler simple_tiler / group_tiler factories delegate to. It produces one TensorAccessPattern per tile group, giving full control over tile size, group repeats, per-dimension step, and iteration order.

Parameters:

Name Type Description Default
tensor_dims Sequence[int]

The dimensions of the tensor to tile.

required
tile_dims Sequence[int]

The dimension of the tile (a contiguous group of elements)

required
tile_group_repeats Sequence[int]

Number of times a tile appears in each dimension in each TensorAccessPattern.

required
tile_group_steps Sequence[int] | None

Space between each tile repeat in each dimension, given in units of tile size. Defaults to None.

None
tile_col_major bool

Iterate column major within each tile. Defaults to False.

False
tile_group_col_major bool

Iterate column major between tiles in a group within a TensorAccessSequence. Defaults to False.

False
iter_col_major bool

Iterate column major over tiles within the TensorAccessSequence. Defaults to False.

False
allow_partial bool

Whether to allow partial tile groups. A tensor always decomposes into tiles evenly, but it may not decompose into tile groups evenly. When False, a partial tile grouping raises a ValueError; when True, partial groupings at the tensor edges are permitted. Defaults to False.

False
pattern_repeat int

Apply the access pattern n times within a single TensorAccessPattern. Defaults to 1.

1
prune_step bool

Prune the iteration steps in the tiling process. Defaults to True.

True

Raises:

Type Description
ValueError

The parameters are validated

ValueError

Some transformations are not expressible in only 4 dimensions of sizes/strides

ValueError

If allow_partial is False, an error will be thrown if partial TensorAccessPatterns are needed to fully tile the tensor.

Returns:

Name Type Description
TensorAccessSequence TensorAccessSequence

A TensorAccessSequence with one tile grouping per TensorAccessPattern, where the tile grouping may or may not be contiguous.

Source code in python/helpers/taplib/tensortiler2d.py
@classmethod
def step_tiler(
    cls,
    tensor_dims: Sequence[int],
    tile_dims: Sequence[int],
    tile_group_repeats: Sequence[int],
    tile_group_steps: Sequence[int] | None = None,
    tile_col_major: bool = False,
    tile_group_col_major: bool = False,
    iter_col_major: bool = False,
    allow_partial: bool = False,
    pattern_repeat: int = 1,
    prune_step: bool = True,
) -> TensorAccessSequence:
    """Build a TensorAccessSequence by tiling a tensor with explicit per-dimension steps.

    This is the general-purpose tiler that the simpler ``simple_tiler`` /
    ``group_tiler`` factories delegate to. It produces one
    TensorAccessPattern per tile group, giving full control over tile
    size, group repeats, per-dimension step, and iteration order.

    Args:
        tensor_dims (Sequence[int]): The dimensions of the tensor to tile.
        tile_dims (Sequence[int]): The dimension of the tile (a contiguous group of elements)
        tile_group_repeats (Sequence[int]): Number of times a tile appears in each dimension in each TensorAccessPattern.
        tile_group_steps (Sequence[int] | None, optional): Space between each tile repeat in each dimension, given in units of tile size. Defaults to None.
        tile_col_major (bool, optional): Iterate column major within each tile. Defaults to False.
        tile_group_col_major (bool, optional): Iterate column major between tiles in a group within a TensorAccessSequence. Defaults to False.
        iter_col_major (bool, optional): Iterate column major over tiles within the TensorAccessSequence. Defaults to False.
        allow_partial (bool, optional): Whether to allow partial tile groups. A tensor always decomposes into tiles evenly, but it may not decompose into tile *groups* evenly. When False, a partial tile grouping raises a ValueError; when True, partial groupings at the tensor edges are permitted. Defaults to False.
        pattern_repeat (int, optional): Apply the access pattern n times within a single TensorAccessPattern. Defaults to 1.
        prune_step (bool, optional): Prune the iteration steps in the tiling process. Defaults to True.

    Raises:
        ValueError: The parameters are validated
        ValueError: Some transformations are not expressible in only 4 dimensions of sizes/strides
        ValueError: If allow_partial is False, an error will be thrown if partial TensorAccessPatterns are needed to fully tile the tensor.

    Returns:
        TensorAccessSequence: A TensorAccessSequence with one tile grouping per TensorAccessPattern,
            where the tile grouping may or may not be contiguous.
    """

    # Validate dimensions
    if tile_group_steps is None:
        tile_group_steps = (1,) * cls._NUM_DIMS
    tensor_dims = validate_tensor_dims(tensor_dims, expected_dims=cls._NUM_DIMS)
    tile_dims = validate_tensor_dims(tile_dims, expected_dims=cls._NUM_DIMS)
    tile_group_repeats = validate_tensor_dims(
        tile_group_repeats, expected_dims=cls._NUM_DIMS
    )
    tile_group_steps = validate_tensor_dims(
        tile_group_steps, expected_dims=cls._NUM_DIMS
    )

    # Check tensor is tileable by tile size
    for i, (tensor_dim, tile_dim) in enumerate(zip(tensor_dims, tile_dims)):
        if tensor_dim % tile_dim != 0:
            raise ValueError(
                f"Tensor dimension {i} ({tensor_dim}) is not divisible by tile dim ({tile_dim})"
            )

    # Validate pattern repeat and check for partial patterns
    if not isinstance(pattern_repeat, int) or pattern_repeat < 1:
        raise ValueError(f"Pattern repeat must be >= 1 but is {pattern_repeat}")
    if not allow_partial:
        for i, (tensor_dim, tile_dim, repeat_dim, step_dim) in enumerate(
            zip(tensor_dims, tile_dims, tile_group_repeats, tile_group_steps)
        ):
            if tensor_dim % (tile_dim * repeat_dim * step_dim) != 0:
                raise ValueError(
                    f"allow_partial={allow_partial} but tensor does not divide evenly into tile groups in dimension {i}"
                )
            if tile_dim * repeat_dim * step_dim > tensor_dim:
                raise ValueError(
                    f"Tile pattern exceeds tensor size in dimension {i} ({tile_dim}x{repeat_dim}x{step_dim} > {tensor_dim})"
                )

    # Prune steps/repeat if steps/repeat larger than initial tensor
    tile_group_steps = list(tile_group_steps)
    tile_group_repeats = list(tile_group_repeats)
    for i, (tensor_dim, tile_dim, tile_step) in enumerate(
        zip(tensor_dims, tile_dims, tile_group_steps)
    ):
        tile_group_repeats[i] = min(
            tile_group_repeats[i], ceildiv(tensor_dim // tile_dim, tile_step)
        )
        if tile_step > tensor_dim // tile_dim:
            tile_group_steps[i] = 1

    # Calculate the number of steps (number of taps) that needs to be in the generated sequence
    steps_per_dim = cls.__get_num_steps(
        tensor_dims=tensor_dims,
        tile_dims=tile_dims,
        step_dims=tile_group_steps,
        repeat_dims=tile_group_repeats,
    )
    num_steps = int(np.prod(steps_per_dim))

    # Define a function to calculate the offset of each tap in the sequence.
    def offset_fn(step_num: int, _prev_offset: int) -> int:
        tile_offsets = cls.__tile_offset_by_step_num(
            step_num,
            tile_group_steps,
            tile_group_repeats,
            steps_per_dim,
            iter_col_major,
        )
        total_offset = 0
        num_dims = len(tile_offsets)
        for dim, (offset, tile_dim) in enumerate(zip(tile_offsets, tile_dims)):
            total_offset += (
                offset
                * tile_dim
                * (
                    int(np.prod(tensor_dims[dim + 1 :]))
                    if dim < num_dims - 1
                    else 1
                )
            )
        return total_offset

    # Define a function that generates either sizes or strides for each tap in the sequence.
    def sizes_or_strides_fn(step_num, _prev_sizes, is_sizes):
        tile_offsets = cls.__tile_offset_by_step_num(
            step_num,
            tile_group_steps,
            tile_group_repeats,
            steps_per_dim,
            iter_col_major,
        )

        iter_sizes, iter_strides = cls.__sizes_strides_for_step_tile_group(
            tensor_dims,
            tile_dims,
            tile_group_steps,
            tile_group_repeats,
            tile_offsets,
            tile_col_major,
            tile_group_col_major,
            pattern_repeat=pattern_repeat,
            prune_step=prune_step,
        )
        if is_sizes:
            return iter_sizes
        else:
            return iter_strides

    sizes_fn = partial(sizes_or_strides_fn, is_sizes=True)
    strides_fn = partial(sizes_or_strides_fn, is_sizes=False)

    # Construct the sequence without defaults, fully relying on the constructor functions.
    return TensorAccessSequence(
        tensor_dims,
        num_steps,
        sizes_fn=sizes_fn,
        strides_fn=strides_fn,
        offset_fn=offset_fn,
    )

Utilities

validate_and_clean_sizes_strides

validate_and_clean_sizes_strides(
    sizes: Sequence[int] | None,
    strides: Sequence[int] | None,
    allow_none: bool = False,
    expected_dims: int | None = None,
) -> tuple[Sequence[int] | None, Sequence[int] | None]

This is a helper function to validate sizes, strides and remove any unused values from upper dimensions if possible.

Parameters:

Name Type Description Default
sizes Sequence[int] | None

The transformation strides, or None

required
strides Sequence[int] | None

The transformation sizes, or None

required
allow_none bool

Allow sizes and/or strides to be None. Defaults to False.

False
expected_dims int | None

Number of dimensions expected for both sizes and strides. Defaults to None.

None

Raises:

Type Description
ValueError

Validate sizes and strides

Returns:

Type Description
tuple[Sequence[int] | None, Sequence[int] | None]

tuple[Sequence[int] | None, Sequence[int] | None]: The 'cleaned' sizes and strides.

Source code in python/helpers/taplib/utils.py
def validate_and_clean_sizes_strides(
    sizes: Sequence[int] | None,
    strides: Sequence[int] | None,
    allow_none: bool = False,
    expected_dims: int | None = None,
) -> tuple[Sequence[int] | None, Sequence[int] | None]:
    """
    This is a helper function to validate sizes, strides and remove any
    unused values from upper dimensions if possible.

    Args:
        sizes (Sequence[int] | None): The transformation strides, or None
        strides (Sequence[int] | None): The transformation sizes, or None
        allow_none (bool, optional): Allow sizes and/or strides to be None. Defaults to False.
        expected_dims (int | None, optional): Number of dimensions expected for both sizes and strides. Defaults to None.

    Raises:
        ValueError: Validate sizes and strides

    Returns:
        tuple[Sequence[int] | None, Sequence[int] | None]: The 'cleaned' sizes and strides.
    """
    if not allow_none:
        if sizes is None:
            raise ValueError("Sizes is None, but expected Sequence[int]")
        if strides is None:
            raise ValueError("Strides is None, but expected Sequence[int]")
    # After this point can assume None is ok for sizes/strides

    if not (expected_dims is None):
        if expected_dims < 1:
            raise ValueError(f"Expected dimensions ({expected_dims}) should be >= 1")

    if sizes is None and strides is None:
        # nothing to do
        return None, None

    # Validate dimensions
    if (not (sizes is None)) and len(sizes) == 0:
        raise ValueError("len(sizes) must be >0")
    if (not (strides is None)) and len(strides) == 0:
        raise ValueError("len(strides) must be >0")

    if sizes and strides:
        if expected_dims:
            if len(sizes) != expected_dims:
                raise ValueError(
                    f"Num dimensions of sizes ({sizes}) is not expected number of dimensions ({expected_dims})"
                )
            if len(strides) != expected_dims:
                raise ValueError(
                    f"Num dimensions of strides ({strides}) is not expected number of dimensions ({expected_dims})"
                )
        elif len(strides) != len(sizes):
            raise ValueError(
                f"len(sizes) ({len(sizes)}) != len(strides) ({len(strides)})"
            )
    if strides:
        num_dims = len(strides)
    else:
        assert sizes is not None
        num_dims = len(sizes)

    # Validate sizes/strides values
    if sizes:
        sizes = deepcopy(sizes)
        for s in sizes:
            if s < 1:
                raise ValueError(f"All sizes must be >= 1, but got {sizes}")
    if strides:
        strides = deepcopy(strides)
        for s in strides:
            if s < 0:
                raise ValueError(f"All strides must be >= 0, but got {strides}")

    # Clean (set size=1, stride=0 for as many dims as possible)
    if sizes and strides:
        strides = list(strides)
        # Leave last dimension strides as whatever it happens to be
        for i in range(num_dims - 1):
            if sizes[i] == 1:
                strides[i] = 0
            else:
                break
    return sizes, strides

validate_tensor_dims

validate_tensor_dims(
    tensor_dims: Sequence[int],
    expected_dims: int | None = None,
) -> Sequence[int]

This is a helper function used to validate dimensions of tensors, namely be ensuring each dimension is > 0 and the dimensionality is as expected.

Parameters:

Name Type Description Default
tensor_dims Sequence[int]

Tensor dimensions to check

required
expected_dims int | None

Expected number of dimensions. Defaults to None.

None

Raises:

Type Description
ValueError

Validate the tensor dimensions

Returns:

Type Description
Sequence[int]

Sequence[int]: The validated tensor dimensions.

Source code in python/helpers/taplib/utils.py
def validate_tensor_dims(
    tensor_dims: Sequence[int], expected_dims: int | None = None
) -> Sequence[int]:
    """
    This is a helper function used to validate dimensions of tensors, namely
    be ensuring each dimension is > 0 and the dimensionality is as expected.

    Args:
        tensor_dims (Sequence[int]): Tensor dimensions to check
        expected_dims (int | None, optional): Expected number of dimensions. Defaults to None.

    Raises:
        ValueError: Validate the tensor dimensions

    Returns:
        Sequence[int]: The validated tensor dimensions.
    """
    if not (expected_dims is None):
        if expected_dims < 1:
            raise ValueError(f"Expected dimensions ({expected_dims}) should be >= 1")
    tensor_dims = deepcopy(tensor_dims)

    # Validate tensor dims and offset, then set
    if len(tensor_dims) == 0:
        raise ValueError(
            f"Number of tensor dimensions must be >= 1 (dimensions={tensor_dims})"
        )
    for d in tensor_dims:
        if d <= 0:
            raise ValueError(
                f"Each tensor dimension must be >= 1 (dimensions={tensor_dims})"
            )

    # We can treat a 1-dimensional tensor as a 2-dimensional tensor,
    if len(tensor_dims) == 1:
        tensor_dims = [1, tensor_dims[0]]

    if not (expected_dims is None) and len(tensor_dims) != expected_dims:
        raise ValueError(
            f"Tensor dimension ({tensor_dims}) does not match expected dimension ({expected_dims})"
        )

    return tensor_dims

validate_offset

validate_offset(
    offset: int, tensor_dims: Sequence[int] | None
) -> int

This is a helper function to validate an offset into the tensor. It primarily checks to see if the offset is a valid index to the tensor.

Parameters:

Name Type Description Default
offset int

The offset to check.

required
tensor_dims Sequence[int] | None

The dimensions of the tensor the offset corresponds to.

required

Raises:

Type Description
ValueError

Validate the offset.

Returns:

Name Type Description
int int

The validated offset.

Source code in python/helpers/taplib/utils.py
def validate_offset(offset: int, tensor_dims: Sequence[int] | None) -> int:
    """
    This is a helper function to validate an offset into the tensor.
    It primarily checks to see if the offset is a valid index to the tensor.

    Args:
        offset (int): The offset to check.
        tensor_dims (Sequence[int] | None): The dimensions of the tensor the offset corresponds to.

    Raises:
        ValueError: Validate the offset.

    Returns:
        int: The validated offset.
    """
    if offset < 0:
        raise ValueError(f"Offset must be >= 0 (offset={offset})")
    if tensor_dims:
        if offset >= np.prod(tensor_dims):
            raise ValueError(
                f"Offset too large: {offset}. Max value allowed for tensor: {np.prod(tensor_dims)}"
            )
    return offset