Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2936_GQAPIntegration/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/GRASP/GRASP.cs @ 17717

Last change on this file since 17717 was 16712, checked in by gkronber, 6 years ago

#2936: adapted branch to new persistence (works with HL trunk r16711)

File size: 16.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2017 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.Linq;
24using System.Threading;
25using HeuristicLab.Common;
26using HeuristicLab.Core;
27using HeuristicLab.Data;
28using HeuristicLab.Encodings.IntegerVectorEncoding;
29using HeuristicLab.Optimization;
30using HeuristicLab.Parameters;
31using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
32using HEAL.Attic;
33
34namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms {
35
36  /// <summary>
37  /// This is an implementation of the algorithm described in Mateus, G.R., Resende, M.G.C. & Silva, R.M.A. J Heuristics (2011) 17: 527. https://doi.org/10.1007/s10732-010-9144-0
38  /// </summary>
39  /// <remarks>
40  /// A fine distinction to the described algorithm is that newly found best solutions are always accepted into the elite set. This is reasonable, but not mentioned in the paper by Mateus et al.
41  /// </remarks>
42  [Item("GRASP+PR (GQAP)", "The algorithm implements the Greedy Randomized Adaptive Search Procedure (GRASP) with Path Relinking as described in Mateus, G., Resende, M., and Silva, R. 2011. GRASP with path-relinking for the generalized quadratic assignment problem. Journal of Heuristics 17, Springer Netherlands, pp. 527-565.")]
43  [Creatable(CreatableAttribute.Categories.PopulationBasedAlgorithms)]
44  [StorableType("1FF6B7FC-58CA-4F89-BCAF-31CFC0B0546C")]
45  public sealed class GRASP : StochasticAlgorithm<GRASPContext, IntegerVectorEncoding> {
46
47    public override bool SupportsPause {
48      get { return true; }
49    }
50
51    public override Type ProblemType {
52      get { return typeof(GQAP); }
53    }
54
55    public new GQAP Problem {
56      get { return (GQAP)base.Problem; }
57      set { base.Problem = value; }
58    }
59
60    [Storable]
61    private FixedValueParameter<IntValue> eliteSetSizeParameter;
62    private IFixedValueParameter<IntValue> EliteSetSizeParameter {
63      get { return eliteSetSizeParameter; }
64    }
65    [Storable]
66    private FixedValueParameter<IntValue> minimiumEliteSetSizeParameter;
67    public IFixedValueParameter<IntValue> MinimumEliteSetSizeParameter {
68      get { return minimiumEliteSetSizeParameter; }
69    }
70    [Storable]
71    private FixedValueParameter<IntValue> maximumLocalSearchIterationsParameter;
72    public IFixedValueParameter<IntValue> MaximumLocalSearchIterationsParameter {
73      get { return maximumLocalSearchIterationsParameter; }
74    }
75    [Storable]
76    private FixedValueParameter<PercentValue> candidateSizeFactorParameter;
77    public IFixedValueParameter<PercentValue> CandidateSizeFactorParameter {
78      get { return candidateSizeFactorParameter; }
79    }
80    [Storable]
81    private FixedValueParameter<IntValue> maximumCandidateListSizeParameter;
82    public IFixedValueParameter<IntValue> MaximumCandidateListSizeParameter {
83      get { return maximumCandidateListSizeParameter; }
84    }
85    [Storable]
86    private FixedValueParameter<PercentValue> oneMoveProbabilityParameter;
87    public IFixedValueParameter<PercentValue> OneMoveProbabilityParameter {
88      get { return oneMoveProbabilityParameter; }
89    }
90    [Storable]
91    private FixedValueParameter<IntValue> minimumDifferenceParameter;
92    public IFixedValueParameter<IntValue> MinimumDifferenceParameter {
93      get { return minimumDifferenceParameter; }
94    }
95   
96    public int EliteSetSize {
97      get { return eliteSetSizeParameter.Value.Value; }
98      set { eliteSetSizeParameter.Value.Value = value; }
99    }
100    public int MinimumEliteSetSize {
101      get { return minimiumEliteSetSizeParameter.Value.Value; }
102      set { minimiumEliteSetSizeParameter.Value.Value = value; }
103    }
104    public int MaximumLocalSearchIterations {
105      get { return maximumLocalSearchIterationsParameter.Value.Value; }
106      set { maximumLocalSearchIterationsParameter.Value.Value = value; }
107    }
108    public double CandidateSizeFactor {
109      get { return candidateSizeFactorParameter.Value.Value; }
110      set { candidateSizeFactorParameter.Value.Value = value; }
111    }
112    public int MaximumCandidateListSize {
113      get { return maximumCandidateListSizeParameter.Value.Value; }
114      set { maximumCandidateListSizeParameter.Value.Value = value; }
115    }
116    public double OneMoveProbability {
117      get { return oneMoveProbabilityParameter.Value.Value; }
118      set { oneMoveProbabilityParameter.Value.Value = value; }
119    }
120    public int MinimumDifference {
121      get { return minimumDifferenceParameter.Value.Value; }
122      set { minimumDifferenceParameter.Value.Value = value; }
123    }
124
125    [StorableConstructor]
126    private GRASP(StorableConstructorFlag _) : base(_) { }
127    private GRASP(GRASP original, Cloner cloner)
128      : base(original, cloner) {
129      eliteSetSizeParameter = cloner.Clone(original.eliteSetSizeParameter);
130      minimiumEliteSetSizeParameter = cloner.Clone(original.minimiumEliteSetSizeParameter);
131      maximumLocalSearchIterationsParameter = cloner.Clone(original.maximumLocalSearchIterationsParameter);
132      candidateSizeFactorParameter = cloner.Clone(original.candidateSizeFactorParameter);
133      maximumCandidateListSizeParameter = cloner.Clone(original.maximumCandidateListSizeParameter);
134      oneMoveProbabilityParameter = cloner.Clone(original.oneMoveProbabilityParameter);
135      minimumDifferenceParameter = cloner.Clone(original.minimumDifferenceParameter);
136    }
137    public GRASP() {
138      Parameters.Add(eliteSetSizeParameter = new FixedValueParameter<IntValue>("EliteSetSize", "The (maximum) size of the elite set.", new IntValue(10)));
139      Parameters.Add(minimiumEliteSetSizeParameter = new FixedValueParameter<IntValue>("MinimumEliteSetSize", "(ρ) The minimal size of the elite set, before local search and path relinking are applied.", new IntValue(2)));
140      Parameters.Add(maximumLocalSearchIterationsParameter = new FixedValueParameter<IntValue>("MaximumLocalSearchIteration", "The maximum number of iterations that the approximate local search should run", new IntValue(100)));
141      Parameters.Add(candidateSizeFactorParameter = new FixedValueParameter<PercentValue>("CandidateSizeFactor", "(η) Determines the size of the set of feasible moves in each path - relinking step relative to the maximum size.A value of 50 % means that only half of all possible moves are considered each step.", new PercentValue(0.5)));
142      Parameters.Add(maximumCandidateListSizeParameter = new FixedValueParameter<IntValue>("MaximumCandidateListSize", "The maximum number of candidates that should be found in each step.", new IntValue(10)));
143      Parameters.Add(oneMoveProbabilityParameter = new FixedValueParameter<PercentValue>("OneMoveProbability", "The probability for performing a 1-move, which is the opposite of performing a 2-move.", new PercentValue(.5)));
144      Parameters.Add(minimumDifferenceParameter = new FixedValueParameter<IntValue>("MinimumDifference", "The minimum amount of difference between two solutions so that they are both accepted in the elite set.", new IntValue(4)));
145
146      Problem = new GQAP();
147    }
148
149    public override IDeepCloneable Clone(Cloner cloner) {
150      return new GRASP(this, cloner);
151    }
152
153    protected override void Initialize(CancellationToken cancellationToken) {
154      base.Initialize(cancellationToken);
155
156      Context.Problem = Problem;
157      Context.BestSolution = null;
158
159      Results.Add(new Result("Iterations", new IntValue(Context.Iterations)));
160      Results.Add(new Result("EvaluatedSolutions", new IntValue(Context.EvaluatedSolutions)));
161      Results.Add(new Result("BestQuality", new DoubleValue(Context.BestQuality)));
162      Results.Add(new Result("BestSolution", typeof(GQAPAssignment)));
163    }
164
165    protected override void Run(CancellationToken cancellationToken) {
166      base.Run(cancellationToken);
167      var eq = new IntegerVectorEqualityComparer();
168      var lastUpdate = ExecutionTime;
169      while (!StoppingCriterion()) { // line 2 in Algorithm 1
170        // next: line 3 in Algorithm 1
171        IntegerVector pi_prime_vec = null;
172        try {
173          pi_prime_vec = GreedyRandomizedSolutionCreator.CreateSolution(Context.Random, Problem.ProblemInstance, 10, true, cancellationToken);
174        } catch (OperationCanceledException) { break; }
175
176        if (Context.PopulationCount >= MinimumEliteSetSize) { // line 4 in Algorithm 1
177          GQAPSolution pi_prime;
178          if (!Problem.ProblemInstance.IsFeasible(pi_prime_vec)) // line 5 in Algorithm 1
179            pi_prime = (GQAPSolution)Context.AtPopulation(Context.Random.Next(Context.PopulationCount)).Solution.Clone(); // line 6 in Algorithm 1
180          else {
181            // This is necessary, because pi_prime has not been evaluated yet and such details are not covered in Algorithm 1
182            pi_prime = Problem.ProblemInstance.ToEvaluatedSolution(pi_prime_vec);
183            Context.EvaluatedSolutions++;
184          }
185
186          ApproxLocalSearch(pi_prime); // line 8 in Algorithm 1
187          var pi_plus = Context.AtPopulation(Context.Random.Next(Context.PopulationCount)); // line 9 in Algorithm 1
188          pi_prime = PathRelinking(pi_prime, pi_plus.Solution); // line 10 in Algorithm 1
189          ApproxLocalSearch(pi_prime); // line 11 in Algorithm 1
190          var fitness = Problem.ProblemInstance.ToSingleObjective(pi_prime.Evaluation);
191          // Book-keeping
192          var newBest = Context.BestQuality > fitness;
193          if (newBest) {
194            Context.BestQuality = fitness;
195            Context.BestSolution = (GQAPSolution)pi_prime.Clone();
196          }
197
198          if (Context.PopulationCount == EliteSetSize) { // line 12 in Algorithm 1
199            double[] similarities = Context.Population.Select(x => HammingSimilarityCalculator.CalculateSimilarity(x.Solution.Assignment, pi_prime.Assignment)).ToArray();
200            // NOTE: In the original paper it is not described that new best solutions are accepted regardless of difference
201            // in this implementation a new best solution is always accepted into the elite set
202            if (newBest || similarities.Max() <= 1.0 - (MinimumDifference / (double)pi_prime.Assignment.Length)) { // cond. 2 of line 13 in Algorithm 1
203              var replacement = Context.Population.Select((v, i) => new { Scope = v, Index = i })
204                                          .Where(x => x.Scope.Fitness >= fitness) // cond. 1 of line 13 in Algorithm 1
205                                          .OrderByDescending(x => similarities[x.Index]) // line 14 in Algorithm 1
206                                          .FirstOrDefault();
207              if (replacement != null) {
208                Context.ReplaceAtPopulation(replacement.Index, Context.ToScope(pi_prime, fitness)); // line 14 in Algorithm 1
209              }
210            }
211            // NOTE: In the original paper it is not described that new best solutions are accepted regardless of difference
212            // in this implementation a new best solution is always accepted into the elite set
213          } else if (newBest || IsSufficientlyDifferent(pi_prime.Assignment)) { // line 17 in Algorithm 1
214            Context.AddToPopulation(Context.ToScope(pi_prime, Problem.ProblemInstance.ToSingleObjective(pi_prime.Evaluation))); // line 18 in Algorithm 1
215          }
216        } else if (Problem.ProblemInstance.IsFeasible(pi_prime_vec) /* cond. 1 of line 21 in Algorithm 1 */
217          && IsSufficientlyDifferent(pi_prime_vec))  /* cond. 2 of line 21 in Algorithm 1 */ {
218          var pi_prime = Problem.ProblemInstance.ToEvaluatedSolution(pi_prime_vec);
219          Context.EvaluatedSolutions++;
220          var fitness = Problem.ProblemInstance.ToSingleObjective(pi_prime.Evaluation);
221          Context.AddToPopulation(Context.ToScope(pi_prime, fitness)); /* line 22 in Algorithm 1 */
222          // Book-keeping
223          if (Context.PopulationCount == 1 || Context.BestQuality > fitness) {
224            Context.BestQuality = fitness;
225            Context.BestSolution = (GQAPSolution)pi_prime.Clone();
226          }
227        } else if (Context.PopulationCount == 0) {
228          // Book-keeping
229          // pi_prime_vec is not feasible, but no other feasible solution has been found so far
230          var eval = Problem.ProblemInstance.Evaluate(pi_prime_vec);
231          Context.EvaluatedSolutions++;
232          var fit = Problem.ProblemInstance.ToSingleObjective(eval);
233          if (double.IsNaN(Context.BestQuality) || fit < Context.BestQuality) {
234            Context.BestQuality = fit;
235            Context.BestSolution = new GQAPSolution(pi_prime_vec, eval);
236          }
237        }
238
239        IResult result;
240        if (ExecutionTime - lastUpdate > TimeSpan.FromSeconds(1)) {
241          if (Results.TryGetValue("Iterations", out result))
242            ((IntValue)result.Value).Value = Context.Iterations;
243          else Results.Add(new Result("Iterations", new IntValue(Context.Iterations)));
244          if (Results.TryGetValue("EvaluatedSolutions", out result))
245            ((IntValue)result.Value).Value = Context.EvaluatedSolutions;
246          else Results.Add(new Result("EvaluatedSolutions", new IntValue(Context.EvaluatedSolutions)));
247          lastUpdate = ExecutionTime;
248        }
249        if (Results.TryGetValue("BestQuality", out result))
250          ((DoubleValue)result.Value).Value = Context.BestQuality;
251        else Results.Add(new Result("BestQuality", new DoubleValue(Context.BestQuality)));
252        if (Results.TryGetValue("BestSolution", out result)) {
253          var solAssign = (GQAPAssignment)result.Value;
254          if (solAssign != null) {
255            solAssign.Solution = Context.BestSolution;
256          } else {
257            result.Value = new GQAPAssignment(Context.BestSolution, Problem.ProblemInstance);
258          }
259        } else Results.Add(new Result("BestSolution", new GQAPAssignment(Context.BestSolution, Problem.ProblemInstance)));
260
261        try {
262          Context.RunOperator(Analyzer, cancellationToken);
263        } catch (OperationCanceledException) { }
264
265        Context.Iterations++;
266        if (cancellationToken.IsCancellationRequested) break;
267      }
268      IResult result2;
269      if (Results.TryGetValue("Iterations", out result2))
270        ((IntValue)result2.Value).Value = Context.Iterations;
271      else Results.Add(new Result("Iterations", new IntValue(Context.Iterations)));
272      if (Results.TryGetValue("EvaluatedSolutions", out result2))
273        ((IntValue)result2.Value).Value = Context.EvaluatedSolutions;
274      else Results.Add(new Result("EvaluatedSolutions", new IntValue(Context.EvaluatedSolutions)));
275    }
276
277    private bool IsSufficientlyDifferent(IntegerVector vec) {
278      return Context.Population.All(x =>
279        HammingSimilarityCalculator.CalculateSimilarity(vec, x.Solution.Assignment) <= 1.0 - (MinimumDifference / (double)vec.Length)
280      );
281    }
282
283    private GQAPSolution PathRelinking(GQAPSolution pi_prime, GQAPSolution pi_plus) {
284      // Following code represents line 1 of Algorithm 4
285      IntegerVector source = pi_prime.Assignment, target = pi_plus.Assignment;
286      Evaluation sourceEval = pi_prime.Evaluation, targetEval = pi_plus.Evaluation;
287      var sourceFit = Problem.ProblemInstance.ToSingleObjective(sourceEval);
288      var targetFit = Problem.ProblemInstance.ToSingleObjective(targetEval);
289      if (targetFit < sourceFit) {
290        var h = source;
291        source = target;
292        target = h;
293        var hh = sourceEval;
294        sourceEval = targetEval;
295        targetEval = hh;
296      }
297      int evaluatedSolutions;
298      // lines 2-36 of Algorithm 4 are implemented in the following call
299      var pi_star = GQAPPathRelinking.Apply(Context.Random, source, sourceEval,
300        target, targetEval, Problem.ProblemInstance, CandidateSizeFactor,
301        out evaluatedSolutions);
302      Context.EvaluatedSolutions += evaluatedSolutions;
303      return pi_star;
304    }
305
306    private void ApproxLocalSearch(GQAPSolution pi_prime) {
307      var localSearchEvaluations = 0;
308      ApproximateLocalSearch.Apply(Context.Random, pi_prime, MaximumCandidateListSize,
309        OneMoveProbability, MaximumLocalSearchIterations, Problem.ProblemInstance, out localSearchEvaluations);
310      Context.EvaluatedSolutions += localSearchEvaluations;
311    }
312  }
313}
Note: See TracBrowser for help on using the repository browser.