예제 #1
0
파일: Sampling.py 프로젝트: cfm25/pgmpy
    def _get_kernel_from_markov_model(self, model):
        """
        Computes the Gibbs transition models from a Markov Network.
        'Probabilistic Graphical Model Principles and Techniques', Koller and
        Friedman, Section 12.3.3 pp 512-513.

        Parameters:
        -----------
        model: MarkovModel
            The model from which probabilities will be computed.
        """
        self.variables = np.array(model.nodes())
        factors_dict = {var: [] for var in self.variables}
        for factor in model.get_factors():
            for var in factor.scope():
                factors_dict[var].append(factor)

        # Take factor product
        factors_dict = {var: factor_product(*factors) if len(factors) > 1 else factors[0]
                        for var, factors in factors_dict.items()}
        self.cardinalities = {var: factors_dict[var].get_cardinality([var])[var] for var in self.variables}

        for var in self.variables:
            other_vars = [v for v in self.variables if var != v]
            other_cards = [self.cardinalities[v] for v in other_vars]
            kernel = {}
            factor = factors_dict[var]
            scope = set(factor.scope())
            for tup in itertools.product(*[range(card) for card in other_cards]):
                states = [State(var, s) for var, s in zip(other_vars, tup) if var in scope]
                reduced_factor = factor.reduce(states, inplace=False)
                kernel[tup] = reduced_factor.values / sum(reduced_factor.values)
            self.transition_models[var] = kernel
예제 #2
0
파일: Sampling.py 프로젝트: cfm25/pgmpy
    def _get_kernel_from_bayesian_model(self, model):
        """
        Computes the Gibbs transition models from a Bayesian Network.
        'Probabilistic Graphical Model Principles and Techniques', Koller and
        Friedman, Section 12.3.3 pp 512-513.

        Parameters:
        -----------
        model: BayesianModel
            The model from which probabilities will be computed.
        """
        self.variables = np.array(model.nodes())
        self.cardinalities = {var: model.get_cpds(var).variable_card for var in self.variables}

        for var in self.variables:
            other_vars = [v for v in self.variables if var != v]
            other_cards = [self.cardinalities[v] for v in other_vars]
            cpds = [cpd for cpd in model.cpds if var in cpd.scope()]
            prod_cpd = factor_product(*cpds)
            kernel = {}
            scope = set(prod_cpd.scope())
            for tup in itertools.product(*[range(card) for card in other_cards]):
                states = [State(v, s) for v, s in zip(other_vars, tup) if v in scope]
                prod_cpd_reduced = prod_cpd.reduce(states, inplace=False)
                kernel[tup] = prod_cpd_reduced.values / sum(prod_cpd_reduced.values)
            self.transition_models[var] = kernel
예제 #3
0
    def map_query(self, variables=None, evidence=None, elimination_order=None):
        """
        Computes the MAP Query over the variables given the evidence.

        Parameters
        ----------
        variables: list
            list of variables over which we want to compute the max-marginal.
        evidence: dict
            a dict key, value pair as {var: state_of_var_observed}
            None if no evidence
        elimination_order: list
            order of variable eliminations (if nothing is provided) order is
            computed automatically

        Examples
        --------
        >>> from pgmpy.inference import VariableElimination
        >>> from pgmpy.models import BayesianModel
        >>> import numpy as np
        >>> import pandas as pd
        >>> values = pd.DataFrame(np.random.randint(low=0, high=2, size=(1000, 5)),
        ...                       columns=['A', 'B', 'C', 'D', 'E'])
        >>> model = BayesianModel([('A', 'B'), ('C', 'B'), ('C', 'D'), ('B', 'E')])
        >>> model.fit(values)
        >>> inference = VariableElimination(model)
        >>> phi_query = inference.map_query(['A', 'B'])
        """
        elimination_variables = set(self.variables) - set(
            evidence.keys()) if evidence else set()
        final_distribution = self._variable_elimination(
            elimination_variables,
            'maximize',
            evidence=evidence,
            elimination_order=elimination_order)
        # To handle the case when no argument is passed then
        # _variable_elimination returns a dict.
        if isinstance(final_distribution, dict):
            final_distribution = final_distribution.values()
        distribution = factor_product(*final_distribution)
        argmax = np.argmax(distribution.values)
        assignment = distribution.assignment([argmax])[0]

        map_query_results = {}
        for var_assignment in assignment:
            var, value = var_assignment
            map_query_results[var] = value

        if not variables:
            return map_query_results
        else:
            return_dict = {}
            for var in variables:
                return_dict[var] = map_query_results[var]
            return return_dict
