Skip to main content

ruda_tensor/
device.rs

1pub use ruda_core::device::*;
2use ruda_core::tensor::{BoolDType, DType, FloatDType, IntDType};
3use crate::Backend;
4pub use ruda_core::tensor::device_settings::{DeviceSettings, DeviceError};
5use ruda_core::tensor::device_settings::DeviceSettingsRegistry;
6
7#[cfg(feature = "std")]
8pub use std::collections::HashMap;
9#[cfg(not(feature = "std"))]
10pub use hashbrown::HashMap;
11
12/// Device trait for all ruda backend devices.
13pub trait DeviceOps: Clone + Default + PartialEq + Send + Sync + core::fmt::Debug + Device {
14    /// Returns the [device id](DeviceId).
15    fn id(&self) -> DeviceId {
16        self.to_id()
17    }
18
19    /// Returns the inner device without autodiff enabled.
20    ///
21    /// For most devices this is a no-op that returns `self`. For autodiff-enabled
22    /// devices, this returns the underlying inner device.
23    fn inner(&self) -> &Self {
24        self
25    }
26}
27
28/// Get the [`device`'s settings](DeviceSettings).
29pub fn get_device_settings<B: Backend>(device: &B::Device) -> DeviceSettings {
30    let default_settings = || {
31        DeviceSettings::new(
32            default_float::<B>(),
33            default_int::<B>(),
34            default_bool::<B>(device),
35        )
36    };
37    DeviceSettingsRegistry::get_or_insert(device, default_settings)
38}
39
40fn default_bool<B: Backend>(device: &B::Device) -> BoolDType {
41    // NOTE: this fallback logic is mostly tied to the dispatch backend since we still have associated
42    // element types. Once they're removed, we need to have some sort of `DeviceDefaults` trait that provides
43    // per-device defaults instead.
44
45    // dtype.into() handles u8/u32 conversion to Bool(..)
46    let default_bool: BoolDType = <B::BoolElem as crate::Element>::dtype().into();
47    ruda_core::tensor::device_settings::select_bool_dtype(default_bool, |dtype| B::supports_dtype(device, dtype))
48}
49
50fn default_float<B: Backend>() -> FloatDType {
51    <B::FloatElem as crate::Element>::dtype().into()
52}
53
54fn default_int<B: Backend>() -> IntDType {
55    <B::IntElem as crate::Element>::dtype().into()
56}
57
58fn check_dtype_support<B: Backend>(
59    device: &B::Device,
60    dtype: impl Into<DType>,
61) -> Result<(), DeviceError> {
62    let dtype = dtype.into();
63    // Default dtypes should have `DTypeUsage::general()`. Types restricted to specialized
64    // operations should not be used as default.
65    if B::supports_dtype(device, dtype) {
66        Ok(())
67    } else {
68        Err(DeviceError::unsupported_dtype(device, dtype))
69    }
70}
71
72/// Sets the default data types for the device.
73///
74/// This updates the device's default data types used for tensor creation.
75///
76/// Settings can only be initialized once per device. Subsequent calls for
77/// the same device return [`DeviceError::AlreadyInitialized`].
78///
79/// # Note
80///
81/// Initialization must happen before any tensor creation on the device.
82/// The first tensor operation will lock the device to its defaults, causing
83/// any subsequent initialization attempt to return [`DeviceError::AlreadyInitialized`].
84///
85/// # Example
86///
87/// ```rust, ignore
88/// fn example<B: Backend>() {
89///     let device = B::Device::default();
90///     
91///     // Update the device settings
92///     set_default_dtypes::<B>(&device, DType::F16, DType::I32);
93///     
94///     // All float tensors created after this will use F16 by default
95///     let tensor = Tensor::<B, 2>::zeros([2, 3], &device);
96///     // All int tensors created after this will use I32 default
97///     let tensor = Tensor::<B, 2, Int>::zeros([2, 3], &device);
98/// }
99/// ```
100pub fn set_default_dtypes<B: Backend>(
101    device: &B::Device,
102    float_dtype: impl Into<FloatDType>,
103    int_dtype: impl Into<IntDType>,
104) -> Result<(), DeviceError> {
105    let float_dtype = float_dtype.into();
106    let int_dtype = int_dtype.into();
107    check_dtype_support::<B>(device, float_dtype)?;
108    check_dtype_support::<B>(device, int_dtype)?;
109
110    let settings = DeviceSettings::new(float_dtype, int_dtype, default_bool::<B>(device));
111
112    initialize_unchecked(device, settings)?;
113    Ok(())
114}
115
116/// Sets the default floating-point data type for the device.
117///
118/// This updates the device's default data types used for tensor creation.
119///
120/// Settings can only be initialized once per device. Subsequent calls for
121/// the same device return [`DeviceError::AlreadyInitialized`].
122///
123/// # Note
124///
125/// Initialization must happen before any tensor creation on the device.
126/// The first tensor operation will lock the device to its defaults, causing
127/// any subsequent initialization attempt to return [`DeviceError::AlreadyInitialized`].
128///
129/// # Example
130///
131/// ```rust, ignore
132/// fn example<B: Backend>() {
133///     let device = B::Device::default();
134///     
135///     // Update the device settings
136///     set_default_float_dtype::<B>(&device, DType::F16);
137///     
138///     // All float tensors created after this will use F16 by default
139///     let tensor = Tensor::<B, 2>::zeros([2, 3], &device);
140/// }
141/// ```
142pub fn set_default_float_dtype<B: Backend>(
143    device: &B::Device,
144    dtype: impl Into<FloatDType>,
145) -> Result<(), DeviceError> {
146    let dtype = dtype.into();
147    check_dtype_support::<B>(device, dtype)?;
148
149    let settings = DeviceSettings::new(dtype, default_int::<B>(), default_bool::<B>(device));
150
151    initialize_unchecked(device, settings)?;
152    Ok(())
153}
154
155/// Sets the default integer data type for the device.
156///
157/// This updates the device's default data types used for tensor creation.
158///
159/// Settings can only be initialized once per device. Subsequent calls for
160/// the same device return [`DeviceError::AlreadyInitialized`].
161///
162/// # Note
163///
164/// Initialization must happen before any tensor creation on the device.
165/// The first tensor operation will lock the device to its defaults, causing
166/// any subsequent initialization attempt to return [`DeviceError::AlreadyInitialized`].
167///
168/// # Example
169///
170/// ```rust, ignore
171/// fn example<B: Backend>() {
172///     let device = B::Device::default();
173///     
174///     // Update the device settings
175///     set_default_int_dtype::<B>(&device, DType::I32);
176///     
177///     // All int tensors created after this will use I32 default
178///     let tensor = Tensor::<B, 2, Int>::zeros([2, 3], &device);
179/// }
180/// ```
181pub fn set_default_int_dtype<B: Backend>(
182    device: &B::Device,
183    dtype: impl Into<IntDType>,
184) -> Result<(), DeviceError> {
185    let dtype = dtype.into();
186    check_dtype_support::<B>(device, dtype)?;
187
188    let settings = DeviceSettings::new(default_float::<B>(), dtype, default_bool::<B>(device));
189
190    initialize_unchecked(device, settings)?;
191    Ok(())
192}
193
194// Unchecked dtypes
195fn initialize_unchecked<D: DeviceOps>(
196    device: &D,
197    settings: DeviceSettings,
198) -> Result<(), DeviceError> {
199    DeviceSettingsRegistry::init(device, settings)
200}
201
202mod adapters;