Skip to content

fit.identifiability

Parameter identifiability by profile likelihood.

A fit gives the parameters which describe the data best. Identifiability asks how well the data determines every one of them: a parameter is identifiable if the data constrains it to a finite interval, and non-identifiable if it can be changed without the model describing the data worse.

The profile likelihood answers this by scanning every parameter around the optimum. The scanned parameter is fixed at a value and all other parameters are optimized again, so the profile is the best cost the model reaches with the parameter at that value (Raue et al. 2009):

PL(θi) = min over θj≠i of cost(θ)

The cost of sbmlsim is the cost of scipy.optimize.least_squares, i.e., half the sum of the squared weighted residuals, cost = 0.5 * Σ r². With residuals which are standardized by the errors of the data, 2 * cost is the negative log-likelihood up to a constant, and the profile is compared with the threshold of the likelihood ratio test (Raue et al. 2009, Kreutz et al. 2013):

cost_threshold = cost_min + chi2.ppf(alpha, df) / 2

with df = 1 for the pointwise confidence intervals of single parameters, i.e., a threshold of 1.92 above the minimal cost at 95% confidence, and df = #parameters for simultaneous intervals. The values of a parameter at which the profile crosses the threshold are the bounds of its confidence interval. The intervals are invariant under a transformation of the parameters and may be asymmetric, which is where the intervals of the Fisher information matrix fail for the non-linear models of systems biology (Wieland et al. 2021).

The shape of the profile classifies the parameter (Raue et al. 2009):

  • identifiable: the profile crosses the threshold on both sides of the optimum, the confidence interval is finite,
  • practically non-identifiable: the profile has a minimum but stays below the threshold on one or both sides, i.e., up to the bound of the parameter. The data does not determine the parameter towards small and/or large values,
  • structurally non-identifiable: the profile is flat over the whole scanned range, the parameter is compensated by the other parameters and the data carries no information about it.

The scans run in logarithmic parameter space, as the optimization does. The step along a profile is adaptive (Schälte et al. 2023): a step which raises the cost by more than a fraction of the threshold is halved and repeated, a step which raises it by little is enlarged, so the profile is resolved where it changes. The other parameters start from the previous point of the profile (Simpson & Maclaren 2023) and their paths are stored, so a parameter which is coupled to the scanned one is seen in its path (Maiwald et al. 2016). A scan stops when the profile crosses the threshold, at the bound of the parameter or after max_points; a bound which is reached below the threshold is an open interval (Borisov & Metelkin 2020). A scan which finds a lower cost than the optimum reports it: the fit did not converge, and the threshold is taken relative to the lowest cost of all profiles.

The scans are independent, so they run in the worker pool of the fit runner, two scans per parameter. A parallel analysis needs the same if __name__ == "__main__": guard as a parallel fit.

References

Raue A, Kreutz C, Maiwald T, Bachmann J, Schilling M, Klingmüller U, Timmer J. Structural and practical identifiability analysis of partially observed dynamical models by exploiting the profile likelihood. Bioinformatics. 2009;25(15):1923-1929. doi:10.1093/bioinformatics/btp358

Kreutz C, Raue A, Kaschek D, Timmer J. Profile likelihood in systems biology. FEBS J. 2013;280(11):2564-2571. doi:10.1111/febs.12276

Maiwald T, Hass H, Steiert B, Vanlier J, Engesser R, Raue A, Kipkeew F, Bock HH, Kaschek D, Kreutz C, Timmer J. Driving the model to its limit: profile likelihood based model reduction. PLoS ONE. 2016;11(9):e0162366. doi:10.1371/journal.pone.0162366

Wieland FG, Hauber AL, Rosenblatt M, Tönsing C, Timmer J. On structural and practical identifiability. Curr Opin Syst Biol. 2021;25:60-69. doi:10.1016/j.coisb.2021.03.005

Borisov I, Metelkin E. Confidence intervals by constrained optimization, an algorithm and software package for practical identifiability analysis in systems biology. PLoS Comput Biol. 2020;16(12):e1008495. doi:10.1371/journal.pcbi.1008495

Simpson MJ, Maclaren OJ. Profile-wise analysis: a profile likelihood-based workflow for identifiability analysis, estimation, and prediction with mechanistic mathematical models. PLoS Comput Biol. 2023;19(9):e1011515. doi:10.1371/journal.pcbi.1011515

Schälte Y, Fröhlich F, Jost PJ, Vanhoefer J, Pathirana D, Stapor P, Lakrisenko P, Wang D, Raimúndez E, Merkt S, Schmiester L, Städter P, Grein S, Dudkin E, Doresic D, Weindl D, Hasenauer J. pyPESTO: a modular and scalable tool for parameter estimation for dynamic models. Bioinformatics. 2023;39(11):btad711. doi:10.1093/bioinformatics/btad711

Identifiability

Bases: StrEnum

Identifiability of a parameter, read from the shape of its profile.

The classification follows Raue et al. 2009: a parameter is identifiable if its profile crosses the threshold on both sides of the optimum, practically non-identifiable if the profile stays below the threshold up to a bound of the parameter, on one or both sides, and structurally non-identifiable if the profile is flat on both sides.

label property

label

Get the description of the identifiability.

is_identifiable property

is_identifiable

Check whether the parameter is identifiable.

ProfileSettings dataclass

ProfileSettings(
    alpha=0.95,
    degrees_of_freedom=1,
    initial_step=0.1,
    min_step=0.01,
    max_step=1.0,
    step_factor=2.0,
    max_cost_fraction=0.2,
    max_points=50,
    flatness=0.05,
    reoptimize=True,
    optimizer_kwargs=(lambda: {"diff_step": 0.05})(),
)

Settings of a profile likelihood analysis.

The steps are in logarithmic parameter space, i.e., in decades of the parameter, because the scans run in the space the optimization runs in.

Attributes:

Name Type Description
alpha float

confidence level of the intervals.

degrees_of_freedom int

degrees of freedom of the chi-square threshold, 1 for pointwise confidence intervals of single parameters, the number of parameters for simultaneous intervals.

initial_step float

first step of a scan in decades of the parameter.

min_step float

smallest step in decades, a step is not halved below it.

max_step float

largest step in decades.

step_factor float

factor a step is reduced or enlarged by.

max_cost_fraction float

largest increase of the cost a single step may cause, as a fraction of the distance between the minimal cost and the threshold. A step with a larger increase is reduced and repeated, so a profile has at least 1 / max_cost_fraction points between the optimum and the threshold.

max_points int

largest number of points of a scan in one direction.

flatness float

a profile which rises by less than this fraction of the distance between the minimal cost and the threshold, on both sides, is flat, i.e., the parameter is structurally non-identifiable.

reoptimize bool

optimize the other parameters at every point of a scan, which is the profile likelihood. Without it the other parameters stay at the optimum, which is a plain scan of the cost and a lower bound of the profile: it is faster and finds the non-identifiable parameters, but its intervals are too narrow for coupled parameters.

optimizer_kwargs Mapping[str, Any]

arguments of scipy.optimize.least_squares for the optimization of the other parameters.

delta property

delta

Get the chi-square quantile of the confidence level.

This is the threshold on -2 log L, i.e., on twice the cost.

threshold

threshold(cost)

Get the threshold on the cost for a minimal cost.

Parameters:

Name Type Description Default
cost float

minimal cost.

required

Returns:

Type Description
float

The cost at which the profile crosses the confidence level,

float

cost + delta / 2 for the cost convention 0.5 * Σ r².

to_dict

to_dict()

Convert to a dictionary of JSON serializable values.

from_dict staticmethod

from_dict(d)

Create settings from a dictionary, i.e., from the stored JSON.

ParameterProfile dataclass

ParameterProfile(
    pid,
    values,
    costs,
    paths,
    converged,
    index_optimum,
    ci_lower=None,
    ci_upper=None,
    identifiability=None,
)

Profile likelihood of a single parameter.

