Instruction:
Can you implement a function in Python 3 that
returns the factors that have been added till now to the graph.

Response:
def get_factors(self, node=None):
        """
        Returns the factors that have been added till now to the graph.

        If node is not None, it would return the factor corresponding to the
        given node.

        Examples
        --------
        >>> from pgmpy.models import FactorGraph
        >>> from pgmpy.factors.discrete import DiscreteFactor
        >>> G = FactorGraph()
        >>> G.add_nodes_from(['a', 'b', 'c'])
        >>> phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4))
        >>> phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4))
        >>> G.add_factors(phi1, phi2)
        >>> G.add_nodes_from([phi1, phi2])
        >>> G.add_edges_from([('a', phi1), ('b', phi1),
        ...                   ('b', phi2), ('c', phi2)])
        >>> G.get_factors()
        >>> G.get_factors(node=phi1)
        """
        if node is None:
            return self.factors
        else:
            factor_nodes = self.get_factor_nodes()
            if node not in factor_nodes:
                raise ValueError('Factors are not associated with the '
                                 'corresponding node.')
            factors = list(filter(lambda x: set(x.scope()) == set(self.neighbors(node)),
                                  self.factors))
            return factors[0]


Instruction:
Can you generate the documentation for the following Python 3 function
def local_score(self, variable, parents):
        "Computes a score that measures how much a \
        given variable is \"influenced\" by a given list of potential parents."

        var_states = self.state_names[variable]
        var_cardinality = len(var_states)
        state_counts = self.state_counts(variable, parents)
        num_parents_states = float(len(state_counts.columns))

        score = 0
        for parents_state in state_counts:  # iterate over df columns (only 1 if no parents)
            conditional_sample_size = sum(state_counts[parents_state])

            score += (lgamma(self.equivalent_sample_size / num_parents_states) -
                      lgamma(conditional_sample_size + self.equivalent_sample_size / num_parents_states))

            for state in var_states:
                if state_counts[parents_state][state] > 0:
                    score += (lgamma(state_counts[parents_state][state] +
                                     self.equivalent_sample_size / (num_parents_states * var_cardinality)) -
                              lgamma(self.equivalent_sample_size / (num_parents_states * var_cardinality)))
        return score

Response:
Computes a score that measures how much a \
 given variable is influenced by a given list of potential parents.


Instruction:
Implement a function in Python 3 to
return the cliques in the junction tree which are a subset of the given nodes.

Response:
def _get_clique(self, junction_tree, nodes):
        """
        Extracting the cliques from the junction tree which are a subset of
        the given nodes.

        Parameters:
        ----------
        junction_tree: Junction tree
            from which the nodes are to be extracted.

        nodes: iterable container
            A container of nodes (list, dict, set, etc.).
        """

        return [clique for clique in junction_tree.nodes() if set(nodes).issubset(clique)][0]


Instruction:
Can you write a function in Python 3 where it
returns the evidence dict for a particular time slice and shift.

Response:
def _get_evidence(self, evidence_dict, time_slice, shift):
        """
        Getting the evidence belonging to a particular timeslice.

        Parameters:
        ----------
        evidence: dict
            a dict key, value pair as {var: state_of_var_observed}
            None if no evidence

        time: int
            the evidence corresponding to the time slice

        shift: int
            shifting the evidence corresponding to the given time slice.
        """
        if evidence_dict:
            return {(node[0], shift): evidence_dict[node] for node in evidence_dict if node[1] == time_slice}


Instruction:
Make a summary of the following Python 3 code
def _marginalize_factor(self, nodes, factor):
        marginalizing_nodes = list(set(factor.scope()).difference(nodes))
        return factor.marginalize(marginalizing_nodes, inplace=False)

Response: Marginalizes the factor selectively for a set of variables.


Instruction:
How would you code a function in Python 3 to
return the required factor for the given belief prop and evidence.

Response:
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


Instruction:
Explain what the following Python 3 code does
def _shift_factor(self, factor, shift):
        new_scope = self._shift_nodes(factor.scope(), shift)
        return DiscreteFactor(new_scope, factor.cardinality, factor.values)

Response: Shifts a factor to a certain required time slice.


Instruction:
Make a summary of the following Python 3 code
def forward_inference(self, variables, evidence=None, args=None):
        variable_dict = defaultdict(list)
        for var in variables:
            variable_dict[var[1]].append(var)

        time_range = max(variable_dict)
        if evidence:
            evid_time_range = max([time_slice for var, time_slice in evidence.keys()])
            time_range = max(time_range, evid_time_range)

        start_bp = BeliefPropagation(self.start_junction_tree)
        mid_bp = BeliefPropagation(self.one_and_half_junction_tree)
        evidence_0 = self._get_evidence(evidence, 0, 0)
        interface_nodes_dict = {}
        potential_dict = {}

        if evidence:
            interface_nodes_dict = {k: v for k, v in evidence_0.items() if k in self.interface_nodes_0}
        initial_factor = self._get_factor(start_bp, evidence_0)
        marginalized_factor = self._marginalize_factor(self.interface_nodes_0, initial_factor)
        potential_dict[0] = marginalized_factor
        self._update_belief(mid_bp, self.in_clique, marginalized_factor)

        if variable_dict[0]:
            factor_values = start_bp.query(variable_dict[0], evidence=evidence_0, joint=False)
        else:
            factor_values = {}

        for time_slice in range(1, time_range + 1):
            evidence_time = self._get_evidence(evidence, time_slice, 1)
            if interface_nodes_dict:
                evidence_time.update(interface_nodes_dict)

            if variable_dict[time_slice]:
                variable_time = self._shift_nodes(variable_dict[time_slice], 1)
                new_values = mid_bp.query(variable_time, evidence=evidence_time, joint=False)
                changed_values = {}
                for key in new_values.keys():
                    new_key = (key[0], time_slice)
                    new_factor = DiscreteFactor([new_key], new_values[key].cardinality, new_values[key].values)
                    changed_values[new_key] = new_factor
                factor_values.update(changed_values)

            clique_phi = self._get_factor(mid_bp, evidence_time)
            out_clique_phi = self._marginalize_factor(self.interface_nodes_1, clique_phi)
            new_factor = self._shift_factor(out_clique_phi, 0)
            potential_dict[time_slice] = new_factor
            mid_bp = BeliefPropagation(self.one_and_half_junction_tree)
            self._update_belief(mid_bp, self.in_clique, new_factor)

            if evidence_time:
                interface_nodes_dict = {(k[0], 0): v for k, v in evidence_time.items() if k in self.interface_nodes_1}
            else:
                interface_nodes_dict = {}

        if args == 'potential':
            return potential_dict

        return factor_values

Response: Forward inference method using belief propagation.


Instruction:
Given the following Python 3 function, write the documentation
def backward_inference(self, variables, evidence=None):
        variable_dict = defaultdict(list)
        for var in variables:
            variable_dict[var[1]].append(var)
        time_range = max(variable_dict)
        interface_nodes_dict = {}
        if evidence:
            evid_time_range = max([time_slice for var, time_slice in evidence.keys()])
            time_range = max(time_range, evid_time_range)
        end_bp = BeliefPropagation(self.start_junction_tree)
        potential_dict = self.forward_inference(variables, evidence, 'potential')
        update_factor = self._shift_factor(potential_dict[time_range], 1)
        factor_values = {}

        for time_slice in range(time_range, 0, -1):
            evidence_time = self._get_evidence(evidence, time_slice, 1)
            evidence_prev_time = self._get_evidence(evidence, time_slice - 1, 0)
            if evidence_prev_time:
                interface_nodes_dict = {k: v for k, v in evidence_prev_time.items() if k in self.interface_nodes_0}
            if evidence_time:
                evidence_time.update(interface_nodes_dict)
            mid_bp = BeliefPropagation(self.one_and_half_junction_tree)
            self._update_belief(mid_bp, self.in_clique, potential_dict[time_slice - 1])
            forward_factor = self._shift_factor(potential_dict[time_slice], 1)
            self._update_belief(mid_bp, self.out_clique, forward_factor, update_factor)

            if variable_dict[time_slice]:
                variable_time = self._shift_nodes(variable_dict[time_slice], 1)
                new_values = mid_bp.query(variable_time, evidence=evidence_time, joint=False)
                changed_values = {}
                for key in new_values.keys():
                    new_key = (key[0], time_slice)
                    new_factor = DiscreteFactor([new_key], new_values[key].cardinality, new_values[key].values)
                    changed_values[new_key] = new_factor
                factor_values.update(changed_values)

            clique_phi = self._get_factor(mid_bp, evidence_time)
            in_clique_phi = self._marginalize_factor(self.interface_nodes_0, clique_phi)
            update_factor = self._shift_factor(in_clique_phi, 1)

        out_clique_phi = self._shift_factor(update_factor, 0)
        self._update_belief(end_bp, self.start_interface_clique, potential_dict[0], out_clique_phi)
        evidence_0 = self._get_evidence(evidence, 0, 0)
        if variable_dict[0]:
            factor_values.update(end_bp.query(variable_dict[0], evidence_0, joint=False))
        return factor_values

Response: This method uses the backward inference method using belief propagation.


Instruction:
Given the following Python 3 function, write the documentation
def query(self, variables, evidence=None, args='exact'):
        if args == 'exact':
            return self.backward_inference(variables, evidence)

Response: Query Dynamic Bayesian Network using Interface Algorithm.


Instruction:
Can you generate the documentation for the following Python 3 function
def add_node(self, node, weight=None):

        # Check for networkx 2.0 syntax
        if isinstance(node, tuple) and len(node) == 2 and isinstance(node[1], dict):
            node, attrs = node
            if attrs.get('weight', None) is not None:
                attrs['weight'] = weight
        else:
            attrs = {'weight': weight}

        super(DAG, self).add_node(node, weight=weight)

Response: Add a node to the DAG.


Instruction:
Can you write a function in Python 3 where it
adds multiple nodes to the Graph.

Response:
def add_nodes_from(self, nodes, weights=None):
        """
        Add multiple nodes to the Graph.

        **The behviour of adding weights is different than in networkx.

        Parameters
        ----------
        nodes: iterable container
            A container of nodes (list, dict, set, or any hashable python
            object).

        weights: list, tuple (default=None)
            A container of weights (int, float). The weight value at index i
            is associated with the variable at index i.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG()
        >>> G.add_nodes_from(nodes=['A', 'B', 'C'])
        >>> sorted(G.nodes())
        ['A', 'B', 'C']

        Adding nodes with weights:
        >>> G.add_nodes_from(nodes=['D', 'E'], weights=[0.3, 0.6])
        >>> G.node['D']
        {'weight': 0.3}
        >>> G.node['E']
        {'weight': 0.6}
        >>> G.node['A']
        {'weight': None}
        """
        nodes = list(nodes)

        if weights:
            if len(nodes) != len(weights):
                raise ValueError("The number of elements in nodes and weights"
                                 "should be equal.")
            for index in range(len(nodes)):
                self.add_node(node=nodes[index], weight=weights[index])
        else:
            for node in nodes:
                self.add_node(node=node)


Instruction:
Here you have a function in Python 3, explain what it does
def add_edge(self, u, v, weight=None):
        super(DAG, self).add_edge(u, v, weight=weight)

Response: Add an edge between u and v.


Instruction:
Can you tell what is the following Python 3 function doing
def add_edges_from(self, ebunch, weights=None):
        ebunch = list(ebunch)

        if weights:
            if len(ebunch) != len(weights):
                raise ValueError("The number of elements in ebunch and weights"
                                 "should be equal")
            for index in range(len(ebunch)):
                self.add_edge(ebunch[index][0], ebunch[index][1],
                              weight=weights[index])
        else:
            for edge in ebunch:
                self.add_edge(edge[0], edge[1])

Response: Add all the edges from the given edge containers to the graph.


Instruction:
Write a Python 3 script for
creating a MoralGraph that contains all the immoralities in the DAG and all the parents of the node.

Response:
def moralize(self):
        """
        Removes all the immoralities in the DAG and creates a moral
        graph (UndirectedGraph).

        A v-structure X->Z<-Y is an immorality if there is no directed edge
        between X and Y.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG(ebunch=[('diff', 'grade'), ('intel', 'grade')])
        >>> moral_graph = G.moralize()
        >>> moral_graph.edges()
        [('intel', 'grade'), ('intel', 'diff'), ('grade', 'diff')]
        """
        moral_graph = UndirectedGraph(self.to_undirected().edges())

        for node in self.nodes():
            moral_graph.add_edges_from(
                itertools.combinations(self.get_parents(node), 2))

        return moral_graph


Instruction:
Create a Python 3 function for
returning a list of roots of the graph.

Response:
def get_roots(self):
        """
        Returns a list of roots of the graph.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> graph = DAG([('A', 'B'), ('B', 'C'), ('B', 'D'), ('E', 'B')])
        >>> graph.get_roots()
        ['A', 'E']
        """
        return [node for node, in_degree in dict(self.in_degree()).items() if in_degree == 0]


Instruction:
Can you generate a brief explanation for the following Python 3 code
def get_independencies(self, latex=False):
        independencies = Independencies()
        for start in (self.nodes()):
            rest = set(self.nodes()) - {start}
            for r in range(len(rest)):
                for observed in itertools.combinations(rest, r):
                    d_seperated_variables = rest - set(observed) - set(
                        self.active_trail_nodes(start, observed=observed)[start])
                    if d_seperated_variables:
                        independencies.add_assertions([start, d_seperated_variables, observed])

        independencies.reduce()

        if not latex:
            return independencies
        else:
            return independencies.latex_string()

Response: Computes the independencies of the current node.


Instruction:
Explain what the following Python 3 code does
def local_independencies(self, variables):
        def dfs(node):
            """
            Returns the descendents of node.

            This is a very simple dfs
            which does not remember which nodes it has visited.
            """
            descendents = []
            visit = [node]
            while visit:
                n = visit.pop()
                neighbors = list(self.neighbors(n))
                visit.extend(neighbors)
                descendents.extend(neighbors)
            return descendents

        independencies = Independencies()
        for variable in variables if isinstance(variables, (list, tuple)) else [variables]:
            non_descendents = set(self.nodes()) - {variable} - set(dfs(variable))
            parents = set(self.get_parents(variable))
            if non_descendents - parents:
                independencies.add_assertions([variable, non_descendents - parents, parents])
        return independencies

Response: Returns an Independencies containing the local independencies of each of the variables.


Instruction:
Write a Python 3 script for
checking whether the given model is I - equivalent.

Response:
def is_iequivalent(self, model):
        """
        Checks whether the given model is I-equivalent

        Two graphs G1 and G2 are said to be I-equivalent if they have same skeleton
        and have same set of immoralities.

        Note: For same skeleton different names of nodes can work but for immoralities
        names of nodes must be same

        Parameters
        ----------
        model : A DAG object, for which you want to check I-equivalence

        Returns
        --------
        boolean : True if both are I-equivalent, False otherwise

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG()
        >>> G.add_edges_from([('V', 'W'), ('W', 'X'),
        ...                   ('X', 'Y'), ('Z', 'Y')])
        >>> G1 = DAG()
        >>> G1.add_edges_from([('W', 'V'), ('X', 'W'),
        ...                    ('X', 'Y'), ('Z', 'Y')])
        >>> G.is_iequivalent(G1)
        True

        """
        if not isinstance(model, DAG):
            raise TypeError('model must be an instance of DAG')
        skeleton = nx.algorithms.isomorphism.GraphMatcher(self.to_undirected(), model.to_undirected())
        if skeleton.is_isomorphic() and self.get_immoralities() == model.get_immoralities():
            return True
        return False


Instruction:
Implement a Python 3 function for
returning a set of all the immoralities in the model.

Response:
def get_immoralities(self):
        """
        Finds all the immoralities in the model
        A v-structure X -> Z <- Y is an immorality if there is no direct edge between X and Y .

        Returns
        -------
        set: A set of all the immoralities in the model

        Examples
        ---------
        >>> from pgmpy.base import DAG
        >>> student = DAG()
        >>> student.add_edges_from([('diff', 'grade'), ('intel', 'grade'),
        ...                         ('intel', 'SAT'), ('grade', 'letter')])
        >>> student.get_immoralities()
        {('diff','intel')}
        """
        immoralities = set()
        for node in self.nodes():
            for parents in itertools.combinations(self.predecessors(node), 2):
                if not self.has_edge(parents[0], parents[1]) and not self.has_edge(parents[1], parents[0]):
                    immoralities.add(tuple(sorted(parents)))
        return immoralities


Instruction:
Write a Python 3 function that can
return True if there is any active trail between start and end node.

Response:
def is_active_trail(self, start, end, observed=None):
        """
        Returns True if there is any active trail between start and end node
        Parameters
        ----------
        start : Graph Node
        end : Graph Node
        observed : List of nodes (optional)
            If given the active trail would be computed assuming these nodes to be observed.
        additional_observed : List of nodes (optional)
            If given the active trail would be computed assuming these nodes to be observed along with
            the nodes marked as observed in the model.
        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> student = DAG()
        >>> student.add_nodes_from(['diff', 'intel', 'grades', 'letter', 'sat'])
        >>> student.add_edges_from([('diff', 'grades'), ('intel', 'grades'), ('grades', 'letter'),
        ...                         ('intel', 'sat')])
        >>> student.is_active_trail('diff', 'intel')
        False
        >>> student.is_active_trail('grades', 'sat')
        True
        """
        if end in self.active_trail_nodes(start, observed)[start]:
            return True
        else:
            return False


Instruction:
Explain what the following Python 3 code does
def get_markov_blanket(self, node):
        children = self.get_children(node)
        parents = self.get_parents(node)
        blanket_nodes = children + parents
        for child_node in children:
            blanket_nodes.extend(self.get_parents(child_node))
        blanket_nodes = set(blanket_nodes)
        blanket_nodes.remove(node)
        return list(blanket_nodes)

Response: Returns a markov blanket for a random variable.


Instruction:
Create a Python 3 function to
return a dictionary with the given variables as keys and all the nodes reachable from that respective variable as values and all the nodes reachable from that respective variable as values.

Response:
def active_trail_nodes(self, variables, observed=None):
        """
        Returns a dictionary with the given variables as keys and all the nodes reachable
        from that respective variable as values.
        Parameters
        ----------
        variables: str or array like
            variables whose active trails are to be found.
        observed : List of nodes (optional)
            If given the active trails would be computed assuming these nodes to be observed.
        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> student = DAG()
        >>> student.add_nodes_from(['diff', 'intel', 'grades'])
        >>> student.add_edges_from([('diff', 'grades'), ('intel', 'grades')])
        >>> student.active_trail_nodes('diff')
        {'diff': {'diff', 'grades'}}
        >>> student.active_trail_nodes(['diff', 'intel'], observed='grades')
        {'diff': {'diff', 'intel'}, 'intel': {'diff', 'intel'}}
        References
        ----------
        Details of the algorithm can be found in 'Probabilistic Graphical Model
        Principles and Techniques' - Koller and Friedman
        Page 75 Algorithm 3.1
        """
        if observed:
            observed_list = observed if isinstance(observed, (list, tuple)) else [observed]
        else:
            observed_list = []
        ancestors_list = self._get_ancestors_of(observed_list)

        # Direction of flow of information
        # up ->  from parent to child
        # down -> from child to parent

        active_trails = {}
        for start in variables if isinstance(variables, (list, tuple)) else [variables]:
            visit_list = set()
            visit_list.add((start, 'up'))
            traversed_list = set()
            active_nodes = set()
            while visit_list:
                node, direction = visit_list.pop()
                if (node, direction) not in traversed_list:
                    if node not in observed_list:
                        active_nodes.add(node)
                    traversed_list.add((node, direction))
                    if direction == 'up' and node not in observed_list:
                        for parent in self.predecessors(node):
                            visit_list.add((parent, 'up'))
                        for child in self.successors(node):
                            visit_list.add((child, 'down'))
                    elif direction == 'down':
                        if node not in observed_list:
                            for child in self.successors(node):
                                visit_list.add((child, 'down'))
                        if node in ancestors_list:
                            for parent in self.predecessors(node):
                                visit_list.add((parent, 'up'))
            active_trails[start] = active_nodes
        return active_trails


Instruction:
Write a Python 3 script for
returning a dictionary of all the observed nodes and their ancestors including the node itself.

Response:
def _get_ancestors_of(self, obs_nodes_list):
        """
        Returns a dictionary of all ancestors of all the observed nodes including the
        node itself.
        Parameters
        ----------
        obs_nodes_list: string, list-type
            name of all the observed nodes
        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> model = DAG([('D', 'G'), ('I', 'G'), ('G', 'L'),
        ...                        ('I', 'L')])
        >>> model._get_ancestors_of('G')
        {'D', 'G', 'I'}
        >>> model._get_ancestors_of(['G', 'I'])
        {'D', 'G', 'I'}
        """
        if not isinstance(obs_nodes_list, (list, tuple)):
            obs_nodes_list = [obs_nodes_list]

        for node in obs_nodes_list:
            if node not in self.nodes():
                raise ValueError('Node {s} not in not in graph'.format(s=node))

        ancestors_list = set()
        nodes_list = set(obs_nodes_list)
        while nodes_list:
            node = nodes_list.pop()
            if node not in ancestors_list:
                nodes_list.update(self.predecessors(node))
            ancestors_list.add(node)
        return ancestors_list


