#region License Information /* HeuristicLab * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.IntegerVectorEncoding; using HeuristicLab.Operators; using HeuristicLab.Optimization; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.Random; namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment { /// /// 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 /// [Item("ApproximateLocalSearch", "The approximate local search is 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.")] [StorableClass] public class ApproximateLocalSearch : SingleSuccessorOperator, IProblemInstanceAwareGQAPOperator, IQualityAwareGQAPOperator, IGQAPLocalImprovementOperator, IAssignmentAwareGQAPOperator, IStochasticOperator { public IProblem Problem { get; set; } public Type ProblemType { get { return typeof(GQAP); } } public ILookupParameter ProblemInstanceParameter { get { return (ILookupParameter)Parameters["ProblemInstance"]; } } public ILookupParameter AssignmentParameter { get { return (ILookupParameter)Parameters["Assignment"]; } } public ILookupParameter QualityParameter { get { return (ILookupParameter)Parameters["Quality"]; } } public ILookupParameter EvaluationParameter { get { return (ILookupParameter)Parameters["Evaluation"]; } } public IValueLookupParameter MaximumIterationsParameter { get { return (IValueLookupParameter)Parameters["MaximumIterations"]; } } public ILookupParameter EvaluatedSolutionsParameter { get { return (ILookupParameter)Parameters["EvaluatedSolutions"]; } } public ILookupParameter RandomParameter { get { return (ILookupParameter)Parameters["Random"]; } } public IValueLookupParameter MaximumCandidateListSizeParameter { get { return (IValueLookupParameter)Parameters["MaximumCandidateListSize"]; } } public IValueLookupParameter OneMoveProbabilityParameter { get { return (IValueLookupParameter)Parameters["OneMoveProbability"]; } } public ILookupParameter ResultsParameter { get { return (ILookupParameter)Parameters["Results"]; } } public IValueLookupParameter GreedyParameter { get { return (IValueLookupParameter)Parameters["Greedy"]; } } [StorableConstructor] protected ApproximateLocalSearch(bool deserializing) : base(deserializing) { } protected ApproximateLocalSearch(ApproximateLocalSearch original, Cloner cloner) : base(original, cloner) { } public ApproximateLocalSearch() : base() { Parameters.Add(new LookupParameter("ProblemInstance", GQAP.ProblemInstanceDescription)); Parameters.Add(new LookupParameter("Assignment", GQAPSolutionCreator.AssignmentDescription)); Parameters.Add(new LookupParameter("Quality", "")); Parameters.Add(new LookupParameter("Evaluation", GQAP.EvaluationDescription)); Parameters.Add(new ValueLookupParameter("MaximumIterations", "The maximum number of iterations that should be performed.")); Parameters.Add(new LookupParameter("EvaluatedSolutions", "The number of evaluated solution equivalents.")); Parameters.Add(new LookupParameter("Random", "The random number generator to use.")); Parameters.Add(new ValueLookupParameter("MaximumCandidateListSize", "The maximum number of candidates that should be found in each step.", new IntValue(10))); Parameters.Add(new ValueLookupParameter("OneMoveProbability", "The probability for performing a 1-move, which is the opposite of performing a 2-move.", new PercentValue(.5))); Parameters.Add(new LookupParameter("Results", "The result collection that stores the results.")); Parameters.Add(new ValueLookupParameter("Greedy", "Whether to use a greedy selection strategy or a probabilistic one.", new BoolValue(true))); } public override IDeepCloneable Clone(Cloner cloner) { return new ApproximateLocalSearch(this, cloner); } public static void Apply(IRandom random, GQAPSolution sol, int maxCLS, double oneMoveProbability, int maximumIterations, GQAPInstance problemInstance, out int evaluatedSolutions, bool greedy = true) { var fit = problemInstance.ToSingleObjective(sol.Evaluation); var eval = sol.Evaluation; Apply(random, sol.Assignment, ref fit, ref eval, maxCLS, oneMoveProbability, maximumIterations, problemInstance, out evaluatedSolutions, greedy); sol.Evaluation = eval; } /// /// /// The random number generator to use. /// The equipment-location assignment vector. /// The solution quality. /// The evaluation result of the solution. /// The maximum number of candidates that should be found in each step. /// The probability for performing a 1-move, which is the opposite of performing a 2-move. /// The maximum number of iterations that should be performed each time the candidate list is generated. /// The problem instance that contains the data. /// The number of evaluated solutions. /// Greedy selection performed better in 5 of 8 instances according to the paper public static void Apply(IRandom random, IntegerVector assignment, ref double quality, ref Evaluation evaluation, int maxCLS, double oneMoveProbability, int maximumIterations, GQAPInstance problemInstance, out int evaluatedSolutions, bool greedy = true) { evaluatedSolutions = 0; var capacities = problemInstance.Capacities; var demands = problemInstance.Demands; var evaluations = 0.0; var deltaEvaluationFactor = 1.0 / assignment.Length; while (true) { // line 1 of Algorithm 3 var count = 0; // line 2 of Algorithm 3 var CLS = new List>(); // line 3 of Algorithm 3 do { var move = Move(random, assignment, oneMoveProbability, capacities); // line 4 of Algorithm 3 var moveEval = GQAPNMoveEvaluator.Evaluate(move, assignment, evaluation, problemInstance); evaluations += move.Indices.Count * deltaEvaluationFactor; double moveQuality = problemInstance.ToSingleObjective(moveEval); if (moveEval.ExcessDemand <= 0.0 && moveQuality < quality) { // line 5 of Algorithm 3 CLS.Add(Tuple.Create(move, moveQuality, moveEval)); // line 6 of Algorithm 3 } count++; // line 8 of Algorithm 3 } while (CLS.Count < maxCLS && count < maximumIterations); // line 9 of Algorithm 3 if (CLS.Count == 0) { // line 10 of Algorithm 3 evaluatedSolutions += (int)Math.Ceiling(evaluations); return; // END } else { // line 11 of Algorithm 3 Tuple selected; if (greedy) { selected = CLS.MinItems(x => x.Item2).Shuffle(random).First(); } else { selected = CLS.SampleProportional(random, 1, CLS.Select(x => 1.0 / x.Item2), false, false).Single(); } NMoveMaker.Apply(assignment, selected.Item1); quality = selected.Item2; evaluation = selected.Item3; } } } private static NMove Move(IRandom random, IntegerVector assignment, double oneMoveProbability, DoubleArray capacities) { if (random.NextDouble() < oneMoveProbability) return StochasticNMoveSingleMoveGenerator.GenerateOneMove(random, assignment, capacities); return StochasticNMoveSingleMoveGenerator.GenerateTwoMove(random, assignment, capacities); } public override IOperation Apply() { var evaluation = EvaluationParameter.ActualValue; var quality = QualityParameter.ActualValue; var fit = quality.Value; var evaluatedSolutions = 0; Apply(RandomParameter.ActualValue, AssignmentParameter.ActualValue, ref fit, ref evaluation, MaximumCandidateListSizeParameter.ActualValue.Value, OneMoveProbabilityParameter.ActualValue.Value, MaximumIterationsParameter.ActualValue.Value, ProblemInstanceParameter.ActualValue, out evaluatedSolutions, GreedyParameter.ActualValue.Value); EvaluationParameter.ActualValue = evaluation; quality.Value = fit; EvaluatedSolutionsParameter.ActualValue.Value += evaluatedSolutions; return base.Apply(); } } }