package nars.lab.plugin.app.plan;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import nars.storage.Memory;
import nars.config.Parameters;
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.BudgetFunctions;
import nars.inference.TemporalRules;
import nars.inference.TruthFunctions;
import nars.io.Texts;
import nars.language.Conjunction;
import nars.language.Implication;
import nars.language.Interval;
import nars.language.Term;
import nars.operator.Operation;
import static nars.lab.plugin.app.plan.MultipleExecutionManager.isPlanTerm;
import nars.gui.graph.ImplicationGraph;
import nars.gui.graph.ImplicationGraph.Cause;
import nars.gui.graph.ImplicationGraph.PostCondition;
public class GraphExecutive {
public final Memory memory;
public final ImplicationGraph implication;
final int maxConsecutiveIntervalTerms = 1;
/** controls the relative weigting of edges and vertices for particle traversals */
double conceptExpectationFactor = 1.0;
double causeRelevanceFactor = 1.0;
double conceptPriorityFactor = 1.0;
double minEdgeCost = 1.0;
double costPerDelayMagniutde = 0.25;
public GraphExecutive(Memory memory, MultipleExecutionManager exec) {
super();
this.memory = memory;
this.implication = new ImplicationGraph(memory);
}
protected void accumulate(final Term t, final Cause[] path) {
for (final Cause s : path)
s.addActivity(1.0);
}
/** returns maximum value */
public double fadeAccumulatedSentences(double rate) {
double max = 0;
for (final Cause c: implication.edgeSet()) {
double vv = c.multActivity(rate);
if (vv > max) max = vv;
}
return max;
}
/** whether the Term is currently a valid goal for the implication graph to plan for */
public boolean isPlannable(final Term goal) {
PostCondition goalPostCondition = new PostCondition(goal);
/** must be in the graph and have at least one incoming edge */
if (implication.containsVertex(goalPostCondition)) {
return implication.inDegreeOf(goalPostCondition) > 0;
}
return false;
}
public class ParticlePath implements Comparable<ParticlePath> {
final public Term goal;
Cause[] bestPath;
double distance;
double score;
public ParticlePath(final Term goal, final List<Cause> path, final double cost) {
this.goal = goal;
addPath(path, cost);
}
public void addPath(final List<Cause> p, final double cost) {
double pathScore = 0;
for (Cause c : p)
pathScore += c.getTruth().getExpectation();
if ((this.bestPath == null) || (score < pathScore)) {
this.bestPath = p.toArray(new Cause[p.size()]);
this.distance = cost;
this.score = pathScore;
}
}
@Override
public final int compareTo(final ParticlePath o) {
return Double.compare(o.score(), score());
}
@Override
public String toString() {
return "[" + Texts.n4((float)score()) + "|" + Texts.n4((float)distance) + "] "/*+ target */ + " <- " + Arrays.toString(bestPath);
}
/** can be used to favor the total activation, or short distnce, or combinations of other factors */
private double score() {
return score;
}
}
public class ParticleActivation {
private final ImplicationGraph graph;
/** caches sentence costs while traversing */
transient public final Map<Cause, Double> sentenceCosts = new HashMap();
public final Map<Term, ParticlePath> termPaths = new HashMap();
TreeSet<ParticlePath> paths;
final boolean avoidCycles = true;
private int edgeDecisionPass = 0;
private int edgeDecisionFailCyclical = 0;
private int edgeDecisionFailInvalidVertex = 0;
private int edgeDecisionFailInactiveEdge = 0;
private int pathFailEmpty = 0;
private int pathFailNoOperation = 0;
private int pathsValid = 0;
private int numIterations = 0;
private final Term goal, goalPost;
/** optional required source vertex to reach */
private final Term source;
final int initialEdgeCosts = 512;
List<Cause> possibleEdge = new ArrayList();
double[] possibleEdgeCost = new double[initialEdgeCosts];
public ParticleActivation(ImplicationGraph graph, final Term goal, final Term goalPost) {
this(graph, goal, goalPost, null);
}
public ParticleActivation(ImplicationGraph graph, final Term goal, final Term goalPost, final Term source) {
this.graph = graph;
this.goal = goal;
this.goalPost = goalPost;
this.source = source;
}
final public void addEdgeCost(final Cause ca, final double c) {
possibleEdge.add(ca);
final int nes = possibleEdge.size();
if (nes == possibleEdgeCost.length) {
double[] n = new double[nes*2];
System.arraycopy(possibleEdgeCost, 0, n, 0, possibleEdgeCost.length);
possibleEdgeCost = n;
}
possibleEdgeCost[nes-1] = c;
}
public SortedSet<ParticlePath> activate(final boolean forward, int iterations, double distance) {
//TODO cache pathways in the graph for faster traversal. must store source leading edge, destination(s) and their distances
List<Cause> currentPath = new ArrayList();
sentenceCosts.clear();
for (int i = 0; i < iterations; i++) {
numIterations++;
currentPath.clear();
double energy = distance;
Term currentVertex = goalPost;
boolean choicesAvailable = false;
boolean operationTraversed = false;
while (energy > 0) {
if (!graph.containsVertex(currentVertex))
break;
Set<Cause> graphEdges = forward ?
graph.outgoingEdgesOf(currentVertex) :
graph.incomingEdgesOf(currentVertex);
possibleEdge.clear();
Cause currentSentence = null;
double totalProb = 0;
//remove edges which loop to the target goal precondition OR postcondition
for (final Cause s : graphEdges) {
Term etarget = forward ? s.effect : s.cause;
if ((avoidCycles) && (etarget == goalPost)) {
edgeDecisionFailCyclical++;
continue;
}
if (!validVertex(etarget)) {
edgeDecisionFailInvalidVertex++;
continue;
}
double ew = getTraversalCost(s, etarget);
//ignore if this edge will cost more energy than allowed
if (ew > energy)
continue;
currentSentence = s;
totalProb += 1.0 / ew;
addEdgeCost(currentSentence, ew);
edgeDecisionPass++;
if (etarget instanceof Operation) {
operationTraversed = true;
}
}
if (possibleEdge.isEmpty()) {
//particle went as far as it can
break;
}
Cause nextEdge;
int nextEdgeIndex;
if (possibleEdge.size() == 1) {
nextEdgeIndex = 0;
}
else {
choicesAvailable = true;
nextEdgeIndex = chooseEdge(totalProb);
}
nextEdge = possibleEdge.get(nextEdgeIndex);
currentPath.add(nextEdge);
energy -= possibleEdgeCost[nextEdgeIndex];
currentVertex = forward ? nextEdge.effect : nextEdge.cause;
if ((source!=null) && (nextEdge.cause.equals(source)))
break;
}
if (currentPath.isEmpty()) {
pathFailEmpty++;
continue;
}
if ((source!=null) && (!currentPath.get(currentPath.size()-1).cause.equals(source))) {
//this path is invalid because it doesnt terminate at the required source postcondition
continue;
}
if (!operationTraversed) {
pathFailNoOperation++;
continue;
}
ParticlePath ppath = termPaths.get(currentVertex);
if (ppath == null) {
termPaths.put(currentVertex,
ppath = new ParticlePath(goal, currentPath, distance - energy));
}
else {
ppath.addPath(currentPath, distance - energy);
}
pathsValid++;
if (!choicesAvailable) {
//we've found the only path, so and we dont need to iterate any further
break;
}
}
this.paths = new TreeSet(termPaths.values());
for (ParticlePath p : this.paths) {
accumulate(p.goal, p.bestPath);
//System.out.println(" " + p);
}
return this.paths;
}
/** total cost of a traversal, which includes the edge cost and the target vertex cost. any value > 1 */
public double getTraversalCost(final Cause s, final Term nextTerm) {
Double d = sentenceCosts.get(s);
if (d!=null)
return d;
double conceptExpectation, conceptPriority, delayCost = 0;
if (nextTerm instanceof Interval) {
Interval i = (Interval)nextTerm;
conceptExpectation = 1.0;
conceptPriority = 1.0;
delayCost = i.magnitude * costPerDelayMagniutde;
}
else if (nextTerm instanceof Operation) {
conceptExpectation = 1.0;
conceptPriority = 1.0;
}
else {
if (conceptExpectationFactor > 0)
conceptExpectation = getEffectiveExpectation(memory, nextTerm);
else
conceptExpectation = 0;
if (conceptPriorityFactor > 0)
conceptPriority = getEffectivePriority(memory, nextTerm);
else
conceptPriority = 0;
}
double causeRelevancy = causeRelevanceFactor > 0 ? getCauseRelevancy(s, goal) : 0.0;
double c = causeRelevanceFactor * (1.0 - causeRelevancy) +
conceptExpectationFactor * (1.0 - conceptExpectation) +
conceptPriorityFactor * (1.0 - conceptPriority);
c/= (causeRelevanceFactor + conceptExpectationFactor + conceptPriorityFactor);
c+= minEdgeCost;
c+= delayCost;
//System.out.println(" s " + s + " >> " + nextTerm + " : " + " = " + c);
sentenceCosts.put(s, c);
return c;
}
/** choose a sentence according to a random probability
* where lower cost = higher probability. */
public int chooseEdge(double totalProb) {
//TODO disallow edge that completes cycle back to target or traversed edge?
// probably an option to allow cycles
double r = Memory.randomNumber.nextDouble() * totalProb;
final int pes = possibleEdge.size();
int i;
for (i = 0; i < pes; i++) {
double v = possibleEdgeCost[i];
double edgeProb = 1.0 / v;
r -= edgeProb;
if (r <= 0) {
//selected the next Edge
return i;
}
}
return i-1;
}
public String getStatus() {
return "iterations=" + numIterations +
", pathsFound=" + paths.size() +
", pathsValid=" + pathsValid +
", pathEmpty=" + pathFailEmpty +
", pathNoOperations=" + pathFailNoOperation +
", edgeDecisionPass=" + edgeDecisionPass +
", edgeDecisionFailInactiveEdge=" + edgeDecisionFailInactiveEdge +
", edgeDecisionFailInvalidVertex=" + edgeDecisionFailInvalidVertex +
", edgeDecisionFailCyclical=" + edgeDecisionFailCyclical;
}
public boolean validVertex(final Term x) {
return true;
}
//public void reset()
}
/** returns (no relevancy) 0..1.0 (high relevancy) */
public double getCauseRelevancy(final Cause c, final Term goal) {
// //how desired is the implication?
// Concept w=memory.concept(c.getImplication().getPredicate());
// if(w!=null && w.getDesire()!=null) {
// return Math.max(0.0, Math.min(1.0, c.getTruth().getExpectation()*w.getDesire().getExpectation()));
// }
// return Math.max(0.0, Math.min(1.0, c.getTruth().getExpectation()));//+c.getRelevancy(goal)));//getCauseRelevancy(c) * c.getRelevancy(goal);
if (c.getTruth().getExpectation() < 0.5f) {
System.err.println(c + " has inverse truth which could be used to negatively weight results");
}
return c.getTruth().getExpectation();
}
/** returns (no relevancy) 0..1.0 (high relevancy) */
public double getCauseRelevancy(final Cause c) {
return getCauseRelevancy(c, null);
}
public static double getActualPriority(final Memory memory, final Term t) {
double p;
Concept c = memory.concept(t);
if ((c!=null) && (!c.beliefs.isEmpty())) {
//Sentence bestBelief = c.beliefs.get(0);
//if (bestBelief!=null)
// return c.getPriority() * bestBelief.truth.getExpectation();
return c.getPriority(); //it is not expectation cause the goal could be to make a judgement true but also false
}
//System.err.println("No Concept priority available for " + t);
//Probably an input term, so make it high priority
return 1;
}
/** between 0...1 */
public static float getActualExpectation(final Memory memory, final Term t) {
double p;
Concept c = memory.concept(t);
if ((c!=null) && (!c.beliefs.isEmpty())) {
Sentence bestBelief = c.beliefs.get(0).sentence;
if (bestBelief!=null)
return bestBelief.truth.getExpectation();
}
//System.err.println("No Concept confidence available for " + t);
//if no concept confidence is available, assume 0
return 0f;
}
/** returns 0..1.0, 1.0 being highest priority, 0 = no priority */
public static double getEffectivePriority(final Memory memory, final Term current) {
double p;
//default priority for intervals
if ((current instanceof Interval) || (current instanceof Operation))
p = 1.0f;
//get the priority for the postcondition's actual concept
else if (current instanceof PostCondition)
p = getActualPriority(memory, ((PostCondition)current).term[0]);
else
p = getActualPriority(memory, current);
return p;
}
public static float getEffectiveExpectation(final Memory memory, final Term current) {
//get the priority for the postcondition's actual concept
if (current instanceof PostCondition)
return getActualExpectation(memory, ((PostCondition)current).term[0]);
else
return getActualExpectation(memory, current);
}
public class ParticlePlan implements Comparable<ParticlePlan> {
public final Cause[] path;
public final List<Term> sequence;
public final double distance;
public final double pathScore;
public final TruthValue truth;
public final BudgetValue budget;
//private float minConf;
private Task solution;
private Task goal;
// if (path.length == 0) return 0;
//
// float min = Float.MAX_VALUE;
// for (final Sentence s : path) {
// float c = s.truth.getConfidence();
// if (c < min)
// min = c;
// }
// return min;
public ParticlePlan(Cause[] path, List<Term> sequence, double pathScore, double distance) {
this.path = path;
this.sequence = sequence;
this.pathScore = pathScore;
this.distance = distance;
//this.minConf = 1.0f;
TruthValue tr=null;
for (final Cause s : path) {
if(tr==null) {
tr=s.getTruth();
} else {
tr=TruthFunctions.deduction(tr, s.getTruth());
}
//float c = s.getTruth().getConfidence();
//if (c < minConf) {
// minConf = c;
//}
}
truth=tr;
//float freq = 1.0f;
//float freq = 0.5f + (minConf)/2f;
//truth = new TruthValue(freq, minConf);
budget = new BudgetValue(1.0f, Parameters.DEFAULT_GOAL_DURABILITY,
BudgetFunctions.truthToQuality(truth));
budget.andPriority(score() / path.length);
}
public float score() {
return (float)(/*minConf * */pathScore);
}
@Override
public final int compareTo(final ParticlePlan o) {
int i = Double.compare(o.score(), score());
if ((i == 0) && (o != this)) {
return -1;
}
return i;
}
public TruthValue getTruth() {
return truth;
}
@Override
public String toString() {
return "[" + score() + "|" + distance + "] " + sequence;
}
public Task planTask(Concept c, Task goal, Term goalTerm, char punctuation) {
Cause currentEdge = path[path.length-1];
Stamp stamp = new Stamp(goal.sentence.stamp, currentEdge.getStamp(), memory.time());
//todo: evidental base hm
//memory.setTheNewStamp(stamp);
//memory.setCurrentTask(task);
//remove final element from path if it's equal to target
/*if (seq.get(seq.size()-1).equals(target)) {
seq.remove(seq.size()-1);
}*/
Term subj = sequence.size() > 1 ?
Conjunction.make(sequence.toArray(new Term[sequence.size()]), TemporalRules.ORDER_FORWARD)
:
sequence.get(0);
//val=TruthFunctions.abduction(val, newEvent.sentence.truth);
Term imp = Implication.make(subj, goalTerm, TemporalRules.ORDER_FORWARD);
if (imp == null) {
throw new RuntimeException("Invalid implication: " + subj + " =\\> " + goalTerm);
}
this.goal = goal;
this.solution = new Task(new Sentence(imp, punctuation, truth, stamp), budget, goal);
return solution;
}
/** the task that caused the need for this plan */
public Task getGoal() { return goal; }
/** the plan's created task */
public Task getSolution() { return solution; }
}
protected void particlePredict(final Term source, final double distance, final int particles) {
ParticleActivation act = new ParticleActivation(implication, source, source, null);
SortedSet<ParticlePath> paths = act.activate(true, particles, distance);
if (!paths.isEmpty())
System.out.println(source + " predicts: " + paths);
}
public TreeSet<ParticlePlan> particlePlan(final Term target, final double distance, final int particles) {
return particlePlan(target, distance, particles, null);
}
/**
*
* @param target starting goal vertex to plan from
* @param distance
* @param particles
* @param source required source vertex to reach, or null if none is required (accepts any)
* @return
*/
public TreeSet<ParticlePlan> particlePlan(final Term target, final double distance, final int particles, final Term source) {
PostCondition targetPost = new PostCondition(target);
if (!implication.containsVertex(targetPost)) {
//System.out.println(" plan for " + target + ": missing postCondition vertex");
return null;
}
ParticleActivation act = new ParticleActivation(implication, target, targetPost, source) {
@Override public boolean validVertex(final Term x) {
//additional restriction on path's vertices
return !targetPost.equals(x);
}
};
SortedSet<ParticlePath> roots = act.activate(false, particles, distance);
if (roots == null) {
return null;
}
/* if (memory.emitting(ParticlePath.class)) {
for (ParticlePath pp : roots) {
memory.emit(ParticlePath.class, target, pp);
}
}*/
TreeSet<ParticlePlan> plans = new TreeSet();
for (final ParticlePath pp : roots) {
Cause[] path = pp.bestPath;
if (path.length == 0)
throw new RuntimeException("ParticlePath empty: " + pp);
int operations = 0;
List<Term> seq = new ArrayList(path.length);
//Calculate path back to target
long accumulatedDelay = 0;
for (int i = path.length-1; i >=0; ) {
Cause s = path[i];
Term term = s.cause;
i--; //next impl
if (isPlanTerm(term)) {
boolean isInterval = term instanceof Interval;
if (!isInterval) {
if (accumulatedDelay > 0) {
seq.addAll(Interval.intervalTimeSequence(
accumulatedDelay, maxConsecutiveIntervalTerms, memory) );
accumulatedDelay = 0;
}
seq.add(term);
}
else {
Interval in = (Interval)term;
long time = in.getTime(memory);
accumulatedDelay += time;
}
}
else {
//accumulate delay if the temporal rule involves time difference??
/*
if (nonIntervalAdded) {
////ignore prefix intervals
int temporal = (s.content).getTemporalOrder();
if (temporal == TemporalRules.ORDER_FORWARD) {
accumulatedDelay++;
}
}
*/
}
if (term instanceof Operation)
operations++;
}
//TODO check this prior to above loop, to avoid wasting that effort
if (operations == 0)
continue;
if (seq.isEmpty())
continue;
int lastTerm = seq.size()-1;
if (seq.get(lastTerm) instanceof Interval)
seq.remove(lastTerm);
//System.out.println(" cause: " + Arrays.toString(path));
ParticlePlan rp = new ParticlePlan(path, seq, pp.score(), pp.distance);
//System.out.println(" +path: " + pp);
plans.add(rp);
}
return plans;
}
protected Task planTask(nars.control.DerivationContext nal, ParticlePlan plan, Concept c, Task task, Term target, char punctuation) {
Task newTask = plan.planTask(c, task, target, punctuation);
//it comes through temporal induction with correct truth value anyway
//nal.derivedTask(newTask, false, true, null, null); //and if this is a implication then wrong
// memory.executive.addExecution(c, newTask);
return newTask;
}
public int plan(final nars.control.DerivationContext nal, Concept c, Task task, Term target, int particles, double searchDistance, char punctuation, int maxTasks) {
TreeSet<ParticlePlan> plans = particlePlan(target, searchDistance, particles);
int n = 0;
boolean emittingPlans = memory.emitting(ParticlePlan.class);
for (ParticlePlan p : plans) {
planTask(nal, p, c, task, target, punctuation);
if (emittingPlans) {
memory.emit(ParticlePlan.class, target, p);
}
if (n++ == maxTasks)
break;
}
// memory.logic.PLAN_TASK_PLANNED.commit(n);
return n;
}
// protected void plan(Task task, Task __not_used_newEvent) {
//
// Term t = task.getContent();
// if (t == null) return;
//
// if ((t instanceof Implication) && (t.getTemporalOrder()!=TemporalRules.ORDER_NONE)) {
//
// //System.out.println("plan: task=" + task + " newEvent=" + __not_used_newEvent);
//
// Implication i = (Implication) t;
// Term target;
//
// //implication.add(task.sentence);
//
// if (i.getTemporalOrder() == TemporalRules.ORDER_FORWARD) {
// target = i.getPredicate();
// } else {
// //TODO reverse it
// target = i.getSubject();
// }
//
// if (target != null) {
// System.err.println("plan: " + target);
// System.exit(1);
// ///plan(task, target, ...);
// }
// }
//
//// System.out.println("Goals");
//// for (Task t : tasks) {
//// System.out.println(t + " " + t.getParentBelief());
//// //System.out.println(getImplicationPath(t.getParentBelief()));
//// }
//// System.out.println();
// }
// public String getImplicationPath(Sentence s) {
// Term t = s.content;
// if (t instanceof Implication) {
// return getImplicationPath(((Implication)t).getPredicate());
// }
// else {
// return getImplicationPath(t);
// }
// //return "";
// }
// public String getImplicationPath(Term t) {
// KShortestPaths ksp = new KShortestPaths(implication, t, 5);
// return ksp.getPaths(t).toString();
//
// }
//
/** TODO */
public void reset() {
}
// public boolean isActionable(final Task newEvent, Memory mem) {
// /*if(!((newEvent.isInput()) || (newEvent.getCause()!=null))) {
// return false;
// }*/
//
// Term newcontent = newEvent.sentence.content;
// if (newcontent instanceof Operation) {
// Term pred = ((Operation) newcontent).getPredicate();
// if (pred.equals(mem.getOperator("^want")) || pred.equals(mem.getOperator("^believe"))) {
// return false;
// }
// }
//
// plan(newEvent, (Task)null);
//
// return true;
// }
//
// public boolean isActionable(final Task task, final Task newEvent) {
//
// plan(task, newEvent);
// return true;
//
// /*
// if (task.sentence.stamp.getOccurrenceTime() == Stamp.ETERNAL) {
// return false;
// }
//
// if (!task.sentence.isJudgment()) {
// return false;
// }
// */
//// if ((newEvent == null)
//// || (rankBelief(newEvent.sentence) < rankBelief(task.sentence))) {
////
//// return true;
////
//// /*return
//// ((shortTermMemory.isEmpty()
//// ||
//// !equalSubTermsInRespectToImageAndProduct(
//// shortTermMemory.getLast().getContent(),
//// task.getContent()))
//// );
//// */
//// }
// //return false;
// }
// /** doesnt work yet and may not be necessary */
//
// @Deprecated public static class CandidateSequenceRoot implements Comparable {
// public final Term root;
// public final double distance;
//
// public CandidateSequenceRoot(Term root, double distance) {
// this.root = root;
// this.distance = distance;
// }
//
// @Override
// public int compareTo(Object o) {
// if (o instanceof CandidateSequenceRoot) {
// CandidateSequenceRoot csr = (CandidateSequenceRoot)o;
// return Double.compare(csr.distance, distance);
// }
// return -1;
// }
//
// @Override
// public String toString() {
// return root.toString() + "|" + distance;
// }
//
//
// }
//
//
//
// @Deprecated protected List<Term> planExhaustive(Term target, double remainingDistance, List<Term> parentPath, double[] distResult) {
//
// if (remainingDistance <= 0)
// return Collections.EMPTY_LIST;
//
// ClosestFirstIterator<Term, Sentence> cfi = new ClosestFirstIterator<Term, Sentence>(new EdgeReversedGraph(implication), target, remainingDistance);
//
//
// if (parentPath == null)
// parentPath = Collections.EMPTY_LIST;
//
// SortedSet<CandidateSequenceRoot> roots = new TreeSet();
//
// while (cfi.hasNext()) {
// Term v = cfi.next();
//
// double length = cfi.getShortestPathLength(v);
// if (length == 0) continue;
//
//
// //dont settle for 1-edge hop from target, we need further
// if (implication.getEdgeTarget( cfi.getSpanningTreeEdge(v) ).equals(target) ) {
// //System.out.println(v + " " + cfi.getSpanningTreeEdge(v) + " ==? " + target);
// continue;
// }
//
// if ((!v.equals(target)) /*&& (!parentPath.contains(v))*/) {
// //ignore intervals as roots
// if (!(v instanceof Interval))
// roots.add(new CandidateSequenceRoot(v, length));
// }
// }
// if (roots.isEmpty())
// return Collections.EMPTY_LIST;
//
// double initialRemainingDistance = remainingDistance;
//
//
// for (final CandidateSequenceRoot csroot : roots) {
// final Term root = csroot.root;
// if (root == target) continue;
//
// remainingDistance = initialRemainingDistance - csroot.distance;
// if (remainingDistance < 0)
// continue;
//
// //Calculate path back to target
//
// List<Term> path = new ArrayList();
// Term current = root;
// Sentence currentEdge = null;
// int operations = 0;
//
// while (current != target) {
//
// boolean isOperation = (current instanceof Operation);
// if (isOperation)
// operations++;
//
// //only include Operations and Intervals
// if (isOperation || (current instanceof Interval)) {
// path.add(current);
// }
// //but if it's something else, we need to transclude it because it may indicate other necessary preconditions
// else if ((!current.equals(target))) {
//
// //Transclude best subpath iff vertex has other preconditions
//
// /*if (implication.outgoingEdgesOf(current).size() > 1) {
// //ignore a preconditon with a postcondition
// continue;
// }*/
//
// //TODO should the precondition branches be sorted, maybe shortest first?
//
// boolean goodPreconditions = true;
// Set<Sentence> preconditions = implication.incomingEdgesOf(current);
// for (Sentence s : preconditions) {
// if (!s.equals(currentEdge)) {
// //System.out.println(" precondition: " + current + " = " + s);
// Term preconditionSource = implication.getEdgeSource(s);
//
// if (parentPath!=null) {
// /*if (!parentPath.contains(preconditionSource))*/ {
// if (!preconditionSource.equals(target) ) {
// List<Term> preconditionPlan = null;
// try {
// double[] d = new double[1];
// preconditionPlan = planExhaustive(preconditionSource, remainingDistance, path, d);
//
// if (!((preconditionPlan.size() == 0) || (preconditionPlan == null))) {
// if (remainingDistance - d[0] > 0) {
// if (!preconditionPlan.contains(preconditionSource)) { path.addAll(preconditionPlan);
// if (validPlanComponent(preconditionSource))
// path.add(preconditionSource);
// }
// remainingDistance -= d[0];
// }
// else {
// //ignore this condition sequence because it would exceed the search distance
// System.out.println(" excess subpath: " + remainingDistance + " " + d[0] + " " + preconditionPlan);
// goodPreconditions = false;
// break;
//
// }
// }
// }
// catch (Throwable e) {
//
// System.err.println(e + " " +target + " " + path + " " + preconditionSource + " " + parentPath);
// System.err.println(" " + preconditionPlan);
// new Window("Implications", new JGraphXGraphPanel(memory.executive.graph.implication)).show(500,500);
// try {
// System.in.read();
// } catch (IOException ex) {
// Logger.getLogger(GraphExecutive.class.getName()).log(Level.SEVERE, null, ex);
// }
//
// }
//
// }
//
// }
// }
//
// }
//
// }
//
// if (!goodPreconditions)
// break;
// }
//
// currentEdge = cfi.getSpanningTreeEdge(current);
// if (currentEdge == null) {
// //Should mean we have returned to target
// break;
// }
// current = implication.getEdgeTarget(currentEdge);
// }
//
// if (operations == 0)
// continue;
// if (path.size() < 2)
// continue;
//
//
// System.out.println(path + " " + root + " in " + roots);
//
// distResult[0] = initialRemainingDistance - remainingDistance;
// return path;
// }
//
// return Collections.EMPTY_LIST;
// }
}