Skip to main content

burn_tensor/bridge/
kind.rs

1use alloc::vec::Vec;
2use burn_backend::{TensorMetadata, TensorPrimitive, get_device_settings};
3use burn_dispatch::{Dispatch, DispatchTensor};
4use burn_std::DeviceSettings;
5
6/// A type-level representation of the kind of a float tensor
7#[derive(Clone, Debug)]
8pub struct Float;
9
10/// A type-level representation of the kind of a int tensor.
11#[derive(Clone, Debug)]
12pub struct Int;
13
14/// A type-level representation of the kind of a bool tensor.
15#[derive(Clone, Debug)]
16pub struct Bool;
17
18mod sealed {
19    pub trait Sealed {}
20}
21
22impl sealed::Sealed for Float {}
23impl sealed::Sealed for Int {}
24impl sealed::Sealed for Bool {}
25
26/// A type-level representation of the kind of a tensor.
27/// Metadata access is lazy.
28///
29/// # Notes
30/// This trait is intentionally sealed to keep the set of tensor kinds closed.
31///
32/// Although exposed publicly, tensor kinds are not meant to be extensible:
33/// the backend dispatch system, `DType`, and all tensor ops assume a fixed,
34/// closed set of tensor kinds (e.g. Float, Int, Bool), each mapping directly to a
35/// corresponding backend implementation.
36pub trait TensorKind: sealed::Sealed + Clone + Send + Sync + core::fmt::Debug {
37    /// The tensor kind identifier.
38    const KIND: Kind;
39
40    /// The name of the tensor kind.
41    fn name() -> &'static str {
42        Self::KIND.as_str()
43    }
44}
45
46impl TensorKind for Float {
47    const KIND: Kind = Kind::Float;
48}
49
50impl TensorKind for Int {
51    const KIND: Kind = Kind::Int;
52}
53
54impl TensorKind for Bool {
55    const KIND: Kind = Kind::Bool;
56}
57
58/// Represents the kind of a [`Tensor`](crate::Tensor).
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
60pub enum Kind {
61    /// A float tensor kind.
62    Float,
63    /// An integer tensor kind.
64    Int,
65    /// A boolean tensor kind.
66    Bool,
67}
68
69impl Kind {
70    /// Get the string representation of the [`Kind`].
71    pub fn as_str(&self) -> &'static str {
72        match self {
73            Kind::Float => "Float",
74            Kind::Int => "Int",
75            Kind::Bool => "Bool",
76        }
77    }
78}
79
80/// A type-tagged tensor at the bridge layer between the high-level tensor API
81/// and the dispatch system.
82///
83/// `BridgeTensor` serves as the runtime representation for the public tensor
84/// kinds (Float, Int, Bool) and internal variants like quantized floats, wrapping
85/// the uniform [`DispatchTensor`] used by the underlying dispatch layer. This
86/// separation keeps tensor kind tracking out of the backends while avoiding
87/// exposure of backend-level primitives in the public API.
88pub struct BridgeTensor {
89    blob: bridge_opaque::Opaque,
90}
91
92// Aligned, type-erased storage for `BridgeTensorVariant`. See `crate::macros`
93// for why this indirection exists (it keeps the dispatch type tree out of
94// downstream MIR).
95burn_std::obfuscate!(
96    type: BridgeTensorVariant,
97    module: bridge_opaque,
98    derives: [Send, Sync]
99);
100
101impl core::fmt::Debug for BridgeTensor {
102    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103        f.debug_struct("BridgeTensor")
104            .field("kind", &self.kind())
105            .finish()
106    }
107}
108
109impl Clone for BridgeTensor {
110    fn clone(&self) -> Self {
111        Self::new(self.as_variant().clone())
112    }
113}
114
115impl BridgeTensor {
116    fn as_variant(&self) -> &BridgeTensorVariant {
117        self.blob.as_ref()
118    }
119
120    fn into_variant(self) -> BridgeTensorVariant {
121        self.blob.into_inner()
122    }
123
124    fn new(inner: BridgeTensorVariant) -> Self {
125        Self {
126            blob: bridge_opaque::Opaque::new(inner),
127        }
128    }
129}
130
131#[derive(Clone, Debug)]
132/// Private type obfucated by Blob.
133enum BridgeTensorVariant {
134    /// A boolean tensor.
135    Bool(DispatchTensor),
136    /// An integer tensor.
137    Int(DispatchTensor),
138    /// A floating-point tensor.
139    Float(DispatchTensor),
140    /// A quantized floating-point tensor.
141    QFloat(DispatchTensor),
142}
143
144/// Runtime tag identifying which variant a [`BridgeTensor`] wraps.
145///
146/// Exposed so callers can dispatch on the variant without having to reach the
147/// private [`BridgeTensorVariant`].
148#[derive(Clone, Copy, Debug, PartialEq, Eq)]
149pub enum BridgeKind {
150    /// A boolean tensor.
151    Bool,
152    /// An integer tensor.
153    Int,
154    /// A floating-point tensor.
155    Float,
156    /// A quantized floating-point tensor.
157    QFloat,
158}
159
160/// Switches visibility based on the `extension` feature: `pub` when enabled
161/// (so backend-extension authors can call these), `pub(crate)` otherwise (so
162/// burn-tensor's own ops keep working without leaking the dispatch types).
163macro_rules! ext_fn {
164    ($(#[$meta:meta])* fn $($tt:tt)+) => {
165        #[cfg(feature = "extension")]
166        $(#[$meta])*
167        pub fn $($tt)+
168        #[cfg(not(feature = "extension"))]
169        $(#[$meta])*
170        pub(crate) fn $($tt)+
171    };
172}
173
174impl BridgeTensor {
175    ext_fn! {
176        /// Builds a bridge tensor that wraps a floating-point dispatch tensor.
177        ///
178        /// Available with the `extension` feature for backend-extension authors.
179        fn float(tensor: DispatchTensor) -> Self {
180            Self::new(BridgeTensorVariant::Float(tensor))
181        }
182    }
183
184    ext_fn! {
185        /// Builds a bridge tensor that wraps an integer dispatch tensor.
186        ///
187        /// Available with the `extension` feature for backend-extension authors.
188        fn int(tensor: DispatchTensor) -> Self {
189            Self::new(BridgeTensorVariant::Int(tensor))
190        }
191    }
192
193    ext_fn! {
194        /// Builds a bridge tensor that wraps a boolean dispatch tensor.
195        ///
196        /// Available with the `extension` feature for backend-extension authors.
197        fn bool(tensor: DispatchTensor) -> Self {
198            Self::new(BridgeTensorVariant::Bool(tensor))
199        }
200    }
201
202    ext_fn! {
203        /// Builds a bridge tensor that wraps a quantized floating-point dispatch tensor.
204        ///
205        /// Available with the `extension` feature for backend-extension authors.
206        fn qfloat(tensor: DispatchTensor) -> Self {
207            Self::new(BridgeTensorVariant::QFloat(tensor))
208        }
209    }
210
211    /// Returns the runtime tag identifying which variant this tensor wraps.
212    pub fn kind(&self) -> BridgeKind {
213        match self.as_variant() {
214            BridgeTensorVariant::Bool(_) => BridgeKind::Bool,
215            BridgeTensorVariant::Int(_) => BridgeKind::Int,
216            BridgeTensorVariant::Float(_) => BridgeKind::Float,
217            BridgeTensorVariant::QFloat(_) => BridgeKind::QFloat,
218        }
219    }
220
221    /// Returns `true` if this tensor is the float variant.
222    pub fn is_float(&self) -> bool {
223        matches!(self.kind(), BridgeKind::Float)
224    }
225
226    /// Returns `true` if this tensor is the int variant.
227    pub fn is_int(&self) -> bool {
228        matches!(self.kind(), BridgeKind::Int)
229    }
230
231    /// Returns `true` if this tensor is the bool variant.
232    pub fn is_bool(&self) -> bool {
233        matches!(self.kind(), BridgeKind::Bool)
234    }
235
236    /// Returns `true` if this tensor is the quantized float variant.
237    pub fn is_qfloat(&self) -> bool {
238        matches!(self.kind(), BridgeKind::QFloat)
239    }
240
241    ext_fn! {
242        /// Consumes the bridge tensor and returns its variant tag together with
243        /// the underlying dispatch tensor.
244        ///
245        /// Available with the `extension` feature for backend-extension authors.
246        fn into_parts(self) -> (BridgeKind, DispatchTensor) {
247            match self.into_variant() {
248                BridgeTensorVariant::Bool(t) => (BridgeKind::Bool, t),
249                BridgeTensorVariant::Int(t) => (BridgeKind::Int, t),
250                BridgeTensorVariant::Float(t) => (BridgeKind::Float, t),
251                BridgeTensorVariant::QFloat(t) => (BridgeKind::QFloat, t),
252            }
253        }
254    }
255
256    ext_fn! {
257        /// Borrows the bridge tensor as its variant tag together with a reference
258        /// to the underlying dispatch tensor.
259        ///
260        /// Available with the `extension` feature for backend-extension authors.
261        fn as_parts(&self) -> (BridgeKind, &DispatchTensor) {
262            match self.as_variant() {
263                BridgeTensorVariant::Bool(t) => (BridgeKind::Bool, t),
264                BridgeTensorVariant::Int(t) => (BridgeKind::Int, t),
265                BridgeTensorVariant::Float(t) => (BridgeKind::Float, t),
266                BridgeTensorVariant::QFloat(t) => (BridgeKind::QFloat, t),
267            }
268        }
269    }
270
271    /// Returns the dtype of the tensor.
272    pub fn dtype(&self) -> burn_std::DType {
273        match self.as_variant() {
274            BridgeTensorVariant::Bool(tensor) => tensor.dtype(),
275            BridgeTensorVariant::Int(tensor) => tensor.dtype(),
276            BridgeTensorVariant::Float(tensor) => tensor.dtype(),
277            BridgeTensorVariant::QFloat(tensor) => tensor.dtype(),
278        }
279    }
280
281    /// Whether the tensor's buffer can be mutated in place (see
282    /// [`TensorMetadata::can_mut`]).
283    pub fn can_mut(&self) -> bool {
284        match self.as_variant() {
285            BridgeTensorVariant::Bool(tensor) => tensor.can_mut(),
286            BridgeTensorVariant::Int(tensor) => tensor.can_mut(),
287            BridgeTensorVariant::Float(tensor) => tensor.can_mut(),
288            BridgeTensorVariant::QFloat(tensor) => tensor.can_mut(),
289        }
290    }
291
292    /// Returns the shape of the tensor.
293    pub fn shape(&self) -> burn_std::Shape {
294        match self.as_variant() {
295            BridgeTensorVariant::Bool(tensor) => tensor.shape(),
296            BridgeTensorVariant::Int(tensor) => tensor.shape(),
297            BridgeTensorVariant::Float(tensor) => tensor.shape(),
298            BridgeTensorVariant::QFloat(tensor) => tensor.shape(),
299        }
300    }
301
302    /// Returns the number of dimensions of the tensor.
303    pub fn rank(&self) -> usize {
304        match self.as_variant() {
305            BridgeTensorVariant::Bool(tensor) => tensor.rank(),
306            BridgeTensorVariant::Int(tensor) => tensor.rank(),
307            BridgeTensorVariant::Float(tensor) => tensor.rank(),
308            BridgeTensorVariant::QFloat(tensor) => tensor.rank(),
309        }
310    }
311
312    pub(crate) fn as_dispatch(&self) -> &DispatchTensor {
313        match self.as_variant() {
314            BridgeTensorVariant::Bool(tensor) => tensor,
315            BridgeTensorVariant::Int(tensor) => tensor,
316            BridgeTensorVariant::Float(tensor) => tensor,
317            BridgeTensorVariant::QFloat(tensor) => tensor,
318        }
319    }
320
321    #[cfg(feature = "autodiff")]
322    pub(crate) fn as_float(&self) -> &DispatchTensor {
323        match self.as_variant() {
324            BridgeTensorVariant::Float(tensor) => tensor,
325            _ => panic!("Should be Float primitive kind"),
326        }
327    }
328
329    pub(crate) fn into_dispatch_vec(tensors: Vec<Self>) -> Vec<DispatchTensor> {
330        tensors.into_iter().map(Into::into).collect()
331    }
332
333    pub(crate) fn into_float(self) -> DispatchTensor {
334        match self.into_variant() {
335            BridgeTensorVariant::Float(tensor) => tensor,
336            // Returns the dequantized float tensor.
337            BridgeTensorVariant::QFloat(tensor) => {
338                TensorPrimitive::<Dispatch>::QFloat(tensor).tensor()
339            }
340            _ => panic!("Should be Float primitive kind"),
341        }
342    }
343
344    pub(crate) fn device_settings(&self) -> DeviceSettings {
345        let device = match self.as_variant() {
346            BridgeTensorVariant::Bool(tensor) => tensor.device(),
347            BridgeTensorVariant::Int(tensor) => tensor.device(),
348            BridgeTensorVariant::Float(tensor) => tensor.device(),
349            BridgeTensorVariant::QFloat(tensor) => tensor.device(),
350        };
351
352        get_device_settings::<Dispatch>(&device)
353    }
354}
355
356impl From<BridgeTensor> for DispatchTensor {
357    fn from(value: BridgeTensor) -> Self {
358        match value.into_variant() {
359            BridgeTensorVariant::Bool(tensor) => tensor,
360            BridgeTensorVariant::Int(tensor) => tensor,
361            BridgeTensorVariant::Float(tensor) => tensor,
362            BridgeTensorVariant::QFloat(tensor) => tensor,
363        }
364    }
365}