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

org.chocosolver.solver.constraints.nary.binPacking.PropBinPacking Maven / Gradle / Ivy

There is a newer version: 4.10.17
Show newest version
/*
 * This file is part of choco-solver, http://choco-solver.org/
 *
 * Copyright (c) 2023, IMT Atlantique. All rights reserved.
 *
 * Licensed under the BSD 4-clause license.
 *
 * See LICENSE file in the project root for full license information.
 */
package org.chocosolver.solver.constraints.nary.binPacking;

import org.chocosolver.memory.IStateInt;
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.solver.variables.delta.IIntDeltaMonitor;
import org.chocosolver.solver.variables.events.IntEventType;
import org.chocosolver.solver.variables.events.PropagatorEventType;
import org.chocosolver.util.ESat;
import org.chocosolver.util.objects.setDataStructures.ISet;
import org.chocosolver.util.objects.setDataStructures.ISetIterator;
import org.chocosolver.util.objects.setDataStructures.SetFactory;
import org.chocosolver.util.objects.setDataStructures.SetType;
import org.chocosolver.util.procedure.UnaryIntProcedure;
import org.chocosolver.util.tools.ArrayUtils;

import java.util.BitSet;
import java.util.Comparator;
import java.util.stream.IntStream;

/**
 * Propagator for a Bin Packing constraint
 * This propagator is an implementation of filtering rules introduced in the following paper :
 * Shaw, P. (2004). A Constraint for Bin Packing. In M. Wallace (Ed.), Principles and Practice of Constraint Programming – CP 2004 (pp. 648–662). Springer Berlin Heidelberg.
 *
 * @author Arthur Godet , Jean-Guillaume Fages
 */
public class PropBinPacking extends Propagator {
    private final IntVar[] itemBin;
    protected int[] itemSize;
    protected IntVar[] binLoad;
    protected final int offset;

    private final int nbItems;
    private final int nbAvailableBins;

    protected IIntDeltaMonitor[] monitors;
    protected ISet[] P;
    protected ISet[] R;
    protected IStateInt[] sumR;
    private final IStateInt[] sumP;
    private final BitSet binsToProcess;

    // NoSum parameters and Java variables
    private final boolean useNoSumFiltering;
    private int sumA;
    private int sumB;
    private int sumC;
    private int k;
    private int kPrime;
    private final int[] indexSortedBySize;
    private final int[] X;
    private int xSize;

    @SuppressWarnings("Convert2Diamond")
    private final UnaryIntProcedure procedure = new UnaryIntProcedure() {
        int item;

        @Override
        public UnaryIntProcedure set(Integer itemIdx) {
            item = itemIdx;
            return this;
        }

        @Override
        public void execute(int bin) throws ContradictionException {
            bin -= offset;
            if (bin >= 0 && bin < nbAvailableBins && P[bin].contains(item)) {
                P[bin].remove(item);
                binLoad[bin].updateUpperBound(sumP[bin].add(-itemSize[item]), PropBinPacking.this);
            }
        }
    };

    /**
     * Propagator for a Bin Packing constraint
     *
     * @param itemBin bin of every item (possibly with offset)
     * @param itemSize size of every item
     * @param binLoad total load of every bin
     * @param offset index offset: binOfItem[i] = k means item i is in bin k-offset
     */
    public PropBinPacking(IntVar[] itemBin, int[] itemSize, IntVar[] binLoad, int offset) {
        this(itemBin, itemSize, binLoad, offset, true);
    }

