1use crate::ant::Ant;
2use crate::io::{export_dot, load_graph};
3use crate::petgraph::graph::{Graph, NodeIndex};
4use crate::petgraph::prelude::*;
5use crate::petgraph::EdgeType;
6use crate::Objective;
7use std::cmp::PartialEq;
8use std::f64::EPSILON;
9use std::fmt::{Debug, Display};
10use std::marker::Send;
11use std::path::PathBuf;
12use std::sync::mpsc::channel;
13use std::thread::sleep;
14use std::time::Duration;
15use threadpool::ThreadPool;
16
17#[derive(Clone, Copy, Debug)]
18pub struct ColonyOpts<N> {
19 pub verbose: bool,
21 pub initphe: f64,
23 pub evaporation: f64,
25 pub iterations: u32,
27 pub threshold: f64,
29 pub density: f64,
31 pub alpha: i32,
33 pub beta: i32,
35 pub seed: Option<u64>,
37 pub visit_node: Option<N>,
39 pub visit_count: Option<usize>,
41 pub reach_dead_end: bool,
43 pub start_node: Option<N>,
45 pub wander: f64,
47 pub ants: usize,
49 pub cpus: usize,
51}
52
53impl<N: 'static + Clone + Display + Debug + PartialEq + Send> Default for ColonyOpts<N> {
54 fn default() -> Self {
56 ColonyOpts {
57 verbose: false,
58 initphe: 1.0,
59 evaporation: 0.95,
60 iterations: 100,
61 threshold: 0.9,
62 density: 3.0,
63 alpha: 1,
64 beta: 2,
65 wander: 0.15,
66 cpus: 8,
67 ants: 50,
68 seed: None,
69 start_node: None,
70 visit_node: None,
71 visit_count: None,
72 reach_dead_end: false,
73 }
74 }
75}
76
77#[derive(Debug, Clone)]
78pub struct Colony<N, Ty: Clone + EdgeType> {
79 pub viz_graph: Graph<N, f64, Ty>,
80 pub pheromone_graph: Graph<N, f64, Ty>,
81 pub opt: ColonyOpts<N>,
82 shortest_path: Vec<usize>,
83 iteration: u32,
84 init_seed: u64,
85 obj: Objective,
86 start_idx: usize,
87}
88
89impl Colony<String, Undirected> {
90 pub fn with_input_path(opt: ColonyOpts<String>, input: PathBuf) -> Self {
91 let (viz_graph, pheromone_graph) = load_graph(input, opt.initphe);
92 Self::from_options(opt, viz_graph, pheromone_graph)
93 }
94}
95
96impl<
97 N: 'static + Clone + Display + Debug + PartialEq + Send,
98 Ty: 'static + Clone + EdgeType + Send,
99 > Colony<N, Ty>
100{
101 pub fn from_options(
102 opt: ColonyOpts<N>,
103 viz_graph: Graph<N, f64, Ty>,
104 pheromone_graph: Graph<N, f64, Ty>,
105 ) -> Self {
106 let seed = match opt.seed {
107 Some(seed) => seed,
108 None => {
109 rand::random::<u64>()
111 }
112 };
113
114 info!("Seed: {}", seed);
115
116 let visit_node = opt.visit_node.clone();
118 let obj = if let Some(name) = visit_node {
119 let mut idx = 0;
121 let mut found = false;
122 for node in viz_graph.raw_nodes() {
123 if node.weight == name {
124 found = true;
125 break;
126 }
127 idx += 1;
128 }
129 if !found {
130 panic!("could not find the requested objective node");
131 }
132 Objective::ReachNodeIdx(idx)
133 } else if let Some(count) = opt.visit_count {
134 Objective::VisitNNodes(count)
135 } else if opt.reach_dead_end {
136 Objective::ReachDeadEnd
137 } else {
138 Objective::VisitAllNodes
139 };
140
141 let start_node = opt.start_node.clone();
142 let start_idx = if let Some(name) = start_node {
143 let mut idx = 0;
145 let mut found = false;
146 for node in viz_graph.raw_nodes() {
147 if node.weight == name {
148 found = true;
149 break;
150 }
151 idx += 1;
152 }
153 if !found {
154 panic!("could not find the requested starting node");
155 }
156 idx
157 } else {
158 0
159 };
160
161 Colony {
162 viz_graph,
163 pheromone_graph,
164 opt,
165 shortest_path: Vec::new(),
166 iteration: 0,
167 init_seed: seed,
168 obj: obj,
169 start_idx,
170 }
171 }
172
173 pub fn unnormalized_probability(&self, from: usize, to: usize) -> f64 {
175 let node1 = NodeIndex::new(from);
176 let node2 = NodeIndex::new(to);
177 match self.pheromone_graph.find_edge(node1, node2) {
178 None => 0.0,
179 Some(edge) => {
180 let tau = self.pheromone_graph[edge].powi(self.opt.alpha);
182 let viz = self.viz_graph[edge].powi(self.opt.beta);
184 tau * viz
185 }
186 }
187 }
188
189 pub fn update_pheromones(&mut self, new_path: Vec<usize>) {
191 if self.iteration == 0 && self.opt.verbose {
193 self.export();
194 export_dot(&self.viz_graph, "dotgraphs/visibility.dot");
195 }
196
197 self.shortest_path = new_path.clone();
198 for edge_no in 0..self.pheromone_graph.edge_count() {
200 self.pheromone_graph[EdgeIndex::new(edge_no)] *= 1.0 - self.opt.evaporation;
201 if new_path.contains(&edge_no) {
202 self.pheromone_graph[EdgeIndex::new(edge_no)] +=
203 self.opt.density / (new_path.len() as f64);
204 }
205 }
206 self.iteration += 1;
207
208 if self.opt.verbose {
209 self.export();
210 }
211 }
212
213 pub fn explore(&mut self) -> (bool, f64, Vec<N>) {
215 let pool = ThreadPool::new(self.opt.cpus);
217
218 let min_equal_paths = ((self.opt.ants as f64) * self.opt.threshold) as u32;
219
220 while self.iteration < self.opt.iterations {
221 let (tx, rx) = channel();
222
223 for ant_no in 0..self.opt.ants {
224 let tx = tx.clone();
225 let colony = self.clone();
226 let current_seed = self.init_seed;
227 let objective = self.obj.clone();
228 let start_idx = self.start_idx;
229 pool.execute(move || {
230 sleep(Duration::from_millis(5));
232
233 let mut ant = Ant::from_node(ant_no, start_idx, current_seed, objective);
235 while ant.move_ant(&colony) {}
236 ant.remove_cycles(&colony);
237 tx.send(ant)
238 .expect("channel will be there waiting for the pool");
239 });
240
241 self.init_seed += 1;
242 }
243
244 let mut shortest_path: Vec<usize> = Vec::new();
245 let mut lowest_weight = 0.0;
246 let mut equal_paths = 1;
247
248 rx.iter().take(self.opt.ants).for_each(|ant| {
250 if ant.number == 0 || ant.edge_weight < lowest_weight {
251 lowest_weight = ant.edge_weight;
252 shortest_path = ant.visited_edges.clone();
253 equal_paths = 1; } else if (lowest_weight - ant.edge_weight).abs() < EPSILON {
255 equal_paths += 1;
257 }
258 });
259
260 if equal_paths >= min_equal_paths && self.iteration > 0 {
261 self.shortest_path = shortest_path;
262 info!(
263 "Stagnation reached: {:0.2}% of ants have the same path",
264 self.opt.threshold * 100.0
265 );
266 return self.end(false);
267 }
268
269 self.update_pheromones(shortest_path);
270 info!("Completed iteration {}", self.iteration);
271 }
272 self.end(true)
273 }
274
275 fn end(&self, max_iter: bool) -> (bool, f64, Vec<N>) {
276 self.export();
277 if max_iter {
278 warn!("Max iterations reached")
279 }
280 let mut weight = 0.0;
282 let mut path = Vec::new();
283 for edge_idx in self.shortest_path.clone() {
284 let edge = EdgeIndex::new(edge_idx);
285 let (start_nidx, end_nidx) = self.viz_graph.edge_endpoints(edge).unwrap();
286 let start_node = self.viz_graph[start_nidx].clone();
287 let end_node = self.viz_graph[end_nidx].clone();
288 if path.is_empty() {
289 if start_nidx == NodeIndex::new(self.start_idx) {
290 path.push(start_node);
291 path.push(end_node);
292 } else {
293 path.push(end_node);
295 path.push(start_node);
296 }
297 } else if path.last().unwrap() == &start_node {
298 path.push(end_node);
299 } else {
300 path.push(start_node);
301 }
302
303 weight += self.viz_graph[edge];
304 }
305
306 println!(
307 "iterations: {}\nweight: {}\npath: {:?}",
308 self.iteration + 1,
309 weight,
310 path,
311 );
312
313 (!max_iter, weight, path)
314 }
315
316 fn export(&self) {
317 export_dot(
318 &self.pheromone_graph,
319 &format!("dotgraphs/pheromones_it_{}.dot", self.iteration),
320 );
321 }
322}
323
324#[test]
325fn test_colony_complete() {
326 use std::path::PathBuf;
327 use std::str::FromStr;
328 let colony_opt = ColonyOpts {
329 verbose: false,
330 initphe: 1.0,
331 evaporation: 0.95,
332 iterations: 100,
333 threshold: 1.0,
334 density: 1.0,
335 alpha: 1,
336 beta: 2,
337 seed: Some(4580147489518812583),
338 visit_node: None,
339 visit_count: None,
340 reach_dead_end: false,
341 start_node: None,
342 wander: 0.15,
343 ants: 10,
344 cpus: 1,
345 };
346
347 let mut colony =
348 Colony::with_input_path(colony_opt, PathBuf::from_str("complete_graph.txt").unwrap());
349
350 let (success, weight, path) = colony.explore();
351
352 assert!(success);
353 assert!(weight - 8.1 < EPSILON);
354 assert_eq!(path, vec!["S", "A", "D", "B", "C"]);
355}