precedence_net/
network_builder.rs

1use crate::activity;
2use crate::activity::Activity;
3use crate::activity_builder::ActivityBuilder;
4use crate::{id, Error, Network, Result};
5use crate::{DurationType, StartType};
6use petgraph::graphmap::DiGraphMap;
7use petgraph::Direction;
8use std::collections::HashMap;
9
10#[derive(Default)]
11pub struct NetworkBuilder {
12    activities: HashMap<u64, ActivityBuilder>,
13    connections: DiGraphMap<u64, ()>,
14}
15
16impl NetworkBuilder {
17    pub(crate) fn new(
18        activities: HashMap<u64, ActivityBuilder>,
19        connections: DiGraphMap<u64, ()>,
20    ) -> NetworkBuilder {
21        NetworkBuilder {
22            activities,
23            connections,
24        }
25    }
26
27    /// Add an activity to the network.
28    ///
29    /// The `reference` and `description` of the activity will be set to `reference`. The `minimum_duration`, `expected_duration` and `maximum_duration` will all be set to `duration`. The `start_type`
30    /// will be set to `StartType::Earliest` and the `duration_type` will be set to `DurationType::Expected`.
31    pub fn add_activity(&mut self, reference: &str, duration: f64) -> Result<()> {
32        self.add_extended_activity(
33            reference,
34            reference,
35            duration,
36            duration,
37            duration,
38            StartType::Earliest,
39            DurationType::Expected,
40        )
41    }
42
43    /// Removes an activity and all edges associated with it
44    pub fn remove_activity(&mut self, reference: &str) -> Result<()> {
45        self.activities
46            .remove(&id(&reference))
47            .ok_or_else(|| Error::UnknownReference(reference.to_string()))?;
48
49        if !self.connections.remove_node(id(&reference)) {
50            return Err(Error::UnknownReference(reference.to_string()));
51        };
52
53        Ok(())
54    }
55
56    pub fn remove_edge(&mut self, origin_reference: &str, target_reference: &str) -> Result<()> {
57        let origin = self.activity(id(&origin_reference))?;
58        let target = self.activity(id(&target_reference))?;
59
60        if self
61            .connections
62            .remove_edge(id(&origin), id(&target))
63            .is_none()
64        {
65            return Err(Error::UnknownEdge(
66                origin_reference.to_string(),
67                target_reference.to_string(),
68            ));
69        }
70
71        Ok(())
72    }
73
74    /// Add a customised activity to the network.
75    ///
76    /// This method is for creating activities with differing durations, start types or duration
77    /// types.
78    ///
79    /// ```ignore
80    /// network_builder.add_extended_activity("develop",
81    ///                                       "Develop System",
82    ///                                       1.0, 3.0, 5.0,
83    ///                                       StartType::Earliest,
84    ///                                       DurationType::Expected)?;
85    /// ```
86    pub fn add_extended_activity(
87        &mut self,
88        reference: &str,
89        description: &str,
90        minimum_duration: f64,
91        expected_duration: f64,
92        maximum_duration: f64,
93        start_type: StartType,
94        duration_type: DurationType,
95    ) -> Result<()> {
96        let mut activity_builder = ActivityBuilder::new(
97            reference,
98            description,
99            expected_duration,
100            minimum_duration,
101            maximum_duration,
102        );
103        activity_builder.start_type = start_type;
104        activity_builder.duration_type = duration_type;
105
106        self.activities
107            .insert(id(&reference), activity_builder.clone());
108
109        self.connections.add_node(id(&activity_builder));
110
111        Ok(())
112    }
113
114    /// Connect two activities together
115    ///
116    /// ```ignore
117    /// // a -> b -> c
118    /// network_builder.connect("a", "b")?;
119    /// network_builder.connect("b", "c")?;
120    /// ```
121    pub fn connect(&mut self, origin_reference: &str, target_reference: &str) -> Result<()> {
122        let origin = self
123            .activities
124            .get(&id(&origin_reference))
125            .ok_or_else(|| Error::UnknownReference(origin_reference.to_string()))?;
126
127        let target = self
128            .activities
129            .get(&id(&target_reference))
130            .ok_or_else(|| Error::UnknownReference(target_reference.to_string()))?;
131
132        self.connections.add_edge(id(&origin), id(&target), ());
133
134        Ok(())
135    }
136
137    fn mut_activity(&mut self, id: u64) -> Result<&mut ActivityBuilder> {
138        self.activities.get_mut(&id).ok_or(Error::UnknownId(id))
139    }
140
141    fn activity(&self, id: u64) -> Result<&ActivityBuilder> {
142        self.activities.get(&id).ok_or(Error::UnknownId(id))
143    }
144
145    fn previous_activities(&self, id: u64) -> Result<Vec<&ActivityBuilder>> {
146        self.connections
147            .neighbors_directed(id, Direction::Incoming)
148            .map(|ref id| self.activities.get(id).ok_or(Error::UnknownId(*id)))
149            .collect::<Result<Vec<&ActivityBuilder>>>()
150    }
151
152    fn next_activities(&self, id: u64) -> Result<Vec<&ActivityBuilder>> {
153        self.connections
154            .neighbors_directed(id, Direction::Outgoing)
155            .map(|ref id| self.activities.get(id).ok_or(Error::UnknownId(*id)))
156            .collect::<Result<Vec<&ActivityBuilder>>>()
157    }
158
159    fn forward_pass(&mut self) -> Result<()> {
160        let mut activity_ids: Vec<u64> = Vec::new();
161
162        for id in self
163            .connections
164            .neighbors_directed(id(&activity::START_REFERENCE), Direction::Outgoing)
165        {
166            activity_ids.push(id);
167        }
168
169        while !activity_ids.is_empty() {
170            let current_activity_id = activity_ids.remove(0);
171            let prev_activities = self.previous_activities(current_activity_id)?;
172            if prev_activities
173                .iter()
174                .all(|activity| activity.earliest_finish.is_some())
175            {
176                let earliest_start = prev_activities
177                    .iter()
178                    .map(|a| a.earliest_finish.ok_or(Error::FieldNotSet))
179                    .collect::<Result<Vec<f64>>>()?
180                    .iter()
181                    .copied()
182                    .fold(0.0, f64::max);
183
184                let depth = prev_activities
185                    .iter()
186                    .map(|a| a.depth)
187                    .max()
188                    .ok_or(Error::FieldNotSet)?;
189
190                let activity = self.mut_activity(current_activity_id)?;
191                activity.earliest_start(earliest_start);
192                activity.earliest_finish(earliest_start + activity.duration()?);
193                activity.depth = depth + 1;
194
195                for next_activity in self.next_activities(current_activity_id)?.iter() {
196                    activity_ids.push(id(&next_activity.reference));
197                }
198            } else {
199                activity_ids.push(current_activity_id)
200            }
201        }
202        Ok(())
203    }
204
205    fn backward_pass(&mut self) -> Result<()> {
206        let mut activity_ids: Vec<u64> = Vec::new();
207
208        for id in self
209            .connections
210            .neighbors_directed(id(&activity::FINISH_REFERENCE), Direction::Incoming)
211        {
212            activity_ids.push(id);
213        }
214
215        while !activity_ids.is_empty() {
216            let current_activity_id = activity_ids.remove(0);
217            let next_activities = self.next_activities(current_activity_id)?;
218            if next_activities
219                .iter()
220                .all(|activity| activity.latest_start.is_some())
221            {
222                let latest_finish = next_activities
223                    .iter()
224                    .map(|a| a.latest_start.ok_or(Error::FieldNotSet))
225                    .collect::<Result<Vec<f64>>>()?
226                    .iter()
227                    .copied()
228                    .fold(f64::MAX, f64::min);
229
230                let activity = self.mut_activity(current_activity_id)?;
231                activity.latest_finish(latest_finish);
232                activity.latest_start(latest_finish - activity.duration()?);
233
234                for prev_activity in self.previous_activities(current_activity_id)?.iter() {
235                    activity_ids.push(id(&prev_activity.reference));
236                }
237            } else {
238                activity_ids.push(current_activity_id)
239            }
240        }
241
242        Ok(())
243    }
244
245    /// Creates a [Network] from the [NetworkBuilder]
246    ///
247    /// ```ignore
248    /// let network = network_builder.build();
249    pub(crate) fn build(&mut self) -> Result<Network> {
250        if petgraph::algo::is_cyclic_directed(&self.connections.clone().into_graph::<usize>()) {
251            return Err(Error::CycleDetected);
252        }
253
254        for activity in self.activities.values_mut() {
255            activity.earliest_start = None;
256            activity.latest_start = None;
257            activity.earliest_finish = None;
258            activity.latest_finish = None;
259        }
260
261        let mut start_activity = ActivityBuilder::new(
262            activity::START_REFERENCE,
263            activity::START_REFERENCE,
264            0.0,
265            0.0,
266            0.0,
267        );
268
269        start_activity.earliest_start(0.0);
270        start_activity.earliest_finish(0.0);
271        start_activity.latest_start(0.0);
272        start_activity.latest_finish(0.0);
273
274        let start_id = id(&activity::START_REFERENCE);
275
276        self.connections.add_node(start_id);
277
278        for &id in self.activities.keys() {
279            if self
280                .connections
281                .edges_directed(id, Direction::Incoming)
282                .count()
283                == 0
284            {
285                self.connections.add_edge(start_id, id, ());
286            }
287        }
288        self.activities.insert(start_id, start_activity);
289
290        let finish_id = id(&activity::FINISH_REFERENCE);
291        let finish_activity = ActivityBuilder::new(
292            activity::FINISH_REFERENCE,
293            activity::FINISH_REFERENCE,
294            0.0,
295            0.0,
296            0.0,
297        );
298
299        self.connections.add_node(finish_id);
300
301        for &id in self.activities.keys() {
302            if id == finish_id {
303                continue;
304            }
305            if self
306                .connections
307                .edges_directed(id, Direction::Outgoing)
308                .count()
309                == 0
310            {
311                self.connections.add_edge(id, finish_id, ());
312            }
313        }
314        self.activities.insert(finish_id, finish_activity);
315
316        self.forward_pass()?;
317
318        let finish_activity = self.mut_activity(id(&activity::FINISH_REFERENCE))?;
319        let latest_finish = finish_activity.earliest_finish.ok_or(Error::FieldNotSet)?;
320        let latest_start = latest_finish - finish_activity.duration()?;
321
322        finish_activity.latest_finish(latest_finish);
323        finish_activity.latest_start(latest_start);
324
325        self.backward_pass()?;
326
327        let mut activities = HashMap::<u64, Activity>::new();
328        let mut connections = DiGraphMap::<u64, ()>::new();
329
330        for &id in self.activities.keys() {
331            let activity = self.activity(id)?.try_into()?;
332            activities.insert(id, activity);
333
334            connections.add_node(id);
335            for next_neighboud_id in self.connections.neighbors_directed(id, Direction::Outgoing) {
336                connections.add_edge(id, next_neighboud_id, ());
337            }
338        }
339
340        activities.remove(&start_id);
341        activities.remove(&finish_id);
342
343        self.activities.remove(&start_id);
344        self.activities.remove(&finish_id);
345
346        connections.remove_node(start_id);
347        connections.remove_node(finish_id);
348
349        self.connections.remove_node(start_id);
350        self.connections.remove_node(finish_id);
351
352        let network = Network::new(activities, connections, latest_start);
353
354        Ok(network)
355    }
356
357    pub fn update_activity<F>(&mut self, reference: &str, with_activity_builder: F) -> Result<()>
358    where
359        F: FnOnce(&mut ActivityBuilder),
360    {
361        let activity_builder = self.mut_activity(id(&reference))?;
362        with_activity_builder(activity_builder);
363
364        Ok(())
365    }
366}
367
368impl From<Network> for NetworkBuilder {
369    fn from(network: Network) -> NetworkBuilder {
370        network.to_builder()
371    }
372}
373
374impl From<&Network> for NetworkBuilder {
375    fn from(network: &Network) -> NetworkBuilder {
376        network.to_builder()
377    }
378}