Skip to content

Commit 6f705dd

Browse files
Merge remote-tracking branch 'upstream/hotfixes' into release
2 parents 76c1145 + f5e78bb commit 6f705dd

File tree

1 file changed

+164
-41
lines changed

1 file changed

+164
-41
lines changed

pm4py/objects/powl/obj.py

Lines changed: 164 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
from abc import ABC, abstractmethod
3030

3131

32-
3332
class POWL(ProcessTree, ABC):
3433
def print(self) -> None:
3534
print(self.to_string())
@@ -43,10 +42,14 @@ def simplify(self) -> "POWL":
4342
def validate_partial_orders(self):
4443
if isinstance(self, StrictPartialOrder):
4544
if not self.order.is_irreflexive():
46-
raise Exception("The irreflexivity of the partial order is violated!")
45+
raise Exception(
46+
"The irreflexivity of the partial order is violated!"
47+
)
4748
if not self.order.is_transitive():
48-
raise Exception("The transitivity of the partial order is violated!")
49-
if hasattr(self, 'children'):
49+
raise Exception(
50+
"The transitivity of the partial order is violated!"
51+
)
52+
if hasattr(self, "children"):
5053
for child in self.children:
5154
child.validate_partial_orders()
5255

@@ -84,7 +87,10 @@ def copy(self):
8487

8588
def __eq__(self, other: object) -> bool:
8689
if isinstance(other, Transition):
87-
return self._label == other._label and self._identifier == other._identifier
90+
return (
91+
self._label == other._label
92+
and self._identifier == other._identifier
93+
)
8894
return False
8995

9096
def equal_content(self, other: object) -> bool:
@@ -116,7 +122,9 @@ def copy(self):
116122

117123

118124
class FrequentTransition(Transition):
119-
def __init__(self, label, min_freq: Union[str, int], max_freq: Union[str, int]) -> None:
125+
def __init__(
126+
self, label, min_freq: Union[str, int], max_freq: Union[str, int]
127+
) -> None:
120128
self.skippable = False
121129
self.selfloop = False
122130
if min_freq == 0:
@@ -126,7 +134,15 @@ def __init__(self, label, min_freq: Union[str, int], max_freq: Union[str, int])
126134
min_freq = "1"
127135
self.activity = label
128136
if self.skippable or self.selfloop:
129-
label = str(label) + "\n" + "[" + str(min_freq) + "," + str(max_freq) + "]"
137+
label = (
138+
str(label)
139+
+ "\n"
140+
+ "["
141+
+ str(min_freq)
142+
+ ","
143+
+ str(max_freq)
144+
+ "]"
145+
)
130146

131147
super().__init__(label=label)
132148

@@ -140,15 +156,14 @@ def __init__(self, nodes: TList[POWL]) -> None:
140156
self.additional_information = None
141157

142158
def copy(self):
143-
copied_nodes = {n:n.copy() for n in self.order.nodes}
159+
copied_nodes = {n: n.copy() for n in self.order.nodes}
144160
res = StrictPartialOrder(list(copied_nodes.values()))
145161
for n1 in self.order.nodes:
146162
for n2 in self.order.nodes:
147163
if self.order.is_edge(n1, n2):
148164
res.add_edge(copied_nodes[n1], copied_nodes[n2])
149165
return res
150166

151-
152167
def _set_order(self, nodes: TList[POWL]) -> None:
153168
self.order = BinaryRelation(nodes)
154169

@@ -162,10 +177,52 @@ def get_children(self) -> TList[POWL]:
162177
return self.order.nodes
163178

164179
def to_string(self, level=0, indent=False, max_indent=sys.maxsize) -> str:
165-
model_string = STRICT_PARTIAL_ORDER_LABEL + self.order.__repr__()
166-
if indent:
167-
model_string = "\n".join(hie_utils.indent_representation(model_string, max_indent=max_indent))
168-
return model_string
180+
"""
181+
Represents a StrictPartialOrder as a string, avoiding infinite recursion.
182+
183+
Parameters
184+
----------
185+
level : int
186+
Current indentation level
187+
indent : bool
188+
Whether to indent the output
189+
max_indent : int
190+
Maximum indentation level
191+
192+
Returns
193+
-------
194+
str
195+
String representation of the partial order
196+
"""
197+
# Start with the partial order label
198+
rep = f"{STRICT_PARTIAL_ORDER_LABEL}=(nodes={{"
199+
200+
# Represent the nodes (children)
201+
nodes_str = []
202+
for i, node in enumerate(self.order.nodes):
203+
# Call to_string on each child with increased level, preventing recursive blow-up
204+
node_str = node.to_string(level=level + 1, indent=False, max_indent=max_indent)
205+
nodes_str.append(node_str)
206+
rep += ", ".join(nodes_str)
207+
rep += "}, order={"
208+
209+
# Represent the edges in the partial order
210+
edges_str = []
211+
for source in self.order.nodes:
212+
for target in self.order.nodes:
213+
if self.order.is_edge(source, target):
214+
# Use a simplified representation for source and target to avoid recursion
215+
source_str = source.label if source.label else f"id_{hash(source)}"
216+
target_str = target.label if target.label else f"id_{hash(target)}"
217+
edges_str.append(f"{source_str}-->{target_str}")
218+
rep += ", ".join(edges_str)
219+
rep += "})"
220+
221+
# Apply indentation if requested
222+
if indent and level <= max_indent:
223+
rep = "\n".join(hie_utils.indent_representation(rep, max_indent=max_indent))
224+
225+
return rep
169226

