Spaces:
Sleeping
Sleeping
refactor: more robust parsing of operators
Browse files- pysr/julia_helpers.py +2 -2
- pysr/sr.py +18 -3
- pysr/test/test.py +10 -0
pysr/julia_helpers.py
CHANGED
|
@@ -41,8 +41,8 @@ def jl_array(x, dtype=None):
|
|
| 41 |
return jl_convert(jl.Array[dtype], x)
|
| 42 |
|
| 43 |
|
| 44 |
-
def jl_is_function(f):
|
| 45 |
-
return jl.seval("op -> op isa Function")(f)
|
| 46 |
|
| 47 |
|
| 48 |
def jl_serialize(obj: Any) -> NDArray[np.uint8]:
|
|
|
|
| 41 |
return jl_convert(jl.Array[dtype], x)
|
| 42 |
|
| 43 |
|
| 44 |
+
def jl_is_function(f) -> bool:
|
| 45 |
+
return cast(bool, jl.seval("op -> op isa Function")(f))
|
| 46 |
|
| 47 |
|
| 48 |
def jl_serialize(obj: Any) -> NDArray[np.uint8]:
|
pysr/sr.py
CHANGED
|
@@ -13,7 +13,7 @@ from datetime import datetime
|
|
| 13 |
from io import StringIO
|
| 14 |
from multiprocessing import cpu_count
|
| 15 |
from pathlib import Path
|
| 16 |
-
from typing import Callable, Dict, List, Literal, Optional, Tuple, Union, cast
|
| 17 |
|
| 18 |
import numpy as np
|
| 19 |
import pandas as pd
|
|
@@ -44,6 +44,7 @@ from .julia_helpers import (
|
|
| 44 |
_load_cluster_manager,
|
| 45 |
jl_array,
|
| 46 |
jl_deserialize,
|
|
|
|
| 47 |
jl_serialize,
|
| 48 |
)
|
| 49 |
from .julia_import import SymbolicRegression, jl
|
|
@@ -1695,11 +1696,25 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
| 1695 |
optimize=self.weight_optimize,
|
| 1696 |
)
|
| 1697 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1698 |
# Call to Julia backend.
|
| 1699 |
# See https://github.com/MilesCranmer/SymbolicRegression.jl/blob/master/src/OptionsStruct.jl
|
| 1700 |
options = SymbolicRegression.Options(
|
| 1701 |
-
binary_operators=
|
| 1702 |
-
unary_operators=
|
| 1703 |
bin_constraints=jl_array(bin_constraints),
|
| 1704 |
una_constraints=jl_array(una_constraints),
|
| 1705 |
complexity_of_operators=complexity_of_operators,
|
|
|
|
| 13 |
from io import StringIO
|
| 14 |
from multiprocessing import cpu_count
|
| 15 |
from pathlib import Path
|
| 16 |
+
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union, cast
|
| 17 |
|
| 18 |
import numpy as np
|
| 19 |
import pandas as pd
|
|
|
|
| 44 |
_load_cluster_manager,
|
| 45 |
jl_array,
|
| 46 |
jl_deserialize,
|
| 47 |
+
jl_is_function,
|
| 48 |
jl_serialize,
|
| 49 |
)
|
| 50 |
from .julia_import import SymbolicRegression, jl
|
|
|
|
| 1696 |
optimize=self.weight_optimize,
|
| 1697 |
)
|
| 1698 |
|
| 1699 |
+
jl_binary_operators: list[Any] = []
|
| 1700 |
+
jl_unary_operators: list[Any] = []
|
| 1701 |
+
for input_list, output_list, name in [
|
| 1702 |
+
(binary_operators, jl_binary_operators, "binary"),
|
| 1703 |
+
(unary_operators, jl_unary_operators, "unary"),
|
| 1704 |
+
]:
|
| 1705 |
+
for op in input_list:
|
| 1706 |
+
jl_op = jl.seval(op)
|
| 1707 |
+
if not jl_is_function(jl_op):
|
| 1708 |
+
raise ValueError(
|
| 1709 |
+
f"When building `{name}_operators`, `'{op}'` did not return a Julia function"
|
| 1710 |
+
)
|
| 1711 |
+
output_list.append(jl_op)
|
| 1712 |
+
|
| 1713 |
# Call to Julia backend.
|
| 1714 |
# See https://github.com/MilesCranmer/SymbolicRegression.jl/blob/master/src/OptionsStruct.jl
|
| 1715 |
options = SymbolicRegression.Options(
|
| 1716 |
+
binary_operators=jl_array(jl_binary_operators, dtype=jl.Function),
|
| 1717 |
+
unary_operators=jl_array(jl_unary_operators, dtype=jl.Function),
|
| 1718 |
bin_constraints=jl_array(bin_constraints),
|
| 1719 |
una_constraints=jl_array(una_constraints),
|
| 1720 |
complexity_of_operators=complexity_of_operators,
|
pysr/test/test.py
CHANGED
|
@@ -431,6 +431,16 @@ class TestPipeline(unittest.TestCase):
|
|
| 431 |
)
|
| 432 |
np.testing.assert_allclose(model.predict(self.X), model3.predict(self.X))
|
| 433 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 434 |
|
| 435 |
def manually_create_model(equations, feature_names=None):
|
| 436 |
if feature_names is None:
|
|
|
|
| 431 |
)
|
| 432 |
np.testing.assert_allclose(model.predict(self.X), model3.predict(self.X))
|
| 433 |
|
| 434 |
+
def test_jl_function_error(self):
|
| 435 |
+
# TODO: Move this to better class
|
| 436 |
+
with self.assertRaises(ValueError) as cm:
|
| 437 |
+
PySRRegressor(unary_operators=["1"]).fit([[1]], [1])
|
| 438 |
+
|
| 439 |
+
self.assertIn(
|
| 440 |
+
"When building `unary_operators`, `'1'` did not return a Julia function",
|
| 441 |
+
str(cm.exception),
|
| 442 |
+
)
|
| 443 |
+
|
| 444 |
|
| 445 |
def manually_create_model(equations, feature_names=None):
|
| 446 |
if feature_names is None:
|