JuliaML / JuliaML/MLUtils.jl

Design discussion: should MLUtils lean more on views vs copies?

Open
#231 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Julia
Stars
124
Forks
23
PR merge metrics
No merged PRs in 30d

Description

An audit of which MLUtils operations return **views** (alias the input) vs **copies** (allocate fresh memory) turned up a couple of internal inconsistencies and some divergence from PyTorch's conventions. Opening this to discuss whether MLUtils should standardize more aggressively on views.

All view/copy classifications below were verified empirically (mutate the output, check whether the input changes), not just read off the source.

## Current behavior

**Return views (alias the input):**
- `obsview` / `ObsView` indexing, `splitobs`, `shuffleobs`, `oversample` / `undersample`, and the data-container forms of `kfolds` / `leavepout` / `timeseries_kfolds` — all via `obsview`
- `chunk` (array input) — `view` / `selectdim`
- `flatten`, `unsqueeze` — `reshape`

**Allocate a copy:**
- `getobs` (this is the materialization boundary), `randobs`, `unbatch`
- `batch`, `batchseq`, `batch_sequence`, `stack`
- `unstack` — **explicit** `copy(selectdim(...))`
- `normalise`, `rescale`, `rpad_constant`, the `*_like` family
- `group_counts`, `group_indices`

**Lazy containers** (no copy at construction; `getobs` on access copies): `BatchView`, `DataLoader` / `eachobs`, `SlidingWindow` (`slidingwindow`), `mapobs` / `filterobs` / `groupobs` / `joinobs`.

The organizing principle is clean and worth stating explicitly somewhere: **`obsview` = view, `getobs` = copy.**

## Things this surfaces

1. **`chunk` vs `unstack` inconsistency.** Two near-identical "split an array along a dimension" operations make opposite choices: `chunk` returns views, `unstack` copies each slice (`[copy(selectdim(...)) ...]`). PyTorch's analogs — `torch.chunk`/`torch.split` and `torch.unbind` — are *both* views.

2. **`slidingwindow` copies; PyTorch `Tensor.unfold` is a zero-copy strided view.** Each window is materialized via `getobs`. For `AbstractArray` we could offer a strided view instead (harder to do generically over arbitrary `getobs` containers, but doable for arrays).

3. **Undocumented aliasing.** `chunk`, `flatten`, and `unsqueeze` return views, so mutating their output mutates the input — none of the docstrings say so. (Related to the `chunk` rrule TODO tracked in #227.)

4. **`getobs(x, i)` always copies**, whereas PyTorch basic indexing `x[i]` is a view. This is a deliberate "getobs materializes" contract, but worth confirming we want it.

## PyTorch comparison (verified on torch 2.x)

| MLUtils | PyTorch analog | PyTorch | MLUtils |
|---------|---------------|---------|---------|
| `chunk` | `torch.chunk` / `split` | view | view ✓ |
| `unstack` | `torch.unbind` | **view** | **copy** ✗ |
| `unbatch` | `torch.unbind` | **view** | **copy** ✗ |
| `slidingwindow[i]` | `Tensor.unfold` | **view** | **copy** ✗ |
| `getobs(x, i)` scalar | `x[i]` (basic index) | **view** | **copy** ✗ |
| `getobs(x, [i,j])` | `x[[i,j]]` (advanced index) | copy | copy ✓ |
| `obsview` | basic slice / `narrow` / `select` | view | view ✓ |
| `flatten`, `unsqueeze` | `flatten`, `unsqueeze` | view | view ✓ |
| `batch` / `stack` | `torch.stack` | copy | copy ✓ |
| `rpad_constant` | `F.pad` | copy | copy ✓ |
| `*_like` | `torch.*_like` | copy | copy ✓ |

PyTorch is internally consistent (both `chunk` and `unbind` are views, sliding windows are views) and leans harder toward views in general; MLUtils agrees on the structural/reshape/`_like` ops but diverges on `unstack`, `unbatch`, `slidingwindow`, and scalar `getobs`.

## Questions for discussion

- Should `unstack` return views to match `chunk` (and PyTorch `unbind`), or is the copy intentional and worth documenting as such?
- Is a view-returning sliding window over `AbstractArray` (à la `unfold`) worth adding?
- At minimum, should we document the view/copy semantics in each affected docstring?
- Bigger picture: do we want to commit to and document `obsview`(view)/`getobs`(copy) as *the* design principle, and audit the array utilities (`chunk`/`unstack`/`flatten`/`unsqueeze`) for consistency against it?

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.