narust-158 0.3.1

A Rust reimplementation of OpenNARS 1.5.8
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
//! 🎯复刻OpenNARS `nars.inference.BudgetFunctions`

use crate::{
    debug_assert_matches,
    entity::*,
    global::*,
    inference::{Budget, Truth},
    language::Term,
};

/// 预算函数
/// * 🚩【2024-05-03 14:48:13】现在仍依照OpenNARS原意「直接创建新值」
///   * 📝本身复制值也没多大性能损耗
///   * 📌「直接创建新值」会更方便后续调用
///     * 📄减少无谓的`.clone()`
/// * ❌【2024-06-24 16:30:56】不能使用`impl Budget + Sized`
///   * 📝这会碰上生命周期问题:不能保证返回的值一定不包含对传入参数的借用
///   * 📄首次错误出现位置:[`crate::inference::BudgetInference::merge_from`]
///
/// * ⚠️【2024-06-20 19:56:05】此处仅存储「纯函数」:不在其中修改传入量的函数
pub trait BudgetFunctions: Budget {
    /* ----------------------- Belief evaluation ----------------------- */

    /// 模拟`BudgetFunctions.truthToQuality`
    ///
    /// # 📄OpenNARS
    ///
    /// Determine the quality of a judgement by its truth value alone
    ///
    /// Mainly decided by confidence, though binary judgement is also preferred
    ///
    /// @param t The truth value of a judgement
    /// @return The quality of the judgement, according to truth value only
    fn truth_to_quality(truth: &impl Truth) -> ShortFloat {
        // * 🚩现在从更原始(无需反复转换)的`_float`函数中来
        ShortFloat::from_float(Self::truth_to_quality_float(truth))
    }
    fn truth_to_quality_float(truth: &impl Truth) -> Float {
        // * 🚩真值⇒质量:期望与「0.75(1-期望)」的最大值
        // * 📝函数:max(c * (f - 0.5) + 0.5, 0.375 - 0.75 * c * (f - 0.5))
        // * 📍最小值:当exp=3/7时,全局最小值为3/7(max的两端相等)
        // * 🔑max(x,y) = (x+y+|x-y|)/2
        let exp = truth.expectation();
        exp.max((1.0 - exp) * 0.75)
    }

    /// 模拟`BudgetFunctions.rankBelief`
    /// * 🚩🆕【2024-05-03 21:46:17】仅传入「语句」中的「真值」与「时间戳长度」,而非「语句」本身
    ///   * 🚩`judgement.getTruth()` => `truth`
    ///   * 🚩`judgement.getStamp().length()` => `stamp_len`
    /// * 📝在使用该函数返回值的地方,仅为「比较大小」
    ///   * 但[`ShortFloat`]已经实现了[`Ord`]并且需要[`UtilityFunctions::or`]
    ///
    /// # 📄OpenNARS
    ///
    /// Determine the rank of a judgement by its quality and originality (stamp length), called from Concept
    ///
    /// @param judgement The judgement to be ranked
    /// @return The rank of the judgement, according to truth value only
    fn rank_belief(judgement: &impl Judgement) -> Float {
        // * 🚩两个指标:信度 + 原创性(时间戳长度)
        // * 📝与信度正相关,与「时间戳长度」负相关;二者有一个好,那就整体好
        let confidence = judgement.confidence();
        let originality =
            ShortFloat::from_float(1.0 / (judgement.evidence_length() as Float + 1.0));
        (confidence | originality).to_float()
    }

    /* ----- Functions used both in direct and indirect processing of tasks ----- */

    /// 概念的「总体优先级」
    /// * 📝用于概念的「激活」函数上
    /// Recalculate the quality of the concept [to be refined to show extension/intension balance]
    fn concept_total_quality(concept: &Concept) -> ShortFloat {
        // * 🚩计算所有词项链的「平均优先级」
        let link_priority = concept.term_links_average_priority();
        let link_priority = ShortFloat::from_float(link_priority);
        // * 🚩词项复杂性指标:自身复杂性倒数
        let term_complexity_factor = 1.0 / concept.term().complexity() as Float;
        let term_complexity_factor = ShortFloat::from_float(term_complexity_factor);
        // * 🚩总体:任意更大就行;结构简单的基本总是最好的;词项越复杂,质量下限越低
        // * 📝计算方法:扩展逻辑或
        link_priority | term_complexity_factor
    }

    /// # 📄OpenNARS
    ///
    /// Evaluate the quality of the judgment as a solution to a problem
    /// * ⚠️这个返回值必须在0~1之间
    fn solution_quality(query: &impl Sentence, solution: &impl Judgement) -> ShortFloat {
        // * 🚩根据「一般疑问 | 特殊疑问/目标」拆解
        // * 📝一般疑问 ⇒ 解の信度
        // * 📝特殊疑问 ⇒ 解の期望 / 解の复杂度
        let has_query_var = query.content().contain_var_q();
        match has_query_var {
            // * 🚩【特殊疑问/目标】 "what" question or goal
            true => ShortFloat::from_float(
                solution.expectation() / solution.content().complexity() as Float,
            ),
            // * 🚩【一般疑问】 "yes/no" question
            false => solution.confidence(),
        }
    }

    /// 模拟`BudgetFunctions.solutionEval`
    /// * ✅【2024-06-23 01:37:36】目前已按照改版OpenNARS设置
    ///
    /// # 📄OpenNARS
    ///
    /// Evaluate the quality of a belief as a solution to a problem, then reward
    /// the belief and de-prioritize the problem
    ///
    /// @param problem  The problem (question or goal) to be solved
    /// @param solution The belief as solution
    /// @param task     The task to be immediately processed, or null for continued
    ///                 process
    /// @return The budget for the new task which is the belief activated, if
    ///         necessary
    fn solution_eval(
        problem: &impl Question,
        solution: &impl Judgement,
        question_task_budget: &impl Budget,
    ) -> BudgetValue {
        /* 📄OpenNARS改版:
        final float newP = or(questionTaskBudget.getPriority(), solutionQuality(problem, solution));
        final float newD = questionTaskBudget.getDurability();
        final float newQ = truthToQuality(solution);
        return new BudgetValue(newP, newD, newQ); */
        // * ️📝新优先级 = 任务优先级 | 解决方案质量
        let p = question_task_budget.priority() | Self::solution_quality(problem, solution);
        // * 📝新耐久度 = 任务耐久度
        let d = question_task_budget.durability();
        // * ️📝新质量 = 解决方案の真值→质量
        let q = Self::truth_to_quality(solution);
        // 返回
        BudgetValue::new(p, d, q)
    }

    /// 统一的「修正规则」预算函数
    /// * 🚩依照改版OpenNARS,从旧稿中重整
    /// * ✅完全脱离「推理上下文」仅有纯粹的「真值/预算值」计算
    /// * ✅其中对「任务链可空性=信念链可空性」做断言:`feedBackToLinks == current_links_budget.is_some()`
    fn revise(
        new_belief_truth: &impl Truth, // from task
        old_belief_truth: &impl Truth, // from belief
        revised_truth: &impl Truth,
        current_task_budget: &impl Budget,
        current_links_budget: Option<(&impl Budget, &impl Budget)>,
    ) -> ReviseResult {
        // * 🚩计算落差 | t = task, b = belief
        let dif_to_new_task =
            ShortFloat::from_float(revised_truth.expectation_abs_dif(new_belief_truth));
        let dif_to_old_belief =
            ShortFloat::from_float(revised_truth.expectation_abs_dif(old_belief_truth));
        // * 🚩若有:反馈到 [任务链, 信念链]
        let new_links_budget = current_links_budget.map(|(t_budget, b_budget)| {
            [
                // * 📝当前任务链 降低预算:
                // * * p = link & !difT
                // * * d = link & !difT
                // * * q = link
                BudgetValue::new(
                    t_budget.priority() & !dif_to_new_task,
                    t_budget.durability() & !dif_to_new_task,
                    t_budget.quality(),
                ),
                // * 📝当前信念链 降低预算:
                // * * p = link & !difB
                // * * d = link & !difB
                // * * q = link
                BudgetValue::new(
                    b_budget.priority() & !dif_to_old_belief,
                    b_budget.durability() & !dif_to_old_belief,
                    b_budget.quality(),
                ),
            ]
        });
        // * 🚩用落差降低优先级、耐久度
        // * 📝当前任务 降低预算:
        // * * p = task & !difT
        // * * d = task & !difT
        // * * q = task
        let new_task_budget = BudgetValue::new(
            current_task_budget.priority() & !dif_to_new_task,
            current_task_budget.durability() | !dif_to_new_task,
            current_task_budget.quality(),
        );
        // * 🚩用更新后的值计算新差 | ❓此时是否可能向下溢出?
        // * 📝新差 = 修正后信念.信度 - max(新信念.信度, 旧信念.信度)
        let dif = revised_truth.confidence()
            - old_belief_truth
                .confidence()
                .max(old_belief_truth.confidence());
        // * 🚩计算新预算值
        // * 📝优先级 = 差 | 当前任务
        // * 📝耐久度 = (差 + 当前任务) / 2
        // * 📝质量 = 新真值→质量
        let new_budget = BudgetValue::new(
            dif | current_task_budget.priority(),
            ShortFloat::arithmetical_average([dif, current_task_budget.durability()]),
            Self::truth_to_quality(revised_truth),
        );
        // 返回
        ReviseResult {
            new_budget,
            new_task_budget,
            new_links_budget,
        }
    }

    /// 模拟`BudgetFunctions.update`
    ///
    /// # 📄OpenNARS
    ///
    /// Update a belief
    ///
    /// @param task   The task containing new belief
    /// @param bTruth Truth value of the previous belief
    /// @return Budget value of the updating task
    fn update(
        task_truth: &impl Truth,
        task_budget: &mut Self,
        b_truth: &impl Truth,
    ) -> BudgetValue {
        /* 📄OpenNARS源码:
        Truth tTruth = task.getSentence().getTruth();
        float dif = tTruth.getExpDifAbs(bTruth);
        float priority = or(dif, task.getPriority());
        float durability = aveAri(dif, task.getDurability());
        float quality = truthToQuality(bTruth);
        return new BudgetValue(priority, durability, quality); */
        // * 🚩计算落差
        let dif = ShortFloat::from_float(task_truth.expectation_abs_dif(b_truth));
        // * 🚩根据落差计算预算值
        // * 📝优先级 = 落差 | 任务
        // * 📝耐久度 = (落差 + 任务) / 2
        // * 📝质量 = 信念真值→质量
        let priority = dif | task_budget.priority();
        let durability = ShortFloat::arithmetical_average([dif, task_budget.durability()]);
        let quality = Self::truth_to_quality(task_truth);
        BudgetValue::new(priority, durability, quality)
    }

    /* ----------------------- Links ----------------------- */

    /// 模拟`BudgetFunctions.distributeAmongLinks`
    ///
    /// # 📄OpenNARS
    /// Distribute the budget of a task among the links to it
    ///
    /// @param b The original budget
    /// @param n Number of links
    /// @return Budget value for each link
    fn distribute_among_links(&self, n: usize) -> BudgetValue {
        /* 📄OpenNARS源码:
        float priority = (float) (b.getPriority() / Math.sqrt(n));
        return new BudgetValue(priority, b.getDurability(), b.getQuality()); */
        // * 📝优先级 = 原 / √链接数
        // * 📝耐久度 = 原
        // * 📝质量 = 原
        let priority = self.priority().to_float() / (n as Float).sqrt();
        BudgetValue::new(
            ShortFloat::from_float(priority),
            self.durability(),
            self.quality(),
        )
    }

    /* ----------------------- Concept ----------------------- */

