Skip to content

Commit db48f62

Browse files
authored
Merge pull request #1 from simonprovost/refactor/support_py310_plus
2 parents 5048514 + 1a338c7 commit db48f62

16 files changed

+228
-73
lines changed

.github/workflows/build-and-test.yml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ jobs:
1212
strategy:
1313
matrix:
1414
os: [ubuntu-latest, windows-latest, macos-latest]
15-
python-version: [3.7, 3.8, 3.9]
15+
python-version: ["3.10", "3.11", "3.12"]
1616
steps:
17-
- uses: actions/checkout@v2
17+
- uses: actions/checkout@v4
1818
- name: Set up Python
19-
uses: actions/setup-python@v2
19+
uses: actions/setup-python@v5
2020
with:
2121
python-version: ${{ matrix.python-version }}
2222
- name: Display python version
@@ -33,8 +33,9 @@ jobs:
3333
- name: Run tests
3434
run: |
3535
pytest ./tests --cov-config=.coveragerc --cov-report=xml --cov=deepforest deepforest
36-
- name: Publish code coverage
37-
uses: codecov/codecov-action@v1
38-
with:
39-
token: ${{ secrets.CODECOV_TOKEN }}
40-
file: ./coverage.xml
36+
# This step requires CODECOV_TOKEN, which is only avail. in the original repo. We therefore comment that out.
37+
# - name: Publish code coverage
38+
# uses: codecov/codecov-action@v5
39+
# with:
40+
# token: ${{ secrets.CODECOV_TOKEN }}
41+
# file: ./coverage.xml

.github/workflows/build-wheels.yml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,28 @@ jobs:
1414
matrix:
1515
os: [ubuntu-latest, windows-latest, macos-latest]
1616
steps:
17-
- uses: actions/checkout@v2
17+
- uses: actions/checkout@v4
1818

1919
- name: Set up QEMU
2020
if: runner.os == 'Linux'
21-
uses: docker/setup-qemu-action@v1
21+
uses: docker/setup-qemu-action@v3
2222
with:
2323
platforms: all
2424

