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 solver.constraints.Propagator; 008import solver.constraints.PropagatorPriority; 009import solver.exception.ContradictionException; 010import solver.variables.EventType; 011import solver.variables.SetVar; 012import solver.variables.delta.monitor.SetDeltaMonitor; 013import util.ESat; 014import util.procedure.IntProcedure; 015 016/** 017 * <p> 018 * Join a unary relation with a binary relation. The {@code take} variable is 019 * the unary relation and the {@code children} variables are the binary 020 * relation. The {@code to} variable is the result of the join. 021 * </p> 022 * <p> 023 * Here is how the binary relation is encoded. Consider the relation: 024 * {@code (0, 1), (0, 2), (1, 3), (2, 1)}. This is encoded as 3 different 025 * {@code children} variables: {@code child0={1, 2}, child1={3}, child2={1}}. 026 * </p> 027 * 028 * @author jimmy 029 */ 030public class PropJoinRelation extends Propagator<SetVar> { 031 032 private final SetVar take; 033 private final SetDeltaMonitor takeD; 034 private final IndexedBipartiteSet dontCare; 035 private final SetVar[] children; 036 private final SetDeltaMonitor[] childrenD; 037 private final SetVar to; 038 private final SetDeltaMonitor toD; 039 040 public PropJoinRelation(SetVar take, SetVar[] children, SetVar to) { 041 super(buildArray(take, to, children), PropagatorPriority.QUADRATIC, true); 042 this.take = take; 043 this.takeD = take.monitorDelta(aCause); 044 this.dontCare = new IndexedBipartiteSet(take.getSolver().getEnvironment(), PropUtil.iterateEnv(take)); 045 this.children = children; 046 this.childrenD = PropUtil.monitorDeltas(children, aCause); 047 this.to = to; 048 this.toD = to.monitorDelta(aCause); 049 } 050 051 private static SetVar[] buildArray(SetVar take, SetVar to, SetVar[] children) { 052 SetVar[] array = new SetVar[children.length + 2]; 053 array[0] = take; 054 array[1] = to; 055 System.arraycopy(children, 0, array, 2, children.length); 056 return array; 057 } 058 059 private boolean isTakeVar(int idx) { 060 return idx == 0; 061 } 062 063 private boolean isToVar(int idx) { 064 return idx == 1; 065 } 066 067 private boolean isChildVar(int idx) { 068 return idx >= 2; 069 } 070 071 private int getChildVarIndex(int idx) { 072 assert isChildVar(idx); 073 return idx - 2; 074 } 075 076 @Override 077 public boolean advise(int idxVarInProp, int mask) { 078 if (isChildVar(idxVarInProp)) { 079 return dontCare.contains(getChildVarIndex(idxVarInProp)); 080 } 081 return super.advise(idxVarInProp, mask); 082 } 083 084 @Override 085 public int getPropagationConditions(int vIdx) { 086 return EventType.ADD_TO_KER.mask + EventType.REMOVE_FROM_ENVELOPE.mask; 087 } 088 089 private void findMate(int toEnv) throws ContradictionException { 090 boolean inKer = to.kernelContains(toEnv); 091 int mate = -1; 092 for (int j = take.getEnvelopeFirst(); j != SetVar.END; j = take.getEnvelopeNext()) { 093 if (children[j].envelopeContains(toEnv)) { 094 // Found a second mate. 095 if (mate != -1 || !inKer) { 096 mate = -2; 097 break; 098 } 099 mate = j; 100 } 101 } 102 if (mate == -1) { 103 // No mates. 104 to.removeFromEnvelope(toEnv, aCause); 105 } else if (mate != -2 && inKer) { 106 // One mate. 107 take.addToKernel(mate, aCause); 108 PropUtil.kerSubsetKer(children[mate], to, aCause); 109 PropUtil.envSubsetEnv(children[mate], to, aCause); 110 children[mate].addToKernel(toEnv, aCause); 111 } 112 } 113 114 @Override 115 public void propagate(int evtmask) throws ContradictionException { 116 // Prune take 117 for (int i = take.getEnvelopeFirst(); i != SetVar.END; i = take.getEnvelopeNext()) { 118 if (i < 0 || i >= children.length || !PropUtil.isKerSubsetEnv(children[i], to)) { 119 take.removeFromEnvelope(i, aCause); 120 dontCare.remove(i); 121 } 122 } 123 124 // Pick to and prune child 125 for (int i = take.getKernelFirst(); i != SetVar.END; i = take.getKernelNext()) { 126 PropUtil.kerSubsetKer(children[i], to, aCause); 127 PropUtil.envSubsetEnv(children[i], to, aCause); 128 } 129 130 // Pick take, pick child, pick take, prune to 131 for (int i = to.getEnvelopeFirst(); i != SetVar.END; i = to.getEnvelopeNext()) { 132 findMate(i); 133 } 134 } 135 136 @Override 137 public void propagate(int idxVarInProp, int mask) throws ContradictionException { 138 if (isTakeVar(idxVarInProp)) { 139 takeD.freeze(); 140 takeD.forEach(pruneToOnTakeEnv, EventType.REMOVE_FROM_ENVELOPE); 141 takeD.forEach(pickToAndPruneChildOnTakeKer, EventType.ADD_TO_KER); 142 takeD.unfreeze(); 143 } else if (isToVar(idxVarInProp)) { 144 toD.freeze(); 145 toD.forEach(pruneChildOnToEnv, EventType.REMOVE_FROM_ENVELOPE); 146 toD.forEach(pickTakeOnToKer, EventType.ADD_TO_KER); 147 toD.unfreeze(); 148 if ((EventType.REMOVE_FROM_ENVELOPE.mask & mask) != 0) { 149 TIntArrayList removed = null; 150 for (int i = take.getEnvelopeFirst(); i != SetVar.END; i = take.getEnvelopeNext()) { 151 if (!PropUtil.isKerSubsetEnv(children[i], to)) { 152 take.removeFromEnvelope(i, aCause); 153 dontCare.remove(i); 154 // Cannot call findMate here because we inside iterating take env. 155 // Queue up the even to do later. 156 if (removed == null) { 157 removed = new TIntArrayList(1); 158 } 159 removed.add(i); 160 } 161 } 162 if (removed != null) { 163 TIntIterator it = removed.iterator(); 164 while (it.hasNext()) { 165 SetVar child = children[it.next()]; 166 for (int i = child.getEnvelopeFirst(); i != SetVar.END; i = child.getEnvelopeNext()) { 167 if (to.envelopeContains(i)) { 168 findMate(i); 169 } 170 } 171 } 172 } 173 } 174 } else { 175 assert isChildVar(idxVarInProp); 176 final int id = getChildVarIndex(idxVarInProp); 177 final SetDeltaMonitor childD = childrenD[id]; 178 childD.freeze(); 179 // Note that we MUST prune even if id is not in env(take) to ensure 180 // idempotence. Otherwise if id is removed from take and val removed 181 // from child at the same time is no longer supported, we need to 182 // remove val from to as well. 183 childD.forEach(pruneToOnChildEnv, EventType.REMOVE_FROM_ENVELOPE); 184 if (take.kernelContains(id)) { 185 childD.forEach(pickToOnChildKer, EventType.ADD_TO_KER); 186 } else if (take.envelopeContains(id)) { 187 IntProcedure pruneTakeOnChildKer = new IntProcedure() { 188 @Override 189 public void execute(int childKer) throws ContradictionException { 190 if (!to.envelopeContains(childKer)) { 191 take.removeFromEnvelope(id, aCause); 192 dontCare.remove(id); 193 for (int i = children[id].getEnvelopeFirst(); i != SetVar.END; i = children[id].getEnvelopeNext()) { 194 if (to.envelopeContains(i)) { 195 findMate(i); 196 } 197 } 198 } 199 } 200 }; 201 childD.forEach(pruneTakeOnChildKer, EventType.ADD_TO_KER); 202 } 203 childD.unfreeze(); 204 } 205 } 206 private final IntProcedure pruneToOnTakeEnv = new IntProcedure() { 207 @Override 208 public void execute(int takeEnv) throws ContradictionException { 209 assert !take.envelopeContains(takeEnv); 210 dontCare.remove(takeEnv); 211 for (int i = children[takeEnv].getEnvelopeFirst(); i != SetVar.END; i = children[takeEnv].getEnvelopeNext()) { 212 if (to.envelopeContains(i)) { 213 findMate(i); 214 } 215 } 216 } 217 }; 218 private final IntProcedure pickToAndPruneChildOnTakeKer = new IntProcedure() { 219 @Override 220 public void execute(int takeKer) throws ContradictionException { 221 assert take.kernelContains(takeKer); 222 223 SetVar child = children[takeKer]; 224 PropUtil.kerSubsetKer(child, to, aCause); 225 PropUtil.envSubsetEnv(child, to, aCause); 226 } 227 }; 228 private final IntProcedure pruneToOnChildEnv = new IntProcedure() { 229 @Override 230 public void execute(int childEnv) throws ContradictionException { 231 // Note the the child may no longer be in take, but still need to find mate. 232 // For example: 233 // take = {0,1,2}, child0 = {0}, child1 = {1}, child2 = {2}, to = {0,1,2} 234 // remove 2 from take and 2 from child2 235 // take = {0,1}, child0 = {0}, child1 = {1}, child2 = {}, to = {0,1,2} 236 // Need to find mate for 2 on child2 or else to will keep 2, and break 237 // idempotency. 238 if (to.envelopeContains(childEnv)) { 239 findMate(childEnv); 240 } 241 } 242 }; 243 private final IntProcedure pickToOnChildKer = new IntProcedure() { 244 @Override 245 public void execute(int childKer) throws ContradictionException { 246 // assert id in ker(take) 247 to.addToKernel(childKer, aCause); 248 } 249 }; 250 private final IntProcedure pruneChildOnToEnv = new IntProcedure() { 251 @Override 252 public void execute(int toEnv) throws ContradictionException { 253 assert !to.envelopeContains(toEnv); 254 255 for (int takeKer = take.getKernelFirst(); takeKer != SetVar.END; takeKer = take.getKernelNext()) { 256 children[takeKer].removeFromEnvelope(toEnv, aCause); 257 } 258 } 259 }; 260 private final IntProcedure pickTakeOnToKer = new IntProcedure() { 261 @Override 262 public void execute(int toVal) throws ContradictionException { 263 assert to.kernelContains(toVal); 264 findMate(toVal); 265 } 266 }; 267 268 @Override 269 public ESat isEntailed() { 270 for (int i = take.getKernelFirst(); i != SetVar.END; i = take.getKernelNext()) { 271 if (i < 0 || i >= children.length) { 272 return ESat.FALSE; 273 } 274 for (int j = children[i].getKernelFirst(); j != SetVar.END; j = children[i].getKernelNext()) { 275 if (!to.envelopeContains(j)) { 276 return ESat.FALSE; 277 } 278 } 279 } 280 boolean completelyInstantiated = take.instantiated() && to.instantiated(); 281 int count = 0; 282 SetVar[] taken = new SetVar[take.getEnvelopeSize()]; 283 for (int i = take.getEnvelopeFirst(); i != SetVar.END; i = take.getEnvelopeNext()) { 284 if (i >= 0 && i < children.length) { 285 SetVar child = children[i]; 286 completelyInstantiated = completelyInstantiated && child.instantiated(); 287 taken[count++] = child; 288 } 289 } 290 if (count < taken.length) { 291 taken = Arrays.copyOf(taken, count); 292 } 293 for (int i = to.getKernelFirst(); i != SetVar.END; i = to.getKernelNext()) { 294 if (!PropUtil.envsContain(taken, i)) { 295 return ESat.FALSE; 296 } 297 } 298 return completelyInstantiated ? ESat.TRUE : ESat.UNDEFINED; 299 } 300 301 @Override 302 public String toString() { 303 return "joinRelation(" + take + ", " + Arrays.toString(children) + ", " + to + ")"; 304 } 305}