# Tensorism

A Rust library built on top of [`ndarray`](https://crates.io/crates/ndarray) that provides a domain-specific language (DSL) for expressing tensor computations using named indexes.

## Goals

- Make multi-index array expressions **explicit**, **readable**, and **compositional**
- Remain compatible with the `ndarray` ecosystem
- Provide compile-time index checking via Ricci indexes

## Quick example

```rust
use ndarray::Array2;
use tensorism::new_ndarray;

// Generate a 2D array from indexed expressions
let x: Array2<f64> = new_ndarray! { for i j => a[i, j] + b[j] };
```

Index `i` ranges over the first dimension of `a`, while `j` ranges over the second dimension of `a` and the only dimension of `b` (which must be compatible).

## Features

### Array generation

A top-level `for ⟨indexes⟩ => ⟨body⟩` inside `new_ndarray!` generates a new `ndarray::Array` whose number of dimensions matches the number of indexes:

```rust
let y = new_ndarray! {
    for i j k =>
        if p[i, j] - 0.3 < 0.4 * q[j, k] {
            r[j] * q[j, k] + 0.2
        } else {
            0.5 * s[i, j, k]
        }
};
```

### Iterators and aggregation

When used as a sub-expression, `for ⟨indexes⟩ => ⟨body⟩` evaluates to a Rust iterator, enabling aggregations:

```rust
let x: i64 = new_ndarray! {
    Iterator::sum(for i => Iterator::min(for j => a[i, j]).unwrap())
};
```

### Indexers

Tensorism supports special indexers inside bracket syntax:

- `rev: i` — iterates over the axis in reverse order
- `plain: ⟨expr⟩` — uses a fixed `usize` expression independent of iteration
- User-defined indexers via `Reindexing1`, `Reindexing2`, etc.

```rust
let array = new_ndarray! {
    for i j => a[sorting_reindexing[i], rev: j] + b[j, plain: 3 * SIZE + 1]
};
```

### Aliases

Fresh index names can be bound using `let` inside `for` constructs:

```rust
let array = new_ndarray! {
    for i j let k = reindexing2[rev: i, j] => x[k, j] + y[k]
};
```

### Conditionals

Nested `for` constructs support conditional guards with `if`:

```rust
let conditional_sum = new_ndarray! {
    Iterator::sum(for i j if u[i] < v[i, j] => w[i, j] * u[i])
};
```

## Status

Tensorism is currently **experimental**. The evaluation strategy is primarily iterator-based, and the DSL may evolve in non-backward-compatible ways in future versions.

## License

MIT
