Examples

Arrivals Time and Calendars

For this example we used the log that we can find in the folder example_arrivals/BPIChallenge2012A.xes. This event log pertains to a loan application process of a Dutch financial institute. The data contains all applications filed trough an online system in 2016 and their subsequent events until February 1st 2017, 15:11. The company providing the data and the process under consideration is the same as doi:10.4121/uuid:3926db30-f712-4394-aebc-75976070e91f.

The petrinet found by the inductive miner is the following:

Alt Text

In this example we want to show different ways to generate the arrivals times of tokens in the simulation. As exaplained in the inter_trigger_timer class we can define the arrivals with 3 methods:

    "interTriggerTimer": {
        "type": "distribution",
        "name": "exponential",
        "parameters": {
            "scale": 300
        }
    }

In addition, it is possible to add a schedule for token arrival times, i.e. when a new instance of the process can start. For example, we establish that a new trace of this process can start only from Monday to Friday, from 8 a.m. to 3 p.m.

    "interTriggerTimer": {
        "type": "distribution",
        "name": "exponential",
        "parameters": {
            "scale": 20
        },
        "calendar": {
            "days": [0, 1, 2, 3, 4],
            "hour_min": 8,
            "hour_max": 15
        }
    }

To set a fixed time interval between arrivals, simply define a uniform distribution with the min and max parameters of the same value. As in the following example where we set 5 minutes (i.e. 300 seconds) between each token.

    "interTriggerTimer": {
        "type": "distribution",
        "name": "uniform",
        "parameters": {
            "low": 300,
            "high": 300
        }
    }
    "interTriggerTimer": {
        "type": "custom"
    }

In the following example we define a simple time series model from the python library (statsmodels). The AutoReg model is trained on the real log and then we used it to predict the next token arrival in the process.

Finally, we defined the two calendars for Role 1 and Role 2 with the following code. Role 1 resources, Sara and Mike, work Monday through Friday, 8 a.m. to 4 p.m.

   "Role 1": {
        "resources": ["Sara", "Mike"],
        "calendar": {
            "days": [0, 1, 2, 3, 4],
            "hour_min": 8,
            "hour_max": 16
        }
   } 

While Role 2 resources, Ellen and Sue, work Monday through Saturday, 8 a.m. to 7 p.m.

   "Role 2": {
        "resources": ["Ellen", "Sue"],
        "calendar": {
            "days": [0, 1, 2, 3, 4, 5],
            "hour_min": 8,
            "hour_max": 19
        }
   }

To run the example with a exponential distribution:

   python run_simulation.py -p example/example_arrivals/bpi2012.pnml -s /example/example_arrivals/input_arrivals_example_distribution.json -t 20 -i 1 -o example_arrivals

or use the short command

   python run_simulation.py -e arrivalsD

To run the example with a time series model:

   python run_simulation.py -p example/example_arrivals/bpi2012.pnml -s example/example_arrivals/input_arrivals_example_timeseries.json -t 10 -i 1 -o example_arrivals

or use the short command

   python run_simulation.py -e arrivalsS

Decision Mining of petrinet

For this example we used the log that we can find in the folder example_decision_mining/BPIChallenge2012A.xes. This event log pertains to a loan application process of a Dutch financial institute. The data contains all applications filed trough an online system in 2016 and their subsequent events until February 1st 2017, 15:11. The company providing the data and the process under consideration is the same as doi:10.4121/uuid:3926db30-f712-4394-aebc-75976070e91f. Every process execution represents a single application to the bank by a customer for a personal loan. The application can be accepted or rejected by the bank, likewise the customer can withdraw the application at any time.

The petrinet found by the Inductive Miner algorithm from pm4py, is the following:

Alt Text

We also want to show how to add a case-specific attribute, requested loan amount, to each simulation trace. Through the function case_function_attribute(case_id) it is easy to add the attribute/s case with the return of a dictionary. In this case, for each trace, we randomly generate a loan amount from 1000 to 99999 euros.

    def case_function_attribute(case):
        return {"AMOUNT": random.randint(1000, 99999)}

The aim of this example is to present different ways to choose the next activity from a specific decision point of Petri net model. In the following image, the four decision points in the process are highlighted with three different colours.

Alt Text

