# GlucoFlow: four Euler steps instead of a diffusion chain, and a dataset contract you can swap

> The research code for a conditional rectified flow model that samples full future trajectories in one, two, or four steps, ships a Stage-A temporal checkpoint, and separates the shared schema, the model, and the evaluation utilities so a new target domain plugs in through one adapter.

**OliverDOU776/Few-step-probabilistic-glucose-forecasting-from-continuous-glucose-monitoring-and-meal-images** — Official research code for GlucoFlow: few-step multimodal probabilistic glucose forecasting.

- Repository: https://github.com/OliverDOU776/Few-step-probabilistic-glucose-forecasting-from-continuous-glucose-monitoring-and-meal-images
- Stars: 397 · Forks: 13
- Language: Python
- License: not declared
- Published: 2026-09-18 · Updated: 2026-09-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/oliverdou776-few-step-probabilistic-glucose-forecasting-from-continuous-glucose-

## Inference cost is the nfe argument, and one to four steps is the whole claim

The model is a conditional rectified flow, and the argument for it is stated as a table row rather than as an abstract claim. The fast probabilistic inference row says it uses a conditional rectified flow with only 1, 2, or 4 Euler steps instead of a long diffusion chain. The number of function evaluations is the parameter: a diffusion sampler needs many forward passes to move from noise to a sample, and a rectified flow trained to move in nearly straight lines can be integrated with a handful of Euler steps.

The knob is named `nfe` in the sampling call, and the released checkpoint ships with a data-free test that exercises inference at exactly those three settings. The smoke test is described as a data-free checkpoint and NFE=1/2/4 inference test, so you can confirm the checkpoint loads and samples at every speed point before you have assembled a dataset at all.

That framing matters for the deployment claim in the documentation. One of the situations the project names as a good fit is iterative generative forecasting being too slow for the intended deployment path. If you wanted one trajectory, none of this would matter. The reason to sample a distribution rather than a point is precisely the cases the project lists: calibrated ranges, event probabilities, or any task-specific summary derived from many complete future trajectories.

## The Stage-A checkpoint assumes you bring your own normalization statistics

There is a released temporal checkpoint at `checkpoints/stage_a.pt`, and the quick start pairs it with a condition that is easy to skim past. Inputs must be normalized using statistics fitted on the training split of your target application. Not the statistics from the original study, not a set shipped in the repository, and not a global mean and variance over the whole series.

Loading it looks like this:

```python
from glucoflow.models import build_flow_model

model = build_flow_model(history_len=24, prediction_len=24)
state = torch.load(
    Path("checkpoints/stage_a.pt"),
    map_location="cpu",
    weights_only=True,
)
model.load_state_dict(state, strict=True)
model.eval()
```

Two details in that snippet are deliberate. `weights_only=True` restricts what the unpickler can construct and narrows the attack surface, but it does not make an untrusted checkpoint safe. PyTorch advises loading data only from trusted sources. And `strict=True` means the state dictionary must match the architecture exactly, so a mismatch fails loudly at load time instead of silently leaving some layers at their random initialisation. Combined with the separate instruction about normalization, the quick start is telling you that the checkpoint is a starting point for a shape and a scale you supply, not a plug and play predictor.

The architecture itself is parameterised by the two lengths you pass in. `history_len=24` and `prediction_len=24` are the example's choices, and they define the window the model consumes and the window it produces, so the same call shape serves a different target application with different horizons.

## The conditioning tensor is a slot, and the shipped encoder is only one way to fill it

Side information enters through a single optional argument, `meal_embed`, and the project is careful about what that name means. In the associated glucose study the tensor represents meal photographs and nutrients. For a different application it can represent anything at all, as long as it matches the configured embedding dimension. The documentation names text, categorical metadata, sensor context, interventions, and another modality as the alternatives.

The mechanism is the same either way:

```python
conditioning = torch.randn(2, 512)

with torch.no_grad():
    conditioned_samples = model.sample(
        history,
        nfe=2,
        num_samples=100,
        time_features=time_features,
        meal_embed=conditioning,
    )
```

The dimension is the contract. A 512-wide vector is what this example uses, and swapping in a text encoder or a sensor encoder means matching that width.

The optionality is handled explicitly rather than by crashing. The project lists a null token as one of the conditioning options, meaning a target application with no usable side information can pass an explicit absence instead of a zero tensor, so the model learns what to do with the missing case. That is a meaningfully better design than a zero vector, because a model can learn to distinguish a genuine all-zero measurement from no measurement.

For the modality dropout mentioned in the adaptation guidance, the point is the same: training with conditioning dropped out some of the time is what makes the conditional path degrade gracefully rather than collapsing when the input is absent.

## One dataset contract, and the glucose column names are not required to survive

The data interface is a single class, `glucoflow.data.adapters.base.DatasetOutput`, and the minimum time-series table is three columns:

```text
subject_id | timestamp | glucose_mgdl
```

A ready-to-run CSV template ships at `examples/custom_adapter.py`, and the directory it expects is small:

```text
my_dataset/
├── cgm.csv          # required
├── meals.csv        # optional conditioning events
└── subjects.csv     # optional subject metadata
```

Only the first file is required. The other two are optional because the conditioning path is optional, which ties the data layout back to the model signature: a dataset with no `meals.csv` still trains, it just trains the unconditional path.

What makes the contract usable outside diabetes is the two escape hatches the documentation offers. For a non-glucose application you can map the target signal into the supplied schema for a quick prototype, which is a few hours of work and gets you a baseline, or you can fork the schema and rename the fields to something that reads correctly in your domain. The project is explicit that you should not inherit the glucose defaults for the forecasting task itself: history length, forecast length, sampling interval, normalization strategy, and evaluation metrics all need to be chosen for the target application rather than copied.

