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

org.chocosolver.solver.variables.IntVar Maven / Gradle / Ivy

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.variables;

import org.chocosolver.solver.ICause;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.expression.discrete.arithmetic.ArExpression;
import org.chocosolver.solver.learn.ExplanationForSignedClause;
import org.chocosolver.solver.learn.XParameters;
import org.chocosolver.solver.variables.delta.IIntDeltaMonitor;
import org.chocosolver.solver.variables.events.IEventType;
import org.chocosolver.solver.variables.impl.siglit.SignedLiteral;
import org.chocosolver.util.iterators.DisposableRangeIterator;
import org.chocosolver.util.iterators.DisposableValueIterator;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableRangeSet;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableSet;

import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;


/**
 * Interface for integer variables. Provides every required services.
 * The domain is explicitly represented but is not (and should not be) accessible from outside.
 * 
*

* CPRU r544: remove default implementation * * @author Charles Prud'homme * @since 18 nov. 2010 */ public interface IntVar extends ICause, Variable, Iterable, ArExpression { /** * Provide a minimum value for integer variable lower bound. * Do not prevent from underflow, but may avoid it, somehow. */ int MIN_INT_BOUND = Integer.MIN_VALUE / 100; /** * Provide a minimum value for integer variable lower bound. * Do not prevent from overflow, but may avoid it, somehow. */ int MAX_INT_BOUND = Integer.MAX_VALUE / 100; /** * Removes valuefrom the domain of this. The instruction comes from propagator. *

    *
  • If value is out of the domain, nothing is done and the return value is false,
  • *
  • if removing value leads to a dead-end (domain wipe-out), * a ContradictionException is thrown,
  • *
  • otherwise, if removing value from the domain can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param value value to remove from the domain (int) * @param cause removal releaser * @return true if the value has been removed, false otherwise * @throws ContradictionException if the domain become empty due to this action */ boolean removeValue(int value, ICause cause) throws ContradictionException; /** * Removes valuefrom the domain of this. The instruction comes from propagator. *

* This method deals with value as long. * If such a long can be safely cast to an int, this falls back to regular case (int). * Otherwise, it can either trivially do nothing or fail. *

*
    *
  • If value is out of the domain, nothing is done and the return value is false,
  • *
  • if removing value leads to a dead-end (domain wipe-out), * a ContradictionException is thrown,
  • *
  • otherwise, if removing value from the domain can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param value value to remove from the domain (int) * @param cause removal releaser * @return true if the value has been removed, false otherwise * @throws ContradictionException if the domain become empty due to this action */ default boolean removeValue(long value, ICause cause) throws ContradictionException{ if ((int) value != value) { // cannot be cast to an int return false; } else { return removeValue((int) value, cause); } } /** * Removes the value in valuesfrom the domain of this. The instruction comes from propagator. *
    *
  • If all values are out of the domain, nothing is done and the return value is false,
  • *
  • if removing a value leads to a dead-end (domain wipe-out), * a ContradictionException is thrown,
  • *
  • otherwise, if removing the values from the domain can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param values set of ordered values to remove * @param cause removal release * @return true if at least a value has been removed, false otherwise * @throws ContradictionException if the domain become empty due to this action */ default 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 if(hasEnumeratedDomain()){ int value = vlb; while (value <= vub) { hasChanged |= removeValue(value, cause); value = values.nextValue(value); } } return hasChanged; } /** * Removes all values from the domain of this except those in values. The instruction comes from propagator. *
    *
  • If all values are out of the domain, * a ContradictionException is thrown,
  • *
  • if the domain is a subset of values, * nothing is done and the return value is false,
  • *
  • otherwise, if removing all values but values from the domain can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param values set of ordered values to keep in the domain * @param cause removal release * @return true if a at least a value has been removed, false otherwise * @throws ContradictionException if the domain become empty due to this action */ default 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); if(hasEnumeratedDomain()) { 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)) { hasChanged |= removeValue(value, cause); } } } return hasChanged; } /** * Removes values between [from, to] from the domain of this. The instruction comes from propagator. *
    *
  • If union between values and the current domain is empty, nothing is done and the return value is false,
  • *
  • if removing a value leads to a dead-end (domain wipe-out), * a ContradictionException is thrown,
  • *
  • otherwise, if removing at least a value from the domain can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param from lower bound of the interval to remove (int) * @param to upper bound of the interval to remove(int) * @param cause removal releaser * @return true if the value has been removed, false otherwise * @throws ContradictionException if the domain become empty due to this action */ default 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 (hasEnumeratedDomain()) { boolean done = false; for (int v = from; v <= to; v++) { done |= removeValue(v, cause); } return done; } return false; } /** * Instantiates the domain of this to value. The instruction comes from propagator. *
    *
  • If the domain of this is already instantiated to value, * nothing is done and the return value is false,
  • *
  • If the domain of this is already instantiated to another value, * then a ContradictionException is thrown,
  • *
  • Otherwise, the domain of this is restricted to value and the observers are notified * and the return value is true.
  • *
* * @param value instantiation value (int) * @param cause instantiation releaser * @return true if the instantiation is done, false otherwise * @throws ContradictionException if the domain become empty due to this action */ boolean instantiateTo(int value, ICause cause) throws ContradictionException; /** * Instantiates the domain of this to value. The instruction comes from propagator. *

* This method deals with value as long. * If such a long can be safely cast to an int, this falls back to regular case (int). * Otherwise, it can either trivially do nothing or fail. *

*
    *
  • If the domain of this is already instantiated to value, * nothing is done and the return value is false,
  • *
  • If the domain of this is already instantiated to another value, * then a ContradictionException is thrown,
  • *
  • Otherwise, the domain of this is restricted to value and the observers are notified * and the return value is true.
  • *
* * @param value instantiation value (int) * @param cause instantiation releaser * @return true if the instantiation is done, false otherwise * @throws ContradictionException if the domain become empty due to this action */ default boolean instantiateTo(long value, ICause cause) throws ContradictionException{ if ((int) value != value) { // cannot be cast to an int return instantiateTo(value < getLB() ? getLB() - 1 : getUB() + 1, cause); } else { return instantiateTo((int) value, cause); } } /** * Updates the lower bound of the domain of this to value. * The instruction comes from propagator. *
    *
  • If value is smaller than the lower bound of the domain, nothing is done and the return value is false,
  • *
  • if updating the lower bound to value leads to a dead-end (domain wipe-out), * a ContradictionException is thrown,
  • *
  • otherwise, if updating the lower bound to value can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param value new lower bound (included) * @param cause updating releaser * @return true if the lower bound has been updated, false otherwise * @throws ContradictionException if the domain become empty due to this action */ boolean updateLowerBound(int value, ICause cause) throws ContradictionException; /** * Updates the lower bound of the domain of this to value. * The instruction comes from propagator. *

* This method deals with value as long. * If such a long can be safely cast to an int, this falls back to regular case (int). * Otherwise, it can either trivially do nothing or fail. *

*
    *
  • If value is smaller than the lower bound of the domain, nothing is done and the return value is false,
  • *
  • if updating the lower bound to value leads to a dead-end (domain wipe-out), * a ContradictionException is thrown,
  • *
  • otherwise, if updating the lower bound to value can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param value new lower bound (included) * @param cause updating releaser * @return true if the lower bound has been updated, false otherwise * @throws ContradictionException if the domain become empty due to this action */ default boolean updateLowerBound(long value, ICause cause) throws ContradictionException { if ((int) value != value) { // cannot be cast to an int if (value < getLB()) { return false; } else { // then value >> getLB, this fails return updateLowerBound(getUB() + 1, cause); } } else { return updateLowerBound((int) value, cause); } } /** * Updates the upper bound of the domain of this to value. * The instruction comes from propagator. *
    *
  • If value is greater than the upper bound of the domain, nothing is done and the return value is false,
  • *
  • if updating the upper bound to value leads to a dead-end (domain wipe-out), * a ContradictionException is thrown,
  • *
  • otherwise, if updating the upper bound to value can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param value new upper bound (included) * @param cause update releaser * @return true if the upper bound has been updated, false otherwise * @throws ContradictionException if the domain become empty due to this action */ boolean updateUpperBound(int value, ICause cause) throws ContradictionException; /** * Updates the upper bound of the domain of this to value. * The instruction comes from propagator. *

* This method deals with value as long. * If such a long can be safely cast to an int, this falls back to regular case (int). * Otherwise, it can either trivially do nothing or fail. *

*
    *
  • If value is greater than the upper bound of the domain, nothing is done and the return value is false,
  • *
  • if updating the upper bound to value leads to a dead-end (domain wipe-out), * a ContradictionException is thrown,
  • *
  • otherwise, if updating the upper bound to value can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param value new upper bound (included) * @param cause update releaser * @return true if the upper bound has been updated, false otherwise * @throws ContradictionException if the domain become empty due to this action */ default boolean updateUpperBound(long value, ICause cause) throws ContradictionException { if ((int) value != value) { // cannot be cast to an int if (value > getUB()) { return false; } else { // then value << getUB, this fails return updateUpperBound(getLB() - 1, cause); } } else { return updateUpperBound((int) value, cause); } } /** * Updates the lower bound and the upper bound of the domain of this to, resp. lb and ub. * The instruction comes from propagator. *

*

    *
  • If lb is smaller than the lower bound of the domain * and ub is greater than the upper bound of the domain, *

    * nothing is done and the return value is false,

  • *
  • if updating the lower bound to lb, or updating the upper bound to ub leads to a dead-end (domain wipe-out), * or if lb is strictly greater than ub, * a ContradictionException is thrown,
  • *
  • otherwise, if updating the lower bound to lb and/or the upper bound to ub * can be done safely can be done safely, * the event type is created (the original event can be promoted) and observers are notified * and the return value is true
  • *
* * @param lb new lower bound (included) * @param ub new upper bound (included) * @param cause update releaser * @return true if the upper bound has been updated, false otherwise * @throws ContradictionException if the domain become empty due to this action */ default boolean updateBounds(int lb, int ub, ICause cause) throws ContradictionException{ return updateLowerBound(lb, cause) | updateUpperBound(ub, cause); } /** * Checks if a value v belongs to the domain of this * * @param value int * @return true if the value belongs to the domain of this, false otherwise. */ boolean contains(int value); /** * Checks wether this is instantiated to val * * @param value int * @return true if this is instantiated to val, false otherwise */ boolean isInstantiatedTo(int value); /** * Retrieves the current value of the variable if instantiated * * @return the current value * @throws IllegalStateException when the variable is not instantiated */ int getValue() throws IllegalStateException; /** * Retrieves the lower bound of the variable * * @return the lower bound */ int getLB(); /** * Retrieves the upper bound of the variable * * @return the upper bound */ int getUB(); /** * Returns the range of this domain, that is, the difference between the upper bound and the lower bound. * * @return the range of this domain */ int getRange(); /** * Returns the first value just after v in this which is in the domain. * If no such value exists, returns Integer.MAX_VALUE; *

* To iterate over the values in a IntVar, * use the following loop: *

*

     * int ub = iv.getUB();
     * for (int i = iv.getLB(); i <= ub; i = iv.nextValue(i)) {
     *     // operate on value i here
     * }
* * @param v the value to start checking (exclusive) * @return the next value in the domain */ int nextValue(int v); /** * Returns the first value just after v in this which is out of the domain. * If v is less than or equal to {@link #getLB()}-2, returns v + 1, * if v is greater than or equal to {@link #getUB()}, returns v + 1. * * @param v the value to start checking (exclusive) * @return the next value out of the domain */ int nextValueOut(int v); /** * Returns the previous value just before v in this. * If no such value exists, returns Integer.MIN_VALUE; *

* To iterate over the values in a IntVar, * use the following loop: *

*

     * int lb = iv.getLB();
     * for (int i = iv.getUB(); i >= lb; i = iv.previousValue(i)) {
     *     // operate on value i here
     * }
* * @param v the value to start checking (exclusive) * @return the previous value in the domain */ int previousValue(int v); /** * Returns the first value just before v in this which is out of the domain. * If v is greater than or equal to {@link #getUB()}+2, returns v - 1, * if v is less than or equal to {@link #getLB()}, returns v - 1. * * @param v the value to start checking (exclusive) * @return the previous value out of the domain */ int previousValueOut(int v); /** * Retrieves an iterator over values of this. *

* The values can be iterated in a bottom-up way or top-down way. *

* To bottom-up iterate over the values in a IntVar, * use the following loop: *

*

     * DisposableValueIterator vit = var.getValueIterator(true);
     * while(vit.hasNext()){
     *     int v = vit.next();
     *     // operate on value v here
     * }
     * vit.dispose();
*

*

* To top-down iterate over the values in a IntVar, * use the following loop: * *

     * DisposableValueIterator vit = var.getValueIterator(false);
     * while(vit.hasPrevious()){
     *     int v = vit.previous();
     *     // operate on value v here
     * }
     * vit.dispose();
* * Using both previous and next can lead to unexpected behaviour. * * @param bottomUp way to iterate over values. true means from lower bound to upper bound, * false means from upper bound to lower bound. * @return a disposable iterator over values of this. */ DisposableValueIterator getValueIterator(boolean bottomUp); /** * Retrieves an iterator over ranges (or intervals) of this. *

* The ranges can be iterated in a bottom-up way or top-down way. *

* To bottom-up iterate over the values in a IntVar, * use the following loop: *

*

     * DisposableRangeIterator rit = var.getRangeIterator(true);
     * while (rit.hasNext()) {
     *     int from = rit.min();
     *     int to = rit.max();
     *     // operate on range [from,to] here
     *     rit.next();
     * }
     * rit.dispose();
*

* To top-down iterate over the values in a IntVar, * use the following loop: * *

     * DisposableRangeIterator rit = var.getRangeIterator(false);
     * while (rit.hasPrevious()) {
     *     int from = rit.min();
     *     int to = rit.max();
     *     // operate on range [from,to] here
     *     rit.previous();
     * }
     * rit.dispose();
* * Using both previous and next can lead to unexpected behaviour. * * @param bottomUp way to iterate over ranges. true means from lower bound to upper bound, * false means from upper bound to lower bound. * @return a disposable iterator over ranges of this. */ DisposableRangeIterator getRangeIterator(boolean bottomUp); /** * Indicates wether (or not) this has an enumerated domain (represented in extension) * or not (only bounds) * * @return true if the domain is enumerated, false otherwise. */ boolean hasEnumeratedDomain(); /** * Allow to monitor removed values of this. * * @param propagator the cause that requires to monitor delta * @return a delta monitor */ IIntDeltaMonitor monitorDelta(ICause propagator); /** * @return true iff the variable has a binary domain */ boolean isBool(); @Override default void forEachIntVar(Consumer action) { action.accept(this); } /** * @param evt original event * @return transforms the original event wrt this IntVar */ default IEventType transformEvent(IEventType evt) { return evt; } @Override default IntVar intVar() { return this; } @Override default int getNoChild() { return 1; } @Override default boolean isExpressionLeaf() { return true; } /** * Create the signed literal. * @param rootDomain the domain at root node */ void createLit(IntIterableRangeSet rootDomain); /** * @return the current signed literal * @implSpec a call to {@link #createLit(IntIterableRangeSet)} is required */ SignedLiteral getLit(); /** * Flush the current signed literal */ default void flushLit() { this.getLit().clear(); } /** * Perform the union of this internal signed literal and {@code set}: *

{@code lit} = {@code set} ∪ {@code lit} * * @param set set of ints to join this signed literal with * @param explanation the explanation * @implNote {@code set} is considered as read-only and is not intended to modified. * Before this methods ends, {@code set} is recycled and must not be used. * @apiNote This method is supposed to be called on non-pivot variables only. * It can be called many times on the same variable while explaning a cause * since it applies a union operation on signed literal. */ default void unionLit(IntIterableRangeSet set, ExplanationForSignedClause explanation) { if (XParameters.FINE_PROOF) { System.out.printf("%s: %s ∪ %s\n", getName(), getLit(), set); } this.getLit().addAll(set); if (this.getLit().isEmpty()) { if (XParameters.FINE_PROOF) { System.out.print("skip\n"); } } else { if (XParameters.FINE_PROOF) { System.out.print("\n"); } explanation.addLit(this); } explanation.returnSet(set); } /** * Perform the union of this internal signed literal and the range [{@code l}, {@code u}]: *

{@code lit} = [{@code l}, {@code u}] ∪ {@code lit} * * @param l inclusive lower bound * @param u inclusive upper bound * @param explanation the explanation * @apiNote This method is supposed to be called on non-pivot variables only. * It can be called many times on the same variable while explaning a cause * since it applies a union operation on signed literal. */ default void unionLit(int l, int u, ExplanationForSignedClause explanation) { if (XParameters.FINE_PROOF) { System.out.printf("%s: %s ∪ [%d, %d]", getName(), getLit(), l, u); } this.getLit().addBetween(l, u); if (this.getLit().isEmpty()) { if (XParameters.FINE_PROOF) { System.out.print("-- skip\n"); } } else { if (XParameters.FINE_PROOF) { System.out.print("\n"); } explanation.addLit(this); } } /** * Perform the union of this internal signed literal and {{@code v}}: *

{@code lit} = {{@code v}} ∪ {@code lit} * * @param v int value * @param explanation the explanation * @apiNote This method is supposed to be called on non-pivot variables only. * It can be called many times on the same variable while explaning a cause * since it applies a union operation on signed literal. */ default void unionLit(int v, ExplanationForSignedClause explanation) { if (XParameters.FINE_PROOF) { System.out.printf("%s: %s ∪ {%d}", getName(), getLit(), v); } this.getLit().add(v); if (this.getLit().isEmpty()) { if (XParameters.FINE_PROOF) { System.out.print("skip\n"); } } else { if (XParameters.FINE_PROOF) { System.out.print("\n"); } explanation.addLit(this); } } /** * Perform the intersection of this internal signed literal and {@code set}: *

{@code lit} = {@code set} ∩ {@code lit} * * @param set set of ints to cross this signed literal with. * @param explanation the explanation * @implNote {@code set} is considered as read-only and is not intended to modified. * Before this methods ends, {@code set} is recycled and must no be used. * @apiNote This method is supposed to be called on pivot variables only. * It can be called only once on the pivot variable while explaining a cause * since it applies an intersection operation on signed literal. */ default void intersectLit(IntIterableRangeSet set, ExplanationForSignedClause explanation) { if (explanation.contains(this)) { if (XParameters.FINE_PROOF) { System.out.printf("%s: %s ∩ %s", getName(), getLit(), set); } this.getLit().retainAll(set); if (this.getLit().isEmpty()) { if (XParameters.FINE_PROOF) { System.out.print(" -- remove"); } explanation.removeLit(this); } if (XParameters.FINE_PROOF) { System.out.print("\n"); } explanation.returnSet(set); } else { // this is the first occurrence of the variable during explanation this.unionLit(set, explanation); } } /** * Perform the intersection of this internal signed literal and the range [{@code l}, {@code u}]: *

{@code lit} = [{@code l}, {@code u}] ∩ {@code lit} * * @param l inclusive lower bound * @param u inclusive upper bound * @param explanation the explanation * @apiNote This method is supposed to be called on pivot variables only. * It can be called only once on the pivot variable while explaining a cause * since it applies an intersection operation on signed literal. */ default void intersectLit(int l, int u, ExplanationForSignedClause explanation) { if (explanation.contains(this)) { if (XParameters.FINE_PROOF) { System.out.printf("%s: %s ∩ [%d,%d]", getName(), getLit(), l, u); } this.getLit().retainBetween(l, u); if (this.getLit().isEmpty()) { if (XParameters.FINE_PROOF) { System.out.print(" -- remove"); } explanation.removeLit(this); } if (XParameters.FINE_PROOF) { System.out.print("\n"); } } else { // this is the first occurrence of the variable during explanation this.unionLit(l, u, explanation); } } /** * Perform the intersection of this internal signed literal and {{@code v}}: *

{@code lit} = {{@code v}} ∩ {@code lit} * * @param v int value * @param explanation the explanation * @apiNote This method is supposed to be called on pivot variables only. * It can be called only once on the pivot variable while explaining a cause * since it applies an intersection operation on signed literal. */ default void intersectLit(int v, ExplanationForSignedClause explanation) { if (explanation.contains(this)) { if (XParameters.FINE_PROOF) { System.out.printf("%s: %s ∩ {%d}", getName(), getLit(), v); } this.getLit().retain(v); if (this.getLit().isEmpty()) { if (XParameters.FINE_PROOF) { System.out.print(" -- remove"); } explanation.removeLit(this); } if (XParameters.FINE_PROOF) { System.out.print("\n"); } } else { // this is the first occurrence of the variable during explanation this.unionLit(v, explanation); } } default IntStream stream() { Spliterators.AbstractIntSpliterator it = new Spliterators.AbstractIntSpliterator(IntVar.this.getDomainSize(), Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL) { final int[] val = {IntVar.this.getLB() - 1}; @Override public boolean tryAdvance(IntConsumer action) { if ((val[0] = IntVar.this.nextValue(val[0])) < Integer.MAX_VALUE) { action.accept(val[0]); return true; } return false; } }; return StreamSupport.intStream(it, false); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy