///|
pub fn acyclic_run(g : Graph) -> Unit {
let acyclicer = g.graph().get_string_or("acyclicer", "")
let fas = if acyclicer == "greedy" {
greedy_fas(g, weight_fn=weight_fn(g))
} else {
dfs_fas(g)
}
for e in fas {
if g.edge_obj(e) is Some(label) {
g.remove_edge_obj(e)
let reversed = as_attrs_label(label)
reversed.set_bool("reversed", true)
if e.name is Some(name) {
reversed.set_string("forwardName", name)
}
g.set_edge(e.w, e.v, label=Value::VAttrs(reversed), name=unique_id("rev"))
}
}
}
///|
fn weight_fn(g : Graph) -> (EdgeObj) -> Double {
(e : EdgeObj) => {
match g.edge_obj(e) {
Some(Value::VAttrs(attrs)) => attrs.get_float_or("weight", 1.0)
Some(Value::VInt(v)) => v.to_double()
Some(Value::VFloat(v)) => v
_ => 1.0
}
}
}
///|
fn dfs_fas(g : Graph) -> Array[EdgeObj] {
let fas = []
let stack = Set::new()
let visited = Set::new()
fn dfs(
g : Graph,
v : String,
stack : Set[String],
visited : Set[String],
fas : Array[EdgeObj],
) -> Unit {
if visited.contains(v) {
return
}
visited.add(v)
stack.add(v)
for e in g.out_edges(v) {
if stack.contains(e.w) {
fas.push(e)
} else {
dfs(g, e.w, stack, visited, fas)
}
}
stack.remove(v)
}
for v in g.nodes() {
dfs(g, v, stack, visited, fas)
}
fas
}
///|
pub fn acyclic_undo(g : Graph) -> Unit {
let edges = g.edges()
for e in edges {
if g.edge_obj(e) is Some(Value::VAttrs(label)) {
if label.get_bool_or("reversed", false) {
g.remove_edge_obj(e)
let forward_name = label.get_string("forwardName")
label.remove("reversed")
label.remove("forwardName")
let name = forward_name
g.set_edge(e.w, e.v, label=Value::VAttrs(label), name?)
}
}
}
}
///|
fn as_attrs_label(value : Value) -> Attrs {
match value {
Value::VAttrs(attrs) => clone_attrs(attrs)
Value::VInt(v) => {
let attrs = empty_attrs()
attrs.set_float("weight", v.to_double())
attrs
}
Value::VFloat(v) => {
let attrs = empty_attrs()
attrs.set_float("weight", v)
attrs
}
Value::VBool(v) => {
let attrs = empty_attrs()
attrs.set_bool("value", v)
attrs
}
Value::VString(v) => {
let attrs = empty_attrs()
attrs.set_string("value", v)
attrs
}
Value::VNull => empty_attrs()
}
}