OPForch: PyTorch Optimum-Path Forest Classifiers
OPForch implements Optimum-Path Forest classifiers with PyTorch tensors for CPU and CUDA execution. It is a tensor-first successor to OPFython and retains the familiar OPF training, prediction, persistence, distance, streaming, and conversion APIs.
Installation
OPForch requires Python 3.12 or newer and PyTorch 2.0 or newer.
pip install opforch
For development:
git clone https://github.com/gugarosa/opforch.git
cd opforch
uv sync --locked
Quick start
This example works with an installed package and does not require repository data files.
import torch
from opforch.models import SupervisedOPF
X = torch.tensor([[0.0, 0.0], [0.1, 0.2], [1.0, 1.0], [1.1, 1.2]])
Y = torch.tensor([0, 0, 1, 1])
queries = torch.tensor([[0.05, 0.1], [1.05, 1.1]])
model = SupervisedOPF(distance="euclidean", device="cpu")
model.fit(X, Y)
predictions = model.predict(queries)
print(predictions)
Select a CUDA device through the existing device argument:
model = SupervisedOPF(distance="euclidean", device="cuda:0")
Classifiers
| Classifier | Description |
|---|---|
SupervisedOPF |
MST-based prototype detection and cost competition |
KNNSupervisedOPF |
k-NN density clustering with validation-driven k selection |
SemiSupervisedOPF |
Labeled and unlabeled sample propagation |
UnsupervisedOPF |
Density clustering with normalized-cut selection |
All classifiers retain fit(), predict(), save(), and load() support.
The package also includes 47 distance metrics, OPF data loaders and
converters, train/test split helpers, tensor-backed subgraphs, and the public
DeviceManager API.
Only load model checkpoints from trusted sources: whole-model load() uses
Python object serialization. save() does not relocate the live model;
load() restores state onto the receiving model's requested device.
SupervisedOPF.learn() and prune() are supervised-only utilities.
Semi-supervised training uses fit(X_train, Y_train, X_unlabeled); the inherited
supervised utilities are not supported semi-supervised training APIs.
See examples/applications for complete classifier
workflows.
Architecture
Classifiers own training and prediction workflows. Subgraph owns aligned
sample and per-node tensor state, while KNNSubgraph adds neighbour arcs and
density operations. Distance functions remain plain callables in the mathematical
registry. Streaming and conversion code own file formats and sample partitioning.
This separation keeps algorithm-specific decisions in the classifiers without duplicating the graph's state layout or adding framework-specific model layers.
Pre-computed distances
A distance file contains a square matrix for the complete dataset. When splitting or reordering samples, pass their original matrix row positions so training and prediction select the correct rows and columns:
from opforch.math.general import pre_compute_distance
from opforch.stream import splitter
pre_compute_distance(X, "distances.pt", distance="euclidean")
X_train, X_test, Y_train, Y_test, I_train, I_test = splitter.split_with_index(X, Y)
model = SupervisedOPF(distance="euclidean", pre_computed_distance="distances.pt")
model.fit(X_train, Y_train, I_train=I_train)
predictions = model.predict(X_test, I_val=I_test)
The same indexing applies to all four classifiers and the KNNSubgraph
distance wrappers. Indices must be non-negative integers within the matrix.
.pt and .pth distance files use PyTorch's restricted tensor loader, not
whole-model deserialization.
Without explicit indices, each input is assumed to start at matrix position
zero; sample IDs are not inferred from feature values.
SemiSupervisedOPF.fit() accepts I_unlabeled for the unlabeled samples.
Its default is the positions immediately following the labeled samples.
SupervisedOPF.learn() and prune() accept I_train and I_val; indices
follow samples when they are exchanged or removed.
Data and numerical contracts
Features have shape (n_samples, n_features), with one label and, when
provided, one index per sample. Supervised and semi-supervised training
support a single labeled class. A k-NN training graph requires
1 <= k < n_samples, excluding self-neighbours.
Training tensors or NumPy arrays can share storage with a fitted graph when no conversion is needed. Do not mutate those inputs after fitting. Learning clones its input partitions before exchanging samples.
Supervised prediction marks its winning nodes and their predecessor paths as relevant. Pruning keeps these paths and class prototypes; it is not a guarantee of unchanged accuracy on other data.
Label metrics require equally sized, non-empty vectors of non-negative integer class IDs. Classes are numbered from zero; validation subsets may omit classes. Confusion matrices include classes found only in predictions, and purity supports more clusters than ground-truth classes. OPF accuracy retains its false-positive/false-negative normalization, treating undefined error-rate terms as zero. It is not ordinary fraction-correct accuracy.
Z-score normalization maps constant columns to zero. Min-max normalization
through get_distances(normalize=True) likewise returns zeros for a
constant distance matrix.
Development
Follow CONVENTIONS.md for code style, Google-style docstrings, error messages, and the standards used for reviews.
uv run pytest
uv run pre-commit run --all-files
uv build
Citation
If you use OPForch, please also cite the original LibOPF authors:
J. P. Papa, A. X. Falcão and C. T. N. Suzuki.
Supervised Pattern Classification based on Optimum-Path Forest.
International Journal of Imaging Systems and Technology (2009).
Support
Open an issue for bug reports, questions, and suggestions.
Metadata
Release files for opforch 3.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| opforch-3.0.0.tar.gz | 44.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| opforch-3.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 96.0 kB
Release files / opforch-3.0.0.tar.gz
| Download URL | opforch-3.0.0.tar.gz |
|---|---|
| Size | 44.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dcbe1252e012b848d281f4e1b6b9f4554b9378615e45d7e1ef355a0e8f9731ec
|
|
BLAKE2b-256 checksum How to use checksums |
7124ea1bf0815eb5d90dc7d538a871a267c5d5425351e00c1ed10f78243ab95f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logRelease files / opforch-3.0.0-py3-none-any.whl
| Download URL | opforch-3.0.0-py3-none-any.whl |
|---|---|
| Size | 51.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
430a2d6d584df9a1d93cb37ec419e3c2094a6d44d6c33ae0e8bfbb81db44d054
|
|
BLAKE2b-256 checksum How to use checksums |
487ede87f2230d5ad15aba35494273495fa0b126c5d072341ce4045fc5ecd9ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency log