All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.btrplace.scheduler.choco.extensions.TaskScheduler Maven / Gradle / Ivy

Go to download

Implementation of the VM scheduler that use the Constraint Programming solver CHOCO to compute solutions.

The newest version!
/*
 * Copyright  2020 The BtrPlace Authors. All rights reserved.
 * Use of this source code is governed by a LGPL-style
 * license that can be found in the LICENSE.txt file.
 */

package org.btrplace.scheduler.choco.extensions;


import gnu.trove.map.hash.TIntIntHashMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import org.chocosolver.memory.IStateInt;
import org.chocosolver.memory.IStateIntVector;
import org.chocosolver.solver.constraints.Constraint;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.constraints.PropagatorPriority;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.util.ESat;
import org.chocosolver.util.tools.ArrayUtils;

import java.util.Arrays;
import java.util.BitSet;

/**
 * A constraint to schedule tasks with regards to their resource usages on resources having a finite amount to share.
 * Tasks and resources can have multiple dimensions.
 * There is only 2 kind of tasks. cTasks that are already placed and necessarily starts at 0 and dTasks that
 * are not placed but end necessarily at the end of the schedule.
 * Inspired by the cumulative constraint.
 *
 * @author Fabien Hermenier
 */
public class TaskScheduler extends Constraint {

    /**
     * Make a new constraint.
     *
     * @param earlyStarts a variable for each resource to indicate the earliest moment a task can arrive on the resource
     * @param lastEnds    a variable for each resource to indicate the latest moment a task can stay on the resource
     * @param capas       the capacity for each resource and for each dimension
     * @param cHosters    the placement variable of each cTask
     * @param cUsages     the resource usage of each cTask for each dimension
     * @param cEnds       the moment each cTask ends
     * @param dHosters    the placement variable of each dTask
     * @param dUsages     the resource usage of each dTask for each dimension
     * @param dStarts     the moment each dTask starts
     * @param assocs      indicate association between cTasks and dTasks. Associated tasks cannot overlap on a same resource
     */
    public TaskScheduler(IntVar[] earlyStarts,
                         IntVar[] lastEnds,
                         int[][] capas,
                         IntVar[] cHosters,
                         int[][] cUsages,
                         IntVar[] cEnds,
                         IntVar[] dHosters,
                         int[][] dUsages,
                         IntVar[] dStarts,
                         int[] assocs) {

        super("TaskScheduler", new TaskSchedulerPropagator(earlyStarts, lastEnds, capas, cHosters, cUsages, cEnds, dHosters, dUsages, dStarts, assocs));
    }

    static class TaskSchedulerPropagator extends Propagator {

      private final LocalTaskScheduler[] scheds;

      private final IntVar[] cHosters;

      private final IntVar[] cEnds;

      private final IntVar[] dHosters;

      private final IntVar[] dStarts;

      private final int nbHosts;

      private final int nbDims;

      private final int[][] capacities;

      private final int[][] cUsages;

      private final int[][] dUsages;

      private final IStateIntVector[] vIns;

      // Because Choco 4.0.5 no longer provides size().
      private final IStateInt[] vInsSize;

      private final IntVar[] earlyStarts;

      private final IntVar[] lastEnds;

      private final IStateInt watchDTask;

      private final BitSet watchHosts;

