001    package ifs.dbt;
002    
003    
004    import ifs.extension.*;
005    import ifs.model.*;
006    import ifs.solver.*;
007    import ifs.util.*;
008    import java.util.*;
009    
010    /**
011     * Maintenance of arc consistency in dynamic backtracking.
012     * <br><br>
013     * The difference between {@link MacPropagation} and this DBT propagation is that all not-assigned values 
014     * of an assigned variable are marked as nogood. Also, when a dead end is reached, unassignment or failure
015     * takes place.
016     * <br><br>
017     * This IFS solver extension is to be used only in case of dynamic backtracking and it has no parameters.
018     *
019     * 
020     * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
021     * @version 1.0
022     */
023    public class DbtPropagation extends MacPropagation implements SolverListener {
024        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(DbtPropagation.class);
025        
026        /** 
027         * Constructor. No parameter is taken from properties.
028         */
029        public DbtPropagation(Solver solver, DataProperties properties) {
030            super(solver, properties);
031            solver.addSolverListener(this);
032        }
033        
034        /**
035         * Propagation takes place every time a value is assigned to a variable.
036         * <br><br>
037         * <li>Prints a warning if the value is nogood (should not never happen),
038         * <li>sets all other values of the variable to nogood (explanation is the assigned value itself),
039         * <li>runs propagation.
040         *
041         * @see MacPropagation#propagate(Variable)
042         */
043        public void afterAssigned(long iteration, Value value) {
044            iIteration = iteration;
045            if (!isGood(value)) {
046                sLogger.warn(value.variable().getName()+" = "+value.getName()+" -- not good value assigned (noGood:"+noGood(value)+")");
047                setGood(value);
048            }
049            
050            HashSet noGood = new HashSet(1);
051            
052            noGood.add(value);
053            for (Enumeration i = value.variable().values().elements(); i.hasMoreElements();) {
054                Value anotherValue = (Value) i.nextElement();
055                if (anotherValue.equals(value) || !isGood(anotherValue)) continue;
056                setNoGood(anotherValue, noGood);
057            }
058            propagate(value.variable());
059        }
060        
061        /**
062         * Undo propagation when a value is unassigned.
063         * <br><br>
064         * <li>Prints an error if the value is nogood (should not never happen),
065         * <li>runs propagation undo.
066         *
067         * @see MacPropagation#undoPropagate(Variable)
068         */
069        public void afterUnassigned(long iteration, Value value) {
070            iIteration = iteration;
071            if (!isGood(value)) {
072                sLogger.error(value.variable().getName()+" = "+value.getName()+" -- not good value unassigned (noGood:"+noGood(value)+")");
073            }
074            undoPropagate(value.variable());
075        }
076        
077        /**
078         * If no variable is selected (all variables are assinged), unassign the last assigned variable.
079         * <br><br>
080         * Do not allow to select an assigned variable.
081         * <br><br>
082         * If no variable is selected (because all variables are assigned, see {@link DbtVariableSelection}):
083         * <ul>
084         * <li> find the last assigned variable and 
085         * <li> unassign it (explanation is a union of assignments of all other variables).
086         * </ul>
087         *
088         * @see DbtVariableSelection#selectVariable(Solution)
089         */
090        public boolean variableSelected(long iteration, Variable variable) {
091            if (variable == null) {
092                sLogger.debug("No variable selected -> backtrack.");
093                Variable lastVariable = null;
094                
095                for (Enumeration i = getModel().assignedVariables().elements(); i.hasMoreElements();) {
096                    Variable var = (Variable) i.nextElement();
097                    
098                    if (lastVariable == null || lastVariable.getAssignment().lastAssignmentIteration()<var.getAssignment().lastAssignmentIteration()) {
099                        lastVariable = var;
100                    }
101                }
102                if (lastVariable == null) {
103                    sLogger.error("No assignment -> fail");
104                    getSolver().stopSolver();
105                    return false;
106                }
107                sLogger.debug("Unassign:" + lastVariable.getName());
108                HashSet noGoods = new HashSet();
109                
110                for (Enumeration i = getModel().assignedVariables().elements(); i.hasMoreElements();) {
111                    Variable var = (Variable) i.nextElement();
112                    
113                    if (!var.equals(lastVariable)) {
114                        noGoods.add(var.getAssignment());
115                    }
116                }
117                Value value = lastVariable.getAssignment();
118                
119                lastVariable.unassign(iteration);
120                setNoGood(value, noGoods);
121                return false;
122            }
123            if (variable.getAssignment() != null) {
124                sLogger.error("Assigned value selected -- not supported by DBT.");
125                return false;
126            }
127            return true;
128        }
129        
130        /**
131         * If no value is selected (because of a dead end), make some unassignments.
132         * <br><br>
133         * If no value is selected 
134         * (e.g., because the selected variable has all values marked as nogood, see {@link DbtValueSelection}),
135         * <ul>
136         * <li> compute a union of explanations of all values, 
137         * <ul><li> if it is empty fail (inconsistency is found),</ul>
138         * <li> otherwise pick the last assigned variable from the computed union of explanation and unassign it
139         * <ul>(explanation for that is the computed union of explanations without the last assignment).</ul>
140         * </ul>
141         *
142         * @see DbtVariableSelection#selectVariable(Solution)
143         */
144        public boolean valueSelected(long iteration, Variable variable, Value value) {
145            if (variable != null && value == null) {
146                HashSet noGoods = new HashSet();
147                
148                for (Enumeration i = variable.values().elements(); i.hasMoreElements();) {
149                    Value val = (Value) i.nextElement();
150                    if (noGood(val) != null) {
151                        noGoods.addAll(noGood(val));
152                    }
153                }
154                if (noGoods.isEmpty()) {
155                    sLogger.debug("Fail");
156                    getSolver().stopSolver();
157                    return false;
158                }
159                Variable lastVariable = null;
160                
161                for (Iterator i = noGoods.iterator(); i.hasNext();) {
162                    Value val = (Value) i.next();
163                    Variable var = val.variable();
164                    
165                    if (lastVariable == null || lastVariable.getAssignment().lastAssignmentIteration()<var.getAssignment().lastAssignmentIteration()) {
166                        lastVariable = var;
167                    }
168                }
169                Value assignment = lastVariable.getAssignment();
170                
171                noGoods.remove(assignment);
172                lastVariable.unassign(iteration);
173                setNoGood(assignment, noGoods);
174            }
175            return true;
176        }
177    }