MilesCranmer commited on
Commit
5fa8a27
2 Parent(s): fe3d590 7185bbb

Merge pull request #188 from MilesCranmer/fix-julia-1-6

Browse files
.github/workflows/CI_Windows.yml CHANGED
@@ -26,7 +26,7 @@ jobs:
26
  shell: bash
27
  strategy:
28
  matrix:
29
- julia-version: ['1.7.1']
30
  python-version: ['3.9']
31
  os: [windows-2019]
32
 
 
26
  shell: bash
27
  strategy:
28
  matrix:
29
+ julia-version: ['1.6', '1.7.1']
30
  python-version: ['3.9']
31
  os: [windows-2019]
32
 
pysr/julia_helpers.py CHANGED
@@ -1,34 +1,76 @@
1
  """Functions for initializing the Julia environment and installing deps."""
2
  import sys
 
3
  import warnings
4
  from pathlib import Path
5
  import os
6
 
7
  from .version import __version__, __symbolic_regression_jl_version__
8
 
 
9
  julia_initialized = False
10
 
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  def install(julia_project=None, quiet=False): # pragma: no cover
13
  """
14
  Install PyCall.jl and all required dependencies for SymbolicRegression.jl.
15
 
16
  Also updates the local Julia registry.
17
  """
18
- # Set JULIA_PROJECT so that we install in the pysr environment
19
- julia_project, is_shared = _get_julia_project(julia_project)
20
- if is_shared:
21
- os.environ["JULIA_PROJECT"] = "@" + str(julia_project)
22
- else:
23
- os.environ["JULIA_PROJECT"] = str(julia_project)
24
-
25
  import julia
26
 
 
 
 
 
27
  julia.install(quiet=quiet)
28
 
29
  if is_shared:
30
  # is_shared is only true if the julia_project arg was None
31
- # See _get_julia_project
32
  Main = init_julia(None)
33
  else:
34
  Main = init_julia(julia_project)
@@ -36,7 +78,7 @@ def install(julia_project=None, quiet=False): # pragma: no cover
36
  Main.eval("using Pkg")
37
 
38
  io = "devnull" if quiet else "stderr"
39
- io_arg = f"io={io}" if is_julia_version_greater_eq(Main, "1.6") else ""
40
 
41
  # Can't pass IO to Julia call as it evaluates to PyObject, so just directly
42
  # use Main.eval:
@@ -56,7 +98,7 @@ def install(julia_project=None, quiet=False): # pragma: no cover
56
  )
57
 
58
 