The points are sorted by the value of the parameter, the optimum is one of them. Every point carries the full parameter vector the cost was reached with, so the paths of the other parameters along the profile are known.

Attributes:

Name Type Description
pid str

id of the profiled parameter.

values ndarray

values of the parameter at the points, ascending.

costs ndarray

cost at every point.

paths ndarray

values of all parameters at every point, one row per point in the order of the parameters of the problem.

converged ndarray

whether the optimization of the other parameters converged at every point.

index_optimum int

index of the optimum in the points.

ci_lower float | None

lower bound of the confidence interval, None if the profile stays below the threshold down to the bound.

ci_upper float | None

upper bound of the confidence interval, None if the profile stays below the threshold up to the bound.

identifiability Identifiability | None

classification of the parameter, set by evaluate.

value_optimum property

value_optimum

Get the value of the parameter at the optimum.

cost_optimum property

cost_optimum

Get the cost at the optimum.

cost_min property

cost_min

Get the lowest cost of the profile, of the converged points.

side

side(direction)

Get the points on one side of the optimum, from the optimum outward.

Parameters:

Name Type Description Default
direction int

-1 for the points below the optimum, +1 above.

required

Returns:

Type Description
tuple[ndarray, ndarray]

The values and the costs, the optimum first.

crossing

crossing(threshold, direction)

Get the value at which the profile crosses the threshold.

The crossing is interpolated between the last point below and the first point above the threshold, in logarithmic space where the two values are positive, i.e. where the scan ran on a logarithmic scale, and linearly otherwise.

Parameters:

Name Type Description Default
threshold float

threshold on the cost.

required
direction int

-1 for the crossing below the optimum, +1 above.

required

Returns:

Type Description
float | None

The value of the parameter at the crossing, None if the profile

float | None

does not reach the threshold on that side.

rise

rise(direction)

Get how far the profile rises above its minimum on one side.

Parameters:

Name Type Description Default
direction int

-1 for the side below the optimum, +1 above.

required

Returns:

Type Description
float

The largest cost on the side minus the lowest cost of the profile.

evaluate

evaluate(threshold, flatness_cost)

Set the confidence interval and the classification.

Parameters:

Name Type Description Default
threshold float

threshold on the cost of the confidence intervals.

required
flatness_cost float

rise of the cost below which a side is flat.

required

to_dict

to_dict()

Convert to a dictionary of JSON serializable values.

from_dict staticmethod

from_dict(d)

Create a profile from a dictionary, i.e., from the stored JSON.

IdentifiabilityResult dataclass

IdentifiabilityResult(
    opid,
    parameter_set,
    parameters,
    settings,
    fit_settings,
    cost,
    profiles,
    duration=0.0,
)

Result of a profile likelihood analysis.

Attributes:

Name Type Description
opid str

id of the optimization problem.

parameter_set ParameterSet

parameters the profiles were computed around.

parameters list[FitParameter]

fit parameters of the problem, with their bounds and units.

settings ProfileSettings

settings of the analysis.

fit_settings FitSettings

settings of the fit, which define the cost.

cost float

cost of the parameter set.

profiles dict[str, ParameterProfile]

profile of every analysed parameter by parameter id.

duration float

duration of the analysis in seconds.

pids property

pids

Get the ids of the parameters of the problem.

cost_min property

cost_min

Get the lowest cost, of the parameter set or of any profile point.

threshold property

threshold

Get the threshold on the cost of the confidence intervals.

flatness_cost property

flatness_cost

Get the rise of the cost below which a profile is flat.

n_identifiable property

n_identifiable

Get the number of identifiable parameters.

better_optimum property

better_optimum

Check whether a profile found a lower cost than the parameter set.

The threshold is relative to the lowest cost, so the intervals are still valid, but the fit did not converge to the optimum.

evaluate

evaluate()

Set the confidence intervals and the classifications.

parameter

parameter(pid)

Get the fit parameter of an id.

Raises:

Type Description
KeyError

if the problem has no parameter with the id.

summary_df

summary_df()

Get the table of the analysis, one row per profiled parameter.

Returns:

Type Description
DataFrame

