Skip to main content

narust_158/language/
variable_process.rs

1//! 变量处理
2//! * 🎯承载所有与「变量」有关的处理
3//!
4//! ! ⚠️【2024-06-19 23:01:30】此处有关「变量处理」的逻辑尚未稳定:
5//!   * 🚧有待在OpenNARS改版中「函数式改造」
6
7use crate::{
8    language::{CompoundTermRef, CompoundTermRefMut, Term},
9    symbols::*,
10};
11use nar_dev_utils::void;
12use rand::{rngs::StdRng, seq::SliceRandom, RngCore, SeedableRng};
13use std::{collections::HashMap, ops::BitAnd};
14
15/// 用于表示「变量替换」的字典
16/// * 🎯NAL-6中的「变量替换」「变量代入」
17#[derive(Debug, Default, Clone)]
18#[doc(alias = "VariableSubstitution")]
19pub struct VarSubstitution {
20    map: HashMap<Term, Term>,
21}
22
23/// 快捷构造宏
24///
25/// ## 语法
26///
27/// ```
28/// use narust_158::substitution;
29/// use narust_158::language::Term;
30///
31/// // 直接像一个字典那样构造
32/// substitution! {
33///     "A" => "B" // 无需逗号
34///     "C" => "D"
35/// };
36/// substitution! {
37///     "A" => "B", // 有逗号的版本
38///     "C" => "D",
39/// };
40/// ```
41#[macro_export]
42macro_rules! substitution {
43    (
44        $(
45            $to_be_substitute:expr => $substituted:expr $(,)?
46        )*
47    ) => {
48        $crate::language::variable_process::VarSubstitution::from_pairs([
49            $(
50                (
51                    $to_be_substitute.parse::<Term>().unwrap(),
52                    $substituted.parse::<Term>().unwrap(),
53                )
54            ),*
55        ])
56    };
57}
58
59impl VarSubstitution {
60    /// 构造函数
61    pub fn new() -> Self {
62        Self::default()
63    }
64
65    /// 从其它构造出「散列映射」的地方构造
66    pub fn from(map: impl Into<HashMap<Term, Term>>) -> Self {
67        Self { map: map.into() }
68    }
69
70    /// 从其它构造出「散列映射」的地方构造
71    pub fn from_pairs(pairs: impl IntoIterator<Item = (Term, Term)>) -> Self {
72        Self {
73            map: HashMap::from_iter(pairs),
74        }
75    }
76
77    /// 尝试获取「替代项」
78    /// * 🎯变量替换
79    pub fn get(&self, key: &Term) -> Option<&Term> {
80        self.map.get(key)
81    }
82
83    /// 链式获取「变量替换」最终点
84    /// * 🚩一路查找到头
85    /// * 📄{A -> B, B -> C}, A => Some(C)
86    /// * 📄{A -> B, B -> C}, B => Some(C)
87    /// * 📄{A -> B, B -> C}, C => None
88    pub fn chain_get(&self, key: &Term) -> Option<&Term> {
89        // * ⚠️此时应该传入非空值
90        // * 🚩从「起始点」开始查找
91        let mut end_point = self.get(key)?;
92        // * 🚩非空⇒一直溯源
93        loop {
94            match self.get(end_point) {
95                Some(next_point) => {
96                    debug_assert!(end_point != key, "不应有循环替换之情况!{key} @ {self:?}");
97                    end_point = next_point
98                }
99                None => break Some(end_point),
100            }
101        }
102    }
103
104    /// 尝试判断「是否有键」
105    /// * 🎯变量重命名
106    pub fn has(&self, key: &Term) -> bool {
107        self.map.contains_key(key)
108    }
109
110    /// 获取「可替换的变量个数」
111    /// * 🚩映射的大小
112    /// * 🎯变量重命名
113    pub fn len(&self) -> usize {
114        self.map.len()
115    }
116
117    /// 判断「是否为空」
118    /// * 🎯变量替换后检查「是否已替换」
119    pub fn is_empty(&self) -> bool {
120        self.map.is_empty()
121    }
122
123    /// 设置「替代项」
124    /// * 🎯寻找可替换变量,并返回结果
125    /// * 🚩只在没有键时复制`key`,并且总是覆盖`value`值
126    pub fn put(&mut self, key: &Term, value: Term) {
127        match self.map.get_mut(key) {
128            // * 🚩有键⇒覆盖
129            Some(old_value) => *old_value = value,
130            // * 🚩无键⇒插入
131            None => void(self.map.insert(key.clone(), value)),
132        }
133    }
134
135    /// 删除映射中的「恒等替换」
136    /// * 📄`$1 => $1`
137    pub fn reduce_identities(&mut self) {
138        // * 🚩直接调用内置方法
139        self.map.retain(|k, v| k != v);
140    }
141}
142
143impl CompoundTermRefMut<'_> {
144    /// 带函数指针的「递归替换词项」方法
145    /// * 🎯区分「变量重命名中的『非链式替换』」与「变量归一化中的『链式替换』」
146    fn _apply_substitute(
147        &mut self,
148        substitution: &VarSubstitution,
149        get_f: for<'t> fn(&'t VarSubstitution, &Term) -> Option<&'t Term>,
150    ) {
151        // * 🚩遍历替换内部所有元素
152        for inner in self.components() {
153            // * 🚩若有「替换方案」⇒替换
154            if let Some(substitute_term) = get_f(substitution, inner) {
155                // * ⚠️此处的「被替换词项」可能不是「变量词项」
156                // * 📄NAL-6变量引入时会建立「临时共同变量」匿名词项,以替换非变量词项
157                // * 🚩一路追溯到「没有再被传递性替换」的词项(最终点)
158                let substitute = substitute_term.clone();
159                // ! 🚩不使用set_term_when_dealing_variables
160                *inner = substitute;
161            }
162            // * 🚩复合词项⇒递归深入
163            else if let Some(mut inner_compound) = inner.as_compound_mut() {
164                inner_compound._apply_substitute(substitution, get_f);
165            }
166        }
167        // * 🚩可交换⇒替换之后重排顺序
168        if self.is_commutative() {
169            // re-order
170            self.reorder_components();
171        }
172        // * 📝【2024-08-08 13:03:24】所谓「共同变量」总会有所谓「泄漏」的问题
173        //   * 💡关键在于「是否最终能被当作『普通变量』对待」
174        //   * 🚩方案:将其就视作「普通变量」,判别方式就是「是否在词项本身域外」
175        // // 检查是否会有「共同变量泄漏」问题
176        // if cfg!(debug_assertions) {
177        //     self.for_each_atom(&mut |atom| {
178        //         debug_assert!(
179        //             !is_common_variable(atom) || substitution.chain_get(atom).is_some(),
180        //             "common variable {atom} leaked!\nsubstitution = {substitution:?}"
181        //         )
182        //     });
183        // }
184        // * ✅不再需要重新生成名称
185    }
186
187    /// 📄OpenNARS `CompoundTerm.applySubstitute` 方法
188    /// * 🚩直接分派给其组分
189    /// * 📝OpenNARS中「原子词项」不参与「变量替代」:执行无效果
190    /// * 🚩
191    ///
192    /// # 📄OpenNARS
193    ///
194    /// Recursively apply a substitute to the current CompoundTerm
195    pub fn apply_substitute(&mut self, substitution: &VarSubstitution) {
196        self._apply_substitute(substitution, VarSubstitution::chain_get)
197    }
198
199    /// 📄OpenNARS `Term.renameVariables` 方法
200    /// * 🚩重命名自身变量为一系列「固定编号」
201    ///   * 📌整体逻辑:将其中所有不同名称的「变量」编篡到一个字典中,排序后以编号重命名(抹消具体名称)
202    ///   * 📝因为这些变量都位于「词项内部」,即「变量作用域全被约束在词项内」,故无需考虑「跨词项编号歧义」的问题
203    /// * 📌变量替换的数字索引从`1`开始
204    ///   * 📝与变量类型完全无关(from OpenNARS)
205    ///     * 📄`(*, $A, #A, ?A)` => `(*, $1, #2, ?3)`
206    /// * 🎯用于将「变量」统一命名成固定的整数编号
207    /// * ❓目前对此存疑:必要性何在?
208    ///   * ~~不一致性:输入`<$A --> $B>`再输入`<$B --> $A>`会被看作是一样的变量~~
209    ///   * 📌既然是「变量作用域对整个词项封闭」那**任意名称都没问题**
210    ///
211    /// # 📄OpenNARS
212    ///
213    /// @ Term: Blank method to be override in CompoundTerm
214    ///
215    /// @ CompoundTerm:
216    ///   * Rename the variables in the compound, called from Sentence constructors
217    ///   * Recursively rename the variables in the compound
218    pub fn rename_variables(&mut self) {
219        // 创建「变量替换」
220        let mut substitution = VarSubstitution::new();
221        // 填充「变量映射对」
222        // * 🚩从`1`开始
223        self.inner().for_each_atom_mut(&mut |atom| {
224            // 条件:是变量 & 之前没出现过
225            if atom.instanceof_variable() && !substitution.has(atom) {
226                // * 🚩替换:类型不变,名称换成「映射大小+1」(唯一的,从1开始)
227                substitution.put(atom, Term::make_var_similar(atom, substitution.len() + 1));
228            }
229        });
230        // 清理无关变量
231        substitution.reduce_identities();
232        // 应用
233        // * 🚩【2024-08-19 22:11:58】非链式应用:对于「重命名变量」只需浅层替换
234        self._apply_substitute(&substitution, VarSubstitution::get);
235    }
236}
237
238/// `unify`的前半部分
239/// * 🎯复用「二词项」和「四词项」,兼容借用规则
240/// * 🚩从「将要被统一的词项」中计算出「变量替换映射」
241fn unify_find(
242    var_type: &str,
243    to_be_unified_1: &Term,
244    to_be_unified_2: &Term,
245    shuffle_rng_seed: u64,
246) -> Unification {
247    let mut unify_map_1 = VarSubstitution::new();
248    let mut unify_map_2 = VarSubstitution::new();
249    let has_unification = find_unification(
250        var_type,
251        to_be_unified_1,
252        to_be_unified_2,
253        &mut unify_map_1,
254        &mut unify_map_2,
255        shuffle_rng_seed,
256    );
257    // 返回获取的映射,以及「是否有替换」
258    Unification {
259        has_unification,
260        unify_map_1,
261        unify_map_2,
262    }
263}
264
265/// 【对外接口】统一独立变量
266pub fn unify_find_i(
267    to_be_unified_1: &Term,
268    to_be_unified_2: &Term,
269    shuffle_rng_seed: u64,
270) -> Unification {
271    unify_find(
272        VAR_INDEPENDENT,
273        to_be_unified_1,
274        to_be_unified_2,
275        shuffle_rng_seed,
276    )
277}
278
279/// 【对外接口】统一非独变量
280pub fn unify_find_d(
281    to_be_unified_1: &Term,
282    to_be_unified_2: &Term,
283    shuffle_rng_seed: u64,
284) -> Unification {
285    unify_find(
286        VAR_DEPENDENT,
287        to_be_unified_1,
288        to_be_unified_2,
289        shuffle_rng_seed,
290    )
291}
292
293/// 【对外接口】统一查询变量
294pub fn unify_find_q(
295    to_be_unified_1: &Term,
296    to_be_unified_2: &Term,
297    shuffle_rng_seed: u64,
298) -> Unification {
299    unify_find(
300        VAR_QUERY,
301        to_be_unified_1,
302        to_be_unified_2,
303        shuffle_rng_seed,
304    )
305}
306
307/// 多值输出:寻找「归一替换」的中间结果
308/// * 🎯使用类似`unify_find(t1, t2).apply_to(c1, c2)`完成「可变性隔离」
309#[derive(Debug, Clone)]
310pub struct Unification {
311    /// 是否能归一
312    pub has_unification: bool,
313    /// 如若归一,归一要换掉的变量映射 @ 词项1
314    pub unify_map_1: VarSubstitution,
315    /// 如若归一,归一要换掉的变量映射 @ 词项2
316    pub unify_map_2: VarSubstitution,
317}
318
319impl Unification {
320    /// 重定向到[`unify_apply`]
321    /// * 🚩返回「是否可归一化」
322    /// * 🚩【2024-07-09 21:48:43】目前作为一个实用的「链式应用方法」用以替代公开的`unifyApply`
323    #[inline]
324    pub fn apply_to(&self, parent1: CompoundTermRefMut, parent2: CompoundTermRefMut) -> bool {
325        unify_apply(parent1, parent2, self)
326    }
327
328    /// 同[`Self::apply_to`],但允许应用在任何词项中
329    /// * 🚩一律返回「是否已归一化」
330    ///   * ⚠️对「单个复合词项」仍可能应用归一化失败:与「应用到哪儿」无关
331    pub fn apply_to_term(&self, parent1: &mut Term, parent2: &mut Term) -> bool {
332        // * 🚩只有俩词项是复合词项时,才进行应用
333        match [parent1.as_compound_mut(), parent2.as_compound_mut()] {
334            [Some(parent1), Some(parent2)] => self.apply_to(parent1, parent2),
335            _ => self.has_unification,
336        }
337    }
338}
339
340/// 使用「统一结果」统一两个复合词项
341/// * ⚠️会修改原有的复合词项
342///
343/// @param parent1 [&m] 要被修改的复合词项1
344/// @param parent2 [&m] 要被修改的复合词项2
345/// @param result  [] 上一个「寻找归一映射」的结果
346fn unify_apply(
347    unified_in_1: CompoundTermRefMut,
348    unified_in_2: CompoundTermRefMut,
349    unification: &Unification,
350) -> bool {
351    let Unification {
352        has_unification,
353        unify_map_1,
354        unify_map_2,
355    } = unification;
356    // 根据「变量替换映射」在两头相应地替换变量
357    apply_unify_one(unified_in_1, unify_map_1);
358    apply_unify_one(unified_in_2, unify_map_2);
359    *has_unification
360}
361
362/// 得出「替代结果」后,将映射表应用到词项上
363fn apply_unify_one(mut unified_in: CompoundTermRefMut, substitution: &VarSubstitution) {
364    // * 🚩映射表非空⇒替换
365    if substitution.is_empty() {
366        return;
367    }
368    // * 🚩应用 & 重命名
369    unified_in.apply_substitute(substitution);
370    // 替换后设置词项
371    // 📄 `((CompoundTerm) compound1).renameVariables();`
372    // 📄 `setConstant(true);` @ `CompoundTerm`
373    // unified_in_1.is_constant = true;
374    unified_in.rename_variables();
375}
376
377/// 🆕将上述方法放在映射表的方法上
378impl VarSubstitution {
379    /// 将映射表的替换模式应用到「复合词项可变引用」上
380    /// * 🎯用于「只需单个替换」的情况
381    ///   * 📄首先出自「条件演绎/归纳」
382    pub fn apply_to(&self, to: CompoundTermRefMut) {
383        apply_unify_one(to, self)
384    }
385
386    /// 尝试将映射表的替换模式应用到任意词项上
387    /// * 🎯用于「先应用,再判断词项类型」的情况
388    #[inline]
389    pub fn apply_to_term(&self, to: &mut Term) {
390        if let Some(to) = to.as_compound_mut() {
391            // 传入(因此可内联)
392            self.apply_to(to);
393        }
394    }
395}
396
397/// 多值输出:寻找「归一替换」的中间结果
398/// ! ❌【2024-07-09 21:14:17】暂且不复刻`unifyApplied`:自成体系但不完整,需要结合`applyUnifyToNew`等「函数式方法」
399pub type AppliedCompounds = [Term; 2];
400
401/// 判断两个复合词项是否「容器相同」
402/// * 🚩只判断有关「怎么包含词项」的信息,不判断具体内容
403fn is_same_kind_compound(t1: CompoundTermRef, t2: CompoundTermRef) -> bool {
404    // * 🚩判断尺寸
405    if t1.size() != t2.size() {
406        return false;
407    }
408    // * 🚩判断「像」的关系位置(占位符位置)
409    if (t1.instanceof_image() && t2.instanceof_image())
410        && t1.get_placeholder_index() != t2.get_placeholder_index()
411    {
412        // 均为像,但占位符位置不同⇒否决
413        return false;
414    }
415    // * 🚩验证通过
416    true
417}
418
419/// 📄OpenNARS `Variable.findSubstitute` 方法
420/// * 💫【2024-04-21 21:40:45】目前尚未能完全理解此处的逻辑
421/// * 📝【2024-04-21 21:50:42】递归查找一个「同位替代」的「变量→词项」映射
422/// * ⚠️【2024-07-10 14:40:06】目前对「可交换词项」沿用OpenNARS的「随机打乱」方案
423///   * ✅能保证「推理器相同,随机运行的结果不因系统时间而变」
424///   * 💫因借用问题,需要每次使用时引入一个「随机种子」作为随机因子
425///
426/// # 📄OpenNARS
427///
428/// To recursively find a substitution that can unify two Terms without changing them
429///
430/// @param type            The type of variable that can be substituted
431/// @param to_be_unified_1 The first term to be unified
432/// @param to_be_unified_2 The second term to be unified
433/// @param map_1  The substitution for term1 formed so far
434/// @param map_2  The substitution for term2 formed so far
435/// @return Whether the unification is possible
436///
437/// # 📄案例
438///
439/// ## 1 from OpenNARS调试 @ 【2024-04-21 21:48:21】
440///
441/// 传入
442///
443/// - type: "$"
444/// - to_be_unified_1: "<$1 --> B>"
445/// - to_be_unified_2: "<C --> B>"
446/// - map_1: HashMap{}
447/// - map_2: HashMap{}
448///
449/// 结果
450///
451/// - 返回值 = true
452/// - map_1: HashMap{ Term"$1" => Term"C" }
453/// - map_2: HashMap{}
454///
455/// ## 2 from OpenNARS调试 @ 【2024-04-21 22:05:46】
456///
457/// 传入
458///
459/// - type: "$"
460/// - to_be_unified_1: "<<A --> $1> ==> <B --> $1>>"
461/// - to_be_unified_2: "<B --> C>"
462/// - map_1: HashMap{}
463/// - map_2: HashMap{}
464///
465/// 结果
466///
467/// - 返回值 = true
468/// - map_1: HashMap{ Term"$1" => Term"C" }
469/// - map_2: HashMap{}
470fn find_unification(
471    var_type: &str,
472    to_be_unified_1: &Term,
473    to_be_unified_2: &Term,
474    map_1: &mut VarSubstitution,
475    map_2: &mut VarSubstitution,
476    shuffle_rng_seed: u64,
477) -> bool {
478    struct UnificationStatus<'s> {
479        /// 统一的变量类型
480        var_type: &'s str,
481        /// 需要统一的俩词项中,最大的变量id
482        max_var_id: usize,
483        // /// 根部词项1
484        // root_1: &'s Term,
485        // /// 根部词项2
486        // root_2: &'s Term,
487    }
488
489    // 构造状态:原先用闭包能捕获的所有【不变】常量
490    let status = UnificationStatus {
491        var_type,
492        max_var_id: Term::maximum_variable_id_multi([to_be_unified_1, to_be_unified_2]),
493        // root_1: to_be_unified_1,
494        // root_2: to_be_unified_2,
495    };
496
497    impl UnificationStatus<'_> {
498        /// 是【确定需要归一化】的变量
499        /// * 📄临时的「共用变量」
500        /// * 📄满足指定标识符的变量词项
501        /// * 🚩【2024-07-09 22:46:21】因为要捕获「变量类型」故需使用闭包
502        /// * 📝【2024-07-09 22:47:34】OpenNARS中似乎只在 `to_be_unified_1` 中出现「共用变量」
503        fn as_correct_var<'t>(&self, t: &'t Term) -> Option<(&'t Term, usize)> {
504            t.as_variable() // 首先是个「变量」词项
505                .filter(|_| t.get_variable_type() == self.var_type) // 类型必须是指定类型
506                .map(|id| (t, id)) // 需要附带词项引用,以便后续拷贝
507        }
508
509        /// 📄OpenNARS `Variable.isCommonVariable` 函数
510        /// * 🚩【2024-08-08 13:22:09】现在不再使用特别的标识符,而是与「变量词项」一视同仁——只判断是否为「根部之外」的变量
511        ///   * id小于原先的「最大id」 ⇒ 一定是「新创的变量」 ⇒ 一定是「共同变量」
512        #[inline]
513        fn is_common_variable(&self, v: &Term) -> bool {
514            v.as_variable().is_some_and(|id| id > self.max_var_id)
515        }
516
517        /// 制作一个由id1 id2共同决定的、在词项自身变量范围之外的id
518        /// * 📌假定:自身的「最大变量id」大于0,即 `max_var_id > 0`
519        ///   * 💭若根部词项没变量,就不会执行「创建共同变量」的操作
520        /// * 📝原理 & 证明
521        ///   * ℹ️前提:`id1 ∈ [0, max_var_id]`、`id2 ∈ [0, max_var_id]`
522        ///   * 📍推论:`(max_var_id + 1) * (1 + id1) ≥ max_var_id + 1 > max_var_id`
523        ///     * ✅满足「在词项自身变量范围之外」
524        ///   * 📍推论:`(max_var_id + 1) * (1 + id1) + id2 ≤ max_id_1 = (max_var_id + 1) * (1 + id1) + max_var_id]`
525        ///     *  `(max_var_id + 1) * (1 + (id1 + 1)) + id2 ≥ max_id_next = (max_var_id + 1) * (1 + (id1 + 1))`
526        ///     *  `max_id_1 = (max_var_id + 1) * (1 + id1) + max_var_id < (max_var_id + 1) * (1 + id1) + (max_var_id + 1) = (max_var_id + 1) * (1 + (id1 + 1)) = max_id_next`
527        fn common_var_id_from(&self, id1: usize, id2: usize) -> usize {
528            (self.max_var_id + 1) * (1 + id1) + id2
529        }
530
531        /// 📄OpenNARS `Variable.makeCommonVariable` 函数
532        /// * 📌制作临时的「共用变量」词项
533        /// * 🎯用于「变量统一」方法
534        /// * 🚩【2024-08-08 13:43:24】现在创建一个新的「域外变量」代替
535        #[inline]
536        fn make_common_variable(&self, id1: usize, id2: usize) -> Term {
537            Term::from_var_similar(self.var_type, self.common_var_id_from(id1, id2))
538        }
539    }
540
541    /// 递归用子函数
542    fn find_unification_sub(
543        status: &UnificationStatus,
544        [to_be_unified_1, to_be_unified_2]: [&Term; 2],
545        [map_1, map_2]: [&mut VarSubstitution; 2],
546        shuffle_rng_seed: u64, // ! 在递归传入时刷新
547    ) -> bool {
548        let is_same_type = to_be_unified_1.is_same_type(to_be_unified_2);
549        match [
550            status.as_correct_var(to_be_unified_1),
551            status.as_correct_var(to_be_unified_2),
552        ] {
553            // * 🚩[$1 x ?] 对应位置是变量
554            // * 🚩[$1 x $2] 若同为变量⇒统一二者(制作一个「共同变量」)
555            [Some((var_1, id1)), Some((var_2, id2))] => {
556                // * 🚩已有替换⇒直接使用已有替换(看子项有无替换) | 递归深入
557                // already mapped
558                if let Some(ref mapped) = map_1.get(var_1).cloned() {
559                    return find_unification_sub(
560                        status,
561                        [mapped, to_be_unified_2],
562                        [map_1, map_2],
563                        shuffle_rng_seed,
564                    );
565                }
566                // not mapped yet
567                // * 🚩生成一个外界输入中不可能的变量词项作为「匿名变量」
568                let common_var = status.make_common_variable(id1, id2);
569                // * 🚩建立映射:var1 -> commonVar @ term1
570                // * 🚩建立映射:var2 -> commonVar @ term2
571                map_1.put(var_1, common_var.clone()); // unify
572                map_2.put(var_2, common_var); // unify
573                true
574            }
575            // * 🚩[$1 x _2] 若并非变量⇒尝试消元划归
576            // * 📝此处意味「两个变量合并成一个变量」 | 后续「重命名变量」会将其消去
577            [Some((var_1, _)), None] => {
578                // * 🚩已有替换⇒直接使用已有替换(看子项有无替换) | 递归深入
579                // already mapped
580                if let Some(ref mapped) = map_1.get(var_1).cloned() {
581                    return find_unification_sub(
582                        status,
583                        [mapped, to_be_unified_2],
584                        [map_1, map_2],
585                        shuffle_rng_seed,
586                    );
587                }
588                // * 🚩建立映射:var1 -> term2 @ term1
589                // elimination
590                map_1.put(var_1, to_be_unified_2.clone());
591                // * 🚩尝试消除「共同变量」
592                if status.is_common_variable(var_1) {
593                    // * 🚩建立映射:var1 -> term2 @ term2
594                    map_2.put(var_1, to_be_unified_2.clone());
595                }
596                true
597            }
598            // * 🚩[? x $2] 对应位置是变量
599            [None, Some((var_2, _))] => {
600                // * 🚩已有替换⇒直接使用已有替换(看子项有无替换) | 递归深入
601                // already mapped
602                if let Some(ref mapped) = map_2.get(var_2).cloned() {
603                    return find_unification_sub(
604                        status,
605                        [to_be_unified_1, mapped],
606                        [map_1, map_2],
607                        shuffle_rng_seed,
608                    );
609                }
610                // not mapped yet
611                // * 🚩[_1 x $2] 若非变量⇒尝试消元划归
612                /*
613                 * 📝【2024-04-22 00:13:19】发生在如下场景:
614                 * <(&&, <A-->C>, <B-->$2>) ==> <C-->$2>>.
615                 * <(&&, <A-->$1>, <B-->D>) ==> <$1-->D>>.
616                 * <(&&, <A-->C>, <B-->D>) ==> <C-->D>>?
617                 * 📌要点:可能两边各有「需要被替换」的地方
618                 */
619                // * 🚩建立映射:var2 -> term1 @ term2
620                // elimination
621                map_2.put(var_2, to_be_unified_1.clone());
622                // * 🚩尝试消除「共同变量」
623                if status.is_common_variable(var_2) {
624                    // * 🚩建立映射:var2 -> term1 @ term2
625                    map_1.put(var_2, to_be_unified_1.clone());
626                }
627                true
628            }
629            // * 🚩均非变量
630            [None, None] => match [to_be_unified_1.as_compound(), to_be_unified_2.as_compound()] {
631                // * 🚩都是复合词项⇒尝试深入
632                [Some(compound_1), Some(compound_2)] if is_same_type => {
633                    // * 🚩替换前提:容器相似(大小相同、像占位符位置相同)
634                    if !is_same_kind_compound(compound_1, compound_2) {
635                        return false;
636                    }
637                    // * 🚩复制词项列表 | 实际上只需拷贝其引用
638                    // * 📝【2024-07-10 14:53:16】随机打乱不影响内部值,也不影响原有排序
639                    let mut list = compound_1.clone_component_refs();
640                    // * 🚩可交换⇒打乱
641                    // * 📝from Wang:需要让算法(对两个词项)的时间复杂度为定值(O(n)而非O(n!))
642                    // * ⚠️全排列的技术难度:多次尝试会修改映射表,需要多次复制才能在检验的同时完成映射替换
643                    //    * 💭【2024-07-10 14:50:09】这意味着较大的计算成本
644                    // * ✨现将`rng`外置:用于在「递归深入」中产生新随机数,增强算法随机性并仍保证宏观确定性
645                    let mut rng = StdRng::seed_from_u64(shuffle_rng_seed);
646                    if compound_1.is_commutative() {
647                        list.shuffle(&mut rng);
648                        // ! 边缘情况:   `<(*, $1, $2) --> [$1, $2]>` => `<(*, A, A) --> [A]>`
649                        // ! 边缘情况:   `<<A --> [$1, $2]> ==> <A --> (*, $1, $2)>>`
650                        // !      +  `<A --> [B, C]>` |- `<A --> (*, B, C)>`✅
651                        // !      +  `<A --> [B]>` |- `<A --> (*, B, B)>`❌
652                    }
653                    // * 🚩按位置逐一遍历
654                    // * ✨【2024-07-10 15:02:10】更新机制:不再是「截断性返回」而是「逐个尝试」
655                    //    * ⚠️与OpenNARS的核心区别:始终遍历所有子项,而非「一个不符就返回」
656                    (list.into_iter().zip(compound_2.components.iter()))
657                        // * 🚩逐个尝试归一化
658                        .map(|(inner1, inner2)| {
659                            find_unification_sub(
660                                status,
661                                [inner1, inner2],
662                                [map_1, map_2],
663                                rng.next_u64(),
664                            )
665                        })
666                        // * 🚩非惰性迭代:只有「所有子项均能归一化」才算「能归一化」
667                        //   * ⚠️不允许改为`all`:此处须强制遍历完所有子项(用`fold`+`BitAnd`)
668                        //   * 📝Rust中`bool | bool`也算合法:非惰性迭代,保证「有副作用的bool函数」正常起效
669                        .fold(true, BitAnd::bitand)
670                }
671                // * 🚩其它情况
672                _ => to_be_unified_1 == to_be_unified_2, // for atomic constant terms
673            },
674        }
675    }
676    // 记录「根部坐标」从根部开始
677    find_unification_sub(
678        &status,
679        [to_be_unified_1, to_be_unified_2],
680        [map_1, map_2],
681        shuffle_rng_seed,
682    )
683}
684
685/// 📄OpenNARS `Variable.hasSubstitute` 方法
686/// * 🚩判断「是否有可能被替换」
687///   * ⚠️反常情况:即便是「没有变量需要替换」,只要「模式有所匹配」就能发生替换
688///
689/// # 📄OpenNARS
690///
691/// Check if two terms can be unified
692///
693///  @param type  The type of variable that can be substituted
694///  @param term1 The first term to be unified
695///  @param term2 The second term to be unified
696///  @return Whether there is a substitution
697fn has_unification(
698    var_type: &str,
699    to_be_unified_1: &Term,
700    to_be_unified_2: &Term,
701    shuffle_rng_seed: u64,
702) -> bool {
703    // 📄 `return findSubstitute(type, term1, term2, new HashMap<Term, Term>(), new HashMap<Term, Term>());`
704    find_unification(
705        var_type,
706        to_be_unified_1,
707        to_be_unified_2,
708        // 创建一个临时的「变量替换映射」
709        &mut VarSubstitution::new(),
710        &mut VarSubstitution::new(),
711        shuffle_rng_seed,
712    )
713}
714/// 🆕【对外接口】查找独立变量归一方式
715pub fn has_unification_i(
716    to_be_unified_1: &Term,
717    to_be_unified_2: &Term,
718    shuffle_rng_seed: u64,
719) -> bool {
720    has_unification(
721        VAR_INDEPENDENT,
722        to_be_unified_1,
723        to_be_unified_2,
724        shuffle_rng_seed,
725    )
726}
727
728/// 🆕【对外接口】查找非独变量归一方式
729pub fn has_unification_d(
730    to_be_unified_1: &Term,
731    to_be_unified_2: &Term,
732    shuffle_rng_seed: u64,
733) -> bool {
734    has_unification(
735        VAR_DEPENDENT,
736        to_be_unified_1,
737        to_be_unified_2,
738        shuffle_rng_seed,
739    )
740}
741
742/// 🆕【对外接口】查找查询变量归一方式
743pub fn has_unification_q(
744    to_be_unified_1: &Term,
745    to_be_unified_2: &Term,
746    shuffle_rng_seed: u64,
747) -> bool {
748    has_unification(
749        VAR_QUERY,
750        to_be_unified_1,
751        to_be_unified_2,
752        shuffle_rng_seed,
753    )
754}
755
756#[cfg(test)]
757mod tests {
758    use super::*;
759    use crate::util::AResult;
760    use crate::{ok, test_term as term};
761    use nar_dev_utils::macro_once;
762    use rand::Rng;
763
764    /// 测试/变量替换
765    #[test]
766    fn apply_substitute() -> AResult {
767        fn test(substitution: &VarSubstitution, term: &str, expected: &str) {
768            let parse = |term: &str| match term.parse() {
769                Ok(term) => term,
770                Err(e) => panic!("{term:?}解析失败: {e}"),
771            };
772            let mut term: Term = parse(term);
773            let expected: Term = parse(expected);
774            let mut compound = term
775                .as_compound_mut()
776                .expect("传入的不是复合词项,无法进行替换");
777            compound.apply_substitute(substitution);
778            assert_eq!(term, expected);
779        }
780        // 映射表
781        let substitution = substitution!(
782            "var_word" => "word"
783            "$1" => "1"
784            "?1" => "(/, A, <lock --> swan>, _, [1])" // 变量⇒复合词项(实际情况不出现)
785            "[#1]" => "<X --> (*, Y, [Z])>" // 复合词项⇒复合词项(实际情况不出现)
786        );
787        let substitution2 = substitution!(
788            "$1" => "(/,$1,_,{L2})" // ! ⚠️注意:嵌套变量
789        );
790        macro_once! {
791            // * 🚩模式:待替换词项, 替换 => 替换后词项
792            macro test(
793                $(
794                    $term_str:expr, $substitution:expr
795                    => $substituted_str:expr
796                )*
797            ) {
798                $(
799                    test(&$substitution, $term_str, $substituted_str);
800                )*
801            }
802            // * 🚩一般复合词项
803            "(&&, A, var_word)", substitution => "(&&, A, word)"
804            "(&&, var_word, A)", substitution => "(&&, word, A)"
805            "(&&, A, var_word, B)", substitution => "(&&, A, word, B)"
806            "(&&, var_word, A, B)", substitution => "(&&, word, A, B)"
807            // * 🚩陈述
808            "<A --> var_word>", substitution => "<A --> word>"
809            "<var_word --> A>", substitution => "<word --> A>"
810            "<A <-> var_word>", substitution => "<A <-> word>"
811            "<var_word <-> A>", substitution => "<word <-> A>"
812            "<A ==> var_word>", substitution => "<A ==> word>"
813            "<var_word ==> A>", substitution => "<word ==> A>"
814            "<A --> $1>", substitution => "<A --> 1>"
815            "<$1 --> A>", substitution => "<1 --> A>"
816            "<$1 --> var_word>", substitution => "<1 --> word>"
817            "<var_word --> $1>", substitution => "<word --> 1>"
818            // * 🚩多层复合词项
819            "<<$1 --> A> ==> <B --> $1>>", substitution => "<<1 --> A> ==> <B --> 1>>"
820            "<<$1 --> var_word> --> (*, var_word, $1)>", substitution => "<<1 --> word> --> (*, word, 1)>"
821            "<<var_word --> A> ==> [#1]>", substitution => "<<word --> A> ==> <X --> (*, Y, [Z])>>"
822            "(--, (&&, (||, (&, (|, (*, ?1), x), x), x), x))", substitution => "(--, (&&, (||, (&, (|, (*, (/, A, <lock --> swan>, _, [1])), x), x), x), x))"
823            // ! from issue #1: unsafe可变引用迭代器的迭代器失效——边迭代边修改,且在修改后又递归深入
824            "<<{O1} --> $1> ==> <{O2} --> $1>>", substitution2 => "<<{O1} --> (/,$1,_,{L2})> ==> <{O2} --> (/,$1,_,{L2})>>"
825        }
826        ok!()
827    }
828
829    /// 测试 / unify_find | Unification::apply_to_term | Unification::apply_to
830    #[test]
831    fn unify() -> AResult {
832        let mut rng = StdRng::from_seed([0; 32]);
833        fn test(
834            mut term1: Term,
835            mut term2: Term,
836            var_type: &str,
837            expected_1: Term,
838            expected_2: Term,
839            shuffle_rng: &mut impl Rng,
840        ) {
841            print!("unify: {term1}, {term2} =={var_type}=> ",);
842            unify_find(var_type, &term1, &term2, shuffle_rng.next_u64())
843                .apply_to_term(&mut term1, &mut term2);
844            println!("{term1}, {term2}");
845            assert_eq!(term1, expected_1);
846            assert_eq!(term2, expected_2);
847        }
848        macro_once! {
849            macro test(
850                $(
851                    $term_str1:expr, $term_str2:expr
852                    => $var_type:expr =>
853                    $substituted_str1:expr, $substituted_str2:expr
854                )*
855            ) {
856                $(
857                    test(
858                        term!($term_str1),
859                        term!($term_str2),
860                        $var_type,
861                        term!($substituted_str1),
862                        term!($substituted_str2),
863                        &mut rng // 用上预置的随机生成器
864                    );
865                )*
866            }
867            // ! 变量替换只会发生在复合词项之中:原子词项不会因此改变自身 //
868            "$1", "A" => "$" => "$1", "A"
869
870            // 各个位置、各个角度(双向)的替换 //
871            // 单侧偏替换
872            "<$1 --> B>", "<A --> B>" => "$" => "<A --> B>", "<A --> B>"
873            "<A --> $1>", "<A --> B>" => "$" => "<A --> B>", "<A --> B>"
874            "<A --> B>", "<$1 --> B>" => "$" => "<A --> B>", "<A --> B>"
875            "<A --> B>", "<A --> $1>" => "$" => "<A --> B>", "<A --> B>"
876            // 双侧偏替换
877            "<$a --> B>", "<A --> $b>" => "$" => "<A --> B>", "<A --> B>"
878            // 单侧全替换
879            "<A --> B>", "<$a --> $b>" => "$" => "<A --> B>", "<A --> B>"
880
881            // 三种变量正常运行 & 一元复合词项 //
882            "(--, $1)", "(--, 1)" => "$" => "(--, 1)", "(--, 1)"
883            "(--, #1)", "(--, 1)" => "#" => "(--, 1)", "(--, 1)"
884            "(--, ?1)", "(--, 1)" => "?" => "(--, 1)", "(--, 1)"
885            // ! ⚠️【2024-04-22 12:32:47】以下示例失效:第二个例子中,OpenNARS在「第一个失配」后,就无心再匹配第二个了
886            // * ✅【2024-07-10 14:59:26】已解决:在「逐个查找替换」的「复合词项递归深入」中,不应「一不符合就截断式返回」
887            //   * 📝每次「查找映射替换」均会改变「替换映射」,而「循环过程中途返回」会影响后续词项的替换
888            //   * 📌【2024-07-10 15:00:45】目前认定:这三种例子均应成功
889            "(*, $i, #d, ?q)", "(*, I, D, Q)" => "$" => "(*, I, #d, ?q)", "(*, I, D, Q)"
890            "(*, $i, #d, ?q)", "(*, I, D, Q)" => "#" => "(*, $i, D, ?q)", "(*, I, D, Q)"
891            "(*, $i, #d, ?q)", "(*, I, D, Q)" => "?" => "(*, $i, #d, Q)", "(*, I, D, Q)"
892
893            // 多元复合词项(有序):按顺序匹配 //
894            "(*, $c, $b, $a)", "(*, (--, C), <B1 --> B2>, A)" => "$" => "(*, (--, C), <B1 --> B2>, A)", "(*, (--, C), <B1 --> B2>, A)"
895               "<(*, <A-->C>, <B-->$2>) ==> <C-->$2>>", "<(*, <A-->$1>, <B-->D>) ==> <$1-->D>>"
896            => "$"
897            => "<(*, <A-->C>, <B-->D>) ==> <C-->D>>", "<(*, <A-->C>, <B-->D>) ==> <C-->D>>"
898
899            // 无序词项 | ⚠️【2024-04-22 12:38:38】对于无序词项的「模式匹配」需要进一步商酌 //
900            "{$c}", "{中心点}" => "$" => "{中心点}", "{中心点}" // 平凡情况
901            "[$c]", "[中心点]" => "$" => "[中心点]", "[中心点]" // 平凡情况
902            // "<$a <-> Bb>", "<Aa <-> Bb>" => "$" => "<Aa <-> Bb>", "<Aa <-> Bb>" // 无需交换顺序,但会被自动排序导致「顺序不一致」
903            // "<Aa <-> $b>", "<Aa <-> Bb>" => "$" => "<Aa <-> Bb>", "<Aa <-> Bb>" // 无需交换顺序,但会被自动排序导致「顺序不一致」
904            // "<$a <-> $b>", "<Aa <-> Bb>" => "$" => "<Aa <-> Bb>", "<Aa <-> Bb>" // 无需交换顺序,但会被自动排序导致「顺序不一致」
905            // "<Bb <-> $a>", "<Aa <-> Bb>" => "$" => "<Aa <-> Bb>", "<Aa <-> Bb>" // 顺序不一致
906            // "<$b <-> Aa>", "<Aa <-> Bb>" => "$" => "<Aa <-> Bb>", "<Aa <-> Bb>" // 顺序不一致
907            // "<$b <-> $a>", "<Aa <-> Bb>" => "$" => "<Aa <-> Bb>", "<Aa <-> Bb>" // 顺序不一致
908            // 平凡情况
909            // "{$1,2,3}", "{0, 2, 3}" => "$" => "{0, 2, 3}", "{0, 2, 3}"
910            // "{1,$2,3}", "{1, 0, 3}" => "$" => "{1, 0, 3}", "{1, 0, 3}"
911            // "{1,2,$3}", "{1, 2, 0}" => "$" => "{1, 2, 0}", "{1, 2, 0}"
912            // 无序集合×复合
913            // "{1, (*, X), (*, $x)}", "{1, (*, Y), (*, X)}" => "$" => "{1, (*, Y), (*, X)}", "{1, (*, Y), (*, X)}"
914        }
915        ok!()
916    }
917
918    #[test]
919    fn rename_variables() -> AResult {
920        fn test(mut term: Term, expected: Term) {
921            // 解析构造词项
922            print!("{term}");
923            // 重命名变量
924            let mut compound = term.as_compound_mut().expect("非复合词项,无法重命名变量");
925            compound.rename_variables();
926            println!("=> {term}");
927            // 比对
928            assert_eq!(term, expected);
929        }
930        macro_once! {
931            // * 🚩模式:词项字符串 ⇒ 预期词项字符串
932            macro test($($term:literal => $expected:expr )*) {
933                $(
934                    test(term!($term), term!($expected));
935                )*
936            }
937            // 简单情况(一层) //
938            // 复合词项
939            "{$A, $B}" => "{$1, $2}"
940            "[$A, $B]" => "[$1, $2]"
941            "(&, $A, $B)" => "(&, $1, $2)"
942            "(|, $A, $B)" => "(|, $1, $2)"
943            "(-, $A, $B)" => "(-, $1, $2)"
944            "(~, $A, $B)" => "(~, $1, $2)"
945            "(*, $A, $B)" => "(*, $1, $2)"
946            r"(/, $R, _)" => r"(/, $1, _)"
947            r"(\, $R, _)" => r"(\, $1, _)"
948            r"(/, $R, _, $A)" => r"(/, $1, _, $2)"
949            r"(\, $R, _, $A)" => r"(\, $1, _, $2)"
950            r"(&&, $A, $B)" => r"(&&, $1, $2)"
951            r"(||, $A, $B)" => r"(||, $1, $2)"
952            r"(--, $A)" => r"(--, $1)"
953            // 陈述
954            "<$A --> $B>" => "<$1 --> $2>"
955            "<$A <-> $B>" => "<$1 <-> $2>"
956            "<$A ==> $B>" => "<$1 ==> $2>"
957            "<$A <=> $B>" => "<$1 <=> $2>"
958            // 复杂情况 //
959            // 不同变量名称,数值不会重复
960            "(*, $A, $B, $C)" => "(*, $1, $2, $3)"
961            "(*, #A, #B, #C)" => "(*, #1, #2, #3)"
962            "(*, ?A, ?B, ?C)" => "(*, ?1, ?2, ?3)"
963            // 不同变量类型,数值不会重复
964            "(*, $A, #A, ?A)" => "(*, $1, #2, ?3)"
965            // 复合词项:递归深入
966            "(*, A, $B, [C, #D])" => "(*, A, $1, [C, #2])"
967            "<(--, (--, (--, (--, (--, (--, (--, (--, A)))))))) --> (/, (-, ?B, C), _, (/, (/, (/, (/, (/, #D, _), _), _), _), _))>" => "<(--, (--, (--, (--, (--, (--, (--, (--, A)))))))) --> (/, (-, ?1, C), _, (/, (/, (/, (/, (/, #2, _), _), _), _), _))>"
968            "<<A --> $B> ==> <#C --> D>>" => "<<A --> $1> ==> <#2 --> D>>"
969            "<<A --> #B> ==> <$B --> D>>" => "<<A --> #1> ==> <$2 --> D>>"
970            // 相同变量,数值相同
971            "<<A --> $B> ==> <$B --> D>>" => "<<A --> $1> ==> <$1 --> D>>"
972            "(*, $A, $A, $A)" => "(*, $1, $1, $1)"
973            "(*, (*, $A, $A, $A), (*, $A, $A, $A), (*, $A, $A, $A))" => "(*, (*, $1, $1, $1), (*, $1, $1, $1), (*, $1, $1, $1))"
974        }
975        ok!()
976    }
977}