use super::SyllogismPosition;
use crate::{
control::*,
entity::*,
inference::{rules::utils::*, *},
language::*,
symbols::*,
};
use nar_dev_utils::{unwrap_or_return, RefCount};
use ReasonDirection::*;
use SyllogismPosition::*;
fn switch_order(compound: CompoundTermRef, index: usize) -> bool {
(compound.instanceof_difference() && index == 1)
|| (compound.instanceof_image() && index > 0)
}
fn switch_by_order<T>(compound: CompoundTermRef, index: usize, [sub, pre]: [T; 2]) -> [T; 2] {
match switch_order(compound, index) {
true => [pre, sub],
false => [sub, pre],
}
}
pub fn structural_compose_both(
compound: CompoundTermRef,
index: usize,
statement: StatementRef,
side: SyllogismPosition,
context: &mut ReasonContextConcept,
) {
let direction = context.reason_direction();
let indicated = side.select_one(statement.sub_pre());
if *compound == *indicated {
return;
}
let [statement_sub, statement_pre] = statement.sub_pre();
let sub_pre = [&statement_sub, &statement_pre];
let mut components = compound.clone_components();
let [term_self_side, other_statement_component] = side.select(sub_pre); if components.contains(other_statement_component) {
return;
}
let cloned_statement_sub_pre = || [statement_sub.clone(), statement_pre.clone()];
let [sub, pre] = match components.contains(term_self_side) {
true => side.select([
compound.inner.clone(),
{
let term_opposite = side.select_another([statement_sub, statement_pre]); components[index] = term_opposite.clone(); unwrap_or_return!(?Term::make_compound_term(compound, components))
},
]),
false => cloned_statement_sub_pre(),
};
let [sub, pre] = switch_by_order(compound, index, [sub, pre]);
let content = unwrap_or_return!(?Term::make_statement(&statement, sub, pre));
let task_truth = context
.current_task()
.get_()
.as_judgement()
.map(TruthValue::from);
let truth = match direction {
Forward => match compound.size() {
2.. => task_truth.map(|task| task.analytic_deduction(context.reasoning_reliance())),
_ => task_truth.map(|task| task.identity()),
},
Backward => None,
};
let budget = match direction {
Forward => context.budget_compound_forward(truth.as_ref(), &content),
Backward => context.budget_compound_backward_weak(&content),
};
context.single_premise_task_structural(content, truth, budget);
}
pub fn structural_decompose_both(
statement: StatementRef,
index: usize,
context: &mut ReasonContextConcept,
) {
let [sub, pre] = statement.sub_pre();
if !sub.is_same_type(pre) {
return;
}
let [sub, pre] = match [sub.as_compound(), pre.as_compound()] {
[Some(sub), Some(pre)] => [sub, pre],
_ => return,
};
let [sub_size, pre_size] = [sub.size(), pre.size()];
if !(sub_size == pre_size && index < sub_size) {
return;
}
let at_index = |compound: CompoundTermRef| compound.component_at(index).unwrap().clone(); let sub_inner = at_index(sub);
let pre_inner = at_index(pre);
let [content_sub, content_pre] = switch_by_order(sub, index, [sub_inner, pre_inner]);
let content = unwrap_or_return!(?Term::make_statement(&statement, content_sub, content_pre));
let direction = context.reason_direction();
let task_is_judgement = context.current_task().get_().is_judgement();
let task_truth = context
.current_task()
.get_()
.as_judgement()
.map(TruthValue::from);
if !(direction == Forward) && !sub.instanceof_product()
&& sub.size() > 1
&& task_is_judgement
{
return;
}
let truth = match direction {
Forward => task_truth.map(|truth| truth.identity()),
Backward => None,
};
let budget = match direction {
Forward => context.budget_compound_forward(truth.as_ref(), &content),
Backward => context.budget_compound_backward(&content),
};
context.single_premise_task_structural(content, truth, budget);
}
pub fn structural_compose_one(
compound: CompoundTermRef,
index: usize,
statement: StatementRef,
context: &mut ReasonContextConcept,
) {
if context.reason_direction() == Backward {
return;
}
let task_truth = TruthValue::from(context.current_task().get_().unwrap_judgement());
let truth_deduction = task_truth.analytic_deduction(context.reasoning_reliance());
let component = unwrap_or_return!(?compound.component_at(index));
let compound = compound.inner.clone();
let [sub, pre] = statement.sub_pre();
let (sub_pre, to_not_ded) = match [*component == *sub, *component == *pre] {
[true, _] => match (compound.identifier(), index) {
(INTERSECTION_EXT_OPERATOR, _)
| (DIFFERENCE_EXT_OPERATOR, 0) => ([compound, pre.clone()], false),
(DIFFERENCE_INT_OPERATOR, 1) => ([compound, pre.clone()], true),
_ => return,
},
[_, true] => match (compound.identifier(), index) {
(INTERSECTION_INT_OPERATOR, _)
| (DIFFERENCE_INT_OPERATOR, 0) => ([sub.clone(), compound], false),
(DIFFERENCE_EXT_OPERATOR, 1) => ([sub.clone(), compound], true),
_ => return,
},
_ => return,
};
let truth = match to_not_ded {
true => truth_deduction.negation(), false => truth_deduction, };
structural_statement(sub_pre, truth, context);
}
pub fn structural_decompose_one(
compound: CompoundTermRef,
index: usize,
statement: StatementRef,
context: &mut ReasonContextConcept,
) {
if context.reason_direction() == Backward {
return;
}
let task_truth = TruthValue::from(context.current_task().get_().unwrap_judgement());
let truth_deduction = task_truth.analytic_deduction(context.reasoning_reliance());
let [sub, pre] = statement.sub_pre();
let component = unwrap_or_return!(?compound.component_at(index)).clone(); let (sub_pre, to_not_ded) = match [*compound == *sub, *compound == *pre] {
[true, _] => match compound.identifier() {
INTERSECTION_INT_OPERATOR => ([component, pre.clone()], false),
SET_EXT_OPERATOR if compound.size() > 1 => (
[
unwrap_or_return!(?Term::make_set_ext(component)),
pre.clone(),
],
false,
),
DIFFERENCE_INT_OPERATOR => ([component, pre.clone()], index == 1),
_ => return,
},
[_, true] => match compound.identifier() {
INTERSECTION_EXT_OPERATOR => ([sub.clone(), component], false),
SET_INT_OPERATOR if compound.size() > 1 => (
[
sub.clone(),
unwrap_or_return!(?Term::make_set_int(component)),
],
false,
),
DIFFERENCE_EXT_OPERATOR => ([sub.clone(), component], index == 1),
_ => return,
},
_ => return,
};
let truth = match to_not_ded {
true => truth_deduction.negation(), false => truth_deduction, };
structural_statement(sub_pre, truth, context);
}
fn structural_statement(
[sub, pre]: [Term; 2],
truth: TruthValue,
context: &mut ReasonContextConcept,
) {
let content = {
let task_ref = context.current_task();
let task_rc = task_ref.get_();
let task_statement = unwrap_or_return!(?task_rc.content().as_statement());
unwrap_or_return!(?Term::make_statement(&task_statement, sub, pre))
};
let budget = context.budget_compound_forward(&truth, &content);
context.single_premise_task_structural(content, Some(truth), budget);
}
pub fn transform_set_relation(
compound: CompoundTermRef,
statement: StatementRef,
side: SyllogismPosition,
context: &mut ReasonContextConcept,
) {
if compound.size() > 1 {
return;
}
if statement.instanceof_inheritance() {
match (compound.identifier(), side) {
(SET_EXT_OPERATOR, Subject) | (SET_INT_OPERATOR, Predicate) => return,
_ => {}
}
}
let [sub, pre] = statement.sub_pre();
let [sub, pre] = [sub.clone(), pre.clone()];
let content = match statement.identifier() {
INHERITANCE_RELATION => Term::make_similarity(sub, pre),
_ => match (compound.identifier(), side) {
(SET_EXT_OPERATOR, Subject) | (SET_INT_OPERATOR, Predicate) => {
Term::make_inheritance(pre, sub)
}
_ => Term::make_inheritance(sub, pre),
},
};
let content = unwrap_or_return!(?content);
let task_truth = context
.current_task()
.get_()
.as_judgement()
.map(TruthValue::from);
let direction = context.reason_direction();
let truth = match direction {
Forward => task_truth.map(|truth| truth.identity()),
Backward => None,
};
let budget = match direction {
Forward => context.budget_compound_forward(truth.as_ref(), &content),
Backward => context.budget_compound_backward(&content),
};
context.single_premise_task_structural(content, truth, budget);
}
#[doc(alias = "structural_compound")]
pub fn structural_junction(
compound: CompoundTermRef,
component: &Term,
compound_from: PremiseSource,
context: &mut ReasonContextConcept,
) {
if !component.is_constant() {
return;
}
let task_truth = context
.current_task()
.get_()
.as_judgement()
.map(TruthValue::from);
let direction = context.reason_direction();
let content = match compound_from {
PremiseSource::Task => component.clone(),
PremiseSource::Belief => compound.inner.clone(),
};
let truth = match direction {
Forward => task_truth.map(|truth| {
match (compound_from == PremiseSource::Task) == compound.instanceof_conjunction() {
true => truth.analytic_deduction(context.reasoning_reliance()),
false => truth
.negation()
.analytic_deduction(context.reasoning_reliance())
.negation(),
}
}),
Backward => None,
};
let budget = match direction {
Forward => context.budget_forward(truth.as_ref()),
Backward => context.budget_compound_backward(&content),
};
context.single_premise_task_structural(content, truth, budget);
}
pub fn transform_negation(
negation: CompoundTerm,
compound_from: PremiseSource,
context: &mut ReasonContextConcept,
) {
fn unwrap_negation(negation: CompoundTerm) -> Term {
let (_, components) = negation.unwrap();
components.into_vec().pop().unwrap() }
let content = match compound_from {
PremiseSource::Task => unwrap_negation(negation),
PremiseSource::Belief => negation.into(),
};
let direction = context.reason_direction();
let task_truth = context
.current_task()
.get_()
.as_judgement()
.map(TruthValue::from);
let truth = match direction {
Forward => task_truth.map(|truth| truth.negation()),
Backward => None,
};
let budget = match direction {
Forward => context.budget_compound_forward(truth.as_ref(), &content),
Backward => context.budget_compound_backward(&content),
};
context.single_premise_task_structural(content, truth, budget);
}
pub fn contraposition(
statement: Statement,
main_sentence_from: PremiseSource,
context: &mut ReasonContextConcept,
) {
let direction = context.reason_direction();
let (main_sentence_truth, main_sentence_punctuation) = match main_sentence_from {
PremiseSource::Task => {
let task_rc = context.current_task();
let task = &*task_rc.get_();
(
task.as_judgement().map(TruthValue::from),
task.punctuation(),
)
}
PremiseSource::Belief => {
let belief = context.current_belief().unwrap();
(
belief.as_judgement().map(TruthValue::from),
belief.punctuation(),
)
}
};
let (sub, copula, pre) = statement.unwrap();
let content = unwrap_or_return!(
?Term::make_statement_relation(
copula, unwrap_or_return!(?Term::make_negation(pre)), unwrap_or_return!(?Term::make_negation(sub)),
)
);
let truth = match direction {
Forward => match content.instanceof_implication() {
true => main_sentence_truth.map(|truth| truth.contraposition()),
false => main_sentence_truth.map(|truth| truth.identity()),
},
Backward => None,
};
let budget = match direction {
Forward => context.budget_compound_forward(truth.as_ref(), &content),
Backward => match content.instanceof_implication() {
true => context.budget_compound_backward_weak(&content),
false => context.budget_compound_backward(&content),
},
};
context.single_premise_task_full(content, main_sentence_punctuation, truth, budget);
}
#[cfg(test)]
mod tests {
use crate::expectation_tests;
expectation_tests! {
compose_both_int_ext: {
"
nse <A --> B>.
nse (&,A,C).
cyc 10
"
=> OUT "<(&,A,C) --> (&,B,C)>" in outputs
}
compose_both_int_ext_answer: {
"
nse <A --> B>.
nse <(&,A,C) --> (&,B,C)>?
cyc 20
"
=> ANSWER "<(&,A,C) --> (&,B,C)>" in outputs
}
compose_both_int_int: {
"
nse <A --> B>.
nse (|,A,C).
cyc 10
"
=> OUT "<(|,A,C) --> (|,B,C)>" in outputs
}
compose_both_int_int_answer: {
"
nse <A --> B>.
nse <(|,A,C) --> (|,B,C)>?
cyc 20
"
=> ANSWER "<(|,A,C) --> (|,B,C)>" in outputs
}
compose_both_diff_ext: {
"
nse <A --> B>.
nse (-,A,C).
cyc 10
"
=> OUT "<(-,A,C) --> (-,B,C)>" in outputs
}
compose_both_diff_ext_answer: {
"
nse <A --> B>.
nse <(-,A,C) --> (-,B,C)>?
cyc 20
"
=> ANSWER "<(-,A,C) --> (-,B,C)>" in outputs
}
compose_both_diff_ext_rev: {
"
nse <A --> B>.
nse (-,C,A).
cyc 10
"
=> OUT "<(-,C,B) --> (-,C,A)>" in outputs
}
compose_both_diff_ext_rev_answer: {
"
nse <A --> B>.
nse <(-,C,B) --> (-,C,A)>?
cyc 20
"
=> ANSWER "<(-,C,B) --> (-,C,A)>" in outputs
}
compose_both_diff_int: {
"
nse <A --> B>.
nse (~,A,C).
cyc 10
"
=> OUT "<(~,A,C) --> (~,B,C)>" in outputs
}
compose_both_diff_int_answer: {
"
nse <A --> B>.
nse <(~,A,C) --> (~,B,C)>?
cyc 20
"
=> ANSWER "<(~,A,C) --> (~,B,C)>" in outputs
}
compose_both_diff_int_rev: {
"
nse <A --> B>.
nse (~,C,A).
cyc 10
"
=> OUT "<(~,C,B) --> (~,C,A)>" in outputs
}
compose_both_diff_int_rev_answer: {
"
nse <A --> B>.
nse <(~,C,B) --> (~,C,A)>?
cyc 20
"
=> ANSWER "<(~,C,B) --> (~,C,A)>" in outputs
}
compose_both_product: {
"
nse <A --> B>.
nse (*,C,A).
cyc 10
"
=> OUT "<(*,C,A) --> (*,C,B)>" in outputs
}
compose_both_product_answer: {
"
nse <A --> B>.
nse <(*,C,A) --> (*,C,B)>?
cyc 20
"
=> ANSWER "<(*,C,A) --> (*,C,B)>" in outputs
}
compose_both_image_ext_1: { "
nse <R --> S>.
nse (/,R,_,A).
cyc 10
"
=> OUT "<(/,R,_,A) --> (/,S,_,A)>" in outputs
}
compose_both_image_ext_1_answer: { "
nse <R --> S>.
nse <(/,R,_,A) --> (/,S,_,A)>?
cyc 20
"
=> ANSWER "<(/,R,_,A) --> (/,S,_,A)>" in outputs
}
compose_both_image_ext_2: {
"
nse <A --> B>.
nse (/,R,_,A).
cyc 10
"
=> OUT "<(/,R,_,B) --> (/,R,_,A)>" in outputs
}
compose_both_image_ext_2_answer: {
"
nse <A --> B>.
nse <(/,R,_,B) --> (/,R,_,A)>?
cyc 20
"
=> ANSWER "<(/,R,_,B) --> (/,R,_,A)>" in outputs
}
compose_both_image_int_1: { r"
nse <R --> S>.
nse (\,R,_,A).
cyc 10
"
=> OUT r"<(\,R,_,A) --> (\,S,_,A)>" in outputs
}
compose_both_image_int_1_answer: { r"
nse <R --> S>.
nse <(\,R,_,A) --> (\,S,_,A)>?
cyc 20
"
=> ANSWER r"<(\,R,_,A) --> (\,S,_,A)>" in outputs
}
compose_both_image_int_2: {
r"
nse <A --> B>.
nse (\,R,_,A).
cyc 10
"
=> OUT r"<(\,R,_,B) --> (\,R,_,A)>" in outputs
}
compose_both_image_int_2_answer: {
r"
nse <A --> B>.
nse <(\,R,_,B) --> (\,R,_,A)>?
cyc 20
"
=> ANSWER r"<(\,R,_,B) --> (\,R,_,A)>" in outputs
}
decompose_both_int_ext: {
"
nse <(&,A,C) --> (&,B,C)>.
cyc 20
"
=> OUT "<A --> B>" in outputs
}
decompose_both_int_ext_answer: {
"
nse <(&,A,C) --> (&,B,C)>.
nse <A --> B>?
cyc 30
"
=> ANSWER "<A --> B>" in outputs
}
decompose_both_int_int: {
"
nse <(|,A,C) --> (|,B,C)>.
cyc 20
"
=> OUT "<A --> B>" in outputs
}
decompose_both_int_int_answer: {
"
nse <(|,A,C) --> (|,B,C)>.
nse <A --> B>?
cyc 30
"
=> ANSWER "<A --> B>" in outputs
}
decompose_both_diff_ext: {
"
nse <(-,A,C) --> (-,B,C)>.
cyc 20
"
=> OUT "<A --> B>" in outputs
}
decompose_both_diff_ext_answer: {
"
nse <(-,A,C) --> (-,B,C)>.
nse <A --> B>?
cyc 30
"
=> ANSWER "<A --> B>" in outputs
}
decompose_both_diff_ext_rev: {
"
nse <(-,C,B) --> (-,C,A)>.
cyc 20
"
=> OUT "<A --> B>" in outputs
}
decompose_both_diff_ext_rev_answer: {
"
nse <(-,C,B) --> (-,C,A)>.
nse <A --> B>?
cyc 30
"
=> ANSWER "<A --> B>" in outputs
}
decompose_both_diff_int: {
"
nse <(~,A,C) --> (~,B,C)>.
cyc 30
" => OUT "<A --> B>" in outputs
}
decompose_both_diff_int_answer: {
"
nse <(~,A,C) --> (~,B,C)>.
nse <A --> B>?
cyc 30
"
=> ANSWER "<A --> B>" in outputs
}
decompose_both_diff_int_rev: {
"
nse <(~,C,B) --> (~,C,A)>.
cyc 20
"
=> OUT "<A --> B>" in outputs
}
decompose_both_diff_int_rev_answer: {
"
nse <(~,C,B) --> (~,C,A)>.
nse <A --> B>?
cyc 30
"
=> ANSWER "<A --> B>" in outputs
}
decompose_both_product: {
"
nse <(*,C,A) --> (*,C,B)>.
cyc 20
"
=> OUT "<A --> B>" in outputs
}
decompose_both_product_answer: {
"
nse <(*,C,A) --> (*,C,B)>.
nse <A --> B>?
cyc 30
"
=> ANSWER "<A --> B>" in outputs
}
decompose_both_image_ext_1: { "
nse <(/,R,_,A) --> (/,S,_,A)>.
cyc 20
"
=> OUT "<R --> S>" in outputs
}
decompose_both_image_ext_1_answer: { "
nse <(/,R,_,A) --> (/,S,_,A)>.
nse <R --> S>?
cyc 30
"
=> ANSWER "<R --> S>" in outputs
}
decompose_both_image_ext_2: {
"
nse <(/,R,_,B) --> (/,R,_,A)>.
cyc 20
"
=> OUT "<A --> B>" in outputs
}
decompose_both_image_ext_2_answer: {
"
nse <(/,R,_,B) --> (/,R,_,A)>.
nse <A --> B>?
cyc 30
"
=> ANSWER "<A --> B>" in outputs
}
decompose_both_image_int_1: { r"
nse <(\,R,_,A) --> (\,S,_,A)>.
cyc 20
"
=> OUT r"<R --> S>" in outputs
}
decompose_both_image_int_1_answer: { r"
nse <(\,R,_,A) --> (\,S,_,A)>.
nse <R --> S>?
cyc 30
"
=> ANSWER r"<R --> S>" in outputs
}
decompose_both_image_int_2: {
r"
nse <(\,R,_,B) --> (\,R,_,A)>.
cyc 20
"
=> OUT r"<A --> B>" in outputs
}
decompose_both_image_int_2_answer: {
r"
nse <(\,R,_,B) --> (\,R,_,A)>.
nse <A --> B>?
cyc 30
"
=> ANSWER r"<A --> B>" in outputs
}
compose_one_int_ext: {
"
nse <A --> B>.
nse (&,A,C)?
cyc 10
"
=> OUT "<(&,A,C) --> B>" in outputs
}
compose_one_int_int: {
"
nse <A --> B>.
nse (|,B,C)?
cyc 10
"
=> OUT "<A --> (|,B,C)>" in outputs
}
compose_one_diff_ext: {
"
nse <A --> B>.
nse (-,A,C)?
cyc 10
"
=> OUT "<(-,A,C) --> B>" in outputs
}
compose_one_diff_int: {
"
nse <A --> B>.
nse (~,B,C)?
cyc 10
"
=> OUT "<A --> (~,B,C)>" in outputs
}
compose_one_diff_ext_neg: {
"
nse <A --> B>. %0%
nse (-,C,B)?
cyc 10
"
=> OUT "<A --> (-,C,B)>" in outputs
}
compose_one_diff_int_neg: {
"
nse <A --> B>. %0%
nse (~,C,A)?
cyc 10
"
=> OUT "<(~,C,A) --> B>" in outputs
}
decompose_one_int_ext: { "
nse <A --> (&,B,C)>.
cyc 10
"
=> OUT "<A --> B>" in outputs
}
decompose_one_int_int: { "
nse <(|,A,C) --> B>.
cyc 10
"
=> OUT "<A --> B>" in outputs
}
decompose_one_set_ext_1: {
"
nse <{A,C} --> B>.
cyc 10
"
=> OUT "<{A} --> B>" in outputs
}
decompose_one_set_ext_2: {
"
nse <{A,C} --> B>.
cyc 10
"
=> OUT "<{C} --> B>" in outputs
}
decompose_one_set_int_1: {
"
nse <A --> [B,C]>.
cyc 10
"
=> OUT "<A --> [B]>" in outputs
}
decompose_one_set_int_2: {
"
nse <A --> [B,C]>.
cyc 10
"
=> OUT "<A --> [C]>" in outputs
}
decompose_one_diff_ext: {
"
nse <A --> (-,B,C)>.
cyc 10
"
=> OUT "<A --> B>" in outputs
}
decompose_one_diff_ext_neg: {
"
nse <A --> (-,B,C)>. %0%
cyc 10
"
=> OUT "<A --> C>" in outputs
}
decompose_one_diff_int: {
"
nse <(~,A,C) --> B>.
cyc 10
"
=> OUT "<A --> B>" in outputs
}
decompose_one_diff_int_neg: {
"
nse <(~,A,C) --> B>. %0%
cyc 10
"
=> OUT "<C --> B>" in outputs
}
transform_set_ext: {
"
nse <A --> {B}>.
cyc 10
"
=> OUT "<A <-> {B}>" in outputs
}
transform_set_int: {
"
nse <[A] --> B>.
cyc 10
"
=> OUT "<B <-> [A]>" in outputs }
transform_set_ext_sub: {
"
nse <A <-> {B}>.
cyc 10
"
=> OUT "<A --> {B}>" in outputs
}
transform_set_ext_pre: { "
nse <A <-> {B}>.
nse <{B} --> A>?
cyc 10
"
=> OUT "<{B} --> A>" in outputs
}
transform_set_int_sub: {
"
nse <[A] <-> B>.
cyc 10
"
=> OUT "<[A] --> B>" in outputs
}
transform_set_int_pre: { "
nse <[A] <-> B>.
nse <B --> [A]>?
cyc 10
"
=> OUT "<B --> [A]>" in outputs
}
structural_conjunction: {
"
nse (&&, A, B).
cyc 10
"
=> OUT "A" in outputs
}
structural_disjunction: { "
nse (||, A, B).
cyc 10
"
=> OUT "A" in outputs
}
structural_conjunction_backward: { "
nse A.
nse (&&, A, B)?
cyc 10
"
=> ANSWER "(&&, A, B)" in outputs
}
structural_disjunction_backward: {
"
nse A.
nse (||, A, B)?
cyc 10
"
=> ANSWER "(||, A, B)" in outputs
}
transform_negation_forward: {
"
nse (--,A).
cyc 10
"
=> OUT "A" in outputs
}
transform_negation_backward: {
"
nse A. %0%
nse (--,A)?
cyc 10
"
=> ANSWER "(--,A)" in outputs
}
contraposition: {
"
nse <(--,A) ==> B>.
cyc 10
"
=> OUT "<(--,B) ==> A>" in outputs
}
}
}