001package org.cpsolver.ifs.heuristics; 002 003import java.util.ArrayList; 004import java.util.HashMap; 005import java.util.List; 006import java.util.Map; 007 008import org.apache.logging.log4j.Logger; 009import org.cpsolver.ifs.model.InfoProvider; 010import org.cpsolver.ifs.model.Neighbour; 011import org.cpsolver.ifs.model.Value; 012import org.cpsolver.ifs.model.Variable; 013import org.cpsolver.ifs.solution.Solution; 014import org.cpsolver.ifs.solver.Solver; 015import org.cpsolver.ifs.util.DataProperties; 016import org.cpsolver.ifs.util.Progress; 017 018/** 019 * A round robin neighbour selection. Two or more {@link NeighbourSelection} 020 * needs to be registered within the selection. This selection criterion takes 021 * the registered neighbour selections one by one and performs 022 * {@link NeighbourSelection#init(Solver)} and then it is using 023 * {@link NeighbourSelection#selectNeighbour(Solution)} to select a neighbour. 024 * When null is returned by the underlaying selection, next registered neighbour 025 * selection is initialized and used for the following selection(s). If the last 026 * registered selection returns null, the selection is returned to the first 027 * registered neighbour selection (it is initialized before used again). 028 * 029 * <br> 030 * <br> 031 * 032 * @author Tomáš Müller 033 * @version StudentSct 1.3 (Student Sectioning)<br> 034 * Copyright (C) 2007 - 2014 Tomáš Müller<br> 035 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 036 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 037 * <br> 038 * This library is free software; you can redistribute it and/or modify 039 * it under the terms of the GNU Lesser General Public License as 040 * published by the Free Software Foundation; either version 3 of the 041 * License, or (at your option) any later version. <br> 042 * <br> 043 * This library is distributed in the hope that it will be useful, but 044 * WITHOUT ANY WARRANTY; without even the implied warranty of 045 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 046 * Lesser General Public License for more details. <br> 047 * <br> 048 * You should have received a copy of the GNU Lesser General Public 049 * License along with this library; if not see 050 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 051 * 052 * @param <V> Variable 053 * @param <T> Value 054 */ 055public class RoundRobinNeighbourSelection<V extends Variable<V, T>, T extends Value<V, T>> extends StandardNeighbourSelection<V, T> { 056 protected static Logger sLogger = org.apache.logging.log4j.LogManager.getLogger(RoundRobinNeighbourSelection.class); 057 private int iSelectionIdx = -1; 058 private List<NeighbourSelection<V, T>> iSelections = new ArrayList<NeighbourSelection<V, T>>(); 059 protected Solver<V, T> iSolver = null; 060 061 /** 062 * Constructor 063 * 064 * @param properties 065 * configuration 066 * @throws Exception thrown when initialization fails 067 */ 068 public RoundRobinNeighbourSelection(DataProperties properties) throws Exception { 069 super(properties); 070 } 071 072 /** Register a neighbour selection 073 * @param selection a neighbour selection to include in the selection 074 **/ 075 public void registerSelection(NeighbourSelection<V, T> selection) { 076 iSelections.add(selection); 077 } 078 079 /** Initialization */ 080 @Override 081 public void init(Solver<V, T> solver) { 082 super.init(solver); 083 iSolver = solver; 084 } 085 086 /** 087 * Select neighbour. A first registered selections is initialized and used 088 * until it returns null, then the second registered selections is 089 * initialized and used and vice versa. 090 */ 091 @Override 092 public Neighbour<V, T> selectNeighbour(Solution<V, T> solution) { 093 int nrChanges = 0; 094 while (nrChanges <= iSelections.size()) { 095 int selectionIndex = getSelectionIndex(); 096 NeighbourSelection<V, T> selection = iSelections.get(selectionIndex % iSelections.size()); 097 Neighbour<V, T> neighbour = selection.selectNeighbour(solution); 098 if (neighbour != null) 099 return neighbour; 100 changeSelection(selectionIndex); 101 nrChanges ++; 102 } 103 return null; 104 } 105 106 public int getSelectionIndex() { 107 if (iSelectionIdx == -1) changeSelection(-1); 108 iSolver.currentSolution().getLock().readLock().lock(); 109 try { 110 return iSelectionIdx; 111 } finally { 112 iSolver.currentSolution().getLock().readLock().unlock(); 113 } 114 } 115 116 /** Change selection 117 * @param selectionIndex current selection index 118 **/ 119 @SuppressWarnings("unchecked") 120 public void changeSelection(int selectionIndex) { 121 iSolver.currentSolution().getLock().writeLock().lock(); 122 try { 123 Progress progress = Progress.getInstance(iSolver.currentSolution().getModel()); 124 int newSelectionIndex = 1 + selectionIndex; 125 if (newSelectionIndex <= iSelectionIdx) return; // already changed 126 if (selectionIndex == -1 && iSelectionIdx >= 0) return; // already changed 127 iSelectionIdx = newSelectionIndex; 128 if (selectionIndex >= 0) { 129 try { 130 NeighbourSelection<V, T> selection = iSelections.get(selectionIndex % iSelections.size()); 131 if (selection instanceof InfoProvider) { 132 Map<String, String> info = new HashMap<String, String>(); 133 ((InfoProvider<V, T>)selection).getInfo(iSolver.currentSolution().getAssignment(), info); 134 if (!info.isEmpty()) 135 for (Map.Entry<String, String> e: info.entrySet()) 136 progress.debug(e.getKey() + ": " + e.getValue()); 137 } 138 } catch (Exception e) {} 139 } 140 sLogger.info("Phase changed to " + ((newSelectionIndex % iSelections.size()) + 1)); 141 progress.debug(iSolver.currentSolution().toString()); 142 if (iSolver.currentSolution().getBestInfo() == null || iSolver.getSolutionComparator().isBetterThanBestSolution(iSolver.currentSolution())) 143 iSolver.currentSolution().saveBest(); 144 iSelections.get(iSelectionIdx % iSelections.size()).init(iSolver); 145 } finally { 146 iSolver.currentSolution().getLock().writeLock().unlock(); 147 } 148 } 149 150 public NeighbourSelection<V, T> getSelection() { 151 return iSelections.get(getSelectionIndex() % iSelections.size()); 152 } 153}