diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7715aed7..71bbc182 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,7 @@ jobs: - "--features serde" - "--features datasets" - "--features ndarray-bindings" + - "--features std,ndarray-bindings" - "" steps: - uses: actions/checkout@v4 @@ -72,9 +73,39 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: Stable Build run: cargo build --no-default-features ${{ matrix.features }} + - name: Assert ndarray feature resolution + shell: bash + run: | + case "${{ matrix.features }}" in + "--features ndarray-bindings") + ci/check-ndarray-features.sh no-std --no-default-features ${{ matrix.features }} + ;; + "--features std,ndarray-bindings") + ci/check-ndarray-features.sh std --no-default-features ${{ matrix.features }} + ;; + "") + ci/check-ndarray-features.sh absent --no-default-features + # Default features and explicit std must not activate ndarray. + ci/check-ndarray-features.sh absent + ci/check-ndarray-features.sh absent --no-default-features --features std + # Default plus bindings keeps ndarray's std feature on. + ci/check-ndarray-features.sh std --features default,ndarray-bindings + ;; + *) + ci/check-ndarray-features.sh absent --no-default-features ${{ matrix.features }} + ;; + esac - name: Tests - if: matrix.features == '--features ndarray-bindings' + if: matrix.features == '--features ndarray-bindings' || matrix.features == '--features std,ndarray-bindings' run: cargo test --no-default-features ${{ matrix.features }} + - name: Explicit std does not activate ndarray + if: matrix.features == '' + run: cargo build --no-default-features --features std + - name: Default plus ndarray-bindings + if: matrix.features == '' + run: | + cargo build --features default,ndarray-bindings + cargo test --features default,ndarray-bindings msrv: # Verify the declared rust-version (MSRV) in Cargo.toml still builds. diff --git a/CHANGELOG.md b/CHANGELOG.md index c250a5db..7d0dfa47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.0] +### Changed +- `Cargo.toml`: optional `ndarray` is now `default-features = false`, and the default feature set is `std` (`ndarray?/std`). Ordinary builds still enable ndarray's standard-library support when `ndarray-bindings` is on, while `std` alone does not activate ndarray. The `std` feature only controls ndarray's standard-library support; smartcore itself is not `no_std` (#421). + - **Migration:** builds that use `--no-default-features --features ndarray-bindings` (or `default-features = false` with `ndarray-bindings`) no longer get ndarray's `std` feature. Add `std` to the feature list to keep the previous behavior. + ## [0.6.16] ### Fixed - `predict`, `predict_proba`, `predict_oob`, `predict_matrix` and `decision_function` now return `Err(Failed)` instead of panicking when `fit` has not run (#469, #470). Models return the same unfitted error even after deserialization, when the state fields are `None`. The guarded methods cover the decision trees, the random forests, the extra-trees regressor, the KNN classifier and regressor, `SVC`, `MultiClassSVC`, `SVR`, the linear models, the naive Bayes classifiers and `XGRegressor`. diff --git a/Cargo.toml b/Cargo.toml index 4ec3a4b5..b94a1a0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ exclude = [ [dependencies] approx = "0.5.1" cfg-if = "1.0.0" -ndarray = { version = "0.17", optional = true } +ndarray = { version = "0.17", optional = true, default-features = false } num-traits = "0.2.12" num = "0.4" rand = { version = "0.10.1", default-features = false, features = ["alloc"] } @@ -35,9 +35,17 @@ ordered-float = "5.1.0" typetag = { version = "0.2", optional = true } [features] -# No features enabled by default; keeps the build WASM-compatible -# and avoids pulling in serde/rand unless explicitly requested. -default = [] +# `std` stays on by default so ordinary builds keep ndarray's standard-library +# support when `ndarray-bindings` is enabled. Weak forwarding (`ndarray?/std`) +# does not activate the optional ndarray dependency by itself. `std` only +# controls ndarray's standard-library support; smartcore itself is not +# `no_std`. serde and rand stay opt-in, which keeps the default dependency set +# WASM-compatible. +default = ["std"] + +# Enable ndarray's `std` feature when ndarray is already enabled. +# The `?` keeps `--features std` from pulling ndarray in on its own. +std = ["ndarray?/std"] # Enable serde Serialize/Deserialize for models and related types. # Also enables typetag on non-wasm targets for trait object serialization. diff --git a/ci/check-ndarray-features.sh b/ci/check-ndarray-features.sh new file mode 100755 index 00000000..0737f671 --- /dev/null +++ b/ci/check-ndarray-features.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Assert how ndarray resolves for a set of cargo feature flags. +# Usage: ci/check-ndarray-features.sh [cargo feature flags...] +set -euo pipefail + +expect="$1" +shift + +# `-i ndarray --depth 0` prints only the ndarray node, as "ndarray vX.Y.Z|feat1,feat2". +# Dev-dependency edges are excluded so test-only crates cannot affect the result. +if ! line="$(cargo tree -q -e normal,build -i ndarray --depth 0 --format '{p}|{f}' "$@" 2>&1)"; then + case "$line" in + *"did not match any packages"*) line="" ;; + *) printf '%s\n' "$line" >&2; exit 1 ;; + esac +fi + +if [ -z "$line" ]; then + actual="absent" +elif [[ ",${line#*|}," == *",std,"* ]]; then + actual="std" +else + actual="no-std" +fi + +if [ "$actual" != "$expect" ]; then + echo "ndarray with [$*]: expected $expect, got $actual (${line:-not in graph})" >&2 + exit 1 +fi +echo "ndarray with [$*]: $actual"