Intervals

[1]:
from part import Interval, Atomic

Construction

An interval can be created using:

  • the constructor

  • static methods

[2]:
print(Interval[int](lower_value=10, upper_value=20))
[10;20)
[3]:
print(Interval[str](lower_value="abc", upper_value="def", upper_closed=True))
['abc';'def']
[4]:
print(Interval[int](lower_closed=None, lower_value=10, upper_value=20))
(10;20)
[5]:
print(Interval[int]())
(-inf;+inf)
[6]:
print(Atomic[int].from_tuple((10, 20)))
[10;20)
[7]:
print(Atomic[int].from_tuple((10, 20, None)))
(10;20)
[8]:
print(Atomic[int].from_tuple((10, 20, None, True)))
(10;20]
[9]:
print(Atomic[int].from_value(10))
[10;10]
[10]:
print(Interval[int].lower_limit(value=10))
[10;+inf)
[11]:
print(Interval[int].lower_limit(value=10, closed=None))
(10;+inf)
[12]:
print(Interval[int].upper_limit(value=10))
(-inf;10)
[13]:
print(Interval[int].upper_limit(value=10, closed=True))
(-inf;10]

Properties

Properties can be easily accessed.

[14]:
a = Atomic[int].from_tuple((10, 20, None))
print(a.lower)
print(a.lower_value)
print(a.lower_closed)
print(a.upper)
print(a.upper_value)
print(a.upper_closed)
10+
10
None
20-
20
None

Comparison

Intervals can be compared using Allen’s algebra:

[15]:
a = Atomic[int].from_tuple((10, 20, None))
[16]:
a.meets(Atomic[int].from_tuple((20, 30)))
[16]:
False
[17]:
a.meets(Atomic[int].from_tuple((20, 30)), strict=False)
[17]:
True
[18]:
a.overlaps(Atomic[int].from_tuple((15, 30)))
[18]:
True
[19]:
a.starts(Atomic[int].from_tuple((20, 40, None)))
[19]:
False
[20]:
a.starts(Atomic[int].from_tuple((10, 40, None)), strict=False)
[20]:
True
[21]:
a.during(Atomic[int].from_tuple((0, 30)))
[21]:
True
[22]:
a.finishes(Atomic[int].from_tuple((0, 20)))
[22]:
True

Operations

Intervals support set operation: union, intersection and complement. These operations produce instance of FrozenSetInterval.

[23]:
a = Atomic[int].from_tuple((10, 20, None))
b = Atomic[int].from_tuple((15, 30))
c = Atomic[int].from_tuple((30, 40))
[24]:
print(a | b)
print(a | c)
print(a & b)
print(a & c)
print(a - b)
print(a ^ b)
print(~a)
(10;30)
(10;20) | [30;40)
[15;20)

(10;15)
(10;15) | [20;30)
(-inf;10] | [20;+inf)

Interval sets

There exists two versions of interval sets:

  • FrozenIntervalSet

  • MutableIntervalSet

FrozenIntervalSet is slightly more efficient than MutableIntervalSet.

Frozen Interval Set

[25]:
from part import FrozenIntervalSet

Construction

A frozen interval set can be constructed using an iterable of interval-like values.

[26]:
a = FrozenIntervalSet[int]([2, (6, 7), (8, 9, None), (10, 11, True, True)])
[27]:
print(a)
[2;2] | [6;7) | (8;9) | [10;11]

Properties

  • the number of intervals can be known using the standard function len;

  • iteration over a set of intervals is obtained by using the standard function iter;

  • the intervals are accessible using their indices.

[28]:
print(len(a))
print([str(interval) for interval in a])
print(a[0])
print(a[2])
print(a[1:3])
4
['[2;2]', '[6;7)', '(8;9)', '[10;11]']
[2;2]
(8;9)
[6;7) | (8;9)

Comparison

Interval set can be compared using the set comparison operators.

[29]:
a = FrozenIntervalSet[int]([2, (6, 7), (8, 9, None), (10, 11, True, True)])
b = FrozenIntervalSet[int]([(0, 7), (8, 13)])
print(a)
print(b)
[2;2] | [6;7) | (8;9) | [10;11]
[0;7) | [8;13)
[30]:
a <= b
[30]:
True
[31]:
a < b
[31]:
True
[32]:
a == b
[32]:
False
[33]:
a >= b
[33]:
False
[34]:
a > b
[34]:
False

Operations

