Skip to content

Log Signature

log_signatures_pytorch.log_signature.log_signature(path, depth, stream=False, method='default', mode='words', sparse=False, eps=0.0, lengths=None)

Compute log-signatures for batched paths.

The log-signature is a compressed representation of the signature. Two bases are supported:

  • mode="words" (default): Signatory-style Lyndon words basis (triangular/gather projection)
  • mode="hall": classic Hall basis

Parameters:

Name Type Description Default
path Tensor

Tensor of shape (batch, length, dim) representing batched paths. For a single path, pass path.unsqueeze(0) to add a batch dimension.

required
depth int

Maximum depth to truncate log-signature computation. The output dimension is logsigdim(dim, depth) for mode="hall" and logsigdim_words(dim, depth) for mode="words".

required
stream bool

If True, computed log-signatures are returned for each step. Default is False.

False
method str

Computation method: "default" (signature then log) or "bch_sparse" (sparse Hall-BCH, supported for depth <= 4). For higher depths, "bch_sparse" falls back to the default path automatically. Default is "default".

'default'
mode str

Basis for the log-signature coordinates: "words" (default) or "hall". "words" is only available with method="default".

'words'
sparse bool

If True, use sparse signature computation for paths with repeated points. Only applies when method="default". Default is False.

False
eps float

Threshold for change detection when using sparse mode. Default is 0.0.

0.0
lengths Tensor

Tensor of shape (batch,) with valid lengths for padded batches when using sparse mode. Default is None.

None

Returns:

Type Description
Tensor

If stream=False: Tensor of shape (batch, D) where D = logsigdim(dim, depth) for mode="hall" and D = logsigdim_words(dim, depth) for mode="words".

If stream=True: Tensor of shape (batch, length-1, D) with the same D definition as above.

Raises:

Type Description
ValueError

If path is not three-dimensional, if method is not "default" or "bch_sparse", or if an unsupported mode/method combination is requested.

Examples:

