cairn_knowledge_graph/operations/selection/operators/
and.rs

1use once_cell::sync::Lazy;
2
3use graphblas_sparse_linear_algebra::operators::{
4    element_wise_multiplication::ElementWiseVectorMultiplicationMonoidOperator, monoid::LogicalAnd,
5    options::OperatorOptions,
6};
7
8use crate::error::GraphComputingError;
9use crate::operations::selection::vertex_selection::VertexSelection;
10
11static DEFAULT_GRAPHBLAS_OPERATOR_OPTIONS: Lazy<OperatorOptions> =
12    Lazy::new(|| OperatorOptions::new_default());
13
14static GRAPHBLAS_VECTOR_AND_OPERATOR: Lazy<ElementWiseVectorMultiplicationMonoidOperator<bool>> =
15    Lazy::new(|| {
16        ElementWiseVectorMultiplicationMonoidOperator::<bool>::new(
17            &LogicalAnd::<bool>::new(),
18            &DEFAULT_GRAPHBLAS_OPERATOR_OPTIONS,
19            None,
20        )
21    });
22
23pub trait AndOperator<RightHandSide = Self> {
24    type Output;
25    fn and(&self, right_hand_side: &RightHandSide) -> Result<Self::Output, GraphComputingError>;
26    // TODO: consider introducing a selection/exclusion mask for improved API clarity
27    fn and_with_mask(
28        &self,
29        right_hand_side: &RightHandSide,
30        mask: &RightHandSide,
31    ) -> Result<Self::Output, GraphComputingError>;
32}
33
34impl<'g> AndOperator for VertexSelection<'g> {
35    type Output = VertexSelection<'g>;
36
37    fn and(&self, right_hand_side: &Self) -> Result<Self, GraphComputingError> {
38        // TODO: Size checking
39
40        let mut resulting_vertex_selection = self.clone();
41        GRAPHBLAS_VECTOR_AND_OPERATOR.apply(
42            self.vertex_mask_ref(),
43            right_hand_side.vertex_mask_ref(),
44            resulting_vertex_selection.vertex_mask_mut_ref(),
45        )?;
46        Ok(resulting_vertex_selection)
47    }
48
49    /// The operator applies to all coordinates that the mask selects. Elements in the left-hand-side that the mask does not select remain unchanged.
50    fn and_with_mask(
51        &self,
52        right_hand_side: &Self,
53        mask: &Self,
54    ) -> Result<Self, GraphComputingError> {
55        // TODO: Size checking
56
57        let mut resulting_vertex_selection = self.clone();
58        GRAPHBLAS_VECTOR_AND_OPERATOR.apply_with_mask(
59            mask.vertex_mask_ref(),
60            self.vertex_mask_ref(),
61            right_hand_side.vertex_mask_ref(),
62            resulting_vertex_selection.vertex_mask_mut_ref(),
63        )?;
64        Ok(resulting_vertex_selection)
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    use crate::graph::vertex::VertexValue;
73    use crate::operations::select_vertex::SelectVertex;
74
75    use crate::tests::standard_graph_for_testing::standard_graph_for_testing;
76
77    #[test]
78    fn test_and_operator_for_vertex_selection() {
79        let graph = standard_graph_for_testing();
80
81        let negative_selection = graph
82            .select_vertices_connected_to_vertex_by_key(String::from("sign"), &"negative")
83            .unwrap();
84        let integer_selection = graph
85            .select_vertices_connected_to_vertex_by_key(String::from("is_a"), &"integer")
86            .unwrap();
87
88        let negative_integer_selection = negative_selection.and(&integer_selection).unwrap();
89        let negative_integers = negative_integer_selection.vertex_values_ref().unwrap();
90
91        assert_eq!(negative_integers, vec!(&VertexValue::Integer8Bit(-1)));
92    }
93
94    #[test]
95    fn test_and_operator_for_vertex_selection_with_mask() {
96        let graph = standard_graph_for_testing();
97
98        let negative_selection = graph
99            .select_vertices_connected_to_vertex_by_key(String::from("sign"), &"negative")
100            .unwrap();
101        let integer_selection = graph
102            .select_vertices_connected_to_vertex_by_key(String::from("is_a"), &"integer")
103            .unwrap();
104        let real_number_selection = graph
105            .select_vertices_connected_to_vertex_by_key(String::from("is_a"), &"real_number")
106            .unwrap();
107
108        let selection_of_real_numbers_without_positive_integers = real_number_selection
109            .and_with_mask(&negative_selection, &integer_selection)
110            .unwrap();
111        let real_numbers_without_positive_integers =
112            selection_of_real_numbers_without_positive_integers
113                .vertex_values_ref()
114                .unwrap();
115
116        assert_eq!(real_numbers_without_positive_integers.len(), 4);
117        assert!(real_numbers_without_positive_integers.contains(&&VertexValue::Integer8Bit(-1)));
118        assert!(real_numbers_without_positive_integers
119            .contains(&&VertexValue::FloatingPoint32Bit(-1.1)));
120        assert!(
121            real_numbers_without_positive_integers.contains(&&VertexValue::FloatingPoint32Bit(1.1))
122        );
123        assert!(
124            real_numbers_without_positive_integers.contains(&&VertexValue::FloatingPoint32Bit(1.2))
125        );
126    }
127}