10.5. EpiM

10.5.1. A Basic Process Encoding

These examples shows the basic usage of EpiM. After each run a PDF summary is compiled. We let in(x) and out(x) denote the input and output operators of a process respectively.

Explore in the playground.

 1from mod.epim.process import Process, names
 2
 3# MØD print options
 4printer = GraphPrinter()
 5
 6# Specify the names to be used:
 7x, y, z, w = names("x y z w")
 8
 9# Specify the behavior of a process
10p1 = Process().input(x, z).output(z, w)
11p2 = Process().output(x, y)
12P = p1 | p2
13
14# Encode the process as a MØD graph:
15G = P.encode()
16
17# Put a visualisation of the encoding in the summary:
18G.print(printer)

10.5.2. A Process Encoding with Restrictions

We can restrict names of a process.

Explore in the playground.

 1from mod.epim.process import Process, names
 2
 3# MØD print options
 4printer = GraphPrinter()
 5
 6# Specify the names to be used:
 7x, y, z, w = names("x y z w")
 8
 9# Specify the behavior of a process
10p1 = Process().input(x, z).output(z, w)
11p2 = Process().restrict(x).output(x, y)
12P = p1 | p2
13
14# Encode the process as a MØD graph:
15G = P.encode()
16
17# Put a visualisation of the encoding in the summary:
18G.print(printer)

10.5.3. A Recursive Encoding

We can also specify process calls in an encoding.

Explore in the playground.

 1from mod.epim.process import Process, names
 2
 3# MØD print options
 4printer = GraphPrinter()
 5
 6# Specify the names to be used:
 7x, y = names("x y")
 8
 9# Specify the behavior of a process
10# Note, the second argument to call() is 
11# the arguments passed to the recursive invocation of "A".
12A = Process().input(x, y).call("A", [x])
13
14# Encode A and put a visualisation of the encoding in the summary:
15A.encode().print(printer)

10.5.4. Config for Visualization of Encodings

The graphs of the encoded processes can be filtered and “prettified” using either the print options used by MØD for graph visualization or the image config provided by EpiM.

Explore in the playground.

 1from mod.epim.gen_image import image_config
 2from mod.epim.process import Process, names
 3
 4# Show the names of a process encoding: 
 5image_config.show_variables = True
 6
 7# Show the pointer vertices used for encoding process calls:
 8image_config.show_pointers = True
 9
10# Show the special root vertex "go" of an encoding:
11image_config.show_root = True
12
13# Hide implementation detail specific vertices such as 
14# the vertices with terms t(p) or t(s) with degrees of 2:
15image_config.collapse_two_edges = False
16
17# MØD print options
18printer = GraphPrinter()
19
20# Specify the names to be used:
21x, y, z, w = names("x y z w")
22
23# Specify the behavior of a process:
24p1 = Process().input(x, z).output(z, w)
25p2 = Process().output(x, y)
26P = p1 | p2
27
28# Encode and print P
29G = P.encode()
30G.print(printer)

10.5.5. Compute Execution Spaces

We can compute the execution space of a process.

Explore in the playground.

 1from mod.epim.transform.reduction_dg import ReductionDG
 2from mod.epim.process import Process, names
 3
 4# Specify the names to be used:
 5x, y, z, w = names("x y z w")
 6
 7# Specify the behavior of a process:
 8p1 = Process().input(x, z).output(z, w)
 9p2 = Process().output(x, y) + Process().output(x, y)
10P = (p1 | p2)
11
12# Compute the execution space for P
13exec_space = ReductionDG(P)
14exec_space.calc()
15
16# Print the resulting derivation graph to the summary PDF:
17exec_space.print()
18# exec_space.print(True) #prints the entire derivation graph

10.5.6. Process Execution Spaces with Recursive Processes

We can compute the execution space of processes defined with recursive processes.

Explore in the playground.

 1from mod.epim.transform.reduction_dg import ReductionDG
 2from mod.epim.process import Process, names
 3from mod.epim.recursive_process import RecursiveProcess
 4
 5# Specify the names to be used:
 6x, y, z = names("x y z")
 7
 8# Define a recursive process:
 9rp = RecursiveProcess()
10
11# Specify the name, arguments, and behavior of a recursive process:
12A = Process().input(x, y).call("A", [y])
13rp.add("A", [x], A)
14
15B = Process().output(x, x).call("B", [x])
16rp.add("B", [x], B)
17
18# Specify the behavior of a process:
19P = A | B
20
21# Compute the execution space for P provided with the recursive process rp:
22exec_space = ReductionDG(P, rp)
23exec_space.calc()
24
25# Print the resulting derivation graph to the summary PDF:
26exec_space.print()

10.5.7. Hospital

The execution space for the process Hospital. From the execution space, we see that Hospital can end up in a deadlock. This happens specifically, when the process P synchronizes with the process H.

Explore in the playground.

 1from mod.epim.process import Process, names, Name, process_config
 2from mod.epim.transform.reduction_dg import ReductionDG
 3from mod.epim.recursive_process import RecursiveProcess
 4
 5# Do not print the arguments of an encoded process call:
 6process_config.print_args = False
 7
 8# Specify the names to be used:
 9s, n, pn, j, h, cu, ki, d, x = names("s n pn j h cu ki d x")
10free_names = [s, n, ki, cu]
11
12# Define a recursive process:
13rp = RecursiveProcess()
14
15# Specify the name, arguments, and behavior of a recursive process:
16P = Process().output(s, n).input(n, d).call("Pp", free_names)
17rp.add("P", free_names, P)
18
19Pp = Process().input(ki, x) + Process().input(cu, x).call("P", free_names)
20rp.add("Pp", free_names, Pp)
21
22J = Process().input(s, pn).output(pn, j).output(cu, j).call("J", free_names)
23rp.add("J", free_names, J)
24
25H = Process().input(s, pn).output(pn, h).output(ki, h).call("H", free_names)
26rp.add("H", free_names, H)
27
28# Define the process Hospital:
29Hospital = P | J | H
30
31# Compute the execution space of Hospital given the recursive process rp:
32exec_space = ReductionDG(Hospital, rp)
33exec_space.calc()
34
35# Print the resulting derivation graph to the summary PDF:
36exec_space.print()