Skip to main content

lattices/
map_union.rs

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