예제 #4
0
    def map_query(self, variables=None, evidence=None, elimination_order=None):
        """
        Computes the MAP Query over the variables given the evidence.

        Parameters
        ----------
        variables: list
            list of variables over which we want to compute the max-marginal.
        evidence: dict
            a dict key, value pair as {var: state_of_var_observed}
            None if no evidence
        elimination_order: list
            order of variable eliminations (if nothing is provided) order is
            computed automatically

        Examples
        --------
        >>> from pgmpy.inference import VariableElimination
        >>> from pgmpy.models import BayesianModel
        >>> import numpy as np
        >>> import pandas as pd
        >>> values = pd.DataFrame(np.random.randint(low=0, high=2, size=(1000, 5)),
        ...                       columns=['A', 'B', 'C', 'D', 'E'])
        >>> model = BayesianModel([('A', 'B'), ('C', 'B'), ('C', 'D'), ('B', 'E')])
        >>> model.fit(values)
        >>> inference = VariableElimination(model)
        >>> phi_query = inference.map_query(['A', 'B'])
        """
        elimination_variables = set(self.variables) - set(evidence.keys()) if evidence else set()
        final_distribution = self._variable_elimination(elimination_variables, 'maximize',
                                                        evidence=evidence,
                                                        elimination_order=elimination_order)
        # To handle the case when no argument is passed then
        # _variable_elimination returns a dict.
        if isinstance(final_distribution, dict):
            final_distribution = final_distribution.values()
        distribution = factor_product(*final_distribution)
        argmax = np.argmax(distribution.values)
        assignment = distribution.assignment([argmax])[0]

        map_query_results = {}
        for var_assignment in assignment:
            var, value = var_assignment
            map_query_results[var] = value

        if not variables:
            return map_query_results
        else:
            return_dict = {}
            for var in variables:
                return_dict[var] = map_query_results[var]
            return return_dict
예제 #5
0
    def _get_factor(self, belief_prop, evidence):
        """
        Extracts the required factor from the junction tree.

        Parameters:
        ----------
        belief_prop: Belief Propagation
            Belief Propagation which needs to be updated.

        evidence: dict
            a dict key, value pair as {var: state_of_var_observed}
        """
        final_factor = factor_product(*belief_prop.junction_tree.get_factors())
        if evidence:
            for var in evidence:
                if var in final_factor.scope():
                    final_factor.reduce([(var, evidence[var])])
        return final_factor
예제 #6
0
    def _get_factor(self, belief_prop, evidence):
        """
        Extracts the required factor from the junction tree.

        Parameters:
        ----------
        belief_prop: Belief Propagation
            Belief Propagation which needs to be updated.

        evidence: dict
            a dict key, value pair as {var: state_of_var_observed}
        """
        final_factor = factor_product(*belief_prop.junction_tree.get_factors())
        if evidence:
            for var in evidence:
                if var in final_factor.scope():
                    final_factor.reduce([(var, evidence[var])])
        return final_factor
예제 #7
0
    def max_marginal(self,
                     variables=None,
                     evidence=None,
                     elimination_order=None):
        """
        Computes the max-marginal over the variables given the evidence.

        Parameters
        ----------
        variables: list
            list of variables over which we want to compute the max-marginal.
        evidence: dict
            a dict key, value pair as {var: state_of_var_observed}
            None if no evidence
        elimination_order: list
            order of variable eliminations (if nothing is provided) order is
            computed automatically

        Examples
        --------
        >>> import numpy as np
        >>> import pandas as pd
        >>> from pgmpy.models import BayesianModel
        >>> from pgmpy.inference import VariableElimination
        >>> values = pd.DataFrame(np.random.randint(low=0, high=2, size=(1000, 5)),
        ...                       columns=['A', 'B', 'C', 'D', 'E'])
        >>> model = BayesianModel([('A', 'B'), ('C', 'B'), ('C', 'D'), ('B', 'E')])
        >>> model.fit(values)
        >>> inference = VariableElimination(model)
        >>> phi_query = inference.max_marginal(['A', 'B'])
        """
        if not variables:
            variables = []
        final_distribution = self._variable_elimination(
            variables,
            'maximize',
            evidence=evidence,
            elimination_order=elimination_order)

        # To handle the case when no argument is passed then
        # _variable_elimination returns a dict.
        if isinstance(final_distribution, dict):
            final_distribution = final_distribution.values()
        return np.max(factor_product(*final_distribution).values)
예제 #8
0
    def query(self, variables, conditions, elimination_order=None):
        """
        Examples
        --------
        >>> from pgmpy.BayesianModel import BayesianModel
        >>> bm = BayesianModel([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e')])
        >>> # add cpds
        >>> VariableElimination(bm).query(variables={'c':{}})
        """
        if not elimination_order:
            elimination_order = list(set(self.variables) - set(variables.keys()) - set(conditions.keys()))

        for node in elimination_order:
            working_factors = self.factors
            phi = factor_product(*working_factors[node]).marginalize(node)
            del working_factors[node]
            for var in phi.variables:
                try:
                    working_factors[var].append(phi)
                except KeyError:
                    working_factors[var] = [phi]