    /// 模拟`BudgetFunctions.activate`
    /// * 🚩【2024-05-02 20:55:40】虽然涉及「概念」,但实际上只用到了「概念作为预算值的部分」
    /// * 📌【2024-05-02 20:56:11】目前要求「概念」一方使用同样的「短浮点」
    /// * 🚩【2024-05-03 14:58:03】此处是「修改」语义
    /// * ⚠️参数顺序和OpenNARS仍然保持相同:`self`指代其中的`concept`参数
    ///
    /// # 📄OpenNARS
    ///
    /// Activate a concept by an incoming TaskLink
    ///
    /// @param concept The concept
    /// @param budget  The budget for the new item
    #[doc(alias = "activate")]
    fn activate_to_concept(&self, concept: &Concept) -> BudgetValue {
        // * 🚩直接计算
        let [cp, cd] = [concept.priority(), concept.durability()];
        let [bp, bd] = [self.priority(), self.durability()];
        // * 📝优先级 = 概念 | 参考
        // * 📝耐久度 = (概念 + 参考) / 2
        // * 📝质量 = 综合所有词项链后的新「质量」
        BudgetValue::new(
            cp | bp,
            ShortFloat::arithmetical_average([cd, bd]),
            Self::concept_total_quality(concept),
        )
    }

    /* ---------------- Bag functions, on all Items ------------------- */

    /// 模拟`BudgetFunctions.forget`
    /// * 🚩【2024-05-03 14:57:06】此处是「修改」语义,而非「创建新值」语义
    /// * 🚩【2024-06-24 16:13:41】现在跟从改版OpenNARS,转为「创建新值」语义
    ///
    /// # 📄OpenNARS
    ///
    /// Decrease Priority after an item is used, called in Bag
    ///
    /// After a constant time, p should become d*p.
    ///
    /// Since in this period, the item is accessed c*p times, each time p-q should multiple d^(1/(c*p)).
    ///
    /// The intuitive meaning of the parameter "forgetRate" is:
    /// after this number of times of access, priority 1 will become d, it is a system parameter adjustable in run time.
    ///
    /// - @param budget            The previous budget value
    /// - @param forgetRate        The budget for the new item
    /// - @param relativeThreshold The relative threshold of the bag
    fn forget(&self, forget_rate: Float, relative_threshold: Float) -> Float {
        /* 📄OpenNARS源码:
        double quality = budget.getQuality() * relativeThreshold; // re-scaled quality
        double p = budget.getPriority() - quality; // priority above quality
        if (p > 0) {
            quality += p * Math.pow(budget.getDurability(), 1.0 / (forgetRate * p));
        } // priority Durability
        budget.setPriority((float) quality); */
        let [p, d, q] = self.pdq_float();
        // * 🚩先放缩「质量」
        let scaled_q = q * relative_threshold;
        // * 🚩计算优先级和「放缩后质量」的差
        let dif_p_q = p - scaled_q;
        // * 🚩计算新的优先级
        match dif_p_q > 0.0 {
            // * 🚩差值 > 0 | 衰减
            true => scaled_q + dif_p_q * d.powf(1.0 / (forget_rate * dif_p_q)),
            // * 🚩差值 < 0 | 恒定
            false => scaled_q,
        }
    }

    /// 模拟`BudgetValue.merge`,亦与`BudgetFunctions.merge`相同
    /// * 📝【2024-05-03 14:55:29】虽然现在「预算函数」以「直接创建新值」为主范式,
    ///   * 但在用到该函数的`merge`方法上,仍然是「修改」语义——需要可变引用
    /// * 🚩【2024-06-24 16:15:22】现在跟从改版OpenNARS,直接创建新值
    ///
    /// # 📄OpenNARS
    ///
    /// ## `BudgetValue`
    ///
    /// Merge one BudgetValue into another
    ///
    /// ## `BudgetFunctions`
    ///
    /// Merge an item into another one in a bag, when the two are identical
    /// except in budget values
    ///
    /// @param baseValue   The budget value to be modified
    /// @param adjustValue The budget doing the adjusting
    fn merge(&self, other: &impl Budget) -> BudgetValue {
        let p = self.priority().max(other.priority());
        let d = self.durability().max(other.durability());
        let q = self.quality().max(other.quality());
        BudgetValue::new(p, d, q)
    }

