qualia_core_db/solvers/fuzzy_query/
evaluate.rs1use super::solution::{FuzzyResultSet, FuzzySolution};
9use super::DegreeNorm;
10use crate::sparql_ast::BindingRow;
11
12pub fn annotate<F>(rows: &[BindingRow], degree_of: F) -> FuzzyResultSet
14where
15 F: Fn(&BindingRow) -> f64,
16{
17 FuzzyResultSet::from_solutions(
18 rows.iter()
19 .map(|r| FuzzySolution::new(*r, degree_of(r)))
20 .collect(),
21 )
22}
23
24pub fn collect_from<P, F>(mut pull: P, degree_of: F, cap: usize) -> FuzzyResultSet
30where
31 P: FnMut(&mut BindingRow) -> bool,
32 F: Fn(&BindingRow) -> f64,
33{
34 let mut out = Vec::new();
35 let mut row = BindingRow::new();
36 while out.len() < cap {
37 row.clear();
38 if !pull(&mut row) {
39 break;
40 }
41 out.push(FuzzySolution::new(row, degree_of(&row)));
42 }
43 FuzzyResultSet::from_solutions(out)
44}
45
46pub fn conjunctive_query(
50 pattern_sets: &[FuzzyResultSet],
51 norm: DegreeNorm,
52 threshold: f64,
53) -> FuzzyResultSet {
54 let mut it = pattern_sets.iter();
55 let mut acc = match it.next() {
56 Some(first) => first.clone(),
57 None => return FuzzyResultSet::new(),
58 };
59 for next in it {
60 acc = acc.join(next, norm);
61 }
62 acc.threshold(threshold)
63}
64
65#[cfg(test)]
66mod tests {
67 use super::super::membership::approximately;
68 use super::*;
69 use crate::sparql_ast::{BindingRow, VariableId};
70
71 fn row(pairs: &[(VariableId, u64)]) -> BindingRow {
72 let mut r = BindingRow::new();
73 for &(v, val) in pairs {
74 r.set(v, val);
75 }
76 r
77 }
78
79 #[test]
80 fn annotate_with_a_fuzzy_filter() {
81 let rows = [
83 row(&[(0, 100), (1, 30)]),
84 row(&[(0, 101), (1, 25)]),
85 row(&[(0, 102), (1, 50)]),
86 ];
87 let set = annotate(&rows, |r| {
88 approximately(r.get(1).unwrap() as f64, 30.0, 10.0)
89 });
90 let ranked = set.order_by_degree_desc();
91 assert_eq!(ranked.solutions[0].row.get(0), Some(100)); assert!(ranked.solutions.last().unwrap().degree.abs() < 1e-9); }
94
95 #[test]
96 fn collect_from_a_simulated_operator() {
97 let data = [row(&[(0, 1), (1, 30)]), row(&[(0, 2), (1, 28)])];
99 let mut i = 0;
100 let pull = |out: &mut BindingRow| {
101 if i < data.len() {
102 *out = data[i];
103 i += 1;
104 true
105 } else {
106 false
107 }
108 };
109 let set = collect_from(
110 pull,
111 |r| approximately(r.get(1).unwrap() as f64, 30.0, 5.0),
112 1000,
113 );
114 assert_eq!(set.len(), 2);
115 }
116
117 #[test]
118 fn collect_respects_cap() {
119 let pull = |out: &mut BindingRow| {
121 out.set(0, 1);
122 true
123 };
124 let set = collect_from(pull, |_| 1.0, 10);
125 assert_eq!(set.len(), 10);
126 }
127
128 #[test]
129 fn conjunctive_bgp_join_and_threshold() {
130 let a = annotate(&[row(&[(0, 1), (1, 30)]), row(&[(0, 2), (1, 20)])], |r| {
132 approximately(r.get(1).unwrap() as f64, 30.0, 10.0)
133 });
134 let b = annotate(&[row(&[(0, 1), (2, 99)])], |_| 1.0);
135 let q = conjunctive_query(&[a, b], DegreeNorm::Godel, 0.5);
136 assert_eq!(q.len(), 1);
137 assert_eq!(q.solutions[0].row.get(2), Some(99));
138 assert!(q.solutions[0].degree >= 0.5);
139 }
140
141 #[test]
142 fn empty_patterns_is_empty() {
143 assert!(conjunctive_query(&[], DegreeNorm::Godel, 0.0).is_empty());
144 }
145}