예제 #1
0
 def emissionProb(self, node, is_partial_graph_index=False):
     # Access the emission matrix with the full graph indices
     node_full = self.partialGraphIndexToFullGraphIndex(
         node) if is_partial_graph_index == True else node
     prob = self.L[node_full].reshape((-1, ))
     if (self.inFeedbackSet(node_full, is_partial_graph_index=False)):
         return fbsData(prob, 0)
     return fbsData(prob, -1)
예제 #2
0
    def emissionProb(self, node, is_partial_graph_index=False):
        # Access the emission matrix with the full graph indices
        node_full = self.partialGraphIndexToFullGraphIndex(
            node) if is_partial_graph_index == True else node
        y = self.ys[int(node_full)]
        if (not np.any(np.isnan(y))):
            prob = self.recognizerFunc(y).reshape((-1, ))
        else:
            prob = np.zeros_like(self.pi0).reshape((-1, ))

        if (self.inFeedbackSet(node_full, is_partial_graph_index=False)):
            return fbsData(prob, 0)
        return fbsData(prob, -1)
예제 #3
0
    def vData( self, U, V, node, edges=None ):
        # THESE MUST NOT MODIFY U OR V!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        # VERY IMPORTANT!!!!!! PROBABLY ENFORCE THIS SOMEHOW IN THE FURURE
        V_row, V_col, _ = V

        if( self.inFeedbackSet( node, is_partial_graph_index=True ) ):
            # This is going to be empty because by construction,
            # if a node is in the fbs, all the information from
            # going down the down edges can be gained by going
            # up an up edge.
            if( edges is None ):
                return []
            elif( isinstance( edges, Iterable ) ):
                return []
            else:
                return fbsData( np.array( [] ), -1 )

        if( ~np.any( np.in1d( V_row, node ) ) ):
            # If the node isn't in the V object (node is a leaf)
            ans = [ fbsData( np.array( [] ), -1 ) ]
        elif( edges is None ):
            # Return the data over all down edges
            ans = self.vDataFromMask( V, np.in1d( V_row, node ) )
        elif( isinstance( edges, Iterable ) and len( edges ) > 0 ):
            # Return over only certain edges
            mask = np.in1d( V_row, node )
            for e in edges:
                mask &= np.in1d( V_col, e )
            ans = self.vDataFromMask( V, mask )
        elif( isinstance( edges, Iterable ) and len( edges ) == 0 ):
            # This happens when we're not passed a down edge.  In this
            # case, we should return an empty v value, not a leaf v value
            return [ fbsData( np.array( [] ), -1 ) ]
        else:
            # Only looking at one edge
            ans = self.vDataFromMask( V, np.in1d( V_col, edges ) )

        if( len( ans ) == 0 ):
            # If we're looking at a leaf, return all 0s
            nVals = 1 if edges is None else len( edges )
            ans = []
            for _ in range( nVals ):
                ans.append( fbsData( np.array( [] ), -1 ) )

        for v in ans:
            data = v.data
            assert np.any( np.isnan( data ) ) == False, data

        return ans
예제 #4
0
    def integrate(cls, integrand, axes):

        # Check if we need to use the regular integrate
        if (not isinstance(integrand, fbsData)):
            return GraphHMM.integrate(integrand, axes)

        # Need adjusted axes because the relative axes in integrand change as we reduce
        # over each axis
        assert isinstance(axes, Iterable)
        if (len(axes) == 0):
            return integrand

        integrand, fbs_axis = (integrand.data, integrand.fbs_axis)

        assert max(axes) < integrand.ndim
        axes = np.array(axes)
        axes[axes < 0] = integrand.ndim + axes[axes < 0]
        adjusted_axes = np.array(sorted(axes)) - np.arange(len(axes))
        for ax in adjusted_axes:
            integrand = logsumexp(integrand, axis=ax)

        if (fbs_axis > -1):
            fbs_axis -= len(adjusted_axes)
            # assert fbs_axis > -1, adjusted_axes

        return fbsData(integrand, fbs_axis)
예제 #5
0
    def genFilterProbs(self):

        # Initialize U and V
        U = []
        for node in self.partial_graph.nodes:
            U.append(fbsData(np.zeros(self.K), -1))

        V_row = self.partial_graph.pmask.row
        V_col = self.partial_graph.pmask.col
        V_data = []
        for node in self.partial_graph.pmask.row:
            V_data.append(fbsData(np.zeros(self.K), -1))

        # Invalidate all data elements
        for node in self.partial_graph.nodes:
            U[node].data[:] = np.nan
            self.assignV((V_row, V_col, V_data), node, np.nan, keep_shape=True)

        return U, (V_row, V_col, V_data)
