
org.chocosolver.solver.constraints.reification.PropXeqYHalfReif Maven / Gradle / Ivy
The newest version!
/*
* This file is part of choco-solver, http://choco-solver.org/
*
* Copyright (c) 2025, 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.reification;
import org.chocosolver.sat.Reason;
import org.chocosolver.solver.constraints.Explained;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.constraints.PropagatorPriority;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.events.IntEventType;
import org.chocosolver.util.ESat;
/**
* A propagator dedicated to express b ⇒ x == y
*
*
* @author Charles Prud'homme
* @since 08/02/2024
*/
@Explained
public class PropXeqYHalfReif extends Propagator {
private static final int THRESHOLD = 300;
private final IntVar x;
private final IntVar y;
private final BoolVar b;
public PropXeqYHalfReif(IntVar x, IntVar y, BoolVar b) {
// The priority is set to 'LINEAR' to delay the propagation of this constraint
super(new IntVar[]{x, y, b}, PropagatorPriority.LINEAR, false, false);
this.x = x;
this.y = y;
this.b = b;
}
@Override
public int getPropagationConditions(int vIdx) {
if (vIdx < 2) {
return IntEventType.all();
}
return IntEventType.INCLOW.getMask();
}
@Override
public void propagate(int evtmask) throws ContradictionException {
if (b.isInstantiatedTo(0)) {
// if b is false, then no filtering is required
setPassive();
} else if (b.isInstantiatedTo(1)) {
// if b is true, then x and y must be equal
if (x.isInstantiated()) {
y.instantiateTo(x.getValue(), this,
lcg() ? Reason.r(x.getValLit(), b.getValLit()) : Reason.undef());
setPassive();
} else if (y.isInstantiated()) {
x.instantiateTo(y.getValue(), this,
lcg() ? Reason.r(y.getValLit(), b.getValLit()) : Reason.undef());
setPassive();
} else {
// x and y are not instantiated, but b is true, so x and y must be equal
do {
vars[0].updateLowerBound(vars[1].getLB(), this,
lcg() ? Reason.r(vars[1].getMinLit(), b.getValLit()) : Reason.undef());
} while (vars[1].updateLowerBound(vars[0].getLB(), this,
lcg() ? Reason.r(vars[0].getMinLit(), b.getValLit()) : Reason.undef()));
do {
vars[0].updateUpperBound(vars[1].getUB(), this,
lcg() ? Reason.r(vars[1].getMaxLit(), b.getValLit()) : Reason.undef());
} while (vars[1].updateUpperBound(vars[0].getUB(), this,
lcg() ? Reason.r(vars[0].getMaxLit(), b.getValLit()) : Reason.undef()));
// if x and y support value removal, then remove value from x if not in y and vice versa
if (vars[0].hasEnumeratedDomain() && vars[1].hasEnumeratedDomain()) {
if ((long) vars[0].getDomainSize() + vars[1].getDomainSize() > THRESHOLD) return;
int ub = vars[0].getUB();
for (int val = vars[0].getLB(); val <= ub; val = vars[0].nextValue(val)) {
if (!vars[1].contains(val)) {
vars[0].removeValue(val, this,
lcg() ? Reason.r(vars[1].getLit(val, IntVar.LR_NE), b.getValLit()) : Reason.undef());
}
}
ub = vars[1].getUB();
for (int val = vars[1].getLB(); val <= ub; val = vars[1].nextValue(val)) {
if (!vars[0].contains(val)) {
vars[1].removeValue(val, this,
lcg() ? Reason.r(vars[0].getLit(val, IntVar.LR_NE), b.getValLit()) : Reason.undef());
}
}
}
}
}
// if b is undefined and x and y are different, b must be false
else {
int k = (x.isInstantiated() ? 0b01 : 0b00) + (y.isInstantiated() ? 0b10 : 0b00);
switch (k) {
case 0b11:
if (x.getValue() != y.getValue()) {
b.setToFalse(this, lcg() ? Reason.r(x.getValLit(), y.getValLit()) : Reason.undef());
setPassive();
}
break;
case 0b10:
if (!x.contains(y.getValue())) {
b.setToFalse(this, lcg() ? Reason.r(y.getValLit(), x.getLit(y.getValue(), IntVar.LR_EQ)) : Reason.undef());
setPassive();
}
break;
case 0b01:
if (!y.contains(x.getValue())) {
b.setToFalse(this, lcg() ? Reason.r(x.getValLit(), y.getLit(x.getValue(), IntVar.LR_EQ)) : Reason.undef());
setPassive();
}
break;
case 0b00:
if (x.getLB() > y.getUB()) {
b.setToFalse(this, lcg() ? Reason.r(x.getMinLit(), y.getMaxLit()) : Reason.undef());
setPassive();
} else if (x.getUB() < y.getLB()) {
b.setToFalse(this, lcg() ? Reason.r(x.getMaxLit(), y.getMinLit()) : Reason.undef());
setPassive();
}
if ((long) vars[0].getDomainSize() + vars[1].getDomainSize() > THRESHOLD) return;
// check if x and y have a common value
int smallest = vars[0].getDomainSize() < vars[1].getDomainSize() ? 0 : 1;
int other = 1 - smallest;
int ub = vars[smallest].getUB();
for (int val = vars[smallest].getLB(); val <= ub; val = vars[smallest].nextValue(val)) {
if (vars[other].contains(val)) {
return;
}
}
// if no common value, then b must be false
b.setToFalse(this, lcg() ? Propagator.reason(b, x, y) : Reason.undef());
default:
break;
}
}
}
@Override
public ESat isEntailed() {
if (isCompletelyInstantiated()) {
if (b.isInstantiatedTo(1)) {
return ESat.eval(x.getValue() == y.getValue());
}
return ESat.TRUE;
}
return ESat.UNDEFINED;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy