Skip to main content

Python API for the OPF (Optimum-Path Forest) library (C++/pybind11 backend)

Project description

opfppy — OPF Pretty Python

opfppy (OPF Pretty PYthon) is a high-level Python API for the Optimum-Path Forest classifier/clusterer, backed by a C++20/pybind11 extension (opfpy).


Python Layer Naming Convention

This project has three Python-facing layers, each with a distinct purpose:

Layer Package / module Full name Purpose
C++ extension opfpy OPF Python Raw pybind11 bindings — thin bridge to C++; no Python utilities
Cython wrapper opfpy_cython OPF Python (Cython) Typed Cython classes over opfpy; low-level, no high-level helpers
Python shim opfppy OPF Pretty PYthon Full Python API: pretty repr, DistanceMetric enum, high-level workflows

What opfppy adds over the raw opfpy / opfpy_cython layers

The opfpy and opfpy_cython layers expose only what the C++ binding provides directly. opfppy adds:

Feature opfpy / opfpy_cython opfppy
Pretty __repr__ for Node, Subgraph, OPF ❌ raw <opfpy.Node object …> ✅ human-readable, head/tail truncation
DistanceMetric enum (EUCLIDEAN, MANHATTAN, …) ❌ integer ids only ✅ named constants + string resolver
resolve_distance(name/enum/int) "manhattan", "L1", DistanceMetric.MANHATTAN, 3 all accepted
register_distance(name, id) ✅ extension point for custom metrics
High-level workflow functions train_and_classify, learn_and_classify, cluster_and_propagate, semi_supervised, …
Utility helpers load, split, merge, normalize, accuracy, info, k_fold, compute_distance_matrix, write/read_distance_matrix
Windows DLL setup ❌ (must be done manually) ✅ automatic on import opfppy
Single bootstrap import ❌ requires sys.path manipulation import opfppy is enough

Requirements

Tool Version
Python ≥ 3.11
C++ compiler GCC/Clang with C++20 (MSYS2 UCRT64 on Windows)
CMake ≥ 3.16
Conan 2.x
plyfile ≥ 1.0

Building the Extension

# From the project root, inside the MSYS2 UCRT64 shell:
bash ./build_opfpy.sh

The compiled module (opfpy.pyd / opfpy.so) is placed in pythonlib/bin/.
See build_opfpy.md for step-by-step details.


Quick Start

import opfppy                                     # single import — DLL setup done automatically

data = opfppy.Subgraph.from_original_file("../data/boat.dat")
train_sg, test_sg = opfppy.split_subgraph(data, 0.5)

from opfppy.supervised import train_and_classify
acc = train_and_classify(train_sg, test_sg)
print(f"Accuracy: {acc:.2%}")

Package Layout

opfppy/
  __init__.py          # package root; bootstrap (sys.path, DLL dirs), re-exports
  node.py              # Node shim class — pretty repr, wrap(), register()
  subgraph.py          # Subgraph shim class — pretty repr, factory class-methods
  opf_class.py         # OPF shim class — pretty repr, wrap(), register()
  distance.py          # DistanceMetric enum, resolve(), register()
  ply_adapter.py       # SplatSubGraph class, encode/decode SH, PLY loading
  utils.py             # I/O, split/merge/normalize, accuracy, distance matrix
  supervised.py        # train, classify, learn, prune helpers
  unsupervised.py      # cluster, knn_classify, semi_supervised helpers
examples/
  example1_supervised.py            # supervised OPF, no learning
  example2_learning.py              # supervised OPF with learning
  example3_precomputed_distances.py # distance matrix I/O + supervised OPF
  example4_normalization.py         # feature normalization + supervised OPF
  example5_unsupervised.py          # unsupervised clustering + k-NN classify
  example6_semi_supervised.py       # semi-supervised OPF
  example7_ply_subgraph.py          # basic PLY → Subgraph conversion
  example8_splat_subgraph.py        # advanced PLY → SplatSubGraph with metadata

