Compatibility and stability policy
Public API contract
matten exports the following public names from the crate root:
#![allow(unused)]
fn main() {
use matten::Tensor; // always
use matten::{MattenError, DataFormat}; // always
use matten::MattenLimits; // always (RFC-018)
use matten::SliceBuilder; // always; returned by Tensor::slice()
use matten::Element; // #[cfg(feature = "dynamic")]
use matten::NumericPolicy; // #[cfg(feature = "dynamic")] (RFC-017)
}
SliceBuilder is returned by Tensor::slice() and is held by value; users
do not need to import it by name in the common case.
IntoSliceRange and SliceConvert are hidden implementation plumbing for
SliceBuilder::range. They are exported #[doc(hidden)] and use a private
sealed::Sealed supertrait so downstream crates cannot meaningfully implement
them. Users never need to name them in imports.
SliceSpecRepr is #[doc(hidden)]; it is a visibility-chain artefact and
not part of the stable API.
Panic zone vs Result zone
This split is a permanent design decision and will not change:
| Zone | When | Guarantee |
|---|---|---|
| Panic | Local, trusted, literal construction | Rich matten … error in …: message |
| Result | Any external boundary (parsing, files, user shapes) | Result<Tensor, MattenError> — never panics on ordinary input |
See Error model for the full list of each zone’s APIs.
Feature flags
| Feature | Default | Stability |
|---|---|---|
serde | yes | stable |
json | yes | stable |
csv | yes | stable |
dynamic | no | stable (dynamic ingestion) |
Disabling default features is supported: default-features = false gives
the lean core. Enabling dynamic does not rename or remove any numeric Tensor API.
v0.x compatibility
matten is on the v0.x line. The policy:
- Breaking changes are allowed but must be documented in CHANGELOG.
- Public API churn decreases after each minor release.
- Feature-gated additions (new
#[cfg]methods) are not breaking. #[non_exhaustive]onMattenErrorandDataFormatmeans match arms must include a wildcard — new variants may be added without a semver break.
Family version and core dependency requirement
The published crates are released as one lock-step family (RFC-030): matching
crate versions are the supported, documented set. Downstream examples therefore
show explicit matched pins such as matten = "0.46.0" plus a companion at the
same release.
Inside the workspace, companion crates inherit the core dependency from
[workspace.dependencies] as a broad pre-1.0 requirement (matten = "0" plus a
local path). This is a maintenance policy (RFC-064): it reduces version-line
churn in member manifests while keeping release identity, maturity labels, and
user-facing install guidance tied to the lock-step family.
matten-stats — a deliberate ddof divergence (RFC-078, RFC-083, RFC-084)
matten-stats is a companion crate at production-ready candidate maturity
(RFC-084, promoted from Experimental once its six-function surface settled —
no usage-history claim is made; that is what the candidate label is for). Its
covariance and correlation functions use the sample estimator
(ddof = 1, i.e. divide by n - 1), diverging deliberately from core
matten’s population var/std (ddof = 0). This matches the
near-universal default in inferential statistics (NumPy, R, pandas) and is
the only behavioral divergence between a companion and core in the family.
correlation is unaffected by the choice — the n - 1 factors cancel — so
only covariance’s numeric output actually differs from what core’s
population convention would produce. covariance_population (RFC-083) gives
the population estimator explicitly, and skewness/kurtosis (RFC-083) each
match their own ecosystem default rather than always bias-correcting. See
RFC-078 §4.1 and RFC-083 §4.1 for the full rationale.
v1.0 requirements
v1.0.0 requires explicit maintainer confirmation. Before that can happen:
- public API review must be complete;
cargo public-apisnapshot must be taken and approved;- the panic/Result split must be finalised;
- the
serdecanonical format must be declared stable; - limitations and non-goals must be clearly documented;
- if any lock-step family crate remains
production-ready candidate, the v1.0 release RFC must include the RFC-067 family maturity table and explicitly decide that crate’s inclusion without silently promoting its maturity label.
MSRV
rust-version = "1.85" (Rust 2024 edition). The MSRV may be relaxed in a
future release; it will not be raised without a documented breaking change.
Formatting contract
Tensor implements Debug for compact, single-line inspection output, and — as of RFC-100 —
Display for a human-facing rendering: rank 0/1/2 as a right-aligned grid, rank > 2 as a flat
shape=... values=... list, truncated past 12 values/columns unless {:#} is used. See
Display / formatting for the full contract. Both are
now a documented compatibility surface — a change to either’s exact output is a breaking
change to be treated with the same care as any other public API.
Deferred items
The following items were considered and explicitly deferred:
| Item | Status | Reason |
|---|---|---|
Display for Tensor | Implemented | RFC-100: rank ≤ 2 as a right-aligned grid, rank > 2 flat; see the Formatting contract section above. |
is_empty() | Implemented (RFC-108) | len() == 0 is reachable via slicing to a zero-sized shape (e.g. t.slice().range(0..0).all().build()) and, since RFC-111, directly via any constructor (Tensor::try_new(vec![], &[0, 3]) now succeeds). is_empty() returns self.len() == 0; false for every scalar (len() == 1) and every ordinary tensor. |
set_flat | Not planned under that name | The shipped spelling is get_flat_mut — *t.get_flat_mut(i)? = v — mirroring get_flat rather than adding a set_* family; see the Mutable element API row below. |
arange max elements | 1<<20 (~1 M) | Lowered from 1<<28 in v0.12.0 for OOM safety. |
get_flat | Implemented | Tensor::get_flat(index) -> Option<f64> added in v0.11.0. |
| Negative slice indices | Supported | Shipped in 0.41.0 (RFC-088). slice_str("-1,:") takes the last row; out-of-range errors rather than clamping. slice()’s builder does not accept them. |
Step slicing ::2 | Supported | slice_str("0:10:2") grammar works. |
| Mutable element API | Supported | RFC-104. get_mut(coord) / get_flat_mut(index) on numeric tensors, mirroring get/get_flat exactly (Option<&mut f64>, same panic-on-dynamic guard). get_element_mut(coord) on dynamic tensors (Option<&mut Element>) — the caller reads, changes, or replaces the variant; the library never chooses one, so there is no coercion question. This was previously deferred on the claim that mutation needs a representation change first — that claim was wrong: Tensor already owns its Vec<f64> outright, so &mut self is exclusive by the borrow checker and there is nothing to reconcile. On a dynamic tensor whose storage is shared (e.g. an RFC-102 slice), the first write materializes it — copying into a fresh allocation and detaching from the source, so the write can never reach a shared parent. This is a no-op when already uniquely owned. One side effect worth knowing: materializing a slice releases the source’s allocation it was otherwise keeping alive — RFC-102’s retention cost arriving here as an incidental escape hatch. IndexMut, iter_mut, as_mut_slice, and set/set_flat remain deliberately out of scope — see Mutable element access for the full contract. |
| Slicing on dynamic tensors | Supported | RFC-102. slice().build() and slice_str() work on dynamic tensors, returning a dynamic tensor with is_dynamic() == true. Storage is shared, not copied (Arc::clone, RFC-012’s copy-on-write model) — slicing selects positions; it does not interpret Element values, so Text, None, and Bool survive a slice unchanged alongside Int/Float. The slice grammar and every numeric result are unchanged. Sharing has a retention cost: a slice keeps its source’s entire allocation alive for as long as the slice lives, even after the source is dropped. Release it explicitly with Tensor::from_elements(slice.to_elements(), slice.shape()). See Slicing for details. |
| Batched matmul (rank > 2) | Not planned | The boundary is deliberate, not a gap: rank > 2 exists for shape manipulation — reshape, transpose, swap_axes, stack all accept it — and arithmetic is rank ≤ 2. matmul/dot support [n]×[n], [m,n]×[n], [n]×[n,p] and [m,n]×[n,p]. A batched result would also be rank 3, which Display renders as a flat list by design (see the Formatting contract section), so the output would be less readable than looping over rank-2 multiplications. |
| Axis reductions on dynamic | Not needed yet | Convert with try_numeric() first. |
Phase status
The v0.20 family completed the materialization phase: the core numeric comfort
APIs (RFC-038 — elementwise, selection, creation, and shape helpers) and the
30_–40_ famous-problem examples program (RFC-043–048). The matten-data
CSV→tensor ingestion API first shipped in this family (RFC-034, RFC-035).
The v0.21 family delivered selective boundary implementation: shape composition
(concatenate / stack), small statistics (var / std), linalg-lite helpers
(norm / trace / outer), and the matten-data scope guard. These are additive
under lock-step family versioning (RFC-030).
The v0.22 family promotes matten-data to Beta: the RFC-036 example suite
(data_00–data_05) plus an explicit malformed-CSV test complete the documented
Beta gate (RFC-023 §9). Maturity is a per-crate Status label, not a separate version,
under lock-step family versioning (RFC-030).
The v0.23 family adds the production migration guide (RFC-050–052): when to stay vs.
migrate, per-target playbooks (ndarray, nalgebra, Polars/Pandas, Candle, NumPy), and the
bridge conversion-contract template with the matten-ndarray reference contract. This is
documentation only — no public API, runtime, or dependency change, and core matten gains
no dependency.
The v0.24 family completes the reduction surface (RFC-055 / RFC-056): every scalar value
reduction (try_sum / try_mean / try_min / try_max / try_norm) and every axis reduction
(try_sum_axis / try_mean_axis / try_min_axis / try_max_axis) now has a non-panicking
Result form, joining try_var / try_std and their axis variants. The panic forms are
unchanged in behaviour and remain convenience wrappers. These are additive under lock-step
family versioning (RFC-030); no existing signature, numeric result, output shape, NaN policy,
or dependency changes, and core matten gains no dependency.
The v0.25 family opens the companion-maturity line by promoting matten-ndarray from
production-ready candidate to production-ready (RFC-057). This is a maturity Status label
only — no API, runtime, error-variant, dependency, copy-semantics, or ndarray-version change —
and it does not imply v1.0, which still requires explicit maintainer confirmation. Under
lock-step family versioning (RFC-030) the crate stays on the shared family version. matten-mlprep
and matten-data remain at Beta pending their own maturity decisions.
The v0.26 family continues the companion-maturity line by promoting matten-mlprep from
Beta to production-ready candidate (RFC-058). Label/docs only — no API, runtime,
error-variant, or dependency change. The candidate rung reflects an honest limitation:
train_test_split is ordered-only (no shuffle), acceptable if that documented limit is
acceptable; full production-ready is deferred (RFC-058 §5.1). This does not imply v1.0.
matten-ndarray remains production-ready; matten-data remains Beta pending its own
maturity decision.
The v0.27 family completes the companion-maturity line by promoting matten-data from
Beta to production-ready candidate (RFC-059), with two promotion-blocking hygiene fixes
first (a maturity-neutral package description; required-features = ["csv"] on the data_0X
examples). Label/docs/packaging only — no API, runtime, error-variant, or dependency change, and
no scope expansion: the RFC-042 lock holds (still a CSV→tensor on-ramp, not a dataframe
engine). Full production-ready is deferred to a separate future review. This does not imply
v1.0. The ladder then read matten-ndarray production-ready, matten-mlprep and matten-data
production-ready candidates.
matten-mlprep was subsequently promoted from production-ready candidate to
production-ready (RFC-080), now that RFC-058 §5.1’s Option B exit criterion is satisfied:
RFC-077 added train_test_split_seeded, closing the ordered-only-split caveat that held the
promotion at candidate. Label/docs only — no API, runtime, error-variant, dependency, or version
change, and no other crate’s maturity changes. matten-data was production-ready candidate at this
point and was later promoted to production-ready as well (RFC-085), once RFC-059 §6’s deferred
full-production review ran; matten-stats was Experimental at this point and was later promoted
to production-ready candidate as well, once its surface settled (RFC-083, RFC-084).
The v0.28 family moves the matten-ndarray bridge to ndarray 0.17 (RFC-062): the
supported requirement changes from the 0.16 minor to 0.17 (CI targets 0.17.2). Because
to_arrayd/from_arrayd expose ndarray::ArrayD<f64>, the supported ndarray minor is part of the
bridge’s public type identity — consumers build against ndarray 0.17. (ndarray 0.17.0 is yanked;
use a non-yanked 0.17 patch.) RFC-062 first evaluated supporting 0.16 and 0.17 together via a
bounded range; the maintainer chose the single-version requirement to keep Cargo.toml simple and
readable — the architect ruling listed this as an acceptable alternative. No bridge API, behavior,
copy-semantics, error, or zero-copy change, and core matten still carries no ndarray dependency.
A public-dependency compatibility event handled as a lock-step family minor (RFC-030); it does
not imply v1.0.
The v0.29 release family adds RFC-063 visual-understanding work: Markdown / ASCII diagrams
for broadcasting, shape operations, matrix multiplication, axis reductions, statistics reductions,
dynamic readiness, and the matten-data table-to-Tensor flow; canonical visual-summary examples;
and the local-only tools/matten-report Markdown/plain-text report tool. This release does not
change public API, published crate dependency graphs, core default features, runtime behavior, MSRV,
maturity labels, or scope boundaries. Richer visualization/reporting work remains deferred and
requires separate handoffs or RFC approval.
The v0.30 release family adds RFC-065 educational visualization and tensor-learning path work on
top of the RFC-063 base: positioning guardrails, worked learner questions for shape/data meaning,
and a local-only tools/matten-report --demo educational-path report. This release remains
docs/examples/local-tool only: no public visualization/report crate, plotting dependency, expression
tracer, autograd, public API change, published dependency change, runtime behavior change, MSRV
change, or maturity-label change.
The v0.31 release family is an RFC-066/RFC-067 policy-cleanup release: the reviewed v1.0 readiness audit is recorded, BF-1 is remediated, RFC-067 resolves the MD-1 companion-maturity policy question, and the v1.0 gates now require the RFC-067 family maturity table for any future v1.0 release RFC with candidate-labeled companions. This release does not authorize v1.0 release preparation and does not change public API, published crate dependency graphs, runtime behavior, MSRV, feature flags, maturity labels, or companion scope.
The v0.36 release family adds the RFC-069 input-mode local HTML slice:
tools/matten-report can write a bounded static self-contained HTML summary for
--input <csv> --kind data-readiness --select <cols> when --format html --output <path> is explicit. Markdown/plain text remains the default report
output, HTML still requires explicit --output, and generated artifacts remain
local files rather than checked-in assets. The input-mode HTML report is
summary-only: it escapes user-controlled strings and bounds column lists, long
fields, conversion errors, and tensor previews. This release does not change
public API, published crate dependency graphs, core runtime behavior, MSRV,
feature flags, maturity labels, or companion scope. Public matten-report /
matten-viz crates, core visualization APIs, expression tracing, autograd, SVG,
Vega-Lite, JSON report output, notebook, GUI, browser-app scope, and general raw
CSV HTML rendering remain deferred.
The v0.37 release family adds the RFC-071 fixed-demo private JSON slice:
tools/matten-report can write deterministic private-local JSON artifacts for
the five fixed demos when --format json --output <path> is explicit. Markdown
remains the default report output, JSON remains local-file-only, and
schema_version: 0 means the format is not a public compatibility contract.
This release does not change public API, published crate dependency graphs,
core runtime behavior, MSRV, feature flags, maturity labels, or companion
scope. Public matten-report / matten-viz crates, public report schemas,
core visualization APIs, expression tracing, autograd, input-mode JSON, SVG,
Vega-Lite, notebook, GUI, browser-app scope, and general raw CSV JSON rendering
remain deferred.
The v0.38 release family adds the RFC-073 private input-mode JSON slice:
tools/matten-report can write a bounded private-local JSON artifact for the
existing CSV data-readiness input path when --format json --output <path> is
explicit, covering both successful strict numeric conversion and bounded
conversion-error outcomes. input_mode: "csv" extends the schema_version: 0
envelope; the five fixed-demo JSON artifacts remain byte-identical. Every
user-controlled string and list (paths, headers, column names, conversion-error
text, tensor previews) is bounded with structured truncation metadata; raw CSV
rows/cells and unselected cell values are never emitted; non-finite tensor
values are rejected before any write. This release does not change public API,
published crate dependency graphs, core runtime behavior, MSRV, feature flags,
maturity labels, or companion scope. Public matten-report / matten-viz
crates, public report schemas, input-mode JSON for other report kinds, raw CSV
export, core visualization APIs, expression tracing, autograd, SVG, Vega-Lite,
notebook, GUI, and browser-app scope remain deferred.