Click here to Skip to main content
15,886,026 members
Articles / General Programming / Algorithms

Introduction to Numerical Solutions

Rate me:
Please Sign up or sign in to vote.
4.89/5 (11 votes)
24 Feb 2012CPOL11 min read 30.2K   970   36  
An introduction to numerical solver algorithms with general purpose demonstration code.
///////////////////////////////////////////////////////////////////////////////
//
//  SolutionVisualizer.cs
//
//  By Philip R. Braica (HoshiKata@aol.com, VeryMadSci@gmail.com)
//
//  Distributed under the The Code Project Open License (CPOL)
//  http://www.codeproject.com/info/cpol10.aspx
///////////////////////////////////////////////////////////////////////////////

// Using.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

// Namespace.
namespace SolverDemo
{
    /// <summary>
    /// Visualize the error with two different axises.
    /// </summary>
    public partial class SolutionVisualizer : UserControl
    {
        #region Protected data.
        /// <summary>
        /// Lock object.
        /// </summary>
        protected object m_lockObject = new object();

        /// <summary>
        /// Parameter list.
        /// </summary>
        protected List<Solver.Parameter> m_parameters = null;

        /// <summary>
        /// Error Delegate.
        /// </summary>
        protected Solver.ComputeError_Delegate m_computeErrorDelegate = null;

        /// <summary>
        /// Best point.
        /// </summary>
        protected Solver.Step m_best = null;

        /// <summary>
        /// Setup the checkboxes once, that way the selection doesn't keep changing.
        /// </summary>
        protected bool once = true;
        #endregion

        #region Public Interface: SolutionVisualizer(), void Setup( ... )

        /// <summary>
        /// Constructor.
        /// </summary>
        public SolutionVisualizer()
        {
            InitializeComponent();

            ComboBox[] cbs = { comboBox1, comboBox2 };
            for (int i = 0; i < cbs.Length; i++)
            {
                cbs[i].SelectedIndexChanged += new EventHandler(SolutionVisualizer_DebounceUpdate);
            }
            checkBox1.CheckedChanged += new EventHandler(SolutionVisualizer_DebounceUpdate);
            checkBox2.CheckedChanged += new EventHandler(SolutionVisualizer_DebounceUpdate);
            numericUpDown1.ValueChanged += new EventHandler(SolutionVisualizer_DebounceUpdate);
        }

        /// <summary>
        /// Setup.
        /// </summary>
        /// <param name="p"></param>
        /// <param name="computeErrorDelegate"></param>
        /// <param name="best"></param>
        public void Setup(
            List<Solver.Parameter> p,
            Solver.ComputeError_Delegate computeErrorDelegate, 
            Solver.Step best)
        {
            lock (m_lockObject)
            {
                m_parameters = p;
                m_computeErrorDelegate = computeErrorDelegate;
                m_best = best;
                List<string> names = new List<string>();
                for (int i = 0; i < p.Count; i++)
                {
                    names.Add(p[i].Name);
                }
                ComboBox[] cbs = { comboBox1, comboBox2 };
                for (int i = 0; i < cbs.Length; i++)
                {
                    // If the number of names changed then reset indexes and selected.
                    if (cbs[i].Items.Count != names.Count)
                    {
                        once = true;
                    }
                    cbs[i].Items.Clear();
                    cbs[i].Items.AddRange(names.ToArray());
                    if (once)
                    {
                        cbs[i].SelectedIndex = i < names.Count ? i : names.Count - 1;
                        cbs[i].Text = cbs[i].SelectedItem.ToString();
                    }
                }
                once = false;
            }
            SolutionVisualizer_DebounceUpdate(this, EventArgs.Empty);
        }
        #endregion

        #region Simple redraw debounce: SolutionVisualizer_DebounceUpdate() -> timer1_Tick() -> redraw()
        /// <summary>
        /// Trigger redraw.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SolutionVisualizer_DebounceUpdate(object sender, EventArgs e)
        {
            timer1.Enabled = true;
        }

        /// <summary>
        /// Redraw timer.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            timer1.Enabled = false;
            lock (m_lockObject)
            {
                redraw();
            }
        }
        #endregion

        /// <summary>
        /// Redraw chart.
        /// </summary>
        protected virtual void redraw()
        {
            string pstr = comboBox1.Text;
            string sstr = comboBox2.Text;
            int pind = 0;
            int sind = 0;
            for (int i = 0; i < m_parameters.Count; i++)
            {
                if (pstr == m_parameters[i].Name) pind = i;
                if (sstr == m_parameters[i].Name) sind = i;
            }
            List<Series> set = null;
            if (checkBox2.Checked)
            {
                // Stacked set of lines.
                set = makeStacked(
                    m_parameters[pind].Name, // Label name.
                    Color.Blue, // Base color.
                    pind, // Principal index.
                    sind, // Secondary index.
                    10);  // Number of lines.
            }
            else
            {
                // Two seperate indexes as they pass through the center point.
                Series s1 = makeSeries(m_parameters[pind].Name, Color.Green, pind);
                Series s2 = makeSeries(m_parameters[sind].Name, Color.Blue, sind);
                s2.YAxisType = AxisType.Secondary;
                set.Add(s1);
                set.Add(s2);
            }

            chart1.Series.Clear();
            chart1.ChartAreas[0].RecalculateAxesScale();
            for (int i = 0; i < set.Count; i++)
            {
                chart1.Series.Add(set[i]);
            }            
        }

