Skip to content

Commit c0a47de

Browse files
In progress commit on branch installer_script.
1 parent 1c0c4a3 commit c0a47de

File tree

6 files changed

+127
-94
lines changed

6 files changed

+127
-94
lines changed

CHANGELOG.txt

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ Added logsignature calculations to Path.
44
Added the signature_to_logsignature function.
55
Added the multi_signature_combine function.
66
Improved speed and stability of the backwards operation through Path.
7-
Added (fixed) memory profiling.
8-
Dramatically improved the speed of several calculations, in particular the the forward calculation of signatures and logsignatures, via parallelisation
7+
Dramatically improved the speed of several calculations, in particular the the forward calculation of signatures and logsignatures, via parallelisation.
8+
Added support for multiple versions of PyTorch via a custom install script.
9+
The usual documentation improvements.
910

1011
1.1.3
1112
-----

docs/pages/examples/online.rst

+7-2
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,18 @@ In code, this problem can be solved like this:
2121
# Generate some more data for the path
2222
Y = torch.rand(1, 7, 5)
2323
# Calculate the signature of the overall path
24-
sig_XY = signatory.signature(Y, 3, basepoint=X[:, -1, :], initial=sig_X)
24+
final_X = X[:, -1, :]
25+
sig_XY = signatory.signature(Y, 3, basepoint=final_X, initial=sig_X)
2526
2627
# This is equivalent to
2728
XY = torch.cat([X, Y], dim=1)
2829
sig_XY = signatory.signature(XY, 3)
2930
30-
As can be seen, two pieces of information need to be provided: the final value of :attr:`X` along the stream dimension, and the signature of :attr:`X`.
31+
As can be seen, two pieces of information need to be provided: the final value of :attr:`X` along the stream dimension, and the signature of :attr:`X`. But not :attr:`X` itself.
32+
33+
The first method (using the :attr:`initial` argument) will be much quicker than the second (simpler) method. The first
34+
method efficiently uses just the new information :attr:`Y`, whilst the second method unnecessarily iterates over all of
35+
the old information :attr:`X`.
3136

3237
In particular note that we only needed the last value of :attr:`X`. If memory efficiency is a concern, then by using the first method we can discard the other 999 terms of :attr:`X` without an issue!
3338

src/signatory/signature_module.py

+29-33
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from torch import nn
2020
from torch import autograd
2121
from torch.autograd import function as autograd_function
22+
import warnings
2223

2324
from . import backend
2425
# noinspection PyUnresolvedReferences
@@ -203,65 +204,53 @@ def _signature_batch_trick(path, depth, stream, basepoint, inverse, initial):
203204

