graphblas_sparse_linear_algebra/operators/reduce/
monoid.rs

1use std::convert::TryInto;
2
3use crate::collections::sparse_matrix::GetGraphblasSparseMatrix;
4use crate::collections::sparse_vector::GetGraphblasSparseVector;
5use crate::context::CallGraphBlasContext;
6use crate::error::SparseLinearAlgebraError;
7use crate::graphblas_bindings::{
8    GrB_Matrix_reduce_BOOL, GrB_Matrix_reduce_FP32, GrB_Matrix_reduce_FP64,
9    GrB_Matrix_reduce_INT16, GrB_Matrix_reduce_INT32, GrB_Matrix_reduce_INT64,
10    GrB_Matrix_reduce_INT8, GrB_Matrix_reduce_Monoid, GrB_Matrix_reduce_UINT16,
11    GrB_Matrix_reduce_UINT32, GrB_Matrix_reduce_UINT64, GrB_Matrix_reduce_UINT8,
12    GrB_Vector_reduce_BOOL, GrB_Vector_reduce_FP32, GrB_Vector_reduce_FP64,
13    GrB_Vector_reduce_INT16, GrB_Vector_reduce_INT32, GrB_Vector_reduce_INT64,
14    GrB_Vector_reduce_INT8, GrB_Vector_reduce_UINT16, GrB_Vector_reduce_UINT32,
15    GrB_Vector_reduce_UINT64, GrB_Vector_reduce_UINT8,
16};
17use crate::operators::binary_operator::AccumulatorBinaryOperator;
18use crate::operators::mask::VectorMask;
19use crate::operators::monoid::Monoid;
20use crate::operators::options::{
21    GetOperatorOptions, GetOptionsForOperatorWithMatrixArgument, WithTransposeMatrixArgument,
22};
23use crate::value_type::utilities_to_implement_traits_for_all_value_types::{
24    convert_mut_scalar_to_type, identity_conversion,
25    implement_macro_for_all_value_types_and_2_typed_graphblas_functions_with_mutable_scalar_type_conversion,
26};
27use crate::value_type::{ConvertScalar, ValueType};
28
29// Implemented methods do not provide mutable access to GraphBLAS operators or options.
30// Code review must consider that no mtable access is provided.
31// https://doc.rust-lang.org/nomicon/send-and-sync.html
32unsafe impl Send for MonoidReducer {}
33unsafe impl Sync for MonoidReducer {}
34
35#[derive(Debug, Clone)]
36pub struct MonoidReducer {}
37
38impl MonoidReducer {
39    pub fn new() -> Self {
40        Self {}
41    }
42}
43
44pub trait MonoidVectorReducer<EvaluationDomain: ValueType> {
45    fn to_column_vector(
46        &self,
47        operator: &impl Monoid<EvaluationDomain>,
48        argument: &impl GetGraphblasSparseMatrix,
49        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
50        product: &mut impl GetGraphblasSparseVector,
51        mask: &impl VectorMask,
52        options: &impl GetOptionsForOperatorWithMatrixArgument,
53    ) -> Result<(), SparseLinearAlgebraError>;
54
55    fn to_row_vector(
56        &self,
57        operator: &impl Monoid<EvaluationDomain>,
58        argument: &impl GetGraphblasSparseMatrix,
59        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
60        product: &mut impl GetGraphblasSparseVector,
61        mask: &impl VectorMask,
62        options: &(impl GetOptionsForOperatorWithMatrixArgument + WithTransposeMatrixArgument),
63    ) -> Result<(), SparseLinearAlgebraError>;
64}
65
66impl<EvaluationDomain: ValueType> MonoidVectorReducer<EvaluationDomain> for MonoidReducer {
67    fn to_column_vector(
68        &self,
69        operator: &impl Monoid<EvaluationDomain>,
70        argument: &impl GetGraphblasSparseMatrix,
71        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
72        product: &mut impl GetGraphblasSparseVector,
73        mask: &impl VectorMask,
74        options: &impl GetOptionsForOperatorWithMatrixArgument,
75    ) -> Result<(), SparseLinearAlgebraError> {
76        let context = product.context_ref();
77
78        context.call(
79            || unsafe {
80                GrB_Matrix_reduce_Monoid(
81                    product.graphblas_vector(),
82                    mask.graphblas_vector(),
83                    accumulator.accumulator_graphblas_type(),
84                    operator.graphblas_type(),
85                    argument.graphblas_matrix(),
86                    options.graphblas_descriptor(),
87                )
88            },
89            unsafe { product.graphblas_vector_ref() },
90        )?;
91
92        Ok(())
93    }
94
95    fn to_row_vector(
96        &self,
97        operator: &impl Monoid<EvaluationDomain>,
98        argument: &impl GetGraphblasSparseMatrix,
99        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
100        product: &mut impl GetGraphblasSparseVector,
101        mask: &impl VectorMask,
102        options: &(impl GetOptionsForOperatorWithMatrixArgument + WithTransposeMatrixArgument),
103    ) -> Result<(), SparseLinearAlgebraError> {
104        self.to_column_vector(
105            operator,
106            argument,
107            accumulator,
108            product,
109            mask,
110            &options.with_negated_transpose_matrix_argument(),
111        )
112    }
113}
114
115pub trait MonoidScalarReducer<EvaluationDomain: ValueType> {
116    fn matrix_to_scalar(
117        &self,
118        operator: &impl Monoid<EvaluationDomain>,
119        argument: &impl GetGraphblasSparseMatrix,
120        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
121        product: &mut EvaluationDomain,
122        options: &impl GetOptionsForOperatorWithMatrixArgument,
123    ) -> Result<(), SparseLinearAlgebraError>;
124
125    fn vector_to_scalar(
126        &self,
127        operator: &impl Monoid<EvaluationDomain>,
128        argument: &impl GetGraphblasSparseVector,
129        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
130        product: &mut EvaluationDomain,
131        options: &impl GetOperatorOptions,
132    ) -> Result<(), SparseLinearAlgebraError>;
133}
134
135macro_rules! implement_monoid_reducer {
136    ($value_type:ty, $graphblas_implementation_type:ty, $matrix_reducer_operator:ident, $vector_reducer_operator:ident, $convert_to_type:ident) => {
137        impl MonoidScalarReducer<$value_type> for MonoidReducer {
138            fn matrix_to_scalar(
139                &self,
140                operator: &impl Monoid<$value_type>,
141                argument: &impl GetGraphblasSparseMatrix,
142                accumulator: &impl AccumulatorBinaryOperator<$value_type>,
143                product: &mut $value_type,
144                options: &impl GetOptionsForOperatorWithMatrixArgument,
145            ) -> Result<(), SparseLinearAlgebraError> {
146                let context = argument.context_ref();
147                let mut tmp_product = product.clone().to_type()?;
148
149                // TODO: support detailed error information
150                context.call_without_detailed_error_information(|| unsafe {
151                    $matrix_reducer_operator(
152                        &mut tmp_product,
153                        accumulator.accumulator_graphblas_type(),
154                        operator.graphblas_type(),
155                        argument.graphblas_matrix(),
156                        options.graphblas_descriptor(),
157                    )
158                })?;
159
160                $convert_to_type!(tmp_product, $value_type);
161                *product = tmp_product;
162                Ok(())
163            }
164
165            // TODO: support detailed error information
166            fn vector_to_scalar(
167                &self,
168                operator: &impl Monoid<$value_type>,
169                argument: &impl GetGraphblasSparseVector,
170                accumulator: &impl AccumulatorBinaryOperator<$value_type>,
171                product: &mut $value_type,
172                options: &impl GetOperatorOptions,
173            ) -> Result<(), SparseLinearAlgebraError> {
174                let context = argument.context_ref();
175                let mut tmp_product = product.clone().to_type()?;
176
177                context.call_without_detailed_error_information(|| unsafe {
178                    $vector_reducer_operator(
179                        &mut tmp_product,
180                        accumulator.accumulator_graphblas_type(),
181                        operator.graphblas_type(),
182                        argument.graphblas_vector(),
183                        options.graphblas_descriptor(),
184                    )
185                })?;
186
187                $convert_to_type!(tmp_product, $value_type);
188                *product = tmp_product;
189                Ok(())
190            }
191        }
192    };
193}
194
195implement_macro_for_all_value_types_and_2_typed_graphblas_functions_with_mutable_scalar_type_conversion!(
196    implement_monoid_reducer,
197    GrB_Matrix_reduce,
198    GrB_Vector_reduce
199);
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    use crate::collections::Collection;
206    use crate::context::Context;
207    use crate::operators::binary_operator::Assignment;
208    use crate::operators::binary_operator::First;
209    use crate::operators::mask::SelectEntireVector;
210    use crate::operators::monoid::Plus as MonoidPlus;
211    use crate::operators::options::OperatorOptions;
212    use crate::operators::options::OptionsForOperatorWithMatrixArgument;
213
214    use crate::collections::sparse_matrix::operations::FromMatrixElementList;
215    use crate::collections::sparse_matrix::GetMatrixDimensions;
216    use crate::collections::sparse_matrix::{MatrixElementList, Size, SparseMatrix};
217    use crate::collections::sparse_vector::operations::FromVectorElementList;
218    use crate::collections::sparse_vector::operations::GetSparseVectorElementValue;
219    use crate::collections::sparse_vector::{SparseVector, VectorElementList};
220    use crate::value_type::utilities_to_implement_traits_for_all_value_types::implement_macro_for_all_value_types_except_bool;
221
222    macro_rules! test_monoid {
223        ($value_type:ty) => {
224            paste::paste! {
225                #[test]
226                fn [<test_monoid_to_vector_reducer_ $value_type>]() {
227                    let context = Context::init_default().unwrap();
228
229                    let element_list = MatrixElementList::<$value_type>::from_element_vector(vec![
230                        (1, 1, 1 as $value_type).into(),
231                        (1, 5, 1 as $value_type).into(),
232                        (2, 1, 2 as $value_type).into(),
233                        (4, 2, 4 as $value_type).into(),
234                        (5, 2, 5 as $value_type).into(),
235                    ]);
236
237                    let matrix_size: Size = (10, 15).into();
238                    let matrix = SparseMatrix::<$value_type>::from_element_list(
239                        context.clone(),
240                        matrix_size,
241                        element_list,
242                        &First::<$value_type>::new(),
243                    )
244                    .unwrap();
245
246                    let mut product_vector =
247                        SparseVector::<$value_type>::new(context.clone(), matrix_size.row_height()).unwrap();
248
249                    let reducer = MonoidReducer::new(
250                    );
251
252                    reducer.to_column_vector(
253                        &MonoidPlus::<$value_type>::new(),
254                        &matrix, &Assignment::<$value_type>::new(),
255                        &mut product_vector, &SelectEntireVector::new(context.clone()),
256                        &OptionsForOperatorWithMatrixArgument::new_default()).unwrap();
257
258                    println!("{}", product_vector);
259
260                    assert_eq!(product_vector.number_of_stored_elements().unwrap(), 4);
261                    assert_eq!(product_vector.element_value_or_default(1).unwrap(), 2 as $value_type);
262                    assert_eq!(product_vector.element_value_or_default(2).unwrap(), 2 as $value_type);
263                    assert_eq!(product_vector.element_value(9).unwrap(), None);
264
265                    let mask_element_list = VectorElementList::<$value_type>::from_element_vector(vec![
266                        (1, 1 as $value_type).into(),
267                        (2, 2 as $value_type).into(),
268                        (4, 4 as $value_type).into(),
269                        // (5, 5).into(),
270                    ]);
271
272                    let mask = SparseVector::<$value_type>::from_element_list(
273                        context.clone(),
274                        matrix_size.row_height(),
275                        mask_element_list,
276                        &First::<$value_type>::new(),
277                    )
278                    .unwrap();
279
280                    let mut product_vector =
281                        SparseVector::<$value_type>::new(context.clone(), matrix_size.row_height()).unwrap();
282
283                    reducer
284                        .to_column_vector(
285                            &MonoidPlus::<$value_type>::new(),
286                            &matrix, &Assignment::<$value_type>::new(),
287                            &mut product_vector,
288                            &mask,
289                            &OptionsForOperatorWithMatrixArgument::new_default())
290                        .unwrap();
291
292                    println!("{}", matrix);
293                    println!("{}", product_vector);
294
295                    assert_eq!(product_vector.number_of_stored_elements().unwrap(), 3);
296                    assert_eq!(product_vector.element_value_or_default(1).unwrap(), 2 as $value_type);
297                    assert_eq!(product_vector.element_value_or_default(2).unwrap(), 2 as $value_type);
298                    assert_eq!(product_vector.element_value(5).unwrap(), None);
299                    assert_eq!(product_vector.element_value(9).unwrap(), None);
300                }
301
302                #[test]
303                fn [<test_monoid_to_scalar_reducer_for_matrix_ $value_type>]() {
304                    let context = Context::init_default().unwrap();
305
306                    let element_list = MatrixElementList::<$value_type>::from_element_vector(vec![
307                        (1, 1, 1 as $value_type).into(),
308                        (1, 5, 1 as $value_type).into(),
309                        (2, 1, 2 as $value_type).into(),
310                        (4, 2, 4 as $value_type).into(),
311                        (5, 2, 5 as $value_type).into(),
312                    ]);
313
314                    let matrix_size: Size = (10, 15).into();
315                    let matrix = SparseMatrix::<$value_type>::from_element_list(
316                        context.clone(),
317                        matrix_size,
318                        element_list,
319                        &First::<$value_type>::new(),
320                    )
321                    .unwrap();
322
323                    let mut product = 1 as $value_type;
324
325                    let reducer = MonoidReducer::new(
326                    );
327
328                    reducer.matrix_to_scalar(
329                        &MonoidPlus::<$value_type>::new(),
330                        &matrix,
331                        &Assignment::new(),
332                        &mut product,
333                        &OptionsForOperatorWithMatrixArgument::new_default(),).unwrap();
334
335                    println!("{}", product);
336
337                    assert_eq!(product, 13 as $value_type);
338                }
339
340                #[test]
341                fn [<test_monoid_to_scalar_reducer_for_vector_ $value_type>]() {
342                    let context = Context::init_default().unwrap();
343
344                    let element_list = VectorElementList::<$value_type>::from_element_vector(vec![
345                        (1, 1 as $value_type).into(),
346                        (2, 2 as $value_type).into(),
347                        (4, 4 as $value_type).into(),
348                        (5, 5 as $value_type).into(),
349                    ]);
350
351                    let vector_length = 10;
352                    let vector = SparseVector::<$value_type>::from_element_list(
353                        context.clone(),
354                        vector_length,
355                        element_list,
356                        &First::<$value_type>::new(),
357                    )
358                    .unwrap();
359
360                    let mut product = 0 as $value_type;
361
362                    let reducer = MonoidReducer::new(
363                    );
364
365                    reducer.vector_to_scalar(&MonoidPlus::<$value_type>::new(), &vector, &Assignment::new(), &mut product, &OperatorOptions::new_default(),).unwrap();
366
367                    println!("{}", product);
368
369                    assert_eq!(product, 12 as $value_type);
370                }
371            }
372        };
373    }
374
375    implement_macro_for_all_value_types_except_bool!(test_monoid);
376}