# Polyhedral Lower Previsions¶

class improb.lowprev.lowpoly.LowPoly(pspace=None, mapping=None, lprev=None, uprev=None, prev=None, lprob=None, uprob=None, prob=None, bba=None, credalset=None, number_type=None)

Bases: improb.lowprev.LowPrev

An arbitrary finitely generated lower prevision, that is, a finite intersection of half-spaces, each of which constrains the set of probability mass functions.

This class is derived from collections.MutableMapping: keys are (gamble, event) pairs, values are (lprev, uprev) pairs:

```>>> lpr = LowPoly(pspace='abcd', number_type='fraction')
>>> lpr[{'a': 2, 'b': 3}, 'abcd'] = ('1.5', '1.9')
>>> lpr[{'c': 1, 'd': 8}, 'cd'] = ('1.2', None)
>>> print(lpr)
a b c d
2 3 0 0 | a b c d : [3/2  , 19/10]
0 0 1 8 |     c d : [6/5  ,      ]
```

Instead of working on the mapping directly, you can use the convenience methods set_lower(), set_upper(), and set_precise():

```>>> lpr = LowPoly(pspace='abcd', number_type='fraction')
>>> lpr.set_lower({'a': 2, 'b': 3}, '1.5')
>>> lpr.set_upper({'a': 2, 'b': 3}, '1.9')
>>> lpr.set_lower({'c': 1, 'd': 8}, '1.2', event='cd')
>>> print(lpr)
a b c d
2 3 0 0 | a b c d : [3/2  , 19/10]
0 0 1 8 |     c d : [6/5  ,      ]
```
__init__(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.

Parameters: pspace (list or similar; see Possibility Spaces) – The possibility space. mapping (collections.Mapping) – Mapping from (gamble, event) to (lower prevision, upper prevision). lprev (collections.Mapping) – Mapping from gamble to lower prevision. uprev (collections.Mapping) – Mapping from gamble to upper prevision. prev (collections.Mapping) – Mapping from gamble to precise prevision. lprob (collections.Mapping or collections.Sequence) – Mapping from event to lower probability. uprob (collections.Mapping or collections.Sequence) – Mapping from event to upper probability. prob (collections.Mapping or collections.Sequence) – Mapping from event to precise probability. bba (collections.Mapping) – Mapping from event to basic belief assignment (useful for constructing belief functions). credalset (collections.Sequence) – Sequence of probability mass functions. number_type (str) – The number type. If not specified, it is determined using get_number_type_from_sequences() on all values.

Generally, you can pass a 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)}))
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'}))
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']))
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 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
```
extend(keys=None, lower=True, upper=True, algorithm='linprog')

Calculate coherent extension to the given keys (gamble/event pairs), using linear programming.

Parameters: keys (collections.Iterable) – The gamble/event pairs to extend. If None, then extends to the full domain, given by get_extend_domain() (for LowPoly this raises a ValueError, however some derived classes implement this if they have a finite domain). lower (bool) – Whether to extend the lower bounds. upper (bool) – Whether to extend the upper bounds.
```>>> pspace = PSpace('xyz')
>>> lpr = LowPoly(pspace=pspace, lprob=['0.1', '0.2', '0.15'], number_type='fraction')
>>> print(lpr)
x y z
0 0 1 | x y z : [3/20,     ]
0 1 0 | x y z : [1/5 ,     ]
1 0 0 | x y z : [1/10,     ]
>>> for event in pspace.subsets(empty=False):
...     lpr.extend((subevent, event) for subevent in pspace.subsets(event))
>>> print(lpr)
x y z
0 0 0 | x y z : [0    , 0    ]
0 0 1 | x y z : [3/20 , 7/10 ]
0 1 0 | x y z : [1/5  , 3/4  ]
0 1 1 | x y z : [7/20 , 9/10 ]
1 0 0 | x y z : [1/10 , 13/20]
1 0 1 | x y z : [1/4  , 4/5  ]
1 1 0 | x y z : [3/10 , 17/20]
1 1 1 | x y z : [1    , 1    ]
0 0 0 | x y   : [0    , 0    ]
0 1 0 | x y   : [4/17 , 15/17]
1 0 0 | x y   : [2/17 , 13/17]
1 1 0 | x y   : [1    , 1    ]
0 0 0 | x   z : [0    , 0    ]
0 0 1 | x   z : [3/16 , 7/8  ]
1 0 0 | x   z : [1/8  , 13/16]
1 0 1 | x   z : [1    , 1    ]
0 0 0 | x     : [0    , 0    ]
1 0 0 | x     : [1    , 1    ]
0 0 0 |   y z : [0    , 0    ]
0 0 1 |   y z : [1/6  , 7/9  ]
0 1 0 |   y z : [2/9  , 5/6  ]
0 1 1 |   y z : [1    , 1    ]
0 0 0 |   y   : [0    , 0    ]
0 1 0 |   y   : [1    , 1    ]
0 0 0 |     z : [0    , 0    ]
0 0 1 |     z : [1    , 1    ]
```
get_coherent(algorithm='linprog')

Return a coherent version, using linear programming.

get_credal_set(event=True)

Return extreme points of the credal set conditional on event.

Returns: The extreme points. Yields a tuple for each extreme point.
get_lower(gamble, event=True, algorithm='linprog')

Calculate lower expectation, using Algorithm 4 of Walley, Pelessoni, and Vicig (2004) [2]. The algorithm deals properly with zero probabilities.

get_matrix(gamble=None, event=True)

Matrix representing the constraints of this lower prevision conditional on the given event.

get_relevant_items(event=True)

Calculate the relevant items for calculating the natural extension conditional on an event.

This is part (a) (b) (c) of Algorithm 4 of Walley, Pelessoni, and Vicig (2004) [2]. Also see their Algorithm 2, which is a special case of Algorithm 4 but with event equal to the empty set.

is_avoiding_sure_loss(algorithm='linprog')

Check avoiding sure loss by linear programming.

This is Algorithm 2 of Walley, Pelessoni, and Vicig (2004) [2].

set_lower(gamble, lprev, event=True)

Constrain the expectation of gamble to be at least lprev.

Parameters: gamble (dict or similar; see Gambles) – The gamble whose expectation to bound. lprev (float or similar; see Values) – The lower bound for this expectation.
set_precise(gamble, prev, event=True)

Constrain the expectation of gamble to be exactly prev.

Parameters: gamble (dict or similar; see Gambles) – The gamble whose expectation to bound. prev (float or similar; see Values) – The precise bound for this expectation.
set_upper(gamble, uprev, event=True)

Constrain the expectation of gamble to be at most uprev.

Parameters: gamble (dict or similar; see Gambles) – The gamble whose expectation to bound. uprev (float or similar; see Values) – The upper bound for this expectation.

The following examples presume:

```>>> from improb.lowprev.lowpoly import LowPoly
>>> from improb.lowprev.lowprob import LowProb
>>> from improb.lowprev.prob import Prob
>>> from improb.decision.opt import OptLowPrevMax
```

## Natural Extension¶

```>>> lpr = LowPoly(pspace=3, number_type='fraction')
>>> print "%.6f" % lpr.get_lower([1,2,3])
1.000000
>>> print "%.6f" % lpr.get_upper([1,2,3])
3.000000
>>> lpr.set_lower([1,2,3], 1.5)
>>> lpr.set_upper([1,2,3], 2.5)
>>> print(lpr.get_matrix())
H-representation
linearity 1  1
begin
6 4 rational
1 -1 -1 -1
0 1 0 0
0 0 1 0
0 0 0 1
0 -1/2 1/2 3/2
0 3/2 1/2 -1/2
end
minimize
0 0 0 0
>>> print(lpr.get_lower([1,2,3]))
3/2
>>> print(lpr.get_upper([1,2,3]))
5/2
>>> print('\n'.join(' '.join(str(x) for x in prob) for prob in sorted(lpr.get_credal_set())))
0 1/2 1/2
0 1 0
1/4 0 3/4
1/2 1/2 0
3/4 0 1/4
```

Another example:

```>>> lpr = LowPoly(pspace=4, number_type='fraction')
>>> lpr.set_lower([4,2,1,0], 3)
>>> lpr.set_upper([4,1,2,0], 3)
>>> lpr.is_avoiding_sure_loss()
True
>>> lpr.is_coherent()
True
>>> lpr.is_linear()
False
>>> print(lpr.get_lower([1,0,0,0]))
1/2
>>> print(lpr.get_upper([1,0,0,0]))
3/4
>>> print(lpr)
0 1 2 3
4 1 2 0 | 0 1 2 3 : [ , 3]
4 2 1 0 | 0 1 2 3 : [3,  ]
>>> opt = OptLowPrevMax(lpr)
>>> list(opt([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]]))
[[1, 0, 0, 0], [0, 1, 0, 0]]
>>> list(opt([[0,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]]))
[[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
```

Another example, involving zero probabilities (see Example 9 and 11 in [2]):

```>>> pspace = PSpace(2, 2, 2, 2, 2)
>>> a1 = pspace.make_event((0,), (0, 1), (0, 1), (0, 1), (0, 1))
>>> a2 = pspace.make_event((0, 1), (0,), (0, 1), (0, 1), (0, 1))
>>> a3 = pspace.make_event((0, 1), (0, 1), (0,), (0, 1), (0, 1))
>>> a4 = pspace.make_event((0, 1), (0, 1), (0, 1), (0,), (0, 1))
>>> a5 = pspace.make_event((0, 1), (0, 1), (0, 1), (0, 1), (0,))
>>> lpr = LowPoly(pspace, number_type='fraction')
>>> lpr[a1, True] = ('0.6', '0.6')
>>> lpr[a1.complement() | a2, True] = ('0.4', '0.4')
>>> lpr[a2 | a3, True] = ('0.8', '0.8')
>>> lpr[a3 & a4, True] = ('0.3', '0.3')
>>> lpr[a4.complement() | a5, True] = ('0.5', '0.5')
>>> lpr[a2 | a5, True] = ('0.6', '0.6')
>>> lpr.get_lower(a3)
Fraction(2, 5)
>>> lpr.get_upper(a3)
Fraction(4, 5)
>>> lpr.get_lower(a4, event=a3)
Fraction(3, 8)
>>> lpr.get_upper(a4, event=a3)
Fraction(3, 4)
>>> lpr.get_lower((a1 & a2).complement(), event=(a1 & a2) | (a1.complement() & a2.complement()))
0
```

Another more complex example:

```>>> lpr_s = LowPoly(pspace=3, number_type='fraction')
>>> lpr_s.set_lower([1,0,0], '0.4')
>>> lpr_s.set_upper([1,0,0], '0.5')
>>> lpr_s.set_lower([0,1,0], '0.3')
>>> lpr_s.set_upper([0,1,0], '0.4')
>>> lpr_s.set_lower([0,0,1], '0.2')
>>> lpr_s.set_upper([0,0,1], '0.2')
>>> lpr_s.is_coherent()
True
>>> print(lpr_s.get_lower([1,0,0]))
2/5
>>> print(lpr_s.get_upper([1,0,0]))
1/2
>>> print(lpr_s.get_lower([0,1,0]))
3/10
>>> print(lpr_s.get_upper([0,1,0]))
2/5
>>> print(lpr_s.get_lower([0,0,1]))
1/5
>>> print(lpr_s.get_upper([0,0,1]))
1/5
>>> print(lpr_s.get_lower([-7,5,20]))
2
>>> print(lpr_s.get_upper([-7,5,20]))
16/5
>>> lpr_t_s = [LowPoly(pspace=3, number_type='fraction') for i in xrange(3)]
>>> lpr_t_s[0].set_precise([1,0,0], '0.6')
>>> lpr_t_s[0].set_precise([0,1,0], '0.3')
>>> lpr_t_s[0].set_precise([0,0,1], '0.1')
>>> lpr_t_s[0].is_coherent()
True
>>> lpr_t_s[1].set_precise([1,0,0], '0.3')
>>> lpr_t_s[1].set_precise([0,1,0], '0.4')
>>> lpr_t_s[1].set_precise([0,0,1], '0.3')
>>> lpr_t_s[1].is_coherent()
True
>>> lpr_t_s[2].set_precise([1,0,0], '0.1')
>>> lpr_t_s[2].set_precise([0,1,0], '0.4')
>>> lpr_t_s[2].set_precise([0,0,1], '0.5')
>>> lpr_t_s[2].is_coherent()
True
>>> # a gamble which is a function of s and t; s comes as first
>>> # argument (in order to match lpr_t_s): gamble_t_s[s][t]
>>> gamble_t_s = [[1,0,0], [1,0,0], [1,0,0]] # event t=0
>>> # calculate its lower prevision by marginal extension
>>> print(lpr_s.get_lower([lpr_t.get_lower(gamble_t)
...                        for lpr_t, gamble_t in zip(lpr_t_s, gamble_t_s)]))
19/50
>>> print(lpr_s.get_upper([lpr_t.get_upper(gamble_t)
...                        for lpr_t, gamble_t in zip(lpr_t_s, gamble_t_s)]))
41/100
>>> gamble_t_s = [[0,1,0], [0,1,0], [0,1,0]] # event t=1
>>> print(lpr_s.get_lower([lpr_t.get_lower(gamble_t)
...                        for lpr_t, gamble_t in zip(lpr_t_s, gamble_t_s)]))
7/20
>>> print(lpr_s.get_upper([lpr_t.get_upper(gamble_t)
...                        for lpr_t, gamble_t in zip(lpr_t_s, gamble_t_s)]))
9/25
>>> gamble_t_s = [[0,0,1], [0,0,1], [0,0,1]] # event t=2
>>> print(lpr_s.get_lower([lpr_t.get_lower(gamble_t)
...                        for lpr_t, gamble_t in zip(lpr_t_s, gamble_t_s)]))
6/25
>>> print(lpr_s.get_upper([lpr_t.get_upper(gamble_t)
...                        for lpr_t, gamble_t in zip(lpr_t_s, gamble_t_s)]))
13/50
```

## Mobius Transform¶

```>>> lpr = LowProb(pspace=2)
>>> lpr.set_lower([0,0], 0)
>>> lpr.set_lower([1,0], 0.3)
>>> lpr.set_lower([0,1], 0.2)
>>> lpr.set_lower([1,1], 1)
>>> print(lpr.mobius)
: 0.0
0   : 0.3
1 : 0.2
0 1 : 0.5
```

## Frechet Bounds¶

```>>> lpr = LowPoly(pspace=4, number_type='fraction')
>>> lpr.set_precise([1,1,0,0], '0.6')
>>> lpr.set_precise([0,1,1,0], '0.7')
>>> print(lpr.get_lower([0,1,0,0])) # max(0.6+0.7-1,0)
3/10
>>> print(lpr.get_upper([0,1,0,0])) # min(0.6,0.7)
3/5
>>> lpr.is_linear()
True
>>> for vert in lpr.get_credal_set():
...     print(" ".join("%.2f" % float(x) for x in vert))
0.30 0.30 0.40 0.00
0.00 0.60 0.10 0.30
```

## Avoiding Sure Loss¶

This example incurs sure loss because the maximum of the sum of the gambles [1,2,3,0] and [3,2,1,0] is 4, however 2.5 + 2.5 is strictly larger than 4.

```>>> lpr = LowPoly(pspace=4)
>>> lpr.set_lower([1,2,3,0], 2.5)
>>> lpr.set_lower([3,2,1,0], 2.5)
>>> lpr.is_avoiding_sure_loss()
False
```

A few simple examples:

```>>> lpr = LowPoly(pspace='xyz')
>>> lpr.is_avoiding_sure_loss()
True
```
```>>> lpr = LowPoly(pspace='xyz', lprob=['0.1', '0.2', '0.15'])
>>> lpr[{'x':1}, 'x'] = (1, None)
>>> lpr[{'x':0}, 'x'] = (0, None)
>>> lpr.is_avoiding_sure_loss()
True
```

See if we can handle zero probabilities:

```>>> lpr = LowPoly(pspace='abcd', number_type='fraction')
>>> lpr[{'a': 1}, True] = (1, None)
>>> print(lpr)
a b c d
1 0 0 0 | a b c d : [1,  ]
>>> lpr.is_avoiding_sure_loss()
True
```

Slightly more complicated:

```>>> lpr = LowPoly(pspace='abcd', number_type='fraction')
>>> lpr[{'a': 1}, True] = (1, None)
>>> lpr[{'b': 1}, 'bcd'] = (2, None) # obviously incurs sure loss!
>>> print(lpr)
a b c d
1 0 0 0 | a b c d : [1,  ]
0 1 0 0 |   b c d : [2,  ]
>>> lpr.is_avoiding_sure_loss()
False
```

And even slightly more complicated:

```>>> lpr = LowPoly(pspace='abcd', number_type='fraction')
>>> lpr[{'a': 1}, True] = (1, None)
>>> lpr[{'b': 1}, 'bcd'] = ('2/3', None)
>>> lpr[{'c': 1}, 'bcd'] = ('2/3', None) # sum larger than one on this layer
>>> print(lpr)
a b c d
1 0 0 0 | a b c d : [1  ,    ]
0 0 1 0 |   b c d : [2/3,    ]
0 1 0 0 |   b c d : [2/3,    ]
>>> lpr.is_avoiding_sure_loss()
False
```

Another more complex example:

```>>> lpr_s_t = LowPoly(pspace=9)
>>> lpr_s_t.set_lower([1,0,0,0,0,0,0,0,0], 0.500)
>>> lpr_s_t.set_upper([1,0,0,0,0,0,0,0,0], 0.666)
>>> lpr_s_t.set_lower([0,1,0,0,0,0,0,0,0], 0.222)
>>> lpr_s_t.set_upper([0,1,0,0,0,0,0,0,0], 0.272)
>>> lpr_s_t.set_lower([0,0,1,0,0,0,0,0,0], 0.125)
>>> lpr_s_t.set_upper([0,0,1,0,0,0,0,0,0], 0.181)
>>> lpr_s_t.set_lower([0,0,0,1,0,0,0,0,0], 0.222)
>>> lpr_s_t.set_upper([0,0,0,1,0,0,0,0,0], 0.333)
>>> lpr_s_t.set_lower([0,0,0,0,1,0,0,0,0], 0.363)
>>> lpr_s_t.set_upper([0,0,0,0,1,0,0,0,0], 0.444)
>>> lpr_s_t.set_lower([0,0,0,0,0,1,0,0,0], 0.250)
>>> lpr_s_t.set_upper([0,0,0,0,0,1,0,0,0], 0.363)
>>> lpr_s_t.set_lower([0,0,0,0,0,0,1,0,0], 0.111)
>>> lpr_s_t.set_upper([0,0,0,0,0,0,1,0,0], 0.166)
>>> lpr_s_t.set_lower([0,0,0,0,0,0,0,1,0], 0.333)
>>> lpr_s_t.set_upper([0,0,0,0,0,0,0,1,0], 0.363)
>>> lpr_s_t.set_lower([0,0,0,0,0,0,0,0,1], 0.454)
>>> lpr_s_t.set_upper([0,0,0,0,0,0,0,0,1], 0.625)
>>> lpr_s_t.is_avoiding_sure_loss()
False
```

## Coherence¶

```>>> lpr = LowPoly(pspace=4)
>>> lpr.set_lower([1,2,3,0], 2.5)
>>> lpr.is_coherent()
True
>>> lpr.set_upper([2,4,6,0], 3) # coherence requires at least 5
>>> lpr.is_coherent()
False
```

## Linearity¶

```>>> lpr = LowPoly(pspace=4)
>>> lpr.set_lower([1,2,3,0], 2.5)
>>> lpr.is_linear()
False
>>> lpr.set_upper([2,4,6,0], 5)
>>> lpr.is_linear()
True
```

## Marginal Extension¶

Finding coherent lower and upper bounds for the oil wildcatter example in [1]:

```>>> # lower previsions over s, given t
>>> lpr_s_t = [LowPoly(pspace=3),
...            LowPoly(pspace=3),
...            LowPoly(pspace=3)]
>>> # lower prevision over t
>>> lpr_t = LowPoly(pspace=3)
>>> lpr_s_t[0].set_lower([1,0,0], 0.5)
>>> lpr_s_t[0].set_upper([1,0,0], 0.666)
>>> lpr_s_t[0].set_lower([0,1,0], 0.222)
>>> lpr_s_t[0].set_upper([0,1,0], 0.272)
>>> lpr_s_t[0].set_lower([0,0,1], 0.125)
>>> lpr_s_t[0].set_upper([0,0,1], 0.181)
>>> lpr_s_t[0].is_coherent()
False
>>> print(float(lpr_s_t[0].get_lower([1,0,0]))) # not coherent!
0.547
>>> print(float(lpr_s_t[0].get_upper([1,0,0]))) # not coherent!
0.653
>>> print(float(lpr_s_t[0].get_lower([0,1,0])))
0.222
>>> print(float(lpr_s_t[0].get_upper([0,1,0])))
0.272
>>> print(float(lpr_s_t[0].get_lower([0,0,1])))
0.125
>>> print(float(lpr_s_t[0].get_upper([0,0,1])))
0.181
>>> lpr_s_t[1].set_lower([1,0,0], 0.222)
>>> lpr_s_t[1].set_upper([1,0,0], 0.333)
>>> lpr_s_t[1].set_lower([0,1,0], 0.363)
>>> lpr_s_t[1].set_upper([0,1,0], 0.444)
>>> lpr_s_t[1].set_lower([0,0,1], 0.250)
>>> lpr_s_t[1].set_upper([0,0,1], 0.363)
>>> lpr_s_t[1].is_coherent()
True
>>> print(float(lpr_s_t[1].get_lower([1,0,0])))
0.222
>>> print(float(lpr_s_t[1].get_upper([1,0,0])))
0.333
>>> print(float(lpr_s_t[1].get_lower([0,1,0])))
0.363
>>> print(float(lpr_s_t[1].get_upper([0,1,0])))
0.444
>>> print(float(lpr_s_t[1].get_lower([0,0,1])))
0.25
>>> print(float(lpr_s_t[1].get_upper([0,0,1])))
0.363
>>> lpr_s_t[2].set_lower([1,0,0], 0.111)
>>> lpr_s_t[2].set_upper([1,0,0], 0.166)
>>> lpr_s_t[2].set_lower([0,1,0], 0.333)
>>> lpr_s_t[2].set_upper([0,1,0], 0.363)
>>> lpr_s_t[2].set_lower([0,0,1], 0.454)
>>> lpr_s_t[2].set_upper([0,0,1], 0.625)
>>> lpr_s_t[2].is_coherent()
False
>>> print(float(lpr_s_t[2].get_lower([1,0,0])))
0.111
>>> print(float(lpr_s_t[2].get_upper([1,0,0])))
0.166
>>> print(float(lpr_s_t[2].get_lower([0,1,0])))
0.333
>>> print(float(lpr_s_t[2].get_upper([0,1,0])))
0.363
>>> print(float(lpr_s_t[2].get_lower([0,0,1]))) # not coherent!
0.471
>>> print(float(lpr_s_t[2].get_upper([0,0,1]))) # not coherent!
0.556
>>> lpr_t.set_lower([1,0,0], 0.181)
>>> lpr_t.set_upper([1,0,0], 0.222)
>>> lpr_t.set_lower([0,1,0], 0.333)
>>> lpr_t.set_upper([0,1,0], 0.363)
>>> lpr_t.set_lower([0,0,1], 0.444)
>>> lpr_t.set_upper([0,0,1], 0.454)
>>> lpr_t.is_coherent()
False
>>> print(float(lpr_t.get_lower([1,0,0]))) # not coherent!
0.183
>>> print(float(lpr_t.get_upper([1,0,0])))
0.222
>>> print(float(lpr_t.get_lower([0,1,0])))
0.333
>>> print(float(lpr_t.get_upper([0,1,0])))
0.363
>>> print(float(lpr_t.get_lower([0,0,1])))
0.444
>>> print(float(lpr_t.get_upper([0,0,1])))
0.454
>>> # now some calculations
>>> gamble_s = [-7, 5, 20]
>>> minus_gamble_s = [7, -5, -20]
>>> print(float(lpr_s_t[0].get_lower(gamble_s)))
-0.961
>>> print(float(lpr_s_t[0].get_lower(minus_gamble_s)))
-1.151
>>> print(float(lpr_s_t[1].get_lower(gamble_s)))
4.754
>>> print(float(lpr_s_t[1].get_lower(minus_gamble_s)))
-7.781
>>> print(float(lpr_s_t[2].get_lower(gamble_s)))
10.073
>>> print(float(lpr_s_t[2].get_lower(minus_gamble_s)))
-12.008
>>> # calculate its lower prevision by marginal extension
>>> # XXX this is a quick hackish way to set up the marginal extension
>>> # XXX see decision tree example for something more sane
>>> lpr = LowPoly(pspace=9)
>>> def get_lower(gamble_s_t):
...     return lpr_t.get_lower([lpr_s.get_lower(gamble_s)
...                             for lpr_s, gamble_s
...                             in zip(lpr_s_t, gamble_s_t)])
>>> def get_upper(gamble_s_t):
...     return lpr_t.get_upper([lpr_s.get_upper(gamble_s)
...                             for lpr_s, gamble_s
...                             in zip(lpr_s_t, gamble_s_t)])
>>> lpr.get_lower = get_lower
>>> lpr.get_upper = get_upper
>>> # a gamble which is a function of s and t; t comes as first
>>> # argument (in order to match lpr_s_t): gamble_s_t[t][s]
>>> gamble_s_t = [[-7,5,20], [-7,5,20], [-7,5,20]]
>>> # do the calculations
>>> print(float(lpr.get_lower(gamble_s_t)))
5.846906
>>> print(float(lpr.get_upper(gamble_s_t)))
8.486768
>>> # another gamble which is a function of s and t
>>> gamble_s_t = [[-7,5,20], [0,0,0], [0,0,0]]
>>> # calculate its lower prevision by marginal extension
>>> print(float(lpr.get_lower(gamble_s_t)))
-0.213342
>>> # another gamble which is a function of s and t
>>> gamble_s_t = [[7,-5,-20], [0,0,0], [0,0,0]]
>>> # calculate its lower prevision by marginal extension
>>> print(float(lpr.get_lower(gamble_s_t)))
-0.255522
>>> # another gamble which is a function of s and t
>>> gamble_s_t = [[-1,-1,-1], [-1,11,26], [-1,11,26]]
>>> # calculate its lower prevision by marginal extension
>>> print(float(lpr.get_lower(gamble_s_t)))
10.506248
>>> print(float(lpr.get_upper(gamble_s_t)))
12.995135
```

## Generalized Bayes Rule¶

The next example is effectively the Monty Hall problem.

```>>> lpr = LowPoly(pspace=4, number_type='fraction')
>>> lpr.set_lower([1, 1, 0, 0], '1/3')
>>> lpr.set_lower([0, 0, 1, 0], '1/3')
>>> lpr.set_lower([0, 0, 0, 1], '1/3')
>>> print(lpr.get_lower([1, 1, 0, 0], set([0, 3])))
0
>>> print(lpr.get_lower([0, 0, 1, 1], set([0, 3])))
1/2
```

Footnotes

 [1] Kikuti, D., Cozman, F., de Campos, C.: Partially ordered preferences in decision trees: Computing strategies with imprecision in probabilities. In: R. Brafman, U. Junker (eds.) IJCAI-05 Multidisciplinary Workshop on Advances in Preference Handling, pp. 118–123, 2005.
 [2] (1, 2, 3, 4) Peter Walley, Renato Pelessoni, and Paolo Vicig. Journal of Statistical Planning and Inference, 126(1):119-151, November 2004.