lernen 0.2.0 → 0.3.0

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.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +15 -0
  3. data/README.md +534 -48
  4. data/Rakefile +26 -7
  5. data/Steepfile +14 -0
  6. data/examples/ripper_prism.rb +63 -0
  7. data/examples/uri_parse_regexp.rb +73 -0
  8. data/lib/lernen/algorithm/cex_processor/acex.rb +43 -0
  9. data/lib/lernen/algorithm/cex_processor/prefix_transformer_acex.rb +43 -0
  10. data/lib/lernen/algorithm/cex_processor.rb +115 -0
  11. data/lib/lernen/algorithm/kearns_vazirani/discrimination_tree.rb +207 -0
  12. data/lib/lernen/algorithm/kearns_vazirani/kearns_vazirani_learner.rb +100 -0
  13. data/lib/lernen/algorithm/kearns_vazirani.rb +44 -0
  14. data/lib/lernen/algorithm/kearns_vazirani_vpa/discrimination_tree_vpa.rb +246 -0
  15. data/lib/lernen/algorithm/kearns_vazirani_vpa/kearns_vazirani_vpa_learner.rb +89 -0
  16. data/lib/lernen/algorithm/kearns_vazirani_vpa.rb +35 -0
  17. data/lib/lernen/algorithm/learner.rb +82 -0
  18. data/lib/lernen/algorithm/lsharp/lsharp_learner.rb +367 -0
  19. data/lib/lernen/algorithm/lsharp/observation_tree.rb +115 -0
  20. data/lib/lernen/algorithm/lsharp.rb +43 -0
  21. data/lib/lernen/algorithm/lstar/lstar_learner.rb +49 -0
  22. data/lib/lernen/algorithm/lstar/observation_table.rb +214 -0
  23. data/lib/lernen/algorithm/lstar.rb +49 -0
  24. data/lib/lernen/algorithm/procedural/atr_manager.rb +200 -0
  25. data/lib/lernen/algorithm/procedural/procedural_learner.rb +223 -0
  26. data/lib/lernen/algorithm/procedural/procedural_sul.rb +47 -0
  27. data/lib/lernen/algorithm/procedural/return_indices_acex.rb +58 -0
  28. data/lib/lernen/algorithm/procedural.rb +57 -0
  29. data/lib/lernen/algorithm.rb +19 -0
  30. data/lib/lernen/automaton/dfa.rb +204 -0
  31. data/lib/lernen/automaton/mealy.rb +108 -0
  32. data/lib/lernen/automaton/moore.rb +122 -0
  33. data/lib/lernen/automaton/moore_like.rb +83 -0
  34. data/lib/lernen/automaton/proc_util.rb +93 -0
  35. data/lib/lernen/automaton/spa.rb +368 -0
  36. data/lib/lernen/automaton/transition_system.rb +209 -0
  37. data/lib/lernen/automaton/vpa.rb +300 -0
  38. data/lib/lernen/automaton.rb +19 -493
  39. data/lib/lernen/equiv/combined_oracle.rb +57 -0
  40. data/lib/lernen/equiv/exhaustive_search_oracle.rb +60 -0
  41. data/lib/lernen/equiv/moore_like_simulator_oracle.rb +36 -0
  42. data/lib/lernen/equiv/oracle.rb +109 -0
  43. data/lib/lernen/equiv/random_walk_oracle.rb +69 -0
  44. data/lib/lernen/equiv/random_well_matched_word_oracle.rb +139 -0
  45. data/lib/lernen/equiv/random_word_oracle.rb +71 -0
  46. data/lib/lernen/equiv/spa_simulator_oracle.rb +39 -0
  47. data/lib/lernen/equiv/test_words_oracle.rb +42 -0
  48. data/lib/lernen/equiv/transition_system_simulator_oracle.rb +36 -0
  49. data/lib/lernen/equiv/vpa_simulator_oracle.rb +48 -0
  50. data/lib/lernen/equiv.rb +25 -0
  51. data/lib/lernen/graph.rb +215 -0
  52. data/lib/lernen/system/block_sul.rb +41 -0
  53. data/lib/lernen/system/moore_like_simulator.rb +45 -0
  54. data/lib/lernen/system/moore_like_sul.rb +33 -0
  55. data/lib/lernen/system/sul.rb +126 -0
  56. data/lib/lernen/system/transition_system_simulator.rb +40 -0
  57. data/lib/lernen/system.rb +72 -0
  58. data/lib/lernen/version.rb +2 -1
  59. data/lib/lernen.rb +284 -34
  60. data/rbs_collection.lock.yaml +16 -0
  61. data/rbs_collection.yaml +14 -0
  62. data/renovate.json +6 -0
  63. data/sig/generated/lernen/algorithm/cex_processor/acex.rbs +30 -0
  64. data/sig/generated/lernen/algorithm/cex_processor/prefix_transformer_acex.rbs +27 -0
  65. data/sig/generated/lernen/algorithm/cex_processor.rbs +59 -0
  66. data/sig/generated/lernen/algorithm/kearns_vazirani/discrimination_tree.rbs +68 -0
  67. data/sig/generated/lernen/algorithm/kearns_vazirani/kearns_vazirani_learner.rbs +51 -0
  68. data/sig/generated/lernen/algorithm/kearns_vazirani.rbs +32 -0
  69. data/sig/generated/lernen/algorithm/kearns_vazirani_vpa/discrimination_tree_vpa.rbs +73 -0
  70. data/sig/generated/lernen/algorithm/kearns_vazirani_vpa/kearns_vazirani_vpa_learner.rbs +51 -0
  71. data/sig/generated/lernen/algorithm/kearns_vazirani_vpa.rbs +20 -0
  72. data/sig/generated/lernen/algorithm/learner.rbs +53 -0
  73. data/sig/generated/lernen/algorithm/lsharp/lsharp_learner.rbs +103 -0
  74. data/sig/generated/lernen/algorithm/lsharp/observation_tree.rbs +53 -0
  75. data/sig/generated/lernen/algorithm/lsharp.rbs +38 -0
  76. data/sig/generated/lernen/algorithm/lstar/lstar_learner.rbs +38 -0
  77. data/sig/generated/lernen/algorithm/lstar/observation_table.rbs +79 -0
  78. data/sig/generated/lernen/algorithm/lstar.rbs +37 -0
  79. data/sig/generated/lernen/algorithm/procedural/atr_manager.rbs +80 -0
  80. data/sig/generated/lernen/algorithm/procedural/procedural_learner.rbs +79 -0
  81. data/sig/generated/lernen/algorithm/procedural/procedural_sul.rbs +36 -0
  82. data/sig/generated/lernen/algorithm/procedural/return_indices_acex.rbs +33 -0
  83. data/sig/generated/lernen/algorithm/procedural.rbs +27 -0
  84. data/sig/generated/lernen/algorithm.rbs +10 -0
  85. data/sig/generated/lernen/automaton/dfa.rbs +93 -0
  86. data/sig/generated/lernen/automaton/mealy.rbs +61 -0
  87. data/sig/generated/lernen/automaton/moore.rbs +69 -0
  88. data/sig/generated/lernen/automaton/moore_like.rbs +63 -0
  89. data/sig/generated/lernen/automaton/proc_util.rbs +38 -0
  90. data/sig/generated/lernen/automaton/spa.rbs +125 -0
  91. data/sig/generated/lernen/automaton/transition_system.rbs +108 -0
  92. data/sig/generated/lernen/automaton/vpa.rbs +109 -0
  93. data/sig/generated/lernen/automaton.rbs +15 -0
  94. data/sig/generated/lernen/equiv/combined_oracle.rbs +27 -0
  95. data/sig/generated/lernen/equiv/exhaustive_search_oracle.rbs +38 -0
  96. data/sig/generated/lernen/equiv/moore_like_simulator_oracle.rbs +27 -0
  97. data/sig/generated/lernen/equiv/oracle.rbs +75 -0
  98. data/sig/generated/lernen/equiv/random_walk_oracle.rbs +41 -0
  99. data/sig/generated/lernen/equiv/random_well_matched_word_oracle.rbs +70 -0
  100. data/sig/generated/lernen/equiv/random_word_oracle.rbs +45 -0
  101. data/sig/generated/lernen/equiv/spa_simulator_oracle.rbs +30 -0
  102. data/sig/generated/lernen/equiv/test_words_oracle.rbs +20 -0
  103. data/sig/generated/lernen/equiv/transition_system_simulator_oracle.rbs +27 -0
  104. data/sig/generated/lernen/equiv/vpa_simulator_oracle.rbs +33 -0
  105. data/sig/generated/lernen/equiv.rbs +11 -0
  106. data/sig/generated/lernen/graph.rbs +80 -0
  107. data/sig/generated/lernen/system/block_sul.rbs +29 -0
  108. data/sig/generated/lernen/system/moore_like_simulator.rbs +31 -0
  109. data/sig/generated/lernen/system/moore_like_sul.rbs +28 -0
  110. data/sig/generated/lernen/system/sul.rbs +87 -0
  111. data/sig/generated/lernen/system/transition_system_simulator.rbs +28 -0
  112. data/sig/generated/lernen/system.rbs +62 -0
  113. data/sig/generated/lernen/version.rbs +6 -0
  114. data/sig/generated/lernen.rbs +214 -0
  115. data/sig-test/generated/test/example_test.rbs +14 -0
  116. data/sig-test/generated/test/lernen/algorithm/kearns_vazirani_test.rbs +16 -0
  117. data/sig-test/generated/test/lernen/algorithm/kearns_vazirani_vpa_test.rbs +10 -0
  118. data/sig-test/generated/test/lernen/algorithm/lsharp_test.rbs +16 -0
  119. data/sig-test/generated/test/lernen/algorithm/lstar_test.rbs +16 -0
  120. data/sig-test/generated/test/lernen/algorithm/procedural_test.rbs +10 -0
  121. data/sig-test/generated/test/lernen/automaton/dfa_test.rbs +19 -0
  122. data/sig-test/generated/test/lernen/automaton/mealy_test.rbs +19 -0
  123. data/sig-test/generated/test/lernen/automaton/moore_test.rbs +19 -0
  124. data/sig-test/generated/test/lernen/automaton/proc_util_test.rbs +19 -0
  125. data/sig-test/generated/test/lernen/automaton/spa_test.rbs +19 -0
  126. data/sig-test/generated/test/lernen/automaton/vpa_test.rbs +19 -0
  127. data/sig-test/generated/test/lernen/equiv/exhaustive_search_oracle_test.rbs +10 -0
  128. data/sig-test/generated/test/lernen/equiv/random_walk_oracle_test.rbs +10 -0
  129. data/sig-test/generated/test/lernen/equiv/random_word_oracle_test.rbs +10 -0
  130. data/sig-test/generated/test/lernen/system/block_sul_test.rbs +16 -0
  131. data/sig-test/generated/test/lernen/system/moore_like_simulator_test.rbs +16 -0
  132. data/sig-test/generated/test/lernen/system/transition_system_simulator_test.rbs +13 -0
  133. data/sig-test/generated/test/lernen/system_test.rbs +11 -0
  134. data/sig-test/generated/test/lernen_test.rbs +13 -0
  135. metadata +131 -11
  136. data/.yardopts +0 -3
  137. data/lib/lernen/cex_processor.rb +0 -92
  138. data/lib/lernen/kearns_vazirani.rb +0 -310
  139. data/lib/lernen/lsharp.rb +0 -344
  140. data/lib/lernen/lstar.rb +0 -170
  141. data/lib/lernen/oracle.rb +0 -119
  142. data/lib/lernen/sul.rb +0 -210
