Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop support for Python 3.7, increase minimum versions for SciPy/NumPy #927

Merged
merged 2 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repos:
rev: v3.15.0
hooks:
- id: pyupgrade
args: [--py37-plus]
args: [--py38-plus]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
Expand Down
6 changes: 1 addition & 5 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ stages:
vmImage: 'ubuntu-latest'
strategy:
matrix:
Python37:
python.version: '3.7'
Python38:
python.version: '3.8'

Expand All @@ -84,7 +82,7 @@ stages:
displayName: 'Install dependencies'
- script: |
python -m pip install --upgrade build pip wheel
python -m pip install asteval==0.9.28 numpy==1.19.0 scipy==1.6.0 uncertainties==3.1.4
python -m pip install asteval==0.9.28 numpy==1.23.0 scipy==1.8.0 uncertainties==3.1.4
displayName: 'Install minimum required version of dependencies'
- script: |
python -m build
Expand Down Expand Up @@ -119,8 +117,6 @@ stages:
vmImage: 'ubuntu-latest'
strategy:
matrix:
Python37:
python.version: '3.7'
Python38:
python.version: '3.8'
Python39:
Expand Down
6 changes: 3 additions & 3 deletions doc/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ Downloading and Installation
Prerequisites
~~~~~~~~~~~~~

Lmfit works with `Python`_ versions 3.7 and higher. Version
Lmfit works with `Python`_ versions 3.8 and higher. Version
0.9.15 is the final version to support Python 2.7.

Lmfit requires the following Python packages, with versions given:
* `NumPy`_ version 1.19 or higher.
* `SciPy`_ version 1.6 or higher.
* `NumPy`_ version 1.23 or higher.
* `SciPy`_ version 1.8 or higher.
* `asteval`_ version 0.9.28 or higher.
* `uncertainties`_ version 3.1.4 or higher.

Expand Down
25 changes: 18 additions & 7 deletions doc/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,24 @@ significant to the use and behavior of the library. This is not meant
to be a comprehensive list of changes. For such a complete record,
consult the `lmfit GitHub repository`_.

.. _whatsnew_130_label:

Version 1.3.0 Release Notes (TBD)
=================================

New features:

Bug fixes/enhancements:

Maintenance/Deprecations:

