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}