#region License Information /* HeuristicLab * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Forms; using HeuristicLab.Analysis; using HeuristicLab.Collections; using HeuristicLab.Common; using HeuristicLab.Core.Views; using HeuristicLab.Data; using HeuristicLab.MainForm; using HeuristicLab.MainForm.WindowsForms; namespace HeuristicLab.Optimization.Views { [View("Run-length Distribution View")] [Content(typeof(RunCollection), false)] public partial class RunCollectionRLDView : ItemView { private const string AllRuns = "All Runs"; public new RunCollection Content { get { return (RunCollection)base.Content; } set { base.Content = value; } } private double[] levels; private bool suppressUpdates; private readonly IndexedDataTable combinedDataTable; public IndexedDataTable CombinedDataTable { get { return combinedDataTable; } } public RunCollectionRLDView() { InitializeComponent(); combinedDataTable = new IndexedDataTable("Combined DataTable", "A data table containing the ECDF of each of a number of groups.") { VisualProperties = { YAxisTitle = "Proportion of reached targets" } }; viewHost.Content = combinedDataTable; suppressUpdates = false; } #region Content events protected override void RegisterContentEvents() { base.RegisterContentEvents(); Content.ItemsAdded += Content_ItemsAdded; Content.ItemsRemoved += Content_ItemsRemoved; Content.CollectionReset += Content_CollectionReset; Content.UpdateOfRunsInProgressChanged += Content_UpdateOfRunsInProgressChanged; Content.OptimizerNameChanged += Content_AlgorithmNameChanged; } protected override void DeregisterContentEvents() { Content.ItemsAdded -= Content_ItemsAdded; Content.ItemsRemoved -= Content_ItemsRemoved; Content.CollectionReset -= Content_CollectionReset; Content.UpdateOfRunsInProgressChanged -= Content_UpdateOfRunsInProgressChanged; Content.OptimizerNameChanged -= Content_AlgorithmNameChanged; base.DeregisterContentEvents(); } private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs e) { if (suppressUpdates) return; if (InvokeRequired) { Invoke(new CollectionItemsChangedEventHandler(Content_ItemsAdded), sender, e); return; } UpdateGroupComboBox(); UpdateDataTableComboBox(); foreach (var run in e.Items) RegisterRunEvents(run); } private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs e) { if (suppressUpdates) return; if (InvokeRequired) { Invoke(new CollectionItemsChangedEventHandler(Content_ItemsRemoved), sender, e); return; } UpdateGroupComboBox(); UpdateDataTableComboBox(); foreach (var run in e.Items) DeregisterRunEvents(run); } private void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs e) { if (suppressUpdates) return; if (InvokeRequired) { Invoke(new CollectionItemsChangedEventHandler(Content_CollectionReset), sender, e); return; } UpdateGroupComboBox(); UpdateDataTableComboBox(); foreach (var run in e.OldItems) DeregisterRunEvents(run); } private void Content_AlgorithmNameChanged(object sender, EventArgs e) { if (InvokeRequired) Invoke(new EventHandler(Content_AlgorithmNameChanged), sender, e); else UpdateCaption(); } private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) { if (InvokeRequired) { Invoke(new EventHandler(Content_UpdateOfRunsInProgressChanged), sender, e); return; } suppressUpdates = Content.UpdateOfRunsInProgress; if (!suppressUpdates) { UpdateDataTableComboBox(); UpdateGroupComboBox(); UpdateRuns(); } } private void RegisterRunEvents(IRun run) { run.PropertyChanged += run_PropertyChanged; } private void DeregisterRunEvents(IRun run) { run.PropertyChanged -= run_PropertyChanged; } private void run_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (suppressUpdates) return; if (InvokeRequired) { Invoke((Action)run_PropertyChanged, sender, e); } else { if (e.PropertyName == "Visible") UpdateRuns(); } } #endregion protected override void OnContentChanged() { base.OnContentChanged(); dataTableComboBox.Items.Clear(); groupComboBox.Items.Clear(); combinedDataTable.Rows.Clear(); UpdateCaption(); if (Content != null) { UpdateGroupComboBox(); UpdateDataTableComboBox(); } } protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); groupComboBox.Enabled = Content != null; dataTableComboBox.Enabled = Content != null; fixedTargetButton.Enabled = Content != null && levels != null && dataTableComboBox.SelectedIndex >= 0; } private void UpdateRuns() { if (InvokeRequired) { Invoke((Action)UpdateRuns); return; } SuspendRepaint(); try { combinedDataTable.VisualProperties.XAxisLogScale = false; combinedDataTable.Rows.Clear(); var table = (string)dataTableComboBox.SelectedItem; if (string.IsNullOrEmpty(table)) return; if (levels == null) { var worst = Content.Select(x => ((IndexedDataTable)x.Results[table]).Rows.First().Values.Max(y => y.Item2)).First(); var best = Content.Select(x => ((IndexedDataTable)x.Results[table]).Rows.First().Values.Min(y => y.Item2)).Last(); levels = Enumerable.Range(0, 11).Select(x => worst + (x / 10.0) * (best - worst)).ToArray(); suppressTargetsEvents = true; targetsTextBox.Text = string.Join(" ; ", levels); suppressTargetsEvents = false; } var selectedGroup = (string)groupComboBox.SelectedItem; if (string.IsNullOrEmpty(selectedGroup)) return; List>> groupedRuns; if (selectedGroup == AllRuns) groupedRuns = new List>> { Tuple.Create(AllRuns, Content.ToList()) }; else groupedRuns = (from r in Content group r by r.Parameters[selectedGroup].ToString() into g select Tuple.Create(g.Key, g.ToList())).ToList(); var xAxisTitles = new HashSet(); foreach (var group in groupedRuns) { var hits = new SortedList(); foreach (var run in group.Item2) { var resultsTable = (IndexedDataTable)run.Results[table]; xAxisTitles.Add(resultsTable.VisualProperties.XAxisTitle); var values = resultsTable.Rows.First().Values; var maximization = values.First().Item2 < values.Last().Item2; var i = 0; var j = 0; var current = values[j]; var prev = Tuple.Create(-1.0, double.NaN); while (i < levels.Length) { if ((double.IsNaN(prev.Item2) || prev.Item2 != current.Item2) && (maximization && current.Item2 >= levels[i] || !maximization && current.Item2 <= levels[i])) { if (hits.ContainsKey(current.Item1)) hits[current.Item1]++; else hits[current.Item1] = 1; i++; } else { j++; if (j >= values.Count) break; prev = current; current = values[j]; } } } var row = new IndexedDataRow(group.Item1) { VisualProperties = { ChartType = DataRowVisualProperties.DataRowChartType.StepLine } }; var total = 0.0; foreach (var h in hits) { total += h.Value; row.Values.Add(Tuple.Create(h.Key, total / (group.Item2.Count * levels.Length))); } combinedDataTable.Rows.Add(row); } combinedDataTable.VisualProperties.XAxisTitle = string.Join(" / ", xAxisTitles); combinedDataTable.VisualProperties.XAxisLogScale = logScalingCheckBox.Checked; } finally { ResumeRepaint(true); } } private void UpdateGroupComboBox() { string selectedItem = (string)groupComboBox.SelectedItem; var groupings = Content.ParameterNames.OrderBy(x => x).ToArray(); groupComboBox.Items.Clear(); groupComboBox.Items.Add(AllRuns); groupComboBox.Items.AddRange(groupings); if (selectedItem != null && groupComboBox.Items.Contains(selectedItem)) { groupComboBox.SelectedItem = selectedItem; } else if (groupComboBox.Items.Count > 0) { groupComboBox.SelectedItem = groupComboBox.Items[0]; } } private void UpdateDataTableComboBox() { string selectedItem = (string)dataTableComboBox.SelectedItem; dataTableComboBox.Items.Clear(); var dataTables = (from run in Content from result in run.Results where result.Value is IndexedDataTable select result.Key).Distinct().ToArray(); dataTableComboBox.Items.AddRange(dataTables); if (selectedItem != null && dataTableComboBox.Items.Contains(selectedItem)) { dataTableComboBox.SelectedItem = selectedItem; } else if (dataTableComboBox.Items.Count > 0) { dataTableComboBox.SelectedItem = dataTableComboBox.Items[0]; } } private void UpdateCaption() { Caption = Content != null ? Content.OptimizerName + " RLD View" : ViewAttribute.GetViewName(GetType()); } private void groupComboBox_SelectedIndexChanged(object sender, EventArgs e) { UpdateRuns(); SetEnabledStateOfControls(); } private void dataTableComboBox_SelectedIndexChanged(object sender, EventArgs e) { UpdateRuns(); SetEnabledStateOfControls(); } private void logScalingCheckBox_CheckedChanged(object sender, EventArgs e) { combinedDataTable.VisualProperties.XAxisLogScale = logScalingCheckBox.Checked; } private bool suppressTargetsEvents; private void targetsTextBox_Validating(object sender, CancelEventArgs e) { if (suppressTargetsEvents) return; var targetStrings = targetsTextBox.Text.Split(new[] { ';', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries); var targetList = new List(); foreach (var ts in targetStrings) { double t; if (!double.TryParse(ts, out t)) { errorProvider.SetError(targetsTextBox, "Not all targets can be parsed: " + ts); e.Cancel = true; return; } targetList.Add(t); } if (targetList.Count == 0) { errorProvider.SetError(targetsTextBox, "Give at least one target value!"); e.Cancel = true; return; } e.Cancel = false; errorProvider.SetError(targetsTextBox, null); levels = targetList.ToArray(); UpdateRuns(); SetEnabledStateOfControls(); } private void generateTargetsButton_Click(object sender, EventArgs e) { decimal max = 1, min = 0, count = 10; if (levels != null) { max = (decimal)Math.Max(levels.First(), levels.Last()); min = (decimal)Math.Min(levels.First(), levels.Last()); count = levels.Length; } else if (Content.Count > 0 && dataTableComboBox.SelectedIndex >= 0) { var table = (string)dataTableComboBox.SelectedItem; var worst = Content.Select(x => ((IndexedDataTable)x.Results[table]).Rows.First().Values.Max(y => y.Item2)).First(); var best = Content.Select(x => ((IndexedDataTable)x.Results[table]).Rows.First().Values.Min(y => y.Item2)).Last(); max = (decimal)Math.Max(best, worst); min = (decimal)Math.Min(best, worst); count = 10; } using (var dialog = new DefineArithmeticProgressionDialog(false, min, max, (max - min) / count)) { if (dialog.ShowDialog() == DialogResult.OK) { if (dialog.Values.Any()) { var maximization = true; if (Content.Count > 0 && Content.First().Parameters.ContainsKey("Maximization")) maximization = ((BoolValue)Content.First().Parameters["Maximization"]).Value; levels = maximization ? dialog.Values.Select(x => (double)x).ToArray() : dialog.Values.Reverse().Select(x => (double)x).ToArray(); UpdateRuns(); SetEnabledStateOfControls(); } } } } private void fixedTargetButton_Click(object sender, EventArgs e) { var table = (string)dataTableComboBox.SelectedItem; foreach (var run in Content) { var resultsTable = (IndexedDataTable)run.Results[table]; var values = resultsTable.Rows.First().Values; var maximization = values.First().Item2 < values.Last().Item2; var i = 0; var j = 0; var current = values[j]; var prev = Tuple.Create(-1.0, double.NaN); while (i < levels.Length) { if (prev.Item2 != current.Item2 && (maximization && current.Item2 >= levels[i] || !maximization && current.Item2 <= levels[i])) { run.Results[table + ".Target" + levels[i]] = new DoubleValue(current.Item1); i++; } else { j++; if (j >= values.Count) break; prev = current; current = values[j]; } } } } private void fixedCostButton_Click(object sender, EventArgs e) { var table = (string)dataTableComboBox.SelectedItem; var budgetStrings = budgetsTextBox.Text.Split(new[] { ';', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries); if (budgetStrings.Length == 0) { MessageBox.Show("Define a number of budgets."); return; } var budgetList = new List(); foreach (var bs in budgetStrings) { double v; if (!double.TryParse(bs, out v)) { MessageBox.Show("Budgets must be a valid number: " + bs); return; } budgetList.Add(v); } budgetList.Sort(); foreach (var run in Content) { var resultsTable = (IndexedDataTable)run.Results[table]; var values = resultsTable.Rows.First().Values; var i = 0; var j = 0; var current = values[j]; var prev = Tuple.Create(-1.0, double.NaN); while (i < budgetList.Count) { if (prev.Item2 != current.Item2 && current.Item1 >= budgetList[i]) { run.Results[table + ".Cost" + budgetList[i]] = new DoubleValue(double.IsNaN(prev.Item2) || current.Item1 == budgetList[i] ? current.Item2 : prev.Item2); i++; } else { j++; if (j >= values.Count) break; prev = current; current = values[j]; } } } } } }