1use anyhow::anyhow;
2use once_cell::sync::{Lazy, OnceCell};
3use serde::{Deserialize, Serialize};
4#[cfg(not(target_arch = "wasm32"))]
5use std::cell::Cell;
6use std::collections::{HashMap, HashSet};
7use std::future::Future;
8use std::pin::Pin;
9use std::sync::atomic::{AtomicU32, Ordering};
10#[cfg(feature = "wgpu")]
11use std::sync::Arc;
12#[cfg(target_arch = "wasm32")]
13use std::sync::Mutex;
14use std::sync::RwLock;
15
16mod placement;
17
18pub use placement::*;
19
20type ResidencyMarkFn = fn(&GpuTensorHandle);
21type ResidencyClearFn = fn(&GpuTensorHandle);
22type SequenceThresholdFn = fn() -> Option<usize>;
23type WorkgroupSizeHintFn = fn() -> Option<u32>;
24
25static RESIDENCY_MARK: OnceCell<ResidencyMarkFn> = OnceCell::new();
26static RESIDENCY_CLEAR: OnceCell<ResidencyClearFn> = OnceCell::new();
27static SEQUENCE_THRESHOLD_PROVIDER: OnceCell<SequenceThresholdFn> = OnceCell::new();
28static WORKGROUP_SIZE_HINT_PROVIDER: OnceCell<WorkgroupSizeHintFn> = OnceCell::new();
29
30pub type GpuHandleIdentity = (u32, u64);
31
32static LOGICAL_HANDLES: Lazy<RwLock<HashSet<GpuHandleIdentity>>> =
33 Lazy::new(|| RwLock::new(HashSet::new()));
34static LOGICAL_HANDLE_HITS: Lazy<RwLock<HashMap<GpuHandleIdentity, u64>>> =
35 Lazy::new(|| RwLock::new(HashMap::new()));
36static TRANSPOSED_HANDLES: Lazy<RwLock<HashMap<GpuHandleIdentity, TransposeInfo>>> =
37 Lazy::new(|| RwLock::new(HashMap::new()));
38
39static HANDLE_CLASS_NAMES: Lazy<RwLock<HashMap<GpuHandleIdentity, String>>> =
40 Lazy::new(|| RwLock::new(HashMap::new()));
41
42pub const fn handle_identity(handle: &GpuTensorHandle) -> GpuHandleIdentity {
43 (handle.device_id, handle.buffer_id)
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub struct TransposeInfo {
48 pub base_rows: usize,
49 pub base_cols: usize,
50}
51
52pub fn register_residency_mark(handler: ResidencyMarkFn) {
55 let _ = RESIDENCY_MARK.set(handler);
56}
57
58pub fn mark_residency(handle: &GpuTensorHandle) {
61 if let Some(handler) = RESIDENCY_MARK.get() {
62 handler(handle);
63 }
64}
65
66pub fn register_residency_clear(handler: ResidencyClearFn) {
70 let _ = RESIDENCY_CLEAR.set(handler);
71}
72
73pub fn clear_residency(handle: &GpuTensorHandle) {
76 if let Some(handler) = RESIDENCY_CLEAR.get() {
77 handler(handle);
78 }
79}
80
81pub fn register_sequence_threshold_provider(provider: SequenceThresholdFn) {
85 let _ = SEQUENCE_THRESHOLD_PROVIDER.set(provider);
86}
87
88pub fn sequence_threshold_hint() -> Option<usize> {
90 SEQUENCE_THRESHOLD_PROVIDER
91 .get()
92 .and_then(|provider| provider())
93}
94
95pub fn register_workgroup_size_hint_provider(provider: WorkgroupSizeHintFn) {
99 let _ = WORKGROUP_SIZE_HINT_PROVIDER.set(provider);
100}
101
102pub fn workgroup_size_hint() -> Option<u32> {
104 WORKGROUP_SIZE_HINT_PROVIDER
105 .get()
106 .and_then(|provider| provider())
107}
108
109pub fn export_context(kind: AccelContextKind) -> Option<AccelContextHandle> {
112 provider().and_then(|p| p.export_context(kind))
113}
114
115#[cfg(feature = "wgpu")]
119pub fn export_wgpu_buffer(handle: &GpuTensorHandle) -> Option<WgpuBufferRef> {
120 provider_for_handle(handle).and_then(|p| p.export_wgpu_buffer(handle))
121}
122
123pub fn handle_precision(handle: &GpuTensorHandle) -> Option<ProviderPrecision> {
125 handle
126 .descriptor
127 .element_type
128 .and_then(NumericElementType::precision)
129}
130
131pub fn set_handle_class_name(handle: &GpuTensorHandle, class_name: impl Into<String>) {
137 if let Ok(mut guard) = HANDLE_CLASS_NAMES.write() {
138 guard.insert(handle_identity(handle), class_name.into());
139 }
140}
141
142pub fn handle_class_name(handle: &GpuTensorHandle) -> Option<String> {
144 HANDLE_CLASS_NAMES
145 .read()
146 .ok()
147 .and_then(|guard| guard.get(&handle_identity(handle)).cloned())
148 .or_else(|| {
149 handle
150 .descriptor
151 .element_type
152 .map(|element_type| element_type.class_name().to_string())
153 })
154}
155
156pub fn clear_handle_class_name(handle: &GpuTensorHandle) {
158 if let Ok(mut guard) = HANDLE_CLASS_NAMES.write() {
159 guard.remove(&handle_identity(handle));
160 }
161}
162
163pub fn set_handle_logical(handle: &GpuTensorHandle, logical: bool) {
166 let identity = handle_identity(handle);
167 if let Ok(mut guard) = LOGICAL_HANDLES.write() {
168 if logical {
169 guard.insert(identity);
170 if let Ok(mut hits) = LOGICAL_HANDLE_HITS.write() {
171 *hits.entry(identity).or_insert(0) += 1;
172 }
173 } else {
174 guard.remove(&identity);
175 if let Ok(mut hits) = LOGICAL_HANDLE_HITS.write() {
176 hits.remove(&identity);
177 }
178 }
179 }
180 if logical {
181 if let Ok(mut classes) = HANDLE_CLASS_NAMES.write() {
182 classes.insert(identity, "logical".into());
183 }
184 } else if let Ok(mut classes) = HANDLE_CLASS_NAMES.write() {
185 if classes
186 .get(&identity)
187 .is_some_and(|class| class == "logical")
188 {
189 classes.remove(&identity);
190 }
191 }
192}
193
194pub fn clear_handle_logical(handle: &GpuTensorHandle) {
196 set_handle_logical(handle, false);
197}
198
199pub fn handle_is_logical(handle: &GpuTensorHandle) -> bool {
201 LOGICAL_HANDLES
202 .read()
203 .map(|guard| guard.contains(&handle_identity(handle)))
204 .unwrap_or(false)
205}
206
207pub fn handle_logical_hits(buffer_id: u64) -> Option<u64> {
208 LOGICAL_HANDLE_HITS.read().ok().and_then(|guard| {
209 let mut matches = guard
210 .iter()
211 .filter_map(|((_, candidate), hits)| (*candidate == buffer_id).then_some(*hits));
212 let first = matches.next()?;
213 Some(matches.fold(first, u64::saturating_add))
214 })
215}
216
217pub fn handle_logical_hits_for_handle(handle: &GpuTensorHandle) -> Option<u64> {
218 LOGICAL_HANDLE_HITS
219 .read()
220 .ok()
221 .and_then(|guard| guard.get(&handle_identity(handle)).copied())
222}
223
224pub fn record_handle_transpose(handle: &GpuTensorHandle, base_rows: usize, base_cols: usize) {
225 if let Ok(mut guard) = TRANSPOSED_HANDLES.write() {
226 guard.insert(
227 handle_identity(handle),
228 TransposeInfo {
229 base_rows,
230 base_cols,
231 },
232 );
233 }
234}
235
236pub fn clear_handle_transpose(handle: &GpuTensorHandle) {
237 if let Ok(mut guard) = TRANSPOSED_HANDLES.write() {
238 guard.remove(&handle_identity(handle));
239 }
240}
241
242pub fn handle_transpose_info(handle: &GpuTensorHandle) -> Option<TransposeInfo> {
243 TRANSPOSED_HANDLES
244 .read()
245 .ok()
246 .and_then(|guard| guard.get(&handle_identity(handle)).copied())
247}
248
249pub fn handle_is_transposed(handle: &GpuTensorHandle) -> bool {
250 handle_transpose_info(handle).is_some()
251}
252
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
254pub enum GpuTensorStorage {
255 Real,
256 ComplexInterleaved,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
265pub enum IntegerElementType {
266 I8,
267 I16,
268 I32,
269 I64,
270 U8,
271 U16,
272 U32,
273 U64,
274}
275
276impl IntegerElementType {
277 pub const fn element_size(self) -> usize {
278 match self {
279 Self::I8 | Self::U8 => 1,
280 Self::I16 | Self::U16 => 2,
281 Self::I32 | Self::U32 => 4,
282 Self::I64 | Self::U64 => 8,
283 }
284 }
285}
286
287pub fn handle_integer_type(handle: &GpuTensorHandle) -> Option<IntegerElementType> {
289 handle
290 .descriptor
291 .element_type
292 .and_then(NumericElementType::integer_type)
293}
294
295impl Default for GpuTensorStorage {
296 fn default() -> Self {
297 Self::Real
298 }
299}
300
301#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
309pub struct GpuTensorDescriptor {
310 pub element_type: Option<NumericElementType>,
311 pub storage: Option<GpuTensorStorage>,
312 pub provenance: Option<GpuHandleProvenance>,
313}
314
315impl GpuTensorDescriptor {
316 pub const fn numeric(element_type: NumericElementType, storage: GpuTensorStorage) -> Self {
317 Self {
318 element_type: Some(element_type),
319 storage: Some(storage),
320 provenance: None,
321 }
322 }
323}
324
325#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
326pub struct GpuTensorHandle {
327 pub shape: Vec<usize>,
328 pub device_id: u32,
332 pub buffer_id: u64,
333 #[serde(default)]
334 pub descriptor: GpuTensorDescriptor,
335}
336
337impl GpuTensorHandle {
338 pub fn new(shape: Vec<usize>, device_id: u32, buffer_id: u64) -> Self {
339 Self {
340 shape,
341 device_id,
342 buffer_id,
343 descriptor: GpuTensorDescriptor::default(),
344 }
345 }
346
347 pub fn with_numeric_descriptor(
348 mut self,
349 element_type: NumericElementType,
350 storage: GpuTensorStorage,
351 ) -> Self {
352 self.descriptor.element_type = Some(element_type);
353 self.descriptor.storage = Some(storage);
354 self
355 }
356
357 pub fn with_provenance(mut self, provenance: GpuHandleProvenance) -> Self {
358 self.descriptor.provenance = Some(provenance);
359 self
360 }
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
369pub enum GpuHandleProvenance {
370 Automatic,
371 Explicit,
372}
373
374pub fn set_handle_provenance(handle: &mut GpuTensorHandle, provenance: GpuHandleProvenance) {
375 handle.descriptor.provenance = Some(provenance);
376}
377
378pub fn handle_provenance(handle: &GpuTensorHandle) -> Option<GpuHandleProvenance> {
379 handle.descriptor.provenance
380}
381
382pub fn mark_handle_explicit(handle: &mut GpuTensorHandle) {
383 set_handle_provenance(handle, GpuHandleProvenance::Explicit);
384}
385
386pub fn mark_handle_automatic(handle: &mut GpuTensorHandle) {
387 set_handle_provenance(handle, GpuHandleProvenance::Automatic);
388}
389
390pub fn handle_is_explicit(handle: &GpuTensorHandle) -> bool {
391 handle_provenance(handle) == Some(GpuHandleProvenance::Explicit)
392}
393
394pub fn clear_handle_metadata(handle: &GpuTensorHandle) {
399 clear_residency(handle);
400 clear_handle_class_name(handle);
401 clear_handle_logical(handle);
402 clear_handle_transpose(handle);
403}
404
405#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
406pub enum ProviderSpectralRange {
407 Onesided,
408 Twosided,
409 Centered,
410}
411
412#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
413pub enum ProviderSpectralFrameMode {
414 Sliding {
415 hop: usize,
416 },
417 ColumnSliding {
418 hop: usize,
419 input_rows: usize,
420 frames_per_column: usize,
421 },
422 FoldedColumns {
423 input_rows: usize,
424 },
425}
426
427#[derive(Clone, Debug)]
428pub struct ProviderSpectralRequest<'a> {
429 pub input: &'a GpuTensorHandle,
430 pub input_len: usize,
431 pub input_complex: bool,
432 pub window: &'a [f64],
433 pub nfft: usize,
434 pub frame_count: usize,
435 pub frame_mode: ProviderSpectralFrameMode,
436 pub range: ProviderSpectralRange,
437 pub denominator: f64,
438}
439
440#[derive(Clone, Debug)]
441pub struct ProviderSpectralResult {
442 pub s: GpuTensorHandle,
443 pub ps: GpuTensorHandle,
444 pub rows: usize,
445 pub cols: usize,
446}
447
448#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
449pub enum ProviderEnvelopeMethod {
450 Analytic,
451 AnalyticFir { filter_len: usize },
452 Rms { window_len: usize },
453}
454
455#[derive(Clone, Debug)]
456pub struct ProviderEnvelopeRequest<'a> {
457 pub input: &'a GpuTensorHandle,
458 pub channel_len: usize,
459 pub channel_count: usize,
460 pub output_shape: &'a [usize],
461 pub method: ProviderEnvelopeMethod,
462}
463
464#[derive(Clone, Debug)]
465pub struct ProviderEnvelopeResult {
466 pub upper: GpuTensorHandle,
467 pub lower: GpuTensorHandle,
468}
469
470#[derive(Clone, Debug)]
471pub struct ProviderHilbertRequest<'a> {
472 pub input: &'a GpuTensorHandle,
473 pub length: Option<usize>,
475 pub dim: usize,
477}
478
479#[derive(Clone, Debug)]
480pub struct ProviderModulationRequest<'a> {
481 pub input: &'a GpuTensorHandle,
482 pub constellation: &'a [f64],
484}
485
486#[derive(Clone, Debug)]
487pub struct ProviderBitModulationRequest<'a> {
488 pub input: &'a GpuTensorHandle,
489 pub input_rows: usize,
491 pub bits_per_symbol: usize,
493 pub constellation: &'a [f64],
495}
496
497pub async fn uniform_spectral_estimate(
498 request: ProviderSpectralRequest<'_>,
499) -> anyhow::Result<ProviderSpectralResult> {
500 validate_uniform_spectral_request(&request)?;
501
502 let provider = provider_for_handle(request.input)
503 .ok_or_else(|| anyhow!("uniform_spectral_estimate: GPU provider unavailable"))?;
504 provider.uniform_spectral_estimate(&request).await
505}
506
507fn validate_uniform_spectral_request(request: &ProviderSpectralRequest<'_>) -> anyhow::Result<()> {
508 let invalid_frame_mode = matches!(
509 request.frame_mode,
510 ProviderSpectralFrameMode::Sliding { hop: 0 }
511 | ProviderSpectralFrameMode::ColumnSliding { hop: 0, .. }
512 );
513 let invalid_input_coverage = match request.frame_mode {
514 ProviderSpectralFrameMode::Sliding { hop } => {
515 let required = request
516 .frame_count
517 .checked_sub(1)
518 .and_then(|frames| frames.checked_mul(hop))
519 .and_then(|offset| offset.checked_add(request.window.len()));
520 required.is_none_or(|required| required > request.input_len)
521 }
522 ProviderSpectralFrameMode::ColumnSliding {
523 hop,
524 input_rows,
525 frames_per_column,
526 } => {
527 if input_rows == 0 || frames_per_column == 0 {
528 true
529 } else {
530 let last_frame = request.frame_count - 1;
531 let source_col = last_frame / frames_per_column;
532 let segment = last_frame % frames_per_column;
533 source_col
534 .checked_mul(input_rows)
535 .and_then(|base| {
536 segment
537 .checked_mul(hop)
538 .and_then(|step| base.checked_add(step))
539 })
540 .and_then(|offset| offset.checked_add(request.window.len()))
541 .is_none_or(|required| required > request.input_len)
542 }
543 }
544 ProviderSpectralFrameMode::FoldedColumns { input_rows } => {
545 input_rows == 0
546 || input_rows
547 .checked_mul(request.frame_count)
548 .is_none_or(|required| required > request.input_len)
549 }
550 };
551 if request.window.is_empty()
552 || request.nfft == 0
553 || request.frame_count == 0
554 || invalid_frame_mode
555 || invalid_input_coverage
556 || !request.denominator.is_finite()
557 || request.denominator <= 0.0
558 {
559 return Err(anyhow!("uniform_spectral_estimate: invalid request"));
560 }
561
562 Ok(())
563}
564
565pub async fn signal_envelope(
566 request: ProviderEnvelopeRequest<'_>,
567) -> anyhow::Result<ProviderEnvelopeResult> {
568 let expected_len = request
569 .channel_len
570 .checked_mul(request.channel_count)
571 .ok_or_else(|| anyhow!("signal_envelope: invalid request"))?;
572 let output_len = request
573 .output_shape
574 .iter()
575 .try_fold(1usize, |acc, &dim| acc.checked_mul(dim))
576 .ok_or_else(|| anyhow!("signal_envelope: invalid request"))?;
577 let input_len = request
578 .input
579 .shape
580 .iter()
581 .try_fold(1usize, |acc, &dim| acc.checked_mul(dim))
582 .ok_or_else(|| anyhow!("signal_envelope: invalid request"))?;
583 if request.channel_len == 0
584 || request.channel_count == 0
585 || request.output_shape.is_empty()
586 || output_len != expected_len
587 || input_len != expected_len
588 || !provider_envelope_input_shape_matches(
589 &request.input.shape,
590 request.channel_len,
591 request.channel_count,
592 )
593 {
594 return Err(anyhow!("signal_envelope: invalid request"));
595 }
596
597 match request.method {
598 ProviderEnvelopeMethod::AnalyticFir { filter_len }
599 | ProviderEnvelopeMethod::Rms {
600 window_len: filter_len,
601 } if filter_len == 0 => return Err(anyhow!("signal_envelope: invalid request")),
602 _ => {}
603 }
604
605 let provider = provider_for_handle(request.input)
606 .ok_or_else(|| anyhow!("signal_envelope: GPU provider unavailable"))?;
607 provider.signal_envelope(&request).await
608}
609
610fn provider_envelope_input_shape_matches(
611 shape: &[usize],
612 channel_len: usize,
613 channel_count: usize,
614) -> bool {
615 if channel_count == 1 {
616 return match shape {
617 [len] => *len == channel_len,
618 [rows, cols] => {
619 (*rows == channel_len && *cols == 1) || (*rows == 1 && *cols == channel_len)
620 }
621 _ => false,
622 };
623 }
624
625 matches!(shape, [rows, cols] if *rows == channel_len && *cols == channel_count)
626}
627
628pub async fn signal_hilbert(
629 request: ProviderHilbertRequest<'_>,
630) -> anyhow::Result<GpuTensorHandle> {
631 if request.length == Some(0) {
632 return Err(anyhow!("signal_hilbert: invalid request"));
633 }
634 if request.dim >= request.input.shape.len() {
635 return Err(anyhow!("signal_hilbert: invalid request"));
636 }
637
638 let provider = provider_for_handle(request.input)
639 .ok_or_else(|| anyhow!("signal_hilbert: GPU provider unavailable"))?;
640 provider.signal_hilbert(&request).await
641}
642
643#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
644pub struct ApiDeviceInfo {
645 pub device_id: u32,
646 pub name: String,
647 pub vendor: String,
648 pub memory_bytes: Option<u64>,
649 pub backend: Option<String>,
650}
651
652#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
653pub struct ReduceDimResult {
654 pub values: GpuTensorHandle,
655 pub indices: GpuTensorHandle,
656}
657
658#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
659pub struct ProviderCumminResult {
660 pub values: GpuTensorHandle,
661 pub indices: GpuTensorHandle,
662}
663
664pub type ProviderCummaxResult = ProviderCumminResult;
669
670#[derive(Debug, Clone, Copy, PartialEq, Eq)]
672pub enum AccelContextKind {
673 Plotting,
674}
675
676#[derive(Clone)]
678pub enum AccelContextHandle {
679 #[cfg(feature = "wgpu")]
680 Wgpu(WgpuContextHandle),
681}
682
683impl AccelContextHandle {
684 #[cfg(feature = "wgpu")]
686 pub fn as_wgpu(&self) -> Option<&WgpuContextHandle> {
687 match self {
688 AccelContextHandle::Wgpu(ctx) => Some(ctx),
689 }
690 }
691}
692
693#[cfg(feature = "wgpu")]
695#[derive(Clone)]
696pub struct WgpuContextHandle {
697 pub instance: Arc<wgpu::Instance>,
698 pub device: Arc<wgpu::Device>,
699 pub queue: Arc<wgpu::Queue>,
700 pub adapter: Arc<wgpu::Adapter>,
701 pub adapter_info: wgpu::AdapterInfo,
702 pub limits: wgpu::Limits,
703 pub features: wgpu::Features,
704}
705
706#[cfg(feature = "wgpu")]
712#[derive(Clone)]
713pub struct WgpuBufferRef {
714 pub buffer: Arc<wgpu::Buffer>,
715 pub len: usize,
716 pub shape: Vec<usize>,
717 pub element_size: usize,
718 pub precision: ProviderPrecision,
719}
720
721pub fn handle_storage(handle: &GpuTensorHandle) -> GpuTensorStorage {
722 handle.descriptor.storage.unwrap_or_default()
723}
724
725#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
726pub enum PagefunOp {
727 Mtimes,
728}
729
730#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
731pub struct PagefunRequest {
732 pub op: PagefunOp,
733 pub inputs: Vec<GpuTensorHandle>,
734 pub output_shape: Vec<usize>,
735 pub page_dims: Vec<usize>,
736 pub input_page_dims: Vec<Vec<usize>>,
737}
738
739#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
740pub enum FindDirection {
741 First,
742 Last,
743}
744
745#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
746pub struct ProviderFindResult {
747 pub linear: GpuTensorHandle,
748 pub rows: GpuTensorHandle,
749 pub cols: GpuTensorHandle,
750 pub values: Option<GpuTensorHandle>,
751}
752
753#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
754pub struct ProviderBandwidth {
755 pub lower: u32,
756 pub upper: u32,
757}
758
759#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
760pub enum ProviderSymmetryKind {
761 Symmetric,
762 Skew,
763}
764
765#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
766pub enum ProviderHermitianKind {
767 Hermitian,
768 Skew,
769}
770
771#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
772pub struct ProviderLuResult {
773 pub combined: GpuTensorHandle,
774 pub lower: GpuTensorHandle,
775 pub upper: GpuTensorHandle,
776 pub perm_matrix: GpuTensorHandle,
777 pub perm_vector: GpuTensorHandle,
778}
779
780#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
781pub struct ProviderCholResult {
782 pub factor: GpuTensorHandle,
783 pub info: u32,
785}
786
787#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
788pub struct ProviderQrResult {
789 pub q: GpuTensorHandle,
790 pub r: GpuTensorHandle,
791 pub perm_matrix: GpuTensorHandle,
792 pub perm_vector: GpuTensorHandle,
793}
794
795#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
796pub struct ProviderQrPowerIterResult {
797 pub q: GpuTensorHandle,
798 pub r: GpuTensorHandle,
799 pub perm_matrix: GpuTensorHandle,
800 pub perm_vector: GpuTensorHandle,
801}
802
803#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
804pub struct ProviderLinsolveOptions {
805 pub lower: bool,
806 pub upper: bool,
807 pub rectangular: bool,
808 pub transposed: bool,
809 pub conjugate: bool,
810 pub symmetric: bool,
811 pub posdef: bool,
812 pub need_rcond: bool,
813 pub rcond: Option<f64>,
814}
815
816#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
817pub struct ProviderLinsolveResult {
818 pub solution: GpuTensorHandle,
819 pub reciprocal_condition: f64,
820}
821
822#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
823pub struct ProviderPinvOptions {
824 pub tolerance: Option<f64>,
825}
826
827#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
828pub struct ProviderPolyvalMu {
829 pub mean: f64,
830 pub scale: f64,
831}
832
833#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
834pub struct ProviderPolyvalOptions {
835 pub mu: Option<ProviderPolyvalMu>,
836}
837
838#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
839pub struct ProviderInvOptions {}
840
841#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
842pub struct ProviderPolyfitResult {
843 pub coefficients: Vec<f64>,
844 pub r_matrix: Vec<f64>,
845 pub normr: f64,
846 pub df: f64,
847 pub mu: [f64; 2],
848}
849
850#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
852pub struct ProviderPolyderQuotient {
853 pub numerator: GpuTensorHandle,
854 pub denominator: GpuTensorHandle,
855}
856
857#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
859pub enum ProviderCondNorm {
860 Two,
861 One,
862 Inf,
863 Fro,
864}
865
866#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
868pub enum ProviderNormOrder {
869 Two,
870 One,
871 Inf,
872 NegInf,
873 Zero,
874 Fro,
875 Nuc,
876 P(f64),
877}
878
879#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
880pub enum ProviderInterp1Method {
881 Linear,
882 Nearest,
883}
884
885#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
886pub enum ProviderInterp1Extrapolation {
887 Nan,
888 Extrapolate,
889 Value,
890}
891
892#[derive(Debug, Clone)]
893pub struct ProviderInterp1Request<'a> {
894 pub x: &'a GpuTensorHandle,
897 pub y: &'a GpuTensorHandle,
898 pub xq: &'a GpuTensorHandle,
899 pub sample_len: usize,
900 pub series_count: usize,
901 pub query_len: usize,
902 pub output_shape: &'a [usize],
903 pub method: ProviderInterp1Method,
904 pub extrapolation: ProviderInterp1Extrapolation,
905 pub extrapolation_value: f64,
906}
907
908#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
909pub struct ProviderEigResult {
910 pub eigenvalues: GpuTensorHandle,
911 pub diagonal: GpuTensorHandle,
912 pub right: GpuTensorHandle,
913 pub left: Option<GpuTensorHandle>,
914}
915
916#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
917pub enum ProviderQrPivot {
918 Matrix,
919 Vector,
920}
921
922#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
923pub struct ProviderQrOptions {
924 pub economy: bool,
925 pub pivot: ProviderQrPivot,
926}
927
928impl Default for ProviderQrOptions {
929 fn default() -> Self {
930 Self {
931 economy: false,
932 pivot: ProviderQrPivot::Matrix,
933 }
934 }
935}
936
937#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
938pub enum ProviderPrecision {
939 F32,
940 F64,
941}
942
943#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
948pub enum SpawnHandleConcurrency {
949 ImmutableShare,
951 CopyOnWrite,
953 SynchronizedMutation,
955 Reject,
957}
958
959impl SpawnHandleConcurrency {
960 pub fn as_str(self) -> &'static str {
961 match self {
962 SpawnHandleConcurrency::ImmutableShare => "immutable_share",
963 SpawnHandleConcurrency::CopyOnWrite => "copy_on_write",
964 SpawnHandleConcurrency::SynchronizedMutation => "synchronized_mutation",
965 SpawnHandleConcurrency::Reject => "reject",
966 }
967 }
968}
969
970#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
971pub enum ReductionTwoPassMode {
972 Auto,
973 ForceOn,
974 ForceOff,
975}
976
977impl ReductionTwoPassMode {
978 pub fn as_str(self) -> &'static str {
979 match self {
980 ReductionTwoPassMode::Auto => "auto",
981 ReductionTwoPassMode::ForceOn => "force_on",
982 ReductionTwoPassMode::ForceOff => "force_off",
983 }
984 }
985}
986
987#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
988pub enum ReductionFlavor {
989 Sum,
990 Mean,
991 CustomScale(f64),
992}
993
994impl ReductionFlavor {
995 pub fn is_mean(self) -> bool {
996 matches!(self, ReductionFlavor::Mean)
997 }
998
999 pub fn scale(self, reduce_len: usize) -> f64 {
1000 match self {
1001 ReductionFlavor::Sum => 1.0,
1002 ReductionFlavor::Mean => {
1003 if reduce_len == 0 {
1004 1.0
1005 } else {
1006 1.0 / reduce_len as f64
1007 }
1008 }
1009 ReductionFlavor::CustomScale(scale) => scale,
1010 }
1011 }
1012}
1013
1014#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1016pub enum CorrcoefNormalization {
1017 Unbiased,
1018 Biased,
1019}
1020
1021#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1023pub enum CorrcoefRows {
1024 All,
1025 Complete,
1026 Pairwise,
1027}
1028
1029#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1031pub struct CorrcoefOptions {
1032 pub normalization: CorrcoefNormalization,
1033 pub rows: CorrcoefRows,
1034}
1035
1036impl Default for CorrcoefOptions {
1037 fn default() -> Self {
1038 Self {
1039 normalization: CorrcoefNormalization::Unbiased,
1040 rows: CorrcoefRows::All,
1041 }
1042 }
1043}
1044
1045#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1047pub enum CovNormalization {
1048 Unbiased,
1049 Biased,
1050}
1051
1052#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1054pub enum CovRows {
1055 All,
1056 OmitRows,
1057 PartialRows,
1058}
1059
1060#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1062pub struct CovarianceOptions {
1063 pub normalization: CovNormalization,
1064 pub rows: CovRows,
1065 pub has_weight_vector: bool,
1066}
1067
1068impl Default for CovarianceOptions {
1069 fn default() -> Self {
1070 Self {
1071 normalization: CovNormalization::Unbiased,
1072 rows: CovRows::All,
1073 has_weight_vector: false,
1074 }
1075 }
1076}
1077
1078#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1080pub enum ProviderStdNormalization {
1081 Sample,
1082 Population,
1083}
1084
1085#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1087pub enum ProviderNanMode {
1088 Include,
1089 Omit,
1090}
1091
1092#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1094pub enum ProviderMovingWindowOp {
1095 Sum,
1096 Mean,
1097 Prod,
1098 Min,
1099 Max,
1100 Median,
1101 Std,
1102 Var,
1103}
1104
1105#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1107pub enum ProviderMovingWindowEndpoints {
1108 Shrink,
1109 Discard,
1110 Fill(f64),
1111}
1112
1113#[derive(Debug, Clone, Copy)]
1115pub struct ProviderMovingWindowRequest<'a> {
1116 pub input: &'a GpuTensorHandle,
1117 pub output_shape: &'a [usize],
1118 pub dim: usize,
1120 pub before: usize,
1121 pub after: usize,
1122 pub op: ProviderMovingWindowOp,
1123 pub endpoints: ProviderMovingWindowEndpoints,
1124 pub nan_mode: ProviderNanMode,
1125 pub normalization: ProviderStdNormalization,
1126}
1127
1128#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1130pub enum ProviderModeAxes {
1131 Default,
1133 Dim(usize),
1135 All,
1137}
1138
1139#[derive(Debug, Clone, Copy)]
1141pub struct ProviderModeRequest<'a> {
1142 pub input: &'a GpuTensorHandle,
1143 pub axes: ProviderModeAxes,
1144 pub want_frequency: bool,
1145 pub want_ties: bool,
1146}
1147
1148#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1155pub struct ProviderModeTiedSets {
1156 pub values: HostTensorOwned,
1157 pub offsets: Vec<usize>,
1158 pub counts: Vec<usize>,
1159}
1160
1161#[derive(Debug, Clone, PartialEq)]
1168pub struct ProviderModeResult {
1169 pub values: GpuTensorHandle,
1170 pub frequencies: Option<GpuTensorHandle>,
1171 pub ties: Option<ProviderModeTiedSets>,
1172}
1173
1174#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1176pub enum ProviderScanDirection {
1177 Forward,
1178 Reverse,
1179}
1180
1181#[derive(Debug, Clone, Copy)]
1183pub enum ProviderTrapezoidSpacing<'a> {
1184 Unit,
1185 Scalar(f64),
1186 ScalarHandle(&'a GpuTensorHandle),
1187 Vector(&'a GpuTensorHandle),
1188 Tensor(&'a GpuTensorHandle),
1189}
1190
1191#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1193pub enum SortOrder {
1194 Ascend,
1195 Descend,
1196}
1197
1198#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1200pub enum SortComparison {
1201 Auto,
1202 Real,
1203 Abs,
1204}
1205
1206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1208pub struct SortResult {
1209 pub values: HostTensorOwned,
1210 pub indices: HostTensorOwned,
1211}
1212
1213#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1214pub struct SortRowsColumnSpec {
1215 pub index: usize,
1216 pub order: SortOrder,
1217}
1218
1219#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1221pub enum UniqueOrder {
1222 Sorted,
1223 Stable,
1224}
1225
1226#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1228pub enum UniqueOccurrence {
1229 First,
1230 Last,
1231}
1232
1233#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1235pub struct UniqueOptions {
1236 pub rows: bool,
1237 pub order: UniqueOrder,
1238 pub occurrence: UniqueOccurrence,
1239 pub treat_missing_as_distinct: bool,
1240 pub explicit_order: bool,
1241 pub explicit_occurrence: bool,
1242}
1243
1244#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1246pub struct UniqueResult {
1247 pub values: HostTensorOwned,
1248 pub ia: HostTensorOwned,
1249 pub ic: HostTensorOwned,
1250}
1251
1252#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1254pub enum UnionOrder {
1255 Sorted,
1256 Stable,
1257}
1258
1259#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1261pub struct UnionOptions {
1262 pub rows: bool,
1263 pub order: UnionOrder,
1264}
1265
1266#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1268pub struct UnionResult {
1269 pub values: HostTensorOwned,
1270 pub ia: HostTensorOwned,
1271 pub ib: HostTensorOwned,
1272}
1273
1274#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1276pub enum FspecialFilter {
1277 Average {
1278 rows: u32,
1279 cols: u32,
1280 },
1281 Disk {
1282 radius: f64,
1283 size: u32,
1284 },
1285 Gaussian {
1286 rows: u32,
1287 cols: u32,
1288 sigma: f64,
1289 },
1290 Laplacian {
1291 alpha: f64,
1292 },
1293 Log {
1294 rows: u32,
1295 cols: u32,
1296 sigma: f64,
1297 },
1298 Motion {
1299 length: u32,
1300 kernel_size: u32,
1301 angle_degrees: f64,
1302 oversample: u32,
1303 },
1304 Prewitt,
1305 Sobel,
1306 Unsharp {
1307 alpha: f64,
1308 },
1309}
1310
1311#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1313pub struct FspecialRequest {
1314 pub filter: FspecialFilter,
1315}
1316
1317#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1319pub enum ImfilterPadding {
1320 Constant,
1321 Replicate,
1322 Symmetric,
1323 Circular,
1324}
1325
1326#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1328pub enum ImfilterShape {
1329 Same,
1330 Full,
1331 Valid,
1332}
1333
1334#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1336pub enum ImfilterMode {
1337 Correlation,
1338 Convolution,
1339}
1340
1341#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1343pub struct ImfilterOptions {
1344 pub padding: ImfilterPadding,
1345 pub constant_value: f64,
1346 pub shape: ImfilterShape,
1347 pub mode: ImfilterMode,
1348}
1349
1350impl Default for ImfilterOptions {
1351 fn default() -> Self {
1352 Self {
1353 padding: ImfilterPadding::Constant,
1354 constant_value: 0.0,
1355 shape: ImfilterShape::Same,
1356 mode: ImfilterMode::Correlation,
1357 }
1358 }
1359}
1360
1361#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1363pub enum SetdiffOrder {
1364 Sorted,
1365 Stable,
1366}
1367
1368#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1370pub struct SetdiffOptions {
1371 pub rows: bool,
1372 pub order: SetdiffOrder,
1373}
1374
1375#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1377pub struct SetdiffResult {
1378 pub values: HostTensorOwned,
1379 pub ia: HostTensorOwned,
1380}
1381
1382#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1384pub struct IsMemberOptions {
1385 pub rows: bool,
1386}
1387
1388#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1390pub struct HostLogicalOwned {
1391 pub data: Vec<u8>,
1392 pub shape: Vec<usize>,
1393}
1394
1395#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1397pub struct IsMemberResult {
1398 pub mask: HostLogicalOwned,
1399 pub loc: HostTensorOwned,
1400}
1401
1402#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1403pub enum ProviderConvMode {
1404 Full,
1405 Same,
1406 Valid,
1407}
1408
1409#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1410pub enum ProviderConvOrientation {
1411 Row,
1412 Column,
1413}
1414
1415#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1416pub struct ProviderConv1dOptions {
1417 pub mode: ProviderConvMode,
1418 pub orientation: ProviderConvOrientation,
1419}
1420
1421#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1422pub struct ProviderIirFilterOptions {
1423 pub dim: usize,
1425 pub zi: Option<GpuTensorHandle>,
1427 pub unit_denominator: bool,
1432}
1433
1434#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1435pub struct ProviderIirFilterResult {
1436 pub output: GpuTensorHandle,
1438 pub final_state: Option<GpuTensorHandle>,
1440}
1441
1442#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1443pub struct ProviderMoments2 {
1444 pub mean: GpuTensorHandle,
1445 pub ex2: GpuTensorHandle,
1446}
1447
1448#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1449pub struct ProviderDispatchStats {
1450 pub count: u64,
1452 pub total_wall_time_ns: u64,
1454}
1455
1456#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
1457pub struct ProviderFallbackStat {
1458 pub reason: String,
1459 pub count: u64,
1460}
1461
1462#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1463pub struct ProviderTelemetry {
1464 pub fused_elementwise: ProviderDispatchStats,
1465 pub fused_reduction: ProviderDispatchStats,
1466 pub matmul: ProviderDispatchStats,
1467 pub linsolve: ProviderDispatchStats,
1468 pub mldivide: ProviderDispatchStats,
1469 pub mrdivide: ProviderDispatchStats,
1470 pub upload_bytes: u64,
1471 pub download_bytes: u64,
1472 pub solve_fallbacks: Vec<ProviderFallbackStat>,
1473 pub fusion_cache_hits: u64,
1474 pub fusion_cache_misses: u64,
1475 pub bind_group_cache_hits: u64,
1476 pub bind_group_cache_misses: u64,
1477 pub bind_group_cache_by_layout: Option<Vec<BindGroupLayoutTelemetry>>,
1479 pub kernel_launches: Vec<KernelLaunchTelemetry>,
1481}
1482
1483#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1484pub struct BindGroupLayoutTelemetry {
1485 pub tag: String,
1486 pub hits: u64,
1487 pub misses: u64,
1488}
1489
1490#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1491pub struct KernelAttrTelemetry {
1492 pub key: String,
1493 pub value: u64,
1494}
1495
1496#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1497pub struct KernelLaunchTelemetry {
1498 pub kernel: String,
1499 pub precision: Option<String>,
1500 pub shape: Vec<KernelAttrTelemetry>,
1501 pub tuning: Vec<KernelAttrTelemetry>,
1502}
1503
1504pub type AccelProviderFuture<'a, T> = Pin<Box<dyn Future<Output = anyhow::Result<T>> + 'a>>;
1505pub type AccelDownloadFuture<'a> = AccelProviderFuture<'a, crate::HostTensorOwned>;
1506pub type AccelIntegerDownloadFuture<'a> = AccelProviderFuture<'a, crate::HostIntegerTensorOwned>;
1507pub type AccelNumericDownloadFuture<'a> = AccelProviderFuture<'a, crate::HostNumericTensorOwned>;
1508
1509fn unsupported_future<T>(message: &'static str) -> AccelProviderFuture<'static, T> {
1510 Box::pin(async move { Err(anyhow::anyhow!(message)) })
1511}
1512
1513pub trait AccelProvider: Send + Sync {
1515 fn upload(&self, host: &crate::HostTensorView) -> anyhow::Result<GpuTensorHandle>;
1516 fn download<'a>(&'a self, h: &'a GpuTensorHandle) -> AccelDownloadFuture<'a>;
1517
1518 fn upload_numeric(
1524 &self,
1525 host: &crate::HostNumericTensorView,
1526 ) -> anyhow::Result<GpuTensorHandle> {
1527 host.validate()?;
1528 let handle = match (host.data, host.storage) {
1529 (crate::HostNumericDataView::F64(data), GpuTensorStorage::Real) => {
1530 self.upload(&crate::HostTensorView {
1531 data,
1532 shape: host.shape,
1533 })
1534 }
1535 (data, GpuTensorStorage::Real) if data.element_type().integer_type().is_some() => {
1536 let data = match data {
1537 crate::HostNumericDataView::I8(data) => crate::HostIntegerDataView::I8(data),
1538 crate::HostNumericDataView::I16(data) => crate::HostIntegerDataView::I16(data),
1539 crate::HostNumericDataView::I32(data) => crate::HostIntegerDataView::I32(data),
1540 crate::HostNumericDataView::I64(data) => crate::HostIntegerDataView::I64(data),
1541 crate::HostNumericDataView::U8(data) => crate::HostIntegerDataView::U8(data),
1542 crate::HostNumericDataView::U16(data) => crate::HostIntegerDataView::U16(data),
1543 crate::HostNumericDataView::U32(data) => crate::HostIntegerDataView::U32(data),
1544 crate::HostNumericDataView::U64(data) => crate::HostIntegerDataView::U64(data),
1545 crate::HostNumericDataView::F64(_) | crate::HostNumericDataView::F32(_) => {
1546 unreachable!("guarded integer numeric transfer")
1547 }
1548 };
1549 self.upload_integer(&crate::HostIntegerTensorView {
1550 data,
1551 shape: host.shape,
1552 })
1553 }
1554 (data, storage) => Err(anyhow!(
1555 "provider must implement native {:?} {:?} uploads through upload_numeric",
1556 data.element_type(),
1557 storage
1558 )),
1559 }?;
1560 let expected_element = host.data.element_type();
1561 if handle.shape != host.shape
1562 || handle.descriptor.element_type != Some(expected_element)
1563 || handle.descriptor.storage != Some(host.storage)
1564 {
1565 let _ = self.free(&handle);
1566 return Err(anyhow!(
1567 "provider returned an invalid numeric upload descriptor: expected shape {:?}, element {:?}, storage {:?}; got shape {:?}, element {:?}, storage {:?}",
1568 host.shape,
1569 expected_element,
1570 host.storage,
1571 handle.shape,
1572 handle.descriptor.element_type,
1573 handle.descriptor.storage
1574 ));
1575 }
1576 Ok(handle)
1577 }
1578
1579 fn download_numeric<'a>(&'a self, h: &'a GpuTensorHandle) -> AccelNumericDownloadFuture<'a> {
1584 Box::pin(async move {
1585 let element_type = h.descriptor.element_type.ok_or_else(|| {
1586 anyhow!("numeric handle is missing its durable element descriptor")
1587 })?;
1588 let storage = h.descriptor.storage.ok_or_else(|| {
1589 anyhow!("numeric handle is missing its durable storage descriptor")
1590 })?;
1591 if element_type.integer_type().is_some() {
1592 if storage != GpuTensorStorage::Real {
1593 return Err(anyhow!(
1594 "provider must implement native complex integer downloads through download_numeric"
1595 ));
1596 }
1597 let downloaded = self.download_integer(h).await?;
1598 let numeric: crate::HostNumericTensorOwned = downloaded.into();
1599 numeric.validate()?;
1600 return Ok(numeric);
1601 }
1602 if element_type == NumericElementType::F32 {
1603 return Err(anyhow!(
1604 "provider must implement native single downloads through download_numeric"
1605 ));
1606 }
1607 let downloaded = self.download(h).await?;
1608 let numeric = crate::HostNumericTensorOwned {
1609 data: crate::HostNumericDataOwned::F64(downloaded.data),
1610 shape: downloaded.shape,
1611 storage: downloaded.storage,
1612 };
1613 numeric.validate()?;
1614 Ok(numeric)
1615 })
1616 }
1617
1618 fn upload_integer(
1622 &self,
1623 _host: &crate::HostIntegerTensorView,
1624 ) -> anyhow::Result<GpuTensorHandle> {
1625 Err(anyhow::anyhow!(
1626 "provider does not support exact native integer buffers"
1627 ))
1628 }
1629
1630 fn download_integer<'a>(&'a self, _h: &'a GpuTensorHandle) -> AccelIntegerDownloadFuture<'a> {
1632 Box::pin(async {
1633 Err(anyhow::anyhow!(
1634 "provider does not support exact native integer buffers"
1635 ))
1636 })
1637 }
1638
1639 fn free(&self, h: &GpuTensorHandle) -> anyhow::Result<()>;
1640 fn device_info(&self) -> String;
1641 fn device_id(&self) -> u32 {
1645 0
1646 }
1647
1648 fn spawn_handle_concurrency(&self) -> SpawnHandleConcurrency {
1654 SpawnHandleConcurrency::Reject
1655 }
1656
1657 fn capability_snapshot(&self) -> ProviderCapabilitySnapshot {
1661 ProviderCapabilitySnapshot::conservative(self)
1662 }
1663
1664 fn query_feasibility(&self, query: &ProviderFeasibilityQuery) -> ProviderFeasibility {
1667 query.conservative_transfer_feasibility(self.precision())
1668 }
1669
1670 fn estimate_cost(&self, _query: &ProviderCostQuery) -> Option<ProviderCostEstimate> {
1675 None
1676 }
1677
1678 fn placement_resources(&self) -> runmat_execution::ProviderResourceSnapshot {
1683 let info = self.device_info_struct();
1684 let capacity = info.memory_bytes;
1685 runmat_execution::ProviderResourceSnapshot {
1686 device_id: self.device_id(),
1687 capacity_bytes: capacity,
1688 live_bytes: 0,
1689 reclaimable_bytes: 0,
1690 scratch_available_bytes: capacity,
1691 queue_depth: None,
1692 queue_limit: None,
1693 lost: false,
1694 epoch: 0,
1695 }
1696 }
1697
1698 fn export_context(&self, _kind: AccelContextKind) -> Option<AccelContextHandle> {
1701 None
1702 }
1703
1704 #[cfg(feature = "wgpu")]
1706 fn export_wgpu_buffer(&self, _handle: &GpuTensorHandle) -> Option<WgpuBufferRef> {
1707 let _ = _handle;
1708 None
1709 }
1710
1711 fn gather_linear(
1718 &self,
1719 _source: &GpuTensorHandle,
1720 _indices: &[u32],
1721 _output_shape: &[usize],
1722 ) -> anyhow::Result<GpuTensorHandle> {
1723 Err(anyhow::anyhow!("gather_linear not supported by provider"))
1724 }
1725
1726 fn scatter_linear(
1733 &self,
1734 _target: &GpuTensorHandle,
1735 _indices: &[u32],
1736 _values: &GpuTensorHandle,
1737 ) -> anyhow::Result<()> {
1738 Err(anyhow::anyhow!("scatter_linear not supported by provider"))
1739 }
1740
1741 fn device_info_struct(&self) -> ApiDeviceInfo {
1743 ApiDeviceInfo {
1744 device_id: 0,
1745 name: self.device_info(),
1746 vendor: String::new(),
1747 memory_bytes: None,
1748 backend: None,
1749 }
1750 }
1751
1752 fn precision(&self) -> ProviderPrecision {
1753 ProviderPrecision::F64
1754 }
1755
1756 fn read_scalar(&self, _h: &GpuTensorHandle, _linear_index: usize) -> anyhow::Result<f64> {
1758 Err(anyhow::anyhow!("read_scalar not supported by provider"))
1759 }
1760
1761 fn zeros(&self, _shape: &[usize]) -> anyhow::Result<GpuTensorHandle> {
1763 Err(anyhow::anyhow!("zeros not supported by provider"))
1764 }
1765
1766 fn zeros_with_storage(
1772 &self,
1773 shape: &[usize],
1774 storage: GpuTensorStorage,
1775 ) -> anyhow::Result<GpuTensorHandle> {
1776 if storage == GpuTensorStorage::Real {
1777 self.zeros(shape)
1778 } else {
1779 Err(anyhow::anyhow!(
1780 "zeros_with_storage not supported by provider for {storage:?}"
1781 ))
1782 }
1783 }
1784
1785 fn ones(&self, _shape: &[usize]) -> anyhow::Result<GpuTensorHandle> {
1787 Err(anyhow::anyhow!("ones not supported by provider"))
1788 }
1789
1790 fn zeros_like(&self, prototype: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
1792 self.zeros(&prototype.shape)
1793 }
1794
1795 fn zeros_integer_like(
1799 &self,
1800 _prototype: &GpuTensorHandle,
1801 _shape: &[usize],
1802 ) -> anyhow::Result<GpuTensorHandle> {
1803 Err(anyhow::anyhow!(
1804 "zeros_integer_like not supported by provider"
1805 ))
1806 }
1807
1808 fn fill(&self, shape: &[usize], value: f64) -> anyhow::Result<GpuTensorHandle> {
1810 if value == 0.0 {
1811 return self.zeros(shape);
1812 }
1813 if let Ok(base) = self.zeros(shape) {
1814 match self.scalar_add(&base, value) {
1815 Ok(out) => {
1816 let _ = self.free(&base);
1817 return Ok(out);
1818 }
1819 Err(_) => {
1820 let _ = self.free(&base);
1821 }
1822 }
1823 }
1824 let len: usize = shape.iter().copied().product();
1825 let data = vec![value; len];
1826 let view = HostTensorView { data: &data, shape };
1827 self.upload(&view)
1828 }
1829
1830 fn fill_like(
1832 &self,
1833 prototype: &GpuTensorHandle,
1834 value: f64,
1835 ) -> anyhow::Result<GpuTensorHandle> {
1836 if value == 0.0 {
1837 return self.zeros_like(prototype);
1838 }
1839 if let Ok(base) = self.zeros_like(prototype) {
1840 match self.scalar_add(&base, value) {
1841 Ok(out) => {
1842 let _ = self.free(&base);
1843 return Ok(out);
1844 }
1845 Err(_) => {
1846 let _ = self.free(&base);
1847 }
1848 }
1849 }
1850 self.fill(&prototype.shape, value)
1851 }
1852
1853 fn ones_like(&self, prototype: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
1855 self.ones(&prototype.shape)
1856 }
1857
1858 fn eye(&self, _shape: &[usize]) -> anyhow::Result<GpuTensorHandle> {
1860 Err(anyhow::anyhow!("eye not supported by provider"))
1861 }
1862
1863 fn eye_like(&self, prototype: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
1865 self.eye(&prototype.shape)
1866 }
1867
1868 fn meshgrid(&self, _axes: &[MeshgridAxisView<'_>]) -> anyhow::Result<ProviderMeshgridResult> {
1870 Err(anyhow::anyhow!("meshgrid not supported by provider"))
1871 }
1872
1873 fn ndgrid(&self, _request: &ProviderNdgridRequest<'_>) -> anyhow::Result<ProviderNdgridResult> {
1875 Err(anyhow::anyhow!("ndgrid not supported by provider"))
1876 }
1877
1878 fn black_scholes_price(
1880 &self,
1881 _request: &ProviderBlackScholesPriceRequest<'_>,
1882 ) -> anyhow::Result<ProviderBlackScholesPriceResult> {
1883 Err(anyhow::anyhow!(
1884 "black_scholes_price not supported by provider"
1885 ))
1886 }
1887
1888 fn adam_update(
1890 &self,
1891 _request: &ProviderAdamUpdateRequest<'_>,
1892 ) -> anyhow::Result<ProviderAdamUpdateResult> {
1893 Err(anyhow::anyhow!("adam_update not supported by provider"))
1894 }
1895
1896 fn crossentropy_terms(
1898 &self,
1899 _request: &ProviderCrossentropyRequest<'_>,
1900 ) -> anyhow::Result<ProviderCrossentropyResult> {
1901 Err(anyhow::anyhow!(
1902 "crossentropy_terms not supported by provider"
1903 ))
1904 }
1905
1906 fn diag_from_vector(
1908 &self,
1909 _vector: &GpuTensorHandle,
1910 _offset: isize,
1911 ) -> anyhow::Result<GpuTensorHandle> {
1912 Err(anyhow::anyhow!(
1913 "diag_from_vector not supported by provider"
1914 ))
1915 }
1916
1917 fn diag_from_vector_sized(
1921 &self,
1922 _vector: &GpuTensorHandle,
1923 _offset: isize,
1924 _rows: usize,
1925 _cols: usize,
1926 ) -> anyhow::Result<GpuTensorHandle> {
1927 Err(anyhow::anyhow!(
1928 "diag_from_vector_sized not supported by provider"
1929 ))
1930 }
1931
1932 fn diag_extract(
1934 &self,
1935 _matrix: &GpuTensorHandle,
1936 _offset: isize,
1937 ) -> anyhow::Result<GpuTensorHandle> {
1938 Err(anyhow::anyhow!("diag_extract not supported by provider"))
1939 }
1940
1941 fn tril<'a>(
1943 &'a self,
1944 _matrix: &'a GpuTensorHandle,
1945 _offset: isize,
1946 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
1947 Box::pin(async move { Err(anyhow!("tril not supported by provider")) })
1948 }
1949
1950 fn triu<'a>(
1952 &'a self,
1953 _matrix: &'a GpuTensorHandle,
1954 _offset: isize,
1955 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
1956 Box::pin(async move { Err(anyhow!("triu not supported by provider")) })
1957 }
1958
1959 fn polyval(
1961 &self,
1962 _coefficients: &GpuTensorHandle,
1963 _points: &GpuTensorHandle,
1964 _options: &ProviderPolyvalOptions,
1965 ) -> anyhow::Result<GpuTensorHandle> {
1966 Err(anyhow::anyhow!("polyval not supported by provider"))
1967 }
1968
1969 fn polyfit<'a>(
1971 &'a self,
1972 _x: &'a GpuTensorHandle,
1973 _y: &'a GpuTensorHandle,
1974 _degree: usize,
1975 _weights: Option<&'a GpuTensorHandle>,
1976 ) -> AccelProviderFuture<'a, ProviderPolyfitResult> {
1977 Box::pin(async move { Err(anyhow::anyhow!("polyfit not supported by provider")) })
1978 }
1979
1980 fn polyder_single<'a>(
1982 &'a self,
1983 _polynomial: &'a GpuTensorHandle,
1984 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
1985 Box::pin(async move { Err(anyhow::anyhow!("polyder_single not supported by provider")) })
1986 }
1987
1988 fn polyder_product<'a>(
1990 &'a self,
1991 _p: &'a GpuTensorHandle,
1992 _q: &'a GpuTensorHandle,
1993 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
1994 Box::pin(async move { Err(anyhow::anyhow!("polyder_product not supported by provider")) })
1995 }
1996
1997 fn polyder_quotient<'a>(
1999 &'a self,
2000 _u: &'a GpuTensorHandle,
2001 _v: &'a GpuTensorHandle,
2002 ) -> AccelProviderFuture<'a, ProviderPolyderQuotient> {
2003 Box::pin(async move {
2004 Err(anyhow::anyhow!(
2005 "polyder_quotient not supported by provider"
2006 ))
2007 })
2008 }
2009
2010 fn polyint(
2012 &self,
2013 _polynomial: &GpuTensorHandle,
2014 _constant: f64,
2015 ) -> anyhow::Result<GpuTensorHandle> {
2016 Err(anyhow::anyhow!("polyint not supported by provider"))
2017 }
2018
2019 fn random_uniform(&self, _shape: &[usize]) -> anyhow::Result<GpuTensorHandle> {
2021 Err(anyhow::anyhow!("random_uniform not supported by provider"))
2022 }
2023
2024 fn random_uniform_like(&self, prototype: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
2026 self.random_uniform(&prototype.shape)
2027 }
2028
2029 fn random_normal(&self, _shape: &[usize]) -> anyhow::Result<GpuTensorHandle> {
2031 Err(anyhow::anyhow!("random_normal not supported by provider"))
2032 }
2033
2034 fn random_normal_like(&self, prototype: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
2036 self.random_normal(&prototype.shape)
2037 }
2038
2039 fn random_exponential(&self, _mu: f64, _shape: &[usize]) -> anyhow::Result<GpuTensorHandle> {
2041 Err(anyhow::anyhow!(
2042 "random_exponential not supported by provider"
2043 ))
2044 }
2045
2046 fn random_normrnd(
2048 &self,
2049 _mu: f64,
2050 _sigma: f64,
2051 _shape: &[usize],
2052 ) -> anyhow::Result<GpuTensorHandle> {
2053 Err(anyhow::anyhow!("random_normrnd not supported by provider"))
2054 }
2055
2056 fn random_unifrnd(
2058 &self,
2059 _a: f64,
2060 _b: f64,
2061 _shape: &[usize],
2062 ) -> anyhow::Result<GpuTensorHandle> {
2063 Err(anyhow::anyhow!("random_unifrnd not supported by provider"))
2064 }
2065
2066 fn stochastic_evolution(
2067 &self,
2068 _state: &GpuTensorHandle,
2069 _drift: f64,
2070 _scale: f64,
2071 _steps: u32,
2072 ) -> anyhow::Result<GpuTensorHandle> {
2073 Err(anyhow::anyhow!(
2074 "stochastic_evolution not supported by provider"
2075 ))
2076 }
2077
2078 fn set_rng_state(&self, _state: u64) -> anyhow::Result<()> {
2080 Err(anyhow::anyhow!("set_rng_state not supported by provider"))
2081 }
2082
2083 fn fspecial(&self, _request: &FspecialRequest) -> anyhow::Result<GpuTensorHandle> {
2085 Err(anyhow::anyhow!("fspecial not supported by provider"))
2086 }
2087
2088 fn peaks(&self, _n: usize) -> anyhow::Result<GpuTensorHandle> {
2091 Err(anyhow::anyhow!("peaks not supported by provider"))
2092 }
2093
2094 fn peaks_xy(
2097 &self,
2098 _x: &GpuTensorHandle,
2099 _y: &GpuTensorHandle,
2100 ) -> anyhow::Result<GpuTensorHandle> {
2101 Err(anyhow::anyhow!("peaks_xy not supported by provider"))
2102 }
2103
2104 fn hann_window(&self, _len: usize, _periodic: bool) -> anyhow::Result<GpuTensorHandle> {
2105 Err(anyhow::anyhow!("hann_window not supported by provider"))
2106 }
2107
2108 fn hamming_window(&self, _len: usize, _periodic: bool) -> anyhow::Result<GpuTensorHandle> {
2109 Err(anyhow::anyhow!("hamming_window not supported by provider"))
2110 }
2111
2112 fn blackman_window(&self, _len: usize, _periodic: bool) -> anyhow::Result<GpuTensorHandle> {
2113 Err(anyhow::anyhow!("blackman_window not supported by provider"))
2114 }
2115
2116 fn imfilter<'a>(
2118 &'a self,
2119 _image: &'a GpuTensorHandle,
2120 _kernel: &'a GpuTensorHandle,
2121 _options: &'a ImfilterOptions,
2122 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2123 unsupported_future("imfilter not supported by provider")
2124 }
2125
2126 fn random_permutation(&self, _n: usize, _k: usize) -> anyhow::Result<GpuTensorHandle> {
2128 Err(anyhow!("random_permutation not supported by provider"))
2129 }
2130
2131 fn random_permutation_like(
2133 &self,
2134 _prototype: &GpuTensorHandle,
2135 n: usize,
2136 k: usize,
2137 ) -> anyhow::Result<GpuTensorHandle> {
2138 self.random_permutation(n, k)
2139 }
2140
2141 fn covariance<'a>(
2143 &'a self,
2144 _matrix: &'a GpuTensorHandle,
2145 _second: Option<&'a GpuTensorHandle>,
2146 _weights: Option<&'a GpuTensorHandle>,
2147 _options: &'a CovarianceOptions,
2148 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2149 unsupported_future("covariance not supported by provider")
2150 }
2151
2152 fn corrcoef<'a>(
2154 &'a self,
2155 _matrix: &'a GpuTensorHandle,
2156 _options: &'a CorrcoefOptions,
2157 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2158 unsupported_future("corrcoef not supported by provider")
2159 }
2160
2161 fn covariance_to_correlation(
2163 &self,
2164 _matrix: &GpuTensorHandle,
2165 ) -> anyhow::Result<ProviderCovarianceToCorrelationResult> {
2166 Err(anyhow::anyhow!(
2167 "covariance_to_correlation not supported by provider"
2168 ))
2169 }
2170
2171 fn linspace(&self, _start: f64, _stop: f64, _count: usize) -> anyhow::Result<GpuTensorHandle> {
2173 Err(anyhow::anyhow!("linspace not supported by provider"))
2174 }
2175 fn elem_add<'a>(
2176 &'a self,
2177 _a: &'a GpuTensorHandle,
2178 _b: &'a GpuTensorHandle,
2179 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2180 unsupported_future("elem_add not supported by provider")
2181 }
2182 fn elem_mul<'a>(
2183 &'a self,
2184 _a: &'a GpuTensorHandle,
2185 _b: &'a GpuTensorHandle,
2186 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2187 unsupported_future("elem_mul not supported by provider")
2188 }
2189 fn elem_max<'a>(
2190 &'a self,
2191 _a: &'a GpuTensorHandle,
2192 _b: &'a GpuTensorHandle,
2193 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2194 unsupported_future("elem_max not supported by provider")
2195 }
2196 fn elem_min<'a>(
2197 &'a self,
2198 _a: &'a GpuTensorHandle,
2199 _b: &'a GpuTensorHandle,
2200 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2201 unsupported_future("elem_min not supported by provider")
2202 }
2203 fn elem_sub<'a>(
2204 &'a self,
2205 _a: &'a GpuTensorHandle,
2206 _b: &'a GpuTensorHandle,
2207 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2208 unsupported_future("elem_sub not supported by provider")
2209 }
2210 fn elem_div<'a>(
2211 &'a self,
2212 _a: &'a GpuTensorHandle,
2213 _b: &'a GpuTensorHandle,
2214 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2215 unsupported_future("elem_div not supported by provider")
2216 }
2217 fn elem_rem<'a>(
2219 &'a self,
2220 _a: &'a GpuTensorHandle,
2221 _b: &'a GpuTensorHandle,
2222 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2223 unsupported_future("elem_rem not supported by provider")
2224 }
2225 fn elem_mod<'a>(
2227 &'a self,
2228 _a: &'a GpuTensorHandle,
2229 _b: &'a GpuTensorHandle,
2230 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2231 unsupported_future("elem_mod not supported by provider")
2232 }
2233 fn elem_pow<'a>(
2234 &'a self,
2235 _a: &'a GpuTensorHandle,
2236 _b: &'a GpuTensorHandle,
2237 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2238 unsupported_future("elem_pow not supported by provider")
2239 }
2240
2241 fn complex_from_real<'a>(
2244 &'a self,
2245 _real: &'a GpuTensorHandle,
2246 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2247 unsupported_future("complex_from_real not supported by provider")
2248 }
2249
2250 fn complex_from_real_imag<'a>(
2255 &'a self,
2256 _real: &'a GpuTensorHandle,
2257 _imag: &'a GpuTensorHandle,
2258 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2259 unsupported_future("complex_from_real_imag not supported by provider")
2260 }
2261
2262 fn modulate_constellation<'a>(
2265 &'a self,
2266 _request: ProviderModulationRequest<'a>,
2267 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2268 unsupported_future("modulate_constellation not supported by provider")
2269 }
2270
2271 fn modulate_bits_constellation<'a>(
2274 &'a self,
2275 _request: ProviderBitModulationRequest<'a>,
2276 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2277 unsupported_future("modulate_bits_constellation not supported by provider")
2278 }
2279
2280 fn elem_hypot<'a>(
2281 &'a self,
2282 _a: &'a GpuTensorHandle,
2283 _b: &'a GpuTensorHandle,
2284 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2285 unsupported_future("elem_hypot not supported by provider")
2286 }
2287 fn elem_ge<'a>(
2288 &'a self,
2289 _a: &'a GpuTensorHandle,
2290 _b: &'a GpuTensorHandle,
2291 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2292 unsupported_future("elem_ge not supported by provider")
2293 }
2294 fn elem_le<'a>(
2295 &'a self,
2296 _a: &'a GpuTensorHandle,
2297 _b: &'a GpuTensorHandle,
2298 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2299 unsupported_future("elem_le not supported by provider")
2300 }
2301 fn elem_lt<'a>(
2302 &'a self,
2303 _a: &'a GpuTensorHandle,
2304 _b: &'a GpuTensorHandle,
2305 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2306 unsupported_future("elem_lt not supported by provider")
2307 }
2308 fn elem_gt<'a>(
2309 &'a self,
2310 _a: &'a GpuTensorHandle,
2311 _b: &'a GpuTensorHandle,
2312 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2313 unsupported_future("elem_gt not supported by provider")
2314 }
2315 fn elem_eq<'a>(
2316 &'a self,
2317 _a: &'a GpuTensorHandle,
2318 _b: &'a GpuTensorHandle,
2319 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2320 unsupported_future("elem_eq not supported by provider")
2321 }
2322 fn elem_ne<'a>(
2323 &'a self,
2324 _a: &'a GpuTensorHandle,
2325 _b: &'a GpuTensorHandle,
2326 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2327 unsupported_future("elem_ne not supported by provider")
2328 }
2329 fn logical_and(
2330 &self,
2331 _a: &GpuTensorHandle,
2332 _b: &GpuTensorHandle,
2333 ) -> anyhow::Result<GpuTensorHandle> {
2334 Err(anyhow::anyhow!("logical_and not supported by provider"))
2335 }
2336 fn logical_or(
2337 &self,
2338 _a: &GpuTensorHandle,
2339 _b: &GpuTensorHandle,
2340 ) -> anyhow::Result<GpuTensorHandle> {
2341 Err(anyhow::anyhow!("logical_or not supported by provider"))
2342 }
2343 fn logical_xor(
2344 &self,
2345 _a: &GpuTensorHandle,
2346 _b: &GpuTensorHandle,
2347 ) -> anyhow::Result<GpuTensorHandle> {
2348 Err(anyhow::anyhow!("logical_xor not supported by provider"))
2349 }
2350 fn logical_not(&self, _a: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
2351 Err(anyhow::anyhow!("logical_not not supported by provider"))
2352 }
2353 fn logical_islogical(&self, a: &GpuTensorHandle) -> anyhow::Result<bool> {
2354 Ok(handle_is_logical(a))
2355 }
2356 fn logical_isreal(&self, _a: &GpuTensorHandle) -> anyhow::Result<bool> {
2357 Err(anyhow::anyhow!("logical_isreal not supported by provider"))
2358 }
2359 fn logical_isfinite(&self, _a: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
2360 Err(anyhow::anyhow!(
2361 "logical_isfinite not supported by provider"
2362 ))
2363 }
2364 fn logical_isnan(&self, _a: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
2365 Err(anyhow::anyhow!("logical_isnan not supported by provider"))
2366 }
2367 fn logical_isinf(&self, _a: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
2368 Err(anyhow::anyhow!("logical_isinf not supported by provider"))
2369 }
2370 fn elem_atan2<'a>(
2371 &'a self,
2372 _y: &'a GpuTensorHandle,
2373 _x: &'a GpuTensorHandle,
2374 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2375 unsupported_future("elem_atan2 not supported by provider")
2376 }
2377 fn unary_sin<'a>(
2379 &'a self,
2380 _a: &'a GpuTensorHandle,
2381 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2382 unsupported_future("unary_sin not supported by provider")
2383 }
2384 fn unary_sinc<'a>(
2385 &'a self,
2386 _a: &'a GpuTensorHandle,
2387 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2388 unsupported_future("unary_sinc not supported by provider")
2389 }
2390 fn unary_gamma<'a>(
2391 &'a self,
2392 _a: &'a GpuTensorHandle,
2393 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2394 unsupported_future("unary_gamma not supported by provider")
2395 }
2396 fn unary_gammaln<'a>(
2397 &'a self,
2398 _a: &'a GpuTensorHandle,
2399 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2400 unsupported_future("unary_gammaln not supported by provider")
2401 }
2402 fn unary_erf<'a>(
2403 &'a self,
2404 _a: &'a GpuTensorHandle,
2405 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2406 unsupported_future("unary_erf not supported by provider")
2407 }
2408 fn unary_erfcinv<'a>(
2409 &'a self,
2410 _a: &'a GpuTensorHandle,
2411 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2412 unsupported_future("unary_erfcinv not supported by provider")
2413 }
2414 fn unary_factorial<'a>(
2415 &'a self,
2416 _a: &'a GpuTensorHandle,
2417 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2418 unsupported_future("unary_factorial not supported by provider")
2419 }
2420 fn unary_asinh<'a>(
2421 &'a self,
2422 _a: &'a GpuTensorHandle,
2423 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2424 unsupported_future("unary_asinh not supported by provider")
2425 }
2426 fn unary_sinh<'a>(
2427 &'a self,
2428 _a: &'a GpuTensorHandle,
2429 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2430 unsupported_future("unary_sinh not supported by provider")
2431 }
2432 fn unary_cosh<'a>(
2433 &'a self,
2434 _a: &'a GpuTensorHandle,
2435 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2436 unsupported_future("unary_cosh not supported by provider")
2437 }
2438 fn unary_asin<'a>(
2439 &'a self,
2440 _a: &'a GpuTensorHandle,
2441 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2442 unsupported_future("unary_asin not supported by provider")
2443 }
2444 fn unary_acos<'a>(
2445 &'a self,
2446 _a: &'a GpuTensorHandle,
2447 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2448 unsupported_future("unary_acos not supported by provider")
2449 }
2450 fn unary_acosh<'a>(
2451 &'a self,
2452 _a: &'a GpuTensorHandle,
2453 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2454 unsupported_future("unary_acosh not supported by provider")
2455 }
2456 fn unary_tan<'a>(
2457 &'a self,
2458 _a: &'a GpuTensorHandle,
2459 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2460 unsupported_future("unary_tan not supported by provider")
2461 }
2462 fn unary_tanh<'a>(
2463 &'a self,
2464 _a: &'a GpuTensorHandle,
2465 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2466 unsupported_future("unary_tanh not supported by provider")
2467 }
2468 fn unary_atan<'a>(
2469 &'a self,
2470 _a: &'a GpuTensorHandle,
2471 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2472 unsupported_future("unary_atan not supported by provider")
2473 }
2474 fn unary_atanh<'a>(
2475 &'a self,
2476 _a: &'a GpuTensorHandle,
2477 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2478 unsupported_future("unary_atanh not supported by provider")
2479 }
2480 fn unary_ceil<'a>(
2481 &'a self,
2482 _a: &'a GpuTensorHandle,
2483 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2484 unsupported_future("unary_ceil not supported by provider")
2485 }
2486 fn unary_floor<'a>(
2487 &'a self,
2488 _a: &'a GpuTensorHandle,
2489 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2490 unsupported_future("unary_floor not supported by provider")
2491 }
2492 fn unary_round<'a>(
2493 &'a self,
2494 _a: &'a GpuTensorHandle,
2495 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2496 unsupported_future("unary_round not supported by provider")
2497 }
2498 fn round_digits<'a>(
2499 &'a self,
2500 _a: &'a GpuTensorHandle,
2501 _digits: i32,
2502 _significant: bool,
2503 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2504 unsupported_future("round_digits not supported by provider")
2505 }
2506 fn unary_fix<'a>(
2507 &'a self,
2508 _a: &'a GpuTensorHandle,
2509 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2510 unsupported_future("unary_fix not supported by provider")
2511 }
2512 fn unary_cos<'a>(
2513 &'a self,
2514 _a: &'a GpuTensorHandle,
2515 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2516 unsupported_future("unary_cos not supported by provider")
2517 }
2518 fn unary_angle<'a>(
2519 &'a self,
2520 _a: &'a GpuTensorHandle,
2521 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2522 unsupported_future("unary_angle not supported by provider")
2523 }
2524 fn unary_imag<'a>(
2525 &'a self,
2526 _a: &'a GpuTensorHandle,
2527 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2528 unsupported_future("unary_imag not supported by provider")
2529 }
2530 fn unary_real<'a>(
2531 &'a self,
2532 _a: &'a GpuTensorHandle,
2533 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2534 unsupported_future("unary_real not supported by provider")
2535 }
2536 fn unary_conj<'a>(
2537 &'a self,
2538 _a: &'a GpuTensorHandle,
2539 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2540 unsupported_future("unary_conj not supported by provider")
2541 }
2542 fn unary_abs<'a>(
2543 &'a self,
2544 _a: &'a GpuTensorHandle,
2545 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2546 unsupported_future("unary_abs not supported by provider")
2547 }
2548 fn unary_sign<'a>(
2549 &'a self,
2550 _a: &'a GpuTensorHandle,
2551 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2552 unsupported_future("unary_sign not supported by provider")
2553 }
2554 fn unary_heaviside<'a>(
2555 &'a self,
2556 _a: &'a GpuTensorHandle,
2557 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2558 unsupported_future("unary_heaviside not supported by provider")
2559 }
2560 fn unary_exp<'a>(
2561 &'a self,
2562 _a: &'a GpuTensorHandle,
2563 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2564 unsupported_future("unary_exp not supported by provider")
2565 }
2566 fn activation_elu<'a>(
2568 &'a self,
2569 _a: &'a GpuTensorHandle,
2570 _alpha: f64,
2571 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2572 unsupported_future("activation_elu not supported by provider")
2573 }
2574 fn activation_softmax_rows<'a>(
2576 &'a self,
2577 _a: &'a GpuTensorHandle,
2578 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2579 unsupported_future("activation_softmax_rows not supported by provider")
2580 }
2581 fn unary_expm1<'a>(
2582 &'a self,
2583 _a: &'a GpuTensorHandle,
2584 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2585 unsupported_future("unary_expm1 not supported by provider")
2586 }
2587 fn unary_log<'a>(
2588 &'a self,
2589 _a: &'a GpuTensorHandle,
2590 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2591 unsupported_future("unary_log not supported by provider")
2592 }
2593 fn unary_log2<'a>(
2594 &'a self,
2595 _a: &'a GpuTensorHandle,
2596 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2597 unsupported_future("unary_log2 not supported by provider")
2598 }
2599 fn unary_log10<'a>(
2600 &'a self,
2601 _a: &'a GpuTensorHandle,
2602 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2603 unsupported_future("unary_log10 not supported by provider")
2604 }
2605 fn unary_log1p<'a>(
2606 &'a self,
2607 _a: &'a GpuTensorHandle,
2608 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2609 unsupported_future("unary_log1p not supported by provider")
2610 }
2611 fn unary_sqrt<'a>(
2612 &'a self,
2613 _a: &'a GpuTensorHandle,
2614 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2615 unsupported_future("unary_sqrt not supported by provider")
2616 }
2617 fn unary_double<'a>(
2618 &'a self,
2619 _a: &'a GpuTensorHandle,
2620 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2621 unsupported_future("unary_double not supported by provider")
2622 }
2623 fn unary_single<'a>(
2624 &'a self,
2625 _a: &'a GpuTensorHandle,
2626 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2627 unsupported_future("unary_single not supported by provider")
2628 }
2629 fn unary_pow2<'a>(
2630 &'a self,
2631 _a: &'a GpuTensorHandle,
2632 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2633 unsupported_future("unary_pow2 not supported by provider")
2634 }
2635 fn unary_nextpow2<'a>(
2636 &'a self,
2637 _a: &'a GpuTensorHandle,
2638 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2639 unsupported_future("unary_nextpow2 not supported by provider")
2640 }
2641 fn pow2_scale(
2642 &self,
2643 _mantissa: &GpuTensorHandle,
2644 _exponent: &GpuTensorHandle,
2645 ) -> anyhow::Result<GpuTensorHandle> {
2646 Err(anyhow::anyhow!("pow2_scale not supported by provider"))
2647 }
2648 fn scalar_rsub(&self, _a: &GpuTensorHandle, _scalar: f64) -> anyhow::Result<GpuTensorHandle> {
2650 Err(anyhow::anyhow!("scalar_rsub not supported by provider"))
2651 }
2652 fn scalar_rdiv(&self, _a: &GpuTensorHandle, _scalar: f64) -> anyhow::Result<GpuTensorHandle> {
2653 Err(anyhow::anyhow!("scalar_rdiv not supported by provider"))
2654 }
2655 fn scalar_add(&self, _a: &GpuTensorHandle, _scalar: f64) -> anyhow::Result<GpuTensorHandle> {
2657 Err(anyhow::anyhow!("scalar_add not supported by provider"))
2658 }
2659 fn scalar_sub(&self, _a: &GpuTensorHandle, _scalar: f64) -> anyhow::Result<GpuTensorHandle> {
2660 Err(anyhow::anyhow!("scalar_sub not supported by provider"))
2661 }
2662 fn scalar_mul(&self, _a: &GpuTensorHandle, _scalar: f64) -> anyhow::Result<GpuTensorHandle> {
2663 Err(anyhow::anyhow!("scalar_mul not supported by provider"))
2664 }
2665 fn scalar_max(&self, _a: &GpuTensorHandle, _scalar: f64) -> anyhow::Result<GpuTensorHandle> {
2666 Err(anyhow::anyhow!("scalar_max not supported by provider"))
2667 }
2668 fn scalar_min(&self, _a: &GpuTensorHandle, _scalar: f64) -> anyhow::Result<GpuTensorHandle> {
2669 Err(anyhow::anyhow!("scalar_min not supported by provider"))
2670 }
2671 fn scalar_div(&self, _a: &GpuTensorHandle, _scalar: f64) -> anyhow::Result<GpuTensorHandle> {
2672 Err(anyhow::anyhow!("scalar_div not supported by provider"))
2673 }
2674 fn sort_dim<'a>(
2675 &'a self,
2676 _a: &'a GpuTensorHandle,
2677 _dim: usize,
2678 _order: SortOrder,
2679 _comparison: SortComparison,
2680 ) -> AccelProviderFuture<'a, SortResult> {
2681 unsupported_future("sort_dim not supported by provider")
2682 }
2683 fn sort_rows<'a>(
2684 &'a self,
2685 _a: &'a GpuTensorHandle,
2686 _columns: &'a [SortRowsColumnSpec],
2687 _comparison: SortComparison,
2688 ) -> AccelProviderFuture<'a, SortResult> {
2689 unsupported_future("sort_rows not supported by provider")
2690 }
2691 fn matmul<'a>(
2692 &'a self,
2693 _a: &'a GpuTensorHandle,
2694 _b: &'a GpuTensorHandle,
2695 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2696 unsupported_future("matmul not supported by provider")
2697 }
2698
2699 fn syrk(&self, _a: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
2700 Err(anyhow::anyhow!("syrk not supported by provider"))
2701 }
2702 fn pagefun(&self, _request: &PagefunRequest) -> anyhow::Result<GpuTensorHandle> {
2703 Err(anyhow::anyhow!("pagefun not supported by provider"))
2704 }
2705
2706 fn matmul_epilogue<'a>(
2711 &'a self,
2712 a: &'a GpuTensorHandle,
2713 b: &'a GpuTensorHandle,
2714 epilogue: &'a MatmulEpilogue,
2715 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2716 Box::pin(async move {
2717 if epilogue.is_noop() {
2718 return self.matmul(a, b).await;
2719 }
2720 Err(anyhow::anyhow!("matmul_epilogue not supported by provider"))
2721 })
2722 }
2723 fn image_normalize<'a>(
2724 &'a self,
2725 _input: &'a GpuTensorHandle,
2726 _desc: &'a ImageNormalizeDescriptor,
2727 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2728 unsupported_future("image_normalize fusion not supported by provider")
2729 }
2730 fn matmul_power_step<'a>(
2731 &'a self,
2732 _lhs: &'a GpuTensorHandle,
2733 _rhs: &'a GpuTensorHandle,
2734 _epilogue: &'a PowerStepEpilogue,
2735 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2736 unsupported_future("matmul_power_step normalization not supported by provider")
2737 }
2738 fn linsolve<'a>(
2739 &'a self,
2740 _lhs: &'a GpuTensorHandle,
2741 _rhs: &'a GpuTensorHandle,
2742 _options: &'a ProviderLinsolveOptions,
2743 ) -> AccelProviderFuture<'a, ProviderLinsolveResult> {
2744 unsupported_future("linsolve not supported by provider")
2745 }
2746 fn inv<'a>(
2747 &'a self,
2748 _matrix: &'a GpuTensorHandle,
2749 _options: ProviderInvOptions,
2750 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2751 unsupported_future("inv not supported by provider")
2752 }
2753 fn pinv<'a>(
2754 &'a self,
2755 _matrix: &'a GpuTensorHandle,
2756 _options: ProviderPinvOptions,
2757 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2758 unsupported_future("pinv not supported by provider")
2759 }
2760 fn cond<'a>(
2761 &'a self,
2762 _matrix: &'a GpuTensorHandle,
2763 _norm: ProviderCondNorm,
2764 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2765 Box::pin(async move { Err(anyhow::anyhow!("cond not supported by provider")) })
2766 }
2767 fn norm<'a>(
2768 &'a self,
2769 _tensor: &'a GpuTensorHandle,
2770 _order: ProviderNormOrder,
2771 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2772 Box::pin(async move { Err(anyhow::anyhow!("norm not supported by provider")) })
2773 }
2774 fn interp1<'a>(
2775 &'a self,
2776 _request: &'a ProviderInterp1Request<'a>,
2777 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2778 unsupported_future("interp1 not supported by provider")
2779 }
2780 fn rank<'a>(
2781 &'a self,
2782 _matrix: &'a GpuTensorHandle,
2783 _tolerance: Option<f64>,
2784 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2785 Box::pin(async move { Err(anyhow::anyhow!("rank not supported by provider")) })
2786 }
2787 fn rcond<'a>(
2788 &'a self,
2789 _matrix: &'a GpuTensorHandle,
2790 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2791 Box::pin(async move { Err(anyhow::anyhow!("rcond not supported by provider")) })
2792 }
2793 fn mldivide<'a>(
2794 &'a self,
2795 _lhs: &'a GpuTensorHandle,
2796 _rhs: &'a GpuTensorHandle,
2797 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2798 Box::pin(async move { Err(anyhow::anyhow!("mldivide not supported by provider")) })
2799 }
2800 fn mrdivide<'a>(
2801 &'a self,
2802 _lhs: &'a GpuTensorHandle,
2803 _rhs: &'a GpuTensorHandle,
2804 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2805 Box::pin(async move { Err(anyhow::anyhow!("mrdivide not supported by provider")) })
2806 }
2807 fn eig<'a>(
2808 &'a self,
2809 _a: &'a GpuTensorHandle,
2810 _compute_left: bool,
2811 ) -> AccelProviderFuture<'a, ProviderEigResult> {
2812 Box::pin(async move { Err(anyhow::anyhow!("eig not supported by provider")) })
2813 }
2814 fn lu<'a>(&'a self, _a: &'a GpuTensorHandle) -> AccelProviderFuture<'a, ProviderLuResult> {
2815 Box::pin(async move { Err(anyhow::anyhow!("lu not supported by provider")) })
2816 }
2817
2818 fn chol<'a>(
2819 &'a self,
2820 _a: &'a GpuTensorHandle,
2821 _lower: bool,
2822 ) -> AccelProviderFuture<'a, ProviderCholResult> {
2823 Box::pin(async move { Err(anyhow::anyhow!("chol not supported by provider")) })
2824 }
2825 fn qr<'a>(
2826 &'a self,
2827 _a: &'a GpuTensorHandle,
2828 _options: ProviderQrOptions,
2829 ) -> AccelProviderFuture<'a, ProviderQrResult> {
2830 Box::pin(async move { Err(anyhow::anyhow!("qr not supported by provider")) })
2831 }
2832 fn take_matmul_sources(
2833 &self,
2834 _product: &GpuTensorHandle,
2835 ) -> Option<(GpuTensorHandle, GpuTensorHandle)> {
2836 None
2837 }
2838 fn qr_power_iter<'a>(
2839 &'a self,
2840 product: &'a GpuTensorHandle,
2841 _product_lhs: Option<&'a GpuTensorHandle>,
2842 q_handle: &'a GpuTensorHandle,
2843 options: &'a ProviderQrOptions,
2844 ) -> AccelProviderFuture<'a, Option<ProviderQrPowerIterResult>> {
2845 let _ = (product, q_handle, options);
2846 Box::pin(async move { Ok(None) })
2847 }
2848 fn transpose(&self, _a: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
2849 Err(anyhow::anyhow!("transpose not supported by provider"))
2850 }
2851 fn conv1d(
2852 &self,
2853 _signal: &GpuTensorHandle,
2854 _kernel: &GpuTensorHandle,
2855 _options: ProviderConv1dOptions,
2856 ) -> anyhow::Result<GpuTensorHandle> {
2857 Err(anyhow::anyhow!("conv1d not supported by provider"))
2858 }
2859 fn conv2d(
2860 &self,
2861 _signal: &GpuTensorHandle,
2862 _kernel: &GpuTensorHandle,
2863 _mode: ProviderConvMode,
2864 ) -> anyhow::Result<GpuTensorHandle> {
2865 Err(anyhow::anyhow!("conv2d not supported by provider"))
2866 }
2867 fn iir_filter<'a>(
2868 &'a self,
2869 _b: &'a GpuTensorHandle,
2870 _a: &'a GpuTensorHandle,
2871 _x: &'a GpuTensorHandle,
2872 _options: ProviderIirFilterOptions,
2873 ) -> AccelProviderFuture<'a, ProviderIirFilterResult> {
2874 Box::pin(async move { Err(anyhow::anyhow!("iir_filter not supported by provider")) })
2875 }
2876 fn uniform_spectral_estimate<'a>(
2877 &'a self,
2878 _request: &'a ProviderSpectralRequest<'a>,
2879 ) -> AccelProviderFuture<'a, ProviderSpectralResult> {
2880 unsupported_future("uniform_spectral_estimate not supported by provider")
2881 }
2882 fn signal_envelope<'a>(
2883 &'a self,
2884 _request: &'a ProviderEnvelopeRequest<'a>,
2885 ) -> AccelProviderFuture<'a, ProviderEnvelopeResult> {
2886 unsupported_future("signal_envelope not supported by provider")
2887 }
2888 fn signal_hilbert<'a>(
2889 &'a self,
2890 _request: &'a ProviderHilbertRequest<'a>,
2891 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2892 unsupported_future("signal_hilbert not supported by provider")
2893 }
2894 fn permute(
2896 &self,
2897 _handle: &GpuTensorHandle,
2898 _order: &[usize],
2899 ) -> anyhow::Result<GpuTensorHandle> {
2900 Err(anyhow::anyhow!("permute not supported by provider"))
2901 }
2902 fn flip(&self, _handle: &GpuTensorHandle, _axes: &[usize]) -> anyhow::Result<GpuTensorHandle> {
2903 Err(anyhow::anyhow!("flip not supported by provider"))
2904 }
2905 fn circshift(
2906 &self,
2907 _handle: &GpuTensorHandle,
2908 _shifts: &[isize],
2909 ) -> anyhow::Result<GpuTensorHandle> {
2910 Err(anyhow::anyhow!("circshift not supported by provider"))
2911 }
2912 fn diff_dim(
2913 &self,
2914 _handle: &GpuTensorHandle,
2915 _order: usize,
2916 _dim: usize,
2917 ) -> anyhow::Result<GpuTensorHandle> {
2918 Err(anyhow::anyhow!("diff_dim not supported by provider"))
2919 }
2920 fn gradient_dim(
2921 &self,
2922 _handle: &GpuTensorHandle,
2923 _dim: usize,
2924 _spacing: f64,
2925 ) -> anyhow::Result<GpuTensorHandle> {
2926 Err(anyhow::anyhow!("gradient_dim not supported by provider"))
2927 }
2928 fn gradient_dim_with_coordinates(
2929 &self,
2930 _handle: &GpuTensorHandle,
2931 _dim: usize,
2932 _coordinates: &GpuTensorHandle,
2933 ) -> anyhow::Result<GpuTensorHandle> {
2934 Err(anyhow::anyhow!(
2935 "gradient_dim_with_coordinates not supported by provider"
2936 ))
2937 }
2938 fn fft_dim<'a>(
2940 &'a self,
2941 _handle: &'a GpuTensorHandle,
2942 _len: Option<usize>,
2943 _dim: usize,
2944 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2945 unsupported_future("fft_dim not supported by provider")
2946 }
2947 fn ifft_dim<'a>(
2948 &'a self,
2949 _handle: &'a GpuTensorHandle,
2950 _len: Option<usize>,
2951 _dim: usize,
2952 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2953 unsupported_future("ifft_dim not supported by provider")
2954 }
2955 fn fft_extract_real<'a>(
2956 &'a self,
2957 _handle: &'a GpuTensorHandle,
2958 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
2959 unsupported_future("fft_extract_real not supported by provider")
2960 }
2961 fn unique<'a>(
2962 &'a self,
2963 _handle: &'a GpuTensorHandle,
2964 _options: &'a UniqueOptions,
2965 ) -> AccelProviderFuture<'a, UniqueResult> {
2966 Box::pin(async move { Err(anyhow::anyhow!("unique not supported by provider")) })
2967 }
2968 fn union<'a>(
2969 &'a self,
2970 _a: &'a GpuTensorHandle,
2971 _b: &'a GpuTensorHandle,
2972 _options: &'a UnionOptions,
2973 ) -> AccelProviderFuture<'a, UnionResult> {
2974 Box::pin(async move { Err(anyhow::anyhow!("union not supported by provider")) })
2975 }
2976 fn setdiff<'a>(
2977 &'a self,
2978 _a: &'a GpuTensorHandle,
2979 _b: &'a GpuTensorHandle,
2980 _options: &'a SetdiffOptions,
2981 ) -> AccelProviderFuture<'a, SetdiffResult> {
2982 Box::pin(async move { Err(anyhow::anyhow!("setdiff not supported by provider")) })
2983 }
2984 fn ismember<'a>(
2985 &'a self,
2986 _a: &'a GpuTensorHandle,
2987 _b: &'a GpuTensorHandle,
2988 _options: &'a IsMemberOptions,
2989 ) -> AccelProviderFuture<'a, IsMemberResult> {
2990 Box::pin(async move { Err(anyhow::anyhow!("ismember not supported by provider")) })
2991 }
2992 fn reshape(
2993 &self,
2994 handle: &GpuTensorHandle,
2995 new_shape: &[usize],
2996 ) -> anyhow::Result<GpuTensorHandle> {
2997 let mut updated = handle.clone();
2998 updated.shape = new_shape.to_vec();
2999 Ok(updated)
3000 }
3001 fn cat(&self, _dim: usize, _inputs: &[GpuTensorHandle]) -> anyhow::Result<GpuTensorHandle> {
3003 Err(anyhow::anyhow!("cat not supported by provider"))
3004 }
3005 fn repmat(
3006 &self,
3007 _handle: &GpuTensorHandle,
3008 _reps: &[usize],
3009 ) -> anyhow::Result<GpuTensorHandle> {
3010 Err(anyhow::anyhow!("repmat not supported by provider"))
3011 }
3012 fn kron(&self, _a: &GpuTensorHandle, _b: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
3014 Err(anyhow::anyhow!("kron not supported by provider"))
3015 }
3016 fn cross(
3018 &self,
3019 _lhs: &GpuTensorHandle,
3020 _rhs: &GpuTensorHandle,
3021 _dim: Option<usize>,
3022 ) -> anyhow::Result<GpuTensorHandle> {
3023 Err(anyhow::anyhow!("cross not supported by provider"))
3024 }
3025 fn reduce_sum<'a>(
3026 &'a self,
3027 _a: &'a GpuTensorHandle,
3028 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3029 unsupported_future("reduce_sum not supported by provider")
3030 }
3031 fn reduce_sum_dim<'a>(
3032 &'a self,
3033 _a: &'a GpuTensorHandle,
3034 _dim: usize,
3035 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3036 unsupported_future("reduce_sum_dim not supported by provider")
3037 }
3038 fn reduce_integer_sum_native<'a>(
3042 &'a self,
3043 _a: &'a GpuTensorHandle,
3044 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3045 unsupported_future("reduce_integer_sum_native not supported by provider")
3046 }
3047 fn reduce_integer_sum_native_dim<'a>(
3049 &'a self,
3050 _a: &'a GpuTensorHandle,
3051 _dim: usize,
3052 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3053 unsupported_future("reduce_integer_sum_native_dim not supported by provider")
3054 }
3055 fn dot<'a>(
3056 &'a self,
3057 _lhs: &'a GpuTensorHandle,
3058 _rhs: &'a GpuTensorHandle,
3059 _dim: Option<usize>,
3060 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3061 unsupported_future("dot not supported by provider")
3062 }
3063 fn reduce_nnz<'a>(
3064 &'a self,
3065 _a: &'a GpuTensorHandle,
3066 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3067 unsupported_future("reduce_nnz not supported by provider")
3068 }
3069 fn reduce_nnz_dim<'a>(
3070 &'a self,
3071 _a: &'a GpuTensorHandle,
3072 _dim: usize,
3073 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3074 unsupported_future("reduce_nnz_dim not supported by provider")
3075 }
3076 fn reduce_prod<'a>(
3077 &'a self,
3078 _a: &'a GpuTensorHandle,
3079 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3080 unsupported_future("reduce_prod not supported by provider")
3081 }
3082 fn reduce_prod_dim<'a>(
3083 &'a self,
3084 _a: &'a GpuTensorHandle,
3085 _dim: usize,
3086 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3087 unsupported_future("reduce_prod_dim not supported by provider")
3088 }
3089 fn reduce_integer_prod_native<'a>(
3092 &'a self,
3093 _a: &'a GpuTensorHandle,
3094 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3095 unsupported_future("reduce_integer_prod_native not supported by provider")
3096 }
3097 fn reduce_integer_prod_native_dim<'a>(
3099 &'a self,
3100 _a: &'a GpuTensorHandle,
3101 _dim: usize,
3102 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3103 unsupported_future("reduce_integer_prod_native_dim not supported by provider")
3104 }
3105 fn reduce_integer_mean_native<'a>(
3109 &'a self,
3110 _a: &'a GpuTensorHandle,
3111 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3112 unsupported_future("reduce_integer_mean_native not supported by provider")
3113 }
3114 fn reduce_integer_mean_native_dim<'a>(
3116 &'a self,
3117 _a: &'a GpuTensorHandle,
3118 _dim: usize,
3119 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3120 unsupported_future("reduce_integer_mean_native_dim not supported by provider")
3121 }
3122 fn reduce_integer_mean_native_dims<'a>(
3126 &'a self,
3127 _a: &'a GpuTensorHandle,
3128 _dims_zero_based: &'a [usize],
3129 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3130 unsupported_future("reduce_integer_mean_native_dims not supported by provider")
3131 }
3132 fn cast_to_integer<'a>(
3136 &'a self,
3137 _a: &'a GpuTensorHandle,
3138 _target: IntegerElementType,
3139 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3140 unsupported_future("cast_to_integer not supported by provider")
3141 }
3142 fn reduce_mean<'a>(
3143 &'a self,
3144 _a: &'a GpuTensorHandle,
3145 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3146 unsupported_future("reduce_mean not supported by provider")
3147 }
3148 fn reduce_mean_nd<'a>(
3150 &'a self,
3151 _a: &'a GpuTensorHandle,
3152 _dims_zero_based: &'a [usize],
3153 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3154 unsupported_future("reduce_mean_nd not supported by provider")
3155 }
3156 fn reduce_moments_nd<'a>(
3159 &'a self,
3160 _a: &'a GpuTensorHandle,
3161 _dims_zero_based: &'a [usize],
3162 ) -> AccelProviderFuture<'a, ProviderMoments2> {
3163 unsupported_future("reduce_moments_nd not supported by provider")
3164 }
3165 fn reduce_mean_dim<'a>(
3166 &'a self,
3167 _a: &'a GpuTensorHandle,
3168 _dim: usize,
3169 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3170 unsupported_future("reduce_mean_dim not supported by provider")
3171 }
3172 fn reduce_std<'a>(
3173 &'a self,
3174 _a: &'a GpuTensorHandle,
3175 _normalization: ProviderStdNormalization,
3176 _nan_mode: ProviderNanMode,
3177 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3178 unsupported_future("reduce_std not supported by provider")
3179 }
3180 fn reduce_std_dim<'a>(
3181 &'a self,
3182 _a: &'a GpuTensorHandle,
3183 _dim: usize,
3184 _normalization: ProviderStdNormalization,
3185 _nan_mode: ProviderNanMode,
3186 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3187 unsupported_future("reduce_std_dim not supported by provider")
3188 }
3189 fn reduce_any<'a>(
3190 &'a self,
3191 _a: &'a GpuTensorHandle,
3192 _omit_nan: bool,
3193 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3194 unsupported_future("reduce_any not supported by provider")
3195 }
3196 fn reduce_any_dim<'a>(
3197 &'a self,
3198 _a: &'a GpuTensorHandle,
3199 _dim: usize,
3200 _omit_nan: bool,
3201 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3202 unsupported_future("reduce_any_dim not supported by provider")
3203 }
3204 fn reduce_all<'a>(
3205 &'a self,
3206 _a: &'a GpuTensorHandle,
3207 _omit_nan: bool,
3208 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3209 unsupported_future("reduce_all not supported by provider")
3210 }
3211 fn reduce_all_dim<'a>(
3212 &'a self,
3213 _a: &'a GpuTensorHandle,
3214 _dim: usize,
3215 _omit_nan: bool,
3216 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3217 unsupported_future("reduce_all_dim not supported by provider")
3218 }
3219 fn reduce_median<'a>(
3220 &'a self,
3221 _a: &'a GpuTensorHandle,
3222 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3223 unsupported_future("reduce_median not supported by provider")
3224 }
3225 fn reduce_median_dim<'a>(
3226 &'a self,
3227 _a: &'a GpuTensorHandle,
3228 _dim: usize,
3229 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3230 unsupported_future("reduce_median_dim not supported by provider")
3231 }
3232 fn mode_values<'a>(
3233 &'a self,
3234 _request: &'a ProviderModeRequest<'a>,
3235 ) -> AccelProviderFuture<'a, ProviderModeResult> {
3236 unsupported_future("mode_values not supported by provider")
3237 }
3238 fn moving_window<'a>(
3239 &'a self,
3240 _request: &'a ProviderMovingWindowRequest<'a>,
3241 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3242 unsupported_future("moving_window not supported by provider")
3243 }
3244 fn reduce_min<'a>(
3245 &'a self,
3246 _a: &'a GpuTensorHandle,
3247 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3248 unsupported_future("reduce_min not supported by provider")
3249 }
3250 fn reduce_min_dim<'a>(
3251 &'a self,
3252 _a: &'a GpuTensorHandle,
3253 _dim: usize,
3254 ) -> AccelProviderFuture<'a, ReduceDimResult> {
3255 unsupported_future("reduce_min_dim not supported by provider")
3256 }
3257 fn reduce_max<'a>(
3258 &'a self,
3259 _a: &'a GpuTensorHandle,
3260 ) -> AccelProviderFuture<'a, GpuTensorHandle> {
3261 unsupported_future("reduce_max not supported by provider")
3262 }
3263 fn reduce_max_dim<'a>(
3264 &'a self,
3265 _a: &'a GpuTensorHandle,
3266 _dim: usize,
3267 ) -> AccelProviderFuture<'a, ReduceDimResult> {
3268 unsupported_future("reduce_max_dim not supported by provider")
3269 }
3270 fn cumsum_scan(
3271 &self,
3272 _input: &GpuTensorHandle,
3273 _dim: usize,
3274 _direction: ProviderScanDirection,
3275 _nan_mode: ProviderNanMode,
3276 ) -> anyhow::Result<GpuTensorHandle> {
3277 Err(anyhow::anyhow!("cumsum_scan not supported by provider"))
3278 }
3279 fn integer_cumsum_scan(
3280 &self,
3281 _input: &GpuTensorHandle,
3282 _dim: usize,
3283 _direction: ProviderScanDirection,
3284 ) -> anyhow::Result<GpuTensorHandle> {
3285 Err(anyhow::anyhow!(
3286 "integer_cumsum_scan not supported by provider"
3287 ))
3288 }
3289 fn trapz_dim(
3290 &self,
3291 _input: &GpuTensorHandle,
3292 _dim: usize,
3293 _spacing: ProviderTrapezoidSpacing<'_>,
3294 ) -> anyhow::Result<GpuTensorHandle> {
3295 Err(anyhow::anyhow!("trapz_dim not supported by provider"))
3296 }
3297 fn cumtrapz_dim(
3298 &self,
3299 _input: &GpuTensorHandle,
3300 _dim: usize,
3301 _spacing: ProviderTrapezoidSpacing<'_>,
3302 ) -> anyhow::Result<GpuTensorHandle> {
3303 Err(anyhow::anyhow!("cumtrapz_dim not supported by provider"))
3304 }
3305 fn cumprod_scan(
3306 &self,
3307 _input: &GpuTensorHandle,
3308 _dim: usize,
3309 _direction: ProviderScanDirection,
3310 _nan_mode: ProviderNanMode,
3311 ) -> anyhow::Result<GpuTensorHandle> {
3312 Err(anyhow::anyhow!("cumprod_scan not supported by provider"))
3313 }
3314 fn integer_cumprod_scan(
3315 &self,
3316 _input: &GpuTensorHandle,
3317 _dim: usize,
3318 _direction: ProviderScanDirection,
3319 ) -> anyhow::Result<GpuTensorHandle> {
3320 Err(anyhow::anyhow!(
3321 "integer_cumprod_scan not supported by provider"
3322 ))
3323 }
3324 fn cummin_scan(
3325 &self,
3326 _input: &GpuTensorHandle,
3327 _dim: usize,
3328 _direction: ProviderScanDirection,
3329 _nan_mode: ProviderNanMode,
3330 ) -> anyhow::Result<ProviderCumminResult> {
3331 Err(anyhow::anyhow!("cummin_scan not supported by provider"))
3332 }
3333 fn integer_cummin_scan(
3334 &self,
3335 _input: &GpuTensorHandle,
3336 _dim: usize,
3337 _direction: ProviderScanDirection,
3338 ) -> anyhow::Result<ProviderCumminResult> {
3339 Err(anyhow::anyhow!(
3340 "integer_cummin_scan not supported by provider"
3341 ))
3342 }
3343 fn cummax_scan(
3344 &self,
3345 _input: &GpuTensorHandle,
3346 _dim: usize,
3347 _direction: ProviderScanDirection,
3348 _nan_mode: ProviderNanMode,
3349 ) -> anyhow::Result<ProviderCummaxResult> {
3350 Err(anyhow::anyhow!("cummax_scan not supported by provider"))
3351 }
3352 fn integer_cummax_scan(
3353 &self,
3354 _input: &GpuTensorHandle,
3355 _dim: usize,
3356 _direction: ProviderScanDirection,
3357 ) -> anyhow::Result<ProviderCummaxResult> {
3358 Err(anyhow::anyhow!(
3359 "integer_cummax_scan not supported by provider"
3360 ))
3361 }
3362
3363 fn find(
3364 &self,
3365 _a: &GpuTensorHandle,
3366 _limit: Option<usize>,
3367 _direction: FindDirection,
3368 ) -> anyhow::Result<ProviderFindResult> {
3369 Err(anyhow::anyhow!("find not supported by provider"))
3370 }
3371
3372 fn fused_elementwise(
3373 &self,
3374 _shader: &str,
3375 _inputs: &[GpuTensorHandle],
3376 _output_shape: &[usize],
3377 _len: usize,
3378 ) -> anyhow::Result<GpuTensorHandle> {
3379 Err(anyhow::anyhow!(
3380 "fused_elementwise not supported by provider"
3381 ))
3382 }
3383
3384 fn fused_elementwise_multi(
3393 &self,
3394 _shader: &str,
3395 _inputs: &[GpuTensorHandle],
3396 _output_shape: &[usize],
3397 _len: usize,
3398 _num_outputs: usize,
3399 ) -> anyhow::Result<Vec<GpuTensorHandle>> {
3400 Err(anyhow::anyhow!(
3401 "fused_elementwise_multi not supported by provider"
3402 ))
3403 }
3404
3405 fn map_nan_to_zero(&self, _a: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
3407 Err(anyhow::anyhow!("map_nan_to_zero not supported by provider"))
3408 }
3409
3410 fn not_nan_mask(&self, _a: &GpuTensorHandle) -> anyhow::Result<GpuTensorHandle> {
3412 Err(anyhow::anyhow!("not_nan_mask not supported by provider"))
3413 }
3414
3415 #[allow(clippy::too_many_arguments)]
3422 fn fused_reduction(
3423 &self,
3424 _shader: &str,
3425 _inputs: &[GpuTensorHandle],
3426 _output_shape: &[usize],
3427 _reduce_len: usize,
3428 _num_slices: usize,
3429 _workgroup_size: u32,
3430 _flavor: ReductionFlavor,
3431 ) -> anyhow::Result<GpuTensorHandle> {
3432 Err(anyhow::anyhow!("fused_reduction not supported by provider"))
3433 }
3434
3435 fn warmup(&self) {}
3437
3438 fn fused_cache_counters(&self) -> (u64, u64) {
3440 (0, 0)
3441 }
3442
3443 fn last_warmup_millis(&self) -> Option<u64> {
3445 None
3446 }
3447
3448 fn telemetry_snapshot(&self) -> ProviderTelemetry {
3450 let (hits, misses) = self.fused_cache_counters();
3451 ProviderTelemetry {
3452 fused_elementwise: ProviderDispatchStats::default(),
3453 fused_reduction: ProviderDispatchStats::default(),
3454 matmul: ProviderDispatchStats::default(),
3455 linsolve: ProviderDispatchStats::default(),
3456 mldivide: ProviderDispatchStats::default(),
3457 mrdivide: ProviderDispatchStats::default(),
3458 upload_bytes: 0,
3459 download_bytes: 0,
3460 solve_fallbacks: Vec::new(),
3461 fusion_cache_hits: hits,
3462 fusion_cache_misses: misses,
3463 bind_group_cache_hits: 0,
3464 bind_group_cache_misses: 0,
3465 bind_group_cache_by_layout: None,
3466 kernel_launches: Vec::new(),
3467 }
3468 }
3469
3470 fn reset_telemetry(&self) {}
3472
3473 fn default_reduction_workgroup_size(&self) -> u32 {
3475 256
3476 }
3477
3478 fn two_pass_threshold(&self) -> usize {
3480 1024
3481 }
3482
3483 fn reduction_two_pass_mode(&self) -> ReductionTwoPassMode {
3485 ReductionTwoPassMode::Auto
3486 }
3487
3488 fn scatter_column(
3491 &self,
3492 _matrix: &GpuTensorHandle,
3493 _col_index: usize,
3494 _values: &GpuTensorHandle,
3495 ) -> anyhow::Result<GpuTensorHandle> {
3496 Err(anyhow::anyhow!("scatter_column not supported by provider"))
3497 }
3498
3499 fn scatter_row(
3502 &self,
3503 _matrix: &GpuTensorHandle,
3504 _row_index: usize,
3505 _values: &GpuTensorHandle,
3506 ) -> anyhow::Result<GpuTensorHandle> {
3507 Err(anyhow::anyhow!("scatter_row not supported by provider"))
3508 }
3509
3510 fn sub2ind(
3511 &self,
3512 _dims: &[usize],
3513 _strides: &[usize],
3514 _inputs: &[&GpuTensorHandle],
3515 _scalar_mask: &[bool],
3516 _len: usize,
3517 _output_shape: &[usize],
3518 ) -> anyhow::Result<GpuTensorHandle> {
3519 Err(anyhow::anyhow!("sub2ind not supported by provider"))
3520 }
3521
3522 fn supports_ind2sub(&self) -> bool {
3524 false
3525 }
3526
3527 fn ind2sub(
3529 &self,
3530 _dims: &[usize],
3531 _strides: &[usize],
3532 _indices: &GpuTensorHandle,
3533 _total: usize,
3534 _len: usize,
3535 _output_shape: &[usize],
3536 ) -> anyhow::Result<Vec<GpuTensorHandle>> {
3537 Err(anyhow::anyhow!("ind2sub not supported by provider"))
3538 }
3539
3540 fn issymmetric(
3542 &self,
3543 _matrix: &GpuTensorHandle,
3544 _kind: ProviderSymmetryKind,
3545 _tolerance: f64,
3546 ) -> anyhow::Result<bool> {
3547 Err(anyhow::anyhow!(
3548 "issymmetric predicate not supported by provider"
3549 ))
3550 }
3551
3552 fn ishermitian<'a>(
3554 &'a self,
3555 _matrix: &'a GpuTensorHandle,
3556 _kind: ProviderHermitianKind,
3557 _tolerance: f64,
3558 ) -> AccelProviderFuture<'a, bool> {
3559 Box::pin(async move {
3560 Err(anyhow::anyhow!(
3561 "ishermitian predicate not supported by provider"
3562 ))
3563 })
3564 }
3565
3566 fn bandwidth(&self, _matrix: &GpuTensorHandle) -> anyhow::Result<ProviderBandwidth> {
3568 Err(anyhow::anyhow!("bandwidth not supported by provider"))
3569 }
3570
3571 fn sym_rcm<'a>(&'a self, _matrix: &'a GpuTensorHandle) -> AccelProviderFuture<'a, Vec<usize>> {
3576 Box::pin(async move { Err(anyhow::anyhow!("sym_rcm not supported by provider")) })
3577 }
3578}
3579
3580static GLOBAL_PROVIDER: Lazy<RwLock<Option<&'static dyn AccelProvider>>> =
3581 Lazy::new(|| RwLock::new(None));
3582static PROVIDER_REGISTRY: Lazy<RwLock<HashMap<u32, &'static dyn AccelProvider>>> =
3583 Lazy::new(|| RwLock::new(HashMap::new()));
3584static DEVICE_ID_COUNTER: AtomicU32 = AtomicU32::new(1);
3585
3586#[cfg(not(target_arch = "wasm32"))]
3587thread_local! {
3588 static THREAD_PROVIDER: Cell<Option<&'static dyn AccelProvider>> = Cell::new(None);
3589}
3590
3591#[cfg(target_arch = "wasm32")]
3592static WASM_THREAD_PROVIDER: Lazy<Mutex<Option<&'static dyn AccelProvider>>> =
3593 Lazy::new(|| Mutex::new(None));
3594
3595#[cfg(not(target_arch = "wasm32"))]
3596fn replace_thread_provider(
3597 provider: Option<&'static dyn AccelProvider>,
3598) -> Option<&'static dyn AccelProvider> {
3599 THREAD_PROVIDER.with(|cell| {
3600 let prev = cell.get();
3601 cell.set(provider);
3602 prev
3603 })
3604}
3605
3606#[cfg(target_arch = "wasm32")]
3607fn replace_thread_provider(
3608 provider: Option<&'static dyn AccelProvider>,
3609) -> Option<&'static dyn AccelProvider> {
3610 let mut slot = WASM_THREAD_PROVIDER
3611 .lock()
3612 .expect("wasm provider mutex poisoned");
3613 let prev = *slot;
3614 *slot = provider;
3615 prev
3616}
3617
3618#[cfg(not(target_arch = "wasm32"))]
3619fn current_thread_provider() -> Option<&'static dyn AccelProvider> {
3620 THREAD_PROVIDER.with(|cell| cell.get())
3621}
3622
3623#[cfg(target_arch = "wasm32")]
3624fn current_thread_provider() -> Option<&'static dyn AccelProvider> {
3625 WASM_THREAD_PROVIDER
3626 .lock()
3627 .expect("wasm provider mutex poisoned")
3628 .as_ref()
3629 .copied()
3630}
3631
3632pub unsafe fn register_provider(p: &'static dyn AccelProvider) {
3642 replace_thread_provider(Some(p));
3643 if let Ok(mut guard) = GLOBAL_PROVIDER.write() {
3644 *guard = Some(p);
3645 }
3646 register_provider_for_device(p.device_id(), p);
3647}
3648
3649unsafe fn register_provider_for_device(device_id: u32, provider: &'static dyn AccelProvider) {
3650 if let Ok(mut guard) = PROVIDER_REGISTRY.write() {
3651 guard.insert(device_id, provider);
3652 }
3653}
3654
3655pub fn provider() -> Option<&'static dyn AccelProvider> {
3656 if let Some(p) = current_thread_provider() {
3657 return Some(p);
3658 }
3659 GLOBAL_PROVIDER
3660 .read()
3661 .ok()
3662 .and_then(|guard| guard.as_ref().copied())
3663}
3664
3665pub fn clear_provider() {
3672 replace_thread_provider(None);
3673 if let Ok(mut guard) = GLOBAL_PROVIDER.write() {
3674 *guard = None;
3675 }
3676}
3677
3678pub fn provider_for_device(device_id: u32) -> Option<&'static dyn AccelProvider> {
3679 if let Some(registered) = PROVIDER_REGISTRY
3680 .read()
3681 .ok()
3682 .and_then(|guard| guard.get(&device_id).copied())
3683 {
3684 return Some(registered);
3685 }
3686 if let Some(thread_provider) = current_thread_provider() {
3687 if thread_provider.device_id() == device_id {
3688 return Some(thread_provider);
3689 }
3690 }
3691 GLOBAL_PROVIDER
3694 .read()
3695 .ok()
3696 .and_then(|guard| guard.as_ref().copied())
3697}
3698
3699pub fn provider_for_handle(handle: &GpuTensorHandle) -> Option<&'static dyn AccelProvider> {
3700 provider_for_device(handle.device_id)
3701}
3702
3703pub fn spawn_handle_concurrency_for(handle: &GpuTensorHandle) -> Option<SpawnHandleConcurrency> {
3704 provider_for_handle(handle).map(AccelProvider::spawn_handle_concurrency)
3705}
3706
3707pub fn next_device_id() -> u32 {
3708 DEVICE_ID_COUNTER.fetch_add(1, Ordering::Relaxed)
3709}
3710
3711pub struct ThreadProviderGuard {
3712 prev: Option<&'static dyn AccelProvider>,
3713}
3714
3715impl ThreadProviderGuard {
3716 pub fn set(provider: Option<&'static dyn AccelProvider>) -> Self {
3717 let prev = replace_thread_provider(provider);
3718 ThreadProviderGuard { prev }
3719 }
3720}
3721
3722impl Drop for ThreadProviderGuard {
3723 fn drop(&mut self) {
3724 let prev = self.prev.take();
3725 replace_thread_provider(prev);
3726 }
3727}
3728
3729pub fn set_thread_provider(provider: Option<&'static dyn AccelProvider>) {
3730 replace_thread_provider(provider);
3731}
3732
3733pub async fn try_elem_add(a: &GpuTensorHandle, b: &GpuTensorHandle) -> Option<GpuTensorHandle> {
3735 if a.device_id == b.device_id {
3736 let p = provider_for_handle(a)?;
3737 if let Ok(h) = p.elem_add(a, b).await {
3738 return Some(h);
3739 }
3740 }
3741 None
3742}
3743
3744pub async fn try_elem_hypot(a: &GpuTensorHandle, b: &GpuTensorHandle) -> Option<GpuTensorHandle> {
3746 if a.device_id == b.device_id {
3747 let p = provider_for_handle(a)?;
3748 if let Ok(h) = p.elem_hypot(a, b).await {
3749 return Some(h);
3750 }
3751 }
3752 None
3753}
3754
3755pub async fn try_elem_max(a: &GpuTensorHandle, b: &GpuTensorHandle) -> Option<GpuTensorHandle> {
3757 if a.device_id == b.device_id {
3758 let p = provider_for_handle(a)?;
3759 if let Ok(h) = p.elem_max(a, b).await {
3760 return Some(h);
3761 }
3762 }
3763 None
3764}
3765
3766pub async fn try_elem_min(a: &GpuTensorHandle, b: &GpuTensorHandle) -> Option<GpuTensorHandle> {
3768 if a.device_id == b.device_id {
3769 let p = provider_for_handle(a)?;
3770 if let Ok(h) = p.elem_min(a, b).await {
3771 return Some(h);
3772 }
3773 }
3774 None
3775}
3776
3777pub async fn try_elem_atan2(y: &GpuTensorHandle, x: &GpuTensorHandle) -> Option<GpuTensorHandle> {
3779 if y.device_id == x.device_id {
3780 let p = provider_for_handle(y)?;
3781 if let Ok(h) = p.elem_atan2(y, x).await {
3782 return Some(h);
3783 }
3784 }
3785 None
3786}
3787
3788#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
3794pub enum NumericElementType {
3795 F64,
3796 F32,
3797 I8,
3798 I16,
3799 I32,
3800 I64,
3801 U8,
3802 U16,
3803 U32,
3804 U64,
3805}
3806
3807impl NumericElementType {
3808 pub const fn element_size(self) -> usize {
3809 match self {
3810 Self::F64 | Self::I64 | Self::U64 => 8,
3811 Self::F32 | Self::I32 | Self::U32 => 4,
3812 Self::I16 | Self::U16 => 2,
3813 Self::I8 | Self::U8 => 1,
3814 }
3815 }
3816
3817 pub const fn class_name(self) -> &'static str {
3818 match self {
3819 Self::F64 => "double",
3820 Self::F32 => "single",
3821 Self::I8 => "int8",
3822 Self::I16 => "int16",
3823 Self::I32 => "int32",
3824 Self::I64 => "int64",
3825 Self::U8 => "uint8",
3826 Self::U16 => "uint16",
3827 Self::U32 => "uint32",
3828 Self::U64 => "uint64",
3829 }
3830 }
3831
3832 pub const fn precision(self) -> Option<ProviderPrecision> {
3833 match self {
3834 Self::F64 => Some(ProviderPrecision::F64),
3835 Self::F32 => Some(ProviderPrecision::F32),
3836 Self::I8
3837 | Self::I16
3838 | Self::I32
3839 | Self::I64
3840 | Self::U8
3841 | Self::U16
3842 | Self::U32
3843 | Self::U64 => None,
3844 }
3845 }
3846
3847 pub const fn integer_type(self) -> Option<IntegerElementType> {
3848 match self {
3849 Self::F64 | Self::F32 => None,
3850 Self::I8 => Some(IntegerElementType::I8),
3851 Self::I16 => Some(IntegerElementType::I16),
3852 Self::I32 => Some(IntegerElementType::I32),
3853 Self::I64 => Some(IntegerElementType::I64),
3854 Self::U8 => Some(IntegerElementType::U8),
3855 Self::U16 => Some(IntegerElementType::U16),
3856 Self::U32 => Some(IntegerElementType::U32),
3857 Self::U64 => Some(IntegerElementType::U64),
3858 }
3859 }
3860}
3861
3862impl From<IntegerElementType> for NumericElementType {
3863 fn from(value: IntegerElementType) -> Self {
3864 match value {
3865 IntegerElementType::I8 => Self::I8,
3866 IntegerElementType::I16 => Self::I16,
3867 IntegerElementType::I32 => Self::I32,
3868 IntegerElementType::I64 => Self::I64,
3869 IntegerElementType::U8 => Self::U8,
3870 IntegerElementType::U16 => Self::U16,
3871 IntegerElementType::U32 => Self::U32,
3872 IntegerElementType::U64 => Self::U64,
3873 }
3874 }
3875}
3876
3877#[derive(Debug, Clone, Copy)]
3879pub enum HostNumericDataView<'a> {
3880 F64(&'a [f64]),
3881 F32(&'a [f32]),
3882 I8(&'a [i8]),
3883 I16(&'a [i16]),
3884 I32(&'a [i32]),
3885 I64(&'a [i64]),
3886 U8(&'a [u8]),
3887 U16(&'a [u16]),
3888 U32(&'a [u32]),
3889 U64(&'a [u64]),
3890}
3891
3892impl HostNumericDataView<'_> {
3893 pub const fn element_type(self) -> NumericElementType {
3894 match self {
3895 Self::F64(_) => NumericElementType::F64,
3896 Self::F32(_) => NumericElementType::F32,
3897 Self::I8(_) => NumericElementType::I8,
3898 Self::I16(_) => NumericElementType::I16,
3899 Self::I32(_) => NumericElementType::I32,
3900 Self::I64(_) => NumericElementType::I64,
3901 Self::U8(_) => NumericElementType::U8,
3902 Self::U16(_) => NumericElementType::U16,
3903 Self::U32(_) => NumericElementType::U32,
3904 Self::U64(_) => NumericElementType::U64,
3905 }
3906 }
3907
3908 pub const fn len(self) -> usize {
3909 match self {
3910 Self::F64(data) => data.len(),
3911 Self::F32(data) => data.len(),
3912 Self::I8(data) => data.len(),
3913 Self::I16(data) => data.len(),
3914 Self::I32(data) => data.len(),
3915 Self::I64(data) => data.len(),
3916 Self::U8(data) => data.len(),
3917 Self::U16(data) => data.len(),
3918 Self::U32(data) => data.len(),
3919 Self::U64(data) => data.len(),
3920 }
3921 }
3922
3923 pub const fn is_empty(self) -> bool {
3924 self.len() == 0
3925 }
3926}
3927
3928#[derive(Debug, Clone, Copy)]
3931pub struct HostNumericTensorView<'a> {
3932 pub data: HostNumericDataView<'a>,
3933 pub shape: &'a [usize],
3934 pub storage: GpuTensorStorage,
3935}
3936
3937impl HostNumericTensorView<'_> {
3938 pub fn validate(self) -> anyhow::Result<()> {
3939 let logical_len = self
3940 .shape
3941 .iter()
3942 .try_fold(1usize, |len, &dim| len.checked_mul(dim))
3943 .ok_or_else(|| anyhow!("numeric transfer shape overflows usize"))?;
3944 let expected = match self.storage {
3945 GpuTensorStorage::Real => logical_len,
3946 GpuTensorStorage::ComplexInterleaved => logical_len
3947 .checked_mul(2)
3948 .ok_or_else(|| anyhow!("complex numeric transfer length overflows usize"))?,
3949 };
3950 if self.data.len() != expected {
3951 return Err(anyhow!(
3952 "{} transfer has {} elements for shape {:?} and {:?} storage; expected {}",
3953 self.data.element_type().class_name(),
3954 self.data.len(),
3955 self.shape,
3956 self.storage,
3957 expected
3958 ));
3959 }
3960 Ok(())
3961 }
3962
3963 pub const fn element_type(self) -> NumericElementType {
3964 self.data.element_type()
3965 }
3966}
3967
3968#[derive(Debug, Clone, PartialEq)]
3970pub enum HostNumericDataOwned {
3971 F64(Vec<f64>),
3972 F32(Vec<f32>),
3973 I8(Vec<i8>),
3974 I16(Vec<i16>),
3975 I32(Vec<i32>),
3976 I64(Vec<i64>),
3977 U8(Vec<u8>),
3978 U16(Vec<u16>),
3979 U32(Vec<u32>),
3980 U64(Vec<u64>),
3981}
3982
3983impl HostNumericDataOwned {
3984 pub const fn element_type(&self) -> NumericElementType {
3985 match self {
3986 Self::F64(_) => NumericElementType::F64,
3987 Self::F32(_) => NumericElementType::F32,
3988 Self::I8(_) => NumericElementType::I8,
3989 Self::I16(_) => NumericElementType::I16,
3990 Self::I32(_) => NumericElementType::I32,
3991 Self::I64(_) => NumericElementType::I64,
3992 Self::U8(_) => NumericElementType::U8,
3993 Self::U16(_) => NumericElementType::U16,
3994 Self::U32(_) => NumericElementType::U32,
3995 Self::U64(_) => NumericElementType::U64,
3996 }
3997 }
3998
3999 pub fn as_view(&self) -> HostNumericDataView<'_> {
4000 match self {
4001 Self::F64(data) => HostNumericDataView::F64(data),
4002 Self::F32(data) => HostNumericDataView::F32(data),
4003 Self::I8(data) => HostNumericDataView::I8(data),
4004 Self::I16(data) => HostNumericDataView::I16(data),
4005 Self::I32(data) => HostNumericDataView::I32(data),
4006 Self::I64(data) => HostNumericDataView::I64(data),
4007 Self::U8(data) => HostNumericDataView::U8(data),
4008 Self::U16(data) => HostNumericDataView::U16(data),
4009 Self::U32(data) => HostNumericDataView::U32(data),
4010 Self::U64(data) => HostNumericDataView::U64(data),
4011 }
4012 }
4013
4014 pub fn len(&self) -> usize {
4015 self.as_view().len()
4016 }
4017
4018 pub fn is_empty(&self) -> bool {
4019 self.len() == 0
4020 }
4021}
4022
4023#[derive(Debug, Clone, PartialEq)]
4026pub struct HostNumericTensorOwned {
4027 pub data: HostNumericDataOwned,
4028 pub shape: Vec<usize>,
4029 pub storage: GpuTensorStorage,
4030}
4031
4032impl HostNumericTensorOwned {
4033 pub fn as_view(&self) -> HostNumericTensorView<'_> {
4034 HostNumericTensorView {
4035 data: self.data.as_view(),
4036 shape: &self.shape,
4037 storage: self.storage,
4038 }
4039 }
4040
4041 pub fn validate(&self) -> anyhow::Result<()> {
4042 self.as_view().validate()
4043 }
4044}
4045
4046#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4048pub struct HostTensorOwned {
4049 pub data: Vec<f64>,
4050 pub shape: Vec<usize>,
4051 pub storage: GpuTensorStorage,
4052}
4053
4054#[derive(Debug)]
4055pub struct HostTensorView<'a> {
4056 pub data: &'a [f64],
4057 pub shape: &'a [usize],
4058}
4059
4060#[derive(Debug, Clone, Copy)]
4062pub enum HostIntegerDataView<'a> {
4063 I8(&'a [i8]),
4064 I16(&'a [i16]),
4065 I32(&'a [i32]),
4066 I64(&'a [i64]),
4067 U8(&'a [u8]),
4068 U16(&'a [u16]),
4069 U32(&'a [u32]),
4070 U64(&'a [u64]),
4071}
4072
4073impl HostIntegerDataView<'_> {
4074 pub fn element_type(self) -> IntegerElementType {
4075 match self {
4076 Self::I8(_) => IntegerElementType::I8,
4077 Self::I16(_) => IntegerElementType::I16,
4078 Self::I32(_) => IntegerElementType::I32,
4079 Self::I64(_) => IntegerElementType::I64,
4080 Self::U8(_) => IntegerElementType::U8,
4081 Self::U16(_) => IntegerElementType::U16,
4082 Self::U32(_) => IntegerElementType::U32,
4083 Self::U64(_) => IntegerElementType::U64,
4084 }
4085 }
4086
4087 pub fn len(self) -> usize {
4088 match self {
4089 Self::I8(data) => data.len(),
4090 Self::I16(data) => data.len(),
4091 Self::I32(data) => data.len(),
4092 Self::I64(data) => data.len(),
4093 Self::U8(data) => data.len(),
4094 Self::U16(data) => data.len(),
4095 Self::U32(data) => data.len(),
4096 Self::U64(data) => data.len(),
4097 }
4098 }
4099
4100 pub fn is_empty(self) -> bool {
4101 self.len() == 0
4102 }
4103}
4104
4105#[derive(Debug, Clone, Copy)]
4107pub struct HostIntegerTensorView<'a> {
4108 pub data: HostIntegerDataView<'a>,
4109 pub shape: &'a [usize],
4110}
4111
4112#[derive(Debug, Clone, PartialEq, Eq)]
4114pub enum HostIntegerDataOwned {
4115 I8(Vec<i8>),
4116 I16(Vec<i16>),
4117 I32(Vec<i32>),
4118 I64(Vec<i64>),
4119 U8(Vec<u8>),
4120 U16(Vec<u16>),
4121 U32(Vec<u32>),
4122 U64(Vec<u64>),
4123}
4124
4125impl HostIntegerDataOwned {
4126 pub fn element_type(&self) -> IntegerElementType {
4127 match self {
4128 Self::I8(_) => IntegerElementType::I8,
4129 Self::I16(_) => IntegerElementType::I16,
4130 Self::I32(_) => IntegerElementType::I32,
4131 Self::I64(_) => IntegerElementType::I64,
4132 Self::U8(_) => IntegerElementType::U8,
4133 Self::U16(_) => IntegerElementType::U16,
4134 Self::U32(_) => IntegerElementType::U32,
4135 Self::U64(_) => IntegerElementType::U64,
4136 }
4137 }
4138}
4139
4140#[derive(Debug, Clone, PartialEq, Eq)]
4142pub struct HostIntegerTensorOwned {
4143 pub data: HostIntegerDataOwned,
4144 pub shape: Vec<usize>,
4145}
4146
4147impl<'a> From<HostTensorView<'a>> for HostNumericTensorView<'a> {
4148 fn from(value: HostTensorView<'a>) -> Self {
4149 Self {
4150 data: HostNumericDataView::F64(value.data),
4151 shape: value.shape,
4152 storage: GpuTensorStorage::Real,
4153 }
4154 }
4155}
4156
4157impl<'a> From<&'a HostTensorOwned> for HostNumericTensorView<'a> {
4158 fn from(value: &'a HostTensorOwned) -> Self {
4159 Self {
4160 data: HostNumericDataView::F64(&value.data),
4161 shape: &value.shape,
4162 storage: value.storage,
4163 }
4164 }
4165}
4166
4167impl<'a> From<HostIntegerDataView<'a>> for HostNumericDataView<'a> {
4168 fn from(value: HostIntegerDataView<'a>) -> Self {
4169 match value {
4170 HostIntegerDataView::I8(data) => Self::I8(data),
4171 HostIntegerDataView::I16(data) => Self::I16(data),
4172 HostIntegerDataView::I32(data) => Self::I32(data),
4173 HostIntegerDataView::I64(data) => Self::I64(data),
4174 HostIntegerDataView::U8(data) => Self::U8(data),
4175 HostIntegerDataView::U16(data) => Self::U16(data),
4176 HostIntegerDataView::U32(data) => Self::U32(data),
4177 HostIntegerDataView::U64(data) => Self::U64(data),
4178 }
4179 }
4180}
4181
4182impl<'a> From<HostIntegerTensorView<'a>> for HostNumericTensorView<'a> {
4183 fn from(value: HostIntegerTensorView<'a>) -> Self {
4184 Self {
4185 data: value.data.into(),
4186 shape: value.shape,
4187 storage: GpuTensorStorage::Real,
4188 }
4189 }
4190}
4191
4192impl From<HostIntegerDataOwned> for HostNumericDataOwned {
4193 fn from(value: HostIntegerDataOwned) -> Self {
4194 match value {
4195 HostIntegerDataOwned::I8(data) => Self::I8(data),
4196 HostIntegerDataOwned::I16(data) => Self::I16(data),
4197 HostIntegerDataOwned::I32(data) => Self::I32(data),
4198 HostIntegerDataOwned::I64(data) => Self::I64(data),
4199 HostIntegerDataOwned::U8(data) => Self::U8(data),
4200 HostIntegerDataOwned::U16(data) => Self::U16(data),
4201 HostIntegerDataOwned::U32(data) => Self::U32(data),
4202 HostIntegerDataOwned::U64(data) => Self::U64(data),
4203 }
4204 }
4205}
4206
4207impl From<HostIntegerTensorOwned> for HostNumericTensorOwned {
4208 fn from(value: HostIntegerTensorOwned) -> Self {
4209 Self {
4210 data: value.data.into(),
4211 shape: value.shape,
4212 storage: GpuTensorStorage::Real,
4213 }
4214 }
4215}
4216
4217#[derive(Debug)]
4219pub struct MeshgridAxisView<'a> {
4220 pub data: &'a [f64],
4221}
4222
4223#[derive(Debug, Clone)]
4225pub struct ProviderMeshgridResult {
4226 pub outputs: Vec<GpuTensorHandle>,
4227}
4228
4229#[derive(Debug, Clone, Copy)]
4231pub struct ProviderNdgridAxis<'a> {
4232 pub handle: &'a GpuTensorHandle,
4233}
4234
4235#[derive(Debug, Clone, Copy)]
4238pub struct ProviderNdgridRequest<'a> {
4239 pub axes: &'a [ProviderNdgridAxis<'a>],
4240 pub output_shape: &'a [usize],
4241 pub output_count: usize,
4242}
4243
4244#[derive(Debug, Clone)]
4246pub struct ProviderNdgridResult {
4247 pub outputs: Vec<GpuTensorHandle>,
4248}
4249
4250#[derive(Debug, Clone, Copy)]
4252pub struct ProviderBlackScholesPriceInput<'a> {
4253 pub handle: &'a GpuTensorHandle,
4254 pub shape: &'a [usize],
4256 pub strides: &'a [usize],
4258}
4259
4260#[derive(Debug, Clone, Copy)]
4263pub struct ProviderBlackScholesPriceRequest<'a> {
4264 pub inputs: &'a [ProviderBlackScholesPriceInput<'a>],
4265 pub output_shape: &'a [usize],
4266 pub len: usize,
4267}
4268
4269#[derive(Debug, Clone)]
4271pub struct ProviderBlackScholesPriceResult {
4272 pub call: GpuTensorHandle,
4273 pub put: GpuTensorHandle,
4274}
4275
4276#[derive(Debug, Clone)]
4279pub struct ProviderCovarianceToCorrelationResult {
4280 pub correlation: GpuTensorHandle,
4281 pub sigma: GpuTensorHandle,
4282}
4283
4284#[derive(Debug, Clone, Copy)]
4286pub struct ProviderAdamUpdateRequest<'a> {
4287 pub parameters: &'a GpuTensorHandle,
4288 pub gradient: &'a GpuTensorHandle,
4289 pub average_grad: Option<&'a GpuTensorHandle>,
4290 pub average_sq_grad: Option<&'a GpuTensorHandle>,
4291 pub iteration: usize,
4292 pub learn_rate: f64,
4293 pub gradient_decay_factor: f64,
4294 pub squared_gradient_decay_factor: f64,
4295 pub epsilon: f64,
4296}
4297
4298#[derive(Debug, Clone)]
4300pub struct ProviderAdamUpdateResult {
4301 pub parameters: GpuTensorHandle,
4302 pub average_grad: GpuTensorHandle,
4303 pub average_sq_grad: GpuTensorHandle,
4304}
4305
4306#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4308pub enum ProviderCrossentropyMode {
4309 SingleLabel,
4310 MultiLabel,
4311}
4312
4313#[derive(Debug, Clone, Copy)]
4315pub struct ProviderCrossentropyRequest<'a> {
4316 pub predictions: &'a GpuTensorHandle,
4317 pub targets: &'a GpuTensorHandle,
4318 pub weights: Option<&'a GpuTensorHandle>,
4319 pub mask: Option<&'a GpuTensorHandle>,
4320 pub mode: ProviderCrossentropyMode,
4321}
4322
4323#[derive(Debug, Clone)]
4325pub struct ProviderCrossentropyResult {
4326 pub losses: GpuTensorHandle,
4327}
4328
4329#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4335pub enum ScaleOp {
4336 Multiply,
4337 Divide,
4338}
4339
4340#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4341pub struct MatmulEpilogue {
4342 pub alpha: f64,
4344 pub beta: f64,
4346 pub row_scale: Option<GpuTensorHandle>,
4348 pub col_scale: Option<GpuTensorHandle>,
4350 pub row_op: ScaleOp,
4352 pub col_op: ScaleOp,
4354 #[serde(default)]
4356 pub clamp_min: Option<f64>,
4357 #[serde(default)]
4359 pub clamp_max: Option<f64>,
4360 #[serde(default)]
4362 pub pow_exponent: Option<f64>,
4363 #[serde(default)]
4365 pub diag_output: Option<GpuTensorHandle>,
4366}
4367
4368impl MatmulEpilogue {
4369 pub fn noop() -> Self {
4370 Self {
4371 alpha: 1.0,
4372 beta: 0.0,
4373 row_scale: None,
4374 col_scale: None,
4375 row_op: ScaleOp::Multiply,
4376 col_op: ScaleOp::Multiply,
4377 clamp_min: None,
4378 clamp_max: None,
4379 pow_exponent: None,
4380 diag_output: None,
4381 }
4382 }
4383 pub fn is_noop(&self) -> bool {
4384 self.alpha == 1.0
4385 && self.beta == 0.0
4386 && self.row_scale.is_none()
4387 && self.col_scale.is_none()
4388 && self.clamp_min.is_none()
4389 && self.clamp_max.is_none()
4390 && self.pow_exponent.is_none()
4391 && self.diag_output.is_none()
4392 }
4393}
4394
4395#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
4396pub struct PowerStepEpilogue {
4397 pub epsilon: f64,
4398}
4399
4400impl Default for PowerStepEpilogue {
4401 fn default() -> Self {
4402 Self { epsilon: 0.0 }
4403 }
4404}
4405
4406#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4407pub struct ImageNormalizeDescriptor {
4408 pub batch: usize,
4409 pub height: usize,
4410 pub width: usize,
4411 pub epsilon: f64,
4412 #[serde(default)]
4413 pub gain: Option<f64>,
4414 #[serde(default)]
4415 pub bias: Option<f64>,
4416 #[serde(default)]
4417 pub gamma: Option<f64>,
4418 #[serde(default = "default_image_normalize_clamp_zero")]
4419 pub clamp_zero: bool,
4420}
4421
4422fn default_image_normalize_clamp_zero() -> bool {
4423 true
4424}
4425
4426#[cfg(test)]
4427mod tests {
4428 use super::*;
4429
4430 struct TestProvider {
4431 device_id: u32,
4432 name: &'static str,
4433 spawn_concurrency: SpawnHandleConcurrency,
4434 }
4435
4436 struct DefaultNumericAdapterProvider;
4437
4438 impl AccelProvider for DefaultNumericAdapterProvider {
4439 fn upload(&self, host: &HostTensorView) -> anyhow::Result<GpuTensorHandle> {
4440 assert_eq!(host.data, &[1.0, 2.0]);
4441 Ok(GpuTensorHandle {
4442 shape: host.shape.to_vec(),
4443 device_id: 404,
4444 buffer_id: 1,
4445 descriptor: GpuTensorDescriptor::numeric(
4446 NumericElementType::F64,
4447 GpuTensorStorage::Real,
4448 ),
4449 })
4450 }
4451
4452 fn download<'a>(&'a self, h: &'a GpuTensorHandle) -> AccelDownloadFuture<'a> {
4453 Box::pin(async move {
4454 Ok(HostTensorOwned {
4455 data: vec![1.0, 2.0],
4456 shape: h.shape.clone(),
4457 storage: GpuTensorStorage::Real,
4458 })
4459 })
4460 }
4461
4462 fn upload_integer(&self, host: &HostIntegerTensorView) -> anyhow::Result<GpuTensorHandle> {
4463 assert!(matches!(host.data, HostIntegerDataView::U64([1, u64::MAX])));
4464 Ok(GpuTensorHandle {
4465 shape: host.shape.to_vec(),
4466 device_id: 404,
4467 buffer_id: 2,
4468 descriptor: GpuTensorDescriptor::numeric(
4469 NumericElementType::U64,
4470 GpuTensorStorage::Real,
4471 ),
4472 })
4473 }
4474
4475 fn download_integer<'a>(
4476 &'a self,
4477 h: &'a GpuTensorHandle,
4478 ) -> AccelIntegerDownloadFuture<'a> {
4479 Box::pin(async move {
4480 Ok(HostIntegerTensorOwned {
4481 data: HostIntegerDataOwned::U64(vec![1, u64::MAX]),
4482 shape: h.shape.clone(),
4483 })
4484 })
4485 }
4486
4487 fn free(&self, _h: &GpuTensorHandle) -> anyhow::Result<()> {
4488 Ok(())
4489 }
4490
4491 fn device_info(&self) -> String {
4492 "legacy-numeric-provider".to_string()
4493 }
4494 }
4495
4496 impl AccelProvider for TestProvider {
4497 fn upload(&self, _host: &HostTensorView) -> anyhow::Result<GpuTensorHandle> {
4498 Err(anyhow!("test provider upload should not be called"))
4499 }
4500
4501 fn download<'a>(&'a self, _h: &'a GpuTensorHandle) -> AccelDownloadFuture<'a> {
4502 unsupported_future("test provider download should not be called")
4503 }
4504
4505 fn free(&self, _h: &GpuTensorHandle) -> anyhow::Result<()> {
4506 Err(anyhow!("test provider free should not be called"))
4507 }
4508
4509 fn device_info(&self) -> String {
4510 self.name.to_string()
4511 }
4512
4513 fn device_id(&self) -> u32 {
4514 self.device_id
4515 }
4516
4517 fn spawn_handle_concurrency(&self) -> SpawnHandleConcurrency {
4518 self.spawn_concurrency
4519 }
4520 }
4521
4522 static PROVIDER_TEST_LOCK: Lazy<std::sync::Mutex<()>> = Lazy::new(|| std::sync::Mutex::new(()));
4523 static PROVIDER_A: TestProvider = TestProvider {
4524 device_id: 101,
4525 name: "provider-a",
4526 spawn_concurrency: SpawnHandleConcurrency::ImmutableShare,
4527 };
4528 static PROVIDER_B: TestProvider = TestProvider {
4529 device_id: 202,
4530 name: "provider-b",
4531 spawn_concurrency: SpawnHandleConcurrency::Reject,
4532 };
4533 static PROVIDER_C: TestProvider = TestProvider {
4534 device_id: 303,
4535 name: "provider-c",
4536 spawn_concurrency: SpawnHandleConcurrency::CopyOnWrite,
4537 };
4538
4539 fn resolve_ready<F: Future>(future: F) -> F::Output {
4540 struct NoopWake;
4541 impl std::task::Wake for NoopWake {
4542 fn wake(self: std::sync::Arc<Self>) {}
4543 }
4544 let waker = std::task::Waker::from(std::sync::Arc::new(NoopWake));
4545 let mut context = std::task::Context::from_waker(&waker);
4546 let mut future = std::pin::pin!(future);
4547 match future.as_mut().poll(&mut context) {
4548 std::task::Poll::Ready(output) => output,
4549 std::task::Poll::Pending => panic!("test adapter future unexpectedly pending"),
4550 }
4551 }
4552
4553 fn register_test_providers() {
4554 clear_provider();
4555 unsafe {
4556 register_provider(&PROVIDER_A);
4557 register_provider(&PROVIDER_B);
4558 }
4559 }
4560
4561 fn test_handle(device_id: u32) -> GpuTensorHandle {
4562 GpuTensorHandle::new(vec![1], device_id, 42)
4563 }
4564
4565 #[test]
4566 fn numeric_transfer_contract_is_exhaustive_and_native() {
4567 let f64_values = [1.0_f64, 2.0];
4568 let f32_values = [1.0_f32, 2.0];
4569 let i8_values = [1_i8, 2];
4570 let i16_values = [1_i16, 2];
4571 let i32_values = [1_i32, 2];
4572 let i64_values = [1_i64, 9_007_199_254_740_993];
4573 let u8_values = [1_u8, 2];
4574 let u16_values = [1_u16, 2];
4575 let u32_values = [1_u32, 2];
4576 let u64_values = [1_u64, 9_007_199_254_740_993];
4577 let cases = [
4578 (
4579 HostNumericDataView::F64(&f64_values),
4580 NumericElementType::F64,
4581 ),
4582 (
4583 HostNumericDataView::F32(&f32_values),
4584 NumericElementType::F32,
4585 ),
4586 (HostNumericDataView::I8(&i8_values), NumericElementType::I8),
4587 (
4588 HostNumericDataView::I16(&i16_values),
4589 NumericElementType::I16,
4590 ),
4591 (
4592 HostNumericDataView::I32(&i32_values),
4593 NumericElementType::I32,
4594 ),
4595 (
4596 HostNumericDataView::I64(&i64_values),
4597 NumericElementType::I64,
4598 ),
4599 (HostNumericDataView::U8(&u8_values), NumericElementType::U8),
4600 (
4601 HostNumericDataView::U16(&u16_values),
4602 NumericElementType::U16,
4603 ),
4604 (
4605 HostNumericDataView::U32(&u32_values),
4606 NumericElementType::U32,
4607 ),
4608 (
4609 HostNumericDataView::U64(&u64_values),
4610 NumericElementType::U64,
4611 ),
4612 ];
4613
4614 for (data, element_type) in cases {
4615 let transfer = HostNumericTensorView {
4616 data,
4617 shape: &[1, 2],
4618 storage: GpuTensorStorage::Real,
4619 };
4620 transfer.validate().expect("matching native transfer");
4621 assert_eq!(transfer.element_type(), element_type);
4622 assert_eq!(element_type.class_name(), data.element_type().class_name());
4623 assert_eq!(
4624 element_type.element_size(),
4625 data.element_type().element_size()
4626 );
4627 }
4628
4629 assert_eq!(
4630 NumericElementType::F64.precision(),
4631 Some(ProviderPrecision::F64)
4632 );
4633 assert_eq!(
4634 NumericElementType::F32.precision(),
4635 Some(ProviderPrecision::F32)
4636 );
4637 assert_eq!(
4638 NumericElementType::U64.integer_type(),
4639 Some(IntegerElementType::U64)
4640 );
4641 assert_eq!(
4642 NumericElementType::I64.integer_type(),
4643 Some(IntegerElementType::I64)
4644 );
4645 assert_eq!(NumericElementType::F64.integer_type(), None);
4646 }
4647
4648 #[test]
4649 fn numeric_transfer_contract_validates_real_and_complex_lengths() {
4650 let real = HostNumericTensorOwned {
4651 data: HostNumericDataOwned::F32(vec![1.0, 2.0]),
4652 shape: vec![2, 1],
4653 storage: GpuTensorStorage::Real,
4654 };
4655 real.validate().expect("native single real transfer");
4656
4657 let complex = HostNumericTensorOwned {
4658 data: HostNumericDataOwned::U64(vec![1, u64::MAX, 2, u64::MAX - 1]),
4659 shape: vec![1, 2],
4660 storage: GpuTensorStorage::ComplexInterleaved,
4661 };
4662 complex.validate().expect("native complex integer transfer");
4663
4664 let mismatch = HostNumericTensorView {
4665 data: HostNumericDataView::I16(&[1, 2, 3]),
4666 shape: &[2, 2],
4667 storage: GpuTensorStorage::Real,
4668 };
4669 let error = mismatch.validate().expect_err("shape mismatch must reject");
4670 assert!(error.to_string().contains("expected 4"));
4671
4672 let overflow = HostNumericTensorView {
4673 data: HostNumericDataView::F64(&[]),
4674 shape: &[usize::MAX, 2],
4675 storage: GpuTensorStorage::Real,
4676 };
4677 assert!(overflow.validate().is_err());
4678 }
4679
4680 #[test]
4681 fn gpu_handle_descriptor_survives_clone_serialization_and_semantic_metadata_cleanup() {
4682 let handle = GpuTensorHandle::new(vec![2, 3], 17, 29)
4683 .with_numeric_descriptor(
4684 NumericElementType::U64,
4685 GpuTensorStorage::ComplexInterleaved,
4686 )
4687 .with_provenance(GpuHandleProvenance::Explicit);
4688 let cloned = handle.clone();
4689 clear_handle_metadata(&handle);
4690 assert_eq!(handle_integer_type(&cloned), Some(IntegerElementType::U64));
4691 assert_eq!(handle_precision(&cloned), None);
4692 assert_eq!(
4693 handle_storage(&cloned),
4694 GpuTensorStorage::ComplexInterleaved
4695 );
4696 assert_eq!(
4697 handle_provenance(&cloned),
4698 Some(GpuHandleProvenance::Explicit)
4699 );
4700 assert_eq!(handle_class_name(&cloned).as_deref(), Some("uint64"));
4701
4702 let encoded = serde_json::to_string(&cloned).expect("serialize durable handle");
4703 let decoded: GpuTensorHandle =
4704 serde_json::from_str(&encoded).expect("deserialize durable handle");
4705 assert_eq!(decoded, cloned);
4706
4707 let legacy: GpuTensorHandle =
4708 serde_json::from_str(r#"{"shape":[1,1],"device_id":5,"buffer_id":8}"#)
4709 .expect("deserialize legacy handle");
4710 assert_eq!(legacy.descriptor, GpuTensorDescriptor::default());
4711 assert_eq!(handle_storage(&legacy), GpuTensorStorage::Real);
4712 }
4713
4714 #[test]
4715 fn durable_physical_descriptor_is_the_only_numeric_authority() {
4716 let floating = GpuTensorHandle::new(vec![1, 2], 23, 31).with_numeric_descriptor(
4717 NumericElementType::F32,
4718 GpuTensorStorage::ComplexInterleaved,
4719 );
4720
4721 assert_eq!(handle_precision(&floating), Some(ProviderPrecision::F32));
4722 assert_eq!(handle_integer_type(&floating), None);
4723 assert_eq!(
4724 handle_storage(&floating),
4725 GpuTensorStorage::ComplexInterleaved
4726 );
4727 assert_eq!(handle_class_name(&floating).as_deref(), Some("single"));
4728 clear_handle_metadata(&floating);
4729
4730 let integer = GpuTensorHandle::new(vec![1, 2], 37, 41)
4731 .with_numeric_descriptor(NumericElementType::U64, GpuTensorStorage::Real);
4732
4733 assert_eq!(handle_precision(&integer), None);
4734 assert_eq!(handle_integer_type(&integer), Some(IntegerElementType::U64));
4735 assert_eq!(handle_class_name(&integer).as_deref(), Some("uint64"));
4736 clear_handle_metadata(&integer);
4737 }
4738
4739 #[test]
4740 fn removed_physical_side_metadata_surface_stays_absent() {
4741 let source = include_str!("lib.rs");
4742 let forbidden = [
4743 ["set_handle_", "precision"].concat(),
4744 ["set_handle_", "integer_type"].concat(),
4745 ["set_handle_", "storage"].concat(),
4746 ["clear_handle_", "precision"].concat(),
4747 ["clear_handle_", "integer_type"].concat(),
4748 ["clear_handle_", "storage"].concat(),
4749 ["HANDLE_", "PRECISIONS"].concat(),
4750 ["HANDLE_", "INTEGER_TYPES"].concat(),
4751 ["HANDLE_", "STORAGES"].concat(),
4752 ];
4753 for symbol in forbidden {
4754 assert!(
4755 !source.contains(&symbol),
4756 "removed symbol returned: {symbol}"
4757 );
4758 }
4759 }
4760
4761 #[test]
4762 fn numeric_transfer_default_adapter_preserves_double_and_integer_storage() {
4763 let provider = DefaultNumericAdapterProvider;
4764 let double = HostNumericTensorView {
4765 data: HostNumericDataView::F64(&[1.0, 2.0]),
4766 shape: &[1, 2],
4767 storage: GpuTensorStorage::Real,
4768 };
4769 let double_handle = provider
4770 .upload_numeric(&double)
4771 .expect("double upload adapter");
4772 let double_download = resolve_ready(provider.download_numeric(&double_handle))
4773 .expect("double download adapter");
4774 assert_eq!(
4775 double_download.data,
4776 HostNumericDataOwned::F64(vec![1.0, 2.0])
4777 );
4778
4779 let integer = HostNumericTensorView {
4780 data: HostNumericDataView::U64(&[1, u64::MAX]),
4781 shape: &[1, 2],
4782 storage: GpuTensorStorage::Real,
4783 };
4784 let integer_handle = provider
4785 .upload_numeric(&integer)
4786 .expect("integer upload adapter");
4787 let integer_download = resolve_ready(provider.download_numeric(&integer_handle))
4788 .expect("integer download adapter");
4789 assert_eq!(
4790 integer_download.data,
4791 HostNumericDataOwned::U64(vec![1, u64::MAX])
4792 );
4793 clear_handle_metadata(&integer_handle);
4794 }
4795
4796 #[test]
4797 fn numeric_transfer_default_adapter_rejects_widened_single_and_complex_integer() {
4798 let provider = DefaultNumericAdapterProvider;
4799 let single = HostNumericTensorView {
4800 data: HostNumericDataView::F32(&[1.0, 2.0]),
4801 shape: &[1, 2],
4802 storage: GpuTensorStorage::Real,
4803 };
4804 assert!(provider.upload_numeric(&single).is_err());
4805
4806 let complex_integer = HostNumericTensorView {
4807 data: HostNumericDataView::I16(&[1, 2, 3, 4]),
4808 shape: &[1, 2],
4809 storage: GpuTensorStorage::ComplexInterleaved,
4810 };
4811 assert!(provider.upload_numeric(&complex_integer).is_err());
4812
4813 let single_handle = GpuTensorHandle {
4814 shape: vec![1, 2],
4815 device_id: 404,
4816 buffer_id: 3,
4817 descriptor: GpuTensorDescriptor::numeric(
4818 NumericElementType::F32,
4819 GpuTensorStorage::Real,
4820 ),
4821 };
4822 let error = resolve_ready(provider.download_numeric(&single_handle))
4823 .expect_err("widened single download must reject");
4824 assert!(error.to_string().contains("native single"));
4825 clear_handle_metadata(&single_handle);
4826
4827 let complex_integer_handle = GpuTensorHandle {
4828 shape: vec![1, 2],
4829 device_id: 404,
4830 buffer_id: 4,
4831 descriptor: GpuTensorDescriptor::numeric(
4832 NumericElementType::I16,
4833 GpuTensorStorage::ComplexInterleaved,
4834 ),
4835 };
4836 let error = resolve_ready(provider.download_numeric(&complex_integer_handle))
4837 .expect_err("complex integer default download must reject");
4838 assert!(error.to_string().contains("complex integer"));
4839 clear_handle_metadata(&complex_integer_handle);
4840
4841 let descriptorless = GpuTensorHandle::new(vec![1, 2], 404, 5);
4842 let error = resolve_ready(provider.download_numeric(&descriptorless))
4843 .expect_err("descriptorless numeric download must reject");
4844 assert!(error.to_string().contains("durable element descriptor"));
4845 }
4846
4847 #[test]
4848 fn conservative_feasibility_rejects_operations_without_execution() {
4849 let query = ProviderFeasibilityQuery {
4850 operation: ProviderOperationIdentity::new("legacy.elementwise"),
4851 family: ProviderOperationFamily::Elementwise,
4852 inputs: vec![ProviderRepresentation {
4853 element_type: ProviderElementType::F64,
4854 storage: ProviderStorage::DenseReal,
4855 layout: ProviderLayout::ColumnMajorContiguous,
4856 shape: vec![4, 4],
4857 residency: ProviderResidency::Host,
4858 }],
4859 outputs: Vec::new(),
4860 workload: ProviderWorkload {
4861 elements: Some(16),
4862 ..ProviderWorkload::default()
4863 },
4864 };
4865
4866 assert!(matches!(
4867 PROVIDER_A.query_feasibility(&query),
4868 ProviderFeasibility::Rejected {
4869 rejection: ProviderRejection {
4870 code: ProviderRejectionCode::UnsupportedOperation,
4871 ..
4872 }
4873 }
4874 ));
4875 assert!(!PROVIDER_A
4876 .capability_snapshot()
4877 .supports(ProviderOperationFamily::Elementwise));
4878
4879 let snapshot = PROVIDER_A.capability_snapshot();
4880 let encoded = serde_json::to_string(&snapshot).expect("serialize capability snapshot");
4881 let decoded: ProviderCapabilitySnapshot =
4882 serde_json::from_str(&encoded).expect("deserialize capability snapshot");
4883 assert_eq!(decoded, snapshot);
4884 }
4885
4886 #[test]
4887 fn semantic_handle_metadata_is_namespaced_by_device_and_buffer() {
4888 let mut first = test_handle(PROVIDER_A.device_id())
4889 .with_numeric_descriptor(NumericElementType::F32, GpuTensorStorage::Real);
4890 let mut second = test_handle(PROVIDER_B.device_id()).with_numeric_descriptor(
4891 NumericElementType::U64,
4892 GpuTensorStorage::ComplexInterleaved,
4893 );
4894 clear_handle_metadata(&first);
4895 clear_handle_metadata(&second);
4896
4897 set_handle_class_name(&first, "single");
4898 set_handle_class_name(&second, "uint64");
4899 set_handle_logical(&first, true);
4900 record_handle_transpose(&first, 2, 3);
4901 mark_handle_automatic(&mut first);
4902 mark_handle_explicit(&mut second);
4903
4904 assert_eq!(handle_precision(&first), Some(ProviderPrecision::F32));
4905 assert_eq!(handle_precision(&second), None);
4906 assert_eq!(handle_class_name(&first).as_deref(), Some("logical"));
4907 assert_eq!(handle_class_name(&second).as_deref(), Some("uint64"));
4908 assert_eq!(handle_integer_type(&first), None);
4909 assert_eq!(handle_integer_type(&second), Some(IntegerElementType::U64));
4910 assert!(handle_is_logical(&first));
4911 assert!(!handle_is_logical(&second));
4912 assert_eq!(
4913 handle_transpose_info(&first).map(|info| (info.base_rows, info.base_cols)),
4914 Some((2, 3))
4915 );
4916 assert_eq!(handle_transpose_info(&second), None);
4917 assert_eq!(handle_storage(&first), GpuTensorStorage::Real);
4918 assert_eq!(
4919 handle_storage(&second),
4920 GpuTensorStorage::ComplexInterleaved
4921 );
4922 assert_eq!(
4923 handle_provenance(&first),
4924 Some(GpuHandleProvenance::Automatic)
4925 );
4926 assert!(handle_is_explicit(&second));
4927
4928 clear_handle_metadata(&first);
4929 assert_eq!(handle_precision(&first), Some(ProviderPrecision::F32));
4930 assert_eq!(handle_class_name(&first).as_deref(), Some("single"));
4931 assert!(!handle_is_logical(&first));
4932 assert_eq!(handle_transpose_info(&first), None);
4933 assert_eq!(
4934 handle_provenance(&first),
4935 Some(GpuHandleProvenance::Automatic)
4936 );
4937 assert_eq!(handle_precision(&second), None);
4938 assert_eq!(handle_integer_type(&second), Some(IntegerElementType::U64));
4939 assert!(handle_is_explicit(&second));
4940 clear_handle_metadata(&second);
4941 }
4942
4943 fn spectral_request<'a>(
4944 input: &'a GpuTensorHandle,
4945 frame_mode: ProviderSpectralFrameMode,
4946 ) -> ProviderSpectralRequest<'a> {
4947 static WINDOW: [f64; 4] = [1.0, 1.0, 1.0, 1.0];
4948 ProviderSpectralRequest {
4949 input,
4950 input_len: 16,
4951 input_complex: false,
4952 window: &WINDOW,
4953 nfft: 8,
4954 frame_count: 3,
4955 frame_mode,
4956 range: ProviderSpectralRange::Onesided,
4957 denominator: 1.0,
4958 }
4959 }
4960
4961 #[test]
4962 fn provider_envelope_shape_guard_rejects_equal_len_layout_spoofing() {
4963 assert!(provider_envelope_input_shape_matches(&[2, 3], 2, 3));
4964 assert!(provider_envelope_input_shape_matches(&[6, 1], 6, 1));
4965 assert!(provider_envelope_input_shape_matches(&[1, 6], 6, 1));
4966 assert!(provider_envelope_input_shape_matches(&[6], 6, 1));
4967
4968 assert!(!provider_envelope_input_shape_matches(&[3, 2], 2, 3));
4969 assert!(!provider_envelope_input_shape_matches(&[6], 2, 3));
4970 assert!(!provider_envelope_input_shape_matches(&[2, 1, 3], 2, 3));
4971 }
4972
4973 #[test]
4974 fn provider_for_device_prefers_registered_device_over_thread_provider() {
4975 let _lock = PROVIDER_TEST_LOCK
4976 .lock()
4977 .expect("provider test lock poisoned");
4978 register_test_providers();
4979 let _thread_provider = ThreadProviderGuard::set(Some(&PROVIDER_B));
4980
4981 let provider = provider_for_device(PROVIDER_A.device_id()).expect("provider for device");
4982
4983 assert_eq!(provider.device_info(), PROVIDER_A.name);
4984 clear_provider();
4985 }
4986
4987 #[test]
4988 fn provider_for_handle_uses_handle_device_owner() {
4989 let _lock = PROVIDER_TEST_LOCK
4990 .lock()
4991 .expect("provider test lock poisoned");
4992 register_test_providers();
4993 let _thread_provider = ThreadProviderGuard::set(Some(&PROVIDER_B));
4994
4995 let provider =
4996 provider_for_handle(&test_handle(PROVIDER_A.device_id())).expect("provider for handle");
4997
4998 assert_eq!(provider.device_info(), PROVIDER_A.name);
4999 clear_provider();
5000 }
5001
5002 #[test]
5003 fn clearing_active_provider_preserves_live_handle_owner() {
5004 let _lock = PROVIDER_TEST_LOCK
5005 .lock()
5006 .expect("provider test lock poisoned");
5007 register_test_providers();
5008 let handle = test_handle(PROVIDER_A.device_id());
5009
5010 clear_provider();
5011
5012 assert!(provider().is_none());
5013 let owner = provider_for_handle(&handle).expect("registered handle owner survives clear");
5014 assert_eq!(owner.device_info(), PROVIDER_A.name);
5015 }
5016
5017 #[test]
5018 fn concurrent_registration_keeps_each_threads_active_provider() {
5019 let _lock = PROVIDER_TEST_LOCK
5020 .lock()
5021 .expect("provider test lock poisoned");
5022 clear_provider();
5023
5024 let first = std::thread::spawn(|| {
5025 unsafe { register_provider(&PROVIDER_A) };
5026 std::thread::yield_now();
5027 provider().map(AccelProvider::device_info)
5028 });
5029 let second = std::thread::spawn(|| {
5030 unsafe { register_provider(&PROVIDER_B) };
5031 std::thread::yield_now();
5032 provider().map(AccelProvider::device_info)
5033 });
5034
5035 assert_eq!(
5036 first.join().expect("first registration thread"),
5037 Some(PROVIDER_A.name.to_string())
5038 );
5039 assert_eq!(
5040 second.join().expect("second registration thread"),
5041 Some(PROVIDER_B.name.to_string())
5042 );
5043 clear_provider();
5044 }
5045
5046 #[test]
5047 fn spawn_handle_concurrency_for_uses_registered_owner() {
5048 let _lock = PROVIDER_TEST_LOCK
5049 .lock()
5050 .expect("provider test lock poisoned");
5051 register_test_providers();
5052 let _thread_provider = ThreadProviderGuard::set(Some(&PROVIDER_B));
5053
5054 let concurrency = spawn_handle_concurrency_for(&test_handle(PROVIDER_A.device_id()))
5055 .expect("spawn concurrency");
5056
5057 assert_eq!(concurrency, PROVIDER_A.spawn_concurrency);
5058 clear_provider();
5059 }
5060
5061 #[test]
5062 fn provider_keeps_thread_local_active_provider_semantics() {
5063 let _lock = PROVIDER_TEST_LOCK
5064 .lock()
5065 .expect("provider test lock poisoned");
5066 register_test_providers();
5067 let _thread_provider = ThreadProviderGuard::set(Some(&PROVIDER_A));
5068
5069 let active = provider().expect("active provider");
5070
5071 assert_eq!(active.device_info(), PROVIDER_A.name);
5072 clear_provider();
5073 }
5074
5075 #[test]
5076 fn unregistered_thread_provider_only_matches_own_device_before_global_fallback() {
5077 let _lock = PROVIDER_TEST_LOCK
5078 .lock()
5079 .expect("provider test lock poisoned");
5080 clear_provider();
5081 unsafe {
5082 register_provider(&PROVIDER_A);
5083 }
5084 let _thread_provider = ThreadProviderGuard::set(Some(&PROVIDER_C));
5085
5086 let own_device = provider_for_device(PROVIDER_C.device_id()).expect("own provider");
5087 let fallback = provider_for_device(404).expect("global fallback provider");
5088
5089 assert_eq!(own_device.device_info(), PROVIDER_C.name);
5090 assert_eq!(fallback.device_info(), PROVIDER_A.name);
5091 clear_provider();
5092 }
5093
5094 #[test]
5095 fn uniform_spectral_request_validates_sliding_input_coverage() {
5096 let input = test_handle(PROVIDER_A.device_id());
5097 let mut request = spectral_request(&input, ProviderSpectralFrameMode::Sliding { hop: 6 });
5098 assert!(validate_uniform_spectral_request(&request).is_ok());
5099
5100 request.input_len = 15;
5101 assert!(validate_uniform_spectral_request(&request).is_err());
5102 }
5103
5104 #[test]
5105 fn uniform_spectral_request_rejects_sliding_coverage_overflow() {
5106 let input = test_handle(PROVIDER_A.device_id());
5107 let mut request = spectral_request(&input, ProviderSpectralFrameMode::Sliding { hop: 2 });
5108 request.frame_count = usize::MAX;
5109
5110 assert!(validate_uniform_spectral_request(&request).is_err());
5111 }
5112
5113 #[test]
5114 fn uniform_spectral_request_validates_folded_input_coverage() {
5115 let input = test_handle(PROVIDER_A.device_id());
5116 let mut request = spectral_request(
5117 &input,
5118 ProviderSpectralFrameMode::FoldedColumns { input_rows: 5 },
5119 );
5120 assert!(validate_uniform_spectral_request(&request).is_ok());
5121
5122 request.frame_mode = ProviderSpectralFrameMode::FoldedColumns { input_rows: 0 };
5123 assert!(validate_uniform_spectral_request(&request).is_err());
5124
5125 request.frame_mode = ProviderSpectralFrameMode::FoldedColumns { input_rows: 6 };
5126 assert!(validate_uniform_spectral_request(&request).is_err());
5127 }
5128
5129 #[test]
5130 fn uniform_spectral_request_rejects_folded_coverage_overflow() {
5131 let input = test_handle(PROVIDER_A.device_id());
5132 let request = spectral_request(
5133 &input,
5134 ProviderSpectralFrameMode::FoldedColumns {
5135 input_rows: usize::MAX,
5136 },
5137 );
5138
5139 assert!(validate_uniform_spectral_request(&request).is_err());
5140 }
5141
5142 #[test]
5143 fn image_normalize_descriptor_omitted_clamp_zero_defaults_true() {
5144 let payload = r#"{
5145 "batch": 2,
5146 "height": 4,
5147 "width": 5,
5148 "epsilon": 0.000001
5149 }"#;
5150
5151 let desc: ImageNormalizeDescriptor =
5152 serde_json::from_str(payload).expect("deserialize descriptor");
5153
5154 assert!(
5155 desc.clamp_zero,
5156 "legacy serialized descriptors should default to clamped image normalize"
5157 );
5158 }
5159
5160 #[test]
5161 fn image_normalize_descriptor_explicit_false_preserves_unclamped() {
5162 let payload = r#"{
5163 "batch": 2,
5164 "height": 4,
5165 "width": 5,
5166 "epsilon": 0.000001,
5167 "clamp_zero": false
5168 }"#;
5169
5170 let desc: ImageNormalizeDescriptor =
5171 serde_json::from_str(payload).expect("deserialize descriptor");
5172
5173 assert!(
5174 !desc.clamp_zero,
5175 "explicit clamp_zero=false should preserve unclamped semantics"
5176 );
5177 }
5178}