/* * Copyright (C) 2014 peiwang * * This program 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package nars.inference; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import nars.storage.Memory; import nars.config.Parameters; import nars.control.DerivationContext; import nars.control.TemporalInferenceControl; import nars.entity.BudgetValue; import nars.entity.Sentence; import nars.entity.Stamp; import nars.entity.Task; import nars.entity.TaskLink; import nars.entity.TermLink; import nars.entity.TruthValue; import nars.io.Symbols; import nars.language.CompoundTerm; import nars.language.Conjunction; import nars.language.Equivalence; import nars.language.Implication; import nars.language.Inheritance; import nars.language.Interval; import nars.language.Product; import nars.language.Similarity; import nars.language.Statement; import nars.language.Term; import nars.language.Terms; import nars.language.Variable; import nars.operator.Operation; /** * * @author peiwang */ public class TemporalRules { public static final int ORDER_NONE = 2; public static final int ORDER_FORWARD = 1; public static final int ORDER_CONCURRENT = 0; public static final int ORDER_BACKWARD = -1; public static final int ORDER_INVALID = -2; public final static int reverseOrder(final int order) { if (order == ORDER_NONE) { return ORDER_NONE; } else { return -order; } } public final static boolean matchingOrder(final Sentence a, final Sentence b) { return matchingOrder(a.getTemporalOrder(), b.getTemporalOrder()); } public final static boolean matchingOrder(final int order1, final int order2) { return (order1 == order2) || (order1 == ORDER_NONE) || (order2 == ORDER_NONE); } public final static int dedExeOrder(final int order1, final int order2) { int order = ORDER_INVALID; if ((order1 == order2) || (order2 == TemporalRules.ORDER_NONE)) { order = order1; } else if ((order1 == TemporalRules.ORDER_NONE) || (order1 == TemporalRules.ORDER_CONCURRENT)) { order = order2; } else if (order2 == TemporalRules.ORDER_CONCURRENT) { order = order1; } return order; } public final static int abdIndComOrder(final int order1, final int order2) { int order = ORDER_INVALID; if (order2 == TemporalRules.ORDER_NONE) { order = order1; } else if ((order1 == TemporalRules.ORDER_NONE) || (order1 == TemporalRules.ORDER_CONCURRENT)) { order = reverseOrder(order2); } else if ((order2 == TemporalRules.ORDER_CONCURRENT) || (order1 == -order2)) { order = order1; } return order; } public final static int analogyOrder(final int order1, final int order2, final int figure) { int order = ORDER_INVALID; if ((order2 == TemporalRules.ORDER_NONE) || (order2 == TemporalRules.ORDER_CONCURRENT)) { order = order1; } else if ((order1 == TemporalRules.ORDER_NONE) || (order1 == TemporalRules.ORDER_CONCURRENT)) { order = (figure < 20) ? order2 : reverseOrder(order2); } else if (order1 == order2) { if ((figure == 12) || (figure == 21)) { order = order1; } } else if ((order1 == -order2)) { if ((figure == 11) || (figure == 22)) { order = order1; } } return order; } public static final int resemblanceOrder(final int order1, final int order2, final int figure) { int order = ORDER_INVALID; int order1Reverse = reverseOrder(order1); if ((order2 == TemporalRules.ORDER_NONE)) { order = (figure > 20) ? order1 : order1Reverse; // switch when 11 or 12 } else if ((order1 == TemporalRules.ORDER_NONE) || (order1 == TemporalRules.ORDER_CONCURRENT)) { order = (figure % 10 == 1) ? order2 : reverseOrder(order2); // switch when 12 or 22 } else if (order2 == TemporalRules.ORDER_CONCURRENT) { order = (figure > 20) ? order1 : order1Reverse; // switch when 11 or 12 } else if (order1 == order2) { order = (figure == 21) ? order1 : -order1; } return order; } public static final int composeOrder(final int order1, final int order2) { int order = ORDER_INVALID; if (order2 == TemporalRules.ORDER_NONE) { order = order1; } else if (order1 == TemporalRules.ORDER_NONE) { order = order2; } else if (order1 == order2) { order = order1; } return order; } /** whether temporal induction can generate a task by avoiding producing wrong terms; only one temporal operator is allowed */ public final static boolean tooMuchTemporalStatements(final Term t) { return (t == null) || (t.containedTemporalRelations() > 1); } /** whether a term can be used in temoralInduction(,,) */ protected static boolean termForTemporalInduction(final Term t) { return (t instanceof Inheritance) || (t instanceof Similarity); } public static List<Task> temporalInduction(final Sentence s1, final Sentence s2, final nars.control.DerivationContext nal, boolean SucceedingEventsInduction) { if ((s1.truth==null) || (s2.truth==null) || s1.punctuation!=Symbols.JUDGMENT_MARK || s2.punctuation!=Symbols.JUDGMENT_MARK || s1.isEternal() || s2.isEternal()) return Collections.EMPTY_LIST; Term t1 = s1.term; Term t2 = s2.term; boolean deriveSequenceOnly = Statement.invalidStatement(t1, t2, true); if (Statement.invalidStatement(t1, t2, false)) return Collections.EMPTY_LIST; Term t11=null; Term t22=null; if (!deriveSequenceOnly && termForTemporalInduction(t1) && termForTemporalInduction(t2)) { Statement ss1 = (Statement) t1; Statement ss2 = (Statement) t2; Variable var1 = new Variable("$0"); Variable var2 = new Variable("$1"); /* if (ss1.getSubject().equals(ss2.getSubject())) { t11 = Statement.make(ss1, var1, ss1.getPredicate()); t22 = Statement.make(ss2, var2, ss2.getPredicate()); } else if (ss1.getPredicate().equals(ss2.getPredicate())) { t11 = Statement.make(ss1, ss1.getSubject(), var1); t22 = Statement.make(ss2, ss2.getSubject(), var2); }*/ if(ss2.containsTermRecursively(ss1.getSubject())) { HashMap<Term,Term> subs=new HashMap(); subs.put(ss1.getSubject(), var1); if(ss2.containsTermRecursively(ss1.getPredicate())) { subs.put(ss1.getPredicate(), var2); } t11=ss1.applySubstitute(subs); t22=ss2.applySubstitute(subs); } if(ss1.containsTermRecursively(ss2.getSubject())) { HashMap<Term,Term> subs=new HashMap(); subs.put(ss2.getSubject(), var1); if(ss1.containsTermRecursively(ss2.getPredicate())) { subs.put(ss2.getPredicate(), var2); } t11=ss1.applySubstitute(subs); t22=ss2.applySubstitute(subs); } //allow also temporal induction on operator arguments: if(ss2 instanceof Operation ^ ss1 instanceof Operation) { if(ss2 instanceof Operation && !(ss2.getSubject() instanceof Variable)) {//it is an operation, let's look if one of the arguments is same as the subject of the other term Term comp=ss1.getSubject(); Term ss2_term = ((Operation)ss2).getSubject(); boolean applicableVariableType = !(comp instanceof Variable && ((Variable)comp).hasVarIndep()); if(ss2_term instanceof Product) { Product ss2_prod=(Product) ss2_term; if(applicableVariableType && Terms.contains(ss2_prod.term, comp)) { //only if there is one and it isnt a variable already Term[] ars = ss2_prod.cloneTermsReplacing(comp, var1); t11 = Statement.make(ss1, var1, ss1.getPredicate()); Operation op=(Operation) Operation.make( new Product(ars), ss2.getPredicate() ); t22 = op; } } } if(ss1 instanceof Operation && !(ss1.getSubject() instanceof Variable)) {//it is an operation, let's look if one of the arguments is same as the subject of the other term Term comp=ss2.getSubject(); Term ss1_term = ((Operation)ss1).getSubject(); boolean applicableVariableType = !(comp instanceof Variable && ((Variable)comp).hasVarIndep()); if(ss1_term instanceof Product) { Product ss1_prod=(Product) ss1_term; if(applicableVariableType && Terms.contains(ss1_prod.term, comp)) { //only if there is one and it isnt a variable already Term[] ars = ss1_prod.cloneTermsReplacing(comp, var1); t22 = Statement.make(ss2, var1, ss2.getPredicate()); Operation op=(Operation) Operation.make( new Product(ars), ss1.getPredicate() ); t11 = op; } } } } } int durationCycles = Parameters.DURATION; long time1 = s1.getOccurenceTime(); long time2 = s2.getOccurenceTime(); long timeDiff = time2 - time1; List<Interval> interval=null; if (!concurrent(time1, time2, durationCycles)) { interval = Interval.intervalTimeSequence(Math.abs(timeDiff), Parameters.TEMPORAL_INTERVAL_PRECISION, nal.mem()); if (timeDiff > 0) { t1 = Conjunction.make(t1, interval, ORDER_FORWARD); if(t11!=null) { t11 = Conjunction.make(t11, interval, ORDER_FORWARD); } } else { t2 = Conjunction.make(t2, interval, ORDER_FORWARD); if(t22!=null) { t22 = Conjunction.make(t22, interval, ORDER_FORWARD); } } } int order = order(timeDiff, durationCycles); TruthValue givenTruth1 = s1.truth; TruthValue givenTruth2 = s2.truth; //This code adds a penalty for large time distance (TODO probably revise) Sentence s3 = s2.projection(s1.getOccurenceTime(), nal.memory.time()); givenTruth2 = s3.truth; // TruthFunctions. TruthValue truth1 = TruthFunctions.induction(givenTruth1, givenTruth2); TruthValue truth2 = TruthFunctions.induction(givenTruth2, givenTruth1); TruthValue truth3 = TruthFunctions.comparison(givenTruth1, givenTruth2); TruthValue truth4 = TruthFunctions.intersection(givenTruth1, givenTruth2); BudgetValue budget1 = BudgetFunctions.forward(truth1, nal); budget1.setPriority(budget1.getPriority() * Parameters.TEMPORAL_INDUCTION_PRIORITY_PENALTY); BudgetValue budget2 = BudgetFunctions.forward(truth2, nal); budget2.setPriority(budget2.getPriority() * Parameters.TEMPORAL_INDUCTION_PRIORITY_PENALTY); BudgetValue budget3 = BudgetFunctions.forward(truth3, nal); budget3.setPriority(budget3.getPriority() * Parameters.TEMPORAL_INDUCTION_PRIORITY_PENALTY); BudgetValue budget4 = BudgetFunctions.forward(truth4, nal); //this one is sequence in sequenceBag, no need to reduce here //https://groups.google.com/forum/#!topic/open-nars/0k-TxYqg4Mc if(!SucceedingEventsInduction) { //reduce priority according to temporal distance //it was not "semantically" connected by temporal succession int tt1=(int) s1.getOccurenceTime(); int tt2=(int) s1.getOccurenceTime(); int d=Math.abs(tt1-tt2)/Parameters.DURATION; if(d!=0) { double mul=1.0/((double)d); budget1.setPriority((float) (budget1.getPriority()*mul)); budget2.setPriority((float) (budget2.getPriority()*mul)); budget3.setPriority((float) (budget3.getPriority()*mul)); budget4.setPriority((float) (budget4.getPriority()*mul)); } } Statement statement1 = Implication.make(t1, t2, order); Statement statement2 = Implication.make(t2, t1, reverseOrder(order)); Statement statement3 = Equivalence.make(t1, t2, order); Term statement4 = null; switch (order) { case TemporalRules.ORDER_FORWARD: statement4 = Conjunction.make(t1, interval, s2.term, order); break; case TemporalRules.ORDER_BACKWARD: statement4 = Conjunction.make(s2.term, interval, t1, reverseOrder(order)); break; default: statement4 = Conjunction.make(t1, s2.term, order); break; } //maybe this way is also the more flexible and intelligent way to introduce variables for the case above //TODO: rethink this for 1.6.3 //"Perception Variable Introduction Rule" - https://groups.google.com/forum/#!topic/open-nars/uoJBa8j7ryE if(!deriveSequenceOnly && statement2!=null) { //there is no general form //ok then it may be the (&/ =/> case which //is discussed here: https://groups.google.com/forum/#!topic/open-nars/uoJBa8j7ryE Statement st=statement2; if(st.getPredicate() instanceof Inheritance && (st.getSubject() instanceof Conjunction || st.getSubject() instanceof Operation)) { Term precon=(Term) st.getSubject(); Inheritance consequence=(Inheritance) st.getPredicate(); Term pred=consequence.getPredicate(); Term sub=consequence.getSubject(); //look if subject is contained in precon: boolean SubsSub=precon.containsTermRecursively(sub); boolean SubsPred=precon.containsTermRecursively(pred); Variable v1=new Variable("$91"); Variable v2=new Variable("$92"); HashMap<Term,Term> app=new HashMap<Term,Term>(); if(SubsSub || SubsPred) { if(SubsSub) app.put(sub, v1); if(SubsPred) app.put(pred,v2); Term res=((CompoundTerm) statement2).applySubstitute(app); if(res!=null) { //ok we applied it, all we have to do now is to use it t22=((Statement)res).getSubject(); t11=((Statement)res).getPredicate(); } } } } List<Task> success=new ArrayList<Task>(); if(!deriveSequenceOnly && t11!=null && t22!=null) { Statement statement11 = Implication.make(t11, t22, order); Statement statement22 = Implication.make(t22, t11, reverseOrder(order)); Statement statement33 = Equivalence.make(t11, t22, order); if(!tooMuchTemporalStatements(statement11)) { List<Task> t=nal.doublePremiseTask(statement11, truth1, budget1,true, false); if(t!=null) { success.addAll(t); } } if(!tooMuchTemporalStatements(statement22)) { List<Task> t=nal.doublePremiseTask(statement22, truth2, budget2,true, false); if(t!=null) { success.addAll(t); } } if(!tooMuchTemporalStatements(statement33)) { List<Task> t=nal.doublePremiseTask(statement33, truth3, budget3,true, false); if(t!=null) { success.addAll(t); } } } if(!deriveSequenceOnly && !tooMuchTemporalStatements(statement1)) { List<Task> t=nal.doublePremiseTask(statement1, truth1, budget1,true, false); if(t!=null) { success.addAll(t); for(Task task : t) { task.setObservablePrediction(true); //we assume here that this function is used for observable events currently } } } if(!deriveSequenceOnly && !tooMuchTemporalStatements(statement2)) { List<Task> t=nal.doublePremiseTask(statement2, truth2, budget2,true, false); if(t!=null) { success.addAll(t); for(Task task : t) { task.setObservablePrediction(true); //we assume here that this function is used for observable events currently } /*Task task=t; //micropsi inspired strive for knowledge //get strongest belief of that concept and use the revison truth, if there is no, use this truth double conf=task.sentence.truth.getConfidence(); Concept C=nal.memory.concept(task.sentence.term); if(C!=null && C.beliefs!=null && C.beliefs.size()>0) { Sentence bel=C.beliefs.get(0).sentence; TruthValue cur=bel.truth; conf=Math.max(cur.getConfidence(), conf); //no matter if revision is possible, it wont be below max //if there is no overlapping evidental base, use revision: boolean revisable=true; for(long l: bel.stamp.evidentialBase) { for(long h: task.sentence.stamp.evidentialBase) { if(l==h) { revisable=false; break; } } } if(revisable) { conf=TruthFunctions.revision(task.sentence.truth, bel.truth).getConfidence(); } } questionFromLowConfidenceHighPriorityJudgement(task, conf, nal); */ } } if(!deriveSequenceOnly && !tooMuchTemporalStatements(statement3)) { List<Task> t=nal.doublePremiseTask(statement3, truth3, budget3,true, false); if(t!=null) { for(Task task : t) { task.setObservablePrediction(true); //we assume here that this function is used for observable events currently } success.addAll(t); } } if(!tooMuchTemporalStatements(statement4)) { List<Task> tl=nal.doublePremiseTask(statement4, truth4, budget4,true, false); if(tl!=null) { for(Task t : tl) { //fill sequenceTask buffer due to the new derived sequence if(t.sentence.isJudgment() && !t.sentence.isEternal() && t.sentence.term instanceof Conjunction && ((Conjunction) t.sentence.term).getTemporalOrder() != TemporalRules.ORDER_NONE && ((Conjunction) t.sentence.term).getTemporalOrder() != TemporalRules.ORDER_INVALID) { TemporalInferenceControl.addToSequenceTasks(nal, t); } success.add(t); } } } return success; } private static void questionFromLowConfidenceHighPriorityJudgement(Task task, double conf, final DerivationContext nal) { if(nal.memory.emotion.busy()<Parameters.CURIOSITY_BUSINESS_THRESHOLD && Parameters.CURIOSITY_ALSO_ON_LOW_CONFIDENT_HIGH_PRIORITY_BELIEF && task.sentence.punctuation==Symbols.JUDGMENT_MARK && conf<Parameters.CURIOSITY_CONFIDENCE_THRESHOLD && task.getPriority()>Parameters.CURIOSITY_PRIORITY_THRESHOLD) { if(task.sentence.term instanceof Implication) { boolean valid=false; if(task.sentence.term instanceof Implication) { Implication equ=(Implication) task.sentence.term; if(equ.getTemporalOrder()!=TemporalRules.ORDER_NONE) { valid=true; } } if(valid) { Sentence tt2=new Sentence(task.sentence.term.clone(),Symbols.QUESTION_MARK,null,new Stamp(task.sentence.stamp.clone(),nal.memory.time())); BudgetValue budg=task.budget.clone(); budg.setPriority(budg.getPriority()*Parameters.CURIOSITY_DESIRE_PRIORITY_MUL); budg.setDurability(budg.getPriority()*Parameters.CURIOSITY_DESIRE_DURABILITY_MUL); nal.singlePremiseTask(tt2, task.budget.clone()); } } } } /** * Evaluate the quality of the judgment as a solution to a problem * * @param problem A goal or question * @param solution The solution to be evaluated * @return The quality of the judgment as the solution */ public static float solutionQuality(boolean rateByConfidence, final Task probT, final Sentence solution, Memory memory) { Sentence problem = probT.sentence; if (!matchingOrder(problem.getTemporalOrder(), solution.getTemporalOrder())) { return 0.0F; } TruthValue truth = solution.truth; if (problem.getOccurenceTime()!=solution.getOccurenceTime()) { truth = solution.projectionTruth(problem.getOccurenceTime(), memory.time()); } //when the solutions are comparable, we have to use confidence!! else truth expectation. //this way negative evidence can update the solution instead of getting ignored due to lower truth expectation. //so the previous handling to let whether the problem has query vars decide was wrong. if (!rateByConfidence) { return (float) (truth.getExpectation() / Math.sqrt(Math.sqrt(Math.sqrt(solution.term.getComplexity()*Parameters.COMPLEXITY_UNIT)))); } else { return truth.getConfidence(); } } /* ----- Functions used both in direct and indirect processing of tasks ----- */ /** * 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 */ public static BudgetValue solutionEval(final Task problem, final Sentence solution, Task task, final nars.control.DerivationContext nal) { BudgetValue budget = null; boolean feedbackToLinks = false; if (task == null) { task = nal.getCurrentTask(); feedbackToLinks = true; } boolean judgmentTask = task.sentence.isJudgment(); boolean rateByConfidence = problem.getTerm().hasVarQuery(); //here its whether its a what or where question for budget adjustment final float quality = TemporalRules.solutionQuality(rateByConfidence, problem, solution, nal.mem()); if (problem.sentence.isGoal()) { nal.memory.emotion.adjustHappy(quality, task.getPriority(), nal); } if (judgmentTask) { task.incPriority(quality); } else { float taskPriority = task.getPriority(); //+goal satisfication is a matter of degree - https://groups.google.com/forum/#!topic/open-nars/ZfCM416Dx1M budget = new BudgetValue(UtilityFunctions.or(taskPriority, quality), task.getDurability(), BudgetFunctions.truthToQuality(solution.truth)); task.setPriority(Math.min(1 - quality, taskPriority)); } if (feedbackToLinks) { TaskLink tLink = nal.getCurrentTaskLink(); tLink.setPriority(Math.min(1 - quality, tLink.getPriority())); TermLink bLink = nal.getCurrentBeliefLink(); bLink.incPriority(quality); } return budget; } public static int order(final long timeDiff, final int durationCycles) { final int halfDuration = durationCycles/2; if (timeDiff > halfDuration) { return ORDER_FORWARD; } else if (timeDiff < -halfDuration) { return ORDER_BACKWARD; } else { return ORDER_CONCURRENT; } } /** if (relative) event B after (stationary) event A then order=forward; * event B before then order=backward * occur at the same time, relative to duration: order = concurrent */ public static int order(final long a, final long b, final int durationCycles) { if ((a == Stamp.ETERNAL) || (b == Stamp.ETERNAL)) throw new RuntimeException("order() does not compare ETERNAL times"); return order(b - a, durationCycles); } public static boolean concurrent(final long a, final long b, final int durationCycles) { //since Stamp.ETERNAL is Integer.MIN_VALUE, //avoid any overflow errors by checking eternal first if (a == Stamp.ETERNAL) { //if both are eternal, consider concurrent. this is consistent with the original //method of calculation which compared equivalent integer values only return (b == Stamp.ETERNAL); } else if (b == Stamp.ETERNAL) { return false; //a==b was compared above } else { return order(a, b, durationCycles) == ORDER_CONCURRENT; } } }