Types

Intervals

Intervals represent a contiguous set of elements. They are right-open by default

>>> from pyicl import Interval
>>> interval = Interval(0, 10)
>>> print interval
[0,10)
>>> 0 in interval
True
>>> 9 in interval
True
>>> 10 in interval
False

This is shown by square and round brackets in the string representation. The bounds are accessed via the bounds property.

>>> from pyicl import IntervalBounds
>>> print Interval(0, 3)
[0,3)
>>> print Interval(0, 3, IntervalBounds.LEFT_OPEN)
(0,3]
>>> print Interval(0, 3, IntervalBounds.CLOSED)
[0,3]
>>> print Interval(0, 3, IntervalBounds.OPEN)
(0,3)
>>> print Interval(0, 3, IntervalBounds.OPEN).bounds
OPEN

Empty intervals and intervals with exactly one element in them can be constructed

>>> Interval().empty
True
>>> 1 in Interval(1)
True

The bounds can be accessed as lower and upper

>>> Interval(0, 3).lower
0
>>> Interval(0, 3).upper
3

Intervals over discrete domains such as integers have first and last properties.

>>> from pyicl import IntInterval
>>> IntInterval(0, 3).first
0
>>> IntInterval(0, 3).last
2

Intervals are false if empty

>>> bool(Interval())
False
>>> Interval().empty
True
>>> bool(Interval(1, 10))
True

Element and interval membership is straightforward

>>> 1 in Interval(0, 2)
True
>>> 3 in Interval(0, 2)
False
>>> Interval(0, 2) in Interval(0, 2)
True
>>> Interval(0, 3) in Interval(0, 2)
False

Equivalence works as expected

>>> Interval(0, 2) == Interval(0, 2)
True
>>> Interval(0, 3) != Interval(0, 2)
True

Ordering works thus

>>> Interval(1, 3) < Interval(5, 6)
True
>>> Interval(1, 3) < Interval(1, 6)
True
>>> Interval(1, 6) < Interval(1, 3)
False
>>> Interval(5, 6) > Interval(1, 3)
True
>>> Interval(1, 3) < Interval(1, 5)
True
>>> Interval(1, 5) > Interval(1, 3)
True
>>> Interval(1, 3) <= Interval(5, 6)
True
>>> Interval(5, 6) >= Interval(1, 3)
True
>>> Interval(1, 3) <= Interval(1, 5)
True
>>> Interval(1, 5) >= Interval(1, 3)
True
>>> Interval(1, 3) > Interval(5, 6)
False
>>> Interval(5, 6) < Interval(1, 3)
False
>>> Interval(1, 3) > Interval(1, 5)
False
>>> Interval(1, 5) < Interval(1, 3)
False

There are also tests for exclusive ordering

>>> IntInterval(1, 3).exclusive_less(IntInterval(1, 5))
False
>>> IntInterval(1, 5).exclusive_less(IntInterval(5, 7))
True
>>> IntInterval(1, 5).lower_less(IntInterval(2, 7))
True
>>> IntInterval(1, 5).lower_less(IntInterval(1, 7))
False
>>> IntInterval(1, 5).lower_equal(IntInterval(1, 7))
True
>>> IntInterval(1, 5).lower_equal(IntInterval(0, 7))
False
>>> IntInterval(1, 5).lower_less_equal(IntInterval(2, 7))
True
>>> IntInterval(1, 5).lower_less_equal(IntInterval(0, 7))
False
>>> IntInterval(1, 5).upper_less(IntInterval(2, 7))
True
>>> IntInterval(1, 5).upper_less(IntInterval(1, 5))
False
>>> IntInterval(1, 5).upper_equal(IntInterval(1, 5))
True
>>> IntInterval(1, 5).upper_equal(IntInterval(0, 7))
False
>>> IntInterval(1, 5).upper_less_equal(IntInterval(2, 7))
True
>>> IntInterval(1, 5).upper_less_equal(IntInterval(0, 3))
False

Size comes in different forms. Discrete data types have a cardinality property

>>> from pyicl import FloatInterval
>>> IntInterval(1, 5).size
4
>>> IntInterval(1, 5).cardinality
4
>>> len(Interval(1, 5))
4
>>> len(FloatInterval(1, 5))
4

Intervals can be subtracted from the left or the right

>>> print Interval(1, 5).right_subtract(Interval(3, 5))
[1,3)
>>> print Interval(1, 5).left_subtract(Interval(1, 3))
[3,5)

Intervals can be intersected and tested for intersections and disjointedness

>>> print Interval(1, 5).intersection(Interval(0, 3))
[1,3)
>>> Interval(1, 3) == Interval(1, 5) & Interval(0, 3)
True
>>> interval = Interval(1, 5)
>>> interval &= Interval(0, 3)
>>> Interval(1, 3) == interval
True
>>> Interval(3, 5).intersects(Interval(1, 4))
True
>>> Interval(3, 5).intersects(Interval(1, 3))
False
>>> Interval(3, 5).disjoint(Interval(1, 4))
False
>>> Interval(3, 5).disjoint(Interval(1, 3))
True

