1use std::{mem::ManuallyDrop, ptr};
2
3use singe_cuda::data_type::{DataType, DataTypeLike};
4
5use crate::{
6 context::{Context, ContextRef},
7 error::{Error, Result},
8 sys, try_ffi,
9 utility::to_i64_vec,
10};
11
12#[derive(Debug)]
13pub struct TensorDescriptor {
14 handle: sys::cutensorTensorDescriptor_t,
15 context: ContextRef,
16 shape: Vec<u64>,
17 strides: Option<Vec<u64>>,
18 data_type: DataType,
19 alignment_requirement: u32,
20}
21
22impl TensorDescriptor {
23 pub fn create_for<T: DataTypeLike>(
24 ctx: &Context,
25 shape: &[u64],
26 alignment_requirement: u32,
27 ) -> Result<Self> {
28 Self::create(ctx, shape, T::data_type(), alignment_requirement)
29 }
30
31 pub fn create(
32 ctx: &Context,
33 shape: &[u64],
34 data_type: DataType,
35 alignment_requirement: u32,
36 ) -> Result<Self> {
37 Self::create_with_strides(ctx, shape, None, data_type, alignment_requirement)
38 }
39
40 pub fn create_with_strides(
55 ctx: &Context,
56 shape: &[u64],
57 strides: Option<&[u64]>,
58 data_type: DataType,
59 alignment_requirement: u32,
60 ) -> Result<Self> {
61 ctx.bind()?;
62
63 let rank = shape.len() as u32;
64 if let Some(strides) = strides
65 && shape.len() != strides.len()
66 {
67 return Err(Error::TensorStrideMismatch {
68 rank,
69 stride_length: strides.len(),
70 });
71 }
72
73 let extent = to_i64_vec(shape, "shape")?;
74 let strides_vec = strides.map(<[u64]>::to_vec);
75 let stride_i64 = strides_vec
76 .as_ref()
77 .map(|values| to_i64_vec(values, "strides"))
78 .transpose()?;
79
80 let mut handle = ptr::null_mut();
81 unsafe {
82 try_ffi!(sys::cutensorCreateTensorDescriptor(
83 ctx.as_raw(),
84 &raw mut handle,
85 rank,
86 extent.as_ptr(),
87 stride_i64.as_ref().map_or(ptr::null(), Vec::as_ptr),
88 data_type.into(),
89 alignment_requirement,
90 ))?;
91 }
92
93 if handle.is_null() {
94 return Err(Error::NullHandle);
95 }
96
97 Ok(Self {
98 handle,
99 context: ctx.as_context_ref(),
100 shape: shape.to_vec(),
101 strides: strides_vec,
102 data_type,
103 alignment_requirement,
104 })
105 }
106
107 pub fn create_with_strides_for<T: DataTypeLike>(
108 ctx: &Context,
109 shape: &[u64],
110 strides: Option<&[u64]>,
111 alignment_requirement: u32,
112 ) -> Result<Self> {
113 Self::create_with_strides(ctx, shape, strides, T::data_type(), alignment_requirement)
114 }
115
116 pub fn shape(&self) -> &[u64] {
117 &self.shape
118 }
119
120 pub fn strides(&self) -> Option<&[u64]> {
121 self.strides.as_deref()
122 }
123
124 pub fn rank(&self) -> u32 {
125 self.shape.len() as u32
126 }
127
128 pub fn data_type(&self) -> DataType {
129 self.data_type
130 }
131
132 pub fn alignment_requirement(&self) -> u32 {
133 self.alignment_requirement
134 }
135
136 pub(crate) fn context(&self) -> &ContextRef {
137 &self.context
138 }
139
140 pub const fn as_raw(&self) -> sys::cutensorTensorDescriptor_t {
141 self.handle
142 }
143
144 pub unsafe fn from_raw(
154 handle: sys::cutensorTensorDescriptor_t,
155 ctx: &Context,
156 shape: Vec<u64>,
157 strides: Option<Vec<u64>>,
158 data_type: DataType,
159 alignment_requirement: u32,
160 ) -> Self {
161 Self {
162 handle,
163 context: ctx.as_context_ref(),
164 shape,
165 strides,
166 data_type,
167 alignment_requirement,
168 }
169 }
170
171 pub fn into_raw(self) -> sys::cutensorTensorDescriptor_t {
177 let descriptor = ManuallyDrop::new(self);
178 descriptor.handle
179 }
180}
181
182impl Drop for TensorDescriptor {
183 fn drop(&mut self) {
184 if let Err(err) = self.context.bind() {
185 #[cfg(debug_assertions)]
186 eprintln!("failed to bind cutensor context before destroying tensor descriptor: {err}");
187 }
188
189 unsafe {
190 if let Err(err) = try_ffi!(sys::cutensorDestroyTensorDescriptor(self.handle)) {
191 #[cfg(debug_assertions)]
192 eprintln!("failed to destroy cutensor tensor descriptor: {err}");
193 }
194 }
195 }
196}
197
198#[derive(Debug)]
199pub struct BlockSparseTensorDescriptor {
200 handle: sys::cutensorBlockSparseTensorDescriptor_t,
201 context: ContextRef,
202 mode_count: u32,
203 non_zero_block_count: u64,
204 data_type: DataType,
205}
206
207impl BlockSparseTensorDescriptor {
208 pub fn create(
253 ctx: &Context,
254 section_count_per_mode: &[u32],
255 section_extents: &[u64],
256 non_zero_block_count: u64,
257 non_zero_coordinates: &[i32],
258 block_strides: Option<&[u64]>,
259 data_type: DataType,
260 ) -> Result<Self> {
261 ctx.bind()?;
262
263 let mode_count = section_count_per_mode.len() as u32;
264 let expected_section_extents = section_count_per_mode
265 .iter()
266 .try_fold(0usize, |sum, &value| sum.checked_add(value as usize))
267 .ok_or(Error::OutOfRange {
268 name: "section_extents".into(),
269 })?;
270 if section_extents.len() != expected_section_extents {
271 return Err(Error::LengthMismatch {
272 name: "section_extents".into(),
273 expected: expected_section_extents,
274 actual: section_extents.len(),
275 });
276 }
277
278 let expected_coordinates =
279 mode_count
280 .checked_mul(non_zero_block_count as u32)
281 .ok_or(Error::OutOfRange {
282 name: "non_zero_coordinates".into(),
283 })?;
284 if non_zero_coordinates.len() != expected_coordinates as usize {
285 return Err(Error::LengthMismatch {
286 name: "non_zero_coordinates".into(),
287 expected: expected_coordinates as usize,
288 actual: non_zero_coordinates.len(),
289 });
290 }
291
292 if let Some(block_strides) = block_strides
293 && block_strides.len() != expected_coordinates as usize
294 {
295 return Err(Error::TensorStrideMismatch {
296 rank: expected_coordinates,
297 stride_length: block_strides.len(),
298 });
299 }
300
301 let num_sections_per_mode = section_count_per_mode.to_vec();
302 let section_extents = to_i64_vec(section_extents, "section_extents")?;
303 let block_strides = block_strides
304 .map(|block_strides| to_i64_vec(block_strides, "block_strides"))
305 .transpose()?;
306
307 let mut handle = ptr::null_mut();
308 unsafe {
309 try_ffi!(sys::cutensorCreateBlockSparseTensorDescriptor(
310 ctx.as_raw(),
311 &raw mut handle,
312 mode_count,
313 non_zero_block_count,
314 num_sections_per_mode.as_ptr(),
315 section_extents.as_ptr(),
316 non_zero_coordinates.as_ptr(),
317 block_strides.as_ref().map_or(ptr::null(), Vec::as_ptr),
318 data_type.into(),
319 ))?;
320 }
321
322 if handle.is_null() {
323 return Err(Error::NullHandle);
324 }
325
326 Ok(Self {
327 handle,
328 context: ctx.as_context_ref(),
329 mode_count,
330 non_zero_block_count,
331 data_type,
332 })
333 }
334
335 pub fn mode_count(&self) -> u32 {
336 self.mode_count
337 }
338
339 pub fn non_zero_block_count(&self) -> u64 {
340 self.non_zero_block_count
341 }
342
343 pub fn data_type(&self) -> DataType {
344 self.data_type
345 }
346
347 pub(crate) fn context(&self) -> &ContextRef {
348 &self.context
349 }
350
351 pub const fn as_raw(&self) -> sys::cutensorBlockSparseTensorDescriptor_t {
352 self.handle
353 }
354
355 pub unsafe fn from_raw(
366 handle: sys::cutensorBlockSparseTensorDescriptor_t,
367 ctx: &Context,
368 mode_count: u32,
369 non_zero_block_count: u64,
370 data_type: DataType,
371 ) -> Self {
372 Self {
373 handle,
374 context: ctx.as_context_ref(),
375 mode_count,
376 non_zero_block_count,
377 data_type,
378 }
379 }
380
381 pub fn into_raw(self) -> sys::cutensorBlockSparseTensorDescriptor_t {
387 let descriptor = ManuallyDrop::new(self);
388 descriptor.handle
389 }
390}
391
392impl Drop for BlockSparseTensorDescriptor {
393 fn drop(&mut self) {
394 if let Err(err) = self.context.bind() {
395 #[cfg(debug_assertions)]
396 eprintln!(
397 "failed to bind cutensor context before destroying block sparse tensor descriptor: {err}"
398 );
399 }
400
401 unsafe {
402 if let Err(err) = try_ffi!(sys::cutensorDestroyBlockSparseTensorDescriptor(self.handle))
403 {
404 #[cfg(debug_assertions)]
405 eprintln!("failed to destroy cutensor block sparse tensor descriptor: {err}");
406 }
407 }
408 }
409}