This is the second of four short posts about matten. The first post explained the motivation. This one shows what the library looks like in practice.
Getting started
# Cargo.toml
[dependencies]
matten = "0.28"
The default feature set includes serde, json, and csv. If you want the smallest possible dependency footprint, you can turn them off:
matten = { version = "0.28", default-features = false }
Creating tensors
The whole import is use matten::Tensor;. No generic parameters, no lifetime annotations.
use matten::Tensor;
// From data and an explicit shape
let a = Tensor::new(vec![1.0, 2.0, 3.0, 4.0], &[2, 2]);
assert_eq!(a.shape(), &[2, 2]);
assert_eq!(a.ndim(), 2);
// Convenience constructors
let z = Tensor::zeros(&[3, 3]);
let o = Tensor::ones(&[3, 3]);
let f = Tensor::full(&[2, 4], 5.0);
Shape mismatches produce an actionable error rather than a panic when you use the boundary-style constructor:
use matten::{MattenError, Tensor};
let result = Tensor::try_new(vec![1.0, 2.0, 3.0], &[2, 2]);
assert!(matches!(result, Err(MattenError::Shape { .. })));
Arithmetic and broadcasting
The operators work on references, so you keep ownership of the originals. Shape broadcasting follows NumPy-style right-alignment rules.
use matten::Tensor;
let a = Tensor::new(vec![1.0, 2.0, 3.0, 4.0], &[2, 2]);
let b = Tensor::ones(&[2, 2]);
let c = &a + &b; // [2.0, 3.0, 4.0, 5.0]
let d = &a * 2.0; // scalar broadcast: [2.0, 4.0, 6.0, 8.0]
// Broadcasting a row across a matrix
let row = Tensor::new(vec![1.0, 2.0], &[1, 2]);
let mat = Tensor::ones(&[3, 2]);
let result = &mat + &row; // shape [3, 2]
Shape operations
use matten::Tensor;
let t = Tensor::new(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0], &[2, 3]);
let flat = t.flatten(); // shape [6]
let reshaped = t.reshape(&[3, 2])?;
let transposed = t.transpose()?; // shape [3, 2]
// Reductions
let s = t.sum(); // scalar
let m = t.mean()?;
let col_sums = t.sum_axis(0)?; // shape [3]
JSON and CSV
Both are on by default. The API returns Result at the boundary, so a malformed input gives you an error rather than a panic.
use matten::Tensor;
// JSON — two accepted forms
let t = Tensor::from_json(r#"{"shape":[2,2],"data":[1.0,2.0,3.0,4.0]}"#)?;
let t = Tensor::from_json("[[1.0, 2.0], [3.0, 4.0]]")?;
// From a file
let t = Tensor::load_json("data/tensor.json")?;
// CSV
let t = Tensor::from_csv("1.0,2.0,3.0\n4.0,5.0,6.0\n")?;
let t = Tensor::load_csv("data/matrix.csv")?;
Serialisation goes through serde, so serde_json::to_string(&t) and serde_json::from_str(&json_str) round-trip correctly when the json or serde feature is active.
Error handling
matten has two deliberate error zones.
- Internal shape operations (constructing from
new, reshaping, slicing) panic with an actionable message — useful during fast prototyping because you see the problem immediately. - External boundary operations (
from_json,from_csv,load_*) always returnResult<Tensor, MattenError>, because real input data is not always clean.-
MattenErroris#[non_exhaustive], so match on the variant you care about and use a wildcard for the rest:
-
use matten::{MattenError, Tensor};
match Tensor::from_csv("1.0,not_a_number\n") {
Ok(t) => println!("got shape {:?}", t.shape()),
Err(MattenError::Parse { .. }) => println!("bad input"),
Err(e) => println!("other error: {e:?}"),
}
That covers the everyday numeric core. The next post covers something different: what happens when the input data is not a clean f64 matrix — when it has mixed types, missing values, or integers alongside floats.
Links: crates.io · docs.rs · mdBook · repository
Top comments (0)