Signatures

At the heart of the package is the signatory.signature() function.

Note

It comes with quite a lot of optional arguments, but most of them won’t need to be used for most use cases. See Simple example for a straightforward look at how to use it.

signatory.signature(path: torch.Tensor, depth: int, stream: bool = False, basepoint: Union[bool, torch.Tensor] = False, inverse: bool = False, initial: Optional[torch.Tensor] = None, scalar_term: bool = False) torch.Tensor

Applies the signature transform to a stream of data.

The input path is expected to be a three-dimensional tensor, with dimensions \((N, L, C)\), where \(N\) is the batch size, \(L\) is the length of the input sequence, and \(C\) denotes the number of channels. Thus each batch element is interpreted as a stream of data \((x_1, \ldots, x_L)\), where each \(x_i \in \mathbb{R}^C\).

Let \(f = (f_1, \ldots, f_C) \colon [0, 1] \to \mathbb{R}^C\), be the unique continuous piecewise linear path such that \(f(\tfrac{i - 1}{N - 1}) = x_i\). Then and the signature transform of depth depth is computed, defined by

\[\mathrm{Sig}(\text{path}) = \left(\left( \,\underset{0 < t_1 < \cdots < t_k < 1}{\int\cdots\int} \prod_{j = 1}^k \frac{\mathrm d f_{i_j}}{\mathrm dt}(t_j) \mathrm dt_1 \cdots \mathrm dt_k \right)_{\!\!1 \leq i_1, \ldots, i_k \leq C}\right)_{\!\!1\leq k \leq \text{depth}}.\]

This gives a tensor of shape

\[(N, C + C^2 + \cdots + C^\text{depth}).\]
Parameters
  • path (torch.Tensor) – The batch of input paths to apply the signature transform to.

  • depth (int) – The depth to truncate the signature at.

  • stream (bool, optional) – Defaults to False. If False then the usual signature transform of the whole path is computed. If True then the signatures of all paths \((x_1, \ldots, x_j)\), for \(j=2, \ldots, L\), are returned. (Or \(j=1, \ldots, L\) is basepoint is passed, see below.)

  • basepoint (bool or torch.Tensor, optional) – Defaults to False. If basepoint is True then an additional point \(x_0 = 0 \in \mathbb{R}^C\) is prepended to the path before the signature transform is applied. (If this is False then the signature transform is invariant to translations of the path, which may or may not be desirable. Setting this to True removes this invariance.) Alternatively it may be a torch.Tensor specifying the value of \(x_0\), in which case it should have shape \((N, C)\).

  • inverse (bool, optional) – Defaults to False. If True then it is in fact the inverse signature that is computed. (Signatures form a group under the operation of the tensor product; the inverse is defined with respect to this operation.) From a machine learning perspective it does not particularly matter whether the signature or the inverse signature is computed - both represent essentially the same information as each other.

  • initial (None or torch.Tensor, optional) – Defaults to None. If it is a torch.Tensor then it must be of size \((N, C + C^2 + ... + C^\text{depth})\), corresponding to the signature of another path. Then this signature is pre-tensor-multiplied on to the signature of path. For a more thorough explanation, see this example.

  • scalar_term (bool, optional) – Defaults to False. If True then the first channel of the computed signature will be filled with the constant 1 (in accordance with the usual mathematical definition). If False then this channel is omitted (in accordance with useful machine learning practice).

Returns

A torch.Tensor. Given an input torch.Tensor of shape \((N, L, C)\), and input arguments depth, basepoint, stream, then the return value is, in pseudocode:

if stream:
    if basepoint is True or isinstance(basepoint, torch.Tensor):
        return torch.Tensor of shape (N, L, C + C^2 + ... + C^depth)
    else:
        return torch.Tensor of shape (N, L - 1, C + C^2 + ... + C^depth)
else:
    return torch.Tensor of shape (N, C + C^2 + ... + C^depth)

Note that the number of output channels may be calculated via the convenience function signatory.signature_channels().

class signatory.Signature(depth: int, stream: bool = False, inverse: bool = False, scalar_term: bool = False, **kwargs)

torch.nn.Module wrapper around the signatory.signature() function.

