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