170227
def __repr__(self) -> str:
171228
return self.to_string()
@@ -220,19 +277,28 @@ def equal_content(self, other: object) -> bool:
220277
for j in range(len(ordered_nodes_1)):
221278
target_1 = ordered_nodes_1[j]
222279
target_2 = ordered_nodes_2[j]
223-
if self.order.is_edge(source_1, target_1) and not other.order.is_edge(source_2, target_2):
280+
if self.order.is_edge(
281+
source_1, target_1
282+
) and not other.order.is_edge(source_2, target_2):
224283
return False
225-
if not self.order.is_edge(source_1, target_1) and other.order.is_edge(source_2, target_2):
284+
if not self.order.is_edge(
285+
source_1, target_1
286+
) and other.order.is_edge(source_2, target_2):
226287
return False
227288
return True
228289

229290
def simplify_using_frequent_transitions(self) -> "StrictPartialOrder":
230-
new_nodes = {node: node.simplify_using_frequent_transitions() for node in self.children}
291+
new_nodes = {
292+
node: node.simplify_using_frequent_transitions()
293+
for node in self.children
294+
}
231295
res = StrictPartialOrder(list(new_nodes.values()))
232296
for node_1 in self.children:
233297
for node_2 in self.children:
234298
if self.partial_order.is_edge(node_1, node_2):
235-
res.partial_order.add_edge(new_nodes[node_1], new_nodes[node_2])
299+
res.partial_order.add_edge(
300+
new_nodes[node_1], new_nodes[node_2]
301+
)
236302

237303
return res
238304

@@ -244,7 +310,9 @@ def simplify(self) -> "StrictPartialOrder":
244310

245311
def connected(node):
246312
for node2 in self.children:
247-
if self.partial_order.is_edge(node, node2) or self.partial_order.is_edge(node2, node):
313+
if self.partial_order.is_edge(
314+
node, node2
315+
) or self.partial_order.is_edge(node2, node):
248316
return True
249317
return False
250318

@@ -273,14 +341,25 @@ def connected(node):
273341
for node_1 in self.children:
274342
for node_2 in self.children:
275343
if self.partial_order.is_edge(node_1, node_2):
276-
if node_1 in simplified_nodes.keys() and node_2 in simplified_nodes.keys():
277-
res.partial_order.add_edge(simplified_nodes[node_1], simplified_nodes[node_2])
344+
if (
345+
node_1 in simplified_nodes.keys()
346+
and node_2 in simplified_nodes.keys()
347+
):
348+
res.partial_order.add_edge(
349+
simplified_nodes[node_1], simplified_nodes[node_2]
350+
)
278351
elif node_1 in simplified_nodes.keys():
279-
res.partial_order.add_edge(simplified_nodes[node_1], start_nodes[node_2])
352+
res.partial_order.add_edge(
353+
simplified_nodes[node_1], start_nodes[node_2]
354+
)
280355
elif node_2 in simplified_nodes.keys():
281-
res.partial_order.add_edge(end_nodes[node_1], simplified_nodes[node_2])
356+
res.partial_order.add_edge(
357+
end_nodes[node_1], simplified_nodes[node_2]
358+
)
282359
else:
283-
res.partial_order.add_edge(end_nodes[node_1], start_nodes[node_2])
360+
res.partial_order.add_edge(
361+
end_nodes[node_1], start_nodes[node_2]
362+
)
284363
for po, simplified_po in sub_nodes.items():
285364
for node_1 in simplified_po.children:
286365
for node_2 in simplified_po.children:
@@ -305,7 +384,9 @@ class OperatorPOWL(POWL):
305384
def __init__(self, operator: Operator, children: TList[POWL]) -> None:
306385
if operator is Operator.XOR:
307386
if len(children) < 2:
308-
raise Exception("Cannot create a choice of less than 2 submodels!")
387+
raise Exception(
388+
"Cannot create a choice of less than 2 submodels!"
389+
)
309390
elif operator is Operator.LOOP:
310391
if len(children) != 2:
311392
raise Exception("Only loops of length 2 are supported!")
@@ -350,33 +431,68 @@ def simplify_using_frequent_transitions(self) -> POWL:
350431
if self.operator is Operator.XOR and len(self.children) == 2:
351432
child_0 = self.children[0]
352433
child_1 = self.children[1]
353-
if isinstance(child_0, Transition) and isinstance(child_1, SilentTransition):
354-
return FrequentTransition(label=child_0.label, min_freq=0, max_freq=1)
355-
elif isinstance(child_1, Transition) and isinstance(child_0, SilentTransition):
356-
return FrequentTransition(label=child_1.label, min_freq=0, max_freq=1)
434+
if isinstance(child_0, Transition) and isinstance(
435+
child_1, SilentTransition
436+
):
437+
return FrequentTransition(
438+
label=child_0.label, min_freq=0, max_freq=1
439+
)
440+
elif isinstance(child_1, Transition) and isinstance(
441+
child_0, SilentTransition
442+
):
443+
return FrequentTransition(
444+
label=child_1.label, min_freq=0, max_freq=1
445+
)
357446

