[16955] | 1 | using System;
|
---|
| 2 | using System.Collections.Generic;
|
---|
| 3 | using System.Linq;
|
---|
| 4 | using System.Threading;
|
---|
| 5 | using HeuristicLab.Analysis.FitnessLandscape;
|
---|
| 6 | using HeuristicLab.Core;
|
---|
| 7 | using HeuristicLab.Data;
|
---|
| 8 | using HeuristicLab.Encodings.PermutationEncoding;
|
---|
| 9 | using HeuristicLab.Problems.Instances;
|
---|
| 10 | using HeuristicLab.Problems.Instances.QAPLIB;
|
---|
| 11 | using HeuristicLab.Problems.QuadraticAssignment;
|
---|
| 12 | using HeuristicLab.Random;
|
---|
| 13 | using static HeuristicLab.Analysis.FitnessLandscape.QAPDirectedWalk;
|
---|
| 14 |
|
---|
| 15 | namespace WalkExporter {
|
---|
| 16 | class DirectedWalk {
|
---|
| 17 | public int? Seed;
|
---|
| 18 | public QuadraticAssignmentProblem Problem;
|
---|
| 19 | public WalkType Type;
|
---|
| 20 | public int Paths;
|
---|
| 21 | public double EvaluatedSolutionEquiv;
|
---|
| 22 | public bool BestImprovement;
|
---|
| 23 |
|
---|
| 24 |
|
---|
| 25 | public static (Experiment experiment, Dictionary<int, Knowledgebase> training, Dictionary<int, Knowledgebase> test)
|
---|
| 26 | PerformExperiment(WalkType type, bool bestImprovement = true) {
|
---|
| 27 |
|
---|
| 28 | string typeStr;
|
---|
| 29 | switch (type) {
|
---|
| 30 | case WalkType.RandomRandom: typeStr = "(rr)-dw"; break;
|
---|
| 31 | case WalkType.RandomLocal: typeStr = "(rl)-dw"; break;
|
---|
| 32 | case WalkType.LocalLocal: typeStr = "(ll)-dw"; break;
|
---|
| 33 | case WalkType.LocalInverse: typeStr = "(li)-dw"; break;
|
---|
| 34 | default: throw new ArgumentException(string.Format("unknown type {0}", type), "type");
|
---|
| 35 | }
|
---|
| 36 | var experiment = new Experiment() { Algorithm = typeStr, Trials = new List<Exploration>() };
|
---|
| 37 | var training = new[] { 1, 2, 5, 10, 20, 50, 100, 200, 500 }.ToDictionary(x => x, x => new Knowledgebase() { Problems = new List<ProblemInstanceDescriptor>() });
|
---|
| 38 | var test = new[] { 1, 2, 5, 10, 20, 50, 100, 200, 500 }.ToDictionary(x => x, x => new Knowledgebase() { Problems = new List<ProblemInstanceDescriptor>() });
|
---|
| 39 |
|
---|
| 40 | foreach (var dimension in new[] { 20, 30, 40 }) {
|
---|
| 41 | var provider = new OneSizeInstanceProvider(dimension);
|
---|
| 42 | foreach (var desc in provider.GetDataDescriptors()) {
|
---|
| 43 | var qapData = provider.LoadData(desc);
|
---|
| 44 |
|
---|
| 45 | var qap = new QuadraticAssignmentProblem();
|
---|
| 46 | qap.Load(qapData);
|
---|
| 47 | var calculator = new DirectedWalk() {
|
---|
| 48 | Problem = qap,
|
---|
| 49 | BestImprovement = bestImprovement,
|
---|
| 50 | Paths = 1000,
|
---|
| 51 | Seed = 42,
|
---|
| 52 | Type = type
|
---|
| 53 | };
|
---|
| 54 | var solutions = calculator.CalculateRelinkingPoints().ToList();
|
---|
| 55 | var walk = calculator.Run(solutions).ToList();
|
---|
| 56 |
|
---|
| 57 | var exploration = new Exploration() { Problem = qapData.Name, Dimension = qapData.Dimension, Walks = new List<Walk>() };
|
---|
| 58 | foreach (var w in walk) {
|
---|
| 59 | exploration.Walks.Add(new Walk() {
|
---|
| 60 | QualityTrail = w.Select(x => x.Item2).ToList()
|
---|
| 61 | });
|
---|
| 62 | }
|
---|
| 63 | experiment.Trials.Add(exploration);
|
---|
| 64 | AddToKnowledgeBase(training, qapData, calculator, walk, 0);
|
---|
| 65 | AddToKnowledgeBase(test, qapData, calculator, walk, 500);
|
---|
| 66 | }
|
---|
| 67 | }
|
---|
| 68 | return (experiment, training, test);
|
---|
| 69 | }
|
---|
| 70 |
|
---|
| 71 | private static void AddToKnowledgeBase(Dictionary<int, Knowledgebase> training, QAPData qapData, DirectedWalk calculator, List<List<Tuple<Permutation, double>>> walk, int start) {
|
---|
| 72 | foreach (var t in training.Keys) {
|
---|
| 73 | var kb = training[t];
|
---|
| 74 | var sbf = CurveAnalysis<Permutation>.GetCharacteristics(walk.Skip(start).Take(t), (left, right) => (1.0 - HammingSimilarityCalculator.CalculateSimilarity(left, right)) * left.Length);
|
---|
| 75 | var trail = walk.Skip(start).Take(t).SelectMany(x => x).Select(x => x.Item2).ToArray();
|
---|
| 76 | var ia = new InformationAnalysis(trail);
|
---|
| 77 | var clen = RuggednessCalculator.CalculateCorrelationLength(trail, out double[] acf);
|
---|
| 78 |
|
---|
| 79 | var pid = new ProblemInstanceDescriptor() {
|
---|
| 80 | Name = qapData.Name,
|
---|
| 81 | Class = Util.GetGeneratorClass(qapData.Name),
|
---|
| 82 | Dimension = qapData.Dimension,
|
---|
| 83 | DescriptionEffort = calculator.EvaluatedSolutionEquiv / 1000 * t,
|
---|
| 84 | Features = new List<KeyValue>()
|
---|
| 85 | };
|
---|
| 86 | pid.Features.Add(new KeyValue { Key = "AC1", ContinuousValue = acf[1] });
|
---|
| 87 | pid.Features.Add(new KeyValue { Key = "CorrelationLength", DiscreteValue = clen });
|
---|
| 88 | foreach (var f in CurveAnalysisResult.AllFeatures.Zip(sbf.GetValues(), (a, b) => new { Feature = a, Value = b })) {
|
---|
| 89 | pid.Features.Add(new KeyValue { Key = f.Feature.ToString(), ContinuousValue = f.Value });
|
---|
| 90 | }
|
---|
| 91 | foreach (var f in ia.GetFeatures()) {
|
---|
| 92 | if (f.Item2 is double d) pid.Features.Add(new KeyValue { Key = f.Item1, ContinuousValue = d });
|
---|
| 93 | else if (f.Item2 is int i) pid.Features.Add(new KeyValue { Key = f.Item1, DiscreteValue = i });
|
---|
| 94 | }
|
---|
| 95 |
|
---|
| 96 | kb.Problems.Add(pid);
|
---|
| 97 | }
|
---|
| 98 | }
|
---|
| 99 |
|
---|
| 100 | public IEnumerable<Permutation> CalculateRelinkingPoints() {
|
---|
| 101 | IRandom random = Seed.HasValue ? new MersenneTwister((uint)Seed.Value) : new MersenneTwister();
|
---|
| 102 | var pathCount = Paths;
|
---|
| 103 |
|
---|
| 104 | var perm = new Permutation(PermutationTypes.Absolute, Problem.Weights.Rows, random);
|
---|
| 105 | var startLocal = Type == WalkType.LocalGlobal || Type == WalkType.LocalLocal;
|
---|
| 106 | var targetLocal = Type == WalkType.LocalLocal || Type == WalkType.RandomLocal;
|
---|
| 107 | var targetGlobal = Type == WalkType.LocalGlobal || Type == WalkType.RandomGlobal;
|
---|
| 108 | var inverseWalk = Type == WalkType.LocalInverse;
|
---|
| 109 |
|
---|
| 110 | if (Type == WalkType.LocalInverse) pathCount--; // inverse walks equal one walk per solution
|
---|
| 111 | for (var i = 0; i <= pathCount; i++) {
|
---|
| 112 | var start = i % 2 == 0;
|
---|
| 113 | var target = i % 2 == 1;
|
---|
| 114 | if (inverseWalk || start && startLocal) {
|
---|
| 115 | perm = new Permutation(PermutationTypes.Absolute, Problem.Weights.Rows, random);
|
---|
| 116 | var fit = new DoubleValue(QAPEvaluator.Apply(perm, Problem.Weights, Problem.Distances));
|
---|
| 117 | EvaluatedSolutionEquiv++;
|
---|
| 118 | var evalSol = new IntValue(0);
|
---|
| 119 | QAPExhaustiveSwap2LocalImprovement.ImproveFast(perm, Problem.Weights, Problem.Distances, fit, new IntValue(0), evalSol, Problem.Maximization.Value, int.MaxValue, CancellationToken.None);
|
---|
| 120 | EvaluatedSolutionEquiv += evalSol.Value;
|
---|
| 121 | } else if (target && (targetLocal || targetGlobal)) {
|
---|
| 122 | if (targetGlobal) {
|
---|
| 123 | perm = Problem.BestKnownSolutions.SampleRandom(random);
|
---|
| 124 | } else {
|
---|
| 125 | perm = new Permutation(PermutationTypes.Absolute, Problem.Weights.Rows, random);
|
---|
| 126 | var fit = new DoubleValue(QAPEvaluator.Apply(perm, Problem.Weights, Problem.Distances));
|
---|
| 127 | EvaluatedSolutionEquiv++;
|
---|
| 128 | var evalSol = new IntValue(0);
|
---|
| 129 | QAPExhaustiveSwap2LocalImprovement.ImproveFast(perm, Problem.Weights, Problem.Distances, fit, new IntValue(0), evalSol, Problem.Maximization.Value, int.MaxValue, CancellationToken.None);
|
---|
| 130 | EvaluatedSolutionEquiv += evalSol.Value;
|
---|
| 131 | }
|
---|
| 132 | } else { // random
|
---|
| 133 | BiasedShuffle(perm, random);
|
---|
| 134 | }
|
---|
| 135 | yield return (Permutation)perm.Clone();
|
---|
| 136 | }
|
---|
| 137 | }
|
---|
| 138 |
|
---|
| 139 | public IEnumerable<List<Tuple<Permutation, double>>> Run(IEnumerable<Permutation> permutations) {
|
---|
| 140 | if (Type == WalkType.LocalInverse) return RunInverse(permutations);
|
---|
| 141 | return RunRegular(permutations);
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | private IEnumerable<List<Tuple<Permutation, double>>> RunInverse(IEnumerable<Permutation> permutations) {
|
---|
| 145 | var qap = (QuadraticAssignmentProblem)Problem;
|
---|
| 146 | var min = qap.LowerBound.Value;
|
---|
| 147 | var max = qap.AverageQuality.Value;
|
---|
| 148 | var bestImprovement = BestImprovement;
|
---|
| 149 |
|
---|
| 150 | foreach (var start in permutations) {
|
---|
| 151 | var startFitness = QAPEvaluator.Apply(start, qap.Weights, qap.Distances);
|
---|
| 152 | EvaluatedSolutionEquiv++;
|
---|
| 153 |
|
---|
| 154 | // inverse walks are applied to all solutions
|
---|
| 155 | Func<Tuple<Permutation, double>, IEnumerable<Tuple<Permutation, double>>> inverseNeighborFunc = (p) => RestrictedInverseNeighborhood(qap, p.Item1, p.Item2, start);
|
---|
| 156 | var walk = DirectedWalk<Permutation>.WalkRestricted(qap.Maximization.Value, inverseNeighborFunc, start, startFitness, !bestImprovement);
|
---|
| 157 | yield return walk.Select(x => Tuple.Create(x.Item1, (x.Item2 - min) / (max - min))).ToList();
|
---|
| 158 | } // end paths
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | private IEnumerable<List<Tuple<Permutation, double>>> RunRegular(IEnumerable<Permutation> permutations) {
|
---|
| 162 | var iter = permutations.GetEnumerator();
|
---|
| 163 | if (!iter.MoveNext()) yield break;
|
---|
| 164 | var bestImprovement = BestImprovement;
|
---|
| 165 |
|
---|
| 166 | var qap = (QuadraticAssignmentProblem)Problem;
|
---|
| 167 | var min = qap.LowerBound.Value;
|
---|
| 168 | var max = qap.AverageQuality.Value;
|
---|
| 169 |
|
---|
| 170 | var start = iter.Current;
|
---|
| 171 |
|
---|
| 172 | while (iter.MoveNext()) {
|
---|
| 173 | var startFitness = QAPEvaluator.Apply(start, qap.Weights, qap.Distances);
|
---|
| 174 | EvaluatedSolutionEquiv++;
|
---|
| 175 | var end = iter.Current;
|
---|
| 176 |
|
---|
| 177 | var invSol = new int[start.Length];
|
---|
| 178 | Func<Tuple<Permutation, double>, IEnumerable<Tuple<Permutation, double>>> regularNeighborFunc = (p) => RestrictedNeighborhood(qap, p.Item1, p.Item2, end, invSol);
|
---|
| 179 | var walk = DirectedWalk<Permutation>.WalkRestricted(qap.Maximization.Value, regularNeighborFunc, start, startFitness, !bestImprovement);
|
---|
| 180 |
|
---|
| 181 | var trail = new List<Tuple<Permutation, double>>();
|
---|
| 182 | foreach (var step in walk) {
|
---|
| 183 | for (var i = 0; i < invSol.Length; i++)
|
---|
| 184 | invSol[step.Item1[i]] = i;
|
---|
| 185 | trail.Add(Tuple.Create(step.Item1, (step.Item2 - min) / (max - min)));
|
---|
| 186 | }
|
---|
| 187 | yield return trail;
|
---|
| 188 |
|
---|
| 189 | start = end;
|
---|
| 190 | } // end paths
|
---|
| 191 | }
|
---|
| 192 |
|
---|
| 193 | private IEnumerable<Tuple<Permutation, double>> RestrictedInverseNeighborhood(QuadraticAssignmentProblem qap, Permutation current, double currentFit, Permutation start) {
|
---|
| 194 | var evalSolPerMove = 4.0 / current.Length;
|
---|
| 195 | var N = current.Length;
|
---|
| 196 | for (var index = 0; index < N; index++) {
|
---|
| 197 | if (current[index] != start[index]) continue;
|
---|
| 198 | for (var k = 0; k < N; k++) {
|
---|
| 199 | if (k == index) continue;
|
---|
| 200 | var fit = QAPSwap2MoveEvaluator.Apply(current, new Swap2Move(index, k), qap.Weights, qap.Distances);
|
---|
| 201 | EvaluatedSolutionEquiv += evalSolPerMove;
|
---|
| 202 | current.Swap(index, k);
|
---|
| 203 | yield return Tuple.Create(current, currentFit + fit);
|
---|
| 204 | current.Swap(index, k); // reverse
|
---|
| 205 | }
|
---|
| 206 | }
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | private IEnumerable<Tuple<Permutation, double>> RestrictedNeighborhood(QuadraticAssignmentProblem qap, Permutation current, double currentFit, Permutation target, int[] inverse) {
|
---|
| 210 | var evalSolPerMove = 4.0 / current.Length;
|
---|
| 211 | for (var index = 0; index < current.Length; index++) {
|
---|
| 212 | if (current[index] == target[index]) continue;
|
---|
| 213 | var k = inverse[target[index]];
|
---|
| 214 | var fit = QAPSwap2MoveEvaluator.Apply(current, new Swap2Move(index, k), qap.Weights, qap.Distances);
|
---|
| 215 | EvaluatedSolutionEquiv += evalSolPerMove;
|
---|
| 216 | current.Swap(index, k);
|
---|
| 217 | yield return Tuple.Create(current, currentFit + fit);
|
---|
| 218 | current.Swap(index, k); // reverse
|
---|
| 219 | }
|
---|
| 220 | }
|
---|
| 221 |
|
---|
| 222 | // permutation must be strictly different in every position
|
---|
| 223 | private static void BiasedShuffle(Permutation p, IRandom random) {
|
---|
| 224 | for (var i = p.Length - 1; i > 0; i--) {
|
---|
| 225 | // Swap element "i" with a random earlier element (excluding itself)
|
---|
| 226 | var swapIndex = random.Next(i);
|
---|
| 227 | var h = p[swapIndex];
|
---|
| 228 | p[swapIndex] = p[i];
|
---|
| 229 | p[i] = h;
|
---|
| 230 | }
|
---|
| 231 | }
|
---|
| 232 | }
|
---|
| 233 | }
|
---|