/*
* 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.cp.solver.variables.integer.IntVarEvent;
import choco.kernel.common.logging.ChocoLogging;
import choco.kernel.common.util.tools.ArrayUtils;
import choco.kernel.memory.IEnvironment;
import choco.kernel.memory.IStateBitSet;
import choco.kernel.memory.IStateInt;
import choco.kernel.memory.structure.SBitSet;
import choco.kernel.solver.ContradictionException;
import choco.kernel.solver.constraints.integer.AbstractLargeIntSConstraint;
import choco.kernel.solver.variables.integer.IntDomainVar;
/**
* @author Fabien Hermenier
*/
public class SlicesScheduler extends AbstractLargeIntSConstraint {
private LocalScheduler[] scheds;
private IntDomainVar[] cHosters;
private IntDomainVar[] cEnds;
private IntDomainVar[] dHosters;
private IntDomainVar[] dStarts;
private int[] capacityCPU;
private int[] capacityMem;
private int[] dCPUHeights;
private int[] dMemHeights;
private int[] cCPUHeights;
private int[] cMemHeights;
private IStateInt toInstantiate;
private IEnvironment env;
private IStateBitSet[] ins;
public SlicesScheduler(IEnvironment env,
int[] capacityCPU,
int[] capacityMem,
IntDomainVar[] cHosters,
int[] cCPUHeights,
int[] cMemHeights,
IntDomainVar[] cEnds,
IntDomainVar[] dHosters,
int[] dCPUHeights,
int[] dMemHeights,
IntDomainVar[] dStarts,
int[] assocs) {
super(ArrayUtils.append(dHosters, cHosters, cEnds, dStarts));
this.env = env;
this.cHosters = cHosters;
this.dHosters = dHosters;
this.cEnds = cEnds;
this.dStarts = dStarts;
this.capacityCPU = capacityCPU;
this.capacityMem = capacityMem;
this.dCPUHeights = dCPUHeights;
this.dMemHeights = dMemHeights;
this.cCPUHeights = cCPUHeights;
this.cMemHeights = cMemHeights;
scheds = new LocalScheduler[capacityCPU.length];
BitSet[] outs = new BitSet[scheds.length];
for (int i = 0; i < scheds.length; i++) {
outs[i] = new BitSet(cHosters.length);
//scheds[i].setOuts(outs[i]);
}
for (int i = 0; i < cHosters.length; i++) {
outs[cHosters[i].getVal()].set(i);
}
this.ins = new IStateBitSet[scheds.length];
for (int i = 0; i < scheds.length; i++) {
ins[i] = new SBitSet(env, dHosters.length);
scheds[i] = new LocalScheduler(i, capacityCPU[i], capacityMem[i],
cCPUHeights,
cMemHeights,
cEnds,
outs[i],
dCPUHeights,
dMemHeights,
dStarts,
ins[i],
assocs);
}
}
@Override
public void awake() throws ContradictionException {
this.toInstantiate = env.makeInt(dHosters.length);
//Check whether some hosting variable are already instantiated
for (int i = 0; i < dHosters.length; i++) {
if (dHosters[i].isInstantiated()) {
int nIdx = dHosters[i].getVal();
toInstantiate.add(-1);
ins[nIdx].set(i);
}
}
}
@Override
public void propagate() throws ContradictionException {
if (isFull2()) {
for (int i = 0; i < scheds.length; i++) {
if (!scheds[i].propagate()) {
fail();
}
}
}
}
@Override
public void awakeOnInst(int idx) throws ContradictionException {
if (idx < dHosters.length) {
toInstantiate.add(-1);
int nIdx = vars[idx].getVal();
//ChocoLogging.getSearchLogger().finest(idx + " on " + nIdx);
ins[nIdx].set(idx);
//ChocoLogging.getSearchLogger().finest(vars[idx].pretty());
for (int x = 0; x < scheds.length; x++) {
if (x != nIdx) {
//ChocoLogging.getSearchLogger().finest(x + " not on " + idx);
ins[x].clear(idx);
} else {
//ChocoLogging.getSearchLogger().finest(x + " on " + idx);
ins[x].set(idx);
}
}
}
this.constAwake(false);
}
private boolean isFull2() {
return toInstantiate.get() == 0;
}
@Override
public int getFilteredEventMask(int idx) {
return IntVarEvent.INSTINT_MASK;
}
@Override
public boolean isSatisfied() {
int[] vals = new int[vars.length];
for (int i = 0; i < vals.length; i++) {
vals[i] = vars[i].getVal();
}
return isSatisfied(vals);
}
@Override
public boolean isSatisfied(int[] vals) {
//Split this use tab to ease the analysis
int[] dHostersVals = new int[dHosters.length];
int[] dStartsVals = new int[dStarts.length];
int[] cHostersVals = new int[cHosters.length];
int[] cEndsVals = new int[cEnds.length];
//dHosters, cHosters, cEnds, dStarts
for (int i = 0; i < dHosters.length; i++) {
dHostersVals[i] = vals[i];
dStartsVals[i] = vals[i + dHosters.length + cHosters.length + cEnds.length];
}
for (int i = 0; i < cHosters.length; i++) {
cHostersVals[i] = vals[i + dHosters.length];
cEndsVals[i] = vals[i + dHosters.length + cHosters.length];
}
//A hashmap to save the changes of each node (relatives to the previous moment) in the resources distribution
TIntIntHashMap[] cpuChanges = new TIntIntHashMap[capacityCPU.length];
TIntIntHashMap[] memChanges = new TIntIntHashMap[capacityMem.length];
for (int i = 0; i < capacityMem.length; i++) {
cpuChanges[i] = new TIntIntHashMap();
memChanges[i] = new TIntIntHashMap();
}
for (int i = 0; i < dHostersVals.length; i++) {
int nIdx = dHostersVals[i];
cpuChanges[nIdx].put(dStartsVals[i], cpuChanges[nIdx].get(dStartsVals[i]) - dCPUHeights[i]);
memChanges[nIdx].put(dStartsVals[i], memChanges[nIdx].get(dStartsVals[i]) - dMemHeights[i]);
}
int[] currentFreeCPU = Arrays.copyOf(capacityCPU, capacityCPU.length);
int[] currentFreeMem = Arrays.copyOf(capacityMem, capacityMem.length);
for (int i = 0; i < cHostersVals.length; i++) {
int nIdx = cHostersVals[i];
cpuChanges[nIdx].put(cEndsVals[i], cpuChanges[nIdx].get(cEndsVals[i]) + cCPUHeights[i]);
memChanges[nIdx].put(cEndsVals[i], memChanges[nIdx].get(cEndsVals[i]) + cMemHeights[i]);
currentFreeCPU[nIdx] -= cCPUHeights[i];
currentFreeMem[nIdx] -= cMemHeights[i];
}
for (int x = 0; x < capacityCPU.length; x++) {
//Now we check the evolution of the absolute free space.
ChocoLogging.getBranchingLogger().finest("--- " + x + " isSatisfied() ---");
for (int i = 0; i < cHostersVals.length; i++) {
ChocoLogging.getBranchingLogger().finest(i + " " + cEnds[i].pretty() + " ends at " + cEndsVals[i]);
}
for (int i = 0; i < dHostersVals.length; i++) {
ChocoLogging.getBranchingLogger().finest(dStarts[i].pretty());
}
ChocoLogging.getBranchingLogger().finest(x + " currentFreeCPU=" + currentFreeCPU[x]);
ChocoLogging.getBranchingLogger().finest(x + " currentFreeMem=" + currentFreeMem[x]);
ChocoLogging.getBranchingLogger().finest(cpuChanges[x].toString());
ChocoLogging.getBranchingLogger().finest(memChanges[x].toString());
for (int i = 0; i < cpuChanges[x].keys().length; i++) {
currentFreeCPU[x] += cpuChanges[x].get(i);
currentFreeMem[x] += memChanges[x].get(i);
if (currentFreeCPU[x] < 0 || currentFreeMem[x] < 0) {
ChocoLogging.getMainLogger().severe(x + " at moment " + i + ": freeCPU=" + currentFreeCPU[x] + ", freeMem=" + currentFreeMem[x]);
//System.exit(1);
return false;
}
}
}
return true;
}
}