001package org.clafer.choco.constraint.propagator; 002 003import org.clafer.collection.Triple; 004import org.clafer.common.Util; 005import solver.constraints.Propagator; 006import solver.constraints.PropagatorPriority; 007import solver.exception.ContradictionException; 008import solver.variables.EventType; 009import solver.variables.IntVar; 010import solver.variables.SetVar; 011import solver.variables.Variable; 012import util.ESat; 013 014/** 015 * Sums a set and |set| &le n. This implementation <b>assumes</b> that the 016 * envelope and kernel are sorted, undefined behaviour otherwise. 017 * 018 * @author jimmy 019 */ 020public class PropSetSum extends Propagator<Variable> { 021 022 private final SetVar set; 023 private final IntVar setCard; 024 private final IntVar sum; 025 026 public PropSetSum(SetVar set, IntVar setCard, IntVar sum) { 027 super(new Variable[]{set, setCard, sum}, PropagatorPriority.LINEAR, false); 028 this.set = set; 029 this.setCard = setCard; 030 this.sum = sum; 031 } 032 033 private boolean isSetVar(int idx) { 034 return idx == 0; 035 } 036 037 private boolean isSetCardVar(int idx) { 038 return idx == 1; 039 } 040 041 private boolean isSumVar(int idx) { 042 return idx == 2; 043 } 044 045 @Override 046 public int getPropagationConditions(int vIdx) { 047 if (isSetVar(vIdx)) { 048 return EventType.ADD_TO_KER.mask + EventType.REMOVE_FROM_ENVELOPE.mask; 049 } 050 assert isSumVar(vIdx) || isSetCardVar(vIdx); 051 return EventType.INSTANTIATE.mask + EventType.BOUND.mask; 052 } 053 054 private Triple<int[], int[], int[]> findKerSmallestLargest(int n) { 055 final int[] ker = PropUtil.iterateKer(set); 056 final int[] smallest = new int[n]; 057 final int[] largest = new int[n]; 058 059 int chooseSize = set.getEnvelopeSize() - ker.length; 060 int highStart = chooseSize - n; 061 062 int index = 0; 063 for (int i = set.getEnvelopeFirst(); i != SetVar.END; i = set.getEnvelopeNext()) { 064 if (!Util.in(i, ker)) { 065 if (index < smallest.length) { 066 smallest[index] = i; 067 } 068 index++; 069 if (index > highStart) { 070 largest[chooseSize - index] = i; 071 } 072 } 073 } 074 return new Triple<>(ker, smallest, largest); 075 } 076 077 @Override 078 public void propagate(int evtmask) throws ContradictionException { 079 boolean changed; 080 do { 081 changed = false; 082 final int envSize = set.getEnvelopeSize(); 083 final int kerSize = set.getKernelSize(); 084 085 setCard.updateLowerBound(kerSize, aCause); 086 setCard.updateUpperBound(envSize, aCause); 087 088 int lbEnd = setCard.getLB() - kerSize; 089 int ubEnd = setCard.getUB() - kerSize; 090 091 final Triple<int[], int[], int[]> triple = findKerSmallestLargest(ubEnd); 092 int[] ker = triple.getFst(); 093 int[] smallest = triple.getSnd(); 094 int[] largest = triple.getThd(); 095 096 int kerSum = Util.sum(ker); 097 int low = kerSum; 098 int lowEnd; 099 int lowCandidate = 0; // Remove this value when adding another. 100 for (lowEnd = 0; lowEnd < ubEnd; lowEnd++) { 101 int val = smallest[lowEnd]; 102 if (!(lowEnd < lbEnd || val < 0)) { 103 // Still room left. Don't remove lowCandidate if it's negative. 104 lowCandidate = Math.max(lowCandidate, 0); 105 break; 106 } 107 lowCandidate = val; 108 low += val; 109 } 110 int high = kerSum; 111 int highStart; 112 int highCandidate = 0; // Remove this value when adding another. 113 for (highStart = 0; highStart < ubEnd; highStart++) { 114 int val = largest[highStart]; 115 if (!(highStart < lbEnd || val > 0)) { 116 // Still room left. Don't remove highCandidate if it's positive. 117 highCandidate = Math.min(highCandidate, 0); 118 break; 119 } 120 highCandidate = val; 121 high += val; 122 } 123 highStart = envSize - kerSize - highStart; 124 125 sum.updateLowerBound(low, aCause); 126 sum.updateUpperBound(high, aCause); 127 128 int lb = sum.getLB(); 129 int ub = sum.getUB(); 130 131 int index = 0; 132 for (int i = set.getEnvelopeFirst(); i != SetVar.END; i = set.getEnvelopeNext()) { 133 if (!Util.in(i, ker)) { 134 if (index >= lowEnd) { 135 int val = low + i - lowCandidate; 136 // Adding i will never be under the upper bound. 137 if (val > ub) { 138 changed |= set.removeFromEnvelope(i, aCause); 139 } 140 } 141 if (index < highStart) { 142 int val = high + i - highCandidate; 143 // Adding i will never be over the lower bound. 144 if (val < lb) { 145 changed |= set.removeFromEnvelope(i, aCause); 146 } 147 } 148 index++; 149 } 150 } 151 } while (changed); 152 } 153 154 @Override 155 public void propagate(int idxVarInProp, int mask) throws ContradictionException { 156 forcePropagate(EventType.FULL_PROPAGATION); 157 } 158 159 @Override 160 public ESat isEntailed() { 161 int envSize = set.getEnvelopeSize(); 162 int kerSize = set.getKernelSize(); 163 if (kerSize > setCard.getUB()) { 164 return ESat.FALSE; 165 } 166 int[] ker = PropUtil.iterateKer(set); 167 int low = 0; 168 int high = 0; 169 // The number of elements seen in env but not in ker. 170 int index = 0; 171 int lowEnd = Math.max(setCard.getLB(), envSize) - kerSize; 172 int highStart = envSize - kerSize - lowEnd; 173 for (int i = set.getEnvelopeFirst(); i != SetVar.END; i = set.getEnvelopeNext()) { 174 if (Util.in(i, ker)) { 175 low += i; 176 high += i; 177 } else { 178 if (index < lowEnd && i < 0) { 179 low += i; 180 } 181 if (index >= highStart && i > 0) { 182 high += i; 183 } 184 index++; 185 } 186 } 187 if (sum.getLB() > high || sum.getUB() < low) { 188 return ESat.FALSE; 189 } 190 return low == high && sum.instantiated() ? ESat.TRUE : ESat.UNDEFINED; 191 } 192 193 @Override 194 public String toString() { 195 return "sum(" + set + ") = " + sum + " where " + setCard; 196 } 197}