        @SuppressWarnings("squid:S3346")
        public TaskSchedulerPropagator(IntVar[] earlyStarts,
                                       IntVar[] lastEnds,
                                       int[][] capas,
                                       IntVar[] cHosters,
                                       int[][] cUsages,
                                       IntVar[] cEnds,
                                       IntVar[] dHosters,
                                       int[][] dUsages,
                                       IntVar[] dStarts,
                                       int[] assocs) {
            super(ArrayUtils.append(dHosters, cHosters, cEnds, dStarts, earlyStarts, lastEnds), PropagatorPriority.VERY_SLOW, false);
            this.cHosters = cHosters;
            this.dHosters = dHosters;
            this.cEnds = cEnds;
            this.dStarts = dStarts;
            this.nbHosts = capas.length;
            this.nbDims = capas[0].length;

            assert cUsages.length == cHosters.length;
            assert dUsages.length == dHosters.length;
            assert cUsages.length == 0 || cUsages[0].length == nbDims;
            assert dUsages.length == 0 || dUsages[0].length == nbDims;
            this.capacities = capas;
            this.cUsages = cUsages;
            this.dUsages = dUsages;

            scheds = new LocalTaskScheduler[nbHosts];
            this.vIns = new IStateIntVector[nbHosts];
            this.vInsSize = new IStateInt[nbHosts];
            this.earlyStarts = earlyStarts;
            this.lastEnds = lastEnds;
            BitSet[] outs = new BitSet[nbHosts];
            int nbCTasks = cHosters.length;
            for (int h = 0; h < nbHosts; h++) {
                outs[h] = new BitSet(nbCTasks);
            }

            for (int ct = 0; ct < nbCTasks; ct++) {
                assert cHosters[ct].isInstantiated();
                outs[cHosters[ct].getValue()].set(ct);
            }


            int[] revAssociations = new int[nbCTasks];
            for (int ct = 0; ct < revAssociations.length; ct++) {
                revAssociations[ct] = LocalTaskScheduler.NO_ASSOCIATIONS;
            }

            for (int dt = 0; dt < assocs.length; dt++) {
                if (assocs[dt] != LocalTaskScheduler.NO_ASSOCIATIONS) {
                    revAssociations[assocs[dt]] = dt;
                }
            }

            for (int h = 0; h < nbHosts; h++) {
                vIns[h] = earlyStarts[0].getModel().getEnvironment().makeIntVector(0, 0);
                vInsSize[h] = earlyStarts[0].getModel().getEnvironment().makeInt(0);
                scheds[h] = new LocalTaskScheduler(h,
                        this.earlyStarts[h],
                        this.lastEnds[h],
                        this.capacities,
                        this.cHosters,
                        this.cUsages,
                        this.cEnds,
                        outs[h],
                        this.dHosters,
                        this.dUsages,
                        this.dStarts,
                        this.vIns[h],
                        this.vInsSize[h],
                        assocs,
                        revAssociations,
                        this
                );
            }

            watchDTask = earlyStarts[0].getModel().getEnvironment().makeInt(0);
            watchHosts = new BitSet(nbHosts);
        }

        /**
         * check dStart[dt] >= earlyStart[dHost[dt]] for all dTasks
         *
         * @param changes the profile. Will be updated with the dSlices value
         * @return the satisfaction status
         */
        private ESat checkDSlices(TIntIntHashMap[][] changes) {
            for (int dt = 0; dt < dHosters.length; dt++) {
                if (!dHosters[dt].isInstantiated() || !dStarts[dt].isInstantiated()) {
                    return ESat.UNDEFINED;
                }
                int h = dHosters[dt].getValue();
                int t = dStarts[dt].getValue();
                if (t < earlyStarts[h].getValue()) {
                    return ESat.FALSE;
                }
                for (int d = 0; d < nbDims; d++) {
                    changes[h][d].put(t, changes[h][d].get(t) - dUsages[dt][d]);
                }
            }
            return ESat.TRUE;
        }

        /**
         * check cEnd[ct] <= lastEnd[cHost[ct]] for all cTasks.
         *
         * @param changes  will be modified
         * @param initFree the initial amount of free resources
         * @return the satisfaction status
         */
        private ESat checkCSlices(TIntIntHashMap[][] changes, int[][] initFree) {

            for (int ct = 0; ct < cHosters.length; ct++) {
                if (!cHosters[ct].isInstantiated() || !cEnds[ct].isInstantiated()) {
                    return ESat.UNDEFINED;
                }
                int h = cHosters[ct].getValue();
                int t = cEnds[ct].getValue();
                if (t > lastEnds[h].getValue()) {
                    return ESat.FALSE;
                }
                for (int d = 0; d < nbDims; d++) {
                    changes[h][d].put(t, changes[h][d].get(t) + cUsages[ct][d]);
                    initFree[h][d] -= cUsages[ct][d];
                }
            }
            return ESat.TRUE;
        }

