feat: update workspace paths and enhance gitignore

- Updated stablediffusion crate path from "../stable-diffusion-burn" to "./crates/stable-diffusion-burn" for proper workspace resolution
- Enhanced .gitignore to include generated model files (.mpk, .pt, .bin, .safetensors, .ckpt) and user_data directory
- Added Cargo.lock to gitignore with appropriate comment
- Reorganized IDE files section in gitignore for better clarity
- Added newline at end of file for proper formatting
This commit is contained in:
2026-03-05 19:39:14 +01:00
parent 4bb7ca9074
commit 3a67c0979c
1605 changed files with 537032 additions and 2 deletions

View File

@@ -0,0 +1,33 @@
[package]
authors = ["laggui <lagrange.guillaume.1@gmail.com>", "nathanielsimard <nathaniel.simard.42@gmail.com>"]
categories = ["science"]
description = "Intermediate representation for the Burn framework"
edition.workspace = true
keywords = ["deep-learning", "machine-learning", "tensor"]
license.workspace = true
name = "burn-ir"
readme.workspace = true
repository = "https://github.com/tracel-ai/burn/tree/main/crates/burn-ir"
documentation = "https://docs.rs/burn-ir"
version.workspace = true
[lints]
workspace = true
[features]
default = ["std"]
std = ["burn-backend/std"]
doc = ["default"]
tracing = [
"burn-backend/tracing",
]
[dependencies]
serde = { workspace = true }
hashbrown = { workspace = true } # no_std compatible
burn-backend = { path = "../burn-backend", version = "=0.21.0-pre.2", default-features = false }
[package.metadata.docs.rs]
features = ["doc"]
rustdoc-args = ["--cfg", "docsrs"]

View File

@@ -0,0 +1,7 @@
# Burn Intermediate Representation
Defines an Intermediate Representation (IR) used to represent tensors and operations.
The abstraction over computation allows execution across different targets (e.g., remote backend).
It also enables optimization and transformation of tensor computations before execution (e.g.,
operator fusion).

View File

