Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2634: Integrated RLD analysis into trunk

  • HeuristicLab.Analysis:
    • IndexedDataTable<T>, IndexedDataRow<T>
    • QualityPerClockAnalyzer, QualityPerEvaluationsAnalyzer
    • ExpectedRuntimeHelper
  • HeuristicLab.Analysis.Views:
    • IndexedDataTableView
  • HeuristicLab.Optimization.Views:
    • RunCollectionRLDView

To test:

  1. Configure an algorithm/problem combination
  2. In the algorithm's analyzers add the QualityPerEvaluationsAnalyzer
  3. Make sure the BestAverageWorstQualityAnalyzer is executed before
  4. Run the algorithm several times
  5. In the Runs tab select the "Run-length Distribution View"
File size: 50.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.ComponentModel;
25using System.Drawing;
26using System.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          };
411
412          var ecdf = 0.0;
413          var missedecdf = 0.0;
414          var iter = misses[list.Key].GetEnumerator();
415          var moreMisses = iter.MoveNext();
416          var totalTargets = noRuns;
417          if (aggregateTargetsCheckBox.Checked) totalTargets *= targets.Length;
418          var movingTargets = totalTargets;
419          foreach (var h in list.Value) {
420            while (moreMisses && iter.Current.Key <= h.Key) {
421              missedecdf += iter.Current.Value;
422              movingTargets -= iter.Current.Value;
423              if (row.Points.Count > 0 && row.Points.Last().XValue == iter.Current.Key)
424                row.Points.Last().SetValueY(ecdf / movingTargets);
425              else row.Points.AddXY(iter.Current.Key, ecdf / movingTargets);
426              if (boundShadingCheckBox.Checked) {
427                if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == iter.Current.Key)
428                  rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
429                else rowShade.Points.Add(new DataPoint(iter.Current.Key, new[] { ecdf / totalTargets, (ecdf + missedecdf) / totalTargets }));
430              }
431              moreMisses = iter.MoveNext();
432            }
433            ecdf += h.Value;
434            if (row.Points.Count > 0 && row.Points.Last().XValue == h.Key)
435              row.Points.Last().SetValueY(ecdf / movingTargets);
436            else row.Points.AddXY(h.Key, ecdf / movingTargets);
437            if (missedecdf > 0 && boundShadingCheckBox.Checked) {
438              if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == h.Key)
439                rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
440              else rowShade.Points.Add(new DataPoint(h.Key, new[] { ecdf / totalTargets, (ecdf + missedecdf) / totalTargets }));
441            }
442          }
443
444          while (moreMisses) {
445            // if there are misses beyond the last hit we extend the shaded area
446            missedecdf += iter.Current.Value;
447            //movingTargets -= iter.Current.Value;
448            if (row.Points.Count > 0 && row.Points.Last().XValue == iter.Current.Key)
449              row.Points.Last().SetValueY(ecdf / movingTargets);
450            else row.Points.AddXY(iter.Current.Key, ecdf / movingTargets);
451            if (boundShadingCheckBox.Checked) {
452              if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == iter.Current.Key)
453                rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
454              else rowShade.Points.Add(new DataPoint(iter.Current.Key, new[] { ecdf / totalTargets, (ecdf + missedecdf) / totalTargets }));
455            }
456            moreMisses = iter.MoveNext();
457          }
458
459          if (maxLength > 0 && (row.Points.Count == 0 || row.Points.Last().XValue < maxLength))
460            row.Points.AddXY(maxLength, ecdf / movingTargets);
461
462          if (row.Points.Count > 0) {
463            var point = row.Points.Last();
464            point.Label = row.Name;
465            point.MarkerStyle = MarkerStyle.Cross;
466            point.MarkerBorderWidth = 1;
467          }
468
469          ConfigureSeries(row);
470          targetChart.Series.Add(rowShade);
471          targetChart.Series.Add(row);
472        }
473        colorCount = (colorCount + 1) % colors.Length;
474        if (colorCount == 0) lineStyleCount = (lineStyleCount + 1) % lineStyles.Length;
475      }
476
477      if (targets.Length == 1)
478        targetChart.ChartAreas[0].AxisY.Title = "Probability to be " + (targets[0] * 100) + "% worse than best";
479      else targetChart.ChartAreas[0].AxisY.Title = "Proportion of reached targets";
480      targetChart.ChartAreas[0].AxisX.Title = string.Join(" / ", xAxisTitles);
481      targetChart.ChartAreas[0].AxisX.IsLogarithmic = CanDisplayLogarithmic();
482      targetChart.ChartAreas[0].CursorY.Interval = 0.05;
483      UpdateErtTables(groupedRuns);
484    }
485
486    private void GenerateDefaultTargets() {
487      targets = new[] { 0.1, 0.05, 0.02, 0.01, 0 };
488      suppressTargetsEvents = true;
489      targetsTextBox.Text = string.Join("% ; ", targets.Select(x => x * 100)) + "%";
490      suppressTargetsEvents = false;
491    }
492
493    private void CalculateHitsForEachTarget(Dictionary<string, SortedList<double, int>> hits,
494                                            Dictionary<string, SortedList<double, int>> misses,
495                                            IndexedDataRow<double> row, ProblemDescription problem,
496                                            string group, double bestTarget, double maxEffort) {
497      foreach (var t in targets.Select(x => Tuple.Create((problem.IsMaximization() ? (1 - x) : (1 + x)) * bestTarget, x))) {
498        var l = t.Item1;
499        var key = group + "_" + (t.Item2 * 100) + "%_" + l;
500        if (!hits.ContainsKey(key)) {
501          hits.Add(key, new SortedList<double, int>());
502          misses.Add(key, new SortedList<double, int>());
503        }
504        var hit = false;
505        foreach (var v in row.Values) {
506          if (v.Item1 > maxEffort) break;
507          if (problem.IsMaximization() && v.Item2 >= l || !problem.IsMaximization() && v.Item2 <= l) {
508            if (hits[key].ContainsKey(v.Item1))
509              hits[key][v.Item1]++;
510            else hits[key][v.Item1] = 1;
511            hit = true;
512            break;
513          }
514        }
515        if (!hit) {
516          var max = Math.Min(row.Values.Last().Item1, maxEffort);
517          if (misses[key].ContainsKey(max))
518            misses[key][max]++;
519          else misses[key][max] = 1;
520        }
521      }
522    }
523
524    private double CalculateHitsForAllTargets(Dictionary<string, SortedList<double, int>> hits,
525                                              Dictionary<string, SortedList<double, int>> misses,
526                                              IndexedDataRow<double> row, ProblemDescription problem,
527                                              string group, double bestTarget, double maxEffort) {
528      var values = row.Values;
529      if (!hits.ContainsKey(group)) {
530        hits.Add(group, new SortedList<double, int>());
531        misses.Add(group, new SortedList<double, int>());
532      }
533
534      var i = 0;
535      var j = 0;
536      while (i < targets.Length && j < values.Count) {
537        var target = (problem.IsMaximization() ? (1 - targets[i]) : (1 + targets[i])) * bestTarget;
538        var current = values[j];
539        if (current.Item1 > maxEffort) break;
540        if (problem.IsMaximization() && current.Item2 >= target
541          || !problem.IsMaximization() && current.Item2 <= target) {
542          if (hits[group].ContainsKey(current.Item1)) hits[group][current.Item1]++;
543          else hits[group][current.Item1] = 1;
544          i++;
545        } else {
546          j++;
547        }
548      }
549      if (j == values.Count) j--;
550      var effort = Math.Min(values[j].Item1, maxEffort);
551      if (i < targets.Length) {
552        if (misses[group].ContainsKey(effort))
553          misses[group][effort] += targets.Length - i;
554        else misses[group][effort] = targets.Length - i;
555      }
556      return effort;
557    }
558
559    private void UpdateErtTables(Dictionary<string, Dictionary<ProblemDescription, Tuple<double, List<IRun>>>> groupedRuns) {
560      ertTableView.Content = null;
561      var columns = 1 + targets.Length + 1;
562      var matrix = new string[groupedRuns.Count * groupedRuns.Max(x => x.Value.Count) + groupedRuns.Max(x => x.Value.Count), columns];
563      var rowCount = 0;
564
565      var tableName = (string)dataTableComboBox.SelectedItem;
566      if (string.IsNullOrEmpty(tableName)) return;
567
568      var targetsPerProblem = CalculateBestTargetPerProblemInstance(tableName);
569
570      var colNames = new string[columns];
571      colNames[0] = colNames[colNames.Length - 1] = string.Empty;
572      for (var i = 0; i < targets.Length; i++) {
573        colNames[i + 1] = targets[i].ToString("0.0%");
574      }
575      var problems = groupedRuns.SelectMany(x => x.Value.Keys).Distinct().ToList();
576
577      foreach (var problem in problems) {
578        matrix[rowCount, 0] = problem.ToString();
579        for (var i = 0; i < targets.Length; i++) {
580          matrix[rowCount, i + 1] = (targetsPerProblem[problem] * (problem.IsMaximization() ? (1 - targets[i]) : (1 + targets[i]))).ToString(CultureInfo.CurrentCulture.NumberFormat);
581        }
582        matrix[rowCount, columns - 1] = "#succ";
583        rowCount++;
584
585        foreach (var group in groupedRuns) {
586          matrix[rowCount, 0] = group.Key;
587          if (!group.Value.ContainsKey(problem)) {
588            matrix[rowCount, columns - 1] = "N/A";
589            rowCount++;
590            continue;
591          }
592          var runs = group.Value[problem].Item2;
593          ErtCalculationResult result = default(ErtCalculationResult);
594          for (var i = 0; i < targets.Length; i++) {
595            result = ExpectedRuntimeHelper.CalculateErt(runs, tableName, (problem.IsMaximization() ? (1 - targets[i]) : (1 + targets[i])) * group.Value[problem].Item1, problem.IsMaximization());
596            matrix[rowCount, i + 1] = result.ToString();
597          }
598          matrix[rowCount, columns - 1] = targets.Length > 0 ? result.SuccessfulRuns + "/" + result.TotalRuns : "-";
599          rowCount++;
600        }
601      }
602      ertTableView.Content = new StringMatrix(matrix) { ColumnNames = colNames };
603      ertTableView.DataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
604    }
605    #endregion
606
607    #region Performance analysis by (multiple) budget(s)
608    private void UpdateResultsByCost() {
609      // necessary to reset log scale -> empty chart cannot use log scaling
610      byCostDataTable.VisualProperties.XAxisLogScale = false;
611      byCostDataTable.Rows.Clear();
612
613      var table = (string)dataTableComboBox.SelectedItem;
614      if (string.IsNullOrEmpty(table)) return;
615
616      if (budgets == null) GenerateDefaultBudgets(table);
617
618      var groupedRuns = GroupRuns();
619      if (groupedRuns.Count == 0) return;
620
621      var colorCount = 0;
622      var lineStyleCount = 0;
623
624      var targetsPerProblem = CalculateBestTargetPerProblemInstance((string)dataTableComboBox.SelectedItem);
625
626      foreach (var group in groupedRuns) {
627        var hits = new Dictionary<string, SortedList<double, double>>();
628
629        foreach (var problem in group.Value) {
630          foreach (var run in problem.Value.Item2) {
631            var resultsTable = (IndexedDataTable<double>)run.Results[table];
632
633            if (eachOrAllBudgetsCheckBox.Checked) {
634              CalculateHitsForEachBudget(hits, resultsTable.Rows.First(), group.Value.Count, problem.Key, group.Key, problem.Value.Item2.Count, targetsPerProblem[problem.Key]);
635            } else {
636              CalculateHitsForAllBudgets(hits, resultsTable.Rows.First(), group.Value.Count, problem.Key, group.Key, problem.Value.Item2.Count, targetsPerProblem[problem.Key]);
637            }
638          }
639        }
640
641        foreach (var list in hits) {
642          var row = new IndexedDataRow<double>(list.Key) {
643            VisualProperties = {
644              ChartType = DataRowVisualProperties.DataRowChartType.StepLine,
645              LineWidth = 2,
646              Color = colors[colorCount],
647              LineStyle = hlLineStyles[lineStyleCount],
648              StartIndexZero = false
649            }
650          };
651
652          var total = 0.0;
653          foreach (var h in list.Value) {
654            total += h.Value;
655            row.Values.Add(Tuple.Create(h.Key, total));
656          }
657
658          byCostDataTable.Rows.Add(row);
659        }
660        colorCount = (colorCount + 1) % colors.Length;
661        if (colorCount == 0) lineStyleCount = (lineStyleCount + 1) % lineStyles.Length;
662      }
663
664      byCostDataTable.VisualProperties.XAxisTitle = "Targets to Best-Known Ratio";
665      byCostDataTable.VisualProperties.XAxisLogScale = byCostDataTable.Rows.Count > 0 && budgetLogScalingCheckBox.Checked;
666    }
667
668    private void GenerateDefaultBudgets(string table) {
669      var runs = GroupRuns().SelectMany(x => x.Value.Values).SelectMany(x => x.Item2).ToList();
670      var min = runs.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Select(y => y.Item1).Min()).Min();
671      var max = runs.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Select(y => y.Item1).Max()).Max();
672
673      var maxMagnitude = (int)Math.Ceiling(Math.Log10(max));
674      var minMagnitude = (int)Math.Floor(Math.Log10(min));
675      if (maxMagnitude - minMagnitude >= 3) {
676        budgets = new double[maxMagnitude - minMagnitude];
677        for (var i = minMagnitude; i < maxMagnitude; i++) {
678          budgets[i - minMagnitude] = Math.Pow(10, i);
679        }
680      } else {
681        var range = max - min;
682        budgets = Enumerable.Range(0, 6).Select(x => min + (x / 5.0) * range).ToArray();
683      }
684      suppressBudgetsEvents = true;
685      budgetsTextBox.Text = string.Join(" ; ", budgets);
686      suppressBudgetsEvents = false;
687    }
688
689    private void CalculateHitsForEachBudget(Dictionary<string, SortedList<double, double>> hits, IndexedDataRow<double> row, int groupCount, ProblemDescription problem, string groupName, int problemCount, double bestTarget) {
690      foreach (var b in budgets) {
691        var key = groupName + "-" + b;
692        if (!hits.ContainsKey(key)) hits.Add(key, new SortedList<double, double>());
693        Tuple<double, double> prev = null;
694        foreach (var v in row.Values) {
695          if (v.Item1 >= b) {
696            // the budget may be too low to achieve any target
697            if (prev == null && v.Item1 != b) break;
698            var tgt = ((prev == null || v.Item1 == b) ? v.Item2 : prev.Item2);
699            tgt = problem.IsMaximization() ? bestTarget / tgt : tgt / bestTarget;
700            if (hits[key].ContainsKey(tgt))
701              hits[key][tgt] += 1.0 / (groupCount * problemCount);
702            else hits[key][tgt] = 1.0 / (groupCount * problemCount);
703            break;
704          }
705          prev = v;
706        }
707        if (hits[key].Count == 0) hits.Remove(key);
708      }
709    }
710
711    private void CalculateHitsForAllBudgets(Dictionary<string, SortedList<double, double>> hits, IndexedDataRow<double> row, int groupCount, ProblemDescription problem, string groupName, int problemCount, double bestTarget) {
712      var values = row.Values;
713      if (!hits.ContainsKey(groupName)) hits.Add(groupName, new SortedList<double, double>());
714
715      var i = 0;
716      var j = 0;
717      Tuple<double, double> prev = null;
718      while (i < budgets.Length && j < values.Count) {
719        var current = values[j];
720        if (current.Item1 >= budgets[i]) {
721          if (prev != null || current.Item1 == budgets[i]) {
722            var tgt = (prev == null || current.Item1 == budgets[i]) ? current.Item2 : prev.Item2;
723            tgt = problem.IsMaximization() ? bestTarget / tgt : tgt / bestTarget;
724            if (!hits[groupName].ContainsKey(tgt)) hits[groupName][tgt] = 0;
725            hits[groupName][tgt] += 1.0 / (groupCount * problemCount * budgets.Length);
726          }
727          i++;
728        } else {
729          j++;
730          prev = current;
731        }
732      }
733      var lastTgt = values.Last().Item2;
734      lastTgt = problem.IsMaximization() ? bestTarget / lastTgt : lastTgt / bestTarget;
735      if (i < budgets.Length && !hits[groupName].ContainsKey(lastTgt)) hits[groupName][lastTgt] = 0;
736      while (i < budgets.Length) {
737        hits[groupName][lastTgt] += 1.0 / (groupCount * problemCount * budgets.Length);
738        i++;
739      }
740    }
741    #endregion
742
743    private void UpdateCaption() {
744      Caption = Content != null ? Content.OptimizerName + " RLD View" : ViewAttribute.GetViewName(GetType());
745    }
746
747    private void groupComboBox_SelectedIndexChanged(object sender, EventArgs e) {
748      UpdateRuns();
749      SetEnabledStateOfControls();
750    }
751    private void problemComboBox_SelectedIndexChanged(object sender, EventArgs e) {
752      UpdateRuns();
753      SetEnabledStateOfControls();
754    }
755    private void dataTableComboBox_SelectedIndexChanged(object sender, EventArgs e) {
756      if (dataTableComboBox.SelectedIndex >= 0)
757        GenerateDefaultBudgets((string)dataTableComboBox.SelectedItem);
758      UpdateRuns();
759      SetEnabledStateOfControls();
760    }
761
762    private void logScalingCheckBox_CheckedChanged(object sender, EventArgs e) {
763      UpdateResultsByTarget();
764      byCostDataTable.VisualProperties.XAxisLogScale = byCostDataTable.Rows.Count > 0 && budgetLogScalingCheckBox.Checked;
765    }
766
767    private void boundShadingCheckBox_CheckedChanged(object sender, EventArgs e) {
768      UpdateResultsByTarget();
769    }
770
771    #region Event handlers for target analysis
772    private bool suppressTargetsEvents;
773    private void targetsTextBox_Validating(object sender, CancelEventArgs e) {
774      if (suppressTargetsEvents) return;
775      var targetStrings = targetsTextBox.Text.Split(new[] { '%', ';', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
776      var targetList = new List<decimal>();
777      foreach (var ts in targetStrings) {
778        decimal t;
779        if (!decimal.TryParse(ts, out t)) {
780          errorProvider.SetError(targetsTextBox, "Not all targets can be parsed: " + ts);
781          e.Cancel = true;
782          return;
783        }
784        targetList.Add(t / 100);
785      }
786      if (targetList.Count == 0) {
787        errorProvider.SetError(targetsTextBox, "Give at least one target value!");
788        e.Cancel = true;
789        return;
790      }
791      e.Cancel = false;
792      errorProvider.SetError(targetsTextBox, null);
793      targets = targetList.Select(x => (double)x).OrderByDescending(x => x).ToArray();
794      suppressTargetsEvents = true;
795      try { targetsTextBox.Text = string.Join("% ; ", targets.Select(x => x * 100)) + "%"; } finally { suppressTargetsEvents = false; }
796
797      UpdateResultsByTarget();
798      SetEnabledStateOfControls();
799    }
800
801    private void aggregateTargetsCheckBox_CheckedChanged(object sender, EventArgs e) {
802      SuspendRepaint();
803      try {
804        UpdateResultsByTarget();
805      } finally { ResumeRepaint(true); }
806    }
807
808    private void generateTargetsButton_Click(object sender, EventArgs e) {
809      decimal max = 1, min = 0, count = 10;
810      if (targets != null) {
811        max = (decimal)targets.Max();
812        min = (decimal)targets.Min();
813        count = targets.Length;
814      } else if (Content.Count > 0 && dataTableComboBox.SelectedIndex >= 0) {
815        var table = (string)dataTableComboBox.SelectedItem;
816        max = (decimal)Content.Where(x => x.Results.ContainsKey(table)).Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Max(y => y.Item2)).Max();
817        min = (decimal)Content.Where(x => x.Results.ContainsKey(table)).Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Min(y => y.Item2)).Min();
818        count = 6;
819      }
820      using (var dialog = new DefineArithmeticProgressionDialog(false, min, max, (max - min) / count)) {
821        if (dialog.ShowDialog() == DialogResult.OK) {
822          if (dialog.Values.Any()) {
823            targets = dialog.Values.Select(x => (double)x).ToArray();
824            suppressTargetsEvents = true;
825            targetsTextBox.Text = string.Join("% ; ", targets);
826            suppressTargetsEvents = false;
827
828            UpdateResultsByTarget();
829            SetEnabledStateOfControls();
830          }
831        }
832      }
833    }
834
835    private void addTargetsAsResultButton_Click(object sender, EventArgs e) {
836      var table = (string)dataTableComboBox.SelectedItem;
837
838      var targetsPerProblem = CalculateBestTargetPerProblemInstance(table);
839
840      foreach (var run in Content) {
841        if (!run.Results.ContainsKey(table)) continue;
842        var resultsTable = (IndexedDataTable<double>)run.Results[table];
843        var values = resultsTable.Rows.First().Values;
844        var i = 0;
845        var j = 0;
846        var pd = new ProblemDescription(run);
847        while (i < targets.Length && j < values.Count) {
848          var target = (pd.IsMaximization() ? (1 - targets[i]) : (1 + targets[i])) * targetsPerProblem[pd];
849          var current = values[j];
850          if (pd.IsMaximization() && current.Item2 >= target
851              || !pd.IsMaximization() && current.Item2 <= target) {
852            run.Results[table + ".Target" + target] = new DoubleValue(current.Item1);
853            i++;
854          } else {
855            j++;
856          }
857        }
858      }
859    }
860    #endregion
861
862    #region Event handlers for cost analysis
863    private bool suppressBudgetsEvents;
864    private void budgetsTextBox_Validating(object sender, CancelEventArgs e) {
865      if (suppressBudgetsEvents) return;
866      var budgetStrings = budgetsTextBox.Text.Split(new[] { ';', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
867      var budgetList = new List<double>();
868      foreach (var ts in budgetStrings) {
869        double b;
870        if (!double.TryParse(ts, out b)) {
871          errorProvider.SetError(budgetsTextBox, "Not all targets can be parsed: " + ts);
872          e.Cancel = true;
873          return;
874        }
875        budgetList.Add(b);
876      }
877      if (budgetList.Count == 0) {
878        errorProvider.SetError(budgetsTextBox, "Give at least one target value!");
879        e.Cancel = true;
880        return;
881      }
882      e.Cancel = false;
883      errorProvider.SetError(budgetsTextBox, null);
884      budgets = budgetList.ToArray();
885      UpdateResultsByCost();
886      SetEnabledStateOfControls();
887    }
888
889    private void eachOrAllBudgetsCheckBox_CheckedChanged(object sender, EventArgs e) {
890      var each = eachOrAllBudgetsCheckBox.Checked;
891      eachOrAllBudgetsCheckBox.Text = each ? "each" : "all";
892      SuspendRepaint();
893      try {
894        UpdateResultsByCost();
895      } finally { ResumeRepaint(true); }
896    }
897
898    private void generateBudgetsButton_Click(object sender, EventArgs e) {
899      decimal max = 1, min = 0, count = 10;
900      if (budgets != null) {
901        max = (decimal)budgets.Max();
902        min = (decimal)budgets.Min();
903        count = budgets.Length;
904      } else if (Content.Count > 0 && dataTableComboBox.SelectedIndex >= 0) {
905        var table = (string)dataTableComboBox.SelectedItem;
906        min = (decimal)Content.Where(x => x.Results.ContainsKey(table)).Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Min(y => y.Item1)).Min();
907        max = (decimal)Content.Where(x => x.Results.ContainsKey(table)).Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Max(y => y.Item1)).Max();
908        count = 6;
909      }
910      using (var dialog = new DefineArithmeticProgressionDialog(false, min, max, (max - min) / count)) {
911        if (dialog.ShowDialog() == DialogResult.OK) {
912          if (dialog.Values.Any()) {
913            budgets = dialog.Values.OrderBy(x => x).Select(x => (double)x).ToArray();
914
915            suppressBudgetsEvents = true;
916            budgetsTextBox.Text = string.Join(" ; ", budgets);
917            suppressBudgetsEvents = false;
918
919            UpdateResultsByCost();
920            SetEnabledStateOfControls();
921          }
922        }
923      }
924    }
925
926    private void addBudgetsAsResultButton_Click(object sender, EventArgs e) {
927      var table = (string)dataTableComboBox.SelectedItem;
928      var budgetStrings = budgetsTextBox.Text.Split(new[] { ';', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
929      if (budgetStrings.Length == 0) {
930        MessageBox.Show("Define a number of budgets.");
931        return;
932      }
933      var budgetList = new List<double>();
934      foreach (var bs in budgetStrings) {
935        double v;
936        if (!double.TryParse(bs, out v)) {
937          MessageBox.Show("Budgets must be a valid number: " + bs);
938          return;
939        }
940        budgetList.Add(v);
941      }
942      budgetList.Sort();
943
944      foreach (var run in Content) {
945        if (!run.Results.ContainsKey(table)) continue;
946        var resultsTable = (IndexedDataTable<double>)run.Results[table];
947        var values = resultsTable.Rows.First().Values;
948        var i = 0;
949        var j = 0;
950        Tuple<double, double> prev = null;
951        while (i < budgetList.Count && j < values.Count) {
952          var current = values[j];
953          if (current.Item1 >= budgetList[i]) {
954            if (prev != null || current.Item1 == budgetList[i]) {
955              var tgt = (prev == null || current.Item1 == budgetList[i]) ? current.Item2 : prev.Item2;
956              run.Results[table + ".Cost" + budgetList[i]] = new DoubleValue(tgt);
957            }
958            i++;
959          } else {
960            j++;
961            prev = current;
962          }
963        }
964      }
965    }
966    #endregion
967
968    #region Helpers
969    private Dictionary<ProblemDescription, double> CalculateBestTargetPerProblemInstance(string table) {
970      return (from r in Content
971              where r.Visible
972              let pd = new ProblemDescription(r)
973              let target = r.Parameters.ContainsKey("BestKnownQuality")
974                           && r.Parameters["BestKnownQuality"] is DoubleValue
975                ? ((DoubleValue)r.Parameters["BestKnownQuality"]).Value
976                : ((IndexedDataTable<double>)r.Results[table]).Rows.First().Values.Last().Item2
977              group target by pd into g
978              select new { Problem = g.Key, Target = g.Key.IsMaximization() ? g.Max() : g.Min() })
979        .ToDictionary(x => x.Problem, x => x.Target);
980    }
981
982    private void UpdateRuns() {
983      if (InvokeRequired) {
984        Invoke((Action)UpdateRuns);
985        return;
986      }
987      SuspendRepaint();
988      try {
989        UpdateResultsByTarget();
990        UpdateResultsByCost();
991      } finally { ResumeRepaint(true); }
992    }
993    #endregion
994
995    private void ConfigureSeries(Series series) {
996      series.SmartLabelStyle.Enabled = true;
997      series.SmartLabelStyle.AllowOutsidePlotArea = LabelOutsidePlotAreaStyle.No;
998      series.SmartLabelStyle.CalloutLineAnchorCapStyle = LineAnchorCapStyle.None;
999      series.SmartLabelStyle.CalloutLineColor = series.Color;
1000      series.SmartLabelStyle.CalloutLineWidth = 2;
1001      series.SmartLabelStyle.CalloutStyle = LabelCalloutStyle.Underlined;
1002      series.SmartLabelStyle.IsOverlappedHidden = false;
1003      series.SmartLabelStyle.MaxMovingDistance = 200;
1004      series.ToolTip = series.LegendText + " X = #VALX, Y = #VALY";
1005    }
1006
1007    private void chart_MouseDown(object sender, MouseEventArgs e) {
1008      HitTestResult result = targetChart.HitTest(e.X, e.Y);
1009      if (result.ChartElementType == ChartElementType.LegendItem) {
1010        ToggleTargetChartSeriesVisible(result.Series);
1011      }
1012    }
1013    private void chart_MouseMove(object sender, MouseEventArgs e) {
1014      HitTestResult result = targetChart.HitTest(e.X, e.Y);
1015      if (result.ChartElementType == ChartElementType.LegendItem)
1016        this.Cursor = Cursors.Hand;
1017      else
1018        this.Cursor = Cursors.Default;
1019    }
1020    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
1021      foreach (LegendItem legendItem in e.LegendItems) {
1022        var series = targetChart.Series[legendItem.SeriesName];
1023        if (series != null) {
1024          bool seriesIsInvisible = invisibleTargetSeries.Any(x => x.Name == series.Name);
1025          foreach (LegendCell cell in legendItem.Cells) {
1026            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
1027          }
1028        }
1029      }
1030    }
1031
1032    private void ToggleTargetChartSeriesVisible(Series series) {
1033      var indexList = invisibleTargetSeries.FindIndex(x => x.Name == series.Name);
1034      var indexChart = targetChart.Series.IndexOf(series);
1035      if (targetChart.Series.Count == 1) targetChart.ChartAreas[0].AxisX.IsLogarithmic = false;
1036      targetChart.Series.RemoveAt(indexChart);
1037      var s = indexList >= 0 ? invisibleTargetSeries[indexList] : new Series(series.Name) {
1038        Color = series.Color,
1039        ChartType = series.ChartType,
1040        BorderWidth = series.BorderWidth,
1041        BorderDashStyle = series.BorderDashStyle
1042      };
1043      if (indexList < 0) {
1044        // hide
1045        invisibleTargetSeries.Add(series);
1046        var shadeSeries = targetChart.Series.FirstOrDefault(x => x.Name == series.Name + "-range");
1047        if (shadeSeries != null) {
1048          if (targetChart.Series.Count == 1) targetChart.ChartAreas[0].AxisX.IsLogarithmic = false;
1049          targetChart.Series.Remove(shadeSeries);
1050          invisibleTargetSeries.Add(shadeSeries);
1051          indexChart--;
1052        }
1053      } else {
1054        // show
1055        invisibleTargetSeries.RemoveAt(indexList);
1056        var shadeSeries = invisibleTargetSeries.FirstOrDefault(x => x.Name == series.Name + "-range");
1057        if (shadeSeries != null) {
1058          invisibleTargetSeries.Remove(shadeSeries);
1059          InsertOrAddSeries(indexChart, shadeSeries);
1060          indexChart++;
1061        }
1062      }
1063      InsertOrAddSeries(indexChart, s);
1064      targetChart.ChartAreas[0].AxisX.IsLogarithmic = CanDisplayLogarithmic();
1065    }
1066
1067    private bool CanDisplayLogarithmic() {
1068      return targetLogScalingCheckBox.Checked
1069        && targetChart.Series.Count > 0 // must have a series
1070        && targetChart.Series.Any(x => x.Points.Count > 0) // at least one series must have points
1071        && targetChart.Series.All(s => s.Points.All(p => p.XValue > 0)); // all points must be positive
1072    }
1073
1074    private void InsertOrAddSeries(int index, Series s) {
1075      if (targetChart.Series.Count <= index)
1076        targetChart.Series.Add(s);
1077      else targetChart.Series.Insert(index, s);
1078    }
1079
1080    private class ProblemDescription {
1081      private readonly bool matchAll;
1082      public static readonly ProblemDescription MatchAll = new ProblemDescription() {
1083        ProblemName = "All with Best-Known"
1084      };
1085
1086      private ProblemDescription() {
1087        ProblemType = string.Empty;
1088        ProblemName = string.Empty;
1089        Evaluator = string.Empty;
1090        Maximization = string.Empty;
1091        DisplayProblemType = false;
1092        DisplayProblemName = false;
1093        DisplayEvaluator = false;
1094        DisplayMaximization = false;
1095        matchAll = true;
1096      }
1097
1098      public ProblemDescription(IRun run) {
1099        ProblemType = GetStringValueOrEmpty(run, "Problem Type");
1100        ProblemName = GetStringValueOrEmpty(run, "Problem Name");
1101        Evaluator = GetStringValueOrEmpty(run, "Evaluator");
1102        Maximization = GetMaximizationValueOrEmpty(run, "Maximization");
1103        DisplayProblemType = !string.IsNullOrEmpty(ProblemType);
1104        DisplayProblemName = !string.IsNullOrEmpty(ProblemName);
1105        DisplayEvaluator = !string.IsNullOrEmpty(Evaluator);
1106        DisplayMaximization = !string.IsNullOrEmpty(Maximization);
1107        matchAll = false;
1108      }
1109
1110      public bool DisplayProblemType { get; set; }
1111      public string ProblemType { get; set; }
1112      public bool DisplayProblemName { get; set; }
1113      public string ProblemName { get; set; }
1114      public bool DisplayEvaluator { get; set; }
1115      public string Evaluator { get; set; }
1116      public bool DisplayMaximization { get; set; }
1117      public string Maximization { get; set; }
1118
1119      public bool IsMaximization() {
1120        return Maximization == "MAX";
1121      }
1122
1123      public bool Match(IRun run) {
1124        return matchAll ||
1125               GetStringValueOrEmpty(run, "Problem Type") == ProblemType
1126               && GetStringValueOrEmpty(run, "Problem Name") == ProblemName
1127               && GetStringValueOrEmpty(run, "Evaluator") == Evaluator
1128               && GetMaximizationValueOrEmpty(run, "Maximization") == Maximization;
1129      }
1130
1131      private string GetStringValueOrEmpty(IRun run, string key) {
1132        return run.Parameters.ContainsKey(key) ? ((StringValue)run.Parameters[key]).Value : string.Empty;
1133      }
1134
1135      private string GetMaximizationValueOrEmpty(IRun run, string key) {
1136        return run.Parameters.ContainsKey(key) ? (((BoolValue)run.Parameters[key]).Value ? "MAX" : "MIN") : string.Empty;
1137      }
1138
1139      public override bool Equals(object obj) {
1140        var other = obj as ProblemDescription;
1141        if (other == null) return false;
1142        return ProblemType == other.ProblemType
1143               && ProblemName == other.ProblemName
1144               && Evaluator == other.Evaluator
1145               && Maximization == other.Maximization;
1146      }
1147
1148      public override int GetHashCode() {
1149        return ProblemType.GetHashCode() ^ ProblemName.GetHashCode() ^ Evaluator.GetHashCode() ^ Maximization.GetHashCode();
1150      }
1151
1152      public override string ToString() {
1153        return string.Join("  --  ", new[] {
1154          (DisplayProblemType ? ProblemType : string.Empty),
1155          (DisplayProblemName ? ProblemName : string.Empty),
1156          (DisplayEvaluator ? Evaluator : string.Empty),
1157          (DisplayMaximization ? Maximization : string.Empty)}.Where(x => !string.IsNullOrEmpty(x)));
1158      }
1159    }
1160  }
1161}
Note: See TracBrowser for help on using the repository browser.