Combining signaturesΒΆ
Suppose we have two paths, and want to combine their signatures. That is, we know the signatures of the two paths, and would like to know the signature of the two paths concatenated together. This can be done with the signatory.signature_combine()
function.
import torch
import signatory
depth = 3
input_channels = 5
path1 = torch.rand(1, 10, input_channels)
path2 = torch.rand(1, 5, input_channels)
sig_path1 = signatory.signature(path1, depth)
sig_path2 = signatory.signature(path2, depth,
basepoint=path1[:, -1, :])
### OPTION 1: efficient, using signature_combine
sig_combined = signatory.signature_combine(sig_path1, sig_path2,
input_channels, depth)
### OPTION 2: inefficient, without using signature_combine
path_combined = torch.cat([path1, path2], dim=1)
sig_combined = signatory.signature(path_combined, depth)
### Both options will produce the same value for sig_combined
Danger
Note in particular that the end of path1
is used as the basepoint
when calculating sig_path2
in Option 1. It is important that path2
starts from the same place that path1
finishes. Otherwise there will be a jump between the end of path1
and the start of path2
which the signature will not see.
If it is known that path1[:, -1, :] == path2[:, 0, :]
, so that in fact path1
does finish where path2
starts, then only in this case can the use of basepoint
safely be skipped. (And if basepoint
is set to this value then it will not change the result.)
With Option 2 it is clearest what is being computed. However this is also going to be slower: the signature of path1
is already known, but Option 2 does not use this information at all, and instead performs a lot of unnecessary computation. Furthermore its calculation requires holding all of path1
in memory, instead of just path1[:, -1, :]
.
Note how with Option 1, once sig_path1
has been computed, then the only thing that must now be held in memory is sig_path1
and path1[:, -1, :]
. This means that the amount of memory required is independent of the length of path1
. Thus if path
is very long, or can grow to arbitrary length as time goes by, then the use of this option (over Option 2) is crucial.
Tip
Combining signatures in this way is the most sensible way to do things if the signature of path2
is actually desirable information on its own.
However if only the signature of the combined path is of interest, then this can be computed even more efficiently by
sig_path1 = signatory.signature(path1, depth)
sig_combined = signatory.signature(path2, depth,
basepoint=path1[:, -1, :],
initial=sig_path1)
For further examples of this nature, see Computing the signature of an incoming stream of data.