org.chocosolver.solver.variables.impl.SetVarImpl 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 gnu.trove.set.hash.TIntHashSet;
import org.chocosolver.solver.ICause;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.constraints.Constraint;
import org.chocosolver.solver.constraints.ConstraintsName;
import org.chocosolver.solver.constraints.set.PropCardinality;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.SetVar;
import org.chocosolver.solver.variables.delta.ISetDeltaMonitor;
import org.chocosolver.solver.variables.delta.SetDelta;
import org.chocosolver.solver.variables.delta.monitor.SetDeltaMonitor;
import org.chocosolver.solver.variables.events.SetEventType;
import org.chocosolver.solver.variables.impl.scheduler.SetEvtScheduler;
import org.chocosolver.util.iterators.EvtScheduler;
import org.chocosolver.util.objects.setDataStructures.ISet;
import org.chocosolver.util.objects.setDataStructures.SetFactory;
import org.chocosolver.util.objects.setDataStructures.SetType;
import org.chocosolver.util.objects.setDataStructures.Set_ReadOnly;
/**
* Set variable to represent a set of integers, i.e. a value is a set
*
* @author Jean-Guillaume Fages
* @since Oct 2012
*/
public class SetVarImpl extends AbstractVariable implements SetVar {
//***********************************************************************************
// VARIABLES
//***********************************************************************************
private final ISet lb, ub, lbReadOnly, ubReadOnly;
private SetDelta delta;
private boolean reactOnModification;
private IntVar cardinality = null;
//***********************************************************************************
// CONSTRUCTORS
//***********************************************************************************
/**
* Creates a Set variable
*
* @param name name of the variable
* @param ker initial kernel domain
* @param kerType data structure of the kernel
* @param env initial envelope domain
* @param envType data structure of the envelope
* @param model solver of the variable.
*/
public SetVarImpl(String name, int[] ker, SetType kerType, int[] env, SetType envType, Model model) {
super(name, model);
ker = new TIntHashSet(ker).toArray();
env = new TIntHashSet(env).toArray();
int offSet = env.length>0?env[0]:0;
for(int i:env){
offSet = Math.min(offSet,i);
}
lb = SetFactory.makeStoredSet(kerType, offSet, model);
ub = SetFactory.makeStoredSet(envType, offSet, model);
lbReadOnly = new Set_ReadOnly(lb);
ubReadOnly = new Set_ReadOnly(ub);
for(int i:env){
ub.add(i);
}
for(int i:ker){
lb.add(i);
if(!ub.contains(i)){
throw new UnsupportedOperationException("Invalid SetVar domain definition : "
+i+" is in the LB but not in the UB.");
}
}
}
/**
* Creates a set variable, of domain [lb, ub]
* Beware : Use this constructor with caution (domain is directly accessible)
* lb and ub should be created properly (e.g. lb subset of ub) and should not be modified externally
*
* Both lb and ub should be backtrackable sets (stored sets): use SetFactory.makeStoredSet to build them
*
* @param name name of the variable
* @param lb lower bound of the set variable (mandatory elements)
* @param ub upper bound of the set variable (potential elements)
* @param model solver of the variable.
*/
public SetVarImpl(String name, ISet lb, ISet ub, Model model) {
super(name, model);
this.lb = lb;
this.ub = ub;
lbReadOnly = new Set_ReadOnly(lb);
ubReadOnly = new Set_ReadOnly(ub);
for(int i:lb){
if(!ub.contains(i)){
throw new UnsupportedOperationException("Invalid SetVar domain definition : "
+i+" is in the LB but not in the UB.");
}
}
}
/**
* Creates a fixed Set variable, equal to value
* Beware : Use this constructor with caution (domain is directly accessible)
* value should be created properly and should not be modified afterward
*
* @param name name of the variable
* @param value value of the set variable
* @param model solver of the variable.
*/
public SetVarImpl(String name, ISet value, Model model) {
super(name, model);
lb = value;
ub = lb;
lbReadOnly = new Set_ReadOnly(lb);
ubReadOnly = new Set_ReadOnly(ub);
}
/**
* Creates a fixed Set variable, equal to value
*
* @param name name of the variable
* @param value value of the set variable
* @param model solver of the variable.
*/
public SetVarImpl(String name, int[] value, Model model) {
super(name, model);
lb = SetFactory.makeConstantSet(new TIntHashSet(value).toArray());
ub = lb;
lbReadOnly = new Set_ReadOnly(lb);
ubReadOnly = new Set_ReadOnly(ub);
}
//***********************************************************************************
// METHODS
//***********************************************************************************
@Override
public boolean isInstantiated() {
return ub.size() == lb.size();
}
@Override
public ISet getLB() {
return lbReadOnly;
}
@Override
public ISet getUB() {
return ubReadOnly;
}
@Override
public IntVar getCard() {
if(!hasCard()){
int ubc = ub.size(), lbc = lb.size();
if(ubc==lbc) cardinality = model.intVar(ubc);
else{
cardinality = model.intVar(name+".card", lbc, ubc);
new Constraint(ConstraintsName.SETCARD, new PropCardinality(this, cardinality)).post();
}
}
return cardinality;
}
@Override
public boolean hasCard() {
return cardinality != null;
}
@Override
public void setCard(IntVar card) {
if(!hasCard()){
cardinality=card;
new Constraint(ConstraintsName.SETCARD, new PropCardinality(this, card)).post();
} else {
model.arithm(cardinality, "=", card).post();
}
}
@Override
public boolean force(int element, ICause cause) throws ContradictionException {
assert cause != null;
if (!ub.contains(element)) {
contradiction(cause, "");
return true;
}
if (lb.add(element)) {
if (reactOnModification) {
delta.add(element, SetDelta.LB, cause);
}
SetEventType e = SetEventType.ADD_TO_KER;
notifyPropagators(e, cause);
return true;
}
return false;
}
@Override
public boolean remove(int element, ICause cause) throws ContradictionException {
assert cause != null;
if (lb.contains(element)) {
contradiction(cause, "");
return true;
}
if (ub.remove(element)) {
if (reactOnModification) {
delta.add(element, SetDelta.UB, cause);
}
SetEventType e = SetEventType.REMOVE_FROM_ENVELOPE;
notifyPropagators(e, cause);
return true;
}
return false;
}
@Override
public boolean instantiateTo(int[] value, ICause cause) throws ContradictionException {
boolean changed = !isInstantiated();
for (int i : value) {
force(i, cause);
}
if (lb.size() != value.length) {
contradiction(cause, "");
}
if (ub.size() != value.length) {
for (int i : getUB()) {
if (!getLB().contains(i)) {
remove(i, cause);
}
}
}
return changed;
}
@Override
public ISetDeltaMonitor monitorDelta(ICause propagator) {
createDelta();
return new SetDeltaMonitor(getDelta(), propagator);
}
@Override
public SetDelta getDelta() {
return delta;
}
@Override
public int getTypeAndKind() {
return VAR | SET;
}
@Override
protected EvtScheduler createScheduler() {
return new SetEvtScheduler();
}
@Override
public String toString() {
if(isInstantiated()){
return getName()+" = "+getLB().toString();
}else {
return getName()+" = ["+getLB()+", "+getUB()+"]";
}
}
@Override
public void createDelta() {
if (!reactOnModification) {
reactOnModification = true;
delta = new SetDelta(model.getEnvironment());
}
}
}