package gipad.plan.choco.constraints; import solver.Solver; import solver.constraints.Propagator; import solver.constraints.PropagatorPriority; import solver.exception.ContradictionException; import solver.variables.EventType; import solver.variables.IntVar; import util.ESat; import java.util.BitSet; /** * Created by IntelliJ IDEA. * User: Arnaud Letort * Date: 22/03/13 * Time: 11:45 * * Implementation of the k-dimensional sweep algorithm RING version (without precedence relations). */ @SuppressWarnings({"serial","unused","rawtypes"}) public class PropTTPCDynamicSweepLoads extends Propagator<IntVar> { private final Solver s; private final int nbTasks; private final int nbResources; private final int[] capacities; private final int[][] successors; private final int[][] predecessors; // ===== LOADS (begin) ===== private final int[] interestingTimePoints; private final int[] interestingResources; private int nextIndexInterestingTP; private final IntVar[][] loads; // ===== LOADS (end) ===== private DynamicSweepMinKDimPrecColRings sweepMin; private DynamicSweepMaxKDimPrecColRings sweepMax; private DynamicSweepGreedyKDimPrecColRings sweepGreedy; private final boolean aggregateMode; // Optimization private final boolean greedyMode; private final Rings ring; private int[] mapping; // mapping[relative id] = absolute id private int[] mappingRev; // mappingRev[absolute id] = relative id private int nbTasksInFilteringAlgo; private final int[] resourceTypes; // resourceTypes[r] give the type of the r^h resource, 0 = cumulative, 1 = colored public static final int CUMULATIVE = 0; public static final int COLORED = 1; private int nbEventsToAdd; private int[] datesAPEvents; private int[][] heightsAPEvents; private int[] ls; private int[] us; private int[] ld; private int[] le; private int[] ue; private int[][] c; // [COLORS] // Allow to have non-contigus colors on a given resource (+ scan the colors used (sparse)) private int[][] cUsed; // [RESOURCE][INDEX]. cUsed[r][i] give the i(th) color used on resource r. // [COLORS] /** * * @param vars The variables in the following order : * starts, durations, ends, * list_of_consumptions_of_t0, list_of_consumptions_of_t1, ... * * capacity_of_resource_r0, capacity_of_resource_r1, ... * * load_of_interestingTimePoints_0_interestingResources_0, load_of_interestingTimePoints_0_interestingResources_1, ... * load_of_interestingTimePoints_1_interestingResources_0, load_of_interestingTimePoints_1_interestingResources_1, ... * * * * @param nbTasks The number of tasks (items) * @param nbResources The number of resources (dimensions) * @param capacities The capacity of each resource * @param succ The successors matrix, where succ[t] gives the list of successors of task t * @param resourceTypes The type of each resource (possible values are : PropTTPCDynamicSweepLoads.CUMULATIVE and PropTTPCDynamicSweepLoads.COLORED) * @param interestingTimePoints The list of interesting time points * @param interestingResources The list of interesting resources */ public PropTTPCDynamicSweepLoads(IntVar[] vars, int nbTasks, int nbResources, int[] capacities, int[][] succ, int[] resourceTypes, int[] interestingTimePoints, int[] interestingResources) { super(vars, PropagatorPriority.QUADRATIC,false); this.s = solver; this.nbTasks = nbTasks; this.nbResources = nbResources; this.capacities = capacities; this.resourceTypes = resourceTypes; this.aggregateMode = false; // TODO this.greedyMode = false; this.nbTasksInFilteringAlgo = nbTasks; this.nbEventsToAdd = 0; this.datesAPEvents = null; this.heightsAPEvents = null; this.ring = new Rings(nbResources,nbTasks); this.ls = new int[nbTasks]; this.us = new int[nbTasks]; this.ld = new int[nbTasks]; this.le = new int[nbTasks]; this.ue = new int[nbTasks]; this.c = new int[nbTasks][nbResources]; int[] nbpreds = new int[nbTasks]; this.successors = new int[succ.length][]; for (int i=0;i<nbTasks;i++) { this.successors[i] = new int[succ[i].length]; for (int j=0;j<successors[i].length;j++) { this.successors[i][j] = succ[i][j]; nbpreds[successors[i][j]]++; } } this.predecessors = new int[succ.length][]; for (int i=0;i<nbTasks;i++) { this.predecessors[i] = new int[nbpreds[i]]; } int tsuc; for (int i=0;i<nbTasks;i++) { if (successors[i] != null) { for (int j=0;j<successors[i].length;j++) { tsuc = successors[i][j]; nbpreds[tsuc]--; predecessors[tsuc][nbpreds[tsuc]] = i; } } } // [COLORS] int nbRead, i; // temp cUsed = new int[nbResources][]; boolean[] colorsOnR = new boolean[nbTasks+1]; // colorsOnR[i] indicates whether or not color i is used on resource r. int nbColorsOnR; for (int r=0;r<nbResources;r++) { if (resourceTypes[r] == COLORED) { nbColorsOnR = 1; // neutral color for (int t=0;t<nbTasks;t++) { if (colorsOnR[getColor(vars,t,r,nbTasks,nbResources)] == false) { nbColorsOnR++; colorsOnR[getColor(vars,t,r,nbTasks,nbResources)] = true; } } cUsed[r] = new int[nbColorsOnR]; nbRead = 1; // neutral color i = 0; while (nbRead != nbColorsOnR) { if ( colorsOnR[i] ) { cUsed[r][nbRead] = i; nbRead++; } i++; } } else { assert (resourceTypes[r] == CUMULATIVE); cUsed[r] = null; } // [COLORS] } // ===== LOADS (begin) ===== this.loads = new IntVar[interestingTimePoints.length][interestingResources.length]; this.interestingTimePoints = interestingTimePoints; this.interestingResources = interestingResources; this.nextIndexInterestingTP = 0; int lIdx = 3*nbTasks+nbTasks*nbResources+nbResources; for (i=0;i<interestingTimePoints.length;i++) { for (int j=0;j<interestingResources.length;j++) { loads[i][j] = vars[lIdx+i*interestingResources.length+j]; } } // ===== LOADS (end) ===== } private int getColor(IntVar[] vars, int t, int r, int nbTasks, int nbResources) { return vars[3*nbTasks+t*nbResources+r].getValue(); } @Override public int getPropagationConditions(int vIdx) { return EventType.BOUND.mask + EventType.INSTANTIATE.mask; } @Override public void propagate(int evtmask) throws ContradictionException { this.mainLoop(); } @Override public void propagate(int idxVarInProp, int mask) throws ContradictionException { this.forcePropagate(EventType.FULL_PROPAGATION); } @Override public ESat isEntailed() { int minStart = Integer.MAX_VALUE; int maxEnd = Integer.MIN_VALUE; // compute min start and max end for(int is=0, id=nbTasks, ie=2*nbTasks, ih=3*nbTasks;is<this.nbTasks;is++,id++,ie++,ih+=nbResources) { // is = start index, ..., ih = height index if (!vars[is].instantiated() || !vars[id].instantiated() || !vars[ie].instantiated()) return ESat.UNDEFINED; for(int r=0;r< nbResources;r++) { if (!vars[ih+r].instantiated()) return ESat.UNDEFINED; } if (vars[is].getValue() < minStart) minStart = vars[is].getValue(); if (vars[ie].getValue() > maxEnd) maxEnd = vars[ie].getValue(); } int[] sumHeight = new int[nbResources]; int[][] countColors = new int[nbResources][]; int[] nbDistinctColors = new int[nbResources]; int ctr; // scan the time axis and check the heights and the colors for(int i=minStart;i<=maxEnd;i++) { for(int r=0;r< nbResources;r++) { if (resourceTypes[r] == CUMULATIVE) { sumHeight[r] = 0; } else if (resourceTypes[r] == COLORED) { countColors[r] = new int[nbTasks+1]; // +1 for the neutral color. nbDistinctColors[r] = 0; } else { // System.out.println(" ! unknown resource type ! "); assert false; } } for(int is=0, ie=2*nbTasks, ih=3*nbTasks;is<this.nbTasks;is++,ie++,ih += nbResources) { if ( i >= vars[is].getValue() && i < vars[ie].getValue() ) { // the task overlap the time point. for(int r=0;r< nbResources;r++) { if (resourceTypes[r] == CUMULATIVE) { sumHeight[r] += vars[ih+r].getValue(); } else if (resourceTypes[r] == COLORED) { ctr = vars[ih+r].getValue(); if (countColors[r][ctr] == 0) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } } } for(int r=0;r< nbResources;r++) { if (resourceTypes[r] == CUMULATIVE) { if (sumHeight[r] > capacities[r]) { // System.out.println(" resource (cumulative) overflow r"+r+" i="+i+" sumHeight[r]="+sumHeight[r]+" > capacities[r]="+capacities[r]); return ESat.FALSE; } } else if (resourceTypes[r] == COLORED) { if (nbDistinctColors[r] > capacities[r]) { // System.out.println(" resource (colored) overflow r"+r+" i="+i+" nbDistinctColors[r]="+nbDistinctColors[r]+" > capacities[r]="+capacities[r]); return ESat.FALSE; } } else { assert (false); } } } // scan precedence relations for (int is=0, ie=2*nbTasks;is<this.nbTasks;is++,ie++) { for (int j=0;j<successors[is].length;j++) { if ( vars[ie].getValue() > vars[successors[is][j]].getValue() ) { return ESat.FALSE; } } } return ESat.TRUE; } public void mainLoop() throws ContradictionException { int state = 0; int[][] eventsToAdd; boolean succeed = false; boolean res, max; do { // for(int is=0;is<nbTasks;is++) { // vars[is].notifyMonitors(null, null); // } // copy variable bounds into arrays //copyAndAggregate(ls,us,ld,le,ue,h); copyAndAggregate(); // ===== GREEDY MODE ===== if (greedyMode == true && state == 0) { // greedy mode is ON and it is the first loop this.sweepGreedy = new DynamicSweepGreedyKDimPrecColRings(); succeed = this.sweepGreedy.greedy(); if (succeed) { assert(this.sweepGreedy.allTasksAreFixed()); for(int is=0;is<this.nbTasksInFilteringAlgo;is++) { // update variables and stop ! vars[mapping[is]].updateLowerBound(sweepGreedy.ls(is), this); vars[mapping[is]].updateUpperBound(sweepGreedy.us(is), this); vars[mapping[is]+2*nbTasks].updateLowerBound(sweepGreedy.le(is), this); vars[mapping[is]+2*nbTasks].updateUpperBound(sweepGreedy.ue(is), this); } } } // ===== NORMAL MODE ===== if (greedyMode == false || !succeed) { // greedy mode is OFF or fails. Run the dynamic sweep this.sweepMin = new DynamicSweepMinKDimPrecColRings(); this.sweepMax = new DynamicSweepMaxKDimPrecColRings(); res = sweepMin.sweepMin(); res = sweepMax.sweepMax(); max = false; while (res) { if (max) { if (sweepMin.isSweepMaxNeeded()) { // check if sweep max should be run. res = sweepMax.sweepMax(); } else { res = false; assert(false == sweepMax.sweepMax()); } } else { res = sweepMin.sweepMin(); } max = !max; } state = 0; //update variable bounds boolean allFixed = true; for(int is=0;is<nbTasksInFilteringAlgo;is++) { vars[mapping[is]].updateLowerBound(ls[is],this); vars[mapping[is]].updateUpperBound(us[is],this); vars[mapping[is]+nbTasks].updateLowerBound(ld[is],this); vars[mapping[is]+2*nbTasks].updateLowerBound(le[is],this); vars[mapping[is]+2*nbTasks].updateUpperBound(ue[is],this); state = state + (us[is]-ls[is])+(ue[is]-le[is])+ld[is]; // ===== LOADS (begin) ===== allFixed is true iff all the tasks are fixed if (allFixed && (us[is]-ls[is])+(ue[is]-le[is]) != 0) { allFixed = false; } // ===== LOADS (end) ===== } // check ! for(int is=0;is<nbTasksInFilteringAlgo;is++) { state = state-(vars[mapping[is]].getUB()-vars[mapping[is]].getLB()) -(vars[mapping[is]+2*nbTasks].getUB()-vars[mapping[is]+2*nbTasks].getLB()) -vars[mapping[is]+nbTasks].getLB(); } // ===== LOADS (begin) ===== int lIdx = 3*nbTasks+nbTasks*nbResources+nbResources; if (allFixed && state == 0) { for (int i=0;i<interestingTimePoints.length;i++) { for (int j=0;j<interestingResources.length;j++) { loads[i][j].updateUpperBound(loads[i][j].getLB(),this); //System.out.println("load tp="+interestingTimePoints[i]+" res="+interestingResources[j]+" = "+loads[i][j].getValue()); } } } // ===== LOADS (end) ===== } } while (state!= 0); } /** * Copy bounds of rtasks into arrays. * Build an aggregated cumulative profile with instantiated tasks. * */ public void copyAndAggregate() throws ContradictionException { nbEventsToAdd = 0; mapping = new int[nbTasks]; mappingRev = new int[nbTasks]; // absolute => relative if (aggregateMode == true) { boolean[] isFixed = new boolean[nbTasks]; IncHeapEvents hEvents = new IncHeapEvents(2*nbTasks); datesAPEvents = null; heightsAPEvents = null; // FIRST LOOP: generate events related to fixed tasks; record fixed tasks (in isFixed) for(int is=0, id=nbTasks, ie=2*nbTasks, ih=3*nbTasks;is<nbTasks;is++,id++,ie++,ih+=nbResources) { if (vars[is].instantiated() && vars[id].instantiated() && vars[ie].instantiated()) { // the task is instantiated. isFixed[is] = true; hEvents.add(vars[is].getValue(), is, Event.FSCP, -1); hEvents.add(vars[ie].getValue(), is, Event.FECP, -1); } } // SECOND LOOP: update bounds of succ and pred of fixed tasks. int len; for(int is=0, ie=2*nbTasks; is<isFixed.length;is++,ie++) { if ( isFixed[is] ) { for (int sid=0;sid<successors[is].length;sid++) { // update earliest start of successors. vars[successors[is][sid]].updateLowerBound(vars[ie].getValue(), this); } for (int sid=0;sid<predecessors[is].length;sid++) { // update latest end of predecessors. vars[2*nbTasks+predecessors[is][sid]].updateUpperBound(vars[ie].getValue(), this); } } } // THIRD LOOP: copy of variable bounds. int copyIdx = 0; for(int is=0, id=nbTasks, ie=2*nbTasks, ih=3*nbTasks;is<nbTasks;is++,id++,ie++,ih+=nbResources) { if ( !isFixed[is] ) { ls[copyIdx] = vars[is].getLB(); us[copyIdx] = vars[is].getUB(); ld[copyIdx] = vars[id].getLB(); le[copyIdx] = vars[ie].getLB(); ue[copyIdx] = vars[ie].getUB(); for (int r=0;r<nbResources;r++) { c[copyIdx][r] = vars[ih+r].getLB(); } mapping[copyIdx] = is; mappingRev[is] = copyIdx; copyIdx++; } else { // task 'is' is fixed => task 'is' has no mappingRev.. mappingRev[is] = -1; } } nbTasksInFilteringAlgo = copyIdx; // build the aggregated profile if (!hEvents.isEmpty()) { // there are instantiated tasks int hidx; datesAPEvents = new int[hEvents.nbEvents()]; heightsAPEvents = new int[nbResources][hEvents.nbEvents()]; int evtIdx = 0; int[] curEvt = hEvents.peek(); int delta, date; int[] height, prevHeight; delta = curEvt[0]; date = delta; prevHeight = new int[nbResources]; // init. to 0 height = new int[nbResources]; // init. to 0 while (!hEvents.isEmpty()) { if (date != delta) { if (differ(prevHeight, height)) { // variation of the consumption datesAPEvents[evtIdx] = delta; // event date for (int r=0;r<nbResources;r++) { heightsAPEvents[r][evtIdx] = prevHeight[r]-height[r]; // decrement } evtIdx++; for (int r=0;r<nbResources;r++) { //prevHeight = height.clone(); prevHeight[r] = height[r]; } } delta = date; } curEvt = hEvents.poll(); hidx = 3 * nbTasks + curEvt[1] * nbResources; if (curEvt[2] == Event.FSCP) { for (int r=0;r<nbResources;r++) { height[r] += vars[hidx+r].getValue(); } } else if (curEvt[2] == Event.FECP) { for (int r=0;r<nbResources;r++) { height[r] -= vars[hidx+r].getValue(); } } if (!hEvents.isEmpty()) date = hEvents.peekDate(); } // creer le dernier ! datesAPEvents[evtIdx] = date; for (int r=0;r<nbResources;r++) { heightsAPEvents[r][evtIdx] = prevHeight[r]-height[r]; // decrement } nbEventsToAdd = evtIdx + 1; } } else { for(int is=0, id=nbTasks, ie=2*nbTasks, ih=3*nbTasks;is<nbTasks;is++,id++,ie++,ih+=nbResources) { ls[is] = vars[is].getLB(); us[is] = vars[is].getUB(); ld[is] = vars[id].getLB(); le[is] = vars[ie].getLB(); ue[is] = vars[ie].getUB(); for(int r=0;r<nbResources;r++) { c[is][r] = vars[ih+r].getValue(); } mapping[is] = is; mappingRev[is] = is; } } } private boolean differ(int[] a, int[] b) { int i=0; while (i<a.length && (a[i] == b[i]) ) { i++; } return i<a.length; } public int getRelativeId(int absoluteId) { return mappingRev[absoluteId]; } public int getAbsoluteId(int relativeId) { return mapping[relativeId]; } /** * Circular double linked lists which record the status of the tasks. */ class Rings { protected final int nbItems; public final int ptrNone; public final int ptrReady; public final int ptrCheck; public final int ptrConflict; // add the resource id to get the corresponding list of tasks in conflict public final static int NONE = -1; public final static int READY = -2; public final static int CHECK = -3; protected final int[] ring; protected final int[] backward; protected final int[] forward; public Rings(int k, int n) { this.nbItems = n; this.ring = new int[n+3+k]; this.backward = new int[n+3+k]; this.forward = new int[n+3+k]; for (int i=0;i<n;i++) { this.ring[i] = -1; this.backward[i] = i; this.forward[i] = i; } this.ptrNone = n; this.ptrReady = n+1; this.ptrCheck = n+2; this.ptrConflict = n+3; for (int i=n;i<ring.length;i++) { ring[i] = -666; backward[i] = i; forward[i] = i; } } public void setNone(int t) { // supprime t du ring courant forward[backward[t]] = forward[t]; // le suivant du precedent de t = le suivant de t backward[forward[t]] = backward[t]; // le precedent du suivant de t = le precedent de t // pas de ring pour none en pratique. (car aucun parcours de cette liste) forward[t] = t; backward[t] = t; ring[t] = NONE; } public void setReady(int t) { // supprime t du ring courant forward[backward[t]] = forward[t]; // le suivant du precedent de t = le suivant de t backward[forward[t]] = backward[t]; // le precedent du suivant de t = le precedent de t // insertion dans le ring none. forward[t] = forward[ptrReady]; backward[t] = ptrReady; forward[ptrReady] = t; backward[forward[t]] = t; ring[t] = READY; } public void setCheck(int t) { // supprime t du ring courant forward[backward[t]] = forward[t]; // le suivant du precedent de t = le suivant de t backward[forward[t]] = backward[t]; // le precedent du suivant de t = le precedent de t // insertion dans le ring check. forward[t] = forward[ptrCheck]; backward[t] = ptrCheck; forward[ptrCheck] = t; backward[forward[t]] = t; ring[t] = CHECK; } public void setConflict(int t, int rc) { assert(rc >= 0); // supprime t du ring courant forward[backward[t]] = forward[t]; // le suivant du precedent de t = le suivant de t backward[forward[t]] = backward[t]; // le precedent du suivant de t = le precedent de t // insertion dans le ring conflict. forward[t] = forward[ptrConflict+rc]; backward[t] = ptrConflict+rc; forward[ptrConflict+rc] = t; backward[forward[t]] = t; ring[t] = rc; } public int firstInCheck() { return forward[ptrCheck]; } public int firstInConflict(int r) { return forward[ptrConflict+r]; } public int firstInReady() { return forward[ptrReady]; } public int next(int t) { return forward[t]; } public boolean inConflict(int t) { return (ring[t] >= 0); } public boolean inNone(int t) { return (ring[t] == NONE); } public boolean inReady(int t) { return (ring[t] == READY); } public boolean inCheck(int t) { return (ring[t] == CHECK); } public boolean isEmptyReady() { return (ptrReady == forward[ptrReady]); } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("NONE : "+noneToString()+"\n") .append("READY : "+readyToString()+"\n") .append("CHECK : "+checkToString()+"\n"); // .append("CONFLICT : "+conflictToString()+"\n"); return sb.toString(); } public String noneToString() { StringBuffer sb = new StringBuffer("[ "); for (int i=0;i<nbItems;i++) { if (ring[i] == NONE) { sb.append(" t"+i); } } sb.append(" ]"); return sb.toString(); } public String readyToString() { StringBuffer sb = new StringBuffer("[ "); int ptrCur = forward[ptrReady]; while (ptrCur != ptrReady) { sb.append(" t"+ptrCur); ptrCur = forward[ptrCur]; } sb.append(" ]"); return sb.toString(); } public String checkToString() { StringBuffer sb = new StringBuffer("[ "); int ptrCur = forward[ptrCheck]; while (ptrCur != ptrCheck) { sb.append(" t"+ptrCur); ptrCur = forward[ptrCur]; } sb.append(" ]"); return sb.toString(); } } /** * Heap recording the events by ascending order. */ class IncHeapEvents { private int[] date; // Key ! private int[] task; private int[] type; private int[] dec; private int size; private int nbEvents; public final int[][] bufferPoll; public IncHeapEvents(int _size) { this.nbEvents = 0; this.size = _size; date = new int[size]; task = new int[size]; type = new int[size]; dec = new int[size]; bufferPoll = new int[size][]; } public void clear() { nbEvents = 0; } /** * Inserts the specified event into this heap * @param _date * @param _task * @param _type * @param _dec */ public void add(int _date, int _task, int _type, int _dec) { int i = nbEvents; int parent = parent(i); while ( (i>0) && (date[parent] > _date) ) { date[i] = date[parent]; task[i] = task[parent]; type[i] = type[parent]; dec[i] = dec[parent]; i = parent; parent = parent(i); } date[i] = _date; task[i] = _task; type[i] = _type; dec[i] = _dec; nbEvents++; } /** * Retrieves and removes the top of this heap, or returns null if this queue is empty. * @return */ public int[] poll() { if (nbEvents == 0) return null; int[] top = new int[4]; top[0] = date[0]; top[1] = task[0]; top[2] = type[0]; top[3] = dec[0]; nbEvents--; date[0] = date[nbEvents]; task[0] = task[nbEvents]; type[0] = type[nbEvents]; dec[0] = dec[nbEvents]; int vdate = date[0]; int vtask = task[0]; int vtype = type[0]; int vdec = dec[0]; int i = 0; int j; while (!isLeaf(i)) { j = leftChild(i); if (hasRightChild(i) && date[rightChild(i)] < date[leftChild(i)]) { j = rightChild(i); } if (vdate <= date[j]) break; date[i] = date[j]; task[i] = task[j]; type[i] = type[j]; dec[i] = dec[j]; i = j; } date[i] = vdate; task[i] = vtask; type[i] = vtype; dec[i] = vdec; return top; } public int pollAllTopItems() { if (nbEvents == 0) return 0; int firstDate = date[0]; int nbExtractedItems = 0; while (!isEmpty() && date[0] == firstDate) { bufferPoll[nbExtractedItems] = poll(); if (bufferPoll[nbExtractedItems] != null) { nbExtractedItems++; } else { break; } } return nbExtractedItems; } /** * Removes the top of this heap. (does not check if empty) * @return */ public void remove() { nbEvents--; date[0] = date[nbEvents]; task[0] = task[nbEvents]; type[0] = type[nbEvents]; dec[0] = dec[nbEvents]; int vdate = date[0]; int vtask = task[0]; int vtype = type[0]; int vdec = dec[0]; int i = 0; int j; while (!isLeaf(i)) { j = leftChild(i); if (hasRightChild(i) && date[rightChild(i)] < date[leftChild(i)]) { j = rightChild(i); } if (vdate <= date[j]) break; date[i] = date[j]; task[i] = task[j]; type[i] = type[j]; dec[i] = dec[j]; i = j; } date[i] = vdate; task[i] = vtask; type[i] = vtype; dec[i] = vdec; } /** * Retrieves, but does not remove, the top event of this heap. * @return */ public int[] peek() { if (isEmpty()) return null; else { int[] res = new int[4]; res[0] = date[0]; res[1] = task[0]; res[2] = type[0]; res[3] = dec[0]; return res; } } /** * Retrieves, but does not remove, the date of the top event of this heap. Doesn't check if the heap is empty. * @return */ public int peekDate() { return date[0]; } /** * Retrieves, but does not remove, the task of the top event of this heap. Doesn't check if the heap is empty. * @return */ public int peekTask() { return task[0]; } /** * Retrieves, but does not remove, the type of the top event of this heap. Doesn't check if the heap is empty. * @return */ public int peekType() { return type[0]; } /** * Retrieves, but does not remove, the decrement of the top event of this heap. Doesn't check if the heap is empty. * @return */ public int peekDec() { return dec[0]; } public boolean isEmpty() { return (nbEvents == 0); } private int parent(int _child) { return ((_child + 1) >> 1) - 1; } private int leftChild(int _parent) { return ((_parent + 1) << 1) - 1; } private int rightChild(int _parent) { return ((_parent + 1) << 1); } private boolean isLeaf(int i) { return ( (((i + 1) << 1) - 1) >= nbEvents); } private boolean hasRightChild(int i) { return ( ((i + 1) << 1) < nbEvents); } public String toString() { String res = ""; int i; for(i=0;i<nbEvents;i++) { res += "<date="+date[i]+",task="+task[i]+",type="; switch(type[i]) { case Event.SCP : res += "SCP"; break; case Event.ECP : res += "ECP"; break; case Event.PR : res += "PR"; break; case Event.CCP : res += "CCP"; break; case Event.FSCP : res += "FSCP"; break; case Event.FECP : res += "FECP"; break; default : res += "UNKNOWN EVENT"; assert(false); break; } res += ",dec="+dec[i]+">\n"; } return res; } public String peekEvent() { String res = ""; res += "<date="+date[0]+",task="+task[0]+",type="; switch(type[0]) { case Event.SCP : res += "SCP"; break; case Event.ECP : res += "ECP"; break; case Event.PR : res += "PR"; break; case Event.CCP : res += "CCP"; break; case Event.FSCP : res += "FSCP"; break; case Event.FECP : res += "FECP"; break; default : res += "UNKNOWN EVENT"; assert(false); break; } res += ",dec="+dec[0]+">\n"; return res; } public int nbEvents() { return nbEvents; } } /** * Heap recording the events by descending order. */ class DecHeapEvents { private int[] date; // Key ! private int[] task; private int[] type; private int[] dec; private int size; private int nbEvents; public final int[][] bufferPoll; public DecHeapEvents(int _size) { this.nbEvents = 0; this.size = _size; date = new int[size]; task = new int[size]; type = new int[size]; dec = new int[size]; bufferPoll = new int[size][]; } public void clear() { nbEvents = 0; } /** * Inserts the specified event into this heap * @param _date * @param _task * @param _type * @param _dec */ public void add(int _date, int _task, int _type, int _dec) { int i = nbEvents; int parent = parent(i); while ( (i>0) && (date[parent] < _date) ) { date[i] = date[parent]; task[i] = task[parent]; type[i] = type[parent]; dec[i] = dec[parent]; i = parent; parent = parent(i); } date[i] = _date; task[i] = _task; type[i] = _type; dec[i] = _dec; nbEvents++; } /** * Retrieves and removes the top of this heap, or returns null if this queue is empty. * @return */ public int[] poll() { if (nbEvents == 0) return null; int[] top = new int[4]; top[0] = date[0]; top[1] = task[0]; top[2] = type[0]; top[3] = dec[0]; nbEvents--; date[0] = date[nbEvents]; task[0] = task[nbEvents]; type[0] = type[nbEvents]; dec[0] = dec[nbEvents]; int vdate = date[0]; int vtask = task[0]; int vtype = type[0]; int vdec = dec[0]; int i = 0; int j; while (!isLeaf(i)) { j = leftChild(i); if (hasRightChild(i) && date[rightChild(i)] > date[leftChild(i)]) { j = rightChild(i); } if (vdate >= date[j]) break; date[i] = date[j]; task[i] = task[j]; type[i] = type[j]; dec[i] = dec[j]; i = j; } date[i] = vdate; task[i] = vtask; type[i] = vtype; dec[i] = vdec; return top; } public int pollAllTopItems() { if (nbEvents == 0) return 0; int firstDate = date[0]; int nbExtractedItems = 0; while (!isEmpty() && date[0] == firstDate) { bufferPoll[nbExtractedItems] = poll(); if (bufferPoll[nbExtractedItems] != null) { nbExtractedItems++; } else { break; } } return nbExtractedItems; } /** * Removes the top of this heap. (without any check) * @return */ public void remove() { nbEvents--; date[0] = date[nbEvents]; task[0] = task[nbEvents]; type[0] = type[nbEvents]; dec[0] = dec[nbEvents]; int vdate = date[0]; int vtask = task[0]; int vtype = type[0]; int vdec = dec[0]; int i = 0; int j; while (!isLeaf(i)) { j = leftChild(i); if (hasRightChild(i) && date[rightChild(i)] > date[leftChild(i)]) { j = rightChild(i); } if (vdate >= date[j]) break; date[i] = date[j]; task[i] = task[j]; type[i] = type[j]; dec[i] = dec[j]; i = j; } date[i] = vdate; task[i] = vtask; type[i] = vtype; dec[i] = vdec; } /** * Retrieves, but does not remove, the top event of this heap. * @return */ public int[] peek() { if (isEmpty()) return null; else { int[] res = new int[4]; res[0] = date[0]; res[1] = task[0]; res[2] = type[0]; res[3] = dec[0]; return res; } } /** * Retrieves, but does not remove, the date of the top event of this heap. Doesn't check if the heap is empty. * @return */ public int peekDate() { return date[0]; } /** * Retrieves, but does not remove, the task of the top event of this heap. Doesn't check if the heap is empty. * @return */ public int peekTask() { return task[0]; } /** * Retrieves, but does not remove, the type of the top event of this heap. Doesn't check if the heap is empty. * @return */ public int peekType() { return type[0]; } /** * Retrieves, but does not remove, the decrement of the top event of this heap. Doesn't check if the heap is empty. * @return */ public int peekDec() { return dec[0]; } public boolean isEmpty() { return (nbEvents == 0); } private int parent(int _child) { return ((_child + 1) >> 1) - 1; } private int leftChild(int _parent) { return ((_parent + 1) << 1) - 1; } private int rightChild(int _parent) { return ((_parent + 1) << 1); } private boolean isLeaf(int i) { return ( (((i + 1) << 1) - 1) >= nbEvents); } private boolean hasRightChild(int i) { return ( ((i + 1) << 1) < nbEvents); } public String toString() { String res = ""; int i; for(i=0;i<nbEvents;i++) { res += "<date="+date[i]+",task="+task[i]+",type="; switch(type[i]) { case Event.SCP : res += "SCP"; break; case Event.ECP : res += "ECP"; break; case Event.PR : res += "PR"; break; case Event.CCP : res += "CCP"; break; case Event.FSCP : res += "FSCP"; break; case Event.FECP : res += "FECP"; break; default : res += "UNKNOWN EVENT"; assert(false); break; } res += ",dec="+dec[i]+"> "; } return res; } public String peekEvent() { String res = ""; res += "<date="+date[0]+",task="+task[0]+",type="; switch(type[0]) { case Event.SCP : res += "SCP"; break; case Event.ECP : res += "ECP"; break; case Event.PR : res += "PR"; break; case Event.CCP : res += "CCP"; break; case Event.FSCP : res += "FSCP"; break; case Event.FECP : res += "FECP"; break; default : res += "UNKNOWN EVENT"; assert(false); break; } res += ",dec="+dec[0]+"> "; return res; } public int nbEvents() { return nbEvents; } } /** * Event types */ class Event { public final static int SCP = 0; // start of a compulsory part of a fixed task. public final static int ECP = 1; // end of a compulsory part of a non-fixed task. public final static int PR = 2; // earliest start of a task. public final static int CCP = 3; // Latest start of a task initially without compulsory part. public final static int RS = 7; // earliest end of a task, needed for precedences ("Release Successors") public final static int FSCP = 4; // Start of a compulsory part of a fixed task. public final static int FECP = 5; // End of a compulsory part of a fixed task. public final static int AP = 6; // Aggregation event } class DynamicSweepMinKDimPrecColRings { private final IncHeapEvents hEvents; private int delta; private int date; private final int[] bufferPR; private int nbItemsBufferPR; private final int[] bufferRS; private int nbItemsBufferRS; private boolean prunning; private final int[] nbDistinctColors; // [RESOURCE] # of distinct colours on the given resource private final int[][] countColors; // [RESOURCE][COLOR] give the number of tasks using a given color on a given resource. private final int[] gap; private final int[] gapi; private final int[] nbpreds; private int maxDate; private int minDate; // h[task][resource] public DynamicSweepMinKDimPrecColRings() { this.hEvents = new IncHeapEvents(4*nbTasksInFilteringAlgo+2*(nbTasks-nbTasksInFilteringAlgo)); this.gap = new int[nbResources]; this.gapi = new int[nbResources]; this.nbpreds = new int[nbTasksInFilteringAlgo]; // [COLORS] this.nbDistinctColors = new int[nbResources]; this.countColors = new int[nbResources][]; for (int r=0;r<nbResources;r++) { if ( resourceTypes[r] == COLORED ) { this.countColors[r] = new int[nbTasks+1]; // neutral color } else { assert (resourceTypes[r] == CUMULATIVE); this.countColors[r] = null; } } // [COLORS] this.bufferPR = new int[nbTasksInFilteringAlgo]; this.nbItemsBufferPR = 0; this.bufferRS = new int[nbTasksInFilteringAlgo]; this.nbItemsBufferRS = 0; this.prunning = false; } public void addAggregatedProfile() { for(int i=0;i<nbEventsToAdd;i++) { hEvents.add(datesAPEvents[i], -1, Event.AP, i); if (datesAPEvents[i] < this.minDate) minDate = datesAPEvents[i]; if (datesAPEvents[i] > this.maxDate) maxDate = datesAPEvents[i]; } } public void adjustMin(int t, int minStart, int minEnd) { //System.out.println("adjust task="+t+" to "+minStart); assert(minStart < minEnd); assert(minStart + ld[t] == minEnd); if ( minStart > ls[t] ) { prunning = true; ls[t] = minStart; le[t] = minEnd; } } protected void generateMinEvents() { this.hEvents.clear(); for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { assert (countColors.length >= 1); // at least the neutral color. countColors[r][0] = 1; // avoid the handle the specific case of the neutral color during the sweep. nbDistinctColors[r] = 0; for (int i=1;i<countColors[r].length;i++) { countColors[r][i] = 0; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] = capacities[r]; gapi[r] = capacities[r]; } // [COLORS] } int absoluteT, relativePred; for(int t=0;t<nbTasksInFilteringAlgo;t++) { absoluteT = getAbsoluteId(t); if ( t == 0 || ls[t] < minDate ) minDate = ls[t]; if ( t == 0 || ue[t] > maxDate ) maxDate = ue[t]; // the task has at least 1 predecessors. // scan pred, if the current pred was not fixed (copyAndAggregate(...)) then nbpred[t]++; for (int pred=0;pred<predecessors[absoluteT].length;pred++) { relativePred = getRelativeId(predecessors[absoluteT][pred]); if ( relativePred != -1 ) { nbpreds[t]++; } } if (nbpreds[t] == 0) { ring.setNone(t); hEvents.add(us[t],t, Event.SCP,-1); if ( us[t] < le[t] ) { // has a compulsory part hEvents.add(le[t],t, Event.ECP,-1); } if ( successors[absoluteT].length != 0 ) { hEvents.add(le[t],t, Event.RS,-1); } if ( ls[t] < us[t] ) { // not scheduled hEvents.add(ls[t],t, Event.PR,-1); } else { ring.setReady(t); } } } } public boolean sweepMin() throws ContradictionException { // ===== LOADS (begin) ===== nextIndexInterestingTP = 0; // ===== LOADS (end) ===== //this.printTasks(); prunning = false; generateMinEvents(); addAggregatedProfile(); while (!hEvents.isEmpty()) { processEvents(); filter(); } assert (minProperty()); return prunning; } private void addTask(int t) throws ContradictionException { if (delta > us[t]) { contradiction(null, "earliest task of t"+t+" introduce at "+delta+" (after its latest start)"); } adjustMin(t,delta,delta+ld[t]); int absoluteT = getAbsoluteId(t); ring.setNone(t); if (ls[t] == delta) { // ls of task t is reintroduce at delta. (=> no PR) if (ls[t] == us[t]) { // task t is fixed (=> no SCP but directly decrease the gap) for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { int ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[t][r]; } // [COLORS] } ring.setReady(t); } else { bufferPR[nbItemsBufferPR] = t; nbItemsBufferPR++; hEvents.add(us[t],t, Event.SCP,-1); } if ( us[t] < le[t] ) { // has a compulsory part hEvents.add(le[t],t, Event.ECP,-1); } if ( successors[absoluteT].length != 0) { hEvents.add(le[t],t, Event.RS,-1); } } else { // ls of task t is reintroduce after delta. (=> SCP) hEvents.add(us[t],t, Event.SCP,-1); if ( us[t] < le[t] ) { // has a compulsory part hEvents.add(le[t],t, Event.ECP,-1); } if ( ls[t] < us[t] ) { // not scheduled hEvents.add(ls[t],t, Event.PR,-1); } else { ring.setReady(t); } if ( successors[absoluteT].length != 0) { hEvents.add(le[t],t, Event.RS,-1); } } } private void processEvents() throws ContradictionException { int t, tp, ecpi, rc, absoluteT, relativeSucc, ctr; int nbExtractedItems = hEvents.pollAllTopItems(); // result in hEvents.bufferPoll int[][] evts = hEvents.bufferPoll; // 0:date,1:task;2:type;3:dec nbItemsBufferPR = 0; nbItemsBufferRS = 0; assert(nbExtractedItems != 0); delta = evts[0][0]; for (int i=0;i<nbExtractedItems;i++) { if ( evts[i][2] == Event.SCP ) { t = evts[i][1]; ecpi = le[t]; if (ring.inConflict(t)) { // = CONFLICT adjustMin(t,us[t],ue[t]); ring.setReady(t); } else if (ring.inCheck(t)) { ring.setReady(t); } if (delta < le[t]) { for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[t][r]; } // [COLORS] } if (ecpi <= delta) { hEvents.add(le[t],t, Event.ECP,-1); } } } else if ( evts[i][2] == Event.ECP ) { t = evts[i][1]; if (ring.inConflict(t)) { // = CONFLICT adjustMin(t,us[t],ue[t]); ring.setReady(t); } if (le[t] > delta) { hEvents.add(le[t],t, Event.ECP,-1); } else { if (ring.inCheck(t)) { ring.setReady(t); } for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 1 ) { nbDistinctColors[r]--; } countColors[r][ctr]--; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] += c[t][r]; } // [COLORS] } } } else if ( evts[i][2] == Event.PR ) { // PR event are processed later bufferPR[nbItemsBufferPR] = evts[i][1]; nbItemsBufferPR++; } else if ( evts[i][2] == Event.RS ) { bufferRS[nbItemsBufferRS] = evts[i][1]; nbItemsBufferRS++; } else if ( evts[i][2] == Event.AP ) { // <0 == SCP ; >0 == ECP assert false; // no AGGREGATE EVENTS for colors ! for(int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = heightsAPEvents[r][evts[i][3]]; if ( ctr < 0 ) { // start of fixed task ctr = -ctr; if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } else if ( ctr > 0 ) { if ( countColors[r][ctr] == 1 ) { nbDistinctColors[r]--; } countColors[r][ctr]--; } else { System.out.println(" AGGREGATED EVENT : NEUTRAL COLOR (TBD) "); // TODO do not generate such events. assert (false); } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] += heightsAPEvents[r][evts[i][3]]; } // [COLORS] } } } for (int i=0;i<nbItemsBufferRS;i++) { t = bufferRS[i]; if ( delta == le[t] && !ring.inConflict(t) ) { absoluteT = getAbsoluteId(t); for (int s=0;s<successors[absoluteT].length;s++) { relativeSucc = getRelativeId(successors[absoluteT][s]); if (relativeSucc != -1) { // the successor was not fixed during copyAndAggregate(...) nbpreds[relativeSucc]--; assert (nbpreds[relativeSucc] >= 0); if (nbpreds[relativeSucc] == 0) { addTask(relativeSucc); } } } } else if ( ring.inConflict(t) ) { // t is in conflict hEvents.add(delta + ld[t], t, Event.RS, -1); // push the event to first possible date. } else { hEvents.add(le[t], t, Event.RS, -1); // synchronize the event with the current le[t]. } } if (!hEvents.isEmpty()) { date = hEvents.peekDate(); } else { date = maxDate; } for (int i=0;i<nbItemsBufferPR;i++) { // PR event are processed now t = bufferPR[i]; if ( ( rc = exceedGap(t)) != -1) { // one of the gap is exceeded ring.setConflict(t,rc); } else if (le[t] > date) { ring.setCheck(t); } else { ring.setReady(t); } } } private void filter() throws ContradictionException { int r,t,next,rc,ecpi; boolean b; for (r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { if ( nbDistinctColors[r] > capacities[r] ) { contradiction(null, "overload (colored) in ["+delta+","+date+") on resource r"+r); } } else { assert (resourceTypes[r] == CUMULATIVE); if (gap[r]<0) { contradiction(null, "overload (cumulative) in ["+delta+","+date+") on resource r"+r); } } // [COLORS] } // ===== LOADS (begin) ===== if ( nextIndexInterestingTP < interestingTimePoints.length ) { int iTP = interestingTimePoints[nextIndexInterestingTP]; if ( iTP < date && iTP >= delta ) { // the interesting time point is in [delta,date) do { // update the load variables of the interesting time points in [delta,date) for (int idr=0;idr<interestingResources.length;idr++) { r = interestingResources[idr]; if ( resourceTypes[r] == COLORED ) { loads[iTP][r].updateLowerBound(nbDistinctColors[r], aCause); } else { loads[iTP][r].updateLowerBound(capacities[r]-gap[r],aCause); } } nextIndexInterestingTP++; if (nextIndexInterestingTP >= interestingTimePoints.length) break; iTP = interestingTimePoints[nextIndexInterestingTP]; } while ( iTP < date ); } } // ===== LOADS (end) ===== for (r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { if (nbDistinctColors[r] == capacities[r]) { // capacity is reached next = ring.firstInCheck(); while (next != ring.ptrCheck) { t = next; next = ring.next(t); if ( countColors[r][c[t][r]] == 0 ) { if (le[t] > delta) { ring.setConflict(t,r); } else { ring.setReady(t); } } } } } else { assert (resourceTypes[r] == CUMULATIVE); if (gapi[r] > gap[r]) { // the gap decreases next = ring.firstInCheck(); while (next != ring.ptrCheck) { t = next; next = ring.next(t); if (c[t][r]>gap[r]) { if (le[t] > delta) { ring.setConflict(t,r); } else { ring.setReady(t); } } } gapi[r] = gap[r]; } } // [COLORS] } for (r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { b = nbDistinctColors[r] < capacities[r]; next = ring.firstInConflict(r); while (next != ring.ptrConflict+r) { t = next; next = ring.next(t); if ( b || countColors[r][c[t][r]] != 0 ) { if ((rc=exceedGap(t)) != -1) { // one of the gapi is exceeded. ring.setConflict(t,rc); } else { ecpi = le[t]; adjustMin(t,delta,delta+ld[t]); if (le[t]>date) {ring.setCheck(t);} else {ring.setReady(t);} if (us[t]>=ecpi && us[t]<le[t]) { hEvents.add(le[t],t, Event.ECP,-1); } } } } } else { assert (resourceTypes[r] == CUMULATIVE); if (gapi[r]<gap[r]) { // the gap increases next = ring.firstInConflict(r); while (next != ring.ptrConflict+r) { t = next; next = ring.next(t); if (c[t][r]<=gap[r]) { if ((rc=exceedGap(t)) != -1) { // one of the gapi is exceeded. ring.setConflict(t,rc); } else { ecpi = le[t]; adjustMin(t,delta,delta+ld[t]); if (le[t]>date) {ring.setCheck(t);} else {ring.setReady(t);} if (us[t]>=ecpi && us[t]<le[t]) { hEvents.add(le[t],t, Event.ECP,-1); } } } } gapi[r] = gap[r]; } } // [COLORS] } } // It returns -1 is the task does not exceed any gap. // Otherwise, it returns the a resource where it exceeds. private int exceedGap(int t) { for (int r=0;r<nbResources;r++) { if ( resourceTypes[r] == COLORED ) { if ( nbDistinctColors[r] == capacities[r] && countColors[r][c[t][r]] == 0 ) return r; } else { assert (resourceTypes[r] == CUMULATIVE); if ( c[t][r] > gap[r] ) return r; } } return -1; } public boolean isSweepMaxNeeded() { int t, tMaxEcp = -1, maxEcp1 = Integer.MIN_VALUE, maxEcp2 = Integer.MIN_VALUE; boolean cp; int scp, ecp; // compute the 2 max values of ecp. for(t=0;t<nbTasksInFilteringAlgo;t++) { if ( us[t] < le[t] ) { cp = true; scp = us[t]; ecp = le[t]; } else { cp = false; scp = -1; ecp = -1; } if (cp && ecp >= maxEcp1) { maxEcp2 = maxEcp1; maxEcp1 = ecp; tMaxEcp = t; } else if (cp && ecp > maxEcp2) { maxEcp2 = ecp; } } if (tMaxEcp == -1) return false; // if no CP, stop saturation ! for(t=0;t<nbTasksInFilteringAlgo;t++) { if ((ls[t] != us[t] && t != tMaxEcp && us[t] < maxEcp1) || (ls[t] != us[t] && t == tMaxEcp && us[t] < maxEcp2)) { return true; } } return false; } private boolean minProperty() { int[] sum = new int[nbResources]; int[][] count = new int[nbResources][]; int[] nbDistinct = new int[nbResources]; int t, tp, i, r, absoluteT, relativeSucc; for(t=0;t<nbTasksInFilteringAlgo;t++) { // for each task t ... absoluteT = getAbsoluteId(t); assert (absoluteT != -1); for (int succ=0;succ<successors[absoluteT].length;succ++) { // (check precedences relations) relativeSucc = getRelativeId(successors[absoluteT][succ]); if (relativeSucc != -1 && le[t]>ls[relativeSucc]) return false; } for(i=ls[t];i<le[t];i++) { // ... scheduled to its earliest position ... for (r=0;r<nbResources;r++) { if (resourceTypes[r] == CUMULATIVE) { sum[r] = c[t][r]; } else if (resourceTypes[r] == COLORED) { count[r] = new int[nbTasks+1]; // alloc + init to 0 if (c[t][r] != 0) { // // iff this is not the neutral color nbDistinct[r] = 1; count[r][c[t][r]] = 1; } } else { System.out.println(" ! unknown resource type ! "); return false; } } for(tp=0;tp<nbTasksInFilteringAlgo;tp++) { // ... + the aggregated profile (without t) ... if ((t != tp) && (us[tp] <= i) && (i<le[tp])) { for (r=0;r<nbResources;r++) { if (resourceTypes[r] == CUMULATIVE) { sum[r] += c[tp][r]; } else { // COLORED if (c[tp][r] != 0) { if (count[r][c[t][r]] == 0) { nbDistinct[r]++; } count[r][c[t][r]]++; } } } } } for (r=0;r<nbResources;r++) { // ... check that the limit is never exceeded if (resourceTypes[r] == CUMULATIVE) { if (sum[r] > capacities[r]) { return false; } } else { if (nbDistinct[r] > capacities[r]) { return false; } } } } } return true; } // ==================================================== // ==================================================== public void printEvent(int[] evt) { System.out.print("<date="+evt[0]+",task="+evt[1]+",type="); switch (evt[2]) { case Event.SCP : System.out.print("SCP"); break; case Event.ECP : System.out.print("ECP"); break; case Event.PR : System.out.print("PR"); break; case Event.CCP : System.out.print("CCP"); break; case Event.FSCP : System.out.print("FSCP"); break; case Event.FECP : System.out.print("FECP"); break; case Event.AP : System.out.print("AP"); break; case Event.RS : System.out.print("RS"); break; } System.out.println(",dec="+evt[3]+">"); } public void printTasks() { for (int t=0;t<nbTasksInFilteringAlgo;t++) { printTask(t); } } public void printTask(int i) { int absoluteT = getAbsoluteId(i); System.out.print("Task:"+i+"("+absoluteT+"): s:["+ls[i]+".."+us[i]+"] d:["+ld[i]+".."+ld[i]+"] e:["+le[i]+".."+ue[i]+"]"); if (us[i] < le[i]) System.out.println("| scp:"+us[i]+" ecp:"+le[i]); else System.out.println();; } } class DynamicSweepMaxKDimPrecColRings { private final DecHeapEvents hEvents; private int delta; private int date; private final int[] bufferPR; private int nbItemsBufferPR; private final int[] bufferRS; private int nbItemsBufferRS; private boolean prunning; private final int[] nbDistinctColors; // [RESOURCE] # of distinct colours on the given resource private final int[][] countColors; // [RESOURCE][COLOR] give the number of tasks using a given color on a given resource. private final int[] gap; private final int[] gapi; private final int[] nbsuccs; private int maxDate; private int minDate; // h[task][resource] public DynamicSweepMaxKDimPrecColRings() { this.hEvents = new DecHeapEvents(4*nbTasksInFilteringAlgo+2*(nbTasks-nbTasksInFilteringAlgo)); this.gap = new int[nbResources]; this.gapi = new int[nbResources]; this.nbsuccs = new int[nbTasksInFilteringAlgo]; // [COLORS] this.nbDistinctColors = new int[nbResources]; this.countColors = new int[nbResources][]; for (int r=0;r<nbResources;r++) { if ( resourceTypes[r] == COLORED ) { this.countColors[r] = new int[nbTasks+1]; // neutral color } else { assert (resourceTypes[r] == CUMULATIVE); this.countColors[r] = null; } } // [COLORS] this.bufferPR = new int[nbTasksInFilteringAlgo]; this.nbItemsBufferPR = 0; this.bufferRS = new int[nbTasksInFilteringAlgo]; this.nbItemsBufferRS = 0; this.prunning = false; } public void addAggregatedProfile() { for(int i=0;i<nbEventsToAdd;i++) { hEvents.add(datesAPEvents[i]-1, -1, Event.AP, i); if (datesAPEvents[i] < this.minDate) minDate = datesAPEvents[i]; if (datesAPEvents[i] > this.maxDate) maxDate = datesAPEvents[i]; } } public void adjustMax(int t, int maxStart, int maxEnd) { assert(maxStart < maxEnd); assert(maxStart + ld[t] == maxEnd); if ( maxEnd < ue[t] ) { prunning = true; us[t] = maxStart; ue[t] = maxEnd; } } protected void generateMaxEvents() { this.hEvents.clear(); for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { assert (countColors.length >= 1); // at least the neutral color. countColors[r][0] = 1; // avoid the handle the specific case of the neutral color during the sweep. nbDistinctColors[r] = 0; for (int i=1;i<countColors[r].length;i++) { countColors[r][i] = 0; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] = capacities[r]; gapi[r] = capacities[r]; } // [COLORS] } int absoluteT, relativeSucc; for(int t=0;t<nbTasksInFilteringAlgo;t++) { absoluteT = getAbsoluteId(t); if ( t == 0 || ls[t] < minDate ) minDate = ls[t]; if ( t == 0 || ue[t] > maxDate ) maxDate = ue[t]; for (int succ=0;succ<successors[absoluteT].length;succ++) { relativeSucc = getRelativeId(successors[absoluteT][succ]); if ( relativeSucc != -1 ) { nbsuccs[t]++; } } if (nbsuccs[t] == 0) { ring.setNone(t); hEvents.add(le[t]-1,t, Event.SCP,-1); if ( us[t] < le[t] ) { // has a compulsory part hEvents.add(us[t]-1,t, Event.ECP,-1); } if ( predecessors[absoluteT].length != 0 ) { hEvents.add(us[t]-1,t, Event.RS,-1); } if ( ls[t] < us[t] ) { // not scheduled hEvents.add(ue[t]-1,t, Event.PR,-1); } else { ring.setReady(t); } } } } public boolean sweepMax() throws ContradictionException { // ===== LOADS (begin) ===== nextIndexInterestingTP = interestingTimePoints.length-1; // ===== LOADS (end) ===== prunning = false; generateMaxEvents(); addAggregatedProfile(); while (!hEvents.isEmpty()) { processEvents(); filter(); } assert (maxProperty()); return prunning; } private void addTask(int t) throws ContradictionException { if (delta < le[t]-1 ) { contradiction(null, "latest end of task t"+t+" introduce at "+delta+ "(+1) (before its earliest end)"); } adjustMax(t,delta-ld[t]+1,delta+1); int absoluteT = getAbsoluteId(t); ring.setNone(t); if (ue[t] == delta+1) { // ue of task t is reintroduce at delta+1. (=> no PR) if (ls[t] == us[t]) { // task t is fixed (=> no SCP but directly decrease the gap) for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { int ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[t][r]; } // [COLORS] } ring.setReady(t); } else { // add the PR event in the buffer (because it needs to be processed now) /*hEvents.add(ls[t],t,Event.PR,-1);*/ bufferPR[nbItemsBufferPR] = t; nbItemsBufferPR++; hEvents.add(le[t]-1,t, Event.SCP,-1); } if ( us[t] < le[t] ) { // has a compulsory part hEvents.add(us[t]-1,t, Event.ECP,-1); } if ( successors[absoluteT].length != 0) { hEvents.add(us[t]-1,t, Event.RS,-1); } } else { // ls of task t is reintroduce after delta. (=> SCP) hEvents.add(le[t]-1,t, Event.SCP,-1); if ( us[t] < le[t] ) { // has a compulsory part hEvents.add(us[t]-1,t, Event.ECP,-1); } if ( ls[t] < us[t] ) { // not scheduled hEvents.add(ue[t]-1,t, Event.PR,-1); } else { ring.setReady(t); } if ( successors[absoluteT].length != 0) { hEvents.add(us[t]-1,t, Event.RS,-1); } } } private void processEvents() throws ContradictionException { int t, tp, ecpi, rc, absoluteT, relativePred, ctr; int nbExtractedItems = hEvents.pollAllTopItems(); // result in hEvents.bufferPoll int[][] evts = hEvents.bufferPoll; // 0:date,1:task;2:type;3:dec nbItemsBufferPR = 0; nbItemsBufferRS = 0; assert(nbExtractedItems != 0); delta = evts[0][0]; for (int i=0;i<nbExtractedItems;i++) { //System.out.print("Event: "); printEvent(evts[i]); if ( evts[i][2] == Event.SCP ) { t = evts[i][1]; ecpi = us[t]-1; if (ring.inConflict(t)) { // = CONFLICT adjustMax(t,ls[t],le[t]); ring.setReady(t); } else if (ring.inCheck(t)) { ring.setReady(t); } if (delta > us[t]-1) { for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[t][r]; } // [COLORS] } if (ecpi >= delta) { hEvents.add(us[t]-1,t, Event.ECP,-1); } } } else if ( evts[i][2] == Event.ECP ) { t = evts[i][1]; if (ring.inConflict(t)) { // = CONFLICT adjustMax(t,ls[t],le[t]); ring.setReady(t); } if (us[t]-1 < delta) { hEvents.add(us[t]-1,t, Event.ECP,-1); } else { if (ring.inCheck(t)) { ring.setReady(t); } for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 1 ) { nbDistinctColors[r]--; } countColors[r][ctr]--; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] += c[t][r]; } // [COLORS] } } } else if ( evts[i][2] == Event.PR ) { // PR event are processed later bufferPR[nbItemsBufferPR] = evts[i][1]; nbItemsBufferPR++; } else if (evts[i][2] == Event.RS) { bufferRS[nbItemsBufferRS] = evts[i][1]; nbItemsBufferRS++; } else if ( evts[i][2] == Event.AP ) { for(int r=0;r<nbResources;r++) { //gap[k] -= heightsAPEvents[k][evts[i][3]]; // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = heightsAPEvents[r][evts[i][3]]; if ( ctr > 0 ) { // start of fixed task if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } else if ( ctr < 0 ) { ctr = -ctr; if ( countColors[r][ctr] == 1 ) { nbDistinctColors[r]--; } countColors[r][ctr]--; } else { System.out.println(" AGGREGATED EVENT : NEUTRAL COLOR (TBD) "); // TODO do not generate such events. } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= heightsAPEvents[r][evts[i][3]]; } // [COLORS] } } } for (int i=0;i<nbItemsBufferRS;i++) { t = bufferRS[i]; if ( delta == us[t]-1 && !ring.inConflict(t) ) { // (SYNC && !CONFLICT) RS is at its final position absoluteT = getAbsoluteId(t); for (int p=0;p<predecessors[absoluteT].length;p++) { relativePred = getRelativeId(predecessors[absoluteT][p]); if ( relativePred != -1 ) { nbsuccs[relativePred]--; if (nbsuccs[relativePred] == 0) { addTask(relativePred); } } } } else if ( ring.inConflict(t) /*delta == us[t]-1*/ ) { // ( (SYNC || !SYNC) && !CONFLICT) ) hEvents.add(delta - ld[t], t, Event.RS, -1); } else { hEvents.add(us[t]-1, t, Event.RS, -1); } } if (!hEvents.isEmpty()) { date = hEvents.peekDate(); } else { date = minDate; } for (int i=0;i<nbItemsBufferPR;i++) { // PR event are processed now t = bufferPR[i]; if ( ( rc = exceedGap(t)) != -1) { // one of the gap is exceeded ring.setConflict(t,rc); } else if (us[t]-1 < date) { ring.setCheck(t); } else { ring.setReady(t); } } } private void filter() throws ContradictionException { int r,t,next,rc,ecpi; boolean b; for (r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { if ( nbDistinctColors[r] > capacities[r] ) { contradiction(null, "overload (colored) in ["+delta+","+date+") on resource r"+r); } } else { assert (resourceTypes[r] == CUMULATIVE); if (gap[r]<0) { contradiction(null, "overload (cumulative) in ["+delta+","+date+") on resource r"+r); } } // [COLORS] } // ===== LOADS (begin) ===== if (nextIndexInterestingTP >= 0 ) { int iTP = interestingTimePoints[nextIndexInterestingTP]; if ( iTP > date && iTP <= delta) { // the interesting time point is in [delta,date) do { // update the load variables of the interesting time points in (date,delta] for (int idr=0;idr<interestingResources.length;idr++) { r = interestingResources[idr]; if ( resourceTypes[r] == COLORED ) { loads[iTP][r].updateLowerBound(nbDistinctColors[r],aCause); } else { loads[iTP][r].updateLowerBound(capacities[r]-gap[r],aCause); } } nextIndexInterestingTP--; if (nextIndexInterestingTP < 0) break; iTP = interestingTimePoints[nextIndexInterestingTP]; } while ( iTP < date ); } } // ===== LOADS (end) ===== for (r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { if (nbDistinctColors[r] == capacities[r]) { // capacity is reached next = ring.firstInCheck(); while (next != ring.ptrCheck) { t = next; next = ring.next(t); if ( countColors[r][c[t][r]] == 0 ) { if (us[t]-1 < delta) { ring.setConflict(t,r); } else { ring.setReady(t); } } } } } else { // TODO HERE assert (resourceTypes[r] == CUMULATIVE); if (gapi[r] > gap[r]) { // the gap decreases next = ring.firstInCheck(); while (next != ring.ptrCheck) { t = next; next = ring.next(t); if (c[t][r]>gap[r]) { if (us[t]-1 < delta) { ring.setConflict(t,r); } else { ring.setReady(t); } } } gapi[r] = gap[r]; } } // [COLORS] } for (r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { b = nbDistinctColors[r] < capacities[r]; next = ring.firstInConflict(r); while (next != ring.ptrConflict+r) { t = next; next = ring.next(t); if ( b || countColors[r][c[t][r]] != 0 ) { if ((rc=exceedGap(t)) != -1) { // one of the gapi is exceeded. ring.setConflict(t,rc); } else { ecpi = us[t]-1; adjustMax(t,delta-ld[t]+1,delta+1); if (us[t]-1<date) {ring.setCheck(t);} else {ring.setReady(t);} if (le[t]-1<=ecpi && us[t]<le[t]) { hEvents.add(us[t]-1,t, Event.ECP,-1); } } } } } else { assert (resourceTypes[r] == CUMULATIVE); if (gapi[r]<gap[r]) { next = ring.firstInConflict(r); while (next != ring.ptrConflict+r) { t = next; next = ring.next(t); if (c[t][r]<=gap[r]) { if ((rc=exceedGap(t)) != -1) { // one of the gapi is exceeded. ring.setConflict(t,rc); } else { ecpi = us[t]-1; adjustMax(t,delta-ld[t]+1,delta+1); if (us[t]-1<date) {ring.setCheck(t);} else {ring.setReady(t);} if (le[t]-1<=ecpi && us[t]<le[t]) { hEvents.add(us[t]-1,t, Event.ECP,-1); } } } } gapi[r] = gap[r]; } } // [COLORS] } } // It returns -1 is the task does not exceed any gap. // Otherwise, it returns the a resource where it exceeds. private int exceedGap(int t) { for (int r=0;r<nbResources;r++) { if ( resourceTypes[r] == COLORED ) { if ( nbDistinctColors[r] == capacities[r] && countColors[r][c[t][r]] == 0 ) return r; } else { assert (resourceTypes[r] == CUMULATIVE); if ( c[t][r] > gap[r] ) return r; } } return -1; } private boolean maxProperty() { //// System.out.println(" END"); //// for(int i=0;i<n;i++) printTask(i); // int[] sum = new int[nbResources]; // int t, tp, i, r, len, absoluteT, relativePred; // for(t=0;t<nbTasksInFilteringAlgo;t++) { // for each task t ... // absoluteT = getAbsoluteId(t); // for (int pred=0;pred<predecessors[absoluteT].length;pred++) { // (check precedences relations) // relativePred = getRelativeId(predecessors[absoluteT][pred]); // if (relativePred != -1 && us[t]<ue[relativePred]) return false; // } // for(i=us[t];i<ue[t];i++) { // ... scheduled to its latest position // for (r=0;r<nbResources;r++) { // sum[r] = c[t][r]; // } // for(tp=0;tp<nbTasksInFilteringAlgo;tp++) { // compute the // if ((t != tp) && (us[tp] <= i) && (i<le[tp])) { // for (r=0;r<nbResources;r++) { // sum[r] += c[tp][r]; // } // } // } // for (r=0;r<nbResources;r++) { // if (sum[r] > capacities[r]) { // return false; // } // } // } // } // return true; int[] sum = new int[nbResources]; int[][] count = new int[nbResources][]; int[] nbDistinct = new int[nbResources]; int t, tp, i, r, absoluteT, relativePred; for(t=0;t<nbTasksInFilteringAlgo;t++) { // for each task t ... absoluteT = getAbsoluteId(t); assert (absoluteT != -1); for (int pred=0;pred<successors[absoluteT].length;pred++) { // (check precedences relations) relativePred = getRelativeId(successors[absoluteT][pred]); if (relativePred != -1 && us[t]<ue[relativePred]) return false; } for(i=us[t];i<ue[t];i++) { // ... scheduled to its earliest position ... for (r=0;r<nbResources;r++) { if (resourceTypes[r] == CUMULATIVE) { sum[r] = c[t][r]; } else if (resourceTypes[r] == COLORED) { count[r] = new int[nbTasks+1]; // alloc + init to 0 if (c[t][r] != 0) { // // iff this is not the neutral color nbDistinct[r] = 1; count[r][c[t][r]] = 1; } } else { System.out.println(" ! unknown resource type ! "); return false; } } for(tp=0;tp<nbTasksInFilteringAlgo;tp++) { // ... + the aggregated profile (without t) ... if ((t != tp) && (us[tp] <= i) && (i<le[tp])) { for (r=0;r<nbResources;r++) { if (resourceTypes[r] == CUMULATIVE) { sum[r] += c[tp][r]; } else { // COLORED if (c[tp][r] != 0) { if (count[r][c[t][r]] == 0) { nbDistinct[r]++; } count[r][c[t][r]]++; } } } } } for (r=0;r<nbResources;r++) { // ... check that the limit is never exceeded if (resourceTypes[r] == CUMULATIVE) { if (sum[r] > capacities[r]) { return false; } } else { if (nbDistinct[r] > capacities[r]) { return false; } } } } } return true; } public void printEvent(int[] evt) { System.out.print("<date="+evt[0]+",task="+evt[1]+",type="); switch (evt[2]) { case Event.SCP : System.out.print("SCP"); break; case Event.ECP : System.out.print("ECP"); break; case Event.PR : System.out.print("PR"); break; case Event.CCP : System.out.print("CCP"); break; case Event.FSCP : System.out.print("FSCP"); break; case Event.FECP : System.out.print("FECP"); break; case Event.AP : System.out.print("AP"); break; case Event.RS : System.out.print("RS"); break; } System.out.println(",dec="+evt[3]+">"); } public void printTasks() { for (int t=0;t<nbTasksInFilteringAlgo;t++) { printTask(t); } } public void printTask(int i) { System.out.print("Task:"+i+" : s:["+ls[i]+".."+us[i]+"] d:["+ld[i]+".."+ld[i]+"] e:["+le[i]+".."+ue[i]+"] h["+i+"][0]="+c[i][0]+" h["+i+"][1]="+c[i][1]+" "); if (us[i] < le[i]) System.out.println("| scp:"+us[i]+" ecp:"+le[i]); else System.out.println();; } } /** * Greedy Mode */ class DynamicSweepGreedyKDimPrecColRings { private final Rings ringGreedy; private final int[] lsGreedy; // earliest start private final int[] usGreedy; // latest start private final int[] leGreedy; // earliest end private final int[] ueGreedy; // latest end private final int[] nbpreds; private final BitSet isFECPRead; private final IncHeapEvents hEvents; private int delta; private int date; private final BitSet toBeFixed; private final Trail trail; private final int[] bufferPR; private int nbItemsBufferPR; private final int[] nbDistinctColors; // [RESOURCE] # of distinct colours on the given resource private final int[][] countColors; // [RESOURCE][COLOR] give the number of tasks using a given color on a given resource. private final int[] gap; private final int[] gapi; //private int[][] heightsAPEvents; private int maxDate; private int minDate; // h[task][resource] public DynamicSweepGreedyKDimPrecColRings() { this.hEvents = new IncHeapEvents(4*nbTasksInFilteringAlgo+2*(nbTasks-nbTasksInFilteringAlgo)); // TODO check the size this.lsGreedy = ls.clone(); this.usGreedy = us.clone(); this.leGreedy = le.clone(); this.ueGreedy = ue.clone(); //this.hGreedy = h; //this.ldGreedy = ld; this.nbpreds = new int[nbTasksInFilteringAlgo]; this.isFECPRead = new BitSet(nbTasksInFilteringAlgo); this.gap = new int[nbResources]; this.gapi = new int[nbResources]; // [COLORS] this.nbDistinctColors = new int[nbResources]; this.countColors = new int[nbResources][]; for (int r=0;r<nbResources;r++) { if ( resourceTypes[r] == COLORED ) { this.countColors[r] = new int[nbTasks+1]; // neutral color } else { assert (resourceTypes[r] == CUMULATIVE); this.countColors[r] = null; } } // [COLORS] this.toBeFixed = new BitSet(nbTasksInFilteringAlgo); this.trail = new Trail((int) Math.pow(nbTasksInFilteringAlgo,1.5) + 5*nbTasksInFilteringAlgo*nbResources); // TODO find a 'good' value. this.ringGreedy = new Rings(nbResources,nbTasksInFilteringAlgo); this.bufferPR = new int[nbTasksInFilteringAlgo]; this.nbItemsBufferPR = 0; } public void addAggregatedProfile() { for(int i=0;i<nbEventsToAdd;i++) { hEvents.add(datesAPEvents[i], -1, Event.AP, i); if (datesAPEvents[i] < this.minDate) minDate = datesAPEvents[i]; if (datesAPEvents[i] > this.maxDate) maxDate = datesAPEvents[i]; } } public void adjustMin(int t, int minStart, int minEnd) { //System.out.println(" CALL adjust_min(t="+t+", ls="+minStart+")"); assert(minStart < minEnd); lsGreedy[t] = minStart; leGreedy[t] = minEnd; } public void adjustMax(int t, int maxStart, int maxEnd) { //System.out.println(" CALL adjust_max(t="+t+", us="+maxStart+")"); assert(maxStart < maxEnd); usGreedy[t] = maxStart; ueGreedy[t] = maxEnd; } protected void generateGreedyEvents() { this.hEvents.clear(); this.toBeFixed.clear(); // TODO del this.isFECPRead.clear(); // TODO del for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { assert (countColors.length >= 1); // at least the neutral color. countColors[r][0] = 1; // avoid the handle the specific case of the neutral color during the sweep. nbDistinctColors[r] = 0; for (int i=1;i<countColors[r].length;i++) { countColors[r][i] = 0; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] = capacities[r]; gapi[r] = capacities[r]; } // [COLORS] } int absoluteT, relativePred; for(int t=0;t<nbTasksInFilteringAlgo;t++) { absoluteT = getAbsoluteId(t); if ( t == 0 || lsGreedy[t] < minDate ) minDate = lsGreedy[t]; if ( t == 0 || ueGreedy[t] > maxDate ) maxDate = ueGreedy[t]; // the task has at least 1 predecessors. // scan pred, if the current pred was not fixed (copyAndAggregate(...)) then nbpred[t]++; for (int pred=0;pred<predecessors[absoluteT].length;pred++) { relativePred = getRelativeId(predecessors[absoluteT][pred]); if ( relativePred != -1 ) { nbpreds[t]++; } } if ( nbpreds[t] == 0 ) { // iff no predecessor, t is added in the process ringGreedy.setNone(t); if (lsGreedy[t] == usGreedy[t] && ld[t] > 0) { hEvents.add(usGreedy[t],t, Event.FSCP,-1); hEvents.add(leGreedy[t],t, Event.FECP,-1); } else if ( ld[t] > 0 ) { toBeFixed.set(t,true); hEvents.add(lsGreedy[t],t, Event.PR,-1); hEvents.add(usGreedy[t],t, Event.SCP,-1); if ( usGreedy[t] < leGreedy[t] ) { hEvents.add(leGreedy[t],t, Event.ECP,-1); } } else { // zero duration -- can fix right now adjustMax(t,lsGreedy[t],leGreedy[t]); } } } } public boolean greedy() { generateGreedyEvents(); addAggregatedProfile(); this.delta = minDate; trail.push(Trail.DELTA,minDate-1); while ( !hEvents.isEmpty() || !ringGreedy.isEmptyReady() ) { if ( !ringGreedy.isEmptyReady() ) { greedyAssign(); } else { if (!greedyPhase1()) { return false; } } if ( !greedyPhase2() ) { return false; } trail.push(Trail.DELTA,delta); } // assert (greedyProperty()); return true; } // ? private boolean greedyProperty() { // TODO new property due to colors if ( !toBeFixed.isEmpty() ) { return false; } int[] sum = new int[nbResources]; for (int t=0;t<nbTasksInFilteringAlgo;t++) { if ( lsGreedy[t] < leGreedy[t] ) { for (int r=0;r<nbResources;r++) { sum[r] = c[t][r]; } for (int tp=0;tp<nbTasksInFilteringAlgo;tp++) { if ( tp != t && usGreedy[tp] <= lsGreedy[t] && lsGreedy[t] < leGreedy[tp]) { for (int r=0;r<nbResources;r++) { sum[r] += c[tp][r]; } } } for (int r=0;r<nbResources;r++) { if ( sum[r] > capacities[r] ) { return false; } } } } return true; } // OK private boolean greedyPhase1() { //System.out.println(" CALL greedy_phase1()"); int t, ecpi, rc, ctr; int nbExtractedItems = hEvents.pollAllTopItems(); // result in hEvents.bufferPoll int[][] evts = hEvents.bufferPoll; // 0:date,1:task;2:type;3:dec nbItemsBufferPR = 0; assert(nbExtractedItems != 0); delta = evts[0][0]; for (int i=0;i<nbExtractedItems;i++) { //printEvent(evts[i]); if ( evts[i][2] == Event.FSCP ) { t = evts[i][1]; trail.push(Trail.FSCP,t); for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[t][r]; } // [COLORS] } } else if ( evts[i][2] == Event.FECP ) { t = evts[i][1]; trail.push(Trail.FECP,t); for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 1 ) { nbDistinctColors[r]--; } countColors[r][ctr]--; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] += c[t][r]; } // [COLORS] } // ------------------------- // MODIF FOR PRECEDENCES int relativeSucc, absoluteT; absoluteT = getAbsoluteId(t); if ( isFECPRead.get(t) == false ) { // never read before isFECPRead.set(t,true); for (int succ=0;succ<successors[absoluteT].length;succ++) { relativeSucc = getRelativeId(successors[absoluteT][succ]); if ( relativeSucc != -1 ) { nbpreds[relativeSucc]--; assert (nbpreds[relativeSucc] >= 0); if ( lsGreedy[relativeSucc] < delta ) { // update the lower bounds. if ( delta > usGreedy[relativeSucc] ) { return false; } lsGreedy[relativeSucc] = delta; leGreedy[relativeSucc] = delta+ld[relativeSucc]; } if ( nbpreds[relativeSucc] == 0 ) { // we can now add the task in the process. ringGreedy.setNone(relativeSucc); if ( lsGreedy[relativeSucc] == delta ) { // the PR can be added at the current sweep-line position if ( lsGreedy[relativeSucc] == usGreedy[relativeSucc] ) { // task relativeSucc is fixed (=> no SCP but directly decrease the gap) toBeFixed.set(relativeSucc,false); trail.push(Trail.FSCP,relativeSucc); for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[relativeSucc][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[relativeSucc][r]; } // [COLORS] } hEvents.add(leGreedy[relativeSucc],relativeSucc, Event.FECP,-1); } else { // task is not yet fixed toBeFixed.set(relativeSucc,true); bufferPR[nbItemsBufferPR] = relativeSucc; nbItemsBufferPR++; hEvents.add(usGreedy[relativeSucc],relativeSucc, Event.SCP,-1); if ( usGreedy[relativeSucc] < leGreedy[relativeSucc] ) { // has a compulsory part hEvents.add(leGreedy[relativeSucc],relativeSucc, Event.ECP,-1); } } } else { // ls of task t is reintroduce after delta. (=> SCP) assert ( lsGreedy[relativeSucc]>delta ); if ( lsGreedy[relativeSucc] == usGreedy[relativeSucc] ) { // task relativeSucc is fixed (=> no SCP but directly decrease the gap) toBeFixed.set(relativeSucc,false); hEvents.add(lsGreedy[relativeSucc],relativeSucc, Event.FSCP,-1); hEvents.add(leGreedy[relativeSucc],relativeSucc, Event.FECP,-1); } else { // task is not yet fixed toBeFixed.set(relativeSucc,true); hEvents.add(lsGreedy[relativeSucc],relativeSucc, Event.PR,-1); hEvents.add(usGreedy[relativeSucc],relativeSucc, Event.SCP,-1); if ( usGreedy[relativeSucc] < leGreedy[relativeSucc] ) { // has a compulsory part hEvents.add(leGreedy[relativeSucc],relativeSucc, Event.ECP,-1); } } } } } } } // END MODIF FOR PRECEDENCES // ------------------------- } else if ( evts[i][2] == Event.SCP && toBeFixed.get(evts[i][1]) == true ) { t = evts[i][1]; ecpi = leGreedy[t]; if (ringGreedy.inConflict(t)) { // = CONFLICT adjustMin(t,usGreedy[t],ueGreedy[t]); if ( greedyFix(t) ) { continue; } } else if (ringGreedy.inCheck(t)) { greedyFix(t); } trail.push(Trail.SCP,t); if (delta < leGreedy[t]) { for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[t][r]; } // [COLORS] } if (ecpi <= delta) { hEvents.add(leGreedy[t],t, Event.ECP,-1); } } } else if ( evts[i][2] == Event.ECP && toBeFixed.get(evts[i][1]) == true ) { t = evts[i][1]; if (ringGreedy.inConflict(t)) { // = CONFLICT adjustMin(t,usGreedy[t],ueGreedy[t]); if ( greedyFix(t) ) { continue; } // BUG HERE } if (leGreedy[t] > delta) { hEvents.add(leGreedy[t],t, Event.ECP,-1); } else { trail.push(Trail.ECP,t); if (ringGreedy.inCheck(t)) { greedyFix(t); } for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 1 ) { nbDistinctColors[r]--; } countColors[r][ctr]--; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] += c[t][r]; } // [COLORS] } } } else if ( evts[i][2] == Event.PR && toBeFixed.get(evts[i][1]) == true ) { // PR event are processed later bufferPR[nbItemsBufferPR] = evts[i][1]; nbItemsBufferPR++; } else if ( evts[i][2] == Event.AP ) { for(int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = heightsAPEvents[r][evts[i][3]]; if ( ctr < 0 ) { // start of fixed task ctr = -ctr; if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } else if ( ctr > 0 ) { if ( countColors[r][ctr] == 1 ) { nbDistinctColors[r]--; } countColors[r][ctr]--; } else { System.out.println(" AGGREGATED EVENT WITH A NEUTRAL COLOR "); // TODO do not generate such events. } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] += heightsAPEvents[r][evts[i][3]]; } // [COLORS] } } } if (!hEvents.isEmpty()) { date = hEvents.peekDate(); } else { date = maxDate; } for (int i=0;i<nbItemsBufferPR;i++) { t = bufferPR[i]; if ( exceedGap(t) == -1 && leGreedy[t] <= date ) { greedyFix(t); } } for (int i=0;i<nbItemsBufferPR;i++) { // PR event are processed now t = bufferPR[i]; if ( toBeFixed.get(t) ) { trail.push(Trail.PR,t); if ( (rc = exceedGap(t)) != -1) { // one of the gap is exceeded ringGreedy.setConflict(t,rc); } else { ringGreedy.setCheck(t); } } } return true; //System.out.println(" END greedy_phase1(): delta="+delta+" delta'="+date); } // OK private boolean greedyFix(int t) { //System.out.println(" CALL greedy_fix(t="+t+",delta="+delta+")"); int ctr; if ( lsGreedy[t] < delta ) { //System.out.println(" -> SET READY t="+t+" ls="+ls[t]); ringGreedy.setReady(t); return false; //System.out.println(" END greedy_fix "); } else { toBeFixed.set(t,false); ringGreedy.setNone(t); adjustMax(t,lsGreedy[t],leGreedy[t]); trail.push(Trail.FSCP,t); hEvents.add(leGreedy[t],t, Event.FECP,-1); for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[t][r]; } // [COLORS] } //System.out.println(" END greedy_fix "); return true; } } // OK private boolean greedyPhase2() { int r,t,next,rc,ecpi; boolean fixed = false; for (r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { if ( nbDistinctColors[r] > capacities[r] ) { return false; } } else { assert (resourceTypes[r] == CUMULATIVE); if (gap[r]<0) { return false; } } // [COLORS] } for (r=0;r<nbResources;r++) { next = ringGreedy.firstInConflict(r); while (next != ringGreedy.ptrConflict+r) { t = next; next = ringGreedy.next(t); // [COLORS] if ((resourceTypes[r] == CUMULATIVE && gapi[r] < gap[r] && c[t][r] <= gap[r] && delta+ld[t] <= date) || // TODO sortir le test sur le gap (ne depend pas de la tache) (resourceTypes[r] == COLORED && (nbDistinctColors[r] < capacities[r] || countColors[r][c[t][r]] != 0) && delta+ld[t] <= date)) { // [COLORS] if ( ( rc = exceedGap(t)) != -1) { // one of the gap is exceeded ringGreedy.setConflict(t,rc); } else { adjustMin(t,delta,delta+ld[t]); fixed |= greedyFix(t); } } } } for (r=0;r<nbResources;r++) { // [COLORS] if (resourceTypes[r] == COLORED) { next = ringGreedy.firstInConflict(r); while (next != ringGreedy.ptrConflict+r) { t = next; next = ringGreedy.next(t); if ( nbDistinctColors[r] < capacities[r] || countColors[r][c[t][r]] != 0 ) { if ((rc=exceedGap(t)) != -1) { // one of the gapi is exceeded. ringGreedy.setConflict(t,rc); } else { trail.push(Trail.PR,t); ecpi = leGreedy[t]; adjustMin(t,delta,delta+ld[t]); ringGreedy.setCheck(t); if (usGreedy[t]>=ecpi && usGreedy[t]<leGreedy[t]) { hEvents.add(leGreedy[t],t, Event.ECP,-1); } } } } } else { assert (resourceTypes[r] == CUMULATIVE); if (gapi[r]<gap[r]) { // the gap increases next = ringGreedy.firstInConflict(r); while (next != ringGreedy.ptrConflict+r) { t = next; next = ringGreedy.next(t); if (c[t][r]<=gap[r]) { if ((rc=exceedGap(t)) != -1) { // one of the gapi is exceeded. ringGreedy.setConflict(t,rc); } else { trail.push(Trail.PR,t); ecpi = leGreedy[t]; adjustMin(t,delta,delta+ld[t]); ringGreedy.setCheck(t); if (usGreedy[t]>=ecpi && usGreedy[t]<leGreedy[t]) { hEvents.add(leGreedy[t],t, Event.ECP,-1); } } } } gapi[r] = gap[r]; } } // [COLORS] } for (r=0;r<nbResources;r++) { // [COLORS] if (resourceTypes[r] == COLORED) { next = ringGreedy.firstInCheck(); while (next != ringGreedy.ptrCheck) { t = next; next = ringGreedy.next(t); if ( nbDistinctColors[r] == capacities[r] && countColors[r][c[t][r]] == 0 ) { if (leGreedy[t] > delta) { ringGreedy.setConflict(t,r); } else { greedyFix(t); } } } } else { if (gapi[r] > gap[r] || fixed) { // the gap decreases next = ringGreedy.firstInCheck(); while (next != ringGreedy.ptrCheck) { t = next; next = ringGreedy.next(t); if ( c[t][r]>gap[r] ) { if (leGreedy[t] > delta) { ringGreedy.setConflict(t,r); } else { greedyFix(t); } } } gapi[r] = gap[r]; } } // [COLORS] } return true; } // OK private void greedyAssign() { int t = -1, next, tag, item[]; int tp = -1, ctr; next = ringGreedy.firstInReady(); while (next != ringGreedy.ptrReady) { t = next; next = ringGreedy.next(t); if ( tp == -1 || lsGreedy[t] < lsGreedy[tp] ) { tp = t; } } ringGreedy.setNone(tp); while ( delta >= lsGreedy[tp] ) { item = trail.pop(); // 0:tag, 1:data tag = item[0]; t = item[1]; if ( tag == Trail.FSCP ) { assert ( t >= 0 && t < nbTasksInFilteringAlgo); hEvents.add(delta,t, Event.FSCP,-1); for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 1 ) { nbDistinctColors[r]--; } countColors[r][ctr]--; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] += c[t][r]; } // [COLORS] } } else if ( tag == Trail.FECP ) { hEvents.add(delta,t, Event.FECP,-1); for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[t][r]; } // [COLORS] } } else if ( tag == Trail.SCP && toBeFixed.get(t) ) { hEvents.add(delta,t, Event.SCP,-1); if ( delta < leGreedy[t] ) { for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 1 ) { nbDistinctColors[r]--; } countColors[r][ctr]--; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] += c[t][r]; } // [COLORS] } } } else if ( tag == Trail.ECP && toBeFixed.get(t) ) { hEvents.add(delta,t, Event.ECP,-1); for (int r=0;r<nbResources;r++) { // [COLORS] if ( resourceTypes[r] == COLORED ) { ctr = c[t][r]; if ( ctr != 0 ) { // is not the neutral color if ( countColors[r][ctr] == 0 ) { nbDistinctColors[r]++; } countColors[r][ctr]++; } } else { assert (resourceTypes[r] == CUMULATIVE); gap[r] -= c[t][r]; } // [COLORS] } } else if ( tag == Trail.PR && toBeFixed.get(t) && !ringGreedy.inNone(t) ) { hEvents.add(lsGreedy[t],t, Event.PR,-1); ringGreedy.setNone(t); } else if ( tag == Trail.DELTA && delta > t ) { // here 't' is a timepoint (not a task) date = delta; delta = t; } } toBeFixed.set(tp,false); adjustMax(tp,lsGreedy[tp],leGreedy[tp]); hEvents.add(lsGreedy[tp],tp, Event.FSCP,-1); hEvents.add(leGreedy[tp],tp, Event.FECP,-1); assert ( ringGreedy.isEmptyReady() ); } // It returns -1 if the task does not exceed any gap. // Otherwise, it returns the a resource where it exceeds. private int exceedGap(int t) { for (int r=0;r<nbResources;r++) { if ( resourceTypes[r] == COLORED ) { if ( nbDistinctColors[r] == capacities[r] && countColors[r][c[t][r]] == 0 ) return r; } else { assert (resourceTypes[r] == CUMULATIVE); if ( c[t][r] > gap[r] ) return r; } } return -1; } public int ls(int t) { return lsGreedy[t]; } public int us(int t) { return usGreedy[t]; } public int le(int t) { return leGreedy[t]; } public int ue(int t) { return ueGreedy[t]; } public int ld(int t) { return ld[t]; } public boolean allTasksAreFixed() { for (int t=0;t<nbTasksInFilteringAlgo;t++) { if ( lsGreedy[t] != usGreedy[t] ) return false; } return true; } // ==================================================== // ==================================================== public void printEvent(int[] evt) { System.out.print("\t<date="+evt[0]+",task="+evt[1]+",type="); switch (evt[2]) { case Event.SCP : System.out.print("SCP"); break; case Event.ECP : System.out.print("ECP"); break; case Event.PR : System.out.print("PR"); break; case Event.CCP : System.out.print("CCP"); break; case Event.FSCP : System.out.print("FSCP"); break; case Event.FECP : System.out.print("FECP"); break; case Event.AP : System.out.print("AP"); break; } System.out.println(",dec="+evt[3]+">"); } public void printTasks() { for (int t=0;t<nbTasksInFilteringAlgo;t++) { printTask(t); } } public void printTask(int i) { System.out.print("Task:"+i+" : s:["+lsGreedy[i]+".."+usGreedy[i]+"] d:["+ld[i]+".."+ld[i]+"] e:["+leGreedy[i]+".."+ueGreedy[i]+"]"); if (usGreedy[i] < leGreedy[i]) System.out.println("| scp:"+usGreedy[i]+" ecp:"+leGreedy[i]); else System.out.println();; } } class Trail { private int size; private int current; private int[] tag; private int[] data; private final int[] buffer; public final static int FSCP = 1; public final static int FECP = 2; public final static int SCP = 3; public final static int ECP = 4; public final static int PR = 5; public final static int DELTA = 6; public Trail(int _size) { this.size = _size; this.current = 0; this.tag = new int[size]; this.data = new int[size]; this.buffer = new int[2]; } public void push(int _tag, int _data) { //System.out.println(" <- PUSH "+this); if ( current >= size ) { increaseSize(); } assert (current < size); assert (tag.length == size); tag[current] = _tag; data[current] = _data; current++; } public int[] pop() { //System.out.println(" -> POP "+this); current--; buffer[0] = tag[current]; buffer[1] = data[current]; return buffer; } public void increaseSize() { //System.out.print(" -> INCREASE TRAIL SIZE "); //long start, duration, end; // start = System.currentTimeMillis(); size = (int)(size * 1.5); int[] _tag = new int[size]; int[] _data = new int[size]; for (int i=0;i<current;i++) { _tag[i] = tag[i]; _data[i] = data[i]; } tag = _tag; data = _data; // end = System.currentTimeMillis(); // duration = end - start; //System.out.println("IN "+duration+" ms"); } public String toString() { StringBuilder sb = new StringBuilder(); for (int i=0;i<current;i++) { sb.append("<"); switch (tag[i]) { case 1: sb.append("FSCP"); break; case 2: sb.append("FECP"); break; case 3: sb.append("SCP"); break; case 4: sb.append("ECP"); break; case 5: sb.append("PR"); break; case 6: sb.append("DELTA"); break; } sb.append(","+data[i]+">,"); } return sb.toString(); } } }