Fitting models¶
If your main question is "how do I use this with my own files?", start with Use your own TAC, AIF, and time data.
Fast path¶
Most users only need this:
- Load TAC, AIF, and time arrays from disk.
- Make sure
tacsis(T, N)or(T,). - Call
fit_compartment_model(...).
High-level API¶
Use fit_compartment_model(...) when your inputs are NumPy arrays or when you want one entrypoint that handles basic preprocessing.
fit_compartment_model(
tacs,
aif,
time,
*,
model="3k_vB",
vB=None,
aif_time=None,
interpolate=True,
frame_duration=1.0 / 60.0,
time_unit="min",
device=None,
dtype=None,
reg=1e-6,
eps=1e-8,
column_scale=True,
nnls=False,
nnls_max_iter=200,
nnls_tol=1e-6,
)
Inputs¶
tacs: tissue activity curves with shape(T, N)or(T,)aif: arterial input function with shape(T,)time: TAC time vector with shape(T,)aif_time: optional separate time vector foraif
Outputs¶
The function always returns NumPy arrays:
kinetic_paramsfitted_tacs, shape(T, N)for multiple TACsmse, shape(N,)
Supported model variants¶
Use one of the following model names:
"3k"or"lls_3k": fixedvB, returns[K1, k2, k3, Ki]"3k_vB"or"lls_3k_vB": fitsvB, returns[K1, k2, k3, vB, Ki]"4k"or"lls_4k": fixedvB, returns[K1, k2, k3, k4, Ki]"4k_vB"or"lls_4k_vB": fitsvB, returns[K1, k2, k3, k4, vB, Ki]
For fixed-vB models, pass vB= explicitly.
Time handling¶
time_unit="min"is the default.- If your time vectors are in seconds, set
time_unit="s". interpolate=Truebuilds a common internal grid before fitting.frame_durationis interpreted in minutes.- If
interpolate=False,timeandaif_timemust match exactly.
Recommended usage¶
Typical example:
import numpy as np
from lls import fit_compartment_model
time_sec = np.linspace(0, 45 * 60, 30, dtype=np.float32)
aif = np.exp(-time_sec / 120.0).astype(np.float32)
tacs = (aif[:, None] * np.linspace(0.5, 1.2, 100)[None, :]).astype(np.float32)
params, fitted, mse = fit_compartment_model(
tacs=tacs,
aif=aif,
time=time_sec,
model="4k_vB",
time_unit="s",
interpolate=True,
frame_duration=1.0 / 60.0,
)
Fixed-vB fitting:
params, fitted, mse = fit_compartment_model(
tacs=tacs,
aif=aif,
time=time_sec,
model="3k",
vB=0.05,
time_unit="s",
)
Separate TAC and AIF time vectors:
params, fitted, mse = fit_compartment_model(
tacs=tacs,
aif=aif,
time=time_tac_s,
aif_time=time_aif_s,
model="3k_vB",
time_unit="s",
interpolate=True,
)
Low-level Torch APIs¶
If you already manage tensors, dtype, device placement, and interpolation yourself, call the solver functions directly:
lls_3k(Cpet, Cp, t, vB=...)lls_3k_vB(Cpet, Cp, t)lls_4k(Cpet, Cp, t, vB=...)lls_4k_vB(Cpet, Cp, t)
These functions:
- require
torch.Tensorinputs - expect matching dtype and device
- return Torch tensors
- do not perform time conversion or interpolation
Example:
import torch
from lls import lls_4k_vB
t = torch.linspace(0.0, 40.0, 30, dtype=torch.float32)
Cp = torch.exp(-t / 10.0)
Cpet = Cp[:, None] * torch.linspace(0.6, 1.1, 64)[None, :]
kinetic_params, fitted_Cpet, mse = lls_4k_vB(
Cpet=Cpet,
Cp=Cp,
t=t,
)
For validation workflows, see Simulation pipeline and Prediction and benchmarking.