pm4py/pm4py-core

What does "Exception: Loop doesn't have 2 childs" mean?

galenseilis opened this issue · 4 comments

I have an event log with 1034827 rows. After preparing it with pm4py.format_dataframe and running pm4py.discover_process_tree_inductive the result, I then ran pm4py.convert_to_bpmn in which it seems to have run into Exception: Loop doesn't have 2 childs.

I don't know the inner workings of PM4Py to infer what this means, but I know this code works on a handful of subsets of the data. Is 1034827 too much? Or is there a data quality issue that that needs to be addressed? Or something else?

Here is the part of my traceback concerning PM4Py (the rest is Kedro pipeline calls that I don't think have any relevance).

│                                                                                                  │
│   17 │   │   │   name='mine_process_tree'                                                        │
│   18 │   │   ),                                                                                  │
│   19 │   │   node(                                                                               │
│ > 20 │   │   │   func=lambda tree: pm4py.convert_to_bpmn(tree),                                  │
│   21 │   │   │   inputs='process_tree',                                                          │
│   22 │   │   │   outputs='bpmn_model',                                                           │
│   23 │   │   │   name='make_bpmn_model'                                                          │
│                                                                                                  │
\pm4py\convert.py:160 in convert_to_bpmn        │
│                                                                                                  │
│   157 │   │   return args[0]                                                                     │
│   158elif isinstance(args[0], ProcessTree):                                                 │
│   159 │   │   from pm4py.objects.conversion.process_tree.variants import to_bpmn                 │
│ > 160 │   │   return to_bpmn.apply(args[0])                                                      │
│   161else:                                                                                  │
│   162 │   │   # try to convert the object to a Petri net. Then, use the PM4Py PN-to-BPMN conve   │163 │   │   # to get the BPMN object                                                           │
│                                                                                                  │
| \pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:257 in apply                                                                         │
│                                                                                                  │
│   254end_event = BPMN.NormalEndEvent(name="end")                                            │
│   255bpmn.add_node(start_event)                                                             │
│   256bpmn.add_node(end_event)                                                               │
│ > 257bpmn, counts, _, _ = recursively_add_tree(tree, tree, bpmn, start_event, end_event,    │
│   258bpmn = delete_tau_transitions(bpmn, counts)                                            │
│   259 │                                                                                          │
│   260for node in bpmn.get_nodes():                                                          │
│                                                                                                  │
| \pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:179 in recursively_add_tree                                                          │
│                                                                                                  │
│   176 │   │   initial_intermediate_task = initial_event                                          │
│   177 │   │   bpmn, final_intermediate_task, counts = add_tau_task(bpmn, counts)                 │
│   178 │   │   for i in range(len(tree_childs)):                                                  │
│ > 179 │   │   │   bpmn, counts, initial_connect, final_connect = recursively_add_tree(tree, tr   │
│   180 │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   initial_   │
│   181 │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   final_in   │
│   182 │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   rec_dept   │
│                                                                                                  │
| \pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:156 in recursively_add_tree                                                          │
│                                                                                                  │
│   153elif tree.operator == Operator.PARALLEL:                                               │
│   154 │   │   bpmn, split_gateway, join_gateway, counts = add_parallel_gateway(bpmn, counts)     │
│   155 │   │   for subtree in tree_childs:                                                        │
│ > 156 │   │   │   bpmn, counts, x, y = recursively_add_tree(tree, subtree, bpmn, split_gateway   │
│   157 │   │   │   │   │   │   │   │   │   │   │   │   │     counts,                              │
│   158 │   │   │   │   │   │   │   │   │   │   │   │   │     rec_depth + 1)                       │
│   159 │   │   bpmn.add_flow(BPMN.Flow(initial_event, split_gateway))                             │
│                                                                                                  │
| \pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:179 in recursively_add_tree                                                          │
│                                                                                                  │
│   176 │   │   initial_intermediate_task = initial_event                                          │
│   177 │   │   bpmn, final_intermediate_task, counts = add_tau_task(bpmn, counts)                 │
│   178 │   │   for i in range(len(tree_childs)):                                                  │
│ > 179 │   │   │   bpmn, counts, initial_connect, final_connect = recursively_add_tree(tree, tr   │
│   180 │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   initial_   │
│   181 │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   final_in   │
│   182 │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   rec_dept   │
│                                                                                                  │
│ \pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:156 in recursively_add_tree                                                          │
│                                                                                                  │
│   153elif tree.operator == Operator.PARALLEL:                                               │
│   154 │   │   bpmn, split_gateway, join_gateway, counts = add_parallel_gateway(bpmn, counts)     │
│   155 │   │   for subtree in tree_childs:                                                        │
│ > 156 │   │   │   bpmn, counts, x, y = recursively_add_tree(tree, subtree, bpmn, split_gateway   │
│   157 │   │   │   │   │   │   │   │   │   │   │   │   │     counts,                              │
│   158 │   │   │   │   │   │   │   │   │   │   │   │   │     rec_depth + 1)                       │
│   159 │   │   bpmn.add_flow(BPMN.Flow(initial_event, split_gateway))                             │
│                                                                                                  │
│\pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:179 in recursively_add_tree                                                          │
│                                                                                                  │
│   176 │   │   initial_intermediate_task = initial_event                                          │
│   177 │   │   bpmn, final_intermediate_task, counts = add_tau_task(bpmn, counts)                 │
│   178 │   │   for i in range(len(tree_childs)):                                                  │
│ > 179 │   │   │   bpmn, counts, initial_connect, final_connect = recursively_add_tree(tree, tr   │
│   180 │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   initial_   │
│   181 │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   final_in   │
│   182 │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   rec_dept   │
│                                                                                                  │
│\pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:199 in recursively_add_tree                                                          │
│                                                                                                  │
│   196 │   │   │   do = tree_childs[0]                                                            │
│   197 │   │   │   redo = tree_childs[1]                                                          │
│   198 │   │   │   bpmn, split, join, counts = add_xor_gateway(bpmn, counts)                      │
│ > 199 │   │   │   bpmn, counts, i, y = recursively_add_tree(tree, do, bpmn, join, split, count   │
│   200 │   │   │   bpmn, counts, x, y = recursively_add_tree(tree, redo, bpmn, split, join, cou   │
│   201 │   │   │   bpmn.add_flow(BPMN.Flow(initial_event, join))                                  │
│   202 │   │   │   bpmn.add_flow(BPMN.Flow(split, final_event))                                   │
│                                                                                                  │
│ \pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:145 in recursively_add_tree                                                          │
│                                                                                                  │
│   142elif tree.operator == Operator.XOR:                                                    │
│   143 │   │   bpmn, split_gateway, join_gateway, counts = add_xor_gateway(bpmn, counts)          │
│   144 │   │   for subtree in tree_childs:                                                        │
│ > 145 │   │   │   bpmn, counts, x, y = recursively_add_tree(tree, subtree, bpmn, split_gateway   │
│   146 │   │   │   │   │   │   │   │   │   │   │   │   │     counts,                              │
│   147 │   │   │   │   │   │   │   │   │   │   │   │   │     rec_depth + 1)                       │
│   148 │   │   bpmn.add_flow(BPMN.Flow(initial_event, split_gateway))                             │
│                                                                                                  │
│\pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:156 in recursively_add_tree                                                          │
│                                                                                                  │
│   153elif tree.operator == Operator.PARALLEL:                                               │
│   154 │   │   bpmn, split_gateway, join_gateway, counts = add_parallel_gateway(bpmn, counts)     │
│   155 │   │   for subtree in tree_childs:                                                        │
│ > 156 │   │   │   bpmn, counts, x, y = recursively_add_tree(tree, subtree, bpmn, split_gateway   │
│   157 │   │   │   │   │   │   │   │   │   │   │   │   │     counts,                              │
│   158 │   │   │   │   │   │   │   │   │   │   │   │   │     rec_depth + 1)                       │
│   159 │   │   bpmn.add_flow(BPMN.Flow(initial_event, split_gateway))                             │
│                                                                                                  │
│\pm4py\objects\conversion\process_tree\variants │
│ \to_bpmn.py:194 in recursively_add_tree                                                          │
│                                                                                                  │
│   191 │                                                                                          │
│   192elif tree.operator == Operator.LOOP:                                                   │
│   193 │   │   if len(tree_childs) != 2:                                                          │
│ > 194 │   │   │   raise Exception("Loop doesn't have 2 childs")                                  │
│   195 │   │   else:                                                                              │
│   196 │   │   │   do = tree_childs[0]                                                            │
│   197 │   │   │   redo = tree_childs[1]

Dear @galenseilis

Could you confirm you have the latest version of pm4py version 2.7.5?

Dear @galenseilis

Could you confirm you have the latest version of pm4py version 2.7.5?

@fit-alessandro-berti

Yes, I can confirm that:

>>> pm4py.__version__
'2.7.5'

Also, I am using Python 3.10.4, if that makes any difference. The OS is Windows Server 2019 Standard.

Thanks for signaling. We'll release a fix for this in the next release.

In the meanwhile, you can use the following workaround:

pm4py.convert_to_bpmn(*pm4py.convert_to_petri_net(tree))