Package solver.constraints.nary

Source Code of solver.constraints.nary.PropSort

/**
*  Copyright (c) 1999-2014, Ecole des Mines de Nantes
*  All rights reserved.
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions are met:
*
*      * Redistributions of source code must retain the above copyright
*        notice, this list of conditions and the following disclaimer.
*      * Redistributions in binary form must reproduce the above copyright
*        notice, this list of conditions and the following disclaimer in the
*        documentation and/or other materials provided with the distribution.
*      * Neither the name of the Ecole des Mines de Nantes nor the
*        names of its contributors may be used to endorse or promote products
*        derived from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
*  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*  DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
*  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package solver.constraints.nary;

import gnu.trove.map.hash.THashMap;
import gnu.trove.stack.TIntStack;
import gnu.trove.stack.array.TIntArrayStack;
import solver.Solver;
import solver.constraints.Propagator;
import solver.constraints.PropagatorPriority;
import solver.exception.ContradictionException;
import solver.variables.IntVar;
import util.ESat;
import util.objects.PriorityQueue;
import util.tools.ArrayUtils;

import java.io.Serializable;
import java.util.Arrays;

/**
* <code>SortingConstraint</code> is a constraint that ensures
* that a vector is the sorted version of a second one. The filtering
* algorithm is the version of Kurt Mehlhorn and Sven Thiel, from
* CP'00 (<i>Faster algorithms for Bound-Consistency of the Sortedness
* and the Alldifferent Constraint</i>).
*
* @author Sylvain Bouveret (initial code)
* @author Charles Prud'homme (migration to choco3, debugging)
* @since 17 apr. 2014
*/

public final class PropSort extends Propagator<IntVar> {

    private int n; // size of X, and obviously Y

    private PriorityQueue pQueue; // a priority queue
    private IntVar[] x, y; // ref to X and Y, instead of vars
    private int[] f, fPrime;
    private int[][] xyGraph;
    private int[] dfsNodes;
    private int[] sccNumbers;
    private int currentSccNumber;
    private int[] tmpArray;
    private int[][] sccSequences;
    private TIntStack s1;
    private Stack2 s2;
    private int[] recupStack = new int[3];
    private int[] recupStack2 = new int[3];

    /**
     * Creates a new <code>PropSort</code> instance.
     *
     * @param x the first array of integer variables
     * @param y the second array of integer variables
     */
    public PropSort(IntVar[] x, IntVar[] y) {
        super(ArrayUtils.append(x, y), PropagatorPriority.LINEAR, false);
        if (x.length != y.length || x.length == 0) {
            throw new IllegalArgumentException("PropSort Error: the two vectors "
                    + "must be of the same (non zero) size");
        }
        this.n = x.length;
        this.x = x;
        this.y = y;
        this.f = new int[this.n];
        this.fPrime = new int[this.n];
        this.xyGraph = new int[this.n][this.n];
        this.sccSequences = new int[this.n][this.n];
        this.dfsNodes = new int[this.n];
        this.sccNumbers = new int[this.n];
        this.tmpArray = new int[this.n];
        this.pQueue = new PriorityQueue(this.n);
        this.s1 = new TIntArrayStack(this.n);
        this.s2 = new Stack2(this.n);
    }


    @Override
    public void propagate(int evtmask) throws ContradictionException {
        filter();
    }

    @Override
    public ESat isEntailed() {
        if (isCompletelyInstantiated()) {
            int[] _x = new int[this.n];
            for (int i = 0; i < n; i++) {
                _x[i] = x[i].getValue();
            }
            java.util.Arrays.sort(_x);

            int i;
            i = 0;
            while (i < n && _x[i] == y[i].getValue()) {
                i++;
            }
            return ESat.eval(i == n);
        }
        return ESat.UNDEFINED;
    }

    @Override
    public void duplicate(Solver solver, THashMap<Object, Object> identitymap) {
        if (!identitymap.containsKey(this)) {
            int size = this.x.length;
            IntVar[] X = new IntVar[size];
            for (int i = 0; i < size; i++) {
                this.x[i].duplicate(solver, identitymap);
                X[i] = (IntVar) identitymap.get(this.x[i]);
            }
            size = this.y.length;
            IntVar[] Y = new IntVar[size];
            for (int i = 0; i < size; i++) {
                this.x[i].duplicate(solver, identitymap);
                Y[i] = (IntVar) identitymap.get(this.y[i]);
            }
            identitymap.put(this, new PropSort(X, Y));
        }
    }

    private void filter() throws ContradictionException {
        int i, jprime, tmp, j, k;

        for (i = 0; i < this.n; i++) {
            Arrays.fill(this.xyGraph[i], -1);
            Arrays.fill(this.sccSequences[i], -1);
        }


        /////////////////////////////////////////////////////////////
        // Normalizing the vectors...
        /////////////////////////////////////////////////////////////
        normalize(y);

        /////////////////////////////////////////////////////////////
        // Computing the perfect maching f... (optimized !)
        /////////////////////////////////////////////////////////////
        this.pQueue.clear();
        for (i = 0; i < this.n; i++) {
            if (intersect(0, i)) {
                this.pQueue.addElement(i, x[i].getUB());
            }
        }

        this.f[0] = this.computeF(0);

        for (j = 1; j < this.n; j++) {
            for (i = 0; i < this.n; i++) {
                if (x[i].getLB() > y[j - 1].getUB() && x[i].getLB() <= y[j].getUB()) {
                    this.pQueue.addElement(i, x[i].getUB());
                }
            }
            this.f[j] = this.computeF(j);
        }

        /////////////////////////////////////////////////////////////
        // Narrowing the upper bounds of y...
        /////////////////////////////////////////////////////////////

        for (i = 0; i < this.n; i++) {
            y[i].updateUpperBound(x[this.f[i]].getUB(), aCause);
        }

        /////////////////////////////////////////////////////////////
        // Computing the perfect maching f'... (optimized !)
        /////////////////////////////////////////////////////////////

        this.pQueue.clear();

        for (i = 0; i < this.n; i++) {
            if (intersect(n - 1, i)) {
                this.pQueue.addElement(i, -x[i].getLB());
            }
        }

        this.fPrime[this.n - 1] = this.computeFPrime(this.n - 1);

        for (j = this.n - 2; j >= 0; j--) {
            for (i = 0; i < this.n; i++) {
                if (x[i].getUB() < y[j + 1].getLB() && x[i].getUB() >= y[j].getLB()) {
                    this.pQueue.addElement(i, -x[i].getLB());
                }
            }
            this.fPrime[j] = this.computeFPrime(j);
        }

        /////////////////////////////////////////////////////////////
        // Narrowing the lower bounds of y...
        /////////////////////////////////////////////////////////////

        for (i = 0; i < this.n; i++) {
            y[i].updateLowerBound(x[this.fPrime[i]].getLB(), aCause);
        }

        /////////////////////////////////////////////////////////////
        // Computing the strong connected components (optimized)...
        /////////////////////////////////////////////////////////////

        for (j = 0; j < this.n; j++) { // for each y
            tmp = 0;
            jprime = this.f[j]; // jprime is the number of x associated with y_j
            for (i = 0; i < this.n; i++) { // for each other y
                if (j != i && intersect(i, jprime)) {
                    this.xyGraph[j][tmp] = i;
                    tmp++;
                }
            }
        }

        this.dfs();

        /////////////////////////////////////////////////////////////
        // Narrowing the lower bounds of x... (to be optimized)
        /////////////////////////////////////////////////////////////

        Arrays.fill(this.tmpArray, 0);
        for (i = 0; i < this.n; i++) {
            this.sccSequences[this.sccNumbers[i]][tmpArray[this.sccNumbers[i]]] = i;
            tmpArray[this.sccNumbers[i]]++;
        }

        for (i = 0; i < this.n && this.sccSequences[i][0] != -1; i++) { // for each strongly connected component...
            for (j = 0; j < this.n && this.sccSequences[i][j] != -1; j++) { // for each x of the component
                jprime = this.f[this.sccSequences[i][j]];
                for (k = 0; k < this.n && this.sccSequences[i][k] != -1 && x[jprime].getLB() > y[this.sccSequences[i][k]].getUB(); k++) {
                }
                // scan the sequence of the ys of the connected component, until one becomes greater than or equal to x
                assert (this.sccSequences[i][k] != -1);
                x[jprime].updateLowerBound(y[this.sccSequences[i][k]].getLB(), aCause);
            }
        }

        /////////////////////////////////////////////////////////////
        // Narrowing the upper bounds of x... (to be optimized)
        /////////////////////////////////////////////////////////////

        Arrays.fill(this.tmpArray, 0);
        for (i = this.n - 1; i >= 0; i--) {
            this.sccSequences[this.sccNumbers[i]][tmpArray[this.sccNumbers[i]]] = i;
            tmpArray[this.sccNumbers[i]]++;
        }

        for (i = 0; i < this.n && this.sccSequences[i][0] != -1; i++) { // for each strongly connected component...
            for (j = 0; j < this.n && this.sccSequences[i][j] != -1; j++) { // for each x of the component
                jprime = this.f[this.sccSequences[i][j]];
                for (k = 0; k < this.n && this.sccSequences[i][k] != -1 && x[jprime].getUB() < y[this.sccSequences[i][k]].getLB(); k++) {
                }
                // scan the sequence of the ys of the connected component, until one becomes lower than or equal to x
                assert (this.sccSequences[i][k] != -1);
                x[jprime].updateUpperBound(y[this.sccSequences[i][k]].getUB(), aCause);
            }
        }
    }

    private void normalize(IntVar[] y) throws ContradictionException {
        for (int i = 1; i < this.n; i++) {
            y[i].updateLowerBound(y[i - 1].getLB(), aCause);
        }

        for (int i = this.n - 2; i >= 0; i--) {
            y[i].updateUpperBound(y[i + 1].getUB(), aCause);
        }
    }


    private boolean intersect(int y, int x) {
        return (this.x[x].getLB() >= this.y[y].getLB() && this.x[x].getLB() <= this.y[y].getUB())
                || (this.x[x].getUB() >= this.y[y].getLB() && this.x[x].getUB() <= this.y[y].getUB())
                || (this.y[y].getLB() >= this.x[x].getLB() && this.y[y].getLB() <= this.x[x].getUB())
                || (this.y[y].getUB() >= this.x[x].getLB() && this.y[y].getUB() <= this.x[x].getUB());


    }

    private int computeF(int j) throws ContradictionException {
        if (this.pQueue.isEmpty()) {
            this.contradiction(null, "");
        }
        int i = this.pQueue.pop();
        if (x[i].getUB() < y[j].getLB()) {
            this.contradiction(null, "");
        }

        return i;
    }

    private int computeFPrime(int j) throws ContradictionException {
        if (this.pQueue.isEmpty()) {
            this.contradiction(null, "");
        }
        int i = this.pQueue.pop();
        if (x[i].getLB() > y[j].getUB()) {
            this.contradiction(null, "");
        }

        return i;
    }


    private void dfs() {
        Arrays.fill(this.dfsNodes, 0);
        this.s1.clear();
        this.s2.clear();
        this.currentSccNumber = 0;

        int i;
        for (i = 0; i < this.n; i++) {
            if (this.dfsNodes[i] == 0) {
                this.dfsVisit(i);
            }
        }
        while (s1.size() > 0 && !s2.isEmpty()) {
            s2.peek(recupStack);
            do {
                i = this.s1.pop();
                this.sccNumbers[i] = currentSccNumber;
            } while (s1.size() > 0 && i != recupStack[0]);
            currentSccNumber++;
            s2.pop();
        }
    }

    private void dfsVisit(int node) {
        int i;
        this.dfsNodes[node] = 1;
        if (this.s2.isEmpty()) {
            this.s1.push(node);
            this.s2.push(node, node, x[f[node]].getUB());
            i = 0;
            while (xyGraph[node][i] != -1) {
                if (dfsNodes[xyGraph[node][i]] == 0) {
                    this.dfsVisit(xyGraph[node][i]);
                }
                i++;
            }
        } else {
            while (this.s2.peek(this.recupStack) && this.recupStack[2] < y[node].getLB()) {// the topmost component cannot reach "node".
                while ((i = this.s1.pop()) != this.recupStack[0]) {
                    this.sccNumbers[i] = currentSccNumber;
                }
                this.sccNumbers[i] = currentSccNumber;
                this.s2.pop();
                currentSccNumber++;
            }
            this.s1.push(node);
            this.recupStack[0] = node;
            this.recupStack[1] = node;
            this.recupStack[2] = this.x[this.f[node]].getUB();
            this.mergeStack(node);
            i = 0;
            while (xyGraph[node][i] != -1) {
                if (dfsNodes[xyGraph[node][i]] == 0) {
                    this.dfsVisit(xyGraph[node][i]);
                }
                i++;
            }
        }

        this.dfsNodes[node] = 2;
    }

    private boolean mergeStack(int node) {
        this.s2.peek(this.recupStack2);
        boolean flag = false;
        while (!this.s2.isEmpty() && y[this.recupStack2[1]].getUB() >= x[this.f[node]].getLB()) {
            flag = true;
            this.recupStack[0] = this.recupStack2[0];
            this.recupStack[1] = node;
            this.recupStack[2] = this.recupStack[2] > this.recupStack2[2] ? this.recupStack[2] : this.recupStack2[2];
            this.s2.pop();
            this.s2.peek(this.recupStack2);
        }
        this.s2.push(this.recupStack[0], this.recupStack[1], this.recupStack[2]);
        return flag;
    }


    //////////////////////
    //////////////////////
    //////////////////////


    private static class Stack2 implements Serializable {
        private int[] roots;
        private int[] rightMosts;
        private int[] maxXs;
        private int n;
        private int nbElts = 0;

        public Stack2(int _n) {
            this.n = _n;
            this.roots = new int[_n];
            this.rightMosts = new int[_n];
            this.maxXs = new int[_n];
        }

        public boolean push(int root, int rightMost, int maxX) {
            if (this.nbElts == this.n) {
                return false;
            }
            this.roots[this.nbElts] = root;
            this.rightMosts[this.nbElts] = rightMost;
            this.maxXs[this.nbElts] = maxX;
            this.nbElts++;
            return true;
        }

        public boolean pop() {
            if (this.isEmpty()) {
                return false;
            }
            this.nbElts--;
            return true;
        }

        public boolean pop(int[] x) {
            if (this.isEmpty()) {
                return false;
            }
            this.nbElts--;
            x[0] = this.roots[this.nbElts];
            x[1] = this.rightMosts[this.nbElts];
            x[2] = this.maxXs[this.nbElts];
            return true;
        }

        public boolean peek(int[] x) {
            if (this.isEmpty()) {
                return false;
            }
            x[0] = this.roots[this.nbElts - 1];
            x[1] = this.rightMosts[this.nbElts - 1];
            x[2] = this.maxXs[this.nbElts - 1];
            return true;
        }

        public boolean isEmpty() {
            return (this.nbElts == 0);
        }

        public void clear() {
            this.nbElts = 0;
        }

        @Override
        public String toString() {
            StringBuilder s = new StringBuilder();
            for (int i = 0; i < this.nbElts; i++) {
                s.append(" <").append(this.roots[i]).append(", ")
                        .append(this.rightMosts[i]).append(", ").append(this.maxXs[i]).append(">");
            }
            return s.toString();
        }
    }

}
TOP

Related Classes of solver.constraints.nary.PropSort

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.