Skip to content
Merged
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
286 changes: 142 additions & 144 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,190 +1,188 @@
# churon: ONNX Runtime Integration for R
# churon

[![R-CMD-check](https://github.com/churon-project/churon/workflows/R-CMD-check/badge.svg)](https://github.com/churon-project/churon/actions)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![CRAN version](https://www.r-pkg.org/badges/version/churon)](https://CRAN.R-project.org/package=churon)
[![R CMD Check](https://github.com/mrchypark/churon/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/mrchypark/churon/actions/workflows/R-CMD-check.yaml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE.md)

churon provides R bindings for ONNX Runtime, enabling high-performance machine learning inference with ONNX models.
Run ONNX models from R using [ONNX Runtime](https://onnxruntime.ai/).
churon provides model sessions, named tensor inputs and outputs, and a bundled
MNIST model through Rust bindings built with [extendr](https://extendr.github.io/).

## Features
## Install

- 🚀 **High Performance**: Core logic implemented in Rust for speed
- 🛡️ **Memory Safety**: Rust's memory safety guarantees
- 🔧 **Comprehensive Error Handling**: Detailed error messages and validation
- 🌐 **Multiple Execution Providers**: CUDA, TensorRT, DirectML, OneDNN, CoreML, CPU support (on supported platforms)
- 📦 **Bundled Example Models**: MNIST included for testing

## Installation

### System Requirements

- R (>= 4.0.0)
- Supported platforms: macOS (ARM64), Linux (x64/ARM64), Windows (x64)

### Install from R-universe (Recommended)

Pre-built binaries are available from [R-universe](https://mrchypark.r-universe.dev/churon), which means **no Rust toolchain required**:
Install the released package from [CRAN](https://CRAN.R-project.org/package=churon):

```r
install.packages('churon', repos = c('https://mrchypark.r-universe.dev', 'https://cloud.r-project.org'))
install.packages("churon")
```

### Install from GitHub
R 4.0.0 or later is required. Installing a binary package does not require Rust.
Building from source requires Cargo and rustc 1.88.0 or later, along with the
platform's R compilation tools. Rust dependencies are vendored in the CRAN
source package for offline compilation.

If you prefer to build from source (requires Rust >= 1.88.0):
For alternative builds, use [R-universe](https://mrchypark.r-universe.dev/churon):

```r
devtools::install_github("mrchypark/churon")
install.packages(
"churon",
repos = c("https://mrchypark.r-universe.dev", "https://cloud.r-project.org")
)
```

## Quick Start
## Set up ONNX Runtime

### Basic Usage
ONNX Runtime is a separate native library. The R package can load without it,
but inference requires it. Nothing is downloaded automatically.

For a first run, install the runtime into a temporary directory:

```r
library(churon)

# Download ONNX Runtime once after installing churon
if (!onnx_runtime_is_installed()) {
install_onnx_runtime()
}
install_onnx_runtime(destdir = file.path(tempdir(), "onnxruntime"))
check_onnx_runtime_available()
# TRUE
```

# Check available example models
models <- onnx_example_models()
print(models)
The installer requires an explicit destination, verifies the download's SHA-256
checksum, and sets `ORT_DYLIB_PATH` for the current R session. It supports runtime
versions 1.28.0 and 1.29.0; the default is 1.29.0.

# Create a session with the example MNIST model
session <- onnx_example_session("mnist")
To keep the runtime between sessions, choose a persistent directory instead of
`tempdir()`. After installation, inspect the library's full path:

# Get model information
input_info <- onnx_input_info(session)
output_info <- onnx_output_info(session)
```r
get_onnx_runtime_info()$dylib_path
```

print(input_info[[1]]$get_name()) # Input tensor name
print(input_info[[1]]$get_shape()) # Input tensor shape
print(input_info[[1]]$get_data_type()) # Input tensor data type
In each new R session, set that path **before** loading churon. You can also set
`ORT_DYLIB_PATH` in your `.Renviron` file. It must point to the library file, not
its directory:

# Run inference
input_name <- input_info[[1]]$get_name()
input_shape <- input_info[[1]]$get_shape() # c(1, 1, 28, 28) for MNIST
| Platform | Library under your installation directory |
| --- | --- |
| macOS ARM64 | `lib/libonnxruntime.dylib` |
| Linux x64 / ARM64 | `lib/libonnxruntime.so` |
| Windows x64 / ARM64 | `lib/onnxruntime.dll` |

# Create random input (28x28 grayscale image)
input_data <- array(rnorm(prod(input_shape)), dim = input_shape)
result <- onnx_run(session, setNames(list(input_data), input_name))
An existing compatible ONNX Runtime installation can be used the same way,
without running the installer. Restart R before switching a runtime that has
already been loaded. The installer does not support Intel macOS.

# Result is class probabilities
cat("Predicted digit:", which.max(result[[1]]) - 1, "\n")
```
## Run the bundled MNIST model

### Using Custom ONNX Models
Once the runtime is configured, this example runs without any model downloads:

```r
library(churon)

# Load any ONNX model
session <- onnx_session("path/to/your/model.onnx")
session <- onnx_example_session("mnist")

# Get expected input/output information
input_info <- onnx_input_info(session)
output_info <- onnx_output_info(session)
# MNIST expects one grayscale image: batch x channel x height x width.
# A blank image demonstrates the API; it is not a useful recognition example.
image <- array(0, dim = c(1, 1, 28, 28))
input_name <- onnx_input_info(session)[[1]]$get_name()
inputs <- setNames(list(image), input_name)

# Run inference with your data
inputs <- list()
inputs[[input_info[[1]]$get_name()]] <- your_data
outputs <- onnx_run(session, inputs)
scores <- as.numeric(outputs[[1]])
predicted_digit <- which.max(scores) - 1L
predicted_digit
```

### Session Management
Inputs are a named list of numeric vectors, matrices, or arrays. Names must
match the model's input names. Outputs are a named list of tensors; their
meaning depends on the model. The MNIST outputs are scores for digits 0–9,
not automatically normalized probabilities.

```r
# Create session with default providers
session <- onnx_session("model.onnx")
For real images, use the preprocessing and tensor layout expected by your
model. churon does not resize, normalize, or otherwise preprocess images.

# Get available providers (note: may return limited set on current platform)
providers <- onnx_providers(session)
cat("Available providers:", paste(providers, collapse = ", "), "\n")
## Use your own model

# Get model path
model_path <- onnx_model_path(session)
```

### Safe Session Creation with Error Handling
Replace the path and supply `input_data` with the dimensions and preprocessing
required by your model:

```r
# Safe session creation with automatic error handling
session <- safe_onnx_session("model.onnx")

if (!is.null(session)) {
# Session created successfully
result <- onnx_run(session, inputs)
} else {
cat("Failed to create session\n")
}

# Safe inference with monitoring
result <- safe_onnx_run(session, inputs, monitor_performance = TRUE)
```

## API Reference

### Core Functions

| Function | Description |
|----------|-------------|
| `onnx_session(model_path, providers = NULL)` | Create an ONNX Runtime session |
| `onnx_run(session, inputs)` | Run inference with input data |
| `onnx_input_info(session)` | Get input tensor information |
| `onnx_output_info(session)` | Get output tensor information |
| `onnx_providers(session)` | Get available execution providers |

### Example Models

| Function | Description |
|----------|-------------|
| `onnx_example_models()` | List bundled example models |
| `onnx_example_session(model_name)` | Create session with example model |

### Utility Functions

| Function | Description |
|----------|-------------|
| `install_onnx_runtime(version)` | Download and install ONNX Runtime |
| `find_model_path(model_name)` | Find full path to a model file |
| `get_onnx_runtime_info()` | Get ONNX Runtime version info |
| `check_onnx_runtime_available()` | Check if ONNX Runtime is available |
| `safe_onnx_session()` | Create session with error handling |
| `safe_onnx_run()` | Run inference with error handling |

## Current Status
session <- onnx_session("path/to/model.onnx")

### ✅ Fully Working Features

- ONNX model loading and session management
- Model metadata extraction and querying
- Tensor conversion (f32, f64 support)
- Inference execution with multiple execution providers
- Comprehensive error handling and validation
- Bundled MNIST model for testing
input_info <- onnx_input_info(session)
output_info <- onnx_output_info(session)
print(input_info)
print(output_info)

### ⚠️ Limitations
# For a single-input model, after preparing input_data:
inputs <- setNames(list(input_data), input_info[[1]]$get_name())
outputs <- onnx_run(session, inputs)
```

- **Platform Support**: Currently optimized for macOS ARM64
- **Execution Providers**: Some providers (CUDA, TensorRT) require additional system libraries
- **Performance Features**: Advanced optimization features pending implementation
For models with multiple inputs, provide one named list entry per input.
Use the model's specification for tensor dimensions: the current metadata
implementation returns `-1` as a shape placeholder, so do not construct arrays
from `$get_shape()`.

## API at a glance

| Function | Purpose |
| --- | --- |
| `onnx_session(model_path)` | Load a model into a session |
| `onnx_run(session, inputs)` | Run inference and return named output tensors |
| `onnx_input_info(session)` / `onnx_output_info(session)` | Inspect tensor metadata |
| `onnx_model_path(session)` | Get the session's model path |
| `onnx_providers(session)` | Get the session's reported execution providers |
| `onnx_example_models()` | List bundled model paths |
| `onnx_example_session("mnist")` | Load the bundled MNIST model |
| `install_onnx_runtime(destdir = ...)` | Explicitly download and install the runtime |
| `onnx_runtime_is_installed()` | Check whether a runtime library file can be found |
| `check_onnx_runtime_available()` | Check whether the configured runtime path exists |
| `get_onnx_runtime_info()` | Inspect runtime paths, platform, and configuration |
| `safe_onnx_session()` / `safe_onnx_run()` | Warn and return `NULL` on failure |
| `batch_process_data(session, data_list)` | Process a list of input sets in batches |

See `?onnx_session`, `?onnx_run`, and `?install_onnx_runtime` in R for details,
or read the [reference manual](https://CRAN.R-project.org/web/packages/churon/churon.pdf).

## Current limitations

- **CPU inference:** the current R session API uses CPU execution. Its
`providers` argument validates names but does not yet select a backend;
passing `"cuda"` does not enable GPU inference.
- **Tensor types:** the public inference API accepts numeric inputs and converts
them to float32. Do not assume support for integer, boolean, string, or
float64 model inputs.
- **Shape metadata:** reported shapes are placeholders. Supply dimensions from
your model's specification.
- **Runtime checks:** path checks confirm that a file exists, not that it is a
compatible library. Actual model loading can still fail.

## Troubleshooting

**Runtime not found:** inspect `get_onnx_runtime_info()`. Set `ORT_DYLIB_PATH`
to the full library filename before loading churon, or run the installer with
an explicit destination. Temporary installations disappear after the R session.

**Model or input errors:** check that the model file exists, input names match,
and tensor types and dimensions agree with the model. `onnx_run()` reports
errors; `safe_onnx_run()` turns them into warnings and returns `NULL`.

## Development and support

Report bugs or request features in
[GitHub issues](https://github.com/mrchypark/churon/issues). Include your OS,
R and churon versions, runtime configuration, and a minimal reproducible example.
Pull requests are welcome.

With the development dependencies and Rust toolchain installed, run from a
checkout:

## Contributing
```r
devtools::load_all()
devtools::test()
```

1. Fork this repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
Tests requiring ONNX Runtime are skipped when it is unavailable.

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

## Acknowledgments

- [ONNX Runtime](https://onnxruntime.ai/) team
- [extendr](https://github.com/extendr/extendr) project
- R community
churon is licensed under [MIT](LICENSE.md). The bundled MNIST model and external
ONNX Runtime library have their own licensing terms; see
[third-party notices](inst/COPYRIGHTS) and [contributors](inst/AUTHORS).
Loading