graphblas_sparse_linear_algebra 0.59.0

Wrapper for SuiteSparse:GraphBLAS
Documentation
use std::convert::TryInto;

use crate::collections::sparse_matrix::GetGraphblasSparseMatrix;
use crate::collections::sparse_vector::GetGraphblasSparseVector;
use crate::context::CallGraphBlasContext;
use crate::error::SparseLinearAlgebraError;
use crate::graphblas_bindings::{
    GrB_Matrix_reduce_BOOL, GrB_Matrix_reduce_FP32, GrB_Matrix_reduce_FP64,
    GrB_Matrix_reduce_INT16, GrB_Matrix_reduce_INT32, GrB_Matrix_reduce_INT64,
    GrB_Matrix_reduce_INT8, GrB_Matrix_reduce_Monoid, GrB_Matrix_reduce_UINT16,
    GrB_Matrix_reduce_UINT32, GrB_Matrix_reduce_UINT64, GrB_Matrix_reduce_UINT8,
    GrB_Vector_reduce_BOOL, GrB_Vector_reduce_FP32, GrB_Vector_reduce_FP64,
    GrB_Vector_reduce_INT16, GrB_Vector_reduce_INT32, GrB_Vector_reduce_INT64,
    GrB_Vector_reduce_INT8, GrB_Vector_reduce_UINT16, GrB_Vector_reduce_UINT32,
    GrB_Vector_reduce_UINT64, GrB_Vector_reduce_UINT8,
};
use crate::operators::binary_operator::AccumulatorBinaryOperator;
use crate::operators::mask::VectorMask;
use crate::operators::monoid::Monoid;
use crate::operators::options::{
    GetOperatorOptions, GetOptionsForOperatorWithMatrixArgument, WithTransposeMatrixArgument,
};
use crate::value_type::utilities_to_implement_traits_for_all_value_types::{
    convert_mut_scalar_to_type, identity_conversion,
    implement_macro_for_all_value_types_and_2_typed_graphblas_functions_with_mutable_scalar_type_conversion,
};
use crate::value_type::{ConvertScalar, ValueType};

// Implemented methods do not provide mutable access to GraphBLAS operators or options.
// Code review must consider that no mtable access is provided.
// https://doc.rust-lang.org/nomicon/send-and-sync.html
unsafe impl Send for MonoidReducer {}
unsafe impl Sync for MonoidReducer {}

#[derive(Debug, Clone)]
pub struct MonoidReducer {}

impl MonoidReducer {
    pub fn new() -> Self {
        Self {}
    }
}

pub trait MonoidVectorReducer<EvaluationDomain: ValueType> {
    fn to_column_vector(
        &self,
        operator: &impl Monoid<EvaluationDomain>,
        argument: &impl GetGraphblasSparseMatrix,
        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
        product: &mut impl GetGraphblasSparseVector,
        mask: &impl VectorMask,
        options: &impl GetOptionsForOperatorWithMatrixArgument,
    ) -> Result<(), SparseLinearAlgebraError>;

    fn to_row_vector(
        &self,
        operator: &impl Monoid<EvaluationDomain>,
        argument: &impl GetGraphblasSparseMatrix,
        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
        product: &mut impl GetGraphblasSparseVector,
        mask: &impl VectorMask,
        options: &(impl GetOptionsForOperatorWithMatrixArgument + WithTransposeMatrixArgument),
    ) -> Result<(), SparseLinearAlgebraError>;
}

impl<EvaluationDomain: ValueType> MonoidVectorReducer<EvaluationDomain> for MonoidReducer {
    fn to_column_vector(
        &self,
        operator: &impl Monoid<EvaluationDomain>,
        argument: &impl GetGraphblasSparseMatrix,
        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
        product: &mut impl GetGraphblasSparseVector,
        mask: &impl VectorMask,
        options: &impl GetOptionsForOperatorWithMatrixArgument,
    ) -> Result<(), SparseLinearAlgebraError> {
        let context = product.context_ref();

        context.call(
            || unsafe {
                GrB_Matrix_reduce_Monoid(
                    product.graphblas_vector_ptr(),
                    mask.graphblas_vector_ptr(),
                    accumulator.accumulator_graphblas_type(),
                    operator.graphblas_type(),
                    argument.graphblas_matrix_ptr(),
                    options.graphblas_descriptor(),
                )
            },
            unsafe { product.graphblas_vector_ptr_ref() },
        )?;

        Ok(())
    }

    fn to_row_vector(
        &self,
        operator: &impl Monoid<EvaluationDomain>,
        argument: &impl GetGraphblasSparseMatrix,
        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
        product: &mut impl GetGraphblasSparseVector,
        mask: &impl VectorMask,
        options: &(impl GetOptionsForOperatorWithMatrixArgument + WithTransposeMatrixArgument),
    ) -> Result<(), SparseLinearAlgebraError> {
        self.to_column_vector(
            operator,
            argument,
            accumulator,
            product,
            mask,
            &options.with_negated_transpose_matrix_argument(),
        )
    }
}