Parameters
forward(path: torch.Tensor, basepoint: Union[bool, torch.Tensor] = False, initial: Optional[torch.Tensor] = None) torch.Tensor

The forward operation.

Parameters
Returns

As signatory.signature().

signatory.signature_channels(channels: int, depth: int, scalar_term: bool = False) int

Computes the number of output channels from a signature call. Specifically, it computes

\[\text{channels} + \text{channels}^2 + \cdots + \text{channels}^\text{depth}.\]
Parameters
  • channels (int) – The number of channels in the input; that is, the dimension of the space that the input path resides in.

  • depth (int) – The depth of the signature that is being computed.

  • scalar_term (bool, optional) – Defaults to False. Whether to include the constant ‘1’ scalar that may be included.

Returns

An int specifying the number of channels in the signature of the path.

signatory.extract_signature_term(sigtensor: torch.Tensor, channels: int, depth: int, scalar_term: bool = False) torch.Tensor

Extracts a particular term from a signature.

The signature to depth \(d\) of a batch of paths in \(\mathbb{R}^\text{C}\) is a tensor with \(C + C^2 + \cdots + C^d\) channels. (See signatory.signature().) This function extracts the depth term of that, returning a tensor with just \(C^\text{depth}\) channels.

Parameters
  • sigtensor (torch.Tensor) – The signature to extract the term from. Should be a result from the signatory.signature() function.

  • channels (int) – The number of input channels \(C\).

  • depth (int) – The depth of the term to be extracted from the signature.

  • scalar_term (bool, optional) – Whether the signature was called with scalar_term=True or not.

Returns

The torch.Tensor corresponding to the depth term of the signature.

signatory.signature_combine(sigtensor1: torch.Tensor, sigtensor2: torch.Tensor, input_channels: int, depth: int, inverse: bool = False, scalar_term: bool = False) torch.Tensor

Combines two signatures into a single signature.

Usage is most clear by example. See Combining signatures.

See also signatory.multi_signature_combine() for a more general version.

Parameters
  • sigtensor1 (torch.Tensor) – The signature of a path, as returned by signatory.signature(). This should be a two-dimensional tensor.

  • sigtensor2 (torch.Tensor) – The signature of a second path, as returned by signatory.signature(), with the same shape as sigtensor1. Note that when the signature of the second path was created, it should have been called with basepoint set to the final value of the path that created sigtensor1. (See Combining signatures.)

  • input_channels (int) – The number of channels in the two paths that were used to compute sigtensor1 and sigtensor2. This must be the same for both sigtensor1 and sigtensor2.

  • depth (int) – The depth that sigtensor1 and sigtensor2 have been calculated to. This must be the same for both sigtensor1 and sigtensor2.

  • inverse (bool, optional) – Defaults to False. Whether sigtensor1 and sigtensor2 were created with inverse=True. This must be the same for both sigtensor1 and sigtensor2.

  • scalar_term (bool, optional) – Defaults to False. Whether sigtensor1 and sigtensor2 were created with scalar_term=True. This must the same for both sigtensor1 and sigtensor2.

Returns

Let path1 be the path whose signature is sigtensor1. Let path2 be the path whose signature is sigtensor2. Then this function returns the signature of the concatenation of path1 and path2 along their stream dimension.

Danger

There is a subtle bug which can occur when using this function incautiously. Make sure that sigtensor2 is created with an appropriate basepoint, see Combining signatures.

If this is not done then the return value of this function will be essentially meaningless numbers.

signatory.multi_signature_combine(sigtensors: List[torch.Tensor], input_channels: int, depth: int, inverse: bool = False, scalar_term: bool = False) torch.Tensor

Combines multiple signatures into a single signature.

See also signatory.signature_combine() for a simpler version.

Parameters
Returns

Let sigtensors be a list of tensors, call them \(\text{sigtensor}_i\) for \(i = 0, 1, \ldots, k\). Let \(\text{path}_i\) be the path whose signature is \(\text{sigtensor}_i\). Then this function returns the signature of the concatenation of \(\text{path}_i\) along their stream dimension.

Danger

Make sure that each element of sigtensors is created with an appropriate basepoint, as with signatory.signature_combine().