lattices/
map_union.rs

1//! Module containing the [`MapUnion`] lattice and aliases for different datastructures.
2
3use std::cmp::Ordering::{self, *};
4use std::collections::{BTreeMap, HashMap};
5use std::fmt::Debug;
6use std::marker::PhantomData;
7
8use cc_traits::{Collection, GetKeyValue, Iter, MapInsert, SimpleCollectionRef};
9
10use crate::cc_traits::{GetMut, Keyed, Map, MapIter, SimpleKeyedRef};
11use crate::collections::{ArrayMap, MapMapValues, OptionMap, SingletonMap, VecMap};
12use crate::{Atomize, DeepReveal, IsBot, IsTop, LatticeBimorphism, LatticeFrom, LatticeOrd, Merge};
13
14/// Map-union compound lattice.
15///
16/// Each key corresponds to a lattice value instance. Merging map-union lattices is done by
17/// unioning the keys and merging the values of intersecting keys.
18#[repr(transparent)]
19#[derive(Copy, Clone, Debug, Default)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct MapUnion<Map>(Map);
22impl<Map> MapUnion<Map> {
23    /// Create a new `MapUnion` from a `Map`.
24    pub fn new(val: Map) -> Self {
25        Self(val)
26    }
27
28    /// Create a new `MapUnion` from an `Into<Map>`.
29    pub fn new_from(val: impl Into<Map>) -> Self {
30        Self::new(val.into())
31    }
32
33    /// Reveal the inner value as a shared reference.
34    pub fn as_reveal_ref(&self) -> &Map {
35        &self.0
36    }
37
38    /// Reveal the inner value as an exclusive reference.
39    pub fn as_reveal_mut(&mut self) -> &mut Map {
40        &mut self.0
41    }
42
43    /// Gets the inner by value, consuming self.
44    pub fn into_reveal(self) -> Map {
45        self.0
46    }
47}
48
49impl<Map, Val> DeepReveal for MapUnion<Map>
50where
51    Map: Keyed<Item = Val> + MapMapValues<Val>,
52    Val: DeepReveal,
53{
54    type Revealed = Map::MapValue<Val::Revealed>;
55
56    fn deep_reveal(self) -> Self::Revealed {
57        self.0.map_values(DeepReveal::deep_reveal)
58    }
59}
60
61impl<MapSelf, MapOther, K, ValSelf, ValOther> Merge<MapUnion<MapOther>> for MapUnion<MapSelf>
62where
63    MapSelf: Keyed<Key = K, Item = ValSelf>
64        + Extend<(K, ValSelf)>
65        + for<'a> GetMut<&'a K, Item = ValSelf>,
66    MapOther: IntoIterator<Item = (K, ValOther)>,
67    ValSelf: Merge<ValOther> + LatticeFrom<ValOther>,
68    ValOther: IsBot,
69{
70    fn merge(&mut self, other: MapUnion<MapOther>) -> bool {
71        let mut changed = false;
72        // This vec collect is needed to prevent simultaneous mut references `self.0.extend` and
73        // `self.0.get_mut`.
74        // TODO(mingwei): This could be fixed with a different structure, maybe some sort of
75        // `Collection` entry API.
76        let iter: Vec<_> = other
77            .0
78            .into_iter()
79            .filter(|(_k_other, val_other)| !val_other.is_bot())
80            .filter_map(|(k_other, val_other)| {
81                match self.0.get_mut(&k_other) {
82                    // Key collision, merge into `self`.
83                    Some(mut val_self) => {
84                        changed |= val_self.merge(val_other);
85                        None
86                    }
87                    // New value, convert for extending.
88                    None => {
89                        changed = true;
90                        Some((k_other, ValSelf::lattice_from(val_other)))
91                    }
92                }
93            })
94            .collect();
95        self.0.extend(iter);
96        changed
97    }
98}
99
100impl<MapSelf, MapOther, K, ValSelf, ValOther> LatticeFrom<MapUnion<MapOther>> for MapUnion<MapSelf>
101where
102    MapSelf: Keyed<Key = K, Item = ValSelf> + FromIterator<(K, ValSelf)>,
103    MapOther: IntoIterator<Item = (K, ValOther)>,
104    ValSelf: LatticeFrom<ValOther>,
105{
106    fn lattice_from(other: MapUnion<MapOther>) -> Self {
107        Self(
108            other
109                .0
110                .into_iter()
111                .map(|(k_other, val_other)| (k_other, LatticeFrom::lattice_from(val_other)))
112                .collect(),
113        )
114    }
115}
116
117impl<MapSelf, MapOther, K, ValSelf, ValOther> PartialOrd<MapUnion<MapOther>> for MapUnion<MapSelf>
118where
119    MapSelf: Map<K, ValSelf, Key = K, Item = ValSelf> + MapIter + SimpleKeyedRef,
120    MapOther: Map<K, ValOther, Key = K, Item = ValOther> + MapIter + SimpleKeyedRef,
121    ValSelf: PartialOrd<ValOther> + IsBot,
122    ValOther: IsBot,
123{
124    fn partial_cmp(&self, other: &MapUnion<MapOther>) -> Option<Ordering> {
125        let mut self_any_greater = false;
126        let mut other_any_greater = false;
127        let self_keys = self
128            .0
129            .iter()
130            .filter(|(_k, v)| !v.is_bot())
131            .map(|(k, _v)| <MapSelf as SimpleKeyedRef>::into_ref(k));
132        let other_keys = other
133            .0
134            .iter()
135            .filter(|(_k, v)| !v.is_bot())
136            .map(|(k, _v)| <MapOther as SimpleKeyedRef>::into_ref(k));
137        for k in self_keys.chain(other_keys) {
138            match (self.0.get(k), other.0.get(k)) {
139                (Some(self_value), Some(other_value)) => {
140                    match self_value.partial_cmp(&*other_value)? {
141                        Less => {
142                            other_any_greater = true;
143                        }
144                        Greater => {
145                            self_any_greater = true;
146                        }
147                        Equal => {}
148                    }
149                }
150                (Some(_), None) => {
151                    self_any_greater = true;
152                }
153                (None, Some(_)) => {
154                    other_any_greater = true;
155                }
156                (None, None) => unreachable!(),
157            }
158            if self_any_greater && other_any_greater {
159                return None;
160            }
161        }
162        match (self_any_greater, other_any_greater) {
163            (true, false) => Some(Greater),
164            (false, true) => Some(Less),
165            (false, false) => Some(Equal),
166            // We check this one after each loop iteration above.
167            (true, true) => unreachable!(),
168        }
169    }
170}
171impl<MapSelf, MapOther> LatticeOrd<MapUnion<MapOther>> for MapUnion<MapSelf> where
172    Self: PartialOrd<MapUnion<MapOther>>
173{
174}
175
176impl<MapSelf, MapOther, K, ValSelf, ValOther> PartialEq<MapUnion<MapOther>> for MapUnion<MapSelf>
177where
178    MapSelf: Map<K, ValSelf, Key = K, Item = ValSelf> + MapIter + SimpleKeyedRef,
179    MapOther: Map<K, ValOther, Key = K, Item = ValOther> + MapIter + SimpleKeyedRef,
180    ValSelf: PartialEq<ValOther> + IsBot,
181    ValOther: IsBot,
182{
183    fn eq(&self, other: &MapUnion<MapOther>) -> bool {
184        let self_keys = self
185            .0
186            .iter()
187            .filter(|(_k, v)| !v.is_bot())
188            .map(|(k, _v)| <MapSelf as SimpleKeyedRef>::into_ref(k));
189        let other_keys = other
190            .0
191            .iter()
192            .filter(|(_k, v)| !v.is_bot())
193            .map(|(k, _v)| <MapOther as SimpleKeyedRef>::into_ref(k));
194        for k in self_keys.chain(other_keys) {
195            match (self.0.get(k), other.0.get(k)) {
196                (Some(self_value), Some(other_value)) => {
197                    if *self_value != *other_value {
198                        return false;
199                    }
200                }
201                (None, None) => unreachable!(),
202                _ => {
203                    return false;
204                }
205            }
206        }
207
208        true
209    }
210}
211impl<MapSelf> Eq for MapUnion<MapSelf> where Self: PartialEq {}
212
213impl<Map> IsBot for MapUnion<Map>
214where
215    Map: Iter,
216    Map::Item: IsBot,
217{
218    fn is_bot(&self) -> bool {
219        self.0.iter().all(|v| v.is_bot())
220    }
221}
222
223impl<Map> IsTop for MapUnion<Map> {
224    fn is_top(&self) -> bool {
225        false
226    }
227}
228
229impl<Map, K, Val> Atomize for MapUnion<Map>
230where
231    Map: 'static
232        + IntoIterator<Item = (K, Val)>
233        + Keyed<Key = K, Item = Val>
234        + Extend<(K, Val)>
235        + for<'a> GetMut<&'a K, Item = Val>,
236    K: 'static + Clone,
237    Val: 'static + Atomize + LatticeFrom<<Val as Atomize>::Atom>,
238{
239    type Atom = MapUnionSingletonMap<K, Val::Atom>;
240
241    // TODO: use impl trait, then remove 'static.
242    type AtomIter = Box<dyn Iterator<Item = Self::Atom>>;
243
244    fn atomize(self) -> Self::AtomIter {
245        Box::new(self.0.into_iter().flat_map(|(k, val)| {
246            val.atomize()
247                .map(move |v| MapUnionSingletonMap::new_from((k.clone(), v)))
248        }))
249    }
250}
251
252/// [`std::collections::HashMap`]-backed [`MapUnion`] lattice.
253pub type MapUnionHashMap<K, Val> = MapUnion<HashMap<K, Val>>;
254
255/// [`std::collections::BTreeMap`]-backed [`MapUnion`] lattice.
256pub type MapUnionBTreeMap<K, Val> = MapUnion<BTreeMap<K, Val>>;
257
258/// [`Vec`]-backed [`MapUnion`] lattice.
259pub type MapUnionVec<K, Val> = MapUnion<VecMap<K, Val>>;
260
261/// Array-backed [`MapUnion`] lattice.
262pub type MapUnionArrayMap<K, Val, const N: usize> = MapUnion<ArrayMap<K, Val, N>>;
263
264/// [`crate::collections::SingletonMap`]-backed [`MapUnion`] lattice.
265pub type MapUnionSingletonMap<K, Val> = MapUnion<SingletonMap<K, Val>>;
266
267/// [`Option`]-backed [`MapUnion`] lattice.
268pub type MapUnionOptionMap<K, Val> = MapUnion<OptionMap<K, Val>>;
269
270/// Composable bimorphism, wraps an existing morphism by partitioning it per key.
271///
272/// For example, `KeyedBimorphism<..., CartesianProduct<...>>` is a join.
273pub struct KeyedBimorphism<MapOut, Bimorphism> {
274    bimorphism: Bimorphism,
275    _phantom: PhantomData<fn() -> MapOut>,
276}
277impl<MapOut, Bimorphism> KeyedBimorphism<MapOut, Bimorphism> {
278    /// Create a `KeyedBimorphism` using `bimorphism` for handling values.
279    pub fn new(bimorphism: Bimorphism) -> Self {
280        Self {
281            bimorphism,
282            _phantom: PhantomData,
283        }
284    }
285}
286impl<MapA, MapB, MapOut, ValFunc> LatticeBimorphism<MapUnion<MapA>, MapUnion<MapB>>
287    for KeyedBimorphism<MapOut, ValFunc>
288where
289    ValFunc: LatticeBimorphism<MapA::Item, MapB::Item>,
290    MapA: MapIter + SimpleKeyedRef + SimpleCollectionRef,
291    MapB: for<'a> GetKeyValue<&'a MapA::Key, Key = MapA::Key> + SimpleCollectionRef,
292    MapA::Key: Clone + Eq,
293    MapA::Item: Clone,
294    MapB::Item: Clone,
295    MapOut: Default + MapInsert<MapA::Key> + Collection<Item = ValFunc::Output>,
296{
297    type Output = MapUnion<MapOut>;
298
299    fn call(&mut self, lat_a: MapUnion<MapA>, lat_b: MapUnion<MapB>) -> Self::Output {
300        let mut output = MapUnion::<MapOut>::default();
301        for (key, val_a) in lat_a.as_reveal_ref().iter() {
302            let key = <MapA as SimpleKeyedRef>::into_ref(key);
303            let Some((_key, val_b)) = lat_b.as_reveal_ref().get_key_value(key) else {
304                continue;
305            };
306            let val_a = <MapA as SimpleCollectionRef>::into_ref(val_a).clone();
307            let val_b = <MapB as SimpleCollectionRef>::into_ref(val_b).clone();
308
309            let val_out = LatticeBimorphism::call(&mut self.bimorphism, val_a, val_b);
310            <MapOut as MapInsert<_>>::insert(output.as_reveal_mut(), key.clone(), val_out);
311        }
312        output
313    }
314}
315
316#[cfg(test)]
317mod test {
318    use std::collections::HashSet;
319
320    use super::*;
321    use crate::collections::SingletonSet;
322    use crate::set_union::{CartesianProductBimorphism, SetUnionHashSet, SetUnionSingletonSet};
323    use crate::test::{cartesian_power, check_all, check_atomize_each, check_lattice_bimorphism};
324
325    #[test]
326    fn test_map_union() {
327        let mut my_map_a = <MapUnionHashMap<&str, SetUnionHashSet<u64>>>::default();
328        let my_map_b = <MapUnionSingletonMap<&str, SetUnionSingletonSet<u64>>>::new(SingletonMap(
329            "hello",
330            SetUnionSingletonSet::new(SingletonSet(100)),
331        ));
332        let my_map_c =
333            MapUnionSingletonMap::new_from(("hello", SetUnionHashSet::new_from([100, 200])));
334        my_map_a.merge(my_map_b);
335        my_map_a.merge(my_map_c);
336    }
337
338    #[test]
339    fn consistency_atomize() {
340        let mut test_vec = Vec::new();
341
342        // Size 0.
343        test_vec.push(MapUnionHashMap::default());
344        // Size 1.
345        for key in [0, 1] {
346            for value in [vec![], vec![0], vec![1], vec![0, 1]] {
347                test_vec.push(MapUnionHashMap::new(HashMap::from_iter([(
348                    key,
349                    SetUnionHashSet::new(HashSet::from_iter(value)),
350                )])));
351            }
352        }
353        // Size 2.
354        for [val_a, val_b] in cartesian_power(&[vec![], vec![0], vec![1], vec![0, 1]]) {
355            test_vec.push(MapUnionHashMap::new(HashMap::from_iter([
356                (0, SetUnionHashSet::new(HashSet::from_iter(val_a.clone()))),
357                (1, SetUnionHashSet::new(HashSet::from_iter(val_b.clone()))),
358            ])));
359        }
360
361        check_all(&test_vec);
362        check_atomize_each(&test_vec);
363    }
364
365    /// Check that a key with a value of bottom is the same as an empty map, etc.
366    #[test]
367    fn test_collapes_bot() {
368        let map_empty = <MapUnionHashMap<&str, SetUnionHashSet<u64>>>::default();
369        let map_a_bot = <MapUnionSingletonMap<&str, SetUnionHashSet<u64>>>::new(SingletonMap(
370            "a",
371            Default::default(),
372        ));
373        let map_b_bot = <MapUnionSingletonMap<&str, SetUnionHashSet<u64>>>::new(SingletonMap(
374            "b",
375            Default::default(),
376        ));
377
378        assert_eq!(map_empty, map_a_bot);
379        assert_eq!(map_empty, map_b_bot);
380        assert_eq!(map_a_bot, map_b_bot);
381    }
382
383    #[test]
384    fn test_join_aka_keyed_cartesian_product() {
385        let items_a = &[
386            MapUnionHashMap::new_from([("foo", SetUnionHashSet::new_from(["bar"]))]),
387            MapUnionHashMap::new_from([("foo", SetUnionHashSet::new_from(["baz"]))]),
388            MapUnionHashMap::new_from([("hello", SetUnionHashSet::new_from(["world"]))]),
389        ];
390        let items_b = &[
391            MapUnionHashMap::new_from([("foo", SetUnionHashSet::new_from(["bang"]))]),
392            MapUnionHashMap::new_from([(
393                "hello",
394                SetUnionHashSet::new_from(["goodbye", "farewell"]),
395            )]),
396        ];
397
398        check_lattice_bimorphism(
399            KeyedBimorphism::<HashMap<_, _>, _>::new(
400                CartesianProductBimorphism::<HashSet<_>>::default(),
401            ),
402            items_a,
403            items_a,
404        );
405        check_lattice_bimorphism(
406            KeyedBimorphism::<HashMap<_, _>, _>::new(
407                CartesianProductBimorphism::<HashSet<_>>::default(),
408            ),
409            items_a,
410            items_b,
411        );
412        check_lattice_bimorphism(
413            KeyedBimorphism::<HashMap<_, _>, _>::new(
414                CartesianProductBimorphism::<HashSet<_>>::default(),
415            ),
416            items_b,
417            items_a,
418        );
419        check_lattice_bimorphism(
420            KeyedBimorphism::<HashMap<_, _>, _>::new(
421                CartesianProductBimorphism::<HashSet<_>>::default(),
422            ),
423            items_b,
424            items_b,
425        );
426
427        check_lattice_bimorphism(
428            KeyedBimorphism::<BTreeMap<_, _>, _>::new(
429                CartesianProductBimorphism::<HashSet<_>>::default(),
430            ),
431            items_a,
432            items_a,
433        );
434        check_lattice_bimorphism(
435            KeyedBimorphism::<BTreeMap<_, _>, _>::new(
436                CartesianProductBimorphism::<HashSet<_>>::default(),
437            ),
438            items_a,
439            items_b,
440        );
441        check_lattice_bimorphism(
442            KeyedBimorphism::<BTreeMap<_, _>, _>::new(
443                CartesianProductBimorphism::<HashSet<_>>::default(),
444            ),
445            items_b,
446            items_a,
447        );
448        check_lattice_bimorphism(
449            KeyedBimorphism::<BTreeMap<_, _>, _>::new(
450                CartesianProductBimorphism::<HashSet<_>>::default(),
451            ),
452            items_b,
453            items_b,
454        );
455    }
456}