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}