diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 45520b0..d6cf678 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -55,12 +55,67 @@ jobs: with: command: publish args: --username __token__ --password ${{ secrets.PYPI_TOKEN }} --interpreter python --skip-existing --manifest-path py-forust/Cargo.toml - +# "3.8", "3.9", "3.10", macos-build-test: strategy: matrix: - pyversion: ["3.8", "3.9", "3.10", "3.11", "3.12"] + pyversion: ["3.11", "3.12"] runs-on: "macos-latest" + steps: + - uses: actions/checkout@v3 + - name: Install latests stable Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.pyversion }} + architecture: x64 + - name: Install deps + run: pip install numpy pandas seaborn xgboost=='1.6.1' scikit-learn toml + - run: | + cp README.md py-forust/README.md + cp LICENSE py-forust/LICENSE + - name: Update TOML + run: python scripts/remove-optional-deps.py + - name: Build test data + run: python scripts/make_resources.py + - name: Run tests + run: cargo test --verbose + - name: Build Wheels with maturin + uses: PyO3/maturin-action@v1 + with: + target: x86_64 + command: build + args: --release --strip --interpreter python --manifest-path py-forust/Cargo.toml --out dist + - name: Install wheel + run: cd py-forust; pip install --no-deps --force-reinstall -e .; cd .. + - name: Run Package Tests + run: | + pip install pytest pytest-cov 'black>=24.0.0,<25.0.0' ruff setuptools --upgrade + cd py-forust + ruff check . + black --check . + pytest --cov-fail-under=90 tests + cd .. + - name: Save Artifacts + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist + - if: "startsWith(github.ref, 'refs/tags/')" + name: Publish Wheels + uses: PyO3/maturin-action@v1 + with: + command: publish + target: x86_64 + args: --username __token__ --password ${{ secrets.PYPI_TOKEN }} --interpreter python --skip-existing --manifest-path py-forust/Cargo.toml + + macos-13-build-test: + strategy: + matrix: + pyversion: ["3.8", "3.9", "3.10"] + runs-on: "macos-13" steps: - uses: actions/checkout@v3 - name: Install latests stable Rust @@ -111,6 +166,7 @@ jobs: target: x86_64 args: --username __token__ --password ${{ secrets.PYPI_TOKEN }} --interpreter python --skip-existing --manifest-path py-forust/Cargo.toml + linux-build-test: runs-on: ubuntu-latest strategy: diff --git a/Cargo.toml b/Cargo.toml index 1a8dba4..d096454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "forust-ml" -version = "0.4.7" +version = "0.4.8" edition = "2021" authors = ["James Inlow "] homepage = "https://github.com/jinlow/forust" diff --git a/README.md b/README.md index 1cd8670..69e83d4 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ pip install forust To use in a rust project add the following to your Cargo.toml file. ```toml -forust-ml = "0.4.7" +forust-ml = "0.4.8" ``` ## Usage diff --git a/py-forust/Cargo.toml b/py-forust/Cargo.toml index a8efa0d..def662e 100644 --- a/py-forust/Cargo.toml +++ b/py-forust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "py-forust" -version = "0.4.7" +version = "0.4.8" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,7 +10,7 @@ crate-type = ["cdylib"] [dependencies] pyo3 = { version = "0.20.0", features = ["extension-module"] } -forust-ml = { version = "0.4.7", path = "../" } +forust-ml = { version = "0.4.8", path = "../" } numpy = "0.20.0" ndarray = "0.15.1" serde_plain = { version = "1.0" } diff --git a/py-forust/forust/__init__.py b/py-forust/forust/__init__.py index 5ef05ce..5b7a258 100644 --- a/py-forust/forust/__init__.py +++ b/py-forust/forust/__init__.py @@ -95,6 +95,10 @@ def _xgboost_tree_to_nodes( ) if "leaf" not in xgb_node: buffer.extend(xgb_node["children"]) + # We can't depend on the children to be in the "depth-wise" order, sometimes they + # will be built with lossguide, in which case the order will differ. Because of this, + # We need do tresort the list, by the node number... + node_list = sorted(node_list, key=lambda n: n["num"]) # Ensure the nodeids all align with the nodes index for idx, node in enumerate(node_list): if idx != node["num"]: diff --git a/py-forust/tests/test_booster.py b/py-forust/tests/test_booster.py index 07abf69..d17cfff 100644 --- a/py-forust/tests/test_booster.py +++ b/py-forust/tests/test_booster.py @@ -1157,6 +1157,28 @@ def test_booster_to_xgboosts_with_contributions_shapley_from_xgboost(X_y): assert np.allclose(fmod_preds, xmod.predict(X, output_margin=True), atol=0.00001) +@pytest.mark.parametrize("growth_policy", ["depthwise", "lossguide"]) +def test_from_xgboost(X_y, growth_policy): + X, y = X_y + X = X.astype(np.float32) + xmod = XGBClassifier( + n_estimators=200, + learning_rate=0.03, + max_depth=10, + objective="binary:logitraw", + eval_metric="auc", + tree_method="hist", + base_score=0.5, + growth_policy=growth_policy, + ) + xmod.fit(X, y) + + fmod = forust._from_xgboost_model(xmod) + assert np.allclose( + xmod.predict(X, output_margin=True), fmod.predict(X), atol=0.00001 + ) + + def test_missing_branch_with_contributions(X_y): X, y = X_y X = X diff --git a/rs-example.md b/rs-example.md index 8737067..7e90bd8 100644 --- a/rs-example.md +++ b/rs-example.md @@ -3,7 +3,7 @@ To run this example, add the following code to your `Cargo.toml` file. ```toml [dependencies] -forust-ml = "0.4.7" +forust-ml = "0.4.8" polars = "0.28" reqwest = { version = "0.11", features = ["blocking"] } ```