Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ProblemRefactoring/HeuristicLab.Problems.QuadraticAssignment/3.3/QuadraticAssignmentProblem.cs @ 14778

Last change on this file since 14778 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
RevLine 
[5558]1#region License Information
2/* HeuristicLab
[12012]3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[5558]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;
[7626]23using System.Collections.Generic;
[5558]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;
[5562]33using HeuristicLab.PluginInfrastructure;
[7558]34using HeuristicLab.Problems.Instances;
[5558]35
36namespace HeuristicLab.Problems.QuadraticAssignment {
[13173]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.")]
[12504]38  [Creatable(CreatableAttribute.Categories.CombinatorialProblems, Priority = 140)]
[5558]39  [StorableClass]
[13396]40  public sealed class QuadraticAssignmentProblem : SingleObjectiveProblem<PermutationEncoding, Permutation>,
[7558]41    IProblemInstanceConsumer<QAPData>,
42    IProblemInstanceConsumer<TSPData> {
[5953]43
[7201]44    public static new Image StaticItemImage {
[13396]45      get { return Common.Resources.VSImageLibrary.Type; }
[5558]46    }
47
[13396]48    public override bool Maximization { get { return false; } }
49
[5558]50    #region Parameter Properties
[6525]51    public IValueParameter<ItemSet<Permutation>> BestKnownSolutionsParameter {
52      get { return (IValueParameter<ItemSet<Permutation>>)Parameters["BestKnownSolutions"]; }
[6342]53    }
[5641]54    public IValueParameter<Permutation> BestKnownSolutionParameter {
55      get { return (IValueParameter<Permutation>)Parameters["BestKnownSolution"]; }
[5558]56    }
[5641]57    public IValueParameter<DoubleMatrix> WeightsParameter {
58      get { return (IValueParameter<DoubleMatrix>)Parameters["Weights"]; }
[5558]59    }
[5838]60    public IValueParameter<DoubleMatrix> DistancesParameter {
61      get { return (IValueParameter<DoubleMatrix>)Parameters["Distances"]; }
[5558]62    }
[7877]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    }
[5558]69    #endregion
70
71    #region Properties
[6525]72    public ItemSet<Permutation> BestKnownSolutions {
[6342]73      get { return BestKnownSolutionsParameter.Value; }
74      set { BestKnownSolutionsParameter.Value = value; }
75    }
[5558]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    }
[5838]84    public DoubleMatrix Distances {
85      get { return DistancesParameter.Value; }
86      set { DistancesParameter.Value = value; }
[5558]87    }
[7877]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    }
[5563]96
[5583]97    private BestQAPSolutionAnalyzer BestQAPSolutionAnalyzer {
98      get { return Operators.OfType<BestQAPSolutionAnalyzer>().FirstOrDefault(); }
99    }
[6342]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    }
[5558]108    #endregion
109
110    [StorableConstructor]
111    private QuadraticAssignmentProblem(bool deserializing) : base(deserializing) { }
[5562]112    private QuadraticAssignmentProblem(QuadraticAssignmentProblem original, Cloner cloner)
[5558]113      : base(original, cloner) {
[7351]114      RegisterEventHandlers();
[5558]115    }
116    public QuadraticAssignmentProblem()
[13396]117      : base(new PermutationEncoding("Assignment") { Length = 5 }) {
[6525]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));
[5648]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));
[5558]120      Parameters.Add(new ValueParameter<DoubleMatrix>("Weights", "The strength of the connection between the facilities.", new DoubleMatrix(5, 5)));
[5838]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)));
[7877]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."));
[5558]124
[7877]125      WeightsParameter.GetsCollected = false;
[5558]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
[7877]134      DistancesParameter.GetsCollected = false;
[5838]135      Distances = new DoubleMatrix(new double[,] {
[5558]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();
[7351]144      RegisterEventHandlers();
[5558]145    }
146
[13396]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
[5558]161    public override IDeepCloneable Clone(Cloner cloner) {
162      return new QuadraticAssignmentProblem(this, cloner);
163    }
164
[6342]165    [StorableHook(HookType.AfterDeserialization)]
166    private void AfterDeserialization() {
[7351]167      RegisterEventHandlers();
[6342]168    }
169
[5558]170    #region Events
[13469]171    //TODO check with abhem if this is necessary
172    //protected override void OnSolutionCreatorChanged() {
173    //  Parameterize();
174    //  base.OnSolutionCreatorChanged();
175    //}
[5562]176    protected override void OnEvaluatorChanged() {
[13396]177      Evaluator.QualityParameter.ActualNameChanged += Evaluator_QualityParameter_ActualNameChanged;
178      Parameterize();
[5562]179      base.OnEvaluatorChanged();
[5558]180    }
[5583]181    private void Evaluator_QualityParameter_ActualNameChanged(object sender, EventArgs e) {
[13396]182      Parameterize();
[5583]183    }
[5562]184    private void WeightsParameter_ValueChanged(object sender, EventArgs e) {
[13396]185      Weights.RowsChanged += Weights_RowsChanged;
186      Weights.ColumnsChanged += Weights_ColumnsChanged;
187      Weights.ToStringChanged += Weights_ToStringChanged;
[5855]188      AdjustDistanceMatrix();
[5558]189    }
[5562]190    private void Weights_RowsChanged(object sender, EventArgs e) {
[5855]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    }
[7877]204    private void Weights_ToStringChanged(object sender, EventArgs e) {
205      UpdateParameterValues();
206    }
[5855]207    private void DistancesParameter_ValueChanged(object sender, EventArgs e) {
[13396]208      Distances.RowsChanged += Distances_RowsChanged;
209      Distances.ColumnsChanged += Distances_ColumnsChanged;
210      Distances.ToStringChanged += Distances_ToStringChanged;
[5855]211      AdjustWeightsMatrix();
[5562]212    }
[5855]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    }
[7877]227    private void Distances_ToStringChanged(object sender, EventArgs e) {
228      UpdateParameterValues();
229    }
[5558]230    #endregion
231
[7351]232    private void RegisterEventHandlers() {
[13396]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;
[5558]241    }
242
[7877]243    #region Helpers
[5558]244    private void InitializeOperators() {
[7626]245      var defaultOperators = new HashSet<IPermutationOperator>(new IPermutationOperator[] {
246        new PartiallyMatchedCrossover(),
247        new Swap2Manipulator(),
248        new ExhaustiveSwap2MoveGenerator()
249      });
250      Operators.AddRange(defaultOperators);
[6088]251      Operators.AddRange(ApplicationManager.Manager.GetInstances<IQAPMoveEvaluator>());
[13396]252      Operators.AddRange(ApplicationManager.Manager.GetInstances<IQAPLocalImprovementOperator>());
[5583]253      Operators.Add(new BestQAPSolutionAnalyzer());
[6342]254      Operators.Add(new QAPAlleleFrequencyAnalyzer());
255      Operators.Add(new QAPPopulationDiversityAnalyzer());
[11300]256
[9029]257      Operators.Add(new QAPSimilarityCalculator());
[13396]258      Parameterize();
[5558]259    }
[13396]260    private void Parameterize() {
261      var operators = new List<IItem>();
[5583]262      if (BestQAPSolutionAnalyzer != null) {
[13396]263        operators.Add(BestQAPSolutionAnalyzer);
[5583]264        BestQAPSolutionAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
[5838]265        BestQAPSolutionAnalyzer.DistancesParameter.ActualName = DistancesParameter.Name;
[5583]266        BestQAPSolutionAnalyzer.WeightsParameter.ActualName = WeightsParameter.Name;
267        BestQAPSolutionAnalyzer.BestKnownQualityParameter.ActualName = BestKnownQualityParameter.Name;
[6342]268        BestQAPSolutionAnalyzer.BestKnownSolutionsParameter.ActualName = BestKnownSolutionsParameter.Name;
[5583]269        BestQAPSolutionAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
270      }
[6342]271      if (QAPAlleleFrequencyAnalyzer != null) {
[13396]272        operators.Add(QAPAlleleFrequencyAnalyzer);
[6342]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) {
[13396]280        operators.Add(QAPPopulationDiversityAnalyzer);
[6342]281        QAPPopulationDiversityAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
282        QAPPopulationDiversityAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
283      }
[13396]284      foreach (var localOpt in Operators.OfType<IQAPLocalImprovementOperator>()) {
285        operators.Add(localOpt);
[6342]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      }
[9029]291
[13396]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();
[9029]324      if (similarityCalculator != null) {
[13396]325        similarityCalculator.SolutionVariableName = Encoding.Name;
[9029]326        similarityCalculator.QualityVariableName = Evaluator.QualityParameter.ActualName;
327      }
[13396]328
329      if (operators.Count > 0) Encoding.ConfigureOperators(operators);
[5562]330    }
[5598]331
[5855]332    private void AdjustDistanceMatrix() {
333      if (Distances.Rows != Weights.Rows || Distances.Columns != Weights.Columns) {
334        ((IStringConvertibleMatrix)Distances).Rows = Weights.Rows;
[13396]335        Encoding.Length = Weights.Rows;
[5855]336      }
337    }
338
339    private void AdjustWeightsMatrix() {
340      if (Weights.Rows != Distances.Rows || Weights.Columns != Distances.Columns) {
341        ((IStringConvertibleMatrix)Weights).Rows = Distances.Rows;
[13396]342        Encoding.Length = Distances.Rows;
[5855]343      }
344    }
[7877]345
346    private void UpdateParameterValues() {
[8910]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
[13396]351      var lbSolutionQuality = Evaluate(lbSolution);
[8910]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;
[13396]355        BestKnownQuality = LowerBound.Value;
[8910]356      }
357      AverageQuality = new DoubleValue(ComputeAverageQuality());
[7877]358    }
[8910]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    }
[5558]376    #endregion
[5562]377
[7558]378    public void Load(QAPData data) {
379      var weights = new DoubleMatrix(data.Weights);
380      var distances = new DoubleMatrix(data.Distances);
[7641]381      Name = data.Name;
382      Description = data.Description;
[7558]383      Load(weights, distances);
[13396]384      if (data.BestKnownQuality.HasValue) BestKnownQuality = data.BestKnownQuality.Value;
[7558]385      EvaluateAndLoadAssignment(data.BestKnownAssignment);
[5562]386      OnReset();
387    }
[5563]388
[7558]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());
[7641]396      Name = data.Name;
397      Description = data.Description;
[7558]398      Load(weights, distances);
[13396]399      if (data.BestKnownQuality.HasValue) BestKnownQuality = data.BestKnownQuality.Value;
[7558]400      EvaluateAndLoadAssignment(data.BestKnownTour);
[6891]401      OnReset();
[5563]402    }
[6952]403
[7558]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!");
[6952]415
[7558]416      Weights = weights;
417      Distances = distances;
[13396]418      Encoding.Length = weights.Rows;
[6952]419
[13396]420      BestKnownQualityParameter.Value = null;
[7558]421      BestKnownSolution = null;
422      BestKnownSolutions = null;
[7877]423      UpdateParameterValues();
[7558]424    }
[6952]425
[7558]426    public void EvaluateAndLoadAssignment(int[] assignment) {
427      if (assignment == null || assignment.Length == 0) return;
428      var vector = new Permutation(PermutationTypes.Absolute, assignment);
[13396]429      var result = Evaluate(vector);
430      BestKnownQuality = result;
[7558]431      BestKnownSolution = vector;
[13396]432      BestKnownSolutions = new ItemSet<Permutation> { (Permutation)vector.Clone() };
[6952]433    }
[5558]434  }
435}
Note: See TracBrowser for help on using the repository browser.