#region License Information /* HeuristicLab * Copyright (C) 2002-2012 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 System.Threading; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.IntegerVectorEncoding; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.Random; namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment { [Item("GreedyRandomizedSolutionCreator", "Creates a solution according to the procedure 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 GreedyRandomizedSolutionCreator : GQAPStochasticSolutionCreator, IEvaluatorAwareGQAPOperator { public IValueLookupParameter MaximumTriesParameter { get { return (IValueLookupParameter)Parameters["MaximumTries"]; } } public IValueLookupParameter CreateMostFeasibleSolutionParameter { get { return (IValueLookupParameter)Parameters["CreateMostFeasibleSolution"]; } } public IValueLookupParameter EvaluatorParameter { get { return (IValueLookupParameter)Parameters["Evaluator"]; } } [StorableConstructor] protected GreedyRandomizedSolutionCreator(bool deserializing) : base(deserializing) { } protected GreedyRandomizedSolutionCreator(GreedyRandomizedSolutionCreator original, Cloner cloner) : base(original, cloner) { } public GreedyRandomizedSolutionCreator() : base() { Parameters.Add(new ValueLookupParameter("MaximumTries", "The maximum number of tries to create a feasible solution after which an exception is thrown. If it is set to 0 or a negative value there will be an infinite number of attempts.", new IntValue(100000))); Parameters.Add(new ValueLookupParameter("CreateMostFeasibleSolution", "If this is set to true the operator will always succeed, and outputs the solution with the least violation instead of throwing an exception.", new BoolValue(false))); Parameters.Add(new ValueLookupParameter("Evaluator", "The evaluator that is used to evaluate GQAP solutions.")); } public override IDeepCloneable Clone(Cloner cloner) { return new GreedyRandomizedSolutionCreator(this, cloner); } public static IntegerVector CreateSolution(IRandom random, DoubleArray demands, DoubleArray capacities, IGQAPEvaluator evaluator, int maximumTries, bool createMostFeasibleSolution, CancellationToken cancelToken) { int tries = 0; var assignment = new Dictionary(demands.Length); DoubleArray slack = new DoubleArray(capacities.Length); double minViolation = double.MaxValue; Dictionary bestAssignment = null; HashSet CF = new HashSet(), // set of chosen facilities / equipments T = new HashSet(), // set of facilities / equpiments that can be assigned to the set of chosen locations (CL) CL = new HashSet(), // set of chosen locations F = new HashSet(Enumerable.Range(0, demands.Length)), // set of (initially) all facilities / equipments L = new HashSet(Enumerable.Range(0, capacities.Length)); // set of (initially) all locations while (maximumTries <= 0 || tries < maximumTries) { cancelToken.ThrowIfCancellationRequested(); assignment.Clear(); for (int i = 0; i < capacities.Length; i++) slack[i] = capacities[i]; CF.Clear(); T.Clear(); CL.Clear(); F.Clear(); F.UnionWith(Enumerable.Range(0, demands.Length)); L.Clear(); L.UnionWith(Enumerable.Range(0, capacities.Length)); double threshold = 1.0; do { if (L.Any() && random.NextDouble() < threshold) { int l = L.SampleRandom(random); L.Remove(l); CL.Add(l); T = new HashSet(WithDemandEqualOrLess(F, GetMaximumSlack(slack, CL), demands)); } if (T.Any()) { int f = T.SampleRandom(random); T.Remove(f); F.Remove(f); CF.Add(f); int l = WithSlackGreaterOrEqual(CL, demands[f], slack).SampleRandom(random); assignment.Add(f, l); slack[l] -= demands[f]; T = new HashSet(WithDemandEqualOrLess(F, GetMaximumSlack(slack, CL), demands)); threshold = 1.0 - (double)T.Count / Math.Max(F.Count, 1.0); } } while (T.Any() || L.Any()); if (maximumTries > 0) tries++; if (!F.Any()) { bestAssignment = assignment; break; } else if (createMostFeasibleSolution) { // complete the solution and remember the one with least violation foreach (var l in L.ToArray()) { CL.Add(l); L.Remove(l); } while (F.Any()) { var f = F.MaxItems(x => demands[x]).SampleRandom(random); var l = CL.MaxItems(x => slack[x]).SampleRandom(random); F.Remove(f); assignment.Add(f, l); slack[l] -= demands[f]; } double violation = evaluator.EvaluateOverbooking(slack, capacities); if (violation < minViolation) { bestAssignment = assignment; assignment = new Dictionary(demands.Length); minViolation = violation; } } } if (bestAssignment == null || bestAssignment.Count != demands.Length) throw new InvalidOperationException(String.Format("No solution could be found in {0} tries.", maximumTries)); return new IntegerVector(bestAssignment.OrderBy(x => x.Key).Select(x => x.Value).ToArray()); } protected override IntegerVector CreateRandomSolution(IRandom random, DoubleArray demands, DoubleArray capacities) { return CreateSolution(random, demands, capacities, EvaluatorParameter.ActualValue, MaximumTriesParameter.ActualValue.Value, CreateMostFeasibleSolutionParameter.ActualValue.Value, CancellationToken); } private static IEnumerable WithDemandEqualOrLess(IEnumerable facilities, double maximum, DoubleArray demands) { foreach (int f in facilities) { if (demands[f] <= maximum) yield return f; } } private static double GetMaximumSlack(DoubleArray slack, HashSet CL) { return slack.Select((val, idx) => new { idx, val }).Where(x => CL.Contains(x.idx)).Select(x => x.val).Max(); } private static IEnumerable WithSlackGreaterOrEqual(HashSet locations, double minimum, DoubleArray slack) { foreach (int l in locations) { if (slack[l] >= minimum) yield return l; } } } }