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

org.chocosolver.solver.variables.view.integer.IntAffineView 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.variables.view.integer;

import org.chocosolver.sat.Reason;
import org.chocosolver.solver.ICause;
import org.chocosolver.solver.constraints.Explained;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.exception.SolverException;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.delta.IIntDeltaMonitor;
import org.chocosolver.solver.variables.delta.NoDelta;
import org.chocosolver.solver.variables.events.IEventType;
import org.chocosolver.solver.variables.events.IntEventType;
import org.chocosolver.solver.variables.impl.scheduler.IntEvtScheduler;
import org.chocosolver.solver.variables.view.IntView;
import org.chocosolver.solver.variables.view.ViewDeltaMonitor;
import org.chocosolver.util.iterators.DisposableRangeIterator;
import org.chocosolver.util.iterators.DisposableValueIterator;
import org.chocosolver.util.iterators.EvtScheduler;

import static org.chocosolver.solver.variables.events.IntEventType.DECUPP;
import static org.chocosolver.solver.variables.events.IntEventType.INCLOW;

/**
 * 
* * @author Charles Prud'homme * @since 05/10/2023 */ @Explained public final class IntAffineView extends IntView { public final boolean p; // positive public final int a; public final int b; /** * y is an affine view of x: y = a*x + b. * * @param var a integer variable * @param a a coefficient * @param b a constant */ public IntAffineView(final I var, int a, int b) { super(a + ".(" + var.getName() + ") + " + b, var); this.p = a >= 0; this.a = Math.abs(a); this.b = b; } @Override public IIntDeltaMonitor monitorDelta(ICause propagator) { var.createDelta(); if (var.getDelta() == NoDelta.singleton) { return IIntDeltaMonitor.Default.NONE; } return new ViewDeltaMonitor(var.monitorDelta(propagator)) { @Override protected int transform(int value) { return (p ? value : -value) * a + b; } }; } @Override public boolean removeValue(int value, ICause cause, Reason reason) throws ContradictionException { assert cause != null; int inf = getLB(); int sup = getUB(); if (inf > value || value > sup) return false; value -= b; if (a > 1) { if ((value % a) != 0) { return false; } value = value / a; } if (!p) { value = -value; } if (var.removeValue(value, this, reason)) {// todo channel?? IntEventType e = IntEventType.REMOVE; if (value == inf) { e = IntEventType.INCLOW; } else if (value == sup) { e = IntEventType.DECUPP; } if (this.isInstantiated()) { e = IntEventType.INSTANTIATE; } this.notifyPropagators(e, cause); return true; } return false; } @Override public boolean instantiateTo(int value, ICause cause, Reason reason) throws ContradictionException { assert cause != null; value -= b; if (a > 1) { if ((value % a) != 0) { if (getModel().getSolver().isLCG()) { getModel().getSolver().getSat().cEnqueue(0, reason); } this.contradiction(this, MSG_INST); } value /= a; } if (!p) { value = -value; } if (var.instantiateTo(value, this, reason)) { // todo channel?? notifyPropagators(IntEventType.INSTANTIATE, cause); return true; } return false; } @Override public boolean updateLowerBound(int value, ICause cause, Reason reason) throws ContradictionException { assert cause != null; int old = this.getLB(); if (old >= value) return false; value--; value -= b; if (a > 1) { value = value / a - (value % a < 0 ? 1 : 0); } boolean change; if (!p) { change = var.updateUpperBound(-value - 1, this, reason); // todo channel?? } else { change = var.updateLowerBound(value + 1, this, reason); // todo channel?? } if (change) { IntEventType e = IntEventType.INCLOW; if (isInstantiated()) { e = IntEventType.INSTANTIATE; } this.notifyPropagators(e, cause); return true; } return false; } @Override public boolean updateUpperBound(int value, ICause cause, Reason reason) throws ContradictionException { assert cause != null; int old = this.getUB(); if (old <= value) return false; value -= b; if (a > 1) { value = value / a - (value % a < 0 ? 1 : 0); } boolean change; if (!p) { change = var.updateLowerBound(-value, this, reason); // todo channel?? } else { change = var.updateUpperBound(value, this, reason); // todo channel?? } if (change) { IntEventType e = IntEventType.DECUPP; if (isInstantiated()) { e = IntEventType.INSTANTIATE; } this.notifyPropagators(e, cause); return true; } return false; } @Override public DisposableValueIterator getValueIterator(boolean bottomUp) { if (_viterator == null || _viterator.isNotReusable()) { _viterator = new DisposableValueIterator() { DisposableValueIterator vit; @Override public void bottomUpInit() { super.bottomUpInit(); vit = var.getValueIterator(p); } @Override public void topDownInit() { super.topDownInit(); vit = var.getValueIterator(!p); } @Override public boolean hasNext() { return p ? vit.hasNext() : vit.hasPrevious(); } @Override public boolean hasPrevious() { return p ? vit.hasPrevious() : vit.hasNext(); } @Override public int next() { return (p ? vit.next() : -vit.previous()) * a + b; } @Override public int previous() { return (p ? vit.previous() : -vit.next()) * a + b; } @Override public void dispose() { super.dispose(); vit.dispose(); } }; } if (bottomUp) { _viterator.bottomUpInit(); } else { _viterator.topDownInit(); } return _viterator; } @Override public DisposableRangeIterator getRangeIterator(boolean bottomUp) { if (_riterator == null || _riterator.isNotReusable()) { if (a == 1) { // range iterator works on var _riterator = new DisposableRangeIterator() { DisposableRangeIterator rit; int min; int max; @Override public void bottomUpInit() { rit = var.getRangeIterator(p); if (p) { min = rit.min() + b; max = rit.max() + b; } else { min = -rit.max() + b; max = -rit.min() + b; } } @Override public void topDownInit() { rit = var.getRangeIterator(!p); if (p) { min = rit.min() + b; max = rit.max() + b; } else { min = -rit.max() + b; max = -rit.min() + b; } } @Override public boolean hasNext() { return p ? rit.hasNext() : rit.hasPrevious(); } @Override public boolean hasPrevious() { return p ? rit.hasPrevious() : rit.hasNext(); } @Override public void next() { if (p) { rit.next(); min = rit.min() + b; max = rit.max() + b; } else { rit.previous(); min = -rit.max() + b; max = -rit.min() + b; } } @Override public void previous() { if (p) { rit.previous(); min = rit.min() + b; max = rit.max() + b; } else { rit.next(); min = -rit.max() + b; max = -rit.min() + b; } } @Override public int min() { return min; } @Override public int max() { return max; } }; } else { // value iterator works on this _riterator = new DisposableRangeIterator() { DisposableValueIterator vit; int min; int max; boolean iterable; @Override public void bottomUpInit() { vit = getValueIterator(true); iterable = vit.hasNext(); min = max = vit.next(); } @Override public void topDownInit() { vit = getValueIterator(false); iterable = vit.hasPrevious(); min = max = vit.previous(); } @Override public boolean hasNext() { boolean isIterable = iterable; iterable = vit.hasNext(); return isIterable; } @Override public boolean hasPrevious() { boolean isIterable = iterable; iterable = vit.hasPrevious(); return isIterable; } @Override public void next() { min = max = vit.next(); } @Override public void previous() { min = max = vit.previous(); } @Override public int min() { return min; } @Override public int max() { return max; } }; } } if (bottomUp) { _riterator.bottomUpInit(); } else { _riterator.topDownInit(); } return _riterator; } @Override public boolean contains(int value) { value -= b; if (a > 1) { if ((value % a) != 0) { return false; } value /= a; } value = (!p) ? -value : value; return var.contains(value); } @Override public boolean isInstantiatedTo(int value) { return var.isInstantiated() && contains(value); } @Override public int getValue() throws IllegalStateException { if (!isInstantiated()) { throw new IllegalStateException("getValue() can be only called on instantiated variable. " + name + " is not instantiated"); } return (p ? var.getValue() : -var.getValue()) * a + b; } @Override public int getLB() { return (!p ? -var.getUB() : var.getLB()) * a + b; } @Override public int getUB() { return (!p ? -var.getLB() : var.getUB()) * a + b; } @Override public int nextValue(int v) { if (v < getLB()) return getLB(); if (v > getUB()) return Integer.MAX_VALUE; // y = p * a * x + b where p in {-1,1}, a and b in Z. // => x = (y - b) / (p * a) v -= b; v = (v < 0 && a > 1 && v % a != 0) ? v - a : v; v /= (p ? a : -a); if (p) { v = var.nextValue(v); } else { v = var.previousValue(v); } if (v == Integer.MIN_VALUE || v == Integer.MAX_VALUE) return Integer.MAX_VALUE; return (p ? v : -v) * a + b; } @Override public int nextValueOut(int v) { v++; if (v < getLB() || v > getUB()) return v; // y = p * a * x + b where p in {-1,1}, a and b in Z. // => x = (y - b) / (p * a) double w = v - b; w /= (p ? a : -a); int k = (int) w; if (w == k && var.contains(k)) { if (a > 1) { v++; } else { if (p) { k = var.nextValueOut(k); } else { k = var.previousValueOut(k); } v = ((p ? k : -k) * a + b); } } return v; } @Override public int previousValue(int v) { if (v > getUB()) return getUB(); if (v < getLB()) return Integer.MIN_VALUE; // y = p * a * x + b where p in {-1,1}, a and b in Z. // => x = (y - b) / (p * a) v -= b; v = (v > 0 && a > 1 && v % a != 0) ? v + a : v; v /= (p ? a : -a); if (p) { v = var.previousValue(v); } else { v = var.nextValue(v); } if (v == Integer.MIN_VALUE || v == Integer.MAX_VALUE) return Integer.MIN_VALUE; return (p ? v : -v) * a + b; } @Override public int previousValueOut(int v) { v--; if (v < getLB() || v > getUB()) return v; // y = p * a * x + b where p in {-1,1}, a and b in Z. // => x = (y - b) / (p * a) double w = v - b; w /= (p ? a : -a); int k = (int) w; if (w == k && var.contains(k)) { if (a > 1) { v--; } else { if (p) { k = var.previousValueOut(k); } else { k = var.nextValueOut(k); } v = ((p ? k : -k) * a + b); } } return v; } @Override protected EvtScheduler createScheduler() { return new IntEvtScheduler(); } @Override public String toString() { return this.getName() + "[" + getLB() + "," + getUB() + "]"; } @Override public IEventType transformEvent(IEventType evt) { if (evt == INCLOW) { if (!p) return DECUPP; } else if (evt == DECUPP) { if (!p) return INCLOW; } return evt; } @Override public boolean hasEnumeratedDomain() { return var.hasEnumeratedDomain() || a > 1; } public boolean equals(IntVar v, int a, int b) { if (!this.var.equals(v)) return false; return this.a == Math.abs(a) && this.b == b && this.p == (a >= 0); } @Override public int getLit(int val, int t) { val -= b; if (a > 1) { int k = val % a; val = val / a; if (k != 0) { if (t == 0) { throw new SolverException("Cannot compute lit from " + this + " and " + val + " and " + t); } if (t == 1) { //return MiniSat.falseLit; throw new SolverException("Check falseLit"); } if (t == 2 && k > 0) { val++; } if (t == 3 && k < 0) { val--; } } } if (!p) { val = -val; if (t >= 2) { assert 5 - t >= 0; return var.getLit(val, 5 - t); } } return var.getLit(val, t); } @Override public int getMinLit() { return p ? var.getMinLit() : var.getMaxLit(); } @Override public int getMaxLit() { return p ? var.getMaxLit() : var.getMinLit(); } @Override public int getValLit() { return var.getValLit(); } }