代码之家  ›  专栏  ›  技术社区  ›  Saif

在SWIFT中使用C++FFT代码

  •  0
  • Saif  · 技术社区  · 7 年前

    在C++中,当我在main()中输入时,RL\u输入={1,2,-3,4},IM\u输入={4,3,2,1},我得到的答案是RL\u输出={4,6,-8,2},IM\u输出={2,-4,-6,-8}。

    我想从SWIFT中调用这个C++代码。因此,在SWIFT中,我想做以下事情:

       let (RL_Output, IM_Output) = Some_Swift_Function([1,2,-3,4], [-4,3,2,1]) // INPUT RL & IM
       print(RL_Output)
       print(IM_Output)
    
       // RL_Output = [4, 6, -8, 2]  //Answer (REAL)
       // IM_Output = [2, -4, -6, -8] //Answer (IMAG)
    

    如何使用我的C++代码(如下所示)实现上述功能?

        //FftRealPairTest.cpp
        #include <algorithm>
        #include <cmath>
        #include <cstdlib>
        #include <iomanip>
        #include <iostream>
        #include <random>
        #include <vector>
        #include "FftRealPair.hpp"
    
        using std::cout;
        using std::endl;
        using std::vector;
    
        int main() {
            vector<double> inputreal({1,2,-3,4});
    
            vector<double> inputimag({-4,3,2,1});
    
            vector<double> actualoutreal(inputreal);
    
            vector<double> actualoutimag(inputimag);
    
            Fft::transform(actualoutreal, actualoutimag);
    
            std::cout << "REAL:" << std::endl;
            for (int i = 0; i < inputimag.size(); ++i)
            {
                std::cout << actualoutreal[i] << std::endl;
            }
    
    
            std::cout << "IMAG" << std::endl;
            for (int i = 0; i < inputimag.size(); ++i)
            {
                std::cout << actualoutimag[i] << std::endl;
            }
            
        }
    
    
        /////////////////////////////////////////////////
    
        //FftRealPair.cpp
        /*
         * Free FFT and convolution (C++)
         *
         * Copyright (c) 2017 Project Nayuki. (MIT License)
         * https://www.nayuki.io/page/free-small-fft-in-multiple-languages
         *
         * 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.
         */
    
        #include <algorithm>
        #include <cmath>
        #include <cstddef>
        #include <cstdint>
        #include "FftRealPair.hpp"
    
        using std::size_t;
        using std::vector;
    
    
        // Private function prototypes
        static size_t reverseBits(size_t x, int n);
    
    
        void Fft::transform(vector<double> &real, vector<double> &imag) {
            size_t n = real.size();
            if (n != imag.size())
                throw "Mismatched lengths";
            if (n == 0)
                return;
            else if ((n & (n - 1)) == 0)  // Is power of 2
                transformRadix2(real, imag);
            else  // More complicated algorithm for arbitrary sizes
                transformBluestein(real, imag);
        }
    
    
        void Fft::inverseTransform(vector<double> &real, vector<double> &imag) {
            transform(imag, real);
        }
    
    
        void Fft::transformRadix2(vector<double> &real, vector<double> &imag) {
            // Length variables
            size_t n = real.size();
            if (n != imag.size())
                throw "Mismatched lengths";
            int levels = 0;  // Compute levels = floor(log2(n))
            for (size_t temp = n; temp > 1U; temp >>= 1)
                levels++;
            if (static_cast<size_t>(1U) << levels != n)
                throw "Length is not a power of 2";
    
            // Trignometric tables
            vector<double> cosTable(n / 2);
            vector<double> sinTable(n / 2);
            for (size_t i = 0; i < n / 2; i++) {
                cosTable[i] = std::cos(2 * M_PI * i / n);
                sinTable[i] = std::sin(2 * M_PI * i / n);
            }
    
            // Bit-reversed addressing permutation
            for (size_t i = 0; i < n; i++) {
                size_t j = reverseBits(i, levels);
                if (j > i) {
                    std::swap(real[i], real[j]);
                    std::swap(imag[i], imag[j]);
                }
            }
    
            // Cooley-Tukey decimation-in-time radix-2 FFT
            for (size_t size = 2; size <= n; size *= 2) {
                size_t halfsize = size / 2;
                size_t tablestep = n / size;
                for (size_t i = 0; i < n; i += size) {
                    for (size_t j = i, k = 0; j < i + halfsize; j++, k += tablestep) {
                        size_t l = j + halfsize;
                        double tpre =  real[l] * cosTable[k] + imag[l] * sinTable[k];
                        double tpim = -real[l] * sinTable[k] + imag[l] * cosTable[k];
                        real[l] = real[j] - tpre;
                        imag[l] = imag[j] - tpim;
                        real[j] += tpre;
                        imag[j] += tpim;
                    }
                }
                if (size == n)  // Prevent overflow in 'size *= 2'
                    break;
            }
        }
    
    
        void Fft::transformBluestein(vector<double> &real, vector<double> &imag) {
            // Find a power-of-2 convolution length m such that m >= n * 2 + 1
            size_t n = real.size();
            if (n != imag.size())
                throw "Mismatched lengths";
            size_t m = 1;
            while (m / 2 <= n) {
                if (m > SIZE_MAX / 2)
                    throw "Vector too large";
                m *= 2;
            }
    
            // Trignometric tables
            vector<double> cosTable(n), sinTable(n);
            for (size_t i = 0; i < n; i++) {
                unsigned long long temp = static_cast<unsigned long long>(i) * i;
                temp %= static_cast<unsigned long long>(n) * 2;
                double angle = M_PI * temp / n;
                // Less accurate alternative if long long is unavailable: double angle = M_PI * i * i / n;
                cosTable[i] = std::cos(angle);
                sinTable[i] = std::sin(angle);
            }
    
            // Temporary vectors and preprocessing
            vector<double> areal(m), aimag(m);
            for (size_t i = 0; i < n; i++) {
                areal[i] =  real[i] * cosTable[i] + imag[i] * sinTable[i];
                aimag[i] = -real[i] * sinTable[i] + imag[i] * cosTable[i];
            }
            vector<double> breal(m), bimag(m);
            breal[0] = cosTable[0];
            bimag[0] = sinTable[0];
            for (size_t i = 1; i < n; i++) {
                breal[i] = breal[m - i] = cosTable[i];
                bimag[i] = bimag[m - i] = sinTable[i];
            }
    
            // Convolution
            vector<double> creal(m), cimag(m);
            convolve(areal, aimag, breal, bimag, creal, cimag);
    
            // Postprocessing
            for (size_t i = 0; i < n; i++) {
                real[i] =  creal[i] * cosTable[i] + cimag[i] * sinTable[i];
                imag[i] = -creal[i] * sinTable[i] + cimag[i] * cosTable[i];
            }
        }
    
    
        void Fft::convolve(const vector<double> &x, const vector<double> &y, vector<double> &out) {
            size_t n = x.size();
            if (n != y.size() || n != out.size())
                throw "Mismatched lengths";
            vector<double> outimag(n);
            convolve(x, vector<double>(n), y, vector<double>(n), out, outimag);
        }
    
    
        void Fft::convolve(
                           const vector<double> &xreal, const vector<double> &ximag,
                           const vector<double> &yreal, const vector<double> &yimag,
                           vector<double> &outreal, vector<double> &outimag) {
    
            size_t n = xreal.size();
            if (n != ximag.size() || n != yreal.size() || n != yimag.size()
                || n != outreal.size() || n != outimag.size())
                throw "Mismatched lengths";
    
            vector<double> xr(xreal);
            vector<double> xi(ximag);
            vector<double> yr(yreal);
            vector<double> yi(yimag);
            transform(xr, xi);
            transform(yr, yi);
            
            for (size_t i = 0; i < n; i++) {
                double temp = xr[i] * yr[i] - xi[i] * yi[i];
                xi[i] = xi[i] * yr[i] + xr[i] * yi[i];
                xr[i] = temp;
            }
            inverseTransform(xr, xi);
            
            for (size_t i = 0; i < n; i++) {  // Scaling (because this FFT implementation omits it)
                outreal[i] = xr[i] / n;
                outimag[i] = xi[i] / n;
            }
        }
    
    
        static size_t reverseBits(size_t x, int n) {
            size_t result = 0;
            for (int i = 0; i < n; i++, x >>= 1)
                result = (result << 1) | (x & 1U);
            return result;
        }
    
    
    
        ////////////////////////////////////////////////////
    
    
        //FftRealPair.hpp
    
        /*
         * Free FFT and convolution (C++)
         *
         * Copyright (c) 2017 Project Nayuki. (MIT License)
         * https://www.nayuki.io/page/free-small-fft-in-multiple-languages
         *
         * 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.
         */
    
        #pragma once
    
        #include <vector>
    
    
        namespace Fft {
    
            /*
             * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
             * The vector can have any length. This is a wrapper function.
             */
            void transform(std::vector<double> &real, std::vector<double> &imag);
    
    
            /*
             * Computes the inverse discrete Fourier transform (IDFT) of the given complex vector, storing the result back into the vector.
             * The vector can have any length. This is a wrapper function. This transform does not perform scaling, so the inverse is not a true inverse.
             */
            void inverseTransform(std::vector<double> &real, std::vector<double> &imag);
    
    
    
    
    
            /*
             * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
             * The vector's length must be a power of 2. Uses the Cooley-Tukey decimation-in-time radix-2 algorithm.
             */
            void transformRadix2(std::vector<double> &real, std::vector<double> &imag);
    
    
            /*
             * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
             * The vector can have any length. This requires the convolution function, which in turn requires the radix-2 FFT function.
             * Uses Bluestein's chirp z-transform algorithm.
             */
            void transformBluestein(std::vector<double> &real, std::vector<double> &imag);
    
    
            /*
             * Computes the circular convolution of the given real vectors. Each vector's length must be the same.
             */
            void convolve(const std::vector<double> &x, const std::vector<double> &y, std::vector<double> &out);
    
    
            /*
             * Computes the circular convolution of the given complex vectors. Each vector's length must be the same.
             */
            void convolve(
                          const std::vector<double> &xreal, const std::vector<double> &ximag,
                          const std::vector<double> &yreal, const std::vector<double> &yimag,
                          std::vector<double> &outreal, std::vector<double> &outimag);
            
        }
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   Anatoli P    7 年前

    如果您想从Swift调用该C++代码,则需要通过Objective-C++进行桥接。在这里进行一个简单的搜索,就会发现许多关于如何做到这一点的帖子。

    在这种情况下,我们希望在将C++/Objective-C++/Swift粘合在一起时尽量减少数据复制,以减少对性能的负面影响。 Array 属于 Double Swift中的s将其数据存储在连续存储器中,如 双重的 withUnsafeMutableBufferPointer 大堆 似乎是一个很有希望的解决方案。不过,我会很小心,首先在一个简单的测试程序上测试这种方法。如果时间允许的话,我会试着在接下来的几天里想出一些办法。

    大堆 https://developer.apple.com/documentation/swift/array . 另一个非常有用的资源是 https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216-CH2-ID0 ,您可能已经在搜索此主题时看到了。

    总的来说,需要注意的一件事是 vector 例如,如果调整向量的大小,C++中的存储可以在内存中重新定位,在这种情况下,当从Swift桥接到C++时,我们必须制作额外的数据副本。然而,这段C++代码似乎不会移动底层存储。

    2017年10月25日更新:使用 WithUnsafemultableBufferpointer 将需要在内存中复制阵列,因为创建 矢量 直接使用给定缓冲区。看见 How to cheaply assign C-style array to std::vector? .

    然而,由于有C版本的库可用,这就变得小菜一碟了:

    • 添加 fft.h fft.c 到您的Xcode项目。
    • 进口 快速傅立叶变换。h 在桥接标题中。

    然后,您可以在Swift中使用C代码,如下所示:

    var dReal : [Double] = [1,2,-3,4]
    var dImg : [Double] = [-4,3,2,1]
    
    dReal.withUnsafeMutableBufferPointer { (real : inout UnsafeMutableBufferPointer<Double> ) in
        dImg.withUnsafeMutableBufferPointer { (img : inout UnsafeMutableBufferPointer<Double>) in
            if (real.count == img.count) {
                Fft_transform(real.baseAddress, img.baseAddress, real.count)
            }
        }
    }
    

    当然,这只是一个简单的例子。您可以使其更美观,添加错误处理等。