Classical set operations are defined.

[35]:
a = FrozenIntervalSet[int]([(2, 8), (10, 11, True, True)])
b = FrozenIntervalSet[int]([(0, 7), (8, 13)])
print(a)
print(b)
[2;8) | [10;11]
[0;7) | [8;13)
[36]:
print((10,13) in a)
print((10,13) in b)
print(a | b)
print(a.union(b))
print(a & b)
print(a.intersection(b))
False
True
[0;13)
[0;13)
[2;7) | [10;11]
[2;7) | [10;11]
[37]:
print(~a)
(-inf;2) | [8;10) | (11;+inf)
[38]:
print(a - b)
print(a.difference(b))
print(a ^ b)
print(a.symmetric_difference(b))
[7;8)
[7;8)
[0;2) | [7;10) | (11;13)
[0;2) | [7;10) | (11;13)

Selection

[39]:
a = FrozenIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
[40]:
print([str(interval) for interval in a.select((5, 9))])
['[6;7)']
[41]:
print([str(interval) for interval in a.select((2, 9))])
['[2;2]', '[6;7)']
[42]:
print([str(interval) for interval in a.select((2, 9), strict=False)])
['[2;2]', '[6;7)', '(8;10)']

Mutable Interval Set

In addition to frozen interval set operations, the mutable interval set implements method that can modify the set.

[43]:
from part import MutableIntervalSet

Element operations

[44]:
a = MutableIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
print(a)
a.add((2, 6))
print(a)
[2;2] | [6;7) | (8;10) | [11;13]
[2;7) | (8;10) | [11;13]
[45]:
a = MutableIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
print(a)
a.discard((2, 8))
print(a)
[2;2] | [6;7) | (8;10) | [11;13]
(8;10) | [11;13]
[46]:
a = MutableIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
print(a)
try:
    a.remove((2, 8))
except KeyError as e:
    print(e)
[2;2] | [6;7) | (8;10) | [11;13]
'(2, 8)'
[47]:
a = MutableIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
print(a)
print(a.pop())
print(a)
[2;2] | [6;7) | (8;10) | [11;13]
[2;2]
[6;7) | (8;10) | [11;13]
[48]:
a = MutableIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
print(a)
a.clear()
print(a)
[2;2] | [6;7) | (8;10) | [11;13]

Set operations

[49]:
a = MutableIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
b = MutableIntervalSet[int]([(0, 7), (8, 12)])
print(a)
print(b)
a.update(b)
print(a)
a |= MutableIntervalSet[int]([(7, 8)])
print(a)
[2;2] | [6;7) | (8;10) | [11;13]
[0;7) | [8;12)
[0;7) | [8;13]
[0;13]
[50]:
a = MutableIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
b = MutableIntervalSet[int]([(0, 7), (8, 12)])
print(a)
print(b)
a.intersection_update(b)
print(a)
a &= MutableIntervalSet[int]([(6, 11)])
print(a)
[2;2] | [6;7) | (8;10) | [11;13]
[0;7) | [8;12)
[2;2] | [6;7) | (8;10) | [11;12)
[6;7) | (8;10)
[51]:
a = MutableIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
b = MutableIntervalSet[int]([(0, 7), (8, 12)])
print(a)
print(b)
a.difference_update(b)
print(a)
a -= MutableIntervalSet[int]([12])
print(a)
[2;2] | [6;7) | (8;10) | [11;13]
[0;7) | [8;12)
[12;13]
(12;13]
[52]:
a = MutableIntervalSet[int]([2, (6, 7), (8, 10, None), (11, 13, True, True)])
b = MutableIntervalSet[int]([(0, 7), (8, 12)])
print(a)
print(b)
a.symmetric_difference_update(b)
print(a)
a ^= MutableIntervalSet[int]([(8,12)])
print(a)
[2;2] | [6;7) | (8;10) | [11;13]
[0;7) | [8;12)
[0;2) | (2;6) | [8;8] | [10;11) | [12;13]
[0;2) | (2;6) | (8;10) | [11;13]

Interval dicts

There exists two versions of interval dicts:

  • FrozenIntervalDict

  • MutableIntervalDict

FrozenIntervalDict is slightly more efficient than MutableIntervalDict.

Frozen Interval Dict

[53]:
from part import FrozenIntervalDict

Construction

A frozen interval dict can be constructed using an iterable.