@@ -0,0 +1,63 @@
use burn_backend::{
Backend, Shape,
tensor::{BoolTensor, FloatTensor, IntTensor, QuantizedTensor},
};
/// A tensor representation containing a reference to a tensor resource with a given shape.
#[derive(Clone)]
pub struct TensorHandle<H: Clone> {
/// The type that can be used to point to a tensor of any kind.
pub handle: H,
/// The shape associated to the tensor.
pub shape: Shape,
}
/// Backend extension trait that allows an existing [backend](Backend) to use the Burn tensor
/// intermediate representation for compilation purpose or other...
pub trait BackendIr: Backend {
/// The type that can be used to point to a tensor of any kind.
type Handle: Sync + Send + Clone;
/// Convert a [handle](BackendIr::Handle) to a [float tensor](Backend::FloatTensorPrimitive).
fn float_tensor(handle: TensorHandle<Self::Handle>) -> FloatTensor<Self>;
/// Convert a [handle](BackendIr::Handle) to an [int tensor](Backend::IntTensorPrimitive).
fn int_tensor(handle: TensorHandle<Self::Handle>) -> IntTensor<Self>;
/// Convert a [handle](BackendIr::Handle) to a [bool tensor](Backend::BoolTensorPrimitive).
fn bool_tensor(handle: TensorHandle<Self::Handle>) -> BoolTensor<Self>;
/// Convert a [handle](BackendIr::Handle) to a [quantized tensor](Backend::QuantizedTensorPrimitive).
fn quantized_tensor(handle: TensorHandle<Self::Handle>) -> QuantizedTensor<Self>;
/// Convert a [float tensor](Backend::FloatTensorPrimitive) to a [handle](BackendIr::Handle).
fn float_tensor_handle(tensor: FloatTensor<Self>) -> Self::Handle;
/// Convert an [int tensor](Backend::IntTensorPrimitive) to a [handle](BackendIr::Handle).
fn int_tensor_handle(tensor: IntTensor<Self>) -> Self::Handle;
/// Convert a [bool tensor](Backend::BoolTensorPrimitive) to a [handle](BackendIr::Handle).
fn bool_tensor_handle(tensor: BoolTensor<Self>) -> Self::Handle;
/// Convert a [quantized tensor](Backend::QuantizedTensorPrimitive) to a [handle](BackendIr::Handle).
fn quantized_tensor_handle(tensor: QuantizedTensor<Self>) -> Self::Handle;
}
/// Handle which points to a backend tensor primitive kind.
#[derive(Clone, Debug)]
pub enum HandleKind<B: Backend> {
/// Float tensor handle.
Float(B::FloatTensorPrimitive),
/// Int tensor handle.
Int(B::IntTensorPrimitive),
/// Bool tensor handle.
Bool(B::BoolTensorPrimitive),
/// Quantized tensor handle.
Quantized(B::QuantizedTensorPrimitive),
}
impl<B: Backend> HandleKind<B> {
/// Returns the handle kind name.
pub fn name(&self) -> &str {
match self {
HandleKind::Float(_) => "float",
HandleKind::Int(_) => "int",
HandleKind::Bool(_) => "bool",
HandleKind::Quantized(_) => "quantized",
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
use hashbrown::HashMap;
use crate::{BackendIr, TensorHandle, TensorId, TensorIr, TensorStatus};
/// Keep all [tensor handles](BackendIr::Handle) in one place and ensure that all resources
/// are used optimally.
#[derive(Default)]
pub struct HandleContainer<H> {
handles: HashMap<TensorId, Handle<H>>,
counter: u64,
}
impl<H: Clone> HandleContainer<H> {
/// Fork the container, useful for autotune.
pub fn fork(&self) -> Self {
let mut handles = HashMap::with_capacity(self.handles.len());
for (id, handle) in self.handles.iter() {
handles.insert(*id, handle.clone());
}
Self {
handles,
counter: self.counter,
}
}
}
impl<H> core::fmt::Debug for HandleContainer<H> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("HandleContainer")
.field("handles", &self.handles.keys()) // only care about the IDs when debugging
.field("counter", &self.counter)
.finish()
}
}
/// Backend [tensor handle](BackendIr::Handle) wrapper tracking their creation state
#[derive(Clone)]
pub enum Handle<H> {
/// No [tensor handle](BackendIr::Handle) has been created yet
NotInit,
/// A [tensor handle](BackendIr::Handle) has been created
Existing(H),
}
impl<H: Clone> HandleContainer<H> {
/// Create a new HandleContainer
pub fn new() -> Self {
Self {
handles: HashMap::new(),
counter: 0,
}
}
/// Register a handle for the given [tensor id](TensorId).
pub fn register_handle(&mut self, id: TensorId, handle: H) {
self.handles.insert(id, Handle::Existing(handle));
}
/// Whether an handle exists.
pub fn has_handle(&mut self, id: &TensorId) -> bool {
self.handles.contains_key(id)
}
/// Get the reference to a handle.
pub fn get_handle_ref(&self, id: &TensorId) -> Option<&H> {
self.handles
.get(id)
.filter(|h| !matches!(h, Handle::NotInit))
.map(|h| match h {
Handle::Existing(handle) => handle,
Handle::NotInit => unreachable!(),
})
}
/// Get the handle for the given [tensor id](TensorId). The status is used to determine if the
/// tensor should be popped out of the current tensor map, necessary for inplace operations.
///
/// # Warnings
///
/// Make sure the status corresponds to the operation you want to execute the handle on,
/// otherwise you might remove a tensor handle that will be required in the future.
pub fn get_handle(&mut self, id: &TensorId, status: &TensorStatus) -> H {
let (id, handle) = self
.handles
.remove_entry(id)
.unwrap_or_else(|| panic!("Should have handle for tensor {id:?}"));
match handle {
Handle::Existing(handle) => match status {
TensorStatus::ReadOnly => {
self.handles.insert(id, Handle::Existing(handle.clone()));
handle
}
TensorStatus::ReadWrite => handle,
TensorStatus::NotInit => panic!(
"Cannot get uninitialized tensor {id:?}. Tensor exist but with wrong status"
),
},
Handle::NotInit => panic!("Cannot get uninitialized handle {id:?}."),
}
}
/// Get the tensor handle for the given [tensor intermediate representation](TensorIr).
pub fn get_tensor_handle(&mut self, tensor: &TensorIr) -> TensorHandle<H> {
TensorHandle {
handle: self.get_handle(&tensor.id, &tensor.status),
shape: tensor.shape.clone(),
}
}
/// Get the [float tensor](burn_backend::backend::Backend::FloatTensorPrimitive) corresponding to the
/// given [tensor intermediate representation](TensorIr).
pub fn get_float_tensor<B>(&mut self, tensor: &TensorIr) -> B::FloatTensorPrimitive
where
B: BackendIr<Handle = H>,
{
B::float_tensor(self.get_tensor_handle(tensor))
}
/// Get the [int tensor](burn_backend::backend::Backend::IntTensorPrimitive) corresponding to the
/// given [tensor intermediate representation](TensorIr).
pub fn get_int_tensor<B>(&mut self, tensor: &TensorIr) -> B::IntTensorPrimitive
where
B: BackendIr<Handle = H>,
{
B::int_tensor(self.get_tensor_handle(tensor))
}
/// Get the [bool tensor](burn_backend::backend::Backend::BoolTensorPrimitive) corresponding to the
/// given [tensor intermediate representation](TensorIr).
pub fn get_bool_tensor<B>(&mut self, tensor: &TensorIr) -> B::BoolTensorPrimitive
where
B: BackendIr<Handle = H>,
{
B::bool_tensor(self.get_tensor_handle(tensor))
}
/// Get the [quantized tensor](burn_backend::backend::Backend::QuantizedTensorPrimitive) corresponding to the
/// given [tensor intermediate representation](TensorIr).
pub fn get_quantized_tensor<B>(&mut self, tensor: &TensorIr) -> B::QuantizedTensorPrimitive
where
B: BackendIr<Handle = H>,
{
B::quantized_tensor(self.get_tensor_handle(tensor))
}
/// Register a new [float tensor](burn_backend::backend::Backend::FloatTensorPrimitive) with the corresponding [tensor id](TensorId).
pub fn register_float_tensor<B>(&mut self, id: &TensorId, tensor: B::FloatTensorPrimitive)
where
B: BackendIr<Handle = H>,
{
let handle = B::float_tensor_handle(tensor);
self.handles.insert(*id, Handle::Existing(handle));
}
/// Register a new [quantized tensor](burn_backend::backend::Backend::QuantizedTensorPrimitive) with the corresponding [tensor ids](TensorId).
pub fn register_quantized_tensor<B>(
&mut self,
id: &TensorId,
tensor: B::QuantizedTensorPrimitive,
) where
B: BackendIr<Handle = H>,
{
let handle = B::quantized_tensor_handle(tensor);
self.handles.insert(*id, Handle::Existing(handle));
}
/// Register a new [int tensor](burn_backend::backend::Backend::IntTensorPrimitive) with the corresponding [tensor id](TensorId).
pub fn register_int_tensor<B>(&mut self, id: &TensorId, tensor: B::IntTensorPrimitive)
where
B: BackendIr<Handle = H>,
{
let handle = B::int_tensor_handle(tensor);
self.handles.insert(*id, Handle::Existing(handle));
}
/// Register a new [bool tensor](burn_backend::backend::Backend::BoolTensorPrimitive) with the corresponding [tensor id](TensorId).
pub fn register_bool_tensor<B>(&mut self, id: &TensorId, tensor: B::BoolTensorPrimitive)
where
B: BackendIr<Handle = H>,
{
let handle = B::bool_tensor_handle(tensor);
self.handles.insert(*id, Handle::Existing(handle));
}
/// Lazily create a new empty tensor and return its corresponding [tensor id](TensorId).
pub fn create_tensor_uninit(&mut self) -> TensorId {
let id = TensorId::new(self.counter);
self.counter += 1;
self.handles.insert(id, Handle::NotInit);
id
}
/// Remove tensor handle from container.
pub fn remove_handle(&mut self, id: TensorId) -> Option<Handle<H>> {
self.handles.remove(&id)
}
/// Remove tensor handle from container if writable
pub fn free(&mut self, tensor: &TensorIr) {
match tensor.status {
TensorStatus::ReadOnly => (),
TensorStatus::NotInit => (),
TensorStatus::ReadWrite => {
self.handles.remove(&tensor.id);
}
};
}
/// Returns the number of handles.
pub fn num_handles(&self) -> usize {
self.handles.len()
}
}

View File

@@ -0,0 +1,21 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
//! Burn intermediate representation.
extern crate alloc;
mod backend;
mod builder;
mod handle;
mod operation;
mod scalar;
mod tensor;
pub use backend::*;
pub use builder::*;
pub use handle::*;
pub use operation::*;
pub use scalar::*;
pub use tensor::*;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
use burn_backend::{DType, Scalar};
use burn_backend::{Element, ElementConversion};
use core::hash::Hash;
use serde::{Deserialize, Serialize};
/// A scalar representation.
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum ScalarIr {
Float(f64),
Int(i64),
UInt(u64),
Bool(bool),
}
impl Hash for ScalarIr {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
match self {
ScalarIr::Float(x) => x.to_bits().hash(state),
ScalarIr::Int(x) => x.hash(state),
ScalarIr::UInt(x) => x.hash(state),
ScalarIr::Bool(x) => x.hash(state),
}
}
}
impl ScalarIr {
/// Creates a scalar with the specified data type.
pub fn new<E: ElementConversion>(value: E, dtype: &DType) -> Self {
if dtype.is_float() {
Self::Float(value.elem())
} else if dtype.is_int() {
Self::Int(value.elem())
} else if dtype.is_uint() {
Self::UInt(value.elem())
} else if dtype.is_bool() {
Self::Bool(value.elem())
} else {
unimplemented!("Scalar not supported for {dtype:?}")
}
}
/// Converts and returns the converted element.
pub fn elem<E: Element>(self) -> E {
match self {
ScalarIr::Float(x) => x.elem(),
ScalarIr::Int(x) => x.elem(),
ScalarIr::UInt(x) => x.elem(),
ScalarIr::Bool(x) => x.elem(),
}
}
}
// The enums are similar, but both types have different roles:
// - `Scalar`: runtime literal value
// - `ScalarIr`: serializable literal representation (used for IR)
impl From<Scalar> for ScalarIr {
fn from(value: Scalar) -> Self {
match value {
Scalar::Float(x) => Self::Float(x),
Scalar::Int(x) => Self::Int(x),
Scalar::UInt(x) => Self::UInt(x),
Scalar::Bool(x) => Self::Bool(x),
}
}
}
impl From<ScalarIr> for Scalar {
fn from(value: ScalarIr) -> Self {
match value {
ScalarIr::Float(x) => Self::Float(x),
ScalarIr::Int(x) => Self::Int(x),
ScalarIr::UInt(x) => Self::UInt(x),
ScalarIr::Bool(x) => Self::Bool(x),
}
}
}

View File

@@ -0,0 +1,67 @@
use serde::{Deserialize, Serialize};
use burn_backend::{DType, Shape};
/// The tensor unique identifier.
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct TensorId {
value: u64,
}
impl core::fmt::Display for TensorId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("TensorId({:?})", self.value))
}
}
/// The status of the current tensor.
#[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum TensorStatus {
/// The tensor can be read, but not written.
ReadOnly,
/// The tensor can be mutated inplace.
ReadWrite,
/// No handle exists for that tensor.
NotInit,
}
/// A tensor definition represents a snapshot of a tensor when it was used.
///
/// # Example
///
/// A tensor that is used multiple times has its status updated for each operation.
///
/// 1. Status::NotInit
/// 2. Status::ReadOnly
/// 3. Status::ReadOnly
/// 4. Status::ReadWrite
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct TensorIr {
/// The [tensor id](TensorId).
pub id: TensorId,
/// The shape of the tensor.
pub shape: Shape,
/// The [status](TensorStatus) of the tensor when it was used.
pub status: TensorStatus,
/// The [type](DType) of the tensor.
pub dtype: DType,
}
impl TensorId {
/// Create a new tensor id.
pub fn new(value: u64) -> Self {
Self { value }
}
}
impl TensorIr {
/// Create a new tensor that is not already initialized.
pub fn uninit(id: TensorId, shape: Shape, dtype: DType) -> Self {
Self {
id,
status: TensorStatus::NotInit,
shape,
dtype,
}
}
}