Instruction:
Can you write a function in Python 3 where it
reduces the distribution to the context of the given variable values.

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

        Let C(X,Y ; K, h, g) be some canonical form over X,Y where,

        k = [[K_XX, K_XY],        ;       h = [[h_X],
             [K_YX, K_YY]]                     [h_Y]]

        The formula for the obtained conditional distribution for setting
        Y = y is given by,

        .. math:: K' = K_{XX}
        .. math:: h' = h_X - K_{XY} * y
        .. math:: g' = g + {h^T}_Y * y - 0.5 * y^T * K_{YY} * y

        Parameters
        ----------
        values: list, array-like
            A list of tuples of the form (variable name, variable value).

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

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

        Examples
        --------
        >>> import numpy as np
        >>> from pgmpy.factors.continuous import CanonicalDistribution
        >>> phi = CanonicalDistribution(['X1', 'X2', 'X3'],
        ...                       np.array([[1, -1, 0], [-1, 4, -2], [0, -2, 4]]),
        ...                       np.array([[1], [4], [-1]]), -2)
        >>> phi.variables
        ['X1', 'X2', 'X3']

        >>> phi.K
        array([[ 1., -1.],
               [-1.,  3.]])

        >>> phi.h
        array([[ 1. ],
               [ 3.5]])

        >>> phi.g
        -2

        >>> phi.reduce([('X3', 0.25)])

        >>> phi.variables
        ['X1', 'X2']

        >>> phi.K
        array([[ 1, -1],
               [-1,  4]])

        >>> phi.h
        array([[ 1. ],
               [ 4.5]])

        >>> phi.g
        -2.375
        """
        if not isinstance(values, (list, tuple, np.ndarray)):
            raise TypeError("variables: Expected type list or array-like, "
                            "got type {var_type}".format(var_type=type(values)))

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

        phi = self if inplace else self.copy()

        var_to_reduce = [var for var, value in values]

        # index_to_keep -> j vector
        index_to_keep = [self.variables.index(var) for var in self.variables
                         if var not in var_to_reduce]
        # index_to_reduce -> i vector
        index_to_reduce = [self.variables.index(var) for var in var_to_reduce]

        K_i_i = self.K[np.ix_(index_to_keep, index_to_keep)]
        K_i_j = self.K[np.ix_(index_to_keep, index_to_reduce)]
        K_j_j = self.K[np.ix_(index_to_reduce, index_to_reduce)]
        h_i = self.h[index_to_keep]
        h_j = self.h[index_to_reduce]

        # The values for the reduced variables.
        y = np.array([value for var, value in values]).reshape(len(index_to_reduce), 1)

        phi.variables = [self.variables[index] for index in index_to_keep]
        phi.K = K_i_i
        phi.h = h_i - np.dot(K_i_j, y)
        phi.g = self.g + (np.dot(h_j.T, y) - (0.5 * np.dot(np.dot(y.T, K_j_j), y)))[0][0]

        if not inplace:
            return phi


Instruction:
Can you generate a brief explanation for the following Python 3 code
def marginalize(self, variables, inplace=True):
        if not isinstance(variables, (list, tuple, np.ndarray)):
            raise TypeError("variables: Expected type list or array-like, "
                            "got type {var_type}".format(var_type=type(variables)))

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

        phi = self if inplace else self.copy()

        # index_to_keep -> i vector
        index_to_keep = [self.variables.index(var) for var in self.variables
                         if var not in variables]
        # index_to_marginalize -> j vector
        index_to_marginalize = [self.variables.index(var) for var in variables]

        K_i_i = self.K[np.ix_(index_to_keep, index_to_keep)]
        K_i_j = self.K[np.ix_(index_to_keep, index_to_marginalize)]
        K_j_i = self.K[np.ix_(index_to_marginalize, index_to_keep)]
        K_j_j = self.K[np.ix_(index_to_marginalize, index_to_marginalize)]
        K_j_j_inv = np.linalg.inv(K_j_j)
        h_i = self.h[index_to_keep]
        h_j = self.h[index_to_marginalize]

        phi.variables = [self.variables[index] for index in index_to_keep]

        phi.K = K_i_i - np.dot(np.dot(K_i_j, K_j_j_inv), K_j_i)
        phi.h = h_i - np.dot(np.dot(K_i_j, K_j_j_inv), h_j)
        phi.g = self.g + 0.5 * (len(variables) * np.log(2 * np.pi) -
                                np.log(abs(np.linalg.det(K_j_j))) + np.dot(np.dot(h_j.T, K_j_j), h_j))[0][0]

        if not inplace:
            return phi

Response: u Marginalizes the factor with marginalized values.


Instruction:
Write a Python 3 function for
giving the CanonicalDistribution operation (product or divide) with the other factor. The product of two canonical factors over the same scope X is simply: C(K1, h1, g1) * C(K2, h2, g2) = C(K1+K2, h1+h2, g1+g2) The division of canonical forms is defined analogously: C(K1, h1, g1) / C(K2, h2, g2) = C(K1-K2, h1-h2, g1- g2) When we have two canonical factors over different scopes X and Y, we simply extend the scope of both to make their scopes match and then perform the operation of the above equation. The extension of the scope is performed by simply adding zero entries to both the K matrices and the h vectors. Parameters ---------- other: CanonicalFactor The CanonicalDistribution to be multiplied. operation: String 'product' for multiplication operation and 'divide' for division operation. Returns ------- CanonicalDistribution or None: if inplace=True (default) returns None if inplace=False returns a new CanonicalDistribution instance. Example ------- >>> import numpy as np >>> from pgmpy.factors.continuous import CanonicalDistribution >>> phi1 = CanonicalDistribution(['x1', 'x2', 'x3'], np.array([[1, -1, 0], [-1, 4, -2], [0, -2, 4]]), np.array([[1], [4], [-1]]), -2) >>> phi2 = CanonicalDistribution(['x1', 'x2'], np.array([[3, -2], [-2, 4]]), np.array([[5], [-1]]), 1) >>> phi3 = phi1 * phi2 >>> phi3.K array([[ 4., -3., 0.], [-3., 8., -2.], [ 0., -2., 4.]]) >>> phi3.h array([ 6., 3., -1.]) >>> phi3.g -1 >>> phi4 = phi1 / phi2 >>> phi4.K array([[-2., 1., 0.], [ 1., 0., -2.], [ 0., -2., 4.]]) >>> phi4.h array([-4., 5., -1.]) >>> phi4.g -3

Response:
def _operate(self, other, operation, inplace=True):
        """
        Gives the CanonicalDistribution operation (product or divide) with
        the other factor.

        The product of two canonical factors over the same scope
        X is simply:

        C(K1, h1, g1) * C(K2, h2, g2) = C(K1+K2, h1+h2, g1+g2)

        The division of canonical forms is defined analogously:

        C(K1, h1, g1) / C(K2, h2, g2) = C(K1-K2, h1-h2, g1- g2)

        When we have two canonical factors over different scopes X and Y,
        we simply extend the scope of both to make their scopes match and
        then perform the operation of the above equation. The extension of
        the scope is performed by simply adding zero entries to both the K
        matrices and the h vectors.

        Parameters
        ----------
        other: CanonicalFactor
            The CanonicalDistribution to be multiplied.

        operation: String
            'product' for multiplication operation and
            'divide' for division operation.

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

        Example
        -------
        >>> import numpy as np
        >>> from pgmpy.factors.continuous import CanonicalDistribution
        >>> phi1 = CanonicalDistribution(['x1', 'x2', 'x3'],
                                   np.array([[1, -1, 0], [-1, 4, -2], [0, -2, 4]]),
                                   np.array([[1], [4], [-1]]), -2)
        >>> phi2 = CanonicalDistribution(['x1', 'x2'], np.array([[3, -2], [-2, 4]]),
                                   np.array([[5], [-1]]), 1)

        >>> phi3 = phi1 * phi2
        >>> phi3.K
        array([[ 4., -3.,  0.],
               [-3.,  8., -2.],
               [ 0., -2.,  4.]])
        >>> phi3.h
        array([ 6.,  3., -1.])
        >>> phi3.g
        -1

        >>> phi4 = phi1 / phi2
        >>> phi4.K
        array([[-2.,  1.,  0.],
               [ 1.,  0., -2.],
               [ 0., -2.,  4.]])
        >>> phi4.h
        array([-4.,  5., -1.])
        >>> phi4.g
        -3

        """
        if not isinstance(other, CanonicalDistribution):
            raise TypeError(
                "CanonicalDistribution object can only be multiplied or divided "
                "with an another CanonicalDistribution object. Got {other_type}, "
                "expected CanonicalDistribution.".format(other_type=type(other)))

        phi = self if inplace else self.copy()

        all_vars = self.variables + [var for var in other.variables if var not in self.variables]
        no_of_var = len(all_vars)

        self_var_index = [all_vars.index(var) for var in self.variables]
        other_var_index = [all_vars.index(var) for var in other.variables]

        def _extend_K_scope(K, index):
            ext_K = np.zeros([no_of_var, no_of_var])
            ext_K[np.ix_(index, index)] = K
            return ext_K

        def _extend_h_scope(h, index):
            ext_h = np.zeros(no_of_var).reshape(no_of_var, 1)
            ext_h[index] = h
            return ext_h

        phi.variables = all_vars

        if operation == 'product':
            phi.K = _extend_K_scope(self.K, self_var_index) + _extend_K_scope(other.K, other_var_index)
            phi.h = _extend_h_scope(self.h, self_var_index) + _extend_h_scope(other.h, other_var_index)
            phi.g = self.g + other.g

        else:
            phi.K = _extend_K_scope(self.K, self_var_index) - _extend_K_scope(other.K, other_var_index)
            phi.h = _extend_h_scope(self.h, self_var_index) - _extend_h_scope(other.h, other_var_index)
            phi.g = self.g - other.g

        if not inplace:
            return phi


Instruction:
Make a summary of the following Python 3 code
def get_variables(self):
        self.variables = defaultdict(list)
        for variable in self.network.findall('Variable'):
            _variables = defaultdict(list)
            for var in variable.findall('StateVar'):
                state_variables = defaultdict(list)
                state_variables['vnamePrev'] = var.get('vnamePrev')
                state_variables['vnameCurr'] = var.get('vnameCurr')
                if var.get('fullyObs'):
                    state_variables['fullyObs'] = True
                else:
                    state_variables['fullyObs'] = False
                state_variables['ValueEnum'] = []
                if var.find('NumValues') is not None:
                    for i in range(0, int(var.find('NumValues').text)):
                        state_variables['ValueEnum'].append('s' + str(i))
                if var.find('ValueEnum') is not None:
                    state_variables['ValueEnum'] = \
                        var.find('ValueEnum').text.split()
                _variables['StateVar'].append(state_variables)

            for var in variable.findall('ObsVar'):
                obs_variables = defaultdict(list)
                obs_variables['vname'] = var.get('vname')
                obs_variables['ValueEnum'] = \
                    var.find('ValueEnum').text.split()
                _variables['ObsVar'].append(obs_variables)

            for var in variable.findall('ActionVar'):
                action_variables = defaultdict(list)
                action_variables['vname'] = var.get('vname')
                action_variables['ValueEnum'] = \
                    var.find('ValueEnum').text.split()
                _variables['ActionVar'].append(action_variables)

            for var in variable.findall('RewardVar'):
                reward_variables = defaultdict(list)
                reward_variables['vname'] = var.get('vname')
                _variables['RewardVar'].append(reward_variables)

            self.variables.update(_variables)

        return self.variables

Response: Returns list of variables of the current resource.


Instruction:
Here you have a function in Python 3, explain what it does
def get_state_transition_function(self):
        state_transition_function = []
        for variable in self.network.findall('StateTransitionFunction'):
            for var in variable.findall('CondProb'):
                cond_prob = defaultdict(list)
                cond_prob['Var'] = var.find('Var').text
                cond_prob['Parent'] = var.find('Parent').text.split()
                if not var.find('Parameter').get('type'):
                    cond_prob['Type'] = 'TBL'
                else:
                    cond_prob['Type'] = var.find('Parameter').get('type')
                cond_prob['Parameter'] = self.get_parameter(var)
                state_transition_function.append(cond_prob)

        return state_transition_function

Response: Returns the transition of the state variables as nested dict in the case of table type parameter and nested structure in case of decision diagram parameter.


Instruction:
Given the following Python 3 function, write the documentation
def get_obs_function(self):
        obs_function = []
        for variable in self.network.findall('ObsFunction'):
            for var in variable.findall('CondProb'):
                cond_prob = defaultdict(list)
                cond_prob['Var'] = var.find('Var').text
                cond_prob['Parent'] = var.find('Parent').text.split()
                if not var.find('Parameter').get('type'):
                    cond_prob['Type'] = 'TBL'
                else:
                    cond_prob['Type'] = var.find('Parameter').get('type')
                cond_prob['Parameter'] = self.get_parameter(var)
                obs_function.append(cond_prob)

        return obs_function

Response:
Returns the observation function as nested dict in the case of table -
        type parameter and a nested structure in case of decision diagram parameter -
        type parameter -
        decision diagram parameter -
        decision diagram parameter -


Instruction:
Can you create a Python 3 function that
returns the reward function as nested dict in the case of table - type parameter and nested structure in case of decision diagram parameter - type parameter - decision diagram parameter - parameter - tuples of all tables -

Response:
def get_reward_function(self):
        """
        Returns the reward function as nested dict in the case of table-
        type parameter and a nested structure in case of
        decision diagram parameter

        Example
        --------
        >>> reader = PomdpXReader('Test_PomdpX.xml')
        >>> reader.get_reward_function()
        [{'Var': 'reward_rover',
              'Parent': ['action_rover', 'rover_0', 'rock_0'],
              'Type': 'TBL',
              'Parameter': [{'Instance': ['ame', 's1', '*'],
                             'ValueTable': ['10']},
                         ...
                        ]
        }]
        """
        reward_function = []
        for variable in self.network.findall('RewardFunction'):
            for var in variable.findall('Func'):
                func = defaultdict(list)
                func['Var'] = var.find('Var').text
                func['Parent'] = var.find('Parent').text.split()
                if not var.find('Parameter').get('type'):
                    func['Type'] = 'TBL'
                else:
                    func['Type'] = var.find('Parameter').get('type')
                func['Parameter'] = self.get_parameter(var)
                reward_function.append(func)

        return reward_function


Instruction:
Make a summary of the following Python 3 code
def get_parameter(self, var):
        parameter = []

        for parameter_tag in var.findall('Parameter'):
            parameter_type = 'TBL'
            if parameter_tag.get('type') is not None:
                parameter_type = parameter_tag.get('type')
            if parameter_type == 'TBL':
                parameter = self.get_parameter_tbl(parameter_tag)
            elif parameter_type == 'DD':
                parameter = defaultdict(list)
                parameter = self.get_parameter_dd(parameter_tag)

        return parameter

Response: This method returns the parameter of the functional tags in the function.


Instruction:
Implement a function in Python 3 to
add the value enum to the xml

Response:
def _add_value_enum(self, var, tag):
        """
        supports adding variables to the xml

        Parameters
        ---------------
        var: The SubElement variable
        tag: The SubElement tag to which enum value is to be added

        Return
        ---------------
        None
        """
        if var['ValueEnum'][0] == 's0':
                numvalues_tag = etree.SubElement(tag, 'NumValues')
                numvalues_tag.text = str(int(var['ValueEnum'][-1][-1]) + 1)
        else:
            valueenum_tag = etree.SubElement(tag, 'ValueEnum')
            valueenum_tag.text = ''
            for value in var['ValueEnum']:
                valueenum_tag.text += value + ' '
            valueenum_tag.text = valueenum_tag.text[:-1]


Instruction:
Can you write a function in Python 3 where it
returns the PomdpX xml containing variables tag

Response:
def get_variables(self):
        """
        Add variables to PomdpX

        Return
        ---------------
        xml containing variables tag
        """
        state_variables = self.model['variables']['StateVar']
        for var in state_variables:
            state_var_tag = etree.SubElement(self.variable, 'StateVar', attrib={'vnamePrev': var['vnamePrev'],
                                                                                'vnameCurr': var['vnameCurr'],
                                                                                'fullyObs': 'true' if var['fullyObs']
                                                                                else 'false'})
            self._add_value_enum(var, state_var_tag)

        obs_variables = self.model['variables']['ObsVar']
        for var in obs_variables:
            obs_var_tag = etree.SubElement(self.variable, 'ObsVar', attrib={'vname': var['vname']})
            self._add_value_enum(var, obs_var_tag)

        action_variables = self.model['variables']['ActionVar']
        for var in action_variables:
            action_var_tag = etree.SubElement(self.variable, 'ActionVar', attrib={'vname': var['vname']})
            self._add_value_enum(var, action_var_tag)

        reward_var = self.model['variables']['RewardVar']
        for var in reward_var:
            etree.SubElement(self.variable, 'RewardVar', attrib={'vname': var['vname']})

        return self.__str__(self.variable)[:-1]


Instruction:
Given the following Python 3 function, write the documentation
def add_conditions(self, condition, condprob):
        var_tag = etree.SubElement(condprob, 'Var')
        var_tag.text = condition['Var']
        parent_tag = etree.SubElement(condprob, 'Parent')
        parent_tag.text = ''
        for parent in condition['Parent']:
            parent_tag.text += parent + ' '
        parent_tag.text = parent_tag.text[:-1]
        parameter_tag = etree.SubElement(condprob, 'Parameter', attrib={'type': condition['Type']
                                                                        if condition['Type'] is not None
                                                                        else 'TBL'})
        if condition['Type'] == 'DD':
            dag_tag = etree.SubElement(parameter_tag, 'DAG')
            parameter_dict = condition['Parameter']
            if 'SubDAGTemplate' in parameter_dict:
                subdag_tag = etree.SubElement(parameter_tag, 'SubDAGTemplate', attrib={'id': parameter_dict['id']})
                self.add_parameter_dd(subdag_tag, parameter_dict['SubDAGTemplate'])
                del parameter_dict['SubDAGTemplate']
                del parameter_dict['id']
                self.add_parameter_dd(dag_tag, parameter_dict)
            else:
                self.add_parameter_dd(dag_tag, parameter_dict)
        else:
            for parameter in condition['Parameter']:
                entry = etree.SubElement(parameter_tag, 'Entry')
                instance = etree.SubElement(entry, 'Instance')
                instance.text = ''
                for instance_var in parameter['Instance']:
                    instance.text += instance_var + ' '
                length_instance = len(parameter['Instance'])
                if len(parameter['Instance'][length_instance - 1]) > 1:
                        instance.text = instance.text[:-1]
                if len(parameter['Instance']) == 1:
                    instance.text = ' ' + instance.text
                if condprob.tag == 'Func':
                    table = 'ValueTable'
                else:
                    table = 'ProbTable'
                prob_table = parameter[table]
                prob_table_tag = etree.SubElement(entry, table)
                prob_table_tag.text = ''
                for probability in prob_table:
                    prob_table_tag.text += probability + ' '
                prob_table_tag.text = prob_table_tag.text[:-1]

Response: helper function for adding probability conditions for model \


Instruction:
Create a Python 3 function for
adding initial belief tag to pomdpx model

Response:
def add_initial_belief(self):
        """
        add initial belief tag to pomdpx model

        Return
        ---------------
        string containing the xml for initial belief tag
        """
        initial_belief = self.model['initial_state_belief']
        for condition in initial_belief:
            condprob = etree.SubElement(self.initial_belief, 'CondProb')
            self.add_conditions(condition, condprob)
        return self.__str__(self.initial_belief)[:-1]


Instruction:
Make a summary of the following Python 3 code
def add_state_transition_function(self):
        state_transition_function = self.model['state_transition_function']
        for condition in state_transition_function:
            condprob = etree.SubElement(self.transition_function, 'CondProb')
            self.add_conditions(condition, condprob)
        return self.__str__(self.transition_function)[:-1]

Response: add state transition function tag to pomdpx model


Instruction:
Explain what the following Python 3 code does
def add_obs_function(self):
        obs_function = self.model['obs_function']
        for condition in obs_function:
            condprob = etree.SubElement(self.observation_function, 'CondProb')
            self.add_conditions(condition, condprob)
        return self.__str__(self.observation_function)[:-1]

Response: add observation function tag to pomdpx model


Instruction:
How would you explain what the following Python 3 function does
def add_reward_function(self):
        reward_function = self.model['reward_function']
        for condition in reward_function:
            condprob = etree.SubElement(self.reward_function, 'Func')
            self.add_conditions(condition, condprob)
        return self.__str__(self.reward_function)[:-1]

Response: add reward function tag to pomdpx model


Instruction:
Write a Python 3 script for
returning the grammar of the UAI file.

Response:
def get_grammar(self):
        """
        Returns the grammar of the UAI file.
        """
        network_name = Word(alphas).setResultsName('network_name')
        no_variables = Word(nums).setResultsName('no_variables')
        grammar = network_name + no_variables
        self.no_variables = int(grammar.parseString(self.network)['no_variables'])
        domain_variables = (Word(nums)*self.no_variables).setResultsName('domain_variables')
        grammar += domain_variables
        no_functions = Word(nums).setResultsName('no_functions')
        grammar += no_functions
        self.no_functions = int(grammar.parseString(self.network)['no_functions'])
        integer = Word(nums).setParseAction(lambda t: int(t[0]))
        for function in range(0, self.no_functions):
            scope_grammar = Word(nums).setResultsName('fun_scope_' + str(function))
            grammar += scope_grammar
            function_scope = grammar.parseString(self.network)['fun_scope_' + str(function)]
            function_grammar = ((integer)*int(function_scope)).setResultsName('fun_' + str(function))
            grammar += function_grammar

        floatnumber = Combine(Word(nums) + Optional(Literal(".") + Optional(Word(nums))))
        for function in range(0, self.no_functions):
            no_values_grammar = Word(nums).setResultsName('fun_no_values_' + str(function))
            grammar += no_values_grammar
            no_values = grammar.parseString(self.network)['fun_no_values_' + str(function)]
            values_grammar = ((floatnumber)*int(no_values)).setResultsName('fun_values_' + str(function))
            grammar += values_grammar
        return grammar


Instruction:
Given the following Python 3 function, write the documentation
def get_variables(self):
        variables = []
        for var in range(0, self.no_variables):
            var_name = "var_" + str(var)
            variables.append(var_name)
        return variables

Response: Returns a list of variables.


Instruction:
Implement a Python 3 function for
returning the domain of the variables in the UAI file.

Response:
def get_domain(self):
        """
        Returns the dictionary of variables with keys as variable name
        and values as domain of the variables.

        Returns
        -------
        dict: dictionary containing variables and their domains

        Example
        -------
        >>> reader = UAIReader('TestUAI.uai')
        >>> reader.get_domain()
        {'var_0': '2', 'var_1': '2', 'var_2': '3'}
        """
        domain = {}
        var_domain = self.grammar.parseString(self.network)['domain_variables']
        for var in range(0, len(var_domain)):
            domain["var_" + str(var)] = var_domain[var]
        return domain


Instruction:
Given the following Python 3 function, write the documentation
def get_edges(self):
        edges = []
        for function in range(0, self.no_functions):
            function_variables = self.grammar.parseString(self.network)['fun_' + str(function)]
            if isinstance(function_variables, int):
                function_variables = [function_variables]
            if self.network_type == 'BAYES':
                child_var = "var_" + str(function_variables[-1])
                function_variables = function_variables[:-1]
                for var in function_variables:
                    edges.append((child_var, "var_" + str(var)))
            elif self.network_type == "MARKOV":
                function_variables = ["var_" + str(var) for var in function_variables]
                edges.extend(list(combinations(function_variables, 2)))
        return set(edges)

Response: Returns the edges of the UAI network.


Instruction:
Write a Python 3 function for
returning list of tuples of child variable and CPD in case of Bayesian and list of tuple of scope of variables and values in case of Markov.

Response:
def get_tables(self):
        """
        Returns list of tuple of child variable and CPD in case of Bayesian
        and list of tuple of scope of variables and values in case of Markov.

        Returns
        -------
        list : list of tuples of child variable and values in Bayesian
            list of tuples of scope of variables and values in case of Markov.

        Example
        -------
        >>> reader = UAIReader('TestUAI.uai')
        >>> reader.get_tables()
        [(['var_0', 'var_1'], ['4.000', '2.400', '1.000', '0.000']),
         (['var_0', 'var_1', 'var_2'],
          ['2.2500', '3.2500', '3.7500', '0.0000', '0.0000', '10.0000',
           '1.8750', '4.0000', '3.3330', '2.0000', '2.0000', '3.4000'])]
        """
        tables = []
        for function in range(0, self.no_functions):
            function_variables = self.grammar.parseString(self.network)['fun_' + str(function)]
            if isinstance(function_variables, int):
                function_variables = [function_variables]
            if self.network_type == 'BAYES':
                child_var = "var_" + str(function_variables[-1])
                values = self.grammar.parseString(self.network)['fun_values_' + str(function)]
                tables.append((child_var, list(values)))
            elif self.network_type == "MARKOV":
                function_variables = ["var_" + str(var) for var in function_variables]
                values = self.grammar.parseString(self.network)['fun_values_' + str(function)]
                tables.append((function_variables, list(values)))
        return tables


Instruction:
Can you generate a brief explanation for the following Python 3 code
def get_model(self):
        if self.network_type == 'BAYES':
            model = BayesianModel()
            model.add_nodes_from(self.variables)
            model.add_edges_from(self.edges)

            tabular_cpds = []
            for cpd in self.tables:
                child_var = cpd[0]
                states = int(self.domain[child_var])
                arr = list(map(float, cpd[1]))
                values = np.array(arr)
                values = values.reshape(states, values.size // states)
                tabular_cpds.append(TabularCPD(child_var, states, values))

            model.add_cpds(*tabular_cpds)
            return model

        elif self.network_type == 'MARKOV':
            model = MarkovModel(self.edges)

            factors = []
            for table in self.tables:
                variables = table[0]
                cardinality = [int(self.domain[var]) for var in variables]
                value = list(map(float, table[1]))
                factor = DiscreteFactor(variables=variables, cardinality=cardinality, values=value)
                factors.append(factor)

            model.add_factors(*factors)
            return model

Response: Returns an instance of Bayesian Model or Markov Model.


Instruction:
Explain what the following Python 3 code does
def get_domain(self):
        if isinstance(self.model, BayesianModel):
            cpds = self.model.get_cpds()
            cpds.sort(key=lambda x: x.variable)
            domain = {}
            for cpd in cpds:
                domain[cpd.variable] = str(cpd.variable_card)
            return domain
        elif isinstance(self.model, MarkovModel):
            factors = self.model.get_factors()
            domain = {}
            for factor in factors:
                variables = factor.variables
                for var in variables:
                    if var not in domain:
                        domain[var] = str(factor.get_cardinality([var])[var])
            return domain
        else:
            raise TypeError("Model must be an instance of Markov or Bayesian model.")

Response: Returns a dictionary of domain of each variable in the network.


Instruction:
Implement a Python 3 function for
adding functions to the network.

Response:
def get_functions(self):
        """
        Adds functions to the network.

        Example
        -------
        >>> writer = UAIWriter(model)
        >>> writer.get_functions()
        """
        if isinstance(self.model, BayesianModel):
            cpds = self.model.get_cpds()
            cpds.sort(key=lambda x: x.variable)
            variables = sorted(self.domain.items(), key=lambda x: (x[1], x[0]))
            functions = []
            for cpd in cpds:
                child_var = cpd.variable
                evidence = cpd.variables[:0:-1]
                function = [str(variables.index((var, self.domain[var]))) for var in evidence]
                function.append(str(variables.index((child_var, self.domain[child_var]))))
                functions.append(function)
            return functions
        elif isinstance(self.model, MarkovModel):
            factors = self.model.get_factors()
            functions = []
            variables = sorted(self.domain.items(), key=lambda x: (x[1], x[0]))
            for factor in factors:
                scope = factor.scope()
                function = [str(variables.index((var, self.domain[var]))) for var in scope]
                functions.append(function)
            return functions
        else:
            raise TypeError("Model must be an instance of Markov or Bayesian model.")


Instruction:
How would you explain what the following Python 3 function does
def get_tables(self):
        if isinstance(self.model, BayesianModel):
            cpds = self.model.get_cpds()
            cpds.sort(key=lambda x: x.variable)
            tables = []
            for cpd in cpds:
                values = list(map(str, cpd.values.ravel()))
                tables.append(values)
            return tables
        elif isinstance(self.model, MarkovModel):
            factors = self.model.get_factors()
            tables = []
            for factor in factors:
                values = list(map(str, factor.values.ravel()))
                tables.append(values)
            return tables
        else:
            raise TypeError("Model must be an instance of Markov or Bayesian model.")

Response: Adds tables to the network.


Instruction:
How would you implement a function in Python 3 that
writes the xml data into the file.

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

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

        Examples
        -------
        >>> writer = UAIWriter(model)
        >>> writer.write_xmlbif(test_file)
        """
        writer = self.__str__()
        with open(filename, 'w') as fout:
            fout.write(writer)