pub trait MonoidScalarReducer<EvaluationDomain: ValueType> {
    fn matrix_to_scalar(
        &self,
        operator: &impl Monoid<EvaluationDomain>,
        argument: &impl GetGraphblasSparseMatrix,
        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
        product: &mut EvaluationDomain,
        options: &impl GetOptionsForOperatorWithMatrixArgument,
    ) -> Result<(), SparseLinearAlgebraError>;

    fn vector_to_scalar(
        &self,
        operator: &impl Monoid<EvaluationDomain>,
        argument: &impl GetGraphblasSparseVector,
        accumulator: &impl AccumulatorBinaryOperator<EvaluationDomain>,
        product: &mut EvaluationDomain,
        options: &impl GetOperatorOptions,
    ) -> Result<(), SparseLinearAlgebraError>;
}

macro_rules! implement_monoid_reducer {
    ($value_type:ty, $graphblas_implementation_type:ty, $matrix_reducer_operator:ident, $vector_reducer_operator:ident, $convert_to_type:ident) => {
        impl MonoidScalarReducer<$value_type> for MonoidReducer {
            fn matrix_to_scalar(
                &self,
                operator: &impl Monoid<$value_type>,
                argument: &impl GetGraphblasSparseMatrix,
                accumulator: &impl AccumulatorBinaryOperator<$value_type>,
                product: &mut $value_type,
                options: &impl GetOptionsForOperatorWithMatrixArgument,
            ) -> Result<(), SparseLinearAlgebraError> {
                let context = argument.context_ref();
                let mut tmp_product = product.clone().to_type()?;

                // TODO: support detailed error information
                context.call_without_detailed_error_information(|| unsafe {
                    $matrix_reducer_operator(
                        &mut tmp_product,
                        accumulator.accumulator_graphblas_type(),
                        operator.graphblas_type(),
                        argument.graphblas_matrix_ptr(),
                        options.graphblas_descriptor(),
                    )
                })?;

                $convert_to_type!(tmp_product, $value_type);
                *product = tmp_product;
                Ok(())
            }

            // TODO: support detailed error information
            fn vector_to_scalar(
                &self,
                operator: &impl Monoid<$value_type>,
                argument: &impl GetGraphblasSparseVector,
                accumulator: &impl AccumulatorBinaryOperator<$value_type>,
                product: &mut $value_type,
                options: &impl GetOperatorOptions,
            ) -> Result<(), SparseLinearAlgebraError> {
                let context = argument.context_ref();
                let mut tmp_product = product.clone().to_type()?;

                context.call_without_detailed_error_information(|| unsafe {
                    $vector_reducer_operator(
                        &mut tmp_product,
                        accumulator.accumulator_graphblas_type(),
                        operator.graphblas_type(),
                        argument.graphblas_vector_ptr(),
                        options.graphblas_descriptor(),
                    )
                })?;

                $convert_to_type!(tmp_product, $value_type);
                *product = tmp_product;
                Ok(())
            }
        }
    };
}

implement_macro_for_all_value_types_and_2_typed_graphblas_functions_with_mutable_scalar_type_conversion!(
    implement_monoid_reducer,
    GrB_Matrix_reduce,
    GrB_Vector_reduce
);

#[cfg(test)]
mod tests {
    use super::*;

    use crate::collections::Collection;
    use crate::context::Context;
    use crate::operators::binary_operator::Assignment;
    use crate::operators::binary_operator::First;
    use crate::operators::mask::SelectEntireVector;
    use crate::operators::monoid::Plus as MonoidPlus;
    use crate::operators::options::OperatorOptions;
    use crate::operators::options::OptionsForOperatorWithMatrixArgument;

    use crate::collections::sparse_matrix::operations::FromMatrixElementList;
    use crate::collections::sparse_matrix::GetMatrixDimensions;
    use crate::collections::sparse_matrix::{MatrixElementList, Size, SparseMatrix};
    use crate::collections::sparse_vector::operations::FromVectorElementList;
    use crate::collections::sparse_vector::operations::GetSparseVectorElementValue;
    use crate::collections::sparse_vector::{SparseVector, VectorElementList};
    use crate::value_type::utilities_to_implement_traits_for_all_value_types::implement_macro_for_all_value_types_except_bool;

