Spaces:
Sleeping
Sleeping
MilesCranmer
commited on
Merge pull request #228 from MilesCranmer/optimize=3
Browse filesMake Julia startup options configurable; set optimize=3
- docs/param_groupings.yml +1 -0
- pysr/julia_helpers.py +40 -11
- pysr/sr.py +27 -19
- pysr/test/test.py +18 -0
- pysr/version.py +1 -1
docs/param_groupings.yml
CHANGED
@@ -82,6 +82,7 @@
|
|
82 |
- delete_tempfiles
|
83 |
- julia_project
|
84 |
- update
|
|
|
85 |
- Exporting the Results:
|
86 |
- equation_file
|
87 |
- output_jax_format
|
|
|
82 |
- delete_tempfiles
|
83 |
- julia_project
|
84 |
- update
|
85 |
+
- julia_kwargs
|
86 |
- Exporting the Results:
|
87 |
- equation_file
|
88 |
- output_jax_format
|
pysr/julia_helpers.py
CHANGED
@@ -10,6 +10,8 @@ from .version import __version__, __symbolic_regression_jl_version__
|
|
10 |
|
11 |
juliainfo = None
|
12 |
julia_initialized = False
|
|
|
|
|
13 |
|
14 |
|
15 |
def _load_juliainfo():
|
@@ -143,13 +145,18 @@ def _check_for_conflicting_libraries(): # pragma: no cover
|
|
143 |
)
|
144 |
|
145 |
|
146 |
-
def init_julia(julia_project=None, quiet=False):
|
147 |
"""Initialize julia binary, turning off compiled modules if needed."""
|
148 |
global julia_initialized
|
|
|
|
|
149 |
|
150 |
if not julia_initialized:
|
151 |
_check_for_conflicting_libraries()
|
152 |
|
|
|
|
|
|
|
153 |
from julia.core import JuliaInfo, UnsupportedPythonError
|
154 |
|
155 |
_julia_version_assertion()
|
@@ -167,21 +174,37 @@ def init_julia(julia_project=None, quiet=False):
|
|
167 |
if not info.is_pycall_built():
|
168 |
raise ImportError(_import_error())
|
169 |
|
170 |
-
|
171 |
-
try:
|
172 |
-
from julia import Main as _Main
|
173 |
|
174 |
-
|
|
|
175 |
except UnsupportedPythonError:
|
176 |
# Static python binary, so we turn off pre-compiled modules.
|
177 |
-
|
|
|
178 |
|
179 |
-
|
180 |
-
from julia import Main as _Main
|
181 |
|
182 |
-
|
183 |
|
184 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
Main.eval("using Pkg")
|
186 |
|
187 |
io_arg = _get_io_arg(quiet)
|
@@ -193,6 +216,11 @@ def init_julia(julia_project=None, quiet=False):
|
|
193 |
f"{io_arg})"
|
194 |
)
|
195 |
|
|
|
|
|
|
|
|
|
|
|
196 |
julia_initialized = True
|
197 |
return Main
|
198 |
|
@@ -234,7 +262,7 @@ def _backend_version_assertion(Main):
|
|
234 |
if backend_version != expected_backend_version: # pragma: no cover
|
235 |
warnings.warn(
|
236 |
f"PySR backend (SymbolicRegression.jl) version {backend_version} "
|
237 |
-
"does not match expected version {expected_backend_version}. "
|
238 |
"Things may break. "
|
239 |
"Please update your PySR installation with "
|
240 |
"`python -c 'import pysr; pysr.install()'`."
|
@@ -257,6 +285,7 @@ def _update_julia_project(Main, is_shared, io_arg):
|
|
257 |
try:
|
258 |
if is_shared:
|
259 |
_add_sr_to_julia_project(Main, io_arg)
|
|
|
260 |
Main.eval(f"Pkg.resolve({io_arg})")
|
261 |
except (JuliaError, RuntimeError) as e:
|
262 |
raise ImportError(_import_error()) from e
|
|
|
10 |
|
11 |
juliainfo = None
|
12 |
julia_initialized = False
|
13 |
+
julia_kwargs_at_initialization = None
|
14 |
+
julia_activated_env = None
|
15 |
|
16 |
|
17 |
def _load_juliainfo():
|
|
|
145 |
)
|
146 |
|
147 |
|
148 |
+
def init_julia(julia_project=None, quiet=False, julia_kwargs=None):
|
149 |
"""Initialize julia binary, turning off compiled modules if needed."""
|
150 |
global julia_initialized
|
151 |
+
global julia_kwargs_at_initialization
|
152 |
+
global julia_activated_env
|
153 |
|
154 |
if not julia_initialized:
|
155 |
_check_for_conflicting_libraries()
|
156 |
|
157 |
+
if julia_kwargs is None:
|
158 |
+
julia_kwargs = {"optimize": 3}
|
159 |
+
|
160 |
from julia.core import JuliaInfo, UnsupportedPythonError
|
161 |
|
162 |
_julia_version_assertion()
|
|
|
174 |
if not info.is_pycall_built():
|
175 |
raise ImportError(_import_error())
|
176 |
|
177 |
+
from julia.core import Julia
|
|
|
|
|
178 |
|
179 |
+
try:
|
180 |
+
Julia(**julia_kwargs)
|
181 |
except UnsupportedPythonError:
|
182 |
# Static python binary, so we turn off pre-compiled modules.
|
183 |
+
julia_kwargs = {**julia_kwargs, "compiled_modules": False}
|
184 |
+
Julia(**julia_kwargs)
|
185 |
|
186 |
+
from julia import Main as _Main
|
|
|
187 |
|
188 |
+
Main = _Main
|
189 |
|
190 |
+
if julia_activated_env is None:
|
191 |
+
julia_activated_env = processed_julia_project
|
192 |
+
|
193 |
+
if julia_initialized and julia_kwargs_at_initialization is not None:
|
194 |
+
# Check if the kwargs are the same as the previous initialization
|
195 |
+
init_set = set(julia_kwargs_at_initialization.items())
|
196 |
+
new_set = set(julia_kwargs.items())
|
197 |
+
set_diff = new_set - init_set
|
198 |
+
# Remove the `compiled_modules` key, since it is not a user-specified kwarg:
|
199 |
+
set_diff = {k: v for k, v in set_diff if k != "compiled_modules"}
|
200 |
+
if len(set_diff) > 0:
|
201 |
+
warnings.warn(
|
202 |
+
"Julia has already started. The new Julia options "
|
203 |
+
+ str(set_diff)
|
204 |
+
+ " will be ignored."
|
205 |
+
)
|
206 |
+
|
207 |
+
if julia_initialized and julia_activated_env != processed_julia_project:
|
208 |
Main.eval("using Pkg")
|
209 |
|
210 |
io_arg = _get_io_arg(quiet)
|
|
|
216 |
f"{io_arg})"
|
217 |
)
|
218 |
|
219 |
+
julia_activated_env = processed_julia_project
|
220 |
+
|
221 |
+
if not julia_initialized:
|
222 |
+
julia_kwargs_at_initialization = julia_kwargs
|
223 |
+
|
224 |
julia_initialized = True
|
225 |
return Main
|
226 |
|
|
|
262 |
if backend_version != expected_backend_version: # pragma: no cover
|
263 |
warnings.warn(
|
264 |
f"PySR backend (SymbolicRegression.jl) version {backend_version} "
|
265 |
+
f"does not match expected version {expected_backend_version}. "
|
266 |
"Things may break. "
|
267 |
"Please update your PySR installation with "
|
268 |
"`python -c 'import pysr; pysr.install()'`."
|
|
|
285 |
try:
|
286 |
if is_shared:
|
287 |
_add_sr_to_julia_project(Main, io_arg)
|
288 |
+
Main.eval("using Pkg")
|
289 |
Main.eval(f"Pkg.resolve({io_arg})")
|
290 |
except (JuliaError, RuntimeError) as e:
|
291 |
raise ImportError(_import_error()) from e
|
pysr/sr.py
CHANGED
@@ -581,10 +581,15 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
581 |
inputting to PySR. Can help PySR fit noisy data.
|
582 |
Default is `False`.
|
583 |
select_k_features : int
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
|
|
|
|
|
|
|
|
|
|
588 |
**kwargs : dict
|
589 |
Supports deprecated keyword arguments. Other arguments will
|
590 |
result in an error.
|
@@ -733,6 +738,7 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
733 |
extra_jax_mappings=None,
|
734 |
denoise=False,
|
735 |
select_k_features=None,
|
|
|
736 |
**kwargs,
|
737 |
):
|
738 |
|
@@ -827,6 +833,7 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
827 |
# Pre-modelling transformation
|
828 |
self.denoise = denoise
|
829 |
self.select_k_features = select_k_features
|
|
|
830 |
|
831 |
# Once all valid parameters have been assigned handle the
|
832 |
# deprecated kwargs
|
@@ -1259,6 +1266,17 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
1259 |
+ len(packed_modified_params["unary_operators"])
|
1260 |
> 0
|
1261 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1262 |
return packed_modified_params
|
1263 |
|
1264 |
def _validate_and_set_fit_params(self, X, y, Xresampled, weights, variable_names):
|
@@ -1469,31 +1487,21 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
1469 |
batch_size = mutated_params["batch_size"]
|
1470 |
update_verbosity = mutated_params["update_verbosity"]
|
1471 |
progress = mutated_params["progress"]
|
|
|
1472 |
|
1473 |
# Start julia backend processes
|
1474 |
-
|
1475 |
-
if multithreading:
|
1476 |
-
os.environ["JULIA_NUM_THREADS"] = str(self.procs)
|
1477 |
-
|
1478 |
-
Main = init_julia(self.julia_project)
|
1479 |
|
1480 |
if cluster_manager is not None:
|
1481 |
cluster_manager = _load_cluster_manager(cluster_manager)
|
1482 |
|
1483 |
-
if
|
1484 |
-
|
1485 |
-
Main.eval("using Pkg")
|
1486 |
io = "devnull" if update_verbosity == 0 else "stderr"
|
1487 |
io_arg = (
|
1488 |
f"io={io}" if is_julia_version_greater_eq(version=(1, 6, 0)) else ""
|
1489 |
)
|
1490 |
-
|
1491 |
-
Main.eval(
|
1492 |
-
f'Pkg.activate("{_escape_filename(julia_project)}", shared = Bool({int(is_shared)}), {io_arg})'
|
1493 |
-
)
|
1494 |
-
|
1495 |
-
if self.update:
|
1496 |
-
_update_julia_project(Main, is_shared, io_arg)
|
1497 |
|
1498 |
SymbolicRegression = _load_backend(Main)
|
1499 |
|
|
|
581 |
inputting to PySR. Can help PySR fit noisy data.
|
582 |
Default is `False`.
|
583 |
select_k_features : int
|
584 |
+
Whether to run feature selection in Python using random forests,
|
585 |
+
before passing to the symbolic regression code. None means no
|
586 |
+
feature selection; an int means select that many features.
|
587 |
+
Default is `None`.
|
588 |
+
julia_kwargs : dict
|
589 |
+
Keyword arguments to pass to `julia.core.Julia(...)` to initialize
|
590 |
+
the Julia runtime. The default, when `None`, is to set `threads` equal
|
591 |
+
to `procs`, and `optimize` to 3.
|
592 |
+
Default is `None`.
|
593 |
**kwargs : dict
|
594 |
Supports deprecated keyword arguments. Other arguments will
|
595 |
result in an error.
|
|
|
738 |
extra_jax_mappings=None,
|
739 |
denoise=False,
|
740 |
select_k_features=None,
|
741 |
+
julia_kwargs=None,
|
742 |
**kwargs,
|
743 |
):
|
744 |
|
|
|
833 |
# Pre-modelling transformation
|
834 |
self.denoise = denoise
|
835 |
self.select_k_features = select_k_features
|
836 |
+
self.julia_kwargs = julia_kwargs
|
837 |
|
838 |
# Once all valid parameters have been assigned handle the
|
839 |
# deprecated kwargs
|
|
|
1266 |
+ len(packed_modified_params["unary_operators"])
|
1267 |
> 0
|
1268 |
)
|
1269 |
+
|
1270 |
+
julia_kwargs = {}
|
1271 |
+
if self.julia_kwargs is not None:
|
1272 |
+
for key, value in self.julia_kwargs.items():
|
1273 |
+
julia_kwargs[key] = value
|
1274 |
+
if "optimize" not in julia_kwargs:
|
1275 |
+
julia_kwargs["optimize"] = 3
|
1276 |
+
if "threads" not in julia_kwargs and packed_modified_params["multithreading"]:
|
1277 |
+
julia_kwargs["threads"] = self.procs
|
1278 |
+
packed_modified_params["julia_kwargs"] = julia_kwargs
|
1279 |
+
|
1280 |
return packed_modified_params
|
1281 |
|
1282 |
def _validate_and_set_fit_params(self, X, y, Xresampled, weights, variable_names):
|
|
|
1487 |
batch_size = mutated_params["batch_size"]
|
1488 |
update_verbosity = mutated_params["update_verbosity"]
|
1489 |
progress = mutated_params["progress"]
|
1490 |
+
julia_kwargs = mutated_params["julia_kwargs"]
|
1491 |
|
1492 |
# Start julia backend processes
|
1493 |
+
Main = init_julia(self.julia_project, julia_kwargs=julia_kwargs)
|
|
|
|
|
|
|
|
|
1494 |
|
1495 |
if cluster_manager is not None:
|
1496 |
cluster_manager = _load_cluster_manager(cluster_manager)
|
1497 |
|
1498 |
+
if self.update:
|
1499 |
+
_, is_shared = _process_julia_project(self.julia_project)
|
|
|
1500 |
io = "devnull" if update_verbosity == 0 else "stderr"
|
1501 |
io_arg = (
|
1502 |
f"io={io}" if is_julia_version_greater_eq(version=(1, 6, 0)) else ""
|
1503 |
)
|
1504 |
+
_update_julia_project(Main, is_shared, io_arg)
|
|
|
|
|
|
|
|
|
|
|
|
|
1505 |
|
1506 |
SymbolicRegression = _load_backend(Main)
|
1507 |
|
pysr/test/test.py
CHANGED
@@ -12,6 +12,7 @@ import pickle as pkl
|
|
12 |
import tempfile
|
13 |
from pathlib import Path
|
14 |
|
|
|
15 |
from .. import PySRRegressor
|
16 |
from ..sr import (
|
17 |
run_feature_selection,
|
@@ -566,6 +567,23 @@ class TestMiscellaneous(unittest.TestCase):
|
|
566 |
with self.assertRaises(ValueError):
|
567 |
model.fit(X, y)
|
568 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
569 |
def test_extra_sympy_mappings_undefined(self):
|
570 |
"""extra_sympy_mappings=None errors for custom operators"""
|
571 |
model = PySRRegressor(unary_operators=["square2(x) = x^2"])
|
|
|
12 |
import tempfile
|
13 |
from pathlib import Path
|
14 |
|
15 |
+
from .. import julia_helpers
|
16 |
from .. import PySRRegressor
|
17 |
from ..sr import (
|
18 |
run_feature_selection,
|
|
|
567 |
with self.assertRaises(ValueError):
|
568 |
model.fit(X, y)
|
569 |
|
570 |
+
def test_changed_options_warning(self):
|
571 |
+
"""Check that a warning is given if Julia options are changed."""
|
572 |
+
if julia_helpers.julia_kwargs_at_initialization is None:
|
573 |
+
julia_helpers.init_julia(julia_kwargs={"threads": 2, "optimize": 3})
|
574 |
+
|
575 |
+
cur_init = julia_helpers.julia_kwargs_at_initialization
|
576 |
+
|
577 |
+
threads_to_change = cur_init["threads"] + 1
|
578 |
+
with warnings.catch_warnings():
|
579 |
+
warnings.simplefilter("error")
|
580 |
+
with self.assertRaises(Exception) as context:
|
581 |
+
julia_helpers.init_julia(
|
582 |
+
julia_kwargs={"threads": threads_to_change, "optimize": 3}
|
583 |
+
)
|
584 |
+
self.assertIn("Julia has already started", str(context.exception))
|
585 |
+
self.assertIn("threads", str(context.exception))
|
586 |
+
|
587 |
def test_extra_sympy_mappings_undefined(self):
|
588 |
"""extra_sympy_mappings=None errors for custom operators"""
|
589 |
model = PySRRegressor(unary_operators=["square2(x) = x^2"])
|
pysr/version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1 |
-
__version__ = "0.11.
|
2 |
__symbolic_regression_jl_version__ = "0.14.4"
|
|
|
1 |
+
__version__ = "0.11.11"
|
2 |
__symbolic_regression_jl_version__ = "0.14.4"
|