Repository: statrs-dev/statrs (language: rust)

Issue:
Adding Feature Flags and optional dependency? 
Hi,

This is an amazing package with high quality code. However, I find it hard to include it in my work for the following reasons:

1. I am trying to slim down my binary and if more statistical distributions are needed, I want to add them incrementally by adding more features.
2. For my purpose, I almost never need multivariable distributions. I am already depending on Faer crate for linear algebra and I do not want another package for the same stuff. Again, I think nalgebra should be an optional dependency.

I am using statrs's code right now, which I copied. I am creditting the project. I mostly only need survival functions so I stripped down the code. 

You can find my project here: https://github.com/abstractqqq/polars_ds_extension



Discussion / hints:
I'd consider multivariate as a feature; nalgebra is a big compile compared to the rest of statrs, @henryjac thoughts?

@abstractqqq Are there not other options than per distribution features for slimming down binary size for your cdylib? I don't know much about it, but is there not a way to ensure your API can't expose certain parts of `statrs` in a way that `rustc` can figure out what doesn't need to be built into the binary?

Additionally, as I have minimal exposure to building a library to call from C, I'd deeply appreciate reading suggestions for my learning. 
Thank you for responding. I am also not an expert in compilation. Still, I think nalgebra is only used in multivariate case and so multivariate can be behind a feature flag... Coming from Python, the pain of depending on both tensorflow and pytorch is just too much... It would be great if there is a "NumPy" in Rust and then it can be added as a dependency without question. But right now I think for higher dimension matrix and linear algebra, Faer is doing better than nalgebra...
FYI, PyO3 is a great project, and they have [bindings](https://github.com/PyO3/rust-numpy) for the numpy C API

As far as a decision on feature flag, I'm interested, but I want to get some other's perspectives as well.

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index ab92cb19..b6277e90 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,13 +17,26 @@ include = ["CHANGELOG.md", "LICENSE.md", "src/", "tests/"]
 name = "statrs"
 path = "src/lib.rs"
 
+[features]
+default = ["nalgebra"]
+nalgebra = ["dep:nalgebra"]
+
 [dependencies]
 rand = "0.8"
-nalgebra = { version = "0.32", default-features = false, features = ["rand", "std"] }
 approx = "0.5.0"
 num-traits = "0.2.14"
 
+[dependencies.nalgebra]
+version = "0.32"
+optional = true
+default-features = false
+features = ["rand", "std"]
+
 [dev-dependencies]
 criterion = "0.3.3"
 anyhow = "1.0"
-nalgebra = { version = "0.32", default-features = false, features = ["macros"] }
+
+[dev-dependencies.nalgebra]
+version = "0.32"
+default-features = false
+features = ["macros"]
diff --git a/src/distribution/internal.rs b/src/distribution/internal.rs
index 0837dc40..72c86e28 100644
--- a/src/distribution/internal.rs
+++ b/src/distribution/internal.rs
@@ -1,8 +1,5 @@
-use nalgebra::{Dim, OVector};
 use num_traits::Num;
 
-use crate::StatsError;
-
 /// Returns true if there are no elements in `x` in `arr`
 /// such that `x <= 0.0` or `x` is `f64::NAN` and `sum(arr) > 0.0`.
 /// IF `incl_zero` is true, it tests for `x < 0.0` instead of `x <= 0.0`
@@ -17,11 +14,17 @@ pub fn is_valid_multinomial(arr: &[f64], incl_zero: bool) -> bool {
     sum != 0.0
 }
 
+#[cfg(feature = "nalgebra")]
+use nalgebra::{Dim, OVector};
+
+#[cfg(feature = "nalgebra")]
 pub fn check_multinomial<D>(arr: &OVector<f64, D>, accept_zeroes: bool) -> crate::Result<()>
 where
     D: Dim,
     nalgebra::DefaultAllocator: nalgebra::allocator::Allocator<f64, D>,
 {
+    use crate::StatsError;
+
     if arr.len() < 2 {
         return Err(StatsError::BadParams);
     }
@@ -253,6 +256,7 @@ pub mod test {
         check_sum_pmf_is_cdf(dist, x_max);
     }
 
+    #[cfg(feature = "nalgebra")]
     #[test]
     fn test_is_valid_multinomial() {
         use std::f64;
diff --git a/src/distribution/mod.rs b/src/distribution/mod.rs
index b146f9d4..6e43db8e 100644
--- a/src/distribution/mod.rs
+++ b/src/distribution/mod.rs
@@ -13,6 +13,7 @@ pub use self::cauchy::Cauchy;
 pub use self::chi::Chi;
 pub use self::chi_squared::ChiSquared;
 pub use self::dirac::Dirac;
+#[cfg(feature = "nalgebra")]
 pub use self::dirichlet::Dirichlet;
 pub use self::discrete_uniform::DiscreteUniform;
 pub use self::empirical::Empirical;
@@ -25,8 +26,11 @@ pub use self::hypergeometric::Hypergeometric;
 pub use self::inverse_gamma::InverseGamma;
 pub use self::laplace::Laplace;
 pub use self::log_normal::LogNormal;
+#[cfg(feature = "nalgebra")]
 pub use self::multinomial::Multinomial;
+#[cfg(feature = "nalgebra")]
 pub use self::multivariate_normal::MultivariateNormal;
+#[cfg(feature = "nalgebra")]
 pub use self::multivariate_students_t::MultivariateStudent;
 pub use self::negative_binomial::NegativeBinomial;
 pub use self::normal::Normal;
@@ -45,6 +49,7 @@ mod cauchy;
 mod chi;
 mod chi_squared;
 mod dirac;
+#[cfg(feature = "nalgebra")]
 mod dirichlet;
 mod discrete_uniform;
 mod empirical;
@@ -59,8 +64,11 @@ mod internal;
 mod inverse_gamma;
 mod laplace;
 mod log_normal;
+#[cfg(feature = "nalgebra")]
 mod multinomial;
+#[cfg(feature = "nalgebra")]
 mod multivariate_normal;
+#[cfg(feature = "nalgebra")]
 mod multivariate_students_t;
 mod negative_binomial;
 mod normal;


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 5c031691..2b39f41e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -23,6 +23,9 @@ jobs:
       - name: Run cargo clippy
         run: cargo clippy --all-targets
 
+      - name: Run cargo clippy without default features
+        run: cargo clippy --no-default-features --all-targets
+
   fmt:
     runs-on: ubuntu-latest
     steps:
@@ -48,5 +51,5 @@ jobs:
         uses: dtolnay/rust-toolchain@stable
 
       - name: Test default features
-        run: cargo test --all-targets
+        run: cargo test