Example #1
0
 def make_random(cls, pspace=None, division=None, zero=True,
                 number_type='float'):
     """Generate a random coherent lower probability."""
     # for now this is just a pretty dumb method
     pspace = PSpace.make(pspace)
     lpr = cls(pspace=pspace, number_type=number_type)
     for event in pspace.subsets():
         if len(event) == 0:
             continue
         if len(event) == len(pspace):
             continue
         gamble = event.indicator(number_type=number_type)
         if division is None:
             # a number between 0 and len(event) / len(pspace)
             lprob = random.random() * len(event) / len(pspace)
         else:
             # a number between 0 and len(event) / len(pspace)
             # but discretized
             lprob = Fraction(
                 random.randint(
                     0 if zero else 1,
                     (division * len(event)) // len(pspace)),
                 division)
         # make a copy of lpr
         tmplpr = cls(pspace=pspace, mapping=lpr, number_type=number_type)
         # set new assignment, and give up if it incurs sure loss
         tmplpr.set_lower(gamble, lprob)
         if not tmplpr.is_avoiding_sure_loss():
             break
         else:
             lpr.set_lower(gamble, lprob)
     # done! return coherent version of it
     return lpr.get_coherent()
Example #2
0
 def __init__(self, pspace, data=None):
     self._data = OrderedDict()
     self._pspace = PSpace.make(pspace)
     # extract data
     if isinstance(data, collections.Mapping):
         for key, value in data.iteritems():
             self[key] = value
     elif data is not None:
         raise TypeError('data must be a mapping')
Example #3
0
 def __init__(self, pspace, data=None):
     self._data = OrderedDict()
     self._pspace = PSpace.make(pspace)
     # extract data
     if isinstance(data, collections.Mapping):
         for key, value in data.iteritems():
             self[key] = value
     elif data is not None:
         raise TypeError('data must be a mapping')
Example #4
0
    def make_random(cls,
                    pspace=None,
                    division=None,
                    zero=True,
                    number_type='float'):
        """Generate a random probability mass function.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param division: If specified, probabilities
            will be divisible by this factor.
        :type division: :class:`int`
        :param zero: Whether to allow for zero probability.
        :type zero: :class:`bool`
        :param number_type: The number type.
        :type number_type: :class:`str`

        >>> import random
        >>> random.seed(25)
        >>> print(Prob.make_random("abcd", division=10))
        a : 0.4
        b : 0.0
        c : 0.1
        d : 0.5
        >>> random.seed(25)
        >>> print(Prob.make_random("abcd", division=10, zero=False))
        a : 0.3
        b : 0.1
        c : 0.2
        d : 0.4
        """
        pspace = PSpace.make(pspace)
        lpr = cls(pspace=pspace, number_type=number_type)
        # get a uniform sample from the simplex
        probs = [-math.log(random.random()) for omega in pspace]
        sum_probs = sum(probs)
        probs = [prob / sum_probs for prob in probs]
        if division is not None:
            # discretize the probabilities
            probs = [
                int(prob * division + 0.5) + (0 if zero else 1)
                for prob in probs
            ]
            # now, again ensure they sum to division
            while sum(probs) < division:
                probs[random.randrange(len(probs))] += 1
            while sum(probs) > division:
                while True:
                    idx = random.randrange(len(probs))
                    if probs[idx] > (1 if zero else 2):
                        probs[idx] -= 1
                        break
            # convert to fraction
            probs = [fractions.Fraction(prob, division) for prob in probs]
        # return the probability
        return cls(pspace=pspace, number_type=number_type, prob=probs)
Example #5
0
    def make_random(cls, pspace=None, division=None, zero=True,
                    number_type='float'):
        """Generate a random probability mass function.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param division: If specified, probabilities
            will be divisible by this factor.
        :type division: :class:`int`
        :param zero: Whether to allow for zero probability.
        :type zero: :class:`bool`
        :param number_type: The number type.
        :type number_type: :class:`str`

        >>> import random
        >>> random.seed(25)
        >>> print(Prob.make_random("abcd", division=10))
        a : 0.4
        b : 0.0
        c : 0.1
        d : 0.5
        >>> random.seed(25)
        >>> print(Prob.make_random("abcd", division=10, zero=False))
        a : 0.3
        b : 0.1
        c : 0.2
        d : 0.4
        """
        pspace = PSpace.make(pspace)
        lpr = cls(pspace=pspace, number_type=number_type)
        # get a uniform sample from the simplex
        probs = [-math.log(random.random()) for omega in pspace]
        sum_probs = sum(probs)
        probs = [prob / sum_probs for prob in probs]
        if division is not None:
            # discretize the probabilities
            probs = [int(prob * division + 0.5) + (0 if zero else 1)
                     for prob in probs]
            # now, again ensure they sum to division
            while sum(probs) < division:
                probs[random.randrange(len(probs))] += 1
            while sum(probs) > division:
                while True:
                    idx = random.randrange(len(probs))
                    if probs[idx] > (1 if zero else 2):
                        probs[idx] -= 1
                        break
            # convert to fraction
            probs = [fractions.Fraction(prob, division) for prob in probs]
        # return the probability
        return cls(pspace=pspace, number_type=number_type, prob=probs)
Example #6
0
    def make_random(cls,
                    pspace=None,
                    division=None,
                    zero=True,
                    number_type='float'):
        """Generate a random coherent lower probability.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param division: If specified, generated numbers
            will be divisible by this factor.
        :type division: :class:`int`
        :param zero: Whether to allow for zero probability.
        :type zero: :class:`bool`
        :param number_type: The number type.
        :type number_type: :class:`str`

        .. todo::

           Current implementation is highly inefficient. Refactor to use
           :meth:`improb.lowprev.lowpoly.LowPoly.make_random` instead.
        """
        # for now this is just a pretty dumb method
        pspace = PSpace.make(pspace)
        lpr = cls(pspace=pspace, number_type=number_type)
        for event in pspace.subsets():
            if len(event) == 0:
                continue
            if len(event) == len(pspace):
                continue
            gamble = event.indicator(number_type=number_type)
            if division is None:
                # a number between 0 and len(event) / len(pspace)
                lprob = random.random() * len(event) / len(pspace)
            else:
                # a number between 0 and len(event) / len(pspace)
                # but discretized
                lprob = Fraction(
                    random.randint(0 if zero else 1,
                                   (division * len(event)) // len(pspace)),
                    division)
            # make a copy of lpr
            tmplpr = cls(pspace=pspace, mapping=lpr, number_type=number_type)
            # set new assignment, and give up if it incurs sure loss
            tmplpr.set_lower(gamble, lprob)
            if not tmplpr.is_avoiding_sure_loss():
                break
            else:
                lpr.set_lower(gamble, lprob)
        # done! return coherent version of it
        return lpr.get_coherent()
Example #7
0
    def make_random(cls, pspace=None, division=None, zero=True,
                    number_type='float'):
        """Generate a random coherent lower probability.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param division: If specified, generated numbers
            will be divisible by this factor.
        :type division: :class:`int`
        :param zero: Whether to allow for zero probability.
        :type zero: :class:`bool`
        :param number_type: The number type.
        :type number_type: :class:`str`

        .. todo::

           Current implementation is highly inefficient. Refactor to use
           :meth:`improb.lowprev.lowpoly.LowPoly.make_random` instead.
        """
        # for now this is just a pretty dumb method
        pspace = PSpace.make(pspace)
        lpr = cls(pspace=pspace, number_type=number_type)
        for event in pspace.subsets():
            if len(event) == 0:
                continue
            if len(event) == len(pspace):
                continue
            gamble = event.indicator(number_type=number_type)
            if division is None:
                # a number between 0 and len(event) / len(pspace)
                lprob = random.random() * len(event) / len(pspace)
            else:
                # a number between 0 and len(event) / len(pspace)
                # but discretized
                lprob = Fraction(
                    random.randint(
                        0 if zero else 1,
                        (division * len(event)) // len(pspace)),
                    division)
            # make a copy of lpr
            tmplpr = cls(pspace=pspace, mapping=lpr, number_type=number_type)
            # set new assignment, and give up if it incurs sure loss
            tmplpr.set_lower(gamble, lprob)
            if not tmplpr.is_avoiding_sure_loss():
                break
            else:
                lpr.set_lower(gamble, lprob)
        # done! return coherent version of it
        return lpr.get_coherent()
Example #8
0
def timeit(n=10, m=10):
    k = 10000 // n # number of trials, for timing

    gambles = [
        dict((i, random.randint(1, m))
             for i in xrange(n))
        for j in xrange(k)]

    pspace = PSpace(n)
    s = SetFunction(
        pspace=pspace,
        number_type='fraction')
    # hack so we do not need to fill the set function with data
    # (this solves issues when testing for large n)
    s._data = defaultdict(lambda: random.randint(-len(pspace), len(pspace)))

    t = time.clock()
    for gamble in gambles:
        s.get_choquet(gamble)
    return time.clock() - t
Example #9
0
    def make_random(cls, pspace=None, division=None, zero=True,
                    number_type='float'):
        """Generate a random probability mass function.

        >>> import random
        >>> random.seed(25)
        >>> print(Prob.make_random("abcd", division=10))
        a : 0.4
        b : 0.0
        c : 0.1
        d : 0.5
        >>> random.seed(25)
        >>> print(Prob.make_random("abcd", division=10, zero=False))
        a : 0.3
        b : 0.1
        c : 0.2
        d : 0.4
        """
        pspace = PSpace.make(pspace)
        lpr = cls(pspace=pspace, number_type=number_type)
        # get a uniform sample from the simplex
        probs = [-math.log(random.random()) for omega in pspace]
        sum_probs = sum(probs)
        probs = [prob / sum_probs for prob in probs]
        if division is not None:
            # discretize the probabilities
            probs = [int(prob * division + 0.5) + (0 if zero else 1)
                     for prob in probs]
            # now, again ensure they sum to division
            while sum(probs) < division:
                probs[random.randrange(len(probs))] += 1
            while sum(probs) > division:
                while True:
                    idx = random.randrange(len(probs))
                    if probs[idx] > (1 if zero else 2):
                        probs[idx] -= 1
                        break
            # convert to fraction
            probs = [fractions.Fraction(prob, division) for prob in probs]
        # return the probability
        return cls(pspace=pspace, number_type=number_type, prob=probs)
Example #10
0
    def __init__(self, pspace, data=None, number_type=None):
        """Construct a set function on the power set of the given
        possibility space.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param data: A mapping that defines the value on each event (missing values default to zero).
        :type data: :class:`dict`
        """
        if number_type is None:
            if data is not None:
                number_type = cdd.get_number_type_from_sequences(
                    data.itervalues())
            else:
                number_type = 'float'
        cdd.NumberTypeable.__init__(self, number_type)
        self._pspace = PSpace.make(pspace)
        self._data = {}
        if data is not None:
            for event, value in data.iteritems():
                self[event] = value
Example #11
0
    def __init__(self, pspace, data=None, number_type=None):
        """Construct a set function on the power set of the given
        possibility space.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param data: A mapping that defines the value on each event (missing values default to zero).
        :type data: :class:`dict`
        """
        if number_type is None:
            if data is not None:
                number_type = cdd.get_number_type_from_sequences(
                    data.itervalues())
            else:
                number_type = 'float'
        cdd.NumberTypeable.__init__(self, number_type)
        self._pspace = PSpace.make(pspace)
        self._data = {}
        if data is not None:
            for event, value in data.iteritems():
                self[event] = value
Example #12
0
    def make_extreme_n_monotone(cls, pspace, monotonicity=None):
        """Yield extreme lower probabilities with given monotonicity.

        .. warning::

           Currently this doesn't work very well except for the cases
           below.

        >>> lprs = list(LowProb.make_extreme_n_monotone('abc', monotonicity=2))
        >>> len(lprs)
        8
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        False
        >>> lprs = list(LowProb.make_extreme_n_monotone('abc', monotonicity=3))
        >>> len(lprs)
        7
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        True
        >>> lprs = list(LowProb.make_extreme_n_monotone('abcd', monotonicity=2))
        >>> len(lprs)
        41
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        False
        >>> all(lpr.is_n_monotone(4) for lpr in lprs)
        False
        >>> lprs = list(LowProb.make_extreme_n_monotone('abcd', monotonicity=3))
        >>> len(lprs)
        16
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(4) for lpr in lprs)
        False
        >>> lprs = list(LowProb.make_extreme_n_monotone('abcd', monotonicity=4))
        >>> len(lprs)
        15
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(4) for lpr in lprs)
        True
        >>> # cddlib hangs on larger possibility spaces
        >>> #lprs = list(LowProb.make_extreme_n_monotone('abcde', monotonicity=2))
        """
        pspace = PSpace.make(pspace)
        # constraint for empty set and full set
        matrix = cdd.Matrix(
            [[0] + [1 if event.is_false() else 0 for event in pspace.subsets()],
             [-1] + [1 if event.is_true() else 0 for event in pspace.subsets()]],
            linear=True,
            number_type='fraction')
        # constraints for monotonicity
        constraints = [
            dict(constraint) for constraint in
            cls.get_constraints_n_monotone(pspace, xrange(1, monotonicity + 1))]
        matrix.extend([[0] + [constraint.get(event, 0)
                              for event in pspace.subsets()]
                       for constraint in constraints])
        matrix.rep_type = cdd.RepType.INEQUALITY

        # debug: simplify matrix
        #print(pspace, monotonicity) # debug
        #print("original:", len(matrix))
        #matrix.canonicalize()
        #print("new     :", len(matrix))
        #print(matrix) # debug

        # calculate extreme points
        poly = cdd.Polyhedron(matrix)
        # convert these points back to lower probabilities
        #print(poly.get_generators()) # debug
        for vert in poly.get_generators():
            yield cls(
                pspace=pspace,
                lprob=dict((event, vert[1 + index])
                           for index, event in enumerate(pspace.subsets())),
                number_type='fraction')
Example #13
0
    def get_constraints_n_monotone(cls, pspace, monotonicity=None):
        """Yields constraints for lower probabilities with given
        monotonicity.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param monotonicity: Requested level of monotonicity (see
            notes below for details).
        :type monotonicity: :class:`int` or
            :class:`collections.Iterable` of :class:`int`

        As described in
        :meth:`~improb.setfunction.SetFunction.get_constraints_bba_n_monotone`,
        the n-monotonicity constraints on basic belief assignment are:

        .. math::

            \sum_{B\colon C\subseteq B\subseteq A}m(B)\ge 0

        for all :math:`C\subseteq A\subseteq\Omega`, with
        :math:`1\le|C|\le n`.

        By the Mobius transform, this is equivalent to:

        .. math::

            \sum_{B\colon C\subseteq B\subseteq A}
            \sum_{D\colon D\subseteq B}(-1)^{|B\setminus D|}
            \underline{P}(D)\ge 0

        Once noted that

        .. math::

            (C\subseteq B\subseteq A\quad \& \quad D\subseteq B)
            \iff
            (C\cup D\subseteq B\subseteq A\quad \& \quad D\subseteq A),

        we can conveniently rewrite the sum as:

        .. math::

            \sum_{D\colon D\subseteq A}
            \sum_{B\colon C\cup D\subseteq B\subseteq A}(-1)^{|B\setminus D|}
            \underline{P}(D)\ge 0

        This implementation iterates over all :math:`C\subseteq
        A\subseteq\Omega`, with :math:`|C|=n`, and yields each
        constraint as an iterable of (event, coefficient) pairs, where
        zero coefficients are omitted.

        .. note::

            As just mentioned, this method returns the constraints
            corresponding to the latter equation for :math:`|C|`
            equal to *monotonicity*. To get all the
            constraints for n-monotonicity, call this method with
            *monotonicity=xrange(1, n + 1)*.

            The rationale for this approach is that, in case you
            already know that (n-1)-monotonicity is satisfied, then
            you only need the constraints for *monotonicity=n* to
            check for n-monotonicity.

        .. note::

            The trivial constraints that the empty set must have lower
            probability zero, and that the possibility space must have
            lower probability one, are not included: so for
            *monotonicity=0* this method returns an empty iterator.

        >>> pspace = PSpace("abc")
        >>> for mono in xrange(1, len(pspace) + 1):
        ...     print("{0} monotonicity:".format(mono))
        ...     print(" ".join("{0:<{1}}".format("".join(i for i in event), len(pspace))
        ...                    for event in pspace.subsets()))
        ...     constraints = [
        ...         dict(constraint) for constraint in
        ...         LowProb.get_constraints_n_monotone(pspace, mono)]
        ...     constraints = [
        ...         [constraint.get(event, 0) for event in pspace.subsets()]
        ...         for constraint in constraints]
        ...     for constraint in sorted(constraints):
        ...         print(" ".join("{0:<{1}}".format(value, len(pspace))
        ...                        for value in constraint))
        1 monotonicity:
            a   b   c   ab  ac  bc  abc
        -1  0   0   1   0   0   0   0  
        -1  0   1   0   0   0   0   0  
        -1  1   0   0   0   0   0   0  
        0   -1  0   0   0   1   0   0  
        0   -1  0   0   1   0   0   0  
        0   0   -1  0   0   0   1   0  
        0   0   -1  0   1   0   0   0  
        0   0   0   -1  0   0   1   0  
        0   0   0   -1  0   1   0   0  
        0   0   0   0   -1  0   0   1  
        0   0   0   0   0   -1  0   1  
        0   0   0   0   0   0   -1  1  
        2 monotonicity:
            a   b   c   ab  ac  bc  abc
        0   0   0   1   0   -1  -1  1  
        0   0   1   0   -1  0   -1  1  
        0   1   0   0   -1  -1  0   1  
        1   -1  -1  0   1   0   0   0  
        1   -1  0   -1  0   1   0   0  
        1   0   -1  -1  0   0   1   0  
        3 monotonicity:
            a   b   c   ab  ac  bc  abc
        -1  1   1   1   -1  -1  -1  1  
        """
        pspace = PSpace.make(pspace)
        # check type
        if monotonicity is None:
            raise ValueError("specify monotonicity")
        elif isinstance(monotonicity, collections.Iterable):
            # special case: return it for all values in the iterable
            for mono in monotonicity:
                for constraint in cls.get_constraints_n_monotone(pspace, mono):
                    yield constraint
            return
        elif not isinstance(monotonicity, (int, long)):
            raise TypeError("monotonicity must be integer")
        # check value
        if monotonicity < 0:
            raise ValueError("specify a non-negative monotonicity")
        if monotonicity == 0:
            # don't return constraints in this case
            return
        # yield all constraints
        for event_a in pspace.subsets(size=xrange(monotonicity, len(pspace) + 1)):
            for subevent_c in pspace.subsets(event_a, size=monotonicity):
                yield (
                    (subevent_d,
                     sum((-1) ** len(event_b - subevent_d)
                         for event_b in pspace.subsets(
                             event_a, contains=(subevent_d | subevent_c))))
                    for subevent_d in pspace.subsets(event_a))
Example #14
0
    def __init__(self, pspace=None, mapping=None,
                 lprev=None, uprev=None, prev=None,
                 lprob=None, uprob=None, prob=None,
                 bba=None, credalset=None, number_type=None):
        """Construct a polyhedral lower prevision on *pspace*.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param mapping: Mapping from (gamble, event) to (lower prevision, upper prevision).
        :type mapping: :class:`collections.Mapping`
        :param lprev: Mapping from gamble to lower prevision.
        :type lprev: :class:`collections.Mapping`
        :param uprev: Mapping from gamble to upper prevision.
        :type uprev: :class:`collections.Mapping`
        :param prev: Mapping from gamble to precise prevision.
        :type prev: :class:`collections.Mapping`
        :param lprob: Mapping from event to lower probability.
        :type lprob: :class:`collections.Mapping` or :class:`collections.Sequence`
        :param uprob: Mapping from event to upper probability.
        :type uprob: :class:`collections.Mapping` or :class:`collections.Sequence`
        :param prob: Mapping from event to precise probability.
        :type prob: :class:`collections.Mapping` or :class:`collections.Sequence`
        :param bba: Mapping from event to basic belief assignment (useful for constructing belief functions).
        :type bba: :class:`collections.Mapping`
        :param credalset: Sequence of probability mass functions.
        :type credalset: :class:`collections.Sequence`
        :param number_type: The number type. If not specified, it is
            determined using
            :func:`~cdd.get_number_type_from_sequences` on all
            values.
        :type number_type: :class:`str`

        Generally, you can pass a :class:`dict` as a keyword argument
        in order to initialize the lower and upper previsions and/or
        probabilities:

        >>> print(LowPoly(pspace=3, mapping={
        ...     ((3, 1, 2), True): (1.5, None),
        ...     ((1, 0, -1), (1, 2)): (0.25, 0.3)})) # doctest: +NORMALIZE_WHITESPACE
         0    1   2
        3.0  1.0 2.0  | 0 1 2 : [1.5 ,     ]
        1.0  0.0 -1.0 |   1 2 : [0.25, 0.3 ]
        >>> print(LowPoly(pspace=3,
        ...     lprev={(1, 3, 2): 1.5, (2, 0, -1): 1},
        ...     uprev={(2, 0, -1): 1.9},
        ...     prev={(9, 8, 20): 15},
        ...     lprob={(1, 2): 0.2, (1,): 0.1},
        ...     uprob={(1, 2): 0.3, (0,): 0.9},
        ...     prob={(2,): '0.3'})) # doctest: +NORMALIZE_WHITESPACE
          0    1   2
         0.0  0.0 1.0  | 0 1 2 : [0.3 , 0.3 ]
         0.0  1.0 0.0  | 0 1 2 : [0.1 ,     ]
         0.0  1.0 1.0  | 0 1 2 : [0.2 , 0.3 ]
         1.0  0.0 0.0  | 0 1 2 : [    , 0.9 ]
         1.0  3.0 2.0  | 0 1 2 : [1.5 ,     ]
         2.0  0.0 -1.0 | 0 1 2 : [1.0 , 1.9 ]
         9.0  8.0 20.0 | 0 1 2 : [15.0, 15.0]

        A credal set can be specified simply as a list:

        >>> print(LowPoly(pspace=3,
        ...     credalset=[['0.1', '0.45', '0.45'],
        ...                ['0.4', '0.3', '0.3'],
        ...                ['0.3', '0.2', '0.5']]))
          0     1     2  
        -10   10    0     | 0 1 2 : [-1,   ]
        -1    -2    0     | 0 1 2 : [-1,   ]
        1     1     1     | 0 1 2 : [1 , 1 ]
        50/23 40/23 0     | 0 1 2 : [1 ,   ]

        As a special case, for lower/upper/precise probabilities, if
        you need to set values on singletons, you can use a list
        instead of a dictionary:

        >>> print(LowPoly(pspace='abc', lprob=['0.1', '0.2', '0.3'])) # doctest: +NORMALIZE_WHITESPACE
        a b c
        0 0 1 | a b c : [3/10, ]
        0 1 0 | a b c : [1/5 , ]
        1 0 0 | a b c : [1/10, ]

        If the first argument is a :class:`LowPoly` instance, then it
        is copied. For example:

        >>> from improb.lowprev.lowprob import LowProb
        >>> lpr = LowPoly(pspace='abc', lprob=['0.1', '0.1', '0.1'])
        >>> print(lpr)
        a b c
        0 0 1 | a b c : [1/10,     ]
        0 1 0 | a b c : [1/10,     ]
        1 0 0 | a b c : [1/10,     ]
        >>> lprob = LowProb(lpr)
        >>> print(lprob)
        a     : 1/10
          b   : 1/10
            c : 1/10
        """

        def iter_items(obj):
            """Return an iterator over all items of the mapping or the
            sequence.
            """
            if isinstance(obj, collections.Mapping):
                return obj.iteritems()
            elif isinstance(obj, collections.Sequence):
                if len(obj) < len(self.pspace):
                    raise ValueError('sequence too short')
                return (((omega,), value)
                        for omega, value in itertools.izip(self.pspace, obj))
            else:
                raise TypeError(
                    'expected collections.Mapping or collections.Sequence')

        def get_number_type(xprevs, xprobs):
            """Determine number type from arguments."""
            # special case: nothing specified, defaults to float
            if (all(xprev is None for xprev in xprevs)
                and all(xprob is None for xprob in xprobs)):
                return 'float'
            # inspect all values
            for xprev in xprevs:
                if xprev is None:
                    continue
                for key, value in xprev.iteritems():
                    # inspect gamble
                    if isinstance(key, Gamble):
                        if key.number_type == 'float':
                            return 'float'
                    elif isinstance(key, collections.Sequence):
                        if cdd.get_number_type_from_sequences(key) == 'float':
                            return 'float'
                    elif isinstance(key, collections.Mapping):
                        if cdd.get_number_type_from_sequences(key.itervalues()) == 'float':
                            return 'float'
                    # inspect value(s)
                    if isinstance(value, collections.Sequence):
                        if cdd.get_number_type_from_sequences(value) == 'float':
                            return 'float'
                    else:
                        if cdd.get_number_type_from_value(value) == 'float':
                            return 'float'
            for xprob in xprobs:
                if xprob is None:
                    continue
                for key, value in iter_items(xprob):
                    if cdd.get_number_type_from_value(value) == 'float':
                        return 'float'
            # everything is fraction
            return 'fraction'

        # if first argument is a LowPoly, then override all other arguments
        if isinstance(pspace, LowPoly):
            mapping = dict(pspace.iteritems())
            number_type = pspace.number_type
            pspace = pspace.pspace
        # initialize everything
        self._pspace = PSpace.make(pspace)
        if number_type is None:
            number_type = get_number_type(
                [mapping, lprev, uprev, prev, bba],
                [lprob, uprob, prob]
                + (credalset if credalset else []))
        cdd.NumberTypeable.__init__(self, number_type)
        self._mapping = {}
        if mapping:
            for key, value in mapping.iteritems():
                self[key] = value
        if lprev:
            for gamble, value in lprev.iteritems():
                self.set_lower(gamble, value)
        if uprev:
            for gamble, value in uprev.iteritems():
                self.set_upper(gamble, value)
        if prev:
            for gamble, value in prev.iteritems():
                self.set_precise(gamble, value)
        if lprob:
            for event, value in iter_items(lprob):
                event = self.pspace.make_event(event)
                self.set_lower(event, value)
        if uprob:
            for event, value in iter_items(uprob):
                event = self.pspace.make_event(event)
                self.set_upper(event, value)
        if prob:
            for event, value in iter_items(prob):
                event = self.pspace.make_event(event)
                self.set_precise(event, value)
        if bba:
            setfunc = SetFunction(
                pspace=self.pspace,
                data=bba,
                number_type=self.number_type)
            for event in self.pspace.subsets():
                self.set_lower(event, setfunc.get_zeta(event))
        if credalset:
            # set up polyhedral representation
            mat = cdd.Matrix([(['1'] + credalprob) for credalprob in credalset])
            mat.rep_type = cdd.RepType.GENERATOR
            poly = cdd.Polyhedron(mat)
            dualmat = poly.get_inequalities()
            #print(mat)
            #print(dualmat)
            for rownum, row in enumerate(dualmat):
                if rownum in dualmat.lin_set:
                    self.set_precise(row[1:], -row[0])
                else:
                    self.set_lower(row[1:], -row[0])
Example #15
0
    def make_extreme_n_monotone(cls, pspace, monotonicity=None):
        """Yield extreme lower probabilities with given monotonicity.

        .. warning::

           Currently this doesn't work very well except for the cases
           below.

        >>> lprs = list(LowProb.make_extreme_n_monotone('abc', monotonicity=2))
        >>> len(lprs)
        8
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        False
        >>> lprs = list(LowProb.make_extreme_n_monotone('abc', monotonicity=3))
        >>> len(lprs)
        7
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        True
        >>> lprs = list(LowProb.make_extreme_n_monotone('abcd', monotonicity=2))
        >>> len(lprs)
        41
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        False
        >>> all(lpr.is_n_monotone(4) for lpr in lprs)
        False
        >>> lprs = list(LowProb.make_extreme_n_monotone('abcd', monotonicity=3))
        >>> len(lprs)
        16
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(4) for lpr in lprs)
        False
        >>> lprs = list(LowProb.make_extreme_n_monotone('abcd', monotonicity=4))
        >>> len(lprs)
        15
        >>> all(lpr.is_coherent() for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(2) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(3) for lpr in lprs)
        True
        >>> all(lpr.is_n_monotone(4) for lpr in lprs)
        True
        >>> # cddlib hangs on larger possibility spaces
        >>> #lprs = list(LowProb.make_extreme_n_monotone('abcde', monotonicity=2))
        """
        pspace = PSpace.make(pspace)
        # constraint for empty set and full set
        matrix = cdd.Matrix([
            [0] + [1 if event.is_false() else 0 for event in pspace.subsets()],
            [-1] + [1 if event.is_true() else 0 for event in pspace.subsets()]
        ],
                            linear=True,
                            number_type='fraction')
        # constraints for monotonicity
        constraints = [
            dict(constraint) for constraint in cls.get_constraints_n_monotone(
                pspace, xrange(1, monotonicity + 1))
        ]
        matrix.extend(
            [[0] + [constraint.get(event, 0) for event in pspace.subsets()]
             for constraint in constraints])
        matrix.rep_type = cdd.RepType.INEQUALITY

        # debug: simplify matrix
        #print(pspace, monotonicity) # debug
        #print("original:", len(matrix))
        #matrix.canonicalize()
        #print("new     :", len(matrix))
        #print(matrix) # debug

        # calculate extreme points
        poly = cdd.Polyhedron(matrix)
        # convert these points back to lower probabilities
        #print(poly.get_generators()) # debug
        for vert in poly.get_generators():
            yield cls(pspace=pspace,
                      lprob=dict(
                          (event, vert[1 + index])
                          for index, event in enumerate(pspace.subsets())),
                      number_type='fraction')
Example #16
0
    def __init__(self,
                 pspace=None,
                 mapping=None,
                 lprev=None,
                 uprev=None,
                 prev=None,
                 lprob=None,
                 uprob=None,
                 prob=None,
                 bba=None,
                 credalset=None,
                 number_type=None):
        """Construct a polyhedral lower prevision on *pspace*.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param mapping: Mapping from (gamble, event) to (lower prevision, upper prevision).
        :type mapping: :class:`collections.Mapping`
        :param lprev: Mapping from gamble to lower prevision.
        :type lprev: :class:`collections.Mapping`
        :param uprev: Mapping from gamble to upper prevision.
        :type uprev: :class:`collections.Mapping`
        :param prev: Mapping from gamble to precise prevision.
        :type prev: :class:`collections.Mapping`
        :param lprob: Mapping from event to lower probability.
        :type lprob: :class:`collections.Mapping` or :class:`collections.Sequence`
        :param uprob: Mapping from event to upper probability.
        :type uprob: :class:`collections.Mapping` or :class:`collections.Sequence`
        :param prob: Mapping from event to precise probability.
        :type prob: :class:`collections.Mapping` or :class:`collections.Sequence`
        :param bba: Mapping from event to basic belief assignment (useful for constructing belief functions).
        :type bba: :class:`collections.Mapping`
        :param credalset: Sequence of probability mass functions.
        :type credalset: :class:`collections.Sequence`
        :param number_type: The number type. If not specified, it is
            determined using
            :func:`~cdd.get_number_type_from_sequences` on all
            values.
        :type number_type: :class:`str`

        Generally, you can pass a :class:`dict` as a keyword argument
        in order to initialize the lower and upper previsions and/or
        probabilities:

        >>> print(LowPoly(pspace=3, mapping={
        ...     ((3, 1, 2), True): (1.5, None),
        ...     ((1, 0, -1), (1, 2)): (0.25, 0.3)})) # doctest: +NORMALIZE_WHITESPACE
         0    1   2
        3.0  1.0 2.0  | 0 1 2 : [1.5 ,     ]
        1.0  0.0 -1.0 |   1 2 : [0.25, 0.3 ]
        >>> print(LowPoly(pspace=3,
        ...     lprev={(1, 3, 2): 1.5, (2, 0, -1): 1},
        ...     uprev={(2, 0, -1): 1.9},
        ...     prev={(9, 8, 20): 15},
        ...     lprob={(1, 2): 0.2, (1,): 0.1},
        ...     uprob={(1, 2): 0.3, (0,): 0.9},
        ...     prob={(2,): '0.3'})) # doctest: +NORMALIZE_WHITESPACE
          0    1   2
         0.0  0.0 1.0  | 0 1 2 : [0.3 , 0.3 ]
         0.0  1.0 0.0  | 0 1 2 : [0.1 ,     ]
         0.0  1.0 1.0  | 0 1 2 : [0.2 , 0.3 ]
         1.0  0.0 0.0  | 0 1 2 : [    , 0.9 ]
         1.0  3.0 2.0  | 0 1 2 : [1.5 ,     ]
         2.0  0.0 -1.0 | 0 1 2 : [1.0 , 1.9 ]
         9.0  8.0 20.0 | 0 1 2 : [15.0, 15.0]

        A credal set can be specified simply as a list:

        >>> print(LowPoly(pspace=3,
        ...     credalset=[['0.1', '0.45', '0.45'],
        ...                ['0.4', '0.3', '0.3'],
        ...                ['0.3', '0.2', '0.5']]))
          0     1     2  
        -10   10    0     | 0 1 2 : [-1,   ]
        -1    -2    0     | 0 1 2 : [-1,   ]
        1     1     1     | 0 1 2 : [1 , 1 ]
        50/23 40/23 0     | 0 1 2 : [1 ,   ]

        As a special case, for lower/upper/precise probabilities, if
        you need to set values on singletons, you can use a list
        instead of a dictionary:

        >>> print(LowPoly(pspace='abc', lprob=['0.1', '0.2', '0.3'])) # doctest: +NORMALIZE_WHITESPACE
        a b c
        0 0 1 | a b c : [3/10, ]
        0 1 0 | a b c : [1/5 , ]
        1 0 0 | a b c : [1/10, ]

        If the first argument is a :class:`LowPoly` instance, then it
        is copied. For example:

        >>> from improb.lowprev.lowprob import LowProb
        >>> lpr = LowPoly(pspace='abc', lprob=['0.1', '0.1', '0.1'])
        >>> print(lpr)
        a b c
        0 0 1 | a b c : [1/10,     ]
        0 1 0 | a b c : [1/10,     ]
        1 0 0 | a b c : [1/10,     ]
        >>> lprob = LowProb(lpr)
        >>> print(lprob)
        a     : 1/10
          b   : 1/10
            c : 1/10
        """
        def iter_items(obj):
            """Return an iterator over all items of the mapping or the
            sequence.
            """
            if isinstance(obj, collections.Mapping):
                return obj.iteritems()
            elif isinstance(obj, collections.Sequence):
                if len(obj) < len(self.pspace):
                    raise ValueError('sequence too short')
                return (((omega, ), value)
                        for omega, value in itertools.izip(self.pspace, obj))
            else:
                raise TypeError(
                    'expected collections.Mapping or collections.Sequence')

        def get_number_type(xprevs, xprobs):
            """Determine number type from arguments."""
            # special case: nothing specified, defaults to float
            if (all(xprev is None for xprev in xprevs)
                    and all(xprob is None for xprob in xprobs)):
                return 'float'
            # inspect all values
            for xprev in xprevs:
                if xprev is None:
                    continue
                for key, value in xprev.iteritems():
                    # inspect gamble
                    if isinstance(key, Gamble):
                        if key.number_type == 'float':
                            return 'float'
                    elif isinstance(key, collections.Sequence):
                        if cdd.get_number_type_from_sequences(key) == 'float':
                            return 'float'
                    elif isinstance(key, collections.Mapping):
                        if cdd.get_number_type_from_sequences(
                                key.itervalues()) == 'float':
                            return 'float'
                    # inspect value(s)
                    if isinstance(value, collections.Sequence):
                        if cdd.get_number_type_from_sequences(
                                value) == 'float':
                            return 'float'
                    else:
                        if cdd.get_number_type_from_value(value) == 'float':
                            return 'float'
            for xprob in xprobs:
                if xprob is None:
                    continue
                for key, value in iter_items(xprob):
                    if cdd.get_number_type_from_value(value) == 'float':
                        return 'float'
            # everything is fraction
            return 'fraction'

        # if first argument is a LowPoly, then override all other arguments
        if isinstance(pspace, LowPoly):
            mapping = dict(pspace.iteritems())
            number_type = pspace.number_type
            pspace = pspace.pspace
        # initialize everything
        self._pspace = PSpace.make(pspace)
        if number_type is None:
            number_type = get_number_type([mapping, lprev, uprev, prev, bba],
                                          [lprob, uprob, prob] +
                                          (credalset if credalset else []))
        cdd.NumberTypeable.__init__(self, number_type)
        self._mapping = {}
        if mapping:
            for key, value in mapping.iteritems():
                self[key] = value
        if lprev:
            for gamble, value in lprev.iteritems():
                self.set_lower(gamble, value)
        if uprev:
            for gamble, value in uprev.iteritems():
                self.set_upper(gamble, value)
        if prev:
            for gamble, value in prev.iteritems():
                self.set_precise(gamble, value)
        if lprob:
            for event, value in iter_items(lprob):
                event = self.pspace.make_event(event)
                self.set_lower(event, value)
        if uprob:
            for event, value in iter_items(uprob):
                event = self.pspace.make_event(event)
                self.set_upper(event, value)
        if prob:
            for event, value in iter_items(prob):
                event = self.pspace.make_event(event)
                self.set_precise(event, value)
        if bba:
            setfunc = SetFunction(pspace=self.pspace,
                                  data=bba,
                                  number_type=self.number_type)
            for event in self.pspace.subsets():
                self.set_lower(event, setfunc.get_zeta(event))
        if credalset:
            # set up polyhedral representation
            mat = cdd.Matrix([(['1'] + credalprob)
                              for credalprob in credalset])
            mat.rep_type = cdd.RepType.GENERATOR
            poly = cdd.Polyhedron(mat)
            dualmat = poly.get_inequalities()
            #print(mat)
            #print(dualmat)
            for rownum, row in enumerate(dualmat):
                if rownum in dualmat.lin_set:
                    self.set_precise(row[1:], -row[0])
                else:
                    self.set_lower(row[1:], -row[0])
Example #17
0
    def get_constraints_n_monotone(cls, pspace, monotonicity=None):
        """Yields constraints for lower probabilities with given
        monotonicity.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param monotonicity: Requested level of monotonicity (see
            notes below for details).
        :type monotonicity: :class:`int` or
            :class:`collections.Iterable` of :class:`int`

        As described in
        :meth:`~improb.setfunction.SetFunction.get_constraints_bba_n_monotone`,
        the n-monotonicity constraints on basic belief assignment are:

        .. math::

            \sum_{B\colon C\subseteq B\subseteq A}m(B)\ge 0

        for all :math:`C\subseteq A\subseteq\Omega`, with
        :math:`1\le|C|\le n`.

        By the Mobius transform, this is equivalent to:

        .. math::

            \sum_{B\colon C\subseteq B\subseteq A}
            \sum_{D\colon D\subseteq B}(-1)^{|B\setminus D|}
            \underline{P}(D)\ge 0

        Once noted that

        .. math::

            (C\subseteq B\subseteq A\quad \& \quad D\subseteq B)
            \iff
            (C\cup D\subseteq B\subseteq A\quad \& \quad D\subseteq A),

        we can conveniently rewrite the sum as:

        .. math::

            \sum_{D\colon D\subseteq A}
            \sum_{B\colon C\cup D\subseteq B\subseteq A}(-1)^{|B\setminus D|}
            \underline{P}(D)\ge 0

        This implementation iterates over all :math:`C\subseteq
        A\subseteq\Omega`, with :math:`|C|=n`, and yields each
        constraint as an iterable of (event, coefficient) pairs, where
        zero coefficients are omitted.

        .. note::

            As just mentioned, this method returns the constraints
            corresponding to the latter equation for :math:`|C|`
            equal to *monotonicity*. To get all the
            constraints for n-monotonicity, call this method with
            *monotonicity=xrange(1, n + 1)*.

            The rationale for this approach is that, in case you
            already know that (n-1)-monotonicity is satisfied, then
            you only need the constraints for *monotonicity=n* to
            check for n-monotonicity.

        .. note::

            The trivial constraints that the empty set must have lower
            probability zero, and that the possibility space must have
            lower probability one, are not included: so for
            *monotonicity=0* this method returns an empty iterator.

        >>> pspace = PSpace("abc")
        >>> for mono in xrange(1, len(pspace) + 1):
        ...     print("{0} monotonicity:".format(mono))
        ...     print(" ".join("{0:<{1}}".format("".join(i for i in event), len(pspace))
        ...                    for event in pspace.subsets()))
        ...     constraints = [
        ...         dict(constraint) for constraint in
        ...         LowProb.get_constraints_n_monotone(pspace, mono)]
        ...     constraints = [
        ...         [constraint.get(event, 0) for event in pspace.subsets()]
        ...         for constraint in constraints]
        ...     for constraint in sorted(constraints):
        ...         print(" ".join("{0:<{1}}".format(value, len(pspace))
        ...                        for value in constraint))
        1 monotonicity:
            a   b   c   ab  ac  bc  abc
        -1  0   0   1   0   0   0   0  
        -1  0   1   0   0   0   0   0  
        -1  1   0   0   0   0   0   0  
        0   -1  0   0   0   1   0   0  
        0   -1  0   0   1   0   0   0  
        0   0   -1  0   0   0   1   0  
        0   0   -1  0   1   0   0   0  
        0   0   0   -1  0   0   1   0  
        0   0   0   -1  0   1   0   0  
        0   0   0   0   -1  0   0   1  
        0   0   0   0   0   -1  0   1  
        0   0   0   0   0   0   -1  1  
        2 monotonicity:
            a   b   c   ab  ac  bc  abc
        0   0   0   1   0   -1  -1  1  
        0   0   1   0   -1  0   -1  1  
        0   1   0   0   -1  -1  0   1  
        1   -1  -1  0   1   0   0   0  
        1   -1  0   -1  0   1   0   0  
        1   0   -1  -1  0   0   1   0  
        3 monotonicity:
            a   b   c   ab  ac  bc  abc
        -1  1   1   1   -1  -1  -1  1  
        """
        pspace = PSpace.make(pspace)
        # check type
        if monotonicity is None:
            raise ValueError("specify monotonicity")
        elif isinstance(monotonicity, collections.Iterable):
            # special case: return it for all values in the iterable
            for mono in monotonicity:
                for constraint in cls.get_constraints_n_monotone(pspace, mono):
                    yield constraint
            return
        elif not isinstance(monotonicity, (int, long)):
            raise TypeError("monotonicity must be integer")
        # check value
        if monotonicity < 0:
            raise ValueError("specify a non-negative monotonicity")
        if monotonicity == 0:
            # don't return constraints in this case
            return
        # yield all constraints
        for event_a in pspace.subsets(size=xrange(monotonicity,
                                                  len(pspace) + 1)):
            for subevent_c in pspace.subsets(event_a, size=monotonicity):
                yield ((subevent_d,
                        sum((-1)**len(event_b - subevent_d)
                            for event_b in pspace.subsets(
                                event_a, contains=(subevent_d | subevent_c))))
                       for subevent_d in pspace.subsets(event_a))
Example #18
0
    def get_constraints_bba_n_monotone(cls, pspace, monotonicity=None):
        """Yields constraints for basic belief assignments with given
        monotonicity.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param monotonicity: Requested level of monotonicity (see
            notes below for details).
        :type monotonicity: :class:`int` or
            :class:`collections.Iterable` of :class:`int`

        This follows the algorithm described in Proposition 2 (for
        1-monotonicity) and Proposition 4 (for n-monotonicity) of
        *Chateauneuf and Jaffray, 1989. Some characterizations of
        lower probabilities and other monotone capacities through the
        use of Mobius inversion. Mathematical Social Sciences 17(3),
        pages 263-283*:

        A set function :math:`s` defined on the power set of
        :math:`\Omega` is :math:`n`-monotone if and only if its Mobius
        transform :math:`m` satisfies:

        .. math::

            m(\emptyset)=0, \qquad\sum_{A\subseteq\Omega} m(A)=1,

        and

        .. math::

            \sum_{B\colon C\subseteq B\subseteq A} m(B)\ge 0

        for all :math:`C\subseteq A\subseteq\Omega`, with
        :math:`1\le|C|\le n`.

        This implementation iterates over all :math:`C\subseteq
        A\subseteq\Omega`, with :math:`|C|=n`, and yields each
        constraint as an iterable of the events :math:`\{B\colon
        C\subseteq B\subseteq A\}`. For example, you can then check
        the constraint by summing over this iterable.

        .. note::

            As just mentioned, this method returns the constraints
            corresponding to the latter equation for :math:`|C|`
            equal to *monotonicity*. To get all the
            constraints for n-monotonicity, call this method with
            *monotonicity=xrange(1, n + 1)*.

            The rationale for this approach is that, in case you
            already know that (n-1)-monotonicity is satisfied, then
            you only need the constraints for *monotonicity=n* to
            check for n-monotonicity.

        .. note::

            The trivial constraints that the empty set must have mass
            zero, and that the masses must sum to one, are not
            included: so for *monotonicity=0* this method returns an
            empty iterator.

        >>> pspace = "abc"
        >>> for mono in xrange(1, len(pspace) + 1):
        ...     print("{0} monotonicity:".format(mono))
        ...     print(" ".join("{0:<{1}}".format("".join(i for i in event), len(pspace))
        ...                    for event in PSpace(pspace).subsets()))
        ...     constraints = SetFunction.get_constraints_bba_n_monotone(pspace, mono)
        ...     constraints = [set(constraint) for constraint in constraints]
        ...     constraints = [[1 if event in constraint else 0
        ...                     for event in PSpace(pspace).subsets()]
        ...                    for constraint in constraints]
        ...     for constraint in sorted(constraints):
        ...         print(" ".join("{0:<{1}}"
        ...                        .format(value, len(pspace))
        ...                        for value in constraint))
        1 monotonicity:
            a   b   c   ab  ac  bc  abc
        0   0   0   1   0   0   0   0  
        0   0   0   1   0   0   1   0  
        0   0   0   1   0   1   0   0  
        0   0   0   1   0   1   1   1  
        0   0   1   0   0   0   0   0  
        0   0   1   0   0   0   1   0  
        0   0   1   0   1   0   0   0  
        0   0   1   0   1   0   1   1  
        0   1   0   0   0   0   0   0  
        0   1   0   0   0   1   0   0  
        0   1   0   0   1   0   0   0  
        0   1   0   0   1   1   0   1  
        2 monotonicity:
            a   b   c   ab  ac  bc  abc
        0   0   0   0   0   0   1   0  
        0   0   0   0   0   0   1   1  
        0   0   0   0   0   1   0   0  
        0   0   0   0   0   1   0   1  
        0   0   0   0   1   0   0   0  
        0   0   0   0   1   0   0   1  
        3 monotonicity:
            a   b   c   ab  ac  bc  abc
        0   0   0   0   0   0   0   1  
        """
        pspace = PSpace.make(pspace)
        # check type
        if monotonicity is None:
            raise ValueError("specify monotonicity")
        elif isinstance(monotonicity, collections.Iterable):
            # special case: return it for all values in the iterable
            for mono in monotonicity:
                for constraint in cls.get_constraints_bba_n_monotone(
                        pspace, mono):
                    yield constraint
            return
        elif not isinstance(monotonicity, (int, long)):
            raise TypeError("monotonicity must be integer")
        # check value
        if monotonicity < 0:
            raise ValueError("specify a non-negative monotonicity")
        if monotonicity == 0:
            # don't return constraints in this case
            return
        # yield all constraints
        for event in pspace.subsets(size=xrange(monotonicity,
                                                len(pspace) + 1)):
            for subevent in pspace.subsets(event, size=monotonicity):
                yield pspace.subsets(event, contains=subevent)
Example #19
0
 def __init__(self, pspace, number_type=None):
     if number_type is None:
         number_type = 'float'
     cdd.NumberTypeable.__init__(self, number_type)
     self._pspace = PSpace.make(pspace)
Example #20
0
    def get_constraints_bba_n_monotone(cls, pspace, monotonicity=None):
        """Yields constraints for basic belief assignments with given
        monotonicity.

        :param pspace: The possibility space.
        :type pspace: |pspacetype|
        :param monotonicity: Requested level of monotonicity (see
            notes below for details).
        :type monotonicity: :class:`int` or
            :class:`collections.Iterable` of :class:`int`

        This follows the algorithm described in Proposition 2 (for
        1-monotonicity) and Proposition 4 (for n-monotonicity) of
        *Chateauneuf and Jaffray, 1989. Some characterizations of
        lower probabilities and other monotone capacities through the
        use of Mobius inversion. Mathematical Social Sciences 17(3),
        pages 263-283*:

        A set function :math:`s` defined on the power set of
        :math:`\Omega` is :math:`n`-monotone if and only if its Mobius
        transform :math:`m` satisfies:

        .. math::

            m(\emptyset)=0, \qquad\sum_{A\subseteq\Omega} m(A)=1,

        and

        .. math::

            \sum_{B\colon C\subseteq B\subseteq A} m(B)\ge 0

        for all :math:`C\subseteq A\subseteq\Omega`, with
        :math:`1\le|C|\le n`.

        This implementation iterates over all :math:`C\subseteq
        A\subseteq\Omega`, with :math:`|C|=n`, and yields each
        constraint as an iterable of the events :math:`\{B\colon
        C\subseteq B\subseteq A\}`. For example, you can then check
        the constraint by summing over this iterable.

        .. note::

            As just mentioned, this method returns the constraints
            corresponding to the latter equation for :math:`|C|`
            equal to *monotonicity*. To get all the
            constraints for n-monotonicity, call this method with
            *monotonicity=xrange(1, n + 1)*.

            The rationale for this approach is that, in case you
            already know that (n-1)-monotonicity is satisfied, then
            you only need the constraints for *monotonicity=n* to
            check for n-monotonicity.

        .. note::

            The trivial constraints that the empty set must have mass
            zero, and that the masses must sum to one, are not
            included: so for *monotonicity=0* this method returns an
            empty iterator.

        >>> pspace = "abc"
        >>> for mono in xrange(1, len(pspace) + 1):
        ...     print("{0} monotonicity:".format(mono))
        ...     print(" ".join("{0:<{1}}".format("".join(i for i in event), len(pspace))
        ...                    for event in PSpace(pspace).subsets()))
        ...     constraints = SetFunction.get_constraints_bba_n_monotone(pspace, mono)
        ...     constraints = [set(constraint) for constraint in constraints]
        ...     constraints = [[1 if event in constraint else 0
        ...                     for event in PSpace(pspace).subsets()]
        ...                    for constraint in constraints]
        ...     for constraint in sorted(constraints):
        ...         print(" ".join("{0:<{1}}"
        ...                        .format(value, len(pspace))
        ...                        for value in constraint))
        1 monotonicity:
            a   b   c   ab  ac  bc  abc
        0   0   0   1   0   0   0   0  
        0   0   0   1   0   0   1   0  
        0   0   0   1   0   1   0   0  
        0   0   0   1   0   1   1   1  
        0   0   1   0   0   0   0   0  
        0   0   1   0   0   0   1   0  
        0   0   1   0   1   0   0   0  
        0   0   1   0   1   0   1   1  
        0   1   0   0   0   0   0   0  
        0   1   0   0   0   1   0   0  
        0   1   0   0   1   0   0   0  
        0   1   0   0   1   1   0   1  
        2 monotonicity:
            a   b   c   ab  ac  bc  abc
        0   0   0   0   0   0   1   0  
        0   0   0   0   0   0   1   1  
        0   0   0   0   0   1   0   0  
        0   0   0   0   0   1   0   1  
        0   0   0   0   1   0   0   0  
        0   0   0   0   1   0   0   1  
        3 monotonicity:
            a   b   c   ab  ac  bc  abc
        0   0   0   0   0   0   0   1  
        """
        pspace = PSpace.make(pspace)
        # check type
        if monotonicity is None:
            raise ValueError("specify monotonicity")
        elif isinstance(monotonicity, collections.Iterable):
            # special case: return it for all values in the iterable
            for mono in monotonicity:
                for constraint in cls.get_constraints_bba_n_monotone(pspace, mono):
                    yield constraint
            return
        elif not isinstance(monotonicity, (int, long)):
            raise TypeError("monotonicity must be integer")
        # check value
        if monotonicity < 0:
            raise ValueError("specify a non-negative monotonicity")
        if monotonicity == 0:
            # don't return constraints in this case
            return
        # yield all constraints
        for event in pspace.subsets(size=xrange(monotonicity, len(pspace) + 1)):
            for subevent in pspace.subsets(event, size=monotonicity):
                yield pspace.subsets(event, contains=subevent)