///|
pub fn k_shortest_paths(
graph : @graph.FactoryGraph,
source : String,
target : String,
k : Int,
flow_type? : @types.FlowType = @types.FlowType::MaterialFlow,
criteria? : CostCriteria = MinimumTransportDelay,
) -> Array[PathResult] {
let result : Array[PathResult] = []
let initial = dijkstra_shortest_path(
graph,
source,
target,
flow_type~,
criteria~,
)
match initial {
None => return result
Some(p0) => result.push(p0)
}
let candidates : Array[PathResult] = []
for iter = 1; iter < k; iter = iter + 1 {
let prev_path = result[iter - 1]
for i = 0; i < prev_path.nodes.length() - 1; i = i + 1 {
let spur_node = prev_path.nodes[i]
let root_path_nodes = []
let root_path_edges = []
for j = 0; j <= i; j = j + 1 {
root_path_nodes.push(prev_path.nodes[j])
if j < i {
root_path_edges.push(prev_path.edges[j])
}
}
// Clone graph to temporarily disable edges
let spur_graph = graph.clone()
// Remove edges that are part of previous paths that share root path
for p in result {
let mut match_root = true
for j = 0; j <= i; j = j + 1 {
if j >= p.nodes.length() || p.nodes[j] != root_path_nodes[j] {
match_root = false
break
}
}
if match_root && i < p.edges.length() {
let edge_to_disable = p.edges[i]
match spur_graph.get_edge(edge_to_disable.id()) {
Some(e) => e.set_active(false)
None => ()
}
}
}
// Hide root path nodes except spur node
for j = 0; j < i; j = j + 1 {
let n_hide = root_path_nodes[j]
let out_e = spur_graph.get_out_edges(n_hide)
for e in out_e {
e.set_active(false)
}
}
// Find spur path
let spur_path_opt = dijkstra_shortest_path(
spur_graph,
spur_node,
target,
flow_type~,
criteria~,
)
match spur_path_opt {
Some(spur_path) => {
let total_nodes = []
for n in root_path_nodes {
total_nodes.push(n)
}
for j = 1; j < spur_path.nodes.length(); j = j + 1 {
total_nodes.push(spur_path.nodes[j])
}
let total_edges = []
for e in root_path_edges {
total_edges.push(e)
}
for e in spur_path.edges {
total_edges.push(e)
}
let mut total_delay = 0.0
let mut min_bw = 1.0e9
let mut total_cost = 0.0
for e in total_edges {
total_delay = total_delay + e.transport_delay_sec()
if e.bandwidth() < min_bw {
min_bw = e.bandwidth()
}
let target_node = graph.get_node(e.target_id())
total_cost = total_cost +
compute_edge_weight(e, target_node, criteria)
}
let candidate_path : PathResult = {
nodes: total_nodes,
edges: total_edges,
total_cost,
total_delay_sec: total_delay,
bottleneck_bandwidth: min_bw,
}
// Check if candidate already in candidates
let mut exists = false
for c in candidates {
if c.nodes.length() == candidate_path.nodes.length() {
let mut same = true
for idx = 0; idx < c.nodes.length(); idx = idx + 1 {
if c.nodes[idx] != candidate_path.nodes[idx] {
same = false
break
}
}
if same {
exists = true
break
}
}
}
if !exists {
candidates.push(candidate_path)
}
}
None => ()
}
}
if candidates.length() == 0 {
break
}
// Sort candidate paths by total_cost
let mut best_idx = 0
let mut best_cost = 1.0e12
for c_idx = 0; c_idx < candidates.length(); c_idx = c_idx + 1 {
if candidates[c_idx].total_cost < best_cost {
best_cost = candidates[c_idx].total_cost
best_idx = c_idx
}
}
let chosen = candidates[best_idx]
let _ = candidates.remove(best_idx)
result.push(chosen)
}
result
}