Skip to content

Commit 56cd99f

Browse files
Merge pull request #171 from AutomatedProcessImprovement/control-flow-discovery-fix
Control flow discovery fix 1. Do not escape paths with double quotes in non-Windows systems to avoid errors. 2. Discovery of final BPS model now uses the correct XES log transformation when SplitMiner1 is the discovery algorithm (previously, it created a XES with both start and end events, and SplitMiner interpreted this as self-loops in all activities).
2 parents 02afc5f + dc8fc8f commit 56cd99f

File tree

5 files changed

+157
-9
lines changed

5 files changed

+157
-9
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
66
name = "simod"
7-
version = "5.1.5"
7+
version = "5.1.6"
88
authors = [
99
"Ihar Suvorau <[email protected]>",
1010
"David Chapela <[email protected]>",

src/simod/control_flow/discovery.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,11 @@ def add_bpmn_diagram_to_model(bpmn_model_path: Path):
140140
"""
141141
global bpmn_layout_jar_path
142142

143-
args = [
144-
"java",
145-
"-jar",
146-
'"' + str(bpmn_layout_jar_path) + '"',
147-
'"' + str(bpmn_model_path) + '"'
148-
]
143+
if is_windows():
144+
args = ["java", "-jar", '"' + str(bpmn_layout_jar_path) + '"', '"' + str(bpmn_model_path) + '"']
145+
else:
146+
args = ["java", "-jar", str(bpmn_layout_jar_path), str(bpmn_model_path)]
147+
149148
print_step(f"Adding BPMN diagram to the model: {args}")
150149
execute_external_command(args)
151150

src/simod/simod.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from simod.resource_model.repair import repair_with_missing_activities
3131
from simod.resource_model.settings import HyperoptIterationParams as ResourceModelHyperoptIterationParams
3232
from simod.runtime_meter import RuntimeMeter
33+
from simod.settings.control_flow_settings import ProcessModelDiscoveryAlgorithm
3334
from simod.settings.simod_settings import SimodSettings
3435
from simod.simulation.parameters.BPS_model import BPSModel
3536
from simod.simulation.prosimos import simulate_and_evaluate
@@ -213,7 +214,10 @@ def run(self, runtimes: Optional[RuntimeMeter] = None):
213214
)
214215
# Instantiate event log to discover the process model with
215216
xes_log_path = self._best_result_dir / f"{self._event_log.process_name}_train_val.xes"
216-
self._event_log.train_validation_to_xes(xes_log_path)
217+
if best_control_flow_params.mining_algorithm is ProcessModelDiscoveryAlgorithm.SPLIT_MINER_V1:
218+
self._event_log.train_validation_to_xes(xes_log_path, only_complete_events=True)
219+
else:
220+
self._event_log.train_validation_to_xes(xes_log_path)
217221
# Discover the process model
218222
discover_process_model(
219223
log_path=xes_log_path,
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?xml version="1.0" encoding="UTF-8"?><definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="http://www.omg.org/bpmn20" exporter="ProM. http://www.promtools.org/prom6" exporterVersion="6.3" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"><process id="proc_202125197">
2+
<startEvent id="node_bd0aff33-74bf-4708-b70c-537f73d32d2c" name=""/>
3+
<endEvent id="node_78c1c673-495d-4b8d-b05d-42638d5de46c" name=""/>
4+
<task id="node_a0945a12-7e36-4a15-92c6-4a4973eda9b9" name="W_Handle leads">
5+
<standardLoopCharacteristics testBefore="false"/>
6+
</task>
7+
<task id="node_4f108168-54a6-456b-acf5-f98d4e902c68" name="W_Call after offers">
8+
<standardLoopCharacteristics testBefore="false"/>
9+
</task>
10+
<task id="node_4dc21123-2cff-482e-b19d-7ef5d11bc12c" name="End"/>
11+
<task id="node_ffd70b5b-8db5-4b3f-ae73-3a5eede600a5" name="W_Call incomplete files">
12+
<standardLoopCharacteristics testBefore="false"/>
13+
</task>
14+
<task id="node_db5c7824-e7d7-4008-8f23-dea554d6bc29" name="W_Validate application">
15+
<standardLoopCharacteristics testBefore="false"/>
16+
</task>
17+
<task id="node_d2d9db90-141c-4dd9-a08d-c310e74bb1d4" name="W_Complete application">
18+
<standardLoopCharacteristics testBefore="false"/>
19+
</task>
20+
<task id="node_7183cdb2-6d64-4af7-8110-cfbd65d39c70" name="Start"/>
21+
<task id="node_a6399e55-fa52-41cc-9bc5-d267db419a0c" name="W_Assess potential fraud">
22+
<standardLoopCharacteristics testBefore="false"/>
23+
</task>
24+
<exclusiveGateway id="node_9cb9e9ff-853b-4b1b-aa04-d463cb2e79e4" name="" gatewayDirection="Converging">
25+
<incoming>
26+
node_01d8c20e-52d6-4fbf-80d1-f7d458406138</incoming>
27+
<incoming>
28+
node_c450dccc-8229-4e76-b957-494c44fec17e</incoming>
29+
<outgoing>
30+
node_2b39336a-70f4-4dea-a952-b6f543777b5d</outgoing>
31+
</exclusiveGateway>
32+
<exclusiveGateway id="node_0fd985e7-d73d-4689-957a-515b0e17b2f0" name="" gatewayDirection="Converging">
33+
<incoming>
34+
node_4cf191ad-01b2-4038-8b26-18a164000b05</incoming>
35+
<incoming>
36+
node_24fc93fc-4b66-4f44-9e8e-a649a3fcdc12</incoming>
37+
<outgoing>
38+
node_135b1874-4857-4b7a-a81d-50beb311441c</outgoing>
39+
</exclusiveGateway>
40+
<exclusiveGateway id="node_2747f1fc-aa96-4232-96a1-45ccb73333b0" name="" gatewayDirection="Diverging">
41+
<incoming>
42+
node_cbce22a3-4f84-4bea-bc66-d6acc60d873a</incoming>
43+
<outgoing>
44+
node_4cf191ad-01b2-4038-8b26-18a164000b05</outgoing>
45+
<outgoing>
46+
node_6c2703c8-ee82-43e5-ba14-1721cf5138c9</outgoing>
47+
</exclusiveGateway>
48+
<exclusiveGateway id="node_477528f4-b023-46d5-9ba8-68ea7be789eb" name="" gatewayDirection="Diverging">
49+
<incoming>
50+
node_2ea0cee5-c9bf-4d90-bf38-82614ca84894</incoming>
51+
<outgoing>
52+
node_a1fdda3a-cf96-4980-a32c-cac63028ef50</outgoing>
53+
<outgoing>
54+
node_b96514e9-abb8-47da-9ce9-6ef16bd246db</outgoing>
55+
<outgoing>
56+
node_5679761c-c5d3-4e3a-8248-13fd7b48ab95</outgoing>
57+
</exclusiveGateway>
58+
<exclusiveGateway id="node_ca100f94-484b-4ec3-8f0c-3bcef06973c8" name="" gatewayDirection="Diverging">
59+
<incoming>
60+
node_6659ae33-5879-40ee-9b4c-12113190ead1</incoming>
61+
<outgoing>
62+
node_01d8c20e-52d6-4fbf-80d1-f7d458406138</outgoing>
63+
<outgoing>
64+
node_83305431-0a61-43bc-ac36-3acd90ad606c</outgoing>
65+
</exclusiveGateway>
66+
<exclusiveGateway id="node_7bae894c-932a-476f-b91e-911220376611" name="" gatewayDirection="Converging">
67+
<incoming>
68+
node_46301cea-0047-452b-a608-16a7e9f79cfe</incoming>
69+
<incoming>
70+
node_80986967-4875-41c2-828f-3e144d755b81</incoming>
71+
<incoming>
72+
node_5679761c-c5d3-4e3a-8248-13fd7b48ab95</incoming>
73+
<outgoing>
74+
node_c450dccc-8229-4e76-b957-494c44fec17e</outgoing>
75+
</exclusiveGateway>
76+
<sequenceFlow id="node_e3c70fd3-097e-49d2-8710-9cb4ec658576" name="" sourceRef="node_d2d9db90-141c-4dd9-a08d-c310e74bb1d4" targetRef="node_4f108168-54a6-456b-acf5-f98d4e902c68"/>
77+
<sequenceFlow id="node_e50aa326-4026-4f7f-8050-f2e5f4ef4286" name="" sourceRef="node_4dc21123-2cff-482e-b19d-7ef5d11bc12c" targetRef="node_78c1c673-495d-4b8d-b05d-42638d5de46c"/>
78+
<sequenceFlow id="node_08a48377-5512-4dfd-9e96-fde430046296" name="" sourceRef="node_bd0aff33-74bf-4708-b70c-537f73d32d2c" targetRef="node_7183cdb2-6d64-4af7-8110-cfbd65d39c70"/>
79+
<sequenceFlow id="node_cbce22a3-4f84-4bea-bc66-d6acc60d873a" name="" sourceRef="node_7183cdb2-6d64-4af7-8110-cfbd65d39c70" targetRef="node_2747f1fc-aa96-4232-96a1-45ccb73333b0"/>
80+
<sequenceFlow id="node_6c2703c8-ee82-43e5-ba14-1721cf5138c9" name="" sourceRef="node_2747f1fc-aa96-4232-96a1-45ccb73333b0" targetRef="node_a0945a12-7e36-4a15-92c6-4a4973eda9b9"/>
81+
<sequenceFlow id="node_6659ae33-5879-40ee-9b4c-12113190ead1" name="" sourceRef="node_4f108168-54a6-456b-acf5-f98d4e902c68" targetRef="node_ca100f94-484b-4ec3-8f0c-3bcef06973c8"/>
82+
<sequenceFlow id="node_83305431-0a61-43bc-ac36-3acd90ad606c" name="" sourceRef="node_ca100f94-484b-4ec3-8f0c-3bcef06973c8" targetRef="node_db5c7824-e7d7-4008-8f23-dea554d6bc29"/>
83+
<sequenceFlow id="node_2ea0cee5-c9bf-4d90-bf38-82614ca84894" name="" sourceRef="node_db5c7824-e7d7-4008-8f23-dea554d6bc29" targetRef="node_477528f4-b023-46d5-9ba8-68ea7be789eb"/>
84+
<sequenceFlow id="node_a1fdda3a-cf96-4980-a32c-cac63028ef50" name="" sourceRef="node_477528f4-b023-46d5-9ba8-68ea7be789eb" targetRef="node_a6399e55-fa52-41cc-9bc5-d267db419a0c"/>
85+
<sequenceFlow id="node_b96514e9-abb8-47da-9ce9-6ef16bd246db" name="" sourceRef="node_477528f4-b023-46d5-9ba8-68ea7be789eb" targetRef="node_ffd70b5b-8db5-4b3f-ae73-3a5eede600a5"/>
86+
<sequenceFlow id="node_80986967-4875-41c2-828f-3e144d755b81" name="" sourceRef="node_a6399e55-fa52-41cc-9bc5-d267db419a0c" targetRef="node_7bae894c-932a-476f-b91e-911220376611"/>
87+
<sequenceFlow id="node_46301cea-0047-452b-a608-16a7e9f79cfe" name="" sourceRef="node_ffd70b5b-8db5-4b3f-ae73-3a5eede600a5" targetRef="node_7bae894c-932a-476f-b91e-911220376611"/>
88+
<sequenceFlow id="node_5679761c-c5d3-4e3a-8248-13fd7b48ab95" name="" sourceRef="node_477528f4-b023-46d5-9ba8-68ea7be789eb" targetRef="node_7bae894c-932a-476f-b91e-911220376611"/>
89+
<sequenceFlow id="node_135b1874-4857-4b7a-a81d-50beb311441c" name="" sourceRef="node_0fd985e7-d73d-4689-957a-515b0e17b2f0" targetRef="node_d2d9db90-141c-4dd9-a08d-c310e74bb1d4"/>
90+
<sequenceFlow id="node_24fc93fc-4b66-4f44-9e8e-a649a3fcdc12" name="" sourceRef="node_a0945a12-7e36-4a15-92c6-4a4973eda9b9" targetRef="node_0fd985e7-d73d-4689-957a-515b0e17b2f0"/>
91+
<sequenceFlow id="node_4cf191ad-01b2-4038-8b26-18a164000b05" name="" sourceRef="node_2747f1fc-aa96-4232-96a1-45ccb73333b0" targetRef="node_0fd985e7-d73d-4689-957a-515b0e17b2f0"/>
92+
<sequenceFlow id="node_2b39336a-70f4-4dea-a952-b6f543777b5d" name="" sourceRef="node_9cb9e9ff-853b-4b1b-aa04-d463cb2e79e4" targetRef="node_4dc21123-2cff-482e-b19d-7ef5d11bc12c"/>
93+
<sequenceFlow id="node_c450dccc-8229-4e76-b957-494c44fec17e" name="" sourceRef="node_7bae894c-932a-476f-b91e-911220376611" targetRef="node_9cb9e9ff-853b-4b1b-aa04-d463cb2e79e4"/>
94+
<sequenceFlow id="node_01d8c20e-52d6-4fbf-80d1-f7d458406138" name="" sourceRef="node_ca100f94-484b-4ec3-8f0c-3bcef06973c8" targetRef="node_9cb9e9ff-853b-4b1b-aa04-d463cb2e79e4"/>
95+
</process>
96+
<bpmndi:BPMNDiagram id="id_161575504">
97+
<bpmndi:BPMNPlane bpmnElement="proc_202125197"/>
98+
</bpmndi:BPMNDiagram>
99+
</definitions>

tests/test_control_flow/test_discovery.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import shutil
12
import tempfile
23
from pathlib import Path
34

@@ -6,7 +7,7 @@
67
from pix_framework.discovery.gateway_probabilities import GatewayProbabilitiesDiscoveryMethod
78
from pix_framework.io.bpmn import get_activities_names_from_bpmn
89

9-
from simod.control_flow.discovery import discover_process_model
10+
from simod.control_flow.discovery import discover_process_model, post_process_bpmn_self_loops
1011
from simod.control_flow.settings import HyperoptIterationParams
1112
from simod.settings.common_settings import Metric
1213
from simod.settings.control_flow_settings import ProcessModelDiscoveryAlgorithm
@@ -103,3 +104,48 @@ def test_discover_process_model_explicit_self_loops(entry_point, test_data):
103104
# Commented because SM2 doesn't sort the events, thus no parallelism
104105
# parallel_gateways = root.findall(".//bpmn:parallelGateway", namespaces=ns)
105106
# assert len(parallel_gateways) == 2, "There should only be two parallel gateways in this model"
107+
108+
109+
def test_transform_process_model_explicit_self_loops(entry_point):
110+
with tempfile.TemporaryDirectory() as tmp_dir:
111+
# Copy source model with self-loops
112+
original_model_path = entry_point / "process_model_with_SplitMiner_self_loops.bpmn"
113+
model_path = Path(tmp_dir) / "process_model_with_SplitMiner_self_loops.bpmn"
114+
shutil.copy(original_model_path, model_path)
115+
# Fix process model with self-loops in all activities except Start and End
116+
post_process_bpmn_self_loops(model_path)
117+
# Assert that no implicit self-loops are there
118+
tree = etree.parse(model_path)
119+
root = tree.getroot()
120+
ns = {"bpmn": root.nsmap.get(None, "http://www.omg.org/spec/BPMN/20100524/MODEL")}
121+
tasks = root.findall(".//bpmn:task", namespaces=ns)
122+
for task in tasks:
123+
assert task.find(
124+
"bpmn:standardLoopCharacteristics",
125+
namespaces=ns
126+
) is None, f"Task '{task.get('name')}' has an implicit self loop"
127+
if task.get("name") == "Start":
128+
# Find the incoming flow of the "Start" task
129+
task_id = task.get("id")
130+
sequence_flows = root.findall(".//bpmn:sequenceFlow", namespaces=ns)
131+
incoming_flows = [flow for flow in sequence_flows if flow.get("targetRef") == task_id]
132+
assert len(incoming_flows) == 1, f"Task 'Start' should have exactly one incoming flow"
133+
# Assert that the source element of the incoming flow is the start event
134+
incoming_flow_source = incoming_flows[0].get("sourceRef")
135+
start_events = root.findall(".//bpmn:startEvent", namespaces=ns)
136+
start_event_ids = {event.get("id") for event in start_events}
137+
assert incoming_flow_source in start_event_ids, f"'Start' task was modified."
138+
elif task.get("name") == "End":
139+
# Find the outgoing flow of the "End" task
140+
task_id = task.get("id")
141+
sequence_flows = root.findall(".//bpmn:sequenceFlow", namespaces=ns)
142+
outgoing_flows = [flow for flow in sequence_flows if flow.get("sourceRef") == task_id]
143+
assert len(outgoing_flows) == 1, f"Task 'End' should have exactly one outgoing flow"
144+
# Assert that the target element of the outgoing flow is the end event
145+
outgoing_flow_target = outgoing_flows[0].get("targetRef")
146+
end_events = root.findall(".//bpmn:endEvent", namespaces=ns)
147+
end_event_ids = {event.get("id") for event in end_events}
148+
assert outgoing_flow_target in end_event_ids, f"'End' task was modified."
149+
# Verify number of gateways is original + 2 per self-loop activity
150+
exclusive_gateways = root.findall(".//bpmn:exclusiveGateway", namespaces=ns)
151+
assert len(exclusive_gateways) == 18, "There should only be 18 exclusive gateways in this model"

0 commit comments

Comments
 (0)