source: trunk/sources/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs @ 14654

Last change on this file since 14654 was 14654, checked in by abeham, 4 years ago

#2634:

  • restructured code a little to be easier to understand
  • removed some redundancies
File size: 56.6 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 System;
23using System.Collections.Generic;
24using System.ComponentModel;
25using System.Drawing;
26using System.Globalization;
27using System.Linq;
28using System.Windows.Forms;
29using System.Windows.Forms.DataVisualization.Charting;
30using HeuristicLab.Analysis;
31using HeuristicLab.Collections;
32using HeuristicLab.Core;
33using HeuristicLab.Core.Views;
34using HeuristicLab.Data;
35using HeuristicLab.MainForm;
36using HeuristicLab.MainForm.WindowsForms;
37
38namespace HeuristicLab.Optimization.Views {
39  [View("Run-length Distribution View")]
40  [Content(typeof(RunCollection), false)]
41  public partial class RunCollectionRLDView : ItemView {
42    private List<Series> invisibleTargetSeries;
43
44    private const string AllRuns = "All Runs";
45
46    private static readonly Color[] colors = new[] {
47      Color.FromArgb(0x40, 0x6A, 0xB7),
48      Color.FromArgb(0xB1, 0x6D, 0x01),
49      Color.FromArgb(0x4E, 0x8A, 0x06),
50      Color.FromArgb(0x75, 0x50, 0x7B),
51      Color.FromArgb(0x72, 0x9F, 0xCF),
52      Color.FromArgb(0xA4, 0x00, 0x00),
53      Color.FromArgb(0xAD, 0x7F, 0xA8),
54      Color.FromArgb(0x29, 0x50, 0xCF),
55      Color.FromArgb(0x90, 0xB0, 0x60),
56      Color.FromArgb(0xF5, 0x89, 0x30),
57      Color.FromArgb(0x55, 0x57, 0x53),
58      Color.FromArgb(0xEF, 0x59, 0x59),
59      Color.FromArgb(0xED, 0xD4, 0x30),
60      Color.FromArgb(0x63, 0xC2, 0x16),
61    };
62    private static readonly ChartDashStyle[] lineStyles = new[] {
63      ChartDashStyle.Solid,
64      ChartDashStyle.Dash,
65      ChartDashStyle.DashDot,
66      ChartDashStyle.Dot
67    };
68    private static readonly DataRowVisualProperties.DataRowLineStyle[] hlLineStyles = new[] {
69      DataRowVisualProperties.DataRowLineStyle.Solid,
70      DataRowVisualProperties.DataRowLineStyle.Dash,
71      DataRowVisualProperties.DataRowLineStyle.DashDot,
72      DataRowVisualProperties.DataRowLineStyle.Dot
73    };
74
75    public new RunCollection Content {
76      get { return (RunCollection)base.Content; }
77      set { base.Content = value; }
78    }
79
80    private double[] targets;
81    private double[] budgets;
82    private bool targetsAreRelative = true;
83    private readonly BindingList<ProblemInstance> problems;
84
85    private bool suppressUpdates;
86    private readonly IndexedDataTable<double> byCostDataTable;
87    public IndexedDataTable<double> ByCostDataTable {
88      get { return byCostDataTable; }
89    }
90
91    public RunCollectionRLDView() {
92      InitializeComponent();
93      invisibleTargetSeries = new List<Series>();
94
95      targetChart.CustomizeAllChartAreas();
96      targetChart.ChartAreas[0].CursorX.Interval = 1;
97      targetChart.SuppressExceptions = true;
98      byCostDataTable = new IndexedDataTable<double>("ECDF by Cost", "A data table containing the ECDF of each of a number of groups.") {
99        VisualProperties = {
100          YAxisTitle = "Proportion of unused budgets",
101          YAxisMinimumFixedValue = 0,
102          YAxisMinimumAuto = false,
103          YAxisMaximumFixedValue = 1,
104          YAxisMaximumAuto = false
105        }
106      };
107      byCostViewHost.Content = byCostDataTable;
108      suppressUpdates = true;
109      relativeOrAbsoluteComboBox.SelectedItem = targetsAreRelative ? "relative" : "absolute";
110      problems = new BindingList<ProblemInstance>();
111      problemComboBox.DataSource = new BindingSource() { DataSource = problems };
112      problemComboBox.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
113      suppressUpdates = false;
114    }
115
116    #region Content events
117    protected override void RegisterContentEvents() {
118      base.RegisterContentEvents();
119      Content.ItemsAdded += Content_ItemsAdded;
120      Content.ItemsRemoved += Content_ItemsRemoved;
121      Content.CollectionReset += Content_CollectionReset;
122      Content.UpdateOfRunsInProgressChanged += Content_UpdateOfRunsInProgressChanged;
123      Content.OptimizerNameChanged += Content_AlgorithmNameChanged;
124    }
125    protected override void DeregisterContentEvents() {
126      Content.ItemsAdded -= Content_ItemsAdded;
127      Content.ItemsRemoved -= Content_ItemsRemoved;
128      Content.CollectionReset -= Content_CollectionReset;
129      Content.UpdateOfRunsInProgressChanged -= Content_UpdateOfRunsInProgressChanged;
130      Content.OptimizerNameChanged -= Content_AlgorithmNameChanged;
131      base.DeregisterContentEvents();
132    }
133
134    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
135      if (suppressUpdates) return;
136      if (InvokeRequired) {
137        Invoke(new CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded), sender, e);
138        return;
139      }
140      UpdateGroupAndProblemComboBox();
141      UpdateDataTableComboBox();
142      foreach (var run in e.Items)
143        RegisterRunEvents(run);
144    }
145    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
146      if (suppressUpdates) return;
147      if (InvokeRequired) {
148        Invoke(new CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved), sender, e);
149        return;
150      }
151      UpdateGroupAndProblemComboBox();
152      UpdateDataTableComboBox();
153      foreach (var run in e.Items)
154        DeregisterRunEvents(run);
155    }
156    private void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
157      if (suppressUpdates) return;
158      if (InvokeRequired) {
159        Invoke(new CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset), sender, e);
160        return;
161      }
162      UpdateGroupAndProblemComboBox();
163      UpdateDataTableComboBox();
164      foreach (var run in e.OldItems)
165        DeregisterRunEvents(run);
166    }
167    private void Content_AlgorithmNameChanged(object sender, EventArgs e) {
168      if (InvokeRequired)
169        Invoke(new EventHandler(Content_AlgorithmNameChanged), sender, e);
170      else UpdateCaption();
171    }
172    private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
173      if (InvokeRequired) {
174        Invoke(new EventHandler(Content_UpdateOfRunsInProgressChanged), sender, e);
175        return;
176      }
177      suppressUpdates = Content.UpdateOfRunsInProgress;
178      if (!suppressUpdates) {
179        UpdateDataTableComboBox();
180        UpdateGroupAndProblemComboBox();
181        UpdateRuns();
182      }
183    }
184
185    private void RegisterRunEvents(IRun run) {
186      run.PropertyChanged += run_PropertyChanged;
187    }
188    private void DeregisterRunEvents(IRun run) {
189      run.PropertyChanged -= run_PropertyChanged;
190    }
191    private void run_PropertyChanged(object sender, PropertyChangedEventArgs e) {
192      if (suppressUpdates) return;
193      if (InvokeRequired) {
194        Invoke((Action<object, PropertyChangedEventArgs>)run_PropertyChanged, sender, e);
195      } else {
196        if (e.PropertyName == "Visible")
197          UpdateRuns();
198      }
199    }
200    #endregion
201
202    protected override void OnContentChanged() {
203      base.OnContentChanged();
204      dataTableComboBox.Items.Clear();
205      groupComboBox.Items.Clear();
206      targetChart.ChartAreas[0].AxisX.IsLogarithmic = false;
207      targetChart.Series.Clear();
208      invisibleTargetSeries.Clear();
209      byCostDataTable.VisualProperties.XAxisLogScale = false;
210      byCostDataTable.Rows.Clear();
211
212      UpdateCaption();
213      if (Content != null) {
214        UpdateGroupAndProblemComboBox();
215        UpdateDataTableComboBox();
216      }
217    }
218
219
220    private void UpdateGroupAndProblemComboBox() {
221      var selectedGroupItem = (string)groupComboBox.SelectedItem;
222
223      var groupings = Content.ParameterNames.OrderBy(x => x).ToArray();
224      groupComboBox.Items.Clear();
225      groupComboBox.Items.Add(AllRuns);
226      groupComboBox.Items.AddRange(groupings);
227      if (selectedGroupItem != null && groupComboBox.Items.Contains(selectedGroupItem)) {
228        groupComboBox.SelectedItem = selectedGroupItem;
229      } else if (groupComboBox.Items.Count > 0) {
230        groupComboBox.SelectedItem = groupComboBox.Items[0];
231      }
232
233      var table = (string)dataTableComboBox.SelectedItem;
234      var problemDict = CalculateBestTargetPerProblemInstance(table);
235
236      var problemTypesDifferent = problemDict.Keys.Select(x => x.ProblemType).Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1;
237      var problemNamesDifferent = problemDict.Keys.Select(x => x.ProblemName).Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1;
238      var evaluatorDifferent = problemDict.Keys.Select(x => x.Evaluator).Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1;
239      var maximizationDifferent = problemDict.Keys.Select(x => x.Maximization).Distinct().Count() > 1;
240      var allEqual = !problemTypesDifferent && !problemNamesDifferent && !evaluatorDifferent && !maximizationDifferent;
241
242      var selectedProblemItem = (ProblemInstance)problemComboBox.SelectedItem;
243      problems.Clear();
244      problems.Add(ProblemInstance.MatchAll);
245      if (problems[0].Equals(selectedProblemItem)) problemComboBox.SelectedItem = problems[0];
246      foreach (var p in problemDict.ToList()) {
247        p.Key.BestKnownQuality = p.Value;
248        p.Key.DisplayProblemType = problemTypesDifferent;
249        p.Key.DisplayProblemName = problemNamesDifferent || allEqual;
250        p.Key.DisplayEvaluator = evaluatorDifferent;
251        p.Key.DisplayMaximization = maximizationDifferent;
252        problems.Add(p.Key);
253        if (p.Key.Equals(selectedProblemItem)) problemComboBox.SelectedItem = p.Key;
254      }
255      SetEnabledStateOfControls();
256    }
257
258    private void UpdateDataTableComboBox() {
259      string selectedItem = (string)dataTableComboBox.SelectedItem;
260
261      dataTableComboBox.Items.Clear();
262      var dataTables = (from run in Content
263                        from result in run.Results
264                        where result.Value is IndexedDataTable<double>
265                        select result.Key).Distinct().ToArray();
266
267      dataTableComboBox.Items.AddRange(dataTables);
268      if (selectedItem != null && dataTableComboBox.Items.Contains(selectedItem)) {
269        dataTableComboBox.SelectedItem = selectedItem;
270      } else if (dataTableComboBox.Items.Count > 0) {
271        dataTableComboBox.SelectedItem = dataTableComboBox.Items[0];
272      }
273    }
274
275    protected override void SetEnabledStateOfControls() {
276      base.SetEnabledStateOfControls();
277      groupComboBox.Enabled = Content != null;
278      problemComboBox.Enabled = Content != null && problemComboBox.Items.Count > 1;
279      dataTableComboBox.Enabled = Content != null && dataTableComboBox.Items.Count > 1;
280      addTargetsAsResultButton.Enabled = Content != null && targets != null && dataTableComboBox.SelectedIndex >= 0;
281      addBudgetsAsResultButton.Enabled = Content != null && budgets != null && dataTableComboBox.SelectedIndex >= 0;
282    }
283
284    private Dictionary<string, Dictionary<ProblemInstance, List<IRun>>> GroupRuns() {
285      var groupedRuns = new Dictionary<string, Dictionary<ProblemInstance, List<IRun>>>();
286
287      var table = (string)dataTableComboBox.SelectedItem;
288      if (string.IsNullOrEmpty(table)) return groupedRuns;
289
290      var selectedGroup = (string)groupComboBox.SelectedItem;
291      if (string.IsNullOrEmpty(selectedGroup)) return groupedRuns;
292
293      var selectedProblem = (ProblemInstance)problemComboBox.SelectedItem;
294      if (selectedProblem == null) return groupedRuns;
295     
296      foreach (var x in (from r in Content
297                         where (selectedGroup == AllRuns || r.Parameters.ContainsKey(selectedGroup))
298                           && selectedProblem.Match(r)
299                           && r.Results.ContainsKey(table)
300                           && r.Visible
301                         let key = selectedGroup == AllRuns ? AllRuns : r.Parameters[selectedGroup].ToString()
302                         group r by key into g
303                         select Tuple.Create(g.Key, g.ToList()))) {
304        var pDict = problems.ToDictionary(r => r, _ => new List<IRun>());
305        foreach (var y in x.Item2) {
306          var pd = new ProblemInstance(y);
307          List<IRun> l;
308          if (pDict.TryGetValue(pd, out l)) l.Add(y);
309        }
310        groupedRuns[x.Item1] = pDict.Where(a => a.Value.Count > 0).ToDictionary(a => a.Key, a => a.Value);
311      }
312
313      return groupedRuns;
314    }
315
316    #region Performance analysis by (multiple) target(s)
317    private void UpdateResultsByTarget() {
318      // necessary to reset log scale -> empty chart cannot use log scaling
319      targetChart.ChartAreas[0].AxisX.IsLogarithmic = false;
320      targetChart.Series.Clear();
321      invisibleTargetSeries.Clear();
322
323      var table = (string)dataTableComboBox.SelectedItem;
324      if (string.IsNullOrEmpty(table)) return;
325
326      if (targets == null) GenerateDefaultTargets();
327
328      var groupedRuns = GroupRuns();
329      if (groupedRuns.Count == 0) return;
330
331      var xAxisTitles = new HashSet<string>();
332
333      // hits describes the number of target hits at a certain time for a certain group
334      var hits = new Dictionary<string, SortedList<double, int>>();
335      // misses describes the number of target misses after a certain time for a certain group
336      // for instance when a run ends, but has not achieved all targets, misses describes
337      // how many targets have been left open at the point when the run ended
338      var misses = new Dictionary<string, SortedList<double, int>>();
339
340      var aggregate = aggregateTargetsCheckBox.Checked;
341      double minEff = double.MaxValue, maxEff = double.MinValue;
342      var noRuns = 0;
343      foreach (var group in groupedRuns) {
344        SortedList<double, int> epdfHits = null, epdfMisses = null;
345        if (aggregate) {
346          hits[group.Key] = epdfHits = new SortedList<double, int>();
347          misses[group.Key] = epdfMisses = new SortedList<double, int>();
348        }
349        foreach (var problem in group.Value) {
350          var max = problem.Key.IsMaximization();
351          var absTargets = GetAbsoluteTargets(problem.Key).ToArray();
352          foreach (var run in problem.Value) {
353            noRuns++;
354            var resultsTable = (IndexedDataTable<double>)run.Results[table];
355            xAxisTitles.Add(resultsTable.VisualProperties.XAxisTitle);
356
357            var efforts = absTargets.Select(t => GetEffortToHitTarget(resultsTable.Rows.First().Values, t, max)).ToArray();
358            minEff = Math.Min(minEff, efforts.Min(x => x.Item2));
359            maxEff = Math.Max(maxEff, efforts.Max(x => x.Item2));
360            for (var idx = 0; idx < efforts.Length; idx++) {
361              var e = efforts[idx];
362              if (!aggregate) {
363                var key = group.Key + "@" + (targetsAreRelative
364                  ? (targets[idx] * 100).ToString(CultureInfo.CurrentCulture.NumberFormat) + "%"
365                  : targets[idx].ToString(CultureInfo.CurrentCulture.NumberFormat));
366                if (!hits.TryGetValue(key, out epdfHits))
367                  hits[key] = epdfHits = new SortedList<double, int>();
368                if (!misses.TryGetValue(key, out epdfMisses))
369                  misses[key] = epdfMisses = new SortedList<double, int>();
370              }
371              var list = e.Item1 ? epdfHits : epdfMisses;
372              int v;
373              if (list.TryGetValue(e.Item2, out v))
374                list[e.Item2] = v + 1;
375              else list[e.Item2] = 1;
376            }
377          }
378        }
379      }
380
381      UpdateTargetChartAxisXBounds(minEff, maxEff);
382
383      DrawTargetsEcdf(hits, misses, noRuns);
384
385      if (targets.Length == 1) {
386        if (targetsAreRelative)
387          targetChart.ChartAreas[0].AxisY.Title = "Probability to be " + (targets[0] * 100) + "% worse than best";
388        else targetChart.ChartAreas[0].AxisY.Title = "Probability to reach at least a fitness of " + targets[0];
389      } else targetChart.ChartAreas[0].AxisY.Title = "Proportion of reached targets";
390      targetChart.ChartAreas[0].AxisX.Title = string.Join(" / ", xAxisTitles);
391      targetChart.ChartAreas[0].AxisX.IsLogarithmic = CanDisplayLogarithmic();
392      targetChart.ChartAreas[0].CursorY.Interval = 0.05;
393
394      UpdateErtTables(groupedRuns);
395    }
396
397    private void DrawTargetsEcdf(Dictionary<string, SortedList<double, int>> hits, Dictionary<string, SortedList<double, int>> misses, int noRuns) {
398      var colorCount = 0;
399      var lineStyleCount = 0;
400     
401      var showMarkers = markerCheckBox.Checked;
402      foreach (var list in hits) {
403        var row = new Series(list.Key) {
404          ChartType = SeriesChartType.StepLine,
405          BorderWidth = 3,
406          Color = colors[colorCount],
407          BorderDashStyle = lineStyles[lineStyleCount],
408        };
409        var rowShade = new Series(list.Key + "-range") {
410          IsVisibleInLegend = false,
411          ChartType = SeriesChartType.Range,
412          Color = Color.FromArgb(32, colors[colorCount]),
413          YValuesPerPoint = 2
414        };
415
416        var ecdf = 0.0;
417        var missedecdf = 0.0;
418        var iter = misses[list.Key].GetEnumerator();
419        var moreMisses = iter.MoveNext();
420        var totalTargets = noRuns;
421        if (aggregateTargetsCheckBox.Checked) totalTargets *= targets.Length;
422        var movingTargets = totalTargets;
423        var labelPrinted = false;
424        foreach (var h in list.Value) {
425          var prevmissedecdf = missedecdf;
426          while (moreMisses && iter.Current.Key <= h.Key) {
427            if (!labelPrinted && row.Points.Count > 0) {
428              var point = row.Points.Last();
429              point.Label = row.Name;
430              point.MarkerStyle = MarkerStyle.Cross;
431              point.MarkerBorderWidth = 1;
432              point.MarkerSize = 10;
433              labelPrinted = true;
434              rowShade.Points.Add(new DataPoint(point.XValue, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
435            }
436            missedecdf += iter.Current.Value;
437            movingTargets -= iter.Current.Value;
438            if (row.Points.Count > 0 && row.Points.Last().XValue == iter.Current.Key) {
439              row.Points.Last().SetValueY(ecdf / movingTargets);
440              row.Points.Last().Color = Color.FromArgb(255 - (int)Math.Floor(255 * (prevmissedecdf / totalTargets)), colors[colorCount]);
441            } else {
442              var dp = new DataPoint(iter.Current.Key, ecdf / movingTargets) {
443                Color = Color.FromArgb(255 - (int)Math.Floor(255 * (prevmissedecdf / totalTargets)), colors[colorCount])
444              };
445              if (showMarkers) {
446                dp.MarkerStyle = MarkerStyle.Circle;
447                dp.MarkerBorderWidth = 1;
448                dp.MarkerSize = 5;
449              }
450              row.Points.Add(dp);
451              prevmissedecdf = missedecdf;
452            }
453            if (boundShadingCheckBox.Checked) {
454              if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == iter.Current.Key)
455                rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
456              else rowShade.Points.Add(new DataPoint(iter.Current.Key, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
457            }
458            moreMisses = iter.MoveNext();
459            if (!labelPrinted) {
460              var point = row.Points.Last();
461              point.Label = row.Name;
462              point.MarkerStyle = MarkerStyle.Cross;
463              point.MarkerBorderWidth = 1;
464              point.MarkerSize = 10;
465              labelPrinted = true;
466            }
467          }
468          ecdf += h.Value;
469          if (row.Points.Count > 0 && row.Points.Last().XValue == h.Key) {
470            row.Points.Last().SetValueY(ecdf / movingTargets);
471            row.Points.Last().Color = Color.FromArgb(255 - (int)Math.Floor(255 * (missedecdf / totalTargets)), colors[colorCount]);
472          } else {
473            var dp = new DataPoint(h.Key, ecdf / movingTargets) {
474              Color = Color.FromArgb(255 - (int)Math.Floor(255 * (missedecdf / totalTargets)), colors[colorCount])
475            };
476            if (showMarkers) {
477              dp.MarkerStyle = MarkerStyle.Circle;
478              dp.MarkerBorderWidth = 1;
479              dp.MarkerSize = 5;
480            }
481            row.Points.Add(dp);
482          }
483          if (missedecdf > 0 && boundShadingCheckBox.Checked) {
484            if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == h.Key)
485              rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
486            else rowShade.Points.Add(new DataPoint(h.Key, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
487          }
488        }
489
490        while (moreMisses) {
491          // if there are misses beyond the last hit we extend the shaded area
492          missedecdf += iter.Current.Value;
493          var dp = new DataPoint(iter.Current.Key, ecdf / movingTargets) {
494            Color = Color.FromArgb(255 - (int)Math.Floor(255 * (missedecdf / totalTargets)), colors[colorCount])
495          };
496          if (showMarkers) {
497            dp.MarkerStyle = MarkerStyle.Circle;
498            dp.MarkerBorderWidth = 1;
499            dp.MarkerSize = 5;
500          }
501          row.Points.Add(dp);
502          if (boundShadingCheckBox.Checked) {
503            rowShade.Points.Add(new DataPoint(iter.Current.Key, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
504          }
505          moreMisses = iter.MoveNext();
506
507          if (!labelPrinted && row.Points.Count > 0) {
508            var point = row.Points.Last();
509            point.Label = row.Name;
510            point.MarkerStyle = MarkerStyle.Cross;
511            point.MarkerBorderWidth = 1;
512            point.MarkerSize = 10;
513            labelPrinted = true;
514          }
515        }
516
517        if (!labelPrinted) {
518          var point = row.Points.Last();
519          point.Label = row.Name;
520          point.MarkerStyle = MarkerStyle.Cross;
521          point.MarkerBorderWidth = 1;
522          point.MarkerSize = 10;
523          rowShade.Points.Add(new DataPoint(point.XValue, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
524          labelPrinted = true;
525        }
526
527        ConfigureSeries(row);
528        targetChart.Series.Add(rowShade);
529        targetChart.Series.Add(row);
530
531        colorCount = (colorCount + 1) % colors.Length;
532        if (colorCount == 0) lineStyleCount = (lineStyleCount + 1) % lineStyles.Length;
533      }
534    }
535
536    private void UpdateTargetChartAxisXBounds(double minEff, double maxEff) {
537      var minZeros = (int)Math.Floor(Math.Log10(minEff));
538      var maxZeros = (int)Math.Floor(Math.Log10(maxEff));
539      var axisMin = (decimal)Math.Pow(10, minZeros);
540      var axisMax = (decimal)Math.Pow(10, maxZeros);
541      if (!targetLogScalingCheckBox.Checked) {
542        var minAdd = (decimal)Math.Pow(10, minZeros - 1) * 2;
543        var maxAdd = (decimal)Math.Pow(10, maxZeros - 1) * 2;
544        while (axisMin + minAdd < (decimal)minEff) axisMin += minAdd;
545        while (axisMax <= (decimal)maxEff) axisMax += maxAdd;
546      } else axisMax = (decimal)Math.Pow(10, (int)Math.Ceiling(Math.Log10(maxEff)));
547      targetChart.ChartAreas[0].AxisX.Minimum = (double)axisMin;
548      targetChart.ChartAreas[0].AxisX.Maximum = (double)axisMax;
549    }
550
551    private IEnumerable<double> GetAbsoluteTargets(ProblemInstance pInstance) {
552      if (!targetsAreRelative) return targets;
553      var maximization = pInstance.IsMaximization();
554      var bestKnown = pInstance.BestKnownQuality;
555      if (double.IsNaN(bestKnown)) throw new ArgumentException("Problem instance does not have a defined best - known quality.");
556      IEnumerable<double> tmp = null;
557      if (bestKnown > 0) {
558        tmp = targets.Select(x => (maximization ? (1 - x) : (1 + x)) * bestKnown);
559      } else if (bestKnown < 0) {
560        tmp = targets.Select(x => (!maximization ? (1 - x) : (1 + x)) * bestKnown);
561      } else {
562        // relative to 0 is impossible
563        tmp = targets;
564      }
565      return tmp;
566    }
567
568    private double[] GetAbsoluteTargetsWorstToBest(ProblemInstance pInstance) {
569      if (double.IsNaN(pInstance.BestKnownQuality)) throw new ArgumentException("Problem instance does not have a defined best-known quality.");
570      var absTargets = GetAbsoluteTargets(pInstance);
571      return (pInstance.IsMaximization()
572        ? absTargets.OrderBy(x => x) : absTargets.OrderByDescending(x => x)).ToArray();
573    }
574
575    private void GenerateDefaultTargets() {
576      targets = new[] { 0.1, 0.095, 0.09, 0.085, 0.08, 0.075, 0.07, 0.065, 0.06, 0.055, 0.05, 0.045, 0.04, 0.035, 0.03, 0.025, 0.02, 0.015, 0.01, 0.005, 0 };
577      suppressTargetsEvents = true;
578      targetsTextBox.Text = string.Join("% ; ", targets.Select(x => x * 100)) + "%";
579      suppressTargetsEvents = false;
580    }
581
582    private Tuple<bool, double> GetEffortToHitTarget(
583        IEnumerable<Tuple<double, double>> convergenceGraph,
584        double absTarget, bool maximization) {
585      var hit = false;
586      var effort = double.NaN;
587      foreach (var dent in convergenceGraph) {
588        effort = dent.Item1;
589        hit = maximization && dent.Item2 >= absTarget || !maximization && dent.Item2 <= absTarget;
590        if (hit) break;
591      }
592      if (double.IsNaN(effort)) throw new ArgumentException("Convergence graph is empty.", "convergenceGraph");
593      return Tuple.Create(hit, effort);
594    }
595
596    private void UpdateErtTables(Dictionary<string, Dictionary<ProblemInstance, List<IRun>>> groupedRuns) {
597      ertTableView.Content = null;
598      var columns = 1 + targets.Length + 1;
599      var matrix = new string[groupedRuns.Count * groupedRuns.Max(x => x.Value.Count) + groupedRuns.Max(x => x.Value.Count), columns];
600      var rowCount = 0;
601
602      var tableName = (string)dataTableComboBox.SelectedItem;
603      if (string.IsNullOrEmpty(tableName)) return;
604     
605      var problems = groupedRuns.SelectMany(x => x.Value.Keys).Distinct().ToList();
606
607      foreach (var problem in problems) {
608        var max = problem.IsMaximization();
609        matrix[rowCount, 0] = problem.ToString();
610        var absTargets = GetAbsoluteTargetsWorstToBest(problem);
611        for (var i = 0; i < absTargets.Length; i++) {
612          matrix[rowCount, i + 1] = absTargets[i].ToString(CultureInfo.CurrentCulture.NumberFormat);
613        }
614        matrix[rowCount, columns - 1] = "#succ";
615        rowCount++;
616
617        foreach (var group in groupedRuns) {
618          matrix[rowCount, 0] = group.Key;
619          if (!group.Value.ContainsKey(problem)) {
620            matrix[rowCount, columns - 1] = "N/A";
621            rowCount++;
622            continue;
623          }
624          var runs = group.Value[problem];
625          var result = default(ErtCalculationResult);
626          for (var i = 0; i < absTargets.Length; i++) {
627            result = ExpectedRuntimeHelper.CalculateErt(runs, tableName, absTargets[i], max);
628            matrix[rowCount, i + 1] = result.ToString();
629          }
630          matrix[rowCount, columns - 1] = targets.Length > 0 ? result.SuccessfulRuns + "/" + result.TotalRuns : "-";
631          rowCount++;
632        }
633      }
634      ertTableView.Content = new StringMatrix(matrix);
635      ertTableView.DataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
636    }
637    #endregion
638
639    #region Performance analysis by (multiple) budget(s)
640    private void UpdateResultsByCost() {
641      // necessary to reset log scale -> empty chart cannot use log scaling
642      byCostDataTable.VisualProperties.XAxisLogScale = false;
643      byCostDataTable.Rows.Clear();
644
645      var table = (string)dataTableComboBox.SelectedItem;
646      if (string.IsNullOrEmpty(table)) return;
647
648      if (budgets == null) GenerateDefaultBudgets(table);
649
650      var groupedRuns = GroupRuns();
651      if (groupedRuns.Count == 0) return;
652
653      var colorCount = 0;
654      var lineStyleCount = 0;
655     
656      foreach (var group in groupedRuns) {
657        var hits = new Dictionary<string, SortedList<double, double>>();
658
659        foreach (var problem in group.Value) {
660          foreach (var run in problem.Value) {
661            var resultsTable = (IndexedDataTable<double>)run.Results[table];
662
663            if (aggregateBudgetsCheckBox.Checked) {
664              CalculateHitsForAllBudgets(hits, resultsTable.Rows.First(), group.Value.Count, problem.Key, group.Key, problem.Value.Count);
665            } else {
666              CalculateHitsForEachBudget(hits, resultsTable.Rows.First(), group.Value.Count, problem.Key, group.Key, problem.Value.Count);
667            }
668          }
669        }
670
671        foreach (var list in hits) {
672          var row = new IndexedDataRow<double>(list.Key) {
673            VisualProperties = {
674              ChartType = DataRowVisualProperties.DataRowChartType.StepLine,
675              LineWidth = 2,
676              Color = colors[colorCount],
677              LineStyle = hlLineStyles[lineStyleCount],
678              StartIndexZero = false
679            }
680          };
681
682          var total = 0.0;
683          foreach (var h in list.Value) {
684            total += h.Value;
685            row.Values.Add(Tuple.Create(h.Key, total));
686          }
687
688          byCostDataTable.Rows.Add(row);
689        }
690        colorCount = (colorCount + 1) % colors.Length;
691        if (colorCount == 0) lineStyleCount = (lineStyleCount + 1) % lineStyles.Length;
692      }
693
694      byCostDataTable.VisualProperties.XAxisTitle = "Targets to Best-Known Ratio";
695      byCostDataTable.VisualProperties.XAxisLogScale = byCostDataTable.Rows.Count > 0 && budgetLogScalingCheckBox.Checked;
696    }
697
698    private void GenerateDefaultBudgets(string table) {
699      var runs = GroupRuns().SelectMany(x => x.Value.Values).SelectMany(x => x).ToList();
700      var min = runs.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Select(y => y.Item1).Min()).Min();
701      var max = runs.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Select(y => y.Item1).Max()).Max();
702      var points = 20;
703      budgets = Enumerable.Range(1, points).Select(x => min + (x / (double)points) * (max - min)).ToArray();
704      suppressBudgetsEvents = true;
705      budgetsTextBox.Text = string.Join(" ; ", budgets);
706      suppressBudgetsEvents = false;
707    }
708
709    private void CalculateHitsForEachBudget(Dictionary<string, SortedList<double, double>> hits, IndexedDataRow<double> row, int groupCount, ProblemInstance problem, string groupName, int problemCount) {
710      var max = problem.IsMaximization();
711      foreach (var b in budgets) {
712        var key = groupName + "-" + b;
713        if (!hits.ContainsKey(key)) hits.Add(key, new SortedList<double, double>());
714        Tuple<double, double> prev = null;
715        foreach (var v in row.Values) {
716          if (v.Item1 >= b) {
717            // the budget may be too low to achieve any target
718            if (prev == null && v.Item1 != b) break;
719            var tgt = ((prev == null || v.Item1 == b) ? v.Item2 : prev.Item2);
720            var relTgt = CalculateRelativeDifference(max, problem.BestKnownQuality, tgt) + 1;
721            if (hits[key].ContainsKey(relTgt))
722              hits[key][relTgt] += 1.0 / (groupCount * problemCount);
723            else hits[key][relTgt] = 1.0 / (groupCount * problemCount);
724            break;
725          }
726          prev = v;
727        }
728        if (hits[key].Count == 0) hits.Remove(key);
729      }
730    }
731
732    private void CalculateHitsForAllBudgets(Dictionary<string, SortedList<double, double>> hits, IndexedDataRow<double> row, int groupCount, ProblemInstance problem, string groupName, int problemCount) {
733      var values = row.Values;
734      if (!hits.ContainsKey(groupName)) hits.Add(groupName, new SortedList<double, double>());
735
736      var i = 0;
737      var j = 0;
738      Tuple<double, double> prev = null;
739      var max = problem.IsMaximization();
740      while (i < budgets.Length && j < values.Count) {
741        var current = values[j];
742        if (current.Item1 >= budgets[i]) {
743          if (prev != null || current.Item1 == budgets[i]) {
744            var tgt = (prev == null || current.Item1 == budgets[i]) ? current.Item2 : prev.Item2;
745            var relTgt = CalculateRelativeDifference(max, problem.BestKnownQuality, tgt) + 1;
746            if (!hits[groupName].ContainsKey(relTgt)) hits[groupName][relTgt] = 0;
747            hits[groupName][relTgt] += 1.0 / (groupCount * problemCount * budgets.Length);
748          }
749          i++;
750        } else {
751          j++;
752          prev = current;
753        }
754      }
755      var lastTgt = values.Last().Item2;
756      var lastRelTgt = CalculateRelativeDifference(max, problem.BestKnownQuality, lastTgt) + 1;
757      if (i < budgets.Length && !hits[groupName].ContainsKey(lastRelTgt)) hits[groupName][lastRelTgt] = 0;
758      while (i < budgets.Length) {
759        hits[groupName][lastRelTgt] += 1.0 / (groupCount * problemCount * budgets.Length);
760        i++;
761      }
762    }
763    #endregion
764
765    private void UpdateCaption() {
766      Caption = Content != null ? Content.OptimizerName + " RLD View" : ViewAttribute.GetViewName(GetType());
767    }
768
769    private void groupComboBox_SelectedIndexChanged(object sender, EventArgs e) {
770      UpdateRuns();
771      SetEnabledStateOfControls();
772    }
773    private void problemComboBox_SelectedIndexChanged(object sender, EventArgs e) {
774      UpdateRuns();
775      SetEnabledStateOfControls();
776    }
777    private void dataTableComboBox_SelectedIndexChanged(object sender, EventArgs e) {
778      if (dataTableComboBox.SelectedIndex >= 0)
779        GenerateDefaultBudgets((string)dataTableComboBox.SelectedItem);
780      UpdateBestKnownQualities();
781      UpdateRuns();
782      SetEnabledStateOfControls();
783    }
784
785    private void logScalingCheckBox_CheckedChanged(object sender, EventArgs e) {
786      UpdateResultsByTarget();
787      byCostDataTable.VisualProperties.XAxisLogScale = byCostDataTable.Rows.Count > 0 && budgetLogScalingCheckBox.Checked;
788    }
789
790    private void boundShadingCheckBox_CheckedChanged(object sender, EventArgs e) {
791      UpdateResultsByTarget();
792    }
793
794    #region Event handlers for target analysis
795    private bool suppressTargetsEvents;
796    private void targetsTextBox_Validating(object sender, CancelEventArgs e) {
797      if (suppressTargetsEvents) return;
798      var targetStrings = targetsTextBox.Text.Split(new[] { '%', ';', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
799      var targetList = new List<decimal>();
800      foreach (var ts in targetStrings) {
801        decimal t;
802        if (!decimal.TryParse(ts, out t)) {
803          errorProvider.SetError(targetsTextBox, "Not all targets can be parsed: " + ts);
804          e.Cancel = true;
805          return;
806        }
807        if (targetsAreRelative)
808          targetList.Add(t / 100);
809        else targetList.Add(t);
810      }
811      if (targetList.Count == 0) {
812        errorProvider.SetError(targetsTextBox, "Give at least one target value!");
813        e.Cancel = true;
814        return;
815      }
816      e.Cancel = false;
817      errorProvider.SetError(targetsTextBox, null);
818      targets = targetsAreRelative ? targetList.Select(x => (double)x).OrderByDescending(x => x).ToArray() : targetList.Select(x => (double)x).ToArray();
819      suppressTargetsEvents = true;
820      try {
821        if (targetsAreRelative)
822          targetsTextBox.Text = string.Join("% ; ", targets.Select(x => x * 100)) + "%";
823        else targetsTextBox.Text = string.Join(" ; ", targets);
824      } finally { suppressTargetsEvents = false; }
825
826      UpdateResultsByTarget();
827      SetEnabledStateOfControls();
828    }
829
830    private void aggregateTargetsCheckBox_CheckedChanged(object sender, EventArgs e) {
831      SuspendRepaint();
832      try {
833        UpdateResultsByTarget();
834      } finally { ResumeRepaint(true); }
835    }
836
837    private void relativeOrAbsoluteComboBox_SelectedIndexChanged(object sender, EventArgs e) {
838      if (suppressUpdates) return;
839      var pd = (ProblemInstance)problemComboBox.SelectedItem;
840      if (!double.IsNaN(pd.BestKnownQuality)) {
841        var max = pd.IsMaximization();
842        if (targetsAreRelative) targets = GetAbsoluteTargets(pd).ToArray();
843        else {
844          // Rounding to 5 digits since it's certainly appropriate for this application
845          if (pd.BestKnownQuality > 0) {
846            targets = targets.Select(x => Math.Round(max ? 1.0 - (x / pd.BestKnownQuality) : (x / pd.BestKnownQuality) - 1.0, 5)).ToArray();
847          } else if (pd.BestKnownQuality < 0) {
848            targets = targets.Select(x => Math.Round(!max ? 1.0 - (x / pd.BestKnownQuality) : (x / pd.BestKnownQuality) - 1.0, 5)).ToArray();
849          }
850        }
851      }
852      targetsAreRelative = (string)relativeOrAbsoluteComboBox.SelectedItem == "relative";
853      SuspendRepaint();
854      suppressTargetsEvents = true;
855      try {
856        if (targetsAreRelative)
857          targetsTextBox.Text = string.Join("% ; ", targets.Select(x => x * 100)) + "%";
858        else targetsTextBox.Text = string.Join(" ; ", targets);
859
860        UpdateResultsByTarget();
861      } finally { suppressTargetsEvents = false; ResumeRepaint(true); }
862    }
863
864    private void generateTargetsButton_Click(object sender, EventArgs e) {
865      decimal max = 1, min = 0, count = 10;
866      if (targets != null) {
867        max = (decimal)targets.Max();
868        min = (decimal)targets.Min();
869        count = targets.Length;
870      } else if (Content.Count > 0 && dataTableComboBox.SelectedIndex >= 0) {
871        var table = (string)dataTableComboBox.SelectedItem;
872        max = (decimal)Content.Where(x => x.Results.ContainsKey(table)).Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Max(y => y.Item2)).Max();
873        min = (decimal)Content.Where(x => x.Results.ContainsKey(table)).Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Min(y => y.Item2)).Min();
874        count = 6;
875      }
876      using (var dialog = new DefineArithmeticProgressionDialog(false, min, max, (max - min) / count)) {
877        if (dialog.ShowDialog() == DialogResult.OK) {
878          if (dialog.Values.Any()) {
879            targets = dialog.Values.Select(x => (double)x).ToArray();
880            suppressTargetsEvents = true;
881            if (targetsAreRelative)
882              targetsTextBox.Text = string.Join("% ; ", targets);
883            else targetsTextBox.Text = string.Join(" ; ", targets);
884            suppressTargetsEvents = false;
885
886            UpdateResultsByTarget();
887            SetEnabledStateOfControls();
888          }
889        }
890      }
891    }
892
893    private void addTargetsAsResultButton_Click(object sender, EventArgs e) {
894      var table = (string)dataTableComboBox.SelectedItem;
895      if (string.IsNullOrEmpty(table)) return;
896     
897      foreach (var run in Content) {
898        if (!run.Results.ContainsKey(table)) continue;
899        var resultsTable = (IndexedDataTable<double>)run.Results[table];
900        var values = resultsTable.Rows.First().Values;
901        var i = 0;
902        var j = 0;
903        var pd = new ProblemInstance(run);
904        var max = pd.IsMaximization();
905        var absTargets = GetAbsoluteTargetsWorstToBest(problems.Single(x => x.Equals(pd)));
906        while (i < absTargets.Length && j < values.Count) {
907          var target = absTargets[i];
908          var current = values[j];
909          if (max && current.Item2 >= target || !max && current.Item2 <= target) {
910            run.Results[table + ".Target" + target] = new DoubleValue(current.Item1);
911            i++;
912          } else {
913            j++;
914          }
915        }
916      }
917    }
918
919    private void markerCheckBox_CheckedChanged(object sender, EventArgs e) {
920      SuspendRepaint();
921      try {
922        UpdateResultsByTarget();
923      } finally { ResumeRepaint(true); }
924    }
925    #endregion
926
927    #region Event handlers for cost analysis
928    private bool suppressBudgetsEvents;
929    private void budgetsTextBox_Validating(object sender, CancelEventArgs e) {
930      if (suppressBudgetsEvents) return;
931      var budgetStrings = budgetsTextBox.Text.Split(new[] { ';', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
932      var budgetList = new List<double>();
933      foreach (var ts in budgetStrings) {
934        double b;
935        if (!double.TryParse(ts, out b)) {
936          errorProvider.SetError(budgetsTextBox, "Not all targets can be parsed: " + ts);
937          e.Cancel = true;
938          return;
939        }
940        budgetList.Add(b);
941      }
942      if (budgetList.Count == 0) {
943        errorProvider.SetError(budgetsTextBox, "Give at least one target value!");
944        e.Cancel = true;
945        return;
946      }
947      e.Cancel = false;
948      errorProvider.SetError(budgetsTextBox, null);
949      budgets = budgetList.ToArray();
950      UpdateResultsByCost();
951      SetEnabledStateOfControls();
952    }
953
954    private void aggregateBudgetsCheckBox_CheckedChanged(object sender, EventArgs e) {
955      SuspendRepaint();
956      try {
957        UpdateResultsByCost();
958      } finally { ResumeRepaint(true); }
959    }
960
961    private void generateBudgetsButton_Click(object sender, EventArgs e) {
962      decimal max = 1, min = 0, count = 10;
963      if (budgets != null) {
964        max = (decimal)budgets.Max();
965        min = (decimal)budgets.Min();
966        count = budgets.Length;
967      } else if (Content.Count > 0 && dataTableComboBox.SelectedIndex >= 0) {
968        var table = (string)dataTableComboBox.SelectedItem;
969        min = (decimal)Content.Where(x => x.Results.ContainsKey(table)).Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Min(y => y.Item1)).Min();
970        max = (decimal)Content.Where(x => x.Results.ContainsKey(table)).Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Max(y => y.Item1)).Max();
971        count = 6;
972      }
973      using (var dialog = new DefineArithmeticProgressionDialog(false, min, max, (max - min) / count)) {
974        if (dialog.ShowDialog() == DialogResult.OK) {
975          if (dialog.Values.Any()) {
976            budgets = dialog.Values.OrderBy(x => x).Select(x => (double)x).ToArray();
977
978            suppressBudgetsEvents = true;
979            budgetsTextBox.Text = string.Join(" ; ", budgets);
980            suppressBudgetsEvents = false;
981
982            UpdateResultsByCost();
983            SetEnabledStateOfControls();
984          }
985        }
986      }
987    }
988
989    private void addBudgetsAsResultButton_Click(object sender, EventArgs e) {
990      var table = (string)dataTableComboBox.SelectedItem;
991      var budgetStrings = budgetsTextBox.Text.Split(new[] { ';', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
992      if (budgetStrings.Length == 0) {
993        MessageBox.Show("Define a number of budgets.");
994        return;
995      }
996      var budgetList = new List<double>();
997      foreach (var bs in budgetStrings) {
998        double v;
999        if (!double.TryParse(bs, out v)) {
1000          MessageBox.Show("Budgets must be a valid number: " + bs);
1001          return;
1002        }
1003        budgetList.Add(v);
1004      }
1005      budgetList.Sort();
1006
1007      foreach (var run in Content) {
1008        if (!run.Results.ContainsKey(table)) continue;
1009        var resultsTable = (IndexedDataTable<double>)run.Results[table];
1010        var values = resultsTable.Rows.First().Values;
1011        var i = 0;
1012        var j = 0;
1013        Tuple<double, double> prev = null;
1014        while (i < budgetList.Count && j < values.Count) {
1015          var current = values[j];
1016          if (current.Item1 >= budgetList[i]) {
1017            if (prev != null || current.Item1 == budgetList[i]) {
1018              var tgt = (prev == null || current.Item1 == budgetList[i]) ? current.Item2 : prev.Item2;
1019              run.Results[table + ".Cost" + budgetList[i]] = new DoubleValue(tgt);
1020            }
1021            i++;
1022          } else {
1023            j++;
1024            prev = current;
1025          }
1026        }
1027      }
1028    }
1029    #endregion
1030
1031    #region Helpers
1032    private double CalculateRelativeDifference(bool maximization, double bestKnown, double fit) {
1033      if (bestKnown == 0) {
1034        // no relative difference with respect to bestKnown possible
1035        return maximization ? -fit : fit;
1036      }
1037      var absDiff = (fit - bestKnown);
1038      var relDiff = absDiff / bestKnown;
1039      if (maximization) {
1040        return bestKnown > 0 ? -relDiff : relDiff;
1041      } else {
1042        return bestKnown > 0 ? relDiff : -relDiff;
1043      }
1044    }
1045
1046    private Dictionary<ProblemInstance, double> CalculateBestTargetPerProblemInstance(string table) {
1047      if (table == null) table = string.Empty;
1048      return (from r in Content
1049              where r.Visible
1050              let pd = new ProblemInstance(r)
1051              let target = r.Parameters.ContainsKey("BestKnownQuality")
1052                           && r.Parameters["BestKnownQuality"] is DoubleValue
1053                ? ((DoubleValue)r.Parameters["BestKnownQuality"]).Value
1054                : (r.Results.ContainsKey(table) && r.Results[table] is IndexedDataTable<double>
1055                  ? ((IndexedDataTable<double>)r.Results[table]).Rows.First().Values.Last().Item2
1056                  : double.NaN)
1057              group target by pd into g
1058              select new { Problem = g.Key, Target = g.Any(x => !double.IsNaN(x)) ? (g.Key.IsMaximization() ? g.Max() : g.Where(x => !double.IsNaN(x)).Min()) : double.NaN })
1059        .ToDictionary(x => x.Problem, x => x.Target);
1060    }
1061
1062    private void UpdateRuns() {
1063      if (InvokeRequired) {
1064        Invoke((Action)UpdateRuns);
1065        return;
1066      }
1067      SuspendRepaint();
1068      try {
1069        UpdateResultsByTarget();
1070        UpdateResultsByCost();
1071      } finally { ResumeRepaint(true); }
1072    }
1073
1074    private void UpdateBestKnownQualities() {
1075      var table = (string)dataTableComboBox.SelectedItem;
1076      if (string.IsNullOrEmpty(table)) return;
1077
1078      var targetsPerProblem = CalculateBestTargetPerProblemInstance(table);
1079      foreach (var pd in problems) {
1080        double bkq;
1081        if (targetsPerProblem.TryGetValue(pd, out bkq))
1082          pd.BestKnownQuality = bkq;
1083        else pd.BestKnownQuality = double.NaN;
1084      }
1085      //problemComboBox.ResetBindings();
1086    }
1087    #endregion
1088
1089    private void ConfigureSeries(Series series) {
1090      series.SmartLabelStyle.Enabled = true;
1091      series.SmartLabelStyle.AllowOutsidePlotArea = LabelOutsidePlotAreaStyle.No;
1092      series.SmartLabelStyle.CalloutLineAnchorCapStyle = LineAnchorCapStyle.None;
1093      series.SmartLabelStyle.CalloutLineColor = series.Color;
1094      series.SmartLabelStyle.CalloutLineWidth = 2;
1095      series.SmartLabelStyle.CalloutStyle = LabelCalloutStyle.Underlined;
1096      series.SmartLabelStyle.IsOverlappedHidden = false;
1097      series.SmartLabelStyle.MaxMovingDistance = 200;
1098      series.ToolTip = series.LegendText + " X = #VALX, Y = #VALY";
1099    }
1100
1101    private void chart_MouseDown(object sender, MouseEventArgs e) {
1102      HitTestResult result = targetChart.HitTest(e.X, e.Y);
1103      if (result.ChartElementType == ChartElementType.LegendItem) {
1104        ToggleTargetChartSeriesVisible(result.Series);
1105      }
1106    }
1107    private void chart_MouseMove(object sender, MouseEventArgs e) {
1108      HitTestResult result = targetChart.HitTest(e.X, e.Y);
1109      if (result.ChartElementType == ChartElementType.LegendItem)
1110        this.Cursor = Cursors.Hand;
1111      else
1112        this.Cursor = Cursors.Default;
1113    }
1114    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
1115      foreach (LegendItem legendItem in e.LegendItems) {
1116        var series = targetChart.Series[legendItem.SeriesName];
1117        if (series != null) {
1118          bool seriesIsInvisible = invisibleTargetSeries.Any(x => x.Name == series.Name);
1119          foreach (LegendCell cell in legendItem.Cells) {
1120            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
1121          }
1122        }
1123      }
1124    }
1125
1126    private void ToggleTargetChartSeriesVisible(Series series) {
1127      var indexList = invisibleTargetSeries.FindIndex(x => x.Name == series.Name);
1128      var indexChart = targetChart.Series.IndexOf(series);
1129      if (targetChart.Series.Count == 1) targetChart.ChartAreas[0].AxisX.IsLogarithmic = false;
1130      targetChart.Series.RemoveAt(indexChart);
1131      var s = indexList >= 0 ? invisibleTargetSeries[indexList] : new Series(series.Name) {
1132        Color = series.Color,
1133        ChartType = series.ChartType,
1134        BorderWidth = series.BorderWidth,
1135        BorderDashStyle = series.BorderDashStyle
1136      };
1137      if (indexList < 0) {
1138        // hide
1139        invisibleTargetSeries.Add(series);
1140        var shadeSeries = targetChart.Series.FirstOrDefault(x => x.Name == series.Name + "-range");
1141        if (shadeSeries != null) {
1142          if (targetChart.Series.Count == 1) targetChart.ChartAreas[0].AxisX.IsLogarithmic = false;
1143          targetChart.Series.Remove(shadeSeries);
1144          invisibleTargetSeries.Add(shadeSeries);
1145          indexChart--;
1146        }
1147      } else {
1148        // show
1149        invisibleTargetSeries.RemoveAt(indexList);
1150        var shadeSeries = invisibleTargetSeries.FirstOrDefault(x => x.Name == series.Name + "-range");
1151        if (shadeSeries != null) {
1152          invisibleTargetSeries.Remove(shadeSeries);
1153          InsertOrAddSeries(indexChart, shadeSeries);
1154          indexChart++;
1155        }
1156      }
1157      InsertOrAddSeries(indexChart, s);
1158      targetChart.ChartAreas[0].AxisX.IsLogarithmic = CanDisplayLogarithmic();
1159    }
1160
1161    private bool CanDisplayLogarithmic() {
1162      return targetLogScalingCheckBox.Checked
1163        && targetChart.Series.Count > 0 // must have a series
1164        && targetChart.Series.Any(x => x.Points.Count > 0) // at least one series must have points
1165        && targetChart.Series.All(s => s.Points.All(p => p.XValue > 0)); // all points must be positive
1166    }
1167
1168    private void InsertOrAddSeries(int index, Series s) {
1169      if (targetChart.Series.Count <= index)
1170        targetChart.Series.Add(s);
1171      else targetChart.Series.Insert(index, s);
1172    }
1173
1174    private class ProblemInstance : INotifyPropertyChanged {
1175      private readonly bool matchAll;
1176      public static readonly ProblemInstance MatchAll = new ProblemInstance() {
1177        ProblemName = "All with Best-Known"
1178      };
1179
1180      private ProblemInstance() {
1181        ProblemType = string.Empty;
1182        ProblemName = string.Empty;
1183        Evaluator = string.Empty;
1184        Maximization = string.Empty;
1185        DisplayProblemType = false;
1186        DisplayProblemName = false;
1187        DisplayEvaluator = false;
1188        DisplayMaximization = false;
1189        matchAll = true;
1190        BestKnownQuality = double.NaN;
1191      }
1192
1193      public ProblemInstance(IRun run) {
1194        ProblemType = GetStringValueOrEmpty(run, "Problem Type");
1195        ProblemName = GetStringValueOrEmpty(run, "Problem Name");
1196        Evaluator = GetStringValueOrEmpty(run, "Evaluator");
1197        Maximization = GetMaximizationValueOrEmpty(run, "Maximization");
1198        DisplayProblemType = !string.IsNullOrEmpty(ProblemType);
1199        DisplayProblemName = !string.IsNullOrEmpty(ProblemName);
1200        DisplayEvaluator = !string.IsNullOrEmpty(Evaluator);
1201        DisplayMaximization = !string.IsNullOrEmpty(Maximization);
1202        matchAll = false;
1203        BestKnownQuality = GetDoubleValueOrNaN(run, "BestKnownQuality");
1204      }
1205
1206      private bool displayProblemType;
1207      public bool DisplayProblemType {
1208        get { return displayProblemType; }
1209        set {
1210          if (displayProblemType == value) return;
1211          displayProblemType = value;
1212          OnPropertyChanged("DisplayProblemType");
1213        }
1214      }
1215      private string problemType;
1216      public string ProblemType {
1217        get { return problemType; }
1218        set {
1219          if (problemType == value) return;
1220          problemType = value;
1221          OnPropertyChanged("ProblemType");
1222        }
1223      }
1224      private bool displayProblemName;
1225      public bool DisplayProblemName {
1226        get { return displayProblemName; }
1227        set {
1228          if (displayProblemName == value) return;
1229          displayProblemName = value;
1230          OnPropertyChanged("DisplayProblemName");
1231        }
1232      }
1233      private string problemName;
1234      public string ProblemName {
1235        get { return problemName; }
1236        set {
1237          if (problemName == value) return;
1238          problemName = value;
1239          OnPropertyChanged("ProblemName");
1240        }
1241      }
1242      private bool displayEvaluator;
1243      public bool DisplayEvaluator {
1244        get { return displayEvaluator; }
1245        set {
1246          if (displayEvaluator == value) return;
1247          displayEvaluator = value;
1248          OnPropertyChanged("DisplayEvaluator");
1249        }
1250      }
1251      private string evaluator;
1252      public string Evaluator {
1253        get { return evaluator; }
1254        set {
1255          if (evaluator == value) return;
1256          evaluator = value;
1257          OnPropertyChanged("Evaluator");
1258        }
1259      }
1260      private bool displayMaximization;
1261      public bool DisplayMaximization {
1262        get { return displayMaximization; }
1263        set {
1264          if (displayMaximization == value) return;
1265          displayMaximization = value;
1266          OnPropertyChanged("DisplayMaximization");
1267        }
1268      }
1269      private string maximization;
1270      public string Maximization {
1271        get { return maximization; }
1272        set {
1273          if (maximization == value) return;
1274          maximization = value;
1275          OnPropertyChanged("Maximization");
1276        }
1277      }
1278      private double bestKnownQuality;
1279      public double BestKnownQuality {
1280        get { return bestKnownQuality; }
1281        set {
1282          if (bestKnownQuality == value) return;
1283          bestKnownQuality = value;
1284          OnPropertyChanged("BestKnownQuality");
1285        }
1286      }
1287
1288      public bool IsMaximization() {
1289        return Maximization == "MAX";
1290      }
1291
1292      public bool Match(IRun run) {
1293        return matchAll ||
1294               GetStringValueOrEmpty(run, "Problem Type") == ProblemType
1295               && GetStringValueOrEmpty(run, "Problem Name") == ProblemName
1296               && GetStringValueOrEmpty(run, "Evaluator") == Evaluator
1297               && GetMaximizationValueOrEmpty(run, "Maximization") == Maximization;
1298      }
1299
1300      private double GetDoubleValueOrNaN(IRun run, string key) {
1301        IItem param;
1302        if (run.Parameters.TryGetValue(key, out param)) {
1303          var dv = param as DoubleValue;
1304          return dv != null ? dv.Value : double.NaN;
1305        }
1306        return double.NaN;
1307      }
1308
1309      private string GetStringValueOrEmpty(IRun run, string key) {
1310        IItem param;
1311        if (run.Parameters.TryGetValue(key, out param)) {
1312          var sv = param as StringValue;
1313          return sv != null ? sv.Value : string.Empty;
1314        }
1315        return string.Empty;
1316      }
1317
1318      private string GetMaximizationValueOrEmpty(IRun run, string key) {
1319        IItem param;
1320        if (run.Parameters.TryGetValue(key, out param)) {
1321          var bv = param as BoolValue;
1322          return bv != null ? (bv.Value ? "MAX" : "MIN") : string.Empty;
1323        }
1324        return string.Empty;
1325      }
1326
1327      public override bool Equals(object obj) {
1328        var other = obj as ProblemInstance;
1329        if (other == null) return false;
1330        return ProblemType == other.ProblemType
1331               && ProblemName == other.ProblemName
1332               && Evaluator == other.Evaluator
1333               && Maximization == other.Maximization;
1334      }
1335
1336      public override int GetHashCode() {
1337        return ProblemType.GetHashCode() ^ ProblemName.GetHashCode() ^ Evaluator.GetHashCode() ^ Maximization.GetHashCode();
1338      }
1339
1340      public override string ToString() {
1341        return string.Join("  --  ", new[] {
1342          (DisplayProblemType ? ProblemType : string.Empty),
1343          (DisplayProblemName ? ProblemName : string.Empty),
1344          (DisplayEvaluator ? Evaluator : string.Empty),
1345          (DisplayMaximization ? Maximization : string.Empty),
1346          !double.IsNaN(BestKnownQuality) ? BestKnownQuality.ToString(CultureInfo.CurrentCulture.NumberFormat) : string.Empty }.Where(x => !string.IsNullOrEmpty(x)));
1347      }
1348
1349      public event PropertyChangedEventHandler PropertyChanged;
1350      protected virtual void OnPropertyChanged(string propertyName = null) {
1351        var handler = PropertyChanged;
1352        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
1353      }
1354    }
1355  }
1356}
Note: See TracBrowser for help on using the repository browser.