openprocess 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cpnpy/__init__.py +46 -0
- openprocess/__init__.py +57 -0
- openprocess/analysis/__init__.py +0 -0
- openprocess/analysis/state_space.py +521 -0
- openprocess/analysis/state_space_process.py +251 -0
- openprocess/cli.py +742 -0
- openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/answer.pnml +31 -0
- openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/question.md +38 -0
- openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/answer.pnml +27 -0
- openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/net.pnml +29 -0
- openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/question.md +65 -0
- openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/log.txt +1 -0
- openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/question.md +67 -0
- openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/question.md +75 -0
- openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/ts.txt +5 -0
- openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/net.pnml +27 -0
- openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/question.md +65 -0
- openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/log.txt +1 -0
- openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/question.md +62 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/log.txt +1 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m1.pnml +36 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m2.pnml +28 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m3.pnml +30 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/net.pnml +36 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/question.md +69 -0
- openprocess/exercises/pack.md +14 -0
- openprocess/flow/__init__.py +50 -0
- openprocess/flow/box.py +466 -0
- openprocess/flow/boxes/__init__.py +7 -0
- openprocess/flow/boxes/check.py +119 -0
- openprocess/flow/boxes/compare.py +16 -0
- openprocess/flow/boxes/cpn.py +53 -0
- openprocess/flow/boxes/discover.py +124 -0
- openprocess/flow/boxes/filter.py +80 -0
- openprocess/flow/boxes/input.py +124 -0
- openprocess/flow/boxes/output.py +52 -0
- openprocess/flow/boxes/predict.py +186 -0
- openprocess/flow/boxes/science.py +159 -0
- openprocess/flow/boxes/sweeps.py +18 -0
- openprocess/flow/convert.py +187 -0
- openprocess/flow/datasets.py +198 -0
- openprocess/flow/explain.py +115 -0
- openprocess/flow/library.py +222 -0
- openprocess/flow/record.py +385 -0
- openprocess/flow/runner.py +357 -0
- openprocess/flow/sweep.py +92 -0
- openprocess/flow/types.py +290 -0
- openprocess/flow/workflow.py +628 -0
- openprocess/gui/__init__.py +0 -0
- openprocess/gui/app.py +90 -0
- openprocess/gui/arc_editing.py +295 -0
- openprocess/gui/canvas.py +1414 -0
- openprocess/gui/flow/__init__.py +8 -0
- openprocess/gui/flow/canvas.py +854 -0
- openprocess/gui/flow/page.py +972 -0
- openprocess/gui/flow/templates.py +131 -0
- openprocess/gui/flow/viewers.py +665 -0
- openprocess/gui/items.py +1275 -0
- openprocess/gui/learn/answer_boxes.py +978 -0
- openprocess/gui/learn/concealment.py +91 -0
- openprocess/gui/learn/mode.py +1181 -0
- openprocess/gui/panning.py +241 -0
- openprocess/gui/resources/openprocess-icon.png +0 -0
- openprocess/gui/studio/__init__.py +1 -0
- openprocess/gui/studio/__main__.py +3 -0
- openprocess/gui/studio/app.py +4031 -0
- openprocess/gui/studio/charts.py +115 -0
- openprocess/gui/studio/compare_page.py +487 -0
- openprocess/gui/studio/cpn_page.py +1858 -0
- openprocess/gui/studio/definition_view.py +284 -0
- openprocess/gui/studio/derivation_view.py +421 -0
- openprocess/gui/studio/documents.py +152 -0
- openprocess/gui/studio/dotted_chart.py +1401 -0
- openprocess/gui/studio/file_dialogs.py +143 -0
- openprocess/gui/studio/filter_dialog.py +247 -0
- openprocess/gui/studio/graph_builders.py +176 -0
- openprocess/gui/studio/graph_view.py +682 -0
- openprocess/gui/studio/instances.py +413 -0
- openprocess/gui/studio/log_editor.py +675 -0
- openprocess/gui/studio/log_page.py +800 -0
- openprocess/gui/studio/markdown_view.py +127 -0
- openprocess/gui/studio/mathtext.py +260 -0
- openprocess/gui/studio/ml_highlighter.py +75 -0
- openprocess/gui/studio/model_page.py +760 -0
- openprocess/gui/studio/net_comparison.py +124 -0
- openprocess/gui/studio/notes_overlay.py +275 -0
- openprocess/gui/studio/petri_page.py +844 -0
- openprocess/gui/studio/regions_view.py +502 -0
- openprocess/gui/studio/sidebar.py +149 -0
- openprocess/gui/studio/style.py +503 -0
- openprocess/gui/studio/tool_icons.py +134 -0
- openprocess/gui/studio/updates.py +439 -0
- openprocess/gui/studio/widgets.py +899 -0
- openprocess/gui/studio/workers.py +60 -0
- openprocess/gui/studio/workspace.py +447 -0
- openprocess/gui/theme.py +394 -0
- openprocess/gui/tidy.py +86 -0
- openprocess/io/__init__.py +0 -0
- openprocess/io/cpn_reader.py +389 -0
- openprocess/io/cpn_writer.py +357 -0
- openprocess/learn/__init__.py +23 -0
- openprocess/learn/answers.py +188 -0
- openprocess/learn/checks.py +953 -0
- openprocess/learn/computed.py +1180 -0
- openprocess/learn/context.py +145 -0
- openprocess/learn/exam.py +169 -0
- openprocess/learn/exercise-packs.md +325 -0
- openprocess/learn/importer.py +216 -0
- openprocess/learn/notation.py +474 -0
- openprocess/learn/pack.py +511 -0
- openprocess/learn/sheet.py +296 -0
- openprocess/mining/__init__.py +73 -0
- openprocess/mining/analysis.py +689 -0
- openprocess/mining/columns.py +282 -0
- openprocess/mining/compare_nets.py +246 -0
- openprocess/mining/conformance/__init__.py +0 -0
- openprocess/mining/conformance/alignments.py +263 -0
- openprocess/mining/conformance/quality.py +145 -0
- openprocess/mining/conformance/token_replay.py +252 -0
- openprocess/mining/csv_import.py +222 -0
- openprocess/mining/definitions.py +584 -0
- openprocess/mining/dfg.py +187 -0
- openprocess/mining/discovery/__init__.py +0 -0
- openprocess/mining/discovery/alpha.py +168 -0
- openprocess/mining/discovery/heuristics.py +332 -0
- openprocess/mining/discovery/inductive.py +477 -0
- openprocess/mining/discovery/state_regions.py +62 -0
- openprocess/mining/filtering.py +237 -0
- openprocess/mining/footprint.py +183 -0
- openprocess/mining/invariants.py +191 -0
- openprocess/mining/layout.py +279 -0
- openprocess/mining/log.py +364 -0
- openprocess/mining/petrinet.py +354 -0
- openprocess/mining/playout.py +75 -0
- openprocess/mining/pm4py_bridge.py +82 -0
- openprocess/mining/pnml.py +223 -0
- openprocess/mining/processtree.py +216 -0
- openprocess/mining/regions.py +476 -0
- openprocess/mining/stats.py +160 -0
- openprocess/mining/structure.py +374 -0
- openprocess/mining/transition_system.py +409 -0
- openprocess/mining/xes.py +399 -0
- openprocess/ml/__init__.py +0 -0
- openprocess/ml/ast_nodes.py +332 -0
- openprocess/ml/builtins.py +364 -0
- openprocess/ml/colorsets.py +522 -0
- openprocess/ml/errors.py +60 -0
- openprocess/ml/evaluator.py +754 -0
- openprocess/ml/lexer.py +277 -0
- openprocess/ml/multiset.py +417 -0
- openprocess/ml/parser.py +737 -0
- openprocess/ml/values.py +319 -0
- openprocess/model/__init__.py +0 -0
- openprocess/model/declarations.py +617 -0
- openprocess/model/examples.py +98 -0
- openprocess/model/net.py +701 -0
- openprocess/model/plain.py +192 -0
- openprocess/references.py +280 -0
- openprocess/sim/__init__.py +0 -0
- openprocess/sim/binding.py +620 -0
- openprocess/sim/export.py +66 -0
- openprocess/sim/simulator.py +315 -0
- openprocess/teaching/__init__.py +4 -0
- openprocess/teaching/answers.py +4 -0
- openprocess/teaching/checks.py +5 -0
- openprocess/teaching/pack.py +4 -0
- openprocess/teaching/sheet.py +4 -0
- openprocess-0.7.0.dist-info/METADATA +927 -0
- openprocess-0.7.0.dist-info/RECORD +173 -0
- openprocess-0.7.0.dist-info/WHEEL +5 -0
- openprocess-0.7.0.dist-info/entry_points.txt +6 -0
- openprocess-0.7.0.dist-info/licenses/LICENSE +21 -0
- openprocess-0.7.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Plain (uncoloured) Petri nets in the editor.
|
|
2
|
+
|
|
3
|
+
The editor draws and simulates :class:`~openprocess.model.net.CPNet` models. A
|
|
4
|
+
plain Petri net -- the kind the course uses: black tokens, arc weights,
|
|
5
|
+
WF-nets -- is simply a CPN in which every place has the colour set ``UNIT``:
|
|
6
|
+
|
|
7
|
+
==================== ========================== ============================
|
|
8
|
+
plain net stored in the editor as shown on the canvas as
|
|
9
|
+
==================== ========================== ============================
|
|
10
|
+
3 tokens in *p* initial marking ``3`()`` three black dots
|
|
11
|
+
arc of weight 2 inscription ``2`()`` a small "2" on the arc
|
|
12
|
+
silent transition τ ``Transition.silent`` a black bar
|
|
13
|
+
==================== ========================== ============================
|
|
14
|
+
|
|
15
|
+
That way the plain editor reuses everything the CPN editor has (undo,
|
|
16
|
+
saving, the token game) with no second implementation. For analysis the
|
|
17
|
+
drawing is converted to a :class:`~openprocess.mining.petrinet.PetriNet`
|
|
18
|
+
(:func:`to_petri_net`) -- the model the soundness check, the reachability
|
|
19
|
+
graph, the footprint and conformance checking work on -- and back again
|
|
20
|
+
(:func:`from_petri_net`) to edit a discovered or imported net.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import re
|
|
26
|
+
|
|
27
|
+
from ..mining.petrinet import Marking, PetriNet
|
|
28
|
+
from .net import Arc, CPNet, Place, Transition
|
|
29
|
+
|
|
30
|
+
#: Node sizes used for plain nets (model units).
|
|
31
|
+
PLACE_SIZE = 40.0
|
|
32
|
+
TRANSITION_SIZE = (40.0, 40.0)
|
|
33
|
+
SILENT_SIZE = (14.0, 40.0)
|
|
34
|
+
|
|
35
|
+
_TERM = re.compile(r"^\s*(\d+)\s*`\s*\(\s*\)\s*$")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def token_count(text: str) -> int:
|
|
39
|
+
"""How many black tokens a UNIT marking or arc inscription stands for.
|
|
40
|
+
|
|
41
|
+
Accepts what people type or what files contain: ``""`` (none), ``"3"``,
|
|
42
|
+
``"3`()"``, ``"()"``, ``"1`()++2`()"``. Anything else counts as 1, the
|
|
43
|
+
safe default for an arc.
|
|
44
|
+
"""
|
|
45
|
+
text = (text or "").strip()
|
|
46
|
+
if not text:
|
|
47
|
+
return 0
|
|
48
|
+
if text.isdigit():
|
|
49
|
+
return int(text)
|
|
50
|
+
total = 0
|
|
51
|
+
for term in text.split("++"):
|
|
52
|
+
term = term.strip()
|
|
53
|
+
if term == "()":
|
|
54
|
+
total += 1
|
|
55
|
+
continue
|
|
56
|
+
match = _TERM.match(term)
|
|
57
|
+
if match is None:
|
|
58
|
+
return 1
|
|
59
|
+
total += int(match.group(1))
|
|
60
|
+
return total
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def tokens_text(count: int) -> str:
|
|
64
|
+
"""The CPN ML text for ``count`` black tokens (``""`` for none)."""
|
|
65
|
+
return f"{count}`()" if count > 0 else ""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def weight_of(arc: Arc) -> int:
|
|
69
|
+
"""An arc's weight (an empty inscription means 1)."""
|
|
70
|
+
return max(1, token_count(arc.expression_text) or 1)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def is_plain(net: CPNet) -> bool:
|
|
74
|
+
"""Is every place a UNIT place (so the net can be analysed as a plain net)?"""
|
|
75
|
+
return all(place.colour_set_name == "UNIT" for place in net.all_places())
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def new_plain_net(name: str) -> CPNet:
|
|
79
|
+
"""An empty plain Petri net, ready to draw on."""
|
|
80
|
+
net = CPNet(name)
|
|
81
|
+
net.add_declaration("colset UNIT = unit;")
|
|
82
|
+
net.add_page("Net")
|
|
83
|
+
net.plain = True
|
|
84
|
+
return net
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def to_petri_net(net: CPNet) -> PetriNet:
|
|
88
|
+
"""The drawing as a :class:`PetriNet` (ids, names and positions kept).
|
|
89
|
+
|
|
90
|
+
Positions become PNML-style coordinates (y pointing down). A
|
|
91
|
+
double-headed arc becomes one arc each way. If the net has exactly one
|
|
92
|
+
place without outgoing arcs (a WF-net's sink), the final marking is one
|
|
93
|
+
token there.
|
|
94
|
+
"""
|
|
95
|
+
petri = PetriNet(net.name)
|
|
96
|
+
initial: dict[str, int] = {}
|
|
97
|
+
for place in net.all_places():
|
|
98
|
+
node = petri.add_place(place.name or place.id, id=place.id)
|
|
99
|
+
node.position = (place.graphics.x, -place.graphics.y)
|
|
100
|
+
node.name_offset = _pnml_offset(place.graphics.label_offsets.get("name"))
|
|
101
|
+
count = token_count(place.initial_marking_text)
|
|
102
|
+
if count:
|
|
103
|
+
initial[place.id] = count
|
|
104
|
+
for transition in net.all_transitions():
|
|
105
|
+
silent = getattr(transition, "silent", False)
|
|
106
|
+
node = petri.add_transition(None if silent else (transition.name or transition.id),
|
|
107
|
+
name=transition.name or ("τ" if silent else transition.id),
|
|
108
|
+
id=transition.id)
|
|
109
|
+
node.position = (transition.graphics.x, -transition.graphics.y)
|
|
110
|
+
node.name_offset = _pnml_offset(transition.graphics.label_offsets.get("name"))
|
|
111
|
+
if getattr(net, "names_outside", False):
|
|
112
|
+
petri.info["names_outside"] = "true"
|
|
113
|
+
for arc in net.all_arcs():
|
|
114
|
+
weight = weight_of(arc)
|
|
115
|
+
bends = [(x, -y) for x, y in arc.bendpoints] # transition -> place
|
|
116
|
+
if arc.is_input:
|
|
117
|
+
petri.add_arc(arc.place_id, arc.transition_id, weight).points = bends[::-1]
|
|
118
|
+
if arc.is_output:
|
|
119
|
+
petri.add_arc(arc.transition_id, arc.place_id, weight).points = list(bends)
|
|
120
|
+
petri.initial_marking = Marking(initial)
|
|
121
|
+
sinks = petri.sink_places()
|
|
122
|
+
if len(sinks) == 1:
|
|
123
|
+
petri.final_marking = Marking({sinks[0]: 1})
|
|
124
|
+
return petri
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def from_petri_net(petri: PetriNet) -> CPNet:
|
|
128
|
+
"""A plain net the editor can draw, from a mined, imported or typed
|
|
129
|
+
:class:`PetriNet`. Nodes without a stored position are laid out left to
|
|
130
|
+
right, as in the course's figures."""
|
|
131
|
+
net = new_plain_net(petri.name)
|
|
132
|
+
page = net.pages[0]
|
|
133
|
+
positions = _positions(petri)
|
|
134
|
+
# Names outside when the file says so -- or when places have long names
|
|
135
|
+
# (a mined net's "p({a},{b,e})" would make enormous circles).
|
|
136
|
+
net.names_outside = petri.info.get("names_outside") == "true" or any(
|
|
137
|
+
len(place.name) > 5 for place in petri.places.values())
|
|
138
|
+
for place in petri.places.values():
|
|
139
|
+
x, y = positions[place.id]
|
|
140
|
+
node = Place(id=place.id, name=place.name, colour_set_name="UNIT",
|
|
141
|
+
initial_marking_text=tokens_text(petri.initial_marking[place.id]))
|
|
142
|
+
node.graphics.x, node.graphics.y = x, -y
|
|
143
|
+
node.graphics.width = node.graphics.height = PLACE_SIZE
|
|
144
|
+
if place.name_offset is not None:
|
|
145
|
+
node.graphics.label_offsets["name"] = _model_offset(place.name_offset)
|
|
146
|
+
page.places.append(node)
|
|
147
|
+
for transition in petri.transitions.values():
|
|
148
|
+
x, y = positions[transition.id]
|
|
149
|
+
node = Transition(id=transition.id,
|
|
150
|
+
name="" if transition.silent and transition.name in ("τ", "tau")
|
|
151
|
+
else transition.name)
|
|
152
|
+
node.silent = transition.silent
|
|
153
|
+
node.graphics.x, node.graphics.y = x, -y
|
|
154
|
+
node.graphics.width, node.graphics.height = SILENT_SIZE if node.silent else \
|
|
155
|
+
TRANSITION_SIZE
|
|
156
|
+
if transition.name_offset is not None:
|
|
157
|
+
node.graphics.label_offsets["name"] = _model_offset(transition.name_offset)
|
|
158
|
+
page.transitions.append(node)
|
|
159
|
+
for arc in petri.arcs:
|
|
160
|
+
if arc.source in petri.places:
|
|
161
|
+
place_id, transition_id, orientation = arc.source, arc.target, "PtoT"
|
|
162
|
+
else:
|
|
163
|
+
place_id, transition_id, orientation = arc.target, arc.source, "TtoP"
|
|
164
|
+
points = [(x, -y) for x, y in getattr(arc, "points", [])] # source -> target
|
|
165
|
+
page.arcs.append(Arc(place_id=place_id, transition_id=transition_id,
|
|
166
|
+
orientation=orientation,
|
|
167
|
+
expression_text="" if arc.weight == 1 else tokens_text(arc.weight),
|
|
168
|
+
bendpoints=points if orientation == "TtoP" else points[::-1]))
|
|
169
|
+
return net
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _pnml_offset(offset) -> tuple[float, float] | None:
|
|
173
|
+
"""Editor label offset (model units, y up) -> PNML offset (y down)."""
|
|
174
|
+
return None if offset is None else (offset[0], -offset[1])
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _model_offset(offset: tuple[float, float]) -> tuple[float, float]:
|
|
178
|
+
return offset[0], -offset[1]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _positions(petri: PetriNet) -> dict[str, tuple[float, float]]:
|
|
182
|
+
"""Stored positions where every node has one; otherwise a fresh layered
|
|
183
|
+
layout, flowing left to right."""
|
|
184
|
+
nodes = list(petri.places.values()) + list(petri.transitions.values())
|
|
185
|
+
if nodes and all(node.position is not None for node in nodes):
|
|
186
|
+
return {node.id: node.position for node in nodes}
|
|
187
|
+
from ..mining.layout import layered_layout
|
|
188
|
+
sizes = {node.id: (PLACE_SIZE, PLACE_SIZE) for node in petri.places.values()}
|
|
189
|
+
sizes.update({node.id: TRANSITION_SIZE for node in petri.transitions.values()})
|
|
190
|
+
edges = [(arc.source, arc.target) for arc in petri.arcs]
|
|
191
|
+
layout = layered_layout(sizes, edges, layer_gap=60.0, node_gap=50.0)
|
|
192
|
+
return {node: (x, y) for node, (x, y) in layout.positions.items()}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""Where OpenProcess's notation and algorithms come from.
|
|
2
|
+
|
|
3
|
+
Every algorithm and notation convention OpenProcess implements follows a published
|
|
4
|
+
source. This module lists them once:
|
|
5
|
+
|
|
6
|
+
* :data:`REFERENCES` -- the works, as full citations;
|
|
7
|
+
* :data:`TOPICS` -- what OpenProcess does, grouped as in the app, with the works it
|
|
8
|
+
follows and the module that implements it.
|
|
9
|
+
|
|
10
|
+
Every detail given (pages, DOIs, links) was checked against the publisher, a
|
|
11
|
+
bibliography database or the paper itself; anything that could not be
|
|
12
|
+
confirmed is left out rather than guessed.
|
|
13
|
+
|
|
14
|
+
The app shows this as *Help ▸ References*, and ``docs/references.md`` is
|
|
15
|
+
generated from it with ``python -m openprocess.references > docs/references.md``
|
|
16
|
+
(a test checks the two agree). Module docstrings cite the same works at the
|
|
17
|
+
point of use, often down to the definition or section.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class Reference:
|
|
27
|
+
key: str
|
|
28
|
+
#: The citation, in Markdown (titles in italics).
|
|
29
|
+
citation: str
|
|
30
|
+
#: A DOI (without https://doi.org/) or a URL, when there is one.
|
|
31
|
+
link: str = ""
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def url(self) -> str:
|
|
35
|
+
if not self.link:
|
|
36
|
+
return ""
|
|
37
|
+
return self.link if self.link.startswith("http") else f"https://doi.org/{self.link}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
REFERENCES: tuple[Reference, ...] = (
|
|
41
|
+
# -- process mining ---------------------------------------------------------------------
|
|
42
|
+
Reference("aalst2016", "W.M.P. van der Aalst. *Process Mining: Data Science in Action*, "
|
|
43
|
+
"2nd edition. Springer, 2016.", "10.1007/978-3-662-49851-4"),
|
|
44
|
+
Reference("aalst2004", "W.M.P. van der Aalst, A.J.M.M. Weijters and L. Maruster. "
|
|
45
|
+
"*Workflow Mining: Discovering Process Models from Event Logs*. IEEE "
|
|
46
|
+
"Transactions on Knowledge and Data Engineering 16(9):1128–1142, 2004.",
|
|
47
|
+
"10.1109/TKDE.2004.47"),
|
|
48
|
+
Reference("aalst2013", "W.M.P. van der Aalst and B.F. van Dongen. *Discovering Petri "
|
|
49
|
+
"Nets from Event Logs*. Transactions on Petri Nets and Other Models of "
|
|
50
|
+
"Concurrency VII, LNCS 7480. Springer, 2013."),
|
|
51
|
+
Reference("leemans2013", "S.J.J. Leemans, D. Fahland and W.M.P. van der Aalst. "
|
|
52
|
+
"*Discovering Block-Structured Process Models from Event Logs – A Constructive "
|
|
53
|
+
"Approach*. Application and Theory of Petri Nets and Concurrency (PETRI NETS "
|
|
54
|
+
"2013), LNCS 7927, pp. 311–329. Springer, 2013.", "10.1007/978-3-642-38697-8_17"),
|
|
55
|
+
Reference("leemans2014", "S.J.J. Leemans, D. Fahland and W.M.P. van der Aalst. "
|
|
56
|
+
"*Discovering Block-Structured Process Models from Event Logs Containing "
|
|
57
|
+
"Infrequent Behaviour*. Business Process Management Workshops (BPM 2013), "
|
|
58
|
+
"LNBIP 171, pp. 66–78. Springer, 2014.", "10.1007/978-3-319-06257-0_6"),
|
|
59
|
+
Reference("weijters2003", "A.J.M.M. Weijters and W.M.P. van der Aalst. *Rediscovering "
|
|
60
|
+
"Workflow Models from Event-Based Data using Little Thumb*. Integrated "
|
|
61
|
+
"Computer-Aided Engineering 10(2):151–162, 2003."),
|
|
62
|
+
Reference("weijters2006", "A.J.M.M. Weijters, W.M.P. van der Aalst and A.K. Alves de "
|
|
63
|
+
"Medeiros. *Process Mining with the HeuristicsMiner Algorithm*. BETA Working "
|
|
64
|
+
"Paper Series, WP 166, Eindhoven University of Technology, 2006."),
|
|
65
|
+
Reference("aalst2010", "W.M.P. van der Aalst, V. Rubin, H.M.W. Verbeek, B.F. van Dongen, "
|
|
66
|
+
"E. Kindler and C.W. Günther. *Process Mining: A Two-Step Approach to Balance "
|
|
67
|
+
"Between Underfitting and Overfitting*. Software and Systems Modeling 9(1):87–111, "
|
|
68
|
+
"2010.", "10.1007/s10270-008-0106-z"),
|
|
69
|
+
Reference("leemans2019", "S.J.J. Leemans, E. Poppe and M.T. Wynn. *Directly "
|
|
70
|
+
"Follows-Based Process Mining: Exploration & a Case Study*. International "
|
|
71
|
+
"Conference on Process Mining (ICPM 2019), 2019."),
|
|
72
|
+
Reference("song2007", "M. Song and W.M.P. van der Aalst. *Supporting Process Mining by "
|
|
73
|
+
"Showing Events at a Glance*. 17th Annual Workshop on Information Technologies "
|
|
74
|
+
"and Systems (WITS 2007), pp. 139–145, 2007."),
|
|
75
|
+
# -- conformance --------------------------------------------------------------------------
|
|
76
|
+
Reference("carmona2018", "J. Carmona, B. van Dongen, A. Solti and M. Weidlich. "
|
|
77
|
+
"*Conformance Checking: Relating Processes and Models*. Springer, 2018.",
|
|
78
|
+
"10.1007/978-3-319-99414-7"),
|
|
79
|
+
Reference("rozinat2008", "A. Rozinat and W.M.P. van der Aalst. *Conformance Checking of "
|
|
80
|
+
"Processes Based on Monitoring Real Behavior*. Information Systems 33(1):64–95, "
|
|
81
|
+
"2008.", "10.1016/j.is.2007.07.001"),
|
|
82
|
+
Reference("adriansyah2014", "A. Adriansyah. *Aligning Observed and Modeled Behavior*. "
|
|
83
|
+
"PhD thesis, Eindhoven University of Technology, 2014."),
|
|
84
|
+
Reference("munoz2010", "J. Muñoz-Gama and J. Carmona. *A Fresh Look at Precision in "
|
|
85
|
+
"Process Conformance*. Business Process Management (BPM 2010), LNCS 6336, "
|
|
86
|
+
"pp. 211–226. Springer, 2010.", "10.1007/978-3-642-15618-2_16"),
|
|
87
|
+
Reference("berti2019", "A. Berti, S.J. van Zelst and W.M.P. van der Aalst. *Process "
|
|
88
|
+
"Mining for Python (PM4Py): Bridging the Gap Between Process- and Data "
|
|
89
|
+
"Science*. ICPM Demo Track 2019, CEUR-WS 2374, pp. 13–16, 2019.",
|
|
90
|
+
"https://ceur-ws.org/Vol-2374/paper4.pdf"),
|
|
91
|
+
# -- Petri nets ------------------------------------------------------------------------------
|
|
92
|
+
Reference("aalst2000", "W.M.P. van der Aalst. *Workflow Verification: Finding "
|
|
93
|
+
"Control-Flow Errors Using Petri-Net-Based Techniques*. Business Process "
|
|
94
|
+
"Management: Models, Techniques, and Empirical Studies, LNCS 1806, "
|
|
95
|
+
"pp. 161–183. Springer, 2000.", "10.1007/3-540-45594-9_11"),
|
|
96
|
+
Reference("murata1989", "T. Murata. *Petri Nets: Properties, Analysis and Applications*. "
|
|
97
|
+
"Proceedings of the IEEE 77(4):541–580, 1989."),
|
|
98
|
+
Reference("karp1969", "R.M. Karp and R.E. Miller. *Parallel Program Schemata*. Journal of "
|
|
99
|
+
"Computer and System Sciences 3(2):147–195, 1969."),
|
|
100
|
+
Reference("desel1995", "J. Desel and J. Esparza. *Free Choice Petri Nets*. Cambridge "
|
|
101
|
+
"Tracts in Theoretical Computer Science 40. Cambridge University Press, 1995."),
|
|
102
|
+
Reference("cortadella1998", "J. Cortadella, M. Kishinevsky, L. Lavagno and A. Yakovlev. "
|
|
103
|
+
"*Deriving Petri Nets from Finite Transition Systems*. IEEE Transactions on "
|
|
104
|
+
"Computers 47(8):859–882, 1998."),
|
|
105
|
+
Reference("ehrenfeucht1990", "A. Ehrenfeucht and G. Rozenberg. *Partial (Set) "
|
|
106
|
+
"2-Structures. Part I: Basic Notions and the Representation Problem*, Acta "
|
|
107
|
+
"Informatica 27(4):315–342, 1990; *Part II: State Spaces of Concurrent "
|
|
108
|
+
"Systems*, Acta Informatica 27(4):343–368, 1990."),
|
|
109
|
+
Reference("jensen2009", "K. Jensen and L.M. Kristensen. *Coloured Petri Nets: Modelling "
|
|
110
|
+
"and Validation of Concurrent Systems*. Springer, 2009.", "10.1007/b95112"),
|
|
111
|
+
# -- formats and drawing -------------------------------------------------------------------
|
|
112
|
+
Reference("xes", "IEEE Standard for eXtensible Event Stream (XES) for Achieving "
|
|
113
|
+
"Interoperability in Event Logs and Event Streams. IEEE Std 1849-2016 "
|
|
114
|
+
"(revised as IEEE Std 1849-2023).", "https://xes-standard.org"),
|
|
115
|
+
Reference("pnml", "ISO/IEC 15909-2:2011. *Systems and software engineering — High-level "
|
|
116
|
+
"Petri nets — Part 2: Transfer format*."),
|
|
117
|
+
Reference("cpntools", "CPN Tools and its model file format (`.cpn`)."),
|
|
118
|
+
Reference("sugiyama1981", "K. Sugiyama, S. Tagawa and M. Toda. *Methods for Visual "
|
|
119
|
+
"Understanding of Hierarchical System Structures*. IEEE Transactions on "
|
|
120
|
+
"Systems, Man, and Cybernetics 11(2):109–125, 1981.",
|
|
121
|
+
"10.1109/TSMC.1981.4308636"),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
BY_KEY = {reference.key: reference for reference in REFERENCES}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass(frozen=True)
|
|
128
|
+
class Topic:
|
|
129
|
+
name: str
|
|
130
|
+
#: What OpenProcess does, and which convention it follows (Markdown).
|
|
131
|
+
note: str
|
|
132
|
+
#: Keys of :data:`REFERENCES`.
|
|
133
|
+
sources: tuple[str, ...]
|
|
134
|
+
#: Where it is implemented.
|
|
135
|
+
module: str = ""
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
#: (section, topics) in the order the app and the docs show them.
|
|
139
|
+
TOPICS: tuple[tuple[str, tuple[Topic, ...]], ...] = (
|
|
140
|
+
("Notation", (
|
|
141
|
+
Topic("Event logs as multisets of traces",
|
|
142
|
+
"A log written as $L = [\\langle a,b,c \\rangle^3, \\langle a,c \\rangle]$: a "
|
|
143
|
+
"multiset of traces, the exponent the number of cases. *Log from notation…* "
|
|
144
|
+
"and `log.txt` files use it.", ("aalst2016",), "openprocess.mining.log"),
|
|
145
|
+
Topic("Events, traces, classifiers and the XES attributes",
|
|
146
|
+
"`concept:name`, `time:timestamp`, `org:resource` and `lifecycle:transition`, "
|
|
147
|
+
"and classifiers that turn events into activity labels.", ("xes", "aalst2016"),
|
|
148
|
+
"openprocess.mining.log, openprocess.mining.xes"),
|
|
149
|
+
Topic("Petri nets, markings and WF-nets",
|
|
150
|
+
"$N = (P, T, F)$, markings as multisets of places, a WF-net's source place $i$ "
|
|
151
|
+
"and sink place $o$, and $[i]$ / $[o]$ for its initial and final marking.",
|
|
152
|
+
("aalst2000", "murata1989", "aalst2016"), "openprocess.mining.petrinet"),
|
|
153
|
+
Topic("Footprint relations",
|
|
154
|
+
"$a \\rightarrow_L b$, $a \\leftarrow_L b$, $a \\parallel_L b$ and $a \\#_L b$, "
|
|
155
|
+
"derived from directly-follows $a >_L b$.", ("aalst2004", "aalst2016"),
|
|
156
|
+
"openprocess.mining.footprint"),
|
|
157
|
+
Topic("Process trees",
|
|
158
|
+
"The operators $\\rightarrow$ (sequence), $\\times$ (exclusive choice), "
|
|
159
|
+
"$\\wedge$ (parallel) and $\\circlearrowleft$ (loop), and $\\tau$ for a silent "
|
|
160
|
+
"step.", ("leemans2013", "aalst2016"), "openprocess.mining.processtree"),
|
|
161
|
+
Topic("Transition systems and regions",
|
|
162
|
+
"$TS = (S, E, T, s_{in})$; a region entered, exited or not crossed by each "
|
|
163
|
+
"event; generalised excitation regions $GER(e)$ and pre-/post-regions.",
|
|
164
|
+
("cortadella1998", "aalst2010", "aalst2016"), "openprocess.mining.transition_system, "
|
|
165
|
+
"openprocess.mining.regions"),
|
|
166
|
+
Topic("Alignments",
|
|
167
|
+
"Moves $(a, a)$, $(a, \\gg)$ and $(\\gg, a)$, with the standard cost "
|
|
168
|
+
"function.", ("carmona2018", "adriansyah2014"),
|
|
169
|
+
"openprocess.mining.conformance.alignments"),
|
|
170
|
+
Topic("Coloured Petri nets and CPN ML",
|
|
171
|
+
"Colour sets, variables, arc inscriptions, guards and timed tokens as in CPN "
|
|
172
|
+
"Tools.", ("jensen2009", "cpntools"), "openprocess.ml, openprocess.model, openprocess.sim"),
|
|
173
|
+
)),
|
|
174
|
+
("Discovery", (
|
|
175
|
+
Topic("α-algorithm", "The eight steps from $T_L$ to $\\alpha(L)$, with $X_L$ and "
|
|
176
|
+
"$Y_L$.", ("aalst2004", "aalst2013", "aalst2016"), "openprocess.mining.discovery.alpha"),
|
|
177
|
+
Topic("Inductive Miner (IM) and IM – infrequent (IMf)",
|
|
178
|
+
"Cuts, base cases and fall-throughs on the directly-follows graph; IMf filters "
|
|
179
|
+
"infrequent behaviour.", ("leemans2013", "leemans2014"),
|
|
180
|
+
"openprocess.mining.discovery.inductive"),
|
|
181
|
+
Topic("Heuristics Miner",
|
|
182
|
+
"Dependency measures $a \\Rightarrow b$, length-one and length-two loops, and "
|
|
183
|
+
"the thresholds.", ("weijters2003", "weijters2006", "aalst2016"),
|
|
184
|
+
"openprocess.mining.discovery.heuristics"),
|
|
185
|
+
Topic("State-based regions (two-phase discovery)",
|
|
186
|
+
"A transition system from the log (prefix, postfix; sequence, multiset, set; a "
|
|
187
|
+
"horizon), its minimal regions, and the synthesised net.",
|
|
188
|
+
("aalst2010", "aalst2013", "cortadella1998", "ehrenfeucht1990", "aalst2016"),
|
|
189
|
+
"openprocess.mining.discovery.state_regions"),
|
|
190
|
+
Topic("Directly-follows graphs (process maps)",
|
|
191
|
+
"Frequencies and times on the edges, and simplification.", ("leemans2019",
|
|
192
|
+
"aalst2016"),
|
|
193
|
+
"openprocess.mining.dfg"),
|
|
194
|
+
Topic("ILP Miner and heuristics nets (optional)",
|
|
195
|
+
"Provided by PM4Py when it is installed.", ("berti2019",),
|
|
196
|
+
"openprocess.mining.pm4py_bridge"),
|
|
197
|
+
)),
|
|
198
|
+
("Conformance and quality", (
|
|
199
|
+
Topic("Token-based replay", "Produced, consumed, missing and remaining tokens, and "
|
|
200
|
+
"fitness.", ("rozinat2008", "aalst2016"), "openprocess.mining.conformance.token_replay"),
|
|
201
|
+
Topic("Alignments", "Optimal alignments by search over the synchronous product.",
|
|
202
|
+
("carmona2018", "adriansyah2014"), "openprocess.mining.conformance.alignments"),
|
|
203
|
+
Topic("Precision (escaping edges)", "ETConformance precision.", ("munoz2010",
|
|
204
|
+
"carmona2018"),
|
|
205
|
+
"openprocess.mining.conformance.quality"),
|
|
206
|
+
Topic("Generalisation and simplicity", "Token-based generalisation and simplicity, "
|
|
207
|
+
"computed as in PM4Py.", ("berti2019", "aalst2016"),
|
|
208
|
+
"openprocess.mining.conformance.quality"),
|
|
209
|
+
)),
|
|
210
|
+
("Petri net analysis", (
|
|
211
|
+
Topic("Soundness of WF-nets", "Option to complete, proper completion, no dead "
|
|
212
|
+
"transitions, and the short-circuited net $\\overline{N}$.",
|
|
213
|
+
("aalst2000",), "openprocess.mining.analysis"),
|
|
214
|
+
Topic("Free-choice, well-structured, S-coverable",
|
|
215
|
+
"Structural properties and the handles behind a problem.",
|
|
216
|
+
("aalst2000", "desel1995"), "openprocess.mining.structure"),
|
|
217
|
+
Topic("Boundedness, liveness, deadlocks, reversibility",
|
|
218
|
+
"Decided on the reachability graph, or the coverability graph for unbounded "
|
|
219
|
+
"nets.", ("murata1989", "karp1969"), "openprocess.mining.analysis"),
|
|
220
|
+
Topic("Incidence matrix and invariants", "P- and T-invariants.", ("murata1989",),
|
|
221
|
+
"openprocess.mining.invariants"),
|
|
222
|
+
Topic("CPN simulation and state spaces", "Bindings, enabling and the state space "
|
|
223
|
+
"report.", ("jensen2009",), "openprocess.sim, openprocess.analysis"),
|
|
224
|
+
)),
|
|
225
|
+
("Files and pictures", (
|
|
226
|
+
Topic("XES event logs", "Reading and writing `.xes` and `.xes.gz`.", ("xes",),
|
|
227
|
+
"openprocess.mining.xes"),
|
|
228
|
+
Topic("PNML Petri nets", "Reading and writing `.pnml`.", ("pnml",), "openprocess.mining.pnml"),
|
|
229
|
+
Topic("CPN Tools models", "Reading and writing `.cpn`.", ("cpntools", "jensen2009"),
|
|
230
|
+
"openprocess.io"),
|
|
231
|
+
Topic("Dotted chart", "Events as dots over time, per case or per resource.",
|
|
232
|
+
("song2007",), "openprocess.gui.studio.dotted_chart"),
|
|
233
|
+
Topic("Layered graph layout", "How discovered models and graphs are drawn.",
|
|
234
|
+
("sugiyama1981",), "openprocess.mining.layout"),
|
|
235
|
+
)),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def markdown() -> str:
|
|
240
|
+
"""The page as Markdown (``docs/references.md``)."""
|
|
241
|
+
lines = ["<!-- Generated by `python -m openprocess.references > docs/references.md`. "
|
|
242
|
+
"Do not edit by hand. -->", "", "# References", "",
|
|
243
|
+
"Where OpenProcess's notation and algorithms come from. Each topic names the works "
|
|
244
|
+
"it follows and the module that implements it; the module's docstring cites "
|
|
245
|
+
"the work again at the point of use. The definitions of every property the "
|
|
246
|
+
"analyses report are in [definitions.md](definitions.md).", ""]
|
|
247
|
+
numbers = {reference.key: index for index, reference in enumerate(REFERENCES, 1)}
|
|
248
|
+
for section, topics in TOPICS:
|
|
249
|
+
lines += [f"## {section}", ""]
|
|
250
|
+
for topic in topics:
|
|
251
|
+
cited = ", ".join(f"[{numbers[key]}]" for key in topic.sources)
|
|
252
|
+
where = f" *(`{topic.module}`)*" if topic.module else ""
|
|
253
|
+
lines.append(f"- **{topic.name}** — {topic.note} {cited}{where}")
|
|
254
|
+
lines.append("")
|
|
255
|
+
lines += ["## Works cited", ""]
|
|
256
|
+
for index, reference in enumerate(REFERENCES, 1):
|
|
257
|
+
link = f" <{reference.url}>" if reference.url else ""
|
|
258
|
+
lines.append(f"{index}. {reference.citation}{link}")
|
|
259
|
+
return "\n".join(lines) + "\n"
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
if __name__ == "__main__":
|
|
263
|
+
print(markdown(), end="")
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def topics_for_module(module: str) -> list[Topic]:
|
|
267
|
+
"""The topics implemented in ``module`` (``openprocess.mining.discovery.alpha``),
|
|
268
|
+
for showing an algorithm's sources next to its code."""
|
|
269
|
+
found = []
|
|
270
|
+
for _section, topics in TOPICS:
|
|
271
|
+
for topic in topics:
|
|
272
|
+
names = [m.strip() for m in topic.module.split(",") if m.strip()]
|
|
273
|
+
if any(module == m or module.startswith(m + ".") for m in names):
|
|
274
|
+
found.append(topic)
|
|
275
|
+
return found
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def reference(key: str) -> Reference | None:
|
|
279
|
+
"""The work with ``key``, or None."""
|
|
280
|
+
return next((r for r in REFERENCES if r.key == key), None)
|
|
File without changes
|