bdi4jade

Learning Based Plan Selection extension supporting nominal

3/10/2015 11:38:17 PM

Details

diff --git a/bdi-jade-extensions/.gitignore b/bdi-jade-extensions/.gitignore
index 5e56e04..a56cb9b 100644
--- a/bdi-jade-extensions/.gitignore
+++ b/bdi-jade-extensions/.gitignore
@@ -1 +1,2 @@
 /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
index e2a98e4..87591a2 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/InfluenceFactor.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/InfluenceFactor.java
@@ -2,18 +2,40 @@ package bdi4jade.extension.planselection.learningbased;
 
 import bdi4jade.belief.Belief;
 
-public class InfluenceFactor {
+/**
+ * An abstraction of an influence factor which is context variable and maps to a
+ * belief. An influence factor affects plan outcomes.
+ * 
+ * @author João Faccin
+ */
+public abstract class InfluenceFactor {
 
 	private Belief<?, ?> belief;
 
+	/**
+	 * Creates a new influence factor and maps it to a belief.
+	 * 
+	 * @param belief
+	 *            A belief to be mapped.
+	 */
 	public InfluenceFactor(Belief<?, ?> belief) {
 		this.belief = belief;
 	}
 
+	/**
+	 * Returns the name of the mapped belief.
+	 * 
+	 * @return Belief's name
+	 */
 	public String getBeliefName() {
 		return (String) this.belief.getName();
 	}
 
+	/**
+	 * Returns the value of mapped belief.
+	 * 
+	 * @return Belief's value.
+	 */
 	public Object getBeliefValue() {
 		return this.belief.getValue();
 	}
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningAlgorithm.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningAlgorithm.java
index f7ad425..9d21c0f 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningAlgorithm.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningAlgorithm.java
@@ -1,10 +1,134 @@
 package bdi4jade.extension.planselection.learningbased;
 
+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.core.DenseInstance;
+import weka.core.Instance;
+import weka.core.Instances;
+import bdi4jade.extension.planselection.learningbased.util.Utils;
 import bdi4jade.goal.Softgoal;
 import bdi4jade.plan.Plan;
 
-public interface LearningAlgorithm {
+/**
+ * It represents the algorithm used by the {@link}
+ * LearningBasedPlanSelectionStrategy. It uses an algorithm, specified in
+ * {@link} PlanMetadata, to predict an expected contribution of a plan's
+ * outcome.
+ * 
+ * @author João Faccin
+ */
+public class LearningAlgorithm {
+
+	private Instances trainingInstances;
+
+	public LearningAlgorithm() {
+	}
+
+	/**
+	 * Predicts an expected plan's contribution for a given softgoal based on
+	 * plan metadata and current context conditions.
+	 * 
+	 * @param plan
+	 *            A plan which expected contribution we want to predict.
+	 * @param softgoal
+	 *            A softgoal which plan's contribution is related.
+	 * @return An expected plan contribution value.
+	 */
+	@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
+				.getMinPlanExecutions()) {
+			String filePath = planMetadata.getFilePath()
+					+ (plan.getId() + "_" + plan.getClass().getSimpleName()
+							+ "_" + softgoal).toLowerCase() + ".arff";
+			if (!new File(filePath).exists()) {
+				try {
+					Utils.writeToFile(filePath,
+							planMetadata.getArffFileHeader());
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		} else {
+
+			if (planMetadata.getPlanExecutionsCounter() == planMetadata
+					.getMinPlanExecutions()
+					|| planMetadata.getPlanExecutionsCounter()
+							% planMetadata.getLearningGap() == 0) {
+				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 = planMetadata.getModel().classifyInstance(instance);
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+
+		switch (planMetadata.getOptimizationFunction()) {
+		case MINIMIZE:
+			return 1 - prediction;
+		default:
+			return prediction;
+		}
+	}
+
+	/**
+	 * Generates a prediction model from a training set of a given plan and
+	 * softgoal.
+	 * 
+	 * @param plan
+	 *            A plan which prediction model we want to generate.
+	 * @param softgoal
+	 *            A softgoal that the generated prediction model will relate.
+	 */
+	@SuppressWarnings("unchecked")
+	private void learnFromTrainingSet(Plan plan, Softgoal softgoal) {
+
+		PlanMetadata planMetadata = ((Map<Softgoal, PlanMetadata>) plan
+				.getMetadata(PlanMetadata.METADATA_NAME)).get(softgoal);
+
+		try {
+			trainingInstances = new Instances(new BufferedReader(
+					new FileReader(
+							planMetadata.getFilePath()
+									+ (plan.getId() + "_"
+											+ plan.getClass().getSimpleName()
+											+ "_" + softgoal).toLowerCase()
+									+ ".arff")));
+
+			trainingInstances
+					.setClassIndex(trainingInstances.numAttributes() - 1);
+
+			planMetadata.getModel().buildClassifier(trainingInstances);
 
-	public double predictExpectedContribution(Plan plan, Softgoal softgoal);
+		} 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/learningalgorithm/LinearRegressionAlgorithm.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/learningalgorithm/LinearRegressionAlgorithm.java
index 107cbd5..88b9b1d 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/learningalgorithm/LinearRegressionAlgorithm.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/learningalgorithm/LinearRegressionAlgorithm.java
@@ -7,7 +7,6 @@ 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;
@@ -20,7 +19,9 @@ import bdi4jade.plan.Plan;
 public class LinearRegressionAlgorithm implements LearningAlgorithm {
 
 	private Instances trainingInstances;
-	private LinearRegression model;
+
+	//Ver PlanMetadata
+	//private LinearRegression model;
 
 	@Override
 	@SuppressWarnings("unchecked")
@@ -31,9 +32,9 @@ public class LinearRegressionAlgorithm implements LearningAlgorithm {
 				.getMetadata(PlanMetadata.METADATA_NAME)).get(softgoal);
 
 		if (planMetadata.getPlanExecutionsCounter() < PlanMetadata.MIN_PLAN_EXECUTIONS) {
-			String filePath = "instances/"
-					+ plan.getClass().getSimpleName().toLowerCase() + "_"
-					+ softgoal + ".arff";
+			String filePath = "/home/jgfaccin/git/bdi4jade/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/instances/"
+					+ (plan.getId() + "_" + plan.getClass().getSimpleName()
+							+ "_" + softgoal).toLowerCase() + ".arff";
 			if (!new File(filePath).exists()) {
 				try {
 					Utils.writeToFile(filePath,
@@ -42,45 +43,70 @@ public class LinearRegressionAlgorithm implements LearningAlgorithm {
 					e.printStackTrace();
 				}
 			}
-			planMetadata.increasePlanExecutionsCounter();
 		} else {
-			learnFromTrainingSet(plan, softgoal);
+
+			if (planMetadata.getPlanExecutionsCounter() == PlanMetadata.MIN_PLAN_EXECUTIONS
+					|| planMetadata.getPlanExecutionsCounter()
+							% PlanMetadata.LEARNING_GAP == 0) {
+				learnFromTrainingSet(plan, softgoal);
+			}
+			//learnFromTrainingSet(plan, softgoal);
 
 			int numOfFactors = planMetadata.getInfluenceFactors().size();
 
 			Instance instance = new DenseInstance(numOfFactors);
 
 			for (int i = 0; i < numOfFactors; i++) {
+				// it was Double.valueOf((Integer)
+				// planMetadata.getInfluenceFactors...
 				instance.setValue(trainingInstances.attribute(i),
-						(double) planMetadata.getInfluenceFactors().get(i)
+						(Double) planMetadata.getInfluenceFactors().get(i)
 								.getBeliefValue());
 			}
 
 			try {
-				prediction = model.classifyInstance(instance);
-				// System.out.println("Current Instance (" + instance + "): " +
-				// prediction);
+				//prediction = model.classifyInstance(instance);
+				prediction = planMetadata.getModel().classifyInstance(instance);
 			} catch (Exception e) {
 				e.printStackTrace();
 			}
 		}
 
-		return prediction;
+		switch (planMetadata.getOptimizationFunction()) {
+		case MINIMIZE:
+			return 1 - prediction;
+		default:
+			return prediction;
+		}
 	}
 
+	@SuppressWarnings("unchecked")
 	private void learnFromTrainingSet(Plan plan, Softgoal softgoal) {
+		
+		
+		PlanMetadata planMetadata = ((Map<Softgoal, PlanMetadata>) plan
+				.getMetadata(PlanMetadata.METADATA_NAME)).get(softgoal);
+		
 		try {
-			trainingInstances = new Instances(new BufferedReader(
-					new FileReader("instances/"
-							+ plan.getClass().getSimpleName().toLowerCase()
-							+ "_" + softgoal + ".arff")));
+			trainingInstances = new Instances(
+					new BufferedReader(
+							new FileReader(
+									"/home/jgfaccin/git/bdi4jade/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/instances/"
+											+ (plan.getId()
+													+ "_"
+													+ plan.getClass()
+															.getSimpleName()
+													+ "_" + softgoal)
+													.toLowerCase() + ".arff")));
 
 			trainingInstances
 					.setClassIndex(trainingInstances.numAttributes() - 1);
 
-			model = new LinearRegression();
-			model.buildClassifier(trainingInstances);
+			//model = new LinearRegression();
+			//model.buildClassifier(trainingInstances);
 
+			planMetadata.getModel().buildClassifier(trainingInstances);
+			
 		} catch (FileNotFoundException e) {
 			e.printStackTrace();
 		} catch (IOException e) {
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedCapability.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedCapability.java
index 991fedc..c544013 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedCapability.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedCapability.java
@@ -4,6 +4,12 @@ import bdi4jade.annotation.Belief;
 import bdi4jade.core.Capability;
 import bdi4jade.extension.planselection.utilitybased.SoftgoalPreferences;
 
+/**
+ * Represents a capability that implements the {@link}
+ * LearningBasedPlanSelectionStrategy.
+ * 
+ * @author João Faccin
+ */
 public class LearningBasedCapability extends Capability {
 
 	private static final long serialVersionUID = -1044132085270106726L;
@@ -11,14 +17,12 @@ public class LearningBasedCapability extends Capability {
 	@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.
+	/**
+	 * Default constructor that sets the {@link}
+	 * LearningBasedPlanSelectionStrategy as the plan selection strategy.
 	 */
-	public LearningBasedCapability(LearningAlgorithm learningAlgorithm) {
-		setPlanSelectionStrategy(new LearningBasedPlanSelectionStrategy(
-				learningAlgorithm));
+	public LearningBasedCapability() {
+		setPlanSelectionStrategy(new LearningBasedPlanSelectionStrategy());
 	}
 
 }
\ 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
index 3bba0f1..204b20e 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanBody.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanBody.java
@@ -6,10 +6,20 @@ import java.util.Map;
 import bdi4jade.goal.Softgoal;
 import bdi4jade.plan.planbody.AbstractPlanBody;
 
+/**
+ * Represents the learning-based plan body abstraction, being an extension of
+ * the {@link} AbstractPlanBody.
+ * 
+ * @author João Faccin
+ */
 public abstract class LearningBasedPlanBody extends AbstractPlanBody {
 
 	private static final long serialVersionUID = -5064965263121492233L;
 
+	/**
+	 * Notifies some elements of a plan metadata (e.g. {@link} Outcome instance)
+	 * that a plan execution will start.
+	 */
 	@SuppressWarnings("unchecked")
 	@Override
 	public void onStart() {
@@ -21,6 +31,10 @@ public abstract class LearningBasedPlanBody extends AbstractPlanBody {
 		}
 	}
 
+	/**
+	 * Notifies some elements of a plan metadata (e.g. {@link} Outcome instance)
+	 * that a plan execution ended.
+	 */
 	@SuppressWarnings("unchecked")
 	@Override
 	public int onEnd() {
@@ -29,7 +43,9 @@ public abstract class LearningBasedPlanBody extends AbstractPlanBody {
 
 		for (PlanMetadata metadata : planMetadata) {
 			metadata.getNotifiedAtEndedPlanExecution();
+			metadata.increasePlanExecutionsCounter();
 		}
+
 		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
index dc83e44..0aca05b 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanSelectionStrategy.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/LearningBasedPlanSelectionStrategy.java
@@ -9,23 +9,24 @@ import bdi4jade.plan.Plan;
 import bdi4jade.reasoning.AbstractReasoningStrategy;
 import bdi4jade.reasoning.PlanSelectionStrategy;
 
+/**
+ * A learning-based implementation of the {@link} PlanSelectionStrategy. It
+ * selects the plan that has the best expected contribution based on a predicted
+ * outcome value and agent preferences.
+ * 
+ * @author João Faccin
+ */
 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.
+	/**
+	 * Default constructor that initializes the {@link} LearningAlgorithm used
+	 * in the plan selection process.
 	 */
-	public LearningBasedPlanSelectionStrategy(
-			LearningAlgorithm learningAlgorithm) {
-		this.learningAlgorithm = learningAlgorithm;
+	public LearningBasedPlanSelectionStrategy() {
+		this.learningAlgorithm = new LearningAlgorithm();
 	}
 
 	@Override
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/NominalInfluenceFactor.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/NominalInfluenceFactor.java
new file mode 100644
index 0000000..917468f
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/NominalInfluenceFactor.java
@@ -0,0 +1,60 @@
+package bdi4jade.extension.planselection.learningbased;
+
+import java.util.ArrayList;
+
+import bdi4jade.belief.Belief;
+
+/**
+ * An implementation of the abstract class {@link} InfluenceFactor to allow the
+ * use of discrete values as influence factor.
+ * 
+ * @author João Faccin
+ */
+public class NominalInfluenceFactor extends InfluenceFactor {
+
+	private ArrayList<String> possibleValues;
+
+	public NominalInfluenceFactor(Belief<?, ?> belief) {
+		super(belief);
+		this.possibleValues = new ArrayList<String>();
+	}
+
+	/**
+	 * Creates a new influence factor and maps it to a belief. Also, define a
+	 * set of possible values that this influence factor can have.
+	 * 
+	 * @param belief
+	 *            A belief to be mapped.
+	 * @param possibleValues
+	 *            A set of values that the influence factor can have.
+	 */
+	public NominalInfluenceFactor(Belief<?, ?> belief,
+			ArrayList<String> possibleValues) {
+		super(belief);
+		this.possibleValues = possibleValues;
+	}
+
+	/**
+	 * Adds a new value to the set of possible values that the influence factor
+	 * can have.
+	 * 
+	 * @param possibleValue
+	 *            A new possible value.
+	 */
+	public void addPossibleValue(String possibleValue) {
+		possibleValues.add(possibleValue);
+	}
+
+	@Override
+	public String toString() {
+		String influenceFactor = "@attribute " + getBeliefName() + " {"
+				+ possibleValues.get(0);
+		for (int i = 1; i < possibleValues.size(); i++) {
+			influenceFactor += "," + possibleValues.get(i);
+		}
+		influenceFactor += "}";
+
+		return influenceFactor;
+	}
+
+}
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/NumericInfluenceFactor.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/NumericInfluenceFactor.java
new file mode 100644
index 0000000..2da3609
--- /dev/null
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/NumericInfluenceFactor.java
@@ -0,0 +1,23 @@
+package bdi4jade.extension.planselection.learningbased;
+
+import bdi4jade.belief.Belief;
+
+/**
+ * An implementation of the abstract class {@link} InfluenceFactor to allow the
+ * use of continuous values as influence factor.
+ * 
+ * @author João Faccin
+ */
+public class NumericInfluenceFactor extends InfluenceFactor {
+
+	public NumericInfluenceFactor(Belief<?, ?> belief) {
+		super(belief);
+	}
+
+	@Override
+	public String toString() {
+		String influenceFactor = "@attribute " + getBeliefName() + " numeric";
+		return influenceFactor;
+	}
+	
+}
diff --git a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/OptimizationFunction.java b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/OptimizationFunction.java
index ca87a99..9c1a9e5 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/OptimizationFunction.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/OptimizationFunction.java
@@ -1,5 +1,11 @@
 package bdi4jade.extension.planselection.learningbased;
 
+/**
+ * An optimization function that indicates how an outcome value influences on
+ * preference satisfaction.
+ * 
+ * @author João Faccin
+ */
 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
index 30e7cd0..bc0cdb2 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/Outcome.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/Outcome.java
@@ -1,26 +1,38 @@
 package bdi4jade.extension.planselection.learningbased;
 
+/**
+ * An abstraction of an outcome. It represents a measurement that can be taken
+ * during and/or after a plan execution.
+ * 
+ * @author João Faccin
+ */
 public abstract class Outcome {
 
-	private String name;
-
-	public Outcome(String name) {
-		this.name = name;
-	}
+	/**
+	 * Gets the final measurement of an outcome value.
+	 * 
+	 * @return An outcome measurement.
+	 */
+	public abstract double getMeasurement();
 
-	public String getName() {
-		return this.name;
+	/**
+	 * Used in cases that a measurement is an interval between two values, e.g.
+	 * difference between final and initial time.
+	 */
+	public void startMeasurement() {
 	}
 
-	/*
-	 * 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.
+	/**
+	 * Used in cases that a measurement is an interval between two values, e.g.
+	 * difference between final and initial time.
 	 */
-	public abstract double getMeasurement();
-	
-	public void startMeasurement() {}
-	
-	public void endMeasurement() {}
+	public void endMeasurement() {
+	};
 	
+	@Override
+	public String toString() {
+		String outcome = "@attribute " + this.getClass().getSimpleName() + " numeric";
+		return outcome;
+	}
+
 }
\ 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
index 68156c7..2c886ff 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/PlanMetadata.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/PlanMetadata.java
@@ -1,19 +1,30 @@
 package bdi4jade.extension.planselection.learningbased;
 
 import java.io.IOException;
+import java.nio.file.FileSystems;
 import java.util.ArrayList;
 
+import weka.classifiers.Classifier;
 import bdi4jade.extension.planselection.learningbased.util.Utils;
 import bdi4jade.goal.Softgoal;
 import bdi4jade.plan.Plan;
 
+/**
+ * Represents the metadata associated to a plan. It relates a set of influence
+ * factors to an outcome and to a specific softgoal. This information is
+ * subsequent used by the {@link} LearningAlgorithm class in the plan selection
+ * process.
+ * 
+ * @author João Faccin
+ */
 public class PlanMetadata {
 
 	public static final String METADATA_NAME = PlanMetadata.class
 			.getSimpleName();
-	
-	public static final int MIN_PLAN_EXECUTIONS = 50;
-	
+
+	private final int MIN_PLAN_EXECUTIONS;
+	private final int LEARNING_GAP;
+
 	private Plan plan;
 	private Softgoal softgoal;
 	private Outcome outcome;
@@ -21,9 +32,35 @@ public class PlanMetadata {
 	private ArrayList<InfluenceFactor> influenceFactors;
 	private String currentInstance;
 	private int planExecutionsCounter;
+	private Classifier model;
+	private String filePath;
 
+	/**
+	 * Creates a new instance of plan metadata relating a plan to a specific
+	 * softgoal.
+	 * 
+	 * @param plan
+	 *            A plan which this metadata is related.
+	 * @param softgoal
+	 *            A softgoal which this metadata refers to.
+	 * @param outcome
+	 *            An outcome to be monitored in each plan execution.
+	 * @param optFunction
+	 *            An optimization function.
+	 * @param modelClass
+	 *            The learning algorithm which will be used in the learning
+	 *            process.
+	 * @param minPlanExecutions
+	 *            An integer indicating the minimum number of plan's executions
+	 *            to be performed before the first learning process.
+	 * @param learningGap
+	 *            An integer indicating the interval of plan's executions
+	 *            between two learning processes.
+	 */
 	public PlanMetadata(Plan plan, Softgoal softgoal, Outcome outcome,
-			OptimizationFunction optFunction) {
+			OptimizationFunction optFunction,
+			Class<? extends Classifier> modelClass, int minPlanExecutions,
+			int learningGap) {
 		this.plan = plan;
 		this.softgoal = softgoal;
 		this.outcome = outcome;
@@ -31,11 +68,47 @@ public class PlanMetadata {
 		this.influenceFactors = new ArrayList<>();
 		this.currentInstance = new String();
 		this.planExecutionsCounter = 0;
+		this.filePath = FileSystems.getDefault().getPath("").toString();
+		this.MIN_PLAN_EXECUTIONS = minPlanExecutions;
+		this.LEARNING_GAP = learningGap;
+		try {
+			model = modelClass.newInstance();
+		} catch (InstantiationException e) {
+			e.printStackTrace();
+		} catch (IllegalAccessException e) {
+			e.printStackTrace();
+		}
 	}
 
+	/**
+	 * Creates a new instance of plan metadata relating a plan to a specific
+	 * softgoal.
+	 * 
+	 * @param plan
+	 *            A plan which this metadata is related.
+	 * @param softgoal
+	 *            A softgoal which this metadata refers to.
+	 * @param outcome
+	 *            An outcome to be monitored in each plan execution.
+	 * @param optFunction
+	 *            An optimization function.
+	 * @param influenceFactors
+	 *            A set of influence factors related to an outcome.
+	 * @param modelClass
+	 *            The learning algorithm which will be used in the learning
+	 *            process.
+	 * @param minPlanExecutions
+	 *            An integer indicating the minimum number of plan's executions
+	 *            to be performed before the first learning process.
+	 * @param learningGap
+	 *            An integer indicating the interval of plan's executions
+	 *            between two learning processes.
+	 */
 	public PlanMetadata(Plan plan, Softgoal softgoal, Outcome outcome,
 			OptimizationFunction optFunction,
-			ArrayList<InfluenceFactor> influenceFactors) {
+			ArrayList<InfluenceFactor> influenceFactors,
+			Class<? extends Classifier> modelClass, int minPlanExecutions,
+			int learningGap) {
 		this.plan = plan;
 		this.softgoal = softgoal;
 		this.outcome = outcome;
@@ -43,12 +116,45 @@ public class PlanMetadata {
 		this.influenceFactors = influenceFactors;
 		this.currentInstance = new String();
 		this.planExecutionsCounter = 0;
+		this.filePath = FileSystems.getDefault().getPath("").toString();
+		this.MIN_PLAN_EXECUTIONS = minPlanExecutions;
+		this.LEARNING_GAP = learningGap;
+		try {
+			model = modelClass.newInstance();
+		} catch (InstantiationException e) {
+			e.printStackTrace();
+		} catch (IllegalAccessException e) {
+			e.printStackTrace();
+		}
 	}
 
+	/**
+	 * Returns the prediction model built using the learning algorithm specified
+	 * in the {@link} PlanMetadata constructor.
+	 * 
+	 * @return The prediction model.
+	 */
+	public Classifier getModel() {
+		return model;
+	}
+
+	/**
+	 * Adds an influence factor to the existing set of influence factors.
+	 * 
+	 * @param influenceFactor
+	 */
 	public void addInfluenceFactor(InfluenceFactor influenceFactor) {
 		this.influenceFactors.add(influenceFactor);
 	}
 
+	public int getLearningGap() {
+		return this.LEARNING_GAP;
+	}
+
+	public int getMinPlanExecutions() {
+		return this.MIN_PLAN_EXECUTIONS;
+	}
+
 	public Outcome getOutcome() {
 		return this.outcome;
 	}
@@ -60,26 +166,47 @@ public class PlanMetadata {
 	public ArrayList<InfluenceFactor> getInfluenceFactors() {
 		return this.influenceFactors;
 	}
-	
+
 	public int getPlanExecutionsCounter() {
 		return this.planExecutionsCounter;
 	}
-	
+
+	/**
+	 * Increases the plan executions counter.
+	 */
 	public void increasePlanExecutionsCounter() {
 		this.planExecutionsCounter++;
 	}
 
+	/**
+	 * Represents the notification of a started plan execution.
+	 */
 	public void getNotifiedAtStartedPlanExecution() {
 		updateCurrentInstance();
 		this.outcome.startMeasurement();
 	}
 
+	/**
+	 * Represents the notification of an already ended plan execution.
+	 */
 	public void getNotifiedAtEndedPlanExecution() {
 		this.outcome.endMeasurement();
+
 		this.currentInstance = currentInstance + this.outcome.getMeasurement();
 		saveInstance();
 	}
 
+	public void setFilePath(String filePath) {
+		this.filePath = filePath;
+	}
+
+	public String getFilePath() {
+		return this.filePath;
+	}
+
+	/**
+	 * Stores an instance with the current values of influence factors.
+	 */
 	private void updateCurrentInstance() {
 		for (InfluenceFactor influenceFactor : influenceFactors) {
 			this.currentInstance = this.currentInstance
@@ -87,25 +214,29 @@ public class PlanMetadata {
 		}
 	}
 
+	/**
+	 * Writes the current instance in a specific file.
+	 */
 	private void saveInstance() {
-		String filePath = "instances/"
-				+ this.plan.getClass().getSimpleName().toLowerCase() + "_"
-				+ this.softgoal + ".arff";
+		String filePath = this.filePath
+				+ (this.plan.getId() + "_"
+						+ this.plan.getClass().getSimpleName() + "_" + this.softgoal)
+						.toLowerCase() + ".arff";
 		try {
 			Utils.writeToFile(filePath, this.currentInstance);
 		} catch (IOException e) {
-			System.out.println("A problem occurred when trying to save a context instance!");
+			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
+	/**
+	 * Returns a string with the specific format of a header of the .arff file
+	 * used in the learning process.
+	 * 
+	 * @return An .arff file header.
 	 */
 	public String getArffFileHeader() {
 		String lineSeparator = System.getProperty("line.separator");
@@ -116,12 +247,13 @@ public class PlanMetadata {
 		header.append(relation);
 
 		for (InfluenceFactor influenceFactor : influenceFactors) {
-			header.append("@attribute ");
-			header.append(influenceFactor.getBeliefName());
-			header.append(" numeric");
+			header.append(influenceFactor);
 			header.append(lineSeparator);
 		}
 
+		header.append(outcome);
+		header.append(lineSeparator);
+
 		header.append("@data");
 		header.append(lineSeparator);
 		return header.toString();
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
index c1fdc73..3c7a8ea 100644
--- a/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/util/Utils.java
+++ b/bdi-jade-extensions/src/bdi4jade/extension/planselection/learningbased/util/Utils.java
@@ -4,19 +4,31 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 
+/**
+ * Implements useful methods to be used in general applications.
+ * 
+ * @author João Faccin
+ */
 public class Utils {
 
 	public Utils() {
 	}
 
-	public static void writeToFile(String filePath, String text) throws IOException {
+	/**
+	 * Writes a given text into a file specified in parameters.
+	 * 
+	 * @param filePath
+	 *            Specifies a path to a file which a text will be write.
+	 * @param text
+	 *            A string to be write in filePath.
+	 * @throws IOException
+	 */
+	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