Instruction:
Create a Python 3 function for
adding an edge between two clique nodes.

Response:
def add_edge(self, u, v, **kwargs):
        """
        Add an edge between two clique nodes.

        Parameters
        ----------
        u, v: nodes
            Nodes can be any list or set or tuple of nodes forming a clique.

        Examples
        --------
        >>> from pgmpy.models import JunctionTree
        >>> G = JunctionTree()
        >>> G.add_nodes_from([('a', 'b', 'c'), ('a', 'b'), ('a', 'c')])
        >>> G.add_edges_from([(('a', 'b', 'c'), ('a', 'b')),
        ...                   (('a', 'b', 'c'), ('a', 'c'))])
        """
        if u in self.nodes() and v in self.nodes() and nx.has_path(self, u, v):
            raise ValueError('Addition of edge between {u} and {v} forms a cycle breaking the '
                             'properties of Junction Tree'.format(u=str(u), v=str(v)))

        super(JunctionTree, self).add_edge(u, v, **kwargs)


Instruction:
Given the following Python 3 function, write the documentation
def check_model(self):
        if not nx.is_connected(self):
            raise ValueError('The Junction Tree defined is not fully connected.')

        return super(JunctionTree, self).check_model()

Response: Check the model for various errors.


Instruction:
Write a Python 3 function for
returning a copy of JunctionTree.

Response:
def copy(self):
        """
        Returns a copy of JunctionTree.

        Returns
        -------
        JunctionTree : copy of JunctionTree

        Examples
        --------
        >>> import numpy as np
        >>> from pgmpy.factors.discrete import DiscreteFactor
        >>> from pgmpy.models import JunctionTree
        >>> G = JunctionTree()
        >>> G.add_edges_from([(('a', 'b', 'c'), ('a', 'b')), (('a', 'b', 'c'), ('a', 'c'))])
        >>> phi1 = DiscreteFactor(['a', 'b'], [1, 2], np.random.rand(2))
        >>> phi2 = DiscreteFactor(['a', 'c'], [1, 2], np.random.rand(2))
        >>> G.add_factors(phi1,phi2)
        >>> modelCopy = G.copy()
        >>> modelCopy.edges()
        [(('a', 'b'), ('a', 'b', 'c')), (('a', 'c'), ('a', 'b', 'c'))]
        >>> G.factors
        [<DiscreteFactor representing phi(a:1, b:2) at 0xb720ee4c>,
         <DiscreteFactor representing phi(a:1, c:2) at 0xb4e1e06c>]
        >>> modelCopy.factors
        [<DiscreteFactor representing phi(a:1, b:2) at 0xb4bd11ec>,
         <DiscreteFactor representing phi(a:1, c:2) at 0xb4bd138c>]

        """
        copy = JunctionTree(self.edges())
        copy.add_nodes_from(self.nodes())
        if self.factors:
            factors_copy = [factor.copy() for factor in self.factors]
            copy.add_factors(*factors_copy)
        return copy


Instruction:
Write a Python 3 script for
generating ProbModelXML lines for a given model.

Response:
def generate_probmodelxml(model, encoding='utf-8', prettyprint=True):
    """
    Generate ProbModelXML lines for model.

    Parameters
    ----------
    model : Graph
        The Bayesian or Markov Model
    encoding : string (optional)
        Encoding for text data
    prettyprint: bool (optional)
        If True uses line breaks and indenting in output XML.

    Examples
    --------
    >>> G = nx.path_graph(5)
    >>> s = pgmpy.readwrite.generate_ProbModelXML(G) # doctest: +SKIP
    >>> for line in pgmpy.readwrite.generate_ProbModelXML(G):  #doctest: +SKIP
    ...     print(line)
    """
    writer = ProbModelXMLWriter(G, encoding=encoding, prettyprint=prettyprint)
    for line in str(writer).splitlines():
        yield line


Instruction:
Write a Python 3 function for
writing a ProbModelXML model to path.

Response:
def write_probmodelxml(model, path, encoding='utf-8', prettyprint=True):
    """
    Write model in ProbModelXML format to path.

    Parameters
    ----------
    model : A NetworkX graph
            Bayesian network or Markov network
    path : file or string
            File or filename to write.
            Filenames ending in .gz or .bz2 will be compressed.
    encoding : string (optional)
            Encoding for text data.
    prettyprint : bool (optional)
            If True use line breaks and indenting in output XML.

    Examples
    --------
    >>> G = nx.path_graph(4)
    >>> pgmpy.readwrite.write_probmodelxml(G, "test.probmodelxml")
    """
    writer = ProbModelXMLWriter(model, path, encoding=encoding,
                                prettyprint=prettyprint)
    writer.dump(path)


Instruction:
Given the following Python 3 function, write the documentation
def get_probmodel_data(model):
    if not isinstance(model, BayesianModel):
        raise TypeError("Model must an instance of BayesianModel.")
    model_data = {'probnet': {'type': 'BayesianNetwork', 'Variables': {}}}

    variables = model.nodes()
    for var in variables:
        model_data['probnet']['Variables'][var] = model.node[var]

    model_data['probnet']['edges'] = {}
    edges = model.edges()
    for edge in edges:
        model_data['probnet']['edges'][str(edge)] = model.adj[edge[0]][edge[1]]

    model_data['probnet']['Potentials'] = []
    cpds = model.get_cpds()
    for cpd in cpds:
        potential_dict = {}
        potential_dict['Variables'] = {}
        evidence = cpd.variables[:0:-1]
        if evidence:
            potential_dict['Variables'][cpd.variable] = evidence
        else:
            potential_dict['Variables'][cpd.variable] = []
        potential_dict['type'] = "Table"
        potential_dict['role'] = "conditionalProbability"
        potential_dict['Values'] = " ".join([str(val) for val in cpd.values.ravel().astype(float)]) + " "
        model_data['probnet']['Potentials'].append(potential_dict)

    return model_data

Response: Returns the model_data dictionary containing model data for the given Bayesian model.


Instruction:
Can you tell what is the following Python 3 function doing
def _add_additional_properties(position, properties_dict):
        add_prop = etree.SubElement(position, 'AdditionalProperties')
        for key, value in properties_dict.items():
            etree.SubElement(add_prop, 'Property', attrib={'name': key, 'value': value})

Response: Adds AdditionalProperties to the ProbModelXML.


Instruction:
Write a Python 3 function for
adding a node to the ProbModelXML.

Response:
def _add_variable(self, variable):
        """
        Adds a node to the ProbModelXML.
        """
        # TODO: Add feature for accepting additional properties of states.
        variable_data = self.data['probnet']['Variables'][variable]
        variable_element = etree.SubElement(self.variables, 'Variable', attrib={'name': variable,
                                                                                'type': variable_data['type'],
                                                                                'role': variable_data['role']})

        try:
            etree.SubElement(variable_element, 'Comment').text = variable_data['Comment']
        except KeyError:
            pass
        try:
            etree.SubElement(variable_element, 'Coordinates', variable_data['Coordinates'])
        except KeyError:
            pass

        try:
            for key, value in sorted(variable_data['AdditionalProperties'].items()):
                etree.SubElement(variable_element, 'Property', attrib={'name': key, 'value': value})
        except KeyError:
            etree.SubElement(variable_element, 'AdditionalProperties')
        states = etree.SubElement(variable_element, 'States')
        for s in sorted(variable_data['States']):
            state = etree.SubElement(states, 'State', attrib={'name': s})
            try:
                self._add_additional_properties(state, variable_data['States'][s]['AdditionalProperties'])
            except KeyError:
                etree.SubElement(state, 'AdditionalProperties')


Instruction:
How would you explain what the following Python 3 function does
def _add_link(self, edge):
        edge_data = self.data['probnet']['edges'][edge]
        if isinstance(edge, six.string_types):
            edge = eval(edge)
        link = etree.SubElement(self.links, 'Link', attrib={'var1': edge[0], 'var2': edge[1],
                                                            'directed': edge_data['directed']})
        try:
            etree.SubElement(link, 'Comment').text = edge_data['Comment']
        except KeyError:
            pass
        try:
            etree.SubElement(link, 'Label').text = edge_data['Label']
        except KeyError:
            pass
        try:
            self._add_additional_properties(link, edge_data['AdditionalProperties'])
        except KeyError:
            etree.SubElement(link, 'AdditionalProperties')

Response: Adds an edge to the ProbModelXML.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _add_constraint(self, constraint):
        constraint_data = self.data['probnet']['AdditionalConstraints'][constraint]
        constraint_element = etree.SubElement(
            self.additional_constraints, 'Constraint', attrib={'name': constraint})
        for argument in sorted(constraint_data):
            name = argument
            value = constraint_data[name]
            etree.SubElement(constraint_element, 'Argument', attrib={'name': name, 'value': value})

Response: Adds constraint to the ProbModelXML.


Instruction:
Here you have a function in Python 3, explain what it does
def _add_decision_criteria(self, criteria_dict):
        decision_tag = etree.SubElement(self.xml, 'DecisionCriteria', attrib={})
        for criteria in sorted(criteria_dict):
            criteria_tag = etree.SubElement(decision_tag, 'Criterion', attrib={'name': criteria})
            self._add_additional_properties(criteria_tag, criteria_dict[criteria])

Response: Adds Decision Criteria to the ProbModelXML.


Instruction:
Implement a function in Python 3 to
add Potentials to the ProbModelXML.

Response:
def _add_potential(self, potential, parent_tag):
        """
        Adds Potentials to the ProbModelXML.

        Parameters
        ----------
        potential: dict
            Dictionary containing Potential data.
            For example: {'role': 'Utility',
                          'Variables': ['D0', 'D1', 'C0', 'C1'],
                          'type': 'Tree/ADD',
                          'UtilityVaribale': 'U1'}
        parent_tag: etree Element
            etree element which would contain potential tag
            For example: <Element Potentials at 0x7f315fc44b08>
                         <Element Branch at 0x7f315fc44c88>
                         <Element Branch at 0x7f315fc44d88>
                         <Element Subpotentials at 0x7f315fc44e48>

        Examples
        --------
        >>> writer = ProbModelXMLWriter(model)
        >>> writer._add_potential(potential, parent_tag)
        """
        potential_type = potential['type']
        try:
            potential_tag = etree.SubElement(parent_tag, 'Potential', attrib={
                'type': potential['type'], 'role': potential['role']})
        except KeyError:
            potential_tag = etree.SubElement(parent_tag, 'Potential', attrib={
                'type': potential['type']})
        self._add_element(potential, 'Comment', potential_tag)
        if 'AdditionalProperties' in potential:
            self._add_additional_properties(potential_tag, potential['AdditionalProperties'])
        if potential_type == "delta":
            etree.SubElement(potential_tag, 'Variable', attrib={'name': potential['Variable']})
            self._add_element(potential, 'State', potential_tag)
            self._add_element(potential, 'StateIndex', potential_tag)
            self._add_element(potential, 'NumericValue', potential_tag)
        else:
            if 'UtilityVariable' in potential:
                etree.SubElement(potential_tag, 'UtilityVariable', attrib={
                    'name': potential['UtilityVariable']})
            if 'Variables' in potential:
                variable_tag = etree.SubElement(potential_tag, 'Variables')
                for var in sorted(potential['Variables']):
                    etree.SubElement(variable_tag, 'Variable', attrib={'name': var})
                    for child in sorted(potential['Variables'][var]):
                        etree.SubElement(variable_tag, 'Variable', attrib={'name': child})
            self._add_element(potential, 'Values', potential_tag)
            if 'UncertainValues' in potential:
                value_tag = etree.SubElement(potential_tag, 'UncertainValues', attrib={})
                for value in sorted(potential['UncertainValues']):
                    try:
                        etree.SubElement(value_tag, 'Value', attrib={
                            'distribution': value['distribution'],
                            'name': value['name']}).text = value['value']
                    except KeyError:
                        etree.SubElement(value_tag, 'Value', attrib={
                            'distribution': value['distribution']}).text = value['value']
            if 'TopVariable' in potential:
                etree.SubElement(potential_tag, 'TopVariable', attrib={'name': potential['TopVariable']})
            if 'Branches' in potential:
                branches_tag = etree.SubElement(potential_tag, 'Branches')
                for branch in potential['Branches']:
                    branch_tag = etree.SubElement(branches_tag, 'Branch')
                    if 'States' in branch:
                        states_tag = etree.SubElement(branch_tag, 'States')
                        for state in sorted(branch['States']):
                            etree.SubElement(states_tag, 'State', attrib={'name': state['name']})
                    if 'Potential' in branch:
                        self._add_potential(branch['Potential'], branch_tag)
                    self._add_element(potential, 'Label', potential_tag)
                    self._add_element(potential, 'Reference', potential_tag)
                    if 'Thresholds' in branch:
                        thresholds_tag = etree.SubElement(branch_tag, 'Thresholds')
                        for threshold in branch['Thresholds']:
                            try:
                                etree.SubElement(thresholds_tag, 'Threshold', attrib={
                                    'value': threshold['value'], 'belongsTo': threshold['belongsTo']})
                            except KeyError:
                                etree.SubElement(thresholds_tag, 'Threshold', attrib={
                                    'value': threshold['value']})
            self._add_element(potential, 'Model', potential_tag)
            self._add_element(potential, 'Coefficients', potential_tag)
            self._add_element(potential, 'CovarianceMatrix', potential_tag)
            if 'Subpotentials' in potential:
                subpotentials = etree.SubElement(potential_tag, 'Subpotentials')
                for subpotential in potential['Subpotentials']:
                    self._add_potential(subpotential, subpotentials)
            if 'Potential' in potential:
                self._add_potential(potential['Potential'], potential_tag)
            if 'NumericVariables' in potential:
                numvar_tag = etree.SubElement(potential_tag, 'NumericVariables')
                for var in sorted(potential['NumericVariables']):
                    etree.SubElement(numvar_tag, 'Variable', attrib={'name': var})


Instruction:
Can you generate the documentation for the following Python 3 function
def dump(self, stream):
        if self.prettyprint:
            self.indent(self.xml)
        document = etree.ElementTree(self.xml)
        header = '<?xml version="1.0" encoding="%s"?>' % self.encoding
        stream.write(header.encode(self.encoding))
        document.write(stream, encoding=self.encoding)

Response: Dumps the data to stream after appending header.


Instruction:
How would you code a function in Python 3 to
write the xml data into the file.

Response:
def write_file(self, filename):
        """
        Write the xml data into the file.

        Parameters
        ----------
        filename: Name of the file.

        Examples
        -------
        >>> writer = ProbModelXMLWriter(model)
        >>> writer.write_file(test_file)
        """
        writer = self.__str__()[:-1].decode('utf-8')
        with open(filename, 'w') as fout:
            fout.write(writer)


Instruction:
Implement a Python 3 function for
creating a BayesianModel object based on the type of ProbNetXML passed to ProbModelXMLReader class.

Response:
def create_probnet(self):
        """
        Returns a BayesianModel or MarkovModel object depending on the
        type of ProbModelXML passed to ProbModelXMLReader class.
        """
        self.probnet = {}
        # Add general properties
        probnet_elem = self.xml.find('ProbNet')
        self.probnet['type'] = probnet_elem.attrib['type']
        if probnet_elem.find('Comment') is not None:
            self.add_comment(probnet_elem.find('Comment').text)
        if probnet_elem.find('Language') is not None:
            self.add_language(probnet_elem.find('Language').text)
        if probnet_elem.find('AdditionalProperties') is not None:
            self.probnet['AdditionalProperties'] = {}
            for prop in probnet_elem.find('AdditionalProperties'):
                self.add_additional_property(self.probnet['AdditionalProperties'], prop)

        # Add additional Constraints
        self.probnet['AdditionalConstraints'] = {}
        for constraint in probnet_elem.findall('AdditionalConstraints/Constraint'):
            self.add_probnet_additionalconstraints(constraint)

        # Add Decision Criterion
        self.probnet['DecisionCriteria'] = {}
        for criterion in probnet_elem.findall('DecisionCriteria/Criterion'):
            self.add_criterion(criterion)

        # Add nodes
        self.probnet['Variables'] = {}
        for variable in probnet_elem.find('Variables'):
            self.add_node(variable)

        # Add edges
        self.probnet['edges'] = {}
        for edge in probnet_elem.findall('Links/Link'):
            self.add_edge(edge)

        # Add CPD
        self.probnet['Potentials'] = []
        for potential in probnet_elem.findall('Potentials/Potential'):
            probnet_dict = {}
            self.add_potential(potential, probnet_dict)
            self.probnet['Potentials'].append(probnet_dict)


Instruction:
Write a Python 3 function that can
add Additional Constraints to the ProbModel XML file.

Response:
def add_probnet_additionalconstraints(self, constraint):
        """
        Adds Additional Constraints to the probnet dict.

        Parameters
        ----------
        criterion: <Element Constraint at AdditionalConstraints Node in XML>
            etree Element consisting Constraint tag.

        Examples
        -------
        >>> reader = ProbModelXMLReader()
        >>> reader.add_additionalconstraints(constraint)
        """
        constraint_name = constraint.attrib['name']
        self.probnet['AdditionalConstraints'][constraint_name] = {}
        for argument in constraint.findall('Argument'):
            argument_name = argument.attrib['name']
            argument_value = argument.attrib['value']
            self.probnet['AdditionalConstraints'][constraint_name][argument_name] = argument_value


Instruction:
Can you generate the documentation for the following Python 3 function
def add_criterion(self, criterion):
        criterion_name = criterion.attrib['name']
        self.probnet['DecisionCriteria'][criterion_name] = {}
        if criterion.find('AdditionalProperties/Property') is not None:
            for prop in criterion.findall('AdditionalProperties/Property'):
                prop_name = prop.attrib['name']
                prop_value = prop.attrib['value']
                self.probnet['DecisionCriteria'][criterion_name]['AdditionalProperties'][prop_name] = prop_value

Response: Adds Decision Criteria to the probnet dict.


Instruction:
Given the following Python 3 function, write the documentation
def add_node(self, variable):
        # TODO: Do some checks with variable type and roles. Right now I don't know when they are to be used.
        variable_name = variable.attrib['name']
        self.probnet['Variables'][variable_name] = {}
        self.probnet['Variables'][variable_name]['type'] = variable.attrib['type']
        self.probnet['Variables'][variable_name]['role'] = variable.attrib['role']
        if variable.find('Comment') is not None:
            self.probnet['Variables'][variable_name]['Comment'] = variable.find('Comment').text
        if variable.find('Coordinates') is not None:
            self.probnet['Variables'][variable_name]['Coordinates'] = variable.find('Coordinates').attrib
        if variable.find('AdditionalProperties/Property') is not None:
            self.probnet['Variables'][variable_name]['AdditionalProperties'] = {}
            for prop in variable.findall('AdditionalProperties/Property'):
                self.probnet['Variables'][variable_name]['AdditionalProperties'][prop.attrib['name']] = \
                    prop.attrib['value']
        if variable.find('States/State') is None:
            warnings.warn("States not available for node: " + variable_name)
        else:
            self.probnet['Variables'][variable_name]['States'] = {state.attrib['name']: {
                prop.attrib['name']: prop.attrib['value'] for
                prop in state.findall('AdditionalProperties/Property')} for state in variable.findall(
                'States/State')}

Response: Adds a variable to the ProbModel XML file.


Instruction:
Can you implement a function in Python 3 that
adds Edges to the ProbModel XML file.

Response:
def add_edge(self, edge):
        """
        Adds Edges to the probnet dict.

        Parameters
        ----------
        edge: <Element Link at Links Node in XML>
            etree Element consisting Variable tag.

        Examples
        -------
        >>> reader = ProbModelXMLReader()
        >>> reader.add_edge(edge)
        """
        var1 = edge.findall('Variable')[0].attrib['name']
        var2 = edge.findall('Variable')[1].attrib['name']
        self.probnet['edges'][(var1, var2)] = {}
        self.probnet['edges'][(var1, var2)]['directed'] = edge.attrib['directed']
        # TODO: check for the case of undirected graphs if we need to add to both elements of the dic for a single edge.
        if edge.find('Comment') is not None:
            self.probnet['edges'][(var1, var2)]['Comment'] = edge.find('Comment').text
        if edge.find('Label') is not None:
            self.probnet['edges'][(var1, var2)]['Label'] = edge.find('Label').text
        if edge.find('AdditionalProperties/Property') is not None:
            self.probnet['edges'][(var1, var2)]['AdditionalProperties'] = {}
            for prop in edge.findall('AdditionalProperties/Property'):
                self.probnet['edges'][(var1, var2)]['AdditionalProperties'][prop.attrib['name']] = prop.attrib['value']


Instruction:
Explain what the following Python 3 code does
def add_potential(self, potential, potential_dict):
        potential_type = potential.attrib['type']
        potential_dict['type'] = potential_type
        try:
            potential_dict['role'] = potential.attrib['role']
        except KeyError:
            pass
        if potential.find('Comment') is not None:
            potential_dict['Comment'] = potential.find('Comment').text
        for prop in potential.findall('AdditionalProperties/Property'):
            potential_dict['AdditionalProperties'][prop.attrib['name']] = prop.attrib['value']
        if potential_type == "delta":
            potential_dict['Variable'] = potential.find('Variable').attrib['name']
            if potential.find('State') is not None:
                potential_dict['State'] = potential.find('State').text
            if potential.find('StateIndex') is not None:
                potential_dict['StateIndex'] = potential.find('StateIndex').text
            if potential.find('NumericValue') is not None:
                potential_dict['NumericValue'] = potential.find('NumericValue').text
        else:
            if potential.find('UtilityVariable') is not None:
                potential_dict['UtilityVaribale'] = potential.find('UtilityVariable').attrib['name']
            if len(potential.findall('Variables/Variable')):
                potential_dict['Variables'] = {}
                var_list = []
                for var in potential.findall('Variables/Variable'):
                    var_list.append(var.attrib['name'])
                potential_dict['Variables'][var_list[0]] = var_list[1:]
            if potential.find('Values') is not None:
                potential_dict['Values'] = potential.find('Values').text
            if len(potential.findall('UncertainValues/Value')):
                potential_dict['UncertainValues'] = []
                for value in potential.findall('UncertainValues/Value'):
                    try:
                        potential_dict['UncertainValues'].append(
                            {'distribution': value.attrib['distribution'], 'name': value.attrib['name'],
                             'value': value.text})
                    except KeyError:
                        potential_dict['UncertainValues'].append(
                            {'distribution': value.attrib['distribution'], 'value': value.text})
            if potential.find('TopVariable') is not None:
                potential_dict['TopVariable'] = potential.find('TopVariable').attrib['name']

            if len(potential.findall('Branches/Branch')):
                potential_dict['Branches'] = []
                for branch in potential.findall('Branches/Branch'):
                    branch_dict = {}
                    if len(branch.findall('States/State')):
                        states = []
                        for state in branch.findall('States/State'):
                            states.append({'name': state.attrib['name']})
                        branch_dict['States'] = states
                    if branch.find('Potential') is not None:
                        branch_potential = {}
                        self.add_potential(branch.find('Potential'), branch_potential)
                        branch_dict['Potential'] = branch_potential
                    if branch.find('Label') is not None:
                        label = branch.find('Label').text
                        branch_dict['Label'] = label
                    if branch.find('Reference') is not None:
                        reference = branch.find('Reference').text
                        branch_dict['Reference'] = reference
                    if len(branch.findall('Thresholds/Threshold')):
                        thresholds = []
                        for threshold in branch.findall('Thresholds/Threshold'):
                            try:
                                thresholds.append({
                                    'value': threshold.attrib['value'], 'belongsTo': threshold.attrib['belongsTo']})
                            except KeyError:
                                thresholds.append({'value': threshold.attrib['value']})
                        branch_dict['Thresholds'] = thresholds
                    potential_dict['Branches'].append(branch_dict)

            if potential.find('Model') is not None:
                potential_dict['Model'] = potential.find('Model').text
            if len(potential.findall('Subpotentials/Potential')):
                potential_dict['Subpotentials'] = []
                for subpotential in potential.findall('Subpotentials/Potential'):
                    subpotential_dict = {}
                    self.add_potential(subpotential, subpotential_dict)
                    potential_dict['Subpotentials'].append(subpotential_dict)
            if potential.find('Coefficients') is not None:
                potential_dict['Coefficients'] = potential.find('Coefficients').text
            if potential.find('CovarianceMatrix') is not None:
                potential_dict['CovarianceMatrix'] = potential.find('CovarianceMatrix').text
            if potential.find('Potential') is not None:
                potential_dict['Potential'] = {}
                self.add_potential(potential.find('Potential'), potential_dict['Potential'])
            if len(potential.findall('NumericVariables/Variable')):
                potential_dict['NumericVariables'] = []
                for variable in potential.findall('NumericVariables/Variable'):
                    potential_dict['NumericVariables'].append(variable.attrib['name'])

Response: Adds Potential to the dict.


Instruction:
How would you explain what the following Python 3 function does
def get_model(self):
        if self.probnet.get('type') == "BayesianNetwork":
            model = BayesianModel()
            model.add_nodes_from(self.probnet['Variables'].keys())
            model.add_edges_from(self.probnet['edges'].keys())

            tabular_cpds = []
            cpds = self.probnet['Potentials']
            for cpd in cpds:
                var = list(cpd['Variables'].keys())[0]
                states = self.probnet['Variables'][var]['States']
                evidence = cpd['Variables'][var]
                evidence_card = [len(self.probnet['Variables'][evidence_var]['States'])
                                 for evidence_var in evidence]
                arr = list(map(float, cpd['Values'].split()))
                values = np.array(arr)
                values = values.reshape((len(states), values.size//len(states)))
                tabular_cpds.append(TabularCPD(var, len(states), values, evidence, evidence_card))

            model.add_cpds(*tabular_cpds)

            variables = model.nodes()
            for var in variables:
                for prop_name, prop_value in self.probnet['Variables'][var].items():
                    model.node[var][prop_name] = prop_value
            edges = model.edges()

            if nx.__version__.startswith('1'):
                for edge in edges:
                    for prop_name, prop_value in self.probnet['edges'][edge].items():
                        model.edge[edge[0]][edge[1]][prop_name] = prop_value
            else:
                for edge in edges:
                    for prop_name, prop_value in self.probnet['edges'][edge].items():
                        model.adj[edge[0]][edge[1]][prop_name] = prop_value
            return model
        else:
            raise ValueError("Please specify only Bayesian Network.")

Response: Returns the model instance of the ProbModel.


Instruction:
How would you code a function in Python 3 to
return a generator that yields all possible directed acyclic graphs with a given set of nodes.

Response:
def all_dags(self, nodes=None):
        """
        Computes all possible directed acyclic graphs with a given set of nodes,
        sparse ones first. `2**(n*(n-1))` graphs need to be searched, given `n` nodes,
        so this is likely not feasible for n>6. This is a generator.

        Parameters
        ----------
        nodes: list of nodes for the DAGs (optional)
            A list of the node names that the generated DAGs should have.
            If not provided, nodes are taken from data.

        Returns
        -------
        dags: Generator object for nx.DiGraphs
            Generator that yields all acyclic nx.DiGraphs, ordered by number of edges. Empty DAG first.

        Examples
        --------
        >>> import pandas as pd
        >>> from pgmpy.estimators import ExhaustiveSearch
        >>> s = ExhaustiveSearch(pd.DataFrame(data={'Temperature': [23, 19],
                                                    'Weather': ['sunny', 'cloudy'],
                                                    'Humidity': [65, 75]}))
        >>> list(s.all_dags())
        [<networkx.classes.digraph.DiGraph object at 0x7f6955216438>,
         <networkx.classes.digraph.DiGraph object at 0x7f6955216518>,
        ....
        >>> [dag.edges() for dag in s.all_dags()]
        [[], [('Humidity', 'Temperature')], [('Humidity', 'Weather')],
        [('Temperature', 'Weather')], [('Temperature', 'Humidity')],
        ....
        [('Weather', 'Humidity'), ('Weather', 'Temperature'), ('Temperature', 'Humidity')]]

        """
        if nodes is None:
            nodes = sorted(self.state_names.keys())
        if len(nodes) > 6:
            warn("Generating all DAGs of n nodes likely not feasible for n>6!")
            warn("Attempting to search through {0} graphs".format(2**(len(nodes)*(len(nodes)-1))))

        edges = list(combinations(nodes, 2))  # n*(n-1) possible directed edges
        edges.extend([(y, x) for x, y in edges])
        all_graphs = powerset(edges)  # 2^(n*(n-1)) graphs

        for graph_edges in all_graphs:
            graph = nx.DiGraph()
            graph.add_nodes_from(nodes)
            graph.add_edges_from(graph_edges)
            if nx.is_directed_acyclic_graph(graph):
                yield graph


Instruction:
Create a Python 3 function for
computing a list of DAGs and their structure scores ordered by score.

Response:
def all_scores(self):
        """
        Computes a list of DAGs and their structure scores, ordered by score.

        Returns
        -------
        list: a list of (score, dag) pairs
            A list of (score, dag)-tuples, where score is a float and model a acyclic nx.DiGraph.
            The list is ordered by score values.

        Examples
        --------
        >>> import pandas as pd
        >>> import numpy as np
        >>> from pgmpy.estimators import ExhaustiveSearch, K2Score
        >>> # create random data sample with 3 variables, where B and C are identical:
        >>> data = pd.DataFrame(np.random.randint(0, 5, size=(5000, 2)), columns=list('AB'))
        >>> data['C'] = data['B']
        >>> searcher = ExhaustiveSearch(data, scoring_method=K2Score(data))
        >>> for score, model in searcher.all_scores():
        ...   print("{0}\t{1}".format(score, model.edges()))
        -24234.44977974726      [('A', 'B'), ('A', 'C')]
        -24234.449760691063     [('A', 'B'), ('C', 'A')]
        -24234.449760691063     [('A', 'C'), ('B', 'A')]
        -24203.700955937973     [('A', 'B')]
        -24203.700955937973     [('A', 'C')]
        -24203.700936881774     [('B', 'A')]
        -24203.700936881774     [('C', 'A')]
        -24203.700936881774     [('B', 'A'), ('C', 'A')]
        -24172.952132128685     []
        -16597.30920265254      [('A', 'B'), ('A', 'C'), ('B', 'C')]
        -16597.30920265254      [('A', 'B'), ('A', 'C'), ('C', 'B')]
        -16597.309183596342     [('A', 'B'), ('C', 'A'), ('C', 'B')]
        -16597.309183596342     [('A', 'C'), ('B', 'A'), ('B', 'C')]
        -16566.560378843253     [('A', 'B'), ('C', 'B')]
        -16566.560378843253     [('A', 'C'), ('B', 'C')]
        -16268.324549347722     [('A', 'B'), ('B', 'C')]
        -16268.324549347722     [('A', 'C'), ('C', 'B')]
        -16268.324530291524     [('B', 'A'), ('B', 'C')]
        -16268.324530291524     [('B', 'C'), ('C', 'A')]
        -16268.324530291524     [('B', 'A'), ('C', 'B')]
        -16268.324530291524     [('C', 'A'), ('C', 'B')]
        -16268.324530291524     [('B', 'A'), ('B', 'C'), ('C', 'A')]
        -16268.324530291524     [('B', 'A'), ('C', 'A'), ('C', 'B')]
        -16237.575725538434     [('B', 'C')]
        -16237.575725538434     [('C', 'B')]
        """

        scored_dags = sorted([(self.scoring_method.score(dag), dag) for dag in self.all_dags()],
                             key=lambda x: x[0])
        return scored_dags


Instruction:
Write a Python 3 function that can
estimate the DAG structure that fits best to the given data set.

Response:
def estimate(self):
        """
        Estimates the `DAG` structure that fits best to the given data set,
        according to the scoring method supplied in the constructor.
        Exhaustively searches through all models. Only estimates network structure, no parametrization.

        Returns
        -------
        model: `DAG` instance
            A `DAG` with maximal score.

        Examples
        --------
        >>> import pandas as pd
        >>> import numpy as np
        >>> from pgmpy.estimators import ExhaustiveSearch
        >>> # create random data sample with 3 variables, where B and C are identical:
        >>> data = pd.DataFrame(np.random.randint(0, 5, size=(5000, 2)), columns=list('AB'))
        >>> data['C'] = data['B']
        >>> est = ExhaustiveSearch(data)
        >>> best_model = est.estimate()
        >>> best_model
        <pgmpy.base.DAG.DAG object at 0x7f695c535470>
        >>> best_model.edges()
        [('B', 'C')]
        """

        best_dag = max(self.all_dags(), key=self.scoring_method.score)

        best_model = DAG()
        best_model.add_nodes_from(sorted(best_dag.nodes()))
        best_model.add_edges_from(sorted(best_dag.edges()))
        return best_model


Instruction:
Write a Python 3 script for
adding variables to the NoisyOrModel.

Response:
def add_variables(self, variables, cardinality, inhibitor_probability):
        """
        Adds variables to the NoisyOrModel.

        Parameters
        ----------
        variables: list, tuple, dict (array like)
            array containing names of the variables that are to be added.

        cardinality: list, tuple, dict (array like)
            array containing integers representing the cardinality
            of the variables.

        inhibitor_probability: list, tuple, dict (array_like)
            array containing the inhibitor probabilities corresponding to each variable.

        Examples
        --------
        >>> from pgmpy.models import NoisyOrModel
        >>> model = NoisyOrModel(['x1', 'x2', 'x3'], [2, 3, 2], [[0.6, 0.4],
        ...                                                      [0.2, 0.4, 0.7],
        ...                                                      [0.1, 0. 4]])
        >>> model.add_variables(['x4'], [3], [0.1, 0.4, 0.2])
        """
        if len(variables) == 1:
            if not isinstance(inhibitor_probability[0], (list, tuple)):
                inhibitor_probability = [inhibitor_probability]

        if len(variables) != len(cardinality):
            raise ValueError("Size of variables and cardinality should be same")
        elif any(cardinal != len(prob_array) for prob_array, cardinal in zip(inhibitor_probability, cardinality)) or \
                len(cardinality) != len(inhibitor_probability):
            raise ValueError("Size of variables and inhibitor_probability should be same")
        elif not all(0 <= item <= 1 for item in chain.from_iterable(inhibitor_probability)):
            raise ValueError("Probability values should be between 0 and 1(both inclusive).")
        else:
            self.variables = np.concatenate((self.variables, variables))
            self.cardinality = np.concatenate((self.cardinality, cardinality))
            self.inhibitor_probability.extend(inhibitor_probability)


Instruction:
Write a Python 3 script to
delete variables from the NoisyOrModel.

Response:
def del_variables(self, variables):
        """
        Deletes variables from the NoisyOrModel.

        Parameters
        ----------
        variables: list, tuple, dict (array like)
            list of variables to be deleted.

        Examples
        --------
        >>> from pgmpy.models import NoisyOrModel
        >>> model = NoisyOrModel(['x1', 'x2', 'x3'], [2, 3, 2], [[0.6, 0.4],
        ...                                                      [0.2, 0.4, 0.7],
        ...                                                      [0.1, 0. 4]])
        >>> model.del_variables(['x1'])
        """
        variables = [variables] if isinstance(variables, six.string_types) else set(variables)
        indices = [index for index, variable in enumerate(self.variables) if variable in variables]
        self.variables = np.delete(self.variables, indices, 0)
        self.cardinality = np.delete(self.cardinality, indices, 0)
        self.inhibitor_probability = [prob_array for index, prob_array in enumerate(self.inhibitor_probability)
                                      if index not in indices]


Instruction:
Create a Python 3 function to
return the samples according to the return_type

Response:
def _return_samples(return_type, samples):
    """
        A utility function to return samples according to type
    """
    if return_type.lower() == "dataframe":
        if HAS_PANDAS:
            return pandas.DataFrame.from_records(samples)
        else:
            warn("Pandas installation not found. Returning numpy.recarray object")
            return samples
    else:
        return samples


Instruction:
Create a Python 3 function to
return the cardinality of a given variable in the current context.

Response:
def get_cardinality(self, variables):
        """
        Returns cardinality of a given variable

        Parameters
        ----------
        variables: list, array-like
                A list of variable names.

        Returns
        -------
        dict: Dictionary of the form {variable: variable_cardinality}

        Examples
        --------
        >>> from pgmpy.factors.discrete import DiscreteFactor
        >>> phi = DiscreteFactor(['x1', 'x2', 'x3'], [2, 3, 2], range(12))
        >>> phi.get_cardinality(['x1'])
        {'x1': 2}
        >>> phi.get_cardinality(['x1', 'x2'])
        {'x1': 2, 'x2': 3}
        """
        if isinstance(variables, six.string_types):
            raise TypeError("variables: Expected type list or array-like, got type str")

        if not all([var in self.variables for var in variables]):
            raise ValueError("Variable not in scope")

        return {var: self.cardinality[self.variables.index(var)] for var in variables}


Instruction:
Can you write a function in Python 3 where it
returns a list of assignments for the corresponding index.

Response:
def assignment(self, index):
        """
        Returns a list of assignments for the corresponding index.

        Parameters
        ----------
        index: list, array-like
            List of indices whose assignment is to be computed

        Returns
        -------
        list: Returns a list of full assignments of all the variables of the factor.

        Examples
        --------
        >>> import numpy as np
        >>> from pgmpy.factors.discrete import DiscreteFactor
        >>> phi = DiscreteFactor(['diff', 'intel'], [2, 2], np.ones(4))
        >>> phi.assignment([1, 2])
        [[('diff', 0), ('intel', 1)], [('diff', 1), ('intel', 0)]]
        """
        index = np.array(index)

        max_possible_index = np.prod(self.cardinality) - 1
        if not all(i <= max_possible_index for i in index):
            raise IndexError("Index greater than max possible index")

        assignments = np.zeros((len(index), len(self.scope())), dtype=np.int)
        rev_card = self.cardinality[::-1]
        for i, card in enumerate(rev_card):
            assignments[:, i] = index % card
            index = index // card

        assignments = assignments[:, ::-1]

        return [[(key, val) for key, val in zip(self.variables, values)] for values in assignments]


Instruction:
Implement a Python 3 function for
returning the identity factor of the current factor.

Response:
def identity_factor(self):
        """
        Returns the identity factor.

        Def: The identity factor of a factor has the same scope and cardinality as the original factor,
             but the values for all the assignments is 1. When the identity factor is multiplied with
             the factor it returns the factor itself.

        Returns
        -------
        DiscreteFactor: The identity factor.

        Examples
        --------
        >>> from pgmpy.factors.discrete import DiscreteFactor
        >>> phi = DiscreteFactor(['x1', 'x2', 'x3'], [2, 3, 2], range(12))
        >>> phi_identity = phi.identity_factor()
        >>> phi_identity.variables
        ['x1', 'x2', 'x3']
        >>> phi_identity.values
        array([[[ 1.,  1.],
                [ 1.,  1.],
                [ 1.,  1.]],

               [[ 1.,  1.],
                [ 1.,  1.],
                [ 1.,  1.]]])
        """
        return DiscreteFactor(self.variables, self.cardinality, np.ones(self.values.size))


Instruction:
How would you implement a function in Python 3 that
modifies the factor with marginalized values. Parameters ---------- variables: list, array-like List of variables over which to marginalize. inplace: boolean If inplace=True it will modify the factor itself, else would return a new factor. Returns ------- DiscreteFactor or None: if inplace=True (default) returns None if inplace=False returns a new `DiscreteFactor` instance. Examples -------- >>> from pgmpy.factors.discrete import DiscreteFactor >>> phi = DiscreteFactor(['x1', 'x2', 'x3'], [2, 3, 2], range(12)) >>> phi.marginalize(['x1', 'x3']) >>> phi.values array([ 14., 22., 30.]) >>> phi.variables ['x2']

Response:
def marginalize(self, variables, inplace=True):
        """
        Modifies the factor with marginalized values.

        Parameters
        ----------
        variables: list, array-like
            List of variables over which to marginalize.

        inplace: boolean
            If inplace=True it will modify the factor itself, else would return
            a new factor.

        Returns
        -------
        DiscreteFactor or None: if inplace=True (default) returns None
                        if inplace=False returns a new `DiscreteFactor` instance.

        Examples
        --------
        >>> from pgmpy.factors.discrete import DiscreteFactor
        >>> phi = DiscreteFactor(['x1', 'x2', 'x3'], [2, 3, 2], range(12))
        >>> phi.marginalize(['x1', 'x3'])
        >>> phi.values
        array([ 14.,  22.,  30.])
        >>> phi.variables
        ['x2']
        """

        if isinstance(variables, six.string_types):
            raise TypeError("variables: Expected type list or array-like, got type str")

        phi = self if inplace else self.copy()

        for var in variables:
            if var not in phi.variables:
                raise ValueError("{var} not in scope.".format(var=var))

        var_indexes = [phi.variables.index(var) for var in variables]

        index_to_keep = sorted(set(range(len(self.variables))) - set(var_indexes))
        phi.variables = [phi.variables[index] for index in index_to_keep]
        phi.cardinality = phi.cardinality[index_to_keep]

        phi.values = np.sum(phi.values, axis=tuple(var_indexes))

        if not inplace:
            return phi


Instruction:
Write a Python 3 function for
normalizing the values of factor so that they sum to 1.

Response:
def normalize(self, inplace=True):
        """
        Normalizes the values of factor so that they sum to 1.

        Parameters
        ----------
        inplace: boolean
            If inplace=True it will modify the factor itself, else would return
            a new factor

        Returns
        -------
        DiscreteFactor or None: if inplace=True (default) returns None
                        if inplace=False returns a new `DiscreteFactor` instance.

        Examples
        --------
        >>> from pgmpy.factors.discrete import DiscreteFactor
        >>> phi = DiscreteFactor(['x1', 'x2', 'x3'], [2, 3, 2], range(12))
        >>> phi.values
        array([[[ 0,  1],
                [ 2,  3],
                [ 4,  5]],

               [[ 6,  7],
                [ 8,  9],
                [10, 11]]])
        >>> phi.normalize()
        >>> phi.variables
        ['x1', 'x2', 'x3']
        >>> phi.cardinality
        array([2, 3, 2])
        >>> phi.values
        array([[[ 0.        ,  0.01515152],
                [ 0.03030303,  0.04545455],
                [ 0.06060606,  0.07575758]],

               [[ 0.09090909,  0.10606061],
                [ 0.12121212,  0.13636364],
                [ 0.15151515,  0.16666667]]])

        """
        phi = self if inplace else self.copy()

        phi.values = phi.values / phi.values.sum()

        if not inplace:
            return phi


Instruction:
Write a Python 3 function that can
reduce the factor to the context of given variable values.

Response:
def reduce(self, values, inplace=True):
        """
        Reduces the factor to the context of given variable values.

        Parameters
        ----------
        values: list, array-like
            A list of tuples of the form (variable_name, variable_state).

        inplace: boolean
            If inplace=True it will modify the factor itself, else would return
            a new factor.

        Returns
        -------
        DiscreteFactor or None: if inplace=True (default) returns None
                        if inplace=False returns a new `DiscreteFactor` instance.

        Examples
        --------
        >>> from pgmpy.factors.discrete import DiscreteFactor
        >>> phi = DiscreteFactor(['x1', 'x2', 'x3'], [2, 3, 2], range(12))
        >>> phi.reduce([('x1', 0), ('x2', 0)])
        >>> phi.variables
        ['x3']
        >>> phi.cardinality
        array([2])
        >>> phi.values
        array([0., 1.])
        """
        if isinstance(values, six.string_types):
            raise TypeError("values: Expected type list or array-like, got type str")

        if (any(isinstance(value, six.string_types) for value in values) or
                not all(isinstance(state, (int, np.integer)) for var, state in values)):
            raise TypeError("values: must contain tuples or array-like elements of the form "
                            "(hashable object, type int)")

        phi = self if inplace else self.copy()

        var_index_to_del = []
        slice_ = [slice(None)] * len(self.variables)
        for var, state in values:
            var_index = phi.variables.index(var)
            slice_[var_index] = state
            var_index_to_del.append(var_index)

        var_index_to_keep = sorted(set(range(len(phi.variables))) - set(var_index_to_del))
        # set difference is not gaurenteed to maintain ordering
        phi.variables = [phi.variables[index] for index in var_index_to_keep]
        phi.cardinality = phi.cardinality[var_index_to_keep]
        phi.values = phi.values[tuple(slice_)]

        if not inplace:
            return phi


Instruction:
Write a Python 3 function that can
return a new DiscreteFactor instance with the sum of the two values.

Response:
def sum(self, phi1, inplace=True):
        """
        DiscreteFactor sum with `phi1`.

        Parameters
        ----------
        phi1: `DiscreteFactor` instance.
            DiscreteFactor to be added.

        inplace: boolean
            If inplace=True it will modify the factor itself, else would return
            a new factor.

        Returns
        -------
        DiscreteFactor or None: if inplace=True (default) returns None
                        if inplace=False returns a new `DiscreteFactor` instance.

        Example
        -------
        >>> from pgmpy.factors.discrete import DiscreteFactor
        >>> phi1 = DiscreteFactor(['x1', 'x2', 'x3'], [2, 3, 2], range(12))
        >>> phi2 = DiscreteFactor(['x3', 'x4', 'x1'], [2, 2, 2], range(8))
        >>> phi1.sum(phi2, inplace=True)
        >>> phi1.variables
        ['x1', 'x2', 'x3', 'x4']
        >>> phi1.cardinality
        array([2, 3, 2, 2])
        >>> phi1.values
        array([[[[ 0,  0],
                 [ 4,  6]],

                [[ 0,  4],
                 [12, 18]],

                [[ 0,  8],
                 [20, 30]]],

               [[[ 6, 18],
                 [35, 49]],

                [[ 8, 24],
                 [45, 63]],

                [[10, 30],
                 [55, 77]]]])
        """
        phi = self if inplace else self.copy()
        if isinstance(phi1, (int, float)):
            phi.values += phi1
        else:
            phi1 = phi1.copy()

            # modifying phi to add new variables
            extra_vars = set(phi1.variables) - set(phi.variables)
            if extra_vars:
                slice_ = [slice(None)] * len(phi.variables)
                slice_.extend([np.newaxis] * len(extra_vars))
                phi.values = phi.values[tuple(slice_)]

                phi.variables.extend(extra_vars)

                new_var_card = phi1.get_cardinality(extra_vars)
                phi.cardinality = np.append(phi.cardinality, [new_var_card[var] for var in extra_vars])

            # modifying phi1 to add new variables
            extra_vars = set(phi.variables) - set(phi1.variables)
            if extra_vars:
                slice_ = [slice(None)] * len(phi1.variables)
                slice_.extend([np.newaxis] * len(extra_vars))
                phi1.values = phi1.values[tuple(slice_)]

                phi1.variables.extend(extra_vars)
                # No need to modify cardinality as we don't need it.

            # rearranging the axes of phi1 to match phi
            for axis in range(phi.values.ndim):
                exchange_index = phi1.variables.index(phi.variables[axis])
                phi1.variables[axis], phi1.variables[exchange_index] = phi1.variables[exchange_index], \
                    phi1.variables[axis]
                phi1.values = phi1.values.swapaxes(axis, exchange_index)

            phi.values = phi.values + phi1.values

        if not inplace:
            return phi


Instruction:
Can you generate the documentation for the following Python 3 function
def divide(self, phi1, inplace=True):
        phi = self if inplace else self.copy()
        phi1 = phi1.copy()

        if set(phi1.variables) - set(phi.variables):
            raise ValueError("Scope of divisor should be a subset of dividend")

        # Adding extra variables in phi1.
        extra_vars = set(phi.variables) - set(phi1.variables)
        if extra_vars:
            slice_ = [slice(None)] * len(phi1.variables)
            slice_.extend([np.newaxis] * len(extra_vars))
            phi1.values = phi1.values[tuple(slice_)]

            phi1.variables.extend(extra_vars)

        # Rearranging the axes of phi1 to match phi
        for axis in range(phi.values.ndim):
            exchange_index = phi1.variables.index(phi.variables[axis])
            phi1.variables[axis], phi1.variables[exchange_index] = phi1.variables[exchange_index], phi1.variables[axis]
            phi1.values = phi1.values.swapaxes(axis, exchange_index)

        phi.values = phi.values / phi1.values

        # If factor division 0/0 = 0 but is undefined for x/0. In pgmpy we are using
        # np.inf to represent x/0 cases.
        phi.values[np.isnan(phi.values)] = 0

        if not inplace:
            return phi

Response: Returns a new DiscreteFactor instance with the denominator of division by phi1.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def copy(self):
        # not creating a new copy of self.values and self.cardinality
        # because __init__ methods does that.
        return DiscreteFactor(self.scope(), self.cardinality, self.values, state_names=self.state_names)

Response: Returns a copy of the factor.


Instruction:
How would you explain what the following Python 3 function does
def _str(self, phi_or_p="phi", tablefmt="grid", print_state_names=True):
        string_header = list(map(lambda x: six.text_type(x), self.scope()))
        string_header.append('{phi_or_p}({variables})'.format(phi_or_p=phi_or_p,
                                                              variables=','.join(string_header)))

        value_index = 0
        factor_table = []
        for prob in product(*[range(card) for card in self.cardinality]):
            if self.state_names and print_state_names:
                prob_list = ["{var}({state})".format(
                    var=list(self.variables)[i], state=self.state_names[list(
                        self.variables)[i]][prob[i]])
                             for i in range(len(self.variables))]
            else:
                prob_list = ["{s}_{d}".format(s=list(self.variables)[i], d=prob[i])
                             for i in range(len(self.variables))]

            prob_list.append(self.values.ravel()[value_index])
            factor_table.append(prob_list)
            value_index += 1

        return tabulate(factor_table, headers=string_header, tablefmt=tablefmt, floatfmt=".4f")

Response: Generate the string from the __str__ method.


Instruction:
Write a Python 3 script to
check whether given parameter is a 1d array like object and returns a numpy array object

Response:
def _check_1d_array_object(parameter, name_param):
    """
    Checks whether given parameter is a 1d array like object, and returns a numpy array object
    """
    if isinstance(parameter, (np.ndarray, list, tuple, np.matrix)):
        parameter = np.array(parameter)
        if parameter.ndim != 1:
            raise TypeError("{} should be a 1d array type object".format(name_param))
    else:
        raise TypeError("{} should be a 1d array type object".format(name_param))

    return parameter


Instruction:
How would you explain what the following Python 3 function does
def _check_length_equal(param_1, param_2, name_param_1, name_param_2):
    if len(param_1) != len(param_2):
        raise ValueError("Length of {} must be same as Length of {}".format(name_param_1, name_param_2))

Response: Checks that the length of two arguments is equal to the length of the base class.


Instruction:
Explain what the following Python 3 code does
def get_variables(self):
        variables = [variable.find('NAME').text for variable in self.network.findall('VARIABLE')]
        return variables

Response: Returns list of variables of the network


Instruction:
Write a Python 3 function that can
return the edges of the network

Response:
def get_edges(self):
        """
        Returns the edges of the network

        Examples
        --------
        >>> reader = XMLBIF.XMLBIFReader("xmlbif_test.xml")
        >>> reader.get_edges()
        [['family-out', 'light-on'],
         ['family-out', 'dog-out'],
         ['bowel-problem', 'dog-out'],
         ['dog-out', 'hear-bark']]
        """
        edge_list = [[value, key] for key in self.variable_parents
                     for value in self.variable_parents[key]]
        return edge_list


Instruction:
Write a Python 3 function that can
return the states of variables present in the network

Response:
def get_states(self):
        """
        Returns the states of variables present in the network

        Examples
        --------
        >>> reader = XMLBIF.XMLBIFReader("xmlbif_test.xml")
        >>> reader.get_states()
        {'bowel-problem': ['true', 'false'],
         'dog-out': ['true', 'false'],
         'family-out': ['true', 'false'],
         'hear-bark': ['true', 'false'],
         'light-on': ['true', 'false']}
        """
        variable_states = {variable.find('NAME').text: [outcome.text for outcome in variable.findall('OUTCOME')]
                           for variable in self.network.findall('VARIABLE')}
        return variable_states


Instruction:
Create a Python 3 function for
returning the parents of the variables present in the network

Response:
def get_parents(self):
        """
        Returns the parents of the variables present in the network

        Examples
        --------
        >>> reader = XMLBIF.XMLBIFReader("xmlbif_test.xml")
        >>> reader.get_parents()
        {'bowel-problem': [],
         'dog-out': ['family-out', 'bowel-problem'],
         'family-out': [],
         'hear-bark': ['dog-out'],
         'light-on': ['family-out']}
        """
        variable_parents = {definition.find('FOR').text: [edge.text for edge in definition.findall('GIVEN')]
                            for definition in self.network.findall('DEFINITION')}
        return variable_parents


Instruction:
Can you generate a brief explanation for the following Python 3 code
def get_values(self):
        variable_CPD = {definition.find('FOR').text: list(map(float, table.text.split()))
                        for definition in self.network.findall('DEFINITION')
                        for table in definition.findall('TABLE')}
        for variable in variable_CPD:
            arr = np.array(variable_CPD[variable])
            arr = arr.reshape((len(self.variable_states[variable]),
                               arr.size // len(self.variable_states[variable])), order='F')
            variable_CPD[variable] = arr
        return variable_CPD

Response: Returns the CPD of the variables present in the network


Instruction:
Implement a function in Python 3 to
return the property of the variable

Response:
def get_property(self):
        """
        Returns the property of the variable

        Examples
        --------
        >>> reader = XMLBIF.XMLBIFReader("xmlbif_test.xml")
        >>> reader.get_property()
        {'bowel-problem': ['position = (190, 69)'],
         'dog-out': ['position = (155, 165)'],
         'family-out': ['position = (112, 69)'],
         'hear-bark': ['position = (154, 241)'],
         'light-on': ['position = (73, 165)']}
        """
        variable_property = {variable.find('NAME').text: [property.text for property in variable.findall('PROPERTY')]
                             for variable in self.network.findall('VARIABLE')}
        return variable_property


Instruction:
Can you generate the documentation for the following Python 3 function
def get_variables(self):
        variables = self.model.nodes()
        variable_tag = {}
        for var in sorted(variables):
            variable_tag[var] = etree.SubElement(self.network, "VARIABLE", attrib={'TYPE': 'nature'})
            etree.SubElement(variable_tag[var], "NAME").text = var
        return variable_tag

Response: Add variables to XMLBIF object


Instruction:
Create a Python 3 function for
getting the states of the XMLBIF.

Response:
def get_states(self):
        """
        Add outcome to variables of XMLBIF

        Return
        ------
        dict: dict of type {variable: outcome tags}

        Examples
        --------
        >>> writer = XMLBIFWriter(model)
        >>> writer.get_states()
        {'dog-out': [<Element OUTCOME at 0x7ffbabfcdec8>, <Element OUTCOME at 0x7ffbabfcdf08>],
         'family-out': [<Element OUTCOME at 0x7ffbabfd4108>, <Element OUTCOME at 0x7ffbabfd4148>],
         'bowel-problem': [<Element OUTCOME at 0x7ffbabfd4088>, <Element OUTCOME at 0x7ffbabfd40c8>],
         'hear-bark': [<Element OUTCOME at 0x7ffbabfcdf48>, <Element OUTCOME at 0x7ffbabfcdf88>],
         'light-on': [<Element OUTCOME at 0x7ffbabfcdfc8>, <Element OUTCOME at 0x7ffbabfd4048>]}
        """
        outcome_tag = {}
        cpds = self.model.get_cpds()
        for cpd in cpds:
            var = cpd.variable
            outcome_tag[var] = []
            if cpd.state_names is None or cpd.state_names.get(var) is None:
                states = range(cpd.get_cardinality([var])[var])
            else:
                states = cpd.state_names[var]

            for state in states:
                state_tag = etree.SubElement(self.variables[var], "OUTCOME")
                state_tag.text = self._make_valid_state_name(state)
                outcome_tag[var].append(state_tag)
        return outcome_tag


Instruction:
Given the following Python 3 function, write the documentation
def _make_valid_state_name(self, state_name):
        s = str(state_name)
        s_fixed = pp.CharsNotIn(pp.alphanums + "_").setParseAction(pp.replaceWith("_")).transformString(s)
        if not s_fixed[0].isalpha():
            s_fixed = "state" + s_fixed
        return s_fixed

Response: Transform the input state_name into a valid state in XMLBIF.


Instruction:
How would you code a function in Python 3 to
get properties of the current object.

Response:
def get_properties(self):
        """
        Add property to variables in XMLBIF

        Return
        ------
        dict: dict of type {variable: property tag}

        Examples
        --------
        >>> writer = XMLBIFWriter(model)
        >>> writer.get_property()
        {'light-on': <Element PROPERTY at 0x7f7a2ffac1c8>,
         'family-out': <Element PROPERTY at 0x7f7a2ffac148>,
         'hear-bark': <Element PROPERTY at 0x7f7a2ffac188>,
         'bowel-problem': <Element PROPERTY at 0x7f7a2ffac0c8>,
         'dog-out': <Element PROPERTY at 0x7f7a2ffac108>}
        """
        variables = self.model.nodes()
        property_tag = {}
        for var in sorted(variables):
            properties = self.model.node[var]
            property_tag[var] = etree.SubElement(self.variables[var], "PROPERTY")
            for prop, val in properties.items():
                property_tag[var].text = str(prop) + " = " + str(val)
        return property_tag


Instruction:
Given the following Python 3 function, write the documentation
def get_definition(self):
        cpds = self.model.get_cpds()
        cpds.sort(key=lambda x: x.variable)
        definition_tag = {}
        for cpd in cpds:
            definition_tag[cpd.variable] = etree.SubElement(self.network, "DEFINITION")
            etree.SubElement(definition_tag[cpd.variable], "FOR").text = cpd.variable
            for child in sorted(cpd.variables[:0:-1]):
                etree.SubElement(definition_tag[cpd.variable], "GIVEN").text = child

        return definition_tag

Response: Add Definition to XMLBIF


Instruction:
Make a summary of the following Python 3 code
def get_values(self):
        cpds = self.model.get_cpds()
        definition_tag = self.definition
        table_tag = {}
        for cpd in cpds:
            table_tag[cpd.variable] = etree.SubElement(definition_tag[cpd.variable], "TABLE")
            table_tag[cpd.variable].text = ''
            for val in cpd.get_values().ravel(order="F"):
                table_tag[cpd.variable].text += str(val) + ' '

        return table_tag

Response: Get the values of the CO - COVER and other related variables.


Instruction:
Make a summary of the following Python 3 code
def write_xmlbif(self, filename):
        with open(filename, 'w') as fout:
            fout.write(self.__str__())

Response: Write the xml data into the file.


Instruction:
Write a Python 3 script to
return the marginal distribution over variables.

Response:
def marginal_distribution(self, variables, inplace=True):
        """
        Returns the marginal distribution over variables.

        Parameters
        ----------
        variables: string, list, tuple, set, dict
                Variable or list of variables over which marginal distribution needs
                to be calculated
        inplace: Boolean (default True)
                If False return a new instance of JointProbabilityDistribution

        Examples
        --------
        >>> import numpy as np
        >>> from pgmpy.factors.discrete import JointProbabilityDistribution
        >>> values = np.random.rand(12)
        >>> prob = JointProbabilityDistribution(['x1', 'x2', 'x3'], [2, 3, 2], values/np.sum(values))
        >>> prob.marginal_distribution(['x1', 'x2'])
        >>> print(prob)
        x1    x2      P(x1,x2)
        ----  ----  ----------
        x1_0  x2_0      0.1502
        x1_0  x2_1      0.1626
        x1_0  x2_2      0.1197
        x1_1  x2_0      0.2339
        x1_1  x2_1      0.1996
        x1_1  x2_2      0.1340
        """
        return self.marginalize(list(set(list(self.variables)) -
                                     set(variables if isinstance(
                                         variables, (list, set, dict, tuple)) else [variables])),
                                inplace=inplace)


Instruction:
Can you tell what is the following Python 3 function doing
def check_independence(self, event1, event2, event3=None, condition_random_variable=False):
        JPD = self.copy()
        if isinstance(event1, six.string_types):
            raise TypeError('Event 1 should be a list or array-like structure')

        if isinstance(event2, six.string_types):
            raise TypeError('Event 2 should be a list or array-like structure')

        if event3:
            if isinstance(event3, six.string_types):
                raise TypeError('Event 3 cannot of type string')

            elif condition_random_variable:
                if not all(isinstance(var, six.string_types) for var in event3):
                    raise TypeError('Event3 should be a 1d list of strings')
                event3 = list(event3)
                # Using the definition of conditional independence
                # If P(X,Y|Z) = P(X|Z)*P(Y|Z)
                # This can be expanded to P(X,Y,Z)*P(Z) == P(X,Z)*P(Y,Z)
                phi_z = JPD.marginal_distribution(event3, inplace=False).to_factor()
                for variable_pair in itertools.product(event1, event2):
                    phi_xyz = JPD.marginal_distribution(event3 + list(variable_pair), inplace=False).to_factor()
                    phi_xz = JPD.marginal_distribution(event3 + [variable_pair[0]], inplace=False).to_factor()
                    phi_yz = JPD.marginal_distribution(event3 + [variable_pair[1]], inplace=False).to_factor()
                    if phi_xyz * phi_z != phi_xz * phi_yz:
                        return False
                return True
            else:
                JPD.conditional_distribution(event3)

        for variable_pair in itertools.product(event1, event2):
            if (JPD.marginal_distribution(variable_pair, inplace=False) !=
                    JPD.marginal_distribution(variable_pair[0], inplace=False) *
                    JPD.marginal_distribution(variable_pair[1], inplace=False)):
                return False
        return True

Response: Checks if the Joint Probability Distribution satisfies the given independence condition.


Instruction:
Implement a function in Python 3 to
return the independent variables in the joint probability distribution.

Response:
def get_independencies(self, condition=None):
        """
        Returns the independent variables in the joint probability distribution.
        Returns marginally independent variables if condition=None.
        Returns conditionally independent variables if condition!=None

        Parameter
        ---------
        condition: array_like
                Random Variable on which to condition the Joint Probability Distribution.

        Examples
        --------
        >>> import numpy as np
        >>> from pgmpy.factors.discrete import JointProbabilityDistribution
        >>> prob = JointProbabilityDistribution(['x1', 'x2', 'x3'], [2, 3, 2], np.ones(12)/12)
        >>> prob.get_independencies()
        (x1 _|_ x2)
        (x1 _|_ x3)
        (x2 _|_ x3)
        """
        JPD = self.copy()
        if condition:
            JPD.conditional_distribution(condition)
        independencies = Independencies()
        for variable_pair in itertools.combinations(list(JPD.variables), 2):
            if (JPD.marginal_distribution(variable_pair, inplace=False) ==
                    JPD.marginal_distribution(variable_pair[0], inplace=False) *
                    JPD.marginal_distribution(variable_pair[1], inplace=False)):
                independencies.add_assertions(variable_pair)
        return independencies


Instruction:
Can you generate the documentation for the following Python 3 function
def conditional_distribution(self, values, inplace=True):
        JPD = self if inplace else self.copy()
        JPD.reduce(values)
        JPD.normalize()
        if not inplace:
            return JPD

Response: Returns a new JointProbabilityDistribution object with conditional distribution of the given set of values.