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

org.chocosolver.solver.variables.view.IntView Maven / Gradle / Ivy

There is a newer version: 4.10.17
Show newest version
/*
 * This file is part of choco-solver, http://choco-solver.org/
 *
 * Copyright (c) 2023, 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;


import org.chocosolver.solver.ICause;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.learn.ExplanationForSignedClause;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.Variable;
import org.chocosolver.solver.variables.delta.IDelta;
import org.chocosolver.solver.variables.delta.IntDelta;
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.siglit.SignedLiteral;
import org.chocosolver.util.iterators.*;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableRangeSet;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableSet;

import java.util.Iterator;
import java.util.function.Consumer;

import static org.chocosolver.util.objects.setDataStructures.iterable.IntIterableSetUtils.unionOf;

/**
 * Abstract class for defining integer views on integer variables
 * 
* "A view implements the same operations as a variable. A view stores a reference to a variable. * Invoking an operation on the view executes the appropriate operation on the view's variable." *

* Based on "Views and Iterators for Generic Constraint Implementations"
* C. Shulte and G. Tack.
* Eleventh International Conference on Principles and Practice of Constraint Programming *
* * @author Charles Prud'homme * @since 18/03/11 */ public abstract class IntView extends AbstractView implements IntVar { /** * Observed variable */ protected final I var; /** * To store removed values */ protected IntDelta delta; /** * Value iterator */ protected DisposableValueIterator _viterator; /** * Range iterator */ protected DisposableRangeIterator _riterator; /** * Value iterator allowing for(int i:this) loops */ private final IntVarValueIterator _javaIterator = new IntVarValueIterator(this); /** * Signed Literal */ protected SignedLiteral literal; /** * Create a view based on {@code var} * @param name name of the view * @param var observed variable */ protected IntView(String name, I var) { super(name, var); this.var = var; this.delta = NoDelta.singleton; } /** * Action to execute on {@link #var} when this view requires to instantiate it * @param value value before modification of the view * @return true if {@link #var} has been modified * @throws ContradictionException if modification fails */ protected boolean doInstantiateVar(int value) throws ContradictionException{ throw new UnsupportedOperationException(); } /** * Action to execute on {@link #var} when this view requires to update its lower bound * @param value value before modification of the view * @return true if {@link #var} has been modified * @throws ContradictionException if modification fails */ protected boolean doUpdateLowerBoundOfVar(int value) throws ContradictionException{ throw new UnsupportedOperationException(); } /** * Action to execute on {@link #var} when this view requires to update its upper bound * @param value value before modification of the view * @return true if {@link #var} has been modified * @throws ContradictionException if modification fails */ protected boolean doUpdateUpperBoundOfVar(int value) throws ContradictionException{ throw new UnsupportedOperationException(); } /** * Action to execute on {@link #var} when this view requires to remove a value from it * @param value value before modification of the view * @return true if {@link #var} has been modified * @throws ContradictionException if modification fails */ protected boolean doRemoveValueFromVar(int value) throws ContradictionException{ throw new UnsupportedOperationException(); } /** * Action to execute on {@link #var} when this view requires to remove an interval from it * @param from first value of the interval before modification of the view * @param to last value of the interval before modification of the view * @return true if {@link #var} has been modified * @throws ContradictionException if modification fails */ protected boolean doRemoveIntervalFromVar(int from, int to) throws ContradictionException{ throw new UnsupportedOperationException(); } @Override public boolean removeValue(int value, ICause cause) throws ContradictionException { assert cause != null; int inf = getLB(); int sup = getUB(); if (inf <= value && value <= sup) { IntEventType e = IntEventType.REMOVE; model.getSolver().getEventObserver().removeValue(this, value, cause); if (doRemoveValueFromVar(value)) { 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; }else{ model.getSolver().getEventObserver().undo(); } } return false; } @Override public boolean removeValues(IntIterableSet values, ICause cause) throws ContradictionException { assert cause != null; boolean hasChanged = false, fixpoint; int vlb, vub; do { int nlb = getLB(); int nub = getUB(); vlb = values.nextValue(nlb - 1); vub = values.previousValue(nub + 1); if (!hasChanged && (vlb > nub || vub < nlb)) { return false; } if (vlb == nlb) { // look for the new lb do { nlb = nextValue(nlb); vlb = values.nextValue(nlb - 1); } while (nlb < Integer.MAX_VALUE && nub < Integer.MAX_VALUE && vlb == nlb); } if (vub == nub) { // look for the new ub do { nub = previousValue(nub); vub = values.previousValue(nub + 1); } while (nlb > Integer.MIN_VALUE && nub > Integer.MIN_VALUE && vub == nub); } // the new bounds are now known, delegate to the right method fixpoint = updateBounds(nlb, nub, cause); hasChanged |= fixpoint; } while (fixpoint); // now deal with holes int value = vlb, to = vub; boolean hasRemoved = false; while (value <= to) { model.getSolver().getEventObserver().removeValue(this, value, cause); if(doRemoveValueFromVar(value)){ hasRemoved = true; }else{ model.getSolver().getEventObserver().undo(); } value = values.nextValue(value); } if (hasRemoved) { IntEventType e = IntEventType.REMOVE; if (var.isInstantiated()) { e = IntEventType.INSTANTIATE; } this.notifyPropagators(e, cause); } return hasRemoved || hasChanged; } @Override public boolean removeInterval(int from, int to, ICause cause) throws ContradictionException { assert cause != null; if (from <= getLB()) { return updateLowerBound(to + 1, cause); } else if (getUB() <= to) { return updateUpperBound(from - 1, cause); } else if(var.hasEnumeratedDomain()){ for (int v = from; v <= to; v++) { if (this.contains(v)) { model.getSolver().getEventObserver().removeValue(this, v, cause); } } boolean done = doRemoveIntervalFromVar(from, to); if (done) { notifyPropagators(IntEventType.REMOVE, cause); }// no else needed, since all values were checked return done; } return false; } @Override public boolean removeAllValuesBut(IntIterableSet values, ICause cause) throws ContradictionException { boolean hasChanged = false, fixpoint; int nlb, nub; do { int clb = getLB(); int cub = getUB(); nlb = values.nextValue(clb - 1); nub = values.previousValue(cub + 1); // the new bounds are now known, delegate to the right method fixpoint = updateBounds(nlb, nub, cause); hasChanged |= fixpoint; } while (fixpoint); // now deal with holes int to = previousValue(nub); boolean hasRemoved = false; int value = nextValue(nlb); // iterate over the values in the domain, remove the ones that are not in values for (; value <= to; value = nextValue(value)) { if (!values.contains(value)) { model.getSolver().getEventObserver().removeValue(this, value, cause); if(doRemoveValueFromVar(value)){ hasRemoved = true; }else{ model.getSolver().getEventObserver().undo(); } } } if (hasRemoved) { IntEventType e = IntEventType.REMOVE; if (isInstantiated()) { e = IntEventType.INSTANTIATE; } this.notifyPropagators(e, cause); } return hasRemoved || hasChanged; } @Override public boolean instantiateTo(int value, ICause cause) throws ContradictionException { assert cause != null; model.getSolver().getEventObserver().instantiateTo(this, value, cause, getLB(), getUB()); boolean done = doInstantiateVar(value); if (done) { notifyPropagators(IntEventType.INSTANTIATE, cause); return true; }else{ model.getSolver().getEventObserver().undo(); } return false; } @Override public boolean updateLowerBound(int value, ICause cause) throws ContradictionException { assert cause != null; int old = this.getLB(); if (old < value) { model.getSolver().getEventObserver().updateLowerBound(this, value, getLB(), cause); IntEventType e = IntEventType.INCLOW; boolean done = doUpdateLowerBoundOfVar(value); if (isInstantiated()) { e = IntEventType.INSTANTIATE; } if (done) { this.notifyPropagators(e, cause); return true; }else{ model.getSolver().getEventObserver().undo(); } } return false; } @Override public boolean updateUpperBound(int value, ICause cause) throws ContradictionException { assert cause != null; int old = this.getUB(); if (old > value) { model.getSolver().getEventObserver().updateUpperBound(this, value, getUB(), cause); IntEventType e = IntEventType.DECUPP; boolean done = doUpdateUpperBoundOfVar(value); if (isInstantiated()) { e = IntEventType.INSTANTIATE; } if (done) { this.notifyPropagators(e, cause); return true; }else{ model.getSolver().getEventObserver().undo(); } } return false; } @Override public boolean updateBounds(int lb, int ub, ICause cause) throws ContradictionException { assert cause != null; int olb = this.getLB(); int oub = this.getUB(); boolean hasChanged = false; if (olb < lb || oub > ub) { IntEventType e = null; if (olb < lb) { model.getSolver().getEventObserver().updateLowerBound(this, lb, getLB(), cause); e = IntEventType.INCLOW; if(doUpdateLowerBoundOfVar(lb)){ hasChanged = true; }else{ model.getSolver().getEventObserver().undo(); } } if (oub > ub) { e = e == null ? IntEventType.DECUPP : IntEventType.BOUND; model.getSolver().getEventObserver().updateUpperBound(this, ub, getUB(), cause); if(doUpdateUpperBoundOfVar(ub)){ hasChanged = true; }else{ model.getSolver().getEventObserver().undo(); } } if (isInstantiated()) { e = IntEventType.INSTANTIATE; } if (hasChanged) { this.notifyPropagators(e, cause); } } return hasChanged; } @Override public int getTypeAndKind() { return Variable.VIEW | Variable.INT; } public I getVariable() { return var; } @Override public int getDomainSize() { return var.getDomainSize(); } @Override public int getRange() { return var.getRange(); } @Override public boolean hasEnumeratedDomain() { return var.hasEnumeratedDomain(); } @Override public boolean isInstantiated() { return getVariable().isInstantiated(); } @Override public IDelta getDelta() { return var.getDelta(); } @Override public void createDelta() { var.createDelta(); } @Override public void notify(IEventType event, int variableIdx) throws ContradictionException { super.notifyPropagators(transformEvent(event), this); } @Override public DisposableValueIterator getValueIterator(boolean bottomUp) { if (_viterator == null || _viterator.isNotReusable()) { _viterator = new DisposableValueBoundIterator(this); } if (bottomUp) { _viterator.bottomUpInit(); } else { _viterator.topDownInit(); } return _viterator; } @Override public DisposableRangeIterator getRangeIterator(boolean bottomUp) { if (_riterator == null || _riterator.isNotReusable()) { _riterator = new DisposableRangeBoundIterator(this); } if (bottomUp) { _riterator.bottomUpInit(); } else { _riterator.topDownInit(); } return _riterator; } @Override public Iterator iterator() { _javaIterator.reset(); return _javaIterator; } @Override public void forEachIntVar(Consumer action) { action.accept(var); action.accept(this); } @Override public void createLit(IntIterableRangeSet rootDomain) { if (this.literal != null) { throw new IllegalStateException("createLit(Implications) called twice"); } this.literal = new SignedLiteral.Set(rootDomain); } @Override public SignedLiteral getLit() { if (this.literal == null) { throw new NullPointerException("getLit() called on null, a call to createLit(Implications) is required"); } return this.literal; } @Override public void explain(int p, ExplanationForSignedClause explanation) { IntVar pivot = explanation.readVar(p); IntVar other = (this == pivot ? getVariable() : this); IntIterableRangeSet dom = explanation.complement(other); other.unionLit(dom, explanation); dom = explanation.complement(pivot); unionOf(dom, explanation.readDom(p)); pivot.intersectLit(dom, explanation); } }