    /**
     * Propagator for a Bin Packing constraint
     *
     * @param itemBin bin of every item (possibly with offset)
     * @param itemSize size of every item
     * @param binLoad total load of every bin
     * @param offset index offset: binOfItem[i] = k means item i is in bin k-offset
     * @param useNoSumFiltering indicates whether to use NoSum filterings or not (should be true)
     */
    public PropBinPacking(IntVar[] itemBin, int[] itemSize, IntVar[] binLoad, int offset, boolean useNoSumFiltering) {
        super(ArrayUtils.append(itemBin, binLoad), PropagatorPriority.LINEAR, true);
        this.itemBin = itemBin;
        this.itemSize = itemSize;
        this.binLoad = binLoad;
        this.offset = offset;
        this.useNoSumFiltering = useNoSumFiltering;

        nbItems = itemBin.length;
        nbAvailableBins = binLoad.length;

        monitors = new IIntDeltaMonitor[nbItems];
        for(int i=0; i -itemSize[i]))
            .mapToInt(i -> i)
            .toArray();
    }

    @Override
    public int getPropagationConditions(int vIdx) {
        if(vIdx < nbItems) {
            return IntEventType.all();
        } else {
            return IntEventType.boundAndInst();
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////    FILTERING ALGORITHMS    //////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////

    private void removeItemFromBin(int binIdx, int itemIdx) throws ContradictionException {
        if(P[binIdx].contains(itemIdx)) {
            P[binIdx].remove(itemIdx);
            binLoad[binIdx].updateUpperBound(sumP[binIdx].add(-itemSize[itemIdx]), this);
            binsToProcess.set(binIdx);
            if(itemBin[itemIdx].isInstantiated()) {
                updateRAfterInstantiation(itemBin[itemIdx].getValue() - offset, itemIdx);
            }
        }
    }

    protected void updateRAfterInstantiation(int binIdx, int itemIdx) throws ContradictionException {
        if(R[binIdx].add(itemIdx)) {
            binLoad[binIdx].updateLowerBound(sumR[binIdx].add(itemSize[itemIdx]), this);
            binsToProcess.set(binIdx);
            for(int k = 0; k binLoad[j].getUB() && itemBin[i].removeValue(j+offset, this)) {
                        hasFiltered = true;
                        removeItemFromBin(j, i);
                    }
                }
            }
        }
        return hasFiltered;
    }

    private void processBin(int j) throws ContradictionException {
        binsToProcess.clear(j);
        boolean hasFiltered;
        do {
            hasFiltered = singleItemEliminationAndCommitment(j);
            if(useNoSumFiltering) {
                hasFiltered |= noSumFiltering(j);
            }
        } while(hasFiltered);
    }

    ////////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////    NO_SUM METHODS    /////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////

    private void fillXArrayWithCj(int j, int idxToRemove) {
        xSize = 0;
        for(int i = 0; i < nbItems; i++) {
            if(indexSortedBySize[i] != idxToRemove
                && P[j].contains(indexSortedBySize[i])
                && !R[j].contains(indexSortedBySize[i])
            ) {
                X[xSize++] = indexSortedBySize[i];
            }
        }
    }

    private void noSumInit() {
        sumA = 0;
        sumB = 0;
        sumC = 0;
        k = 0;
        kPrime = 0;
    }

    private boolean noSumComputings(int alpha, int beta) {
        while(kPrime < xSize && sumC + itemSize[X[xSize-1-kPrime]] < alpha) {
            sumC += itemSize[X[xSize-1-kPrime]];
            kPrime++;
        }
        if(kPrime < xSize) {
            sumB = itemSize[X[xSize-1-kPrime]];
        }
        while(k= alpha) {
                    kPrime--;
                    sumC -= itemSize[X[xSize-1-kPrime]];
                    sumB += itemSize[X[xSize-1-kPrime]] - itemSize[X[xSize-1-kPrime-k-1]];
                }
            }
        }
        return sumA < alpha;
    }

    private boolean noSum(int j, int alpha, int beta) {
        return noSum(j, alpha, beta, -1);
    }

    private boolean noSum(int j, int alpha, int beta, int idxToRemove) {
        if(alpha <= 0 || beta >= sumP[j].get()-sumR[j].get()) {
            return false;
        }
        noSumInit();
        fillXArrayWithCj(j, idxToRemove);
        return noSumComputings(alpha, beta);
    }

    private boolean noSumFiltering(int j) throws ContradictionException {
        boolean hasFiltered = false;
        // Pruning Rule
        if(noSum(j, binLoad[j].getLB()-sumR[j].get(), binLoad[j].getUB()-sumR[j].get())) {
            fails();
        }
        // Tightening Bounds on Bin Load
        int lbVal = binLoad[j].getLB()-sumR[j].get();
        if(noSum(j, lbVal, lbVal)) {
            hasFiltered = true;
            binLoad[j].updateLowerBound(sumR[j].get() + sumB, this);
        }
        int ubVal = binLoad[j].getUB()-sumR[j].get();
        if(noSum(j, ubVal, ubVal)) {
            binLoad[j].updateUpperBound(sumR[j].get() + sumA + sumC, this);
        }
        // Elimination and Commitment of Items
        ISetIterator iter = P[j].iterator();
        while(iter.hasNext()) {
            int i = iter.nextInt();
            if(!R[j].contains(i)) {
                int lbVal2 = binLoad[j].getLB()-sumR[j].get()-itemSize[i];
                int ubVal2 = binLoad[j].getUB()-sumR[j].get()-itemSize[i];
                if(noSum(j, lbVal2, ubVal2, i)) {
                    if(itemBin[i].removeValue(j+offset, this)) {
                        hasFiltered = true;
                        removeItemFromBin(j, i);
                    }
                }
                lbVal2 += itemSize[i];
                ubVal2 += itemSize[i];
                if(noSum(j, lbVal2, ubVal2, i)) {
                    hasFiltered |= itemBin[i].instantiateTo(j+offset, this);
                    updateRAfterInstantiation(j, i);
                }
            }
        }
        return hasFiltered;
    }

    ////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////    PROPAGATION    ///////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void propagate(int idxVarInProp, int mask) throws ContradictionException {
        if(idxVarInProp < nbItems) {
            monitors[idxVarInProp].forEachRemVal(procedure.set(idxVarInProp));
            if(itemBin[idxVarInProp].isInstantiated()) {
                int j = itemBin[idxVarInProp].getValue() - offset;
                updateRAfterInstantiation(j, idxVarInProp);
                binsToProcess.set(j);
            }
        } else {
            binsToProcess.set(idxVarInProp - nbItems);
        }
        forcePropagate(PropagatorEventType.CUSTOM_PROPAGATION);
    }

    @Override
    public void propagate(int evtmask) throws ContradictionException {
        if(PropagatorEventType.isFullPropagation(evtmask)) {
            for(int i = 0; i=nbAvailableBins+offset){
                    return ESat.FALSE;
                }
            }
        }
        for(int b = 0; b binLoad[b].getUB() || max < binLoad[b].getLB()) {
                return ESat.FALSE;
            }
        }
        if(isCompletelyInstantiated()) {
            return ESat.TRUE;
        }
        return ESat.UNDEFINED;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy