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 314f2141d8
commit 8212174cc4
Signed by: senvas
GPG key ID: F96CF119C34B64A6
6 changed files with 124 additions and 7 deletions

60
src/heuristic.rs Normal file
View file

@ -0,0 +1,60 @@
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 {
bounds: Vec::new(),
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 {
bounds: Vec::new(),
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 {
bounds: Vec::new(),
src_type: items1[i].clone(), dst_type: items2[i].clone() }
);
}
cost
}
(a, b) => {
if a == b {
return 0;
} else {
return 10;
}
}
}
}
}

View file

@ -7,6 +7,8 @@ pub mod context;
pub mod constraint_system;
pub mod morphism_graph;
pub mod heuristic;
#[cfg(test)]
mod test;

View file

@ -230,6 +230,7 @@ pub trait Morphism : Sized {
#[derive(Clone, PartialEq, Debug)]
pub enum MorphismInstance<M: Morphism + Clone> {
//Id{ ψ: TypeTerm }
Primitive{
ψ: TypeTerm,
σ: HashMapSubst,
@ -262,6 +263,17 @@ impl<M: Morphism + Clone> MorphismInstance<M> {
self.get_type().strip_common_rungs()
}
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::Primitive { ψ, σ, morph } => {

View file

@ -2,7 +2,8 @@ use {
crate::{
morphism_graph::{Morphism, MorphismInstance, MorphismType, MorphismBase},
term::*,
HashMapSubst
HashMapSubst,
heuristic::*,
}
};
@ -11,8 +12,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> >,
}
@ -39,7 +41,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
}
@ -47,7 +49,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;
@ -61,8 +69,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{ bounds: Vec::new(), src_type: new_path.cur_type.clone(), dst_type: self.goal.clone() });
new_path.morphisms.push(morph_inst);
self.queue.push(new_path);
@ -71,8 +80,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 */

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

@ -0,0 +1,26 @@
use crate::{heuristic::*, dict::*, parser::*, morphism_graph::*};
#[test]
fn test_heuristic() {
let mut dict = BimapTypeDict::new();
assert_eq!(
estimated_morphism_cost(
&MorphismType {
bounds: Vec::new(),
src_type: dict.parse("A").expect("parse"),
dst_type: dict.parse("A").expect("parse")
}),
1
);
assert_eq!(
estimated_morphism_cost(
&MorphismType {
bounds: Vec::new(),
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

@ -6,3 +6,4 @@ pub mod pnf;
pub mod context;
pub mod constraint_system;
pub mod morphism_graph;
pub mod heuristic;