Coverage Report - weka.classifiers.bayes.net.search.local.LAGDHillClimber
 
Classes in this File Line Coverage Branch Coverage Complexity
LAGDHillClimber
0%
0/126
0%
0/58
2.778
 
 1  
 /*
 2  
  *   This program is free software: you can redistribute it and/or modify
 3  
  *   it under the terms of the GNU General Public License as published by
 4  
  *   the Free Software Foundation, either version 3 of the License, or
 5  
  *   (at your option) any later version.
 6  
  *
 7  
  *   This program is distributed in the hope that it will be useful,
 8  
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 9  
  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 10  
  *   GNU General Public License for more details.
 11  
  *
 12  
  *   You should have received a copy of the GNU General Public License
 13  
  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 14  
  */
 15  
 
 16  
 /*
 17  
  * LAGDHillClimber.java
 18  
  * Copyright (C) 2005-2012 Manuel Neubach
 19  
  * 
 20  
  */
 21  
 
 22  
 package weka.classifiers.bayes.net.search.local;
 23  
 
 24  
 import java.util.Enumeration;
 25  
 import java.util.Vector;
 26  
 
 27  
 import weka.classifiers.bayes.BayesNet;
 28  
 import weka.core.Instances;
 29  
 import weka.core.Option;
 30  
 import weka.core.RevisionUtils;
 31  
 import weka.core.Utils;
 32  
 
 33  
 /** 
 34  
  <!-- globalinfo-start -->
 35  
  * This Bayes Network learning algorithm uses a Look Ahead Hill Climbing algorithm called LAGD Hill Climbing. Unlike Greedy Hill Climbing it doesn't calculate a best greedy operation (adding, deleting or reversing an arc) but a sequence of nrOfLookAheadSteps operations, which leads to a network structure whose score is most likely higher in comparison to the network obtained by performing a sequence of nrOfLookAheadSteps greedy operations. The search is not restricted by an order on the variables (unlike K2). The difference with B and B2 is that this hill climber also considers arrows part of the naive Bayes structure for deletion.
 36  
  * <p/>
 37  
  <!-- globalinfo-end -->
 38  
  *
 39  
  <!-- options-start -->
 40  
  * Valid options are: <p/>
 41  
  * 
 42  
  * <pre> -L &lt;nr of look ahead steps&gt;
 43  
  *  Look Ahead Depth</pre>
 44  
  * 
 45  
  * <pre> -G &lt;nr of good operations&gt;
 46  
  *  Nr of Good Operations</pre>
 47  
  * 
 48  
  * <pre> -P &lt;nr of parents&gt;
 49  
  *  Maximum number of parents</pre>
 50  
  * 
 51  
  * <pre> -R
 52  
  *  Use arc reversal operation.
 53  
  *  (default false)</pre>
 54  
  * 
 55  
  * <pre> -N
 56  
  *  Initial structure is empty (instead of Naive Bayes)</pre>
 57  
  * 
 58  
  * <pre> -mbc
 59  
  *  Applies a Markov Blanket correction to the network structure, 
 60  
  *  after a network structure is learned. This ensures that all 
 61  
  *  nodes in the network are part of the Markov blanket of the 
 62  
  *  classifier node.</pre>
 63  
  * 
 64  
  * <pre> -S [BAYES|MDL|ENTROPY|AIC|CROSS_CLASSIC|CROSS_BAYES]
 65  
  *  Score type (BAYES, BDeu, MDL, ENTROPY and AIC)</pre>
 66  
  * 
 67  
  <!-- options-end -->
 68  
  * 
 69  
  * @author Manuel Neubach
 70  
  * @version $Revision: 8034 $
 71  
  */
 72  0
 public class LAGDHillClimber 
 73  
     extends HillClimber {
 74  
   
 75  
     /** for serialization */
 76  
     static final long serialVersionUID = 7217437499439184344L;
 77  
 
 78  
     /** Number of Look Ahead Steps **/
 79  0
     int m_nNrOfLookAheadSteps = 2;
 80  
 
 81  
     /** Number of Good Operations per Step **/
 82  0
     int m_nNrOfGoodOperations = 5;
 83  
 
 84  
    /**
 85  
      * search determines the network structure/graph of the network
 86  
      * 
 87  
      * @param bayesNet the network
 88  
      * @param instances the data to use
 89  
      * @throws Exception if something goes wrong
 90  
      */
 91  
    protected void search(BayesNet bayesNet, Instances instances) throws Exception {
 92  0
         int k=m_nNrOfLookAheadSteps;  // Number of Look Ahead Steps
 93  0
         int l=m_nNrOfGoodOperations; // Number of Good Operations per step
 94  0
         lookAheadInGoodDirectionsSearch(bayesNet, instances, k, l);
 95  0
    } // search
 96  
 
 97  
 
 98  
    /**
 99  
     * lookAheadInGoodDirectionsSearch determines the network structure/graph of the network
 100  
     * with best score according to LAGD Hill Climbing
 101  
     * 
 102  
     * @param bayesNet the network
 103  
     * @param instances the data to use
 104  
     * @param nrOfLookAheadSteps
 105  
     * @param nrOfGoodOperations
 106  
     * @throws Exception if something goes wrong
 107  
     */
 108  
     protected void lookAheadInGoodDirectionsSearch(BayesNet bayesNet, Instances instances, int nrOfLookAheadSteps, int nrOfGoodOperations) throws Exception {
 109  0
          System.out.println("Initializing Cache");
 110  0
          initCache(bayesNet, instances);
 111  
 
 112  0
          while (nrOfLookAheadSteps>1) {         
 113  0
             System.out.println("Look Ahead Depth: "+nrOfLookAheadSteps);
 114  0
             boolean legalSequence = true;
 115  0
             double sequenceDeltaScore = 0;
 116  0
             Operation [] bestOperation=new Operation [nrOfLookAheadSteps];
 117  
          
 118  0
             bestOperation = getOptimalOperations(bayesNet, instances, nrOfLookAheadSteps, nrOfGoodOperations);
 119  0
             for (int i = 0; i < nrOfLookAheadSteps; i++) {
 120  0
                if (bestOperation [i] == null) {
 121  0
                   legalSequence=false;
 122  
                } else {
 123  0
                   sequenceDeltaScore += bestOperation [i].m_fDeltaScore;
 124  
                }
 125  
             }
 126  0
             while (legalSequence && sequenceDeltaScore > 0) {
 127  0
                System.out.println("Next Iteration..........................");
 128  0
                for (int i = 0; i < nrOfLookAheadSteps; i++) {
 129  0
                   performOperation(bayesNet, instances,bestOperation [i]);
 130  
                }
 131  0
                bestOperation = getOptimalOperations(bayesNet, instances, nrOfLookAheadSteps, nrOfGoodOperations);
 132  0
                sequenceDeltaScore = 0;
 133  0
                for (int i = 0; i < nrOfLookAheadSteps; i++) {
 134  0
                   if (bestOperation [i] != null) {
 135  0
                      System.out.println(bestOperation [i].m_nOperation + " " + bestOperation [i].m_nHead + " " + bestOperation [i].m_nTail);
 136  0
                      sequenceDeltaScore += bestOperation [i].m_fDeltaScore;
 137  
                   } else {
 138  0
                      legalSequence = false;
 139  
 
 140  
                   }
 141  0
                   System.out.println("DeltaScore: "+sequenceDeltaScore);
 142  
                }
 143  
             }
 144  0
             --nrOfLookAheadSteps;
 145  0
          }
 146  
 
 147  
          /** last steps with greedy HC **/          
 148  0
          Operation oOperation = getOptimalOperation(bayesNet, instances);
 149  0
          while ((oOperation != null) && (oOperation.m_fDeltaScore > 0)) {
 150  0
             performOperation(bayesNet, instances, oOperation);
 151  0
             System.out.println("Performing last greedy steps");
 152  0
             oOperation = getOptimalOperation(bayesNet, instances);
 153  
          }               
 154  
          // free up memory
 155  0
          m_Cache = null;
 156  0
     } // lookAheadInGoodDirectionsSearch
 157  
 
 158  
     /**
 159  
       * getAntiOperation determines the Operation, which is needed to cancel oOperation
 160  
       * 
 161  
       * @param oOperation Operation to cancel
 162  
       * @return antiOperation to oOperation
 163  
       * @throws Exception if something goes wrong
 164  
       */
 165  
     protected Operation getAntiOperation(Operation oOperation) throws Exception {
 166  0
         if (oOperation.m_nOperation == Operation.OPERATION_ADD)
 167  0
            return (new Operation (oOperation.m_nTail, oOperation.m_nHead, Operation.OPERATION_DEL));
 168  
         else {
 169  0
            if (oOperation.m_nOperation == Operation.OPERATION_DEL)
 170  0
               return (new Operation (oOperation.m_nTail, oOperation.m_nHead, Operation.OPERATION_ADD));
 171  
            else {
 172  0
               return (new Operation (oOperation.m_nHead, oOperation.m_nTail, Operation.OPERATION_REVERSE));
 173  
            }
 174  
          }
 175  
     } // getAntiOperation
 176  
 
 177  
 
 178  
     /**
 179  
       * getGoodOperations determines the nrOfGoodOperations best Operations, which are considered for
 180  
       * the calculation of an optimal operationsequence
 181  
       * @param bayesNet Bayes network to apply operation on
 182  
       * @param instances data set to learn from
 183  
       * @param nrOfGoodOperations number of good operations to consider
 184  
       * @return good operations to consider
 185  
       * @throws Exception if something goes wrong
 186  
       **/
 187  
     protected Operation [] getGoodOperations(BayesNet bayesNet, Instances instances, int nrOfGoodOperations) throws Exception {
 188  0
                 Operation [] goodOperations=new Operation [nrOfGoodOperations];
 189  0
                        for (int i = 0; i < nrOfGoodOperations; i++) {
 190  0
                    goodOperations [i] = getOptimalOperation(bayesNet, instances);
 191  0
                    if (goodOperations[i] != null) {
 192  0
                       m_Cache.put(goodOperations [i], -1E100);
 193  0
                    } else i=nrOfGoodOperations;
 194  
                 }
 195  0
                 for (int i = 0; i < nrOfGoodOperations; i++) {
 196  0
                    if (goodOperations[i] != null) {
 197  0
                       if (goodOperations [i].m_nOperation!=Operation.OPERATION_REVERSE) {
 198  0
                          m_Cache.put(goodOperations [i], goodOperations [i].m_fDeltaScore);
 199  
                       } else {
 200  0
                          m_Cache.put(goodOperations [i], goodOperations [i].m_fDeltaScore - m_Cache.m_fDeltaScoreAdd[goodOperations[i].m_nHead] [goodOperations [i].m_nTail]);
 201  
                       }
 202  0
                    } else i=nrOfGoodOperations;
 203  
                 }
 204  0
                 return goodOperations;
 205  
     } // getGoodOperations
 206  
 
 207  
     /**
 208  
       * getOptimalOperations determines an optimal operationsequence in respect of the parameters 
 209  
       * nrOfLookAheadSteps and nrOfGoodOperations
 210  
       * @param bayesNet Bayes network to apply operation on
 211  
       * @param instances data set to learn from
 212  
       * @param nrOfLookAheadSteps number of lood ahead steps to use
 213  
       * @param nrOfGoodOperations number of good operations to consider
 214  
       * @return optimal sequence of operations in respect to nrOfLookAheadSteps and nrOfGoodOperations
 215  
       * @throws Exception if something goes wrong
 216  
       **/
 217  
     protected Operation [] getOptimalOperations(BayesNet bayesNet, Instances instances, int nrOfLookAheadSteps, int nrOfGoodOperations) throws Exception {
 218  0
        if (nrOfLookAheadSteps == 1) { // Abbruch der Rekursion
 219  0
           Operation [] bestOperation = new Operation [1];
 220  0
           bestOperation [0] = getOptimalOperation(bayesNet, instances);
 221  0
           return(bestOperation);  // Abbruch der Rekursion
 222  
        } else {
 223  0
           double bestDeltaScore = 0;
 224  0
           double currentDeltaScore = 0;
 225  0
           Operation [] bestOperation = new Operation [nrOfLookAheadSteps];
 226  0
           Operation [] goodOperations = new Operation [nrOfGoodOperations];
 227  0
           Operation [] tempOperation = new Operation [nrOfLookAheadSteps-1];
 228  0
           goodOperations = getGoodOperations(bayesNet, instances, nrOfGoodOperations);
 229  0
           for (int i = 0; i < nrOfGoodOperations; i++) {
 230  0
            if (goodOperations[i] != null) {
 231  0
              performOperation(bayesNet, instances, goodOperations [i]);
 232  0
              tempOperation = getOptimalOperations(bayesNet, instances, nrOfLookAheadSteps-1, nrOfGoodOperations); // rekursiver Abstieg
 233  0
              currentDeltaScore = goodOperations [i].m_fDeltaScore;
 234  0
              for (int j = 0; j < nrOfLookAheadSteps-1; j++) {
 235  0
                 if (tempOperation [j] != null) {
 236  0
                    currentDeltaScore += tempOperation [j].m_fDeltaScore;
 237  
                 }
 238  
              }
 239  0
              performOperation(bayesNet, instances, getAntiOperation(goodOperations [i]));
 240  0
                       if (currentDeltaScore > bestDeltaScore) {
 241  0
                         bestDeltaScore = currentDeltaScore;
 242  0
                         bestOperation [0] = goodOperations [i];
 243  0
                         for (int j = 1; j < nrOfLookAheadSteps; j++) {
 244  0
                           bestOperation [j] = tempOperation [j-1];
 245  
                         }
 246  
                     }
 247  0
             } else i=nrOfGoodOperations;
 248  
            }
 249  0
            return(bestOperation);
 250  
        }
 251  
     } // getOptimalOperations
 252  
 
 253  
 
 254  
         /**
 255  
          * Sets the max number of parents
 256  
          *
 257  
          * @param nMaxNrOfParents the max number of parents
 258  
          */
 259  
         public void setMaxNrOfParents(int nMaxNrOfParents) {
 260  0
           m_nMaxNrOfParents = nMaxNrOfParents;
 261  0
         } 
 262  
 
 263  
         /**
 264  
          * Gets the max number of parents.
 265  
          *
 266  
          * @return the max number of parents
 267  
          */
 268  
         public int getMaxNrOfParents() {
 269  0
           return m_nMaxNrOfParents;
 270  
         } 
 271  
 
 272  
         /**
 273  
          * Sets the number of look-ahead steps
 274  
          *
 275  
          * @param nNrOfLookAheadSteps the number of look-ahead steps
 276  
          */
 277  
         public void setNrOfLookAheadSteps(int nNrOfLookAheadSteps) {
 278  0
           m_nNrOfLookAheadSteps = nNrOfLookAheadSteps;
 279  0
         } 
 280  
 
 281  
         /**
 282  
          * Gets the number of look-ahead steps
 283  
          *
 284  
          * @return the number of look-ahead step
 285  
          */
 286  
         public int getNrOfLookAheadSteps() {
 287  0
           return m_nNrOfLookAheadSteps;
 288  
         } 
 289  
 
 290  
         /**
 291  
          * Sets the number of "good operations"
 292  
          *
 293  
          * @param nNrOfGoodOperations the number of "good operations"
 294  
          */
 295  
         public void setNrOfGoodOperations(int nNrOfGoodOperations) {
 296  0
           m_nNrOfGoodOperations = nNrOfGoodOperations;
 297  0
         } 
 298  
 
 299  
         /**
 300  
          * Gets the number of "good operations"
 301  
          *
 302  
          * @return the number of "good operations"
 303  
          */
 304  
         public int getNrOfGoodOperations() {
 305  0
           return m_nNrOfGoodOperations;
 306  
         } 
 307  
 
 308  
 
 309  
         /**
 310  
          * Returns an enumeration describing the available options.
 311  
          *
 312  
          * @return an enumeration of all the available options.
 313  
          */
 314  
         public Enumeration listOptions() {
 315  0
                 Vector newVector = new Vector();
 316  
 
 317  0
                 newVector.addElement(new Option("\tLook Ahead Depth", "L", 2, "-L <nr of look ahead steps>"));
 318  0
                 newVector.addElement(new Option("\tNr of Good Operations", "G", 5, "-G <nr of good operations>"));
 319  
 
 320  0
                 Enumeration enm = super.listOptions();
 321  0
                 while (enm.hasMoreElements()) {
 322  0
                         newVector.addElement(enm.nextElement());
 323  
                 }
 324  0
                 return newVector.elements();
 325  
         } // listOptions
 326  
 
 327  
         /**
 328  
          * Parses a given list of options. Valid options are:<p>
 329  
          *
 330  
          <!-- options-start -->
 331  
          * Valid options are: <p/>
 332  
          * 
 333  
          * <pre> -L &lt;nr of look ahead steps&gt;
 334  
          *  Look Ahead Depth</pre>
 335  
          * 
 336  
          * <pre> -G &lt;nr of good operations&gt;
 337  
          *  Nr of Good Operations</pre>
 338  
          * 
 339  
          * <pre> -P &lt;nr of parents&gt;
 340  
          *  Maximum number of parents</pre>
 341  
          * 
 342  
          * <pre> -R
 343  
          *  Use arc reversal operation.
 344  
          *  (default false)</pre>
 345  
          * 
 346  
          * <pre> -N
 347  
          *  Initial structure is empty (instead of Naive Bayes)</pre>
 348  
          * 
 349  
          * <pre> -mbc
 350  
          *  Applies a Markov Blanket correction to the network structure, 
 351  
          *  after a network structure is learned. This ensures that all 
 352  
          *  nodes in the network are part of the Markov blanket of the 
 353  
          *  classifier node.</pre>
 354  
          * 
 355  
          * <pre> -S [BAYES|MDL|ENTROPY|AIC|CROSS_CLASSIC|CROSS_BAYES]
 356  
          *  Score type (BAYES, BDeu, MDL, ENTROPY and AIC)</pre>
 357  
          * 
 358  
          <!-- options-end -->
 359  
          *
 360  
          * @param options the list of options as an array of strings
 361  
          * @throws Exception if an option is not supported
 362  
          */
 363  
         public void setOptions(String[] options) throws Exception {
 364  0
                   String sNrOfLookAheadSteps = Utils.getOption('L', options);
 365  0
                 if (sNrOfLookAheadSteps.length() != 0) {
 366  0
                   setNrOfLookAheadSteps(Integer.parseInt(sNrOfLookAheadSteps));
 367  
                 } else {
 368  0
                   setNrOfLookAheadSteps(2);
 369  
                 }
 370  
 
 371  0
                 String sNrOfGoodOperations = Utils.getOption('G', options);
 372  0
                 if (sNrOfGoodOperations.length() != 0) {
 373  0
                   setNrOfGoodOperations(Integer.parseInt(sNrOfGoodOperations));
 374  
                 } else {
 375  0
                   setNrOfGoodOperations(5);
 376  
                 }
 377  
                 
 378  0
                 super.setOptions(options);
 379  0
         } // setOptions
 380  
 
 381  
         /**
 382  
          * Gets the current settings of the search algorithm.
 383  
          *
 384  
          * @return an array of strings suitable for passing to setOptions
 385  
          */
 386  
         public String[] getOptions() {
 387  0
                 String[] superOptions = super.getOptions();
 388  0
                 String[] options = new String[9 + superOptions.length];
 389  0
                 int current = 0;
 390  
 
 391  0
                 options[current++] = "-L";
 392  0
                 options[current++] = "" + m_nNrOfLookAheadSteps;
 393  
                 
 394  0
                 options[current++] = "-G";
 395  0
                 options[current++] = "" + m_nNrOfGoodOperations;
 396  
 
 397  
                 // insert options from parent class
 398  0
                 for (int iOption = 0; iOption < superOptions.length; iOption++) {
 399  0
                         options[current++] = superOptions[iOption];
 400  
                 }
 401  
 
 402  
                 // Fill up rest with empty strings, not nulls!
 403  0
                 while (current < options.length) {
 404  0
                         options[current++] = "";
 405  
                 }
 406  0
                 return options;
 407  
         } // getOptions
 408  
 
 409  
 
 410  
         /**
 411  
          * This will return a string describing the search algorithm.
 412  
          * @return The string.
 413  
          */
 414  
         public String globalInfo() {
 415  0
           return "This Bayes Network learning algorithm uses a Look Ahead Hill Climbing algorithm called LAGD Hill Climbing." +
 416  
           " Unlike Greedy Hill Climbing it doesn't calculate a best greedy operation (adding, deleting or reversing an arc) " + 
 417  
           "but a sequence of nrOfLookAheadSteps operations, which leads to a network structure whose score is most likely " +
 418  
           "higher in comparison to the network obtained by performing a sequence of nrOfLookAheadSteps greedy operations. " +
 419  
           "The search is not restricted by an order " +
 420  
           "on the variables (unlike K2). The difference with B and B2 is that this hill " +
 421  
           "climber also considers arrows part of the naive Bayes structure for deletion.";
 422  
         } // globalInfo
 423  
 
 424  
         /**
 425  
          * @return a string to describe the Number of Look Ahead Steps option.
 426  
          */
 427  
         public String nrOfLookAheadStepsTipText() {
 428  0
           return "Sets the Number of Look Ahead Steps. 'nrOfLookAheadSteps = 2' means that all network structures in a " +
 429  
           "distance of 2 (from the current network structure) are taken into account for the decision which arcs to add, " +
 430  
           "remove or reverse. 'nrOfLookAheadSteps = 1' results in Greedy Hill Climbing." ;
 431  
         } // nrOfLookAheadStepsTipText
 432  
 
 433  
         /**
 434  
          * @return a string to describe the Number of Good Operations option.
 435  
          */
 436  
         public String nrOfGoodOperationsTipText() {
 437  0
           return "Sets the Number of Good Operations per Look Ahead Step. 'nrOfGoodOperations = 5' means that for the next " +
 438  
           "Look Ahead Step only the 5 best Operations (adding, deleting or reversing an arc) are taken into account for the " +
 439  
           "calculation of the best sequence consisting of nrOfLookAheadSteps operations." ;
 440  
         } // nrOfGoodOperationsTipText
 441  
 
 442  
         /**
 443  
          * Returns the revision string.
 444  
          * 
 445  
          * @return                the revision
 446  
          */
 447  
         public String getRevision() {
 448  0
           return RevisionUtils.extract("$Revision: 8034 $");
 449  
         }
 450  
 
 451  
 } // LAGDHillClimber