Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PerformanceComparison/HeuristicLab.OptimizationExpertSystem/3.3/Views/UnderstandingSolutionsView.cs @ 13743

Last change on this file since 13743 was 13743, checked in by abeham, 8 years ago

#2457: adding relational SOM projection for solution network visualization

File size: 20.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 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 HeuristicLab.Analysis;
23using HeuristicLab.Analysis.QualityAnalysis;
24using HeuristicLab.Common;
25using HeuristicLab.Core;
26using HeuristicLab.Data;
27using HeuristicLab.MainForm;
28using HeuristicLab.Optimization;
29using HeuristicLab.OptimizationExpertSystem.Common;
30using HeuristicLab.Random;
31using System;
32using System.Collections.Generic;
33using System.Linq;
34using System.Windows.Forms;
35using System.Windows.Forms.DataVisualization.Charting;
36
37namespace HeuristicLab.OptimizationExpertSystem {
38  [View("Understanding Solutions")]
39  [Content(typeof(KnowledgeCenter), IsDefaultView = false)]
40  public partial class UnderstandingSolutionsView : KnowledgeCenterViewBase {
41    protected virtual bool SuppressEvents { get; set; }
42
43    public UnderstandingSolutionsView() {
44      InitializeComponent();
45      solutionNetworkProjectionComboBox.SelectedIndex = 0;
46    }
47
48    protected override void OnContentChanged() {
49      base.OnContentChanged();
50      UpdateSimilarityCalculators();
51      UpdateNamesComboboxes();
52      UpdateSolutionVisualization();
53    }
54
55    protected override void OnProblemChanged() {
56      base.OnProblemInstancesChanged();
57      UpdateSimilarityCalculators();
58      UpdateNamesComboboxes();
59      UpdateSolutionVisualization();
60    }
61
62    protected override void OnProblemSolutionsChanged() {
63      base.OnProblemSolutionsChanged();
64      UpdateNamesComboboxes();
65      UpdateSolutionVisualization();
66    }
67
68    protected override void OnSolutionSeedingPoolChanged() {
69      base.OnSolutionSeedingPoolChanged();
70      UpdateSolutionNetworkAnalysis(similarityComboBox.SelectedItem as ISolutionSimilarityCalculator, (string)solutionNetworkProjectionComboBox.SelectedItem);
71    }
72
73    private void UpdateSimilarityCalculators() {
74      var selected = (ISolutionSimilarityCalculator)(similarityComboBox.SelectedIndex >= 0 ? similarityComboBox.SelectedItem : null);
75      similarityComboBox.Items.Clear();
76
77      if (Content == null || Content.Problem == null) return;
78
79      foreach (var calc in Content.Problem.Operators.OfType<ISolutionSimilarityCalculator>()) {
80        similarityComboBox.Items.Add(calc);
81        if (selected != null && calc.ItemName == selected.ItemName) similarityComboBox.SelectedItem = calc;
82      }
83      if (selected == null && similarityComboBox.Items.Count > 0)
84        similarityComboBox.SelectedIndex = 0;
85    }
86
87    private void UpdateNamesComboboxes() {
88      var selectedSolutionName = solutionNameComboBox.SelectedIndex >= 0 ? (string)solutionNameComboBox.SelectedItem : string.Empty;
89
90      solutionNameComboBox.Items.Clear();
91      if (Content == null) return;
92      var solutionNames = Content.Problem.Solutions.Select(x => x.Solution).OfType<IScope>().SelectMany(x => x.Variables);
93
94      foreach (var sn in solutionNames.GroupBy(x => x.Name).OrderBy(x => x.Key)) {
95        solutionNameComboBox.Items.Add(sn.Key);
96        // either it was previously selected, or the variable value is defined in the HeuristicLab.Encodings sub-namespace
97        if (sn.Key == selectedSolutionName || (string.IsNullOrEmpty(selectedSolutionName) && sn.All(x => x.Value != null && x.Value.GetType().FullName.StartsWith("HeuristicLab.Encodings."))))
98          solutionNameComboBox.SelectedItem = sn.Key;
99      }
100    }
101   
102    public void UpdateSolutionVisualization() {
103      if (InvokeRequired) { Invoke((Action)UpdateSolutionVisualization); return;  }
104      var qualityName = Content.Problem.Problem.Evaluator.QualityParameter.ActualName;
105      UpdateSolutionQualityAnalysis(qualityName);
106      if (similarityComboBox.SelectedIndex >= 0) {
107        var calculator = (ISolutionSimilarityCalculator)similarityComboBox.SelectedItem;
108        UpdateSolutionDiversityAnalysis(calculator);
109        UpdateSolutionFdcAnalysis(calculator, fdcBetweenBestCheckBox.Checked);
110        UpdateSolutionLengthScaleAnalysis(calculator);
111        UpdateSolutionNetworkAnalysis(calculator, (string)solutionNetworkProjectionComboBox.SelectedItem);
112      } else {
113        solutionsDiversityViewHost.Content = null;
114        solutionsFdcViewHost.Content = null;
115        solutionsLengthScaleViewHost.Content = null;
116        solutionsNetworkChart.Series.First().Points.Clear();
117      }
118    }
119
120    private void UpdateSolutionQualityAnalysis(string qualityName) {
121      var dt = solutionsQualityViewHost.Content as DataTable;
122      if (dt == null) {
123        dt = QualityDistributionAnalyzer.PrepareTable(qualityName);
124        dt.VisualProperties.Title = "Quality Distribution";
125        solutionsQualityViewHost.Content = dt;
126      }
127      QualityDistributionAnalyzer.UpdateTable(dt, GetSolutionScopes().Select(x => GetQuality(x, qualityName) ?? double.NaN).Where(x => !double.IsNaN(x)));
128    }
129
130    private void UpdateSolutionDiversityAnalysis(ISolutionSimilarityCalculator calculator) {
131      try {
132        solutionsDiversityViewHost.Content = null;
133        var solutionScopes = GetSolutionScopes();
134        var similarities = new double[solutionScopes.Count, solutionScopes.Count];
135        for (var i = 0; i < solutionScopes.Count; i++) {
136          for (var j = 0; j < solutionScopes.Count; j++)
137            similarities[i, j] = calculator.CalculateSolutionSimilarity(solutionScopes[i], solutionScopes[j]);
138        }
139        var hm = new HeatMap(similarities, "Solution Similarities", 0.0, 1.0);
140        solutionsDiversityViewHost.Content = hm;
141      } catch { }
142    }
143
144    private void UpdateSolutionFdcAnalysis(ISolutionSimilarityCalculator calculator, bool distanceToBest) {
145      try {
146        solutionsFdcViewHost.Content = null;
147        fdcSpearmanLabel.Text = "-";
148        fdcPearsonLabel.Text = "-";
149        var solutionScopes = GetSolutionScopes();
150        var points = new List<Point2D<double>>();
151        if (distanceToBest) {
152          var maximization = ((IValueParameter<BoolValue>)Content.Problem.MaximizationParameter).Value.Value;
153          var bestSolutions = (maximization ? solutionScopes.MaxItems(x => GetQuality(x, calculator.QualityVariableName) ?? double.NegativeInfinity)
154                                            : solutionScopes.MinItems(x => GetQuality(x, calculator.QualityVariableName) ?? double.PositiveInfinity)).ToList();
155          foreach (var solScope in solutionScopes.Except(bestSolutions)) {
156            var maxSimilarity = bestSolutions.Max(x => calculator.CalculateSolutionSimilarity(solScope, x));
157            var qDiff = (GetQuality(solScope, calculator.QualityVariableName) ?? double.NaN)
158                      - (GetQuality(bestSolutions[0], calculator.QualityVariableName) ?? double.NaN);
159            points.Add(new Point2D<double>(Math.Abs(qDiff), 1.0 - maxSimilarity));
160          }
161        } else {
162          for (int i = 0; i < solutionScopes.Count; i++) {
163            for (int j = 0; j < solutionScopes.Count; j++) {
164              if (i == j) continue;
165              var qDiff = (GetQuality(solutionScopes[i], calculator.QualityVariableName) ?? double.NaN)
166                          - (GetQuality(solutionScopes[j], calculator.QualityVariableName) ?? double.NaN);
167              if (double.IsNaN(qDiff)) continue;
168              points.Add(new Point2D<double>(Math.Abs(qDiff), 1.0 - calculator.CalculateSolutionSimilarity(solutionScopes[i], solutionScopes[j])));
169            }
170          }
171        }
172        var xs = points.Select(p => p.X).ToArray();
173        var ys = points.Select(p => p.Y).ToArray();
174        var scorr = alglib.spearmancorr2(xs, ys);
175        var pcorr = alglib.pearsoncorr2(xs, ys);
176        pcorr = pcorr * pcorr;
177        fdcSpearmanLabel.Text = scorr.ToString("F2");
178        fdcPearsonLabel.Text = pcorr.ToString("F2");
179
180        var splot = new ScatterPlot("Fitness-Distance", "");
181        splot.VisualProperties.XAxisTitle = "Absolute Fitness Difference";
182        splot.VisualProperties.XAxisMinimumAuto = false;
183        splot.VisualProperties.XAxisMinimumFixedValue = 0.0;
184        splot.VisualProperties.YAxisTitle = "Solution Distance";
185        splot.VisualProperties.YAxisMinimumAuto = false;
186        splot.VisualProperties.YAxisMinimumFixedValue = 0.0;
187        splot.VisualProperties.YAxisMaximumAuto = false;
188        splot.VisualProperties.YAxisMaximumFixedValue = 1.0;
189        var row = new ScatterPlotDataRow("Fdc", "", points);
190        row.VisualProperties.PointSize = 10;
191        row.VisualProperties.ShowRegressionLine = true;
192        splot.Rows.Add(row);
193        solutionsFdcViewHost.Content = splot;
194      } catch { }
195    }
196
197    private void UpdateSolutionLengthScaleAnalysis(ISolutionSimilarityCalculator calculator) {
198      try {
199        solutionsLengthScaleViewHost.Content = null;
200        var dt = solutionsLengthScaleViewHost.Content as DataTable;
201        if (dt == null) {
202          dt = QualityDistributionAnalyzer.PrepareTable("Length Scale");
203          solutionsLengthScaleViewHost.Content = dt;
204        }
205        QualityDistributionAnalyzer.UpdateTable(dt, CalculateLengthScale(calculator));
206      } catch {
207        solutionsLengthScaleViewHost.Content = null;
208      }
209    }
210
211    private IEnumerable<double> CalculateLengthScale(ISolutionSimilarityCalculator calculator) {
212      var solutionScopes = GetSolutionScopes();
213      for (var i = 0; i < solutionScopes.Count; i++) {
214        for (var j = 0; j < solutionScopes.Count; j++) {
215          if (i == j) continue;
216          var sim = calculator.CalculateSolutionSimilarity(solutionScopes[i], solutionScopes[j]);
217          if (sim.IsAlmost(0)) continue;
218          var qDiff = (GetQuality(solutionScopes[i], calculator.QualityVariableName) ?? double.NaN)
219                    - (GetQuality(solutionScopes[j], calculator.QualityVariableName) ?? double.NaN);
220          if (!double.IsNaN(qDiff)) yield return Math.Abs(qDiff) / sim;
221        }
222      }
223    }
224
225    private void UpdateSolutionNetworkAnalysis(ISolutionSimilarityCalculator calculator, string projection) {
226      var series = solutionsNetworkChart.Series["SolutionSeries"];
227      var seedingSeries = solutionsNetworkChart.Series["SeedingSolutionSeries"];
228      try {
229        series.Points.Clear();
230        seedingSeries.Points.Clear();
231        var solutionScopes = GetSolutionScopes();
232        var dissimilarities = new DoubleMatrix(solutionScopes.Count, solutionScopes.Count);
233        for (var i = 0; i < solutionScopes.Count; i++) {
234          for (var j = 0; j < solutionScopes.Count; j++) {
235            if (i == j) continue;
236            dissimilarities[i, j] = 1.0 - calculator.CalculateSolutionSimilarity(solutionScopes[i], solutionScopes[j]);
237          }
238        }
239        DoubleMatrix coords = null;
240        if (projection == "SOM")
241          coords = Som(dissimilarities, new MersenneTwister(42), jittering: true);
242        else coords = MultidimensionalScaling.KruskalShepard(dissimilarities);
243        for (var i = 0; i < coords.Rows; i++) {
244          var quality = GetQuality(solutionScopes[i], calculator.QualityVariableName) ?? double.NaN;
245          var dataPoint = new DataPoint() {
246            Name = (i + 1).ToString(),
247            XValue = coords[i, 0],
248            YValues = new[] {coords[i, 1], quality},
249            Label = (i + 1) + ": " + quality,
250            Tag = solutionScopes[i]
251          };
252          if (Content.SolutionSeedingPool.Contains(solutionScopes[i]) && Content.SolutionSeedingPool.ItemChecked(solutionScopes[i]))
253            seedingSeries.Points.Add(dataPoint);
254          else series.Points.Add(dataPoint);
255        }
256      } catch {
257        // problems in calculating the similarity
258        series.Points.Clear();
259        seedingSeries.Points.Clear();
260      }
261    }
262
263    private void SimilarityComboBoxOnSelectedIndexChanged(object sender, EventArgs e) {
264      if (InvokeRequired) { Invoke((Action<object, EventArgs>)SimilarityComboBoxOnSelectedIndexChanged, sender, e); return; }
265      var calculator = (ISolutionSimilarityCalculator)similarityComboBox.SelectedItem;
266      if (calculator != null) {
267        calculator.SolutionVariableName = (string)solutionNameComboBox.SelectedItem;
268        calculator.QualityVariableName = Content.Problem.Problem.Evaluator.QualityParameter.ActualName;
269      }
270      UpdateSolutionDiversityAnalysis(calculator);
271      UpdateSolutionFdcAnalysis(calculator, fdcBetweenBestCheckBox.Checked);
272      UpdateSolutionLengthScaleAnalysis(calculator);
273      UpdateSolutionNetworkAnalysis(calculator, (string)solutionNetworkProjectionComboBox.SelectedItem);
274    }
275
276    private void SolutionNameComboBoxOnSelectedIndexChanged(object sender, EventArgs e) {
277      if (InvokeRequired) { Invoke((Action<object, EventArgs>)SolutionNameComboBoxOnSelectedIndexChanged, sender, e); return; }
278      var calculator = (ISolutionSimilarityCalculator)similarityComboBox.SelectedItem;
279      if (calculator != null) {
280        calculator.SolutionVariableName = (string)solutionNameComboBox.SelectedItem;
281        calculator.QualityVariableName = Content.Problem.Problem.Evaluator.QualityParameter.ActualName;
282      }
283      UpdateSolutionDiversityAnalysis(calculator);
284      UpdateSolutionFdcAnalysis(calculator, fdcBetweenBestCheckBox.Checked);
285      UpdateSolutionLengthScaleAnalysis(calculator);
286      UpdateSolutionNetworkAnalysis(calculator, (string)solutionNetworkProjectionComboBox.SelectedItem);
287    }
288
289    private void FdcBetweenBestCheckBoxOnCheckedChanged(object sender, EventArgs e) {
290      if (InvokeRequired) { Invoke((Action<object, EventArgs>)FdcBetweenBestCheckBoxOnCheckedChanged, sender, e); return; }
291      UpdateSolutionFdcAnalysis((ISolutionSimilarityCalculator)similarityComboBox.SelectedItem, fdcBetweenBestCheckBox.Checked);
292    }
293
294    private void SolutionNetworkProjectionComboBoxOnSelectedIndexChanged(object sender, EventArgs e) {
295      if (InvokeRequired) { Invoke((Action<object, EventArgs>)SolutionNetworkProjectionComboBoxOnSelectedIndexChanged, sender, e); return; }
296      var calculator = (ISolutionSimilarityCalculator)similarityComboBox.SelectedItem;
297      if (calculator != null) {
298        calculator.SolutionVariableName = (string)solutionNameComboBox.SelectedItem;
299        calculator.QualityVariableName = Content.Problem.Problem.Evaluator.QualityParameter.ActualName;
300      }
301      UpdateSolutionNetworkAnalysis(calculator, (string)solutionNetworkProjectionComboBox.SelectedItem);
302    }
303
304    private void SolutionsNetworkChartOnMouseClick(object sender, MouseEventArgs e) {
305      var result = solutionsNetworkChart.HitTest(e.X, e.Y);
306      if (result.ChartElementType == ChartElementType.DataPoint) {
307        var point = (DataPoint)result.Object;
308        var solutionScope = (IScope)point.Tag;
309        if (!Content.SolutionSeedingPool.Contains(solutionScope)) return;
310        Content.SolutionSeedingPool.SetItemCheckedState(solutionScope, !Content.SolutionSeedingPool.ItemChecked(solutionScope));
311      }
312    }
313
314    private void SolutionsNetworkChartOnMouseDoubleClick(object sender, MouseEventArgs e) {
315      var result = solutionsNetworkChart.HitTest(e.X, e.Y);
316      if (result.ChartElementType == ChartElementType.DataPoint) {
317        var point = (DataPoint)result.Object;
318        var solutionScope = (IScope)point.Tag;
319        MainForm.ShowContent(solutionScope, true);
320      }
321    }
322
323    #region Helpers
324    private List<IScope> GetSolutionScopes() {
325      return Content.Problem.Solutions.Select(x => x.Solution).OfType<IScope>().ToList();
326    }
327
328    private double? GetQuality(IScope scope, string qualityName) {
329      IVariable v;
330      if (!scope.Variables.TryGetValue(qualityName, out v)) return null;
331      var dval = v.Value as DoubleValue;
332      if (dval == null) return null;
333      return dval.Value;
334    }
335
336    #region SOM projection
337    /// <summary>
338    /// This is the online algorithm described in
339    /// Olteanu, M. and Villa-Vialaneix, N. 2015.
340    /// On-line relational and multiple relational SOM.
341    /// Neurocomputing 147, pp. 15-30.
342    /// </summary>
343    /// <param name="dissimilarities">The full NxN matrix containing all dissimilarities between N points.</param>
344    /// <param name="random">The random number generator to use.</param>
345    /// <param name="somSize">The length of a side of the SOM grid (there are somSize * somSize neurons).</param>
346    /// <param name="iterations">The amount of iterations to perform in learning.</param>
347    /// <param name="learningRate">The initial learning rate.</param>
348    /// <param name="learningRadius">The initial learning radius.</param>
349    /// <param name="jittering">If the final coordinates should be jittered slightly within the grid.</param>
350    /// <returns>A matrix of coordinates having N rows and 2 columns.</returns>
351    private DoubleMatrix Som(DoubleMatrix dissimilarities, IRandom random, int somSize = 5, int iterations = 100, double learningRate = double.NaN, double learningRadius = 5.0, bool jittering = true) {
352      var K = somSize * somSize;
353      var N = dissimilarities.Rows;
354      if (double.IsNaN(learningRate)) learningRate = 1.0 / Math.Sqrt(2.0 * N);
355      var fixedLearningRate = learningRate / 10.0;
356      var varLearningRate = 9.0 * fixedLearningRate;
357      Func<int, int, double> learningRateT = (maxIter, iter) => {
358        return varLearningRate * ((maxIter - iter) / (double)maxIter) + fixedLearningRate;
359      };
360      Func<int, int> getX = (neuron) => neuron % somSize;
361      Func<int, int> getY = (neuron) => neuron / somSize;
362      Func<int, int, int, int, double> neighborhood = (maxIter, iter, k, bmu) => {
363        var sigma = 1.0 * ((maxIter - iter) / (double)maxIter) + 0.0001;
364        var xK = getX(k);
365        var yK = getY(k);
366        var xW = getX(bmu);
367        var yW = getY(bmu);
368        var d = (xK - xW) * (xK - xW) + (yK - yW) * (yK - yW);
369        return Math.Exp(-d / (2.0 * sigma * sigma));
370      };
371      var alphas = Enumerable.Range(0, K).Select(k => Enumerable.Range(0, N).Select(_ => random.NextDouble()).ToArray()).ToArray();
372      // normalize s.t. sum(alphas[k]) = 1
373      for (var k = 0; k < K; k++) {
374        var sum = alphas[k].Sum();
375        for (var i = 0; i < alphas[k].Length; i++) alphas[k][i] /= sum;
376      }
377      var oldAlphas = alphas.Select(x => (double[])x.Clone()).ToArray();
378
379      for (var iter = 0; iter < iterations; iter++) {
380        var pointShuffle = Enumerable.Range(0, N).Shuffle(random).ToArray();
381        for (var p = 0; p < N; p++) {
382          var i = pointShuffle[p];
383          var bmu = GetBestMatchingUnit(dissimilarities, alphas, i);
384
385          for (var k = 0; k < K; k++) {
386            for (var j = 0; j < N; j++) {
387              alphas[k][j] = oldAlphas[k][j] + learningRateT(iterations, iter) * neighborhood(iterations, iter, k, bmu) * ((i == j ? 1.0 : 0.0) - oldAlphas[k][j]);
388            }
389          }
390        }
391        for (var k = 0; k < K; k++) {
392          for (var j = 0; j < N; j++) {
393            oldAlphas[k][j] = alphas[k][j];
394          }
395        }
396      }
397
398      var result = new DoubleMatrix(N, 2);
399      for (var i = 0; i < N; i++) {
400        var bmu = GetBestMatchingUnit(dissimilarities, alphas, i);
401        if (!jittering) {
402          result[i, 0] = getX(bmu);
403          result[i, 1] = getY(bmu);
404        } else {
405          result[i, 0] = getX(bmu) + random.NextDouble() * 0.8;
406          result[i, 1] = getY(bmu) + random.NextDouble() * 0.8;
407        }
408      }
409      return result;
410    }
411
412    private int GetBestMatchingUnit(DoubleMatrix D, double[][] alphas, int i) {
413      var bmu = -1;
414      var minV = double.MaxValue;
415      for (var k = 0; k < alphas.Length; k++) {
416        var Daki = 0.0;
417        var akDak = 0.0;
418        for (var r = 0; r < D.Rows; r++) {
419          var Dakr = 0.0;
420          for (var s = 0; s < D.Rows; s++) {
421            Dakr += D[r, s] * alphas[k][s];
422          }
423          if (r == i) Daki = Dakr;
424          akDak += alphas[k][r] * Dakr;
425        }
426        var v = Daki - 0.5 * akDak;
427        if (v < minV) {
428          bmu = k;
429          minV = v;
430        }
431      }
432      return bmu;
433    }
434    #endregion
435
436    private DoubleMatrix Mds(DoubleMatrix dissimilarities) {
437      return MultidimensionalScaling.KruskalShepard(dissimilarities);
438    }
439    #endregion
440  }
441}
Note: See TracBrowser for help on using the repository browser.