![test workflow](https://github.com/c-salomonsen/lls/actions/workflows/tests.yml/badge.svg)

# Linear Least Squares - Fast Kinetic Modeling

This repository contains the source code for a fast linear least squares implementation of reversible and irreversible two-tissue compartment models, as well as a fast implementation of the Patlak plot.

## Installation

There are two ways of installing the project:

### 1. From my private python packaging index (simplest)

Start by installing a version of PyTorch compatible with your machine:

```sh
pip install torch
# or
uv add torch
```

then install with:

```sh
pip install --index-url https://pypi.salodev.no/ lls
# or
uv add --index-url https://pypi.salodev.no/ lls
```

If you are on an older macOS system with Intel chip, **skip installing PyTorch**, and instead install with:

```sh
pip install --index-url https://pypi.salodev.no/ lls --extra mac-intel
# or
uv add --index-url https://pypi.salodev.no/ lls --extra mac-intel
```

Which will install a verified PyTorch version for you. Then [verify the installation](#verify-installation-success).

### 2. From this repository

Ensure you have ssh-keys authenticated with your GitHub account, then install using:

```sh
python -m venv venv
source venv/bin/activate
pip install "lls @ git+ssh://git@github.com/c-salomonsen/LLS.git@main"
# or 
uv init
uv add "lls @ git+ssh://git@github.com/c-salomonsen/LLS.git@main"
```

### Verify installation success

The repo only provides a single function `get_kinetic_maps`, and you validate that it works by:

```sh
python -c "from lls import get_kinetic_maps"
```

## Usage

This repository provides a command `get_kinetic_maps`, which accepts `numpy` arrays of tissue curves, the arterial input function and a vector denoting sampling times.

```python
import scipy.ndimage as ndimage

from lls import get_kinetic_maps

# Setup/load your data
dpet_img: np.ndarray  # shape (time, depth, height, width)
aif: np.ndarray  # shape (time)
time: np.ndarray  # shape (time)

T, D, H, W = dpet_img.shape

# Reshape dpet_img to tissue curves (for voxel-wise modeling)
tissue_curves = dpet_img.reshape(T, D * H * W)  # shape (time, n_curves)

# Compute compartment model
params = get_kinetic_maps(
    tissue_curves,
    aif,
    time,
    method="lls3k",  # Also supports 'lls4k' or 'patlak'.
    device="cpu",  # Also supports 'mps' or 'cuda' for hardware acceleration
    n_batches=3,  # Testing shows that for a ~10 million voxel image a batch size of 3 is fastest.
)

for k, p in params.items():
    p = p.reshape(D * H * W)  # Reshape to image dimensions (3D)

    # Recommended step: add median filter to remove salt'n'pepper noise
    p = ndimage.median_filter(p, size=(3, 3, 3), mode="nearest")

    # Further processing or visualization
    ...
```
