/// <summary> /// This function computes the the multiplication of the transpose of the trajectory matrix H by an arbitrary vector v, i.e. H' * v. /// Since the trajectory matrix is a Hankel matrix, using the Discrete Fourier Transform, /// the multiplication is carried out in O(N.log(N)) instead of O(N^2), wheere N is the series length. /// For details, refer to Algorithm 3 in http://arxiv.org/pdf/0911.4498.pdf. /// </summary> /// <param name="vector">The input vector</param> /// <param name="result">The output vector allocated by the caller</param> /// <param name="add">Whether the multiplication result should be added to the current value in result</param> /// <param name="srcIndex">The starting index for the vector argument</param> /// <param name="dstIndex">The starting index for the result</param> private void FftMultiplyTranspose(Single[] vector, Single[] result, bool add = false, int srcIndex = 0, int dstIndex = 0) { _ectx.Assert(srcIndex >= 0); _ectx.Assert(dstIndex >= 0); _ectx.Assert(Utils.Size(vector) >= _windowSize + srcIndex); _ectx.Assert(Utils.Size(result) >= _k + dstIndex); int i; // Computing the FFT of the trajectory matrix if (!_isSeriesFftCached) { CacheInputSeriesFft(); } // Computing the FFT of the input vector for (i = 0; i < _k - 1; ++i) { _inputRe[i] = 0; } for (i = _k - 1; i < _seriesLength; ++i) { _inputRe[i] = vector[_seriesLength - i - 1 + srcIndex]; } FftUtils.ComputeForwardFft(_inputRe, _allZerosIm, _outputRe, _outputIm, _inputRe.Length); // Computing the element-by-element product in the Fourier space double re; double im; for (i = 0; i < _seriesLength; ++i) { re = _outputRe[i]; im = _outputIm[i]; _outputRe[i] = _cachedSeriesFftRe[i] * re - _cachedSeriesFftIm[i] * im; _outputIm[i] = _cachedSeriesFftRe[i] * im + _cachedSeriesFftIm[i] * re; } // Computing the inverse FFT of the result FftUtils.ComputeBackwardFft(_outputRe, _outputIm, _outputRe, _outputIm, _inputRe.Length); // Generating the output if (add) { for (i = 0; i < _k; ++i) { result[i + dstIndex] += RoundUpToReal(_outputRe[_windowSize - 1 + i], _outputIm[_windowSize - 1 + i]); } } else { for (i = 0; i < _k; ++i) { result[i + dstIndex] = RoundUpToReal(_outputRe[_windowSize - 1 + i], _outputIm[_windowSize - 1 + i]); } } }
/// <summary> /// This function computes the efficient Hankelization of the matrix sigma * u * v' using Fast Fourier Transform in in O((L + K) * log(L + K)). /// For details, refer to Algorithm 4 in http://arxiv.org/pdf/0911.4498.pdf. /// </summary> /// <param name="u">The u vector</param> /// <param name="v">The v vector</param> /// <param name="sigma">The scalar coefficient</param> /// <param name="result">The output series</param> /// <param name="add">Whether the hankelization result should be added to the current value in result</param> /// <param name="uIndex">The starting index for the u vector argument</param> /// <param name="vIndex">The starting index for the v vector argument</param> /// <param name="dstIndex">The starting index for the result</param> /// <param name="start">The staring index of the series to be reconstructed (by default zero)</param> /// <param name="end">The ending index of the series to be reconstructed (by default series length)</param> private void FftRankOneHankelization(Single[] u, Single[] v, Single sigma, Single[] result, bool add = false, int uIndex = 0, int vIndex = 0, int dstIndex = 0, int?start = null, int?end = null) { int s; int e; int us; int ue; int vs; int ve; int i; s = start ?? 0; e = end ?? _seriesLength - 1; ComputeBoundryIndices(s, e, out us, out ue, out vs, out ve); _ectx.Assert(0 <= ue && ue < _windowSize); _ectx.Assert(0 <= us && us <= ue); _ectx.Assert(0 <= ve && ve < _k); _ectx.Assert(0 <= vs && vs <= ve); var len = e - s + 1; _ectx.Assert(uIndex >= 0); _ectx.Assert(vIndex >= 0); _ectx.Assert(dstIndex >= 0); _ectx.Assert(Utils.Size(u) >= _windowSize + uIndex); _ectx.Assert(Utils.Size(v) >= _k + vIndex); _ectx.Assert(Utils.Size(result) >= len + dstIndex); _ectx.Assert(!Single.IsNaN(sigma)); _ectx.Assert(!Single.IsInfinity(sigma)); if (!_isSeriesFftCached) { CacheInputSeriesFft(); } // Computing the FFT of u for (i = us; i <= ue; ++i) { _inputRe[i - us] = u[i + uIndex]; } for (i = ue + 1; i < len + us; ++i) { _inputRe[i - us] = 0; } FftUtils.ComputeForwardFft(_inputRe, _allZerosIm, _outputRe, _outputIm, len); // Computing the FFT of v for (i = vs; i <= ve; ++i) { _inputRe[i - vs] = v[i + vIndex]; } for (i = ve + 1; i < len + vs; ++i) { _inputRe[i - vs] = 0; } FftUtils.ComputeForwardFft(_inputRe, _allZerosIm, _inputRe, _allZerosIm, len); // Computing the element-by-element product in the Fourier space double re; double im; for (i = 0; i < len; ++i) { re = _outputRe[i]; im = _outputIm[i]; _outputRe[i] = _inputRe[i] * re - _allZerosIm[i] * im; _outputIm[i] = _inputRe[i] * im + _allZerosIm[i] * re; } // Setting _allZerosIm to 0's again for (i = 0; i < _seriesLength; ++i) { _allZerosIm[i] = 0; } // Computing the inverse FFT of the result FftUtils.ComputeBackwardFft(_outputRe, _outputIm, _outputRe, _outputIm, len); // Generating the output int a = Math.Min(ue - us + 1, ve - vs + 1); if (add) { for (i = 0; i < a; ++i) { result[i + dstIndex] += RoundUpToReal(_outputRe[i], _outputIm[i], sigma / (i + 1)); } for (i = a; i < len - a + 1; ++i) { result[i + dstIndex] += RoundUpToReal(_outputRe[i], _outputIm[i], sigma / a); } for (i = len - a + 1; i < len; ++i) { result[i + dstIndex] += RoundUpToReal(_outputRe[i], _outputIm[i], sigma / (len - i)); } } else { for (i = 0; i < a; ++i) { result[i + dstIndex] = RoundUpToReal(_outputRe[i], _outputIm[i], sigma / (i + 1)); } for (i = a; i < len - a + 1; ++i) { result[i + dstIndex] = RoundUpToReal(_outputRe[i], _outputIm[i], sigma / a); } for (i = len - a + 1; i < len; ++i) { result[i + dstIndex] = RoundUpToReal(_outputRe[i], _outputIm[i], sigma / (len - i)); } } }