Instruction:
Here you have a function in Python 3, explain what it does
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)

        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(var_cardinality) - lgamma(conditional_sample_size + var_cardinality)

            for state in var_states:
                if state_counts[parents_state][state] > 0:
                    score += lgamma(state_counts[parents_state][state] + 1)
        return score

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


Instruction:
Write a Python 3 script for
running the command to download target.

Response:
def _clean_check(cmd, target):
    """
    Run the command to download target. If the command fails, clean up before
    re-raising the error.
    """
    try:
        subprocess.check_call(cmd)
    except subprocess.CalledProcessError:
        if os.access(target, os.F_OK):
            os.unlink(target)
        raise


Instruction:
Write a Python 3 script for
downloading the file at url to target using Powershell.

Response:
def download_file_powershell(url, target):
    """
    Download the file at url to target using Powershell (which will validate
    trust). Raise an exception if the command cannot complete.
    """
    target = os.path.abspath(target)
    cmd = [
        'powershell',
        '-Command',
        "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(),
    ]
    _clean_check(cmd, target)


Instruction:
Create a Python 3 function for
downloading setuptools from a specified location and return its filename.

Response:
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
                        to_dir=os.curdir, delay=15,
                        downloader_factory=get_best_downloader):
    """Download setuptools from a specified location and return its filename

    `version` should be a valid setuptools version number that is available
    as an egg for download under the `download_base` URL (which should end
    with a '/'). `to_dir` is the directory where the egg will be downloaded.
    `delay` is the number of seconds to pause before an actual download
    attempt.

    ``downloader_factory`` should be a function taking no arguments and
    returning a function for downloading a URL to a target.
    """
    # making sure we use the absolute path
    to_dir = os.path.abspath(to_dir)
    tgz_name = "setuptools-%s.tar.gz" % version
    url = download_base + tgz_name
    saveto = os.path.join(to_dir, tgz_name)
    if not os.path.exists(saveto):  # Avoid repeated downloads
        log.warn("Downloading %s", url)
        downloader = downloader_factory()
        downloader(url, saveto)
    return os.path.realpath(saveto)


Instruction:
How would you explain what the following Python 3 function does
def _parse_args():
    parser = optparse.OptionParser()
    parser.add_option(
        '--user', dest='user_install', action='store_true', default=False,
        help='install in user site package (requires Python 2.6 or later)')
    parser.add_option(
        '--download-base', dest='download_base', metavar="URL",
        default=DEFAULT_URL,
        help='alternative URL from where to download the setuptools package')
    parser.add_option(
        '--insecure', dest='downloader_factory', action='store_const',
        const=lambda: download_file_insecure, default=get_best_downloader,
        help='Use internal, non-validating downloader'
    )
    options, args = parser.parse_args()
    # positional arguments are ignored
    return options

Response: Parse the command line for options


Instruction:
How would you explain what the following Python 3 function does
def add_edge(self, u, v, **kwargs):
        if u == v:
            raise ValueError('Self loops are not allowed.')
        if u in self.nodes() and v in self.nodes() and nx.has_path(self, v, u):
            raise ValueError(
                'Loops are not allowed. Adding the edge from (%s->%s) forms a loop.' % (u, v))
        else:
            super(BayesianModel, self).add_edge(u, v, **kwargs)

Response: Add an edge between u and v.


Instruction:
Make a summary of the following Python 3 code
def remove_node(self, node):
        affected_nodes = [v for u, v in self.edges() if u == node]

        for affected_node in affected_nodes:
            node_cpd = self.get_cpds(node=affected_node)
            if node_cpd:
                node_cpd.marginalize([node], inplace=True)

        if self.get_cpds(node=node):
            self.remove_cpds(node)
        super(BayesianModel, self).remove_node(node)

Response: Remove a node from the model.


Instruction:
Make a summary of the following Python 3 code
def add_cpds(self, *cpds):
        for cpd in cpds:
            if not isinstance(cpd, (TabularCPD, ContinuousFactor)):
                raise ValueError('Only TabularCPD or ContinuousFactor can be added.')

            if set(cpd.scope()) - set(cpd.scope()).intersection(
                    set(self.nodes())):
                raise ValueError('CPD defined on variable not in the model', cpd)

            for prev_cpd_index in range(len(self.cpds)):
                if self.cpds[prev_cpd_index].variable == cpd.variable:
                    logging.warning("Replacing existing CPD for {var}".format(var=cpd.variable))
                    self.cpds[prev_cpd_index] = cpd
                    break
            else:
                self.cpds.append(cpd)

Response: Adds a set of CPDs to the current model.


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

Response:
def get_cpds(self, node=None):
        """
        Returns the cpd of the node. If node is not specified returns all the CPDs
        that have been added till now to the graph

        Parameter
        ---------
        node: any hashable python object (optional)
            The node whose CPD we want. If node not specified returns all the
            CPDs added to the model.

        Returns
        -------
        A list of TabularCPDs.

        Examples
        --------
        >>> from pgmpy.models import BayesianModel
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> student = BayesianModel([('diff', 'grade'), ('intel', 'grade')])
        >>> cpd = TabularCPD('grade', 2, [[0.1, 0.9, 0.2, 0.7],
        ...                               [0.9, 0.1, 0.8, 0.3]],
        ...                  ['intel', 'diff'], [2, 2])
        >>> student.add_cpds(cpd)
        >>> student.get_cpds()
        """
        if node is not None:
            if node not in self.nodes():
                raise ValueError('Node not present in the Directed Graph')
            for cpd in self.cpds:
                if cpd.variable == node:
                    return cpd
            else:
                return None
        else:
            return self.cpds


Instruction:
Create a Python 3 function for
removing the cpds that are provided in the argument. Parameters ---------- *cpds: TabularCPD object A CPD object on any subset of the variables of the model which is to be associated with the model. Examples -------- >>> from pgmpy.models import BayesianModel >>> from pgmpy.factors.discrete import TabularCPD >>> student = BayesianModel([('diff', 'grade'), ('intel', 'grade')]) >>> cpd = TabularCPD('grade', 2, [[0.1, 0.9, 0.2, 0.7], ... [0.9, 0.1, 0.8, 0.3]], ... ['intel', 'diff'], [2, 2]) >>> student.add_cpds(cpd) >>> student.remove_cpds(cpd)

Response:
def remove_cpds(self, *cpds):
        """
        Removes the cpds that are provided in the argument.

        Parameters
        ----------
        *cpds: TabularCPD object
            A CPD object on any subset of the variables of the model which
            is to be associated with the model.

        Examples
        --------
        >>> from pgmpy.models import BayesianModel
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> student = BayesianModel([('diff', 'grade'), ('intel', 'grade')])
        >>> cpd = TabularCPD('grade', 2, [[0.1, 0.9, 0.2, 0.7],
        ...                               [0.9, 0.1, 0.8, 0.3]],
        ...                  ['intel', 'diff'], [2, 2])
        >>> student.add_cpds(cpd)
        >>> student.remove_cpds(cpd)
        """
        for cpd in cpds:
            if isinstance(cpd, six.string_types):
                cpd = self.get_cpds(cpd)
            self.cpds.remove(cpd)


Instruction:
Create a Python 3 function to
return the cardinality of the node.

Response:
def get_cardinality(self, node=None):
        """
        Returns the cardinality of the node. Throws an error if the CPD for the
        queried node hasn't been added to the network.

        Parameters
        ----------
        node: Any hashable python object(optional).
              The node whose cardinality we want. If node is not specified returns a
              dictionary with the given variable as keys and their respective cardinality
              as values.

        Returns
        -------
        int or dict : If node is specified returns the cardinality of the node.
                      If node is not specified returns a dictionary with the given
                      variable as keys and their respective cardinality as values.

        Examples
        --------
        >>> from pgmpy.models import BayesianModel
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> student = BayesianModel([('diff', 'grade'), ('intel', 'grade')])
        >>> cpd_diff = TabularCPD('diff',2,[[0.6,0.4]]);
        >>> cpd_intel = TabularCPD('intel',2,[[0.7,0.3]]);
        >>> cpd_grade = TabularCPD('grade', 2, [[0.1, 0.9, 0.2, 0.7],
        ...                                     [0.9, 0.1, 0.8, 0.3]],
        ...                                 ['intel', 'diff'], [2, 2])
        >>> student.add_cpds(cpd_diff,cpd_intel,cpd_grade)
        >>> student.get_cardinality()
        defaultdict(int, {'diff': 2, 'grade': 2, 'intel': 2})

        >>> student.get_cardinality('intel')
        2
        """

        if node:
            return self.get_cpds(node).cardinality[0]
        else:
            cardinalities = defaultdict(int)
            for cpd in self.cpds:
                cardinalities[cpd.variable] = cpd.cardinality[0]
            return cardinalities


Instruction:
How would you implement a function in Python 3 that
checks the model for various errors.

Response:
def check_model(self):
        """
        Check the model for various errors. This method checks for the following
        errors.

        * Checks if the sum of the probabilities for each state is equal to 1 (tol=0.01).
        * Checks if the CPDs associated with nodes are consistent with their parents.

        Returns
        -------
        check: boolean
            True if all the checks are passed
        """
        for node in self.nodes():
            cpd = self.get_cpds(node=node)

            if cpd is None:
                raise ValueError('No CPD associated with {}'.format(node))
            elif isinstance(cpd, (TabularCPD, ContinuousFactor)):
                evidence = cpd.get_evidence()
                parents = self.get_parents(node)
                if set(evidence if evidence else []) != set(parents if parents else []):
                    raise ValueError("CPD associated with {node} doesn't have "
                                     "proper parents associated with it.".format(node=node))
                if not cpd.is_valid_cpd():
                    raise ValueError("Sum or integral of conditional probabilites for node {node}"
                                     " is not equal to 1.".format(node=node))
        return True


Instruction:
Can you tell what is the following Python 3 function doing
def to_markov_model(self):
        moral_graph = self.moralize()
        mm = MarkovModel(moral_graph.edges())
        mm.add_factors(*[cpd.to_factor() for cpd in self.cpds])

        return mm

Response: Converts the bayesian model to markov model.


Instruction:
Make a summary of the following Python 3 code
def fit(self, data, estimator=None, state_names=[], complete_samples_only=True, **kwargs):

        from pgmpy.estimators import MaximumLikelihoodEstimator, BayesianEstimator, BaseEstimator

        if estimator is None:
            estimator = MaximumLikelihoodEstimator
        else:
            if not issubclass(estimator, BaseEstimator):
                raise TypeError("Estimator object should be a valid pgmpy estimator.")

        _estimator = estimator(self, data, state_names=state_names,
                               complete_samples_only=complete_samples_only)
        cpds_list = _estimator.get_parameters(**kwargs)
        self.add_cpds(*cpds_list)

Response: Fits the CPD model to the data set.


Instruction:
Can you generate the documentation for the following Python 3 function
def predict(self, data):
        from pgmpy.inference import VariableElimination

        if set(data.columns) == set(self.nodes()):
            raise ValueError("No variable missing in data. Nothing to predict")

        elif set(data.columns) - set(self.nodes()):
            raise ValueError("Data has variables which are not in the model")

        missing_variables = set(self.nodes()) - set(data.columns)
        pred_values = defaultdict(list)

        # Send state_names dict from one of the estimated CPDs to the inference class.
        model_inference = VariableElimination(self, state_names=self.get_cpds()[0].state_names)
        for index, data_point in data.iterrows():
            states_dict = model_inference.map_query(variables=missing_variables, evidence=data_point.to_dict())
            for k, v in states_dict.items():
                pred_values[k].append(v)
        return pd.DataFrame(pred_values, index=data.index)

Response: Predict states of all the missing variables in the data.


Instruction:
Implement a function in Python 3 to
predict probabilities of all states of the missing variables.

Response:
def predict_probability(self, data):
        """
        Predicts probabilities of all states of the missing variables.

        Parameters
        ----------
        data : pandas DataFrame object
            A DataFrame object with column names same as the variables in the model.

        Examples
        --------
        >>> import numpy as np
        >>> import pandas as pd
        >>> from pgmpy.models import BayesianModel
        >>> values = pd.DataFrame(np.random.randint(low=0, high=2, size=(100, 5)),
        ...                       columns=['A', 'B', 'C', 'D', 'E'])
        >>> train_data = values[:80]
        >>> predict_data = values[80:]
        >>> model = BayesianModel([('A', 'B'), ('C', 'B'), ('C', 'D'), ('B', 'E')])
        >>> model.fit(values)
        >>> predict_data = predict_data.copy()
        >>> predict_data.drop('B', axis=1, inplace=True)
        >>> y_prob = model.predict_probability(predict_data)
        >>> y_prob
            B_0         B_1
        80  0.439178    0.560822
        81  0.581970    0.418030
        82  0.488275    0.511725
        83  0.581970    0.418030
        84  0.510794    0.489206
        85  0.439178    0.560822
        86  0.439178    0.560822
        87  0.417124    0.582876
        88  0.407978    0.592022
        89  0.429905    0.570095
        90  0.581970    0.418030
        91  0.407978    0.592022
        92  0.429905    0.570095
        93  0.429905    0.570095
        94  0.439178    0.560822
        95  0.407978    0.592022
        96  0.559904    0.440096
        97  0.417124    0.582876
        98  0.488275    0.511725
        99  0.407978    0.592022
        """
        from pgmpy.inference import VariableElimination

        if set(data.columns) == set(self.nodes()):
            raise ValueError("No variable missing in data. Nothing to predict")

        elif set(data.columns) - set(self.nodes()):
            raise ValueError("Data has variables which are not in the model")

        missing_variables = set(self.nodes()) - set(data.columns)
        pred_values = defaultdict(list)

        model_inference = VariableElimination(self)
        for index, data_point in data.iterrows():
            full_distribution = model_inference.query(variables=missing_variables, evidence=data_point.to_dict())
            states_dict = {}
            for var in missing_variables:
                states_dict[var] = full_distribution.marginalize(missing_variables - {var}, inplace=False)
            for k, v in states_dict.items():
                for l in range(len(v.values)):
                    state = self.get_cpds(k).state_names[k][l]
                    pred_values[k + '_' + str(state)].append(v.values[l])
        return pd.DataFrame(pred_values, index=data.index)


