ruPRIM 0.1.8

Parallel primitives, reductions, scans, and indexing for Ruda.
Documentation
# ruPRIM

[English](../../README.md) | [简体中文](../zh/README.md) | [日本語](../ja/README.md) | [Deutsch](../de/README.md) | **Русский**

**Английский** | [简体中文](../zh/README.md)

Параллельные примитивы, сокращения, сканирования и индексирование для Ruda.

- Cargo пакет: `ruPRIM`
- Крейт Rust: `ruprim`

## Feature

| Feature |Операции|
| --- | --- |
|`tensor-reduce`|Приведение всего тензора и оси|
|`tensor-reduce-autotune`|Автонастройка снижения|
|`tensor-scan`|Совокупная сумма, произведение, минимум и максимум|
|`elementwise`|Поэлементные операции|
|`indexing`|Выбор, нарезка, сбор и рассеяние|

## Краткое руководство

Сборка из рабочей области RUDA:

```sh
git clone https://github.com/shuqi2077/RUDA.git
cd RUDA
cargo build --release --locked -p ruPRIM --features tensor-reduce,tensor-scan,indexing
```

## Документация

- [Руководство пользователя](../../../docs/ru/libraries/ruprim.md)
- [Настройка среды](../../../docs/ru/getting-started.md)
- [Функции Cargo](../../Cargo.toml) · [Экспорт модулей](../../src/lib.rs)

## ruPRIM Руководство пользователя

[Вычислительные библиотеки](../../../docs/ru/libraries/README.md) · [Тензорная платформа](../../../docs/ru/tensor-framework.md) · [中文](../zh/README.md)

ruPRIM обеспечивает сокращение тензора устройства, совокупное сканирование, поэлементные операции и индексирование. На этой странице используется `RudaTensor<R>`, где R — среда выполнения устройства.

### 1. Настройте зависимости

Пакет Cargo — `ruPRIM`; его имя для импорта в Rust — `ruprim`. Включите функции для необходимых вам операций:

| Feature |Интерфейс|
| --- | --- |
|`tensor-reduce`|`ruprim::reduce::tensor`: сокращение всего тензора и оси|
|`tensor-reduce-autotune`| Автонастройка редукции; также включает tensor-reduce |
|`tensor-scan`|`ruprim::scan`: совокупная сумма, произведение, минимум, максимум.|
|`elementwise`|`ruprim::elementwise`: поэлементное вычисление|
|`indexing`| `ruprim::indexing`: выбор, срезы, gather, scatter; также включает elementwise |

В этой конфигурации каталог приложения размещается рядом с исходным каталогом `RUDA`. См. раздел [Начало работы](../../../docs/ru/getting-started.md) для настройки NVIDIA.

```toml
[dependencies]
ruprim = { package = "ruPRIM", path = "../RUDA/ruPRIM", default-features = false, features = ["std", "tensor-reduce", "tensor-scan", "indexing"] }
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` использует матрицу F32 `[[1, 2, 3], [4, 5, 6]]` для вычисления общего значения, средних значений строк, индексов argmax строк и сумм префиксов строк. Запустите `cargo run` из каталога приложения:

```rust
use ruda_core::tensor::{DType, TensorMetadata, data::TensorData};
use ruda_driver_cuda::{CudaDevice, CudaRuntime};
use ruda_kernel::tensor::{readback::into_data_sync, transfer::from_data};
use ruprim::reduce::{
    components::instructions::ReduceOperationConfig,
    tensor::{KernelReduceStrategy, SumStrategy, reduce_dim, sum},
};
use ruprim::scan::cumsum;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let device = CudaDevice::default();
    let input = from_data::<CudaRuntime>(
        TensorData::new(vec![1f32, 2., 3., 4., 5., 6.], [2, 3]),
        &device,
    );
    let total = sum(
        input.clone(),
        SumStrategy::Chained(KernelReduceStrategy::Unspecified),
    )?;
    assert_eq!(total.shape().as_slice(), &[1]);
    assert_eq!(into_data_sync(total).to_vec::<f32>()?, [21.0]);

    let means = reduce_dim(
        input.clone(), None, 1,
        KernelReduceStrategy::Unspecified, ReduceOperationConfig::Mean,
    )?;
    assert_eq!(means.shape().as_slice(), &[2, 1]);
    assert_eq!(into_data_sync(means).to_vec::<f32>()?, [2.0, 5.0]);

    let indices = reduce_dim(
        input.clone(), Some(DType::I32), 1,
        KernelReduceStrategy::Unspecified, ReduceOperationConfig::ArgMax,
    )?;
    assert_eq!(into_data_sync(indices).to_vec::<i32>()?, [2, 2]);

    let prefix = cumsum(input, 1);
    assert_eq!(into_data_sync(prefix).to_vec::<f32>()?, [1., 3., 6., 4., 9., 15.]);
    Ok(())
}
```

`sum` уменьшает все элементы и возвращает форму `[1]`. `reduce_dim` обрабатывает только выбранную ось, обычно сохраняя ранг и устанавливая длину этой оси равной 1. Таким образом, строка означает, что она имеет форму `[2, 1]`, а не `[2]`.

### 3. Операции и стратегии сокращения

Полный порядок параметров для тензорных интерфейсов:

|Функция|Цель|
| --- | --- |
|`sum(tensor, strategy)`|Целотензорная сумма|
|`sum_fallback(tensor, strategy)`|Целотензорная сумма; переключает OneShot на Chained, когда требуемое атомарное добавление недоступно|
|`reduce(tensor, output_dtype, strategy, config)`|По очереди уменьшает каждую ось и возвращает форму [1]|
|`reduce_dim(tensor, output_dtype, dim, strategy, config)`|Уменьшает выбранную ось.|

Выберите `config` с помощью `ReduceOperationConfig`:

|Операция|`reduce_dim` вывод|
| --- | --- |
|`Sum`, `Prod`, `Mean`|Сумма, произведение, среднее значение; выбранная длина оси 1|
|`Min`, `Max`, `MaxAbs`|Минимальное, максимальное, максимальное абсолютное значение; выбранная длина оси 1|
|`ArgMin`, `ArgMax`|Индексы с отсчетом от нуля в пределах выбранной оси; длина оси 1|
|`TopK(k)`|Верхние значения k вдоль выбранной оси; длина оси k|
|`ArgTopK(k)`|Соответствующие индексы внутри оси; длина оси k|

Сокращение индекса требует явного целочисленного вывода dtype, например `Some(DType::I32)`. Для уменьшения стоимости введите `None`; вывод сохраняет ввод dtype. Не используйте `output_dtype` в качестве общего параметра приведения. F16/BF16 Sum, Prod и Mean используют накопление FP32 в этом пути перед записью входных данных dtype. Ось должна быть допустимой, а TopK k должен находиться в `1..=axis length`. Используйте `reduce_dim` для TopK, а не целотензорный `reduce`, который сбрасывает окончательную форму на `[1]`.

Выберите `SumStrategy` следующим образом:

- `OneShot(ruda_count)`: явно устанавливает положительное количество рабочих групп и требует атомарного добавления для входа dtype.
- `Chained(KernelReduceStrategy::Unspecified)`: использует поэтапные сокращения, не требуя глобального атомарного сложения всей тензорной суммы; используется в примере.
- `Autotune`: доступно с `tensor-reduce-autotune`. Без этой функции по умолчанию используется `OneShot(4)`; при этом значением по умолчанию является `Autotune`.

`KernelReduceStrategy` предлагает `Unspecified`, `Specific(ReduceStrategy)` и `Autotune` с ограниченными возможностями. Используйте «Специальный», чтобы исправить низкоуровневую стратегию. Без автонастройки значением по умолчанию является «Не указано».

Интерфейсы редукции возвращают `Result<RudaTensor<R>, ReduceError>`. Ось вне допустимого диапазона даёт `InvalidAxis`; отсутствие атомарного сложения для OneShot — `MissingAtomicAdd`. `sum_fallback` заменяет лишь неподдерживаемый случай атомарного сложения OneShot и не переключает выполнение на CPU.

### 4. Совокупное сканирование

Все четыре функции принимают `(tensor, dim)`:

|Функция|Результат для одной входной строки [3, 1, 2]|
| --- | --- |
|`cumsum`|[3, 4, 6]|
|`cumprod`|[3, 3, 6]|
|`cummin`|[3, 1, 1]|
|`cummax`|[3, 3, 3]|

Это включающие префиксные операции. Выходная форма и dtype соответствуют входным; пакетные строки обрабатываются независимо. dim начинается с нуля и должен находиться в пределах входного ранга. Сканирует возвращаемые тензоры напрямую, а не `Result`.

Текущий алгоритм считывает соответствующий префикс для каждой выходной позиции, давая общее количество чтений O(n²) для оси длины n. Учитывайте эти затраты на длинных осях сканирования, а не оценивайте работу только по количеству входных элементов.

### 5. Отбор, сбор, нарезка и целочисленные степени.

Добавьте эту функцию в тот же файл и вызовите `indexing_example(&device)?;` перед возвратом функции main. Он повторно использует более ранний импорт:

```rust
fn indexing_example(device: &CudaDevice) -> Result<(), Box<dyn std::error::Error>> {
    use ruda_core::tensor::element::Scalar;
    use ruprim::{
        elementwise::binary::integer_power,
        indexing::{gather, select, slice},
    };

    let input = from_data::<CudaRuntime>(
        TensorData::new(vec![1f32, 2., 3., 4., 5., 6.], [2, 3]),
        device,
    );
    let columns = from_data::<CudaRuntime>(
        TensorData::new(vec![2i32, 0], [2]), device,
    );
    let selected = select(input.clone(), 1, columns);
    assert_eq!(into_data_sync(selected).to_vec::<f32>()?, [3., 1., 6., 4.]);

    let positions = from_data::<CudaRuntime>(
        TensorData::new(vec![2i32, 0, 1, 1], [2, 2]), device,
    );
    let gathered = gather(1, input.clone(), positions);
    assert_eq!(into_data_sync(gathered).to_vec::<f32>()?, [3., 1., 5., 5.]);

    let sliced = slice(input.clone(), &[0..2, 1..3]);
    assert_eq!(into_data_sync(sliced).to_vec::<f32>()?, [2., 3., 5., 6.]);

    let squared = integer_power::scalar(input, Scalar::Int(2));
    assert_eq!(into_data_sync(squared).to_vec::<f32>()?, [1., 4., 9., 16., 25., 36.]);
    Ok(())
}
```

|Функция|Индексы и вывод|
| --- | --- |
|`select(tensor, dim, indices)`|Одномерные целочисленные индексы; применяет одни и те же позиции к другим измерениям, заменяя длину выбранной оси количеством индексов|
|`gather(dim, tensor, indices)`|dim — первый аргумент; выбирает входное значение для каждой выходной позиции, при этом выходная форма равна форме индексов|
|`slice(tensor, ranges)`|Полуоткрытые диапазоны `start..end` на ось; в примере выбираются столбцы 1 и 2 из всех строк|
|`slice_assign(tensor, slices, value)`|Использует `ruda_core::tensor::Slice`; записывает значение в выбранную область и возвращает обновленный тензор|

Индексы и данные должны совместно использовать одно устройство; индексы должны быть допустимыми целыми числами, отсчитываемыми от нуля. Для сбора используйте тензоры индексов с одинаковым рангом и соответствующими невыбранным измерениям. В каждой строке примера можно выбирать разные столбцы. Диапазоны срезов должны лежать в пределах их осей, а форма значения назначения должна соответствовать выбранной области.

Целочисленные степени находятся в `ruprim::elementwise::binary::integer_power`:

- `scalar(input, Scalar::Int(exponent))`: для всех элементов используется один целочисленный показатель степени.
- `tensor(base, exponents)`: использует базы с плавающей запятой и тензоры целочисленной экспоненты с широковещательно-совместимыми формами; вывод сохраняет базу dtype.

Используйте `Scalar::Int(-2)` для отрицательного показателя степени, а не для преобразования показателя степени в формат с плавающей запятой. Индексирующие и поэлементные интерфейсы напрямую возвращают тензоры устройств. Чтобы сохранить входные данные для других операций, передайте клонированные дескрипторы, как в примере, и сохраните тензор, возвращаемый каждой операцией.

API Ссылка: [Сокращение](../../src/reduce/tensor/base.rs), [Сканирование](../../src/scan/tensor.rs), [Индексирование](../../src/indexing/mod.rs), [Целые степени](../../src/elementwise/binary/integer_power.rs).