diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b0624df88..1965d7810 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -32,12 +32,12 @@ jobs: strategy: matrix: julia-version: ['1'] - python-version: ['3.11'] + python-version: ['3.12'] os: [ubuntu-latest] test-id: [main] include: - julia-version: '1.6' - python-version: '3.7' + python-version: '3.8' os: ubuntu-latest test-id: include - julia-version: '1' @@ -99,11 +99,11 @@ jobs: strategy: matrix: os: ['ubuntu-latest'] - python-version: ['3.11'] + python-version: ['3.12'] julia-version: ['1'] include: - os: ubuntu-latest - python-version: '3.7' + python-version: '3.8' julia-version: '1.6' steps: - uses: actions/checkout@v4 @@ -122,7 +122,7 @@ jobs: shell: bash -l {0} strategy: matrix: - python-version: ['3.11'] + python-version: ['3.12'] os: ['ubuntu-latest'] steps: @@ -181,8 +181,8 @@ jobs: strategy: matrix: python-version: - - '3.11' - - '3.7' + - '3.12' + - '3.8' os: ['ubuntu-latest'] steps: @@ -199,10 +199,10 @@ jobs: pip install mypy - name: "Install additional dependencies" run: python -m pip install jax jaxlib torch - if: ${{ matrix.python-version != '3.7' }} + if: ${{ matrix.python-version != '3.8' }} - name: "Run mypy" run: python -m mypy --install-types --non-interactive pysr - if: ${{ matrix.python-version != '3.7' }} + if: ${{ matrix.python-version != '3.8' }} - name: "Run compatible mypy" run: python -m mypy --ignore-missing-imports pysr - if: ${{ matrix.python-version == '3.7' }} + if: ${{ matrix.python-version == '3.8' }} diff --git a/.github/workflows/CI_Windows.yml b/.github/workflows/CI_Windows.yml index c687b1790..49b471eed 100644 --- a/.github/workflows/CI_Windows.yml +++ b/.github/workflows/CI_Windows.yml @@ -30,7 +30,7 @@ jobs: strategy: matrix: julia-version: ['1'] - python-version: ['3.11'] + python-version: ['3.12'] os: [windows-latest] steps: diff --git a/.github/workflows/CI_docker_large_nightly.yml b/.github/workflows/CI_docker_large_nightly.yml index 2077247e7..561ce1551 100644 --- a/.github/workflows/CI_docker_large_nightly.yml +++ b/.github/workflows/CI_docker_large_nightly.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: julia-version: ['1.6', '1'] - python-version: ['3.7', '3.11'] + python-version: ['3.8', '3.12'] os: [ubuntu-latest] arch: ['linux/amd64', 'linux/arm64'] diff --git a/.github/workflows/CI_large_nightly.yml b/.github/workflows/CI_large_nightly.yml index 973e25411..cbd9a7ef3 100644 --- a/.github/workflows/CI_large_nightly.yml +++ b/.github/workflows/CI_large_nightly.yml @@ -23,8 +23,8 @@ jobs: strategy: fail-fast: false matrix: - julia-version: ['1.6', '1.8', '1.9'] - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + julia-version: ['1.6', '1.8', '1.10'] + python-version: ['3.8', '3.10', '3.12'] os: [ubuntu-latest, macos-latest, windows-latest] steps: diff --git a/.github/workflows/CI_mac.yml b/.github/workflows/CI_mac.yml index 4c4a52908..e9763f0c7 100644 --- a/.github/workflows/CI_mac.yml +++ b/.github/workflows/CI_mac.yml @@ -30,7 +30,7 @@ jobs: strategy: matrix: julia-version: ['1'] - python-version: ['3.11'] + python-version: ['3.12'] os: [macos-latest] steps: diff --git a/.github/workflows/docker_deploy.yml b/.github/workflows/docker_deploy.yml index 6942944fd..097697010 100644 --- a/.github/workflows/docker_deploy.yml +++ b/.github/workflows/docker_deploy.yml @@ -18,8 +18,8 @@ jobs: matrix: os: [ubuntu-latest] arch: [linux/amd64] - python-version: [3.11.6] - julia-version: [1.9.4] + python-version: [3.12.3] + julia-version: [1.10.3] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/docs/examples.md b/docs/examples.md index a5774a06b..754875e7c 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -520,6 +520,8 @@ a constant `"2.6353e-22[m s⁻²]"`. Note that this expression has a large dynamic range so may be difficult to find. Consider searching with a larger `niterations` if needed. +Note that you can also search for exclusively dimensionless constants by settings +`dimensionless_constants_only` to `true`. ## 11. Additional features diff --git a/environment.yml b/environment.yml index 610662e4d..24ba80de2 100644 --- a/environment.yml +++ b/environment.yml @@ -2,7 +2,7 @@ name: test channels: - conda-forge dependencies: - - python>=3.7 + - python>=3.8 - sympy>=1.0.0,<2.0.0 - pandas>=0.21.0,<3.0.0 - numpy>=1.13.0,<2.0.0 diff --git a/pyproject.toml b/pyproject.toml index f2c465e1a..94851ea67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta" [project] name = "pysr" -version = "0.18.3" +version = "0.18.4" authors = [ {name = "Miles Cranmer", email = "miles.cranmer@gmail.com"}, ] description = "Simple and efficient symbolic regression" readme = {file = "README.md", content-type = "text/markdown"} license = {file = "LICENSE"} -requires-python = ">=3.7" +requires-python = ">=3.8" classifiers = [ "Programming Language :: Python :: 3", "Operating System :: OS Independent", @@ -34,4 +34,5 @@ profile = "black" dev-dependencies = [ "pre-commit>=3.7.0", "ipython>=8.23.0", + "ipykernel>=6.29.4", ] diff --git a/pysr/julia_import.py b/pysr/julia_import.py index dc881bf21..f1c155136 100644 --- a/pysr/julia_import.py +++ b/pysr/julia_import.py @@ -35,32 +35,15 @@ os.environ[k] = os.environ.get(k, default) +autoload_extensions = os.environ.get("PYSR_AUTOLOAD_EXTENSIONS") +if autoload_extensions is not None: + # Deprecated; so just pass to juliacall + os.environ["PYTHON_JULIACALL_AUTOLOAD_IPYTHON_EXTENSION"] = autoload_extensions + from juliacall import Main as jl # type: ignore jl_version = (jl.VERSION.major, jl.VERSION.minor, jl.VERSION.patch) -# Next, automatically load the juliacall extension if we're in a Jupyter notebook -autoload_extensions = os.environ.get("PYSR_AUTOLOAD_EXTENSIONS", "yes") -if autoload_extensions in {"yes", ""} and jl_version >= (1, 9, 0): - try: - get_ipython = sys.modules["IPython"].get_ipython - - if "IPKernelApp" not in get_ipython().config: - raise ImportError("console") - - print( - "Detected Jupyter notebook. Loading juliacall extension. Set `PYSR_AUTOLOAD_EXTENSIONS=no` to disable." - ) - - # TODO: Turn this off if juliacall does this automatically - get_ipython().run_line_magic("load_ext", "juliacall") - except Exception: - pass -elif autoload_extensions not in {"no", "yes", ""}: - warnings.warn( - "PYSR_AUTOLOAD_EXTENSIONS environment variable is set to something other than 'yes' or 'no' or ''." - ) - jl.seval("using SymbolicRegression") SymbolicRegression = jl.SymbolicRegression diff --git a/pysr/juliapkg.json b/pysr/juliapkg.json index 8ce86a56d..db8de4ec1 100644 --- a/pysr/juliapkg.json +++ b/pysr/juliapkg.json @@ -3,7 +3,7 @@ "packages": { "SymbolicRegression": { "uuid": "8254be44-1295-4e6a-a16d-46603ac705cb", - "version": "=0.24.3" + "version": "=0.24.4" }, "Serialization": { "uuid": "9e88b42a-f829-5b0c-bbe9-9e923198166b", diff --git a/pysr/param_groupings.yml b/pysr/param_groupings.yml index 5c7dc4780..0ff9d63da 100644 --- a/pysr/param_groupings.yml +++ b/pysr/param_groupings.yml @@ -14,6 +14,7 @@ - loss_function - model_selection - dimensional_constraint_penalty + - dimensionless_constants_only - Working with Complexities: - parsimony - constraints diff --git a/pysr/sr.py b/pysr/sr.py index cb3a05c66..2f976ec1e 100644 --- a/pysr/sr.py +++ b/pysr/sr.py @@ -328,6 +328,9 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator): dimensional_constraint_penalty : float Additive penalty for if dimensional analysis of an expression fails. By default, this is `1000.0`. + dimensionless_constants_only : bool + Whether to only search for dimensionless constants, if using units. + Default is `False`. use_frequency : bool Whether to measure the frequency of complexities, and use that instead of parsimony to explore equation space. Will naturally @@ -688,6 +691,7 @@ def __init__( complexity_of_variables: Union[int, float] = 1, parsimony: float = 0.0032, dimensional_constraint_penalty: Optional[float] = None, + dimensionless_constants_only: bool = False, use_frequency: bool = True, use_frequency_in_tournament: bool = True, adaptive_parsimony_scaling: float = 20.0, @@ -783,6 +787,7 @@ def __init__( self.complexity_of_variables = complexity_of_variables self.parsimony = parsimony self.dimensional_constraint_penalty = dimensional_constraint_penalty + self.dimensionless_constants_only = dimensionless_constants_only self.use_frequency = use_frequency self.use_frequency_in_tournament = use_frequency_in_tournament self.adaptive_parsimony_scaling = adaptive_parsimony_scaling @@ -1654,6 +1659,7 @@ def _run(self, X, y, mutated_params, weights, seed): # These have the same name: parsimony=self.parsimony, dimensional_constraint_penalty=self.dimensional_constraint_penalty, + dimensionless_constants_only=self.dimensionless_constants_only, alpha=self.alpha, maxdepth=maxdepth, fast_cycle=self.fast_cycle, diff --git a/pysr/test/test_startup.py b/pysr/test/test_startup.py index 3d716b515..8a93ad3a3 100644 --- a/pysr/test/test_startup.py +++ b/pysr/test/test_startup.py @@ -118,10 +118,6 @@ def test_bad_startup_options(self): code="import juliacall; import pysr", msg="juliacall module already imported.", ), - dict( - code='import os; os.environ["PYSR_AUTOLOAD_EXTENSIONS"] = "foo"; import pysr', - msg="PYSR_AUTOLOAD_EXTENSIONS environment variable is set", - ), ] for warning_test in warning_tests: result = subprocess.run( diff --git a/requirements.txt b/requirements.txt index 27e6ce514..58ee1c411 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ sympy>=1.0.0,<2.0.0 pandas>=0.21.0,<3.0.0 numpy>=1.13.0,<2.0.0 scikit_learn>=1.0.0,<2.0.0 -juliacall==0.9.19 +juliacall==0.9.20 click>=7.0.0,<9.0.0 setuptools>=50.0.0 typing_extensions>=4.0.0,<5.0.0; python_version < "3.8"