use anyhow::anyhow;
use std::fs::OpenOptions;
use std::io::BufReader;
use csv::ReaderBuilder;
use indexmap::IndexMap;
use petgraph::graph::{Graph, NodeIndex};
use petgraph::stable_graph::DefaultIx;
use crate::embed::gkernel::idmap::*;
use crate::embed::gkernel::pgraph::*;
pub struct EuNode {
num: u32,
labels: Nweight<u8>,
}
impl EuNode {
pub fn new(num: u32, labels: Nweight<u8>) -> Self {
EuNode { num, labels }
}
#[allow(unused)]
pub fn get_num(&self) -> u32 {
self.num
}
}
impl HasNweight<u8> for EuNode {
fn get_nweight(&self) -> &Nweight<u8> {
&self.labels
} }
#[derive(Default)]
pub struct EuEdge {
labels: Eweight<u8>,
}
impl HasEweight<u8> for EuEdge {
fn get_eweight(&self) -> &Eweight<u8> {
&self.labels
}
}
#[allow(unused)]
pub fn read_maileu_data(
dir: String,
) -> anyhow::Result<(
Graph<EuNode, EuEdge, petgraph::Directed, DefaultIx>,
IdMap<u8, u8>,
)> {
let delim = b' ';
let nb_fields = 2;
let filepath = std::path::Path::new(&dir).join("email-Eu-core-department-labels.txt");
let fileres = OpenOptions::new().read(true).open(&filepath);
if fileres.is_err() {
log::error!(
"read_maileu_data : reload could not open file {:?}",
filepath.as_os_str()
);
println!(
"directed_from_csv could not open file {:?}",
filepath.as_os_str()
);
return Err(anyhow!(
"directed_from_csv could not open file {}",
filepath.display()
));
}
let file = fileres?;
let bufreader = BufReader::new(file);
let mut nb_record = 0;
let mut node: usize;
let mut label: u8;
let mut rdr = ReaderBuilder::new()
.delimiter(delim)
.flexible(false)
.has_headers(false)
.from_reader(bufreader);
let mut nodelabels = Vec::<u8>::with_capacity(1005);
for result in rdr.records() {
let record = result?;
if record.len() != nb_fields {
println!(
"non constant number of fields at record {} first record has {}",
nb_record + 1,
nb_fields
);
return Err(anyhow!(
"non constant number of fields at record {} first record has {}",
nb_record + 1,
nb_fields
));
}
let field = record.get(0).unwrap();
if let Ok(idx) = field.parse::<usize>() {
node = idx;
assert!(node <= 1005);
} else {
return Err(anyhow!(
"error decoding field 1 of record {}",
nb_record + 1
));
}
let field = record.get(1).unwrap();
if let Ok(lbl) = field.parse::<u8>() {
label = lbl;
} else {
return Err(anyhow!(
"error decoding field 2 of record {}",
nb_record + 1
));
}
nodelabels.push(label);
nb_record += 1;
} let mut relabel = IndexMap::<u8, u8>::new();
for i in &nodelabels {
if *i == 0 {
relabel.insert(*i, 42u8);
} else {
relabel.insert(*i, *i);
}
}
let filepath = std::path::Path::new(&dir).join("email-Eu-core.txt");
let fileres = OpenOptions::new().read(true).open(&filepath);
if fileres.is_err() {
log::error!(
"ProcessingState reload_json : reload could not open file {:?}",
filepath.as_os_str()
);
println!(
"directed_from_csv could not open file {:?}",
filepath.as_os_str()
);
return Err(anyhow!(
"directed_from_csv could not open file {}",
filepath.display()
));
}
let file = fileres?;
let bufreader = BufReader::new(file);
nb_record = 0;
let mut nb_self_loops = 0;
let mut nb_double_dir = 0; let mut node1: u32;
let mut node2: u32;
let mut gnode1: Option<NodeIndex>;
let mut gnode2: Option<NodeIndex>;
let mut graph = Graph::<EuNode, EuEdge, petgraph::Directed>::new();
let mut nodeset = IndexMap::<u32, NodeIndex>::new();
let mut rdr = ReaderBuilder::new()
.delimiter(delim)
.flexible(false)
.has_headers(false)
.from_reader(bufreader);
for result in rdr.records() {
let record = result?;
if record.len() != nb_fields {
println!(
"non constant number of fields at record {} first record has {}",
nb_record + 1,
nb_fields
);
return Err(anyhow!(
"non constant number of fields at record {} first record has {}",
nb_record + 1,
nb_fields
));
}
let field = record.get(0).unwrap();
if let Ok(idx) = field.parse::<u32>() {
node1 = idx;
if !nodeset.contains_key(&node1) {
let label = nodelabels[node1 as usize];
let relabelled = relabel.get(&label).unwrap();
if log::log_enabled!(log::Level::Debug) && label == 0 {
log::info!("found label 0 at node {}", node1);
assert_eq!(*relabelled, 42u8);
}
let labels = Nweight::<u8>::new(Vec::from([*relabelled]));
gnode1 = Some(graph.add_node(EuNode::new(node1, labels)));
nodeset.insert(node1, gnode1.unwrap());
} else {
gnode1 = Some(*nodeset.get(&node1).unwrap());
}
} else {
return Err(anyhow!(
"error decoding field 1 of record {}",
nb_record + 1
));
}
let field = record.get(1).unwrap();
if let Ok(idx) = field.parse::<u32>() {
node2 = idx;
if !nodeset.contains_key(&node2) {
let label = nodelabels[node2 as usize];
let relabelled = relabel.get(&label).unwrap();
let labels = Nweight::<u8>::new(Vec::from([*relabelled]));
gnode2 = Some(graph.add_node(EuNode::new(node2, labels)));
nodeset.insert(node2, gnode2.unwrap());
} else {
gnode2 = Some(*nodeset.get(&node2).unwrap());
}
} else {
return Err(anyhow!(
"error decoding field 2 of record {}",
nb_record + 1
));
}
if graph.contains_edge(gnode2.unwrap(), gnode1.unwrap()) {
nb_double_dir += 1;
}
graph.update_edge(gnode1.unwrap(), gnode2.unwrap(), EuEdge::default());
if gnode1.unwrap() == gnode2.unwrap() {
nb_self_loops += 1;
}
log::debug!(
" index1 {} , index2 {}, gnode1 {:?} gnode2 {:?} edge count {}",
node1,
node2,
gnode1.unwrap(),
gnode2.unwrap(),
graph.edge_count()
);
nb_record += 1;
} log::info!(
"nb self loops : {}, nb bidirectional edges {}",
nb_self_loops,
nb_double_dir
);
log::info!("nb nodes = {}", graph.raw_nodes().len());
log::info!("nb edges = {}", graph.raw_edges().len());
let idmap = IdMap::<u8, u8>::new(nodeset, relabel);
Ok((graph, idmap))
}
#[cfg(test)]
mod tests {
const MAILEU_DIR: &str = "/home/jpboth/Data/Graphs/Email-Eu";
use super::*;
fn log_init_test() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[test]
fn test_load_maileu() {
log_init_test();
let res_graph = read_maileu_data(String::from(MAILEU_DIR));
assert!(res_graph.is_ok());
let (graph, _nodeset) = res_graph.unwrap();
assert_eq!(graph.raw_nodes().len(), 1005);
assert_eq!(graph.raw_edges().len(), 25571);
let first_node_weight = graph.node_weight(NodeIndex::new(0)).unwrap();
let first_node_nweight = first_node_weight.get_nweight();
let first_node_labels = first_node_nweight.get_labels();
assert_eq!(first_node_labels.len(), 1);
assert_eq!(first_node_labels[0], 1); let first_node_weight = &graph.raw_nodes()[0].weight;
let first_node_nweight = first_node_weight.get_nweight();
let first_node_labels = first_node_nweight.get_labels();
assert_eq!(first_node_labels.len(), 1);
assert_eq!(first_node_labels[0], 1);
let node130_weight = graph.node_weight(NodeIndex::new(130)).unwrap();
let node130_nweight = node130_weight.get_nweight();
let node130_nweight_labels = node130_nweight.get_labels();
assert_eq!(node130_nweight_labels.len(), 1);
assert_eq!(node130_nweight_labels[0], 42u8);
} }