Hide keyboard shortcuts

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 

5 

6from pythomata.core import Alphabet, SymbolType 

7 

8IndexNotFound = ValueError("No symbol for that index.") 

9SymbolNotFound = ValueError("No symbol for that index.") 

10 

11 

12class ArrayAlphabet(Alphabet[SymbolType]): 

13 """An alphabet implemented with an array.""" 

14 

15 def __init__(self, symbols: List[SymbolType]): 

16 """Initialize the array alphabet.""" 

17 self.symbols = tuple(symbols) # type: Tuple[SymbolType, ...] 

18 

19 def get_symbol(self, index: int) -> SymbolType: 

20 """ 

21 Get the symbol of the alphabet, given the index. 

22 

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 

31 

32 def get_symbol_index(self, symbol: SymbolType) -> int: 

33 """ 

34 Get the index of a symbol of the alphabet. 

35 

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) 

41 

42 if index == -1: 

43 raise SymbolNotFound 

44 

45 return index 

46 

47 def __get_symbol_index(self, symbol: SymbolType) -> int: 

48 """ 

49 Iterate over the list of symbols and find the index. 

50 

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 

58 

59 @property 

60 def size(self) -> int: 

61 """ 

62 Get the size of the alphabet. 

63 

64 :return: the size of the alphabet. 

65 """ 

66 return len(self.symbols) 

67 

68 def __iter__(self) -> Iterable[SymbolType]: 

69 """Iterate over the alphabet.""" 

70 return iter(self.symbols) 

71 

72 

73class MapAlphabet(Alphabet[SymbolType]): 

74 """An alphabet implemented with a mapping.""" 

75 

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 

80 

81 def get_symbol(self, index: int) -> SymbolType: 

82 """ 

83 Get the symbol of the alphabet, given the index. 

84 

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 

93 

94 def get_symbol_index(self, symbol: SymbolType) -> int: 

95 """ 

96 Get the index of a symbol of the alphabet. 

97 

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] 

103 

104 @property 

105 def size(self) -> int: 

106 """ 

107 Get the size of the alphabet. 

108 

109 :return: the size of the alphabet. 

110 """ 

111 return len(self.symbols) 

112 

113 def __iter__(self): 

114 """Iterate over the alphabet.""" 

115 return iter(self.symbols) 

116 

117 

118class RangeIntAlphabet(Alphabet[int]): 

119 """ 

120 Use ranges to represent a collection of symbols. 

121 

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 """ 

125 

126 def __init__(self, stop: int, start: int = 0, step: int = 1): 

127 """ 

128 Initialize the range (start included, end NOT included). 

129 

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) 

136 

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 

143 

144 def get_symbol_index(self, symbol: int) -> int: 

145 """Get the index, given the symbol.""" 

146 return self.r.index(symbol) 

147 

148 @property 

149 def size(self) -> int: 

150 """Get the size of the alphabet.""" 

151 return len(self.r) 

152 

153 def __iter__(self): 

154 """Iterate over the alphabet.""" 

155 return self.r 

156 

157 

158def from_array(symbols: Iterable[SymbolType]) -> Alphabet: 

159 """Get an alphabet from arrays.""" 

160 return ArrayAlphabet(list(symbols)) 

161 

162 

163class VectorizedAlphabet(Generic[SymbolType], Alphabet[Tuple[SymbolType, ...]]): 

164 """ 

165 Vectorized version of an alphabet. 

166 

167 The result of this operation is a new alphabet whose symbols 

168 are vectors of symbols of the original alphabet. 

169 """ 

170 

171 def __init__(self, alphabet: Alphabet[SymbolType], n: int): 

172 """ 

173 Initialize the vectorized alphabet. 

174 

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 

181 

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) 

194 

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 

205 

206 @property 

207 def size(self) -> int: 

208 """Get the size of the alphabet.""" 

209 return self._alphabet.size ** self.n 

210 

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) 

215 

216 

217class SymbolicAlphabet(Alphabet[str]): 

218 """ 

219 The alphabet used by a Symbolic automaton. 

220 

221 >>> alphabet = SymbolicAlphabet(5) 

222 >>> alphabet.get_symbol(0) 

223 '00000' 

224 

225 >>> alphabet.get_symbol(1) 

226 '00001' 

227 

228 >>> alphabet.get_symbol_index("00010") 

229 2 

230 

231 >>> alphabet.size # 2 ** 5 

232 32 

233 

234 >>> alphabet.get_symbol_index("0000") # length is not correct 

235 Traceback (most recent call last): 

236 ... 

237 ValueError: No symbol for that index. 

238 

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 """ 

244 

245 def __init__(self, nb_propositions: int): 

246 """ 

247 Initialize a Symbolic Alphabet. 

248 

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) 

253 

254 def get_symbol(self, index: int) -> str: 

255 """Get a symbol given its index.""" 

256 return "".join(self._inner_alphabet.get_symbol(index)) 

257 

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)) 

261 

262 @property 

263 def size(self) -> int: 

264 """Get the size of the alphabet.""" 

265 return self._inner_alphabet.size 

266 

267 def __iter__(self): 

268 """Iterate over the alphabet.""" 

269 return iter(self._inner_alphabet)