inc_complete/db/
mod.rs

1use crate::cell::CellData;
2use crate::{Cell, Computation};
3use petgraph::graph::DiGraph;
4
5mod handle;
6mod tests;
7
8pub use handle::DbHandle;
9
10const START_VERSION: u32 = 1;
11
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct Db<C: Computation> {
14    cells: DiGraph<CellData, ()>,
15    version: u32,
16    storage: C::Storage,
17}
18
19impl<C: Computation> Db<C>
20where
21    C::Storage: Default,
22{
23    pub fn new() -> Self {
24        Self {
25            cells: DiGraph::default(),
26            version: START_VERSION,
27            storage: Default::default(),
28        }
29    }
30}
31
32impl<C: Computation> Db<C> {
33    /// True if a given input is stale and needs to be re-computed.
34    /// Inputs which have never been computed are also considered stale.
35    ///
36    /// This does not actually re-compute the input.
37    pub fn is_stale<Concrete: Computation>(&self, input: &Concrete) -> bool {
38        // If the cell doesn't exist, it is definitely stale
39        let Some(cell) = self.get_cell(input) else {
40            println!("{} is stale", std::any::type_name::<Concrete>());
41            return true;
42        };
43        let r = self.is_stale_cell(cell);
44        println!("{} is stale: {r}", std::any::type_name::<Concrete>());
45        r
46    }
47
48    /// True if a given cell is stale and needs to be re-computed.
49    /// This does not actually re-compute the input.
50    pub fn is_stale_cell(&self, cell: Cell) -> bool {
51        let computation_id = self.cells[cell.index()].computation_id;
52        if C::output_is_unset::<C>(cell, computation_id, computation_id, self) {
53            println!("output {} is unset", computation_id);
54            return true;
55        }
56
57        let neighbors = self.cells.neighbors(cell.index()).collect::<Vec<_>>();
58
59        // if any dependency may have changed, this cell is stale
60        neighbors.into_iter().any(|dependency_id| {
61            let dependency = &self.cells[dependency_id];
62            let cell = &self.cells[cell.index()];
63
64            dependency.last_verified_version != self.version
65                || dependency.last_updated_version > cell.last_verified_version
66        })
67    }
68
69    /// Return the corresponding Cell for a given input, if it exists.
70    ///
71    /// This will not update any values.
72    pub fn get_cell<ConcreteC: Computation>(&self, input: &ConcreteC) -> Option<Cell> {
73        C::dispatch_input_to_cell(input, &self.storage)
74    }
75
76    pub fn get_or_insert_cell<ConcreteC>(&mut self, input: ConcreteC) -> Cell
77    where
78        ConcreteC: Computation,
79    {
80        if let Some(cell) = C::dispatch_input_to_cell(&input, &self.storage) {
81            cell
82        } else {
83            let computation_id = C::computation_id_of::<ConcreteC>();
84
85            let new_id = self.cells.add_node(CellData::new(computation_id));
86            let cell = Cell::new(new_id);
87            C::dispatch_insert_new_cell(cell, input, &mut self.storage);
88            cell
89        }
90    }
91
92    /// Updates an input with a new value
93    ///
94    /// May panic in Debug mode if the input is not an input - ie. it has at least 1 dependency.
95    /// Note that this step is skipped when compiling in Release mode.
96    pub fn update_input<ConcreteC: Computation>(
97        &mut self,
98        input: ConcreteC,
99        new_value: ConcreteC::Output,
100    ) where
101        ConcreteC: std::fmt::Debug,
102        C::Output: Eq,
103    {
104        let debug = format!("{input:?}");
105        let cell_id = self.get_or_insert_cell(input);
106        debug_assert!(
107            self.is_input(cell_id),
108            "`{debug:?}` is not an input - inputs must have 0 dependencies",
109        );
110
111        // need to split dispatch_run into dispatch_run and dispatch_update
112        let cell = &self.cells[cell_id.index()];
113        let computation_id = cell.computation_id;
114
115        let changed = C::dispatch_update_output::<ConcreteC, C>(
116            cell_id,
117            computation_id,
118            computation_id,
119            new_value,
120            self,
121        );
122        let cell = &mut self.cells[cell_id.index()];
123
124        if changed {
125            self.version += 1;
126            cell.last_updated_version = self.version;
127            cell.last_verified_version = self.version;
128        } else {
129            cell.last_verified_version = self.version;
130        }
131    }
132
133    fn is_input(&self, cell: Cell) -> bool {
134        self.cells.neighbors(cell.index()).count() == 0
135    }
136
137    pub(crate) fn handle(&mut self, cell: Cell) -> DbHandle<C> {
138        DbHandle::new(self, cell)
139    }
140
141    pub fn storage(&self) -> &C::Storage {
142        &self.storage
143    }
144
145    pub fn storage_mut(&mut self) -> &mut C::Storage {
146        &mut self.storage
147    }
148
149    #[cfg(test)]
150    pub(crate) fn unwrap_cell_value<Concrete: Computation>(&self, input: &Concrete) -> &CellData
151    where
152        Concrete: std::fmt::Debug,
153    {
154        let cell = self
155            .get_cell(input)
156            .unwrap_or_else(|| panic!("unwrap_cell_value: Expected cell for `{input:?}` to exist"));
157        &self.cells[cell.index()]
158    }
159}
160
161impl<C: Computation + Clone> Db<C>
162where
163    C::Output: Eq,
164{
165    /// Similar to `update_input` but runs the compute function
166    /// instead of accepting a given value. This also will not update
167    /// `self.version`
168    fn run_compute_function(&mut self, cell_id: Cell)
169    where
170        C::Output: Eq,
171    {
172        let cell = &self.cells[cell_id.index()];
173        let computation_id = cell.computation_id;
174
175        let changed = C::dispatch_run::<C>(cell_id, computation_id, computation_id, self);
176
177        let cell = &mut self.cells[cell_id.index()];
178        cell.last_verified_version = self.version;
179
180        if changed {
181            cell.last_updated_version = self.version;
182        }
183    }
184
185    /// Trigger an update of the given cell, recursively checking and re-running any out of date
186    /// dependencies.
187    fn update_cell(&mut self, cell_id: Cell) {
188        let cell = &self.cells[cell_id.index()];
189
190        if cell.last_verified_version != self.version {
191            // if any dependency may have changed, update
192            if self.is_stale_cell(cell_id) {
193                self.run_compute_function(cell_id);
194            } else {
195                let cell = &mut self.cells[cell_id.index()];
196                cell.last_verified_version = self.version;
197            }
198        }
199    }
200
201    /// Retrieves the up to date value for the given computation, re-running any dependencies as
202    /// necessary.
203    ///
204    /// This function can panic if the dynamic type of the value returned by `compute.run(..)` is not `T`.
205    pub fn get<Concrete: Computation>(&mut self, compute: Concrete) -> &Concrete::Output {
206        let cell_id = self.get_or_insert_cell(compute);
207        self.get_with_cell::<Concrete>(cell_id)
208    }
209
210    /// Retrieves the up to date value for the given cell, re-running any dependencies as
211    /// necessary.
212    ///
213    /// This function can panic if the dynamic type of the value returned by `compute.run(..)` is not `T`.
214    pub fn get_with_cell<Concrete: Computation>(&mut self, cell_id: Cell) -> &Concrete::Output {
215        self.update_cell(cell_id);
216
217        let computation_id = self.cells[cell_id.index()].computation_id;
218        println!("get_with_cell get_storage_mut::<{}> with id {}", std::any::type_name::<Concrete>(), computation_id);
219        let container = C::get_storage_mut::<Concrete>(computation_id, self.storage_mut());
220        Concrete::get_function_and_output(cell_id, container)
221            .1
222            .expect("cell result should have been computed already")
223    }
224}