#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.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.Random; using HEAL.Attic; namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment { [Item("DiscreteLocationCrossover", "Combines the assignment to locations from various parents.")] [StorableType("E001CEB3-DAA4-4AF4-843B-2DD951F0EAA6")] public class DiscreteLocationCrossover : GQAPCrossover { [StorableConstructor] protected DiscreteLocationCrossover(StorableConstructorFlag _) : base(_) { } protected DiscreteLocationCrossover(DiscreteLocationCrossover original, Cloner cloner) : base(original, cloner) { } public DiscreteLocationCrossover() : base() { } public override IDeepCloneable Clone(Cloner cloner) { return new DiscreteLocationCrossover(this, cloner); } public static IntegerVector Apply(IRandom random, ItemArray parents, DoubleArray demands, DoubleArray capacities) { var locations = capacities.Length; var cap = Math.Max(parents[0].Length / locations, 1); var lookup = new List[parents.Length][]; for (var p = 0; p < parents.Length; p++) { lookup[p] = new List[locations]; var assign = parents[p]; for (var e = 0; e < parents[p].Length; e++) { var loc = assign[e]; if (lookup[p][loc] == null) lookup[p][loc] = new List(cap); lookup[p][loc].Add(e); } } var slack = capacities.ToArray(); IntegerVector child = new IntegerVector(parents[0].Length); var takenEquip = new bool[child.Length]; foreach (var loc in Enumerable.Range(0, locations).Shuffle(random)) { int parent = random.Next(parents.Length); if (lookup[parent][loc] == null) { int tmp = parent; do { tmp = (tmp + 1) % parents.Length; } while (tmp != parent && lookup[tmp][loc] == null); if (parent == tmp) continue; else parent = tmp; } foreach (var equip in lookup[parent][loc]) { if (!takenEquip[equip]) { child[equip] = loc; takenEquip[equip] = true; slack[loc] -= demands[equip]; } } } var order = Enumerable.Range(0, takenEquip.Length) .Where(x => !takenEquip[x]) .Shuffle(random); // avoid bias foreach (var e in order) { var assigned = false; // try 1: find a parent where equipment can be assigned feasibly var fallback = -1; var count = 1; foreach (var p in parents.Shuffle(random)) { if (slack[p[e]] >= demands[e]) { child[e] = p[e]; slack[child[e]] -= demands[e]; assigned = true; break; } else if (random.NextDouble() < 1.0 / count) { fallback = p[e]; } count++; } // try 2: find a random feasible location if (!assigned) { var possible = Enumerable.Range(0, locations).Where(x => slack[x] >= demands[e]).ToList(); if (possible.Count > 0) { var loc = possible.SampleRandom(random); child[e] = loc; slack[loc] -= demands[e]; } else { // otherwise: fallback child[e] = fallback; slack[child[e]] -= demands[e]; } } } return child; } protected override IntegerVector Cross(IRandom random, ItemArray parents, GQAPInstance problemInstance) { return Apply(random, parents, problemInstance.Demands, problemInstance.Capacities); } } }