Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving PropDiffN performance #663

Merged
merged 7 commits into from
Jan 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1220,8 +1220,7 @@ default Constraint diffN(IntVar[] X, IntVar[] Y, IntVar[] width, IntVar[] height
Model model = X[0].getModel();
Constraint diffNCons = new Constraint(
ConstraintsName.DIFFN,
new PropDiffN(X, Y, width, height, false),
new PropDiffN(X, Y, width, height, false)
new PropDiffN(X, Y, width, height)
);
if (addCumulativeReasoning) {
IntVar[] EX = new IntVar[X.length];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
package org.chocosolver.solver.constraints.nary;

import gnu.trove.list.array.TIntArrayList;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.constraints.PropagatorPriority;
import org.chocosolver.solver.exception.ContradictionException;
Expand All @@ -18,9 +19,7 @@
import org.chocosolver.solver.variables.events.PropagatorEventType;
import org.chocosolver.util.ESat;
import org.chocosolver.util.objects.graphs.UndirectedGraph;
import org.chocosolver.util.objects.setDataStructures.ISet;
import org.chocosolver.util.objects.setDataStructures.ISetIterator;
import org.chocosolver.util.objects.setDataStructures.SetFactory;
import org.chocosolver.util.objects.setDataStructures.SetType;
import org.chocosolver.util.tools.ArrayUtils;

Expand All @@ -36,95 +35,117 @@ public class PropDiffN extends Propagator<IntVar> {

private int n;
private UndirectedGraph overlappingBoxes;
private ISet boxesToCompute;
private boolean fast;
private TIntArrayList boxesToCompute;
private TIntArrayList pruneList;

//***********************************************************************************
// CONSTRUCTOR
//***********************************************************************************

public PropDiffN(IntVar[] x, IntVar[] y, IntVar[] dx, IntVar[] dy, boolean fast) {
public PropDiffN(IntVar[] x, IntVar[] y, IntVar[] dx, IntVar[] dy) {
super(ArrayUtils.append(x, y, dx, dy), PropagatorPriority.LINEAR, true);
this.fast = fast;
n = x.length;
if (!(n == y.length && n == dx.length && n == dy.length)) {
throw new SolverException("PropDiffN variable arrays do not have same size");
}
overlappingBoxes = new UndirectedGraph(model, n, SetType.LINKED_LIST, true);
boxesToCompute = SetFactory.makeStoredSet(SetType.LINKED_LIST, 0, model);
}
boxesToCompute = new TIntArrayList(n);
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (mayOverlap(i, j)) {
overlappingBoxes.addEdge(i, j);
}
}
}
pruneList = new TIntArrayList(n);
}

//***********************************************************************************
// METHODS
//***********************************************************************************

@Override
public int getPropagationConditions(int idx) {
if (fast) return IntEventType.instantiation();
return IntEventType.boundAndInst();
}

@Override
public void propagate(int varIdx, int mask) throws ContradictionException {
int v = varIdx % n;
@Override
public void propagate(int varIdx, int mask) throws ContradictionException {
prop(varIdx);
forcePropagate(PropagatorEventType.CUSTOM_PROPAGATION);
}

private void prop(int varIdx) {
int v = varIdx % n;
ISetIterator iter = overlappingBoxes.getNeighOf(v).iterator();
while (iter.hasNext()) {
int i = iter.nextInt();
if (!mayOverlap(v, i)) {
overlappingBoxes.removeEdge(v, i);
}
}
if (!boxesToCompute.contains(v)) {
boxesToCompute.add(v);
}
forcePropagate(PropagatorEventType.CUSTOM_PROPAGATION);
}
if (!mayOverlap(v, i)) {
overlappingBoxes.removeEdge(v, i);
}
}
if (!boxesToCompute.contains(v)) {
boxesToCompute.add(v);
}
}

@Override
public void propagate(int evtmask) throws ContradictionException {
if (PropagatorEventType.isFullPropagation(evtmask)) {
for (int i = 0; i < n; i++) {
overlappingBoxes.getNeighOf(i).clear();
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (mayOverlap(i, j)) {
overlappingBoxes.addEdge(i, j);
if (boxInstantiated(i) && boxInstantiated(j)) {
fails(); // TODO: could be more precise, for explanation purpose
boolean hasFiltered = true;
while(hasFiltered) {
hasFiltered = false;
if(PropagatorEventType.isFullPropagation(evtmask)) {
boxesToCompute.resetQuick();
for (int i = 0; i < n; i++) {
boxesToCompute.add(i);
for (int j = i + 1; j < n; j++) {
if (mayOverlap(i, j)) {
overlappingBoxes.addEdge(i, j);
if (boxInstantiated(i) && boxInstantiated(j)) {
fails(); // TODO: could be more precise, for explanation purpose
}
} else {
overlappingBoxes.removeEdge(i, j);
cprudhom marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
pruneList.clear();
for(int k = 0; k<boxesToCompute.size(); k++) {
int i = boxesToCompute.getQuick(k);
energyCheck(i);
hasFiltered |= prune(i);
}
boxesToCompute.clear();
for (int i = 0; i < n; i++) {
boxesToCompute.add(i);
for(int k = 0; k< pruneList.size(); k++) {
prop(pruneList.getQuick(k));
}
}
ISetIterator iter = boxesToCompute.iterator();
while (iter.hasNext()) {
filterFromBox(iter.nextInt());
}
boxesToCompute.clear();
}

private boolean mayOverlap(int i, int j) {
return isNotDisjoint(i, j, true) && isNotDisjoint(i, j, false);
}

private boolean isNotDisjoint(int i, int j, boolean horizontal) {
int off = (horizontal) ? 0 : n;
return (vars[i + off].getLB() < vars[j + off].getUB() + vars[j + off + 2 * n].getUB())
&& (vars[j + off].getLB() < vars[i + off].getUB() + vars[i + off + 2 * n].getUB());
private boolean prune(int j) throws ContradictionException {
boolean hasFiltered = false;
ISetIterator iter = overlappingBoxes.getNeighOf(j).iterator();
while (iter.hasNext()) {
int i = iter.nextInt();
if(doOverlap(i, j, true)) {
hasFiltered |= filter(i, j, false);
}
if(doOverlap(i, j, false)) {
hasFiltered |= filter(i, j, true);
}
}
return hasFiltered;
}

protected void filterFromBox(int i) throws ContradictionException {
// check energy
private void energyCheck(int i) throws ContradictionException {
int xm = vars[i].getLB();
int xM = vars[i].getUB() + vars[i + 2 * n].getUB();
int ym = vars[i + n].getLB();
int yM = vars[i + n].getUB() + vars[i + 3 * n].getUB();
int am = vars[i + 2 * n].getLB() * vars[i + 3 * n].getLB();
int xLengthMin = vars[i + 2 * n].getLB();
int yLengthMin = vars[i + 3 * n].getLB();
ISetIterator iter = overlappingBoxes.getNeighOf(i).iterator();
while (iter.hasNext()) {
int j = iter.nextInt();
Expand All @@ -136,51 +157,80 @@ protected void filterFromBox(int i) throws ContradictionException {
if (am > (xM - xm) * (yM - ym)) {
fails(); // TODO: could be more precise, for explanation purpose
}
xLengthMin = Math.min(xLengthMin, vars[j + 2 * n].getLB());
yLengthMin = Math.min(yLengthMin, vars[j + 3 * n].getLB());
}
// mandatory part based filtering
boolean horizontal = true;
boolean vertical = false;
iter = overlappingBoxes.getNeighOf(i).iterator(); // reset iteration
while (iter.hasNext()) {
int j = iter.nextInt();
if (doOverlap(i, j, horizontal)) {
filter(i, j, vertical);
}
if (doOverlap(i, j, vertical)) {
filter(i, j, horizontal);

if (xLengthMin > 0 && yLengthMin > 0) {
int maxNumberRectangles = ((xM - xm) / xLengthMin) * ((yM - ym) / yLengthMin);
if (maxNumberRectangles < overlappingBoxes.getNeighOf(i).size()+1) {
fails();
}
assert !(doOverlap(i, j, horizontal) && doOverlap(i, j, vertical));
}
}

private boolean mayOverlap(int i, int j) {
return isNotDisjoint(i, j, true) && isNotDisjoint(i, j, false);
}

private boolean isNotDisjoint(int i, int j, boolean horizontal) {
int off = (horizontal) ? 0 : n;
return (vars[i + off].getLB() < vars[j + off].getUB() + vars[j + off + 2 * n].getUB())
&& (vars[j + off].getLB() < vars[i + off].getUB() + vars[i + off + 2 * n].getUB());
}

private boolean doOverlap(int i, int j, boolean hori) {
int offSet = hori ? 0 : n;
int S_i = vars[i + offSet].getUB();
int s_i = vars[i + offSet].getUB();
int e_i = vars[i + offSet].getLB() + vars[i + 2 * n + offSet].getLB();
int S_j = vars[j + offSet].getUB();
int s_j = vars[j + offSet].getUB();
int e_j = vars[j + offSet].getLB() + vars[j + 2 * n + offSet].getLB();
return (S_i < e_i && e_j > S_i && S_j < e_i)
|| (S_j < e_j && e_i > S_j && S_i < e_j);
return (s_i < e_i && e_j > s_i && s_j < e_i)
|| (s_j < e_j && e_i > s_j && s_i < e_j);
}

private void filter(int i, int j, boolean hori) throws ContradictionException {
private boolean filter(int i, int j, boolean hori) throws ContradictionException {
boolean hasFiltered = false;
int offSet = hori ? 0 : n;
int S_i = vars[i + offSet].getUB();
int s_i = vars[i + offSet].getUB();
int e_i = vars[i + offSet].getLB() + vars[i + 2 * n + offSet].getLB();
int S_j = vars[j + offSet].getUB();
int s_j = vars[j + offSet].getUB();
int e_j = vars[j + offSet].getLB() + vars[j + 2 * n + offSet].getLB();
if (S_i < e_i || S_j < e_j) {
if (e_j > S_i) {
vars[j + offSet].updateLowerBound(e_i, this);
vars[i + offSet].updateUpperBound(S_j - vars[i + 2 * n + offSet].getLB(), this);
vars[i + offSet + 2 * n].updateUpperBound(S_j - vars[i + offSet].getLB(), this);
if (s_i < e_i || s_j < e_j) {
if (e_j > s_i) {
if(vars[j + offSet].updateLowerBound(e_i, this)) {
if(!pruneList.contains(j)) {
pruneList.add(j);
}
hasFiltered = true;
}
boolean filtPrun1 = vars[i + offSet].updateUpperBound(s_j - vars[i + 2 * n + offSet].getLB(), this);
boolean filtPrun2 = vars[i + offSet + 2 * n].updateUpperBound(s_j - vars[i + offSet].getLB(), this);
if(filtPrun1 || filtPrun2) {
if(!pruneList.contains(i)) {
pruneList.add(i);
}
hasFiltered = true;
}
}
if (S_j < e_i) {
vars[i + offSet].updateLowerBound(e_j, this);
vars[j + offSet].updateUpperBound(S_i - vars[j + 2 * n + offSet].getLB(), this);
vars[j + offSet + 2 * n].updateUpperBound(S_i - vars[j + offSet].getLB(), this);
if (s_j < e_i) {
if(vars[i + offSet].updateLowerBound(e_j, this)) {
if(!pruneList.contains(i)) {
pruneList.add(i);
}
hasFiltered = true;
}
boolean filtPrun1 = vars[j + offSet].updateUpperBound(s_i - vars[j + 2 * n + offSet].getLB(), this);
boolean filtPrun2 = vars[j + offSet + 2 * n].updateUpperBound(s_i - vars[j + offSet].getLB(), this);
if(filtPrun1 || filtPrun2) {
if(!pruneList.contains(j)) {
pruneList.add(j);
}
hasFiltered = true;
}
}
}
return hasFiltered;
}

@Override
Expand All @@ -203,7 +253,7 @@ public ESat isEntailed() {

private boolean boxInstantiated(int i) {
return vars[i].isInstantiated() && vars[i + n].isInstantiated()
&& vars[i + 2 * n].isInstantiated() && vars[i + 3 * n].isInstantiated();
&& vars[i + 2 * n].isInstantiated() && vars[i + 3 * n].isInstantiated();
}

@Override
Expand Down