        /// <summary>
        /// Adjust color brightness.
        /// </summary>
        /// <param name="c"></param>
        /// <param name="change"></param>
        /// <returns></returns>
        protected Color adjustColorBrightness(Color c, float change)
        {
            change = change < -100 ? -100 : change > 100 ? 100 : change * 0.01f;
            return (change < 0) ? ControlPaint.Dark(c, -change) : change == 0 ? c : ControlPaint.Light(c, change);
        }

        /// <summary>
        /// Make an array of colors light to dark of color c where c is in the middle.
        /// </summary>
        /// <param name="c"></param>
        /// <param name="div"></param>
        /// <returns></returns>
        protected Color[] makeColorArray(Color c, int div)
        {
            Color[] clrs = new Color[div];
            for (int i = 0; i < div; i++)
            {
                float change = (i - (div >> 1)) * 30;
                clrs[i] = adjustColorBrightness(c, change);
            }
            return clrs;
        }

        /// <summary>
        /// Make a series.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="color"></param>
        /// <param name="primary"></param>
        /// <param name="secondary"></param>
        /// <returns></returns>
        protected List<Series> makeStacked(string name, Color color, int primary, int secondary, int divisions)
        {
            List<Series> set = new List<Series>();
            bool full = checkBox1.Checked;
            double delta = (double)numericUpDown1.Value;
            divisions = divisions < 1 ? 1 : divisions;
            double sMin = full ? m_parameters[secondary].MinValue : m_best.ParamValues[secondary] - delta;
            double sMax = full ? m_parameters[secondary].MaxValue : m_best.ParamValues[secondary] + delta;
            double pMin = full ? m_parameters[primary].MinValue : m_best.ParamValues[primary] - delta;
            double pMax = full ? m_parameters[primary].MaxValue : m_best.ParamValues[primary] + delta;
            double sScale = (sMax - sMin) / divisions;
            double pScale = (pMax - pMin) / 200;
            Color [] clrs = makeColorArray(color, divisions + 1);
            
            int ind = 0;
            double[] p = new double[m_best.ParamValues.Length];
            for (int i = 0; i < p.Length; i++)
            {
                p[i] = m_best.ParamValues[i];
            }
            
            for (double sx = sMin; sx < sMax; sx += sScale)
            {
                Series s = new Series();
                s.Name = name + " " + sx.ToString();
                s.Color = clrs[ind];
                ind++;
                s.ChartType = SeriesChartType.Line;
                p[secondary] = sx;
                surfacePlot1.YAxis.Add(sx);
                for (double px = pMin; px < pMax; px += pScale)
                {
                    p[primary] = px;
                    double y = m_computeErrorDelegate(p);
                    surfacePlot1.Values.Add(y);
                    s.Points.Add(new DataPoint(px, y));
                }
                set.Add(s);
            }

            surfacePlot1.XAxis.Clear();
            surfacePlot1.YAxis.Clear();
            surfacePlot1.Values.Clear();
            
            surfacePlot1.ColorList.Clear();
            //            surfacePlot1.ColorList.AddRange(new Color []{Color.Red, Color.Orange, Color.Yellow, Color.Green,Color.Blue,Color.Indigo, Color.Violet});
            surfacePlot1.ColorList.AddRange(new Color[] { Color.White, Color.Black });
            sScale = (sMax-sMin) / 200;
            pScale = (pMax - pMin) / 200;
            for (double px = pMin; px < pMax; px += pScale)
            {
                surfacePlot1.XAxis.Add(px);
            }
            for (double sx = sMin; sx < sMax; sx += sScale)
            {
                ind++;
                p[secondary] = sx;
                surfacePlot1.YAxis.Add(sx);
                for (double px = pMin; px < pMax; px += pScale)
                {
                    p[primary] = px;
                    double y = m_computeErrorDelegate(p);
                    surfacePlot1.Values.Add(y);
                }
            }
            surfacePlot1.Redraw();
            return set;
        }

        /// <summary>
        /// Make a series.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="color"></param>
        /// <param name="paramIndex"></param>
        /// <returns></returns>
        protected Series makeSeries(string name, Color color, int paramIndex)
        {
            Series s = new Series();
            s.Color = color;
            s.ChartType = SeriesChartType.Line;
            double[] p = new double[m_best.ParamValues.Length];
            for (int i =0; i< p.Length; i++)
            {
                p[i] = m_best.ParamValues[i];
            }
            if (checkBox1.Checked)
            {
                for (double x = m_parameters[paramIndex].MinValue; x < m_parameters[paramIndex].MaxValue; x += m_parameters[paramIndex].InitialStepSize / 10)
                {
                    p[paramIndex] = x;
                    double y = m_computeErrorDelegate(p);
                    s.Points.Add(new DataPoint(x, y));
                }
            }
            else
            {
                double delta = (double)numericUpDown1.Value;
                for (double x = m_best.ParamValues[paramIndex] - delta; x < m_best.ParamValues[paramIndex] + delta; x += delta / 20)
                {
                    p[paramIndex] = x;
                    double y = m_computeErrorDelegate(p);
                    s.Points.Add(new DataPoint(x, y));
                }
            }
            return s;
        }

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
United States United States
Phil is a Principal Software developer focusing on weird yet practical algorithms that run the gamut of embedded and desktop (PID loops, Kalman filters, FFTs, client-server SOAP bindings, ASIC design, communication protocols, game engines, robotics).

In his personal life he is a part time mad scientist, full time dad, and studies small circle jujitsu, plays guitar and piano.

Comments and Discussions