204205
def signature(path, depth, stream=False, basepoint=False, inverse=False, initial=None):
205206
# type: (torch.Tensor, int, bool, Union[bool, torch.Tensor], bool, Union[None, torch.Tensor]) -> torch.Tensor
207+
206208
r"""Applies the signature transform to a stream of data.
207209
208210
The input :attr:`path` is expected to be a three-dimensional tensor, with dimensions :math:`(N, L, C)`, where
209211
:math:`N` is the batch size, :math:`L` is the length of the input sequence, and :math:`C` denotes the number of
210212
channels. Thus each batch element is interpreted as a stream of data :math:`(x_1, \ldots, x_L)`, where each
211213
:math:`x_i \in \mathbb{R}^C`.
212214
213-
Each path is then lifted to a piecewise linear path :math:`X \colon [0, 1] \to \mathbb{R}^C` and the signature
214-
transform of :attr:`path` to depth :attr:`depth`, is computed, defined by
215+
Let :math:`f = (f_1, \ldots, f_C) \colon [0, 1] \to \mathbb{R}^C`, be the unique continuous piecewise linear path
216+
such that :math:`f(\tfrac{i - 1}{N - 1}) = x_i`. Then and the signature transform of depth :attr:`depth` is
217+
computed, defined by
215218
216219
.. math::
217-
\exp(x_2 - x_1) \otimes \exp(x_3 - x_2) \otimes \cdots \otimes \exp(x_L - x_{L - 1}),
220+
\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}}.
218221
219-
which gives a tensor of shape
222+
This gives a tensor of shape
220223
221224
.. math::
222225
(N, C + C^2 + \cdots + C^\text{depth}).
223226
224-
If :attr:`basepoint` is True then an additional point :math:`x_0 = 0 \in \mathbb{R}^C` is prepended to the path
225-
before the signature transform is applied. Alternatively it can be a :class:`torch.Tensor` of shape :math:`(N, C)`
226-
specifying the point to prepend.
227-
228-
If :attr:`stream` is True then the signatures of all paths :math:`(x_1, \ldots, x_j)`, for :math:`j=2, \ldots, L`,
229-
are computed. (Or :math:`(x_0, \ldots, x_j)`, for :math:`j=1, \ldots, L` if :attr:`basepoint` is provided. In
230-
neither case is the signature of the path of a single element computed, as that isn't defined.)
231-
232227
Arguments:
233228
path (:class:`torch.Tensor`): The batch of input paths to apply the signature transform to.
234229
235230
depth (int): The depth to truncate the signature at.
236231
237-
stream (bool, optional): Defaults to False. If False then the signature transform of the whole path is computed.
238-
If True then the signature of all intermediate paths are also computed.
232+
stream (bool, optional): Defaults to False. If False then the usual signature transform of the whole path is
233+
computed. If True then the signatures of all paths :math:`(x_1, \ldots, x_j)`, for :math:`j=2, \ldots, L`,
234+
are returned.
239235
240-
basepoint (bool or :class:`torch.Tensor`, optional): Defaults to False. If True, then the input paths will have
241-
an additional point at the origin prepended to the start of the sequence. (If this is False then the
242-
signature transform is invariant to translations of the path, which may or may not be desirable.)
243-
Alternatively it may be a :class:`torch.Tensor` specifying the point to prepend, in which case it should
244-
have shape :math:`(N, C)`
236+
basepoint (bool or :class:`torch.Tensor`, optional): Defaults to False. If :attr:`basepoint` is True then an
237+
additional point :math:`x_0 = 0 \in \mathbb{R}^C` is prepended to the path before the signature transform is
238+
applied. (If this is False then the signature transform is invariant to translations of the path, which may
239+
or may not be desirable. Setting this to True removes this invariance.)
240+
Alternatively it may be a :class:`torch.Tensor` specifying the value of :math:`x_0`, in which case it should
241+
have shape :math:`(N, C)`.
245242
246243
inverse (bool, optional): Defaults to False. If True then it is in fact the inverse signature that is computed.
247-
That is,
248-
249-
.. math::
250-
\exp(x_{L - 1} - x_L) \otimes \cdots \otimes \exp(x_2 - x_3) \otimes \exp(x_1 - x_2).
251-
244+
That is, we flip the input path along its stream dimension before computing the signature. (But without the
245+
extra computational overhead of actually performing that flip separately.)
252246
From a machine learning perspective it does not particularly matter whether the signature or the inverse
253247
signature is computed - both represent essentially the same information as each other.
254248
255249
initial (None or :class:`torch.Tensor`, optional): Defaults to None. If it is a :class:`torch.Tensor` then it
256-
must be of size :math:`(N, C + C^2 + ... + C^\text{depth})`, and it will be premultiplied to the signature,
257-
so that in fact
258-
259-
.. math::
260-
\text{initial} \otimes \exp(x_2 - x_1) \otimes \exp(x_3 - x_2) \otimes \cdots \otimes \exp(x_L - x_{L - 1})
261-
262-
is computed. (Or the appropriate modification of this if :attr:`inverse=True` or if :attr:`basepoint` is
263-
passed.) If this argument is None then this extra multiplication is not done, and the signature is
264-
calculated as previously described.
250+
must be of size :math:`(N, C + C^2 + ... + C^\text{depth})`, corresponding to the signature of another path.
251+
Then this signature is pre-tensor-multiplied on to the signature of :attr:`path`. For a more thorough
252+
explanation, see :ref:`this example<examples-online>`.
253+
(The appropriate modifications are made if :attr:`inverse=True` or if :attr:`basepoint`.)
265254
266255
Returns:
267256
A :class:`torch.Tensor`. Given an input :class:`torch.Tensor` of shape :math:`(N, L, C)`, and input arguments
@@ -281,6 +270,13 @@ def signature(path, depth, stream=False, basepoint=False, inverse=False, initial
281270
:func:`signatory.signature_channels`.
282271
"""
283272

273+
if initial is not None and basepoint is False:
274+
warnings.warn("Argument 'initial' has been set but argument 'basepoint' has not. This is almost certainly a "
275+
"mistake. Argument 'basepoint' should be set to the final value of the path whose signature is "
276+
"'initial'. See the documentation at\n"
277+
" https://signatory.readthedocs.io/en/latest/pages/examples/online.html\n"
278+
"for more information.")
279+
284280
_signature_checkargs(path, depth, basepoint, initial)
285281

286282
# Coming up is a somewhat involved set of optimisations via parallelisation.

0 commit comments

Comments
 (0)