Skip to main content

cairn_knowledge_graph/operations/selection/operators/
exclusive_or.rs

1use once_cell::sync::Lazy;
2
3use graphblas_sparse_linear_algebra::operators::{
4    element_wise_addition::ElementWiseVectorAdditionMonoidOperator, monoid::LogicalExclusiveOr,
5    options::OperatorOptions,
6};
7
8use crate::error::GraphComputingError;
9
10use crate::operations::selection::vertex_selection::VertexSelection;
11
12static DEFAULT_GRAPHBLAS_OPERATOR_OPTIONS: Lazy<OperatorOptions> =
13    Lazy::new(|| OperatorOptions::new_default());
14
15static GRAPHBLAS_VECTOR_EXCLUSIVE_OR_OPERATOR: Lazy<ElementWiseVectorAdditionMonoidOperator<bool>> =
16    Lazy::new(|| {
17        ElementWiseVectorAdditionMonoidOperator::<bool>::new(
18            &LogicalExclusiveOr::<bool>::new(),
19            &DEFAULT_GRAPHBLAS_OPERATOR_OPTIONS,
20            None,
21        )
22    });
23
24pub trait ExclusiveOrOperator<RightHandSide = Self> {
25    type Output;
26    fn exclusive_or(
27        &self,
28        right_hand_side: &RightHandSide,
29    ) -> Result<Self::Output, GraphComputingError>;
30    fn exclusive_or_with_mask(
31        &self,
32        right_hand_side: &RightHandSide,
33        mask: &RightHandSide,
34    ) -> Result<Self::Output, GraphComputingError>;
35}
36
37impl<'g> ExclusiveOrOperator for VertexSelection<'g> {
38    type Output = VertexSelection<'g>;
39
40    fn exclusive_or(&self, right_hand_side: &Self) -> Result<Self, GraphComputingError> {
41        // TODO: Size checking
42
43        let mut resulting_vertex_selection = self.clone();
44        GRAPHBLAS_VECTOR_EXCLUSIVE_OR_OPERATOR.apply(
45            self.vertex_mask_ref(),
46            right_hand_side.vertex_mask_ref(),
47            resulting_vertex_selection.vertex_mask_mut_ref(),
48        )?;
49        Ok(resulting_vertex_selection)
50    }
51
52    /// The operator applies to all coordinates that the mask selects. Elements in the left-hand-side that the mask does not select remain unchanged.
53    fn exclusive_or_with_mask(
54        &self,
55        right_hand_side: &Self,
56        mask: &Self,
57    ) -> Result<Self, GraphComputingError> {
58        // TODO: Size checking
59
60        let mut resulting_vertex_selection = self.clone();
61        GRAPHBLAS_VECTOR_EXCLUSIVE_OR_OPERATOR.apply_with_mask(
62            mask.vertex_mask_ref(),
63            self.vertex_mask_ref(),
64            right_hand_side.vertex_mask_ref(),
65            resulting_vertex_selection.vertex_mask_mut_ref(),
66        )?;
67        Ok(resulting_vertex_selection)
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    use crate::graph::vertex::VertexValue;
76    use crate::operations::select_vertex::SelectVertex;
77
78    use crate::tests::standard_graph_for_testing::standard_graph_for_testing;
79
80    #[test]
81    fn test_exclusive_or_operator_for_vertex_selection() {
82        let graph = standard_graph_for_testing();
83
84        let larger_than_one_selection = graph
85            .select_vertices_connected_to_vertex_by_key(String::from("larger_than"), &"1")
86            .unwrap();
87        let integer_selection = graph
88            .select_vertices_connected_to_vertex_by_key(String::from("is_a"), &"integer")
89            .unwrap();
90
91        let larger_than_one_or_integer_but_not_both_selection = larger_than_one_selection
92            .exclusive_or(&integer_selection)
93            .unwrap();
94        let mut larger_than_one_or_integer_but_not_both =
95            larger_than_one_or_integer_but_not_both_selection
96                .vertex_values_ref()
97                .unwrap();
98
99        larger_than_one_or_integer_but_not_both.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
100        ();
101        larger_than_one_or_integer_but_not_both.dedup();
102
103        assert_eq!(larger_than_one_or_integer_but_not_both.len(), 5);
104        assert!(larger_than_one_or_integer_but_not_both.contains(&&VertexValue::Integer8Bit(-1)));
105        assert!(
106            larger_than_one_or_integer_but_not_both.contains(&&VertexValue::UnsignedInteger8Bit(0))
107        );
108        assert!(
109            larger_than_one_or_integer_but_not_both.contains(&&VertexValue::UnsignedInteger8Bit(1))
110        );
111        assert!(larger_than_one_or_integer_but_not_both
112            .contains(&&VertexValue::FloatingPoint32Bit(1.1)));
113        assert!(larger_than_one_or_integer_but_not_both
114            .contains(&&VertexValue::FloatingPoint32Bit(1.2)));
115    }
116
117    #[test]
118    fn test_exclusive_or_operator_for_vertex_selection_with_mask() {
119        let graph = standard_graph_for_testing();
120
121        let larger_than_one_selection = graph
122            .select_vertices_connected_to_vertex_by_key(String::from("larger_than"), &"1")
123            .unwrap();
124        let equal_to_one_selection = graph
125            .select_vertices_connected_to_vertex_by_key(String::from("equal_to"), &"1_duplicate")
126            .unwrap();
127        let integer_selection = graph
128            .select_vertices_connected_to_vertex_by_key(String::from("is_a"), &"integer")
129            .unwrap();
130
131        let selection_of_integers_equal_to_or_larger_than_one = equal_to_one_selection
132            .exclusive_or_with_mask(&larger_than_one_selection, &integer_selection)
133            .unwrap();
134        let mut integers_equal_to_or_larger_than_one =
135            selection_of_integers_equal_to_or_larger_than_one
136                .vertex_values_ref()
137                .unwrap();
138
139        integers_equal_to_or_larger_than_one.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
140        ();
141        integers_equal_to_or_larger_than_one.dedup();
142
143        assert_eq!(integers_equal_to_or_larger_than_one.len(), 2);
144        assert!(
145            integers_equal_to_or_larger_than_one.contains(&&VertexValue::UnsignedInteger8Bit(1))
146        );
147        assert!(
148            integers_equal_to_or_larger_than_one.contains(&&VertexValue::UnsignedInteger8Bit(2))
149        );
150    }
151}