예제 #9
0
    def max_marginal(self, variables=None, evidence=None, elimination_order=None):
        """
        Computes the max-marginal over the variables given the evidence.

        Parameters
        ----------
        variables: list
            list of variables over which we want to compute the max-marginal.
        evidence: dict
            a dict key, value pair as {var: state_of_var_observed}
            None if no evidence
        elimination_order: list
            order of variable eliminations (if nothing is provided) order is
            computed automatically

        Examples
        --------
        >>> import numpy as np
        >>> import pandas as pd
        >>> from pgmpy.models import BayesianModel
        >>> from pgmpy.inference import VariableElimination
        >>> values = pd.DataFrame(np.random.randint(low=0, high=2, size=(1000, 5)),
        ...                       columns=['A', 'B', 'C', 'D', 'E'])
        >>> model = BayesianModel([('A', 'B'), ('C', 'B'), ('C', 'D'), ('B', 'E')])
        >>> model.fit(values)
        >>> inference = VariableElimination(model)
        >>> phi_query = inference.max_marginal(['A', 'B'])
        """
        if not variables:
            variables = []
        final_distribution = self._variable_elimination(variables, 'maximize',
                                                        evidence=evidence,
                                                        elimination_order=elimination_order)

        # To handle the case when no argument is passed then
        # _variable_elimination returns a dict.
        if isinstance(final_distribution, dict):
            final_distribution = final_distribution.values()
        return np.max(factor_product(*final_distribution).values)