As exaplained in the event trace class, we can define the 3 methods to define the next activity of decision point:

    "probability": {
        "skip_2": "AUTO",
        "A_ACCEPTED": "AUTO",
        "skip_3": "AUTO",
        "A_FINALIZED": "AUTO"
    }
    "probability": {
        "A_PREACCEPTED": 0.20,
        "skip_1": 0.80
    }
   "probability": {
      "A_CANCELLED": "CUSTOM",
      "A_DECLINED": "CUSTOM",
      "tauSplit_5": "CUSTOM"
   }

To train the model we used as input the following feature: the presence of A_PREACCEPTED, A_ACCEPTED, A_FINALIZED activities in the prefix of trace, the requested loan amount, the weekday and hour of the time of arrival in the decision point.

Alt Text

In general, if the user does not define probability parameters in the JSON file, the simulator automatically applies the AUTO mode for each decision point. Otherwise, if the user defines the mode for one or more decision points, the user still has to specify all of them even if they are in AUTO mode.

To run the example:

   python main.py -p ../example/example_decision_mining/bpi2012.pnml -s ../example/example_decision_mining/input_decision_mining_example.json -t 10 -i 1 -o example_decision_mining

or use the short command

   python run_simulation.py -e decision_mining

Process Times of petrinet

For this example, we used the log located in the example_process_times/synthetic_log.xes folder. To generate the synthetic log we define a simulation model inspired by the BPIC2012 process (the log in the other two examples). The log contains 4000 traces with 20 different activities.

Let's define the following Petri net model to generate a new simulation.

Alt Text

The aim of this example is to present several ways to set the processing time of each activity and the waiting time between them.

As explained in custom_function, we can apply two methods to define processing and waiting times:

"A_FINALIZED": {
    "name": "uniform",
    "parameters": {
        "low": 3600,
        "high": 7200
    }
}
  "processing_time": {
      "A_FINALIZED":  { "name": "custom"}
  }

Be careful: The simulator raises a WARNING if the distribution produces a negative value as processing/waiting time and instead sets the value to zero.

For this example, two random forests are trained to predict the processing time of the activity W_Complete_preaccepted_appl and the waiting time before the activity W_Call_after_offer. The remaining processing times are set with different probability distributions, and the waiting times are undefined, except for the A_REGISTERED activity. For the latter, it is assumed that 5 to 10 minutes are required to prepare documentation for registration, which is not included in the processing time. Therefore, the waiting times between the remaining activities are generated only by resource contention.

"waiting_time": {
    "W_Call_after_offer": {
       "name": "custom"
    },
    "A_REGISTERED": {
        "name": "uniform",
        "parameters": {
            "low": 300,
            "high": 600
        }
    }
}

The following figure describes the features involved in training the two random forest models. All features except "start_time" are intercase features that RIMS can provide during simulation. From the code shown, it can be seen that it is simple to predict the prediction at runtime using the intercase features.

Alt Text

def custom_processing_time(buffer: Buffer):
    input_feature = list()
    input_feature.append(buffer.get_feature("wip_start"))
    input_feature.append(buffer.get_feature("wip_activity"))
    input_feature.append(buffer.get_feature("start_time").weekday())
    input_feature.append(buffer.get_feature("start_time").hour)
    loaded_model = pickle.load(open(os.getcwd()+'/example/example_process_times/processing_time_random_forest.pkl', 'rb'))
    y_pred_f = loaded_model.predict([input_feature])
    return int(y_pred_f[0])
def custom_waiting_time(buffer: Buffer):
    input_feature = list()
    buffer.print_values()
    input_feature.append(buffer.get_feature("wip_wait"))
    input_feature.append(buffer.get_feature("wip_activity"))
    input_feature.append(buffer.get_feature("enabled_time").weekday())
    input_feature.append(buffer.get_feature("enabled_time").hour)
    input_feature.append(buffer.get_feature("ro_single"))
    input_feature.append(buffer.get_feature("queue"))
    loaded_model = pickle.load(
        open(os.getcwd() + '/example/example_process_times/waiting_time_random_forest.pkl', 'rb'))
    y_pred_f = loaded_model.predict([input_feature])
    return int(y_pred_f[0])

To run the example:

   python main.py -p ../example/example_decision_mining/bpi2012.pnml -s ../example/example_decision_mining/input_decision_mining_example.json -t 10 -i 1 -o example_decision_mining

or use the short command

   python run_simulation.py -e process_times

