/* * Concept.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 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 Open-NARS. If not, see <http://www.gnu.org/licenses/>. */ package nars.entity; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import nars.inference.TruthFunctions; import nars.util.Events.BeliefSelect; import nars.util.Events.ConceptBeliefAdd; import nars.util.Events.ConceptBeliefRemove; import nars.util.Events.ConceptGoalAdd; import nars.util.Events.ConceptGoalRemove; import nars.util.Events.ConceptQuestionAdd; import nars.util.Events.ConceptQuestionRemove; import nars.util.Events.TaskLinkAdd; import nars.util.Events.TaskLinkRemove; import nars.util.Events.TermLinkAdd; import nars.util.Events.TermLinkRemove; import nars.util.Events.UnexecutableGoal; import nars.storage.Memory; import nars.io.NARConsole; import nars.config.Parameters; import nars.control.DerivationContext; import static nars.inference.BudgetFunctions.distributeAmongLinks; import static nars.inference.BudgetFunctions.rankBelief; import static nars.inference.LocalRules.revisible; import static nars.inference.LocalRules.revision; import static nars.inference.LocalRules.trySolution; import nars.inference.SyllogisticRules; import nars.inference.TemporalRules; import static nars.inference.TemporalRules.solutionQuality; import nars.io.Symbols; import nars.io.Symbols.NativeOperator; import nars.language.CompoundTerm; import nars.language.Equivalence; import nars.language.Implication; import nars.language.Term; import nars.language.Variable; import nars.operator.Operation; import nars.operator.Operator; import nars.plugin.mental.InternalExperience; import nars.storage.Bag; import nars.storage.LevelBag; import nars.language.Conjunction; import nars.language.Interval; import nars.util.Events.EnactableExplainationAdd; import nars.util.Events.EnactableExplainationRemove; import static nars.inference.UtilityFunctions.or; import nars.language.Variables; import nars.operator.mental.Anticipate; import nars.util.Events; import static nars.inference.UtilityFunctions.or; import nars.io.Output; import nars.language.Statement; public class Concept extends Item<Term> { /** * The term is the unique ID of the concept */ public final Term term; /** * Task links for indirect processing */ public final Bag<TaskLink,Task> taskLinks; /** * Term links between the term and its components and compounds; beliefs */ public final Bag<TermLink,TermLink> termLinks; /** * Link templates of TermLink, only in concepts with CompoundTerm Templates * are used to improve the efficiency of TermLink building */ public List<TermLink> termLinkTemplates; /** * Pending Question directly asked about the term * * Note: since this is iterated frequently, an array should be used. To * avoid iterator allocation, use .get(n) in a for-loop */ public final List<Task> questions; /** * Pending Quests to be answered by new desire values */ public final ArrayList<Task> quests; /** * Judgments directly made about the term Use ArrayList because of access * and insertion in the middle */ public final ArrayList<Task> beliefs; public final ArrayList<Task> executable_preconditions; /** * Desire values on the term, similar to the above one */ public final ArrayList<Task> desires; /** * Reference to the memory to which the Concept belongs */ public final Memory memory; /** * The display window */ //public final ArrayList<ArrayList<Long>> evidentalDiscountBases=new ArrayList<ArrayList<Long>>(); /* ---------- constructor and initialization ---------- */ /** * Constructor, called in Memory.getConcept only * * @param tm A term corresponding to the concept * @param memory A reference to the memory */ public Concept(final BudgetValue b, final Term tm, final Memory memory) { super(b); this.term = tm; this.memory = memory; this.questions = new ArrayList<>(); this.beliefs = new ArrayList<>(); this.executable_preconditions = new ArrayList<>(); this.quests = new ArrayList<>(); this.desires = new ArrayList<>(); this.taskLinks = new LevelBag<>(Parameters.TASK_LINK_BAG_LEVELS, Parameters.TASK_LINK_BAG_SIZE); this.termLinks = new LevelBag<>(Parameters.TERM_LINK_BAG_LEVELS, Parameters.TERM_LINK_BAG_SIZE); if (tm instanceof CompoundTerm) { this.termLinkTemplates = ((CompoundTerm) tm).prepareComponentLinks(); } else { this.termLinkTemplates = null; } } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Concept)) return false; return ((Concept)obj).name().equals(name()); } @Override public int hashCode() { return name().hashCode(); } @Override public Term name() { return term; } /* ---------- direct processing of tasks ---------- */ /** * Directly process a new task. Called exactly once on each task. Using * local information and finishing in a constant time. Provide feedback in * the taskBudget value of the task. * <p> * called in Memory.immediateProcess only * * @param task The task to be processed * @return whether it was processed */ public boolean observable = false; public boolean directProcess(final DerivationContext nal, final Task task) { if(task.isInput()) { observable = true; } char type = task.sentence.punctuation; switch (type) { case Symbols.JUDGMENT_MARK: //memory.logic.JUDGMENT_PROCESS.commit(); processJudgment(nal, task); break; case Symbols.GOAL_MARK: //memory.logic.GOAL_PROCESS.commit(); processGoal(nal, task, true); break; case Symbols.QUESTION_MARK: case Symbols.QUEST_MARK: //memory.logic.QUESTION_PROCESS.commit(); processQuestion(nal, task); break; default: return false; } maintainDisappointedAnticipations(); if (task.aboveThreshold()) { // still need to be processed //memory.logic.LINK_TO_TASK.commit(); linkToTask(task,nal); } return true; } /** * To accept a new judgment as belief, and check for revisions and solutions * * @param judg The judgment to be accepted * @param task The task to be processed * @return Whether to continue the processing of the task */ protected void processJudgment(final DerivationContext nal, final Task task) { final Sentence judg = task.sentence; //check whether it satisfies anticipation: if(task.isInput() && !task.sentence.isEternal() && this.negConfirmation != null && task.sentence.getOccurenceTime() > this.negConfirm_abort_mintime) { if(task.sentence.truth.getExpectation() > Parameters.DEFAULT_CONFIRMATION_EXPECTATION) { if(((Statement) this.negConfirmation.sentence.term).getPredicate().equals(task.sentence.getTerm())) { nal.memory.emit(Output.CONFIRM.class,((Statement) this.negConfirmation.sentence.term).getPredicate()); this.negConfirmation = null; //confirmed } } } final Task oldBeliefT = selectCandidate(task, beliefs, true); // only revise with the strongest -- how about projection? Sentence oldBelief = null; if (oldBeliefT != null) { oldBelief = oldBeliefT.sentence; final Stamp newStamp = judg.stamp; final Stamp oldStamp = oldBelief.stamp; //when table is full, the latter check is especially important too if (newStamp.equals(oldStamp,false,true,true,false) && task.sentence.truth.equals(oldBelief.truth)) { //if (task.getParentTask() != null && task.getParentTask().sentence.isJudgment()) { ////task.budget.decPriority(0); // duplicated task //} //// else: activated belief memory.removeTask(task, "Duplicated"); return; } else if (revisible(judg, oldBelief)) { nal.setTheNewStamp(newStamp, oldStamp, memory.time()); Sentence projectedBelief = oldBelief.projection(memory.time(), newStamp.getOccurrenceTime()); if (projectedBelief!=null) { if (projectedBelief.getOccurenceTime()!=oldBelief.getOccurenceTime()) { // nal.singlePremiseTask(projectedBelief, task.budget); } nal.setCurrentBelief(projectedBelief); revision(judg, projectedBelief, false, nal); } } } if (task.aboveThreshold()) { int nnq = questions.size(); for (int i = 0; i < nnq; i++) { trySolution(judg, questions.get(i), nal, true); } addToTable(task, false, beliefs, Parameters.CONCEPT_BELIEFS_MAX, ConceptBeliefAdd.class, ConceptBeliefRemove.class); //if taskLink predicts this concept then add to predictive Task target = task; Term term = target.getTerm(); if(//target.isObservablePrediction() && target.sentence.isEternal() && term instanceof Implication && !term.hasVarIndep()) //Might be relaxed in the future!! { Implication imp = (Implication) term; if(imp.getTemporalOrder() == TemporalRules.ORDER_FORWARD) { //also it has to be enactable, meaning the last entry of the sequence before the interval is an operation: Term subj = imp.getSubject(); Term pred = imp.getPredicate(); Concept pred_conc = nal.memory.concept(pred); if(pred_conc != null /*&& !(pred instanceof Operation)*/ && (subj instanceof Conjunction)) { Conjunction conj = (Conjunction) subj; if(conj.getTemporalOrder() == TemporalRules.ORDER_FORWARD && conj.term.length >= 4 && conj.term.length%2 == 0 && conj.term[conj.term.length-1] instanceof Interval && conj.term[conj.term.length-2] instanceof Operation) { //we do not add the target, instead the strongest belief in the target concept if(beliefs.size() > 0) { Task strongest_target = null; //beliefs.get(0); //get the first eternal: for(Task t : beliefs) { if(t.sentence.isEternal()) { strongest_target = t; break; } } int a = pred_conc.executable_preconditions.size(); //at first we have to remove the last one with same content from table int i_delete = -1; for(int i=0; i < pred_conc.executable_preconditions.size(); i++) { if(CompoundTerm.cloneDeepReplaceIntervals(pred_conc.executable_preconditions.get(i).getTerm()).equals( CompoundTerm.cloneDeepReplaceIntervals(strongest_target.getTerm()))) { i_delete = i; //even these with same term but different intervals are removed here break; } } if(i_delete != -1) { pred_conc.executable_preconditions.remove(i_delete); } Term[] prec = ((Conjunction) ((Implication) strongest_target.getTerm()).getSubject()).term; for(int i=0;i<prec.length-2;i++) { if(prec[i] instanceof Operation) { //don't react to precondition with an operation before the last return; //for now, these can be decomposed into smaller such statements anyway } } //this way the strongest confident result of this content is put into table but the table ranked according to truth expectation pred_conc.addToTable(strongest_target, true, pred_conc.executable_preconditions, Parameters.CONCEPT_BELIEFS_MAX, EnactableExplainationAdd.class, EnactableExplainationRemove.class); } } } } } } } protected void addToTable(final Task task, final boolean rankTruthExpectation, final ArrayList<Task> table, final int max, final Class eventAdd, final Class eventRemove, final Object... extraEventArguments) { int preSize = table.size(); Task removedT; Sentence removed = null; removedT = addToTable(task, table, max, rankTruthExpectation); if(removedT != null) { removed=removedT.sentence; } if (removed != null) { memory.event.emit(eventRemove, this, removed, task, extraEventArguments); } if ((preSize != table.size()) || (removed != null)) { memory.event.emit(eventAdd, this, task, extraEventArguments); } } /** * whether a concept's desire exceeds decision threshold */ public boolean isDesired() { TruthValue desire=this.getDesire(); if(desire==null) { return false; } return desire.getExpectation() > memory.param.decisionThreshold.get(); } /** * Entry point for all potentially executable tasks. * Returns true if the Task has a Term which can be executed */ public boolean executeDecision(final Task t) { //if (isDesired()) if(memory.allowExecution) { Term content = t.getTerm(); if(content instanceof Operation && !content.hasVarDep() && !content.hasVarIndep()) { Operation op=(Operation)content; Operator oper = op.getOperator(); op.setTask(t); if(!oper.call(op, memory)) { return false; } this.memory.lastDecision = t; //depriorize everything related to the previous decisions: successfulOperationHandler(this.memory); //this.memory.sequenceTasks = new LevelBag<>(Parameters.SEQUENCE_BAG_LEVELS, Parameters.SEQUENCE_BAG_SIZE); return true; } } return false; } public static void successfulOperationHandler(Memory memory) { //multiple versions are necessary, but we do not allow duplicates if(Parameters.CONSIDER_NEW_OPERATION_BIAS == 1.0f) { return; } for(Task s : memory.sequenceTasks) { if(memory.lastDecision != null && (s.getTerm() instanceof Operation)) { if(!s.getTerm().equals(memory.lastDecision.getTerm())) { s.setPriority(s.getPriority()*Parameters.CONSIDER_NEW_OPERATION_BIAS); continue; //depriorized already, we can look at the next now } } if(memory.lastDecision != null && (s.getTerm() instanceof Conjunction)) { Conjunction seq = (Conjunction) s.getTerm(); if(seq.getTemporalOrder() == TemporalRules.ORDER_FORWARD) { for(Term w : seq.term) { if((w instanceof Operation) && !w.equals(memory.lastDecision.getTerm())) { s.setPriority(s.getPriority()*Parameters.CONSIDER_NEW_OPERATION_BIAS); break; //break because just penalty once, not for each term ^^ } } } } } } /** * To accept a new goal, and check for revisions and realization, then * decide whether to actively pursue it * * @param judg The judgment to be accepted * @param task The task to be processed * @return Whether to continue the processing of the task */ protected boolean processGoal(final DerivationContext nal, final Task task, boolean shortcut) { final Sentence goal = task.sentence; final Task oldGoalT = selectCandidate(task, desires, true); // revise with the existing desire values Sentence oldGoal = null; if (oldGoalT != null) { oldGoal = oldGoalT.sentence; final Stamp newStamp = goal.stamp; final Stamp oldStamp = oldGoal.stamp; if (newStamp.equals(oldStamp,false,false,true,false)) { return false; // duplicate } if (revisible(goal, oldGoal)) { nal.setTheNewStamp(newStamp, oldStamp, memory.time()); Sentence projectedGoal = oldGoal.projection(task.sentence.getOccurenceTime(), newStamp.getOccurrenceTime()); if (projectedGoal!=null) { // if (goal.after(oldGoal, nal.memory.param.duration.get())) { //no need to project the old goal, it will be projected if selected anyway now // nal.singlePremiseTask(projectedGoal, task.budget); //return; // } nal.setCurrentBelief(projectedGoal); if(!(task.sentence.term instanceof Operation)) { boolean successOfRevision=revision(task.sentence, projectedGoal, false, nal); if(successOfRevision) { // it is revised, so there is a new task for which this function will be called return false; // with higher/lower desire } //it is not allowed to go on directly due to decision making https://groups.google.com/forum/#!topic/open-nars/lQD0no2ovx4 } } } } Stamp s2=goal.stamp.clone(); s2.setOccurrenceTime(memory.time()); if(s2.after(task.sentence.stamp, nal.memory.param.duration.get())) { //this task is not up to date we have to project it first Sentence projGoal = task.sentence.projection(memory.time(), nal.memory.param.duration.get()); if(projGoal!=null && projGoal.truth.getExpectation() > nal.memory.param.decisionThreshold.get()) { nal.singlePremiseTask(projGoal, task.budget.clone()); //keep goal updated // return false; //outcommented, allowing "roundtrips now", relevant for executing multiple steps of learned implication chains } } if (task.aboveThreshold()) { final Task beliefT = selectCandidate(task, beliefs, false); // check if the Goal is already satisfied double AntiSatisfaction = 0.5f; //we dont know anything about that goal yet, so we pursue it to remember it because its maximally unsatisfied if (beliefT != null) { Sentence belief = beliefT.sentence; Sentence projectedBelief = belief.projection(task.sentence.getOccurenceTime(), nal.memory.param.duration.get()); trySolution(projectedBelief, task, nal, true); // check if the Goal is already satisfied (manipulate budget) AntiSatisfaction = task.sentence.truth.getExpDifAbs(belief.truth); } double Satisfaction=1.0-AntiSatisfaction; TruthValue T=goal.truth.clone(); T.setFrequency((float) (T.getFrequency()-Satisfaction)); //decrease frequency according to satisfaction value boolean fullfilled = AntiSatisfaction < Parameters.SATISFACTION_TRESHOLD; Sentence projectedGoal = goal.projection(nal.memory.time(),nal.memory.time()); if (projectedGoal != null && task.aboveThreshold() && !fullfilled && projectedGoal.truth.getExpectation() > nal.memory.param.decisionThreshold.get()) { try{ Operation bestop = null; float bestop_truthexp = 0.0f; TruthValue bestop_truth = null; Task executable_precond = null; //long distance = -1; long mintime = -1; long maxtime = -1; for(Task t: this.executable_preconditions) { Term[] prec = ((Conjunction) ((Implication) t.getTerm()).getSubject()).term; Term[] newprec = new Term[prec.length-3]; for(int i=0;i<prec.length-3;i++) { //skip the last part: interval, operator, interval newprec[i] = prec[i]; } //distance = Interval.magnitudeToTime(((Interval)prec[prec.length-1]).magnitude, nal.memory.param.duration); mintime = nal.memory.time() + Interval.magnitudeToTime(((Interval)prec[prec.length-1]).magnitude-1, nal.memory.param.duration); maxtime = nal.memory.time() + Interval.magnitudeToTime(((Interval)prec[prec.length-1]).magnitude+2, nal.memory.param.duration); Operation op = (Operation) prec[prec.length-2]; Term precondition = Conjunction.make(newprec,TemporalRules.ORDER_FORWARD); Concept preconc = nal.memory.concept(precondition); long newesttime = -1; Task bestsofar = null; if(preconc != null) { //ok we can look now how much it is fullfilled //check recent events in event bag for(Task p : this.memory.sequenceTasks) { if(p.sentence.term.equals(preconc.term) && p.sentence.isJudgment() && !p.sentence.isEternal() && p.sentence.getOccurenceTime() > newesttime && p.sentence.getOccurenceTime() <= memory.time()) { newesttime = p.sentence.getOccurenceTime(); bestsofar = p; //we use the newest for now } } if(bestsofar == null) { continue; } //ok now we can take the desire value: TruthValue A = projectedGoal.getTruth(); //and the truth of the hypothesis: TruthValue Hyp = t.sentence.truth; //and the truth of the precondition: Sentence projectedPrecon = bestsofar.sentence.projection(memory.time() /*- distance*/, memory.time()); if(projectedPrecon.isEternal()) { continue; //projection wasn't better than eternalization, too long in the past } //debug start //long timeA = memory.time(); //long timeOLD = bestsofar.sentence.stamp.getOccurrenceTime(); //long timeNEW = projectedPrecon.stamp.getOccurrenceTime(); //debug end TruthValue precon = projectedPrecon.truth; //and derive the conjunction of the left side: TruthValue leftside = TruthFunctions.desireDed(A, Hyp); //in order to derive the operator desire value: TruthValue opdesire = TruthFunctions.desireDed(precon, leftside); float expecdesire = opdesire.getExpectation(); if(expecdesire > bestop_truthexp) { bestop = op; bestop_truthexp = expecdesire; bestop_truth = opdesire; executable_precond = t; } } } if(bestop != null && bestop_truthexp > memory.param.decisionThreshold.get() /*&& Math.random() < bestop_truthexp */) { Task t = new Task(new Sentence(bestop,Symbols.JUDGMENT_MARK,bestop_truth, projectedGoal.stamp), new BudgetValue(1.0f,1.0f,1.0f)); //System.out.println("used " +t.getTerm().toString() + String.valueOf(memory.randomNumber.nextInt())); if(!task.sentence.stamp.evidenceIsCyclic()) { if(!executeDecision(t)) { //this task is just used as dummy memory.emit(UnexecutableGoal.class, task, this, nal); } else { memory.decisionBlock = memory.time() + Parameters.AUTOMATIC_DECISION_USUAL_DECISION_BLOCK_CYCLES; SyllogisticRules.generatePotentialNegConfirmation(nal, executable_precond.sentence, executable_precond.budget, mintime, maxtime, 2); } } } }catch(Exception ex) { System.out.println("Failure in operation choice rule, analyze!"); } questionFromGoal(task, nal); addToTable(task, false, desires, Parameters.CONCEPT_GOALS_MAX, ConceptGoalAdd.class, ConceptGoalRemove.class); InternalExperience.InternalExperienceFromTask(memory,task,false); if(nal.memory.time() >= memory.decisionBlock && !executeDecision(task)) { memory.emit(UnexecutableGoal.class, task, this, nal); return true; //it was made true by itself } return false; } return fullfilled; } return false; } private void questionFromGoal(final Task task, final DerivationContext nal) { if(Parameters.QUESTION_GENERATION_ON_DECISION_MAKING || Parameters.HOW_QUESTION_GENERATION_ON_DECISION_MAKING) { //ok, how can we achieve it? add a question of whether it is fullfilled ArrayList<Term> qu=new ArrayList<Term>(); if(Parameters.HOW_QUESTION_GENERATION_ON_DECISION_MAKING) { if(!(task.sentence.term instanceof Equivalence) && !(task.sentence.term instanceof Implication)) { Variable how=new Variable("?how"); //Implication imp=Implication.make(how, task.sentence.term, TemporalRules.ORDER_CONCURRENT); Implication imp2=Implication.make(how, task.sentence.term, TemporalRules.ORDER_FORWARD); //qu.add(imp); if(!(task.sentence.term instanceof Operation)) { qu.add(imp2); } } } if(Parameters.QUESTION_GENERATION_ON_DECISION_MAKING) { qu.add(task.sentence.term); } for(Term q : qu) { if(q!=null) { Stamp st = new Stamp(task.sentence.stamp,nal.memory.time()); st.setOccurrenceTime(task.sentence.getOccurenceTime()); //set tense of question to goal tense Sentence s=new Sentence(q,Symbols.QUESTION_MARK,null,st); if(s!=null) { BudgetValue budget=new BudgetValue(task.getPriority()*Parameters.CURIOSITY_DESIRE_PRIORITY_MUL,task.getDurability()*Parameters.CURIOSITY_DESIRE_DURABILITY_MUL,1); nal.singlePremiseTask(s, budget); } } } } } /** * To answer a question by existing beliefs * * @param task The task to be processed * @return Whether to continue the processing of the task */ protected void processQuestion(final DerivationContext nal, final Task task) { Task quesTask = task; boolean newQuestion = true; for (final Task t : questions) { if (t.sentence.equalsContent(quesTask.sentence)) { quesTask = t; newQuestion = false; break; } } if (newQuestion) { if (questions.size() + 1 > Parameters.CONCEPT_QUESTIONS_MAX) { Task removed = questions.remove(0); // FIFO memory.event.emit(ConceptQuestionRemove.class, this, removed); } questions.add(task); memory.event.emit(ConceptQuestionAdd.class, this, task); } Sentence ques = quesTask.sentence; final Task newAnswerT = (ques.isQuestion()) ? selectCandidate(quesTask, beliefs, false) : selectCandidate(quesTask, desires, false); if (newAnswerT != null) { trySolution(newAnswerT.sentence, task, nal, true); } else if(task.isInput() && !quesTask.getTerm().hasVarQuery() && quesTask.getBestSolution() != null) { //show previously found solution anyway in case of input memory.emit(Events.Answer.class, quesTask, quesTask.getBestSolution()); } } /** * Link to a new task from all relevant concepts for continued processing in * the near future for unspecified time. * <p> * The only method that calls the TaskLink constructor. * * @param task The task to be linked * @param content The content of the task */ public void linkToTask(final Task task, DerivationContext cont) { final BudgetValue taskBudget = task.budget; insertTaskLink(new TaskLink(task, null, taskBudget, Parameters.TERM_LINK_RECORD_LENGTH), cont); // link type: SELF if (!(term instanceof CompoundTerm)) { return; } if (termLinkTemplates.isEmpty()) { return; } final BudgetValue subBudget = distributeAmongLinks(taskBudget, termLinkTemplates.size()); if (subBudget.aboveThreshold()) { for (int t = 0; t < termLinkTemplates.size(); t++) { TermLink termLink = termLinkTemplates.get(t); if(termLink.type == TermLink.TEMPORAL) continue; // if (!(task.isStructural() && (termLink.getType() == TermLink.TRANSFORM))) { // avoid circular transform Term componentTerm = termLink.target; Concept componentConcept = memory.conceptualize(subBudget, componentTerm); if (componentConcept != null) { componentConcept.insertTaskLink( new TaskLink(task, termLink, subBudget, Parameters.TERM_LINK_RECORD_LENGTH), cont ); } // } } buildTermLinks(taskBudget); // recursively insert TermLink } } /** * Add a new belief (or goal) into the table Sort the beliefs/desires by * rank, and remove redundant or low rank one * * @param newSentence The judgment to be processed * @param table The table to be revised * @param capacity The capacity of the table * @return whether table was modified */ public static Task addToTable(final Task newTask, final List<Task> table, final int capacity, boolean rankTruthExpectation) { Sentence newSentence = newTask.sentence; final float rank1 = rankBelief(newSentence, rankTruthExpectation); // for the new isBelief float rank2; int i; for (i = 0; i < table.size(); i++) { Sentence judgment2 = table.get(i).sentence; rank2 = rankBelief(judgment2, rankTruthExpectation); if (rank1 >= rank2) { if (newSentence.equivalentTo(judgment2)) { //System.out.println(" ---------- Equivalent Belief: " + newSentence + " == " + judgment2); return null; } table.add(i, newTask); break; } } if (table.size() == capacity) { // nothing } else if (table.size() > capacity) { Task removed = table.remove(table.size() - 1); return removed; } else if (i == table.size()) { // branch implies implicit table.size() < capacity table.add(newTask); } return null; } /** * Select a belief value or desire value for a given query * * @param query The query to be processed * @param list The list of beliefs or desires to be used * @return The best candidate selected */ public Task selectCandidate(final Task query, final List<Task> list, boolean forRevision) { // if (list == null) { // return null; // } float currentBest = 0; float beliefQuality; Task candidate = null; boolean rateByConfidence = true; //table vote, yes/no question / local processing synchronized (list) { for (int i = 0; i < list.size(); i++) { Task judgT = list.get(i); Sentence judg = judgT.sentence; beliefQuality = solutionQuality(rateByConfidence, query, judg, memory); //makes revision explicitly search for if (beliefQuality > currentBest /*&& (!forRevision || judgT.sentence.equalsContent(query)) */ /*&& (!forRevision || !Stamp.baseOverlap(query.stamp.evidentialBase, judg.stamp.evidentialBase)) */) { currentBest = beliefQuality; candidate = judgT; } } } return candidate; } public float negConfirmationPriority = 0.0f; public Task negConfirmation = null; public long negConfirm_abort_mintime = 0; public long negConfirm_abort_maxtime = 0; /* ---------- insert Links for indirect processing ---------- */ /** * Insert a TaskLink into the TaskLink bag * <p> * called only from Memory.continuedProcess * * @param taskLink The termLink to be inserted */ protected boolean insertTaskLink(final TaskLink taskLink, DerivationContext nal) { Task target = taskLink.getTarget(); Task ques = taskLink.getTarget(); if((ques.sentence.isQuestion() || ques.sentence.isQuest()) && ques.getTerm().hasVarQuery()) { //ok query var, search boolean newAnswer = false; for(TaskLink t : this.taskLinks) { Term[] u = new Term[] { ques.getTerm(), t.getTerm() }; if(!t.getTerm().hasVarQuery() && Variables.unify(Symbols.VAR_QUERY, u)) { Concept c = nal.memory.concept(t.getTerm()); if(c != null && ques.sentence.isQuestion() && c.beliefs.size() > 0) { final Task taskAnswer = c.beliefs.get(0); if(taskAnswer!=null) { newAnswer |= trySolution(taskAnswer.sentence, ques, nal, false); //order important here } } if(c != null && ques.sentence.isQuest() && c.desires.size() > 0) { final Task taskAnswer = c.desires.get(0); if(taskAnswer!=null) { newAnswer |= trySolution(taskAnswer.sentence, ques, nal, false); //order important here } } } } if(newAnswer && ques.isInput()) { memory.emit(Events.Answer.class, ques, ques.getBestSolution()); } } //belief side: Task t = taskLink.getTarget(); if(t.sentence.isJudgment()) { //ok query var, search for(TaskLink quess: this.taskLinks) { ques = quess.getTarget(); if((ques.sentence.isQuestion() || ques.sentence.isQuest()) && ques.getTerm().hasVarQuery()) { boolean newAnswer = false; Term[] u = new Term[] { ques.getTerm(), t.getTerm() }; if(!t.getTerm().hasVarQuery() && Variables.unify(Symbols.VAR_QUERY, u)) { Concept c = nal.memory.concept(t.getTerm()); if(c != null && ques.sentence.isQuestion() && c.beliefs.size() > 0) { final Task taskAnswer = c.beliefs.get(0); if(taskAnswer!=null) { newAnswer |= trySolution(taskAnswer.sentence, ques, nal, false); //order important here } } if(c != null && ques.sentence.isQuest() && c.desires.size() > 0) { final Task taskAnswer = c.desires.get(0); if(taskAnswer!=null) { newAnswer |= trySolution(taskAnswer.sentence, ques, nal, false); //order important here } } } if(newAnswer && ques.isInput()) { memory.emit(Events.Answer.class, ques, ques.getBestSolution()); } } } } //HANDLE MAX PER CONTENT //if taskLinks already contain a certain amount of tasks with same content then one has to go boolean isEternal = target.sentence.isEternal(); int nSameContent = 0; float lowest_priority = Float.MAX_VALUE; TaskLink lowest = null; for(TaskLink tl : taskLinks) { Sentence s = tl.getTarget().sentence; if(s.getTerm().equals(taskLink.getTerm()) && s.isEternal() == isEternal) { nSameContent++; //same content and occurrence-type, so count +1 if(tl.getPriority() < lowest_priority) { //the current one has lower priority so save as lowest lowest_priority = tl.getPriority(); lowest = tl; } if(nSameContent > Parameters.TASKLINK_PER_CONTENT) { //ok we reached the maximum so lets delete the lowest taskLinks.take(lowest); memory.emit(TaskLinkRemove.class, lowest, this); break; } } } //END HANDLE MAX PER CONTENT TaskLink removed = taskLinks.putIn(taskLink); if (removed!=null) { if (removed == taskLink) { memory.emit(TaskLinkRemove.class, taskLink, this); return false; } else { memory.emit(TaskLinkRemove.class, removed, this); } removed.end(); } memory.emit(TaskLinkAdd.class, taskLink, this); return true; } /** * Recursively build TermLinks between a compound and its components * <p> * called only from Memory.continuedProcess * * @param taskBudget The BudgetValue of the task */ public void buildTermLinks(final BudgetValue taskBudget) { if (termLinkTemplates.size() == 0) { return; } BudgetValue subBudget = distributeAmongLinks(taskBudget, termLinkTemplates.size()); if (!subBudget.aboveThreshold()) { return; } for (final TermLink template : termLinkTemplates) { if (template.type != TermLink.TRANSFORM) { Term target = template.target; final Concept concept = memory.conceptualize(taskBudget, target); if (concept == null) { continue; } // this termLink to that insertTermLink(new TermLink(target, template, subBudget)); // that termLink to this concept.insertTermLink(new TermLink(term, template, subBudget)); if (target instanceof CompoundTerm && template.type != TermLink.TEMPORAL) { concept.buildTermLinks(subBudget); } } } } /** * Insert a TermLink into the TermLink bag * <p> * called from buildTermLinks only * * @param termLink The termLink to be inserted */ public boolean insertTermLink(final TermLink termLink) { TermLink removed = termLinks.putIn(termLink); if (removed!=null) { if (removed == termLink) { memory.emit(TermLinkRemove.class, termLink, this); return false; } else { memory.emit(TermLinkRemove.class, removed, this); } } memory.emit(TermLinkAdd.class, termLink, this); return true; } /** * Return a string representation of the concept, called in ConceptBag only * * @return The concept name, with taskBudget in the full version */ @Override public String toString() { // called from concept bag //return (super.toStringBrief() + " " + key); return super.toStringExternal(); } /** * called from {@link NARConsole} */ @Override public String toStringLong() { String res = toStringExternal() + " " + term.name() + toStringIfNotNull(termLinks.size(), "termLinks") + toStringIfNotNull(taskLinks.size(), "taskLinks") + toStringIfNotNull(beliefs.size(), "beliefs") + toStringIfNotNull(desires.size(), "desires") + toStringIfNotNull(questions.size(), "questions") + toStringIfNotNull(quests.size(), "quests"); //+ toStringIfNotNull(null, "questions"); /*for (Task t : questions) { res += t.toString(); }*/ // TODO other details? return res; } private String toStringIfNotNull(final Object item, final String title) { if (item == null) { return ""; } final String itemString = item.toString(); return new StringBuilder(2 + title.length() + itemString.length() + 1). append(" ").append(title).append(':').append(itemString).toString(); } /** * Recalculate the quality of the concept [to be refined to show * extension/intension balance] * * @return The quality value */ @Override public float getQuality() { float linkPriority = termLinks.getAveragePriority(); float termComplexityFactor = 1.0f / term.getComplexity()*Parameters.COMPLEXITY_UNIT; float result = or(linkPriority, termComplexityFactor); if (result < 0) { throw new RuntimeException("Concept.getQuality < 0: result=" + result + ", linkPriority=" + linkPriority + " ,termComplexityFactor=" + termComplexityFactor + ", termLinks.size=" + termLinks.size()); } return result; } /** * Return the templates for TermLinks, only called in * Memory.continuedProcess * * @return The template get */ public List<TermLink> getTermLinkTemplates() { return termLinkTemplates; } /** * Select a isBelief to interact with the given task in inference * <p> * get the first qualified one * <p> * only called in RuleTables.reason * * @param task The selected task * @return The selected isBelief */ public Sentence getBelief(final DerivationContext nal, final Task task) { final Stamp taskStamp = task.sentence.stamp; final long currentTime = memory.time(); for (final Task beliefT : beliefs) { Sentence belief = beliefT.sentence; nal.emit(BeliefSelect.class, belief); nal.setTheNewStamp(taskStamp, belief.stamp, currentTime); Sentence projectedBelief = belief.projection(taskStamp.getOccurrenceTime(), memory.time()); /*if (projectedBelief.getOccurenceTime() != belief.getOccurenceTime()) { nal.singlePremiseTask(projectedBelief, task.budget); }*/ return projectedBelief; // return the first satisfying belief } return null; } public Sentence getBeliefForTemporalInference(final Task task) { if(task.sentence.isEternal()) { //this is for event-event inference only return null; } Sentence bestSoFar = null; long distance = Long.MAX_VALUE; for (final Task beliefT : beliefs) { if(!beliefT.sentence.isEternal()) { long distance_new = Math.abs(task.sentence.getOccurenceTime() - beliefT.sentence.getOccurenceTime()); if(distance_new < distance) { distance = distance_new; bestSoFar = beliefT.sentence; } } } return bestSoFar; } /** * Get the current overall desire value. TODO to be refined */ public TruthValue getDesire() { if (desires.isEmpty()) { return null; } TruthValue topValue = desires.get(0).sentence.truth; return topValue; } @Override public void end() { for (Task t : questions) t.end(); for (Task t : quests) t.end(); questions.clear(); quests.clear(); desires.clear(); //evidentalDiscountBases.clear(); termLinks.clear(); taskLinks.clear(); beliefs.clear(); termLinkTemplates.clear(); } /** * Collect direct isBelief, questions, and desires for display * * @return String representation of direct content */ public String displayContent() { final StringBuilder buffer = new StringBuilder(18); buffer.append("\n Beliefs:\n"); if (!beliefs.isEmpty()) { for (Task t : beliefs) { buffer.append(t.sentence).append('\n'); } } if (!questions.isEmpty()) { buffer.append("\n Question:\n"); for (Task t : questions) { buffer.append(t).append('\n'); } } return buffer.toString(); } public void maintainDisappointedAnticipations() { //here we can check the expiration of the feedback: if(this.negConfirmation != null && this.memory.time() > this.negConfirm_abort_maxtime) { //at first search beliefs for input tasks: boolean cancelled = false; for(TaskLink tl : this.taskLinks) { //search for input in tasklinks (beliefs alone can not take temporality into account as the eternals will win) Task t = tl.targetTask; if(t!= null && t.sentence.isJudgment() && t.isInput() && !t.sentence.isEternal() && t.sentence.truth.getExpectation() > Parameters.DEFAULT_CONFIRMATION_EXPECTATION && CompoundTerm.cloneDeepReplaceIntervals(t.sentence.term).equals(CompoundTerm.cloneDeepReplaceIntervals(this.getTerm()))) { if(t.sentence.getOccurenceTime() >= this.negConfirm_abort_mintime && t.sentence.getOccurenceTime() <= this.negConfirm_abort_maxtime) { cancelled = true; break; } } } if(cancelled) { memory.emit(Output.CONFIRM.class,((Statement) this.negConfirmation.sentence.term).getPredicate()); this.negConfirmation = null; //confirmed return; } memory.inputTask(this.negConfirmation, false); //disappointed //if(this.negConfirmationPriority >= 2) { // System.out.println(this.negConfirmation.sentence.term); //} memory.emit(Output.DISAPPOINT.class,((Statement) this.negConfirmation.sentence.term).getPredicate()); this.negConfirmation = null; } } /** * Replace default to prevent repeated inference, by checking TaskLink * * @param taskLink The selected TaskLink * @param time The current time * @return The selected TermLink */ public TermLink selectTermLink(final TaskLink taskLink, final long time) { maintainDisappointedAnticipations(); int toMatch = Parameters.TERM_LINK_MAX_MATCHED; //Math.min(memory.param.termLinkMaxMatched.get(), termLinks.size()); for (int i = 0; (i < toMatch) && (termLinks.size() > 0); i++) { final TermLink termLink = termLinks.takeNext(); if (termLink==null) break; if (taskLink.novel(termLink, time)) { //return, will be re-inserted in caller method when finished processing it return termLink; } returnTermLink(termLink); } return null; } public void returnTermLink(TermLink termLink) { termLinks.putBack(termLink, memory.cycles(memory.param.termLinkForgetDurations), memory); } /** * Return the questions, called in ComposionalRules in * dedConjunctionByQuestion only */ public List<Task> getQuestions() { return questions; } public void discountConfidence(final boolean onBeliefs) { if (onBeliefs) { for (final Task t : beliefs) { t.sentence.discountConfidence(); } } else { for (final Task t : desires) { t.sentence.discountConfidence(); } } } /** get a random belief, weighted by their sentences confidences */ public Sentence getBeliefRandomByConfidence() { if (beliefs.isEmpty()) return null; float totalConfidence = getBeliefConfidenceSum(); float r = Memory.randomNumber.nextFloat() * totalConfidence; Sentence s = null; for (int i = 0; i < beliefs.size(); i++) { s = beliefs.get(i).sentence; r -= s.truth.getConfidence(); if (r < 0) return s; } return s; } public float getBeliefConfidenceSum() { float t = 0; for (final Task ts : beliefs) t += ts.sentence.truth.getConfidence(); return t; } public float getBeliefFrequencyMean() { if (beliefs.isEmpty()) return 0.5f; float t = 0; for (final Task s : beliefs) t += s.sentence.truth.getFrequency(); return t / beliefs.size(); } public CharSequence getBeliefsSummary() { if (beliefs.isEmpty()) return "0 beliefs"; StringBuilder sb = new StringBuilder(); for (Task ts : beliefs) sb.append(ts.toString()).append('\n'); return sb; } public CharSequence getDesiresSummary() { if (desires.isEmpty()) return "0 desires"; StringBuilder sb = new StringBuilder(); for (Task ts : desires) sb.append(ts.sentence.toString()).append('\n'); return sb; } public NativeOperator operator() { return term.operator(); } public Term getTerm() { return term; } /** returns unmodifidable collection wrapping beliefs */ public List<Task> getBeliefs() { return Collections.unmodifiableList(beliefs); } /** returns unmodifidable collection wrapping beliefs */ public List<Task> getDesires() { return Collections.unmodifiableList(desires); } }