Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2521_ProblemRefactoring/HeuristicLab.Problems.QuadraticAssignment/3.3/QuadraticAssignmentProblem.cs @ 17695

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

#2521:

  • Moving solution creator parameter from problems to algorithms (breaking wiring in some HeuristicOptimizationProblems)
  • Disallowing evaluator or encoding changes in encoding-specific base problems (to avoid confusion in derived problems whether this needs to be handled or not)
  • Added private set to ReferenceParameter property (serialization)
File size: 17.1 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.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Threading;
27using HEAL.Attic;
28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Data;
31using HeuristicLab.Encodings.PermutationEncoding;
32using HeuristicLab.Optimization;
33using HeuristicLab.Parameters;
34using HeuristicLab.PluginInfrastructure;
35using HeuristicLab.Problems.Instances;
36
37namespace HeuristicLab.Problems.QuadraticAssignment {
38  [Item("Quadratic Assignment Problem (QAP)", "The Quadratic Assignment Problem (QAP) can be described as the problem of assigning N facilities to N fixed locations such that there is exactly one facility in each location and that the sum of the distances multiplied by the connection strength between the facilities becomes minimal.")]
39  [Creatable(CreatableAttribute.Categories.CombinatorialProblems, Priority = 140)]
40  [StorableType("A86B1F49-D8E6-45E4-8EFB-8F5CCA2F9DC7")]
41  public sealed class QuadraticAssignmentProblem : PermutationProblem,
42    IProblemInstanceConsumer<QAPData>,
43    IProblemInstanceConsumer<TSPData>, IProblemInstanceExporter<QAPData> {
44    public static int ProblemSizeLimit = 1000;
45
46    public static new Image StaticItemImage {
47      get { return Common.Resources.VSImageLibrary.Type; }
48    }
49
50    #region Parameter Properties
51    [Storable] public IValueParameter<ItemSet<Permutation>> BestKnownSolutionsParameter { get; private set; }
52    [Storable] public IValueParameter<Permutation> BestKnownSolutionParameter { get; private set; }
53    [Storable] public IValueParameter<DoubleMatrix> WeightsParameter { get; private set; }
54    [Storable] public IValueParameter<DoubleMatrix> DistancesParameter { get; private set; }
55    [Storable] public IValueParameter<DoubleValue> LowerBoundParameter { get; private set; }
56    [Storable] public IValueParameter<DoubleValue> AverageQualityParameter { get; private set; }
57    #endregion
58
59    #region Properties
60    public ItemSet<Permutation> BestKnownSolutions {
61      get { return BestKnownSolutionsParameter.Value; }
62      set { BestKnownSolutionsParameter.Value = value; }
63    }
64    public Permutation BestKnownSolution {
65      get { return BestKnownSolutionParameter.Value; }
66      set { BestKnownSolutionParameter.Value = value; }
67    }
68    public DoubleMatrix Weights {
69      get { return WeightsParameter.Value; }
70      set { WeightsParameter.Value = value; }
71    }
72    public DoubleMatrix Distances {
73      get { return DistancesParameter.Value; }
74      set { DistancesParameter.Value = value; }
75    }
76    public DoubleValue LowerBound {
77      get { return LowerBoundParameter.Value; }
78      set { LowerBoundParameter.Value = value; }
79    }
80    public DoubleValue AverageQuality {
81      get { return AverageQualityParameter.Value; }
82      set { AverageQualityParameter.Value = value; }
83    }
84
85    private BestQAPSolutionAnalyzer BestQAPSolutionAnalyzer {
86      get { return Operators.OfType<BestQAPSolutionAnalyzer>().FirstOrDefault(); }
87    }
88
89    private QAPAlleleFrequencyAnalyzer QAPAlleleFrequencyAnalyzer {
90      get { return Operators.OfType<QAPAlleleFrequencyAnalyzer>().FirstOrDefault(); }
91    }
92    #endregion
93
94    [StorableConstructor]
95    private QuadraticAssignmentProblem(StorableConstructorFlag _) : base(_) { }
96    private QuadraticAssignmentProblem(QuadraticAssignmentProblem original, Cloner cloner)
97      : base(original, cloner) {
98      BestKnownSolutionsParameter = cloner.Clone(original.BestKnownSolutionsParameter);
99      BestKnownSolutionParameter = cloner.Clone(original.BestKnownSolutionParameter);
100      WeightsParameter = cloner.Clone(original.WeightsParameter);
101      DistancesParameter = cloner.Clone(original.DistancesParameter);
102      LowerBoundParameter = cloner.Clone(original.LowerBoundParameter);
103      AverageQualityParameter = cloner.Clone(original.AverageQualityParameter);
104    }
105    public QuadraticAssignmentProblem()
106      : base(new PermutationEncoding("Assignment") { Length = 5 }) {
107      Maximization = false;
108      Encoding.LengthParameter.ReadOnly = DimensionRefParameter.ReadOnly = true;
109      Encoding.PermutationTypeParameter.ReadOnly = PermutationTypeRefParameter.ReadOnly = true;
110      PermutationTypeRefParameter.Hidden = true;
111
112      Parameters.Add(BestKnownSolutionsParameter = 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));
113      Parameters.Add(BestKnownSolutionParameter = 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));
114      Parameters.Add(WeightsParameter = new ValueParameter<DoubleMatrix>("Weights", "The strength of the connection between the facilities."));
115      Parameters.Add(DistancesParameter = new ValueParameter<DoubleMatrix>("Distances", "The distance matrix which can either be specified directly without the coordinates, or can be calculated automatically from the coordinates."));
116      Parameters.Add(LowerBoundParameter = new OptionalValueParameter<DoubleValue>("LowerBound", "The Gilmore-Lawler lower bound to the solution quality."));
117      Parameters.Add(AverageQualityParameter = new OptionalValueParameter<DoubleValue>("AverageQuality", "The expected quality of a random solution."));
118
119      WeightsParameter.GetsCollected = false;
120      Weights = new DoubleMatrix(new double[,] {
121        { 0, 1, 0, 0, 1 },
122        { 1, 0, 1, 0, 0 },
123        { 0, 1, 0, 1, 0 },
124        { 0, 0, 1, 0, 1 },
125        { 1, 0, 0, 1, 0 }
126      }, @readonly: true);
127
128      DistancesParameter.GetsCollected = false;
129      Distances = new DoubleMatrix(new double[,] {
130        {   0, 360, 582, 582, 360 },
131        { 360,   0, 360, 582, 582 },
132        { 582, 360,   0, 360, 582 },
133        { 582, 582, 360,   0, 360 },
134        { 360, 582, 582, 360,   0 }
135      }, @readonly: true);
136
137      InitializeOperators();
138    }
139
140    public override ISingleObjectiveEvaluationResult Evaluate(Permutation assignment, IRandom random, CancellationToken cancellationToken) {
141      var quality = Evaluate(assignment, cancellationToken);
142      return new SingleObjectiveEvaluationResult(quality);
143    }
144
145    public double Evaluate(Permutation assignment, CancellationToken cancellationToken) {
146      double quality = 0;
147      for (int i = 0; i < assignment.Length; i++) {
148        for (int j = 0; j < assignment.Length; j++) {
149          quality += Weights[i, j] * Distances[assignment[i], assignment[j]];
150        }
151      }
152      return quality;
153    }
154
155    public override IDeepCloneable Clone(Cloner cloner) {
156      return new QuadraticAssignmentProblem(this, cloner);
157    }
158
159    #region Events
160    private void Evaluator_QualityParameter_ActualNameChanged(object sender, EventArgs e) {
161      Parameterize();
162    }
163    #endregion
164
165    protected override void DimensionOnChanged() {
166      base.DimensionOnChanged();
167      if (Dimension != Weights.Rows) Dimension = Weights.Rows;
168    }
169
170    #region Helpers
171    private void InitializeOperators() {
172      Operators.AddRange(ApplicationManager.Manager.GetInstances<IQAPMoveEvaluator>());
173      Operators.AddRange(ApplicationManager.Manager.GetInstances<IQAPLocalImprovementOperator>());
174      Operators.Add(new BestQAPSolutionAnalyzer());
175      Operators.Add(new QAPAlleleFrequencyAnalyzer());
176
177      Operators.Add(new QAPSimilarityCalculator());
178      Parameterize();
179    }
180    private void Parameterize() {
181      var operators = new List<IItem>();
182      if (BestQAPSolutionAnalyzer != null) {
183        operators.Add(BestQAPSolutionAnalyzer);
184        BestQAPSolutionAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
185        BestQAPSolutionAnalyzer.DistancesParameter.ActualName = DistancesParameter.Name;
186        BestQAPSolutionAnalyzer.WeightsParameter.ActualName = WeightsParameter.Name;
187        BestQAPSolutionAnalyzer.BestKnownQualityParameter.ActualName = BestKnownQualityParameter.Name;
188        BestQAPSolutionAnalyzer.BestKnownSolutionsParameter.ActualName = BestKnownSolutionsParameter.Name;
189        BestQAPSolutionAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
190      }
191      if (QAPAlleleFrequencyAnalyzer != null) {
192        operators.Add(QAPAlleleFrequencyAnalyzer);
193        QAPAlleleFrequencyAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
194        QAPAlleleFrequencyAnalyzer.BestKnownSolutionParameter.ActualName = BestKnownSolutionParameter.Name;
195        QAPAlleleFrequencyAnalyzer.DistancesParameter.ActualName = DistancesParameter.Name;
196        QAPAlleleFrequencyAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
197        QAPAlleleFrequencyAnalyzer.WeightsParameter.ActualName = WeightsParameter.Name;
198      }
199      foreach (var localOpt in Operators.OfType<IQAPLocalImprovementOperator>()) {
200        operators.Add(localOpt);
201        localOpt.DistancesParameter.ActualName = DistancesParameter.Name;
202        localOpt.MaximizationParameter.ActualName = MaximizationParameter.Name;
203        localOpt.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
204        localOpt.WeightsParameter.ActualName = WeightsParameter.Name;
205      }
206
207      foreach (var moveOp in Operators.OfType<IQAPMoveEvaluator>()) {
208        operators.Add(moveOp);
209        moveOp.DistancesParameter.ActualName = DistancesParameter.Name;
210        moveOp.WeightsParameter.ActualName = WeightsParameter.Name;
211        moveOp.QualityParameter.ActualName = Evaluator.QualityParameter.Name;
212
213        var swaMoveOp = moveOp as QAPSwap2MoveEvaluator;
214        if (swaMoveOp != null) {
215          var moveQualityName = swaMoveOp.MoveQualityParameter.ActualName;
216          foreach (var o in Encoding.Operators.OfType<IPermutationSwap2MoveQualityOperator>())
217            o.MoveQualityParameter.ActualName = moveQualityName;
218        }
219        var invMoveOp = moveOp as QAPInversionMoveEvaluator;
220        if (invMoveOp != null) {
221          var moveQualityName = invMoveOp.MoveQualityParameter.ActualName;
222          foreach (var o in Encoding.Operators.OfType<IPermutationInversionMoveQualityOperator>())
223            o.MoveQualityParameter.ActualName = moveQualityName;
224        }
225        var traMoveOp = moveOp as QAPTranslocationMoveEvaluator;
226        if (traMoveOp != null) {
227          var moveQualityName = traMoveOp.MoveQualityParameter.ActualName;
228          foreach (var o in Encoding.Operators.OfType<IPermutationTranslocationMoveQualityOperator>())
229            o.MoveQualityParameter.ActualName = moveQualityName;
230        }
231        var scrMoveOp = moveOp as QAPScrambleMoveEvaluator;
232        if (scrMoveOp != null) {
233          var moveQualityName = scrMoveOp.MoveQualityParameter.ActualName;
234          foreach (var o in Encoding.Operators.OfType<IPermutationScrambleMoveQualityOperator>())
235            o.MoveQualityParameter.ActualName = moveQualityName;
236        }
237      }
238      foreach (var similarityCalculator in Operators.OfType<ISolutionSimilarityCalculator>()) {
239        operators.Add(similarityCalculator);
240        similarityCalculator.SolutionVariableName = Encoding.Name;
241        similarityCalculator.QualityVariableName = Evaluator.QualityParameter.ActualName;
242        var qapsimcalc = similarityCalculator as QAPSimilarityCalculator;
243        if (qapsimcalc != null) {
244          qapsimcalc.Weights = Weights;
245          qapsimcalc.Distances = Distances;
246        }
247      }
248
249      if (operators.Count > 0) Encoding.ConfigureOperators(operators);
250    }
251
252    private void UpdateParameterValues() {
253      Permutation lbSolution;
254      // calculate the optimum of a LAP relaxation and use it as lower bound of our QAP
255      LowerBound = new DoubleValue(GilmoreLawlerBoundCalculator.CalculateLowerBound(Weights, Distances, out lbSolution));
256      // evaluate the LAP optimal solution as if it was a QAP solution
257      var lbSolutionQuality = Evaluate(lbSolution, CancellationToken.None);
258      // in case both qualities are the same it means that the LAP optimum is also a QAP optimum
259      if (LowerBound.Value.IsAlmost(lbSolutionQuality)) {
260        BestKnownSolution = lbSolution;
261        BestKnownQuality = LowerBound.Value;
262      }
263      AverageQuality = new DoubleValue(ComputeAverageQuality());
264    }
265
266    private double ComputeAverageQuality() {
267      double rt = 0, rd = 0, wt = 0, wd = 0;
268      int n = Weights.Rows;
269      for (int i = 0; i < n; i++)
270        for (int j = 0; j < n; j++) {
271          if (i == j) {
272            rd += Distances[i, i];
273            wd += Weights[i, i];
274          } else {
275            rt += Distances[i, j];
276            wt += Weights[i, j];
277          }
278        }
279
280      return rt * wt / (n * (n - 1)) + rd * wd / n;
281    }
282    #endregion
283
284    public void Load(QAPData data) {
285      if (data.Dimension > ProblemSizeLimit) throw new System.IO.InvalidDataException("The problem is limited to instance of size " + ProblemSizeLimit + ". You can change this limit by modifying " + nameof(QuadraticAssignmentProblem) + "." + nameof(ProblemSizeLimit) + "!");
286      var weights = new DoubleMatrix(data.Weights, @readonly: true);
287      var distances = new DoubleMatrix(data.Distances, @readonly: true);
288      Name = data.Name;
289      Description = data.Description;
290      Load(weights, distances);
291      if (data.BestKnownQuality.HasValue) BestKnownQuality = data.BestKnownQuality.Value;
292      EvaluateAndLoadAssignment(data.BestKnownAssignment);
293      OnReset();
294    }
295
296    public void Load(TSPData data) {
297      if (data.Dimension > ProblemSizeLimit) throw new System.IO.InvalidDataException("The problem is limited to instance of size " + ProblemSizeLimit + ". You can change this limit by modifying " + nameof(QuadraticAssignmentProblem) + "." + nameof(ProblemSizeLimit) + "!");
298      var w = new double[data.Dimension, data.Dimension];
299      for (int i = 0; i < data.Dimension; i++)
300        w[i, (i + 1) % data.Dimension] = 1;
301      var weights = new DoubleMatrix(w, @readonly: true);
302      var distances = new DoubleMatrix(data.GetDistanceMatrix(), @readonly: true);
303      Name = data.Name;
304      Description = data.Description;
305      Load(weights, distances);
306      if (data.BestKnownQuality.HasValue) BestKnownQuality = data.BestKnownQuality.Value;
307      EvaluateAndLoadAssignment(data.BestKnownTour);
308      OnReset();
309    }
310
311    public void Load(DoubleMatrix weights, DoubleMatrix distances) {
312      if (weights == null || weights.Rows == 0)
313        throw new System.IO.InvalidDataException("The given instance does not contain weights!");
314      if (weights.Rows != weights.Columns)
315        throw new System.IO.InvalidDataException("The weights matrix is not a square matrix!");
316      if (distances == null || distances.Rows == 0)
317        throw new System.IO.InvalidDataException("The given instance does not contain distances!");
318      if (distances.Rows != distances.Columns)
319        throw new System.IO.InvalidDataException("The distances matrix is not a square matrix!");
320      if (weights.Rows != distances.Columns)
321        throw new System.IO.InvalidDataException("The weights matrix and the distance matrix are not of equal size!");
322
323      Weights = weights;
324      Distances = distances;
325      Dimension = weights.Rows;
326
327      BestKnownQualityParameter.Value = null;
328      BestKnownSolution = null;
329      BestKnownSolutions = null;
330      UpdateParameterValues();
331    }
332
333    public void EvaluateAndLoadAssignment(int[] assignment) {
334      if (assignment == null || assignment.Length == 0) return;
335      var vector = new Permutation(PermutationTypes.Absolute, assignment);
336      var result = Evaluate(vector, CancellationToken.None);
337      BestKnownQuality = result;
338      BestKnownSolution = vector;
339      BestKnownSolutions = new ItemSet<Permutation> { (Permutation)vector.Clone() };
340    }
341
342    public QAPData Export() {
343      return new QAPData() {
344        Name = Name,
345        Description = Description,
346        Dimension = Weights.Rows,
347        Weights = Weights.CloneAsMatrix(),
348        Distances = Distances.CloneAsMatrix(),
349        BestKnownAssignment = BestKnownSolution?.ToArray(),
350        BestKnownQuality = !double.IsNaN(BestKnownQuality) ? BestKnownQuality : (double?)null
351      };
352    }
353  }
354}
Note: See TracBrowser for help on using the repository browser.