precedence_net/
network.rs

1use crate::activity::Activity;
2use crate::activity_builder::ActivityBuilder;
3use crate::{id, Error, NetworkBuilder, Result};
4use petgraph::graphmap::DiGraphMap;
5use petgraph::Direction;
6
7use std::collections::HashMap;
8use std::fmt::Write;
9
10#[derive(Default, Clone, Debug)]
11pub struct Network {
12    activities: HashMap<u64, Activity>,
13    connections: DiGraphMap<u64, ()>,
14    finish_earliest_start: f64,
15}
16
17impl Network {
18    pub fn builder() -> NetworkBuilder {
19        NetworkBuilder::default()
20    }
21
22    pub(crate) fn new(
23        activities: HashMap<u64, Activity>,
24        connections: DiGraphMap<u64, ()>,
25        finish_earliest_start: f64,
26    ) -> Self {
27        Self {
28            activities,
29            connections,
30            finish_earliest_start,
31        }
32    }
33
34    fn id_activity(&self, id: u64) -> Result<&Activity> {
35        self.activities.get(&id).ok_or(Error::UnknownId(id))
36    }
37
38    fn activity(&self, reference: &str) -> Result<&Activity> {
39        self.activities
40            .get(&id(&reference))
41            .ok_or_else(|| Error::UnknownReference(reference.to_string()))
42    }
43
44    /// A list of all activities in the precedence network
45    pub fn activities(&self) -> Result<Vec<&str>> {
46        let mut depth_and_references: Vec<(usize, &str)> = self
47            .activities
48            .values()
49            .map(|activity| (activity.depth, activity.reference.as_str()))
50            .collect();
51
52        depth_and_references.sort_by_key(|&(depth, _)| depth);
53        Ok(depth_and_references
54            .iter()
55            .map(|(_, reference)| *reference)
56            .collect())
57    }
58
59    /// A list of all edges in the precedence network
60    pub fn edges(&self) -> Result<Vec<(&str, &str)>> {
61        self.connections
62            .all_edges()
63            .map(|(origin_id, target_id, _)| {
64                match (self.id_activity(origin_id), self.id_activity(target_id)) {
65                    (Ok(origin), Ok(target)) => {
66                        Ok((origin.reference.as_str(), target.reference.as_str()))
67                    }
68                    (Err(error), _) => Err(error),
69                    (_, Err(error)) => Err(error),
70                }
71            })
72            .collect::<Result<Vec<(&str, &str)>>>()
73    }
74
75    /// Test whether two activities are connected
76    pub fn connected(&self, origin_reference: &str, target_reference: &str) -> Result<bool> {
77        let origin = self.activity(origin_reference)?;
78        let target = self.activity(target_reference)?;
79
80        match self.connections.edge_weight(id(origin), id(target)) {
81            Some(_) => Ok(true),
82            None => Ok(false),
83        }
84    }
85
86    /// A list of all start activities
87    ///
88    /// A start activity is an activity with no preceding activities.
89    pub fn start_activities(&self) -> Result<Vec<&str>> {
90        let mut start_activities = Vec::new();
91        for id in self.activities.keys() {
92            if self
93                .connections
94                .edges_directed(*id, Direction::Incoming)
95                .count()
96                == 0
97            {
98                start_activities.push(self.id_activity(*id)?.reference.as_str());
99            }
100        }
101        Ok(start_activities)
102    }
103
104    /// A list of all finish activities
105    ///
106    /// A finish activity is an activity with no dependant activities.
107    pub fn finish_activities(&self) -> Result<Vec<&str>> {
108        let mut finish_activities = Vec::new();
109        for id in self.activities.keys() {
110            if self
111                .connections
112                .edges_directed(*id, Direction::Outgoing)
113                .count()
114                == 0
115            {
116                finish_activities.push(self.id_activity(*id)?.reference.as_str());
117            }
118        }
119        Ok(finish_activities)
120    }
121
122    fn ids_to_references(&self, ids: Vec<u64>) -> Result<Vec<&str>> {
123        Ok(ids
124            .iter()
125            .map(|id| self.id_activity(*id))
126            .collect::<Result<Vec<&Activity>>>()?
127            .iter()
128            .map(|activity| activity.reference.as_str())
129            .collect())
130    }
131
132    /// Returns a list of activities that depend on the completion of the given activity
133    pub fn next_activities(&self, reference: &str) -> Result<Vec<&str>> {
134        self.ids_to_references(
135            self.connections
136                .neighbors_directed(id(&reference), Direction::Outgoing)
137                .collect(),
138        )
139    }
140
141    /// Returns a list of activities that the given activity depends on
142    pub fn previous_activities(&self, reference: &str) -> Result<Vec<&str>> {
143        self.ids_to_references(
144            self.connections
145                .neighbors_directed(id(&reference), Direction::Incoming)
146                .collect(),
147        )
148    }
149
150    /// The earliest possible finish of an activity
151    pub fn earliest_finish(&self, reference: &str) -> Result<f64> {
152        Ok(self.activity(reference)?.earliest_finish)
153    }
154
155    /// The latest possible finish of an activity
156    pub fn latest_finish(&self, reference: &str) -> Result<f64> {
157        Ok(self.activity(reference)?.latest_finish)
158    }
159
160    /// The latest possible start of an activity
161    pub fn latest_start(&self, reference: &str) -> Result<f64> {
162        Ok(self.activity(reference)?.latest_start)
163    }
164
165    /// The earliest possible start of an activity
166    pub fn earliest_start(&self, reference: &str) -> Result<f64> {
167        Ok(self.activity(reference)?.earliest_start)
168    }
169
170    /// Returns true if the given activity is on the critical path
171    pub fn on_critical_path(&self, reference: &str) -> Result<bool> {
172        let activity = self.activity(reference)?;
173        Ok(activity.on_critical_path())
174    }
175
176    /// A list of all activities on the critical path
177    pub fn critical_path_activities(&self) -> Result<Vec<&str>> {
178        Ok(self
179            .activities
180            .values()
181            .filter(|activity| activity.on_critical_path())
182            .map(|activity| activity.reference.as_str())
183            .collect())
184    }
185
186    /// The total float (or slack) of an activity
187    ///
188    /// Slack is how much the activity can be delayed to not cause
189    /// the final completion time of all activities to be delayed.
190    pub fn total_float(&self, reference: &str) -> Result<f64> {
191        Ok(self.activity(reference)?.total_float())
192    }
193
194    /// The free (or early) float of an activity
195    ///
196    /// The free float is how much the activity can be delayed to not cause
197    /// a delay in the earliest start of any subsequent activity.
198    pub fn free_float(&self, reference: &str) -> Result<f64> {
199        let activity = self.activity(reference)?;
200        let min_earliest_start = self
201            .next_activities(reference)?
202            .iter()
203            .map(|reference| self.activity(reference))
204            .collect::<Result<Vec<&Activity>>>()?
205            .iter()
206            .map(|activity| activity.earliest_start)
207            .fold(self.finish_earliest_start, f64::min);
208
209        let max_earliest_finish = self
210            .previous_activities(reference)?
211            .iter()
212            .map(|reference| self.activity(reference))
213            .collect::<Result<Vec<&Activity>>>()?
214            .iter()
215            .map(|activity| activity.earliest_finish)
216            .fold(0.0, f64::max);
217
218        Ok(min_earliest_start - max_earliest_finish - activity.duration())
219    }
220
221    /// The start of an activity, which can vary depending on how `activity_type` was set
222    pub fn start(&self, reference: &str) -> Result<f64> {
223        Ok(self.activity(reference)?.start())
224    }
225
226    /// The finish time of an activity, which can vary depending on how `activity_type` was set
227    pub fn finish(&self, reference: &str) -> Result<f64> {
228        Ok(self.activity(reference)?.finish())
229    }
230
231    /// Whether an activity is active at a certain time
232    pub fn active_at(&self, reference: &str, time: f64) -> Result<bool> {
233        Ok(self.activity(reference)?.active_on(time))
234    }
235
236    /// Whether an activity is active during a certain time range
237    pub fn active_during(&self, reference: &str, range: std::ops::Range<f64>) -> Result<bool> {
238        Ok(self.activity(reference)?.active_during(range))
239    }
240
241    /// The mean duration of the activity
242    ///
243    /// The mean is calculated as: `((4 * expected_duration) + minimum_duration + maximum_duration) / 6`
244    pub fn mean_duration(&self, reference: &str) -> Result<f64> {
245        Ok(self.activity(reference)?.mean_duration())
246    }
247
248    /// The variance of the duration of the activity
249    ///
250    /// The variance is calculated as: `variance ** 2`
251    pub fn variance(&self, reference: &str) -> Result<f64> {
252        Ok(self.activity(reference)?.variance())
253    }
254
255    /// The standard deviation of the duration of the activity
256    ///
257    /// This is calculated as: `(maximum_duration - minimum_duration) / 6`
258    pub fn standard_deviation(&self, reference: &str) -> Result<f64> {
259        Ok(self.activity(reference)?.standard_deviation())
260    }
261
262    /// The depth is a measure of how far the activity is from the start
263    ///
264    /// All start activities have depth 1 and all other activities have depth of the maximum depth
265    /// of their preceding activities + 1.
266    pub fn depth(&self, reference: &str) -> Result<usize> {
267        Ok(self.activity(reference)?.depth)
268    }
269
270    /// Returns a string containing a representation suitable for `dot` and other programs in
271    /// the GraphViz package.
272    pub fn to_dot(&self) -> Result<String> {
273        let mut s = String::new();
274
275        writeln!(&mut s, "digraph {{")?;
276        writeln!(&mut s, "rankdir=LR;")?;
277        writeln!(&mut s, "node [shape=Mrecord];")?;
278        for activity in self.activities.values() {
279            write!(&mut s, "\"{}\" ", activity.reference)?;
280            write!(&mut s, "[label=\"{}|", activity.description)?;
281            write!(&mut s, "{{")?;
282            write!(
283                &mut s,
284                "{{{}|{}}}",
285                activity.earliest_start, activity.latest_start,
286            )?;
287            write!(
288                &mut s,
289                "|{{{}|{{{}|{}}}}}",
290                activity.reference,
291                activity.total_float(),
292                self.free_float(&activity.reference)?
293            )?;
294            write!(
295                &mut s,
296                "|{{{}|{}}}}}",
297                activity.earliest_finish, activity.latest_finish
298            )?;
299            writeln!(&mut s, "|{}\"];", activity.duration())?;
300        }
301
302        for (from, to) in self.edges()?.iter() {
303            write!(&mut s, "\"{}\" -> \"{}\"", from, to)?;
304            if self.on_critical_path(from)? && self.on_critical_path(to)? {
305                write!(&mut s, "[style=bold]")?;
306            }
307            writeln!(&mut s, ";")?;
308        }
309        write!(&mut s, "}}")?;
310        Ok(s)
311    }
312
313    /// Returns a [NetworkBuilder] representing the current structure of the Network.
314    pub(crate) fn to_builder(&self) -> NetworkBuilder {
315        let mut activities = HashMap::<u64, ActivityBuilder>::new();
316        for (&id, activity) in self.activities.iter() {
317            activities.insert(id, activity.into());
318        }
319
320        NetworkBuilder::new(activities, self.connections.clone())
321    }
322
323    pub fn update_activity<F>(self, reference: &str, with_activity_builder: F) -> Result<Network>
324    where
325        F: FnOnce(&mut ActivityBuilder),
326    {
327        let mut network_builder = self.to_builder();
328        network_builder.update_activity(reference, with_activity_builder)?;
329
330        Self::try_from(network_builder)
331    }
332}
333
334impl TryFrom<NetworkBuilder> for Network {
335    type Error = crate::Error;
336
337    fn try_from(mut network_builder: NetworkBuilder) -> std::result::Result<Network, Self::Error> {
338        network_builder.build()
339    }
340}
341
342impl TryFrom<&mut NetworkBuilder> for Network {
343    type Error = crate::Error;
344
345    fn try_from(network_builder: &mut NetworkBuilder) -> std::result::Result<Network, Self::Error> {
346        network_builder.build()
347    }
348}