diff --git a/Cargo.toml b/Cargo.toml index 52397129..31ce1f3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,13 @@ hashbrown = "0.12.0" csv = { version = "1.1", optional = true } serde = { version = "1.0", optional = true } impl-trait-for-tuples = "0.2" +deepsize = "0.2.0" +deepsize_derive = "0.1.2" +textwrap = "0.15.0" + +# TODO: eliminate dependency on timely-dataflow by cloning relevant +# parts. +timely = "0.12.0" [[bench]] name = "galen" diff --git a/benches/galen.rs b/benches/galen.rs index a56cb50c..5b828089 100644 --- a/benches/galen.rs +++ b/benches/galen.rs @@ -3,11 +3,15 @@ use csv::{Reader as CsvReader, ReaderBuilder}; use dbsp::{ - algebra::{FiniteMap, HasZero, ZSetHashMap}, + algebra::HasZero, circuit::{trace::SchedulerEvent, GlobalNodeId, Root, Runtime, Stream}, monitor::TraceMonitor, operator::{CsvSource, DelayedFeedback}, profile::CPUProfiler, + trace::{ + ord::{OrdIndexedZSet, OrdZSet, OrdZSetSpine}, + BatchReader, + }, }; use std::{collections::HashMap, fmt::Write, fs, fs::File, path::PathBuf}; @@ -37,9 +41,9 @@ q(?x,?e,?o) :- q(?x,?y,?z),r(?y,?u,?e),q(?z,?u,?o). type Number = u32; type Weight = isize; -fn csv_source(file: &str) -> CsvSource, T> +fn csv_source(file: &str) -> CsvSource> where - T: Clone, + T: Clone + Ord, { let path: PathBuf = ["benches", "galen_data", file].iter().collect(); @@ -56,6 +60,7 @@ fn main() { let monitor = TraceMonitor::new_panic_on_error(); let root = Root::build(|circuit| { + /* let cpu_profiler = CPUProfiler::new(); cpu_profiler.attach(circuit, "cpu profiler"); @@ -72,13 +77,14 @@ fn main() { .or_insert_with(|| String::new()); metadata_string.clear(); node.summary(metadata_string); - //} - //SchedulerEvent::StepEnd => { + } + SchedulerEvent::StepEnd => { let graph = monitor_clone.visualize_circuit_annotate(&|node_id| { let mut metadata_string = metadata .get(node_id) .map(ToString::to_string) .unwrap_or_else(|| "".to_string()); + writeln!(metadata_string, "id: {}", node_id).unwrap(); if let Some(cpu_profile) = cpu_profiler.operator_profile(node_id) { writeln!( metadata_string, @@ -97,6 +103,7 @@ fn main() { _ => {} } }); + */ let p_source = csv_source::<(Number, Number)>("p.txt"); let q_source = csv_source::<(Number, Number, Number)>("q.txt"); @@ -105,133 +112,131 @@ fn main() { let u_source = csv_source::<(Number, Number, Number)>("u.txt"); let s_source = csv_source::<(Number, Number)>("s.txt"); - let p: Stream<_, ZSetHashMap<_, Weight>> = + let p: Stream<_, OrdZSet<_, Weight>> = circuit.region("p", || circuit.add_source(p_source)); - let q: Stream<_, ZSetHashMap<_, Weight>> = + let q: Stream<_, OrdZSet<_, Weight>> = circuit.region("q", || circuit.add_source(q_source)); - let r: Stream<_, ZSetHashMap<_, Weight>> = + let r: Stream<_, OrdZSet<_, Weight>> = circuit.region("r", || circuit.add_source(r_source)); - let c: Stream<_, ZSetHashMap<_, Weight>> = + let c: Stream<_, OrdZSet<_, Weight>> = circuit.region("c", || circuit.add_source(c_source)); - let u: Stream<_, ZSetHashMap<_, Weight>> = + let u: Stream<_, OrdZSet<_, Weight>> = circuit.region("u", || circuit.add_source(u_source)); - let s: Stream<_, ZSetHashMap<_, Weight>> = + let s: Stream<_, OrdZSet<_, Weight>> = circuit.region("s", || circuit.add_source(s_source)); let (outp, outq) = circuit - .iterate_with_conditions(|child| { - let pvar: DelayedFeedback<_, ZSetHashMap<(Number, Number), Weight>> = + .fixedpoint(|child| { + let pvar: DelayedFeedback<_, OrdZSet<(Number, Number), Weight>> = DelayedFeedback::new(child); - let qvar: DelayedFeedback<_, ZSetHashMap<(Number, Number, Number), Weight>> = + let qvar: DelayedFeedback<_, OrdZSet<(Number, Number, Number), Weight>> = DelayedFeedback::new(child); - let p_by_1: Stream<_, ZSetHashMap<_, _>> = pvar.stream().index(); - let p_by_2: Stream<_, ZSetHashMap<_, _>> = - pvar.stream().index_with(|&(x, y)| (y, x)); - let p_by_12: Stream<_, ZSetHashMap<_, _>> = - pvar.stream().index_with(|&(x, y)| ((x, y), ())); - let u_by_1: Stream<_, ZSetHashMap<_, _>> = - u.delta0(child).index_with(|&(x, y, z)| (x, (y, z))); - let q_by_1: Stream<_, ZSetHashMap<_, _>> = - qvar.stream().index_with(|&(x, y, z)| (x, (y, z))); - let q_by_2: Stream<_, ZSetHashMap<_, _>> = - qvar.stream().index_with(|&(x, y, z)| (y, (x, z))); - let q_by_12: Stream<_, ZSetHashMap<_, _>> = - qvar.stream().index_with(|&(x, y, z)| ((x, y), z)); - let q_by_23: Stream<_, ZSetHashMap<_, _>> = - qvar.stream().index_with(|&(x, y, z)| ((y, z), x)); - let c_by_2: Stream<_, ZSetHashMap<_, _>> = - c.delta0(child).index_with(|&(x, y, z)| (y, (x, z))); - let r_by_1: Stream<_, ZSetHashMap<_, _>> = - r.delta0(child).index_with(|&(x, y, z)| (x, (y, z))); - let s_by_1: Stream<_, ZSetHashMap<_, _>> = s.delta0(child).index(); + let p_by_1 = pvar.stream().index::>(); + let p_by_2 = pvar + .stream() + .index_with::, _>(|&(x, y)| (y, x)); + let p_by_12 = pvar + .stream() + .index_with::, _>(|&(x, y)| ((x, y), ())); + let u_by_1 = u + .delta0(child) + .index_with::, _>(|&(x, y, z)| (x, (y, z))); + let q_by_1 = qvar + .stream() + .index_with::, _>(|&(x, y, z)| (x, (y, z))); + let q_by_2 = qvar + .stream() + .index_with::, _>(|&(x, y, z)| (y, (x, z))); + let q_by_12 = qvar + .stream() + .index_with::, _>(|&(x, y, z)| ((x, y), z)); + let q_by_23 = qvar + .stream() + .index_with::, _>(|&(x, y, z)| ((y, z), x)); + let c_by_2 = c + .delta0(child) + .index_with::, _>(|&(x, y, z)| (y, (x, z))); + let r_by_1 = r + .delta0(child) + .index_with::, _>(|&(x, y, z)| (x, (y, z))); + let s_by_1 = s.delta0(child).index::>(); // IR1: p(x,z) :- p(x,y), p(y,z). - let ir1 = child.region("IR1", || { - p_by_2.join_incremental_nested(&p_by_1, |&_y, &x, &z| (x, z)) - }); - ir1.inspect(|zs: &ZSetHashMap<_, _>| println!("ir1: {}", zs.support_size())); + let ir1 = + child.region("IR1", || p_by_2.join_trace(&p_by_1, |&_y, &x, &z| (x, z))); + ir1.inspect(|zs: &OrdZSet<_, _>| println!("ir1: {}", zs.len())); // IR2: q(x,r,z) := p(x,y), q(y,r,z) let ir2 = child.region("IR2", || { - p_by_2.join_incremental_nested(&q_by_1, |&_y, &x, &(r, z)| (x, r, z)) + p_by_2.join_trace(&q_by_1, |&_y, &x, &(r, z)| (x, r, z)) }); - ir2.inspect(|zs: &ZSetHashMap<_, _>| println!("ir2: {}", zs.support_size())); + ir2.inspect(|zs: &OrdZSet<_, _>| println!("ir2: {}", zs.len())); // IR3: p(x,z) := p(y,w), u(w,r,z), q(x,r,y) let ir3 = child.region("IR3", || { p_by_2 - .join_incremental_nested::<_, _, _, _, _, _, _, ZSetHashMap<_, _>>( - &u_by_1, - |&_w, &y, &(r, z)| ((r, y), z), - ) - .index::<_, _, _, ZSetHashMap<_, _>>() - .join_incremental_nested(&q_by_23, |&(_r, _y), &z, &x| (x, z)) + .join_trace::<_, _, OrdZSet<_, _>>(&u_by_1, |&_w, &y, &(r, z)| { + ((r, y), z) + }) + .index::>() + .join_trace(&q_by_23, |&(_r, _y), &z, &x| (x, z)) }); - ir3.inspect(|zs: &ZSetHashMap<_, _>| println!("ir3: {}", zs.support_size())); + ir3.inspect(|zs: &OrdZSet<_, _>| println!("ir3: {}", zs.len())); // IR4: p(x,z) := c(y,w,z), p(x,w), p(x,y) let ir4_1 = child.region("IR4-1", || { - c_by_2.join_incremental_nested::<_, _, _, _, _, _, _, ZSetHashMap<_, _>>( - &p_by_2, - |&_w, &(y, z), &x| ((x, y), z), - ) + c_by_2.join_trace::<_, _, OrdZSet<_, _>>(&p_by_2, |&_w, &(y, z), &x| { + ((x, y), z) + }) }); - ir4_1 - .inspect(|zs: &ZSetHashMap<_, _>| println!("ir4_1: {}", zs.support_size())); + ir4_1.inspect(|zs: &OrdZSet<_, _>| println!("ir4_1: {}", zs.len())); let ir4 = child.region("IR4-2", || { ir4_1 - .index::<_, _, _, ZSetHashMap<_, _>>() - .join_incremental_nested(&p_by_12, |&(x, _y), &z, &()| (x, z)) + .index::>() + .join_trace(&p_by_12, |&(x, _y), &z, &()| (x, z)) }); - ir4.inspect(|zs: &ZSetHashMap<_, _>| println!("ir4: {}", zs.support_size())); + ir4.inspect(|zs: &OrdZSet<_, _>| println!("ir4: {}", zs.len())); // IR5: q(x,q,z) := q(x,r,z), s(r,q) let ir5 = child.region("IR5", || { - q_by_2.join_incremental_nested(&s_by_1, |&_r, &(x, z), &q| (x, q, z)) + q_by_2.join_trace(&s_by_1, |&_r, &(x, z), &q| (x, q, z)) }); - ir5.inspect(|zs: &ZSetHashMap<_, _>| println!("ir5: {}", zs.support_size())); + ir5.inspect(|zs: &OrdZSet<_, _>| println!("ir5: {}", zs.len())); // IR6: q(x,e,o) := q(x,y,z), r(y,u,e), q(z,u,o) - let ir6 = child.region("IR6", || { + let ir6_1 = child.region("IR6_1", || { q_by_2 - .join_incremental_nested::<_, _, _, _, _, _, _, ZSetHashMap<_, _>>( - &r_by_1, - |&_y, &(x, z), &(u, e)| ((z, u), (x, e)), - ) - .index::<_, _, _, ZSetHashMap<_, _>>() - .join_incremental_nested(&q_by_12, |&(_z, _u), &(x, e), &o| (x, e, o)) + .join_trace::<_, _, OrdZSet<_, _>>(&r_by_1, |&_y, &(x, z), &(u, e)| { + ((z, u), (x, e)) + }) + .index::>() + }); + let ir6 = child.region("IR6", || { + ir6_1.join_trace(&q_by_12, |&(_z, _u), &(x, e), &o| (x, e, o)) }); - ir6.inspect(|zs: &ZSetHashMap<_, _>| println!("ir6: {}", zs.support_size())); - let p = p - .delta0(child) - .sum([&ir1, &ir3, &ir4]) - .distinct_incremental_nested(); + ir6.inspect(|zs: &OrdZSet<_, _>| println!("ir6: {}", zs.len())); - let q = q - .delta0(child) - .sum([&ir2, &ir5, &ir6]) - .distinct_incremental_nested(); + let p = p.delta0(child).sum([&ir1, &ir3, &ir4]).distinct_trace(); + + let q = q.delta0(child).sum([&ir2, &ir5, &ir6]).distinct_trace(); pvar.connect(&p); qvar.connect(&q); Ok(( - vec![ - p.condition(HasZero::is_zero), - p.integrate_nested().condition(HasZero::is_zero), - q.condition(HasZero::is_zero), - q.integrate_nested().condition(HasZero::is_zero), - ], - (p.integrate().export(), q.integrate().export()), + p.integrate_trace::>().export(), + q.integrate_trace::>().export(), )) }) .unwrap(); - outp.inspect(|zs: &ZSetHashMap<_, _>| println!("outp: {}", zs.support_size())); - outq.inspect(|zs: &ZSetHashMap<_, _>| println!("outq: {}", zs.support_size())); + outp.consolidate::>() + .inspect(|zs: &OrdZSet<_, _>| println!("outp: {}", zs.len())); + outq.consolidate::>() + .inspect(|zs: &OrdZSet<_, _>| println!("outq: {}", zs.len())); }) .unwrap(); diff --git a/benches/path.rs b/benches/path.rs index e873e5ae..067e490c 100644 --- a/benches/path.rs +++ b/benches/path.rs @@ -1,14 +1,22 @@ use dbsp::{ - algebra::{FiniteHashMap, HasZero, MapBuilder}, circuit::{trace::SchedulerEvent, GlobalNodeId, Root, Stream}, monitor::TraceMonitor, operator::{DelayedFeedback, Generator}, + profile::CPUProfiler, + trace::{ + ord::{OrdIndexedZSet, OrdZSet, OrdZSetSpine}, + Batch, + }, }; -use std::{collections::HashMap, fs, vec}; + +use std::{collections::HashMap, fmt::Write, fs}; fn main() { let monitor = TraceMonitor::new_panic_on_error(); let root = Root::build(|circuit| { + let cpu_profiler = CPUProfiler::new(); + cpu_profiler.attach(circuit, "cpu profiler"); + monitor.attach(circuit, "monitor"); let mut metadata = >::new(); let mut nsteps = 0; @@ -24,11 +32,24 @@ fn main() { } SchedulerEvent::StepEnd => { let graph = monitor_clone.visualize_circuit_annotate(&|node_id| { - metadata + let mut metadata_string = metadata .get(node_id) .map(ToString::to_string) - .unwrap_or_else(|| "".to_string()) + .unwrap_or_else(|| "".to_string()); + + if let Some(cpu_profile) = cpu_profiler.operator_profile(node_id) { + writeln!( + metadata_string, + "invocations: {}", + cpu_profile.invocations() + ) + .unwrap(); + writeln!(metadata_string, "time: {:?}", cpu_profile.total_time()) + .unwrap(); + }; + metadata_string }); + fs::write(format!("path.{}.dot", nsteps), graph.to_dot()).unwrap(); nsteps += 1; } @@ -38,22 +59,24 @@ fn main() { const LAYER: u32 = 200; - let mut edges: FiniteHashMap<(u32, u32), i32> = FiniteHashMap::new(); + let mut tuples = Vec::new(); for layer in 0..5 { for from in 0..LAYER { for to in 0..LAYER { - edges.increment(&(from + (LAYER * layer), to + LAYER * (layer + 1)), 1); + tuples.push((((from + (LAYER * layer), to + LAYER * (layer + 1)), ()), 1)); } } } - let edges: Stream<_, FiniteHashMap<(u32, u32), i32>> = + let edges = >::from_tuples((), tuples); + + let edges: Stream<_, OrdZSet<(u32, u32), i32>> = circuit.add_source(Generator::new(move || edges.clone())); let _paths = circuit - .iterate_with_conditions(|child| { + .fixedpoint(|child| { // ```text - // distinct_incremental_nested + // distinct_trace // ┌───┐ ┌───┐ // edges │ │ │ │ paths // ────┬────────►│ + ├──────────┤ ├────────┬───► @@ -66,34 +89,27 @@ fn main() { // └────────►│ X │ ◄─────────────────────┘ // │ │ // └───┘ - // join_incremental_nested + // join_trace // ``` let edges = edges.delta0(child); - let paths_delayed = >>::new(child); + let paths_delayed = >>::new(child); - let paths_inverted: Stream<_, FiniteHashMap<(u32, u32), i32>> = + let paths_inverted: Stream<_, OrdZSet<(u32, u32), i32>> = paths_delayed.stream().map_keys(|&(x, y)| (y, x)); - let paths_inverted_indexed: Stream<_, FiniteHashMap>> = + let paths_inverted_indexed: Stream<_, OrdIndexedZSet> = paths_inverted.index(); - let edges_indexed: Stream<_, FiniteHashMap>> = - edges.index(); + let edges_indexed: Stream<_, OrdIndexedZSet> = edges.index(); let paths = edges .plus( &paths_inverted_indexed - .join_incremental_nested(&edges_indexed, |_via, from, to| (*from, *to)), + .join_trace(&edges_indexed, |_via, from, to| (*from, *to)), ) - .distinct_incremental_nested(); + .distinct_trace(); paths_delayed.connect(&paths); - let output = paths.integrate(); - Ok(( - vec![ - paths.condition(HasZero::is_zero), - paths.integrate_nested().condition(HasZero::is_zero), - ], - output.export(), - )) + + Ok(paths.integrate_trace::>().export()) }) .unwrap(); }) diff --git a/src/algebra/finite_map/map_macro.rs b/src/algebra/finite_map/map_macro.rs deleted file mode 100644 index 42128785..00000000 --- a/src/algebra/finite_map/map_macro.rs +++ /dev/null @@ -1,32 +0,0 @@ -/// Allows easily creating [`FiniteHashMap`](crate::algebra::FiniteHashMap)s -#[macro_export] -macro_rules! finite_map { - // Create an empty map - () => { - $crate::algebra::FiniteHashMap::new() - }; - - // Create a map from elements - ($($key:expr => $value:expr),+ $(,)?) => {{ - let mut map = $crate::algebra::FiniteHashMap::with_capacity( - $crate::count_elements!($($key),+), - ); - - $( <$crate::algebra::FiniteHashMap<_,_> as $crate::algebra::MapBuilder<_,_>>::increment(&mut map, &$key, $value); )+ - - map - }}; -} - -/// Support macro for counting the number of map elements -#[macro_export] -#[doc(hidden)] -macro_rules! count_elements { - (@replace $_:expr) => { - () - }; - - ($($_:expr),+) => { - <[()]>::len(&[$($crate::count_elements!(@replace $_),)+]) - }; -} diff --git a/src/algebra/finite_map/mod.rs b/src/algebra/finite_map/mod.rs deleted file mode 100644 index eaea6ae6..00000000 --- a/src/algebra/finite_map/mod.rs +++ /dev/null @@ -1,552 +0,0 @@ -#[macro_use] -mod map_macro; -#[cfg(test)] -mod tests; - -use crate::{ - algebra::{ - Add, AddAssign, AddAssignByRef, AddByRef, GroupValue, HasZero, Neg, NegByRef, - WithNumEntries, - }, - shared_ref_self_generic, NumEntries, -}; -use hashbrown::{ - hash_map, - hash_map::{Entry, HashMap, RawEntryMut}, -}; -use std::{ - fmt::{Debug, Formatter, Result}, - hash::Hash, - iter::FromIterator, - mem::swap, -}; - -/// The properties we expect for a FiniteMap key. -pub trait KeyProperties: 'static + Clone + Eq + Hash {} - -impl KeyProperties for T where T: 'static + Clone + Eq + Hash {} - -/// A trait to iterate over keys in the support of a finite map. -/// -/// We make this a separate trait, so as not to pollute -/// `trait FiniteMap` with the lifetime parameter. -pub trait WithSupport<'a, KeyType> -where - KeyType: 'a, -{ - type SupportIterator: Iterator; - - fn support(self) -> Self::SupportIterator; -} - -/// Interface to build maps element-by-element. -/// -/// This interface is functionally equivalent to `FromIterator`, -/// and can be used in contexts where (1) laziness is not required, -/// and (2) it is simpler to push data into the map rather than have -/// the map pull data from an iterator. -/// -/// `MapBuilder` is a separate trait and not part of `FiniteMap`, since -/// some implementers support only the insertion but not the querying side of -/// the map API. An example is `Vec<(Key, Value)>`, which implements -/// `MapBuilder`, but not `FiniteMap`. -pub trait MapBuilder { - /// Create an empty map. - fn empty() -> Self; - - /// Create an empty map with specified capacity. - fn with_capacity(capacity: usize) -> Self; - - /// Increase the value associated with `key` by the specified `value`. - fn increment(&mut self, key: &Key, value: Value); - - /// Increase the value associated with `key` by the specified `value`. - fn increment_owned(&mut self, key: Key, value: Value); -} - -impl MapBuilder for Vec<(Key, Value)> -where - Key: Clone, -{ - fn empty() -> Self { - vec![] - } - - fn with_capacity(capacity: usize) -> Self { - Vec::with_capacity(capacity) - } - - fn increment(&mut self, key: &Key, value: Value) { - self.push((key.clone(), value)); - } - - fn increment_owned(&mut self, key: Key, value: Value) { - self.push((key, value)); - } -} - -/// Finite map trait. -/// -/// A finite map maps arbitrary values (comparable for equality) -/// to values in a group. It has finite support: it is non-zero -/// only for a finite number of values. -/// -/// `KeyType` - Type of values stored in finite map. -/// `ValueType` - Type of results. -pub trait FiniteMap: - GroupValue - + IntoIterator - + FromIterator<(Key, Value)> - + MapBuilder - + WithNumEntries -where - Key: KeyProperties, -{ - /// Find the value associated to the specified key - fn lookup(&self, key: &Key) -> Value; - - /// Find the value associated to the specified key. - /// - /// Returns `None` when `key` is not in the support of `self`. - fn get_in_support(&self, key: &Key) -> Option<&Value>; - - /// Modify the value associated with `key`. - fn update(&mut self, key: &Key, f: F) - where - F: FnOnce(&mut Value); - - /// Modify the value associated with `key`. - fn update_owned(&mut self, key: Key, f: F) - where - F: FnOnce(&mut Value); - - /// The size of the support: number of elements for which the map does not - /// return zero. - fn support_size(&self) -> usize; -} - -#[derive(Clone)] -pub struct FiniteHashMap { - // Unfortunately I cannot just implement these traits for - // HashMap since they conflict with some existing traits. - // We maintain the invariant that the keys (and only these keys) - // that have non-zero values are in this map. - pub(super) value: HashMap, -} - -shared_ref_self_generic!(, FiniteHashMap); - -impl NumEntries for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue + NumEntries, -{ - fn num_entries(&self) -> usize { - match Value::const_num_entries() { - None => { - let mut res = 0; - for (_, v) in self.into_iter() { - res += v.num_entries(); - } - res - } - Some(n) => n * self.support_size(), - } - } - fn const_num_entries() -> Option { - None - } -} - -impl FiniteHashMap { - /// Create a new map - pub fn new() -> Self { - Self { - value: HashMap::default(), - } - } - - /// Create an empty [`FiniteHashMap`] with the capacity to hold `size` - /// elements without reallocating. - pub fn with_capacity(size: usize) -> Self { - Self { - value: HashMap::with_capacity(size), - } - } -} - -impl IntoIterator for FiniteHashMap { - type Item = (Key, Value); - type IntoIter = hash_map::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.value.into_iter() - } -} - -impl<'a, Key, Value> IntoIterator for &'a FiniteHashMap { - type Item = (&'a Key, &'a Value); - type IntoIter = hash_map::Iter<'a, Key, Value>; - - fn into_iter(self) -> Self::IntoIter { - self.value.iter() - } -} - -impl FromIterator<(Key, Value)> for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - fn from_iter(iter: T) -> Self - where - T: IntoIterator, - { - let mut result = Self::new(); - for (k, v) in iter { - result.increment(&k, v); - } - - result - } -} - -impl WithNumEntries for FiniteHashMap { - fn num_entries(&self) -> usize { - self.value.len() - } -} - -impl MapBuilder for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - fn empty() -> Self { - Self::new() - } - - fn with_capacity(capacity: usize) -> Self { - Self::with_capacity(capacity) - } - - fn increment(&mut self, key: &Key, value: Value) { - if value.is_zero() { - return; - } - - match self.value.raw_entry_mut().from_key(key) { - RawEntryMut::Vacant(vacant) => { - vacant.insert(key.clone(), value); - } - - RawEntryMut::Occupied(mut occupied) => { - occupied.get_mut().add_assign(value); - if occupied.get().is_zero() { - occupied.remove_entry(); - } - } - } - } - - fn increment_owned(&mut self, key: Key, value: Value) { - if value.is_zero() { - return; - } - - // This has been a known issue since 2015: https://github.com/rust-lang/rust/issues/56167 - // We should use a different implementation or API if one becomes available. - match self.value.entry(key) { - Entry::Vacant(vacant) => { - vacant.insert(value); - } - - Entry::Occupied(mut occupied) => { - occupied.get_mut().add_assign(value); - if occupied.get().is_zero() { - occupied.remove_entry(); - } - } - } - } -} - -impl FiniteMap for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - fn lookup(&self, key: &Key) -> Value { - self.value.get(key).cloned().unwrap_or_else(Value::zero) - } - - fn get_in_support(&self, key: &Key) -> Option<&Value> { - self.value.get(key) - } - - fn update(&mut self, key: &Key, f: F) - where - F: FnOnce(&mut Value), - { - match self.value.raw_entry_mut().from_key(key) { - RawEntryMut::Occupied(mut oe) => { - let val = oe.get_mut(); - f(val); - if val.is_zero() { - oe.remove(); - } - } - RawEntryMut::Vacant(ve) => { - let mut val = Value::zero(); - f(&mut val); - if !val.is_zero() { - ve.insert(key.clone(), val); - } - } - } - } - - fn update_owned(&mut self, key: Key, f: F) - where - F: FnOnce(&mut Value), - { - match self.value.entry(key) { - Entry::Occupied(mut oe) => { - let val = oe.get_mut(); - f(val); - if val.is_zero() { - oe.remove(); - } - } - Entry::Vacant(ve) => { - let mut val = Value::zero(); - f(&mut val); - ve.insert(val); - } - } - } - - fn support_size(&self) -> usize { - self.value.len() - } -} - -impl<'a, Key, Value> WithSupport<'a, Key> for &'a FiniteHashMap { - type SupportIterator = hash_map::Keys<'a, Key, Value>; - - fn support(self) -> Self::SupportIterator { - self.value.keys() - } -} - -impl Default for FiniteHashMap -where - Key: KeyProperties, -{ - fn default() -> Self { - Self::new() - } -} - -impl Add for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - type Output = Self; - - fn add(self, other: Self) -> Self { - fn add_inner( - mut this: FiniteHashMap, - other: FiniteHashMap, - ) -> FiniteHashMap - where - Key: KeyProperties, - Value: GroupValue, - { - for (key, value) in other.value { - match this.value.entry(key) { - Entry::Vacant(vacant) => { - vacant.insert(value); - } - - Entry::Occupied(mut occupied) => { - occupied.get_mut().add_assign(value); - if occupied.get().is_zero() { - occupied.remove_entry(); - } - } - } - } - - this - } - - if self.support_size() > other.support_size() { - add_inner(self, other) - } else { - add_inner(other, self) - } - } -} -impl AddByRef for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - fn add_by_ref(&self, other: &Self) -> Self { - fn add_inner( - mut this: FiniteHashMap, - other: &FiniteHashMap, - ) -> FiniteHashMap - where - Key: KeyProperties, - Value: GroupValue, - { - for (key, value) in &other.value { - match this.value.raw_entry_mut().from_key(key) { - RawEntryMut::Vacant(vacant) => { - vacant.insert(key.clone(), value.clone()); - } - - RawEntryMut::Occupied(mut occupied) => { - occupied.get_mut().add_assign_by_ref(value); - if occupied.get().is_zero() { - occupied.remove_entry(); - } - } - } - } - - this - } - - if self.support_size() > other.support_size() { - add_inner(self.clone(), other) - } else { - add_inner(other.clone(), self) - } - } -} - -impl AddAssign for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - fn add_assign(&mut self, other: Self) { - for (key, value) in other.value { - match self.value.entry(key) { - Entry::Vacant(vacant) => { - vacant.insert(value); - } - - Entry::Occupied(mut occupied) => { - occupied.get_mut().add_assign(value); - if occupied.get().is_zero() { - occupied.remove_entry(); - } - } - } - } - } -} - -impl AddAssignByRef for FiniteHashMap -where - KeyType: KeyProperties, - ValueType: GroupValue, -{ - fn add_assign_by_ref(&mut self, other: &Self) { - for (key, value) in &other.value { - match self.value.raw_entry_mut().from_key(key) { - RawEntryMut::Vacant(vacant) => { - vacant.insert(key.clone(), value.clone()); - } - - RawEntryMut::Occupied(mut occupied) => { - occupied.get_mut().add_assign_by_ref(value); - if occupied.get().is_zero() { - occupied.remove_entry(); - } - } - } - } - } -} - -impl HasZero for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - fn is_zero(&self) -> bool { - self.value.is_empty() - } - - fn zero() -> Self { - Self::default() - } -} - -impl NegByRef for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - fn neg_by_ref(&self) -> Self { - let mut result = self.clone(); - for val in result.value.values_mut() { - let mut tmp = Value::zero(); - swap(val, &mut tmp); - *val = tmp.neg(); - } - - result - } -} - -impl Neg for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - type Output = Self; - - fn neg(mut self) -> Self { - for val in self.value.values_mut() { - let mut tmp = Value::zero(); - swap(val, &mut tmp); - *val = tmp.neg(); - } - - self - } -} - -impl PartialEq for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ - fn eq(&self, other: &Self) -> bool { - self.value.eq(&other.value) - } -} - -impl Eq for FiniteHashMap -where - Key: KeyProperties, - Value: GroupValue, -{ -} - -impl Debug for FiniteHashMap -where - K: Debug, - V: Debug, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - self.value.fmt(f) - } -} diff --git a/src/algebra/finite_map/tests.rs b/src/algebra/finite_map/tests.rs deleted file mode 100644 index 9883300d..00000000 --- a/src/algebra/finite_map/tests.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::algebra::{ - AddAssignByRef, AddByRef, FiniteHashMap, FiniteMap, HasZero, MapBuilder, NegByRef, WithSupport, -}; -use std::ops::{Add, Neg}; - -type Map = FiniteHashMap; - -#[test] -fn hashmap_tests() { - let mut z = Map::with_capacity(5); - assert_eq!(0, z.support_size()); - assert_eq!(finite_map! {}, z); - assert_eq!(0, z.lookup(&0)); // not present -> 0 - assert_eq!(None, z.get_in_support(&0)); // not present -> 0 - assert_eq!(z, Map::zero()); - assert_eq!(z.support().cloned().collect::>(), vec![]); - assert!(z.is_zero()); - - let z2 = Map::new(); - assert_eq!(z, z2); - - z.increment(&0, 1); - assert_eq!(1, z.support_size()); - assert_eq!(finite_map! { 0 => 1 }, z); - assert_eq!(1, z.lookup(&0)); - assert_eq!(0, z.lookup(&1)); - assert_eq!(Some(1), z.get_in_support(&0).cloned()); - assert_eq!(None, z.get_in_support(&1)); - assert_eq!(z.support().cloned().collect::>(), vec![0]); - - assert_ne!(z, Map::zero()); - assert!(!z.is_zero()); - - z.increment_owned(2, 0); - assert_eq!(1, z.support_size()); - assert_eq!(finite_map! { 0 => 1 }, z); - assert_eq!(z.support().cloned().collect::>(), vec![0]); - - z.increment(&1, -1); - assert_eq!(2, z.support_size()); - assert_eq!(finite_map! { 0 => 1, 1 => -1 }, z); - let mut support = z.support().cloned().collect::>(); - support.sort_unstable(); - assert_eq!(support, vec![0, 1]); - - z.increment_owned(-1, 1); - assert_eq!(3, z.support_size()); - assert_eq!(finite_map! { -1 => 1, 0 => 1, 1 => -1 }, z); - let mut support = z.support().cloned().collect::>(); - support.sort_unstable(); - assert_eq!(support, vec![-1, 0, 1]); - - let d = z.neg_by_ref(); - assert_eq!(3, d.support_size()); - assert_eq!(finite_map! { -1 => -1, 0 => -1, 1 => 1 }, d); - assert_ne!(d, z); - let mut support = z.support().cloned().collect::>(); - support.sort_unstable(); - assert_eq!(support, vec![-1, 0, 1]); - - let d = z.clone().neg(); - assert_eq!(3, d.support_size()); - assert_eq!(finite_map! { -1 => -1, 0 => -1, 1 => 1 }, d); - assert_ne!(d, z); - - let i: Map = d.clone().into_iter().collect(); - assert_eq!(i, d); - - z.increment(&1, 1); - assert_eq!(2, z.support_size()); - assert_eq!(finite_map! { -1 => 1, 0 => 1 }, z); - - let mut z2 = z.add_by_ref(&z); - assert_eq!(2, z2.support_size()); - assert_eq!(finite_map! { -1 => 2, 0 => 2 }, z2); - - let z2_owned = z.clone().add(z.clone()); - assert_eq!(2, z2_owned.support_size()); - assert_eq!(finite_map! { -1 => 2, 0 => 2 }, z2_owned); - - z2.add_assign_by_ref(&z); - assert_eq!(2, z2.support_size()); - assert_eq!(finite_map! { -1 => 3, 0 => 3 }, z2); - - z2.increment_owned(4, 2); - assert_eq!(finite_map! { -1 => 3, 0 => 3, 4 => 2 }, z2); -} diff --git a/src/algebra/mod.rs b/src/algebra/mod.rs index bcb6ae9d..2a68d863 100644 --- a/src/algebra/mod.rs +++ b/src/algebra/mod.rs @@ -1,22 +1,21 @@ //! This module contains declarations of abstract algebraic concepts: //! monoids, groups, rings, etc. -#[macro_use] -pub mod finite_map; -mod checked_int; -mod zset; - -pub use checked_int::CheckedInt; -pub use finite_map::{FiniteHashMap, FiniteMap, MapBuilder, WithSupport}; -pub use zset::{IndexedZSet, IndexedZSetHashMap, ZSet, ZSetHashMap}; - use std::{ num::Wrapping, ops::{Add, AddAssign, Mul, Neg}, rc::Rc, }; +#[macro_use] +mod checked_int; +mod zset; + +pub use checked_int::CheckedInt; +pub use zset::{IndexedZSet, ZSet}; + /// A trait for types that have a zero value. +/// /// This is simlar to the standard Zero trait, but that /// trait depends on Add and HasZero doesn't. pub trait HasZero { @@ -116,15 +115,6 @@ where } } -/// A trait for objects with size. -pub trait WithNumEntries { - /// Returns the number of entries in `self`. - /// - /// Container types have a well-defined notion of size - /// (the number of entries in the container). Scalars have size 1. - fn num_entries(&self) -> usize; -} - /// Like the Add trait, but with arguments by reference. pub trait AddByRef { fn add_by_ref(&self, other: &Self) -> Self; @@ -220,7 +210,7 @@ impl GroupValue for T where } /// A Group with a multiplication operation is a Ring. -pub trait RingValue: GroupValue + MulByRef + HasOne {} +pub trait RingValue: GroupValue + Mul + MulByRef + HasOne {} /// Default implementation of RingValue for all types that have the required /// traits. @@ -235,6 +225,7 @@ impl RingValue for T where + AddAssignByRef + Neg + NegByRef + + Mul + MulByRef + HasOne { @@ -249,7 +240,7 @@ pub trait ZRingValue: RingValue { fn le0(&self) -> bool; } -/// Default implementation of ZRingValue for all types that have the required +/// Default implementation of `ZRingValue` for all types that have the required /// traits. impl ZRingValue for T where @@ -263,6 +254,7 @@ where + AddAssignByRef + Neg + NegByRef + + Mul + MulByRef + HasOne + Ord, diff --git a/src/algebra/zset/mod.rs b/src/algebra/zset/mod.rs index 467b6096..29a634e3 100644 --- a/src/algebra/zset/mod.rs +++ b/src/algebra/zset/mod.rs @@ -1,25 +1,29 @@ -#[cfg(test)] -pub(crate) mod tests; +#[macro_use] +mod zset_macro; -use crate::algebra::{ - finite_map::{FiniteHashMap, FiniteMap, KeyProperties, MapBuilder}, - ZRingValue, +use crate::{ + algebra::{GroupValue, HasOne, ZRingValue}, + trace::{cursor::Cursor, Batch, Builder}, + NumEntries, SharedRef, }; +// TODO: allow arbitrary `Time` types? +/// An indexed Z-set maps arbitrary keys to Z-set values. +pub trait IndexedZSet: + Batch