(self, paths, l=1, considerLongerPaths=True, log=True, minL=None)
Calculates the (log-)likelihood of the **first** l layers of a multi-order network model
        using all observed paths of (at least) length l

        @param paths: the path statistics for which to calculate the layer likelihood

        @param l: number of layers for which likelihood shall be calculated
            Paths of length l (and possibly longer) will be used to calculate the likelihood
            of model layers for all orders up to l

        @param considerLongerPaths: whether or not to include paths longer than l
            in the calculation of the likelihood. In general, when calculating the likelihood
            of a multi-order model which combines orders from 1 to l, this should be set to
            true only for the value of l that corresponds to the largest order in the model.

        @param log: whether to compute Log-Likelihood (default: True)

        @param minL: minimum length of paths which enter the likelihood calculation. For the
            default value None, all paths with at least length l will be considered.

        @returns: the (log-)likelihood of the model layer given the path statistics
Calculates the (log-)likelihood of the **first** l layers of a multi-order network model
        using all observed paths of (at least) length l
Calculates the ( log - ) likelihood of the ** first ** l layers of a multi - order network model using all observed paths of ( at least ) length l
def getLayerLikelihood(self, paths, l=1, considerLongerPaths=True, log=True, minL=None):
        """
        Calculates the (log-)likelihood of the **first** l layers of a multi-order network model
        using all observed paths of (at least) length l

        @param paths: the path statistics for which to calculate the layer likelihood

        @param l: number of layers for which likelihood shall be calculated
            Paths of length l (and possibly longer) will be used to calculate the likelihood
            of model layers for all orders up to l

        @param considerLongerPaths: whether or not to include paths longer than l
            in the calculation of the likelihood. In general, when calculating the likelihood
            of a multi-order model which combines orders from 1 to l, this should be set to
            true only for the value of l that corresponds to the largest order in the model.

        @param log: whether to compute Log-Likelihood (default: True)

        @param minL: minimum length of paths which enter the likelihood calculation. For the
            default value None, all paths with at least length l will be considered.

        @returns: the (log-)likelihood of the model layer given the path statistics
        """

        # m is the maximum length of any path in the data
        m = max(paths.paths)

        assert m >= l and len(paths.paths[l])>0, 'Error: there are no paths of length l or longer'

        if minL == None:
            minL = l

         # Set maximum length of paths to consider in likelihood calculation
        if considerLongerPaths:
            maxL = m
        else:
            maxL = l

        # For the paths S_k of length k (or longer) that we observe, we need to calculate
        # the probability of observing all paths in S_k based on the probabilities of
        # individual paths (which are calculated using the underlying Markov model(s))

        # n is the total number of path observations
        n = 0

        # Initialize likelihood
        L = 0

        # compute likelihood for all longest paths
        # up to the maximum path length maxL
        for k in range(minL, maxL+1):
            for p in paths.paths[k]:

                # Only consider observations as *longest* path
                if paths.paths[k][p][1]>0:

                    # Add m_i observations of path p to total number of observations n
                    n += paths.paths[k][p][1]

                    # special case: to calculate the likelihood of the path based on a zero-order model we
                    # use the 'start' -> v transitions in the respective model instance
                    if l==0:
                        for s in range(len(p)):
                            L += _np.log(self.T[0][self.layers[0].nodes.index(p[s]), self.layers[0].nodes.index('start')]) * paths.paths[k][p][1]

                    # general case: compute likelihood of path based on
                    # hierarchy of higher-order models as follows ...
                    else:

                        # 1.) transform the path into a sequence of (two or more) l-th-order nodes
                        nodes = self.layers[l].pathToHigherOrderNodes(p)
                        # print('l-th order path = ', str(nodes))

                        # 2.) nodes[0] is the prefix of the k-th order transitions, which we
                        #   can transform into multiple transitions in lower order models.
                        #   Example: for a path a-b-c-d of length three, the node sequence
                        #   at order l=3 is ['a-b-c', 'b-c-d'] and thus the prefix is 'a-b-c'.
                        prefix = nodes[0]

                        # 3.) We extract the transitions for the prefix based on models of
                        #   orders k_<l. In our example, we have the transitions ...
                        #   (a-b, b-c) for k_=2
                        #   (a, b) for k_=1, and
                        #   (start, a) for k_=0
                        transitions = {}

                        # for all k_<l in descending order
                        for k_ in range(l-1, -1, -1):
                            #print('prefix = ', prefix)
                            x = prefix.split(self.layers[k_].separator)
                            transitions[k_] = self.layers[k_].pathToHigherOrderNodes(x)
                            #print('transition (k_=', k_,') = ', transitions[k_])
                            prefix = transitions[k_][0]

                        # 4.) Using Bayes theorem, we calculate the likelihood of a path a-b-c-d-e
                        #   of length four for l=4 as a single transition in a fourth-order model, and
                        #   four additional transitions in the k_=0, 1, 2 and 3-order models, i.e. we have ...
                        #   P(a-b-c-d-e) = P(e|a-b-c-d) * [ P(d|a-b-c) * P(c|a-b) * P(b|a) * P(a) ]
                        #   If we were to model the same path based on model hierarchy with a maximum order of l=2,
                        #   we instead have three transitions in the second-order model and two additional transitions
                        #   in the k_=0 and k_=1 order models for the prefix 'a-b' ...
                        #   P(a-b-c-d-e) = P(e|c-d) * P(d|b-c) * P(c|a-b) * [ P(b|a) * P(a) ]

                        # First multiply the transitions in the l-th order model ...
                        for s in range(len(nodes)-1):
                            # print((nodes[s], nodes[s+1]))
                            # print(T[model.nodes.index(nodes[s+1]), model.nodes.index(nodes[s])])
                            L += _np.log(self.T[l][self.layers[l].nodes.index(nodes[s+1]), self.layers[l].nodes.index(nodes[s])]) * paths.paths[k][p][1]

                        # ... then multiply additional transition probabilities for the prefix ...
                        for k_ in range(0, l):
                            L += _np.log(self.T[k_][self.layers[k_].nodes.index(transitions[k_][1]), self.layers[k_].nodes.index(transitions[k_][0])]) * paths.paths[k][p][1]

            if n == 0:
                L = 0
        if log:
            assert L<=0, 'Log-Likelihood out of bounds'
            return L, n
        else:
            assert L>=0 and L<=1, 'Likelihood out of bounds'
            return _np.exp(L), n