>>> import torch
>>> from log_signatures_pytorch import log_signature, logsigdim
>>> from log_signatures_pytorch.lyndon_words import logsigdim_words
>>>
>>> # Single path (add batch dimension)
>>> path = torch.tensor([[0.0, 0.0], [1.0, 1.0], [2.0, 0.0]]).unsqueeze(0)
>>> log_sig = log_signature(path, depth=2)
>>> log_sig.shape
torch.Size([1, 3])
>>> logsigdim_words(2, 2)
3
>>>
>>> # Batched paths
>>> batch_paths = torch.tensor([
...     [[0.0, 0.0], [1.0, 1.0]],
...     [[0.0, 0.0], [2.0, 2.0]],
... ])
>>> log_sig = log_signature(batch_paths, depth=2)
>>> log_sig.shape
torch.Size([2, 3])
>>>
>>> # Streaming log-signatures
>>> log_sig_stream = log_signature(path, depth=2, stream=True)
>>> log_sig_stream.shape
torch.Size([1, 2, 3])
>>>
>>> # Using BCH method (faster for depth <= 4)
>>> log_sig_bch = log_signature(path, depth=2, method="bch_sparse", mode="hall")
>>> log_sig_bch.shape
torch.Size([1, 3])
Source code in src/log_signatures_pytorch/log_signature.py
def log_signature(
    path: Tensor,
    depth: int,
    stream: bool = False,
    method: str = "default",
    mode: str = "words",
    sparse: bool = False,
    eps: float = 0.0,
    lengths: Tensor | None = None,
) -> Tensor:
    """Compute log-signatures for batched paths.

    The log-signature is a compressed representation of the signature. Two bases are
    supported:

    - ``mode=\"words\"`` (default): Signatory-style Lyndon words basis (triangular/gather projection)
    - ``mode=\"hall\"``: classic Hall basis

    Parameters
    ----------
    path : Tensor
        Tensor of shape ``(batch, length, dim)`` representing batched paths.
        For a single path, pass ``path.unsqueeze(0)`` to add a batch dimension.
    depth : int
        Maximum depth to truncate log-signature computation. The output dimension
        is ``logsigdim(dim, depth)`` for ``mode="hall"`` and
        ``logsigdim_words(dim, depth)`` for ``mode="words"``.
    stream : bool, optional
        If True, computed log-signatures are returned for each step. Default is False.
    method : str, optional
        Computation method: "default" (signature then log) or "bch_sparse"
        (sparse Hall-BCH, supported for depth <= 4). For higher depths,
        "bch_sparse" falls back to the default path automatically.
        Default is "default".
    mode : str, optional
        Basis for the log-signature coordinates: "words" (default) or "hall".
        "words" is only available with ``method=\"default\"``.
    sparse : bool, optional
        If True, use sparse signature computation for paths with repeated points.
        Only applies when ``method=\"default\"``. Default is False.
    eps : float, optional
        Threshold for change detection when using sparse mode. Default is 0.0.
    lengths : Tensor, optional
        Tensor of shape ``(batch,)`` with valid lengths for padded batches when
        using sparse mode. Default is None.

    Returns
    -------
    Tensor
        If ``stream=False``: Tensor of shape ``(batch, D)`` where
        ``D = logsigdim(dim, depth)`` for ``mode=\"hall\"`` and
        ``D = logsigdim_words(dim, depth)`` for ``mode=\"words\"``.

        If ``stream=True``: Tensor of shape ``(batch, length-1, D)`` with
        the same ``D`` definition as above.

    Raises
    ------
    ValueError
        If ``path`` is not three-dimensional, if ``method`` is not
        "default" or "bch_sparse", or if an unsupported ``mode``/``method``
        combination is requested.

    Examples
    --------
    >>> import torch
    >>> from log_signatures_pytorch import log_signature, logsigdim
    >>> from log_signatures_pytorch.lyndon_words import logsigdim_words
    >>>
    >>> # Single path (add batch dimension)
    >>> path = torch.tensor([[0.0, 0.0], [1.0, 1.0], [2.0, 0.0]]).unsqueeze(0)
    >>> log_sig = log_signature(path, depth=2)
    >>> log_sig.shape
    torch.Size([1, 3])
    >>> logsigdim_words(2, 2)
    3
    >>>
    >>> # Batched paths
    >>> batch_paths = torch.tensor([
    ...     [[0.0, 0.0], [1.0, 1.0]],
    ...     [[0.0, 0.0], [2.0, 2.0]],
    ... ])
    >>> log_sig = log_signature(batch_paths, depth=2)
    >>> log_sig.shape
    torch.Size([2, 3])
    >>>
    >>> # Streaming log-signatures
    >>> log_sig_stream = log_signature(path, depth=2, stream=True)
    >>> log_sig_stream.shape
    torch.Size([1, 2, 3])
    >>>
    >>> # Using BCH method (faster for depth <= 4)
    >>> log_sig_bch = log_signature(path, depth=2, method="bch_sparse", mode="hall")
    >>> log_sig_bch.shape
    torch.Size([1, 3])
    """
    if path.ndim != 3:
        msg = (
            f"Path must be of shape (batch, path_length, path_dim); got {path.shape}. "
            "Wrap a single path with path.unsqueeze(0)."
        )
        raise ValueError(msg)

    mode = (mode or "words").lower()
    if mode not in {"hall", "words"}:
        raise ValueError(f"Unsupported mode '{mode}'. Use 'hall' or 'words'.")

    method = (method or "default").lower()
    if method == "bch_sparse":
        if mode != "hall":
            raise ValueError("mode='words' is only supported with method='default'.")
        if not sparse_bch_supports_depth(depth):
            log_sig = _batch_log_signature(
                path,
                depth=depth,
                stream=stream,
                mode=mode,
                sparse=sparse,
                eps=eps,
                lengths=lengths,
            )
        else:
            log_sig = _batch_log_signature_bch(
                path,
                depth=depth,
                stream=stream,
            )
    elif method in {"default", None}:
        log_sig = _batch_log_signature(
            path,
            depth=depth,
            stream=stream,
            mode=mode,
            sparse=sparse,
            eps=eps,
            lengths=lengths,
        )
    else:
        raise ValueError(
            f"Unsupported method '{method}'. Use 'default' or 'bch_sparse'."
        )

    return log_sig

log_signatures_pytorch.log_signature.signature_to_logsignature(signature, depth, mode='words')

Convert signature to log-signature.

Parameters:

Name Type Description Default
signature Tensor

Signature tensor with arbitrary leading batch/window dimensions and trailing dimension equal to the flattened signature size width + width^2 + ... + width^depth. This includes outputs from :func:signature, :func:windowed_signature, or any precomputed flattened signature with that final dimension.

required
depth int

Depth of the log-signature.

required
mode str

Basis for the log-signature coordinates: "words" (default) or "hall".

'words'

Returns:

Type Description
Tensor

Log-signature tensor with the same leading shape as signature but with the last dimension replaced by the log-signature dimension (logsigdim or logsigdim_words depending on mode).

