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

org.chocosolver.solver.constraints.nary.sum.PropSum Maven / Gradle / Ivy

There is a newer version: 4.10.16
Show newest version
/**
 * Copyright (c) 2016, 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:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the .
 * 4. Neither the name of the  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  ''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  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 org.chocosolver.solver.constraints.nary.sum;

import org.chocosolver.solver.constraints.Operator;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.constraints.PropagatorPriority;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.explanations.RuleStore;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.events.IEventType;
import org.chocosolver.solver.variables.events.IntEventType;
import org.chocosolver.util.ESat;

/**
 * A propagator for SUM(x_i) o b
 * 
* Based on "Bounds Consistency Techniques for Long Linear Constraint"
* W. Harvey and J. Schimpf *

* * @author Charles Prud'homme * @since 18/03/11 */ public class PropSum extends Propagator { /** * The position of the last positive coefficient */ protected final int pos; /** * Number of variables */ protected final int l; /** * Bound to respect */ protected final int b; /** * Variability of each variable (ie domain amplitude) */ protected final int[] I; /** * Stores the maximal variability */ protected int maxI; /** * SUm of lower bounds */ protected int sumLB; /** * Sum of upper bounds */ protected int sumUB; /** * The operator among EQ, LE, GE and NE */ protected final Operator o; /** * Creates a sum propagator: SUM(x_i) o b * Coefficients are induced by pos: * those before pos (included) are equal to 1, * the other ones are equal to -1. * * @param variables list of integer variables * @param pos position of the last positive coefficient * @param o operator amng EQ, LE, GE and NE * @param b bound to respect */ public PropSum(IntVar[] variables, int pos, Operator o, int b) { this(variables, pos, o, b, computePriority(variables.length), false); } PropSum(IntVar[] variables, int pos, Operator o, int b, PropagatorPriority priority, boolean reactOnFineEvent){ super(variables, priority, reactOnFineEvent); this.pos = pos; this.o = o; this.b = b; l = variables.length; I = new int[l]; maxI = 0; } /** * Compute the priority of the propagator wrt the number of involved variables * @param nbvars number of variables * @return the priority */ protected static PropagatorPriority computePriority(int nbvars) { if (nbvars == 1) { return PropagatorPriority.UNARY; } else if (nbvars == 2) { return PropagatorPriority.BINARY; } else if (nbvars == 3) { return PropagatorPriority.TERNARY; } else { return PropagatorPriority.LINEAR; } } @Override public int getPropagationConditions(int vIdx) { switch (o) { case NQ: return IntEventType.instantiation(); case LE: return IntEventType.combine(IntEventType.INSTANTIATE, vIdx < pos ? IntEventType.INCLOW : IntEventType.DECUPP); case GE: return IntEventType.combine(IntEventType.INSTANTIATE, vIdx < pos ? IntEventType.DECUPP : IntEventType.INCLOW); default: return IntEventType.boundAndInst(); } } /** * Prepare the propagation: compute sumLB, sumUB and I */ protected void prepare() { sumLB = sumUB = 0; int i = 0; int lb, ub; maxI = 0; for (; i < pos; i++) { // first the positive coefficients lb = vars[i].getLB(); ub = vars[i].getUB(); sumLB += lb; sumUB += ub; I[i] = (ub - lb); if(maxI < I[i])maxI = I[i]; } for (; i < l; i++) { // then the negative ones lb = -vars[i].getUB(); ub = -vars[i].getLB(); sumLB += lb; sumUB += ub; I[i] = (ub - lb); if(maxI < I[i])maxI = I[i]; } } @Override public void propagate(int evtmask) throws ContradictionException { filter(); } /** * Execute filtering wrt the operator * @throws ContradictionException if contradiction is detected */ protected void filter() throws ContradictionException { prepare(); switch (o) { case LE: filterOnLeq(); break; case GE: filterOnGeq(); break; case NQ: filterOnNeq(); break; default: filterOnEq(); break; } } /** * Apply filtering when operator is EQ * @throws ContradictionException if contradiction is detected */ protected void filterOnEq() throws ContradictionException { boolean anychange; int F = b - sumLB; int E = sumUB - b; do { anychange = false; if (F < 0 || E < 0) { fails(); } if (maxI > F || maxI > E) { int lb, ub, i = 0; maxI = 0; // positive coefficients first while (i < pos) { if (I[i] - F > 0) { lb = vars[i].getLB(); ub = lb + I[i]; if (vars[i].updateUpperBound(F + lb, this)) { int nub = vars[i].getUB(); E += nub - ub; I[i] = nub - lb; anychange = true; } } if (I[i] + E > 0) { ub = vars[i].getUB(); lb = ub - I[i]; if (vars[i].updateLowerBound(ub - E, this)) { int nlb = vars[i].getLB(); F -= nlb - lb; I[i] = ub - nlb; anychange = true; } } if(maxI < I[i])maxI = I[i]; i++; } // then negative ones while (i < l) { if (I[i] - F > 0) { lb = -vars[i].getUB(); ub = lb + I[i]; if (vars[i].updateLowerBound(-F - lb, this)) { int nub = -vars[i].getLB(); E += nub - ub; I[i] = nub - lb; anychange = true; } } if (I[i] - E > 0) { ub = -vars[i].getLB(); lb = ub - I[i]; if (vars[i].updateUpperBound(-ub + E, this)) { int nlb = -vars[i].getUB(); F -= nlb - lb; I[i] = ub - nlb; anychange = true; } } if(maxI < I[i])maxI = I[i]; i++; } } // useless since true when all variables are instantiated if (F <= 0 && E <= 0) { this.setPassive(); return; } }while (anychange) ; } /** * Apply filtering when operator is LE * @throws ContradictionException if contradiction is detected */ protected void filterOnLeq() throws ContradictionException { int F = b - sumLB; int E = sumUB - b; if (F < 0) { fails(); } if (maxI > F) { maxI = 0; int lb, ub, i = 0; // positive coefficients first while (i < pos) { if (I[i] - F > 0) { lb = vars[i].getLB(); ub = lb + I[i]; if (vars[i].updateUpperBound(F + lb, this)) { int nub = vars[i].getUB(); E += nub - ub; I[i] = nub - lb; } } if(maxI < I[i])maxI = I[i]; i++; } // then negative ones while (i < l) { if (I[i] - F > 0) { lb = -vars[i].getUB(); ub = lb + I[i]; if (vars[i].updateLowerBound(-F - lb, this)) { int nub = -vars[i].getLB(); E += nub - ub; I[i] = nub - lb; } } if(maxI < I[i])maxI = I[i]; i++; } } if (E <= 0) { this.setPassive(); } } /** * Apply filtering when operator is GE * @throws ContradictionException if contradiction is detected */ protected void filterOnGeq() throws ContradictionException { int F = b - sumLB; int E = sumUB - b; if (E < 0) { fails(); } if(maxI > E) { maxI = 0; int lb, ub, i = 0; // positive coefficients first while (i < pos) { if (I[i] - E > 0) { ub = vars[i].getUB(); lb = ub - I[i]; if (vars[i].updateLowerBound(ub - E, this)) { int nlb = vars[i].getLB(); F -= nlb - lb; I[i] = ub - nlb; } } if(maxI < I[i])maxI = I[i]; i++; } // then negative ones while (i < l) { if (I[i] - E > 0) { ub = -vars[i].getLB(); lb = ub - I[i]; if (vars[i].updateUpperBound(-ub + E, this)) { int nlb = -vars[i].getUB(); F -= nlb - lb; I[i] = ub - nlb; } } if(maxI < I[i])maxI = I[i]; i++; } } if (F <= 0) { this.setPassive(); } } /** * Apply filtering when operator is NE * @throws ContradictionException if contradiction is detected */ protected void filterOnNeq() throws ContradictionException { int F = b - sumLB; int E = sumUB - b; if (F < 0 || E < 0) { setPassive(); return; } int w = -1; int sum = 0; for (int i = 0; i < l; i++) { if (vars[i].isInstantiated()) { sum += i < pos ? vars[i].getValue() : -vars[i].getValue(); } else if (w == -1) { w = i; } else return; } if (w == -1) { if (sum == b) { this.fails(); } } else { vars[w].removeValue(w < pos ? b - sum : sum - b, this); } } @Override public ESat isEntailed() { int sumUB = 0, sumLB = 0, i = 0; for (; i < pos; i++) { // first the positive coefficients sumLB += vars[i].getLB(); sumUB += vars[i].getUB(); } for (; i < l; i++) { // then the negative ones sumLB -= vars[i].getUB(); sumUB -= vars[i].getLB(); } return check(sumLB, sumUB); } /** * Whether the current state of the scalar product is entailed * @param sumLB sum of lower bounds * @param sumUB sum of upper bounds * @return the entailment check */ protected ESat check(int sumLB, int sumUB){ switch (o) { case NQ: if (sumUB < b || sumLB > b) { return ESat.TRUE; } if (sumUB == b && sumLB == b) { return ESat.FALSE; } return ESat.UNDEFINED; case LE: if (sumUB <= b) { return ESat.TRUE; } if (sumLB > b) { return ESat.FALSE; } return ESat.UNDEFINED; case GE: if (sumLB >= b) { return ESat.TRUE; } if (sumUB < b) { return ESat.FALSE; } return ESat.UNDEFINED; default: if (sumLB == b && sumUB == b) { return ESat.TRUE; } if (sumUB < b || sumLB > b) { return ESat.FALSE; } return ESat.UNDEFINED; } } @Override public String toString() { StringBuilder linComb = new StringBuilder(20); linComb.append(pos == 0 ? "-" : "").append(vars[0].getName()); int i = 1; for (; i < pos; i++) { linComb.append(" + ").append(vars[i].getName()); } for (; i < l; i++) { linComb.append(" - ").append(vars[i].getName()); } linComb.append(" ").append(o).append(" "); linComb.append(b); return linComb.toString(); } @Override public boolean why(RuleStore ruleStore, IntVar var, IEventType evt, int value) { boolean newrules = ruleStore.addPropagatorActivationRule(this); // 1. find the pos of var in vars boolean ispos; if (pos < (l / 2)) { int i; i = 0; while (i < pos && vars[i] != var) { i++; } ispos = i < pos; } else { int i; i = pos; while (i < l && vars[i] != var) { i++; } ispos = i == l; } // to deal with BoolVar: any event is automatically promoted to INSTANTIATE if (IntEventType.isInstantiate(evt.getMask())) { assert var.isBool() : "BoolVar excepted"; evt = (var.getValue() == 0 ? IntEventType.DECUPP : IntEventType.INCLOW); } if (IntEventType.isInclow(evt.getMask())) { // explain LB int i = 0; for (; i < pos; i++) { // first the positive coefficients if (vars[i] != var) { if (ispos) { newrules |= ruleStore.addUpperBoundRule(vars[i]); } else { newrules |= ruleStore.addLowerBoundRule(vars[i]); } } } for (; i < l; i++) { // then the negative ones if (vars[i] != var) { if (ispos) { newrules |= ruleStore.addLowerBoundRule(vars[i]); } else { newrules |= ruleStore.addUpperBoundRule(vars[i]); } } } } else if (IntEventType.isDecupp(evt.getMask())) { // explain UB int i = 0; for (; i < pos; i++) { // first the positive coefficients if (vars[i] != var) { if (ispos) { newrules |= ruleStore.addLowerBoundRule(vars[i]); } else { newrules |= ruleStore.addUpperBoundRule(vars[i]); } } } for (; i < l; i++) { // then the negative ones if (vars[i] != var) { if (ispos) { newrules |= ruleStore.addUpperBoundRule(vars[i]); } else { newrules |= ruleStore.addLowerBoundRule(vars[i]); } } } } else { for (int i = 0; i < vars.length; i++) { newrules |= ruleStore.addFullDomainRule(vars[i]); } } return newrules; } protected static int nb(Operator co){ switch (co){ case LE: return 1; case GE: return -1; default: return 0; } } protected static Operator nop(Operator co){ switch (co){ case LE: return Operator.GE; case GE: return Operator.LE; default: return Operator.getOpposite(co); } } protected PropSum opposite(){ return new PropSum(vars, pos, nop(o), b + nb(o)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy