chronos4pm 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christian Imenkamp
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include main.py
2
+ recursive-include examples *.py
@@ -0,0 +1,348 @@
1
+ Metadata-Version: 2.4
2
+ Name: chronos4pm
3
+ Version: 0.1.0
4
+ Summary: Zero-shot process-mining forecasts with Chronos-2 and PM4Py data structures
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: pm4py==2.7.23.4
10
+ Requires-Dist: ebi-pm==0.3.12
11
+ Requires-Dist: chronos-forecasting==2.1.0
12
+ Requires-Dist: numpy<3,>=2
13
+ Requires-Dist: pandas<4,>=2.2
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest>=8; extra == "dev"
16
+ Requires-Dist: mypy>=1.15; extra == "dev"
17
+ Requires-Dist: pandas-stubs; extra == "dev"
18
+ Requires-Dist: ruff; extra == "dev"
19
+ Requires-Dist: build; extra == "dev"
20
+ Requires-Dist: twine; extra == "dev"
21
+ Dynamic: license-file
22
+
23
+ # Chronos4PM
24
+
25
+ Forecast process behavior with a frozen Chronos-2 model, using familiar
26
+ [PM4Py](https://processintelligence.solutions/pm4py) event logs, pandas DataFrames,
27
+ activity names, and directly-follows graphs.
28
+
29
+ This standalone package extracts the 16 task mappings from
30
+ [Chronos-2 meets Process Mining](https://github.com/chimenkamp/chronos-2-meets-process-mining).
31
+ It does not import or modify the benchmark repository. It contains no training,
32
+ fine-tuning, benchmark runner, paper reporting, or FEEED integration.
33
+
34
+ **These functions forecast the next window or event.** PM4Py normally describes
35
+ an already observed log. Dictionary keys and tuple structures are familiar, but
36
+ distribution values here are probabilities, not observed integer counts. This
37
+ is a forecast API, not a drop-in replacement for every PM4Py function or option.
38
+
39
+ ## Install
40
+
41
+ Use Python 3.11 or newer in a separate environment. From this repository:
42
+
43
+ ```bash
44
+ python -m venv .venv
45
+ source .venv/bin/activate
46
+ python -m pip install .
47
+ python main.py
48
+ ```
49
+
50
+ `main.py` runs a small illustrative event log through the real model. The first
51
+ forecast downloads the checkpoint from Hugging Face unless it is already cached.
52
+ Importing `chronos4pm` does not load a model. The default device is CPU.
53
+
54
+ The distribution name and import name are both `chronos4pm`. This repository does
55
+ not publish anything to PyPI. Until a release is uploaded, install from a checkout
56
+ or a supplied wheel:
57
+
58
+ ```bash
59
+ python -m pip install /path/to/Chronos4PM
60
+ # Or, after building:
61
+ python -m pip install dist/chronos4pm-0.1.0-py3-none-any.whl
62
+ ```
63
+
64
+ The package metadata pins `pm4py==2.7.23.4`, `ebi-pm==0.3.12`, and
65
+ `chronos-forecasting==2.1.0`. EBI's Python import is `ebi`. Other transitive
66
+ dependencies are resolved by pip; the package is not a complete lock of the
67
+ paper's experimental environment.
68
+
69
+ ## Load a log and forecast
70
+
71
+ ```python
72
+ import pm4py
73
+ import chronos4pm as cpm
74
+
75
+ log = pm4py.read_xes("completed_cases.xes") # DataFrame or legacy EventLog
76
+ dfg, start_activities, end_activities = cpm.discover_dfg(log, n_windows=8)
77
+
78
+ print(dfg) # {("Register", "Review"): probability, ...}
79
+ print(start_activities) # {"Register": probability, ...}
80
+ print(cpm.discover_temporal_profile(log, n_windows=8))
81
+ # {("Register", "Review"): (mean_seconds, stdev_seconds), ...}
82
+ ```
83
+
84
+ For tabular input, use columns `case:concept:name`, `concept:name`, and
85
+ `time:timestamp`. For example:
86
+
87
+ ```python
88
+ import pandas as pd
89
+ import chronos4pm as cpm
90
+
91
+ log = pd.read_csv("completed_cases.csv")
92
+ log["time:timestamp"] = pd.to_datetime(log["time:timestamp"], utc=True)
93
+ probabilities = cpm.get_start_activities(log, n_windows=8)
94
+
95
+ # Custom column names work too:
96
+ dfg, starts, ends = cpm.discover_dfg(
97
+ custom_log,
98
+ activity_key="activity",
99
+ timestamp_key="timestamp",
100
+ case_id_key="case_id",
101
+ n_windows=8,
102
+ )
103
+ ```
104
+
105
+ `custom_log` above is your DataFrame with the three named columns. Inputs are
106
+ copied; the package does not mutate them. Events are sorted by timestamp and
107
+ cases by first timestamp, then case identifier. Supplied timezones are preserved
108
+ because calendar covariates use the observed local hour. Keep timestamps and the
109
+ forecast origin consistently timezone-aware (or consistently naive); normalize
110
+ to UTC explicitly, as above, when working with mixed input offsets. Use the same
111
+ time basis as the benchmark when comparing calendar covariates.
112
+
113
+ ## Windows and forecast boundaries
114
+
115
+ Aggregate methods divide completed cases into **eight equal-size chronological
116
+ windows** by default and predict one further window of that same case count.
117
+ `n_windows` changes the number of history points. Case count must be at least
118
+ `n_windows` and divisible by it; the package rejects an uneven split rather than
119
+ discarding cases. A window is measured in cases, not hours or days.
120
+
121
+ Supply only completed historical cases. When `forecast_origin` is supplied,
122
+ every context case must end strictly before it. Automatic selected/global
123
+ covariates require this known timestamp to construct the benchmark's future
124
+ calendar covariates:
125
+
126
+ ```python
127
+ probabilities = cpm.get_start_activities(
128
+ log,
129
+ n_windows=8,
130
+ covariates="selected",
131
+ forecast_origin=pd.Timestamp("2026-01-01T00:00:00Z"),
132
+ )
133
+ ```
134
+
135
+ Choose an origin after **all** events in your completed-case context. Without
136
+ `forecast_origin`, target-only calls cannot check that external boundary; the
137
+ caller is responsible for supplying genuinely historical data. The package does
138
+ not construct the benchmark's held-out blocks or rolling evaluation origins.
139
+
140
+ ## API
141
+
142
+ Every function takes `covariates="target_only"`, `"selected"`, or `"global"`.
143
+ Aggregate functions accept the shared keyword parameters listed below.
144
+
145
+ | Function | Forecast result |
146
+ | --- | --- |
147
+ | `discover_dfg(log)` | `(edge_probabilities, start_probabilities, end_probabilities)` |
148
+ | `discover_dfg_edges(log)` | `set[tuple[str, str]]`, from binary-presence scores ≥ 0.5 |
149
+ | `get_start_activities(log)` | `dict[str, float]` of start probabilities |
150
+ | `get_end_activities(log)` | `dict[str, float]` of end probabilities |
151
+ | `get_event_attribute_values(log, attribute="concept:name")` | `dict[str, float]` of event-mass probabilities |
152
+ | `get_variants_as_tuples(log)` | `dict[tuple[str, ...], float]` of variant probabilities |
153
+ | `discover_footprints(log)` | Dictionary of `activities`, `sequence`, and `parallel` sets |
154
+ | `discover_performance_dfg(log)` | `(edge_mean_seconds, start_probabilities, end_probabilities)` |
155
+ | `get_performance_bottlenecks(log)` | List of `(edge, seconds)` pairs, longest duration first |
156
+ | `discover_temporal_profile(log)` | `dict[tuple[str, str], tuple[float, float]]` of mean/stdev seconds |
157
+ | `analyse_entropy(log)` | EBI trace-distribution entropy forecast, in bits |
158
+ | `analyse_variety(log)` | EBI trace-variety forecast |
159
+ | `analyse_completeness(log)` | EBI event-log completeness forecast |
160
+ | `predict_next_activity(log, prefix)` | Next activity label (`str`) |
161
+ | `predict_next_event_time(log, prefix)` | Duration until the next event, in seconds (`float`) |
162
+ | `predict_remaining_time(log, prefix)` | Remaining case duration, in seconds (`float`) |
163
+
164
+ The three `analyse_*` names follow EBI; the prefix prediction and bottleneck/edge
165
+ helpers have no equivalent PM4Py prediction API. `discover_footprints` exposes
166
+ only the three fields modeled by the benchmark. `discover_performance_dfg` uses
167
+ PM4Py's **mean** edge duration target, not its default collection of aggregations.
168
+ DFG tuple methods also run start/end distribution forecasts; these are three
169
+ separate task requests sharing the cached model.
170
+
171
+ | Shared keyword | Default | Meaning |
172
+ | --- | --- | --- |
173
+ | `n_windows` | `8` | Equal-size aggregate history windows; aggregate methods only |
174
+ | `forecast_origin` | omitted | Known next-window origin; aggregate methods only |
175
+ | `activity_key` | `"concept:name"` | Activity column/event key |
176
+ | `timestamp_key` | `"time:timestamp"` | Timestamp column/event key |
177
+ | `case_id_key` | `"case:concept:name"` | Case column for DataFrames |
178
+ | `attribute_allowlist` | omitted | Event-carried attributes eligible for automatic global covariates |
179
+ | `past_covariates` | `{}` | Additional named, aligned numeric or categorical histories |
180
+ | `future_covariates` | `{}` | Additional known length-one values; names must occur in past covariates |
181
+
182
+ For `get_event_attribute_values`, use `attribute` to choose the activity/target
183
+ column; it takes precedence over `activity_key`.
184
+
185
+ ## Predict from a case prefix
186
+
187
+ `log` contains completed prior cases. `prefix` contains **only events observed
188
+ so far** in one current case. It may be a PM4Py `Trace`, a one-case `EventLog`,
189
+ or a one-case DataFrame. Every context case must end before the first prefix
190
+ event; overlapping context is rejected, not silently removed.
191
+
192
+ ```python
193
+ import pandas as pd
194
+ import chronos4pm as cpm
195
+
196
+ history = pd.read_csv("completed_cases.csv")
197
+ prefix = pd.DataFrame(
198
+ [
199
+ {
200
+ "case:concept:name": "open-case",
201
+ "concept:name": "Register",
202
+ "time:timestamp": pd.Timestamp("2026-01-01T09:00:00Z"),
203
+ },
204
+ {
205
+ "case:concept:name": "open-case",
206
+ "concept:name": "Review",
207
+ "time:timestamp": pd.Timestamp("2026-01-01T10:00:00Z"),
208
+ },
209
+ ]
210
+ )
211
+
212
+ activity = cpm.predict_next_activity(history, prefix, covariates="selected")
213
+ next_seconds = cpm.predict_next_event_time(history, prefix, covariates="selected")
214
+ remaining_seconds = cpm.predict_remaining_time(history, prefix, covariates="global")
215
+ ```
216
+
217
+ The history must be earlier than this example's case start. Next activity uses
218
+ one-hot observed activity histories on context-supported labels. It needs one
219
+ observed event and does not predict an end-of-case class. Next-event time needs
220
+ two events to observe at least one interval. Remaining time uses PM4Py's remaining
221
+ durations from completed prior cases **at the same prefix position**, including
222
+ terminal zero durations when present in history. It rejects positions with no
223
+ context support. Pass a prefix whose case is still running; the package cannot
224
+ infer whether the last supplied event is actually terminal.
225
+
226
+ ## Covariates as parameters
227
+
228
+ `target_only` adds no automatic covariates. `selected` and `global` use the
229
+ benchmark's task-dependent selections:
230
+
231
+ - Aggregate selected sets use progress/calendar and task-specific event volume
232
+ or activity, variant and DFG support. Performance tasks select timing instead
233
+ of mean prefix length. Global sets add all applicable aggregate families.
234
+ - Prefix sets use observed position, activity (where applicable), elapsed time,
235
+ previous intervals, and cyclic calendar features. Global remaining-time inputs
236
+ include collision-safe JSON encodings of complete observed activity prefixes.
237
+ - Global attributes are discovered only from context events. Case attributes
238
+ are forward-filled after their first observation; event attributes are aligned
239
+ directly. Trace metadata is not promoted into event covariates. Use
240
+ `attribute_allowlist=["customer_type", "org:resource"]` to restrict fields to
241
+ those known at prediction time. Availability cannot be inferred from a name.
242
+
243
+ You can add your own series to any set. For an aggregate call with eight windows:
244
+
245
+ ```python
246
+ probabilities = cpm.get_start_activities(
247
+ log,
248
+ n_windows=8,
249
+ past_covariates={"staff_on_shift": [4, 4, 5, 5, 6, 5, 4, 5]},
250
+ future_covariates={"staff_on_shift": [6]},
251
+ )
252
+ ```
253
+
254
+ Numerical lists/arrays and categorical string lists/arrays are supported. Each
255
+ past covariate is one-dimensional and has exactly the target history length;
256
+ each future covariate has length one and the same name as a past series. Names
257
+ must not collide with automatic covariates. Arrays are shared across target
258
+ channels, not extra jointly forecast targets.
259
+
260
+ | Task | Alignment of caller-provided past covariates |
261
+ | --- | --- |
262
+ | Aggregate | One value per chronological context window |
263
+ | Next activity | One value per observed prefix event; future covariates are rejected |
264
+ | Next-event time | One value per observed interval, aligned to its **starting** event; future values describe the current observed event |
265
+ | Remaining time | One value per chronologically ordered context case reaching the prefix position; future values describe the current observed prefix |
266
+
267
+ Only supply values available at the forecast origin. Shape validation cannot
268
+ detect a future-derived attribute. Full histories are passed to Chronos so its
269
+ categorical encoding occurs before its internal context truncation; this avoids
270
+ changing the long-history remaining-time mapping. Preparing very large histories
271
+ can therefore use substantial host memory.
272
+
273
+ ## Override the model constant
274
+
275
+ Set `chronos4pm.MODEL` once before calling the functions. It is deliberately not
276
+ a parameter repeated on every task method.
277
+
278
+ ```python
279
+ from dataclasses import replace
280
+ import chronos4pm as cpm
281
+
282
+ cpm.MODEL = replace(cpm.MODEL, device_map="mps") # Apple Silicon; "cuda" for CUDA
283
+
284
+ # Or choose a different checkpoint:
285
+ cpm.MODEL = cpm.ModelConfig(
286
+ repository_id="amazon/chronos-2",
287
+ revision="29ec3766d36d6f73f0696f85560a422f50e8498c",
288
+ device_map="cpu",
289
+ )
290
+ ```
291
+
292
+ | Benchmark checkpoint | Revision |
293
+ | --- | --- |
294
+ | `autogluon/chronos-2-small` (default) | `ddec01313e50b6bc58ebaa92ede81bc24a3d9f9a` |
295
+ | `amazon/chronos-2` | `29ec3766d36d6f73f0696f85560a422f50e8498c` |
296
+ | `autogluon/chronos-2-synth` | `3607918a9fd027d5c465d8213e46b98e2c041cea` |
297
+
298
+ The last configuration's pipeline is cached. Changing the constant causes the
299
+ next call to load that configuration. Do not change the global constant while
300
+ other threads are forecasting. `ModelConfig.batch_size` controls the internal
301
+ series batch (default 256); a multivariate request may require a larger minimum.
302
+ The package performs one request at a time and propagates model/download/memory
303
+ errors. It does not substitute another model or shrink the process-element
304
+ support. Chronos-1/Bolt checkpoints are not supported by this Chronos-2 API.
305
+
306
+ ## Method boundaries
307
+
308
+ The package retains native PM4Py/EBI target construction, one-step Chronos-2
309
+ median forecasts, Euclidean probability-simplex projection, binary edge
310
+ thresholding, and none/sequence/parallel argmax decoding. Missing performance
311
+ observations remain NaN in history. Distributions cannot introduce unseen
312
+ activities, edges, or variants. Unknown activities in an observed prefix have
313
+ zero entries in the context-supported one-hot channels.
314
+
315
+ Duration and EBI scalar forecasts are returned raw, as in the benchmark. They
316
+ can be negative or outside a statistic's natural bounds; temporal standard
317
+ deviations are not silently clipped. Use these methods for experimentation and
318
+ evaluate them on your own held-out data. A successful forecast does not establish
319
+ accuracy. Remaining-time prediction was implemented in the benchmark but excluded
320
+ from the current paper's 15-task evaluation.
321
+
322
+ The package does not reproduce the paper's experiment orchestration, metrics,
323
+ quantile reports, data selection, or runtime measurements. Use the original
324
+ repository for those. No benchmark artifacts or results are bundled here.
325
+
326
+ ## Development and building a release
327
+
328
+ ```bash
329
+ python -m pip install -e '.[dev]'
330
+ python -m pytest -q
331
+ python -m ruff check .
332
+ python -m ruff format --check .
333
+ python -m mypy chronos4pm tests examples main.py
334
+ RUN_CHRONOS_INTEGRATION=1 python -m pytest -q
335
+ python -m build
336
+ python -m twine check dist/*
337
+ ```
338
+
339
+ Default tests isolate decoding with explicit test doubles and call real PM4Py
340
+ and EBI. The opt-in integration tests execute all 16 tasks in all three covariate
341
+ arms with the real default checkpoint. Set `HF_HUB_OFFLINE=1` to use only already
342
+ cached Hugging Face files. Tests use illustrative logs, not the paper's datasets.
343
+ Wheel and source archives are created in `dist/`; building does not publish them.
344
+
345
+ The implementation is divided by responsibility: public functions in `api.py`,
346
+ native histories/decoders in `tasks.py`, prefix mappings in `predictive.py`,
347
+ covariate construction in `covariates.py`, input validation in `logs.py`, and
348
+ lazy model inference in `inference.py`. `main.py` only starts the example.