source: branches/PerformanceComparison/HeuristicLab.OptimizationExpertSystem/3.3/ExpertSystem.cs @ 13551

Last change on this file since 13551 was 13551, checked in by abeham, 5 years ago

#2457: Added characteristic calculator for qap and adapted expert system view

File size: 17.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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.Collections.Generic;
24using System.ComponentModel;
25using System.Drawing;
26using System.IO;
27using System.Linq;
28using System.Threading.Tasks;
29using HeuristicLab.Analysis;
30using HeuristicLab.Collections;
31using HeuristicLab.Common;
32using HeuristicLab.Common.Resources;
33using HeuristicLab.Core;
34using HeuristicLab.Data;
35using HeuristicLab.MainForm;
36using HeuristicLab.Optimization;
37using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
38using HeuristicLab.Persistence.Default.Xml;
39using RunCreationClient = HeuristicLab.Clients.OKB.RunCreation.RunCreationClient;
40using SingleObjectiveOKBProblem = HeuristicLab.Clients.OKB.RunCreation.SingleObjectiveOKBProblem;
41
42namespace HeuristicLab.OptimizationExpertSystem {
43  [Item("Expert-System", "Currently in experimental phase, an expert system that makes algorithm suggestions based on fitness landscape analysis features and an optimization knowledge base.")]
44  [StorableClass]
45  [Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 119)]
46  public sealed class ExpertSystem : NamedItem, IStorableContent, INotifyPropertyChanged {
47
48    public string Filename { get; set; }
49
50    public static new Image StaticItemImage {
51      get { return VSImageLibrary.Library; }
52    }
53
54    [Storable]
55    private int maximumEvaluations;
56    public int MaximumEvaluations {
57      get { return maximumEvaluations; }
58      set {
59        if (maximumEvaluations == value) return;
60        maximumEvaluations = value;
61        OnPropertyChanged("MaximumEvaluations");
62        UpdateSuggestions();
63      }
64    }
65
66    [Storable]
67    private RunCollection runs;
68    public RunCollection Runs {
69      get { return runs; }
70    }
71
72    [Storable]
73    private RunCollection knowledgeBase;
74    public RunCollection KnowledgeBase {
75      get { return knowledgeBase; }
76      set {
77        if (knowledgeBase == value) return;
78        knowledgeBase = value;
79        OnPropertyChanged("KnowledgeBase");
80      }
81    }
82
83    [Storable]
84    private SingleObjectiveOKBProblem problem;
85    public SingleObjectiveOKBProblem Problem {
86      get { return problem; }
87      set {
88        if (problem == value) return;
89        problem = value;
90        OnPropertyChanged("Problem");
91        UpdateSuggestions();
92      }
93    }
94
95    [Storable]
96    private ItemList<IAlgorithm> suggestedInstances;
97    private ReadOnlyItemList<IAlgorithm> readOnlySuggestedInstances;
98    public ReadOnlyItemList<IAlgorithm> SuggestedInstances {
99      get { return readOnlySuggestedInstances; }
100    }
101
102    [Storable]
103    private RunCollection problemInstances;
104    public RunCollection ProblemInstances {
105      get { return problemInstances; }
106      set {
107        if (problemInstances == value) return;
108        problemInstances = value;
109        OnPropertyChanged("ProblemInstances");
110      }
111    }
112
113    [Storable]
114    private BidirectionalLookup<long, IRun> algorithmId2RunMapping;
115    [Storable]
116    private BidirectionalDictionary<long, IAlgorithm> algorithmId2AlgorithmInstanceMapping;
117
118    private bool Maximization {
119      get {
120        return Problem != null && Problem.ProblemId >= 0 && ((IValueParameter<BoolValue>)Problem.MaximizationParameter).Value.Value;
121      }
122    }
123
124    [StorableConstructor]
125    private ExpertSystem(bool deserializing) : base(deserializing) { }
126    private ExpertSystem(ExpertSystem original, Cloner cloner)
127      : base(original, cloner) {
128      runs = cloner.Clone(original.runs);
129      knowledgeBase = cloner.Clone(original.knowledgeBase);
130      suggestedInstances = cloner.Clone(original.suggestedInstances);
131      readOnlySuggestedInstances = suggestedInstances.AsReadOnly();
132      problemInstances = cloner.Clone(original.problemInstances);
133      problem = cloner.Clone(original.problem);
134      algorithmId2RunMapping = new BidirectionalLookup<long, IRun>();
135      foreach (var kvp in original.algorithmId2RunMapping.FirstEnumerable) {
136        algorithmId2RunMapping.AddRangeFirst(kvp.Key, kvp.Select(cloner.Clone));
137      }
138      algorithmId2AlgorithmInstanceMapping = new BidirectionalDictionary<long, IAlgorithm>();
139      foreach (var kvp in original.algorithmId2AlgorithmInstanceMapping) {
140        algorithmId2AlgorithmInstanceMapping.Add(kvp.Key, cloner.Clone(kvp.Value));
141      }
142      RegisterEventHandlers();
143    }
144    public ExpertSystem() {
145      Name = ItemName;
146      Description = ItemDescription;
147      runs = new RunCollection();
148      knowledgeBase = new RunCollection();
149      suggestedInstances = new ItemList<IAlgorithm>();
150      readOnlySuggestedInstances = suggestedInstances.AsReadOnly();
151      problemInstances = new RunCollection();
152      problem = new SingleObjectiveOKBProblem();
153      algorithmId2RunMapping = new BidirectionalLookup<long, IRun>();
154      algorithmId2AlgorithmInstanceMapping = new BidirectionalDictionary<long, IAlgorithm>();
155      RegisterEventHandlers();
156    }
157
158    private void ProblemOnProblemChanged(object sender, EventArgs eventArgs) {
159      if (Problem == null) return;
160    }
161
162    public override IDeepCloneable Clone(Cloner cloner) {
163      return new ExpertSystem(this, cloner);
164    }
165
166    [StorableHook(HookType.AfterDeserialization)]
167    private void AfterDeserialization() {
168      readOnlySuggestedInstances = suggestedInstances.AsReadOnly();
169      RegisterEventHandlers();
170    }
171
172    private void RegisterEventHandlers() {
173      problem.ProblemChanged += ProblemOnProblemChanged;
174      runs.CollectionReset += InformationChanged;
175      runs.ItemsAdded += InformationChanged;
176      runs.ItemsRemoved += InformationChanged;
177      runs.Reset += InformationChanged;
178      runs.UpdateOfRunsInProgressChanged += InformationChanged;
179      knowledgeBase.CollectionReset += InformationChanged;
180      knowledgeBase.ItemsAdded += InformationChanged;
181      knowledgeBase.ItemsRemoved += InformationChanged;
182    }
183
184    private void InformationChanged(object sender, EventArgs e) {
185      var runCollection = sender as RunCollection;
186      if (runCollection != null && runCollection.UpdateOfRunsInProgress) return;
187      UpdateSuggestions();
188    }
189
190    public void UpdateInstanceMap() {
191      var flaValues = ProblemInstances.ResultNames
192                      .Where(x => x.StartsWith("Characteristic.")
193                        && ProblemInstances.All(y => y.Results.ContainsKey(x) && y.Results[x] is DoubleValue))
194                      .Select(x => x).ToList();
195      var ds = new double[ProblemInstances.Count, flaValues.Count];
196      var instanceCounter = 0;
197      foreach (var instance in ProblemInstances) {
198        for (var feature = 0; feature < flaValues.Count; feature++)
199          ds[instanceCounter, feature] = ((DoubleValue)instance.Results[flaValues[feature]]).Value;
200        instanceCounter++;
201      }
202
203      int info;
204      double[] s2;
205      double[,] v;
206      alglib.pcabuildbasis(ds, instanceCounter, flaValues.Count, out info, out s2, out v);
207
208      ProblemInstances.UpdateOfRunsInProgress = true;
209      try {
210        instanceCounter = 0;
211        foreach (var instance in ProblemInstances) {
212          double x = 0, y = 0;
213          for (var feature = 0; feature < flaValues.Count; feature++) {
214            x += ds[instanceCounter, feature] * v[feature, 0];
215            y += ds[instanceCounter, feature] * v[feature, 1];
216          }
217          IItem item;
218          if (instance.Results.TryGetValue("Projection.PCA1", out item)) {
219            ((DoubleValue)item).Value = x;
220          } else instance.Results.Add("Projection.PCA1", new DoubleValue(x));
221          if (instance.Results.TryGetValue("Projection.PCA2", out item)) {
222            ((DoubleValue)item).Value = y;
223          } else instance.Results.Add("Projection.PCA2", new DoubleValue(y));
224
225          instanceCounter++;
226        }
227      } finally { ProblemInstances.UpdateOfRunsInProgress = false; }
228    }
229
230    private static readonly HashSet<string> InterestingValueNames = new HashSet<string>() {
231      "QualityPerEvaluations", "Problem Name", "Problem Type", "Algorithm Name", "Algorithm Type", "Maximization", "BestKnownQuality"
232    };
233
234    public async void UpdateKnowledgeBaseAsync(IProgress progress) {
235      progress.Start("Updating Knowledge Base from OKB");
236      await Task.Factory.StartNew(() => { DoUpdateKnowledgeBase(progress); }, TaskCreationOptions.LongRunning);
237    }
238
239    public void UpdateKnowledgeBase() {
240      DoUpdateKnowledgeBase(new Progress(string.Empty, ProgressState.Started));
241    }
242
243    private void DoUpdateKnowledgeBase(IProgress progress) {
244      var queryClient = Clients.OKB.Query.QueryClient.Instance;
245      var adminClient = Clients.OKB.Administration.AdministrationClient.Instance;
246      try {
247        progress.Status = "Downloading run information...";
248        progress.ProgressValue = 0;
249        // FIXME: How to tell if refresh is necessary?
250        queryClient.Refresh();
251        progress.ProgressValue = 0.5;
252        progress.Status = "Downloading algorithm and problem instance information...";
253        // FIXME: How to tell if refresh is necessary?
254        adminClient.Refresh();
255
256        var probInstance = adminClient.Problems.SingleOrDefault(x => x.Id == Problem.ProblemId);
257        if (probInstance == null) throw new InvalidOperationException("The chosen problem instance cannot be found in the OKB.");
258        var probClassId = probInstance.ProblemClassId;
259
260        var problemClassFilter = (Clients.OKB.Query.StringComparisonAvailableValuesFilter)queryClient.Filters.Single(x => x.Label == "Problem Class Name");
261        problemClassFilter.Value = adminClient.ProblemClasses.Single(x => x.Id == probClassId).Name;
262
263        progress.Status = "Downloading problem instances...";
264        progress.ProgressValue = 0;
265        var p = 0;
266        ProblemInstances.UpdateOfRunsInProgress = true;
267        ProblemInstances.Clear();
268        var totalProblems = adminClient.Problems.Count(x => x.ProblemClassId == probClassId);
269        foreach (var problInst in adminClient.Problems.Where(x => x.ProblemClassId == probClassId)) {
270          progress.Status = string.Format("Downloading problem {0} (okb-id: {1})....", problInst.Name, problInst.Id);
271          var data = Clients.OKB.Administration.AdministrationClient.GetProblemData(problInst.Id);
272          if (data != null) {
273            using (var stream = new MemoryStream(data)) {
274              try {
275                var prob = (IProblem)XmlParser.Deserialize<IContent>(stream);
276                var probRun = new Run() { Name = prob.Name };
277                prob.CollectParameterValues(probRun.Parameters);
278                progress.Status += Environment.NewLine + "Downloading characteristics...";
279                foreach (var v in RunCreationClient.GetCharacteristicValues(problInst.Id)) {
280                  probRun.Results.Add("Characteristic." + v.Name, RunCreationClient.Instance.ConvertToItem(v));
281                }
282                ProblemInstances.Add(probRun);
283              } catch { }
284              stream.Close();
285            }
286          }
287          p++;
288          progress.ProgressValue = p / (double)totalProblems;
289        }
290
291        algorithmId2AlgorithmInstanceMapping.Clear();
292        progress.Status = "Downloading algorithm instances...";
293        progress.ProgressValue = 0;
294        p = 0;
295        foreach (var algInst in adminClient.Algorithms) {
296          progress.Status = string.Format("Downloading algorithm {0} (okb-id: {1})...", algInst.Name, algInst.Id);
297          var data = Clients.OKB.Administration.AdministrationClient.GetAlgorithmData(algInst.Id);
298          if (data != null) {
299            using (var stream = new MemoryStream(data)) {
300              try {
301                var alg = (IAlgorithm)XmlParser.Deserialize<IContent>(stream);
302                algorithmId2AlgorithmInstanceMapping.Add(algInst.Id, alg);
303              } catch { }
304              stream.Close();
305            }
306          }
307          p++;
308          progress.ProgressValue = p / (double)adminClient.Algorithms.Count;
309        }
310
311        var interestingValues = queryClient.ValueNames.Where(x => InterestingValueNames.Contains(x.Name)).ToList();
312
313        progress.Status = "Obtaining number of runs...";
314        progress.ProgressValue = 0;
315        p = 0;
316        var count = queryClient.GetNumberOfRuns(problemClassFilter);
317        if (count == 0) return;
318
319        var runIds = queryClient.GetRunIds(problemClassFilter).ToList();
320        var conversions = new List<Task>();
321        var runList = new List<IRun>();
322        while (p < count) {
323          var nextIds = runIds.Skip(p).Take(500).ToList();
324          progress.Status = string.Format("Downloading runs {0} to {1} of {2}...", p, p + nextIds.Count, count);
325          var okbRuns = queryClient.GetRunsWithValues(nextIds, true, interestingValues);
326          conversions.Add(Task.Factory.StartNew(() => {
327            var hlRuns = okbRuns.AsParallel().Select(x => new { AlgorithmId = x.Algorithm.Id, Run = queryClient.ConvertToOptimizationRun(x) }).ToList();
328            lock (runList) {
329              foreach (var r in hlRuns) {
330                algorithmId2RunMapping.Add(r.AlgorithmId, r.Run);
331                runList.Add(r.Run);
332              }
333            }
334          }));
335          p += nextIds.Count;
336          progress.ProgressValue = p / (double)count;
337        }
338        Task.WaitAll(conversions.ToArray());
339
340        progress.Status = "Finishing...";
341        var algInstRunDict = runList.Where(x => x.Parameters.ContainsKey("Problem Name") && x.Parameters["Problem Name"] is StringValue)
342                                          .GroupBy(x => ((StringValue)x.Parameters["Problem Name"]).Value)
343                                          .ToDictionary(x => x.Key, x => x.GroupBy(y => ((StringValue)y.Parameters["Algorithm Name"]).Value)
344                                                                                  .ToDictionary(y => y.Key, y => y.ToList()));
345
346        foreach (var instance in ProblemInstances) {
347          IItem probNameParam;
348          if (!instance.Parameters.TryGetValue("Problem Name", out probNameParam)) continue;
349
350          var probInstanceName = ((StringValue)probNameParam).Value;
351          var bkQuality = ((DoubleValue)instance.Parameters["BestKnownQuality"]).Value;
352          var maximization = ((BoolValue)instance.Parameters["Maximization"]).Value;
353
354          if (!algInstRunDict.ContainsKey(probInstanceName)) continue;
355          foreach (var kvp in algInstRunDict[probInstanceName]) {
356            var algInstanceName = kvp.Key;
357            // TODO: Things needs to be configurable here (table name, targets)
358            foreach (var target in new[] { 1, 1.01, 1.05, 1.1, 1.2, 1.5 }) {
359              var result = ExpectedRuntimeHelper.CalculateErt(kvp.Value, "QualityPerEvaluations", bkQuality * target, maximization);
360              var resultName = algInstanceName + "@" + ((target - 1) * 100) + "%";
361              IItem item;
362              if (instance.Results.TryGetValue(resultName, out item)) {
363                ((DoubleValue)item).Value = Math.Log10(result.ExpectedRuntime);
364              } else instance.Results.Add(resultName, new DoubleValue(Math.Log10(result.ExpectedRuntime)));
365            }
366          }
367        }
368        KnowledgeBase = new RunCollection(runList);
369      } finally { progress.Finish(); ProblemInstances.UpdateOfRunsInProgress = false; }
370    }
371
372    private void UpdateSuggestions() {
373      if (Problem == null) return;
374      var instances = new SortedList<double, IAlgorithm>();
375      foreach (var relevantRuns in knowledgeBase.GroupBy(x => x.Algorithm)) {
376        var avgQuality = 0.0;
377        var counter = 0;
378        foreach (var run in relevantRuns) {
379          var performanceGraph = ((IndexedDataTable<double>)run.Results["QualityPerEvaluations"]);
380          try {
381            avgQuality += performanceGraph.Rows.First().Values.TakeWhile(x => x.Item1 < MaximumEvaluations).Last().Item2;
382            counter++;
383          } catch { continue; }
384        }
385        avgQuality /= counter;
386        instances.Add(avgQuality, relevantRuns.Key);
387      }
388
389      suggestedInstances.Clear();
390      var instanceLadder = instances.Select(x => (IAlgorithm)x.Value.Clone());
391      suggestedInstances.AddRange(Maximization ? instanceLadder.Reverse() : instanceLadder);
392    }
393
394    public event PropertyChangedEventHandler PropertyChanged;
395    private void OnPropertyChanged(string propertyName) {
396      var handler = PropertyChanged;
397      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
398    }
399  }
400}
Note: See TracBrowser for help on using the repository browser.