Skip to content

Commit

Permalink
feat(lattiecs): add Lattice[Bi]Morphism traits, impls for cartesian…
Browse files Browse the repository at this point in the history
… product, pair, and keyed (#1062)

Fix #1063
  • Loading branch information
MingweiSamuel committed Feb 27, 2024
1 parent b364328 commit c8d6985
Show file tree
Hide file tree
Showing 7 changed files with 399 additions and 14 deletions.
14 changes: 13 additions & 1 deletion docs/docs/hydroflow/lattices_crate/lattice_math.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ A function $f: S\rightarrow T$ from lattice domain $S$ to lattice codomain $T$ i
it structurally preserves merges, i.e. merges distribute across the function. For all $a,b\in S$:
$$
f(a \sqcup_S b) \quad=\quad f(a) \sqcup_T f(b)
\quad\quad\quad\mathrm{\textit{(morphism)}}
\quad\quad\quad\mathrm{\textit{(lattice morphism)}}
$$
(Because both the domain and codomain are semilattice spaces, _semilattice homomorphism_ is the
most precise term for this.)
Expand All @@ -172,6 +172,18 @@ Lattice morphisms are a special kind of monotonic function which are _differenti
Because merge distributes over a morphism, we can evaluate the morphisms on a small "delta" of data
and merge that delta into the existing result rather than recompute the entire morphism on all data.

### Lattice Bimorphism

A function $f: R\times S\rightarrow T$ is a _lattice bimorphism_ if it acts like a lattice morphism separately in both of its arguments. For all $a, \delta a \in R$ and $b, \delta b \in S$:
$$
f(a \sqcup_R \delta a,\ b) \quad=\quad f(a,\ b)\ \sqcup_T\ f(\delta a,\ b) \\
f(a,\ b \sqcup_S \delta b) \quad=\quad f(a,\ b)\ \sqcup_T\ f(a,\ \delta b) \\
\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\mathrm{\textit{(lattice bimorphism)}}
$$

Lattice bimorphisms are _differentially computable_ in both arguments, in the same way morphisms
are.

### Further Reading

* [Hydroflow Thesis (2021)](https://hydro.run/papers/hydroflow-thesis.pdf)
16 changes: 13 additions & 3 deletions lattices/src/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use std::hash::Hash;
use std::marker::PhantomData;

use cc_traits::{
covariant_item_mut, covariant_item_ref, covariant_key_ref, simple_keyed_ref, Collection,
CollectionMut, CollectionRef, Get, GetKeyValue, GetKeyValueMut, GetMut, Iter, IterMut, Keyed,
KeyedRef, Len, MapIter, MapIterMut, SimpleKeyedRef,
covariant_item_mut, covariant_item_ref, covariant_key_ref, simple_collection_ref,
simple_keyed_ref, Collection, CollectionMut, CollectionRef, Get, GetKeyValue, GetKeyValueMut,
GetMut, Iter, IterMut, Keyed, KeyedRef, Len, MapIter, MapIterMut, SimpleCollectionRef,
SimpleKeyedRef,
};

/// Trait for transforming the values of a map without changing the overall type of the data structure.
Expand Down Expand Up @@ -89,6 +90,9 @@ impl<T> CollectionRef for VecSet<T> {

covariant_item_ref!();
}
impl<T> SimpleCollectionRef for VecSet<T> {
simple_collection_ref!();
}
impl<'a, Q, T> Get<&'a Q> for VecSet<T>
where
T: Borrow<Q>,
Expand Down Expand Up @@ -355,6 +359,9 @@ impl<T> From<T> for SingletonSet<T> {
impl<T> Collection for SingletonSet<T> {
type Item = T;
}
impl<T> SimpleCollectionRef for SingletonSet<T> {
simple_collection_ref!();
}
impl<T> Len for SingletonSet<T> {
fn len(&self) -> usize {
1
Expand Down Expand Up @@ -930,6 +937,9 @@ impl<T, const N: usize> CollectionRef for ArraySet<T, N> {

covariant_item_ref!();
}
impl<T, const N: usize> SimpleCollectionRef for ArraySet<T, N> {
simple_collection_ref!();
}
impl<'a, Q, T, const N: usize> Get<&'a Q> for ArraySet<T, N>
where
T: Borrow<Q>,
Expand Down
66 changes: 66 additions & 0 deletions lattices/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,69 @@ pub trait DeepReveal {
/// Reveals the underlying lattice types recursively.
fn deep_reveal(self) -> Self::Revealed;
}

/// Semilattice morphism. Lattice merge must distribute over this unary function.
///
/// Use [`crate::test::check_lattice_morphism`] to spot-test an implementation.
///
/// See the [lattice math doc's lattice morphism section](https://hydro.run/docs/hydroflow/lattices_crate/lattice_math/#lattice-morphism).
pub trait LatticeMorphism<LatIn> {
/// The output lattice type.
type Output;
/// Executes the function.
fn call(&mut self, lat_in: LatIn) -> Self::Output;
}

/// Semilattice bimorphism. Lattice merge must distribute over this binary function, in both arguments.
///
/// Use [`crate::test::check_lattice_bimorphism`] to spot-test an implementation.
///
/// See the [lattice math doc's lattice bimorphism section](https://hydro.run/docs/hydroflow/lattices_crate/lattice_math/#lattice-bimorphism).
pub trait LatticeBimorphism<LatA, LatB> {
/// The output lattice type.
type Output;
/// Executes the function.
fn call(&mut self, lat_a: LatA, lat_b: LatB) -> Self::Output;
}

/// Converts a closure to a morphism. Does not check for correctness.
pub fn closure_to_morphism<LatIn, LatOut, F>(
func: F,
) -> impl LatticeMorphism<LatIn, Output = LatOut>
where
F: FnMut(LatIn) -> LatOut,
{
struct FnMorphism<F>(F);
impl<F, LatIn, LatOut> LatticeMorphism<LatIn> for FnMorphism<F>
where
F: FnMut(LatIn) -> LatOut,
{
type Output = LatOut;

fn call(&mut self, lat_in: LatIn) -> Self::Output {
(self.0)(lat_in)
}
}
FnMorphism(func)
}

/// Converts a closure to a bimorphism. Does not check for correctness.
pub fn closure_to_bimorphism<LatA, LatB, LatOut, F>(
func: F,
) -> impl LatticeBimorphism<LatA, LatB, Output = LatOut>
where
F: FnMut(LatA, LatB) -> LatOut,
{
struct FnBimorphism<F>(F);
impl<F, LatA, LatB, LatOut> LatticeBimorphism<LatA, LatB> for FnBimorphism<F>
where
F: FnMut(LatA, LatB) -> LatOut,
{
type Output = LatOut;

fn call(&mut self, lat_a: LatA, lat_b: LatB) -> Self::Output {
(self.0)(lat_a, lat_b)
}
}
FnBimorphism(func)
}
128 changes: 124 additions & 4 deletions lattices/src/map_union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
use std::cmp::Ordering::{self, *};
use std::collections::{BTreeMap, HashMap};
use std::fmt::Debug;
use std::marker::PhantomData;

use cc_traits::Iter;
use cc_traits::{Collection, GetKeyValue, Iter, MapInsert, SimpleCollectionRef};

use crate::cc_traits::{GetMut, Keyed, Map, MapIter, SimpleKeyedRef};
use crate::collections::{ArrayMap, MapMapValues, OptionMap, SingletonMap, VecMap};
use crate::{Atomize, DeepReveal, IsBot, IsTop, LatticeFrom, LatticeOrd, Merge};
use crate::{Atomize, DeepReveal, IsBot, IsTop, LatticeBimorphism, LatticeFrom, LatticeOrd, Merge};

/// Map-union compound lattice.
///
Expand Down Expand Up @@ -269,14 +270,59 @@ pub type MapUnionSingletonMap<K, Val> = MapUnion<SingletonMap<K, Val>>;
/// [`Option`]-backed [`MapUnion`] lattice.
pub type MapUnionOptionMap<K, Val> = MapUnion<OptionMap<K, Val>>;

/// Composable bimorphism, wraps an existing morphism by partitioning it per key.
///
/// For example, `KeyedBimorphism<..., CartesianProduct<...>>` is a join.
pub struct KeyedBimorphism<MapOut, Bimorphism> {
bimorphism: Bimorphism,
_phantom: PhantomData<fn() -> MapOut>,
}
impl<MapOut, Bimorphism> From<Bimorphism> for KeyedBimorphism<MapOut, Bimorphism> {
fn from(bimorphism: Bimorphism) -> Self {
Self {
bimorphism,
_phantom: PhantomData,
}
}
}
impl<MapA, MapB, MapOut, ValFunc> LatticeBimorphism<MapUnion<MapA>, MapUnion<MapB>>
for KeyedBimorphism<MapOut, ValFunc>
where
ValFunc: LatticeBimorphism<MapA::Item, MapB::Item>,
MapA: MapIter + SimpleKeyedRef + SimpleCollectionRef,
MapB: for<'a> GetKeyValue<&'a MapA::Key, Key = MapA::Key> + SimpleCollectionRef,
MapA::Key: Clone + Eq,
MapA::Item: Clone,
MapB::Item: Clone,
MapOut: Default + MapInsert<MapA::Key> + Collection<Item = ValFunc::Output>,
{
type Output = MapUnion<MapOut>;

fn call(&mut self, lat_a: MapUnion<MapA>, lat_b: MapUnion<MapB>) -> Self::Output {
let mut output = MapUnion::<MapOut>::default();
for (key, val_a) in lat_a.as_reveal_ref().iter() {
let key = <MapA as SimpleKeyedRef>::into_ref(key);
let Some((_key, val_b)) = lat_b.as_reveal_ref().get_key_value(key) else {
continue;
};
let val_a = <MapA as SimpleCollectionRef>::into_ref(val_a).clone();
let val_b = <MapB as SimpleCollectionRef>::into_ref(val_b).clone();

let val_out = LatticeBimorphism::call(&mut self.bimorphism, val_a, val_b);
<MapOut as MapInsert<_>>::insert(output.as_reveal_mut(), key.clone(), val_out);
}
output
}
}

#[cfg(test)]
mod test {
use std::collections::HashSet;

use super::*;
use crate::collections::SingletonSet;
use crate::set_union::{SetUnionHashSet, SetUnionSingletonSet};
use crate::test::{cartesian_power, check_all, check_atomize_each};
use crate::set_union::{CartesianProductBimorphism, SetUnionHashSet, SetUnionSingletonSet};
use crate::test::{cartesian_power, check_all, check_atomize_each, check_lattice_bimorphism};

#[test]
fn test_map_union() {
Expand Down Expand Up @@ -335,4 +381,78 @@ mod test {
assert_eq!(map_empty, map_b_bot);
assert_eq!(map_a_bot, map_b_bot);
}

#[test]
fn test_join_aka_keyed_cartesian_product() {
let items_a = &[
MapUnionHashMap::new_from([("foo", SetUnionHashSet::new_from(["bar"]))]),
MapUnionHashMap::new_from([("foo", SetUnionHashSet::new_from(["baz"]))]),
MapUnionHashMap::new_from([("hello", SetUnionHashSet::new_from(["world"]))]),
];
let items_b = &[
MapUnionHashMap::new_from([("foo", SetUnionHashSet::new_from(["bang"]))]),
MapUnionHashMap::new_from([(
"hello",
SetUnionHashSet::new_from(["goodbye", "farewell"]),
)]),
];

check_lattice_bimorphism(
KeyedBimorphism::<HashMap<_, _>, _>::from(
CartesianProductBimorphism::<HashSet<_>>::default(),
),
items_a,
items_a,
);
check_lattice_bimorphism(
KeyedBimorphism::<HashMap<_, _>, _>::from(
CartesianProductBimorphism::<HashSet<_>>::default(),
),
items_a,
items_b,
);
check_lattice_bimorphism(
KeyedBimorphism::<HashMap<_, _>, _>::from(
CartesianProductBimorphism::<HashSet<_>>::default(),
),
items_b,
items_a,
);
check_lattice_bimorphism(
KeyedBimorphism::<HashMap<_, _>, _>::from(
CartesianProductBimorphism::<HashSet<_>>::default(),
),
items_b,
items_b,
);

check_lattice_bimorphism(
KeyedBimorphism::<BTreeMap<_, _>, _>::from(
CartesianProductBimorphism::<HashSet<_>>::default(),
),
items_a,
items_a,
);
check_lattice_bimorphism(
KeyedBimorphism::<BTreeMap<_, _>, _>::from(
CartesianProductBimorphism::<HashSet<_>>::default(),
),
items_a,
items_b,
);
check_lattice_bimorphism(
KeyedBimorphism::<BTreeMap<_, _>, _>::from(
CartesianProductBimorphism::<HashSet<_>>::default(),
),
items_b,
items_a,
);
check_lattice_bimorphism(
KeyedBimorphism::<BTreeMap<_, _>, _>::from(
CartesianProductBimorphism::<HashSet<_>>::default(),
),
items_b,
items_b,
);
}
}
36 changes: 33 additions & 3 deletions lattices/src/pair.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::cmp::Ordering::{self, *};

use crate::{DeepReveal, IsBot, IsTop, LatticeFrom, LatticeOrd, Merge};
use crate::{DeepReveal, IsBot, IsTop, LatticeBimorphism, LatticeFrom, LatticeOrd, Merge};

/// Pair compound lattice.
///
Expand Down Expand Up @@ -137,13 +137,24 @@ where
}
}

/// Bimorphism which pairs up the two input lattices.
#[derive(Default)]
pub struct PairBimorphism;
impl<LatA, LatB> LatticeBimorphism<LatA, LatB> for PairBimorphism {
type Output = Pair<LatA, LatB>;

fn call(&mut self, lat_a: LatA, lat_b: LatB) -> Self::Output {
Pair::new(lat_a, lat_b)
}
}

#[cfg(test)]
mod test {
use std::collections::HashSet;

use super::*;
use crate::set_union::SetUnionHashSet;
use crate::test::check_all;
use crate::set_union::{SetUnionBTreeSet, SetUnionHashSet};
use crate::test::{check_all, check_lattice_bimorphism};
use crate::WithTop;

#[test]
Expand Down Expand Up @@ -189,4 +200,23 @@ mod test {

check_all(&test_vec);
}

#[test]
fn test_pair_bimorphism() {
let items_a = &[
SetUnionHashSet::new_from([]),
SetUnionHashSet::new_from([0]),
SetUnionHashSet::new_from([1]),
SetUnionHashSet::new_from([0, 1]),
];
let items_b = &[
SetUnionBTreeSet::new("hello".chars().collect()),
SetUnionBTreeSet::new("world".chars().collect()),
];

check_lattice_bimorphism(PairBimorphism, items_a, items_a);
check_lattice_bimorphism(PairBimorphism, items_a, items_b);
check_lattice_bimorphism(PairBimorphism, items_b, items_a);
check_lattice_bimorphism(PairBimorphism, items_b, items_b);
}
}
Loading

0 comments on commit c8d6985

Please sign in to comment.