    /// Forward inference result and adjustment
    fn forward(truth: Option<&impl Truth>, content: Option<&Term>) -> BudgetInferenceParameters {
        // * 📝真值转质量,用不到词项
        debug_assert_matches!((truth, content), (Some(..), None));
        let inference_quality = truth.map_or(ShortFloat::ONE, Self::truth_to_quality);
        let complexity = 1;
        BudgetInferenceParameters {
            inference_quality, // 默认值:1
            complexity,
        }
    }

    /// Backward inference result and adjustment, stronger case
    fn backward(truth: Option<&impl Truth>, content: Option<&Term>) -> BudgetInferenceParameters {
        // * 📝真值转质量,用不到词项
        debug_assert_matches!((truth, content), (Some(..), None));
        let inference_quality = truth.map_or(ShortFloat::ONE, Self::truth_to_quality);
        let complexity = 1;
        BudgetInferenceParameters {
            inference_quality, // 默认值:1
            complexity,
        }
    }

    /// Backward inference result and adjustment, weaker case
    fn backward_weak(
        truth: Option<&impl Truth>,
        content: Option<&Term>,
    ) -> BudgetInferenceParameters {
        // * 📝真值转质量,用不到词项
        debug_assert_matches!((truth, content), (Some(..), None));
        let inference_quality =
            ShortFloat::W2C1() * truth.map_or(ShortFloat::ONE, Self::truth_to_quality);
        let complexity = 1;
        BudgetInferenceParameters {
            inference_quality, // 默认值:1
            complexity,
        }
    }

    /// Forward inference with CompoundTerm conclusion
    fn compound_forward(
        truth: Option<&impl Truth>,
        content: Option<&Term>,
    ) -> BudgetInferenceParameters {
        // * 📝真值转质量,用到词项的复杂度
        debug_assert_matches!((truth, content), (Some(..), Some(..)));
        let inference_quality = truth.map_or(ShortFloat::ONE, Self::truth_to_quality);
        let complexity = content.map_or(1, Term::complexity);
        BudgetInferenceParameters {
            inference_quality, // 默认值:1
            complexity,        // 默认值:1
        }
    }

    /// Backward inference with CompoundTerm conclusion, stronger case
    fn compound_backward(
        truth: Option<&impl Truth>,
        content: Option<&Term>,
    ) -> BudgetInferenceParameters {
        // * 📝用到词项的复杂度,用不到真值
        debug_assert_matches!((truth, content), (None, Some(..)));
        let inference_quality = ShortFloat::ONE;
        let complexity = content.map_or(1, Term::complexity);
        BudgetInferenceParameters {
            inference_quality,
            complexity, // 默认值:1
        }
    }

    /// Backward inference with CompoundTerm conclusion, weaker case
    fn compound_backward_weak(
        truth: Option<&impl Truth>,
        content: Option<&Term>,
    ) -> BudgetInferenceParameters {
        // * 📝用到词项的复杂度,用不到真值
        debug_assert_matches!((truth, content), (None, Some(..)));
        let inference_quality = ShortFloat::W2C1();
        let complexity = content.map_or(1, Term::complexity);
        BudgetInferenceParameters {
            inference_quality,
            complexity, // 默认值:1
        }
    }

