fastforest 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.
- fastforest-0.1.0/.github/workflows/ci.yml +73 -0
- fastforest-0.1.0/.gitignore +17 -0
- fastforest-0.1.0/Cargo.lock +343 -0
- fastforest-0.1.0/Cargo.toml +27 -0
- fastforest-0.1.0/DEV.md +95 -0
- fastforest-0.1.0/LICENSE +202 -0
- fastforest-0.1.0/PKG-INFO +410 -0
- fastforest-0.1.0/README.md +384 -0
- fastforest-0.1.0/_config.yml +1 -0
- fastforest-0.1.0/_layouts/default.html +52 -0
- fastforest-0.1.0/pyproject.toml +40 -0
- fastforest-0.1.0/python/fastforest/__init__.py +147 -0
- fastforest-0.1.0/python/fastforest/analysis.py +374 -0
- fastforest-0.1.0/python/fastforest/preprocessing.py +159 -0
- fastforest-0.1.0/python/fastforest/sklearn.py +89 -0
- fastforest-0.1.0/src/forest.rs +1053 -0
- fastforest-0.1.0/src/lib.rs +12 -0
- fastforest-0.1.0/src/preprocessing.rs +701 -0
- fastforest-0.1.0/src/python.rs +497 -0
- fastforest-0.1.0/src/split.rs +410 -0
- fastforest-0.1.0/src/workbench.rs +98 -0
- fastforest-0.1.0/tests/test_fastforest.py +140 -0
- fastforest-0.1.0/tools/accuracy.py +222 -0
- fastforest-0.1.0/tools/bench.py +39 -0
- fastforest-0.1.0/tools/sweep.py +88 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ['v*']
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ['3.10', '3.11', '3.12', '3.13']
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v7
|
|
17
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
18
|
+
- uses: actions/setup-python@v7
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
- run: pip install -e '.[dev]'
|
|
22
|
+
- run: pytest -q
|
|
23
|
+
|
|
24
|
+
build:
|
|
25
|
+
needs: test
|
|
26
|
+
strategy:
|
|
27
|
+
matrix:
|
|
28
|
+
os: [ubuntu-latest, macos-latest]
|
|
29
|
+
runs-on: ${{ matrix.os }}
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v7
|
|
32
|
+
- uses: PyO3/maturin-action@v1
|
|
33
|
+
with:
|
|
34
|
+
args: --release --out dist -i python3.10 -i python3.11 -i python3.12 -i python3.13
|
|
35
|
+
manylinux: auto
|
|
36
|
+
- uses: actions/upload-artifact@v7
|
|
37
|
+
with:
|
|
38
|
+
name: wheels-${{ matrix.os }}
|
|
39
|
+
path: dist
|
|
40
|
+
|
|
41
|
+
sdist:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v7
|
|
45
|
+
- uses: PyO3/maturin-action@v1
|
|
46
|
+
with:
|
|
47
|
+
command: sdist
|
|
48
|
+
args: -o dist
|
|
49
|
+
- uses: actions/upload-artifact@v7
|
|
50
|
+
with:
|
|
51
|
+
name: wheels-sdist
|
|
52
|
+
path: dist
|
|
53
|
+
|
|
54
|
+
publish:
|
|
55
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
56
|
+
needs: [build, sdist]
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
permissions:
|
|
59
|
+
id-token: write
|
|
60
|
+
contents: write
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v7
|
|
63
|
+
- uses: actions/download-artifact@v8
|
|
64
|
+
with:
|
|
65
|
+
path: dist
|
|
66
|
+
merge-multiple: true
|
|
67
|
+
- uses: softprops/action-gh-release@v3
|
|
68
|
+
with:
|
|
69
|
+
files: dist/*
|
|
70
|
+
generate_release_notes: true
|
|
71
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
72
|
+
with:
|
|
73
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "autocfg"
|
|
7
|
+
version = "1.5.1"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "cfg-if"
|
|
13
|
+
version = "1.0.4"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "chacha20"
|
|
19
|
+
version = "0.10.1"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
|
|
22
|
+
dependencies = [
|
|
23
|
+
"cfg-if",
|
|
24
|
+
"cpufeatures",
|
|
25
|
+
"rand_core",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[[package]]
|
|
29
|
+
name = "cpufeatures"
|
|
30
|
+
version = "0.3.0"
|
|
31
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
32
|
+
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
|
33
|
+
dependencies = [
|
|
34
|
+
"libc",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[[package]]
|
|
38
|
+
name = "crossbeam-deque"
|
|
39
|
+
version = "0.8.7"
|
|
40
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
41
|
+
checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
|
|
42
|
+
dependencies = [
|
|
43
|
+
"crossbeam-epoch",
|
|
44
|
+
"crossbeam-utils",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[[package]]
|
|
48
|
+
name = "crossbeam-epoch"
|
|
49
|
+
version = "0.9.20"
|
|
50
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
51
|
+
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
|
|
52
|
+
dependencies = [
|
|
53
|
+
"crossbeam-utils",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[[package]]
|
|
57
|
+
name = "crossbeam-utils"
|
|
58
|
+
version = "0.8.22"
|
|
59
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
60
|
+
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
|
|
61
|
+
|
|
62
|
+
[[package]]
|
|
63
|
+
name = "either"
|
|
64
|
+
version = "1.17.0"
|
|
65
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
66
|
+
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
|
|
67
|
+
|
|
68
|
+
[[package]]
|
|
69
|
+
name = "fastforest"
|
|
70
|
+
version = "0.1.0"
|
|
71
|
+
dependencies = [
|
|
72
|
+
"ndarray",
|
|
73
|
+
"numpy",
|
|
74
|
+
"pyo3",
|
|
75
|
+
"rand",
|
|
76
|
+
"rayon",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[[package]]
|
|
80
|
+
name = "getrandom"
|
|
81
|
+
version = "0.4.3"
|
|
82
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
83
|
+
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
|
84
|
+
dependencies = [
|
|
85
|
+
"cfg-if",
|
|
86
|
+
"libc",
|
|
87
|
+
"r-efi",
|
|
88
|
+
"rand_core",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[[package]]
|
|
92
|
+
name = "heck"
|
|
93
|
+
version = "0.5.0"
|
|
94
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
95
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
96
|
+
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "libc"
|
|
99
|
+
version = "0.2.189"
|
|
100
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
101
|
+
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
|
102
|
+
|
|
103
|
+
[[package]]
|
|
104
|
+
name = "matrixmultiply"
|
|
105
|
+
version = "0.3.11"
|
|
106
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
107
|
+
checksum = "3f607c237553f086e7043417a51df26b2eb899d3caff94e6a67592ff992fedc7"
|
|
108
|
+
dependencies = [
|
|
109
|
+
"autocfg",
|
|
110
|
+
"rawpointer",
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
[[package]]
|
|
114
|
+
name = "ndarray"
|
|
115
|
+
version = "0.17.2"
|
|
116
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
117
|
+
checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d"
|
|
118
|
+
dependencies = [
|
|
119
|
+
"matrixmultiply",
|
|
120
|
+
"num-complex",
|
|
121
|
+
"num-integer",
|
|
122
|
+
"num-traits",
|
|
123
|
+
"portable-atomic",
|
|
124
|
+
"portable-atomic-util",
|
|
125
|
+
"rawpointer",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
[[package]]
|
|
129
|
+
name = "num-complex"
|
|
130
|
+
version = "0.4.6"
|
|
131
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
132
|
+
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
|
133
|
+
dependencies = [
|
|
134
|
+
"num-traits",
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
[[package]]
|
|
138
|
+
name = "num-integer"
|
|
139
|
+
version = "0.1.47"
|
|
140
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
141
|
+
checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
|
|
142
|
+
dependencies = [
|
|
143
|
+
"num-traits",
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
[[package]]
|
|
147
|
+
name = "num-traits"
|
|
148
|
+
version = "0.2.19"
|
|
149
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
150
|
+
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
151
|
+
dependencies = [
|
|
152
|
+
"autocfg",
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
[[package]]
|
|
156
|
+
name = "numpy"
|
|
157
|
+
version = "0.29.0"
|
|
158
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
159
|
+
checksum = "6a5b15d63a5ff39e378daed0e1340d3a5964703ea9712eb09a0dc66fade996f4"
|
|
160
|
+
dependencies = [
|
|
161
|
+
"libc",
|
|
162
|
+
"ndarray",
|
|
163
|
+
"num-complex",
|
|
164
|
+
"num-integer",
|
|
165
|
+
"num-traits",
|
|
166
|
+
"pyo3",
|
|
167
|
+
"pyo3-build-config",
|
|
168
|
+
"rustc-hash",
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
[[package]]
|
|
172
|
+
name = "once_cell"
|
|
173
|
+
version = "1.21.4"
|
|
174
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
175
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
176
|
+
|
|
177
|
+
[[package]]
|
|
178
|
+
name = "portable-atomic"
|
|
179
|
+
version = "1.15.0"
|
|
180
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
181
|
+
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
|
|
182
|
+
|
|
183
|
+
[[package]]
|
|
184
|
+
name = "portable-atomic-util"
|
|
185
|
+
version = "0.2.7"
|
|
186
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
187
|
+
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
|
|
188
|
+
dependencies = [
|
|
189
|
+
"portable-atomic",
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
[[package]]
|
|
193
|
+
name = "proc-macro2"
|
|
194
|
+
version = "1.0.107"
|
|
195
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
196
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
197
|
+
dependencies = [
|
|
198
|
+
"unicode-ident",
|
|
199
|
+
]
|
|
200
|
+
|
|
201
|
+
[[package]]
|
|
202
|
+
name = "pyo3"
|
|
203
|
+
version = "0.29.2"
|
|
204
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
205
|
+
checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
|
|
206
|
+
dependencies = [
|
|
207
|
+
"libc",
|
|
208
|
+
"once_cell",
|
|
209
|
+
"portable-atomic",
|
|
210
|
+
"pyo3-build-config",
|
|
211
|
+
"pyo3-ffi",
|
|
212
|
+
"pyo3-macros",
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
[[package]]
|
|
216
|
+
name = "pyo3-build-config"
|
|
217
|
+
version = "0.29.2"
|
|
218
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
219
|
+
checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
|
|
220
|
+
dependencies = [
|
|
221
|
+
"target-lexicon",
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
[[package]]
|
|
225
|
+
name = "pyo3-ffi"
|
|
226
|
+
version = "0.29.2"
|
|
227
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
228
|
+
checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
|
|
229
|
+
dependencies = [
|
|
230
|
+
"libc",
|
|
231
|
+
"pyo3-build-config",
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
[[package]]
|
|
235
|
+
name = "pyo3-macros"
|
|
236
|
+
version = "0.29.2"
|
|
237
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
238
|
+
checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
|
|
239
|
+
dependencies = [
|
|
240
|
+
"proc-macro2",
|
|
241
|
+
"pyo3-macros-backend",
|
|
242
|
+
"quote",
|
|
243
|
+
"syn",
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
[[package]]
|
|
247
|
+
name = "pyo3-macros-backend"
|
|
248
|
+
version = "0.29.2"
|
|
249
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
250
|
+
checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
|
|
251
|
+
dependencies = [
|
|
252
|
+
"heck",
|
|
253
|
+
"proc-macro2",
|
|
254
|
+
"quote",
|
|
255
|
+
"syn",
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
[[package]]
|
|
259
|
+
name = "quote"
|
|
260
|
+
version = "1.0.47"
|
|
261
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
262
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
263
|
+
dependencies = [
|
|
264
|
+
"proc-macro2",
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
[[package]]
|
|
268
|
+
name = "r-efi"
|
|
269
|
+
version = "6.0.0"
|
|
270
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
271
|
+
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
|
272
|
+
|
|
273
|
+
[[package]]
|
|
274
|
+
name = "rand"
|
|
275
|
+
version = "0.10.2"
|
|
276
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
277
|
+
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
|
|
278
|
+
dependencies = [
|
|
279
|
+
"chacha20",
|
|
280
|
+
"getrandom",
|
|
281
|
+
"rand_core",
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
[[package]]
|
|
285
|
+
name = "rand_core"
|
|
286
|
+
version = "0.10.1"
|
|
287
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
288
|
+
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
|
289
|
+
|
|
290
|
+
[[package]]
|
|
291
|
+
name = "rawpointer"
|
|
292
|
+
version = "0.2.1"
|
|
293
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
294
|
+
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
|
|
295
|
+
|
|
296
|
+
[[package]]
|
|
297
|
+
name = "rayon"
|
|
298
|
+
version = "1.12.0"
|
|
299
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
300
|
+
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
|
|
301
|
+
dependencies = [
|
|
302
|
+
"either",
|
|
303
|
+
"rayon-core",
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
[[package]]
|
|
307
|
+
name = "rayon-core"
|
|
308
|
+
version = "1.13.0"
|
|
309
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
310
|
+
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
|
311
|
+
dependencies = [
|
|
312
|
+
"crossbeam-deque",
|
|
313
|
+
"crossbeam-utils",
|
|
314
|
+
]
|
|
315
|
+
|
|
316
|
+
[[package]]
|
|
317
|
+
name = "rustc-hash"
|
|
318
|
+
version = "2.1.3"
|
|
319
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
320
|
+
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
|
321
|
+
|
|
322
|
+
[[package]]
|
|
323
|
+
name = "syn"
|
|
324
|
+
version = "2.0.119"
|
|
325
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
326
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
327
|
+
dependencies = [
|
|
328
|
+
"proc-macro2",
|
|
329
|
+
"quote",
|
|
330
|
+
"unicode-ident",
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
[[package]]
|
|
334
|
+
name = "target-lexicon"
|
|
335
|
+
version = "0.13.5"
|
|
336
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
337
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
338
|
+
|
|
339
|
+
[[package]]
|
|
340
|
+
name = "unicode-ident"
|
|
341
|
+
version = "1.0.24"
|
|
342
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
343
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "fastforest"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
rust-version = "1.91"
|
|
6
|
+
license = "Apache-2.0"
|
|
7
|
+
description = "Fast approximate random forests in Rust with Python bindings"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
|
|
10
|
+
[lib]
|
|
11
|
+
name = "fastforest"
|
|
12
|
+
crate-type = ["cdylib", "rlib"]
|
|
13
|
+
|
|
14
|
+
[profile.release]
|
|
15
|
+
lto = true
|
|
16
|
+
codegen-units = 1
|
|
17
|
+
|
|
18
|
+
[dependencies]
|
|
19
|
+
ndarray = ">=0.17.2"
|
|
20
|
+
numpy = { version = ">=0.29.0", optional = true }
|
|
21
|
+
pyo3 = { version = ">=0.29.2", optional = true }
|
|
22
|
+
rand = ">=0.10.2"
|
|
23
|
+
rayon = ">=1.12.0"
|
|
24
|
+
|
|
25
|
+
[features]
|
|
26
|
+
python = ["dep:numpy", "dep:pyo3"]
|
|
27
|
+
extension-module = ["python", "pyo3/extension-module"]
|
fastforest-0.1.0/DEV.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Development
|
|
2
|
+
|
|
3
|
+
`fastforest` is a Rust library with a private PyO3 extension and a small public Python façade.
|
|
4
|
+
|
|
5
|
+
## Layout
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
src/forest.rs forest algorithm and Rust tests
|
|
9
|
+
src/split.rs interchangeable split searches and their reusable scratch
|
|
10
|
+
src/workbench.rs experimental strategy enums and leaf estimation
|
|
11
|
+
src/preprocessing.rs native fitted mixed-column schema and encoding
|
|
12
|
+
src/python.rs private PyO3/NumPy boundary
|
|
13
|
+
python/fastforest/__init__.py public Python API and array conversion
|
|
14
|
+
python/fastforest/preprocessing.py container adapter, display metadata, and aggregation
|
|
15
|
+
python/fastforest/sklearn.py reproducible sklearn mixed-data benchmark preprocessors
|
|
16
|
+
python/fastforest/analysis.py NumPy analysis results, perturbations, clustering, and lazy plots
|
|
17
|
+
tests/test_fastforest.py Python API narratives and validation
|
|
18
|
+
tools/bench.py fit, OOB, and prediction timings
|
|
19
|
+
tools/accuracy.py tabular regression accuracy and timing comparisons
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The core depends on `ndarray` but not Python. PyO3 and rust-numpy are optional behind the `python` feature; maturin enables `extension-module`, which enables it. The Rust crate therefore remains directly usable as an `rlib`.
|
|
23
|
+
|
|
24
|
+
## Design
|
|
25
|
+
|
|
26
|
+
The Rust encoder fits the input schema and transforms inference data, parallelizing independent columns with Rayon. Every non-missing column is parsed numerically when possible and otherwise lexically ordered. Training features are contiguous `u32` ranks, dummy indicators, and missing indicators. The encoder retains the original-column mapping, type, integral-display flag, missing rule, median when required, and native cutoff boundaries. Python only validates container shape and names, selects column buffers, and reconstructs small display metadata. Pandas categoricals cross the boundary as integer codes plus one vocabulary rather than per-row Python objects. Rust-numpy borrows the generated arrays without another copy, and PyO3 detaches from Python while Rust runs.
|
|
27
|
+
|
|
28
|
+
Tree construction uses `u32` feature ranks, candidate cutoffs, and row indexes. Temporary 16-byte training nodes hold rank cutoffs, allowing OOB prediction directly against the encoded training matrix. Once OOB is complete, every split rank becomes the greatest native value on its left side. Final trees own flat vectors of 16-byte inference nodes containing an `f32` native cutoff or leaf value plus `u32` feature and child indexes. A native value goes right when it is greater than the stored left boundary, exactly matching insertion-rank behavior for values unseen during training. Sibling children are adjacent, so the right child is `left + 1`.
|
|
29
|
+
|
|
30
|
+
The uniform `u32` training representation supports arbitrary practical cardinality without the bandwidth cost of `u64`; adaptive `u8`/`u16` columns are intentionally deferred until benchmarks justify their complexity. Candidate de-duplication packs the `u32` feature and cutoff into a `u64` key. `usize` is reserved for Rust indexing boundaries. A seeded RNG first generates one seed per tree, so Rayon can build trees in parallel without changing results. Prediction is parallel over rows; each worker evaluates all trees for its rows and writes directly to its own output elements, requiring no shared accumulator or reduction buffer.
|
|
31
|
+
|
|
32
|
+
Tree construction accumulates normalized split-gain importance outside the compact inference nodes. Native analysis operations return per-tree predictions and encoded-feature path contributions efficiently without exposing the internal node representation; Python then sums derived features back into their original columns. A path contribution is the change in successive node values assigned to the feature selecting that child, so forest bias plus all contributions exactly reconstructs the prediction.
|
|
33
|
+
|
|
34
|
+
The higher analysis layer is deliberately NumPy-only. A shared feature resolver handles array indices, data-frame column names, and grouped features. Permutation and drop-column importance share its group representation; PDP/ICE reuse the same named data and prediction interface. Tie-aware Spearman ranks and average-linkage clustering are small local implementations rather than scipy dependencies. Result objects contain ordinary arrays and import matplotlib lazily only for their `plot` methods.
|
|
35
|
+
|
|
36
|
+
OOB is optional because it predicts every training row with every tree for which that row was not sampled. During tree construction a compact in-bag mask is retained, then discarded after forest-level OOB sums and counts have been calculated.
|
|
37
|
+
|
|
38
|
+
Candidate cutoffs are deduplicated as exact `(feature, value)` pairs. The default permits two proposals per requested unique candidate; experiments showed that larger retry bounds disproportionately increase fit time on discrete features. A 20-row floor keeps candidate coverage from collapsing quadratically in small nodes. Reusable per-tree scratch storage keeps de-duplication overhead low.
|
|
39
|
+
|
|
40
|
+
Interchangeable tree-building choices use enum dispatch rather than trait objects in the node loop. `Workbench` owns orthogonal split-search, feature-selection, and leaf-estimation settings; `split::find_split` dispatches to a self-contained implementation sharing only the split result and scratch storage. Numeric value features and their missing indicators are atomic sampling groups; the broader `feature_sampling="columns"` experiment groups every encoding from each original column. The production histogram splitter evaluates at most 320 sampled ranks and uses 75% of feature units on datasets through 8,000 rows. Above that size, adaptive fitting compares 60% and 90% feature sampling using paired OOB pilots before fitting the production forest. The original random splitter remains available and retains its original RNG call order exactly when selected.
|
|
41
|
+
|
|
42
|
+
Categorical subset splits and learned missing routing belong at a separate boundary: they require encoder metadata and a richer stored split predicate, not another branch inside ordered cutoff scoring. Those experiments should extend the workbench at that encoder/predicate layer while retaining the same tree builder and leaf strategy.
|
|
43
|
+
|
|
44
|
+
## Testing
|
|
45
|
+
|
|
46
|
+
Tests favor a few complete narratives over many single-assertion tests. The main Rust test covers fitting, tree invariants, prediction quality, OOB, determinism, per-tree predictions, split importance, and exact explanation reconstruction. The main Python test demonstrates the public workflow including array conversion, OOB, importance, uncertainty, explanations, PDP/ICE, correlation clustering, and nonlinear dependence. Distinct validation and numerical edge cases share one focused test in each layer.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cargo fmt
|
|
50
|
+
cargo check
|
|
51
|
+
cargo test
|
|
52
|
+
maturin develop
|
|
53
|
+
pytest -q
|
|
54
|
+
chkstyle python/fastforest tests tools
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
For performance measurements:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
maturin develop --release
|
|
61
|
+
python tools/bench.py --rows 60000 --cols 50 --trees 100
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For an accuracy and timing comparison using an identical fixed split for each model:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python tools/accuracy.py
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
California Housing is the default. The other choices are `concrete`, `sgemm`, `diamonds`, `allstate`, and `diabetes`. Every dataset uses one reproducible 80/20 split. Mixed-data sklearn RF comparisons use `sklearn_preprocessor`: one-hot encoding through 20 levels and target encoding above 20. HistGBM uses its documented native categorical support, target-encoding only features beyond its 255-category limit.
|
|
71
|
+
|
|
72
|
+
Each model/dataset combination runs in a spawned process and has a 180-second default timeout. The child signals readiness after process startup and input deserialization; only then does the timeout begin. Reported fit time includes model and preprocessor construction, schema inspection, and `fit`, but excludes process startup and IPC.
|
|
73
|
+
|
|
74
|
+
The sklearn random forest uses all available cores, as do FastForest and histogram GBM internally.
|
|
75
|
+
For focused FastForest tuning, add `--ff_only` and vary `--min_node_size`, `--bootstrap_fraction`, `--bootstrap_max`, `--replacement`, `--max_node_samples`, or `--cutoff_divisor`. Pass `--no-adaptive` when directly comparing fixed split-search settings. Experimental comparisons additionally accept `--splitter`, `--max_features`, and `--leaf_regularization`. The tools use `call_parse`, so CLI names match their underscored function parameters.
|
|
76
|
+
|
|
77
|
+
Run a reproducible parameter grid (SGEMM by default) and save its metrics and timings with:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python tools/sweep.py
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`tools/sweep.py` accepts comma-separated `--splitters`, `--max_features`, and `--leaf_regularizations` and records every strategy axis in its CSV. This is the preferred workbench for factorial experiments; selected configurations can then be compared against sklearn with the singular forms accepted by `tools/accuracy.py`.
|
|
84
|
+
|
|
85
|
+
## Versioning and release
|
|
86
|
+
|
|
87
|
+
The canonical version lives in `Cargo.toml`; `pyproject.toml` gets it through `dynamic = ["version"]`.
|
|
88
|
+
|
|
89
|
+
Once the repository has been created and added to the workspace, release flow is:
|
|
90
|
+
|
|
91
|
+
1. Run the Rust and Python tests against a release build.
|
|
92
|
+
2. Confirm the release version in `Cargo.toml`.
|
|
93
|
+
3. Run `ship-release`.
|
|
94
|
+
|
|
95
|
+
GitHub Actions builds Linux and macOS wheels plus an sdist, publishes tagged builds, and creates the GitHub release.
|