1 /*
2  * ArgMin.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  * ArgMin 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 ArgMin 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 minIndex;
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 minIndex    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      */
ArgMin(IntVar[] list, IntVar minIndex, int indexOffset)78     public ArgMin(IntVar[] list, IntVar minIndex, int indexOffset) {
79 
80         this(list, minIndex);
81         this.indexOffset = indexOffset;
82     }
83 
ArgMin(IntVar[] list, IntVar minIndex)84     public ArgMin(IntVar[] list, IntVar minIndex) {
85 
86         checkInputForNullness(new String[] {"list", "minIndex"}, new Object[][] {list, {minIndex}});
87 
88         this.queueIndex = 1;
89         this.numberId = idNumber.incrementAndGet();
90         this.indexOffset = 0;
91         this.minIndex = minIndex;
92         this.list = Arrays.copyOf(list, list.length);
93 
94         setScope(Stream.concat(Stream.of(minIndex), Stream.of(list)));
95     }
96 
97     /**
98      * It constructs max constraint.
99      *
100      * @param minIndex    variable denoting the index of minimum value
101      * @param variables   the array of variables for which the minimum value is imposed.
102      * @param indexOffset the offset for the index that is computed from 1 by default (if needed from 0, use -1 for this parameter)
103      */
ArgMin(List<? extends IntVar> variables, IntVar minIndex, int indexOffset)104     public ArgMin(List<? extends IntVar> variables, IntVar minIndex, int indexOffset) {
105         this(variables, minIndex);
106         this.indexOffset = indexOffset;
107     }
108 
ArgMin(List<? extends IntVar> variables, IntVar minIndex)109     public ArgMin(List<? extends IntVar> variables, IntVar minIndex) {
110 
111         this(variables.toArray(new IntVar[variables.size()]), minIndex);
112 
113     }
114 
consistency(Store store)115     @Override public void consistency(Store store) {
116 
117         if (firstConsistencyCheck) {
118             minIndex.domain.in(store.level, minIndex, 1 + indexOffset, list.length + indexOffset);
119             firstConsistencyCheck = false;
120         }
121 
122 	int lb = IntDomain.MaxInt;
123 	int ub = IntDomain.MaxInt;
124 	int pos = -1;
125 
126 	// find lower/upper bounds for elements on list
127 	for (int i = 0; i < list.length; i++) {
128 
129 	    int vDomMin = list[i].dom().min();
130 	    if (lb > vDomMin) {
131 		lb = vDomMin;
132 	    }
133 
134 	    int vDomMax = list[i].dom().max();
135 	    if (ub > vDomMax) {
136 		ub = vDomMax;
137 		pos = i;
138 	    }
139 	}
140 
141 	if (lb == ub)
142 	    minIndex.domain.inMax(store.level, minIndex, pos + 1 + indexOffset);
143 
144 	// find min/max values for index
145 	IntervalDomain idxDomain = new IntervalDomain();
146 	for (int i = 0; i < list.length; i++) {
147 	    int cp = i + 1 + indexOffset;
148 
149 	    if (list[i].min() <= ub) {
150 		if (idxDomain.getSize() == 0)
151 		    idxDomain.unionAdapt(cp, cp);
152 		else
153 		    idxDomain.addLastElement(cp);
154 	    }
155 	}
156 	if (idxDomain.isEmpty())
157 	    throw Store.failException;
158 	else
159 	    minIndex.domain.in(store.level, minIndex, idxDomain);
160 
161 	// find min value for variables indexed by index variable
162 	lb = IntDomain.MaxInt;
163 	pos = -1;
164 	for (ValueEnumeration e = minIndex.dom().valueEnumeration(); e.hasMoreElements(); ) {
165 	    int i = e.nextElement() - 1 - indexOffset;
166 
167 	    int vDomMin = list[i].dom().min();
168 	    if (lb > vDomMin) {
169 		lb = vDomMin;
170 		pos = i;
171 	    }
172 	}
173 	if (list[pos].singleton())
174 	    minIndex.domain.in(store.level, minIndex, pos + 1 + indexOffset, pos + 1 + indexOffset);
175 
176 	if (minIndex.singleton()) {
177 
178 	    int idx = minIndex.value() - 1 - indexOffset;
179 	    IntVar y = list[idx];
180 
181 	    for (int i = 0; i < list.length; i++) {
182 
183 		// prune variables before and after index of max value
184 		IntVar x = list[i];
185 		if (i < idx) {
186 		    // x > y
187 		    x.domain.inMin(store.level, x, y.min() + 1);
188 		    y.domain.inMax(store.level, y, x.max() - 1);
189 		}
190 		else {
191 		    // x >= y
192 		    x.domain.inMin(store.level, x, y.min());
193 		    y.domain.inMax(store.level, y, x.max());
194 		}
195 	    }
196 	} else {
197 	    // prune values on the list
198 	    int im = minIndex.min();
199 	    for (int i = 0; i < list.length; i++) {
200 		int cp = i + 1 + indexOffset;
201 
202 		if (cp < im)
203 		    list[i].domain.inMin(store.level, list[i], lb + 1);
204 		else if (cp > im)
205 		    list[i].domain.inMin(store.level, list[i], lb);
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 == minIndex)
227             return IntDomain.ANY;
228         else {
229 	    return IntDomain.BOUND;
230 	}
231     }
232 
satisfied()233     @Override public boolean satisfied() {
234 
235         boolean sat = minIndex.singleton();
236 
237         if (!sat)
238             return false;
239 
240         int MIN = list[minIndex.value() - 1 - indexOffset].value();
241         int i = 0, eq = 0;
242         while (sat && i < list.length) {
243             if (list[i].singleton() && list[i].value() >= MIN)
244                 eq++;
245             sat = list[i].min() >= MIN;
246             i++;
247         }
248 
249         return sat && eq == list.length;
250     }
251 
toString()252     @Override public String toString() {
253 
254         StringBuffer result = new StringBuffer(id());
255 
256         result.append(" : ArgMin(  [ ");
257         for (int i = 0; i < list.length; i++) {
258             result.append(list[i]);
259             if (i < list.length - 1)
260                 result.append(", ");
261         }
262 
263         result.append("], ").append(this.minIndex);
264         result.append(", "+indexOffset+")");
265 
266         return result.toString();
267     }
268 
269 }
270