diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4ba9c6fb..24e48211 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-added-large-files - id: check-merge-conflict @@ -12,7 +12,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.9.10 + rev: v0.16.2 hooks: # Run the linter. - id: ruff diff --git a/doc/conf.py b/doc/conf.py index 41c41496..25753aa7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -31,7 +31,7 @@ # -- Custom pre-build -------------------------------------------------------- -subprocess.run([sys.executable, "md2rst.py"]) # noqa: S603 +subprocess.run([sys.executable, "md2rst.py"], check=True) # -- General configuration --------------------------------------------------- diff --git a/doc/example/example_visualization.ipynb b/doc/example/example_visualization.ipynb index f1d89b13..abc97cfa 100644 --- a/doc/example/example_visualization.ipynb +++ b/doc/example/example_visualization.ipynb @@ -2,6 +2,9 @@ "cells": [ { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "# Visualization of data and simulations\n", "\n", @@ -10,23 +13,23 @@ "Some basic visualizations can be generated from a PEtab problem directly, without the need for a visualization specification file. This is illustrated in the first part of this notebook. For more advanced visualizations, a visualization specification file is required. This is illustrated in the second part of this notebook.\n", "\n", "For the following demonstrations, we will use two example problems obtained from the [Benchmark collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/), [Fujita_SciSignal2010](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/master/Benchmark-Models/Fujita_SciSignal2010) and [Isensee_JCB2018](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/master/Benchmark-Models/Isensee_JCB2018). Their specifics don't matter for the purpose of this notebook—we just need some PEtab problems to work with." - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "import matplotlib.pyplot as plt\n", + "from petab.visualize import plot_problem\n", "\n", "import petab\n", "from petab import Problem\n", - "from petab.visualize import plot_problem\n", "\n", "example_dir_fujita = Path(\"example_Fujita\")\n", "petab_yaml_fujita = example_dir_fujita / \"Fujita.yaml\"\n", @@ -40,34 +43,34 @@ "plt.rcParams[\"font.size\"] = 12\n", "plt.rcParams[\"figure.dpi\"] = 150\n", "plt.rcParams[\"legend.fontsize\"] = 10" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", - "source": [ - "## Visualization without visualization specification file" - ], "metadata": { "collapsed": false - } + }, + "source": [ + "## Visualization without visualization specification file" + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "### Plotting measurements\n", "\n", "For the most basic visualization, we can use the [plot_problem()](https://petab.readthedocs.io/projects/libpetab-python/en/latest/build/_autosummary/petab.visualize.html#petab.visualize.plot_problem) function." - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "# load PEtab problem\n", @@ -75,13 +78,13 @@ "\n", "# plot measurements\n", "petab.visualize.plot_problem(petab_problem);" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "As nothing was specified regarding what should be plotted, the defaults were used. Namely, it was assumed that measurements are time series data, and they were grouped by observables.\n", "\n", @@ -92,14 +95,14 @@ "#### By observable\n", "\n", "We can specify how many subplots there should be and what should be plotted on each of them. It can easily be done by providing `grouping_list`, which by default specifies, which observables should be plotted on a particular plot. The value of `grouping_list` should be a list of lists, each sublist corresponds to a separate plot." - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab.visualize.plot_problem(\n", @@ -108,25 +111,25 @@ " group_by=\"observable\",\n", ")\n", "plt.gcf().set_size_inches(10, 4)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "#### By simulation condition\n", "\n", "Another option is to specify which simulation conditions should be plotted:" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab.visualize.plot_problem(\n", @@ -138,25 +141,25 @@ " ],\n", " group_by=\"simulation\",\n", ");" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "#### By datasetId\n", "\n", "Finally, measurements can be grouped by `datasetId`s as specified in the measurements table, by passing lists of `datasetId`s. Each sublist corresponds to a subplot:" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab.visualize.plot_problem(\n", @@ -189,25 +192,25 @@ " ],\n", " group_by=\"dataset\",\n", ");" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "### Plotting simulations\n", "\n", "We can also plot simulations together with the measurements, for example, to judge the model fit. For this, we need to provide a simulation file as `simulations_df`. A simulation file has the same format as the measurement file, but instead of the `measurement` column, it contains simulation outputs in the `simulation` column. The simulations are plotted as solid lines, while the measurements are plotted as dashed lines:" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "simu_file_Fujita = example_dir_fujita / \"Fujita_simulatedData.tsv\"\n", @@ -225,185 +228,185 @@ " plotted_noise=\"provided\",\n", ")\n", "plt.gcf().set_size_inches(10, 4)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", - "source": [ - "It is also possible to plot only the simulations without the measurements by setting `petab_problem.measurement_df = None`." - ], "metadata": { "collapsed": false - } + }, + "source": [ + "It is also possible to plot only the simulations without the measurements by setting `petab_problem.measurement_df = None`." + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "## Visualization with a visualization specification file\n", "\n", "As described in the [PEtab documentation](https://petab.readthedocs.io/en/latest/documentation_data_format.html), the visualization specification file is a tab-separated value file specifying which data to plot in which way. In the following, we will build up a visualization specification file step by step." - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", - "source": [ - "Without a visualization file, the independent variable defaults to time, and each observable is plotted in a separate subplot:" - ], "metadata": { "collapsed": false - } + }, + "source": [ + "Without a visualization file, the independent variable defaults to time, and each observable is plotted in a separate subplot:" + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab_problem = Problem.from_yaml(petab_yaml_fujita)\n", "petab.visualize.plot_problem(petab_problem);" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "First, let us create a visualization specification file with only mandatory columns. In fact, there is only one mandatory column: `plotId`.\n", "The most basic visualization file looks like this:" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab_problem.visualization_df = petab.get_visualization_df(\n", " example_dir_fujita / \"visuSpecs\" / \"Fujita_visuSpec_mandatory.tsv\"\n", ")\n", "petab_problem.visualization_df" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", - "source": [ - "This way, all data will be shown in a single plot, taking time as independent variable. This is not very appealing yet, but we will improve it step by step." - ], "metadata": { "collapsed": false - } + }, + "source": [ + "This way, all data will be shown in a single plot, taking time as independent variable. This is not very appealing yet, but we will improve it step by step." + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab.visualize.plot_problem(petab_problem)\n", "plt.gcf().set_size_inches(10, 4)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "### Logarithmic scale and offset\n", "\n", "Let's change some settings. For example, we can change the scale of the y-axis to logarithmic and apply an offset for the independent variable:" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab_problem.visualization_df = petab.get_visualization_df(\n", " example_dir_fujita / \"visuSpecs\" / \"Fujita_visuSpec_1.tsv\"\n", ")\n", "petab_problem.visualization_df" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab.visualize.plot_problem(petab_problem)\n", "plt.gcf().set_size_inches(10, 4)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "### Subplots by observable\n", "\n", "Next, to make the plot less crowded, we group the measurements by observables by adding two subplots and specifying which observables to plot on each via the `yValues` column:" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab_problem.visualization_df = petab.get_visualization_df(\n", " example_dir_fujita / \"visuSpecs\" / \"Fujita_visuSpec_2.tsv\"\n", ")\n", "petab_problem.visualization_df" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab.visualize.plot_problem(petab_problem)\n", "plt.gcf().set_size_inches(10, 4)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "### Subplots by dataset\n", "\n", "We can also plot different datasets (as specified by the optional `datasetId` column in the measurement table) in separate subplots:" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab_problem.visualization_df = petab.get_visualization_df(\n", @@ -412,74 +415,74 @@ " / \"Fujita_visuSpec_individual_datasets.tsv\"\n", ")\n", "petab_problem.visualization_df" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab.visualize.plot_problem(petab_problem)\n", "plt.gcf().set_size_inches(10, 4)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "### Legend entries\n", "\n", "So far, the legend entries don't look very nice. We can change them by specifying the desired labels in the `legendEntries` column:" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab_problem.visualization_df = petab.get_visualization_df(\n", " example_dir_fujita / \"visuSpecs\" / \"Fujita_visuSpec_datasetIds.tsv\"\n", ")\n", "petab_problem.visualization_df" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab.visualize.plot_problem(petab_problem)\n", "plt.gcf().set_size_inches(10, 6)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "### Plotting individual replicates\n", "\n", "If the measurement file contains replicates, the replicates can also be visualized individually by setting the value for `plotTypeData` to `replicate`. Below, you can see the same measurement data plotted as mean and standard deviations (left) and as replicates (right):" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab_problem = Problem.from_yaml(petab_yaml_isensee)\n", @@ -491,25 +494,25 @@ " simulations_df=example_dir_isensee / \"Isensee_simulationData.tsv\",\n", ")\n", "plt.gcf().set_size_inches(16, 9)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "### Scatter plots\n", "\n", "If both measurements and simulated data are available, they can be visualized as scatter plot by setting `plotTypeSimulation` to `ScatterPlot`:" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab_problem = Problem.from_yaml(petab_yaml_isensee)\n", @@ -521,25 +524,25 @@ " simulations_df=example_dir_isensee / \"Isensee_simulationData.tsv\",\n", ")\n", "plt.gcf().set_size_inches(10, 4)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "### Further examples\n", "\n", "Here are some further visualization examples, including barplots (by setting `plotTypeSimulation` to `BarPlot`):" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "petab_problem = petab.Problem.from_yaml(petab_yaml_isensee_vis)\n", @@ -548,30 +551,27 @@ " simulations_df=example_dir_isensee / \"Isensee_simulationData.tsv\",\n", ")\n", "plt.gcf().set_size_inches(20, 12)" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "markdown", + "metadata": { + "collapsed": false + }, "source": [ "Also with a visualization file, there is the option to plot only simulations, only measurements, or both, as was illustrated above in the examples without a visualization file.\n", "\n", "Refer to the [PEtab documentation](https://petab.readthedocs.io/en/latest/documentation_data_format.html) for descriptions of all possible settings. If you have any questions or encounter some problems, please create a GitHub [issue](https://github.com/PEtab-dev/libpetab-python/issues). We will be happy to help!" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, - "outputs": [], - "source": [], "metadata": { "collapsed": false - } + }, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/petab/petablint.py b/petab/petablint.py index afc481e2..6ff52826 100755 --- a/petab/petablint.py +++ b/petab/petablint.py @@ -5,6 +5,7 @@ import argparse import logging import sys +import typing import pydantic from colorama import Fore @@ -22,10 +23,10 @@ class LintFormatter(logging.Formatter): """Custom log formatter""" - formats = { + formats: typing.ClassVar[dict[int, str]] = { logging.DEBUG: Fore.CYAN + "%(message)s", logging.INFO: Fore.GREEN + "%(message)s", - logging.WARN: Fore.YELLOW + "%(message)s", + logging.WARNING: Fore.YELLOW + "%(message)s", logging.ERROR: Fore.RED + "%(message)s", } @@ -155,7 +156,7 @@ def main(): if args.verbose: ch.setLevel(logging.DEBUG) else: - ch.setLevel(logging.WARN) + ch.setLevel(logging.WARNING) if args.yaml_file_name: try: @@ -167,7 +168,7 @@ def main(): path = list(e.absolute_path) path = ( f" at {path[0]}" - + "".join(f"[{str(p)}]" for p in path[1:]) + + "".join(f"[{p!s}]" for p in path[1:]) + ": " ) logger.error( diff --git a/petab/v1/__init__.py b/petab/v1/__init__.py index cd21b88a..9402fe03 100644 --- a/petab/v1/__init__.py +++ b/petab/v1/__init__.py @@ -3,23 +3,23 @@ Contains all functionality related to handling PEtab 1.0 problems. """ -from ..version import __version__ # noqa: F401, E402 -from . import models # noqa: F401, E402 -from .C import * # noqa: F403, F401, E402 -from .calculate import * # noqa: F403, F401, E402 -from .composite_problem import * # noqa: F403, F401, E402 -from .conditions import * # noqa: F403, F401, E402 -from .core import * # noqa: F403, F401, E402 -from .format_version import __format_version__ # noqa: F401, E402 -from .lint import * # noqa: F403, F401, E402 -from .mapping import * # noqa: F403, F401, E402 -from .measurements import * # noqa: F403, F401, E402 -from .models import Model # noqa: F401, E402 -from .observables import * # noqa: F403, F401, E402 -from .parameter_mapping import * # noqa: F403, F401, E402 -from .parameters import * # noqa: F403, F401, E402 -from .problem import * # noqa: F403, F401, E402 -from .sampling import * # noqa: F403, F401, E402 -from .sbml import * # noqa: F403, F401, E402 -from .simulate import * # noqa: F403, F401, E402 -from .yaml import * # noqa: F403, F401, E402 +from ..version import __version__ # noqa: F401 +from . import models # noqa: F401 +from .C import * +from .calculate import * +from .composite_problem import * +from .conditions import * +from .core import * +from .format_version import __format_version__ # noqa: F401 +from .lint import * +from .mapping import * +from .measurements import * +from .models import Model # noqa: F401 +from .observables import * +from .parameter_mapping import * +from .parameters import * +from .problem import * +from .sampling import * +from .sbml import * +from .simulate import * +from .yaml import * diff --git a/petab/v1/calculate.py b/petab/v1/calculate.py index 131d0d60..46fdcb91 100644 --- a/petab/v1/calculate.py +++ b/petab/v1/calculate.py @@ -14,15 +14,15 @@ from .math import sympify_petab __all__ = [ - "calculate_residuals", - "calculate_residuals_for_table", - "get_symbolic_noise_formulas", - "evaluate_noise_formula", "calculate_chi2", "calculate_chi2_for_table_from_residuals", "calculate_llh", "calculate_llh_for_table", + "calculate_residuals", + "calculate_residuals_for_table", "calculate_single_llh", + "evaluate_noise_formula", + "get_symbolic_noise_formulas", ] diff --git a/petab/v1/composite_problem.py b/petab/v1/composite_problem.py index f887ec03..1cdab0b0 100644 --- a/petab/v1/composite_problem.py +++ b/petab/v1/composite_problem.py @@ -5,7 +5,7 @@ import pandas as pd from . import parameters, problem, yaml -from .C import * # noqa: F403 +from .C import * __all__ = ["CompositeProblem"] @@ -23,7 +23,7 @@ class CompositeProblem: def __init__( self, parameter_df: pd.DataFrame = None, - problems: list[problem.Problem] = None, + problems: list[problem.Problem] | None = None, ): """Constructor diff --git a/petab/v1/conditions.py b/petab/v1/conditions.py index 8caf04dc..5dca2f6d 100644 --- a/petab/v1/conditions.py +++ b/petab/v1/conditions.py @@ -10,10 +10,10 @@ from .C import * __all__ = [ - "get_condition_df", - "write_condition_df", "create_condition_df", + "get_condition_df", "get_parametric_overrides", + "write_condition_df", ] diff --git a/petab/v1/core.py b/petab/v1/core.py index 6a142781..4ca10b7d 100644 --- a/petab/v1/core.py +++ b/petab/v1/core.py @@ -15,22 +15,22 @@ from pandas.api.types import is_string_dtype from . import yaml -from .C import * # noqa: F403 +from .C import * logger = logging.getLogger(__name__) __all__ = [ + "concat_tables", + "create_combine_archive", + "flatten_timepoint_specific_output_overrides", + "get_notnull_columns", "get_simulation_df", - "write_simulation_df", "get_visualization_df", - "write_visualization_df", - "get_notnull_columns", - "flatten_timepoint_specific_output_overrides", - "concat_tables", - "to_float_if_float", "is_empty", - "create_combine_archive", - "unique_preserve_order", + "to_float_if_float", "unflatten_simulation_df", + "unique_preserve_order", + "write_simulation_df", + "write_visualization_df", ] POSSIBLE_GROUPVARS_FLATTENED_PROBLEM = [ diff --git a/petab/v1/distributions.py b/petab/v1/distributions.py index aed5fe2f..ce6d4663 100644 --- a/petab/v1/distributions.py +++ b/petab/v1/distributions.py @@ -22,16 +22,16 @@ ) __all__ = [ - "Distribution", "Cauchy", "ChiSquare", + "Distribution", "Exponential", "Gamma", "Laplace", + "LogUniform", "Normal", "Rayleigh", "Uniform", - "LogUniform", ] @@ -56,7 +56,10 @@ class Distribution(abc.ABC): """ def __init__( - self, *, log: bool | float = False, trunc: tuple[float, float] = None + self, + *, + log: bool | float = False, + trunc: tuple[float, float] | None = None, ): if log is True: log = np.exp(1) @@ -297,7 +300,7 @@ def _inverse_transform_sample(self, shape) -> np.ndarray | float: ) return self._ppf_transformed_untruncated(uniform_sample) - def _repr(self, pars: dict[str, Any] = None) -> str: + def _repr(self, pars: dict[str, Any] | None = None) -> str: """Return a string representation of the distribution.""" pars = ", ".join(f"{k}={v}" for k, v in pars.items()) if pars else "" @@ -608,7 +611,7 @@ class ChiSquare(Distribution): def __init__( self, - dof: int | float, + dof: float, trunc: tuple[float, float] | None = None, log: bool | float = False, ): diff --git a/petab/v1/lint.py b/petab/v1/lint.py index 434b5030..df9fc6e2 100644 --- a/petab/v1/lint.py +++ b/petab/v1/lint.py @@ -15,7 +15,7 @@ import petab.v1 as petab from . import core, measurements, parameters -from .C import * # noqa: F403 +from .C import * from .math import sympify_petab from .models import Model @@ -478,7 +478,7 @@ def assert_parameter_id_is_string(parameter_df: pd.DataFrame) -> None: f"{PARAMETER_ID} {parameter_id} starts with integer." ) else: - raise AssertionError(f"Empty {PARAMETER_ID} found.") + raise AssertionError(f"Empty {PARAMETER_ID} found.") # noqa TRY004 def assert_unique_parameter_ids(parameter_df: pd.DataFrame) -> None: @@ -792,8 +792,9 @@ def observable_table_has_nontrivial_noise_formula( return ( not observable_df[NOISE_FORMULA] .apply( - lambda x: is_scalar_float(x) - or re.match(r"^[\w]+$", str(x)) is not None + lambda x: ( + is_scalar_float(x) or re.match(r"^[\w]+$", str(x)) is not None + ) ) .all() ) diff --git a/petab/v1/mapping.py b/petab/v1/mapping.py index 81f77017..af344920 100644 --- a/petab/v1/mapping.py +++ b/petab/v1/mapping.py @@ -6,14 +6,14 @@ import pandas as pd from . import lint -from .C import * # noqa: F403 +from .C import * from .models import Model __all__ = [ - "get_mapping_df", - "write_mapping_df", "check_mapping_df", + "get_mapping_df", "resolve_mapping", + "write_mapping_df", ] diff --git a/petab/v1/math/SympyVisitor.py b/petab/v1/math/SympyVisitor.py index 949366b3..64284e7f 100644 --- a/petab/v1/math/SympyVisitor.py +++ b/petab/v1/math/SympyVisitor.py @@ -39,12 +39,12 @@ } _unary_funcs = { "exp": sp.exp, - "log10": lambda x, evaluate=True: -sp.oo - if x.is_zero is True - else sp.log(x, 10, evaluate=evaluate), - "log2": lambda x, evaluate=True: -sp.oo - if x.is_zero is True - else sp.log(x, 2, evaluate=evaluate), + "log10": lambda x, evaluate=True: ( + -sp.oo if x.is_zero is True else sp.log(x, 10, evaluate=evaluate) + ), + "log2": lambda x, evaluate=True: ( + -sp.oo if x.is_zero is True else sp.log(x, 2, evaluate=evaluate) + ), "ln": sp.log, "sqrt": sp.sqrt, "abs": sp.Abs, diff --git a/petab/v1/math/printer.py b/petab/v1/math/printer.py index f2146233..968cfe71 100644 --- a/petab/v1/math/printer.py +++ b/petab/v1/math/printer.py @@ -1,5 +1,6 @@ """A PEtab-compatible sympy string-printer.""" +import typing from itertools import chain, islice import sympy as sp @@ -12,7 +13,7 @@ class PetabStrPrinter(StrPrinter): """A PEtab-compatible sympy string-printer.""" #: Mapping of sympy functions to PEtab functions - _func_map = { + _func_map: typing.ClassVar[dict[str, str]] = { "asin": "arcsin", "acos": "arccos", "atan": "arctan", diff --git a/petab/v1/math/sympify.py b/petab/v1/math/sympify.py index 1d44e16d..adfee2f9 100644 --- a/petab/v1/math/sympify.py +++ b/petab/v1/math/sympify.py @@ -14,7 +14,7 @@ def sympify_petab( - expr: str | int | float | sp.Basic, evaluate: bool = True + expr: str | float | sp.Basic, evaluate: bool = True ) -> sp.Expr | sp.Basic: """Convert PEtab math expression to sympy expression. @@ -65,9 +65,9 @@ def sympify_petab( if isinstance(expr, sp.Basic): return sympify_petab(petab_math_str(expr)) - if isinstance(expr, int) or isinstance(expr, np.integer): + if isinstance(expr, (int, np.integer)): return sp.Integer(expr) - if isinstance(expr, float) or isinstance(expr, np.floating): + if isinstance(expr, (float, np.floating)): return sp.Float(expr) try: @@ -112,5 +112,5 @@ def sympify_petab( class MathErrorListener(ErrorListener): """Error listener for math expression parser/lexer.""" - def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): # noqa N803 + def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): raise ValueError(f"Syntax error at {line}:{column}: {msg}") diff --git a/petab/v1/measurements.py b/petab/v1/measurements.py index f23a21c1..a39a21ec 100644 --- a/petab/v1/measurements.py +++ b/petab/v1/measurements.py @@ -1,5 +1,4 @@ """Functions operating on the PEtab measurement table""" -# noqa: F405 import itertools import math @@ -10,7 +9,7 @@ import pandas as pd from . import core, lint, observables -from .C import * # noqa: F403 +from .C import * __all__ = [ "assert_overrides_match_parameter_count", @@ -19,8 +18,8 @@ "get_measurement_parameter_ids", "get_rows_for_condition", "get_simulation_conditions", - "measurements_have_replicates", "measurement_is_at_steady_state", + "measurements_have_replicates", "split_parameter_replacement_list", "write_measurement_df", ] diff --git a/petab/v1/models/__init__.py b/petab/v1/models/__init__.py index a35ad432..75a6bb9c 100644 --- a/petab/v1/models/__init__.py +++ b/petab/v1/models/__init__.py @@ -12,4 +12,4 @@ from .model import Model # noqa F401 -__all__ = ["MODEL_TYPE_SBML", "MODEL_TYPE_PYSB", "known_model_types", "Model"] +__all__ = ["MODEL_TYPE_PYSB", "MODEL_TYPE_SBML", "Model", "known_model_types"] diff --git a/petab/v1/models/model.py b/petab/v1/models/model.py index 96613757..c4f0b9ef 100644 --- a/petab/v1/models/model.py +++ b/petab/v1/models/model.py @@ -22,7 +22,9 @@ def __repr__(self): @staticmethod @abc.abstractmethod def from_file( - filepath_or_buffer: Any, model_id: str, base_path: str | Path = None + filepath_or_buffer: Any, + model_id: str, + base_path: str | Path | None = None, ) -> Model: """Load the model from the given path/URL @@ -138,8 +140,8 @@ def is_state_variable(self, id_: str) -> bool: def model_factory( filepath_or_buffer: Any, model_language: str, - model_id: str = None, - base_path: str | Path = None, + model_id: str | None = None, + base_path: str | Path | None = None, ) -> Model: """Create a PEtab model instance from the given model diff --git a/petab/v1/models/pysb_model.py b/petab/v1/models/pysb_model.py index 6927ecb9..acf8405f 100644 --- a/petab/v1/models/pysb_model.py +++ b/petab/v1/models/pysb_model.py @@ -60,7 +60,7 @@ class PySBModel(Model): def __init__( self, model: pysb.Model, - model_id: str = None, + model_id: str | None = None, rel_path: Path | str | None = None, base_path: str | Path | None = None, ): @@ -81,7 +81,9 @@ def __init__( @staticmethod def from_file( - filepath_or_buffer, model_id: str = None, base_path: str | Path = None + filepath_or_buffer, + model_id: str | None = None, + base_path: str | Path | None = None, ) -> PySBModel: return PySBModel( model=_pysb_model_from_path( diff --git a/petab/v1/models/sbml_model.py b/petab/v1/models/sbml_model.py index 2d31c0b9..6b15a02e 100644 --- a/petab/v1/models/sbml_model.py +++ b/petab/v1/models/sbml_model.py @@ -33,7 +33,7 @@ def __init__( sbml_model: libsbml.Model = None, sbml_reader: libsbml.SBMLReader = None, sbml_document: libsbml.SBMLDocument = None, - model_id: str = None, + model_id: str | None = None, rel_path: Path | str | None = None, base_path: str | Path | None = None, ): @@ -94,7 +94,9 @@ def __setstate__(self, state): @staticmethod def from_file( - filepath_or_buffer, model_id: str = None, base_path: str | Path = None + filepath_or_buffer, + model_id: str | None = None, + base_path: str | Path | None = None, ) -> SbmlModel: sbml_reader, sbml_document, sbml_model = get_sbml_model( _generate_path(filepath_or_buffer, base_path=base_path) @@ -109,7 +111,7 @@ def from_file( ) @staticmethod - def from_string(sbml_string, model_id: str = None) -> SbmlModel: + def from_string(sbml_string, model_id: str | None = None) -> SbmlModel: """Create SBML model from an SBML string. :param sbml_string: SBML model as string. diff --git a/petab/v1/observables.py b/petab/v1/observables.py index 38c539c7..d5862ccc 100644 --- a/petab/v1/observables.py +++ b/petab/v1/observables.py @@ -8,7 +8,7 @@ import pandas as pd from . import core, lint -from .C import * # noqa: F403 +from .C import * from .math import sympify_petab from .models import Model diff --git a/petab/v1/parameter_mapping.py b/petab/v1/parameter_mapping.py index a438c6b2..9bb09638 100644 --- a/petab/v1/parameter_mapping.py +++ b/petab/v1/parameter_mapping.py @@ -21,7 +21,7 @@ observables, parameters, ) -from .C import * # noqa: F403 +from .C import * from .mapping import resolve_mapping from .models import Model @@ -31,16 +31,16 @@ logger = logging.getLogger(__name__) __all__ = [ + "ParMappingDict", + "ParMappingDictQuadruple", + "ParMappingDictTuple", + "ScaleMappingDict", + "ScaleMappingDictTuple", "get_optimization_to_simulation_parameter_mapping", "get_parameter_mapping_for_condition", "handle_missing_overrides", "merge_preeq_and_sim_pars", "merge_preeq_and_sim_pars_condition", - "ParMappingDict", - "ParMappingDictTuple", - "ScaleMappingDict", - "ScaleMappingDictTuple", - "ParMappingDictQuadruple", ] @@ -140,7 +140,7 @@ def get_optimization_to_simulation_parameter_mapping( # Ensure inputs are okay _perform_mapping_checks( measurement_df, - allow_timepoint_specific_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, # noqa: E251,E501 + allow_timepoint_specific_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, ) if simulation_conditions is None: @@ -157,7 +157,7 @@ def get_optimization_to_simulation_parameter_mapping( for par_id in output_parameters: simulation_parameters[par_id] = np.nan - num_threads = int(os.environ.get(ENV_NUM_THREADS, 1)) + num_threads = int(os.environ.get(ENV_NUM_THREADS, "1")) # If sequential execution is requested, let's not create any # thread-allocation overhead @@ -287,7 +287,7 @@ def _map_condition(packed_args): warn_unmapped=warn_unmapped, scaled_parameters=scaled_parameters, fill_fixed_parameters=fill_fixed_parameters, - allow_timepoint_specific_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, # noqa: E251,E501 + allow_timepoint_specific_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, ) par_map_sim, scale_map_sim = get_parameter_mapping_for_condition( @@ -302,7 +302,7 @@ def _map_condition(packed_args): warn_unmapped=warn_unmapped, scaled_parameters=scaled_parameters, fill_fixed_parameters=fill_fixed_parameters, - allow_timepoint_specific_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, # noqa: E251,E501 + allow_timepoint_specific_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, ) return par_map_preeq, par_map_sim, scale_map_preeq, scale_map_sim @@ -390,7 +390,7 @@ def get_parameter_mapping_for_condition( if cur_measurement_df is not None: _perform_mapping_checks( cur_measurement_df, - allow_timepoint_specific_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, # noqa: E251,E501 + allow_timepoint_specific_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, ) if simulation_parameters is None: @@ -437,7 +437,7 @@ def get_parameter_mapping_for_condition( def _output_parameters_to_nan(mapping: ParMappingDict) -> None: """Set output parameters in mapping dictionary to nan""" rex = re.compile("^(noise|observable)Parameter[0-9]+_") - for key in mapping.keys(): + for key in mapping: try: matches = rex.match(key) except TypeError: @@ -633,7 +633,7 @@ def _perform_mapping_checks( """ if lint.measurement_table_has_timepoint_specific_mappings( measurement_df, - allow_scalar_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, # noqa: E251,E501 + allow_scalar_numeric_noise_parameters=allow_timepoint_specific_numeric_noise_parameters, ): # we could allow that for floats, since they don't matter in this # function and would be simply ignored @@ -645,7 +645,7 @@ def _perform_mapping_checks( def handle_missing_overrides( mapping_par_opt_to_par_sim: ParMappingDict, warn: bool = True, - condition_id: str = None, + condition_id: str | None = None, ) -> None: """ Find all observable parameters and noise parameters that were not mapped diff --git a/petab/v1/parameters.py b/petab/v1/parameters.py index 82e23669..3efdaddb 100644 --- a/petab/v1/parameters.py +++ b/petab/v1/parameters.py @@ -3,7 +3,8 @@ import numbers import warnings from collections import OrderedDict -from collections.abc import Iterable, Sequence, Set +from collections.abc import Iterable, Sequence +from collections.abc import Set as AbstractSet from pathlib import Path from typing import ( Literal, @@ -14,7 +15,7 @@ import pandas as pd from . import conditions, core, lint, measurements, observables -from .C import * # noqa: F403 +from .C import * from .models import Model __all__ = [ @@ -161,8 +162,8 @@ def create_parameter_df( model: Model | None = None, include_optional: bool = False, parameter_scale: str = LOG10, - lower_bound: Iterable = None, - upper_bound: Iterable = None, + lower_bound: Iterable | None = None, + upper_bound: Iterable | None = None, mapping_df: pd.DataFrame | None = None, ) -> pd.DataFrame: """Create a new PEtab parameter table @@ -260,7 +261,7 @@ def get_required_parameters_for_parameter_table( observable_df: pd.DataFrame, measurement_df: pd.DataFrame, mapping_df: pd.DataFrame = None, -) -> Set[str]: +) -> AbstractSet[str]: """ Get set of parameters which need to go into the parameter table @@ -397,7 +398,7 @@ def get_valid_parameters_for_parameter_table( for from_id, to_id in zip( mapping_df.index.values, mapping_df[MODEL_ENTITY_ID], strict=True ): - if to_id in parameter_ids.keys(): + if to_id in parameter_ids: parameter_ids[from_id] = None if observable_df is not None: @@ -441,7 +442,7 @@ def append_overrides(overrides): def get_priors_from_df( parameter_df: pd.DataFrame, mode: Literal["initialization", "objective"], - parameter_ids: Sequence[str] = None, + parameter_ids: Sequence[str] | None = None, ) -> list[tuple]: """Create list with information about the parameter priors diff --git a/petab/v1/priors.py b/petab/v1/priors.py index b8bf8dcb..1cdf66a9 100644 --- a/petab/v1/priors.py +++ b/petab/v1/priors.py @@ -73,7 +73,7 @@ def __init__( self, type_: str, parameters: tuple, - bounds: tuple = None, + bounds: tuple | None = None, transformation: str = C.LIN, _bounds_truncate: bool = True, ): diff --git a/petab/v1/problem.py b/petab/v1/problem.py index 6da82a2f..ae09e75b 100644 --- a/petab/v1/problem.py +++ b/petab/v1/problem.py @@ -27,7 +27,7 @@ sbml, yaml, ) -from .C import * # noqa: F403 +from .C import * from .models import MODEL_TYPE_SBML from .models.model import Model, model_factory from .models.sbml_model import SbmlModel @@ -77,14 +77,14 @@ def __init__( sbml_reader: libsbml.SBMLReader = None, sbml_document: libsbml.SBMLDocument = None, model: Model = None, - model_id: str = None, + model_id: str | None = None, condition_df: pd.DataFrame = None, measurement_df: pd.DataFrame = None, parameter_df: pd.DataFrame = None, visualization_df: pd.DataFrame = None, observable_df: pd.DataFrame = None, mapping_df: pd.DataFrame = None, - extensions_config: dict = None, + extensions_config: dict | None = None, config: ProblemConfig = None, ): self.condition_df: pd.DataFrame | None = condition_df @@ -179,14 +179,14 @@ def __str__(self): @staticmethod def from_files( - sbml_file: str | Path = None, - condition_file: str | Path | Iterable[str | Path] = None, - measurement_file: str | Path | Iterable[str | Path] = None, - parameter_file: str | Path | Iterable[str | Path] = None, - visualization_files: str | Path | Iterable[str | Path] = None, - observable_files: str | Path | Iterable[str | Path] = None, - model_id: str = None, - extensions_config: dict = None, + sbml_file: str | Path | None = None, + condition_file: str | Path | Iterable[str | Path] | None = None, + measurement_file: str | Path | Iterable[str | Path] | None = None, + parameter_file: str | Path | Iterable[str | Path] | None = None, + visualization_files: str | Path | Iterable[str | Path] | None = None, + observable_files: str | Path | Iterable[str | Path] | None = None, + model_id: str | None = None, + extensions_config: dict | None = None, ) -> Problem: """ Factory method to load model and tables from files. @@ -261,7 +261,7 @@ def from_files( @staticmethod def from_yaml( - yaml_config: dict | Path | str, base_path: str | Path = None + yaml_config: dict | Path | str, base_path: str | Path | None = None ) -> Problem: """ Factory method to load model and tables as specified by YAML file. @@ -1014,7 +1014,7 @@ def n_priors(self) -> int: return self.parameter_df[OBJECTIVE_PRIOR_PARAMETERS].notna().sum() - def add_condition(self, id_: str, name: str = None, **kwargs): + def add_condition(self, id_: str, name: str | None = None, **kwargs): """Add a simulation condition to the problem. Arguments: @@ -1035,11 +1035,11 @@ def add_condition(self, id_: str, name: str = None, **kwargs): def add_observable( self, id_: str, - formula: str | float | int, - noise_formula: str | float | int = None, - noise_distribution: str = None, - transform: str = None, - name: str = None, + formula: str | float, + noise_formula: str | float | None = None, + noise_distribution: str | None = None, + transform: str | None = None, + name: str | None = None, **kwargs, ): """Add an observable to the problem. @@ -1080,13 +1080,13 @@ def add_parameter( id_: str, estimate: bool | str | int = True, nominal_value: Number | None = None, - scale: str = None, - lb: Number = None, - ub: Number = None, - init_prior_type: str = None, - init_prior_pars: str | Sequence = None, - obj_prior_type: str = None, - obj_prior_pars: str | Sequence = None, + scale: str | None = None, + lb: Number | None = None, + ub: Number | None = None, + init_prior_type: str | None = None, + init_prior_pars: str | Sequence | None = None, + obj_prior_type: str | None = None, + obj_prior_pars: str | Sequence | None = None, **kwargs, ): """Add a parameter to the problem. @@ -1149,9 +1149,9 @@ def add_measurement( sim_cond_id: str, time: float, measurement: float, - observable_parameters: Sequence[str | float] = None, - noise_parameters: Sequence[str | float] = None, - preeq_cond_id: str = None, + observable_parameters: Sequence[str | float] | None = None, + noise_parameters: Sequence[str | float] | None = None, + preeq_cond_id: str | None = None, ): """Add a measurement to the problem. diff --git a/petab/v1/sampling.py b/petab/v1/sampling.py index 035fe7aa..5cbdba88 100644 --- a/petab/v1/sampling.py +++ b/petab/v1/sampling.py @@ -5,7 +5,7 @@ import numpy as np import pandas as pd -from .C import * # noqa: F403 +from .C import * __all__ = ["sample_from_prior", "sample_parameter_startpoints"] @@ -39,8 +39,8 @@ def sample_from_prior( def sample_parameter_startpoints( parameter_df: pd.DataFrame, n_starts: int = 100, - seed: int = None, - parameter_ids: Sequence[str] = None, + seed: int | None = None, + parameter_ids: Sequence[str] | None = None, ) -> np.array: """Create :class:`numpy.array` with starting points for an optimization diff --git a/petab/v1/sbml.py b/petab/v1/sbml.py index b939e45e..e8ed10aa 100644 --- a/petab/v1/sbml.py +++ b/petab/v1/sbml.py @@ -235,7 +235,7 @@ def load_sbml_from_file( def get_model_for_condition( petab_problem: "petab.Problem", - sim_condition_id: str = None, + sim_condition_id: str | None = None, preeq_condition_id: str | None = None, ) -> tuple[libsbml.SBMLDocument, libsbml.Model]: """Create an SBML model for the given condition. @@ -268,7 +268,7 @@ def get_model_for_condition( ) ( parameter_map, - scale_map, + _scale_map, ) = petab.parameter_mapping.get_parameter_mapping_for_condition( condition_id=sim_condition_id, is_preeq=False, diff --git a/petab/v1/simplify.py b/petab/v1/simplify.py index 78c039b1..d9db5ce1 100644 --- a/petab/v1/simplify.py +++ b/petab/v1/simplify.py @@ -7,15 +7,15 @@ import petab.v1 as petab from . import Problem -from .C import * # noqa: F403 +from .C import * from .lint import lint_problem __all__ = [ + "condition_parameters_to_parameter_table", "remove_nan_measurements", - "remove_unused_observables", "remove_unused_conditions", + "remove_unused_observables", "simplify_problem", - "condition_parameters_to_parameter_table", ] diff --git a/petab/v1/visualize/__init__.py b/petab/v1/visualize/__init__.py index 15385697..f84ac476 100644 --- a/petab/v1/visualize/__init__.py +++ b/petab/v1/visualize/__init__.py @@ -28,11 +28,11 @@ __all__.extend( [ - "plot_without_vis_spec", - "plot_with_vis_spec", - "plot_problem", + "MPLPlotter", "plot_goodness_of_fit", + "plot_problem", "plot_residuals_vs_simulation", - "MPLPlotter", + "plot_with_vis_spec", + "plot_without_vis_spec", ] ) diff --git a/petab/v1/visualize/plot_data_and_simulation.py b/petab/v1/visualize/plot_data_and_simulation.py index 5ca8c6fb..c8f925b7 100644 --- a/petab/v1/visualize/plot_data_and_simulation.py +++ b/petab/v1/visualize/plot_data_and_simulation.py @@ -14,7 +14,7 @@ IdsList = list[str] NumList = list[int] -__all__ = ["plot_with_vis_spec", "plot_without_vis_spec", "plot_problem"] +__all__ = ["plot_problem", "plot_with_vis_spec", "plot_without_vis_spec"] def plot_with_vis_spec( diff --git a/petab/v1/visualize/plot_residuals.py b/petab/v1/visualize/plot_residuals.py index a1f2ec9b..c0b95fcc 100644 --- a/petab/v1/visualize/plot_residuals.py +++ b/petab/v1/visualize/plot_residuals.py @@ -193,7 +193,7 @@ def plot_goodness_of_fit( error_name = "mean of squared residuals" error = np.mean(np.power(residual_df["residual"], 2)) - slope, intercept, r_value, p_value, std_err = stats.linregress( + slope, intercept, r_value, p_value, _std_err = stats.linregress( simulations_df["simulation"], petab_problem.measurement_df["measurement"], ) # x, y diff --git a/petab/v1/visualize/plotter.py b/petab/v1/visualize/plotter.py index 14af5650..047edf73 100644 --- a/petab/v1/visualize/plotter.py +++ b/petab/v1/visualize/plotter.py @@ -13,7 +13,7 @@ from ..C import * from .plotting import DataPlot, DataProvider, DataSeries, Figure, Subplot -__all__ = ["Plotter", "MPLPlotter", "SeabornPlotter"] +__all__ = ["MPLPlotter", "Plotter", "SeabornPlotter"] #: Line style (:class:`matplotlib.lines.Line2D` options) for the measurement diff --git a/petab/v1/visualize/plotting.py b/petab/v1/visualize/plotting.py index e2c55755..5dacd9b1 100644 --- a/petab/v1/visualize/plotting.py +++ b/petab/v1/visualize/plotting.py @@ -17,11 +17,11 @@ ) __all__ = [ - "DataSeries", "DataPlot", - "Subplot", - "Figure", "DataProvider", + "DataSeries", + "Figure", + "Subplot", "VisSpecParser", ] @@ -398,7 +398,7 @@ def save_to_tsv(self, output_file_path: str = "visuSpec.tsv") -> None: if key in visu_dict: visu_dict[key].append(value) else: - visu_dict[key] = [row[key]] + visu_dict[key] = [value] visu_df = pd.DataFrame.from_dict(visu_dict) visu_df.to_csv(output_file_path, sep="\t", index=False) @@ -1099,7 +1099,7 @@ def _vis_spec_rows_for_obs( n_rows = len(dataset_ids) columns_dict = {DATASET_ID: dataset_ids, Y_VALUES: [obs_id] * n_rows} - for column in settings: + for column in settings: # noqa PLC0206 if column in columns_to_expand: columns_dict[column] = [settings[column]] * n_rows diff --git a/petab/v1/yaml.py b/petab/v1/yaml.py index cefc594c..669c192f 100644 --- a/petab/v1/yaml.py +++ b/petab/v1/yaml.py @@ -14,7 +14,7 @@ from pandas.io.common import get_handle from ..versions import parse_version -from .C import * # noqa: F403 +from .C import * # directory with PEtab yaml schema files SCHEMA_DIR = Path(__file__).parent.parent / "schemas" @@ -25,15 +25,15 @@ } __all__ = [ - "validate", - "validate_yaml_syntax", - "validate_yaml_semantics", - "load_yaml", - "is_composite_problem", "assert_single_condition_and_sbml_file", - "write_yaml", "create_problem_yaml", "get_path_prefix", + "is_composite_problem", + "load_yaml", + "validate", + "validate_yaml_semantics", + "validate_yaml_syntax", + "write_yaml", ] @@ -256,7 +256,7 @@ def create_problem_yaml( yaml_file: str | Path, visualization_files: str | Path | list[str | Path] | None = None, relative_paths: bool = True, - mapping_files: str | Path | list[str | Path] = None, + mapping_files: str | Path | list[str | Path] | None = None, ) -> None: """Create and write default YAML file for a single PEtab problem diff --git a/petab/v2/__init__.py b/petab/v2/__init__.py index a9f018c5..e67f2dea 100644 --- a/petab/v2/__init__.py +++ b/petab/v2/__init__.py @@ -4,36 +4,36 @@ """ # TODO: move this module to v2 -from petab.v1.distributions import * # noqa: F401, E402 -from petab.v1.mapping import ( # noqa: F403, F401, E402 +from petab.v1.distributions import * +from petab.v1.mapping import ( # noqa: F401 get_mapping_df, write_mapping_df, ) -from petab.v1.measurements import ( # noqa: F401, E402 +from petab.v1.measurements import ( # noqa: F401 get_measurement_df, write_measurement_df, ) -from petab.v1.observables import ( # noqa: F401, E402 +from petab.v1.observables import ( # noqa: F401 get_observable_df, write_observable_df, ) -from petab.v1.parameters import ( # noqa: F401, E402 +from petab.v1.parameters import ( # noqa: F401 get_parameter_df, write_parameter_df, ) -from petab.v1.yaml import load_yaml # noqa: F401, E402 +from petab.v1.yaml import load_yaml # noqa: F401 # import after v1 -from ..version import __version__ # noqa: F401, E402 -from . import ( # noqa: F401, E402 - C, # noqa: F401, E402 - models, # noqa: F401, E402 +from ..version import __version__ # noqa: F401 +from . import ( # noqa: F401 + C, + models, ) -from .conditions import * # noqa: F403, F401, E402 -from .core import * # noqa: F401, E402 -from .experiments import ( # noqa: F401, E402 +from .conditions import * +from .core import * +from .experiments import ( # noqa: F401 get_experiment_df, write_experiment_df, ) -from .lint import lint_problem # noqa: F401, E402 -from .models import MODEL_TYPE_PYSB, MODEL_TYPE_SBML, Model # noqa: F401, E402 +from .lint import lint_problem # noqa: F401 +from .models import MODEL_TYPE_PYSB, MODEL_TYPE_SBML, Model # noqa: F401 diff --git a/petab/v2/_helpers.py b/petab/v2/_helpers.py index 3201769a..d0269cd7 100644 --- a/petab/v2/_helpers.py +++ b/petab/v2/_helpers.py @@ -1,3 +1,3 @@ """Various internal helper functions.""" -from ..v1.core import to_float_if_float # noqa: F401, E402 +from ..v1.core import to_float_if_float # noqa: F401 diff --git a/petab/v2/calculate.py b/petab/v2/calculate.py index 854e51c9..1e811974 100644 --- a/petab/v2/calculate.py +++ b/petab/v2/calculate.py @@ -14,15 +14,15 @@ from .math import sympify_petab __all__ = [ - "calculate_residuals", - "calculate_residuals_for_table", - "get_symbolic_noise_formulas", - "evaluate_noise_formula", "calculate_chi2", "calculate_chi2_for_table_from_residuals", "calculate_llh", "calculate_llh_for_table", + "calculate_residuals", + "calculate_residuals_for_table", "calculate_single_llh", + "evaluate_noise_formula", + "get_symbolic_noise_formulas", ] diff --git a/petab/v2/converters.py b/petab/v2/converters.py index ae19686d..e5e9c4f4 100644 --- a/petab/v2/converters.py +++ b/petab/v2/converters.py @@ -58,7 +58,9 @@ class ExperimentsToSbmlConverter: #: pre-equilibration indicator to 0. CONDITION_ID_PREEQ_OFF = "_petab_preequilibration_off" - def __init__(self, problem: Problem, default_priority: float = None): + def __init__( + self, problem: Problem, default_priority: float | None = None + ): """Initialize the converter. :param problem: The PEtab problem to convert. @@ -80,7 +82,7 @@ def __init__(self, problem: Problem, default_priority: float = None): "Only single-model PEtab problems are supported." ) if not isinstance(problem.model, SbmlModel): - raise ValueError("Only SBML models are supported.") + raise TypeError("Only SBML models are supported.") self._original_problem = problem self._new_problem = deepcopy(self._original_problem) @@ -109,14 +111,16 @@ def _preprocess(self) -> None: """Check whether we can handle the given problem and store some model information.""" model = self._model - if model.getLevel() < 3: - # try to upgrade the SBML model - if not model.getSBMLDocument().setLevelAndVersion(3, 2): - raise ValueError( - "Cannot handle SBML models with SBML level < 3, " - "because they do not support initial values for event " - "triggers and automatic upconversion of the model failed." - ) + # try to upgrade the SBML model if necessary + if ( + model.getLevel() < 3 + and not model.getSBMLDocument().setLevelAndVersion(3, 2) + ): + raise ValueError( + "Cannot handle SBML models with SBML level < 3, " + "because they do not support initial values for event " + "triggers and automatic upconversion of the model failed." + ) # Apply default priority to all events that do not have a priority if self._default_priority is not None: diff --git a/petab/v2/core.py b/petab/v2/core.py index 985b9b0d..79d57729 100644 --- a/petab/v2/core.py +++ b/petab/v2/core.py @@ -9,7 +9,7 @@ import traceback from abc import abstractmethod from collections.abc import Sequence -from enum import Enum +from enum import StrEnum from itertools import chain from math import nan from numbers import Number @@ -18,7 +18,6 @@ TYPE_CHECKING, Annotated, Any, - Generic, Literal, Self, TypeVar, @@ -61,25 +60,25 @@ __all__ = [ - "Problem", - "ProblemConfig", - "Observable", - "ObservableTable", - "NoiseDistribution", "Change", "Condition", "ConditionTable", - "ExperimentPeriod", "Experiment", + "ExperimentPeriod", "ExperimentTable", - "Measurement", - "MeasurementTable", "Mapping", "MappingTable", + "Measurement", + "MeasurementTable", + "NoiseDistribution", + "Observable", + "ObservableTable", "Parameter", "ParameterScale", "ParameterTable", "PriorDistribution", + "Problem", + "ProblemConfig", ] logger = logging.getLogger(__name__) @@ -134,7 +133,7 @@ def _valid_petab_id_or_none(v: str) -> str | None: return v -class ParameterScale(str, Enum): +class ParameterScale(StrEnum): """Parameter scales. Parameter scales as used in the PEtab parameter table. @@ -145,7 +144,7 @@ class ParameterScale(str, Enum): LOG10 = C.LOG10 -class NoiseDistribution(str, Enum): +class NoiseDistribution(StrEnum): """Noise distribution types. Noise distributions as used in the PEtab observable table. @@ -161,7 +160,7 @@ class NoiseDistribution(str, Enum): LOG_LAPLACE = C.LOG_LAPLACE -class PriorDistribution(str, Enum): +class PriorDistribution(StrEnum): """Prior types. Prior types as used in the PEtab parameter table. @@ -219,7 +218,7 @@ class PriorDistribution(str, Enum): T = TypeVar("T", bound=BaseModel) -class BaseTable(BaseModel, Generic[T]): +class BaseTable[T: BaseModel](BaseModel): """Base class for PEtab tables.""" #: The table elements @@ -232,7 +231,7 @@ class BaseTable(BaseModel, Generic[T]): #: This is usually the directory of the PEtab YAML file. base_path: AnyUrl | Path | None = Field(exclude=True, default=None) - def __init__(self, elements: list[T] = None, **kwargs) -> None: + def __init__(self, elements: list[T] | None = None, **kwargs) -> None: """Initialize the BaseTable with a list of elements.""" if elements is None: elements = [] @@ -262,12 +261,10 @@ def __getitem__(self, id_: str) -> T: @abstractmethod def from_df(cls, df: pd.DataFrame, **kwargs) -> BaseTable[T]: """Create a table from a DataFrame.""" - pass @abstractmethod def to_df(self) -> pd.DataFrame: """Convert the table to a DataFrame.""" - pass @classmethod def from_tsv( @@ -277,7 +274,7 @@ def from_tsv( df = pd.read_csv(_generate_path(file_path, base_path), sep="\t") return cls.from_df(df, rel_path=file_path, base_path=base_path) - def to_tsv(self, file_path: str | Path = None) -> None: + def to_tsv(self, file_path: str | Path | None = None) -> None: """Write the table to a TSV file.""" df = self.to_df() df.to_csv( @@ -516,7 +513,7 @@ def __add__(self, other: Change) -> Condition: raise TypeError("Can only add Change to Condition") return Condition(id=self.id, changes=self.changes + [other]) - def __iadd__(self, other: Change) -> Condition: + def __iadd__(self, other: Change) -> Self: """Add a change to the set in place.""" if not isinstance(other, Change): raise TypeError("Can only add Change to Condition") @@ -652,7 +649,7 @@ def __add__(self, other: ExperimentPeriod) -> Experiment: raise TypeError("Can only add ExperimentPeriod to Experiment") return Experiment(id=self.id, periods=self.periods + [other]) - def __iadd__(self, other: ExperimentPeriod) -> Experiment: + def __iadd__(self, other: ExperimentPeriod) -> Self: """Add a period to the experiment in place.""" if not isinstance(other, ExperimentPeriod): raise TypeError("Can only add ExperimentPeriod to Experiment") @@ -1142,13 +1139,13 @@ class Problem: def __init__( self, - models: list[Model] = None, - condition_tables: list[ConditionTable] = None, - experiment_tables: list[ExperimentTable] = None, - observable_tables: list[ObservableTable] = None, - measurement_tables: list[MeasurementTable] = None, - parameter_tables: list[ParameterTable] = None, - mapping_tables: list[MappingTable] = None, + models: list[Model] | None = None, + condition_tables: list[ConditionTable] | None = None, + experiment_tables: list[ExperimentTable] | None = None, + observable_tables: list[ObservableTable] | None = None, + measurement_tables: list[MeasurementTable] | None = None, + parameter_tables: list[ParameterTable] | None = None, + mapping_tables: list[MappingTable] | None = None, extensions: ProblemExtensions = None, config: ProblemConfig = None, ): @@ -1237,7 +1234,7 @@ def __getitem__( @staticmethod def from_yaml( - yaml_config: dict | Path | str, base_path: str | Path = None + yaml_config: dict | Path | str, base_path: str | Path | None = None ) -> Problem: """ Factory method to load model and tables as specified by YAML file. @@ -1957,7 +1954,7 @@ def n_priors(self) -> int: return sum(p.prior_distribution is not None for p in self.parameters) def validate( - self, validation_tasks: list[ValidationTask] = None + self, validation_tasks: list[ValidationTask] | None = None ) -> ValidationResultList: """Validate the PEtab problem. @@ -2009,7 +2006,7 @@ def validate( for task in validation_tasks or self.validation_tasks: try: cur_result = task.run(self) - except Exception as e: + except Exception as e: # noqa BLE001 cur_result = ValidationIssue( ValidationIssueSeverity.CRITICAL, f"Validation task {task} failed with exception: {e}\n" @@ -2046,7 +2043,10 @@ def assert_valid(self, **kwargs) -> None: ) def add_condition( - self, id_: str, name: str = None, **kwargs: Number | str | sp.Expr + self, + id_: str, + name: str | None = None, + **kwargs: Number | str | sp.Expr, ): """Add a simulation condition to the problem. @@ -2080,11 +2080,11 @@ def add_observable( self, id_: str, formula: str, - noise_formula: str | float | int = None, - noise_distribution: str = None, - observable_placeholders: list[str] = None, - noise_placeholders: list[str] = None, - name: str = None, + noise_formula: str | float | None = None, + noise_distribution: str | None = None, + observable_placeholders: list[str] | None = None, + noise_placeholders: list[str] | None = None, + name: str | None = None, **kwargs, ): """Add an observable to the problem. @@ -2129,10 +2129,10 @@ def add_parameter( id_: str, estimate: bool | str = True, nominal_value: Number | None = None, - lb: Number = None, - ub: Number = None, - prior_dist: str = None, - prior_pars: str | Sequence = None, + lb: Number | None = None, + ub: Number | None = None, + prior_dist: str | None = None, + prior_pars: str | Sequence | None = None, **kwargs, ): """Add a parameter to the problem. @@ -2183,8 +2183,11 @@ def add_measurement( time: float, measurement: float, experiment_id: str | None = None, - observable_parameters: Sequence[str | float] | str | float = None, - noise_parameters: Sequence[str | float] | str | float = None, + observable_parameters: Sequence[str | float] + | str + | float + | None = None, + noise_parameters: Sequence[str | float] | str | float | None = None, ): """Add a measurement to the problem. @@ -2223,7 +2226,10 @@ def add_measurement( ) def add_mapping( - self, petab_id: str, model_id: str = None, name: str = None + self, + petab_id: str, + model_id: str | None = None, + name: str | None = None, ): """Add a mapping table entry to the problem. @@ -2318,7 +2324,7 @@ def __iadd__(self, other): self.experiment_tables.append(ExperimentTable()) self.experiment_tables[-1] += other else: - raise ValueError( + raise ValueError( # noqa: TRY004 f"Cannot add object of type {type(other)} to Problem." ) return self @@ -2465,14 +2471,13 @@ def get_output_parameters( if ( mapping.petab_id == candidate and mapping.model_id is not None + ) and ( + self.model + and self.model.symbol_allowed_in_observable_formula( + mapping.model_id + ) ): - if ( - self.model - and self.model.symbol_allowed_in_observable_formula( - mapping.model_id - ) - ): - break + break else: # no mapping to a model entity, so it is an output parameter output_parameters.append(candidate) @@ -2546,7 +2551,7 @@ def _parse_extensions(cls, v): """Parse extensions dict and convert known extensions to their specific config classes.""" if not isinstance(v, dict): - raise ValueError( + raise ValueError( # noqa: TRY004 "extensions must be a dict of extension ID to extension " f"config, got {type(v)}." ) diff --git a/petab/v2/extensions/sciml.py b/petab/v2/extensions/sciml.py index 6c8b1460..4bd406f1 100644 --- a/petab/v2/extensions/sciml.py +++ b/petab/v2/extensions/sciml.py @@ -65,7 +65,9 @@ def _sympify(cls, v): class HybridizationTable: """PEtab SciML hybridization table.""" - def __init__(self, hybridizations: list[Hybridization] = None, **kwargs): + def __init__( + self, hybridizations: list[Hybridization] | None = None, **kwargs + ): self.hybridizations: list[Hybridization] = hybridizations or [] self.rel_path: AnyUrl | Path | None = kwargs.get("rel_path") self.base_path: AnyUrl | Path | None = kwargs.get("base_path") @@ -100,7 +102,7 @@ def to_df(self) -> pd.DataFrame: records = [h.model_dump(by_alias=True) for h in self.hybridizations] return pd.DataFrame(records) - def to_tsv(self, file_path: str | Path = None) -> None: + def to_tsv(self, file_path: str | Path | None = None) -> None: """Write the table to a TSV file.""" df = self.to_df() df.to_csv( @@ -143,11 +145,11 @@ class SciMLConfig(ExtensionConfig): version: str = "0.1.0" required: bool = True #: The paths to the array data files. - array_files: list[AnyUrl | Path] = [] + array_files: list[AnyUrl | Path] = [] # noqa RUF012 #: The paths to the hybridization tables. - hybridization_files: list[AnyUrl | Path] = [] + hybridization_files: list[AnyUrl | Path] = [] # noqa RUF012 #: The neural network IDs and info. - neural_networks: dict[str, NeuralNetConfig] | None = {} + neural_networks: dict[str, NeuralNetConfig] | None = {} # noqa RUF012 #: :meta private: model_config = ConfigDict( diff --git a/petab/v2/extensions/sciml_lint.py b/petab/v2/extensions/sciml_lint.py index e8ad1a3c..e9cf27c1 100644 --- a/petab/v2/extensions/sciml_lint.py +++ b/petab/v2/extensions/sciml_lint.py @@ -22,10 +22,10 @@ from ..C import EXT_ID_SCIML __all__ = [ - "CheckNeuralNetworkModel", + "CheckArrayDataFiles", "CheckHybridizationTable", + "CheckNeuralNetworkModel", "CheckSciMLConditionTable", - "CheckArrayDataFiles", "CheckSciMLParameterTable", ] @@ -274,7 +274,7 @@ def run(self, problem: core.Problem) -> lint.ValidationIssue | None: # re-check in case a target value was assigned some other way. try: sympify_petab(str(hyb.target_value)) - except Exception as e: + except Exception as e: # noqa BLE001 messages.append( f"Hybridization target value for `{hyb.target_id}` is " f"not a valid PEtab math expression (hint: {e})." diff --git a/petab/v2/lint.py b/petab/v2/lint.py index aa0495a1..b0a96d94 100644 --- a/petab/v2/lint.py +++ b/petab/v2/lint.py @@ -3,9 +3,9 @@ from __future__ import annotations import logging +import typing from abc import ABC, abstractmethod from collections import Counter, OrderedDict -from collections.abc import Set from dataclasses import dataclass, field from enum import IntEnum from itertools import chain @@ -20,38 +20,38 @@ logger = logging.getLogger(__name__) __all__ = [ - "ValidationIssueSeverity", - "ValidationIssue", - "ValidationResultList", - "ValidationError", - "ValidationTask", - "CheckModel", - "CheckProblemConfig", - "CheckMeasuredObservablesDefined", - "CheckOverridesMatchPlaceholders", - "CheckMeasuredExperimentsDefined", - "CheckMeasurementModelId", - "CheckPosLogMeasurements", - "CheckValidConditionTargets", - "CheckUniquePrimaryKeys", - "CheckExperimentTable", - "CheckExperimentConditionsExist", "CheckAllParametersPresentInParameterTable", - "CheckValidParameterInConditionOrParameterTable", - "CheckUnusedExperiments", - "CheckObservablesDoNotShadowModelEntities", - "CheckUnusedConditions", - "CheckPriorDistribution", - "CheckUndefinedExperiments", + "CheckArrayDataFiles", + "CheckExperimentConditionsExist", + "CheckExperimentTable", + "CheckHybridizationTable", "CheckInitialChangeSymbols", "CheckMappingTable", + "CheckMeasuredExperimentsDefined", + "CheckMeasuredObservablesDefined", + "CheckMeasurementModelId", + "CheckModel", "CheckNeuralNetworkModel", - "CheckHybridizationTable", + "CheckObservablesDoNotShadowModelEntities", + "CheckOverridesMatchPlaceholders", + "CheckPosLogMeasurements", + "CheckPriorDistribution", + "CheckProblemConfig", "CheckSciMLConditionTable", - "CheckArrayDataFiles", "CheckSciMLParameterTable", - "lint_problem", + "CheckUndefinedExperiments", + "CheckUniquePrimaryKeys", + "CheckUnusedConditions", + "CheckUnusedExperiments", + "CheckValidConditionTargets", + "CheckValidParameterInConditionOrParameterTable", + "ValidationError", + "ValidationIssue", + "ValidationIssueSeverity", + "ValidationResultList", + "ValidationTask", "default_validation_tasks", + "lint_problem", ] @@ -347,7 +347,7 @@ class CheckPosLogMeasurements(ValidationTask): log-transformation are positive.""" def run(self, problem: Problem) -> ValidationIssue | None: - from .core import NoiseDistribution as ND # noqa: N813 + from .core import NoiseDistribution as ND log_observables = { o.id @@ -541,7 +541,7 @@ def run(self, problem: Problem) -> ValidationIssue | None: key for array_data in problem.extensions.sciml.array_data_files for input_array in array_data.inputs.values() - for key in input_array.keys() + for key in input_array } for experiment in problem.experiments: missing_conditions = ( @@ -819,7 +819,7 @@ def run(self, problem: Problem) -> ValidationIssue | None: class CheckPriorDistribution(ValidationTask): """A task to validate the prior distribution of a PEtab problem.""" - _num_pars = { + _num_pars: typing.ClassVar = { PriorDistribution.CAUCHY: 2, PriorDistribution.CHI_SQUARED: 1, PriorDistribution.EXPONENTIAL: 1, @@ -862,7 +862,7 @@ def run(self, problem: Problem) -> ValidationIssue | None: if parameter.estimate and parameter.prior_dist is not None: # .prior_dist fails for non-estimated parameters _ = parameter.prior_dist.sample(1) - except Exception as e: + except Exception as e: # noqa BLE001 messages.append( f"Prior parameters `{parameter.prior_parameters}` " f"for parameter `{parameter.id}` are invalid " @@ -1023,7 +1023,7 @@ def get_valid_parameters_for_parameter_table( ) for mapping in problem.mappings: - if mapping.model_id and mapping.model_id in parameter_ids.keys(): + if mapping.model_id and mapping.model_id in parameter_ids: parameter_ids[mapping.petab_id] = None if problem.extensions.sciml is not None: @@ -1059,7 +1059,7 @@ def append_overrides(overrides): def get_required_parameters_for_parameter_table( problem: Problem, -) -> Set[str]: +) -> set[str]: """ Get the set of parameters that need to go into the parameter table @@ -1197,7 +1197,7 @@ def get_placeholders( # Import SciML validation from sciml_lint at the end to avoid circular # imports. try: - from ..v2.extensions.sciml_lint import ( # noqa: E402 + from ..v2.extensions.sciml_lint import ( CheckArrayDataFiles, CheckHybridizationTable, CheckNeuralNetworkModel, diff --git a/petab/v2/math/__init__.py b/petab/v2/math/__init__.py index 8a5a5559..7a1b1004 100644 --- a/petab/v2/math/__init__.py +++ b/petab/v2/math/__init__.py @@ -1,3 +1,3 @@ """Functions for parsing and evaluating mathematical expressions.""" -from petab.v1.math import * # noqa: F401 +from petab.v1.math import * diff --git a/petab/v2/models/__init__.py b/petab/v2/models/__init__.py index 79ec7639..7b66f4e6 100644 --- a/petab/v2/models/__init__.py +++ b/petab/v2/models/__init__.py @@ -1,3 +1,3 @@ """Handling of different model types supported by PEtab.""" -from ...v1.models import * # noqa: F401, F403 +from ...v1.models import * diff --git a/petab/v2/models/_sbml_utils.py b/petab/v2/models/_sbml_utils.py index cbccde2b..69a489a7 100644 --- a/petab/v2/models/_sbml_utils.py +++ b/petab/v2/models/_sbml_utils.py @@ -34,8 +34,8 @@ def check(res: int): def add_sbml_parameter( model: libsbml.Model, id_: str, - value: float = None, - constant: bool = None, + value: float | None = None, + constant: bool | None = None, ) -> libsbml.Parameter: """Add a parameter to the SBML model.""" param = model.createParameter() diff --git a/petab/v2/models/model.py b/petab/v2/models/model.py index 345247eb..56c213bd 100644 --- a/petab/v2/models/model.py +++ b/petab/v2/models/model.py @@ -1,3 +1,3 @@ """PEtab model abstraction""" -from ...v1.models.model import * # noqa: F401, F403 +from ...v1.models.model import * diff --git a/petab/v2/models/pysb_model.py b/petab/v2/models/pysb_model.py index 4da866e7..1630f02c 100644 --- a/petab/v2/models/pysb_model.py +++ b/petab/v2/models/pysb_model.py @@ -1,3 +1,3 @@ """Functions for handling PySB models""" -from ...v1.models.pysb_model import * # noqa: F401, F403 +from ...v1.models.pysb_model import * diff --git a/petab/v2/models/sbml_model.py b/petab/v2/models/sbml_model.py index b696ce31..e68b1581 100644 --- a/petab/v2/models/sbml_model.py +++ b/petab/v2/models/sbml_model.py @@ -1,3 +1,3 @@ """Functions for handling SBML models""" -from ...v1.models.sbml_model import * # noqa: F401, F403 +from ...v1.models.sbml_model import * diff --git a/petab/v2/petab1to2.py b/petab/v2/petab1to2.py index 98fd41bc..14ed8939 100644 --- a/petab/v2/petab1to2.py +++ b/petab/v2/petab1to2.py @@ -24,7 +24,7 @@ def petab1to2( - yaml_config: Path | str, output_dir: Path | str = None + yaml_config: Path | str, output_dir: Path | str | None = None ) -> v2.Problem | None: """Convert from PEtab 1.0 to PEtab 2.0 format. @@ -153,9 +153,9 @@ def create_experiment_id(sim_cond_id: str, preeq_cond_id: str) -> str: if preeq_cond_id: preeq_cond_id = f"{preeq_cond_id}_" exp_id = f"experiment__{preeq_cond_id}__{sim_cond_id}" - if exp_id in experiments: # noqa: B023 + if exp_id in experiments: i = 1 - while f"{exp_id}_{i}" in experiments: # noqa: B023 + while f"{exp_id}_{i}" in experiments: i += 1 exp_id = f"{exp_id}_{i}" return exp_id @@ -325,9 +325,11 @@ def _copy_file(src: Path | str, dest: Path): src = Path(src.removeprefix("file:/")) if is_url(src): - with get_handle(src, mode="r") as src_handle: - with open(dest, "w") as dest_handle: - dest_handle.write(src_handle.handle.read()) + with ( + get_handle(src, mode="r") as src_handle, + open(dest, "w") as dest_handle, + ): + dest_handle.write(src_handle.handle.read()) return try: diff --git a/pyproject.toml b/pyproject.toml index fe95dc83..e700d0c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,6 +129,9 @@ convention = "pep257" [tool.ruff.lint.per-file-ignores] "tests/*" = ["T201"] +"petab/v1/C.py" = ["N999"] +"petab/v2/C.py" = ["N999"] +"petab/v1/math/SympyVisitor.py" = ["N999"] [tool.ruff.format] docstring-code-format = true diff --git a/tests/test_deprecation_warnings.py b/tests/test_deprecation_warnings.py index 5845a6f0..7680d14b 100644 --- a/tests/test_deprecation_warnings.py +++ b/tests/test_deprecation_warnings.py @@ -5,21 +5,21 @@ def test_deprecated_global(): with pytest.warns(DeprecationWarning): - from petab import Problem # noqa + from petab import Problem - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning): # noqa PT031 import petab petab.Problem() - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning): # noqa PT031 import petab.parameters petab.parameters # noqa with warnings.catch_warnings(): warnings.simplefilter("error") - from petab.v1 import Problem # noqa + from petab.v1 import Problem Problem() diff --git a/tests/v1/test_calculate.py b/tests/v1/test_calculate.py index c13105a8..a0c6e26b 100644 --- a/tests/v1/test_calculate.py +++ b/tests/v1/test_calculate.py @@ -185,8 +185,10 @@ def model_non_numeric_overrides(): OBSERVABLE_FORMULA: ["A"], OBSERVABLE_TRANSFORMATION: [LOG], NOISE_FORMULA: [ - "2*noiseParameter1_obs_a + " - "noiseParameter2_obs_a + par2 + obs_a" + ( + "2*noiseParameter1_obs_a + " + "noiseParameter2_obs_a + par2 + obs_a" + ) ], } ).set_index([OBSERVABLE_ID]) diff --git a/tests/v1/test_combine.py b/tests/v1/test_combine.py index e685bf2b..50c87b32 100644 --- a/tests/v1/test_combine.py +++ b/tests/v1/test_combine.py @@ -4,9 +4,9 @@ from pathlib import Path import pandas as pd +from petab.C import * import petab.v1 as petab -from petab.C import * from petab.v1.models.sbml_model import SbmlModel # import fixtures diff --git a/tests/v1/test_conditions.py b/tests/v1/test_conditions.py index 45059ba1..6fb75d1f 100644 --- a/tests/v1/test_conditions.py +++ b/tests/v1/test_conditions.py @@ -7,10 +7,10 @@ import numpy as np import pandas as pd import pytest +from petab.C import * import petab from petab import conditions -from petab.C import * def test_get_parametric_overrides(): diff --git a/tests/v1/test_deprecated.py b/tests/v1/test_deprecated.py index ef96f2e9..46a37fd3 100644 --- a/tests/v1/test_deprecated.py +++ b/tests/v1/test_deprecated.py @@ -17,13 +17,13 @@ def test_problem_with_sbml_model(): ( model, condition_df, - observable_df, + _observable_df, measurement_df, parameter_df, ) = create_test_data() with pytest.deprecated_call(): - petab_problem = petab.Problem( # noqa: F811 + petab_problem = petab.Problem( model=model, condition_df=condition_df, measurement_df=measurement_df, diff --git a/tests/v1/test_distributions.py b/tests/v1/test_distributions.py index f4b3e3fe..aace4487 100644 --- a/tests/v1/test_distributions.py +++ b/tests/v1/test_distributions.py @@ -32,7 +32,6 @@ Laplace(1, 0.5, log=True), Normal(2, 1, trunc=(1, 2)), Normal(2, 1, log=True, trunc=(0.5, 8)), - Normal(2, 1, log=10), Laplace(1, 2, trunc=(1, 2)), Laplace(1, 0.5, log=True, trunc=(0.5, 8)), Cauchy(2, 1), diff --git a/tests/v1/test_lint.py b/tests/v1/test_lint.py index 4ad2e9b1..5dec74be 100644 --- a/tests/v1/test_lint.py +++ b/tests/v1/test_lint.py @@ -5,10 +5,10 @@ import pandas as pd import pytest +from petab.C import * import petab from petab import lint -from petab.C import * # import fixtures pytest_plugins = [ @@ -136,8 +136,10 @@ def test_assert_overrides_match_parameter_count(): OBSERVABLE_ID: ["0obsPar1noisePar", "2obsPar0noisePar"], OBSERVABLE_FORMULA: [ "1.0", - "observableParameter1_2obsPar0noisePar + " - "observableParameter2_2obsPar0noisePar", + ( + "observableParameter1_2obsPar0noisePar + " + "observableParameter2_2obsPar0noisePar" + ), ], NOISE_FORMULA: ["noiseParameter1_0obsPar1noisePar", "1.0"], } @@ -437,19 +439,16 @@ def test_petablint_succeeds(): measurement_file = os.path.join( script_path, dir_isensee, "Isensee_measurementData.tsv" ) - result = subprocess.run(["petablint", "-m", measurement_file]) # noqa: S603,S607 - assert result.returncode == 0 + subprocess.run(["petablint", "-m", measurement_file], check=True) # noqa: S603,S607 # run with yaml yaml_file = os.path.join(script_path, dir_fujita, "Fujita.yaml") - result = subprocess.run(["petablint", "-v", "-y", yaml_file]) # noqa: S603,S607 - assert result.returncode == 0 + subprocess.run(["petablint", "-v", "-y", yaml_file], check=True) # noqa: S603,S607 parameter_file = os.path.join( script_path, dir_fujita, "Fujita_parameters.tsv" ) - result = subprocess.run(["petablint", "-v", "-p", parameter_file]) # noqa: S603,S607 - assert result.returncode == 0 + subprocess.run(["petablint", "-v", "-p", parameter_file], check=True) # noqa: S603,S607 def test_assert_measurement_conditions_present_in_condition_table(): diff --git a/tests/v1/test_measurements.py b/tests/v1/test_measurements.py index 10f5ba98..14a40954 100644 --- a/tests/v1/test_measurements.py +++ b/tests/v1/test_measurements.py @@ -5,9 +5,9 @@ import numpy as np import pandas as pd +from petab.C import * import petab -from petab.C import * def test_get_measurement_df(): diff --git a/tests/v1/test_model_pysb.py b/tests/v1/test_model_pysb.py index 57371c79..66ba3c9d 100644 --- a/tests/v1/test_model_pysb.py +++ b/tests/v1/test_model_pysb.py @@ -2,7 +2,6 @@ import pysb import pytest - from petab.models.pysb_model import ( PySBModel, parse_species_name, diff --git a/tests/v1/test_observables.py b/tests/v1/test_observables.py index c9932b0d..4941f38b 100644 --- a/tests/v1/test_observables.py +++ b/tests/v1/test_observables.py @@ -5,9 +5,9 @@ import pandas as pd import pytest +from petab.C import * import petab -from petab.C import * # import fixtures pytest_plugins = [ diff --git a/tests/v1/test_parameter_mapping.py b/tests/v1/test_parameter_mapping.py index 4fe44aa5..e02d349b 100644 --- a/tests/v1/test_parameter_mapping.py +++ b/tests/v1/test_parameter_mapping.py @@ -3,10 +3,10 @@ import numpy as np import pandas as pd - -import petab from petab.C import * from petab.models.sbml_model import SbmlModel + +import petab from petab.v1.parameter_mapping import _apply_parameter_table # import fixtures diff --git a/tests/v1/test_parameters.py b/tests/v1/test_parameters.py index 33e7c97d..7aec946f 100644 --- a/tests/v1/test_parameters.py +++ b/tests/v1/test_parameters.py @@ -6,9 +6,9 @@ import numpy as np import pandas as pd import pytest +from petab.C import * import petab -from petab.C import * def test_get_optimization_parameter_scaling(): @@ -106,12 +106,12 @@ def test_get_parameter_df(): PARAMETER_NAME: ["different_name2", "name3"], } ) - for name in parameter_dfs: + for name, df in parameter_dfs.items(): with tempfile.NamedTemporaryFile( mode="w", delete=False, dir=directory ) as fh: parameter_files[name] = fh.name - parameter_dfs[name].to_csv(fh, sep="\t", index=False) + df.to_csv(fh, sep="\t", index=False) # Check that subset files are correctly combined assert petab.get_parameter_df(parameter_files["complete"]).equals( petab.get_parameter_df( diff --git a/tests/v1/test_petab.py b/tests/v1/test_petab.py index af745baf..fd882205 100644 --- a/tests/v1/test_petab.py +++ b/tests/v1/test_petab.py @@ -9,12 +9,12 @@ import numpy as np import pandas as pd import pytest +from petab.C import * +from petab.models.sbml_model import SbmlModel from yaml import safe_load import petab import petab.v1 -from petab.C import * -from petab.models.sbml_model import SbmlModel from petab.v1 import Problem @@ -370,8 +370,10 @@ def test_flatten_timepoint_specific_output_overrides(): "x", ], NOISE_FORMULA: [ - "(observableParameter1_obs1 + observableParameter2_obs1)" - " * noiseParameter1_obs1", + ( + "(observableParameter1_obs1 + observableParameter2_obs1)" + " * noiseParameter1_obs1" + ), 1, ], } @@ -392,24 +394,36 @@ def test_flatten_timepoint_specific_output_overrides(): "obs2__condition1", ], OBSERVABLE_FORMULA: [ - f"observableParameter1_{obs1_1_1_1}" - f" + observableParameter2_{obs1_1_1_1}", - f"observableParameter1_{obs1_2_1_1}" - f" + observableParameter2_{obs1_2_1_1}", - f"observableParameter1_{obs1_2_2_1}" - f" + observableParameter2_{obs1_2_2_1}", + ( + f"observableParameter1_{obs1_1_1_1}" + f" + observableParameter2_{obs1_1_1_1}" + ), + ( + f"observableParameter1_{obs1_2_1_1}" + f" + observableParameter2_{obs1_2_1_1}" + ), + ( + f"observableParameter1_{obs1_2_2_1}" + f" + observableParameter2_{obs1_2_2_1}" + ), "x", ], NOISE_FORMULA: [ - f"(observableParameter1_{obs1_1_1_1}" - f" + observableParameter2_{obs1_1_1_1})" - f" * noiseParameter1_{obs1_1_1_1}", - f"(observableParameter1_{obs1_2_1_1}" - f" + observableParameter2_{obs1_2_1_1})" - f" * noiseParameter1_{obs1_2_1_1}", - f"(observableParameter1_{obs1_2_2_1}" - f" + observableParameter2_{obs1_2_2_1})" - f" * noiseParameter1_{obs1_2_2_1}", + ( + f"(observableParameter1_{obs1_1_1_1}" + f" + observableParameter2_{obs1_1_1_1})" + f" * noiseParameter1_{obs1_1_1_1}" + ), + ( + f"(observableParameter1_{obs1_2_1_1}" + f" + observableParameter2_{obs1_2_1_1})" + f" * noiseParameter1_{obs1_2_1_1}" + ), + ( + f"(observableParameter1_{obs1_2_2_1}" + f" + observableParameter2_{obs1_2_2_1})" + f" * noiseParameter1_{obs1_2_2_1}" + ), 1, ], } diff --git a/tests/v1/test_sbml.py b/tests/v1/test_sbml.py index c38f5ab5..37524514 100644 --- a/tests/v1/test_sbml.py +++ b/tests/v1/test_sbml.py @@ -8,7 +8,7 @@ from petab.v1.models.sbml_model import SbmlModel sys.path.append(os.getcwd()) -import petab # noqa: E402 +import petab def create_test_data(): diff --git a/tests/v1/test_simplify.py b/tests/v1/test_simplify.py index 1724f8bb..18779c2a 100644 --- a/tests/v1/test_simplify.py +++ b/tests/v1/test_simplify.py @@ -5,12 +5,12 @@ import pandas as pd import pytest from pandas.testing import * - -from petab import Problem -from petab.C import * # noqa: F403 +from petab.C import * from petab.models.sbml_model import SbmlModel from petab.simplify import * +from petab import Problem + @pytest.fixture def problem() -> Problem: diff --git a/tests/v1/test_simulate.py b/tests/v1/test_simulate.py index 7945b1bb..7cddc674 100644 --- a/tests/v1/test_simulate.py +++ b/tests/v1/test_simulate.py @@ -8,9 +8,9 @@ import pandas as pd import pytest import scipy.stats +from petab.C import MEASUREMENT import petab -from petab.C import MEASUREMENT class TestSimulator(petab.simulate.Simulator): diff --git a/tests/v1/test_visualization.py b/tests/v1/test_visualization.py index 3c5a3a65..eadce3d7 100644 --- a/tests/v1/test_visualization.py +++ b/tests/v1/test_visualization.py @@ -5,9 +5,9 @@ import matplotlib.pyplot as plt import pytest +from petab.C import * import petab -from petab.C import * from petab.v1.visualize import ( plot_goodness_of_fit, plot_residuals_vs_simulation, diff --git a/tests/v1/test_visualization_data_overview.py b/tests/v1/test_visualization_data_overview.py index 1b42fdbb..45e9324e 100644 --- a/tests/v1/test_visualization_data_overview.py +++ b/tests/v1/test_visualization_data_overview.py @@ -1,9 +1,10 @@ from pathlib import Path from tempfile import TemporaryDirectory -import petab from petab.visualize.data_overview import create_report +import petab + def test_data_overview(): """Data overview generation with Fujita example data from this diff --git a/tests/v1/test_yaml.py b/tests/v1/test_yaml.py index 7dc0b684..419facd8 100644 --- a/tests/v1/test_yaml.py +++ b/tests/v1/test_yaml.py @@ -5,7 +5,6 @@ import pytest from jsonschema.exceptions import ValidationError - from petab.yaml import create_problem_yaml, get_path_prefix, validate diff --git a/tests/v2/test_calculate.py b/tests/v2/test_calculate.py index cba929ae..19eacb0d 100644 --- a/tests/v2/test_calculate.py +++ b/tests/v2/test_calculate.py @@ -185,8 +185,10 @@ def model_non_numeric_overrides(): OBSERVABLE_FORMULA: ["A"], NOISE_DISTRIBUTION: [LOG_NORMAL], NOISE_FORMULA: [ - "2*noiseParameter1_obs_a + " - "noiseParameter2_obs_a + par2 + obs_a" + ( + "2*noiseParameter1_obs_a + " + "noiseParameter2_obs_a + par2 + obs_a" + ) ], NOISE_PLACEHOLDERS: [ "noiseParameter1_obs_a;noiseParameter2_obs_a" diff --git a/tests/v2/test_conversion.py b/tests/v2/test_conversion.py index a98ea039..3380c363 100644 --- a/tests/v2/test_conversion.py +++ b/tests/v2/test_conversion.py @@ -3,8 +3,7 @@ import pandas as pd import pytest -import petab.v1 as v1 -import petab.v2 as v2 +from petab import v1, v2 from petab.v2 import Problem from petab.v2.petab1to2 import petab1to2, v1v2_observable_df diff --git a/tests/v2/test_core.py b/tests/v2/test_core.py index 06cabe60..6f7c3845 100644 --- a/tests/v2/test_core.py +++ b/tests/v2/test_core.py @@ -821,8 +821,10 @@ def test_petablint_v2(tmpdir): problem.measurement_tables[0].rel_path = "measurements.tsv" problem.to_files(Path(tmpdir)) - result = subprocess.run(["petablint", str(Path(tmpdir, "problem.yaml"))]) # noqa: S603,S607 - assert result.returncode == 0 + subprocess.run( # noqa S607 + ["petablint", str(Path(tmpdir, "problem.yaml"))], # noqa S607 + check=True, + ) def test_problem_id(tmpdir): diff --git a/tests/v2/test_mapping.py b/tests/v2/test_mapping.py index e60e9082..933f9a29 100644 --- a/tests/v2/test_mapping.py +++ b/tests/v2/test_mapping.py @@ -6,7 +6,7 @@ import pytest from petab.v2 import get_mapping_df, write_mapping_df -from petab.v2.C import * # noqa: F403 +from petab.v2.C import * def test_get_mapping_df():