def ComputeMultiLevelSig(path, number_of_segment, deg_of_sig, s, sig='False'): """ compute the signature and log-signature of segments of one path of dimension (n, d) """ n_t = path.shape[0] n_Path = path.shape[1] t_vec = np.arange(1, n_t, int(n_t / number_of_segment)) t_vec = np.append(t_vec, n_t) MultiLevelSig = np.empty MultiLevelLogSig = np.empty for i in range(1, number_of_segment + 1, 1): temp_path = path[t_vec[i - 1] - 1:t_vec[i], 0:2] if deg_of_sig == 1: temp = iisignature.sig(temp_path, 1) else: if sig == 'False': templog = iisignature.logsig(temp_path, s) else: templog = iisignature.sig(temp_path, deg_of_sig) tempStart = (temp_path[0, 1]) # print(temp) if (i == 1): #MultiLevelSig = temp MultiLevelLogSig = templog MultiStart = np.array([tempStart]) else: #MultiLevelSig = np.concatenate((MultiLevelSig,temp), axis = 0) MultiLevelLogSig = np.concatenate( (MultiLevelLogSig, templog), axis=0) MultiStart = np.append(MultiStart, [tempStart]) return {'MultiLevelSig': MultiLevelSig, 'MultiLevelLogSig': MultiLevelLogSig, 'MultiStart': MultiStart}
def testa(self): numpy.random.seed(775) d = 3 m = 5 pathLength = 5 numberToDo = 2 paths = numpy.random.uniform(size=(numberToDo,pathLength,d)) sigs = numpy.vstack([iisignature.sig(i,m) for i in paths]) scales = numpy.random.uniform(0.5,0.97,size=(numberToDo,d)) scaledPaths = paths * scales[:,numpy.newaxis,:] scaledSigs = numpy.vstack([iisignature.sig(i,m) for i in scaledPaths]) scaledSigsCalc = iisignature.sigscale(sigs,scales,m) self.assertEqual(scaledSigs.shape,scaledSigsCalc.shape) self.assertLess(diff(scaledSigs,scaledSigsCalc),0.0000001) bumpedScales = 1.001 * scales bumpedSigs = 1.001 * sigs base = numpy.sum(scaledSigsCalc) bump1 = numpy.sum(iisignature.sigscale(bumpedSigs,scales,m)) bump2 = numpy.sum(iisignature.sigscale(sigs,bumpedScales,m)) derivsOfSum = numpy.ones_like(scaledSigsCalc) calculated = iisignature.sigscalebackprop(derivsOfSum,sigs,scales,m) diff1 = (bump1 - base) - numpy.sum(calculated[0] * (bumpedSigs - sigs)) diff2 = (bump2 - base) - numpy.sum(calculated[1] * (bumpedScales - scales)) #print(calculated[1].shape,bumpedScales.shape,scales.shape) #print(calculated[1][0,0],bump2,base,bumpedScales[0,0],scales[0,0]) #print (bump1,bump2,base,diff1,diff2) self.assertLess(numpy.abs(diff1),0.0000001) self.assertLess(numpy.abs(diff2),0.0000001)
def generate(M, N, h_x=0.8, h_y=0.8, scale=1., signature=False, BM=False, dim_BM=2): if BM: X = brownian(M - 1, dim_BM, time=1.) Y = brownian(N - 1, dim_BM, time=1.) else: fbm_generator_X = FBM(M - 1, h_x) fbm_generator_Y = FBM(N - 1, h_y) x = scale * fbm_generator_X.fbm() y = scale * fbm_generator_Y.fbm() X = AddTime().fit_transform([x])[0] Y = AddTime().fit_transform([y])[0] if signature: X = iisignature.sig(X, 5, 2) Y = iisignature.sig(Y, 5, 2) X0 = np.zeros_like(X[0, :].reshape(1, -1)) X0[0, 0] = 1. X = np.concatenate([X0, X]) Y = np.concatenate([X0, Y]) return X, Y
def testcombining(self): dim = 2 level = 2 siglength = iisignature.siglength(dim,level) pathLength = 20 halfPathLength=10 numberToDo=4 path = numpy.random.uniform(size=(numberToDo,pathLength,dim)) sig = iisignature.sig(path,level) sig1 = iisignature.sig(path[:,:halfPathLength],level) sig2 = iisignature.sig(path[:,(halfPathLength-1):],level) combined = iisignature.sigcombine(sig1,sig2,dim,level) self.assertLess(diff(sig,combined),0.0001) extra = numpy.random.uniform(size=(siglength,)) bumpedsig1 = 1.001 * sig1 bumpedsig2 = 1.001 * sig2 base = numpy.sum(iisignature.sigcombine(sig1,sig2,dim,level)) bump1 = numpy.sum(iisignature.sigcombine(bumpedsig1,sig2,dim,level)) bump2 = numpy.sum(iisignature.sigcombine(sig1,bumpedsig2,dim,level)) derivsOfSum = numpy.ones((numberToDo,siglength)) calculated = iisignature.sigcombinebackprop(derivsOfSum,sig1,sig2,dim,level) self.assertEqual(len(calculated), 2) diff1 = (bump1 - base) - numpy.sum(calculated[0] * (bumpedsig1 - sig1)) diff2 = (bump2 - base) - numpy.sum(calculated[1] * (bumpedsig2 - sig2)) #print ("\n",bump1,bump2,base,diff1,diff2) self.assertLess(numpy.abs(diff1),0.000001) self.assertLess(numpy.abs(diff2),0.00001)
def testFewPoints(self): # check sanity of paths with less than 3 points path1=[[4.3,0.8]] path2=numpy.array([[1,2],[2,4]]) m=4 d=2 s=iisignature.prepare(d,m,"cosx") s_a=iisignature.prepare(d,2,"cosx") length=iisignature.siglength(d,m) loglength=iisignature.logsiglength(d,m) loglength_a=iisignature.logsiglength(d,2) blankLogSig=numpy.zeros(loglength) blankLogSig_a=numpy.zeros(loglength_a) blankSig=numpy.zeros(length) self.assertLess(diff(iisignature.sig(path1,m),blankSig),0.000000001) self.assertTrue(numpy.array_equal(iisignature.sig(path1,m,2),numpy.zeros([0,length]))) self.assertLess(diff(iisignature.logsig(path1,s,"C"),blankLogSig),0.000000001) self.assertLess(diff(iisignature.logsig(path1,s,"O"),blankLogSig),0.000000001) self.assertLess(diff(iisignature.logsig(path1,s,"S"),blankLogSig),0.000000001) self.assertLess(diff(iisignature.logsig(path1,s,"X"),blankSig),0.000000001) self.assertLess(diff(iisignature.logsig(path1,s_a,"A"),blankLogSig_a),0.000000001) blankLogSig[:d]=path2[1]-path2[0] blankLogSig_a[:d]=path2[1]-path2[0] blankSig[:d]=path2[1]-path2[0] self.assertLess(diff(iisignature.logsig(path2,s,"C"),blankLogSig),0.000001) self.assertLess(diff(iisignature.logsig(path2,s,"O"),blankLogSig),0.000001) self.assertLess(diff(iisignature.logsig(path2,s,"S"),blankLogSig),0.000001) self.assertLess(diff(iisignature.logsig(path2,s,"X"),blankSig),0.000001) self.assertLess(diff(iisignature.logsig(path2,s_a,"A"),blankLogSig_a),0.000001)
def testa(self): numpy.random.seed(775) d = 3 m = 5 pathLength = 5 numberToDo = 2 paths = numpy.random.uniform(size=(numberToDo,pathLength,d)) sigs = numpy.vstack([iisignature.sig(i,m) for i in paths]) scales = numpy.random.uniform(0.5,0.97,size=(numberToDo,d)) scaledPaths = paths * scales[:,numpy.newaxis,:] scaledSigs = numpy.vstack([iisignature.sig(i,m) for i in scaledPaths]) scaledSigsCalc = iisignature.sigscale(sigs,scales,m) self.assertEqual(scaledSigs.shape,scaledSigsCalc.shape) self.assertLess(diff(scaledSigs,scaledSigsCalc),0.0000001) bumpedScales = 1.001 * scales bumpedSigs = 1.001 * sigs base = numpy.sum(scaledSigsCalc) bump1 = numpy.sum(iisignature.sigscale(bumpedSigs,scales,m)) bump2 = numpy.sum(iisignature.sigscale(sigs,bumpedScales,m)) derivsOfSum = numpy.ones_like(scaledSigsCalc) calculated = iisignature.sigscalebackprop(derivsOfSum,sigs,scales,m) diff1 = (bump1 - base) - numpy.sum(calculated[0] * (bumpedSigs - sigs)) diff2 = (bump2 - base) - numpy.sum(calculated[1] * (bumpedScales - scales)) #print(calculated[1].shape,bumpedScales.shape,scales.shape) #print(calculated[1][0,0],bump2,base,bumpedScales[0,0],scales[0,0]) #print (bump1,bump2,base,diff1,diff2) self.assertLess(numpy.abs(diff1),0.0000001) self.assertLess(numpy.abs(diff2),0.0000001)
def testFewPoints(self): # check sanity of paths with less than 3 points path1=[[4.3,0.8]] path2=numpy.array([[1,2],[2,4]]) m=4 d=2 s=iisignature.prepare(d,m,"cosx") s_a=iisignature.prepare(d,2,"cosx") length=iisignature.siglength(d,m) loglength=iisignature.logsiglength(d,m) loglength_a=iisignature.logsiglength(d,2) blankLogSig=numpy.zeros(loglength) blankLogSig_a=numpy.zeros(loglength_a) blankSig=numpy.zeros(length) self.assertLess(diff(iisignature.sig(path1,m),blankSig),0.000000001) self.assertTrue(numpy.array_equal(iisignature.sig(path1,m,2),numpy.zeros([0,length]))) self.assertLess(diff(iisignature.logsig(path1,s,"C"),blankLogSig),0.000000001) self.assertLess(diff(iisignature.logsig(path1,s,"O"),blankLogSig),0.000000001) self.assertLess(diff(iisignature.logsig(path1,s,"S"),blankLogSig),0.000000001) self.assertLess(diff(iisignature.logsig(path1,s,"X"),blankSig),0.000000001) self.assertLess(diff(iisignature.logsig(path1,s_a,"A"),blankLogSig_a),0.000000001) blankLogSig[:d]=path2[1]-path2[0] blankLogSig_a[:d]=path2[1]-path2[0] blankSig[:d]=path2[1]-path2[0] self.assertLess(diff(iisignature.logsig(path2,s,"C"),blankLogSig),0.000001) self.assertLess(diff(iisignature.logsig(path2,s,"O"),blankLogSig),0.000001) self.assertLess(diff(iisignature.logsig(path2,s,"S"),blankLogSig),0.000001) self.assertLess(diff(iisignature.logsig(path2,s,"X"),blankSig),0.000001) self.assertLess(diff(iisignature.logsig(path2,s_a,"A"),blankLogSig_a),0.000001)
def transform(self, X, y=None): # get the lengths of all pathwise expected signatures lengths = [pwES.shape[0] for pwES in X] if len(list(set(lengths))) == 1: # if all pathwise expected signatures have the same length, the signatures can be computed in batch return iisignature.sig(X, self.order) else: return [iisignature.sig(item, self.order) for item in X]
def naive_sig_kernel(x, y, depth): sigx = iisignature.sig(x, depth, 2) sigy = iisignature.sig(y, depth, 2) k_true = np.ones((len(x), len(y))) for i, sx in enumerate(sigx): for j, sy in enumerate(sigy): k_true[i + 1, j + 1] = 1. + np.dot(sigx[i], sigy[j]) return k_true
def transform(self, X, y=None): # get the lengths of all time series (across items across bags) lengths = [item.shape[0] for bag in X for item in bag] if len(list(set(lengths))) == 1: # if all time series have the same length, the signatures can be computed in batch X = [iisignature.sig(bag, self.order) for bag in X] else: X = [np.array([iisignature.sig(item, self.order) for item in bag]) for bag in X] return [x.mean(0) for x in X]
def testCumulative(self): m=3 d=2 length = 10 path=numpy.random.uniform(size=(length,d)) cumul = iisignature.sig(path,m,2) expected = numpy.array([iisignature.sig(path[:(i+1)],m) for i in range(1,length)]) self.assertTrue(numpy.allclose(expected, cumul)) path=numpy.random.uniform(size=(3,2,length,d)) cumul = iisignature.sig(path,m,2) #expected = numpy.stack([iisignature.sig(path[:,:,:(i+1)],m) for i in range(1,length)],-2) expected = numpy.rollaxis(stack([iisignature.sig(path[:,:,:(i+1)],m) for i in range(1,length)]),0,3) self.assertTrue(numpy.allclose(expected, cumul))
def testCumulative(self): m=3 d=2 length = 10 path=numpy.random.uniform(size=(length,d)) cumul = iisignature.sig(path,m,2) expected = numpy.array([iisignature.sig(path[:(i+1)],m) for i in range(1,length)]) self.assertTrue(numpy.allclose(expected, cumul)) path=numpy.random.uniform(size=(3,2,length,d)) cumul = iisignature.sig(path,m,2) #expected = numpy.stack([iisignature.sig(path[:,:,:(i+1)],m) for i in range(1,length)],-2) expected = numpy.rollaxis(stack([iisignature.sig(path[:,:,:(i+1)],m) for i in range(1,length)]),0,3) self.assertTrue(numpy.allclose(expected, cumul))
def testSig(self): #test that sigjacobian and sigbackprop compatible with sig numpy.random.seed(291) d = 3 m = 5 pathLength = 10 path = numpy.random.uniform(size=(pathLength,d)) path = numpy.cumsum(2 * (path - 0.5),0)#makes it more random-walk-ish, less like a scribble increment = 0.01 * numpy.random.uniform(size=(pathLength,d)) base_sig = iisignature.sig(path,m) target = fdDeriv(lambda x:iisignature.sig(x,m),path,increment,2, nosum=True) gradient = iisignature.sigjacobian(path,m) calculated = numpy.tensordot(increment,gradient) diffs = numpy.max(numpy.abs(calculated - target)) niceOnes = numpy.abs(calculated) > 1.e-4 niceOnes2 = numpy.abs(calculated) < numpy.abs(base_sig) diffs1 = numpy.max(numpy.abs((calculated[niceOnes2] - target[niceOnes2]) / base_sig[niceOnes2])) diffs2 = numpy.max(numpy.abs(calculated[1 - niceOnes2] - target[1 - niceOnes2])) ratioDiffs = numpy.max(numpy.abs(calculated[niceOnes] / target[niceOnes] - 1)) #numpy.set_printoptions(suppress=True,linewidth=os.popen('stty size', #'r').read().split()[1] #LINUX #numpy.set_printoptions(suppress=True,linewidth=150) #print ("") #print (path) #print #(numpy.vstack([range(len(base_sig)),base_sig,calculated,target,(calculated-target)/base_sig,calculated/target-1]).transpose()) #print (diffs, ratioDiffs, diffs1, diffs2) #print(numpy.argmax(numpy.abs(calculated[niceOnes]/target[niceOnes]-1)),numpy.argmax(numpy.abs((calculated-target)/base_sig))) self.assertLess(diffs,0.00001) self.assertLess(ratioDiffs,0.01) self.assertLess(diffs1,0.001) self.assertLess(diffs2,0.00001) #compatibility between sigbackprop and sigjacobian is strong dFdSig = numpy.random.uniform(size=(iisignature.siglength(d,m),)) backProp = iisignature.sigbackprop(dFdSig,path,m) manualCalcBackProp = numpy.dot(gradient,dFdSig) backDiffs = numpy.max(numpy.abs(backProp - manualCalcBackProp)) if 0: # to investigate the compile logic problem I used this and # (d,m,pathLength)=(1,2,2) print("") print(dFdSig) print(path) print(backProp) print(manualCalcBackProp) self.assertLess(backDiffs,0.000001)
def testSig(self): #test that sigjacobian and sigbackprop compatible with sig numpy.random.seed(291) d = 3 m = 5 pathLength = 10 path = numpy.random.uniform(size=(pathLength,d)) path = numpy.cumsum(2 * (path - 0.5),0)#makes it more random-walk-ish, less like a scribble increment = 0.01 * numpy.random.uniform(size=(pathLength,d)) base_sig = iisignature.sig(path,m) target = fdDeriv(lambda x:iisignature.sig(x,m),path,increment,2, nosum=True) gradient = iisignature.sigjacobian(path,m) calculated = numpy.tensordot(increment,gradient) diffs = numpy.max(numpy.abs(calculated - target)) niceOnes = numpy.abs(calculated) > 1.e-4 niceOnes2 = numpy.abs(calculated) < numpy.abs(base_sig) diffs1 = numpy.max(numpy.abs((calculated[niceOnes2] - target[niceOnes2]) / base_sig[niceOnes2])) diffs2 = numpy.max(numpy.abs(calculated[1 - niceOnes2] - target[1 - niceOnes2])) ratioDiffs = numpy.max(numpy.abs(calculated[niceOnes] / target[niceOnes] - 1)) #numpy.set_printoptions(suppress=True,linewidth=os.popen('stty size', #'r').read().split()[1] #LINUX #numpy.set_printoptions(suppress=True,linewidth=150) #print ("") #print (path) #print #(numpy.vstack([range(len(base_sig)),base_sig,calculated,target,(calculated-target)/base_sig,calculated/target-1]).transpose()) #print (diffs, ratioDiffs, diffs1, diffs2) #print(numpy.argmax(numpy.abs(calculated[niceOnes]/target[niceOnes]-1)),numpy.argmax(numpy.abs((calculated-target)/base_sig))) self.assertLess(diffs,0.00001) self.assertLess(ratioDiffs,0.01) self.assertLess(diffs1,0.001) self.assertLess(diffs2,0.00001) #compatibility between sigbackprop and sigjacobian is strong dFdSig = numpy.random.uniform(size=(iisignature.siglength(d,m),)) backProp = iisignature.sigbackprop(dFdSig,path,m) manualCalcBackProp = numpy.dot(gradient,dFdSig) backDiffs = numpy.max(numpy.abs(backProp - manualCalcBackProp)) if 0: # to investigate the compile logic problem I used this and # (d,m,pathLength)=(1,2,2) print("") print(dFdSig) print(path) print(backProp) print(manualCalcBackProp) self.assertLess(backDiffs,0.000001)
def testLevel1(self): m = 1 d = 2 path = numpy.random.uniform(size=(10, d)) rightSig = path[-1, :] - path[0, :] s = iisignature.prepare(d, m, "cosx2") self.assertLess(diff(iisignature.sig(path, m), rightSig), 0.0000001) for type_ in ("C", "O", "S", "X", "A"): self.assertLess(diff(iisignature.logsig(path, s, type_), rightSig), 0.0000001, type_) self.assertLess(diff(rightSig, iisignature.logsigtosig(rightSig, s)), 0.000001) derivs = numpy.array([2.1, 3.2]) pathderivs = numpy.zeros_like(path) pathderivs[-1] = derivs pathderivs[0] = -derivs self.assertLess( diff(iisignature.logsigbackprop(derivs, path, s), pathderivs), 0.00001) self.assertLess( diff(iisignature.logsigbackprop(derivs, path, s, "X"), pathderivs), 0.00001) self.assertLess( diff(iisignature.sigbackprop(derivs, path, m), pathderivs), 0.00001)
def scale_path(x, M, a, level_sig): ''' This function computes a single scaling factor \theta_x (path-dependent) and rescales the path x , i.e. return x_new: t -> \theta_x*x_t, such that ||S_{<level_sig}(x_new)|| < M(1+1/a). Inputs: - x: an array (L,D) representing a path where L is the length and D is the state-space dimension - (int) level_sig: the truncation level to compute the norm of S_{<level_sig}(x) - (int) M: the first parameter used to define the maximum norm allowed - (float) a: the second parameter used to define the maximum norm allowed Outputs: - x_new: the rescaled path ''' D = x.shape[1] maxi = M * (1. + 1. / a) sig = iisignature.sig(x, level_sig) # computes the signature of the path x norm_levels = get_norm_level_sig( sig, D, level_sig ) # gets the norm of each tensor in S(x) truncated at level level_sig norm = np.sum( norm_levels) # gets the norm of S(x) truncated at level level_sig psi = psi_tilde( norm, M, a) # computes an intermediary number which is used to find the scale theta_x = brentq(poly, 0, 10000, args=(psi, norm_levels, level_sig)) # computes the scale return theta_x * x
def signature_of_path(path,upto_level): """Convenience method for calculating the signature of a path using iisignature. Parameters ---------- path : numpy.array A (T,d) dimensional array, with `d` the dimnsion of the path and `T` the number of time-steps. upto_level: int Compute the signature up-to (and including) this level. Returns ------- signature : A `LinearCombination` of `ConcatenationWord`s storing the signature as its coefficients. """ import iisignature d = np.shape(path)[-1] s = iisignature.sig(path,upto_level,1) letters = range(1,d+1) def signature_generator(): yield (words.ConcatenationWord(), 1) for lev in range(1,upto_level+1): for word, val in zip( itertools.product(letters,repeat=lev), s[lev-1]): yield (words.ConcatenationWord(word), val) return lc.LinearCombination.from_generator( signature_generator() )
def get_sigX(X, k): """Returns a matrix containing signatures truncated at k of n samples given in the input tensor X. Parameters ---------- X: array, shape (n, npoints, d) A 3-dimensional array, containing the coordinates in R^d of n piecewise linear paths, each composed of n_points. k: int Truncation order of the signature Returns ------- sigX: array, shape (n,p) A matrix containing in each row the signature truncated at k of a sample. """ if k == 0: return np.full((np.shape(X)[0], 1), 1) else: d = X.shape[2] sigX = np.zeros((np.shape(X)[0], isig.siglength(d, k) + 1)) sigX[:, 0] = 1 for i in range(np.shape(X)[0]): sigX[i, 1:] = isig.sig(X[i, :, :], k) return sigX
def forward(ctx, path, depth): ctx.path = path.detach().cpu() ctx.depth = depth ctx.device = path.device ctx.dtype = path.dtype return torch.tensor(iisignature.sig(ctx.path, ctx.depth), device=ctx.device, dtype=ctx.dtype)
def preprocess(self, X): """ Preprocess training/testing data using signatures. """ data = [sig(self.transform(x), self.level) for x in X] if self.scale: data = preprocessing.scale(data) return data
def consistency(self, coropa, dim, level): #numpy.random.seed(21) s = iisignature.prepare(dim,level,"coshx" if coropa else "cosx") myinfo = {"level":level, "dimension":dim, "methods": ("COSAX" if level <= 2 else "COSX"), "basis":("Standard Hall" if coropa else "Lyndon")} self.assertEqual(iisignature.info(s),myinfo) path = numpy.random.uniform(size=(10,dim)) basis = iisignature.basis(s) logsig = iisignature.logsig(path,s) sig = iisignature.sig(path,level) #check lengths self.assertEqual(len(basis),iisignature.logsiglength(dim,level)) self.assertEqual((len(basis),),logsig.shape) self.assertEqual(sig.shape,(iisignature.siglength(dim,level),)) #calculate a signature from logsig expanded_logsig = [numpy.zeros(dim ** m) for m in range(1,level + 1)] for coeff, expression in zip(logsig,basis): values, depth = valueOfBracket(expression,dim) expanded_logsig[depth - 1]+=values * coeff calculated_sig = numpy.concatenate(exponentiateTensor(expanded_logsig)) self.assertLess(diff(sig,calculated_sig),0.00001) #calculate a log signature from sig fullLogSig = numpy.concatenate(logTensor(splitConcatenatedTensor(sig,dim,level))) fullLogSigLib = iisignature.logsig(path,s,"x") diff1 = numpy.max(numpy.abs(fullLogSigLib - fullLogSig)) #print #(numpy.vstack([fullLogSig,fullLogSigLib,numpy.abs(fullLogSigLib-fullLogSig)]).transpose()) self.assertLess(diff1,0.00001) basisMatrix = [] zeros = [numpy.zeros(dim ** m) for m in range(1,level + 1)] for expression in basis: values, depth = valueOfBracket(expression, dim) temp = zeros[depth - 1] zeros[depth - 1] = values basisMatrix.append(numpy.concatenate(zeros)) zeros[depth - 1] = temp calculatedLogSig = lstsq(numpy.transpose(basisMatrix),fullLogSig)[0] diff2 = numpy.max(numpy.abs(logsig - calculatedLogSig)) self.assertLess(diff2,0.00001) #check consistency of methods slowLogSig = iisignature.logsig(path,s,"o") diffs = numpy.max(numpy.abs(slowLogSig - calculatedLogSig)) self.assertLess(diffs,0.00001) sigLogSig = iisignature.logsig(path,s,"s") diffs = numpy.max(numpy.abs(sigLogSig - calculatedLogSig)) self.assertLess(diffs,0.00001) if level < 3: areaLogSig = iisignature.logsig(path,s,"a") diffs = numpy.max(numpy.abs(areaLogSig - calculatedLogSig)) self.assertLess(diffs,0.00001)
def consistency(self, coropa, dim, level): #numpy.random.seed(21) s = iisignature.prepare(dim,level,"coshx" if coropa else "cosx") myinfo = {"level":level, "dimension":dim, "methods": ("COSAX" if level <= 2 else "COSX"), "basis":("Standard Hall" if coropa else "Lyndon")} self.assertEqual(iisignature.info(s),myinfo) path = numpy.random.uniform(size=(10,dim)) basis = iisignature.basis(s) logsig = iisignature.logsig(path,s) sig = iisignature.sig(path,level) #check lengths self.assertEqual(len(basis),iisignature.logsiglength(dim,level)) self.assertEqual((len(basis),),logsig.shape) self.assertEqual(sig.shape,(iisignature.siglength(dim,level),)) #calculate a signature from logsig expanded_logsig = [numpy.zeros(dim ** m) for m in range(1,level + 1)] for coeff, expression in zip(logsig,basis): values, depth = valueOfBracket(expression,dim) expanded_logsig[depth - 1]+=values * coeff calculated_sig = numpy.concatenate(exponentiateTensor(expanded_logsig)) self.assertLess(diff(sig,calculated_sig),0.00001) #calculate a log signature from sig fullLogSig = numpy.concatenate(logTensor(splitConcatenatedTensor(sig,dim,level))) fullLogSigLib = iisignature.logsig(path,s,"x") diff1 = numpy.max(numpy.abs(fullLogSigLib - fullLogSig)) #print #(numpy.vstack([fullLogSig,fullLogSigLib,numpy.abs(fullLogSigLib-fullLogSig)]).transpose()) self.assertLess(diff1,0.00001) basisMatrix = [] zeros = [numpy.zeros(dim ** m) for m in range(1,level + 1)] for expression in basis: values, depth = valueOfBracket(expression, dim) temp = zeros[depth - 1] zeros[depth - 1] = values basisMatrix.append(numpy.concatenate(zeros)) zeros[depth - 1] = temp calculatedLogSig = lstsq(numpy.transpose(basisMatrix),fullLogSig)[0] diff2 = numpy.max(numpy.abs(logsig - calculatedLogSig)) self.assertLess(diff2,0.00001) #check consistency of methods slowLogSig = iisignature.logsig(path,s,"o") diffs = numpy.max(numpy.abs(slowLogSig - calculatedLogSig)) self.assertLess(diffs,0.00001) sigLogSig = iisignature.logsig(path,s,"s") diffs = numpy.max(numpy.abs(sigLogSig - calculatedLogSig)) self.assertLess(diffs,0.00001) if level < 3: areaLogSig = iisignature.logsig(path,s,"a") diffs = numpy.max(numpy.abs(areaLogSig - calculatedLogSig)) self.assertLess(diffs,0.00001)
def signature(path, depth): """return signature of a path up to level depth""" assert isinstance(depth, int), 'Argument of wrong type' assert depth > 1, 'depth must be > 1' width = path.shape[1] length = path.shape[0] if length <= 1: return np.array([0.]*(tosig.sigdim(width, depth)-1)) return iisignature.sig(path, depth)
def forward(ctx, path, depth): device = path.device # transpose because the PyTorch convention for convolutions is channels first. The iisignature expectation is # that channels are last. path = path.detach().cpu().numpy().transpose() # sloooow CPU :( ctx.path = path ctx.depth = depth return torch.tensor(iisignature.sig(path, depth), dtype=torch.float, device=device)
def treeEsig(tree, truncation): """Function computing the (truncated) Expected Signature of a tree""" w, path = tree['value'] path_dim = path.shape[1] forest = tree['forest'] ES = iisignature.sig(path, truncation) if forest: ES_forest = sum([treeEsig(t, truncation) for t in forest]) ES = iisignature.sigcombine(ES, ES_forest, path_dim, truncation) return w * ES
def _get_tree_reduced_steps(X, order=4, steps=4, tol=0.1): if len(X) < steps: return X dim = X.shape[1] for i in range(steps - 1, len(X)): new_path = X[i - steps + 1:i + 1] new_path2 = np.r_[X[i - steps + 1].reshape(-1, dim), X[i].reshape(-1, dim)] new_path_sig = iisignature.sig(new_path, order) new_path2_sig = iisignature.sig(new_path2, order) norm = np.linalg.norm(new_path_sig - new_path2_sig) if norm < tol: return _get_tree_reduced_steps(np.r_[X[:i - steps + 2], X[i:]]) return X
def signature_matrix(self, market_info): # compute the signature matrix X_ts = self.construct_multi_feature_time_series(self.trend) sig_m = [] stream = [] for i in range(X_ts.shape[0]): stream = list(enumerate(X_ts[i])) path = self.leadlag(stream) sig = iisignature.sig(path, 3) # change the dimension of signature sig = sig.tolist() sig_m.append(sig) return sig_m
def f(path): path = transform(path) if N == 0: sig = np.array([1.]) elif N == 1: sig = np.array( [1., path[-1, 0] - path[0, 0], path[-1, 1] - path[0, 1]]) else: sig = np.r_[1., iisignature.sig(path, N)] return sig.dot(l)
def transform(self, X, y=None): pwES = [] for bag in X: # get the lengths of all time series in the bag lengths = [item.shape[0] for item in bag] if len(list(set(lengths))) == 1: # if all time series have the same length, the (pathwise) signatures can be computed in batch pwES.append(iisignature.sig(bag, self.order, 2)) else: error_message = 'All time series in a bag must have the same length' raise NameError(error_message) return [x.mean(0) for x in pwES]
def preprocess_data(x_train, x_test, flag=None): """Peforms model-dependent preprocessing.""" if flag == 'neuralsig': # We don't need to backprop through the signature if we're just building a model on top # so we actually perform the signature here as a feature transformation, rather than in # the model. path_transform = AddTime() x_train = np.array([ iisignature.sig(x, 4) for x in path_transform.fit_transform(x_train) ]) x_test = np.array([ iisignature.sig(x, 4) for x in path_transform.fit_transform(x_test) ]) elif flag == 'lstm': # LSTM wants another dimension in one place... x_train = np.expand_dims(x_train, 2) x_test = np.expand_dims(x_test, 2) else: # ...everyone else wants the extra dimension in another x_train = np.expand_dims(x_train, 1) x_test = np.expand_dims(x_test, 1) return x_train, x_test
def testLevel1(self): m=1 d=2 path=numpy.random.uniform(size=(10,d)) rightSig = path[-1,:]-path[0,:] s=iisignature.prepare(d,m,"cosx") self.assertLess(diff(iisignature.sig(path,m),rightSig),0.0000001) for type_ in ("C","O","S","X","A"): self.assertLess(diff(iisignature.logsig(path,s,type_),rightSig),0.0000001,type_) derivs=numpy.array([2.1,3.2]) pathderivs=numpy.zeros_like(path) pathderivs[-1]=derivs pathderivs[0]=-derivs self.assertLess(diff(iisignature.logsigbackprop(derivs,path,s),pathderivs),0.00001) self.assertLess(diff(iisignature.logsigbackprop(derivs,path,s,"X"),pathderivs),0.00001) self.assertLess(diff(iisignature.sigbackprop(derivs,path,m),pathderivs),0.00001)
def testjoining(self): numberToDo = 1 dim = 2 level = 2 siglength = iisignature.siglength(dim,level) for fixedPoint, inputDim, fixed in [(float('nan'),dim,False),(0.1,dim - 1,True)]: pathLength = 10 def makePath(): p = numpy.random.uniform(size=(pathLength,dim)) if fixed: p[:,-1] = fixedPoint * numpy.arange(pathLength) return p paths = [makePath() for i in range(numberToDo)] sig = numpy.vstack([iisignature.sig(path,level) for path in paths]) joinee = numpy.zeros((numberToDo,siglength)) for i in range(1,pathLength): displacements = [path[i:(i + 1),:] - path[(i - 1):i,:] for path in paths] displacement = numpy.vstack(displacements) if fixed: displacement = displacement[:,:-1] joinee = iisignature.sigjoin(joinee,displacement,level,fixedPoint) self.assertLess(diff(sig,joinee),0.0001,"fullSig matches sig" + (" with fixed Dim" if fixed else "")) extra = numpy.random.uniform(size=(numberToDo,inputDim)) bumpedExtra = 1.001 * extra bumpedJoinee = 1.001 * joinee base = numpy.sum(iisignature.sigjoin(joinee,extra,level,fixedPoint)) bump1 = numpy.sum(iisignature.sigjoin(bumpedJoinee,extra,level,fixedPoint)) bump2 = numpy.sum(iisignature.sigjoin(joinee,bumpedExtra,level,fixedPoint)) derivsOfSum = numpy.ones((numberToDo,siglength)) calculated = iisignature.sigjoinbackprop(derivsOfSum,joinee,extra, level,fixedPoint) self.assertEqual(len(calculated),3 if fixed else 2) diff1 = (bump1 - base) - numpy.sum(calculated[0] * (bumpedJoinee - joinee)) diff2 = (bump2 - base) - numpy.sum(calculated[1] * (bumpedExtra - extra)) #print ("\n",bump1,bump2,base,diff1,diff2) self.assertLess(numpy.abs(diff1),0.000001,"diff1 as expected " + (" with fixed Dim" if fixed else "")) self.assertLess(numpy.abs(diff2),0.00001,"diff2 as expected " + (" with fixed Dim" if fixed else "")) if fixed: bumpedFixedPoint = fixedPoint * 1.01 bump3 = numpy.sum(iisignature.sigjoin(joinee,extra, level, bumpedFixedPoint)) diff3 = (bump3-base - numpy.sum(calculated[2] * (bumpedFixedPoint-fixedPoint))) #print("\n",bump3,base, fixedPoint, bumpedFixedPoint, calculated[2]) self.assertLess(numpy.abs(diff3),0.00001, "diff3")
def testjoining(self): numberToDo = 1 dim = 2 level = 2 siglength = iisignature.siglength(dim,level) for fixedPoint, inputDim, fixed in [(float('nan'),dim,False),(0.1,dim - 1,True)]: pathLength = 10 def makePath(): p = numpy.random.uniform(size=(pathLength,dim)) if fixed: p[:,-1] = fixedPoint * numpy.arange(pathLength) return p paths = [makePath() for i in range(numberToDo)] sig = numpy.vstack([iisignature.sig(path,level) for path in paths]) joinee = numpy.zeros((numberToDo,siglength)) for i in range(1,pathLength): displacements = [path[i:(i + 1),:] - path[(i - 1):i,:] for path in paths] displacement = numpy.vstack(displacements) if fixed: displacement = displacement[:,:-1] joinee = iisignature.sigjoin(joinee,displacement,level,fixedPoint) self.assertLess(diff(sig,joinee),0.0001,"fullSig matches sig" + (" with fixed Dim" if fixed else "")) extra = numpy.random.uniform(size=(numberToDo,inputDim)) bumpedExtra = 1.001 * extra bumpedJoinee = 1.001 * joinee base = numpy.sum(iisignature.sigjoin(joinee,extra,level,fixedPoint)) bump1 = numpy.sum(iisignature.sigjoin(bumpedJoinee,extra,level,fixedPoint)) bump2 = numpy.sum(iisignature.sigjoin(joinee,bumpedExtra,level,fixedPoint)) derivsOfSum = numpy.ones((numberToDo,siglength)) calculated = iisignature.sigjoinbackprop(derivsOfSum,joinee,extra, level,fixedPoint) self.assertEqual(len(calculated),3 if fixed else 2) diff1 = (bump1 - base) - numpy.sum(calculated[0] * (bumpedJoinee - joinee)) diff2 = (bump2 - base) - numpy.sum(calculated[1] * (bumpedExtra - extra)) #print ("\n",bump1,bump2,base,diff1,diff2) self.assertLess(numpy.abs(diff1),0.000001,"diff1 as expected " + (" with fixed Dim" if fixed else "")) self.assertLess(numpy.abs(diff2),0.00001,"diff2 as expected " + (" with fixed Dim" if fixed else "")) if fixed: bumpedFixedPoint = fixedPoint * 1.01 bump3 = numpy.sum(iisignature.sigjoin(joinee,extra, level, bumpedFixedPoint)) diff3 = (bump3-base - numpy.sum(calculated[2] * (bumpedFixedPoint-fixedPoint))) #print("\n",bump3,base, fixedPoint, bumpedFixedPoint, calculated[2]) self.assertLess(numpy.abs(diff3),0.00001, "diff3")
def path_to_dyadic_sig(self, file, dyadic_level): """Read one sample and output a vector containing a concatenation of signature coefficients. The path is divided into 2^dyadic_levelsubpaths and a signature vector is computed on each subpath. All vectors obtained in this way are then concatenated. Parameters ---------- file: str - If data='quick_draw', it is the string containing the raw drawing coordinates. - If data='urban_sound' or 'motion_sense', it is the path to the sample file. dyadic_level: int It is the level of dyadic partitions considered. The path is divided into 2^dyadic_level subpaths and signatures are computed on each subpath. Returns ------- sig: array, shape (p) A signature vector containing all signature coefficients. It is of shape p=2^dyadic_level*self.get_sig_dimension(). """ path = self.data_to_path(file) n_points = np.shape(path)[0] n_subpaths = 2**dyadic_level window_size = n_points // n_subpaths if n_subpaths > n_points: path = np.vstack( (path, np.zeros((n_subpaths - n_points, np.shape(path)[1])))) window_size = 1 siglength = self.get_sig_dimension() sig = np.zeros(n_subpaths * siglength) for i in range(n_subpaths): if i == n_subpaths - 1: subpath = path[i * window_size:, :] else: subpath = path[i * window_size:(i + 1) * window_size, :] sig[i * siglength:(i + 1) * siglength] = isig.sig( subpath, self.order) return (sig)
def doL2S(self, coropa): numpy.random.seed(212) d = 3 m = 6 path = numpy.random.uniform(size=(12, d)) sig = iisignature.sig(path, m) s = iisignature.prepare(d, m, "S2H" if coropa else "S2") self.assertTrue(iisignature.info(s)["logsigtosig_supported"]) logsig = iisignature.logsig(path, s) sig_ = iisignature.logsigtosig(logsig, s) self.assertEqual(sig.shape, sig_.shape) self.assertTrue(numpy.allclose(sig, sig_)) #Like the other iisig functions, we check that derivatives #of logsigtosig allow sum(logsigtosig) to be backproped correctly #This is a boring thing to calculate, because after the first level #each of the log signature elements is a lie bracket and so #contributes a net total of 0 to the signature derivsOfSum = numpy.ones((sig.shape[0], ), dtype="float64") bumpedLogSig = 1.01 * logsig calculated = iisignature.logsigtosigbackprop(derivsOfSum, logsig, s) #wantedbackprop = allSensitivities(logsig, lambda l: iisignature.logsigtosig(l,s).sum()) manualChange = fdDeriv(lambda x: iisignature.logsigtosig(x, s), logsig, bumpedLogSig - logsig, 6) calculatedChange = numpy.sum((bumpedLogSig - logsig) * calculated) self.assertLess(numpy.abs(manualChange - calculatedChange), 0.00001) #beyond the first level, all zero if m > 1: self.assertLess(numpy.max(numpy.abs(calculated[d:])), 0.00001) self.assertEqual(calculated.shape, logsig.shape) #Now for a better test, we backprop sum(random*logsigtosig) #specifically calculate the change in it caused by bump two ways random = numpy.random.uniform(size=sig.shape[0], ) derivsOfSum = random calculated = iisignature.logsigtosigbackprop(derivsOfSum, logsig, s) manualChange = fdDeriv( lambda x: iisignature.logsigtosig(x, s) * random, logsig, bumpedLogSig - logsig, 4) calculatedChange = numpy.sum((bumpedLogSig - logsig) * calculated) self.assertLess(numpy.abs(manualChange - calculatedChange), 0.00001) self.assertEqual(calculated.shape, logsig.shape)
def testa(self): numpy.random.seed(2141) d = 5 m = 5 pathLength = 4 displacement = numpy.random.uniform(size=(d)) sig,mults = sigOfSegment(displacement,m) path = [numpy.zeros(d),displacement] for x in range(pathLength): displacement1 = numpy.random.uniform(size=(d)) path.append(path[-1] + displacement1) sig1, mults1 = sigOfSegment(displacement1, m) sig, mults2 = chen(sig,sig1) mults+=mults1 + mults2 #print (path) path = numpy.vstack(path) isig = iisignature.sig(path,m,1) for i in range(m): self.assertLess(diff(isig[i],sig[i]),0.00001) self.assertEqual(mults,iisignature.sigmultcount(path,m))
def testa(self): numpy.random.seed(2141) d = 5 m = 5 pathLength = 4 displacement = numpy.random.uniform(size=(d)) sig,mults = sigOfSegment(displacement,m) path = [numpy.zeros(d),displacement] for x in range(pathLength): displacement1 = numpy.random.uniform(size=(d)) path.append(path[-1] + displacement1) sig1, mults1 = sigOfSegment(displacement1, m) sig, mults2 = chen(sig,sig1) mults+=mults1 + mults2 #print (path) path = numpy.vstack(path) isig = iisignature.sig(path,m,1) for i in range(m): self.assertLess(diff(isig[i],sig[i]),0.00001) self.assertEqual(mults,iisignature.sigmultcount(path,m))
def path_to_sig(self, file): """Read one sample and output its signature with the embedding selected by self.embedding. Parameters ---------- file: str - If data='quick_draw', it is the string containing the raw drawing coordinates. - If data='urban_sound' or 'motion_sense', it is the path to the sample file. Returns ------- sig: array, shape (p) Array containing signature coefficients computed on the embedded path of the sample corresponding to file. """ path = self.data_to_path(file) sig = isig.sig(path, self.order) return (sig)
def test_batch(self): numpy.random.seed(734) d=2 m=2 n=15 paths = [numpy.random.uniform(-1,1,size=(6,d)) for i in range(n)] pathArray15=stack(paths) pathArray1315=numpy.reshape(pathArray15,(1,3,1,5,6,d)) sigs = [iisignature.sig(i,m) for i in paths] sigArray=stack(sigs) sigArray15=iisignature.sig(pathArray15,m) sigArray1315=iisignature.sig(pathArray1315,m) siglength=iisignature.siglength(d,m) self.assertEqual(sigArray1315.shape,(1,3,1,5,siglength)) self.assertTrue(numpy.allclose(sigArray1315.reshape(n,siglength),sigs)) self.assertEqual(sigArray15.shape,(15,siglength)) self.assertTrue(numpy.allclose(sigArray15,sigs)) backsigs=[iisignature.sigbackprop(i,j,m) for i,j in zip(sigs,paths)] backsigArray = stack(backsigs) backsigs1315=iisignature.sigbackprop(sigArray1315,pathArray1315,m) self.assertEqual(backsigs1315.shape,(1,3,1,5,6,d)) self.assertTrue(numpy.allclose(backsigs1315.reshape(n,6,2),backsigArray)) data=[numpy.random.uniform(size=(d,)) for i in range(n)] dataArray1315=stack(data).reshape((1,3,1,5,d)) joined=[iisignature.sigjoin(i,j,m) for i,j in zip(sigs,data)] joined1315=iisignature.sigjoin(sigArray1315,dataArray1315,m) self.assertEqual(joined1315.shape,(1,3,1,5,siglength)) self.assertTrue(numpy.allclose(joined1315.reshape(n,-1),stack(joined))) backjoined=[iisignature.sigjoinbackprop(i,j,k,m) for i,j,k in zip(joined,sigs,data)] backjoinedArrays=[stack([i[j] for i in backjoined]) for j in range(2)] backjoined1315=iisignature.sigjoinbackprop(joined1315,sigArray1315,dataArray1315,m) self.assertEqual(backjoined1315[0].shape,sigArray1315.shape) self.assertEqual(backjoined1315[1].shape,dataArray1315.shape) self.assertTrue(numpy.allclose(backjoined1315[0].reshape(n,-1),backjoinedArrays[0])) self.assertTrue(numpy.allclose(backjoined1315[1].reshape(n,-1),backjoinedArrays[1])) scaled=[iisignature.sigscale(i,j,m) for i,j in zip(sigs,data)] scaled1315=iisignature.sigscale(sigArray1315,dataArray1315,m) self.assertEqual(scaled1315.shape,(1,3,1,5,siglength)) self.assertTrue(numpy.allclose(scaled1315.reshape(n,-1),stack(scaled))) backscaled=[iisignature.sigscalebackprop(i,j,k,m) for i,j,k in zip(scaled,sigs,data)] backscaledArrays=[stack([i[j] for i in backscaled]) for j in range(2)] backscaled1315=iisignature.sigscalebackprop(scaled1315,sigArray1315,dataArray1315,m) self.assertEqual(backscaled1315[0].shape,sigArray1315.shape) self.assertEqual(backscaled1315[1].shape,dataArray1315.shape) self.assertTrue(numpy.allclose(backscaled1315[0].reshape(n,-1),backscaledArrays[0])) self.assertTrue(numpy.allclose(backscaled1315[1].reshape(n,-1),backscaledArrays[1])) s_s=(iisignature.prepare(d,m,"cosax"),iisignature.prepare(d,m,"cosahx")) for type in ("c","o","s","x","a","ch","oh","sh","ah"): s=s_s[1 if "h" in type else 0] logsigs = [iisignature.logsig(i,s,type) for i in paths] logsigArray=stack(logsigs) logsigArray1315=iisignature.logsig(pathArray1315,s,type) self.assertEqual(logsigArray1315.shape,(1,3,1,5,logsigs[0].shape[0]),type) self.assertTrue(numpy.allclose(logsigArray1315.reshape(n,-1),logsigArray),type) if type in ("s","x","sh"): backlogs = stack(iisignature.logsigbackprop(i,j,s,type) for i,j in zip(logsigs,paths)) backlogs1315 = iisignature.logsigbackprop(logsigArray1315,pathArray1315,s,type) self.assertEqual(backlogs1315.shape,backsigs1315.shape) self.assertTrue(numpy.allclose(backlogs1315.reshape(n,6,d),backlogs),type) a=iisignature.rotinv2dprepare(m,"a") rots=stack([iisignature.rotinv2d(i,a) for i in paths]) rots1315=iisignature.rotinv2d(pathArray1315,a) self.assertEqual(rots1315.shape,(1,3,1,5,rots.shape[1])) self.assertTrue(numpy.allclose(rots1315.reshape(n,-1),rots))
def dotest(self,type): m = 8 nPaths = 95 nAngles = 348 numpy.random.seed(775) s = iisignature.rotinv2dprepare(m,type) coeffs = iisignature.rotinv2dcoeffs(s) angles = numpy.random.uniform(0,math.pi * 2,size=nAngles + 1) angles[0] = 0 rotationMatrices = [numpy.array([[math.cos(i),math.sin(i)],[-math.sin(i),math.cos(i)]]) for i in angles] paths = [numpy.random.uniform(-1,1,size=(32,2)) for i in range(nPaths)] samePathRotInvs = [iisignature.rotinv2d(numpy.dot(paths[0],mtx),s) for mtx in rotationMatrices] #check the length matches (length,) = samePathRotInvs[0].shape self.assertEqual(length,sum(i.shape[0] for i in coeffs)) self.assertEqual(length,iisignature.rotinv2dlength(s)) if type == "a": self.assertEqual(length,sumCentralBinomialCoefficient(m // 2)) self.assertLess(length,nAngles)#sanity check on the test itself #check that the invariants are invariant if 0: print("\n",numpy.column_stack(samePathRotInvs[0:7])) for i in range(nAngles): if 0 and diff(samePathRotInvs[0],samePathRotInvs[1 + i]) > 0.01: print(i) print(samePathRotInvs[0] - samePathRotInvs[1 + i]) print(diff(samePathRotInvs[0],samePathRotInvs[1 + i])) self.assertLess(diff(samePathRotInvs[0],samePathRotInvs[1 + i]),0.01) #check that the invariants match the coefficients if 1: sigLevel=iisignature.sig(paths[0],m)[iisignature.siglength(2,m-1):] lowerRotinvs = 0 if 2==m else iisignature.rotinv2dlength(iisignature.rotinv2dprepare(m-2,type)) #print("\n",numpy.dot(coeffs[-1],sigLevel),"\n",samePathRotInvs[0][lowerRotinvs:]) #print(numpy.dot(coeffs[-1],sigLevel)-samePathRotInvs[0][lowerRotinvs:]) self.assertTrue(numpy.allclose(numpy.dot(coeffs[-1],sigLevel),samePathRotInvs[0][lowerRotinvs:],atol=0.000001)) #check that we are not missing invariants if type == "a": #print("\nrotinvlength=",length," #siglength=",iisignature.siglength(2,m)) sigOffsets = [] for path in paths: samePathSigs = [iisignature.sig(numpy.dot(path,mtx),m) for mtx in rotationMatrices[1:70]] samePathSigsOffsets = [i - samePathSigs[0] for i in samePathSigs[1:]] sigOffsets.extend(samePathSigsOffsets) #print(numpy.linalg.svd(numpy.row_stack(sigOffsets))[1]) def split(a, dim, level): start = 0 out = [] for m in range(1,level + 1): levelLength = dim ** m out.append(a[:,start:(start + levelLength)]) start = start + levelLength assert(start == a.shape[1]) return out allOffsets = numpy.row_stack(sigOffsets) #print (allOffsets.shape) splits = split(allOffsets,2,m) #print() rank_tolerance = 0.01 # this is hackish #print #([numpy.linalg.matrix_rank(i.astype("float64"),rank_tolerance) #for #i in splits]) #print ([i.shape for i in splits]) #print(numpy.linalg.svd(splits[-1])[1]) #sanity check on the test self.assertLess(splits[-1].shape[1],splits[0].shape[0]) totalUnspannedDimensions = sum(i.shape[1] - numpy.linalg.matrix_rank(i,rank_tolerance) for i in splits) self.assertEqual(totalUnspannedDimensions,length) if 0: #This doesn't work - the rank of the whole thing is less than #sigLength-totalUnspannedDimensions, which suggests that there are #inter-level dependencies, #even though the shuffle product dependencies aren't linear. #I don't know why this is. sigLength = iisignature.siglength(2,m) numNonInvariant = numpy.linalg.matrix_rank(numpy.row_stack(sigOffsets)) predictedNumberInvariant = sigLength - numNonInvariant print(sigLength,length,numNonInvariant) self.assertLess(sigLength,nAngles) self.assertEqual(predictedNumberInvariant,length)
from iisignature_theano import LogSig, Sig #1: SETUP VARIABLES dim=2 level=3 pathlength=4 timedim=False useLogSig = True s=iisignature.prepare(dim,level) numpy.random.seed(51) target = numpy.random.uniform(size=(pathlength,dim)).astype("float32") #target = numpy.cumsum(2*(target-0.5),0)#makes it more random-walk-ish, less like a scribble targetSig = iisignature.logsig(target,s) if useLogSig else iisignature.sig(target,level) start = numpy.random.uniform(size=(pathlength,dim)).astype("float32") start[0,:] = target[0,:] learnable_mask = numpy.ones((pathlength,dim)).astype("float32") learnable_mask[0,:]=0 #to stop the starting point being modified if timedim: for i in six.moves.xrange(pathlength): target[i,0]=start[i,0]=i*0.2 learnable_mask[i,0]=0 learning_rate_decay = 1.003 initial_learning_rate = 1.2 momentum = 0.95 #2: DEFINE THEANO STUFF learning_rate = theano.shared(numpy.float32(initial_learning_rate),"learning_rate")
def forward(self,X): result=iisignature.sig(X.numpy(), self.m) self.save_for_backward(X) return torch.FloatTensor(result)
def perform(self,node,inputs_storage,outputs_storage): x=inputs_storage[0] m=inputs_storage[1] outputs_storage[0][0]=iisignature.sig(x,m)
#does not grow. Even a tiny memory leak becomes very visible e.g. in htop. #add the parent directory, so we find our iisignature build if it was built --inplace sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))) import iisignature length = 20 dim=3 level=2 npaths=3 paths_ = np.random.uniform(size=(npaths,length,dim)) scale_ = np.random.uniform(size=(npaths,dim)) initialsigs_ = np.random.uniform(size=(npaths,iisignature.siglength(dim,level))) p=iisignature.prepare(dim,level,"cosx") while 0: iisignature.sig(paths[0],level) for i in range(10**10): #copy major parts of the input data, in case we are leaking references to it paths=paths_[:] increment=scale=scale_[:] initialsigs=initialsigs_[:] iisignature.sigjoin(initialsigs,scale,level) iisignature.sigscale(initialsigs,scale,level) iisignature.sigjoinbackprop(initialsigs,initialsigs,scale,level) iisignature.sigscalebackprop(initialsigs,initialsigs,scale,level) iisignature.sig(paths[0,:,:],level) iisignature.sigbackprop(initialsigs[0,:],paths[0,:,:],level) #iisignature.sigjacobian(paths[0,:,:],level) #iisignature.prepare(dim,level,"cosx")#much slower than other functions iisignature.logsig(paths[0,:,:],p,"c") iisignature.logsig(paths[0,:,:],p,"o")
def _sigImp(x,m): return iisignature.sig(x,m)
def stream2sig(path, m): return np.hstack([1.0, iisignature.sig(path,m)])
total=[[(0,0)]] for i in range(1,len(seq)): s={seq[i-1],seq[i]} if s in [{0,1},{2,3}]: raise RuntimeError("treelike") for i in seq: offset=np.array([0,0])#change this to [2,0] to display petals separately startpoint=total[-1][-1]+offset if i in [0,1,2,3]: total.append(startpoint+petals[i]) else: raise "whoops" path=np.vstack(total) print("Maximum absolute value of sig elements in each level:") for i,j in enumerate(iisignature.sig(path,14,1)): print("level {:2}:".format(i+1),np.max(np.abs(j))) if 0: import matplotlib.pyplot as plt import matplotlib as mpl #colors = np.arange(path. #plt.plot(path[:,0],path[:,1]) plt.scatter(path[:,0],path[:,1],c=np.arange(path.shape[0]), norm=mpl.colors.Normalize(vmin=0,vmax=path.shape[0])) plt.show() #A sort of Thue Morse chain of fig eights