diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1c4202393..25a1d07bb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -80,7 +80,7 @@ repos: - --blank exclude: src/optimagic/optimization/algo_options.py - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.2 + rev: v0.9.2 hooks: # Run the linter. - id: ruff @@ -97,7 +97,7 @@ repos: - pyi - jupyter - repo: https://github.com/executablebooks/mdformat - rev: 0.7.18 + rev: 0.7.21 hooks: - id: mdformat additional_dependencies: @@ -109,7 +109,7 @@ repos: - '88' files: (README\.md) - repo: https://github.com/executablebooks/mdformat - rev: 0.7.18 + rev: 0.7.21 hooks: - id: mdformat additional_dependencies: @@ -121,7 +121,7 @@ repos: files: (docs/.) exclude: docs/source/how_to/how_to_specify_algorithm_and_algo_options.md - repo: https://github.com/kynan/nbstripout - rev: 0.8.0 + rev: 0.8.1 hooks: - id: nbstripout exclude: | @@ -132,7 +132,7 @@ repos: args: - --drop-empty-cells - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.13.0 + rev: v1.14.1 hooks: - id: mypy files: src|tests diff --git a/.tools/envs/testenv-linux.yml b/.tools/envs/testenv-linux.yml index 68a5bdb8b..3ebccc613 100644 --- a/.tools/envs/testenv-linux.yml +++ b/.tools/envs/testenv-linux.yml @@ -23,7 +23,7 @@ dependencies: - scipy>=1.2.1 # run, tests - sqlalchemy # run, tests - seaborn # dev, tests - - mypy=1.13 # dev, tests + - mypy=1.14.1 # dev, tests - pyyaml # dev, tests - jinja2 # dev, tests - annotated-types # dev, tests diff --git a/.tools/envs/testenv-numpy.yml b/.tools/envs/testenv-numpy.yml index 0e811e586..869502f63 100644 --- a/.tools/envs/testenv-numpy.yml +++ b/.tools/envs/testenv-numpy.yml @@ -21,7 +21,7 @@ dependencies: - scipy>=1.2.1 # run, tests - sqlalchemy # run, tests - seaborn # dev, tests - - mypy=1.13 # dev, tests + - mypy=1.14.1 # dev, tests - pyyaml # dev, tests - jinja2 # dev, tests - annotated-types # dev, tests diff --git a/.tools/envs/testenv-others.yml b/.tools/envs/testenv-others.yml index 33be68ecc..86838f4cc 100644 --- a/.tools/envs/testenv-others.yml +++ b/.tools/envs/testenv-others.yml @@ -21,7 +21,7 @@ dependencies: - scipy>=1.2.1 # run, tests - sqlalchemy # run, tests - seaborn # dev, tests - - mypy=1.13 # dev, tests + - mypy=1.14.1 # dev, tests - pyyaml # dev, tests - jinja2 # dev, tests - annotated-types # dev, tests diff --git a/.tools/envs/testenv-pandas.yml b/.tools/envs/testenv-pandas.yml index 23f5f4055..d1298314b 100644 --- a/.tools/envs/testenv-pandas.yml +++ b/.tools/envs/testenv-pandas.yml @@ -21,7 +21,7 @@ dependencies: - scipy>=1.2.1 # run, tests - sqlalchemy # run, tests - seaborn # dev, tests - - mypy=1.13 # dev, tests + - mypy=1.14.1 # dev, tests - pyyaml # dev, tests - jinja2 # dev, tests - annotated-types # dev, tests diff --git a/docs/source/development/ep-01-pytrees.md b/docs/source/development/ep-01-pytrees.md index c04dd0fb3..051db4de4 100644 --- a/docs/source/development/ep-01-pytrees.md +++ b/docs/source/development/ep-01-pytrees.md @@ -384,7 +384,7 @@ much complexity by avoiding complex pytrees as inputs and outputs at the same ti To see this in action, let's look at an example. We repeat the example from the JAX interface above with the following changes: -1. The 1d numpy array in x\["a"\] is replaced by a DataFrame with `"value"` column +1. The 1d numpy array in x["a"] is replaced by a DataFrame with `"value"` column 1. The "d" entry in the output becomes a Series instead of a 1d numpy array. ```python @@ -460,7 +460,7 @@ very first jacobian: +--------+----------+----------+----------+----------+----------+----------+----------+ ``` -The indices \["j", "k", "l", "m"\] unfortunately never made it into the result because +The indices ["j", "k", "l", "m"] unfortunately never made it into the result because they were only applied to elements that already came from a 2d array and thus always have a 3d Jacobian, i.e. the result entry `["c"][b"]` is a reshaped version of the upper right 2 by 4 array and the result entry `["d"]["b"]` is a reshaped version of the lower diff --git a/docs/source/how_to/how_to_scaling.md b/docs/source/how_to/how_to_scaling.md index f24eb091c..91d820d00 100644 --- a/docs/source/how_to/how_to_scaling.md +++ b/docs/source/how_to/how_to_scaling.md @@ -90,7 +90,7 @@ these considerations are typically tighter than for parameters that have a small on the objective function. Thus, a natural approach to improve the scaling of the optimization problem is to re-map -all parameters such that the bounds are \[0, 1\] for all parameters. This has the +all parameters such that the bounds are [0, 1] for all parameters. This has the additional advantage that absolute and relative convergence criteria on parameter changes become the same. @@ -164,7 +164,7 @@ to a strictly non-zero number for the `"start_values"` and `"gradient"` approach `"bounds"` approach avoids division by exact zeros by construction. The `"clipping_value"` can still be used to avoid extreme upscaling of parameters with very tight bounds. However, this means that the bounds of the re-scaled problem are not -exactly \[0, 1\] for all parameters. +exactly [0, 1] for all parameters. (scaling-default-values)= diff --git a/environment.yml b/environment.yml index 89514bbba..c9e4bea39 100644 --- a/environment.yml +++ b/environment.yml @@ -31,7 +31,7 @@ dependencies: - sphinx-panels # docs - sphinxcontrib-bibtex # docs - seaborn # dev, tests - - mypy=1.13 # dev, tests + - mypy=1.14.1 # dev, tests - pyyaml # dev, tests - jinja2 # dev, tests - furo # dev, docs diff --git a/src/optimagic/differentiation/derivatives.py b/src/optimagic/differentiation/derivatives.py index 065b9b2ad..f0f9d7653 100644 --- a/src/optimagic/differentiation/derivatives.py +++ b/src/optimagic/differentiation/derivatives.py @@ -328,7 +328,7 @@ def first_derivative( f0 = np.array(f0, dtype=np.float64) # convert the raw evaluations to numpy arrays - raw_evals = _convert_evals_to_numpy( + raw_evals_arr = _convert_evals_to_numpy( raw_evals=raw_evals, unpacker=unpacker, registry=registry, @@ -337,9 +337,9 @@ def first_derivative( ) # apply finite difference formulae - evals_data = np.array(raw_evals).reshape(2, n_steps, len(x), -1) - evals_data = np.transpose(evals_data, axes=(0, 1, 3, 2)) - evals = Evals(pos=evals_data[0], neg=evals_data[1]) + evals_data = np.array(raw_evals_arr).reshape(2, n_steps, len(x), -1) + evals_data_transposed = np.transpose(evals_data, axes=(0, 1, 3, 2)) + evals = Evals(pos=evals_data_transposed[0], neg=evals_data_transposed[1]) jac_candidates = {} for m in ["forward", "backward", "central"]: diff --git a/src/optimagic/differentiation/generate_steps.py b/src/optimagic/differentiation/generate_steps.py index a642650e7..ca34b5769 100644 --- a/src/optimagic/differentiation/generate_steps.py +++ b/src/optimagic/differentiation/generate_steps.py @@ -90,9 +90,9 @@ def generate_steps( ) min_steps = base_steps if min_steps is None else min_steps - assert ( - bounds.upper - bounds.lower >= 2 * min_steps - ).all(), "min_steps is too large to fit into bounds." + assert (bounds.upper - bounds.lower >= 2 * min_steps).all(), ( + "min_steps is too large to fit into bounds." + ) upper_step_bounds = bounds.upper - x lower_step_bounds = bounds.lower - x diff --git a/src/optimagic/differentiation/richardson_extrapolation.py b/src/optimagic/differentiation/richardson_extrapolation.py index 3aa189527..a22319cb9 100644 --- a/src/optimagic/differentiation/richardson_extrapolation.py +++ b/src/optimagic/differentiation/richardson_extrapolation.py @@ -52,13 +52,13 @@ def richardson_extrapolation(sequence, steps, method="central", num_terms=None): n_steps = steps.shape[0] num_terms = n_steps if num_terms is None else num_terms - assert ( - seq_len == n_steps - ), "Length of ``steps`` must coincide with length of ``sequence``." + assert seq_len == n_steps, ( + "Length of ``steps`` must coincide with length of ``sequence``." + ) assert num_terms > 0, "``num_terms`` must be greater than zero." - assert ( - seq_len - 1 >= num_terms - ), "``num_terms`` cannot be greater than ``seq_len`` - 1." + assert seq_len - 1 >= num_terms, ( + "``num_terms`` cannot be greater than ``seq_len`` - 1." + ) step_ratio = _compute_step_ratio(steps) order, exponentiation_step = _get_order_and_exponentiation_step(method) diff --git a/src/optimagic/examples/criterion_functions.py b/src/optimagic/examples/criterion_functions.py index 5474a76f4..1ac139c6a 100644 --- a/src/optimagic/examples/criterion_functions.py +++ b/src/optimagic/examples/criterion_functions.py @@ -114,7 +114,7 @@ def rosenbrock_gradient(params: PyTree) -> PyTree: l4 = np.delete(x, [0]) l4 = np.append(l4, 0) l5 = np.full((len(x) - 1), 2) - l5 = np.append(l5, 0) + l5 = np.append(l5, 0) # type: ignore[assignment] flat = 100 * (4 * (l1**3) + 2 * l2 - 2 * (l3**2) - 4 * (l4 * x)) + 2 * l1 - l5 return _unflatten_gradient(flat, params) diff --git a/src/optimagic/logging/logger.py b/src/optimagic/logging/logger.py index 5cd80fa6a..7165022a4 100644 --- a/src/optimagic/logging/logger.py +++ b/src/optimagic/logging/logger.py @@ -187,7 +187,10 @@ def _build_history_dataframe(self) -> pd.DataFrame: times = np.array(history["time"]) times -= times[0] - history["time"] = times.tolist() + # For numpy arrays with ndim = 0, tolist() returns a scalar, which violates the + # type hinting list[Any] from above. As history["time"] is always a list, this + # case is safe to ignore. + history["time"] = times.tolist() # type: ignore[assignment] df = pd.DataFrame(history) df = df.merge( diff --git a/src/optimagic/optimization/multistart.py b/src/optimagic/optimization/multistart.py index c3d4cf3e1..9b7544430 100644 --- a/src/optimagic/optimization/multistart.py +++ b/src/optimagic/optimization/multistart.py @@ -313,9 +313,11 @@ def run_explorations( """ internal_problem = internal_problem.with_step_id(step_id) - x_list = list(sample) + x_list: list[NDArray[np.float64]] = list(sample) - raw_values = np.array(internal_problem.exploration_fun(x_list, n_cores=n_cores)) + raw_values = np.asarray( + internal_problem.exploration_fun(x_list, n_cores=n_cores), dtype=np.float64 + ) is_valid = np.isfinite(raw_values) diff --git a/src/optimagic/optimization/optimize_result.py b/src/optimagic/optimization/optimize_result.py index 5b692fc92..f2895cf53 100644 --- a/src/optimagic/optimization/optimize_result.py +++ b/src/optimagic/optimization/optimize_result.py @@ -173,8 +173,7 @@ def __repr__(self) -> str: if self.start_fun is not None and self.fun is not None: improvement = ( - f"The value of criterion improved from {self.start_fun} to " - f"{self.fun}." + f"The value of criterion improved from {self.start_fun} to {self.fun}." ) else: improvement = None diff --git a/src/optimagic/optimizers/nag_optimizers.py b/src/optimagic/optimizers/nag_optimizers.py index e708b5915..8c8d6875a 100644 --- a/src/optimagic/optimizers/nag_optimizers.py +++ b/src/optimagic/optimizers/nag_optimizers.py @@ -1051,8 +1051,9 @@ def _build_options_dict(user_input, default_options): invalid = [x for x in user_input if x not in full_options] if len(invalid) > 0: raise ValueError( - f"You specified illegal options {', '.join(invalid)}. Allowed are: " - ", ".join(full_options.keys()) + f"You specified illegal options {', '.join(invalid)}. Allowed are: , ".join( + full_options.keys() + ) ) full_options.update(user_input) return full_options diff --git a/src/optimagic/optimizers/scipy_optimizers.py b/src/optimagic/optimizers/scipy_optimizers.py index d980183c8..1aea5d32c 100644 --- a/src/optimagic/optimizers/scipy_optimizers.py +++ b/src/optimagic/optimizers/scipy_optimizers.py @@ -799,11 +799,7 @@ def _solve_internal_problem( ) raw_res = scipy.optimize.brute( func=problem.fun, - ranges=tuple( - map( - tuple, np.column_stack((problem.bounds.lower, problem.bounds.upper)) - ) - ), + ranges=tuple(zip(problem.bounds.lower, problem.bounds.upper, strict=True)), Ns=self.n_grid_points, full_output=True, finish=self.polishing_function, diff --git a/src/optimagic/parameters/consolidate_constraints.py b/src/optimagic/parameters/consolidate_constraints.py index 2282d4a60..21e52a90f 100644 --- a/src/optimagic/parameters/consolidate_constraints.py +++ b/src/optimagic/parameters/consolidate_constraints.py @@ -205,9 +205,9 @@ def _consolidate_fixes_with_equality_constraints( for eq in equality_constraints: if np.isfinite(fixed_value[eq["index"]]).any(): valcounts = _unique_values(fixed_value[eq["index"]]) - assert ( - len(valcounts) == 1 - ), "Equality constrained parameters cannot be fixed to different values." + assert len(valcounts) == 1, ( + "Equality constrained parameters cannot be fixed to different values." + ) fixed_value[eq["index"]] = valcounts[0] return fixed_value diff --git a/tests/estimagic/test_bootstrap.py b/tests/estimagic/test_bootstrap.py index 7fa1380e2..79edc4680 100644 --- a/tests/estimagic/test_bootstrap.py +++ b/tests/estimagic/test_bootstrap.py @@ -123,16 +123,16 @@ def test_bootstrap_existing_outcomes(setup): result = bootstrap( data=setup["df"], outcome=_outcome_func, - n_draws=2, + n_draws=3, ) - assert len(result.outcomes) == 2 + assert len(result.outcomes) == 3 result = bootstrap( outcome=_outcome_func, data=setup["df"], existing_result=result, - n_draws=1, + n_draws=2, ) - assert len(result.outcomes) == 1 + assert len(result.outcomes) == 2 def test_bootstrap_from_outcomes(setup, expected): diff --git a/tests/estimagic/test_estimation_table.py b/tests/estimagic/test_estimation_table.py index 6e1b3baae..e7a935182 100644 --- a/tests/estimagic/test_estimation_table.py +++ b/tests/estimagic/test_estimation_table.py @@ -395,7 +395,7 @@ def test_get_model_names(): def test_get_default_column_names_and_groups(): model_names = ["a_name", "a_name", "(3)", "(4)", "third_name"] res_names, res_groups = _get_default_column_names_and_groups(model_names) - exp_names = [f"({i+1})" for i in range(len(model_names))] + exp_names = [f"({i + 1})" for i in range(len(model_names))] exp_groups = ["a_name", "a_name", "(3)", "(4)", "third_name"] assert res_names == exp_names assert res_groups == exp_groups diff --git a/tests/optimagic/optimization/test_with_constraints.py b/tests/optimagic/optimization/test_with_constraints.py index 8c2836857..dc0f4fd8a 100644 --- a/tests/optimagic/optimization/test_with_constraints.py +++ b/tests/optimagic/optimization/test_with_constraints.py @@ -244,7 +244,10 @@ def test_three_independent_constraints(): ) expected = np.array([0] * 4 + [4, 5] + [0] + [7.5] * 2 + [0]) - aaae(res.params, expected, decimal=4) + # TODO: Increase precision back to decimal=4. The reduced precision is likely due + # to the re-written L-BFGS-B algorithm in SciPy 1.15. + # See https://github.com/optimagic-dev/optimagic/issues/556. + aaae(res.params, expected, decimal=3) INVALID_CONSTRAINT_COMBIS = [