Free cookie consent management tool by TermsFeed Policy Generator

source: branches/1614_GeneralizedQAP/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs @ 15870

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

#1614:

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