Free cookie consent management tool by TermsFeed Policy Generator

source: branches/MemPRAlgorithm/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs @ 14680

Last change on this file since 14680 was 14680, checked in by abeham, 7 years ago

#2701: disabled learning

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