# ruTENSOR
**Английский** | [简体中文](../zh/README.md)
Тензорная линейная алгебра для тензоров устройств Ruda: сокращения и einsum, сокращения, физические перестановки и поэлементные операции.
- Cargo пакет: `ruTENSOR`
- Крейт Rust: `rutensor`
- [Руководство пользователя на английском языке](../../../docs/ru/libraries/rutensor.md)
- [中文使用文档](../../../docs/zh/libraries/rutensor.md)
## ruTENSOR Руководство пользователя
[Вычислительные библиотеки](../../../docs/ru/libraries/README.md) · [Тензорная платформа](../../../docs/ru/tensor-framework.md) · [中文](../zh/README.md)
ruTENSOR обеспечивает сжатие тензоров по именованным осям, сокращения, физические перестановки и поэлементные операции. Входы используют `RudaTensor<R>`; приложение выбирает устройство Runtime. Эта библиотека отличается от платформы более высокого уровня `ruda-tensor`.
### 1. Настройте зависимости
Пакет Cargo — `ruTENSOR`; имя импорта Rust — `rutensor`. По умолчанию включен `std` и вычисление тензора устройства без выбора драйвера. В следующей конфигурации каталог приложения размещается рядом с исходным каталогом `RUDA`:
```toml
[dependencies]
rutensor = { package = "ruTENSOR", path = "../RUDA/ruTENSOR" }
ruda-core = { path = "../RUDA/ruda-core", default-features = false, features = ["std", "tensor-host-data"] }
ruda-kernel = { path = "../RUDA/ruda-kernel", default-features = false, features = ["frontend-std", "device-tensor"] }
ruda-driver-cuda = { path = "../RUDA/ruda-driver-cuda", default-features = false, features = ["std"] }
```
### 2. Свёртка тензоров, редукция и перестановка
Сохраните следующее как `src/main.rs` приложения:
```rust
use ruda_core::tensor::data::TensorData;
use ruda_driver_cuda::{CudaDevice, CudaRuntime};
use ruda_kernel::tensor::{readback::into_data_sync, transfer::from_data};
use rutensor::{einsum, permute, reduce, ReductionOp};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let device = CudaDevice::default();
let a = from_data::<CudaRuntime>(
TensorData::new(vec![1f32, 2., 3., 4., 5., 6.], [2, 3]), &device,
);
let b = from_data::<CudaRuntime>(
TensorData::new(vec![1f32, 0., 0., 1., 1., 1.], [3, 2]), &device,
);
let product = einsum("ik,kj->ij", &[&a, &b])?;
let row_sums = reduce(&a, &[0, 1], &[0], ReductionOp::Sum)?;
let transposed = permute(&a, &[1, 0])?;
println!("product: {:?}", into_data_sync(product).to_vec::<f32>()?);
println!("row sums: {:?}", into_data_sync(row_sums).to_vec::<f32>()?);
println!("transpose: {:?}", into_data_sync(transposed).to_vec::<f32>()?);
Ok(())
}
```
`einsum("ik,kj->ij", ...)` суммирует по k и возвращает форму `[2, 2]`. `reduce` сохраняет режим 0 и уменьшает режим 1, возвращая форму `[2]`. `permute` возвращает недавно выделенный тензор `[3, 2]`, а не представление, совместно использующее входное хранилище.
### 3. Выражения Einsum
`einsum(expression, inputs)` принимает один или несколько входов. Буквы с учетом регистра обозначают оси; правая часть стрелки выбирает и упорядочивает выходные оси.
|`ik,kj->ij`|Умножение матрицы|
|`...ik,...kj->...ij`|Матричное умножение с широковещательными пакетными осями|
|`abc,cde->abde`| Многомерная свёртка тензоров |
|`ij,jk,kl->il`| Свёртка трёх входов |
|`i,j->ij`| Внешнее произведение |
|`ii->i`| Диагональ |
|`ii->`|Трассировка, возвращающая скаляр нулевого ранга|
|`ijk->ki`| Редуцировать j и переставить оставшиеся оси |
|`...i->i`| Редуцировать все оси, обозначенные многоточием |
- Режимы сопоставления входных данных должны иметь равные экстенты или экстенты, равные 1. Оси эллипса транслируются с выравниванием по правому краю.
- Повторяющиеся метки внутри ввода выбирают диагональ; эти оси должны иметь точно одинаковую протяженность.
- Метки выходных данных должны быть уникальными и присутствовать во входных данных.
- Без `->` выходные оси начинаются с осей многоточия, за которыми следуют метки, отсортированные по алфавиту, которые встречаются ровно один раз.
- Скалярный вход использует пустой сегмент метки; например, `,ij->ij` умножает первый скалярный вход в матрицу.
- Входы могут быть несмежными. Вычисления не считывают значения тензора обратно на хост.
Для фиксированных выражений, фигур, шагов и dtypes создайте `EinsumPlan::new(expression, descriptors)` и повторно используйте `execute(inputs)`. Чтобы указать точность вывода и арифметику, используйте `EinsumPlan::with_options` или `einsum_with_options`.
### 4. Дескрипторы и планы выполнения
A `Mode` — это метка `i32`. Одна и та же метка идентифицирует одну и ту же логическую ось в тензорах, независимо от ее физического положения. `TensorDescriptor` хранит экстенты, шаги элементов и хранилище dtype. `OperandDescriptor` добавляет метки и входное унарное преобразование.
Эти функции создают и выполняют план для `D = alpha * A @ B + beta * C`:
```rust
use ruda_kernel::{dsl::Runtime, tensor::RudaTensor};
use rutensor::{
ComputeType, DType, OperandDescriptor, OperationDescriptor,
Plan, Result, TensorDescriptor,
};
fn make_plan<R: Runtime>(
a: &RudaTensor<R>, b: &RudaTensor<R>, c: &RudaTensor<R>,
m: usize, n: usize,
) -> Result<Plan> {
let operation = OperationDescriptor::contraction(
OperandDescriptor::from_tensor(a, &[0, 2])?,
OperandDescriptor::from_tensor(b, &[2, 1])?,
Some(OperandDescriptor::from_tensor(c, &[0, 1])?),
TensorDescriptor::contiguous(&[m, n], DType::F32)?,
&[0, 1],
ComputeType::F32,
)?;
Plan::new(operation)
}
fn execute<R: Runtime>(
plan: &Plan, a: &RudaTensor<R>, b: &RudaTensor<R>, c: &RudaTensor<R>,
alpha: f64, beta: f64,
) -> Result<RudaTensor<R>> {
plan.execute(&[a, b, c], &[alpha, beta])
}
```
Планы не сохраняют входные буферы. Последующие исполнения могут использовать разные тензоры с соответствующими формами, шагами и типами d. Все входы должны находиться на одном устройстве.
|`contraction`| A, B; C необязателен | alpha; beta при наличии C |
|`sum_product`| Все входы произведения; C необязателен | alpha; beta при наличии C |
|`reduction`| A; C необязателен | alpha; beta при наличии C |
|`permutation`| A | alpha |
|`elementwise_binary`| A, B | alpha, beta |
|`elementwise_trinary`| A, B, C | alpha, beta, gamma |
`Plan::execute` выделяет выход. `Plan::execute_into` принимает и возвращает выходной тензор, форма, шаги и dtype которого соответствуют выходному дескриптору. Его буфер должен принадлежать единолично, без совместного использования с входами или другими представлениями.
Используйте `TensorDescriptor::new(extents, strides, dtype)` для дополненных или измененных макетов вывода; выходные оси не должны перекрываться. Явные дескрипторы операций могут добавлять дополнительные оси широковещания через выходную форму.
### 5. Сокращение и поэлементные операции
`reduce(input, input_modes, output_modes, operation)` уменьшает количество меток, отсутствующих в выводе; уменьшенные оси удалены:
|`Sum`|Сумма|0|
|`Product`| Произведение |1|
|`Min`|Минимум|Положительная бесконечность|
|`Max`|Максимум|Отрицательная бесконечность|
`elementwise_binary` и `elementwise_trinary` выравнивают и транслируют именованные оси с помощью `BinaryOp::{Add, Mul, Min, Max}`. Троичные операции оценивают `(alpha * op(A) op_ab beta * op(B)) op_abc gamma * op(C)`.
Выберите `Identity`, `Negate`, `Abs`, `Sqrt`, `Exp`, `Log`, `Sin`, `Cos`, `Tanh`, `Relu`, `Reciprocal` или от `Conjugate` до `OperandDescriptor::with_unary`. `Log` — натуральный логарифм; `Conjugate` равен `Identity` для реальных значений. Мин и Макс распространяются на NaNs.
### 6. Точность, хранение и ошибки
- Типы хранилища: F16, BF16, F32 и F64. Квантованная, целочисленная и комплексная память не принимаются.
- `ComputeType::F32` или `F64` управляет преобразованием входных данных, унарными преобразованиями, произведениями, сокращениями и скалярной арифметикой. Входные данные F64 требуют вычислений F64.
- По умолчанию используется арифметика F64, если какой-либо ввод имеет значение F64, в противном случае F32. Идентичные типы входных хранилищ сохраняют этот тип выходных данных; смешанные входы создают F64, если какой-либо вход равен F64, в противном случае F32.
- Запросить явный вывод dtype для более точного хранения; увеличение точности хранения не может восстановить точность, уже потерянную во входных данных.
- Преобразование вывода происходит при последней записи. Входные данные с dtype, отличным от типа вычисления, требуют буферов преобразования устройства; соответствующие входные данные считываются в исходном формате.
- При сокращении используется параллельное слияние деревьев рабочих групп. Порядок сложения и умножения с плавающей запятой может отличаться от последовательного вычисления.
- Пустые выходные данные не выполняют никаких вычислений; выходные данные нулевого ранга содержат один скаляр. Для расчета формы, шага и адреса используется платформа `usize`; отправки также подчиняются ограничениям ресурсов бэкенда.
- Возвращаемые значения `Result` сообщают об ошибках выражения, дескриптора, формы, устройства и диапазона отправки. Отправка устройства следует за обработкой ошибок среды выполнения; ошибки асинхронного выполнения возникают при синхронизации или обратном чтении.
- Общие сокращения напрямую пересекают координаты сокращения. Они не ищут пути сокращения с несколькими входами и не автоматически переходят к умножению матрицы Tensor Core. Для преобразования хранилища требуется дополнительная память устройства.
ruTENSOR предоставляет Ruda Rust API, а не NVIDIA cuTENSOR C ABI. Устройство должно поддерживать выбранные типы хранилища и вычислений.