ruda_test_utils/test_tensor/
base.rs1use ruda_kernel::dsl as kernel_dsl;
2use ruda_test_runtime::TestRuntime;
3use ruda_kernel::dsl::client::ComputeClient;
4use ruda_kernel::dsl::ir::ElemType;
5use ruda_kernel::dsl::ir::StorageType;
6use ruda_kernel::dsl::prelude::RudaPrimitive;
7use ruda_kernel::library::tensor::TensorHandle;
8use ruda_kernel::dsl::zspace::Shape;
9use ruda_kernel::dsl::zspace::Strides;
10use ruda_core::quant::scheme::QuantScheme;
11use ruda_kernel::quantization::scheme::QuantStore;
12
13use crate::test_tensor::{
14 arange::build_arange,
15 custom::build_custom,
16 eye::build_eye,
17 host_data::{HostData, HostDataType},
18 quant::apply_quantization,
19 random::build_random,
20 strides::StrideSpec,
21 zeros::build_zeros,
22};
23
24#[derive(Clone)]
25pub struct QuantizationInfo {
29 pub scale: TensorHandle<TestRuntime>,
31 pub scheme: QuantScheme,
33 pub shape: Shape,
35}
36
37#[derive(Clone)]
38pub struct TestTensor {
45 pub handle: TensorHandle<TestRuntime>,
47 pub host: HostData,
49 pub quantization: Option<QuantizationInfo>,
51}
52
53#[derive(Clone, Debug)]
54pub enum InputDataType {
55 Standard(StorageType),
56 Quantized(QuantScheme),
57}
58
59impl From<StorageType> for InputDataType {
60 fn from(dtype: StorageType) -> Self {
61 InputDataType::Standard(dtype)
62 }
63}
64
65impl From<ruda_kernel::dsl::ir::ElemType> for InputDataType {
66 fn from(elem: ruda_kernel::dsl::ir::ElemType) -> Self {
67 InputDataType::Standard(StorageType::Scalar(elem))
68 }
69}
70
71impl InputDataType {
72 pub fn storage_type(&self) -> StorageType {
73 match self {
74 InputDataType::Standard(dtype) => *dtype,
75 InputDataType::Quantized(scheme) => {
76 let elem = ElemType::from_quant_value(scheme.value);
77
78 match scheme.store {
79 QuantStore::Native => StorageType::Scalar(elem),
80 QuantStore::PackedNative(_) => {
81 StorageType::Packed(elem, scheme.native_packing())
83 }
84 QuantStore::PackedU32(_) => {
85 let factor = scheme.num_quants();
88 StorageType::Packed(elem, factor)
89 }
90 }
91 }
92 }
93 }
94
95 pub fn is_quantized(&self) -> bool {
96 matches!(self, InputDataType::Quantized(_))
97 }
98
99 pub fn scheme(&self) -> Option<QuantScheme> {
100 match self {
101 InputDataType::Quantized(scheme) => Some(*scheme),
102 _ => None,
103 }
104 }
105}
106
107pub struct TestInput {
108 base_spec: BaseInputSpec,
109 data_kind: DataKind,
110 input_dtype: InputDataType,
111}
112
113pub enum DataKind {
114 Arange {
115 scale: Option<f32>,
116 },
117 Eye,
118 Zeros,
119 Random {
120 seed: u64,
121 distribution: Distribution,
122 },
123 Custom {
124 data: Vec<f32>,
125 },
126}
127
128impl TestInput {
129 pub fn builder(
136 client: ComputeClient<TestRuntime>,
137 shape: impl Into<Shape>,
138 ) -> TestInputBuilder {
139 TestInputBuilder::new(client, shape.into())
140 }
141
142 pub fn new(
143 client: ComputeClient<TestRuntime>,
144 shape: impl Into<Shape>,
145 dtype: impl Into<InputDataType>,
146 stride_spec: StrideSpec,
147 data_kind: DataKind,
148 ) -> Self {
149 let dtype = dtype.into();
150 let storage_type = match &dtype {
151 InputDataType::Standard(dtype) => *dtype,
152 InputDataType::Quantized(_scheme) => {
153 f32::as_type_native_unchecked().storage_type()
156 }
157 };
158
159 let base_spec = BaseInputSpec {
160 client,
161 shape: shape.into(),
162 dtype: storage_type,
163 stride_spec,
164 };
165
166 Self {
167 base_spec,
168 data_kind,
169 input_dtype: dtype,
170 }
171 }
172
173 pub fn generate_with_f32_host_data(self) -> (TensorHandle<TestRuntime>, HostData) {
174 self.generate_host_data(HostDataType::F32)
175 }
176
177 pub fn generate_with_bool_host_data(self) -> (TensorHandle<TestRuntime>, HostData) {
178 self.generate_host_data(HostDataType::Bool)
179 }
180
181 pub fn generate_test_tensor(self) -> TestTensor {
182 let input_dtype = self.input_dtype.clone();
183 let client = self.base_spec.client.clone();
184 let (handle, host) = self.generate_with_f32_host_data();
185
186 let mut tensor = TestTensor {
187 handle,
188 host,
189 quantization: None,
190 };
191
192 if let InputDataType::Quantized(scheme) = input_dtype {
193 apply_quantization(&client, &mut tensor, scheme);
194 }
195
196 tensor
197 }
198
199 pub fn f32_host_data(self) -> HostData {
200 self.generate_host_data(HostDataType::F32).1
201 }
202
203 pub fn bool_host_data(self) -> HostData {
204 self.generate_host_data(HostDataType::Bool).1
205 }
206
207 pub fn generate_without_host_data(self) -> TensorHandle<TestRuntime> {
209 self.generate()
210 }
211
212 pub fn generate(self) -> TensorHandle<TestRuntime> {
213 let (shape, strides, dtype) = (
214 self.base_spec.shape.clone(),
215 self.base_spec.strides(),
216 self.base_spec.dtype,
217 );
218
219 let mut handle = match self.data_kind {
220 DataKind::Arange { scale } => build_arange(self.base_spec, scale),
221 DataKind::Eye => build_eye(self.base_spec),
222 DataKind::Random { seed, distribution } => {
223 build_random(self.base_spec, seed, distribution)
224 }
225 DataKind::Zeros => build_zeros(self.base_spec),
226 DataKind::Custom { data } => build_custom(self.base_spec, data),
227 };
228 handle.metadata.shape = shape;
229 handle.metadata.strides = strides;
230 handle.dtype = dtype;
231
232 handle
233 }
234
235 fn generate_host_data(
236 self,
237 host_data_type: HostDataType,
238 ) -> (TensorHandle<TestRuntime>, HostData) {
239 let client = self.base_spec.client.clone();
240
241 let tensor_handle = self.generate();
242 let host_data =
243 HostData::from_tensor_handle(&client, tensor_handle.clone(), host_data_type);
244
245 (tensor_handle, host_data)
246 }
247}
248
249pub struct BaseInputSpec {
250 pub client: ComputeClient<TestRuntime>,
251 pub shape: Shape,
252 pub dtype: StorageType,
253 pub stride_spec: StrideSpec,
254}
255
256impl BaseInputSpec {
257 pub(crate) fn strides(&self) -> Strides {
258 self.stride_spec.compute_strides(&self.shape)
259 }
260}
261
262pub struct RandomInputSpec {
263 pub seed: u64,
264 pub distribution: Distribution,
265}
266
267#[derive(Copy, Clone)]
268pub enum Distribution {
269 Uniform(f32, f32),
271 Bernoulli(f32),
273 Normal { mean: f32, std: f32 },
275}
276
277pub struct TestInputBuilder {
295 client: ComputeClient<TestRuntime>,
296 shape: Shape,
297 dtype: Option<InputDataType>,
298 stride_spec: StrideSpec,
299}
300
301impl TestInputBuilder {
302 fn new(client: ComputeClient<TestRuntime>, shape: Shape) -> Self {
303 Self {
304 client,
305 shape,
306 dtype: None,
307 stride_spec: StrideSpec::RowMajor,
308 }
309 }
310
311 pub fn dtype(mut self, dtype: impl Into<InputDataType>) -> Self {
313 self.dtype = Some(dtype.into());
314 self
315 }
316
317 pub fn stride(mut self, stride_spec: StrideSpec) -> Self {
319 self.stride_spec = stride_spec;
320 self
321 }
322
323 fn finalize(self, data_kind: DataKind) -> TestInput {
324 let dtype = self.dtype.unwrap_or_else(|| {
325 InputDataType::Standard(f32::as_type_native_unchecked().storage_type())
326 });
327 TestInput::new(self.client, self.shape, dtype, self.stride_spec, data_kind)
328 }
329
330 pub fn arange(self) -> TestInput {
332 self.finalize(DataKind::Arange { scale: None })
333 }
334
335 pub fn arange_scaled(self, scale: f32) -> TestInput {
337 self.finalize(DataKind::Arange { scale: Some(scale) })
338 }
339
340 pub fn eye(self) -> TestInput {
342 self.finalize(DataKind::Eye)
343 }
344
345 pub fn zeros(self) -> TestInput {
347 self.finalize(DataKind::Zeros)
348 }
349
350 pub fn random(self, seed: u64, distribution: Distribution) -> TestInput {
352 self.finalize(DataKind::Random { seed, distribution })
353 }
354
355 pub fn uniform(self, seed: u64, lo: f32, hi: f32) -> TestInput {
357 self.random(seed, Distribution::Uniform(lo, hi))
358 }
359
360 pub fn bernoulli(self, seed: u64, p: f32) -> TestInput {
362 self.random(seed, Distribution::Bernoulli(p))
363 }
364
365 pub fn normal(self, seed: u64, mean: f32, std: f32) -> TestInput {
367 self.random(seed, Distribution::Normal { mean, std })
368 }
369
370 pub fn custom(self, data: Vec<f32>) -> TestInput {
372 self.finalize(DataKind::Custom { data })
373 }
374
375 pub fn linspace(self, start: f32, end: f32) -> TestInput {
380 let num_elems: usize = self.shape.iter().product();
381 let data = if num_elems == 0 {
382 Vec::new()
383 } else if num_elems == 1 {
384 vec![start]
385 } else {
386 let step = (end - start) / (num_elems - 1) as f32;
387 (0..num_elems).map(|i| start + step * i as f32).collect()
388 };
389 self.finalize(DataKind::Custom { data })
390 }
391}