Package net.sf.cpsolver.itc.heuristics.search

Source Code of net.sf.cpsolver.itc.heuristics.search.ItcTabuSearch

package net.sf.cpsolver.itc.heuristics.search;

import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;

import org.apache.log4j.Logger;

import net.sf.cpsolver.ifs.extension.ConflictStatistics;
import net.sf.cpsolver.ifs.extension.Extension;
import net.sf.cpsolver.ifs.heuristics.NeighbourSelection;
import net.sf.cpsolver.ifs.heuristics.ValueSelection;
import net.sf.cpsolver.ifs.model.Model;
import net.sf.cpsolver.ifs.model.Neighbour;
import net.sf.cpsolver.ifs.model.SimpleNeighbour;
import net.sf.cpsolver.ifs.model.Value;
import net.sf.cpsolver.ifs.model.Variable;
import net.sf.cpsolver.ifs.solution.Solution;
import net.sf.cpsolver.ifs.solver.Solver;
import net.sf.cpsolver.ifs.util.DataProperties;
import net.sf.cpsolver.ifs.util.ToolBox;

/**
* Tabu search algorithm.
* <br><br>
* If used as {@link NeighbourSelection}, the most improving (re)assignment of a value to a variable
* is returned (all variables and all their values are enumerated). If there are more than one of
* such assignments, one is selected randomly. A returned assignment can cause unassignment of
* other existing assignments. The search is stopped ({@link ItcTabuSearch#selectNeighbour(Solution)}
* returns null) after TabuSearch.MaxIdle idle (not improving) iterations.
* <br><br>
* If used as {@link ValueSelection}, the most improving (re)assignment of a value to a given variable
* is returned (all values of the given variable are enumerated). If there are more than one of
* such assignments, one is selected randomly. A returned assignment can cause unassignment of
* other existing assignments. 
* <br><br>
* To avoid cycling, a tabu is maintainded during the search. It is the list of the last n
* selected values or {@link TabuElement} if values are implementing this interface. A
* selection of a value that is present in the tabu list is only allowed when it improves the
* best ever found solution.
* <br><br>
* The minimum size of the tabu list is TabuSearch.MinSize, maximum size is TabuSearch.MaxSize (tabu
* list is not used when both sizes are zero). The current size of the tabu list starts at
* MinSize (and is reset to MinSize every time a new best solution is found), it is increased
* by one up to the MaxSize after TabuSearch.MaxIdle / (MaxSize - MinSize) non-improving
* iterations.
* <br><br>
* Conflict-based Statistics {@link ConflictStatistics} (CBS) can be used instead of (or together with)
* tabu list, when CBS is used as a solver extension.
* @version
* ITC2007 1.0<br>
* Copyright (C) 2007 Tomas Muller<br>
* <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
* Lazenska 391, 76314 Zlin, Czech Republic<br>
* <br>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* <br><br>
* This library 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
* Lesser General Public License for more details.
* <br><br>
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
public class ItcTabuSearch implements NeighbourSelection, ValueSelection {
    private static Logger sLog = Logger.getLogger(ItcTabuSearch.class);
    private ConflictStatistics iStat = null;

    private long iFirstIteration = -1;
    private long iMaxIdleIterations = 10000;

    private int iTabuSize = 20;
    private int iTabuMinSize = 20;
    private int iTabuMaxSize = 200;
    private TabuList iTabu = null;
   
    private double iConflictWeight = 100;
    private double iValueWeight = 1;
   
    /**
     * <ul>
     * <li>TabuSearch.MaxIdle ... maximum number of idle iterations (default is 10000)
     * <li>TabuSearch.MinSize ... minimum size of the tabu list
     * <li>TabuSearch.MaxSize ... maximum size of the tabu list
     * <li>Value.ValueWeight ... weight of a value (i.e., {@link Value#toDouble()})
     * <li>Value.ConflictWeight ... weight of a conflicting value (see {@link Model#conflictValues(Value)}),
     * it is also weighted by the past occurrences when conflict-based statistics is used
     * </ul>
     */
    public ItcTabuSearch(DataProperties properties) throws Exception {
        iTabuMinSize = properties.getPropertyInt("TabuSearch.MinSize", iTabuMinSize);
        iTabuMaxSize = properties.getPropertyInt("TabuSearch.MaxSize", iTabuMaxSize);
        if (iTabuMaxSize > 0) iTabu = new TabuList(iTabuMinSize);
        iMaxIdleIterations = properties.getPropertyLong("TabuSearch.MaxIdle", iMaxIdleIterations);
        iConflictWeight = properties.getPropertyDouble("Value.ConflictWeight", iConflictWeight);
        iValueWeight = properties.getPropertyDouble("Value.ValueWeight", iValueWeight);
    }
   
    /** Initialization */
    public void init(Solver solver) {
        for (Enumeration i = solver.getExtensions().elements(); i.hasMoreElements();) {
            Extension extension = (Extension)i.nextElement();
            if (extension instanceof ConflictStatistics)
                iStat = (ConflictStatistics)extension;
        }
    }
   
    /**
     * An element that is to be used to populate (and check) tabu list
     * @param value given value (to be assigned to its variable)
     * @return value or {@link TabuElement#tabuElement()} when the given value implements {@link TabuElement} interface
     */
    public Object tabuElement(Value value) {
        return (value instanceof TabuElement ? ((TabuElement)value).tabuElement() : value);
    }
   
    /**
     * Neighbor selection
     */
    public Neighbour selectNeighbour(Solution solution) {
        if (iFirstIteration<0)
            iFirstIteration = solution.getIteration();
        long idle = solution.getIteration()-Math.max(iFirstIteration,solution.getBestIteration());
        if (idle>iMaxIdleIterations) {
            sLog.debug("  [tabu]    max idle iterations reached");
            iFirstIteration=-1;
            if (iTabu!=null) iTabu.clear();
            return null;
        }
        if (iTabu!=null && iTabuMaxSize>iTabuMinSize) {
            if (idle==0) {
                iTabu.resize(iTabuMinSize);
            } else if (idle%(iMaxIdleIterations/(iTabuMaxSize-iTabuMinSize))==0) {
                iTabu.resize(Math.min(iTabuMaxSize,iTabu.size()+1));
            }
        }
       
        boolean acceptConflicts = solution.getModel().getBestUnassignedVariables()>0;
        Model model = solution.getModel();
        double bestEval = 0.0;
        Vector best = null;
        for (Enumeration e=model.variables().elements();e.hasMoreElements();) {
            Variable variable = (Variable)e.nextElement();
            Value assigned = variable.getAssignment();
            double assignedVal = (assigned==null?iConflictWeight:iValueWeight*assigned.toDouble());
            for (Enumeration f=variable.values().elements();f.hasMoreElements();) {
                Value value = (Value)f.nextElement();
                if (value.equals(assigned)) continue;
                double eval = iValueWeight*value.toDouble() - assignedVal;
                if (acceptConflicts) {
                    Set conflicts = model.conflictValues(value);
                    for (Iterator i=conflicts.iterator();i.hasNext();) {
                        Value conflict = (Value)i.next();
                        eval -= iValueWeight*conflict.toDouble();
                        eval += iConflictWeight * (1.0+(iStat==null?0.0:iStat.countRemovals(solution.getIteration(), conflict, value)));
                    }
                } else {
                    if (model.inConflict(value)) continue;
                }
                if (iTabu!=null && iTabu.contains(tabuElement(value))) {
                    int un = model.nrUnassignedVariables()-(assigned==null?0:1);
                    if (un>model.getBestUnassignedVariables()) continue;
                    if (un==model.getBestUnassignedVariables() && model.getTotalValue()+eval>=solution.getBestValue()) continue;
                }
                if (best==null || bestEval>eval) {
                    if (best==null)
                        best = new Vector();
                    else
                        best.clear();
                    best.add(value);
                    bestEval = eval;
                } else if (bestEval==eval) {
                    best.add(value);
                }
            }
        }
       
        if (best==null) {
            sLog.debug("  [tabu] --none--");
            iFirstIteration=-1;
            if (iTabu!=null) iTabu.clear();
            return null;
        }
        Value bestVal = (Value)ToolBox.random(best);
       
        if (sLog.isDebugEnabled()) {
            Set conflicts = model.conflictValues(bestVal);
            double wconf = (iStat==null?0.0:iStat.countRemovals(solution.getIteration(), conflicts, bestVal));
            sLog.debug("  [tabu] "+bestVal+" ("+(bestVal.variable().getAssignment()==null?"":"was="+bestVal.variable().getAssignment()+", ")+"val="+bestEval+(conflicts.isEmpty()?"":", conf="+(wconf+conflicts.size())+"/"+conflicts)+")");
        }
       
        if (iTabu!=null)
            iTabu.add(tabuElement(bestVal));

        return new SimpleNeighbour(bestVal.variable(),bestVal);       
    }
   
    /**
     * Value selection
     */
    public Value selectValue(Solution solution, Variable variable) {
        if (iFirstIteration<0)
            iFirstIteration = solution.getIteration();
        long idle = solution.getIteration()-Math.max(iFirstIteration,solution.getBestIteration());
        if (idle>iMaxIdleIterations) {
            sLog.debug("  [tabu]    max idle iterations reached");
            iFirstIteration=-1;
            if (iTabu!=null) iTabu.clear();
            return null;
        }
        if (iTabu!=null && iTabuMaxSize>iTabuMinSize) {
            if (idle==0) {
                iTabu.resize(iTabuMinSize);
            } else if (idle%(iMaxIdleIterations/(iTabuMaxSize-iTabuMinSize))==0) {
                iTabu.resize(Math.min(iTabuMaxSize,iTabu.size()+1));
            }
        }

        Model model = solution.getModel();
        double bestEval = 0.0;
        Vector best = null;

        Value assigned = variable.getAssignment();
        //double assignedVal = (assigned==null?-iConflictWeight:iValueWeight*assigned.toDouble());
        double assignedVal = (assigned==null?iConflictWeight:iValueWeight*assigned.toDouble());
        for (Enumeration f=variable.values().elements();f.hasMoreElements();) {
            Value value = (Value)f.nextElement();
            if (value.equals(assigned)) continue;
            Set conflicts = model.conflictValues(value);
            double eval = iValueWeight*value.toDouble() - assignedVal;
            for (Iterator i=conflicts.iterator();i.hasNext();) {
                Value conflict = (Value)i.next();
                eval -= iValueWeight*conflict.toDouble();
                eval += iConflictWeight * (1.0+(iStat==null?0.0:iStat.countRemovals(solution.getIteration(), conflict, value)));
            }
            if (iTabu!=null && iTabu.contains(tabuElement(value))) {
                //if (model.getTotalValue()+eval>=solution.getBestValue()) continue;
                int un = model.nrUnassignedVariables()-(assigned==null?0:1);
                if (un>model.getBestUnassignedVariables()) continue;
                if (un==model.getBestUnassignedVariables() && model.getTotalValue()+eval>=solution.getBestValue()) continue;
        }
            if (best==null || bestEval>eval) {
                if (best==null)
                    best = new Vector();
                else
                    best.clear();
                best.add(value);
                bestEval = eval;
            } else if (bestEval==eval) {
                best.add(value);
            }
        }
       
        if (best==null) return null;
        Value bestVal = (Value)ToolBox.random(best);
       
        if (sLog.isDebugEnabled()) {
            Set conflicts = model.conflictValues(bestVal);
            double wconf = (iStat==null?0.0:iStat.countRemovals(solution.getIteration(), conflicts, bestVal));
            sLog.debug("  [tabu] "+bestVal+" ("+(bestVal.variable().getAssignment()==null?"":"was="+bestVal.variable().getAssignment()+", ")+"val="+bestEval+(conflicts.isEmpty()?"":", conf="+(wconf+conflicts.size())+"/"+conflicts)+")");
        }
       
        if (iTabu!=null) iTabu.add(tabuElement(bestVal));
       
        return bestVal;
    }

   
    /** Tabu-list */
    private static class TabuList {
        private HashSet iList = new HashSet();
        private int iSize;
        private long iIteration = 0;
       
        public TabuList(int size) {
            iSize = size;
        }
       
        public Object add(Object object) {
            if (iSize==0) return object;
            if (contains(object)) {
                iList.remove(new TabuItem(object, 0));
                iList.add(new TabuItem(object, iIteration++));
                return null;
            } else {
                Object oldest = null;
                if (iList.size()>=iSize) oldest = removeOldest();
                iList.add(new TabuItem(object, iIteration++));
                return oldest;
            }
        }
       
        public void resize(int newSize) {
            iSize = newSize;
            while (iList.size()>newSize) removeOldest();
        }
       
        public boolean contains(Object object) {
            return iList.contains(new TabuItem(object,0));
        }
       
        public void clear() {
            iList.clear();
        }
       
        public int size() {
            return iSize;
        }
       
        public Object removeOldest() {
            TabuItem oldest = null;
            for (Iterator i=iList.iterator();i.hasNext();) {
                TabuItem element = (TabuItem)i.next();
                if (oldest==null || oldest.getIteration()>element.getIteration())
                    oldest = element;
            }
            if (oldest==null) return null;
            iList.remove(oldest);
            return oldest.getObject();
        }
       
        public String toString() {
            return new TreeSet(iList).toString();
        }
    }

    /** Tabu item (an item in {@link TabuList}) */
    private static class TabuItem implements Comparable {
        private Object iObject;
        private long iIteration;
        public TabuItem(Object object, long iteration) {
            iObject = object; iIteration = iteration;
        }
        public Object getObject() {
            return iObject;
        }
        public long getIteration() {
            return iIteration;
        }
        public boolean equals(Object object) {
            if (object==null || !(object instanceof TabuItem)) return false;
            return getObject().equals(((TabuItem)object).getObject());
        }
        public int hashCode() {
            return getObject().hashCode();
        }
        public int compareTo(Object o) {
            return Double.compare(getIteration(), ((TabuItem)o).getIteration());
        }
        public String toString() {
            return getObject().toString();
        }
    }
   
    /** This interface is used to populate and check tabu list when
     * implemented by a value (instead of the value itself).
     * This way, e.g., all assignments of a class into the same time (but
     * various rooms) may be considered the same for the tabu list.
     */
    public static interface TabuElement {
        /** Tabu element of a value */
        public Object tabuElement();
    }
}
TOP

Related Classes of net.sf.cpsolver.itc.heuristics.search.ItcTabuSearch

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.