/*
* 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.search;
import java.util.BitSet;
import java.util.List;
import choco.kernel.common.Constant;
import choco.kernel.common.logging.ChocoLogging;
import choco.kernel.solver.ContradictionException;
import choco.kernel.solver.constraints.SConstraint;
import choco.kernel.solver.search.integer.AbstractIntVarSelector;
import choco.kernel.solver.variables.integer.IntDomainVar;
import entropy.configuration.Configuration;
import entropy.configuration.Node;
import entropy.configuration.VirtualMachine;
import entropy.plan.Plan;
import entropy.plan.choco.ReconfigurationProblem;
import entropy.plan.choco.actionModel.ActionModel;
import entropy.plan.choco.actionModel.ActionModels;
import entropy.plan.choco.actionModel.VirtualMachineActionModel;
import entropy.plan.choco.actionModel.slice.DemandingSlice;
/**
* An heuristic to branch first on the start moment of actions
* that arrive on nodes without any outgoing actions.
*
* @author Fabien Hermenier
*/
public class PureIncomingFirst extends AbstractIntVarSelector {
private IntDomainVar[] hoster;
private IntDomainVar[] starts;
private int[] oldPos;
private BitSet[] outs;
private BitSet[] ins;
private List<SConstraint> constraints;
/**
* Make a new heuristics
*
* @param solver the solver to use
* @param actions the actions to consider.
*/
public PureIncomingFirst(ReconfigurationProblem solver, List<ActionModel> actions, List<SConstraint> costConstraints) {
super(solver, ActionModels.extractStarts(actions.toArray(new ActionModel[actions.size()])));
this.pb = solver;
this.constraints = costConstraints;
Configuration cfg = solver.getSourceConfiguration();
hoster = new IntDomainVar[solver.getVirtualMachineActions().size()];
starts = new IntDomainVar[solver.getVirtualMachineActions().size()];
List<VirtualMachineActionModel> vmActions = solver.getVirtualMachineActions();
VirtualMachine[] vms = new VirtualMachine[vmActions.size()];
oldPos = new int[vms.length];
outs = new BitSet[solver.getNodes().length];
ins = new BitSet[solver.getNodes().length];
for (int i = 0; i < solver.getNodes().length; i++) {
outs[i] = new BitSet();
ins[i] = new BitSet();
}
for (int i = 0; i < hoster.length; i++) {
VirtualMachineActionModel action = vmActions.get(i);
DemandingSlice slice = action.getDemandingSlice();
if (slice != null) {
IntDomainVar h = vmActions.get(i).getDemandingSlice().hoster();
IntDomainVar s = vmActions.get(i).getDemandingSlice().start();
hoster[i] = h;
starts[i] = s;
vms[i] = action.getVirtualMachine();
Node n = cfg.getLocation(vms[i]);
if (n == null) {
oldPos[i] = -1;
} else {
oldPos[i] = solver.getNode(n);
outs[solver.getNode(n)].set(i); //VM i was on node n
}
}
}
}
private boolean first = true;
private ReconfigurationProblem pb;
@Override
public IntDomainVar selectVar() {
if (first) {
first = !first;
Plan.logger.info("Activate cost constraints");
Plan.logger.info("End:" + pb.getEnd().pretty());
for (SConstraint sc : constraints) {
pb.postCut(sc);
}
try {
pb.propagate();
} catch (ContradictionException e) {
e.printStackTrace();
// Plan.logger.error(e.getMessage(), e);
pb.setFeasible(false);
pb.post(Constant.FALSE);
}
}
for (int i = 0; i < ins.length; i++) {
ins[i].clear();
}
BitSet stays = new BitSet();
//At this moment, all the hoster of the demanding slices are computed.
//for each node, we compute the number of incoming and outgoing
for (int i = 0; i < hoster.length; i++) {
if (hoster[i] != null && hoster[i].isInstantiated()) {
int newPos = hoster[i].getVal();
if (oldPos[i] != -1 && newPos != oldPos[i]) {
//The VM has move
ins[newPos].set(i);
} else if (oldPos[i] != -1 && newPos == oldPos[i]) {
stays.set(i);
}
}
}
//TODO: start with nodes with a sufficient amount of free resources at startup
for (int x = 0; x < outs.length; x++) {
if (outs[x].cardinality() == 0) { //no outgoing VMs
BitSet in = ins[x];
for (int i = in.nextSetBit(0); i >= 0; i = in.nextSetBit(i + 1)) {
if (starts[i] != null && !starts[i].isInstantiated()) {
return starts[i];
}
}
}
}
//ChocoLogging.getBranchingLogger().finest("No more pure incoming");
//TODO: Decreasing stay at end
//TODO: association between slice on the same node
for (int i = stays.nextSetBit(0); i >= 0; i = stays.nextSetBit(i + 1)) {
if (starts[i] != null && !starts[i].isInstantiated()) {
return starts[i];
}
}
for (int x = 0; x < outs.length; x++) {
BitSet in = ins[x];
//For all the incoming
for (int i = in.nextSetBit(0); i >= 0; i = in.nextSetBit(i + 1)) {
if (starts[i] != null && !starts[i].isInstantiated()) {
return starts[i];
}
}
}
for (int i = 0; i < starts.length; i++) {
IntDomainVar start = starts[i];
if (starts[i] != null && !start.isInstantiated()) {
return start;
}
}
ChocoLogging.getBranchingLogger().finest("No more variables to instantiate here");
return null;
}
}