    macro_rules! test_monoid {
        ($value_type:ty) => {
            paste::paste! {
                #[test]
                fn [<test_monoid_to_vector_reducer_ $value_type>]() {
                    let context = Context::init_default().unwrap();

                    let element_list = MatrixElementList::<$value_type>::from_element_vector(vec![
                        (1, 1, 1 as $value_type).into(),
                        (1, 5, 1 as $value_type).into(),
                        (2, 1, 2 as $value_type).into(),
                        (4, 2, 4 as $value_type).into(),
                        (5, 2, 5 as $value_type).into(),
                    ]);

                    let matrix_size: Size = (10, 15).into();
                    let matrix = SparseMatrix::<$value_type>::from_element_list(
                        context.clone(),
                        matrix_size,
                        element_list,
                        &First::<$value_type>::new(),
                    )
                    .unwrap();

                    let mut product_vector =
                        SparseVector::<$value_type>::new(context.clone(), matrix_size.row_height()).unwrap();

                    let reducer = MonoidReducer::new(
                    );

                    reducer.to_column_vector(
                        &MonoidPlus::<$value_type>::new(),
                        &matrix, &Assignment::<$value_type>::new(),
                        &mut product_vector, &SelectEntireVector::new(context.clone()),
                        &OptionsForOperatorWithMatrixArgument::new_default()).unwrap();

                    println!("{}", product_vector);

                    assert_eq!(product_vector.number_of_stored_elements().unwrap(), 4);
                    assert_eq!(product_vector.element_value_or_default(1).unwrap(), 2 as $value_type);
                    assert_eq!(product_vector.element_value_or_default(2).unwrap(), 2 as $value_type);
                    assert_eq!(product_vector.element_value(9).unwrap(), None);

                    let mask_element_list = VectorElementList::<$value_type>::from_element_vector(vec![
                        (1, 1 as $value_type).into(),
                        (2, 2 as $value_type).into(),
                        (4, 4 as $value_type).into(),
                        // (5, 5).into(),
                    ]);

                    let mask = SparseVector::<$value_type>::from_element_list(
                        context.clone(),
                        matrix_size.row_height(),
                        mask_element_list,
                        &First::<$value_type>::new(),
                    )
                    .unwrap();

                    let mut product_vector =
                        SparseVector::<$value_type>::new(context.clone(), matrix_size.row_height()).unwrap();

                    reducer
                        .to_column_vector(
                            &MonoidPlus::<$value_type>::new(),
                            &matrix, &Assignment::<$value_type>::new(),
                            &mut product_vector,
                            &mask,
                            &OptionsForOperatorWithMatrixArgument::new_default())
                        .unwrap();

                    println!("{}", matrix);
                    println!("{}", product_vector);

                    assert_eq!(product_vector.number_of_stored_elements().unwrap(), 3);
                    assert_eq!(product_vector.element_value_or_default(1).unwrap(), 2 as $value_type);
                    assert_eq!(product_vector.element_value_or_default(2).unwrap(), 2 as $value_type);
                    assert_eq!(product_vector.element_value(5).unwrap(), None);
                    assert_eq!(product_vector.element_value(9).unwrap(), None);
                }

                #[test]
                fn [<test_monoid_to_scalar_reducer_for_matrix_ $value_type>]() {
                    let context = Context::init_default().unwrap();

                    let element_list = MatrixElementList::<$value_type>::from_element_vector(vec![
                        (1, 1, 1 as $value_type).into(),
                        (1, 5, 1 as $value_type).into(),
                        (2, 1, 2 as $value_type).into(),
                        (4, 2, 4 as $value_type).into(),
                        (5, 2, 5 as $value_type).into(),
                    ]);

                    let matrix_size: Size = (10, 15).into();
                    let matrix = SparseMatrix::<$value_type>::from_element_list(
                        context.clone(),
                        matrix_size,
                        element_list,
                        &First::<$value_type>::new(),
                    )
                    .unwrap();

                    let mut product = 1 as $value_type;

                    let reducer = MonoidReducer::new(
                    );

                    reducer.matrix_to_scalar(
                        &MonoidPlus::<$value_type>::new(),
                        &matrix,
                        &Assignment::new(),
                        &mut product,
                        &OptionsForOperatorWithMatrixArgument::new_default(),).unwrap();

                    println!("{}", product);

                    assert_eq!(product, 13 as $value_type);
                }

                #[test]
                fn [<test_monoid_to_scalar_reducer_for_vector_ $value_type>]() {
                    let context = Context::init_default().unwrap();

                    let element_list = VectorElementList::<$value_type>::from_element_vector(vec![
                        (1, 1 as $value_type).into(),
                        (2, 2 as $value_type).into(),
                        (4, 4 as $value_type).into(),
                        (5, 5 as $value_type).into(),
                    ]);

                    let vector_length = 10;
                    let vector = SparseVector::<$value_type>::from_element_list(
                        context.clone(),
                        vector_length,
                        element_list,
                        &First::<$value_type>::new(),
                    )
                    .unwrap();

                    let mut product = 0 as $value_type;

                    let reducer = MonoidReducer::new(
                    );

                    reducer.vector_to_scalar(&MonoidPlus::<$value_type>::new(), &vector, &Assignment::new(), &mut product, &OperatorOptions::new_default(),).unwrap();

                    println!("{}", product);

                    assert_eq!(product, 12 as $value_type);
                }
            }
        };
    }

    implement_macro_for_all_value_types_except_bool!(test_monoid);
}