Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ScopedAlgorithms/HeuristicLab.Problems.QuadraticAssignment/3.3/QuadraticAssignmentProblem.cs @ 14877

Last change on this file since 14877 was 13469, checked in by mkommend, 9 years ago

#2521: Refactored problem base classes and adapted scheduling encoding, scheduling problem and unit tests.

File size: 19.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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 HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Data;
29using HeuristicLab.Encodings.PermutationEncoding;
30using HeuristicLab.Optimization;
31using HeuristicLab.Parameters;
32using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
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  [StorableClass]
40  public sealed class QuadraticAssignmentProblem : SingleObjectiveProblem<PermutationEncoding, Permutation>,
41    IProblemInstanceConsumer<QAPData>,
42    IProblemInstanceConsumer<TSPData> {
43
44    public static new Image StaticItemImage {
45      get { return Common.Resources.VSImageLibrary.Type; }
46    }
47
48    public override bool Maximization { get { return false; } }
49
50    #region Parameter Properties
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<DoubleMatrix> WeightsParameter {
58      get { return (IValueParameter<DoubleMatrix>)Parameters["Weights"]; }
59    }
60    public IValueParameter<DoubleMatrix> DistancesParameter {
61      get { return (IValueParameter<DoubleMatrix>)Parameters["Distances"]; }
62    }
63    public IValueParameter<DoubleValue> LowerBoundParameter {
64      get { return (IValueParameter<DoubleValue>)Parameters["LowerBound"]; }
65    }
66    public IValueParameter<DoubleValue> AverageQualityParameter {
67      get { return (IValueParameter<DoubleValue>)Parameters["AverageQuality"]; }
68    }
69    #endregion
70
71    #region Properties
72    public ItemSet<Permutation> BestKnownSolutions {
73      get { return BestKnownSolutionsParameter.Value; }
74      set { BestKnownSolutionsParameter.Value = value; }
75    }
76    public Permutation BestKnownSolution {
77      get { return BestKnownSolutionParameter.Value; }
78      set { BestKnownSolutionParameter.Value = value; }
79    }
80    public DoubleMatrix Weights {
81      get { return WeightsParameter.Value; }
82      set { WeightsParameter.Value = value; }
83    }
84    public DoubleMatrix Distances {
85      get { return DistancesParameter.Value; }
86      set { DistancesParameter.Value = value; }
87    }
88    public DoubleValue LowerBound {
89      get { return LowerBoundParameter.Value; }
90      set { LowerBoundParameter.Value = value; }
91    }
92    public DoubleValue AverageQuality {
93      get { return AverageQualityParameter.Value; }
94      set { AverageQualityParameter.Value = value; }
95    }
96
97    private BestQAPSolutionAnalyzer BestQAPSolutionAnalyzer {
98      get { return Operators.OfType<BestQAPSolutionAnalyzer>().FirstOrDefault(); }
99    }
100
101    private QAPAlleleFrequencyAnalyzer QAPAlleleFrequencyAnalyzer {
102      get { return Operators.OfType<QAPAlleleFrequencyAnalyzer>().FirstOrDefault(); }
103    }
104
105    private QAPPopulationDiversityAnalyzer QAPPopulationDiversityAnalyzer {
106      get { return Operators.OfType<QAPPopulationDiversityAnalyzer>().FirstOrDefault(); }
107    }
108    #endregion
109
110    [StorableConstructor]
111    private QuadraticAssignmentProblem(bool deserializing) : base(deserializing) { }
112    private QuadraticAssignmentProblem(QuadraticAssignmentProblem original, Cloner cloner)
113      : base(original, cloner) {
114      RegisterEventHandlers();
115    }
116    public QuadraticAssignmentProblem()
117      : base(new PermutationEncoding("Assignment") { Length = 5 }) {
118      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));
119      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));
120      Parameters.Add(new ValueParameter<DoubleMatrix>("Weights", "The strength of the connection between the facilities.", new DoubleMatrix(5, 5)));
121      Parameters.Add(new ValueParameter<DoubleMatrix>("Distances", "The distance matrix which can either be specified directly without the coordinates, or can be calculated automatically from the coordinates.", new DoubleMatrix(5, 5)));
122      Parameters.Add(new OptionalValueParameter<DoubleValue>("LowerBound", "The Gilmore-Lawler lower bound to the solution quality."));
123      Parameters.Add(new OptionalValueParameter<DoubleValue>("AverageQuality", "The expected quality of a random solution."));
124
125      WeightsParameter.GetsCollected = false;
126      Weights = new DoubleMatrix(new double[,] {
127        { 0, 1, 0, 0, 1 },
128        { 1, 0, 1, 0, 0 },
129        { 0, 1, 0, 1, 0 },
130        { 0, 0, 1, 0, 1 },
131        { 1, 0, 0, 1, 0 }
132      });
133
134      DistancesParameter.GetsCollected = false;
135      Distances = new DoubleMatrix(new double[,] {
136        {   0, 360, 582, 582, 360 },
137        { 360,   0, 360, 582, 582 },
138        { 582, 360,   0, 360, 582 },
139        { 582, 582, 360,   0, 360 },
140        { 360, 582, 582, 360,   0 }
141      });
142
143      InitializeOperators();
144      RegisterEventHandlers();
145    }
146
147    public override double Evaluate(Permutation assignment, IRandom random) {
148      return Evaluate(assignment);
149    }
150
151    public double Evaluate(Permutation assignment) {
152      double quality = 0;
153      for (int i = 0; i < assignment.Length; i++) {
154        for (int j = 0; j < assignment.Length; j++) {
155          quality += Weights[i, j] * Distances[assignment[i], assignment[j]];
156        }
157      }
158      return quality;
159    }
160
161    public override IDeepCloneable Clone(Cloner cloner) {
162      return new QuadraticAssignmentProblem(this, cloner);
163    }
164
165    [StorableHook(HookType.AfterDeserialization)]
166    private void AfterDeserialization() {
167      RegisterEventHandlers();
168    }
169
170    #region Events
171    //TODO check with abhem if this is necessary
172    //protected override void OnSolutionCreatorChanged() {
173    //  Parameterize();
174    //  base.OnSolutionCreatorChanged();
175    //}
176    protected override void OnEvaluatorChanged() {
177      Evaluator.QualityParameter.ActualNameChanged += Evaluator_QualityParameter_ActualNameChanged;
178      Parameterize();
179      base.OnEvaluatorChanged();
180    }
181    private void Evaluator_QualityParameter_ActualNameChanged(object sender, EventArgs e) {
182      Parameterize();
183    }
184    private void WeightsParameter_ValueChanged(object sender, EventArgs e) {
185      Weights.RowsChanged += Weights_RowsChanged;
186      Weights.ColumnsChanged += Weights_ColumnsChanged;
187      Weights.ToStringChanged += Weights_ToStringChanged;
188      AdjustDistanceMatrix();
189    }
190    private void Weights_RowsChanged(object sender, EventArgs e) {
191      if (Weights.Rows != Weights.Columns)
192        ((IStringConvertibleMatrix)Weights).Columns = Weights.Rows;
193      else {
194        AdjustDistanceMatrix();
195      }
196    }
197    private void Weights_ColumnsChanged(object sender, EventArgs e) {
198      if (Weights.Rows != Weights.Columns)
199        ((IStringConvertibleMatrix)Weights).Rows = Weights.Columns;
200      else {
201        AdjustDistanceMatrix();
202      }
203    }
204    private void Weights_ToStringChanged(object sender, EventArgs e) {
205      UpdateParameterValues();
206    }
207    private void DistancesParameter_ValueChanged(object sender, EventArgs e) {
208      Distances.RowsChanged += Distances_RowsChanged;
209      Distances.ColumnsChanged += Distances_ColumnsChanged;
210      Distances.ToStringChanged += Distances_ToStringChanged;
211      AdjustWeightsMatrix();
212    }
213    private void Distances_RowsChanged(object sender, EventArgs e) {
214      if (Distances.Rows != Distances.Columns)
215        ((IStringConvertibleMatrix)Distances).Columns = Distances.Rows;
216      else {
217        AdjustWeightsMatrix();
218      }
219    }
220    private void Distances_ColumnsChanged(object sender, EventArgs e) {
221      if (Distances.Rows != Distances.Columns)
222        ((IStringConvertibleMatrix)Distances).Rows = Distances.Columns;
223      else {
224        AdjustWeightsMatrix();
225      }
226    }
227    private void Distances_ToStringChanged(object sender, EventArgs e) {
228      UpdateParameterValues();
229    }
230    #endregion
231
232    private void RegisterEventHandlers() {
233      WeightsParameter.ValueChanged += WeightsParameter_ValueChanged;
234      Weights.RowsChanged += Weights_RowsChanged;
235      Weights.ColumnsChanged += Weights_ColumnsChanged;
236      Weights.ToStringChanged += Weights_ToStringChanged;
237      DistancesParameter.ValueChanged += DistancesParameter_ValueChanged;
238      Distances.RowsChanged += Distances_RowsChanged;
239      Distances.ColumnsChanged += Distances_ColumnsChanged;
240      Distances.ToStringChanged += Distances_ToStringChanged;
241    }
242
243    #region Helpers
244    private void InitializeOperators() {
245      var defaultOperators = new HashSet<IPermutationOperator>(new IPermutationOperator[] {
246        new PartiallyMatchedCrossover(),
247        new Swap2Manipulator(),
248        new ExhaustiveSwap2MoveGenerator()
249      });
250      Operators.AddRange(defaultOperators);
251      Operators.AddRange(ApplicationManager.Manager.GetInstances<IQAPMoveEvaluator>());
252      Operators.AddRange(ApplicationManager.Manager.GetInstances<IQAPLocalImprovementOperator>());
253      Operators.Add(new BestQAPSolutionAnalyzer());
254      Operators.Add(new QAPAlleleFrequencyAnalyzer());
255      Operators.Add(new QAPPopulationDiversityAnalyzer());
256
257      Operators.Add(new QAPSimilarityCalculator());
258      Parameterize();
259    }
260    private void Parameterize() {
261      var operators = new List<IItem>();
262      if (BestQAPSolutionAnalyzer != null) {
263        operators.Add(BestQAPSolutionAnalyzer);
264        BestQAPSolutionAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
265        BestQAPSolutionAnalyzer.DistancesParameter.ActualName = DistancesParameter.Name;
266        BestQAPSolutionAnalyzer.WeightsParameter.ActualName = WeightsParameter.Name;
267        BestQAPSolutionAnalyzer.BestKnownQualityParameter.ActualName = BestKnownQualityParameter.Name;
268        BestQAPSolutionAnalyzer.BestKnownSolutionsParameter.ActualName = BestKnownSolutionsParameter.Name;
269        BestQAPSolutionAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
270      }
271      if (QAPAlleleFrequencyAnalyzer != null) {
272        operators.Add(QAPAlleleFrequencyAnalyzer);
273        QAPAlleleFrequencyAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
274        QAPAlleleFrequencyAnalyzer.BestKnownSolutionParameter.ActualName = BestKnownSolutionParameter.Name;
275        QAPAlleleFrequencyAnalyzer.DistancesParameter.ActualName = DistancesParameter.Name;
276        QAPAlleleFrequencyAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
277        QAPAlleleFrequencyAnalyzer.WeightsParameter.ActualName = WeightsParameter.Name;
278      }
279      if (QAPPopulationDiversityAnalyzer != null) {
280        operators.Add(QAPPopulationDiversityAnalyzer);
281        QAPPopulationDiversityAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
282        QAPPopulationDiversityAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
283      }
284      foreach (var localOpt in Operators.OfType<IQAPLocalImprovementOperator>()) {
285        operators.Add(localOpt);
286        localOpt.DistancesParameter.ActualName = DistancesParameter.Name;
287        localOpt.MaximizationParameter.ActualName = MaximizationParameter.Name;
288        localOpt.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
289        localOpt.WeightsParameter.ActualName = WeightsParameter.Name;
290      }
291
292      foreach (var moveOp in Operators.OfType<IQAPMoveEvaluator>()) {
293        operators.Add(moveOp);
294        moveOp.DistancesParameter.ActualName = DistancesParameter.Name;
295        moveOp.WeightsParameter.ActualName = WeightsParameter.Name;
296        moveOp.QualityParameter.ActualName = Evaluator.QualityParameter.Name;
297
298        var swaMoveOp = moveOp as QAPSwap2MoveEvaluator;
299        if (swaMoveOp != null) {
300          var moveQualityName = swaMoveOp.MoveQualityParameter.ActualName;
301          foreach (var o in Encoding.Operators.OfType<IPermutationSwap2MoveQualityOperator>())
302            o.MoveQualityParameter.ActualName = moveQualityName;
303        }
304        var invMoveOp = moveOp as QAPInversionMoveEvaluator;
305        if (invMoveOp != null) {
306          var moveQualityName = invMoveOp.MoveQualityParameter.ActualName;
307          foreach (var o in Encoding.Operators.OfType<IPermutationInversionMoveQualityOperator>())
308            o.MoveQualityParameter.ActualName = moveQualityName;
309        }
310        var traMoveOp = moveOp as QAPTranslocationMoveEvaluator;
311        if (traMoveOp != null) {
312          var moveQualityName = traMoveOp.MoveQualityParameter.ActualName;
313          foreach (var o in Encoding.Operators.OfType<IPermutationTranslocationMoveQualityOperator>())
314            o.MoveQualityParameter.ActualName = moveQualityName;
315        }
316        var scrMoveOp = moveOp as QAPScrambleMoveEvaluator;
317        if (scrMoveOp != null) {
318          var moveQualityName = scrMoveOp.MoveQualityParameter.ActualName;
319          foreach (var o in Encoding.Operators.OfType<IPermutationScrambleMoveQualityOperator>())
320            o.MoveQualityParameter.ActualName = moveQualityName;
321        }
322      }
323      var similarityCalculator = Operators.OfType<QAPSimilarityCalculator>().SingleOrDefault();
324      if (similarityCalculator != null) {
325        similarityCalculator.SolutionVariableName = Encoding.Name;
326        similarityCalculator.QualityVariableName = Evaluator.QualityParameter.ActualName;
327      }
328
329      if (operators.Count > 0) Encoding.ConfigureOperators(operators);
330    }
331
332    private void AdjustDistanceMatrix() {
333      if (Distances.Rows != Weights.Rows || Distances.Columns != Weights.Columns) {
334        ((IStringConvertibleMatrix)Distances).Rows = Weights.Rows;
335        Encoding.Length = Weights.Rows;
336      }
337    }
338
339    private void AdjustWeightsMatrix() {
340      if (Weights.Rows != Distances.Rows || Weights.Columns != Distances.Columns) {
341        ((IStringConvertibleMatrix)Weights).Rows = Distances.Rows;
342        Encoding.Length = Distances.Rows;
343      }
344    }
345
346    private void UpdateParameterValues() {
347      Permutation lbSolution;
348      // calculate the optimum of a LAP relaxation and use it as lower bound of our QAP
349      LowerBound = new DoubleValue(GilmoreLawlerBoundCalculator.CalculateLowerBound(Weights, Distances, out lbSolution));
350      // evalute the LAP optimal solution as if it was a QAP solution
351      var lbSolutionQuality = Evaluate(lbSolution);
352      // in case both qualities are the same it means that the LAP optimum is also a QAP optimum
353      if (LowerBound.Value.IsAlmost(lbSolutionQuality)) {
354        BestKnownSolution = lbSolution;
355        BestKnownQuality = LowerBound.Value;
356      }
357      AverageQuality = new DoubleValue(ComputeAverageQuality());
358    }
359
360    private double ComputeAverageQuality() {
361      double rt = 0, rd = 0, wt = 0, wd = 0;
362      int n = Weights.Rows;
363      for (int i = 0; i < n; i++)
364        for (int j = 0; j < n; j++) {
365          if (i == j) {
366            rd += Distances[i, i];
367            wd += Weights[i, i];
368          } else {
369            rt += Distances[i, j];
370            wt += Weights[i, j];
371          }
372        }
373
374      return rt * wt / (n * (n - 1)) + rd * wd / n;
375    }
376    #endregion
377
378    public void Load(QAPData data) {
379      var weights = new DoubleMatrix(data.Weights);
380      var distances = new DoubleMatrix(data.Distances);
381      Name = data.Name;
382      Description = data.Description;
383      Load(weights, distances);
384      if (data.BestKnownQuality.HasValue) BestKnownQuality = data.BestKnownQuality.Value;
385      EvaluateAndLoadAssignment(data.BestKnownAssignment);
386      OnReset();
387    }
388
389    public void Load(TSPData data) {
390      if (data.Dimension > 1000)
391        throw new System.IO.InvalidDataException("Instances with more than 1000 customers are not supported by the QAP.");
392      var weights = new DoubleMatrix(data.Dimension, data.Dimension);
393      for (int i = 0; i < data.Dimension; i++)
394        weights[i, (i + 1) % data.Dimension] = 1;
395      var distances = new DoubleMatrix(data.GetDistanceMatrix());
396      Name = data.Name;
397      Description = data.Description;
398      Load(weights, distances);
399      if (data.BestKnownQuality.HasValue) BestKnownQuality = data.BestKnownQuality.Value;
400      EvaluateAndLoadAssignment(data.BestKnownTour);
401      OnReset();
402    }
403
404    public void Load(DoubleMatrix weights, DoubleMatrix distances) {
405      if (weights == null || weights.Rows == 0)
406        throw new System.IO.InvalidDataException("The given instance does not contain weights!");
407      if (weights.Rows != weights.Columns)
408        throw new System.IO.InvalidDataException("The weights matrix is not a square matrix!");
409      if (distances == null || distances.Rows == 0)
410        throw new System.IO.InvalidDataException("The given instance does not contain distances!");
411      if (distances.Rows != distances.Columns)
412        throw new System.IO.InvalidDataException("The distances matrix is not a square matrix!");
413      if (weights.Rows != distances.Columns)
414        throw new System.IO.InvalidDataException("The weights matrix and the distance matrix are not of equal size!");
415
416      Weights = weights;
417      Distances = distances;
418      Encoding.Length = weights.Rows;
419
420      BestKnownQualityParameter.Value = null;
421      BestKnownSolution = null;
422      BestKnownSolutions = null;
423      UpdateParameterValues();
424    }
425
426    public void EvaluateAndLoadAssignment(int[] assignment) {
427      if (assignment == null || assignment.Length == 0) return;
428      var vector = new Permutation(PermutationTypes.Absolute, assignment);
429      var result = Evaluate(vector);
430      BestKnownQuality = result;
431      BestKnownSolution = vector;
432      BestKnownSolutions = new ItemSet<Permutation> { (Permutation)vector.Clone() };
433    }
434  }
435}
Note: See TracBrowser for help on using the repository browser.