/*
* LocalRules.java
*
* Copyright (C) 2008 Pei Wang
*
* This file is part of Open-NARS.
*
* Open-NARS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Open-NARS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the abduction warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Open-NARS. If not, see <http://www.gnu.org/licenses/>.
*/
package nars.inference;
import java.util.Arrays;
import nars.config.Parameters;
import nars.util.Events.Answer;
import nars.util.Events.Unsolved;
import nars.storage.Memory;
import nars.control.DerivationContext;
import nars.entity.BudgetValue;
import nars.entity.Concept;
import nars.entity.Sentence;
import nars.entity.Stamp;
import nars.entity.Task;
import nars.entity.TruthValue;
import static nars.inference.TemporalRules.matchingOrder;
import static nars.inference.TemporalRules.reverseOrder;
import nars.io.Output;
import nars.io.Symbols;
import nars.language.CompoundTerm;
import nars.language.Equivalence;
import nars.language.Inheritance;
import nars.language.Similarity;
import nars.language.Statement;
import nars.language.Term;
import nars.language.Variables;
import static nars.inference.TemporalRules.matchingOrder;
/**
* Directly process a task by a oldBelief, with only two Terms in both. In
* matching, the new task is compared with an existing direct Task in that
* Concept, to carry out:
* <p>
* revision: between judgments or goals on non-overlapping evidence;
* satisfy: between a Sentence and a Question/Goal;
* merge: between items of the same type and stamp;
* conversion: between different inheritance relations.
*/
public class LocalRules {
/* -------------------- same contents -------------------- */
/**
* The task and belief have the same content
* <p>
* called in RuleTables.reason
*
* @param task The task
* @param belief The belief
* @param memory Reference to the memory
*/
public static boolean match(final Task task, final Sentence belief, final DerivationContext nal) {
Sentence sentence = task.sentence;
if (sentence.isJudgment()) {
if (revisible(sentence, belief)) {
return revision(sentence, belief, true, nal);
}
} else {
if (matchingOrder(sentence, belief)) {
Term[] u = new Term[] { sentence.term, belief.term };
if (Variables.unify(Symbols.VAR_QUERY, u)) {
trySolution(belief, task, nal, true);
}
}
}
return false;
}
/**
* Check whether two sentences can be used in revision
*
* @param s1 The first sentence
* @param s2 The second sentence
* @return If revision is possible between the two sentences
*/
public static boolean revisible(final Sentence s1, final Sentence s2) {
if(!s1.isEternal() && !s2.isEternal() && Math.abs(s1.getOccurenceTime() - s2.getOccurenceTime()) > Parameters.REVISION_MAX_OCCURRENCE_DISTANCE) {
return false;
}
return (s1.getRevisible() &&
matchingOrder(s1.getTemporalOrder(), s2.getTemporalOrder()) &&
CompoundTerm.cloneDeepReplaceIntervals(s1.term).equals(CompoundTerm.cloneDeepReplaceIntervals(s2.term)) &&
!Stamp.baseOverlap(s1.stamp.evidentialBase, s2.stamp.evidentialBase));
}
/**
* Belief revision
* <p>
* called from Concept.reviseTable and match
*
* @param newBelief The new belief in task
* @param oldBelief The previous belief with the same content
* @param feedbackToLinks Whether to send feedback to the links
* @param memory Reference to the memory
*/
public static boolean revision(final Sentence newBelief, final Sentence oldBelief, final boolean feedbackToLinks, final DerivationContext nal) {
if (newBelief.term==null) return false;
newBelief.stamp.alreadyAnticipatedNegConfirmation = oldBelief.stamp.alreadyAnticipatedNegConfirmation;
TruthValue newTruth = newBelief.truth;
TruthValue oldTruth = oldBelief.truth;
TruthValue truth = TruthFunctions.revision(newTruth, oldTruth);
BudgetValue budget = BudgetFunctions.revise(newTruth, oldTruth, truth, feedbackToLinks, nal);
if (budget.aboveThreshold()) {
if (nal.doublePremiseTaskRevised(newBelief.term, truth, budget)) {
//nal.mem().logic.BELIEF_REVISION.commit();
return true;
}
}
return false;
}
/**
* Check if a Sentence provide a better answer to a Question or Goal
*
* @param belief The proposed answer
* @param task The task to be processed
* @param memory Reference to the memory
*/
public static boolean trySolution(Sentence belief, final Task task, final DerivationContext nal, boolean report) {
Sentence problem = task.sentence;
Memory memory = nal.mem();
Sentence oldBest = task.getBestSolution();
if (oldBest != null) {
boolean rateByConfidence = oldBest.getTerm().equals(belief.getTerm());
float newQ = TemporalRules.solutionQuality(rateByConfidence, task, belief, memory);
float oldQ = TemporalRules.solutionQuality(rateByConfidence, task, oldBest, memory);
if (oldQ >= newQ) {
if (problem.isGoal()) {
memory.emotion.adjustHappy(oldQ, task.getPriority(),nal);
}
//System.out.println("Unsolved: Solution of lesser quality");
memory.emit(Unsolved.class, task, belief, "Lower quality");
return false;
}
}
/* //TODO evaluate why this was necessary at all!!
Term content = belief.term;
if (content.hasVarIndep()) {
Term u[] = new Term[] { content, problem.term };
boolean unified = Variables.unify(Symbols.VAR_INDEPENDENT, u);
content = u[0];
belief = belief.clone(content);
if ((!unified) || (content == null)) {
throw new RuntimeException("Unification invalid: " + Arrays.toString(u));
}
}*/
task.setBestSolution(memory,belief);
//memory.logic.SOLUTION_BEST.commit(task.getPriority());
BudgetValue budget = TemporalRules.solutionEval(task, belief, task, nal);
if ((budget != null) && budget.aboveThreshold()) {
//Solution Activated
if(task.sentence.punctuation==Symbols.QUESTION_MARK || task.sentence.punctuation==Symbols.QUEST_MARK) {
if(task.isInput() && report) { //only show input tasks as solutions
memory.emit(Answer.class, task, belief);
} else {
memory.emit(Output.class, task, belief); //solution to quests and questions can be always showed
}
} else {
memory.emit(Output.class, task, belief); //goal things only show silence related
}
/*memory.output(task);
//only questions and quests get here because else output is spammed
if(task.sentence.isQuestion() || task.sentence.isQuest()) {
memory.emit(Solved.class, task, belief);
} else {
memory.emit(Output.class, task, belief);
}*/
nal.addTask(nal.getCurrentTask(), budget, belief, task.getParentBelief());
return true;
}
else {
memory.emit(Unsolved.class, task, belief, "Insufficient budget");
}
return false;
}
/* -------------------- same terms, difference relations -------------------- */
/**
* The task and belief match reversely
*
* @param nal Reference to the memory
*/
public static void matchReverse(final DerivationContext nal) {
Task task = nal.getCurrentTask();
Sentence belief = nal.getCurrentBelief();
Sentence sentence = task.sentence;
if (matchingOrder(sentence.getTemporalOrder(), reverseOrder(belief.getTemporalOrder()))) {
if (sentence.isJudgment()) {
inferToSym(sentence, belief, nal);
} else {
conversion(nal);
}
}
}
/**
* Inheritance/Implication matches Similarity/Equivalence
*
* @param asym A Inheritance/Implication sentence
* @param sym A Similarity/Equivalence sentence
* @param figure location of the shared term
* @param nal Reference to the memory
*/
public static void matchAsymSym(final Sentence asym, final Sentence sym, int figure, final DerivationContext nal) {
if (nal.getCurrentTask().sentence.isJudgment()) {
inferToAsym(asym, sym, nal);
} else {
convertRelation(nal);
}
}
/* -------------------- two-premise inference rules -------------------- */
/**
* {<S --> P>, <P --> S} |- <S <-> p> Produce Similarity/Equivalence from a
* pair of reversed Inheritance/Implication
*
* @param judgment1 The first premise
* @param judgment2 The second premise
* @param nal Reference to the memory
*/
private static void inferToSym(Sentence judgment1, Sentence judgment2, DerivationContext nal) {
Statement s1 = (Statement) judgment1.term;
Term t1 = s1.getSubject();
Term t2 = s1.getPredicate();
Term content;
if (s1 instanceof Inheritance) {
content = Similarity.make(t1, t2);
} else {
content = Equivalence.make(t1, t2, s1.getTemporalOrder());
}
TruthValue value1 = judgment1.truth;
TruthValue value2 = judgment2.truth;
TruthValue truth = TruthFunctions.intersection(value1, value2);
BudgetValue budget = BudgetFunctions.forward(truth, nal);
nal.doublePremiseTask(content, truth, budget,false, false); //(allow overlap) but not needed here, isn't detachment
}
/**
* {<S <-> P>, <P --> S>} |- <S --> P> Produce an Inheritance/Implication
* from a Similarity/Equivalence and a reversed Inheritance/Implication
*
* @param asym The asymmetric premise
* @param sym The symmetric premise
* @param nal Reference to the memory
*/
private static void inferToAsym(Sentence asym, Sentence sym, DerivationContext nal) {
Statement statement = (Statement) asym.term;
Term sub = statement.getPredicate();
Term pre = statement.getSubject();
Statement content = Statement.make(statement, sub, pre, statement.getTemporalOrder());
if (content == null) return;
TruthValue truth = TruthFunctions.reduceConjunction(sym.truth, asym.truth);
BudgetValue budget = BudgetFunctions.forward(truth, nal);
nal.doublePremiseTask(content, truth, budget,false, false);
}
/* -------------------- one-premise inference rules -------------------- */
/**
* {<P --> S>} |- <S --> P> Produce an Inheritance/Implication from a
* reversed Inheritance/Implication
*
* @param nal Reference to the memory
*/
private static void conversion(final DerivationContext nal) {
TruthValue truth = TruthFunctions.conversion(nal.getCurrentBelief().truth);
BudgetValue budget = BudgetFunctions.forward(truth, nal);
convertedJudgment(truth, budget, nal);
}
/**
* {<S --> P>} |- <S <-> P> {<S <-> P>} |- <S --> P> Switch between
* Inheritance/Implication and Similarity/Equivalence
*
* @param nal Reference to the memory
*/
private static void convertRelation(final DerivationContext nal) {
TruthValue truth = nal.getCurrentBelief().truth;
if (((CompoundTerm) nal.getCurrentTask().getTerm()).isCommutative()) {
truth = TruthFunctions.abduction(truth, 1.0f);
} else {
truth = TruthFunctions.deduction(truth, 1.0f);
}
BudgetValue budget = BudgetFunctions.forward(truth, nal);
convertedJudgment(truth, budget, nal);
}
/**
* Convert judgment into different relation
* <p>
* called in MatchingRules
*
* @param budget The budget value of the new task
* @param truth The truth value of the new task
* @param nal Reference to the memory
*/
private static void convertedJudgment(final TruthValue newTruth, final BudgetValue newBudget, final DerivationContext nal) {
Statement content = (Statement) nal.getCurrentTask().getTerm();
Statement beliefContent = (Statement) nal.getCurrentBelief().term;
int order = TemporalRules.reverseOrder(beliefContent.getTemporalOrder());
final Term subjT = content.getSubject();
final Term predT = content.getPredicate();
final Term subjB = beliefContent.getSubject();
final Term predB = beliefContent.getPredicate();
Term otherTerm;
if (subjT.hasVarQuery()) {
otherTerm = (predT.equals(subjB)) ? predB : subjB;
content = Statement.make(content, otherTerm, predT, order);
}
if (predT.hasVarQuery()) {
otherTerm = (subjT.equals(subjB)) ? predB : subjB;
content = Statement.make(content, subjT, otherTerm, order);
}
if (content == null) return;
nal.singlePremiseTask(content, Symbols.JUDGMENT_MARK, newTruth, newBudget);
}
}