API Reference

opfppy top-level (shim classes)

Symbol Description
Node Shim over opfpy.Node — inherits all C++ properties + pretty __repr__
Subgraph Shim over opfpy.Subgraph — factory class-methods return opfppy.Subgraph
OPF Shim over opfpy.OPF — inherits all C++ methods + pretty __repr__
DistanceMetric IntEnum: EUCLIDEAN=1 … BRAY_CURTIS=7
resolve_distance(x) Resolve string / enum / int → integer id
register_distance(name, id) Register a custom metric name

All opfpy free functions are also re-exported directly from opfppy: split_subgraph, read_subgraph, write_subgraph, propagate_cluster_labels, eucl_dist, chi_squared_dist, manhattan_dist, canberra_dist, squared_chord_dist, squared_chi_squared_dist, bray_curtis_dist, subgraph_info, k_fold, merge_subgraphs, compute_distance_matrix, write_distance_matrix, hello.


opfppy.distance

Symbol Description
DistanceMetric IntEnum with named constants for all 7 built-in metrics
resolve(x) Accept int, str (case-insensitive, aliases: l1, l2, chi2, bray, …), or DistanceMetricint
register(name, id) Add a custom metric string alias

opfppy.ply_adapter

Symbol Description
SplatSubGraph Subclass of Subgraph with persistent PLY metadata + import/export
from_ply_file(path, profile) Load Gaussian-splat PLY and return (Subgraph, metadata) dict
encode_sh_params(l, m) Pack SH (l, m) into one byte using offset convention
decode_sh_params(byte) Unpack byte back into (l, m)

SH Encoding scheme (3 bits degree + 5 bits index with offset):

  • Packing: byte = (l << 5) | ((m + 16) & 0x1F)
  • Unpacking: l = (byte >> 5) & 0x07, m = (byte & 0x1F) - 16
  • Valid range: l ∈ [0, 7], m ∈ [-16, 15]
  • Examples: f_dc_0 → 16 (00010000), f_rest_44 → 115 (01110011)

Profile options:

  • 'full': All 62 Gaussian properties (x, y, z, nx, ny, nz, f_dc_, f_rest_, opacity, scale_, rot_)
  • 'compact': 14 essential properties (geometry + DC color + opacity + scale + rotation)

Benchmark results (1000 vertices):

Metric Full Compact Savings
Load time 98 ms 55 ms 44% faster
Features per node 62 14 77.4% reduction
Access speed (500 nodes) 4.23 ms 2.90 ms 1.46× faster

opfppy.utils

Function Description
load(path) Load subgraph from LibOPF .dat file (original format)
read(path) Read subgraph from OPF training binary format
write(path, sg) Write subgraph to OPF training binary format
split(sg, pct) Label-stratified split into two subgraphs
merge(sg1, sg2) Merge two subgraphs
k_fold(sg, k) Stratified k-fold partition
normalize(sg) Z-score feature normalization in-place
accuracy(sg) Accuracy from label vs truelabel
info(sg) Dict with nnodes, nlabels, nfeats
load_ply(path, feature_profile) Load Gaussian-splat PLY into (Subgraph, metadata)
compute_distance_matrix(sg, distance) Pairwise distance matrix — accepts int / str / DistanceMetric
write_distance_matrix(mat, path) Write binary distance matrix
read_distance_matrix(path) Read binary distance matrix

Distance IDs / names: 1/"euclidean" · 2/"chi_squared" · 3/"manhattan" · 4/"canberra" · 5/"squared_chord" · 6/"squared_chi_squared" · 7/"bray_curtis".
Aliases: l1 → manhattan, l2 → euclidean, chi2 → chi_squared, bray → bray_curtis, etc.


opfppy.supervised

