001package org.clafer.choco.constraint.propagator; 002 003import gnu.trove.iterator.TIntIterator; 004import gnu.trove.list.array.TIntArrayList; 005import java.util.Arrays; 006import memory.structure.IndexedBipartiteSet; 007import org.clafer.collection.MutableBoolean; 008import solver.constraints.Propagator; 009import solver.constraints.PropagatorPriority; 010import solver.exception.ContradictionException; 011import solver.variables.EventType; 012import solver.variables.IntVar; 013import solver.variables.SetVar; 014import solver.variables.Variable; 015import solver.variables.delta.IIntDeltaMonitor; 016import solver.variables.delta.monitor.SetDeltaMonitor; 017import util.ESat; 018import util.procedure.IntProcedure; 019 020/** 021 * <p> 022 * Join a unary set relation with a binary function. This propagator is a 023 * specialized version of {@link PropJoinRelation}. The {@code take} variable is 024 * the unary relation and the {@code ref} variables are the function. The 025 * {@code to} variable is the result of the join. 026 * </p> 027 * <p> 028 * Here is how the binary function is encoded. Consider the function: 029 * {@code (0, 1), (1, 3), (2, 1)}. This is encoded as 3 different {@code ref} 030 * variables: {@code ref0={1}, ref1={3}, ref2={1}}. 031 * </p> 032 * 033 * @author jimmy 034 */ 035public class PropJoinFunction extends Propagator<Variable> { 036 037 private final SetVar take; 038 private final SetDeltaMonitor takeD; 039 private final IndexedBipartiteSet dontCare; 040 private final IntVar[] refs; 041 private final IIntDeltaMonitor[] refsD; 042 private final SetVar to; 043 private final SetDeltaMonitor toD; 044 045 public PropJoinFunction(SetVar take, IntVar[] refs, SetVar to) { 046 super(buildArray(take, to, refs), PropagatorPriority.QUADRATIC, true); 047 this.take = take; 048 this.takeD = take.monitorDelta(aCause); 049 this.dontCare = new IndexedBipartiteSet(take.getSolver().getEnvironment(), PropUtil.iterateEnv(take)); 050 this.refs = refs; 051 this.refsD = PropUtil.monitorDeltas(refs, aCause); 052 this.to = to; 053 this.toD = to.monitorDelta(aCause); 054 } 055 056 private static Variable[] buildArray(SetVar take, SetVar to, IntVar[] refs) { 057 Variable[] array = new Variable[refs.length + 2]; 058 array[0] = take; 059 array[1] = to; 060 System.arraycopy(refs, 0, array, 2, refs.length); 061 return array; 062 } 063 064 private boolean isTakeVar(int idx) { 065 return idx == 0; 066 } 067 068 private boolean isToVar(int idx) { 069 return idx == 1; 070 } 071 072 private boolean isRefVar(int idx) { 073 return idx >= 2; 074 } 075 076 private int getRefVarIndex(int idx) { 077 assert isRefVar(idx); 078 return idx - 2; 079 } 080 081 @Override 082 public boolean advise(int idxVarInProp, int mask) { 083 if (isRefVar(idxVarInProp)) { 084 return dontCare.contains(getRefVarIndex(idxVarInProp)); 085 } 086 return super.advise(idxVarInProp, mask); 087 } 088 089 @Override 090 public int getPropagationConditions(int vIdx) { 091 if (isTakeVar(vIdx)) { 092 return EventType.ADD_TO_KER.mask + EventType.REMOVE_FROM_ENVELOPE.mask; 093 } 094 if (isToVar(vIdx)) { 095 return EventType.ADD_TO_KER.mask + EventType.REMOVE_FROM_ENVELOPE.mask; 096 } 097 assert isRefVar(vIdx); 098 return EventType.INT_ALL_MASK(); 099 } 100 101 private boolean findMate(int toEnv) throws ContradictionException { 102 boolean inKer = to.kernelContains(toEnv); 103 int mate = -1; 104 for (int j = take.getEnvelopeFirst(); j != SetVar.END; j = take.getEnvelopeNext()) { 105 if (refs[j].contains(toEnv)) { 106 // Found a second mate. 107 if (mate != -1 || !inKer) { 108 mate = -2; 109 break; 110 } 111 mate = j; 112 } 113 } 114 if (mate == -1) { 115 // No mates. 116 to.removeFromEnvelope(toEnv, aCause); 117 } else if (mate != -2 && inKer) { 118 // One mate. 119 take.addToKernel(mate, aCause); 120 return refs[mate].instantiateTo(toEnv, aCause); 121 } 122 return false; 123 } 124 125 private void findMates() throws ContradictionException { 126 for (int i = to.getEnvelopeFirst(); i != SetVar.END; i = to.getEnvelopeNext()) { 127 if (findMate(i)) { 128 findMates(); 129 return; 130 } 131 } 132 } 133 134 @Override 135 public void propagate(int evtmask) throws ContradictionException { 136 // Prune take 137 for (int i = take.getEnvelopeFirst(); i != SetVar.END; i = take.getEnvelopeNext()) { 138 if (i < 0 || i >= refs.length || !PropUtil.isDomIntersectEnv(refs[i], to)) { 139 take.removeFromEnvelope(i, aCause); 140 dontCare.remove(i); 141 } 142 } 143 144 // Pick to and prune refs 145 for (int i = take.getKernelFirst(); i != SetVar.END; i = take.getKernelNext()) { 146 PropUtil.domSubsetEnv(refs[i], to, aCause); 147 if (refs[i].instantiated()) { 148 int value = refs[i].getValue(); 149 to.addToKernel(value, aCause); 150 } 151 } 152 153 // Prune to 154 findMates(); 155 } 156 157 @Override 158 public void propagate(int idxVarInProp, int mask) throws ContradictionException { 159 if (isTakeVar(idxVarInProp)) { 160 takeD.freeze(); 161 takeD.forEach(pruneToOnTakeEnv, EventType.REMOVE_FROM_ENVELOPE); 162 takeD.forEach(pickToAndPruneChildOnTakeKer, EventType.ADD_TO_KER); 163 takeD.unfreeze(); 164 } else if (isToVar(idxVarInProp)) { 165 toD.freeze(); 166 toD.forEach(pruneRefOnToEnv, EventType.REMOVE_FROM_ENVELOPE); 167 toD.forEach(pickTakeOnToKer, EventType.ADD_TO_KER); 168 toD.unfreeze(); 169 if ((EventType.REMOVE_FROM_ENVELOPE.mask & mask) != 0) { 170 TIntArrayList removed = null; 171 for (int i = take.getEnvelopeFirst(); i != SetVar.END; i = take.getEnvelopeNext()) { 172 if (!PropUtil.isDomIntersectEnv(refs[i], to)) { 173 take.removeFromEnvelope(i, aCause); 174 dontCare.remove(i); 175 // Cannot call findMate here because we inside iterating take env. 176 // Queue up the even to do later. 177 if (removed == null) { 178 removed = new TIntArrayList(1); 179 } 180 removed.add(i); 181 } 182 } 183 if (removed != null) { 184 TIntIterator it = removed.iterator(); 185 while (it.hasNext()) { 186 IntVar ref = refs[it.next()]; 187 int ub = ref.getUB(); 188 for (int i = ref.getLB(); i <= ub; i = ref.nextValue(i)) { 189 if (to.envelopeContains(i)) { 190 if (findMate(i)) { 191 findMates(); 192 } 193 } 194 } 195 } 196 } 197 } 198 } else { 199 assert isRefVar(idxVarInProp); 200 final int id = getRefVarIndex(idxVarInProp); 201 final IIntDeltaMonitor refD = refsD[id]; 202 final MutableBoolean bool = new MutableBoolean(); 203 refD.freeze(); 204 IntProcedure pruneToOnRefRem = new IntProcedure() { 205 @Override 206 public void execute(int refRem) throws ContradictionException { 207 if (bool.isClear()) { 208 if (to.envelopeContains(refRem)) { 209 if (findMate(refRem)) { 210 bool.set(); 211 findMates(); 212 } 213 } 214 } 215 } 216 }; 217 refD.forEach(pruneToOnRefRem, EventType.REMOVE); 218 refD.unfreeze(); 219 IntVar ref = refs[id]; 220 if (EventType.isRemove(mask)) { 221 if (!PropUtil.isDomIntersectEnv(ref, to)) { 222 take.removeFromEnvelope(id, aCause); 223 dontCare.remove(id); 224 } 225 } 226 if (ref.instantiated()) { 227 if (take.kernelContains(id)) { 228 to.addToKernel(ref.getValue(), aCause); 229 } 230 } 231 } 232 } 233 private final IntProcedure pruneToOnTakeEnv = new IntProcedure() { 234 @Override 235 public void execute(int takeEnv) throws ContradictionException { 236 assert !take.envelopeContains(takeEnv); 237 238 dontCare.remove(takeEnv); 239 IntVar ref = refs[takeEnv]; 240 int ub = ref.getUB(); 241 for (int i = ref.getLB(); i <= ub; i = ref.nextValue(i)) { 242 if (to.envelopeContains(i)) { 243 if (findMate(i)) { 244 findMates(); 245 return; 246 } 247 } 248 } 249 } 250 }; 251 private final IntProcedure pickToAndPruneChildOnTakeKer = new IntProcedure() { 252 @Override 253 public void execute(int takeKer) throws ContradictionException { 254 assert take.kernelContains(takeKer); 255 256 IntVar ref = refs[takeKer]; 257 PropUtil.domSubsetEnv(ref, to, aCause); 258 if (ref.instantiated()) { 259 to.addToKernel(ref.getValue(), aCause); 260 } 261 } 262 }; 263 private final IntProcedure pruneRefOnToEnv = new IntProcedure() { 264 @Override 265 public void execute(int toEnv) throws ContradictionException { 266 assert !to.envelopeContains(toEnv); 267 268 for (int takeKer = take.getKernelFirst(); takeKer != SetVar.END; takeKer = take.getKernelNext()) { 269 IntVar ref = refs[takeKer]; 270 if (ref.removeValue(toEnv, aCause) && ref.instantiated()) { 271 to.addToKernel(ref.getValue(), aCause); 272 } 273 } 274 } 275 }; 276 private final IntProcedure pickTakeOnToKer = new IntProcedure() { 277 @Override 278 public void execute(int toVal) throws ContradictionException { 279 assert to.kernelContains(toVal); 280 if (findMate(toVal)) { 281 findMates(); 282 } 283 } 284 }; 285 286 @Override 287 public ESat isEntailed() { 288 for (int i = take.getKernelFirst(); i != SetVar.END; i = take.getKernelNext()) { 289 if (i < 0 || i >= refs.length || !PropUtil.isDomIntersectEnv(refs[i], to)) { 290 return ESat.FALSE; 291 } 292 if (refs[i].instantiated()) { 293 int value = refs[i].getValue(); 294 if (!to.envelopeContains(value)) { 295 return ESat.FALSE; 296 } 297 } 298 } 299 boolean completelyInstantiated = take.instantiated() && to.instantiated(); 300 int count = 0; 301 IntVar[] taken = new IntVar[take.getEnvelopeSize()]; 302 for (int i = take.getEnvelopeFirst(); i != SetVar.END; i = take.getEnvelopeNext()) { 303 if (i >= 0 && i < refs.length) { 304 IntVar ref = refs[i]; 305 completelyInstantiated = completelyInstantiated && ref.instantiated(); 306 taken[count++] = ref; 307 } 308 } 309 if (count < taken.length) { 310 taken = Arrays.copyOf(taken, count); 311 } 312 for (int i = to.getKernelFirst(); i != SetVar.END; i = to.getKernelNext()) { 313 if (!PropUtil.domsContain(taken, i)) { 314 return ESat.FALSE; 315 } 316 } 317 return completelyInstantiated ? ESat.TRUE : ESat.UNDEFINED; 318 } 319 320 @Override 321 public String toString() { 322 return "joinFunction(" + take + ", " + Arrays.toString(refs) + ", " + to + ")"; 323 } 324}