- drop support for Python 3.7 that reached EOL on 2023-06-27 (PR #927)


.. _whatsnew_122_label:

Version 1.2.2 Release Notes (July 14, 2023)
=================================================
===========================================

New features:

Expand Down Expand Up @@ -56,13 +70,10 @@ Bug fixes/enhancements:
improved, and create fewer entries in the Table of Contents for Jupyter lab.
(Issue #884; PR #883; PR #902)




.. _whatsnew_121_label:

Version 1.2.1 Release Notes (May 02, 2023)
=================================================
==========================================

Bug fixes/enhancements:

Expand All @@ -75,7 +86,7 @@ Bug fixes/enhancements:
.. _whatsnew_120_label:

Version 1.2.0 Release Notes (April 05, 2023)
=================================================
============================================

New features:

Expand Down Expand Up @@ -110,7 +121,7 @@ Bug fixes/enhancements:
.. _whatsnew_110_label:

Version 1.1.0 Release Notes (November 27, 2022)
=================================================
===============================================

New features:

Expand Down
28 changes: 7 additions & 21 deletions lmfit/minimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import warnings

import numpy as np
from scipy import __version__ as scipy_version
from scipy.linalg import LinAlgError, inv
from scipy.optimize import basinhopping as scipy_basinhopping
from scipy.optimize import brute as scipy_brute
Expand Down Expand Up @@ -1735,12 +1734,8 @@ def basinhopping(self, params=None, max_nfev=None, **kws):
basinhopping_kws = dict(niter=100, T=1.0, stepsize=0.5,
minimizer_kwargs=None, take_step=None,
accept_test=None, callback=None, interval=50,
disp=False, niter_success=None, seed=None)

# FIXME: update when SciPy requirement is >= 1.8
if int(scipy_version.split('.')[1]) >= 8:
basinhopping_kws.update({'target_accept_rate': 0.5,
'stepwise_factor': 0.9})
disp=False, niter_success=None, seed=None,
target_accept_rate=0.5, stepwise_factor=0.9)

basinhopping_kws.update(self.kws)
basinhopping_kws.update(kws)
Expand Down Expand Up @@ -2118,14 +2113,10 @@ def shgo(self, params=None, max_nfev=None, **kws):

self.set_max_nfev(max_nfev, 200000*(result.nvarys+1))

shgo_kws = dict(constraints=None, n=100, iters=1, callback=None,
shgo_kws = dict(constraints=None, n=None, iters=1, callback=None,
minimizer_kwargs=None, options=None,
sampling_method='simplicial')

# FIXME: update when SciPy requirement is >= 1.7
if int(scipy_version.split('.')[1]) >= 7:
shgo_kws['n'] = None

shgo_kws.update(self.kws)
shgo_kws.update(kws)

Expand Down Expand Up @@ -2196,19 +2187,14 @@ def dual_annealing(self, params=None, max_nfev=None, **kws):
result.method = 'dual_annealing'
self.set_max_nfev(max_nfev, 200000*(result.nvarys+1))

da_kws = dict(maxiter=1000, local_search_options={},
initial_temp=5230.0, restart_temp_ratio=2e-05,
visit=2.62, accept=-5.0, maxfun=2*self.max_nfev,
seed=None, no_local_search=False, callback=None, x0=None)
da_kws = dict(maxiter=1000, minimizer_kwargs=None, initial_temp=5230.0,
restart_temp_ratio=2e-05, visit=2.62, accept=-5.0,
maxfun=2*self.max_nfev, seed=None, no_local_search=False,
callback=None, x0=None)

da_kws.update(self.kws)
da_kws.update(kws)

# FIXME: update when SciPy requirement is >= 1.8
# ``local_search_options`` deprecated in favor of ``minimizer_kwargs``
if int(scipy_version.split('.')[1]) >= 8:
da_kws.update({'minimizer_kwargs': da_kws.pop('local_search_options')})

varying = np.asarray([par.vary for par in self.params.values()])
bounds = np.asarray([(par.min, par.max) for par in
self.params.values()])[varying]
Expand Down
7 changes: 3 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ classifiers =
Operating System :: OS Independent
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Expand All @@ -32,12 +31,12 @@ project_urls =

[options]
packages = find:
python_requires = >=3.7
python_requires = >=3.8
setup_requires = setuptools_scm
install_requires =
asteval>=0.9.28
numpy>=1.19
scipy>=1.6
numpy>=1.23
scipy>=1.8
uncertainties>=3.1.4

[options.packages.find]
Expand Down
11 changes: 1 addition & 10 deletions tests/test_ampgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import numpy as np
from numpy.testing import assert_allclose
import pytest
from scipy import __version__ as scipy_version

import lmfit
from lmfit._ampgo import ampgo, tunnel
Expand Down Expand Up @@ -60,15 +59,7 @@ def test_ampgo_local_solver(minimizer_Alpine02):
"""Test AMPGO algorithm with local solver."""
kws = {'local': 'Nelder-Mead'}

# bounds in Nelder-Mead are supported since SciPy v1.7.0
# FIXME: clean this up after we require SciPy >= 1.7.0
if int(scipy_version.split('.')[1]) < 7:
msg = r'Method Nelder-Mead cannot handle constraints nor bounds'
with pytest.warns(RuntimeWarning, match=msg):
out = minimizer_Alpine02.minimize(method='ampgo', **kws)
else:
out = minimizer_Alpine02.minimize(method='ampgo', **kws)

out = minimizer_Alpine02.minimize(method='ampgo', **kws)
out_x = np.array([out.params['x0'].value, out.params['x1'].value])

assert 'ampgo' and 'Nelder-Mead' in out.method
Expand Down
6 changes: 0 additions & 6 deletions tests/test_basinhopping.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import numpy as np
from numpy.testing import assert_allclose
import pytest
from scipy import __version__ as scipy_version
from scipy.optimize import basinhopping

import lmfit
Expand Down Expand Up @@ -62,11 +61,6 @@ def residual_2d(params):
assert_allclose(out.params['x0'].value, ret.x[0], rtol=1e-5)
assert_allclose(out.params['x1'].value, ret.x[1], rtol=1e-5)

# FIXME: update when SciPy requirement is >= 1.8
if int(scipy_version.split('.')[1]) >= 8:
assert 'target_accept_rate' in out.call_kws
assert 'stepwise_factor' in out.call_kws


def test_basinhopping_Alpine02(minimizer_Alpine02):
"""Test basinhopping on Alpine02 function."""
Expand Down
8 changes: 0 additions & 8 deletions tests/test_dual_annealing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import numpy as np
from numpy.testing import assert_allclose
import scipy
from scipy import __version__ as scipy_version

import lmfit

Expand Down Expand Up @@ -54,13 +53,6 @@ def test_da_Alpine02(minimizer_Alpine02):
assert_allclose(max(out_x), max(global_optimum), rtol=1e-3)
assert out.method == 'dual_annealing'

# FIXME: update when SciPy requirement is >= 1.8
# ``local_search_options`` deprecated in favor of ``minimizer_kwargs``
if int(scipy_version.split('.')[1]) >= 8:
assert 'minimizer_kwargs' in out.call_kws
else:
assert 'local_search_options' in out.call_kws


def test_da_bounds(minimizer_Alpine02):
"""Test dual_annealing algorithm with bounds."""
Expand Down
7 changes: 0 additions & 7 deletions tests/test_shgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from numpy.testing import assert_allclose
import pytest
import scipy
from scipy import __version__ as scipy_version

import lmfit

Expand Down Expand Up @@ -82,12 +81,6 @@ def test_shgo_sobol_Alpine02(minimizer_Alpine02):
assert_allclose(min(out_x), min(global_optimum), rtol=1e-3)
assert_allclose(max(out_x), max(global_optimum), rtol=1e-3)

# FIXME: update when SciPy requirement is >= 1.7
if int(scipy_version.split('.')[1]) >= 7:
assert out.call_kws['n'] is None
else:
assert out.call_kws['n'] == 100


def test_shgo_bounds(minimizer_Alpine02):
"""Test SHGO algorithm with bounds."""
Expand Down