<commit_before># -*- coding: utf-8 -*-
"""
Implements the DecisionNode class

Created on Mon Feb 18 10:31:17 2013

Copyright (C) 2013 <NAME> (<EMAIL>)

GNU Affero General Public License

"""
from __future__ import division

import numpy as np
import scipy as sp
import copy
import scipy.stats.distributions as randvars
from node import *
from pynfg.utilities.utilities import convert_2_pureCPT

class DecisionNode(Node):
    """Implements a decision node of the semi-NFG formalism by <NAME>
    
    The :class:`classes.DecisionNode` can be initialized with either a 
    conditional probability distribution (CPT) or a distribution object 
    from :py:mod:`scipy.stats.distributions` (discrete and continuous types 
    are both supported).
    
    :arg name: the name of the DecisionNode, usually descriptive, e.g. D5, 
       for player 5's decision node, or D51 for player 5's 1st decision node, 
       or D512 for player 5's 1st decision node in the 2nd time step, etc.
    :type name: str
    :arg player: the name of the player to which this DecisionNode belongs.
    :type player: str
    :arg space: the list of the possible values for the DecisionNode. The 
       order determines the order of the CPT when generated.
    :type space: list
    :arg parents: the list of the parents of the decision node. All entries 
       must be a :class:`classes.DecisionNode` or a discrete 
       :class:`classes.ChanceNode` or :class:`nodes.DeterNode`. The order of the 
       parents in the list determinesthe rder of the CPT when generated.
    :type parents: list
    :arg description: a description of the decision node, usually including 
       a summary description of the space, parents and children.
    :type description: str
    :arg time: the timestep to which the node belongs. This is generally 
       only used for :class:`seminfg.iterSemiNFG` objects.
    :type time: int
    :arg basename:  Reference to a theoretical node in the base or kernel.
    :type basename: str
    
    Formally, a decision node has the following properties:
        
       * belongs to a human player
       * has a space of possible values.
       * the conditional probability distribution from the values of its 
          parents - given by :py:meth:`classes.DecisionNode.prob()` or
          :py:meth:`classes.ChanceNode.prob()`, is not specified in the game. 
          That distribution is given by the solution concept applied to the 
          semi-NFG. This lack of CPDs at decision nodes is the reason the 
          semi-NFG is said to be based on a semi-Bayes net.

    .. note::
           
       For a :class:`classes.DecisionNode`, the parents nodes must be 
       discrete.
    
    Example::
        
        import scipy.stats.distributions as randvars
                        
        dist1 = randvars.randint
        params1 = [1, 4]
        space1 = [1, 2, 3]
        distip1 = (dist1, params1, space1)
        C1 = ChanceNode('C1', distip=distip1, description='root CN given by randint 1 to 4')
                        
        D1 = DecisionNode('D1', '1', [-1, 0, 1], parents=[C1], description='This is a child node of C1')
                            
    Upon initialization, the following private method is called: 
    :py:meth:`classes.DecisionNode._set_parent_dict()`
    
    Some useful methods are:
       
    * :py:meth:`classes.DecisionNode.draw_value()` 
    * :py:meth:`classes.DecisionNode.prob()`
    * :py:meth:`classes.DecisionNode.logprob()`
    * :py:meth:`classes.DecisionNode.randomCPT()`
    * :py:meth:`classes.DecisionNode.perturbCPT()`
        
    """
    def __init__(self, name, player, space, parents=None, \
                 description='no description', time=None, basename=None, \
                 verbose=False):
        if verbose:
            try:
                print 'Name: '+ name + '\nDescription: '+ description + \
                    '\nPlayer: '+ player 
            except (AttributeError, TypeError):
                raise AssertionError('name, description, \
                                     player should be strings')
        self.continuous = False
        self.name = name
        self.description = description
        self.time = time
        self.basename = basename
        self.player = player
        if isinstance(space, list):
            self.space = space
        else:
            raise TypeError('The space must be a list')
        if parents is None:
            parents = []
        self.parents = self._set_parent_dict(parents)
        self._createCPT()
        self._check_disc_parents()
#        self.set_value(self.space[0])
        self.continuous = False
        
    def __str__(self):
        return self.name
        
    def _createCPT(self):
        """Create a CPT of the correct size with zeros for the DecisionNode
        
        Uses the order of the parents in the parent list as entered by the user 
        to initialize the DecisionNode object and the sizes of space attributes 
        of the parents to create a :py:func:`numpy.zeros()` array of the 
        appropriate size and shape.
        
        """
        CPT_shape = []
        for par in self.parents:
            CPT_shape.append(len(self.parents[par].space))
        CPT_shape.append(len(self.space))
        self.CPT = np.zeros(CPT_shape)
        
    def draw_value(self, parentinput=None, setvalue=True, mode=False):
        """Draw a value from the :class:`classes.DecisionNode` object
        
        :arg parentinput: Optional. Specify values of the parents at which to 
           draw values using the CPT. Keys are parent names. Values are parent 
           values. To specify values for only a subset of the parents, only 
           enter those parents in the dictionary. If no parent values are 
           specified, then the current values of the parents are used. 
        :type parentinput: dict<commit_msg>changed _createCPT method of DecisionNode to work without parents<commit_after># -*- coding: utf-8 -*-
"""
Implements the DecisionNode class

Created on Mon Feb 18 10:31:17 2013

