Browse Source

Merge pull request #588 from TobiasGlaubach/Polynomial-class

Polynomial class
ridge-regression
Christoph Ruegg 8 years ago
committed by GitHub
parent
commit
4dd42f0dcc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 394
      src/Numerics.Tests/PolynomialTests.cs
  2. 9
      src/Numerics/FindRoots.cs
  3. 696
      src/Numerics/Polynomial.cs

394
src/Numerics.Tests/PolynomialTests.cs

@ -0,0 +1,394 @@
// <copyright file="FitTests.cs" company="Math.NET">
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
//
// Copyright (c) 2009-2018 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using MathNet.Numerics;
using MathNet.Numerics.LinearRegression;
using MathNet.Numerics.Statistics;
using NUnit.Framework;
namespace MathNet.Numerics.UnitTests
{
/// <Note>
/// some of these tests were inspired by numpys tests in python for the Polynomial functions.
/// Thanks to the numpy contributers!
/// </Note>
[TestFixture, Category("Calculus")]
public class PolynomialTests
{
[TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x^1 + 3x^2 + 0x^3 + 2x^4")]
[TestCase(new double[0], "")]
[TestCase(new double[] { 0, 4, 3, 0, 0 }, "0 + 4x^1 + 3x^2 + 0x^3 + 0x^4")]
public void ToStringTest(double[] x, string expected)
{
var p = new Polynomial(x);
Assert.AreEqual(expected, p.ToString());
}
[TestCase(new double[] { 5, 4, 3, 0, 2 }, new double[] { 4*1, 3*2, 0*3, 2*4 })]
[TestCase(new double[0], null)]
[TestCase(new double[] { 0, 4, 3, 0, 0 }, new double[] { 4*1, 3*2 })]
public void DifferentiateTest(double[] x, double[] expected)
{
var p = new Polynomial(x);
var p_res = p.Differentiate();
if (expected == null)
{
Assert.IsNull(p_res);
return;
}
else
{
Assert.AreEqual(expected.Length, p_res.Degree, "length mismatch");
for (int k = 0; k < p_res.Degree; k++)
{
Assert.AreEqual(expected[k], p_res.Coeffs[k], "idx: " + k + " mismatch");
}
}
}
[TestCase(new double[] { 5, 4, 3, 0, 2 }, new double[] { 0, 5.0/1.0, 4.0/2.0, 3.0/3.0, 0.0/4.0, 2.0/5.0 })]
[TestCase(new double[0], new double[1] { 0 })]
[TestCase(new double[] { 0, 1, 6, 8 }, new double[] {0, 0.0/1.0, 1.0/2.0, 6.0/3.0, 8.0/4.0})]
public void IntegrateTest(double[] x, double[] expected)
{
var p = new Polynomial(x);
var p_res = p.Integrate();
if (expected == null)
{
Assert.IsNull(p_res);
return;
}
else
{
Assert.AreEqual(expected.Length, p_res.Degree, "length mismatch");
for (int k = 0; k < p_res.Degree; k++)
{
Assert.AreEqual(expected[k], p_res.Coeffs[k], "idx: " + k + " mismatch");
}
}
}
[Test]
public void AddTest()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
var msg = String.Format("At i={0}, j={1}", i, j);
var n = Math.Max(i, j) + 1;
var tgt = new double[n];
tgt[i] += 1;
tgt[j] += 1;
var c1 = new double[i + 1];
var c2 = new double[j + 1];
c1[i] = 1.0;
c2[j] = 1.0;
var p1 = new Polynomial(c1);
var p2 = new Polynomial(c2);
var p_res = Polynomial.Add(p1, p2);
var p_tar = new Polynomial(tgt);
p_res.Trim();
p_tar.Trim();
Assert.AreEqual(p_tar.Degree, p_res.Degree, "length mismatch");
for (int k = 0; k < p_res.Degree; k++)
{
Assert.AreEqual(p_tar.Coeffs[k], p_res.Coeffs[k], msg);
}
}
}
}
[Test]
public void SubstractTest()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
var msg = String.Format("At i={0}, j={1}", i, j);
var n = Math.Max(i, j) + 1;
var tgt = new double[n];
tgt[i] += 1;
tgt[j] -= 1;
var c1 = new double[i + 1];
var c2 = new double[j + 1];
c1[i] = 1.0;
c2[j] = 1.0;
var p1 = new Polynomial(c1);
var p2 = new Polynomial(c2);
var p_res = Polynomial.Substract(p1, p2);
var p_tar = new Polynomial(tgt);
p_res.Trim();
p_tar.Trim();
Assert.AreEqual(p_tar.Degree, p_res.Degree, "length mismatch");
for (int k = 0; k < p_res.Degree; k++)
{
Assert.AreEqual(p_tar.Coeffs[k], p_res.Coeffs[k], msg);
}
}
}
}
[Test]
public void MultiplyTest()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
var msg = String.Format("At i={0}, j={1}", i, j);
var n = i + j + 1;
var tgt = new double[n];
tgt[i + j] += 1;
var c1 = new double[i + 1];
var c2 = new double[j + 1];
c1[i] = 1.0;
c2[j] = 1.0;
var p1 = new Polynomial(c1);
var p2 = new Polynomial(c2);
var p_res = p1 * p2;
var p_tar = new Polynomial(tgt);
p_res.Trim();
p_tar.Trim();
Assert.AreEqual(p_tar.Degree, p_res.Degree, "length mismatch");
for (int k = 0; k < p_res.Degree; k++)
{
Assert.AreEqual(p_tar.Coeffs[k], p_res.Coeffs[k], msg);
}
}
}
}
[TestCase(new double[] { 5, 4, 0 }, "5 + 4x^1")]
[TestCase(new double[] { 0, 0, 0 }, "0")]
[TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x^1 + 3x^2 + 0x^3 + 2x^4")]
[TestCase(new double[] { 0, 0, 8, 0, 0 }, "0 + 0x^1 + 8x^2")]
[TestCase(new double[] { 0, 4, 3, 0, 0 }, "0 + 4x^1 + 3x^2")]
public void TrimTest(double[] x, string expected)
{
var p = new Polynomial(x);
p.Trim();
Assert.AreEqual(expected, p.ToString());
}
public void DivideLongTestScalar(Tuple<double[], double> inVals, Tuple<double[], double> expectedVals)
{
var p1 = new Polynomial(1.0d);
var p2 = new Polynomial(new double[0]);
var tpl = Polynomial.DivideLong(p1, p2);
}
[Test]
public void DivideLongTestWrongInputs()
{
Assert.Throws(typeof(ArgumentOutOfRangeException), () =>
{
var p1 = new Polynomial(1.0d);
var p2 = new Polynomial(new double[0]);
var tpl = Polynomial.DivideLong(p1, p2);
});
Assert.Throws(typeof(ArgumentOutOfRangeException), () =>
{
var p1 = new Polynomial(1.0d);
var p2 = new Polynomial(new double[0]);
var tpl = Polynomial.DivideLong(p2, p1);
});
Assert.Throws(typeof(ArgumentOutOfRangeException), () =>
{
var p1 = new Polynomial(new double[0]);
var p2 = new Polynomial(new double[0]);
var tpl = Polynomial.DivideLong(p2, p1);
});
Assert.Throws(typeof(DivideByZeroException), () =>
{
var p1 = new Polynomial(1.0d);
var p2 = new Polynomial(0.0d);
var tpl = Polynomial.DivideLong(p1, p2);
});
}
[Test]
public void DivideLongTest()
{
var p11 = new Polynomial(2.0d);
var p21 = new Polynomial(2.0d);
var tpl1 = Polynomial.DivideLong(p11, p21);
testEqual(new double[] { 1.0 }, tpl1.Item1);
testEqual(new double[] { 0.0 }, tpl1.Item2);
var p12 = new Polynomial(new double[] { 2.0d, 2.0d });
var p22 = new Polynomial(2.0d);
var tpl2 = Polynomial.DivideLong(p12, p22);
testEqual(new double[] { 1.0, 1.0 }, tpl2.Item1);
testEqual(new double[] { 0.0 }, tpl2.Item2);
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
var msg = String.Format("At i={0}, j={1}", i, j);
var ci = new double[i + 2];
var cj = new double[j + 2];
ci[ci.Length - 1] = 2;
ci[ci.Length - 2] = 1;
cj[cj.Length - 1] = 2;
cj[cj.Length - 2] = 1;
var pi = new Polynomial(ci);
var pj = new Polynomial(cj);
var tgt = Polynomial.Add(pi, pj);
var tpl3 = Polynomial.DivideLong(tgt, pi);
var pquo = tpl3.Item1;
var prem = tpl3.Item2;
var pres = (pquo * pi) + prem;
pres.Trim();
testEqual(pres, tgt, msg);
}
}
}
[Test]
public void GetRootsTest()
{
var tol = 1e-14;
var p1 = new Polynomial(1.0);
var r = p1.GetRoots();
Assert.AreEqual(1, r.Length, "length mismatch");
Assert.AreEqual(1.0, r.FirstOrDefault().Real);
var p2 = new Polynomial(new double[] { 1, 2 });
var r2 = p2.GetRoots();
Assert.AreEqual(1, r2.Length, "length mismatch");
Assert.AreEqual(-0.5, r2.FirstOrDefault().Real, tol);
// T.G: the following expected values were generated using
// numpys np.roots(x) method
// which is equivalent to np.polynomial.polynomial.polyroots
var x_2 = new double[] { -1.0, 1.0 };
var expected_2 = new List<Complex>();
expected_2.Add(new Complex(1.0, 0.0));
testEqual(x_2, expected_2);
var x_3 = new double[] { -1.0, 0.0, 1.0 };
var expected_3 = new List<Complex>();
expected_3.Add(new Complex(1.0, 0.0));
expected_3.Add(new Complex(-1.0, 0.0));
testEqual(x_3, expected_3);
var x_4 = new double[] { -1.0, -0.33333333333333337, 0.33333333333333326, 1.0 };
var expected_4 = new List<Complex>();
expected_4.Add(new Complex(0.9999999999999996, 0.0));
expected_4.Add(new Complex(-0.6666666666666666, 0.7453559924999296));
expected_4.Add(new Complex(-0.6666666666666666, -0.7453559924999296));
testEqual(x_4, expected_4);
}
private void testEqual(double[] x, List<Complex> eIn)
{
var tol = 1e-10;
var r0 = new Polynomial(x).GetRoots().ToList();
var e = eIn.OrderBy(v => v.Real).ToArray();
var r = r0.OrderBy(v => v.Real).ToArray();
Assert.IsNotNull(r);
Assert.AreEqual(e.Length, r.Length, "length mismatch");
for (int k = 0; k < r.Length; k++)
{
var msg = String.Format("At k={0}", k);
Assert.AreEqual(e[k].Real, r[k].Real, tol, msg);
Assert.AreEqual(e[k].Imaginary, r[k].Imaginary, tol, msg);
}
}
private void testEqual(double[] p_tar, double[] p_res, string msg = null)
{
Assert.AreEqual(p_tar.Length, p_res.Length, "length mismatch");
for (int k = 0; k < p_res.Length; k++)
{
Assert.AreEqual(p_tar[k], p_res[k], msg);
}
}
private void testEqual(double[] p_tar, Polynomial p_res, string msg = null)
{
Assert.AreEqual(p_tar.Length, p_res.Degree, "length mismatch");
for (int k = 0; k < p_res.Degree; k++)
{
Assert.AreEqual(p_tar[k], p_res.Coeffs[k], msg);
}
}
private void testEqual(Polynomial p_tar, Polynomial p_res, string msg = null)
{
Assert.AreEqual(p_tar.Degree, p_res.Degree, "length mismatch");
for (int k = 0; k < p_res.Degree; k++)
{
Assert.AreEqual(p_tar.Coeffs[k], p_res.Coeffs[k], msg);
}
}
}
}

9
src/Numerics/FindRoots.cs

@ -110,6 +110,15 @@ namespace MathNet.Numerics
{
return RootFinding.Cubic.Roots(d, c, b, a);
}
/// <summary>
/// Find all roots of a polynomial by calculating the characteristic polynomial of the companion matrix
/// </summary>
/// <param name="poly">the values for the polynomial in ascending order e.G new double[] {5, 0, 2} = "5 + 0 x^1 + 2 x^2"</param>
/// <returns>the roots of the polynomial</returns>
public static Complex[] Polynomial(double[] poly)
{
return new Polynomial(poly).GetRoots();
}
/// <summary>
/// Find all roots of the Chebychev polynomial of the first kind.

696
src/Numerics/Polynomial.cs

@ -0,0 +1,696 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Numerics;
using MathNet.Numerics;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.Statistics;
using MathNet.Numerics.IntegralTransforms;
using MathNet.Numerics.LinearRegression;
using MathNet.Numerics.LinearAlgebra.Factorization;
namespace MathNet.Numerics
{
/// <summary>
/// a class handling REAL VALUED Polynomials, complex coefficients can not be handled (yet)
/// </summary>
public class Polynomial
{
/// <summary>
/// The coefficients of the polynomial in a
/// </summary>
public double[] Coeffs { get; set; }
/// <summary>
/// Only needed for the ToString method
/// </summary>
public string VarName = "x^";
/// <summary>
/// Length of Polynomial (max element + 1) e.G x^5 highest element, will give Length = 6
/// </summary>
public int Degree
{
get
{
return (Coeffs == null ? 0 : Coeffs.Length);
}
}
/// <summary>
/// constructor setting a Polynomial of size n containing only zeros
/// </summary>
/// <param name="n">size of Polynomial</param>
public Polynomial(int n)
{
if (n < 0)
{
throw new ArgumentOutOfRangeException("n must be postive");
}
Coeffs = new double[n];
}
/// <summary>
/// make Polynomial: e.G 3.0 = 3.0 + 0 x^1 + 0 x^2
/// </summary>
/// <param name="coeff">just the "x^0" part</param>
public Polynomial(double coeff)
{
this.Coeffs = new double[1];
Coeffs[0] = coeff;
}
/// <summary>
/// make Polynomial: e.G new double[] {5, 0, 2} = "5 + 0 x^1 + 2 x^2"
/// </summary>
/// <param name="coeffs"> Polynomial coefficiens as enumerable</param>
public Polynomial(IEnumerable<double> coeffs)
{
if (coeffs == null)
{
throw new ArgumentNullException("coeffs");
}
this.Coeffs = coeffs.ToArray();
}
/// <summary>
/// make Polynomial: e.G new double[] {5, 0, 2} = "5 + 0 x^1 + 2 x^2"
/// </summary>
/// <param name="coeffs"> Polynomial coefficiens as array</param>
public Polynomial(double[] coeffs)
{
if (coeffs == null)
{
throw new ArgumentNullException("coeffs");
}
this.Coeffs = new double[coeffs.Length];
Array.Copy(coeffs, this.Coeffs, coeffs.Length);
}
/// <summary>
/// remove all trailing zeros, e.G before: "3 + 2 x^1 + 0 x^2" after: "3 + 2 x^1"
/// </summary>
public void Trim()
{
if (Degree == 1)
return;
int i = Degree - 1;
while (i >= 0 && Coeffs[i] == 0.0)
i--;
if (i < 0)
Coeffs = new double[1] { 0.0 };
else if (i == 0)
Coeffs = new double[1] { Coeffs[0] };
else
{
var hold = new double[i+1];
Array.Copy(Coeffs, hold, i+1);
Coeffs = hold;
}
}
#region Data Interaction
/// <summary>
/// Least-Squares fitting the points (x,y) to a k-order polynomial y : x -> p0 + p1*x + p2*x^2 + ... + pk*x^k
/// </summary>
public static Polynomial Fit(double[] x, double[] y, int order, DirectRegressionMethod method = DirectRegressionMethod.QR)
{
var pArr = MathNet.Numerics.Fit.Polynomial(x, y, order, method);
return new Polynomial(pArr);
}
/// <summary>
/// Evaluate a polynomial at point x.
/// </summary>
/// <param name="z">The location where to evaluate the polynomial at.</param>
public double Evaluate(double z)
{
return MathNet.Numerics.Evaluate.Polynomial(z, Coeffs);
}
/// <summary>
/// Evaluate a polynomial at points z.
/// </summary>
/// <param name="z">The locations where to evaluate the polynomial at.</param>
public IEnumerable<double> Evaluate(IEnumerable<double> z)
{
var Lst = new List<double>();
foreach (var item in z)
{
Lst.Add(Evaluate(item));
}
return Lst;
}
#endregion
#region diff/int
public Polynomial Differentiate()
{
if (Coeffs.Length == 0)
{
return null;
}
var t = this.Clone() as Polynomial;
t.Trim();
var cNew = new double[t.Coeffs.Length - 1];
for (int i = 1; i < t.Coeffs.Length; i++)
{
cNew[i-1] = t.Coeffs[i] * i;
}
var p = new Polynomial(cNew);
p.Trim();
return p;
}
public Polynomial Integrate()
{
var t = this.Clone() as Polynomial;
t.Trim();
var cNew = new double[t.Coeffs.Length + 1];
for (int i = 1; i < cNew.Length; i++)
{
cNew[i] = t.Coeffs[i-1] / i;
}
var p = new Polynomial(cNew);
p.Trim();
return p;
}
#endregion
#region Operators
/// <summary>
/// multiplies a Polynomial by a Polynomial using convolution [ASINCO.libs.subfun.conv(a.Coeffs, b.Coeffs)]
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="b">right Polynomial</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial operator *( Polynomial a, Polynomial b)
{
var aa = a.Clone() as Polynomial;
var bb = b.Clone() as Polynomial;
// do not cut trailing zeros, since it may corrupt the outcom, if the array is of form 1 + x^-1 + x^-2 + x^-3
//a.Trim();
//b.Trim();
double[] ret = conv(aa.Coeffs, bb.Coeffs);
Polynomial ret_p = new Polynomial(ret);
//ret_p.Trim();
return (ret_p);
}
/// <summary>
/// multiplies a Polynomial by a scalar
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="k">scalar value</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial operator *( Polynomial a, double k)
{
var aa = a.Clone() as Polynomial;
for (int ii = 0; ii < aa.Coeffs.Length; ii++)
aa.Coeffs[ii] *= k;
return aa;
}
/// <summary>
/// adds a scalar to a Polynomial (to the x^0 element)
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="k">scalar value</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial operator +( Polynomial a, double k)
{
var aa = a.Clone() as Polynomial;
aa.Coeffs[0] += k;
return aa;
}
/// <summary>
/// substracs a scalar from a Polynomial (from the x^0 element)
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="k">scalar value</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial operator -( Polynomial a, double k)
{
var aa = a.Clone() as Polynomial;
a.Coeffs[0] -= k;
return aa;
}
/// <summary>
/// divide Polynomial by scalar value
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="k">scalar value</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial operator /( Polynomial a, double k)
{
var aa = a.Clone() as Polynomial;
for (int ii = 0; ii < aa.Coeffs.Length; ii++)
aa.Coeffs[ii] /= k;
return aa;
}
/// <summary>
/// Addition of two Polynomials (piecewise)
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="b">right Polynomial</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial operator +( Polynomial a, Polynomial b)
{
return Add(a, b);
}
/// <summary>
/// substraction of two Polynomials (piecewise)
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="b">right Polynomial</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial operator -( Polynomial a, Polynomial b)
{
return Substract(a, b);
}
/// <summary>
/// Calculates the complex roots of the Polynomial by eigenvalue decomposition
/// </summary>
/// <returns>a vector of complex numbers with the roots</returns>
public Complex[] GetRoots()
{
DenseMatrix A = this.GetEigValMatrix();
Complex[] c_vec;
if (A == null)
{
if (Coeffs.Length < 2)
{
var val = Coeffs.Length == 1 ? Coeffs[0] : Double.NaN;
c_vec = new Complex[1] { val };
}
else
c_vec = new Complex[1] { new Complex(-Coeffs[0] / Coeffs[1], 0) };
}
else
{
Evd<double> eigen = A.Evd(Symmetricity.Asymmetric);
c_vec = eigen.EigenValues.ToArray();
}
return c_vec;
}
/// <summary>
/// get the eigenvalue matrix A of this Polynomial such that eig(A) = roots of this Polynomial.
/// </summary>
/// <returns>Eigenvalue matrix A</returns>
/// <note>this matrix is similar to the companion matrix of this polynomial, in such a way, that it's transpose is the columnflip of the companion matrix</note>
public DenseMatrix GetEigValMatrix()
{
Polynomial pLoc = new Polynomial(this.Coeffs);
pLoc.Trim();
int n = pLoc.Coeffs.Length - 1;
if (n < 2)
return null;
double[] p = new double[n];
double a0 = pLoc.Coeffs[n];
for (int ii = n - 1; ii >= 0; ii--)
p[ii] = -pLoc.Coeffs[ii] / a0;
DenseMatrix A0 = DenseMatrix.CreateDiagonal(n - 1, n - 1, 1.0);
DenseMatrix A = new DenseMatrix(n);
A.SetSubMatrix(1, 0, A0);
A.SetRow(0, p.Reverse().ToArray());
return A;
}
/// <summary>
/// pointwise division of two Polynomials
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="b">right Polynomial</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial DividePointwise( Polynomial a, Polynomial b)
{
var aa = a.Clone() as Polynomial;
var bb = b.Clone() as Polynomial;
if (aa.Coeffs.Length != bb.Coeffs.Length)
mkSameLength(ref aa, ref bb);
int n = aa.Coeffs.Length;
double[] res = new double[aa.Coeffs.Length];
for (int ii = 0; ii < n; ii++)
{
res[ii] = aa.Coeffs[ii] / bb.Coeffs[ii];
}
Polynomial res_poly = new Polynomial(res);
return (res_poly);
}
/// <summary>
/// pointwise multiplication of two Polynomials
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="b">right Polynomial</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial MultiplyPointwise( Polynomial a, Polynomial b)
{
var aa = a.Clone() as Polynomial;
var bb = b.Clone() as Polynomial;
if (aa.Coeffs.Length != bb.Coeffs.Length)
mkSameLength(ref aa, ref bb);
int n = aa.Coeffs.Length;
double[] res = new double[aa.Coeffs.Length];
for (int ii = 0; ii < n; ii++)
{
res[ii] = aa.Coeffs[ii] * bb.Coeffs[ii];
}
Polynomial res_poly = new Polynomial(res);
return (res_poly);
}
/// <summary>
/// Addition of two Polynomials (piecewise)
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="b">right Polynomial</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial Add( Polynomial a, Polynomial b)
{
var aa = a.Clone() as Polynomial;
var bb = b.Clone() as Polynomial;
if (aa.Degree != bb.Degree)
mkSameLength(ref aa, ref bb);
int n = aa.Degree;
double[] res = new double[n];
for (int ii = 0; ii < n; ii++)
{
res[ii] = aa.Coeffs[ii] + bb.Coeffs[ii];
}
Polynomial res_poly = new Polynomial(res);
return (res_poly);
}
/// <summary>
/// substraction of two Polynomials (piecewise)
/// </summary>
/// <param name="a">left Polynomial</param>
/// <param name="b">right Polynomial</param>
/// <returns>resulting Polynomial</returns>
public static Polynomial Substract( Polynomial a, Polynomial b)
{
var aa = a.Clone() as Polynomial;
var bb = b.Clone() as Polynomial;
if (aa.Degree != bb.Degree)
mkSameLength(ref aa, ref bb);
int n = aa.Degree;
double[] res = new double[n];
for (int ii = 0; ii < n; ii++)
{
res[ii] = aa.Coeffs[ii] - bb.Coeffs[ii];
}
Polynomial res_poly = new Polynomial(res);
return (res_poly);
}
/// <summary>
/// Division of two polynomials returning the quotient-with-remainder of the two polynomials given
/// </summary>
/// <param name="a">left polynomial</param>
/// <param name="b">right polynomial</param>
/// <returns>a tuple holding quotient in first and remainder in second</returns>
public static Tuple<Polynomial, Polynomial> DivideLong(Polynomial a, Polynomial b)
{
if (a == null)
throw new ArgumentNullException("a");
if (b == null)
throw new ArgumentNullException("b");
if (a.Degree <= 0)
throw new ArgumentOutOfRangeException("a Degree must be greater than zero");
if (b.Degree <= 0)
throw new ArgumentOutOfRangeException("b Degree must be greater than zero");
if (b.Coeffs[b.Degree-1] == 0)
throw new DivideByZeroException("b polynomial ends with zero");
var c1 = a.Coeffs.ToArray();
var c2 = b.Coeffs.ToArray();
var n1 = c1.Length;
var n2 = c2.Length;
double[] quo = null;
double[] rem = null;
if (n2 == 1) // division by scalar
{
var fact = c2[0];
quo = new double[n1];
for (int i = 0; i < n1; i++)
quo[i] = c1[i] / fact;
rem = new double[] { 0 };
}
else if(n1 < n2) // denominator degree higher than nominator degree
{
// quotient always be 0 and return c1 as remainder
quo = new double[] { 0 };
rem = c1.ToArray();
}
else
{
var dn = n1 - n2;
var scl = c2[n2 - 1];
var c22 = new double[n2 - 1];
for (int ii = 0; ii < c22.Length; ii++)
c22[ii] = c2[ii] / scl;
int i = dn;
int j = n1 - 1;
while (i >= 0)
{
var v = c1[j];
var vals = new double[j - i];
for (int k = i; k < j; k++)
c1[k] -= c22[k-i] * v;
i--;
j--;
}
var j1 = j + 1;
var l1 = n1 - j1;
rem = new double[j1];
quo = new double[l1];
for (int k = 0; k < l1; k++)
quo[k] = c1[k + j1] / scl;
for (int k = 0; k < j1; k++)
rem[k] = c1[k];
}
if (rem == null)
throw new NullReferenceException("resulting remainder was null");
if (quo == null)
throw new NullReferenceException("resulting quotient was null");
// output mapping
var pQuo = new Polynomial(quo);
var pRem = new Polynomial(rem);
pRem.Trim();
pQuo.Trim();
return new Tuple<Polynomial, Polynomial>(pQuo, pRem);
}
/// <summary>
/// Division of two polynomials returning the quotient-with-remainder of the two polynomials given
/// </summary>
/// <param name="b">right polynomial</param>
/// <returns>a tuple holding quotient in first and remainder in second</returns>
public Tuple<Polynomial, Polynomial> DivideLong(Polynomial b)
{
return DivideLong(this, b);
}
#endregion
#region Displaying
/// <summary>
/// "0.00 x^3 + 0.00 x^2 + 0.00 x^1 + 0.00" like display of this Polynomial
/// </summary>
/// <returns>string in displayed format</returns>
public override string ToString()
{
return ToString(highestFirst:false);
}
/// <summary>
/// "0.00 x^3 + 0.00 x^2 + 0.00 x^1 + 0.00" like display of this Polynomial
/// </summary>
/// <returns>string in displayed format</returns>
public string ToString(bool highestFirst)
{
string strLoc = "";
if (this.Coeffs == null)
{
return "null";
}
if (this.Coeffs.Length == 0)
{
return "";
}
if (!highestFirst)
{
for (int ii = 0; ii < Coeffs.Length; ii++)
{
if (ii == 0 && Coeffs.Length == 1)
strLoc += String.Format("{0}", this.Coeffs[ii], VarName, ii);
else if(ii == 0)
strLoc += String.Format("{0} + ", this.Coeffs[ii], VarName, ii);
else if (ii == Coeffs.Length - 1)
strLoc += String.Format("{0}{1}{2}", this.Coeffs[ii], VarName, ii);
else
strLoc += String.Format("{0}{1}{2} + ", this.Coeffs[ii], VarName, ii);
}
}
else
{
for (int ii = Coeffs.Length - 1; ii >= 0; ii--)
{
if (ii == 0)
strLoc += this.Coeffs[ii].ToString();
else
strLoc += String.Format("{0}{1}{2} + ", this.Coeffs[ii], VarName, ii);
}
}
return strLoc;
}
#endregion
#region Interfacing
/// <summary>
/// This method returns the coefficcients of the Polynomial as an array the "IsFlipped" property,
/// which is set during construction is taken into account automatically.
/// </summary>
/// <returns>the coefficcients of the Polynomial as an array</returns>
public double[] ToArray()
{
return (Coeffs.ToArray());
}
#endregion
#region Helpers
private static void mkSameLength(ref Polynomial a, ref Polynomial b)
{
double[] aHold = new double[a.Coeffs.Length];
double[] bHold = new double[b.Coeffs.Length];
Array.Copy(a.Coeffs, aHold, a.Coeffs.Length);
Array.Copy(b.Coeffs, bHold, b.Coeffs.Length);
if (a.Coeffs.Length < b.Coeffs.Length)
{
a.Coeffs = new double[b.Coeffs.Length];
b.Coeffs = new double[b.Coeffs.Length];
Array.Copy(aHold, a.Coeffs, aHold.Length);
Array.Copy(bHold, b.Coeffs, bHold.Length);
}
else
{
a.Coeffs = new double[a.Coeffs.Length];
b.Coeffs = new double[a.Coeffs.Length];
Array.Copy(aHold, a.Coeffs, aHold.Length);
Array.Copy(bHold, b.Coeffs, bHold.Length);
}
}
/// <summary>
/// (full) convolution of two arrays
/// </summary>
/// <param name="a">left vector</param>
/// <param name="b">right vector</param>
/// <returns>convolution of a and b as vector</returns>
private static double[] conv(double[] a, double[] b)
{
double[] ret = new double[a.Length + b.Length];
for (int i = 0; i < a.Length; i++)
{
for (int j = 0; j < b.Length; j++)
{
ret[i + j] += a[i] * b[j];
}
}
return ret;
}
public object Clone()
{
return new Polynomial(this.Coeffs);
}
#endregion
}
}
Loading…
Cancel
Save