source: branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Views/PushProgramDebuggerView.cs @ 14834

Last change on this file since 14834 was 14834, checked in by pkimmesw, 3 years ago

#2665 LexicaseSelector, Performance improvements, UI Fixes, Debugger only shows used stacks, fixed Debugger stepping, Added vector expressions, ERCOptions,

File size: 16.0 KB
Line 
1using System.Windows.Forms;
2
3using HeuristicLab.Core.Views;
4using HeuristicLab.MainForm;
5
6namespace HeuristicLab.Problems.ProgramSynthesis.Push.Views {
7  using System;
8  using System.Collections.Generic;
9  using System.Drawing;
10  using System.Globalization;
11  using System.Linq;
12
13  using HeuristicLab.BenchmarkSuite;
14  using HeuristicLab.BenchmarkSuite.Problems;
15  using HeuristicLab.BenchmarkSuite.Views;
16  using HeuristicLab.Core;
17  using HeuristicLab.Encodings.IntegerVectorEncoding;
18  using HeuristicLab.Problems.ProgramSynthesis.Push.Configuration;
19  using HeuristicLab.Problems.ProgramSynthesis.Push.Expressions;
20  using HeuristicLab.Problems.ProgramSynthesis.Push.Interpreter;
21  using HeuristicLab.Problems.ProgramSynthesis.Push.Problem;
22  using HeuristicLab.Problems.ProgramSynthesis.Push.Stack;
23
24  [View("Push Program Debugger")]
25  [Content(typeof(PushSolution), true)]
26  public partial class PushProgramDebuggerView : NamedItemView {
27    private readonly IDictionary<StackTypes, ListBox> debugControlDict = new Dictionary<StackTypes, ListBox>();
28
29    private const string Separator = ", ";
30    private const string EmptySign = "-";
31
32    private const string exampleSplitter = " => ";
33    private const string AbsoluteDiffHeaderText = "Absolute Diff.";
34    private const string RelativeDiffHeaderText = "Relative Diff.";
35    private const string InputHeaderStringFormat = "Input {0} : {1}";
36    private const string EstimatedOuputHeaderStringFormat = "Estimated Output {0} : {1}";
37    private const string OutputHeaderStringFormat = "Output {0} : {1}";
38    private const string GroupBoxTextStringFormat = "{0}[{1}]";
39
40    private PushProgram program;
41    private PooledPushInterpreter interpreter;
42    private PooledPushInterpreter interpreter2; // used to determine noops
43    private PushInterpreterPool pool;
44
45    public PushProgramDebuggerView() {
46      InitializeComponent();
47
48      Name = "Push Program Debugger";
49
50      InitEvents();
51    }
52
53    ~PushProgramDebuggerView() {
54      interpreter.Dispose();
55      interpreter2.Dispose();
56    }
57
58    private void InitEvents() {
59      exampleComboBox.SelectedIndexChanged += SelectedExampleIndexChanged;
60      runButton.Click += RunButtonClick;
61      resetButton.Click += ResetButtonClick;
62      stepButton.Click += StepButtonClick;
63      simplifyButton.Click += SimplifyButtonClick;
64    }
65
66    private void SimplifyButtonClick(object sender, EventArgs e) {
67      var newContent = new PushSolution(
68                         (IntegerVector)Content.IntegerVector.Clone(),
69                         Content.Quality,
70                         (Data)Content.Data.Clone(),
71                         (IRandom)Content.Random.Clone(),
72                         Content.Config,
73                         Content.DataStart,
74                         Content.DataEnd,
75                         true);
76
77      MainFormManager.MainForm.ShowContent(newContent, GetType());
78    }
79
80    private void RunButtonClick(object sender, EventArgs e) {
81      if (interpreter == null)
82        return;
83
84      interpreter.Resume();
85
86      UpdateExecList();
87      UpdateValueLists();
88      CheckIfButtonsCanBeEnabled();
89    }
90
91    private void StepButtonClick(object sender, EventArgs e) {
92      if (interpreter == null || stepWidthBox.Value <= 0)
93        return;
94
95      var count = Math.Min(stepWidthBox.Value, interpreter.ExecStack.Count);
96
97      if (!interpreter.CanStep)
98        return;
99
100      // skip leading noops
101      if (interpreter2.ExecCounter == (program.IsProgram ? 1 : 0) &&
102          skipNoopsCheckBox.Checked &&
103          SkipNoops()) {
104        count = 0; // no entries left, cause all were noops
105      }
106
107      for (var i = 0; i < count; i++) {
108        if (skipNoopsCheckBox.Checked) {
109          interpreter.Step();
110
111          if (SkipNoops())
112            break;
113        } else {
114          interpreter.Step();
115          interpreter2.Step();
116        }
117      }
118
119      stepWidthBox.Maximum = Math.Max(1, interpreter.ExecStack.Count);
120
121      UpdateExecList();
122      UpdateValueLists();
123      CheckIfButtonsCanBeEnabled();
124    }
125
126    private bool SkipNoops() {
127      var skipCount = 0;
128      bool isNoop;
129
130      do {
131        skipCount++;
132        isNoop = !interpreter2.Step();
133      } while (interpreter2.CanStep && isNoop);
134
135      if (isNoop) {
136        interpreter.Step(skipCount);
137      } else if (skipCount > 1) {
138        interpreter.Step(skipCount - 1);
139      }
140
141      return isNoop;
142    }
143
144    private void CheckIfButtonsCanBeEnabled() {
145      runButton.Enabled = interpreter != null && interpreter.CanStep;
146      stepButton.Enabled = interpreter != null && interpreter.CanStep;
147      stepWidthBox.Enabled = interpreter != null && interpreter.CanStep;
148    }
149
150    private void ResetButtonClick(object sender, EventArgs e) {
151      this.ResetDebugging();
152    }
153
154    private void SelectedExampleIndexChanged(object sender, EventArgs e) {
155      this.ResetDebugging();
156    }
157
158    public new PushSolution Content
159    {
160      get { return (PushSolution)base.Content; }
161      set
162      {
163        base.Content = value;
164      }
165    }
166
167    private void ResetDebugging() {
168      if (Content == null ||
169          pool == null ||
170          program == null ||
171          exampleComboBox.SelectedIndex < 0)
172        return;
173
174      var example = Content.Data.Examples[exampleComboBox.SelectedIndex];
175
176      if (interpreter != null) {
177        interpreter.Reset();
178        interpreter2.Reset();
179      }
180
181      interpreter.BooleanStack.Push(example.InputBoolean);
182      interpreter.IntegerStack.Push(example.InputInt);
183      interpreter.FloatStack.Push(example.InputFloat);
184
185      interpreter2.BooleanStack.Push(example.InputBoolean);
186      interpreter2.IntegerStack.Push(example.InputInt);
187      interpreter2.FloatStack.Push(example.InputFloat);
188
189      interpreter.Run(program, true);
190      interpreter2.Run(program, true);
191
192      stepWidthBox.Maximum = interpreter.ExecStack.Count;
193
194      UpdateValueLists();
195      UpdateExecList();
196      CheckIfButtonsCanBeEnabled();
197    }
198
199    protected override void OnContentChanged() {
200      if (Content == null) return;
201
202      Name = "Push Solution";
203      nameTextBox.Text = Name;
204
205      pool = new PushInterpreterPool(Content.Config);
206      program = Content.IntegerVector.ToPushProgram(Content.Config);
207
208      if (interpreter != null) {
209        interpreter.Dispose();
210        interpreter2.Dispose();
211      }
212
213      interpreter = pool.Create(Content.Random);
214      interpreter2 = pool.Create(Content.Random);
215
216      if (Content.Simplify)
217        program = Simplifier.Simplifier.Simplify(program, p => PushEvaluator.Evaluate(p, pool, Content.Random, Content.Data, Content.DataStart, Content.DataEnd).TotalQuality);
218
219      UpdateExamples(Content.Data);
220
221      if (exampleComboBox.SelectedIndex < 0) {
222        exampleComboBox.SelectedIndex = 0; // Triggers ResetDebugging via event
223      } else {
224        ResetDebugging();
225      }
226
227      InitDebugLists(Content.Config);
228      InitResultGrid();
229      ClearLists();
230      UpdateExecList();
231      UpdateValueLists();
232    }
233
234    private void InitResultGrid() {
235      resultsDataGrid.Columns.Clear();
236      resultsDataGrid.Rows.Clear();
237
238      var cellTemplate = new DataGridViewTextBoxCell();
239
240      for (var i = 0; i < Content.Data.InputArgumentTypes.Length; i++) {
241        var headerTypeName = ViewHelper.GetHeaderTypeName(Content.Data.InputArgumentTypes[i]);
242        var column = new DataGridViewColumn {
243          HeaderText = string.Format(InputHeaderStringFormat, i + 1, headerTypeName),
244          AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
245          CellTemplate = cellTemplate
246        };
247
248        column.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
249        resultsDataGrid.Columns.Add(column);
250      }
251
252      for (var i = 0; i < Content.Data.OutputArgumentTypes.Length; i++) {
253        var headerTypeName = ViewHelper.GetHeaderTypeName(Content.Data.OutputArgumentTypes[i]);
254
255        var estimatedOutputColumn = new DataGridViewColumn {
256          HeaderText = string.Format(EstimatedOuputHeaderStringFormat, i + 1, headerTypeName),
257          AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
258          CellTemplate = cellTemplate,
259        };
260
261        var outputColumn = new DataGridViewColumn {
262          HeaderText = string.Format(OutputHeaderStringFormat, i + 1, headerTypeName),
263          AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
264          CellTemplate = cellTemplate
265        };
266
267        estimatedOutputColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
268        outputColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
269
270        resultsDataGrid.Columns.Add(estimatedOutputColumn);
271        resultsDataGrid.Columns.Add(outputColumn);
272      }
273
274      var absoluteDiffColumn = new DataGridViewColumn {
275        HeaderText = AbsoluteDiffHeaderText,
276        AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader,
277        CellTemplate = cellTemplate
278      };
279
280      var relativeDiffColumn = new DataGridViewColumn {
281        HeaderText = RelativeDiffHeaderText,
282        AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader,
283        CellTemplate = cellTemplate,
284      };
285
286      absoluteDiffColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
287      relativeDiffColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
288
289      resultsDataGrid.Columns.Add(absoluteDiffColumn);
290      resultsDataGrid.Columns.Add(relativeDiffColumn);
291
292      using (var pushInterpreter = pool.Create(Content.Random)) {
293        foreach (var example in Content.Data.Examples) {
294          var row = new DataGridViewRow();
295          var absoluteDiff = PushEvaluator.Evaluate(pushInterpreter, program, example, Content.Data.WorstResult);
296          var relativeDiff = absoluteDiff / Math.Abs(Content.Data.BestResult - Content.Data.WorstResult) * 100d;
297
298          row.HeaderCell.Value = row.Index + 1;
299          row.CreateCells(resultsDataGrid);
300
301          for (var i = 0; i < Content.Data.InputArgumentTypes.Length; i++) {
302            row.Cells[i].Value = ViewHelper.StringifyInput(Content.Data.InputArgumentTypes[i], example, Separator);
303          }
304
305          for (var i = 0; i < Content.Data.OutputArgumentTypes.Length; i++) {
306            row.Cells[Content.Data.InputArgumentTypes.Length + i * 2].Value = ViewHelper.StringifyOutput(Content.Data.OutputArgumentTypes[i], example, Separator);
307            row.Cells[Content.Data.InputArgumentTypes.Length + i * 2 + 1].Value = StringifyResult(Content.Data.OutputArgumentTypes[i], pushInterpreter, example, Separator);
308          }
309
310          row.Cells[Content.Data.TotalArgumentCount + 1].Value = absoluteDiff;
311          row.Cells[Content.Data.TotalArgumentCount + 2].Value = relativeDiff + "%";
312
313          this.resultsDataGrid.Rows.Add(row);
314          pushInterpreter.Reset();
315        }
316      }
317    }
318
319    private string StringifyResult(ExampleArgumentType type, IPushInterpreter interpreter, Example example, string valueSeparator) {
320      switch (type) {
321        case ExampleArgumentType.Integer:
322        case ExampleArgumentType.IntegerCollection: return interpreter.IntegerStack.IsEmpty ? EmptySign : string.Join(valueSeparator, interpreter.IntegerStack.Peek(GetCount(interpreter.IntegerStack, example.OutputInt)));
323
324        case ExampleArgumentType.Float:
325        case ExampleArgumentType.FloatCollection: return interpreter.FloatStack.IsEmpty ? EmptySign : string.Join(valueSeparator, interpreter.FloatStack.Peek(GetCount(interpreter.FloatStack, example.OutputFloat)).Select(d => d.ToString(CultureInfo.CurrentUICulture)));
326
327        case ExampleArgumentType.Bool: return interpreter.BooleanStack.IsEmpty ? EmptySign : interpreter.BooleanStack.Top.ToString();
328        case ExampleArgumentType.Char: return interpreter.CharStack.IsEmpty ? EmptySign : interpreter.CharStack.Top.ToString();
329
330        case ExampleArgumentType.String:
331        case ExampleArgumentType.StringCollection: return interpreter.StringStack.IsEmpty ? EmptySign : string.Join(valueSeparator, interpreter.StringStack.Peek(GetCount(interpreter.StringStack, example.OutputString)));
332        default: return string.Empty;
333      }
334    }
335
336    private int GetCount<T>(IPushStack<T> stack, T[] data) {
337      return Math.Max(0, Math.Min(data.Length, stack.Count));
338    }
339
340    private void ClearLists() {
341      foreach (var list in debugControlDict.Values) {
342        list.Items.Clear();
343      }
344    }
345
346    private void UpdateExecList() {
347      execList.Items.Clear();
348      var expressions = interpreter.ExecStack
349        .Reverse()
350        .Select(e => e.StringRepresentation)
351        .ToArray();
352
353      execList.Items.AddRange(expressions);
354      execGroupBox.Text = string.Format(GroupBoxTextStringFormat, Enum.GetName(typeof(StackTypes), StackTypes.Exec), interpreter.ExecStack.Count);
355    }
356
357    private void InitDebugLists(IReadOnlyPushConfiguration config) {
358      debugControlDict.Clear();
359
360      // 2 = ExecList + EmptyColumn which is required to fill empty space
361      while (debugTableLayout.ColumnCount > 2) {
362        debugTableLayout.Controls.RemoveAt(1);
363        debugTableLayout.ColumnCount--;
364      }
365
366      foreach (StackTypes stackType in Enum.GetValues(typeof(StackTypes))) {
367        if (stackType != StackTypes.Exec &&
368            ExpressionTable.GetExpressionsByStackTypes(stackType).Intersect(config.EnabledExpressions).Any()) {
369          var list = GetDebugList(stackType);
370          debugControlDict.Add(stackType, list);
371        }
372      }
373    }
374
375    private ListBox GetDebugList(StackTypes type) {
376      var groupBox = new GroupBox {
377        Anchor = AnchorStyles.Bottom | AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top,
378        AutoSize = true,
379        AutoSizeMode = AutoSizeMode.GrowAndShrink,
380        Text = Enum.GetName(typeof(StackTypes), type)
381      };
382
383      var list = new ListBox {
384        Dock = DockStyle.Fill,
385        DrawMode = DrawMode.OwnerDrawFixed
386      };
387
388      var stackEntryType = type.GetStackEntryType();
389      if (stackEntryType == typeof(double) ||
390          stackEntryType == typeof(long)) {
391        list.DrawItem += (sender, e) => {
392          if (e.Index <= -1) return;
393          var item = list.Items[e.Index];
394
395          e.DrawBackground();
396          e.DrawFocusRectangle();
397
398          var brush = new SolidBrush(e.ForeColor);
399          var size = e.Graphics.MeasureString(item.ToString(), e.Font);
400
401          e.Graphics.DrawString(
402            item.ToString(),
403            e.Font,
404            brush,
405            e.Bounds.Right - size.Width,
406            e.Bounds.Top + (e.Bounds.Height / 2 - size.Height / 2));
407        };
408      }
409
410      groupBox.Controls.Add(list);
411
412      var columnWidth = stackEntryType == typeof(Expression) ? 250 : 150;
413
414      debugTableLayout.ColumnCount++;
415      debugTableLayout.ColumnStyles.Insert(1, new ColumnStyle(SizeType.Absolute, columnWidth));
416      debugTableLayout.Controls.Add(groupBox);
417      debugTableLayout.Controls.SetChildIndex(groupBox, 1);
418
419      return list;
420    }
421
422
423    private void UpdateValueLists() {
424      ClearLists();
425
426      if (Content == null || interpreter == null)
427        return;
428
429      foreach (var pair in debugControlDict) {
430        var stack = interpreter.GetStackEntriesByType<object>(pair.Key);
431        var name = Enum.GetName(typeof(StackTypes), pair.Key);
432
433        pair.Value.Items.AddRange(stack.Reverse().ToArray());
434        ((GroupBox)pair.Value.Parent).Text = string.Format(GroupBoxTextStringFormat, name, pair.Value.Items.Count);
435      }
436    }
437
438    private void UpdateExamples(Data data) {
439      exampleComboBox.Items.Clear();
440      if (data == null) return;
441
442      var stringRepresentations = data.Examples.Select(e =>
443        string.Join(Separator, e.InputArgs) +
444        exampleSplitter +
445        string.Join(Separator, e.OutputArgs));
446
447      foreach (var str in stringRepresentations) {
448        exampleComboBox.Items.Add(str);
449      }
450    }
451  }
452}
Note: See TracBrowser for help on using the repository browser.