@@ -0,0 +1,300 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Lernen
5
+ module Automaton
6
+ # VPA represents a [visily pushdown automaton](https://en.wikipedia.org/wiki/Nested_word#Visibly_pushdown_automaton).
7
+ #
8
+ # Especially, this definition represents 1-SEVPA (1-module single-entry visibly pushdown automaton).
9
+ #
10
+ # @rbs generic In -- Type for input alphabet
11
+ # @rbs generic Call -- Type for call alphabet
12
+ # @rbs generic Return -- Type for return alphabet
13
+ class VPA < MooreLike #[VPA::Conf[Call] | nil, In | Call | Return, bool]
14
+ # Conf is a configuration of VPA run.
15
+ #
16
+ # @rbs skip
17
+ Conf = Data.define(:state, :stack)
18
+
19
+ # @rbs!
20
+ # class Conf[Call] < Data
21
+ # attr_reader state: Integer
22
+ # attr_reader stack: Array[[Integer, Call]]
23
+ # def self.[]: [Call] (Integer state, Array[[Integer, Call]] stack) -> Conf[Call]
24
+ # end
25
+
26
+ # @rbs @initial_state: Integer
27
+ # @rbs @accept_state_set: Set[Integer]
28
+ # @rbs @transition_function: Hash[[Integer, In], Integer]
29
+ # @rbs @return_transition_function: Hash[[Integer, Return], Hash[[Integer, Call], Integer]]
30
+
31
+ #: (
32
+ # Integer initial_state,
33
+ # Set[Integer] accept_state_set,
34
+ # Hash[[Integer, In], Integer] transition_function.
35
+ # Hash[[Integer, Return], Hash[[Integer, Call], Integer]] return_transition_function
36
+ # ) -> void
37
+ def initialize(initial_state, accept_state_set, transition_function, return_transition_function)
38
+ super()
39
+
40
+ @initial_state = initial_state
41
+ @accept_state_set = accept_state_set
42
+ @transition_function = transition_function
43
+ @return_transition_function = return_transition_function
44
+ end
45
+
46
+ attr_reader :initial_state #: Integer
47
+ attr_reader :accept_state_set #: Set[Integer]
48
+ attr_reader :transition_function #: Hash[[Integer, In], Integer]
49
+ attr_reader :return_transition_function #: Hash[[Integer, Return], Hash[[Integer, Call], Integer]]
50
+
51
+ # @rbs return: :vpa
52
+ def type = :vpa
53
+
54
+ # @rbs override
55
+ def initial_conf = Conf[initial_state, []]
56
+
57
+ # @rbs override
58
+ def step_conf(conf, input)
59
+ return nil if conf.nil?
60
+
61
+ next_state = transition_function[[conf.state, input]] # steep:ignore
62
+ return Conf[next_state, conf.stack] if next_state
63
+
64
+ return_transition_guard = return_transition_function[[conf.state, input]] # steep:ignore
65
+ if return_transition_guard
66
+ *next_stack, last_call = conf.stack
67
+ return nil unless last_call
68
+ next_state = return_transition_guard[last_call]
69
+ return Conf[next_state, next_stack]
70
+ end
71
+
72
+ # When there is no usual transition and no return tansition for `input`,
73
+ # then we assume that `input` is a call alphabet.
74
+ Conf[initial_state, conf.stack + [[conf.state, input]]] # steep:ignore
75
+ end
76
+
77
+ # @rbs override
78
+ def output(conf)
79
+ !conf.nil? && accept_state_set.include?(conf.state) && conf.stack.empty?
80
+ end
81
+
82
+ # Checks the structural equality between `self` and `other`.
83
+ #
84
+ #: (untyped other) -> bool
85
+ def ==(other)
86
+ other.is_a?(VPA) && initial_state == other.initial_state && accept_state_set == other.accept_state_set &&
87
+ transition_function == other.transition_function &&
88
+ return_transition_function == other.return_transition_function
89
+ end
90
+
91
+ # Returns the array of states of this VPA.
92
+ #
93
+ # The result array is sorted.
94
+ #
95
+ #: () -> Array[Integer]
96
+ def states
97
+ state_set = Set.new
98
+ state_set << initial_state
99
+ accept_state_set.each { |state| state_set << state }
100
+ transition_function.each do |(state, _), next_state|
101
+ state_set << state
102
+ state_set << next_state
103
+ end
104
+ return_transition_function.each do |(state, _), return_transition_guard|
105
+ state_set << state
106
+ return_transition_guard.each do |(call_state, _), next_state|
107
+ state_set << call_state
108
+ state_set << next_state
109
+ end
110
+ end
111
+ state_set.to_a.sort!
112
+ end
113
+
114
+ # Returns the error state of this VPA.
115
+ #
116
+ # An error state is:
117
+ #
118
+ # - neither a initial state nor accepting states, and
119
+ # - only having self-loops for all `input`.
120
+ #
121
+ # If an error state is not found, it returns `nil`.
122
+ #
123
+ #: () -> (Integer | nil)
124
+ def error_state
125
+ transition_function
126
+ .group_by { |(state, _), _| state }
127
+ .transform_values { _1.to_h { |(_, input), next_state| [input, next_state] } }
128
+ .each do |state, transition_hash|
129
+ # The initial state and accepting states are not an error state.
130
+ next if state == initial_state || accept_state_set.include?(state)
131
+
132
+ # An error state should only have self-loops.
133
+ next unless transition_hash.all? { |_, next_state| state == next_state }
134
+ all_returns_are_self_loops =
135
+ return_transition_function.all? do |_, return_transition_guard|
136
+ return_transition_guard
137
+ .filter { |(call_state, _), _| call_state == state }
138
+ .all? { |_, next_state| state == next_state }
139
+ end
140
+ next unless all_returns_are_self_loops
141
+
142
+ return state
143
+ end
144
+
145
+ nil
146
+ end
147
+
148
+ # Returns a graph of this VPA.
149
+ #
150
+ # (?shows_error_state: bool) -> Graph
151
+ def to_graph(shows_error_state: false)
152
+ error_state = error_state() unless shows_error_state
153
+
154
+ nodes =
155
+ states
156
+ .filter_map do |state|
157
+ next if state == error_state
158
+ shape = accept_state_set.include?(state) ? :doublecircle : :circle #: Graph::node_shape
159
+ [state, Graph::Node[state.to_s, shape]]
160
+ end
161
+ .to_h
162
+
163
+ edges =
164
+ transition_function.filter_map do |(state, input), next_state|
165
+ next if state == error_state || next_state == error_state
166
+ Graph::Edge[state, input.inspect, next_state] # steep:ignore
167
+ end
168
+
169
+ edges +=
170
+ return_transition_function.flat_map do |(state, return_input), return_transition_guard|
171
+ next [] if state == error_state
172
+ return_transition_guard.filter_map do |(call_state, call_input), next_state|
173
+ next if call_state == error_state || next_state == error_state
174
+ label = "#{return_input.inspect} / (#{call_state}, #{call_input.inspect})" # steep:ignore
175
+ Graph::Edge[state, label, next_state]
176
+ end
177
+ end
178
+
179
+ Graph.new(nodes, edges)
180
+ end
181
+
182
+ # Finds a separating word between `vpa1` and `vpa2`.
183
+ #
184
+ #: [In, Call, Return] (
185
+ # Array[In] alphabet,
186
+ # Array[Call] call_alphabet,
187
+ # Array[Return] return_alphabet,
188
+ # VPA[In, Call, Return] vpa1,
189
+ # VPA[In, Call, Return] vpa2
190
+ # ) -> (Array[In | Call | Return] | nil)
191
+ def self.find_separating_word(alphabet, call_alphabet, return_alphabet, vpa1, vpa2)
192
+ raise ArgumentError, "Cannot find a separating word for different type automata" unless vpa2.is_a?(vpa1.class)
193
+
194
+ queue = []
195
+ prefix_hash = {}
196
+
197
+ initial_pair = [vpa1.initial_conf&.state, vpa2.initial_conf&.state]
198
+ queue << initial_pair
199
+ prefix_hash[initial_pair] = []
200
+
201
+ until queue.empty?
202
+ state1, state2 = queue.shift
203
+ prefix = prefix_hash[[state1, state2]]
204
+
205
+ alphabet.each do |input|
206
+ output1, next_conf1 = vpa1.step(state1 && Conf[state1, []], input)
207
+ output2, next_conf2 = vpa2.step(state2 && Conf[state2, []], input)
208
+
209
+ word = prefix + [input]
210
+ return word if output1 != output2
211
+
212
+ next_pair = [next_conf1&.state, next_conf2&.state]
213
+ unless prefix_hash.include?(next_pair)
214
+ queue << next_pair
215
+ prefix_hash[next_pair] = word
216
+ end
217
+ end
218
+
219
+ found_state_pairs = prefix_hash.keys
220
+ call_alphabet.each do |call_input|
221
+ return_alphabet.each do |return_input|
222
+ found_state_pairs.each do |(call_state1, call_state2)|
223
+ return_conf1 = state1 && Conf[state1, [[call_state1, call_input]]] # steep:ignore
224
+ return_conf2 = state2 && Conf[state2, [[call_state2, call_input]]] # steep:ignore
225
+
226
+ output1, next_conf1 = vpa1.step(return_conf1, return_input)
227
+ output2, next_conf2 = vpa2.step(return_conf2, return_input)
228
+
229
+ word = prefix_hash[[call_state1, call_state2]] + [call_input] + prefix + [return_input]
230
+ return word if output1 != output2
231
+
232
+ next_pair = [next_conf1&.state, next_conf2&.state]
233
+ unless prefix_hash.include?(next_pair)
234
+ queue << next_pair
235
+ prefix_hash[next_pair] = word
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ nil
243
+ end
244
+
245
+ # Generates a VPA randomly.
246
+ #
247
+ #: [In, Call, Return] (
248
+ # alphabet: Array[In],
249
+ # call_alphabet: Array[Call],
250
+ # return_alphabet: Array[Return],
251
+ # ?min_state_size: Integer,
252
+ # ?max_state_size: Integer,
253
+ # ?accept_state_size: Integer,
254
+ # ?random: Random,
255
+ # ) -> VPA[In, Call, Return]
256
+ def self.random(
257
+ alphabet:,
258
+ call_alphabet:,
259
+ return_alphabet:,
260
+ min_state_size: 5,
261
+ max_state_size: 10,
262
+ accept_state_size: 2,
263
+ random: Random
264
+ )
265
+ transition_function, reachable_paths =
266
+ TransitionSystem.random_transition_function(
267
+ alphabet:,
268
+ min_state_size:,
269
+ max_state_size:,
270
+ num_reachable_paths: accept_state_size,
271
+ random:
272
+ )
273
+ accept_state_set = reachable_paths.to_set(&:last) #: Set[Integer]
274
+
275
+ state_set = Set.new
276
+ transition_function.each do |(state, _), next_state|
277
+ state_set << state
278
+ state_set << next_state
279
+ end
280
+ states = state_set.to_a
281
+ states.sort!
282
+
283
+ return_transition_function = {}
284
+ states.each do |return_state|
285
+ return_alphabet.each do |return_input|
286
+ return_transition_guard = return_transition_function[[return_state, return_input]] = {}
287
+ states.each do |call_state|
288
+ call_alphabet.each do |call_input|
289
+ next_state = states.sample(random:)
290
+ return_transition_guard[[call_state, call_input]] = next_state
291
+ end
292
+ end
293
+ end
294
+ end
295
+
296
+ new(0, accept_state_set, transition_function, return_transition_function)
297
+ end
298
+ end
299
+ end
300
+ end