Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 17270 was 17270, checked in by abeham, 5 years ago

#2521: worked on removing virtual from Maximization for single-objective problems

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