00001 using System; 00002 00003 namespace FFT 00004 { 00005 /// 00006 /// Convolution based on polynomial evaluation and interpolation. 00007 /// 00008 /// The vectors \f$ \mathbf{a} = [a_0,\ldots,a_{n-1}] \f$ and 00009 /// \f$ \mathbf{b} = [b_0,\ldots,b_{m-1}] \f$ 00010 /// are treated as cooeficients of two polynomials: 00011 /// \f$ A(x) = a_{n-1}*x^{n-1} + \ldots + a_1*x + a_0 \f$ and 00012 /// \f$ B(x) = b_{m-1}*x^{m-1} + \ldots + b_1*x + b_0 \f$. 00013 /// The convolution is calculated in the following way 00014 /// (#Convolution(Complex[], Complex[])): 00015 /// -# generate the roots of the \f$(n+m)\f$-th Chebyshev polynomial 00016 /// of the first kind (#GenerateDomain(Complex[], Complex[])) 00017 /// \f[ \mathbf{x}=[x_0,\ldots,x_{n+m-1}], \quad\text{where}\quad x_k=\frac{\cos[(2k+1)\pi]}{2(n+m-1)+2}; \f] 00018 /// -# evaluate the two polynomials on the generated domain 00019 /// (#Evaluate(Complex[], Complex[])) 00020 /// \f[ \mathbf{y}^A = [A(x_0),\ldots,A(x_{n+m-1})]\quad\text{and}\quad\mathbf{y}^B = [B(x_0),\ldots,B(x_{n+m-1})] \f] 00021 /// -# pointwise multiply the values (#PointwiseMultiply(Complex[], Complex[])) 00022 /// \f[ \mathbf{y} = [A(x_0)*B(x_0),\ldots,A(x_{n+m-1})*B(x_{n+m-1})] \f] 00023 /// -# use \f$ \mathbf{x} \f$ and \f$ \mathbf{y} \f$ to interpolate a polynomial 00024 /// (#Interpolate(Complex[], Complex[])) 00025 /// \f[ C(x) = c_{n+m-1}* x^{m+n-1} + \ldots + c_1 * x + c_0; \f] 00026 /// 00027 /// The convolution of \f$ \mathbf{a} \f$ and \f$ \mathbf{b} \f$ is 00028 /// \f$ \mathbf{c}=[a_0,\ldots,c_{n+m-1}] \f$. 00029 /// 00030 public class PolynomialConvolution : IConvolution 00031 { 00032 /// 00033 /// Computes the convolution. 00034 /// 00035 public virtual Complex[] Convolution(Complex[] a, Complex[] b) 00036 { 00037 Complex[] x = GenerateDomain(a.Length + b.Length - 1); 00038 Complex[] ya = Evaluate(a,x); 00039 Complex[] yb = Evaluate(b,x); 00040 Complex[] y = PointwiseMultiply(ya,yb); 00041 Complex[] c = Interpolate(x,y); 00042 00043 return c; 00044 } 00045 00046 /// 00047 /// Generates domain for evaluation and interpolation. 00048 /// 00049 /// Uses the roots of the Chebyshev polynomial of the first kind of degree <tt>n</tt> 00050 /// on [0,1]. 00051 /// 00052 /// \param n the degree of the Chebyshev polynomial 00053 /// \returns roots of canonical Chebyshev polynomial of first kind of degree 00054 /// <tt>n</tt>. 00055 /// 00056 protected internal Complex[] GenerateDomain(int n) 00057 { 00058 Complex[] res = new Complex[n]; 00059 00060 //Roots of the Chebyshev Polynoials of the first kind 00061 for (int i = 0; i < n; i++) { 00062 res[i] = Math.Cos((2*i+1)*Math.PI/(2*n+2)); 00063 } 00064 00065 return res; 00066 } 00067 00068 /// 00069 /// Newton Interpolation. 00070 /// 00071 /// Calculates the coefficients of a polynomial that takes values <tt>y</tt> 00072 /// at points <tt>x</tt>. 00073 /// 00074 protected internal Complex[] Interpolate(Complex[] x, Complex[] y) 00075 { 00076 if (x.Length != y.Length) { 00077 throw new ArgumentException("The input vectors are of different length."); 00078 } 00079 00080 int n = x.Length; 00081 00082 Complex[] a; // resulting polynomial 00083 Complex[] d; // divided differences (denoted here [y_i,...,y_{i+k}]) 00084 Complex[] p; // polynomial p(x)=(x-x_0)*(x-x_1)*...*(x-x_k) 00085 00086 // Initial values: 00087 a = new Complex[n]; // a(x) = 0 00088 d = (Complex[]) y.Clone(); // d[i] = [y_i] 00089 p = new Complex[n]; p[0] = 1; // p(x) = 1 00090 00091 00092 for (int k = 0; k < n; k++) { 00093 // Add next Newton combination to the solution 00094 // a(x) = a(x) + [y_0,...,y_k]*p(x) 00095 for (int i = 0; i < n; i++) { 00096 a[i] = a[i] + d[0] * p[i]; 00097 } 00098 00099 // Calculate divided differences for the next step 00100 // Before: d[i] = [y_i,...,y_{i+k}] 00101 // Note: if k = n, no iteration is preformed. 00102 for (int i = 0; i < n-(k+1); i++) { 00103 d[i] = (d[i+1] - d[i])/(x[k+i+1] - x[i]); 00104 } 00105 // After: d[i] = [y_i,...,y_{i+k+1}] 00106 00107 // Calculate the p for next iteration. 00108 // Multiply p(x) by (x-x_k) 00109 // Before: p(x) = (x-x_0)*...*(x-x_k) 00110 // Note: Execution during the last iteration of the main loop is redundant. 00111 Complex c = 0; //carry value 00112 for (int i = 0; i < n; i++) { 00113 Complex tmp = p[i]; 00114 p[i] = - x[k]*p[i] + c; 00115 c = tmp; 00116 } 00117 // After: p(x) = (x-x_0)*...*(x_{k+1}) 00118 } 00119 00120 return a; 00121 } 00122 00123 /// 00124 /// Evaluates a polynomial at a sequence of points. 00125 /// 00126 /// \param a the coefficients of the polynomial 00127 /// \param x the points of evaluation 00128 /// \returns vector of values the polynomial takes at each point in <tt>x</tt> 00129 /// 00130 protected internal Complex[] Evaluate(Complex[] a, Complex[] x) 00131 { 00132 Complex[] y = new Complex[x.Length]; 00133 for (int i = 0; i < x.Length; i++) { 00134 y[i] = 0; 00135 for (int j = a.Length - 1; j >= 0; j--) { 00136 y[i] *= x[i]; 00137 y[i] += a[j]; 00138 } 00139 } 00140 00141 return y; 00142 } 00143 00144 /// 00145 /// Pointwise multiplication of two vectors. 00146 /// 00147 protected internal Complex[] PointwiseMultiply(Complex[] a, Complex[] b) 00148 { 00149 if (a.Length != b.Length) { 00150 throw new ArgumentException("Both tables must have the same size."); 00151 } 00152 00153 Complex[] res = new Complex[a.Length]; 00154 00155 for (int i = 0; i < a.Length; i++) { 00156 res[i] = a[i] * b[i]; 00157 } 00158 00159 return res; 00160 } 00161 00162 /// 00163 /// \copybrief FFT::IConvolution::ToString() 00164 /// 00165 /// This method always returns <tt>"Pointwise"</tt>. 00166 public override string ToString() 00167 { 00168 return "Polynomial"; 00169 } 00170 00171 } //class PolynomialConvolution 00172 00173 } //namespace FFT