001    /**
002     * DetailedNFoldEvaluator.java
003     * jCOLIBRI2 framework. 
004     * @author Juan A. Recio-García.
005     * @author Lisa Cummins.
006     * GAIA - Group for Artificial Intelligence Applications
007     * http://gaia.fdi.ucm.es
008     * 07/05/2007
009     */
010    package jcolibri.extensions.evaluation.evaluators;
011    
012    import java.util.ArrayList;
013    import java.util.Collection;
014    import java.util.Date;
015    import java.util.List;
016    
017    import jcolibri.cbrcore.CBRCase;
018    import jcolibri.cbrcore.CBRCaseBase;
019    import jcolibri.extensions.evaluation.MaintenanceEvaluator;
020    
021    import org.apache.commons.logging.LogFactory;
022    
023    /**
024     * This evaluation method divides the case base into several random folds (indicated by the user). 
025     * For each fold, their cases are used as queries and the remaining folds are used together as case base. 
026     * This process is performed several times.
027     * 
028     * @author Juan A. Recio García - GAIA http://gaia.fdi.ucm.es
029     * @author Lisa Cummins.
030     * @version 2.0
031     */
032    public class MaintenanceNFoldEvaluator extends MaintenanceEvaluator
033    {
034        /**
035         * Executes the N-Fold evaluation.
036         * @param numFolds Number of folds (randomly generated).
037         * @param repetitions Number of repetitions
038         */
039        public void NFoldEvaluation(int numFolds, int repetitions)
040        {   try
041            {   //Get the time
042                long t = (new Date()).getTime();
043                int numberOfCycles = 0;
044    
045                // Run the precycle to load the case base
046                LogFactory.getLog(this.getClass()).info("Running precycle()");
047                CBRCaseBase caseBase = app.preCycle();
048    
049                if (!(caseBase instanceof jcolibri.casebase.CachedLinealCaseBase))
050                    LogFactory.getLog(this.getClass()).warn(
051                            "Evaluation should be executed using a cached case base");
052                
053                Collection<CBRCase> cases = new ArrayList<CBRCase>(caseBase.getCases());
054                
055                //For each repetition
056                for(int r=0; r<repetitions; r++)
057                {   //Create the folds
058                    ArrayList<ArrayList<CBRCase>> folds = createFolds(cases, numFolds);
059                    
060                    //For each fold
061                    for(int f=0; f<numFolds; f++)
062                    {   ArrayList<CBRCase> querySet = new ArrayList<CBRCase>();
063                        prepareCases(cases, querySet, f, caseBase, folds);
064                    
065                        //Run cycle for each case in querySet (current fold)
066                        for(CBRCase c: querySet)
067                        {   LogFactory.getLog(this.getClass()).info(
068                                "Running cycle() " + numberOfCycles);
069                            app.cycle(c);
070                            numberOfCycles++;
071                        }          
072                    } 
073                }
074    
075                //Revert case base to original state
076                caseBase.forgetCases(cases);
077                caseBase.learnCases(cases);
078                
079                //Run the poscycle to finish the application
080                LogFactory.getLog(this.getClass()).info("Running postcycle()");
081                app.postCycle();
082    
083                //Complete the evaluation result
084                report.setTotalTime(t);
085                report.setNumberOfCycles(numberOfCycles);
086                
087            } catch (Exception e) 
088            {       LogFactory.getLog(this.getClass()).error(e);
089            }
090    
091        }
092    
093        /**
094         * Prepares the cases for evaluation by setting up test and training sets    
095         * @param originalCases Complete original set of cases
096         * @param querySet Where queries are to be stored
097         * @param fod The fold number
098         * @param caseBase The case base
099         */
100        protected void prepareCases(Collection<CBRCase> originalCases, List<CBRCase> querySet, 
101            int fold, CBRCaseBase caseBase, ArrayList<ArrayList<CBRCase>> folds)
102        {   ArrayList<CBRCase> caseBaseSet = new ArrayList<CBRCase>();
103                    
104            //Obtain the query and casebase sets
105            getFolds(fold, querySet, caseBaseSet, folds);
106                
107            //Clear the caseBase
108            caseBase.forgetCases(originalCases);
109                
110            //Set the cases that acts as casebase in this cycle
111            caseBase.learnCases(caseBaseSet);
112            
113            if(this.simConfig != null && this.editMethod != null)
114            {       // Perform maintenance on this case base
115                    editCaseBase(caseBase);
116            }
117        }
118    
119        /**
120         * Divides the given cases into the given number of folds
121         * @param cases the original cases
122         * @param numFolds the number of folds
123         */
124        protected ArrayList<ArrayList<CBRCase>> createFolds(Collection<CBRCase> cases, int numFolds)
125        {   ArrayList<ArrayList<CBRCase>> folds = new ArrayList<ArrayList<CBRCase>>();
126            int foldsize = cases.size() / numFolds;
127            ArrayList<CBRCase> copy = new ArrayList<CBRCase>(cases);
128            
129            for(int f=0; f<numFolds; f++)
130            {   ArrayList<CBRCase> fold = new ArrayList<CBRCase>();
131                for(int i=0; (i<foldsize)&&(copy.size()>0); i++)
132                {   int random = (int) (Math.random() * copy.size());
133                    CBRCase _case = copy.get( random );
134                    copy.remove(random);
135                    fold.add(_case);
136                }
137                folds.add(fold);
138            }
139            return folds;
140        }
141    
142        /**
143         * Clears the current query and case base sets and populates the query set with fold
144         * f and the case base set with the cases not contained in fold f
145         * @param f the fold to use
146         * @param querySet the set of queries
147         * @param caseBaseSet the set of cases
148         */
149        public static void getFolds(int f, List<CBRCase> querySet, List<CBRCase> caseBaseSet, ArrayList<ArrayList<CBRCase>> folds)
150        {   querySet.clear();
151            caseBaseSet.clear();
152            
153            querySet.addAll(folds.get(f));
154            
155            for(int i=0; i<folds.size(); i++)
156                if(i!=f)
157                    caseBaseSet.addAll(folds.get(i));
158        }
159    
160        
161    }