org.chocosolver.solver.variables.impl.BoolVarImpl 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.
/*
* 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.impl;
import org.chocosolver.memory.structure.IOperation;
import org.chocosolver.solver.ICause;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.delta.IEnumDelta;
import org.chocosolver.solver.variables.delta.IIntDeltaMonitor;
import org.chocosolver.solver.variables.delta.NoDelta;
import org.chocosolver.solver.variables.delta.OneValueDelta;
import org.chocosolver.solver.variables.delta.monitor.OneValueDeltaMonitor;
import org.chocosolver.solver.variables.events.IntEventType;
import org.chocosolver.solver.variables.impl.scheduler.BoolEvtScheduler;
import org.chocosolver.solver.variables.impl.siglit.SignedLiteral;
import org.chocosolver.util.ESat;
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;
/**
*
*
* @author Charles Prud'homme
* @since 18 nov. 2010
*/
public class BoolVarImpl extends AbstractVariable implements BoolVar {
/**
* indicate the value of the domain : false = 0, true = 1, undef = 2
*/
private int mValue;
/**
* To roll back mValue to its initial value
*/
private final IOperation status = () -> mValue = kUNDEF;
/**
* To iterate over removed values
*/
private IEnumDelta delta = NoDelta.singleton;
/**
* To iterate over values in the domain
*/
private DisposableValueIterator _viterator;
/**
* To iterate over ranges
*/
private DisposableRangeIterator _riterator;
/**
* Value iterator allowing for(int i:this) loops
*/
private IntVarValueIterator _javaIterator;
/**
* Set to true if this variable reacts is associated with at least one propagator which reacts
* on value removal
*/
private boolean reactOnRemoval = false;
/**
* Associate boolean variable expressing not(this)
*/
private BoolVar not;
/**
* For boolean expression purpose
*/
private boolean isNot = false;
/**
* Signed Literal
*/
private SignedLiteral.Boolean literal = null;
/**
* Create a BoolVar {0,1} or {true, false}
*
* @param name name of the variable
* @param model declaring model
*/
public BoolVarImpl(String name, Model model) {
super(name, model);
mValue = kUNDEF;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Removes {@code value}from the domain of {@code this}. The instruction comes from {@code propagator}.
*
* - If {@code value} is out of the domain, nothing is done and the return value is {@code false},
* - if removing {@code value} leads to a dead-end (domain wipe-out),
* a {@code ContradictionException} is thrown,
* - otherwise, if removing {@code 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 {@code 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
*/
@Override
public boolean removeValue(int value, ICause cause) throws ContradictionException {
assert cause != null;
if (value == kFALSE)
return instantiateTo(kTRUE, cause);
else if (value == kTRUE)
return instantiateTo(kFALSE, cause);
return false;
}
@Override
public boolean removeValues(IntIterableSet values, ICause cause) throws ContradictionException {
boolean hasChanged = false;
if (values.contains(kFALSE)) {
hasChanged = instantiateTo(kTRUE, cause);
}
if (values.contains(kTRUE)) {
hasChanged = instantiateTo(kFALSE, cause);
}
return hasChanged;
}
@Override
public boolean removeAllValuesBut(IntIterableSet values, ICause cause) throws ContradictionException {
boolean hasChanged = false;
if (!values.contains(kFALSE)) {
hasChanged = instantiateTo(kTRUE, cause);
}
if (!values.contains(kTRUE)) {
hasChanged = instantiateTo(kFALSE, cause);
}
return hasChanged;
}
@Override
public boolean removeInterval(int from, int to, ICause cause) throws ContradictionException {
boolean hasChanged = false;
if (from <= to && from <= 1 && to >= 0) {
if (from == kTRUE) {
hasChanged = instantiateTo(kFALSE, cause);
} else if (to == kFALSE) {
hasChanged = instantiateTo(kTRUE, cause);
} else {
model.getSolver().getEventObserver().instantiateTo(this, 2, cause, kFALSE, kTRUE);
this.contradiction(cause, MSG_UNKNOWN);
}
}
return hasChanged;
}
/**
* Instantiates the domain of {@code this} to {@code value}. The instruction comes from {@code propagator}.
*
* - If the domain of {@code this} is already instantiated to {@code value},
* nothing is done and the return value is {@code false},
* - If the domain of {@code this} is already instantiated to another value,
* then a {@code ContradictionException} is thrown,
* - Otherwise, the domain of {@code this} is restricted to {@code value} and the observers are notified
* and the return value is {@code 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
*/
@Override
public boolean instantiateTo(int value, ICause cause) throws ContradictionException {
// BEWARE: THIS CODE SHOULD NOT BE MOVED TO THE DOMAIN TO NOT DECREASE PERFORMANCES!
assert cause != null;
if ((mValue < kUNDEF && mValue != value) || (value < kFALSE || value > kTRUE)) {
model.getSolver().getEventObserver().instantiateTo(this, value, cause, getLB(), getUB());
this.contradiction(cause, mValue < kUNDEF ? MSG_INST : (value > kTRUE ? MSG_LOW : MSG_UPP));
} else if (mValue == kUNDEF) {
IntEventType e = IntEventType.INSTANTIATE;
this.getModel().getEnvironment().save(status);
if (reactOnRemoval) {
delta.add(kTRUE - value, cause);
}
mValue = value;
model.getSolver().getEventObserver().instantiateTo(this, value, cause, kFALSE, kTRUE);
this.notifyPropagators(e, cause);
return true;
}
return false;
}
/**
* Updates the lower bound of the domain of {@code this} to {@code value}.
* The instruction comes from {@code propagator}.
*
* - If {@code value} is smaller than the lower bound of the domain, nothing is done and the return value is {@code false},
* - if updating the lower bound to {@code value} leads to a dead-end (domain wipe-out),
* a {@code ContradictionException} is thrown,
* - otherwise, if updating the lower bound to {@code 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 {@code 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
*/
@Override
public boolean updateLowerBound(int value, ICause cause) throws ContradictionException {
assert cause != null;
return value > kFALSE && instantiateTo(value, cause);
}
/**
* Updates the upper bound of the domain of {@code this} to {@code value}.
* The instruction comes from {@code propagator}.
*
* - If {@code value} is greater than the upper bound of the domain, nothing is done and the return value is {@code false},
* - if updating the upper bound to {@code value} leads to a dead-end (domain wipe-out),
* a {@code ContradictionException} is thrown,
* - otherwise, if updating the upper bound to {@code 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 {@code 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
*/
@Override
public boolean updateUpperBound(int value, ICause cause) throws ContradictionException {
assert cause != null;
return value < kTRUE && instantiateTo(value, cause);
}
@Override
public boolean updateBounds(int lb, int ub, ICause cause) throws ContradictionException {
boolean hasChanged = false;
if (lb > kTRUE || ub < kFALSE) {
model.getSolver().getEventObserver().instantiateTo(this, 2, cause, kFALSE, kTRUE);
this.contradiction(cause, lb > kTRUE ? MSG_LOW : MSG_UPP);
} else {
if (lb == kTRUE) {
hasChanged = instantiateTo(kTRUE, cause);
}
if (ub == kFALSE) {
hasChanged = instantiateTo(kFALSE, cause);
}
}
return hasChanged;
}
@Override
public boolean isInstantiated() {
return mValue < kUNDEF;
}
@Override
public boolean isInstantiatedTo(int aValue) {
return isInstantiated() && mValue == aValue;
}
@Override
public boolean contains(int aValue) {
if (isInstantiated()) {
return mValue == aValue;
}
return aValue == kFALSE || aValue == kTRUE;
}
@Override
public int getValue() throws IllegalStateException{
if (!isInstantiated()) {
throw new IllegalStateException("getValue() can be only called on instantiated variable. " +
name + " is not instantiated");
}
return getLB();
}
@Override
public ESat getBooleanValue() {
if (isInstantiated()) {
return ESat.eval(getLB() != kFALSE);
}
return ESat.UNDEFINED;
}
/**
* Retrieves the lower bound of the variable
*
* @return the lower bound
*/
@Override
public int getLB() {
if (isInstantiated()) {
return mValue;
}
return kFALSE;
}
/**
* Retrieves the upper bound of the variable
*
* @return the upper bound
*/
@Override
public int getUB() {
if (isInstantiated()) {
return mValue;
}
return kTRUE;
}
@Override
public int getDomainSize() {
return (isInstantiated() ? 1 : 2);
}
@Override
public int getRange() {
return getDomainSize();
}
@Override
public int nextValue(int v) {
if (isInstantiated()) {
final int val = mValue;
return (val > v) ? val : Integer.MAX_VALUE;
} else {
if (v < kFALSE) return kFALSE;
if (v == kFALSE) return kTRUE;
return Integer.MAX_VALUE;
}
}
@Override
public int nextValueOut(int v) {
int lb = 0, ub = 1;
if (isInstantiated()) { // if this is instantiated
lb = ub = mValue;
}
if (lb - 1 <= v && v <= ub) {
return ub + 1;
} else {
return v + 1;
}
}
@Override
public int previousValue(int v) {
if (v > getUB()) return getUB();
if (v > getLB()) return getLB();
return Integer.MIN_VALUE;
}
@Override
public int previousValueOut(int v) {
int lb = 0, ub = 1;
if (isInstantiated()) { // if this is instantiated
lb = ub = mValue;
}
if (lb <= v && v <= ub + 1) {
return lb - 1;
} else {
return v - 1;
}
}
@Override
public boolean hasEnumeratedDomain() {
return true;
}
@Override
public IEnumDelta getDelta() {
return delta;
}
@Override
public String toString() {
if (isInstantiated()) {
return this.name + " = " + mValue;
} else {
return this.name + " = " + "[0,1]";
}
}
////////////////////////////////////////////////////////////////
///// methode liees au fait qu'une variable est observable /////
////////////////////////////////////////////////////////////////
@Override
public void createDelta() {
if (!reactOnRemoval) {
delta = new OneValueDelta(model.getEnvironment());
reactOnRemoval = true;
}
}
@Override
public IIntDeltaMonitor monitorDelta(ICause propagator) {
createDelta();
return new OneValueDeltaMonitor(delta, propagator);
}
@Override
public int getTypeAndKind() {
return VAR | BOOL;
}
@Override
protected EvtScheduler> createScheduler() {
return new BoolEvtScheduler();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@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() {
if (_javaIterator == null) {
_javaIterator = new IntVarValueIterator(this);
}
_javaIterator.reset();
return _javaIterator;
}
@Override
public void _setNot(BoolVar neg) {
this.not = neg;
}
@Override
public BoolVar not() {
if (!hasNot()) {
not = model.boolNotView(this);
not._setNot(this);
}
return not;
}
@Override
public boolean hasNot() {
return not != null;
}
@Override
public boolean isLit() {
return true;
}
@Override
public boolean isNot() {
return isNot;
}
@Override
public void setNot(boolean isNot) {
this.isNot = isNot;
}
@Override
public void createLit(IntIterableRangeSet rootDomain) {
if (this.literal != null) {
throw new IllegalStateException("createLit(Implications) called twice");
}
this.literal = new SignedLiteral.Boolean();
}
@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;
}
}