1use std::any::{Any, TypeId};
29use std::collections::HashMap;
30use std::fmt::{Debug, Display};
31use std::marker::PhantomData;
32use std::sync::{Arc, LazyLock, RwLock};
33use std::time::{Duration, Instant};
34
35use crate::error::{CoreError, CoreResult, ErrorContext};
36
37mod distributed_impl;
39mod gpu_impl;
40#[cfg(feature = "array_protocol_wgpu")]
41pub mod gpu_ndarray;
42#[cfg(feature = "array_protocol_wgpu")]
43mod gpu_ndarray_shaders;
44mod jit_impl;
45mod operations;
46
47pub use crate::array_function_dispatch;
49
50pub mod auto_device;
52pub mod distributed_training;
53pub mod grad;
54pub mod mixed_precision;
55pub mod ml_ops;
56pub mod neural;
57#[cfg(feature = "serialization")]
58pub mod serialization;
59pub mod training;
60
61pub trait ArrayProtocol: Any + Send + Sync {
65 fn array_function(
79 &self,
80 func: &ArrayFunction,
81 types: &[TypeId],
82 args: &[Box<dyn Any>],
83 kwargs: &HashMap<String, Box<dyn Any>>,
84 ) -> Result<Box<dyn Any>, NotImplemented>;
85
86 #[must_use]
88 fn as_any(&self) -> &dyn Any;
89
90 #[must_use]
92 fn shape(&self) -> &[usize] {
93 &[]
94 }
95
96 #[must_use]
98 fn dtype(&self) -> TypeId {
99 TypeId::of::<f64>()
100 }
101
102 #[must_use]
121 fn supports_op(&self, _opname: &str) -> bool {
122 true
123 }
124
125 #[must_use]
127 fn box_clone(&self) -> Box<dyn ArrayProtocol>;
128}
129
130impl Clone for Box<dyn ArrayProtocol> {
132 fn clone(&self) -> Self {
133 self.box_clone()
134 }
135}
136
137#[derive(Debug, Clone, Copy)]
147pub struct NotImplemented;
148
149impl Display for NotImplemented {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 write!(f, "NotImplemented")
152 }
153}
154
155pub type ArrayFunctionImpl = dyn Fn(&[Box<dyn Any>], &HashMap<String, Box<dyn Any>>) -> CoreResult<Box<dyn Any>>
157 + Send
158 + Sync;
159
160#[derive(Clone)]
162pub struct ArrayFunction {
163 pub name: &'static str,
165
166 pub implementation: Arc<ArrayFunctionImpl>,
168}
169
170impl Debug for ArrayFunction {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 f.debug_struct("ArrayFunction")
173 .field("name", &self.name)
174 .finish_non_exhaustive()
175 }
176}
177
178impl PartialEq for ArrayFunction {
179 fn eq(&self, other: &Self) -> bool {
180 self.name == other.name
181 }
182}
183
184impl Eq for ArrayFunction {}
185
186impl std::hash::Hash for ArrayFunction {
187 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
188 self.name.hash(state);
189 }
190}
191
192impl ArrayFunction {
193 #[must_use]
195 pub fn new(name: &'static str) -> Self {
196 Self {
197 name,
198 implementation: Arc::new(|_args, _kwargs| {
200 Err(CoreError::NotImplementedError(ErrorContext::new(
201 "Function not implemented".to_string(),
202 )))
203 }),
204 }
205 }
206}
207
208#[derive(Debug, Clone)]
210pub struct DispatchCacheEntry {
211 #[allow(dead_code)]
213 type_signature: Vec<TypeId>,
214 #[allow(dead_code)]
216 preferred_impl_type: TypeId,
217 timestamp: Instant,
219 hit_count: u64,
221}
222
223#[derive(Debug)]
225pub struct ArrayFunctionRegistry {
226 functions: HashMap<&'static str, ArrayFunction>,
228 dispatch_cache: HashMap<(&'static str, Vec<TypeId>), DispatchCacheEntry>,
230 max_cache_size: usize,
232 cache_ttl: Duration,
234}
235
236impl Default for ArrayFunctionRegistry {
237 fn default() -> Self {
238 Self {
239 functions: HashMap::new(),
240 dispatch_cache: HashMap::new(),
241 max_cache_size: 1000, cache_ttl: Duration::from_secs(300), }
244 }
245}
246
247impl ArrayFunctionRegistry {
248 #[must_use]
250 pub fn global() -> &'static RwLock<Self> {
251 static REGISTRY: LazyLock<RwLock<ArrayFunctionRegistry>> =
252 LazyLock::new(|| RwLock::new(ArrayFunctionRegistry::default()));
253 ®ISTRY
254 }
255
256 pub fn register(&mut self, func: ArrayFunction) {
258 self.functions.insert(func.name, func);
259 }
260
261 #[must_use]
263 #[allow(dead_code)]
264 pub fn get(&self, name: &str) -> Option<&ArrayFunction> {
265 self.functions.get(name)
266 }
267
268 #[must_use]
270 pub fn all_functions(&self) -> Vec<&ArrayFunction> {
271 self.functions.values().collect()
272 }
273
274 #[must_use]
276 pub fn get_cached_dispatch(
277 &self,
278 funcname: &'static str,
279 types: &[TypeId],
280 ) -> Option<&DispatchCacheEntry> {
281 let key = (funcname, types.to_vec());
282 if let Some(entry) = self.dispatch_cache.get(&key) {
283 if entry.timestamp.elapsed() < self.cache_ttl {
285 return Some(entry);
286 }
287 }
288 None
289 }
290
291 pub fn cache_dispatch(
293 &mut self,
294 funcname: &'static str,
295 types: Vec<TypeId>,
296 impl_type: TypeId,
297 ) {
298 if self.dispatch_cache.len() >= self.max_cache_size {
300 self.cleanup_cache();
301 }
302
303 let key = (funcname, types.clone());
304 let entry = DispatchCacheEntry {
305 type_signature: types,
306 preferred_impl_type: impl_type,
307 timestamp: Instant::now(),
308 hit_count: 0,
309 };
310 self.dispatch_cache.insert(key, entry);
311 }
312
313 pub fn update_cache_hit(&mut self, funcname: &'static str, types: &[TypeId]) {
315 let key = (funcname, types.to_vec());
316 if let Some(entry) = self.dispatch_cache.get_mut(&key) {
317 entry.hit_count += 1;
318 }
319 }
320
321 fn cleanup_cache(&mut self) {
323 let now = Instant::now();
324 self.dispatch_cache
325 .retain(|_, entry| now.duration_since(entry.timestamp) < self.cache_ttl);
326
327 if self.dispatch_cache.len() >= self.max_cache_size {
329 let mut entries: Vec<_> = self
330 .dispatch_cache
331 .iter()
332 .map(|(k, v)| (k.clone(), v.hit_count))
333 .collect();
334 entries.sort_by_key(|(_, hit_count)| *hit_count);
335
336 let to_remove = self.dispatch_cache.len() / 4;
338 let keys_to_remove: Vec<_> = entries
339 .iter()
340 .take(to_remove)
341 .map(|(key, _)| key.clone())
342 .collect();
343 for key in keys_to_remove {
344 self.dispatch_cache.remove(&key);
345 }
346 }
347 }
348
349 #[must_use]
351 pub fn cache_stats(&self) -> HashMap<String, u64> {
352 let mut stats = HashMap::new();
353 stats.insert("cache_size".to_string(), self.dispatch_cache.len() as u64);
354 stats.insert("max_cache_size".to_string(), self.max_cache_size as u64);
355
356 let total_hits: u64 = self.dispatch_cache.values().map(|e| e.hit_count).sum();
357 stats.insert("total_hits".to_string(), total_hits);
358
359 stats
360 }
361}
362
363pub fn get_implementing_args<'a>(
378 func_name: &str,
379 args: &'a [Box<dyn Any>],
380) -> Vec<(TypeId, &'a dyn ArrayProtocol)> {
381 if args.is_empty() {
382 return Vec::new();
383 }
384
385 let mut implementing_args = Vec::with_capacity(args.len());
387
388 for arg in args {
389 if let Some(array_protocol_obj) = arg.downcast_ref::<Box<dyn ArrayProtocol>>() {
390 if !array_protocol_obj.supports_op(func_name) {
391 continue;
392 }
393 let type_id = (**array_protocol_obj).type_id();
394 implementing_args.push((type_id, &**array_protocol_obj));
395 }
396 }
397
398 implementing_args
399}
400
401#[allow(dead_code)]
412pub fn array_function_dispatch(
413 func: &ArrayFunction,
414 args: &[Box<dyn Any>],
415 kwargs: &HashMap<String, Box<dyn Any>>,
416) -> CoreResult<Box<dyn Any>> {
417 if args.is_empty() {
419 return (func.implementation)(args, kwargs);
420 }
421
422 let implementing_args = get_implementing_args(func.name, args);
424
425 if implementing_args.is_empty() {
426 return (func.implementation)(args, kwargs);
428 }
429
430 if implementing_args.len() == 1 {
432 let (type_id, array_protocol_obj) = implementing_args[0];
433 let types = [type_id];
434 match array_protocol_obj.array_function(func, &types, args, kwargs) {
435 Ok(result) => return Ok(result),
436 Err(NotImplemented) => {
437 return Err(CoreError::DispatchError(ErrorContext::new(format!(
438 "No implementation found for {} with type {:?}",
439 func.name, type_id
440 ))));
441 }
442 }
443 }
444
445 let mut unique_types = Vec::with_capacity(implementing_args.len());
447 let mut seen_types = std::collections::HashSet::with_capacity(implementing_args.len());
448
449 for &(type_id, _) in &implementing_args {
450 if seen_types.insert(type_id) {
451 unique_types.push(type_id);
452 }
453 }
454
455 for (_, array_protocol_obj) in implementing_args {
457 if let Ok(result) = array_protocol_obj.array_function(func, &unique_types, args, kwargs) {
458 return Ok(result);
459 }
460 }
461
462 Err(CoreError::DispatchError(ErrorContext::new(format!(
464 "No implementation found for {} with {} argument types: {:?}",
465 func.name,
466 unique_types.len(),
467 unique_types
468 ))))
469}
470
471pub struct ArrayFunctionDecorator<F> {
475 function: F,
476 name: &'static str,
477}
478
479impl<F> ArrayFunctionDecorator<F>
480where
481 F: Send + Sync + 'static,
482{
483 #[must_use]
485 pub fn new(function: F, name: &'static str) -> Self {
486 Self { function, name }
487 }
488
489 pub fn register(self) -> F {
491 let implementation = Arc::new(
492 move |_args: &[Box<dyn Any>], _kwargs: &HashMap<String, Box<dyn Any>>| {
493 Err(CoreError::NotImplementedError(ErrorContext::new(
498 "ArrayFunctionDecorator: Type conversion in array_function_dispatch is not implemented yet".to_string()
499 )))
500 },
501 );
502
503 let func = ArrayFunction {
504 name: self.name,
505 implementation,
506 };
507
508 let registry = ArrayFunctionRegistry::global();
510 if let Ok(mut registry) = registry.write() {
511 registry.register(func);
512 } else {
513 eprintln!("Warning: Failed to acquire write lock on ArrayFunctionRegistry, skipping function registration");
514 }
516
517 self.function
518 }
519}
520
521pub trait GPUArray: ArrayProtocol {
523 fn to_gpu(&self) -> CoreResult<Box<dyn GPUArray>>;
525
526 fn to_cpu(&self) -> CoreResult<Box<dyn ArrayProtocol>>;
528
529 #[must_use]
531 fn is_on_gpu(&self) -> bool;
532
533 #[must_use]
535 fn device_info(&self) -> HashMap<String, String>;
536}
537
538pub trait DistributedArray: ArrayProtocol {
540 #[must_use]
542 fn distribution_info(&self) -> HashMap<String, String>;
543
544 fn gather(&self) -> CoreResult<Box<dyn ArrayProtocol>>;
546
547 fn scatter(&self, chunks: usize) -> CoreResult<Box<dyn DistributedArray>>;
549
550 #[must_use]
552 fn is_distributed(&self) -> bool;
553}
554
555pub trait JITArray: ArrayProtocol {
557 fn compile(&self, expression: &str) -> CoreResult<Box<dyn JITFunction>>;
559
560 #[must_use]
562 fn supports_jit(&self) -> bool;
563
564 #[must_use]
566 fn jit_info(&self) -> HashMap<String, String>;
567}
568
569pub trait JITFunction: Send + Sync {
571 fn evaluate(&self, args: &[Box<dyn Any>]) -> CoreResult<Box<dyn Any>>;
573
574 #[must_use]
576 fn source(&self) -> String;
577
578 #[must_use]
580 fn compile_info(&self) -> HashMap<String, String>;
581
582 #[must_use]
584 fn clone_box(&self) -> Box<dyn JITFunction>;
585}
586
587pub trait JITFunctionFactory: Send + Sync {
589 fn create_jit_function(
591 &self,
592 expression: &str,
593 array_typeid: TypeId,
594 ) -> CoreResult<Box<dyn JITFunction>>;
595
596 #[must_use]
598 fn supports_array_type(&self, array_typeid: TypeId) -> bool;
599}
600
601#[derive(Default)]
603pub struct JITFactoryRegistry {
604 factories: Vec<Box<dyn JITFunctionFactory>>,
605}
606
607impl std::fmt::Debug for JITFactoryRegistry {
608 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
609 write!(
610 f,
611 "JITFactoryRegistry {{ factories: {} }}",
612 self.factories.len()
613 )
614 }
615}
616
617impl JITFactoryRegistry {
618 #[must_use]
620 pub fn global() -> &'static RwLock<Self> {
621 static REGISTRY: LazyLock<RwLock<JITFactoryRegistry>> = LazyLock::new(|| {
622 RwLock::new(JITFactoryRegistry {
623 factories: Vec::new(),
624 })
625 });
626 ®ISTRY
627 }
628
629 pub fn register(&mut self, factory: Box<dyn JITFunctionFactory>) {
631 self.factories.push(factory);
632 }
633
634 #[must_use]
636 pub fn get_factory_for_array_type(
637 &self,
638 array_typeid: TypeId,
639 ) -> Option<&dyn JITFunctionFactory> {
640 for factory in &self.factories {
641 if factory.supports_array_type(array_typeid) {
642 return Some(&**factory);
643 }
644 }
645 None
646 }
647}
648
649fn downcast_array_function_arg<C: 'static>(arg: &dyn Any) -> Option<&C> {
665 if let Some(boxed) = arg.downcast_ref::<Box<dyn ArrayProtocol>>() {
666 return boxed.as_any().downcast_ref::<C>();
667 }
668 arg.downcast_ref::<C>()
669}
670
671fn downcast_arg_to_ixdyn<T: Clone + Send + Sync + 'static>(
682 arg: &dyn Any,
683) -> Option<crate::ndarray::Array<T, crate::ndarray::IxDyn>> {
684 if let Some(w) = downcast_array_function_arg::<NdarrayWrapper<T, crate::ndarray::IxDyn>>(arg) {
685 return Some(w.as_array().clone());
686 }
687 if let Some(w) = downcast_array_function_arg::<NdarrayWrapper<T, crate::ndarray::Ix2>>(arg) {
688 return Some(w.as_array().clone().into_dyn());
689 }
690 if let Some(w) = downcast_array_function_arg::<NdarrayWrapper<T, crate::ndarray::Ix1>>(arg) {
691 return Some(w.as_array().clone().into_dyn());
692 }
693 None
694}
695
696#[derive(Debug, Clone)]
698pub struct NdarrayWrapper<T, D: crate::ndarray::Dimension> {
699 array: crate::ndarray::Array<T, D>,
700 phantom: PhantomData<(T, D)>,
701}
702
703impl<T, D> NdarrayWrapper<T, D>
704where
705 T: Clone + 'static,
706 D: crate::ndarray::Dimension + 'static,
707{
708 #[must_use]
710 pub fn new(array: crate::ndarray::Array<T, D>) -> Self {
711 Self {
712 array,
713 phantom: PhantomData,
714 }
715 }
716
717 #[must_use]
719 pub const fn as_array(&self) -> &crate::ndarray::Array<T, D> {
720 &self.array
721 }
722
723 #[must_use]
725 pub fn into_array(self) -> crate::ndarray::Array<T, D> {
726 self.array
727 }
728
729 pub fn array_2(&mut self, newarray: crate::ndarray::Array<T, D>) {
731 self.array = newarray;
732 }
733}
734
735const NDARRAY_WRAPPER_SUPPORTED_OPS: &[&str] = &[
739 "scirs2::array_protocol::operations::add",
740 "scirs2::array_protocol::operations::multiply",
741 "scirs2::array_protocol::operations::matmul",
742 "scirs2::array_protocol::operations::transpose",
743 "scirs2::array_protocol::operations::sum",
744 "scirs2::array_protocol::operations::reshape",
745];
746
747impl<T, D> ArrayProtocol for NdarrayWrapper<T, D>
748where
749 T: Clone + Send + Sync + 'static,
750 D: crate::ndarray::Dimension + Send + Sync + 'static,
751{
752 fn supports_op(&self, opname: &str) -> bool {
753 NDARRAY_WRAPPER_SUPPORTED_OPS.contains(&opname)
754 }
755
756 fn array_function(
757 &self,
758 func: &ArrayFunction,
759 _types: &[TypeId],
760 args: &[Box<dyn Any>],
761 kwargs: &HashMap<String, Box<dyn Any>>,
762 ) -> Result<Box<dyn Any>, NotImplemented> {
763 match func.name {
764 "scirs2::array_protocol::operations::add" => {
765 if args.len() < 2 {
767 return Err(NotImplemented);
768 }
769
770 let other = downcast_array_function_arg::<NdarrayWrapper<T, D>>(args[1].as_ref());
775
776 if let Some(other) = other {
777 if let (Some(a), Some(b)) = (
778 self.as_any().downcast_ref::<NdarrayWrapper<T, D>>(),
779 other.as_any().downcast_ref::<NdarrayWrapper<T, D>>(),
780 ) {
781 if TypeId::of::<T>() == TypeId::of::<f64>() {
783 let a_f64 =
784 unsafe { &*(a as *const _ as *const NdarrayWrapper<f64, D>) };
785 let b_f64 =
786 unsafe { &*(b as *const _ as *const NdarrayWrapper<f64, D>) };
787 let result = a_f64.as_array() + b_f64.as_array();
788 return Ok(Box::new(
789 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
790 ));
791 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
792 let a_f32 =
793 unsafe { &*(a as *const _ as *const NdarrayWrapper<f32, D>) };
794 let b_f32 =
795 unsafe { &*(b as *const _ as *const NdarrayWrapper<f32, D>) };
796 let result = a_f32.as_array() + b_f32.as_array();
797 return Ok(Box::new(
798 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
799 ));
800 }
801 }
802 }
803
804 if TypeId::of::<T>() == TypeId::of::<f64>() {
810 if let Some(other_arr) = downcast_arg_to_ixdyn::<f64>(args[1].as_ref()) {
811 let self_f64 =
812 unsafe { &*(self as *const _ as *const NdarrayWrapper<f64, D>) };
813 let result = self_f64.as_array().to_owned().into_dyn() + other_arr;
814 return Ok(Box::new(
815 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
816 ));
817 }
818 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
819 if let Some(other_arr) = downcast_arg_to_ixdyn::<f32>(args[1].as_ref()) {
820 let self_f32 =
821 unsafe { &*(self as *const _ as *const NdarrayWrapper<f32, D>) };
822 let result = self_f32.as_array().to_owned().into_dyn() + other_arr;
823 return Ok(Box::new(
824 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
825 ));
826 }
827 }
828 Err(NotImplemented)
829 }
830 "scirs2::array_protocol::operations::multiply" => {
831 if args.len() < 2 {
833 return Err(NotImplemented);
834 }
835
836 let other = downcast_array_function_arg::<NdarrayWrapper<T, D>>(args[1].as_ref());
837
838 if let Some(other) = other {
839 if let (Some(a), Some(b)) = (
840 self.as_any().downcast_ref::<NdarrayWrapper<T, D>>(),
841 other.as_any().downcast_ref::<NdarrayWrapper<T, D>>(),
842 ) {
843 if TypeId::of::<T>() == TypeId::of::<f64>() {
844 let a_f64 =
845 unsafe { &*(a as *const _ as *const NdarrayWrapper<f64, D>) };
846 let b_f64 =
847 unsafe { &*(b as *const _ as *const NdarrayWrapper<f64, D>) };
848 let result = a_f64.as_array() * b_f64.as_array();
849 return Ok(Box::new(
850 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
851 ));
852 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
853 let a_f32 =
854 unsafe { &*(a as *const _ as *const NdarrayWrapper<f32, D>) };
855 let b_f32 =
856 unsafe { &*(b as *const _ as *const NdarrayWrapper<f32, D>) };
857 let result = a_f32.as_array() * b_f32.as_array();
858 return Ok(Box::new(
859 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
860 ));
861 }
862 }
863 }
864
865 if TypeId::of::<T>() == TypeId::of::<f64>() {
868 if let Some(other_arr) = downcast_arg_to_ixdyn::<f64>(args[1].as_ref()) {
869 let self_f64 =
870 unsafe { &*(self as *const _ as *const NdarrayWrapper<f64, D>) };
871 let result = self_f64.as_array().to_owned().into_dyn() * other_arr;
872 return Ok(Box::new(
873 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
874 ));
875 }
876 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
877 if let Some(other_arr) = downcast_arg_to_ixdyn::<f32>(args[1].as_ref()) {
878 let self_f32 =
879 unsafe { &*(self as *const _ as *const NdarrayWrapper<f32, D>) };
880 let result = self_f32.as_array().to_owned().into_dyn() * other_arr;
881 return Ok(Box::new(
882 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
883 ));
884 }
885 }
886 Err(NotImplemented)
887 }
888 "scirs2::array_protocol::operations::matmul" => {
889 if args.len() < 2 {
891 return Err(NotImplemented);
892 }
893
894 if TypeId::of::<D>() != TypeId::of::<crate::ndarray::Ix2>() {
897 return Err(NotImplemented);
898 }
899
900 let other = downcast_array_function_arg::<NdarrayWrapper<T, D>>(args[1].as_ref());
901
902 if let Some(other) = other {
903 if TypeId::of::<T>() == TypeId::of::<f64>() {
908 let a_f64 = unsafe {
910 &*(self as *const _ as *const NdarrayWrapper<f64, crate::ndarray::Ix2>)
911 };
912 let b_f64 = unsafe {
913 &*(other as *const _ as *const NdarrayWrapper<f64, crate::ndarray::Ix2>)
914 };
915
916 let ashape = a_f64.as_array().shape();
918 let bshape = b_f64.as_array().shape();
919
920 if ashape.len() != 2 || bshape.len() != 2 || ashape[1] != bshape[0] {
921 return Err(NotImplemented);
922 }
923
924 let result = a_f64.as_array().dot(b_f64.as_array());
927 return Ok(Box::new(
928 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
929 ));
930 }
931 else if TypeId::of::<T>() == TypeId::of::<f32>() {
933 let a_f32 = unsafe {
935 &*(self as *const _ as *const NdarrayWrapper<f32, crate::ndarray::Ix2>)
936 };
937 let b_f32 = unsafe {
938 &*(other as *const _ as *const NdarrayWrapper<f32, crate::ndarray::Ix2>)
939 };
940
941 let ashape = a_f32.as_array().shape();
943 let bshape = b_f32.as_array().shape();
944
945 if ashape.len() != 2 || bshape.len() != 2 || ashape[1] != bshape[0] {
946 return Err(NotImplemented);
947 }
948
949 let result = a_f32.as_array().dot(b_f32.as_array());
952 return Ok(Box::new(
953 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
954 ));
955 }
956 }
957 Err(NotImplemented)
959 }
960 "scirs2::array_protocol::operations::transpose" => {
961 if TypeId::of::<T>() == TypeId::of::<f64>() {
963 let a_f64 = unsafe { &*(self as *const _ as *const NdarrayWrapper<f64, D>) };
964 let result = a_f64.as_array().t().to_owned();
965 return Ok(Box::new(
966 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
967 ));
968 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
969 let a_f32 = unsafe { &*(self as *const _ as *const NdarrayWrapper<f32, D>) };
970 let result = a_f32.as_array().t().to_owned();
971 return Ok(Box::new(
972 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
973 ));
974 }
975 Err(NotImplemented)
976 }
977 "scirs2::array_protocol::operations::sum" => {
978 let axis_ref = kwargs.get("axis").and_then(|a| a.downcast_ref::<usize>());
980
981 if TypeId::of::<T>() == TypeId::of::<f64>() {
982 let a_f64 = unsafe { &*(self as *const _ as *const NdarrayWrapper<f64, D>) };
983 match axis_ref {
984 Some(&_ax) => {
985 let result = a_f64.as_array().sum();
988 return Ok(Box::new(result));
989 }
990 None => {
991 let result = a_f64.as_array().sum();
992 return Ok(Box::new(result));
993 }
994 }
995 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
996 let a_f32 = unsafe { &*(self as *const _ as *const NdarrayWrapper<f32, D>) };
997 match axis_ref {
998 Some(&_ax) => {
999 let result = a_f32.as_array().sum();
1002 return Ok(Box::new(result));
1003 }
1004 None => {
1005 let result = a_f32.as_array().sum();
1006 return Ok(Box::new(result));
1007 }
1008 }
1009 }
1010 Err(NotImplemented)
1011 }
1012 "scirs2::array_protocol::operations::reshape" => {
1013 if let Some(shape) = kwargs
1015 .get("shape")
1016 .and_then(|s| s.downcast_ref::<Vec<usize>>())
1017 {
1018 if TypeId::of::<T>() == TypeId::of::<f64>() {
1019 let a_f64 =
1020 unsafe { &*(self as *const _ as *const NdarrayWrapper<f64, D>) };
1021 match a_f64
1022 .as_array()
1023 .clone()
1024 .into_shape_with_order(shape.clone())
1025 {
1026 Ok(result) => {
1027 return Ok(Box::new(
1028 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
1029 ))
1030 }
1031 Err(_) => return Err(NotImplemented),
1032 }
1033 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
1034 let a_f32 =
1035 unsafe { &*(self as *const _ as *const NdarrayWrapper<f32, D>) };
1036 match a_f32
1037 .as_array()
1038 .clone()
1039 .into_shape_with_order(shape.clone())
1040 {
1041 Ok(result) => {
1042 return Ok(Box::new(
1043 Box::new(NdarrayWrapper::new(result)) as Box<dyn ArrayProtocol>
1044 ))
1045 }
1046 Err(_) => return Err(NotImplemented),
1047 }
1048 }
1049 }
1050 Err(NotImplemented)
1051 }
1052 _ => Err(NotImplemented),
1053 }
1054 }
1055
1056 fn as_any(&self) -> &dyn Any {
1057 self
1058 }
1059
1060 fn shape(&self) -> &[usize] {
1061 self.array.shape()
1062 }
1063
1064 fn dtype(&self) -> TypeId {
1065 TypeId::of::<T>()
1066 }
1067
1068 fn box_clone(&self) -> Box<dyn ArrayProtocol> {
1069 Box::new(self.clone())
1070 }
1071}
1072
1073#[derive(Debug, Clone)]
1077pub struct MockDistributedArray<T: Clone + 'static> {
1078 chunks: Vec<T>,
1079 shape: Vec<usize>,
1080}
1081
1082impl<T: Clone + Send + Sync + 'static> MockDistributedArray<T> {
1083 #[must_use]
1085 pub fn new(chunks: Vec<T>, shape: Vec<usize>) -> Self {
1086 Self { chunks, shape }
1087 }
1088}
1089
1090impl<T: Clone + Send + Sync + 'static> ArrayProtocol for MockDistributedArray<T> {
1091 fn array_function(
1092 &self,
1093 func: &ArrayFunction,
1094 _types: &[TypeId],
1095 _args: &[Box<dyn Any>],
1096 _kwargs: &HashMap<String, Box<dyn Any>>,
1097 ) -> Result<Box<dyn Any>, NotImplemented> {
1098 match func.name {
1099 "scirs2::mean" => {
1100 let result = T::clone(&self.chunks[0]);
1105 Ok(Box::new(result))
1106 }
1107 _ => Err(NotImplemented),
1108 }
1109 }
1110
1111 fn as_any(&self) -> &dyn Any {
1112 self
1113 }
1114
1115 fn shape(&self) -> &[usize] {
1116 &self.shape
1117 }
1118
1119 fn box_clone(&self) -> Box<dyn ArrayProtocol> {
1120 Box::new(self.clone())
1121 }
1122}
1123
1124impl<T: Clone + Send + Sync + 'static> DistributedArray for MockDistributedArray<T> {
1125 fn distribution_info(&self) -> HashMap<String, String> {
1126 let mut info = HashMap::new();
1127 info.insert("type".to_string(), "mock_distributed".to_string());
1128 info.insert("chunks".to_string(), self.chunks.len().to_string());
1129 info
1130 }
1131
1132 fn gather(&self) -> CoreResult<Box<dyn ArrayProtocol>> {
1133 Ok(Box::new(self.clone()) as Box<dyn ArrayProtocol>)
1136 }
1137
1138 fn scatter(&self, _numchunks: usize) -> CoreResult<Box<dyn DistributedArray>> {
1139 Ok(Box::new(self.clone()) as Box<dyn DistributedArray>)
1142 }
1143
1144 fn is_distributed(&self) -> bool {
1145 true
1146 }
1147}
1148
1149#[derive(Debug, Clone)]
1151pub struct MockGPUArray<T: Clone + 'static> {
1152 data: Vec<T>,
1153 shape: Vec<usize>,
1154 device: String,
1155}
1156
1157impl<T: Clone + Send + Sync + 'static> MockGPUArray<T> {
1158 #[must_use]
1160 pub fn new(data: Vec<T>, shape: Vec<usize>, device: String) -> Self {
1161 Self {
1162 data,
1163 shape,
1164 device,
1165 }
1166 }
1167}
1168
1169impl<T: Clone + Send + Sync + 'static> ArrayProtocol for MockGPUArray<T> {
1170 fn array_function(
1171 &self,
1172 func: &ArrayFunction,
1173 _types: &[TypeId],
1174 _args: &[Box<dyn Any>],
1175 _kwargs: &HashMap<String, Box<dyn Any>>,
1176 ) -> Result<Box<dyn Any>, NotImplemented> {
1177 match func.name {
1178 "scirs2::matmul" => {
1179 let result =
1184 MockGPUArray::new(self.data.clone(), self.shape.clone(), self.device.clone());
1185 Ok(Box::new(result))
1186 }
1187 _ => Err(NotImplemented),
1188 }
1189 }
1190
1191 fn as_any(&self) -> &dyn Any {
1192 self
1193 }
1194
1195 fn shape(&self) -> &[usize] {
1196 &self.shape
1197 }
1198
1199 fn box_clone(&self) -> Box<dyn ArrayProtocol> {
1200 Box::new(self.clone())
1201 }
1202}
1203
1204impl<T: Clone + Send + Sync + 'static> GPUArray for MockGPUArray<T> {
1205 fn to_gpu(&self) -> CoreResult<Box<dyn GPUArray>> {
1206 Ok(Box::new(self.clone()) as Box<dyn GPUArray>)
1208 }
1209
1210 fn to_cpu(&self) -> CoreResult<Box<dyn ArrayProtocol>> {
1211 Ok(Box::new(self.clone()) as Box<dyn ArrayProtocol>)
1214 }
1215
1216 fn is_on_gpu(&self) -> bool {
1217 true
1218 }
1219
1220 fn device_info(&self) -> HashMap<String, String> {
1221 let mut info = HashMap::new();
1222 info.insert("device".to_string(), self.device.clone());
1223 info.insert("type".to_string(), "mock_gpu".to_string());
1224 info
1225 }
1226}
1227
1228#[derive(Debug)]
1233pub struct ArrayProtocolFunction<F> {
1234 func: F,
1235 name: &'static str,
1236}
1237
1238impl<F> ArrayProtocolFunction<F> {
1239 #[must_use]
1241 pub fn new(func: F, name: &'static str) -> Self {
1242 Self { func, name }
1243 }
1244}
1245
1246impl<F> ArrayProtocolFunction<F>
1247where
1248 F: Clone + Send + Sync + 'static,
1249{
1250 pub fn register(self) -> F {
1252 let implementation = Arc::new(
1253 move |_args: &[Box<dyn Any>], _kwargs: &HashMap<String, Box<dyn Any>>| {
1254 Err(CoreError::NotImplementedError(ErrorContext::new(
1259 "ArrayProtocolFunction: Implementation for array protocol functions is not complete".to_string()
1260 )))
1261 },
1262 );
1263
1264 let array_func = ArrayFunction {
1265 name: self.name,
1266 implementation,
1267 };
1268
1269 if let Ok(mut registry) = ArrayFunctionRegistry::global().write() {
1271 registry.register(array_func);
1272 } else {
1273 eprintln!("Warning: Failed to acquire write lock on ArrayFunctionRegistry during array protocol building, skipping function registration");
1274 }
1276
1277 self.func
1278 }
1279}
1280
1281#[macro_export]
1323macro_rules! array_function_def {
1324 (fn $name:ident $(<$($gen:ident),*>)? ($($arg:ident : $arg_ty:ty),*) -> $ret:ty $body:block, $funcname:expr) => {
1325 {
1326 fn $name $(<$($gen),*>)? ($($arg : $arg_ty),*) -> $ret $body
1328
1329 $name
1331 }
1332 };
1333}
1334
1335pub use self::distributed_impl::{
1337 ArrayChunk, DistributedBackend, DistributedConfig, DistributedNdarray, DistributionStrategy,
1338};
1339
1340pub use self::gpu_impl::{
1342 kernels as gpu_kernels, GPUArrayBuilder, GPUBackend, GPUConfig, GPUNdarray,
1343};
1344
1345pub use self::jit_impl::{
1347 CraneliftFunctionFactory, JITBackend, JITConfig, JITEnabledArray, JITFunctionImpl, JITManager,
1348 LLVMFunctionFactory,
1349};
1350
1351pub use self::operations::{
1353 add, apply_elementwise, concatenate, inverse, matmul, multiply, reshape, subtract, sum, svd,
1354 transpose, OperationError,
1355};
1356
1357pub use self::ml_ops::{
1359 activation, batch_norm, conv2d, cross_entropy, dropout, max_pool2d, self_attention,
1360 ActivationFunc,
1361};
1362
1363#[allow(dead_code)]
1369pub fn init() {
1370 let mut jit_manager = JITManager::global().write().expect("Operation failed");
1372 jit_manager.initialize();
1373}
1374
1375pub mod traits {
1377 use super::*;
1378
1379 pub trait StridedArray: ArrayProtocol {
1381 #[must_use]
1383 fn strides(&self) -> Vec<usize>;
1384
1385 #[must_use]
1387 fn is_contiguous(&self) -> bool;
1388
1389 #[must_use]
1391 fn is_fortran_contiguous(&self) -> bool;
1392 }
1393
1394 pub trait ZeroCopyArray: ArrayProtocol {
1396 #[must_use]
1398 fn view(&self) -> Box<dyn ZeroCopyArray>;
1399
1400 #[must_use]
1402 fn view_mut(&mut self) -> Box<dyn ZeroCopyArray>;
1403
1404 #[must_use]
1406 fn is_view(&self) -> bool;
1407 }
1408
1409 pub trait DifferentiableArray: ArrayProtocol {
1411 fn gradient(
1413 &self,
1414 variables: &[Box<dyn DifferentiableArray>],
1415 ) -> Vec<Box<dyn DifferentiableArray>>;
1416
1417 fn set_requiresgrad(&mut self, requiresgrad: bool);
1419
1420 #[must_use]
1422 fn requiresgrad(&self) -> bool;
1423
1424 #[must_use]
1426 fn grad(&self) -> Option<Box<dyn DifferentiableArray>>;
1427 }
1428
1429 pub trait AsyncArray: ArrayProtocol {
1431 fn async_op<F, R>(&self, op: F) -> impl std::future::Future<Output = CoreResult<R>>
1433 where
1434 F: FnOnce(&Self) -> CoreResult<R> + Send + 'static,
1435 R: Send + 'static;
1436
1437 #[must_use]
1439 fn supports_async(&self) -> bool;
1440 }
1441}
1442
1443#[cfg(test)]
1444mod tests {
1445 use super::*;
1446
1447 #[test]
1448 fn test_array_protocol_registry() {
1449 let implementation = Arc::new(
1451 move |_args: &[Box<dyn Any>], _kwargs: &HashMap<String, Box<dyn Any>>| {
1452 Ok(Box::new(42.0) as Box<dyn Any>)
1453 },
1454 );
1455
1456 let func = ArrayFunction {
1457 name: "scirs2::test::test_func",
1458 implementation,
1459 };
1460
1461 let registry = ArrayFunctionRegistry::global();
1462 {
1463 let mut reg = registry.write().expect("Operation failed");
1464 reg.register(func.clone());
1465 }
1466
1467 {
1469 let reg = registry.read().expect("Operation failed");
1470 let registered_func = reg
1471 .get("scirs2::test::test_func")
1472 .expect("Operation failed");
1473 assert_eq!(registered_func.name, "scirs2::test::test_func");
1474 }
1475 }
1476
1477 #[test]
1478 fn test_mock_distributed_array() {
1479 let array = MockDistributedArray::new(vec![1.0, 2.0, 3.0], vec![3]);
1480 assert!(array.is_distributed());
1481
1482 let info = array.distribution_info();
1483 assert_eq!(
1484 info.get("type").expect("Operation failed"),
1485 "mock_distributed"
1486 );
1487 assert_eq!(info.get("chunks").expect("Operation failed"), "3");
1488 }
1489
1490 #[test]
1491 fn test_mock_gpu_array() {
1492 let array = MockGPUArray::new(vec![1.0, 2.0, 3.0], vec![3], "cuda:0".to_string());
1493 assert!(array.is_on_gpu());
1494
1495 let info = array.device_info();
1496 assert_eq!(info.get("device").expect("Operation failed"), "cuda:0");
1497 assert_eq!(info.get("type").expect("Operation failed"), "mock_gpu");
1498 }
1499
1500 #[test]
1501 fn test_box_clone() {
1502 let array = crate::ndarray::Array2::<f64>::ones((3, 3));
1504 let wrapped = NdarrayWrapper::new(array);
1505 let boxed: Box<dyn ArrayProtocol> = Box::new(wrapped);
1506 let cloned = boxed.clone();
1507
1508 assert_eq!(cloned.shape(), &[3, 3]);
1510
1511 let array = MockDistributedArray::new(vec![1.0, 2.0, 3.0], vec![3]);
1513 let boxed: Box<dyn ArrayProtocol> = Box::new(array);
1514 let cloned = boxed.clone();
1515
1516 assert_eq!(cloned.shape(), &[3]);
1518 }
1519}
1520
1521#[cfg(test)]
1523mod examples {
1524 use super::*;
1525 use ::ndarray::Array2;
1526 use std::any::Any;
1527 use std::collections::HashMap;
1528
1529 #[test]
1531 fn example_distributed_array() {
1532 let array = Array2::from_shape_fn((10, 5), |(i, j)| (i * 5 + j) as f64);
1537
1538 let config = DistributedConfig {
1540 chunks: 3,
1541 balance: true,
1542 strategy: DistributionStrategy::RowWise,
1543 backend: DistributedBackend::Threaded,
1544 };
1545
1546 let dist_array = DistributedNdarray::from_array(&array, config);
1548
1549 assert_eq!(dist_array.num_chunks(), 3);
1551 assert_eq!(dist_array.shape(), &[10, 5]);
1552
1553 let result = dist_array.to_array().expect("Operation failed");
1555
1556 assert_eq!(result.shape(), array.shape());
1560 assert_eq!(result, array.into_dyn());
1561 }
1562
1563 #[test]
1565 fn example_gpu_array() {
1566 let array = Array2::<f64>::ones((10, 5));
1568
1569 let config = GPUConfig {
1571 backend: GPUBackend::CUDA,
1572 device_id: 0,
1573 async_ops: true,
1574 mixed_precision: false,
1575 memory_fraction: 0.9,
1576 };
1577
1578 let gpu_array = GPUNdarray::new(array.clone(), config);
1580
1581 assert_eq!(gpu_array.shape(), &[10, 5]);
1583 assert!(gpu_array.is_on_gpu());
1584
1585 let info = gpu_array.device_info();
1587 assert_eq!(info.get("backend").expect("Operation failed"), "CUDA");
1588
1589 let gpu_box: Box<dyn ArrayProtocol> = Box::new(gpu_array);
1591 let gpu_clone = gpu_box.clone();
1592
1593 assert_eq!(gpu_clone.shape(), &[10, 5]);
1595 }
1596
1597 #[test]
1599 fn example_jit_array() {
1600 init();
1602
1603 let array = Array2::<f64>::ones((10, 5));
1605 let wrapped = NdarrayWrapper::new(array);
1606
1607 let jitarray: JITEnabledArray<f64, NdarrayWrapper<f64, crate::ndarray::Ix2>> =
1609 JITEnabledArray::new(wrapped);
1610
1611 assert!(jitarray.supports_jit());
1613
1614 let expression = "x + y";
1616 let jit_function = jitarray.compile(expression).expect("Operation failed");
1617
1618 assert_eq!(jit_function.source(), expression);
1620
1621 let info = jitarray.jit_info();
1623 assert_eq!(info.get("supports_jit").expect("Operation failed"), "true");
1624
1625 let jit_box: Box<dyn ArrayProtocol> = Box::new(jitarray);
1627 let jit_clone = jit_box.clone();
1628
1629 assert_eq!(jit_clone.shape(), &[10, 5]);
1631 }
1632
1633 #[test]
1635 fn example_cloning_array_protocol_objects() {
1636 let array = Array2::<f64>::ones((10, 5));
1638 let config = GPUConfig::default();
1639 let gpu_array = GPUNdarray::new(array.clone(), config);
1640
1641 let boxed: Box<dyn ArrayProtocol> = Box::new(gpu_array);
1643 let cloned = boxed.clone();
1644
1645 assert_eq!(cloned.shape(), &[10, 5]);
1647
1648 let config = DistributedConfig {
1650 chunks: 3,
1651 balance: true,
1652 strategy: DistributionStrategy::RowWise,
1653 backend: DistributedBackend::Threaded,
1654 };
1655 let dist_array = DistributedNdarray::from_array(&array, config);
1656
1657 let boxed: Box<dyn ArrayProtocol> = Box::new(dist_array);
1659 let cloned = boxed.clone();
1660
1661 assert_eq!(cloned.shape(), &[10, 5]);
1663 }
1664
1665 #[test]
1711 fn example_array_interoperability() {
1712 init();
1714
1715 let cpu_array = Array2::<f64>::ones((5, 5));
1717
1718 let gpu_config = GPUConfig {
1720 backend: GPUBackend::CUDA,
1721 device_id: 0,
1722 async_ops: false,
1723 mixed_precision: false,
1724 memory_fraction: 0.9,
1725 };
1726 let gpu_array = GPUNdarray::new(cpu_array.clone(), gpu_config);
1727
1728 let dist_config = DistributedConfig {
1730 chunks: 2,
1731 balance: true,
1732 strategy: DistributionStrategy::RowWise,
1733 backend: DistributedBackend::Threaded,
1734 };
1735 let dist_array = DistributedNdarray::from_array(&cpu_array, dist_config);
1736
1737 let gpu_wrapper: Box<dyn ArrayProtocol> = Box::new(gpu_array);
1739 let dist_wrapper: Box<dyn ArrayProtocol> = Box::new(dist_array);
1740
1741 let gpu_clone = gpu_wrapper.clone();
1743 let dist_clone = dist_wrapper.clone();
1744
1745 assert_eq!(gpu_clone.shape(), &[5, 5]);
1746 assert_eq!(dist_clone.shape(), &[5, 5]);
1747 }
1748
1749 #[test]
1751 fn example_custom_array_type() {
1752 use std::sync::Arc;
1753
1754 struct MyCustomArray<T> {
1756 data: Vec<T>,
1757 shape: Vec<usize>,
1758 }
1759
1760 impl<T: Clone + 'static> MyCustomArray<T> {
1761 fn new(data: Vec<T>, shape: Vec<usize>) -> Self {
1762 Self { data, shape }
1763 }
1764
1765 }
1770
1771 impl<T: Clone + Send + Sync + 'static> ArrayProtocol for MyCustomArray<T> {
1773 fn array_function(
1774 &self,
1775 func: &ArrayFunction,
1776 _types: &[TypeId],
1777 _args: &[Box<dyn Any>],
1778 _kwargs: &HashMap<String, Box<dyn Any>>,
1779 ) -> Result<Box<dyn Any>, NotImplemented> {
1780 if func.name == "scirs2::example::custom_sum" {
1781 match std::any::TypeId::of::<T>() {
1783 tid if tid == std::any::TypeId::of::<f64>() => {
1784 let f64_data = unsafe {
1786 std::slice::from_raw_parts(
1787 self.data.as_ptr() as *const f64,
1788 self.data.len(),
1789 )
1790 };
1791 let sum = f64_data.iter().sum::<f64>();
1792 Ok(Box::new(sum))
1793 }
1794 tid if tid == std::any::TypeId::of::<f32>() => {
1795 let f32_data = unsafe {
1797 std::slice::from_raw_parts(
1798 self.data.as_ptr() as *const f32,
1799 self.data.len(),
1800 )
1801 };
1802 let sum = f32_data.iter().sum::<f32>();
1803 Ok(Box::new(sum))
1804 }
1805 _ => Err(NotImplemented),
1806 }
1807 } else {
1808 Err(NotImplemented)
1809 }
1810 }
1811
1812 fn as_any(&self) -> &dyn Any {
1813 self
1814 }
1815
1816 fn shape(&self) -> &[usize] {
1817 &self.shape
1818 }
1819
1820 fn box_clone(&self) -> Box<dyn ArrayProtocol> {
1821 Box::new(MyCustomArray {
1822 data: self.data.clone(),
1823 shape: self.shape.clone(),
1824 })
1825 }
1826 }
1827
1828 let custom_array = MyCustomArray::new(vec![1.0, 2.0, 3.0, 4.0], vec![2, 2]);
1830
1831 let boxed: Box<dyn ArrayProtocol> = Box::new(custom_array);
1833 let cloned = boxed.clone();
1834
1835 assert_eq!(cloned.shape(), &[2, 2]);
1837
1838 let func = ArrayFunction {
1840 name: "scirs2::example::custom_sum",
1841 implementation: Arc::new(move |_args, _kwargs| {
1842 Ok(Box::new(42.0) as Box<dyn Any>)
1844 }),
1845 };
1846
1847 let result = cloned.array_function(
1849 &func,
1850 &[std::any::TypeId::of::<f64>()],
1851 &[],
1852 &HashMap::new(),
1853 );
1854
1855 assert!(result.is_ok());
1857 if let Ok(value) = result {
1858 let sum = *value.downcast_ref::<f64>().expect("Operation failed");
1859 assert_eq!(sum, 10.0);
1860 }
1861 }
1862}
1863impl Clone for Box<dyn JITFunction> {
1865 fn clone(&self) -> Self {
1866 self.clone_box()
1867 }
1868}