001package org.cpsolver.ifs.heuristics; 002 003import java.util.ArrayList; 004import java.util.Iterator; 005import java.util.List; 006import java.util.Set; 007 008import org.cpsolver.ifs.extension.Extension; 009import org.cpsolver.ifs.extension.MacPropagation; 010import org.cpsolver.ifs.model.Value; 011import org.cpsolver.ifs.model.Variable; 012import org.cpsolver.ifs.solution.Solution; 013import org.cpsolver.ifs.solver.Solver; 014import org.cpsolver.ifs.util.DataProperties; 015import org.cpsolver.ifs.util.ToolBox; 016 017 018/** 019 * General implementation of variable selection criterion. <br> 020 * <br> 021 * In case that all variables are assigned, one of the variables is selected 022 * randomly. In case of MPP, the random selection is made among the variables 023 * which have not assigned initial values. <br> 024 * <br> 025 * When there are unassigned variables, a variable is selected randomly among 026 * all unassigned variables (when Variable.RandomSelection is true) or the 027 * following roulette wheel selection takes place (MPP): 028 * <ul> 029 * <li>one point for a variable with no initial assignment 030 * <li>3 * ( 1 + number of conflicts with the initial assignment) for a variable 031 * with an initial assignment 032 * </ul> 033 * <br> 034 * If {@link MacPropagation} is used and Variable.UnassignWhenNoGood parameter 035 * is true, while there is a variable with an empty domain: 036 * <ul> 037 * <li>with Variable.UnassignWhenNoGoodRandomWalk probabilty an arbitrary 038 * assigned variable is selected 039 * <li>otherwise, one variable with empty domain is picked, one of its original 040 * values is picked and one of the variables from the explanation of that value 041 * is then returned. If the explanation is empty, another variable and value is 042 * tried (up to ten times). 043 * </ul> 044 * <br> 045 * Parameters: <br> 046 * <table border='1' summary='Related Solver Parameters'> 047 * <tr> 048 * <th>Parameter</th> 049 * <th>Type</th> 050 * <th>Comment</th> 051 * </tr> 052 * <tr> 053 * <td>Variable.RandomSelection</td> 054 * <td>{@link Boolean}</td> 055 * <td>if true, an unassigned variable is picked randomly</td> 056 * </tr> 057 * <tr> 058 * <td>Variable.UnassignWhenNoGood</td> 059 * <td>{@link Boolean}</td> 060 * <td>if true and if {@link MacPropagation} is used: if there is a variable 061 * with empty domain, assigned variable (which is present in some explanation 062 * for a vairable with empty domain) is selected (for reassignment)</td> 063 * </tr> 064 * <tr> 065 * <td>Variable.UnassignWhenNoGoodRandomWalk</td> 066 * <td>{@link Double}</td> 067 * <td>if Variable.UnassignWhenNoGood is true and if {@link MacPropagation} is 068 * used: if there is a variable with empty domain, with the given probability an 069 * arbitrary assigned variable is selected</td> 070 * </tr> 071 * </table> 072 * 073 * @see VariableSelection 074 * @see Solver 075 * 076 * @version IFS 1.3 (Iterative Forward Search)<br> 077 * Copyright (C) 2006 - 2014 Tomáš Müller<br> 078 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 079 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 080 * <br> 081 * This library is free software; you can redistribute it and/or modify 082 * it under the terms of the GNU Lesser General Public License as 083 * published by the Free Software Foundation; either version 3 of the 084 * License, or (at your option) any later version. <br> 085 * <br> 086 * This library is distributed in the hope that it will be useful, but 087 * WITHOUT ANY WARRANTY; without even the implied warranty of 088 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 089 * Lesser General Public License for more details. <br> 090 * <br> 091 * You should have received a copy of the GNU Lesser General Public 092 * License along with this library; if not see <a href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>. 093 * 094 * @param <V> Variable 095 * @param <T> Value 096 **/ 097public class GeneralVariableSelection<V extends Variable<V, T>, T extends Value<V, T>> implements VariableSelection<V, T> { 098 private boolean iUnassignWhenNotGood = false; 099 private double iUnassignWhenNotGoodRandWalk = 0.02; 100 private boolean iRandomSelection = true; 101 102 private MacPropagation<V, T> iProp = null; 103 104 /** 105 * Constructor 106 * 107 * @param properties 108 * input configuration 109 */ 110 public GeneralVariableSelection(DataProperties properties) { 111 iUnassignWhenNotGood = properties.getPropertyBoolean("Variable.UnassignWhenNoGood", iUnassignWhenNotGood); 112 iUnassignWhenNotGoodRandWalk = properties.getPropertyDouble("Variable.UnassignWhenNoGoodRandomWalk", 113 iUnassignWhenNotGoodRandWalk); 114 iRandomSelection = properties.getPropertyBoolean("Variable.RandomSelection", iRandomSelection); 115 } 116 117 public GeneralVariableSelection() { 118 } 119 120 /** Initialization */ 121 @Override 122 public void init(Solver<V, T> solver) { 123 for (Extension<V, T> extension : solver.getExtensions()) { 124 if (MacPropagation.class.isInstance(extension)) 125 iProp = (MacPropagation<V, T>) extension; 126 } 127 } 128 129 /** Variable selection */ 130 @Override 131 public V selectVariable(Solution<V, T> solution) { 132 if (solution.getModel().variables().size() == solution.getAssignment().nrAssignedVariables()) { 133 if (!solution.getModel().perturbVariables(solution.getAssignment()).isEmpty()) 134 return ToolBox.random(solution.getModel().perturbVariables(solution.getAssignment())); 135 else 136 return ToolBox.random(solution.getAssignment().assignedVariables()); 137 } else { 138 if (iProp != null && iUnassignWhenNotGood) { 139 List<V> noGoodVariables = new ArrayList<V>(); 140 for (V variable : solution.getAssignment().unassignedVariables(solution.getModel())) { 141 if (iProp.goodValues(solution.getAssignment(), variable).isEmpty()) 142 noGoodVariables.add(variable); 143 } 144 if (!noGoodVariables.isEmpty()) { 145 if (ToolBox.random() < iUnassignWhenNotGoodRandWalk) 146 return ToolBox.random(solution.getAssignment().assignedVariables()); 147 for (int attempt = 0; attempt < 10; attempt++) { 148 V noGoodVariable = ToolBox.random(noGoodVariables); 149 T noGoodValue = ToolBox.random(noGoodVariable.values(solution.getAssignment())); 150 Set<T> noGood = iProp.noGood(solution.getAssignment(), noGoodValue); 151 if (noGood != null && !noGood.isEmpty()) 152 return ToolBox.random(noGood).variable(); 153 } 154 } 155 } 156 if (iRandomSelection) 157 return ToolBox.random(solution.getAssignment().unassignedVariables(solution.getModel())); 158 List<Integer> points = new ArrayList<Integer>(); 159 int totalPoints = 0; 160 for (V variable : solution.getAssignment().unassignedVariables(solution.getModel())) { 161 int pointsThisVariable = (variable.getInitialAssignment() != null ? 3 * (1 + solution.getModel().conflictValues(solution.getAssignment(), variable.getInitialAssignment()).size()) : 1); 162 totalPoints += pointsThisVariable; 163 points.add(totalPoints); 164 } 165 int rndPoints = ToolBox.random(totalPoints); 166 Iterator<V> x = solution.getAssignment().unassignedVariables(solution.getModel()).iterator(); 167 for (int i = 0; x.hasNext() && i < points.size(); i++) { 168 V variable = x.next(); 169 int tp = points.get(i); 170 if (tp > rndPoints) 171 return variable; 172 } 173 return ToolBox.random(solution.getAssignment().unassignedVariables(solution.getModel())); 174 } 175 } 176 177}