Coverage for /home/marcofavorito/workfolder/pythomata/pythomata/alphabets.py : 41%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2"""This module provides many popular alphabets."""
3import itertools
4from typing import List, Tuple, Iterable, Iterator, Generic
6from pythomata.core import Alphabet, SymbolType
8IndexNotFound = ValueError("No symbol for that index.")
9SymbolNotFound = ValueError("No symbol for that index.")
12class ArrayAlphabet(Alphabet[SymbolType]):
13 """An alphabet implemented with an array."""
15 def __init__(self, symbols: List[SymbolType]):
16 """Initialize the array alphabet."""
17 self.symbols = tuple(symbols) # type: Tuple[SymbolType, ...]
19 def get_symbol(self, index: int) -> SymbolType:
20 """
21 Get the symbol of the alphabet, given the index.
23 :param index: the index.
24 :return: the corresponding symbol.
25 :raises ValueError: if there is not any symbol for that index.
26 """
27 try:
28 return self.symbols[index]
29 except IndexError:
30 raise IndexNotFound
32 def get_symbol_index(self, symbol: SymbolType) -> int:
33 """
34 Get the index of a symbol of the alphabet.
36 :param symbol: the symbol.
37 :return: its index.
38 :raises ValueError: if the symbol does not belong to the alphabet.
39 """
40 index = self.__get_symbol_index(symbol)
42 if index == -1:
43 raise SymbolNotFound
45 return index
47 def __get_symbol_index(self, symbol: SymbolType) -> int:
48 """
49 Iterate over the list of symbols and find the index.
51 :param symbol: the symbol whose index is requested.
52 :return: its index, or -1 if not found.
53 """
54 for idx, sym in enumerate(self.symbols):
55 if sym == symbol:
56 return idx
57 return -1
59 @property
60 def size(self) -> int:
61 """
62 Get the size of the alphabet.
64 :return: the size of the alphabet.
65 """
66 return len(self.symbols)
68 def __iter__(self) -> Iterable[SymbolType]:
69 """Iterate over the alphabet."""
70 return iter(self.symbols)
73class MapAlphabet(Alphabet[SymbolType]):
74 """An alphabet implemented with a mapping."""
76 def __init__(self, symbols: Iterable[SymbolType]):
77 """Initialize the array alphabet."""
78 self.symbols = tuple(symbols) # type: Tuple[SymbolType, ...]
79 self.symbol_to_index = dict(map(reversed, enumerate(symbols))) # type: ignore
81 def get_symbol(self, index: int) -> SymbolType:
82 """
83 Get the symbol of the alphabet, given the index.
85 :param index: the index.
86 :return: the corresponding symbol.
87 :raises ValueError: if there is not any symbol for that index.
88 """
89 try:
90 return self.symbols[index]
91 except KeyError:
92 raise IndexNotFound
94 def get_symbol_index(self, symbol: SymbolType) -> int:
95 """
96 Get the index of a symbol of the alphabet.
98 :param symbol: the symbol.
99 :return: its index.
100 :raises ValueError: if the symbol does not belong to the alphabet.
101 """
102 return self.symbol_to_index[symbol]
104 @property
105 def size(self) -> int:
106 """
107 Get the size of the alphabet.
109 :return: the size of the alphabet.
110 """
111 return len(self.symbols)
113 def __iter__(self):
114 """Iterate over the alphabet."""
115 return iter(self.symbols)
118class RangeIntAlphabet(Alphabet[int]):
119 """
120 Use ranges to represent a collection of symbols.
122 This alphabet does not store the list of elements explicitly,
123 but it only stores the start and the end element of the range.
124 """
126 def __init__(self, stop: int, start: int = 0, step: int = 1):
127 """
128 Initialize the range (start included, end NOT included).
130 :param start: the start of the range (included)
131 :param stop: the end of the range (excluded)
132 :param step: the step for the range.
133 """
134 assert start < stop, "Start must be lower than stop."
135 self.r = range(start, stop, step)
137 def get_symbol(self, index: int) -> int:
138 """Get the symbol associated to the index."""
139 try:
140 return self.r[index]
141 except IndexError:
142 raise IndexNotFound
144 def get_symbol_index(self, symbol: int) -> int:
145 """Get the index, given the symbol."""
146 return self.r.index(symbol)
148 @property
149 def size(self) -> int:
150 """Get the size of the alphabet."""
151 return len(self.r)
153 def __iter__(self):
154 """Iterate over the alphabet."""
155 return self.r
158def from_array(symbols: Iterable[SymbolType]) -> Alphabet:
159 """Get an alphabet from arrays."""
160 return ArrayAlphabet(list(symbols))
163class VectorizedAlphabet(Generic[SymbolType], Alphabet[Tuple[SymbolType, ...]]):
164 """
165 Vectorized version of an alphabet.
167 The result of this operation is a new alphabet whose symbols
168 are vectors of symbols of the original alphabet.
169 """
171 def __init__(self, alphabet: Alphabet[SymbolType], n: int):
172 """
173 Initialize the vectorized alphabet.
175 :param alphabet: the alphabet.
176 :param n: the number of desired dimensions.
177 :return: the vectorized alphabet.
178 """
179 self._alphabet = alphabet
180 self.n = n
182 def get_symbol(self, index: int) -> Tuple[SymbolType, ...]:
183 """Get the symbol from an index."""
184 symbol_vector = []
185 reminder_index = index
186 i = self.n - 1
187 while i >= 0:
188 new_index = reminder_index // (self._alphabet.size ** i)
189 new_symbol = self._alphabet.get_symbol(new_index)
190 symbol_vector.append(new_symbol)
191 reminder_index %= (self._alphabet.size ** i)
192 i -= 1
193 return tuple(symbol_vector)
195 def get_symbol_index(self, vector: Tuple[SymbolType, ...]) -> int:
196 """Get the index of a symbol."""
197 if len(vector) != self.n:
198 raise SymbolNotFound
199 index_of_vector = 0
200 for position, symbol in enumerate(vector):
201 index = self._alphabet.get_symbol_index(symbol)
202 index_of_vector = index_of_vector * self._alphabet.size
203 index_of_vector += index
204 return index_of_vector
206 @property
207 def size(self) -> int:
208 """Get the size of the alphabet."""
209 return self._alphabet.size ** self.n
211 def __iter__(self) -> Iterator[Tuple[SymbolType, ...]]:
212 """Iterate over the alphabet."""
213 index_vector_iterable = itertools.product(range(self.size), repeat=self.n)
214 return map(lambda x: tuple([self._alphabet.get_symbol(idx) for idx in x]), index_vector_iterable)
217class SymbolicAlphabet(Alphabet[str]):
218 """
219 The alphabet used by a Symbolic automaton.
221 >>> alphabet = SymbolicAlphabet(5)
222 >>> alphabet.get_symbol(0)
223 '00000'
225 >>> alphabet.get_symbol(1)
226 '00001'
228 >>> alphabet.get_symbol_index("00010")
229 2
231 >>> alphabet.size # 2 ** 5
232 32
234 >>> alphabet.get_symbol_index("0000") # length is not correct
235 Traceback (most recent call last):
236 ...
237 ValueError: No symbol for that index.
239 >>> alphabet.get_symbol_index("00002") # forbidden symbol - only one of 0, 1 or X
240 Traceback (most recent call last):
241 ...
242 ValueError: No symbol for that index.
243 """
245 def __init__(self, nb_propositions: int):
246 """
247 Initialize a Symbolic Alphabet.
249 :param nb_propositions: the number of propositions.
250 """
251 self.nb_propositions = nb_propositions
252 self._inner_alphabet = VectorizedAlphabet(ArrayAlphabet[str](['0', '1']), nb_propositions)
254 def get_symbol(self, index: int) -> str:
255 """Get a symbol given its index."""
256 return "".join(self._inner_alphabet.get_symbol(index))
258 def get_symbol_index(self, symbol: str) -> int:
259 """Get the index of a symbol."""
260 return self._inner_alphabet.get_symbol_index(tuple(symbol))
262 @property
263 def size(self) -> int:
264 """Get the size of the alphabet."""
265 return self._inner_alphabet.size
267 def __iter__(self):
268 """Iterate over the alphabet."""
269 return iter(self._inner_alphabet)