Rheon documentation

Rheon

A library for synthetic event log generation with injected concept drifts.

Concepts

Quick start

Generate a labeled log with the simple API.

Describe a base process and a list of drifts, then write a labeled log.

import rheon

drifts = [
    {"type": "control_flow", "mode": "sudden", "drift_point": 0.5, "num_activities": 9},
    {"type": "reassignment", "mode": "gradual", "start_point": 0.3, "end_point": 0.45},
    {"type": "amount", "mode": "sudden", "drift_point": 0.6, "mean": 4000},
]

rheon.generate_log(drifts, "out/example.xes", num_traces=2000, num_activities=8)

This writes two files: the log out/example.xes and a ground-truth metadata sidecar out/example_meta.md.

Read the generated log back with pm4py:

import pm4py

log = pm4py.read_xes("out/example.xes")

Installation

Get the runtime and dependencies in place.

Prerequisites

  • Python 3.10 or newer
  • uv package manager

Set up

Clone the repository and let uv resolve dependencies on first run:

git clone git@github.com:boraderen/rheon.git
cd rheon
uv sync

Example

Run the bundled script to generate a labeled log:

uv run python example/generate_log.py

It injects drifts across every perspective and writes example/example.csv plus its example_meta.md ground-truth sidecar.

Package structure

How the public API and implementation are split.

The package exposes a single function, rheon.generate_log, from rheon/__init__.py. The implementation is just seven small modules.

rheon/
  __init__.py      public exports
  config.py        column keys, labels, GeneratorConfig
  drifts.py        drift spec, validation, timeline helpers
  process_tree.py  build trees and play out activity sequences
  generator.py     the generation engine (the six steps)
  metadata.py      build and render the ground-truth metadata
  write.py         write XES / CSV and the metadata sidecar
example/
  generate_log.py  one mixed-drift example
tests/
  test_*.py        behavior coverage
Path Role
rheon/config.pyColumn names, label helpers, and the parameter bundle.
rheon/drifts.pyNormalizes and validates the drift list.
rheon/process_tree.pyBuilds process trees and plays out traces.
rheon/generator.pyThe generation engine and the public generate_log.
rheon/metadata.pyAssembles and renders the ground-truth metadata.
rheon/write.pyWrites the log (XES or CSV) and the metadata sidecar.

API

Function signature and generator options for rheon.generate_log().

Signature

rheon.generate_log(
    drifts,
    output_path,
    *,
    log_name=None,
    format="xes",          # "xes" or "csv"
    num_traces=1000,
    num_activities=10,
    num_resources=8,
    num_regions=4,
    tree_weights={"sequence": 0.6, "choice": 0.25, "parallel": 0.1, "loop": 0.05},
    start_date=datetime(2020, 1, 1),
    end_date=datetime(2020, 12, 31),
    activity_duration=(30.0, 100.0),
    waiting_time=(15.0, 50.0),
    amount=(1000.0, 40000.0),
    seed=42,
)

drifts is a list of dictionaries, each with a type, a mode, and a position; see Drift types and Drift modes. The parameters below all describe the single base process.

Argument Default Description
driftsList of drift dictionaries to inject.
output_pathDestination of the log.
log_namefile stemName used in metadata and the sidecar filename.
format"xes""xes" or "csv".
num_traces1000Approximate number of cases (not strict).
num_activities10Activities in the base process tree.
num_resources8Size of the resource pool.
num_regions4Number of regions.
tree_weightssee aboveOperator weights for the base tree (sequence, choice, parallel, loop).
start_date2020-01-01Start of the time horizon.
end_date2020-12-31End of the time horizon (with start_date it fixes the window).
activity_duration(30.0, 100.0)(mean, variance) of activity processing time in minutes.
waiting_time(15.0, 50.0)(mean, variance) of the waiting gap between events.
amount(1000.0, 40000.0)(mean, variance) of the case amount.
seed42Random seed.

Cases are spread across [start_date, end_date]. The mean inter-arrival gap is derived as (end_date − start_date) / num_traces. The arrival_rate drift changes that derived rate after its drift point.

Drift modes

How the change is shaped over time.

Every drift declares a mode and a position. All positions are fractions of the time horizon in (0, 1), where 0.5 is the midpoint. The metadata records each position as both a fraction and an absolute timestamp.

Mode Position keys Behavior
sudden drift_point A hard switch: everything after drift_point uses the new behavior.
gradual start_point, end_point A transition window: between the two points the new behavior is mixed in with a probability that rises linearly from 0 to 1.

Drift types

The nine drifts and exactly what each one changes.

A drift's type selects the mechanism. Perspectives (intra-case, resource, inter-case) are a documentation grouping only — generate_log dispatches purely on type.

Perspective Type What changes Type-specific params
intra-casecontrol_flowActivity-ordering structure (a different process tree).num_activities, tree_weights
resourcepool_sizeResources added or removed; durations scale the opposite way.delta, duration_factor
resourcereassignmentA new dominant resource per activity.
resourceworkloadTraces duplicated or dropped; per-resource load shifts.workload_factor
resourcedurationProcessing time of the given resources is scaled.resources, factor
inter-casewaiting_timeThe mean waiting gap between consecutive events of a case shifts (all activities at once).mean, variance
inter-caseamountCase amounts drawn from a shifted distribution.mean, variance
inter-casearrival_rateThe mean gap between case arrivals changes.inter_arrival or factor
inter-caseregionA new dominant region for later cases.

