    001package net.sf.cpsolver.ifs.solution;
    002
    003import java.util.ArrayList;
    004import java.util.Collection;
    005import java.util.List;
    006import java.util.Locale;
    007import java.util.Map;
    008
    009import net.sf.cpsolver.ifs.model.Model;
    010import net.sf.cpsolver.ifs.model.Value;
    011import net.sf.cpsolver.ifs.model.Variable;
    012import net.sf.cpsolver.ifs.perturbations.PerturbationsCounter;
    013import net.sf.cpsolver.ifs.solver.Solver;
    014
    015/**
    016 * Generic solution. <br>
    017 * <br>
    018 * It consist from the model and information about current iteration and
    019 * solution time.
    020 *
    021 * @see Model
    022 * @see net.sf.cpsolver.ifs.solver.Solver
    023 *
    024 * @version IFS 1.2 (Iterative Forward Search)<br>
    025 *          Copyright (C) 2006 - 2010 Tomáš Müller<br>
    026 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    027 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    028 * <br>
    029 *          This library is free software; you can redistribute it and/or modify
    030 *          it under the terms of the GNU Lesser General Public License as
    031 *          published by the Free Software Foundation; either version 3 of the
    032 *          License, or (at your option) any later version. <br>
    033 * <br>
    034 *          This library is distributed in the hope that it will be useful, but
    035 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    036 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    037 *          Lesser General Public License for more details. <br>
    038 * <br>
    039 *          You should have received a copy of the GNU Lesser General Public
    040 *          License along with this library; if not see
    041 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
    042 */
    043
    044public class Solution<V extends Variable<V, T>, T extends Value<V, T>> {
    045    private static java.text.DecimalFormat sTimeFormat = new java.text.DecimalFormat("0.00",
    046            new java.text.DecimalFormatSymbols(Locale.US));
    047
    048    private Model<V, T> iModel;
    049    private long iIteration = 0;
    050    private double iTime = 0.0;
    051
    052    private boolean iBestComplete = false;
    053    private Map<String, String> iBestInfo = null;
    054    private long iBestIteration = -1;
    055    private double iBestTime = -1;
    056    private double iBestPerturbationsPenaly = -1.0;
    057    private double iBestValue = 0;
    058
    059    private List<SolutionListener<V, T>> iSolutionListeners = new ArrayList<SolutionListener<V, T>>();
    060    private PerturbationsCounter<V, T> iPerturbationsCounter = null;
    061
    062    /** Constructor */
    063    public Solution(Model<V, T> model) {
    064        this(model, 0, 0.0);
    065    }
    066
    067    /** Constructor */
    068    public Solution(Model<V, T> model, long iteration, double time) {
    069        iModel = model;
    070        iIteration = iteration;
    071        iTime = time;
    072    }
    073
    074    /** Current iteration */
    075    public long getIteration() {
    076        return iIteration;
    077    }
    078
    079    /** The model associated with the solution */
    080    public Model<V, T> getModel() {
    081        return iModel;
    082    }
    083
    084    /** Current solution time (time in seconds from the start of the solver) */
    085    public double getTime() {
    086        return iTime;
    087    }
    088
    089    /** Update time, increment current iteration */
    090    public void update(double time) {
    091        iTime = time;
    092        iIteration++;
    093        for (SolutionListener<V, T> listener : iSolutionListeners)
    094            listener.solutionUpdated(this);
    095    }
    096
    097    /** Initialization */
    098    public void init(Solver<V, T> solver) {
    099        iIteration = 0;
    100        iTime = 0;
    101        if (iModel != null)
    102            iModel.init(solver);
    103        iPerturbationsCounter = solver.getPerturbationsCounter();
    104    }
    105
    106    @Override
    107    public String toString() {
    108        return "Solution{\n  model=" + iModel + ",\n  iteration=" + iIteration + ",\n  time=" + iTime + "\n}";
    109    }
    110
    111    /**
    112     * Solution information. It consits from info from the model which is
    113     * associated with the solution, time, iteration, speed and infos from all
    114     * solution listeners.
    115     */
    116    public Map<String, String> getInfo() {
    117        Map<String, String> ret = getModel().getInfo();
    118        if (getPerturbationsCounter() != null)
    119            getPerturbationsCounter().getInfo(ret, getModel());
    120        ret.put("Time", sTimeFormat.format(getTime() / 60.0) + " min");
    121        ret.put("Iteration", String.valueOf(getIteration()));
    122        if (getTime() > 0)
    123            ret.put("Speed", sTimeFormat.format((getIteration()) / getTime()) + " it/s");
    124        for (SolutionListener<V, T> listener : iSolutionListeners)
    125            listener.getInfo(this, ret);
    126        return ret;
    127    }
    128
    129    /**
    130     * Extended solution information. Similar to {@link Solution#getInfo()}, but
    131     * some more information (that is more expensive to compute) might be added.
    132     * Also extended model information is added (see
    133     * {@link Model#getExtendedInfo()}) into the resultant table.
    134     */
    135    public Map<String, String> getExtendedInfo() {
    136        Map<String, String> ret = getModel().getExtendedInfo();
    137        if (getPerturbationsCounter() != null)
    138            getPerturbationsCounter().getInfo(ret, getModel());
    139        ret.put("Time", sTimeFormat.format(getTime() / 60.0) + " min");
    140        ret.put("Iteration", String.valueOf(getIteration()));
    141        if (getTime() > 0)
    142            ret.put("Speed", sTimeFormat.format((getIteration()) / getTime()) + " it/s");
    143        for (SolutionListener<V, T> listener : iSolutionListeners)
    144            listener.getInfo(this, ret);
    145        return ret;
    146    }
    147
    148    /**
    149     * Solution information. It consists from info from the model which is
    150     * associated with the solution, time, iteration, speed and infos from all
    151     * solution listeners. Only variables from the given set are included.
    152     */
    153    public Map<String, String> getInfo(Collection<V> variables) {
    154        Map<String, String> ret = getModel().getInfo(variables);
    155        if (getPerturbationsCounter() != null)
    156            getPerturbationsCounter().getInfo(ret, getModel(), variables);
    157        ret.put("Time", sTimeFormat.format(getTime()) + " sec");
    158        ret.put("Iteration", String.valueOf(getIteration()));
    159        if (getTime() > 0)
    160            ret.put("Speed", sTimeFormat.format((getIteration()) / getTime()) + " it/s");
    161        for (SolutionListener<V, T> listener : iSolutionListeners)
    162            listener.getInfo(this, ret, variables);
    163        return ret;
    164    }
    165
    166    /** Info of the best ever found solution */
    167    public Map<String, String> getBestInfo() {
    168        return iBestInfo;
    169    }
    170
    171    /** Iteration when the best ever found solution was found */
    172    public long getBestIteration() {
    173        return (iBestIteration < 0 ? getIteration() : iBestIteration);
    174    }
    175
    176    /** Solution time when the best ever found solution was found */
    177    public double getBestTime() {
    178        return (iBestTime < 0 ? getTime() : iBestTime);
    179    }
    180
    181    /**
    182     * Returns true, if all variables of the best ever solution found are
    183     * assigned
    184     */
    185    public boolean isBestComplete() {
    186        return iBestComplete;
    187    }
    188
    189    /**
    190     * Total value of the best ever found solution -- sum of all assigned values
    191     * (see {@link Value#toDouble()}).
    192     */
    193    public double getBestValue() {
    194        return iBestValue;
    195    }
    196
    197    /** Set total value of the best ever found solution */
    198    public void setBestValue(double bestValue) {
    199        iBestValue = bestValue;
    200    }
    201
    202    /**
    203     * Perturbation penalty of the best ever found solution (see
    204     * {@link PerturbationsCounter})
    205     */
    206    public double getBestPerturbationsPenalty() {
    207        return iBestPerturbationsPenaly;
    208    }
    209
    210    /** Returns perturbation counter */
    211    public PerturbationsCounter<V, T> getPerturbationsCounter() {
    212        return iPerturbationsCounter;
    213    }
    214
    215    /** Clear the best ever found solution */
    216    public void clearBest() {
    217        getModel().clearBest();
    218        iBestInfo = null;
    219        iBestTime = -1;
    220        iBestIteration = -1;
    221        iBestComplete = false;
    222        iBestValue = 0;
    223        iBestPerturbationsPenaly = -1.0;
    224        for (SolutionListener<V, T> listener : iSolutionListeners)
    225            listener.bestCleared(this);
    226    }
    227
    228    /**
    229     * Save the current solution as the best ever found solution (it also calls
    230     * {@link Model#saveBest()})
    231     */
    232    public void saveBest() {
    233        getModel().saveBest();
    234        iBestInfo = getInfo();
    235        iBestTime = getTime();
    236        iBestIteration = getIteration();
    237        iBestComplete = getModel().nrUnassignedVariables() == 0;
    238        iBestValue = getModel().getTotalValue();
    239        iBestPerturbationsPenaly = (iPerturbationsCounter == null ? 0.0 : iPerturbationsCounter
    240                .getPerturbationPenalty(getModel()));
    241        for (SolutionListener<V, T> listener : iSolutionListeners)
    242            listener.bestSaved(this);
    243    }
    244
    245    /**
    246     * Restore the best ever found solution into the current solution (it also
    247     * calls {@link Model#restoreBest()})
    248     */
    249    public void restoreBest() {
    250        if (iBestInfo == null)
    251            return;
    252        getModel().restoreBest();
    253        iTime = iBestTime;
    254        iIteration = iBestIteration;
    255        for (SolutionListener<V, T> listener : iSolutionListeners)
    256            listener.bestRestored(this);
    257    }
    258
    259    /** Adds solution listner */
    260    public void addSolutionListener(SolutionListener<V, T> listener) {
    261        iSolutionListeners.add(listener);
    262    }
    263
    264    /** Removes solution listener */
    265    public void removeSolutionListener(SolutionListener<V, T> listener) {
    266        iSolutionListeners.remove(listener);
    267    }
    268}