1 /* 2 * ArgMax.java 3 * This file is part of JaCoP. 4 * <p> 5 * JaCoP is a Java Constraint Programming solver. 6 * <p> 7 * Copyright (C) 2000-2008 Krzysztof Kuchcinski and Radoslaw Szymanek 8 * <p> 9 * This program is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU Affero General Public License as published by 11 * the Free Software Foundation, either version 3 of the License, or 12 * (at your option) any later version. 13 * <p> 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU Affero General Public License for more details. 18 * <p> 19 * Notwithstanding any other provision of this License, the copyright 20 * owners of this work supplement the terms of this License with terms 21 * prohibiting misrepresentation of the origin of this work and requiring 22 * that modified versions of this work be marked in reasonable ways as 23 * different from the original version. This supplement of the license 24 * terms is in accordance with Section 7 of GNU Affero General Public 25 * License version 3. 26 * <p> 27 * You should have received a copy of the GNU Affero General Public License 28 * along with this program. If not, see <http://www.gnu.org/licenses/>. 29 */ 30 31 package org.jacop.constraints; 32 33 import org.jacop.api.SatisfiedPresent; 34 import org.jacop.core.*; 35 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.concurrent.atomic.AtomicInteger; 39 import java.util.stream.Stream; 40 41 /** 42 * ArgMax constraint provides the index of the maximum 43 * variable from all variables on the list. 44 * 45 * @author Krzysztof Kuchcinski and Radoslaw Szymanek 46 * @version 4.8 47 */ 48 49 public class ArgMax extends Constraint implements SatisfiedPresent { 50 51 final static AtomicInteger idNumber = new AtomicInteger(0); 52 53 boolean firstConsistencyCheck = true; 54 55 /** 56 * It specifies a list of variables among which a maximum value is being searched for. 57 */ 58 final public IntVar list[]; 59 60 /** 61 * It specifies variable max which stores the maximum value present in the list. 62 */ 63 final public IntVar maxIndex; 64 65 66 /** 67 * It specifies indexOffset within an element constraint list[index-indexOffset] = value. 68 */ 69 public int indexOffset; 70 71 /** 72 * It constructs max constraint. 73 * 74 * @param maxIndex variable denoting the index of the maximum value 75 * @param list the array of variables for which the index of the maximum value is imposed. 76 * @param indexOffset the offset for the index that is computed from 1 by default (if needed from 0, use -1 for this parameter) 77 */ ArgMax(IntVar[] list, IntVar maxIndex, int indexOffset)78 public ArgMax(IntVar[] list, IntVar maxIndex, int indexOffset) { 79 this(list, maxIndex); 80 this.indexOffset = indexOffset; 81 } 82 ArgMax(IntVar[] list, IntVar maxIndex)83 public ArgMax(IntVar[] list, IntVar maxIndex) { 84 85 checkInputForNullness(new String[] {"list", "maxIndex"}, new Object[][] {list, {maxIndex}}); 86 87 this.queueIndex = 1; 88 this.numberId = idNumber.incrementAndGet(); 89 this.indexOffset = 0; 90 this.maxIndex = maxIndex; 91 this.list = Arrays.copyOf(list, list.length); 92 93 setScope(Stream.concat(Arrays.stream(list), Stream.of(maxIndex))); 94 } 95 96 /** 97 * It constructs max constraint. 98 * 99 * @param maxIndex variable denoting index of the maximum value 100 * @param variables the array of variables for which the maximum value is imposed. 101 * @param indexOffset the offset for the index that is computed from 1 by default (if needed from 0, use -1 for this parameter) 102 */ ArgMax(List<? extends IntVar> variables, IntVar maxIndex, int indexOffset)103 public ArgMax(List<? extends IntVar> variables, IntVar maxIndex, int indexOffset) { 104 this(variables, maxIndex); 105 this.indexOffset = indexOffset; 106 } 107 ArgMax(List<? extends IntVar> variables, IntVar maxIndex)108 public ArgMax(List<? extends IntVar> variables, IntVar maxIndex) { 109 this(variables.toArray(new IntVar[variables.size()]), maxIndex); 110 } 111 consistency(Store store)112 @Override public void consistency(Store store) { 113 114 if (firstConsistencyCheck) { 115 maxIndex.domain.in(store.level, maxIndex, 1 + indexOffset, list.length + indexOffset); 116 firstConsistencyCheck = false; 117 } 118 119 int lb = IntDomain.MinInt; 120 int ub = IntDomain.MinInt; 121 int pos = -1; 122 123 // find lower/upper bounds for indexed elements on list 124 for (ValueEnumeration e = maxIndex.dom().valueEnumeration(); e.hasMoreElements(); ) { 125 int cp = e.nextElement(); 126 int i = cp - 1 - indexOffset; 127 128 int vDomMin = list[i].min(); 129 if (lb < vDomMin) { 130 lb = vDomMin; 131 pos = i; 132 } 133 134 int vDomMax = list[i].max(); 135 if (ub < vDomMax) { 136 ub = vDomMax; 137 } 138 } 139 if (lb == ub) 140 maxIndex.domain.inMax(store.level, maxIndex, pos + 1 + indexOffset); 141 142 // find min/max values for index 143 IntervalDomain idxDomain = new IntervalDomain(); 144 for (ValueEnumeration e = maxIndex.dom().valueEnumeration(); e.hasMoreElements(); ) { 145 int cp = e.nextElement(); 146 int i = cp - 1 - indexOffset; 147 148 if (list[i].max() >= lb) { 149 if (idxDomain.getSize() == 0) 150 idxDomain.unionAdapt(cp, cp); 151 else 152 idxDomain.addLastElement(cp); 153 } 154 } 155 if (idxDomain.isEmpty()) 156 throw Store.failException; 157 else 158 maxIndex.domain.in(store.level, maxIndex, idxDomain); 159 160 ub = IntDomain.MinInt; 161 pos = -1; 162 for (ValueEnumeration e = maxIndex.dom().valueEnumeration(); e.hasMoreElements(); ) { 163 int i = e.nextElement() - 1 - indexOffset; 164 165 int vDomMax = list[i].max(); 166 if (ub < vDomMax) { 167 ub = vDomMax; 168 pos = i; 169 } 170 } 171 if (list[pos].singleton()) 172 maxIndex.domain.in(store.level, maxIndex, pos + 1 + indexOffset, pos + 1 + indexOffset); 173 174 if (maxIndex.singleton()) { 175 176 int idx = maxIndex.value() - 1 - indexOffset; 177 IntVar y = list[idx]; 178 179 for (int i = 0; i < list.length; i++) { 180 181 // prune variables before and after index of max value 182 IntVar x = list[i]; 183 if (i < idx) { 184 // x < y 185 x.domain.inMax(store.level, x, y.max() - 1); 186 y.domain.inMin(store.level, y, x.min() + 1); 187 } 188 else { 189 // x <= y 190 x.domain.inMax(store.level, x, y.max()); 191 y.domain.inMin(store.level, y, x.min()); 192 } 193 } 194 } else { 195 // prune values on the list 196 int im = maxIndex.min(); 197 for (int i = 0; i < list.length; i++) { 198 int cp = i + 1 + indexOffset; 199 200 // prune variables before and after minimal index of max value 201 IntVar v = list[i]; 202 if (cp < im) 203 v.domain.inMax(store.level, v, ub - 1); 204 else 205 v.domain.inMax(store.level, v, ub); 206 } 207 } 208 209 // if (maxIndex.singleton() && list[maxIndex.value() - 1 - indexOffset].singleton()) 210 // removeConstraint(); 211 } 212 getDefaultConsistencyPruningEvent()213 @Override public int getDefaultConsistencyPruningEvent() { 214 return IntDomain.BOUND; 215 } 216 getConsistencyPruningEvent(Var var)217 @Override public int getConsistencyPruningEvent(Var var) { 218 219 // If consistency function mode 220 if (consistencyPruningEvents != null) { 221 Integer possibleEvent = consistencyPruningEvents.get(var); 222 if (possibleEvent != null) 223 return possibleEvent; 224 } 225 226 if (var == maxIndex) 227 return IntDomain.ANY; 228 else { 229 return IntDomain.BOUND; 230 } 231 } 232 satisfied()233 @Override public boolean satisfied() { 234 235 boolean sat = maxIndex.singleton(); 236 237 int MAX = list[maxIndex.value() - 1 - indexOffset].value(); 238 int i = 0, eq = 0; 239 while (sat && i < list.length) { 240 if (list[i].singleton() && list[i].value() <= MAX) 241 eq++; 242 sat = list[i].max() <= MAX; 243 i++; 244 } 245 246 return sat && eq == list.length; 247 } 248 toString()249 @Override public String toString() { 250 251 StringBuilder result = new StringBuilder(id()); 252 253 result.append(" : ArgMax( [ "); 254 for (int i = 0; i < list.length; i++) { 255 result.append(list[i]); 256 if (i < list.length - 1) 257 result.append(", "); 258 } 259 260 result.append("], ").append(this.maxIndex); 261 result.append(", "+indexOffset+")"); 262 263 return result.toString(); 264 } 265 266 } 267