예제 #10
0
    def _variable_elimination(self, variables, operation, evidence=None, elimination_order=None):
        """
        Implementation of a generalized variable elimination.

        Parameters
        ----------
        variables: list, array-like
            variables that are not to be eliminated.
        operation: str ('marginalize' | 'maximize')
            The operation to do for eliminating the variable.
        evidence: dict
            a dict key, value pair as {var: state_of_var_observed}
            None if no evidence
        elimination_order: list, array-like
            list of variables representing the order in which they
            are to be eliminated. If None order is computed automatically.
        """
        # Dealing with the case when variables is not provided.
        if not variables:
            all_factors = []
            for factor_li in self.factors.values():
                all_factors.extend(factor_li)
            return set(all_factors)

        eliminated_variables = set()
        working_factors = {node: {factor for factor in self.factors[node]}
                           for node in self.factors}

        # Dealing with evidence. Reducing factors over it before VE is run.
        if evidence:
            for evidence_var in evidence:
                for factor in working_factors[evidence_var]:
                    factor_reduced = factor.reduce([(evidence_var, evidence[evidence_var])], inplace=False)
                    for var in factor_reduced.scope():
                        working_factors[var].remove(factor)
                        working_factors[var].add(factor_reduced)
                del working_factors[evidence_var]

        # TODO: Modify it to find the optimal elimination order
        if not elimination_order:
            elimination_order = list(set(self.variables) -
                                     set(variables) -
                                     set(evidence.keys() if evidence else []))

        elif any(var in elimination_order for var in
                 set(variables).union(set(evidence.keys() if evidence else []))):
            raise ValueError("Elimination order contains variables which are in"
                             " variables or evidence args")

        for var in elimination_order:
            # Removing all the factors containing the variables which are
            # eliminated (as all the factors should be considered only once)
            factors = [factor for factor in working_factors[var]
                       if not set(factor.variables).intersection(eliminated_variables)]
            phi = factor_product(*factors)
            phi = getattr(phi, operation)([var], inplace=False)
            del working_factors[var]
            for variable in phi.variables:
                working_factors[variable].add(phi)
            eliminated_variables.add(var)

        final_distribution = set()
        for node in working_factors:
            factors = working_factors[node]
            for factor in factors:
                if not set(factor.variables).intersection(eliminated_variables):
                    final_distribution.add(factor)

        query_var_factor = {}
        for query_var in variables:
            phi = factor_product(*final_distribution)
            query_var_factor[query_var] = phi.marginalize(list(set(variables) -
                                                               set([query_var])),
                                                          inplace=False).normalize(inplace=False)
        return query_var_factor
 def query(self, query, evidence=None, elimination_order=None):
     if not isinstance(query, list):
         query = [query]
     ### DEBUG
     # print(">>>>>--------------------------------------<<<<<<")
     # print(">>> Query: %s" % query)
     # print(">>> Evidence: %s" % evidence)
     # print(">>> Saved MTs:")
     # for mt in self.marginal_trees:
     #     print("--> Nodes: %s" % mt.nodes())
     #     print(mt.evidence)
     ### --- DEBUG
     # See if it is possible to reuse saved MTs
     marginal_tree = None
     reuse_mts = self.find_reusable(query, evidence)
     ### DEBUG
     # print(">>> Reusable MTs:")
     # for mt in reuse_mts:
     #     print("--> Nodes: %s" % mt.nodes())
     #     print(mt.evidence)
     ### --- DEBUG
     factors = []
     if len(reuse_mts) > 0:
         # TODO: choose MT by the size of it
         # Choose one MT and rebuild it for the new query and evidence
         marginal_tree = reuse_mts[0]
         ### DEBUG
         # print(">>> Nodes before evidence")
         # print(marginal_tree.nodes())
         # print(">>> Messages before evidence")
         # for s in marginal_tree.separators:
         #     print("--> Separator %s" % s.__str__())
         #     for f in marginal_tree.separators[s]:
         #         print(f)
         ### --- DEBUG
         # Reduce new evidences
         new_evidence = {k: evidence[k]
                         for k in evidence
                         if k not in marginal_tree.evidence}
         marginal_tree.set_evidence(new_evidence)
         ### DEBUG
         # print(">>> Nodes after evidence")
         # print(marginal_tree.nodes())
         # print(">>> Messages before evidence")
         # for s in marginal_tree.separators:
         #     print("--> Separator %s" % s.__str__())
         #     for f in marginal_tree.separators[s]:
         #         print(f)
         ### --- DEBUG
         # Rebuild MT to answer new query
         marginal_tree = marginal_tree.rebuild(query, evidence)
         # Perform one way propagation
         self.partial_one_way_propagation(marginal_tree)
     else:
         marginal_tree = self._build(query, evidence, elimination_order)
     ### DEBUG
     # print(">>> Saving marginal tree")
     # print(marginal_tree.nodes())
     # print(marginal_tree.evidence)
     ### --- DEBUG
     # Save the new MT
     self.marginal_trees.append(marginal_tree)
     # Answer the query
     node = marginal_tree.root
     # Define the variables to marginalize
     marginalize = set(node) - set(query)
     # Collect factors for the node
     neighbors = marginal_tree.neighbors(node)
     # Collect incoming messages
     for neighbor in neighbors:
         if (neighbor, node) in marginal_tree.separators:
             factors.extend([f for f in marginal_tree.separators[(neighbor, node)]
                             if len(f.variables) > 0])
     ### DEBUG
     # print(">>> Root: %s" % marginal_tree.root.__str__())
     # print("incoming...")
     # for f in factors:
     #     print(f)
     ### --- DEBUG
     # Collect assigned Factors
     factors.extend([f for f in marginal_tree.factor_node_assignment[node]
                     if len(f.variables) > 0])
     ### DEBUG
     # print("assigned...")
     # for f in marginal_tree.factor_node_assignment[node]:
     #     print(f)
     # print("going to SUM OUT %s" % marginalize.__str__())
     # for f in factors:
     #     print(f)
     ### --- DEBUG
     result = None
     # Sum out variables from factors
     if len(factors) > 0:
         result = Inference.sum_out(marginalize, factors)
     ### DEBUG
     # print(">>> After SUM OUT")
     # for f in result:
     #     print(f)
     ### --- DEBUG
     # Multiply all remaining CPDs
     if len(result) > 0:
         result = factor_product(*result)
         ### DEBUG
         # print(">>> After PRODUCT")
         # print(result)
         ### --- DEBUG
         # Normalize
         result.normalize()
         ### DEBUG
         # print(">>> After Normalize")
         # print(result)
         # marginal_tree.draw()
         ### --- DEBUG
     return result
    def _build(self, variables, evidence=None, elimination_order=None):
        if not isinstance(variables, list):
            variables = [variables]
        # Removing barren and independent variables generate sub-models
        # (a modified version of the model).
        # Then, a copy is used to do not disturb the original model.
        model_copy = self.model.copy()
        factors_copy = self.factors.copy()

        # Load all factors used in this session of Variable Elimination
        working_factors = {node: {factor for factor in factors_copy[node]}
                           for node in factors_copy}

        # Reduce working factors
        if evidence:
            for evidence_var in evidence:
                for factor in working_factors[evidence_var].copy():
                    factor_reduced = factor.reduce(
                        '{evidence_var}_{state}'
                        .format(evidence_var=evidence_var,
                                state=evidence[evidence_var]),
                        inplace=False)
                    for var in factor_reduced.scope():
                        working_factors[var].remove(factor)
                        working_factors[var].add(factor_reduced)
                del working_factors[evidence_var]

        if not elimination_order:
            # If is BayesianModel, find a good elimination ordering
            # using Weighted-Min-Fill heuristic.
            if isinstance(model_copy, BayesianModel):
                elim_ord = EliminationOrdering(model_copy)
                elimination_order = elim_ord.find_elimination_ordering(
                    list(set(model_copy.nodes()) -
                         set(variables) -
                         set(evidence.keys()
                             if evidence else [])),
                    elim_ord.weighted_min_fill)
            else:
                elimination_order = list(set(self.variables) -
                                         set(variables) -
                                         set(evidence.keys()
                                             if evidence else []))

        elif any(var in elimination_order for var in
                 set(variables).union(
                     set(evidence.keys() if evidence else []))):
            raise ValueError("Elimination order contains variables"
                             " which are in variables or evidence args")

        # Perform elimination ordering while constructing new Marginal Tree
        marginal_tree = MarginalTree()
        eliminated_variables = set()
        messages = []
        # Variables to keep the last "phi" message and last created "node"
        phi = None
        node = None
        for var in elimination_order:
            # Removing all the factors containing the variables which are
            # eliminated (as all the factors should be considered only once)
            ### DEBUG
            # print(">>> *** Eliminating %s ***" % var)
            ### --- DEBUG
            factors = [factor for factor in working_factors[var]
                       if not set(factor.variables).intersection(
                eliminated_variables)]
            ### DEBUG
            # print(">>> Factors involved")
            # for f in factors:
            #     print(f)
            ### --- DEBUG
            phi = factor_product(*factors)
            ### DEBUG
            # print(">>> Product")
            # print(phi)
            ### --- DEBUG
            phi = phi.marginalize(var, inplace=False)
            ### DEBUG
            # print(">>> Marginalize")
            # print(phi)
            ### --- DEBUG
            del working_factors[var]
            for variable in phi.variables:
                working_factors[variable].add(phi)
            eliminated_variables.add(var)

            # Save new message
            messages.append(phi)
            # Build a Marginal Tree node
            node = set()
            for f in factors:
                node = node.union(f.scope())
            node = tuple(node)
            marginal_tree.add_node(node)
            messages_intersection = set(factors).intersection(set(messages))
            marginal_tree.add_factors_to_node(
                list(set(factors) - messages_intersection), node)
            # Connect nodes, if past messages are used
            messages_used = []
            for m in list(messages_intersection):
                for separator in marginal_tree.separators.copy():
                    if m in marginal_tree.separators[separator]:
                        marginal_tree.add_edge(separator[0], node)
                        new_separator = (separator[0], node)
                        marginal_tree.add_messages_to_separator(
                            m, new_separator)
                        del marginal_tree.separators[separator]
                        messages_used.append(m)
            ### DEBUG
            # print(">>> Messages used")
            # print(marginal_tree.separators[separator])
            ### --- DEBUG
            # If message wasn't used to create the new message,
            # point it to the "empty node".
            if phi not in messages_used:
                marginal_tree.add_messages_to_separator(phi, (node,))
        ### DEBUG
        # print("===> Remaining Factors")
        # for var in working_factors:
        #     print("===> var %s" % var)
        #     for f in working_factors[var]:
        #         print(f)
        # print(">>> Last message")
        # print(phi.variables)
        ### --- DEBUG
        # If var was eliminated, thus no message created
        if not phi.variables:
            used_factors = []
            for var in working_factors:
                for factor in working_factors[var]:
                    if factor not in used_factors:
                        used_factors.append(factor)
            phi = factor_product(*used_factors)
        # Create the query node (where the query is answered)
        query_node = tuple(phi.variables)
        marginal_tree.add_node(query_node)

        ### DEBUG
        # print(">>> Query node")
        # print(query_node)
        # print(">>> All Remaining Factors")
        # for var in working_factors:
        #     print(">>> All Remaining for var %s" % var)
        #     for f in working_factors[var]:
        #         if not set(f.variables).intersection(
        #                eliminated_variables):
        #             if f in messages:
        #                 print("---> A message.")
        #             else:
        #                 print("---> An original factor.")
        #             print(f)
        ### --- DEBUG

        # Add remaining original factors to the query node
        remaining_assignment_factors = []
        remaining_message_factors = []
        for var in working_factors:
            for factor in working_factors[var]:
                if not set(factor.variables).intersection(
                           eliminated_variables):
                    if factor in messages:
                        remaining_message_factors.append(factor)
                    else:
                        remaining_assignment_factors.append(factor)
        # ### DEBUG
        # print("===> Collected ASSIGMENT remaining Factors")
        # for f in remaining_assignment_factors:
        #     print(f)
        # print("===> Collected MESSAGES remaining Factors")
        # for f in remaining_message_factors:
        #     print(f)
        ### --- DEBUG
        # Redirect remaining message factors to query node
        for message in remaining_message_factors:
            for separator in marginal_tree.separators.copy():
                if message in marginal_tree.separators[separator]:
                    marginal_tree.add_edge(separator[0], query_node)
                    new_separator = (separator[0], query_node)
                    marginal_tree.add_messages_to_separator(
                        message, new_separator)
                    del marginal_tree.separators[separator]
        # TO REMOVE
        # marginal_tree.add_edge(node, query_node)
        # marginal_tree.add_messages_to_separator(
        #     phi, (node, query_node))
        # Add remaining original factors to query node
        marginal_tree.add_factors_to_node(
            remaining_assignment_factors, query_node)
        # Define the root node as the query node.
        marginal_tree.root = query_node
        # Update evidence variables of the Marginal tree
        for k in evidence:
            marginal_tree.evidence[k] = evidence[k]
        marginal_tree.observed.extend(list(evidence.keys()))
        return marginal_tree
