org.chocosolver.solver.variables.impl.AbstractVariable 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) 2022, 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.solver.Cause;
import org.chocosolver.solver.ICause;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.constraints.Constraint;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.variables.*;
import org.chocosolver.solver.variables.events.IEventType;
import org.chocosolver.solver.variables.view.IView;
import org.chocosolver.util.iterators.EvtScheduler;
/**
* Class used to factorise code The subclass must implement Variable interface
*
* @author Jean-Guillaume Fages
* @author Charles Prud'homme
* @since 30 june 2011
*/
public abstract class AbstractVariable implements Variable {
/**
* Message associated with last value removals exception.
*/
static final String MSG_REMOVE = "remove last value";
/**
* Message associated with domain wipe out exception.
*/
protected static final String MSG_EMPTY = "empty domain";
/**
* Message associated with double instantiation exception.
*/
protected static final String MSG_INST = "the variable is already instantiated to another value";
/**
* Default exception message.
*/
static final String MSG_UNKNOWN = "unknown value";
/**
* Message associated with wrong upper bound exception.
*/
static final String MSG_UPP = "the new upper bound is lesser than the current lower bound";
/**
* Message associated with wrong lower bound exception.
*/
static final String MSG_LOW = "the new lower bound is greater than the current upper bound";
/**
* Message associated with wrong bounds exception.
*/
static final String MSG_BOUND = "new bounds are incorrect";
/**
* Unique ID of this variable.
*/
private final int ID;
/**
* Reference to the model containing this variable (unique).
*/
protected final Model model;
/**
* Name of the variable.
*/
protected final String name;
/**
* List of propagators of this variable.
*/
protected Propagator>[] propagators;
/**
* Store the index of this variable in each of its propagators.
*/
int[] pindices;
/**
* Dependency indices, for efficient scheduling purpose.
*/
private final int[] dindices;
/**
* Nb dependencies
*/
private final int dsize;
/**
* List of views based on this variable.
*/
private IView>[] views;
/**
* Indices of this variable in subscribed views
*/
private int[] idxInViews;
/**
* Index of the last not null view in views
.
*/
private int vIdx;
/**
* List of monitors observing this variable.
*/
protected IVariableMonitor[] monitors;
/**
* Index of the last not null monitor in monitors
.
*/
protected int mIdx;
/**
* The event scheduler of this variable, for efficient scheduling purpose. It stores propagators
* wrt the propagation conditions.
*/
private final EvtScheduler> scheduler;
/**
* World index of last instantiation event.
*/
private int instWI;
////////////////////////////////////////////////////////////////////////////////////////////////
// FOR PROPAGATION PURPOSE
////////////////////////////////////////////////////////////////////////////////////////////////
/**
* possibly aggregated event's mask
*/
private int mask;
/**
* possibly aggregated event's cause
*/
private ICause cause;
/**
* True if related propagators are scheduled for propagation
*/
public boolean scheduled;
//////////////////////////////////////////////////////////////////////////////////////
/**
* Create the shared data of any type of variable.
*
* @param name name of the variable
* @param model model which declares this variable
*/
protected AbstractVariable(String name, Model model) {
this.name = name;
this.model = model;
this.views = new IView[2];
this.idxInViews = new int[2];
this.monitors = new IVariableMonitor[2];
this.propagators = new Propagator[8];
this.pindices = new int[8];
this.ID = this.model.nextId();
this.model.associates(this);
this.scheduler = createScheduler();
this.dsize = this.scheduler.select(0) + 1;
this.dindices = new int[dsize + 1];
this.instWI = 0; // set to 0 to capture constant automatically
this.scheduled = false;
}
protected abstract EvtScheduler> createScheduler();
@Override
public boolean isScheduled() {
return scheduled;
}
@Override
public void schedule() {
this.scheduled = true;
}
@Override
public void unschedule() {
this.scheduled = false;
}
@Override
public final int getId() {
return ID;
}
@Override
public final int link(Propagator> propagator, int idxInProp) {
// 1. ensure capacity
if (dindices[dsize] == propagators.length) {
Propagator>[] tmp = propagators;
propagators = new Propagator[tmp.length * 3 / 2 + 1];
System.arraycopy(tmp, 0, propagators, 0, dindices[dsize]);
int[] itmp = pindices;
pindices = new int[itmp.length * 3 / 2 + 1];
System.arraycopy(itmp, 0, pindices, 0, dindices[dsize]);
if (pindices.length != propagators.length) {
throw new UnsupportedOperationException("error: pindices.length != propagators.length in " + this);
}
}
// 2. put it in the right place
int pc = propagator.getPropagationConditions(idxInProp);
return subscribe(propagator, idxInProp, scheduler.select(pc));
}
/**
* Move a propagator from position 'from' to position 'to' in {@link #propagators}. The element
* at position 'from' will then be 'null'.
*
* @param from a position in {@link #propagators}
* @param to a position in {@link #propagators}
*/
private void move(int from, int to) {
if (propagators[from] != null) {
propagators[to] = propagators[from];
pindices[to] = pindices[from];
propagators[to].setVIndices(pindices[from], to);
propagators[from] = null;
}
}
int subscribe(Propagator> p, int ip, int i) {
int j = dsize - 1;
for (; j >= i; j--) {
move(dindices[j], dindices[j + 1]);
dindices[j + 1]++;
}
propagators[dindices[i]] = p;
pindices[dindices[i]] = ip;
return dindices[i];
}
@Override
public int swapOnPassivate(Propagator> propagator, int idxInProp) {
int pp = propagator.getVIndice(idxInProp);
assert pp != -1;
int i = scheduler.select(propagator.getPropagationConditions(idxInProp));
move(dindices[i + 1] - 1, pp);
for (int j = i + 1; j < dsize; j++) {
move(dindices[j + 1] - 1, dindices[j] - 1);
dindices[j]--;
}
propagators[dindices[dsize] - 1] = propagator;
pindices[dindices[dsize] - 1] = idxInProp;
return dindices[dsize] - 1;
}
@Override
public int swapOnActivate(Propagator> propagator, int idxInProp) {
int pp = propagator.getVIndice(idxInProp);
if (pp > -1) {
assert propagators[pp] == propagator;
assert dindices[dsize - 1] <= pp && pp <= dindices[dsize];
int i = scheduler.select(propagator.getPropagationConditions(idxInProp));
move(--dindices[dsize], pp);
for (int j = dsize - 1; j >= i; j--) {
move(dindices[j], dindices[j + 1]);
dindices[j + 1]++;
}
propagators[dindices[i]] = propagator;
pindices[dindices[i]] = idxInProp;
return dindices[i];
} else {
assert propagator.getConstraint().getStatus() == Constraint.Status.FREE;
return -1;
}
}
@Override
public final void unlink(Propagator> propagator, int idxInProp) {
int i = propagator.getVIndice(idxInProp); // todo deal with -1
if (i > -1) {
assert propagators[i] == propagator : "Try to unlink :\n" + propagator + "\nfrom " + this.getName() + " but found:\n" + propagators[i];
// Dynamic addition of a propagator may be not considered yet, so the assertion is not correct
int mask = 0;
if (i < dindices[dsize - 1]) { // the position above this limit indicates a propagator that was swapped to passivate
mask = propagator.getPropagationConditions(pindices[i]);
}
cancel(i, scheduler.select(mask));
propagator.setVIndices(idxInProp, -1);
}
}
void cancel(int pp, int i) {
// start moving the other ones
move(dindices[i + 1] - 1, pp);
for (int j = i + 1; j < dsize; j++) {
move(dindices[j + 1] - 1, dindices[j] - 1);
dindices[j]--;
}
dindices[dsize]--;
}
@Override
public final Propagator>[] getPropagators() {
return propagators;
}
@Override
public final Propagator> getPropagator(int idx) {
return propagators[idx];
}
@Override
public final int getNbProps() {
return dindices[dsize];
}
@Override
public final int[] getPIndices() {
return pindices;
}
@Override
public final void setPIndice(int pos, int val) {
pindices[pos] = val;
}
@Override
public final int getDindex(int i) {
return dindices[i];
}
@Override
public final int getIndexInPropagator(int pidx) {
return pindices[pidx];
}
@Override
public int instantiationWorldIndex() {
return this.isInstantiated() ? this.instWI : Integer.MAX_VALUE;
}
@Override
public void recordWorldIndex() {
this.instWI = model.getEnvironment().getWorldIndex();
}
@Override
public final String getName() {
return this.name;
}
////////////////////////////////////////////////////////////////
///// methodes de l'interface Variable /////
////////////////////////////////////////////////////////////////
@Override
public void notifyPropagators(IEventType event, ICause cause) throws ContradictionException {
assert cause != null;
if(this.isInstantiated()){
recordWorldIndex();
}
model.getSolver().getEngine().onVariableUpdate(this, event, cause);
notifyMonitors(event);
notifyViews(event, cause);
}
public void notifyMonitors(IEventType event) throws ContradictionException {
for (int i = mIdx - 1; i >= 0; i--) {
monitors[i].onUpdate(this, event);
}
}
@Override
public void notifyViews(IEventType event, ICause cause) throws ContradictionException {
assert cause != null;
if (cause == Cause.Null) {
for (int i = vIdx - 1; i >= 0; i--) {
views[i].notify(event, idxInViews[i]);
}
} else {
for (int i = vIdx - 1; i >= 0; i--) {
if (views[i] != cause) { // reference is enough
views[i].notify(event, idxInViews[i]);
}
}
}
}
@Override
public void addMonitor(IVariableMonitor> monitor) {
// 1. check the non redundancy of a monitor if expected.
if (model.getSettings().checkDeclaredMonitors()) {
for (int i = 0; i < mIdx; i++) {
if (monitors[i] == monitor) return;
}
}
// 2. then add the monitor
if (mIdx == monitors.length) {
IVariableMonitor>[] tmp = monitors;
monitors = new IVariableMonitor[tmp.length * 3 / 2 + 1];
System.arraycopy(tmp, 0, monitors, 0, mIdx);
}
monitors[mIdx++] = monitor;
}
@Override
public void removeMonitor(IVariableMonitor> monitor) {
int i = mIdx - 1;
for (; i >= 0; i--) {
if (monitors[i] == monitor) break;
}
if (i < mIdx - 1) {
System.arraycopy(monitors, i + 1, monitors, i, mIdx - (i + 1));
}
monitors[--mIdx] = null;
}
@Override
public void subscribeView(IView> view, int idx) {
if (vIdx == views.length) {
IView>[] tmp = views;
int[] tmpIdx = idxInViews;
views = new IView[tmp.length * 3 / 2 + 1];
idxInViews = new int[tmp.length * 3 / 2 + 1];
System.arraycopy(tmp, 0, views, 0, vIdx);
System.arraycopy(tmpIdx, 0, idxInViews, 0, vIdx);
}
views[vIdx] = view;
idxInViews[vIdx] = idx;
vIdx++;
}
@Override
public final void contradiction(ICause cause, String message) throws ContradictionException {
assert cause != null;
model.getSolver().throwsException(cause, this, message);
}
public final Model getModel() {
return model;
}
@Override
public int getNbViews() {
return vIdx;
}
@Override
public IView> getView(int idx) {
return views[idx];
}
@Override
public int compareTo(Variable o) {
return this.getId() - o.getId();
}
@Override
public String toString() {
return getName();
}
/**
* @return true if this variable has a domain included in [0,1].
*/
public final boolean isBool() {
return (getTypeAndKind() & KIND) == BOOL;
}
/**
* @return true if this variable has a singleton domain (different from instantiated)
*/
public final boolean isAConstant() {
return (getTypeAndKind() & TYPE) == CSTE;
}
/**
* @return the event scheduler
*/
public final EvtScheduler> getEvtScheduler() {
return scheduler;
}
@Override
public IntVar asIntVar() {
return (IntVar) this;
}
@Override
public BoolVar asBoolVar() {
return (BoolVar) this;
}
@Override
public RealVar asRealVar() {
return (RealVar) this;
}
@Override
public SetVar asSetVar() {
return (SetVar) this;
}
@Override
public void storeEvents(int m, ICause cause) {
assert cause != null : "an event's cause is not supposed to be null";
if (this.cause == null) {
this.cause = cause;
} else if (this.cause != cause) {
this.cause = Cause.Null;
}
mask |= m;
}
@Override
public void clearEvents() {
this.cause = null;
mask = 0;
this.unschedule();
}
@Override
public int getMask() {
return mask;
}
@Override
public ICause getCause() {
return cause;
}
}