# 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: Union[None, 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

    - depth (int) – as `signatory.signature()`.

    - stream (bool, optional) – as `signatory.signature()`.

    - inverse (bool, optional) – as `signatory.signature()`.

    - scalar_term (bool, optional) – as `signatory.signature()`.

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

    The forward operation.

    Parameters

      - path (`torch.Tensor`) – As `signatory.signature()`.

      - basepoint (bool or `torch.Tensor`, optional) – As `signatory.signature()`.

      - initial (None or `torch.Tensor`, optional) – As `signatory.signature()`.

    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

    - sigtensors (list of `torch.Tensor`) – Signature of multiple paths, all of the same shape. They should all be two-dimensional tensors.

    - input_channels (int) – As `signatory.signature_combine()`.

    - depth (int) – As `signatory.signature_combine()`.

    - inverse (bool, optional) – As `signatory.signature_combine()`.

    - scalar_term (bool, optional) – As `signatory.signature_combine()`.

  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()`.