patternWarehouseGetPatterns(){
+ return INSTANCE.patternWarehouse.getStockedPatterns();
+ }
+
+ //********** Serializing Methods **********//
+
+ /**
+ * Static entry point to restore a previous OCMManager, aka to restore a previous user search.
+ * @param searchSave The deserialized form of the user search state.
+ */
+ public static void deserialize(SearchSave searchSave){
+ INSTANCE.deserializeInstance(searchSave);
+ }
+
+ private void deserializeInstance(SearchSave searchSave){
+ algorithmManager.stopMining();
+ algorithmManager.reload(searchSave.getMultiArmedBandit());
+
+ coactiveLearning = searchSave.getCoactiveLearning();
+ cache = searchSave.getCache();
+ patternWarehouse = searchSave.getPatternWarehouse();
+ currentState = searchSave.getCurrentState();
+ isInitialized = searchSave.getInitialized();
+ firstRanking = searchSave.getFirstRanking();
+ }
+
+ public static void serialize(SearchSave searchSave){
+ INSTANCE.serializeInstance(searchSave);
+ }
+
+ private void serializeInstance(SearchSave searchSave){
+ algorithmManager.save(searchSave);
+
+ searchSave.setCoactiveLearning(coactiveLearning);
+ searchSave.setCache(cache);
+ searchSave.setPatternWarehouse(patternWarehouse);
+ searchSave.setCurrentState(currentState);
+ searchSave.setInitialized(isInitialized);
+ searchSave.setFirstRanking(firstRanking);
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ public static boolean isInitialized(){ return INSTANCE.isInitialized; }
+
+ public static Pattern.WrapperType getCurrentUsedWrapperType(){
+ return currentWrapperType;
+ }
+
+ // Setters //
+}
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/algorithmmanager/AlgorithmManager.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/algorithmmanager/AlgorithmManager.java
new file mode 100644
index 0000000..e55191c
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/algorithmmanager/AlgorithmManager.java
@@ -0,0 +1,289 @@
+package fr.insa.ocm.model.oneclicklearning.algorithmmanager;
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.oneclicklearning.bandit.CesaBianciBandit;
+import fr.insa.ocm.model.oneclicklearning.bandit.MultiArmedBandit;
+import fr.insa.ocm.model.utils.exceptions.NotInitializedException;
+import fr.insa.ocm.model.utils.serialize.SearchSave;
+import fr.insa.ocm.model.wrapper.api.AbstractPattern;
+import fr.insa.ocm.model.wrapper.api.AlgorithmLauncher;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+/**
+ * Algorithm Manager
+ * Manages the startMonitoring and stop of the different data mining algorithm. Also manages their resultsts
+ */
+public class AlgorithmManager {
+
+ // These semaphores are used to synchronize differents mining threads.
+ private static Semaphore operationBandit = new Semaphore(1);
+ private static Semaphore operationInfo = new Semaphore(1);
+ private static Semaphore operationCoactive = new Semaphore(1);
+
+ /**
+ * Mining Thread
+ * Thread that runs algorithms one by one while not stop.
+ */
+ private class MiningThread extends Thread {
+
+ private volatile boolean stopRequested;
+ private int currentAlgorithm;
+
+ /**
+ * Creates a new MiningThread instance
+ */
+ private MiningThread() {
+ this.stopRequested = false;
+ this.setName("Miner #"+nextThreadID);
+ nextThreadID++;
+ }
+
+
+ /**
+ * Runs the thread until {@link #stopMining()} is called.
+ * The method runs data mining algorithm selected by the bandit one by one.
+ * If the thread is stop before adding data to the cache, this step is skip to end the thread.
+ */
+ @Override
+ public void run() {
+ DebugLogger.printDebug("MiningThread (" + Thread.currentThread().getName() + "): is mining.");
+ while (!stopRequested) {
+ AlgorithmManager.operationBandit.acquireUninterruptibly();
+ currentAlgorithm = bandit.getArmToUse();
+ AlgorithmManager.operationBandit.release();
+
+ double startTime = System.currentTimeMillis();
+ List results = algorithmLauncher.startAlgorithm(currentAlgorithm);
+ double elapsedTime = System.currentTimeMillis() - startTime;
+
+ if (!stopRequested) {
+
+ operationInfo.acquireUninterruptibly();
+ nbAlgoLaunched++;
+ nbPatternFound += results.size();
+ operationInfo.release();
+
+
+ operationCoactive.acquireUninterruptibly();
+ OCMManager.cacheAddPatterns(results, elapsedTime, currentAlgorithm);
+ operationCoactive.release();
+ }
+ }
+
+ DebugLogger.printDebug(Thread.currentThread().getName() +": is stopping.");
+ }
+
+ /**
+ * Stops the thread and stop, if possible, the current running algorithm
+ */
+ private void stopMining(){
+ this.stopRequested = true;
+ algorithmLauncher.stopAlgorithm();
+ }
+ }
+
+ //private static final AlgorithmManager INSTANCE = new AlgorithmManager();
+ private static final int MAX_THREAD = 1; // TODO : Il faut prendre en compte dans les wrappers d'algorithm le fait qu'on soit en multithreadé pour > 1;
+
+ private MultiArmedBandit bandit;
+ private AlgorithmLauncher algorithmLauncher;
+ private List listCurrentThread;
+
+ private static int nextThreadID = 0;
+
+ private volatile boolean isMining;
+ private volatile boolean hasBeenInitialized;
+ private volatile int nbAlgoLaunched;
+ private volatile int nbPatternFound;
+
+ /**
+ * Creates a new instance of AlgorithmManager
+ */
+ public AlgorithmManager(){
+ nbAlgoLaunched = 0;
+ nbPatternFound = 0;
+
+ listCurrentThread = new ArrayList<>();
+
+ isMining = false;
+ hasBeenInitialized = false;
+ }
+
+ //********** Initializing Methods **********//
+
+ public void initialize(AbstractPattern.WrapperType wrapperType,
+ String filePath){
+
+ if(!this.isMining) {
+ algorithmLauncher = wrapperType.getAlgorithmLauncher(filePath);
+
+ if (algorithmLauncher != null) {
+ bandit = new CesaBianciBandit(algorithmLauncher.getNbAlgorithms());
+ hasBeenInitialized = true;
+ } else {
+ DebugLogger.printDebug("ERR: Unable to loadData the AlgorithmManager, have you correctly set the type of library you want to use ?", DebugLogger.MessageSeverity.CRITICAL);
+ }
+ } else {
+ DebugLogger.printDebug("LOG: Unable to loadData the AlgorithmManger when it is currently mining.", DebugLogger.MessageSeverity.HIGH);
+ }
+ }
+
+ //********** Serializing Methods **********//
+
+ public void reload(MultiArmedBandit bandit){
+ this.stopAllMiners();
+
+ this.bandit = bandit;
+ this.nbAlgoLaunched = 0;
+ }
+
+ public void save(SearchSave searchSave){
+ searchSave.setMultiArmedBandit(bandit);
+ }
+
+ //********** Public Methods **********//
+
+ /**
+ * Starts the data mining in a thread, {@link MiningThread}.
+ */
+ public void startMining() {
+ if(hasBeenInitialized) {
+ try {
+ operationInfo.acquire();
+ nbAlgoLaunched = 0;
+ nbPatternFound = 0;
+ operationInfo.release();
+ resumeMining();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } else {
+ DebugLogger.printDebug("LOG: Cannot startMonitoring mining since the AlgorithmManager has not been initialized", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ public synchronized void resumeMining(){
+ if(hasBeenInitialized) {
+ isMining = true;
+
+ // Creating the new threads.
+ for (int i = 0; i < MAX_THREAD; i++) {
+ MiningThread newMiningThread = new MiningThread();
+ newMiningThread.setDaemon(true);
+ newMiningThread.start();
+ listCurrentThread.add(newMiningThread);
+ }
+ } else {
+ DebugLogger.printDebug("LOG: Cannot resume mining since the AlgorithmManager has not been initialized", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ public void pauseMining(){
+ this.stopAllMiners();
+ }
+
+ /**
+ * Stops the data mining.
+ */
+ public void stopMining(){
+ pauseMining();
+ }
+
+ //********** Internal Methods **********//
+
+ private synchronized void stopAllMiners(){
+ listCurrentThread.forEach(miningThread -> {
+ if(miningThread != null){
+ miningThread.stopMining();
+ try {
+ DebugLogger.printDebug("AlgorithmManager: Waiting for the stop of "+ miningThread.getName());
+ miningThread.join();
+ DebugLogger.printDebug("AlgorithmManager: "+ miningThread.getName() +" has stopped");
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ listCurrentThread.clear();
+
+ this.isMining = false;
+ }
+
+ //********** Proxy Methods **********//
+
+
+ // Bandit //
+ public void banditUpdateReward(int arm, double reward){
+ bandit.updateReward(arm, reward);
+ }
+
+ public double[] banditGetWeights(){
+ return bandit.getWeights();
+ }
+
+ // Algorithm Launcher //
+
+ public List algorithmLauncherGetListAlgorithmName(){
+ return algorithmLauncher.getListAlgorithmName();
+ }
+
+ public List algorithmLauncherGetListAttributeName(){ return algorithmLauncher.getListAttributeName(); }
+
+ //********** Getters/Setters Methods **********//
+
+ public int getNbAlgoLaunched(){
+ return nbAlgoLaunched;
+ }
+
+ public int getNbPatternFound(){
+ return nbPatternFound;
+ }
+
+ public boolean isMining(){
+ return isMining;
+ }
+
+ public int getNbAttributeMeasures() throws NotInitializedException {
+ if(hasBeenInitialized){
+ return algorithmLauncher.getNbAlgorithms() + algorithmLauncher.getNbAttributes();
+ } else {
+ DebugLogger.printDebug("ERR: Cannot get the number of attribute measure if the AlgorithmManager is not initialized", DebugLogger.MessageSeverity.HIGH);
+ throw new NotInitializedException();
+ }
+ }
+
+ //********** Old Methods **********//
+
+
+// /**
+// * Returns the bandit instance used by the {@link AlgorithmManager}.
+// * @return The instance of the bandit used by the {@link AlgorithmManager}.
+// */
+// public MultiArmedBandit getBandit(){
+// return bandit;
+// }
+//
+// /**
+// * Imports the data from a CSV file.
+// * Do not check if the file exists.
+// * @param filePath The path to the CSV file.
+// */
+// public void importData(String filePath) {
+// algorithmLauncher.importData(filePath);
+// CoactiveLearningRanking.getInstance().init(algorithmLauncher.getNbAttributes() + algorithmLauncher.getNbAlgorithms());
+// }
+//
+// public void reloadData(String filePath){
+// algorithmLauncher = new AlgorithmLauncherRealKD();
+// algorithmLauncher.importData(filePath);
+// }
+
+
+
+
+}
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/bandit/CesaBianciBandit.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/bandit/CesaBianciBandit.java
new file mode 100644
index 0000000..8b8f218
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/bandit/CesaBianciBandit.java
@@ -0,0 +1,145 @@
+package fr.insa.ocm.model.oneclicklearning.bandit;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.utils.RandomSelector;
+
+/**
+ * Cesa Bianci Bandit algorithm
+ * Provides an implementation of the CesiaBianci Bandit algorithm which solved the
+ * multi-armed bandit problem
+ */
+public class CesaBianciBandit implements MultiArmedBandit {
+
+ // The currend weights, v
+ @Expose private volatile double[] weights;
+ // The currend round
+ @Expose private int round;
+ // The value that normalizes the sum of v
+ @Expose private double V;
+
+ //TODO Javadoc for serializing purpose only
+ @SuppressWarnings("unused")
+ public CesaBianciBandit(){}
+
+ public CesaBianciBandit(double[] weights, int round, double V){
+ this.weights = weights;
+ this.round = round;
+ this.V = V;
+ }
+
+ /**
+ * Creates a new CesaBianciBandit instances, with a given number of arms/
+ * @param nbOfArms the number of arms for the instance
+ */
+ public CesaBianciBandit(int nbOfArms){
+ round = 1;
+ V = 0;
+ weights = new double[nbOfArms];
+ for(int i = 0; i< weights.length; ++i) {
+ weights[i] = 1./ (double) weights.length;
+ }
+ V = 1;
+ }
+
+ @Override
+ public void updateReward(int arm, double reward) {
+ if (arm >= weights.length) {
+ throw new IndexOutOfBoundsException("Arm value should be at most: "+(weights.length-1));
+ }
+
+
+ double beta = getBeta();
+ double gamma = getMixtureCoefficient(beta);
+ double n = getLearningRate(gamma);
+ double[] pi = getSelectionDistribution(gamma);
+
+ double[] g = new double[weights.length];
+ V = 0;
+ for(int i=0; iMulti-armed bandit problem representation
+ * This interface provides simple methods that answer to
+ * the multi-armed bandit problem
+ *
+ * @see Multi-armed bandit - Wikipedia
+ */
+public interface MultiArmedBandit {
+
+ /**
+ * Updates the reward for a given arm
+ * @param arm the arm associates to the reward
+ * @param reward reward value for the given arm
+ */
+ void updateReward(int arm, double reward);
+
+ /**
+ * Returns the arm to use.
+ * @return the arm
+ */
+ int getArmToUse();
+
+ /**
+ * Returns the current weights. Also called v.
+ * @return The current weights in an array of double.
+ */
+ double[] getWeights();
+
+}
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/api/AbstractCache.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/api/AbstractCache.java
new file mode 100644
index 0000000..f069bdf
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/api/AbstractCache.java
@@ -0,0 +1,41 @@
+package fr.insa.ocm.model.oneclicklearning.cache.api;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.List;
+
+public abstract class AbstractCache implements Cache {
+
+ @Expose protected int sizeListBestPatterns;
+ @Expose private CacheType cacheType;
+
+ /**
+ * Deserializing constructor.
+ * @param sizeListBestPatterns Deserializing parameter.
+ * @param cacheType Deserializing parameter.
+ */
+ protected AbstractCache(int sizeListBestPatterns,
+ CacheType cacheType){
+ this.sizeListBestPatterns = sizeListBestPatterns;
+ this.cacheType = cacheType;
+ }
+
+ @Override
+ public abstract List getBestPattern();
+
+ @Override
+ public abstract void addPatterns(List newPatterns, double time, int arm);
+
+ @Override
+ public int getSizeListBestPatterns() {
+ return sizeListBestPatterns;
+ }
+
+ @Override
+ public void setSizeListBestPatterns(int size) {
+ if(size > 0){
+ sizeListBestPatterns = size;
+ }
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/api/Cache.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/api/Cache.java
new file mode 100644
index 0000000..dc0ddb9
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/api/Cache.java
@@ -0,0 +1,47 @@
+package fr.insa.ocm.model.oneclicklearning.cache.api;
+
+import fr.insa.ocm.model.oneclicklearning.cache.rank.CacheRanking;
+import fr.insa.ocm.model.oneclicklearning.cache.set.CacheSet;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public interface Cache {
+
+ enum CacheType{
+ RANKING, SET;
+
+ @NotNull
+ public Cache newInstance(){
+ switch (this){
+ case RANKING:
+ return new CacheRanking();
+ case SET:
+ return new CacheSet();
+ default:
+ return new CacheSet();
+ }
+ }
+
+ @Override
+ public String toString() {
+ switch (this){
+ case RANKING:
+ return "Ranking";
+ case SET:
+ return "Set";
+ default:
+ return "";
+ }
+ }
+ }
+
+ List getBestPattern();
+
+ void addPatterns(List newPatterns, double time, int arm);
+
+ int getSizeListBestPatterns();
+
+ void setSizeListBestPatterns(int size);
+}
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/rank/CacheRanking.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/rank/CacheRanking.java
new file mode 100644
index 0000000..218b15a
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/rank/CacheRanking.java
@@ -0,0 +1,96 @@
+package fr.insa.ocm.model.oneclicklearning.cache.rank;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.oneclicklearning.cache.api.AbstractCache;
+import fr.insa.ocm.model.utils.Rank;
+import fr.insa.ocm.model.utils.serialize.SearchSave;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.List;
+
+/**
+ * CacheRanking
+ * Stocks the patterns while mining, and compute greedy ranking.
+ */
+public class CacheRanking extends AbstractCache {
+
+ // Patterns currently in cache
+ @Expose private double utility;
+ // Greedy ranking for the cache
+ @Expose private Rank greedyRanking;
+ // Do all the computation of the greedy ranking
+ private static GreedyRankingComputer greedyComputer;
+
+ /**
+ * Deserializing constructor.
+ * @param sizeListBestPatterns Deserializing parameter.
+ * @param cacheType Deserializing parameter.
+ * @param utility Deserializing parameter.
+ * @param greedyRanking Deserializing parameter.
+ */
+ public CacheRanking(int sizeListBestPatterns,
+ CacheType cacheType,
+ double utility,
+ Rank greedyRanking){
+ super(sizeListBestPatterns, cacheType);
+
+ this.utility = utility;
+ this.greedyRanking = greedyRanking;
+
+ greedyComputer = new GreedyRankingComputer(this);
+ greedyComputer.start();
+ }
+
+ /**
+ * Creates a new CacheRanking instance.
+ */
+ public CacheRanking(){
+ super(20, CacheType.RANKING);
+
+ greedyRanking = new Rank<>();
+ utility = 0;
+
+ greedyComputer = new GreedyRankingComputer(this);
+ greedyComputer.start();
+ }
+
+ //********** Serializing Methods **********//
+
+ public void reload(CacheRanking cache){
+ this.utility = cache.utility;
+ this.greedyRanking = cache.greedyRanking;
+
+ greedyComputer = new GreedyRankingComputer(this);
+ greedyComputer.start();
+ }
+
+ public void save(SearchSave searchSave){
+ searchSave.setCache(this);
+ }
+
+ //********** Public Methods **********//
+
+ @Override
+ public List getBestPattern() {
+ greedyRanking = greedyComputer.getGreedyRanking();
+
+ return new Rank<>(greedyRanking);
+ }
+
+ /**
+ * Adds patterns from a given mining algorithm to the cache.
+ * @param newPatterns The patterns from the mining algorithm
+ * @param time The computation time of the mining algorithm
+ * @param arm The index of the mining algorithm
+ */
+ public void addPatterns(List newPatterns, double time, int arm){
+ greedyComputer.addPatterns(newPatterns, time, arm);
+ }
+
+ @Deprecated
+ public void requestStop(){
+ greedyComputer.requestedStop();
+ }
+}
+
+
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/rank/GreedyRankingComputer.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/rank/GreedyRankingComputer.java
new file mode 100644
index 0000000..dd810a1
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/rank/GreedyRankingComputer.java
@@ -0,0 +1,226 @@
+package fr.insa.ocm.model.oneclicklearning.cache.rank;
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.oneclicklearning.cache.api.Cache;
+import fr.insa.ocm.model.utils.Rank;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class GreedyRankingComputer extends Thread {
+
+ private class GreedyCalculus implements Runnable{
+ private Pattern bestPattern;
+ private double bestUtility;
+ private List candidatePatterns;
+ private Rank greedyRankingCurrent;
+
+ GreedyCalculus(@NotNull final Rank greedyRankingCurrent,
+ @NotNull List candidatePatterns){
+ this.bestPattern = null;
+ this.bestUtility = -1d;
+ this.candidatePatterns = candidatePatterns;
+ this.greedyRankingCurrent = new Rank<>(greedyRankingCurrent);
+ }
+
+ @Nullable
+ Pattern getBestPattern(){
+ return bestPattern;
+ }
+
+ double getBestUtility(){
+ return bestUtility;
+ }
+
+ @Override
+ public void run() {
+ for (Pattern p : candidatePatterns) {
+ greedyRankingCurrent.add(p);
+ double utility = OCMManager.coactiveGetUtility(greedyRankingCurrent);
+ if (utility > bestUtility) {
+ this.bestPattern = p;
+ this.bestUtility = utility;
+ }
+ greedyRankingCurrent.remove(p);
+ }
+
+ }
+ }
+
+ private class AlgorithmPatternResults {
+ int arm;
+ double time;
+ List listFoundPatterns;
+
+ AlgorithmPatternResults(int arm, double time, List listFoundPatterns){
+ this.arm = arm;
+ this.time = time;
+ this.listFoundPatterns = listFoundPatterns;
+ }
+ }
+
+ private volatile boolean stopRequested;
+
+ private final List queuedPatterns = new ArrayList<>();
+ private final Rank greedyRanking = new Rank<>();
+ private double rankingUtility;
+ private CacheRanking cache;
+
+ private final Set armsPulled = new HashSet<>();
+
+ GreedyRankingComputer(@NotNull CacheRanking cache){
+ this.cache = cache;
+
+ stopRequested = false;
+ rankingUtility = 0;
+
+ this.setDaemon(true);
+ this.setName("Greedy Computer");
+ }
+
+ @Override
+ public void run(){
+ List candidatePatterns = new ArrayList<>();
+ AlgorithmPatternResults currentCandidate = new AlgorithmPatternResults(-1, -1, new ArrayList<>());
+
+ try {
+ while (!stopRequested) {
+ synchronized (queuedPatterns) {
+ if (queuedPatterns.size() != 0) {
+ currentCandidate = queuedPatterns.get(0);
+ candidatePatterns.addAll(currentCandidate.listFoundPatterns);
+ queuedPatterns.remove(0);
+ }
+ }
+
+ try {
+ if (!candidatePatterns.isEmpty()) {
+
+ // The Patterns in the greedy ranking needs to be treated as simple Patterns to establish a new ranking.
+ candidatePatterns.addAll(greedyRanking);
+
+ int greedySize = Math.min(cache.getSizeListBestPatterns(), candidatePatterns.size());
+ Rank greedyRankingTmp = new Rank<>();
+
+ for (int i = 0; i < greedySize || candidatePatterns.isEmpty(); ++i) {
+ Pattern bestPattern;
+ double bestUtility;
+
+ Thread[] greedyComputation = new Thread[8];
+ GreedyCalculus[] greedyCalculus = new GreedyCalculus[greedyComputation.length];
+
+ for (int j = 0; j < greedyComputation.length; ++j) {
+ List subList = candidatePatterns.subList((j / greedyComputation.length) * candidatePatterns.size(), ((j + 1) / greedyComputation.length) * candidatePatterns.size());
+
+ greedyCalculus[j] = new GreedyCalculus(greedyRankingTmp, subList);
+
+ greedyComputation[j] = new Thread(greedyCalculus[j]);
+ greedyComputation[j].setName("GreedyCalculus #" + j);
+ greedyComputation[j].start();
+ }
+
+ for (Thread threadGreedy : greedyComputation) {
+ try {
+ threadGreedy.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ bestPattern = greedyCalculus[0].getBestPattern();
+ bestUtility = greedyCalculus[0].getBestUtility();
+
+ for (int j = 1; j < greedyComputation.length; ++j) {
+ if (bestUtility < greedyCalculus[j].getBestUtility()) {
+ bestPattern = greedyCalculus[j].getBestPattern();
+ bestUtility = greedyCalculus[j].getBestUtility();
+ }
+ }
+
+ if (bestPattern == null) {
+ throw new NullPointerException();
+ }
+
+ if (!greedyRankingTmp.contains(bestPattern)) {
+ greedyRankingTmp.add(bestPattern);
+ }
+ candidatePatterns.remove(bestPattern); //It is ok because the cache will be resized with the greedyRankingCurrent
+
+ }
+
+ // Update the MultiArmedBandit
+ double newUtility = OCMManager.coactiveGetUtility(greedyRankingTmp);
+// double reward = (newUtility - rankingUtility)/currentCandidate.time;
+ double reward = (newUtility - rankingUtility) * (5 / currentCandidate.time);
+
+ //TODO Find a better way to calculate the reward.
+ System.out.println("(arm, reward) = (" + currentCandidate.arm + ", " + reward + ")");
+ OCMManager.algorithmManagerUpdateReward(currentCandidate.arm, reward);
+
+ rankingUtility = newUtility;
+
+ synchronized (greedyRanking) {
+ greedyRanking.clear();
+ greedyRanking.addAll(greedyRankingTmp);
+ }
+
+ } else {
+ Thread.sleep(1000);
+ }
+ } catch (NullPointerException e){
+ DebugLogger.printDebug("GreedyRankingComputer: One of the best pattern found was null.", DebugLogger.MessageSeverity.HIGH);
+ }
+
+ candidatePatterns.clear();
+
+ }
+ } catch (InterruptedException e){
+ e.printStackTrace();
+ }
+ }
+
+ void addPatterns(List newPatterns, double time, int arm){
+ AlgorithmPatternResults patternResults = new AlgorithmPatternResults(arm, time, newPatterns);
+ synchronized (armsPulled){
+ // We don't want to see each time the same algorithms results (only for SPMF)
+ if(OCMManager.getCurrentUsedWrapperType().equals(Pattern.WrapperType.SPMF)
+ && armsPulled.contains(arm)){
+ return;
+ }
+ }
+
+ synchronized (queuedPatterns){
+ if(queuedPatterns.size() <= 50) {
+ queuedPatterns.add(patternResults);
+ synchronized (armsPulled){
+ armsPulled.add(arm);
+ }
+ }
+ }
+ }
+
+ Rank getGreedyRanking(){
+ Rank greedyRankingResult;
+
+ synchronized (greedyRanking){
+ greedyRankingResult = new Rank<>(greedyRanking);
+ }
+
+ synchronized (armsPulled){
+ armsPulled.clear();
+ }
+
+ return greedyRankingResult;
+ }
+
+ void requestedStop(){
+ stopRequested = true;
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/set/CacheSet.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/set/CacheSet.java
new file mode 100644
index 0000000..351478d
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/cache/set/CacheSet.java
@@ -0,0 +1,126 @@
+package fr.insa.ocm.model.oneclicklearning.cache.set;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.oneclicklearning.cache.api.AbstractCache;
+import fr.insa.ocm.model.utils.Rank;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.*;
+
+
+public class CacheSet extends AbstractCache {
+
+ private class PatternEntry{
+
+ double utility;
+ Pattern pattern;
+
+ PatternEntry(Pattern pattern, double utility){
+ this.pattern = pattern;
+ this.utility = utility;
+ }
+
+ // Sorting desc
+ int compare(PatternEntry patternEntry){
+ double r = this.utility - patternEntry.utility;
+ if(r < 0){
+ return 1;
+ } else if(r > 0){
+ return -1;
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString(){
+ return "" + utility;
+ }
+ }
+
+ // Stocks the best patterns of each arms.
+ private Map> cachePattern;
+
+ // Stocks the previous utility for each arms.
+ private Map previousUtility;
+
+ /**
+ * Deserializing constructor.
+ * @param sizeListBestPatterns Deserializing parameter.
+ * @param cacheType Deserializing parameter.
+ */
+ public CacheSet(int sizeListBestPatterns,
+ CacheType cacheType){
+ super(sizeListBestPatterns, cacheType);
+
+ this.previousUtility = previousUtility;
+
+ cachePattern = Collections.synchronizedMap(new HashMap<>());
+ }
+
+ public CacheSet(){
+ super(20, CacheType.SET);
+
+ cachePattern = Collections.synchronizedMap(new HashMap<>());
+ previousUtility = new HashMap<>();
+ }
+
+ @Override
+ public List getBestPattern() {
+ List listPattern = new ArrayList<>();
+ List intermediateList = new ArrayList<>();
+ final double[] weightsBandit = OCMManager.banditGetWeights();
+
+ cachePattern.forEach((integer, patternEntries) -> {
+ int numberPattern = (int) (weightsBandit[integer]*sizeListBestPatterns);
+ numberPattern = numberPattern==0 ? 1 : numberPattern;
+
+ intermediateList.addAll(patternEntries.subList(0, numberPattern));
+ });
+
+ intermediateList.sort(PatternEntry::compare);
+ intermediateList.forEach(patternEntry -> listPattern.add(patternEntry.pattern));
+
+ cachePattern.clear();
+ return new Rank<>(listPattern);
+ }
+
+ // TODO -- Find how useful time could be ?
+ public void addPatterns(List newPatterns, double time, int arm){
+ if(!cachePattern.containsKey(arm)){
+ List listPatternEntry = new ArrayList<>();
+ Rank rank = new Rank<>();
+
+ for(Pattern pattern : newPatterns){
+ rank.add(pattern);
+
+ double utility = OCMManager.coactiveGetUtility(rank);
+ PatternEntry entry = new PatternEntry(pattern, utility);
+ listPatternEntry.add(entry);
+
+ rank.clear();
+ }
+
+ // Get the best patterns.
+ listPatternEntry.sort(PatternEntry::compare);
+ listPatternEntry = listPatternEntry.subList(0, sizeListBestPatterns);
+
+ // Calculate the reward.
+ List listPatterns = new ArrayList<>();
+ listPatternEntry.forEach(patternEntry -> listPatterns.add(patternEntry.pattern));
+
+ double newUtility = OCMManager.coactiveGetUtility(listPatterns);
+ double oldUtility = previousUtility.getOrDefault(arm, newUtility);
+
+ double reward = (newUtility - oldUtility) * 2.5 ;
+ System.out.println("(arm, reward) = (" + arm + ", " + reward + ")");
+ System.out.println("\tOld utility: "+oldUtility+"\n\tNew utility: "+newUtility);
+
+ OCMManager.algorithmManagerUpdateReward(arm, reward);
+
+ // Update informations
+ cachePattern.put(arm, listPatternEntry);
+ previousUtility.put(arm, newUtility);
+ }
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/coactivelearning/api/AbstractCoactiveLearning.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/coactivelearning/api/AbstractCoactiveLearning.java
new file mode 100644
index 0000000..1a31224
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/coactivelearning/api/AbstractCoactiveLearning.java
@@ -0,0 +1,82 @@
+package fr.insa.ocm.model.oneclicklearning.coactivelearning.api;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.utils.SystemState;
+import fr.insa.ocm.model.utils.Vector;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AbstractCoactiveLearning implements CoactiveLearning {
+
+ @Expose
+ protected CoactiveType coactiveType;
+
+ @Expose
+ protected Vector weights;
+ @Expose
+ protected SystemState previousSystemState;
+ @Expose
+ protected int nbCycles;
+ @Expose
+ protected int nbInterestingnessMeasures;
+
+ /**
+ * Deserializer constructor.
+ *
+ * @param weights Deserializer parameter.
+ * @param previousSystemState Deserializer parameter.
+ * @param nbCycles Deserializer parameter.
+ * @param nbInterestingnessMeasures Deserializer parameter.
+ * @param coactiveType Deserializer parameter.
+ */
+ protected AbstractCoactiveLearning(Vector weights,
+ SystemState previousSystemState,
+ int nbCycles,
+ int nbInterestingnessMeasures,
+ CoactiveType coactiveType) {
+ this.weights = weights;
+ this.previousSystemState = previousSystemState;
+ this.nbCycles = nbCycles;
+ this.nbInterestingnessMeasures = nbInterestingnessMeasures;
+
+ this.coactiveType = coactiveType;
+ }
+
+ protected AbstractCoactiveLearning(CoactiveType coactiveType) {
+ previousSystemState = new SystemState(new ArrayList<>());
+ nbCycles = 1;
+ }
+
+ //********** Initializing Methods **********//
+
+ @Override
+ public void initialize() {
+ nbInterestingnessMeasures = Pattern.MeasureType.values().length + OCMManager.algorithmLauncherGetListAlgorithmName().size() + OCMManager.algorithmLauncherGetListAttributeName().size();
+ double[] initWeights = new double[nbInterestingnessMeasures];
+ for (int i = 0; i < nbInterestingnessMeasures; i++) {
+ initWeights[i] = 1.0 / nbInterestingnessMeasures;
+ }
+ weights = new Vector(initWeights);
+ }
+
+ //********** Public Methods **********//
+
+ @Override
+ public abstract void updateWeight(SystemState newState);
+
+ @Override
+ public abstract double getUtility(List listPatterns);
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ @Override
+ public abstract double[] getWeights();
+
+ @Override
+ public abstract int getNbCycles();
+}
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/coactivelearning/api/CoactiveLearning.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/coactivelearning/api/CoactiveLearning.java
new file mode 100644
index 0000000..95484d2
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/coactivelearning/api/CoactiveLearning.java
@@ -0,0 +1,49 @@
+package fr.insa.ocm.model.oneclicklearning.coactivelearning.api;
+
+
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.ranking.CoactiveLearningRanking;
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.set.CoactiveLearningSet;
+import fr.insa.ocm.model.utils.SystemState;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.List;
+
+public interface CoactiveLearning {
+
+ enum CoactiveType {
+ RANKING, SET;
+
+ public CoactiveLearning newInstance(){
+ switch (this){
+ case RANKING:
+ return new CoactiveLearningRanking();
+ case SET:
+ return new CoactiveLearningSet();
+ default:
+ return new CoactiveLearningSet();
+ }
+ }
+
+ @Override
+ public String toString() {
+ switch (this){
+ case RANKING:
+ return "Ranking";
+ case SET:
+ return "Set";
+ default:
+ return "";
+ }
+ }
+ }
+
+ void initialize();
+
+ void updateWeight(SystemState newState);
+
+ double getUtility(List listPatterns);
+
+ double[] getWeights();
+
+ int getNbCycles();
+}
diff --git a/src/main/java/fr/insa/ocm/model/oneclicklearning/coactivelearning/ranking/CoactiveLearningRanking.java b/src/main/java/fr/insa/ocm/model/oneclicklearning/coactivelearning/ranking/CoactiveLearningRanking.java
new file mode 100644
index 0000000..bdca1b4
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/oneclicklearning/coactivelearning/ranking/CoactiveLearningRanking.java
@@ -0,0 +1,142 @@
+package fr.insa.ocm.model.oneclicklearning.coactivelearning.ranking;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.api.AbstractCoactiveLearning;
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.api.CoactiveLearning;
+import fr.insa.ocm.model.utils.Rank;
+import fr.insa.ocm.model.utils.SystemState;
+import fr.insa.ocm.model.utils.Vector;
+import fr.insa.ocm.model.utils.serialize.SearchSave;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import java.lang.Math;
+import java.util.List;
+
+/**
+ * Coactive Learning Algorithm
+ * Provides methods for the coactive learning algorithm.
+ */
+public class CoactiveLearningRanking extends AbstractCoactiveLearning {
+
+ private static final int D_MAX_BORN = 250; //born d to this max value
+ @Expose private int d;
+
+ /**
+ * Deserializer constructor.
+ * @param weights Deserializer parameter.
+ * @param previousSystemState Deserializer parameter.
+ * @param nbCycles Deserializer parameter.
+ * @param nbInterestingnessMeasures Deserializer parameter.
+ * @param coactiveType Deserializer parameter.
+ * @param d Deserializer parameter.
+ */
+ public CoactiveLearningRanking(Vector weights,
+ SystemState previousSystemState,
+ int nbCycles,
+ int nbInterestingnessMeasures,
+ CoactiveType coactiveType,
+ int d){
+ super(weights, previousSystemState, nbCycles, nbInterestingnessMeasures, coactiveType);
+
+ this.d = d;
+ }
+
+ /**
+ * Create a new CoactiveLearningRanking instance.
+ */
+ public CoactiveLearningRanking() {
+ super(CoactiveType.RANKING);
+ d = 1;
+ }
+
+ //********** Public Methods **********//
+
+ /**
+ * Updates the weight vector w(t) with the user selection.
+ * The user selection is the patterns he has selected, rejected and not selected or rejected.
+ * @param newState Actual system state which contains the selected, rejected and neutral patterns.
+ */
+ @Override
+ public synchronized void updateWeight(SystemState newState){
+ if(!newState.getProposedRanking().isEmpty()){
+ double s = Math.pow(OCMManager.cacheGetSizeListBestPatterns(), 1/d);
+ double theta_t = 1/(2*s*Math.sqrt(Math.pow(2, Math.floor(Math.log10(nbCycles)))));
+ double z = 0;
+
+ Vector phi_userRanking = auxiliaryFunctionPhi(newState.getUserRanking());
+ Vector phi_proposedRanking = auxiliaryFunctionPhi(newState.getProposedRanking());
+ for(int i = 0; i listPatterns){
+ if(listPatterns instanceof Rank>){
+ return this.getUtilityRanking(new Rank<>(listPatterns));
+ } else {
+ DebugLogger.printDebug("CoactiveLearningRanking: getUtility recieved a List instead of a Rank.", DebugLogger.MessageSeverity.HIGH);
+ }
+ return Double.NaN;
+ }
+
+ /**
+ * Returns the utility of an given pattern rank.
+ * @param rank The rank to get its utility
+ * @return The utility value of the given rank
+ */
+ private double getUtilityRanking(Rank rank){
+ return Vector.scalarProduct(weights, auxiliaryFunctionPhi(rank));
+ }
+
+ //********** Internal Methods **********//
+
+ /**
+ * Computes the auxiliary function Phi.
+ * @param rank The pattern rank
+ * @return The vector computes with the function Phi.
+ */
+ private synchronized Vector auxiliaryFunctionPhi(Rank rank){
+ Vector phiVectorResult = new Vector();
+ for(int i = 0; i listPatterns) {
+ return Vector.scalarProduct(weights, auxiliaryFunctionPhi(listPatterns));
+ }
+
+ //********** Internal Methods **********//
+
+ /**
+ * Computes the auxiliary function Phi.
+ * @param listPatterns The pattern rank
+ * @return The vector computes with the function Phi.
+ */
+ private synchronized Vector auxiliaryFunctionPhi(List listPatterns){
+ Vector phiVectorResult = new Vector();
+ for(int i = 0; i < nbInterestingnessMeasures; ++i){
+ double sum = 0d;
+ for(Pattern pattern : listPatterns){
+ sum += pattern.getAttributesVector().get(i);
+ }
+ phiVectorResult.put(sum);
+ }
+ return phiVectorResult;
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ @Override
+ public synchronized double[] getWeights() {
+ return weights.getValues();
+ }
+
+ @Override
+ public int getNbCycles() {
+ return nbCycles;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/PatternWarehouse.java b/src/main/java/fr/insa/ocm/model/utils/PatternWarehouse.java
new file mode 100644
index 0000000..ca2157a
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/PatternWarehouse.java
@@ -0,0 +1,44 @@
+package fr.insa.ocm.model.utils;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/***
+ * This class should keep all the patterns the user has found interesting
+ */
+public class PatternWarehouse {
+
+ @Expose private List patterns;
+
+ public PatternWarehouse(List listPatterns){
+ patterns = listPatterns;
+ }
+
+ public PatternWarehouse(){
+ patterns = new ArrayList<>();
+ }
+
+ //********** Public Methods **********//
+
+ public synchronized List getStockedPatterns(){
+ List stockedPatterns = new ArrayList<>();
+
+ patterns.forEach(pattern -> stockedPatterns.add(pattern.copy()));
+
+ return stockedPatterns;
+ }
+
+ public synchronized void addToWarehouse(Collection collection){
+ collection.forEach(pattern -> {
+ if(!patterns.contains(pattern)){
+ patterns.add(pattern);
+ }
+ });
+ }
+
+ private synchronized void emptyWarehouse(){ patterns.clear(); }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/RandomSelector.java b/src/main/java/fr/insa/ocm/model/utils/RandomSelector.java
new file mode 100644
index 0000000..50dfb43
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/RandomSelector.java
@@ -0,0 +1,34 @@
+package fr.insa.ocm.model.utils;
+
+import java.util.Random;
+
+/**
+ * RandomSelector contains method to operate probabilistic selection on an array.
+ */
+public final class RandomSelector {
+
+ /**
+ * Returns the selected index based on the weights (probabilities).
+ * Linear O(n) version.
+ * @param weights the probabilities
+ * @return the selected index, -1 if an error occurs.
+ */
+ public static int randomPick(double[] weights) {
+ double weightSum = 0;
+ for (double weight : weights) {
+ weightSum += weight;
+ }
+
+ double randomValue = new Random().nextDouble()*weightSum;
+
+ for(int i=0; i extends ArrayList {
+
+ public Rank(){
+ super();
+ }
+
+ public Rank(int size){
+ super(size);
+ }
+
+ public Rank(Rank rank){ super(rank); }
+
+ public Rank(List list){ super(list); }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insa/ocm/model/utils/SystemState.java b/src/main/java/fr/insa/ocm/model/utils/SystemState.java
new file mode 100644
index 0000000..574e4a3
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/SystemState.java
@@ -0,0 +1,63 @@
+package fr.insa.ocm.model.utils;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.List;
+
+public class SystemState {
+
+ @Expose private Rank proposedRanking;
+ @Expose private List interestingPatterns;
+ @Expose private List neutralPatterns;
+ @Expose private List trashedPatterns;
+
+ public List getInterestingPatterns() {
+ return interestingPatterns;
+ }
+
+ public List getNeutralPatterns() {
+ return neutralPatterns;
+ }
+
+ public List getTrashedPatterns() {
+ return trashedPatterns;
+ }
+
+ public SystemState(List extends Pattern> proposedRank) {
+ proposedRanking = new Rank<>();
+ this.proposedRanking.addAll(proposedRank);
+
+ }
+
+ public Rank getProposedRanking() {
+ return proposedRanking;
+ }
+
+ public Rank getUserRanking() {
+ Rank userRanking = new Rank<>();
+ userRanking.addAll(interestingPatterns);
+ userRanking.addAll(neutralPatterns);
+ return userRanking;
+ }
+
+ private void setInterestingPatterns(List interestingPatterns) {
+ this.interestingPatterns = interestingPatterns;
+ }
+
+ private void setNeutralPatterns(List neutralPatterns) {
+ this.neutralPatterns = neutralPatterns;
+ }
+
+ private void setTrashedPatterns(List trashedPatterns) {
+ this.trashedPatterns = trashedPatterns;
+ }
+
+ public void update(List newInterestingPattern,
+ List newNeutralPattern,
+ List newTrashedPattern){
+ setInterestingPatterns(newInterestingPattern);
+ setNeutralPatterns(newNeutralPattern);
+ setTrashedPatterns(newTrashedPattern);
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/Vector.java b/src/main/java/fr/insa/ocm/model/utils/Vector.java
new file mode 100644
index 0000000..0a07886
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/Vector.java
@@ -0,0 +1,137 @@
+package fr.insa.ocm.model.utils;
+import com.google.gson.annotations.Expose;
+
+import java.lang.Math;
+import java.util.Arrays;
+
+public class Vector {
+
+ @Expose private double[] values;
+
+ public Vector(){
+ values = new double[0];
+ }
+
+ public Vector(double[] vals) {
+ values = new double[vals.length];
+
+ System.arraycopy(vals, 0, values,0, vals.length);
+ }
+
+ public Vector(Vector orig){
+ values = new double[orig.values.length];
+ System.arraycopy(orig.values, 0, values, 0, values.length);
+ }
+
+ //********* Getters/Setters Method ********//
+
+ public double[] getValues() {
+ return values;
+ }
+
+ public int size(){
+ return values.length;
+ }
+
+ //********* Public Method ********//
+
+ public double norm(int p) {
+ if(p == 0){
+ throw new ArithmeticException("Cannot ask the norm with p=0");
+ }
+
+ double result = 0;
+ for (double coords : this.values) {
+ result += Math.pow((Math.abs(coords)),p);
+ }
+
+ result = Math.pow(result,(1.0/p));
+
+ if(result == Double.NaN || result == Double.POSITIVE_INFINITY || result == Double.NEGATIVE_INFINITY){
+ throw new ArithmeticException("Non valid number: "+ result +"\nVector: "+ this.toString() +"\n");
+ }
+
+ return result;
+ }
+
+ public double get(int index){
+ if(index < 0 || index >= values.length){
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ return values[index];
+ }
+
+ public void put(Vector vector) {
+ int oldValuesSize = this.values.length;
+ int size = vector.values.length + oldValuesSize;
+
+ //Resize the current values array while keeping its content.
+ values = Arrays.copyOf(values, size);
+
+ //Adding the vector given in parameter at the end of the current vector.
+ System.arraycopy(vector.values, 0, values, oldValuesSize, size-oldValuesSize);
+ }
+
+ public void put(double value) {
+ values = Arrays.copyOf(values, values.length+1);
+ values[values.length-1] = value;
+ }
+
+ public static double scalarProduct(Vector vector1, Vector vector2) {
+
+ //Checking if both vectors have the same size
+ if(vector1.values.length != vector2.values.length){
+ throw new ArithmeticException("Scalar Product with different sized vectors");
+ }
+
+ double result = 0;
+ int vectorlength = vector1.values.length;
+
+ for(int i = 0; i < vectorlength; i++){
+ result += vector1.values[i] * vector2.values[i];
+ }
+
+ return result;
+ }
+
+ public void minusForEach(double value){
+ for (int i = 0; i < values.length; i++) {
+ values[i] -= value;
+ }
+ }
+
+ //********* Standard Method ********//
+
+ @Override
+ public String toString(){
+ StringBuilder stringBuilder = new StringBuilder("[");
+
+ for(int i = 0; i < values.length-1; ++i){
+ if(i != values.length-1){
+ stringBuilder = stringBuilder.append(values[i]);
+ } else {
+ stringBuilder = stringBuilder.append(values[i]).append(", ");
+ }
+ }
+ stringBuilder = stringBuilder.append("]");
+
+ return stringBuilder.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj){
+ boolean vectorBool = false;
+ if(obj instanceof Vector){
+ Vector inputVector = (Vector) obj;
+
+ if(inputVector.values.length == values.length){
+ vectorBool = true;
+
+ for(int i = 0; i < values.length; ++i){
+ vectorBool = vectorBool && (inputVector.values[i] == values[i]);
+ }
+ }
+ }
+ return vectorBool;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/exceptions/NotInitializedException.java b/src/main/java/fr/insa/ocm/model/utils/exceptions/NotInitializedException.java
new file mode 100644
index 0000000..cccd09f
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/exceptions/NotInitializedException.java
@@ -0,0 +1,5 @@
+package fr.insa.ocm.model.utils.exceptions;
+
+
+public class NotInitializedException extends RuntimeException {
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/fastforward/FastForward.java b/src/main/java/fr/insa/ocm/model/utils/fastforward/FastForward.java
new file mode 100644
index 0000000..493e989
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/fastforward/FastForward.java
@@ -0,0 +1,192 @@
+package fr.insa.ocm.model.utils.fastforward;
+
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FastForward implements Runnable {
+
+ // Used by the caller of FastForward to know when it is finished.
+ private boolean finished;
+
+ // Used by the caller to force the stop of the fastforward.
+ private boolean stopRequested;
+
+ // Defines the number of learning round the fastforward needs to do and the number of seconds to wait between each of them.
+ private double numberRound;
+ private double numberSecPerRound;
+
+ // Used to describe the current progress of the fastforward.
+ private String currentOperation;
+ private String remainingTime;
+ private Double progressLearning;
+ private Double progressMining;
+
+ // Provided by the caller, indicate which patterns to keep/trash.
+ private List listConditions;
+ private Condition.ActionPatternChoice conditionPriority;
+
+ // Intermediate results, the caller needs to get the lasts results for the next learning round.
+ private List listKeptPatterns;
+ private List listNeutralPatterns;
+ private List listTrashedPatterns;
+
+ /**
+ *
+ * @param numberRounds The number of learning round to launch.
+ * @param numberSecPerRound The number of seconds to wait between each learning round.
+ * @param listCondition The list of conditions to keep/trash the patterns.
+ * @param conditionPriority Choose what kind of condition is the most important.
+ * @param listKeptPatterns The list of kept patterns selected during a previous search.
+ * @param listNeutralPatterns The list of neutral patterns selected during a previous search.
+ * @param listTrashedPatterns The list of trashed patterns selected during a previous search.
+ */
+ public FastForward(double numberRounds, double numberSecPerRound,
+ @NotNull List listCondition,
+ @NotNull Condition.ActionPatternChoice conditionPriority,
+ @NotNull List listKeptPatterns,
+ @NotNull List listNeutralPatterns,
+ @NotNull List listTrashedPatterns){
+ this.numberRound = numberRounds;
+ this.numberSecPerRound = numberSecPerRound;
+ this.listConditions = listCondition;
+ this.conditionPriority = conditionPriority;
+
+ this.listKeptPatterns = listKeptPatterns;
+ this.listNeutralPatterns = listNeutralPatterns;
+ this.listTrashedPatterns = listTrashedPatterns;
+
+ finished = false;
+
+ stopRequested = false;
+
+ progressLearning = 0d;
+ progressMining = 0d;
+ currentOperation = "";
+ remainingTime = "";
+ }
+
+ @Override
+ public void run() {
+
+ final int nbIter = 100;
+ try {
+ for (int i = 0; i < numberRound && !stopRequested; ++i) {
+ currentOperation = "Mining";
+ for (int j = 0; j < nbIter && !stopRequested; ++j) {
+ Thread.sleep((long)numberSecPerRound*1000/nbIter);
+ progressMining = (j+1d)/nbIter;
+
+ int remainingTimeSec = (int)((numberRound-i) * numberSecPerRound - (numberSecPerRound/nbIter) * j);
+ remainingTime = remainingTimeSec/60 + " min " + remainingTimeSec%60 + " sec";
+ }
+
+ // We process the data gathered by the mining algorithms.
+ currentOperation = "Processing data";
+
+
+ List bestPatterns = OCMManager.getNewRanking(listKeptPatterns, listNeutralPatterns, listTrashedPatterns);
+ List rawBestPatterns = new ArrayList<>(bestPatterns);
+
+ if(conditionPriority.equals(Condition.ActionPatternChoice.TRASH)){
+ listTrashedPatterns = computeListPattern(bestPatterns, rawBestPatterns, listConditions, Condition.ActionPatternChoice.TRASH);
+ bestPatterns.removeAll(listTrashedPatterns);
+ listKeptPatterns = computeListPattern(bestPatterns, rawBestPatterns, listConditions, Condition.ActionPatternChoice.KEEP);
+ bestPatterns.removeAll(listKeptPatterns);
+ listNeutralPatterns = new ArrayList<>(bestPatterns);
+ }else{
+ listKeptPatterns = computeListPattern(bestPatterns, rawBestPatterns, listConditions, Condition.ActionPatternChoice.KEEP);
+ bestPatterns.removeAll(listKeptPatterns);
+ listTrashedPatterns = computeListPattern(bestPatterns, rawBestPatterns, listConditions, Condition.ActionPatternChoice.TRASH);
+ bestPatterns.removeAll(listTrashedPatterns);
+ listNeutralPatterns = new ArrayList<>(bestPatterns);
+ }
+
+ int nbPatternRank = rawBestPatterns.size();
+ double nbQuality = listKeptPatterns.size() - listTrashedPatterns.size();
+ nbQuality = ((nbQuality/nbPatternRank)+1)/2;
+
+ System.err.println(listKeptPatterns.size() +"-"+ listNeutralPatterns.size()
+ + "-" + listTrashedPatterns.size() + " : " + nbQuality);
+
+ DebugLogger.printQuality(i +";"+ nbQuality);
+
+ progressLearning = ((i+1d)/numberRound);
+ }
+ }catch(InterruptedException e){
+ e.printStackTrace();
+ }
+ currentOperation = "Finished";
+ remainingTime = "Finished";
+
+ finished = true;
+ }
+
+ //********** Internal Methods **********//
+
+ private List computeListPattern(List listPattern,
+ List rawListPattern,
+ List listCondition,
+ Condition.ActionPatternChoice actionOnPattern){
+ List listResultPattern = new ArrayList<>();
+
+ for(Pattern pattern : listPattern){
+ //Check if one of the condition to trash is met by the pattern.
+ for(Condition condition : listCondition){
+ if(condition.getActionPatternChoice().equals(actionOnPattern) && condition.isMet(pattern, rawListPattern)){
+ listResultPattern.add(pattern);
+ break;
+ }
+ }
+ }
+
+ return listResultPattern;
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+ public boolean isFinished(){
+ return finished;
+ }
+
+ public String getCurrentOperation() {
+ return currentOperation;
+ }
+
+ public String getRemainingTime() {
+ return remainingTime;
+ }
+
+ public Double getProgressLearning() {
+ return progressLearning;
+ }
+
+ public Double getProgressMining() {
+ return progressMining;
+ }
+
+ public List getListKeptPatterns() {
+ return listKeptPatterns;
+ }
+
+ public List getListNeutralPatterns() {
+ return listNeutralPatterns;
+ }
+
+ public List getListTrashedPatterns() {
+ return listTrashedPatterns;
+ }
+
+ // Setters //
+ public void setStopRequested() {
+ this.stopRequested = true;
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/Condition.java b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/Condition.java
new file mode 100644
index 0000000..502f17a
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/Condition.java
@@ -0,0 +1,75 @@
+package fr.insa.ocm.model.utils.fastforward.condition;
+
+
+import com.google.gson.annotations.Expose;
+import com.sun.istack.internal.NotNull;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.List;
+
+public abstract class Condition {
+
+ public enum ActionPatternChoice {
+ KEEP, NEUTRAL, TRASH;
+
+ @Override
+ public String toString() {
+ switch(this){
+ case KEEP:
+ return "Keep";
+ case NEUTRAL:
+ return "Neutral";
+ case TRASH:
+ return "Trash";
+ default:
+ return "";
+ }
+ }
+ }
+
+ public enum ConditionType {
+ CONDITION_ATTRIBUTE,
+ CONDITION_ALGORITHM,
+ CONDITION_MEASURE_STATIC,
+ CONDITION_MEASURE_DYNAMIC,
+ CONDITION_MEASURE_BETWEEN;
+
+ @Override
+ public String toString() {
+ switch(this){
+ case CONDITION_ATTRIBUTE:
+ return "Condition Attribute";
+ case CONDITION_ALGORITHM:
+ return "Condition Algorithm";
+ case CONDITION_MEASURE_STATIC:
+ return "Condition Measure Static";
+ case CONDITION_MEASURE_BETWEEN:
+ return "Condition Measure Between";
+ case CONDITION_MEASURE_DYNAMIC:
+ return "Condition Measure Dynamic";
+ default:
+ return "";
+ }
+ }
+ }
+
+ @Expose private ActionPatternChoice actionPatternChoice;
+ @Expose private ConditionType conditionType;
+
+ /**
+ * For Deserialization purpose only.
+ */
+ protected Condition(){}
+
+ protected Condition(@NotNull ActionPatternChoice actionPatternChoice,
+ @NotNull ConditionType conditionType){
+ this.actionPatternChoice = actionPatternChoice;
+ this.conditionType = conditionType;
+ }
+
+ public abstract boolean isMet(Pattern pattern, List listPattern);
+
+ public ActionPatternChoice getActionPatternChoice(){ return actionPatternChoice; }
+
+ public ConditionType getConditionType(){ return conditionType; }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionAlgorithm.java b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionAlgorithm.java
new file mode 100644
index 0000000..149a08a
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionAlgorithm.java
@@ -0,0 +1,63 @@
+package fr.insa.ocm.model.utils.fastforward.condition;
+
+import com.google.gson.annotations.Expose;
+import com.sun.istack.internal.NotNull;
+import com.sun.istack.internal.Nullable;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.List;
+
+public class ConditionAlgorithm extends Condition {
+
+ public enum AlgorithmState{
+ CREATEDBY, NOT_CREATEDBY;
+
+ @Override
+ public String toString() {
+ switch(this){
+ case CREATEDBY:
+ return "has been created";
+ case NOT_CREATEDBY:
+ return "has not been created";
+ default:
+ return "";
+ }
+ }
+ }
+
+ @Expose private AlgorithmState algorithmState;
+ @Expose private String algorithmName;
+
+ /**
+ * For Deserialization purpose only.
+ */
+ public ConditionAlgorithm(){}
+
+ public ConditionAlgorithm(@NotNull ActionPatternChoice actionPatternChoice,
+ String algorithmName,
+ AlgorithmState algorithmState){
+ super(actionPatternChoice, ConditionType.CONDITION_ALGORITHM);
+
+ this.algorithmState = algorithmState;
+ this.algorithmName = algorithmName;
+ }
+
+ @Override
+ public boolean isMet(Pattern pattern, @Nullable List listPattern) {
+ return (pattern.getAlgorithmName().equals(algorithmName) && algorithmState.equals(AlgorithmState.CREATEDBY))
+ || (!pattern.getAlgorithmName().equals(algorithmName) && algorithmState.equals(AlgorithmState.NOT_CREATEDBY));
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ public AlgorithmState getAlgorithmState() {
+ return algorithmState;
+ }
+
+ public String getAlgorithmName() {
+ return algorithmName;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionAttribute.java b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionAttribute.java
new file mode 100644
index 0000000..c09678c
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionAttribute.java
@@ -0,0 +1,76 @@
+package fr.insa.ocm.model.utils.fastforward.condition;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.List;
+
+public class ConditionAttribute extends Condition {
+
+ public enum AttributeState{
+ PRESENT, ABSENT;
+
+ @Override
+ public String toString() {
+ switch(this){
+ case PRESENT:
+ return "present";
+ case ABSENT:
+ return "absent";
+ default:
+ return "";
+ }
+ }
+ }
+
+ @Expose private String attributeName;
+ @Expose private AttributeState attributeState;
+
+ /**
+ * For Deserialization purpose only.
+ */
+ public ConditionAttribute(){}
+
+ public ConditionAttribute(ActionPatternChoice actionPatternChoice,
+ String attributeName,
+ AttributeState attributeState){
+ super(actionPatternChoice, ConditionType.CONDITION_ATTRIBUTE);
+
+ this.attributeName = attributeName;
+ this.attributeState = attributeState;
+ }
+
+ @Override
+ public boolean isMet(Pattern pattern, List listPattern) {
+ return this.isMet(pattern);
+ }
+
+ private boolean isMet(Pattern pattern) {
+ boolean isMet = false;
+ if(attributeState.equals(AttributeState.ABSENT)){
+ isMet = true;
+ }
+
+ // Search in all the attribute names of the pattern.
+ // If the attribute should be absent to met the condition, if there is no such attribute in the pattern, isMet will remain true.
+ // If the attribute should be present to met the condition, if there is a such attribute in the pattern, isMet will switch from false to true once.
+ for (String patternAttributeName : pattern.getListAttributeNames()) {
+ if(patternAttributeName.equals(attributeName)){
+ isMet = !isMet;
+ break;
+ }
+ }
+ return isMet;
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ public String getAttributeName() {
+ return attributeName;
+ }
+
+ public AttributeState getAttributeState() {
+ return attributeState;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionMeasureBetween.java b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionMeasureBetween.java
new file mode 100644
index 0000000..bf4b6e4
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionMeasureBetween.java
@@ -0,0 +1,130 @@
+package fr.insa.ocm.model.utils.fastforward.condition;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public class ConditionMeasureBetween extends Condition {
+
+ public enum IntervalChoice{
+ INSIDE_IN_IN, INSIDE_IN_OUT, INSIDE_OUT_IN, INSIDE_OUT_OUT,
+ OUTSIDE_IN_IN, OUTSIDE_IN_OUT, OUTSIDE_OUT_IN, OUTSIDE_OUT_OUT;
+
+ @Override
+ public String toString() {
+ switch(this){
+ case INSIDE_IN_IN:
+ return "is between [X;Y]";
+ case INSIDE_IN_OUT:
+ return "is between [X;Y[";
+ case INSIDE_OUT_IN:
+ return "is between ]X;Y]";
+ case INSIDE_OUT_OUT:
+ return "is between ]X;Y[";
+ case OUTSIDE_IN_IN:
+ return "is not between [X;Y]";
+ case OUTSIDE_IN_OUT:
+ return "is not between [X;Y[";
+ case OUTSIDE_OUT_IN:
+ return "is not between ]X;Y]";
+ case OUTSIDE_OUT_OUT:
+ return "is not between ]X;Y[";
+ default:
+ return "Oh no D:";
+ }
+ }
+ }
+
+ @Expose private ConditionMeasureStatic lowerBound;
+ @Expose private ConditionMeasureStatic higerBound;
+ @Expose private IntervalChoice intervalChoice;
+
+ public ConditionMeasureBetween(){}
+
+ public ConditionMeasureBetween(@NotNull ActionPatternChoice actionPatternChoice,
+ Pattern.MeasureType measureType,
+ IntervalChoice intervalChoice, double thresholdValueLowerBound, double thresholdValueHigherBound){
+ super(actionPatternChoice, ConditionType.CONDITION_MEASURE_BETWEEN);
+
+ ConditionMeasureStatic.OperatorType operatorTypeLowerBound = null;
+ ConditionMeasureStatic.OperatorType operatorTypeHigherBound = null;
+
+ switch(intervalChoice){
+ case INSIDE_IN_IN:
+ operatorTypeLowerBound = ConditionMeasureStatic.OperatorType.GET;
+ operatorTypeHigherBound = ConditionMeasureStatic.OperatorType.LET;
+ break;
+ case INSIDE_IN_OUT:
+ operatorTypeLowerBound = ConditionMeasureStatic.OperatorType.GET;
+ operatorTypeHigherBound = ConditionMeasureStatic.OperatorType.LT;
+ break;
+ case INSIDE_OUT_IN:
+ operatorTypeLowerBound = ConditionMeasureStatic.OperatorType.GT;
+ operatorTypeHigherBound = ConditionMeasureStatic.OperatorType.LET;
+ break;
+ case INSIDE_OUT_OUT:
+ operatorTypeLowerBound = ConditionMeasureStatic.OperatorType.GT;
+ operatorTypeHigherBound = ConditionMeasureStatic.OperatorType.LT;
+ break;
+ case OUTSIDE_IN_IN:
+ operatorTypeLowerBound = ConditionMeasureStatic.OperatorType.LET;
+ operatorTypeHigherBound = ConditionMeasureStatic.OperatorType.GET;
+ break;
+ case OUTSIDE_IN_OUT:
+ operatorTypeLowerBound = ConditionMeasureStatic.OperatorType.LET;
+ operatorTypeHigherBound = ConditionMeasureStatic.OperatorType.GT;
+ break;
+ case OUTSIDE_OUT_IN:
+ operatorTypeLowerBound = ConditionMeasureStatic.OperatorType.LT;
+ operatorTypeHigherBound = ConditionMeasureStatic.OperatorType.GET;
+ break;
+ case OUTSIDE_OUT_OUT:
+ operatorTypeLowerBound = ConditionMeasureStatic.OperatorType.LT;
+ operatorTypeHigherBound = ConditionMeasureStatic.OperatorType.GT;
+ break;
+ }
+
+ this.intervalChoice = intervalChoice;
+ this.lowerBound = new ConditionMeasureStatic(actionPatternChoice, measureType, operatorTypeLowerBound, thresholdValueLowerBound);
+ this.higerBound = new ConditionMeasureStatic(actionPatternChoice, measureType, operatorTypeHigherBound, thresholdValueHigherBound);
+ }
+
+ @Override
+ public boolean isMet(Pattern pattern, List listPattern) {
+ switch(intervalChoice){
+ case INSIDE_IN_IN:
+ case INSIDE_IN_OUT:
+ case INSIDE_OUT_IN:
+ case INSIDE_OUT_OUT:
+ return lowerBound.isMet(pattern, listPattern) && higerBound.isMet(pattern, listPattern);
+ case OUTSIDE_IN_IN:
+ case OUTSIDE_IN_OUT:
+ case OUTSIDE_OUT_IN:
+ case OUTSIDE_OUT_OUT:
+ return lowerBound.isMet(pattern, listPattern) || higerBound.isMet(pattern, listPattern);
+ default:
+ return false;
+ }
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ public ConditionMeasureStatic getHigerBound() {
+ return higerBound;
+ }
+
+ public ConditionMeasureStatic getLowerBound() {
+ return lowerBound;
+ }
+
+ public IntervalChoice getIntervalChoice() {
+ return intervalChoice;
+ }
+
+ public Pattern.MeasureType getMeasureType(){ return lowerBound.getMeasure(); }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionMeasureDynamic.java b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionMeasureDynamic.java
new file mode 100644
index 0000000..1e8b113
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionMeasureDynamic.java
@@ -0,0 +1,108 @@
+package fr.insa.ocm.model.utils.fastforward.condition;
+
+import com.google.gson.annotations.Expose;
+import com.sun.istack.internal.NotNull;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConditionMeasureDynamic extends Condition {
+
+ public enum CatogoryType{
+ HIGHEST, LOWEST;
+
+ @Override
+ public String toString() {
+ switch(this){
+ case HIGHEST:
+ return "highest";
+ case LOWEST:
+ return "lowest";
+ default:
+ return "";
+ }
+ }
+ }
+
+ @Expose private Pattern.MeasureType measure;
+ @Expose private CatogoryType catogory;
+ @Expose private int index;
+
+ /**
+ * For Deserialization purpose only.
+ */
+ public ConditionMeasureDynamic(){}
+
+ public ConditionMeasureDynamic(@NotNull ActionPatternChoice actionPatternChoice,
+ Pattern.MeasureType measure,
+ CatogoryType catogory,
+ int index){
+ super(actionPatternChoice, ConditionType.CONDITION_MEASURE_DYNAMIC);
+
+ this.measure = measure;
+ this.catogory = catogory;
+ this.index = index;
+ }
+
+ @Override
+ public boolean isMet(Pattern pattern, List listPattern) {
+ double measureValue = this.getMeasureFromPattern(pattern);
+ double measureThresholdValue;
+ boolean conditionIsMet = false;
+ List sortedListPattern = this.getSortedListPattern(listPattern);
+
+ switch(catogory){
+ case HIGHEST:
+ measureThresholdValue = this.getMeasureFromPattern(sortedListPattern.get(index));
+ conditionIsMet = measureValue >= measureThresholdValue;
+ break;
+ case LOWEST:
+ measureThresholdValue = this.getMeasureFromPattern(sortedListPattern.get(sortedListPattern.size() - index));
+ conditionIsMet = measureValue <= measureThresholdValue;
+ break;
+ }
+
+ return conditionIsMet;
+ }
+
+ private double getMeasureFromPattern(Pattern pattern){
+ return pattern.getMeasureValue(measure);
+ }
+
+ private List getSortedListPattern(List listPattern){
+ List sortedListPattern = new ArrayList<>(listPattern);
+
+ sortedListPattern.sort((pattern1, pattern2) -> {
+ double measurePattern1 = this.getMeasureFromPattern(pattern1);
+ double measurePattern2 = this.getMeasureFromPattern(pattern2);
+ double difference = measurePattern1 - measurePattern2;
+ if(difference < 0){
+ return 1;
+ }else if(difference > 0){
+ return -1;
+ }else{
+ return 0;
+ }
+ });
+
+ return sortedListPattern;
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ public Pattern.MeasureType getMeasure() {
+ return measure;
+ }
+
+ public CatogoryType getCatogory() {
+ return catogory;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionMeasureStatic.java b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionMeasureStatic.java
new file mode 100644
index 0000000..520527f
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/fastforward/condition/ConditionMeasureStatic.java
@@ -0,0 +1,95 @@
+package fr.insa.ocm.model.utils.fastforward.condition;
+
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.List;
+
+public class ConditionMeasureStatic extends Condition {
+
+ public enum OperatorType{
+ LT, LET, EQL, GET, GT;
+
+ @Override
+ public String toString() {
+ String result;
+ if(this == LT){
+ result = "<";
+ }else if(this == LET){
+ result = "<=";
+ }else if(this == EQL){
+ result = "=";
+ }else if(this == GET){
+ result = ">=";
+ }else{
+ result = ">";
+ }
+ return result;
+ }
+
+ //TODO Javadoc, dire qu'on fait value1 Operation value2 et qu'on vérifie si c'est vrai
+ public boolean isValid(double value1, double value2){
+ switch (this){
+ case LT:
+ return value1 < value2;
+ case LET:
+ return value1 <= value2;
+ case EQL:
+ return value1 == value2;
+ case GET:
+ return value1 >= value2;
+ case GT:
+ return value1 > value2;
+ default:
+ return false;
+ }
+ }
+ }
+
+ @Expose private Pattern.MeasureType measure;
+ @Expose private OperatorType operator;
+ @Expose private double thresholdValue;
+
+ /**
+ * For Deserialization purpose only.
+ */
+ public ConditionMeasureStatic(){}
+
+ public ConditionMeasureStatic(ActionPatternChoice actionPatternChoice,
+ Pattern.MeasureType measure,
+ OperatorType operator,
+ double thresholdValue){
+ super(actionPatternChoice, ConditionType.CONDITION_MEASURE_STATIC);
+
+ this.measure = measure;
+ this.operator = operator;
+ this.thresholdValue = thresholdValue;
+ }
+
+ @Override
+ public boolean isMet(Pattern pattern, List listPattern) {
+ return this.isMet(pattern);
+ }
+
+ private boolean isMet(Pattern pattern) {
+ return operator.isValid(pattern.getMeasureValue(measure), thresholdValue);
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ public Pattern.MeasureType getMeasure() {
+ return measure;
+ }
+
+ public OperatorType getOperator() {
+ return operator;
+ }
+
+ public double getThresholdValue() {
+ return thresholdValue;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/serialize/OCMDeserializer.java b/src/main/java/fr/insa/ocm/model/utils/serialize/OCMDeserializer.java
new file mode 100644
index 0000000..757a2c4
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/serialize/OCMDeserializer.java
@@ -0,0 +1,222 @@
+package fr.insa.ocm.model.utils.serialize;
+
+import com.google.gson.*;
+import com.sun.istack.internal.NotNull;
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.oneclicklearning.bandit.CesaBianciBandit;
+import fr.insa.ocm.model.oneclicklearning.bandit.MultiArmedBandit;
+import fr.insa.ocm.model.oneclicklearning.cache.api.Cache;
+import fr.insa.ocm.model.oneclicklearning.cache.rank.CacheRanking;
+import fr.insa.ocm.model.oneclicklearning.cache.set.CacheSet;
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.api.CoactiveLearning;
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.ranking.CoactiveLearningRanking;
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.set.CoactiveLearningSet;
+import fr.insa.ocm.model.utils.Rank;
+import fr.insa.ocm.model.utils.SystemState;
+import fr.insa.ocm.model.utils.Vector;
+import fr.insa.ocm.model.wrapper.api.AbstractPattern;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.model.wrapper.realkd.PatternRealKD;
+import fr.insa.ocm.model.wrapper.spmf.PatternSPMF;
+
+import java.io.*;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+public class OCMDeserializer {
+
+ private class PatternDeserializer implements JsonDeserializer{
+
+ @Override
+ public Pattern deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ JsonObject jsonPattern = jsonElement.getAsJsonObject();
+
+ AbstractPattern.WrapperType wrapperType;
+ Vector attributeVector;
+ String patternDescriptor;
+ String algorithmType;
+ double[] measures;
+ List listAttributeNames = new ArrayList<>();
+
+ wrapperType = jsonDeserializer.fromJson(jsonPattern.get("wrapperType"), Pattern.WrapperType.class);
+ attributeVector = jsonDeserializer.fromJson(jsonPattern.get("attributeVector"), Vector.class);
+ patternDescriptor = jsonElement.getAsJsonObject().get("patternDescriptor").getAsString();
+ algorithmType = jsonElement.getAsJsonObject().get("algorithmType").getAsString();
+ JsonArray jsonMeasures = jsonPattern.getAsJsonArray("measures");
+ JsonArray jsonListAttributeNames = jsonElement.getAsJsonObject().get("listAttributeNames").getAsJsonArray();
+
+ measures = new double[jsonMeasures.size()];
+ for(int i = 0; i < jsonMeasures.size(); ++i){
+ measures[i] = jsonMeasures.get(i).getAsDouble();
+ }
+ for(int i = 0; i < jsonListAttributeNames.size(); ++i){
+ listAttributeNames.add(jsonListAttributeNames.get(i).getAsString());
+ }
+
+ // Special parts for each kind of pattern
+ switch (wrapperType){
+ case REALKD:
+ String typePattern = jsonElement.getAsJsonObject().get("type").getAsString();
+
+ return new PatternRealKD(typePattern, listAttributeNames,
+ attributeVector, measures,
+ patternDescriptor, algorithmType);
+ case SPMF:
+ return new PatternSPMF(listAttributeNames, attributeVector,
+ measures,
+ patternDescriptor, algorithmType);
+ default:
+ DebugLogger.printDebug("OCMDeserialiazer: Impossible to retrieve the type of Pattern saved.", DebugLogger.MessageSeverity.HIGH);
+ throw new RuntimeException("Impossible to determine the type of pattern to deserialize.");
+ }
+
+
+ }
+ }
+
+ private class BanditDeserializer implements JsonDeserializer{
+
+ @Override
+ public MultiArmedBandit deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ JsonArray jsonWeights = jsonElement.getAsJsonObject().getAsJsonArray("weights");
+
+ double[] weights = new double[jsonWeights.size()];
+ int round = jsonElement.getAsJsonObject().get("round").getAsInt();
+ double V = jsonElement.getAsJsonObject().get("V").getAsDouble();
+
+ for(int i = 0; i < jsonWeights.size(); ++i){
+ weights[i] = jsonWeights.get(i).getAsDouble();
+ }
+
+ return new CesaBianciBandit(weights, round, V);
+ }
+ }
+
+ private class CacheDeserializer implements JsonDeserializer{
+
+ @Override
+ public Cache deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ JsonObject jsonCache = jsonElement.getAsJsonObject();
+
+ Cache.CacheType cacheType = jsonDeserializer.fromJson(jsonCache.get("cacheType"), Cache.CacheType.class);
+ Integer sizeListBestPatterns = jsonCache.get("sizeListBestPatterns").getAsInt();
+
+ switch (cacheType){
+ case RANKING:
+ Double utility = jsonCache.get("utility").getAsDouble();
+ Rank greedyRanking = new Rank<>();
+
+ JsonArray jsonGreedyRanking = jsonCache.get("greedyRanking").getAsJsonArray();
+ for(int i = 0; i < jsonGreedyRanking.size(); ++i){
+ greedyRanking.add(jsonDeserializer.fromJson(jsonGreedyRanking.get(0), Pattern.class));
+ }
+
+ return new CacheRanking(sizeListBestPatterns, cacheType,
+ utility, greedyRanking);
+ case SET:
+ return new CacheSet(sizeListBestPatterns, cacheType);
+ default:
+ DebugLogger.printDebug("OCMDeserializer: Impossible to retrieve the type of Cache saved.", DebugLogger.MessageSeverity.HIGH);
+ throw new RuntimeException("Impossible to determine the type of cache to deserialize.");
+ }
+ }
+ }
+
+ private class CoactiveLearningDeserializer implements JsonDeserializer{
+
+ @Override
+ public CoactiveLearning deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ JsonObject jsonCoactive = jsonElement.getAsJsonObject();
+
+ // Deserialize common attributs of the CoactiveLearning
+ CoactiveLearning.CoactiveType coactiveType =
+ jsonDeserializer.fromJson(jsonCoactive.get("coactiveType"), CoactiveLearning.CoactiveType.class);
+ Vector weights = jsonDeserializer.fromJson(jsonCoactive.get("weights"), Vector.class);
+ SystemState previousSystemState =
+ jsonDeserializer.fromJson(jsonCoactive.get("previousSystemState"), SystemState.class);
+ int nbCycles = jsonCoactive.get("nbCycles").getAsInt();
+ int nbInterestingnessMeasures = jsonCoactive.get("nbInterestingnessMeasures").getAsInt();
+
+ switch (coactiveType){
+ case RANKING:
+ int d = jsonCoactive.get("d").getAsInt();
+ return new CoactiveLearningRanking(weights,
+ previousSystemState,
+ nbCycles,
+ nbInterestingnessMeasures,
+ coactiveType,
+ d);
+ case SET:
+ return new CoactiveLearningSet(weights,
+ previousSystemState,
+ nbCycles,
+ nbInterestingnessMeasures,
+ coactiveType);
+ default:
+ DebugLogger.printDebug("OCMDeserializer: Impossible to retrieve the type of CoactiveLearning saved.", DebugLogger.MessageSeverity.HIGH);
+ throw new RuntimeException("Impossible to determine the type of coactive learning to deserialize.");
+ }
+ }
+ }
+
+ private static Gson jsonDeserializer;
+ private SearchSave searchSave;
+
+ private String deserializedForm;
+ private String pathLoadFile;
+
+ public OCMDeserializer(@NotNull String pathLoadFile){
+ this.pathLoadFile = pathLoadFile;
+ this.deserializedForm = "";
+
+ jsonDeserializer = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
+ .setPrettyPrinting()
+ .registerTypeAdapter(MultiArmedBandit.class, new BanditDeserializer())
+ .registerTypeAdapter(Pattern.class, new PatternDeserializer())
+ .registerTypeAdapter(Cache.class, new CacheDeserializer())
+ .registerTypeAdapter(CoactiveLearning.class, new CoactiveLearningDeserializer())
+ .create();
+ searchSave = new SearchSave();
+
+ this.deserialize();
+ }
+
+ private void deserialize(){
+ DebugLogger.printDebug("OCMDeserializer: Loading.");
+ this.loadFile();
+
+ searchSave = jsonDeserializer.fromJson(deserializedForm, SearchSave.class);
+
+ DebugLogger.printDebug("OCMDeserializer: Deserializing.");
+ OCMManager.deserialize(searchSave);
+ }
+
+ private void loadFile(){
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(new File(pathLoadFile)));
+
+ String line;
+ StringBuilder stringBuilder = new StringBuilder();
+
+ while ((line = reader.readLine()) != null){
+ stringBuilder = stringBuilder.append(line);
+ }
+
+ deserializedForm = stringBuilder.toString();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if(reader != null){
+ try {
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/serialize/OCMSerializer.java b/src/main/java/fr/insa/ocm/model/utils/serialize/OCMSerializer.java
new file mode 100644
index 0000000..898d8f7
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/serialize/OCMSerializer.java
@@ -0,0 +1,57 @@
+package fr.insa.ocm.model.utils.serialize;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.sun.istack.internal.NotNull;
+import fr.insa.ocm.model.OCMManager;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class OCMSerializer {
+
+ private Gson jsonSerializer;
+ private SearchSave searchSave;
+
+ private String serializedForm;
+ private String pathSaveFile;
+
+ public OCMSerializer(@NotNull String pathSaveFile){
+ this.pathSaveFile = pathSaveFile;
+ serializedForm = "";
+
+ jsonSerializer = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setPrettyPrinting().create();
+ searchSave = new SearchSave();
+
+ this.serialize();
+ }
+
+ private void serialize(){
+ OCMManager.serialize(searchSave);
+
+ serializedForm = jsonSerializer.toJson(searchSave);
+
+ this.saveFile();
+ }
+
+ private void saveFile(){
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(new File(pathSaveFile)));
+
+ writer.write(serializedForm);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if(writer != null){
+ try {
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/serialize/SearchSave.java b/src/main/java/fr/insa/ocm/model/utils/serialize/SearchSave.java
new file mode 100644
index 0000000..7aaf680
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/serialize/SearchSave.java
@@ -0,0 +1,87 @@
+package fr.insa.ocm.model.utils.serialize;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.oneclicklearning.algorithmmanager.AlgorithmManager;
+import fr.insa.ocm.model.oneclicklearning.cache.api.Cache;
+import fr.insa.ocm.model.oneclicklearning.cache.rank.CacheRanking;
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.api.CoactiveLearning;
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.ranking.CoactiveLearningRanking;
+import fr.insa.ocm.model.oneclicklearning.bandit.MultiArmedBandit;
+import fr.insa.ocm.model.utils.PatternWarehouse;
+import fr.insa.ocm.model.utils.SystemState;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.List;
+
+public class SearchSave {
+
+ @Expose private MultiArmedBandit bandit;
+ @Expose private Cache cache;
+ @Expose private CoactiveLearning coactiveLearning;
+ @Expose private PatternWarehouse patternWarehouse;
+ @Expose private SystemState currentState;
+ @Expose private boolean isInitialized;
+ @Expose private boolean firstRanking;
+
+ public SearchSave(){}
+
+ //********** Saving Methods **********//
+
+ public void setMultiArmedBandit(MultiArmedBandit bandit){
+ this.bandit = bandit;
+ }
+
+ public void setCache(Cache cache){
+ this.cache = cache;
+ }
+
+ public void setCoactiveLearning(CoactiveLearning coactiveLearning){
+ this.coactiveLearning = coactiveLearning;
+ }
+
+ public void setPatternWarehouse(PatternWarehouse patternWarehouse){
+ this.patternWarehouse = patternWarehouse;
+ }
+
+ public void setCurrentState(SystemState currentState){
+ this.currentState = currentState;
+ }
+
+ public void setInitialized(boolean initialized) {
+ isInitialized = initialized;
+ }
+
+ public void setFirstRanking(boolean firstRanking) {
+ this.firstRanking = firstRanking;
+ }
+
+ //********** Loading Methods **********//
+
+ public MultiArmedBandit getMultiArmedBandit(){
+ return bandit;
+ }
+
+ public Cache getCache(){
+ return cache;
+ }
+
+ public CoactiveLearning getCoactiveLearning(){
+ return coactiveLearning;
+ }
+
+ public PatternWarehouse getPatternWarehouse(){
+ return patternWarehouse;
+ }
+
+ public SystemState getCurrentState() {
+ return currentState;
+ }
+
+ public boolean getInitialized() {
+ return isInitialized;
+ }
+
+ public boolean getFirstRanking() {
+ return firstRanking;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/serialize/condition/Conditions.java b/src/main/java/fr/insa/ocm/model/utils/serialize/condition/Conditions.java
new file mode 100644
index 0000000..eea20cb
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/serialize/condition/Conditions.java
@@ -0,0 +1,19 @@
+package fr.insa.ocm.model.utils.serialize.condition;
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+
+import java.util.List;
+
+public class Conditions {
+
+ @Expose private List conditions;
+
+ public List getConditions() {
+ return conditions;
+ }
+
+ public void setConditions(List conditions) {
+ this.conditions = conditions;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/serialize/condition/ConditionsDeserializer.java b/src/main/java/fr/insa/ocm/model/utils/serialize/condition/ConditionsDeserializer.java
new file mode 100644
index 0000000..20e1442
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/serialize/condition/ConditionsDeserializer.java
@@ -0,0 +1,67 @@
+package fr.insa.ocm.model.utils.serialize.condition;
+
+import com.google.gson.*;
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.oneclicklearning.bandit.MultiArmedBandit;
+import fr.insa.ocm.model.utils.fastforward.condition.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.List;
+
+public class ConditionsDeserializer {
+
+ private static class ConditionDeserializer implements JsonDeserializer{
+
+ @Override
+ public Condition deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ Condition.ConditionType conditionType = jsonDeserializer.fromJson(jsonElement.getAsJsonObject().get("conditionType"), Condition.ConditionType.class);
+
+ switch (conditionType){
+ case CONDITION_ATTRIBUTE:
+ return jsonDeserializer.fromJson(jsonElement, ConditionAttribute.class);
+ case CONDITION_ALGORITHM:
+ return jsonDeserializer.fromJson(jsonElement, ConditionAttribute.class);
+ case CONDITION_MEASURE_STATIC:
+ return jsonDeserializer.fromJson(jsonElement, ConditionMeasureStatic.class);
+ case CONDITION_MEASURE_DYNAMIC:
+ return jsonDeserializer.fromJson(jsonElement, ConditionMeasureDynamic.class);
+ case CONDITION_MEASURE_BETWEEN:
+ return jsonDeserializer.fromJson(jsonElement, ConditionMeasureBetween.class);
+ default:
+ DebugLogger.printDebug("ConditionsDeserializer: Impossible to retrieve the type of Condition saved.", DebugLogger.MessageSeverity.HIGH);
+ throw new RuntimeException("Impossible to determine the type of condition to deserialize.");
+ }
+ }
+ }
+
+ private static Gson jsonDeserializer;
+
+ public static List loadConditions(@NotNull String pathLoadFile){
+ StringBuilder sbSerializedForm = new StringBuilder("");
+ Conditions conditions;
+ jsonDeserializer = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
+ .setPrettyPrinting()
+ .registerTypeAdapter(Condition.class, new ConditionDeserializer())
+ .create();
+
+ try(BufferedReader reader = new BufferedReader(new FileReader(pathLoadFile))){
+ String line;
+
+ while ((line = reader.readLine()) != null){
+ sbSerializedForm.append(line);
+ }
+
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ conditions = jsonDeserializer.fromJson(sbSerializedForm.toString(), Conditions.class);
+
+ return conditions.getConditions();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/utils/serialize/condition/ConditionsSerializer.java b/src/main/java/fr/insa/ocm/model/utils/serialize/condition/ConditionsSerializer.java
new file mode 100644
index 0000000..fc3a729
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/utils/serialize/condition/ConditionsSerializer.java
@@ -0,0 +1,34 @@
+package fr.insa.ocm.model.utils.serialize.condition;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+public class ConditionsSerializer {
+
+ public static void saveConditions(@NotNull String pathSaveFile, @NotNull List listConditions){
+ Gson jsonSerializer = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
+ .setPrettyPrinting()
+ .create();
+ Conditions conditions = new Conditions();
+ String serializedForm;
+
+ // Wrapping the list of conditions before serializing it.
+ conditions.setConditions(listConditions);
+ serializedForm = jsonSerializer.toJson(conditions);
+
+ // Writing the serialized form to the file.
+ try(BufferedWriter writer = new BufferedWriter(new FileWriter(pathSaveFile))){
+ writer.write(serializedForm);
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/api/AbstractAlgorithmLauncher.java b/src/main/java/fr/insa/ocm/model/wrapper/api/AbstractAlgorithmLauncher.java
new file mode 100644
index 0000000..f8d3982
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/api/AbstractAlgorithmLauncher.java
@@ -0,0 +1,48 @@
+package fr.insa.ocm.model.wrapper.api;
+
+import fr.insa.ocm.model.wrapper.realkd.AlgorithmLauncherRealKD;
+import fr.insa.ocm.model.wrapper.spmf.AlgorithmLauncherSPMF;
+
+import java.util.*;
+
+public abstract class AbstractAlgorithmLauncher implements AlgorithmLauncher {
+
+ // The monitoring the system.
+ // A map of the number of calls for each algorithms.
+ protected static Map mapNumberCallsPerAlgorithm = Collections.synchronizedMap(new HashMap<>());
+
+ // The list of all the attribute names in the current dataset.
+ protected List listAttributeName;
+
+ protected AbstractAlgorithmLauncher(){
+ listAttributeName = new ArrayList<>();
+ }
+
+ public abstract int getNbAlgorithms();
+
+ public abstract List startAlgorithm(int algorithmNumber);
+
+ public abstract void stopAlgorithm();
+
+ @Override
+ public int getNbAttributes() {
+ return listAttributeName.size();
+ }
+
+ @Override
+ public List getListAttributeName() {
+ return new ArrayList<>(listAttributeName);
+ }
+
+ public abstract List getListAlgorithmName();
+
+ public static Map getStatsAlgorithmsLaunched() {
+ return mapNumberCallsPerAlgorithm;
+ }
+
+ public static List getAllAvailableAlgorithm(){
+ List listAllAvailableAlgorithm = new ArrayList<>(AlgorithmLauncherRealKD.getListAvailableMiningAlgorithm());
+ listAllAvailableAlgorithm.addAll(AlgorithmLauncherSPMF.getListAvailableMiningAlgorithm());
+ return listAllAvailableAlgorithm;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/api/AbstractPattern.java b/src/main/java/fr/insa/ocm/model/wrapper/api/AbstractPattern.java
new file mode 100644
index 0000000..1820cc4
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/api/AbstractPattern.java
@@ -0,0 +1,129 @@
+package fr.insa.ocm.model.wrapper.api;
+
+
+import com.google.gson.annotations.Expose;
+import fr.insa.ocm.model.utils.Vector;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AbstractPattern implements Pattern {
+
+ //Intern variables describing the pattern.
+
+ @Expose protected double[] measures;
+
+ @Expose protected Vector attributeVector = new Vector();
+ @Expose protected List listAttributeNames = new ArrayList<>();
+
+ @Expose protected String patternDescriptor = "";
+ @Expose protected String algorithmType = "";
+ @Expose protected WrapperType wrapperType;
+
+ protected AbstractPattern(){
+ measures = new double[MeasureType.values().length];
+ for(int i = 0; i < measures.length; ++i){
+ measures[i] = 0d;
+ }
+ }
+
+ protected AbstractPattern(AbstractPattern pattern){
+ this.measures = pattern.measures;
+
+ this.attributeVector = new Vector(pattern.attributeVector);
+ this.listAttributeNames = new ArrayList<>(pattern.listAttributeNames);
+
+ this.patternDescriptor = pattern.patternDescriptor;
+ this.algorithmType = pattern.algorithmType;
+ this.wrapperType = pattern.wrapperType;
+ }
+
+ @Override
+ public double getMeasureValue(MeasureType measure){
+ return measures[measure.getIndex()];
+ }
+
+ /**
+ * Getter for the attribute vector used to compute the interestingness of this pattern.
+ * @return The vector of presence of attributes. If the attribute of rank i is present in the pattern, the ith value is 1 else it is 0.
+ */
+ @Override
+ public Vector getAttributesVector(){
+ return attributeVector;
+ }
+
+ @Override
+ public List getListAttributeNames(){ return new ArrayList<>(listAttributeNames); }
+
+ @Override
+ public String getAlgorithmName(){
+ return algorithmType;
+ }
+
+ /**
+ * The Vector contains an array of values from 0d to 1d. First come the measures (between 0d and 1d), then the attributes (0d or 1d), then the algorithm (0d or 1d).
+ * @param algorithmLauncher The algorithm launcher from which the pattern ha been created.
+ */
+ protected void computeAttributeVector(AlgorithmLauncher algorithmLauncher){
+ List listAllAttributeNames = algorithmLauncher.getListAttributeName();
+ List listAllAlgorithmNames = algorithmLauncher.getListAlgorithmName();
+ int nbMeasure = measures.length;
+ int nbAttr = listAllAttributeNames.size();
+ int nbAlg = listAllAlgorithmNames.size();
+
+ double attributeValue[] = new double[nbMeasure + nbAttr + nbAlg];
+ for(int i = 0; i < attributeValue.length; ++i){
+ attributeValue[i] = 0d;
+ }
+
+ int offset = 0;
+ System.arraycopy(measures, 0, attributeValue, 0, nbMeasure);
+
+ offset += nbMeasure;
+ for(int i = offset; i < offset+nbAttr; ++i){
+ if(listAttributeNames.contains(listAllAttributeNames.get(i-offset))){
+ attributeValue[i] = 1d;
+ }
+ }
+
+ offset += nbAttr;
+ for(int i = offset; i < offset+nbAlg; ++i){
+ if(listAllAlgorithmNames.get(i-offset).equals(algorithmType)){
+ attributeValue[i] = 1d;
+ }
+ }
+
+ attributeVector = new Vector(attributeValue);
+ }
+
+ /**
+ * Compute the list of attribute name of the current pattern.
+ * The list of those name is lazy computed.
+ */
+ protected void computeAttributeName(AlgorithmLauncher algorithmLauncher){
+ List listAlgoLaunAN = algorithmLauncher.getListAttributeName();
+
+ //For each attribute known in the initial data set, we search it in the pattern.
+ //If and only if the attribute is present in the patten, it is added to the listAttributeNames of the current pattern.
+ for (String attributeStr : listAlgoLaunAN) {
+ if (patternDescriptor.contains(attributeStr)) {
+ listAttributeNames.add(attributeStr);
+ }
+ }
+ }
+
+ //********** Standard Methods **********//
+
+ @Override
+ public boolean equals(Object obj){
+ boolean patternBool = false;
+
+ if(obj instanceof AbstractPattern){
+ AbstractPattern patternTmp = (AbstractPattern) obj;
+
+ patternBool = attributeVector.equals(patternTmp.attributeVector);
+ }
+
+ return patternBool;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/api/AlgorithmLauncher.java b/src/main/java/fr/insa/ocm/model/wrapper/api/AlgorithmLauncher.java
new file mode 100644
index 0000000..cbbcd9b
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/api/AlgorithmLauncher.java
@@ -0,0 +1,18 @@
+package fr.insa.ocm.model.wrapper.api;
+
+import java.util.List;
+
+public interface AlgorithmLauncher{
+
+ int getNbAlgorithms();
+
+ List startAlgorithm(int algorithmNumber);
+
+ void stopAlgorithm();
+
+ int getNbAttributes();
+
+ List getListAttributeName();
+
+ List getListAlgorithmName();
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/api/Pattern.java b/src/main/java/fr/insa/ocm/model/wrapper/api/Pattern.java
new file mode 100644
index 0000000..97153ee
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/api/Pattern.java
@@ -0,0 +1,93 @@
+package fr.insa.ocm.model.wrapper.api;
+
+import fr.insa.ocm.model.utils.Vector;
+import fr.insa.ocm.model.wrapper.realkd.AlgorithmLauncherRealKD;
+import fr.insa.ocm.model.wrapper.spmf.AlgorithmLauncherSPMF;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public interface Pattern{
+
+ enum WrapperType {
+ REALKD, SPMF;
+
+ @Override
+ public String toString() {
+ switch (this){
+ case SPMF:
+ return "SPMF";
+ case REALKD:
+ return "RealKD";
+ default:
+ return "";
+ }
+ }
+
+ public AlgorithmLauncher getAlgorithmLauncher(String pathRawData){
+ switch (this){
+ case SPMF:
+ return new AlgorithmLauncherSPMF(pathRawData);
+ case REALKD:
+ return new AlgorithmLauncherRealKD(pathRawData);
+ default:
+ return null;
+ }
+ }
+ }
+
+ enum MeasureType {
+ FREQUENCY,
+ RELATIVE_SHORTNESS,
+// LIFT,
+// SUBGROUP_INTERSTINGNESS,
+// TARGET_DEVIATION,
+ RELATIVE_PERIODICITY;
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case FREQUENCY:
+ return "Frequency";
+ case RELATIVE_SHORTNESS:
+ return "Relative Shortness";
+// case LIFT:
+// return "Lift";
+// case SUBGROUP_INTERSTINGNESS:
+// return "SubGroup Interestingness";
+// case TARGET_DEVIATION:
+// return "Target Deviation";
+ case RELATIVE_PERIODICITY:
+ return "Relative Periodicity";
+ default:
+ return "";
+ }
+ }
+
+ public int getIndex() {
+ MeasureType[] values = values();
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].equals(this)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public static MeasureType getName(int index) {
+ return values()[index];
+ }
+ }
+
+ double getMeasureValue(MeasureType measure);
+
+ Vector getAttributesVector();
+
+ String getAlgorithmName();
+
+ String toString();
+
+ List getListAttributeNames();
+
+ Pattern copy();
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/realkd/AlgorithmLauncherRealKD.java b/src/main/java/fr/insa/ocm/model/wrapper/realkd/AlgorithmLauncherRealKD.java
new file mode 100644
index 0000000..049a325
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/realkd/AlgorithmLauncherRealKD.java
@@ -0,0 +1,248 @@
+package fr.insa.ocm.model.wrapper.realkd;
+
+import de.unibonn.realkd.algorithms.StoppableMiningAlgorithm;
+import de.unibonn.realkd.algorithms.association.AssociationMiningBeamSearch;
+import de.unibonn.realkd.algorithms.association.AssociationSampler;
+import de.unibonn.realkd.algorithms.emm.ExceptionalModelSampler;
+import de.unibonn.realkd.common.workspace.Workspace;
+import de.unibonn.realkd.common.workspace.Workspaces;
+import de.unibonn.realkd.data.propositions.*;
+import de.unibonn.realkd.data.table.DataTable;
+import de.unibonn.realkd.data.table.attribute.Attribute;
+import fr.insa.ocm.model.wrapper.api.AbstractAlgorithmLauncher;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.util.*;
+
+
+public class AlgorithmLauncherRealKD extends AbstractAlgorithmLauncher{
+
+ // The static list of all usable algorithms.
+ // This list is instantiated only once in the static method "instantiateListAlgorithm"
+ private static List listAvailableMiningAlgorithm = null;
+
+ // The workspace contains the dataset
+ private static String lastMiningAlgorithm;
+ private Workspace currentWorkspace;
+
+ // Algorithm Launcher keeps the track of the current mining algorithm in order to stop it later if needed.
+ private StoppableMiningAlgorithm currentMiningAlgorithm;
+ private List patternResults;
+ private PatternRealKD.PatternType typeOfPatternResult;
+
+ static{
+ // Instantiate the list of available algorithm.
+ listAvailableMiningAlgorithm = new ArrayList<>();
+ listAvailableMiningAlgorithm.add("AssociationMiningBS");
+ listAvailableMiningAlgorithm.add("AssociationSampler");
+
+ listAvailableMiningAlgorithm.forEach(s -> mapNumberCallsPerAlgorithm.put(s, 0));
+ }
+
+ /**
+ * Constructor of algorithm launcher, it needs a path to the data file and a path to the attribute file.
+ */
+ public AlgorithmLauncherRealKD(String pathRawData){
+ super();
+
+ patternResults = new ArrayList<>();
+ currentMiningAlgorithm = null;
+
+ // The creation of a Workspace is only done with the factory "Workspaces"
+ currentWorkspace = Workspaces.workspace();
+
+ this.importData(pathRawData);
+ }
+
+ //********** Implemented methods **********//
+
+ /**
+ * Getter to know the number of data mining algorithm that can be used.
+ * @return The integer representing the number of data mining algorithm that cand be used.
+ */
+ @Override
+ public int getNbAlgorithms(){
+ return listAvailableMiningAlgorithm.size();
+ }
+
+ /**
+ * Main method of the algorithm launcher : it starts the algorithm asked and at the end gives the pattern resulting.
+ * The patterns in the list are directly usable by OCM.
+ * @param algoNb The rank of the algorithm to launch.
+ * @return The list of pattern resulting from the launched data mining algorithm.
+ */
+ @Override
+ public List startAlgorithm(int algoNb){
+ //Clear all the precedent found results.
+ patternResults.clear();
+
+ if(algoNb < listAvailableMiningAlgorithm.size()){
+ //System.err.println("LOG: Getting the algorithm.");
+
+ //Get algorithm from the given number parameter.
+
+ StoppableMiningAlgorithm miningAlgorithm = getAlgorithm(listAvailableMiningAlgorithm.get(algoNb));
+
+ //If no valid algorithm has been found, it returns an empty result list.
+ if(miningAlgorithm == null){
+ System.err.println("ERR: getAlgorithm did not sent a valid algorithm.");
+ return new ArrayList<>();
+ }
+
+ //System.err.println("LOG: Algorithm begins its search.");
+
+ //Launch the data mining algorithm asked and gather the raw results.
+ currentMiningAlgorithm = miningAlgorithm;
+ Collection> results = miningAlgorithm.call();
+ currentMiningAlgorithm = null;
+
+ //System.err.println("LOG: Algorithm ends its search.\nConverting results.");
+
+ //Converts the raw result to have results usable for OCM.
+ convertResults(results);
+ //System.err.println("LOG: Results converted successfully.");
+
+ }else{
+ System.err.println(algoNb + "is not a valid number of algorithm.");
+ }
+
+ return patternResults;
+ }
+
+ /**
+ * Method to request a stop on the current launched algorithm.
+ */
+ @Override
+ public void stopAlgorithm(){
+ if(currentMiningAlgorithm != null){
+ currentMiningAlgorithm.requestStop();
+ currentMiningAlgorithm = null;
+ }
+ }
+
+ /**
+ * Imports the data and the attributes from the csv files.
+ * @param csv The path to the csv file.
+ */
+ public void importData(String csv){
+ CSVLoaderRealKD csvLoaderRealKD = new CSVLoaderRealKD(csv);
+ csvLoaderRealKD.convert();
+ DataTable dataTable = csvLoaderRealKD.createDataTable();
+
+ //Once the data table is created, it is incorporated in the current workspace.
+ currentWorkspace.overwrite(dataTable);
+
+ //Creates all the propositional logic used by RealKD in its algorithms.
+ currentWorkspace.overwrite(new PropositionalLogicFromTableBuilder().build(dataTable));
+
+ //Create the list of name of all attributes
+ createListAttributeName();
+ }
+
+ public List getListAlgorithmName(){
+ return new ArrayList<>(listAvailableMiningAlgorithm);
+ }
+
+ //********** Package methods **********//
+
+ //********** Public methods **********//
+
+ public static List getListAvailableMiningAlgorithm(){
+ return new ArrayList<>(listAvailableMiningAlgorithm);
+ }
+
+ //********** Internal methods **********//
+
+ /**
+ * Method to get an algorithm from its name. The available names are listed in listAvailableMiningAlgorithm.
+ * For each name in this list, an algorithm for the library RealKD is given.
+ * @param name The name of the algorithm to get.
+ * @return The stoppable mining algorithm corresponding to the given name.
+ */
+ private StoppableMiningAlgorithm getAlgorithm(String name){
+ lastMiningAlgorithm = name;
+ int numberCalls;
+
+ if(name == null) {
+ System.err.println("A Null argument was found instead of a valid parameter in getAlgorithm");
+ return null;
+ }
+
+ switch (name) {
+ case "AssociationMiningBS":
+ typeOfPatternResult = PatternRealKD.PatternType.ASSOCIATION;
+ numberCalls = mapNumberCallsPerAlgorithm.getOrDefault(name, 0);
+ mapNumberCallsPerAlgorithm.put(name, numberCalls + 1);
+
+ return new AssociationMiningBeamSearch(currentWorkspace);
+
+ case "AssociationSampler":
+ typeOfPatternResult = PatternRealKD.PatternType.ASSOCIATION;
+ numberCalls = mapNumberCallsPerAlgorithm.getOrDefault(name, 0);
+ mapNumberCallsPerAlgorithm.put(name, numberCalls + 1);
+
+ return new AssociationSampler(currentWorkspace);
+
+ case "ExceptionalModelSampler":
+ typeOfPatternResult = PatternRealKD.PatternType.SUBGROUP;
+ numberCalls = mapNumberCallsPerAlgorithm.getOrDefault(name, 0);
+ mapNumberCallsPerAlgorithm.put(name, numberCalls + 1);
+
+ return new ExceptionalModelSampler(currentWorkspace);
+
+ default:
+ System.err.println(name + " is not a valid algorithm name");
+ return null;
+ }
+ }
+
+ /**
+ * The results sent will be converted in an OCM format. It allows to use the results within the software OCM.
+ * @param results Results should come directly from the RealKD librarie in order to be correctly converted.
+ */
+ private void convertResults(Collection> results){
+
+ //For each pattern from the library RealKD
+ for(de.unibonn.realkd.patterns.Pattern pattern : results){
+
+ if(typeOfPatternResult != null){
+
+ //A type of pattern is required for the pattern to correctly work.
+ patternResults.add(new PatternRealKD(pattern, this, typeOfPatternResult, lastMiningAlgorithm));
+
+ }else{
+
+ System.out.println("ERR: The current type of pattern is null");
+ break;
+
+ }
+
+ }
+
+ typeOfPatternResult = null;
+
+ }
+
+ /**
+ * Called only once when the algorithm launcher is created.
+ * It creates the list of attributes's name of the dataset for a later use.
+ */
+ private void createListAttributeName(){
+
+ if(listAttributeName == null){
+ listAttributeName = new ArrayList<>();
+
+ //Checks if the current workspace has no data tables, if there is none then an error is printed.
+ if(currentWorkspace.getAllDatatables() == null || currentWorkspace.getAllDatatables().isEmpty()){
+ System.err.println("ERR: No Attributes were found. Cannot instantiate listAttributeName in AlgorithmLauncherRealKD");
+ return;
+ }
+
+ //Adds all the attributes names of the first data table of the current workspace
+ for(Attribute> attribute : currentWorkspace.getAllDatatables().get(0).attributes()){
+ listAttributeName.add(attribute.name());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/realkd/CSVLoaderRealKD.java b/src/main/java/fr/insa/ocm/model/wrapper/realkd/CSVLoaderRealKD.java
new file mode 100644
index 0000000..c8a9ab4
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/realkd/CSVLoaderRealKD.java
@@ -0,0 +1,205 @@
+package fr.insa.ocm.model.wrapper.realkd;
+
+import com.sun.istack.internal.NotNull;
+import de.unibonn.realkd.data.table.DataFormatException;
+import de.unibonn.realkd.data.table.DataTable;
+import de.unibonn.realkd.data.table.DataTableFromCSVFileBuilder;
+
+import java.io.*;
+import java.util.*;
+
+class CSVLoaderRealKD {
+
+ private enum AttributeType{
+ NAME,
+ NUMERIC,
+ CATEGORIC
+ }
+
+ private String csvPath;
+ private String datPath;
+ private String attPath;
+
+ private boolean isConverted;
+
+ CSVLoaderRealKD(@NotNull String csv){
+ csvPath = csv;
+
+ //Remove the extension of the file name.
+ String fileName = csvPath.substring(0, csvPath.lastIndexOf("."));
+
+ //Create the new name for the the new data file and new attribute file.
+ datPath = fileName + ".dat";
+ attPath = fileName + ".att";
+
+ isConverted = false;
+ }
+
+ void convert(){
+ if (csvPath == null || datPath == null || attPath == null){
+ System.err.println("ERR: One of the path name is empty");
+ return;
+ }
+
+ BufferedReader readerCSV = null;
+ BufferedWriter writerDAT = null;
+ BufferedWriter writerATT = null;
+
+ String[] attributeArr;
+ AttributeType[] typeArr;
+ try{
+ readerCSV = new BufferedReader(new FileReader(new File(csvPath)));
+ writerDAT = new BufferedWriter(new FileWriter(new File(datPath)));
+ writerATT = new BufferedWriter(new FileWriter(new File(attPath)));
+
+ //Read the first line, which has the attributes.
+ String line = readerCSV.readLine();
+ if(line == null){
+ System.err.println("ERR: Could not read the first line of the CSV file");
+ throw new IOException();
+ }
+
+ //Create the list of attributes for the attribute file
+ attributeArr = line.split(";");
+
+ //Create the list of types of attributes for the attribute file
+ //By default, the first attribute is always the name, all the other ones are numeric (unless detected categoric while reader)
+ typeArr = new AttributeType[attributeArr.length];
+ typeArr[0] = AttributeType.NAME;
+ for(int i = 1; i < typeArr.length; ++i){
+ typeArr[i] = AttributeType.NUMERIC;
+ }
+
+ //Read the other lines which has the data and create the data file
+ while((line = readerCSV.readLine()) != null){
+ //Write the data inside the data file
+ writerDAT.write(line);
+ writerDAT.newLine();
+
+ //Check the type of each attribute (without the first one which is always name)
+ String[] split = line.split(";");
+ if(split.length != typeArr.length){
+ System.err.println("ERR: The CSV is malformed, one line has one less or one more field than expected");
+ System.err.println(line);
+ throw new IOException();
+ }
+
+ for(int i = 1; i < split.length; ++i){
+ //If the attribute is set as numeric and the data is not empty and is not a number, than the corresponding attribute is categoric
+ if(typeArr[i].equals(AttributeType.NUMERIC) && !split[i].equals("") && !split[i].matches("-?\\d+(\\.\\d+)?")) {
+ typeArr[i] = AttributeType.CATEGORIC;
+ }
+ }
+ }
+
+ //Create the attribute file
+ for(int i = 0; i < attributeArr.length; ++i){
+ writerATT.write(attributeArr[i] + ";" + typeArr[i].toString().toLowerCase() + ";" + attributeArr[i] + "\'s description");
+ writerATT.newLine();
+ }
+
+ isConverted = true;
+ }catch (IOException e){
+ e.printStackTrace();
+ }finally {
+ //No matter what happens, close all the file handlers
+ if(readerCSV != null){
+ try {
+ readerCSV.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if(writerDAT != null){
+ try {
+ writerDAT.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if(writerATT != null){
+ try {
+ writerATT.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Static method to create a data table from a data csv file. Used internally to create the dataset.
+ * @return The datatable (defined by RealKD) containing the data and its attributes in a usuable form for RealKD.
+ */
+ DataTable createDataTable(){
+ if(!isConverted){
+ System.err.println("ERR: The CSV has not been converted, thus it is impossible the create the corresponding datatable");
+ return null;
+ }
+
+ DataTable resultDataTable = null;
+
+ //Builder to create a data table. It uses the data file and the attribute file to import the data and the attributes.
+ //Then it gives an ID and a name for the soon to be created data table.
+ DataTableFromCSVFileBuilder builder =
+ new DataTableFromCSVFileBuilder()
+ .setDataCSVFilename(datPath)
+ .setAttributeMetadataCSVFilename(attPath)
+ .setId("OCM")
+ .setName("OCM");
+
+ try{
+ //The builder tries to create the datable.
+ resultDataTable = builder.build();
+ }catch(DataFormatException dfException) {
+ System.err.println("DataFormatException encountered when loading the CSV file.");
+ System.err.println(dfException.getMessage());
+ System.exit(-1);
+ }
+
+ return resultDataTable;
+ }
+
+ public List> loadCSV(){
+ List> stringCSV = new ArrayList<>();
+
+ BufferedReader readerCSV;
+
+ String[] tmp;
+ int nbAttr;
+ try {
+ readerCSV = new BufferedReader(new FileReader(new File(csvPath)));
+
+ //Read the first line, which has the attributes.
+ String line = readerCSV.readLine();
+ if(line == null){
+ System.err.println("ERR: Could not read the first line of the CSV file");
+ throw new IOException();
+ }
+
+ //Create the list of attributes for the attribute file
+ tmp = line.split(";");
+ nbAttr = tmp.length;
+
+ stringCSV.add(new ArrayList<>(Arrays.asList(tmp)));
+
+ while ((line = readerCSV.readLine()) != null){
+ tmp = line.split(";");
+
+ if(tmp.length != nbAttr){
+ System.err.println("ERR: The CSV is malformed, one line has one less or one more field than expected");
+ System.err.println(line);
+ throw new IOException();
+ }
+
+ stringCSV.add(new ArrayList<>(Arrays.asList(tmp)));
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return stringCSV;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/realkd/PatternRealKD.java b/src/main/java/fr/insa/ocm/model/wrapper/realkd/PatternRealKD.java
new file mode 100644
index 0000000..dfc88c9
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/realkd/PatternRealKD.java
@@ -0,0 +1,160 @@
+package fr.insa.ocm.model.wrapper.realkd;
+
+import com.google.gson.annotations.Expose;
+import de.unibonn.realkd.patterns.QualityMeasureId;
+import fr.insa.ocm.model.wrapper.api.AbstractPattern;
+import fr.insa.ocm.model.utils.Vector;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import org.jetbrains.annotations.NotNull;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PatternRealKD extends AbstractPattern {
+
+ enum PatternType{
+ ASSOCIATION, SUBGROUP, UNKNOWN;
+
+ static PatternType getPatternType(String str){
+ switch (str.toLowerCase()){
+ case "association":
+ return PatternType.ASSOCIATION;
+ case "subgroup":
+ return PatternType.SUBGROUP;
+ default:
+ return PatternType.UNKNOWN;
+ }
+ }
+ }
+
+ //The Pattern coming from the RealKD library.
+ //private de.unibonn.realkd.patterns.Pattern pattern;
+
+ //The algorithm launcher from which come from this pattern.
+ //private AlgorithmLauncherRealKD algorithmLauncher;
+
+ //The type of this pattern.
+ @Expose private PatternType type;
+
+ //TODO Javadoc, for serialization only
+ public PatternRealKD(){}
+
+ public PatternRealKD(String type, List listAttributeNames,
+ Vector attributeVector, double[] measures,
+ String patternDescriptor, String algorithmType){
+ this.type = PatternType.getPatternType(type);
+ this.listAttributeNames = listAttributeNames;
+ this.attributeVector = attributeVector;
+
+ this.measures = measures;
+
+ this.patternDescriptor = patternDescriptor;
+ this.algorithmType = algorithmType;
+
+ wrapperType = WrapperType.REALKD;
+ }
+
+ /**
+ * Package visible constructor of a PatternRealKD.
+ * @param p The Pattern obtained from the data mining algorithm, the Pattern is the one from the RealKD library.
+ * @param algorithmLauncherRealKD The Algorithm Launcher which had launched the data mining algorithm to obtain the Pattern.
+ * @param patternType The type of Pattern that were given by the data mining algorithm, e.g. Association, SubGroup, etc.
+ */
+ PatternRealKD(@NotNull de.unibonn.realkd.patterns.Pattern p,
+ @NotNull AlgorithmLauncherRealKD algorithmLauncherRealKD,
+ @NotNull PatternType patternType,
+ @NotNull String algorithmType){
+ type = patternType;
+ this.algorithmType = algorithmType;
+
+ listAttributeNames = new ArrayList<>();
+ wrapperType = WrapperType.REALKD;
+
+ initialize(p, algorithmLauncherRealKD);
+ }
+
+ private PatternRealKD(@NotNull PatternRealKD patternRealKD){
+ super(patternRealKD);
+
+ this.type = patternRealKD.type;
+ }
+
+ //********** Implemented Methods **********//
+
+ public Pattern copy(){
+ return new PatternRealKD(this);
+ }
+
+ //********** Internal Methods **********//
+
+ private void initialize(de.unibonn.realkd.patterns.Pattern p, AlgorithmLauncherRealKD algorithmLauncherRealKD){
+ patternDescriptor = p.descriptor().toString();
+
+ // The order of these operation is important.
+ // computeAttributeVector uses the result of computeAttributeName
+ computeAttributeName(algorithmLauncherRealKD);
+ computeAttributeVector(algorithmLauncherRealKD);
+ computeInterestingValues(p, algorithmLauncherRealKD);
+ }
+
+ private void computeInterestingValues(de.unibonn.realkd.patterns.Pattern p, AlgorithmLauncherRealKD algorithmLauncherRealKD){
+ if(type == PatternType.ASSOCIATION){
+ // Frequency
+ if(p.hasMeasure(QualityMeasureId.FREQUENCY)){
+ measures[MeasureType.FREQUENCY.getIndex()] = p.value(QualityMeasureId.FREQUENCY);
+ }else{
+ measures[MeasureType.FREQUENCY.getIndex()] = 0.;
+ }
+
+ // RelativeShortness
+ double totalNumberAttribute = algorithmLauncherRealKD.getListAttributeName().size();
+ if(totalNumberAttribute != 0) {
+ measures[MeasureType.RELATIVE_SHORTNESS.getIndex()] = listAttributeNames.size() / totalNumberAttribute;
+ }else{
+ measures[MeasureType.RELATIVE_SHORTNESS.getIndex()] = 0.;
+ }
+
+// // Lift
+// if(p.hasMeasure(QualityMeasureId.LIFT)){
+// lift = p.value(QualityMeasureId.LIFT);
+// }else{
+// lift = 0.;
+// }
+ }
+ }
+
+ //********** Standard Methods **********//
+
+ @Override
+ public String toString(){
+ StringBuilder stringBuilder = new StringBuilder("");
+ NumberFormat formatter = new DecimalFormat("#0.00");
+
+ stringBuilder = stringBuilder.append(patternDescriptor).append("\n");
+
+ if(type == PatternType.ASSOCIATION){
+ stringBuilder = stringBuilder.append("[Frequency: ").append(formatter.format(measures[MeasureType.FREQUENCY.getIndex()]))
+ .append(", RelativeShortness: ").append(formatter.format(measures[MeasureType.RELATIVE_SHORTNESS.getIndex()]))
+// .append(", Lift: ").append(formatter.format(getLift()))
+ .append(", Algorithm: ").append(algorithmType).append("]\n");
+ }
+
+ return stringBuilder.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj){
+ boolean patternBool = false;
+
+ if(obj instanceof PatternRealKD){
+ PatternRealKD patternTmp = (PatternRealKD) obj;
+
+ patternBool = patternDescriptor.equals(patternTmp.patternDescriptor);
+ }
+
+ return patternBool;
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/AlgorithmLauncherSPMF.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/AlgorithmLauncherSPMF.java
new file mode 100644
index 0000000..9fe6f8c
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/AlgorithmLauncherSPMF.java
@@ -0,0 +1,263 @@
+package fr.insa.ocm.model.wrapper.spmf;
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.wrapper.api.AbstractAlgorithmLauncher;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.model.wrapper.spmf.algorithm.*;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.*;
+
+
+public class AlgorithmLauncherSPMF extends AbstractAlgorithmLauncher {
+
+ // Settings to tune the algorithms
+ private static volatile double minsupLCM = 0.6;
+ private static volatile double minsupBIDEPlus = 0.5;
+ private static volatile double minsupVMSP = 0.45;
+ private static volatile int maxgapVMSP = 5;
+ private static volatile double minsupPFPM = 0.5;
+ private static volatile int minPeriodicityPFPM = 1;
+ private static volatile int maxPeriodicityPFPM = 14;
+ private static volatile boolean[] measuresPatternDescriptor = new boolean[Pattern.MeasureType.values().length];
+
+ // The list of all available algorithms to use for OCM.
+ private static List listAvailableMiningAlgorithm;
+
+ // The map which keep the link between real attribute name and its number designation.
+ private Map mapIdLabel;
+
+ // The path to the current data file
+ private String pathConvertedData;
+
+
+ static{
+ listAvailableMiningAlgorithm = new ArrayList<>();
+ listAvailableMiningAlgorithm.add(LCMSPMF.NAME);
+ listAvailableMiningAlgorithm.add(BIDEPlusSPMF.NAME);
+ listAvailableMiningAlgorithm.add(VMSPSPMF.NAME);
+ listAvailableMiningAlgorithm.add(PFPMSPMF.NAME);
+
+ listAvailableMiningAlgorithm.forEach(s -> mapNumberCallsPerAlgorithm.put(s, 0));
+
+ for(int i = 0; i < measuresPatternDescriptor.length; ++i){
+ // False for SubGroup and Target Deviation, true else.
+// measuresPatternDescriptor[i] = !(i == Pattern.MeasureType.SUBGROUP_INTERSTINGNESS.getIndex()
+// || i == Pattern.MeasureType.TARGET_DEVIATION.getIndex());
+ measuresPatternDescriptor[i] = true;
+ }
+ }
+
+ public AlgorithmLauncherSPMF(String pathRawData){
+ super();
+
+ listAttributeName = new ArrayList<>();
+ mapIdLabel = new HashMap<>();
+
+ // Directly load the data once the AlgorithmLauncher is created.
+ importData(pathRawData);
+ }
+
+ @Override
+ public int getNbAlgorithms() {
+ return listAvailableMiningAlgorithm.size();
+ }
+
+ @Override
+ public List startAlgorithm(int algorithmNumber) {
+ List listResult = new ArrayList<>();
+
+ if(algorithmNumber < 0 || algorithmNumber >= listAvailableMiningAlgorithm.size()){
+ throw new UnsupportedOperationException("ERR: The algorithm id ask is not a valid one : "+ algorithmNumber + ".");
+ }
+
+ MiningAlgorithmSPMF algorithm = getAlgorithm(listAvailableMiningAlgorithm.get(algorithmNumber));
+
+ if(algorithm == null) {
+ DebugLogger.printDebug("AlgorithmLauncherSPMF: Unable to retrieve a valid algorithm instance.", DebugLogger.MessageSeverity.HIGH);
+ return new ArrayList<>();
+ }
+
+ algorithm.loadData(pathConvertedData);
+
+ // Calling the algorithm
+// System.err.println(Thread.currentThread().getName() + ": is launching " + lastMiningAlgorithm);
+ listResult.addAll(algorithm.call());
+
+ return listResult;
+ }
+
+ @Override
+ public void stopAlgorithm() {
+
+ }
+
+ @Override
+ public List getListAlgorithmName() {
+ return new ArrayList<>(listAvailableMiningAlgorithm);
+ }
+
+ //********** Public methods **********//
+
+ public String getLabel(Integer id){
+ return mapIdLabel.getOrDefault(id, "?");
+ }
+
+ //********** Internal methods **********//
+
+ private MiningAlgorithmSPMF getAlgorithm(String name){
+ MiningAlgorithmSPMF miningAlgorithm = null;
+
+ if(listAvailableMiningAlgorithm.contains(name)){
+ int numberCalls = mapNumberCallsPerAlgorithm.getOrDefault(name, 0);
+ mapNumberCallsPerAlgorithm.put(name, numberCalls+1);
+ }
+
+ switch (name){
+ case LCMSPMF.NAME:
+
+ miningAlgorithm = new LCMSPMF(minsupLCM, Duration.ZERO.plusDays(7), this);
+ break;
+
+ case BIDEPlusSPMF.NAME:
+
+ miningAlgorithm = new BIDEPlusSPMF(minsupBIDEPlus, Duration.ZERO.plusDays(1), Duration.ZERO.plusDays(7), this);
+ break;
+
+ case VMSPSPMF.NAME:
+
+ miningAlgorithm = new VMSPSPMF(minsupVMSP, maxgapVMSP, Duration.ZERO.plusDays(1), Duration.ZERO.plusDays(7), this);
+ break;
+
+ case PFPMSPMF.NAME:
+
+ miningAlgorithm = new PFPMSPMF(Duration.ZERO.plusDays(7), minsupPFPM, minPeriodicityPFPM, maxPeriodicityPFPM, minPeriodicityPFPM, maxPeriodicityPFPM, this);
+ break;
+
+ }
+
+ return miningAlgorithm;
+ }
+
+ /**
+ * Loads the data provided to the AlgorithmLauncher when it is created.
+ * @param csv The path to the csv file where is the data.
+ */
+ private void importData(String csv) {
+ File fileData = new File(csv);
+
+ if(fileData.exists() && fileData.isFile()){
+ String pathRawData = fileData.getAbsolutePath();
+
+ CSVLoaderSPMF csvLoader = new CSVLoaderSPMF(pathRawData);
+
+ listAttributeName = csvLoader.getListItem();
+ mapIdLabel = csvLoader.getMapIdLabel();
+ pathConvertedData = csvLoader.getPathConvertedData();
+ }else{
+ System.err.println("ERR: The given path is incorrect, no file found there : " + csv);
+ }
+ }
+
+ private void exportMapIdLabel(){
+ try(BufferedWriter writer = new BufferedWriter(new FileWriter("mapIdLabel.txt"))){
+ mapIdLabel.forEach((integer, str) -> {
+ try {
+ writer.write(integer + ";" + str);
+ writer.newLine();
+ } catch (IOException e){
+ e.printStackTrace();
+ }
+ });
+
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ //********** Getters/Setters method **********//
+
+ // Getters //
+
+ public static double getMinsupLCM(){ return AlgorithmLauncherSPMF.minsupLCM; }
+
+ public static double getMinsupBIDEPlus(){ return AlgorithmLauncherSPMF.minsupBIDEPlus; }
+
+ public static double getMinsupVMSP(){ return AlgorithmLauncherSPMF.minsupVMSP; }
+
+ public static double getMaxgapVMSP(){ return AlgorithmLauncherSPMF.maxgapVMSP; }
+
+ public static double getMinsupPFPM(){ return AlgorithmLauncherSPMF.minsupPFPM; }
+
+ public static int getMinPeriodicityPFPM(){ return AlgorithmLauncherSPMF.minPeriodicityPFPM; }
+
+ public static int getMaxPeriodicityPFPM(){ return AlgorithmLauncherSPMF.maxPeriodicityPFPM; }
+
+ public static boolean getMeasurePatternDescriptor(int index){
+ return index >= 0 && index < measuresPatternDescriptor.length && measuresPatternDescriptor[index];
+ }
+
+ public static List getListAvailableMiningAlgorithm(){
+ return new ArrayList<>(listAvailableMiningAlgorithm);
+ }
+
+ // Setters //
+
+ public static void setMinsupLCM(double minsupLCM) {
+ AlgorithmLauncherSPMF.minsupLCM = minsupLCM;
+ }
+
+ public static void setMinsupBIDEPlus(double minsupBIDEPlus) {
+ AlgorithmLauncherSPMF.minsupBIDEPlus = minsupBIDEPlus;
+ }
+
+ public static void setMinsupVMSP(double minsupVMSP) {
+ AlgorithmLauncherSPMF.minsupVMSP = minsupVMSP;
+ }
+
+ public static void setMaxgapVMSP(int maxgapVMSP) {
+ AlgorithmLauncherSPMF.maxgapVMSP = maxgapVMSP;
+ }
+
+ public static void setMinsupPFPM(double minsupPFPM){
+ AlgorithmLauncherSPMF.minsupPFPM = minsupPFPM;
+ }
+
+ public static void setMinPeriodicityPFPM(int minPeriodicityPFPM) {
+ AlgorithmLauncherSPMF.minPeriodicityPFPM = minPeriodicityPFPM;
+ }
+
+ public static void setMaxPeriodicityPFPM(int maxPeriodicityPFPM) {
+ AlgorithmLauncherSPMF.maxPeriodicityPFPM = maxPeriodicityPFPM;
+ }
+
+ public static void setMeasuresPatternDescriptor(boolean b, int index){
+ if(index >= 0 && index < measuresPatternDescriptor.length){
+ measuresPatternDescriptor[index] = b;
+ }
+ }
+
+ //********** Main method **********//
+
+ /**
+ * For quick testing purpose only
+ * @param args Main parameters
+ */
+ public static void main(String[] args) {
+ AlgorithmLauncherSPMF algorithmLauncherSPMF = new AlgorithmLauncherSPMF("ressource/sacha_chua_more_preprocessed.csv");
+
+ List listResult = algorithmLauncherSPMF.startAlgorithm(3);
+ algorithmLauncherSPMF.exportMapIdLabel();
+
+ listResult.forEach(System.err::println);
+ System.out.println("listResult.size() = " + listResult.size());
+
+
+
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/CSVLoaderSPMF.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/CSVLoaderSPMF.java
new file mode 100644
index 0000000..4499fb0
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/CSVLoaderSPMF.java
@@ -0,0 +1,115 @@
+package fr.insa.ocm.model.wrapper.spmf;
+
+
+import com.sun.istack.internal.NotNull;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class CSVLoaderSPMF {
+
+ // Extension of the files used by the algorithm.
+ private static final String DAT_EXT = ".spmfdat";
+ private static final String MAP_EXT = ".spmfmap";
+
+ // The path of last files used.
+ private String pathRawData;
+
+ // The informations about the current loaded data.
+ private Map mapLabelId;
+ private Map mapIdLabel;
+ private String pathConvertedData;
+
+
+ CSVLoaderSPMF(@NotNull String pathCSV){
+ this.pathRawData = pathCSV;
+ this.pathConvertedData = "";
+
+ this.mapLabelId = new HashMap<>();
+ this.mapIdLabel = new HashMap<>();
+
+ this.convert();
+ }
+
+ private void convert(){
+ //Remove the extension of the file name.
+ String fileName = pathRawData.substring(0, pathRawData.lastIndexOf("."));
+
+ File convertedFile = new File(fileName + DAT_EXT);
+ File mapFile = new File(fileName + MAP_EXT);
+
+ pathConvertedData = convertedFile.getAbsolutePath();
+
+ BufferedWriter writerSPMFDAT = null;
+ //BufferedWriter writerSPMFMAP = null;
+ BufferedReader readerCSV = null;
+
+ try{
+ readerCSV = new BufferedReader(new FileReader(pathRawData));
+ writerSPMFDAT = new BufferedWriter(new FileWriter(fileName + DAT_EXT));
+ //writerSPMFMAP = new BufferedWriter(new FileWriter(fileName + MAP_EXT));
+
+ String line;
+ int id = 1;
+
+ while ((line = readerCSV.readLine()) != null){
+ String[] lineSplit = line.split(";");
+ String data;
+
+ if(!mapLabelId.containsKey(lineSplit[1])){
+ mapLabelId.put(lineSplit[1], id);
+ mapIdLabel.put(id, lineSplit[1]);
+
+ data = Integer.toString(id);
+ id++;
+ }else{
+ data = mapLabelId.get(lineSplit[1]).toString();
+ }
+
+ writerSPMFDAT.write(lineSplit[0] + ";" + data);
+ writerSPMFDAT.newLine();
+ }
+ } catch (IOException e){
+ e.printStackTrace();
+ } finally {
+ if(readerCSV != null){
+ try {
+ readerCSV.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if(writerSPMFDAT != null){
+ try {
+ writerSPMFDAT.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ /*
+ if(writerSPMFMAP != null){
+ try {
+ writerSPMFMAP.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ */
+ }
+ }
+
+ Map getMapIdLabel(){
+ return mapIdLabel;
+ }
+
+ List getListItem(){
+ return new ArrayList<>(mapLabelId.keySet());
+ }
+
+ String getPathConvertedData(){
+ return pathConvertedData;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/PatternSPMF.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/PatternSPMF.java
new file mode 100644
index 0000000..0bd1398
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/PatternSPMF.java
@@ -0,0 +1,106 @@
+package fr.insa.ocm.model.wrapper.spmf;
+
+import fr.insa.ocm.model.utils.Vector;
+import fr.insa.ocm.model.wrapper.api.AbstractPattern;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.List;
+
+public class PatternSPMF extends AbstractPattern {
+
+ //TODO Javadoc for serializer only
+ public PatternSPMF(List listAttributeNames,
+ Vector attributeVector, double[] measures,
+ String patternDescriptor, String algorithmType){
+
+ this.algorithmType = algorithmType;
+ this.patternDescriptor = patternDescriptor;
+
+ this.attributeVector = attributeVector;
+ this.listAttributeNames = listAttributeNames;
+
+ System.arraycopy(measures, 0, this.measures, 0, this.measures.length);
+
+ wrapperType = WrapperType.SPMF;
+ }
+
+ public PatternSPMF(String patternDescriptor,
+ List listAttributeNames,
+ AlgorithmLauncherSPMF algorithmLauncher,
+ double[] interestingMeasures,
+ String algorithmType){
+ this.patternDescriptor = patternDescriptor;
+ this.listAttributeNames = listAttributeNames;
+ this.algorithmType = algorithmType;
+
+ wrapperType = WrapperType.SPMF;
+
+ initialize(algorithmLauncher, interestingMeasures);
+ }
+
+ private PatternSPMF(PatternSPMF patternSPMF){
+ super(patternSPMF);
+ }
+
+ //********** Implemented Methods **********//
+
+ public Pattern copy(){
+ return new PatternSPMF(this);
+ }
+
+ //********** Internal Methods **********//
+
+ private void initialize(AlgorithmLauncherSPMF algorithmLauncher,
+ double[] interestingMeasures){
+ // The order of these operation is important.
+ // computeAttributeVector uses the result of computeAttributeName
+
+ setInterestingValues(interestingMeasures);
+ computeAttributeVector(algorithmLauncher);
+ computeAttributeName(algorithmLauncher);
+ }
+
+ private void setInterestingValues(double[] interestingMeasures){
+ System.arraycopy(interestingMeasures, 0, measures, 0, measures.length);
+ }
+
+ //********** Standard Methods **********//
+
+ @Override
+ public String toString(){
+ StringBuilder stringBuilder = new StringBuilder("");
+ NumberFormat formatter = new DecimalFormat("#0.00");
+
+ boolean needComma = false;
+
+ stringBuilder.append(patternDescriptor);
+ stringBuilder.append("\n[");
+
+ for(MeasureType measureType : MeasureType.values()){
+ if(AlgorithmLauncherSPMF.getMeasurePatternDescriptor(measureType.getIndex())) {
+ if (needComma) {
+ stringBuilder.append(", ");
+ }
+
+ stringBuilder.append(measureType.toString());
+ stringBuilder.append(": ");
+ stringBuilder.append(formatter.format(measures[measureType.getIndex()]));
+
+ needComma = true;
+ }
+ }
+
+ if(needComma){
+ stringBuilder.append(", ");
+ }
+
+ stringBuilder.append("Algorithm: ");
+ stringBuilder.append(algorithmType);
+ stringBuilder.append("]");
+
+ return stringBuilder.toString();
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/BIDEPlusSPMF.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/BIDEPlusSPMF.java
new file mode 100644
index 0000000..10ea784
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/BIDEPlusSPMF.java
@@ -0,0 +1,128 @@
+package fr.insa.ocm.model.wrapper.spmf.algorithm;
+
+import ca.pfv.spmf.algorithms.sequentialpatterns.prefixspan.AlgoBIDEPlus;
+import ca.pfv.spmf.algorithms.sequentialpatterns.prefixspan.SequentialPattern;
+import ca.pfv.spmf.algorithms.sequentialpatterns.prefixspan.SequentialPatterns;
+import ca.pfv.spmf.patterns.itemset_list_integers_without_support.Itemset;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.model.wrapper.spmf.AlgorithmLauncherSPMF;
+import fr.insa.ocm.model.wrapper.spmf.PatternSPMF;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class BIDEPlusSPMF extends SequentialMiningAlgorithmSPMF {
+
+ // Name of this algorithm
+ public static final String NAME = "BIDEPlus";
+
+ // Extension of the files used by the algorithm
+ private static final String BIDEP_DAT_EXT = ".bidepdat";
+ private static volatile boolean isAlreadyConverted = false;
+
+ // SPMF stuff
+ private AlgoBIDEPlus algorithmBIDEPlus;
+ private double minsup; // Required to launch BIDEPlus
+
+ // From which AlgorithmLauncher comes this algorithm, so Patterns could be easily made.
+ private AlgorithmLauncherSPMF algorithmLauncher;
+ private static int numberSequenceBIDEPlus = 0;
+
+
+ public BIDEPlusSPMF(double minsup, Duration intervalItemset, Duration intervalSequence, AlgorithmLauncherSPMF algorithmLauncher){
+ super(BIDEP_DAT_EXT, intervalItemset, intervalSequence);
+
+ this.minsup = minsup;
+ this.algorithmLauncher = algorithmLauncher;
+
+ algorithmBIDEPlus = new AlgoBIDEPlus();
+ }
+
+ @Override
+ public void loadData(String csvPath) {
+ if(!isAlreadyConverted) {
+ isAlreadyConverted = true;
+ super.loadData(csvPath);
+ numberSequenceBIDEPlus = super.numberSequence;
+ } else {
+ // We need to get the already converted file.
+ String fileName = csvPath.substring(0, csvPath.lastIndexOf("."));
+ File convertedFile = new File(fileName + BIDEPlusSPMF.BIDEP_DAT_EXT);
+
+ pathDataConverted = convertedFile.getAbsolutePath();
+ }
+ }
+
+ @Override
+ public List call() {
+ List resultPattern = new ArrayList<>();
+ try {
+ SequentialPatterns sequentialPatterns = algorithmBIDEPlus.runAlgorithm(pathDataConverted, minsup, null);
+
+ //sequentialPatterns.printFrequentPatterns(algorithmBIDEPlus.patternCount, false);
+ //algorithmBIDEPlus.printStatistics();
+
+ resultPattern.addAll(computeResult(sequentialPatterns));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return resultPattern;
+ }
+
+ @NotNull
+ private List computeResult(@NotNull SequentialPatterns sequentialPatterns){
+ List listPatternResult = new ArrayList<>();
+
+ sequentialPatterns.getLevels().forEach(sequentialPatternsSet -> sequentialPatternsSet.forEach(sequentialPattern -> {
+ listPatternResult.add(computePattern(sequentialPattern));
+ }));
+
+ return listPatternResult;
+ }
+
+ @NotNull
+ private PatternSPMF computePattern(@NotNull SequentialPattern sequentialPattern){
+ double[] interestingMeasures = new double[Pattern.MeasureType.values().length];
+ StringBuilder patternDescriptorBuilder = new StringBuilder("[");
+ Set setAttributeName = new HashSet<>();
+
+
+ for (Itemset itemset : sequentialPattern.getItemsets()) {
+
+ if(sequentialPattern.get(0).equals(itemset)) {
+ patternDescriptorBuilder = patternDescriptorBuilder.append("(");
+ } else {
+ patternDescriptorBuilder = patternDescriptorBuilder.append("\n\t(");
+ }
+
+ for(Integer id : itemset.getItems()){
+ String label = algorithmLauncher.getLabel(id);
+ setAttributeName.add(label);
+
+ if(itemset.get(0).equals(id)){
+ patternDescriptorBuilder = patternDescriptorBuilder.append(label);
+ } else {
+ patternDescriptorBuilder = patternDescriptorBuilder.append(", ").append(label);
+ }
+ }
+
+ patternDescriptorBuilder = patternDescriptorBuilder.append(")");
+ }
+ patternDescriptorBuilder = patternDescriptorBuilder.append("]");
+
+ interestingMeasures[Pattern.MeasureType.FREQUENCY.getIndex()] = ((double) sequentialPattern.getAbsoluteSupport())/numberSequenceBIDEPlus;
+ interestingMeasures[Pattern.MeasureType.RELATIVE_SHORTNESS.getIndex()] = ((double) setAttributeName.size())/algorithmLauncher.getNbAttributes();
+
+ return new PatternSPMF(patternDescriptorBuilder.toString(),
+ new ArrayList<>(setAttributeName),
+ algorithmLauncher,
+ interestingMeasures,
+ BIDEPlusSPMF.NAME);
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/ItemsetMiningAlgorithm.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/ItemsetMiningAlgorithm.java
new file mode 100644
index 0000000..5f3fb44
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/ItemsetMiningAlgorithm.java
@@ -0,0 +1,118 @@
+package fr.insa.ocm.model.wrapper.spmf.algorithm;
+
+
+import java.io.*;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class ItemsetMiningAlgorithm implements MiningAlgorithmSPMF {
+
+ // Interval used to split a long sequence
+ private Duration intervalItemset;
+
+ private String fileExtension;
+
+ String pathDataConverted;
+ int numberItemset; // Be aware that this value is valid only if SequentialMiningAlgorithmSPMF.loadData is called
+
+ ItemsetMiningAlgorithm(String fileExtension, Duration intervalItemset){
+
+ this.fileExtension = fileExtension;
+ this.intervalItemset = intervalItemset;
+
+ numberItemset = 0;
+ }
+
+ @Override
+ public void loadData(String csvPath) {
+ //Remove the extension of the file name.
+ String fileName = csvPath.substring(0, csvPath.lastIndexOf("."));
+
+ File convertedFile = new File(fileName + fileExtension);
+
+ pathDataConverted = convertedFile.getAbsolutePath();
+
+ BufferedWriter writerSPMFDAT = null;
+ BufferedReader readerCSV = null;
+
+ try {
+ writerSPMFDAT = new BufferedWriter(new FileWriter(convertedFile));
+ readerCSV = new BufferedReader(new FileReader(csvPath));
+
+ String line;
+ LocalDateTime thresholdDateTime = LocalDateTime.now();
+ List listAlreadyInItemSet = new ArrayList<>();
+
+ line = readerCSV.readLine();
+ if (line != null) {
+ String[] lineSplit = line.split(";");
+
+ LocalDateTime dateLine = LocalDateTime.parse(lineSplit[0]);
+ thresholdDateTime = dateLine.plus(intervalItemset);
+
+ listAlreadyInItemSet.add(Integer.valueOf(lineSplit[1]));
+ }
+
+ while ((line = readerCSV.readLine()) != null) {
+ String[] lineSplit = line.split(";");
+
+ LocalDateTime dateLine = LocalDateTime.parse(lineSplit[0]);
+
+ // Each itemset should end with a newline. Here we are taking an itemset with all the items of the same day.
+ // It assumes that the file has sorted records from the oldest to the newest.
+ if (dateLine.isAfter(thresholdDateTime)) {
+ // Set the new threshold as the next day if a day as already passed.
+ thresholdDateTime = dateLine.plus(intervalItemset);
+
+ // Write the itemset all at once.
+ Collections.sort(listAlreadyInItemSet);
+ for (Integer idInt : listAlreadyInItemSet) {
+ writerSPMFDAT.write(idInt + " ");
+ }
+
+ writerSPMFDAT.newLine();
+
+ listAlreadyInItemSet.clear();
+ numberItemset++;
+ }
+
+ Integer item = Integer.valueOf(lineSplit[1]);
+
+ if (!listAlreadyInItemSet.contains(item)) {
+ // An itemset should contain only one occurence of an item.
+ listAlreadyInItemSet.add(item);
+ }
+ }
+
+ // Write the last itemset.
+ Collections.sort(listAlreadyInItemSet);
+ for (Integer idInt : listAlreadyInItemSet) {
+ writerSPMFDAT.write(idInt + " ");
+ }
+ writerSPMFDAT.newLine();
+ numberItemset++;
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (writerSPMFDAT != null) {
+ try {
+ writerSPMFDAT.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (readerCSV != null) {
+ try {
+ readerCSV.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/LCMSPMF.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/LCMSPMF.java
new file mode 100644
index 0000000..b17f15a
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/LCMSPMF.java
@@ -0,0 +1,126 @@
+package fr.insa.ocm.model.wrapper.spmf.algorithm;
+
+import ca.pfv.spmf.algorithms.frequentpatterns.lcm.AlgoLCM;
+import ca.pfv.spmf.algorithms.frequentpatterns.lcm.Dataset;
+import ca.pfv.spmf.patterns.itemset_array_integers_with_count.Itemset;
+import ca.pfv.spmf.patterns.itemset_array_integers_with_count.Itemsets;
+import org.jetbrains.annotations.NotNull;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.model.wrapper.spmf.AlgorithmLauncherSPMF;
+import fr.insa.ocm.model.wrapper.spmf.PatternSPMF;
+
+import java.io.*;
+import java.time.Duration;
+import java.util.*;
+
+public class LCMSPMF extends ItemsetMiningAlgorithm {
+
+ // Name of this algorithm
+ public static final String NAME = "LCM";
+
+ // Extension of the files used by the algorithm
+ private static final String LCM_DAT_EXT = ".lcmdat";
+ private static volatile boolean isAlreadyConverted = false;
+
+ // SPMF stuff
+ private AlgoLCM algorithmLCM;
+ private double minsup; // Required to launch AlgoLCM
+
+ // From which AlgorithmLauncher comes this algorithm, so Patterns could be easily made.
+ private AlgorithmLauncherSPMF algorithmLauncher;
+ private static int numberItemSetLCM = 0;
+
+ public LCMSPMF(double minsup, Duration discretizeCriteron, AlgorithmLauncherSPMF algorithmLauncher){
+ super(LCM_DAT_EXT, discretizeCriteron);
+ this.minsup = minsup;
+ this.algorithmLauncher = algorithmLauncher;
+
+ algorithmLCM = new AlgoLCM();
+ }
+
+ @Override
+ public void loadData(String csvPath) {
+ if(!isAlreadyConverted) {
+ isAlreadyConverted = true;
+ super.loadData(csvPath);
+
+ numberItemSetLCM = this.numberItemset;
+ } else {
+ // We need to get the already converted file.
+ String fileName = csvPath.substring(0, csvPath.lastIndexOf("."));
+ File convertedFile = new File(fileName + LCMSPMF.LCM_DAT_EXT);
+
+ pathDataConverted = convertedFile.getAbsolutePath();
+ }
+ }
+
+ @NotNull
+ @Override
+ public List call() {
+ List resultPattern = new ArrayList<>();
+ try {
+ Dataset dataset = new Dataset(pathDataConverted);
+ Itemsets itemsetsResult = algorithmLCM.runAlgorithm(minsup, dataset, null);
+
+ resultPattern.addAll(computeResult(itemsetsResult));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return resultPattern;
+ }
+
+ /**
+ * Create all the Patterns to describe the results given by the algorithm. The resulting pattern are ready to use for OCM.
+ * @param itemsets The itemsets which needs to be converted to Patterns.
+ * @return Create a list of {@link PatternSPMF} to describe the result of the LCM algorithm.
+ */
+ @NotNull
+ private List computeResult(@NotNull Itemsets itemsets){
+ List resultPattern = new ArrayList<>();
+
+ itemsets.getLevels().forEach(levelItemSet -> levelItemSet.forEach(itemset -> {
+ resultPattern.add(computePattern(itemset));
+ }));
+
+ return resultPattern;
+ }
+
+ /**
+ * Create a new pattern corresponding to the current itemset, for a OCM use.
+ * @param itemset The itemset needed to be converted into a Pattern.
+ * @return A new {@link PatternSPMF} which describe the sent itemset.
+ */
+ @NotNull
+ private PatternSPMF computePattern(@NotNull Itemset itemset){
+ int[] items = itemset.getItems();
+ double[] interestingMeasures = new double[Pattern.MeasureType.values().length];
+ StringBuilder patternDescriptorBuilder = new StringBuilder("[");
+ List listAttributeName = new ArrayList<>();
+
+ for (int i = 0; i < interestingMeasures.length; i++) {
+ interestingMeasures[i] = 0d;
+ }
+
+ for(int id : items){
+ String label = algorithmLauncher.getLabel(id);
+ listAttributeName.add(label);
+
+ if(id != items[0]){
+ patternDescriptorBuilder = patternDescriptorBuilder.append(", ").append(label);
+ } else {
+ patternDescriptorBuilder = patternDescriptorBuilder.append(label);
+ }
+ }
+ patternDescriptorBuilder = patternDescriptorBuilder.append("]");
+
+ interestingMeasures[Pattern.MeasureType.FREQUENCY.getIndex()] = ((double) itemset.getAbsoluteSupport())/ numberItemSetLCM;
+ interestingMeasures[Pattern.MeasureType.RELATIVE_SHORTNESS.getIndex()] = ((double) items.length)/algorithmLauncher.getNbAttributes();
+
+ return new PatternSPMF(patternDescriptorBuilder.toString(),
+ listAttributeName,
+ algorithmLauncher,
+ interestingMeasures,
+ LCMSPMF.NAME);
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/MiningAlgorithmSPMF.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/MiningAlgorithmSPMF.java
new file mode 100644
index 0000000..27317ce
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/MiningAlgorithmSPMF.java
@@ -0,0 +1,12 @@
+package fr.insa.ocm.model.wrapper.spmf.algorithm;
+
+import fr.insa.ocm.model.wrapper.spmf.PatternSPMF;
+
+import java.util.List;
+
+public interface MiningAlgorithmSPMF {
+
+ void loadData(String csvPath);
+
+ List call();
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/PFPMSPMF.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/PFPMSPMF.java
new file mode 100644
index 0000000..0b07f81
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/PFPMSPMF.java
@@ -0,0 +1,165 @@
+package fr.insa.ocm.model.wrapper.spmf.algorithm;
+
+import ca.pfv.spmf.algorithms.frequentpatterns.pfpm.AlgoPFPM;
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.model.wrapper.spmf.AlgorithmLauncherSPMF;
+import fr.insa.ocm.model.wrapper.spmf.PatternSPMF;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class PFPMSPMF extends ItemsetMiningAlgorithm {
+
+ // Name of this algorithm
+ public static final String NAME = "PFPM";
+
+ // Extension of the files used by the algorithm
+ private static final String PFPM_DAT_EXT = ".pfpmdat";
+ private static volatile boolean isAlreadyConverted = false;
+
+ // SPMF stuff
+ private AlgoPFPM algorithmPFPM;
+ private double minsup;
+ private int minPeriodicity;
+ private int maxPeriodicity;
+ private int minAveragePeriodicity;
+ private int maxAveragePeriodicity;
+
+ // From which AlgorithmLauncher comes this algorithm, so Patterns could be easily made.
+ private AlgorithmLauncherSPMF algorithmLauncher;
+ private static int numberItemSetPFPM = 0;
+
+ public PFPMSPMF(Duration intervalItemset,
+ double minsup,
+ int minPeriodicity, int maxPeriodicity,
+ int minAveragePeriodicity, int maxAveragePeriodicity,
+ AlgorithmLauncherSPMF algorithmLauncher) {
+ super(PFPMSPMF.PFPM_DAT_EXT, intervalItemset);
+
+ this.minsup = minsup;
+ this.minPeriodicity = minPeriodicity;
+ this.maxPeriodicity = maxPeriodicity;
+ this.minAveragePeriodicity = minAveragePeriodicity;
+ this.maxAveragePeriodicity = maxAveragePeriodicity;
+
+ this.algorithmLauncher = algorithmLauncher;
+
+ algorithmPFPM = new AlgoPFPM(true);
+ algorithmPFPM.setEnableESCP(true);
+ }
+
+
+ @Override
+ public void loadData(String csvPath) {
+ if(!isAlreadyConverted) {
+ isAlreadyConverted = true;
+ super.loadData(csvPath);
+
+ numberItemSetPFPM = this.numberItemset;
+ } else {
+ // We need to get the already converted file.
+ String fileName = csvPath.substring(0, csvPath.lastIndexOf("."));
+ File convertedFile = new File(fileName + PFPMSPMF.PFPM_DAT_EXT);
+
+ pathDataConverted = convertedFile.getAbsolutePath();
+ }
+ }
+
+ @NotNull
+ @Override
+ public List call() {
+ List resultPattern = new ArrayList<>();
+
+ try {
+ algorithmPFPM.runAlgorithm(pathDataConverted, DebugLogger.directoryLog +"junk.txt",
+ minPeriodicity, maxPeriodicity, minAveragePeriodicity,
+ maxAveragePeriodicity);
+
+ //System.out.println(algorithmPFPM.getResultAsString());
+ resultPattern.addAll(computeResult(algorithmPFPM.getResultAsString()));
+ //System.out.println("numberItemSetPFPM = " + numberItemSetPFPM);
+ } catch (IOException e){
+ e.printStackTrace();
+ }
+
+ return resultPattern;
+ }
+
+ @NotNull
+ private List computeResult(@NotNull String results){
+ List resultPattern = new ArrayList<>();
+
+ List listResult = Arrays.asList(results.split("\n"));
+
+ listResult.forEach(strPattern -> {
+ if(strPattern != null){
+ PatternSPMF pattern = computePattern(strPattern);
+ if(pattern != null) {
+ resultPattern.add(pattern);
+ }
+ }
+ });
+
+ return resultPattern;
+ }
+
+ @Nullable
+ private PatternSPMF computePattern(@NotNull String strPattern){
+ String[] patternParts = strPattern.split(";");
+ double[] interestingMeasures = new double[Pattern.MeasureType.values().length];
+ StringBuilder patternDescriptorBuilder = new StringBuilder("[");
+ List listAttributeName = new ArrayList<>();
+
+ // The pattern sent by the PFPM algorithm contains 5 items
+ if(patternParts.length != 5){
+ System.err.println("ERR: Impossible to retrieve information from this pattern: " + strPattern);
+ return null;
+ }
+
+ for (int i = 0; i < interestingMeasures.length; i++) {
+ interestingMeasures[i] = 0d;
+ }
+
+ int indexAttribute = 0;
+ for(String strId : patternParts[0].split(" ")){
+ Integer id = Integer.valueOf(strId);
+ String label = algorithmLauncher.getLabel(id);
+ listAttributeName.add(label);
+
+ if(indexAttribute != 0){
+ patternDescriptorBuilder = patternDescriptorBuilder.append(", ").append(label);
+ } else {
+ patternDescriptorBuilder = patternDescriptorBuilder.append(label);
+ }
+
+ indexAttribute++;
+ }
+ patternDescriptorBuilder = patternDescriptorBuilder.append("]");
+
+ Integer absoluteSupport = Integer.valueOf(patternParts[1]);
+ Integer minPeriod = Integer.valueOf(patternParts[2]);
+ Integer maxPeriod = Integer.valueOf(patternParts[3]);
+
+ interestingMeasures[Pattern.MeasureType.FREQUENCY.getIndex()] = ((double) absoluteSupport)/ numberItemSetPFPM;
+ interestingMeasures[Pattern.MeasureType.RELATIVE_SHORTNESS.getIndex()] = ((double) indexAttribute)/algorithmLauncher.getNbAttributes();
+ interestingMeasures[Pattern.MeasureType.RELATIVE_PERIODICITY.getIndex()] = minPeriod.doubleValue()/ maxPeriod.doubleValue();
+
+ if(interestingMeasures[Pattern.MeasureType.FREQUENCY.getIndex()] < minsup){
+ // The created pattern should have a frequency at least higher then the minsup.
+ return null;
+ }
+
+ return new PatternSPMF(patternDescriptorBuilder.toString(),
+ listAttributeName,
+ algorithmLauncher,
+ interestingMeasures,
+ PFPMSPMF.NAME);
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/SequentialMiningAlgorithmSPMF.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/SequentialMiningAlgorithmSPMF.java
new file mode 100644
index 0000000..4fd9d41
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/SequentialMiningAlgorithmSPMF.java
@@ -0,0 +1,144 @@
+package fr.insa.ocm.model.wrapper.spmf.algorithm;
+
+import fr.insa.ocm.model.wrapper.spmf.PatternSPMF;
+
+import java.io.*;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+abstract class SequentialMiningAlgorithmSPMF implements MiningAlgorithmSPMF {
+
+
+ // Interval used to split a long sequence
+ private Duration intervalItemset;
+ private Duration intervalSequence;
+
+ private String fileExtension;
+
+ String pathDataConverted;
+ int numberSequence; // Be aware that this value is valid only if SequentialMiningAlgorithmSPMF.loadData is called
+
+ SequentialMiningAlgorithmSPMF(String fileExtension, Duration intervalItemset, Duration intervalSequence){
+ this.fileExtension = fileExtension;
+
+ this.intervalItemset = intervalItemset;
+ this.intervalSequence = intervalSequence;
+
+ numberSequence = 0;
+ }
+
+ public void loadData(String csvPath){
+ //Remove the extension of the file name.
+ String fileName = csvPath.substring(0, csvPath.lastIndexOf("."));
+
+ File convertedFile = new File(fileName + fileExtension);
+
+ pathDataConverted = convertedFile.getAbsolutePath();
+
+ BufferedWriter writerSPMFDAT = null;
+ BufferedReader readerCSV = null;
+
+ try{
+ readerCSV = new BufferedReader(new FileReader(new File(csvPath)));
+ writerSPMFDAT = new BufferedWriter(new FileWriter(convertedFile));
+
+ String line;
+ LocalDateTime thresholdItemset = LocalDateTime.now();
+ LocalDateTime thresholdSequence = LocalDateTime.now();
+ List> listAlreadyInItemSet = new ArrayList<>();
+ int indexList = 0;
+
+ line = readerCSV.readLine();
+ if(line != null){
+ String[] lineSplit = line.split(";");
+
+ LocalDateTime dateLine = LocalDateTime.parse(lineSplit[0]);
+
+ thresholdItemset = dateLine.plus(intervalItemset);
+ thresholdSequence = dateLine.plus(intervalSequence);
+
+ listAlreadyInItemSet.add(new ArrayList<>());
+ listAlreadyInItemSet.get(indexList).add(Integer.valueOf(lineSplit[1]));
+ }
+
+ while ((line = readerCSV.readLine()) != null){
+ String[] lineSplit = line.split(";");
+
+ LocalDateTime dateLine = LocalDateTime.parse(lineSplit[0]);
+
+ if(dateLine.isAfter(thresholdSequence)){
+ // Write the sequence in the resulting file
+ this.writeSequence(listAlreadyInItemSet, writerSPMFDAT);
+
+ // Initializing anew the list containing the sequence.
+ indexList = 0;
+ listAlreadyInItemSet.clear();
+ listAlreadyInItemSet.add(new ArrayList<>());
+
+ // Set the new thresholds
+ thresholdItemset = dateLine.plus(intervalItemset);
+ thresholdSequence = dateLine.plus(intervalSequence);
+
+ // Increase the number of Sequence read.
+ numberSequence++;
+
+ }else if(dateLine.isAfter(thresholdItemset)){
+ thresholdItemset = dateLine.plus(intervalItemset);
+
+ if(!listAlreadyInItemSet.get(indexList).isEmpty()){
+ listAlreadyInItemSet.add(new ArrayList<>());
+ indexList++;
+ }
+ }
+
+ Integer item = Integer.valueOf(lineSplit[1]);
+
+ if(!listAlreadyInItemSet.get(indexList).contains(item)) {
+ // An itemset should contain only one occurence of an item.
+ listAlreadyInItemSet.get(indexList).add(item);
+ }
+ }
+
+ // Write the last sequence in the file
+ this.writeSequence(listAlreadyInItemSet, writerSPMFDAT);
+ numberSequence++;
+
+ } catch (IOException e){
+ e.printStackTrace();
+ } finally {
+ if(writerSPMFDAT != null){
+ try {
+ writerSPMFDAT.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if(readerCSV != null){
+ try {
+ readerCSV.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public abstract List call();
+
+ protected void writeSequence(List> listItemsets, BufferedWriter fileWriter) throws IOException {
+ for(List listItemset : listItemsets){
+ Collections.sort(listItemset);
+
+ for(Integer item : listItemset){
+ fileWriter.write(item + " ");
+ }
+
+ fileWriter.write("-1 ");
+ }
+ fileWriter.write("-2");
+ fileWriter.newLine();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/VMSPSPMF.java b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/VMSPSPMF.java
new file mode 100644
index 0000000..4509030
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/model/wrapper/spmf/algorithm/VMSPSPMF.java
@@ -0,0 +1,135 @@
+package fr.insa.ocm.model.wrapper.spmf.algorithm;
+
+import ca.pfv.spmf.algorithms.sequentialpatterns.spam.AlgoVMSP;
+import ca.pfv.spmf.algorithms.sequentialpatterns.spam.PatternVMSP;
+import ca.pfv.spmf.patterns.itemset_list_integers_without_support.Itemset;
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.model.wrapper.spmf.AlgorithmLauncherSPMF;
+import fr.insa.ocm.model.wrapper.spmf.PatternSPMF;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.*;
+
+public class VMSPSPMF extends SequentialMiningAlgorithmSPMF {
+
+ // Name of this algorithm
+ public static final String NAME = "VMSP";
+
+ // Extension of the files used by the algorithm
+ private static final String VMSP_DAT_EXT = ".vmspdat";
+ private static volatile boolean isAlreadyConverted = false;
+
+ // SPMF stuff
+ private AlgoVMSP algorithmVMSP;
+ private double minsup; // Required to launch VMSP
+
+ // From which AlgorithmLauncher comes this algorithm, so Patterns could be easily made.
+ private AlgorithmLauncherSPMF algorithmLauncher;
+ private static int numberSequenceVMSP = 0;
+
+ public VMSPSPMF(double minsup, int maxGap,
+ Duration intervalItemset,
+ Duration intervalSequence,
+ AlgorithmLauncherSPMF algorithmLauncher) {
+ super(VMSP_DAT_EXT, intervalItemset, intervalSequence);
+
+ this.minsup = minsup;
+ this.algorithmLauncher = algorithmLauncher;
+
+ algorithmVMSP = new AlgoVMSP();
+ algorithmVMSP.setMaxGap(maxGap);
+ }
+
+ @Override
+ public void loadData(String csvPath) {
+ if(!isAlreadyConverted) {
+ isAlreadyConverted = true;
+ super.loadData(csvPath);
+
+ numberSequenceVMSP = super.numberSequence;
+ } else {
+ // We need to get the already converted file.
+ String fileName = csvPath.substring(0, csvPath.lastIndexOf("."));
+ File convertedFile = new File(fileName + VMSPSPMF.VMSP_DAT_EXT);
+
+ pathDataConverted = convertedFile.getAbsolutePath();
+ }
+ }
+
+ @NotNull
+ @Override
+ public List call() {
+ List resultPattern = new ArrayList<>();
+
+ try {
+ List> maxPatterns = algorithmVMSP.runAlgorithm(pathDataConverted, DebugLogger.directoryLog +"junk.txt", minsup);
+
+ resultPattern.addAll(computeResult(maxPatterns));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return resultPattern;
+ }
+
+ @NotNull
+ private List computeResult(@NotNull List> listSetPatternVMSP){
+ List listPatternResult = new ArrayList<>();
+
+ listSetPatternVMSP.forEach(setPatternVMSP -> {
+ if(setPatternVMSP != null) {
+ setPatternVMSP.forEach(patternVMSP -> listPatternResult.add(computePattern(patternVMSP)));
+ }
+ });
+
+ return listPatternResult;
+ }
+
+
+ @NotNull
+ private PatternSPMF computePattern(@NotNull PatternVMSP patternVMSP) {
+ double[] interestingMeasures = new double[Pattern.MeasureType.values().length];
+ StringBuilder patternDescriptorBuilder = new StringBuilder("[");
+ Set setAttributeName = new HashSet<>();
+
+ for (Itemset itemset : patternVMSP.getPrefix().getItemsets()) {
+
+ if (patternVMSP.getPrefix().getItemsets().get(0).equals(itemset)) {
+ patternDescriptorBuilder = patternDescriptorBuilder.append(" ... (");
+ } else {
+ patternDescriptorBuilder = patternDescriptorBuilder.append("\n\t... (");
+ }
+
+ for (Integer id : itemset.getItems()) {
+ String label = algorithmLauncher.getLabel(id);
+ setAttributeName.add(label);
+
+ if (itemset.get(0).equals(id)) {
+ patternDescriptorBuilder.append(" ... , ");
+ patternDescriptorBuilder.append(label);
+ patternDescriptorBuilder.append("");
+ } else {
+ patternDescriptorBuilder.append(", ... , ");
+ patternDescriptorBuilder.append(label);
+ patternDescriptorBuilder.append("");
+ }
+ }
+
+ patternDescriptorBuilder = patternDescriptorBuilder.append(", ... ) ...");
+ }
+ patternDescriptorBuilder = patternDescriptorBuilder.append("]");
+
+ interestingMeasures[Pattern.MeasureType.FREQUENCY.getIndex()] = ((double) patternVMSP.getSupport()) / numberSequenceVMSP;
+ interestingMeasures[Pattern.MeasureType.RELATIVE_SHORTNESS.getIndex()] = ((double) setAttributeName.size()) / algorithmLauncher.getNbAttributes();
+
+ return new PatternSPMF(patternDescriptorBuilder.toString(),
+ new ArrayList<>(setAttributeName),
+ algorithmLauncher,
+ interestingMeasures,
+ VMSPSPMF.NAME);
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/OCLApplication.java b/src/main/java/fr/insa/ocm/view/OCLApplication.java
new file mode 100644
index 0000000..3839b2e
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/OCLApplication.java
@@ -0,0 +1,42 @@
+package fr.insa.ocm.view;
+
+import fr.insa.ocm.view.mainuis.MainView;
+import fr.insa.ocm.viewmodel.OCLController;
+import javafx.application.Application;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+public class OCLApplication extends Application{
+
+ private static OCLApplication INSTANCE;
+
+ private static Stage mainStage;
+
+ public OCLApplication(){
+ INSTANCE = this;
+ }
+
+ public static void main(String[] args) {
+ launch(args);
+ }
+
+ @Override
+ public void init(){}
+
+ @Override
+ public void start(Stage primaryStage) throws IOException {
+ mainStage = primaryStage;
+
+ new MainView();
+ }
+
+ @Override
+ public void stop(){
+ OCLController.requestStop();
+ }
+
+ public static Stage getMainStage(){return mainStage;}
+
+ public static Application getInstance(){ return INSTANCE; }
+}
diff --git a/src/main/java/fr/insa/ocm/view/mainuis/DataView.java b/src/main/java/fr/insa/ocm/view/mainuis/DataView.java
new file mode 100644
index 0000000..09e5727
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/mainuis/DataView.java
@@ -0,0 +1,92 @@
+package fr.insa.ocm.view.mainuis;
+
+
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.control.Button;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+public class DataView {
+
+ @FXML private VBox mainVBox = null;
+
+ @FXML private GridPane gridPaneData = null;
+ @FXML private Button buttonClose = null;
+
+ DataView(){
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/mainuis/dataView.fxml"));
+ loader.setController(this);
+
+ try {
+ loader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+//
+// buttonClose.setOnAction(event -> this.buttonClosePressed());
+//
+// List> data = new ArrayList<>();
+//
+// String pathDataFile = OCLController.getInstance().getPathDataFile();
+// if(pathDataFile != null && !pathDataFile.equals("")){
+// CSVLoaderRealKD csvLoader = new CSVLoaderRealKD(pathDataFile);
+// data = csvLoader.loadCSV();
+// }
+//
+//
+// int index = 0;
+// for(List entry : data){
+// Label[] labels = new Label[entry.size()];
+//
+// for (int i = 0; i < labels.length; i++) {
+// if(index == 0){
+// Label label = new Label(entry.get(i));
+//
+// label.setFont(Font.font("Verdana", FontWeight.BOLD, 14));
+// label.setPadding(new Insets(5));
+// label.setStyle("" +
+// "-fx-border-color: black;" +
+// "-fx-border-width: 1px;");
+// label.setMaxWidth(1E300);
+// label.setMaxHeight(1E300);
+//
+// labels[i] = label;
+// }else {
+// Label label = new Label(entry.get(i));
+//
+// label.setFont(Font.font("Verdana", 14));
+// label.setPadding(new Insets(5));
+// label.setStyle("" +
+// "-fx-border-color: black;" +
+// "-fx-border-width: 1px;");
+// label.setMaxWidth(1E300);
+// label.setMaxHeight(1E300);
+//
+// labels[i] = label;
+// }
+// }
+//
+// gridPaneData.addRow(index, labels);
+// index++;
+// }
+//
+// Stage stage = new Stage();
+// stage.setScene(new Scene(mainVBox));
+// stage.setTitle("Data Viewer");
+// stage.show();
+//
+ }
+
+ private void buttonClosePressed(){
+ Stage stage = (Stage) buttonClose.getScene().getWindow();
+ stage.close();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/mainuis/FastForwardView.java b/src/main/java/fr/insa/ocm/view/mainuis/FastForwardView.java
new file mode 100644
index 0000000..4c064d0
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/mainuis/FastForwardView.java
@@ -0,0 +1,186 @@
+package fr.insa.ocm.view.mainuis;
+
+import com.sun.istack.internal.NotNull;
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.utils.serialize.condition.ConditionsDeserializer;
+import fr.insa.ocm.model.utils.serialize.condition.ConditionsSerializer;
+import fr.insa.ocm.view.OCLApplication;
+import fr.insa.ocm.view.misc.FastForwardWaitingView;
+import fr.insa.ocm.view.misc.parts.fastforward.AddConditionView;
+import fr.insa.ocm.view.misc.parts.fastforward.ConditionView;
+import fr.insa.ocm.viewmodel.OCLController;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import javafx.application.Platform;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.concurrent.Task;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FastForwardView {
+
+ @FXML private VBox mainVBox = null;
+ @FXML private ListView listViewMeasure = null;
+ @FXML private Button buttonLaunch = null;
+ @FXML private TextField textFieldNumberRound = null;
+ @FXML private TextField textFieldSecondPerRound = null;
+ @FXML private ChoiceBox choiceBoxKeepTrash = null;
+
+ // Menus to save or load a user list of conditions.
+ @FXML private MenuItem menuItemSave = null;
+ @FXML private MenuItem menuItemLoad = null;
+
+ private List listConditionView;
+ private AddConditionView addConditionView;
+
+ private ObservableList listConditions;
+
+ FastForwardView(){
+ listConditionView = new ArrayList<>();
+ listConditions = FXCollections.observableArrayList();
+ addConditionView = new AddConditionView(this);
+
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/mainuis/fastForwardView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ Scene scene = new Scene(mainVBox);
+ scene.getStylesheets().add(getClass().getResource("/css/button.css").toExternalForm());
+
+ buttonLaunch.setOnAction(event -> this.buttonLaunchPressed());
+
+ choiceBoxKeepTrash.getItems().addAll(Condition.ActionPatternChoice.KEEP, Condition.ActionPatternChoice.TRASH);
+ choiceBoxKeepTrash.setValue(Condition.ActionPatternChoice.TRASH);
+
+ FXMLLoader loaderCondition = new FXMLLoader(getClass().getResource("/fxml/misc/parts/fastforward/addCondition.fxml"));
+ loaderCondition.setController(addConditionView);
+ HBox conditionHBox = new HBox();
+ try {
+ conditionHBox = loaderCondition.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ listConditions.add(conditionHBox);
+
+ listViewMeasure.setItems(listConditions);
+
+ Stage stage = new Stage();
+ stage.setTitle("Choose your Patterns");
+ stage.setScene(scene);
+ stage.show();
+ }
+
+ private void buttonLaunchPressed(){
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ stage.close();
+
+ FastForwardWaitingView fastForwardWaitingView = new FastForwardWaitingView();
+
+ List listCondition = new ArrayList<>();
+
+ for(ConditionView conditionMeasureStaticView : listConditionView){
+ try{
+ listCondition.add(conditionMeasureStaticView.getCondition());
+ } catch (NumberFormatException e){
+ System.err.println("ERR: No valid number was found to create a valid condition");
+ }
+ }
+
+ new Thread(new Task() {
+ @Override
+ protected Void call() throws Exception {
+ OCLController.fastForward(Integer.valueOf(textFieldNumberRound.getText()),
+ Double.valueOf(textFieldSecondPerRound.getText()),
+ listCondition, fastForwardWaitingView, choiceBoxKeepTrash.getValue());
+ return null;
+ }
+ }).start();
+
+ }
+
+ public void buttonAddConditionPressed(){
+ // Remove the last HBox which make possible the user to add a condition.
+ listConditions.remove(listConditions.size() - 1);
+
+ // Add the condition required by the user.
+ ConditionView conditionView = addConditionView.createConditionView();
+ HBox conditionHBox = conditionView.getMainHBox();
+
+ listConditions.add(conditionHBox);
+ listConditionView.add(conditionView);
+
+ // Add in the last position of the ListView the mean to add another condition.
+ listConditions.add(addConditionView.getMainHBox());
+ }
+
+ public void buttonRemoveConditionPressed(@NotNull HBox measure){
+ listConditions.remove(measure);
+ listConditionView.removeIf(conditionView -> conditionView.getMainHBox().equals(measure));
+ }
+
+ @FXML
+ private void menuItemSaveSelected(){
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setInitialDirectory(new File(DebugLogger.directorySave));
+ fileChooser.setTitle("Save your condition list");
+ fileChooser.getExtensionFilters().addAll(
+ new FileChooser.ExtensionFilter("Json", "*.json"));
+ fileChooser.setSelectedExtensionFilter(fileChooser.getExtensionFilters().get(0));
+
+ File fileSelected = fileChooser.showSaveDialog(mainVBox.getScene().getWindow());
+ if(fileSelected != null){
+ final List listConditions = new ArrayList<>();
+ listConditionView.forEach(conditionView -> listConditions.add(conditionView.getCondition()));
+
+ ConditionsSerializer.saveConditions(fileSelected.getAbsolutePath(), listConditions);
+ }
+ }
+
+ @FXML
+ private void menuItemLoadSelected(){
+ // Initialize the filechooser so the user can only choose Json files.
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setInitialDirectory(new File(DebugLogger.directorySave));
+ fileChooser.setTitle("Load your condition list");
+ fileChooser.getExtensionFilters().addAll(
+ new FileChooser.ExtensionFilter("Json", "*.json"));
+ fileChooser.setSelectedExtensionFilter(fileChooser.getExtensionFilters().get(0));
+
+ File fileSelected = fileChooser.showOpenDialog(mainVBox.getScene().getWindow());
+ if(fileSelected != null){
+ // When the file is selected, delete the conditions in the view to replace them with the loaded conditions.
+ List listDeserializedConditions = ConditionsDeserializer.loadConditions(fileSelected.getAbsolutePath());
+
+ listConditionView.clear();
+ Platform.runLater(() -> listConditions.clear());
+
+ for(Condition condition : listDeserializedConditions){
+ ConditionView conditionView = addConditionView.createViewFromCondition(condition);
+ listConditionView.add(conditionView);
+ Platform.runLater(() ->listConditions.add(conditionView.getMainHBox()));
+ }
+ Platform.runLater(() -> listConditions.add(addConditionView.getMainHBox()));
+ }
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/view/mainuis/MainView.java b/src/main/java/fr/insa/ocm/view/mainuis/MainView.java
new file mode 100644
index 0000000..7189ba1
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/mainuis/MainView.java
@@ -0,0 +1,284 @@
+package fr.insa.ocm.view.mainuis;
+
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.utils.serialize.OCMSerializer;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.view.OCLApplication;
+import fr.insa.ocm.view.misc.*;
+import fr.insa.ocm.view.misc.menus.AboutView;
+import fr.insa.ocm.view.misc.menus.MonitorView;
+import fr.insa.ocm.view.misc.menus.LoadStateView;
+import fr.insa.ocm.view.misc.menus.SettingsView;
+import fr.insa.ocm.viewmodel.InfoAlgorithm;
+import fr.insa.ocm.viewmodel.OCLController;
+import javafx.application.Platform;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.concurrent.Task;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.BorderPane;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainView {
+
+ @FXML private BorderPane mainPane = null;
+
+ @FXML private ListView listKeptPatterns = null;
+ private ObservableList keptPatternsStrings = FXCollections.observableArrayList();
+
+ @FXML private Button buttonMining = null;
+ @FXML private Button buttonData = null;
+ @FXML private Button buttonPause = null;
+ @FXML private Button buttonFastForward = null;
+
+ @FXML private Label labelInfo = null;
+
+ private static MainView currentMainView;
+ private Stage mainStage = OCLApplication.getMainStage();
+
+ private boolean dataLoaded;
+
+ // Manage the file inputs of the user
+ private final FileChooser fileChooser = new FileChooser();
+ private boolean firstFile = true;
+
+ public MainView(){
+ dataLoaded = false;
+
+ currentMainView = this;
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/mainuis/mainView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ listKeptPatterns.setItems(keptPatternsStrings);
+
+ MenuBar menuBar = new MenuBar();
+ menuBar.prefWidthProperty().bind(mainStage.widthProperty());
+ mainPane.setTop(menuBar);
+
+ // File menu
+ Menu fileMenu = new Menu("File");
+ MenuItem newDataMenuItem = new MenuItem("Import a new dataset");
+ MenuItem savePatternMenuItem = new MenuItem("Export the patterns");
+ MenuItem saveStateMenuItem = new MenuItem("Save the current search state");
+ MenuItem loadStateMenuItem = new MenuItem("Load a search state");
+ MenuItem exitMenuItem = new MenuItem("Quit");
+
+ Menu editMenu = new Menu("Edit");
+ MenuItem menuItemSettings = new MenuItem("Settings");
+
+ Menu monitoringMenu = new Menu("Monitor");
+ MenuItem menuItemNumberAlgorithmLaunched = new MenuItem("Algorithms Launched Stats");
+ MenuItem menuItemBanditWeights = new MenuItem("Bandit Weights");
+ MenuItem menuItemCoactiveWeights = new MenuItem("Coactive Learning Weights");
+
+ Menu helpMenu = new Menu("Help");
+ MenuItem aboutMenuItem = new MenuItem("About");
+ MenuItem helpMenuItem = new MenuItem("Help");
+
+ exitMenuItem.setOnAction(event -> Platform.exit());
+ newDataMenuItem.setOnAction(event -> this.menuItemNewDataSelected());
+ savePatternMenuItem.setOnAction(event -> this.menuItemSavePatternSelected());
+ saveStateMenuItem.setOnAction(event -> this.menuItemSaveStateSelected());
+ loadStateMenuItem.setOnAction(event -> this.menuItemLoadStateSelected());
+
+ menuItemSettings.setOnAction(event -> this.menuItemSettingsSelected());
+
+ menuItemNumberAlgorithmLaunched.setOnAction(event -> this.menuItemNumberAlgorithmLaunchedSelected());
+ menuItemBanditWeights.setOnAction(event -> this.menuItemBanditWeightsSelected());
+ menuItemCoactiveWeights.setOnAction(event -> this.menuItemCoactiveWeightsSelected());
+
+ aboutMenuItem.setOnAction(event -> this.menuItemAboutSelected());
+ helpMenuItem.setOnAction(event -> this.menuItemHelpSelected());
+
+
+ //add item to fileMenu
+ fileMenu.getItems().addAll(newDataMenuItem, savePatternMenuItem, saveStateMenuItem, loadStateMenuItem,
+ new SeparatorMenuItem(), exitMenuItem);
+ editMenu.getItems().addAll(menuItemSettings);
+ monitoringMenu.getItems().addAll(menuItemNumberAlgorithmLaunched, menuItemBanditWeights, menuItemCoactiveWeights);
+ helpMenu.getItems().addAll(helpMenuItem, aboutMenuItem);
+ //Add menus to menubar
+ menuBar.getMenus().addAll(fileMenu, editMenu, monitoringMenu, helpMenu);
+
+ buttonMining.setOnAction(event -> this.buttonMiningClicked());
+ buttonData.setOnAction(event -> this.buttonDataPressed());
+ buttonPause.setOnAction(event -> this.buttonPausePressed());
+ buttonPause.setText("Pause Mining");
+ buttonFastForward.setOnAction(event -> this.buttonFastForwardPressed());
+
+ mainStage.setScene(new Scene(mainPane, 900, 600));
+ mainStage.setTitle("OneClick Mining");
+ mainStage.show();
+ }
+
+ public static MainView getCurrentMainView(){
+ return currentMainView;
+ }
+
+ public void setDataLoaded(){
+ dataLoaded = true;
+ }
+
+ public Label getLabelInfo(){
+ return labelInfo;
+ }
+
+ public void refreshKeptPatternsList(){
+ List patterns = OCMManager.patternWarehouseGetPatterns();
+ Platform.runLater(() -> keptPatternsStrings.clear());
+
+ for(Pattern p : patterns){
+ Platform.runLater(() -> keptPatternsStrings.add(p.toString()));
+ }
+ }
+
+ private void buttonMiningClicked(){
+ if(dataLoaded && OCMManager.isInitialized() && OCMManager.algorithmManagerIsMining()){
+ new MiningView(OCLController.getUserRank());
+ } else if(dataLoaded && OCMManager.isInitialized()){
+ new ErrorView("You can only request a knowledge gathering when the algorithms are running.");
+ } else if(dataLoaded){
+ new ErrorView("Please wait a few seconds for the initialization of the data handler.");
+ } else {
+ new ErrorView("No data has been found. Have you imported any data ?");
+ }
+ }
+
+ private void buttonDataPressed(){
+ new ErrorView("It's broken D:");
+ }
+
+ private void buttonPausePressed(){
+ if(OCMManager.algorithmManagerIsMining()){
+ new Thread(OCMManager::algorithmManagerPauseMining).start();
+ buttonPause.setText("Resume Mining");
+ InfoAlgorithm.setPaused(true);
+ }else{
+ new Thread(OCMManager::algorithmManagerResumeMining).start();
+ buttonPause.setText("Pause Mining");
+ InfoAlgorithm.setPaused(false);
+ }
+ }
+
+ private void buttonFastForwardPressed(){
+ if(dataLoaded && OCMManager.isInitialized() && OCMManager.algorithmManagerIsMining()){
+ new FastForwardView();
+ } else if(dataLoaded && OCMManager.isInitialized()){
+ new ErrorView("You can only request a fast forward process when the algorithms are running.");
+ } else if(dataLoaded){
+ new ErrorView("Please wait a few seconds for the initialization of the data handler.");
+ } else {
+ new ErrorView("No data has been found. Have you imported any data ?");
+ }
+ }
+
+ //********** Menu Item Methods **********//
+
+ // Menu File //
+
+ private void menuItemNewDataSelected(){
+ fileChooser.setTitle("Choose a file containing your data");
+ File file = fileChooser.showOpenDialog(mainStage);
+ if(file != null){
+ if(firstFile) {
+ new Thread(() -> OCLController.initialize(file.getAbsolutePath())).start();
+ firstFile = false;
+ } else {
+ new Thread(() -> OCLController.reload(file.getAbsolutePath())).start();
+ }
+ dataLoaded = true;
+ }else{
+ new ErrorView("No file was selected !");
+ }
+ }
+
+ private void menuItemSavePatternSelected(){
+ fileChooser.setTitle("Choose a file where to export your results");
+ File file = fileChooser.showSaveDialog(mainStage);
+ if (file != null){
+ OCLController.exportInterestingPatterns(file.getAbsolutePath());
+ }
+ }
+
+ private void menuItemSaveStateSelected(){
+ if(!OCMManager.algorithmManagerIsMining()) {
+ fileChooser.setTitle("Choose a file where to serialize your current research");
+ File file = fileChooser.showSaveDialog(mainStage);
+ if (file != null) {
+ InfoView infoView = new InfoView("Saving your state", "Currently saving your state.");
+ infoView.setDisableContinue(true);
+
+ new Thread(new Task() {
+ @Override
+ protected Void call() throws Exception {
+ new OCMSerializer(file.getAbsolutePath());
+ Thread.sleep(500);
+
+ infoView.setProgress(1.);
+ infoView.setDisableContinue(false);
+
+ return null;
+ }
+ }).start();
+ }
+ }else{
+ new ErrorView("You should pause the mining algorithms before saving your current search state.");
+ }
+ }
+
+ private void menuItemLoadStateSelected(){
+ if(!OCMManager.algorithmManagerIsMining()) {
+ new LoadStateView();
+ }else{
+ new ErrorView("You should pause the mining algorithms before loading any search state.");
+ }
+ }
+
+ // Menu Edit //
+
+ private void menuItemSettingsSelected(){
+ new SettingsView();
+ }
+
+ // Menu Monitor //
+
+ private void menuItemNumberAlgorithmLaunchedSelected(){
+ new MonitorView(MonitorView.MonitorType.ALGORITHM);
+ }
+
+ private void menuItemBanditWeightsSelected(){
+ new MonitorView(MonitorView.MonitorType.BANDIT);
+ }
+
+ private void menuItemCoactiveWeightsSelected(){
+ new MonitorView(MonitorView.MonitorType.COACTIVE);
+ }
+
+ // Menu Help //
+
+ private void menuItemHelpSelected(){
+ new ErrorView("This menu is currently unavailable :c"); //TODO -- Faire une fenêtre d'aide pour l'utilisateur.
+ }
+
+ private void menuItemAboutSelected(){
+ new AboutView();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/mainuis/MiningView.java b/src/main/java/fr/insa/ocm/view/mainuis/MiningView.java
new file mode 100644
index 0000000..fd5936f
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/mainuis/MiningView.java
@@ -0,0 +1,112 @@
+package fr.insa.ocm.view.mainuis;
+
+import com.sun.istack.internal.NotNull;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.view.misc.parts.mining.PatternView;
+import fr.insa.ocm.viewmodel.OCLController;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MiningView {
+
+ @FXML private VBox mainVBox = null;
+ @FXML private ListView listProposedPatterns = null;
+ @FXML private Button buttonConfirm = null;
+ @FXML private Button buttonSetKeep = null;
+ @FXML private Button buttonSetTrash = null;
+
+ @FXML private Label firstLabel = null;
+ @FXML private Label labelRound = null;
+
+ private List patterns;
+
+ private List patternControllers;
+
+ MiningView(@NotNull List userRank){
+ patternControllers = new ArrayList<>();
+ patterns = userRank;
+
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/mainuis/miningView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ Scene scene = new Scene(mainVBox);
+ scene.getStylesheets().add(getClass().getResource("/css/button.css").toExternalForm());
+ scene.getStylesheets().add(getClass().getResource("/css/hbox.css").toExternalForm());
+
+ buttonConfirm.setOnAction(event -> this.buttonConfirmPressed());
+ buttonSetKeep.setOnAction(event -> this.buttonSetKeepPressed());
+ buttonSetTrash.setOnAction(event -> this.buttonSetTrashPressed());
+
+ labelRound.setText("Round "+ OCMManager.coactiveGetNbRound());
+
+ ObservableList hBoxes = FXCollections.observableArrayList();
+
+ patterns.forEach(pattern -> {
+ PatternView patternView = new PatternView(pattern, patterns.indexOf(pattern));
+ patternControllers.add(patternView);
+ hBoxes.add(patternView.getMainHBox());
+ });
+
+ listProposedPatterns.setItems(hBoxes);
+
+ Stage stage = new Stage();
+ stage.setTitle("Choose your Patterns");
+ stage.setScene(scene);
+ stage.show();
+ }
+
+ @FXML
+ private void buttonConfirmPressed(){
+ List keptPatterns = new ArrayList<>();
+ List neutralPatterns = new ArrayList<>();
+ List trashedPatterns = new ArrayList<>();
+
+ for(PatternView patternView : patternControllers){
+ Pattern pattern = patterns.get(patternView.getIndex());
+
+ if(patternView.isKept()){
+ keptPatterns.add(pattern);
+ }else if(patternView.isTrashed()){
+ trashedPatterns.add(pattern);
+ }else{
+ neutralPatterns.add(pattern);
+ }
+ }
+
+ OCLController.setPatternList(keptPatterns, neutralPatterns, trashedPatterns);
+
+ MainView.getCurrentMainView().refreshKeptPatternsList();
+
+ Stage stage = (Stage) buttonConfirm.getScene().getWindow();
+ stage.close();
+ }
+
+ private void buttonSetKeepPressed(){
+ patternControllers.forEach(PatternView::setIsKept);
+ }
+
+ private void buttonSetTrashPressed(){
+ patternControllers.forEach(PatternView::setIsTrashed);
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/ErrorView.java b/src/main/java/fr/insa/ocm/view/misc/ErrorView.java
new file mode 100644
index 0000000..10ef1bb
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/ErrorView.java
@@ -0,0 +1,55 @@
+package fr.insa.ocm.view.misc;
+
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import jline.internal.Nullable;
+
+import java.io.IOException;
+
+public class ErrorView {
+
+ @FXML private VBox mainVBox;
+ @FXML private Button closeButton;
+ @FXML private Label errorLabel;
+
+ private final static String defaultText = "Oh no ! An error has occured ! D:";
+ private String errorText = "";
+
+ public ErrorView(@Nullable String errorTxt){
+ this.errorText = errorTxt;
+ if (errorTxt == null || errorTxt.equals("")){
+ this.errorText = defaultText;
+ }
+
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/errorView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ closeButton.setOnMouseClicked(event -> this.quit());
+ errorLabel.setText(errorText);
+
+ Stage stage = new Stage();
+ stage.setScene(new Scene(mainVBox));
+ stage.setTitle("Error");
+ stage.show();
+ }
+
+ @FXML
+ private void quit(){
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ stage.close();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/FastForwardWaitingView.java b/src/main/java/fr/insa/ocm/view/misc/FastForwardWaitingView.java
new file mode 100644
index 0000000..4340796
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/FastForwardWaitingView.java
@@ -0,0 +1,89 @@
+package fr.insa.ocm.view.misc;
+
+import fr.insa.ocm.viewmodel.OCLController;
+import javafx.application.Platform;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.StringProperty;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.ProgressBar;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+public class FastForwardWaitingView {
+
+ @FXML private VBox mainVBox = null;
+ @FXML private Label labelCurrentOperation = null;
+ @FXML private Label labelTime = null;
+ @FXML private ProgressBar progressBarMining = null;
+ @FXML private ProgressBar progressBarRound = null;
+ @FXML private Button buttonAbortFinish = null;
+
+ private boolean hasFinished;
+
+ public FastForwardWaitingView(){
+ hasFinished = false;
+
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/fastForwardWaitingView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ labelCurrentOperation.setText("Current operation: Initializing the mining process.");
+ labelTime.setText("Estimated time before completion: Unknown");
+ buttonAbortFinish.setText("Abort");
+
+ progressBarMining.setProgress(0);
+ progressBarRound.setProgress(0);
+
+ buttonAbortFinish.setOnAction(event -> this.buttonAbortFinishPressed());
+
+ Stage stage = new Stage();
+ stage.setScene(new Scene(mainVBox));
+ stage.setTitle("Fast Forward Mining");
+ stage.show();
+ }
+
+ public void setHasFinished(boolean b){
+ hasFinished = b;
+ if(hasFinished){
+ Platform.runLater(() -> buttonAbortFinish.setText("Finished"));
+ }
+ }
+
+ public void bindCurrentOperation(StringProperty currentOpration){
+ labelCurrentOperation.textProperty().bind(currentOpration);
+ }
+
+ public void bindCurrentTime(StringProperty remainingTime){
+ labelTime.textProperty().bind(remainingTime);
+ }
+
+ public void bindProgressMining(DoubleProperty progressMining){
+ progressBarMining.progressProperty().bind(progressMining);
+ }
+
+ public void bindProgressLearning(DoubleProperty progressLearning) {
+ progressBarRound.progressProperty().bind(progressLearning);
+ }
+
+ private void buttonAbortFinishPressed(){
+ if(!hasFinished){
+ OCLController.requestFFStop();
+ }
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ stage.close();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/InfoView.java b/src/main/java/fr/insa/ocm/view/misc/InfoView.java
new file mode 100644
index 0000000..d552b38
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/InfoView.java
@@ -0,0 +1,67 @@
+package fr.insa.ocm.view.misc;
+
+import com.sun.istack.internal.NotNull;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.ProgressBar;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+public class InfoView {
+
+ @FXML private VBox mainVBox = null;
+ @FXML private Label labelInfo = null;
+ @FXML private ProgressBar progressBar = null;
+ @FXML private Button buttonContinue = null;
+
+ private String title;
+ private String mainText;
+
+ public InfoView(@NotNull String title, @NotNull String mainText){
+ this.title = title;
+ this.mainText = mainText;
+
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/infoView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ labelInfo.setText(mainText);
+
+ buttonContinue.setOnAction(event -> this.buttonContinuePressed());
+
+ Stage stage = new Stage();
+ stage.setScene(new Scene(mainVBox));
+ stage.setTitle(title);
+ stage.show();
+ }
+
+ public void setDisableContinue(boolean b){
+ buttonContinue.setDisable(b);
+ }
+
+ public void setProgress(double d){
+ d = d > 1 ? 1 : d;
+ d = d < 0 ? 0 : d;
+
+ progressBar.setProgress(d);
+ }
+
+ private void buttonContinuePressed(){
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ stage.close();
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/menus/AboutView.java b/src/main/java/fr/insa/ocm/view/misc/menus/AboutView.java
new file mode 100644
index 0000000..490f44c
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/menus/AboutView.java
@@ -0,0 +1,47 @@
+package fr.insa.ocm.view.misc.menus;
+
+import fr.insa.ocm.viewmodel.OCLController;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+public class AboutView {
+
+ @FXML private VBox mainVBox = null;
+ @FXML private Label versionLabel = null;
+ @FXML private Button closeButton = null;
+
+ public AboutView(){
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/menus/aboutView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ versionLabel.setText("OneClick Mining - Alpha v"+ OCLController.VERSION);
+
+ closeButton.setOnAction(event -> this.close());
+
+ Stage stage = new Stage();
+ stage.setScene(new Scene(mainVBox));
+ stage.setTitle("About OneClick Mining");
+ stage.show();
+ }
+
+ private void close(){
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ stage.close();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/menus/LoadStateView.java b/src/main/java/fr/insa/ocm/view/misc/menus/LoadStateView.java
new file mode 100644
index 0000000..60f9d2c
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/menus/LoadStateView.java
@@ -0,0 +1,169 @@
+package fr.insa.ocm.view.misc.menus;
+
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.utils.serialize.OCMDeserializer;
+import fr.insa.ocm.view.misc.ErrorView;
+import fr.insa.ocm.view.misc.InfoView;
+import fr.insa.ocm.view.mainuis.MainView;
+import fr.insa.ocm.viewmodel.OCLController;
+import javafx.application.Platform;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.concurrent.Task;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+
+import java.io.File;
+import java.io.IOException;
+
+public class LoadStateView {
+
+ @FXML private VBox mainVBox = null;
+
+ @FXML private Button buttonSearch = null;
+ @FXML private Button buttonData = null;
+ @FXML private Button buttonLoad = null;
+
+ @FXML private Label labelSearch = null;
+ @FXML private Label labelData = null;
+
+ private SimpleStringProperty msgSearch;
+ private SimpleStringProperty msgData;
+
+ private File fileSearch;
+ private File fileData;
+
+ private boolean isSearchFileSelected;
+ private boolean isDataFileSelected;
+
+ public LoadStateView(){
+ msgSearch = new SimpleStringProperty("Please select a search state file to load");
+ msgData = new SimpleStringProperty("Please select a the corresponding data file to load");
+
+ isSearchFileSelected = false;
+ isDataFileSelected = false;
+
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/menus/loadStateView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ labelSearch.textProperty().bind(msgSearch);
+ labelData.textProperty().bind(msgData);
+
+ labelSearch.setWrapText(true);
+ labelData.setWrapText(true);
+
+ buttonSearch.setOnAction(event -> this.buttonSearchPressed());
+ buttonData.setOnAction(event -> this.buttonDataPressed());
+ buttonLoad.setOnAction(event -> this.buttonLoadPressed());
+
+ Stage stage = new Stage();
+ stage.setScene(new Scene(mainVBox));
+ stage.setTitle("Load a saved state");
+ stage.show();
+ }
+
+ private void buttonSearchPressed(){
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ FileChooser fileChooserSearch = new FileChooser();
+
+ fileChooserSearch.setTitle("Choose the file which contains the searching state");
+ fileSearch = fileChooserSearch.showOpenDialog(stage);
+
+ if(fileSearch != null){
+ msgSearch.set(fileSearch.getAbsolutePath());
+
+ buttonLoad.setDisable(!isDataFileSelected);
+ isSearchFileSelected = true;
+ }else{
+ new ErrorView("No Search file were selected.");
+ }
+ }
+
+ private void buttonDataPressed(){
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ FileChooser fileChooserData = new FileChooser();
+
+ fileChooserData.setTitle("Choose the file which contains the data associated with the search state");
+ fileData = fileChooserData.showOpenDialog(stage);
+
+ if(fileData != null) {
+ msgData.set(fileData.getAbsolutePath());
+
+ buttonLoad.setDisable(!isSearchFileSelected);
+ isDataFileSelected = true;
+ }else{
+ new ErrorView("No Data file were selected.");
+ }
+ }
+
+ private void buttonLoadPressed(){
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ stage.close();
+
+ InfoView infoView = new InfoView("Loading a state", "Currently loading the selected state.");
+ infoView.setDisableContinue(true);
+
+ new Thread(new Task() {
+ @Override
+ protected Void call() throws Exception {
+ OCMManager.algorithmManagerStopMining();
+
+ // Import the data
+ OCLController.reload(fileData.getPath());
+ Thread.sleep(250);
+ Platform.runLater(() -> infoView.setProgress(0.5));
+
+ // Import the saveState
+ new OCMDeserializer(fileSearch.getAbsolutePath());
+ Thread.sleep(250);
+ Platform.runLater(() -> infoView.setProgress(1.));
+
+ MainView.getCurrentMainView().setDataLoaded();
+ Platform.runLater(() -> MainView.getCurrentMainView().refreshKeptPatternsList());
+
+ Platform.runLater(() -> infoView.setDisableContinue(false));
+
+// OCMManager.getInstance().reloadData(fileData.getPath());
+// OCLController.getInstance().setFromLoadState();
+// Thread.sleep(500);
+// infoView.setProgress(0.5);
+//
+// // Import the saveState
+// new OCMDeserializer(fileSearch.getAbsolutePath());
+// Thread.sleep(500);
+// infoView.setProgress(1.);
+//
+// try{
+// OCMManager.getInstance().setInitialized(true);
+// } catch (Exception e){
+// e.printStackTrace();
+// }
+//
+// MainView.getCurrentMainView().setDataLoaded();
+// Platform.runLater(() -> MainView.getCurrentMainView().refreshKeptPatternsList());
+//
+// infoView.setDisableContinue(false);
+
+ OCMManager.algorithmManagerStartMining();
+
+ return null;
+ }
+ }).start();
+
+
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/menus/MonitorView.java b/src/main/java/fr/insa/ocm/view/misc/menus/MonitorView.java
new file mode 100644
index 0000000..6d4adeb
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/menus/MonitorView.java
@@ -0,0 +1,87 @@
+package fr.insa.ocm.view.misc.menus;
+
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.viewmodel.Monitor;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+public class MonitorView {
+
+ public enum MonitorType{
+ ALGORITHM, BANDIT, COACTIVE;
+
+ public SimpleStringProperty getTextProperty(){
+ switch (this){
+ case ALGORITHM:
+ return Monitor.getMonitorAlgorithmsLaunched();
+ case BANDIT:
+ return Monitor.getMonitorBanditWeights();
+ case COACTIVE:
+ return Monitor.getMonitorCoactiveWeights();
+ default:
+ DebugLogger.printDebug("MonitorView: Unable to retrieve the correct monitor type.", DebugLogger.MessageSeverity.MEDIUM);
+ return new SimpleStringProperty("");
+ }
+ }
+
+ @NotNull
+ public String getStageTitle(){
+ switch (this){
+ case ALGORITHM:
+ return "Monitoring Launched Algorithms";
+ case BANDIT:
+ return "Monitoring Bandit Weights";
+ case COACTIVE:
+ return "Monitoring Coactive Weights";
+ default:
+ return "Monitor";
+ }
+ }
+ }
+
+ @FXML private VBox mainVBox = null;
+ @FXML private Label labelInfo = null;
+ @FXML private Button buttonClose = null;
+
+ private MonitorType monitorType;
+
+ public MonitorView(MonitorType monitorType){
+ this.monitorType = monitorType;
+
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/menus/monitor/monitorView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ buttonClose.setOnAction(event -> this.buttonClosePressed());
+
+ labelInfo.textProperty().bind(monitorType.getTextProperty());
+
+ Stage stage = new Stage();
+ stage.setScene(new Scene(mainVBox));
+ stage.setTitle(monitorType.getStageTitle());
+ stage.show();
+ }
+
+ private void buttonClosePressed(){
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ stage.close();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/menus/SettingsView.java b/src/main/java/fr/insa/ocm/view/misc/menus/SettingsView.java
new file mode 100644
index 0000000..b76f772
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/menus/SettingsView.java
@@ -0,0 +1,135 @@
+package fr.insa.ocm.view.misc.menus;
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.oneclicklearning.cache.set.CacheSet;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.view.misc.parts.settings.library.SettingsRealKDView;
+import fr.insa.ocm.view.misc.parts.settings.library.SettingsSPMFView;
+import fr.insa.ocm.view.misc.parts.settings.pattern.SettingsPatternView;
+import fr.insa.ocm.viewmodel.OCLController;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SettingsView {
+
+ @FXML private VBox mainVBox = null;
+ @FXML private ChoiceBox choiceBoxWrapper = null;
+
+ // Library tab
+ @FXML private ScrollPane paneSettings = null;
+
+ // Pattern tab
+ @FXML private ListView listViewPattern = null;
+ @FXML private TextField textFieldNumberPattern = null;
+
+ // Main buttons
+ @FXML private Button buttonConfirm = null;
+ @FXML private Button buttonCancel = null;
+
+ private SettingsSPMFView settingsSPMFView;
+ private SettingsRealKDView settingsRealKDView;
+ private static Pattern.WrapperType previousValue = Pattern.WrapperType.SPMF;
+ private List listSettingsPatternView;
+
+ public SettingsView(){
+ settingsSPMFView = new SettingsSPMFView();
+ settingsRealKDView = new SettingsRealKDView();
+ listSettingsPatternView = new ArrayList<>();
+
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/menus/settingsView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ //********** Initialization methods **********//
+
+ @FXML
+ public void initialize(){
+ choiceBoxWrapper.getItems().setAll(Pattern.WrapperType.values());
+ choiceBoxWrapper.setValue(previousValue);
+
+ this.choiceBoxWrapperModified();
+
+ choiceBoxWrapper.setOnAction(event -> this.choiceBoxWrapperModified());
+
+ // Pattern tab initialization
+ ObservableList observableSettings = FXCollections.observableArrayList();
+ for(Pattern.MeasureType measureType : Pattern.MeasureType.values()){
+ SettingsPatternView settingsPV = new SettingsPatternView(measureType);
+
+ listSettingsPatternView.add(settingsPV);
+ observableSettings.add(settingsPV.getMainHBox());
+ }
+ listViewPattern.setItems(observableSettings);
+
+ textFieldNumberPattern.setText(OCMManager.cacheGetSizeListBestPatterns() + "");
+
+ // Buttons initialization
+ buttonCancel.setOnAction(event -> this.buttonCancelPressed());
+ buttonConfirm.setOnAction(event -> this.buttonConfirmPressed());
+
+ Stage stage = new Stage();
+ stage.setScene(new Scene(mainVBox));
+ stage.setTitle("Settings");
+ stage.show();
+ }
+
+ //********** Action methods **********//
+
+ private void choiceBoxWrapperModified(){
+
+ switch (choiceBoxWrapper.getValue()){
+ case REALKD:
+ paneSettings.setContent(settingsRealKDView.getMainVBox());
+ break;
+ case SPMF:
+ paneSettings.setContent(settingsSPMFView.getMainVBox());
+ break;
+ }
+ }
+
+ private void buttonCancelPressed(){
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ stage.close();
+ }
+
+ private void buttonConfirmPressed(){
+ OCLController.setWrapperType(choiceBoxWrapper.getValue());
+ previousValue = choiceBoxWrapper.getValue();
+
+ switch (choiceBoxWrapper.getValue()){
+ case REALKD:
+ break;
+ case SPMF:
+ settingsSPMFView.setSettingsAlgorithmLauncher();
+ listSettingsPatternView.forEach(SettingsPatternView::setMeasureChoice);
+ break;
+ }
+
+ try{
+ OCMManager.cacheSetSizeListBestPatterns(Integer.valueOf(textFieldNumberPattern.getText()));
+ } catch (NumberFormatException e){
+ DebugLogger.printDebug("SettingsView: Wrong number given to the ranking size in the settings.", DebugLogger.MessageSeverity.MEDIUM);
+ }
+
+ Stage stage = (Stage) mainVBox.getScene().getWindow();
+ stage.close();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/AddConditionView.java b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/AddConditionView.java
new file mode 100644
index 0000000..8b861a3
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/AddConditionView.java
@@ -0,0 +1,85 @@
+package fr.insa.ocm.view.misc.parts.fastforward;
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.view.mainuis.FastForwardView;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.control.Button;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.layout.HBox;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+
+public class AddConditionView {
+
+ @FXML private HBox mainHBox = null;
+ @FXML private ChoiceBox choiceBoxCondition = null;
+ @FXML private Button buttonAddCondition = null;
+
+ private FastForwardView parent;
+
+ public AddConditionView(@NotNull FastForwardView parent){
+ this.parent = parent;
+ }
+
+ @FXML
+ public void initialize(){
+ buttonAddCondition.setOnAction(event -> parent.buttonAddConditionPressed());
+
+ choiceBoxCondition.getItems().addAll(Condition.ConditionType.values());
+ choiceBoxCondition.setValue(Condition.ConditionType.CONDITION_ATTRIBUTE);
+ }
+
+ public HBox getMainHBox(){ return mainHBox; }
+
+ @Nullable
+ public ConditionView createConditionView(){
+ ConditionView conditionView = null;
+ try {
+ FXMLLoader loader = null;
+ switch (choiceBoxCondition.getValue()) {
+ case CONDITION_ATTRIBUTE:
+ conditionView = new ConditionAttributeView(parent);
+ loader = new FXMLLoader(getClass().getResource("/fxml/misc/parts/fastforward/conditionAttribute.fxml"));
+ break;
+ case CONDITION_ALGORITHM:
+ conditionView = new ConditionAlgorithmView(parent);
+ loader = new FXMLLoader(getClass().getResource("/fxml/misc/parts/fastforward/conditionAlgorithm.fxml"));
+ break;
+ case CONDITION_MEASURE_STATIC:
+ conditionView = new ConditionMeasureStaticView(parent);
+ loader = new FXMLLoader(getClass().getResource("/fxml/misc/parts/fastforward/conditionMeasureStatic.fxml"));
+ break;
+ case CONDITION_MEASURE_BETWEEN:
+ conditionView = new ConditionMeasureBetweenView(parent);
+ loader = new FXMLLoader(getClass().getResource("/fxml/misc/parts/fastforward/conditionMeasureBetween.fxml"));
+ break;
+ case CONDITION_MEASURE_DYNAMIC:
+ conditionView = new ConditionMeasureDynamicView(parent);
+ loader = new FXMLLoader(getClass().getResource("/fxml/misc/parts/fastforward/conditionMeasureDynamic.fxml"));
+ break;
+ }
+ loader.setController(conditionView);
+ loader.load();
+ }catch (IOException | NullPointerException e){
+ e.printStackTrace();
+ }
+ return conditionView;
+ }
+
+ @Nullable
+ public ConditionView createViewFromCondition(@NotNull Condition condition){
+ ConditionView conditionView = null;
+ choiceBoxCondition.setValue(condition.getConditionType());
+
+ conditionView = createConditionView();
+ if (conditionView != null) {
+ conditionView.importCondition(condition);
+ }
+
+ return conditionView;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionAlgorithmView.java b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionAlgorithmView.java
new file mode 100644
index 0000000..336a9c9
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionAlgorithmView.java
@@ -0,0 +1,65 @@
+package fr.insa.ocm.view.misc.parts.fastforward;
+
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.view.mainuis.FastForwardView;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import fr.insa.ocm.model.utils.fastforward.condition.ConditionAlgorithm;
+import javafx.fxml.FXML;
+import javafx.scene.control.ChoiceBox;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class ConditionAlgorithmView extends ConditionView {
+
+ @FXML private ChoiceBox choiceBoxAlgorithmName = null;
+ @FXML private ChoiceBox choiceBoxAlgorithmState = null;
+
+ public ConditionAlgorithmView(FastForwardView parent) {
+ super(parent);
+ }
+
+ public void initialize(){
+ super.initialize();
+
+ choiceBoxAlgorithmState.getItems().addAll(ConditionAlgorithm.AlgorithmState.values());
+ choiceBoxAlgorithmState.setValue(ConditionAlgorithm.AlgorithmState.CREATEDBY);
+
+ List listAlgorithmName = OCMManager.algorithmLauncherGetListAlgorithmName();
+
+ choiceBoxAlgorithmName.getItems().addAll(listAlgorithmName);
+ choiceBoxAlgorithmName.setValue(listAlgorithmName.get(0));
+ }
+
+ @Override
+ public Condition getCondition() {
+ return new ConditionAlgorithm(getActionPatternChoice(),
+ getAlgorithmNameSelected(),
+ getAlgorithmStateSelected());
+ }
+
+ //********** Serialization Methods **********//
+
+ @Override
+ public void importCondition(@NotNull Condition condition){
+ super.importCondition(condition);
+ if(condition instanceof ConditionAlgorithm) {
+ ConditionAlgorithm conditionAlgorithm = (ConditionAlgorithm) condition;
+
+ choiceBoxAlgorithmName.setValue(conditionAlgorithm.getAlgorithmName());
+ choiceBoxAlgorithmState.setValue(conditionAlgorithm.getAlgorithmState());
+ }
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ private String getAlgorithmNameSelected(){
+ return choiceBoxAlgorithmName.getValue();
+ }
+
+ private ConditionAlgorithm.AlgorithmState getAlgorithmStateSelected(){
+ return choiceBoxAlgorithmState.getValue();
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionAttributeView.java b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionAttributeView.java
new file mode 100644
index 0000000..871a625
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionAttributeView.java
@@ -0,0 +1,61 @@
+package fr.insa.ocm.view.misc.parts.fastforward;
+
+
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.view.mainuis.FastForwardView;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import fr.insa.ocm.model.utils.fastforward.condition.ConditionAttribute;
+import javafx.fxml.FXML;
+import javafx.scene.control.ChoiceBox;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class ConditionAttributeView extends ConditionView {
+
+ @FXML private ChoiceBox choiceBoxAttributeName = null;
+ @FXML private ChoiceBox choiceBoxAttributeState = null;
+
+ public ConditionAttributeView(@NotNull FastForwardView parent) {
+ super(parent);
+ }
+
+ @Override
+ public void initialize(){
+ super.initialize();
+
+ choiceBoxAttributeState.getItems().addAll(ConditionAttribute.AttributeState.values());
+ choiceBoxAttributeState.setValue(ConditionAttribute.AttributeState.PRESENT);
+
+ List listAttributeName = OCMManager.algorithmLauncherGetListAttributeName();
+
+ choiceBoxAttributeName.getItems().addAll(listAttributeName);
+ choiceBoxAttributeName.setValue(listAttributeName.get(0));
+ }
+
+ @Override
+ public Condition getCondition() {
+ return new ConditionAttribute(getActionPatternChoice(), getAttributeNameSelected(), getAttributeStateSelected());
+ }
+
+ //********** Serialization Methods **********//
+
+ @Override
+ public void importCondition(@NotNull Condition condition){
+ super.importCondition(condition);
+ if(condition instanceof ConditionAttribute){
+ ConditionAttribute conditionAttribute = (ConditionAttribute) condition;
+
+ choiceBoxAttributeName.setValue(conditionAttribute.getAttributeName());
+ choiceBoxAttributeState.setValue(conditionAttribute.getAttributeState());
+ }
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ private String getAttributeNameSelected(){ return choiceBoxAttributeName.getValue(); }
+
+ private ConditionAttribute.AttributeState getAttributeStateSelected(){ return choiceBoxAttributeState.getValue(); }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionMeasureBetweenView.java b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionMeasureBetweenView.java
new file mode 100644
index 0000000..12de6b1
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionMeasureBetweenView.java
@@ -0,0 +1,74 @@
+package fr.insa.ocm.view.misc.parts.fastforward;
+
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.view.mainuis.FastForwardView;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import fr.insa.ocm.model.utils.fastforward.condition.ConditionMeasureBetween;
+import javafx.fxml.FXML;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.TextField;
+import org.jetbrains.annotations.NotNull;
+
+public class ConditionMeasureBetweenView extends ConditionView{
+
+ @FXML private ChoiceBox choiceBoxMeasure = null;
+ @FXML private ChoiceBox choiceBoxIncluded = null;
+ @FXML private TextField textFieldLowValue = null;
+ @FXML private TextField textFieldHighValue = null;
+
+ public ConditionMeasureBetweenView(@NotNull FastForwardView parent){
+ super(parent);
+ }
+
+ @Override
+ @FXML
+ public void initialize(){
+ super.initialize();
+
+ choiceBoxMeasure.getItems().addAll(Pattern.MeasureType.values());
+ choiceBoxMeasure.setValue(Pattern.MeasureType.FREQUENCY);
+
+ choiceBoxIncluded.getItems().addAll(ConditionMeasureBetween.IntervalChoice.values());
+ choiceBoxIncluded.setValue(ConditionMeasureBetween.IntervalChoice.INSIDE_IN_IN);
+ }
+
+ @Override
+ public Condition getCondition() {
+ return new ConditionMeasureBetween(getActionPatternChoice(), getMeasureType(), getIntervalChoice(), getThresholdValueLowerBound(), getThresholdValueHigherBound());
+ }
+
+ //********** Serialization Methods **********//
+
+ @Override
+ public void importCondition(@NotNull Condition condition){
+ super.importCondition(condition);
+ if(condition instanceof ConditionMeasureBetween){
+ ConditionMeasureBetween conditionMeBe = (ConditionMeasureBetween) condition;
+
+ choiceBoxMeasure.setValue(conditionMeBe.getMeasureType());
+ choiceBoxIncluded.setValue(conditionMeBe.getIntervalChoice());
+ textFieldLowValue.setText(Double.toString(conditionMeBe.getLowerBound().getThresholdValue()));
+ textFieldHighValue.setText(Double.toString(conditionMeBe.getHigerBound().getThresholdValue()));
+ }
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ private ConditionMeasureBetween.IntervalChoice getIntervalChoice(){
+ return choiceBoxIncluded.getValue();
+ }
+
+ private Pattern.MeasureType getMeasureType(){
+ return choiceBoxMeasure.getValue();
+ }
+
+ private double getThresholdValueLowerBound(){
+ return Double.valueOf(textFieldLowValue.getText());
+ }
+
+ private double getThresholdValueHigherBound(){
+ return Double.valueOf(textFieldHighValue.getText());
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionMeasureDynamicView.java b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionMeasureDynamicView.java
new file mode 100644
index 0000000..9243b2f
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionMeasureDynamicView.java
@@ -0,0 +1,66 @@
+package fr.insa.ocm.view.misc.parts.fastforward;
+
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.view.mainuis.FastForwardView;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import fr.insa.ocm.model.utils.fastforward.condition.ConditionMeasureDynamic;
+import javafx.fxml.FXML;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.TextField;
+import org.jetbrains.annotations.NotNull;
+
+public class ConditionMeasureDynamicView extends ConditionView {
+
+ @FXML private ChoiceBox choiceBoxMeasure = null;
+ @FXML private TextField textFieldValue = null;
+ @FXML private ChoiceBox choiceCategory = null;
+
+
+ public ConditionMeasureDynamicView(@NotNull FastForwardView parent){
+ super(parent);
+ }
+
+ @FXML
+ @Override
+ public void initialize(){
+ super.initialize();
+
+ choiceBoxMeasure.getItems().addAll(Pattern.MeasureType.values());
+ choiceBoxMeasure.setValue(Pattern.MeasureType.FREQUENCY);
+
+ choiceCategory.getItems().addAll(ConditionMeasureDynamic.CatogoryType.values());
+ choiceCategory.setValue(ConditionMeasureDynamic.CatogoryType.HIGHEST);
+ }
+
+ @Override
+ public Condition getCondition() {
+ return new ConditionMeasureDynamic(getActionPatternChoice(), getMeasureType(), getCategoryType(), getIndexValue());
+ }
+
+ //********** Serialization Methods **********//
+
+ public void importCondition(@NotNull Condition condition){
+ super.importCondition(condition);
+ if(condition instanceof ConditionMeasureDynamic){
+ ConditionMeasureDynamic conditionMeDy = (ConditionMeasureDynamic) condition;
+
+ choiceBoxMeasure.setValue(conditionMeDy.getMeasure());
+ textFieldValue.setText(Integer.toString(conditionMeDy.getIndex()));
+ choiceCategory.setValue(conditionMeDy.getCatogory());
+ }
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ private Pattern.MeasureType getMeasureType(){
+ return choiceBoxMeasure.getValue();
+ }
+
+ private ConditionMeasureDynamic.CatogoryType getCategoryType(){
+ return choiceCategory.getValue();
+ }
+
+ private int getIndexValue(){
+ return Integer.valueOf(textFieldValue.getText());
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionMeasureStaticView.java b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionMeasureStaticView.java
new file mode 100644
index 0000000..8fc409e
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionMeasureStaticView.java
@@ -0,0 +1,65 @@
+package fr.insa.ocm.view.misc.parts.fastforward;
+
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.view.mainuis.FastForwardView;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import fr.insa.ocm.model.utils.fastforward.condition.ConditionMeasureStatic;
+import javafx.fxml.FXML;
+import javafx.scene.control.*;
+import org.jetbrains.annotations.NotNull;
+
+public class ConditionMeasureStaticView extends ConditionView{
+
+ @FXML private ChoiceBox choiceBoxMeasure = null;
+ @FXML private ChoiceBox choiceBoxOperator = null;
+ @FXML private TextField textFieldValue = null;
+
+
+ public ConditionMeasureStaticView(@NotNull FastForwardView parent){
+ super(parent);
+ }
+
+ @FXML
+ @Override
+ public void initialize(){
+ super.initialize();
+
+ choiceBoxMeasure.getItems().addAll(Pattern.MeasureType.values());
+ choiceBoxMeasure.setValue(Pattern.MeasureType.FREQUENCY);
+
+ choiceBoxOperator.getItems().addAll(ConditionMeasureStatic.OperatorType.values());
+ choiceBoxOperator.setValue(ConditionMeasureStatic.OperatorType.EQL);
+ }
+
+ @Override
+ public Condition getCondition() {
+ return new ConditionMeasureStatic(getActionPatternChoice(), getSelectedMeasureType(), getSelectedOperatorType(), getThresholdValue());
+ }
+
+ //********** Serialization Methods **********//
+
+ public void importCondition(@NotNull Condition condition){
+ super.importCondition(condition);
+ if(condition instanceof ConditionMeasureStatic){
+ ConditionMeasureStatic conditionMeSt = (ConditionMeasureStatic) condition;
+
+ choiceBoxMeasure.setValue(conditionMeSt.getMeasure());
+ choiceBoxOperator.setValue(conditionMeSt.getOperator());
+ textFieldValue.setText(Double.toString(conditionMeSt.getThresholdValue()));
+ }
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ private ConditionMeasureStatic.OperatorType getSelectedOperatorType(){
+ return choiceBoxOperator.getValue();
+ }
+
+ private Pattern.MeasureType getSelectedMeasureType(){
+ return choiceBoxMeasure.getValue();
+ }
+
+ private double getThresholdValue(){
+ return Double.valueOf(textFieldValue.getText());
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionView.java b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionView.java
new file mode 100644
index 0000000..97a67c3
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/fastforward/ConditionView.java
@@ -0,0 +1,73 @@
+package fr.insa.ocm.view.misc.parts.fastforward;
+
+import fr.insa.ocm.view.mainuis.FastForwardView;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.layout.HBox;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class ConditionView {
+
+ @FXML protected HBox mainHBox = null;
+ @FXML protected ToggleButton buttonAlwaysKeep = null;
+ @FXML protected ToggleButton buttonAlwaysTrash = null;
+ @FXML protected Button buttonRemoveCondition = null;
+
+ private FastForwardView parent;
+
+ ConditionView(@NotNull FastForwardView parent){
+ this.parent = parent;
+ }
+
+ public HBox getMainHBox(){ return mainHBox; }
+
+ @FXML
+ public void initialize(){
+ ToggleGroup toggleGroup = new ToggleGroup();
+ buttonAlwaysKeep.setToggleGroup(toggleGroup);
+ buttonAlwaysTrash.setToggleGroup(toggleGroup);
+
+ buttonAlwaysKeep.getStyleClass().addAll("buttonKeep");
+ buttonAlwaysTrash.getStyleClass().addAll("buttonTrash");
+
+ buttonRemoveCondition.setOnAction(event -> parent.buttonRemoveConditionPressed(mainHBox));
+ }
+
+ Condition.ActionPatternChoice getActionPatternChoice(){
+ Condition.ActionPatternChoice actionPatternChoice = Condition.ActionPatternChoice.NEUTRAL;
+ if(buttonAlwaysKeep.isSelected()){
+ actionPatternChoice = Condition.ActionPatternChoice.KEEP;
+ }else if(buttonAlwaysTrash.isSelected()){
+ actionPatternChoice = Condition.ActionPatternChoice.TRASH;
+ }
+
+ return actionPatternChoice;
+ }
+
+ public abstract Condition getCondition();
+
+ @Override
+ public boolean equals(Object obj){
+ if(obj instanceof ConditionView){
+ ConditionView conditionView = (ConditionView) obj;
+ return conditionView.mainHBox.equals(this.mainHBox);
+ }
+ return false;
+ }
+
+ //********** Serialization Methods **********//
+
+ public void importCondition(@NotNull Condition condition){
+ switch (condition.getActionPatternChoice()){
+ case KEEP:
+ buttonAlwaysKeep.setSelected(true);
+ break;
+ case TRASH:
+ buttonAlwaysTrash.setSelected(true);
+ break;
+ }
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/mining/PatternView.java b/src/main/java/fr/insa/ocm/view/misc/parts/mining/PatternView.java
new file mode 100644
index 0000000..f697fa7
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/mining/PatternView.java
@@ -0,0 +1,160 @@
+package fr.insa.ocm.view.misc.parts.mining;
+
+import com.sun.istack.internal.NotNull;
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.utils.Vector;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.layout.HBox;
+
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PatternView {
+
+ @FXML private HBox mainHBox = null;
+
+ @FXML private ToggleButton buttonKeep = null;
+ @FXML private ToggleButton buttonTrash = null;
+
+ @FXML private Label labelPattern = null;
+ @FXML private Label labelExploitation = null;
+ @FXML private Label labelIndicator = null;
+
+ private Pattern pattern;
+ private int index;
+
+ public PatternView(@NotNull Pattern pattern, int index){
+ this.pattern = pattern;
+ this.index = index;
+
+ FXMLLoader loaderPattern = new FXMLLoader(getClass().getResource("/fxml/misc/parts/mining/patternView.fxml"));
+ loaderPattern.setController(this);
+
+ try {
+ loaderPattern.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @FXML
+ public void initialize(){
+ ToggleGroup toggleGroup = new ToggleGroup();
+
+ // Set if it is exploitation or exploration pattern
+ if(isExploitation()) {
+ labelExploitation.setText("Exploitation");
+ labelExploitation.getStyleClass().addAll("exploitation");
+
+ labelIndicator.getStyleClass().addAll("exploitation");
+ } else {
+ labelExploitation.setText("Exploration");
+ labelExploitation.getStyleClass().addAll("exploration");
+
+ labelIndicator.getStyleClass().addAll("exploration");
+ }
+
+ labelIndicator.setText(getMostUsefulWeightName());
+
+ // Set the style of the buttons
+ buttonKeep.getStyleClass().addAll("buttonKeep");
+ buttonTrash.getStyleClass().addAll("buttonTrash");
+
+ buttonKeep.setToggleGroup(toggleGroup);
+ buttonTrash.setToggleGroup(toggleGroup);
+
+ labelPattern.setText(pattern.toString());
+ }
+
+ private boolean isExploitation(){
+ int indexAlgoName = OCMManager.algorithmLauncherGetListAlgorithmName().indexOf(pattern.getAlgorithmName());
+ double[] weights = OCMManager.banditGetWeights();
+
+ // Determine if the pattern is from Exploitation or Exploration.
+ boolean isExploitation = true;
+ for(int i = 0; i < weights.length; ++i){
+ if(i != indexAlgoName){
+ isExploitation = isExploitation && (weights[indexAlgoName] > weights[i]);
+ }
+ }
+
+ return isExploitation;
+ }
+
+ private String getMostUsefulWeightName(){
+ String weightName;
+ double[] weightsCoactive = OCMManager.coactiveGetWeights();
+ double[] weightsPattern = pattern.getAttributesVector().getValues();
+ double[] weightsCartesian = new double[weightsCoactive.length];
+
+ if(weightsPattern.length == weightsCoactive.length){
+ for(int i = 0; i < weightsCoactive.length; ++i){
+ weightsCartesian[i] = (weightsPattern[i] != 0d) ? weightsCoactive[i] : 0d;
+ }
+ } else {
+ DebugLogger.printDebug("PatternView: Unable to match the weights of the Coactive Learning and the weight of the Pattern", DebugLogger.MessageSeverity.HIGH);
+ }
+
+ int index = 0;
+ double max = weightsCartesian[index];
+ for(int i = 1; i < weightsCartesian.length; ++i){
+ if (weightsCartesian[i] > max){
+ max = weightsCartesian[i];
+ index = i;
+ }
+ }
+
+ if (index < Pattern.MeasureType.values().length){
+ weightName = Pattern.MeasureType.getName(index).toString();
+ } else {
+ List listWeightName = new ArrayList<>();
+ for (Pattern.MeasureType measure : Pattern.MeasureType.values()) {
+ listWeightName.add(measure.toString());
+ }
+ listWeightName.addAll(OCMManager.algorithmLauncherGetListAttributeName());
+ listWeightName.addAll(OCMManager.algorithmLauncherGetListAlgorithmName());
+ weightName = listWeightName.get(index);
+ }
+
+ return weightName;
+ }
+
+ //********** Getters/Setters Methods **********//
+
+ // Getters //
+
+ public HBox getMainHBox(){
+ return mainHBox;
+ }
+
+ public boolean isKept(){
+ return buttonKeep.isSelected();
+ }
+
+ public boolean isTrashed(){
+ return buttonTrash.isSelected();
+ }
+
+ public int getIndex(){
+ return index;
+ }
+
+ // Setters //
+
+ public void setIsKept(){
+ buttonKeep.setSelected(true);
+ }
+
+ public void setIsTrashed(){
+ buttonTrash.setSelected(true);
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/settings/library/SettingsRealKDView.java b/src/main/java/fr/insa/ocm/view/misc/parts/settings/library/SettingsRealKDView.java
new file mode 100644
index 0000000..edc8ac8
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/settings/library/SettingsRealKDView.java
@@ -0,0 +1,32 @@
+package fr.insa.ocm.view.misc.parts.settings.library;
+
+
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.layout.VBox;
+
+import java.io.IOException;
+
+public class SettingsRealKDView {
+
+ @FXML private VBox mainVBox = null;
+
+ public SettingsRealKDView(){
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/parts/settings/library/settingsRealKDView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ //********** Getters/Setters methods **********//
+
+ // Getters //
+
+ public VBox getMainVBox(){
+ return mainVBox;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/settings/library/SettingsSPMFView.java b/src/main/java/fr/insa/ocm/view/misc/parts/settings/library/SettingsSPMFView.java
new file mode 100644
index 0000000..d91e8e5
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/settings/library/SettingsSPMFView.java
@@ -0,0 +1,125 @@
+package fr.insa.ocm.view.misc.parts.settings.library;
+
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.wrapper.spmf.AlgorithmLauncherSPMF;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.VBox;
+
+import java.io.IOException;
+
+public class SettingsSPMFView {
+
+ @FXML private VBox mainVBox = null;
+
+ @FXML private TextField minsupLCM = null;
+ @FXML private TextField minsupBIDEPlus = null;
+ @FXML private TextField minsupVMSP = null;
+ @FXML private TextField maxgapVMSP = null;
+ @FXML private TextField minsupPFPM = null;
+ @FXML private TextField minPeriodicity = null;
+ @FXML private TextField maxPeriodicity = null;
+
+ public SettingsSPMFView(){
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/parts/settings/library/settingsSPMFView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ //********** Initialization methods **********//
+
+ @FXML
+ public void initialize(){
+ minsupLCM.setText(AlgorithmLauncherSPMF.getMinsupLCM() + "");
+ minsupBIDEPlus.setText(AlgorithmLauncherSPMF.getMinsupBIDEPlus() + "");
+ minsupVMSP.setText(AlgorithmLauncherSPMF.getMinsupVMSP() + "");
+ maxgapVMSP.setText(AlgorithmLauncherSPMF.getMaxgapVMSP() + "");
+ minsupPFPM.setText(AlgorithmLauncherSPMF.getMinsupPFPM() + "");
+ minPeriodicity.setText(AlgorithmLauncherSPMF.getMinPeriodicityPFPM() + "");
+ maxPeriodicity.setText(AlgorithmLauncherSPMF.getMaxPeriodicityPFPM() + "");
+ }
+
+ //********** Getters/Setters methods **********//
+
+ // Getters //
+
+ public VBox getMainVBox(){
+ return mainVBox;
+ }
+
+ public void setSettingsAlgorithmLauncher(){
+ this.setMinsupLCMValue();
+ this.setMinsupBIDEPlusValue();
+ this.setMinsupVMSPValue();
+ this.setMaxgapVMSPValue();
+ this.setMinsupPFPMValue();
+ this.setMinPeriodicityValue();
+ this.setMaxPeriodicityValue();
+ }
+
+ //********** Internal methods **********//
+
+ private void setMinsupLCMValue(){
+ try{
+ AlgorithmLauncherSPMF.setMinsupLCM(Double.valueOf(minsupLCM.getText()));
+ } catch (NumberFormatException e){
+ DebugLogger.printDebug("SettingsSPMFView: Impossible to get the value for the minsupLSM setting", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ private void setMinsupBIDEPlusValue(){
+ try{
+ AlgorithmLauncherSPMF.setMinsupBIDEPlus(Double.valueOf(minsupBIDEPlus.getText()));
+ } catch (NumberFormatException e){
+ DebugLogger.printDebug("SettingsSPMFView: Impossible to get the value for the minsupBIDEPlus setting", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ private void setMinsupVMSPValue(){
+ try{
+ AlgorithmLauncherSPMF.setMinsupVMSP(Double.valueOf(minsupVMSP.getText()));
+ } catch (NumberFormatException e){
+ DebugLogger.printDebug("SettingsSPMFView: Impossible to get the value for the minsupVMSP setting", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ private void setMaxgapVMSPValue(){
+ try{
+ AlgorithmLauncherSPMF.setMaxgapVMSP(Integer.valueOf(maxgapVMSP.getText()));
+ } catch (NumberFormatException e){
+ DebugLogger.printDebug("SettingsSPMFView: Impossible to get the value for the maxgapVMSP setting", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ private void setMinsupPFPMValue(){
+ try{
+ AlgorithmLauncherSPMF.setMinsupPFPM(Double.valueOf(minsupPFPM.getText()));
+ } catch (NumberFormatException e){
+ DebugLogger.printDebug("SettingsSPMFView: Impossible to get the value for the minsupPFPM setting", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ private void setMinPeriodicityValue(){
+ try{
+ AlgorithmLauncherSPMF.setMinPeriodicityPFPM(Integer.valueOf(minPeriodicity.getText()));
+ } catch (NumberFormatException e){
+ DebugLogger.printDebug("SettingsSPMFView: Impossible to get the value for the minPeriodicity setting", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ private void setMaxPeriodicityValue(){
+ try{
+ AlgorithmLauncherSPMF.setMaxPeriodicityPFPM(Integer.valueOf(maxPeriodicity.getText()));
+ } catch (NumberFormatException e){
+ DebugLogger.printDebug("SettingsSPMFView: Impossible to get the value for the maxPeriodicity setting", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/view/misc/parts/settings/pattern/SettingsPatternView.java b/src/main/java/fr/insa/ocm/view/misc/parts/settings/pattern/SettingsPatternView.java
new file mode 100644
index 0000000..3814102
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/view/misc/parts/settings/pattern/SettingsPatternView.java
@@ -0,0 +1,53 @@
+package fr.insa.ocm.view.misc.parts.settings.pattern;
+
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.model.wrapper.spmf.AlgorithmLauncherSPMF;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.control.CheckBox;
+import javafx.scene.layout.HBox;
+
+import java.io.IOException;
+
+public class SettingsPatternView {
+
+ @FXML private HBox mainHBox = null;
+ @FXML private CheckBox checkBox = null;
+
+ private Pattern.MeasureType measure;
+
+ public SettingsPatternView(Pattern.MeasureType measure){
+ this.measure = measure;
+
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/misc/parts/settings/pattern/settingsPatternView.fxml"));
+ fxmlLoader.setController(this);
+
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ //********** Initialization methods **********//
+
+ @FXML
+ public void initialize(){
+ checkBox.setText(measure.toString());
+
+ checkBox.setSelected(AlgorithmLauncherSPMF.getMeasurePatternDescriptor(measure.getIndex()));
+ }
+
+ //********** Getters/Setters methods **********//
+
+ // Getters //
+
+ public HBox getMainHBox(){
+ return mainHBox;
+ }
+
+ public void setMeasureChoice(){
+ AlgorithmLauncherSPMF.setMeasuresPatternDescriptor(checkBox.isSelected(), measure.getIndex());
+ }
+
+}
diff --git a/src/main/java/fr/insa/ocm/viewmodel/InfoAlgorithm.java b/src/main/java/fr/insa/ocm/viewmodel/InfoAlgorithm.java
new file mode 100644
index 0000000..e724955
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/viewmodel/InfoAlgorithm.java
@@ -0,0 +1,82 @@
+package fr.insa.ocm.viewmodel;
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.wrapper.api.AbstractAlgorithmLauncher;
+import fr.insa.ocm.view.mainuis.MainView;
+import javafx.application.Platform;
+import javafx.beans.property.SimpleStringProperty;
+
+import java.util.List;
+import java.util.Map;
+
+public class InfoAlgorithm extends Thread {
+
+ private static InfoAlgorithm INSTANCE = new InfoAlgorithm();
+
+ private volatile boolean stopRequested;
+ private volatile boolean paused;
+
+ private InfoAlgorithm(){
+ INSTANCE = this;
+
+
+ stopRequested = false;
+ paused = true;
+
+ INSTANCE.start();
+ }
+
+ @Override
+ public void run(){
+ Thread.currentThread().setName("Information Gatherer");
+
+ try {
+ DebugLogger.initializeBanditLog();
+ DebugLogger.initializeCoactiveLog();
+ DebugLogger.initializeAlgorithmLog();
+ DebugLogger.initializeSelectionDistributionLog();
+ } catch (NullPointerException e){
+ DebugLogger.printDebug("InfoAlgorithm: Unable to initialize the bandit or the coactive logger.", DebugLogger.MessageSeverity.MEDIUM);
+ }
+
+ while(!stopRequested){
+ try {
+ changeInfoLabel();
+ DebugLogger.logBandit();
+ DebugLogger.logCoactive();
+ DebugLogger.logAlgorithm();
+ } catch (NullPointerException exception){
+ DebugLogger.printDebug("InfoAlgorithm: Unable to have the informations on the Algorithm Launcher.", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void changeInfoLabel(){
+ MainView mainView = MainView.getCurrentMainView();
+
+ int nbAlgoLaunched = OCMManager.algorithmManagerGetNbAlgoLaunched();
+ int nbPatternFound = OCMManager.algorithmManagerGetNbPatternFound();
+ String status = paused ? "Paused" : "Mining";
+
+ Platform.runLater(() -> mainView.getLabelInfo().setText(
+ "Mining Algorithm status : "+ status + "\n\n" +
+ "Since the last mining round :\n "+
+ nbAlgoLaunched+" algorithms have been launched\n "+
+ nbPatternFound+" patterns have been found"));
+ }
+
+
+ static void requestStop(){
+ INSTANCE.stopRequested = true;
+ }
+
+ public static void setPaused(boolean isPaused){
+ INSTANCE.paused = isPaused;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/viewmodel/Monitor.java b/src/main/java/fr/insa/ocm/viewmodel/Monitor.java
new file mode 100644
index 0000000..87e99a9
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/viewmodel/Monitor.java
@@ -0,0 +1,142 @@
+package fr.insa.ocm.viewmodel;
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.wrapper.api.AbstractAlgorithmLauncher;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import javafx.application.Platform;
+import javafx.beans.property.SimpleStringProperty;
+import org.jetbrains.annotations.NotNull;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class Monitor extends Thread {
+
+ private static Monitor INSTANCE;
+
+ // Monitor some stats about the launched algorithms
+ private SimpleStringProperty monitorAlgorithmsLaunched;
+ private SimpleStringProperty monitorBanditWeight;
+ private SimpleStringProperty monitorCoactiveWeight;
+
+ private static boolean stopRequested = false;
+ private static boolean paused = false;
+
+ private Monitor() {
+ monitorAlgorithmsLaunched = new SimpleStringProperty("");
+ monitorBanditWeight = new SimpleStringProperty("");
+ monitorCoactiveWeight = new SimpleStringProperty("");
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (!stopRequested) {
+ if(!paused) {
+ monitorAlgorithmCalls();
+ monitorBanditWeight();
+ monitorCoactiveWeights();
+ }
+
+ DebugLogger.printDebug("Monitor: Stats refreshed.");
+ Thread.sleep(1000);
+ }
+ } catch (InterruptedException e){
+ e.printStackTrace();
+ }
+ }
+
+ private void monitorAlgorithmCalls() {
+ final Map mapStatsAlgorithmsLaunched = AbstractAlgorithmLauncher.getStatsAlgorithmsLaunched();
+ List listAlgorithmUsed = OCMManager.algorithmLauncherGetListAlgorithmName();
+ StringBuilder stringBuilder = new StringBuilder();
+ for(String algorithmName : mapStatsAlgorithmsLaunched.keySet()){
+ if(listAlgorithmUsed.contains(algorithmName)) {
+ stringBuilder.append("The algorithm named: ");
+ stringBuilder.append(algorithmName);
+ stringBuilder.append(" has been launched ");
+ stringBuilder.append(mapStatsAlgorithmsLaunched.get(algorithmName));
+ stringBuilder.append(" times.\n");
+ }
+ }
+
+ Platform.runLater(() -> monitorAlgorithmsLaunched.set(stringBuilder.toString()));
+ }
+
+ private void monitorBanditWeight() {
+ double[] banditWeights = OCMManager.banditGetWeights();
+ List algorithmNames = OCMManager.algorithmLauncherGetListAlgorithmName();
+
+ if (banditWeights.length == algorithmNames.size()) {
+ Platform.runLater(() -> monitorBanditWeight.set(makeText(banditWeights, algorithmNames)));
+ } else {
+ DebugLogger.printDebug("Monitor: Unable to match the size of the bandit weights with the number of algorithms", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ private void monitorCoactiveWeights() {
+ double[] coactiveWeights = OCMManager.coactiveGetWeights();
+ List names = new ArrayList<>();
+
+ for(Pattern.MeasureType measure : Pattern.MeasureType.values()){
+ names.add(measure.toString());
+ }
+ names.addAll(OCMManager.algorithmLauncherGetListAttributeName());
+ names.addAll(OCMManager.algorithmLauncherGetListAlgorithmName());
+
+ if(coactiveWeights.length == names.size()){
+ Platform.runLater(() -> monitorCoactiveWeight.set(makeText(coactiveWeights, names)));
+ } else {
+ DebugLogger.printDebug("Monitor: Unable to match the size of the coactive weights and the names associated", DebugLogger.MessageSeverity.MEDIUM);
+ }
+ }
+
+ @NotNull
+ private String makeText(double[] weights, List names){
+ StringBuilder strBuild = new StringBuilder("");
+ DecimalFormat decimalFormat = new DecimalFormat("#0.0000");
+
+ for(int i = 0; i < weights.length; ++i){
+ strBuild.append(names.get(i));
+ strBuild.append(" has a coefficient of ");
+ strBuild.append(decimalFormat.format(weights[i]));
+ strBuild.append(".\n");
+ }
+
+ return strBuild.toString();
+ }
+
+ public static void startMonitoring(){
+ INSTANCE = new Monitor();
+ INSTANCE.setDaemon(true);
+ INSTANCE.setName("Monitor");
+ INSTANCE.start();
+ }
+
+ public static void pauseMonitoring(){
+ paused = true;
+ }
+
+ public static void resumeMonitoring(){
+ paused = false;
+ }
+
+ public static void requestStop(){
+ stopRequested = true;
+ }
+
+ public static SimpleStringProperty getMonitorAlgorithmsLaunched(){
+ return INSTANCE.monitorAlgorithmsLaunched;
+ }
+
+ public static SimpleStringProperty getMonitorBanditWeights(){
+ return INSTANCE.monitorBanditWeight;
+ }
+
+ public static SimpleStringProperty getMonitorCoactiveWeights(){
+ return INSTANCE.monitorCoactiveWeight;
+ }
+}
diff --git a/src/main/java/fr/insa/ocm/viewmodel/OCLController.java b/src/main/java/fr/insa/ocm/viewmodel/OCLController.java
new file mode 100644
index 0000000..4ac2ce3
--- /dev/null
+++ b/src/main/java/fr/insa/ocm/viewmodel/OCLController.java
@@ -0,0 +1,320 @@
+package fr.insa.ocm.viewmodel;
+
+import fr.insa.ocm.model.DebugLogger;
+import fr.insa.ocm.model.OCMManager;
+import fr.insa.ocm.model.oneclicklearning.cache.api.Cache;
+import fr.insa.ocm.model.oneclicklearning.coactivelearning.api.CoactiveLearning;
+import fr.insa.ocm.model.utils.fastforward.FastForward;
+import fr.insa.ocm.model.wrapper.api.Pattern;
+import fr.insa.ocm.view.misc.FastForwardWaitingView;
+import fr.insa.ocm.model.utils.fastforward.condition.Condition;
+import javafx.application.Platform;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class OCLController {
+
+ public static final String VERSION = "0.5.0.1";
+
+ private static OCLController INSTANCE = new OCLController();
+
+ private volatile boolean stopRequested = false;
+
+ private List interestingPattern;
+ private List neutralPattern;
+ private List trashedPattern;
+
+ private String pathDataFile = null;
+
+ // Keeps a track of the last fastforward done.
+ private FastForward fastForward;
+
+ // Settings of the controller
+ private Pattern.WrapperType wrapperTypeSelected;
+ private CoactiveLearning.CoactiveType coactiveLearningSelected;
+ private Cache.CacheType cacheTypeSelected;
+
+ //This constructor is valid only if used by the Application launcher
+ private OCLController(){
+ INSTANCE = this;
+
+ interestingPattern = new ArrayList<>();
+ neutralPattern = new ArrayList<>();
+ trashedPattern = new ArrayList<>();
+
+ // Initializing default settings
+ wrapperTypeSelected = Pattern.WrapperType.SPMF;
+ coactiveLearningSelected = CoactiveLearning.CoactiveType.SET;
+ cacheTypeSelected = Cache.CacheType.SET;
+ }
+
+ //********** Initializing Methods **********//
+
+ public static void initialize(String pathDataFile){
+ DebugLogger.printDebug("OCLController: Initializing.");
+ OCMManager.initialize(INSTANCE.wrapperTypeSelected, pathDataFile);
+ OCMManager.algorithmManagerStartMining();
+
+ INSTANCE.pathDataFile = pathDataFile;
+
+ InfoAlgorithm.setPaused(false);
+ Monitor.startMonitoring();
+
+ INSTANCE.waitForOCMResults();
+
+ DebugLogger.printDebug("OCLController: initialized correctly.");
+ }
+
+ public static void reload(String pathDataFile){
+ DebugLogger.printDebug("OCLController: Reloading OCLController.");
+ Monitor.pauseMonitoring();
+ OCMManager.requestStop();
+
+ INSTANCE.stopRequested = false;
+
+ INSTANCE.interestingPattern = new ArrayList<>();
+ INSTANCE.neutralPattern = new ArrayList<>();
+ INSTANCE.trashedPattern = new ArrayList<>();
+
+ INSTANCE.pathDataFile = pathDataFile;
+
+ DebugLogger.printDebug("OCLController: Reloading OCMManager.");
+ OCMManager.reload(INSTANCE.wrapperTypeSelected, INSTANCE.cacheTypeSelected, INSTANCE.coactiveLearningSelected, pathDataFile);
+ OCMManager.algorithmManagerStartMining();
+
+ Monitor.resumeMonitoring();
+
+ INSTANCE.waitForOCMResults();
+
+ DebugLogger.printDebug("OCLController: Reloaded correctly.");
+ }
+
+ //********** Public Methods **********//
+
+ public static void exportInterestingPatterns(String pathFile){
+ List patterns = OCMManager.patternWarehouseGetPatterns();
+
+ BufferedWriter writerTXT = null;
+ try {
+ writerTXT = new BufferedWriter(new FileWriter(new File(pathFile)));
+ writerTXT.write("Interesting patterns:");
+ writerTXT.newLine();
+ writerTXT.write("---------------------");
+ writerTXT.newLine();
+
+ for(Pattern p : patterns){
+ String toWrite = p.toString();
+ writerTXT.write(toWrite);
+ writerTXT.newLine();
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if(writerTXT != null){
+ try {
+ writerTXT.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ //********** Fast Forward Methods **********//
+
+ public static void fastForward(int numberRounds, double numberSecPerRound, List listConditions,
+ FastForwardWaitingView fastForwardWaitingView,
+ Condition.ActionPatternChoice conditionPriority){
+
+ FastForward fastForward = new FastForward(numberRounds, numberSecPerRound,
+ listConditions,
+ conditionPriority,
+ INSTANCE.interestingPattern,
+ INSTANCE.neutralPattern,
+ INSTANCE.trashedPattern);
+
+ INSTANCE.fastForward = fastForward;
+
+ final StringProperty currentOperation = new SimpleStringProperty("");
+ final StringProperty remainingTime = new SimpleStringProperty("");
+ final DoubleProperty progressLearning = new SimpleDoubleProperty(0d);
+ final DoubleProperty progressMining = new SimpleDoubleProperty(0d);
+
+ Platform.runLater(() -> {
+ fastForwardWaitingView.bindCurrentOperation(currentOperation);
+ fastForwardWaitingView.bindCurrentTime(remainingTime);
+ fastForwardWaitingView.bindProgressLearning(progressLearning);
+ fastForwardWaitingView.bindProgressMining(progressMining);
+ });
+
+ Thread threadFF = new Thread(fastForward);
+ threadFF.setName("FastForward");
+ threadFF.setDaemon(true);
+ threadFF.start();
+
+ try {
+ while (!fastForward.isFinished()) {
+ String strCurrentOperation = fastForward.getCurrentOperation();
+ String strRemainingTime = fastForward.getRemainingTime();
+ Double dProgressLearning = fastForward.getProgressLearning();
+ Double dProgressMining = fastForward.getProgressMining();
+
+ Platform.runLater(() -> {
+ currentOperation.setValue("Current Operation: "+ strCurrentOperation + ".");
+ remainingTime.setValue("Estimated time before completion: "+ strRemainingTime);
+ progressLearning.setValue(dProgressLearning);
+ progressMining.setValue(dProgressMining);
+ });
+ Thread.sleep(100);
+ }
+ } catch (InterruptedException e){
+ e.printStackTrace();
+ }
+
+// final int nbIter = 100;
+// try {
+// for (int i = 0; i < nbRound && !INSTANCE.stopRequested && !INSTANCE.ffStopRequested; ++i) {
+// fastForwardWaitingView.setCurrentOperation("Mining");
+// for (int j = 0; j < nbIter && !INSTANCE.stopRequested && !INSTANCE.ffStopRequested; ++j) {
+// Thread.sleep((long)secPerRound*1000/nbIter);
+// double pbm = (j+1.)/nbIter;
+// fastForwardWaitingView.setProgressBarMining(pbm);
+//
+// int remainingTimeSec = (int)((nbRound-i) * secPerRound - (secPerRound/nbIter) * j);
+// fastForwardWaitingView.setCurrentTime(remainingTimeSec/60 + " min " + remainingTimeSec%60 + " sec");
+// }
+//
+// // We process the data gathered by the mining algorithms.
+// fastForwardWaitingView.setCurrentOperation("Processing data");
+//
+//
+//
+// Rank