2525
- name: Build wheels
26-
uses: joerick/cibuildwheel@v1.9.0
26+
uses: pypa/cibuildwheel@v2.16.5
2727
with:
2828
output-dir: wheelhouse
2929
env:
3030
CIBW_ARCHS_LINUX: "x86_64 aarch64"
3131
CIBW_ARCHS_WINDOWS: "AMD64"
3232
CIBW_ARCHS_MACOS: "x86_64"
33-
CIBW_BUILD: cp3*-macosx_x86_64 cp3*-win_amd64 cp3*-manylinux_x86_64 cp3*-manylinux_aarch64
34-
CIBW_SKIP: cp35-* cp36-*
33+
CIBW_BUILD: >-
34+
cp310-manylinux_x86_64 cp310-manylinux_aarch64 cp310-macosx_x86_64 cp310-win_amd64
35+
cp311-manylinux_x86_64 cp311-manylinux_aarch64 cp311-macosx_x86_64 cp311-win_amd64
36+
cp312-manylinux_x86_64 cp312-manylinux_aarch64 cp312-macosx_x86_64 cp312-win_amd64
3537
- name: Store artifacts
36-
uses: actions/upload-artifact@v2
38+
uses: actions/upload-artifact@v4
3739
with:
40+
name: wheels-${{ matrix.os }}
3841
path: ./wheelhouse/*.whl

.github/workflows/code-quality.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ jobs:
1212
strategy:
1313
matrix:
1414
os: [ubuntu-latest]
15-
python-version: [3.7]
15+
python-version: ["3.10"]
1616
steps:
17-
- uses: actions/checkout@v2
17+
- uses: actions/checkout@v4
1818
- name: Set up python
19-
uses: actions/setup-python@v2
19+
uses: actions/setup-python@v5
2020
with:
2121
python-version: ${{ matrix.python-version }}
2222
- name: Display python version
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
name: Manual Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: "Version to release (e.g., 0.1.8 <~ Must be above 0.1.7 as this is the point where we forked DF21)"
8+
required: true
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
release:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: "3.10"
26+
27+
- name: Install build backend
28+
run: |
29+
python -m pip install --upgrade pip
30+
python -m pip install build toml
31+
32+
- name: Update version in setup.py
33+
env:
34+
RELEASE_VERSION: "${{ github.event.inputs.version }}"
35+
run: |
36+
python - <<'PY'
37+
import os
38+
import pathlib
39+
import re
40+
41+
release_version = os.environ["RELEASE_VERSION"].strip()
42+
if not release_version:
43+
raise SystemExit("RELEASE_VERSION is required")
44+
45+
version = pathlib.Path("setup.py")
46+
text = version.read_text()
47+
new_text, count = re.subn(
48+
r'^VERSION = "[^"]+"', f'VERSION = "{release_version}"', text, flags=re.MULTILINE
49+
)
50+
if count == 0:
51+
raise SystemExit("VERSION assignment not found in setup.py")
52+
if new_text != text:
53+
version.write_text(new_text)
54+
PY
55+
shell: bash
56+
57+
- name: Commit version bump
58+
env:
59+
RELEASE_VERSION: "${{ github.event.inputs.version }}"
60+
run: |
61+
git config user.name "github-actions[bot]"
62+
git config user.email "github-actions[bot]@users.noreply.github.com"
63+
git add setup.py
64+
if git diff --cached --quiet; then
65+
echo "No changes to commit"
66+
else
67+
git commit -m "chore: release v${RELEASE_VERSION}"
68+
fi
69+
70+
- name: Create and push tag
71+
env:
72+
RELEASE_VERSION: "${{ github.event.inputs.version }}"
73+
run: |
74+
git tag "v${RELEASE_VERSION}" || true
75+
git push origin HEAD
76+
git push origin "v${RELEASE_VERSION}"
77+
78+
- name: Build distributions
79+
run: python -m build
80+
81+
- name: Publish to PyPI
82+
uses: pypa/gh-action-pypi-publish@release/v1
83+
with:
84+
password: ${{ secrets.PYPI_API_TOKEN }}

README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
Deep Forest (DF) 21
22
===================
33

4+
.. note::
5+
6+
This repository is a community-maintained copy of the original `Deep Forest (DF21) project <https://github.com/LAMDA-NJU/Deep-Forest>`_. We do **not** claim credit for the underlying research or implementation; our goal is to keep the project usable on modern Python versions (e.g., Python 3.10+) while the upstream repository has seen minimal activity since a long time (with one recent try to move to py310+ but with CI/CD failing). If the upstream maintainers prioritise these updates, we are happy to contribute everything back via pull request.
7+
48
|github|_ |readthedocs|_ |codecov|_ |python|_ |pypi|_ |style|_
59

610
.. |github| image:: https://github.com/LAMDA-NJU/Deep-Forest/workflows/DeepForest-CI/badge.svg

deepforest/cascade.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,10 +350,10 @@ def _build_regressor_predictor(
350350
The maximum number of cascade layers in the deep forest. Notice that
351351
the actual number of layers can be smaller than ``max_layers`` because
352352
of the internal early stopping stage.
353-
criterion : :obj:`{"mse", "mae"}`, default= :obj:`"mse"`
354-
The function to measure the quality of a split. Supported criteria are
355-
``mse`` for the mean squared error, which is equal to variance reduction
356-
as feature selection criterion, and ``mae`` for the mean absolute error.
353+
criterion : :obj:`{"squared_error", "absolute_error"}`, default= :obj:`"squared_error"`
354+
The function to measure the quality of a split. Supported criteria are
355+
``squared_error`` for the mean squared error, which is equal to variance reduction
356+
as feature selection criterion, and ``absolute_error`` for the mean absolute error.
357357
n_estimators : :obj:`int`, default=2
358358
The number of estimator in each cascade layer. It will be multiplied
359359
by 2 internally because each estimator contains a
@@ -1553,7 +1553,7 @@ def __init__(
15531553
bin_subsample=200000,
15541554
bin_type="percentile",
15551555
max_layers=20,
1556-
criterion="mse",
1556+
criterion="squared_error",
15571557
n_estimators=2,
15581558
n_trees=100,
15591559
max_depth=None,

deepforest/forest.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@
4949
MAX_INT = np.iinfo(np.int32).max
5050

5151

52+
def _normalize_regression_criterion(criterion: str) -> str:
53+
"""Align legacy regression criteria names with scikit-learn
54+
expectations."""
55+
56+
if criterion == "mse":
57+
return "squared_error"
58+
if criterion == "mae":
59+
return "absolute_error"
60+
return criterion
61+
62+
5263
def _get_n_samples_bootstrap(n_samples, max_samples):
5364
"""
5465
Get the number of samples in a bootstrap sample.
@@ -199,9 +210,7 @@ def _partition_estimators(n_estimators, n_jobs):
199210
n_jobs = min(effective_n_jobs(n_jobs), n_estimators)
200211

201212
# Partition estimators between jobs
202-
n_estimators_per_job = np.full(
203-
n_jobs, n_estimators // n_jobs, dtype=int
204-
)
213+
n_estimators_per_job = np.full(n_jobs, n_estimators // n_jobs, dtype=int)
205214
n_estimators_per_job[: n_estimators % n_jobs] += 1
206215
starts = np.cumsum(n_estimators_per_job)
207216

@@ -840,7 +849,7 @@ def __init__(
840849
self,
841850
n_estimators=100,
842851
*,
843-
criterion="mse",
852+
criterion="squared_error",
844853
max_depth=None,
845854
min_samples_split=2,
846855
min_samples_leaf=1,
@@ -853,6 +862,7 @@ def __init__(
853862
verbose=0,
854863
max_samples=None
855864
):
865+
criterion = _normalize_regression_criterion(criterion)
856866
super().__init__(
857867
base_estimator=DecisionTreeRegressor(),
858868
n_estimators=n_estimators,
@@ -889,7 +899,7 @@ def __init__(
889899
self,
890900
n_estimators=100,
891901
*,
892-
criterion="mse",
902+
criterion="squared_error",
893903
max_depth=None,
894904
min_samples_split=2,
895905
min_samples_leaf=1,
@@ -902,6 +912,7 @@ def __init__(
902912
verbose=0,
903913
max_samples=None
904914
):
915+
criterion = _normalize_regression_criterion(criterion)
905916
super().__init__(
906917
base_estimator=ExtraTreeRegressor(),
907918
n_estimators=n_estimators,

deepforest/tree/tree.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@
4949
DOUBLE = _tree.DOUBLE
5050

5151
CRITERIA_CLF = {"gini": _criterion.Gini, "entropy": _criterion.Entropy}
52-
CRITERIA_REG = {"mse": _criterion.MSE, "mae": _criterion.MAE}
52+
CRITERIA_REG = {
53+
"mse": _criterion.MSE,
54+
"mae": _criterion.MAE,
55+
"squared_error": _criterion.MSE,
56+
"absolute_error": _criterion.MAE,
57+
}
5358

5459
DENSE_SPLITTERS = {
5560
"best": _splitter.BestSplitter,
@@ -181,7 +186,7 @@ def fit(
181186
if self.class_weight is not None:
182187
y_original = np.copy(y)
183188

184-
y_encoded = np.zeros(y.shape, dtype=np.int)
189+
y_encoded = np.zeros(y.shape, dtype=int)
185190
for k in range(self.n_outputs_):
186191
classes_k, y_encoded[:, k] = np.unique(
187192
y[:, k], return_inverse=True
@@ -504,7 +509,7 @@ class DecisionTreeRegressor(RegressorMixin, BaseDecisionTree):
504509
def __init__(
505510
self,
506511
*,
507-
criterion="mse",
512+
criterion="squared_error",
508513
splitter="best",
509514
max_depth=None,
510515
min_samples_split=2,
@@ -580,7 +585,7 @@ class ExtraTreeRegressor(DecisionTreeRegressor):
580585
def __init__(
581586
self,
582587
*,
583-
criterion="mse",
588+
criterion="squared_error",
584589
splitter="random",
585590
max_depth=None,
586591
min_samples_split=2,

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
[build-system]
22
requires = [
3-
"setuptools<60.0",
3+
"setuptools>=61,<70",
44
"wheel",
5+
"numpy==1.26.4",
56
"Cython>=0.28.5,<3.0",
67
"oldest-supported-numpy",
78
"scipy>=1.3.2",
89
]
10+
build-backend = "setuptools.build_meta"
911
[tool.black]
1012
line-length = 79
1113
include = '\.pyi?$'

0 commit comments

Comments
 (0)