lib-laddertypes/src/steiner_tree.rs
2025-02-25 22:54:57 +01:00

250 lines
7.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use {
crate::{
morphism::{
Morphism, MorphismBase, MorphismType
}, MorphismInstance, TypeID, TypeTerm
}, std::collections::HashMap
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\
#[derive(Clone)]
pub struct SteinerTree {
weight: u64,
goals: Vec< TypeTerm >,
pub edges: Vec< MorphismType >,
}
impl SteinerTree {
pub fn into_edges(self) -> Vec< MorphismType > {
self.edges
}
fn add_edge(&mut self, ty: MorphismType) {
self.weight += 1;
let ty = ty.normalize();
// check if by adding this new edge, we reach a goal
let mut new_goals = Vec::new();
let mut added = false;
for g in self.goals.clone() {
if let Ok(σ) = crate::unify(&ty.dst_type, &g) {
if !added {
self.edges.push(ty.clone());
// goal reached.
for e in self.edges.iter_mut() {
e.src_type = e.src_type.apply_substitution(&|x| σ.get(x).cloned()).clone();
e.dst_type = e.dst_type.apply_substitution(&|x| σ.get(x).cloned()).clone();
}
added = true;
} else {
new_goals.push(g);
}
} else {
new_goals.push(g);
}
}
if !added {
self.edges.push(ty.clone());
}
self.goals = new_goals;
}
fn is_solved(&self) -> bool {
self.goals.len() == 0
}
fn contains(&self, t: &TypeTerm) -> Option< HashMap<TypeID, TypeTerm> > {
for e in self.edges.iter() {
if let Ok(σ) = crate::unify(&e.dst_type, t) {
return Some(σ)
}
}
None
}
}
pub struct PathApproxSteinerTreeSolver {
root: TypeTerm,
leaves: Vec< TypeTerm >
}
impl PathApproxSteinerTreeSolver {
pub fn new(
root: TypeTerm,
leaves: Vec<TypeTerm>
) -> Self {
PathApproxSteinerTreeSolver {
root, leaves
}
}
pub fn solve<M: Morphism + Clone + PartialEq>(self, morphisms: &MorphismBase<M>) -> Option< SteinerTree > {
let mut edges = Vec::<MorphismType>::new();
for goal in self.leaves {
eprintln!("solve steiner tree: find path to goal {:?}", goal);
// try to find shortest path from root to current leaf
if let Some(new_path) = morphisms.find_morphism_path(
MorphismType {
src_type: self.root.clone(),
dst_type: goal.clone()
}
) {
eprintln!("path to {:?} has len {}", goal.clone(), new_path.len());
for morph_inst in new_path {
let t = morph_inst.get_type();
if ! edges.contains(&t) {
eprintln!("add edge {:?}", t);
edges.push(t);
}
}
/*
// reduce new path so that it does not collide with any existing path
let mut src_type = self.root.clone();
let mut new_path_iter = new_path.into_iter().peekable();
// check all existing nodes..
if new_path_iter.peek().unwrap().get_type().src_type == src_type {
eprintln!("skip initial node..");
new_path_iter.next();
}
for mt in tree.iter() {
//assert!( mt.src_type == &src_type );
if let Some(t) = new_path_iter.peek() {
eprintln!("");
if &mt.dst_type == &t.get_type().src_type {
// eliminate this node from new path
src_type = new_path_iter.next().unwrap().get_type().src_type;
}
} else {
break;
}
}
for m in new_path_iter {
tree.push(m.get_type());
}
*/
} else {
eprintln!("could not find path\nfrom {:?}\nto {:?}", &self.root, &goal);
return None;
}
}
Some(SteinerTree {
weight: 0,
goals: vec![],
edges
})
}
}
/* given a representation tree with the available
* represenatations `src_types`, try to find
* a sequence of morphisms that span up all
* representations in `dst_types`.
*/
pub struct SteinerTreeProblem {
src_types: Vec< TypeTerm >,
queue: Vec< SteinerTree >
}
impl SteinerTreeProblem {
pub fn new(
src_types: Vec< TypeTerm >,
dst_types: Vec< TypeTerm >
) -> Self {
SteinerTreeProblem {
src_types: src_types.into_iter().map(|t| t.normalize()).collect(),
queue: vec![
SteinerTree {
weight: 0,
goals: dst_types.into_iter().map(|t| t.normalize()).collect(),
edges: Vec::new()
}
]
}
}
pub fn next(&mut self) -> Option< SteinerTree > {
eprintln!("queue size = {}", self.queue.len());
/* FIXME: by giving the highest priority to
* candidate tree with the least remaining goals,
* the optimality of the search algorithm
* is probably destroyed, but it dramatically helps
* to tame the combinatorical explosion in this algorithm.
*/
self.queue.sort_by(|t1, t2|
if t1.goals.len() < t2.goals.len() {
std::cmp::Ordering::Greater
} else if t1.goals.len() == t2.goals.len() {
if t1.weight < t2.weight {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Less
}
} else {
std::cmp::Ordering::Less
}
);
self.queue.pop()
}
/*
pub fn solve_approx_path<M: Morphism + Clone>(&mut self, morphisms: &MorphismBase<M>) -> Option< SteinerTree > {
if let Some(master) = self.src_types.first() {
}
}
*/
pub fn solve_bfs<M: Morphism + Clone>(&mut self, morphisms: &MorphismBase<M>) -> Option< SteinerTree > {
// take the currently smallest tree and extend it by one step
while let Some( mut current_tree ) = self.next() {
// check if current tree is a solution
if current_tree.goals.len() == 0 {
return Some(current_tree);
}
// get all vertices spanned by this tree
let mut current_nodes = self.src_types.clone();
for e in current_tree.edges.iter() {
current_nodes.push( e.dst_type.clone() );
}
// extend the tree by one edge and add it to the queue
for src_type in current_nodes {
for next_morph_inst in morphisms.enum_morphisms(&src_type) {
//let dst_type = TypeTerm::Ladder(vec![dst_halo, dst_ty]).normalize();
let dst_type = next_morph_inst.get_type().dst_type;
if current_tree.contains( &dst_type ).is_none() {
let mut new_tree = current_tree.clone();
{
let src_type = src_type.clone();
let dst_type = dst_type.clone();
new_tree.add_edge(MorphismType { src_type, dst_type }.normalize());
}
self.queue.push( new_tree );
}
}
}
}
None
}
}