Example valid drift specs

The following drifts list is valid with the default generator labels (res_01 ... res_08 and region_1 ... region_4). Use any subset of these dicts; if you add multiple drifts of the same type, their windows must not overlap.

drifts = [
    {
        "type": "control_flow",
        "mode": "sudden",
        "drift_point": 0.35,
        "num_activities": 9,
        "tree_weights": {"sequence": 0.50, "choice": 0.30, "parallel": 0.15, "loop": 0.05},
    },
    {
        "type": "pool_size",
        "mode": "gradual",
        "start_point": 0.20,
        "end_point": 0.30,
        "delta": 2,
        "duration_factor": 1.25,
    },
    {"type": "reassignment", "mode": "sudden", "drift_point": 0.40},
    {
        "type": "workload",
        "mode": "gradual",
        "start_point": 0.70,
        "end_point": 0.85,
        "workload_factor": 1.40,
    },
    {
        "type": "duration",
        "mode": "sudden",
        "drift_point": 0.55,
        "resources": ["res_01", "res_02"],
        "factor": 1.80,
    },
    {
        "type": "waiting_time",
        "mode": "gradual",
        "start_point": 0.45,
        "end_point": 0.60,
        "mean": 45.0,
        "variance": 80.0,
    },
    {
        "type": "amount",
        "mode": "sudden",
        "drift_point": 0.65,
        "mean": 3000.0,
        "variance": 90000.0,
    },
    {
        "type": "arrival_rate",
        "mode": "sudden",
        "drift_point": 0.50,
        "inter_arrival": 240.0,
    },
    {"type": "region", "mode": "gradual", "start_point": 0.75, "end_point": 0.90},
]

For arrival_rate, use either an absolute inter_arrival value in minutes as shown above or a relative factor, for example {"type": "arrival_rate", "mode": "sudden", "drift_point": 0.50, "factor": 0.5}.

What each drift does

control_flow · intra-case

Cases after the drift are played out from a different process tree, so the activity-ordering (trace variants) structure changes. The new tree is built from the drift's num_activities and tree_weights. In sudden mode every case after drift_point uses the new tree; in gradual mode a case in the window picks the new tree with the rising window probability.

pool_size · resource

delta adds (+) or removes () resources at the drift. When it shrinks, the removed resources' later events are redistributed to the rest; when it grows, each new resource becomes dominant for a free activity. Durations scale the opposite way by duration_factor (fewer resources → slower, more → faster). Gradual mode ramps the reassignment and the duration scaling across the window.

reassignment · resource

A new dominant resource is chosen for every activity and the resource column is re-filled after the drift (keeping 80% dominance). In gradual mode the share of events that use the new mapping rises across the window. The before/after dominance maps are written to the metadata.

workload · resource

After the drift, workload_factor sets how many traces to add or remove (1.0 = no change). Added traces are duplicates of existing ones with freshly drawn timing, amount, resources and region; removals never drop the last instance of a variant. The total trace count may change. Gradual mode spreads the duplicates across the window.

duration · resource

The processing time of the given resources' later events is multiplied by factor ("these resources slow down / speed up"). resources may be "all", a count (the first N resources), or an explicit list. Gradual mode ramps the factor from 1 to its target across the window.

waiting_time · inter-case

The mean of the waiting gap between events shifts to mean after the drift — the main driver of throughput time. A gap is the idle time between consecutive events of the same case, and every activity's waiting distribution is updated to the new (mean, variance), so all activities shift at once. Gradual mode blends each gap from its old value toward the new mean across the window.

amount · inter-case

Case amounts after the drift are drawn from a shifted distribution (mean, variance). Gradual mode interpolates the mean and variance across the window.

arrival_rate · inter-case

The mean inter-arrival gap changes after the drift, set by a new absolute inter_arrival or a factor applied to the base. Cases arrive denser or sparser. Gradual mode moves the mean across the window.

region · inter-case

A new dominant region is chosen and the region of later cases is re-filled (keeping 80% dominance). Gradual mode raises the share of cases using the new region across the window.

Output & metadata

An XES or CSV log and a readable metadata sidecar.

Each run writes two files next to output_path (the log suffix follows format):

<name>.xes   (or <name>.csv)
<log_name>_meta.md

Log columns

Events carry:

event:id, case:concept:name, concept:name, start_timestamp, time:timestamp, event:duration_min, org:resource

Each case carries case:amount and case:region. For XES the full metadata is also embedded as a log-level rheon:metadata attribute.

Metadata sidecar

The <log_name>_meta.md file is the ground truth, in three sections:

  • General parameters — the structural, temporal and attribute parameters of the run.
  • Base distributions — the starting state: every activity's dominant resource and its duration and waiting distributions (mean, var), plus the case-level amount distribution, inter-arrival mean and dominant region.
  • Drifts — each drift with its mode and drift point or window (as both a horizon fraction and an absolute timestamp), followed by exactly which distributions or assignments it changed, per activity / resource.