Table with the parameter, its value, unit and bounds, the

DataFrame

confidence interval (NaN for an open side), the classification,

DataFrame

the number of points of the profile and its lowest cost.

report

report(path=None, print_output=False)

Get the text report of the analysis.

Parameters:

Name Type Description Default
path Path | None

file to write the report to.

None
print_output bool

print the report.

False

Returns:

Type Description
str

The report.

to_dict

to_dict()

Convert to a dictionary of JSON serializable values.

from_dict staticmethod

from_dict(d)

Create a result from a dictionary, i.e., from the stored JSON.

to_json

to_json(path=None)

Store the result as JSON.

Parameters:

Name Type Description Default
path Path | None

file to write, the JSON string is returned if it is None.

None

Returns:

Type Description
str | Path

The path or the JSON string.

from_json staticmethod

from_json(json_info)

Load a result from a JSON file or string.

Parameters:

Name Type Description Default
json_info str | Path

path of the file or the JSON string.

required

Returns:

Type Description
IdentifiabilityResult

The result.

cost_threshold

cost_threshold(cost, alpha=0.95, df=1)

Get the threshold on the cost of a likelihood ratio test.

The cost is 0.5 * Σ r², so the threshold is half the chi-square quantile above the minimal cost; at 95% and one degree of freedom it is 1.92.

Parameters:

Name Type Description Default
cost float

minimal cost.

required
alpha float

confidence level.

0.95
df int

degrees of freedom.

1

Returns:

Type Description
float

The threshold on the cost.

profile_likelihood

profile_likelihood(
    problem,
    settings,
    parameter_set,
    profile_settings=None,
    pids=None,
    n_cores=1,
    serial=False,
    show_progress=True,
)

Analyse the identifiability of the parameters of a fit.

Computes the profile likelihood of every parameter around the parameter set and classifies the parameters, see the module documentation.

Parameters:

Name Type Description Default
problem OptimizationProblem

optimization problem, initialized with the settings.

required
settings FitSettings

settings of the fit, which define the cost.

required
parameter_set ParameterSet

optimal parameters, e.g., the best run of a fit.

required
profile_settings ProfileSettings | None

settings of the analysis, the defaults if None.

None
pids Sequence[str] | None

parameters to profile, all parameters of the problem by default.

None
n_cores int

number of worker processes, the scans of the parameters run in parallel. A parallel analysis needs the if __name__ == "__main__": guard like a parallel fit.

1
serial bool

run the scans in this process, whatever n_cores says.

False
show_progress bool

show the progress of the scans on the console.

True

Returns:

Type Description
IdentifiabilityResult

The profiles with the confidence intervals and the classification.

Raises:

Type Description
KeyError

if a parameter id is not a parameter of the problem.

ValueError

if the parameter set is outside of the bounds of the problem.

plot_profiles

plot_profiles(result, path=None, ncols=3)

Plot the profiles of all parameters, one panel per parameter.

Parameters:

Name Type Description Default
result IdentifiabilityResult

result of the analysis.

required
path Path | None

file to save the figure to, the figure is returned otherwise.

None
ncols int

number of panels per row.

3

Returns:

Type Description
Any

The figure.

plot_profile

plot_profile(result, pid, path=None)

Plot the profile of a parameter with the paths of the other parameters.

The upper panel is the profile, the lower panel the other parameters along it, relative to their values at the optimum. A parameter which changes along the profile is coupled to the scanned one (Maiwald et al. 2016).

Parameters:

Name Type Description Default
result IdentifiabilityResult

result of the analysis.

required
pid str

id of the parameter.

required
path Path | None

file to save the figure to, the figure is returned otherwise.

None

Returns:

Type Description
Any

The figure.

plot_all

plot_all(result, output_dir, image_format='svg')

Write the overview figure and the figure of every profile.

Parameters:

Name Type Description Default
result IdentifiabilityResult

result of the analysis.

required
output_dir Path

directory of the figures.

required
image_format str

format of the figures.

'svg'

Returns:

Type Description
list[Path]

The paths of the figures, the overview first.