BlackBox2C
Convert scikit-learn models to native embedded code — C, C++, Arduino, MicroPython
BlackBox2C converts any trained scikit-learn model into a minimal if-else decision tree in your target language. The generated code has zero runtime dependencies, runs on any microcontroller with a C compiler, and fits in a few hundred bytes of FLASH.
How It Works
- Surrogate extraction — A lightweight
DecisionTreeis trained to mimic any black-box model (Random Forest, SVM, MLP, etc.) by generating synthetic boundary samples and labeling them with the original model's predictions. - Rule optimization — Redundant branches are pruned and similar leaves are merged to minimize code size.
- Code generation — The optimized tree is serialized as a pure if-else function in the target language.
Surrogate fidelity: DecisionTree inputs are exported directly. Other estimators are approximated by the surrogate tree above, so generated predictions may differ from the original model. Supply
X_testto measure fidelity on held-out data; without it, BlackBox2C warns that fidelity was measured onX_train. Setfidelity_warning_threshold=Noneonly when you explicitly want to disable low-fidelity warnings.
Supported Models and Targets
| Input models | Output formats |
|---|---|
Any scikit-learn estimator with predict() |
Pure C (C99) |
| Decision Tree, Random Forest, SVM, MLP... | C++11 (class + namespace) |
| Classification and Regression tasks | Arduino (.h with PROGMEM) |
MicroPython (.py module) |
Installation
pip install blackbox2c
Requirements: Python 3.8+, NumPy >= 1.21, scikit-learn >= 1.0.
Tip: Use a virtual environment to keep your project isolated:
# python -m venv .venv && source .venv/bin/activate # Linux/macOS
python -m venv .venv && .venv\Scripts\activate # Windows
pip install blackbox2c
For development (from source):
git clone https://github.com/AxelSkrauba/BlackBox2C.git
cd BlackBox2C
pip install -e ".[dev]"
Quick Start
Classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from blackbox2c import convert
iris = load_iris()
model = RandomForestClassifier(n_estimators=50, random_state=42)
model.fit(iris.data, iris.target)
# Convert to C (default target)
c_code = convert(
model,
iris.data,
feature_names=list(iris.feature_names),
class_names=list(iris.target_names),
max_depth=5,
)
print(c_code)
Generated output:
/*
* Auto-generated C code by BlackBox2C
* - Input features: 4
* - Output classes: 3
* - Precision: 8-bit
*/
#include <stdint.h>
#define setosa 0
#define versicolor 1
#define virginica 2
uint8_t predict(float features[4]) {
if (features[2] <= 2.449999f) {
return 0;
} else {
if (features[3] <= 1.750000f) {
return 1;
} else {
return 2;
}
}
}
Export to Other Formats
# Arduino .ino file
arduino_code = convert(model, iris.data, target='arduino')
# C++ class
cpp_code = convert(model, iris.data, target='cpp')
# MicroPython module
mp_code = convert(model, iris.data, target='micropython')
Regression
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.datasets import load_diabetes
from blackbox2c import convert
data = load_diabetes()
model = GradientBoostingRegressor(random_state=42)
model.fit(data.data, data.target)
c_code = convert(model, data.data, max_depth=5)
# Generates: float predict(float features[10]) { ... }
Feature Analysis
from blackbox2c.analysis import FeatureSensitivityAnalyzer
analyzer = FeatureSensitivityAnalyzer(n_repeats=10, random_state=42)
results = analyzer.analyze(model, X_train, y_train, feature_names=feature_names)
print(results.summary())
# Get top 3 most important features by index
top3 = results.get_top_features(3)
Configuration
from blackbox2c import Converter, ConversionConfig
config = ConversionConfig(
max_depth=5, # Surrogate tree depth (1-10, default 5)
optimize_rules='medium', # 'low' | 'medium' | 'high' | 'qm' | 'bdd' | 'auto'
qm_max_literals=12, # Hard cap on literals processed by QM (v0.2+)
bdd_max_literals=24, # Hard cap on literals processed by BDD (v0.2+)
max_bridge_nodes=4096, # Safe cap for advanced C reconstruction
use_fixed_point=False, # Use integer arithmetic instead of float
precision=8, # Bit width for fixed-point: 8 | 16 | 32
function_name='predict', # Name of the generated function
n_samples=10000, # Synthetic samples for surrogate training
fidelity_warning_threshold=0.95, # Warn below this surrogate fidelity
feature_threshold=None, # Auto-select N most important features
memory_budget_kb=None, # Tune small budgets and validate estimated FLASH
)
converter = Converter(config)
code = converter.convert(model, X_train, target='arduino')
metrics = converter.get_metrics()
# {'fidelity': 0.97, 'complexity': {...}, 'size_estimate': {...}}
memory_budget_kb is checked against the generated code's estimated FLASH
usage after conversion. It remains a heuristic: verify the compiler output on
your target MCU before treating it as a hardware capacity guarantee.
Rule optimization levels
| Level | Algorithm | Notes |
|---|---|---|
low |
No post-processing. | Returns the surrogate tree as-is. |
medium |
Prune internal nodes whose direct children are same-class leaves. | Default. Backward-compatible with v0.1. |
high |
medium + merge sibling leaves with very similar class distributions. |
Backward-compatible with v0.1. |
qm |
Multi-valued Quine-McCluskey boolean minimisation lifted to continuous splits. (v0.2) | Classification only. Capped by qm_max_literals. |
bdd |
Frequency-ordered Reduced Ordered BDD rebuilt as a tree. (v0.2) | Classification only. Capped by bdd_max_literals. |
auto |
Runs every applicable optimiser plus the no-op baseline; returns the smallest. (v0.2) | Falls back safely if caps or bridge limits prevent reconstruction. |
Advanced levels (qm, bdd, auto) preserve 100 % functional
equivalence with the surrogate tree (verified by the test suite).
On regression they emit a single UserWarning and fall back to the
legacy 'high' path. If a literal cap or max_bridge_nodes prevents
safe advanced reconstruction, conversion falls back to the legacy tree
path without changing predictions. Best-case savings on the benchmark
(benchmarks/results/v0.2.md): −47 % FLASH on Iris +
RandomForest. See notebooks/07_advanced_optimization.ipynb
for a guided walkthrough.
CLI
# Convert a pickled model to C
blackbox2c convert model.pkl X_train.npy -o output.c
# Export to Arduino
blackbox2c convert model.pkl X_train.npy -t arduino -o predict.h
# Analyze feature importance
blackbox2c analyze model.pkl X_train.npy --top-n 5
# Export a decision tree directly (no surrogate extraction)
blackbox2c export model.pkl -f cpp -o predictor.hpp
# Help
blackbox2c --help
blackbox2c convert --help
Benchmarks
python benchmarks/benchmark_classic_datasets.py --output results.md
Covers Iris, Wine, Diabetes, and California Housing with Decision Trees, Random Forests, SVMs, and Neural Networks. Metrics: fidelity, estimated FLASH size, tree depth, conversion time.
Note: Code size figures are estimates from BlackBox2C's built-in size estimator, not measurements on real hardware.
Project Structure
blackbox2c/
├── blackbox2c/
│ ├── __init__.py # Public API: convert(), Converter, ConversionConfig
│ ├── converter.py # Main orchestration pipeline
│ ├── config.py # ConversionConfig dataclass
│ ├── surrogate.py # Surrogate tree extraction
│ ├── codegen.py # C code generation
│ ├── optimizer.py # Rule pruning and merging
│ ├── exporters.py # C++, Arduino, MicroPython exporters
│ ├── analysis.py # Feature sensitivity analysis
│ └── cli.py # Command-line interface
├── tests/ # 182 tests, >91% coverage
├── notebooks/ # Jupyter notebook examples (runnable on Colab)
├── benchmarks/ # Classic dataset benchmarks
├── examples/ # Script-based end-to-end examples
└── docs/ # MkDocs documentation source
Comparison with Alternatives
| Feature | BlackBox2C | emlearn | MicroMLGen | TFLite Micro |
|---|---|---|---|---|
| Any sklearn model | ✅ | ⚠️ Trees only | ⚠️ Trees only | ❌ TF only |
| Pure if-else output | ✅ | ✅ | ✅ | ❌ |
| C++ / Arduino / MicroPython | ✅ | ⚠️ Partial | ❌ | ⚠️ Partial |
| Feature selection built-in | ✅ | ❌ | ❌ | ❌ |
| Memory budget control | ✅ | ❌ | ❌ | ⚠️ |
| Zero runtime dependencies | ✅ | ✅ | ✅ | ❌ |
Roadmap (v0.2)
- Quine-McCluskey and BDD rule optimization
- Hardware-validated benchmarks on real MCUs
- Quantization-aware training integration
License
MIT — see LICENSE.
Contributing
Issues and PRs welcome at github.com/AxelSkrauba/BlackBox2C.
Metadata
Release files for blackbox2c 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| blackbox2c-0.2.3.tar.gz | 106.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| blackbox2c-0.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 166.3 kB
Release files / blackbox2c-0.2.3.tar.gz
| Download URL | blackbox2c-0.2.3.tar.gz |
|---|---|
| Size | 106.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c2b53201e7933ba0e16f1402372f8f97cfc0743e2301b5dd072d3b533843d315
|
|
BLAKE2b-256 checksum How to use checksums |
48d5099ed26bce56086e6fe05572d0a53a235446a2658443065f30456484bcaf
|
| 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 Aug 18, 2026.
Transparency logRelease files / blackbox2c-0.2.3-py3-none-any.whl
| Download URL | blackbox2c-0.2.3-py3-none-any.whl |
|---|---|
| Size | 60.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bf20b1314e0eca696a5be8ddd2755bbef4953a79b1df63947a9df0be06ddb53e
|
|
BLAKE2b-256 checksum How to use checksums |
b0056bff9ddbcb23243aed7a5f45c77ae9a6c5843de35569be3799e4b364c3f8
|
| 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 Aug 18, 2026.
Transparency log