/*
* Copyright (c) Fabien Hermenier
*
* This file is part of Entropy.
*
* Entropy is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Entropy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Entropy. If not, see <http://www.gnu.org/licenses/>.
*/
package entropy.plan.choco.constraint.sliceScheduling;
import gnu.trove.TIntIntHashMap;
import java.util.Arrays;
import java.util.BitSet;
import choco.kernel.common.logging.ChocoLogging;
import choco.kernel.memory.IStateBitSet;
import choco.kernel.solver.ContradictionException;
import choco.kernel.solver.variables.integer.IntDomainVar;
/**
* @author Fabien Hermenier
*/
public class LocalScheduler {
private int me;
/**
* My CPU capacity.
*/
private int capacityCPU;
/**
* My memory capacity.
*/
private int capacityMem;
/**
* out[i] = true <=> the consuming slice i will leave me.
*/
private BitSet out;
/**
* The moment the consuming slices ends. Same order as the hosting variables.
*/
private IntDomainVar[] cEnds;
/**
* The CPU height for each consuming slice. Same order as the hosting variables.
*/
private int[] cCPUHeights;
/**
* The Memory height for each consuming slice. Same order as the hosting variables.
*/
private int[] cMemHeights;
//Demanding slice part
/**
* in[i] = true <=> the demanding slice i will come to me.
*/
private IStateBitSet in;
/*
* The moment the demanding slices ends. Same order as the hosting variables.
*/
private IntDomainVar[] dStarts;
/**
* The CPU height for each demanding slice. Same order as the hosting variable.
*/
private int[] dCPUHeights;
/**
* The memory heighr for each demanding slice. Same order as the hosting variable.
*/
private int[] dMemHeights;
/**
* The amount of free memory at startup.
*/
private int startupFreeMem;
/**
* The amount of free CPU at startup.
*/
private int startupFreeCPU;
private static final int DEBUG = -1;
private int[] associations;
private int[] revAssociations;
public static final int NO_ASSOCIATIONS = -1;
private TIntIntHashMap profileMinCPU = new TIntIntHashMap();
private TIntIntHashMap profileMinMem = new TIntIntHashMap();
private int[] sortedMinProfile;
private TIntIntHashMap profileMaxCPU = new TIntIntHashMap();
private TIntIntHashMap profileMaxMem = new TIntIntHashMap();
private int[] sortedMaxProfile;
public LocalScheduler(int me,
int capacityCPU,
int capacityMem,
int[] cCPUHeights,
int[] cMemHeights,
IntDomainVar[] cEnds,
BitSet outs,
int[] dCPUHeights,
int[] dMemHeights,
IntDomainVar[] dStarts,
IStateBitSet in,
int[] assocs) {
this.associations = assocs;
this.me = me;
this.capacityCPU = capacityCPU;
this.capacityMem = capacityMem;
this.cEnds = cEnds;
this.cCPUHeights = cCPUHeights;
this.cMemHeights = cMemHeights;
this.dStarts = dStarts;
this.dCPUHeights = dCPUHeights;
this.dMemHeights = dMemHeights;
this.in = in;
this.out = outs;
revAssociations = new int[cCPUHeights.length];
for (int i = 0; i < revAssociations.length; i++) {
revAssociations[i] = NO_ASSOCIATIONS;
}
for (int i = 0; i < associations.length; i++) {
if (associations[i] != NO_ASSOCIATIONS) {
revAssociations[associations[i]] = i;
}
}
//The amount of free resources at startup
startupFreeMem = capacityMem;
startupFreeCPU = capacityCPU;
for (int j = out.nextSetBit(0); j >= 0; j = out.nextSetBit(j + 1)) {
startupFreeCPU -= cCPUHeights[j];
startupFreeMem -= cMemHeights[j];
}
}
public boolean propagate() throws ContradictionException {
computeProfiles();
if (!checkInvariant()) {
return false;
}
updateDStartsSup();
updateCEndsSup();
updateDStartsInf();
return true;
}
/**
* Translation for a relatives resources changes to an absolute free resources.
*
* @param changes the map that indicates the free CPU variation
* @param sortedMoments the different moments sorted in ascending order
*/
private void toAbsoluteFreeResources(TIntIntHashMap changes, int[] sortedMoments) {
for (int i = 1; i < sortedMoments.length; i++) {
int t = sortedMoments[i];
int lastT = sortedMoments[i - 1];
int lastFree = changes.get(lastT);
changes.put(t, changes.get(t) + lastFree);
}
}
private void computeProfiles() {
//Sur de ce qui est utilise sur la ressource
profileMinCPU.clear();
profileMinMem.clear();
//Maximum simultanee dans le pire des cas sur la ressource
profileMaxCPU.clear();
profileMaxMem.clear();
profileMinCPU.put(0, capacityCPU - startupFreeCPU);
profileMaxCPU.put(0, capacityCPU - startupFreeCPU);
profileMinMem.put(0, capacityMem - startupFreeMem);
profileMaxMem.put(0, capacityMem - startupFreeMem);
for (int i = out.nextSetBit(0); i >= 0; i = out.nextSetBit(i + 1)) {
int t = cEnds[i].getInf();
if (associatedToDSliceOnCurrentNode(i) &&
dCPUHeights[revAssociations[i]] > cCPUHeights[i]) {
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().finest(me + " " + cEnds[i].pretty() + " increasing");
}
profileMaxCPU.put(t, profileMaxCPU.get(t) - cCPUHeights[i]);
profileMaxMem.put(t, profileMaxMem.get(t) - cMemHeights[i]);
} else {
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().finest(me + " " + cEnds[i].pretty() + " decreasing or non-associated (" + (revAssociations[i] >= 0 ? dStarts[revAssociations[i]].pretty() : "no rev") + "?)");
}
profileMinCPU.put(t, profileMinCPU.get(t) - cCPUHeights[i]);
profileMinMem.put(t, profileMinMem.get(t) - cMemHeights[i]);
}
t = cEnds[i].getSup();
if (associatedToDSliceOnCurrentNode(i) &&
dCPUHeights[revAssociations[i]] > cCPUHeights[i]) {
profileMinCPU.put(t, profileMinCPU.get(t) - cCPUHeights[i]);
profileMinMem.put(t, profileMinMem.get(t) - cMemHeights[i]);
} else {
profileMaxCPU.put(t, profileMaxCPU.get(t) - cCPUHeights[i]);
profileMaxMem.put(t, profileMaxMem.get(t) - cMemHeights[i]);
}
}
for (int i = in.nextSetBit(0); i >= 0; i = in.nextSetBit(i + 1)) {
int t = dStarts[i].getSup();
profileMinCPU.put(t, profileMinCPU.get(t) + dCPUHeights[i]);
profileMinMem.put(t, profileMinMem.get(t) + dMemHeights[i]);
t = dStarts[i].getInf();
profileMaxCPU.put(t, profileMaxCPU.get(t) + dCPUHeights[i]);
profileMaxMem.put(t, profileMaxMem.get(t) + dMemHeights[i]);
}
//Now transforms into an absolute profile
sortedMinProfile = null;
sortedMinProfile = profileMinCPU.keys();
Arrays.sort(sortedMinProfile);
sortedMaxProfile = null;
sortedMaxProfile = profileMaxCPU.keys();
profileMaxCPU.keys(sortedMaxProfile);
Arrays.sort(sortedMaxProfile);
toAbsoluteFreeResources(profileMinCPU, sortedMinProfile);
toAbsoluteFreeResources(profileMinMem, sortedMinProfile);
toAbsoluteFreeResources(profileMaxCPU, sortedMaxProfile);
toAbsoluteFreeResources(profileMaxMem, sortedMaxProfile);
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().finest("---" + me + "--- startup=(" + startupFreeCPU + "; " + startupFreeMem + ") init=(" + capacityCPU + "; " + capacityMem + ")");
for (int i = in.nextSetBit(0); i >= 0; i = in.nextSetBit(i + 1)) {
ChocoLogging.getBranchingLogger().finest((dStarts[i].isInstantiated() ? "!" : "?") + " " + dStarts[i].pretty() + " " + dCPUHeights[i] + " " + dMemHeights[i]);
}
for (int i = out.nextSetBit(0); i >= 0; i = out.nextSetBit(i + 1)) {
ChocoLogging.getBranchingLogger().finest((cEnds[i].isInstantiated() ? "!" : "?") + " " + cEnds[i].pretty() + " " + cCPUHeights[i] + " " + cMemHeights[i]);
}
ChocoLogging.getBranchingLogger().finest("---");
ChocoLogging.getBranchingLogger().finest("profileMin=" + prettyProfile(sortedMinProfile, profileMinCPU, profileMinMem));
ChocoLogging.getBranchingLogger().finest("profileMax=" + prettyProfile(sortedMaxProfile, profileMaxCPU, profileMaxMem));
}
}
private boolean associatedToDSliceOnCurrentNode(int cSlice) {
if (revAssociations[cSlice] != NO_ASSOCIATIONS
&& in.get(revAssociations[cSlice])) {
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().finest(me + " " + cEnds[cSlice].getName() + " with " + dStarts[revAssociations[cSlice]]);
}
return true;
}
return false;
}
private boolean associatedToCSliceOnCurrentNode(int dSlice) {
if (associations[dSlice] != NO_ASSOCIATIONS
&& out.get(associations[dSlice])) {
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().finest(me + " " + dStarts[dSlice].getName() + " with " + cEnds[associations[dSlice]]);
}
return true;
}
return false;
}
private String prettyProfile(int[] ascMoments, TIntIntHashMap cpuProfile, TIntIntHashMap memProfile) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < ascMoments.length; i++) {
int t = ascMoments[i];
b.append(t);
b.append(":(");
b.append(cpuProfile.get(t));
b.append(",");
b.append(memProfile.get(t));
b.append(")");
if (i != ascMoments.length - 1) {
b.append(" ");
}
}
return b.toString();
}
private boolean checkInvariant() throws ContradictionException {
for (int i = 0; i < sortedMinProfile.length; i++) {
int t = sortedMinProfile[i];
if (profileMinCPU.get(t) > capacityCPU || profileMinMem.get(t) > capacityMem) {
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().warning(me + ": Invalid profile at moment " + t + " - " + prettyProfile(sortedMinProfile, profileMinCPU, profileMinMem));
}
return false;
}
}
return true;
}
private void updateDStartsInf() throws ContradictionException {
for (int i = in.nextSetBit(0); i >= 0; i = in.nextSetBit(i + 1)) {
if (!dStarts[i].isInstantiated() && !associatedToCSliceOnCurrentNode(i)) {
int dCpu = dCPUHeights[i];
int dMem = dMemHeights[i];
int lastT = -1;
for (int x = sortedMinProfile.length - 1; x >= 0; x--) {
int t = sortedMinProfile[x];
if (t <= dStarts[i].getInf()) {
break;
}
int prevT = sortedMinProfile[x - 1];
if (t <= dStarts[i].getSup()
&& (profileMinCPU.get(prevT) + dCpu > capacityCPU || profileMinMem.get(prevT) + dMem > capacityMem)) {
lastT = t;
break;
}
}
if (lastT != -1) {
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().finest(me + ": " + dStarts[i].pretty() + " lb =" + lastT);
}
dStarts[i].setInf(lastT);
}
}
}
}
private void updateDStartsSup() throws ContradictionException {
int lastSup = -1;
for (int i = sortedMaxProfile.length - 1; i >= 0; i--) {
int t = sortedMaxProfile[i];
if (profileMaxCPU.get(t) <= capacityCPU && profileMaxMem.get(t) <= capacityMem) {
lastSup = t;
} else {
break;
}
}
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().finest(me + ": lastSup=" + lastSup);
}
if (lastSup != -1) {
for (int i = in.nextSetBit(0); i >= 0; i = in.nextSetBit(i + 1)) {
if (!dStarts[i].isInstantiated() && !associatedToCSliceOnCurrentNode(i) && dStarts[i].getSup() > lastSup) {
int s = Math.max(dStarts[i].getInf(), lastSup);
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().finest(me + ": " + dStarts[i].pretty() + " ub=" + s + ");");
}
dStarts[i].setSup(s);
}
}
}
}
private void updateCEndsSup() throws ContradictionException {
for (int i = out.nextSetBit(0); i >= 0; i = out.nextSetBit(i + 1)) {
if (!cEnds[i].isInstantiated() && !associatedToDSliceOnCurrentNode(i)) {
int cCpu = cCPUHeights[i];
int cMem = cMemHeights[i];
int lastT = -1;
for (int x = 0; x < sortedMinProfile.length; x++) {
int t = sortedMinProfile[x];
if (t >= cEnds[i].getSup()) {
break;
} else if (t >= cEnds[i].getInf() && (profileMinCPU.get(t) + cCpu > capacityCPU
|| profileMinMem.get(t) + cMem > capacityMem)) {
lastT = t;
break;
}
}
if (lastT != -1) {
if (me == DEBUG) {
ChocoLogging.getBranchingLogger().finest(me + ": " + cEnds[i].pretty() + " cEndsSup =" + lastT);
}
cEnds[i].setSup(lastT);
}
}
}
}
}