예제 #6
0
    def emissionProb(self, node, is_partial_graph_index=False):
        # Access the emission matrix with the full graph indices
        node_full = self.partialGraphIndexToFullGraphIndex(
            node) if is_partial_graph_index == True else node

        group = self.node_groups[int(node_full)]
        y = self.ys[int(node_full)]
        if (not np.any(np.isnan(y))):
            prob = self.emission_dists[group][:, y].sum(axis=-1)
        else:
            prob = np.zeros_like(self.emission_dists[group][:, 0])

        if (self.inFeedbackSet(node_full, is_partial_graph_index=False)):
            fbs_index = self.fbsIndex(node_full,
                                      is_partial_graph_index=False,
                                      within_graph=True)
            for _ in range(fbs_index):
                prob = prob[None]
            return fbsData(prob, 0)
        return fbsData(prob, -1)
예제 #7
0
    def initialProb(self, node, is_partial_graph_index=False):
        pi = np.copy(self.pi0)
        node_full = self.partialGraphIndexToFullGraphIndex(
            node) if is_partial_graph_index == True else node
        if (int(node_full) in self.possible_latent_states):
            states = self.possible_latent_states[int(node_full)]
            impossible_states = np.setdiff1d(np.arange(pi.shape[-1]), states)
            for state in impossible_states:
                pi[impossible_states] = np.NINF
            pi[states] -= logsumexp(pi)

        return fbsData(pi, -1)
예제 #8
0
    def emissionProb(self, node, is_partial_graph_index=False):
        # Access the emission matrix with the full graph indices
        node_full = self.partialGraphIndexToFullGraphIndex(
            node) if is_partial_graph_index == True else node

        group = self.node_groups[int(node_full)]
        y = self.ys[int(node_full)]
        cond = self.conds[int(node_full)]

        if (not np.any(np.isnan(y))):
            prob = self.recognizerFuncs[group](y, cond).reshape((-1, ))
        else:
            prob = np.zeros_like(self.pi0s[group][:, 0]).reshape((-1, ))

        if (self.inFeedbackSet(node_full, is_partial_graph_index=False)):
            fbs_index = self.fbsIndex(node_full,
                                      is_partial_graph_index=False,
                                      within_graph=True)
            for _ in range(fbs_index):
                prob = prob[None]
            return fbsData(prob, 0)
        return fbsData(prob, -1)
예제 #9
0
    def extendAxes( self, node, term, target_axis, max_dim ):
        # Push the first axis out to target_axis, but don't change
        # the axes past max_dim
        term, fbs_axis = ( term.data, term.fbs_axis )

        # Add axes before the fbsAxes
        for _ in range( max_dim - target_axis - 1 ):
            term = np.expand_dims( term, 1 )
            if( fbs_axis != -1 ):
                fbs_axis += 1

        # Prepend axes
        for _ in range( target_axis ):
            term = np.expand_dims( term, 0 )
            if( fbs_axis != -1 ):
                fbs_axis += 1

        return fbsData( term, fbs_axis )
예제 #10
0
    def transitionProb(self, child, is_partial_graph_index=False):
        parents, parent_order = self.getFullParents(
            child,
            get_order=True,
            is_partial_graph_index=is_partial_graph_index,
            return_partial_graph_index=True)
        child_full = self.partialGraphIndexToFullGraphIndex(
            child) if is_partial_graph_index == True else child
        ndim = len(parents) + 1
        group = self.node_groups[int(child_full)]
        shape = []
        for p, _ in sorted(zip(parents, parent_order), key=lambda po: po[1]):
            full_p = int(self.partialGraphIndexToFullGraphIndex(p))
            shape.append(self.getNodeDim(full_p))
        shape.append(self.getNodeDim(int(child_full)))
        shape = tuple(shape)

        pi = np.copy(self.pis[group][shape])
        # Reshape pi's axes to match parent order
        assert len(parents) + 1 == pi.ndim
        assert parent_order.shape[0] == parents.shape[0]

        # Sort the parent dimensions by parent order
        pi = np.moveaxis(pi, np.arange(ndim),
                         np.hstack((parent_order, ndim - 1)))

        # If we know the latent state for child, then ensure that we
        # transition there. This is intervention!
        modified = False
        for parent, order in zip(parents, parent_order):
            parent_full = self.partialGraphIndexToFullGraphIndex(parent)
            if (int(parent_full) in self.possible_latent_states):
                parent_states = self.possible_latent_states[int(parent_full)]
                impossible_parent_axes = np.setdiff1d(
                    np.arange(pi.shape[order]), parent_states)
                index = [slice(0, s) for s in pi.shape]
                index[order] = impossible_parent_axes
                pi[tuple(index)] = np.NINF
                modified = True

        if (int(child_full) in self.possible_latent_states):
            states = self.possible_latent_states[int(child_full)]
            impossible_axes = np.setdiff1d(np.arange(pi.shape[-1]), states)
            pi[..., impossible_axes] = np.NINF
            modified = True

        # In case entire rows summed to -inf
        pi[np.isnan(pi)] = np.NINF

        # Check if there are nodes in [ child, *parents ] that are in the fbs.
        # If there are, then move their axes
        fbsOffset = lambda x: self.fbsIndex(
            x, is_partial_graph_index=True, within_graph=True) + 1
        fbs_indices = [
            fbsOffset(parent) for parent in parents
            if self.inFeedbackSet(parent, is_partial_graph_index=True)
        ]

        if (self.inFeedbackSet(child,
                               is_partial_graph_index=is_partial_graph_index)):
            fbs_indices.append(
                self.fbsIndex(child,
                              is_partial_graph_index=is_partial_graph_index,
                              within_graph=True) + 1)

        if (len(fbs_indices) > 0):
            expand_by = max(fbs_indices)
            for _ in range(expand_by):
                pi = pi[..., None]

            # If there are parents in the fbs, move them to the appropriate axes
            for i, parent in enumerate(parents):
                if (self.inFeedbackSet(parent, is_partial_graph_index=True)):
                    pi = np.swapaxes(pi, i, fbsOffset(parent) + ndim - 1)

            if (self.inFeedbackSet(
                    child, is_partial_graph_index=is_partial_graph_index)):
                # If the child is in the fbs, then move it to the appropriate axis
                pi = np.swapaxes(pi, ndim - 1, fbsOffset(child) + ndim - 1)

            return fbsData(pi, ndim)
        return fbsData(pi, -1)
