runmat_runtime/builtins/logical/tests/
issparse.rs1use runmat_builtins::{
4 BuiltinCompletionPolicy, BuiltinDescriptor, BuiltinErrorDescriptor, BuiltinOutputMode,
5 BuiltinParamArity, BuiltinParamDescriptor, BuiltinParamType, BuiltinSignatureDescriptor,
6 ResolveContext, Type, Value,
7};
8use runmat_macros::runtime_builtin;
9
10use crate::builtins::common::spec::{
11 BroadcastSemantics, BuiltinFusionSpec, BuiltinGpuSpec, ConstantStrategy, GpuOpKind,
12 ReductionNaN, ResidencyPolicy, ShapeRequirements,
13};
14use crate::BuiltinResult;
15
16#[runmat_macros::register_gpu_spec(builtin_path = "crate::builtins::logical::tests::issparse")]
17pub const GPU_SPEC: BuiltinGpuSpec = BuiltinGpuSpec {
18 name: "issparse",
19 op_kind: GpuOpKind::Custom("metadata"),
20 supported_precisions: &[],
21 broadcast: BroadcastSemantics::None,
22 provider_hooks: &[],
23 constant_strategy: ConstantStrategy::InlineLiteral,
24 residency: ResidencyPolicy::GatherImmediately,
25 nan_mode: ReductionNaN::Include,
26 two_pass_threshold: None,
27 workgroup_size: None,
28 accepts_nan_mode: false,
29 notes:
30 "Reports whether the value is a host sparse matrix; dense gpuArray handles are not sparse.",
31};
32
33#[runmat_macros::register_fusion_spec(builtin_path = "crate::builtins::logical::tests::issparse")]
34pub const FUSION_SPEC: BuiltinFusionSpec = BuiltinFusionSpec {
35 name: "issparse",
36 shape: ShapeRequirements::Any,
37 constant_strategy: ConstantStrategy::InlineLiteral,
38 elementwise: None,
39 reduction: None,
40 emits_nan: false,
41 notes: "Metadata query that executes outside fusion and returns a scalar logical.",
42};
43
44const ISSPARSE_OUTPUT: [BuiltinParamDescriptor; 1] = [BuiltinParamDescriptor {
45 name: "tf",
46 ty: BuiltinParamType::LogicalArray,
47 arity: BuiltinParamArity::Required,
48 default: None,
49 description: "True when input is a sparse matrix.",
50}];
51
52const ISSPARSE_INPUTS: [BuiltinParamDescriptor; 1] = [BuiltinParamDescriptor {
53 name: "A",
54 ty: BuiltinParamType::Any,
55 arity: BuiltinParamArity::Required,
56 default: None,
57 description: "Input value to test.",
58}];
59
60const ISSPARSE_SIGNATURES: [BuiltinSignatureDescriptor; 1] = [BuiltinSignatureDescriptor {
61 label: "tf = issparse(A)",
62 inputs: &ISSPARSE_INPUTS,
63 outputs: &ISSPARSE_OUTPUT,
64}];
65
66const ISSPARSE_ERRORS: [BuiltinErrorDescriptor; 0] = [];
67
68pub const ISSPARSE_DESCRIPTOR: BuiltinDescriptor = BuiltinDescriptor {
69 signatures: &ISSPARSE_SIGNATURES,
70 output_mode: BuiltinOutputMode::Fixed,
71 completion_policy: BuiltinCompletionPolicy::Public,
72 errors: &ISSPARSE_ERRORS,
73};
74
75#[runtime_builtin(
76 name = "issparse",
77 category = "logical/tests",
78 summary = "Return true when a value is a sparse matrix.",
79 keywords = "issparse,sparse,matrix,type,logical",
80 accel = "metadata",
81 type_resolver(bool_scalar_type),
82 descriptor(crate::builtins::logical::tests::issparse::ISSPARSE_DESCRIPTOR),
83 builtin_path = "crate::builtins::logical::tests::issparse"
84)]
85async fn issparse_builtin(value: Value) -> BuiltinResult<Value> {
86 Ok(Value::Bool(matches!(value, Value::SparseTensor(_))))
87}
88
89fn bool_scalar_type(_: &[Type], _context: &ResolveContext) -> Type {
90 Type::Bool
91}
92
93#[cfg(test)]
94pub(crate) mod tests {
95 use super::*;
96 use crate::builtins::common::test_support;
97 use futures::executor::block_on;
98 use runmat_accelerate_api::HostTensorView;
99 use runmat_builtins::{
100 CellArray, CharArray, ComplexTensor, IntValue, LogicalArray, MException, ObjectInstance,
101 SparseTensor, StringArray, StructValue, SymbolicExpr, Tensor, Value,
102 };
103
104 fn run_issparse(value: Value) -> BuiltinResult<Value> {
105 block_on(super::issparse_builtin(value))
106 }
107
108 #[test]
109 fn issparse_type_returns_bool() {
110 assert_eq!(
111 super::bool_scalar_type(&[Type::Unknown], &ResolveContext::new(Vec::new())),
112 Type::Bool
113 );
114 }
115
116 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
117 #[test]
118 fn sparse_tensors_report_true() {
119 let sparse = SparseTensor::new(3, 2, vec![0, 1, 2], vec![1, 2], vec![4.0, -1.0]).unwrap();
120 assert_eq!(
121 run_issparse(Value::SparseTensor(sparse)).unwrap(),
122 Value::Bool(true)
123 );
124 assert_eq!(
125 run_issparse(Value::SparseTensor(SparseTensor::zeros(4, 5))).unwrap(),
126 Value::Bool(true)
127 );
128 }
129
130 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
131 #[test]
132 fn dense_numeric_and_logical_values_report_false() {
133 assert_eq!(
134 run_issparse(Value::Int(IntValue::I32(1))).unwrap(),
135 Value::Bool(false)
136 );
137 assert_eq!(run_issparse(Value::Num(1.0)).unwrap(), Value::Bool(false));
138 assert_eq!(
139 run_issparse(Value::Complex(1.0, -2.0)).unwrap(),
140 Value::Bool(false)
141 );
142 assert_eq!(run_issparse(Value::Bool(true)).unwrap(), Value::Bool(false));
143 assert_eq!(
144 run_issparse(Value::LogicalArray(
145 LogicalArray::new(vec![1, 0], vec![1, 2]).unwrap()
146 ))
147 .unwrap(),
148 Value::Bool(false)
149 );
150 assert_eq!(
151 run_issparse(Value::Tensor(
152 Tensor::new(vec![1.0, 0.0], vec![1, 2]).unwrap()
153 ))
154 .unwrap(),
155 Value::Bool(false)
156 );
157 assert_eq!(
158 run_issparse(Value::ComplexTensor(
159 ComplexTensor::new(vec![(1.0, 2.0)], vec![1, 1]).unwrap()
160 ))
161 .unwrap(),
162 Value::Bool(false)
163 );
164 }
165
166 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
167 #[test]
168 fn containers_text_and_objects_report_false() {
169 let cell = CellArray::new(vec![Value::Num(1.0)], 1, 1).unwrap();
170 let string = Value::String("abc".into());
171 let chars = CharArray::new_row("abc");
172 let strings = StringArray::new(vec!["abc".into()], vec![1, 1]).unwrap();
173 let structure = StructValue::new();
174 let object = ObjectInstance::new("Example".into());
175 assert_eq!(run_issparse(Value::Cell(cell)).unwrap(), Value::Bool(false));
176 assert_eq!(run_issparse(string).unwrap(), Value::Bool(false));
177 assert_eq!(
178 run_issparse(Value::CharArray(chars)).unwrap(),
179 Value::Bool(false)
180 );
181 assert_eq!(
182 run_issparse(Value::StringArray(strings)).unwrap(),
183 Value::Bool(false)
184 );
185 assert_eq!(
186 run_issparse(Value::Struct(structure)).unwrap(),
187 Value::Bool(false)
188 );
189 assert_eq!(
190 run_issparse(Value::Object(object)).unwrap(),
191 Value::Bool(false)
192 );
193 assert_eq!(
194 run_issparse(Value::FunctionHandle("sin".into())).unwrap(),
195 Value::Bool(false)
196 );
197 assert_eq!(
198 run_issparse(Value::Symbolic(SymbolicExpr::variable("x"))).unwrap(),
199 Value::Bool(false)
200 );
201 assert_eq!(
202 run_issparse(Value::ClassRef("Example".into())).unwrap(),
203 Value::Bool(false)
204 );
205 assert_eq!(
206 run_issparse(Value::MException(MException::new(
207 "RunMat:test".into(),
208 "not sparse".into()
209 )))
210 .unwrap(),
211 Value::Bool(false)
212 );
213 }
214
215 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
216 #[test]
217 fn gpu_handles_report_false() {
218 test_support::with_test_provider(|provider| {
219 let tensor = Tensor::new(vec![1.0, 2.0, 3.0], vec![3, 1]).unwrap();
220 let view = HostTensorView {
221 data: &tensor.data,
222 shape: &tensor.shape,
223 };
224 let handle = provider.upload(&view).expect("upload");
225 let result = run_issparse(Value::GpuTensor(handle.clone())).expect("issparse");
226 assert_eq!(result, Value::Bool(false));
227 provider.free(&handle).ok();
228 });
229 }
230}