Skip to content
Pyrula
Pre-release: the packages are not on PyPI yet, so pip install does not resolve. Build from source to try it today.

IntSet

IntSet is an immutable set specialized for non-negative integers. It is backed by a Roaring bitmap, so union, intersection, and difference run entirely in Rust over compressed bitmaps: no per-element Python loop and no GIL contention during the operation. Use it for large sets of integer IDs when a regular ISet of boxed integers is the bottleneck.

The same engine sits underneath analytics systems like Druid, Lucene, and ClickHouse for exactly this reason: bulk set algebra over integer IDs.

from pyrula import IntSet
s = IntSet([1, 2, 3, 3, 2]) # from any iterable of ints; duplicates dropped
len(s) # 3
s.contains(2) # True
2 in s # True (__contains__)
IntSet().is_empty() # True

Values must be non-negative and fit in a 64-bit unsigned integer. Negative values, values above 2**64 - 1, and non-integers raise ValueError / TypeError.

add and remove always return a new IntSet; the original is unchanged. remove is a no-op for absent values.

s = IntSet([1, 2])
s.add(3).to_list() # [1, 2, 3]
s.to_list() # [1, 2] -- unchanged

Each operation runs over compressed bitmaps in Rust without materializing Python integers.

a = IntSet([1, 2, 3, 4])
b = IntSet([3, 4, 5])
a.union(b) # IntSet([1, 2, 3, 4, 5])
a.intersection(b) # IntSet([3, 4])
a.difference(b) # IntSet([1, 2])
a.symmetric_difference(b) # IntSet([1, 2, 5])
IntSet([3, 4]).is_subset(a) # True
IntSet([7, 8]).is_disjoint(a) # True
a.cardinality() # 4

to_list returns a plain Python list[int] in ascending order.

to_bytes writes the set in the Roaring portable byte format; from_bytes reads it back. The bytes are compact (the bitmap is already compressed) and interoperable with other Roaring implementations, which makes IntSet usable as durable checkpoint state, for example, a persisted “seen” set on an exactly-once path.

s = IntSet([1, 5, 9, 2**40])
blob = s.to_bytes() # bytes, Roaring portable format
IntSet.from_bytes(blob).to_list() # [1, 5, 9, 1099511627776]

Use IntSet when the integers are dense or clustered (entity IDs, row offsets, feature flags, graph adjacency), where the Roaring bitmap compresses well and set algebra is hundreds to thousands of times faster than a Python set.

For uniformly random integers spread across the full 64-bit range, the bitmap cannot compress and Roaring is the wrong tool; a plain set or ISet will be faster. Real ID sets almost always cluster, which is the case IntSet is built for.

IntSet mirrors the role of Scala’s scala.collection.immutable.BitSet: an immutable set specialized for non-negative integers with compact storage and fast bulk operations.

PyrulaScala
IntSet([1, 2, 3])BitSet(1, 2, 3)
s.add(x)s + x
s.remove(x)s - x
s.contains(x)s.contains(x)
s.union(t)s ++ t / s.union(t)
s.intersection(t)s.intersect(t)
s.difference(t)s.diff(t)
s.symmetric_difference(t)s.xor(t)
s.is_subset(t)s.subsetOf(t)