triblespace_core/query/equalityconstraint.rs
1use super::*;
2
3/// Constrains two variables to have the same value.
4///
5/// Used to express variable equality when two positions in a triple
6/// share the same logical variable but need distinct [`VariableId`]s
7/// for the [`TribleSetConstraint`](crate::trible::tribleset::triblesetconstraint::TribleSetConstraint)
8/// (which assumes its three positions have distinct ids).
9///
10/// The macro layer emits this automatically when a `_?var` appears in
11/// both the entity and value positions of the same triple.
12pub struct EqualityConstraint {
13 a: VariableId,
14 b: VariableId,
15}
16
17impl EqualityConstraint {
18 /// Creates a constraint requiring `a` and `b` to be bound to the
19 /// same raw value.
20 pub fn new(a: VariableId, b: VariableId) -> Self {
21 EqualityConstraint { a, b }
22 }
23}
24
25impl<'c> Constraint<'c> for EqualityConstraint {
26 fn variables(&self) -> VariableSet {
27 let mut vs = VariableSet::new_empty();
28 vs.set(self.a);
29 vs.set(self.b);
30 vs
31 }
32
33 /// Returns `Some(1)` when the peer variable is already bound
34 /// (exactly one candidate). Returns `None` when the peer is
35 /// unbound — the constraint has no independent opinion about the
36 /// variable's cardinality and defers to other constraints in the
37 /// intersection. This is safe as long as each variable also appears
38 /// in at least one other constraint (which the macro desugaring
39 /// guarantees).
40 fn estimate(&self, variable: VariableId, binding: &Binding) -> Option<usize> {
41 if variable == self.a {
42 if binding.get(self.b).is_some() {
43 Some(1)
44 } else {
45 None
46 }
47 } else if variable == self.b {
48 if binding.get(self.a).is_some() {
49 Some(1)
50 } else {
51 None
52 }
53 } else {
54 None
55 }
56 }
57
58 /// When the peer variable is bound, proposes its value.
59 fn propose(&self, variable: VariableId, binding: &Binding, proposals: &mut Vec<RawValue>) {
60 if variable == self.a {
61 if let Some(v) = binding.get(self.b) {
62 proposals.push(*v);
63 }
64 } else if variable == self.b {
65 if let Some(v) = binding.get(self.a) {
66 proposals.push(*v);
67 }
68 }
69 }
70
71 /// Retains only proposals that match the peer variable's binding.
72 fn confirm(&self, variable: VariableId, binding: &Binding, proposals: &mut Vec<RawValue>) {
73 if variable == self.a {
74 if let Some(peer) = binding.get(self.b) {
75 proposals.retain(|v| v == peer);
76 }
77 } else if variable == self.b {
78 if let Some(peer) = binding.get(self.a) {
79 proposals.retain(|v| v == peer);
80 }
81 }
82 }
83
84 /// Returns `false` when both variables are bound to different values.
85 fn satisfied(&self, binding: &Binding) -> bool {
86 match (binding.get(self.a), binding.get(self.b)) {
87 (Some(a), Some(b)) => a == b,
88 _ => true,
89 }
90 }
91}