/*
* Here comes the text of your license
* Each line should be prefixed with *
*/
package nars.lab.plugin.mental;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import nars.util.EventEmitter;
import nars.util.Events;
import nars.NAR;
import nars.NAR.PluginState;
import nars.config.Parameters;
import nars.util.Plugin;
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 nars.inference.TemporalRules;
import nars.io.Symbols;
import nars.language.Conjunction;
import nars.language.Implication;
import nars.language.Interval;
import nars.language.Term;
import nars.language.Variables;
import nars.operator.mental.Anticipate;
/**
*
* @author tc
*/
public class GlobalAnticipation implements Plugin, EventEmitter.EventObserver {
public final ArrayDeque<Task> stm = new ArrayDeque();
public final List<Task> current_tasks=new ArrayList<Task>();
int MatchUpTo=20;
public void setMatchEventsMax(double value) {
MatchUpTo=(int) value;
}
public double getMatchEventsMax() {
return MatchUpTo;
}
public double TEMPORAL_PREDICTION_FEEDBACK_ACCURACY_DIV=0.01;
public double getTemporalAccuracy() {
return TEMPORAL_PREDICTION_FEEDBACK_ACCURACY_DIV;
}
public void setTemporalAccuracy(double value) {
TEMPORAL_PREDICTION_FEEDBACK_ACCURACY_DIV=value;
}
@Override
public void event(Class event, Object[] args) {
if (event == Events.TaskDerive.class) {
Task derivedTask=(Task) args[0];
if(derivedTask.sentence.term instanceof Implication &&
(((Implication) derivedTask.sentence.term).getTemporalOrder()==TemporalRules.ORDER_FORWARD ||
((Implication) derivedTask.sentence.term).getTemporalOrder()==TemporalRules.ORDER_CONCURRENT)) {
if(!current_tasks.contains(derivedTask)) {
current_tasks.add(derivedTask);
}
}
}
else if (event == Events.ConceptBeliefRemove.class) {
Task removedTask=(Task) args[2]; //task is 3nd
if(current_tasks.contains(removedTask)) {
current_tasks.remove(removedTask);
}
}
else if (event == Events.InduceSucceedingEvent.class) {
Task newEvent = (Task)args[0];
DerivationContext nal= (DerivationContext)args[1];
if (newEvent.sentence.truth!=null) {
stm.add(newEvent);
while(stm.size()>MatchUpTo) {
stm.removeFirst();
}
}
temporalPredictionsAdapt(nal);
}
}
//check all predictive statements, match them with last events
public void temporalPredictionsAdapt(DerivationContext nal) {
if(TEMPORAL_PREDICTION_FEEDBACK_ACCURACY_DIV==0.0f) {
return;
}
ArrayList<Task> lastEvents=new ArrayList<Task>();
for (Task stmLast : stm) {
lastEvents.add(stmLast);
}
if(lastEvents.isEmpty()) {
return;
}
final long duration = nal.memory.param.duration.get();
ArrayList<Task> derivetasks=new ArrayList<Task>();
for(final Task c : current_tasks) { //a =/> b or (&/ a1...an) =/> b
boolean concurrent_conjunction=false;
Term[] args=new Term[1];
Implication imp=(Implication) c.sentence.term.clone();
boolean concurrent_implication=imp.getTemporalOrder()==TemporalRules.ORDER_CONCURRENT;
args[0]=imp.getSubject();
if(imp.getSubject() instanceof Conjunction) {
Conjunction conj=(Conjunction) imp.getSubject();
if(conj.temporalOrder==TemporalRules.ORDER_FORWARD || conj.temporalOrder==TemporalRules.ORDER_CONCURRENT) {
concurrent_conjunction=conj.temporalOrder==TemporalRules.ORDER_CONCURRENT;
args=conj.term; //in case of &/ this are the terms
}
}
int i=0;
boolean matched=true;
int off=0;
long expected_time=lastEvents.get(0).sentence.getOccurenceTime();
for(i=0;i<args.length;i++) {
//handling of intervals:
if(args[i] instanceof Interval) {
if(!concurrent_conjunction) {
expected_time+=((Interval)args[i]).getTime(nal.memory);
}
off++;
continue;
}
if(i-off>=lastEvents.size()) {
break;
}
//handling of other events, seeing if they match and are right in time
if(!Variables.hasSubstitute(Symbols.VAR_INDEPENDENT, args[i], lastEvents.get(i-off).sentence.term)) { //it didnt match, instead sth different unexpected happened
matched=false; //whether intermediate events should be tolerated or not was a important question when considering this,
break; //if it should be allowed, the sequential match does not matter only if the events come like predicted.
} else { //however I decided that sequence matters also for now, because then the more accurate hypothesis wins.
if(lastEvents.get(i-off).sentence.truth.getExpectation()<=0.5f) { //it matched according to sequence, but is its expectation bigger than 0.5? todo: decide how truth values of the expected events
//it didn't happen
matched=false;
break;
}
long occurence=lastEvents.get(i-off).sentence.getOccurenceTime();
boolean right_in_time=Math.abs(occurence-expected_time) < ((double)duration)/TEMPORAL_PREDICTION_FEEDBACK_ACCURACY_DIV;
if(!right_in_time) { //it matched so far, but is the timing right or did it happen when not relevant anymore?
matched=false;
break;
}
}
if(!concurrent_conjunction) {
expected_time+=duration;
}
}
if(concurrent_conjunction && !concurrent_implication) { //implication is not concurrent
expected_time+=duration; //so here we have to add duration
}
else
if(!concurrent_conjunction && concurrent_implication) {
expected_time-=duration;
} //else if both are concurrent, time has never been added so correct
//else if both are not concurrent, time was always added so also correct
//ok it matched, is the consequence also right?
if(matched && lastEvents.size()>args.length-off) {
long occurence=lastEvents.get(args.length-off).sentence.getOccurenceTime();
boolean right_in_time=Math.abs(occurence-expected_time)<((double)duration)/TEMPORAL_PREDICTION_FEEDBACK_ACCURACY_DIV;
if(right_in_time && Variables.hasSubstitute(Symbols.VAR_INDEPENDENT,imp.getPredicate(),lastEvents.get(args.length-off).sentence.term)) { //it matched and same consequence, so positive evidence
//c.sentence.truth=TruthFunctions.revision(c.sentence.truth, new TruthValue(1.0f,Parameters.DEFAULT_JUDGMENT_CONFIDENCE));
Sentence s2=new Sentence(c.sentence.term.clone(),Symbols.JUDGMENT_MARK,new TruthValue(1.0f,Parameters.DEFAULT_JUDGMENT_CONFIDENCE),new Stamp(nal.memory));
Task t=new Task(s2,new BudgetValue(Parameters.DEFAULT_JUDGMENT_PRIORITY,Parameters.DEFAULT_JUDGMENT_DURABILITY,s2.truth));
derivetasks.add(t);
} else { //it matched and other consequence, so negative evidence
// c.sentence.truth=TruthFunctions.revision(c.sentence.truth, new TruthValue(0.0f,Parameters.DEFAULT_JUDGMENT_CONFIDENCE));
Sentence s2=new Sentence(c.sentence.term.clone(),Symbols.JUDGMENT_MARK,new TruthValue(0.0f,Parameters.DEFAULT_JUDGMENT_CONFIDENCE),new Stamp(nal.memory));
Task t=new Task(s2,new BudgetValue(Parameters.DEFAULT_JUDGMENT_PRIORITY,Parameters.DEFAULT_JUDGMENT_DURABILITY,s2.truth));
derivetasks.add(t);
} //todo use derived task with revision instead
}
}
for(Task t: derivetasks) {
if(nal.derivedTask(t, false, false, null, null, false)) {
boolean debug=true;
}
}
ArrayList<Task> toDelete=new ArrayList<Task>();
for(Task t: current_tasks) {
Concept w=nal.memory.concept(t.sentence.term);
if(w==null) { //concept does not exist anymore, delete
toDelete.add(t);
}
}
for(Task t: toDelete) {
current_tasks.remove(t);
}
}
@Override
public boolean setEnabled(NAR n, boolean enabled) {
//Events.TaskDerive.class Events.ConceptBeliefRemove.class
n.memory.event.set(this, enabled, Events.InduceSucceedingEvent.class, Events.TaskDerive.class, Events.ConceptBeliefRemove.class);
return true;
}
}