from collections import Mapping
from murasyp.gambles import Gamble, Ray, Cone
import murasyp.credalsets
import murasyp.mathprog
[docs]class DesirSet(set):
"""A set of cones
:type `data`: a non-:class:`~collections.Mapping`
:class:`~collections.Iterable` :class:`~collections.Container` of
arguments accepted by the :class:`~murasyp.gambles.Cone` constructor.
>>> assert (
... DesirSet('abc') ==
... DesirSet({Cone({Ray({'b': 1})}),
... Cone({Ray({'c': 1})}), Cone({Ray({'a': 1})})})
... )
>>> assert (
... DesirSet(['abc']) ==
... DesirSet({Cone({Ray({'a': 1}), Ray({'b': 1}), Ray({'c': 1})})})
... )
>>> assert (
... DesirSet([['abc']]) ==
... DesirSet({Cone({Ray({'a': 1, 'c': 1, 'b': 1})})})
... )
This class derives from :class:`~set`, so its methods apply here as
well.
.. todo::
test all set methods and fix, or elegantly deal with, broken ones
Additional and changed methods:
* Lower and upper (conditional) expectations can be calculated, using the
``*`` and ``**`` operators, respectively.
>>> D = DesirSet([[Gamble({'a': -1, 'c': '7/90'}),
... Gamble({'a': 1, 'c': '-1/30'}),
... Gamble({'a': -1, 'c': '1/9', 'b': -1}),
... Gamble({'a': 1, 'c': '-1/9', 'b': 1})]])
>>> f = Gamble({'a': -1, 'b': 1, 'c': 0})
>>> D * f
Fraction(-1, 25)
>>> D ** f
Fraction(1, 25)
>>> D * (f | f.support())
Fraction(-2, 5)
>>> D ** (f | f.support())
Fraction(2, 5)
.. note::
The domain of the gamble determines the conditioning event.
We can deal with situations in which the gamble lies on a facet of the set
of desirable gambles that corresponds to a conditioning event of (lower
and/or upper) probability zero.
>>> D = DesirSet()
>>> D.set_pr('a', 1)
>>> D * (Gamble('c') | {'b', 'c'})
0
>>> D * (Gamble('c') | {'a', 'b', 'c'})
0
>>> D.set_pr(Gamble('b') | {'b', 'c'}, '1/2')
>>> D * (Gamble('c') | {'b', 'c'})
Fraction(1, 2)
"""
def __init__(self, data=[]):
"""Initialize a set of desirable gambles"""
if isinstance(data, Mapping):
raise TypeError(type(self) + " does not accept a mapping,"
+ " but you passed it " + str(data))
else:
set.__init__(self, (Cone(element) for element in data))
[docs] def add(self, data):
"""Add a cone to the set of desirable gambles
:type `data`: arguments accepted by the :class:`~murasyp.gambles.Cone`
constructor
>>> D = DesirSet()
>>> assert D == DesirSet()
>>> D.add([Gamble({'a': -.06, 'b': .14, 'c': 1.8, 'd': 0})])
>>> assert (
... D ==
... DesirSet({Cone({Ray({'a': '-1/30', 'c': 1, 'b': '7/90'})})})
... )
.. todo::
see whether all set functionality is carried over
"""
set.add(self, Cone(data))
[docs] def discard(self, data):
"""Remove a cone from the set of desirable gambles
:type `data`: arguments accepted by the :class:`~murasyp.gambles.Cone`
constructor
>>> D = DesirSet({'a','b'})
>>> assert (
... D == DesirSet({Cone({Ray({'b': 1})}), Cone({Ray({'a': 1})})})
... )
>>> D.discard([Ray({'a'})])
>>> assert D == DesirSet({Cone({Ray({'b': 1})})})
.. todo::
see whether all set functionality is carried over
"""
set.discard(self, frozenset(Ray(element) for element in data))
[docs] def pspace(self):
"""The possibility space of the set of desirable gambles
:returns: the possibility space of the set of desirable gambles, i.e.,
the union of the domains of the cones it contains
:rtype: :class:`frozenset`
>>> D = DesirSet(['abc'])
>>> r = Ray({'c': .03, 'd': -.07})
>>> s = Ray({'a': .07, 'e': -.03})
>>> D.add([r, s])
>>> assert D.pspace() == frozenset({'a', 'c', 'b', 'e', 'd'})
"""
return frozenset.union(*(cone.domain() for cone in self))
[docs] def set_lower_pr(self, data, val):
"""Set the lower probability/prevision (expectation) of an event/gamble
:arg `data`: the gamble for which a probability/prevision value is given
:type `data`: arguments accepted by the :class:`~murasyp.gambles.Gamble`
constructor
:arg `val`: the probability/prevision value
:type `val`: a representation of :class:`~numbers.Real`
The nontrivial cone corresponing to the prevision specification is
calculated and added to the set of desirable gambles.
>>> D = DesirSet()
>>> D.set_lower_pr(Gamble({'a', 'b'}) | {'a', 'b', 'c'}, .4)
>>> assert (
... D ==
... DesirSet({Cone({Ray({'a': 1, 'c': '-2/3', 'b': 1}),
... Ray({'a': 1, 'c': 1, 'b': 1})})})
... )
.. note::
The domain of the input gamble determines the conditioning event.
"""
gamble = Gamble(data)
self.add({gamble - gamble._make_rational(val), Ray(gamble.domain())})
[docs] def set_upper_pr(self, data, val):
"""Set the upper probability/prevision (expectation) of an event/gamble
:arg `data`: the gamble for which a probability/prevision value is given
:type `data`: arguments accepted by the :class:`~murasyp.gambles.Gamble`
constructor
:arg `val`: the probability/prevision value
:type `val`: a representation of :class:`~numbers.Real`
The nontrivial cone corresponing to the prevision specification is
calculated and added to the set of desirable gambles.
>>> D = DesirSet()
>>> D.set_upper_pr(Gamble({'a', 'b'}) | {'a', 'b', 'c'}, .4)
>>> assert (
... D ==
... DesirSet({Cone({Ray({'a': -1, 'c': '2/3', 'b': -1}),
... Ray({'a': 1, 'c': 1, 'b': 1})})})
... )
.. note::
The domain of the input gamble determines the conditioning event.
"""
gamble = Gamble(data)
self.set_lower_pr(-gamble, -gamble._make_rational(val))
[docs] def set_pr(self, data, val):
"""Set the probability/prevision (expectation) of an event/gamble
:arg `data`: the gamble for which a probability/prevision value is given
:type `data`: arguments accepted by the :class:`~murasyp.gambles.Gamble`
constructor
:arg `val`: the probability/prevision value
:type `val`: a representation of :class:`~numbers.Real`
This is identical to setting the lower and upper prevision to the same
value.
>>> D = DesirSet()
>>> D.set_pr(Gamble({'a', 'b'}) | {'a', 'b', 'c'}, .4)
>>> assert (
... D ==
... DesirSet({Cone({Ray({'a': -1, 'c': '2/3', 'b': -1}),
... Ray({'a': 1, 'c': 1, 'b': 1})}),
... Cone({Ray({'a': 1, 'c': '-2/3', 'b': 1}),
... Ray({'a': 1, 'c': 1, 'b': 1})})})
... )
.. note::
The domain of the input gamble determines the conditioning event.
"""
self.set_lower_pr(data, val)
self.set_upper_pr(data, val)
[docs] def asl(self):
"""Check whether the set of desirable gambles avoids sure loss
:rtype: :class:`bool`
A set of desirable gambles does not avoid sure loss if and only if some
nonnegative linear combination of desirable gambles is everywhere
negative.
>>> D = DesirSet()
>>> D.add([{'a': -1, 'b': -1, 'c': 1}])
>>> D.add([{'a': 1, 'b': -1, 'c': -1}])
>>> D.asl()
True
>>> D.add([{'a': -1, 'b': 1, 'c': -1}])
>>> D.asl()
False
>>> D = DesirSet('ab')
>>> D.add([{'b': -1}])
>>> D.asl()
True
"""
D = DesirSet([Cone.union(*(self | DesirSet([self.pspace()])))])
return murasyp.mathprog.feasible(D) == set()
[docs] def apl(self):
"""Check whether the set of desirable gambles avoids partial loss
:rtype: :class:`bool`
A set of desirable gambles does not avoid partial loss if and only if
some nonnegative linear combination of desirable gambles is everywhere
nonpositive and somewhere negative.
>>> D = DesirSet()
>>> D.add([{'a': -1, 'b': -1, 'c': 1}])
>>> D.apl()
True
>>> D.add([{'a': -1, 'b': 1, 'c': -1}])
>>> D.apl()
False
We can deal correctly with non-closed sets of desirable gambles, i.e.,
containing non-singleton cones:
>>> D = DesirSet()
>>> D.set_pr(Gamble('b') | {'a', 'b'}, 0)
>>> assert (
... D ==
... DesirSet({Cone({Ray({'a': 1, 'b': 1}), Ray({'b': 1})}),
... Cone({Ray({'a': 1, 'b': 1}), Ray({'b': -1})})})
... )
>>> D.apl()
True
"""
D = self | DesirSet(self.pspace())
return murasyp.mathprog.feasible(D) == set()
def __mul__(self, other):
"""Lower expectation of a gamble"""
gamble = Gamble(other)
indicator = Gamble(gamble.domain())
return murasyp.mathprog.maximize(
self | DesirSet(self.pspace() | gamble.domain()
| indicator.domain())
| DesirSet([{indicator}, {-indicator}, {()}]),
gamble, (0, {indicator: 1, -indicator: -1}))
def __pow__(self, other):
"""Upper expectation of a gamble"""
return - self.__mul__(- other)
[docs] def get_credal(self):
"""Generate the corresponding (closed) credal set
:returns: the (closed) credal set that corresponds as an uncertainty
model
:rtype: :class:`~murasyp.credalsets.CredalSet`
>>> D = DesirSet(['abc'])
>>> D.set_lower_pr({'a': 1, 'b': 0, 'c': 1}, .5)
>>> assert (
... D.get_credal() ==
... CredalSet({PMFunc({'a': '1/2', 'b': '1/2'}),
... PMFunc({'c': '1/2', 'b': '1/2'}),
... PMFunc({'a': 1}), PMFunc({'c': 1})})
... )
"""
C = Cone.union(*self)
return murasyp.credalsets.CredalSet(murasyp.mathprog.vf_enumeration(C))