예제 #13
0
    def _variable_elimination(self,
                              variables,
                              operation,
                              evidence=None,
                              elimination_order=None):
        """
        Implementation of a generalized variable elimination.

        Parameters
        ----------
        variables: list, array-like
            variables that are not to be eliminated.
        operation: str ('marginalize' | 'maximize')
            The operation to do for eliminating the variable.
        evidence: dict
            a dict key, value pair as {var: state_of_var_observed}
            None if no evidence
        elimination_order: list, array-like
            list of variables representing the order in which they
            are to be eliminated. If None order is computed automatically.
        """
        # Dealing with the case when variables is not provided.
        if not variables:
            all_factors = []
            for factor_li in self.factors.values():
                all_factors.extend(factor_li)
            return set(all_factors)

        eliminated_variables = set()
        working_factors = {
            node: {factor
                   for factor in self.factors[node]}
            for node in self.factors
        }

        # Dealing with evidence. Reducing factors over it before VE is run.
        if evidence:
            for evidence_var in evidence:
                for factor in working_factors[evidence_var]:
                    factor_reduced = factor.reduce(
                        [(evidence_var, evidence[evidence_var])],
                        inplace=False)
                    for var in factor_reduced.scope():
                        working_factors[var].remove(factor)
                        working_factors[var].add(factor_reduced)
                del working_factors[evidence_var]

        # TODO: Modify it to find the optimal elimination order
        if not elimination_order:
            elimination_order = list(
                set(self.variables) - set(variables) -
                set(evidence.keys() if evidence else []))

        elif any(var in elimination_order for var in set(variables).union(
                set(evidence.keys() if evidence else []))):
            raise ValueError(
                "Elimination order contains variables which are in"
                " variables or evidence args")

        for var in elimination_order:
            # Removing all the factors containing the variables which are
            # eliminated (as all the factors should be considered only once)
            factors = [
                factor for factor in working_factors[var]
                if not set(factor.variables).intersection(eliminated_variables)
            ]
            phi = factor_product(*factors)
            phi = getattr(phi, operation)([var], inplace=False)
            del working_factors[var]
            for variable in phi.variables:
                working_factors[variable].add(phi)
            eliminated_variables.add(var)

        final_distribution = set()
        for node in working_factors:
            factors = working_factors[node]
            for factor in factors:
                if not set(
                        factor.variables).intersection(eliminated_variables):
                    final_distribution.add(factor)

        query_var_factor = {}
        for query_var in variables:
            phi = factor_product(*final_distribution)
            query_var_factor[query_var] = phi.marginalize(
                list(set(variables) - set([query_var])),
                inplace=False).normalize(inplace=False)
        return query_var_factor
