use crate::graph::{Graph, DirectedGraph};
use crate::centrality::{degree_centrality, betweenness_centrality, pagerank};
use crate::community::{louvain, label_propagation, get_communities, modularity};
use crate::small_world::{clustering_coefficient, average_path_length};
use crate::assortativity::degree_assortativity;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Agent {
pub id: usize,
pub name: String,
pub attributes: HashMap<String, String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AgentNetwork {
pub graph: Graph,
pub agents: HashMap<usize, Agent>,
pub interaction_weights: HashMap<(usize, usize), f64>,
}
impl AgentNetwork {
pub fn new() -> Self {
AgentNetwork {
graph: Graph::new(),
agents: HashMap::new(),
interaction_weights: HashMap::new(),
}
}
pub fn add_agent(&mut self, agent: Agent) {
self.graph.add_node(agent.id);
self.agents.insert(agent.id, agent);
}
pub fn add_communication_link(&mut self, from: usize, to: usize, weight: f64) {
self.graph.add_edge(from, to);
self.interaction_weights.insert((from.min(to), from.max(to)), weight);
}
pub fn record_interaction(&mut self, from: usize, to: usize) {
let key = (from.min(to), from.max(to));
*self.interaction_weights.entry(key).or_insert(0.0) += 1.0;
self.graph.add_edge(from, to);
}
pub fn agent_count(&self) -> usize {
self.agents.len()
}
pub fn get_agent(&self, id: usize) -> Option<&Agent> {
self.agents.get(&id)
}
pub fn most_influential(&self, top_k: usize) -> Vec<(usize, f64)> {
let pr = pagerank(&self.graph, 0.85, 100, 1e-6);
let mut ranked: Vec<(usize, f64)> = pr.into_iter().collect();
ranked.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
ranked.into_iter().take(top_k).collect()
}
pub fn detect_communities(&self) -> HashMap<usize, Vec<usize>> {
let membership = louvain(&self.graph);
let communities = get_communities(&membership);
let mut result = HashMap::new();
for (i, comm) in communities.into_iter().enumerate() {
result.insert(i, comm);
}
result
}
pub fn bridge_agents(&self, top_k: usize) -> Vec<(usize, f64)> {
let bc = betweenness_centrality(&self.graph);
let mut ranked: Vec<(usize, f64)> = bc.into_iter().collect();
ranked.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
ranked.into_iter().take(top_k).collect()
}
pub fn density(&self) -> f64 {
let n = self.graph.node_count();
if n <= 1 { return 0.0; }
let max_edges = n * (n - 1) / 2;
self.graph.edge_count() as f64 / max_edges as f64
}
pub fn summary(&self) -> AgentNetworkSummary {
let pr = pagerank(&self.graph, 0.85, 100, 1e-6);
let membership = louvain(&self.graph);
let n_communities = {
let mut comms: Vec<usize> = membership.values().copied().collect();
comms.sort();
comms.dedup();
comms.len()
};
AgentNetworkSummary {
num_agents: self.agents.len(),
num_links: self.graph.edge_count(),
density: self.density(),
clustering_coefficient: clustering_coefficient(&self.graph),
average_path_length: average_path_length(&self.graph),
assortativity: degree_assortativity(&self.graph),
num_communities: n_communities,
modularity: modularity(&self.graph, &membership),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentNetworkSummary {
pub num_agents: usize,
pub num_links: usize,
pub density: f64,
pub clustering_coefficient: f64,
pub average_path_length: f64,
pub assortativity: f64,
pub num_communities: usize,
pub modularity: f64,
}
pub struct AgentNetworkAnalyzer;
impl AgentNetworkAnalyzer {
pub fn compare(net1: &AgentNetwork, net2: &AgentNetwork) -> NetworkComparison {
let s1 = net1.summary();
let s2 = net2.summary();
NetworkComparison {
density_diff: s1.density - s2.density,
clustering_diff: s1.clustering_coefficient - s2.clustering_coefficient,
path_length_diff: s1.average_path_length - s2.average_path_length,
community_diff: s1.num_communities as f64 - s2.num_communities as f64,
}
}
pub fn from_communication_log(log: &[(usize, usize, f64)], agents: Vec<Agent>) -> AgentNetwork {
let mut net = AgentNetwork::new();
for agent in agents {
net.add_agent(agent);
}
for &(from, to, weight) in log {
net.add_communication_link(from, to, weight);
}
net
}
pub fn random_network(num_agents: usize, connection_prob: f64, agent_names: &[String]) -> AgentNetwork {
let mut net = AgentNetwork::new();
for i in 0..num_agents {
let name = agent_names.get(i).cloned().unwrap_or_else(|| format!("Agent-{}", i));
net.add_agent(Agent {
id: i,
name,
attributes: HashMap::new(),
});
}
let g = crate::models::erdos_renyi(num_agents, connection_prob);
net.graph = g;
net
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkComparison {
pub density_diff: f64,
pub clustering_diff: f64,
pub path_length_diff: f64,
pub community_diff: f64,
}