59
- def import_error_string(julia_project=None):
60
  s = """
61
  Required dependencies are not installed or built. Run the following code in the Python REPL:
62
 
@@ -71,7 +113,7 @@ def import_error_string(julia_project=None):
71
  return s
72
 
73
 
74
- def _get_julia_project(julia_project):
75
  if julia_project is None:
76
  is_shared = True
77
  julia_project = f"pysr-{__version__}"
@@ -81,12 +123,19 @@ def _get_julia_project(julia_project):
81
  return julia_project, is_shared
82
 
83
 
84
- def is_julia_version_greater_eq(Main, version="1.6"):
85
  """Check if Julia version is greater than specified version."""
86
- return Main.eval(f'VERSION >= v"{version}"')
 
 
 
 
 
 
 
87
 
88
 
89
- def check_for_conflicting_libraries(): # pragma: no cover
90
  """Check whether there are conflicting modules, and display warnings."""
91
  # See https://github.com/pytorch/pytorch/issues/78829: importing
92
  # pytorch before running `pysr.fit` causes a segfault.
@@ -106,15 +155,12 @@ def init_julia(julia_project=None):
106
  global julia_initialized
107
 
108
  if not julia_initialized:
109
- check_for_conflicting_libraries()
110
 
111
  from julia.core import JuliaInfo, UnsupportedPythonError
112
 
113
- julia_project, is_shared = _get_julia_project(julia_project)
114
- if is_shared:
115
- os.environ["JULIA_PROJECT"] = "@" + str(julia_project)
116
- else:
117
- os.environ["JULIA_PROJECT"] = str(julia_project)
118
 
119
  try:
120
  info = JuliaInfo.load(julia="julia")
@@ -125,7 +171,7 @@ def init_julia(julia_project=None):
125
  )
126
 
127
  if not info.is_pycall_built():
128
- raise ImportError(import_error_string())
129
 
130
  Main = None
131
  try:
@@ -160,7 +206,7 @@ def _add_sr_to_julia_project(Main, io_arg):
160
 
161
 
162
  def _escape_filename(filename):
163
- """Turns a file into a string representation with correctly escaped backslashes"""
164
  str_repr = str(filename)
165
  str_repr = str_repr.replace("\\", "\\\\")
166
  return str_repr
 
1
  """Functions for initializing the Julia environment and installing deps."""
2
  import sys
3
+ import subprocess
4
  import warnings
5
  from pathlib import Path
6
  import os
7
 
8
  from .version import __version__, __symbolic_regression_jl_version__
9
 
10
+ juliainfo = None
11
  julia_initialized = False
12
 
13
 
14
+ def _load_juliainfo():
15
+ """Execute julia.core.JuliaInfo.load(), and store as juliainfo."""
16
+ global juliainfo
17
+
18
+ if juliainfo is None:
19
+ from julia.core import JuliaInfo
20
+
21
+ try:
22
+ juliainfo = JuliaInfo.load(julia="julia")
23
+ except FileNotFoundError:
24
+ env_path = os.environ["PATH"]
25
+ raise FileNotFoundError(
26
+ f"Julia is not installed in your PATH. Please install Julia and add it to your PATH.\n\nCurrent PATH: {env_path}",
27
+ )
28
+
29
+ return juliainfo
30
+
31
+
32
+ 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())"], capture_output=True
37
+ ).stdout.decode()
38
+ except FileNotFoundError:
39
+ env_path = os.environ["PATH"]
40
+ raise FileNotFoundError(
41
+ f"Julia is not installed in your PATH. Please install Julia and add it to your PATH.\n\nCurrent PATH: {env_path}",
42
+ )
43
+ return Path(julia_env_dir_str)
44
+
45
+
46
+ def _set_julia_project_env(julia_project, is_shared):
47
+ if is_shared:
48
+ if is_julia_version_greater_eq(version=(1, 7, 0)):
49
+ os.environ["JULIA_PROJECT"] = "@" + str(julia_project)
50
+ else:
51
+ julia_env_dir = _get_julia_env_dir()
52
+ os.environ["JULIA_PROJECT"] = str(julia_env_dir / julia_project)
53
+ else:
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.
60
 
61
  Also updates the local Julia registry.
62
  """
 
 
 
 
 
 
 
63
  import julia
64
 
65
+ # Set JULIA_PROJECT so that we install in the pysr environment
66
+ julia_project, is_shared = _process_julia_project(julia_project)
67
+ _set_julia_project_env(julia_project, is_shared)
68
+
69
  julia.install(quiet=quiet)
70
 
71
  if is_shared:
72
  # is_shared is only true if the julia_project arg was None
73
+ # See _process_julia_project
74
  Main = init_julia(None)
75
  else:
76
  Main = init_julia(julia_project)
 
78
  Main.eval("using Pkg")
79
 
80
  io = "devnull" if quiet else "stderr"
81
+ io_arg = f"io={io}" if is_julia_version_greater_eq(version=(1, 6, 0)) else ""
82
 
83
  # Can't pass IO to Julia call as it evaluates to PyObject, so just directly
84
  # use Main.eval:
 
98
  )
99
 
100
 