예제 #14
0
 def query(self, query, evidence=None, elimination_order=None):
     if not isinstance(query, list):
         query = [query]
     ### DEBUG
     # print(">>>>>--------------------------------------<<<<<<")
     # print(">>> Query: %s" % query)
     # print(">>> Evidence: %s" % evidence)
     # print(">>> Saved MTs:")
     # for mt in self.marginal_trees:
     #     print("--> Nodes: %s" % mt.nodes())
     #     print(mt.evidence)
     ### --- DEBUG
     # See if it is possible to reuse saved MTs
     marginal_tree = None
     reuse_mts = self.find_reusable(query, evidence)
     ### DEBUG
     # print(">>> Reusable MTs:")
     # for mt in reuse_mts:
     #     print("--> Nodes: %s" % mt.nodes())
     #     print(mt.evidence)
     ### --- DEBUG
     factors = []
     if len(reuse_mts) > 0:
         # TODO: choose MT by the size of it
         # Choose one MT and rebuild it for the new query and evidence
         marginal_tree = reuse_mts[0]
         ### DEBUG
         # print(">>> Nodes before evidence")
         # print(marginal_tree.nodes())
         # print(">>> Messages before evidence")
         # for s in marginal_tree.separators:
         #     print("--> Separator %s" % s.__str__())
         #     for f in marginal_tree.separators[s]:
         #         print(f)
         ### --- DEBUG
         # Reduce new evidences
         new_evidence = {
             k: evidence[k]
             for k in evidence if k not in marginal_tree.evidence
         }
         marginal_tree.set_evidence(new_evidence)
         ### DEBUG
         # print(">>> Nodes after evidence")
         # print(marginal_tree.nodes())
         # print(">>> Messages before evidence")
         # for s in marginal_tree.separators:
         #     print("--> Separator %s" % s.__str__())
         #     for f in marginal_tree.separators[s]:
         #         print(f)
         ### --- DEBUG
         # Rebuild MT to answer new query
         marginal_tree = marginal_tree.rebuild(query, evidence)
         # Perform one way propagation
         self.partial_one_way_propagation(marginal_tree)
     else:
         marginal_tree = self._build(query, evidence, elimination_order)
     ### DEBUG
     # print(">>> Saving marginal tree")
     # print(marginal_tree.nodes())
     # print(marginal_tree.evidence)
     ### --- DEBUG
     # Save the new MT
     self.marginal_trees.append(marginal_tree)
     # Answer the query
     node = marginal_tree.root
     # Define the variables to marginalize
     marginalize = set(node) - set(query)
     # Collect factors for the node
     neighbors = marginal_tree.neighbors(node)
     # Collect incoming messages
     for neighbor in neighbors:
         if (neighbor, node) in marginal_tree.separators:
             factors.extend([
                 f for f in marginal_tree.separators[(neighbor, node)]
                 if len(f.variables) > 0
             ])
     ### DEBUG
     # print(">>> Root: %s" % marginal_tree.root.__str__())
     # print("incoming...")
     # for f in factors:
     #     print(f)
     ### --- DEBUG
     # Collect assigned Factors
     factors.extend([
         f for f in marginal_tree.factor_node_assignment[node]
         if len(f.variables) > 0
     ])
     ### DEBUG
     # print("assigned...")
     # for f in marginal_tree.factor_node_assignment[node]:
     #     print(f)
     # print("going to SUM OUT %s" % marginalize.__str__())
     # for f in factors:
     #     print(f)
     ### --- DEBUG
     result = None
     # Sum out variables from factors
     if len(factors) > 0:
         result = Inference.sum_out(marginalize, factors)
     ### DEBUG
     # print(">>> After SUM OUT")
     # for f in result:
     #     print(f)
     ### --- DEBUG
     # Multiply all remaining CPDs
     if len(result) > 0:
         result = factor_product(*result)
         ### DEBUG
         # print(">>> After PRODUCT")
         # print(result)
         ### --- DEBUG
         # Normalize
         result.normalize()
         ### DEBUG
         # print(">>> After Normalize")
         # print(result)
         # marginal_tree.draw()
         ### --- DEBUG
     return result