Function Description
train(sg_train) Train supervised OPF in-place
classify(sg_train, sg_test) Classify test nodes in-place
train_and_classify(sg_train, sg_test) Train + classify; returns accuracy
learn_and_classify(train, eval, test, n_iter) Iterative learning + classify; returns accuracy
prune(sg_train, sg_eval, tolerance) Iterative pruning; returns pruning rate

opfppy.unsupervised

Function Description
cluster_and_propagate(sg, k) Cluster in-place and propagate labels
create_arcs(sg, k) Build k-NN adjacency + node radius in native C++ (opf_CreateArcs)
compute_pdf(sg) Compute Gaussian PDF density on a subgraph with adjacency set (opf_PDF)
bestk_min_cut(sg, kmin, kmax) Run native best-k selection (opf_BestkMinCut) then prepare arcs/PDF
knn_classify(sg_train, sg_test) k-NN classify using stored radii
semi_supervised(labeled, unlabeled, eval?) Semi-supervised learning; returns merged graph

Low-level C++ extension (opfpy) and Cython wrapper (opfpy_cython)

opfpy is the raw pybind11 binding — the C++ extension module. It exposes Node, Subgraph, and OPF as plain C-extension types with no Python utilities. opfpy_cython wraps these in typed Cython classes.

Neither layer provides:

  • pretty __repr__ (objects print as <opfpy.Node object at 0x…>)
  • DistanceMetric enum or string distance resolution
  • resolve_distance / register_distance
  • workflow helpers (train_and_classify, normalize, accuracy, etc.)
  • Windows DLL directory setup
  • single-import bootstrap (sys.path must be set manually)

Use opfppy for all application code. Access opfppy._opfpy only when you need the raw C-extension objects for performance-critical inner loops.

Symbol Type Description
Node class Single graph node with all OPF fields
Subgraph class Container of nodes + graph metadata
OPF class Classifier / learner methods (train, classify, learn, create_arcs, destroy_arcs, bestk_min_cut, compute_pdf, cluster, knn_classify, semi_supervised, normalize, accuracy, pruning)
read_subgraph / write_subgraph functions Binary I/O
split_subgraph function Stratified split
propagate_cluster_labels function Label propagation
eucl_dist, chi_squared_dist, … functions Distance metrics (integer id only)
subgraph_info function Metadata dict
k_fold function k-fold partition
merge_subgraphs function Merge
compute_distance_matrix function Pairwise distances (integer id only)
write_distance_matrix / read_distance_matrix functions Distance matrix I/O

Running the Tests

cd pythonlib
# activate .venv first
python -m unittest discover -v

Expected: 80 tests, OK across Phases 1–6 plus PLY adapter tests.


Running the Examples

cd pythonlib
python examples/example1_supervised.py
python examples/example2_learning.py
python examples/example3_precomputed_distances.py
python examples/example4_normalization.py
python examples/example5_unsupervised.py
python examples/example6_semi_supervised.py
python examples/example7_ply_subgraph.py ../../tools/bridge-server/sample.ply full
python examples/example8_splat_subgraph.py ../../tools/bridge-server/sample.ply

Running the Benchmarks

cd pythonlib
# activate .venv first
python -m unittest test_ply_benchmark.TestPlyBenchmark -v

Expected output: Performance comparison of full vs compact profiles on synthetic 1000-vertex PLY.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

opfppy-0.6.0-cp312-cp312-manylinux_2_35_x86_64.whl (362.7 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.35+ x86-64

File details

Details for the file opfppy-0.6.0-cp312-cp312-manylinux_2_35_x86_64.whl.

File metadata

File hashes

Hashes for opfppy-0.6.0-cp312-cp312-manylinux_2_35_x86_64.whl
Algorithm Hash digest
SHA256 27698bbd6bcdde2b336520ab759223933f3b97a9c0f54a7dd3d4c0d35e1558f6
MD5 f21918177e63b323214cacc1b415287e
BLAKE2b-256 c8d9fc0a29083cb7a20b5668ce3dfb826588fb4767dbbd7b8d586ba58eebe680

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page