From 1c8ba9afc5875827be7462143bf111f7f15ed382 Mon Sep 17 00:00:00 2001 From: Frank McSherry Date: Tue, 28 May 2024 09:00:31 -0400 Subject: [PATCH] Introduce IsZero trait for is_zero() --- dogsdogsdogs/src/operators/lookup_map.rs | 2 +- examples/monoid-bfs.rs | 6 +- src/difference.rs | 75 +++++++++++++++--------- src/operators/count.rs | 2 +- 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/dogsdogsdogs/src/operators/lookup_map.rs b/dogsdogsdogs/src/operators/lookup_map.rs index ede8b1f3c..5f45bd1a1 100644 --- a/dogsdogsdogs/src/operators/lookup_map.rs +++ b/dogsdogsdogs/src/operators/lookup_map.rs @@ -7,7 +7,7 @@ use timely::dataflow::operators::Operator; use timely::progress::Antichain; use differential_dataflow::{ExchangeData, Collection, AsCollection, Hashable}; -use differential_dataflow::difference::{Semigroup, Monoid}; +use differential_dataflow::difference::{IsZero, Semigroup, Monoid}; use differential_dataflow::operators::arrange::Arranged; use differential_dataflow::trace::{Cursor, TraceReader}; use differential_dataflow::trace::cursor::IntoOwned; diff --git a/examples/monoid-bfs.rs b/examples/monoid-bfs.rs index f9835cef7..f61ae44ea 100644 --- a/examples/monoid-bfs.rs +++ b/examples/monoid-bfs.rs @@ -18,13 +18,15 @@ pub struct MinSum { value: u32, } -use differential_dataflow::difference::{Semigroup, Multiply}; +use differential_dataflow::difference::{IsZero, Semigroup, Multiply}; +impl IsZero for MinSum { + fn is_zero(&self) -> bool { false } +} impl Semigroup for MinSum { fn plus_equals(&mut self, rhs: &Self) { self.value = std::cmp::min(self.value, rhs.value); } - fn is_zero(&self) -> bool { false } } impl Multiply for MinSum { diff --git a/src/difference.rs b/src/difference.rs index 0a63e1682..ec9ac7ec1 100644 --- a/src/difference.rs +++ b/src/difference.rs @@ -9,6 +9,23 @@ #[deprecated] pub use self::Abelian as Diff; +/// A type that can be an additive identity for all `Semigroup` implementations. +/// +/// This method is extracted from `Semigroup` to avoid ambiguity when used. +/// It refers exclusively to the type itself, and whether it will act as the identity +/// in the course of `Semigroup::plus_equals()`. +pub trait IsZero { + /// Returns true if the element is the additive identity. + /// + /// This is primarily used by differential dataflow to know when it is safe to delete an update. + /// When a difference accumulates to zero, the difference has no effect on any accumulation and can + /// be removed. + /// + /// A semigroup is not obligated to have a zero element, and this method could always return + /// false in such a setting. + fn is_zero(&self) -> bool; +} + /// A type with addition and a test for zero. /// /// These traits are currently the minimal requirements for a type to be a "difference" in differential @@ -20,18 +37,9 @@ pub use self::Abelian as Diff; /// There is a light presumption of commutativity here, in that while we will largely perform addition /// in order of timestamps, for many types of timestamps there is no total order and consequently no /// obvious order to respect. Non-commutative semigroups should be used with care. -pub trait Semigroup : Clone { +pub trait Semigroup : Clone + IsZero { /// The method of `std::ops::AddAssign`, for types that do not implement `AddAssign`. fn plus_equals(&mut self, rhs: &Rhs); - /// Returns true if the element is the additive identity. - /// - /// This is primarily used by differential dataflow to know when it is safe to delete an update. - /// When a difference accumulates to zero, the difference has no effect on any accumulation and can - /// be removed. - /// - /// A semigroup is not obligated to have a zero element, and this method could always return - /// false in such a setting. - fn is_zero(&self) -> bool; } /// A semigroup with an explicit zero element. @@ -61,9 +69,11 @@ pub trait Multiply { /// Implementation for built-in signed integers. macro_rules! builtin_implementation { ($t:ty) => { + impl IsZero for $t { + #[inline] fn is_zero(&self) -> bool { self == &0 } + } impl Semigroup for $t { #[inline] fn plus_equals(&mut self, rhs: &Self) { *self += rhs; } - #[inline] fn is_zero(&self) -> bool { self == &0 } } impl Monoid for $t { @@ -108,9 +118,11 @@ builtin_abelian_implementation!(isize); /// Implementations for wrapping signed integers, which have a different zero. macro_rules! wrapping_implementation { ($t:ty) => { + impl IsZero for $t { + #[inline] fn is_zero(&self) -> bool { self == &std::num::Wrapping(0) } + } impl Semigroup for $t { #[inline] fn plus_equals(&mut self, rhs: &Self) { *self += rhs; } - #[inline] fn is_zero(&self) -> bool { self == &std::num::Wrapping(0) } } impl Monoid for $t { @@ -159,27 +171,25 @@ mod present { } } + impl super::IsZero for Present { + fn is_zero(&self) -> bool { false } + } + impl super::Semigroup for Present { fn plus_equals(&mut self, _rhs: &Self) { } - fn is_zero(&self) -> bool { false } } } // Pair implementations. mod tuples { - use super::{Semigroup, Monoid, Abelian, Multiply}; + use super::{IsZero, Semigroup, Monoid, Abelian, Multiply}; /// Implementations for tuples. The two arguments must have the same length. macro_rules! tuple_implementation { ( ($($name:ident)*), ($($name2:ident)*) ) => ( - impl<$($name: Semigroup),*> Semigroup for ($($name,)*) { - #[allow(non_snake_case)] - #[inline] fn plus_equals(&mut self, rhs: &Self) { - let ($(ref mut $name,)*) = *self; - let ($(ref $name2,)*) = *rhs; - $($name.plus_equals($name2);)* - } + + impl<$($name: IsZero),*> IsZero for ($($name,)*) { #[allow(unused_mut)] #[allow(non_snake_case)] #[inline] fn is_zero(&self) -> bool { @@ -190,6 +200,15 @@ mod tuples { } } + impl<$($name: Semigroup),*> Semigroup for ($($name,)*) { + #[allow(non_snake_case)] + #[inline] fn plus_equals(&mut self, rhs: &Self) { + let ($(ref mut $name,)*) = *self; + let ($(ref $name2,)*) = *rhs; + $($name.plus_equals($name2);)* + } + } + impl<$($name: Monoid),*> Monoid for ($($name,)*) { #[allow(non_snake_case)] #[inline] fn zero() -> Self { @@ -227,15 +246,18 @@ mod tuples { // Vector implementations mod vector { - use super::{Semigroup, Monoid, Abelian, Multiply}; + use super::{IsZero, Semigroup, Monoid, Abelian, Multiply}; + + impl IsZero for Vec { + fn is_zero(&self) -> bool { + self.iter().all(|x| x.is_zero()) + } + } impl Semigroup for Vec { fn plus_equals(&mut self, rhs: &Self) { self.plus_equals(&rhs[..]) } - fn is_zero(&self) -> bool { - self.iter().all(|x| x.is_zero()) - } } impl Semigroup<[R]> for Vec { @@ -251,9 +273,6 @@ mod vector { self.push(element.clone()); } } - fn is_zero(&self) -> bool { - self.iter().all(|x| x.is_zero()) - } } #[cfg(test)] diff --git a/src/operators/count.rs b/src/operators/count.rs index 89659e4ce..85c44c176 100644 --- a/src/operators/count.rs +++ b/src/operators/count.rs @@ -9,7 +9,7 @@ use crate::trace::cursor::IntoOwned; use crate::lattice::Lattice; use crate::{ExchangeData, Collection}; -use crate::difference::Semigroup; +use crate::difference::{IsZero, Semigroup}; use crate::hashable::Hashable; use crate::collection::AsCollection; use crate::operators::arrange::{Arranged, ArrangeBySelf};