diff --git a/.gitignore b/.gitignore
index 838de26..aa1b7f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,6 @@ bimp_test_examples/
*.egg-info
.pymon
.DS_Store
-.vscode/
\ No newline at end of file
+.vscode/
+performance_exp/*/*/results
+performance_exp/events/generated_events
\ No newline at end of file
diff --git a/bpdfr_simulation_engine/control_flow_manager.py b/bpdfr_simulation_engine/control_flow_manager.py
index 0ad8e47..2afbd46 100644
--- a/bpdfr_simulation_engine/control_flow_manager.py
+++ b/bpdfr_simulation_engine/control_flow_manager.py
@@ -95,6 +95,16 @@ def is_start_or_end_event(self):
def is_event(self):
return self.type in [BPMN.START_EVENT, BPMN.END_EVENT, BPMN.INTERMEDIATE_EVENT]
+ def delete_incoming_flow(self, flow_id):
+ "Delete incoming flow if it exists"
+ if flow_id in self.incoming_flows:
+ self.incoming_flows.remove(flow_id)
+
+ def delete_outgoing_flow(self, flow_id):
+ "Delete outgoing flow if it exists"
+ if flow_id in self.outgoing_flows:
+ self.outgoing_flows.remove(flow_id)
+
class ProcessState:
def __init__(self, bpmn_graph):
@@ -171,6 +181,12 @@ def add_bpmn_element(self, element_id, element_info):
self.from_name[element_info.name] = element_id
self.nodes_bitset[element_id] = (1 << len(self.element_info))
+ def remove_incoming_flow(self, element_id, flow_id):
+ self.element_info[element_id].delete_incoming_flow(flow_id)
+
+ def remove_outgoing_flow(self, element_id, flow_id):
+ self.element_info[element_id].delete_outgoing_flow(flow_id)
+
def add_flow_arc(self, flow_id, source_id, target_id):
for node_id in [source_id, target_id]:
if node_id not in self.element_info:
diff --git a/bpdfr_simulation_engine/events_performance_experiments.py b/bpdfr_simulation_engine/events_performance_experiments.py
new file mode 100644
index 0000000..3891caf
--- /dev/null
+++ b/bpdfr_simulation_engine/events_performance_experiments.py
@@ -0,0 +1,72 @@
+import os
+import string
+
+from bpdfr_simulation_engine.control_flow_manager import (
+ BPMN,
+ EVENT_TYPE,
+ BPMNGraph,
+ ElementInfo,
+)
+
+
+def _add_events(
+ bpmn_graph: BPMNGraph,
+ sequence_flow_list,
+ element_probability,
+ num_inserted_events: int,
+):
+ if len(sequence_flow_list) < num_inserted_events:
+ raise ValueError(
+ "A number of inserted events should not be higher than a number of sequence flows present in the BPMN model."
+ )
+
+ # keep track of the added events to the model
+ inserted_events_logs: string = ""
+
+ for i in range(num_inserted_events):
+ # the flow arc to be replaced
+ to_be_deleted_flow_arc = sequence_flow_list[i]
+ to_be_deleted_flow_arc_id = to_be_deleted_flow_arc.attrib["id"]
+
+ source_id = to_be_deleted_flow_arc.attrib["sourceRef"]
+ target_id = to_be_deleted_flow_arc.attrib["targetRef"]
+
+ new_event_id = f"event_{i}"
+
+ # log info about the newly added event
+ inserted_events_logs += f"{new_event_id} between {source_id} and {target_id}.\n"
+
+ # add intermediate event
+ bpmn_graph.add_bpmn_element(
+ new_event_id,
+ ElementInfo(
+ BPMN.INTERMEDIATE_EVENT,
+ new_event_id,
+ new_event_id,
+ EVENT_TYPE.TIMER,
+ ),
+ )
+
+ # remove previously referenced sequence flow in the source & target activity
+ bpmn_graph.remove_outgoing_flow(source_id, to_be_deleted_flow_arc_id)
+ bpmn_graph.remove_incoming_flow(target_id, to_be_deleted_flow_arc_id)
+
+ # add sequence flow to and from the newly added event
+ seq_flow_to_event = f"{source_id}_{new_event_id}"
+ bpmn_graph.add_flow_arc(seq_flow_to_event, source_id, new_event_id)
+ seq_flow_from_event = f"{new_event_id}_{target_id}"
+ bpmn_graph.add_flow_arc(seq_flow_from_event, new_event_id, target_id)
+
+ # duplicate the gateway probability if it existed before
+ if source_id in element_probability:
+ element_probability[source_id].update_candidate_key(
+ to_be_deleted_flow_arc_id, seq_flow_to_event
+ )
+
+ # save logs about events
+ logs_path = os.path.join(
+ os.path.dirname(__file__),
+ f"../performance_exp/events/generated_events/{num_inserted_events}_events_logs.txt",
+ )
+ with open(logs_path, "w+") as logs_file:
+ logs_file.write(inserted_events_logs)
diff --git a/bpdfr_simulation_engine/probability_distributions.py b/bpdfr_simulation_engine/probability_distributions.py
index d7f536a..97a56a7 100644
--- a/bpdfr_simulation_engine/probability_distributions.py
+++ b/bpdfr_simulation_engine/probability_distributions.py
@@ -175,6 +175,20 @@ def get_multiple_flows(self):
selected.append((self.candidates_list[i], None))
return selected if len(selected) > 0 else [(self.get_outgoing_flow(), None)]
+ def update_candidate_key(self, old_candidate_id, new_candidate_id):
+ """
+ After inserting an event, we need to change the candidate id in the list of candidates
+ This needs to happen due to the fact that we removed one gateway and inserted a new one instead
+ """
+
+ try:
+ candidate_index = self.candidates_list.index(old_candidate_id)
+ except ValueError:
+ # in case candidate is not on the list, not updating anything
+ return
+
+ self.candidates_list[candidate_index] = new_candidate_id
+
def random_uniform(start, end):
return numpy.random.uniform(low=start, high=end)
diff --git a/bpdfr_simulation_engine/simulation_engine.py b/bpdfr_simulation_engine/simulation_engine.py
index 4364c0c..bdbd717 100644
--- a/bpdfr_simulation_engine/simulation_engine.py
+++ b/bpdfr_simulation_engine/simulation_engine.py
@@ -644,9 +644,10 @@ def run_simulation(
log_out_path=None,
starting_at=None,
is_event_added_to_log=False,
+ num_generated_events=None,
):
diffsim_info = SimDiffSetup(
- bpmn_path, json_path, is_event_added_to_log, total_cases
+ bpmn_path, json_path, is_event_added_to_log, total_cases, num_generated_events
)
if not diffsim_info:
diff --git a/bpdfr_simulation_engine/simulation_properties_parser.py b/bpdfr_simulation_engine/simulation_properties_parser.py
index e13a6eb..c842cdd 100644
--- a/bpdfr_simulation_engine/simulation_properties_parser.py
+++ b/bpdfr_simulation_engine/simulation_properties_parser.py
@@ -11,6 +11,7 @@
BPMNGraph,
ElementInfo,
)
+from bpdfr_simulation_engine.events_performance_experiments import _add_events
from bpdfr_simulation_engine.prioritisation import AllPriorityRules
from bpdfr_simulation_engine.prioritisation_parser import PrioritisationParser
from bpdfr_simulation_engine.probability_distributions import *
@@ -27,7 +28,7 @@
PRIORITISATION_RULES_SECTION = "prioritisation_rules"
ARRIVAL_TIME_CALENDAR = "arrival_time_calendar"
RESOURCE_CALENDARS = "resource_calendars"
-
+TASK_RESOURCE_DISTR_SECTON = "task_resource_distribution"
def parse_json_sim_parameters(json_path):
with open(json_path) as json_file:
@@ -38,7 +39,7 @@ def parse_json_sim_parameters(json_path):
)
calendars_map = parse_resource_calendars(json_data[RESOURCE_CALENDARS])
task_resource_distribution = parse_task_resource_distributions(
- json_data["task_resource_distribution"], res_pool
+ json_data[TASK_RESOURCE_DISTR_SECTON], res_pool
)
element_distribution = parse_arrival_branching_probabilities(
@@ -234,7 +235,13 @@ def parse_arrival_branching_probabilities(arrival_json, gateway_json):
return element_distribution
-def parse_simulation_model(bpmn_path):
+def parse_simulation_model(
+ bpmn_path, element_probability={}, num_generated_events=None
+):
+ """
+ element_probability parameter is only used for adding probability for newly inserted sequence flows
+ when inserting events
+ """
tree = ET.parse(bpmn_path)
root = tree.getroot()
@@ -276,7 +283,8 @@ def parse_simulation_model(bpmn_path):
# Counting incoming/outgoing flow arcs to handle cases of multiple in/out arcs simultaneously
pending_flow_arcs = list()
- for flow_arc in process.findall("xmlns:sequenceFlow", bpmn_element_ns):
+ sequence_flow_list = process.findall("xmlns:sequenceFlow", bpmn_element_ns)
+ for flow_arc in sequence_flow_list:
# Fixing the case in which a task may have multiple incoming/outgoing flow-arcs
pending_flow_arcs.append(flow_arc)
if flow_arc.attrib["sourceRef"] in elements_map:
@@ -332,6 +340,14 @@ def parse_simulation_model(bpmn_path):
target_id = join_gateways[target_id]
bpmn_graph.add_flow_arc(flow_arc.attrib["id"], source_id, target_id)
+ if num_generated_events != None:
+ _add_events(
+ bpmn_graph,
+ sequence_flow_list,
+ element_probability,
+ num_generated_events,
+ )
+
bpmn_graph.encode_or_join_predecesors()
bpmn_graph.validate_model()
return bpmn_graph
diff --git a/bpdfr_simulation_engine/simulation_setup.py b/bpdfr_simulation_engine/simulation_setup.py
index 04aaedf..501fcd5 100644
--- a/bpdfr_simulation_engine/simulation_setup.py
+++ b/bpdfr_simulation_engine/simulation_setup.py
@@ -8,11 +8,21 @@
from bpdfr_simulation_engine.control_flow_manager import ProcessState, ElementInfo, BPMN
from bpdfr_simulation_engine.probability_distributions import generate_number_from
from bpdfr_simulation_engine.resource_calendar import RCalendar
-from bpdfr_simulation_engine.simulation_properties_parser import parse_simulation_model, parse_json_sim_parameters
+from bpdfr_simulation_engine.simulation_properties_parser import (
+ parse_simulation_model,
+ parse_json_sim_parameters,
+)
class SimDiffSetup:
- def __init__(self, bpmn_path, json_path, is_event_added_to_log, total_cases):
+ def __init__(
+ self,
+ bpmn_path,
+ json_path,
+ is_event_added_to_log,
+ total_cases,
+ num_generated_events=None,
+ ):
self.process_name = ntpath.basename(bpmn_path).split(".")[0]
self.start_datetime = datetime.datetime.now(pytz.utc)
@@ -20,9 +30,15 @@ def __init__(self, bpmn_path, json_path, is_event_added_to_log, total_cases):
self.event_distibution, self.batch_processing, self.case_attributes, self.prioritisation_rules \
= parse_json_sim_parameters(json_path)
- self.bpmn_graph = parse_simulation_model(bpmn_path)
- self.bpmn_graph.set_additional_fields_from_json(self.element_probability, \
- self.task_resource, self.event_distibution, self.batch_processing)
+ self.bpmn_graph = parse_simulation_model(
+ bpmn_path, self.element_probability, num_generated_events
+ )
+ self.bpmn_graph.set_additional_fields_from_json(
+ self.element_probability,
+ self.task_resource,
+ self.event_distibution,
+ self.batch_processing
+ )
if not self.arrival_calendar:
self.arrival_calendar = self.find_arrival_calendar()
diff --git a/performance_exp/batching/bpi2012/input.zip b/performance_exp/batching/bpi2012/input.zip
new file mode 100644
index 0000000..b420c38
Binary files /dev/null and b/performance_exp/batching/bpi2012/input.zip differ
diff --git a/performance_exp/batching/bpi2012/results/.gitkeep b/performance_exp/batching/bpi2012/results/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/performance_exp/batching/perform_batching_runner.py b/performance_exp/batching/perform_batching_runner.py
new file mode 100644
index 0000000..8b645f9
--- /dev/null
+++ b/performance_exp/batching/perform_batching_runner.py
@@ -0,0 +1,284 @@
+import json
+import os
+import uuid
+
+import matplotlib.pyplot as plt
+import numpy as np
+from bpdfr_simulation_engine.simulation_properties_parser import (
+ BATCH_PROCESSING_SECTION,
+ TASK_RESOURCE_DISTR_SECTON,
+)
+from mpl_toolkits import mplot3d
+from performance_exp.batching.testing_files import process_files_setup
+from testing_scripts.bimp_diff_sim_tests import run_diff_res_simulation
+
+
+def main():
+ model_name = "bpi2012"
+ # model_name = "simple_example"
+ model_info = process_files_setup[model_name]
+ max_num_tasks_with_batching = model_info["max_num_tasks_with_batching"]
+ max_iter_num = model_info["max_iter_num"]
+ max_complexity_level = model_info["max_complexity_level"]
+
+ num_batched_tasks_range = range(0, 1 + max_num_tasks_with_batching)
+ rules_level_range = range(1, 1 + max_complexity_level)
+
+ # unique identifier of the experiment run
+ unique_run_id = uuid.uuid4()
+
+ # file for saving received results (number_inserted_events, simulation_time)
+ final_plot_results = _get_abs_path(
+ model_info["results_folder"],
+ f"{unique_run_id}_plot_data.csv",
+ )
+
+ # add name of the model used during this experiment
+ with open(final_plot_results, "a") as plot_file:
+ plot_file.write(f"{model_name}\n\n")
+
+ sim_time_list = []
+ all_rule_complex_level = []
+ all_batched_task = []
+
+ for index in num_batched_tasks_range:
+ print("-------------------------------------------")
+ print(
+ f"Starting Simulation with {index} priority levels in the simulation scenario"
+ )
+ print("-------------------------------------------")
+
+ for rule_complexity_level in rules_level_range:
+ median_sim_time = get_avg_after_all_iters(
+ max_iter_num, index, model_info, rule_complexity_level
+ )
+
+ all_batched_task.append(index)
+ all_rule_complex_level.append(rule_complexity_level)
+ sim_time_list.append(median_sim_time)
+
+ # collect data points used for plotting
+ with open(final_plot_results, "a") as plot_file:
+ plot_file.write(f"{index},{rule_complexity_level},{median_sim_time}\n")
+
+ print(sim_time_list)
+
+ # save plot of the relationship: number of batched tasks - simulation time
+ plt_path = _get_abs_path(
+ model_info["results_folder"],
+ f"{unique_run_id}_plot.png",
+ )
+
+ # show plot of the relationship: number of priority levels - simulation time
+ print(all_batched_task)
+ print(sim_time_list)
+ print(f"np {np.array(sim_time_list)}")
+ print(all_rule_complex_level)
+ print(f"np {np.array(all_rule_complex_level)}")
+ _save_plot_2(
+ # all_batched_task,
+ num_batched_tasks_range,
+ sim_time_list,
+ all_rule_complex_level,
+ max_complexity_level,
+ model_name,
+ model_info["total_cases"],
+ plt_path,
+ )
+
+
+def get_avg_after_all_iters(
+ max_iter_num: int, current_run_index: int, model_info, rule_complexity: int
+):
+ same_index_sim_time_list = []
+ for iter_num in range(0, max_iter_num):
+ sim_time = run_one_iteration(current_run_index, model_info, rule_complexity)
+ print(f"iter {iter_num}: {sim_time}")
+ same_index_sim_time_list.append(sim_time)
+
+ median_sim_time = np.median(same_index_sim_time_list)
+ print(f"median: {median_sim_time}")
+
+ return median_sim_time
+
+
+def run_one_iteration(num_tasks_with_batching: int, model_info, rule_complexity: int):
+ results_folder = model_info["results_folder"]
+ initial_json_path = _get_abs_path(model_info["json"])
+ bpmn_path = _get_abs_path(model_info["bpmn"])
+ demo_stats = _get_abs_path(results_folder, f"{num_tasks_with_batching}_stats.csv")
+ sim_log = _get_abs_path(results_folder, f"{num_tasks_with_batching}_logs.csv")
+
+ new_json_path = _setup_sim_scenario(
+ initial_json_path, num_tasks_with_batching, rule_complexity
+ )
+
+ simulation_time, _ = run_diff_res_simulation(
+ model_info["start_datetime"],
+ model_info["total_cases"],
+ bpmn_path,
+ new_json_path,
+ demo_stats,
+ sim_log,
+ False, # no events in the log
+ None, # no added events
+ )
+
+ return simulation_time
+ # diff_sim_result.print_simulation_results()
+
+
+def _setup_sim_scenario(
+ initial_json_path, num_tasks_with_batching: int, rule_complexity: int
+):
+ """
+ Create case-based prioritisation rules based on the required number (num_prioritisation_rules)
+ Save the newly created json in new location to keep track of the setup for simulations
+ """
+
+ one_batching_rule = _get_rule_by_complexity_level(rule_complexity)
+
+ with open(initial_json_path, "r") as f:
+ json_dict = json.load(f)
+
+ # collect all ids of activities in the BPMN model
+ all_tasks_distr = json_dict[TASK_RESOURCE_DISTR_SECTON]
+ all_tasks_id = map(lambda item: item["task_id"], all_tasks_distr)
+
+ # select only number of activities that should have an assigned batching rule
+ selected_tasks_id = list(all_tasks_id)[:num_tasks_with_batching]
+
+ # create batching setup
+ new_batching_rules_section = [
+ {
+ "task_id": task_id,
+ "type": "Sequential",
+ "batch_frequency": 1.0,
+ "size_distrib": [
+ {"key": "2", "value": 1},
+ ],
+ "duration_distrib": [{"key": "3", "value": 0.8}],
+ "firing_rules": one_batching_rule,
+ }
+ for task_id in selected_tasks_id
+ ]
+
+ json_dict[BATCH_PROCESSING_SECTION] = new_batching_rules_section
+
+ # save modified json as a new file specifying the number of experiment
+ # in order to keep track of run experiments
+ folder_loc = os.path.dirname(initial_json_path)
+ new_filename = f"{num_tasks_with_batching}_batching_exp.json"
+ new_json_path = os.path.join(folder_loc, new_filename)
+
+ with open(new_json_path, "w+") as json_file:
+ json.dump(json_dict, json_file)
+
+ return new_json_path
+
+
+def _get_rule_by_complexity_level(rule_complexity: int):
+ all_rules = {
+ "1": [
+ [
+ {"attribute": "size", "comparison": ">=", "value": 4},
+ ]
+ ],
+ "2": [
+ [
+ {"attribute": "daily_hour", "comparison": "<", "value": "12"},
+ {"attribute": "week_day", "comparison": "=", "value": "Friday"},
+ ]
+ ],
+ "3": [
+ [
+ {"attribute": "daily_hour", "comparison": "<", "value": "12"},
+ {"attribute": "week_day", "comparison": "=", "value": "Friday"},
+ ],
+ [
+ {"attribute": "size", "comparison": ">=", "value": 4},
+ ],
+ ],
+ "4": [
+ [
+ {"attribute": "daily_hour", "comparison": "<", "value": "12"},
+ {"attribute": "week_day", "comparison": "=", "value": "Friday"},
+ ],
+ [
+ {"attribute": "size", "comparison": ">=", "value": 4},
+ {"attribute": "large_wt", "comparison": "<", "value": 3600},
+ ],
+ ],
+ }
+
+ return all_rules[str(rule_complexity)]
+
+
+def _save_plot(xpoints, ypoints, zpoints, model_name, num_of_instances, plt_path):
+ fig = plt.figure()
+ ax = fig.add_subplot(projection="3d")
+
+ ax.scatter3D(xpoints, ypoints, zpoints, c=zpoints, cmap="Greens")
+
+ # give a general title
+ ax.set_title(f"Model: {model_name}, instances: {num_of_instances}")
+
+ # name axis
+ ax.set_xlabel("Number of batched tasks")
+ ax.set_ylabel("Simulation time, sec")
+ ax.set_zlabel("Complexity of the batching rule")
+
+ plt.show()
+ # save as a file
+ # plt.savefig(plt_path, bbox_inches="tight")
+
+
+def _save_plot_2(
+ num_batched_task_arr,
+ sim_time_arr,
+ rule_complexity_level_arr,
+ max_complexity_level,
+ model_name,
+ num_of_instances,
+ plt_path,
+):
+ fig = plt.figure()
+
+ # give a general title
+ # plt.title(f"Model: {model_name}, instances: {num_of_instances}")
+
+ # # provide data points
+ # plt.plot(xpoints, ypoints)
+ ax = fig.add_subplot(projection="3d")
+ colors = ["b", "g", "r", "c", "m", "y"]
+
+ for i, (current_num_batched_task, color) in enumerate(
+ zip(num_batched_task_arr, colors)
+ ):
+ gap = max_complexity_level
+ start = i * gap # 1 - current_num_batched_task
+ end = start + gap
+ xs = sim_time_arr[start:end]
+ ys = rule_complexity_level_arr[start:end]
+ print(f"zs = {current_num_batched_task}")
+ print(start, end, xs, ys)
+ ax.bar(ys, xs, zs=current_num_batched_task, zdir="y", color=color, alpha=0.8)
+
+ # name axis
+ ax.set_xlabel("Complexity of the batching rule")
+ ax.set_ylabel("Number of batched tasks")
+ ax.set_zlabel("Simulation time, sec")
+ ax.set_title(f"Model: {model_name}, instances: {num_of_instances}")
+
+ # plt.colorbar() # show color scale
+ plt.show()
+ # save as a file
+ # plt.savefig(plt_path, bbox_inches="tight")
+
+
+def _get_abs_path(*args):
+ return os.path.join(os.path.dirname(__file__), *args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/performance_exp/batching/testing_files.py b/performance_exp/batching/testing_files.py
new file mode 100644
index 0000000..c275785
--- /dev/null
+++ b/performance_exp/batching/testing_files.py
@@ -0,0 +1,35 @@
+import numpy as np
+
+
+process_files_setup = {
+ "simple_example": {
+ "bpmn": "simple_example/batch-example-end-task.bpmn",
+ "json": "simple_example/batch-example-with-batch.json",
+ "results_folder": "simple_example/results",
+ "start_datetime": "2022-06-21 13:22:30.035185+03:00",
+ "total_cases": 1000,
+ "disc_params": [60, 0.1, 0.9, 0.6, True],
+ "max_num_tasks_with_batching": 5, # should be the max number of tasks in the model
+ "measure_central_tendency": np.median,
+ "max_iter_num": 2,
+ "max_complexity_level": 4,
+ },
+ "bpi2012": {
+ "bpmn": "bpi2012/input/BPI_Challenge_2012_W_Two_TS.bpmn",
+ "json": "bpi2012/input/bpi_2012.json",
+ "results_folder": "bpi2012/results",
+ "start_datetime": "2011-10-01 11:08:36.700000+03:00", # used to be as close to the real log as possible
+ "total_cases": 8616,
+ "disc_params": [
+ 60,
+ 0.5,
+ 0.5,
+ 0.1,
+ True,
+ ], # to know the input required for discovering the json from the log
+ "max_num_tasks_with_batching": 6, # should be the max number of tasks in the model
+ "measure_central_tendency": np.median, # median since this metric is not impacted by outliers, compared to the mean one
+ "max_iter_num": 5,
+ "max_complexity_level": 4,
+ },
+}
diff --git a/performance_exp/events/bpi2012/Archive.zip b/performance_exp/events/bpi2012/Archive.zip
new file mode 100644
index 0000000..b420c38
Binary files /dev/null and b/performance_exp/events/bpi2012/Archive.zip differ
diff --git a/performance_exp/events/bpi2012/results/.gitkeep b/performance_exp/events/bpi2012/results/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/performance_exp/events/generated_events/.gitkeep b/performance_exp/events/generated_events/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/performance_exp/events/input/batch-example-end-task.bpmn b/performance_exp/events/input/batch-example-end-task.bpmn
new file mode 100644
index 0000000..9a4b123
--- /dev/null
+++ b/performance_exp/events/input/batch-example-end-task.bpmn
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+ sid-E469684F-C09F-4A8B-A916-E9927BA15372
+
+
+
+
+
+
+
+
+
+
+ sid-6FD4FFD3-5784-4D33-9509-234EAB886930
+ sid-10E6C62E-2CBD-476A-976B-B862156F5DEC
+
+
+
+
+
+
+
+
+
+
+ sid-9E95A790-241E-4629-8D67-E9A2CE55E3DC
+ sid-FF95F9DA-C10F-455B-B2FC-FBC1C270C0B4
+
+
+
+
+
+
+
+
+
+
+ sid-E469684F-C09F-4A8B-A916-E9927BA15372
+ sid-FA2D48D3-A316-4C2F-90DB-C2390990D727
+
+
+
+
+
+
+
+
+
+
+ Flow_0ah4nto
+ Flow_0plakw8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sid-FA2D48D3-A316-4C2F-90DB-C2390990D727
+ sid-6FD4FFD3-5784-4D33-9509-234EAB886930
+ sid-9E95A790-241E-4629-8D67-E9A2CE55E3DC
+
+
+
+
+
+
+
+ sid-FF95F9DA-C10F-455B-B2FC-FBC1C270C0B4
+ sid-10E6C62E-2CBD-476A-976B-B862156F5DEC
+ Flow_0ah4nto
+
+
+ Flow_0plakw8
+ Flow_1xezdfv
+
+
+
+ Flow_1xezdfv
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/performance_exp/events/input/batch-example-with-batch.json b/performance_exp/events/input/batch-example-with-batch.json
new file mode 100644
index 0000000..1707041
--- /dev/null
+++ b/performance_exp/events/input/batch-example-with-batch.json
@@ -0,0 +1,627 @@
+{
+ "resource_profiles": [
+ {
+ "id": "sid-generic-resource",
+ "name": "Generic_Resource",
+ "resource_list": [
+ {
+ "id": "Resource_1",
+ "name": "Resource_1",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ },
+ {
+ "id": "Resource_2",
+ "name": "Resource_2",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ },
+ {
+ "id": "Resource_3",
+ "name": "Resource_3",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ },
+ {
+ "id": "Resource_4",
+ "name": "Resource_4",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ },
+ {
+ "id": "Resource_5",
+ "name": "Resource_5",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ }
+ ]
+ }
+ ],
+ "arrival_time_distribution": {
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ "arrival_time_calendar": [
+ {
+ "from": "MONDAY",
+ "to": "SUNDAY",
+ "beginTime": "00:00:00.000",
+ "endTime": "23:59:59.999"
+ }
+ ],
+ "gateway_branching_probabilities": [
+ {
+ "gateway_id": "sid-6B518C80-2B96-4C95-B6DE-F9E4A75FF191",
+ "probabilities": [
+ {
+ "path_id": "sid-6FD4FFD3-5784-4D33-9509-234EAB886930",
+ "value": 0.3
+ },
+ {
+ "path_id": "sid-9E95A790-241E-4629-8D67-E9A2CE55E3DC",
+ "value": 0.7
+ }
+ ]
+ }
+ ],
+ "task_resource_distribution": [
+ {
+ "task_id": "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "task_id": "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "task_id": "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "task_id": "sid-503A048D-6344-446A-8D67-172B164CF8FA",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "task_id": "Activity_0ngxjs9",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "resource_calendars": [
+ {
+ "id": "247timetable",
+ "name": "247timetable",
+ "time_periods": [
+ {
+ "from": "MONDAY",
+ "to": "SUNDAY",
+ "beginTime": "00:00:00.000",
+ "endTime": "23:59:59.999"
+ }
+ ]
+ }
+ ],
+ "batch_processing": [
+ {
+ "task_id": "sid-503A048D-6344-446A-8D67-172B164CF8FA",
+ "type": "Sequential",
+ "batch_frequency": 1.0,
+ "size_distrib": [
+ {
+ "key": "1",
+ "value": 6
+ },
+ {
+ "key": "3",
+ "value": 35
+ },
+ {
+ "key": "4",
+ "value": 3
+ }
+ ],
+ "duration_distrib": [
+ {
+ "key": "3",
+ "value": 0.8
+ }
+ ],
+ "firing_rules": [
+ [
+ {
+ "attribute": "size",
+ "comparison": "=",
+ "value": 3
+ }
+ ]
+ ]
+ }
+ ],
+ "case_attributes": [
+ {
+ "name": "client_type",
+ "type": "discrete",
+ "values": [
+ {
+ "key": "REGULAR",
+ "value": 0.8
+ },
+ {
+ "key": "BUSINESS",
+ "value": 0.2
+ }
+ ]
+ },
+ {
+ "name": "loan_amount",
+ "type": "continuous",
+ "values": {
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 240
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ }
+ ],
+ "event_distribution": [
+ {
+ "event_id": "event_0",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 900.0
+ }
+ ]
+ },
+ {
+ "event_id": "event_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 900.0
+ }
+ ]
+ },
+ {
+ "event_id": "event_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 900.0
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/performance_exp/events/input/results/.gitkeep b/performance_exp/events/input/results/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/performance_exp/events/perform_events_runner.py b/performance_exp/events/perform_events_runner.py
new file mode 100644
index 0000000..14dfdea
--- /dev/null
+++ b/performance_exp/events/perform_events_runner.py
@@ -0,0 +1,110 @@
+import json
+import os
+
+import matplotlib.pyplot as plt
+from bpdfr_simulation_engine.simulation_properties_parser import (
+ EVENT_DISTRIBUTION_SECTION,
+)
+from performance_exp.events.testing_files import process_files_setup
+from performance_exp.shared_func import (
+ run_whole_experiment,
+)
+from testing_scripts.bimp_diff_sim_tests import run_diff_res_simulation
+
+
+def main():
+ all_models = [
+ "events_exp",
+ ]
+ for model_name in all_models:
+ run_whole_experiment(
+ model_name,
+ process_files_setup[model_name],
+ "number_of_added_events",
+ _get_abs_path,
+ run_one_iteration,
+ _save_plot,
+ False,
+ )
+
+
+def run_one_iteration(num_inserted_events: int, model_info):
+ initial_json_path = _get_abs_path(model_info["json"])
+ bpmn_path = _get_abs_path(model_info["bpmn"])
+ demo_stats = _get_abs_path(
+ model_info["results_folder"], f"{num_inserted_events}_stats.csv"
+ )
+ sim_log = _get_abs_path(
+ model_info["results_folder"],
+ f"{num_inserted_events}_logs.csv",
+ )
+ new_json_path = _setup_event_distribution(initial_json_path, num_inserted_events)
+
+ simulation_time, _ = run_diff_res_simulation(
+ model_info["start_datetime"],
+ model_info["total_cases"],
+ bpmn_path,
+ new_json_path,
+ demo_stats,
+ sim_log,
+ True,
+ num_inserted_events,
+ )
+
+ return simulation_time
+ # diff_sim_result.print_simulation_results()
+
+
+def _setup_event_distribution(initial_json_path, num_events: int):
+ """
+ Create event distribution for all events that will be later added to the model
+ Save the newly created json in new location to keep track of the setup for simulations
+ """
+
+ event_distr_list = [
+ {
+ "event_id": f"event_{index}",
+ "distribution_name": "fix",
+ "distribution_params": [{"value": 900.0}],
+ }
+ for index in range(num_events)
+ ]
+
+ with open(initial_json_path, "r") as f:
+ json_dict = json.load(f)
+
+ json_dict[EVENT_DISTRIBUTION_SECTION] = event_distr_list
+
+ # save modified json as a new file specifying the number of experiment
+ # in order to keep track of run experiments
+ folder_loc = os.path.dirname(initial_json_path)
+ new_filename = f"{num_events}_events_exp.json"
+ new_json_path = os.path.join(folder_loc, new_filename)
+
+ with open(new_json_path, "w+") as json_file:
+ json.dump(json_dict, json_file)
+
+ return new_json_path
+
+
+def _save_plot(xpoints, ypoints, model_name, num_of_instances, plt_path):
+ # give a general title
+ plt.title(f"Model: {model_name}, instances: {num_of_instances}")
+
+ # name axis
+ plt.xlabel("Number of added events")
+ plt.ylabel("Simulation time, sec")
+
+ # provide data points
+ plt.plot(xpoints, ypoints)
+
+ # save as a file
+ plt.savefig(plt_path, bbox_inches="tight")
+
+
+def _get_abs_path(*args):
+ return os.path.join(os.path.dirname(__file__), *args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/performance_exp/events/testing_files.py b/performance_exp/events/testing_files.py
new file mode 100644
index 0000000..e6a1076
--- /dev/null
+++ b/performance_exp/events/testing_files.py
@@ -0,0 +1,27 @@
+import numpy as np
+
+
+process_files_setup = {
+ "events_exp": {
+ "bpmn": "input/batch-example-end-task.bpmn",
+ "json": "input/batch-example-with-batch.json",
+ "results_folder": "input/results",
+ "start_datetime": "2022-06-21 13:22:30.035185+03:00",
+ "total_cases": 1000,
+ "disc_params": [60, 0.1, 0.9, 0.6, True],
+ "number_of_added_events": 9, # should be equal to the number of sequence flows in the BPMN model
+ "measure_central_tendency": np.median,
+ "max_iter_num": 1,
+ },
+ "bpi2012_median_5": {
+ "bpmn": "bpi2012/BPI_Challenge_2012_W_Two_TS.bpmn",
+ "json": "bpi2012/bpi_2012.json",
+ "results_folder": "bpi2012/results",
+ "start_datetime": "2011-10-01 11:08:36.700000+03:00", # used to be as close to the real log as possible
+ "total_cases": 8616,
+ "disc_params": [60, 0.5, 0.5, 0.1, True], # to know the input required for discovering the json from the log
+ "number_of_added_events": 36, # should be equal to the number of sequence flows in the BPMN model
+ "measure_central_tendency": np.median, # median since this metric is not impacted by outliers, compared to the mean one
+ "max_iter_num": 5,
+ },
+}
diff --git a/performance_exp/prioritisation/bpi2012/input.zip b/performance_exp/prioritisation/bpi2012/input.zip
new file mode 100644
index 0000000..43aae77
Binary files /dev/null and b/performance_exp/prioritisation/bpi2012/input.zip differ
diff --git a/performance_exp/prioritisation/bpi2012/results/.gitkeep b/performance_exp/prioritisation/bpi2012/results/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/performance_exp/prioritisation/perform_prioritisation_runner.py b/performance_exp/prioritisation/perform_prioritisation_runner.py
new file mode 100644
index 0000000..193928c
--- /dev/null
+++ b/performance_exp/prioritisation/perform_prioritisation_runner.py
@@ -0,0 +1,166 @@
+import json
+import os
+
+import matplotlib.pyplot as plt
+
+from bpdfr_simulation_engine.simulation_properties_parser import (
+ PRIORITISATION_RULES_SECTION,
+)
+from performance_exp.prioritisation.testing_files import process_files_setup
+from performance_exp.shared_func import (
+ run_whole_experiment,
+)
+from testing_scripts.bimp_diff_sim_tests import run_diff_res_simulation
+
+
+def main():
+ model_name = "bpi2012"
+
+ run_whole_experiment(
+ model_name,
+ process_files_setup[model_name],
+ "number_of_priority_levels",
+ _get_abs_path,
+ run_one_iteration,
+ _save_plot,
+ True,
+ )
+
+
+def run_one_iteration(num_prioritisation_rules: int, model_info):
+ initial_json_path = _get_abs_path(model_info["json"])
+ bpmn_path = _get_abs_path(model_info["bpmn"])
+ demo_stats = _get_abs_path(
+ model_info["results_folder"], f"{num_prioritisation_rules}_stats.csv"
+ )
+ sim_log = _get_abs_path(
+ model_info["results_folder"],
+ f"{num_prioritisation_rules}_logs.csv",
+ )
+ new_json_path = _setup_sim_scenario(initial_json_path, num_prioritisation_rules)
+
+ simulation_time, _ = run_diff_res_simulation(
+ model_info["start_datetime"],
+ model_info["total_cases"],
+ bpmn_path,
+ new_json_path,
+ demo_stats,
+ sim_log,
+ False, # no events in the log
+ None, # no added events
+ )
+
+ return simulation_time
+ # diff_sim_result.print_simulation_results()
+
+
+def _setup_sim_scenario(initial_json_path, num_prioritisation_rules: int):
+ """
+ Create case-based prioritisation rules based on the required number (num_prioritisation_rules)
+ Save the newly created json in new location to keep track of the setup for simulations
+ """
+
+ prioritisation_rules = [
+ {
+ "priority_level": 1,
+ "rules": [
+ [
+ {
+ "attribute": "loan_amount",
+ "comparison": "in",
+ "value": [2000, "inf"],
+ }
+ ],
+ ],
+ },
+ {
+ "priority_level": 2,
+ "rules": [
+ [
+ {
+ "attribute": "loan_amount",
+ "comparison": "in",
+ "value": [1500, 2000],
+ }
+ ],
+ ],
+ },
+ {
+ "priority_level": 3,
+ "rules": [
+ [
+ {
+ "attribute": "loan_amount",
+ "comparison": "in",
+ "value": [1000, 1500],
+ }
+ ],
+ ],
+ },
+ {
+ "priority_level": 4,
+ "rules": [
+ [
+ {
+ "attribute": "loan_amount",
+ "comparison": "in",
+ "value": [800, 1000],
+ }
+ ],
+ ],
+ },
+ {
+ "priority_level": 5,
+ "rules": [
+ [{"attribute": "loan_amount", "comparison": "in", "value": [500, 800]}],
+ ],
+ },
+ {
+ "priority_level": 6,
+ "rules": [
+ [{"attribute": "loan_amount", "comparison": "in", "value": [0, 500]}],
+ ],
+ },
+ ]
+
+ with open(initial_json_path, "r") as f:
+ json_dict = json.load(f)
+
+ json_dict[PRIORITISATION_RULES_SECTION] = prioritisation_rules[
+ :num_prioritisation_rules
+ ]
+
+ # save modified json as a new file specifying the number of experiment
+ # in order to keep track of run experiments
+ folder_loc = os.path.dirname(initial_json_path)
+ new_filename = f"{num_prioritisation_rules}_prioritisation_rules_exp.json"
+ new_json_path = os.path.join(folder_loc, new_filename)
+
+ with open(new_json_path, "w+") as json_file:
+ json.dump(json_dict, json_file)
+
+ return new_json_path
+
+
+def _save_plot(xpoints, ypoints, model_name, num_of_instances, plt_path, is_ms=False):
+ # give a general title
+ plt.title(f"Model: {model_name}, instances: {num_of_instances}")
+
+ # name axis
+ plt.xlabel("Number of priority levels")
+ time_measure = "ms" if is_ms else "sec"
+ plt.ylabel(f"Simulation time, {time_measure}")
+
+ # provide data points
+ plt.plot(xpoints, ypoints)
+
+ # save as a file
+ plt.savefig(plt_path, bbox_inches="tight")
+
+
+def _get_abs_path(*args):
+ return os.path.join(os.path.dirname(__file__), *args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/performance_exp/prioritisation/simple_example/batch-example-end-task.bpmn b/performance_exp/prioritisation/simple_example/batch-example-end-task.bpmn
new file mode 100644
index 0000000..9a4b123
--- /dev/null
+++ b/performance_exp/prioritisation/simple_example/batch-example-end-task.bpmn
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+ sid-E469684F-C09F-4A8B-A916-E9927BA15372
+
+
+
+
+
+
+
+
+
+
+ sid-6FD4FFD3-5784-4D33-9509-234EAB886930
+ sid-10E6C62E-2CBD-476A-976B-B862156F5DEC
+
+
+
+
+
+
+
+
+
+
+ sid-9E95A790-241E-4629-8D67-E9A2CE55E3DC
+ sid-FF95F9DA-C10F-455B-B2FC-FBC1C270C0B4
+
+
+
+
+
+
+
+
+
+
+ sid-E469684F-C09F-4A8B-A916-E9927BA15372
+ sid-FA2D48D3-A316-4C2F-90DB-C2390990D727
+
+
+
+
+
+
+
+
+
+
+ Flow_0ah4nto
+ Flow_0plakw8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sid-FA2D48D3-A316-4C2F-90DB-C2390990D727
+ sid-6FD4FFD3-5784-4D33-9509-234EAB886930
+ sid-9E95A790-241E-4629-8D67-E9A2CE55E3DC
+
+
+
+
+
+
+
+ sid-FF95F9DA-C10F-455B-B2FC-FBC1C270C0B4
+ sid-10E6C62E-2CBD-476A-976B-B862156F5DEC
+ Flow_0ah4nto
+
+
+ Flow_0plakw8
+ Flow_1xezdfv
+
+
+
+ Flow_1xezdfv
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/performance_exp/prioritisation/simple_example/batch-example-with-batch.json b/performance_exp/prioritisation/simple_example/batch-example-with-batch.json
new file mode 100644
index 0000000..ed52bf4
--- /dev/null
+++ b/performance_exp/prioritisation/simple_example/batch-example-with-batch.json
@@ -0,0 +1,595 @@
+{
+ "resource_profiles": [
+ {
+ "id": "sid-generic-resource",
+ "name": "Generic_Resource",
+ "resource_list": [
+ {
+ "id": "Resource_1",
+ "name": "Resource_1",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ },
+ {
+ "id": "Resource_2",
+ "name": "Resource_2",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ },
+ {
+ "id": "Resource_3",
+ "name": "Resource_3",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ },
+ {
+ "id": "Resource_4",
+ "name": "Resource_4",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ },
+ {
+ "id": "Resource_5",
+ "name": "Resource_5",
+ "cost_per_hour": 1,
+ "amount": 1,
+ "calendar": "247timetable",
+ "assigned_tasks": [
+ "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "sid-503A048D-6344-446A-8D67-172B164CF8FA"
+ ]
+ }
+ ]
+ }
+ ],
+ "arrival_time_distribution": {
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ "arrival_time_calendar": [
+ {
+ "from": "MONDAY",
+ "to": "SUNDAY",
+ "beginTime": "00:00:00.000",
+ "endTime": "23:59:59.999"
+ }
+ ],
+ "gateway_branching_probabilities": [
+ {
+ "gateway_id": "sid-6B518C80-2B96-4C95-B6DE-F9E4A75FF191",
+ "probabilities": [
+ {
+ "path_id": "sid-6FD4FFD3-5784-4D33-9509-234EAB886930",
+ "value": 0.3
+ },
+ {
+ "path_id": "sid-9E95A790-241E-4629-8D67-E9A2CE55E3DC",
+ "value": 0.7
+ }
+ ]
+ }
+ ],
+ "task_resource_distribution": [
+ {
+ "task_id": "sid-4B24111F-B305-4608-9E12-744B47C44D0D",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "task_id": "sid-D048D99D-F549-43B8-8ACB-5AE153B12B0F",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "task_id": "sid-02577CBF-ABA3-4EFD-9480-E1DFCF238B1C",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "task_id": "sid-503A048D-6344-446A-8D67-172B164CF8FA",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "task_id": "Activity_0ngxjs9",
+ "resources": [
+ {
+ "resource_id": "Resource_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_3",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_4",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ },
+ {
+ "resource_id": "Resource_5",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 120
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 1
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "resource_calendars": [
+ {
+ "id": "247timetable",
+ "name": "247timetable",
+ "time_periods": [
+ {
+ "from": "MONDAY",
+ "to": "SUNDAY",
+ "beginTime": "00:00:00.000",
+ "endTime": "23:59:59.999"
+ }
+ ]
+ }
+ ],
+ "batch_processing": [],
+ "case_attributes": [
+ {
+ "name": "client_type",
+ "type": "discrete",
+ "values": [
+ {
+ "key": "REGULAR",
+ "value": 0.8
+ },
+ {
+ "key": "BUSINESS",
+ "value": 0.2
+ }
+ ]
+ },
+ {
+ "name": "loan_amount",
+ "type": "continuous",
+ "values": {
+ "distribution_name": "expon",
+ "distribution_params": [
+ {
+ "value": 1000
+ },
+ {
+ "value": 500.0
+ },
+ {
+ "value": 0
+ },
+ {
+ "value": 5400
+ }
+ ]
+ }
+ }
+ ],
+ "event_distribution": [
+ {
+ "event_id": "event_0",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 900.0
+ }
+ ]
+ },
+ {
+ "event_id": "event_1",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 900.0
+ }
+ ]
+ },
+ {
+ "event_id": "event_2",
+ "distribution_name": "fix",
+ "distribution_params": [
+ {
+ "value": 900.0
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/performance_exp/prioritisation/simple_example/results/.gitkeep b/performance_exp/prioritisation/simple_example/results/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/performance_exp/prioritisation/testing_files.py b/performance_exp/prioritisation/testing_files.py
new file mode 100644
index 0000000..9d6bdbb
--- /dev/null
+++ b/performance_exp/prioritisation/testing_files.py
@@ -0,0 +1,32 @@
+import numpy as np
+
+
+process_files_setup = {
+ "simple_example": {
+ "bpmn": "simple_example/batch-example-end-task.bpmn",
+ "json": "simple_example/batch-example-with-batch.json",
+ "results_folder": "simple_example/results",
+ "start_datetime": "2022-06-21 13:22:30.035185+03:00",
+ "total_cases": 1000,
+ "disc_params": [60, 0.1, 0.9, 0.6, True],
+ "number_of_priority_levels": 6,
+ "max_iter_num": 5,
+ },
+ "bpi2012": {
+ "bpmn": "bpi2012/input/BPI_Challenge_2012_W_Two_TS.bpmn",
+ "json": "bpi2012/input/bpi_2012.json",
+ "results_folder": "bpi2012/results",
+ "start_datetime": "2011-10-01 11:08:36.700000+03:00", # used to be as close to the real log as possible
+ "total_cases": 8616,
+ "disc_params": [
+ 60,
+ 0.5,
+ 0.5,
+ 0.1,
+ True,
+ ], # to know the input required for discovering the json from the log
+ "number_of_priority_levels": 6, # should be enough to detect the main trend of the received results
+ "measure_central_tendency": np.median, # median since this metric is not impacted by outliers, compared to the mean one
+ "max_iter_num": 5,
+ },
+}
diff --git a/performance_exp/shared_func.py b/performance_exp/shared_func.py
new file mode 100644
index 0000000..3cc1686
--- /dev/null
+++ b/performance_exp/shared_func.py
@@ -0,0 +1,169 @@
+import os
+import uuid
+
+import numpy as np
+import pandas as pd
+
+
+def get_central_tendency_over_all_iters(
+ max_iter_num,
+ run_one_iteration,
+ current_index,
+ model_info,
+ measure_central_tendency,
+ _get_abs_path,
+ is_normalised=False,
+):
+ """
+ Run one iteration n number of times and calculate the central tendency of simulation times
+
+ :param int max_iter_num: Number of iteration to run
+ :param func run_one_iteration: Function that needs to be run per one iteration
+ :param int current_index:
+ :param obj model_info: Description of the input provided by the user
+ :param func measure_central_tendency: numpy function used for calculating the central tendency (e.g., np.mean, np.median)
+ :param bool is_normalised: whether the simulation time is divided by the total number of activities during simulation
+ """
+ same_index_sim_time_list = []
+
+ results_folder_path = _get_abs_path(model_info["results_folder"])
+ current_log_path = get_log_filepath(results_folder_path, current_index)
+ normalised_data_path = os.path.join(results_folder_path, "normalised_table.csv")
+
+ for iter_num in range(0, max_iter_num):
+ sim_time = run_one_iteration(current_index, model_info)
+
+ if is_normalised:
+ # in case of normalising, we divide the simulation time
+ # by the number of activities run during the simulation
+ sim_time, num_total_activities, norm_sim_time = get_normalised_sim_time(
+ current_log_path,
+ sim_time,
+ normalised_data_path,
+ current_index,
+ iter_num,
+ )
+
+ print(f"iter {iter_num}: {sim_time}")
+ same_index_sim_time_list.append(sim_time)
+
+ median_sim_time = measure_central_tendency(same_index_sim_time_list)
+ print(f"central_tendency: {median_sim_time}")
+
+ if is_normalised:
+ # append row with statistics for the median value
+ with open(normalised_data_path, "a") as plot_file:
+ plot_file.write(
+ f"{current_index},median,{sim_time},{num_total_activities},{norm_sim_time}\n"
+ )
+
+ return median_sim_time
+
+
+def run_whole_experiment(
+ model_name,
+ model_info,
+ metric_under_performance_range_str,
+ _get_abs_path,
+ run_one_iteration,
+ _save_plot,
+ is_sim_time_normalised,
+):
+ total_number_of_x_values = model_info[metric_under_performance_range_str]
+ measure_central_tendency = (
+ model_info["measure_central_tendency"]
+ if "measure_central_tendency" in model_info
+ else np.median
+ )
+ max_iter_num = model_info["max_iter_num"]
+
+ print(f"Selected log: {model_name}")
+ print(f"Selected function for central tendency: {measure_central_tendency}")
+
+ number_of_events_to_add_list = range(0, 1 + total_number_of_x_values)
+
+ # array to save ordinate (y coordinate) of data points
+ sim_time_list = []
+
+ # unique identifier of the experiment run
+ unique_run_id = uuid.uuid4()
+
+ # file for saving received results (number_inserted_events, simulation_time)
+ final_plot_results = _get_abs_path(
+ model_info["results_folder"],
+ f"{unique_run_id}_plot_data.csv",
+ )
+
+ # add name of the model used during this experiment
+ with open(final_plot_results, "a") as plot_file:
+ plot_file.write(f"{model_name}\n\n")
+
+ # clear file with normalised data
+ results_folder_path = _get_abs_path(model_info["results_folder"])
+ normalised_data = os.path.join(results_folder_path, "normalised_table.csv")
+
+ with open(normalised_data, "a") as plot_file:
+ plot_file.write(
+ f"num_priority_levels,iter,sim_time_sec,num_total_activities,norm_sim_time_ms\n"
+ )
+
+ for index in number_of_events_to_add_list:
+ print("-------------------------------------------")
+ print(f"Starting Simulation with {index} inserted events")
+ print("-------------------------------------------")
+
+ median_sim_time = get_central_tendency_over_all_iters(
+ max_iter_num,
+ run_one_iteration,
+ index,
+ model_info,
+ measure_central_tendency,
+ _get_abs_path,
+ is_sim_time_normalised,
+ )
+ sim_time_list.append(median_sim_time)
+
+ with open(final_plot_results, "a") as plot_file:
+ plot_file.write(f"{index},{median_sim_time}\n")
+
+ print(sim_time_list)
+
+ # save plot of the relationship: number of added events - simulation time
+ plt_path = _get_abs_path(
+ model_info["results_folder"],
+ f"{unique_run_id}_plot.png",
+ )
+ _save_plot(
+ np.array(number_of_events_to_add_list),
+ sim_time_list,
+ model_name,
+ model_info["total_cases"],
+ plt_path,
+ is_sim_time_normalised, # use ms if True
+ )
+
+
+def get_log_filepath(results_folder_path, index):
+ return os.path.join(results_folder_path, f"{index}_logs.csv")
+
+
+def get_total_num_activities(log_path):
+ df = pd.read_csv(log_path)
+ return df.shape[0] # number of rows
+
+
+def get_normalised_sim_time(
+ current_log_path, sim_time, normalised_data_path, current_index, iter_num
+):
+ num_total_activities = get_total_num_activities(current_log_path)
+ norm_sim_time = sim_time / num_total_activities # seconds
+ norm_sim_time = norm_sim_time * 1000 # miliseconds
+
+ # append row with statistics for this current iteration
+ with open(normalised_data_path, "a") as plot_file:
+ plot_file.write(
+ f"{current_index},{iter_num},{sim_time},{num_total_activities},{norm_sim_time}\n"
+ )
+
+ # normalised sim_time used as a final one
+ return norm_sim_time, num_total_activities, norm_sim_time
diff --git a/testing_scripts/bimp_diff_sim_tests.py b/testing_scripts/bimp_diff_sim_tests.py
index 4b6366f..9291390 100644
--- a/testing_scripts/bimp_diff_sim_tests.py
+++ b/testing_scripts/bimp_diff_sim_tests.py
@@ -59,9 +59,9 @@ def run_bimp_simulation(model_file_path, results_file_path, simulation_log,
# return load_bimp_simulation_results(results_file_path, simulation_log)
-def run_diff_res_simulation(start_date, total_cases, bpmn_model, json_sim_params, out_stats_csv_path, out_log_csv_path, is_event_added_to_log = False):
+def run_diff_res_simulation(start_date, total_cases, bpmn_model, json_sim_params, out_stats_csv_path, out_log_csv_path, is_event_added_to_log = False, num_generated_events=None):
s_t = datetime.datetime.now()
- run_simulation(bpmn_model, json_sim_params, total_cases, out_stats_csv_path, out_log_csv_path, start_date, is_event_added_to_log)
+ run_simulation(bpmn_model, json_sim_params, total_cases, out_stats_csv_path, out_log_csv_path, start_date, is_event_added_to_log, num_generated_events)
sim_time = (datetime.datetime.now() - s_t).total_seconds()
# print((datetime.datetime.now() - s_t).total_seconds())
# print("DiffSim Execution Times: %s" %