I have a slightly unusual problem, but I try to avoid re-encoding FFT.
In general, I want to know is: If I have an algorithm implemented for the type float, but it will work wherever a certain defined set of operations (such as complex numbers, which are also defined +, *...), what is the best way to use this algorithm for another type that supports these operations? In practice, this is difficult because, in general, numerical algorithms are written for speed, and not for generality.
In particular:
I work with values with a very high dynamic range, so I would like to store them in the log space (mainly to avoid underutilization).
I would like the FFT magazine of some series:
x = [1,2,3,4,5]
fft_x = [ log( x_val ) for x_val in fft(x) ]
Even this will lead to a significant decrease. I would like to store log values and use +instead *and logaddexpinstead +, etc.
My thought on how to do this is to implement a simple LogFloat class that defines these primitive operations (but works in the log space). Then I could just run the FFT code, allowing my registered values to be used.
class LogFloat:
def __init__(self, sign, log_val):
assert(float(sign) in (-1, 1))
self.sign = int(sign)
self.log_val = log_val
@staticmethod
def from_float(fval):
return LogFloat(sign(fval), log(abs(fval)))
def __imul__(self, lf):
self.sign *= lf.sign
self.log_val += lf.log_val
return self
def __idiv__(self, lf):
self.sign *= lf.sign
self.log_val -= lf.log_val
return self
def __iadd__(self, lf):
if self.sign == lf.sign:
self.log_val = logaddexp(self.log_val, lf.log_val)
else:
if self.log_val > lf.log_val:
self.log_val = log_sub(self.log_val, lf.log_val)
else:
self.log_val = log_sub(lf.log_val, self.log_val)
self.sign *= -1
return self
def __isub__(self, lf):
self.__iadd__(LogFloat(-1 * lf.sign, lf.log_val))
return self
def __pow__(self, lf):
if lf.log_val == -float('inf'):
return LogFloat.from_float(1.0)
lf_value = lf.sign * math.exp(lf.log_val)
if self.sign == -1:
return LogFloat(self.sign**int(lf_value), self.log_val * lf_value)
return LogFloat(self.sign, self.log_val * lf_value)
def __mul__(self, lf):
temp = LogFloat(self.sign, self.log_val)
temp *= lf
return temp
def __div__(self, lf):
temp = LogFloat(self.sign, self.log_val)
temp /= lf
return temp
def __add__(self, lf):
temp = LogFloat(self.sign, self.log_val)
temp += lf
return temp
def __sub__(self, lf):
temp = LogFloat(self.sign, self.log_val)
temp -= lf
return temp
def __str__(self):
result = str(self.sign * math.exp(self.log_val)) + '('
if self.sign == -1:
result += '-'
result += 'e^' + str(self.log_val) + ')'
return result
def __neg__(self):
return LogFloat(-self.sign, self.log_val)
def __radd__(self, val):
if val == 0:
return self
return self + val
Then the idea would be to create a list LogFloats and then use it in FFT:
x_log_float = [ LogFloat.from_float(x_val) for x_val in x ]
fft_x_log_float = fft(x_log_float)
, FFT ( LogFloat, float, , . : , ( , "+", "-", "," /" ..).
, , .. . exmaple, , ( , ).
, , FFT , . , , , ...
, , - , ( N ^ 2 ).
.
* . , LogFloat, .
EDIT:
, LogFloat - ( LogFloat). - , @JFSebastian Python, ( DSP, Wikipedia).