graco 0.1.3

Generalized Rust Ant Colony Optimization
Documentation
use petgraph::dot::Dot;
use petgraph::prelude::*;
use petgraph::EdgeType;

use std::collections::HashMap;
use std::fmt::Display;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;
/// Loads the provided text file as an undirected graph
///
/// Format specification: white space delimited! Node names are strings, weights must be floats
/// node_1  node_2  weight
pub fn load_graph(
    input_filename: PathBuf,
    init_pheromone: f64,
) -> (
    Graph<String, f64, Undirected>,
    Graph<String, f64, Undirected>,
) {
    let input_buf = File::open(input_filename.clone());
    assert!(
        input_buf.is_ok(),
        "could not open file {:?}",
        input_filename
    );

    let buffered = BufReader::new(input_buf.unwrap());

    // The visibility undirected graph itself (eta).
    let mut viz_graph = Graph::new_undirected();
    // The pheromone undirected graph itself (tau) -- easier to build both at the same time.
    let mut pheromone_graph = Graph::new_undirected();
    // NOTE: We expect graphs to work deterministically and therefore onnly use the index of one.

    // Set of all nodes to avoid adding them if they already exist.
    let mut all_nodes = HashMap::<String, NodeIndex>::new();

    for (lno, line_opt) in buffered.lines().enumerate() {
        let line = line_opt.unwrap();
        let vals: Vec<&str> = line.split_ascii_whitespace().collect();
        if vals.is_empty() {
            // Line is empty, ignoring.
            println!("[warn] ignoring empty line {}", lno);
        }
        assert_eq!(
            vals.len(),
            3,
            "[error] line {} has {} items, expected 3!",
            lno,
            vals.len()
        );

        // Add the nodes if they aren't in the graph yet.
        let idx1 = match all_nodes.get(&vals[0].to_string()) {
            Some(e) => *e,
            None => {
                let as_string = vals[0].to_string();
                let idx = viz_graph.add_node(as_string.clone());
                pheromone_graph.add_node(as_string.clone());
                all_nodes.insert(as_string, idx);
                idx
            }
        };

        let idx2 = match all_nodes.get(&vals[1].to_string()) {
            Some(e) => *e,
            None => {
                let as_string = vals[1].to_string();
                let idx = viz_graph.add_node(as_string.clone());
                pheromone_graph.add_node(as_string.clone());
                all_nodes.insert(as_string, idx);
                idx
            }
        };

        let weight: f64 = vals[2].to_string().parse().unwrap_or_else(|_| {
            panic!(
                "could not parse edge weight `{}` as float (line {})",
                vals[2], lno
            )
        });

        viz_graph.add_edge(idx1, idx2, weight);
        pheromone_graph.add_edge(idx1, idx2, init_pheromone);
    }

    info!("Loaded graph from `{:?}`", input_filename);

    (viz_graph, pheromone_graph)
}

/// Exports the graph as a .dot file
pub fn export_dot<N: Display, E: Display, Ty: EdgeType>(
    og: &Graph<N, E, Ty>,
    output_filename: &str,
) {
    debug!("Exporting to {}", output_filename);
    let ofile = File::create(output_filename);
    assert!(ofile.is_ok(), "unable to create file {}", output_filename);

    ofile
        .unwrap()
        .write_all(format!("{}", Dot::new(&og)).as_bytes())
        .expect("unable to write");
}