fuzzylogic
fuzzylogic copied to clipboard
Optional numba performance boost needed?
Well, preliminary timing tests show that fuzzylogic functions are up to 3 times faster than the membership functions in scikit-fuzzy (while having more functionality/safety!), but it's not easy to make numba happy.
from time import perf_counter_ns as time
from statistics import mean, stdev, median
import numpy as np
from numba import jit, njit
from random import randint, random
from fuzzylogic.functions import gauss
def timeit(func):
res = []
for _ in range(100000):
before = time()
func()
res.append(time() - before)
for f in (min, mean, median, stdev):
print(f(res))
return res
def gaussmf(x, mean, sigma):
"""
Gaussian fuzzy membership function.
Parameters
----------
x : 1d array or iterable
Independent variable.
mean : float
Gaussian parameter for center (mean) value.
sigma : float
Gaussian parameter for standard deviation.
Returns
-------
y : 1d array
Gaussian membership function for x.
"""
return np.exp(-((x - mean)**2.) / (2 * sigma**2.))
gauss_vars = [randint(0,50) for _ in range(100)]
@jit
def gauss_numba(c:float, b:float, *, c_m=1):
"""Defined by ae^(-b(x-x0)^2), a gaussian distribution.
Basically a triangular sigmoid function, it comes close to human perception.
vars
----
c_m (a)
defines the maximum y-value of the graph
b
defines the steepness
c (x0)
defines the symmetry center/peak of the graph
"""
assert 0 < c_m <= 1
assert 0 < b, "b must be greater than 0"
def f(x):
try:
o = (x - c)**2
except OverflowError:
return 0
return c_m * exp(-b * o)
return f
def test_gaussmf():
return [gaussmf(x, 10, 5) for x in gauss_vars]
def test_gauss():
g = gauss(10,5)
return [g(x) for x in gauss_vars]
def test_gauss_numba():
g = gauss_numba(10,5, 1)
return [g(x) for x in gauss_vars]
for x in (test_gauss_numba,):
print(x.__name__)
timeit(x)
fails with
Traceback (most recent call last):
File "F:\Dropbox (Privat)\code\justuse\tests\.tests\.test2.py", line 81, in <module>
timeit(x)
File "F:\Dropbox (Privat)\code\justuse\tests\.tests\.test2.py", line 16, in timeit
func()
File "F:\Dropbox (Privat)\code\justuse\tests\.tests\.test2.py", line 76, in test_gauss_numba
g = gauss_numba(10,5, 1)
File "G:\Python398\lib\site-packages\numba\core\dispatcher.py", line 468, in _compile_for_args
error_rewrite(e, 'typing')
File "G:\Python398\lib\site-packages\numba\core\dispatcher.py", line 409, in error_rewrite
raise e.with_traceback(None)
numba.core.errors.TypingError: Failed in object mode pipeline (step: convert make_function into JIT functions)
[1mCannot capture the non-constant value associated with variable 'b' in a function that will escape.
[1m
File ".test2.py", line 60:[0m
[1m
[1m def f(x):
[0m [1m^[0m[0m
[0m
Okay, turns out it is possible to njit the inner functions of those closures, preserving the nice behaviour of the outer functions, but numba can't work with try/except, so this is feasible:
def gauss_numba(c:float, b:float, *, c_m=1):
"""Defined by ae^(-b(x-x0)^2), a gaussian distribution.
Basically a triangular sigmoid function, it comes close to human perception.
vars
----
c_m (a)
defines the maximum y-value of the graph
b
defines the steepness
c (x0)
defines the symmetry center/peak of the graph
"""
assert 0 < c_m <= 1
assert 0 < b, "b must be greater than 0"
max_float = sqrt(sys.float_info.max)
@njit
def f(x):
# instead of try-except OverflowError
if (x-c) > max_float:
return 0
else:
o = (x - c)**2
return c_m * exp(-b * o)
return f
This function is now 3 times as fast as the original one. The original is 3 times as fast as the scikit-fuzzy equivalent.