using System.Windows.Forms; using HeuristicLab.Core.Views; using HeuristicLab.MainForm; namespace HeuristicLab.Problems.ProgramSynthesis.Push.Views { using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Linq; using HeuristicLab.BenchmarkSuite; using HeuristicLab.BenchmarkSuite.Problems; using HeuristicLab.BenchmarkSuite.Views; using HeuristicLab.Core; using HeuristicLab.Encodings.IntegerVectorEncoding; using HeuristicLab.Problems.ProgramSynthesis.Push.Configuration; using HeuristicLab.Problems.ProgramSynthesis.Push.Expressions; using HeuristicLab.Problems.ProgramSynthesis.Push.Interpreter; using HeuristicLab.Problems.ProgramSynthesis.Push.Problem; using HeuristicLab.Problems.ProgramSynthesis.Push.Stack; [View("Push Program Debugger")] [Content(typeof(PushSolution), true)] public partial class PushProgramDebuggerView : NamedItemView { private readonly IDictionary debugControlDict = new Dictionary(); private const string Separator = ", "; private const string EmptySign = "-"; private const string exampleSplitter = " => "; private const string AbsoluteDiffHeaderText = "Absolute Diff."; private const string RelativeDiffHeaderText = "Relative Diff."; private const string InputHeaderStringFormat = "Input {0} : {1}"; private const string EstimatedOuputHeaderStringFormat = "Estimated Output {0} : {1}"; private const string OutputHeaderStringFormat = "Output {0} : {1}"; private const string GroupBoxTextStringFormat = "{0}[{1}]"; private PushProgram program; private PooledPushInterpreter interpreter; private PooledPushInterpreter interpreter2; // used to determine noops private PushInterpreterPool pool; public PushProgramDebuggerView() { InitializeComponent(); Name = "Push Program Debugger"; InitEvents(); } ~PushProgramDebuggerView() { interpreter.Dispose(); interpreter2.Dispose(); } private void InitEvents() { exampleComboBox.SelectedIndexChanged += SelectedExampleIndexChanged; runButton.Click += RunButtonClick; resetButton.Click += ResetButtonClick; stepButton.Click += StepButtonClick; simplifyButton.Click += SimplifyButtonClick; } private void SimplifyButtonClick(object sender, EventArgs e) { var newContent = new PushSolution( (IntegerVector)Content.IntegerVector.Clone(), Content.Quality, (Data)Content.Data.Clone(), (IRandom)Content.Random.Clone(), Content.Config, Content.DataStart, Content.DataEnd, true); MainFormManager.MainForm.ShowContent(newContent, GetType()); } private void RunButtonClick(object sender, EventArgs e) { if (interpreter == null) return; interpreter.Resume(); UpdateExecList(); UpdateValueLists(); CheckIfButtonsCanBeEnabled(); } private void StepButtonClick(object sender, EventArgs e) { if (interpreter == null || stepWidthBox.Value <= 0) return; var count = Math.Min(stepWidthBox.Value, interpreter.ExecStack.Count); if (!interpreter.CanStep) return; // skip leading noops if (interpreter2.ExecCounter == (program.IsProgram ? 1 : 0) && skipNoopsCheckBox.Checked && SkipNoops()) { count = 0; // no entries left, cause all were noops } for (var i = 0; i < count; i++) { if (skipNoopsCheckBox.Checked) { interpreter.Step(); if (SkipNoops()) break; } else { interpreter.Step(); interpreter2.Step(); } } stepWidthBox.Maximum = Math.Max(1, interpreter.ExecStack.Count); UpdateExecList(); UpdateValueLists(); CheckIfButtonsCanBeEnabled(); } private bool SkipNoops() { var skipCount = 0; bool isNoop; do { skipCount++; isNoop = !interpreter2.Step(); } while (interpreter2.CanStep && isNoop); if (isNoop) { interpreter.Step(skipCount); } else if (skipCount > 1) { interpreter.Step(skipCount - 1); } return isNoop; } private void CheckIfButtonsCanBeEnabled() { runButton.Enabled = interpreter != null && interpreter.CanStep; stepButton.Enabled = interpreter != null && interpreter.CanStep; stepWidthBox.Enabled = interpreter != null && interpreter.CanStep; } private void ResetButtonClick(object sender, EventArgs e) { this.ResetDebugging(); } private void SelectedExampleIndexChanged(object sender, EventArgs e) { this.ResetDebugging(); } public new PushSolution Content { get { return (PushSolution)base.Content; } set { base.Content = value; } } private void ResetDebugging() { if (Content == null || pool == null || program == null || exampleComboBox.SelectedIndex < 0) return; var example = Content.Data.Examples[exampleComboBox.SelectedIndex]; if (interpreter != null) { interpreter.Reset(); interpreter2.Reset(); } interpreter.BooleanStack.Push(example.InputBoolean); interpreter.IntegerStack.Push(example.InputInt); interpreter.FloatStack.Push(example.InputFloat); interpreter2.BooleanStack.Push(example.InputBoolean); interpreter2.IntegerStack.Push(example.InputInt); interpreter2.FloatStack.Push(example.InputFloat); interpreter.Run(program, true); interpreter2.Run(program, true); stepWidthBox.Maximum = interpreter.ExecStack.Count; UpdateValueLists(); UpdateExecList(); CheckIfButtonsCanBeEnabled(); } protected override void OnContentChanged() { if (Content == null) return; Name = "Push Solution"; nameTextBox.Text = Name; pool = new PushInterpreterPool(Content.Config); program = Content.IntegerVector.ToPushProgram(Content.Config); if (interpreter != null) { interpreter.Dispose(); interpreter2.Dispose(); } interpreter = pool.Create(Content.Random); interpreter2 = pool.Create(Content.Random); if (Content.Simplify) program = Simplifier.Simplifier.Simplify(program, p => PushEvaluator.Evaluate(p, pool, Content.Random, Content.Data, Content.DataStart, Content.DataEnd).TotalQuality); UpdateExamples(Content.Data); if (exampleComboBox.SelectedIndex < 0) { exampleComboBox.SelectedIndex = 0; // Triggers ResetDebugging via event } else { ResetDebugging(); } InitDebugLists(Content.Config); InitResultGrid(); ClearLists(); UpdateExecList(); UpdateValueLists(); } private void InitResultGrid() { resultsDataGrid.Columns.Clear(); resultsDataGrid.Rows.Clear(); var cellTemplate = new DataGridViewTextBoxCell(); for (var i = 0; i < Content.Data.InputArgumentTypes.Length; i++) { var headerTypeName = ViewHelper.GetHeaderTypeName(Content.Data.InputArgumentTypes[i]); var column = new DataGridViewColumn { HeaderText = string.Format(InputHeaderStringFormat, i + 1, headerTypeName), AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, CellTemplate = cellTemplate }; column.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; resultsDataGrid.Columns.Add(column); } for (var i = 0; i < Content.Data.OutputArgumentTypes.Length; i++) { var headerTypeName = ViewHelper.GetHeaderTypeName(Content.Data.OutputArgumentTypes[i]); var estimatedOutputColumn = new DataGridViewColumn { HeaderText = string.Format(EstimatedOuputHeaderStringFormat, i + 1, headerTypeName), AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, CellTemplate = cellTemplate, }; var outputColumn = new DataGridViewColumn { HeaderText = string.Format(OutputHeaderStringFormat, i + 1, headerTypeName), AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, CellTemplate = cellTemplate }; estimatedOutputColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; outputColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; resultsDataGrid.Columns.Add(estimatedOutputColumn); resultsDataGrid.Columns.Add(outputColumn); } var absoluteDiffColumn = new DataGridViewColumn { HeaderText = AbsoluteDiffHeaderText, AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader, CellTemplate = cellTemplate }; var relativeDiffColumn = new DataGridViewColumn { HeaderText = RelativeDiffHeaderText, AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader, CellTemplate = cellTemplate, }; absoluteDiffColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; relativeDiffColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; resultsDataGrid.Columns.Add(absoluteDiffColumn); resultsDataGrid.Columns.Add(relativeDiffColumn); using (var pushInterpreter = pool.Create(Content.Random)) { foreach (var example in Content.Data.Examples) { var row = new DataGridViewRow(); var absoluteDiff = PushEvaluator.Evaluate(pushInterpreter, program, example, Content.Data.WorstResult); var relativeDiff = absoluteDiff / Math.Abs(Content.Data.BestResult - Content.Data.WorstResult) * 100d; row.HeaderCell.Value = row.Index + 1; row.CreateCells(resultsDataGrid); for (var i = 0; i < Content.Data.InputArgumentTypes.Length; i++) { row.Cells[i].Value = ViewHelper.StringifyInput(Content.Data.InputArgumentTypes[i], example, Separator); } for (var i = 0; i < Content.Data.OutputArgumentTypes.Length; i++) { row.Cells[Content.Data.InputArgumentTypes.Length + i * 2].Value = ViewHelper.StringifyOutput(Content.Data.OutputArgumentTypes[i], example, Separator); row.Cells[Content.Data.InputArgumentTypes.Length + i * 2 + 1].Value = StringifyResult(Content.Data.OutputArgumentTypes[i], pushInterpreter, example, Separator); } row.Cells[Content.Data.TotalArgumentCount + 1].Value = absoluteDiff; row.Cells[Content.Data.TotalArgumentCount + 2].Value = relativeDiff + "%"; this.resultsDataGrid.Rows.Add(row); pushInterpreter.Reset(); } } } private string StringifyResult(ExampleArgumentType type, IPushInterpreter interpreter, Example example, string valueSeparator) { switch (type) { case ExampleArgumentType.Integer: case ExampleArgumentType.IntegerCollection: return interpreter.IntegerStack.IsEmpty ? EmptySign : string.Join(valueSeparator, interpreter.IntegerStack.Peek(GetCount(interpreter.IntegerStack, example.OutputInt))); case ExampleArgumentType.Float: 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))); case ExampleArgumentType.Bool: return interpreter.BooleanStack.IsEmpty ? EmptySign : interpreter.BooleanStack.Top.ToString(); case ExampleArgumentType.Char: return interpreter.CharStack.IsEmpty ? EmptySign : interpreter.CharStack.Top.ToString(); case ExampleArgumentType.String: case ExampleArgumentType.StringCollection: return interpreter.StringStack.IsEmpty ? EmptySign : string.Join(valueSeparator, interpreter.StringStack.Peek(GetCount(interpreter.StringStack, example.OutputString))); default: return string.Empty; } } private int GetCount(IPushStack stack, T[] data) { return Math.Max(0, Math.Min(data.Length, stack.Count)); } private void ClearLists() { foreach (var list in debugControlDict.Values) { list.Items.Clear(); } } private void UpdateExecList() { execList.Items.Clear(); var expressions = interpreter.ExecStack .Reverse() .Select(e => e.StringRepresentation) .ToArray(); execList.Items.AddRange(expressions); execGroupBox.Text = string.Format(GroupBoxTextStringFormat, Enum.GetName(typeof(StackTypes), StackTypes.Exec), interpreter.ExecStack.Count); } private void InitDebugLists(IReadOnlyPushConfiguration config) { debugControlDict.Clear(); // 2 = ExecList + EmptyColumn which is required to fill empty space while (debugTableLayout.ColumnCount > 2) { debugTableLayout.Controls.RemoveAt(1); debugTableLayout.ColumnCount--; } foreach (StackTypes stackType in Enum.GetValues(typeof(StackTypes))) { if (stackType != StackTypes.Exec && ExpressionTable.GetExpressionsByStackTypes(stackType).Intersect(config.EnabledExpressions).Any()) { var list = GetDebugList(stackType); debugControlDict.Add(stackType, list); } } } private ListBox GetDebugList(StackTypes type) { var groupBox = new GroupBox { Anchor = AnchorStyles.Bottom | AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, Text = Enum.GetName(typeof(StackTypes), type) }; var list = new ListBox { Dock = DockStyle.Fill, DrawMode = DrawMode.OwnerDrawFixed }; var stackEntryType = type.GetStackEntryType(); if (stackEntryType == typeof(double) || stackEntryType == typeof(long)) { list.DrawItem += (sender, e) => { if (e.Index <= -1) return; var item = list.Items[e.Index]; e.DrawBackground(); e.DrawFocusRectangle(); var brush = new SolidBrush(e.ForeColor); var size = e.Graphics.MeasureString(item.ToString(), e.Font); e.Graphics.DrawString( item.ToString(), e.Font, brush, e.Bounds.Right - size.Width, e.Bounds.Top + (e.Bounds.Height / 2 - size.Height / 2)); }; } groupBox.Controls.Add(list); var columnWidth = stackEntryType == typeof(Expression) ? 250 : 150; debugTableLayout.ColumnCount++; debugTableLayout.ColumnStyles.Insert(1, new ColumnStyle(SizeType.Absolute, columnWidth)); debugTableLayout.Controls.Add(groupBox); debugTableLayout.Controls.SetChildIndex(groupBox, 1); return list; } private void UpdateValueLists() { ClearLists(); if (Content == null || interpreter == null) return; foreach (var pair in debugControlDict) { var stack = interpreter.GetStackEntriesByType(pair.Key); var name = Enum.GetName(typeof(StackTypes), pair.Key); pair.Value.Items.AddRange(stack.Reverse().ToArray()); ((GroupBox)pair.Value.Parent).Text = string.Format(GroupBoxTextStringFormat, name, pair.Value.Items.Count); } } private void UpdateExamples(Data data) { exampleComboBox.Items.Clear(); if (data == null) return; var stringRepresentations = data.Examples.Select(e => string.Join(Separator, e.InputArgs) + exampleSplitter + string.Join(Separator, e.OutputArgs)); foreach (var str in stringRepresentations) { exampleComboBox.Items.Add(str); } } } }