bdi4jade

Preliminar version of Learning Based Plan Selection extension

1/16/2015 1:37:40 AM

Details

diff --git a/bdi-jade-extensions/.classpath b/bdi-jade-extensions/.classpath
index e0b1538..4b3c509 100644
--- a/bdi-jade-extensions/.classpath
+++ b/bdi-jade-extensions/.classpath
@@ -1,10 +1,11 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/bdi-jade"/>
-	<classpathentry kind="lib" path="/bdi-jade/lib/commons-logging-1.1.3.jar"/>
-	<classpathentry kind="lib" path="/bdi-jade/lib/jade-4.3.2.jar"/>
-	<classpathentry kind="lib" path="/bdi-jade/lib/log4j-1.2.17.jar"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/bdi-jade"/>
+	<classpathentry kind="lib" path="/bdi-jade/lib/commons-logging-1.1.3.jar"/>
+	<classpathentry kind="lib" path="/bdi-jade/lib/jade-4.3.2.jar"/>
+	<classpathentry kind="lib" path="/bdi-jade/lib/log4j-1.2.17.jar"/>
+	<classpathentry kind="lib" path="src/bdi4jade/extension/planselection/learningbased/lib/weka.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/InfluenceFactor.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/InfluenceFactor.java
new file mode 100644
index 0000000..e2a98e4
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/InfluenceFactor.java
@@ -0,0 +1,21 @@
+package bdi4jade.extension.planselection.learningbased;
+
+import bdi4jade.belief.Belief;
+
+public class InfluenceFactor {
+
+	private Belief<?, ?> belief;
+
+	public InfluenceFactor(Belief<?, ?> belief) {
+		this.belief = belief;
+	}
+
+	public String getBeliefName() {
+		return (String) this.belief.getName();
+	}
+
+	public Object getBeliefValue() {
+		return this.belief.getValue();
+	}
+
+}
\ No newline at end of file
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningAlgorithm.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningAlgorithm.java
new file mode 100644
index 0000000..f7ad425
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningAlgorithm.java
@@ -0,0 +1,10 @@
+package bdi4jade.extension.planselection.learningbased;
+
+import bdi4jade.goal.Softgoal;
+import bdi4jade.plan.Plan;
+
+public interface LearningAlgorithm {
+
+	public double predictExpectedContribution(Plan plan, Softgoal softgoal);
+
+}
\ No newline at end of file
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/learningalgorithm/LinearRegressionAlgorithm.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/learningalgorithm/LinearRegressionAlgorithm.java
new file mode 100644
index 0000000..107cbd5
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/learningalgorithm/LinearRegressionAlgorithm.java
@@ -0,0 +1,92 @@
+package bdi4jade.extension.planselection.learningbased.learningalgorithm;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Map;
+
+import weka.classifiers.functions.LinearRegression;
+import weka.core.DenseInstance;
+import weka.core.Instance;
+import weka.core.Instances;
+import bdi4jade.extension.planselection.learningbased.LearningAlgorithm;
+import bdi4jade.extension.planselection.learningbased.PlanMetadata;
+import bdi4jade.extension.planselection.learningbased.util.Utils;
+import bdi4jade.goal.Softgoal;
+import bdi4jade.plan.Plan;
+
+public class LinearRegressionAlgorithm implements LearningAlgorithm {
+
+	private Instances trainingInstances;
+	private LinearRegression model;
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public double predictExpectedContribution(Plan plan, Softgoal softgoal) {
+
+		double prediction = 1;
+		PlanMetadata planMetadata = ((Map<Softgoal, PlanMetadata>) plan
+				.getMetadata(PlanMetadata.METADATA_NAME)).get(softgoal);
+
+		if (planMetadata.getPlanExecutionsCounter() < PlanMetadata.MIN_PLAN_EXECUTIONS) {
+			String filePath = "instances/"
+					+ plan.getClass().getSimpleName().toLowerCase() + "_"
+					+ softgoal + ".arff";
+			if (!new File(filePath).exists()) {
+				try {
+					Utils.writeToFile(filePath,
+							planMetadata.getArffFileHeader());
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+			planMetadata.increasePlanExecutionsCounter();
+		} else {
+			learnFromTrainingSet(plan, softgoal);
+
+			int numOfFactors = planMetadata.getInfluenceFactors().size();
+
+			Instance instance = new DenseInstance(numOfFactors);
+
+			for (int i = 0; i < numOfFactors; i++) {
+				instance.setValue(trainingInstances.attribute(i),
+						(double) planMetadata.getInfluenceFactors().get(i)
+								.getBeliefValue());
+			}
+
+			try {
+				prediction = model.classifyInstance(instance);
+				// System.out.println("Current Instance (" + instance + "): " +
+				// prediction);
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+
+		return prediction;
+	}
+
+	private void learnFromTrainingSet(Plan plan, Softgoal softgoal) {
+		try {
+			trainingInstances = new Instances(new BufferedReader(
+					new FileReader("instances/"
+							+ plan.getClass().getSimpleName().toLowerCase()
+							+ "_" + softgoal + ".arff")));
+
+			trainingInstances
+					.setClassIndex(trainingInstances.numAttributes() - 1);
+
+			model = new LinearRegression();
+			model.buildClassifier(trainingInstances);
+
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+}
\ No newline at end of file
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedCapability.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedCapability.java
new file mode 100644
index 0000000..991fedc
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedCapability.java
@@ -0,0 +1,24 @@
+package bdi4jade.extension.planselection.learningbased;
+
+import bdi4jade.annotation.Belief;
+import bdi4jade.core.Capability;
+import bdi4jade.extension.planselection.utilitybased.SoftgoalPreferences;
+
+public class LearningBasedCapability extends Capability {
+
+	private static final long serialVersionUID = -1044132085270106726L;
+
+	@Belief
+	protected SoftgoalPreferences softgoalPreferences = new SoftgoalPreferences();
+
+	/*
+	 * Passando o LearningAlgorithm como parâmetro aqui ele será instanciado no
+	 * agente. Assim será possível definir diferentes algoritmos de aprendizado
+	 * para diferentes agentes.
+	 */
+	public LearningBasedCapability(LearningAlgorithm learningAlgorithm) {
+		setPlanSelectionStrategy(new LearningBasedPlanSelectionStrategy(
+				learningAlgorithm));
+	}
+
+}
\ No newline at end of file
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanBody.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanBody.java
new file mode 100644
index 0000000..3bba0f1
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanBody.java
@@ -0,0 +1,35 @@
+package bdi4jade.extension.planselection.learningbased;
+
+import java.util.Collection;
+import java.util.Map;
+
+import bdi4jade.goal.Softgoal;
+import bdi4jade.plan.planbody.AbstractPlanBody;
+
+public abstract class LearningBasedPlanBody extends AbstractPlanBody {
+
+	private static final long serialVersionUID = -5064965263121492233L;
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public void onStart() {
+		Collection<PlanMetadata> planMetadata = ((Map<Softgoal, PlanMetadata>) this
+				.getPlan().getMetadata(PlanMetadata.METADATA_NAME)).values();
+
+		for (PlanMetadata metadata : planMetadata) {
+			metadata.getNotifiedAtStartedPlanExecution();
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public int onEnd() {
+		Collection<PlanMetadata> planMetadata = ((Map<Softgoal, PlanMetadata>) this
+				.getPlan().getMetadata(PlanMetadata.METADATA_NAME)).values();
+
+		for (PlanMetadata metadata : planMetadata) {
+			metadata.getNotifiedAtEndedPlanExecution();
+		}
+		return super.onEnd();
+	}
+}
\ No newline at end of file
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanSelectionStrategy.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanSelectionStrategy.java
new file mode 100644
index 0000000..dc83e44
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanSelectionStrategy.java
@@ -0,0 +1,63 @@
+package bdi4jade.extension.planselection.learningbased;
+
+import java.util.Set;
+
+import bdi4jade.extension.planselection.utilitybased.SoftgoalPreferences;
+import bdi4jade.goal.Goal;
+import bdi4jade.goal.Softgoal;
+import bdi4jade.plan.Plan;
+import bdi4jade.reasoning.AbstractReasoningStrategy;
+import bdi4jade.reasoning.PlanSelectionStrategy;
+
+public class LearningBasedPlanSelectionStrategy extends
+		AbstractReasoningStrategy implements PlanSelectionStrategy {
+
+	private LearningAlgorithm learningAlgorithm;
+
+	/*
+	 * Instanciarei um algoritmo de aprendizagem aqui, e.g. public
+	 * UtilityBasedPlanSelectionStrategy(LearningAlgorithm la){}.
+	 * LearningAlgorithm será uma interface que obrigará a criação de uma função
+	 * getValue() ou algo do tipo. Criarei uma série de classes (nesse momento
+	 * apenas duas) que implementam essa interface, e.g.
+	 * LinearRegressionAlgorithm e SVMAlgorithm, que serão criadas no pacote
+	 * implementation.
+	 */
+	public LearningBasedPlanSelectionStrategy(
+			LearningAlgorithm learningAlgorithm) {
+		this.learningAlgorithm = learningAlgorithm;
+	}
+
+	@Override
+	public Plan selectPlan(Goal goal, Set<Plan> candidatePlans) {
+		Plan selectedPlan = null;
+		Double maxContribution = null;
+
+		for (Plan plan : candidatePlans) {
+			double contribution = 0;
+
+			SoftgoalPreferences preferences = (SoftgoalPreferences) plan
+					.getPlanLibrary().getCapability().getBeliefBase()
+					.getBelief(SoftgoalPreferences.NAME);
+
+			for (Softgoal softgoal : capability.getMyAgent().getSoftgoals()) {
+				Double preference = preferences
+						.getPreferenceForSoftgoal(softgoal);
+
+				if (preference != null) {
+					double expectedContribution = learningAlgorithm
+							.predictExpectedContribution(plan, softgoal);
+					contribution += preference * expectedContribution;
+				}
+			}
+
+			if (selectedPlan == null || maxContribution < contribution) {
+				selectedPlan = plan;
+				maxContribution = contribution;
+			}
+
+		}
+		return selectedPlan;
+	}
+
+}
\ No newline at end of file
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/lib/weka.jar b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/lib/weka.jar
new file mode 100644
index 0000000..e5bc6c0
Binary files /dev/null and b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/lib/weka.jar differ
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/OptimizationFunction.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/OptimizationFunction.java
new file mode 100644
index 0000000..ca87a99
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/OptimizationFunction.java
@@ -0,0 +1,5 @@
+package bdi4jade.extension.planselection.learningbased;
+
+public enum OptimizationFunction {
+	MINIMIZE, MAXIMIZE;
+}
\ No newline at end of file
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/Outcome.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/Outcome.java
new file mode 100644
index 0000000..30e7cd0
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/Outcome.java
@@ -0,0 +1,26 @@
+package bdi4jade.extension.planselection.learningbased;
+
+public abstract class Outcome {
+
+	private String name;
+
+	public Outcome(String name) {
+		this.name = name;
+	}
+
+	public String getName() {
+		return this.name;
+	}
+
+	/*
+	 * It may be a function that return the measurement of the outcome. If it's
+	 * time measurement, it can be done through calculation of the difference
+	 * between final and initial time execution.
+	 */
+	public abstract double getMeasurement();
+	
+	public void startMeasurement() {}
+	
+	public void endMeasurement() {}
+	
+}
\ No newline at end of file
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/PlanMetadata.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/PlanMetadata.java
new file mode 100644
index 0000000..68156c7
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/PlanMetadata.java
@@ -0,0 +1,129 @@
+package bdi4jade.extension.planselection.learningbased;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import bdi4jade.extension.planselection.learningbased.util.Utils;
+import bdi4jade.goal.Softgoal;
+import bdi4jade.plan.Plan;
+
+public class PlanMetadata {
+
+	public static final String METADATA_NAME = PlanMetadata.class
+			.getSimpleName();
+	
+	public static final int MIN_PLAN_EXECUTIONS = 50;
+	
+	private Plan plan;
+	private Softgoal softgoal;
+	private Outcome outcome;
+	private OptimizationFunction optFunction;
+	private ArrayList<InfluenceFactor> influenceFactors;
+	private String currentInstance;
+	private int planExecutionsCounter;
+
+	public PlanMetadata(Plan plan, Softgoal softgoal, Outcome outcome,
+			OptimizationFunction optFunction) {
+		this.plan = plan;
+		this.softgoal = softgoal;
+		this.outcome = outcome;
+		this.optFunction = optFunction;
+		this.influenceFactors = new ArrayList<>();
+		this.currentInstance = new String();
+		this.planExecutionsCounter = 0;
+	}
+
+	public PlanMetadata(Plan plan, Softgoal softgoal, Outcome outcome,
+			OptimizationFunction optFunction,
+			ArrayList<InfluenceFactor> influenceFactors) {
+		this.plan = plan;
+		this.softgoal = softgoal;
+		this.outcome = outcome;
+		this.optFunction = optFunction;
+		this.influenceFactors = influenceFactors;
+		this.currentInstance = new String();
+		this.planExecutionsCounter = 0;
+	}
+
+	public void addInfluenceFactor(InfluenceFactor influenceFactor) {
+		this.influenceFactors.add(influenceFactor);
+	}
+
+	public Outcome getOutcome() {
+		return this.outcome;
+	}
+
+	public OptimizationFunction getOptimizationFunction() {
+		return this.optFunction;
+	}
+
+	public ArrayList<InfluenceFactor> getInfluenceFactors() {
+		return this.influenceFactors;
+	}
+	
+	public int getPlanExecutionsCounter() {
+		return this.planExecutionsCounter;
+	}
+	
+	public void increasePlanExecutionsCounter() {
+		this.planExecutionsCounter++;
+	}
+
+	public void getNotifiedAtStartedPlanExecution() {
+		updateCurrentInstance();
+		this.outcome.startMeasurement();
+	}
+
+	public void getNotifiedAtEndedPlanExecution() {
+		this.outcome.endMeasurement();
+		this.currentInstance = currentInstance + this.outcome.getMeasurement();
+		saveInstance();
+	}
+
+	private void updateCurrentInstance() {
+		for (InfluenceFactor influenceFactor : influenceFactors) {
+			this.currentInstance = this.currentInstance
+					+ influenceFactor.getBeliefValue() + ",";
+		}
+	}
+
+	private void saveInstance() {
+		String filePath = "instances/"
+				+ this.plan.getClass().getSimpleName().toLowerCase() + "_"
+				+ this.softgoal + ".arff";
+		try {
+			Utils.writeToFile(filePath, this.currentInstance);
+		} catch (IOException e) {
+			System.out.println("A problem occurred when trying to save a context instance!");
+			e.printStackTrace();
+		}
+		this.currentInstance = "";
+	}
+
+	/*
+	 * This method returns a string in the format:
+	 * @relation relation
+	 * @attribute attr1 numeric 
+	 * @attribute attr2 numeric
+	 * @data
+	 */
+	public String getArffFileHeader() {
+		String lineSeparator = System.getProperty("line.separator");
+		String relation = "@relation " + this.plan.getClass().getSimpleName()
+				+ "-" + this.softgoal + lineSeparator;
+
+		StringBuilder header = new StringBuilder();
+		header.append(relation);
+
+		for (InfluenceFactor influenceFactor : influenceFactors) {
+			header.append("@attribute ");
+			header.append(influenceFactor.getBeliefName());
+			header.append(" numeric");
+			header.append(lineSeparator);
+		}
+
+		header.append("@data");
+		header.append(lineSeparator);
+		return header.toString();
+	}
+}
\ No newline at end of file
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/util/Utils.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/util/Utils.java
new file mode 100644
index 0000000..c1fdc73
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/util/Utils.java
@@ -0,0 +1,22 @@
+package bdi4jade.extension.planselection.learningbased.util;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class Utils {
+
+	public Utils() {
+	}
+
+	public static void writeToFile(String filePath, String text) throws IOException {
+		FileWriter writer = new FileWriter(filePath, true);
+		
+		PrintWriter lineWriter = new PrintWriter(writer);
+		lineWriter.printf("%s" + "%n", text);
+		
+		lineWriter.close();
+		
+	}
+
+}
\ No newline at end of file