001    package net.sf.cpsolver.exam.heuristics;
002    
003    import org.apache.log4j.Logger;
004    
005    import net.sf.cpsolver.exam.neighbours.ExamRandomMove;
006    import net.sf.cpsolver.exam.neighbours.ExamRoomMove;
007    import net.sf.cpsolver.exam.neighbours.ExamSimpleNeighbour;
008    import net.sf.cpsolver.exam.neighbours.ExamTimeMove;
009    import net.sf.cpsolver.ifs.heuristics.NeighbourSelection;
010    import net.sf.cpsolver.ifs.model.Model;
011    import net.sf.cpsolver.ifs.model.Neighbour;
012    import net.sf.cpsolver.ifs.solution.Solution;
013    import net.sf.cpsolver.ifs.solution.SolutionListener;
014    import net.sf.cpsolver.ifs.solver.Solver;
015    import net.sf.cpsolver.ifs.util.DataProperties;
016    import net.sf.cpsolver.ifs.util.Progress;
017    import net.sf.cpsolver.ifs.util.ToolBox;
018    
019    /**
020     * Hill climber. In each iteration, one of the following three neighbourhoods is selected first
021     * <ul>
022     * <li>random move ({@link ExamRandomMove})
023     * <li>period swap ({@link ExamTimeMove})
024     * <li>room swap ({@link ExamRoomMove})
025     * </ul>,
026     * then a neighbour is generated and it is accepted if its value {@link ExamSimpleNeighbour#value()} is 
027     * below or equal to zero. The search is stopped after a given amount of idle iterations (
028     * can be defined by problem property  HillClimber.MaxIdle).
029     * <br><br>
030     * 
031     * @version
032     * ExamTT 1.1 (Examination Timetabling)<br>
033     * Copyright (C) 2008 Tomáš Müller<br>
034     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
035     * Lazenska 391, 76314 Zlin, Czech Republic<br>
036     * <br>
037     * This library is free software; you can redistribute it and/or
038     * modify it under the terms of the GNU Lesser General Public
039     * License as published by the Free Software Foundation; either
040     * version 2.1 of the License, or (at your option) any later version.
041     * <br><br>
042     * This library is distributed in the hope that it will be useful,
043     * but WITHOUT ANY WARRANTY; without even the implied warranty of
044     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
045     * Lesser General Public License for more details.
046     * <br><br>
047     * You should have received a copy of the GNU Lesser General Public
048     * License along with this library; if not, write to the Free Software
049     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
050     */
051    public class ExamHillClimbing implements NeighbourSelection, SolutionListener {
052        private static Logger sLog = Logger.getLogger(ExamHillClimbing.class);
053        private NeighbourSelection[] iNeighbours = null;
054        private int iMaxIdleIters = 25000;
055        private int iLastImprovingIter = 0;
056        private double iBestValue = 0;
057        private int iIter = 0;
058        private Progress iProgress = null;
059        private boolean iActive;
060        private String iName = "Hill climbing";
061    
062        /**
063         * Constructor
064         * @param properties problem properties (use HillClimber.MaxIdle to set maximum number of idle iterations)
065         */
066        public ExamHillClimbing(DataProperties properties) {
067            this(properties, "Hill Climbing");
068        }
069        
070        /**
071         * Constructor
072         * @param properties problem properties (use HillClimber.MaxIdle to set maximum number of idle iterations)
073         */
074        public ExamHillClimbing(DataProperties properties, String name) {
075            iMaxIdleIters = properties.getPropertyInt("HillClimber.MaxIdle", iMaxIdleIters);
076            iNeighbours = new NeighbourSelection[] {
077                    new ExamRandomMove(properties),
078                    new ExamRoomMove(properties),
079                    new ExamTimeMove(properties)
080            };
081            iName = name;
082        }
083        
084        /**
085         * Initialization
086         */
087        public void init(Solver solver) {
088            solver.currentSolution().addSolutionListener(this);
089            for (int i=0;i<iNeighbours.length;i++)
090                iNeighbours[i].init(solver);
091            solver.setUpdateProgress(false);
092            iProgress = Progress.getInstance(solver.currentSolution().getModel());
093            iActive = false;
094        }
095        
096        /**
097         * Select one of the given neighbourhoods randomly, select neighbour, return it if 
098         * its value is below or equal to zero (continue with the next selection otherwise).
099         * Return null when the given number of idle iterations is reached.
100         */
101        public Neighbour selectNeighbour(Solution solution) {
102            if (!iActive) {
103                iProgress.setPhase(iName+"...");
104                iActive = true;
105            }
106            Model model = (Model)solution.getModel();
107            while (true) {
108                iIter ++;
109                iProgress.setProgress(Math.round(100.0*(iIter-iLastImprovingIter)/iMaxIdleIters));
110                if (iIter-iLastImprovingIter>=iMaxIdleIters) break;
111                NeighbourSelection ns = iNeighbours[ToolBox.random(iNeighbours.length)];
112                Neighbour n = ns.selectNeighbour(solution);
113                if (n!=null && n.value()<=0) return n;
114            }
115            iIter = 0; iLastImprovingIter = 0;
116            iActive=false;
117            return null;
118        }
119        
120        /**
121         * Memorize the iteration when the last best solution was found.
122         */
123        public void bestSaved(Solution solution) {
124            if (Math.abs(iBestValue-solution.getBestValue())>=1.0) {
125                iLastImprovingIter = iIter;
126                iBestValue = solution.getBestValue();
127            }
128        }
129        public void solutionUpdated(Solution solution) {}
130        public void getInfo(Solution solution, java.util.Dictionary info) {}
131        public void getInfo(Solution solution, java.util.Dictionary info, java.util.Vector variables) {}
132        public void bestCleared(Solution solution) {}
133        public void bestRestored(Solution solution){}   }