Chronos4PM
Forecast process behavior with a frozen Chronos-2 model, using familiar PM4Py event logs, pandas DataFrames, activity names, and directly-follows graphs.
This standalone package extracts the 16 task mappings from Chronos-2 meets Process Mining. It does not import or modify the benchmark repository. It contains no training, fine-tuning, benchmark runner, paper reporting, or FEEED integration.
These functions forecast the next window or event. PM4Py normally describes an already observed log. Dictionary keys and tuple structures are familiar, but distribution values here are probabilities, not observed integer counts. This is a forecast API, not a drop-in replacement for every PM4Py function or option.
Install
Use Python 3.11 or newer in a separate environment. From this repository:
python -m venv .venv
source .venv/bin/activate
python -m pip install .
python main.py
main.py runs a small illustrative event log through the real model. The first
forecast downloads the checkpoint from Hugging Face unless it is already cached.
Importing chronos4pm does not load a model. The default device is CPU.
The distribution name and import name are both chronos4pm. This repository does
not publish anything to PyPI. Until a release is uploaded, install from a checkout
or a supplied wheel:
python -m pip install /path/to/Chronos4PM
# Or, after building:
python -m pip install dist/chronos4pm-0.1.0-py3-none-any.whl
The package metadata pins pm4py==2.7.23.4, ebi-pm==0.3.12, and
chronos-forecasting==2.1.0. EBI's Python import is ebi. Other transitive
dependencies are resolved by pip; the package is not a complete lock of the
paper's experimental environment.
Load a log and forecast
import pm4py
import chronos4pm as cpm
log = pm4py.read_xes("completed_cases.xes") # DataFrame or legacy EventLog
dfg, start_activities, end_activities = cpm.discover_dfg(log, n_windows=8)
print(dfg) # {("Register", "Review"): probability, ...}
print(start_activities) # {"Register": probability, ...}
print(cpm.discover_temporal_profile(log, n_windows=8))
# {("Register", "Review"): (mean_seconds, stdev_seconds), ...}
For tabular input, use columns case:concept:name, concept:name, and
time:timestamp. For example:
import pandas as pd
import chronos4pm as cpm
log = pd.read_csv("completed_cases.csv")
log["time:timestamp"] = pd.to_datetime(log["time:timestamp"], utc=True)
probabilities = cpm.get_start_activities(log, n_windows=8)
# Custom column names work too:
dfg, starts, ends = cpm.discover_dfg(
custom_log,
activity_key="activity",
timestamp_key="timestamp",
case_id_key="case_id",
n_windows=8,
)
custom_log above is your DataFrame with the three named columns. Inputs are
copied; the package does not mutate them. Events are sorted by timestamp and
cases by first timestamp, then case identifier. Supplied timezones are preserved
because calendar covariates use the observed local hour. Keep timestamps and the
forecast origin consistently timezone-aware (or consistently naive); normalize
to UTC explicitly, as above, when working with mixed input offsets. Use the same
time basis as the benchmark when comparing calendar covariates.
Windows and forecast boundaries
Aggregate methods divide completed cases into eight equal-size chronological
windows by default and predict one further window of that same case count.
n_windows changes the number of history points. Case count must be at least
n_windows and divisible by it; the package rejects an uneven split rather than
discarding cases. A window is measured in cases, not hours or days.
Supply only completed historical cases. When forecast_origin is supplied,
every context case must end strictly before it. Automatic selected/global
covariates require this known timestamp to construct the benchmark's future
calendar covariates:
probabilities = cpm.get_start_activities(
log,
n_windows=8,
covariates="selected",
forecast_origin=pd.Timestamp("2026-01-01T00:00:00Z"),
)
Choose an origin after all events in your completed-case context. Without
forecast_origin, target-only calls cannot check that external boundary; the
caller is responsible for supplying genuinely historical data. The package does
not construct the benchmark's held-out blocks or rolling evaluation origins.
API
Every function takes covariates="target_only", "selected", or "global".
Aggregate functions accept the shared keyword parameters listed below.
| Function | Forecast result |
|---|---|
discover_dfg(log) |
(edge_probabilities, start_probabilities, end_probabilities) |
discover_dfg_edges(log) |
set[tuple[str, str]], from binary-presence scores ≥ 0.5 |
get_start_activities(log) |
dict[str, float] of start probabilities |
get_end_activities(log) |
dict[str, float] of end probabilities |
get_event_attribute_values(log, attribute="concept:name") |
dict[str, float] of event-mass probabilities |
get_variants_as_tuples(log) |
dict[tuple[str, ...], float] of variant probabilities |
discover_footprints(log) |
Dictionary of activities, sequence, and parallel sets |
discover_performance_dfg(log) |
(edge_mean_seconds, start_probabilities, end_probabilities) |
get_performance_bottlenecks(log) |
List of (edge, seconds) pairs, longest duration first |
discover_temporal_profile(log) |
dict[tuple[str, str], tuple[float, float]] of mean/stdev seconds |
analyse_entropy(log) |
EBI trace-distribution entropy forecast, in bits |
analyse_variety(log) |
EBI trace-variety forecast |
analyse_completeness(log) |
EBI event-log completeness forecast |
predict_next_activity(log, prefix) |
Next activity label (str) |
predict_next_event_time(log, prefix) |
Duration until the next event, in seconds (float) |
predict_remaining_time(log, prefix) |
Remaining case duration, in seconds (float) |
The three analyse_* names follow EBI; the prefix prediction and bottleneck/edge
helpers have no equivalent PM4Py prediction API. discover_footprints exposes
only the three fields modeled by the benchmark. discover_performance_dfg uses
PM4Py's mean edge duration target, not its default collection of aggregations.
DFG tuple methods also run start/end distribution forecasts; these are three
separate task requests sharing the cached model.
| Shared keyword | Default | Meaning |
|---|---|---|
n_windows |
8 |
Equal-size aggregate history windows; aggregate methods only |
forecast_origin |
omitted | Known next-window origin; aggregate methods only |
activity_key |
"concept:name" |
Activity column/event key |
timestamp_key |
"time:timestamp" |
Timestamp column/event key |
case_id_key |
"case:concept:name" |
Case column for DataFrames |
attribute_allowlist |
omitted | Event-carried attributes eligible for automatic global covariates |
past_covariates |
{} |
Additional named, aligned numeric or categorical histories |
future_covariates |
{} |
Additional known length-one values; names must occur in past covariates |
For get_event_attribute_values, use attribute to choose the activity/target
column; it takes precedence over activity_key.
Predict from a case prefix
log contains completed prior cases. prefix contains only events observed
so far in one current case. It may be a PM4Py Trace, a one-case EventLog,
or a one-case DataFrame. Every context case must end before the first prefix
event; overlapping context is rejected, not silently removed.
import pandas as pd
import chronos4pm as cpm
history = pd.read_csv("completed_cases.csv")
prefix = pd.DataFrame(
[
{
"case:concept:name": "open-case",
"concept:name": "Register",
"time:timestamp": pd.Timestamp("2026-01-01T09:00:00Z"),
},
{
"case:concept:name": "open-case",
"concept:name": "Review",
"time:timestamp": pd.Timestamp("2026-01-01T10:00:00Z"),
},
]
)
activity = cpm.predict_next_activity(history, prefix, covariates="selected")
next_seconds = cpm.predict_next_event_time(history, prefix, covariates="selected")
remaining_seconds = cpm.predict_remaining_time(history, prefix, covariates="global")
The history must be earlier than this example's case start. Next activity uses one-hot observed activity histories on context-supported labels. It needs one observed event and does not predict an end-of-case class. Next-event time needs two events to observe at least one interval. Remaining time uses PM4Py's remaining durations from completed prior cases at the same prefix position, including terminal zero durations when present in history. It rejects positions with no context support. Pass a prefix whose case is still running; the package cannot infer whether the last supplied event is actually terminal.
Covariates as parameters
target_only adds no automatic covariates. selected and global use the
benchmark's task-dependent selections:
- Aggregate selected sets use progress/calendar and task-specific event volume or activity, variant and DFG support. Performance tasks select timing instead of mean prefix length. Global sets add all applicable aggregate families.
- Prefix sets use observed position, activity (where applicable), elapsed time, previous intervals, and cyclic calendar features. Global remaining-time inputs include collision-safe JSON encodings of complete observed activity prefixes.
- Global attributes are discovered only from context events. Case attributes
are forward-filled after their first observation; event attributes are aligned
directly. Trace metadata is not promoted into event covariates. Use
attribute_allowlist=["customer_type", "org:resource"]to restrict fields to those known at prediction time. Availability cannot be inferred from a name.
You can add your own series to any set. For an aggregate call with eight windows:
probabilities = cpm.get_start_activities(
log,
n_windows=8,
past_covariates={"staff_on_shift": [4, 4, 5, 5, 6, 5, 4, 5]},
future_covariates={"staff_on_shift": [6]},
)
Numerical lists/arrays and categorical string lists/arrays are supported. Each past covariate is one-dimensional and has exactly the target history length; each future covariate has length one and the same name as a past series. Names must not collide with automatic covariates. Arrays are shared across target channels, not extra jointly forecast targets.
| Task | Alignment of caller-provided past covariates |
|---|---|
| Aggregate | One value per chronological context window |
| Next activity | One value per observed prefix event; future covariates are rejected |
| Next-event time | One value per observed interval, aligned to its starting event; future values describe the current observed event |
| Remaining time | One value per chronologically ordered context case reaching the prefix position; future values describe the current observed prefix |
Only supply values available at the forecast origin. Shape validation cannot detect a future-derived attribute. Full histories are passed to Chronos so its categorical encoding occurs before its internal context truncation; this avoids changing the long-history remaining-time mapping. Preparing very large histories can therefore use substantial host memory.
Override the model constant
Set chronos4pm.MODEL once before calling the functions. It is deliberately not
a parameter repeated on every task method.
from dataclasses import replace
import chronos4pm as cpm
cpm.MODEL = replace(cpm.MODEL, device_map="mps") # Apple Silicon; "cuda" for CUDA
# Or choose a different checkpoint:
cpm.MODEL = cpm.ModelConfig(
repository_id="amazon/chronos-2",
revision="29ec3766d36d6f73f0696f85560a422f50e8498c",
device_map="cpu",
)
| Benchmark checkpoint | Revision |
|---|---|
autogluon/chronos-2-small (default) |
ddec01313e50b6bc58ebaa92ede81bc24a3d9f9a |
amazon/chronos-2 |
29ec3766d36d6f73f0696f85560a422f50e8498c |
autogluon/chronos-2-synth |
3607918a9fd027d5c465d8213e46b98e2c041cea |
The last configuration's pipeline is cached. Changing the constant causes the
next call to load that configuration. Do not change the global constant while
other threads are forecasting. ModelConfig.batch_size controls the internal
series batch (default 256); a multivariate request may require a larger minimum.
The package performs one request at a time and propagates model/download/memory
errors. It does not substitute another model or shrink the process-element
support. Chronos-1/Bolt checkpoints are not supported by this Chronos-2 API.
Method boundaries
The package retains native PM4Py/EBI target construction, one-step Chronos-2 median forecasts, Euclidean probability-simplex projection, binary edge thresholding, and none/sequence/parallel argmax decoding. Missing performance observations remain NaN in history. Distributions cannot introduce unseen activities, edges, or variants. Unknown activities in an observed prefix have zero entries in the context-supported one-hot channels.
Duration and EBI scalar forecasts are returned raw, as in the benchmark. They can be negative or outside a statistic's natural bounds; temporal standard deviations are not silently clipped. Use these methods for experimentation and evaluate them on your own held-out data. A successful forecast does not establish accuracy. Remaining-time prediction was implemented in the benchmark but excluded from the current paper's 15-task evaluation.
The package does not reproduce the paper's experiment orchestration, metrics, quantile reports, data selection, or runtime measurements. Use the original repository for those. No benchmark artifacts or results are bundled here.
Development and building a release
python -m pip install -e '.[dev]'
python -m pytest -q
python -m ruff check .
python -m ruff format --check .
python -m mypy chronos4pm tests examples main.py
RUN_CHRONOS_INTEGRATION=1 python -m pytest -q
python -m build
python -m twine check dist/*
Default tests isolate decoding with explicit test doubles and call real PM4Py
and EBI. The opt-in integration tests execute all 16 tasks in all three covariate
arms with the real default checkpoint. Set HF_HUB_OFFLINE=1 to use only already
cached Hugging Face files. Tests use illustrative logs, not the paper's datasets.
Wheel and source archives are created in dist/; building does not publish them.
The implementation is divided by responsibility: public functions in api.py,
native histories/decoders in tasks.py, prefix mappings in predictive.py,
covariate construction in covariates.py, input validation in logs.py, and
lazy model inference in inference.py. main.py only starts the example.
Metadata
Release files for chronos4pm 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chronos4pm-0.1.0.tar.gz | 34.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chronos4pm-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 60.7 kB
Release files / chronos4pm-0.1.0.tar.gz
| Download URL | chronos4pm-0.1.0.tar.gz |
|---|---|
| Size | 34.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0b3530d862ffbc460809efc013e960c8dbb0c6bd6439241930031dd9b5bbd0e7
|
|
BLAKE2b-256 checksum How to use checksums |
3461eaa826952bd1bce5606382ac2a8cdd2c4d389f60babee71d2c6b23f7f0dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / chronos4pm-0.1.0-py3-none-any.whl
| Download URL | chronos4pm-0.1.0-py3-none-any.whl |
|---|---|
| Size | 26.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4020f68fa2851c9be51c9c29883644e8dfa1dd386c1b934ae86dafc08534c880
|
|
BLAKE2b-256 checksum How to use checksums |
2445fbb63390bf3b46a33a99b010a968972428f34b7582d3bfa4948de7fd5a0b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|