blackbox2c 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- blackbox2c-0.1.0/CHANGELOG.md +80 -0
- blackbox2c-0.1.0/LICENSE +21 -0
- blackbox2c-0.1.0/MANIFEST.in +8 -0
- blackbox2c-0.1.0/PKG-INFO +298 -0
- blackbox2c-0.1.0/README.md +257 -0
- blackbox2c-0.1.0/blackbox2c/__init__.py +91 -0
- blackbox2c-0.1.0/blackbox2c/analysis.py +446 -0
- blackbox2c-0.1.0/blackbox2c/cli.py +302 -0
- blackbox2c-0.1.0/blackbox2c/codegen.py +318 -0
- blackbox2c-0.1.0/blackbox2c/config.py +111 -0
- blackbox2c-0.1.0/blackbox2c/converter.py +350 -0
- blackbox2c-0.1.0/blackbox2c/exporters.py +755 -0
- blackbox2c-0.1.0/blackbox2c/optimizer.py +245 -0
- blackbox2c-0.1.0/blackbox2c/surrogate.py +217 -0
- blackbox2c-0.1.0/blackbox2c.egg-info/PKG-INFO +298 -0
- blackbox2c-0.1.0/blackbox2c.egg-info/SOURCES.txt +37 -0
- blackbox2c-0.1.0/blackbox2c.egg-info/dependency_links.txt +1 -0
- blackbox2c-0.1.0/blackbox2c.egg-info/entry_points.txt +2 -0
- blackbox2c-0.1.0/blackbox2c.egg-info/requires.txt +14 -0
- blackbox2c-0.1.0/blackbox2c.egg-info/top_level.txt +1 -0
- blackbox2c-0.1.0/examples/feature_selection_example.py +261 -0
- blackbox2c-0.1.0/examples/iris_example.py +254 -0
- blackbox2c-0.1.0/examples/multi_format_export.py +359 -0
- blackbox2c-0.1.0/examples/temperature_regression.py +359 -0
- blackbox2c-0.1.0/pyproject.toml +110 -0
- blackbox2c-0.1.0/requirements.txt +2 -0
- blackbox2c-0.1.0/setup.cfg +4 -0
- blackbox2c-0.1.0/tests/__init__.py +3 -0
- blackbox2c-0.1.0/tests/test_analysis.py +301 -0
- blackbox2c-0.1.0/tests/test_cli.py +238 -0
- blackbox2c-0.1.0/tests/test_codegen.py +164 -0
- blackbox2c-0.1.0/tests/test_config.py +82 -0
- blackbox2c-0.1.0/tests/test_converter.py +238 -0
- blackbox2c-0.1.0/tests/test_exporters.py +648 -0
- blackbox2c-0.1.0/tests/test_optimizer.py +135 -0
- blackbox2c-0.1.0/tests/test_regression.py +394 -0
- blackbox2c-0.1.0/tests/test_reproducibility.py +151 -0
- blackbox2c-0.1.0/tests/test_surrogate.py +135 -0
- blackbox2c-0.1.0/tests/test_target_param.py +239 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to BlackBox2C will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-04-15
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **`pyproject.toml`**: `project.dependencies` was incorrectly formatted as a TOML table instead of an array of PEP 508 strings, preventing installation (`pip install` failed with `configuration error`).
|
|
12
|
+
- **Regression detection**: `SurrogateExtractor` and `Converter` used a fragile `_estimator_type` instance-attribute check that failed for ensemble regressors (`RandomForestRegressor`, `GradientBoostingRegressor`) in scikit-learn ≥ 1.8. Replaced with `sklearn.base.is_regressor()` with a safe fallback for non-conformant mock models.
|
|
13
|
+
- **Build system**: Removed `setuptools_scm` from build dependencies (was unused; no git tag versioning configured) and dropped legacy `setup.py` that duplicated `pyproject.toml` metadata.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Promoted from beta (`0.1.0b1`) to stable (`0.1.0`).
|
|
17
|
+
- Development Status classifier updated to `5 - Production/Stable`.
|
|
18
|
+
- Added `[tool.setuptools.packages.find]` section to `pyproject.toml` for explicit package discovery.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## [0.1.0b1] - 2025-07-01
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- **Multi-format export**: Support for C, C++11, Arduino, and MicroPython
|
|
26
|
+
- **Regression support**: Full support for regression models (DecisionTreeRegressor, RandomForestRegressor, GradientBoostingRegressor)
|
|
27
|
+
- **Feature analysis**: Automatic feature importance analysis with `FeatureSensitivityAnalyzer`
|
|
28
|
+
- **Factory pattern**: Easy exporter creation with `create_exporter()`
|
|
29
|
+
- **Comprehensive tests**: 128 tests covering all functionality
|
|
30
|
+
- **Complete examples**: 4 full examples demonstrating all features
|
|
31
|
+
|
|
32
|
+
### Features
|
|
33
|
+
- Convert scikit-learn models to optimized embedded code
|
|
34
|
+
- Support for classification and regression tasks
|
|
35
|
+
- Automatic task detection (classification vs regression)
|
|
36
|
+
- Surrogate model extraction for complex models (SVM, Neural Networks)
|
|
37
|
+
- Rule optimization with configurable levels (low, medium, high)
|
|
38
|
+
- Fixed-point arithmetic support for resource-constrained devices
|
|
39
|
+
- Memory budget management
|
|
40
|
+
- Code size estimation
|
|
41
|
+
- Fidelity calculation
|
|
42
|
+
|
|
43
|
+
### Exporters
|
|
44
|
+
- **C Exporter**: Standard C code with if-else logic
|
|
45
|
+
- **C++ Exporter**: Modern C++11 with classes, templates, and STL
|
|
46
|
+
- **Arduino Exporter**: Optimized for Arduino boards with PROGMEM support
|
|
47
|
+
- **MicroPython Exporter**: Pure Python for microcontrollers
|
|
48
|
+
|
|
49
|
+
### Examples
|
|
50
|
+
- `iris_example.py`: Classification with multiple models
|
|
51
|
+
- `temperature_regression.py`: Regression with 5 use cases
|
|
52
|
+
- `feature_selection_example.py`: Feature importance analysis
|
|
53
|
+
- `multi_format_export.py`: Export to all formats
|
|
54
|
+
|
|
55
|
+
### Documentation
|
|
56
|
+
- Comprehensive README with quick start guide
|
|
57
|
+
- API documentation in docstrings
|
|
58
|
+
- Complete examples with explanations
|
|
59
|
+
- Troubleshooting guide
|
|
60
|
+
|
|
61
|
+
### Tests
|
|
62
|
+
- 128 tests (100% passing)
|
|
63
|
+
- ~90% code coverage
|
|
64
|
+
- Unit tests for all modules
|
|
65
|
+
- Integration tests
|
|
66
|
+
- Edge case tests
|
|
67
|
+
|
|
68
|
+
## [Unreleased]
|
|
69
|
+
|
|
70
|
+
### Planned
|
|
71
|
+
- Command-line interface (CLI) enhancements
|
|
72
|
+
- Additional optimization algorithms (Quine-McCluskey, BDD)
|
|
73
|
+
- Hardware-specific optimizations (SIMD, vectorization)
|
|
74
|
+
- Quantization-aware training integration
|
|
75
|
+
- Automated benchmarking on real hardware
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
[0.1.0]: https://github.com/AxelSkrauba/BlackBox2C/releases/tag/v0.1.0
|
|
80
|
+
[0.1.0b1]: https://github.com/AxelSkrauba/BlackBox2C/releases/tag/v0.1.0b1
|
blackbox2c-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BlackBox2C
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blackbox2c
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Convert ML models to optimized code for embedded systems
|
|
5
|
+
Author: Axel Skrauba
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/AxelSkrauba/BlackBox2C
|
|
8
|
+
Project-URL: Documentation, https://axelskrauba.github.io/BlackBox2C
|
|
9
|
+
Project-URL: Repository, https://github.com/AxelSkrauba/BlackBox2C
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/AxelSkrauba/BlackBox2C/issues
|
|
11
|
+
Keywords: machine-learning,embedded-systems,code-generation,tinyml,arduino,micropython
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
24
|
+
Classifier: Operating System :: OS Independent
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: numpy>=1.21.0
|
|
29
|
+
Requires-Dist: scikit-learn>=1.0.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: mkdocs>=1.5.0; extra == "dev"
|
|
37
|
+
Requires-Dist: mkdocs-material>=9.0.0; extra == "dev"
|
|
38
|
+
Provides-Extra: viz
|
|
39
|
+
Requires-Dist: matplotlib>=3.5.0; extra == "viz"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# BlackBox2C
|
|
43
|
+
|
|
44
|
+
**Convert scikit-learn models to native embedded code — C, C++, Arduino, MicroPython**
|
|
45
|
+
|
|
46
|
+
[](https://github.com/AxelSkrauba/BlackBox2C/actions)
|
|
47
|
+
[](https://www.python.org/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
[](CHANGELOG.md)
|
|
50
|
+
|
|
51
|
+
BlackBox2C converts any trained scikit-learn model into a minimal if-else decision tree in your
|
|
52
|
+
target language. The generated code has **zero runtime dependencies**, runs on any microcontroller
|
|
53
|
+
with a C compiler, and fits in a few hundred bytes of FLASH.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## How It Works
|
|
58
|
+
|
|
59
|
+
1. **Surrogate extraction** — A lightweight `DecisionTree` is trained to mimic any black-box model
|
|
60
|
+
(Random Forest, SVM, MLP, etc.) by generating synthetic boundary samples and labeling them with
|
|
61
|
+
the original model's predictions.
|
|
62
|
+
2. **Rule optimization** — Redundant branches are pruned and similar leaves are merged to minimize
|
|
63
|
+
code size.
|
|
64
|
+
3. **Code generation** — The optimized tree is serialized as a pure if-else function in the target
|
|
65
|
+
language.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Supported Models and Targets
|
|
70
|
+
|
|
71
|
+
| Input models | Output formats |
|
|
72
|
+
|---|---|
|
|
73
|
+
| Any scikit-learn estimator with `predict()` | Pure C (C99) |
|
|
74
|
+
| Decision Tree, Random Forest, SVM, MLP... | C++11 (class + namespace) |
|
|
75
|
+
| Classification and Regression tasks | Arduino (`.h` with PROGMEM) |
|
|
76
|
+
| | MicroPython (`.py` module) |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Installation
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# From source (development)
|
|
84
|
+
pip install -e .
|
|
85
|
+
|
|
86
|
+
# With dev dependencies (pytest, mkdocs, etc.)
|
|
87
|
+
pip install -e ".[dev]"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Requirements: Python 3.8+, NumPy >= 1.21, scikit-learn >= 1.0.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Quick Start
|
|
95
|
+
|
|
96
|
+
### Classification
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
100
|
+
from sklearn.datasets import load_iris
|
|
101
|
+
from blackbox2c import convert
|
|
102
|
+
|
|
103
|
+
iris = load_iris()
|
|
104
|
+
model = RandomForestClassifier(n_estimators=50, random_state=42)
|
|
105
|
+
model.fit(iris.data, iris.target)
|
|
106
|
+
|
|
107
|
+
# Convert to C (default target)
|
|
108
|
+
c_code = convert(
|
|
109
|
+
model,
|
|
110
|
+
iris.data,
|
|
111
|
+
feature_names=list(iris.feature_names),
|
|
112
|
+
class_names=list(iris.target_names),
|
|
113
|
+
max_depth=5,
|
|
114
|
+
)
|
|
115
|
+
print(c_code)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Generated output:
|
|
119
|
+
|
|
120
|
+
```c
|
|
121
|
+
/*
|
|
122
|
+
* Auto-generated C code by BlackBox2C
|
|
123
|
+
* - Input features: 4
|
|
124
|
+
* - Output classes: 3
|
|
125
|
+
* - Precision: 8-bit
|
|
126
|
+
*/
|
|
127
|
+
#include <stdint.h>
|
|
128
|
+
|
|
129
|
+
#define setosa 0
|
|
130
|
+
#define versicolor 1
|
|
131
|
+
#define virginica 2
|
|
132
|
+
|
|
133
|
+
uint8_t predict(float features[4]) {
|
|
134
|
+
if (features[2] <= 2.449999f) {
|
|
135
|
+
return 0;
|
|
136
|
+
} else {
|
|
137
|
+
if (features[3] <= 1.750000f) {
|
|
138
|
+
return 1;
|
|
139
|
+
} else {
|
|
140
|
+
return 2;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Export to Other Formats
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
# Arduino .h file
|
|
150
|
+
arduino_code = convert(model, iris.data, target='arduino')
|
|
151
|
+
|
|
152
|
+
# C++ class
|
|
153
|
+
cpp_code = convert(model, iris.data, target='cpp')
|
|
154
|
+
|
|
155
|
+
# MicroPython module
|
|
156
|
+
mp_code = convert(model, iris.data, target='micropython')
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Regression
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from sklearn.ensemble import GradientBoostingRegressor
|
|
163
|
+
from sklearn.datasets import load_diabetes
|
|
164
|
+
from blackbox2c import convert
|
|
165
|
+
|
|
166
|
+
data = load_diabetes()
|
|
167
|
+
model = GradientBoostingRegressor(random_state=42)
|
|
168
|
+
model.fit(data.data, data.target)
|
|
169
|
+
|
|
170
|
+
c_code = convert(model, data.data, max_depth=5)
|
|
171
|
+
# Generates: float predict(float features[10]) { ... }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Feature Analysis
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from blackbox2c.analysis import FeatureSensitivityAnalyzer
|
|
178
|
+
|
|
179
|
+
analyzer = FeatureSensitivityAnalyzer(n_repeats=10, random_state=42)
|
|
180
|
+
results = analyzer.analyze(model, X_train, y_train, feature_names=feature_names)
|
|
181
|
+
print(results.summary())
|
|
182
|
+
|
|
183
|
+
# Get top 3 most important features by index
|
|
184
|
+
top3 = results.get_top_features(3)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Configuration
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from blackbox2c import Converter, ConversionConfig
|
|
193
|
+
|
|
194
|
+
config = ConversionConfig(
|
|
195
|
+
max_depth=5, # Surrogate tree depth (1-10, default 5)
|
|
196
|
+
optimize_rules='medium', # 'low' | 'medium' | 'high'
|
|
197
|
+
use_fixed_point=False, # Use integer arithmetic instead of float
|
|
198
|
+
precision=8, # Bit width for fixed-point: 8 | 16 | 32
|
|
199
|
+
function_name='predict', # Name of the generated function
|
|
200
|
+
n_samples=10000, # Synthetic samples for surrogate training
|
|
201
|
+
feature_threshold=None, # Auto-select N most important features
|
|
202
|
+
memory_budget_kb=None, # Auto-tune params to fit a KB budget
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
converter = Converter(config)
|
|
206
|
+
code = converter.convert(model, X_train, target='arduino')
|
|
207
|
+
metrics = converter.get_metrics()
|
|
208
|
+
# {'fidelity': 0.97, 'complexity': {...}, 'size_estimate': {...}}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## CLI
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Convert a pickled model to C
|
|
217
|
+
blackbox2c convert model.pkl X_train.npy -o output.c
|
|
218
|
+
|
|
219
|
+
# Export to Arduino
|
|
220
|
+
blackbox2c convert model.pkl X_train.npy -t arduino -o predict.h
|
|
221
|
+
|
|
222
|
+
# Analyze feature importance
|
|
223
|
+
blackbox2c analyze model.pkl X_train.npy --top-n 5
|
|
224
|
+
|
|
225
|
+
# Export a decision tree directly (no surrogate extraction)
|
|
226
|
+
blackbox2c export model.pkl -f cpp -o predictor.hpp
|
|
227
|
+
|
|
228
|
+
# Help
|
|
229
|
+
blackbox2c --help
|
|
230
|
+
blackbox2c convert --help
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Benchmarks
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
python benchmarks/benchmark_classic_datasets.py --output results.md
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Covers Iris, Wine, Diabetes, and California Housing with Decision Trees, Random Forests, SVMs,
|
|
242
|
+
and Neural Networks. Metrics: fidelity, estimated FLASH size, tree depth, conversion time.
|
|
243
|
+
|
|
244
|
+
> **Note**: Code size figures are estimates from BlackBox2C's built-in size estimator,
|
|
245
|
+
> not measurements on real hardware.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Project Structure
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
blackbox2c/
|
|
253
|
+
├── blackbox2c/
|
|
254
|
+
│ ├── __init__.py # Public API: convert(), Converter, ConversionConfig
|
|
255
|
+
│ ├── converter.py # Main orchestration pipeline
|
|
256
|
+
│ ├── config.py # ConversionConfig dataclass
|
|
257
|
+
│ ├── surrogate.py # Surrogate tree extraction
|
|
258
|
+
│ ├── codegen.py # C code generation
|
|
259
|
+
│ ├── optimizer.py # Rule pruning and merging
|
|
260
|
+
│ ├── exporters.py # C++, Arduino, MicroPython exporters
|
|
261
|
+
│ ├── analysis.py # Feature sensitivity analysis
|
|
262
|
+
│ └── cli.py # Command-line interface
|
|
263
|
+
├── tests/ # 167 tests, >91% coverage
|
|
264
|
+
├── benchmarks/ # Classic dataset benchmarks
|
|
265
|
+
├── examples/ # End-to-end usage examples
|
|
266
|
+
└── docs/ # MkDocs documentation source
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Comparison with Alternatives
|
|
272
|
+
|
|
273
|
+
| Feature | BlackBox2C | emlearn | MicroMLGen | TFLite Micro |
|
|
274
|
+
|---|---|---|---|---|
|
|
275
|
+
| Any sklearn model | ✅ | ⚠️ Trees only | ⚠️ Trees only | ❌ TF only |
|
|
276
|
+
| Pure if-else output | ✅ | ✅ | ✅ | ❌ |
|
|
277
|
+
| C++ / Arduino / MicroPython | ✅ | ⚠️ Partial | ❌ | ⚠️ Partial |
|
|
278
|
+
| Feature selection built-in | ✅ | ❌ | ❌ | ❌ |
|
|
279
|
+
| Memory budget control | ✅ | ❌ | ❌ | ⚠️ |
|
|
280
|
+
| Zero runtime dependencies | ✅ | ✅ | ✅ | ❌ |
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Roadmap (v0.2)
|
|
285
|
+
|
|
286
|
+
- Quine-McCluskey and BDD rule optimization
|
|
287
|
+
- Hardware-validated benchmarks on real MCUs
|
|
288
|
+
- Quantization-aware training integration
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## License
|
|
293
|
+
|
|
294
|
+
MIT — see [LICENSE](LICENSE).
|
|
295
|
+
|
|
296
|
+
## Contributing
|
|
297
|
+
|
|
298
|
+
Issues and PRs welcome at [github.com/AxelSkrauba/BlackBox2C](https://github.com/AxelSkrauba/BlackBox2C).
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# BlackBox2C
|
|
2
|
+
|
|
3
|
+
**Convert scikit-learn models to native embedded code — C, C++, Arduino, MicroPython**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/AxelSkrauba/BlackBox2C/actions)
|
|
6
|
+
[](https://www.python.org/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](CHANGELOG.md)
|
|
9
|
+
|
|
10
|
+
BlackBox2C converts any trained scikit-learn model into a minimal if-else decision tree in your
|
|
11
|
+
target language. The generated code has **zero runtime dependencies**, runs on any microcontroller
|
|
12
|
+
with a C compiler, and fits in a few hundred bytes of FLASH.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## How It Works
|
|
17
|
+
|
|
18
|
+
1. **Surrogate extraction** — A lightweight `DecisionTree` is trained to mimic any black-box model
|
|
19
|
+
(Random Forest, SVM, MLP, etc.) by generating synthetic boundary samples and labeling them with
|
|
20
|
+
the original model's predictions.
|
|
21
|
+
2. **Rule optimization** — Redundant branches are pruned and similar leaves are merged to minimize
|
|
22
|
+
code size.
|
|
23
|
+
3. **Code generation** — The optimized tree is serialized as a pure if-else function in the target
|
|
24
|
+
language.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Supported Models and Targets
|
|
29
|
+
|
|
30
|
+
| Input models | Output formats |
|
|
31
|
+
|---|---|
|
|
32
|
+
| Any scikit-learn estimator with `predict()` | Pure C (C99) |
|
|
33
|
+
| Decision Tree, Random Forest, SVM, MLP... | C++11 (class + namespace) |
|
|
34
|
+
| Classification and Regression tasks | Arduino (`.h` with PROGMEM) |
|
|
35
|
+
| | MicroPython (`.py` module) |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# From source (development)
|
|
43
|
+
pip install -e .
|
|
44
|
+
|
|
45
|
+
# With dev dependencies (pytest, mkdocs, etc.)
|
|
46
|
+
pip install -e ".[dev]"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Requirements: Python 3.8+, NumPy >= 1.21, scikit-learn >= 1.0.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
### Classification
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
59
|
+
from sklearn.datasets import load_iris
|
|
60
|
+
from blackbox2c import convert
|
|
61
|
+
|
|
62
|
+
iris = load_iris()
|
|
63
|
+
model = RandomForestClassifier(n_estimators=50, random_state=42)
|
|
64
|
+
model.fit(iris.data, iris.target)
|
|
65
|
+
|
|
66
|
+
# Convert to C (default target)
|
|
67
|
+
c_code = convert(
|
|
68
|
+
model,
|
|
69
|
+
iris.data,
|
|
70
|
+
feature_names=list(iris.feature_names),
|
|
71
|
+
class_names=list(iris.target_names),
|
|
72
|
+
max_depth=5,
|
|
73
|
+
)
|
|
74
|
+
print(c_code)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Generated output:
|
|
78
|
+
|
|
79
|
+
```c
|
|
80
|
+
/*
|
|
81
|
+
* Auto-generated C code by BlackBox2C
|
|
82
|
+
* - Input features: 4
|
|
83
|
+
* - Output classes: 3
|
|
84
|
+
* - Precision: 8-bit
|
|
85
|
+
*/
|
|
86
|
+
#include <stdint.h>
|
|
87
|
+
|
|
88
|
+
#define setosa 0
|
|
89
|
+
#define versicolor 1
|
|
90
|
+
#define virginica 2
|
|
91
|
+
|
|
92
|
+
uint8_t predict(float features[4]) {
|
|
93
|
+
if (features[2] <= 2.449999f) {
|
|
94
|
+
return 0;
|
|
95
|
+
} else {
|
|
96
|
+
if (features[3] <= 1.750000f) {
|
|
97
|
+
return 1;
|
|
98
|
+
} else {
|
|
99
|
+
return 2;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Export to Other Formats
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Arduino .h file
|
|
109
|
+
arduino_code = convert(model, iris.data, target='arduino')
|
|
110
|
+
|
|
111
|
+
# C++ class
|
|
112
|
+
cpp_code = convert(model, iris.data, target='cpp')
|
|
113
|
+
|
|
114
|
+
# MicroPython module
|
|
115
|
+
mp_code = convert(model, iris.data, target='micropython')
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Regression
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from sklearn.ensemble import GradientBoostingRegressor
|
|
122
|
+
from sklearn.datasets import load_diabetes
|
|
123
|
+
from blackbox2c import convert
|
|
124
|
+
|
|
125
|
+
data = load_diabetes()
|
|
126
|
+
model = GradientBoostingRegressor(random_state=42)
|
|
127
|
+
model.fit(data.data, data.target)
|
|
128
|
+
|
|
129
|
+
c_code = convert(model, data.data, max_depth=5)
|
|
130
|
+
# Generates: float predict(float features[10]) { ... }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Feature Analysis
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from blackbox2c.analysis import FeatureSensitivityAnalyzer
|
|
137
|
+
|
|
138
|
+
analyzer = FeatureSensitivityAnalyzer(n_repeats=10, random_state=42)
|
|
139
|
+
results = analyzer.analyze(model, X_train, y_train, feature_names=feature_names)
|
|
140
|
+
print(results.summary())
|
|
141
|
+
|
|
142
|
+
# Get top 3 most important features by index
|
|
143
|
+
top3 = results.get_top_features(3)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Configuration
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from blackbox2c import Converter, ConversionConfig
|
|
152
|
+
|
|
153
|
+
config = ConversionConfig(
|
|
154
|
+
max_depth=5, # Surrogate tree depth (1-10, default 5)
|
|
155
|
+
optimize_rules='medium', # 'low' | 'medium' | 'high'
|
|
156
|
+
use_fixed_point=False, # Use integer arithmetic instead of float
|
|
157
|
+
precision=8, # Bit width for fixed-point: 8 | 16 | 32
|
|
158
|
+
function_name='predict', # Name of the generated function
|
|
159
|
+
n_samples=10000, # Synthetic samples for surrogate training
|
|
160
|
+
feature_threshold=None, # Auto-select N most important features
|
|
161
|
+
memory_budget_kb=None, # Auto-tune params to fit a KB budget
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
converter = Converter(config)
|
|
165
|
+
code = converter.convert(model, X_train, target='arduino')
|
|
166
|
+
metrics = converter.get_metrics()
|
|
167
|
+
# {'fidelity': 0.97, 'complexity': {...}, 'size_estimate': {...}}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## CLI
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Convert a pickled model to C
|
|
176
|
+
blackbox2c convert model.pkl X_train.npy -o output.c
|
|
177
|
+
|
|
178
|
+
# Export to Arduino
|
|
179
|
+
blackbox2c convert model.pkl X_train.npy -t arduino -o predict.h
|
|
180
|
+
|
|
181
|
+
# Analyze feature importance
|
|
182
|
+
blackbox2c analyze model.pkl X_train.npy --top-n 5
|
|
183
|
+
|
|
184
|
+
# Export a decision tree directly (no surrogate extraction)
|
|
185
|
+
blackbox2c export model.pkl -f cpp -o predictor.hpp
|
|
186
|
+
|
|
187
|
+
# Help
|
|
188
|
+
blackbox2c --help
|
|
189
|
+
blackbox2c convert --help
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Benchmarks
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
python benchmarks/benchmark_classic_datasets.py --output results.md
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Covers Iris, Wine, Diabetes, and California Housing with Decision Trees, Random Forests, SVMs,
|
|
201
|
+
and Neural Networks. Metrics: fidelity, estimated FLASH size, tree depth, conversion time.
|
|
202
|
+
|
|
203
|
+
> **Note**: Code size figures are estimates from BlackBox2C's built-in size estimator,
|
|
204
|
+
> not measurements on real hardware.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Project Structure
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
blackbox2c/
|
|
212
|
+
├── blackbox2c/
|
|
213
|
+
│ ├── __init__.py # Public API: convert(), Converter, ConversionConfig
|
|
214
|
+
│ ├── converter.py # Main orchestration pipeline
|
|
215
|
+
│ ├── config.py # ConversionConfig dataclass
|
|
216
|
+
│ ├── surrogate.py # Surrogate tree extraction
|
|
217
|
+
│ ├── codegen.py # C code generation
|
|
218
|
+
│ ├── optimizer.py # Rule pruning and merging
|
|
219
|
+
│ ├── exporters.py # C++, Arduino, MicroPython exporters
|
|
220
|
+
│ ├── analysis.py # Feature sensitivity analysis
|
|
221
|
+
│ └── cli.py # Command-line interface
|
|
222
|
+
├── tests/ # 167 tests, >91% coverage
|
|
223
|
+
├── benchmarks/ # Classic dataset benchmarks
|
|
224
|
+
├── examples/ # End-to-end usage examples
|
|
225
|
+
└── docs/ # MkDocs documentation source
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Comparison with Alternatives
|
|
231
|
+
|
|
232
|
+
| Feature | BlackBox2C | emlearn | MicroMLGen | TFLite Micro |
|
|
233
|
+
|---|---|---|---|---|
|
|
234
|
+
| Any sklearn model | ✅ | ⚠️ Trees only | ⚠️ Trees only | ❌ TF only |
|
|
235
|
+
| Pure if-else output | ✅ | ✅ | ✅ | ❌ |
|
|
236
|
+
| C++ / Arduino / MicroPython | ✅ | ⚠️ Partial | ❌ | ⚠️ Partial |
|
|
237
|
+
| Feature selection built-in | ✅ | ❌ | ❌ | ❌ |
|
|
238
|
+
| Memory budget control | ✅ | ❌ | ❌ | ⚠️ |
|
|
239
|
+
| Zero runtime dependencies | ✅ | ✅ | ✅ | ❌ |
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Roadmap (v0.2)
|
|
244
|
+
|
|
245
|
+
- Quine-McCluskey and BDD rule optimization
|
|
246
|
+
- Hardware-validated benchmarks on real MCUs
|
|
247
|
+
- Quantization-aware training integration
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
MIT — see [LICENSE](LICENSE).
|
|
254
|
+
|
|
255
|
+
## Contributing
|
|
256
|
+
|
|
257
|
+
Issues and PRs welcome at [github.com/AxelSkrauba/BlackBox2C](https://github.com/AxelSkrauba/BlackBox2C).
|