Edition, visualisation, traversal

Boolean automata

In [1]:
# We disable autosave for technical reasons.
# Replace 0 by 120 in next line to restore default.
%autosave 0
Autosave disabled
In [2]:
import awalipy # If import fails, check that 
               # Python version used as Jupyter
               # kernel matches the one
               # Awalipy was compiled with.
[Warning] The python module awalipy relies on compilation executed "on-the-fly" depending on the context (type of weights, of labels, etc.). As a result, the very first call to a given function in a given context may take up to 10 seconds. 

(The above warning is displayed every time awalipy is imported.)

Creating an automaton

Alphabets we use are Python str

  • each char of the str is a letter the alphabet
  • order does not matter ("abc" and "bac" represent the same alphabet)
In [3]:
alph1 = "abc"  # represents {a,b,c}
alph2 = "01"   # represents {0,1}

An automaton is built from an alphabet

In [4]:
A = awalipy.Automaton(alph1)
# or directly  `awalipy.Automaton("abc")`

A is an automaton with no states and no transitions over the alphabet {a,b,c}


Adding states

By default the displayed name of the state is given by its index: the first state is named "s0", the second "s1", etc.

In [5]:
stt_0 = A.add_state()
stt_1 = A.add_state()

Adding transitions

In [6]:
tr_0 = A.set_transition(stt_0,stt_1,'a')
tr_1 = A.set_transition(stt_1,stt_0,'b')
tr_2 = A.set_transition(stt_0,stt_0,'b')


There is no Python class for transitions or states. We manipulate only their identifiers.

In [7]:
print (stt_0)
print (tr_2)
0
2

Making states initial or final

In [8]:
A.set_initial(stt_0)
A.set_final(stt_1)

Displaying an automaton

Displaying an automaton graphically, using dot for state placement.
(Works well for small automata only.)

In [9]:
A.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 3->F3 3->2 b

Displayin an automaton in text format.

In [10]:
A
Out[10]:
Automaton (lal_char_b):	Weight Set: B	Alphabet: abc
States:{	0(i)	1(f)	}
Transitions:{	0--a-->1	1--b-->0	0--b-->0		}

NB: the mention (i) following a state means that the state is initial. Similarly, (f) means final and (i,f) means initial and final.

Epsilon transitions

By default, automata do not allow epsilon transitions.
Moreover, automata with/without epsilon transitions are not represented by the same data structure. Allowing epsilon transition hence provokes a complete copy of the automaton.

In [11]:
Ae = A.allow_eps_transition()
Ae.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 3->F3 3->2 b

Adding an epsilon transition using the specific function.

In [12]:
tr_3 = Ae.set_eps_transition(stt_1,stt_0)
Ae.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 3->F3 3->2 \e, b

Epsilon transition is represented by the string "\e".

(NB: the '\' must be escaped by a preceding '\')


Adding an epsilon-transition using the general function.

In [13]:
Ae.set_transition(stt_1,stt_1,"\\e")
Out[13]:
6

For convenience we also allow "" as input to mean epsilon.

In [14]:
Ae.set_transition(stt_0,stt_0,"")
Out[14]:
7

Resulting automaton:

In [15]:
Ae.display()
%3 I2 2 $0 I2->2 F3 2->2 \e, b 3 $1 2->3 a 3->F3 3->2 \e, b 3->3 \e

Deleting edges and states

Let us consider the automaton A.

In [16]:
A.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 3->F3 3->2 b

First we add a bunch of states and transitions

In [17]:
stt_2 = A.add_state()
tr_3 = A.set_transition(stt_1,stt_1,'a')
# Not recording ids of transitions any further
A.set_transition(stt_2,stt_2,'b')
A.set_transition(stt_0,stt_2,'a')
A.set_transition(stt_2,stt_0,'b')
A.set_transition(stt_1,stt_2,'b')
A.set_transition(stt_1,stt_2,'a')
A.set_transition(stt_2,stt_1,'a')
A.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 4 $2 2->4 a 3->F3 3->2 b 3->3 a 3->4 a, b 4->2 b 4->3 a 4->4 b

Deleting a transition by id

In [18]:
A.del_transition(tr_3) # tr_3 is the transition: s2 --a--> s2
A.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 4 $2 2->4 a 3->F3 3->2 b 3->4 a, b 4->2 b 4->3 a 4->4 b

Deleting a transition by triplet (origin, destination, label)

In [19]:
A.del_transition(stt_2,stt_2,'b')
A.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 4 $2 2->4 a 3->F3 3->2 b 3->4 a, b 4->2 b 4->3 a

Deleting all transitions from a state to another (order matters).

In [20]:
A.del_transition(stt_1,stt_2)
A.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 4 $2 2->4 a 3->F3 3->2 b 4->2 b 4->3 a

Deleting a state (and all its outgoing and incoming transitions)

In [21]:
A.del_state(stt_2)
A.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 3->F3 3->2 b

Loading & saving automata

Saving an automaton to file. The format used is JavaScript Object Notation (JSON) hence the extension ".json" .

In [22]:
A.save("fibo.json")
In [23]:
ls
awalipy-2.0.0_rc2.egg-info        cython-src@
awalipy_purepython_extra.py       Edition1.ipynb@
awalipy_purepython_extra.pyc      Edition2.ipynb@
awalipy.so*                       Edition3.ipynb@
build/                            fibo.json
ClassicalTransformations1.ipynb@  fibo_LR_additioner.json
ClassicalTransformations2.ipynb@  RationalExpression.ipynb@
CMakeLists.txt                    setup.py
cython.log

Loading an automaton.

In [24]:
B = awalipy.load("fibo.json")
B.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 3->F3 3->2 b

A few example are given as part of the library. The examples are listed by the following function.

In [25]:
examples_dict = awalipy.list_examples()
for kind in ["automata","ratexps"]:
    print("======== {} ==========".format(kind))
    for name in examples_dict[kind]:
        print("- {}\t{}".format(name, examples_dict[kind][name]))
    print ("")
======== automata ==========
- lamplighter	Group automaton which realizes the Lamplighter group
- minab	Z-min-plus-automaton which computes the minimum among the numbers of 'a' and 'b' in every word
- ab	NFA with epsilon transition which recognizes words in 'a*b*'
- slowcerny	Synchronisable NFA which meets Cerny bound
- evena	DFA which recognizes words with an even number of 'a'
- b1	Z-FA that counts the number of 'b' in words of {a,b}*
- slowgrow	Z-min-plus-automaton with a slow growing length function
- heapmodel	Z-max-plus automaton, heap model with 2 pieces
- c1	Z-FA that converts words of {a,b}^* into their binary values
- d1	Z-FA computes the difference between the numbers of 'a' and 'b'
- binary	Z-FA that converts words of {0,1}^* into their binary values
- url-validator	Automaton with epsilon transition that validates url
- minblocka	Z-min-plus-automaton which computes the length of the shortest block of 'a' in every word
- prob-rabin	R-automaton (probabilistic automaton) that accepts w with prob. expansion(w)
- a1	NFA that accepts words with at least one factor 'ab'
- binary-int	Z-FA that converts words of {0,1}^* into their binary values
- rotation	C-automaton realizing a rotation
- e1	Q-FA that converts words of {a,b}^* into their binary values after the radix point
- gray	Gray code increment
- fibotdc-lr	Sequential transducer which tries to replace 'abb' by 'baa'
- flipper	Transducer that mimics a flipper game
- oddb	DFA which recognizes words with an odd number of 'b'

======== ratexps ==========
- div3b2-exp	Writing in base 2 of the integers divisible by 3
- conway-exp	Running example of an expression in Conway's book Regular Algebra and Finite Machines


Example automata can be loaded with the function load with their name

In [26]:
C = awalipy.load("a1")
C.display()
%3 I2 2 p I2->2 F4 2->2 a, b 3 q 2->3 a 4 r 3->4 b 4->F4 4->4 a, b

A few factories

make_cerny(n) returns an n-states DFA whose smallest synchronising word is of length (n-1)**2. This automaton attains the upper bound for the Cerny conjecture.

In [27]:
awalipy.make_cerny(5).display()
%3 I2 2 $0 I2->2 F2 2->F2 2->2 b 3 $1 2->3 a 3->3 b 4 $2 3->4 a 4->4 b 5 $3 4->5 a 5->5 b 6 $4 5->6 a 6->2 a, b

make_divkbaseb(k,b) returns an automaton accepting integers divisible by k, written in base b.

In [28]:
awalipy.make_divkbaseb(3,2).display()
%3 I2 2 $0 I2->2 F2 2->F2 2->2 0 3 $1 2->3 1 3->2 1 4 $2 3->4 0 4->3 0 4->4 1

make_ladybird(n) returns an automaton with n-states, whose equivalent minimal DFA has 2**n states.

In [29]:
awalipy.make_ladybird(5).display()
%3 I2 2 $0 I2->2 F2 2->F2 3 $1 2->3 a 3->2 c 3->3 b, c 4 $2 3->4 a 4->2 c 4->4 b, c 5 $3 4->5 a 5->2 c 5->5 b, c 6 $4 5->6 a 6->2 a, c 6->6 b, c

See also make_de_bruijn , make_double_ring , make_witness

Generate a random accessible DFA

In [30]:
awalipy.make_random_DFA(5,"ab").display()
%3 I2 2 $0 I2->2 F3 F5 F6 2->2 a 3 $1 2->3 b 3->F3 3->2 a 4 $2 3->4 b 5 $3 4->5 a, b 5->F5 5->4 a 6 $4 5->6 b 6->F6 6->2 a 6->4 b

Access and browsing

In [31]:
A.display()
%3 I2 2 $0 I2->2 F3 2->2 b 3 $1 2->3 a 3->F3 3->2 b

Getting the list of states, of transitions.

(It cannot be assumed that the ids of states are [0,1,...,n] for some n)

In [32]:
A.states(), A.transitions()
Out[32]:
([0, 1], [0, 1, 2])

Getting the list of initial states, of final states.

In [33]:
A.initial_states(), A.final_states()
Out[33]:
([0], [1])

Listing the transitions adjacent to a state.

In [34]:
A.outgoing(stt_1), A.incoming(stt_1)
Out[34]:
([1], [0])

Listing the transitions from a state to another.

In [35]:
A.outin(stt_0,stt_1)
Out[35]:
[0]

Getting information from a transition identifier.

In [36]:
# NB: tr_1 is a transition s1 --b--> s0
print (A.src_of(tr_1))   ## Source of transition tr_2
print (A.label_of(tr_1)) ## Label of tr_2
print (A.dst_of(tr_1))   ## Destination of tr_2
print (A.unpack_transition(tr_1)) ## All the above. (Third component 
                                ## is the weight and may be ignored.)
1
b
0
(1, 'b', '1', 0)

List of states reachable from a state in one transition.

In [37]:
A.successors(stt_0), A.successors(stt_0, 'a')
Out[37]:
([1, 0], [1])

List of states that may reach a state in one transition.

In [38]:
A.predecessors(stt_0), A.predecessors(stt_0, 'a')
Out[38]:
([1, 0], [])

Exercice: breadth-first traversal of A

In [39]:
def bf_traversal(A):
    visited = {}
    for i in A.states():
        visited[i] = False

    to_treat = A.initial_states()
    while len(to_treat) > 0 :
        stt = to_treat.pop()
        if not (visited[stt]):
            visited[stt] = True
            out_trs = A.outgoing(stt)
            for tr in out_trs:
                print (A.unpack_transition(tr))
                to_treat.append(A.dst_of(tr))
In [40]:
bf_traversal(A)
(0, 'a', '1', 1)
(0, 'b', '1', 0)
(1, 'b', '1', 0)
In [41]:
bf_traversal(awalipy.make_ladybird(3))
(0, 'a', '1', 1)
(1, 'b', '1', 1)
(1, 'c', '1', 1)
(1, 'c', '1', 0)
(1, 'a', '1', 2)
(2, 'b', '1', 2)
(2, 'c', '1', 2)
(2, 'c', '1', 0)
(2, 'a', '1', 0)
In [ ]:
 
In [ ]:
 
In [ ]: