    001package org.cpsolver.ifs.assignment;
    002
    003import java.util.ArrayList;
    004import java.util.Collection;
    005import java.util.List;
    006
    007import org.cpsolver.ifs.assignment.context.AssignmentContext;
    008import org.cpsolver.ifs.assignment.context.AssignmentContextHolder;
    009import org.cpsolver.ifs.assignment.context.AssignmentContextReference;
    010import org.cpsolver.ifs.assignment.context.HasAssignmentContext;
    011import org.cpsolver.ifs.constant.ConstantVariable;
    012import org.cpsolver.ifs.criteria.Criterion;
    013import org.cpsolver.ifs.model.Constraint;
    014import org.cpsolver.ifs.model.GlobalConstraint;
    015import org.cpsolver.ifs.model.Model;
    016import org.cpsolver.ifs.model.Value;
    017import org.cpsolver.ifs.model.Variable;
    018
    019
    020/**
    021 * An abstract implementation of an {@link Assignment} object. It contains an instance of
    022 * a given assignment context holder (see {@link AssignmentContextHolder}) and
    023 * implements the assignment logic. But the actual process of storing and retrieving values
    024 * is left on the assignment implementation.
    025 *
    026 * @see Assignment
    027 *
    028 * @author  Tomáš Müller
    029 * @version IFS 1.3 (Iterative Forward Search)<br>
    030 *          Copyright (C) 2014 Tomáš Müller<br>
    031 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    032 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    033 * <br>
    034 *          This library is free software; you can redistribute it and/or modify
    035 *          it under the terms of the GNU Lesser General Public License as
    036 *          published by the Free Software Foundation; either version 3 of the
    037 *          License, or (at your option) any later version. <br>
    038 * <br>
    039 *          This library is distributed in the hope that it will be useful, but
    040 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    041 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    042 *          Lesser General Public License for more details. <br>
    043 * <br>
    044 *          You should have received a copy of the GNU Lesser General Public
    045 *          License along with this library; if not see <a href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>.
    046 * @param <V> Variable
    047 * @param <T> Value
    048 **/
    049public abstract class AssignmentAbstract<V extends Variable<V, T>, T extends Value<V, T>> implements Assignment<V, T> {
    050    protected AssignmentContextHolder<V, T> iContexts;
    051    protected boolean iHasInitialzedContext = false;
    052
    053    /**
    054     * Constructor
    055     * @param contexts an instance of the assignment context holder
    056     */
    057    public AssignmentAbstract(AssignmentContextHolder<V, T> contexts) {
    058        iContexts = contexts;
    059    }
    060
    061    /**
    062     * Checks if the variable is {@link ConstantVariable}, returns {@link AssignmentAbstract#getValueInternal(Variable)}
    063     * if the variable is not a constant.
    064     */
    065    @Override
    066    @SuppressWarnings("unchecked")
    067    public T getValue(V variable) {
    068        if (variable instanceof ConstantVariable<?> && ((ConstantVariable<?>)variable).isConstant())
    069            return ((ConstantVariable<T>)variable).getConstantValue();
    070        return getValueInternal(variable);
    071    }
    072
    073    /**
    074     * Returns assignment of a variable, null if not assigned. To be implemented.
    075     * @param variable a variable in question
    076     * @return assigned value
    077     **/
    078    protected abstract T getValueInternal(V variable);
    079
    080    /**
    081     * Sets an assignment to a variable (unassigns a variable if the given value is null). To be implemented.
    082     * @param iteration current iteration
    083     * @param variable a variable to be assigned
    084     * @param value new assignment, null if to be unassigned
    085     **/
    086    protected abstract void setValueInternal(long iteration, V variable, T value);
    087
    088    /** Assigns a variable with the given value. All the appropriate classes are notified about the change.
    089     * It is using {@link AssignmentAbstract#setValueInternal(long, Variable, Value)} to store the new
    090     * assignment.
    091     * @param iteration current iteration
    092     * @param variable a variable
    093     * @param value one of its values, null if the variable is to be unassigned
    094     * @return previous assignment of the variable
    095     **/
    096    @SuppressWarnings("unchecked")
    097    protected T assign(long iteration, V variable, T value) {
    098        if (variable instanceof ConstantVariable<?> && ((ConstantVariable<?>)variable).isConstant())
    099            return ((ConstantVariable<T>)variable).getConstantValue();
    100
    101        assert variable.getModel() != null && (value == null || variable.equals(value.variable()));
    102        Model<V, T> model = variable.getModel();
    103
    104        // ensure all model, criterion, and constraint assignment contexts are initialized before changing the assignment value
    105        ensureInitializedContext(variable);
    106
    107        // unassign old value, if assigned
    108        T old = getValueInternal(variable);
    109        if (old != null) {
    110            if (old.equals(value)) return old;
    111            if (model != null)
    112                model.beforeUnassigned(this, iteration, old);
    113            setValueInternal(iteration, variable, null);
    114            for (Constraint<V, T> constraint : variable.constraints())
    115                constraint.unassigned(this, iteration, old);
    116            if (model != null)
    117                for (GlobalConstraint<V, T> constraint : model.globalConstraints())
    118                    constraint.unassigned(this, iteration, old);
    119            variable.variableUnassigned(this, iteration, old);
    120            if (model != null)
    121                model.afterUnassigned(this, iteration, old);
    122        }
    123
    124        // assign new value, if provided
    125        if (value != null) {
    126            if (model != null)
    127                model.beforeAssigned(this, iteration, value);
    128            setValueInternal(iteration, variable, value);
    129            for (Constraint<V, T> constraint : variable.constraints())
    130                constraint.assigned(this, iteration, value);
    131            if (model != null)
    132                for (GlobalConstraint<V, T> constraint : model.globalConstraints())
    133                    constraint.assigned(this, iteration, value);
    134            variable.variableAssigned(this, iteration, value);
    135            if (model != null)
    136                model.afterAssigned(this, iteration, value);
    137        }
    138
    139        // return old value
    140        return old;
    141    }
    142
    143    @Override
    144    public T assign(long iteration, T value) {
    145        return assign(iteration, value.variable(), value);
    146    }
    147
    148    @Override
    149    public T unassign(long iteration, V variable) {
    150        return assign(iteration, variable, null);
    151    }
    152
    153    @Override
    154    public T unassign(long iteration, V variable, T value) {
    155        T current = getValue(variable);
    156        if (current == null && value == null) {
    157            return current;
    158        } else if (current != null && current.equals(value)) {
    159            return current;
    160        } else {
    161            return assign(iteration, variable, null);
    162        }
    163    }
    164
    165    @Override
    166    public int nrAssignedVariables() {
    167        return assignedVariables().size();
    168    }
    169
    170    @Override
    171    public Collection<T> assignedValues() {
    172        List<T> values = new ArrayList<T>();
    173        for (V variable: assignedVariables())
    174            values.add(getValueInternal(variable));
    175        return values;
    176    }
    177
    178    @Override
    179    public Collection<V> unassignedVariables(Model<V, T> model) {
    180        List<V> unassigned = new ArrayList<V>();
    181        for (V variable: model.variables())
    182            if (getValue(variable) == null)
    183                unassigned.add(variable);
    184        return unassigned;
    185    }
    186
    187    @Override
    188    public int nrUnassignedVariables(Model<V, T> model) {
    189        return model.variables().size() - nrAssignedVariables();
    190    }
    191
    192    @Override
    193    public <C extends AssignmentContext> C getAssignmentContext(AssignmentContextReference<V, T, C> reference) {
    194        return iContexts.getAssignmentContext(this, reference);
    195    }
    196
    197    @Override
    198    public <C extends AssignmentContext> void clearContext(AssignmentContextReference<V, T, C> reference) {
    199        iContexts.clearContext(reference);
    200    }
    201
    202    @Override
    203    public int getIndex() {
    204        return -1;
    205    }
    206
    207    /**
    208     * Ensure that the model, all criteria, all global constraints and all the related constraints have their assignment contexts initialized.
    209     * @param variable a variable to be changed
    210     */
    211    @SuppressWarnings("unchecked")
    212    protected void ensureInitializedContext(V variable) {
    213        if (!iHasInitialzedContext && variable.getModel() != null) {
    214            if (variable.getModel() instanceof HasAssignmentContext)
    215                iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)variable.getModel()).getAssignmentContextReference());
    216            for (Criterion<V, T> criterion: variable.getModel().getCriteria())
    217                if (criterion instanceof HasAssignmentContext)
    218                    iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)criterion).getAssignmentContextReference());
    219            for (GlobalConstraint<V, T> constraint: variable.getModel().globalConstraints())
    220                if (constraint instanceof HasAssignmentContext)
    221                    iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)constraint).getAssignmentContextReference());
    222            iHasInitialzedContext = true;
    223        }
    224        for (Constraint<V, T> constraint: variable.constraints())
    225            if (constraint instanceof HasAssignmentContext)
    226                iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)constraint).getAssignmentContextReference());
    227    }
    228}