Copyright (C) 2013 <NAME> (<EMAIL>)

GNU Affero General Public License

"""
from __future__ import division

import numpy as np
import scipy as sp
import copy
import scipy.stats.distributions as randvars
from node import *
from pynfg.utilities.utilities import convert_2_pureCPT

class DecisionNode(Node):
    """Implements a decision node of the semi-NFG formalism by <NAME>
    
    The :class:`classes.DecisionNode` can be initialized with either a 
    conditional probability distribution (CPT) or a distribution object 
    from :py:mod:`scipy.stats.distributions` (discrete and continuous types 
    are both supported).
    
    :arg name: the name of the DecisionNode, usually descriptive, e.g. D5, 
       for player 5's decision node, or D51 for player 5's 1st decision node, 
       or D512 for player 5's 1st decision node in the 2nd time step, etc.
    :type name: str
    :arg player: the name of the player to which this DecisionNode belongs.
    :type player: str
    :arg space: the list of the possible values for the DecisionNode. The 
       order determines the order of the CPT when generated.
    :type space: list
    :arg parents: the list of the parents of the decision node. All entries 
       must be a :class:`classes.DecisionNode` or a discrete 
       :class:`classes.ChanceNode` or :class:`nodes.DeterNode`. The order of the 
       parents in the list determinesthe rder of the CPT when generated.
    :type parents: list
    :arg description: a description of the decision node, usually including 
       a summary description of the space, parents and children.
    :type description: str
    :arg time: the timestep to which the node belongs. This is generally 
       only used for :class:`seminfg.iterSemiNFG` objects.
    :type time: int
    :arg basename:  Reference to a theoretical node in the base or kernel.
    :type basename: str
    
    Formally, a decision node has the following properties:
        
       * belongs to a human player
       * has a space of possible values.
       * the conditional probability distribution from the values of its 
          parents - given by :py:meth:`classes.DecisionNode.prob()` or
          :py:meth:`classes.ChanceNode.prob()`, is not specified in the game. 
          That distribution is given by the solution concept applied to the 
          semi-NFG. This lack of CPDs at decision nodes is the reason the 
          semi-NFG is said to be based on a semi-Bayes net.

    .. note::
           
       For a :class:`classes.DecisionNode`, the parents nodes must be 
       discrete.
    
    Example::
        
        import scipy.stats.distributions as randvars
                        
        dist1 = randvars.randint
        params1 = [1, 4]
        space1 = [1, 2, 3]
        distip1 = (dist1, params1, space1)
        C1 = ChanceNode('C1', distip=distip1, description='root CN given by randint 1 to 4')
                        
        D1 = DecisionNode('D1', '1', [-1, 0, 1], parents=[C1], description='This is a child node of C1')
                            
    Upon initialization, the following private method is called: 
    :py:meth:`classes.DecisionNode._set_parent_dict()`
    
    Some useful methods are:
       
    * :py:meth:`classes.DecisionNode.draw_value()` 
    * :py:meth:`classes.DecisionNode.prob()`
    * :py:meth:`classes.DecisionNode.logprob()`
    * :py:meth:`classes.DecisionNode.randomCPT()`
    * :py:meth:`classes.DecisionNode.perturbCPT()`
        
    """
    def __init__(self, name, player, space, parents=None, \
                 description='no description', time=None, basename=None, \
                 verbose=False):
        if verbose:
            try:
                print 'Name: '+ name + '\nDescription: '+ description + \
                    '\nPlayer: '+ player 
            except (AttributeError, TypeError):
                raise AssertionError('name, description, \
                                     player should be strings')
        self.continuous = False
        self.name = name
        self.description = description
        self.time = time
        self.basename = basename
        self.player = player
        if isinstance(space, list):
            self.space = space
        else:
            raise TypeError('The space must be a list')
        if parents is None:
            parents = []
        self.parents = self._set_parent_dict(parents)
        self._createCPT()
        self._check_disc_parents()
#        self.set_value(self.space[0])
        self.continuous = False
        
    def __str__(self):
        return self.name
        
    def _createCPT(self):
        """Create a CPT of the correct size with zeros for the DecisionNode
        
        Uses the order of the parents in the parent list as entered by the user 
        to initialize the DecisionNode object and the sizes of space attributes 
        of the parents to create a :py:func:`numpy.zeros()` array of the 
        appropriate size and shape.
        
        """
        CPT_shape = []
        for par in self.parents:
            CPT_shape.append(len(self.parents[par].space))
        if not CPT_shape:
            CPT_shape.append(1)
        CPT_shape.append(len(self.space))
        self.CPT = np.zeros(CPT_shape)
        
    def draw_value(self, parentinput=None, setvalue=True, mode=False):
        """Draw a value from the :class:`classes.DecisionNode` object
        
        :arg parentinput: Optional. Specify values of the parents at which to 
           draw values using the CPT. Keys are parent names. Values are parent 
           values. To specify values for only a subset of the parents, only 
           enter those parents in the dictionary. If no parent values are 
           specified, then the current values of the parents are used. 
        :type parentinput: dict
{'fraction_non_alphanumeric': 0.07831628374979037, 'fraction_numerical': 0.009475096427972497, 'mean_word_length': 2.267671232876712, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '640730', 'n_tokens_mistral': 3372, 'n_tokens_neox': 3148, 'n_words': 1239}