1use std::borrow::Cow;
4
5use super::kernels::{elementwise as ew, indexing, matmul, reduce};
6use super::{Cpu, CpuBoolStorage, CpuFloatStorage, CpuIntStorage, int_ids_as_usize};
7use crate::dtype::{BoolDType, FloatDType, IntDType};
8use crate::{
9 BinaryOp, CmpOp, DType, Device, Error, IntOps, Layout, Result, Shape, dispatch_int, dispatch_int_raw, dispatch_int2, dispatch_int2_raw,
10};
11
12fn build_int(n: usize, dtype: IntDType, v: i64) -> CpuIntStorage {
13 match dtype {
14 IntDType::I32 => CpuIntStorage::I32(vec![v as i32; n]),
15 IntDType::U32 => CpuIntStorage::U32(vec![v as u32; n]),
16 IntDType::U8 => CpuIntStorage::U8(vec![v as u8; n]),
17 }
18}
19
20impl IntOps<Cpu> for Cpu {
21 fn i_zeros(shape: &Shape, _device: &Cpu, dtype: IntDType) -> Result<<Cpu as Device>::IntStorage> {
22 Ok(build_int(shape.element_count(), dtype, 0))
23 }
24
25 fn i_ones(shape: &Shape, _device: &Cpu, dtype: IntDType) -> Result<<Cpu as Device>::IntStorage> {
26 Ok(build_int(shape.element_count(), dtype, 1))
27 }
28
29 fn i_full(shape: &Shape, value: i64, _device: &Cpu, dtype: IntDType) -> Result<<Cpu as Device>::IntStorage> {
30 Ok(build_int(shape.element_count(), dtype, value))
31 }
32
33 fn i_from_i64<'a>(data: impl Into<Cow<'a, [i64]>>, _device: &Cpu) -> Result<<Cpu as Device>::IntStorage> {
34 let data = data.into();
35 Ok(match data {
36 Cow::Owned(v) => CpuIntStorage::I32(v.iter().map(|&x| x as i32).collect()),
37 Cow::Borrowed(s) => CpuIntStorage::I32(s.iter().map(|&x| x as i32).collect()),
38 })
39 }
40
41 fn i_from_i32<'a>(data: impl Into<Cow<'a, [i32]>>, _device: &Cpu) -> Result<<Cpu as Device>::IntStorage> {
42 let data = data.into();
43 Ok(match data {
44 Cow::Owned(v) => CpuIntStorage::I32(v),
45 Cow::Borrowed(s) => CpuIntStorage::I32(s.to_vec()),
46 })
47 }
48
49 fn i_from_u32<'a>(data: impl Into<Cow<'a, [u32]>>, _device: &Cpu) -> Result<<Cpu as Device>::IntStorage> {
50 let data = data.into();
51 Ok(match data {
52 Cow::Owned(v) => CpuIntStorage::U32(v),
53 Cow::Borrowed(s) => CpuIntStorage::U32(s.to_vec()),
54 })
55 }
56
57 fn i_from_u8<'a>(data: impl Into<Cow<'a, [u8]>>, _device: &Cpu) -> Result<<Cpu as Device>::IntStorage> {
58 let data = data.into();
59 Ok(match data {
60 Cow::Owned(v) => CpuIntStorage::U8(v),
61 Cow::Borrowed(s) => CpuIntStorage::U8(s.to_vec()),
62 })
63 }
64
65 fn i_from_bytes<'a>(
66 bytes: impl Into<Cow<'a, [u8]>>,
67 _shape: &Shape,
68 _device: &Cpu,
69 dtype: IntDType,
70 ) -> Result<<Cpu as Device>::IntStorage> {
71 let bytes = bytes.into();
72 Ok(match dtype {
73 IntDType::I32 => {
74 let v: Vec<i32> = bytes.chunks_exact(4).map(|c| i32::from_le_bytes(c.try_into().unwrap())).collect();
75 CpuIntStorage::I32(v)
76 }
77 IntDType::U32 => {
78 let v: Vec<u32> = bytes.chunks_exact(4).map(|c| u32::from_le_bytes(c.try_into().unwrap())).collect();
79 CpuIntStorage::U32(v)
80 }
81 IntDType::U8 => match bytes {
82 Cow::Owned(b) => CpuIntStorage::U8(b),
83 Cow::Borrowed(b) => CpuIntStorage::U8(b.to_vec()),
84 },
85 })
86 }
87
88 fn i_arange(start: i64, end: i64, step: i64, _device: &Cpu, dtype: IntDType) -> Result<(<Cpu as Device>::IntStorage, usize)> {
89 if step == 0 {
90 return Err(Error::Msg("arange step cannot be 0".into()));
91 }
92 let mut data = Vec::new();
93 let mut v = start;
94 if step > 0 {
95 while v < end {
96 data.push(v);
97 v += step;
98 }
99 } else {
100 while v > end {
101 data.push(v);
102 v += step;
103 }
104 }
105 let n = data.len();
106 let storage = match dtype {
107 IntDType::I32 => {
108 let v: Vec<i32> = data.iter().map(|&x| x as i32).collect();
109 Cpu::i_from_i32(v, &Cpu)?
110 }
111 IntDType::U32 => {
112 let v: Vec<u32> = data.iter().map(|&x| x as u32).collect();
113 Cpu::i_from_u32(v, &Cpu)?
114 }
115 IntDType::U8 => {
116 let v: Vec<u8> = data.iter().map(|&x| x as u8).collect();
117 Cpu::i_from_u8(v, &Cpu)?
118 }
119 };
120 Ok((storage, n))
121 }
122
123 fn i_contiguous(x: &<Cpu as Device>::IntStorage, l: &Layout) -> Result<<Cpu as Device>::IntStorage> {
124 Ok(dispatch_int!(x, |d| super::kernels::iter::gather(d, l)))
125 }
126
127 fn i_cast_float(x: &CpuIntStorage, layout: &Layout, to: FloatDType) -> Result<CpuFloatStorage> {
128 let s = match to {
129 FloatDType::F32 => CpuFloatStorage::F32(dispatch_int_raw!(x, |d| layout.storage_indices().map(|i| d[i] as f32).collect())),
130 FloatDType::F64 => CpuFloatStorage::F64(dispatch_int_raw!(x, |d| layout.storage_indices().map(|i| d[i] as f64).collect())),
131 };
132 Ok(s)
133 }
134
135 fn i_cast_int(x: &CpuIntStorage, layout: &Layout, to: IntDType) -> Result<CpuIntStorage> {
136 let s = match to {
137 IntDType::I32 => CpuIntStorage::I32(dispatch_int_raw!(x, |d| layout.storage_indices().map(|i| d[i] as i32).collect())),
138 IntDType::U32 => CpuIntStorage::U32(dispatch_int_raw!(x, |d| layout.storage_indices().map(|i| d[i] as u32).collect())),
139 IntDType::U8 => CpuIntStorage::U8(dispatch_int_raw!(x, |d| layout.storage_indices().map(|i| d[i] as u8).collect())),
140 };
141 Ok(s)
142 }
143
144 fn i_cast_bool(x: &CpuIntStorage, layout: &Layout, _to: BoolDType) -> Result<CpuBoolStorage> {
145 Ok(CpuBoolStorage(dispatch_int_raw!(x, |d| layout.storage_indices().map(|i| d[i] != 0).collect())))
146 }
147
148 fn i_to_vec(x: &<Cpu as Device>::IntStorage, layout: &Layout) -> Result<Vec<i64>> {
149 Ok(match x {
150 CpuIntStorage::I32(d) => layout.storage_indices().map(|i| d[i] as i64).collect(),
151 CpuIntStorage::U32(d) => layout.storage_indices().map(|i| d[i] as i64).collect(),
152 CpuIntStorage::U8(d) => layout.storage_indices().map(|i| d[i] as i64).collect(),
153 })
154 }
155
156 fn i_to_bytes<'a>(x: &'a <Cpu as Device>::IntStorage, layout: &Layout) -> Result<Cow<'a, [u8]>> {
157 if layout.is_contiguous() {
158 Ok(match x {
159 CpuIntStorage::I32(d) => Cow::Borrowed(bytemuck::cast_slice(d)),
160 CpuIntStorage::U32(d) => Cow::Borrowed(bytemuck::cast_slice(d)),
161 CpuIntStorage::U8(d) => Cow::Borrowed(d),
162 })
163 } else {
164 let contig = Self::i_contiguous(x, layout)?;
165 Ok(match contig {
166 CpuIntStorage::I32(d) => Cow::Owned(bytemuck::cast_slice(&d).to_vec()),
167 CpuIntStorage::U32(d) => Cow::Owned(bytemuck::cast_slice(&d).to_vec()),
168 CpuIntStorage::U8(d) => Cow::Owned(d),
169 })
170 }
171 }
172
173 fn i_binary(
174 lhs: &<Cpu as Device>::IntStorage,
175 lhs_l: &Layout,
176 rhs: &<Cpu as Device>::IntStorage,
177 rhs_l: &Layout,
178 op: BinaryOp,
179 ) -> Result<<Cpu as Device>::IntStorage> {
180 dispatch_int2!(lhs, rhs, "int binary", |a, b| ew::num_binary(a, lhs_l, b, rhs_l, op))
181 }
182
183 fn i_binary_scalar(lhs: &<Cpu as Device>::IntStorage, lhs_l: &Layout, rhs: i64, op: BinaryOp) -> Result<<Cpu as Device>::IntStorage> {
184 Ok(match lhs {
185 CpuIntStorage::I32(d) => CpuIntStorage::I32(ew::num_binary_scalar(d, lhs_l, rhs as i32, op)),
186 CpuIntStorage::U32(d) => CpuIntStorage::U32(ew::num_binary_scalar(d, lhs_l, rhs as u32, op)),
187 CpuIntStorage::U8(d) => CpuIntStorage::U8(ew::num_binary_scalar(d, lhs_l, rhs as u8, op)),
188 })
189 }
190
191 fn i_binary_(
192 dst: &mut <Cpu as Device>::IntStorage,
193 dst_l: &Layout,
194 src: &<Cpu as Device>::IntStorage,
195 src_l: &Layout,
196 op: BinaryOp,
197 ) -> Result<()> {
198 match (dst, src) {
199 (CpuIntStorage::I32(d), CpuIntStorage::I32(s)) => {
200 ew::binary_(d, dst_l, s, src_l, ew::num_binary_fn::<i32>(op));
201 Ok(())
202 }
203 (CpuIntStorage::U32(d), CpuIntStorage::U32(s)) => {
204 ew::binary_(d, dst_l, s, src_l, ew::num_binary_fn::<u32>(op));
205 Ok(())
206 }
207 (CpuIntStorage::U8(d), CpuIntStorage::U8(s)) => {
208 ew::binary_(d, dst_l, s, src_l, ew::num_binary_fn::<u8>(op));
209 Ok(())
210 }
211 (l, r) => Err(Error::DTypeMismatch { lhs: l.dtype(), rhs: r.dtype(), op: "int binary_" }),
212 }
213 }
214
215 fn i_binary_scalar_(dst: &mut <Cpu as Device>::IntStorage, dst_l: &Layout, rhs: i64, op: BinaryOp) -> Result<()> {
216 match dst {
217 CpuIntStorage::I32(d) => {
218 ew::binary_scalar_(d, dst_l, rhs as i32, ew::num_binary_fn::<i32>(op));
219 Ok(())
220 }
221 CpuIntStorage::U32(d) => {
222 ew::binary_scalar_(d, dst_l, rhs as u32, ew::num_binary_fn::<u32>(op));
223 Ok(())
224 }
225 CpuIntStorage::U8(d) => {
226 ew::binary_scalar_(d, dst_l, rhs as u8, ew::num_binary_fn::<u8>(op));
227 Ok(())
228 }
229 }
230 }
231
232 fn i_binary_scalar_lhs(
233 scalar: i64,
234 rhs: &<Cpu as Device>::IntStorage,
235 rhs_l: &Layout,
236 op: BinaryOp,
237 ) -> Result<<Cpu as Device>::IntStorage> {
238 Ok(match rhs {
239 CpuIntStorage::I32(d) => CpuIntStorage::I32(ew::num_scalar_binary(scalar as i32, d, rhs_l, op)),
240 CpuIntStorage::U32(d) => CpuIntStorage::U32(ew::num_scalar_binary(scalar as u32, d, rhs_l, op)),
241 CpuIntStorage::U8(d) => CpuIntStorage::U8(ew::num_scalar_binary(scalar as u8, d, rhs_l, op)),
242 })
243 }
244
245 fn i_unary(x: &<Cpu as Device>::IntStorage, l: &Layout, op: crate::UnaryOp<i64>) -> Result<<Cpu as Device>::IntStorage> {
246 use super::kernels::element::CpuNum;
247 match x {
248 CpuIntStorage::I32(d) => Ok(CpuIntStorage::I32(match op {
249 crate::UnaryOp::Neg => ew::unary(d, l, |v: i32| -v),
250 crate::UnaryOp::Abs => ew::unary(d, l, |v: i32| CpuNum::abs(v)),
251 crate::UnaryOp::Sign => ew::unary(d, l, |v: i32| CpuNum::signum(v)),
252 crate::UnaryOp::Affine(mul, add) => ew::unary(d, l, |v: i32| (v as i64 * mul + add) as i32),
253 crate::UnaryOp::Pow(exp) => ew::unary(d, l, |v: i32| (v as i64).pow(exp as u32) as i32),
254 crate::UnaryOp::Clamp(min, max) => {
255 let lo = min.map(|v| v as i32);
256 let hi = max.map(|v| v as i32);
257 ew::unary(d, l, |v: i32| {
258 let mut val = v;
259 if let Some(lo) = lo {
260 val = val.max(lo);
261 }
262 if let Some(hi) = hi {
263 val = val.min(hi);
264 }
265 val
266 })
267 }
268 })),
269 CpuIntStorage::U32(d) => Ok(CpuIntStorage::U32(match op {
270 crate::UnaryOp::Neg => ew::unary(d, l, |v: u32| -(v as i32) as u32),
271 crate::UnaryOp::Abs => ew::unary(d, l, |v: u32| CpuNum::abs(v)),
272 crate::UnaryOp::Sign => ew::unary(d, l, |v: u32| CpuNum::signum(v)),
273 crate::UnaryOp::Affine(mul, add) => ew::unary(d, l, |v: u32| (v as u64 * mul as u64 + add as u64) as u32),
274 crate::UnaryOp::Pow(exp) => ew::unary(d, l, |v: u32| (v as u64).pow(exp as u32) as u32),
275 crate::UnaryOp::Clamp(min, max) => {
276 let lo = min.map(|v| v.max(0) as u32);
277 let hi = max.map(|v| v as u32);
278 ew::unary(d, l, |v: u32| {
279 let mut val = v;
280 if let Some(lo) = lo {
281 val = val.max(lo);
282 }
283 if let Some(hi) = hi {
284 val = val.min(hi);
285 }
286 val
287 })
288 }
289 })),
290 CpuIntStorage::U8(d) => Ok(CpuIntStorage::U8(match op {
291 crate::UnaryOp::Neg => ew::unary(d, l, |v: u8| -(v as i32) as u8),
292 crate::UnaryOp::Abs => ew::unary(d, l, |v: u8| CpuNum::abs(v)),
293 crate::UnaryOp::Sign => ew::unary(d, l, |v: u8| CpuNum::signum(v)),
294 crate::UnaryOp::Affine(mul, add) => ew::unary(d, l, |v: u8| (v as i64 * mul + add) as u8),
295 crate::UnaryOp::Pow(exp) => ew::unary(d, l, |v: u8| (v as u64).pow(exp as u32) as u8),
296 crate::UnaryOp::Clamp(min, max) => {
297 let lo = min.map(|v| v.max(0) as u8);
298 let hi = max.map(|v| v as u8);
299 ew::unary(d, l, |v: u8| {
300 let mut val = v;
301 if let Some(lo) = lo {
302 val = val.max(lo);
303 }
304 if let Some(hi) = hi {
305 val = val.min(hi);
306 }
307 val
308 })
309 }
310 })),
311 }
312 }
313
314 fn i_unary_(dst: &mut <Cpu as Device>::IntStorage, dst_l: &Layout, op: crate::UnaryOp<i64>) -> Result<()> {
315 use super::kernels::element::CpuNum;
316 match dst {
317 CpuIntStorage::I32(d) => match op {
318 crate::UnaryOp::Neg => {
319 ew::unary_(d, dst_l, |v: i32| -v);
320 Ok(())
321 }
322 crate::UnaryOp::Abs => {
323 ew::unary_(d, dst_l, |v: i32| CpuNum::abs(v));
324 Ok(())
325 }
326 crate::UnaryOp::Sign => {
327 ew::unary_(d, dst_l, |v: i32| CpuNum::signum(v));
328 Ok(())
329 }
330 crate::UnaryOp::Affine(mul, add) => {
331 ew::unary_(d, dst_l, |v: i32| (v as i64 * mul + add) as i32);
332 Ok(())
333 }
334 crate::UnaryOp::Pow(exp) => {
335 ew::unary_(d, dst_l, |v: i32| (v as i64).pow(exp as u32) as i32);
336 Ok(())
337 }
338 crate::UnaryOp::Clamp(min, max) => {
339 let lo = min.map(|v| v as i32);
340 let hi = max.map(|v| v as i32);
341 ew::unary_(d, dst_l, |v: i32| {
342 let mut val = v;
343 if let Some(lo) = lo {
344 val = val.max(lo);
345 }
346 if let Some(hi) = hi {
347 val = val.min(hi);
348 }
349 val
350 });
351 Ok(())
352 }
353 },
354 CpuIntStorage::U32(d) => match op {
355 crate::UnaryOp::Neg => {
356 ew::unary_(d, dst_l, |v: u32| -(v as i32) as u32);
357 Ok(())
358 }
359 crate::UnaryOp::Abs => {
360 ew::unary_(d, dst_l, |v: u32| CpuNum::abs(v));
361 Ok(())
362 }
363 crate::UnaryOp::Sign => {
364 ew::unary_(d, dst_l, |v: u32| CpuNum::signum(v));
365 Ok(())
366 }
367 crate::UnaryOp::Affine(mul, add) => {
368 ew::unary_(d, dst_l, |v: u32| (v as u64 * mul as u64 + add as u64) as u32);
369 Ok(())
370 }
371 crate::UnaryOp::Pow(exp) => {
372 ew::unary_(d, dst_l, |v: u32| (v as u64).pow(exp as u32) as u32);
373 Ok(())
374 }
375 crate::UnaryOp::Clamp(min, max) => {
376 let lo = min.map(|v| v.max(0) as u32);
377 let hi = max.map(|v| v as u32);
378 ew::unary_(d, dst_l, |v: u32| {
379 let mut val = v;
380 if let Some(lo) = lo {
381 val = val.max(lo);
382 }
383 if let Some(hi) = hi {
384 val = val.min(hi);
385 }
386 val
387 });
388 Ok(())
389 }
390 },
391 CpuIntStorage::U8(d) => match op {
392 crate::UnaryOp::Neg => {
393 ew::unary_(d, dst_l, |v: u8| -(v as i32) as u8);
394 Ok(())
395 }
396 crate::UnaryOp::Abs => {
397 ew::unary_(d, dst_l, |v: u8| CpuNum::abs(v));
398 Ok(())
399 }
400 crate::UnaryOp::Sign => {
401 ew::unary_(d, dst_l, |v: u8| CpuNum::signum(v));
402 Ok(())
403 }
404 crate::UnaryOp::Affine(mul, add) => {
405 ew::unary_(d, dst_l, |v: u8| (v as i64 * mul + add) as u8);
406 Ok(())
407 }
408 crate::UnaryOp::Pow(exp) => {
409 ew::unary_(d, dst_l, |v: u8| (v as u64).pow(exp as u32) as u8);
410 Ok(())
411 }
412 crate::UnaryOp::Clamp(min, max) => {
413 let lo = min.map(|v| v.max(0) as u8);
414 let hi = max.map(|v| v as u8);
415 ew::unary_(d, dst_l, |v: u8| {
416 let mut val = v;
417 if let Some(lo) = lo {
418 val = val.max(lo);
419 }
420 if let Some(hi) = hi {
421 val = val.min(hi);
422 }
423 val
424 });
425 Ok(())
426 }
427 },
428 }
429 }
430
431 fn i_matmul(
432 lhs: &<Cpu as Device>::IntStorage,
433 lhs_l: &Layout,
434 rhs: &<Cpu as Device>::IntStorage,
435 rhs_l: &Layout,
436 ) -> Result<(<Cpu as Device>::IntStorage, Shape)> {
437 let result = match (lhs, rhs) {
438 (CpuIntStorage::I32(a), CpuIntStorage::I32(b)) => {
439 let (vec, shape) = matmul::matmul(a, lhs_l, b, rhs_l)?;
440 (CpuIntStorage::I32(vec), shape)
441 }
442 (CpuIntStorage::U32(a), CpuIntStorage::U32(b)) => {
443 let (vec, shape) = matmul::matmul(a, lhs_l, b, rhs_l)?;
444 (CpuIntStorage::U32(vec), shape)
445 }
446 (CpuIntStorage::U8(a), CpuIntStorage::U8(b)) => {
447 let (vec, shape) = matmul::matmul(a, lhs_l, b, rhs_l)?;
448 (CpuIntStorage::U8(vec), shape)
449 }
450 (l, r) => return Err(Error::DTypeMismatch { lhs: l.dtype(), rhs: r.dtype(), op: "int matmul" }),
451 };
452 Ok(result)
453 }
454
455 fn i_cmp(
456 lhs: &<Cpu as Device>::IntStorage,
457 lhs_l: &Layout,
458 rhs: &<Cpu as Device>::IntStorage,
459 rhs_l: &Layout,
460 op: CmpOp,
461 ) -> Result<<Cpu as Device>::BoolStorage> {
462 let v = dispatch_int2_raw!(lhs, rhs, "int cmp", |a, b| ew::num_cmp(a, lhs_l, b, rhs_l, op))?;
463 Ok(CpuBoolStorage(v))
464 }
465
466 fn i_cmp_scalar(lhs: &<Cpu as Device>::IntStorage, lhs_l: &Layout, rhs: i64, op: CmpOp) -> Result<<Cpu as Device>::BoolStorage> {
467 Ok(match lhs {
468 CpuIntStorage::I32(d) => CpuBoolStorage(ew::cmp_scalar(d, lhs_l, rhs as i32, op)),
469 CpuIntStorage::U32(d) => CpuBoolStorage(ew::cmp_scalar(d, lhs_l, rhs as u32, op)),
470 CpuIntStorage::U8(d) => CpuBoolStorage(ew::cmp_scalar(d, lhs_l, rhs as u8, op)),
471 })
472 }
473
474 fn i_reduce(
475 x: &<Cpu as Device>::IntStorage,
476 l: &Layout,
477 dims: &[usize],
478 keepdim: bool,
479 op: crate::ReduceOp,
480 ) -> Result<(<Cpu as Device>::IntStorage, Shape)> {
481 let reducer = reduce::Reducer::from(op);
482 match x {
483 CpuIntStorage::I32(d) => {
484 let (v, s) = reduce::reduce_dims(d, l, dims, keepdim, reducer)?;
485 Ok((CpuIntStorage::I32(v), s))
486 }
487 CpuIntStorage::U32(d) => {
488 let (v, s) = reduce::reduce_dims(d, l, dims, keepdim, reducer)?;
489 Ok((CpuIntStorage::U32(v), s))
490 }
491 CpuIntStorage::U8(d) => {
492 let (v, s) = reduce::reduce_dims(d, l, dims, keepdim, reducer)?;
493 Ok((CpuIntStorage::U8(v), s))
494 }
495 }
496 }
497
498 fn i_index_select(
499 x: &<Cpu as Device>::IntStorage,
500 x_l: &Layout,
501 idx: &<Cpu as Device>::IntStorage,
502 idx_l: &Layout,
503 dim: usize,
504 ) -> Result<(<Cpu as Device>::IntStorage, Shape)> {
505 let ids = int_ids_as_usize(idx, idx_l);
506 match x {
507 CpuIntStorage::I32(d) => {
508 let (v, dims) = indexing::index_select(d, x_l, &ids, idx_l, dim)?;
509 Ok((CpuIntStorage::I32(v), Shape::from(dims)))
510 }
511 CpuIntStorage::U32(d) => {
512 let (v, dims) = indexing::index_select(d, x_l, &ids, idx_l, dim)?;
513 Ok((CpuIntStorage::U32(v), Shape::from(dims)))
514 }
515 CpuIntStorage::U8(d) => {
516 let (v, dims) = indexing::index_select(d, x_l, &ids, idx_l, dim)?;
517 Ok((CpuIntStorage::U8(v), Shape::from(dims)))
518 }
519 }
520 }
521
522 fn i_gather(
523 x: &<Cpu as Device>::IntStorage,
524 x_l: &Layout,
525 idx: &<Cpu as Device>::IntStorage,
526 idx_l: &Layout,
527 dim: usize,
528 ) -> Result<(<Cpu as Device>::IntStorage, Shape)> {
529 let ids = int_ids_as_usize(idx, idx_l);
530 match x {
531 CpuIntStorage::I32(d) => {
532 let (v, dims) = indexing::gather(d, x_l, &ids, idx_l, dim)?;
533 Ok((CpuIntStorage::I32(v), Shape::from(dims)))
534 }
535 CpuIntStorage::U32(d) => {
536 let (v, dims) = indexing::gather(d, x_l, &ids, idx_l, dim)?;
537 Ok((CpuIntStorage::U32(v), Shape::from(dims)))
538 }
539 CpuIntStorage::U8(d) => {
540 let (v, dims) = indexing::gather(d, x_l, &ids, idx_l, dim)?;
541 Ok((CpuIntStorage::U8(v), Shape::from(dims)))
542 }
543 }
544 }
545
546 fn i_cat(srcs: &[(&<Cpu as Device>::IntStorage, &Layout)], dim: usize) -> Result<(<Cpu as Device>::IntStorage, Shape)> {
547 if srcs.is_empty() {
548 return Err(Error::OpRequiresAtLeastOneTensor { op: "cat" });
549 }
550 let dt = srcs[0].0.dtype();
551 for (s, _) in srcs {
552 if s.dtype() != dt {
553 return Err(Error::DTypeMismatch { lhs: dt, rhs: s.dtype(), op: "cat" });
554 }
555 }
556 macro_rules! cat_variant {
557 ($variant:path, $getter:ident) => {{
558 let views: Vec<(&[_], &Layout)> = srcs.iter().map(|(s, l)| ($getter(s), *l)).collect();
559 let (v, shape) = super::kernels::shape::cat(&views, dim)?;
560 Ok(($variant(v), shape))
561 }};
562 }
563 match dt {
564 DType::I32 => cat_variant!(CpuIntStorage::I32, as_i32),
565 DType::U32 => cat_variant!(CpuIntStorage::U32, as_u32),
566 _ => cat_variant!(CpuIntStorage::U8, as_u8),
567 }
568 }
569
570 fn i_arg_reduce(
571 x: &<Cpu as Device>::IntStorage,
572 layout: &Layout,
573 dim: usize,
574 keepdim: bool,
575 take_max: bool,
576 ) -> Result<(<Cpu as Device>::IntStorage, Shape)> {
577 let (indices, shape) = dispatch_int_raw!(x, |d| reduce::arg_reduce(d, layout, dim, keepdim, take_max))?;
578 let indices_u32: Vec<u32> = indices.into_iter().map(|i| i as u32).collect();
579 Ok((CpuIntStorage::U32(indices_u32), shape))
580 }
581
582 fn i_index_add(
583 init: &<Cpu as Device>::IntStorage,
584 init_l: &Layout,
585 idx: &<Cpu as Device>::IntStorage,
586 idx_l: &Layout,
587 src: &<Cpu as Device>::IntStorage,
588 _src_l: &Layout,
589 dim: usize,
590 ) -> Result<<Cpu as Device>::IntStorage> {
591 let idx_u = int_ids_as_usize(idx, idx_l);
592 match (init, src) {
593 (CpuIntStorage::I32(init_d), CpuIntStorage::I32(src_d)) => {
594 let result = indexing::index_add(init_d, init_l, &idx_u, idx_l, src_d, dim)?;
595 Ok(CpuIntStorage::I32(result))
596 }
597 (CpuIntStorage::U32(init_d), CpuIntStorage::U32(src_d)) => {
598 let result = indexing::index_add(init_d, init_l, &idx_u, idx_l, src_d, dim)?;
599 Ok(CpuIntStorage::U32(result))
600 }
601 (CpuIntStorage::U8(init_d), CpuIntStorage::U8(src_d)) => {
602 let result = indexing::index_add(init_d, init_l, &idx_u, idx_l, src_d, dim)?;
603 Ok(CpuIntStorage::U8(result))
604 }
605 (l, r) => Err(Error::DTypeMismatch { lhs: l.dtype(), rhs: r.dtype(), op: "int index_add" }),
606 }
607 }
608
609 fn i_scatter_add(
610 init: &<Cpu as Device>::IntStorage,
611 init_l: &Layout,
612 idx: &<Cpu as Device>::IntStorage,
613 idx_l: &Layout,
614 src: &<Cpu as Device>::IntStorage,
615 _src_l: &Layout,
616 dim: usize,
617 ) -> Result<<Cpu as Device>::IntStorage> {
618 let idx_u = int_ids_as_usize(idx, idx_l);
619 match (init, src) {
620 (CpuIntStorage::I32(init_d), CpuIntStorage::I32(src_d)) => {
621 let result = indexing::scatter_add(init_d, init_l, &idx_u, idx_l, src_d, dim)?;
622 Ok(CpuIntStorage::I32(result))
623 }
624 (CpuIntStorage::U32(init_d), CpuIntStorage::U32(src_d)) => {
625 let result = indexing::scatter_add(init_d, init_l, &idx_u, idx_l, src_d, dim)?;
626 Ok(CpuIntStorage::U32(result))
627 }
628 (CpuIntStorage::U8(init_d), CpuIntStorage::U8(src_d)) => {
629 let result = indexing::scatter_add(init_d, init_l, &idx_u, idx_l, src_d, dim)?;
630 Ok(CpuIntStorage::U8(result))
631 }
632 (l, r) => Err(Error::DTypeMismatch { lhs: l.dtype(), rhs: r.dtype(), op: "int scatter_add" }),
633 }
634 }
635
636 fn i_pick(
637 mask: &<Cpu as Device>::BoolStorage,
638 mask_l: &Layout,
639 on_true: &<Cpu as Device>::IntStorage,
640 true_l: &Layout,
641 on_false: &<Cpu as Device>::IntStorage,
642 false_l: &Layout,
643 ) -> Result<<Cpu as Device>::IntStorage> {
644 let m: Vec<bool> = mask_l.storage_indices().map(|i| mask.0[i]).collect();
645 match (on_true, on_false) {
646 (CpuIntStorage::I32(t), CpuIntStorage::I32(f)) => {
647 let tv: Vec<i32> = true_l.storage_indices().map(|i| t[i]).collect();
648 let fv: Vec<i32> = false_l.storage_indices().map(|i| f[i]).collect();
649 Ok(CpuIntStorage::I32(m.iter().enumerate().map(|(i, &c)| if c { tv[i] } else { fv[i] }).collect()))
650 }
651 (CpuIntStorage::U32(t), CpuIntStorage::U32(f)) => {
652 let tv: Vec<u32> = true_l.storage_indices().map(|i| t[i]).collect();
653 let fv: Vec<u32> = false_l.storage_indices().map(|i| f[i]).collect();
654 Ok(CpuIntStorage::U32(m.iter().enumerate().map(|(i, &c)| if c { tv[i] } else { fv[i] }).collect()))
655 }
656 (CpuIntStorage::U8(t), CpuIntStorage::U8(f)) => {
657 let tv: Vec<u8> = true_l.storage_indices().map(|i| t[i]).collect();
658 let fv: Vec<u8> = false_l.storage_indices().map(|i| f[i]).collect();
659 Ok(CpuIntStorage::U8(m.iter().enumerate().map(|(i, &c)| if c { tv[i] } else { fv[i] }).collect()))
660 }
661 (l, r) => Err(Error::DTypeMismatch { lhs: l.dtype(), rhs: r.dtype(), op: "pick" }),
662 }
663 }
664
665 fn i_pick_true(
666 mask: &<Cpu as Device>::BoolStorage,
667 mask_l: &Layout,
668 value: i64,
669 on_false: &<Cpu as Device>::IntStorage,
670 false_l: &Layout,
671 ) -> Result<<Cpu as Device>::IntStorage> {
672 let m: Vec<bool> = mask_l.storage_indices().map(|i| mask.0[i]).collect();
673 match on_false {
674 CpuIntStorage::I32(f) => {
675 let fv: Vec<i32> = false_l.storage_indices().map(|i| f[i]).collect();
676 let val = value as i32;
677 Ok(CpuIntStorage::I32(m.iter().enumerate().map(|(i, &c)| if c { val } else { fv[i] }).collect()))
678 }
679 CpuIntStorage::U32(f) => {
680 let fv: Vec<u32> = false_l.storage_indices().map(|i| f[i]).collect();
681 let val = value as u32;
682 Ok(CpuIntStorage::U32(m.iter().enumerate().map(|(i, &c)| if c { val } else { fv[i] }).collect()))
683 }
684 CpuIntStorage::U8(f) => {
685 let fv: Vec<u8> = false_l.storage_indices().map(|i| f[i]).collect();
686 let val = value as u8;
687 Ok(CpuIntStorage::U8(m.iter().enumerate().map(|(i, &c)| if c { val } else { fv[i] }).collect()))
688 }
689 }
690 }
691
692 fn i_pick_false(
693 mask: &<Cpu as Device>::BoolStorage,
694 mask_l: &Layout,
695 on_true: &<Cpu as Device>::IntStorage,
696 true_l: &Layout,
697 value: i64,
698 ) -> Result<<Cpu as Device>::IntStorage> {
699 let m: Vec<bool> = mask_l.storage_indices().map(|i| mask.0[i]).collect();
700 match on_true {
701 CpuIntStorage::I32(t) => {
702 let tv: Vec<i32> = true_l.storage_indices().map(|i| t[i]).collect();
703 let val = value as i32;
704 Ok(CpuIntStorage::I32(m.iter().enumerate().map(|(i, &c)| if c { tv[i] } else { val }).collect()))
705 }
706 CpuIntStorage::U32(t) => {
707 let tv: Vec<u32> = true_l.storage_indices().map(|i| t[i]).collect();
708 let val = value as u32;
709 Ok(CpuIntStorage::U32(m.iter().enumerate().map(|(i, &c)| if c { tv[i] } else { val }).collect()))
710 }
711 CpuIntStorage::U8(t) => {
712 let tv: Vec<u8> = true_l.storage_indices().map(|i| t[i]).collect();
713 let val = value as u8;
714 Ok(CpuIntStorage::U8(m.iter().enumerate().map(|(i, &c)| if c { tv[i] } else { val }).collect()))
715 }
716 }
717 }
718
719 fn i_allclose(a: &CpuIntStorage, a_l: &Layout, b: &CpuIntStorage, b_l: &Layout) -> Result<bool> {
720 match (a, b) {
721 (CpuIntStorage::I32(av), CpuIntStorage::I32(bv)) => {
722 Ok(a_l.storage_indices().zip(b_l.storage_indices()).all(|(ai, bi)| av[ai] == bv[bi]))
723 }
724 (CpuIntStorage::U32(av), CpuIntStorage::U32(bv)) => {
725 Ok(a_l.storage_indices().zip(b_l.storage_indices()).all(|(ai, bi)| av[ai] == bv[bi]))
726 }
727 (CpuIntStorage::U8(av), CpuIntStorage::U8(bv)) => {
728 Ok(a_l.storage_indices().zip(b_l.storage_indices()).all(|(ai, bi)| av[ai] == bv[bi]))
729 }
730 _ => Err(crate::Error::DTypeMismatch { lhs: a.dtype(), rhs: b.dtype(), op: "allclose" }),
731 }
732 }
733}
734
735fn as_i32(s: &CpuIntStorage) -> &[i32] {
736 match s {
737 CpuIntStorage::I32(d) => d,
738 _ => unreachable!(),
739 }
740}
741fn as_u32(s: &CpuIntStorage) -> &[u32] {
742 match s {
743 CpuIntStorage::U32(d) => d,
744 _ => unreachable!(),
745 }
746}
747fn as_u8(s: &CpuIntStorage) -> &[u8] {
748 match s {
749 CpuIntStorage::U8(d) => d,
750 _ => unreachable!(),
751 }
752}