Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2521_ProblemRefactoring/HeuristicLab.Problems.LinearAssignment/3.3/LinearAssignmentProblem.cs @ 17696

Last change on this file since 17696 was 17696, checked in by abeham, 4 years ago

#2521:

  • fixed bug in EngineAlgorithm in Problem setter
  • Added storable type to IEncodedProblem
  • fixed some bugs and tests
File size: 10.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Drawing;
24using System.Linq;
25using System.Threading;
26using HEAL.Attic;
27using HeuristicLab.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Data;
30using HeuristicLab.Encodings.PermutationEncoding;
31using HeuristicLab.Optimization;
32using HeuristicLab.Parameters;
33
34namespace HeuristicLab.Problems.LinearAssignment {
35  [Item("Linear Assignment Problem (LAP)", "In the linear assignment problem (LAP) an assignment of workers to jobs has to be found such that each worker is assigned to exactly one job, each job is assigned to exactly one worker and the sum of the resulting costs is minimal (or maximal).")]
36  [Creatable(CreatableAttribute.Categories.CombinatorialProblems, Priority = 130)]
37  [StorableType("7766E004-A93D-4CA6-8012-AE5E8F4C4D85")]
38  public sealed class LinearAssignmentProblem : PermutationProblem {
39    public static readonly string CostsDescription = "The cost matrix that describes the assignment of rows to columns.";
40    public static readonly string RowNamesDescription = "The elements represented by the rows of the costs matrix.";
41    public static readonly string ColumnNamesDescription = "The elements represented by the columns of the costs matrix.";
42
43    public override Image ItemImage {
44      get { return HeuristicLab.Common.Resources.VSImageLibrary.Type; }
45    }
46
47    #region Parameter Properties
48    public IValueParameter<DoubleMatrix> CostsParameter {
49      get { return (IValueParameter<DoubleMatrix>)Parameters["Costs"]; }
50    }
51    public IValueParameter<ItemSet<Permutation>> BestKnownSolutionsParameter {
52      get { return (IValueParameter<ItemSet<Permutation>>)Parameters["BestKnownSolutions"]; }
53    }
54    public IValueParameter<Permutation> BestKnownSolutionParameter {
55      get { return (IValueParameter<Permutation>)Parameters["BestKnownSolution"]; }
56    }
57    public IValueParameter<StringArray> RowNamesParameter {
58      get { return (IValueParameter<StringArray>)Parameters["RowNames"]; }
59    }
60    public IValueParameter<StringArray> ColumnNamesParameter {
61      get { return (IValueParameter<StringArray>)Parameters["ColumnNames"]; }
62    }
63    private IResultParameter<LAPAssignment> BestLAPSolutionParameter {
64      get { return (IResultParameter<LAPAssignment>)Parameters["Best LAP Solution"]; }
65    }
66    #endregion
67
68    #region Properties
69    public DoubleMatrix Costs {
70      get { return CostsParameter.Value; }
71      set { CostsParameter.Value = value; }
72    }
73    public StringArray RowNames {
74      get { return RowNamesParameter.Value; }
75      set { RowNamesParameter.Value = value; }
76    }
77    public StringArray ColumnNames {
78      get { return ColumnNamesParameter.Value; }
79      set { ColumnNamesParameter.Value = value; }
80    }
81    public ItemSet<Permutation> BestKnownSolutions {
82      get { return BestKnownSolutionsParameter.Value; }
83      set { BestKnownSolutionsParameter.Value = value; }
84    }
85    public Permutation BestKnownSolution {
86      get { return BestKnownSolutionParameter.Value; }
87      set { BestKnownSolutionParameter.Value = value; }
88    }
89    #endregion
90
91    [StorableConstructor]
92    private LinearAssignmentProblem(StorableConstructorFlag _) : base(_) { }
93    private LinearAssignmentProblem(LinearAssignmentProblem original, Cloner cloner)
94      : base(original, cloner) {
95      AttachEventHandlers();
96    }
97    public LinearAssignmentProblem()
98      : base(new PermutationEncoding("Assignment")) {
99      Encoding.LengthParameter.ReadOnly = DimensionRefParameter.ReadOnly = true;
100
101      Parameters.Add(new ValueParameter<DoubleMatrix>("Costs", CostsDescription, new DoubleMatrix(3, 3)));
102      Parameters.Add(new OptionalValueParameter<ItemSet<Permutation>>("BestKnownSolutions", "The list of best known solutions which is updated whenever a new better solution is found or may be the optimal solution if it is known beforehand.", null));
103      Parameters.Add(new OptionalValueParameter<Permutation>("BestKnownSolution", "The best known solution which is updated whenever a new better solution is found or may be the optimal solution if it is known beforehand.", null));
104      Parameters.Add(new OptionalValueParameter<StringArray>("RowNames", RowNamesDescription));
105      Parameters.Add(new OptionalValueParameter<StringArray>("ColumnNames", ColumnNamesDescription));
106      Parameters.Add(new ResultParameter<LAPAssignment>("Best LAP Solution", "The best so far LAP solution found."));
107
108      ((ValueParameter<DoubleMatrix>)CostsParameter).ReactOnValueToStringChangedAndValueItemImageChanged = false;
109      ((OptionalValueParameter<StringArray>)RowNamesParameter).ReactOnValueToStringChangedAndValueItemImageChanged = false;
110      ((OptionalValueParameter<StringArray>)ColumnNamesParameter).ReactOnValueToStringChangedAndValueItemImageChanged = false;
111
112      RowNames = new StringArray(new string[] { "Eric", "Robert", "Allison" });
113      ColumnNames = new StringArray(new string[] { "MRI", "Blood test", "Angiogram" });
114      Costs[0, 0] = 4; Costs[0, 1] = 5; Costs[0, 2] = 3;
115      Costs[1, 0] = 6; Costs[1, 1] = 6; Costs[1, 2] = 4;
116      Costs[2, 0] = 5; Costs[2, 1] = 5; Costs[2, 2] = 1;
117
118      Operators.RemoveAll(x => x is IMoveOperator);
119      AttachEventHandlers();
120    }
121
122    public override IDeepCloneable Clone(Cloner cloner) {
123      return new LinearAssignmentProblem(this, cloner);
124    }
125
126    public override ISingleObjectiveEvaluationResult Evaluate(Permutation assignment, IRandom random, CancellationToken cancellationToken) {
127      var costs = Costs;
128
129      int len = assignment.Length;
130      double quality = 0;
131      for (int i = 0; i < len; i++) {
132        quality += costs[i, assignment[i]];
133      }
134
135      return new SingleObjectiveEvaluationResult(quality);
136    }
137
138    public override void Analyze(Permutation[] permutations, double[] qualities, ResultCollection results, IRandom random) {
139      base.Analyze(permutations, qualities, results, random);
140
141      var costs = Costs;
142      var rowNames = RowNames;
143      var columnNames = ColumnNames;
144      bool max = Maximization;
145
146      var sorted = qualities.Select((x, index) => new { Index = index, Quality = x }).OrderBy(x => x.Quality).ToArray();
147      if (max) Array.Reverse(sorted);
148      int i = sorted[0].Index;
149      var best = Tuple.Create(permutations[i], qualities[i]);
150
151      if (double.IsNaN(BestKnownQuality) || IsBetter(best.Item2, BestKnownQuality)) {
152        // if there isn't a best-known quality or we improved the best-known quality we'll add the current solution as best-known
153        BestKnownQuality = best.Item2;
154        BestKnownSolution = (Permutation)best.Item1.Clone();
155        BestKnownSolutions = new ItemSet<Permutation>(new PermutationEqualityComparer());
156        BestKnownSolutions.Add((Permutation)best.Item1.Clone());
157      } else if (BestKnownQuality == best.Item2) {
158        // if we matched the best-known quality we'll try to set the best-known solution if it isn't null
159        // and try to add it to the pool of best solutions if it is different
160        if (BestKnownSolution == null) BestKnownSolution = (Permutation)best.Item1.Clone();
161        if (BestKnownSolutions == null) BestKnownSolutions = new ItemSet<Permutation>(new PermutationEqualityComparer());
162
163        foreach (var k in sorted) { // for each solution that we found check if it is in the pool of best-knowns
164          if (IsBetter(best.Item2, k.Quality)) break; // stop when we reached a solution worse than the best-known quality
165          var p = permutations[k.Index];
166          if (!BestKnownSolutions.Contains(p))
167            BestKnownSolutions.Add((Permutation)permutations[k.Index].Clone());
168        }
169      }
170
171      LAPAssignment solution = BestLAPSolutionParameter.ActualValue;
172      if (solution == null) {
173        solution = new LAPAssignment(costs, rowNames, columnNames, (Permutation)best.Item1.Clone(), new DoubleValue(best.Item2));
174        BestLAPSolutionParameter.ActualValue = solution;
175      } else {
176        if (IsBetter(best.Item2, solution.Quality.Value)) {
177          solution.Costs = costs;
178          solution.Assignment = (Permutation)best.Item1.Clone();
179          solution.Quality.Value = best.Item2;
180          if (rowNames != null)
181            solution.RowNames = rowNames;
182          else solution.RowNames = null;
183          if (columnNames != null)
184            solution.ColumnNames = columnNames;
185          else solution.ColumnNames = null;
186        }
187      }
188
189    }
190
191    #region Events
192    private void Costs_RowsChanged(object sender, EventArgs e) {
193      if (Costs.Rows != Costs.Columns) {
194        ((IStringConvertibleMatrix)Costs).Columns = Costs.Rows;
195        Dimension = Costs.Rows;
196      }
197    }
198    private void Costs_ColumnsChanged(object sender, EventArgs e) {
199      if (Costs.Rows != Costs.Columns) {
200        ((IStringConvertibleMatrix)Costs).Rows = Costs.Columns;
201        Dimension = Costs.Rows;
202      }
203    }
204    private void Costs_Reset(object sender, EventArgs e) {
205      Dimension = Costs.Rows;
206    }
207    #endregion
208
209    #region Helpers
210    [StorableHook(HookType.AfterDeserialization)]
211    private void AfterDeserialization() {
212      AttachEventHandlers();
213    }
214
215    private void AttachEventHandlers() {
216      Costs.RowsChanged += new EventHandler(Costs_RowsChanged);
217      Costs.ColumnsChanged += new EventHandler(Costs_ColumnsChanged);
218      Costs.Reset += new EventHandler(Costs_Reset);
219    }
220    #endregion
221  }
222}
Note: See TracBrowser for help on using the repository browser.