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;