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