001package org.cpsolver.ifs.solution;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.List;
006import java.util.Locale;
007import java.util.Map;
008import java.util.concurrent.locks.ReadWriteLock;
009import java.util.concurrent.locks.ReentrantReadWriteLock;
010
011import org.cpsolver.coursett.criteria.TimetablingCriterion;
012import org.cpsolver.ifs.assignment.Assignment;
013import org.cpsolver.ifs.model.Model;
014import org.cpsolver.ifs.model.Value;
015import org.cpsolver.ifs.model.Variable;
016import org.cpsolver.ifs.perturbations.PerturbationsCounter;
017import org.cpsolver.ifs.solver.Solver;
018
019
020/**
021 * Generic solution. <br>
022 * <br>
023 * It consist from the model and information about current iteration and
024 * solution time.
025 * 
026 * @see Model
027 * @see org.cpsolver.ifs.solver.Solver
028 * 
029 * @author  Tomáš Müller
030 * @version IFS 1.3 (Iterative Forward Search)<br>
031 *          Copyright (C) 2006 - 2014 Tomáš Müller<br>
032 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
033 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
034 * <br>
035 *          This library is free software; you can redistribute it and/or modify
036 *          it under the terms of the GNU Lesser General Public License as
037 *          published by the Free Software Foundation; either version 3 of the
038 *          License, or (at your option) any later version. <br>
039 * <br>
040 *          This library is distributed in the hope that it will be useful, but
041 *          WITHOUT ANY WARRANTY; without even the implied warranty of
042 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
043 *          Lesser General Public License for more details. <br>
044 * <br>
045 *          You should have received a copy of the GNU Lesser General Public
046 *          License along with this library; if not see
047 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
048 * 
049 * @param <V> Variable
050 * @param <T> Value
051 */
052public class Solution<V extends Variable<V, T>, T extends Value<V, T>> {
053    private static java.text.DecimalFormat sTimeFormat = new java.text.DecimalFormat("0.00", new java.text.DecimalFormatSymbols(Locale.US));
054
055    private Model<V, T> iModel;
056    private Assignment<V, T> iAssignment;
057    private long iIteration = 0;
058    private long iFailedIterations = 0;
059    private double iTime = 0.0;
060
061    private Map<String, String> iBestInfo = null;
062    private long iBestIteration = -1;
063    private long iBestFailedIterations = -1;
064    private double iBestTime = -1;
065    private double iBestPerturbationsPenaly = -1.0;
066    private int iBestIndex = -1;
067
068    private List<SolutionListener<V, T>> iSolutionListeners = new ArrayList<SolutionListener<V, T>>();
069    private PerturbationsCounter<V, T> iPerturbationsCounter = null;
070    private final ReadWriteLock iLock = new ReentrantReadWriteLock(false);
071
072    /** Constructor 
073     * @param model problem model
074     **/
075    @Deprecated
076    public Solution(Model<V, T> model) {
077        this(model, model.getDefaultAssignment(), 0, 0.0);
078    }
079    
080    /** Constructor 
081     * @param model problem model
082     * @param assignment current assignment
083     **/
084    public Solution(Model<V, T> model, Assignment<V, T> assignment) {
085        this(model, assignment, 0, 0.0);
086    }
087
088    /** Constructor
089     * @param model problem model
090     * @param assignment current assignment
091     * @param iteration current iteration
092     * @param time current solver time
093     **/
094    public Solution(Model<V, T> model, Assignment<V, T> assignment, long iteration, double time) {
095        iModel = model;
096        iAssignment = assignment;
097        iIteration = iteration;
098        iTime = time;
099    }
100
101    /** Current iteration 
102     * @return current iteration
103     **/
104    public long getIteration() {
105        return iIteration;
106    }
107    
108    /** Number of failed iterations (i.e., number of calls {@link Solution#update(double, boolean)} with false success)
109     * @return number of failed iterations 
110     **/
111    public long getFailedIterations() {
112        return iFailedIterations;
113    }
114    
115    /** Number of failed iterations (i.e., number of calls {@link Solution#update(double, boolean)} with false success) in the best solution
116     * @return number of failed iterations in the best solution
117     **/
118    public long getBestFailedIterations() {
119        return (iBestIteration < 0 ? getFailedIterations() : iBestFailedIterations);
120    }
121
122    /** The model associated with the solution
123     * @return problem model 
124     **/
125    public Model<V, T> getModel() {
126        return iModel;
127    }
128    
129    /** The assignment associated with this solution
130     * @return current assignment
131     **/
132    public Assignment<V, T> getAssignment() {
133        return iAssignment;
134    }
135    
136    /** Set a new assignment 
137     * @param assignment current assignment
138     **/
139    public void setAssignment(Assignment<V, T> assignment) {
140        iAssignment = assignment;
141    }
142
143    /** Current solution time (time in seconds from the start of the solver) 
144     * @return solver time
145     **/
146    public double getTime() {
147        return iTime;
148    }
149
150    /** Update time, increment current iteration 
151     * @param time updated solver time
152     * @param success true if the last iteration was successful
153     * @param master master solution
154     **/
155    public void update(double time, boolean success, Solution<V, T> master) {
156        iLock.writeLock().lock();
157        try {
158            iTime = time;
159            iIteration++;
160            if (!success) iFailedIterations ++;
161            for (SolutionListener<V, T> listener : iSolutionListeners)
162                listener.solutionUpdated(this);
163            if (master != null) {
164                master.iTime = iTime;
165                master.iIteration++;
166                if (!success) master.iFailedIterations ++;
167            }
168        } finally {
169            iLock.writeLock().unlock();
170        }
171    }
172    
173    /** Update time, increment current iteration 
174     * @param time updated solver time
175     * @param success true if the last iteration was successful
176     **/
177    public void update(double time, boolean success) {
178        update(time, success, null);
179    }
180    
181    /** Update time, increment current iteration 
182     * @param time updated solver time
183     * @param master master solution
184     **/
185    public void update(double time, Solution<V, T> master) {
186        update(time, true, master);
187    }
188    
189    /** Update time, increment current iteration 
190     * @param time updated solver time
191     **/
192    public void update(double time) {
193        update(time, true, null);
194    }
195
196    /** Initialization 
197     * @param solver current solver
198     **/
199    public void init(Solver<V, T> solver) {
200        iIteration = 0;
201        iFailedIterations = 0;
202        iTime = 0;
203        if (iModel != null)
204            iModel.init(solver);
205        iPerturbationsCounter = solver.getPerturbationsCounter();
206    }
207
208    /**
209     * String representation -- returns a list of values of objective criteria
210     * @return comma separated string of {@link TimetablingCriterion#toString(Assignment)}
211     */
212    @Override
213    public String toString() {
214        return getModel().toString(getAssignment()) + (getFailedIterations() > 0 ? ", F:" + sTimeFormat.format(100.0 * getFailedIterations() / getIteration()) + "%" : "");
215    }
216
217    /**
218     * Solution information. It consists from info from the model which is
219     * associated with the solution, time, iteration, speed and infos from all
220     * solution listeners.
221     * @return info table
222     */
223    public Map<String, String> getInfo() {
224        Map<String, String> ret = getModel().getInfo(iAssignment);
225        if (getPerturbationsCounter() != null)
226            getPerturbationsCounter().getInfo(getAssignment(), getModel(), ret);
227        ret.put("Time", sTimeFormat.format(getTime() / 60.0) + " min");
228        ret.put("Iteration", getIteration() + (getFailedIterations() > 0 ? " (" + sTimeFormat.format(100.0 * getFailedIterations() / getIteration())+ "% failed)" : ""));
229        if (getTime() > 0)
230            ret.put("Speed", sTimeFormat.format((getIteration()) / getTime()) + " it/s");
231        for (SolutionListener<V, T> listener : iSolutionListeners)
232            listener.getInfo(this, ret);
233        return ret;
234    }
235
236    /**
237     * Extended solution information. Similar to {@link Solution#getInfo()}, but
238     * some more information (that is more expensive to compute) might be added.
239     * Also extended model information is added (see
240     * {@link Model#getExtendedInfo(Assignment)}) into the resultant table.
241     * @return extended info table
242     */
243    public Map<String, String> getExtendedInfo() {
244        Map<String, String> ret = getModel().getExtendedInfo(iAssignment);
245        if (getPerturbationsCounter() != null)
246            getPerturbationsCounter().getInfo(getAssignment(), getModel(), ret);
247        ret.put("Time", sTimeFormat.format(getTime() / 60.0) + " min");
248        ret.put("Iteration", getIteration() + (getFailedIterations() > 0 ? " (" + sTimeFormat.format(100.0 * getFailedIterations() / getIteration())+ "% failed)" : ""));
249        if (getTime() > 0)
250            ret.put("Speed", sTimeFormat.format((getIteration()) / getTime()) + " it/s");
251        if (getBestIteration() > 0)
252            ret.put("Best Iteration", getBestIteration() + (getBestFailedIterations() > 0 ? " (" + sTimeFormat.format(100.0 * getBestFailedIterations() / getBestIteration())+ "% failed)" : ""));
253        if (getBestTime() > 0)
254            ret.put("Best Time", sTimeFormat.format(getBestTime() / 60.0) + " min (" + sTimeFormat.format((getBestIteration()) / getBestTime()) + " it/s)");
255        for (SolutionListener<V, T> listener : iSolutionListeners)
256            listener.getInfo(this, ret);
257        return ret;
258    }
259
260    /**
261     * Solution information. It consists from info from the model which is
262     * associated with the solution, time, iteration, speed and infos from all
263     * solution listeners. Only variables from the given set are included.
264     * @param variables sub-problem
265     * @return info table
266     */
267    public Map<String, String> getInfo(Collection<V> variables) {
268        Map<String, String> ret = getModel().getInfo(iAssignment, variables);
269        if (getPerturbationsCounter() != null)
270            getPerturbationsCounter().getInfo(getAssignment(), getModel(), ret, variables);
271        ret.put("Time", sTimeFormat.format(getTime()) + " sec");
272        ret.put("Iteration", String.valueOf(getIteration()));
273        if (getTime() > 0)
274            ret.put("Speed", sTimeFormat.format((getIteration()) / getTime()) + " it/s");
275        for (SolutionListener<V, T> listener : iSolutionListeners)
276            listener.getInfo(this, ret, variables);
277        return ret;
278    }
279
280    /** Info of the best ever found solution 
281     * @return info table of the best solution
282     **/
283    public Map<String, String> getBestInfo() {
284        return iBestInfo;
285    }
286
287    /** Iteration when the best ever found solution was found 
288     * @return iteration of the best solution
289     **/
290    public long getBestIteration() {
291        return (iBestIteration < 0 ? getIteration() : iBestIteration);
292    }
293
294    /** Solution time when the best ever found solution was found
295     * @return solver time of the best solution
296     **/
297    public double getBestTime() {
298        return (iBestTime < 0 ? getTime() : iBestTime);
299    }
300
301    /**
302     * Returns true, if all variables of the best ever solution found are
303     * assigned
304     * @return true if the best solution has all the variables assigned
305     */
306    public boolean isBestComplete() {
307        return getModel().getBestUnassignedVariables() == 0;
308    }
309    
310    /**
311     * Index of the best assignment.
312     * @return {@link Assignment#getIndex()} of the best saved solution
313     */
314    public int getBestIndex() {
315        return iBestIndex;
316    }
317
318    /**
319     * Total value of the best ever found solution -- sum of all assigned values
320     * (see {@link Value#toDouble(Assignment)}).
321     * @return value of the best solution
322     */
323    public double getBestValue() {
324        return getModel().getBestValue();
325    }
326
327    /** Set total value of the best ever found solution 
328     * @param bestValue value of the best solution
329     **/
330    public void setBestValue(double bestValue) {
331        getModel().setBestValue(bestValue);
332    }
333
334    /**
335     * Perturbation penalty of the best ever found solution (see
336     * {@link PerturbationsCounter})
337     * @return perturbation penalty of the best solution
338     */
339    public double getBestPerturbationsPenalty() {
340        return iBestPerturbationsPenaly;
341    }
342
343    /** Returns perturbation counter 
344     * @return perturbations counter
345     **/
346    public PerturbationsCounter<V, T> getPerturbationsCounter() {
347        return iPerturbationsCounter;
348    }
349
350    /** Clear the best ever found solution */
351    public void clearBest() {
352        iLock.writeLock().lock();
353        try {
354            getModel().clearBest();
355            iBestInfo = null;
356            iBestTime = -1;
357            iBestIteration = -1;
358            iBestFailedIterations = 0;
359            iBestIndex = -1;
360            iBestPerturbationsPenaly = -1.0;
361            for (SolutionListener<V, T> listener : iSolutionListeners)
362                listener.bestCleared(this);
363        } finally {
364            iLock.writeLock().unlock();
365        }
366    }
367    
368    /** True if the solution is complete, i.e., all the variables are assigned 
369     * @return true if all the variables are assigned
370     **/
371    public boolean isComplete() {
372        return getAssignment().nrAssignedVariables() == getModel().variables().size();
373    }
374
375    /**
376     * Save the current solution as the best ever found solution (it also calls
377     * {@link Model#saveBest(Assignment)})
378     * @param master master solution into which information about the best solution are to be copied as well
379     */
380    public void saveBest(Solution<V, T> master) {
381        iLock.writeLock().lock();
382        try {
383            getModel().saveBest(iAssignment);
384            iBestInfo = getInfo();
385            iBestTime = getTime();
386            iBestIteration = getIteration();
387            iBestFailedIterations = getFailedIterations();
388            iBestIndex = getAssignment().getIndex();
389            iBestPerturbationsPenaly = (iPerturbationsCounter == null ? 0.0 : iPerturbationsCounter.getPerturbationPenalty(getAssignment(), getModel()));
390            for (SolutionListener<V, T> listener : iSolutionListeners)
391                listener.bestSaved(this);
392            
393            if (master != null) {
394                // master.iIteration = iIteration;
395                // master.iFailedIterations = iFailedIterations;
396                // master.iTime = iTime;
397                master.iBestInfo = iBestInfo;
398                master.iBestTime = iBestTime;
399                master.iBestIteration = iBestIteration;
400                master.iBestFailedIterations = iBestFailedIterations;
401                master.iBestPerturbationsPenaly = iBestPerturbationsPenaly;
402                master.iBestIndex = iBestIndex;
403            }
404        } finally {
405            iLock.writeLock().unlock();
406        }
407    }
408    
409    public boolean saveBestIfImproving(Solution<V, T> master, SolutionComparator<V, T> comparator) {
410        master.iLock.readLock().lock();
411        try {
412            if (iBestInfo != null && !comparator.isBetterThanBestSolution(this)) return false;
413        } finally {
414            master.iLock.readLock().unlock();
415        }
416        master.iLock.writeLock().lock();
417        try {
418            if (iBestInfo != null && !comparator.isBetterThanBestSolution(this)) return false;
419            getModel().saveBest(iAssignment);
420            iBestInfo = getInfo();
421            iBestTime = getTime();
422            iBestIteration = getIteration();
423            iBestFailedIterations = getFailedIterations();
424            iBestIndex = getAssignment().getIndex();
425            iBestPerturbationsPenaly = (iPerturbationsCounter == null ? 0.0 : iPerturbationsCounter.getPerturbationPenalty(getAssignment(), getModel()));
426            for (SolutionListener<V, T> listener : iSolutionListeners)
427                listener.bestSaved(this);
428            
429            // master.iIteration = iIteration;
430            // master.iFailedIterations = iFailedIterations;
431            // master.iTime = iTime;
432            master.iBestInfo = iBestInfo;
433            master.iBestTime = iBestTime;
434            master.iBestIteration = iBestIteration;
435            master.iBestFailedIterations = iBestFailedIterations;
436            master.iBestPerturbationsPenaly = iBestPerturbationsPenaly;
437            master.iBestIndex = iBestIndex;
438
439            return true;
440        } finally {
441            master.iLock.writeLock().unlock();
442        }
443    }
444        
445    
446    /**
447     * Save the current solution as the best ever found solution (it also calls
448     * {@link Model#saveBest(Assignment)})
449     */
450    public void saveBest() {
451        iLock.writeLock().lock();
452        try {
453            saveBest(null);
454        } finally {
455            iLock.writeLock().unlock();
456        }
457    }
458
459    /**
460     * Restore the best ever found solution into the current solution (it also
461     * calls {@link Model#restoreBest(Assignment)})
462     */
463    public void restoreBest() {
464        iLock.writeLock().lock();
465        try {
466            getModel().restoreBest(iAssignment);
467            // iTime = iBestTime;
468            // iIteration = iBestIteration;
469            // iFailedIterations = iBestFailedIterations;
470            for (SolutionListener<V, T> listener : iSolutionListeners)
471                listener.bestRestored(this);
472        } finally {
473            iLock.writeLock().unlock();
474        }
475    }
476
477    /** Adds solution listener 
478     * @param listener a solution listener
479     **/
480    public void addSolutionListener(SolutionListener<V, T> listener) {
481        iSolutionListeners.add(listener);
482    }
483
484    /** Removes solution listener
485     * @param listener a solution listener
486     **/
487    public void removeSolutionListener(SolutionListener<V, T> listener) {
488        iSolutionListeners.remove(listener);
489    }
490    
491    /** Registered of solution listeners
492     * @return list of registered solution listener
493     **/
494    public List<SolutionListener<V, T>> getSolutionListeners() {
495        return iSolutionListeners;
496    }
497    
498    /**
499     * Return solution lock
500     * @return read-write lock used to lock the solution during a change
501     */
502    public ReadWriteLock getLock() { return iLock; }
503}