Instruction:
Create a Python 3 function to
check whether the bayesian model is Imap for given Joint Probability Distribution and returns a boolean indicating if the bayesian model is Imap for given Joint Probability Distribution.

Response:
def is_imap(self, JPD):
        """
        Checks whether the bayesian model is Imap of given JointProbabilityDistribution

        Parameters
        -----------
        JPD : An instance of JointProbabilityDistribution Class, for which you want to
            check the Imap

        Returns
        --------
        boolean : True if bayesian model is Imap for given Joint Probability Distribution
                False otherwise
        Examples
        --------
        >>> from pgmpy.models import BayesianModel
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> from pgmpy.factors.discrete import JointProbabilityDistribution
        >>> G = BayesianModel([('diff', 'grade'), ('intel', 'grade')])
        >>> diff_cpd = TabularCPD('diff', 2, [[0.2], [0.8]])
        >>> intel_cpd = TabularCPD('intel', 3, [[0.5], [0.3], [0.2]])
        >>> grade_cpd = TabularCPD('grade', 3,
        ...                        [[0.1,0.1,0.1,0.1,0.1,0.1],
        ...                         [0.1,0.1,0.1,0.1,0.1,0.1],
        ...                         [0.8,0.8,0.8,0.8,0.8,0.8]],
        ...                        evidence=['diff', 'intel'],
        ...                        evidence_card=[2, 3])
        >>> G.add_cpds(diff_cpd, intel_cpd, grade_cpd)
        >>> val = [0.01, 0.01, 0.08, 0.006, 0.006, 0.048, 0.004, 0.004, 0.032,
                   0.04, 0.04, 0.32, 0.024, 0.024, 0.192, 0.016, 0.016, 0.128]
        >>> JPD = JointProbabilityDistribution(['diff', 'intel', 'grade'], [2, 3, 3], val)
        >>> G.is_imap(JPD)
        True
        """
        if not isinstance(JPD, JointProbabilityDistribution):
            raise TypeError("JPD must be an instance of JointProbabilityDistribution")
        factors = [cpd.to_factor() for cpd in self.get_cpds()]
        factor_prod = reduce(mul, factors)
        JPD_fact = DiscreteFactor(JPD.variables, JPD.cardinality, JPD.values)
        if JPD_fact == factor_prod:
            return True
        else:
            return False


Instruction:
How would you code a function in Python 3 to
return a copy of the model.

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

        Returns
        -------
        BayesianModel: Copy of the model on which the method was called.

        Examples
        --------
        >>> from pgmpy.models import BayesianModel
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> model = BayesianModel([('A', 'B'), ('B', 'C')])
        >>> cpd_a = TabularCPD('A', 2, [[0.2], [0.8]])
        >>> cpd_b = TabularCPD('B', 2, [[0.3, 0.7], [0.7, 0.3]],
                               evidence=['A'],
                               evidence_card=[2])
        >>> cpd_c = TabularCPD('C', 2, [[0.1, 0.9], [0.9, 0.1]],
                               evidence=['B'],
                               evidence_card=[2])
        >>> model.add_cpds(cpd_a, cpd_b, cpd_c)
        >>> copy_model = model.copy()
        >>> copy_model.nodes()
        ['C', 'A', 'B']
        >>> copy_model.edges()
        [('A', 'B'), ('B', 'C')]
        >>> copy_model.get_cpds()
        [<TabularCPD representing P(A:2) at 0x7f2824930a58>,
         <TabularCPD representing P(B:2 | A:2) at 0x7f2824930a90>,
         <TabularCPD representing P(C:2 | B:2) at 0x7f2824944240>]
        """
        model_copy = BayesianModel()
        model_copy.add_nodes_from(self.nodes())
        model_copy.add_edges_from(self.edges())
        if self.cpds:
            model_copy.add_cpds(*[cpd.copy() for cpd in self.cpds])
        return model_copy


Instruction:
Can you generate a brief explanation for the following Python 3 code
def get_values(self):
        if self.variable in self.variables:
            return self.values.reshape(self.cardinality[0], np.prod(self.cardinality[1:]))
        else:
            return self.values.reshape(1, np.prod(self.cardinality))

Response: Returns the values of the current set of items.


Instruction:
Write a Python 3 function that can
return a copy of the TabularCPD object.

Response:
def copy(self):
        """
        Returns a copy of the TabularCPD object.

        Examples
        --------
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> cpd = TabularCPD('grade', 2,
        ...                  [[0.7, 0.6, 0.6, 0.2],[0.3, 0.4, 0.4, 0.8]],
        ...                  ['intel', 'diff'], [2, 2])
        >>> copy = cpd.copy()
        >>> copy.variable
        'grade'
        >>> copy.variable_card
        2
        >>> copy.evidence
        ['intel', 'diff']
        >>> copy.values
        array([[[ 0.7,  0.6],
                [ 0.6,  0.2]],

               [[ 0.3,  0.4],
                [ 0.4,  0.8]]])
        """
        evidence = self.variables[1:] if len(self.variables) > 1 else None
        evidence_card = self.cardinality[1:] if len(self.variables) > 1 else None
        return TabularCPD(self.variable, self.variable_card, self.get_values(),
                          evidence, evidence_card, state_names=self.state_names)


Instruction:
Explain what the following Python 3 code does
def normalize(self, inplace=True):
        tabular_cpd = self if inplace else self.copy()
        cpd = tabular_cpd.get_values()
        tabular_cpd.values = (cpd / cpd.sum(axis=0)).reshape(tabular_cpd.cardinality)
        if not inplace:
            return tabular_cpd

Response:
Normalizes the cpd table.

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

        Examples
        --------
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> cpd_table = TabularCPD('grade', 2,
        ...                        [[0.7, 0.2, 0.6, 0.2],[0.4, 0.4, 0.4, 0.8]],
        ...                        ['intel', 'diff'], [2, 2])
        >>> cpd_table.normalize()
        >>> cpd_table.get_values()
        array([[ 0.63636364,  0.33333333,  0.6       ,  0.2       ],
               [ 0.36363636,  0.66666667,  0.4       ,  0.8       ]])


Instruction:
How would you implement a function in Python 3 that
modifies the cpd table with marginalized values. Parameters ---------- variables: list, array-like list of variable to be marginalized inplace: boolean If inplace=True it will modify the CPD itself, else would return a new CPD Examples -------- >>> from pgmpy.factors.discrete import TabularCPD >>> cpd_table = TabularCPD('grade', 2, ... [[0.7, 0.6, 0.6, 0.2],[0.3, 0.4, 0.4, 0.8]], ... ['intel', 'diff'], [2, 2]) >>> cpd_table.marginalize(['diff']) >>> cpd_table.get_values() array([[ 0.65, 0.4 ], [ 0.35, 0.6 ]])

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

        Parameters
        ----------
        variables: list, array-like
            list of variable to be marginalized

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

        Examples
        --------
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> cpd_table = TabularCPD('grade', 2,
        ...                        [[0.7, 0.6, 0.6, 0.2],[0.3, 0.4, 0.4, 0.8]],
        ...                        ['intel', 'diff'], [2, 2])
        >>> cpd_table.marginalize(['diff'])
        >>> cpd_table.get_values()
        array([[ 0.65,  0.4 ],
                [ 0.35,  0.6 ]])
        """
        if self.variable in variables:
            raise ValueError("Marginalization not allowed on the variable on which CPD is defined")

        tabular_cpd = self if inplace else self.copy()

        super(TabularCPD, tabular_cpd).marginalize(variables)
        tabular_cpd.normalize()

        if not inplace:
            return tabular_cpd


Instruction:
How would you code a function in Python 3 to
reduce the cpd table to the context of given variable values.

Response:
def reduce(self, values, inplace=True):
        """
        Reduces the cpd table 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.

        Examples
        --------
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> cpd_table = TabularCPD('grade', 2,
        ...                        [[0.7, 0.6, 0.6, 0.2],[0.3, 0.4, 0.4, 0.8]],
        ...                        ['intel', 'diff'], [2, 2])
        >>> cpd_table.reduce([('diff', 0)])
        >>> cpd_table.get_values()
        array([[ 0.7,  0.6],
               [ 0.3,  0.4]])
        """
        if self.variable in (value[0] for value in values):
            raise ValueError("Reduce not allowed on the variable on which CPD is defined")

        tabular_cpd = self if inplace else self.copy()

        super(TabularCPD, tabular_cpd).reduce(values)
        tabular_cpd.normalize()

        if not inplace:
            return tabular_cpd


Instruction:
Can you generate the documentation for the following Python 3 function
def reorder_parents(self, new_order, inplace=True):
        if (len(self.variables) <= 1 or (set(new_order) - set(self.variables)) or
                (set(self.variables[1:]) - set(new_order))):
            raise ValueError("New order either has missing or extra arguments")
        else:
            if new_order != self.variables[1:]:
                evidence = self.variables[1:]
                evidence_card = self.cardinality[1:]
                card_map = dict(zip(evidence, evidence_card))
                old_pos_map = dict(zip(evidence, range(len(evidence))))
                trans_ord = [0] + [(old_pos_map[letter] + 1) for letter in new_order]
                new_values = np.transpose(self.values, trans_ord)

                if inplace:
                    variables = [self.variables[0]] + new_order
                    cardinality = [self.variable_card] + [card_map[var] for var in new_order]
                    super(TabularCPD, self).__init__(variables, cardinality, new_values.flatten('C'))
                    return self.get_values()
                else:
                    return new_values.reshape(self.cardinality[0], np.prod([card_map[var] for var in new_order]))
            else:
                warn("Same ordering provided as current")
                return self.get_values()

Response: Returns a new CPD with the same attributes as the current one.


Instruction:
Write a Python 3 script to
add a list of LinearGaussianCPDs to the Bayesian Model.

Response:
def add_cpds(self, *cpds):
        """
        Add linear Gaussian CPD (Conditional Probability Distribution)
        to the Bayesian Model.

        Parameters
        ----------
        cpds  :  instances of LinearGaussianCPD
            List of LinearGaussianCPDs which will be associated with the model

        Examples
        --------
        >>> from pgmpy.models import LinearGaussianBayesianNetwork
        >>> from pgmpy.factors.continuous import LinearGaussianCPD
        >>> model = LinearGaussianBayesianNetwork([('x1', 'x2'), ('x2', 'x3')])
        >>> cpd1 = LinearGaussianCPD('x1', [1], 4)
        >>> cpd2 = LinearGaussianCPD('x2', [-5, 0.5], 4, ['x1'])
        >>> cpd3 = LinearGaussianCPD('x3', [4, -1], 3, ['x2'])
        >>> model.add_cpds(cpd1, cpd2, cpd3)
        >>> for cpd in model.cpds:
                print(cpd)

        P(x1) = N(1; 4)
        P(x2| x1) = N(0.5*x1_mu); -5)
        P(x3| x2) = N(-1*x2_mu); 4)

        """
        for cpd in cpds:
            if not isinstance(cpd, LinearGaussianCPD):
                raise ValueError('Only LinearGaussianCPD can be added.')

            if set(cpd.variables) - set(cpd.variables).intersection(
                    set(self.nodes())):
                raise ValueError('CPD defined on variable not in the model', cpd)

            for prev_cpd_index in range(len(self.cpds)):
                if self.cpds[prev_cpd_index].variable == cpd.variable:
                    logging.warning("Replacing existing CPD for {var}".format(var=cpd.variable))
                    self.cpds[prev_cpd_index] = cpd
                    break
            else:
                self.cpds.append(cpd)


Instruction:
Can you tell what is the following Python 3 function doing
def to_joint_gaussian(self):
        variables = nx.topological_sort(self)
        mean = np.zeros(len(variables))
        covariance = np.zeros((len(variables), len(variables)))

        for node_idx in range(len(variables)):
            cpd = self.get_cpds(variables[node_idx])
            mean[node_idx] = sum([coeff * mean[variables.index(parent)] for
                                  coeff, parent in zip(cpd.beta_vector, cpd.evidence)]) + cpd.beta_0
            covariance[node_idx, node_idx] = sum(
                [coeff * coeff * covariance[variables.index(parent), variables.index(parent)]
                 for coeff, parent in zip(cpd.beta_vector, cpd.evidence)]) + cpd.variance

        for node_i_idx in range(len(variables)):
            for node_j_idx in range(len(variables)):
                if covariance[node_j_idx, node_i_idx] != 0:
                    covariance[node_i_idx, node_j_idx] = covariance[node_j_idx, node_i_idx]
                else:
                    cpd_j = self.get_cpds(variables[node_j_idx])
                    covariance[node_i_idx, node_j_idx] = sum(
                        [coeff * covariance[node_i_idx, variables.index(parent)]
                         for coeff, parent in zip(cpd_j.beta_vector, cpd_j.evidence)])

        return GaussianDistribution(variables, mean, covariance)

Response: This method converts the linear Gaussian Bayesian Network into a joint Gaussian distribution.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def check_model(self):
        for node in self.nodes():
            cpd = self.get_cpds(node=node)

            if isinstance(cpd, LinearGaussianCPD):
                if set(cpd.evidence) != set(self.get_parents(node)):
                    raise ValueError("CPD associated with %s doesn't have "
                                     "proper parents associated with it." % node)
        return True

Response: Checks the model for various errors.


Instruction:
Can you generate the documentation for the following Python 3 function
def factorset_product(*factorsets_list):
    if not all(isinstance(factorset, FactorSet) for factorset in factorsets_list):
        raise TypeError("Input parameters must be FactorSet instances")
    return reduce(lambda x, y: x.product(y, inplace=False), factorsets_list)

Response: r Base method used for product of two factor sets.


Instruction:
How would you explain what the following Python 3 function does
def factorset_divide(factorset1, factorset2):
    if not isinstance(factorset1, FactorSet) or not isinstance(factorset2, FactorSet):
        raise TypeError("factorset1 and factorset2 must be FactorSet instances")
    return factorset1.divide(factorset2, inplace=False)

Response: r Base method for division of two factor sets.


Instruction:
Can you tell what is the following Python 3 function doing
def divide(self, factorset, inplace=True):
        factor_set = self if inplace else self.copy()
        factor_set1 = factorset.copy()

        factor_set.add_factors(*[phi.identity_factor() / phi for phi in factor_set1.factors])

        if not inplace:
            return factor_set

Response: r Divides two factor sets and returns a new FactorSet instance after division by the factor set.


Instruction:
Make a summary of the following Python 3 code
def add_node(self, node, **attr):
        super(DynamicBayesianNetwork, self).add_node((node, 0), **attr)

Response: Adds a single node to the Network


Instruction:
How would you explain what the following Python 3 function does
def _nodes(self):
        return list(set([node for node, timeslice in
                         super(DynamicBayesianNetwork, self).nodes()]))

Response: Returns the list of nodes present in the network.


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

Response:
def add_edge(self, start, end, **kwargs):
        """
        Add an edge between two nodes.

        The nodes will be automatically added if they are not present in the network.

        Parameters
        ----------
        start: tuple
               Both the start and end nodes should specify the time slice as
               (node_name, time_slice). Here, node_name can be any hashable
               python object while the time_slice is an integer value,
               which denotes the time slice that the node belongs to.

        end: tuple
               Both the start and end nodes should specify the time slice as
               (node_name, time_slice). Here, node_name can be any hashable
               python object while the time_slice is an integer value,
               which denotes the time slice that the node belongs to.

        Examples
        --------
        >>> from pgmpy.models import DynamicBayesianNetwork as DBN
        >>> model = DBN()
        >>> model.add_nodes_from(['D', 'I'])
        >>> model.add_edge(('D',0), ('I',0))
        >>> sorted(model.edges())
        [(('D', 0), ('I', 0)), (('D', 1), ('I', 1))]
        """
        try:
            if len(start) != 2 or len(end) != 2:
                raise ValueError('Nodes must be of type (node, time_slice).')
            elif not isinstance(start[1], int) or not isinstance(end[1], int):
                raise ValueError('Nodes must be of type (node, time_slice).')
            elif start[1] == end[1]:
                start = (start[0], 0)
                end = (end[0], 0)
            elif start[1] == end[1] - 1:
                start = (start[0], 0)
                end = (end[0], 1)
            elif start[1] > end[1]:
                raise NotImplementedError('Edges in backward direction are not allowed.')
            elif start[1] != end[1]:
                raise ValueError("Edges over multiple time slices is not currently supported")
        except TypeError:
            raise ValueError('Nodes must be of type (node, time_slice).')

        if start == end:
            raise ValueError('Self Loops are not allowed')
        elif start in super(DynamicBayesianNetwork, self).nodes() and end \
                in super(DynamicBayesianNetwork, self).nodes() and \
                nx.has_path(self, end, start):
            raise ValueError('Loops are not allowed. Adding the edge from ({start} --> {end}) forms a loop.'.format(
                start=str(start), end=str(end)))

        super(DynamicBayesianNetwork, self).add_edge(start, end, **kwargs)

        if start[1] == end[1]:
            super(DynamicBayesianNetwork, self).add_edge((start[0], 1 - start[1]), (end[0], 1 - end[1]))
        else:
            super(DynamicBayesianNetwork, self).add_node((end[0], 1 - end[1]))


Instruction:
Make a summary of the following Python 3 code
def add_edges_from(self, ebunch, **kwargs):
        for edge in ebunch:
            self.add_edge(edge[0], edge[1])

Response: Add all the edges from the given ebunch to the current node.


Instruction:
Write a Python 3 function that can
return the intra slice edges present in the 2 - TBN.

Response:
def get_intra_edges(self, time_slice=0):
        """
        Returns the intra slice edges present in the 2-TBN.

        Parameter
        ---------
        time_slice: int (whole number)
                The time slice for which to get intra edges. The timeslice
                should be a positive value or zero.

        Examples
        --------
        >>> from pgmpy.models import DynamicBayesianNetwork as DBN
        >>> dbn = DBN()
        >>> dbn.add_nodes_from(['D', 'G', 'I', 'S', 'L'])
        >>> dbn.add_edges_from([(('D', 0), ('G', 0)), (('I', 0), ('G', 0)),
        ...                     (('G', 0), ('L', 0)), (('D', 0), ('D', 1)),
        ...                     (('I', 0), ('I', 1)), (('G', 0), ('G', 1)),
        ...                     (('G', 0), ('L', 1)), (('L', 0), ('L', 1))])
        >>> dbn.get_intra_edges()
        [(('D', 0), ('G', 0)), (('G', 0), ('L', 0)), (('I', 0), ('G', 0))
        """
        if not isinstance(time_slice, int) or time_slice < 0:
            raise ValueError("The timeslice should be a positive value greater than or equal to zero")

        return [tuple((x[0], time_slice) for x in edge) for edge in self.edges() if edge[0][1] == edge[1][1] == 0]


Instruction:
How would you explain what the following Python 3 function does
def get_interface_nodes(self, time_slice=0):
        if not isinstance(time_slice, int) or time_slice < 0:
            raise ValueError("The timeslice should be a positive value greater than or equal to zero")

        return [(edge[0][0], time_slice) for edge in self.get_inter_edges()]

Response: Returns the nodes in the first timeslice whose children are present in the first timeslice.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def get_slice_nodes(self, time_slice=0):
        if not isinstance(time_slice, int) or time_slice < 0:
            raise ValueError("The timeslice should be a positive value greater than or equal to zero")

        return [(node, time_slice) for node in self._nodes()]

Response: Returns the nodes present in a particular timeslice.


Instruction:
Implement a Python 3 function for
returning the CPDs that have been associated with the network.

Response:
def get_cpds(self, node=None, time_slice=0):
        """
        Returns the CPDs that have been associated with the network.

        Parameters
        ----------
        node: tuple (node_name, time_slice)
            The node should be in the following form (node_name, time_slice).
            Here, node_name is the node that is inserted while the time_slice is
            an integer value, which denotes the index of the time_slice that the
            node belongs to.

        time_slice: int
            The time_slice should be a positive integer greater than or equal to zero.

        Examples
        --------
        >>> from pgmpy.models import DynamicBayesianNetwork as DBN
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> dbn = DBN()
        >>> dbn.add_edges_from([(('D',0),('G',0)),(('I',0),('G',0)),(('D',0),('D',1)),(('I',0),('I',1))])
        >>> grade_cpd =  TabularCPD(('G',0), 3, [[0.3,0.05,0.9,0.5],
        ...                                      [0.4,0.25,0.8,0.03],
        ...                                      [0.3,0.7,0.02,0.2]], [('I', 0),('D', 0)],[2,2])
        >>> dbn.add_cpds(grade_cpd)
        >>> dbn.get_cpds()
        """
        # TODO: fix bugs in this
        if node:
            if node not in super(DynamicBayesianNetwork, self).nodes():
                raise ValueError('Node not present in the model.')
            else:
                for cpd in self.cpds:
                    if cpd.variable == node:
                        return cpd
        else:
            return [cpd for cpd in self.cpds if set(list(cpd.variables)).issubset(self.get_slice_nodes(time_slice))]


Instruction:
Explain what the following Python 3 code does
def remove_cpds(self, *cpds):
        for cpd in cpds:
            if isinstance(cpd, (tuple, list)):
                cpd = self.get_cpds(cpd)
            self.cpds.remove(cpd)

Response: This method removes the cpds that are provided in the argument.


Instruction:
Implement a function in Python 3 to
check the model for various errors.

Response:
def check_model(self):
        """
        Check the model for various errors. This method checks for the following
        errors.

        * Checks if the sum of the probabilities in each associated CPD for each
            state is equal to 1 (tol=0.01).
        * Checks if the CPDs associated with nodes are consistent with their parents.

        Returns
        -------
        boolean: True if everything seems to be order. Otherwise raises error
            according to the problem.
        """
        for node in super(DynamicBayesianNetwork, self).nodes():
            cpd = self.get_cpds(node=node)
            if isinstance(cpd, TabularCPD):
                evidence = cpd.variables[:0:-1]
                evidence_card = cpd.cardinality[:0:-1]
                parents = self.get_parents(node)
                if set(evidence) != set(parents if parents else []):
                    raise ValueError("CPD associated with {node} doesn't have "
                                     "proper parents associated with it.".format(node=node))
                if not np.allclose(cpd.to_factor().marginalize([node], inplace=False).values.flatten('C'),
                                   np.ones(np.product(evidence_card)),
                                   atol=0.01):
                    raise ValueError('Sum of probabilities of states for node {node}'
                                     ' is not equal to 1'.format(node=node))
        return True


Instruction:
How would you implement a function in Python 3 that
creates a graph of all the immoralities in the DynamicBayesian Network and returns it.

Response:
def moralize(self):
        """
        Removes all the immoralities in the Network 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.models import DynamicBayesianNetwork as DBN
        >>> dbn = DBN([(('D',0), ('G',0)), (('I',0), ('G',0))])
        >>> moral_graph = dbn.moralize()
        >>> moral_graph.edges()
        [(('G', 0), ('I', 0)),
        (('G', 0), ('D', 0)),
        (('D', 1), ('I', 1)),
        (('D', 1), ('G', 1)),
        (('I', 0), ('D', 0)),
        (('G', 1), ('I', 1))]
        """
        moral_graph = self.to_undirected()

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

        return moral_graph


Instruction:
Here you have a function in Python 3, explain what it does
def copy(self):
        dbn = DynamicBayesianNetwork()
        dbn.add_nodes_from(self._nodes())
        dbn.add_edges_from(self.edges())
        cpd_copy = [cpd.copy() for cpd in self.get_cpds()]
        dbn.add_cpds(*cpd_copy)
        return dbn

Response: Returns a copy of the dynamic bayesian network.


Instruction:
Implement a function in Python 3 to
generate a list of legal operations for a given model.

Response:
def _legal_operations(self, model, tabu_list=[], max_indegree=None):
        """Generates a list of legal (= not in tabu_list) graph modifications
        for a given model, together with their score changes. Possible graph modifications:
        (1) add, (2) remove, or (3) flip a single edge. For details on scoring
        see Koller & Fridman, Probabilistic Graphical Models, Section 18.4.3.3 (page 818).
        If a number `max_indegree` is provided, only modifications that keep the number
        of parents for each node below `max_indegree` are considered."""

        local_score = self.scoring_method.local_score
        nodes = self.state_names.keys()
        potential_new_edges = (set(permutations(nodes, 2)) -
                               set(model.edges()) -
                               set([(Y, X) for (X, Y) in model.edges()]))

        for (X, Y) in potential_new_edges:  # (1) add single edge
            if nx.is_directed_acyclic_graph(nx.DiGraph(list(model.edges()) + [(X, Y)])):
                operation = ('+', (X, Y))
                if operation not in tabu_list:
                    old_parents = model.get_parents(Y)
                    new_parents = old_parents + [X]
                    if max_indegree is None or len(new_parents) <= max_indegree:
                        score_delta = local_score(Y, new_parents) - local_score(Y, old_parents)
                        yield(operation, score_delta)

        for (X, Y) in model.edges():  # (2) remove single edge
            operation = ('-', (X, Y))
            if operation not in tabu_list:
                old_parents = model.get_parents(Y)
                new_parents = old_parents[:]
                new_parents.remove(X)
                score_delta = local_score(Y, new_parents) - local_score(Y, old_parents)
                yield(operation, score_delta)

        for (X, Y) in model.edges():  # (3) flip single edge
            new_edges = list(model.edges()) + [(Y, X)]
            new_edges.remove((X, Y))
            if nx.is_directed_acyclic_graph(nx.DiGraph(new_edges)):
                operation = ('flip', (X, Y))
                if operation not in tabu_list and ('flip', (Y, X)) not in tabu_list:
                    old_X_parents = model.get_parents(X)
                    old_Y_parents = model.get_parents(Y)
                    new_X_parents = old_X_parents + [Y]
                    new_Y_parents = old_Y_parents[:]
                    new_Y_parents.remove(X)
                    if max_indegree is None or len(new_X_parents) <= max_indegree:
                        score_delta = (local_score(X, new_X_parents) +
                                       local_score(Y, new_Y_parents) -
                                       local_score(X, old_X_parents) -
                                       local_score(Y, old_Y_parents))
                        yield(operation, score_delta)


Instruction:
Can you tell what is the following Python 3 function doing
def estimate(self, start=None, tabu_length=0, max_indegree=None):
        epsilon = 1e-8
        nodes = self.state_names.keys()
        if start is None:
            start = DAG()
            start.add_nodes_from(nodes)
        elif not isinstance(start, DAG) or not set(start.nodes()) == set(nodes):
            raise ValueError("'start' should be a DAG with the same variables as the data set, or 'None'.")

        tabu_list = []
        current_model = start

        while True:
            best_score_delta = 0
            best_operation = None

            for operation, score_delta in self._legal_operations(current_model, tabu_list, max_indegree):
                if score_delta > best_score_delta:
                    best_operation = operation
                    best_score_delta = score_delta

            if best_operation is None or best_score_delta < epsilon:
                break
            elif best_operation[0] == '+':
                current_model.add_edge(*best_operation[1])
                tabu_list = ([('-', best_operation[1])] + tabu_list)[:tabu_length]
            elif best_operation[0] == '-':
                current_model.remove_edge(*best_operation[1])
                tabu_list = ([('+', best_operation[1])] + tabu_list)[:tabu_length]
            elif best_operation[0] == 'flip':
                X, Y = best_operation[1]
                current_model.remove_edge(X, Y)
                current_model.add_edge(Y, X)
                tabu_list = ([best_operation] + tabu_list)[:tabu_length]

        return current_model

Response: This method calculates the local hill climbing model for the structure containing the current state of the current state of the structure.


Instruction:
How would you explain what the following Python 3 function does
def get_elimination_order(self, nodes=None):
        if not nodes:
            nodes = self.bayesian_model.nodes()
        nodes = set(nodes)

        ordering = []
        while nodes:
            scores = {node: self.cost(node) for node in nodes}
            min_score_node = min(scores, key=scores.get)
            ordering.append(min_score_node)
            nodes.remove(min_score_node)
        return ordering

Response: Returns the optimal elimination order based on the cost function.


Instruction:
Write a Python 3 function that can
cost function for WeightedMinFill.

Response:
def cost(self, node):
        """
        Cost function for WeightedMinFill.
        The cost of eliminating a node is the sum of weights of the edges that need to
        be added to the graph due to its elimination, where a weight of an edge is the
        product of the weights, domain cardinality, of its constituent vertices.
        """
        edges = combinations(self.moralized_model.neighbors(node), 2)
        return sum([self.bayesian_model.get_cardinality(edge[0]) *
                    self.bayesian_model.get_cardinality(edge[1]) for edge in edges])


Instruction:
How would you implement a function in Python 3 that
returns the cost of eliminating a node.

Response:
def cost(self, node):
        """
        The cost of a eliminating a node is the product of weights, domain cardinality,
        of its neighbors.
        """
        return np.prod([self.bayesian_model.get_cardinality(neig_node) for neig_node in
                        self.moralized_model.neighbors(node)])


Instruction:
Can you write a function in Python 3 where it
returns a dictionary of STATICPROPERTIES

Response:
def get_static_properties(self):
        """
        Returns a dictionary of STATICPROPERTIES

        Examples
        --------
        >>> reader = XBNReader('xbn_test.xml')
        >>> reader.get_static_properties()
        {'FORMAT': 'MSR DTAS XML', 'VERSION': '0.2', 'CREATOR': 'Microsoft Research DTAS'}
        """
        return {tags.tag: tags.get('VALUE') for tags in self.bnmodel.find('STATICPROPERTIES')}


Instruction:
Write a Python 3 function for
returning a list of variables.

Response:
def get_variables(self):
        """
        Returns a list of variables.

        Examples
        --------
        >>> reader = XBNReader('xbn_test.xml')
        >>> reader.get_variables()
        {'a': {'TYPE': 'discrete', 'XPOS': '13495',
               'YPOS': '10465', 'DESCRIPTION': '(a) Metastatic Cancer',
               'STATES': ['Present', 'Absent']}
        'b': {'TYPE': 'discrete', 'XPOS': '11290',
               'YPOS': '11965', 'DESCRIPTION': '(b) Serum Calcium Increase',
               'STATES': ['Present', 'Absent']},
        'c': {....},
        'd': {....},
        'e': {....}
        }
        """
        variables = {}
        for variable in self.bnmodel.find('VARIABLES'):
            variables[variable.get('NAME')] = {'TYPE': variable.get('TYPE'),
                                               'XPOS': variable.get('XPOS'),
                                               'YPOS': variable.get('YPOS'),
                                               'DESCRIPTION': variable.find('DESCRIPTION').text,
                                               'STATES': [state.text for state in variable.findall('STATENAME')]}
        return variables


Instruction:
Write a Python 3 function that can
return a list of tuples containing two elements parent and child for each edge.

Response:
def get_edges(self):
        """
        Returns a list of tuples. Each tuple contains two elements (parent, child) for each edge.

        Examples
        --------
        >>> reader = XBNReader('xbn_test.xml')
        >>> reader.get_edges()
        [('a', 'b'), ('a', 'c'), ('b', 'd'), ('c', 'd'), ('c', 'e')]
        """
        return [(arc.get('PARENT'), arc.get('CHILD')) for arc in self.bnmodel.find('STRUCTURE')]


Instruction:
Can you write a function in Python 3 where it
returns a dictionary of name and its distribution.

Response:
def get_distributions(self):
        """
        Returns a dictionary of name and its distribution. Distribution is a ndarray.

        The ndarray is stored in the standard way such that the rightmost variable changes most often.
        Consider a CPD of variable 'd' which has parents 'b' and 'c' (distribution['CONDSET'] = ['b', 'c'])

                  |  d_0     d_1
        ---------------------------
        b_0, c_0  |  0.8     0.2
        b_0, c_1  |  0.9     0.1
        b_1, c_0  |  0.7     0.3
        b_1, c_1  |  0.05    0.95

        The value of distribution['d']['DPIS'] for the above example will be:
        array([[ 0.8 ,  0.2 ], [ 0.9 ,  0.1 ], [ 0.7 ,  0.3 ], [ 0.05,  0.95]])

        Examples
        --------
        >>> reader = XBNReader('xbn_test.xml')
        >>> reader.get_distributions()
        {'a': {'TYPE': 'discrete', 'DPIS': array([[ 0.2,  0.8]])},
         'e': {'TYPE': 'discrete', 'DPIS': array([[ 0.8,  0.2],
                 [ 0.6,  0.4]]), 'CONDSET': ['c'], 'CARDINALITY': [2]},
         'b': {'TYPE': 'discrete', 'DPIS': array([[ 0.8,  0.2],
                 [ 0.2,  0.8]]), 'CONDSET': ['a'], 'CARDINALITY': [2]},
         'c': {'TYPE': 'discrete', 'DPIS': array([[ 0.2 ,  0.8 ],
                 [ 0.05,  0.95]]), 'CONDSET': ['a'], 'CARDINALITY': [2]},
         'd': {'TYPE': 'discrete', 'DPIS': array([[ 0.8 ,  0.2 ],
                 [ 0.9 ,  0.1 ],
                 [ 0.7 ,  0.3 ],
                 [ 0.05,  0.95]]), 'CONDSET': ['b', 'c']}, 'CARDINALITY': [2, 2]}
        """
        distribution = {}
        for dist in self.bnmodel.find('DISTRIBUTIONS'):
            variable_name = dist.find('PRIVATE').get('NAME')
            distribution[variable_name] = {'TYPE': dist.get('TYPE')}
            if dist.find('CONDSET') is not None:
                distribution[variable_name]['CONDSET'] = [var.get('NAME') for
                                                          var in dist.find('CONDSET').findall('CONDELEM')]
                distribution[variable_name]['CARDINALITY'] = np.array(
                    [len(set(np.array([list(map(int, dpi.get('INDEXES').split()))
                                       for dpi in dist.find('DPIS')])[:, i]))
                     for i in range(len(distribution[variable_name]['CONDSET']))])
            distribution[variable_name]['DPIS'] = np.array(
                [list(map(float, dpi.text.split())) for dpi in dist.find('DPIS')])

        return distribution


Instruction:
How would you code a function in Python 3 to
return an instance of Bayesian Model.

Response:
def get_model(self):
        """
        Returns an instance of Bayesian Model.
        """
        model = BayesianModel()
        model.add_nodes_from(self.variables)
        model.add_edges_from(self.edges)
        model.name = self.model_name

        tabular_cpds = []
        for var, values in self.variable_CPD.items():
            evidence = values['CONDSET'] if 'CONDSET' in values else []
            cpd = values['DPIS']
            evidence_card = values['CARDINALITY'] if 'CARDINALITY' in values else []
            states = self.variables[var]['STATES']
            cpd = TabularCPD(var, len(states), cpd,
                             evidence=evidence,
                             evidence_card=evidence_card)
            tabular_cpds.append(cpd)

        model.add_cpds(*tabular_cpds)

        if nx.__version__.startswith('1'):
            for var, properties in self.variables.items():
                model.node[var] = properties
        else:
            for var, properties in self.variables.items():
                model._node[var] = properties

        return model


Instruction:
Can you generate the documentation for the following Python 3 function
def set_analysisnotebook(self, **data):
        for key, value in data.items():
            self.network.set(str(key), str(value))

Response: Set the attributes for ANALYSISNOTEBOOK tag


Instruction:
Write a Python 3 script to
set the STATICPROPERTIES tag for the network.

Response:
def set_static_properties(self, **data):
        """
        Set STATICPROPERTIES tag for the network

        Parameters
        ----------
        **data: dict
            {name: value} for name and value of the property.

        Examples
        --------
        >>> from pgmpy.readwrite.XMLBeliefNetwork import XBNWriter
        >>> writer = XBNWriter()
        >>> writer.set_static_properties(FORMAT="MSR DTAS XML", VERSION="0.2", CREATOR="Microsoft Research DTAS")
        """
        static_prop = etree.SubElement(self.bnmodel, 'STATICPROPERTIES')
        for key, value in data.items():
            etree.SubElement(static_prop, key, attrib={'VALUE': value})


Instruction:
Create a Python 3 function for
setting variables for the current object.

Response:
def set_variables(self, data):
        """
        Set variables for the network.

        Parameters
        ----------
        data: dict
            dict for variable in the form of example as shown.

        Examples
        --------
        >>> from pgmpy.readwrite.XMLBeliefNetwork import XBNWriter
        >>> writer = XBNWriter()
        >>> writer.set_variables({'a': {'TYPE': 'discrete', 'XPOS': '13495',
        ...                             'YPOS': '10465', 'DESCRIPTION': '(a) Metastatic Cancer',
        ...                             'STATES': ['Present', 'Absent']}
        ...                       'b': {'TYPE': 'discrete', 'XPOS': '11290',
        ...                             'YPOS': '11965', 'DESCRIPTION': '(b) Serum Calcium Increase',
        ...                             'STATES': ['Present', 'Absent']}})
        """
        variables = etree.SubElement(self.bnmodel, "VARIABLES")
        for var in sorted(data):
            variable = etree.SubElement(variables, 'VAR', attrib={'NAME': var, 'TYPE': data[var]['TYPE'],
                                                                  'XPOS': data[var]['XPOS'], 'YPOS': data[var]['YPOS']})
            etree.SubElement(variable, 'DESCRIPTION', attrib={'DESCRIPTION': data[var]['DESCRIPTION']})
            for state in data[var]['STATES']:
                etree.SubElement(variable, 'STATENAME').text = state


Instruction:
Can you generate a brief explanation for the following Python 3 code
def set_edges(self, edge_list):
        structure = etree.SubElement(self.bnmodel, 'STRUCTURE')
        for edge in edge_list:
            etree.SubElement(structure, 'ARC', attrib={'PARENT': edge[0], 'CHILD': edge[1]})

Response: Set edges in the network.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def set_distributions(self):
        distributions = etree.SubElement(self.bnmodel, 'DISTRIBUTIONS')

        cpds = self.model.get_cpds()
        cpds.sort(key=lambda x: x.variable)
        for cpd in cpds:
            cpd_values = cpd.values.ravel()
            var = cpd.variable
            dist = etree.SubElement(distributions, 'DIST', attrib={'TYPE': self.model.node[var]['TYPE']})
            etree.SubElement(dist, 'PRIVATE', attrib={'NAME': var})
            dpis = etree.SubElement(dist, 'DPIS')
            evidence = cpd.variables[:0:-1]
            if evidence:
                condset = etree.SubElement(dist, 'CONDSET')
                for condelem in sorted(evidence):
                    etree.SubElement(condset, 'CONDELEM', attrib={'NAME': condelem})
                # TODO: Get Index value.
                for val in range(0, len(cpd_values), 2):
                    etree.SubElement(dpis, "DPI", attrib={'INDEXES': ' '}).text = \
                        " " + str(cpd_values[val]) + " " + str(cpd_values[val+1]) + " "
            else:
                etree.SubElement(dpis, "DPI").text = ' ' + ' '.join(map(str, cpd_values))

Response: Set distributions in the network.


Instruction:
Can you tell what is the following Python 3 function doing
def add_edge(self, u, v, **kwargs):
        if u != v:
            super(FactorGraph, self).add_edge(u, v, **kwargs)
        else:
            raise ValueError('Self loops are not allowed')

Response: Add an edge between variable_node and factor_node.


Instruction:
Implement a Python 3 function for
checking the model for various errors.

Response:
def check_model(self):
        """
        Check the model for various errors. This method checks for the following
        errors. In the same time it also updates the cardinalities of all the
        random variables.

        * Check whether bipartite property of factor graph is still maintained
          or not.
        * Check whether factors are associated for all the random variables or not.
        * Check if factors are defined for each factor node or not.
        * Check if cardinality information for all the variables is availble or not.
        * Check if cardinality of random variable remains same across all the
          factors.
        """
        variable_nodes = set([x for factor in self.factors for x in factor.scope()])
        factor_nodes = set(self.nodes()) - variable_nodes

        if not all(isinstance(factor_node, DiscreteFactor) for factor_node in factor_nodes):
            raise ValueError('Factors not associated for all the random variables')

        if (not (bipartite.is_bipartite(self)) or
            not (bipartite.is_bipartite_node_set(self, variable_nodes) or
                 bipartite.is_bipartite_node_set(self, variable_nodes))):
            raise ValueError('Edges can only be between variables and factors')

        if len(factor_nodes) != len(self.factors):
            raise ValueError('Factors not associated with all the factor nodes.')

        cardinalities = self.get_cardinality()
        if len(variable_nodes) != len(cardinalities):
            raise ValueError('Factors for all the variables not defined')

        for factor in self.factors:
            for variable, cardinality in zip(factor.scope(), factor.cardinality):
                if (cardinalities[variable] != cardinality):
                    raise ValueError('Cardinality of variable {var} not matching among factors'.format(var=variable))

        return True


Instruction:
Explain what the following Python 3 code does
def get_variable_nodes(self):
        self.check_model()

        variable_nodes = set([x for factor in self.factors for x in factor.scope()])
        return list(variable_nodes)

Response: Returns a list of variable nodes present in the graph.


Instruction:
How would you explain what the following Python 3 function does
def get_factor_nodes(self):
        self.check_model()

        variable_nodes = self.get_variable_nodes()
        factor_nodes = set(self.nodes()) - set(variable_nodes)
        return list(factor_nodes)

Response: Returns a list of all the factors nodes present in the graph.


Instruction:
Explain what the following Python 3 code does
def to_markov_model(self):
        mm = MarkovModel()

        variable_nodes = self.get_variable_nodes()

        if len(set(self.nodes()) - set(variable_nodes)) != len(self.factors):
            raise ValueError('Factors not associated with all the factor nodes.')

        mm.add_nodes_from(variable_nodes)
        for factor in self.factors:
            scope = factor.scope()
            mm.add_edges_from(itertools.combinations(scope, 2))
            mm.add_factors(factor)

        return mm

Response: Converts the factor graph into a markov model.