예제 #15
0
    def _build(self, variables, evidence=None, elimination_order=None):
        if not isinstance(variables, list):
            variables = [variables]
        # Removing barren and independent variables generate sub-models
        # (a modified version of the model).
        # Then, a copy is used to do not disturb the original model.
        model_copy = self.model.copy()
        factors_copy = self.factors.copy()

        # Load all factors used in this session of Variable Elimination
        working_factors = {
            node: {factor
                   for factor in factors_copy[node]}
            for node in factors_copy
        }

        # Reduce working factors
        if evidence:
            for evidence_var in evidence:
                for factor in working_factors[evidence_var].copy():
                    factor_reduced = factor.reduce(
                        '{evidence_var}_{state}'.format(
                            evidence_var=evidence_var,
                            state=evidence[evidence_var]),
                        inplace=False)
                    for var in factor_reduced.scope():
                        working_factors[var].remove(factor)
                        working_factors[var].add(factor_reduced)
                del working_factors[evidence_var]

        if not elimination_order:
            # If is BayesianModel, find a good elimination ordering
            # using Weighted-Min-Fill heuristic.
            if isinstance(model_copy, BayesianModel):
                elim_ord = EliminationOrdering(model_copy)
                elimination_order = elim_ord.find_elimination_ordering(
                    list(
                        set(model_copy.nodes()) - set(variables) -
                        set(evidence.keys() if evidence else [])),
                    elim_ord.weighted_min_fill)
            else:
                elimination_order = list(
                    set(self.variables) - set(variables) -
                    set(evidence.keys() if evidence else []))

        elif any(var in elimination_order for var in set(variables).union(
                set(evidence.keys() if evidence else []))):
            raise ValueError("Elimination order contains variables"
                             " which are in variables or evidence args")

        # Perform elimination ordering while constructing new Marginal Tree
        marginal_tree = MarginalTree()
        eliminated_variables = set()
        messages = []
        # Variables to keep the last "phi" message and last created "node"
        phi = None
        node = None
        for var in elimination_order:
            # Removing all the factors containing the variables which are
            # eliminated (as all the factors should be considered only once)
            ### DEBUG
            # print(">>> *** Eliminating %s ***" % var)
            ### --- DEBUG
            factors = [
                factor for factor in working_factors[var]
                if not set(factor.variables).intersection(eliminated_variables)
            ]
            ### DEBUG
            # print(">>> Factors involved")
            # for f in factors:
            #     print(f)
            ### --- DEBUG
            phi = factor_product(*factors)
            ### DEBUG
            # print(">>> Product")
            # print(phi)
            ### --- DEBUG
            phi = phi.marginalize(var, inplace=False)
            ### DEBUG
            # print(">>> Marginalize")
            # print(phi)
            ### --- DEBUG
            del working_factors[var]
            for variable in phi.variables:
                working_factors[variable].add(phi)
            eliminated_variables.add(var)

            # Save new message
            messages.append(phi)
            # Build a Marginal Tree node
            node = set()
            for f in factors:
                node = node.union(f.scope())
            node = tuple(node)
            marginal_tree.add_node(node)
            messages_intersection = set(factors).intersection(set(messages))
            marginal_tree.add_factors_to_node(
                list(set(factors) - messages_intersection), node)
            # Connect nodes, if past messages are used
            messages_used = []
            for m in list(messages_intersection):
                for separator in marginal_tree.separators.copy():
                    if m in marginal_tree.separators[separator]:
                        marginal_tree.add_edge(separator[0], node)
                        new_separator = (separator[0], node)
                        marginal_tree.add_messages_to_separator(
                            m, new_separator)
                        del marginal_tree.separators[separator]
                        messages_used.append(m)
            ### DEBUG
            # print(">>> Messages used")
            # print(marginal_tree.separators[separator])
            ### --- DEBUG
            # If message wasn't used to create the new message,
            # point it to the "empty node".
            if phi not in messages_used:
                marginal_tree.add_messages_to_separator(phi, (node, ))
        ### DEBUG
        # print("===> Remaining Factors")
        # for var in working_factors:
        #     print("===> var %s" % var)
        #     for f in working_factors[var]:
        #         print(f)
        # print(">>> Last message")
        # print(phi.variables)
        ### --- DEBUG
        # If var was eliminated, thus no message created
        if not phi.variables:
            used_factors = []
            for var in working_factors:
                for factor in working_factors[var]:
                    if factor not in used_factors:
                        used_factors.append(factor)
            phi = factor_product(*used_factors)
        # Create the query node (where the query is answered)
        query_node = tuple(phi.variables)
        marginal_tree.add_node(query_node)

        ### DEBUG
        # print(">>> Query node")
        # print(query_node)
        # print(">>> All Remaining Factors")
        # for var in working_factors:
        #     print(">>> All Remaining for var %s" % var)
        #     for f in working_factors[var]:
        #         if not set(f.variables).intersection(
        #                eliminated_variables):
        #             if f in messages:
        #                 print("---> A message.")
        #             else:
        #                 print("---> An original factor.")
        #             print(f)
        ### --- DEBUG

        # Add remaining original factors to the query node
        remaining_assignment_factors = []
        remaining_message_factors = []
        for var in working_factors:
            for factor in working_factors[var]:
                if not set(
                        factor.variables).intersection(eliminated_variables):
                    if factor in messages:
                        remaining_message_factors.append(factor)
                    else:
                        remaining_assignment_factors.append(factor)
        # ### DEBUG
        # print("===> Collected ASSIGMENT remaining Factors")
        # for f in remaining_assignment_factors:
        #     print(f)
        # print("===> Collected MESSAGES remaining Factors")
        # for f in remaining_message_factors:
        #     print(f)
        ### --- DEBUG
        # Redirect remaining message factors to query node
        for message in remaining_message_factors:
            for separator in marginal_tree.separators.copy():
                if message in marginal_tree.separators[separator]:
                    marginal_tree.add_edge(separator[0], query_node)
                    new_separator = (separator[0], query_node)
                    marginal_tree.add_messages_to_separator(
                        message, new_separator)
                    del marginal_tree.separators[separator]
        # TO REMOVE
        # marginal_tree.add_edge(node, query_node)
        # marginal_tree.add_messages_to_separator(
        #     phi, (node, query_node))
        # Add remaining original factors to query node
        marginal_tree.add_factors_to_node(remaining_assignment_factors,
                                          query_node)
        # Define the root node as the query node.
        marginal_tree.root = query_node
        # Update evidence variables of the Marginal tree
        for k in evidence:
            marginal_tree.evidence[k] = evidence[k]
        marginal_tree.observed.extend(list(evidence.keys()))
        return marginal_tree