From 8d606bc267e48d771e2f846dc715a1f062a9dcfb Mon Sep 17 00:00:00 2001 From: Frank McSherry Date: Mon, 9 Mar 2020 09:37:11 -0400 Subject: [PATCH 1/6] add PartialOrder impls --- timely/src/progress/frontier.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/timely/src/progress/frontier.rs b/timely/src/progress/frontier.rs index b1f6c20fc..47aeb2212 100644 --- a/timely/src/progress/frontier.rs +++ b/timely/src/progress/frontier.rs @@ -156,6 +156,12 @@ impl Antichain { #[inline] pub fn elements(&self) -> &[T] { &self.elements[..] } } +impl PartialOrder for Antichain { + fn less_equal(&self, other: &Self) -> bool { + other.elements().iter().all(|t2| self.elements().iter().any(|t1| t1.less_equal(t2))) + } +} + /// An antichain based on a multiset whose elements frequencies can be updated. /// /// The `MutableAntichain` maintains frequencies for many elements of type `T`, and exposes the set @@ -542,6 +548,12 @@ impl<'a, T: 'a+PartialOrder> AntichainRef<'a, T> { } } +impl<'a, T: PartialOrder> PartialOrder for AntichainRef<'a, T> { + fn less_equal(&self, other: &Self) -> bool { + other.iter().all(|t2| self.iter().any(|t1| t1.less_equal(t2))) + } +} + impl<'a, T: PartialOrder> ::std::ops::Deref for AntichainRef<'a, T> { type Target = [T]; fn deref(&self) -> &Self::Target { From e6f2effa00b470d405af9e603624d650512100f3 Mon Sep 17 00:00:00 2001 From: Frank McSherry Date: Mon, 9 Mar 2020 10:33:16 -0400 Subject: [PATCH 2/6] update dominates --- timely/src/progress/frontier.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/timely/src/progress/frontier.rs b/timely/src/progress/frontier.rs index 47aeb2212..9cbf2641a 100644 --- a/timely/src/progress/frontier.rs +++ b/timely/src/progress/frontier.rs @@ -138,9 +138,10 @@ impl Antichain { } /// Returns true if every element of `other` is greater or equal to some element of `self`. + #[deprecated(since="0.12.0", note="please use `PartialOrder::less_equal` instead")] #[inline] pub fn dominates(&self, other: &Antichain) -> bool { - other.elements().iter().all(|t2| self.elements().iter().any(|t1| t1.less_equal(t2))) + ::less_equal(self, other) } /// Reveals the elements in the antichain. From fd12c63776b6028c4ccbef7ee2b31321b797eb9e Mon Sep 17 00:00:00 2001 From: Frank McSherry Date: Mon, 9 Mar 2020 11:12:27 -0400 Subject: [PATCH 3/6] improve equality testing --- timely/src/progress/frontier.rs | 98 ++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/timely/src/progress/frontier.rs b/timely/src/progress/frontier.rs index 9cbf2641a..657350ecd 100644 --- a/timely/src/progress/frontier.rs +++ b/timely/src/progress/frontier.rs @@ -9,7 +9,11 @@ use crate::order::PartialOrder; /// This antichain implementation allows you to repeatedly introduce elements to the antichain, and /// which will evict larger elements to maintain the *minimal* antichain, those incomparable elements /// no greater than any other element. -#[derive(Clone, Debug, Default, Eq, PartialEq, Abomonation, Serialize, Deserialize)] +/// +/// Two antichains are equal if the contain the same set of elements, even if in different orders. +/// This can make equality testing quadratic, though linear in the common case that the sequences +/// are identical. +#[derive(Clone, Debug, Default, Abomonation, Serialize, Deserialize)] pub struct Antichain { elements: Vec } @@ -59,44 +63,6 @@ impl Antichain { added } - /// Creates a new empty `Antichain`. - /// - /// # Examples - /// - ///``` - /// use timely::progress::frontier::Antichain; - /// - /// let mut frontier = Antichain::::new(); - ///``` - pub fn new() -> Antichain { Antichain { elements: Vec::new() } } - - /// Creates a new singleton `Antichain`. - /// - /// # Examples - /// - ///``` - /// use timely::progress::frontier::Antichain; - /// - /// let mut frontier = Antichain::from_elem(2); - ///``` - pub fn from_elem(element: T) -> Antichain { Antichain { elements: vec![element] } } - - /// Clears the contents of the antichain. - /// - /// # Examples - /// - ///``` - /// use timely::progress::frontier::Antichain; - /// - /// let mut frontier = Antichain::from_elem(2); - /// frontier.clear(); - /// assert!(frontier.elements().is_empty()); - ///``` - pub fn clear(&mut self) { self.elements.clear() } - - /// Sorts the elements so that comparisons between antichains can be made. - pub fn sort(&mut self) where T: Ord { self.elements.sort() } - /// Returns true if any item in the antichain is strictly less than the argument. /// /// # Examples @@ -143,6 +109,47 @@ impl Antichain { pub fn dominates(&self, other: &Antichain) -> bool { ::less_equal(self, other) } +} + +impl Antichain { + + /// Creates a new empty `Antichain`. + /// + /// # Examples + /// + ///``` + /// use timely::progress::frontier::Antichain; + /// + /// let mut frontier = Antichain::::new(); + ///``` + pub fn new() -> Antichain { Antichain { elements: Vec::new() } } + + /// Creates a new singleton `Antichain`. + /// + /// # Examples + /// + ///``` + /// use timely::progress::frontier::Antichain; + /// + /// let mut frontier = Antichain::from_elem(2); + ///``` + pub fn from_elem(element: T) -> Antichain { Antichain { elements: vec![element] } } + + /// Clears the contents of the antichain. + /// + /// # Examples + /// + ///``` + /// use timely::progress::frontier::Antichain; + /// + /// let mut frontier = Antichain::from_elem(2); + /// frontier.clear(); + /// assert!(frontier.elements().is_empty()); + ///``` + pub fn clear(&mut self) { self.elements.clear() } + + /// Sorts the elements so that comparisons between antichains can be made. + pub fn sort(&mut self) where T: Ord { self.elements.sort() } /// Reveals the elements in the antichain. /// @@ -157,6 +164,19 @@ impl Antichain { #[inline] pub fn elements(&self) -> &[T] { &self.elements[..] } } +impl PartialEq for Antichain { + fn eq(&self, other: &Self) -> bool { + // Lengths should be the same, with the option for fast acceptance if identical. + self.elements().len() == other.elements().len() && + ( + self.elements().iter().zip(other.elements().iter()).all(|(t1,t2)| t1 == t2) || + self.elements().iter().all(|t1| other.elements().iter().any(|t2| t1.eq(t2))) + ) + } +} + +impl Eq for Antichain { } + impl PartialOrder for Antichain { fn less_equal(&self, other: &Self) -> bool { other.elements().iter().all(|t2| self.elements().iter().any(|t1| t1.less_equal(t2))) From 145526522a09960922b373fd9d7918b16c3ab376 Mon Sep 17 00:00:00 2001 From: Frank McSherry Date: Mon, 9 Mar 2020 11:41:11 -0400 Subject: [PATCH 4/6] more refactoring of methods --- timely/src/progress/frontier.rs | 74 +++++++++++++-------------------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/timely/src/progress/frontier.rs b/timely/src/progress/frontier.rs index 657350ecd..3a7b72edc 100644 --- a/timely/src/progress/frontier.rs +++ b/timely/src/progress/frontier.rs @@ -161,7 +161,7 @@ impl Antichain { /// let mut frontier = Antichain::from_elem(2); /// assert_eq!(frontier.elements(), &[2]); ///``` - #[inline] pub fn elements(&self) -> &[T] { &self.elements[..] } + #[inline] pub fn elements(&self) -> AntichainRef { AntichainRef::new(&self.elements[..]) } } impl PartialEq for Antichain { @@ -183,6 +183,15 @@ impl PartialOrder for Antichain { } } +impl From> for Antichain { + fn from(vec: Vec) -> Self { + // TODO: We could re-use `vec` with some care. + let mut temp = Antichain::new(); + for elem in vec.into_iter() { temp.insert(elem); } + temp + } +} + /// An antichain based on a multiset whose elements frequencies can be updated. /// /// The `MutableAntichain` maintains frequencies for many elements of type `T`, and exposes the set @@ -479,50 +488,24 @@ impl> MutableAntichainF } /// A wrapper for elements of an antichain. -#[derive(PartialEq, Eq)] -pub struct AntichainRef<'a, T: 'a+PartialOrder> { +pub struct AntichainRef<'a, T: 'a> { /// Elements contained in the antichain. frontier: &'a [T], } -impl<'a, T: 'a+PartialOrder> AntichainRef<'a, T> { +impl<'a, T: 'a> AntichainRef<'a, T> { /// Create a new `AntichainRef` from a reference to a slice of elements forming the frontier. + /// + /// This method does not check that this antichain has any particular properties, for example + /// that there are no elements strictly less than other elements. pub fn new(frontier: &'a [T]) -> Self { Self { frontier, } } +} - /// Returns true if there are no elements in the `AntichainRef`. - /// - /// # Examples - /// - ///``` - /// use timely::progress::frontier::AntichainRef; - /// - /// let frontier = AntichainRef::::new(&[]); - /// assert!(frontier.is_empty()); - ///``` - #[inline] - pub fn is_empty(&self) -> bool { - self.frontier.is_empty() - } - - /// Create an iterator over the elements in this `AntichainRef`. - /// - /// # Examples - /// - ///``` - /// use timely::progress::frontier::AntichainRef; - /// - /// let frontier = AntichainRef::new(&[1u64]); - /// let mut iter = frontier.iter(); - /// assert_eq!(iter.next(), Some(&1u64)); - /// assert_eq!(iter.next(), None); - ///``` - pub fn iter(&self) -> ::std::slice::Iter { - self.frontier.iter() - } +impl<'a, T: 'a+PartialOrder> AntichainRef<'a, T> { /// Returns true if any item in the `AntichainRef` is strictly less than the argument. /// @@ -557,32 +540,35 @@ impl<'a, T: 'a+PartialOrder> AntichainRef<'a, T> { pub fn less_equal(&self, time: &T) -> bool { self.iter().any(|x| x.less_equal(time)) } +} - /// Returns the number of elements in this `AntichainRef`. - pub fn len(&self) -> usize { - self.frontier.len() - } - - /// Copies `self` into a new `Vec`. - pub fn to_vec(&self) -> Vec where T: Clone { - self.frontier.to_vec() +impl<'a, T: PartialEq> PartialEq for AntichainRef<'a, T> { + fn eq(&self, other: &Self) -> bool { + // Lengths should be the same, with the option for fast acceptance if identical. + self.len() == other.len() && + ( + self.iter().zip(other.iter()).all(|(t1,t2)| t1 == t2) || + self.iter().all(|t1| other.iter().any(|t2| t1.eq(t2))) + ) } } +impl<'a, T: Eq> Eq for AntichainRef<'a, T> { } + impl<'a, T: PartialOrder> PartialOrder for AntichainRef<'a, T> { fn less_equal(&self, other: &Self) -> bool { other.iter().all(|t2| self.iter().any(|t1| t1.less_equal(t2))) } } -impl<'a, T: PartialOrder> ::std::ops::Deref for AntichainRef<'a, T> { +impl<'a, T> ::std::ops::Deref for AntichainRef<'a, T> { type Target = [T]; fn deref(&self) -> &Self::Target { self.frontier } } -impl<'a, T: 'a+PartialOrder> ::std::iter::IntoIterator for &'a AntichainRef<'a, T> { +impl<'a, T: 'a> ::std::iter::IntoIterator for &'a AntichainRef<'a, T> { type Item = &'a T; type IntoIter = ::std::slice::Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { From 2525d83c1d7368b33a98dd73de585b50fb22d429 Mon Sep 17 00:00:00 2001 From: Frank McSherry Date: Mon, 9 Mar 2020 12:12:11 -0400 Subject: [PATCH 5/6] fix doctest --- timely/src/progress/frontier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timely/src/progress/frontier.rs b/timely/src/progress/frontier.rs index 3a7b72edc..f4fe21b4a 100644 --- a/timely/src/progress/frontier.rs +++ b/timely/src/progress/frontier.rs @@ -159,7 +159,7 @@ impl Antichain { /// use timely::progress::frontier::Antichain; /// /// let mut frontier = Antichain::from_elem(2); - /// assert_eq!(frontier.elements(), &[2]); + /// assert_eq!(&*frontier.elements(), &[2]); ///``` #[inline] pub fn elements(&self) -> AntichainRef { AntichainRef::new(&self.elements[..]) } } From cd0422d906f6375bd4efab2712736f6c58ee3daa Mon Sep 17 00:00:00 2001 From: Frank McSherry Date: Mon, 9 Mar 2020 14:43:36 -0400 Subject: [PATCH 6/6] copy impl, and to_owned --- timely/src/progress/frontier.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/timely/src/progress/frontier.rs b/timely/src/progress/frontier.rs index f4fe21b4a..1ccadf6ee 100644 --- a/timely/src/progress/frontier.rs +++ b/timely/src/progress/frontier.rs @@ -488,11 +488,21 @@ impl> MutableAntichainF } /// A wrapper for elements of an antichain. +#[derive(Debug)] pub struct AntichainRef<'a, T: 'a> { /// Elements contained in the antichain. frontier: &'a [T], } +impl<'a, T: 'a> Clone for AntichainRef<'a, T> { + fn clone(&self) -> Self { + Self { + frontier: self.frontier.clone(), + } + } +} +impl<'a, T: 'a> Copy for AntichainRef<'a, T> { } + impl<'a, T: 'a> AntichainRef<'a, T> { /// Create a new `AntichainRef` from a reference to a slice of elements forming the frontier. /// @@ -503,6 +513,22 @@ impl<'a, T: 'a> AntichainRef<'a, T> { frontier, } } + + /// Constructs an owned antichain from the antichain reference. + /// + /// # Examples + /// + ///``` + /// use timely::progress::{Antichain, frontier::AntichainRef}; + /// + /// let frontier = AntichainRef::new(&[1u64]); + /// assert_eq!(frontier.to_owned(), Antichain::from_elem(1u64)); + ///``` + pub fn to_owned(&self) -> Antichain where T: Clone { + Antichain { + elements: self.frontier.to_vec() + } + } } impl<'a, T: 'a+PartialOrder> AntichainRef<'a, T> { @@ -574,4 +600,4 @@ impl<'a, T: 'a> ::std::iter::IntoIterator for &'a AntichainRef<'a, T> { fn into_iter(self) -> Self::IntoIter { self.iter() } -} +} \ No newline at end of file