예제 #11
0
    def multiplyTerms(cls, terms):
        # Basically np.einsum but in log space

        assert isinstance(terms, Iterable)

        # Check if we should use the multiply for fbsData or for regular data
        fbs_data_count, non_fbs_data_count = (0, 0)
        for t in terms:
            if (isinstance(t, fbsData)):
                fbs_data_count += 1
            else:
                non_fbs_data_count += 1

        # Can't mix types
        if (not (fbs_data_count == 0 or non_fbs_data_count == 0)):
            print('fbs_data_count', fbs_data_count)
            print('non_fbs_data_count', non_fbs_data_count)
            print(terms)
            for t in terms:
                if (isinstance(t, fbsData)):
                    print('this ones good', t, type(t))
                else:
                    print('this ones bad', t, type(t))
            assert 0

        # Use the regular multiply if we don't have fbs data
        if (fbs_data_count == 0):
            return GraphHMM.multiplyTerms(terms)

        # Remove the empty terms
        terms = [t for t in terms if np.prod(t.shape) > 1]

        if (len(terms) == 0):
            return fbsData(np.array([]), 0)

        # Separate out where the feedback set axes start and get the largest fbs_axis.
        # Need to handle case where ndim of term > all fbs axes
        # terms, fbs_axes_start = list( zip( *terms ) )
        fbs_axes_start = [term.fbs_axis for term in terms]
        terms = [term.data for term in terms]

        if (max(fbs_axes_start) != -1):
            max_fbs_axis = max([
                ax if ax != -1 else term.ndim
                for ax, term in zip(fbs_axes_start, terms)
            ])

            if (max_fbs_axis > 0):
                # Pad extra dims at each term so that the fbs axes start the same way for every term
                for i, ax in enumerate(fbs_axes_start):
                    if (ax == -1):
                        for _ in range(max_fbs_axis - terms[i].ndim + 1):
                            terms[i] = terms[i][..., None]
                    else:
                        for _ in range(max_fbs_axis - ax):
                            terms[i] = np.expand_dims(terms[i], axis=ax)
        else:
            max_fbs_axis = -1

        ndim = max([len(term.shape) for term in terms])

        axes = [[i for i, s in enumerate(t.shape) if s != 1] for t in terms]

        # Get the shape of the output
        shape = np.ones(ndim, dtype=int)
        for ax, term in zip(axes, terms):
            shape[np.array(ax)] = term.squeeze().shape

        total_elts = shape.prod()
        if (total_elts > 1e8):
            assert 0, 'Don\'t do this on a cpu!  Too many terms: %d' % (
                int(total_elts))

        # Build a meshgrid out of each of the terms over the right axes
        # and sum.  Doing it this way because np.einsum doesn't work
        # for matrix multiplication in log space - we can't do np.einsum
        # but add instead of multiply over indices
        ans = np.zeros(shape)
        for ax, term in zip(axes, terms):

            for _ in range(ndim - term.ndim):
                term = term[..., None]

            ans += np.broadcast_to(term, ans.shape)

        return fbsData(ans, max_fbs_axis)
예제 #12
0
 def vBaseCase( self, node ):
     return fbsData( np.array( [] ), -1 )