lattices/lib.rs
1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4use std::cmp::Ordering::{self, *};
5
6use sealed::sealed;
7pub use {cc_traits, variadics};
8
9/// Module for definiting algebraic structures and properties.
10pub mod algebra;
11pub mod collections;
12mod conflict;
13mod dom_pair;
14pub mod ght;
15pub mod map_union;
16pub mod map_union_with_tombstones;
17mod ord;
18mod pair;
19mod point;
20pub mod semiring_application;
21pub mod set_union;
22pub mod set_union_with_tombstones;
23pub mod test;
24pub mod tombstone;
25pub mod union_find;
26mod unit;
27mod vec_union;
28mod with_bot;
29mod with_top;
30
31pub use conflict::Conflict;
32pub use dom_pair::DomPair;
33pub use lattices_macro::*;
34pub use ord::{Max, Min};
35pub use pair::{Pair, PairBimorphism};
36pub use point::Point;
37pub use vec_union::VecUnion;
38pub use with_bot::WithBot;
39pub use with_top::WithTop;
40
41/// Alias trait for lattice types.
42#[sealed]
43pub trait Lattice: Sized + Merge<Self> + LatticeOrd + NaiveLatticeOrd + IsBot + IsTop {}
44#[sealed]
45impl<T> Lattice for T where T: Sized + Merge<Self> + LatticeOrd + NaiveLatticeOrd + IsBot + IsTop {}
46
47/// Alias trait for semirings.
48#[sealed]
49pub trait Semiring<T>: Addition<T> + Multiplication<T> + Zero<T> + One<T> {}
50
51/// Trait for Semiring Addition.
52pub trait Addition<Other> {
53 /// Add-assign `other` into self.
54 fn add(&mut self, other: Self);
55
56 /// Add `this` and `delta` together, returning the new value.
57 fn add_owned(mut self, other: Self) -> Self
58 where
59 Self: Sized,
60 {
61 self.add(other);
62 self
63 }
64}
65
66/// Trait for Semiring Multiplication.
67pub trait Multiplication<Other> {
68 /// Multiply-assign `other` into self.
69 fn mul(&mut self, other: Self);
70
71 /// Multiply `this` and `delta` together, returning the new value.
72 fn mul_owned(mut self, other: Self) -> Self
73 where
74 Self: Sized,
75 {
76 self.mul(other);
77 self
78 }
79}
80
81/// Trait to check if semiring contains a zero.
82pub trait Zero<T> {
83 /// Returns the zero element of the semiring. Identify for the Addition operation.
84 fn zero(&self) -> T;
85}
86
87/// Trait to define a one in a semiring.
88pub trait One<T> {
89 /// Returns the one element of the semiring. Identity for the multiplication operation.
90 fn one(&self) -> T;
91}
92/// Trait for lattice merge (AKA "join" or "least upper bound").
93pub trait Merge<Other> {
94 /// Merge `other` into the `self` lattice.
95 ///
96 /// This operation must be associative, commutative, and idempotent.
97 ///
98 /// Returns `true` if `self` changed, `false` otherwise.
99 /// Returning `true` implies that the new value for `self` is later in the lattice order than
100 /// the old value. Returning `false` means that `self` was unchanged and therefore `other` came
101 /// before `self` (or the two are equal) in the lattice order.
102 fn merge(&mut self, other: Other) -> bool;
103
104 /// Merge `this` and `delta` together, returning the new value.
105 fn merge_owned(mut this: Self, delta: Other) -> Self
106 where
107 Self: Sized,
108 {
109 Self::merge(&mut this, delta);
110 this
111 }
112}
113
114/// Trait for lattice partial order comparison
115/// PartialOrd is implemented for many things, this trait can be used to require the type be a lattice.
116pub trait LatticeOrd<Rhs = Self>: PartialOrd<Rhs> {}
117
118/// Naive lattice compare, based on the [`Merge::merge`] function.
119#[sealed]
120pub trait NaiveLatticeOrd<Rhs = Self>
121where
122 Self: Clone + Merge<Rhs> + Sized,
123 Rhs: Clone + Merge<Self>,
124{
125 /// Naive compare based on the [`Merge::merge`] method. This method can be very inefficient;
126 /// use [`PartialOrd::partial_cmp`] instead.
127 ///
128 /// This method should not be overridden.
129 fn naive_cmp(&self, other: &Rhs) -> Option<Ordering> {
130 let mut self_a = self.clone();
131 let other_a = other.clone();
132 let self_b = self.clone();
133 let mut other_b = other.clone();
134 match (self_a.merge(other_a), other_b.merge(self_b)) {
135 (true, true) => None,
136 (true, false) => Some(Less),
137 (false, true) => Some(Greater),
138 (false, false) => Some(Equal),
139 }
140 }
141}
142#[sealed]
143impl<This, Other> NaiveLatticeOrd<Other> for This
144where
145 Self: Clone + Merge<Other>,
146 Other: Clone + Merge<Self>,
147{
148}
149
150/// Same as `From` but for lattices.
151///
152/// This should only be implemented between different representations of the same lattice type.
153/// This should recursively convert nested lattice types, but not non-lattice ("scalar") types.
154pub trait LatticeFrom<Other> {
155 /// Convert from the `Other` lattice into `Self`.
156 fn lattice_from(other: Other) -> Self;
157}
158
159/// Trait to check if a lattice instance is bottom (⊥).
160pub trait IsBot {
161 /// Returns if `self` is lattice bottom (⊥).
162 ///
163 /// Must be consistent with equality, any element equal to bottom is also considered to be bottom.
164 fn is_bot(&self) -> bool;
165}
166
167/// Trait to check if a lattice instance is top (⊤) and therefore cannot change any futher.
168pub trait IsTop {
169 /// Returns if `self` is lattice top (⊤).
170 ///
171 /// Must be consistent with equality, any element equal to top is also considered to be top.
172 fn is_top(&self) -> bool;
173}
174
175/// Trait to atomize a lattice into individual elements. For example, a [`set_union::SetUnion`]
176/// will be broken up into individual singleton elements.
177///
178/// Formally, breaks up `Self` into an set of lattice points forming a (strong) [antichain](https://en.wikipedia.org/wiki/Antichain).
179/// "Strong" in the sense that any pair of lattice points in the antichain should have a greatest
180/// lower bound (GLB or "meet") of bottom.
181pub trait Atomize: Merge<Self::Atom> {
182 /// The type of atoms for this lattice.
183 type Atom: 'static + IsBot;
184
185 /// The iter type iterating the antichain atoms.
186 type AtomIter: 'static + Iterator<Item = Self::Atom>;
187
188 /// Atomize self: convert into an iter of atoms.
189 ///
190 /// The returned iterator should be empty if and only if `self.is_bot()` is true.
191 /// All atoms in the returned iterator should have `self.is_bot()` be false.
192 ///
193 /// Returned values must merge to reform a value equal to the original `self`.
194 fn atomize(self) -> Self::AtomIter;
195}
196
197/// Trait for recursively revealing the underlying types within lattice types.
198pub trait DeepReveal {
199 /// The underlying type when revealed.
200 type Revealed;
201
202 /// Reveals the underlying lattice types recursively.
203 fn deep_reveal(self) -> Self::Revealed;
204}
205
206/// Semilattice morphism. Lattice merge must distribute over this unary function.
207///
208/// Use [`crate::test::check_lattice_morphism`] to spot-test an implementation.
209///
210/// See the [lattice math doc's lattice morphism section](https://hydro.run/docs/dfir/lattices_crate/lattice_math/#lattice-morphism).
211pub trait LatticeMorphism<LatIn> {
212 /// The output lattice type.
213 type Output;
214 /// Executes the function.
215 fn call(&mut self, lat_in: LatIn) -> Self::Output;
216}
217
218/// Semilattice bimorphism. Lattice merge must distribute over this binary function, in both arguments.
219///
220/// Use [`crate::test::check_lattice_bimorphism`] to spot-test an implementation.
221///
222/// See the [lattice math doc's lattice bimorphism section](https://hydro.run/docs/dfir/lattices_crate/lattice_math/#lattice-bimorphism).
223pub trait LatticeBimorphism<LatA, LatB> {
224 /// The output lattice type.
225 type Output;
226 /// Executes the function.
227 fn call(&mut self, lat_a: LatA, lat_b: LatB) -> Self::Output;
228}
229
230/// Converts a closure to a morphism. Does not check for correctness.
231pub fn closure_to_morphism<LatIn, LatOut, F>(
232 func: F,
233) -> impl LatticeMorphism<LatIn, Output = LatOut>
234where
235 F: FnMut(LatIn) -> LatOut,
236{
237 struct FnMorphism<F>(F);
238 impl<F, LatIn, LatOut> LatticeMorphism<LatIn> for FnMorphism<F>
239 where
240 F: FnMut(LatIn) -> LatOut,
241 {
242 type Output = LatOut;
243
244 fn call(&mut self, lat_in: LatIn) -> Self::Output {
245 (self.0)(lat_in)
246 }
247 }
248 FnMorphism(func)
249}
250
251/// Converts a closure to a bimorphism. Does not check for correctness.
252pub fn closure_to_bimorphism<LatA, LatB, LatOut, F>(
253 func: F,
254) -> impl LatticeBimorphism<LatA, LatB, Output = LatOut>
255where
256 F: FnMut(LatA, LatB) -> LatOut,
257{
258 struct FnBimorphism<F>(F);
259 impl<F, LatA, LatB, LatOut> LatticeBimorphism<LatA, LatB> for FnBimorphism<F>
260 where
261 F: FnMut(LatA, LatB) -> LatOut,
262 {
263 type Output = LatOut;
264
265 fn call(&mut self, lat_a: LatA, lat_b: LatB) -> Self::Output {
266 (self.0)(lat_a, lat_b)
267 }
268 }
269 FnBimorphism(func)
270}