Intervals#

class pba.interval.Interval#

An interval is an uncertain number for which only the endpoints are known, \(x=[a,b]\). This is interpreted as \(x\) being between \(a\) and \(b\) but with no more information about the value of \(x\).

Intervals embody epistemic uncertainty within PBA.

Creation#

Intervals can be created using either of the following:

>>> pba.Interval(0,1)
Interval [0,1]
>>> pba.I(2,3)
Interval [2,3]

Tip

The shorthand I is an alias for Interval

Intervals can also be created from a single value ± half-width:

>>> pba.PM(0,1)
Interval [-1,1]

By default intervals are displayed as Interval [a,b] where a and b are the left and right endpoints respectively. This can be changed using the interval.pm_repr and interval.lr_repr functions.

Arithmetic#

For two intervals [a,b] and [c,d] the following arithmetic operations are defined:

Addition

\([a,b] + [c,d] = [a+c,b+d]\)

Subtraction

\([a,b] - [c,d] = [a-d,b-c]\)

Multiplication

\([a,b] * [c,d] = [\min(ac,ad,bc,bd),\max(ac,ad,bc,bd)]\)

Division

\([a,b] / [c,d] = [a,b] * \frac{1}{[c,d]} \equiv [\min(a/c,a/d,b/c,b/d),\max(a/c,a/d,b/c,b/d)]\)

Alternative arithmetic methods are described in interval.add, interval.sub, interval.mul, interval.div.

Attributes:

left: The left boundary of the interval.

right: The right boundary of the interval.

Default values

If only 1 argument is given then the interval is assumed to be zero width around this value.

If no arguments are given then the interval is assumed to be vaccous (i.e. \([-\infty,\infty]\)). This is implemented as Interval(-np.inf,np.inf).

__init__(left=None, right=None)#

Attributes:

left: The left boundary of the interval.

right: The right boundary of the interval.

Default values

If only 1 argument is given then the interval is assumed to be zero width around this value.

If no arguments are given then the interval is assumed to be vaccous (i.e. \([-\infty,\infty]\)). This is implemented as Interval(-np.inf,np.inf).

add(other, method=None)#

Adds the interval and another object together.

Args:

other: The interval or numeric value to be added. This value must be transformable into an Interval object.

Methods:

p - perfect arithmetic \([a,b]+[c,d] = [a + c, b + d]\)

o - opposite arithmetic \([a,b]+[c,d] = [a + d, b + c]\)

None, i, f - Standard interval arithmetic is used.

Returns:

Interval

padd(other)#

Warning

This method is deprecated. Use add(other, method=’p’) instead.

oadd(other)#

Warning

This method is deprecated. Use add(other, method=’o’) instead.

sub(other, method=None)#

Subtracts other from self.

Args:

other: The interval or numeric value to be subracted. This value must be transformable into an Interval object.

Methods:

p: perfect arithmetic \(a+b = [a.left - b.left, a.right - b.right]\)

o: opposite arithmetic \(a+b = [a.left - b.right, a.right - b.left]\)

None, i, f - Standard interval arithmetic is used.

Returns:

Interval

psub(other)#

Warning

Depreciated use self.sub(other, method = ‘p’) instead

osub(other)#

Warning

Depreciated use self.sub(other, method = ‘o’) instead

mul(other, method=None)#

Multiplies self by other.

Args:

other: The interval or numeric value to be multiplied. This value must be transformable into an Interval object.

Methods:

p: perfect arithmetic \([a,b],[c,d] = [a * c, b * d]\)

o: opposite arithmetic \([a,b],[c,d] = [a * d, b * c]\)

None, i, f - Standard interval arithmetic is used.

Returns:

Interval: The result of the multiplication.

pmul(other)#

Warning

Depreciated use self.mul(other, method = ‘p’) instead

omul(other)#

Warning

Depreciated use self.mul(other, method = ‘o’) instead

div(other, method=None)#

Divides self by other

If \(0 \in other\) it returns a division by zero error

Args:

other (Interval or numeric): The interval or numeric value to be multiplied. This value must be transformable into an Interval object.

Methods:

p: perfect arithmetic \([a,b],[c,d] = [a * 1/c, b * 1/d]\)

o: opposite arithmetic \([a,b],[c,d] = [a * 1/d, b * 1/c]\)

None, i, f - Standard interval arithmetic is used.

Implementation

>>> self.add(1/other, method = method)

Error

If \(0 \in [a,b]\) it returns a division by zero error

pdiv(other)#

Warning

Depreciated use self.div(other, method = ‘p’) instead

odiv(other)#

Warning

Depreciated use self.div(other, method = ‘o’) instead

recip()#

Calculates the reciprocle of the interval.

Returns:

Interval: Equal to \([1/b,1/a]\)

Example:

>>> pba.Interval(2,4).recip()
Interval [0.25, 0.5]

Error

If \(0 \in [a,b]\) it returns a division by zero error

equiv(other: Interval) bool#

Checks whether two intervals are equivalent.

Parameters:

other: The interval to check against.

Returns True if:

self.left == other.right and self.right == other.right

False otherwise.

Error

TypeError: If other is not an instance of Interval

See also

is_same_as()

Examples:

>>> a = Interval(0,1)
>>> b = Interval(0.5,1.5)
>>> c = I(0,1)
>>> a.equiv(b)
False
>>> a.equiv(c)
True
lo()#
Returns:

self.left

Tip

This function is redundant but exists to match Pbox class for possible internal reasons.

hi()#
Returns:

self.right

Tip

This function is redundant but exists to match Pbox class for possible internal reasons.

width() float#

Returns:

float: The width of the interval, \(\mathrm{right} - \mathrm{left}\)

Example:

>>> pba.Interval(0,3).width()
3
halfwidth() float#

Returns:

float: The half-width of the interval, \((\mathrm{right} - \mathrm{left})/2\)

Example:

>>> pba.Interval(0,3).halfwidth()
1.5

Implementation

>>> self.width()/2
midpoint() float#

Returns:

float: The midpoint of the interval, \((\mathrm{right} + \mathrm{left})/2\)

Example:

>>> pba.Interval(0,2).midpoint()
1.0
to_logical()#

Turns the interval into a logical interval, this is done by chacking the truth value of the ends of the interval

Returns:

Logical: The logical interval

Implementation

>>> left = self.left.__bool__()
>>> right = self.right.__bool__()
>>> Logical(left,right)
env(other: list | Interval) Interval#

Calculates the envelope between two intervals

Parameters:

other : Interval or list. The interval to envelope with self

Hint

If other is a list then the envelope is calculated between self and each element of the list. In this case the envelope is calculated recursively and pba.envelope() may be more efficient.

Important

If other is a Pbox then Pbox.env() is called

Returns:

Interval: The envelope of self and other

straddles(N: int | float | Interval, endpoints: bool = True) bool#

Parameters:

N: Number to check. If N is an interval checks whether the whole interval is within self.

endpoints: Whether to include the endpoints within the check

Returns True if:

\(\mathrm{left} \leq N \leq \mathrm{right}\) (Assuming endpoints=True).

For interval values. \(\mathrm{left} \leq N.left \leq \mathrm{right}\) and \(\mathrm{left} \leq N.right \leq \mathrm{right}\) (Assuming endpoints=True).

False otherwise.

Tip

N in self is equivalent to self.straddles(N)

straddles_zero(endpoints=True)#

Checks whether \(0\) is within the interval

Implementation

Equivalent to self.straddles(0,endpoints)

intersection(other: Interval | list) Interval#

Calculates the intersection between intervals

Parameters:

other: The interval to intersect with self. If an interval is not given will try to cast as an interval. If a list is given will calculate the intersection between self and each element of the list.

Returns:

Interval: The intersection of self and other. If no intersection is found returns None

Example:

>>> a = Interval(0,1)
>>> b = Interval(0.5,1.5)
>>> a.intersection(b)
Interval [0.5, 1]
exp()#
log()#
sqrt()#
sample(seed=None, numpy_rng: Generator | None = None) float#

Generate a random sample within the interval.

Parameters:

seed (int, optional): Seed value for random number generation. Defaults to None.

numpy_rng (numpy.random.Generator, optional): Numpy random number generator. Defaults to None.

Returns:

float: Random sample within the interval.

Implementation

If numpy_rng is given:

>>> numpy_rng.uniform(self.left, self.right)

Otherwise the following is used:

>>> import random
>>> random.seed(seed)
>>> self.left + random.random() * self.width()

Examples:

>>> pba.Interval(0,1).sample()
0.6160988752201705
>>> pba.I(0,1).sample(seed = 1)
0.13436424411240122

If a numpy random number generator is given then it is used instead of the default python random number generator. It has to be initialised first.

>>> import numpy as np
>>> rng = np.random.default_rng(seed = 0)
>>> pba.I(0,1).sample(numpy_rng = rng)
0.6369616873214543

Plus-Minus Intervals#

pba.interval.PM(x, hw)#

Create an interval centered around x with a half-width of hw.

Parameters:

x (float): The center value of the interval.

hw (float): The half-width of the interval.

Returns:

Interval: An interval object with lower bound x-hw and upper bound x+hw.

Error

ValueError: If hw is less than 0.

Example:
>>> pba.pm(0, 1)
Interval [-1, 1]
pba.interval.pm_repr()#

Modifies the interval class to display the interval in [midpoint ± half-width] format.

Example:

>>> import pba
>>> pba.interval.pm_repr()
>>> a = pba.Interval(0,1) # defined using left and right. This cannot be overriden.
>>> b = pba.PM(0,1) # defined using midpoint and half-width
>>> print(a)
Interval [0.5 ± 0.5]
>>> print(b)
Interval [0 ± 1]

See also

lr_interval_repr()

pba.interval.lr_repr()#

Modifies the interval class to display the interval in [left, right] format.

Note

This function primarily exists to undo the effects of pm_interval_repr(). By default the interval class displays in this format.

Example:

>>> import pba
>>> pba.interval.pm_repr()
>>> a = pba.Interval(0,1) # defined using left and right, this cannot be overriden.
>>> print(a)
Interval [0.5±0.5]
>>> pba.interval.lr_repr()
>>> b = pba.PM(0,1) # defined using midpoint and half-width
>>> print(b)
Interval [-1,1]