Source code in src/log_signatures_pytorch/log_signature.py
def signature_to_logsignature(
    signature: Tensor,
    depth: int,
    mode: str = "words",
) -> Tensor:
    """Convert signature to log-signature.

    Parameters
    ----------
    signature: Tensor
        Signature tensor with arbitrary leading batch/window dimensions and
        trailing dimension equal to the flattened signature size
        ``width + width^2 + ... + width^depth``. This includes outputs from
        :func:`signature`, :func:`windowed_signature`, or any precomputed
        flattened signature with that final dimension.
    depth: int
        Depth of the log-signature.
    mode: str, optional
        Basis for the log-signature coordinates: "words" (default) or "hall".

    Returns
    -------
    Tensor
        Log-signature tensor with the same leading shape as ``signature`` but
        with the last dimension replaced by the log-signature dimension
        (``logsigdim`` or ``logsigdim_words`` depending on ``mode``).
    """
    mode = (mode or "words").lower()
    if mode not in {"hall", "words"}:
        raise ValueError(f"Unsupported mode '{mode}'. Use 'hall' or 'words'.")

    if signature.ndim < 2:
        raise ValueError(
            "signature tensor must have at least one batch dimension "
            "and a trailing signature dimension."
        )

    sigdim = signature.shape[-1]
    width = _infer_width_from_signature_dim(sigdim, depth)

    leading_shape = signature.shape[:-1]
    flat = signature.reshape(-1, sigdim)

    sig_tensors = _unflatten_signature(flat, width, depth)
    log_sig_tensors = _signature_to_logsignature_tensor(sig_tensors, width, depth)

    projector = _project_to_hall_basis if mode == "hall" else _project_to_words_basis
    projected = projector(log_sig_tensors, width, depth)

    return projected.reshape(*leading_shape, projected.shape[-1])

log_signatures_pytorch.log_signature.windowed_log_signature(path, depth, window_size, hop_size, mode='words')

Sliding-window log-signatures via windowed signatures + projection.

Windows are formed with size=window_size and step=hop_size. Signatures are obtained using :func:windowed_signature (Chen reuse of streaming prefixes), then converted to log-signatures and projected to the requested basis.

Parameters:

Name Type Description Default
path Tensor

Tensor of shape (batch, length, dim) representing batched paths. For a single path, pass path.unsqueeze(0) to add a batch dimension.

required
depth int

Maximum depth to truncate log-signature computation. The output dimension is logsigdim(dim, depth) for mode="hall" and logsigdim_words(dim, depth) for mode="words".

required
window_size int

Size of the window to use for the signature.

required
hop_size int

Hop size to use for the signature.

required
mode str

Basis for the log-signature coordinates: "words" (default) or "hall". "words" is only available with method="default".

'words'

Returns:

Type Description
Tensor

Tensor of shape (batch, num_windows, D) where num_windows = 1 + (length - window_size) // hop_size and D = logsigdim_words(dim, depth) if mode="words" else logsigdim(dim, depth) for Hall.

Raises:

Type Description
ValueError

If path is not three-dimensional or if mode is unsupported.

Source code in src/log_signatures_pytorch/log_signature.py
def windowed_log_signature(
    path: Tensor,
    depth: int,
    window_size: int,
    hop_size: int,
    mode: str = "words",
) -> Tensor:
    """Sliding-window log-signatures via windowed signatures + projection.

    Windows are formed with ``size=window_size`` and ``step=hop_size``.
    Signatures are obtained using :func:`windowed_signature` (Chen reuse of
    streaming prefixes), then converted to log-signatures and projected to the
    requested basis.

    Parameters
    ----------
    path : Tensor
        Tensor of shape ``(batch, length, dim)`` representing batched paths.
        For a single path, pass ``path.unsqueeze(0)`` to add a batch dimension.
    depth : int
        Maximum depth to truncate log-signature computation. The output dimension
        is ``logsigdim(dim, depth)`` for ``mode="hall"`` and
        ``logsigdim_words(dim, depth)`` for ``mode="words"``.
    window_size : int
        Size of the window to use for the signature.
    hop_size : int
        Hop size to use for the signature.
    mode : str, optional
        Basis for the log-signature coordinates: "words" (default) or "hall".
        "words" is only available with ``method=\"default\"``.

    Returns
    -------
    Tensor
        Tensor of shape ``(batch, num_windows, D)`` where
        ``num_windows = 1 + (length - window_size) // hop_size`` and
        ``D = logsigdim_words(dim, depth)`` if ``mode=\"words\"`` else
        ``logsigdim(dim, depth)`` for Hall.

    Raises
    ------
    ValueError
        If ``path`` is not three-dimensional or if ``mode`` is unsupported.
    """
    if path.ndim != 3:
        msg = (
            f"Path must be of shape (batch, path_length, path_dim); got {path.shape}. "
            "Wrap a single path with path.unsqueeze(0)."
        )
        raise ValueError(msg)

    mode = (mode or "words").lower()
    if mode not in {"hall", "words"}:
        raise ValueError(f"Unsupported mode '{mode}'. Use 'hall' or 'words'.")

    batch, _, width = path.shape

    # Reuse efficient streaming→windowed signature computation.
    window_sig = windowed_signature(
        path,
        depth=depth,
        window_size=window_size,
        hop_size=hop_size,
    )  # (batch, num_windows, sigdim)

    batch_windows, num_windows = batch, window_sig.shape[1]

    flattened = window_sig.reshape(batch_windows * num_windows, -1)
    sig_tensors = _unflatten_signature(flattened, width, depth)
    log_sig_tensors = _signature_to_logsignature_tensor(sig_tensors, width, depth)

    projector = _project_to_hall_basis if mode == "hall" else _project_to_words_basis
    projected = projector(log_sig_tensors, width, depth)

    return projected.reshape(batch_windows, num_windows, -1)