MilesCranmer commited on
Commit
009d206
1 Parent(s): a8f4864

Implement DaemonMode for fast startup

Browse files
Files changed (2) hide show
  1. Project.toml +1 -0
  2. pysr/sr.py +65 -14
Project.toml CHANGED
@@ -1,4 +1,5 @@
1
  [deps]
 
2
  SymbolicRegression = "8254be44-1295-4e6a-a16d-46603ac705cb"
3
 
4
  [compat]
 
1
  [deps]
2
+ DaemonMode = "d749ddd5-2b29-4920-8305-6ff5a704e36e"
3
  SymbolicRegression = "8254be44-1295-4e6a-a16d-46603ac705cb"
4
 
5
  [compat]
pysr/sr.py CHANGED
@@ -13,6 +13,8 @@ import shutil
13
  from pathlib import Path
14
  from datetime import datetime
15
  import warnings
 
 
16
 
17
  global_state = dict(
18
  equation_file="hall_of_fame.csv",
@@ -26,6 +28,7 @@ global_state = dict(
26
  multioutput=False,
27
  nout=1,
28
  selection=None,
 
29
  )
30
 
31
  sympy_mappings = {
@@ -132,6 +135,7 @@ def pysr(
132
  tournament_selection_p=1.0,
133
  denoise=False,
134
  Xresampled=None,
 
135
  ):
136
  """Run symbolic regression to fit f(X[i, :]) ~ y[i] for all i.
137
  Note: most default parameters have been tuned over several example
@@ -248,6 +252,10 @@ def pysr(
248
  :type tournament_selection_p: float
249
  :param denoise: Whether to use a Gaussian Process to denoise the data before inputting to PySR. Can help PySR fit noisy data.
250
  :type denoise: bool
 
 
 
 
251
  :returns: Results dataframe, giving complexity, MSE, and equations (as strings), as well as functional forms. If list, each element corresponds to a dataframe of equations for each output.
252
  :type: pd.DataFrame/list
253
  """
@@ -410,6 +418,7 @@ def pysr(
410
  tournament_selection_n=tournament_selection_n,
411
  tournament_selection_p=tournament_selection_p,
412
  denoise=denoise,
 
413
  )
414
 
415
  kwargs = {**_set_paths(tempdir), **kwargs}
@@ -467,12 +476,53 @@ def _set_globals(X, **kwargs):
467
  global_state[key] = value
468
 
469
 
470
- def _final_pysr_process(julia_optimization, runfile_filename, timeout, **kwargs):
471
- command = [
472
- "julia",
473
- f"-O{julia_optimization:d}",
474
- str(runfile_filename),
475
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  if timeout is not None:
477
  command = ["timeout", f"{timeout}"] + command
478
  _cmd_runner(command, **kwargs)
@@ -523,6 +573,7 @@ def _create_julia_files(
523
  pkg_directory,
524
  need_install,
525
  update,
 
526
  **kwargs,
527
  ):
528
  with open(hyperparam_filename, "w") as f:
@@ -534,14 +585,14 @@ def _create_julia_files(
534
  julia_project = pkg_directory
535
  else:
536
  julia_project = Path(julia_project)
537
- print(f"import Pkg", file=f)
538
- print(f'Pkg.activate("{_escape_filename(julia_project)}")', file=f)
539
- if need_install:
540
- print(f"Pkg.instantiate()", file=f)
541
- print("Pkg.update()", file=f)
542
- print("Pkg.precompile()", file=f)
543
- elif update:
544
- print(f"Pkg.update()", file=f)
545
  print(f"using SymbolicRegression", file=f)
546
  print(f'include("{_escape_filename(hyperparam_filename)}")', file=f)
547
  print(f'include("{_escape_filename(dataset_filename)}")', file=f)
 
13
  from pathlib import Path
14
  from datetime import datetime
15
  import warnings
16
+ import shlex
17
+ import time
18
 
19
  global_state = dict(
20
  equation_file="hall_of_fame.csv",
 
28
  multioutput=False,
29
  nout=1,
30
  selection=None,
31
+ daemon=None,
32
  )
33
 
34
  sympy_mappings = {
 
135
  tournament_selection_p=1.0,
136
  denoise=False,
137
  Xresampled=None,
138
+ daemon_mode=False,
139
  ):
140
  """Run symbolic regression to fit f(X[i, :]) ~ y[i] for all i.
141
  Note: most default parameters have been tuned over several example
 
252
  :type tournament_selection_p: float
253
  :param denoise: Whether to use a Gaussian Process to denoise the data before inputting to PySR. Can help PySR fit noisy data.
254
  :type denoise: bool
255
+ :param Xresampled:
256
+ :type Xresampled: np.ndarray/None
257
+ :param daemon_mode: Whether to use DaemonMode.jl for Julia. This will let you quickly restart PySR between calls.
258
+ :type daemon_mode: bool
259
  :returns: Results dataframe, giving complexity, MSE, and equations (as strings), as well as functional forms. If list, each element corresponds to a dataframe of equations for each output.
260
  :type: pd.DataFrame/list
261
  """
 
418
  tournament_selection_n=tournament_selection_n,
419
  tournament_selection_p=tournament_selection_p,
420
  denoise=denoise,
421
+ daemon_mode=daemon_mode,
422
  )
423
 
424
  kwargs = {**_set_paths(tempdir), **kwargs}
 
476
  global_state[key] = value
477
 
478
 
479
+ def _start_julia_daemon(
480
+ julia_optimization, julia_project, need_install, update, **kwargs
481
+ ):
482
+ global global_state
483
+ if global_state["daemon"] is not None:
484
+ return
485
+
486
+ s = "import Pkg;"
487
+ if need_install:
488
+ s += "Pkg.instantiate();"
489
+ s += "Pkg.update();"
490
+ s += "Pkg.precompile();"
491
+ elif update:
492
+ s += "Pkg.update();"
493
+ command = shlex.split(
494
+ f"julia --startup-file=no -O{julia_optimization:d} --project={julia_project} -e"
495
+ f" '{s}using DaemonMode; println(\"Julia server starting!\"); serve(3000, true)'"
496
+ )
497
+ print("Running on", " ".join(command))
498
+ process = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=-1)
499
+ while True:
500
+ line = process.stdout.readline()
501
+ if not line:
502
+ break
503
+ decoded_line = line.decode("utf-8")
504
+ print(decoded_line, end="")
505
+ if "Julia server starting!" in decoded_line:
506
+ break
507
+
508
+ time.sleep(5)
509
+ print("Finished starting Julia daemon.")
510
+ global_state["daemon"] = process
511
+
512
+
513
+ def _final_pysr_process(
514
+ julia_optimization, julia_project, runfile_filename, timeout, daemon_mode, **kwargs
515
+ ):
516
+ if daemon_mode:
517
+ _start_julia_daemon(
518
+ julia_optimization=julia_optimization, julia_project=julia_project, **kwargs
519
+ )
520
+ command = shlex.split(
521
+ f"julia --startup-file=no --project={julia_project}"
522
+ f" -e 'using DaemonMode; runargs()' {runfile_filename}"
523
+ )
524
+ else:
525
+ command = shlex.split(f"julia -O{julia_optimization:d} {runfile_filename}")
526
  if timeout is not None:
527
  command = ["timeout", f"{timeout}"] + command
528
  _cmd_runner(command, **kwargs)
 
573
  pkg_directory,
574
  need_install,
575
  update,
576
+ daemon_mode,
577
  **kwargs,
578
  ):
579
  with open(hyperparam_filename, "w") as f:
 
585
  julia_project = pkg_directory
586
  else:
587
  julia_project = Path(julia_project)
588
+ if not daemon_mode:
589
+ print(f"import Pkg", file=f)
590
+ if need_install:
591
+ print(f"Pkg.instantiate()", file=f)
592
+ print("Pkg.update()", file=f)
593
+ print("Pkg.precompile()", file=f)
594
+ elif update:
595
+ print(f"Pkg.update()", file=f)
596
  print(f"using SymbolicRegression", file=f)
597
  print(f'include("{_escape_filename(hyperparam_filename)}")', file=f)
598
  print(f'include("{_escape_filename(dataset_filename)}")', file=f)