From c75acc0145ea9d9d9d60729009d2b636e94d6306 Mon Sep 17 00:00:00 2001 From: Simon Wolf <swolf@mbp-von-simon.speedport.ip> Date: Tue, 3 Dec 2024 16:59:34 +0100 Subject: [PATCH] Added loadability functionality to import OECL from Cortado and pass own data structure from backend to cortado. Furthermore adapted evaluation package of pm4py and integrated it into project to get rid of dependency mismatch between cortado and ocpa/ocsv. --- src/backend/.DS_Store | Bin 10244 -> 8196 bytes .../api/routes/input_output/importing.py | 42 ++ src/backend/api/websocket/main.py | 3 + src/backend/mine_log.py | 128 ---- src/evaluation/__init__.py | 26 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1232 bytes .../__pycache__/evaluator.cpython-310.pyc | Bin 0 -> 3842 bytes .../earth_mover_distance/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1028 bytes .../__pycache__/evaluator.cpython-310.pyc | Bin 0 -> 2174 bytes .../earth_mover_distance/evaluator.py | 59 ++ .../earth_mover_distance/variants/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1023 bytes .../__pycache__/pyemd.cpython-310.pyc | Bin 0 -> 5072 bytes .../earth_mover_distance/variants/pyemd.py | 175 +++++ src/evaluation/evaluator.py | 114 +++ src/evaluation/generalization/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 998 bytes .../__pycache__/evaluator.cpython-310.pyc | Bin 0 -> 1914 bytes .../__pycache__/parameters.cpython-310.pyc | Bin 0 -> 1230 bytes src/evaluation/generalization/evaluator.py | 43 ++ src/evaluation/generalization/parameters.py | 22 + .../generalization/variants/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 999 bytes .../__pycache__/token_based.cpython-310.pyc | Bin 0 -> 4125 bytes .../generalization/variants/token_based.py | 115 +++ src/evaluation/precision/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 988 bytes .../__pycache__/evaluator.cpython-310.pyc | Bin 0 -> 2647 bytes .../__pycache__/parameters.cpython-310.pyc | Bin 0 -> 1437 bytes .../__pycache__/utils.cpython-310.pyc | Bin 0 -> 4170 bytes src/evaluation/precision/evaluator.py | 82 +++ src/evaluation/precision/parameters.py | 26 + src/evaluation/precision/utils.py | 148 ++++ src/evaluation/precision/variants/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1027 bytes .../align_etconformance.cpython-310.pyc | Bin 0 -> 7289 bytes .../etconformance_token.cpython-310.pyc | Bin 0 -> 3513 bytes .../precision/variants/align_etconformance.py | 274 ++++++++ .../precision/variants/etconformance_token.py | 113 +++ src/evaluation/replay_fitness/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 998 bytes .../__pycache__/evaluator.cpython-310.pyc | Bin 0 -> 3643 bytes .../__pycache__/parameters.cpython-310.pyc | Bin 0 -> 1455 bytes src/evaluation/replay_fitness/evaluator.py | 122 ++++ src/evaluation/replay_fitness/parameters.py | 26 + .../replay_fitness/variants/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1026 bytes .../alignment_based.cpython-310.pyc | Bin 0 -> 4655 bytes .../__pycache__/token_replay.cpython-310.pyc | Bin 0 -> 3963 bytes .../variants/alignment_based.py | 126 ++++ .../replay_fitness/variants/token_replay.py | 100 +++ src/evaluation/simplicity/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 990 bytes .../__pycache__/evaluator.cpython-310.pyc | Bin 0 -> 1728 bytes src/evaluation/simplicity/evaluator.py | 39 ++ .../simplicity/variants/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 990 bytes .../__pycache__/arc_degree.cpython-310.pyc | Bin 0 -> 2389 bytes .../simplicity/variants/arc_degree.py | 70 ++ src/evaluation/soundness/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 966 bytes src/evaluation/soundness/woflan/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1060 bytes .../__pycache__/algorithm.cpython-310.pyc | Bin 0 -> 21598 bytes src/evaluation/soundness/woflan/algorithm.py | 654 ++++++++++++++++++ .../soundness/woflan/graphs/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1101 bytes .../__pycache__/utility.cpython-310.pyc | Bin 0 -> 5420 bytes .../minimal_coverability_graph/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1068 bytes ...minimal_coverability_graph.cpython-310.pyc | Bin 0 -> 5951 bytes .../minimal_coverability_graph.py | 186 +++++ .../graphs/reachability_graph/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1044 bytes .../reachability_graph.cpython-310.pyc | Bin 0 -> 2223 bytes .../reachability_graph/reachability_graph.py | 56 ++ .../restricted_coverability_graph/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1077 bytes ...tricted_coverability_graph.cpython-310.pyc | Bin 0 -> 2923 bytes .../restricted_coverability_graph.py | 89 +++ .../soundness/woflan/graphs/utility.py | 139 ++++ .../woflan/not_well_handled_pairs/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1042 bytes .../not_well_handled_pairs.cpython-310.pyc | Bin 0 -> 2426 bytes .../not_well_handled_pairs.py | 64 ++ .../woflan/place_invariants/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1092 bytes .../place_invariants.cpython-310.pyc | Bin 0 -> 2672 bytes .../__pycache__/s_component.cpython-310.pyc | Bin 0 -> 3315 bytes .../uniform_invariant.cpython-310.pyc | Bin 0 -> 1340 bytes .../__pycache__/utility.cpython-310.pyc | Bin 0 -> 4225 bytes .../place_invariants/place_invariants.py | 66 ++ .../woflan/place_invariants/s_component.py | 90 +++ .../place_invariants/uniform_invariant.py | 24 + .../woflan/place_invariants/utility.py | 118 ++++ src/evaluation/wf_net/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 982 bytes .../__pycache__/evaluator.cpython-310.pyc | Bin 0 -> 1655 bytes src/evaluation/wf_net/evaluator.py | 52 ++ src/evaluation/wf_net/variants/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 981 bytes .../__pycache__/petri_net.cpython-310.pyc | Bin 0 -> 2709 bytes src/evaluation/wf_net/variants/petri_net.py | 101 +++ .../header-bar/header-bar.component.ts | 22 +- .../backendService/backend.service.ts | 19 + .../app/services/logService/log.service.ts | 3 + 107 files changed, 3744 insertions(+), 132 deletions(-) delete mode 100644 src/backend/mine_log.py create mode 100644 src/evaluation/__init__.py create mode 100644 src/evaluation/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/__pycache__/evaluator.cpython-310.pyc create mode 100644 src/evaluation/earth_mover_distance/__init__.py create mode 100644 src/evaluation/earth_mover_distance/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/earth_mover_distance/__pycache__/evaluator.cpython-310.pyc create mode 100644 src/evaluation/earth_mover_distance/evaluator.py create mode 100644 src/evaluation/earth_mover_distance/variants/__init__.py create mode 100644 src/evaluation/earth_mover_distance/variants/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/earth_mover_distance/variants/__pycache__/pyemd.cpython-310.pyc create mode 100644 src/evaluation/earth_mover_distance/variants/pyemd.py create mode 100644 src/evaluation/evaluator.py create mode 100644 src/evaluation/generalization/__init__.py create mode 100644 src/evaluation/generalization/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/generalization/__pycache__/evaluator.cpython-310.pyc create mode 100644 src/evaluation/generalization/__pycache__/parameters.cpython-310.pyc create mode 100644 src/evaluation/generalization/evaluator.py create mode 100644 src/evaluation/generalization/parameters.py create mode 100644 src/evaluation/generalization/variants/__init__.py create mode 100644 src/evaluation/generalization/variants/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/generalization/variants/__pycache__/token_based.cpython-310.pyc create mode 100644 src/evaluation/generalization/variants/token_based.py create mode 100644 src/evaluation/precision/__init__.py create mode 100644 src/evaluation/precision/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/precision/__pycache__/evaluator.cpython-310.pyc create mode 100644 src/evaluation/precision/__pycache__/parameters.cpython-310.pyc create mode 100644 src/evaluation/precision/__pycache__/utils.cpython-310.pyc create mode 100644 src/evaluation/precision/evaluator.py create mode 100644 src/evaluation/precision/parameters.py create mode 100644 src/evaluation/precision/utils.py create mode 100644 src/evaluation/precision/variants/__init__.py create mode 100644 src/evaluation/precision/variants/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/precision/variants/__pycache__/align_etconformance.cpython-310.pyc create mode 100644 src/evaluation/precision/variants/__pycache__/etconformance_token.cpython-310.pyc create mode 100644 src/evaluation/precision/variants/align_etconformance.py create mode 100644 src/evaluation/precision/variants/etconformance_token.py create mode 100644 src/evaluation/replay_fitness/__init__.py create mode 100644 src/evaluation/replay_fitness/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/replay_fitness/__pycache__/evaluator.cpython-310.pyc create mode 100644 src/evaluation/replay_fitness/__pycache__/parameters.cpython-310.pyc create mode 100644 src/evaluation/replay_fitness/evaluator.py create mode 100644 src/evaluation/replay_fitness/parameters.py create mode 100644 src/evaluation/replay_fitness/variants/__init__.py create mode 100644 src/evaluation/replay_fitness/variants/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/replay_fitness/variants/__pycache__/alignment_based.cpython-310.pyc create mode 100644 src/evaluation/replay_fitness/variants/__pycache__/token_replay.cpython-310.pyc create mode 100644 src/evaluation/replay_fitness/variants/alignment_based.py create mode 100644 src/evaluation/replay_fitness/variants/token_replay.py create mode 100644 src/evaluation/simplicity/__init__.py create mode 100644 src/evaluation/simplicity/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/simplicity/__pycache__/evaluator.cpython-310.pyc create mode 100644 src/evaluation/simplicity/evaluator.py create mode 100644 src/evaluation/simplicity/variants/__init__.py create mode 100644 src/evaluation/simplicity/variants/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/simplicity/variants/__pycache__/arc_degree.cpython-310.pyc create mode 100644 src/evaluation/simplicity/variants/arc_degree.py create mode 100644 src/evaluation/soundness/__init__.py create mode 100644 src/evaluation/soundness/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/__init__.py create mode 100644 src/evaluation/soundness/woflan/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/__pycache__/algorithm.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/algorithm.py create mode 100644 src/evaluation/soundness/woflan/graphs/__init__.py create mode 100644 src/evaluation/soundness/woflan/graphs/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/graphs/__pycache__/utility.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__init__.py create mode 100644 src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__pycache__/minimal_coverability_graph.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/minimal_coverability_graph.py create mode 100644 src/evaluation/soundness/woflan/graphs/reachability_graph/__init__.py create mode 100644 src/evaluation/soundness/woflan/graphs/reachability_graph/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/graphs/reachability_graph/__pycache__/reachability_graph.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/graphs/reachability_graph/reachability_graph.py create mode 100644 src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__init__.py create mode 100644 src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__pycache__/restricted_coverability_graph.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/restricted_coverability_graph.py create mode 100644 src/evaluation/soundness/woflan/graphs/utility.py create mode 100644 src/evaluation/soundness/woflan/not_well_handled_pairs/__init__.py create mode 100644 src/evaluation/soundness/woflan/not_well_handled_pairs/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/not_well_handled_pairs/__pycache__/not_well_handled_pairs.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/not_well_handled_pairs/not_well_handled_pairs.py create mode 100644 src/evaluation/soundness/woflan/place_invariants/__init__.py create mode 100644 src/evaluation/soundness/woflan/place_invariants/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/place_invariants/__pycache__/place_invariants.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/place_invariants/__pycache__/s_component.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/place_invariants/__pycache__/uniform_invariant.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/place_invariants/__pycache__/utility.cpython-310.pyc create mode 100644 src/evaluation/soundness/woflan/place_invariants/place_invariants.py create mode 100644 src/evaluation/soundness/woflan/place_invariants/s_component.py create mode 100644 src/evaluation/soundness/woflan/place_invariants/uniform_invariant.py create mode 100644 src/evaluation/soundness/woflan/place_invariants/utility.py create mode 100644 src/evaluation/wf_net/__init__.py create mode 100644 src/evaluation/wf_net/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/wf_net/__pycache__/evaluator.cpython-310.pyc create mode 100644 src/evaluation/wf_net/evaluator.py create mode 100644 src/evaluation/wf_net/variants/__init__.py create mode 100644 src/evaluation/wf_net/variants/__pycache__/__init__.cpython-310.pyc create mode 100644 src/evaluation/wf_net/variants/__pycache__/petri_net.cpython-310.pyc create mode 100644 src/evaluation/wf_net/variants/petri_net.py diff --git a/src/backend/.DS_Store b/src/backend/.DS_Store index 10b404014fd30fd8b8d4dc68d6ab2da8d4b403a1..74b6af4ecf10a5602fdf50e7768df49a5e8fcedb 100644 GIT binary patch delta 129 zcmZn(XmOBWU|?W$DortDU;r^W7$CF&_ryZ6iF%5nq9Az&1_1_!OokGMc!qceJ%)mf zh11w4Ht=p{=V0Mr6rG$RaC367(3{QvBKMd!n@dD63o<i+fdn@L1A{9Ag8=u&!tczJ U`Befr7#SEqb}(#?=b6I{0N3^yEdT%j delta 537 zcmZp1XbF&DU|?W$DortDU{GLSVBlbY&;mRY3&ketDGGxG85tM^7#I>gbMljua`KZl z7EWWI*ucM;or8rV5+u*cpvREOkiw9PP{RP$!YIqYz@YaZ3>ZLwok5SGjG>qznIRvf z9%dp`bvLr=$vgr#jguHk88R7i7*bGGGB`3YFw6rR`yXU111keu<>V&e1`cB*6CDL( z6NAZIBKG1)l4Zd~c{%xc=^#%}ejxDCkcA<IA(Nq)p#;?eCXfnb3*hRY#+&LW7#mxH zj2DGVAsfE=oZv$yU1o-2h9ZV!RI?eo85kJrkQ@(HHMvRnxG<EBV$>RuXy%O#6)b|x zV81dba5FG4xH2#Z@N6vn&ODi4CQyV45_1|L1&j;~1|WKJflTk_W+5HYov{i4t<i`j diff --git a/src/backend/api/routes/input_output/importing.py b/src/backend/api/routes/input_output/importing.py index 178a958..f21641e 100644 --- a/src/backend/api/routes/input_output/importing.py +++ b/src/backend/api/routes/input_output/importing.py @@ -12,6 +12,10 @@ from endpoints.load_event_log import calculate_event_log_properties from fastapi import APIRouter, Depends, File, HTTPException, UploadFile from pydantic import BaseModel +from backend_utilities import mine_log as ml +import tempfile +import os + router = APIRouter(tags=["importing"], prefix="/importing") @@ -39,6 +43,44 @@ class FilePathInput(BaseModel): file_path: str +@router.post("/loadOCELFromFile") +async def load_ocel_from_file( + file: UploadFile = File(...), + config_repo: ConfigurationRepository = Depends(get_config_repo), +): + cache.pcache = {} + + # Save the uploaded file to a temporary location + with tempfile.NamedTemporaryFile(delete=False) as tmp_file: + tmp_file.write(await file.read()) + tmp_file_path = tmp_file.name + + content = "".join([line.decode("UTF-8") for line in file.file]) + + try: + print(f"FILE: {tmp_file_path}") + ocel = await ml.process_ocel(filename=tmp_file_path) + except FileNotFoundError as e: + raise HTTPException( + status_code=404, detail=f"Event log not found ({tmp_file_path})" + ) + finally: + # Clean up the temporary file + os.remove(tmp_file_path) + + #event_log = xes_importer.deserialize(content) + #use_mp = ( + # len(event_log) > config_repo.get_configuration().min_traces_variant_detection_mp + #) + #info = calculate_event_log_properties(event_log, use_mp=use_mp) + print(f"OCEL: {ocel}") + print("\n\n\n") + return ocel + + +class FilePathInput(BaseModel): + file_path: str + @router.post("/loadEventLogFromFilePath") async def load_event_log_from_file_path( d: FilePathInput, config_repo: ConfigurationRepository = Depends(get_config_repo) diff --git a/src/backend/api/websocket/main.py b/src/backend/api/websocket/main.py index 394356f..e8a4728 100644 --- a/src/backend/api/websocket/main.py +++ b/src/backend/api/websocket/main.py @@ -2,15 +2,18 @@ from fastapi.routing import APIRouter from typing import Callable from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect + from api.routes.conformance.variantConformance import ( calculate_alignment_intern_with_timeout, get_alignment_callback, ) + from api.routes.variants.subvariantMining import ( mine_repetition_patterns_with_timeout, RepetitionsMiningConfig, get_repetition_mining_callback, ) + from backend_utilities.configuration.repository import ConfigurationRepositoryFactory from backend_utilities.multiprocessing.pool_factory import PoolFactory from cache import cache diff --git a/src/backend/mine_log.py b/src/backend/mine_log.py deleted file mode 100644 index d7ff26c..0000000 --- a/src/backend/mine_log.py +++ /dev/null @@ -1,128 +0,0 @@ -from fastapi import FastAPI, UploadFile, File -from pydantic import BaseModel -from typing import List, Dict, Any, Optional -import uvicorn - -from ocpa.objects.log.importer.ocel import factory as ocel_import_factory -from ocpa.visualization.log.variants import factory as variants_visualization_factory -from ocpa.algo.util.filtering.log import case_filtering -from ocpa.objects.log.exporter.ocel import factory as ocel_export_factory - -import ocsv.Input_Extraction_Definition as IED # Seems like a helper class to define data structures and querying lanes -import ocsv.Super_Variant_Definition as SVD # Super Variant Definition, super lane definition -import ocsv.Super_Variant_Visualization as SVV # Visualization of super variants -import ocsv.Intra_Variant_Summarization as IAVS # -import ocsv.Summarization_Selection as SS -import ocsv.Intra_Variant_Generation as IAVG -import ocsv.Inter_Variant_Summarization as IEVS -import ocsv.Inter_Variant_Generation as IEVG -import ocsv.Super_Variant_Hierarchy as SVH -import time -import numpy as np - -app = FastAPI() - -class Parameters(BaseModel): - execution_extraction: Optional[str] = "leading_type" - leading_type: Optional[str] = "application" - max_levels: Optional[int] = 4 - frequency_distribution_type: Optional[str] = "NORMAL" - - -@app.post("/process_ocel/") -#async def process_ocel(file: UploadFile = File(...), parameters: Parameters = None): -async def process_ocel(parameters: Parameters = Parameters()): - ''' - # Save the uploaded file - file_location = f"/tmp/{file.filename}" - with open(file_location, "wb+") as file_object: - file_object.write(file.file.read()) - ''' - # Predefined filename - filename = "../ocsv/ocsv/EventLogs/BPI2017-Top10.jsonocel" - #parameters = {"execution_extraction": "leading_type", - #"leading_type": "application"} - # Load the OCEL file - ocel = ocel_import_factory.apply(file_path=filename, parameters=parameters) - - # Step 1: Summarization Generation - all_summarizations, per_variant_dict, per_encoding_dict = IAVG.complete_intra_variant_summarization(ocel) - - # Step 2: Summarization Matching - summarizations = SS.intra_variant_summarization_selection(all_summarizations, per_variant_dict, per_encoding_dict) - - # Step 3: Inter-Variant Summarization - IEVG.NESTED_STRUCTURES = True - initial_set = [summarizations[i] for i in range(len(summarizations))] - hierarchies, final_super_variants = IEVG.generate_super_variant_hierarchy( - initial_set, parameters.max_levels, frequency_distribution_type=getattr(IEVG.Distribution, parameters.frequency_distribution_type) - ) - - # Extract super variants - values = [] - for super_variant in final_super_variants[0]: - values.append("NEW SUPER VARIANT") - values.append(super_variant) - - def extract_super_variants(nested_structure): - """Recursively extract SuperVariant objects from a nested structure.""" - super_variants = [] - if isinstance(nested_structure, tuple): - for item in nested_structure: - super_variants.extend(extract_super_variants(item)) - elif isinstance(nested_structure, list): - for item in nested_structure: - super_variants.extend(extract_super_variants(item)) - elif isinstance(nested_structure, SVD.SuperVariant): - super_variants.append(nested_structure) - return super_variants - - # Create a useful data structure out of the nested list of super variants and tuples containing super variants. - super_variants_dict = {} - current_super_variant = None - - for line in values: - if line == "NEW SUPER VARIANT": - current_super_variant = None - else: - if current_super_variant is None: - current_super_variant = [] - super_variants_dict[len(super_variants_dict)] = current_super_variant - current_super_variant.extend(extract_super_variants(line)) - - def extract_hierarchy_info(hierarchy, level=0, info=None): - """Recursively extract information about the hierarchical structure.""" - if info is None: - info = {"levels": {}, "super_variants": []} - - if isinstance(hierarchy, dict): - for key, value in hierarchy.items(): - if key not in info["levels"]: - info["levels"][key] = [] - info["levels"][key].append(value) - extract_hierarchy_info(value, level + 1, info) - elif isinstance(hierarchy, list): - for item in hierarchy: - extract_hierarchy_info(item, level, info) - elif isinstance(hierarchy, tuple): - for item in hierarchy: - extract_hierarchy_info(item, level, info) - elif isinstance(hierarchy, SVD.SuperVariant): - info["super_variants"].append(hierarchy) - - return info - - # Extract hierarchy information - hierarchy_info_list = [] - for hierarchy in hierarchies: - hierarchy_info = extract_hierarchy_info(hierarchy) - hierarchy_info_list.append(hierarchy_info) - - return { - "super_variants_dict": super_variants_dict, - "hierarchy_info_list": hierarchy_info_list - } - -if __name__ == "__main__": - uvicorn.run(app, host="0.0.0.0", port=8000) - diff --git a/src/evaluation/__init__.py b/src/evaluation/__init__.py new file mode 100644 index 0000000..789b957 --- /dev/null +++ b/src/evaluation/__init__.py @@ -0,0 +1,26 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation import generalization, precision, replay_fitness, simplicity, evaluator, wf_net +import pkgutil + +if pkgutil.find_loader("pyemd"): + # import the EMD only if the pyemd package is installed + from evaluation import earth_mover_distance + +if pkgutil.find_loader("networkx") and pkgutil.find_loader("sympy"): + # import the Woflan package only if NetworkX and sympy are installed + from evaluation import soundness diff --git a/src/evaluation/__pycache__/__init__.cpython-310.pyc b/src/evaluation/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55963c030775480230afbc23cea3e705eb17672b GIT binary patch literal 1232 zcmd1j<>g{vU|=x4>6<>2g@NHQh=Yuo85kHG7#J9e^B5QyQW&BbQW#U1au{=&qL^}- zqnLA9qF8cSqgZp<qS$iTqu3ci^2|9LQ5>lp3pi6)7BUtorZA?k<}l`RMR74QfJNCB zG8P$wS?miLi%e4(Q#f)MbGf6q85vTTgW*_{Gx0bR0|S?Wf`URwMrN@>T4qkFLT0f- zL1IyfLVlV;fUikFrGkcUeo?A|XI@&ql|n{INkOrdzJ5WjNkOGvT4sq}T2W$YUPgXe zYLQ+_s-_+n*ffYnkcma9sS3sUX(i=}MX6Q_mHDL#$%%OiMX4#7#U(|VNu?#J3YjGe ziFqmd`9%u3`6-!cm0%MxOB71;Qc{Z)N-|OvN>Yn*i$M+oaoqhv72H$vQi~FE6aq?< zax#+@d@_?$^NLdy5{nfIKqAE%sVNFc5Q9Kk+(51fhPpz*Ex$A`C9xzkKTk&?HM1lm zwMd~XwWv5VKTp9JW(m|p9fkZNuyZsLOF)h<Qphg=Y1C9m%&Sz$Ni0F=MfW+vgDDD` zd0=m3<QJqWlw>59fV^CunUkZCl&Vl#oSIgeqXV`jskB5P+%v?(KQu(a(JxXV+%YJ~ z(Jv&@TA@6%BqP7HL?N{-6=GIqZb43FYKlU6Vo_0IUP+}wej3;aU)LaK4@bWcM<-7o z&yYxk{2~Q6&k#S?;9vze{~!fNg#gE(5Kre&AIBhtfY6`-|6o@=1%=>Lki$~JCg2I* zG;kahr7EPPmLz886hr+SnO~|<oRMFelcJE3SeB|#l$xBHS(cijkf@NHUjT9%*!#E* z2HTsMlb@Fk_9!IX=_x39rYYp*m*^-Ir=}{{z>;2hdAVMCUa4MwQM!H(B;t$p?ew@_ zf^wB6+bzCyNaDz>0>|+!&Vr)U<V;ZX-r_4tEyzi%j8Drf$xAIRzQt7x4#wonlFD10 zsbz^drHLi^MYq_>)8g||OZ=i(3o27{Q#2WGiKHeLm1M-{=9i@w#e-rtF)umw7Drxc zNqK%zcEv5$;>z5DN=?RFoW-ET1u|Td=N5Z>d`f<DeEcmgsM(qMc|}YN3=Bn}OkczT zVzGh<HW0ziz`$^ey&yZiv?Md<7I#`^UP^pUequ^$Q4t47o|}PzVI@P60Ei7Deg*1> z7N-^!>lc^j=cMVoq!wqF<QM4YX69w)rN^gdmgpCk7Ni#GmL(QtCgzpsrdAZ><QJtD z=@%Cz>mwYbA0MBYmst`YuUAlci^C>2KczG$)s7KVB7tmSVNej@VB})rVG>~yVB+`( E03<McqW}N^ literal 0 HcmV?d00001 diff --git a/src/evaluation/__pycache__/evaluator.cpython-310.pyc b/src/evaluation/__pycache__/evaluator.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..144fed0f31b32085fd8f16ba37e162e693c48748 GIT binary patch literal 3842 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 literal 0 HcmV?d00001 diff --git a/src/evaluation/earth_mover_distance/__init__.py b/src/evaluation/earth_mover_distance/__init__.py new file mode 100644 index 0000000..b686dd1 --- /dev/null +++ b/src/evaluation/earth_mover_distance/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from pm4py.evaluation.earth_mover_distance import evaluator, variants diff --git a/src/evaluation/earth_mover_distance/__pycache__/__init__.cpython-310.pyc b/src/evaluation/earth_mover_distance/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..069ee8fa5be8d80d2b9bd05e376ea18811c2acbc GIT binary patch literal 1028 zcmd1j<>g{vU|{e!5KC8OW?*;>;vi!t1_lNP1_p*=2?hp+6ox2<6vh;$9L8LxD5hNI zC}u{66y{(CO_s#tObiTM3JMAeAsLy)3Tc@+sS26J3I&NpB?|dz3IV<*0hJ0GzWGI| z3Z8js`Bn-UB_##LR{Ht{xh4gbdTE&@dTB+8rFj|oX{kkeDXE%zTwv268bKx&rKTzr z=ckpFCl;kzDOBc{DkLZ7DHNrqWEPhcWhRxDq$*^VC?w{k=;s$H<mRVjrd5JX$ShGP z%}YrwQYgttRVYa<$}I*t2*h#s3srDW%}Xsx%uxs^P0GnkR`AJ8PR%P$RY)vWC;*8R zXQZYmBtZ-UX>kL&A{gol1-Jatyp+U}%=|nZh1ATFjMO59vecsD%=|nBW0)mS6Ll2w zi@?s&NGt(4zDOaz0Hje<Au+E~At$i}p%>lf2oI(xWafdrk&$1Js!)=VSOW5Ld1g+I zLQ<+iX>n>=X^sxqmZZ`Wg>cUh5C6~*1xLR~g>c89AV<HDNNa`i%#w`!(h`N#vQ&s! znYjfynW-rX<%vZ_iFqZJ3i)YZBYa(hoIM=<LL8kueLO=V74nM|+&n}4T!VuZ-28(S z92Ei_gF-x=Lwy{B6aqqn0{nwr^%N9>Q$Y?(1)G2;eAB>jRFtZal3J3OnNtk)b7X$0 zLUBfZX-<klMq*j2LQ!gRYGzq#ibA47a()5GX<+Z;HW+MgVorWuI@qI-bf>4F;F+e7 zmtUfzP@I~oU;|5f<>lpi>3OAk`9<maIgp4i*0<B+dI?JZnoPGiQ_B)_N)t=+i*9k0 zB^G5S=9LutX|mj6kB?8uPmYg|QiUX5sItubJiXM!qLPgG-2AfCqIgieCFUik7J;%* z5i<h=!%BuCRt5$L@vBHbv^ce>SiiVDKPOE;H#09YEx#x|)mT3@udEo%&jUFtNxz`7 zBqKl1SkKTvzc{lbRkt89IXf{uwHT6h^bxk}W3yd9K0Y%qvm`!Vub}c4hfQvNN@-52 R9U~}5voJ6)urP=S002mJLCydG literal 0 HcmV?d00001 diff --git a/src/evaluation/earth_mover_distance/__pycache__/evaluator.cpython-310.pyc b/src/evaluation/earth_mover_distance/__pycache__/evaluator.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6c586d2d84d1a08ec1a29c60f92e349e47b10e5 GIT binary patch literal 2174 zcmd1j<>g{vU|{e!5KGTzXJB{?;vi!-1_lNP1_p-W1_lO(6ox2<6vh;$9L8LxC?-Y_ zn>mL$mnDh?%x1}9&1H*X1G8Ck*rV7}7*p7CIC43oIH6)(QC#i}DeNg6Eet6fsVvRR zQQWEASv*m^sq9&NDV!->shla?DLlO_Akh@oG^P~Z6uuV5X2vM~6xLt{P5#8=ObiTM z3JMAeAsLy)3Tc@+sS26J3I&NpB?|dz3IV<*0hJ0GzWGI|3Z8js`Bn-UB_##LR{Ht{ zxh4gbdTE&@dTB+8rFj|oX{kkeDXE%zTwv268bKx&rKTzr=ckpFCl;kzDOBc{DkLZ7 zDHNrqWEPhcWhRxDq$*^VC?w{k=;s$H<mRVjrd5JX$ShGP%}YrwQYgttRVYa<$}I*t z2*h#s3srDW%}Xsx%uxs^P0GnkR`AJ8PR%P$RY)vWC;*8RXQZYmBtZ-UX>kL&A{gol z1-Jatyp+U}%=|nZh1ATFjMO59vecsD%=|nBW0)mS6Ll2wi@?s&NGt(4zDOaz0Hje< zAu+E~At$i}p%>lf2oI(xWafdrk&$1Js!)=VSOW5Ld1g+ILQ<+iX>n>=X^sxqmZZ`W zg>cUh5C6~*1xLR~g>c89AV<HDNNa`i%#w`!(h`N#vQ&s!nYjfynW-rX<%vZ_iFqZJ z3i)YZBYa(hoIM=<LL8kueLO=V74nM|+&n}4T!VuZ-28(S92Ei_gF-x=Lwy{B6aqqn z0{nwr^%N9>Q$Y?(1)G2;eAB>jRFtZal3J3OnNtk)b7X$0LUBfZX-<klMq*j2LQ!gR zYGzq#ibA47a()5GX<+Z;HW+MgVorWuI@qI-bf>4F;F+e7mtUfzP@I~oU;|5f<>lpi z>3OAk`9<maIgp4i*0<B+dI`#BnvAzt3o27{Q#2WGvAE`y=4vwD;!3SZO^z=u$;>JC z(`3BG9_AVp?CI~93{sDb8DV)-fPsM_l_82Tg&~S5l{tzfg)x{xlj#;mSYlCTVqQtH zpC;=qj`;Yz#N5>Q_*-1@@wxdar8yurPkek~X<`mUrih7wf#DWwK%}d$%SwhKZUzPj z@vB%rv^ce>SiiVDKPOE;H#09YEx#x|)mT3@udEo%&jWcaNxz`7BqKl1SkKTvzc{lb zRkt89IXf{uwHT89^i#_cb4tMpSwA(gs3apkH@_^kC?1sZ67!N%Ve<J!dIgn5ppr)t zRPwMeFfed1a4>>Js#uNmjPy*an2q%eep!VUgOh;*BzGs~r044)?9jtzhXT||nk-Sg zDX9fTsmX~YsVVW9c~M+NsUYWo*thtTON)w9^Gf2O`Ro=uGyx@p90Q935Sx{Ofx#K% zN(BZ6h7yJaj46x@85tQ$m=-YCFfC-vVyR)MVXR?JVd`ZLX3%6#oXH3+zuZ$xiovPO z)z?KKCowO*G%-C@At|+_JT(=PV@k^N;S$BrVk)sHF*mg&wWt`J0(FrASOjE{A(#a! zBi%BKic8>TfQ3N{jKD0Ag5cET{Ja!Y6=je_16Kn}M6i+u6v{=JB^kM36$Pj+0vQ^B zDg-qT;$o0IP6KrmGV_viN>eiP(yhRTgH(gCt^!Pt9yoDYDS)$#LQ-OJYKj6Z`9Ojw zD7B=tC=X&Aa`5PaO$4b0GeE|ILLMXlN_$|zB3=dt22ch>_)3%Y7DstvQC?<Vdhsn5 z5UU6j?YFqoQ%mBZUc1GbSWu8tsmT(>3JP9B2xWAO9V%Z0%AbhrRKx?ySRxDz44^bq z3^I;ON{q1xB%&#Bi#<L*B|kYnK1vUgstF`$J*Xwcklf3Xnpc`z#LU3J5XA-24=P8X zZiXfJ%=|o<Y;I~vVi7127YTw~1<G_qpcX+Czl*DzW2jF^e3)a9r=wrUEiRbu)D*B& zkvs%0DL}ru#bE;}TI@h&Q?U#K0|N^Kj{pxN4<iQ{3ox^>a4<75GBL9J6JrJd;q7cW literal 0 HcmV?d00001 diff --git a/src/evaluation/earth_mover_distance/evaluator.py b/src/evaluation/earth_mover_distance/evaluator.py new file mode 100644 index 0000000..528aeea --- /dev/null +++ b/src/evaluation/earth_mover_distance/evaluator.py @@ -0,0 +1,59 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from pm4py.evaluation.earth_mover_distance.variants import pyemd +from enum import Enum +from pm4py.util import exec_utils +import deprecation +from pm4py.meta import VERSION +import warnings + + +class Variants(Enum): + PYEMD = pyemd + + +DEFAULT_VARIANT = Variants.PYEMD + + +@deprecation.deprecated(deprecated_in="2.2.5", removed_in="3.0", + current_version=VERSION, + details="Use the pm4py.algo.evaluation.earth_mover_distance package") +def apply(lang1, lang2, variant=Variants.PYEMD, parameters=None): + """ + Gets the EMD language between the two languages + + Parameters + ------------- + lang1 + First language + lang2 + Second language + variant + Variants of the algorithm + parameters + Parameters + variants + Variants of the algorithm, including: + - Variants.PYEMD: pyemd based distance + + Returns + ------------- + dist + EMD distance + """ + warnings.warn("Use the pm4py.algo.evaluation.earth_mover_distance package") + return exec_utils.get_variant(variant).apply(lang1, lang2, parameters=parameters) diff --git a/src/evaluation/earth_mover_distance/variants/__init__.py b/src/evaluation/earth_mover_distance/variants/__init__.py new file mode 100644 index 0000000..18335b3 --- /dev/null +++ b/src/evaluation/earth_mover_distance/variants/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from pm4py.evaluation.earth_mover_distance.variants import pyemd diff --git a/src/evaluation/earth_mover_distance/variants/__pycache__/__init__.cpython-310.pyc b/src/evaluation/earth_mover_distance/variants/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fcc433767db38236cdcbadcc1fa3e8966a71da9a GIT binary patch literal 1023 zcmd1j<>g{vU|{e!5KC8JW?*;>;vi!t1_lNP1_p*=5e5c^6ox2<6vh;$9L8LxC?-aR z6y{(CO_s#tObiTM3JMAeAsLy)3Tc@+sS26J3I&NpB?|dz3IV<*0hJ0GzWGI|3Z8js z`Bn-UB_##LR{Ht{xh4gbdTE&@dTB+8rFj|oX{kkeDXE%zTwv268bKx&rKTzr=ckpF zCl;kzDOBc{DkLZ7DHNrqWEPhcWhRxDq$*^VC?w{k=;s$H<mRVjrd5JX$ShGP%}Yrw zQYgttRVYa<$}I*t2*h#s3srDW%}Xsx%uxs^P0GnkR`AJ8PR%P$RY)vWC;*8RXQZYm zBtZ-UX>kL&A{gol1-Jatyp+U}%=|nZh1ATFjMO59vecsD%=|nBW0)mS6Ll2wi@?s& zNGt(4zDOaz0Hje<Au+E~At$i}p%>lf2oI(xWafdrk&$1Js!)=VSOW5Ld1g+ILQ<+i zX>n>=X^sxqmZZ`Wg>cUh5C6~*1xLR~g>c89AV<HDNNa`i%#w`!(h`N#vQ&s!nYjfy znW-rX<%vZ_iFqZJ3i)YZBYa(hoIM=<LL8kueLO=V74nM|+&n}4T!VuZ-28(S92Ei_ zgF-x=Lwy{B6aqqn0{nwr^%N9>Q$Y?(1)G2;eAB>jRFtZal3J3OnNtk)b7X$0LUBfZ zX-<klMq*j2LQ!gRYGzq#ibA47a()5GX<+Z;HW+MgVorWuI@qI-bf>4F;F+e7mtUfz zP@I~oU;|5f<>lpi>3OAk`9<maIgp4i*0<B+dI?JVnvAzt3o27{Q~WfUZ?VV6r{pKc z$4BWwl51*NVooVI;`LG!i%K%$bMwnmi{e2sm6(^Ds#lg+l$n@UQd|VeIV%~8SQr=} z#II`o(BjmhV*TRs{G2rX+|0bpwEUv<RAc?rys~02KM&-XB>jTQl8pR3V?9Fy{o>4$ zRNaEa<m|-s)M7}6(MPyIADavGVJ^^*kI&4@EQycTE2zB1VUwGmQks)$#|X;IAV;w< H2nYZG5j#P+ literal 0 HcmV?d00001 diff --git a/src/evaluation/earth_mover_distance/variants/__pycache__/pyemd.cpython-310.pyc b/src/evaluation/earth_mover_distance/variants/__pycache__/pyemd.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70fc6d67e009093eb97479dc5ddd1ddb6dec2b70 GIT binary patch literal 5072 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 literal 0 HcmV?d00001 diff --git a/src/evaluation/earth_mover_distance/variants/pyemd.py b/src/evaluation/earth_mover_distance/variants/pyemd.py new file mode 100644 index 0000000..e9b3386 --- /dev/null +++ b/src/evaluation/earth_mover_distance/variants/pyemd.py @@ -0,0 +1,175 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from pm4py.util.regex import SharedObj, get_new_char +from pm4py.util import string_distance +import numpy as np +from pyemd import emd +from pm4py.util import exec_utils + + +class Parameters: + STRING_DISTANCE = "string_distance" + + +def normalized_levensthein(s1, s2): + """ + Normalized Levensthein distance + + Parameters + ------------- + s1 + First string + s2 + Second string + + Returns + -------------- + dist + Distance + """ + return float(string_distance.levenshtein(s1, s2)) / float(max(len(s1), len(s2))) + + +def get_act_correspondence(activities, parameters=None): + """ + Gets an encoding for each activity + + Parameters + -------------- + activities + Activities of the two languages + parameters + Parameters + + Returns + ------------- + encoding + Encoding into hex characters + """ + if parameters is None: + parameters = {} + + shared_obj = SharedObj() + ret = {} + for act in activities: + get_new_char(act, shared_obj) + ret[act] = shared_obj.mapping_dictio[act] + + return ret + + +def encode_two_languages(lang1, lang2, parameters=None): + """ + Encode the two languages into hexadecimal strings + + Parameters + -------------- + lang1 + Language 1 + lang2 + Language 2 + parameters + Parameters of the algorithm + + Returns + -------------- + enc1 + Encoding of the first language + enc2 + Encoding of the second language + """ + if parameters is None: + parameters = {} + + all_activities = sorted(list(set(y for x in lang1 for y in x).union(set(y for x in lang2 for y in x)))) + acts_corresp = get_act_correspondence(all_activities, parameters=parameters) + + enc1 = {} + enc2 = {} + + for k in lang1: + new_key = "".join(acts_corresp[i] for i in k) + enc1[new_key] = lang1[k] + + for k in lang2: + new_key = "".join(acts_corresp[i] for i in k) + enc2[new_key] = lang2[k] + + # each language should have the same keys, even if not present + for x in enc1: + if x not in enc2: + enc2[x] = 0.0 + + for x in enc2: + if x not in enc1: + enc1[x] = 0.0 + + enc1 = [(x, y) for x, y in enc1.items()] + enc2 = [(x, y) for x, y in enc2.items()] + + # sort the keys in a decreasing way + enc1 = sorted(enc1, reverse=True, key=lambda x: x[0]) + enc2 = sorted(enc2, reverse=True, key=lambda x: x[0]) + + return enc1, enc2 + + +def apply(lang1, lang2, parameters=None): + """ + Calculates the EMD distance between the two stochastic languages + + Parameters + ------------- + lang1 + First language + lang2 + Second language + parameters + Parameters of the algorithm, including: + - Parameters.STRING_DISTANCE: function that accepts two strings and returns a distance + + Returns + --------------- + emd_dist + EMD distance + """ + if parameters is None: + parameters = {} + + distance_function = exec_utils.get_param_value(Parameters.STRING_DISTANCE, parameters, normalized_levensthein) + + enc1, enc2 = encode_two_languages(lang1, lang2, parameters=parameters) + + # transform everything into a numpy array + first_histogram = np.array([x[1] for x in enc1]) + second_histogram = np.array([x[1] for x in enc2]) + + # including a distance matrix that includes the distance between + # the traces + distance_matrix = [] + for x in enc1: + distance_matrix.append([]) + for y in enc2: + # calculates the (normalized) distance between the strings + dist = distance_function(x[0], y[0]) + distance_matrix[-1].append(float(dist)) + + distance_matrix = np.array(distance_matrix) + + ret = emd(first_histogram, second_histogram, distance_matrix) + + return ret diff --git a/src/evaluation/evaluator.py b/src/evaluation/evaluator.py new file mode 100644 index 0000000..b75264b --- /dev/null +++ b/src/evaluation/evaluator.py @@ -0,0 +1,114 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from pm4py import util as pmutil +from pm4py.algo.conformance.tokenreplay.variants import token_replay +from evaluation.generalization.variants import token_based as generalization_token_based +from evaluation.precision.variants import etconformance_token as precision_token_based +from evaluation.replay_fitness.variants import token_replay as fitness_token_based +from evaluation.simplicity.variants import arc_degree as simplicity_arc_degree +from pm4py.objects import log as log_lib +from pm4py.objects.conversion.log import converter as log_conversion +from pm4py.util import xes_constants as xes_util +from pm4py.util import constants +from enum import Enum +from pm4py.util import exec_utils +import deprecation +from pm4py.meta import VERSION +import warnings + + +class Parameters(Enum): + ACTIVITY_KEY = constants.PARAMETER_CONSTANT_ACTIVITY_KEY + PARAM_FITNESS_WEIGHT = 'fitness_weight' + PARAM_PRECISION_WEIGHT = 'precision_weight' + PARAM_SIMPLICITY_WEIGHT = 'simplicity_weight' + PARAM_GENERALIZATION_WEIGHT = 'generalization_weight' + + +@deprecation.deprecated(deprecated_in="2.2.5", removed_in="3.0", + current_version=VERSION, + details="Use the pm4py.algo.evaluation.evaluator class") +def apply(log, net, initial_marking, final_marking, parameters=None): + """ + Calculates all metrics based on token-based replay and returns a unified dictionary + + Parameters + ----------- + log + Log + net + Petri net + initial_marking + Initial marking + final_marking + Final marking + parameters + Parameters + + Returns + ----------- + dictionary + Dictionary containing fitness, precision, generalization and simplicity; along with the average weight of + these metrics + """ + warnings.warn("Use the pm4py.algo.evaluation.evaluator class") + if parameters is None: + parameters = {} + log = log_conversion.apply(log, parameters, log_conversion.TO_EVENT_LOG) + + activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, log_lib.util.xes.DEFAULT_NAME_KEY) + fitness_weight = exec_utils.get_param_value(Parameters.PARAM_FITNESS_WEIGHT, parameters, 0.25) + precision_weight = exec_utils.get_param_value(Parameters.PARAM_PRECISION_WEIGHT, parameters, 0.25) + simplicity_weight = exec_utils.get_param_value(Parameters.PARAM_SIMPLICITY_WEIGHT, parameters, 0.25) + generalization_weight = exec_utils.get_param_value(Parameters.PARAM_GENERALIZATION_WEIGHT, parameters, 0.25) + + sum_of_weights = (fitness_weight + precision_weight + simplicity_weight + generalization_weight) + fitness_weight = fitness_weight / sum_of_weights + precision_weight = precision_weight / sum_of_weights + simplicity_weight = simplicity_weight / sum_of_weights + generalization_weight = generalization_weight / sum_of_weights + + parameters_tr = {token_replay.Parameters.ACTIVITY_KEY: activity_key} + + aligned_traces = token_replay.apply(log, net, initial_marking, final_marking, parameters=parameters_tr) + + parameters = { + token_replay.Parameters.ACTIVITY_KEY: activity_key + } + + fitness = fitness_token_based.evaluate(aligned_traces) + precision = precision_token_based.apply(log, net, initial_marking, final_marking, parameters=parameters) + generalization = generalization_token_based.get_generalization(net, aligned_traces) + simplicity = simplicity_arc_degree.apply(net) + + metrics_average_weight = fitness_weight * fitness["log_fitness"] + precision_weight * precision \ + + generalization_weight * generalization + simplicity_weight * simplicity + + fscore = 0.0 + if (fitness['log_fitness'] + precision) > 0: + fscore = (2*fitness['log_fitness']*precision)/(fitness['log_fitness']+precision) + dictionary = { + "fitness": fitness, + "precision": precision, + "generalization": generalization, + "simplicity": simplicity, + "metricsAverageWeight": metrics_average_weight, + "fscore": fscore + } + + return dictionary + diff --git a/src/evaluation/generalization/__init__.py b/src/evaluation/generalization/__init__.py new file mode 100644 index 0000000..9972d59 --- /dev/null +++ b/src/evaluation/generalization/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.generalization import evaluator, variants diff --git a/src/evaluation/generalization/__pycache__/__init__.cpython-310.pyc b/src/evaluation/generalization/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed7ee7c7324415daeac154af07fa7e1f6c8b6949 GIT binary patch literal 998 zcmd1j<>g{vU|{gL?wc;j%)sy%#6iYP3=9ko3=9m#5)2FsDGX5zDU2yhIgGhXQB1ka zQOt}CDa^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur0xA_WeDjM^ z6+H9O@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}CG=fYlN=;QL z&QB{TPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5OsfQ&kXfQo znwOGVq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjhs*qT$PyiAs z&PYvBNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN#xP5uCh92U z7lEClkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv!u>|Dh^30qZ zg``x4(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$dr6mffWvLLe zGII-ZGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7xdsO-xcLVu zI4T4<28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0mGp88p=g9n0 zh2o6-(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu=}u2U!81)E zFTX@bp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9i+HJNU4rj{k<lqQzs7v170 zODxJv%quDO(`32D9v`2QpBx__B?(oKnV+YZ4oOIvRp9hc1j;r=%nS?+D;bJd85khM zuLS+j;?$yI{o?ZcoHTux)Z*-t`~v;l%)HFJ^!W7568+-Rg47~isAamT6$Lr@MX5#l v#YM^b2)p!A?b45r&&<m#iI3MSsJz8tlbfGXnv-hB2+Ff83=9k`3?c#m;UhRf literal 0 HcmV?d00001 diff --git a/src/evaluation/generalization/__pycache__/evaluator.cpython-310.pyc b/src/evaluation/generalization/__pycache__/evaluator.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb0b604a58bc366ccb62df039bb9564813e4a89c GIT binary patch literal 1914 zcmd1j<>g{vU|?vx?VBFX&cN^(#6iYv3=9ko3=9m#6Brm6QW&BbQW#U1au{=&qL>&# zY~~#1T$U(SFq<WZEtfrt9n5CU;mGBT;smqVa=4<nQW#U%bGUPPqIjTUyivUF3@IEb zoGlC~oT=>1%u#%)d|CWa{HgpIj8OuqTv>uCTq)eCJSjXWyuB<bY^nTdOeuUR{4I>l zj8Q@<Y{3kg0*S|&7#O$|6ciLfGBS%5(lT>W6*7wz3KEM-6!Oy)0(?yZDit(*^NUgy zJoD1>trRj!N(zdt^z{pJO$sXY(lSf*(uxvG^D^?&Qj7FbQZ@Csz@|Yof=nz*O;sq) zPb(=;EK0RfsLU@_NKVXCC`wJqEG{X^Oe!r&Rmdz+NX$#o&o5HQ%}>cps|1^nS)x#y zmy%kfP?C|VP?B1dTMTj#h~w@Ts^FfQms*sVqYzM<l#`jP;FFo0npd2vkXWox01_$A zNKH{lf*1tS;s$a>Fw_+aZuzBoDTyVS`FT1DshK4isYME9sYS(^`FRS)FiW5&>L}zF zft{m~SORi<kwSg}NTa4gVqT>}PGSi{FS^eW9!ycl%maHPBflV3p(G=*1mxxN%$yvB zq*R5{;?%U#938MNNu?zU;hrHL{-GfXj((8};f_H;j(#DL)(YjBB^mjpB?_r!sSvX= za|?1ZQ&SYm6N`!x^GYfe^3%XZ___u;dpP=qI68Uyc!oqO<QFNpd4~A81_vv+`3EUD zDg-zNg?KuL`ZxwD1cU|!_y@b{DJTS|f*h6#HUUrgrh((AC{-aPwIne!rx@zz$ox`; z;*9*#oD_wO#IjU{qSWNn%(B!Jg+ztq`~r~Ez~0AgFxcM2ocz3Wuty>3PESF>Gfg2c zzeGo&I5kzl2A1^7%ggoB^Gfydi_-OTAQ4}zZ>Pug5|r;W8E<iy<Y%Ym#U~{er>1B! z-r`Ko&nrtUDoHKUWW2@Vnpc{u$#{z^wIVe+zO*DWr`S)E@fLfSYf!MKzh5%Q2xQC% z%f12(3=F9ZQH&`JQB0}KQ7kEp!3>&Aw>ZKQi!u}QN{an7S#NQ~$LA&HrpCwL;);*Y z%}*)K0kL`F;|og@b09KBObiSRw?y1s{ak|_eLSNaLp=Tc;zRtsUHw)v6!9=HK!{(7 z`k}?CMaBBX<@q^j`Yx%(*(Lb}`nj2TnR)5)>6s<^#ia$QMY?4W+jUbb3Ucy`Qj7G9 zi<0$I%Mx=+!O2@c9g<Bls~}vcOn#AGL1hs;0|SF3sAOVcU|`^2;9vxcRIwWA8R?l; zF&pa{{4xnG1}6&zNN!KeNzd1Vnvt2Gr-y2~LP273c4B&}Cd)0}l+=Qv)a1mH)Rg$l zyjxsFsk!-O5H^2uX;D#XUP(MO$K7IwCZ%LhNeK&j5F6w^XOR1B7#J8z7#1+5FfL?d zWT;_DVJKxN;?88KWhr4=z+A(+kTHv;gmnQ+4MPn}32PSHLgpIA8m1cN8kQ8MUe;g+ zO=iC$J_ZH`P_!2$7A5AUmZTOHYqH<sC{HW`1!(at77(k56%;qz>8U00&~UxQnpjYf zQ+bOoCqF$Nl8iv5#4Vl>|9IChSHF;WAAfgEwp+|O`RTVf3sOspGUM}7OK$OJ=4F;- zCg#NFCKhF9=B3}_P0P$faEpXN7O_JuLBx6yFDU-S85kH8LH-80nTv&kk%du$iHVVo zk?mg*NKRAu7JGbrN`7*De3TN-*wBNTUJOZbGLRU|Ps&P7E-BVS_(d-#KfQ<<WUmCs zQ7ox>rMX3*6mg3Sq6L&kpdkZ`)y(`nm~3upNn#Nwwu(eRW`N?Xh#O=O2Q)_o-{OL4 zOHBd0AIZPq^bYd$Ee;z<ez5~(xMBqc1_l-e9swRk5aeLwVdP*GU}9roW8`3FVq{`u I`6t2*0Jd{1-v9sr literal 0 HcmV?d00001 diff --git a/src/evaluation/generalization/__pycache__/parameters.cpython-310.pyc b/src/evaluation/generalization/__pycache__/parameters.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c70a616dcfe5f26650d3e56dc5030e06ccd18456 GIT binary patch literal 1230 zcmd1j<>g{vU|{e!5KAv&W?*;>;vi!d1_lNP1_p*=0|o|$6ox2<6vh;$9L8LxC?-Y_ zn>mL$mnDjYk-?oIg(Zcxg&~DCm8qFIiZz8Tm_d_0@i-F$1DAq=f<j0}X0bwAW=^U? zX0bv+Vo`}gewspnuSr0qf`)H?QL2JxURu7DLPkkRL9vy-enGBDL8V?=W{F-}QDSLc zMt)jqkzPuwrXCmAG>Ar!iAAZY3dQ+pCFO}lsa6V=`K1cUiFpb|sVSMoB}JJ@r6s8f znI#H|c`5q&MGCq3DVb@NU=uP+6iV|_Qi~KyGEx;vQj2nnK@I|O-2Flo+*9*XixP7b z0!ou|GLsd2GLuvDic=L5ixmn$BE=c0DGEstgFsr`K&}Xex<bJ%zceo;u_QA;Pe&m& zvm_(6NTDpXs5mn}Pr(>w3DiU#h5RD0b2Ji5K#ng`$S(kC)Ko~!t5nEIEJ5f+_c_9Y zDGHf+U~gpP7o;kbWF(e=yj-4{lcSK7s!&>-npT>l1GXipv_v7?GsMF`G(^GCFH#}g zF(}B<FC@}hp**uBBfqpnA+;<OVpe8uK~83Bib8o}QBh)ENu@%58rTS5*C1yPN52q9 zCr=;GkVu96A_X_k5I@)8U<Eh-AO%N-0LP#ZPv=k{#~_7((4YYSU{^f_h2T_>!&1Q} z;0fO}a2yq-Dx{>ABxdFmL;W0?U#d`?kzbmVqL7hTma0&cnw*+hmYSlFsF0jr0CF1G z`?w7T+nbn^pO+5yC?wtKDJXcRDdgps=qMDYrYhLLl3savxn6o+sa}3jx_%BM;*0g| z^tfJv@`WbjEf&|j(p*i(Tb#-HdBr7(c_qckAQ@!L2+K7>3=9mZ3{i|J3{gy}%vmf^ ztSOAa44O>0xB?Q35_3~aQj3cHG}&%(#K-3)=BCES-{Ojo&&^LM%>l7_;^PZT6LTOk zpzO&IB_H4z<ml@f;u;k1?C%#G;^-F=@8}%j8Ri)h8Sm{H8O4LlTggzw!N33^e&y+h z7N-^!>lc^j=cMW9X69w4<rk%=8tbR#l@){ec_0rb=@(R%WaQ@=>lqs87iX5F>J}s> zXD6no7DF<nerj1_PANEf>!(AqNoEy<TLAZyUO{CMsOS&{6&)Z32Ll%)SOnx-_W1ae z{N(ufTP&%0rMX2+3=9lWT#y`JT9TOq_9<8~!u1dqCj$e+Ee;z<0NH^;xftX>76u*x I9!3#n0QS9RumAu6 literal 0 HcmV?d00001 diff --git a/src/evaluation/generalization/evaluator.py b/src/evaluation/generalization/evaluator.py new file mode 100644 index 0000000..d183420 --- /dev/null +++ b/src/evaluation/generalization/evaluator.py @@ -0,0 +1,43 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.generalization.variants import token_based +from pm4py.objects.conversion.log import converter as log_conversion +from enum import Enum +from pm4py.util import exec_utils +import deprecation +from pm4py.meta import VERSION +import warnings + +class Variants(Enum): + GENERALIZATION_TOKEN = token_based + + +GENERALIZATION_TOKEN = Variants.GENERALIZATION_TOKEN +VERSIONS = {GENERALIZATION_TOKEN} + + +@deprecation.deprecated(deprecated_in="2.2.5", removed_in="3.0", + current_version=VERSION, + details="Use the pm4py.algo.evaluation.generalization package") +def apply(log, petri_net, initial_marking, final_marking, parameters=None, variant=GENERALIZATION_TOKEN): + warnings.warn("Use the pm4py.algo.evaluation.generalization package") + if parameters is None: + parameters = {} + + return exec_utils.get_variant(variant).apply(log_conversion.apply(log, parameters, log_conversion.TO_EVENT_LOG), + petri_net, + initial_marking, final_marking, parameters=parameters) diff --git a/src/evaluation/generalization/parameters.py b/src/evaluation/generalization/parameters.py new file mode 100644 index 0000000..6475c53 --- /dev/null +++ b/src/evaluation/generalization/parameters.py @@ -0,0 +1,22 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from enum import Enum +from pm4py.util import constants + + +class Parameters(Enum): + ACTIVITY_KEY = constants.PARAMETER_CONSTANT_ACTIVITY_KEY diff --git a/src/evaluation/generalization/variants/__init__.py b/src/evaluation/generalization/variants/__init__.py new file mode 100644 index 0000000..0e206b4 --- /dev/null +++ b/src/evaluation/generalization/variants/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.generalization.variants import token_based diff --git a/src/evaluation/generalization/variants/__pycache__/__init__.cpython-310.pyc b/src/evaluation/generalization/variants/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..435bab56c97036f7ddcb37d1dc3d184ee6f81bc5 GIT binary patch literal 999 zcmd1j<>g{vU|{IE?VB#e%)sy%#6iYP3=9ko3=9m#A`A=+DGX5zDU2yhIgGhXQA~^s zDa^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur0xA_WeDjM^6+H9O z@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}CG=fYlN=;QL&QB{T zPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5OsfQ&kXfQonwOGV zq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjhs*qT$PyiAs&PYvB zNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN#xP5uCh92U7lECl zkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv!u>|Dh^30qZg``x4 z(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$dr6mffWvLLeGII-Z zGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7xdsO-xcLVuI4T4< z28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0mGp88p=g9n0h2o6- z(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu=}u2U!81)EFTX@b zp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9i!H5qSlm*i)s=EWx^7N@58X)@np zkB?8uPmYhjrIcEhm{SUlc)fH;0?Mp{aLW>lG86Mkii<#bW+g)rD+2?B_?4+2TAW%` ztY2K7pOdEVl3JWyl3$>oo0*rHmmZ&<S)yNDT98_#3pGGDwW1&=zbLgxzqlw_A7P(9 ss(t!U<;D8(@tJv<CGqik1(mlrY;yBcN^?@}7(tnqg@J*Ag+V|70PV>;VE_OC literal 0 HcmV?d00001 diff --git a/src/evaluation/generalization/variants/__pycache__/token_based.cpython-310.pyc b/src/evaluation/generalization/variants/__pycache__/token_based.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..033d3985221fb278a3ef089c3d878b816d9b370e GIT binary patch literal 4125 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 I4rV@m0Gjc_vH$=8 literal 0 HcmV?d00001 diff --git a/src/evaluation/generalization/variants/token_based.py b/src/evaluation/generalization/variants/token_based.py new file mode 100644 index 0000000..83f098c --- /dev/null +++ b/src/evaluation/generalization/variants/token_based.py @@ -0,0 +1,115 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from collections import Counter +from math import sqrt + +from pm4py import util as pmutil +from pm4py.algo.conformance.tokenreplay import algorithm as token_replay +from evaluation.generalization.parameters import Parameters +from pm4py.util import exec_utils + + +def get_generalization(petri_net, aligned_traces): + """ + Gets the generalization from the Petri net and the list of activated transitions + during the replay + + The approach has been suggested by the paper + Buijs, Joos CAM, Boudewijn F. van Dongen, and Wil MP van der Aalst. "Quality dimensions in process discovery: + The importance of fitness, precision, generalization and simplicity." + International Journal of Cooperative Information Systems 23.01 (2014): 1440001. + + A token replay is applied and, for each transition, we can measure the number of occurrences + in the replay. The following formula is applied for generalization + + \sum_{t \in transitions} (math.sqrt(1.0/(n_occ_replay(t))) + 1 - ---------------------------------------------------------- + # transitions + + Parameters + ----------- + petri_net + Petri net + aligned_traces + Result of the token-replay + + Returns + ----------- + generalization + Generalization measure + """ + + trans_occ_map = Counter() + for trace in aligned_traces: + for trans in trace["activated_transitions"]: + trans_occ_map[trans] += 1 + inv_sq_occ_sum = 0.0 + for trans in trans_occ_map: + this_term = 1.0 / sqrt(trans_occ_map[trans]) + inv_sq_occ_sum = inv_sq_occ_sum + this_term + for trans in petri_net.transitions: + if trans not in trans_occ_map: + inv_sq_occ_sum = inv_sq_occ_sum + 1 + generalization = 1.0 + if len(petri_net.transitions) > 0: + generalization = 1.0 - inv_sq_occ_sum / float(len(petri_net.transitions)) + return generalization + + +def apply(log, petri_net, initial_marking, final_marking, parameters=None): + """ + Calculates generalization on the provided log and Petri net. + + The approach has been suggested by the paper + Buijs, Joos CAM, Boudewijn F. van Dongen, and Wil MP van der Aalst. "Quality dimensions in process discovery: + The importance of fitness, precision, generalization and simplicity." + International Journal of Cooperative Information Systems 23.01 (2014): 1440001. + + A token replay is applied and, for each transition, we can measure the number of occurrences + in the replay. The following formula is applied for generalization + + \sum_{t \in transitions} (math.sqrt(1.0/(n_occ_replay(t))) + 1 - ---------------------------------------------------------- + # transitions + + Parameters + ----------- + log + Trace log + petri_net + Petri net + initial_marking + Initial marking + final_marking + Final marking + parameters + Algorithm parameters + + Returns + ----------- + generalization + Generalization measure + """ + if parameters is None: + parameters = {} + activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, pmutil.xes_constants.DEFAULT_NAME_KEY) + + parameters_tr = {Parameters.ACTIVITY_KEY: activity_key} + + aligned_traces = token_replay.apply(log, petri_net, initial_marking, final_marking, parameters=parameters_tr) + + return get_generalization(petri_net, aligned_traces) diff --git a/src/evaluation/precision/__init__.py b/src/evaluation/precision/__init__.py new file mode 100644 index 0000000..57ae427 --- /dev/null +++ b/src/evaluation/precision/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.precision import evaluator, variants diff --git a/src/evaluation/precision/__pycache__/__init__.cpython-310.pyc b/src/evaluation/precision/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b3121b84a71b90a3a92e6be05b94631703bc53f GIT binary patch literal 988 zcmd1j<>g{vU|{gS?wc;c%)sy%#6iYP3=9ko3=9m#5)2FsDGX5zDU2yhIgGhXQB1ka zQOt}CDa^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur0xA_WeDjM^ z6+H9O@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}CG=fYlN=;QL z&QB{TPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5OsfQ&kXfQo znwOGVq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjhs*qT$PyiAs z&PYvBNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN#xP5uCh92U z7lEClkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv!u>|Dh^30qZ zg``x4(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$dr6mffWvLLe zGII-ZGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7xdsO-xcLVu zI4T4<28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0mGp88p=g9n0 zh2o6-(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu=}u2U!81)E zFTX@bp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9i+HJNU4rj{k<lqQzs7v170 zODxJv%quDO(`32D9v`2QpBx__B?48DnV+XuP?VaS35xn6P>v~LW?*1g$xy_~zyKkB zMeBzarxq3K7nkSfr0Kh)7H5~_7wG3^=4Ixk$ERnO=ogn3q!#Hy?a@uGD9Fh#N-fea vE=tx%SfdZOMn672GcU6wK3=b&@)n0pZhlH>PO2RvD5tV8Ffgz%hzI}xXOJ`* literal 0 HcmV?d00001 diff --git a/src/evaluation/precision/__pycache__/evaluator.cpython-310.pyc b/src/evaluation/precision/__pycache__/evaluator.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..325520fd35997d4eb08db90fcce60568eeeacea2 GIT binary patch literal 2647 zcmd1j<>g{vU|?8r+c({ehk@ZSh=YvT7#J8F7#J9emoP9eq%cG=q%fv1<uK+lMKR?v zM=>*k_{=#hxvWuaU^Yt*doD*52bj&8!<ow!#RX=w<#6ZnMDc*x>^ZzqyeW(+965Zs z{89W+F@Y!ncZL+s6s{JA6s}aRX67itRKYBvD4|rrEa52ORH0Pi45lcNRNgGn6z&wB zRQ?p+6uw@T6u}h!6oD4TX2vM76!u^SO`*i&ObiTM3JMAeAsLy)3Tc@+sS26J3I&Np zB?|dz3IV<*0hJ0GzWGI|3Z8js`Bn-UB_##LR{Ht{xh4gbdTE&@dTB+8rFj|oX{kke zDXE%zTwv268bKx&rKTzr=ckpFCl;kzDOBc{DkLZ7DHNrqWEPhcWhRxDq$*^VC?w{k z=;s$H<mRVjrd5JX$ShGP%}YrwQYgttRVYa<$}I*t2*h#s3srDW%}Xsx%uxs^P0Gnk zR`AJ8PR%P$RY)vWC;*8RXQZYmBtZ-UX>kL&A{gol1-Jatyp+U}%=|nZh1ATFjMO59 zvecsD%=|nBW0)mS6Ll2wi@?s&NGt(4zDOaz0Hje<Au+E~At$i}p%>lf2oI(xWafdr zk&$1Js!)=VSOW5Ld1g+ILQ<+iX>n>=X^sxqmZZ`Wg>cUh5C6~*1xLR~g>c89AV<HD zNNa`i%#w`!(h`N#vQ&s!nYjfynW-rX<%vZ_iFqZJ3i)YZBYa(hoIM=<LL8kueLO=V z74nM|+&n}4T!VuZ-28(S92Ei_gF-x=Lwy{B6aqqn0{nwr^%N9>Q$Y?(1)G2;eAB>j zRFtZal3J3OnNtk)b7X$0LUBfZX-<klMq*j2LQ!gRYGzq#ibA47a()5GX<+Z;HW+Mg zVorWuI@qI-bf>4F;F+e7mtUfzP@I~oU;|5f<>lpi>3OAk`9<maIgp4i*0<B+dI`#l znoPHZQ%jQb^V0H*auf5CQ{zkWvs3eK2`A=crsu_@NNF<O;!MuZD@!dZNiEW3yrrI; zk(!(xpPE=)8D9)aBYCOC#qoKmCGnYg@oAZP@wtgb*_nCinvAztT=PnEH5qSlrB<XS z$Cs95<`nyBGTveja}5gi^!G~!IRzOr!iokF1_p*yhA74qhA5^~<|vj_)+n|V#$X0b zrdu3giA9--c_qbun(VhY;^XrYb5rBvZ*j%P=jNxB=788d@$rSFi8&CNA|?g~hFij} zA<q7OZvH{Oj(*Or@ge@+u6{+#U>QdrPj|m~6#115Mf?m55aL&ierR!OQL%n;d45is zzDsIxc1eDLer{%7W?p)HdS;1!acMznk!~5pS-Pne1v&XesYUw5MalZ9Wr;bZ;5?*X zP?VaS3CcY%!Tch<g32OJ1_lOcP-zG%7C9JL7&#chVpXh0dPaJtRm{eE2ERZq0H+N| zd6Jlup09^6S`TitLP273c4B&}Cd)0}lvI##Vo7RBd}iJ)uA<c3{4xleKe@E1C^fGn z9-5tRu|v~oGRO_E_yDol85kIxL5_=KU|=X=SiqRVxR8;Np@t=esg$9JJCmW7rG#k# za}7fcO9@jJ%R=T_h8k9|oKgvE4Py;c4RbSdu|f%3Eo&ZA341MT3C9A?8rFr3Ss*=N z`5KlK=3drd22Ga4&y3JQ);F~zBR@r<Bwr!1pdhDG!8Js|8Ie?>MQ37BVs2_lYEdyb zSLh-Dn4goM4yM6nc2Qz-ssc<TFSP_N6OdX`l&Js{fu>uytY=<kNoHb>0<w5oW?o_r zEQP~Wx`9Md)D@sQ59EvhxDc%L1jS=fW=Te_jzVT$a!zSVW?s4#+)$7nNPL4bj$U$p zUU3O1-RlK7208k=hPVdBJNx?uhdBC$#5+2Nc!qg~M8<o&Mk?sqDL5vVWR_)?R4Qbr zLW*~2>Vz8(%|sY3ft0(@QYtAG908fBDX6Z}Re)+Q)<elQkZc9F1LOfDrC9PASZR?U z0|Nu7Sl~hgnkLsRj`GAJP!=n`#R6j8;>*cTkB5{)pj>o|6`VG1@r3xtyN0>?g~a># zyBD#7axE{2-~$n$^m~gtJ+&kr>VHkPTg*B6>9?5kQcG^JLnHPUFG{QyiGhj*cBl?S zK~N+BGE$0xfk73NCFB?w7`P-km{=GEm^hf37$K15FB>Zhvj7tdBisKXkXlV9KR-?J zTkP@iDf!9q@lmqa@~s}!vSM&yRRk`VWFT>$pOlrFTv7~*erR^p%gIkKVgb2Dl7WFC zN*7gK0Vqj>N@G1xswvik6xQ%^8XQ8mSW@#!bBjPZ{1z8PJ4kU6B)DJ&M`nH=Og1;Q zB(aDCWTiNW0F_xq@KC<R0WHphZ*jr&rlx>Bj}&;|@)Q(;w>WGd1+^Whv@cd+U|?Wj h5D?&Dgg_2P9!3sE0cJK<HbxF64kl2rviuWZ1^``q`bGc% literal 0 HcmV?d00001 diff --git a/src/evaluation/precision/__pycache__/parameters.cpython-310.pyc b/src/evaluation/precision/__pycache__/parameters.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a26c6ebefedde9cfb6659519a1ae09993a423893 GIT binary patch literal 1437 zcmd1j<>g{vU|{e!5KFgbVPJR+;vi!d1_lNP1_p*=8wLi36ox2<6vh;$9L8LxC?-Y_ zn>mL$mnDh?%x1}9&1H*XV`Ok=NMTK3Yhg%XOJ!<ij$%(?4`$HhNIcHOz`&)Tpr8<v zky)&emYI{PkXfuykXTfrke{Xy;A;|4si5JTUzDognU|JtrI1llQc!HAuV0XBQc$Uv zmRX{gR+Lzpmyw^ATBMhfs;S2XHVvW?WMWZjszPyoT1k0gQL2?fWqzqba$=rBQEEzN zaY<2TQfW!5LS~6VVqS`Vevv|MeoAIqCD??_5{1&dl++@Hl8jV^lGLKyVvvJC9CyD^ z1^3jv)S|>3g@DqeoXlhepUmXcyy8@a#A1a4kVtVxYKlS<#2}CsH;^lWp{`JH%P-AK zNi50C&(l#z%`C}CEm9~;Eh^5;&r>jlSpqdtM<Krm>>Q265|HDI6!Hr|8Z{LX^C}f` z5=#(z(S45aV2VO!9@rZh`30#8B^ik&ATO6^=Hw_Or7Dybr>2$W=zwiWDlJh6_YCpy z4-HXp^ovvocMJ-0^b3i!Rw&Ob$;dA)QAjOIg_xC@Tac5PnxasiSX7jlS5m2vp9VI< z*EPu5!_hCq(aF=tGbB<WzevH&GsMp|I9S2WKS;q*A;2*x#M3#{$1zADAT%hzKiE}I zK_NI5<giq*33$Rc4ID>BsR}8nC5f3i#ZW&-=9elIXXKaWq$p%0mZd5br6#9lmZhdB zBq}857l51w_C9Wd!S*KR<maV>Jqk&8dI}1jX$pDyB{~Ylsi_J!u%uUBUaps(SE`p^ zl&+rxiTGlDJ3X$Kpgf|<c#Fj~uQXSa@fK%teqM1&VqQrxh?AI;o?nz%l98JXQihBf zVcAKJfq@~FA&N1DA&M!LIg2HVHH9&XErltHJ%u@nBZVcHL6h|sS3qJ>Vs2_lYEf~N zNJ)NnYF>O%YC%q7Wqes;QD$OZNt8%(PHJLaW?p(cL^>@eKR+c(us9>XJieePKfNfm zxHvv3vB*!8^A<;Zd|qO1YJB`HuK4)e{FKrh5Su4HzOXbg2O?9%%)r19B_H4z<ml@f z;u;k1?C%#G;^-F=@8}%j8Ri)h8Sm{H8O4Lb6AAJ6cJ+%7at-itjEoO+4DxjJ3yBhO z_HlLe^Yn9%he*5m`1`v=2?l%khsOs5`MU?X1_#GGIR>p{DB@&bfDpej^+StOi;DG& z%ky*6^m8-wGSl*l(o>D~Q}fD-!TdZ>JS6ECRF-7q=Nao68t4~imZa(yBqnDkrl%G| z$_o9|vc#NHaIVuYC`wJv1m!pV0(fBR6;u|nGB7ZRfeJQI`NzS)#>m3R!pOl07WdQS zxWyhHpOT*(AAgG_HLo<chzS%hT#%Bev?MbJ5>iSK0Z@GFCFkd*<rn29<|U`<fg=MF nRYjl@6l^NOpAZ(v;#(XxkeILorHo>b5DNp301qP%qX;tq{<Ej} literal 0 HcmV?d00001 diff --git a/src/evaluation/precision/__pycache__/utils.cpython-310.pyc b/src/evaluation/precision/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0becb8d68747d016c028d4a88181f84341f3af3e GIT binary patch literal 4170 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 literal 0 HcmV?d00001 diff --git a/src/evaluation/precision/evaluator.py b/src/evaluation/precision/evaluator.py new file mode 100644 index 0000000..b3b6409 --- /dev/null +++ b/src/evaluation/precision/evaluator.py @@ -0,0 +1,82 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.precision.variants import etconformance_token, align_etconformance +from pm4py.objects.conversion.log import converter as log_conversion +from pm4py.objects.petri_net.utils.check_soundness import check_easy_soundness_net_in_fin_marking +from enum import Enum +from pm4py.util import exec_utils +import deprecation +from pm4py.meta import VERSION +import warnings + + +class Variants(Enum): + ETCONFORMANCE_TOKEN = etconformance_token + ALIGN_ETCONFORMANCE = align_etconformance + + +ETCONFORMANCE_TOKEN = Variants.ETCONFORMANCE_TOKEN +ALIGN_ETCONFORMANCE = Variants.ALIGN_ETCONFORMANCE + +VERSIONS = {ETCONFORMANCE_TOKEN, ALIGN_ETCONFORMANCE} + +@deprecation.deprecated(deprecated_in="2.2.5", removed_in="3.0", + current_version=VERSION, + details="Use the pm4py.algo.evaluation.precision package") +def apply(log, net, marking, final_marking, parameters=None, variant=None): + """ + Method to apply ET Conformance + + Parameters + ----------- + log + Trace log + net + Petri net + marking + Initial marking + final_marking + Final marking + parameters + Parameters of the algorithm, including: + pm4py.util.constants.PARAMETER_CONSTANT_ACTIVITY_KEY -> Activity key + variant + Variant of the algorithm that should be applied: + - Variants.ETCONFORMANCE_TOKEN + - Variants.ALIGN_ETCONFORMANCE + """ + warnings.warn("Use the pm4py.algo.evaluation.precision package") + + if parameters is None: + parameters = {} + + log = log_conversion.apply(log, parameters, log_conversion.TO_EVENT_LOG) + + # execute the following part of code when the variant is not specified by the user + if variant is None: + if not (check_easy_soundness_net_in_fin_marking( + net, + marking, + final_marking)): + # in the case the net is not a easy sound workflow net, we must apply token-based replay + variant = ETCONFORMANCE_TOKEN + else: + # otherwise, use the align-etconformance approach (safer, in the case the model contains duplicates) + variant = ALIGN_ETCONFORMANCE + + return exec_utils.get_variant(variant).apply(log, net, marking, + final_marking, parameters=parameters) diff --git a/src/evaluation/precision/parameters.py b/src/evaluation/precision/parameters.py new file mode 100644 index 0000000..fcdbd4c --- /dev/null +++ b/src/evaluation/precision/parameters.py @@ -0,0 +1,26 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from enum import Enum +from pm4py.util import constants +from pm4py.algo.conformance.tokenreplay import algorithm + + +class Parameters(Enum): + ACTIVITY_KEY = constants.PARAMETER_CONSTANT_ACTIVITY_KEY + TOKEN_REPLAY_VARIANT = "token_replay_variant" + CLEANING_TOKEN_FLOOD = "cleaning_token_flood" + SHOW_PROGRESS_BAR = "show_progress_bar" diff --git a/src/evaluation/precision/utils.py b/src/evaluation/precision/utils.py new file mode 100644 index 0000000..c08cc20 --- /dev/null +++ b/src/evaluation/precision/utils.py @@ -0,0 +1,148 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from collections import Counter +from pm4py.objects.log.obj import EventLog, Event, Trace +from pm4py.util import xes_constants as xes_util, constants +import heapq +from pm4py.objects.petri_net.utils.petri_utils import decorate_places_preset_trans, decorate_transitions_prepostset +from pm4py.objects.petri_net.utils import align_utils as utils +from pm4py.objects.petri_net.utils.incidence_matrix import construct + +def __search(sync_net, ini, fin, stop, cost_function, skip): + decorate_transitions_prepostset(sync_net) + decorate_places_preset_trans(sync_net) + + incidence_matrix = construct(sync_net) + ini_vec, fin_vec, cost_vec = utils.__vectorize_initial_final_cost(incidence_matrix, ini, fin, cost_function) + + closed = set() + + ini_state = utils.SearchTuple(0, 0, 0, ini, None, None, None, True) + open_set = [ini_state] + heapq.heapify(open_set) + visited = 0 + queued = 0 + traversed = 0 + + # return all the prefix markings of the optimal alignments as set + ret_markings = None + # keep track of the optimal cost of an alignment (to trim search when needed) + optimal_cost = None + + while not len(open_set) == 0: + curr = heapq.heappop(open_set) + + current_marking = curr.m + + # trim alignments when we already reached an optimal alignment and the + # current cost is greater than the optimal cost + if optimal_cost is not None and curr.f > optimal_cost: + break + + already_closed = current_marking in closed + if already_closed: + continue + + if stop <= current_marking: + # add the current marking to the set + # of returned markings + if ret_markings is None: + ret_markings = set() + ret_markings.add(current_marking) + # close the marking + closed.add(current_marking) + # set the optimal cost + optimal_cost = curr.f + + continue + + closed.add(current_marking) + visited += 1 + + enabled_trans = set() + for p in current_marking: + for t in p.ass_trans: + if t.sub_marking <= current_marking: + enabled_trans.add(t) + + trans_to_visit_with_cost = [(t, cost_function[t]) for t in enabled_trans if + not (t is None or utils.__is_log_move(t, skip) or ( + utils.__is_model_move(t, skip) and not t.label[1] is None))] + + for t, cost in trans_to_visit_with_cost: + traversed += 1 + new_marking = utils.add_markings(current_marking, t.add_marking) + + if new_marking in closed: + continue + g = curr.g + cost + + queued += 1 + new_f = g + + tp = utils.SearchTuple(new_f, g, 0, new_marking, curr, t, None, True) + heapq.heappush(open_set, tp) + + return ret_markings + + +def get_log_prefixes(log, activity_key=xes_util.DEFAULT_NAME_KEY): + """ + Get log prefixes + + Parameters + ---------- + log + Trace log + activity_key + Activity key (must be provided if different from concept:name) + """ + prefixes = {} + prefix_count = Counter() + for trace in log: + for i in range(1, len(trace)): + red_trace = trace[0:i] + prefix = constants.DEFAULT_VARIANT_SEP.join([x[activity_key] for x in red_trace]) + next_activity = trace[i][activity_key] + if prefix not in prefixes: + prefixes[prefix] = set() + prefixes[prefix].add(next_activity) + prefix_count[prefix] += 1 + return prefixes, prefix_count + + +def form_fake_log(prefixes_keys, activity_key=xes_util.DEFAULT_NAME_KEY): + """ + Form fake log for replay (putting each prefix as separate trace to align) + + Parameters + ---------- + prefixes_keys + Keys of the prefixes (to form a log with a given order) + activity_key + Activity key (must be provided if different from concept:name) + """ + fake_log = EventLog() + for prefix in prefixes_keys: + trace = Trace() + prefix_activities = prefix.split(constants.DEFAULT_VARIANT_SEP) + for activity in prefix_activities: + event = Event() + event[activity_key] = activity + trace.append(event) + fake_log.append(trace) + return fake_log diff --git a/src/evaluation/precision/variants/__init__.py b/src/evaluation/precision/variants/__init__.py new file mode 100644 index 0000000..b89f02b --- /dev/null +++ b/src/evaluation/precision/variants/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.precision.variants import etconformance_token, align_etconformance diff --git a/src/evaluation/precision/variants/__pycache__/__init__.cpython-310.pyc b/src/evaluation/precision/variants/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f9b915c37691a80a420d566ad6dac8b6a5082f1 GIT binary patch literal 1027 zcmd1j<>g{vU|`sG+c({anStRkh=Yuo7#J8F7#J9eB^VeOQW&BbQW#U1au{=&qL^}- zqnH^PQka7oG+7dlGcho5DJUo?gk)qEE2L%Sq$*?<D-<Lal_=z=DFpbM1XL<$_~sX- zDtP9l<y$Fal#~<{Tj}c;<eC&z>ZN6t=%p1UmgZ&Tr==F@rKD==ae+;PXat#9l$xqg zoS#-wo>-J>rBIn)s*s$Rr%;rdl383*l$lgolB$qdqL7%EqMu)+kei>9nN|rlA+toG zG%qE!NTDPnRiPxcD7P5oAP~phFI2%jH7~U&F-IYwG$|)DS-~eWIW@01RUxrhp#UUO zoRONMkOVOZq{R*7ieRWK6x{Ml^HLH^GV}9v6jC!wGE$2a%2JDpGxPHljA52QP1I4y zF9JJ9Be4YJ_#%b;0+2>cg~Yr{g`C6^gkE%?BRrU*keLVeMn--?szOOdVhPC0<(WA- z3Q4I7rNyafr8zoaTarpk6v912Jp4mL6de5`6~Y~Zf*k!qBCQq5GfOh^OG^||%Tgg` zW#$&-WTvJllqVJyCFYe>D&(hujqr61a`tfa3vqPv^zjUdRLCz<aPtiDa}5qwaPtpR za8w9z3<~jd4)t*iQV0kQ3h)nh)l*OiP6atE6>I{Y@J$29QBkTwN@_`BW==8G&yo41 z3dI@ur8y}I8Hr`73Pq{OshMS|DGG@S$@v8!r-8kX+hDN0i8=Xs>0pmS(w&}yf@hjS zUVe#=LUC%Uf(<O`m6w<6rRSCE<rk&v=RhL9Sl>>M>m?}tYckyuPAy5!&r8cM%1z8m zPK__g&rZ#|C7hU(nVuJqBIT#aa*I7aJ|#anKK_<$YFT1VDL7T=6%?f=XM)m$URh#M zW@27RaS<p#6)`g~Fsx)KVq;){5WiCNLyJ?3iuH@j^K;VlT~dp)OY#f!b2IZY^U~wf zGfVV~OAAtqbfKo`rdAZ><QJtD=@%Cz>m%&bhufzQRavYbAD@|*SrQ+wS5SG2!zMRB Ur8FnijuDihSr`}?SQtbE0L2AF?*IS* literal 0 HcmV?d00001 diff --git a/src/evaluation/precision/variants/__pycache__/align_etconformance.cpython-310.pyc b/src/evaluation/precision/variants/__pycache__/align_etconformance.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c0eb16b9bd78d4a434e6edbffd14de1d9db639c GIT binary patch literal 7289 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 ClouTU literal 0 HcmV?d00001 diff --git a/src/evaluation/precision/variants/__pycache__/etconformance_token.cpython-310.pyc b/src/evaluation/precision/variants/__pycache__/etconformance_token.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57bf8524fdc6a84f3d0311b3c316e8a7550c7016 GIT binary patch literal 3513 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 WpkQWU;1S?q<YD4rgh3`IK4AdmK@2+p literal 0 HcmV?d00001 diff --git a/src/evaluation/precision/variants/align_etconformance.py b/src/evaluation/precision/variants/align_etconformance.py new file mode 100644 index 0000000..6412d37 --- /dev/null +++ b/src/evaluation/precision/variants/align_etconformance.py @@ -0,0 +1,274 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from pm4py.objects import log as log_lib +from evaluation.precision import utils as precision_utils +from pm4py.objects.petri_net.utils import align_utils as utils, check_soundness +from pm4py.objects.petri_net.obj import Marking +from pm4py.objects.petri_net.utils.petri_utils import construct_trace_net +from pm4py.objects.petri_net.utils.synchronous_product import construct +from pm4py.statistics.start_activities.log.get import get_start_activities +from pm4py.objects.petri_net.utils.align_utils import get_visible_transitions_eventually_enabled_by_marking +from evaluation.precision.parameters import Parameters +from pm4py.util import exec_utils +from pm4py.util import xes_constants +import pkgutil + + +def apply(log, net, marking, final_marking, parameters=None): + """ + Get Align-ET Conformance precision + + Parameters + ---------- + log + Trace log + net + Petri net + marking + Initial marking + final_marking + Final marking + parameters + Parameters of the algorithm, including: + Parameters.ACTIVITY_KEY -> Activity key + """ + + if parameters is None: + parameters = {} + + debug_level = parameters["debug_level"] if "debug_level" in parameters else 0 + + activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, log_lib.util.xes.DEFAULT_NAME_KEY) + + # default value for precision, when no activated transitions (not even by looking at the initial marking) are found + precision = 1.0 + sum_ee = 0 + sum_at = 0 + unfit = 0 + + if not check_soundness.check_easy_soundness_net_in_fin_marking(net, marking, final_marking): + raise Exception("trying to apply Align-ETConformance on a Petri net that is not a easy sound net!!") + + prefixes, prefix_count = precision_utils.get_log_prefixes(log, activity_key=activity_key) + prefixes_keys = list(prefixes.keys()) + fake_log = precision_utils.form_fake_log(prefixes_keys, activity_key=activity_key) + + align_stop_marking = align_fake_log_stop_marking(fake_log, net, marking, final_marking, parameters=parameters) + all_markings = transform_markings_from_sync_to_original_net(align_stop_marking, net, parameters=parameters) + + for i in range(len(prefixes)): + markings = all_markings[i] + + if markings is not None: + log_transitions = set(prefixes[prefixes_keys[i]]) + activated_transitions_labels = set() + for m in markings: + # add to the set of activated transitions in the model the activated transitions + # for each prefix + activated_transitions_labels = activated_transitions_labels.union( + x.label for x in utils.get_visible_transitions_eventually_enabled_by_marking(net, m) if + x.label is not None) + escaping_edges = activated_transitions_labels.difference(log_transitions) + + sum_at += len(activated_transitions_labels) * prefix_count[prefixes_keys[i]] + sum_ee += len(escaping_edges) * prefix_count[prefixes_keys[i]] + + if debug_level > 1: + print("") + print("prefix=", prefixes_keys[i]) + print("log_transitions=", log_transitions) + print("activated_transitions=", activated_transitions_labels) + print("escaping_edges=", escaping_edges) + else: + unfit += prefix_count[prefixes_keys[i]] + + if debug_level > 0: + print("\n") + print("overall unfit", unfit) + print("overall activated transitions", sum_at) + print("overall escaping edges", sum_ee) + + # fix: also the empty prefix should be counted! + start_activities = set(get_start_activities(log, parameters=parameters)) + trans_en_ini_marking = set([x.label for x in get_visible_transitions_eventually_enabled_by_marking(net, marking)]) + diff = trans_en_ini_marking.difference(start_activities) + sum_at += len(log) * len(trans_en_ini_marking) + sum_ee += len(log) * len(diff) + # end fix + + if sum_at > 0: + precision = 1 - float(sum_ee) / float(sum_at) + + return precision + + +def transform_markings_from_sync_to_original_net(markings0, net, parameters=None): + """ + Transform the markings of the sync net (in which alignment stops) into markings of the original net + (in order to measure the precision) + + Parameters + ------------- + markings0 + Markings on the sync net (expressed as place name with count) + net + Petri net + parameters + Parameters of the algorithm + + Returns + ------------- + markings + Markings of the original model (expressed as place with count) + """ + if parameters is None: + parameters = {} + + places_corr = {p.name: p for p in net.places} + + markings = [] + + for i in range(len(markings0)): + res_list = markings0[i] + + # res_list shall be a list of markings. + # If it is None, then there is no correspondence markings + # in the original Petri net + if res_list is not None: + # saves all the markings reached by the optimal alignment + # as markings of the original net + markings.append([]) + + for j in range(len(res_list)): + res = res_list[j] + + atm = Marking() + for pl, count in res.items(): + if pl[0] == utils.SKIP: + atm[places_corr[pl[1]]] = count + markings[-1].append(atm) + else: + markings.append(None) + + return markings + + +def align_fake_log_stop_marking(fake_log, net, marking, final_marking, parameters=None): + """ + Align the 'fake' log with all the prefixes in order to get the markings in which + the alignment stops + + Parameters + ------------- + fake_log + Fake log + net + Petri net + marking + Marking + final_marking + Final marking + parameters + Parameters of the algorithm + + Returns + ------------- + alignment + For each trace in the log, return the marking in which the alignment stops (expressed as place name with count) + """ + if parameters is None: + parameters = {} + + show_progress_bar = exec_utils.get_param_value(Parameters.SHOW_PROGRESS_BAR, parameters, True) + + align_result = [] + + progress = None + if pkgutil.find_loader("tqdm") and show_progress_bar and len(fake_log) > 1: + from tqdm.auto import tqdm + progress = tqdm(total=len(fake_log), desc="computing precision with alignments, completed variants :: ") + + for i in range(len(fake_log)): + trace = fake_log[i] + sync_net, sync_initial_marking, sync_final_marking = build_sync_net(trace, net, marking, final_marking, + parameters=parameters) + stop_marking = Marking() + for pl, count in sync_final_marking.items(): + if pl.name[1] == utils.SKIP: + stop_marking[pl] = count + cost_function = utils.construct_standard_cost_function(sync_net, utils.SKIP) + + # perform the alignment of the prefix + res = precision_utils.__search(sync_net, sync_initial_marking, sync_final_marking, stop_marking, cost_function, + utils.SKIP) + + if res is not None: + align_result.append([]) + for mark in res: + res2 = {} + for pl in mark: + # transforms the markings for easier correspondence at the end + # (distributed engine friendly!) + res2[(pl.name[0], pl.name[1])] = mark[pl] + + align_result[-1].append(res2) + else: + # if there is no path from the initial marking + # replaying the given prefix, then add None + align_result.append(None) + if progress is not None: + progress.update() + + # gracefully close progress bar + if progress is not None: + progress.close() + del progress + + return align_result + + +def build_sync_net(trace, petri_net, initial_marking, final_marking, parameters=None): + """ + Build the sync product net between the Petri net and the trace prefix + + Parameters + --------------- + trace + Trace prefix + petri_net + Petri net + initial_marking + Initial marking + final_marking + Final marking + parameters + Possible parameters of the algorithm + """ + if parameters is None: + parameters = {} + + activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, xes_constants.DEFAULT_NAME_KEY) + + trace_net, trace_im, trace_fm = construct_trace_net(trace, activity_key=activity_key) + + sync_prod, sync_initial_marking, sync_final_marking = construct(trace_net, trace_im, + trace_fm, petri_net, + initial_marking, + final_marking, + utils.SKIP) + + return sync_prod, sync_initial_marking, sync_final_marking diff --git a/src/evaluation/precision/variants/etconformance_token.py b/src/evaluation/precision/variants/etconformance_token.py new file mode 100644 index 0000000..35ed6a4 --- /dev/null +++ b/src/evaluation/precision/variants/etconformance_token.py @@ -0,0 +1,113 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from pm4py.algo.conformance.tokenreplay.variants import token_replay +from pm4py.algo.conformance.tokenreplay import algorithm as executor + +from pm4py.objects import log as log_lib +from evaluation.precision import utils as precision_utils +from pm4py.statistics.start_activities.log.get import get_start_activities +from pm4py.objects.petri_net.utils.align_utils import get_visible_transitions_eventually_enabled_by_marking +from evaluation.precision.parameters import Parameters +from pm4py.util import exec_utils + +""" +Implementation of the approach described in paper + +Muñoz-Gama, Jorge, and Josep Carmona. "A fresh look at precision in process conformance." International Conference +on Business Process Management. Springer, Berlin, Heidelberg, 2010. + +for measuring precision. + +For each prefix in the log, the reflected tasks are calculated (outgoing attributes from the prefix) +Then, a token replay is done on the prefix in order to get activated transitions +Escaping edges is the set difference between activated transitions and reflected tasks + +Then, precision is calculated by the formula used in the paper + +At the moment, the precision value is different from the one provided by the ProM plug-in, +although the implementation seems to follow the paper concept +""" + + +def apply(log, net, marking, final_marking, parameters=None): + """ + Get ET Conformance precision + + Parameters + ---------- + log + Trace log + net + Petri net + marking + Initial marking + final_marking + Final marking + parameters + Parameters of the algorithm, including: + Parameters.ACTIVITY_KEY -> Activity key + """ + + if parameters is None: + parameters = {} + + cleaning_token_flood = exec_utils.get_param_value(Parameters.CLEANING_TOKEN_FLOOD, parameters, False) + token_replay_variant = exec_utils.get_param_value(Parameters.TOKEN_REPLAY_VARIANT, parameters, + executor.Variants.TOKEN_REPLAY) + activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, log_lib.util.xes.DEFAULT_NAME_KEY) + # default value for precision, when no activated transitions (not even by looking at the initial marking) are found + precision = 1.0 + sum_ee = 0 + sum_at = 0 + + parameters_tr = { + token_replay.Parameters.CONSIDER_REMAINING_IN_FITNESS: False, + token_replay.Parameters.TRY_TO_REACH_FINAL_MARKING_THROUGH_HIDDEN: False, + token_replay.Parameters.STOP_IMMEDIATELY_UNFIT: True, + token_replay.Parameters.WALK_THROUGH_HIDDEN_TRANS: True, + token_replay.Parameters.CLEANING_TOKEN_FLOOD: cleaning_token_flood, + token_replay.Parameters.ACTIVITY_KEY: activity_key + } + + prefixes, prefix_count = precision_utils.get_log_prefixes(log, activity_key=activity_key) + prefixes_keys = list(prefixes.keys()) + fake_log = precision_utils.form_fake_log(prefixes_keys, activity_key=activity_key) + + aligned_traces = executor.apply(fake_log, net, marking, final_marking, variant=token_replay_variant, + parameters=parameters_tr) + + # fix: also the empty prefix should be counted! + start_activities = set(get_start_activities(log, parameters=parameters)) + trans_en_ini_marking = set([x.label for x in get_visible_transitions_eventually_enabled_by_marking(net, marking)]) + diff = trans_en_ini_marking.difference(start_activities) + sum_at += len(log) * len(trans_en_ini_marking) + sum_ee += len(log) * len(diff) + # end fix + + for i in range(len(aligned_traces)): + if aligned_traces[i]["trace_is_fit"]: + log_transitions = set(prefixes[prefixes_keys[i]]) + activated_transitions_labels = set( + [x.label for x in aligned_traces[i]["enabled_transitions_in_marking"] if x.label is not None]) + sum_at += len(activated_transitions_labels) * prefix_count[prefixes_keys[i]] + escaping_edges = activated_transitions_labels.difference(log_transitions) + sum_ee += len(escaping_edges) * prefix_count[prefixes_keys[i]] + + if sum_at > 0: + precision = 1 - float(sum_ee) / float(sum_at) + + return precision diff --git a/src/evaluation/replay_fitness/__init__.py b/src/evaluation/replay_fitness/__init__.py new file mode 100644 index 0000000..5e16070 --- /dev/null +++ b/src/evaluation/replay_fitness/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.replay_fitness import evaluator, variants diff --git a/src/evaluation/replay_fitness/__pycache__/__init__.cpython-310.pyc b/src/evaluation/replay_fitness/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1813c392983bd8d398f50e70da01728aeeb752b GIT binary patch literal 998 zcmd1j<>g{vU|@*6?wc;j%)sy%#6iYP3=9ko3=9m#5)2FsDGX5zDU2yhIgGhXQB1ka zQOt}CDa^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur0xA_WeDjM^ z6+H9O@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}CG=fYlN=;QL z&QB{TPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5OsfQ&kXfQo znwOGVq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjhs*qT$PyiAs z&PYvBNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN#xP5uCh92U z7lEClkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv!u>|Dh^30qZ zg``x4(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$dr6mffWvLLe zGII-ZGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7xdsO-xcLVu zI4T4<28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0mGp88p=g9n0 zh2o6-(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu=}u2U!81)E zFTX@bp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9i+HJNU4rj{k<lqQzs7v170 zODxJv%quDO(`32D9v`2QpBx__B?(oKnV+Xulv<FJSQ(#|S(2ApTwDaoHbu+~3=At7 zidY#KAjGc({m|mnqGJ8x^8B1MeV5eY?2`Nf{oKsF%)IpY^vn|d;?jcDB3-Cux~UZf zIr&AYMf$}>$@&Pp^il27kB`sH%PfhH*DI*J#bJ}1pHiBWYR3r5vn&h@3@i*H0st&* BIBEa@ literal 0 HcmV?d00001 diff --git a/src/evaluation/replay_fitness/__pycache__/evaluator.cpython-310.pyc b/src/evaluation/replay_fitness/__pycache__/evaluator.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6dc8ce5267bfefeb7ed21a3157a8b9563ac7dbf6 GIT binary patch literal 3643 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 literal 0 HcmV?d00001 diff --git a/src/evaluation/replay_fitness/__pycache__/parameters.cpython-310.pyc b/src/evaluation/replay_fitness/__pycache__/parameters.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72bdd18a01e2f6aa7b4afa08548a80f2feeb2408 GIT binary patch literal 1455 zcmd1j<>g{vU|{e!5K9kYVPJR+;vi!d1_lNP1_p*=8wLi36ox2<6vh;$9L8LxC?-Y_ zn>mL$mnDh?%x1}9&1H*XV`Ok=NMTK3Yhg%XOJ!<ij$%(?4`$HhNIcHOz`&)Tpr8<v zky)&emYI{PkXfuykXTfrke{Xy;A;|4si5JTUzDognU|JtrI1llQc!HAuV0XBQc$Uv zmRX{gR+Lzpmyw^ATBMhfs;S2XHVvW?WMWZjszPyoT1k0gQL2?fWqzqba$=rBQEEzN zaY<2TQfW!5LS~6VVqS`Vevv|MeoAIqCD??_5{1&dl++@Hl8jV^lGLKyVvvJC9CyD^ z1^3jv)S|>3g@DqeoXlhepUmXcyy8@a#A1a4kVtVxYKlS<#2}CsH;^lWp{`JH%P-AK zNi50C&(l#z%`C}CEm9~;Eh^5;&r>jlSpqdtM<Krm>>Q265|HDI6!Hr|8Z{LX^C}f` z5=#(z(S45aV2VO!9@rZh`30#8B^ik&ATO6^=Hw_Or7Dybr>2$W=zwiWDlJh6_YCpy z4-HXp^ovvocMJ-0^b3i!Rw&Ob$;dA)QAjOIg_xC@Tac5PnxasiSX7jlS5m2vp9VI< z*EPu5!_hCq(aF=tGbB<WzevH&GsMp|I9S2WKS;q*A;2*x#M3#{$1zADAT%hzKiE}I zK_NI5<giq*33$Rc4ID>BsR}8nC5f3i#ZW&-=9elIXXKaWq$p%0mZd5br6#9lmZhdB zBq}857l51w_C9Wd!S*KR<maV>Jqk&8dI}1jX$pDyB{~Ylsi_J!u%uUBUaps(SE`p^ zl&+rxiTGlDJ3X$Kpgf|<c#Fj~uQXSa@fK%teqM1&VqQrxh?AI;o?nz%l98JXQihBf zVcAK6fq@~FA&N1DA&M!LIg2HVHI+GwEs8ybF^VIFDT*_NIha9{<rY^!Vo_plYDsEQ zag<0&es*eJd{JsaPGV(zSz=LUVqQs<NODeUVqRumdOSorEhj%e#ZQy#7Ds%1USe)) zeEco0`1suXl+qj!n<qZLurx6TB2&c7z`zhCAK)0|=<6Ed8WivB?-v~6=ob?2=p5o1 z<{1(h@9i2H#e>2FMJk5!kdPoxr_c~ruzFrc6s|~!zqhMje2{B^k7Hzfm}8KqqhCmr zh_jEYqo1drdptzi&Bx#0WhFxqCj$e7_?4$0TAW%`tY2K7pOdDao0*rHmS2>fYOJ4{ zS5^$>=Yb+6Nxz`7BqKl1SkKTvzc{lbRkt89IXf{uwHQ)x=%<z?=9Gf7pguJ0(lSf( zQj3fA3*dpPS5R5R%D})N1}f@6B_am{8zUPd3nK?3SlmyO;}&~-d`f<DeEcnz)V$K% zA|_DSaY4$a(vr*^NMI^K1VAZ3FF8LiEx#x?F)uk)4;(oV>x)2TD%eznXCW+*#kV+Y XAdzJUN-M=6Ar=N60UkykMiFKJ+mWmN literal 0 HcmV?d00001 diff --git a/src/evaluation/replay_fitness/evaluator.py b/src/evaluation/replay_fitness/evaluator.py new file mode 100644 index 0000000..160f610 --- /dev/null +++ b/src/evaluation/replay_fitness/evaluator.py @@ -0,0 +1,122 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.replay_fitness.variants import alignment_based, token_replay +from pm4py.algo.conformance import alignments +from pm4py.objects.conversion.log import converter as log_conversion +from pm4py.util import exec_utils +from pm4py.objects.petri_net.utils.check_soundness import check_easy_soundness_net_in_fin_marking +from enum import Enum +import deprecation +from pm4py.meta import VERSION +import warnings + + +class Variants(Enum): + ALIGNMENT_BASED = alignment_based + TOKEN_BASED = token_replay + + +class Parameters(Enum): + ALIGN_VARIANT = "align_variant" + + +ALIGNMENT_BASED = Variants.ALIGNMENT_BASED +TOKEN_BASED = Variants.TOKEN_BASED + +VERSIONS = {ALIGNMENT_BASED, TOKEN_BASED} + + +@deprecation.deprecated(deprecated_in="2.2.5", removed_in="3.0", + current_version=VERSION, + details="Use the pm4py.algo.evaluation.replay_fitness package") +def apply(log, petri_net, initial_marking, final_marking, parameters=None, variant=None): + """ + Apply fitness evaluation starting from an event log and a marked Petri net, + by using one of the replay techniques provided by PM4Py + + Parameters + ----------- + log + Trace log object + petri_net + Petri net + initial_marking + Initial marking + final_marking + Final marking + parameters + Parameters related to the replay algorithm + variant + Chosen variant: + - Variants.ALIGNMENT_BASED + - Variants.TOKEN_BASED + + Returns + ---------- + fitness_eval + Fitness evaluation + """ + warnings.warn("Use the pm4py.algo.evaluation.replay_fitness package") + + if parameters is None: + parameters = {} + + # execute the following part of code when the variant is not specified by the user + if variant is None: + if not ( + check_easy_soundness_net_in_fin_marking(petri_net, initial_marking, + final_marking)): + # in the case the net is not a easy sound workflow net, we must apply token-based replay + variant = TOKEN_BASED + else: + # otherwise, use the align-etconformance approach (safer, in the case the model contains duplicates) + variant = ALIGNMENT_BASED + + if variant == TOKEN_BASED: + # execute the token-based replay variant + return exec_utils.get_variant(variant).apply(log_conversion.apply(log, parameters, log_conversion.TO_EVENT_LOG), + petri_net, + initial_marking, final_marking, parameters=parameters) + else: + # execute the alignments based variant, with the specification of the alignments variant + align_variant = exec_utils.get_param_value(Parameters.ALIGN_VARIANT, parameters, + alignments.algorithm.DEFAULT_VARIANT) + return exec_utils.get_variant(variant).apply(log_conversion.apply(log, parameters, log_conversion.TO_EVENT_LOG), + petri_net, + initial_marking, final_marking, align_variant=align_variant, + parameters=parameters) + + +def evaluate(results, parameters=None, variant=TOKEN_BASED): + """ + Evaluate replay results when the replay algorithm has already been applied + + Parameters + ----------- + results + Results of the replay algorithm + parameters + Possible parameters passed to the evaluation + variant + Indicates which evaluator is called + + Returns + ----------- + fitness_eval + Fitness evaluation + """ + return exec_utils.get_variant(variant).evaluate(results, parameters=parameters) diff --git a/src/evaluation/replay_fitness/parameters.py b/src/evaluation/replay_fitness/parameters.py new file mode 100644 index 0000000..2f07722 --- /dev/null +++ b/src/evaluation/replay_fitness/parameters.py @@ -0,0 +1,26 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from enum import Enum +from pm4py.util import constants +from pm4py.algo.conformance.tokenreplay import algorithm + + +class Parameters(Enum): + ACTIVITY_KEY = constants.PARAMETER_CONSTANT_ACTIVITY_KEY + ATTRIBUTE_KEY = constants.PARAMETER_CONSTANT_ATTRIBUTE_KEY + TOKEN_REPLAY_VARIANT = "token_replay_variant" + CLEANING_TOKEN_FLOOD = "cleaning_token_flood" diff --git a/src/evaluation/replay_fitness/variants/__init__.py b/src/evaluation/replay_fitness/variants/__init__.py new file mode 100644 index 0000000..ab66e85 --- /dev/null +++ b/src/evaluation/replay_fitness/variants/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.replay_fitness.variants import alignment_based, token_replay diff --git a/src/evaluation/replay_fitness/variants/__pycache__/__init__.cpython-310.pyc b/src/evaluation/replay_fitness/variants/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d7342cb4e61bd9e1d41984297e91ab3f8dbebf2 GIT binary patch literal 1026 zcmd1j<>g{vU|{%m+c#Z@nStRkh=Yuo7#J8F7#J9eB^VeOQW&BbQW#U1au{=&qL^}- zqnH^PQka7oG+7dlGcho5DJUo?gk)qEE2L%Sq$*?<D-<Lal_=z=DFpbM1XL<$_~sX- zDtP9l<y$Fal#~<{Tj}c;<eC&z>ZN6t=%p1UmgZ&Tr==F@rKD==ae+;PXat#9l$xqg zoS#-wo>-J>rBIn)s*s$Rr%;rdl383*l$lgolB$qdqL7%EqMu)+kei>9nN|rlA+toG zG%qE!NTDPnRiPxcD7P5oAP~phFI2%jH7~U&F-IYwG$|)DS-~eWIW@01RUxrhp#UUO zoRONMkOVOZq{R*7ieRWK6x{Ml^HLH^GV}9v6jC!wGE$2a%2JDpGxPHljA52QP1I4y zF9JJ9Be4YJ_#%b;0+2>cg~Yr{g`C6^gkE%?BRrU*keLVeMn--?szOOdVhPC0<(WA- z3Q4I7rNyafr8zoaTarpk6v912Jp4mL6de5`6~Y~Zf*k!qBCQq5GfOh^OG^||%Tgg` zW#$&-WTvJllqVJyCFYe>D&(hujqr61a`tfa3vqPv^zjUdRLCz<aPtiDa}5qwaPtpR za8w9z3<~jd4)t*iQV0kQ3h)nh)l*OiP6atE6>I{Y@J$29QBkTwN@_`BW==8G&yo41 z3dI@ur8y}I8Hr`73Pq{OshMS|DGG@S$@v8!r-8kX+hDN0i8=Xs>0pmS(w&}yf@hjS zUVe#=LUC%Uf(<O`m6w<6rRSCE<rk&v=RhL9Sl>>M>m?}tYck#9Pt3_o&&y5CD~V4^ zEKW_i#Z!`>othV4lv<FJSm~$9a*I7aJ|#anKK_<cYFT1VDL5(UL8Rl;GE4GOi;MNj z5{ohu^Gb?~Ksl+1nSp^}B|{M#0|SKkm8l<EoLW?@UtFG_lcw*ITAW>yU!b3xnU|TD z9-p3BqF-EEkXob*HAOeIq97-~D78qxxF}g4VW~c<rTS3i#rpB_nR%Hd@$q^EmA5!- Xa`RJ4b5iXXLHU`5fq{XAK|}xmFNQ?e literal 0 HcmV?d00001 diff --git a/src/evaluation/replay_fitness/variants/__pycache__/alignment_based.cpython-310.pyc b/src/evaluation/replay_fitness/variants/__pycache__/alignment_based.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b95eec483b619446e06ca18fcdd770390cd13eb GIT binary patch literal 4655 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 P1CId6^IXgVT0DvXlR9ej literal 0 HcmV?d00001 diff --git a/src/evaluation/replay_fitness/variants/__pycache__/token_replay.cpython-310.pyc b/src/evaluation/replay_fitness/variants/__pycache__/token_replay.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f885b14e679afa5940eb4685676df516b7a943b GIT binary patch literal 3963 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 RHe_Mo5#V8jKrUuJH2`yEJ|X}B literal 0 HcmV?d00001 diff --git a/src/evaluation/replay_fitness/variants/alignment_based.py b/src/evaluation/replay_fitness/variants/alignment_based.py new file mode 100644 index 0000000..b8db21f --- /dev/null +++ b/src/evaluation/replay_fitness/variants/alignment_based.py @@ -0,0 +1,126 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from pm4py.algo.conformance.alignments.petri_net import algorithm as alignments +from pm4py.algo.conformance.alignments.decomposed import algorithm as decomp_alignments +from evaluation.replay_fitness.parameters import Parameters + + +def evaluate(aligned_traces, parameters=None): + """ + Transforms the alignment result to a simple dictionary + including the percentage of fit traces and the average fitness + + Parameters + ---------- + aligned_traces + Alignments calculated for the traces in the log + parameters + Possible parameters of the evaluation + + Returns + ---------- + dictionary + Containing two keys (percFitTraces and averageFitness) + """ + if parameters is None: + parameters = {} + str(parameters) + no_traces = len([x for x in aligned_traces if x is not None]) + no_fit_traces = 0 + sum_fitness = 0.0 + + for tr in aligned_traces: + if tr is not None: + if tr["fitness"] == 1.0: + no_fit_traces = no_fit_traces + 1 + sum_fitness = sum_fitness + tr["fitness"] + + perc_fit_traces = 0.0 + average_fitness = 0.0 + + if no_traces > 0: + perc_fit_traces = (100.0 * float(no_fit_traces)) / (float(no_traces)) + average_fitness = float(sum_fitness) / float(no_traces) + + return {"percFitTraces": perc_fit_traces, "averageFitness": average_fitness, + "percentage_of_fitting_traces": perc_fit_traces, + "average_trace_fitness": average_fitness} + + +def apply(log, petri_net, initial_marking, final_marking, align_variant=alignments.DEFAULT_VARIANT, parameters=None): + """ + Evaluate fitness based on alignments + + Parameters + ---------------- + log + Event log + petri_net + Petri net + initial_marking + Initial marking + final_marking + Final marking + align_variant + Variants of the alignments to apply + parameters + Parameters of the algorithm + + Returns + --------------- + dictionary + Containing two keys (percFitTraces and averageFitness) + """ + if align_variant == decomp_alignments.Variants.RECOMPOS_MAXIMAL.value: + alignment_result = decomp_alignments.apply(log, petri_net, initial_marking, final_marking, + variant=align_variant, parameters=parameters) + else: + alignment_result = alignments.apply(log, petri_net, initial_marking, final_marking, variant=align_variant, + parameters=parameters) + return evaluate(alignment_result) + + +def apply_trace(trace, petri_net, initial_marking, final_marking, best_worst, activity_key): + """ + Performs the basic alignment search, given a trace, a net and the costs of the \"best of the worst\". + The costs of the best of the worst allows us to deduce the fitness of the trace. + We compute the fitness by means of 1 - alignment costs / best of worst costs (i.e. costs of 0 => fitness 1) + + Parameters + ---------- + trace: :class:`list` input trace, assumed to be a list of events (i.e. the code will use the activity key to + get the attributes) + petri_net: :class:`pm4py.objects.petri.net.PetriNet` the Petri net to use in the alignment + initial_marking: :class:`pm4py.objects.petri.net.Marking` initial marking in the Petri net + final_marking: :class:`pm4py.objects.petri.net.Marking` final marking in the Petri net + best_worst: cost of the best worst alignment of a trace (empty trace aligned to the model) + activity_key: :class:`str` (optional) key to use to identify the activity described by the events + + Returns + ------- + dictionary: `dict` with keys **alignment**, **cost**, **visited_states**, **queued_states** and **traversed_arcs** + """ + alignment = alignments.apply_trace(trace, petri_net, initial_marking, final_marking, + {Parameters.ACTIVITY_KEY: activity_key}) + fixed_costs = alignment['cost'] // alignments.utils.STD_MODEL_LOG_MOVE_COST + if best_worst > 0: + fitness = 1 - (fixed_costs / best_worst) + else: + fitness = 1 + return {'trace': trace, 'alignment': alignment['alignment'], 'cost': fixed_costs, 'fitness': fitness, + 'visited_states': alignment['visited_states'], 'queued_states': alignment['queued_states'], + 'traversed_arcs': alignment['traversed_arcs']} diff --git a/src/evaluation/replay_fitness/variants/token_replay.py b/src/evaluation/replay_fitness/variants/token_replay.py new file mode 100644 index 0000000..84c5692 --- /dev/null +++ b/src/evaluation/replay_fitness/variants/token_replay.py @@ -0,0 +1,100 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from pm4py.algo.conformance.tokenreplay import algorithm as executor +from pm4py.algo.conformance.tokenreplay.variants import token_replay +from evaluation.replay_fitness.parameters import Parameters +from pm4py.util import exec_utils +from pm4py.util.xes_constants import DEFAULT_NAME_KEY + + +def evaluate(aligned_traces, parameters=None): + """ + Gets a dictionary expressing fitness in a synthetic way from the list of boolean values + saying if a trace in the log is fit, and the float values of fitness associated to each trace + + Parameters + ------------ + aligned_traces + Result of the token-based replayer + parameters + Possible parameters of the evaluation + + Returns + ----------- + dictionary + Containing two keys (percFitTraces and averageFitness) + """ + if parameters is None: + parameters = {} + no_traces = len(aligned_traces) + fit_traces = len([x for x in aligned_traces if x["trace_is_fit"]]) + sum_of_fitness = sum([x["trace_fitness"] for x in aligned_traces]) + perc_fit_traces = 0.0 + average_fitness = 0.0 + log_fitness = 0 + total_m = sum([x["missing_tokens"] for x in aligned_traces]) + total_c = sum([x["consumed_tokens"] for x in aligned_traces]) + total_r = sum([x["remaining_tokens"] for x in aligned_traces]) + total_p = sum([x["produced_tokens"] for x in aligned_traces]) + if no_traces > 0: + perc_fit_traces = float(100.0 * fit_traces) / float(no_traces) + average_fitness = float(sum_of_fitness) / float(no_traces) + if total_c > 0 and total_p > 0: + log_fitness = 0.5 * (1 - total_m / total_c) + 0.5 * (1 - total_r / total_p) + return {"perc_fit_traces": perc_fit_traces, "average_trace_fitness": average_fitness, "log_fitness": log_fitness, + "percentage_of_fitting_traces": perc_fit_traces } + + +def apply(log, petri_net, initial_marking, final_marking, parameters=None): + """ + Apply token replay fitness evaluation + + Parameters + ----------- + log + Trace log + petri_net + Petri net + initial_marking + Initial marking + final_marking + Final marking + parameters + Parameters + + Returns + ----------- + dictionary + Containing two keys (percFitTraces and averageFitness) + """ + + if parameters is None: + parameters = {} + activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, DEFAULT_NAME_KEY) + token_replay_variant = exec_utils.get_param_value(Parameters.TOKEN_REPLAY_VARIANT, parameters, + executor.Variants.TOKEN_REPLAY) + cleaning_token_flood = exec_utils.get_param_value(Parameters.CLEANING_TOKEN_FLOOD, parameters, False) + remaining_in_fitness = exec_utils.get_param_value(token_replay.Parameters.CONSIDER_REMAINING_IN_FITNESS, parameters, True) + + parameters_tr = {token_replay.Parameters.ACTIVITY_KEY: activity_key, + token_replay.Parameters.CONSIDER_REMAINING_IN_FITNESS: remaining_in_fitness, + token_replay.Parameters.CLEANING_TOKEN_FLOOD: cleaning_token_flood} + + aligned_traces = executor.apply(log, petri_net, initial_marking, final_marking, variant=token_replay_variant, + parameters=parameters_tr) + + return evaluate(aligned_traces) diff --git a/src/evaluation/simplicity/__init__.py b/src/evaluation/simplicity/__init__.py new file mode 100644 index 0000000..ef0d4ef --- /dev/null +++ b/src/evaluation/simplicity/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.simplicity import evaluator, variants diff --git a/src/evaluation/simplicity/__pycache__/__init__.cpython-310.pyc b/src/evaluation/simplicity/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35c404eb3a5ab3c97869cb2417a7a7ce4f61964a GIT binary patch literal 990 zcmd1j<>g{vU|>kR?wc;k%)sy%#6iYP3=9ko3=9m#5)2FsDGX5zDU2yhIgGhXQB1ka zQOt}CDa^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur0xA_WeDjM^ z6+H9O@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}CG=fYlN=;QL z&QB{TPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5OsfQ&kXfQo znwOGVq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjhs*qT$PyiAs z&PYvBNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN#xP5uCh92U z7lEClkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv!u>|Dh^30qZ zg``x4(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$dr6mffWvLLe zGII-ZGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7xdsO-xcLVu zI4T4<28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0mGp88p=g9n0 zh2o6-(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu=}u2U!81)E zFTX@bp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9i+HJNU4rj{k<lqQzs7v170 zODxJv%quDO(`32D9v`2QpBx__B??uLnV+Xu3=YTS%#z9?P?jlTW?*1g$xy_~zyKkB z#ps6?rxq3K7nkSfr0Kh)7H5~_7wG3^=4Ixk$ERnO=ogn3q!#HyEz(V`D9Fh#N-fea vE=tx%*rSiIM?XG3GcU6wK3=b&@)n0pZhlH>PO2RvD6g_GFfgz%hzI}xIq)?k literal 0 HcmV?d00001 diff --git a/src/evaluation/simplicity/__pycache__/evaluator.cpython-310.pyc b/src/evaluation/simplicity/__pycache__/evaluator.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19b414499b921880733c921dc9134ad1b6649a99 GIT binary patch literal 1728 zcmd1j<>g{vU|`_A<D341m4V?gh=YvT7#J8F7#J9eTNoG^QW&BbQW#U1au{=&qL>&# zY~~#1T$U&nFq<WZHJ2@l4a{cEVUJ=@VN7Am;mGBT;)IHEMRB<^q_C%Ov@oP_q_Q+K zM{%ceXYoYwr1E4iM)9VyXYr+Qrf{Wlrf{e5^s=O|rt+jQrSPWkwJ<g_M)9Yx1~X{# zCmv^FVBk_vP*4cT$ShV!%gjkt$ShVUNGvK*$WK!U@HGjjRM7CvFG^ML%uCC+QphMN zDJZtm*DuI5DX7#-%Pi4LD@rWQ%g9elEz(O#)zsqxn+DMcGO;K%RiQXPt)x7$DAh`# zGQU(IIWbS6C^aRsxTGjEsk9_jA+tmwF)u|wzepiBKP5A*5^O?di9%^!N@|fpNk*zd zNorAUF~~t6j=Nu|f_rLSYEfd2LO^L!PG+)#PiAszUU8~IVzEL2NTfI;HANu_Vh~7+ z8^{&GP**6p<(KBAB$j06=jkY<W|m~67Acga78Pga=P4M&EP<M+qmW+&c8*433CQt9 z3i$;fjhYIHd6fz|i6scV=srhyFhwCV5A2PM{DM@4l8nR>keACdb8-}tQWZ*zQ`1Uw zbilSGm6j-kdxm)UhlVIP`b8>)I|c<g`h`SVE0kxJWaO8YD5RF9Ld?p{Ey&4CO;IRM zEGkOOE2&h-PXim_>l)<j;pi9Q=;Z0+84{_GU!>sX8RF*}9IW8xAEe-@5a1XT;^`df z;~1n65E>NVAMC29pb(r2a#$+Z1U%uJ29BemRE3n(lEloMVyK@Z^Gg+qGxAGwQWP>0 z%Tg7JQj=3N%TiMm5*3p33qVc-dmp#KV0#mD^7GQc9)+YkJp~2NG=;qU5*>x&)Kmo< zSkfymFV{=YE7i*{O4rYUM0~NnogUXqP+rqyyv3DRlpLRunqHKes>yhZ#Wk-q7bKcm zk(wM|T9TPl?5D|ii#^OWDA?2AFBzl>88gB%sQ?25Ln=cQV+unQQz~;5OA2E!gC^50 zj<Cd{%*4EsVn0pRTO9H6d5O8H@$t8~;^TAkQ%Z9{Y@Yb|!qUVXh)fX^0|Ub?(O^&C z03T0h&ydJ?#~|l;7gzTnSJ#ycMcfPw5aL&?erR!OQL%n;d45iszDsIxc1eDLer{%7 zW?p)HdS;1!acMznk!~5pdfn8Df}H%K)FS=jqGbKlvc#NHaEjJ11}BW<%#uo&Xnv7i zL1huB@R0-+J}e9j3>*v`j9`%}RwF$lJ<}>?V?Bdk2BF2^RG<LK+=)5q`FaSG^$;d2 z6eK2RC#I)rvfScLNi8T!O-?LHO^MIUyTw(Mnwwt+Ve==K78RxDmBd3c*DZEvs!3)> z3UCk`<RoX1+vFG+7)lrxFs3jrWMpJ0VOqdk!?chwi=~F4hB1YymnoP*li9C`mw|x+ z6r%-+MTxnoC8<Tlpr|NMECK~p@huh*s|aM~E$;Nxl6Yu{-C|8FD9EYQWWL2&kXlld z8K0M0QX~Y59CoM*L}V24fTBf=fq_8@<SdX`Tr47tMIaGPfm`hH@hSPq@$pe|Sc69o zYF;rU?padvN^^@q{=CHn30Y7oggOuwe3|)qFxlMHlEflbkOPE41SsN)Ko!d^4rrPU zzQqO8mYM=~6Ozlp2@2%=TO2l!6ln)a(#4>lWnthE;9=xp<N#v<W;PZ!CJtsMMkYp< He<I8PrH=2@ literal 0 HcmV?d00001 diff --git a/src/evaluation/simplicity/evaluator.py b/src/evaluation/simplicity/evaluator.py new file mode 100644 index 0000000..439848c --- /dev/null +++ b/src/evaluation/simplicity/evaluator.py @@ -0,0 +1,39 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.simplicity.variants import arc_degree +from enum import Enum +from pm4py.util import exec_utils +import deprecation +from pm4py.meta import VERSION +import warnings + + +class Variants(Enum): + SIMPLICITY_ARC_DEGREE = arc_degree + + +SIMPLICITY_ARC_DEGREE = Variants.SIMPLICITY_ARC_DEGREE + +VERSIONS = {SIMPLICITY_ARC_DEGREE} + + +@deprecation.deprecated(deprecated_in="2.2.5", removed_in="3.0", + current_version=VERSION, + details="Use the pm4py.algo.evaluation.simplicity package") +def apply(petri_net, parameters=None, variant=SIMPLICITY_ARC_DEGREE): + warnings.warn("Use the pm4py.algo.evaluation.simplicity package") + return exec_utils.get_variant(variant).apply(petri_net, parameters=parameters) diff --git a/src/evaluation/simplicity/variants/__init__.py b/src/evaluation/simplicity/variants/__init__.py new file mode 100644 index 0000000..0ca2017 --- /dev/null +++ b/src/evaluation/simplicity/variants/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.simplicity.variants import arc_degree diff --git a/src/evaluation/simplicity/variants/__pycache__/__init__.cpython-310.pyc b/src/evaluation/simplicity/variants/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79bce5a0bf4c6ab34b32a6eb4c80d85591765cd5 GIT binary patch literal 990 zcmd1j<>g{vU|^8F<C`wZ%)sy%#6iYP3=9ko3=9m#A`A=+DGX5zDU2yhIgGhXQA~^s zDa^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur0xA_WeDjM^6+H9O z@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}CG=fYlN=;QL&QB{T zPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5OsfQ&kXfQonwOGV zq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjhs*qT$PyiAs&PYvB zNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN#xP5uCh92U7lECl zkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv!u>|Dh^30qZg``x4 z(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$dr6mffWvLLeGII-Z zGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7xdsO-xcLVuI4T4< z28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0mGp88p=g9n0h2o6- z(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu=}u2U!81)EFTX@b zp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9i!H5qSlB^D*er=+GArKb96GT&m4 zk59=@j*q`3ms*yXQwolEy<%|iC1;jY>XjuHWhUm86c>TA%SwhKRt5$L@heq7v^ce> zSiiVDKPOG!CAB!aB)>pEH#09YFFigzvqZnRv>>%e7ixfRYDGa#eo<<XesNK<KEf`2 sgkAbjrN#R3@tJv<CGqik1(mlrY;yBcN^?@}7(scJg@J*Ag+V|70MvdrJpcdz literal 0 HcmV?d00001 diff --git a/src/evaluation/simplicity/variants/__pycache__/arc_degree.cpython-310.pyc b/src/evaluation/simplicity/variants/__pycache__/arc_degree.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e926c1d78c5f68701dcc52538bc816ede860d607 GIT binary patch literal 2389 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 ZDTD1m;aUt5VqxGB;9=xp6kz6K0RVn@ur2@q literal 0 HcmV?d00001 diff --git a/src/evaluation/simplicity/variants/arc_degree.py b/src/evaluation/simplicity/variants/arc_degree.py new file mode 100644 index 0000000..33819ec --- /dev/null +++ b/src/evaluation/simplicity/variants/arc_degree.py @@ -0,0 +1,70 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from statistics import mean +from enum import Enum +from pm4py.util import exec_utils + + +class Parameters(Enum): + K = "k" + + +def apply(petri_net, parameters=None): + """ + Gets simplicity from a Petri net + + Vázquez-Barreiros, Borja, Manuel Mucientes, and Manuel Lama. "ProDiGen: Mining complete, precise and minimal + structure process models with a genetic algorithm." Information Sciences 294 (2015): 315-333. + + Parameters + ----------- + petri_net + Petri net + parameters + Possible parameters of the algorithm: + - K: defines the value to be substracted in the formula: the lower is the value, + the lower is the simplicity value. k is the baseline arc degree (that is subtracted from the others) + + Returns + ----------- + simplicity + Simplicity measure associated to the Petri net + """ + if parameters is None: + parameters = {} + + # original model: we have plenty of choices there. + # one choice is about taking a model containing the most frequent variant, + # along with a short circuit between the final and the initial marking. + # in that case, the average arc degree of the "original model" is 2 + + # keep the default to 2 + k = exec_utils.get_param_value(Parameters.K, parameters, 2) + + # TODO: verify the real provenence of the approach before! + + all_arc_degrees = [] + for place in petri_net.places: + all_arc_degrees.append(len(place.in_arcs) + len(place.out_arcs)) + for trans in petri_net.transitions: + all_arc_degrees.append(len(trans.in_arcs) + len(trans.out_arcs)) + + mean_degree = mean(all_arc_degrees) if all_arc_degrees else 0.0 + + simplicity = 1.0 / (1.0 + max(mean_degree - k, 0)) + + return simplicity diff --git a/src/evaluation/soundness/__init__.py b/src/evaluation/soundness/__init__.py new file mode 100644 index 0000000..6afd88a --- /dev/null +++ b/src/evaluation/soundness/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness import woflan diff --git a/src/evaluation/soundness/__pycache__/__init__.cpython-310.pyc b/src/evaluation/soundness/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d11df19de565faefadc6ec9de3a49a014ff76b9b GIT binary patch literal 966 zcmd1j<>g{vU|?{%<D1UT%)sy%#6iYP3=9ko3=9m#A`A=+DGX5zDU2yhIgGhXQA~^s zDa^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur0xA_WeDjM^6+H9O z@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}CG=fYlN=;QL&QB{T zPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5OsfQ&kXfQonwOGV zq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjhs*qT$PyiAs&PYvB zNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN#xP5uCh92U7lECl zkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv!u>|Dh^30qZg``x4 z(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$dr6mffWvLLeGII-Z zGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7xdsO-xcLVuI4T4< z28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0mGp88p=g9n0h2o6- z(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu=}u2U!81)EFTX@b zp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9i!H5qTQmFK7BB<A^PGT&m4k59=@ zj*pKLNi9pvDFw&5UNI>8^HPh8i$ED<B|{Ml0|SKk6|EmyoLW?@UtFG_lcw*ITAW>y zU!b3xnU|TD9-p3BqF-EEkXoc$mROXTm{+2kT2YXbUzA#;UtE-|kFY@>Zi9Y&d}dx| eNqoFsLFFwDo80`A(wtN~Mo^vvS<1p7AOHYuVl7Dk literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/__init__.py b/src/evaluation/soundness/woflan/__init__.py new file mode 100644 index 0000000..a77c871 --- /dev/null +++ b/src/evaluation/soundness/woflan/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness.woflan import algorithm, graphs, not_well_handled_pairs, place_invariants diff --git a/src/evaluation/soundness/woflan/__pycache__/__init__.cpython-310.pyc b/src/evaluation/soundness/woflan/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f72bf4fca8b2e01d810ec574bcec811ac06597db GIT binary patch literal 1060 zcmd1j<>g{vU|{gQ<D2fx%)sy%#6iYP3=9ko3=9m#3JeSkDGX5zDU2yhIgGhXQB1ka zQOvn4Q7pNvQLKy%Da^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur z0xA_WeDjM^6+H9O@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}C zG=fYlN=;QL&QB{TPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5 zOsfQ&kXfQonwOGVq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjh zs*qT$PyiAs&PYvBNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN z#xP5uCh92U7lEClkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv! zu>|Dh^30qZg``x4(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$d zr6mffWvLLeGII-ZGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7 zxdsO-xcLVuI4T4<28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0m zGp88p=g9n0h2o6-(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu z=}u2U!81)EFTX@bp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9g1G+AzOCg!B) z7iE@Y<lbUSFG?)PD840@mtPWJo|=;rpOKiCl9QSeUyzttRD4UIASW?7H9j-1EU_pv zF|VZ9Pm}EydwhIKesX+#lyquYVooVIq39KZ(o9}zaj{-`ep*gqUJ)px6)`g~Fch(X z2v!CLhLsFO>>xIX_?4s|TAW%`tY2K7pOdEVl3JWyl3$>oo0*rHmmZ&<S)yNDT98_# z3$<7`wW1&=zbLgxzqlw_A7QUP++KZ%z54O-nR%Hd@$q^EmA5!-a`RJ4b5iXXK{+1e J4i*L(0RX_<O)>xg literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/__pycache__/algorithm.cpython-310.pyc b/src/evaluation/soundness/woflan/__pycache__/algorithm.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ed770e1faef9fdd363c56cf7cf76ea405d925a3 GIT binary patch literal 21598 zcmd1j<>g{vU|{ID<D1@F%)sy%#6iZa3=9ko3=9m#4;UF3QW&BbQW#U1au}l+!8B78 zQwn1Wa}IMZOB4$uM2<Cz6)eXV#SW%9qBv6+Q&@7ia=D{;z-m}>cyqa<_`qz|9R6H^ zC_ylrEk`I<I7%4IX3r7I6^Rl7vpI5vaz&#=!EDYPp<J;jF)*7YM?9B1N&?L0%8|^K zijo4expSm*Wuj!jY@QtHT-hjDFq=0=E>}KE0nFyhk;_$#QUtU4bChzGqm;pHfgF`w z)hJakTQEm0S3OFdk-?oIMJPqMg&{>am8F?EO2eHYMI=SEg&{>0%-3{hND)gBZ(&Fg zZ)S?pO0iFoOp$6~jM7fAPmxZMX<>}gNwH6nO_6J1jM7c9PmxbiXkm=fOR-N;Oi^lK zjM7iBPf<=$X<>{qNU={*O;KxMj518IPf<_NXkm;pO0iGTOwnp#j51EKPti`%X<>{q zNwH7SP0?#%j51BJPti{?Xkm;pOR-NeOfhO<j51HLPccq0X<>}ANM+5kOfgL{OEFKe z=w(iENU=<@YGG_<jN(qQPO)iWh_XttO|ffXh_X&$3TDuBOgzrSz`&)Tpr8<vky)&e zmYI{PkXfuykXTfrke{Xy;A;|4si5JTUzDognU|JtrI1llQc!HAuV0XBQc$UvmRX{g zR+Lzpmyw^ATBMhfs;S2XHVvW?WMWZjszPyoT1k0gQL2?fWqzqba$=rBQEEzNaY<2T zQfW!5LS~6VVqS`Vevv|MeoAIqCD??_5{1&dl++@Hl8jV^lGLKyVvvJC9CyD^1^3jv z)S|>3g@DqeoXlhepUmXcyy8@a#A1a4kVtVxYKlS<#2}CsH;^lWp{`JH%P-AKNi50C z&(l#z%`C}CEm9~;Eh^5;&r>jlSpqdtM<Krm>>Q265|HDI6!Hr|8Z{LX^C}f`5=#(z z(S45aV2VO!9@rZh`30#8B^ik&ATO6^=Hw_Or7Dybr>2$W=zwiWDlJh6_YCpy4-HXp z^ovvocMJ-0^b3i!Rw&Ob$;dA)QAjOIg_xC@Tac5PnxasiSX7jlS5m2vp9VI<*EPu5 z!_hCq(aF=tGbB<WzevH&GsMp|I9S2WKS;q*A;2*x#M3#{$1zADAT%hzKiE}IK_NI5 z<giq*33$Rc4ID>BsR}8nC5f3i#ZW&-=9elIXXKaWq$p%0mZd5br6#9lmZhdBBq}85 z7l51w_C9Wd!S*KR<maV>Jqk&8dI}1jX$pDyB{~Ylsi_J!u%uUBUaps(SE`p^l&+rx ziTGlDJ3X$KpmNMllkpadYhG!tCgUyE#DapHN=?RFe98HF#U(|h$tCe6MX9NpjJHIR zGg6bY<J0ns;!{!+Q{qb!i?fS08E?rV#4~dXit-Cmi{g{>^HMTFkq%NIj8ITqnp9kp zSdt17=1<PgD@!dZiO)?e%FfJ7*JQjUk({4fP+F22UyzfSoEo2*SC&|mnV44sQZ53M zD~<=T^7B$5;;JAXNKa{Aa(-EAQECcAcX51X9zrFu;phrA8E^5I6eZ>rr{x#r#wR5f zXM*hGE=VmY%8V~9$;<(9IRZdjztj><##>ye6{*P(nPgA^B4b8aWgx-8z>vxi#hAhX zssN&xQ<$PyQkbJyQ&@r-G+A$P1tb<F=BAdU78OTH6{VJx7Ujh!7AF?OmuIBr#pmUh z#216I+%3U^qRhOK_>|1V^t}AylFa1dTS8FfNMe4PY_~Y#<MR@8Q{&@ramB~y=BJeA zfY?0o@r9*{IZzp?AlH!4AisFWV8?*?a1U3%ct8J;_+bA~KbKpA0YRRAA@MGrj_!W` z!6BZ`!MB8<%8|rYG89QMFhGc3$@-zisYS*5#pU@qY5Fdy#n~nK1^T&}d6{|X@#&c* z`o*OMsYSZbSkz6eD9Fh#N-feaE=tx<ElbQP1(zuLV4vot78mQ6=cnZ)=IJNqq~{l9 zmSp7W6;u`pGB7ZhfT~vx1_lNW1{OvZMh-@>DDJ@b!5R2bY$>c!>?v$f94YKkoGBbp zTq&GU+$mg9JSp5!yeT|Ud?~z9{3(1<0xA4af++$~LMej544Oi>*!@dO3Q9|gZ?PB0 zC&%ZdmfR9RPo=jcO7k+oi3mf62RYT=l0i>`@kr9Q#6YoMo|=;rpOKiCk^@T6iJ3*k zw^(vg(@H>gprv!DrMD!@Q!~>uN<iw-vd=9^3=3guZ!za4r{Cg2<mg-MMe*q%_uOJG zich~Kn3JCj%AN7W$*FmXMVa}<w`7V^LG@5F$R@B;5|c7>GD|AK1%;ocaFGZofrx?# zF$M;PTkOH{&hdV(A-4nqd>ox!<30Vt9D_U^{X&9oNrd`&y7>qB#$(9v1jjr3`v&;? zxq{Tmg!(!Ahq(s1x<FJ0$0JGK5(6dTa91Cncn?QE7ax!!N6(<(TP!}VZXqB$(DXtr zy(Jm$>gnzg0#c9eFi8vxVQOzN`#QVd;&O3ybcqjf4E7Gb#U2#z4sy>e=Ad}@TY^6R z&fcDW?(xCSu6~X|p8mnNWP)6SLxMb=K{kP%0<CD`LxNmg5!ngCk_LsF3<Cp$6)4Rs z!P7h_-DAfbj71>z7`cHNRuq{rA{9lxDNHHMEsRmjDJ&_hEeugCDQqe1EeuhtDI6)B zEeug?DO@SsEeuiYDLg5>Eeuf{DSRpXEeuhdDFP{iEeugykak`aH>7<R#RF;AMe(Lc zq)4_fMDe9arAW6hMDeG{q{y}~L<ywGrO3B1L<y!Sq$sv9L<yxRr6{*BL<y&;q^Pzq zM2V!RrKq<sM2V(oq-eG<M2V$nrD(S>M2V;9r0BLVL`kIRrRcXXL`kL?q!_j^L`kI> zr5Lv`L`kQZq?ooaM9F|!ZY>N^vMClRmMsiXaw%3R)-4QC@+mecwk-@%3MqCe_ALxi ziYX2$jx7vPN-0h$&Mgd4$|)`>t}P5vDk*L$?kx;aswo~Ro-GVfYAIeR-YpDK>M1_K z44Qto*dS$+TQaDb4=d?GY$gT<24_$l)i5wH)G*dC#52?|)iA^})-cyF#52{f)G)*| zr!dqo#It}&Rxrs1CfUIx2bkmplU!hu8%*+mNnS9?2PXN!qyU%{1d~EwQaFVnm|-QO zpQgwyW>AsDpP83gl9`wTt5a|Bre)?KxJ5#s#3c(N<UoWxh)@6#iXZ}%*ou@vEEN!; z3L?}%ggS`O01=uXLJLG_g9sf422f?jQk<HTR%8zn0d<6m96>CwUGTc12$Zftbri^j z@$r73fCHJx#z+k0Vyxna#DiXZJj8@#Q24{*1H=YlP<(s__0}Nq0ScWWI|c@Zm5fCo zUu!ZIIf0x8VncEp*nME9aTlkSfQmg((VoJ<!0;Jl9v5SkP&v3x(nFHQX?2kc$aJWM zpb%aOu`r4Y(Ud{AR|I4*dVP@&GRK{PfuTwo$$E5E#JK|;$(|qs!JY*ZU?0nXY=%}I zkTw%iiz^GH0qg>0Bp2XPO`J<WDGe3{AU1kX2!I@fR1+5G!W|)w<OpOH#90hV-B638 z1kkHObXO>VT!F3#lrr*j3&5?;0=TQxkz9pO2XU?hWk0AZi$H92he?4PhP`c240n<u zl9RBjCC)LRVgTkC5F6bw93aPl8YE?Kt6@15CPSP>prQtvO>VJ)Q+P4DUC@LIZfn3= zH^uSLv{(tZA2p$3S52Ioz$G8HEF%l@8?5Psk!L^^K@m95s364xPUXb81zZ4!fjkM$ zxnKgEeZg+QNOdsV>Ond%+=5d%ac%+C1F%E^VxvbCJ1BT^lhd2vmh&M6L~e3Aan^w9 z1(-D;Ho7%@AZrjU)>gP}qDZzO<cYHsRI@?TVG+231BW!200%d?et<TOJK#1V*A7rg z;;aK(kpwaaYz3GA+XD{6qWJV~kQ_Jyk;AYkp2#o+RnO2cjN*j1-O)o*6cmyeE&M*X z)iOu{ilK}+kAj;BkZSN2YkE;)K?b@bzy&V8KE*`1dyorUd^(ar-Edgr0K^7iaN~dt z)L3K4VhCmcHw-ix!OfPHj78vv0XQ1K1SBrfQ%gYYfhn-|02^Z!EH<E0IBhD*2Duv4 z7SLoY0=J5CKmuUH!35X_VURJP1`Sf9Z8}I@9;ot{Du=W^uqYzVUT{MUo4tY{BT?Fh zv*5N$AlZtdga|u}TtUWQvymHQBed&07o-U6Ls<Snk|f3!50C-aY!Lw&1g=Gp+A<43 zs=?O5T2t6m5o516$Rup`%79FS)<_uj!eWpvu>G(q3ZH6X+~Er{5t};%KqezMkd}c| zg6)Piq0m(jW1m0B6m0e>fK0=vRgfI85~LGsKP>MO(m{-S0zsx?bB`3rbnNY(H6T4; zm%!RcxYZKlhG39+*xbMYG8fb|S`ShIwh`7eg2@nLeke#cHuIrr4SNG;6G#`>PSmu9 zT{SW8hy<C4%^k8J-@w`%7>Rx>NE_H8u*MKx<;1uo8e}9+mtdqqnA3KEv|+der*dLk z5(_dCn@iY1p3F^7-wjd#wiCI&0JVTXH4(O&2~l4Z#e+0sGo24)I-+5-52Oxk39R9R zDo>105<zBQvkP2aLz^@QL5jc@A(z)sNn&gPb*PK5*#Zu)qWJV9a2t@r3)I>nBD~T- znz5NK3JMR5cFS>)a<Fe;%^R%Bi1Akj$S7<!gUdyHEtFFronS{Gmy7sxptuJ#H~|{p z1GQ<2n;01wG8t+aYZ$T^QW#4ai}*mS8paaFET$}G8-^N&EEcf16o>^DXN8KhfyLEo z7_!*F;_Ogy4zRdU4MP?OSez3o&IJ~?t6|9E0*iA)#d*NuUNsC^JYaENs5l>3JgkNx ziw`W$4;2>xizn4EWC?)91)<_XVDY>fhAbhlxG+>)1T0=v!;mEc78iwzi-E=4Y8bM_ zz~bUiaS5>aq#A}S39z^%R9p%yKCgx$OA0J54HcIGi?6I<$dUnz%Ys8q4lKH*h9OH1 zEGiEcRRD|buVKhi0E;TxFw`&xGiWkZ<%1{XKvTm(;2E7_g+zsv%w*6+SYlD7LQ!f# zQEG8&UI}>G13GOA*OHo7l39|OS`3-(Oe{)ONKVX2E(J}PrYNM87G>t8D}?*I`8fK) zwfJc&fycGL<G)3^puu)l(6mZw5oqwKNDq|8z{5ZKAQpJY#Q?+tjfWH&f>_{j1|tv) z+{rfvu|PfYBG8OW5x5_03K9eNLd`%daKFzS!~%ECEI=$!->JwF!~%C~KqJ>hpbkip zHAoE911PcqjTAz=0nA0ICFq?1E>IrK2T!q_1&s~5FfcH%uyQf-F_MmX7`YfBn1!)Q z5UDi_G2d?`qnoA%+AKv8C?^%Uf!yl_B0w`pMW9j2q5u##2t<T{hzJl71tMZVL>!1n z01=?^_o7q~3p{!U9zX*RVC93vz=KAGAXX8G01w!dfLNs<0z8OP4q{b+2=JIj6^K<0 zBEaJcwIEg<hyZu(8$hgP5CQH?AP7*mqR5tkfdSM;ECzXlgMo{YLyUu&a>&CBHrWlO z=f}ywzyKN#2L)iU7$XBi3Bv-$8is|8wM;3Dwah7uwJa%2wX7*jwQMy^Sxhbru`;#n zCCoMKSuD+rDU8`nMe;T5wQM!aDa_^!waj1{R;Y|l4SOwX4GUO?rBI-THHE2^u_&^J z4N5boFlRFrMS@g-=n}RC>?tfMEDM=xIck`)I2Lf$a4ckGWGLZUz+J;y!%@SukQvJ3 zs9}TfYM6o<z~(X(rPeSOC71A|ur@O?GJqC)fbC%@Dy(5F$}izbVS|f<>}4sctzj&x zF5yXGhl{hMFlVzAb=EKzwU_XuaKOYtW@&OJDlkG9j<{tOgJ$S6^Rhv!I24NWON)|I z6~NsV1yIJ%OaT=JX_=`hdI}*Ksd+jIi3;EuYVe%8LRwLNE=*HNzCtm`oXlc{#FUiO z6a|n#Mt)I=ZgOT(a%pBsY6`fx04?cA&jhW60MBbH<fWGAfz}FumU4jCujJ(`l;&j? zmV%a^fSr?=mjW>w>@FRJ#5{%6qN4mFP}Nqf0GWe^8g5kpn#=~7X{7+3CkHVh<ydC1 zLV7-^=qkwvt=uREIZXl8X;u)ktibLp0@e3^FG1ujDX{8zxaxRB7*vUeWTY0s91acv zuru|lBvB-gLRPOz1um5fTAfppk(j5Dp9l9a)Bq)1szAXEQKQLwiv_ew;}%CsYH9(9 zRTK^?e3^4n^KP+c=EWx#B^Te~$S*Acvx@jZl3XE0iFw7~7`(-e@JaD4=ERhgB0i83 z{=}3NkdAmz49Az`-(m&N{%dlA2PqJ<H@5`x@<HnZijtvAC5mtHfkZO%vLWs8;#(}B zFuBDBNm{p9!S24rToP}HUhs;e1adq$b%4e^zJW?%P(PcC9X9O<Dv3dp?|k4Xc%}vg zMjj>}Mm{Dmp9v&`8?*c^0*z~+lpLT23=3=)Z3Y7acyU|{LoHJYLk+_M#)S+eOwCM1 z&Lzw>3=3EmGBh)$FlRFtON05WFn*B=sPR&yRKlLZ0?u!&!Qgo~kPe6`ATvNSa|>7( zLd^w>Fl4b6*_N<Fa&-wu4MPoM3VSc8GyutXm9RteFKaM^CTF58Ebn?k))J*<78RE$ z6qlqH=qQw@Du9=`C}gIA7wu%`6_l2M)`USaTylO<QEGCDo<ca1y1Z1dio`s~!X0R~ z@k=d1YbAg)1t=>*%}%YzEG_{r5&|2lkeXLgRH*=3WCY6L(2*o?sRAClQGhP4(F2<U z&8LtWF+M*jD>b>qO2HpYL$YgfeqKppW?r#EVor`iUTSh`aWSanl9>lu)&Op{Kn#Lr zS%rY2{Nz;7lB&#-3~*>D7=c0|KigU%HK#aL!7VYT7_vkPBj!P|59&h~gW4)J3@!|@ zT(wL!Of`&I3}p;OJfP);!3>2=;IflBQ2-YHpw^W$XeClH$Rd!ZL0(Kvffk?O@F|9^ z9)lD{pio5wA*9sE&&#O<Xa3CiTu5-m=YqWkG9MaFAcHgW(ybJHAuE(XEjGA`=!)}F zORS)AiP_G>q6-#+PWkycsfl@d3L!<MsUR;wZ3VCSgZRc85{w|py!;YKRDzrt;-<+6 zE_gJVZ?QwQK}w3Ls0{;9o8Xo>IE}_<rokqMK?|hvQcH9g85oK|bEX{ZT#RgtO#fM! zs?<RVJKj*wCMQ2RF{jv04~O0&(AayG0ge(AmKyVm6i^eRTNRHdERW=8>#bzGB>@Qz zq$N<LdEjM1Ri+^ssYtmavlz6z4I0q!V1;LzL`d6OPm}W&S7vc$UU5lcUUF&?xE%&6 zg^M~tA%kd<34yp1KxHo**vm$m%(r+^@<-8Zkdm1o0=1EK3!=kN1r&au1sEJ0d^`$_ zY>XU?EQ~^oLSPmTm}Frrn$N(%fRY73O(xJ_G`QK+hE({~Ff3pwVXR?TzyvC;vY1nt zdznCu7)T+Q!jjEYB#{Ct<iLeu7Ar(Ni_M9lmbr$hhPj!smZgL}izAD30ap#nLdIgV z8ip+9B9jtsNKpsTo5fsYTfz-#x|HyM3qKA};it)&xCoZj!0TIcQ%f@PQy>K#ILjp} zK#Da;;KRx>h4TEO?6jQxa!}Q;r{D%E9(BN_Aw(T052mM<6f1zM8??rWMsj{$UTQL^ zJO!<y1?AP$JXnqfE#m~We-cY|5w)hCLU1Z*ks($qpbHCAL5oOB@)Z(6P69cvLIFJg z0LegZpoI}Rl{%nxrJ2PFC8@<FAPtZbyePG}G^YgAYys;4mF0;F#U(}gdFeTo3Q*Uk zrYL~c9)jnDA!b6$4V3g_r4SAt#8&_nw<RFGpaK?TG^mKhS+IhN1zT8=01xDFH(kHf z5^&LjG8_O(Odt#{PeI8U)COP3P|H}tR0FCv8M9f6+-evWFfU}NWvXFZz*57M#R^VT zOj&F$46#DB%wSRGEOw|k;{uKvFb}MfiIJgDyoM=@6Reg6td=E<3rQ_g7HbVe1f*6M zVQ(=Pv~zqF>}+tkl~Jq!O;+F-1=TH}u5u2jYRgZ9l;p+WQW-1{ZZm<(bS#kxHxwH7 z#U+_JIq`X^sVVU#`SB^4#o&QYE6^MhsQ3oAgV2qJRDGa^cV2#pLRx7~P9>r-4Nd?E zTOj2Wq^%0_OA)B0s>yweDX-!dyGy1!c!CR5$lPKs$S=6X0cz3Z<)@_HVgW6|Eb0Q4 zBcSOdh)ilqdTJ49rtcP8Nn%lYYKbOmlo)m|++uXU#h#jznwy$ea*GYp#=ON2ZDOJ~ z>eRsD1`1b1gDyTZuOvUdC^fw_C$T6V?5zM$(FIzl%E89N$N}y%^D&Bmd)y+7JU>`C ztIQy!BBCN7&ft}d;Kr(-Ta_Xr`-8eD;KDl*R9L{viYgg2C7`Sdmn{O#OKI{#dgh=O zB%~|{m)qd(G^pWS)C&q$&@|62F8JEf;#*ws>{)zEG_yD!mVrUblR?EzYDy6(uRsfO zV@=kgc_4GZ1^#MKoxqKhgo-jja;Q~6(Od=w26s>(g4!?~oIFA-j2!=Ym{=G^7&#dE zpk+TB6DVlGYEfzc&=?-P1^^9GBbWTmjNt6auz<OQrG^32bOr4cf!73VCG4;oVgbiO zhGtj|!CAsp!?1uGRC9pp4~TdPPYuHYUTA}v30x5HrLaKE&*Cp~C=q~kc}oPr^#B{F z9;ji+;x7s+5lCTQAOz99kP+0pu3;<+OW_D+C=o`e)#UWM#SWePTFH2ewYa1*Cl#DA zLaNkV%TkLfAz2z!jDoWxc&C9vu`VKh^uY04WeuwA)ADn2^2<RbBrLZUrGnE9lHSBT zWbIZ8)wOzVnj%Hup&QVEL=iY`fJQ%xctP<79&`Z@=YU!|Md0BV@W2i?NESR01In~T z;L#ZHI1f05fs+S2I1d;a++qV|sKgRzdNEnagp^*;62=CQv7m$j8YB~96k}vz6kufe z&%y*E1(-k$El{FhV*;;!`_IC}!pOr|1k#0(L_p;_xRC%F2Et4t;NB3VcLW+sQLkah zVl9#=VM}2GM;F5a_7rAtR5KPSrZ5LHKzlkY&`4%2GA?0DVO_upX+}UJ8KjF1qzhTK zCc7UvQmaHzGHfZNq2>mOL3zLY5(UuWWnJjvWrYIJ;$_gV1~`^NG`WgEgK4+qK#>nx zw~u|c4ibUjH~|li!K0%H6g$u;FfG~!3TIH>DME{Y?F<YI?Vtz%6>ma}0^q#J#R%%k z@-VV734w<Qi$JO{0vw(vLGud80S>7^p?M0LE1?5bir`MDbO}2o*rCD33=L$qB3)4T z6dcH`3mG7R49clBjG)#XXhQ%rXiXqZX-Hk90H33R<R4HK2pXgTB|`<oib81KvB8;l z5XNNYVbKqY7ftRW(7@&`RamUR^*{y`Kz#*-jgb5U&SKyg0uNduVhB9-2Z|YJG?^7) zi=kZ%3=GpjF$8Kmg9Z-3eUJYF%%B!DD2^amgNLaIq!M>5p=S$}auQy8)-Wt!gO!uu zvJ+Hlf(8_rigaoivN(&>O1K~c2qoO$l8qG_cbr8QC0r?N3qWNk2R!b05o$Eq{cdrT zLzmtcZ3ZPsP@4eUaVbg#C42?&*cz<#f);9-c?vL1m~m-KTwG!?034ZoMc@HyM9Bvp zss_(VR)XAzh&k{eGwzr(#~O2c7#J9qfnp9+>w$_k4oJTdTik)vV#FOgY|v*Ccr^}k ze+Se=g%m~2;9{$UwS=vPVF5d+_JZa$L(r^LkwOV4B(H(yqQJwz+R&H-7j2+AuZA%N z%m%BsFX2pKUBHVl4U!9M7>k@!*n$~K_`op=Ru3=e#QgFVV0)SrlJlWkn)E^-MV*Z& zbi0#6cz_FdpjH7<b%Am)XmA2aJ8Vl7D2nvlH2I4_a|gGiK-*<<6LaFh=>xWv0^B@< zl!5u67z1};!Fdf-Iu(JZ1rX5&nmK^xHH#wj+-He34ncFr+dy#$niT@|RoVWFFoAL& zXzGNA2~;t0F|xrbCXi~3ScDJVfhLS#Lw3!KMLeL!e+^>_BPa(Uvo)Fgz#*r}Tm(u1 z5MO|ui>-V(z`($8668XVk9in*7<rhAKw=o~fLD;9f&?`4S;SMqn8LV#2~;(L+EJj2 z5HgJ9SEY%Xvx-v-OH=cbQ;Wg*v@|aXwCO4}1r$YYnk?W(EVzvbu4ryaBT7f8!g#m= zSVI{!aee{j2R23_Mi#~*kQjy^K;Z$d<X1p*#6pG?hHR!HQP2=3C=3z#0@50U<PK1& z1}@E*!7V-pcnQZ*!?1u8lxM&bEN-AYQe;rV4a+eLcos5%^NdXiq$C5U`x?d~2hivM za$W%I3<8ZEEZ~Qj3CTAgb?|&5fl_OM7APoycLzdqidz+E76U1BfF;2p4pIvWY)t`3 zY5--4qWz#04=$q+X&*er28wCO2o9*wM~WSA27tEH3~xahM&L-p8ZV$kcMTLTprp&g z$O3MwGyP}zFTo6HDS*-&q)`THE3hzva~1Ab1GQ){TMDS<*aB!dwve$%9$X^HmT;zk z+ti@6S;JVQ3~rurA(ca{epSMbIXRHP1cz5nW?8CU2&Ap0$z7BT>ZJ%3r6wk4;4L{o zU85rKawPCHBD~~)1(h+@P&&-O!0;LrN}vs0ER14|EdTkyWdsW&By5U6DsYDiXw@VB zPyshq#Y)&9;ZVa^Bn1i!P>P1Pj9C0&Q#zm`H7`F;7Ze7NAkl*c1P3@EijHDQH*l{O z9RtZ?D`-G#Pkw;BDFB*t2743KWrH*oi$Jm%UIZmXaCQaFcf-60Dry)(i!GRngiDxH zK%*?+jvF+?fHsda`&H@TDu8lQ6T!20pezAevr-Jo7kX}*Y(=2?<Xcj(vL`P;FCG*G zNJS6Alb}{t5qN?an@>SOz{JGBP|U%=zyRu=f(s;&XceoGo{^qW6|1qHfu6xHn-tJe zh2%ugkQi*AcYYdl`GGZP|Fr^SH#?}et(TaWm{VDtS*!;c_|jy$#Z{D=n_rfi5}%oO ziygZ0IT=)6z*atSGB7ZJ%5`ucf|`$@0jm_o5~hVrj0`o*DNLmdMLe0{>EHz{C9GNC z>EVUUwJasfU_N^da|+8s=2})Lp93^~ypXw;t%MVle`=U&SW{SgS!&r!xN6vISW?(} zK~utPMOHN&HSD1NaT;i%ha;FllReQNHs0w7+WQP1vxE-W1%qk>(8g5A@F!@DKEE_Q zL!kt;@xK5x^-)}`qmYuBmX=zSng<%bOf7)aB%mF_r6s8;DC2^VDFV<wVg=A}D3)mg z(Afdtu_i0XjA~|lu9X687#%vo4;sdTjKKPV=5XP26OdUP*a-y4rlf&PK^cK3#SG|( z7kGX`2RsJ>nYscG&%>rB@<H>+<(b8)5DyofU|?YIa|<Z~FS-N|;qctzC{HZP%gjqJ zzQqD!-4b$vCbG=@yl{wA5hx4Z;s>n-0S7=lWHk$D3viJ*DF1?bb48LM7HEkBcvQUz zv<d<`MH<DN8J~NLIW0c-78lq6@CIwh5I#7Q2!phOMtP#RAietdqEz(#CPko`DNax} z0re@_xR@AO{&O&aI`9IF0*nGoJd7-03|5CS3=0}efY;=pzA~(60FA|9^d7<E`=F7X zW~N%k6vkR6P;I`DA&cFGAy%}OxrCzxG=dNAw`8%`FiSF|FiA4hFfL?jW-KzSVXkFj zWXNMIlu2i(1@%SQpgIL=nM=6Ay%n~F3^mN4MGnQDH7pBwYryU4Ldz2FEWRxM1p*M; z7BVgnT*y$%S|ZfUP|KFdP|IG!wm=v%WRT8K!=A!y!vL1!NN1?!ED@<;15d-U6m73z zTOhiSp_Z$Lb%9t7*Fr`YhFFtY<{IW2_A<tz)iumD9AKKghD(xRf%rm(8ul8{S~Rdv z7VtuBUm#J#Rl~fHDFx&^7D)!skV6e~4NDng(Grk5pl&X&;atEA(hpHlSYE@iKp12v zBLl)k#d$THpq_MMJ?wmr@KlBJ#Jm#Fk_c$w3C`T`CL1Uhl)xwDixtW<GLthDFv?*_ zk`BpNNXZB1eDH)XWd0XsY@!0V4u<T5hRiZ3XcXiZ7iT8rr0Rf{Q6#5=riruj^2;Hk z&7i&k$}kGFK?yn~BL(D4Q1=j=iJ`L@pm_@L_;+zZVjd_PDS*yp&{J?sD@iRXPb^9) z2D=O9B9Qq7i6t56z5<O!7eiK%K)ef5h*~xzC+6klLnaxLQXw7#EkyClFG;lm&0&M; z;o{W9qT~$F7#(ct89c9(3Yrs#HmRXb1NlV(5?V#6DGDI_b-=UB#ihxh>E-+)NQs%4 zQ=AVv;{+Popw%5|`9-N<Po?C8$N#_wWt8ND=1__=q34|BLexXcOGpKTe`XuxVIpR> zeV~&Ou+)N<SacLXjV=YaEO>AmG=Tw*GruCxat2Tdz;KHTk;F75Aq&L74Ha;g4&1;6 z)y75O1!AD}UAI_4VFX$hl9O|bDX-ubYbxjjhaylL?iNQzVsShuUT?7_78IoBr4+3N zH33;bY2g;TYf5f@UP|#Tp0v!olz7mwE4ih)x0s7lONzjIdcYmXMIa5Fd7uG{r2L}d zqIM9U7gXeeW+UQL5=#;_xgjH)=NT9nqNIvTLGc8d7KsNPDwCO)SdtnKKE~q~3&^Qa zoS;KDK&;|hys*>>J`CU%Yhh_>Y3eOjkc*3>_@Py1YDzp<HAEv=?iOPi`UoCqnXU?` z6b9|gk>CO~Rrna?z$17}|5#Y~7(rurObud8Y>X_7Tuh)b2+$xZXxtrCPAh>&-T9c9 z8n_sFzOwKXfy_fGtw9w6ytKXqI=hK6g{cL+juw4H0%^<-xzuJ#2hF)cOK2B{Sm9ch z5{@j;P%5}Iu3?d60F`WI3`NE@%#hhwMutKK(B@>&XjBbTGgB>VIzug}X8_jW!Vs%n z%aX!U%U;9m!Vt?_%Yjsuv(#`bWG)I#VXb8^^r&IQA)gACkEmfU%B^98+vx-<y&+{T zxPFlZ+rm`?n*8Ih<wmiEdm(dCR|;z_7r1_@uHh=0R0FDsz%vGgr6s)047EHpJPY_z zz(d5D47I%J3^lwdY&Hz(47GeE{53qyj45o{EJe?2coqmOWT*x89N88!ED)^WU&vT1 zP$S^N5UW)ySi@VxFUhb#Xdy!lZ;hZ0n3QB#0Gh!7sTQu`uMu3xl)?@v^=kxcILjD{ zZrAWF-~y?Kh!$=GxuixARC5$=L)cxsvW731L6ak~AJ)19t-gUAF9R-;U^%xKt=Lb@ zQ^?Fq1GTwQ6%upvOY^{|I+f&Sr{)#wDWKH?u+kP({=?41P(U8YffUl<G6B@?2W3;x z<X3jFLUCqpW=>)esB!|8NZ<h<9fg$C;)2W)a9sg9fCo~_flgvd&PXguOfE?+$^;(* z16vRb(h5=tKB*@&KM!AN2yN7Y=An~IAhi(Ma-USt0z#xIX{c#M;Kj^V3a}y(K1>2H z{)%xHhiJtnI3!TIQ=nb}s3!oPx(2N#P6tg<Gh~4l1c}r#moSzvEno(X>VR87jPMqa zWDU~-7HIo`6|{&GG`a*Tbv0RwE`eHkmqEl8Na0qL3OaTw9#W8A2Q5Wrfz0QDO1N7b z(3W*^(LInJP^%A87=hQ#=s?!KfX+RFsf*76P1Y90gH{m6gDX$inwlsk28LoDa6giZ zQHqg^k*R@&u__RnPU7Jqhv>W^rdCO@2VB2|xPfb!qKhC8i4=iX9D^5IW2;)g3x>e~ z0}d1B%)GQBP)LC5m7;4PkKYCnXcYw)qM8QR6h(JImI*=Xho>M7pdgGAgyk7nKtRGW ziXR$Mu#k)5OD{@I&4a6o;sH&Nm*mHTg~7gx;sI4Ba1C5g1x2YTQCy&E1FG}^$nJ-r z3W^6@gMg;{i&9gH9)ZN4gT$GOOOuOGBeUob0|P@4D2_mzHaIv~*g^AgLQJ3mAPI16 zaWI0K`z(+(jo<-0rUniu%>q^dQVW`{098mVj7<Mnm_f13#sr#N0xkUk&7|`&axe-o z%7IB9CZ6vsTwt4vT0o8i?+LZJ#U3A@lAjzO4++-OpoGejnpc`z1ezDV#SQC$XXfYK z;>b%a0nLh6fFtV`YhGz?L1hsr9-|@<=P2ocdt{*QDzqc62QE~K^>B|c<B<aQZoorv zQ65Ct0dD`Ij6vyP3V}yxqT+}$4py9E85h^XwM_=>-y%?nQUuBeQCdXU1Uld=v!t>J zG^A4mDu^Is)1Wq4lqWv@*yk<vunQOM1f@t@eCC1Yg^+fw>Y)o1fd);YEJ!i~X(wh8 zc;*|_MU65c$qZNsfr_;v@FWswV(FGFWCRFOofPZA7Y^%z)E7e*G)v=Blb@6YT@?*c z0n!8R{DTknDguq<gSQ%h7Y#$Y`JlBH;2Co8tSxv_un07}R0Nuw0M877ht|O(wcvqC z@X#H2*bj8RRY6fIcsvW-4us@JMA-mlflGT1n_TeGX?CDFykgLBGz$ZV0BEkBgNXyQ zMuwS(nTH9q?in;h%)`XP45q;=p+PJGW&t)nVTO7xEiOGSWiD+lQ7!>4el7tnVJ>zq WHZD;vUM?;Uc2F6^!^9!UqXz&KN&oNw literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/algorithm.py b/src/evaluation/soundness/woflan/algorithm.py new file mode 100644 index 0000000..88097d4 --- /dev/null +++ b/src/evaluation/soundness/woflan/algorithm.py @@ -0,0 +1,654 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +import copy +import warnings +from enum import Enum + +import deprecation +import networkx as nx +import numpy as np + +# Minimal Coverability Graph +from evaluation.soundness.woflan.graphs.minimal_coverability_graph.minimal_coverability_graph import \ + apply as minimal_coverability_graph +# reachability Graph Creation +from evaluation.soundness.woflan.graphs.reachability_graph.reachability_graph import apply as reachability_graph +# Restricted coverability graph +from evaluation.soundness.woflan.graphs.restricted_coverability_graph.restricted_coverability_graph import \ + construct_tree as restricted_coverability_tree +from evaluation.soundness.woflan.graphs.utility import check_for_dead_tasks +from evaluation.soundness.woflan.graphs.utility import check_for_improper_conditions +from evaluation.soundness.woflan.graphs.utility import check_for_substates +from evaluation.soundness.woflan.graphs.utility import convert_marking +# Importing to discover not-well handled pairs +from evaluation.soundness.woflan.not_well_handled_pairs.not_well_handled_pairs import \ + apply as compute_not_well_handled_pairs +# Importing for place invariants related stuff (s-components, uniform and weighted place invariants) +from evaluation.soundness.woflan.place_invariants.place_invariants import compute_place_invariants +from evaluation.soundness.woflan.place_invariants.s_component import compute_s_components +from evaluation.soundness.woflan.place_invariants.s_component import compute_uncovered_places_in_component +from evaluation.soundness.woflan.place_invariants.utility import \ + compute_uncovered_places as compute_uncovered_place_in_invariants +from evaluation.soundness.woflan.place_invariants.utility import transform_basis +from pm4py.objects.petri_net.utils import petri_utils +from pm4py.objects.petri_net.obj import PetriNet +from pm4py.util import exec_utils + + +class Parameters(Enum): + RETURN_ASAP_WHEN_NOT_SOUND = "return_asap_when_not_sound" + PRINT_DIAGNOSTICS = "print_diagnostics" + RETURN_DIAGNOSTICS = "return_diagnostics" + + +class Outputs(Enum): + S_C_NET = "s_c_net" + PLACE_INVARIANTS = "place_invariants" + UNIFORM_PLACE_INVARIANTS = "uniform_place_invariants" + S_COMPONENTS = "s_components" + UNCOVERED_PLACES_S_COMPONENT = "uncovered_places_s_component" + NOT_WELL_HANDLED_PAIRS = "not_well_handled_pairs" + LEFT = "left" + UNCOVERED_PLACES_UNIFORM = "uncovered_places_uniform" + WEIGHTED_PLACE_INVARIANTS = "weighted_place_invariants" + UNCOVERED_PLACES_WEIGHTED = "uncovered_places_weighted" + MCG = "mcg" + DEAD_TASKS = "dead_tasks" + R_G_S_C = "r_g_s_c" + R_G = "r_g" + LOCKING_SCENARIOS = "locking_scenarios" + RESTRICTED_COVERABILITY_TREE = "restricted_coverability_tree" + + +class woflan: + def __init__(self, net, initial_marking, final_marking, print_diagnostics=False): + self.net = net + self.initial_marking = initial_marking + self.final_marking = final_marking + self.print_diagnostics = print_diagnostics + self.s_c_net = None + self.place_invariants = None + self.uniform_place_invariants = None + self.s_components = None + self.uncovered_places_s_component = None + self.not_well_handled_pairs = None + self.left = None + self.uncovered_places_uniform = None + self.weighted_place_invariants = None + self.uncovered_places_weighted = None + self.mcg = None + self.dead_tasks = None + self.r_g_s_c = None + self.r_g = None + self.locking_scenarios = None + self.restricted_coverability_tree = None + + def set_s_c_net(self, s_c_net): + self.s_c_net = s_c_net + + def set_place_invariants(self, invariants): + self.place_invariants = invariants + + def set_uniform_place_invariants(self, invariants): + self.uniform_place_invariants = invariants + + def set_s_components(self, s_components): + self.s_components = s_components + + def set_uncovered_places_s_component(self, uncovered_places): + self.uncovered_places_s_component = uncovered_places + + def set_not_well_handled_pairs(self, not_well_handled_pairs): + self.not_well_handled_pairs = not_well_handled_pairs + + def set_left(self, left): + self.left = left + + def set_uncovered_places_uniform(self, places): + self.uncovered_places_uniform = places + + def set_weighted_place_invariants(self, invariants): + self.weighted_place_invariants = invariants + + def set_uncovered_places_weighted(self, places): + self.uncovered_places_weighted = places + + def set_mcg(self, mcg): + self.mcg = mcg + + def set_dead_tasks(self, dead_tasks): + self.dead_tasks = dead_tasks + + def set_r_g_s_c(self, r_g): + self.r_g_s_c = r_g + + def set_r_g(self, r_g): + self.r_g = r_g + + def set_locking_scenarios(self, scenarios): + self.locking_scenarios = scenarios + + def set_restricted_coverability_tree(self, graph): + self.restricted_coverability_tree = graph + + def get_net(self): + return self.net + + def get_initial_marking(self): + return self.initial_marking + + def get_final_marking(self): + return self.final_marking + + def get_s_c_net(self): + return self.s_c_net + + def get_place_invariants(self): + return self.place_invariants + + def get_uniform_place_invariants(self): + return self.uniform_place_invariants + + def get_s_components(self): + return self.s_components + + def get_uncovered_places_s_component(self): + return self.uncovered_places_s_component + + def get_not_well_handled_pairs(self): + return self.not_well_handled_pairs + + def get_left(self): + return self.left + + def get_uncovered_places_uniform(self): + return self.uncovered_places_uniform + + def get_weighted_place_invariants(self): + return self.weighted_place_invariants + + def get_uncovered_places_weighted(self): + return self.uncovered_places_weighted + + def get_mcg(self): + return self.mcg + + def get_dead_tasks(self): + return self.dead_tasks + + def get_r_g_s_c(self): + return self.r_g_s_c + + def get_r_g(self): + return self.r_g + + def get_locking_scenarios(self): + return self.locking_scenarios + + def get_restricted_coverability_tree(self): + return self.restricted_coverability_tree + + def get_output(self): + """ + Returns a dictionary representation of the + entities that are calculated during WOFLAN + """ + ret = {} + if self.s_c_net is not None: + ret[Outputs.S_C_NET.value] = self.s_c_net + if self.place_invariants is not None: + ret[Outputs.PLACE_INVARIANTS.value] = self.place_invariants + if self.uniform_place_invariants is not None: + ret[Outputs.UNIFORM_PLACE_INVARIANTS.value] = self.uniform_place_invariants + if self.s_components is not None: + ret[Outputs.S_COMPONENTS.value] = self.s_components + if self.uncovered_places_s_component is not None: + ret[Outputs.UNCOVERED_PLACES_S_COMPONENT.value] = self.uncovered_places_s_component + if self.not_well_handled_pairs is not None: + ret[Outputs.NOT_WELL_HANDLED_PAIRS.value] = self.not_well_handled_pairs + if self.left is not None: + ret[Outputs.LEFT.value] = self.left + if self.uncovered_places_uniform is not None: + ret[Outputs.UNCOVERED_PLACES_UNIFORM.value] = self.uncovered_places_uniform + if self.weighted_place_invariants is not None: + ret[Outputs.WEIGHTED_PLACE_INVARIANTS.value] = self.weighted_place_invariants + if self.uncovered_places_weighted is not None: + ret[Outputs.UNCOVERED_PLACES_WEIGHTED.value] = self.uncovered_places_weighted + if self.mcg is not None: + ret[Outputs.MCG.value] = self.mcg + if self.dead_tasks is not None: + ret[Outputs.DEAD_TASKS.value] = self.dead_tasks + if self.r_g_s_c is not None: + ret[Outputs.R_G_S_C.value] = self.r_g_s_c + if self.r_g is not None: + ret[Outputs.R_G] = self.r_g + if self.locking_scenarios is not None: + ret[Outputs.LOCKING_SCENARIOS] = self.locking_scenarios + if self.restricted_coverability_tree is not None: + ret[Outputs.RESTRICTED_COVERABILITY_TREE] = self.restricted_coverability_tree + return ret + + +def short_circuit_petri_net(net, print_diagnostics=False): + """ + Fist, sink and source place are identified. Then, a transition from source to sink is added to short-circuited + the given petri net. If there is no unique source and sink place, an error gets returned + :param net: Petri net that is going to be short circuited + :return: + """ + s_c_net = copy.deepcopy(net) + no_source_places = 0 + no_sink_places = 0 + sink = None + source = None + for place in s_c_net.places: + if len(place.in_arcs) == 0: + source = place + no_source_places += 1 + if len(place.out_arcs) == 0: + sink = place + no_sink_places += 1 + if (sink is not None) and (source is not None) and no_source_places == 1 and no_sink_places == 1: + # If there is one unique source and sink place, short circuit Petri Net is constructed + t_1 = PetriNet.Transition("short_circuited_transition", "short_circuited_transition") + s_c_net.transitions.add(t_1) + # add arcs in short-circuited net + petri_utils.add_arc_from_to(sink, t_1, s_c_net) + petri_utils.add_arc_from_to(t_1, source, s_c_net) + return s_c_net + else: + if sink is None: + if print_diagnostics: + print("There is no sink place.") + return None + elif source is None: + if print_diagnostics: + print("There is no source place.") + return None + elif no_source_places > 1: + if print_diagnostics: + print("There is more than one source place.") + return None + elif no_sink_places > 1: + if print_diagnostics: + print("There is more than one sink place.") + return None + + +def step_1(woflan_object, return_asap_when_unsound=False): + """ + In the first step, we check if the input is given correct. We check if net is an PM4Py Petri Net representation + and if the exist a correct entry for the initial and final marking. + :param woflan_object: Object that contains all necessary information + :return: Proceed with step 2 if ok; else False + """ + + def check_if_marking_in_net(marking, net): + """ + Checks if the marked place exists in the Petri Net and if there is only one i_m and f_m + :param marking: Marking of Petri Net + :param net: PM4Py representation of Petri Net + :return: Boolean. True if marking can exists; False if not. + """ + for place in marking: + if place in net.places: + return True + return False + + if isinstance(woflan_object.get_net(), PetriNet): + if len(woflan_object.get_initial_marking()) != 1 or len(woflan_object.get_final_marking()) != 1: + if woflan_object.print_diagnostics: + print('There is more than one initial or final marking.') + return False + if check_if_marking_in_net(woflan_object.get_initial_marking(), woflan_object.get_net()): + if check_if_marking_in_net(woflan_object.get_final_marking(), woflan_object.get_net()): + if woflan_object.print_diagnostics: + print("Input is ok.") + return step_2(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + if woflan_object.print_diagnostics: + print('The Petri Net is not PM4Py Petri Net represenatation.') + return False + + +def step_2(woflan_object, return_asap_when_unsound=False): + """ + This method checks if a given Petri net is a workflow net. First, the Petri Net gets short-circuited + (connect start and end place with a tau-transition. Second, the Petri Net gets converted into a networkx graph. + Finally, it is tested if the resulting graph is a strongly connected component. + :param woflan_object: Woflan objet containing all information + :return: Bool=True if net is a WF-Net + """ + + def transform_petri_net_into_regular_graph(still_need_to_discover): + """ + Ths method transforms a list of places and transitions into a networkx graph + :param still_need_to_discover: set of places and transition that are not fully added to graph + :return: + """ + G = nx.DiGraph() + while len(still_need_to_discover) > 0: + element = still_need_to_discover.pop() + G.add_node(element.name) + for in_arc in element.in_arcs: + G.add_node(in_arc.source.name) + G.add_edge(in_arc.source.name, element.name) + for out_arc in element.out_arcs: + G.add_node(out_arc.target.name) + G.add_edge(element.name, out_arc.target.name) + return G + + woflan_object.set_s_c_net(short_circuit_petri_net(woflan_object.get_net(), + print_diagnostics=woflan_object.print_diagnostics)) + if woflan_object.get_s_c_net() == None: + return False + to_discover = woflan_object.get_s_c_net().places | woflan_object.get_s_c_net().transitions + graph = transform_petri_net_into_regular_graph(to_discover) + if not nx.algorithms.components.is_strongly_connected(graph): + if woflan_object.print_diagnostics: + print('Petri Net is a not a worflow net.') + return False + else: + if woflan_object.print_diagnostics: + print("Petri Net is a workflow net.") + return step_3(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + +def step_3(woflan_object, return_asap_when_unsound=False): + woflan_object.set_place_invariants(compute_place_invariants(woflan_object.get_s_c_net())) + woflan_object.set_uniform_place_invariants(transform_basis(woflan_object.get_place_invariants(), style='uniform')) + woflan_object.set_s_components( + compute_s_components(woflan_object.get_s_c_net(), woflan_object.get_uniform_place_invariants())) + woflan_object.set_uncovered_places_s_component( + compute_uncovered_places_in_component(woflan_object.get_s_components(), woflan_object.get_s_c_net())) + if len(woflan_object.get_uncovered_places_s_component()) == 0: + woflan_object.set_left(True) + if woflan_object.print_diagnostics: + print('Every place is covered by s-components.') + return step_10(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + else: + if woflan_object.print_diagnostics: + print('The following places are not covered by an s-component: {}.'.format( + woflan_object.get_uncovered_places_s_component())) + if return_asap_when_unsound: + return False + return step_4(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + +def step_4(woflan_object, return_asap_when_unsound=False): + woflan_object.set_not_well_handled_pairs(compute_not_well_handled_pairs(woflan_object.get_s_c_net())) + if len(woflan_object.get_not_well_handled_pairs()) == 0: + if woflan_object.print_diagnostics: + print('Petri Net is unsound') + woflan_object.set_left(False) + if return_asap_when_unsound: + return False + return step_5(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + else: + if woflan_object.print_diagnostics: + print('Not well-handled pairs are: {}.'.format(woflan_object.get_not_well_handled_pairs())) + woflan_object.set_left(True) + return step_5(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + +def step_5(woflan_object, return_asap_when_unsound=False): + woflan_object.set_uncovered_places_uniform( + compute_uncovered_place_in_invariants(woflan_object.get_uniform_place_invariants(), + woflan_object.get_s_c_net())) + if len(woflan_object.get_uncovered_places_uniform()) == 0: + if woflan_object.print_diagnostics: + print('There are no uncovered places in uniform invariants.') + return step_10(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + else: + if woflan_object.print_diagnostics: + print('The following places are uncovered in uniform invariants: {}'.format( + woflan_object.get_uncovered_places_uniform())) + return step_6(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + +def step_6(woflan_object, return_asap_when_unsound=False): + woflan_object.set_weighted_place_invariants(transform_basis(woflan_object.get_place_invariants(), style='weighted')) + woflan_object.set_uncovered_places_weighted( + compute_uncovered_place_in_invariants(woflan_object.get_weighted_place_invariants(), + woflan_object.get_s_c_net())) + if len(woflan_object.get_uncovered_places_weighted()) == 0: + if woflan_object.print_diagnostics: + print('There are no uncovered places in weighted invariants.') + return step_10(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + else: + if woflan_object.print_diagnostics: + print('The following places are uncovered in weighted invariants: {}'.format( + woflan_object.get_uncovered_places_weighted())) + return step_7(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + +def step_7(woflan_object, return_asap_when_unsound=False): + woflan_object.set_mcg(minimal_coverability_graph(woflan_object.get_s_c_net(), woflan_object.get_initial_marking(), + woflan_object.get_net())) + if len(check_for_improper_conditions(woflan_object.get_mcg())) == 0: + if woflan_object.print_diagnostics: + print('No improper coditions.') + if woflan_object.get_left == True: + return step_8(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + else: + return step_10(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + else: + if woflan_object.print_diagnostics: + print('Improper WPD. The following are the improper conditions: {}.'.format( + check_for_improper_conditions(woflan_object.get_mcg()))) + if return_asap_when_unsound: + return False + return step_9(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + +def step_8(woflan_object, return_asap_when_unsound=False): + if check_for_substates(woflan_object.get_mcg()): + return step_10(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + else: + return step_10(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + +def step_9(woflan_object, return_asap_when_unsound=False): + if woflan_object.print_diagnostics: + print('The following sequences are unbounded: {}'.format(compute_unbounded_sequences(woflan_object))) + return False + + +def step_10(woflan_object, return_asap_when_unsound=False): + if woflan_object.get_mcg() == None: + woflan_object.set_mcg( + minimal_coverability_graph(woflan_object.get_s_c_net(), woflan_object.get_initial_marking(), + woflan_object.get_net())) + woflan_object.set_dead_tasks(check_for_dead_tasks(woflan_object.get_s_c_net(), woflan_object.get_mcg())) + if len(woflan_object.get_dead_tasks()) == 0: + if woflan_object.print_diagnostics: + print('There are no dead tasks.') + if woflan_object.get_left() == True: + return step_11(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + else: + if return_asap_when_unsound: + return False + return step_12(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + else: + if woflan_object.print_diagnostics: + print('The following tasks are dead: {}'.format(woflan_object.get_dead_tasks())) + return False + + +def step_11(woflan_object, return_asap_when_unsound=False): + woflan_object.set_r_g_s_c( + reachability_graph(woflan_object.get_s_c_net(), woflan_object.get_initial_marking(), woflan_object.get_net())) + if nx.is_strongly_connected(woflan_object.get_r_g_s_c()): + if woflan_object.print_diagnostics: + print('All tasks are live.') + return True + else: + if return_asap_when_unsound: + return False + return step_13(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + +def step_12(woflan_object, return_asap_when_unsound=False): + woflan_object.set_r_g_s_c( + reachability_graph(woflan_object.get_s_c_net(), woflan_object.get_initial_marking(), woflan_object.get_net())) + if woflan_object.print_diagnostics: + print('There are non-live tasks.') + if return_asap_when_unsound: + return False + return step_13(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + +def step_13(woflan_object, return_asap_when_unsound=False): + woflan_object.set_locking_scenarios(compute_non_live_sequences(woflan_object)) + if woflan_object.print_diagnostics: + print('The following sequences lead to deadlocks: {}.'.format(woflan_object.get_locking_scenarios())) + return False + + +@deprecation.deprecated('2.2.2', removed_in='3.0.0', + details='deprecated version of WOFLAN; use pm4py.algo.analysis.woflan') +def apply(net, i_m, f_m, parameters=None): + """ + Apply the Woflan Soundness check. Trough this process, different steps are executed. + :param net: Petri Net representation of PM4Py + :param i_m: initial marking of given Net. Marking object of PM4Py + :param f_m: final marking of given Net. Marking object of PM4Py + :return: True, if net is sound; False otherwise. + """ + warnings.warn('deprecated version of WOFLAN; use pm4py.algo.analysis.woflan', + DeprecationWarning) + if parameters is None: + parameters = {} + return_asap_when_unsound = exec_utils.get_param_value(Parameters.RETURN_ASAP_WHEN_NOT_SOUND, parameters, False) + print_diagnostics = exec_utils.get_param_value(Parameters.PRINT_DIAGNOSTICS, parameters, True) + return_diagnostics = exec_utils.get_param_value(Parameters.RETURN_DIAGNOSTICS, parameters, False) + + woflan_object = woflan(net, i_m, f_m, print_diagnostics=print_diagnostics) + step_1_res = step_1(woflan_object, return_asap_when_unsound=return_asap_when_unsound) + + if return_diagnostics: + return step_1_res, woflan_object.get_output() + + return step_1_res + + +def compute_non_live_sequences(woflan_object): + """ + We want to compute the sequences of transitions which lead to deadlocks. + To do this, we first compute a reachbility graph (possible, since we know that the Petri Net is bounded) and then we + convert it to a spanning tree. Afterwards, we compute the paths which lead to nodes from which the final marking cannot + be reached. Note: We are searching for the shortest sequence. After the first red node, all successors are also red. + Therefore, we do not have to consider them. + :param woflan_object: Object that contains the necessary information + :return: List of sequence of transitions, each sequence is a list + """ + woflan_object.set_r_g(reachability_graph(woflan_object.get_net(), woflan_object.get_initial_marking())) + f_m = convert_marking(woflan_object.get_net(), woflan_object.get_final_marking()) + sucessfull_terminate_state = None + for node in woflan_object.get_r_g().nodes: + if all(np.equal(woflan_object.get_r_g().nodes[node]['marking'], f_m)): + sucessfull_terminate_state = node + break + # red nodes are those from which the final marking is not reachable + red_nodes = [] + for node in woflan_object.get_r_g().nodes: + if not nx.has_path(woflan_object.get_r_g(), node, sucessfull_terminate_state): + red_nodes.append(node) + # Compute directed spanning tree + spanning_tree = nx.algorithms.tree.Edmonds(woflan_object.get_r_g()).find_optimum() + queue = set() + paths = {} + # root node + queue.add(0) + paths[0] = [] + processed_nodes = set() + red_paths = [] + while len(queue) > 0: + v = queue.pop() + for node in spanning_tree.neighbors(v): + if node not in paths and node not in processed_nodes: + paths[node] = paths[v].copy() + # we can use directly 0 here, since we are working on a spanning tree and there should be no more edges to a node + paths[node].append(woflan_object.get_r_g().get_edge_data(v, node)[0]['transition']) + if node not in red_nodes: + queue.add(node) + else: + red_paths.append(paths[node]) + processed_nodes.add(v) + return red_paths + + +def compute_unbounded_sequences(woflan_object): + """ + We compute the sequences which lead to an infinite amount of tokens. To do this, we compute a restricted coverability tree. + The tree works similar to the graph, despite we consider tree characteristics during the construction. + :param woflan_object: Woflan object that contains all needed information. + :return: List of unbounded sequences, each sequence is a list of transitions + """ + + def check_for_markings_larger_than_final_marking(graph, f_m): + markings = [] + for node in graph.nodes: + if all(np.greater_equal(graph.nodes[node]['marking'], f_m)): + markings.append(node) + return markings + + woflan_object.set_restricted_coverability_tree( + restricted_coverability_tree(woflan_object.get_net(), woflan_object.get_initial_marking())) + f_m = convert_marking(woflan_object.get_net(), woflan_object.get_final_marking()) + infinite_markings = [] + for node in woflan_object.get_restricted_coverability_tree().nodes: + if np.inf in woflan_object.get_restricted_coverability_tree().nodes[node]['marking']: + infinite_markings.append(node) + larger_markings = check_for_markings_larger_than_final_marking(woflan_object.get_restricted_coverability_tree(), + f_m) + green_markings = [] + for node in woflan_object.get_restricted_coverability_tree().nodes: + add_to_green = True + for marking in infinite_markings: + if nx.has_path(woflan_object.get_restricted_coverability_tree(), node, marking): + add_to_green = False + for marking in larger_markings: + if nx.has_path(woflan_object.get_restricted_coverability_tree(), node, marking): + add_to_green = False + if add_to_green: + green_markings.append(node) + red_markings = [] + for node in woflan_object.get_restricted_coverability_tree().nodes: + add_to_red = True + for node_green in green_markings: + if nx.has_path(woflan_object.get_restricted_coverability_tree(), node, node_green): + add_to_red = False + break + if add_to_red: + red_markings.append(node) + # Make the path as short as possible. If we reach a red state, we stop and do not go further in the "red zone". + queue = set() + queue.add(0) + paths = {} + paths[0] = [] + paths_to_red = [] + while len(queue) > 0: + v = queue.pop() + successors = woflan_object.get_restricted_coverability_tree().successors(v) + for suc in successors: + paths[suc] = paths[v].copy() + paths[suc].append(woflan_object.get_restricted_coverability_tree().get_edge_data(v, suc)['transition']) + if suc in red_markings: + paths_to_red.append(paths[suc]) + else: + queue.add(suc) + return paths_to_red diff --git a/src/evaluation/soundness/woflan/graphs/__init__.py b/src/evaluation/soundness/woflan/graphs/__init__.py new file mode 100644 index 0000000..4855257 --- /dev/null +++ b/src/evaluation/soundness/woflan/graphs/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness.woflan.graphs import utility, minimal_coverability_graph, reachability_graph, restricted_coverability_graph \ No newline at end of file diff --git a/src/evaluation/soundness/woflan/graphs/__pycache__/__init__.cpython-310.pyc b/src/evaluation/soundness/woflan/graphs/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74a70e1f9f35c8fe049f475dc789ce98a56de058 GIT binary patch literal 1101 zcmd1j<>g{vU|^Vf$2UEhnStRkh=Yuo7#J8F7#J9e6&M&8QW&BbQW#U1au{=&qL^}- zqnLA9qF8cSqgWXkQka7oG+7dlGcho5DJUo?gk)qEE2L%Sq$*?<D-<Lal_=z=DFpbM z1XL<$_~sX-DtP9l<y$Fal#~<{Tj}c;<eC&z>ZN6t=%p1UmgZ&Tr==F@rKD==ae+;P zXat#9l$xqgoS#-wo>-J>rBIn)s*s$Rr%;rdl383*l$lgolB$qdqL7%EqMu)+kei>9 znN|rlA+toGG%qE!NTDPnRiPxcD7P5oAP~phFI2%jH7~U&F-IYwG$|)DS-~eWIW@01 zRUxrhp#UUOoRONMkOVOZq{R*7ieRWK6x{Ml^HLH^GV}9v6jC!wGE$2a%2JDpGxPHl zjA52QP1I4yF9JJ9Be4YJ_#%b;0+2>cg~Yr{g`C6^gkE%?BRrU*keLVeMn--?szOOd zVhPC0<(WA-3Q4I7rNyafr8zoaTarpk6v912Jp4mL6de5`6~Y~Zf*k!qBCQq5GfOh^ zOG^||%Tgg`W#$&-WTvJllqVJyCFYe>D&(hujqr61a`tfa3vqPv^zjUdRLCz<aPtiD za}5qwaPtpRa8w9z3<~jd4)t*iQV0kQ3h)nh)l*OiP6atE6>I{Y@J$29QBkTwN@_`B zW==8G&yo413dI@ur8y}I8Hr`73Pq{OshMS|DGG@S$@v8!r-8kX+hDN0i8=Xs>0pmS z(w&}yf@hjSUVe#=LUC%Uf(<O`m6w<6rRSCE<rk&v=RhL9Sl>>M>m?{3XtLa5FD=Q; z$t<b7C6$|*mzkTG6Q7)4mRgjU1Qv}?FG?)PxFuASnwXq{DkfW$3QCj7C8;S`wD@VV z-C~cAPsvY?kB?GHElbQP1*ajsVo-9*OD!(eE6-2MNzBs&+fZBt%8EtI3=9lKEFglF zfq`KqLlHZO4I+MJ>W3Dm78UCkm*?lC>AR#BXP4v`=!4vxmmZ&<S)yNDT98_#Tb5Xq znV46in_5wjlV6ltq+eW=tdFo>A8x%q#Cm;*_4@JgnR%Hd@$q^EmA5!-a`RJ4b5iXX NK}7?|IV=n^0s!CTUU~ok literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/graphs/__pycache__/utility.cpython-310.pyc b/src/evaluation/soundness/woflan/graphs/__pycache__/utility.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1530b9a7d2962d50aef93f7f617d52411e6e553 GIT binary patch literal 5420 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 Y5a3|sVB(PE;OF4t;^q+K5aQth01uYM$N&HU literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__init__.py b/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__init__.py new file mode 100644 index 0000000..c7dc477 --- /dev/null +++ b/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness.woflan.graphs.minimal_coverability_graph import minimal_coverability_graph diff --git a/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__pycache__/__init__.cpython-310.pyc b/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..610b0d1035c2be275c910f7099fa5bae47ffc542 GIT binary patch literal 1068 zcmd1j<>g{vU|^Vk$2Z-DnStRkh=Yuo7#J8F7#J9eMHm<uQW&BbQW#U1au{=&qL>&N zQka7oG+7dlGcho5DJUo?gk)qEE2L%Sq$*?<D-<Lal_=z=DFpbM1XL<$_~sX-DtP9l z<y$Fal#~<{Tj}c;<eC&z>ZN6t=%p1UmgZ&Tr==F@rKD==ae+;PXat#9l$xqgoS#-w zo>-J>rBIn)s*s$Rr%;rdl383*l$lgolB$qdqL7%EqMu)+kei>9nN|rlA+toGG%qE! zNTDPnRiPxcD7P5oAP~phFI2%jH7~U&F-IYwG$|)DS-~eWIW@01RUxrhp#UUOoRONM zkOVOZq{R*7ieRWK6x{Ml^HLH^GV}9v6jC!wGE$2a%2JDpGxPHljA52QP1I4yF9JJ9 zBe4YJ_#%b;0+2>cg~Yr{g`C6^gkE%?BRrU*keLVeMn--?szOOdVhPC0<(WA-3Q4I7 zrNyafr8zoaTarpk6v912Jp4mL6de5`6~Y~Zf*k!qBCQq5GfOh^OG^||%Tgg`W#$&- zWTvJllqVJyCFYe>D&(hujqr61a`tfa3vqPv^zjUdRLCz<aPtiDa}5qwaPtpRa8w9z z3<~jd4)t*iQV0kQ3h)nh)l*OiP6atE6>I{Y@J$29QBkTwN@_`BW==8G&yo413dI@u zr8y}I8Hr`73Pq{OshMS|DGG@S$@v8!r-8kX+hDN0i8=Xs>0pmS(w&}yf@hjSUVe#= zLUC%Uf(<O`m6w<6rRSCE<rk&v=RhL9Sl>>M>m?}dYck%F%FWEn%uURRPtGq(ElNzv z%*iaNj8890EXeTFWWL27AD@z+93LNLn_8BbQwmN8dc~k5k(XLrtXH0&mXnyL2UcFJ zhuf$kQ0`jEP{hK(03m+0=!X`k78UCkm*?lC>AR#BXP4v`=z~niOOH>_EYUA6El4fW zElVuQOw23MO|2-%$uCMR(l0Jb)<-x+AMO-=h*R_-P5~Q)-B<eY@tJv<CGqik1(mlr WY;yBcN^?@}7(qE7<T@4x0RaHoo>x5p literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__pycache__/minimal_coverability_graph.cpython-310.pyc b/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/__pycache__/minimal_coverability_graph.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cadd5961672256893ba7c48e3ba32a774f20bf32 GIT binary patch literal 5951 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 b3K||@C<fKdEDTHn9IT+KgolxfnXecC@Jvgg literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/minimal_coverability_graph.py b/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/minimal_coverability_graph.py new file mode 100644 index 0000000..e149db5 --- /dev/null +++ b/src/evaluation/soundness/woflan/graphs/minimal_coverability_graph/minimal_coverability_graph.py @@ -0,0 +1,186 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +""" +This module is based on: +The minimal coverability graph for Petri nets +from Alain Finkel +""" +import numpy as np +import networkx as nx +from evaluation.soundness.woflan.graphs import utility as helper +from copy import copy + + +def minimal_coverability_tree(net, initial_marking, original_net=None): + """ + This method computes the minimal coverability tree. It is part of a method to obtain a minial coverability graph + :param net: Petri Net + :param initial_marking: Initial Marking of the Petri Net + :param original_net: Petri Net without short-circuited transition + :return: Minimal coverability tree + """ + + def check_if_marking_already_in_processed_nodes(n, processed_nodes): + for node in processed_nodes: + if np.array_equal(G.nodes[node]['marking'], G.nodes[n]['marking']): + return True + return False + + def is_m_smaller_than_other(m, processed_nodes): + for node in processed_nodes: + if all(np.less_equal(m, G.nodes[node]['marking'])): + return True + return False + + def is_m_greater_than_other(m, processed_nodes): + for node in processed_nodes: + if all(np.greater_equal(m, G.nodes[node]['marking'])): + return True + return False + + def get_first_smaller_marking_on_path(n, m2): + path = nx.shortest_path(G, source=0, target=n) + for node in path: + if all(np.less_equal(G.nodes[node]['marking'], m2)): + return node + return None + + def remove_subtree(tree, n): + bfs_tree = nx.bfs_tree(tree, n) + for edge in bfs_tree.edges: + tree.remove_edge(edge[0], edge[1]) + for node in bfs_tree.nodes: + if node != n: + tree.remove_node(node) + return tree + + G = nx.MultiDiGraph() + + incidence_matrix = helper.compute_incidence_matrix(net) + firing_dict = helper.split_incidence_matrix(incidence_matrix, net) + req_dict = helper.compute_firing_requirement(net) + + initial_mark = helper.convert_marking(net, initial_marking, original_net) + j = 0 + unprocessed_nodes = set() + G.add_node(j, marking=initial_mark) + unprocessed_nodes.add(j) + j += 1 + + processed_nodes = set() + + while len(unprocessed_nodes) > 0: + n = unprocessed_nodes.pop() + if check_if_marking_already_in_processed_nodes(n, processed_nodes): + processed_nodes.add(n) + elif is_m_smaller_than_other(G.nodes[n]['marking'], processed_nodes): + G.remove_edge(next(G.predecessors(n)), n) + G.remove_node(n) + elif is_m_greater_than_other(G.nodes[n]['marking'], processed_nodes): + m2 = G.nodes[n]['marking'].copy() + ancestor_bool = False + for ancestor in nx.ancestors(G, n): + if is_m_greater_than_other(G.nodes[n]['marking'], [ancestor]): + i = 0 + while i < len(G.nodes[n]['marking']): + if G.nodes[ancestor]['marking'][i] < G.nodes[n]['marking'][i]: + m2[i] = np.inf + i += 1 + n1 = None + for ancestor in nx.ancestors(G, n): + if all(np.less_equal(G.nodes[ancestor]['marking'], m2)): + n1 = get_first_smaller_marking_on_path(n, m2) + break + if n1 != None: + ancestor_bool = True + G.nodes[n1]['marking'] = m2.copy() + subtree = nx.bfs_tree(G, n1) + for node in subtree: + if node in processed_nodes: + processed_nodes.remove(node) + if node in unprocessed_nodes: + unprocessed_nodes.remove(node) + G = remove_subtree(G, n1) + unprocessed_nodes.add(n1) + processed_nodes_copy = copy(processed_nodes) + for node in processed_nodes_copy: + if node in G.nodes: + if all(np.less_equal(G.nodes[node]['marking'], m2)): + subtree = nx.bfs_tree(G, node) + for node in subtree: + if node in processed_nodes: + processed_nodes.remove(node) + if node in unprocessed_nodes: + unprocessed_nodes.remove(node) + remove_subtree(G, node) + G.remove_node(node) + if not ancestor_bool: + unprocessed_nodes.add(n) + else: + for el in helper.enabled_markings(firing_dict, req_dict, G.nodes[n]['marking']): + G.add_node(j, marking=el[0]) + G.add_edge(n, j, transition=el[1]) + unprocessed_nodes.add(j) + j += 1 + processed_nodes.add(n) + return (G, firing_dict, req_dict) + + +def apply(net, initial_marking, original_net=None): + """ + Apply method from the "outside". + :param net: Petri Net object + :param initial_marking: Initial marking of the Petri Net object + :param original_net: Petri Net object without short-circuited transition. For better usability, initial set to None + :return: MultiDiGraph networkx object + """ + + def detect_same_labelled_nodes(G): + same_labels = {} + for node in G.nodes: + if np.array2string(G.nodes[node]['marking']) not in same_labels: + same_labels[np.array2string(G.nodes[node]['marking'])] = [node] + else: + same_labels[np.array2string(G.nodes[node]['marking'])].append(node) + return same_labels + + def merge_nodes_of_same_label(G, same_labels): + for marking in same_labels: + if len(same_labels[marking]) > 1: + origin = same_labels[marking][0] + i = 1 + while i < len(same_labels[marking]): + G = nx.contracted_nodes(G, origin, same_labels[marking][i]) + i += 1 + return G + + mct, firing_dict, req_dict = minimal_coverability_tree(net, initial_marking, original_net) + mcg = merge_nodes_of_same_label(mct, detect_same_labelled_nodes(mct)) + + to_remove_edges = [] + for edge in mcg.edges: + reachable_markings = helper.enabled_markings(firing_dict, req_dict, mcg.nodes[edge[0]]['marking']) + not_reachable = True + for el in reachable_markings: + if np.array_equal(el[0], mcg.nodes[edge[1]]['marking']): + not_reachable = False + break + if not_reachable: + to_remove_edges.append(edge) + for edge in to_remove_edges: + mcg.remove_edge(edge[0], edge[1]) + return mcg diff --git a/src/evaluation/soundness/woflan/graphs/reachability_graph/__init__.py b/src/evaluation/soundness/woflan/graphs/reachability_graph/__init__.py new file mode 100644 index 0000000..cb23bcc --- /dev/null +++ b/src/evaluation/soundness/woflan/graphs/reachability_graph/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness.woflan.graphs.reachability_graph import reachability_graph diff --git a/src/evaluation/soundness/woflan/graphs/reachability_graph/__pycache__/__init__.cpython-310.pyc b/src/evaluation/soundness/woflan/graphs/reachability_graph/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..356ec3c9aaa77f275ac426ee0e6f1038c5c79b5c GIT binary patch literal 1044 zcmd1j<>g{vU|`sA$2Z-CnStRkh=Yuo7#J8F7#J9eMHm<uQW&BbQW#U1au{=&qL>&N zQka7oG+7dlGcho5DJUo?gk)qEE2L%Sq$*?<D-<Lal_=z=DFpbM1XL<$_~sX-DtP9l z<y$Fal#~<{Tj}c;<eC&z>ZN6t=%p1UmgZ&Tr==F@rKD==ae+;PXat#9l$xqgoS#-w zo>-J>rBIn)s*s$Rr%;rdl383*l$lgolB$qdqL7%EqMu)+kei>9nN|rlA+toGG%qE! zNTDPnRiPxcD7P5oAP~phFI2%jH7~U&F-IYwG$|)DS-~eWIW@01RUxrhp#UUOoRONM zkOVOZq{R*7ieRWK6x{Ml^HLH^GV}9v6jC!wGE$2a%2JDpGxPHljA52QP1I4yF9JJ9 zBe4YJ_#%b;0+2>cg~Yr{g`C6^gkE%?BRrU*keLVeMn--?szOOdVhPC0<(WA-3Q4I7 zrNyafr8zoaTarpk6v912Jp4mL6de5`6~Y~Zf*k!qBCQq5GfOh^OG^||%Tgg`W#$&- zWTvJllqVJyCFYe>D&(hujqr61a`tfa3vqPv^zjUdRLCz<aPtiDa}5qwaPtpRa8w9z z3<~jd4)t*iQV0kQ3h)nh)l*OiP6atE6>I{Y@J$29QBkTwN@_`BW==8G&yo413dI@u zr8y}I8Hr`73Pq{OshMS|DGG@S$@v8!r-8kX+hDN0i8=Xs>0pmS(w&}yf@hjSUVe#= zLUC%Uf(<O`m6w<6rRSCE<rk&v=RhL9Sl>>M>m?}dYck#vDoRaE&PYtk%*iaNj8890 zEXeTFWWL27AD@z+93LNLnp&2aQwolMy<$*O$V)9Q)+^6X%Sp`B11m4q!!)7@l$%yE z6tOTcK!{&8`k}?CMaBBX<@q^j`Yx%(*(Lb}`nj2TnR)5)>6s<^#ia$QMY?5)MVX0t zCAz5<1v&XesYUw5MalXIC+NeSpbv3^KEw(7m`>1-kI&4@EQycTE2zB1VUwGmQks)$ P#|X;dAXl+42nYZG@V`v} literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/graphs/reachability_graph/__pycache__/reachability_graph.cpython-310.pyc b/src/evaluation/soundness/woflan/graphs/reachability_graph/__pycache__/reachability_graph.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..029dc4aa5044bd9ea3d7c1b19c816dd79f30cc94 GIT binary patch literal 2223 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 K9E?1Se9QnYTdiFH literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/graphs/reachability_graph/reachability_graph.py b/src/evaluation/soundness/woflan/graphs/reachability_graph/reachability_graph.py new file mode 100644 index 0000000..6700e6b --- /dev/null +++ b/src/evaluation/soundness/woflan/graphs/reachability_graph/reachability_graph.py @@ -0,0 +1,56 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +import networkx as nx +import numpy as np +from evaluation.soundness.woflan.graphs import utility as helper + + +def apply(net, initial_marking, original_net=None): + """ + Method that computes a reachability graph as networkx object + :param net: Petri Net + :param initial_marking: Initial Marking of the Petri Net + :param original_net: Petri Net without short-circuited transition + :return: Networkx Graph that represents the reachability graph of the Petri Net + """ + initial_marking = helper.convert_marking(net, initial_marking, original_net) + firing_dict = helper.split_incidence_matrix(helper.compute_incidence_matrix(net), net) + req_dict = helper.compute_firing_requirement(net) + look_up_indices = {} + j = 0 + reachability_graph = nx.MultiDiGraph() + reachability_graph.add_node(j, marking=initial_marking) + + working_set = set() + working_set.add(j) + + look_up_indices[np.array2string(initial_marking)] = j + + j += 1 + while len(working_set) > 0: + m = working_set.pop() + possible_markings = helper.enabled_markings(firing_dict, req_dict, reachability_graph.nodes[m]['marking']) + for marking in possible_markings: + if np.array2string(marking[0]) not in look_up_indices: + look_up_indices[np.array2string(marking[0])] = j + reachability_graph.add_node(j, marking=marking[0]) + working_set.add(j) + reachability_graph.add_edge(m, j, transition=marking[1]) + j += 1 + else: + reachability_graph.add_edge(m, look_up_indices[np.array2string(marking[0])], transition=marking[1]) + return reachability_graph diff --git a/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__init__.py b/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__init__.py new file mode 100644 index 0000000..9b54423 --- /dev/null +++ b/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness.woflan.graphs.restricted_coverability_graph import restricted_coverability_graph diff --git a/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__pycache__/__init__.cpython-310.pyc b/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0540b59c9cbccd0fdf1a420b4c396ce2521a4e7 GIT binary patch literal 1077 zcmd1j<>g{vU|=|U$2Z-JnStRkh=Yuo7#J8F7#J9eMHm<uQW&BbQW#U1au{=&qL>&N zQka7oG+7dlGcho5DJUo?gk)qEE2L%Sq$*?<D-<Lal_=z=DFpbM1XL<$_~sX-DtP9l z<y$Fal#~<{Tj}c;<eC&z>ZN6t=%p1UmgZ&Tr==F@rKD==ae+;PXat#9l$xqgoS#-w zo>-J>rBIn)s*s$Rr%;rdl383*l$lgolB$qdqL7%EqMu)+kei>9nN|rlA+toGG%qE! zNTDPnRiPxcD7P5oAP~phFI2%jH7~U&F-IYwG$|)DS-~eWIW@01RUxrhp#UUOoRONM zkOVOZq{R*7ieRWK6x{Ml^HLH^GV}9v6jC!wGE$2a%2JDpGxPHljA52QP1I4yF9JJ9 zBe4YJ_#%b;0+2>cg~Yr{g`C6^gkE%?BRrU*keLVeMn--?szOOdVhPC0<(WA-3Q4I7 zrNyafr8zoaTarpk6v912Jp4mL6de5`6~Y~Zf*k!qBCQq5GfOh^OG^||%Tgg`W#$&- zWTvJllqVJyCFYe>D&(hujqr61a`tfa3vqPv^zjUdRLCz<aPtiDa}5qwaPtpRa8w9z z3<~jd4)t*iQV0kQ3h)nh)l*OiP6atE6>I{Y@J$29QBkTwN@_`BW==8G&yo413dI@u zr8y}I8Hr`73Pq{OshMS|DGG@S$@v8!r-8kX+hDN0i8=Xs>0pmS(w&}yf@hjSUVe#= zLUC%Uf(<O`m6w<6rRSCE<rk&v=RhL9Sl>>M>m?}dYck%FElLF?t>lu_l=$TQvecr) zq|BVmlFIn>qQrs>KTYOa?D6p_`N{F|Q4XnPi8-a<q@Y&}N)>sj#l?E%`Dr<cd3s>w z#d>&6D+1-Ql?+8J3=9zBSG#^_acWVqesOtzPMW?;YH@Z+et~{&W?p7qdVG3jiGFcu zL28k1Sz=LUVqS@EYDGa#eo<<XesNK<KEgHnaM$QVT%!+hjXqx2=*P!r=4F<|$LkeT Z-r}&y%}*)KNws4H6#*azvM>k;002mUTWA0P literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__pycache__/restricted_coverability_graph.cpython-310.pyc b/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/__pycache__/restricted_coverability_graph.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..357971bbf8304707ad42eafbf85db13656fa6522 GIT binary patch literal 2923 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 literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/restricted_coverability_graph.py b/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/restricted_coverability_graph.py new file mode 100644 index 0000000..e0019e1 --- /dev/null +++ b/src/evaluation/soundness/woflan/graphs/restricted_coverability_graph/restricted_coverability_graph.py @@ -0,0 +1,89 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +import numpy as np +import networkx as nx +from evaluation.soundness.woflan.graphs import utility as helper + + +def construct_tree(net, initial_marking): + """ + Construct a restricted coverability marking. + For more information, see the thesis "Verification of WF-nets", 4.3. + :param net: + :param initial_marking: + :return: + """ + initial_marking = helper.convert_marking(net, initial_marking) + firing_dict = helper.split_incidence_matrix(helper.compute_incidence_matrix(net), net) + req_dict = helper.compute_firing_requirement(net) + look_up_indices = {} + j = 0 + coverability_graph = nx.DiGraph() + coverability_graph.add_node(j, marking=initial_marking) + look_up_indices[np.array2string(initial_marking)] = j + + j += 1 + new_arc = True + while new_arc: + new_arc = False + nodes = list(coverability_graph.nodes).copy() + while len(nodes) > 0: + m = nodes.pop() + if not np.inf in coverability_graph.nodes[m]['marking']: + possible_markings = helper.enabled_markings(firing_dict, req_dict, + coverability_graph.nodes[m]['marking']) + m2 = None + if len(possible_markings) > 0: + for marking in possible_markings: + # check for m1 + since we want to construct a tree, we do not want that a marking is already in a graph since it is going to have an arc + if np.array2string(marking[0]) not in look_up_indices: + if check_if_transition_unique(m, coverability_graph, marking[1]): + m2 = marking + new_arc = True + break + if new_arc: + break + if new_arc: + m3 = np.zeros(len(list(net.places))) + for place in list(net.places): + if check_for_smaller_marking(m2, coverability_graph, list(net.places).index(place), m, look_up_indices): + m3[list(net.places).index(place)] = np.inf + else: + m3[list(net.places).index(place)] = m2[0][list(net.places).index(place)] + coverability_graph.add_node(j, marking=m3) + coverability_graph.add_edge(m, j, transition=m2[1]) + look_up_indices[np.array2string(m3)] = j + j += 1 + return coverability_graph + + +def check_if_transition_unique(marking, graph, transition): + for edge in graph.out_edges(marking): + if graph[edge[0]][edge[1]]['transition'] == transition: + return False + return True + + +def check_for_smaller_marking(marking, coverability_graph, index, current_node, look_up_indices): + for node in coverability_graph.nodes: + if all(np.less_equal(coverability_graph.nodes[node]['marking'], marking[0])): + if coverability_graph.nodes[node]['marking'][index] < marking[0][index]: + if nx.has_path(coverability_graph, + look_up_indices[np.array2string(coverability_graph.nodes[node]['marking'])], + current_node): + return True + return False diff --git a/src/evaluation/soundness/woflan/graphs/utility.py b/src/evaluation/soundness/woflan/graphs/utility.py new file mode 100644 index 0000000..ef646a4 --- /dev/null +++ b/src/evaluation/soundness/woflan/graphs/utility.py @@ -0,0 +1,139 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +import numpy as np +import networkx as nx + +def compute_incidence_matrix(net): + """ + Given a Petri Net, the incidence matrix is computed. An incidence matrix has n rows (places) and m columns + (transitions). + :param net: Petri Net object + :return: Incidence matrix + """ + n = len(net.transitions) + m = len(net.places) + C = np.zeros((m, n)) + i = 0 + transition_list = list(net.transitions) + place_list = list(net.places) + while i < n: + t = transition_list[i] + for in_arc in t.in_arcs: + # arcs that go to transition + C[place_list.index(in_arc.source), i] -= (1*in_arc.weight) + for out_arc in t.out_arcs: + # arcs that lead away from transition + C[place_list.index(out_arc.target), i] += (1*out_arc.weight) + i += 1 + return C + + +def split_incidence_matrix(matrix, net): + """ + We split the incidence matrix columnwise to get the firing information for each transition + :param matrix: incidence matrix + :param net: Petri Net + :return: Dictionary, whereby the key is an np array that contains the firing information and the value is the name + of the transition + """ + transition_dict = {} + i = 0 + while i < len(net.transitions): + transition_dict[list(net.transitions)[i]] = np.hsplit(np.transpose(matrix), 1)[0][i] + i += 1 + return transition_dict + +def compute_firing_requirement(net): + place_list=list(net.places) + transition_dict={} + for transition in net.transitions: + temp_array=np.zeros(len(place_list)) + for arc in transition.in_arcs: + temp_array[place_list.index(arc.source)] -=1*arc.weight + transition_dict[transition]=temp_array + return transition_dict + +def enabled_markings(firing_dict, req_dict,marking): + enabled_transitions = [] + for transition, requirment in req_dict.items(): + if all(np.greater_equal(marking, requirment.copy()*-1)): + enabled_transitions.append(transition) + new_markings = [] + for transition in enabled_transitions: + new_marking = marking + firing_dict[transition] + new_markings.append((new_marking, transition)) + return new_markings + +def convert_marking(net, marking, original_net=None): + """ + Takes an marking as input and converts it into an Numpy Array + :param net: PM4Py Petri Net object + :param marking: Marking that should be converted + :param original_net: PM4Py Petri Net object without short-circuited transition + :return: Numpy array representation + """ + marking_list=list(el.name for el in marking.keys()) + place_list = list(el.name for el in net.places) + mark = np.zeros(len(place_list)) + for index, value in enumerate(mark): + if place_list[index] in marking_list: + #TODO: Is setting the value to 1 ok in this case? + mark[index]=1 + return mark + +def check_for_dead_tasks(net, graph): + """ + We compute a list of dead tasks. A dead task is a task which does not appear in the Minimal Coverability Graph + :param net: Petri Net representation of PM4Py + :param graph: Minimal coverability graph. NetworkX MultiDiGraph object. + :return: list of dead tasks + """ + tasks=[] + for transition in list(net.transitions): + if transition.label != None: + tasks.append(transition) + for node,targets in graph.edges()._adjdict.items(): + for target_node,activties in targets.items(): + for option,activity in activties.items(): + if activity['transition'] in tasks: + tasks.remove(activity['transition']) + return tasks + +def check_for_improper_conditions(mcg): + """ + An improper condition is a state in the minimum-coverability graph with an possible infinite amount of tokens + :param mcg: networkx object (minimal coverability graph) + :return: True, if there are no improper conditions; false otherwise + """ + improper_states=[] + for node in mcg.nodes: + if np.inf in mcg.nodes[node]['marking']: + improper_states.append(node) + return improper_states + +def check_for_substates(mcg): + """ + Checks if a substate exists in a given mcg + :param mcg: Minimal coverability graph (networkx object) + :return: True, if there exist no substate; False otherwise + """ + for node in mcg.nodes: + reachable_states = nx.descendants(mcg, node) + for state in reachable_states: + if all(np.less(mcg.nodes[node]['marking'],mcg.nodes[state]['marking'])): + return False + return True diff --git a/src/evaluation/soundness/woflan/not_well_handled_pairs/__init__.py b/src/evaluation/soundness/woflan/not_well_handled_pairs/__init__.py new file mode 100644 index 0000000..05f5f47 --- /dev/null +++ b/src/evaluation/soundness/woflan/not_well_handled_pairs/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness.woflan.not_well_handled_pairs import not_well_handled_pairs diff --git a/src/evaluation/soundness/woflan/not_well_handled_pairs/__pycache__/__init__.cpython-310.pyc b/src/evaluation/soundness/woflan/not_well_handled_pairs/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4473ff44e33d890a9527d36972ce51162493e36 GIT binary patch literal 1042 zcmd1j<>g{vU|@K9$2Z-SnStRkh=Yuo7#J8F7#J9eMHm<uQW&BbQW#U1au{=&qL>&N zQka7oG+7dlGcho5DJUo?gk)qEE2L%Sq$*?<D-<Lal_=z=DFpbM1XL<$_~sX-DtP9l z<y$Fal#~<{Tj}c;<eC&z>ZN6t=%p1UmgZ&Tr==F@rKD==ae+;PXat#9l$xqgoS#-w zo>-J>rBIn)s*s$Rr%;rdl383*l$lgolB$qdqL7%EqMu)+kei>9nN|rlA+toGG%qE! zNTDPnRiPxcD7P5oAP~phFI2%jH7~U&F-IYwG$|)DS-~eWIW@01RUxrhp#UUOoRONM zkOVOZq{R*7ieRWK6x{Ml^HLH^GV}9v6jC!wGE$2a%2JDpGxPHljA52QP1I4yF9JJ9 zBe4YJ_#%b;0+2>cg~Yr{g`C6^gkE%?BRrU*keLVeMn--?szOOdVhPC0<(WA-3Q4I7 zrNyafr8zoaTarpk6v912Jp4mL6de5`6~Y~Zf*k!qBCQq5GfOh^OG^||%Tgg`W#$&- zWTvJllqVJyCFYe>D&(hujqr61a`tfa3vqPv^zjUdRLCz<aPtiDa}5qwaPtpRa8w9z z3<~jd4)t*iQV0kQ3h)nh)l*OiP6atE6>I{Y@J$29QBkTwN@_`BW==8G&yo413dI@u zr8y}I8Hr`73Pq{OshMS|DGG@S$@v8!r-8kX+hDN0i8=Xs>0pmS(w&}yf@hjSUVe#= zLUC%Uf(<O`m6w<6rRSCE<rk&v=RhL9Sl>>M>m?}dYck#v%gZl`FHg<MiO)#POUX%1 zi7!aZEGqWXWWL27AD@z+93LNLlv<XUQwmN4dc~mRke6CqtXH0&mXnyLhh2XWC_AlW zC}Lq?fDpeb^+StOi;DG&%ky*6^j%VmvrF;|^m8-wGV{{o(=$u-i%Sbqi*(Bpi!u}Q zN_0~z3Ucy`Qj7G9i<0#b_Ups#*N51zkKKO#`1s7c%#!$cy@JYH95%W6DWy57c8s9> L4RR6-gMa`4aG^}v literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/not_well_handled_pairs/__pycache__/not_well_handled_pairs.cpython-310.pyc b/src/evaluation/soundness/woflan/not_well_handled_pairs/__pycache__/not_well_handled_pairs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..622581dfc87812eca74c98dd856d4429946ff1cb GIT binary patch literal 2426 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 literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/not_well_handled_pairs/not_well_handled_pairs.py b/src/evaluation/soundness/woflan/not_well_handled_pairs/not_well_handled_pairs.py new file mode 100644 index 0000000..e025631 --- /dev/null +++ b/src/evaluation/soundness/woflan/not_well_handled_pairs/not_well_handled_pairs.py @@ -0,0 +1,64 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +import networkx as nx + +def create_network_graph(net): + """ + Transform a given Petri Net in a network graph. Each place and transition is node and gets duplicated. + The even numbers handle the inputs of a node, the odds the output. + :param net: PM4Py Petri Net representation + :return: networkx.DiGraph(), bookkeeping dictionary + """ + graph = nx.DiGraph() + places = list(net.places) + transitions = list(net.transitions) + nodes=set(places) | set(transitions) + bookkeeping={} + for index,el in enumerate(nodes): + bookkeeping[el]=index*2 + for node in nodes: + graph.add_node(bookkeeping[node]) + graph.add_node(bookkeeping[node]+1) + graph.add_edge(bookkeeping[node], bookkeeping[node]+1, capacity=1) + #add edges for outgoing arcs in former Petri Net + for element in nodes: + for arc in element.out_arcs: + graph.add_edge(bookkeeping[element]+1, bookkeeping[arc.target], capacity=1) + #add edges for ingoing arcs in former Petri Net + for element in nodes: + for arc in element.in_arcs: + graph.add_edge(bookkeeping[arc.source]+1, bookkeeping[element], capacity=1) + return graph,bookkeeping + +def apply(net): + """ + Using the max-flow min-cut theorem, we compute a list of nett well handled TP and PT pairs + (T=transition, P=place) + :param net: Petri Net + :return: List + """ + graph,booking=create_network_graph(net) + pairs=[] + for place in net.places: + for transition in net.transitions: + p=booking[place] + t=booking[transition] + if nx.maximum_flow_value(graph, p+1, t)>1: + pairs.append((p+1,t)) + if nx.maximum_flow_value(graph, t+1, p)>1: + pairs.append((t+1,p)) + return pairs diff --git a/src/evaluation/soundness/woflan/place_invariants/__init__.py b/src/evaluation/soundness/woflan/place_invariants/__init__.py new file mode 100644 index 0000000..2c856d1 --- /dev/null +++ b/src/evaluation/soundness/woflan/place_invariants/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness.woflan.place_invariants import place_invariants, s_component, uniform_invariant, utility diff --git a/src/evaluation/soundness/woflan/place_invariants/__pycache__/__init__.cpython-310.pyc b/src/evaluation/soundness/woflan/place_invariants/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..842536a4fe3905874110b6b9b4ad256f5c2bf694 GIT binary patch literal 1092 zcmd1j<>g{vU|{%l$2UEQnStRkh=Yuo7#J8F7#J9e6&M&8QW&BbQW#U1au{=&qL^}- zqnLA9qF8cSqgWXkQka7oG+7dlGcho5DJUo?gk)qEE2L%Sq$*?<D-<Lal_=z=DFpbM z1XL<$_~sX-DtP9l<y$Fal#~<{Tj}c;<eC&z>ZN6t=%p1UmgZ&Tr==F@rKD==ae+;P zXat#9l$xqgoS#-wo>-J>rBIn)s*s$Rr%;rdl383*l$lgolB$qdqL7%EqMu)+kei>9 znN|rlA+toGG%qE!NTDPnRiPxcD7P5oAP~phFI2%jH7~U&F-IYwG$|)DS-~eWIW@01 zRUxrhp#UUOoRONMkOVOZq{R*7ieRWK6x{Ml^HLH^GV}9v6jC!wGE$2a%2JDpGxPHl zjA52QP1I4yF9JJ9Be4YJ_#%b;0+2>cg~Yr{g`C6^gkE%?BRrU*keLVeMn--?szOOd zVhPC0<(WA-3Q4I7rNyafr8zoaTarpk6v912Jp4mL6de5`6~Y~Zf*k!qBCQq5GfOh^ zOG^||%Tgg`W#$&-WTvJllqVJyCFYe>D&(hujqr61a`tfa3vqPv^zjUdRLCz<aPtiD za}5qwaPtpRa8w9z3<~jd4)t*iQV0kQ3h)nh)l*OiP6atE6>I{Y@J$29QBkTwN@_`B zW==8G&yo413dI@ur8y}I8Hr`73Pq{OshMS|DGG@S$@v8!r-8kX+hDN0i8=Xs>0pmS z(w&}yf@hjSUVe#=LUC%Uf(<O`m6w<6rRSCE<rk&v=RhL9Sl>>M>m?{3XtLZAD9A}n zPL0pZD@!cOOw21OzQtV}pPZjtke`>DS8_|RG%qtPzbF@>;1+vnNoG!FNu{4A+b#C^ z_>}zQ`1mND)Uw2!QgAZSD+Z;Qywu`iz4H9DoWwjmbSsKL`L2kWfq|ij1w^niFfgoS zC}Ib(LBy|O{m|mnqGJ8x^8B1MeV5eY?2`Nf{oKsF%)IpY^vn|d;?jcDB3-DHbyF(} za`KB(i}Z_&lJyY|(T6)kAL0;wbcg82$7kkcmc+;F6;$5hu*uC&Da}c>V+555AcwIq H$Or%c<*{2h literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/place_invariants/__pycache__/place_invariants.cpython-310.pyc b/src/evaluation/soundness/woflan/place_invariants/__pycache__/place_invariants.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3c4ef8ebc11e41f3bf6cfb1a7a8fb015dea5484 GIT binary patch literal 2672 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 L1BU<yBM%b*<3t%; literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/place_invariants/__pycache__/s_component.cpython-310.pyc b/src/evaluation/soundness/woflan/place_invariants/__pycache__/s_component.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b1b3d43cb875ab94d4cf042390f6da3addce3a7 GIT binary patch literal 3315 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 Y=A_zz%Ccfm%4K2T5#V6r;Nmd^08F@^D*ylh literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/place_invariants/__pycache__/uniform_invariant.cpython-310.pyc b/src/evaluation/soundness/woflan/place_invariants/__pycache__/uniform_invariant.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e4eece20dcf20ef62a30b39f141e42c92a12f5b GIT binary patch literal 1340 zcmd1j<>g{vU|<ls>znSy!octt#6iYP3=9ko3=9m#8Vn2!DGX5zDU2yhIgGhXQA~^= zHggVhE=v>(BSQ*H3Tq2P6l)4wFoPz0;&CPh1}+5!1%;4|%wmPK%$!t(%wmOt#G(>~ z{4|9CUz31J1r6W)qErRXytI5Pg^ZGtf?_Lu{eoPRf=a!#%o4q{qQug?jQq6JBE6JU zO+7BKX%LMd6N^$)6^irIO3D+9Qmqs!^Gg+y6Y~^`Qd2UEONug+N=s4|GD{Q^^HTKl zixhJ6Q!>*k!6syuD3s=<q!uZZWTYyTq!#5CgB%3nxch}FxTof&7A58=1e7M_WF{;4 zWG1KP6{jjB7Aq8hM2a&~QxuXQ27$D=fm{&`b%lakeraAxVo7Fxo{mClW=TeBkwRH& zQE_H|o`NyV5~ztf3i(A~=V&CBfE-_>kY51OsHu>cSE-PbSc1@t?sJ3(Qxr1uz~0En zFGy7=$w({#dAU3@Cr2SERiU&vHLWy92W(4HX^BF(XNZS?Xo!NNU!+2~V^ENzUr402 zLV0FMMt*6DLTXtm#H`HRf}G6M6ovA{qN2pSl1hdAG_VoAu0hToj(#DIPM$uVA(0CC zMG9`7A%3pG!3u8vK?;rv0ggc-p3b2@jzJ0mp+N!u!LE7=3c;x$hoyo|z!Sb{;5aHu zRY*xKNzBYChWa@&zf_?(Bfm5!MIj@xELEW>H90l2EHy<TQ6V|M0OT~V_i-Bxwl^^+ zKQA5ZQAoPeQ&8|sQ^?CN(NQQ)O;xagCB5?Ua=rAtQoa15bp0Gi#24$^>2bXT<p)j1 zTN26nxdo*qsqqCliOH$)nR#W2MVX0tCB>SIxA;qn67!1F@{4lglM;(Fi<22anxL4O zfq{XAfq}spl#e7B7#K<zY8aXsYZ*%zYZyWKsh6pisfH<-L6h0<7JF%4CdeF3##^k# zC6zg;noLEYOjramMU(j!b6#r6Edg{VL<xhFcxGw}lDk$i6mc*xF#M{~4=qkDD%LM9 z&(BHIcS$YIF3B&@&&|xs%uA0?&n(d|E-gqc(uI0QH?^W5C%-7QNWZu!SwFQbF{c!q zruB<K$vZE#xLChDKP@LQPaoY8`cS7MoS;`wd5bl%pdhDG1mt%%1_lNWMjl2M#v)Lu z<EP1Xi#<L*B|kYn{+2DmCOx=KdJvoR&~3sH00+@66Ozm*Ey>KuEU5$sEZ9BZ&_W_O f85kIDaoFVMr<CTT+JR!U7!;l?3_Jonj66&LE%2Qy literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/place_invariants/__pycache__/utility.cpython-310.pyc b/src/evaluation/soundness/woflan/place_invariants/__pycache__/utility.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c67d4152c917af1c7b3f51e3af9046d5769ed240 GIT binary patch literal 4225 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 XPO2TK@Gb^PvM_K6a4>TT@HhhipQ@FW literal 0 HcmV?d00001 diff --git a/src/evaluation/soundness/woflan/place_invariants/place_invariants.py b/src/evaluation/soundness/woflan/place_invariants/place_invariants.py new file mode 100644 index 0000000..4dcd43c --- /dev/null +++ b/src/evaluation/soundness/woflan/place_invariants/place_invariants.py @@ -0,0 +1,66 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +import numpy as np +import sympy + +def compute_place_invariants(net): + """ + We compute the NUllspace of the incidence matrix and obtain the place-invariants. + :param net: Petri Net of which we want to know the place invariants. + :return: Set of place invariants of the given Petri Net. + """ + + def compute_incidence_matrix(net): + """ + Given a Petri Net, the incidence matrix is computed. An incidence matrix has n rows (places) and m columns + (transitions). + :param net: Petri Net object + :return: Incidence matrix + """ + n = len(net.transitions) + m = len(net.places) + C = np.zeros((m, n)) + i = 0 + transition_list = list(net.transitions) + place_list = list(net.places) + while i < n: + t = transition_list[i] + for in_arc in t.in_arcs: + # arcs that go to transition + C[place_list.index(in_arc.source), i] -= 1 + for out_arc in t.out_arcs: + # arcs that lead away from transition + C[place_list.index(out_arc.target), i] += 1 + i += 1 + return C + + def extract_basis_vectors(incidence_matrix): + """ + The name of the method describes what we want t achieve. We calculate the nullspace of the transposed identity matrix. + :param incidence_matrix: Numpy Array + :return: a collection of numpy arrays that form a base of transposed A + """ + # To have the same dimension as described as in https://www7.in.tum.de/~esparza/fcbook-middle.pdf and to get the correct nullspace, we have to transpose + A = np.transpose(incidence_matrix) + # exp from book https://www7.in.tum.de/~esparza/fcbook-middle.pdf + x = sympy.Matrix(A).nullspace() + # TODO: Question here: Will x be always rational? Depends on sympy implementation. Normaly, yes, we we will have rational results + x = np.array(x).astype(np.float64) + return x + + A = compute_incidence_matrix(net) + return extract_basis_vectors(A) diff --git a/src/evaluation/soundness/woflan/place_invariants/s_component.py b/src/evaluation/soundness/woflan/place_invariants/s_component.py new file mode 100644 index 0000000..73b7f1a --- /dev/null +++ b/src/evaluation/soundness/woflan/place_invariants/s_component.py @@ -0,0 +1,90 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness.woflan.place_invariants.uniform_invariant import apply as compute_uniform_invariants + +def apply(net): + """ + General method to obtain a list of S-components + :param net: Petri Net for which S-components should be computed + :return: A list of S-components + """ + uniform_invariants=compute_uniform_invariants(net) + return compute_s_components(net, uniform_invariants) + + +def compute_s_components(net, p_invariants): + """ + We perform the hint in 5.4.4 of https://pure.tue.nl/ws/portalfiles/portal/1596223/9715985.pdf + :param p_invariants: Semi-positive basis we calculate previously + :return: A list of S-Components. A s-component consists of a set which includes all related transitions a places + """ + + def compare_lists(list1, list2): + """ + :param list1: a list + :param list2: a list + :return: a number how often a item from list1 appears in list2 + """ + counter = 0 + for el in list1: + if el in list2: + counter += 1 + return counter + + s_components = [] + place_list = list(net.places) + for invariant in p_invariants: + i = 0 + s_component = [] + for el in invariant: + if el > 0: + place = place_list[i] + s_component.append(place) + for in_arc in place.in_arcs: + s_component.append(in_arc.source) + for out_arc in place.out_arcs: + s_component.append(out_arc.target) + i += 1 + if len(s_component) != 0: + is_s_component = True + for el in s_component: + if el in net.transitions: + places_before = [arc.source for arc in el.in_arcs] + if compare_lists(s_component, places_before) != 1: + is_s_component = False + break + places_after = [arc.target for arc in el.out_arcs] + if compare_lists(s_component, places_after) != 1: + is_s_component = False + break + if is_s_component: + s_components.append(set(s_component)) + return s_components + +def compute_uncovered_places_in_component(s_components, net): + """ + We check for uncovered places + :param s_components: List of s_components + :param net: Petri Net representation of PM4Py + :return: List of uncovered places + """ + place_list=list(net.places) + for component in s_components: + for el in component: + if el in place_list: + place_list.remove(el) + return place_list diff --git a/src/evaluation/soundness/woflan/place_invariants/uniform_invariant.py b/src/evaluation/soundness/woflan/place_invariants/uniform_invariant.py new file mode 100644 index 0000000..d6c8500 --- /dev/null +++ b/src/evaluation/soundness/woflan/place_invariants/uniform_invariant.py @@ -0,0 +1,24 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.soundness.woflan.place_invariants.place_invariants import compute_place_invariants +from evaluation.soundness.woflan.place_invariants.utility import transform_basis + +def apply(net): + place_invariants= compute_place_invariants(net) + modified_invariants=transform_basis(place_invariants, style='uniform') + return modified_invariants + diff --git a/src/evaluation/soundness/woflan/place_invariants/utility.py b/src/evaluation/soundness/woflan/place_invariants/utility.py new file mode 100644 index 0000000..cf2ec9b --- /dev/null +++ b/src/evaluation/soundness/woflan/place_invariants/utility.py @@ -0,0 +1,118 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +import numpy as np + + +def removearray(L, arr): + """ + Remove an array from a given list and return the list with the removed element. + :param L: list object + :param arr: array that has to be removed + :return: list object without array + """ + ind = 0 + size = len(L) + while ind != size and not np.array_equal(L[ind], arr): + ind += 1 + if ind != size: + L.pop(ind) + else: + raise ValueError('array not found in list.') + +def transform_basis(basis, style=None): + """ + We construct a (I)LP to transform our basis into a set of vectors by using linear combination to fit certain styles/ + properties + :param basis: list of p-invariants. Commonly computed by the method 'compute_place_invariants' in + place_invariants.py + :param style: String that is used to construct certain constraints + At the moment, 'uniform' (all weights have value 0 or 1), and 'weighted' (all weights are >=0) are supported + :return: List of p-invariants that fits the style + """ + import pulp + + if style==None: + style='weighted' + + # First, we want to check if a vector of a basis only contains non-positve entries. If so, then we multiply the + # vector -1. + modified_base = [] + for vector in basis: + all_non_positiv = True + for entry in vector: + if entry > 0: + all_non_positiv = False + if all_non_positiv: + modified_base.append(-1 * vector) + else: + modified_base.append(vector) + #For uniform variants, it is necessary that the weight for a place is either 0 or 1. We collect the variants for + #which this condition does not hold. We also collect the variants for the weighted invariants the entry is <0. + to_modify = [] + for vector in modified_base: + for entry in vector: + if ((entry < 0 or entry > 1) and style=='uniform') or ( entry < 0 and style=='weighted'): + to_modify.append(vector) + break + # if we have nothing to modify, we are done + if len(to_modify) > 0: + for vector in to_modify: + removearray(modified_base, vector) + set_B = range(0, len(modified_base)) + prob = pulp.LpProblem("linear_combination", pulp.LpMinimize) + X = pulp.LpVariable.dicts("x", set_B, cat='Integer') + y = pulp.LpVariable("y", cat='Integer', lowBound=1) + # add objective + prob += pulp.lpSum(X[i] for i in set_B) + if style=='uniform': + # variables for uniform. Therefore, the resulting weight can either be 0 or 1 + z = pulp.LpVariable.dicts("z", range(0, len(vector)), lowBound=0, upBound=1, cat='Integer') + # add constraints + for i in range(len(vector)): + prob += pulp.lpSum(X[j]*modified_base[j][i] for j in range(len(modified_base)))+y*vector[i]== z[i] + elif style=='weighted': + for i in range(len(vector)): + prob += pulp.lpSum(X[j]*modified_base[j][i] for j in range(len(modified_base)))+y*vector[i] >= 0 + prob.solve() + new_vector = np.zeros(len(vector)) + if style=='weighted': + for i in range(len(new_vector)): + new_vector[i] = y.varValue * vector[i] + for j in range(len(modified_base)): + new_vector[i] = new_vector[i] + modified_base[j][i] * X[j].varValue + elif style=='uniform': + for i in range(len(new_vector)): + new_vector[i] = z[i].varValue + modified_base.append(new_vector) + return modified_base + +def compute_uncovered_places(invariants, net): + """ + Compute a list of uncovered places for invariants of a given Petri Net. Note that there exists a separate algorithm + for s-components + :param invariants: list of invariants. Each invariants is a numpy-Array representation + :param net: Petri Net object of PM4Py + :return: List of uncovered place over all invariants + """ + place_list=list(net.places) + unncovered_list=place_list.copy() + for invariant in invariants: + for index, value in enumerate(invariant): + if value != 0: + if place_list[index] in unncovered_list: + unncovered_list.remove(place_list[index]) + return unncovered_list diff --git a/src/evaluation/wf_net/__init__.py b/src/evaluation/wf_net/__init__.py new file mode 100644 index 0000000..b8ddfdc --- /dev/null +++ b/src/evaluation/wf_net/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.wf_net import evaluator, variants diff --git a/src/evaluation/wf_net/__pycache__/__init__.cpython-310.pyc b/src/evaluation/wf_net/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e65adaa65b90217a1def9be58b8827b53ddf6c1 GIT binary patch literal 982 zcmd1j<>g{vU|?vt?wc;i%)sy%#6iYP3=9ko3=9m#5)2FsDGX5zDU2yhIgGhXQB1ka zQOt}CDa^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur0xA_WeDjM^ z6+H9O@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}CG=fYlN=;QL z&QB{TPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5OsfQ&kXfQo znwOGVq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjhs*qT$PyiAs z&PYvBNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN#xP5uCh92U z7lEClkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv!u>|Dh^30qZ zg``x4(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$dr6mffWvLLe zGII-ZGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7xdsO-xcLVu zI4T4<28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0mGp88p=g9n0 zh2o6-(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu=}u2U!81)E zFTX@bp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9i+HJNU4rj{k<lqQzs7v170 zODxJv%quDO(`32D9v`2QpBx__B?wiJnV+Xuo)({%T2chcE=9}?3=At7idY#KAjGc- z{m|mnqGJ8x^8B1MeV5eY?2`Nf{oKsF%)IpY^vn|d;?jcDB3-B@x~UZfIr&AYMf$}> t$@&O8^dWZW$H!;pWtPOp>lIYq;;_lhPbtkwwPOV3Q5FUU1{MYp0RW)*F|Pms literal 0 HcmV?d00001 diff --git a/src/evaluation/wf_net/__pycache__/evaluator.cpython-310.pyc b/src/evaluation/wf_net/__pycache__/evaluator.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d3139da6d04c932419207fe0dc2780ce71c0706 GIT binary patch literal 1655 zcmd1j<>g{vU|`U?<D0&Oje+4Yh=Yt-85kHG7#J9ea~K#HQW&BbQW#U1au}l+Qy5d2 zbC`0OqnH^XVk}WCDU2yBIjp&CQEXr_)*SX+jwlXB26u)OwiNajh7|Tx=4R$7&Qz8x zt`v?G&J?Z`?q22;rc}->?lh(po)q2|#%9JSo)o5F22H-i<4g<;TnY*b3LzPp#R_Se zIjIVn#R>(9MI{RPX$k?pCIOWS8ov2OsS2KXY57(P86_nJ#a8<I1-T{#m3nEJC3<N^ ziKTfN`Dv*|dMT-zdR$=BAR0j?7Nw>t6z8XvlqVLYS}9cKmntMD<|!1VreqeE6lErr zmZU0VmMA3VrRe7uDdgs-WTsVuO~@=!D9uYrEmA1SNL46FEy^thIS9mY_X|~UPt8j$ zO3YCRC{4=AOjhv8Ois-!PE|-ORww|86lbKSC?r7)0%>srxgr?q3I(_P(!7+!lFa-( z9fj1)l8n?Mg|gJ5;>`R!1!I^cP!n|&@{7RE(MT)-Ilf3CzW}6BQz0?0QXwa?1fdt* z=Lip`C}ie=y^)b$kg8CUkyrxqa(QM>jzUtZLTPboT4|0B*p{Ty5`}Qj5D)**5CuoS zNQH36pdd%TkVtEV^30Nq{L&JI)Us5FS(&*7Ihm;`3gwAKMTvPOl?wT3U?Y58gPc7a z{X!g_JbgSvA{FwB6x=*R{9J>B72N!T6dV--9D_nUokM*bgA@Wng97}6UG)?cf>S{b zO9h*NCw$Yuaa5G5kdj)Gn3+=y^>bu?sX}o^erZmMLPla)szOm}a%yH-YKlUlLUMiq z$Z25j<2D#<Z(>e<UOL#LkaVY~px~LNke6Sgqfnfhs$c_4dgbNidg*zkdih1^`Z<t@ zFV?ry<9Z3oQ+}F^w^&^BN^><CZ*dl+mK0^i=cSftGT!1!tw>FdFD=Q;DNY6{M8=G; z3@E_Bz>vxi#hAhn#gxhn%51?5noPGi!V-%z6Z1-n{WMu`am2^xCFZ8a$KT?LkI&6d zDa`?~dE(;>OA~V-GDXY`3=Fq816)IbJmdXbLsl{rfl2@{@heh4v^ce>SiiVDKPOG! zCAB!aB)>pEH#09YFFigzvqZnRv>>%ew+v#XZfZqAPJU5pk$!PevVLk=VooVI<?5HG zfjkBi$uH6?s4M~%BvPP)goS~DfrEjA5iC;0YNThRXH>;%tY@HS@XNLYl+(-8bn{Y6 z6p}Mile0lNJ~tl}UnTkAh=OG9#GLeey~Mo4oXTQwk&v97n4YT1e2X_FwV)_9Ik6-) zB|bCn7FSU!STl&t4ow%ypa6h{Fo+FukTb|-N(>APB@7D~Qy5E_7BVq1a59uIFJP%* zTF98iTEkGon8MTxDwvr4ig*|p7(nq>kXV$Mn_7}uRIJH%i=#ZT2ozAow^%@|TS6{S zw`At$g+ru@Kz7~YPERd~hlb%T*2IE>oJvjRTg-W>B}Je}zQqoe1IG!3;AUW8C=z90 zV2}ejl#PLbfrpuiQA7wVrpbGYJw84qKRG_WNDvgeEU9^=xkXGMCO0fVGV}AIq!4kW z2Z<v+s8z*~WWfaqT~JB{JGBTDYqz*yW~8PRal#w|aTh%46oFiLi^B$z=<GnLwpfgT cfq{jALx6{ogNcWUhf#o;gN2Ea<-Zsc0Ci;A=Kufz literal 0 HcmV?d00001 diff --git a/src/evaluation/wf_net/evaluator.py b/src/evaluation/wf_net/evaluator.py new file mode 100644 index 0000000..c270f08 --- /dev/null +++ b/src/evaluation/wf_net/evaluator.py @@ -0,0 +1,52 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +import warnings +from enum import Enum + +import deprecation + +from evaluation.wf_net.variants import petri_net +from pm4py.util import exec_utils + + +class Variants(Enum): + PETRI_NET = petri_net + + +@deprecation.deprecated(deprecated_in='2.2.2', removed_in='3.0.0', + details='this wf-net check is moved to the pm4py.algo.analysis package') +def apply(net, parameters=None, variant=Variants.PETRI_NET): + warnings.warn('this wf-net check is moved to the pm4py.algo.analysis package', DeprecationWarning) + """ + Checks if a Petri net is a workflow net + + Parameters + --------------- + net + Petri net + parameters + Parameters of the algorithm + variant + Variant of the algorithm, possibe values: + - Variants.PETRI_NET + + Returns + --------------- + boolean + Boolean value + """ + return exec_utils.get_variant(variant).apply(net, parameters=parameters) diff --git a/src/evaluation/wf_net/variants/__init__.py b/src/evaluation/wf_net/variants/__init__.py new file mode 100644 index 0000000..00ebe89 --- /dev/null +++ b/src/evaluation/wf_net/variants/__init__.py @@ -0,0 +1,17 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +from evaluation.wf_net.variants import petri_net diff --git a/src/evaluation/wf_net/variants/__pycache__/__init__.cpython-310.pyc b/src/evaluation/wf_net/variants/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c60fae0298498dca73b0eee057fbcb1a322423b8 GIT binary patch literal 981 zcmd1j<>g{vU|=x6<C`wP%)sy%#6iYP3=9ko3=9m#A`A=+DGX5zDU2yhIgGhXQA~^s zDa^qPnk<ROnHU(j6ciK`LNYRo71A<uQWY|b6$%oIN)+<b6asur0xA_WeDjM^6+H9O z@~spyN=gcft@QN^a!m><_0lp+^wNqFOY<`F(^8A{Qc^YbxWJ}CG=fYlN=;QL&QB{T zPb^BcQmD)?RY*?EQz%MJ$t*4@%1kOPNma-!QAo^7(a$eZ$jwj5OsfQ&kXfQonwOGV zq)?KPs!)<zlv@mP5QyXM7pmZ%nwMIXn4=I-nv|27tl*QGoSIjhs*qT$PyiAs&PYvB zNP-vy(&7ekMKIJA3U2wOc`1n{nfZA-3aObT8L34IWvNBQnfZAN#xP5uCh92U7lECl zkyrw9e33$a0Z5~!LSkN}LQY}{LNB_{5gtrY$jk$KBO|{cRiPv!u>|Dh^30qZg``x4 z(&E&#(i|PIElH&%3gMn19{!;r3XXn}3gM1HL5_YQk=6?3nI#$dr6mffWvLLeGII-Z zGE-9&$`gx<67xzb74p--M)<l0IeR$zg*ZBS`gn#!D&!X_xOs;7xdsO-xcLVuI4T4< z28DP!hx#}MDFlQD1^5TM>M1A$r-B@o3N`^x_@;s5s3=t-CAB0mGp88p=g9n0h2o6- z(wr29jKs23g`(8t)XcKf6oo{E<op7V)4<-xZ7|s0#GL%Rbg)Mu=}u2U!81)EFTX@b zp*S^F!3LJ}%FE03((_97@{7{-b0864tZ%2s^%9i!H5qSl7NnLGWya^FmiTEh-(ruC zPsvY?kH00AT9%ko3XXQY@-&dNURh#MW@27RaS<q|tYj!+Wnh31zY_IBi&Kk=^^42% zbJFx(Qj4=o@(c8HGxIX@(&N)JOZ1CN3sQ@8p$6!tRutsq7o`^I7Z)Y#Bka+K*rN|s nR;(W%pP83g5+AQuP<e~PCO1E&G$+-L5tKz)7#J8>7z6|W`7|?% literal 0 HcmV?d00001 diff --git a/src/evaluation/wf_net/variants/__pycache__/petri_net.cpython-310.pyc b/src/evaluation/wf_net/variants/__pycache__/petri_net.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..963b1854ad3fc02917178a81b5cdb3666ea9d2bd GIT binary patch literal 2709 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 M0Z>-uVCK^R03>VqE&u=k literal 0 HcmV?d00001 diff --git a/src/evaluation/wf_net/variants/petri_net.py b/src/evaluation/wf_net/variants/petri_net.py new file mode 100644 index 0000000..39dfcaf --- /dev/null +++ b/src/evaluation/wf_net/variants/petri_net.py @@ -0,0 +1,101 @@ +''' + This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). + + PM4Py is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PM4Py is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PM4Py. If not, see <https://www.gnu.org/licenses/>. +''' +import copy + +from pm4py.objects.petri_net.utils import petri_utils as pn_utils +from pm4py.objects.petri_net.obj import PetriNet + + +def _short_circuit_petri_net(net): + """ + Creates a short circuited Petri net, + whether an unique source place and sink place are there, + by connecting the sink with the source + + Parameters + --------------- + net + Petri net + + Returns + --------------- + boolean + Boolean value + """ + s_c_net = copy.deepcopy(net) + no_source_places = 0 + no_sink_places = 0 + sink = None + source = None + for place in s_c_net.places: + if len(place.in_arcs) == 0: + source = place + no_source_places += 1 + if len(place.out_arcs) == 0: + sink = place + no_sink_places += 1 + if (sink is not None) and (source is not None) and no_source_places == 1 and no_sink_places == 1: + # If there is one unique source and sink place, short circuit Petri Net is constructed + t_1 = PetriNet.Transition("short_circuited_transition", "short_circuited_transition") + s_c_net.transitions.add(t_1) + # add arcs in short-circuited net + pn_utils.add_arc_from_to(sink, t_1, s_c_net) + pn_utils.add_arc_from_to(t_1, source, s_c_net) + return s_c_net + else: + return None + + +def apply(net, parameters=None): + """ + Checks if a Petri net is a workflow net + + Parameters + --------------- + net + Petri net + parameters + Parameters of the algorithm + + Returns + --------------- + boolean + Boolean value + """ + if parameters is None: + parameters = {} + + import networkx as nx + + scnet = _short_circuit_petri_net(net) + if scnet is None: + return False + nodes = scnet.transitions | scnet.places + graph = nx.DiGraph() + while len(nodes) > 0: + element = nodes.pop() + graph.add_node(element.name) + for in_arc in element.in_arcs: + graph.add_node(in_arc.source.name) + graph.add_edge(in_arc.source.name, element.name) + for out_arc in element.out_arcs: + graph.add_node(out_arc.target.name) + graph.add_edge(element.name, out_arc.target.name) + if nx.algorithms.components.is_strongly_connected(graph): + return True + else: + return False diff --git a/src/frontend/src/app/components/header-bar/header-bar.component.ts b/src/frontend/src/app/components/header-bar/header-bar.component.ts index 3112d6c..6466660 100644 --- a/src/frontend/src/app/components/header-bar/header-bar.component.ts +++ b/src/frontend/src/app/components/header-bar/header-bar.component.ts @@ -77,7 +77,7 @@ export class HeaderBarComponent implements OnDestroy { //TODO: remove comments importOCEL(): void { - console.log("We try to upload an OCEL, but nothing is yet implemented") + //console.log("We try to upload an OCEL, but nothing is yet implemented") this.fileUploadOCEL.nativeElement.click(); } @@ -89,7 +89,8 @@ export class HeaderBarComponent implements OnDestroy { let backendCall; if (!environment.electron) { console.log('Debug: Electron is not the detected environment, using uploadEventLog.'); - backendCall = this.backendService.uploadEventLog(fileList[0]); + //backendCall = this.backendService.uploadEventLog(fileList[0]); + backendCall = this.backendService.uploadOCEL(fileList[0]); } else { console.log('Debug: Electron environment detected, using loadEventLogFromFilePath.'); backendCall = this.backendService.loadEventLogFromFilePath(fileList[0]['path']); @@ -97,9 +98,22 @@ export class HeaderBarComponent implements OnDestroy { this.loadingOverlayService.showLoader( 'Importing OCEL. For large logs this can take up to several minutes' ); - backendCall.subscribe(() => { - this.loadingOverlayService.hideLoader(); + backendCall.subscribe({ + next: () => { + this.loadingOverlayService.hideLoader(); + }, + error: (err) => { + console.error('Error importing OCEL:', err); + this.loadingOverlayService.hideLoader(); + }, + complete: () => { + this.loadingOverlayService.hideLoader(); + } }); + + //backendCall.subscribe(() => { + //this.loadingOverlayService.hideLoader(); + //}); } // reset form diff --git a/src/frontend/src/app/services/backendService/backend.service.ts b/src/frontend/src/app/services/backendService/backend.service.ts index 2c4c378..c65e150 100644 --- a/src/frontend/src/app/services/backendService/backend.service.ts +++ b/src/frontend/src/app/services/backendService/backend.service.ts @@ -92,6 +92,25 @@ export class BackendService { ); } + uploadOCEL(file: File) { + let formData = new FormData(); + formData.append('file', file); + + return this.httpClient + .post( + ROUTES.HTTP_BASE_URL + ROUTES.IMPORT + 'loadOCELFromFile', + formData + ) + .pipe( + mapVariants(), + tap((res) => { + this.logService.processOCEL(res, file['path']); + }) + ); + } + + + loadProcessTreeFromFilePath(filePath: string): void { this.httpClient .post( diff --git a/src/frontend/src/app/services/logService/log.service.ts b/src/frontend/src/app/services/logService/log.service.ts index f3a8322..8cba08e 100644 --- a/src/frontend/src/app/services/logService/log.service.ts +++ b/src/frontend/src/app/services/logService/log.service.ts @@ -250,6 +250,9 @@ export class LogService { this.logGranularity = res['timeGranularity']; this.logModifications = []; } + + public processOCEL(res, filePath = null) { + } } export class LogStats { -- GitLab