Running the template is one command:

```bash
python examples/custom_adapter.py /path/to/my_dataset
```

## Training is two reference scripts, not a framework

There is no training loop abstraction here, and that is a deliberate shape. Two scripts are given as references rather than as a library API. `scripts/pretrain.py` is the reference for multi-dataset temporal pretraining, and `scripts/finetune_cgmacros.py` is the reference for target-domain adaptation with optional conditioning and modality dropout.

The documentation says plainly that the reusable modules under `src/glucoflow/` can also be called directly from a cleaner application-specific trainer. So the intended path for someone with their own training infrastructure is to import the model and the data utilities and write the loop themselves, using the reference scripts as worked examples rather than as a dependency.

The checkpoint decision is where this matters most. The released checkpoint is described as a useful initialisation when the target signal is sufficiently related to the original physiological time series, and the alternative is to pretrain the same architecture on your own source datasets. So there is no third option of fine-tuning a checkpoint that has nothing to do with your signal, which is a distinction worth holding onto given the model is described as reusable.

Alongside those, `scripts/prepare_data.py` handles preprocessing and feature caching, and `scripts/validate_release.py` performs source-tree and release-integrity checks. The presence of a release validation script in a project with no GitHub releases is a small oddity, and it suggests the release artefacts are produced and checked outside the tag system.

The package itself is small and the dependency list is honest about it: matplotlib, numpy, pandas, PyYAML, scikit-learn, scipy, and torch, with `open-clip-torch` and Pillow behind the vision extra and pytest and ruff behind dev.

## The documentation spends a step on keeping model selection honest

Step five of the bring-your-own-dataset list is not a code step. It is a rule: fit normalization, choose checkpoints, tune uncertainty scales, and select operating parameters using training and validation data only, and evaluate the held-out test split after the configuration is fixed.

That instruction is aimed at a specific and common failure in probabilistic forecasting. A point forecast evaluated with mean absolute error can hide a badly calibrated distribution, so the temptation is to look at the test split while tuning the uncertainty scale until the intervals look right. The evaluation utilities are organised to make that harder rather than easier: `src/glucoflow/evaluation/` holds reusable splits, windows, and probabilistic metrics, so the split is something you construct once and reuse rather than something each experiment redraws.

The calibration language elsewhere in the documentation is consistent with this. The project lists calibrated ranges and event probabilities as the reason to sample a distribution at all, and mentions tuning uncertainty scales as a parameter you choose. If the intervals are the product, they are also the easiest thing in a probabilistic model to make look good by accident.

The safety framing is stated once, at the top, and it is unambiguous: GlucoFlow is research software, not a medical device, and must not be used for diagnosis, insulin dosing, treatment, alarms, or other clinical decisions. Given the domain, that line is doing real work rather than being boilerplate.

## Conclusion

This suits a researcher who has a large time-series source dataset and a small target one, and who needs a distribution over futures rather than a point prediction, such as an interval for a threshold alarm or a trajectory for planning. It is not a clinical tool, and the project says so in a callout at the top of its own documentation: research software, not a medical device, not for diagnosis, dosing, treatment, or alarms. Before you run it, check three things: that your Python is between 3.10 and 3.12, since the package metadata excludes 3.13; that you can fit normalization statistics on your own training split rather than reusing the glucose defaults, which the documentation explicitly warns against; and that the released Stage-A checkpoint is related enough to your signal to be a useful initialisation rather than something you should pretrain from scratch. The last push was 2026-08-20 and the repository publishes no GitHub releases.

## FAQ

### What makes GlucoFlow faster than a diffusion sampler at inference?

It uses a conditional rectified flow integrated with as few as 1, 2, or 4 Euler steps rather than a long diffusion chain, and the number of function evaluations is the nfe argument to the sample call. A data-free smoke test exercises inference at all three settings.

### Can I use the released GlucoFlow checkpoint on my own dataset?

Yes, as an initialisation, and the documentation is specific about the condition: it helps when the target signal is sufficiently related to the original physiological time series. Inputs must be normalized with statistics fitted on the training split of your target application, and the alternative is pretraining the same architecture on your own source datasets.

### How does GlucoFlow handle side information that may be missing?

The model takes an optional conditioning tensor through meal_embed, which in the glucose study represents meal photographs and nutrients. A null token is one of the documented conditioning options, so an application with no usable side information can pass an explicit absence rather than an arbitrary placeholder, and adaptation uses modality dropout.

### Is GlucoFlow allowed for clinical use?

No. The project states plainly that it is research software, not a medical device, and must not be used for diagnosis, insulin dosing, treatment, alarms, or other clinical decisions. It requires Python 3.10 to 3.12 and ships as version 0.2.0 with no GitHub releases.

## Sources

- [Issues](https://github.com/OliverDOU776/Few-step-probabilistic-glucose-forecasting-from-continuous-glucose-monitoring-and-meal-images/issues)
- [OliverDOU776/Few-step-probabilistic-glucose-forecasting-from-continuous-glucose-monitoring-and-meal-images on GitHub](https://github.com/OliverDOU776/Few-step-probabilistic-glucose-forecasting-from-continuous-glucose-monitoring-and-meal-images)
- [README](https://github.com/OliverDOU776/Few-step-probabilistic-glucose-forecasting-from-continuous-glucose-monitoring-and-meal-images/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/oliverdou776-few-step-probabilistic-glucose-forecasting-from-continuous-glucose-
