Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions .agents/skills/tenferro-compute/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,19 @@ not when changing tenferro itself.

## Non-negotiable defaults

- Dense flat buffers are column-major: the leftmost dimension varies fastest.
- Bind one backend/runtime/compiler and reuse it across related work.
- Compile traced programs once outside repeated execution loops.
- **Column-major storage.** Dense buffers are column-major: the leftmost
dimension varies fastest. Row-major data passed to `from_vec_col_major` is
silently reinterpreted as column-major — permuted/wrong values, never
rejected.
- **No facade crate.** `cargo add tenferro` fails by design; depend on the
crates you need (`tenferro-runtime`, `tenferro-cpu`, and operation crates).
- **Explicit backend.** Direct operations take an explicit backend argument;
construct the backend/runtime once and reuse it — per-call construction
discards the buffer pool.
- **Einsum dialect.** Equations need the explicit arrow (`"ij,jk->ik"`); `...`
ellipsis is unsupported.
- **Result-returning operators.** Traced operators return `Result`; propagate
with `?`.
- CPU/GPU transfers are explicit; unsupported GPU operations do not silently
fall back to CPU.
- Traced standard extensions need an explicitly installed extension module and
Expand Down
44 changes: 44 additions & 0 deletions .agents/skills/tenferro-compute/references/api-cheatsheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,47 @@ Import recipes:

If a method is present in the guide but Rust reports E0599, check the owning
crate's root re-exports and bring its `*Ext` trait into the local module.

## Borrowing external memory

Wrap an existing column-major buffer (a `faer::Mat`, an ndarray view, or any
slice plus strides) zero-copy with `TypedTensorView::from_slice`; use
`TypedTensorViewMut::from_slice` to write through the borrowed buffer. This is
how a tenferro kernel consumes memory owned by another library without a
migration: no tensor ownership is transferred.

<!-- snippet-source: docs/tutorial-code/src/bin/tenferro_compute_skill.rs#borrowing-external-memory -->
```rust
use tenferro_runtime::TypedTensorView;
use tenferro_tensor::TypedTensorViewMut;

// Wrap a column-major faer::Mat without copying. faer pads columns to
// alignment, so the borrowed slice spans `col_stride * ncols` elements and the
// column stride is passed explicitly; the padding is never read logically.
let mat = faer::Mat::from_fn(2, 3, |r, c| (r * 3 + c) as f64);
let data = unsafe { std::slice::from_raw_parts(mat.as_ref().as_ptr(), (mat.col_stride() as usize) * mat.ncols()) };
let view = TypedTensorView::from_slice(vec![2, 3], vec![1_isize, mat.col_stride()], 0, data)?;
assert_eq!(view.get(&[1, 2]), Some(&5.0));

// Wrap an ndarray row-major view the same way. Strides are arbitrary, so a
// row-major buffer is not transposed; it is only wrapped.
let arr = ndarray::Array2::from_shape_vec((2, 3), (0..6).map(|i| i as f64).collect())?;
let nview = TypedTensorView::from_slice(arr.shape(), arr.strides(), 0, arr.as_slice().expect("row-major array is contiguous"))?;
assert_eq!(nview.get(&[0, 2]), Some(&2.0));

// The mutable variant writes through the borrowed buffer.
let mut buffer = [0.0_f64, 0.0, 0.0, 0.0];
let mut mview = TypedTensorViewMut::from_slice(vec![2, 2], vec![1, 2], 0, &mut buffer)?;
*mview.get_mut(&[0, 0]).expect("in-bounds index") = 7.0;
assert_eq!(buffer[0], 7.0);
```
<!-- end-snippet-source -->

