Skip to main content

burn_std/element/
base.rs

1use core::cmp::Ordering;
2use rand::Rng;
3
4use crate::distribution::Distribution;
5use crate::{BoolStore, DType, bf16, f16, flex32};
6
7use super::cast::ToElement;
8
9/// Core element trait for tensor values.
10///
11/// This trait defines the minimal set of capabilities required for a type to be
12/// stored and manipulated as a tensor element across all backends.
13pub trait Element:
14    ToElement
15    + ElementRandom
16    + ElementConversion
17    + ElementEq
18    + bytemuck::CheckedBitPattern
19    + bytemuck::NoUninit
20    + bytemuck::Zeroable
21    + core::fmt::Debug
22    + core::fmt::Display
23    + Default
24    + Send
25    + Sync
26    + Copy
27    + 'static
28{
29    /// The dtype of the element.
30    fn dtype() -> DType;
31}
32
33/// Ordered element trait for tensor values.
34///
35/// This trait extends [`Element`] with ordering semantics, enabling comparison
36/// and order-dependent operations in generic Rust implementations.
37///
38/// Backends that implement these operations entirely at the device level do
39/// not rely on this trait. It only constrains the scalar type for generic Rust code.
40pub trait ElementOrdered: Element + ElementComparison + ElementLimits {}
41
42/// Element conversion trait for tensor.
43pub trait ElementConversion {
44    /// Converts an element to another element.
45    ///
46    /// # Arguments
47    ///
48    /// * `elem` - The element to convert.
49    ///
50    /// # Returns
51    ///
52    /// The converted element.
53    fn from_elem<E: ToElement>(elem: E) -> Self;
54
55    /// Converts and returns the converted element.
56    fn elem<E: Element>(self) -> E;
57}
58
59/// Element trait for random value of a tensor.
60pub trait ElementRandom {
61    /// Returns a random value for the given distribution.
62    ///
63    /// # Arguments
64    ///
65    /// * `distribution` - The distribution to sample from.
66    /// * `rng` - The random number generator.
67    ///
68    /// # Returns
69    ///
70    /// The random value.
71    fn random<R: Rng>(distribution: Distribution, rng: &mut R) -> Self;
72}
73
74/// Element trait for equality of a tensor.
75pub trait ElementEq {
76    /// Returns whether `self` and `other` are equal.
77    fn eq(&self, other: &Self) -> bool;
78}
79
80/// Element ordering trait.
81pub trait ElementComparison {
82    /// Returns and [Ordering] between `self` and `other`.
83    fn cmp(&self, other: &Self) -> Ordering;
84}
85
86/// Element limits trait.
87pub trait ElementLimits {
88    /// The minimum representable value
89    const MIN: Self;
90    /// The maximum representable value
91    const MAX: Self;
92}
93
94/// Element addition trait.
95pub trait ElementAdd {
96    /// Addition between `self` and `other`.
97    fn add(self, other: Self) -> Self;
98}
99
100/// Macro to implement the element trait for a type.
101#[macro_export]
102macro_rules! make_element {
103    (
104        ty $type:ident,
105        convert $convert:expr,
106        random $random:expr,
107        cmp $cmp:expr,
108        add $add:expr,
109        dtype $dtype:expr
110    ) => {
111        make_element!(ty $type, convert $convert, random $random, cmp $cmp, add $add, dtype $dtype, min $type::MIN, max $type::MAX);
112    };
113        (
114        ty $type:ident,
115        convert $convert:expr,
116        random $random:expr,
117        cmp $cmp:expr,
118        add $add:expr,
119        dtype $dtype:expr,
120        min $min:expr,
121        max $max:expr
122    ) => {
123        make_element!(ty $type, convert $convert, random $random, cmp $cmp, dtype $dtype, min $min, max $max);
124
125        impl ElementAdd for $type {
126            #[inline(always)]
127            fn add(self, other: Self) -> Self {
128                $add(self, other)
129            }
130        }
131    };
132    (
133        ty $type:ident,
134        convert $convert:expr,
135        random $random:expr,
136        cmp $cmp:expr,
137        dtype $dtype:expr,
138        min $min:expr,
139        max $max:expr
140    ) => {
141        impl Element for $type {
142            #[inline(always)]
143            fn dtype() -> $crate::DType {
144                $dtype
145            }
146        }
147        impl ElementEq for $type {
148            fn eq(&self, other: &Self) -> bool {
149                self == other
150            }
151        }
152
153        impl ElementConversion for $type {
154            #[inline(always)]
155            fn from_elem<E: ToElement>(elem: E) -> Self {
156                #[allow(clippy::redundant_closure_call)]
157                $convert(&elem)
158            }
159            #[inline(always)]
160            fn elem<E: Element>(self) -> E {
161                E::from_elem(self)
162            }
163        }
164
165        impl ElementRandom for $type {
166            fn random<R: Rng>(distribution: Distribution, rng: &mut R) -> Self {
167                #[allow(clippy::redundant_closure_call)]
168                $random(distribution, rng)
169            }
170        }
171
172        impl ElementComparison for $type {
173            fn cmp(&self, other: &Self) -> Ordering {
174                let a = self.elem::<$type>();
175                let b = other.elem::<$type>();
176                #[allow(clippy::redundant_closure_call)]
177                $cmp(&a, &b)
178            }
179        }
180
181        impl ElementLimits for $type {
182            const MIN: Self = $min;
183            const MAX: Self = $max;
184        }
185
186        impl ElementOrdered for $type {}
187
188    };
189}
190
191make_element!(
192    ty f64,
193    convert ToElement::to_f64,
194    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
195    cmp |a: &f64, b: &f64| a.total_cmp(b),
196    add |a, b| a + b,
197    dtype DType::F64
198);
199
200make_element!(
201    ty f32,
202    convert ToElement::to_f32,
203    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
204    cmp |a: &f32, b: &f32| a.total_cmp(b),
205    add |a, b| a + b,
206    dtype DType::F32
207);
208
209make_element!(
210    ty i64,
211    convert ToElement::to_i64,
212    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
213    cmp |a: &i64, b: &i64| Ord::cmp(a, b),
214    add |a, b| a + b,
215    dtype DType::I64
216);
217
218make_element!(
219    ty u64,
220    convert ToElement::to_u64,
221    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
222    cmp |a: &u64, b: &u64| Ord::cmp(a, b),
223    add |a, b| a + b,
224    dtype DType::U64
225);
226
227make_element!(
228    ty i32,
229    convert ToElement::to_i32,
230    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
231    cmp |a: &i32, b: &i32| Ord::cmp(a, b),
232    add |a, b| a + b,
233    dtype DType::I32
234);
235
236make_element!(
237    ty u32,
238    convert ToElement::to_u32,
239    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
240    cmp |a: &u32, b: &u32| Ord::cmp(a, b),
241    add |a, b| a + b,
242    dtype DType::U32
243);
244
245make_element!(
246    ty i16,
247    convert ToElement::to_i16,
248    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
249    cmp |a: &i16, b: &i16| Ord::cmp(a, b),
250    add |a, b| a + b,
251    dtype DType::I16
252);
253
254make_element!(
255    ty u16,
256    convert ToElement::to_u16,
257    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
258    cmp |a: &u16, b: &u16| Ord::cmp(a, b),
259    add |a, b| a + b,
260    dtype DType::U16
261);
262
263make_element!(
264    ty i8,
265    convert ToElement::to_i8,
266    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
267    cmp |a: &i8, b: &i8| Ord::cmp(a, b),
268    add |a, b| a + b,
269    dtype DType::I8
270);
271
272make_element!(
273    ty u8,
274    convert ToElement::to_u8,
275    random |distribution: Distribution, rng: &mut R| distribution.sampler(rng).sample(),
276    cmp |a: &u8, b: &u8| Ord::cmp(a, b),
277    add |a, b| a + b,
278    dtype DType::U8
279);
280
281make_element!(
282    ty f16,
283    convert ToElement::to_f16,
284    random |distribution: Distribution, rng: &mut R| {
285        let sample: f32 = distribution.sampler(rng).sample();
286        f16::from_elem(sample)
287    },
288    cmp |a: &f16, b: &f16| a.total_cmp(b),
289    add |a: f16, b: f16| f16::from_f32(a.to_f32() + b.to_f32()),
290    dtype DType::F16
291);
292make_element!(
293    ty bf16,
294    convert ToElement::to_bf16,
295    random |distribution: Distribution, rng: &mut R| {
296        let sample: f32 = distribution.sampler(rng).sample();
297        bf16::from_elem(sample)
298    },
299    cmp |a: &bf16, b: &bf16| a.total_cmp(b),
300    add |a, b| a + b,
301    dtype DType::BF16
302);
303
304make_element!(
305    ty flex32,
306    convert |elem: &dyn ToElement| flex32::from_f32(elem.to_f32()),
307    random |distribution: Distribution, rng: &mut R| {
308        let sample: f32 = distribution.sampler(rng).sample();
309        flex32::from_elem(sample)
310    },
311    cmp |a: &flex32, b: &flex32| a.total_cmp(b),
312    add |a: flex32, b: flex32| flex32::from_f32(a.to_f32() + b.to_f32()),
313    dtype DType::Flex32,
314    min flex32::from_f32(f16::MIN.to_f32_const()),
315    max flex32::from_f32(f16::MAX.to_f32_const())
316);
317
318make_element!(
319    ty bool,
320    convert ToElement::to_bool,
321    random |distribution: Distribution, rng: &mut R| {
322        let sample: u8 = distribution.sampler(rng).sample();
323        bool::from_elem(sample)
324    },
325    cmp |a: &bool, b: &bool| Ord::cmp(a, b),
326    dtype DType::Bool(BoolStore::Native),
327    min false,
328    max true
329);