/* *Copyright 2007, 2011 CCLS Columbia University (USA), LIFO University of Orleans (France), BRGM (France) * *Authors: Cyril Nortet, Xiangrong Kong, Ansaf Salleb-Aouissi, Christel Vrain, Daniel Cassard * *This file is part of QuantMiner. * *QuantMiner is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. * *QuantMiner is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * *You should have received a copy of the GNU General Public License along with QuantMiner. If not, see <http://www.gnu.org/licenses/>. */ package src.solver; import java.util.*; import src.apriori.*; import src.database.*; import src.geneticAlgorithm.*; public class RuleTester extends Thread { //test rules // Classe permettant l'execution d'un code particulier durant le calcul des regles : // Class allowing to execute a particular code during the cumputation of rules: static public class IndicateurCalculRegles { public void IndiquerFinCalcul() { } public void EnvoyerInfo(String sNouvelleInfo) { } public void IndiquerNombreReglesATester(int iNombreReglesATester) { } } // Definition d'un traitement specifique a inserer au cours de l'execution de l'algorithme Apriori : // Definition of a specific treatment to do during the apriori algorithm execution public class TraitementPendantCalculFrequents extends AprioriQuantitative.TraitementExternePendantCalcul { public boolean ExecuterTraitementExterne() { try { sleep(0); } catch (InterruptedException e) { } return m_bEnExecution; } } private ResolutionContext m_contexteResolution = null; private RuleOptimizer m_optimiseurCourant = null; // Object containing the method of optimizing rules public boolean m_bEnExecution = false; private boolean m_bResultatDisponible = false; // Indique qu'un ensemble de regles vient d'etre calcule en totalite // Indicate that a set of rule are completely calculated AprioriQuantitative m_apriori = null; int m_iNombreTotalAttributsQuant = 0; int m_iNombreItemsQuantConsideres = 0; private int m_iMinimumItemsQuantConsideres = 0; //minimum # of quantitative attributes in a rule private int m_iMaximumItemsQuantConsideres = 0; //maximum # of quantitative attributes in a rule int m_iTailleFrequent = 0; //size of a frequent item set int m_iIndiceFrequent = 0; int m_iIndiceCombinaisonItemsQuant = 0; int m_iIndiceRepartitionItems = 0; public boolean m_bFinTestRegles = false; public int m_iNombreReglesTestees = 0; // Compteur du nombre de regles deja testees par l'algorithme -- count the number of rules already tested by the algorithm ArrayList m_listeRegles = null; private ArrayList m_listeAttributsQuant = null; private IndicateurCalculRegles m_indicateurCalcul = null; private boolean m_bIndiquerFinCalcul = false; private boolean m_bModeSpecialComptabilisationRegles = false; private int m_iNombreReglesComptabilisees = 0; float m_fMinSupp = 0.0f; float m_fMinConf = 0.0f; int m_iNombreDisjonctionsGauche = 0; int m_iNombreDisjonctionsDroite = 0; public RuleTester(ResolutionContext contexteResolution, IndicateurCalculRegles indicateurCalcul) { m_contexteResolution = contexteResolution; m_indicateurCalcul = indicateurCalcul; m_bIndiquerFinCalcul = true; m_bEnExecution = false; m_bResultatDisponible = false; m_iNombreReglesTestees = 0; m_bModeSpecialComptabilisationRegles = false; m_iNombreReglesComptabilisees = 0; // Pre-calculate a l'aide de l'algorithme Apriori standard : // pre-calculate with apriori standard algorithm if (m_contexteResolution == null) return; switch (m_contexteResolution.m_iTechniqueResolution) { case ResolutionContext.TECHNIQUE_APRIORI_QUAL : m_fMinSupp = m_contexteResolution.m_parametresRegles.m_fMinSupp; m_fMinConf = m_contexteResolution.m_parametresRegles.m_fMinConf; m_iMinimumItemsQuantConsideres = 0; m_iMaximumItemsQuantConsideres = 0; m_iNombreDisjonctionsGauche = 1; //Disjunctions 'OR' m_iNombreDisjonctionsDroite = 1; break; case ResolutionContext.TECHNIQUE_ALGO_GENETIQUE : m_fMinSupp = m_contexteResolution.m_parametresReglesQuantitatives.m_fMinSupp; m_fMinConf = m_contexteResolution.m_parametresReglesQuantitatives.m_fMinConf; m_iNombreDisjonctionsGauche = m_contexteResolution.m_parametresReglesQuantitatives.m_iNombreDisjonctionsGauche; m_iNombreDisjonctionsDroite = m_contexteResolution.m_parametresReglesQuantitatives.m_iNombreDisjonctionsDroite; m_iMinimumItemsQuantConsideres = m_contexteResolution.m_parametresReglesQuantitatives.m_iNombreMinAttributsQuant; m_iMaximumItemsQuantConsideres = m_contexteResolution.m_parametresReglesQuantitatives.m_iNombreMaxAttributsQuant; break; case ResolutionContext.TECHNIQUE_RECUIT_SIMULE : m_fMinSupp = m_contexteResolution.m_parametresReglesQuantitatives.m_fMinSupp; m_fMinConf = m_contexteResolution.m_parametresReglesQuantitatives.m_fMinConf; m_iNombreDisjonctionsGauche = m_contexteResolution.m_parametresReglesQuantitatives.m_iNombreDisjonctionsGauche; m_iNombreDisjonctionsDroite = m_contexteResolution.m_parametresReglesQuantitatives.m_iNombreDisjonctionsDroite; m_iMinimumItemsQuantConsideres = m_contexteResolution.m_parametresReglesQuantitatives.m_iNombreMinAttributsQuant; m_iMaximumItemsQuantConsideres = m_contexteResolution.m_parametresReglesQuantitatives.m_iNombreMaxAttributsQuant; break; default : return; } if (m_contexteResolution.m_gestionnaireBD == null) return; // Mise en place d'une nouvelle execution de l'algorithme Arpriori, repertoriee dans le contexte d'execution : // Setting for a new execution of apriori defined in the context of execution: m_contexteResolution.m_aprioriCourant = null; m_apriori = new AprioriQuantitative(m_contexteResolution); m_contexteResolution.m_aprioriCourant = m_apriori; m_apriori.SpecifierSupportMinimal( m_fMinSupp ); m_apriori.SpecifierTraitementExterne(new TraitementPendantCalculFrequents()); m_apriori.ExecuterPretraitement(true); //execute pre-process } public void AutoriserIndicationFinCalcul(boolean bIndiquerFinCalcul) { m_bIndiquerFinCalcul = bIndiquerFinCalcul; } //Define the optimizer(e.g. Genetic algorithm) used to optimize rules public void DefinirOptimiseurRegle(RuleOptimizer optimiseur) { if (optimiseur != null) { m_optimiseurCourant = optimiseur; m_optimiseurCourant.DefinirContexteResolution(m_contexteResolution); } } //start to do optimization public void run() { int iTypePriseEnCompte = 0; int iIndiceAttributsQuant = 0; int iNombreAttributsQuantBase = 0; AttributQuantitative attributQuant = null; boolean bFinInitApriori = false; int iTailleFrequents = 0; int iNombreReglesATester = 0; m_iNombreReglesTestees = 0; m_bResultatDisponible = false; m_listeRegles = new ArrayList(); if (m_apriori == null) return; m_bEnExecution = true; bFinInitApriori = false; iTailleFrequents = 2; if (m_indicateurCalcul != null) m_indicateurCalcul.EnvoyerInfo("Pre-computation with the Apriori algorithm:\n"); try { while ( (m_bEnExecution) && (!bFinInitApriori) && ( iTailleFrequents <= 3)) { // suppress later the third condition // On genere les listes de K-items frequency successively: // We generate the list of the frequency of K-items successively: if (m_indicateurCalcul != null) m_indicateurCalcul.EnvoyerInfo(" Computing the set of "+String.valueOf(iTailleFrequents)+" consecutive frequent modalities..."); bFinInitApriori = !m_apriori.GenererNouvelleListeItemSets(); if (m_indicateurCalcul != null) { if (bFinInitApriori) m_indicateurCalcul.EnvoyerInfo("FINISH!\n"); else m_indicateurCalcul.EnvoyerInfo("OK\n"); } iTailleFrequents++; if (bFinInitApriori || ( iTailleFrequents > 3) ) { // suppress later the second condition m_apriori.ElaguerItemsetsSelonFiltre(); // On repertorie uniquement les attributs quantitatifs a prendre en compte : // we list only the quantitative attributes to consider: m_listeAttributsQuant = new ArrayList(); m_iNombreTotalAttributsQuant = 0; if (m_iMaximumItemsQuantConsideres > 0) { iNombreAttributsQuantBase = m_apriori.ObtenirNombreAttributsQuantitatifs(); for (iIndiceAttributsQuant = 0; iIndiceAttributsQuant < iNombreAttributsQuantBase; iIndiceAttributsQuant++) { attributQuant = m_apriori.ObtenirAttributQuantitatif(iIndiceAttributsQuant); if (attributQuant != null) { iTypePriseEnCompte = m_contexteResolution.ObtenirTypePrisEnCompteAttribut(attributQuant.ObtenirNom()); if (iTypePriseEnCompte != ResolutionContext.PRISE_EN_COMPTE_ITEM_NULLE_PART) { m_listeAttributsQuant.add(attributQuant); m_iNombreTotalAttributsQuant++; } } } if (m_iMaximumItemsQuantConsideres > m_iNombreTotalAttributsQuant) m_iMaximumItemsQuantConsideres = m_iNombreTotalAttributsQuant; } // Calculate the number of rules to test: if (m_indicateurCalcul != null) m_indicateurCalcul.EnvoyerInfo("\n\nComputing the number of rules to test..."); //number of rules to test iNombreReglesATester = ComptabiliserNombreMaxReglesTestees(); if (m_indicateurCalcul != null) { m_indicateurCalcul.IndiquerNombreReglesATester(iNombreReglesATester); m_indicateurCalcul.EnvoyerInfo(": " + String.valueOf(iNombreReglesATester) + " rules.\n\n\n"); } } } // Calculate the rules: if (m_bEnExecution) { m_iNombreItemsQuantConsideres = m_iMinimumItemsQuantConsideres; m_iTailleFrequent = Math.max(2-m_iNombreItemsQuantConsideres, 0); m_iIndiceFrequent = 0; m_iIndiceCombinaisonItemsQuant = 0; m_iIndiceRepartitionItems = 0; m_bFinTestRegles = false; m_bModeSpecialComptabilisationRegles = false; if (m_indicateurCalcul != null) //print the information of the context to the context text area m_indicateurCalcul.EnvoyerInfo( m_contexteResolution.ObtenirInfosContexte(false) ); while ( (m_bEnExecution) && (!m_bFinTestRegles) ) { //Calculate new rule CalculerNouvelleRegle(); try { sleep(1); } catch (InterruptedException e) {}; } } } catch (java.lang.OutOfMemoryError e) { if (m_indicateurCalcul != null) { m_indicateurCalcul.EnvoyerInfo( "\n\n\nDEPASSEMENT DES CAPACITES MEMOIRE !" ); m_indicateurCalcul.EnvoyerInfo( "\nPlease intensify filtering or reduce the minimum support...\n" ); } } m_bResultatDisponible = true; //Calculation finishes if ( (m_indicateurCalcul != null) && (m_bIndiquerFinCalcul) ) m_indicateurCalcul.IndiquerFinCalcul(); } //END OF RUN // Simule une optimisation afin de compter le nombre de regles qui seront testees : // Simulate an optimization to count the number of rules that wull be tested public int ComptabiliserNombreMaxReglesTestees() { int iNombreNouvellesBoucles = 0; if (m_apriori == null) return 0; m_iNombreItemsQuantConsideres = m_iMinimumItemsQuantConsideres; m_iTailleFrequent = Math.max(2-m_iNombreItemsQuantConsideres, 0); m_iIndiceFrequent = 0; m_iIndiceCombinaisonItemsQuant = 0; m_iIndiceRepartitionItems = 0; m_bFinTestRegles = false; m_bModeSpecialComptabilisationRegles = true; m_iNombreReglesComptabilisees = 0; iNombreNouvellesBoucles = 0; while ( (m_bEnExecution) && (!m_bFinTestRegles) ) { CalculerNouvelleRegle(); iNombreNouvellesBoucles++; if (iNombreNouvellesBoucles > 25000) { try { sleep(1); } catch (InterruptedException e) {}; iNombreNouvellesBoucles = 0; if (m_indicateurCalcul != null) m_indicateurCalcul.EnvoyerInfo("."); } } return m_iNombreReglesComptabilisees; } //calculate new rules void CalculerNouvelleRegle() { ItemSet itemSetFrequent = null; AttributQuantitative attributQuant = null; int iIndiceItemRegle = 0; int iIndiceAttributQuant = 0; int iIndiceEvolution = 0; ItemQualitative itemQual = null; ItemQuantitative itemQuant = null; boolean [] tRepartitionItems = null; int iIndiceAttributQuantAjoute = 0; boolean [] tCombinaisonItemsQuant = null; ArrayList listeRegles = null; if (m_optimiseurCourant == null) { m_bFinTestRegles = true; return; } // On arrete quand on a teste toutes les regles possibles dont le nombre d'attributs quantitatifs ne depasse pas le nombre autorise : // We stop when we have tested all the possible rules for which the number of quantative attributes does not exceed the authorized number: if (m_iNombreItemsQuantConsideres > m_iMaximumItemsQuantConsideres) { m_bFinTestRegles = true; return; } if (m_iTailleFrequent > 0) { itemSetFrequent = m_apriori.RecupererFrequent(m_iTailleFrequent, m_iIndiceFrequent); if (itemSetFrequent == null) { m_iTailleFrequent++; m_iIndiceFrequent = 0; m_iIndiceCombinaisonItemsQuant = 0; m_iIndiceRepartitionItems = 0; itemSetFrequent = m_apriori.RecupererFrequent(m_iTailleFrequent, m_iIndiceFrequent); } // Cas ou tous les items ont ete traites pour un nombre d'attributs quantitatifs donne : // Case where all the items are treated for a given number of quantitative attribute: if (itemSetFrequent == null) { m_iNombreItemsQuantConsideres++; m_iTailleFrequent = Math.max(2-m_iNombreItemsQuantConsideres, 0); m_iIndiceFrequent = 0; m_iIndiceCombinaisonItemsQuant = 0; m_iIndiceRepartitionItems = 0; return; } } if (m_iNombreItemsQuantConsideres > 0) { tCombinaisonItemsQuant = AprioriQuantitative.CalculerEnsemblesItems(m_iIndiceCombinaisonItemsQuant, m_iNombreTotalAttributsQuant, m_iNombreItemsQuantConsideres); if (tCombinaisonItemsQuant==null) { if (m_iTailleFrequent == 0) { // Cas ou on testait des regles 100% quantitatives // case where we tested pure quantitative rules m_iTailleFrequent++; m_iIndiceFrequent = 0; } else m_iIndiceFrequent++; m_iIndiceCombinaisonItemsQuant = 0; m_iIndiceRepartitionItems = 0; return; } } // Calcul de la repartition des items a droite et a gauche. Dans le tableau // ou chaque indice correspond a un item (les "iTailleFrequent" premiers sont les items qualitatifs) // on placera les items ayant la valeur "vrai" dans la case du tableau qui lui correspond // dans la partie gauche de la regle, les autres integrant la partie droite. // Calculate repartition of items on the right and left. In the table // where each index corresponds to an item (the first "iTailleFrequent" are the qualitative items // we will place the items having "true" value in their corresponding table cell // in the right side of the ru;e, the rest going to the right side. tRepartitionItems = AprioriQuantitative.CalculerRepartitionItems(m_iIndiceRepartitionItems, m_iTailleFrequent+m_iNombreItemsQuantConsideres); if (tRepartitionItems == null) { if (m_iNombreItemsQuantConsideres==0) m_iIndiceFrequent++; else m_iIndiceCombinaisonItemsQuant++; m_iIndiceRepartitionItems = 0; return; } else { // Construction du schema de la rule optimiser : AssociationRule regle = null; int iNombreItemsGauche = 0; int iNombreItemsDroite = 0; int iNombreItems = 0; // Number of items in the rule, qualitatifs and quantitatifs int iIndiceAjoutGauche = 0; int iIndiceAjoutDroite = 0; iNombreItems = m_iTailleFrequent + m_iNombreItemsQuantConsideres; // On compte le nombre d'items qu'on va placer a gauche : // We count the number of items to be placed on the left: iNombreItemsGauche = 0; iNombreItemsDroite = 0; for (iIndiceItemRegle = 0; iIndiceItemRegle < iNombreItems; iIndiceItemRegle++) { if (tRepartitionItems[iIndiceItemRegle]) iNombreItemsGauche++; else iNombreItemsDroite++; } //create a new rule template regle = new AssociationRule(iNombreItemsGauche, iNombreItemsDroite, m_iNombreDisjonctionsGauche, m_iNombreDisjonctionsDroite); iIndiceAjoutGauche = 0; iIndiceAjoutDroite = 0; // On specifie les items qualitatifs : // We specify all qualitative items: for (iIndiceItemRegle = 0; iIndiceItemRegle < m_iTailleFrequent; iIndiceItemRegle++) { itemQual = itemSetFrequent.ObtenirItem(iIndiceItemRegle); if (tRepartitionItems[iIndiceItemRegle]) { regle.AssignerItemGauche(itemQual, iIndiceAjoutGauche); iIndiceAjoutGauche++; } else { regle.AssignerItemDroite(itemQual, iIndiceAjoutDroite); iIndiceAjoutDroite++; } } // On specifie les attributs quantitatifs : // We specify all quantitative items: if (m_iNombreItemsQuantConsideres > 0) { iIndiceAttributQuantAjoute = 0; for (iIndiceAttributQuant = 0; iIndiceAttributQuant < m_iNombreTotalAttributsQuant; iIndiceAttributQuant++) { if (tCombinaisonItemsQuant[iIndiceAttributQuant]) { attributQuant = (AttributQuantitative)(m_listeAttributsQuant.get(iIndiceAttributQuant)); if (tRepartitionItems[m_iTailleFrequent + iIndiceAttributQuantAjoute]) { itemQuant = new ItemQuantitative(attributQuant, m_iNombreDisjonctionsGauche); regle.AssignerItemGauche(itemQuant, iIndiceAjoutGauche); iIndiceAjoutGauche++; } else { itemQuant = new ItemQuantitative(attributQuant, m_iNombreDisjonctionsDroite); regle.AssignerItemDroite(itemQuant, iIndiceAjoutDroite); iIndiceAjoutDroite++; } iIndiceAttributQuantAjoute++; } } } if ( m_contexteResolution.EstRegleValide(regle) ) { // Dans le mode de comptabilisation des r�gles, on n'a rien � optimiser : //if in the mode of calculating max rule to test, simply increase m_iNombreReglesComptabilisees if (m_bModeSpecialComptabilisationRegles) m_iNombreReglesComptabilisees++; // Otherwise, optimise the rule: else { if ( m_optimiseurCourant.OptimiseRegle(regle) ) if ( m_listeRegles != null ) { m_listeRegles.add(regle); } // On indique qu'on vient de tester une nouvelle forme de regle : // we indicate that we just tested a new form of rule: m_iNombreReglesTestees++; } } m_iIndiceRepartitionItems++; } } public void ArreterExecution() { m_bEnExecution = false; } public AssociationRule ObtenirRegleCalculee(int iIndiceRegle) { if (m_listeRegles != null) { try { return (AssociationRule)(m_listeRegles.get(iIndiceRegle)); } catch (IndexOutOfBoundsException e) { return null; } } else return null; } public ArrayList ObtenirListeReglesOptimales() { return m_listeRegles; } public boolean EstResultatDisponible() { return m_bResultatDisponible; } }