问题
I have C++ FFT code (pasted below).
In C++, when I input in main(), RL_Input = {1,2,-3,4}, IM_Input = {-4,3,2,1}, I get the answer as RL_Output = {4, 6, -8, 2}, IM_Output = {2, -4, -6, -8}.
I want to call this C++ code from SWIFT. So, in SWIFT, I want to do something as follows:
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)
How do I do the above using the C++ code that I have (given below)?
//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:
If you would like to invoke that C++ code from Swift, you will need to bridge via Objective-C++. A simple search here on SO will reveal numerous posts on how to do that.
In this case we would want to minimize copying of the data as we glue C++/Objective-C++/Swift together in order to reduce negatively affecting performance. Array
of Double
s in Swift stores its data in contiguous storage, as Double
is not a class. The withUnsafeMutableBufferPointer
method of Array
appears to be a promising option for a solution. I would be careful, though, and test this approach on a simple test program first. I'll try to come up with something in the next few days, time permitting.
Please see documentation for Array
at https://developer.apple.com/documentation/swift/array. Another very useful resource is https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216-CH2-ID0, which you may have already seen searching this topic.
One thing to be careful about in general is that vector
storage in C++ can be relocated in memory if vector is resized, for example, in which case we would have to make extra copies of the data when bridging from Swift to C++. However, this C++ code doesn't seem to do anything that would move the underlying storage.
Update 10/25/2017: Using withUnsafeMutableBufferPointer
will require copying arrays in memory because it is problematic to create a vector
that directly, in-place, uses a given buffer. See How to cheaply assign C-style array to std::vector?.
However, since there is a C version of the library available, this becomes a piece of cake:
- Add
fft.h
andfft.c
to your Xcode project. - Import
fft.h
in your bridging header.
Then you can use the C code in Swift like this:
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)
}
}
}
Of course, this is just a bare-bones example. You can make it fancier, add error handling etc.
来源:https://stackoverflow.com/questions/46904905/use-c-fft-code-in-swift