Skip to content
Open
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 @@ -56,4 +56,18 @@ public CumulFilter(int nbMaxTasks){
* @throws ContradictionException
*/
public abstract void filter(IntVar[] s, IntVar[] d, IntVar[] e, IntVar[] h, IntVar capa, ISet tasks, Propagator<IntVar> aCause) throws ContradictionException;

protected void propStartDurationEndRelation(IntVar[] s, IntVar[] d, IntVar[] e, IntVar[] h, Propagator<IntVar> aCause) throws ContradictionException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if we don't do that at all and leave it to arithm ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said, the idea is to avoid a ping-pong between arithm constraints and the cumulative one, as the latter could be long to initiate. But maybe it is OK to allow it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I found a new reason : some filtering algorithms rely on the hypothesis of the task relation, especially the one in NRJCumulFilter

But I still can't really explain why we have the performance issues

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the slow donw mainly comes from the fact that, with views, only the modified tasks are updated and with the proposal all of them are updated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One possible way to avoid iterating over all tasks is to use IVariableMonitor to store modified tasks and only iterate over them. This is, for example, what is done in ActivityBased search.

int n = s.length;
boolean hasFiltered;
for (int i = 0; i < n; i++) {
d[i].updateLowerBound(0, aCause);
h[i].updateLowerBound(0, aCause);
do {
hasFiltered = s[i].updateBounds(e[i].getLB() - d[i].getUB(), e[i].getUB() - d[i].getLB(), aCause);
hasFiltered |= e[i].updateBounds(s[i].getLB() + d[i].getLB(), s[i].getUB() + d[i].getUB(), aCause);
hasFiltered |= d[i].updateBounds(e[i].getLB() - s[i].getUB(), e[i].getUB() - s[i].getLB(), aCause);
} while (hasFiltered);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public void filter(IntVar[] s, IntVar[] d, IntVar[] e, IntVar[] h, IntVar capa,
}
}
}
propStartDurationEndRelation(s, d, e, h, aCause);
}

private static class StartComparator implements IntComparator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,6 @@ public void filter(IntVar[] s, IntVar[] d, IntVar[] e, IntVar[] h, IntVar capa,
}
}
}
propStartDurationEndRelation(s, d, e, h, aCause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public void filter(IntVar[] s, IntVar[] d, IntVar[] e, IntVar[] h, IntVar capa,
}
}
}
propStartDurationEndRelation(s, d, e, h, aCause);
}

private void filterInf(IntVar start, int dlb, int hlb, int min, int max, int[] time, int capaMax, Propagator<IntVar> aCause) throws ContradictionException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public void propagate(int evtmask) throws ContradictionException {
super.propagate(evtmask);
graphComputation();
} else {
propIni();
if (full) {
filter(allTasks);
} else {
Expand All @@ -105,6 +106,7 @@ public void propagate(int evtmask) throws ContradictionException {
}
}
}
propIni();
}
toCompute.clear();
full = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public void filter(IntVar[] s, IntVar[] d, IntVar[] e, IntVar[] h, IntVar capa,
if(!FIXPOINT)break;
}
pruneMin(s, aCause);
propStartDurationEndRelation(s, d, e, h, aCause);
// symmetric approach for the end upper bounds
i = 0;
tIter = tasksToUSe.iterator();
Expand All @@ -125,6 +126,7 @@ public void filter(IntVar[] s, IntVar[] d, IntVar[] e, IntVar[] h, IntVar capa,
if(!FIXPOINT)break;
}
pruneMax(e, aCause);
propStartDurationEndRelation(s, d, e, h, aCause);
}while(FIXPOINT && again);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public void filter(IntVar[] s, IntVar[] d, IntVar[] e, IntVar[] h, IntVar capa,
}
}
}
propStartDurationEndRelation(s, d, e, h, aCause);
}

