use crate::{
language::{CompoundTermRef, CompoundTermRefMut, Term},
symbols::*,
};
use nar_dev_utils::void;
use rand::{rngs::StdRng, seq::SliceRandom, RngCore, SeedableRng};
use std::{collections::HashMap, ops::BitAnd};
#[derive(Debug, Default, Clone)]
#[doc(alias = "VariableSubstitution")]
pub struct VarSubstitution {
map: HashMap<Term, Term>,
}
#[macro_export]
macro_rules! substitution {
(
$(
$to_be_substitute:expr => $substituted:expr $(,)?
)*
) => {
$crate::language::variable_process::VarSubstitution::from_pairs([
$(
(
$to_be_substitute.parse::<Term>().unwrap(),
$substituted.parse::<Term>().unwrap(),
)
),*
])
};
}
impl VarSubstitution {
pub fn new() -> Self {
Self::default()
}
pub fn from(map: impl Into<HashMap<Term, Term>>) -> Self {
Self { map: map.into() }
}
pub fn from_pairs(pairs: impl IntoIterator<Item = (Term, Term)>) -> Self {
Self {
map: HashMap::from_iter(pairs),
}
}
pub fn get(&self, key: &Term) -> Option<&Term> {
self.map.get(key)
}
pub fn chain_get(&self, key: &Term) -> Option<&Term> {
let mut end_point = self.get(key)?;
loop {
match self.get(end_point) {
Some(next_point) => {
debug_assert!(end_point != key, "不应有循环替换之情况!{key} @ {self:?}");
end_point = next_point
}
None => break Some(end_point),
}
}
}
pub fn has(&self, key: &Term) -> bool {
self.map.contains_key(key)
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn put(&mut self, key: &Term, value: Term) {
match self.map.get_mut(key) {
Some(old_value) => *old_value = value,
None => void(self.map.insert(key.clone(), value)),
}
}
pub fn reduce_identities(&mut self) {
self.map.retain(|k, v| k != v);
}
}
impl CompoundTermRefMut<'_> {
fn _apply_substitute(
&mut self,
substitution: &VarSubstitution,
get_f: for<'t> fn(&'t VarSubstitution, &Term) -> Option<&'t Term>,
) {
for inner in self.components() {
if let Some(substitute_term) = get_f(substitution, inner) {
let substitute = substitute_term.clone();
*inner = substitute;
}
else if let Some(mut inner_compound) = inner.as_compound_mut() {
inner_compound._apply_substitute(substitution, get_f);
}
}
if self.is_commutative() {
self.reorder_components();
}
}
pub fn apply_substitute(&mut self, substitution: &VarSubstitution) {
self._apply_substitute(substitution, VarSubstitution::chain_get)
}
pub fn rename_variables(&mut self) {
let mut substitution = VarSubstitution::new();
self.inner().for_each_atom_mut(&mut |atom| {
if atom.instanceof_variable() && !substitution.has(atom) {
substitution.put(atom, Term::make_var_similar(atom, substitution.len() + 1));
}
});
substitution.reduce_identities();
self._apply_substitute(&substitution, VarSubstitution::get);
}
}
fn unify_find(
var_type: &str,
to_be_unified_1: &Term,
to_be_unified_2: &Term,
shuffle_rng_seed: u64,
) -> Unification {
let mut unify_map_1 = VarSubstitution::new();
let mut unify_map_2 = VarSubstitution::new();
let has_unification = find_unification(
var_type,
to_be_unified_1,
to_be_unified_2,
&mut unify_map_1,
&mut unify_map_2,
shuffle_rng_seed,
);
Unification {
has_unification,
unify_map_1,
unify_map_2,
}
}
pub fn unify_find_i(
to_be_unified_1: &Term,
to_be_unified_2: &Term,
shuffle_rng_seed: u64,
) -> Unification {
unify_find(
VAR_INDEPENDENT,
to_be_unified_1,
to_be_unified_2,
shuffle_rng_seed,
)
}
pub fn unify_find_d(
to_be_unified_1: &Term,
to_be_unified_2: &Term,
shuffle_rng_seed: u64,
) -> Unification {
unify_find(
VAR_DEPENDENT,
to_be_unified_1,
to_be_unified_2,
shuffle_rng_seed,
)
}
pub fn unify_find_q(
to_be_unified_1: &Term,
to_be_unified_2: &Term,
shuffle_rng_seed: u64,
) -> Unification {
unify_find(
VAR_QUERY,
to_be_unified_1,
to_be_unified_2,
shuffle_rng_seed,
)
}
#[derive(Debug, Clone)]
pub struct Unification {
pub has_unification: bool,
pub unify_map_1: VarSubstitution,
pub unify_map_2: VarSubstitution,
}
impl Unification {
#[inline]
pub fn apply_to(&self, parent1: CompoundTermRefMut, parent2: CompoundTermRefMut) -> bool {
unify_apply(parent1, parent2, self)
}
pub fn apply_to_term(&self, parent1: &mut Term, parent2: &mut Term) -> bool {
match [parent1.as_compound_mut(), parent2.as_compound_mut()] {
[Some(parent1), Some(parent2)] => self.apply_to(parent1, parent2),
_ => self.has_unification,
}
}
}
fn unify_apply(
unified_in_1: CompoundTermRefMut,
unified_in_2: CompoundTermRefMut,
unification: &Unification,
) -> bool {
let Unification {
has_unification,
unify_map_1,
unify_map_2,
} = unification;
apply_unify_one(unified_in_1, unify_map_1);
apply_unify_one(unified_in_2, unify_map_2);
*has_unification
}
fn apply_unify_one(mut unified_in: CompoundTermRefMut, substitution: &VarSubstitution) {
if substitution.is_empty() {
return;
}
unified_in.apply_substitute(substitution);
unified_in.rename_variables();
}
impl VarSubstitution {
pub fn apply_to(&self, to: CompoundTermRefMut) {
apply_unify_one(to, self)
}
#[inline]
pub fn apply_to_term(&self, to: &mut Term) {
if let Some(to) = to.as_compound_mut() {
self.apply_to(to);
}
}
}
pub type AppliedCompounds = [Term; 2];
fn is_same_kind_compound(t1: CompoundTermRef, t2: CompoundTermRef) -> bool {
if t1.size() != t2.size() {
return false;
}
if (t1.instanceof_image() && t2.instanceof_image())
&& t1.get_placeholder_index() != t2.get_placeholder_index()
{
return false;
}
true
}
fn find_unification(
var_type: &str,
to_be_unified_1: &Term,
to_be_unified_2: &Term,
map_1: &mut VarSubstitution,
map_2: &mut VarSubstitution,
shuffle_rng_seed: u64,
) -> bool {
struct UnificationStatus<'s> {
var_type: &'s str,
max_var_id: usize,
}
let status = UnificationStatus {
var_type,
max_var_id: Term::maximum_variable_id_multi([to_be_unified_1, to_be_unified_2]),
};
impl UnificationStatus<'_> {
fn as_correct_var<'t>(&self, t: &'t Term) -> Option<(&'t Term, usize)> {
t.as_variable() .filter(|_| t.get_variable_type() == self.var_type) .map(|id| (t, id)) }
#[inline]
fn is_common_variable(&self, v: &Term) -> bool {
v.as_variable().is_some_and(|id| id > self.max_var_id)
}
fn common_var_id_from(&self, id1: usize, id2: usize) -> usize {
(self.max_var_id + 1) * (1 + id1) + id2
}
#[inline]
fn make_common_variable(&self, id1: usize, id2: usize) -> Term {
Term::from_var_similar(self.var_type, self.common_var_id_from(id1, id2))
}
}
fn find_unification_sub(
status: &UnificationStatus,
[to_be_unified_1, to_be_unified_2]: [&Term; 2],
[map_1, map_2]: [&mut VarSubstitution; 2],
shuffle_rng_seed: u64, ) -> bool {
let is_same_type = to_be_unified_1.is_same_type(to_be_unified_2);
match [
status.as_correct_var(to_be_unified_1),
status.as_correct_var(to_be_unified_2),
] {
[Some((var_1, id1)), Some((var_2, id2))] => {
if let Some(ref mapped) = map_1.get(var_1).cloned() {
return find_unification_sub(
status,
[mapped, to_be_unified_2],
[map_1, map_2],
shuffle_rng_seed,
);
}
let common_var = status.make_common_variable(id1, id2);
map_1.put(var_1, common_var.clone()); map_2.put(var_2, common_var); true
}
[Some((var_1, _)), None] => {
if let Some(ref mapped) = map_1.get(var_1).cloned() {
return find_unification_sub(
status,
[mapped, to_be_unified_2],
[map_1, map_2],
shuffle_rng_seed,
);
}
map_1.put(var_1, to_be_unified_2.clone());
if status.is_common_variable(var_1) {
map_2.put(var_1, to_be_unified_2.clone());
}
true
}
[None, Some((var_2, _))] => {
if let Some(ref mapped) = map_2.get(var_2).cloned() {
return find_unification_sub(
status,
[to_be_unified_1, mapped],
[map_1, map_2],
shuffle_rng_seed,
);
}
map_2.put(var_2, to_be_unified_1.clone());
if status.is_common_variable(var_2) {
map_1.put(var_2, to_be_unified_1.clone());
}
true
}
[None, None] => match [to_be_unified_1.as_compound(), to_be_unified_2.as_compound()] {
[Some(compound_1), Some(compound_2)] if is_same_type => {
if !is_same_kind_compound(compound_1, compound_2) {
return false;
}
let mut list = compound_1.clone_component_refs();
let mut rng = StdRng::seed_from_u64(shuffle_rng_seed);
if compound_1.is_commutative() {
list.shuffle(&mut rng);
}
(list.into_iter().zip(compound_2.components.iter()))
.map(|(inner1, inner2)| {
find_unification_sub(
status,
[inner1, inner2],
[map_1, map_2],
rng.next_u64(),
)
})
.fold(true, BitAnd::bitand)
}
_ => to_be_unified_1 == to_be_unified_2, },
}
}
find_unification_sub(
&status,
[to_be_unified_1, to_be_unified_2],
[map_1, map_2],
shuffle_rng_seed,
)
}
fn has_unification(
var_type: &str,
to_be_unified_1: &Term,
to_be_unified_2: &Term,
shuffle_rng_seed: u64,
) -> bool {
find_unification(
var_type,
to_be_unified_1,
to_be_unified_2,
&mut VarSubstitution::new(),
&mut VarSubstitution::new(),
shuffle_rng_seed,
)
}
pub fn has_unification_i(
to_be_unified_1: &Term,
to_be_unified_2: &Term,
shuffle_rng_seed: u64,
) -> bool {
has_unification(
VAR_INDEPENDENT,
to_be_unified_1,
to_be_unified_2,
shuffle_rng_seed,
)
}
pub fn has_unification_d(
to_be_unified_1: &Term,
to_be_unified_2: &Term,
shuffle_rng_seed: u64,
) -> bool {
has_unification(
VAR_DEPENDENT,
to_be_unified_1,
to_be_unified_2,
shuffle_rng_seed,
)
}
pub fn has_unification_q(
to_be_unified_1: &Term,
to_be_unified_2: &Term,
shuffle_rng_seed: u64,
) -> bool {
has_unification(
VAR_QUERY,
to_be_unified_1,
to_be_unified_2,
shuffle_rng_seed,
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::AResult;
use crate::{ok, test_term as term};
use nar_dev_utils::macro_once;
use rand::Rng;
#[test]
fn apply_substitute() -> AResult {
fn test(substitution: &VarSubstitution, term: &str, expected: &str) {
let parse = |term: &str| match term.parse() {
Ok(term) => term,
Err(e) => panic!("{term:?}解析失败: {e}"),
};
let mut term: Term = parse(term);
let expected: Term = parse(expected);
let mut compound = term
.as_compound_mut()
.expect("传入的不是复合词项,无法进行替换");
compound.apply_substitute(substitution);
assert_eq!(term, expected);
}
let substitution = substitution!(
"var_word" => "word"
"$1" => "1"
"?1" => "(/, A, <lock --> swan>, _, [1])" "[#1]" => "<X --> (*, Y, [Z])>" );
let substitution2 = substitution!(
"$1" => "(/,$1,_,{L2})" );
macro_once! {
macro test(
$(
$term_str:expr, $substitution:expr
=> $substituted_str:expr
)*
) {
$(
test(&$substitution, $term_str, $substituted_str);
)*
}
"(&&, A, var_word)", substitution => "(&&, A, word)"
"(&&, var_word, A)", substitution => "(&&, word, A)"
"(&&, A, var_word, B)", substitution => "(&&, A, word, B)"
"(&&, var_word, A, B)", substitution => "(&&, word, A, B)"
"<A --> var_word>", substitution => "<A --> word>"
"<var_word --> A>", substitution => "<word --> A>"
"<A <-> var_word>", substitution => "<A <-> word>"
"<var_word <-> A>", substitution => "<word <-> A>"
"<A ==> var_word>", substitution => "<A ==> word>"
"<var_word ==> A>", substitution => "<word ==> A>"
"<A --> $1>", substitution => "<A --> 1>"
"<$1 --> A>", substitution => "<1 --> A>"
"<$1 --> var_word>", substitution => "<1 --> word>"
"<var_word --> $1>", substitution => "<word --> 1>"
"<<$1 --> A> ==> <B --> $1>>", substitution => "<<1 --> A> ==> <B --> 1>>"
"<<$1 --> var_word> --> (*, var_word, $1)>", substitution => "<<1 --> word> --> (*, word, 1)>"
"<<var_word --> A> ==> [#1]>", substitution => "<<word --> A> ==> <X --> (*, Y, [Z])>>"
"(--, (&&, (||, (&, (|, (*, ?1), x), x), x), x))", substitution => "(--, (&&, (||, (&, (|, (*, (/, A, <lock --> swan>, _, [1])), x), x), x), x))"
"<<{O1} --> $1> ==> <{O2} --> $1>>", substitution2 => "<<{O1} --> (/,$1,_,{L2})> ==> <{O2} --> (/,$1,_,{L2})>>"
}
ok!()
}
#[test]
fn unify() -> AResult {
let mut rng = StdRng::from_seed([0; 32]);
fn test(
mut term1: Term,
mut term2: Term,
var_type: &str,
expected_1: Term,
expected_2: Term,
shuffle_rng: &mut impl Rng,
) {
print!("unify: {term1}, {term2} =={var_type}=> ",);
unify_find(var_type, &term1, &term2, shuffle_rng.next_u64())
.apply_to_term(&mut term1, &mut term2);
println!("{term1}, {term2}");
assert_eq!(term1, expected_1);
assert_eq!(term2, expected_2);
}
macro_once! {
macro test(
$(
$term_str1:expr, $term_str2:expr
=> $var_type:expr =>
$substituted_str1:expr, $substituted_str2:expr
)*
) {
$(
test(
term!($term_str1),
term!($term_str2),
$var_type,
term!($substituted_str1),
term!($substituted_str2),
&mut rng );
)*
}
"$1", "A" => "$" => "$1", "A"
"<$1 --> B>", "<A --> B>" => "$" => "<A --> B>", "<A --> B>"
"<A --> $1>", "<A --> B>" => "$" => "<A --> B>", "<A --> B>"
"<A --> B>", "<$1 --> B>" => "$" => "<A --> B>", "<A --> B>"
"<A --> B>", "<A --> $1>" => "$" => "<A --> B>", "<A --> B>"
"<$a --> B>", "<A --> $b>" => "$" => "<A --> B>", "<A --> B>"
"<A --> B>", "<$a --> $b>" => "$" => "<A --> B>", "<A --> B>"
"(--, $1)", "(--, 1)" => "$" => "(--, 1)", "(--, 1)"
"(--, #1)", "(--, 1)" => "#" => "(--, 1)", "(--, 1)"
"(--, ?1)", "(--, 1)" => "?" => "(--, 1)", "(--, 1)"
"(*, $i, #d, ?q)", "(*, I, D, Q)" => "$" => "(*, I, #d, ?q)", "(*, I, D, Q)"
"(*, $i, #d, ?q)", "(*, I, D, Q)" => "#" => "(*, $i, D, ?q)", "(*, I, D, Q)"
"(*, $i, #d, ?q)", "(*, I, D, Q)" => "?" => "(*, $i, #d, Q)", "(*, I, D, Q)"
"(*, $c, $b, $a)", "(*, (--, C), <B1 --> B2>, A)" => "$" => "(*, (--, C), <B1 --> B2>, A)", "(*, (--, C), <B1 --> B2>, A)"
"<(*, <A-->C>, <B-->$2>) ==> <C-->$2>>", "<(*, <A-->$1>, <B-->D>) ==> <$1-->D>>"
=> "$"
=> "<(*, <A-->C>, <B-->D>) ==> <C-->D>>", "<(*, <A-->C>, <B-->D>) ==> <C-->D>>"
"{$c}", "{中心点}" => "$" => "{中心点}", "{中心点}" "[$c]", "[中心点]" => "$" => "[中心点]", "[中心点]" }
ok!()
}
#[test]
fn rename_variables() -> AResult {
fn test(mut term: Term, expected: Term) {
print!("{term}");
let mut compound = term.as_compound_mut().expect("非复合词项,无法重命名变量");
compound.rename_variables();
println!("=> {term}");
assert_eq!(term, expected);
}
macro_once! {
macro test($($term:literal => $expected:expr )*) {
$(
test(term!($term), term!($expected));
)*
}
"{$A, $B}" => "{$1, $2}"
"[$A, $B]" => "[$1, $2]"
"(&, $A, $B)" => "(&, $1, $2)"
"(|, $A, $B)" => "(|, $1, $2)"
"(-, $A, $B)" => "(-, $1, $2)"
"(~, $A, $B)" => "(~, $1, $2)"
"(*, $A, $B)" => "(*, $1, $2)"
r"(/, $R, _)" => r"(/, $1, _)"
r"(\, $R, _)" => r"(\, $1, _)"
r"(/, $R, _, $A)" => r"(/, $1, _, $2)"
r"(\, $R, _, $A)" => r"(\, $1, _, $2)"
r"(&&, $A, $B)" => r"(&&, $1, $2)"
r"(||, $A, $B)" => r"(||, $1, $2)"
r"(--, $A)" => r"(--, $1)"
"<$A --> $B>" => "<$1 --> $2>"
"<$A <-> $B>" => "<$1 <-> $2>"
"<$A ==> $B>" => "<$1 ==> $2>"
"<$A <=> $B>" => "<$1 <=> $2>"
"(*, $A, $B, $C)" => "(*, $1, $2, $3)"
"(*, #A, #B, #C)" => "(*, #1, #2, #3)"
"(*, ?A, ?B, ?C)" => "(*, ?1, ?2, ?3)"
"(*, $A, #A, ?A)" => "(*, $1, #2, ?3)"
"(*, A, $B, [C, #D])" => "(*, A, $1, [C, #2])"
"<(--, (--, (--, (--, (--, (--, (--, (--, A)))))))) --> (/, (-, ?B, C), _, (/, (/, (/, (/, (/, #D, _), _), _), _), _))>" => "<(--, (--, (--, (--, (--, (--, (--, (--, A)))))))) --> (/, (-, ?1, C), _, (/, (/, (/, (/, (/, #2, _), _), _), _), _))>"
"<<A --> $B> ==> <#C --> D>>" => "<<A --> $1> ==> <#2 --> D>>"
"<<A --> #B> ==> <$B --> D>>" => "<<A --> #1> ==> <$2 --> D>>"
"<<A --> $B> ==> <$B --> D>>" => "<<A --> $1> ==> <$1 --> D>>"
"(*, $A, $A, $A)" => "(*, $1, $1, $1)"
"(*, (*, $A, $A, $A), (*, $A, $A, $A), (*, $A, $A, $A))" => "(*, (*, $1, $1, $1), (*, $1, $1, $1), (*, $1, $1, $1))"
}
ok!()
}
}