(self)
This function implements the sub path expansion, i.e. 
        for a four-gram a,b,c,d, the paths a->b, b->c, c->d of 
        length one and the paths a->b->c and b->c->d of length 
        two will be counted.
This function implements the sub path expansion, i.e. 
        for a four-gram a,b,c,d, the paths a->b, b->c, c->d of 
        length one and the paths a->b->c and b->c->d of length 
        two will be counted.
This function implements the sub path expansion i . e . for a four - gram a b c d the paths a - > b b - > c c - > d of length one and the paths a - > b - > c and b - > c - > d of length two will be counted .
def expandSubPaths(self):
        """
        This function implements the sub path expansion, i.e. 
        for a four-gram a,b,c,d, the paths a->b, b->c, c->d of 
        length one and the paths a->b->c and b->c->d of length 
        two will be counted.
        """             

        # nothing to see here ... 
        if len(self.paths)==0:
            return

        Log.add('Calculating sub path statistics ... ')

        # the expansion of all subpaths in paths with a maximum path length of maxL
        # neccessarily generates paths of *any* length up to MaxL.
        # Forcing the generation of all these indices here, prevents us 
        # from mutating indices during subpath creation. The fact that indices are
        # immutable allows us to use efficient iterators and prevent unncessary copying

        # Thanks to the use of defaultdict, the following trick will prevent us from 
        # repeatedly testing whether l already exists as a key

        for l in range(max(self.paths)):
                self.paths[l] = self.paths[l]

        # expand subpaths in all paths of any length ... 
        for pathLength in self.paths:
            for path in self.paths[pathLength]:
                                
                # The frequency is given by the number of occurrences as longest 
                # path, which is stored in the second entry of the numpy array
                frequency = self.paths[pathLength][path][1]

                # Generate all subpaths of length k for k = 0 to k = pathLength-1 (inclusive)
                for k in range(0, pathLength):
                    # Generate subpaths of length k for all start indices s for s = 0 to s = pathLength-k (inclusive)
                    for s in range(0, pathLength-k+1):
                        # Add frequency as a subpath to *first* entry of occurrence counter
                        self.paths[k][path[s:s+k+1]] += (frequency,0)
def expandSubPaths ( self ) : # nothing to see here ...  if len ( self . paths ) == 0 : return Log . add ( 'Calculating sub path statistics ... ' ) # the expansion of all subpaths in paths with a maximum path length of maxL # neccessarily generates paths of *any* length up to MaxL. # Forcing the generation of all these indices here, prevents us  # from mutating indices during subpath creation. The fact that indices are # immutable allows us to use efficient iterators and prevent unncessary copying # Thanks to the use of defaultdict, the following trick will prevent us from  # repeatedly testing whether l already exists as a key for l in range ( max ( self . paths ) ) : self . paths [ l ] = self . paths [ l ] # expand subpaths in all paths of any length ...  for pathLength in self . paths : for path in self . paths [ pathLength ] : # The frequency is given by the number of occurrences as longest  # path, which is stored in the second entry of the numpy array frequency = self . paths [ pathLength ] [ path ] [ 1 ] # Generate all subpaths of length k for k = 0 to k = pathLength-1 (inclusive) for k in range ( 0 , pathLength ) : # Generate subpaths of length k for all start indices s for s = 0 to s = pathLength-k (inclusive) for s in range ( 0 , pathLength - k + 1 ) : # Add frequency as a subpath to *first* entry of occurrence counter self . paths [ k ] [ path [ s : s + k + 1 ] ] += ( frequency , 0 )