358447
if self.operator is Operator.LOOP and len(self.children) == 2:
359448
child_0 = self.children[0]
360449
child_1 = self.children[1]
361-
if isinstance(child_0, Transition) and isinstance(child_1, SilentTransition):
362-
return FrequentTransition(label=child_0.label, min_freq=1, max_freq="-")
363-
elif isinstance(child_1, Transition) and isinstance(child_0, SilentTransition):
364-
return FrequentTransition(label=child_1.label, min_freq=0, max_freq="-")
365-
366-
return OperatorPOWL(self.operator, [child.simplify_using_frequent_transitions() for child in self.children])
450+
if isinstance(child_0, Transition) and isinstance(
451+
child_1, SilentTransition
452+
):
453+
return FrequentTransition(
454+
label=child_0.label, min_freq=1, max_freq="-"
455+
)
456+
elif isinstance(child_1, Transition) and isinstance(
457+
child_0, SilentTransition
458+
):
459+
return FrequentTransition(
460+
label=child_1.label, min_freq=0, max_freq="-"
461+
)
462+
463+
return OperatorPOWL(
464+
self.operator,
465+
[
466+
child.simplify_using_frequent_transitions()
467+
for child in self.children
468+
],
469+
)
367470

368471
def simplify(self) -> "OperatorPOWL":
369472
if self.operator is Operator.XOR and len(self.children) == 2:
370473
child_0 = self.children[0]
371474
child_1 = self.children[1]
372475

373476
def merge_with_children(child0, child1):
374-
if isinstance(child0, SilentTransition) and isinstance(child1, OperatorPOWL) \
375-
and child1.operator is Operator.LOOP:
477+
if (
478+
isinstance(child0, SilentTransition)
479+
and isinstance(child1, OperatorPOWL)
480+
and child1.operator is Operator.LOOP
481+
):
376482
if isinstance(child1.children[0], SilentTransition):
377-
return OperatorPOWL(Operator.LOOP, [n.simplify() for n in child1.children])
483+
return OperatorPOWL(
484+
Operator.LOOP,
485+
[n.simplify() for n in child1.children],
486+
)
378487
elif isinstance(child1.children[1], SilentTransition):
379-
return OperatorPOWL(Operator.LOOP, list(reversed([n.simplify() for n in child1.children])))
488+
return OperatorPOWL(
489+
Operator.LOOP,
490+
list(
491+
reversed(
492+
[n.simplify() for n in child1.children]
493+
)
494+
),
495+
)
380496

381497
return None
382498

@@ -392,11 +508,18 @@ def merge_with_children(child0, child1):
392508
new_children = []
393509
for child in self.children:
394510
s_child = child.simplify()
395-
if isinstance(s_child, OperatorPOWL) and s_child.operator is Operator.XOR:
511+
if (
512+
isinstance(s_child, OperatorPOWL)
513+
and s_child.operator is Operator.XOR
514+
):
396515
for node in s_child.children:
397516
new_children.append(node.simplify())
398517
else:
399518
new_children.append(s_child)
400-
return OperatorPOWL(Operator.XOR, [child for child in new_children])
519+
return OperatorPOWL(
520+
Operator.XOR, [child for child in new_children]
521+
)
401522
else:
402-
return OperatorPOWL(self.operator, [child.simplify() for child in self.children])
523+
return OperatorPOWL(
524+
self.operator, [child.simplify() for child in self.children]
525+
)

0 commit comments

Comments
 (0)