"Custom function my_pow throws MethodError despite NaN handling" #641
-
DescriptionI'm trying to define a custom function Codeimport numpy as np
import sympy
from pysr import PySRRegressor,
default_pysr_params = dict(
populations=80,
model_selection="best",
)
# Generate some data
np.random.seed(0)
X = np.random.randn(100, 5)
y = np.sum(X**2, axis=1) + 0.1 * np.random.randn(100)
# Define a custom power function class
class my_pow(sympy.Function):
@classmethod
def eval(cls, x, n):
if x.is_negative:
return sympy.nan
return x ** n
# Learn equations
model_p = PySRRegressor(
niterations=60,
binary_operators=["+", "*","/", "-"],
# unary_operators=["my_pow(x::T, n::T) where T = (x >= zero(T)) ? x^clamp(n, 3, 9) : T(NaN)"],
# unary_operators=[ "my_pow(x::T, n::T) where T = abs(x)^clamp(n, 3, 9)"],
unary_operators=[
"my_pow(x, n) = x >= 0 ? x^(3 + 6 * (n - 0.5)^2) : convert(typeof(x), NaN)"
],
extra_sympy_mappings={"my_pow": lambda x, n: np.where(x >= 0, x ** (3 + 6 * (n - 0.5)**2), np.nan)},
parsimony = 6.4e-8,
**default_pysr_params,
)
model_p.fit(X2, y)
explanation :
I use Julia's ternary operator ?: to handle invalid inputs.
x >= 0 ? checks if x is non-negative.
If x is non-negative, we compute x^(3 + 6 * (n - 0.5)^2).
This is a more complex expression that ensures n is always between 3 and 9:
(n - 0.5)^2 maps any n to a value between 0 and 0.25.
6 * (n - 0.5)^2 maps this to a value between 0 and 6.
Adding 3 gives us a range of 3 to 9.
If x is negative, we return convert(typeof(x), NaN) to ensure type stability.
But I faced the following issue:
```julia
---------------------------------------------------------------------------
JuliaError Traceback (most recent call last)
[<ipython-input-26-d46dfbb57121>](https://localhost:8080/#) in <cell line: 24>()
22 )
23
---> 24 model_p.fit(X2, y)
2 frames
[/usr/local/lib/python3.10/dist-packages/juliacall/__init__.py](https://localhost:8080/#) in __call__(self, *args, **kwargs)
221 return ValueBase.__dir__(self) + self._jl_callmethod($(pyjl_methodnum(pyjlany_dir)))
222 def __call__(self, *args, **kwargs):
--> 223 return self._jl_callmethod($(pyjl_methodnum(pyjlany_call)), args, kwargs)
224 def __bool__(self):
225 return True
JuliaError: The operator `my_pow` is not well-defined over the real line, as it threw the error `MethodError` when evaluating the input -100.0. You can work around this by returning NaN for invalid inputs. For example, `safe_log(x::T) where {T} = x > 0 ? log(x) : T(NaN)`. I would appreciate it if any idea how to fix this issue or other alternatives. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
I think it's because you put my_pow in unary operators, but it is actually a binary operator. (I guess the error message for such an error should be better) Also your |
Beta Was this translation helpful? Give feedback.
I think it's because you put my_pow in unary operators, but it is actually a binary operator.
(I guess the error message for such an error should be better)
Also your
extra_sympy_mappings
can't use numpy functions – only sympy ones.