101
+ def _import_error_string(julia_project=None):
102
  s = """
103
  Required dependencies are not installed or built. Run the following code in the Python REPL:
104
 
 
113
  return s
114
 
115
 
116
+ def _process_julia_project(julia_project):
117
  if julia_project is None:
118
  is_shared = True
119
  julia_project = f"pysr-{__version__}"
 
123
  return julia_project, is_shared
124
 
125
 
126
+ def is_julia_version_greater_eq(juliainfo=None, version=(1, 6, 0)):
127
  """Check if Julia version is greater than specified version."""
128
+ if juliainfo is None:
129
+ juliainfo = _load_juliainfo()
130
+ current_version = (
131
+ juliainfo.version_major,
132
+ juliainfo.version_minor,
133
+ juliainfo.version_patch,
134
+ )
135
+ return current_version >= version
136
 
137
 
138
+ def _check_for_conflicting_libraries(): # pragma: no cover
139
  """Check whether there are conflicting modules, and display warnings."""
140
  # See https://github.com/pytorch/pytorch/issues/78829: importing
141
  # pytorch before running `pysr.fit` causes a segfault.
 
155
  global julia_initialized
156
 
157
  if not julia_initialized:
158
+ _check_for_conflicting_libraries()
159
 
160
  from julia.core import JuliaInfo, UnsupportedPythonError
161
 
162
+ julia_project, is_shared = _process_julia_project(julia_project)
163
+ _set_julia_project_env(julia_project, is_shared)
 
 
 
164
 
165
  try:
166
  info = JuliaInfo.load(julia="julia")
 
171
  )
172
 
173
  if not info.is_pycall_built():
174
+ raise ImportError(_import_error_string())
175
 
176
  Main = None
177
  try:
 
206
 
207
 
208
  def _escape_filename(filename):
209
+ """Turn a path into a string with correctly escaped backslashes."""
210
  str_repr = str(filename)
211
  str_repr = str_repr.replace("\\", "\\\\")
212
  return str_repr
pysr/sr.py CHANGED
@@ -23,11 +23,11 @@ from sklearn.utils.validation import (
23
 
24
  from .julia_helpers import (
25
  init_julia,
26
- _get_julia_project,
27
  is_julia_version_greater_eq,
28
  _escape_filename,
29
  _add_sr_to_julia_project,
30
- import_error_string,
31
  )
32
  from .export_numpy import CallableEquation
33
  from .export_latex import generate_single_table, generate_multiple_tables, to_latex
@@ -1437,10 +1437,12 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
1437
  cluster_manager = Main.eval(f"addprocs_{cluster_manager}")
1438
 
1439
  if not already_ran:
1440
- julia_project, is_shared = _get_julia_project(self.julia_project)
1441
  Main.eval("using Pkg")
1442
  io = "devnull" if update_verbosity == 0 else "stderr"
1443
- io_arg = f"io={io}" if is_julia_version_greater_eq(Main, "1.6") else ""
 
 
1444
 
1445
  Main.eval(
1446
  f'Pkg.activate("{_escape_filename(julia_project)}", shared = Bool({int(is_shared)}), {io_arg})'
@@ -1453,7 +1455,7 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
1453
  _add_sr_to_julia_project(Main, io_arg)
1454
  Main.eval(f"Pkg.resolve({io_arg})")
1455
  except (JuliaError, RuntimeError) as e:
1456
- raise ImportError(import_error_string(julia_project)) from e
1457
  Main.eval("using SymbolicRegression")
1458
 
1459
  Main.plus = Main.eval("(+)")
 
23
 
24
  from .julia_helpers import (
25
  init_julia,
26
+ _process_julia_project,
27
  is_julia_version_greater_eq,
28
  _escape_filename,
29
  _add_sr_to_julia_project,
30
+ _import_error_string,
31
  )
32
  from .export_numpy import CallableEquation
33
  from .export_latex import generate_single_table, generate_multiple_tables, to_latex
 
1437
  cluster_manager = Main.eval(f"addprocs_{cluster_manager}")
1438
 
1439
  if not already_ran:
1440
+ julia_project, is_shared = _process_julia_project(self.julia_project)
1441
  Main.eval("using Pkg")
1442
  io = "devnull" if update_verbosity == 0 else "stderr"
1443
+ io_arg = (
1444
+ f"io={io}" if is_julia_version_greater_eq(version=(1, 6, 0)) else ""
1445
+ )
1446
 
1447
  Main.eval(
1448
  f'Pkg.activate("{_escape_filename(julia_project)}", shared = Bool({int(is_shared)}), {io_arg})'
 
1455
  _add_sr_to_julia_project(Main, io_arg)
1456
  Main.eval(f"Pkg.resolve({io_arg})")
1457
  except (JuliaError, RuntimeError) as e:
1458
+ raise ImportError(_import_error_string(julia_project)) from e
1459
  Main.eval("using SymbolicRegression")
1460
 
1461
  Main.plus = Main.eval("(+)")