def getLayerLikelihood ( self , paths , l = 1 , considerLongerPaths = True , log = True , minL = None ) : # m is the maximum length of any path in the data m = max ( paths . paths ) assert m >= l and len ( paths . paths [ l ] ) > 0 , 'Error: there are no paths of length l or longer' if minL == None : minL = l # Set maximum length of paths to consider in likelihood calculation if considerLongerPaths : maxL = m else : maxL = l # For the paths S_k of length k (or longer) that we observe, we need to calculate # the probability of observing all paths in S_k based on the probabilities of # individual paths (which are calculated using the underlying Markov model(s)) # n is the total number of path observations n = 0 # Initialize likelihood L = 0 # compute likelihood for all longest paths # up to the maximum path length maxL for k in range ( minL , maxL + 1 ) : for p in paths . paths [ k ] : # Only consider observations as *longest* path if paths . paths [ k ] [ p ] [ 1 ] > 0 : # Add m_i observations of path p to total number of observations n n += paths . paths [ k ] [ p ] [ 1 ] # special case: to calculate the likelihood of the path based on a zero-order model we # use the 'start' -> v transitions in the respective model instance if l == 0 : for s in range ( len ( p ) ) : L += _np . log ( self . T [ 0 ] [ self . layers [ 0 ] . nodes . index ( p [ s ] ) , self . layers [ 0 ] . nodes . index ( 'start' ) ] ) * paths . paths [ k ] [ p ] [ 1 ] # general case: compute likelihood of path based on # hierarchy of higher-order models as follows ... else : # 1.) transform the path into a sequence of (two or more) l-th-order nodes nodes = self . layers [ l ] . pathToHigherOrderNodes ( p ) # print('l-th order path = ', str(nodes)) # 2.) nodes[0] is the prefix of the k-th order transitions, which we #   can transform into multiple transitions in lower order models. #   Example: for a path a-b-c-d of length three, the node sequence #   at order l=3 is ['a-b-c', 'b-c-d'] and thus the prefix is 'a-b-c'. prefix = nodes [ 0 ] # 3.) We extract the transitions for the prefix based on models of #   orders k_<l. In our example, we have the transitions ... #   (a-b, b-c) for k_=2 #   (a, b) for k_=1, and #   (start, a) for k_=0 transitions = { } # for all k_<l in descending order for k_ in range ( l - 1 , - 1 , - 1 ) : #print('prefix = ', prefix) x = prefix . split ( self . layers [ k_ ] . separator ) transitions [ k_ ] = self . layers [ k_ ] . pathToHigherOrderNodes ( x ) #print('transition (k_=', k_,') = ', transitions[k_]) prefix = transitions [ k_ ] [ 0 ] # 4.) Using Bayes theorem, we calculate the likelihood of a path a-b-c-d-e #   of length four for l=4 as a single transition in a fourth-order model, and #   four additional transitions in the k_=0, 1, 2 and 3-order models, i.e. we have ... #   P(a-b-c-d-e) = P(e|a-b-c-d) * [ P(d|a-b-c) * P(c|a-b) * P(b|a) * P(a) ] #   If we were to model the same path based on model hierarchy with a maximum order of l=2, #   we instead have three transitions in the second-order model and two additional transitions #   in the k_=0 and k_=1 order models for the prefix 'a-b' ... #   P(a-b-c-d-e) = P(e|c-d) * P(d|b-c) * P(c|a-b) * [ P(b|a) * P(a) ] # First multiply the transitions in the l-th order model ... for s in range ( len ( nodes ) - 1 ) : # print((nodes[s], nodes[s+1])) # print(T[model.nodes.index(nodes[s+1]), model.nodes.index(nodes[s])]) L += _np . log ( self . T [ l ] [ self . layers [ l ] . nodes . index ( nodes [ s + 1 ] ) , self . layers [ l ] . nodes . index ( nodes [ s ] ) ] ) * paths . paths [ k ] [ p ] [ 1 ] # ... then multiply additional transition probabilities for the prefix ... for k_ in range ( 0 , l ) : L += _np . log ( self . T [ k_ ] [ self . layers [ k_ ] . nodes . index ( transitions [ k_ ] [ 1 ] ) , self . layers [ k_ ] . nodes . index ( transitions [ k_ ] [ 0 ] ) ] ) * paths . paths [ k ] [ p ] [ 1 ] if n == 0 : L = 0 if log : assert L <= 0 , 'Log-Likelihood out of bounds' return L , n else : assert L >= 0 and L <= 1 , 'Likelihood out of bounds' return _np . exp ( L ) , n