use crate::core::paths::dijkstra;
use crate::core::types::{BaseGraph, GraphConstructor, NodeId};
use ordered_float::OrderedFloat;
use std::cmp::Reverse;
use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
fn find_path<A, Ty>(
graph: &BaseGraph<A, OrderedFloat<f64>, Ty>,
source: NodeId,
target: NodeId,
blocked: &HashSet<NodeId>,
) -> Option<Vec<NodeId>>
where
Ty: crate::core::types::GraphConstructor<A, OrderedFloat<f64>>,
{
let n = graph.node_count();
let mut prev: Vec<Option<NodeId>> = vec![None; n];
let mut visited = vec![false; n];
let mut queue = VecDeque::new();
visited[source.index()] = true;
queue.push_back(source);
while let Some(u) = queue.pop_front() {
if u == target {
let mut path = Vec::new();
let mut cur = u;
path.push(cur);
while let Some(p) = prev[cur.index()] {
cur = p;
path.push(cur);
}
path.reverse();
return Some(path);
}
for v in graph.neighbors(u) {
if !visited[v.index()] && !blocked.contains(&v) {
visited[v.index()] = true;
prev[v.index()] = Some(u);
queue.push_back(v);
}
}
}
None
}
pub fn local_node_connectivity<A, Ty>(
graph: &BaseGraph<A, OrderedFloat<f64>, Ty>,
source: NodeId,
target: NodeId,
) -> usize
where
Ty: GraphConstructor<A, OrderedFloat<f64>>,
{
let mut connectivity = 0;
let mut blocked = HashSet::new();
while let Some(path) = find_path(graph, source, target, &blocked) {
for &node in path.iter().skip(1).take(path.len() - 2) {
blocked.insert(node);
}
connectivity += 1;
}
connectivity
}
pub fn maximum_independent_set<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> HashSet<NodeId>
where
Ty: GraphConstructor<A, f64>,
{
let mut mis = HashSet::new();
let mut nodes: Vec<NodeId> = graph.nodes().map(|(u, _)| u).collect();
let neighbor_cache: HashMap<NodeId, HashSet<NodeId>> = nodes
.iter()
.map(|&u| (u, graph.neighbors(u).collect()))
.collect();
nodes.sort_by_key(|&u| neighbor_cache.get(&u).unwrap().len());
let mut used = HashSet::new();
for u in nodes {
if !used.contains(&u) {
mis.insert(u);
if let Some(neighbors) = neighbor_cache.get(&u) {
for &v in neighbors {
used.insert(v);
}
}
}
}
mis
}
pub fn max_clique<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> HashSet<NodeId>
where
Ty: GraphConstructor<A, f64>,
{
let mut best = HashSet::new();
let neighbor_cache: HashMap<NodeId, HashSet<NodeId>> = graph
.nodes()
.map(|(u, _)| (u, graph.neighbors(u).collect()))
.collect();
for (node, _) in graph.nodes() {
let mut clique = HashSet::new();
clique.insert(node);
let mut neighbors: Vec<NodeId> =
neighbor_cache.get(&node).unwrap().iter().cloned().collect();
neighbors.sort_by_key(|u| std::cmp::Reverse(neighbor_cache.get(u).unwrap().len()));
for v in neighbors {
if clique
.iter()
.all(|&w| neighbor_cache.get(&w).unwrap().contains(&v))
{
clique.insert(v);
}
}
if clique.len() > best.len() {
best = clique;
}
}
best
}
pub fn clique_removal<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> Vec<HashSet<NodeId>>
where
Ty: GraphConstructor<A, f64>,
{
let mut cliques = Vec::new();
let mut remaining: HashSet<NodeId> = graph.nodes().map(|(u, _)| u).collect();
while !remaining.is_empty() {
let clique = max_clique(graph)
.into_iter()
.filter(|u| remaining.contains(u))
.collect::<HashSet<_>>();
if clique.is_empty() {
break;
}
for u in &clique {
remaining.remove(u);
}
cliques.push(clique);
}
cliques
}
pub fn large_clique_size<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> usize
where
Ty: GraphConstructor<A, f64>,
{
max_clique(graph).len()
}
pub fn average_clustering<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> f64
where
Ty: GraphConstructor<A, f64>,
{
let mut total = 0.0;
let mut count = 0;
let neighbor_cache: HashMap<NodeId, HashSet<NodeId>> = graph
.nodes()
.map(|(u, _)| (u, graph.neighbors(u).collect()))
.collect();
for (u, _) in graph.nodes() {
let neighbors = neighbor_cache.get(&u).unwrap();
let k = neighbors.len();
if k < 2 {
continue;
}
let mut links = 0;
let neighbor_vec: Vec<&NodeId> = neighbors.iter().collect();
for i in 0..neighbor_vec.len() {
for j in (i + 1)..neighbor_vec.len() {
if neighbor_cache
.get(neighbor_vec[i])
.unwrap()
.contains(neighbor_vec[j])
{
links += 1;
}
}
}
let possible = k * (k - 1) / 2;
total += links as f64 / possible as f64;
count += 1;
}
if count > 0 {
total / count as f64
} else {
0.0
}
}
pub fn densest_subgraph<A, Ty>(
graph: &BaseGraph<A, f64, Ty>,
_iterations: Option<usize>,
) -> HashSet<NodeId>
where
Ty: GraphConstructor<A, f64>,
{
let mut best_density = 0.0;
let mut best_set = HashSet::new();
let mut current_nodes: HashSet<NodeId> = graph.nodes().map(|(u, _)| u).collect();
let mut degrees: HashMap<NodeId, usize> = current_nodes
.iter()
.map(|&u| {
(
u,
graph
.neighbors(u)
.filter(|v| current_nodes.contains(v))
.count(),
)
})
.collect();
let mut heap: BinaryHeap<Reverse<(ordered_float::OrderedFloat<f64>, NodeId)>> =
BinaryHeap::new();
for (&u, °) in °rees {
heap.push(Reverse((OrderedFloat(deg as f64), u)));
}
while !current_nodes.is_empty() {
let total_edges: usize = degrees.values().sum::<usize>() / 2;
let density = total_edges as f64 / current_nodes.len() as f64;
if density > best_density {
best_density = density;
best_set = current_nodes.clone();
}
if let Some(Reverse((_, u))) = heap.pop() {
if !current_nodes.contains(&u) {
continue;
}
current_nodes.remove(&u);
let neighbors = graph.neighbors(u).collect::<HashSet<_>>();
for v in neighbors {
if current_nodes.contains(&v) {
if let Some(d) = degrees.get_mut(&v) {
*d = d.saturating_sub(1);
heap.push(Reverse((OrderedFloat(*d as f64), v)));
}
}
}
} else {
break;
}
}
best_set
}
pub fn diameter<A, Ty>(graph: &BaseGraph<A, OrderedFloat<f64>, Ty>) -> f64
where
Ty: GraphConstructor<A, OrderedFloat<f64>>,
{
if let Some((start, _)) = graph.nodes().next() {
let distances = dijkstra(graph, start);
distances
.into_iter()
.filter_map(|d| d.map(|od| od.0))
.fold(0.0, f64::max)
} else {
0.0
}
}
pub fn min_weighted_vertex_cover<A, Ty>(
graph: &crate::core::types::BaseGraph<A, f64, Ty>,
_weight: Option<&dyn Fn(NodeId) -> f64>,
) -> std::collections::HashSet<NodeId>
where
Ty: crate::core::types::GraphConstructor<A, f64>,
{
let mut cover = HashSet::new();
let mut uncovered: HashSet<(NodeId, NodeId)> = graph.edges().map(|(u, v, _)| (u, v)).collect();
while !uncovered.is_empty() {
let best = graph
.nodes()
.map(|(u, _)| u)
.filter(|u| !cover.contains(u))
.max_by_key(|&u| {
let count = graph
.neighbors(u)
.filter(|w| uncovered.contains(&(u, *w)) || uncovered.contains(&(*w, u)))
.count();
count
});
if let Some(best) = best {
cover.insert(best);
uncovered.retain(|&(u, v)| u != best && v != best);
} else {
break;
}
}
cover
}
pub fn min_maximal_matching<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> HashSet<(NodeId, NodeId)>
where
Ty: GraphConstructor<A, f64>,
{
let mut matching = HashSet::new();
let mut matched = HashSet::new();
for (u, v, _) in graph.edges() {
if !matched.contains(&u) && !matched.contains(&v) {
matching.insert((u, v));
matched.insert(u);
matched.insert(v);
}
}
matching
}
pub fn ramsey_r2<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> (HashSet<NodeId>, HashSet<NodeId>)
where
Ty: GraphConstructor<A, f64>,
{
let clique = max_clique(graph);
let independent_set = maximum_independent_set(graph);
(clique, independent_set)
}
pub fn christofides<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> (Vec<NodeId>, f64)
where
A: Clone,
Ty: GraphConstructor<A, f64> + GraphConstructor<A, OrderedFloat<f64>>,
{
greedy_tsp(
&graph.convert::<OrderedFloat<f64>>(),
graph.nodes().next().unwrap().0,
)
}
pub fn traveling_salesman_problem<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> (Vec<NodeId>, f64)
where
A: Clone,
Ty: GraphConstructor<A, f64> + GraphConstructor<A, OrderedFloat<f64>>,
{
greedy_tsp(
&graph.convert::<OrderedFloat<f64>>(),
graph.nodes().next().unwrap().0,
)
}
pub fn greedy_tsp<A, Ty>(
graph: &BaseGraph<A, OrderedFloat<f64>, Ty>,
source: NodeId,
) -> (Vec<NodeId>, f64)
where
Ty: GraphConstructor<A, OrderedFloat<f64>>,
{
let mut unvisited: HashSet<NodeId> = graph.nodes().map(|(u, _)| u).collect();
let mut tour = Vec::new();
let mut cost = 0.0;
let mut current = source;
tour.push(current);
unvisited.remove(¤t);
while !unvisited.is_empty() {
let distances = dijkstra(graph, current);
let (next_node, next_cost) = unvisited
.iter()
.filter_map(|v| distances[v.index()].map(|d| (*v, d.0)))
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
.unwrap_or((current, 0.0));
tour.push(next_node);
cost += next_cost;
current = next_node;
unvisited.remove(¤t);
}
let distances = dijkstra(graph, current);
if let Some(d) = distances[source.index()] {
cost += d.0;
tour.push(source);
}
(tour, cost)
}
pub fn simulated_annealing_tsp<A, Ty>(
graph: &BaseGraph<A, f64, Ty>,
init_cycle: Vec<NodeId>,
) -> (Vec<NodeId>, f64)
where
A: Clone,
Ty: GraphConstructor<A, f64> + GraphConstructor<A, OrderedFloat<f64>>,
{
let cost = tour_cost(&graph.convert::<OrderedFloat<f64>>(), &init_cycle);
(init_cycle, cost)
}
pub fn threshold_accepting_tsp<A, Ty>(
graph: &BaseGraph<A, f64, Ty>,
init_cycle: Vec<NodeId>,
) -> (Vec<NodeId>, f64)
where
A: Clone,
Ty: GraphConstructor<A, f64> + GraphConstructor<A, OrderedFloat<f64>>,
{
let cost = tour_cost(&graph.convert::<OrderedFloat<f64>>(), &init_cycle);
(init_cycle, cost)
}
pub fn asadpour_atsp<A, Ty>(_graph: &BaseGraph<A, f64, Ty>) -> (Vec<NodeId>, f64)
where
Ty: GraphConstructor<A, f64>,
{
unimplemented!("Asadpour ATSP algorithm is not implemented yet")
}
fn tour_cost<A, Ty>(graph: &BaseGraph<A, OrderedFloat<f64>, Ty>, tour: &[NodeId]) -> f64
where
Ty: GraphConstructor<A, OrderedFloat<f64>>,
{
let mut cost = 0.0;
for i in 0..tour.len() - 1 {
let distances = dijkstra(graph, tour[i]);
if let Some(d) = distances[tour[i + 1].index()] {
cost += d.0;
}
}
cost
}
pub fn treewidth_min_degree<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> (usize, Vec<NodeId>)
where
Ty: GraphConstructor<A, f64>,
{
let mut order = Vec::new();
let mut remaining: HashSet<NodeId> = graph.nodes().map(|(u, _)| u).collect();
let mut neighbor_cache: HashMap<NodeId, HashSet<NodeId>> = graph
.nodes()
.map(|(u, _)| (u, graph.neighbors(u).collect()))
.collect();
let mut treewidth = 0;
let mut heap: BinaryHeap<Reverse<(usize, NodeId)>> = BinaryHeap::new();
for (&u, neighbors) in &neighbor_cache {
heap.push(Reverse((neighbors.len(), u)));
}
while !remaining.is_empty() {
let Reverse((deg, u)) = heap.pop().unwrap();
if !remaining.contains(&u) {
continue;
}
if deg > treewidth {
treewidth = deg;
}
order.push(u);
remaining.remove(&u);
let neighbors = neighbor_cache.get(&u).unwrap().clone();
for &v in &neighbors {
if remaining.contains(&v) {
if let Some(entry) = neighbor_cache.get_mut(&v) {
entry.remove(&u);
heap.push(Reverse((entry.len(), v)));
}
}
}
}
(treewidth, order)
}
pub fn treewidth_min_fill_in<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> (usize, Vec<NodeId>)
where
Ty: GraphConstructor<A, f64>,
{
let mut order = Vec::new();
let mut remaining: HashSet<NodeId> = graph.nodes().map(|(u, _)| u).collect();
let mut treewidth = 0;
while !remaining.is_empty() {
let &u = remaining
.iter()
.min_by_key(|&&u| {
let neighbors: Vec<NodeId> = graph
.neighbors(u)
.filter(|v| remaining.contains(v))
.collect();
let mut fill_in = 0;
for i in 0..neighbors.len() {
for j in i + 1..neighbors.len() {
if !graph.neighbors(neighbors[i]).any(|x| x == neighbors[j]) {
fill_in += 1;
}
}
}
fill_in
})
.unwrap();
let deg = graph.neighbors(u).filter(|v| remaining.contains(v)).count();
if deg > treewidth {
treewidth = deg;
}
order.push(u);
remaining.remove(&u);
}
(treewidth, order)
}