Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ScopedBasicAlgorithm/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs @ 14420

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

#2708: added binary version of mempr with new concepts of scope in basic alg

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