torchtsetlin.data¶
Encoders¶
torchtsetlin.data.encoders ¶
Booleanization: turning continuous, categorical and image data into Boolean features.
All encoders are torch.nn.Module objects so they can be moved to a device, saved in a
state_dict and composed with :class:torchtsetlin.data.Compose. They follow the
scikit-learn fit / transform protocol on top of forward (transform is an
alias of forward; fit_transform chains both).
BooleanEncoder ¶
Bases: Module
Base class for encoders. Subclasses implement :meth:forward (and optionally
:meth:fit). transform is an alias of forward.
Binarizer ¶
Bases: BooleanEncoder
x > threshold (or >=) per element. threshold may be a scalar or a tensor
broadcastable to the input (e.g. one threshold per feature).
Source code in src/torchtsetlin/data/encoders.py
ThermometerEncoder ¶
ThermometerEncoder(n_bits: int = 8, strategy: str = 'quantile', thresholds: Optional[Tensor] = None, value_range: Optional[Sequence[float]] = None, flatten: bool = True)
Bases: BooleanEncoder
Thermometer (ordinal) encoding of continuous features.
Each feature v becomes n_bits Booleans [v >= t_1, ..., v >= t_n] for
increasing thresholds t_k. Clauses can then express ranges with two literals
(x>=t_a AND NOT x>=t_b). Thresholds are learned by :meth:fit (quantiles by default)
or given explicitly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_bits
|
int
|
thresholds per feature. |
8
|
strategy
|
str
|
threshold strategy (see :func: |
'quantile'
|
thresholds
|
Optional[Tensor]
|
explicit |
None
|
value_range
|
Optional[Sequence[float]]
|
|
None
|
flatten
|
bool
|
return |
True
|
Source code in src/torchtsetlin/data/encoders.py
feature_names ¶
Names of the Boolean outputs, e.g. "age>=30.0".
Source code in src/torchtsetlin/data/encoders.py
decode_range ¶
Range (lo, hi) implied by included positive/negated bits of one feature.
Source code in src/torchtsetlin/data/encoders.py
OneHotEncoder ¶
Bases: BooleanEncoder
One Boolean per category value per column. Categories are integers (fit records the
number of categories per column, or pass n_categories).
Source code in src/torchtsetlin/data/encoders.py
BitPlaneEncoder ¶
Bases: BooleanEncoder
Plain binary (place-value) encoding of non-negative integers with n_bits bits,
most significant bit first. Images (N, C, H, W) become (N, C*n_bits, H, W).
Source code in src/torchtsetlin/data/encoders.py
AdaptiveThresholdEncoder ¶
Bases: BooleanEncoder
Adaptive (local) thresholding for grayscale / multi-channel images, as used to booleanize MNIST/CIFAR for convolutional Tsetlin machines.
bit = pixel > local_mean(pixel) - C where the local mean is computed over a
block_size x block_size window, either uniformly (method="mean") or with a
Gaussian kernel (method="gaussian", OpenCV ADAPTIVE_THRESH_GAUSSIAN_C).
Input (N, C, H, W) (or (N, H, W)) -> Boolean of the same shape.
Source code in src/torchtsetlin/data/encoders.py
ColorThermometerEncoder ¶
Bases: BooleanEncoder
Per-channel thermometer encoding of images: each pixel channel becomes n_bits
planes [v >= k * 255/(n_bits+1)]. (N, C, H, W) -> (N, C*n_bits, H, W).
value_range defaults to (0, 255); use (0, 1) for float images.
Source code in src/torchtsetlin/data/encoders.py
HypervectorEncoder ¶
HypervectorEncoder(vocab_size: int, dim: int = 1024, n_bits: int = 8, bind_position: bool = False, seed: int = 0)
Bases: BooleanEncoder
Sparse binary hypervector encoding of discrete tokens (Halenka et al., 2024).
Every token id t < vocab_size owns a fixed random hypervector with n_bits ones
among dim positions. A sample (a set / sequence of token ids) is bundled with
bitwise OR. Optionally each position p is bound by cyclically shifting the token's
hypervector by p (bind_position=True), so word order is preserved.
Input: (N, L) long tensor of token ids (padding id -1 is ignored).
Output: (N, dim) Boolean.
Source code in src/torchtsetlin/data/encoders.py
PyramidEncoder ¶
Bases: BooleanEncoder
Stack each pixel's neighbourhood at several resolutions into extra Boolean planes.
A k x k patch is all a dense Tsetlin machine sees of a pixel, and some classes simply
are not decidable from it — a patch of building interior and a patch of road look alike
once illumination varies; telling them apart needs to know where the horizon is. This is
the gap an encoder-decoder closes in a CNN, and it can be closed natively here: OR-pool
the Boolean planes to build a pyramid, take the same k x k patch at each level, and
resample every level back to full resolution. A clause then reads a pixel's immediate
neighbourhood and the coarse structure around it, with no float parameter and no
gradient anywhere.
Feed the result to a model with patch_size=1: the neighbourhood is already in the
planes, and asking for a second one on top multiplies the feature count for nothing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scales
|
Sequence[int]
|
downsampling factors, ascending. |
(1, 2, 4)
|
patch
|
int
|
neighbourhood side length taken at each level. |
3
|
mode
|
str
|
pooling mode for the pyramid (see :func: |
'or'
|
Shape
- input:
(B, Z, H, W)Boolean. - output:
(B, Z * patch * patch * len(scales), H, W)Boolean.
Example
enc = PyramidEncoder(scales=(1, 2, 4), patch=3) planes = enc(x) # (B, Z93, H, W) model = SegmentationTsetlinMachine(4, 300, T=60, patch_size=1)
Source code in src/torchtsetlin/data/encoders.py
Flatten ¶
Compose ¶
Bases: BooleanEncoder
Chain encoders / callables. fit fits each stage on the output of the previous.
Source code in src/torchtsetlin/data/encoders.py
thermometer_thresholds ¶
thermometer_thresholds(x: Tensor, n_bits: int, strategy: str = 'quantile', value_range: Optional[Sequence[float]] = None) -> Tensor
Per-feature thresholds (F, n_bits) for thermometer encoding.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
|
required |
n_bits
|
int
|
thresholds per feature. |
required |
strategy
|
str
|
|
'quantile'
|
value_range
|
Optional[Sequence[float]]
|
optional global |
None
|
Source code in src/torchtsetlin/data/encoders.py
Graphs¶
torchtsetlin.data.graphs ¶
Graph-structured input for :class:~torchtsetlin.models.GraphTsetlinMachine.
A :class:GraphBatch holds a batch of directed multigraphs whose nodes carry Boolean feature
vectors and whose edges carry an integer type. Node features are usually sparse
hypervectors of symbols, built by a :class:SymbolTable (Halenka et al., 2024; Granmo et al.,
2025, arXiv:2507.14874), but any Boolean vector works: an image patch with its position bits
is a node, so a graph without edges is the input of a convolutional Tsetlin machine.
Storage is padded: features (B, P, F) with P the largest node count of the batch and a
(B, P) node mask. Edges are kept per graph, sorted by graph, with local node indices,
so that slicing a batch (batch[idx]) is cheap and keeps every edge inside its graph.
SymbolTable ¶
SymbolTable(symbols: Iterable, dim: int = 128, bits: int = 2, encoding: str = 'random', seed: int = 0)
Map symbol names to sparse hypervector positions (the node properties of a graph).
Every symbol owns bits positions out of dim. A node's feature vector is the OR
(bundle) of its symbols' positions. The three encodings follow the reference
GraphTsetlinMachine (Graphs in graphs.py):
"random":bitsdistinct random positions per symbol (seeded);"double_hashing": positionsi mod dimandp - (i mod p)withpthe largest prime belowdim(always 2 bits);"one_hot":dim = len(symbols)and symboliowns positioni. This is a plain Boolean feature per symbol, and it makes decoding exact.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbols
|
Iterable
|
the symbol names (any hashable values). |
required |
dim
|
int
|
hypervector size (ignored for |
128
|
bits
|
int
|
positions per symbol (ignored for |
2
|
encoding
|
str
|
|
'random'
|
seed
|
int
|
seed of the random positions. |
0
|
Source code in src/torchtsetlin/data/graphs.py
vector ¶
(dim,) Boolean hypervector of a set of symbols (their bundle).
Source code in src/torchtsetlin/data/graphs.py
encode ¶
(n, dim) Boolean feature matrix, one row per node's symbol set.
Source code in src/torchtsetlin/data/graphs.py
decode ¶
Symbols whose positions are all set in vector ((dim,) Boolean).
Source code in src/torchtsetlin/data/graphs.py
feature_names ¶
Names of the dim positions: the symbol for one-hot tables, else hv[k].
Source code in src/torchtsetlin/data/graphs.py
GraphBatch ¶
GraphBatch(node_features: Tensor, n_nodes: Tensor, edges: Optional[Tensor] = None, edge_type: Optional[Tensor] = None, edges_per_graph: Optional[Tensor] = None, node_type: Optional[Tensor] = None, n_edge_types: Optional[int] = None, n_node_types: Optional[int] = None, edge_type_names: Optional[Sequence[str]] = None)
A padded batch of directed multigraphs with typed edges.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_features
|
Tensor
|
|
required |
n_nodes
|
Tensor
|
|
required |
edges
|
Optional[Tensor]
|
|
None
|
edge_type
|
Optional[Tensor]
|
|
None
|
edges_per_graph
|
Optional[Tensor]
|
|
None
|
node_type
|
Optional[Tensor]
|
optional |
None
|
n_edge_types
|
Optional[int]
|
number of edge types of the dataset (kept by slices, so a batch that
happens to lack a type still declares it). Defaults to |
None
|
n_node_types
|
Optional[int]
|
number of node types of the dataset. Defaults to |
None
|
edge_type_names
|
Optional[Sequence[str]]
|
optional names of the edge types, for printing clauses. |
None
|
Messages travel along an edge from its source to its destination, so a node learns about the nodes that have an edge into it. Undirected relations need an edge each way, usually with two types (as in the sequence demo: "left" and "right").
Build batches with :meth:from_graphs, :meth:from_sequences or :meth:from_images;
index them like tensors along the graph axis (batch[3], batch[2:10],
batch[torch.tensor([4, 1])]) and concatenate them with :meth:cat.
Source code in src/torchtsetlin/data/graphs.py
cat
staticmethod
¶
Concatenate batches along the graph axis (padding to the largest node count).
Source code in src/torchtsetlin/data/graphs.py
flat_edges ¶
(src, dst, type) with node indices into the flattened B * P node axis.
Source code in src/torchtsetlin/data/graphs.py
from_graphs
classmethod
¶
from_graphs(node_features: Sequence, edges: Optional[Sequence] = None, edge_types: Optional[Sequence] = None, node_types: Optional[Sequence] = None, n_edge_types: Optional[int] = None, n_node_types: Optional[int] = None, edge_type_names: Optional[Sequence[str]] = None) -> GraphBatch
Build a batch from per-graph lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_features
|
Sequence
|
per graph, an |
required |
edges
|
Optional[Sequence]
|
per graph, an |
None
|
edge_types
|
Optional[Sequence]
|
per graph, an |
None
|
node_types
|
Optional[Sequence]
|
per graph, an |
None
|
Source code in src/torchtsetlin/data/graphs.py
from_sequences
classmethod
¶
from_sequences(sequences: Sequence[Sequence], symbols: SymbolTable, edge_types: Tuple[str, str] = ('left', 'right'), unknown: str = 'ignore') -> GraphBatch
One node per sequence element, chained in both directions.
Node k sends to k - 1 over edge type 0 (named edge_types[0]) and to
k + 1 over type 1, as in the reference SequenceClassificationDemo. An element
may be a single symbol or a set of symbols. Symbols missing from the table are
ignored (unknown="ignore") or rejected ("error").
Source code in src/torchtsetlin/data/graphs.py
from_images
classmethod
¶
from_images(images, patch_size: Union[int, Tuple[int, int]], stride: Union[int, Tuple[int, int]] = 1, position_encoding: bool = True) -> GraphBatch
One node per image patch and no edges (paper Sec. 3.2, "disconnected nodes").
Node features are the Z * kh * kw patch pixels followed by thermometer-coded
patch coordinates, exactly the patch features of
:class:~torchtsetlin.models.ConvTsetlinMachine with the same arguments. A depth-1
graph machine on these graphs is therefore the convolutional coalesced machine.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
images
|
Boolean |
required |
Source code in src/torchtsetlin/data/graphs.py
Datasets¶
torchtsetlin.data.datasets ¶
Ready-made Boolean datasets and helpers to wrap your own data.
BooleanTensorDataset ¶
Bases: TensorDataset
TensorDataset that coerces features to torch.bool and targets to long
(or float for regression). Keeps everything on one device for fast GPU epochs.
Source code in src/torchtsetlin/data/datasets.py
TransformDataset ¶
TransformDataset(base: Dataset, transform: Optional[Callable] = None, target_transform: Optional[Callable] = None)
Bases: Dataset
Apply a (booleanizing) transform lazily to the samples of another dataset.
Source code in src/torchtsetlin/data/datasets.py
to_boolean_tensors ¶
Coerce x to a Boolean tensor and y to a long tensor on device.
Source code in src/torchtsetlin/data/datasets.py
make_xor ¶
make_xor(n: int, n_features: int = 2, seed: Optional[int] = 0, device=None) -> Tuple[Tensor, Tensor]
Random Boolean vectors labelled by x0 XOR x1 (other features are distractors).
Source code in src/torchtsetlin/data/datasets.py
make_noisy_xor ¶
make_noisy_xor(n: int, n_features: int = 12, noise: float = 0.4, seed: Optional[int] = 0, device=None) -> Tuple[Tensor, Tensor]
The Noisy XOR benchmark of the original Tsetlin machine paper: 12 Boolean features
of which only the first two matter (y = x0 XOR x1); a fraction noise of the
training labels are flipped.
Source code in src/torchtsetlin/data/datasets.py
make_parity ¶
make_parity(n: int, n_bits: int = 4, n_features: int = 8, seed: Optional[int] = 0, device=None) -> Tuple[Tensor, Tensor]
Parity of the first n_bits features (harder than XOR).
Source code in src/torchtsetlin/data/datasets.py
make_2d_noisy_xor ¶
make_2d_noisy_xor(n: int, size: int = 4, noise: float = 0.0, seed: Optional[int] = 0, device=None, max_tries: int = 200) -> Tuple[Tensor, Tensor]
2D Noisy XOR of the Convolutional TM paper: a size x size binary image whose
pixels are random, except for one 2 x 2 patch (at a random position) that carries
the class pattern.
Class 1 patterns are the diagonals [[1,0],[0,1]] / [[0,1],[1,0]], class 0
patterns are the horizontal bars [[1,1],[0,0]] / [[0,0],[1,1]]. Backgrounds are
resampled so that no other window contains any of the four patterns (otherwise the
label would be ambiguous). A fraction noise of the labels is flipped.
Returns images (n, 1, size, size) and labels (n,).
Source code in src/torchtsetlin/data/datasets.py
make_shapes ¶
Toy image classification: a hollow 3x3 circle (class 0) or a 3x3 cross (class 1)
at a random position in a size x size image (Chapter 4 of the Tsetlin machine book).
Returns (n, 1, size, size) Boolean images and labels.
Source code in src/torchtsetlin/data/datasets.py
make_3d_shapes ¶
make_3d_shapes(n: int, size: Tuple[int, int, int] = (8, 12, 12), seed: Optional[int] = 0, device=None) -> Tuple[Tensor, Tensor]
Toy volume classification that needs depth context: a 3x3x3 shape at a random
position in a (D, H, W) volume.
Class 0 (spindle) stacks the 2D slices plus, dot, plus; class 1 (3D cross) stacks
dot, plus, dot. Every single slice therefore looks the same in both classes (a plus
or a dot) — only their order along depth tells them apart, which a 3D window with
kd = 3 sees in one clause.
Returns (n, 1, D, H, W) Boolean volumes and labels.
Source code in src/torchtsetlin/data/datasets.py
make_segmentation_shapes ¶
make_segmentation_shapes(n: int, size: int = 16, seed: Optional[int] = 0, device=None) -> Tuple[Tensor, Tensor]
Toy dense-prediction task: label every pixel background / disc / ring.
Two solid discs and two rings of radius 2-3 are stamped into a size x size Boolean
image. Disc interiors and ring interiors look identical on their own, so the classes are
only separable from a pixel's neighbourhood — which is exactly what a dense Tsetlin
machine gets to see, and what a per-pixel lookup cannot solve.
Returns (n, 1, size, size) Boolean images and (n, size, size) long label maps
with classes 0 = background, 1 = disc, 2 = ring.
Source code in src/torchtsetlin/data/datasets.py
make_scenes ¶
make_scenes(n: int, size: Union[int, Tuple[int, int]] = 32, seed: Optional[int] = 0, device: Optional[Union[str, device]] = None) -> Tuple[Tensor, Tensor]
Synthetic CamVid-like street scenes for semantic segmentation.
Generates the properties that make CamVid hard rather than the photographs themselves (which are not redistributable): a wavy horizon, a large contiguous sky, buildings standing on the horizon, trees, and a road, with per-class texture (window grids, foliage noise, lane markings) and a global illumination jitter so that the classes are not separable by colour alone. Class frequencies are heavily imbalanced, as in CamVid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
number of scenes. |
required |
size
|
Union[int, Tuple[int, int]]
|
|
32
|
seed
|
Optional[int]
|
RNG seed ( |
0
|
device
|
Optional[Union[str, device]]
|
device for the returned tensors. |
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
|
Tensor
|
long |
Tuple[Tensor, Tensor]
|
( |
Booleanize the images before handing them to a model, for example with
:class:~torchtsetlin.data.ColorThermometerEncoder, which keeps the ordering between
intensity levels that a plain threshold would throw away.
Source code in src/torchtsetlin/data/datasets.py
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | |
balanced_class_probabilities ¶
balanced_class_probabilities(labels: Tensor, n_classes: Optional[int] = None, ignore_index: Optional[int] = None, floor: float = 0.0) -> Tensor
Per-class feedback probabilities that equalise how often each class is learned from.
Dense targets are dominated by whichever class covers the most pixels, and a Tsetlin
machine has no loss to reweight — the only lever is which pixels produce feedback.
This returns p_k = min_j freq_j / freq_k (so the rarest class keeps probability 1),
ready to pass as class_feedback_p to
:class:~torchtsetlin.models.SegmentationTsetlinMachine.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
labels
|
Tensor
|
any tensor of integer labels (a label map or a flat vector). |
required |
n_classes
|
Optional[int]
|
number of classes (inferred from the maximum label if omitted). |
None
|
ignore_index
|
Optional[int]
|
label to exclude from the frequency count. |
None
|
floor
|
float
|
lower bound on the returned probabilities; raising it (e.g. |
0.0
|
Source code in src/torchtsetlin/data/datasets.py
load_torchvision_boolean ¶
load_torchvision_boolean(dataset_cls, root: str, train: bool, encoder: Optional[Callable] = None, threshold: float = 0.3, device=None, download: bool = True, max_samples: Optional[int] = None, **kwargs) -> Tuple[Tensor, Tensor]
Load a torchvision dataset fully into memory and booleanize it.
encoder receives a float tensor (N, C, H, W) scaled to [0, 1] and must return
a Boolean tensor. By default pixels are thresholded at threshold (0.3, as in the
original MNIST experiments). Returns (x_bool, y_long) on device.
Source code in src/torchtsetlin/data/datasets.py
load_mnist_boolean ¶
load_mnist_boolean(root: str = './data', train: bool = True, threshold: float = 0.3, encoder: Optional[Callable] = None, device=None, flatten: bool = False, max_samples: Optional[int] = None) -> Tuple[Tensor, Tensor]
MNIST as Boolean tensors (N, 1, 28, 28) (or (N, 784) with flatten=True).
Requires torchvision.
Source code in src/torchtsetlin/data/datasets.py
load_segmentation_boolean ¶
load_segmentation_boolean(dataset: Dataset, encoder: Optional[Callable] = None, size: Optional[Union[int, Tuple[int, int]]] = None, n_bits: int = 4, device: Optional[Union[str, device]] = None, max_samples: Optional[int] = None, ignore_index: int = 255, remap: Optional[dict] = None) -> Tuple[Tensor, Tensor]
Load a torchvision segmentation dataset into memory as Boolean planes + label maps.
Segmentation datasets yield (image, mask) PIL pairs rather than the packed tensors
:func:load_torchvision_boolean expects, and the mask must be resized with nearest
interpolation — bilinear would invent class indices that do not exist. This handles both.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataset
|
Dataset
|
an instantiated torchvision segmentation dataset, e.g.
|
required |
encoder
|
Optional[Callable]
|
maps a float image batch |
None
|
size
|
Optional[Union[int, Tuple[int, int]]]
|
|
None
|
n_bits
|
int
|
thermometer levels per channel for the default encoder. |
4
|
device
|
Optional[Union[str, device]]
|
device for the returned tensors. |
None
|
max_samples
|
Optional[int]
|
stop after this many images. |
None
|
ignore_index
|
int
|
mask value to keep as the void class. Pass the same value as the
model's |
255
|
remap
|
Optional[dict]
|
optional |
None
|
Returns:
| Type | Description |
|---|---|
Tuple[Tensor, Tensor]
|
|
Source code in src/torchtsetlin/data/datasets.py
load_voc_segmentation_boolean ¶
load_voc_segmentation_boolean(root: str = './data', image_set: str = 'train', size: Union[int, Tuple[int, int]] = 96, n_bits: int = 4, device=None, download: bool = True, max_samples: Optional[int] = None, **kwargs) -> Tuple[Tensor, Tensor]
Pascal VOC 2012 segmentation as Boolean planes + label maps (21 classes + void).
Void pixels keep the value 255, so pass ignore_index=255 to the model. Requires
torchvision.