use crate::{
control::{ContextDerivation, ReasonContext, ReasonContextDirect},
entity::{BudgetValue, Concept, Judgement, Punctuation, RCTask, Sentence, ShortFloat, Stamp},
inference::{
try_solution_apply_context, try_solution_apply_task, try_solution_calculate, Budget,
BudgetFunctions, BudgetInference, Evidential, TruthFunctions,
},
language::Term,
util::ToDisplayAndBrief,
};
use nar_dev_utils::RefCount;
pub fn process_direct(context: &mut ReasonContextDirect) {
let task_punctuation = context.current_task.get_().punctuation();
use Punctuation::*;
match task_punctuation {
Judgement => process_judgement(context),
Question => process_question(context),
}
}
fn process_judgement(context: &mut ReasonContextDirect) {
let this = context.current_concept();
let task = &context.current_task;
debug_assert!(task.get_().is_judgement());
let judgment = task.get_().as_judgement().unwrap().clone();
let old_belief = evaluation(&judgment, this.beliefs(), BudgetValue::solution_quality);
if let Some((old_belief, ..)) = old_belief {
if judgment.evidential_eq(old_belief) {
let task = task.get_(); if let Some(parent) = task.parent_task() {
if parent.get_().is_judgement() {
drop(task); let mut mut_task = context.current_task_mut();
mut_task.mut_().set_priority(ShortFloat::ZERO);
} return;
}
}
else if judgment.revisable_to(old_belief) {
let has_overlap = judgment.evidential_overlap(old_belief);
if !has_overlap {
revision_direct(context, judgment.clone(), old_belief.clone());
}
}
}
let budget_threshold = context.parameters().budget_threshold; if context
.current_task .get_()
.budget_above_threshold(budget_threshold)
{
let this = context.core.current_concept_mut();
let mut results = vec![];
for existed_question in this.questions() {
let result =
try_solution_calculate(&judgment, &existed_question.get_(), budget_threshold);
results.push((existed_question.clone_(), result));
}
for (mut existed_question, result) in results {
try_solution_apply_task(&result, &mut existed_question.mut_(), &judgment);
try_solution_apply_context(result, &judgment, context);
}
let this = context.current_concept_mut();
let overflowed_belief = this.add_belief(judgment);
if let Some(overflowed_belief) = overflowed_belief {
let message = format!(
"!!! Overflowed Belief in '{}': {}",
this.term(),
overflowed_belief.to_display_long()
);
context.report_comment(message);
}
}
}
fn process_question(context: &mut ReasonContextDirect) {
let budget_threshold = context.parameters().budget_threshold;
let mut question_task = context.current_task.clone_(); let question_task_ref = question_task.get_();
debug_assert!(
question_task_ref.is_question(),
"要处理的必须是「问题」:{question_task:?}"
);
let this = context.current_concept();
let existed_question = find_existed_question(this, question_task_ref.content());
let is_new_question = existed_question.is_none();
let query = existed_question.unwrap_or(&question_task).clone_(); let new_answer = evaluation(
&*query.get_(),
this.beliefs(),
BudgetValue::solution_quality,
);
if let Some((answer, ..)) = new_answer {
let answer = answer.clone(); let result = try_solution_calculate(&answer, &question_task.get_(), budget_threshold);
drop(question_task_ref);
try_solution_apply_task(&result, &mut question_task.mut_(), &answer);
try_solution_apply_context(result, &answer, context);
} else {
drop(question_task_ref);
}
if is_new_question {
let overflowed_question = context.current_concept_mut().add_question(question_task);
if let Some(task) = overflowed_question {
context.report_comment(format!(
"!!! Overflowed Question Task: {}",
task.get_().to_display_long()
));
}
}
else {
context.report_comment(format!(
"!!! Skipped Non-new Question Task: {}",
question_task.get_().to_display_long()
));
}
}
fn revision_direct(
context: &mut ReasonContextDirect,
new_belief: impl Judgement,
old_belief: impl Judgement,
) {
let new_content = new_belief.clone_content();
let new_truth = new_belief.revision(&old_belief);
let new_budget = BudgetValue::revise_direct(
&new_belief,
&old_belief,
&new_truth,
&mut *context.current_task.mut_(),
);
let new_stamp = Stamp::from_merge_unchecked(
&new_belief,
&old_belief,
context.time(),
context.max_evidence_base_length(),
);
context.double_premise_task_revision(new_content, new_truth, new_budget, new_stamp);
}
fn find_existed_question<'c>(concept: &'c Concept, task_content: &Term) -> Option<&'c RCTask> {
concept
.questions()
.find(
|question| question.get_().content() == task_content,
)
}
fn evaluation<'a, S, J>(
query: &S,
list: impl IntoIterator<Item = &'a J>,
solution_quality: fn(&S, &J) -> ShortFloat,
) -> Option<(&'a J, ShortFloat)>
where
S: Sentence,
J: Judgement + 'a,
{
let mut current_best = ShortFloat::default();
let mut candidate = None;
for judgement in list {
let belief_quality = solution_quality(query, judgement);
if belief_quality > current_best {
current_best = belief_quality;
candidate = Some(judgement);
}
}
candidate.map(|solution| (solution, current_best))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
control::Reasoner,
expect_narsese_term,
inference::{tools::*, InferenceEngine},
};
use navm::output::Output;
const ENGINE: InferenceEngine = InferenceEngine::new(
process_direct,
InferenceEngine::ECHO.transform_f(),
InferenceEngine::ECHO.matching_f(),
InferenceEngine::ECHO.reason_f(),
);
fn reasoner() -> Reasoner {
create_reasoner_from_engine(ENGINE)
}
#[test]
fn direct_answer_question() {
let mut vm = reasoner();
vm.input_fetch_print_expect(
"
nse Sentence.
nse Sentence?
cyc 2
",
expect_narsese_term!(ANSWER "Sentence" in outputs),
);
}
#[test]
fn answer_question_multiple_time() {
let mut vm = reasoner();
let has_answer = |answer: &Output| matches!(answer, Output::ANSWER { .. });
vm.input_fetch_print_expect(
"
vol 100
nse Sentence.
nse Sentence?
cyc 2
",
has_answer,
);
vm.input_fetch_print_expect(
"
nse Sentence?
cyc 2
",
has_answer,
);
vm.input_fetch_print_expect(
"
nse Sentence?
cyc 2
",
has_answer,
);
vm.input_fetch_print_expect(
"
nse Sentence?
cyc 2
",
has_answer,
);
}
#[test]
fn stability() {
let mut vm = reasoner();
for i in 0..0x10 {
let _outs = vm.input_cmds_and_fetch_out(&format!(
"
nse <A{i} --> B>.
nse <A{i} --> B>?
rem cyc 10
"
));
}
vm.input_cmds("cyc 1000");
}
}