Skip to main content

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}