        /**
         * check resource profile on each host
         *
         * @param changes  the resource profiles
         * @param initFree the initial amount of free resources
         * @return the satisfaction status
         */
        private ESat checkProfiles(TIntIntHashMap[][] changes, int[][] initFree) {
            boolean ok = true;
            for (int h = 0; h < nbHosts; h++) {
                TIntObjectHashMap myChanges = myChanges(changes[h]);
                int[] moments = myChanges.keys(new int[myChanges.size()]);
                Arrays.sort(moments);
                for (int t : moments) {
                    boolean bad = false;
                    for (int d = 0; d < nbDims; d++) {
                        initFree[h][d] += myChanges.get(t)[d];
                        if (initFree[h][d] < 0) {
                            bad = true;
                        }
                    }
                    if (bad) {
                        ok = false;
                        break;
                    }
                }
            }
            return ESat.eval(ok);
        }

        @Override
        public ESat isEntailed() {

            //A hashmap to save the changes of each node (relatives to the previous moment) and each dimension
            TIntIntHashMap[][] changes = new TIntIntHashMap[nbHosts][nbDims];
            int[][] initFree = new int[nbHosts][];
            for (int h = 0; h < nbHosts; h++) {
                for (int d = 0; d < nbDims; d++) {
                    changes[h][d] = new TIntIntHashMap();
                }
                initFree[h] = Arrays.copyOf(capacities[h], capacities[h].length);
            }

            ESat sat = checkDSlices(changes);
            if (ESat.TRUE != sat) {
                return sat;
            }

            sat = checkCSlices(changes, initFree);
            if (ESat.TRUE != sat) {
                return sat;
            }

            return checkProfiles(changes, initFree);
        }

        @Override
        public void propagate(int evtmask) throws ContradictionException {
            int freeDTask = watchDTask.get();
            if (freeDTask < dHosters.length && (!dHosters[freeDTask].isInstantiated() || updateVInsAndWatch(freeDTask))) {
                return;
            }
            watchHosts.set(0, nbHosts);
            do {
                for (int h = watchHosts.nextSetBit(0); h >= 0; h = watchHosts.nextSetBit(h + 1)) {
                    scheds[h].propagate(watchHosts);
                    watchHosts.clear(h);
                }
            } while (!watchHosts.isEmpty());

        }

        private boolean updateVInsAndWatch(int dt) {
            int d = dt;
            while (d < dHosters.length && dHosters[d].isInstantiated()) {
                int h = dHosters[d].getValue();
                vIns[h].add(d);
                vInsSize[h].add(1);
                d++;
            }
            watchDTask.set(d);
            return d < dHosters.length;
        }

        private TIntObjectHashMap myChanges(TIntIntHashMap[] change) {
            TIntObjectHashMap map = new TIntObjectHashMap<>();
            for (int d = 0; d < nbDims; d++) {
                for (int t : change[d].keys()) {
                    int[] upd = map.get(t);
                    if (upd == null) {
                        upd = new int[nbDims];
                        map.put(t, upd);
                    }
                    upd[d] += change[d].get(t);
                }
            }
            return map;
        }

        private static String prettyChanges(TIntObjectHashMap changes) {
            int[] moments = changes.keys(new int[changes.size()]);
            Arrays.sort(moments);
            StringBuilder b = new StringBuilder();
            for (int t : moments) {
                b.append(t).append('=').append(Arrays.toString(changes.get(t))).append(' ');
            }
            return b.toString();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy