Fetching latest headlines…
matten: The core `Tensor`
NORTH AMERICA
🇺🇸 United StatesJune 29, 2026

matten: The core `Tensor`

0 views0 likes0 comments
Originally published byDev.to

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 return Result<Tensor, MattenError>, because real input data is not always clean.

MattenError is #[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

Comments (0)

Sign in to join the discussion

Be the first to comment!