# 1.1. What is RustyML
RustyML is a machine learning and deep learning library written entirely in Rust. It covers the full workflow a data-science project needs: data preprocessing, feature engineering, model training, and evaluation. It provides classical machine-learning estimators (linear models, decision trees, SVMs, clustering, dimensionality reduction, anomaly detection) as well as a Keras-style neural-network framework.
This guide documents version `0.16`. The API is stabilizing, but minor releases can still introduce breaking changes, so pin a concrete version in `Cargo.toml` for production rather than tracking `*`. The authoritative API reference is at [docs.rs/rustyml](https://docs.rs/rustyml), and the source at [github.com/SomeB1oody/RustyML](https://github.com/SomeB1oody/RustyML).
## 1.1.1. Pure Rust, end to end
RustyML contains no C or C++ code: no BLAS to link, no LAPACK, no CUDA. That makes it highly portable. It spares you from configuring a complicated environment by hand, and it keeps you from hitting inscrutable errors at build time. This suits both production and newcomers. Safe Rust makes up most of the code, so the compiler guarantees memory safety.
For performance, matrix multiplication goes through the pure-Rust [`gemmkit`](https://crates.io/crates/gemmkit) crate (reaching `ndarray` via the zero-copy [`gemmkit-ndarray`](https://crates.io/crates/gemmkit-ndarray) adapter). It dispatches at run time to the widest SIMD the current CPU actually supports (AVX-512F, AVX2+FMA, NEON, wasm `simd128`, with a scalar fallback). It also decides on its own whether a given product is worth threading, and across how many workers.
## 1.1.2. Parallelism
RustyML parallelizes its compute-heavy kernels with Rayon, but never blindly. Below a certain size, the overhead of multithreading makes the parallel path slower than the serial one. Every kernel class therefore has a calibrated size threshold, and it switches to the parallel path only once parallel is measurably faster.
Those thresholds are not hardcoded constants. `rustyml::tuning` lets you override them at run time without a recompile. This matters most when you deploy the same binary to machines with very different core counts. See [Performance Tuning and Parallelism](../Chapter-07/7.3._Performance_Tuning_and_Parallelism.md) for the details.
What the design buys you:
- Parallel reductions are *deterministic*. The blocked fold sums in a fixed order no matter how many threads run it, so results do not drift as you scale cores.
- Performance is predictable: no garbage-collection pauses, no JIT warmup, and no global interpreter lock serializing your threads.
- Nearly every randomized component honors a global seed (see [Reproducibility and Random Seeds](../Chapter-07/7.1._Reproducibility_and_Random_Seeds.md)), so a run reproduces across machines. The dimensionality reducers' iterative eigensolvers are deliberately left out, because they converge to the same result whatever the seed is.
## 1.1.3. Features and modules
RustyML is split into 5 modules, each controlled by a Cargo feature (`prelude` is shared). Naming features lets you compile only the parts you use. `machine_learning`, `neural_network`, `utils`, and `metrics` all enable `math` automatically.
| `machine_learning` | Classical machine-learning estimators |
| `neural_network` | The `Sequential` model plus layers (`Dense`, convolution, pooling, recurrent, dropout, normalization), activations, optimizers (`SGD`, `Adam`, `AdamW`, `RMSprop`, `AdaGrad`), and losses |
| `utils` | Preprocessing (the `StandardScaler` family of scalers, label helpers such as `to_categorical`) and dataset splitting (`train_test_split`, `train_test_split_stratified`) |
| `metrics` | Evaluation metrics for regression, classification (`ConfusionMatrix`, ROC AUC, log loss, ...), and clustering (ARI, silhouette, ...) |
| `math` | Numeric computation and `gemmkit`-backed matrix products |
The `default` feature turns everything on. There is also a separate `show_progress` feature that draws training progress bars. See [Installation and Feature Flags](./1.2._Installation_and_Feature_Flags.md) for the details.
## 1.1.4. An API modeled on scikit-learn and Keras
Classical estimators follow scikit-learn, exposing methods like `fit` and `predict`. The neural network follows Keras's `Sequential` model, with its `add` / `build` / `compile` / `fit` / `predict` flow. This makes it easier to pick up if you know the Python data-science ecosystem. What changes is that data is `ndarray` arrays rather than NumPy (see [Working with ndarray](./1.3._Working_with_ndarray.md)), and fallible calls return `Result` instead of raising exceptions.
Here is a classical machine-learning example. You can see the API shape follows scikit-learn:
```rust
use rustyml::prelude::machine_learning::*;
use ndarray::array;
fn main() {
// new(fit_intercept); the default solver is the exact closed form
let mut model = LinearRegression::new(true);
let x = array![[1.0, 2.0], [2.0, 3.0], [3.0, 4.0]];
let y = array![6.0, 9.0, 12.0];
model.fit(&x, &y).unwrap();
let predictions = model.predict(&x).unwrap();
println!("predictions: {:?}", predictions);
}
```
And here is neural-network code, whose architecture follows Keras:
```rust
use rustyml::prelude::neural_network::*;
use ndarray::Array;
fn main() {
// 4 samples, 8 input features, 1 output
let x = Array::ones((4, 8)).into_dyn();
let y = Array::ones((4, 1)).into_dyn();
// The builder collects the layers, and `build` gives every layer the shape
// that reaches it. A layer names its own output width alone.
let mut model = SequentialBuilder::new()
.add(Dense::new(16, Activation::ReLU).unwrap())
.add(Dense::new(1, Activation::Linear).unwrap())
.build(&Shape::known(&[4, 8]))
.unwrap();
model.compile(
Adam::new(0.001, 0.9, 0.999, 1e-8, 0.0).unwrap(),
MeanSquaredError::new(),
);
model.summary(); // prints the architecture, just like Keras
model.fit(&x, &y, 5).unwrap();
let predictions = model.predict(&x).unwrap();
println!("prediction shape: {:?}", predictions.shape());
}
```
The metrics keep the same design as scikit-learn too (every metric takes its arguments in `(y_true, y_pred)` order):
```rust
use rustyml::metrics::*;
use ndarray::array;
fn main() {
let y_true = array![1.0, 0.0, 0.0, 1.0, 1.0];
let y_pred = array![1.0, 0.0, 1.0, 1.0, 0.0];
let cm = ConfusionMatrix::new(&y_true, &y_pred);
println!("accuracy: {:.3}", cm.accuracy());
println!("f1 score: {:.3}", cm.f1_score());
}
```
Unlike Python, RustyML wraps the result of a fallible call in `Result<T, Error>`. You can `match` on it to handle each outcome separately: either it succeeded and gives you `T`, or it failed and gives you an `Error`. See [Error Handling](./1.6._Error_Handling.md).
RustyML validates hyperparameters at the point you supply them, and it rejects an illegal value on the spot. Configuration uses the builder pattern. An estimator names its essential hyperparameters in `new`, then layers optional settings through chained `with_*` methods. Each method validates what it receives, for example `LinearRegression::new(true).with_regularization(..)?`.
## 1.1.5. What pure Rust buys you
A RustyML program compiles to a single self-contained binary. There is no extra toolchain to install and no complicated environment to configure. Trained classical models and neural-network weights serialize to binary through `save_to_path` / `load_from_path`. See [Model Persistence in Depth](../Chapter-07/7.2._Model_Persistence_in_Depth.md) for the details. And because there is no GC, no interpreter, and no warmup, latency is predictable.
## 1.1.6. Notes on scope
RustyML is CPU-only. There is no GPU or CUDA backend. The neural-network framework suits small-to-medium models and deep learning that sits close to classical ML. It is not for training large vision or language models.
The classical `machine_learning` and `utils` estimators all take an `f64` feature matrix, but the element type of what `predict` gives back varies by model. See the table in [Working with ndarray](./1.3._Working_with_ndarray.md). The neural-network stack works in `f32`, and its tensor type is `Tensor = ArrayD<f32>`. The framework does not build a dynamic autodiff graph the way PyTorch does.