Skip to main content

cairn_knowledge_graph/operations/selection/
vertex_selection.rs

1use once_cell::sync::Lazy;
2
3use graphblas_sparse_linear_algebra::operators::extract::SubVectorExtractor;
4use graphblas_sparse_linear_algebra::operators::insert::{
5    InsertScalarIntoVector, InsertScalarIntoVectorTrait,
6};
7use graphblas_sparse_linear_algebra::operators::options::OperatorOptions;
8use graphblas_sparse_linear_algebra::operators::{
9    element_wise_multiplication::ElementWiseVectorMultiplicationMonoidOperator, monoid::LogicalAnd,
10};
11use graphblas_sparse_linear_algebra::util::ElementIndexSelector;
12use graphblas_sparse_linear_algebra::value_types::sparse_vector::{
13    GetVectorElementList, SparseVector, VectorElementList,
14};
15
16use crate::error::{GraphComputingError, LogicError, LogicErrorType};
17use crate::graph::graph::{Graph, GraphTrait};
18use crate::graph::vertex::{Vertex, VertexIndex, VertexValue};
19
20static DEFAULT_GRAPHBLAS_OPERATOR_OPTIONS: Lazy<OperatorOptions> =
21    Lazy::new(|| OperatorOptions::new_default());
22
23static GRAPHBLAS_OPERATOR_OPTIONS_TO_USE_MASK_COMPLEMENT: Lazy<OperatorOptions> =
24    Lazy::new(|| OperatorOptions::new(false, false, true, false, false));
25
26static GRAPHBLAS_SUB_VECTOR_EXTRACTOR: Lazy<SubVectorExtractor<bool, bool>> =
27    Lazy::new(|| SubVectorExtractor::<bool, bool>::new(&DEFAULT_GRAPHBLAS_OPERATOR_OPTIONS, None));
28
29static GRAPHBLAS_SCALAR_INTO_VECTOR_INSERTER_WITH_MASK_COMPLEMENT: Lazy<
30    InsertScalarIntoVector<bool, bool>,
31> = Lazy::new(|| {
32    InsertScalarIntoVector::<bool, bool>::new(
33        &GRAPHBLAS_OPERATOR_OPTIONS_TO_USE_MASK_COMPLEMENT,
34        None,
35    )
36});
37
38static GRAPHBLAS_VECTOR_AND_OPERATOR: Lazy<ElementWiseVectorMultiplicationMonoidOperator<bool>> =
39    Lazy::new(|| {
40        ElementWiseVectorMultiplicationMonoidOperator::<bool>::new(
41            &LogicalAnd::<bool>::new(),
42            &DEFAULT_GRAPHBLAS_OPERATOR_OPTIONS,
43            None,
44        )
45    });
46
47// pub trait VertexSelectionTrait {
48//     fn select_vertex_ref(&self) -> Result<Vec<&Vertex>, GraphComputingError>;
49//     // fn select_vertex_index_ref(&self) -> Result<Vec<VertexIndex>, GraphComputingError>;
50//     fn select_vertex_key_ref(&self) -> Result<Vec<&str>, GraphComputingError>;
51//     fn select_vertex_value_ref(&self) -> Result<Vec<&VertexValue>, GraphComputingError>;
52// }
53
54#[derive(Clone, Debug)]
55pub struct VertexSelection<'g> {
56    graph: &'g Graph,
57    vertex_mask: SparseVector<bool>,
58}
59
60impl<'g> VertexSelection<'g> {
61    pub(crate) fn new(
62        graph: &'g Graph,
63        vertex_mask: SparseVector<bool>,
64    ) -> Result<Self, GraphComputingError> {
65        #[cfg(debug_assertions)]
66        let graph_vertex_capacity = graph.vertex_capacity()?;
67        if vertex_mask.length()? != graph_vertex_capacity {
68            return Err(LogicError::new(
69                LogicErrorType::DimensionMismatch,
70                format!(
71                    "Length of vertex_mask {:?}, does not match the graph's vertex capacity {:?}",
72                    vertex_mask.length()?,
73                    graph_vertex_capacity
74                ),
75                None,
76            )
77            .into());
78        }
79
80        Ok(Self { graph, vertex_mask })
81    }
82
83    pub(crate) fn vertex_mask_ref(&self) -> &SparseVector<bool> {
84        &self.vertex_mask
85    }
86
87    pub(crate) fn vertex_mask_mut_ref(&mut self) -> &mut SparseVector<bool> {
88        &mut self.vertex_mask
89    }
90
91    pub(crate) fn to_full_vertex_mask(&self) -> Result<SparseVector<bool>, GraphComputingError> {
92        let mut full_vertex_mask = self.vertex_mask.clone();
93        GRAPHBLAS_SCALAR_INTO_VECTOR_INSERTER_WITH_MASK_COMPLEMENT.apply_with_mask(
94            &mut full_vertex_mask,
95            &ElementIndexSelector::Index(
96                &self
97                    .graph
98                    .index_mask_with_all_vertices()
99                    .get_element_list()?
100                    .indices_ref()
101                    .to_owned(),
102            ),
103            &false,
104            &self.vertex_mask, // TODO: technically, the complement of false can cause unnecessary updates
105        )?;
106        Ok(full_vertex_mask)
107    }
108
109    /// The mask selects which empty values shall be set to false. The mask does not remove elements already in the selection.
110    pub(crate) fn to_full_vertex_mask_with_mask(
111        &self,
112        mask: &SparseVector<bool>,
113    ) -> Result<SparseVector<bool>, GraphComputingError> {
114        let mut filtered_mask = SparseVector::new(
115            self.graph_ref().graphblas_context_ref(),
116            &self.graph_ref().vertex_capacity()?,
117        )?;
118        GRAPHBLAS_VECTOR_AND_OPERATOR.apply(
119            self.graph_ref().index_mask_with_all_vertices(),
120            mask,
121            &mut filtered_mask,
122        )?;
123
124        let mut filtered_vertex_mask = SparseVector::new(
125            self.graph_ref().graphblas_context_ref(),
126            &self.graph_ref().vertex_capacity()?,
127        )?;
128        GRAPHBLAS_VECTOR_AND_OPERATOR.apply(&self.vertex_mask, mask, &mut filtered_vertex_mask)?;
129
130        let mut full_vertex_mask = SparseVector::new(
131            self.graph_ref().graphblas_context_ref(),
132            &self.graph_ref().vertex_capacity()?,
133        )?;
134        GRAPHBLAS_VECTOR_AND_OPERATOR.apply(
135            &filtered_vertex_mask,
136            &filtered_mask,
137            &mut full_vertex_mask,
138        )?;
139
140        GRAPHBLAS_SCALAR_INTO_VECTOR_INSERTER_WITH_MASK_COMPLEMENT.apply_with_mask(
141            &mut full_vertex_mask,
142            &ElementIndexSelector::Index(
143                &filtered_mask.get_element_list()?.indices_ref().to_owned(),
144            ),
145            &false,
146            &self.vertex_mask,
147        )?;
148
149        Ok(full_vertex_mask)
150    }
151
152    pub(crate) fn graph_ref(&'g self) -> &'g Graph {
153        self.graph
154    }
155
156    fn get_selected_elements(&self) -> Result<VectorElementList<bool>, GraphComputingError> {
157        let mut selected_vertices_mask = SparseVector::new(
158            self.graph.graphblas_context_ref(),
159            &self.graph.vertex_capacity()?,
160        )?;
161        GRAPHBLAS_SUB_VECTOR_EXTRACTOR.apply_with_mask(
162            &self.vertex_mask,
163            &ElementIndexSelector::All,
164            &mut selected_vertices_mask,
165            &self.vertex_mask,
166        )?;
167        Ok(selected_vertices_mask.get_element_list()?)
168    }
169
170    pub(crate) fn vertex_indices_ref(&self) -> Result<Vec<VertexIndex>, GraphComputingError> {
171        let selected_vertex_elements = self.get_selected_elements()?;
172        let raw_vertex_indices = selected_vertex_elements.indices_ref();
173
174        // TODO: parallelization
175        let mut vertex_indices = Vec::with_capacity(raw_vertex_indices.len());
176        for index in raw_vertex_indices.into_iter() {
177            let vertex_index = VertexIndex::new(index.clone());
178            vertex_indices.push(vertex_index);
179        }
180        Ok(vertex_indices)
181    }
182
183    pub fn vertices_ref(&self) -> Result<Vec<&Vertex>, GraphComputingError> {
184        let selected_vertex_elements = self.get_selected_elements()?;
185        let vertex_indices = selected_vertex_elements.indices_ref();
186
187        let mut selected_vertices = Vec::with_capacity(vertex_indices.len());
188        for vertex_index in vertex_indices.into_iter() {
189            let selected_vertex = self
190                .graph
191                .vertex_store_ref()
192                .get_ref(VertexIndex::new(vertex_index.clone()));
193
194            match selected_vertex {
195                Ok(vertex) => selected_vertices.push(vertex),
196                Err(_) => {
197                    // TODO: match actual error type
198                    return Err(LogicError::new(
199                        LogicErrorType::VertexMustExist,
200                        String::from("A vertex was selected that does not exist"),
201                        None,
202                    )
203                    .into());
204                }
205            }
206        }
207        Ok(selected_vertices)
208    }
209
210    pub fn vertex_keys_ref(&self) -> Result<Vec<&str>, GraphComputingError> {
211        let selected_vertex_elements = self.get_selected_elements()?;
212        let vertex_indices = selected_vertex_elements.indices_ref();
213
214        let mut selected_vertex_keys = Vec::with_capacity(vertex_indices.len());
215        for vertex_index in vertex_indices.into_iter() {
216            let selected_vertex = self
217                .graph
218                .vertex_store_ref()
219                .get_ref(VertexIndex::new(vertex_index.clone()));
220
221            match selected_vertex {
222                Ok(vertex) => selected_vertex_keys.push(vertex.key_ref()),
223                Err(_) => {
224                    // TODO: match actual error type
225                    return Err(LogicError::new(
226                        LogicErrorType::VertexMustExist,
227                        String::from("A vertex was selected that does not exist"),
228                        None,
229                    )
230                    .into());
231                }
232            }
233        }
234        Ok(selected_vertex_keys)
235    }
236
237    pub fn vertex_values_ref(&self) -> Result<Vec<&VertexValue>, GraphComputingError> {
238        let selected_vertex_elements = self.get_selected_elements()?;
239        let vertex_indices = selected_vertex_elements.indices_ref();
240
241        let mut selected_vertex_values = Vec::with_capacity(vertex_indices.len());
242        for vertex_index in vertex_indices.into_iter() {
243            let selected_vertex = self
244                .graph
245                .vertex_store_ref()
246                .get_ref(VertexIndex::new(vertex_index.clone()));
247
248            match selected_vertex {
249                Ok(vertex) => selected_vertex_values.push(vertex.value_ref()),
250                Err(_) => {
251                    // TODO: match actual error type
252                    return Err(LogicError::new(
253                        LogicErrorType::VertexMustExist,
254                        String::from("A vertex was selected that does not exist"),
255                        None,
256                    )
257                    .into());
258                }
259            }
260        }
261        Ok(selected_vertex_values)
262    }
263}
264
265#[cfg(test)]
266mod tests {
267    use super::*;
268
269    use graphblas_sparse_linear_algebra::value_types::sparse_vector::GetVectorElementValue;
270
271    use crate::graph::edge::DirectedEdgeDefinedByKeys;
272    use crate::graph::graph::GraphTrait;
273    use crate::graph::vertex::VertexKeyAndIndexConversion;
274    use crate::operations::add_edge::AddEdge;
275    use crate::operations::add_vertex::AddVertex;
276    use crate::operations::select_vertex::SelectVertex;
277
278    use crate::tests::standard_graph_for_testing::standard_graph_for_testing;
279
280    #[test]
281    fn test_query_result() {
282        let initial_vertex_capacity = 10;
283        let initial_edge_type_capacity = 10;
284        let mut graph = Graph::new(initial_vertex_capacity, initial_edge_type_capacity).unwrap();
285
286        let vertex_key_1 = String::from("vertex_1");
287        let vertex_value_1 = String::from("value_1").into();
288
289        let vertex_key_2 = String::from("vertex_1");
290        let vertex_value_2 = String::from("value_2").into();
291
292        let vertex_1 = Vertex::new(vertex_key_1, vertex_value_1);
293        let vertex_2 = Vertex::new(vertex_key_2, vertex_value_2);
294
295        let edge_vertex1_vertex2 = DirectedEdgeDefinedByKeys::new(
296            vertex_1.clone().into(),
297            String::from("edge_type_1"),
298            vertex_2.clone().into(),
299        );
300        let edge_vertex2_vertex1 = DirectedEdgeDefinedByKeys::new(
301            vertex_2.clone().into(),
302            String::from("edge_type_1"),
303            vertex_1.clone().into(),
304        );
305        let edge_vertex1_vertex2_type2 = DirectedEdgeDefinedByKeys::new(
306            vertex_1.clone().into(),
307            String::from("edge_type_2"),
308            vertex_2.clone().into(),
309        );
310
311        graph.add_or_replace_vertex(vertex_1.clone()).unwrap();
312        graph.add_or_replace_vertex(vertex_2.clone()).unwrap();
313
314        graph
315            .add_edge_and_edge_type_using_keys(edge_vertex1_vertex2.clone())
316            .unwrap();
317        graph
318            .add_edge_and_edge_type_using_keys(edge_vertex2_vertex1.clone())
319            .unwrap();
320        graph
321            .add_edge_and_edge_type_using_keys(edge_vertex1_vertex2_type2.clone())
322            .unwrap();
323
324        let vertex_mask =
325            SparseVector::new(&graph.graphblas_context_ref(), &initial_vertex_capacity).unwrap();
326        let vertex_selection = VertexSelection::new(&graph, vertex_mask).unwrap();
327
328        let vertices = vertex_selection.vertices_ref().unwrap();
329        assert_eq!(vertices.len(), 0);
330    }
331
332    #[test]
333    fn to_vertex_full_mask() {
334        let graph = standard_graph_for_testing();
335
336        let negative_selection = graph
337            .select_vertices_connected_to_vertex_by_key(String::from("sign"), &"negative")
338            .unwrap();
339
340        let negative_selection_as_full_mask = negative_selection.to_full_vertex_mask().unwrap();
341        assert_eq!(
342            negative_selection_as_full_mask.length().unwrap(),
343            graph.vertex_capacity().unwrap()
344        );
345        assert_eq!(
346            negative_selection_as_full_mask
347                .number_of_stored_elements()
348                .unwrap(),
349            graph.number_of_vertices().unwrap()
350        );
351
352        let index = graph
353            .vertex_key_ref_to_vertex_index_ref("1".into())
354            .unwrap();
355        assert_eq!(
356            negative_selection_as_full_mask
357                .get_element_value(index.index_ref())
358                .unwrap(),
359            false
360        );
361
362        let index = graph
363            .vertex_key_ref_to_vertex_index_ref("0".into())
364            .unwrap();
365        assert_eq!(
366            negative_selection_as_full_mask
367                .get_element_value(index.index_ref())
368                .unwrap(),
369            false
370        );
371
372        let index = graph
373            .vertex_key_ref_to_vertex_index_ref("-1".into())
374            .unwrap();
375        assert_eq!(
376            negative_selection_as_full_mask
377                .get_element_value(index.index_ref())
378                .unwrap(),
379            true
380        );
381
382        let index = graph
383            .vertex_key_ref_to_vertex_index_ref("-1.1".into())
384            .unwrap();
385        assert_eq!(
386            negative_selection_as_full_mask
387                .get_element_value(index.index_ref())
388                .unwrap(),
389            true
390        );
391    }
392
393    #[test]
394    fn to_vertex_full_mask_with_mask() {
395        let graph = standard_graph_for_testing();
396
397        let negative_selection = graph
398            .select_vertices_connected_to_vertex_by_key(String::from("sign"), &"negative")
399            .unwrap();
400
401        let integer_selection = graph
402            .select_vertices_connected_to_vertex_by_key(String::from("is_a"), &"integer")
403            .unwrap();
404
405        let negative_selection_as_full_mask = negative_selection
406            .to_full_vertex_mask_with_mask(integer_selection.vertex_mask_ref())
407            .unwrap();
408
409        assert_eq!(
410            negative_selection_as_full_mask
411                .number_of_stored_elements()
412                .unwrap(),
413            integer_selection
414                .vertex_mask_ref()
415                .number_of_stored_elements()
416                .unwrap()
417        );
418
419        let index = graph
420            .vertex_key_ref_to_vertex_index_ref("1".into())
421            .unwrap();
422        assert_eq!(
423            negative_selection_as_full_mask
424                .get_element_value(index.index_ref())
425                .unwrap(),
426            false
427        );
428
429        let index = graph
430            .vertex_key_ref_to_vertex_index_ref("1_duplicate".into())
431            .unwrap();
432        assert_eq!(
433            negative_selection_as_full_mask
434                .get_element_value(index.index_ref())
435                .unwrap(),
436            false
437        );
438
439        let index = graph
440            .vertex_key_ref_to_vertex_index_ref("0".into())
441            .unwrap();
442        assert_eq!(
443            negative_selection_as_full_mask
444                .get_element_value(index.index_ref())
445                .unwrap(),
446            false
447        );
448
449        let index = graph
450            .vertex_key_ref_to_vertex_index_ref("2".into())
451            .unwrap();
452        assert_eq!(
453            negative_selection_as_full_mask
454                .get_element_value(index.index_ref())
455                .unwrap(),
456            false
457        );
458
459        let index = graph
460            .vertex_key_ref_to_vertex_index_ref("-1".into())
461            .unwrap();
462        assert_eq!(
463            negative_selection_as_full_mask
464                .get_element_value(index.index_ref())
465                .unwrap(),
466            true
467        );
468
469        let index = graph
470            .vertex_key_ref_to_vertex_index_ref("-1.1".into())
471            .unwrap();
472        assert_eq!(
473            negative_selection_as_full_mask
474                .get_element_value(index.index_ref())
475                .unwrap(),
476            false
477        );
478    }
479}