Skip to main content
Rust4PM

Rust crate

Add the process_mining crate to a new project, load a real event log, discover a process model and check how well it fits the data.

Create a project

shell
cargo new my-analysis
cd my-analysis
cargo add process_mining@=0.6.2

This needs a Rust toolchain. The default features cover XES, OCEL 2.0 XML and JSON, discovery and conformance. SQLite, DuckDB and a few other formats are optional feature flags. The crate is still in active development, so pin the version you build against.

Get the sample data

The quickstart uses the Road Traffic Fine Management log: 150,370 fines, 561,470 events, 11 activities. Put the data folder in the project root, next to Cargo.toml.

The program

Replace src/main.rs with:

rust
use process_mining::conformance::case_centric::alignments::{
    align_variants, compute_fitness, AlignmentOptions,
};
use process_mining::core::event_data::case_centric::utils::activity_projection::EventLogActivityProjection;
use process_mining::discovery::case_centric::alphappp::full::{
    alphappp_discover_petri_net, AlphaPPPConfig,
};
use process_mining::discovery::case_centric::dfg::discover_dfg;
use process_mining::{EventLog, Exportable, Importable};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // The file extension (.xes.gz) selects the format.
    let log = EventLog::import_from_path("data/road-traffic-fines.xes.gz")?;
    println!("{} traces", log.traces.len());

    // Directly-follows graph: which activity follows which, and how often.
    let dfg = discover_dfg(&log);
    println!("{} activities", dfg.activities.len());

    // Alpha+++ works on the activity projection: every trace variant once, with its count.
    let projection = EventLogActivityProjection::from(&log);
    println!("{} variants", projection.traces.len());

    let config = AlphaPPPConfig {
        balance_thresh: 0.1,
        fitness_thresh: 0.8,
        replay_thresh: 0.0,
        log_repair_skip_df_thresh_rel: 4.0,
        log_repair_loop_df_thresh_rel: 4.0,
        absolute_df_clean_thresh: 1,
        relative_df_clean_thresh: 0.01,
    };
    let net = alphappp_discover_petri_net(&projection, config);
    println!(
        "{} places, {} transitions",
        net.places.len(),
        net.transitions.len()
    );

    // How well does the model explain the log? Align every variant, then aggregate.
    let options = AlignmentOptions::default();
    let alignments = align_variants(&net, &projection, &options);
    let fitness = compute_fitness(&alignments, &net, &options)?;
    println!("log fitness {:.3}", fitness.log_fitness);

    net.export_to_path("model.pnml")?;
    Ok(())
}

Run it in release mode, since discovery and alignments are much faster with optimizations:

cargo run --release
output
150370 traces
11 activities
231 variants
10 places, 16 transitions
log fitness 0.886

Alpha+++ also prints about 20 lines of progress information while it runs, which the output above leaves out.

The program step by step

Import and export

EventLog, OCEL, PetriNet and the other data types implement the Importable and Exportable traits. import_from_path and export_to_path pick the format from the file extension (.xes, .xes.gz, .json, .pnml, ...). For files without a telling extension, import_from_reader and export_to_writer take the format explicitly. The file formats page lists every supported format per type.

Discover a directly-follows graph

discover_dfg counts how often each activity directly follows another. The result has activities, edges with frequencies, and start and end activities.

Discover a Petri net with Alpha+++

Alpha+++ works on an EventLogActivityProjection, a compact form of the log that keeps every trace variant once with its count. The road traffic log has 231 variants, so everything after this step is fast even though the log has 150,370 traces.

The AlphaPPPConfig thresholds filter rare behavior before and during discovery. The discovery how-to explains each of them.

Check conformance

align_variants computes an optimal alignment between the model and each variant (in parallel). compute_fitness combines them, weighted by how often each variant occurs, and returns log_fitness, average_fitness, perfectly_fitting_frac and total_costs. A log fitness of 0.886 means the model explains most of the observed behavior but not all of it.

Next steps