Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "simod"
version = "5.1.5"
version = "5.1.6"
authors = [
"Ihar Suvorau <ihar.suvorau@gmail.com>",
"David Chapela <david.chapela@ut.ee>",
Expand Down
11 changes: 5 additions & 6 deletions src/simod/control_flow/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,11 @@ def add_bpmn_diagram_to_model(bpmn_model_path: Path):
"""
global bpmn_layout_jar_path

args = [
"java",
"-jar",
'"' + str(bpmn_layout_jar_path) + '"',
'"' + str(bpmn_model_path) + '"'
]
if is_windows():
args = ["java", "-jar", '"' + str(bpmn_layout_jar_path) + '"', '"' + str(bpmn_model_path) + '"']
else:
args = ["java", "-jar", str(bpmn_layout_jar_path), str(bpmn_model_path)]

print_step(f"Adding BPMN diagram to the model: {args}")
execute_external_command(args)

Expand Down
6 changes: 5 additions & 1 deletion src/simod/simod.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from simod.resource_model.repair import repair_with_missing_activities
from simod.resource_model.settings import HyperoptIterationParams as ResourceModelHyperoptIterationParams
from simod.runtime_meter import RuntimeMeter
from simod.settings.control_flow_settings import ProcessModelDiscoveryAlgorithm
from simod.settings.simod_settings import SimodSettings
from simod.simulation.parameters.BPS_model import BPSModel
from simod.simulation.prosimos import simulate_and_evaluate
Expand Down Expand Up @@ -213,7 +214,10 @@ def run(self, runtimes: Optional[RuntimeMeter] = None):
)
# Instantiate event log to discover the process model with
xes_log_path = self._best_result_dir / f"{self._event_log.process_name}_train_val.xes"
self._event_log.train_validation_to_xes(xes_log_path)
if best_control_flow_params.mining_algorithm is ProcessModelDiscoveryAlgorithm.SPLIT_MINER_V1:
self._event_log.train_validation_to_xes(xes_log_path, only_complete_events=True)
else:
self._event_log.train_validation_to_xes(xes_log_path)
# Discover the process model
discover_process_model(
log_path=xes_log_path,
Expand Down
99 changes: 99 additions & 0 deletions tests/assets/process_model_with_SplitMiner_self_loops.bpmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?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">
<startEvent id="node_bd0aff33-74bf-4708-b70c-537f73d32d2c" name=""/>
<endEvent id="node_78c1c673-495d-4b8d-b05d-42638d5de46c" name=""/>
<task id="node_a0945a12-7e36-4a15-92c6-4a4973eda9b9" name="W_Handle leads">
<standardLoopCharacteristics testBefore="false"/>
</task>
<task id="node_4f108168-54a6-456b-acf5-f98d4e902c68" name="W_Call after offers">
<standardLoopCharacteristics testBefore="false"/>
</task>
<task id="node_4dc21123-2cff-482e-b19d-7ef5d11bc12c" name="End"/>
<task id="node_ffd70b5b-8db5-4b3f-ae73-3a5eede600a5" name="W_Call incomplete files">
<standardLoopCharacteristics testBefore="false"/>
</task>
<task id="node_db5c7824-e7d7-4008-8f23-dea554d6bc29" name="W_Validate application">
<standardLoopCharacteristics testBefore="false"/>
</task>
<task id="node_d2d9db90-141c-4dd9-a08d-c310e74bb1d4" name="W_Complete application">
<standardLoopCharacteristics testBefore="false"/>
</task>
<task id="node_7183cdb2-6d64-4af7-8110-cfbd65d39c70" name="Start"/>
<task id="node_a6399e55-fa52-41cc-9bc5-d267db419a0c" name="W_Assess potential fraud">
<standardLoopCharacteristics testBefore="false"/>
</task>
<exclusiveGateway id="node_9cb9e9ff-853b-4b1b-aa04-d463cb2e79e4" name="" gatewayDirection="Converging">
<incoming>
node_01d8c20e-52d6-4fbf-80d1-f7d458406138</incoming>
<incoming>
node_c450dccc-8229-4e76-b957-494c44fec17e</incoming>
<outgoing>
node_2b39336a-70f4-4dea-a952-b6f543777b5d</outgoing>
</exclusiveGateway>
<exclusiveGateway id="node_0fd985e7-d73d-4689-957a-515b0e17b2f0" name="" gatewayDirection="Converging">
<incoming>
node_4cf191ad-01b2-4038-8b26-18a164000b05</incoming>
<incoming>
node_24fc93fc-4b66-4f44-9e8e-a649a3fcdc12</incoming>
<outgoing>
node_135b1874-4857-4b7a-a81d-50beb311441c</outgoing>
</exclusiveGateway>
<exclusiveGateway id="node_2747f1fc-aa96-4232-96a1-45ccb73333b0" name="" gatewayDirection="Diverging">
<incoming>
node_cbce22a3-4f84-4bea-bc66-d6acc60d873a</incoming>
<outgoing>
node_4cf191ad-01b2-4038-8b26-18a164000b05</outgoing>
<outgoing>
node_6c2703c8-ee82-43e5-ba14-1721cf5138c9</outgoing>
</exclusiveGateway>
<exclusiveGateway id="node_477528f4-b023-46d5-9ba8-68ea7be789eb" name="" gatewayDirection="Diverging">
<incoming>
node_2ea0cee5-c9bf-4d90-bf38-82614ca84894</incoming>
<outgoing>
node_a1fdda3a-cf96-4980-a32c-cac63028ef50</outgoing>
<outgoing>
node_b96514e9-abb8-47da-9ce9-6ef16bd246db</outgoing>
<outgoing>
node_5679761c-c5d3-4e3a-8248-13fd7b48ab95</outgoing>
</exclusiveGateway>
<exclusiveGateway id="node_ca100f94-484b-4ec3-8f0c-3bcef06973c8" name="" gatewayDirection="Diverging">
<incoming>
node_6659ae33-5879-40ee-9b4c-12113190ead1</incoming>
<outgoing>
node_01d8c20e-52d6-4fbf-80d1-f7d458406138</outgoing>
<outgoing>
node_83305431-0a61-43bc-ac36-3acd90ad606c</outgoing>
</exclusiveGateway>
<exclusiveGateway id="node_7bae894c-932a-476f-b91e-911220376611" name="" gatewayDirection="Converging">
<incoming>
node_46301cea-0047-452b-a608-16a7e9f79cfe</incoming>
<incoming>
node_80986967-4875-41c2-828f-3e144d755b81</incoming>
<incoming>
node_5679761c-c5d3-4e3a-8248-13fd7b48ab95</incoming>
<outgoing>
node_c450dccc-8229-4e76-b957-494c44fec17e</outgoing>
</exclusiveGateway>
<sequenceFlow id="node_e3c70fd3-097e-49d2-8710-9cb4ec658576" name="" sourceRef="node_d2d9db90-141c-4dd9-a08d-c310e74bb1d4" targetRef="node_4f108168-54a6-456b-acf5-f98d4e902c68"/>
<sequenceFlow id="node_e50aa326-4026-4f7f-8050-f2e5f4ef4286" name="" sourceRef="node_4dc21123-2cff-482e-b19d-7ef5d11bc12c" targetRef="node_78c1c673-495d-4b8d-b05d-42638d5de46c"/>
<sequenceFlow id="node_08a48377-5512-4dfd-9e96-fde430046296" name="" sourceRef="node_bd0aff33-74bf-4708-b70c-537f73d32d2c" targetRef="node_7183cdb2-6d64-4af7-8110-cfbd65d39c70"/>
<sequenceFlow id="node_cbce22a3-4f84-4bea-bc66-d6acc60d873a" name="" sourceRef="node_7183cdb2-6d64-4af7-8110-cfbd65d39c70" targetRef="node_2747f1fc-aa96-4232-96a1-45ccb73333b0"/>
<sequenceFlow id="node_6c2703c8-ee82-43e5-ba14-1721cf5138c9" name="" sourceRef="node_2747f1fc-aa96-4232-96a1-45ccb73333b0" targetRef="node_a0945a12-7e36-4a15-92c6-4a4973eda9b9"/>
<sequenceFlow id="node_6659ae33-5879-40ee-9b4c-12113190ead1" name="" sourceRef="node_4f108168-54a6-456b-acf5-f98d4e902c68" targetRef="node_ca100f94-484b-4ec3-8f0c-3bcef06973c8"/>
<sequenceFlow id="node_83305431-0a61-43bc-ac36-3acd90ad606c" name="" sourceRef="node_ca100f94-484b-4ec3-8f0c-3bcef06973c8" targetRef="node_db5c7824-e7d7-4008-8f23-dea554d6bc29"/>
<sequenceFlow id="node_2ea0cee5-c9bf-4d90-bf38-82614ca84894" name="" sourceRef="node_db5c7824-e7d7-4008-8f23-dea554d6bc29" targetRef="node_477528f4-b023-46d5-9ba8-68ea7be789eb"/>
<sequenceFlow id="node_a1fdda3a-cf96-4980-a32c-cac63028ef50" name="" sourceRef="node_477528f4-b023-46d5-9ba8-68ea7be789eb" targetRef="node_a6399e55-fa52-41cc-9bc5-d267db419a0c"/>
<sequenceFlow id="node_b96514e9-abb8-47da-9ce9-6ef16bd246db" name="" sourceRef="node_477528f4-b023-46d5-9ba8-68ea7be789eb" targetRef="node_ffd70b5b-8db5-4b3f-ae73-3a5eede600a5"/>
<sequenceFlow id="node_80986967-4875-41c2-828f-3e144d755b81" name="" sourceRef="node_a6399e55-fa52-41cc-9bc5-d267db419a0c" targetRef="node_7bae894c-932a-476f-b91e-911220376611"/>
<sequenceFlow id="node_46301cea-0047-452b-a608-16a7e9f79cfe" name="" sourceRef="node_ffd70b5b-8db5-4b3f-ae73-3a5eede600a5" targetRef="node_7bae894c-932a-476f-b91e-911220376611"/>
<sequenceFlow id="node_5679761c-c5d3-4e3a-8248-13fd7b48ab95" name="" sourceRef="node_477528f4-b023-46d5-9ba8-68ea7be789eb" targetRef="node_7bae894c-932a-476f-b91e-911220376611"/>
<sequenceFlow id="node_135b1874-4857-4b7a-a81d-50beb311441c" name="" sourceRef="node_0fd985e7-d73d-4689-957a-515b0e17b2f0" targetRef="node_d2d9db90-141c-4dd9-a08d-c310e74bb1d4"/>
<sequenceFlow id="node_24fc93fc-4b66-4f44-9e8e-a649a3fcdc12" name="" sourceRef="node_a0945a12-7e36-4a15-92c6-4a4973eda9b9" targetRef="node_0fd985e7-d73d-4689-957a-515b0e17b2f0"/>
<sequenceFlow id="node_4cf191ad-01b2-4038-8b26-18a164000b05" name="" sourceRef="node_2747f1fc-aa96-4232-96a1-45ccb73333b0" targetRef="node_0fd985e7-d73d-4689-957a-515b0e17b2f0"/>
<sequenceFlow id="node_2b39336a-70f4-4dea-a952-b6f543777b5d" name="" sourceRef="node_9cb9e9ff-853b-4b1b-aa04-d463cb2e79e4" targetRef="node_4dc21123-2cff-482e-b19d-7ef5d11bc12c"/>
<sequenceFlow id="node_c450dccc-8229-4e76-b957-494c44fec17e" name="" sourceRef="node_7bae894c-932a-476f-b91e-911220376611" targetRef="node_9cb9e9ff-853b-4b1b-aa04-d463cb2e79e4"/>
<sequenceFlow id="node_01d8c20e-52d6-4fbf-80d1-f7d458406138" name="" sourceRef="node_ca100f94-484b-4ec3-8f0c-3bcef06973c8" targetRef="node_9cb9e9ff-853b-4b1b-aa04-d463cb2e79e4"/>
</process>
<bpmndi:BPMNDiagram id="id_161575504">
<bpmndi:BPMNPlane bpmnElement="proc_202125197"/>
</bpmndi:BPMNDiagram>
</definitions>
48 changes: 47 additions & 1 deletion tests/test_control_flow/test_discovery.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import shutil
import tempfile
from pathlib import Path

Expand All @@ -6,7 +7,7 @@
from pix_framework.discovery.gateway_probabilities import GatewayProbabilitiesDiscoveryMethod
from pix_framework.io.bpmn import get_activities_names_from_bpmn

from simod.control_flow.discovery import discover_process_model
from simod.control_flow.discovery import discover_process_model, post_process_bpmn_self_loops
from simod.control_flow.settings import HyperoptIterationParams
from simod.settings.common_settings import Metric
from simod.settings.control_flow_settings import ProcessModelDiscoveryAlgorithm
Expand Down Expand Up @@ -103,3 +104,48 @@ def test_discover_process_model_explicit_self_loops(entry_point, test_data):
# Commented because SM2 doesn't sort the events, thus no parallelism
# parallel_gateways = root.findall(".//bpmn:parallelGateway", namespaces=ns)
# assert len(parallel_gateways) == 2, "There should only be two parallel gateways in this model"


def test_transform_process_model_explicit_self_loops(entry_point):
with tempfile.TemporaryDirectory() as tmp_dir:
# Copy source model with self-loops
original_model_path = entry_point / "process_model_with_SplitMiner_self_loops.bpmn"
model_path = Path(tmp_dir) / "process_model_with_SplitMiner_self_loops.bpmn"
shutil.copy(original_model_path, model_path)
# Fix process model with self-loops in all activities except Start and End
post_process_bpmn_self_loops(model_path)
# Assert that no implicit self-loops are there
tree = etree.parse(model_path)
root = tree.getroot()
ns = {"bpmn": root.nsmap.get(None, "http://www.omg.org/spec/BPMN/20100524/MODEL")}
tasks = root.findall(".//bpmn:task", namespaces=ns)
for task in tasks:
assert task.find(
"bpmn:standardLoopCharacteristics",
namespaces=ns
) is None, f"Task '{task.get('name')}' has an implicit self loop"
if task.get("name") == "Start":
# Find the incoming flow of the "Start" task
task_id = task.get("id")
sequence_flows = root.findall(".//bpmn:sequenceFlow", namespaces=ns)
incoming_flows = [flow for flow in sequence_flows if flow.get("targetRef") == task_id]
assert len(incoming_flows) == 1, f"Task 'Start' should have exactly one incoming flow"
# Assert that the source element of the incoming flow is the start event
incoming_flow_source = incoming_flows[0].get("sourceRef")
start_events = root.findall(".//bpmn:startEvent", namespaces=ns)
start_event_ids = {event.get("id") for event in start_events}
assert incoming_flow_source in start_event_ids, f"'Start' task was modified."
elif task.get("name") == "End":
# Find the outgoing flow of the "End" task
task_id = task.get("id")
sequence_flows = root.findall(".//bpmn:sequenceFlow", namespaces=ns)
outgoing_flows = [flow for flow in sequence_flows if flow.get("sourceRef") == task_id]
assert len(outgoing_flows) == 1, f"Task 'End' should have exactly one outgoing flow"
# Assert that the target element of the outgoing flow is the end event
outgoing_flow_target = outgoing_flows[0].get("targetRef")
end_events = root.findall(".//bpmn:endEvent", namespaces=ns)
end_event_ids = {event.get("id") for event in end_events}
assert outgoing_flow_target in end_event_ids, f"'End' task was modified."
# Verify number of gateways is original + 2 per self-loop activity
exclusive_gateways = root.findall(".//bpmn:exclusiveGateway", namespaces=ns)
assert len(exclusive_gateways) == 18, "There should only be 18 exclusive gateways in this model"