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:
33
crates/stable-diffusion-burn/burn-crates/burn-ir/Cargo.toml
Normal file
33
crates/stable-diffusion-burn/burn-crates/burn-ir/Cargo.toml
Normal 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"]
|
||||
@@ -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).
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
1113
crates/stable-diffusion-burn/burn-crates/burn-ir/src/builder.rs
Normal file
1113
crates/stable-diffusion-burn/burn-crates/burn-ir/src/builder.rs
Normal file
File diff suppressed because it is too large
Load Diff
216
crates/stable-diffusion-burn/burn-crates/burn-ir/src/handle.rs
Normal file
216
crates/stable-diffusion-burn/burn-crates/burn-ir/src/handle.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
21
crates/stable-diffusion-burn/burn-crates/burn-ir/src/lib.rs
Normal file
21
crates/stable-diffusion-burn/burn-crates/burn-ir/src/lib.rs
Normal 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::*;
|
||||
3011
crates/stable-diffusion-burn/burn-crates/burn-ir/src/operation.rs
Normal file
3011
crates/stable-diffusion-burn/burn-crates/burn-ir/src/operation.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user