1use crate::petgraph::graph::Graph;
2use crate::petgraph::prelude::*;
3use std::fmt;
4
5#[derive(Debug, Clone, Copy)]
6pub struct Resource {
7 pub id: u32,
9 pub reusability: u16,
11}
12
13#[derive(Debug, Clone)]
14pub struct Activity {
15 pub earliest_start: u32,
17 pub earliest_finish: u32,
19 pub slack: u32,
21 pub resources: Vec<Resource>,
23 pub dummy: bool,
25}
26
27impl fmt::Display for Activity {
28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 if self.dummy {
30 write!(
31 f,
32 "dur: {}\t#r: {}",
33 self.earliest_finish - self.earliest_start,
34 self.resources.len()
35 )
36 } else if self.resources.len() > 1 {
37 write!(
38 f,
39 "dur: {}\t#r: {}\n({} .. {})",
40 self.earliest_finish - self.earliest_start,
41 self.resources.len(),
42 self.resources[0].id,
43 self.resources[self.resources.len() - 1].id
44 )
45 } else {
46 write!(
47 f,
48 "dur: {}\t#r: {}\n({})",
49 self.earliest_finish - self.earliest_start,
50 self.resources.len(),
51 self.resources[0].id
52 )
53 }
54 }
55}
56
57impl Activity {
58 pub fn start() -> Activity {
59 Activity {
60 earliest_start: 0,
61 earliest_finish: 0,
62 slack: 0,
63 resources: vec![],
64 dummy: true,
65 }
66 }
67
68 pub fn from_earliest_start(start: u32, duration: u32, resources: Vec<Resource>) -> Activity {
69 Activity {
70 earliest_start: start,
71 earliest_finish: start + duration,
72 slack: 0,
73 resources: resources.clone(),
74 dummy: false,
75 }
76 }
77
78 pub fn duration(&self) -> u32 {
79 self.earliest_finish - self.earliest_start
80 }
81}
82
83#[derive(Clone, Copy)]
84pub enum BuildUntil {
85 Exhaustion,
86 UntilNActivities(usize),
87}
88
89#[derive(Clone, Copy)]
90pub enum ResAlloc {
91 Single,
93 Dual,
95}
96
97fn place_dual_resource_activities<V: Copy, W: Copy, T: fmt::Display>(
98 mut activities: &mut Graph<Activity, T, Directed>,
99 resources: Vec<Resource>,
100 prev_node: NodeIndex<u32>,
101 activity_duration: u32,
102 edge_weighting: W,
103 activity_valid: V,
104 condition: BuildUntil,
105) where
106 V: Fn(Activity, Activity) -> bool,
107 W: Fn(Activity, Activity) -> T,
108{
109 let mut available_resources = resources.clone();
110
111 let get_node_uid = |res_1: Resource, res_2: Resource| {
112 if res_1.id > res_2.id {
113 (res_1.id, res_2.id)
114 } else {
115 (res_2.id, res_1.id)
116 }
117 };
118
119 let mut node_id: Option<(u32, u32)> = None;
120 let prev_activity = activities[prev_node].clone();
122 if !prev_activity.dummy {
123 let res_1 = prev_activity.resources[0];
125 let res_2 = prev_activity.resources[1];
126 for res in &mut available_resources {
127 if res.id == res_1.id || res.id == res_2.id {
128 res.reusability -= 1;
129 }
130 }
131
132 node_id = Some(get_node_uid(res_1, res_2));
133 }
134
135 let cur_time = prev_activity.earliest_finish;
136 let mut added_nodes = Vec::new();
137
138 for i in 0..available_resources.len() {
139 if available_resources[i].reusability == 0 {
140 continue; }
142
143 for j in 0..available_resources.len() {
144 if i == j {
145 continue;
146 }
147
148 if let BuildUntil::UntilNActivities(max_num) = condition {
149 if activities.node_count() >= max_num {
150 break;
151 }
152 }
153
154 if available_resources[j].reusability == 0 {
155 continue; }
157
158 if let Some(prev_id) = node_id {
159 if prev_id == get_node_uid(available_resources[i], available_resources[j]) {
161 continue;
162 }
163 }
164
165 let new_node_id = get_node_uid(available_resources[i], available_resources[j]);
166 if added_nodes.contains(&new_node_id) {
167 continue;
168 }
169 added_nodes.push(new_node_id);
170
171 let new_activity = Activity::from_earliest_start(
173 cur_time,
174 activity_duration,
175 vec![available_resources[i], available_resources[j]],
176 );
177
178 if !activity_valid(prev_activity.clone(), new_activity.clone()) {
180 continue;
181 }
182
183 let new_node = activities.add_node(new_activity.clone());
184
185 activities.add_edge(
186 prev_node,
187 new_node,
188 edge_weighting(prev_activity.clone(), new_activity),
189 );
190
191 place_dual_resource_activities(
192 &mut activities,
193 available_resources.clone(),
194 new_node,
195 activity_duration,
196 edge_weighting,
197 activity_valid,
198 condition,
199 );
200 }
201 }
202}
203
204fn place_sgl_resource_activities<V: Copy, W: Copy, T: fmt::Display>(
205 mut activities: &mut Graph<Activity, T, Directed>,
206 resources: Vec<Resource>,
207 prev_node: NodeIndex<u32>,
208 activity_duration: u32,
209 edge_weighting: W,
210 activity_valid: V,
211 condition: BuildUntil,
212) where
213 V: Fn(Activity, Activity) -> bool,
214 W: Fn(Activity, Activity) -> T,
215{
216 let mut available_resources = resources.clone();
217
218 let mut node_id: Option<u32> = None;
219 let prev_activity = activities[prev_node].clone();
221 if !prev_activity.dummy {
222 let res_1 = prev_activity.resources[0];
224 for res in &mut available_resources {
225 if res.id == res_1.id {
226 res.reusability -= 1;
227 break;
228 }
229 }
230
231 node_id = Some(res_1.id);
232 }
233
234 let cur_time = prev_activity.earliest_finish;
235 let mut added_nodes = Vec::new();
236
237 for resource in available_resources.clone() {
238 if resource.reusability == 0 {
239 continue; }
241
242 if let BuildUntil::UntilNActivities(max_num) = condition {
243 if activities.node_count() >= max_num {
244 break;
245 }
246 }
247
248 if let Some(prev_id) = node_id {
249 if prev_id == resource.id {
251 continue;
252 }
253 }
254
255 let new_node_id = resource.id;
256 if added_nodes.contains(&new_node_id) {
257 continue;
258 }
259 added_nodes.push(new_node_id);
260
261 let new_activity =
263 Activity::from_earliest_start(cur_time, activity_duration, vec![resource]);
264
265 if !activity_valid(prev_activity.clone(), new_activity.clone()) {
267 continue;
268 }
269
270 let new_node = activities.add_node(new_activity.clone());
271
272 activities.add_edge(
273 prev_node,
274 new_node,
275 edge_weighting(prev_activity.clone(), new_activity),
276 );
277
278 place_sgl_resource_activities(
279 &mut activities,
280 available_resources.clone(),
281 new_node,
282 activity_duration,
283 edge_weighting,
284 activity_valid,
285 condition,
286 );
287 }
288}
289
290pub fn build_equal_activities<V: Copy, W: Copy, T: fmt::Display>(
291 resources: Vec<Resource>,
292 activity_duration: u32,
293 edge_weighting: W,
294 activity_valid: V,
295 condition: BuildUntil,
296 alloc: ResAlloc,
297) -> Graph<Activity, T, Directed>
298where
299 V: Fn(Activity, Activity) -> bool,
300 W: Fn(Activity, Activity) -> T,
301{
302 let mut activities: Graph<Activity, T, Directed> = Graph::new();
303 let start_node = activities.add_node(Activity::start());
304
305 match alloc {
306 ResAlloc::Dual => place_dual_resource_activities(
307 &mut activities,
308 resources,
309 start_node,
310 activity_duration,
311 edge_weighting,
312 activity_valid,
313 condition,
314 ),
315 ResAlloc::Single => place_sgl_resource_activities(
316 &mut activities,
317 resources,
318 start_node,
319 activity_duration,
320 edge_weighting,
321 activity_valid,
322 condition,
323 ),
324 };
325
326 activities
327}
328
329#[test]
330fn test_builder_dual_resource() {
331 use crate::io::export_dot;
332 let sc1 = Resource {
333 id: 1,
334 reusability: 5,
335 };
336 let sc2 = Resource {
337 id: 2,
338 reusability: 5,
339 };
340 let sc3 = Resource {
341 id: 3,
342 reusability: 5,
343 };
344
345 let always_true = |_, _| true;
346
347 let activities = build_equal_activities(
348 vec![sc1, sc2, sc3],
349 60,
350 always_true,
351 always_true,
352 BuildUntil::Exhaustion,
353 ResAlloc::Dual,
354 );
355 assert_eq!(activities.node_count(), 298);
356 assert_eq!(activities.edge_count(), 297);
357 export_dot(&activities, "dotgraphs/test_builder_exhaustion_dual.dot");
358
359 let activities = build_equal_activities(
361 vec![sc1, sc2, sc3],
362 60,
363 always_true,
364 always_true,
365 BuildUntil::UntilNActivities(100),
366 ResAlloc::Dual,
367 );
368 assert_eq!(activities.node_count(), 100);
369 assert_eq!(activities.edge_count(), 99);
370 export_dot(&activities, "dotgraphs/test_builder_untilN_dual.dot");
371}
372
373#[test]
374fn test_builder_sgl_resource() {
375 use crate::io::export_dot;
376 let sc1 = Resource {
377 id: 1,
378 reusability: 1,
379 };
380 let sc2 = Resource {
381 id: 2,
382 reusability: 1,
383 };
384 let sc3 = Resource {
385 id: 3,
386 reusability: 1,
387 };
388
389 let always_true = |_, _| true;
390
391 let activities = build_equal_activities(
392 vec![sc1, sc2, sc3],
393 60,
394 always_true,
395 always_true,
396 BuildUntil::Exhaustion,
397 ResAlloc::Single,
398 );
399 export_dot(&activities, "dotgraphs/test_builder_exhaustion_single.dot");
400
401 let activities = build_equal_activities(
402 vec![sc1, sc2, sc3],
403 60,
404 always_true,
405 always_true,
406 BuildUntil::UntilNActivities(25),
407 ResAlloc::Single,
408 );
409 export_dot(&activities, "dotgraphs/test_builder_untilN_single.dot");
410}