[54]:
print(FrozenIntervalDict[int, int]({(10, 15): 1, (20, 25): 2, (30, 35): 3}))
{'[10;15)': 1, '[20;25)': 2, '[30;35)': 3}
[55]:
print(FrozenIntervalDict[int, int]([((10, 15), 1), ((20, 25), 2), ((30, 35), 3)]))
{'[10;15)': 1, '[20;25)': 2, '[30;35)': 3}

Properties

  • the number of intervals can be known using the standard function len;

  • iteration over a dict of intervals is obtained by using the standard function iter;

  • the values are accessible using their intervals;

  • new dict can be created using the slice notation.

[56]:
a = FrozenIntervalDict[int, int]({(10, 15): 1, (20, 25): 2, (30, 35): 3})
print(len(a))
print([str(interval) for interval in a])
print(a[(10, 15)])
print(a[(11, 12)])
print((11, 12) in a)
print((12, 17) in a)
try:
    print(a[(12, 17)])
except KeyError as e:
    print(e)
print(a[12:32])
3
['[10;15)', '[20;25)', '[30;35)']
1
1
True
False
'(12, 17)'
{'[12;15)': 1, '[20;25)': 2, '[30;32)': 3}

Iteration

As for classical python dictionaries, methods keys, values and items are available.

[57]:
a = FrozenIntervalDict[int, int]({(10, 15): 1, (20, 25): 2, (30, 35): 3})
print([str(interval) for interval in a.keys()])
print([str(value) for value in a.values()])
print([f"{interval}:{value}" for interval, value in a.items()])
['[10;15)', '[20;25)', '[30;35)']
['1', '2', '3']
['[10;15):1', '[20;25):2', '[30;35):3']

Selection

[58]:
a = FrozenIntervalDict[int, int]({(10, 15): 1, (20, 25): 2, (30, 35): 3})
print([str(interval) for interval in a.select((12, 26))])
print([str(interval) for interval in a.select((12, 22), strict=False)])
['[20;25)']
['[10;15)', '[20;25)']

Compression

[59]:
a = FrozenIntervalDict[int, int]({(10, 15): 1, (14, 25): 1, (30, 35): 2, (33, 45): 2})
print(a)
print(a.compress())
{'[10;14)': 1, '[14;25)': 1, '[30;33)': 2, '[33;45)': 2}
{'[10;25)': 1, '[30;45)': 2}

Mutable Interval Dict

[60]:
from part import MutableIntervalDict

Modify items

[61]:
a = MutableIntervalDict[int, int]({(10, 15): 1, (20, 25): 2, (30, 35): 3})
print(a)
a[12] = 4
print(a)
a[14:22] = 5
print(a)
del a[12:22]
print(a)
{'[10;15)': 1, '[20;25)': 2, '[30;35)': 3}
{'[10;12)': 1, '[12;12]': 4, '(12;15)': 1, '[20;25)': 2, '[30;35)': 3}
{'[10;12)': 1, '[12;12]': 4, '(12;14)': 1, '[14;22)': 5, '[22;25)': 2, '[30;35)': 3}
{'[10;12)': 1, '[22;25)': 2, '[30;35)': 3}

Update

[62]:
a = MutableIntervalDict[int, int](
    {(10, 15): 1, (20, 25): 2, (30, 35): 3},
    operator=lambda x, y: x + y
)
a |= MutableIntervalDict[int, int]({(14, 21): 2})
print(a)
{'[10;14)': 1, '[14;15)': 3, '[15;20)': 2, '[20;21)': 4, '[21;25)': 2, '[30;35)': 3}
[63]:
a = MutableIntervalDict[float, set](
    operator=lambda x, y: x | y,
    strict=False,
)
print(a)
a |= MutableIntervalDict[float, set]({(1.2, 3.6): {1}, (3.8, 4.5) : {2}})
print(a)
a |= MutableIntervalDict[float, set]({(2, 4): {3}})
print(a)
{}
{'[1.2;3.6)': {1}, '[3.8;4.5)': {2}}
{'[1.2;2)': {1}, '[2;3.6)': {1, 3}, '[3.6;3.8)': {3}, '[3.8;4)': {2, 3}, '[4;4.5)': {2}}

Clear

[64]:
a = MutableIntervalDict[int, int]({(10, 15): 1, (20, 25): 2, (30, 35): 3})
print(a)
a.clear()
print(a)
{'[10;15)': 1, '[20;25)': 2, '[30;35)': 3}
{}