protected void filterInf(IntVar start, int elb, int dlb, int hlb, int min, int max, int[] time, int capaMax, Propagator<IntVar> aCause) throws ContradictionException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,9 +505,6 @@ default IntVar[][] intVarMatrix(String name, int dim1, int dim2, int[] values) {
* Creates a task variable, based on a starting time <i>s</i> and a duration <i>d</i>
* such that: s + d = e, where <i>e</i> is the ending time.
*
* A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
* this will not be done automatically.
*
* @param s integer variable, starting time
* @param d fixed duration
* @return a task variable.
Expand All @@ -520,9 +517,6 @@ default Task taskVar(IntVar s, int d) {
* Creates a task variable, based on a starting time <i>s</i> and a duration <i>d</i>
* such that: s + d = e, where <i>e</i> is the ending time.
*
* A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
* this will not be done automatically.
*
* @param s integer variable, starting time
* @param d integer variable, duration
* @return a task variable.
Expand All @@ -541,9 +535,6 @@ default Task taskVar(IntVar s, IntVar d) {
* Creates a task variable, based on a starting time <i>s</i> and a duration <i>d</i>
* such that: s + d = e, where <i>e</i> is the ending time.
*
* A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
* this will not be done automatically.
*
* @param s integer variable, starting time
* @param d fixed duration
* @param e integer variable, ending time
Expand All @@ -557,9 +548,6 @@ default Task taskVar(IntVar s, int d, IntVar e) {
* Creates a task variable, made of a starting time <i>s</i>,
* a duration <i>d</i> and an ending time <i>e</i> such that: s + d = e.
*
* A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
* this will not be done automatically.
*
* @param s integer variable, starting time
* @param d integer variable, duration
* @param e integer variable, ending time
Expand All @@ -574,9 +562,6 @@ default Task taskVar(IntVar s, IntVar d, IntVar e) {
* where task <i>i</i> is made of a starting time <i>s_i</i>,
* a processing time <i>p_i</i> and an ending time <i>e_i</i> such that: s_i + p_i = e_i.
*
* A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
* this will not be done automatically.
*
* @param s integer variables, starting times
* @param p integer variables, processing times
* @param e integer variables, ending times
Expand All @@ -599,9 +584,6 @@ default Task[] taskVarArray(IntVar[] s, IntVar[] p, IntVar[] e) {
* a processing time <i>p_(i,j)</i> and an ending time <i>e_(i,j)</i> such that:
* s_(i,j) + p_(i,j) = e_(i,j).
*
* A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
* this will not be done automatically.
*
* @param s integer variables, starting times
* @param p integer variables, processing times
* @param e integer variables, ending times
Expand Down
155 changes: 67 additions & 88 deletions solver/src/main/java/org/chocosolver/solver/variables/Task.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@
*/
package org.chocosolver.solver.variables;

import org.chocosolver.solver.ICause;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.constraints.Constraint;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.learn.ExplanationForSignedClause;
import org.chocosolver.solver.variables.events.IEventType;
import org.chocosolver.solver.variables.events.IntEventType;
import org.chocosolver.solver.variables.view.integer.IntAffineView;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableRangeSet;

import java.util.ArrayList;
import java.util.function.Consumer;

import static org.chocosolver.util.objects.setDataStructures.iterable.IntIterableSetUtils.unionOf;
Expand All @@ -29,7 +28,7 @@
* @author Jean-Guillaume Fages
* @since 04/02/2013
*/
public class Task {
public class Task implements ICause {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused: the Task object was initially designed to maintain the relationship between start, duration and end during propagation. Now, if the relation is maintained explicitly by propagators, what is the purpose of Task ?


//***********************************************************************************
// VARIABLES
Expand All @@ -38,7 +37,7 @@ public class Task {
private final IntVar start;
private final IntVar duration;
private final IntVar end;
private IVariableMonitor<IntVar> update;
private Constraint arithmConstraint;

//***********************************************************************************
// CONSTRUCTORS
Expand All @@ -53,17 +52,12 @@ public class Task {
* @param lst latest starting time
* @param d duration
* @param ect earliest completion time
* @param lct latest completion time time
* @param lct latest completion time
*/
public Task(Model model, int est, int lst, int d, int ect, int lct) {
start = model.intVar(est, lst);
start = model.intVar(Math.max(est, ect - d), Math.min(lst, lct - d));
duration = model.intVar(d);
if(ect == est+d && lct == lst+d) {
end = start.getModel().offset(start, d);
} else {
end = model.intVar(ect, lct);
declareMonitor();
}
end = start.getModel().offset(start, d);
}

/**
Expand All @@ -79,6 +73,27 @@ public Task(IntVar s, int d) {
end = start.getModel().offset(start, d);
}

/**
* Container representing a task:
* It ensures that: start + duration = end, end being an offset view of start + duration.
*
* @param s start variable
* @param d duration value
*/
public Task(IntVar s, IntVar d) {
if (d.isInstantiated()) {
start = s;
duration = d;
end = start.getModel().offset(start, d.getValue());
} else {
start = s;
duration = d;
end = start.getModel().intVar(s.getLB() + d.getLB(), s.getUB() + d.getUB());
arithmConstraint = start.getModel().arithm(start, "+", duration, "=", end);
arithmConstraint.post();
}
}

/**
* Container representing a task:
* It ensures that: start + duration = end, end being an offset view of start + duration.
Expand All @@ -88,12 +103,7 @@ public Task(IntVar s, int d) {
* @param e end variable
*/
public Task(IntVar s, int d, IntVar e) {
start = s;
duration = start.getModel().intVar(d);
end = e;
if(!isOffsetView(s, d, e)) {
declareMonitor();
}
this(s, s.getModel().intVar(d), e);
}

/**
Expand All @@ -105,38 +115,32 @@ public Task(IntVar s, int d, IntVar e) {
* @param e end variable
*/
public Task(IntVar s, IntVar d, IntVar e) {
this(s, d, e, !d.isInstantiated() || !isOffsetView(s, d.getValue(), e));
}

private Task(IntVar s, IntVar d, IntVar e, boolean postArithm) {
start = s;
duration = d;
end = e;
if(!d.isInstantiated() || !isOffsetView(s, d.getValue(), e)) {
declareMonitor();
if (postArithm) {
arithmConstraint = start.getModel().arithm(start, "+", duration, "=", end);
arithmConstraint.post();
}
}

private static boolean isOffsetView(IntVar s, int d, IntVar e) {
if(e instanceof IntAffineView) {
IntAffineView<?> intOffsetView = (IntAffineView<?>) e;
return intOffsetView.equals(s, 1, d);
if (intOffsetView.p) {
return intOffsetView.equals(s, 1, d);
} else {
intOffsetView = (IntAffineView<?>) s;
return intOffsetView.equals(e.neg().intVar(), -1, -d);
}
}
return false;
}

private void declareMonitor() {
if (start.hasEnumeratedDomain() || duration.hasEnumeratedDomain() || end.hasEnumeratedDomain()) {
update = new TaskMonitor(start, duration, end, true);
} else {
update = new TaskMonitor(start, duration, end, false);
}
Model model = start.getModel();
//noinspection unchecked
ArrayList<Task> tset = (ArrayList<Task>) model.getHook(Model.TASK_SET_HOOK_NAME);
if(tset == null){
tset = new ArrayList<>();
model.addHook(Model.TASK_SET_HOOK_NAME, tset);
}
tset.add(this);
}

//***********************************************************************************
// METHODS
//***********************************************************************************
Expand All @@ -147,7 +151,15 @@ private void declareMonitor() {
* @throws ContradictionException thrown if a inconsistency has been detected between start, end and duration
*/
public void ensureBoundConsistency() throws ContradictionException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here again, is this method still relevant?

update.onUpdate(start, IntEventType.REMOVE);
boolean hasFiltered;
do {
// start
hasFiltered = start.updateBounds(end.getLB() - duration.getUB(), end.getUB() - duration.getLB(), this);
// end
hasFiltered |= end.updateBounds(start.getLB() + duration.getLB(), start.getUB() + duration.getUB(), this);
// duration
hasFiltered |= duration.updateBounds(end.getLB() - start.getUB(), end.getUB() - start.getLB(), this);
} while (hasFiltered);
}

//***********************************************************************************
Expand All @@ -166,8 +178,8 @@ public IntVar getEnd() {
return end;
}

public IVariableMonitor<IntVar> getMonitor() {
return update;
public Constraint getArithmConstraint() {
return arithmConstraint;
}

private static void doExplain(IntVar S, IntVar D, IntVar E,
Expand Down Expand Up @@ -201,54 +213,21 @@ private static void doExplain(IntVar S, IntVar D, IntVar E,
@Override
public String toString() {
return "Task[" +
"start=" + start +
", duration=" + duration +
", end=" + end +
']';
}

private static class TaskMonitor implements IVariableMonitor<IntVar> {
private final IntVar S, D, E;
private final boolean isEnum;

private TaskMonitor(IntVar S, IntVar D, IntVar E, boolean isEnum) {
this.S = S;
this.D = D;
this.E = E;
S.addMonitor(this);
D.addMonitor(this);
E.addMonitor(this);
this.isEnum = isEnum;
}

@Override
public void onUpdate(IntVar var, IEventType evt) throws ContradictionException {
boolean fixpoint;
do {
// start
fixpoint = S.updateBounds(E.getLB() - D.getUB(), E.getUB() - D.getLB(), this);
// end
fixpoint |= E.updateBounds(S.getLB() + D.getLB(), S.getUB() + D.getUB(), this);
// duration
fixpoint |= D.updateBounds(E.getLB() - S.getUB(), E.getUB() - S.getLB(), this);
} while (fixpoint && isEnum);
}

@Override
public void explain(int p, ExplanationForSignedClause explanation) {
doExplain(S, D, E, p, explanation);
}
"start=" + start +
", duration=" + duration +
", end=" + end +
']';
}

@Override
public void forEachIntVar(Consumer<IntVar> action) {
action.accept(S);
action.accept(D);
action.accept(E);
}
@Override
public void explain(int p, ExplanationForSignedClause explanation) {
doExplain(start, duration, end, p, explanation);
}

@Override
public String toString() {
return "Task["+S.getName()+"+"+D.getName()+"="+E.getName()+"]";
}
@Override
public void forEachIntVar(Consumer<IntVar> action) {
action.accept(start);
action.accept(duration);
action.accept(end);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ public void test5(){

@Test(groups="10s", timeOut=60000)
public void test6(){
long time = System.currentTimeMillis();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be removed

while ((System.currentTimeMillis() - time) < 10000);
// this tests raises an exception which is in fact due to the time limit
// and unlucky random heuristic (fixed by adding last conflict)
test(16,3,2,4,4,1);
Expand Down
Loading