Package solver.constraints.ternary

Source Code of solver.constraints.ternary.PropDivXYZ

/*
* 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.ternary;

import gnu.trove.map.hash.THashMap;
import solver.Solver;
import solver.constraints.Propagator;
import solver.constraints.PropagatorPriority;
import solver.exception.ContradictionException;
import solver.variables.IntVar;
import solver.variables.VariableFactory;
import util.ESat;

/**
* X/Y = Z
* A propagator for the constraint Z = X / Y where X, Y and Z are integer, possibly negative, variables
* The filtering algorithm both supports bounded and enumerated integer variables
*/
public class PropDivXYZ extends Propagator<IntVar> {

    // Z = X/Y
    final IntVar X, Y, Z;

    // Abs views of respectively X, Y and Z
    final IntVar absX, absY, absZ;


    public PropDivXYZ(IntVar x, IntVar y, IntVar z) {
        this(x, y, z, VariableFactory.abs(x), VariableFactory.abs(y), VariableFactory.abs(z));
    }

    private PropDivXYZ(IntVar x, IntVar y, IntVar z, IntVar ax, IntVar ay, IntVar az) {
        super(new IntVar[]{x, y, z}, PropagatorPriority.TERNARY, false);
        this.X = x;
        this.Y = y;
        this.Z = z;
        this.absX = ax;
        this.absY = ay;
        this.absZ = az;
    }

    /**
     * The main propagation method that filters according to the constraint defintion
     *
     * @param evtmask: is it the initial propagation or not?
     * @throws ContradictionException
     */
    @Override
    public void propagate(int evtmask) throws ContradictionException {
        boolean hasChanged;
        int mask;
        do {
            mask = 0;
            mask += X.isInstantiated() ? 1 : 0;
            mask += Y.isInstantiated() ? 2 : 0;
            mask += Z.isInstantiated() ? 4 : 0;

            hasChanged = Y.removeValue(0, aCause);
            if (outInterval(Y, 0, 0)) return;

            int vx, vy, vz;
            switch (mask) {
                case 0: // nothing instanciated
                    hasChanged |= updateAbsX();
                    hasChanged |= updateAbsY();
                    hasChanged |= updateAbsZ();
                    break;
                case 1: // X is instanciated
                    hasChanged |= updateAbsY();
                    hasChanged |= updateAbsZ();
                    if (X.isInstantiatedTo(0)) {
                        // sY!=0 && sX=0 => sZ=0
                        hasChanged |= Z.instantiateTo(0, aCause);
                    }
                    break;
                case 2: // Y is instanciated
                    hasChanged |= updateAbsX();
                    hasChanged |= updateAbsZ();
                    break;
                case 3: // X and Y are instanciated
                    vx = X.getValue();
                    vy = Y.getValue();
                    hasChanged |= updateAbsX();
                    hasChanged |= updateAbsY();
                    vz = vx / vy;//(int) Math.floor((double) (vx + ((vx * vy < 0 ? 1 : 0) * (vy - 1))) / (double) vy);
                    if (vy != 0) {
                        if (inInterval(Z, vz, vz)) return; // entail
                    }
                    break;
                case 4: // Z is instanciated
                    hasChanged |= updateAbsX();
                    hasChanged |= updateAbsY();
                    // sZ = 0 && sX!=0 => |x| < |y|
                    if (Z.isInstantiatedTo(0) && !X.contains(0)) {
                        hasChanged |= absX.updateUpperBound(absY.getUB() - 1, aCause);
                    }
                    break;
                case 5: // X and Z are instanciated
                    vx = X.getValue();
                    vz = Z.getValue();
                    hasChanged |= updateAbsX();
                    hasChanged |= updateAbsZ();
                    if (vz != 0 && vx == 0) {
                        this.contradiction(X, "");
                    }
                    hasChanged |= updateAbsY();
                    break;
                case 6: // Y and Z are instanciated
                    vy = Y.getValue();
                    vz = Z.getValue();
                    hasChanged |= updateAbsY();
                    hasChanged |= updateAbsZ();
                    if (vz == 0) {
                        if (inInterval(X, -Math.abs(vy) + 1, Math.abs(vy) - 1)) return;
                    } else { // Y*Z > 0  ou < 0
                        hasChanged |= updateAbsX();
                    }
                    break;
                case 7: // X, Y and Z are instanciated
                    vx = X.getValue();
                    vy = Y.getValue();
                    vz = Z.getValue();
                    int val = vx / vy;
                    if ((vz != val)) {
                        contradiction(Z, "");
                    } else {
                        return;
                    }
                    break;
            }
            //------ update sign ---------
            // at this step, Y != 0 => sY != 0
            if (absX.getUB() < absY.getLB()) {
                // sX!=0 && |X|<|Y| => sZ=0
                hasChanged |= Z.instantiateTo(0, aCause);
            } else if (X.getLB() > 0 && absX.getLB() >= absY.getUB()) {
                // sX=1 && |X|>=|Y| => sZ=sY
                hasChanged |= sameSign(Z, Y);
                hasChanged |= sameSign(Y, Z);
            } else if (X.getUB() < 0 && absX.getLB() >= absY.getUB()) {
                // sX=-1 && |X|>=|Y| => sZ=-sY
                hasChanged |= oppSign(Z, Y);
                hasChanged |= oppSign(Y, Z);
            } //*/
        } while (hasChanged);
    }


