#region License Information /* HeuristicLab * Copyright (C) 2002-2016 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.Drawing; using System.Linq; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; using HeuristicLab.Collections; using HeuristicLab.Data; using HeuristicLab.MainForm; using HeuristicLab.MainForm.WindowsForms; using HeuristicLab.Problems.DataAnalysis; using HeuristicLab.Problems.DataAnalysis.Views; using HeuristicLab.Visualization.ChartControlsExtensions; namespace HeuristicLab.Algorithms.DataAnalysis.Views { [View("Interactive Estimator")] [Content(typeof(GaussianProcessRegressionSolution))] public partial class GaussianProcessRegressionSolutionInteractiveRangeEstimatorView : DataAnalysisSolutionEvaluationView { private const int DrawingSteps = 1000; private const string EstimatedMeanSeriesName = "Estimated Mean"; private const string EstimatedVarianceSeriesName = "95% Conficence Interval"; private readonly List dimensionNames; private readonly ObservableList dimensionTrackbars; private int ActiveDimension { get { return dimensionTrackbars.FindIndex(tb => tb.Checked); } } public new GaussianProcessRegressionSolution Content { get { return (GaussianProcessRegressionSolution)base.Content; } set { base.Content = value; } } public GaussianProcessRegressionSolutionInteractiveRangeEstimatorView() : base() { dimensionNames = new List(); dimensionTrackbars = new ObservableList(); dimensionTrackbars.ItemsAdded += (sender, args) => { ForEach(args.Items.Select(i => i.Value), RegisterEvents); }; dimensionTrackbars.ItemsRemoved += (sender, args) => { ForEach(args.Items.Select(i => i.Value), DeregisterEvents); }; dimensionTrackbars.CollectionReset += (sender, args) => { ForEach(args.OldItems.Select(i => i.Value), DeregisterEvents); ForEach(args.Items.Select(i => i.Value), RegisterEvents); }; InitializeComponent(); // Avoid additional horizontal scrollbar var vertScrollWidth = SystemInformation.VerticalScrollBarWidth; tableLayoutPanel.Padding = new Padding(0, 0, vertScrollWidth, 0); // Configure axis chart.CustomizeAllChartAreas(); chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true; chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true; chart.ChartAreas[0].CursorX.Interval = 1; chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true; chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true; chart.ChartAreas[0].CursorY.Interval = 0; } private void RedrawChart() { chart.Series.Clear(); chart.ChartAreas[0].AxisX.StripLines.Clear(); if (Content == null || ActiveDimension < 0) return; double minX = dimensionTrackbars[ActiveDimension].Limits.Lower; double maxX = dimensionTrackbars[ActiveDimension].Limits.Upper; decimal stepSize = (decimal)((maxX - minX) / DrawingSteps); // Build dataset var activeXs = Enumerable.Range(0, DrawingSteps).Select(i => (decimal)minX + i * stepSize).ToList(); var fixedXs = dimensionTrackbars.Select(tb => tb.Value).ToList(); var values = new double[DrawingSteps, dimensionNames.Count]; for (int r = 0; r < DrawingSteps; r++) { for (int c = 0; c < dimensionNames.Count; c++) { values[r, c] = (double)(c == ActiveDimension ? activeXs[r] : fixedXs[c]); } } var dataset = new Dataset(dimensionNames, values); // Estimations var model = Content.Model; var means = model.GetEstimatedValues(dataset, Enumerable.Range(0, DrawingSteps)).ToList(); var variances = model.GetEstimatedVariances(dataset, Enumerable.Range(0, DrawingSteps)).ToList(); // Charting config chart.ChartAreas[0].AxisX.Minimum = minX; chart.ChartAreas[0].AxisX.Maximum = maxX; chart.ChartAreas[0].AxisX.Interval = (maxX - minX) / 10; // ToDo only databind and put config in codebehind chart.Series.Add(EstimatedVarianceSeriesName); chart.Series[EstimatedVarianceSeriesName].LegendText = EstimatedVarianceSeriesName; chart.Series[EstimatedVarianceSeriesName].ChartType = SeriesChartType.Range; chart.Series[EstimatedVarianceSeriesName].EmptyPointStyle.Color = chart.Series[EstimatedVarianceSeriesName].Color; chart.Series.Add(EstimatedMeanSeriesName); chart.Series[EstimatedMeanSeriesName].LegendText = EstimatedMeanSeriesName; chart.Series[EstimatedMeanSeriesName].ChartType = SeriesChartType.FastLine; // Charting databind var lower = means.Zip(variances, GetLowerConfBound).ToList(); var upper = means.Zip(variances, GetUpperConfBound).ToList(); chart.Series[EstimatedVarianceSeriesName].Points.DataBindXY(activeXs, lower, upper); chart.Series[EstimatedMeanSeriesName].Points.DataBindXY(activeXs, means); // Update StripLines var trainingValues = Content.ProblemData.Dataset.GetDoubleValues(dimensionNames[ActiveDimension], Content.ProblemData.TrainingIndices); var trainingRange = new DoubleRange(trainingValues.Min(), trainingValues.Max()); if (minX < trainingRange.Start) CreateAndAddStripLine(minX, trainingRange.Start, Color.FromArgb(40, 223, 58, 2), Color.Transparent); if (maxX > trainingRange.End) CreateAndAddStripLine(trainingRange.End, maxX, Color.FromArgb(40, 223, 58, 2), Color.Transparent); // Update axis description chart.ChartAreas[0].AxisX.Title = dimensionNames[ActiveDimension]; } private void CreateAndAddStripLine(double start, double end, Color color, Color secondColor) { StripLine stripLine = new StripLine { BackColor = color, BackSecondaryColor = secondColor, StripWidth = end - start, IntervalOffset = start }; chart.ChartAreas[0].AxisX.StripLines.Add(stripLine); } private void UpdateConfigurationControls() { tableLayoutPanel.SuspendRepaint(); tableLayoutPanel.SuspendLayout(); tableLayoutPanel.RowCount = 0; tableLayoutPanel.Controls.Clear(); dimensionNames.Clear(); dimensionTrackbars.Clear(); if (Content == null) { tableLayoutPanel.ResumeLayout(true); tableLayoutPanel.ResumeRepaint(true); return; } dimensionNames.AddRange(Content.ProblemData.AllowedInputVariables); var ranges = new List(); for (int i = 0; i < dimensionNames.Count; i++) { var values = Content.ProblemData.Dataset.GetDoubleValues(dimensionNames[i], Content.ProblemData.AllIndices); double min, max, interval; ChartUtil.CalculateAxisInterval(values.Min(), values.Max(), 10, out min, out max, out interval); ranges.Add(new DoubleLimit(min, max)); } tableLayoutPanel.RowCount = dimensionNames.Count; while (tableLayoutPanel.RowStyles.Count < dimensionNames.Count) tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); for (int i = 0; i < dimensionNames.Count; i++) { var name = dimensionNames[i]; var trainingData = Content.ProblemData.Dataset.GetDoubleValues(name, Content.ProblemData.TrainingIndices).ToList(); var dimensionTrackbar = new DensityTrackbar(name, ranges[i], trainingData); // events registered automatically dimensionTrackbars.Add(dimensionTrackbar); dimensionTrackbar.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; tableLayoutPanel.Controls.Add(dimensionTrackbar, 0, i); } if (dimensionTrackbars.Any()) dimensionTrackbars.First().Checked = true; tableLayoutPanel.ResumeLayout(true); tableLayoutPanel.ResumeRepaint(true); } private void RegisterEvents(DensityTrackbar trackbar) { trackbar.CheckedChanged += DimensionTrackbar_CheckedChanged; trackbar.ValueChanged += DimensionTrackbar_ValueChanged; trackbar.LimitsChanged += DimensionTrackbar_LimitsChanged; } private void DeregisterEvents(DensityTrackbar trackbar) { trackbar.CheckedChanged -= DimensionTrackbar_CheckedChanged; trackbar.ValueChanged -= DimensionTrackbar_ValueChanged; trackbar.LimitsChanged -= DimensionTrackbar_LimitsChanged; } private void DimensionTrackbar_CheckedChanged(object sender, EventArgs e) { var trackBarSender = sender as DensityTrackbar; if (trackBarSender == null || !trackBarSender.Checked) return; // Uncheck all others foreach (var tb in dimensionTrackbars.Except(new[] { trackBarSender })) tb.Checked = false; RedrawChart(); } private void DimensionTrackbar_LimitsChanged(object sender, EventArgs e) { RedrawChart(); } private void DimensionTrackbar_ValueChanged(object sender, EventArgs e) { RedrawChart(); } #region Events protected override void RegisterContentEvents() { base.RegisterContentEvents(); Content.ModelChanged += Content_ModelChanged; Content.ProblemDataChanged += Content_ProblemDataChanged; } protected override void DeregisterContentEvents() { base.DeregisterContentEvents(); Content.ModelChanged -= Content_ModelChanged; Content.ProblemDataChanged -= Content_ProblemDataChanged; } protected override void OnContentChanged() { base.OnContentChanged(); UpdateConfigurationControls(); RedrawChart(); } private void Content_ModelChanged(object sender, EventArgs e) { RedrawChart(); } private void Content_ProblemDataChanged(object sender, EventArgs e) { UpdateConfigurationControls(); RedrawChart(); } private void Chart_MouseDoubleClick(object sender, MouseEventArgs e) { var result = chart.HitTest(e.X, e.Y); if (true || result.ChartArea != null && (result.ChartElementType == ChartElementType.PlottingArea || result.ChartElementType == ChartElementType.Gridlines) || result.ChartElementType == ChartElementType.StripLines) { foreach (var axis in result.ChartArea.Axes) axis.ScaleView.ZoomReset(int.MaxValue); } } private void chart_MouseMove(object sender, MouseEventArgs e) { //HitTestResult result = chart.HitTest(e.X, e.Y); //if (result.ChartElementType == ChartElementType.LegendItem && result.Series.Name != TARGETVARIABLE_SERIES_NAME) // Cursor = Cursors.Hand; //else // Cursor = Cursors.Default; } private void chart_MouseDown(object sender, MouseEventArgs e) { //HitTestResult result = chart.HitTest(e.X, e.Y); //if (result.ChartElementType == ChartElementType.LegendItem && result.Series.Name != TARGETVARIABLE_SERIES_NAME) { // ToggleSeriesData(result.Series); //} } private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) { //if (chart.Series.Count != 4) return; //e.LegendItems[0].Cells[1].ForeColor = this.chart.Series[EstimatedMeanSeriesName].Points.Count == 0 ? Color.Gray : Color.Black; //e.LegendItems[1].Cells[1].ForeColor = this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black; //e.LegendItems[2].Cells[1].ForeColor = this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black; //e.LegendItems[3].Cells[1].ForeColor = this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black; } #endregion #region Helper private double GetLowerConfBound(double m, double s2) { return m - 1.96 * Math.Sqrt(s2); } private double GetUpperConfBound(double m, double s2) { return m + 1.96 * Math.Sqrt(s2); } public static void ForEach(IEnumerable source, Action action) { foreach (T item in source) action(item); } #endregion } }