Because strides are arbitrary, row-major data wraps without transposition —
but kernels are tuned for column-major contiguity. Materialize a copy when the
wrapped buffer is row-major or its lifetime ends before the operation;
`TypedTensorView::duplicate()` requires a column-major-contiguous view, so for
a row-major wrap copy into a fresh `TypedTensor` (or an owned `Vec` in
column-major order) instead. If you search for the retired
`TypedStridedTensorView` (tenferro-rs#886), stop: `TypedTensorView::from_slice`
is its successor.
45 changes: 45 additions & 0 deletions .agents/skills/tenferro-compute/references/pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,48 @@ is in the [traced API example](api-cheatsheet.md#traced-tensors-and-extensions).
`blas-accelerate`) when using `cpu-blas`.
- CUDA is explicit: enable `tenferro-gpu`'s `cuda` feature and upload CPU
tensors before CUDA operations. There is no implicit transfer.

## Per-origin traps

Same traps, keyed by the priors you arrived with.

### If you come from ndarray / NumPy

- Your buffers are row-major; passing them to `from_vec_col_major` silently
reinterprets them as column-major (no error, permuted/wrong values). Reorder
explicitly or wrap with
`TypedTensorView::from_slice` (see the [API cheatsheet](api-cheatsheet.md#borrowing-external-memory)).
- `cargo add tenferro` fails: there is no facade crate. Add
`tenferro-runtime` + `tenferro-cpu` (plus operation crates).
- `Array2::dot` is a free-standing habit; tenferro direct ops take an explicit
`&mut CpuBackend` argument: `a.matmul(&b, &mut backend)`.
- Your priors do not include a dtype-erased tensor; `Tensor` (runtime dtype)
has no ndarray counterpart — use `TypedTensor<T>`.

### If you come from nalgebra

- nalgebra is column-major like tenferro, so `.data` / `as_slice()` buffers map
directly to `from_vec_col_major`.
- `Matrix::dot` / `gemm` are methods without an execution context; tenferro
direct ops need the explicit backend argument. Eager and traced tiers drop
it: `EagerTensor` methods run through the `EagerRuntime`, traced methods
build a graph.
- A single `DMatrix<T>` maps to `TypedTensor<T>`; there is no separate runtime
dtype type unless you actually need runtime dtype selection.

### If you come from PyTorch / JAX

- Trace one level of `?`: traced operators return `Result`; `a.matmul(&b)` is
a `Result`, unlike `torch.matmul` / `jnp.matmul`.
- Einsum needs the explicit arrow and rejects `...` (see
[Einsum syntax](#einsum-syntax)).
- The backend is not ambient: eager code must own an `EagerRuntime`, traced
code must register an engine plus extension modules (see
[Extension registration](#extension-registration)).

## Retired names

Searching for the retired `TypedStridedTensorView` (tenferro-rs#886) leads
nowhere: the name survives only in the obsolete-names vocabulary test. The
zero-copy view API is `TypedTensorView::from_slice` / `TypedTensorViewMut::from_slice`
(see the [API cheatsheet](api-cheatsheet.md#borrowing-external-memory)).
16 changes: 13 additions & 3 deletions .claude/skills/tenferro-compute/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,19 @@ not when changing tenferro itself.

## Non-negotiable defaults

- Dense flat buffers are column-major: the leftmost dimension varies fastest.
- Bind one backend/runtime/compiler and reuse it across related work.
- Compile traced programs once outside repeated execution loops.
- **Column-major storage.** Dense buffers are column-major: the leftmost
dimension varies fastest. Row-major data passed to `from_vec_col_major` is
silently reinterpreted as column-major — permuted/wrong values, never
rejected.
- **No facade crate.** `cargo add tenferro` fails by design; depend on the
crates you need (`tenferro-runtime`, `tenferro-cpu`, and operation crates).
- **Explicit backend.** Direct operations take an explicit backend argument;
construct the backend/runtime once and reuse it — per-call construction
discards the buffer pool.
- **Einsum dialect.** Equations need the explicit arrow (`"ij,jk->ik"`); `...`
ellipsis is unsupported.
- **Result-returning operators.** Traced operators return `Result`; propagate
with `?`.
- CPU/GPU transfers are explicit; unsupported GPU operations do not silently
fall back to CPU.
- Traced standard extensions need an explicitly installed extension module and
Expand Down
44 changes: 44 additions & 0 deletions .claude/skills/tenferro-compute/references/api-cheatsheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,47 @@ Import recipes:

If a method is present in the guide but Rust reports E0599, check the owning
crate's root re-exports and bring its `*Ext` trait into the local module.

## Borrowing external memory

Wrap an existing column-major buffer (a `faer::Mat`, an ndarray view, or any
slice plus strides) zero-copy with `TypedTensorView::from_slice`; use
`TypedTensorViewMut::from_slice` to write through the borrowed buffer. This is
how a tenferro kernel consumes memory owned by another library without a
migration: no tensor ownership is transferred.

<!-- snippet-source: docs/tutorial-code/src/bin/tenferro_compute_skill.rs#borrowing-external-memory -->
```rust
use tenferro_runtime::TypedTensorView;
use tenferro_tensor::TypedTensorViewMut;

// Wrap a column-major faer::Mat without copying. faer pads columns to
// alignment, so the borrowed slice spans `col_stride * ncols` elements and the
// column stride is passed explicitly; the padding is never read logically.
let mat = faer::Mat::from_fn(2, 3, |r, c| (r * 3 + c) as f64);
let data = unsafe { std::slice::from_raw_parts(mat.as_ref().as_ptr(), (mat.col_stride() as usize) * mat.ncols()) };
let view = TypedTensorView::from_slice(vec![2, 3], vec![1_isize, mat.col_stride()], 0, data)?;
assert_eq!(view.get(&[1, 2]), Some(&5.0));

// Wrap an ndarray row-major view the same way. Strides are arbitrary, so a
// row-major buffer is not transposed; it is only wrapped.
let arr = ndarray::Array2::from_shape_vec((2, 3), (0..6).map(|i| i as f64).collect())?;
let nview = TypedTensorView::from_slice(arr.shape(), arr.strides(), 0, arr.as_slice().expect("row-major array is contiguous"))?;
assert_eq!(nview.get(&[0, 2]), Some(&2.0));

// The mutable variant writes through the borrowed buffer.
let mut buffer = [0.0_f64, 0.0, 0.0, 0.0];
let mut mview = TypedTensorViewMut::from_slice(vec![2, 2], vec![1, 2], 0, &mut buffer)?;
*mview.get_mut(&[0, 0]).expect("in-bounds index") = 7.0;
assert_eq!(buffer[0], 7.0);
```
<!-- end-snippet-source -->

Because strides are arbitrary, row-major data wraps without transposition —
but kernels are tuned for column-major contiguity. Materialize a copy when the
wrapped buffer is row-major or its lifetime ends before the operation;
`TypedTensorView::duplicate()` requires a column-major-contiguous view, so for
a row-major wrap copy into a fresh `TypedTensor` (or an owned `Vec` in
column-major order) instead. If you search for the retired
`TypedStridedTensorView` (tenferro-rs#886), stop: `TypedTensorView::from_slice`
is its successor.
45 changes: 45 additions & 0 deletions .claude/skills/tenferro-compute/references/pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,48 @@ is in the [traced API example](api-cheatsheet.md#traced-tensors-and-extensions).
`blas-accelerate`) when using `cpu-blas`.
- CUDA is explicit: enable `tenferro-gpu`'s `cuda` feature and upload CPU
tensors before CUDA operations. There is no implicit transfer.

## Per-origin traps

Same traps, keyed by the priors you arrived with.

### If you come from ndarray / NumPy

- Your buffers are row-major; passing them to `from_vec_col_major` silently
reinterprets them as column-major (no error, permuted/wrong values). Reorder
explicitly or wrap with
`TypedTensorView::from_slice` (see the [API cheatsheet](api-cheatsheet.md#borrowing-external-memory)).
- `cargo add tenferro` fails: there is no facade crate. Add
`tenferro-runtime` + `tenferro-cpu` (plus operation crates).
- `Array2::dot` is a free-standing habit; tenferro direct ops take an explicit
`&mut CpuBackend` argument: `a.matmul(&b, &mut backend)`.
- Your priors do not include a dtype-erased tensor; `Tensor` (runtime dtype)
has no ndarray counterpart — use `TypedTensor<T>`.

### If you come from nalgebra

- nalgebra is column-major like tenferro, so `.data` / `as_slice()` buffers map
directly to `from_vec_col_major`.
- `Matrix::dot` / `gemm` are methods without an execution context; tenferro
direct ops need the explicit backend argument. Eager and traced tiers drop
it: `EagerTensor` methods run through the `EagerRuntime`, traced methods
build a graph.
- A single `DMatrix<T>` maps to `TypedTensor<T>`; there is no separate runtime
dtype type unless you actually need runtime dtype selection.

### If you come from PyTorch / JAX

- Trace one level of `?`: traced operators return `Result`; `a.matmul(&b)` is
a `Result`, unlike `torch.matmul` / `jnp.matmul`.
- Einsum needs the explicit arrow and rejects `...` (see
[Einsum syntax](#einsum-syntax)).
- The backend is not ambient: eager code must own an `EagerRuntime`, traced
code must register an engine plus extension modules (see
[Extension registration](#extension-registration)).

## Retired names

Searching for the retired `TypedStridedTensorView` (tenferro-rs#886) leads
nowhere: the name survives only in the obsolete-names vocabulary test. The
zero-copy view API is `TypedTensorView::from_slice` / `TypedTensorViewMut::from_slice`
(see the [API cheatsheet](api-cheatsheet.md#borrowing-external-memory)).
16 changes: 13 additions & 3 deletions .kimi/skills/tenferro-compute/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,19 @@ not when changing tenferro itself.

## Non-negotiable defaults

- Dense flat buffers are column-major: the leftmost dimension varies fastest.
- Bind one backend/runtime/compiler and reuse it across related work.
- Compile traced programs once outside repeated execution loops.
- **Column-major storage.** Dense buffers are column-major: the leftmost
dimension varies fastest. Row-major data passed to `from_vec_col_major` is
silently reinterpreted as column-major — permuted/wrong values, never
rejected.
- **No facade crate.** `cargo add tenferro` fails by design; depend on the
crates you need (`tenferro-runtime`, `tenferro-cpu`, and operation crates).
- **Explicit backend.** Direct operations take an explicit backend argument;
construct the backend/runtime once and reuse it — per-call construction
discards the buffer pool.
- **Einsum dialect.** Equations need the explicit arrow (`"ij,jk->ik"`); `...`
ellipsis is unsupported.
- **Result-returning operators.** Traced operators return `Result`; propagate
with `?`.
- CPU/GPU transfers are explicit; unsupported GPU operations do not silently
fall back to CPU.
- Traced standard extensions need an explicitly installed extension module and
Expand Down
44 changes: 44 additions & 0 deletions .kimi/skills/tenferro-compute/references/api-cheatsheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,47 @@ Import recipes:

If a method is present in the guide but Rust reports E0599, check the owning
crate's root re-exports and bring its `*Ext` trait into the local module.

## Borrowing external memory

Wrap an existing column-major buffer (a `faer::Mat`, an ndarray view, or any
slice plus strides) zero-copy with `TypedTensorView::from_slice`; use
`TypedTensorViewMut::from_slice` to write through the borrowed buffer. This is
how a tenferro kernel consumes memory owned by another library without a
migration: no tensor ownership is transferred.

<!-- snippet-source: docs/tutorial-code/src/bin/tenferro_compute_skill.rs#borrowing-external-memory -->
```rust
use tenferro_runtime::TypedTensorView;
use tenferro_tensor::TypedTensorViewMut;

// Wrap a column-major faer::Mat without copying. faer pads columns to
// alignment, so the borrowed slice spans `col_stride * ncols` elements and the
// column stride is passed explicitly; the padding is never read logically.
let mat = faer::Mat::from_fn(2, 3, |r, c| (r * 3 + c) as f64);
let data = unsafe { std::slice::from_raw_parts(mat.as_ref().as_ptr(), (mat.col_stride() as usize) * mat.ncols()) };
let view = TypedTensorView::from_slice(vec![2, 3], vec![1_isize, mat.col_stride()], 0, data)?;
assert_eq!(view.get(&[1, 2]), Some(&5.0));

// Wrap an ndarray row-major view the same way. Strides are arbitrary, so a
// row-major buffer is not transposed; it is only wrapped.
let arr = ndarray::Array2::from_shape_vec((2, 3), (0..6).map(|i| i as f64).collect())?;
let nview = TypedTensorView::from_slice(arr.shape(), arr.strides(), 0, arr.as_slice().expect("row-major array is contiguous"))?;
assert_eq!(nview.get(&[0, 2]), Some(&2.0));

// The mutable variant writes through the borrowed buffer.
let mut buffer = [0.0_f64, 0.0, 0.0, 0.0];
let mut mview = TypedTensorViewMut::from_slice(vec![2, 2], vec![1, 2], 0, &mut buffer)?;
*mview.get_mut(&[0, 0]).expect("in-bounds index") = 7.0;
assert_eq!(buffer[0], 7.0);
```
<!-- end-snippet-source -->

Because strides are arbitrary, row-major data wraps without transposition —
but kernels are tuned for column-major contiguity. Materialize a copy when the
wrapped buffer is row-major or its lifetime ends before the operation;
`TypedTensorView::duplicate()` requires a column-major-contiguous view, so for
a row-major wrap copy into a fresh `TypedTensor` (or an owned `Vec` in
column-major order) instead. If you search for the retired
`TypedStridedTensorView` (tenferro-rs#886), stop: `TypedTensorView::from_slice`
is its successor.
45 changes: 45 additions & 0 deletions .kimi/skills/tenferro-compute/references/pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,48 @@ is in the [traced API example](api-cheatsheet.md#traced-tensors-and-extensions).
`blas-accelerate`) when using `cpu-blas`.
- CUDA is explicit: enable `tenferro-gpu`'s `cuda` feature and upload CPU
tensors before CUDA operations. There is no implicit transfer.

## Per-origin traps

Same traps, keyed by the priors you arrived with.

### If you come from ndarray / NumPy

- Your buffers are row-major; passing them to `from_vec_col_major` silently
reinterprets them as column-major (no error, permuted/wrong values). Reorder
explicitly or wrap with
`TypedTensorView::from_slice` (see the [API cheatsheet](api-cheatsheet.md#borrowing-external-memory)).
- `cargo add tenferro` fails: there is no facade crate. Add
`tenferro-runtime` + `tenferro-cpu` (plus operation crates).
- `Array2::dot` is a free-standing habit; tenferro direct ops take an explicit
`&mut CpuBackend` argument: `a.matmul(&b, &mut backend)`.
- Your priors do not include a dtype-erased tensor; `Tensor` (runtime dtype)
has no ndarray counterpart — use `TypedTensor<T>`.

### If you come from nalgebra

- nalgebra is column-major like tenferro, so `.data` / `as_slice()` buffers map
directly to `from_vec_col_major`.
- `Matrix::dot` / `gemm` are methods without an execution context; tenferro
direct ops need the explicit backend argument. Eager and traced tiers drop
it: `EagerTensor` methods run through the `EagerRuntime`, traced methods
build a graph.
- A single `DMatrix<T>` maps to `TypedTensor<T>`; there is no separate runtime
dtype type unless you actually need runtime dtype selection.

### If you come from PyTorch / JAX

- Trace one level of `?`: traced operators return `Result`; `a.matmul(&b)` is
a `Result`, unlike `torch.matmul` / `jnp.matmul`.
- Einsum needs the explicit arrow and rejects `...` (see
[Einsum syntax](#einsum-syntax)).
- The backend is not ambient: eager code must own an `EagerRuntime`, traced
code must register an engine plus extension modules (see
[Extension registration](#extension-registration)).

## Retired names

Searching for the retired `TypedStridedTensorView` (tenferro-rs#886) leads
nowhere: the name survives only in the obsolete-names vocabulary test. The
zero-copy view API is `TypedTensorView::from_slice` / `TypedTensorViewMut::from_slice`
(see the [API cheatsheet](api-cheatsheet.md#borrowing-external-memory)).
Loading
Loading