initial cost heuristic for morphism types & accelerated graph search (wip)

This commit is contained in:
Michael Sippel 2025-05-29 13:13:52 +02:00
parent 32cb3cf98a
commit 267b5e009e
Signed by: senvas
GPG key ID: F96CF119C34B64A6
6 changed files with 115 additions and 7 deletions

55
src/heuristic.rs Normal file
View file

@ -0,0 +1,55 @@
use crate::{MorphismType, TypeTerm};
pub fn estimated_morphism_cost(ty: &MorphismType) -> u64 {
if let Ok((ψ,σ)) = crate::subtype_unify(&ty.src_type, &ty.dst_type) {
1
} else {
match (ty.src_type.clone().normalize(),
ty.dst_type.clone().normalize())
{
(TypeTerm::Ladder(r1),
TypeTerm::Ladder(r2)) => {
let mut cost = 10;
for i in 0..usize::min( r1.len(), r2.len() ) {
cost += estimated_morphism_cost(&MorphismType {
src_type: r1[i].clone(), dst_type: r2[i].clone() });
}
cost
}
(TypeTerm::Spec(a1),
TypeTerm::Spec(a2)) => {
let mut cost = 10;
for i in 0..usize::min( a1.len(), a2.len() ) {
cost += estimated_morphism_cost(
&MorphismType {src_type: a1[i].clone(), dst_type: a2[i].clone() });
}
cost
}
(TypeTerm::Seq{ seq_repr: sr1, items: items1 },
TypeTerm::Seq{ seq_repr: sr2, items: items2 }) => {
let mut cost = 10;
/*
estimated_morphism_cost(
&MorphismType { src_type: sr1, dst_type: sr2 }
);
*/
for i in 0..usize::min( items1.len(), items2.len() ) {
cost += estimated_morphism_cost(
&MorphismType { src_type: items1[i].clone(), dst_type: items2[i].clone() }
);
}
cost
}
(a, b) => {
if a == b {
return 0;
} else {
return 10;
}
}
}
}
}

View file

@ -17,6 +17,8 @@ pub mod morphism;
pub mod morphism_base;
pub mod morphism_path;
pub mod heuristic;
#[cfg(test)]
mod test;

View file

@ -219,6 +219,17 @@ impl<M: Morphism + Clone> MorphismInstance<M> {
self.get_type().strip_halo()
}
pub fn get_weight(&self) -> u64 {
match self {
MorphismInstance::Id { ψ } => 0,
MorphismInstance::Primitive { ψ, σ, morph } => 1,
MorphismInstance::Chain { path } => path.iter().map(|m| m.get_weight()).sum(),
MorphismInstance::MapSeq { ψ, seq_repr, item_morph } => item_morph.get_weight() + 1,
MorphismInstance::MapStruct { ψ, src_struct_repr, dst_struct_repr, member_morph } => member_morph.iter().map(|m| m.1.get_weight()).sum(),
MorphismInstance::MapEnum { ψ, enum_repr, variant_morph } => variant_morph.iter().map(|m| m.1.get_weight()).sum()
}
}
pub fn get_type(&self) -> MorphismType {
match self {
MorphismInstance::Id { ψ } => {

View file

@ -5,6 +5,7 @@ use {
morphism_base::MorphismBase,
substitution::Substitution,
term::*, desugared_term::*,
heuristic::*,
}
};
@ -13,8 +14,9 @@ use {
#[derive(Clone)]
pub struct MorphismPath<M: Morphism + Clone> {
pub weight: u64,
pub est_remain: u64,
pub cur_type: TypeTerm,
pub morphisms: Vec< MorphismInstance<M> >
pub morphisms: Vec< MorphismInstance<M> >,
}
@ -41,7 +43,7 @@ impl<'a, M:Morphism+Clone> ShortestPathProblem<'a, M> {
ShortestPathProblem {
morphism_base,
queue: vec![
MorphismPath::<M> { weight: 0, cur_type: ty.src_type, morphisms: vec![] }
MorphismPath::<M> { weight: 0, est_remain: estimated_morphism_cost(&ty), cur_type: ty.src_type, morphisms: vec![] }
],
goal: ty.dst_type
}
@ -49,7 +51,13 @@ impl<'a, M:Morphism+Clone> ShortestPathProblem<'a, M> {
pub fn advance(&mut self, prev_path: &MorphismPath<M>, morph_inst: MorphismInstance<M>) {
let dst_type = morph_inst.get_type().dst_type;
//eprintln!("try morph to {:?}", dst_type.clone());//.sugar(type_dict).pretty(type_dict, 0));
/*
eprintln!("try morph to {:?} (weight: {}) (prev: {} + est {})", dst_type.clone(),
morph_inst.get_weight(),
prev_path.weight,
prev_path.est_remain,
);//.sugar(type_dict).pretty(type_dict, 0));
*/
let mut creates_loop = false;
@ -63,8 +71,9 @@ impl<'a, M:Morphism+Clone> ShortestPathProblem<'a, M> {
}
if ! creates_loop {
new_path.weight += 1;//next_morph_inst.get_weight();
new_path.weight += morph_inst.get_weight();
new_path.cur_type = dst_type;
new_path.est_remain = estimated_morphism_cost(&MorphismType{ src_type: new_path.cur_type.clone(), dst_type: self.goal.clone() });
new_path.morphisms.push(morph_inst);
self.queue.push(new_path);
@ -73,8 +82,15 @@ impl<'a, M:Morphism+Clone> ShortestPathProblem<'a, M> {
pub fn solve(&mut self) -> Option< Vec<MorphismInstance<M>> > {
while ! self.queue.is_empty() {
/* take the shortest partial path and try to advance it by one step */
self.queue.sort_by( |p1,p2| p2.weight.cmp(&p1.weight));
/* take the most promising partial path and try to advance it by one step */
self.queue.sort_by( |p1,p2| ( p2.weight + p2.est_remain ).cmp(&( p1.weight + p1.est_remain ) ));
/*
eprintln!("===== TOP 5 PATHS =====\nGoal: {}", self.goal.pretty(dict, 0));
for i in 1 ..= usize::min(self.queue.len(), 5) {
let path = &self.queue[self.queue.len() - i];
eprintln!("[[ {} ]] (w: {}, est remain: {}) --- {}", i, path.weight, path.est_remain, path.cur_type.pretty(dict, 0));
}
*/
if let Some(mut cur_path) = self.queue.pop() {
/* 1. Check if goal is already reached by the current path */

24
src/test/heuristic.rs Normal file
View file

@ -0,0 +1,24 @@
use crate::{heuristic::*, dict::*, parser::*, morphism::*};
#[test]
fn test_heuristic() {
let mut dict = BimapTypeDict::new();
assert_eq!(
estimated_morphism_cost(
&MorphismType {
src_type: dict.parse("A").expect("parse"),
dst_type: dict.parse("A").expect("parse")
}),
1
);
assert_eq!(
estimated_morphism_cost(
&MorphismType {
src_type: dict.parse("<Digit 10> ~ Char ~ Ascii ~ native.UInt8").expect("parse"),
dst_type: dict.parse("<Digit 16> ~ native.UInt8").expect("parse")
}),
41
);
}

View file

@ -7,4 +7,4 @@ pub mod pnf;
pub mod substitution;
pub mod unification;
pub mod morphism;
pub mod heuristic;