    @Override
    public ESat isEntailed() {
        // forbid Y=0
        if (Y.isInstantiatedTo(0)) {
            return ESat.FALSE;
        }
        // X=0 => Z=0
        if (X.isInstantiatedTo(0) && !Z.contains(0)) {
            return ESat.FALSE;
        }
        // check sign
        boolean pos = (X.getLB() >= 0 && Y.getLB() >= 0) || (X.getUB() < 0 && Y.getUB() < 0);
        if (pos && Z.getUB() < 0) {
            return ESat.FALSE;
        }
        boolean neg = (X.getLB() >= 0 && Y.getUB() < 0) || (X.getUB() < 0 && Y.getLB() >= 0);
        if (neg && Z.getLB() > 0) {
            return ESat.FALSE;
        }
        // compute absolute bounds
        int minAbsX;
        if (X.getLB() > 0) {
            minAbsX = X.getLB();
        } else if (X.getUB() < 0) {
            minAbsX = -X.getUB();
        } else {
            minAbsX = 0;
        }
        int maxAbsX = Math.max(X.getUB(), -X.getLB());
        int minAbsY;
        if (Y.getLB() > 0) {
            minAbsY = Y.getLB();
        } else if (Y.getUB() < 0) {
            minAbsY = -Y.getUB();
        } else {
            minAbsY = 1;
        }
        int maxAbsY = Math.max(Y.getUB(), -Y.getLB());
        int minAbsZ;
        if (Z.getLB() > 0) {
            minAbsZ = Z.getLB();
        } else if (Z.getUB() < 0) {
            minAbsZ = -Z.getUB();
        } else {
            minAbsZ = 0;
        }
        int maxAbsZ = Math.max(Z.getUB(), -Z.getLB());
        // check absolute bounds
        if ((minAbsZ > maxAbsX / minAbsY) || (maxAbsZ < minAbsX / maxAbsY)) {
            return ESat.FALSE;
        }
        // check case Z=0
        if ((Z.isInstantiatedTo(0) && minAbsX > maxAbsY) || (maxAbsX < minAbsY && !Z.contains(0))) {
            return ESat.FALSE;
        }
        // check tuple
        if (isCompletelyInstantiated()) {
            return ESat.eval(X.getValue() / Y.getValue() == Z.getValue());
        }
        return ESat.UNDEFINED;
    }


    /**
     * ensure v is already included in [lb;ub]
     *
     * @param v  variable to check
     * @param lb new lower bound
     * @param ub new upper bound
     * @throws solver.exception.ContradictionException
     */
    private boolean inInterval(IntVar v, int lb, int ub) throws ContradictionException {
        if (v.getLB() >= lb && v.getUB() <= ub) {
            setPassive(); // v is already included
            return true;
        } else {
            if (v.getLB() > ub || v.getUB() < lb) {
                contradiction(v, ""); // v is excluded from [lb;ub]
            } else {
                v.updateLowerBound(lb, aCause);
                v.updateUpperBound(ub, aCause);
                setPassive();
                return true;
            }
        }
        return false;
    }

