1use std::collections::{HashMap, HashSet};
5use std::sync::atomic::{AtomicBool, Ordering};
6
7use crate::hyperpath_queue::PriorityQueue;
8use crate::transit_network::Link;
9
10pub struct Strategy<'a> {
12 pub labels: HashMap<String, f64>,
14 pub freqs: HashMap<String, f64>,
17 pub a_set: Vec<&'a Link>,
19}
20
21pub(crate) const ALPHA: f64 = 1.0;
26
27pub static VERBOSE: AtomicBool = AtomicBool::new(false);
28
29pub(crate) fn verbose() -> bool {
30 VERBOSE.load(Ordering::Relaxed)
31}
32
33pub fn find_optimal_strategy<'a>(
34 all_links: &'a [Link],
35 all_stops: &HashSet<String>,
36 destination: &str,
37) -> Strategy<'a> {
38 if verbose() {
40 println!("1.1 Initialization \\\\");
41 }
42
43 let mut n_id: HashMap<&str, usize> = HashMap::with_capacity(all_stops.len());
49 let mut n_name: Vec<&str> = Vec::with_capacity(all_stops.len());
50 macro_rules! intern {
51 ($s:expr) => {{
52 let s: &str = $s;
53 match n_id.get(s) {
54 Some(&id) => id,
55 None => {
56 let id = n_name.len();
57 n_id.insert(s, id);
58 n_name.push(s);
59 id
60 }
61 }
62 }};
63 }
64
65 for stop in all_stops {
66 intern!(stop.as_str());
67 }
68 let n_stops = n_name.len();
69
70 let m = all_links.len();
71 let mut l_from = vec![0usize; m];
72 let mut l_to = vec![0usize; m];
73 let mut l_cost = vec![0.0f64; m];
74 let mut l_head = vec![0.0f64; m];
75 for (k, link) in all_links.iter().enumerate() {
76 l_from[k] = intern!(link.from_node.as_str());
77 l_to[k] = intern!(link.to_node.as_str());
78 l_cost[k] = link.travel_cost;
79 l_head[k] = link.headway;
80 }
81 let dest_id = intern!(destination);
82 let n = n_name.len();
83
84 let mut u = vec![f64::INFINITY; n];
86 let mut f = vec![0.0f64; n];
87 u[dest_id] = 0.0;
88 if verbose() {
89 for (id, name) in n_name.iter().enumerate() {
90 println!("$f_{{{}}} = 0$ \\\\ ", name);
91 if id == dest_id {
92 println!("$u_{{{}}} = 0$ \\\\ ", name);
93 } else {
94 println!("$u_{{{}}} = Infinity$ \\\\ ", name);
95 }
96 }
97 }
98
99 let mut overline_a: Vec<Option<usize>> = Vec::with_capacity(m / 2);
103 let mut a_set_idx: Vec<Vec<usize>> = vec![Vec::new(); n];
104
105 let mut adj_by_to: Vec<Vec<usize>> = vec![Vec::new(); n];
108 for k in 0..m {
109 adj_by_to[l_to[k]].push(k);
110 }
111
112 let mut pq = PriorityQueue::with_capacity(m);
116 for k in 0..m {
117 pq.push(k, u[l_to[k]] + l_cost[k]);
118 }
119 pq.init();
120 if verbose() {
121 pq.print();
122 }
123 while pq.len() > 0 {
124 if verbose() {
126 pq.print();
127 }
128 let entry_id = match pq.pop() {
129 Some(id) => id,
130 None => break,
131 };
132 let priority = pq.priority(entry_id);
133 if priority.is_infinite() && priority > 0.0 {
134 break;
135 }
136 let k = pq.link(entry_id);
137 let i = l_from[k];
138 let j = l_to[k];
139 let sum_uc = u[j] + l_cost[k];
140
141 if verbose() {
143 println!("Process: $a = (i, j) = ({}, {})$, \\\\ ", n_name[i], n_name[j]);
144 }
145 if f[i].is_infinite() {
149 continue;
150 }
151 if u[i] <= sum_uc {
178 continue;
179 }
180 if verbose() {
181 println!(
182 "\\quad $u_i \\leq u_j + c_a : {} \\leq {}$ - FALSE \\\\ ",
183 u[i], sum_uc
184 );
185 }
186 if l_head[k] <= 0.0 {
187 u[i] = sum_uc;
193 f[i] = f64::INFINITY;
194 for &idx in &a_set_idx[i] {
195 overline_a[idx] = None;
196 }
197 a_set_idx[i].clear();
198 overline_a.push(Some(k));
199 a_set_idx[i].push(overline_a.len() - 1);
200 if verbose() {
201 println!(
202 "\\quad no-wait link: $u_i = u_j + c_a = {}$, $f_i = \\infty$, basket replaced by $({}, {})$ \\\\ ",
203 sum_uc, n_name[i], n_name[j]
204 );
205 }
206 } else {
207 let freq = 1.0 / l_head[k];
208 if verbose() {
209 println!("\\quad $f_a = {}$ \\\\ ", freq);
210 println!("\\quad $u_j + c_a = {}$ \\\\ ", sum_uc);
211 println!("\\quad $u_i = {}$ \\\\ ", u[i]);
212 }
213 let new_u = if f[i] == 0.0 {
214 (ALPHA + freq * sum_uc) / freq
216 } else {
217 (f[i] * u[i] + freq * sum_uc) / (f[i] + freq)
218 };
219 u[i] = new_u;
220 f[i] += freq;
221 overline_a.push(Some(k));
222 a_set_idx[i].push(overline_a.len() - 1);
223 if verbose() {
224 println!(
225 "\\quad$u_i = \\frac{{f_i * u_i + f_a * (u_j + c_a)}}{{f_i + f_a}} = {}$, $f_i = {}$ \\\\ ",
226 new_u, f[i]
227 );
228 println!(
229 "\\quad $\\overline{{A}} = \\overline{{A}} \\cup {{({}, {})}}$ \\\\ ",
230 n_name[i], n_name[j]
231 );
232 }
233 }
234
235 for &kk in &adj_by_to[i] {
238 pq.update(kk, u[i] + l_cost[kk]);
239 }
240 if verbose() {
241 println!("Node labels: \\\\");
242 for (id, name) in n_name.iter().enumerate() {
243 println!("${} -> (u_i, f_i) = ({}, {})$ \\\\ ", name, u[id], f[id]);
244 }
245 }
246 }
247
248 let a_set: Vec<&'a Link> = overline_a
251 .into_iter()
252 .flatten()
253 .map(|k| &all_links[k])
254 .collect();
255
256 let mut labels: HashMap<String, f64> = HashMap::with_capacity(n_stops);
259 let mut freqs: HashMap<String, f64> = HashMap::with_capacity(n_stops);
260 for id in 0..n_stops {
261 labels.insert(n_name[id].to_string(), u[id]);
262 freqs.insert(n_name[id].to_string(), f[id]);
263 }
264
265 Strategy {
266 labels,
267 freqs,
268 a_set,
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 #[test]
277 fn test_hyper_paths() {
278 VERBOSE.store(true, Ordering::Relaxed);
279 let all_nodes: HashSet<String> = ["A", "X", "X2", "Y", "Y3", "B"]
280 .iter()
281 .map(|s| s.to_string())
282 .collect();
283 let all_links = vec![
284 Link::new("A", "B", "Line 1", 25.0, 6.0),
285 Link::new("A", "X2", "Line 2", 7.0, 6.0),
286 Link::new("X2", "X", "Line 2", 0.0, 0.0),
287 Link::new("X", "X2", "Line 2", 0.0, 6.0),
288 Link::new("X2", "Y", "Line 2", 6.0, 0.0),
289 Link::new("Y3", "Y", "Line 3", 0.0, 15.0),
290 Link::new("Y", "B", "Line 4", 10.0, 3.0),
291 Link::new("X", "Y3", "Line 3", 4.0, 15.0),
292 Link::new("Y", "Y3", "Line 3", 0.0, 15.0),
293 Link::new("Y3", "B", "Line 3", 4.0, 0.0),
294 ];
295 let destination_node = "B";
296 let ops = find_optimal_strategy(&all_links, &all_nodes, destination_node);
297
298 const EPS: f64 = 1e-9;
299
300 let expected_labels: HashMap<&str, f64> = HashMap::from([
303 ("A", 27.75),
304 ("X", 19.071428571428573),
305 ("X2", 17.5),
306 ("Y", 11.5),
307 ("Y3", 4.0),
308 ("B", 0.0),
309 ]);
310 let expected_freqs: HashMap<&str, f64> = HashMap::from([
312 ("A", 1.0 / 3.0),
313 ("X", 7.0 / 30.0),
314 ("X2", f64::INFINITY),
315 ("Y", 0.4),
316 ("Y3", f64::INFINITY),
317 ("B", 0.0),
318 ]);
319 let expected_a_set: Vec<&Link> = vec![
321 &all_links[9],
323 &all_links[8],
325 &all_links[7],
327 &all_links[6],
329 &all_links[4],
331 &all_links[3],
333 &all_links[1],
335 &all_links[0],
337 ];
338
339 assert_eq!(
340 ops.labels.len(),
341 expected_labels.len(),
342 "Incorrect number of labels"
343 );
344 assert_eq!(
345 ops.freqs.len(),
346 expected_freqs.len(),
347 "Incorrect number of frequencies"
348 );
349 assert_eq!(
350 ops.a_set.len(),
351 expected_a_set.len(),
352 "Incorrect number of links in attractive set"
353 );
354
355 for (k, v) in &ops.labels {
356 assert!(
357 expected_labels.contains_key(k.as_str()),
358 "Incorrect label key {} has met",
359 k
360 );
361 let want = expected_labels[k.as_str()];
362 assert!(
363 (v - want).abs() <= EPS,
364 "Incorrect label value for node {}: got {}, want {}",
365 k,
366 v,
367 want
368 );
369 }
370 for (k, v) in &ops.freqs {
371 assert!(
372 expected_freqs.contains_key(k.as_str()),
373 "Incorrect frequency key {} has met",
374 k
375 );
376 let want = expected_freqs[k.as_str()];
377 if want.is_infinite() {
378 assert!(
379 v.is_infinite() && *v > 0.0,
380 "Frequency for node {} must be +Inf, got {}",
381 k,
382 v
383 );
384 } else {
385 assert!(
386 (v - want).abs() <= EPS,
387 "Incorrect frequency value for node {}: got {}, want {}",
388 k,
389 v,
390 want
391 );
392 }
393 }
394 for (i, v) in ops.a_set.iter().enumerate() {
395 println!("{:?} {:?}", v, expected_a_set[i]);
396 assert!(
397 std::ptr::eq(*v, expected_a_set[i]),
398 "Incorrect link in attractive set at index {}",
399 i
400 );
401 }
402 }
403
404 #[test]
405 fn test_no_wait_replaces_basket() {
406 let all_nodes: HashSet<String> = ["I", "W", "D"].iter().map(|s| s.to_string()).collect();
410 let all_links = vec![
411 Link::new("I", "D", "Bus", 4.0, 6.0),
413 Link::new("I", "W", "Walk", 3.0, 0.0),
415 Link::new("W", "D", "Walk", 2.0, 0.0),
417 ];
418 let ops = find_optimal_strategy(&all_links, &all_nodes, "D");
419
420 assert!((ops.labels["I"] - 5.0).abs() <= 1e-12);
421 assert!((ops.labels["W"] - 2.0).abs() <= 1e-12);
422 assert!(ops.freqs["I"].is_infinite());
423 assert!(ops.freqs["W"].is_infinite());
424
425 assert_eq!(
427 ops.a_set.len(),
428 2,
429 "basket of I must hold only the no-wait link"
430 );
431 for link in &ops.a_set {
432 assert_eq!(
433 link.headway, 0.0,
434 "only no-wait links expected in the attractive set"
435 );
436 }
437 }
438}