    /// 从「预算推理函数 枚举」到「预算推理函数指针」
    fn budget_inference_function_from<T: Truth>(
        function_enum: BudgetInferenceFunction,
    ) -> BudgetInferenceF<T> {
        use BudgetInferenceFunction::*;
        match function_enum {
            Forward => Self::forward,
            Backward => Self::backward,
            BackwardWeak => Self::backward_weak,
            CompoundForward => Self::compound_forward,
            CompoundBackward => Self::compound_backward,
            CompoundBackwardWeak => Self::compound_backward_weak,
        }
    }
    /// Common processing for all inference step
    ///
    /// @param inferenceQuality [] Quality of the inference
    /// @param complexity       [] Syntactic complexity of the conclusion
    /// @return [] Budget of the conclusion task
    fn budget_inference<T: Truth>(
        function: BudgetInferenceFunction,
        truth: Option<&T>,
        content: Option<&Term>,
        task_link_budget: &impl Budget,
        belief_link_budget: Option<&impl Budget>,
        target_activation: ShortFloat,
    ) -> BudgetInferenceResult {
        // * 🚩应用函数,提取其中的「推理优先级」和「复杂度」
        let budget_inference_function = Self::budget_inference_function_from::<T>(function);
        let BudgetInferenceParameters {
            inference_quality,
            complexity,
        } = budget_inference_function(truth, content);
        // * 🚩获取「任务链」和「信念链」的优先级(默认0)与耐久度(默认1)
        // * 📝p = self ?? 0
        // * 📝d = self ?? 1
        let [t_link_p, t_link_d] = [task_link_budget.priority(), task_link_budget.durability()];
        let [b_link_p, b_link_d] = match belief_link_budget {
            // * 🚩有信念链⇒取其值
            Some(budget) => [budget.priority(), budget.durability()],
            // * 🚩无信念链⇒默认为[0, 1]
            None => [ShortFloat::ZERO, ShortFloat::ONE],
        };
        // * 🚩更新预算
        // * 📝p = task | belief
        // * 📝d = (task / complexity) & belief
        // * 📝q = inferenceQuality / complexity
        let [p, d, q] = [
            t_link_p | b_link_p,
            (t_link_d / complexity) & b_link_d,
            inference_quality / complexity,
        ];
        // * 🚩有信念链⇒更新信念链预算值
        // * 🚩【2024-06-20 17:11:30】现在返回一个新的预算值
        let new_belief_link_budget = belief_link_budget.map(|b_link_budget| {
            // * 📌此处仅在「概念推理」中出现:能使用可空值处理
            // * 📝p = belief | quality | targetActivation
            // * 📝d = belief | quality
            // * 📝q = belief
            // * 🚩提升优先级
            let [b_link_p, b_link_d, b_link_q] = b_link_budget.pdq();
            BudgetValue::new(b_link_p | q | target_activation, b_link_d | q, b_link_q)
        });
        // * 🚩返回预算值
        BudgetInferenceResult {
            new_budget: BudgetValue::new(p, d, q),
            new_belief_link_budget,
        }
    }
}

/// 修正规则的预算推理结果
/// * 🎯用于[`BudgetFunctions::revise`]
pub struct ReviseResult {
    /// 新预算
    pub new_budget: BudgetValue,
    /// 新任务预算
    pub new_task_budget: BudgetValue,
    /// [新任务链预算, 新信念链预算](可空)
    /// * 📌左边任务链,右边信念链
    /// * 🎯统一二者的可空性 from `feedbackToLinks`
    pub new_links_budget: Option<[BudgetValue; 2]>,
}

mod budget_inference_functions {
    use super::*;

    pub struct BudgetInferenceParameters {
        /// * 🚩目前只用于「预算推理」的被除数(除以复杂度)上
        pub inference_quality: ShortFloat,
        pub complexity: usize,
    }

    /// 统一的「预算值参数计算函数」指针类型(带泛型)
    pub type BudgetInferenceF<T> = fn(Option<&T>, Option<&Term>) -> BudgetInferenceParameters;

    /// 所有可用的预算值函数
    /// * 🎯统一呈现「在推理过程中计算预算值」的「预算超参数」
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    pub enum BudgetInferenceFunction {
        /// 前向推理
        Forward,
        /// 反向强推理
        Backward,
        /// 反向弱推理
        BackwardWeak,
        /// 复合前向推理
        CompoundForward,
        /// 复合反向强推理
        CompoundBackward,
        /// 复合反向弱推理
        CompoundBackwardWeak,
    }

    pub struct BudgetInferenceResult {
        /// 预算推理算出的新预算
        pub new_budget: BudgetValue,
        /// 预算推理算出的「新信念链预算」
        pub new_belief_link_budget: Option<BudgetValue>,
    }
}
pub use budget_inference_functions::*;

/// 自动实现「预算函数」
/// * 🎯直接在「预算值」上加功能
impl<B: Budget> BudgetFunctions for B {}

/// TODO: 单元测试
#[cfg(test)]
mod tests {}