To see more complex case studies, it is possible to refer to the following papers: Runtime integration of machine learning and simulation for business processes (to appear at the ICPM conference) and Recommending the optimal policy by learning to act from temporal data. In the second paper, an ad-hoc version of ${RIMS}{\mathsf{_{Tool}}}$ is used to simulate the behavior of the environment in response to agent action recommended by the optimal policy discovered in a Reinforcement Learning scenario.

 1"""
 2.. include:: ../README.md
 3.. include:: example/example_arrivals/arrivals-example.md
 4.. include:: example/example_decision_mining/decision_mining-example.md
 5.. include:: example/example_process_times/process_times-example.md
 6"""
 7
 8import csv
 9import simpy
10import utility
11from process import SimulationProcess
12from event_trace import Token
13from parameters import Parameters
14import sys, getopt
15from utility import *
16import pm4py
17from inter_trigger_timer import InterTriggerTimer
18from result_analysis import Result
19from datetime import timedelta
20import warnings
21import sys
22
23EXAMPLE = {'arrivalsD': ['example/example_arrivals/bpi2012.pnml', 'example/example_arrivals/input_arrivals_example_distribution.json', 1, 15, 'example_arrivals'],
24           'arrivalsS': ['example/example_arrivals/bpi2012.pnml', 'example/example_arrivals/input_arrivals_example_timeseries.json', 1, 15, 'example_arrivals'],
25           'process_times': ['example/example_process_times/synthetic_petrinet.pnml', 'example/example_process_times/input_process_times_example.json', 1, 15, 'example_process_times'],
26           'decision mining': ['example/example_decision_mining/bpi2012.pnml', 'example/example_decision_mining/input_decision_mining_example.json', 1, 10, 'example_decision_mining'],
27           }
28
29
30def setup(env: simpy.Environment, PATH_PETRINET, params, i, NAME):
31    simulation_process = SimulationProcess(env, params)
32    utility.define_folder_output("output/output_{}".format(NAME))
33    f = open("output/output_{}/simulated_log_{}_{}".format(NAME, NAME, i)+".csv", 'w')
34    writer = csv.writer(f)
35    writer.writerow(Buffer(writer).get_buffer_keys())
36    net, im, fm = pm4py.read_pnml(PATH_PETRINET)
37    interval = InterTriggerTimer(params, simulation_process, params.START_SIMULATION)
38    for i in range(0, params.TRACES):
39        prefix = Prefix()
40        itime = interval.get_next_arrival(env, i)
41        yield env.timeout(itime)
42        parallel_object = utility.ParallelObject()
43        time_trace = params.START_SIMULATION + timedelta(seconds=env.now)
44        env.process(Token(i, net, im, params, simulation_process, prefix, 'sequential', writer, parallel_object, time_trace, None).simulation(env))
45
46
47def run_simulation(PATH_PETRINET, PATH_PARAMETERS, N_SIMULATION, N_TRACES, NAME):
48    params = Parameters(PATH_PARAMETERS, N_TRACES)
49    for i in range(0, N_SIMULATION):
50        env = simpy.Environment()
51        env.process(setup(env, PATH_PETRINET, params, i, NAME))
52        env.run(until=params.SIM_TIME)
53
54    result = Result("output_{}".format(NAME), params)
55    result._analyse()
56
57
58def main(argv):
59    opts, args = getopt.getopt(argv, "h:p:s:t:i:o:e:")
60    for opt, arg in opts:
61        print(opt, arg)
62        if opt == '-h':
63            print('main.py -p <petrinet in pnml format> -s <parameters read from file .json> -t <total number of traces> -i <total number of simulation>')
64            sys.exit()
65        elif opt == "-p":
66            PATH_PETRINET = arg
67        elif opt == "-s":
68            PATH_PARAMETERS = arg
69        elif opt == "-t":
70            N_TRACES = int(arg)
71        elif opt == "-i":
72            N_SIMULATION = int(arg)
73        elif opt == "-o":
74            NAME = arg
75        elif opt == "-e":
76            if arg in EXAMPLE:
77                PATH_PETRINET, PATH_PARAMETERS, N_SIMULATION, N_TRACES, NAME = EXAMPLE[arg]
78            else:
79                raise ValueError('The keywords for the provided examples are the following: arrivalsD, arrivalsS, decision_mining, process_times')
80    print(PATH_PETRINET, PATH_PARAMETERS, N_SIMULATION, N_TRACES, NAME)
81    run_simulation(PATH_PETRINET, PATH_PARAMETERS, N_SIMULATION, N_TRACES, NAME)
82
83
84if __name__ == "__main__":
85    warnings.filterwarnings("ignore")
86    main(sys.argv[1:])