org.chocosolver.solver.constraints.ternary.PropModXYZ Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of choco-solver Show documentation
Show all versions of choco-solver Show documentation
Open-source constraint solver.
The newest version!
/*
* This file is part of choco-solver, http://choco-solver.org/
*
* Copyright (c) 2024, 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.ternary;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.constraints.PropagatorPriority;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.variables.*;
import org.chocosolver.solver.variables.events.IntEventType;
import org.chocosolver.util.ESat;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableBitSet;
/**
* 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
*
* @author Arthur Godet
* @since 29/03/2019
*/
public class PropModXYZ extends Propagator {
private final IntVar x;
private final IntVar y;
private final IntVar z;
private IntIterableBitSet usedValues;
private final boolean allEnnums;
private static final int THRESHOLD = 10_000;
public PropModXYZ(IntVar x, IntVar y, IntVar z) {
super(new IntVar[]{x, y, z}, PropagatorPriority.TERNARY, false);
this.x = x;
this.y = y;
this.z = z;
this.allEnnums = x.hasEnumeratedDomain() && y.hasEnumeratedDomain() && z.hasEnumeratedDomain();
if (z.hasEnumeratedDomain()) {
usedValues = new IntIterableBitSet();
usedValues.setOffset(z.getLB());
}
}
@Override
public int getPropagationConditions(int vIdx) {
return IntEventType.boundAndInst();
}
@Override
public void propagate(int evtmask) throws ContradictionException {
y.removeValue(0, this);
int zlb, zub;
int xlb = x.getLB();
int xub = x.getUB();
int ylb = y.getLB();
int yub = y.getUB();
if (xlb >= 0) {
zlb = 0;
zub = Math.min(xub, Math.max(Math.abs(y.getLB()), Math.abs(y.getUB())) - 1);
} else if (xub < 0) {
zub = 0;
zlb = Math.max(xlb, -Math.max(Math.abs(y.getLB()), Math.abs(y.getUB())) + 1);
} else {
zlb = Math.max(xlb, Math.min(ylb, Math.min(-ylb, -yub)) + 1);
zub = Math.min(xub, Math.max(yub, Math.max(-ylb, -yub)) - 1);
}
z.updateBounds(zlb, zub, this);
long card = (long) x.getDomainSize() * y.getDomainSize();
if (allEnnums && card <= THRESHOLD) {
propagateEnumerated();
} else {
propagateBounded();
}
}
private void propagateEnumerated() throws ContradictionException {
usedValues.clear();
for (int vx = x.getLB(); vx <= x.getUB(); vx = x.nextValue(vx)) {
boolean toRemove = true;
for (int vy = y.getLB(); vy <= y.getUB(); vy = y.nextValue(vy)) {
if (vy != 0 && z.contains(vx % vy)) {
usedValues.add(vx % vy);
toRemove = false;
}
}
if (toRemove) {
x.removeValue(vx, this);
}
}
z.removeAllValuesBut(usedValues, this);
for (int vy = y.getLB(); vy <= y.getUB(); vy = y.nextValue(vy)) {
if (!containsOneDivid(x, vy, z)) {
y.removeValue(vy, this);
}
}
}
private void propagateBounded() throws ContradictionException {
boolean hasChange = true;
while (hasChange) {
hasChange = false;
// filter bounds for X
while (!containsOneDivid(x.getLB(), y, z)) {
hasChange |= x.updateLowerBound(x.getLB() + 1, this);
}
while (!containsOneDivid(x.getUB(), y, z)) {
hasChange |= x.updateUpperBound(x.getUB() - 1, this);
}
// filter bounds for Z
while (!containsOneDividLB(x, y, z.getLB())) {
hasChange |= z.updateLowerBound(z.getLB() + 1, this);
}
while (!containsOneDividUB(x, y, z.getUB())) {
hasChange |= z.updateUpperBound(z.getUB() - 1, this);
}
// filter bounds for Y
while (y.getLB() == 0 || !containsOneDivid(x, y.getLB(), z)) {
hasChange |= y.updateLowerBound(y.getLB() + 1, this);
}
while (y.getUB() == 0 || !containsOneDivid(x, y.getUB(), z)) {
hasChange |= y.updateUpperBound(y.getUB() - 1, this);
}
}
}
private static boolean containsOneDivid(int v, IntVar Y, IntVar Z) {
for (int vy = Y.getLB(); vy <= Y.getUB(); vy = Y.nextValue(vy)) {
if (vy != 0) {
for (int vz = Z.getLB(); vz <= Z.getUB(); vz = Z.nextValue(vz)) {
if (v % vy == vz) {
return true;
}
}
}
}
return false;
}
private static boolean containsOneDivid(IntVar X, int v, IntVar Z) {
if (v != 0) {
for (int vx = X.getLB(); vx <= X.getUB(); vx = X.nextValue(vx)) {
for (int vz = Z.getLB(); vz <= Z.getUB(); vz = Z.nextValue(vz)) {
if (vx % v == vz) {
return true;
}
}
}
}
return false;
}
private static boolean containsOneDividLB(IntVar X, IntVar Y, int v) {
for (int vy = Y.getLB(); vy <= Y.getUB(); vy = Y.nextValue(vy)) {
if (vy == 0) continue;
for (int vx = X.getLB(); vx <= X.getUB(); vx = X.nextValue(vx)) {
if (vx % vy == v) {
return true;
}
}
}
return false;
}
private static boolean containsOneDividUB(IntVar X, IntVar Y, int v) {
for (int vy = Y.getUB(); vy >= Y.getLB(); vy = Y.previousValue(vy)) {
if (vy == 0) continue;
for (int vx = X.getUB(); vx >= X.getLB(); vx = X.previousValue(vx)) {
if (vx % vy == v) {
return true;
}
}
}
return false;
}
@Override
public ESat isEntailed() {
if (x.isInstantiated() && y.isInstantiated() && z.isInstantiated()) {
return y.getValue() != 0 && x.getValue() % y.getValue() == z.getValue() ? ESat.TRUE : ESat.FALSE;
}
return ESat.UNDEFINED;
}
@Override
public String toString() {
return x.getName() + " % " + y.getName() + " = " + z.getName();
}
}