    /**
     * ensure variable v does not take values in [lb;ub]
     *
     * @param v  variable to check
     * @param lb new lower bound
     * @param ub new upper bound
     * @return true iff a value has been removed from v
     * @throws solver.exception.ContradictionException
     */
    private boolean outInterval(IntVar v, int lb, int ub) throws ContradictionException {
        if (lb > ub) {
            setPassive();
            return true;
        } else if (lb < ub) {
            if (v.getLB() > ub || v.getUB() < lb) {
                setPassive(); // v is already disjoint from [lb;ub]
                return true;
            } else {
                if (v.getLB() >= lb && v.getUB() <= ub) {
                    contradiction(v, ""); // v is included in [lb;ub]
                } else {
                    if (v.getLB() >= lb) {
                        v.updateLowerBound(ub + 1, aCause);
                    } else {
                        if (v.getUB() <= ub) {
                            v.updateUpperBound(lb - 1, aCause);
                        }
                    }
                    setPassive();
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Update upper and lower bounds of variable X
     *
     * @return true iff a modification occurred
     * @throws ContradictionException
     */
    private boolean updateAbsX() throws ContradictionException {
        return absX.updateLowerBound(absZ.getLB() * absY.getLB(), aCause) | absX.updateUpperBound((absZ.getUB() * absY.getUB()) + absY.getUB() - 1, aCause);
    }

    /**
     * Update upper and lower bounds of variable Y
     *
     * @return true iff a modification occurred
     * @throws ContradictionException
     */
    private boolean updateAbsY() throws ContradictionException {
        boolean res = absZ.getLB() != 0 && absY.updateUpperBound((int) Math.floor(absX.getUB() / absZ.getLB()), aCause);
        int zlb = absZ.getLB();
        int zub = absZ.getUB();
        int xlb = absX.getLB();
        int yub = absY.getUB();
        int num = xlb - (yub - 1);
        if (num >= 0 && zub != 0) {
            res |= absY.updateLowerBound((int) Math.ceil(num / zub), aCause);
        } else {
            res |= zlb != 0 && absY.updateLowerBound(-(int) Math.floor((-xlb + (yub - 1)) / zlb), aCause);
        }
        return res;
    }

    /**
     * Update upper and lower bounds of variable Y
     *
     * @return true iff a modification occurred
     * @throws ContradictionException
     */
    private boolean updateAbsZ() throws ContradictionException {
        boolean res = absY.getLB() != 0 && absZ.updateUpperBound((int) Math.floor(absX.getUB() / absY.getLB()), aCause);
        int xlb = absX.getLB();
        int ylb = absY.getLB();
        int yub = absY.getUB();
        int num = xlb - (yub - 1);
        if (num >= 0 && yub != 0) {
            res |= absZ.updateLowerBound((int) Math.ceil(num / yub), aCause);
        } else {
            res |= ylb != 0 && absZ.updateLowerBound(-(int) Math.floor((-xlb + (yub - 1)) / ylb), aCause);
        }
        return res;
    }

    /**
     * A take the signs of B
     *
     * @param a first var
     * @param b second var
     */
    protected boolean sameSign(IntVar a, IntVar b) throws ContradictionException {
        boolean res = false;
        if (b.getLB() >= 0) {
            res = a.updateLowerBound(0, aCause);
        }
        if (b.getUB() <= 0) {
            res |= a.updateUpperBound(0, aCause);
        }
        if (!b.contains(0)) {
            res |= a.removeValue(0, aCause);
        }
        return res;
    }

    /**
     * A take the opposite signs of B
     *
     * @param a first var
     * @param b second var
     */
    protected boolean oppSign(IntVar a, IntVar b) throws ContradictionException {
        boolean res = false;
        if (b.getLB() >= 0) {
            res = a.updateUpperBound(0, aCause);
        }
        if (b.getUB() <= 0) {
            res |= a.updateLowerBound(0, aCause);
        }
        if (b.contains(0)) {
            res |= a.removeValue(0, aCause);
        }
        return res;
    }

    @Override
    public void duplicate(Solver solver, THashMap<Object, Object> identitymap) {
        if (!identitymap.containsKey(this)) {
            this.vars[0].duplicate(solver, identitymap);
            IntVar X = (IntVar) identitymap.get(this.vars[0]);
            this.vars[1].duplicate(solver, identitymap);
            IntVar Y = (IntVar) identitymap.get(this.vars[1]);
            this.vars[2].duplicate(solver, identitymap);
            IntVar Z = (IntVar) identitymap.get(this.vars[2]);

            this.absX.duplicate(solver, identitymap);
            IntVar absX = (IntVar) identitymap.get(this.absX);
            this.absY.duplicate(solver, identitymap);
            IntVar absY = (IntVar) identitymap.get(this.absY);
            this.absZ.duplicate(solver, identitymap);
            IntVar absZ = (IntVar) identitymap.get(this.absZ);


            identitymap.put(this, new PropDivXYZ(X, Y, Z, absX, absY, absZ));
        }
    }
}
TOP

Related Classes of solver.constraints.ternary.PropDivXYZ

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.