Intervals can be tested whether they touch one another. “Touches” is not a reflexive operation.

>>> Interval(0, 3).touches(Interval(3, 5))
True
>>> Interval(0, 3).touches(Interval(4, 5))
False
>>> Interval(0, 3).touches(Interval(2, 5))
False
>>> Interval(3, 5).touches(Interval(0, 3))
False

The interval between two intervals can be calculated.

>>> print Interval(0, 3).inner_complement(Interval(6, 9))
[3,6)
>>> print Interval(6, 9).inner_complement(Interval(0, 3))
[3,6)

The distance between two intervals can be calculated.

>>> print Interval(0, 3).distance(Interval(6, 9))
3
>>> print Interval(6, 9).distance(Interval(0, 3))
3

IntervalSets

An interval set is a set that is implemented as a set of elements. They can be constructed empty, with a single element, with an interval or with another interval set

>>> from pyicl import IntervalSet, Interval, IntervalSet
>>> IntervalSet().empty
True
>>> .1 in IntervalSet(.1)
True
>>> interval_set = IntervalSet(Interval(1, 9))
>>> 5 in interval_set
True
>>> 5 in IntervalSet(interval_set)
True

Iteration over intervals occurs at a segmental level. That is the members of the interval set are presented as distinct whole intervals

>>> for interval in IntervalSet(Interval(1, 9)).add(Interval(20, 29)):
...     print interval
[1,9)
[20,29)

It is possible to look for the interval that an element is in

>>> print IntervalSet(Interval(1, 9)).find(5)
[1,9)
>>> print IntervalSet(Interval(1, 9)).find(10)
None

Similarly to intervals, there are tests for emptyness

>>> bool(IntervalSet())
False
>>> bool(IntervalSet(1))
True
>>> IntervalSet().empty
True
>>> IntervalSet(1).empty
False

Containedness is as expected

>>> 1 in IntervalSet(1)
True
>>> 2 in IntervalSet(1)
False
>>> 2 in IntervalSet(Interval(1, 9))
True
>>> Interval(1, 9) in IntervalSet(Interval(1, 9))
True
>>> IntervalSet(Interval(1, 9)) in IntervalSet(Interval(1, 9))
True

Equivalences and orderings are also as expected

>>> from pyicl import IntIntervalSet
>>> IntIntervalSet(1) == IntIntervalSet(1)
True
>>> IntIntervalSet(1) == IntIntervalSet(IntInterval(1))
True
>>> IntIntervalSet(1) == IntIntervalSet(IntInterval(1, 3))
False
>>> IntIntervalSet(1) != IntIntervalSet(IntInterval(1, 3))
True
>>> IntIntervalSet(1) < IntIntervalSet(IntInterval(1, 3))
True
>>> IntIntervalSet(1) > IntIntervalSet(IntInterval(1, 3))
False

Discrete data types have a size and cardinality. Interval sets also have an iterative size describing how many intervals they are composed of

>>> IntIntervalSet(IntInterval(1, 5)).size
4
>>> IntIntervalSet(IntInterval(1, 5)).cardinality
4
>>> len(IntIntervalSet(IntInterval(1, 5)))
4
>>> IntIntervalSet(IntInterval(1, 5)).iterative_size
1
>>> IntIntervalSet(IntInterval(1, 5)).add(IntInterval(7,10)).iterative_size
2
>>> IntIntervalSet(IntInterval(1, 5)).add(IntInterval(5,10)).iterative_size
1
>>> IntIntervalSet(IntInterval(1, 5)).interval_count
1

As with intervals, interval sets have bounds. Discrete data types also have a first and a last element

>>> IntervalSet(Interval(1, 5)).lower
1
>>> IntervalSet(Interval(1, 5)).upper
5
>>> IntIntervalSet(IntInterval(1, 5)).first
1
>>> IntIntervalSet(IntInterval(1, 5)).last
4

IntervalMaps

An interval map is a map (or in python-esque, a dict) that is implemented as a map of interval-value pairs. Each element has a particular value associated with it. The collection of elements is stored as a set of intervals such that each element of an interval maps to the same value.

The domain of an interval map is the domain of the intervals in the map. The codomain of an interval map is the data type of the values that the intervals map to. An element of an interval map is a pair mapping an element of the domain to a value in the codomain.

>>> from pyicl import IntervalMap
>>> print IntervalMap.Element(1, "value")
1; value

A segment of an interval map is a pair mapping an interval in the domain to a value in the codomain.

>>> print IntervalMap.Segment(Interval(1, 5), 10.4)
[1,5); 10.4

Interval maps can be constructed empty or from a segment

>>> IntervalMap().empty
True
>>> 3 in IntervalMap(IntervalMap.Segment(Interval(1, 5), object()))
True

Interval maps can be populated in several ways. The add method will combine values in the codomain.

>>> map = IntervalMap(IntervalMap.Segment(Interval(1, 5), "X"))
>>> print map.add(IntervalMap.Segment(Interval(3, 8), "Y"))
{([1,3)->X)([3,5)->XY)([5,8)->Y)}

+= acts in the same way as add.

>>> map = IntervalMap(IntervalMap.Segment(Interval(1, 5), "X"))
>>> map += (IntervalMap.Segment(Interval(3, 8), "Y"))
>>> print map
{([1,3)->X)([3,5)->XY)([5,8)->Y)}

The insert method only inserts into the map where there is no collision.

>>> map = IntervalMap(IntervalMap.Segment(Interval(1, 5), "X"))
>>> print map.insert(IntervalMap.Segment(Interval(3, 8), "Y"))
{([1,5)->X)([5,8)->Y)}

The set method will override values.

>>> print IntervalMap(IntervalMap.Segment(Interval(1, 5), "X")).set(IntervalMap.Element(3, "Y"))
{([1,3)->X)([3,3]->Y)((3,5)->X)}

If the codomain of a map supports subtraction, so does the map

>>> map = IntervalMap(IntervalMap.Segment(Interval(1, 5), 2))
>>> print map - IntervalMap.Segment(Interval(2, 4), 1)
{([1,2)->2)([2,4)->1)([4,5)->2)}

The erase method will remove values

>>> map = IntervalMap(IntervalMap.Segment(Interval(1, 5), 2))
>>> print map.erase(Interval(2, 4))
{([1,2)->2)([4,5)->2)}

Interval maps are presented as sequences of segments

>>> for segment in IntervalMap(IntervalMap.Segment(Interval(1, 5), "value1")).add(IntervalMap.Segment(Interval(11, 15), "value2")):
...     print segment
[1,5); value1
[11,15); value2

The segment that contains a value in the domain can be found if it is in the map

>>> print IntervalMap(IntervalMap.Segment(Interval(1, 5), object())).find(2).interval
[1,5)
>>> print IntervalMap(IntervalMap.Segment(Interval(1, 5), object())).find(12)
None

A map can test whether it holds a particular value or interval in the domain, an element, a segment or another map

>>> map = IntervalMap(IntervalMap.Segment(Interval(1, 5), "X"))
>>> 2 in map
True
>>> Interval(1, 3) in map
True
>>> IntervalMap.Element(3, "X") in map
True
>>> IntervalMap.Element(8, "X") in map
False
>>> IntervalMap.Element(3, "Y") in map
False
>>> IntervalMap.Segment(Interval(1, 3), "X") in map
True
>>> IntervalMap.Segment(Interval(1, 8), "X") in map
False
>>> IntervalMap.Segment(Interval(1, 3), "Y") in map
False
>>> map in map
True
>>> IntervalMap(IntervalMap.Segment(Interval(1, 5), "X")) in map
True
>>> IntervalMap(IntervalMap.Segment(Interval(1, 5), "Y")) in map
False

Equality operators work as expected

>>> segment = IntervalMap.Segment(Interval(1, 5), "X")
>>> map == IntervalMap(segment)
True
>>> map != IntervalMap()
True

The size of an interval map is the number of elements it holds. The iterative size or count is how many segments it holds

>>> from pyicl import IntIntervalMap
>>> IntIntervalMap().size
0
>>> IntIntervalMap(IntIntervalMap.Segment(IntInterval(1, 5), "X")).size
4
>>> IntIntervalMap(IntIntervalMap.Segment(IntInterval(1, 5), "X")).cardinality
4
>>> len(IntIntervalMap(IntIntervalMap.Segment(IntInterval(1, 5), "X")))
4
>>> IntervalMap(IntervalMap.Segment(Interval(1, 5), "X")).iterative_size
1
>>> IntervalMap(IntervalMap.Segment(Interval(1, 5), "X")).interval_count
1

Intersections between maps and various other types are possible

>>> from pyicl import IntervalIntMap
>>> map = IntervalIntMap(IntervalIntMap.Segment(Interval(1, 5), 2))
>>> print map.intersection(Interval(2, 4))
{([2,4)->2)}
>>> print map.intersection(5)
{}
>>> print map.intersection(3)
{([3,3]->2)}

Symmetric difference or XOR is available

>>> from pyicl import IntervalIntMap
>>> map = IntervalIntMap(IntervalIntMap.Segment(Interval(3, 5), 2))
>>> print map ^ IntervalIntMap.Segment(Interval(0, 10), 4)
{([0,3)->4)([5,10)->4)}
>>> map ^= IntervalIntMap.Segment(Interval(0, 10), 4)
>>> print map
{([0,3)->4)([5,10)->4)}

IntervalBounds

Interval bounds can be open or closed on the left or right of the interval.

>>> from pyicl import IntervalBounds
>>> IntervalBounds.OPEN.closed_on_right
False
>>> IntervalBounds.OPEN.closed_on_left
False
>>> IntervalBounds.LEFT_OPEN.closed_on_right
True
>>> IntervalBounds.LEFT_OPEN.closed_on_left
False
>>> IntervalBounds.RIGHT_OPEN.closed_on_right
False
>>> IntervalBounds.RIGHT_OPEN.closed_on_left
True
>>> IntervalBounds.CLOSED.closed_on_right
True
>>> IntervalBounds.CLOSED.closed_on_left
True

Table Of Contents

Previous topic

Definition and Basic Usage

Next topic

Examples

This Page