-
Notifications
You must be signed in to change notification settings - Fork 286
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'upstream/hotfixes' into release
- Loading branch information
Showing
13 changed files
with
542 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from pm4py.algo.discovery.powl.inductive.variants import * | ||
|
1 change: 1 addition & 0 deletions
1
pm4py/algo/discovery/powl/inductive/variants/brute_force/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from pm4py.algo.discovery.powl.inductive.variants.brute_force import * |
202 changes: 202 additions & 0 deletions
202
pm4py/algo/discovery/powl/inductive/variants/brute_force/bf_partial_order_cut.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
from abc import ABC | ||
from collections import Counter | ||
from itertools import combinations | ||
from typing import Any, Optional, Dict, List, Generic, Tuple, Collection | ||
|
||
from pm4py.algo.discovery.inductive.cuts.abc import Cut, T | ||
|
||
from pm4py.algo.discovery.inductive.dtypes.im_ds import IMDataStructureUVCL | ||
from pm4py.objects.powl.BinaryRelation import BinaryRelation | ||
from pm4py.objects.powl.obj import StrictPartialOrder, POWL | ||
from pm4py.objects.dfg import util as dfu | ||
from pm4py.statistics.eventually_follows.uvcl.get import apply as to_efg | ||
|
||
# MAX_NUM_PARTITIONS = 1000 | ||
|
||
def remove(blocks, g): | ||
res = [] | ||
for g2 in blocks: | ||
if not g2.__str__().__eq__(g.__str__()): | ||
res.append(g2) | ||
return res | ||
|
||
|
||
def contains(blocks, g): | ||
for g2 in blocks: | ||
if g2.__str__().__eq__(g.__str__()): | ||
return True | ||
return False | ||
|
||
|
||
def xor(a, b): | ||
if a and not b: | ||
return True | ||
elif not a and b: | ||
return True | ||
else: | ||
return False | ||
|
||
|
||
def get_partitions_of_size_k(nodes, k=None): | ||
n = len(nodes) | ||
if k is not None: | ||
if k < 1: | ||
raise ValueError( | ||
"Can't partition in a negative or zero number of groups" | ||
) | ||
elif k > n: | ||
return | ||
|
||
def set_partitions_helper(L, k): | ||
n = len(L) | ||
if k == 1: | ||
yield [tuple(L)] | ||
elif n == k: | ||
yield [tuple([s]) for s in L] | ||
else: | ||
e, *M = L | ||
for p in set_partitions_helper(M, k - 1): | ||
yield [tuple([e]), *p] | ||
for p in set_partitions_helper(M, k): | ||
for i in range(len(p)): | ||
yield p[:i] + [tuple([e]) + p[i]] + p[i + 1:] | ||
|
||
if k is None: | ||
for k in range(1, n + 1): | ||
yield from set_partitions_helper(nodes, k) | ||
else: | ||
yield from set_partitions_helper(nodes, k) | ||
|
||
|
||
def partition(collection): | ||
# count = 1 | ||
i = len(collection) | ||
while i > 1: | ||
for part in get_partitions_of_size_k(collection, i): | ||
# if count > MAX_NUM_PARTITIONS: | ||
# return | ||
# count = count + 1 | ||
yield part | ||
i = i - 1 | ||
return | ||
|
||
|
||
def generate_order(parts, efg): | ||
nodes = parts | ||
po = BinaryRelation(nodes) | ||
for group_1, group_2 in combinations(nodes, 2): | ||
all_ef_g1_g2 = True | ||
all_ef_g2_g1 = True | ||
none_ef_g1_g2 = True | ||
none_ef_g2_g1 = True | ||
for a in group_1: | ||
for b in group_2: | ||
if (a, b) in efg: | ||
none_ef_g1_g2 = False | ||
else: | ||
all_ef_g1_g2 = False | ||
|
||
if (b, a) in efg: | ||
none_ef_g2_g1 = False | ||
else: | ||
all_ef_g2_g1 = False | ||
|
||
if all_ef_g1_g2 and none_ef_g2_g1: | ||
po.add_edge(group_1, group_2) | ||
elif none_ef_g1_g2 and all_ef_g2_g1: | ||
po.add_edge(group_2, group_1) | ||
|
||
return po | ||
|
||
|
||
def is_valid_order(po, dfg_graph, efg): | ||
if not po.is_strict_partial_order(): | ||
return False | ||
|
||
start_blocks = po.nodes | ||
end_blocks = po.nodes | ||
|
||
for group_1, group_2 in combinations(po.nodes, 2): | ||
|
||
edge_g1_g2 = po.is_edge(group_1, group_2) | ||
edge_g2_g1 = po.is_edge(group_2, group_1) | ||
|
||
if edge_g1_g2: | ||
start_blocks = remove(start_blocks, group_2) | ||
end_blocks = remove(end_blocks, group_1) | ||
if edge_g2_g1: | ||
start_blocks = remove(start_blocks, group_1) | ||
end_blocks = remove(end_blocks, group_2) | ||
|
||
all_ef_g1_g2 = True | ||
all_ef_g2_g1 = True | ||
|
||
for a in group_1: | ||
for b in group_2: | ||
if not (a, b) in efg: | ||
all_ef_g1_g2 = False | ||
if not (b, a) in efg: | ||
all_ef_g2_g1 = False | ||
if all_ef_g1_g2 and all_ef_g2_g1 and (edge_g1_g2 or edge_g2_g1): | ||
return False | ||
if not edge_g1_g2 and not edge_g2_g1 and not (all_ef_g1_g2 and all_ef_g2_g1): | ||
return False | ||
|
||
n = len(po.nodes) | ||
for i in range(n): | ||
group = po.nodes[i] | ||
c1 = contains(start_blocks, group) | ||
c2 = len(set(group).intersection(set(dfg_graph.start_activities.keys()))) > 0 | ||
c3 = contains(end_blocks, group) | ||
c4 = len(set(group).intersection(set(dfg_graph.end_activities.keys()))) > 0 | ||
if (c1 and not c2) or (c3 and not c4): | ||
return False | ||
|
||
return True | ||
|
||
|
||
class BruteForcePartialOrderCut(Cut[T], ABC, Generic[T]): | ||
|
||
@classmethod | ||
def operator(cls, parameters: Optional[Dict[str, Any]] = None) -> StrictPartialOrder: | ||
return StrictPartialOrder([]) | ||
|
||
@classmethod | ||
def holds(cls, obj: T, parameters: Optional[Dict[str, Any]] = None) -> Optional[BinaryRelation]: | ||
dfg_graph = obj.dfg | ||
efg = to_efg(obj) | ||
alphabet = sorted(dfu.get_vertices(dfg_graph), key=lambda g: g.__str__()) | ||
for part in partition(alphabet): | ||
# print(part) | ||
po = generate_order(part, efg) | ||
if is_valid_order(po, dfg_graph, efg): | ||
return po | ||
return None | ||
|
||
@classmethod | ||
def apply(cls, obj: T, parameters: Optional[Dict[str, Any]] = None) -> Optional[Tuple[StrictPartialOrder, List[POWL]]]: | ||
g = cls.holds(obj, parameters) | ||
if g is None: | ||
return g | ||
children = cls.project(obj, g.nodes, parameters) | ||
po = StrictPartialOrder(children) | ||
for i, j in combinations(range(len(g.nodes)), 2): | ||
if g.is_edge_id(i, j): | ||
po.order.add_edge(children[i], children[j]) | ||
elif g.is_edge_id(j, i): | ||
po.order.add_edge(children[j], children[i]) | ||
return po, po.children | ||
|
||
|
||
class BruteForcePartialOrderCutUVCL(BruteForcePartialOrderCut[IMDataStructureUVCL]): | ||
|
||
@classmethod | ||
def project(cls, obj: IMDataStructureUVCL, groups: List[Collection[Any]], | ||
parameters: Optional[Dict[str, Any]] = None) -> List[IMDataStructureUVCL]: | ||
r = list() | ||
for g in groups: | ||
c = Counter() | ||
for t in obj.data_structure: | ||
c[tuple(filter(lambda e: e in g, t))] = obj.data_structure[t] | ||
r.append(c) | ||
return list(map(lambda l: IMDataStructureUVCL(l), r)) |
29 changes: 29 additions & 0 deletions
29
pm4py/algo/discovery/powl/inductive/variants/brute_force/factory.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from typing import List, Optional, Dict, Any, Tuple, Type | ||
|
||
from pm4py.algo.discovery.powl.inductive.cuts.concurrency import POWLConcurrencyCutUVCL | ||
from pm4py.algo.discovery.powl.inductive.cuts.factory import S, T, CutFactory | ||
from pm4py.algo.discovery.powl.inductive.cuts.loop import POWLLoopCutUVCL | ||
from pm4py.algo.discovery.powl.inductive.cuts.sequence import POWLStrictSequenceCutUVCL | ||
from pm4py.algo.discovery.powl.inductive.cuts.xor import POWLExclusiveChoiceCutUVCL | ||
from pm4py.algo.discovery.inductive.dtypes.im_ds import IMDataStructureUVCL, IMDataStructure | ||
from pm4py.algo.discovery.powl.inductive.variants.brute_force.bf_partial_order_cut import BruteForcePartialOrderCutUVCL | ||
from pm4py.algo.discovery.powl.inductive.variants.powl_discovery_varaints import POWLDiscoveryVariant | ||
from pm4py.objects.powl.obj import POWL | ||
|
||
|
||
class CutFactoryPOBF(CutFactory): | ||
|
||
@classmethod | ||
def get_cuts(cls, obj: T, inst: POWLDiscoveryVariant, parameters: Optional[Dict[str, Any]] = None) -> List[Type[S]]: | ||
if type(obj) is IMDataStructureUVCL: | ||
return [POWLExclusiveChoiceCutUVCL, POWLStrictSequenceCutUVCL, POWLConcurrencyCutUVCL, POWLLoopCutUVCL, BruteForcePartialOrderCutUVCL] | ||
return list() | ||
|
||
@classmethod | ||
def find_cut(cls, obj: IMDataStructure, inst: POWLDiscoveryVariant, parameters: Optional[Dict[str, Any]] = None) -> Optional[ | ||
Tuple[POWL, List[T]]]: | ||
for c in CutFactoryPOBF.get_cuts(obj, inst): | ||
r = c.apply(obj, parameters) | ||
if r is not None: | ||
return r | ||
return None |
1 change: 1 addition & 0 deletions
1
pm4py/algo/discovery/powl/inductive/variants/clustering/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from pm4py.algo.discovery.powl.inductive.variants.clustering import * |
Oops, something went wrong.