Skip to content

Hall BCH

log_signatures_pytorch.hall_bch.HallBCH

Truncated BCH on Hall-basis coordinates (supports depth <= 4).

This class implements the Baker-Campbell-Hausdorff formula directly in Hall basis coordinates, allowing incremental log-signature computation without materializing the full tensor-algebra signature.

Parameters:

Name Type Description Default
width int

Path dimension (size of the alphabet).

required
depth int

Truncation depth. Must be <= 4 for exact computation.

required
device device

Device on which to store structure constants and perform computations.

required
dtype dtype

Data type for computations.

required

Attributes:

Name Type Description
width int

Path dimension.

depth int

Truncation depth.

dim int

Dimension of the log-signature (logsigdim(width, depth)).

device device

Device for computations.

dtype dtype

Data type for computations.

Examples:

>>> import torch
>>> from log_signatures_pytorch.hall_bch import HallBCH
>>>
>>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
>>> x = torch.tensor([[1.0, 2.0, 0.0]])  # Hall coordinates (batch=1, dim=3)
>>> y = torch.tensor([[3.0, 4.0, 0.0]])  # Hall coordinates (batch=1, dim=3)
>>> result = bch.bch(x, y)
>>> result.shape
torch.Size([1, 3])
Source code in src/log_signatures_pytorch/hall_bch.py
class HallBCH:
    """Truncated BCH on Hall-basis coordinates (supports depth <= 4).

    This class implements the Baker-Campbell-Hausdorff formula directly in
    Hall basis coordinates, allowing incremental log-signature computation
    without materializing the full tensor-algebra signature.

    Parameters
    ----------
    width : int
        Path dimension (size of the alphabet).
    depth : int
        Truncation depth. Must be <= 4 for exact computation.
    device : torch.device
        Device on which to store structure constants and perform computations.
    dtype : torch.dtype
        Data type for computations.

    Attributes
    ----------
    width : int
        Path dimension.
    depth : int
        Truncation depth.
    dim : int
        Dimension of the log-signature (logsigdim(width, depth)).
    device : torch.device
        Device for computations.
    dtype : torch.dtype
        Data type for computations.

    Examples
    --------
    >>> import torch
    >>> from log_signatures_pytorch.hall_bch import HallBCH
    >>>
    >>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
    >>> x = torch.tensor([[1.0, 2.0, 0.0]])  # Hall coordinates (batch=1, dim=3)
    >>> y = torch.tensor([[3.0, 4.0, 0.0]])  # Hall coordinates (batch=1, dim=3)
    >>> result = bch.bch(x, y)
    >>> result.shape
    torch.Size([1, 3])
    """

    def __init__(
        self,
        width: int,
        depth: int,
        device: torch.device,
        dtype: torch.dtype,
    ):
        self.width = width
        self.depth = depth
        self.dim = logsigdim(width, depth)
        sparse_idx, sparse_vals = _sparse_constants(width, depth)
        self._sparse_idx = sparse_idx.to(device=device)
        self._sparse_vals = sparse_vals.to(device=device, dtype=dtype)
        self.device = device
        self.dtype = dtype

    def increment_to_hall(self, delta: torch.Tensor) -> torch.Tensor:
        """Embed path increment (batch, width) into Hall coordinates.

        Converts a path increment (which lives in degree-1 of the free Lie algebra)
        into Hall basis coordinates by placing it in the first ``width`` components
        and zeroing the rest.

        Parameters
        ----------
        delta : torch.Tensor
            Tensor of shape ``(batch, width)`` representing path increments.

        Returns
        -------
        torch.Tensor
            Tensor of shape ``(batch, self.dim)`` with the increment embedded
            in Hall coordinates.

        Examples
        --------
        >>> import torch
        >>> from log_signatures_pytorch.hall_bch import HallBCH
        >>>
        >>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
        >>> delta = torch.tensor([[1.0, 2.0]])  # (batch=1, width=2)
        >>> result = bch.increment_to_hall(delta)
        >>> result.shape
        torch.Size([1, 3])
        >>> result[:, :2]  # First two components match delta
        tensor([[1., 2.]])
        """
        batch = delta.shape[0]
        out = torch.zeros(batch, self.dim, device=self.device, dtype=self.dtype)
        out[:, : self.width] = delta
        return out

    def bracket_sparse(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
        """Sparse scatter-based bracket using nonzero structure constants.

        Computes the Lie bracket [x, y] in Hall coordinates using sparse
        structure constants for efficiency.

        Parameters
        ----------
        x : torch.Tensor
            Tensor of shape ``(batch, self.dim)`` in Hall coordinates.
        y : torch.Tensor
            Tensor of shape ``(batch, self.dim)`` in Hall coordinates.

        Returns
        -------
        torch.Tensor
            Tensor of shape ``(batch, self.dim)`` representing [x, y] in
            Hall coordinates.

        Notes
        -----
        This implementation uses sparse structure constants to avoid computing
        all possible bracket combinations, improving efficiency.

        Examples
        --------
        >>> import torch
        >>> from log_signatures_pytorch.hall_bch import HallBCH
        >>>
        >>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
        >>> x = torch.tensor([[1.0, 2.0, 0.0]])
        >>> y = torch.tensor([[3.0, 4.0, 0.0]])
        >>> bracket = bch.bracket_sparse(x, y)
        >>> bracket.shape
        torch.Size([1, 3])
        """
        if self._sparse_idx.numel() == 0:
            return torch.zeros_like(x)
        i_idx = self._sparse_idx[:, 0]
        j_idx = self._sparse_idx[:, 1]
        k_idx = self._sparse_idx[:, 2]
        coeff = self._sparse_vals
        # Gather x_i and y_j for all nonzeros
        xi = x[:, i_idx]  # (batch, nnz)
        yj = y[:, j_idx]  # (batch, nnz)
        contrib = xi * yj * coeff  # broadcast over batch
        out = torch.zeros_like(x)
        out.scatter_add_(1, k_idx.unsqueeze(0).expand_as(contrib), contrib)
        return out

    def bch(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
        """BCH(x, y) truncated to depth <= 4 (caps at 4 if configured higher).

        Computes the Baker-Campbell-Hausdorff formula BCH(x, y) = log(exp(x) exp(y))
        in Hall coordinates, truncated to the specified depth.

        Parameters
        ----------
        x : torch.Tensor
            Tensor of shape ``(batch, self.dim)`` in Hall coordinates.
        y : torch.Tensor
            Tensor of shape ``(batch, self.dim)`` in Hall coordinates.

        Returns
        -------
        torch.Tensor
            Tensor of shape ``(batch, self.dim)`` representing BCH(x, y) in
            Hall coordinates.

        Notes
        -----
        This method supports depths up to 4. If a higher depth is configured,
        it caps the computation at depth 4. The caller should use the default
        signature→log path for higher depths.

        Examples
        --------
        >>> import torch
        >>> from log_signatures_pytorch.hall_bch import HallBCH
        >>>
        >>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
        >>> x = torch.tensor([[1.0, 2.0, 0.0]])
        >>> y = torch.tensor([[3.0, 4.0, 0.0]])
        >>> result = bch.bch(x, y)
        >>> result.shape
        torch.Size([1, 3])
        """
        depth = self.depth
        bracket = self.bracket_sparse
        if depth <= 4:
            return self._bch_closed_form(x, y, bracket, depth)
        # Unsupported depth; caller should have fallen back to default path.
        return self._bch_closed_form(x, y, bracket, 4)

    def _bch_closed_form(self, x, y, bracket, depth):
        z = x + y
        if depth == 1:
            return z
        xy = bracket(x, y)
        z = z + 0.5 * xy
        if depth == 2:
            return z
        x_xy = bracket(x, xy)
        y_yx = bracket(y, bracket(y, x))
        z = z + (1.0 / 12.0) * (x_xy + y_yx)
        if depth == 3:
            return z
        y_xxy = bracket(y, x_xy)
        z = z - (1.0 / 24.0) * y_xxy
        return z

bch(x, y)

BCH(x, y) truncated to depth <= 4 (caps at 4 if configured higher).

Computes the Baker-Campbell-Hausdorff formula BCH(x, y) = log(exp(x) exp(y)) in Hall coordinates, truncated to the specified depth.

Parameters:

Name Type Description Default
x Tensor

Tensor of shape (batch, self.dim) in Hall coordinates.

required
y Tensor

Tensor of shape (batch, self.dim) in Hall coordinates.

required

Returns:

Type Description
Tensor

Tensor of shape (batch, self.dim) representing BCH(x, y) in Hall coordinates.

Notes

This method supports depths up to 4. If a higher depth is configured, it caps the computation at depth 4. The caller should use the default signature→log path for higher depths.

Examples:

>>> import torch
>>> from log_signatures_pytorch.hall_bch import HallBCH
>>>
>>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
>>> x = torch.tensor([[1.0, 2.0, 0.0]])
>>> y = torch.tensor([[3.0, 4.0, 0.0]])
>>> result = bch.bch(x, y)
>>> result.shape
torch.Size([1, 3])
Source code in src/log_signatures_pytorch/hall_bch.py
def bch(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
    """BCH(x, y) truncated to depth <= 4 (caps at 4 if configured higher).

    Computes the Baker-Campbell-Hausdorff formula BCH(x, y) = log(exp(x) exp(y))
    in Hall coordinates, truncated to the specified depth.

    Parameters
    ----------
    x : torch.Tensor
        Tensor of shape ``(batch, self.dim)`` in Hall coordinates.
    y : torch.Tensor
        Tensor of shape ``(batch, self.dim)`` in Hall coordinates.

    Returns
    -------
    torch.Tensor
        Tensor of shape ``(batch, self.dim)`` representing BCH(x, y) in
        Hall coordinates.

    Notes
    -----
    This method supports depths up to 4. If a higher depth is configured,
    it caps the computation at depth 4. The caller should use the default
    signature→log path for higher depths.

    Examples
    --------
    >>> import torch
    >>> from log_signatures_pytorch.hall_bch import HallBCH
    >>>
    >>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
    >>> x = torch.tensor([[1.0, 2.0, 0.0]])
    >>> y = torch.tensor([[3.0, 4.0, 0.0]])
    >>> result = bch.bch(x, y)
    >>> result.shape
    torch.Size([1, 3])
    """
    depth = self.depth
    bracket = self.bracket_sparse
    if depth <= 4:
        return self._bch_closed_form(x, y, bracket, depth)
    # Unsupported depth; caller should have fallen back to default path.
    return self._bch_closed_form(x, y, bracket, 4)

bracket_sparse(x, y)

Sparse scatter-based bracket using nonzero structure constants.

Computes the Lie bracket [x, y] in Hall coordinates using sparse structure constants for efficiency.

Parameters:

Name Type Description Default
x Tensor

Tensor of shape (batch, self.dim) in Hall coordinates.

required
y Tensor

Tensor of shape (batch, self.dim) in Hall coordinates.

required

Returns:

Type Description
Tensor

Tensor of shape (batch, self.dim) representing [x, y] in Hall coordinates.

Notes

This implementation uses sparse structure constants to avoid computing all possible bracket combinations, improving efficiency.

Examples:

>>> import torch
>>> from log_signatures_pytorch.hall_bch import HallBCH
>>>
>>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
>>> x = torch.tensor([[1.0, 2.0, 0.0]])
>>> y = torch.tensor([[3.0, 4.0, 0.0]])
>>> bracket = bch.bracket_sparse(x, y)
>>> bracket.shape
torch.Size([1, 3])
Source code in src/log_signatures_pytorch/hall_bch.py
def bracket_sparse(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
    """Sparse scatter-based bracket using nonzero structure constants.

    Computes the Lie bracket [x, y] in Hall coordinates using sparse
    structure constants for efficiency.

    Parameters
    ----------
    x : torch.Tensor
        Tensor of shape ``(batch, self.dim)`` in Hall coordinates.
    y : torch.Tensor
        Tensor of shape ``(batch, self.dim)`` in Hall coordinates.

    Returns
    -------
    torch.Tensor
        Tensor of shape ``(batch, self.dim)`` representing [x, y] in
        Hall coordinates.

    Notes
    -----
    This implementation uses sparse structure constants to avoid computing
    all possible bracket combinations, improving efficiency.

    Examples
    --------
    >>> import torch
    >>> from log_signatures_pytorch.hall_bch import HallBCH
    >>>
    >>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
    >>> x = torch.tensor([[1.0, 2.0, 0.0]])
    >>> y = torch.tensor([[3.0, 4.0, 0.0]])
    >>> bracket = bch.bracket_sparse(x, y)
    >>> bracket.shape
    torch.Size([1, 3])
    """
    if self._sparse_idx.numel() == 0:
        return torch.zeros_like(x)
    i_idx = self._sparse_idx[:, 0]
    j_idx = self._sparse_idx[:, 1]
    k_idx = self._sparse_idx[:, 2]
    coeff = self._sparse_vals
    # Gather x_i and y_j for all nonzeros
    xi = x[:, i_idx]  # (batch, nnz)
    yj = y[:, j_idx]  # (batch, nnz)
    contrib = xi * yj * coeff  # broadcast over batch
    out = torch.zeros_like(x)
    out.scatter_add_(1, k_idx.unsqueeze(0).expand_as(contrib), contrib)
    return out

increment_to_hall(delta)

Embed path increment (batch, width) into Hall coordinates.

Converts a path increment (which lives in degree-1 of the free Lie algebra) into Hall basis coordinates by placing it in the first width components and zeroing the rest.

Parameters:

Name Type Description Default
delta Tensor

Tensor of shape (batch, width) representing path increments.

required

Returns:

Type Description
Tensor

Tensor of shape (batch, self.dim) with the increment embedded in Hall coordinates.

Examples:

>>> import torch
>>> from log_signatures_pytorch.hall_bch import HallBCH
>>>
>>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
>>> delta = torch.tensor([[1.0, 2.0]])  # (batch=1, width=2)
>>> result = bch.increment_to_hall(delta)
>>> result.shape
torch.Size([1, 3])
>>> result[:, :2]  # First two components match delta
tensor([[1., 2.]])
Source code in src/log_signatures_pytorch/hall_bch.py
def increment_to_hall(self, delta: torch.Tensor) -> torch.Tensor:
    """Embed path increment (batch, width) into Hall coordinates.

    Converts a path increment (which lives in degree-1 of the free Lie algebra)
    into Hall basis coordinates by placing it in the first ``width`` components
    and zeroing the rest.

    Parameters
    ----------
    delta : torch.Tensor
        Tensor of shape ``(batch, width)`` representing path increments.

    Returns
    -------
    torch.Tensor
        Tensor of shape ``(batch, self.dim)`` with the increment embedded
        in Hall coordinates.

    Examples
    --------
    >>> import torch
    >>> from log_signatures_pytorch.hall_bch import HallBCH
    >>>
    >>> bch = HallBCH(width=2, depth=2, device=torch.device("cpu"), dtype=torch.float32)
    >>> delta = torch.tensor([[1.0, 2.0]])  # (batch=1, width=2)
    >>> result = bch.increment_to_hall(delta)
    >>> result.shape
    torch.Size([1, 3])
    >>> result[:, :2]  # First two components match delta
    tensor([[1., 2.]])
    """
    batch = delta.shape[0]
    out = torch.zeros(batch, self.dim, device=self.device, dtype=self.dtype)
    out[:, : self.width] = delta
    return out

log_signatures_pytorch.hall_bch.sparse_bch_supports_depth(depth)

Return True if the BCH truncation is implemented for this depth.

Checks whether the sparse Hall-BCH method supports the given depth. Currently, only depths up to 4 are supported.

Parameters:

Name Type Description Default
depth int

Truncation depth to check.

required

Returns:

Type Description
bool

True if depth <= 4, False otherwise.

Source code in src/log_signatures_pytorch/hall_bch.py
def sparse_bch_supports_depth(depth: int) -> bool:
    """Return True if the BCH truncation is implemented for this depth.

    Checks whether the sparse Hall-BCH method supports the given depth.
    Currently, only depths up to 4 are supported.

    Parameters
    ----------
    depth : int
        Truncation depth to check.

    Returns
    -------
    bool
        True if depth <= 4, False otherwise.
    """
    return depth <= 4