Spaces:
Running
Running
MilesCranmer
commited on
Merge pull request #197 from MilesCranmer/custom-julia-project
Browse files- .github/workflows/CI.yml +4 -0
- .github/workflows/CI_Windows.yml +2 -0
- .github/workflows/CI_conda_forge.yml +1 -1
- .github/workflows/CI_docker.yml +1 -1
- .github/workflows/CI_docker_large_nightly.yml +1 -1
- .github/workflows/CI_large_nightly.yml +1 -1
- .github/workflows/CI_mac.yml +2 -0
- environment.yml +2 -1
- pysr/julia_helpers.py +35 -26
- test/test_env.py +48 -0
.github/workflows/CI.yml
CHANGED
@@ -69,6 +69,8 @@ jobs:
|
|
69 |
run: pip install torch # (optional import)
|
70 |
- name: "Run Torch tests"
|
71 |
run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test_torch
|
|
|
|
|
72 |
- name: Coveralls
|
73 |
env:
|
74 |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
@@ -121,6 +123,8 @@ jobs:
|
|
121 |
run: python3 -m pip install coverage coveralls
|
122 |
- name: "Run tests"
|
123 |
run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test
|
|
|
|
|
124 |
- name: Coveralls
|
125 |
env:
|
126 |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
69 |
run: pip install torch # (optional import)
|
70 |
- name: "Run Torch tests"
|
71 |
run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test_torch
|
72 |
+
- name: "Run custom env tests"
|
73 |
+
run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test_env
|
74 |
- name: Coveralls
|
75 |
env:
|
76 |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
123 |
run: python3 -m pip install coverage coveralls
|
124 |
- name: "Run tests"
|
125 |
run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test
|
126 |
+
- name: "Run custom env tests"
|
127 |
+
run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test_env
|
128 |
- name: Coveralls
|
129 |
env:
|
130 |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
.github/workflows/CI_Windows.yml
CHANGED
@@ -63,3 +63,5 @@ jobs:
|
|
63 |
run: pip install torch # (optional import)
|
64 |
- name: "Run Torch tests"
|
65 |
run: python -m unittest test.test_torch
|
|
|
|
|
|
63 |
run: pip install torch # (optional import)
|
64 |
- name: "Run Torch tests"
|
65 |
run: python -m unittest test.test_torch
|
66 |
+
- name: "Run custom env tests"
|
67 |
+
run: python -m unittest test.test_env
|
.github/workflows/CI_conda_forge.yml
CHANGED
@@ -34,4 +34,4 @@ jobs:
|
|
34 |
- name: "Install pysr-forge"
|
35 |
run: conda activate test && mamba install pysr
|
36 |
- name: "Run tests"
|
37 |
-
run: conda activate test && python3 -m unittest test.test
|
|
|
34 |
- name: "Install pysr-forge"
|
35 |
run: conda activate test && mamba install pysr
|
36 |
- name: "Run tests"
|
37 |
+
run: conda activate test && python3 -m unittest test.test && python3 -m unittest test.test_env
|
.github/workflows/CI_docker.yml
CHANGED
@@ -38,4 +38,4 @@ jobs:
|
|
38 |
- name: Build docker
|
39 |
run: docker build -t pysr --build-arg ARCH=${{ matrix.arch }} --build-arg VERSION=${{ matrix.julia-version }} --build-arg PYVERSION=${{ matrix.python-version }} .
|
40 |
- name: Test docker
|
41 |
-
run: docker run --platform=${{ matrix.arch }} --rm pysr /bin/bash -c 'python3 -m unittest test.test'
|
|
|
38 |
- name: Build docker
|
39 |
run: docker build -t pysr --build-arg ARCH=${{ matrix.arch }} --build-arg VERSION=${{ matrix.julia-version }} --build-arg PYVERSION=${{ matrix.python-version }} .
|
40 |
- name: Test docker
|
41 |
+
run: docker run --platform=${{ matrix.arch }} --rm pysr /bin/bash -c 'python3 -m unittest test.test && python3 -m unittest test.test_env'
|
.github/workflows/CI_docker_large_nightly.yml
CHANGED
@@ -31,4 +31,4 @@ jobs:
|
|
31 |
- name: Build docker
|
32 |
run: docker build -t pysr --build-arg ARCH=${{ matrix.arch }} --build-arg VERSION=${{ matrix.julia-version }} --build-arg PYVERSION=${{ matrix.python-version }} .
|
33 |
- name: Test docker
|
34 |
-
run: docker run --platform=${{ matrix.arch }} --rm pysr /bin/bash -c 'python3 -m unittest test.test'
|
|
|
31 |
- name: Build docker
|
32 |
run: docker build -t pysr --build-arg ARCH=${{ matrix.arch }} --build-arg VERSION=${{ matrix.julia-version }} --build-arg PYVERSION=${{ matrix.python-version }} .
|
33 |
- name: Test docker
|
34 |
+
run: docker run --platform=${{ matrix.arch }} --rm pysr /bin/bash -c 'python3 -m unittest test.test && python3 -m unittest test.test_env'
|
.github/workflows/CI_large_nightly.yml
CHANGED
@@ -41,4 +41,4 @@ jobs:
|
|
41 |
python setup.py install
|
42 |
python -c 'import pysr; pysr.install()'
|
43 |
- name: "Run tests"
|
44 |
-
run: python -m unittest test.test
|
|
|
41 |
python setup.py install
|
42 |
python -c 'import pysr; pysr.install()'
|
43 |
- name: "Run tests"
|
44 |
+
run: python -m unittest test.test && python -m unittest test.test_env
|
.github/workflows/CI_mac.yml
CHANGED
@@ -67,3 +67,5 @@ jobs:
|
|
67 |
run: pip install torch # (optional import)
|
68 |
- name: "Run Torch tests"
|
69 |
run: python -m unittest test.test_torch
|
|
|
|
|
|
67 |
run: pip install torch # (optional import)
|
68 |
- name: "Run Torch tests"
|
69 |
run: python -m unittest test.test_torch
|
70 |
+
- name: "Run custom env tests"
|
71 |
+
run: python -m unittest test.test_env
|
environment.yml
CHANGED
@@ -9,4 +9,5 @@ dependencies:
|
|
9 |
- scikit-learn
|
10 |
- setuptools
|
11 |
- pyjulia
|
12 |
-
- openlibm
|
|
|
|
9 |
- scikit-learn
|
10 |
- setuptools
|
11 |
- pyjulia
|
12 |
+
- openlibm
|
13 |
+
- openspecfun
|
pysr/julia_helpers.py
CHANGED
@@ -33,7 +33,9 @@ def _get_julia_env_dir():
|
|
33 |
# Have to manually get env dir:
|
34 |
try:
|
35 |
julia_env_dir_str = subprocess.run(
|
36 |
-
["julia", "-e using Pkg; print(Pkg.envdir())"],
|
|
|
|
|
37 |
).stdout.decode()
|
38 |
except FileNotFoundError:
|
39 |
env_path = os.environ["PATH"]
|
@@ -54,6 +56,12 @@ def _set_julia_project_env(julia_project, is_shared):
|
|
54 |
os.environ["JULIA_PROJECT"] = str(julia_project)
|
55 |
|
56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
def install(julia_project=None, quiet=False): # pragma: no cover
|
58 |
"""
|
59 |
Install PyCall.jl and all required dependencies for SymbolicRegression.jl.
|
@@ -64,28 +72,13 @@ def install(julia_project=None, quiet=False): # pragma: no cover
|
|
64 |
|
65 |
_version_assertion()
|
66 |
# Set JULIA_PROJECT so that we install in the pysr environment
|
67 |
-
|
68 |
-
_set_julia_project_env(
|
69 |
|
70 |
julia.install(quiet=quiet)
|
|
|
|
|
71 |
|
72 |
-
if is_shared:
|
73 |
-
# is_shared is only true if the julia_project arg was None
|
74 |
-
# See _process_julia_project
|
75 |
-
Main = init_julia(None)
|
76 |
-
else:
|
77 |
-
Main = init_julia(julia_project)
|
78 |
-
|
79 |
-
Main.eval("using Pkg")
|
80 |
-
|
81 |
-
io = "devnull" if quiet else "stderr"
|
82 |
-
io_arg = f"io={io}" if is_julia_version_greater_eq(version=(1, 6, 0)) else ""
|
83 |
-
|
84 |
-
# Can't pass IO to Julia call as it evaluates to PyObject, so just directly
|
85 |
-
# use Main.eval:
|
86 |
-
Main.eval(
|
87 |
-
f'Pkg.activate("{_escape_filename(julia_project)}", shared = Bool({int(is_shared)}), {io_arg})'
|
88 |
-
)
|
89 |
if is_shared:
|
90 |
# Install SymbolicRegression.jl:
|
91 |
_add_sr_to_julia_project(Main, io_arg)
|
@@ -117,11 +110,14 @@ def _import_error_string(julia_project=None):
|
|
117 |
def _process_julia_project(julia_project):
|
118 |
if julia_project is None:
|
119 |
is_shared = True
|
120 |
-
|
|
|
|
|
|
|
121 |
else:
|
122 |
is_shared = False
|
123 |
-
|
124 |
-
return
|
125 |
|
126 |
|
127 |
def is_julia_version_greater_eq(juliainfo=None, version=(1, 6, 0)):
|
@@ -151,7 +147,7 @@ def _check_for_conflicting_libraries(): # pragma: no cover
|
|
151 |
)
|
152 |
|
153 |
|
154 |
-
def init_julia(julia_project=None):
|
155 |
"""Initialize julia binary, turning off compiled modules if needed."""
|
156 |
global julia_initialized
|
157 |
|
@@ -161,8 +157,8 @@ def init_julia(julia_project=None):
|
|
161 |
from julia.core import JuliaInfo, UnsupportedPythonError
|
162 |
|
163 |
_version_assertion()
|
164 |
-
|
165 |
-
_set_julia_project_env(
|
166 |
|
167 |
try:
|
168 |
info = JuliaInfo.load(julia="julia")
|
@@ -189,11 +185,24 @@ def init_julia(julia_project=None):
|
|
189 |
|
190 |
Main = _Main
|
191 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
julia_initialized = True
|
193 |
return Main
|
194 |
|
195 |
|
196 |
def _add_sr_to_julia_project(Main, io_arg):
|
|
|
197 |
Main.sr_spec = Main.PackageSpec(
|
198 |
name="SymbolicRegression",
|
199 |
url="https://github.com/MilesCranmer/SymbolicRegression.jl",
|
|
|
33 |
# Have to manually get env dir:
|
34 |
try:
|
35 |
julia_env_dir_str = subprocess.run(
|
36 |
+
["julia", "-e using Pkg; print(Pkg.envdir())"],
|
37 |
+
capture_output=True,
|
38 |
+
env=os.environ,
|
39 |
).stdout.decode()
|
40 |
except FileNotFoundError:
|
41 |
env_path = os.environ["PATH"]
|
|
|
56 |
os.environ["JULIA_PROJECT"] = str(julia_project)
|
57 |
|
58 |
|
59 |
+
def _get_io_arg(quiet):
|
60 |
+
io = "devnull" if quiet else "stderr"
|
61 |
+
io_arg = f"io={io}" if is_julia_version_greater_eq(version=(1, 6, 0)) else ""
|
62 |
+
return io_arg
|
63 |
+
|
64 |
+
|
65 |
def install(julia_project=None, quiet=False): # pragma: no cover
|
66 |
"""
|
67 |
Install PyCall.jl and all required dependencies for SymbolicRegression.jl.
|
|
|
72 |
|
73 |
_version_assertion()
|
74 |
# Set JULIA_PROJECT so that we install in the pysr environment
|
75 |
+
processed_julia_project, is_shared = _process_julia_project(julia_project)
|
76 |
+
_set_julia_project_env(processed_julia_project, is_shared)
|
77 |
|
78 |
julia.install(quiet=quiet)
|
79 |
+
Main = init_julia(julia_project, quiet=quiet)
|
80 |
+
io_arg = _get_io_arg(quiet)
|
81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
if is_shared:
|
83 |
# Install SymbolicRegression.jl:
|
84 |
_add_sr_to_julia_project(Main, io_arg)
|
|
|
110 |
def _process_julia_project(julia_project):
|
111 |
if julia_project is None:
|
112 |
is_shared = True
|
113 |
+
processed_julia_project = f"pysr-{__version__}"
|
114 |
+
elif julia_project[0] == "@":
|
115 |
+
is_shared = True
|
116 |
+
processed_julia_project = julia_project[1:]
|
117 |
else:
|
118 |
is_shared = False
|
119 |
+
processed_julia_project = Path(julia_project)
|
120 |
+
return processed_julia_project, is_shared
|
121 |
|
122 |
|
123 |
def is_julia_version_greater_eq(juliainfo=None, version=(1, 6, 0)):
|
|
|
147 |
)
|
148 |
|
149 |
|
150 |
+
def init_julia(julia_project=None, quiet=False):
|
151 |
"""Initialize julia binary, turning off compiled modules if needed."""
|
152 |
global julia_initialized
|
153 |
|
|
|
157 |
from julia.core import JuliaInfo, UnsupportedPythonError
|
158 |
|
159 |
_version_assertion()
|
160 |
+
processed_julia_project, is_shared = _process_julia_project(julia_project)
|
161 |
+
_set_julia_project_env(processed_julia_project, is_shared)
|
162 |
|
163 |
try:
|
164 |
info = JuliaInfo.load(julia="julia")
|
|
|
185 |
|
186 |
Main = _Main
|
187 |
|
188 |
+
if julia_initialized:
|
189 |
+
Main.eval("using Pkg")
|
190 |
+
|
191 |
+
io_arg = _get_io_arg(quiet)
|
192 |
+
# Can't pass IO to Julia call as it evaluates to PyObject, so just directly
|
193 |
+
# use Main.eval:
|
194 |
+
Main.eval(
|
195 |
+
f'Pkg.activate("{_escape_filename(processed_julia_project)}",'
|
196 |
+
f"shared = Bool({int(is_shared)}), "
|
197 |
+
f"{io_arg})"
|
198 |
+
)
|
199 |
+
|
200 |
julia_initialized = True
|
201 |
return Main
|
202 |
|
203 |
|
204 |
def _add_sr_to_julia_project(Main, io_arg):
|
205 |
+
Main.eval("using Pkg")
|
206 |
Main.sr_spec = Main.PackageSpec(
|
207 |
name="SymbolicRegression",
|
208 |
url="https://github.com/MilesCranmer/SymbolicRegression.jl",
|
test/test_env.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Contains tests for creating and initializing custom Julia projects."""
|
2 |
+
|
3 |
+
import unittest
|
4 |
+
import os
|
5 |
+
from pysr import julia_helpers
|
6 |
+
from tempfile import TemporaryDirectory
|
7 |
+
|
8 |
+
|
9 |
+
class TestJuliaProject(unittest.TestCase):
|
10 |
+
"""Various tests for working with Julia projects."""
|
11 |
+
|
12 |
+
def test_custom_shared_env(self):
|
13 |
+
"""Test that we can use PySR in a custom shared env."""
|
14 |
+
with TemporaryDirectory() as tmpdir:
|
15 |
+
# Create a temp depot to store julia packages (and our custom env)
|
16 |
+
Main = julia_helpers.init_julia()
|
17 |
+
|
18 |
+
# Set up env:
|
19 |
+
if "JULIA_DEPOT_PATH" not in os.environ:
|
20 |
+
old_env = None
|
21 |
+
os.environ["JULIA_DEPOT_PATH"] = tmpdir
|
22 |
+
else:
|
23 |
+
old_env = os.environ["JULIA_DEPOT_PATH"]
|
24 |
+
os.environ[
|
25 |
+
"JULIA_DEPOT_PATH"
|
26 |
+
] = f"{tmpdir}:{os.environ['JULIA_DEPOT_PATH']}"
|
27 |
+
Main.eval(
|
28 |
+
f'pushfirst!(DEPOT_PATH, "{julia_helpers._escape_filename(tmpdir)}")'
|
29 |
+
)
|
30 |
+
test_env_name = "@pysr_test_env"
|
31 |
+
julia_helpers.install(julia_project=test_env_name)
|
32 |
+
Main = julia_helpers.init_julia(julia_project=test_env_name)
|
33 |
+
|
34 |
+
# Try to use env:
|
35 |
+
Main.eval("using SymbolicRegression")
|
36 |
+
Main.eval("using Pkg")
|
37 |
+
|
38 |
+
# Assert we actually loaded it:
|
39 |
+
cur_project_dir = Main.eval("splitdir(dirname(Base.active_project()))[1]")
|
40 |
+
potential_shared_project_dirs = Main.eval("Pkg.envdir(DEPOT_PATH[1])")
|
41 |
+
self.assertEqual(cur_project_dir, potential_shared_project_dirs)
|
42 |
+
|
43 |
+
# Clean up:
|
44 |
+
Main.eval("pop!(DEPOT_PATH)")
|
45 |
+
if old_env is None:
|
46 |
+
del os.environ["JULIA_DEPOT_PATH"]
|
47 |
+
else:
|
48 |
+
os.environ["JULIA_DEPOT_PATH"] = old_env
|