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
data/lib/lernen.rb CHANGED
@@ -1,79 +1,329 @@
1
1
  # frozen_string_literal: true
2
+ # rbs_inline: enabled
2
3
 
3
4
  require "set"
4
5
 
5
- module Lernen
6
- # Error is an error class for this library.
7
- class Error < StandardError
8
- end
9
- end
10
-
11
- require_relative "lernen/automaton"
12
- require_relative "lernen/cex_processor"
13
- require_relative "lernen/oracle"
14
- require_relative "lernen/sul"
15
- require_relative "lernen/version"
6
+ require "lernen/version"
7
+ require "lernen/graph"
8
+ require "lernen/automaton"
9
+ require "lernen/system"
10
+ require "lernen/equiv"
11
+ require "lernen/algorithm"
16
12
 
17
- require_relative "lernen/lstar"
18
- require_relative "lernen/kearns_vazirani"
19
- require_relative "lernen/lsharp"
20
-
21
- # Lernen is a simple automata learning library.
13
+ # Lernen is an automata learning library written in Ruby.
14
+ #
15
+ # **Automata learning** (active automata learning) is a known technique to infer an automaton
16
+ # from a teacher program; here, a teacher is an abstraction of a system (or a program) that
17
+ # can answer two kinds of queries. One kind of queries is **membership** query, which takes
18
+ # an input word and returns a boolean value whether the word is accepted or rejected by the system.
19
+ # Another kind of queries is **equivalence** query, which takes a hypothesis (under-learning) automaton
20
+ # and returns `true` if the hypothesis is equivalent to the system, or returns a counterexample
21
+ # word if it is not equivalent. Automata learning algorithms use these queries to gather
22
+ # information about the black-box system and infer an automaton which is equivalent to the system.
23
+ #
24
+ # This library implements some automata learning algorithms.
25
+ #
26
+ # - L* (also known as Angluin's L*) is a common and classic algorithm for automata learning, introduced
27
+ # by [Angluin (1987) "Learning Regular Sets from Queries and Counterexamples"](https://dl.acm.org/doi/10.1016/0890-5401%2887%2990052-6).
28
+ # This algorithm uses an observation table for collecting query results and inferring an automaton.
29
+ # Our implementation also accepts the Rivest-Schapire counterexample processing optimization described by
30
+ # [Rivest & Schapire (1993) "Inference of Finite Automata Using Homing Sequences"](https://www.sciencedirect.com/science/article/pii/S0890540183710217).
31
+ # - Kearns-Vazirani is also a common and classic algorithm, introduced by
32
+ # [Kearns & Vazirani (1994) "An Introduction to Computational Learning Theory"](https://direct.mit.edu/books/monograph/2604/An-Introduction-to-Computational-Learning-Theory).
33
+ # This algorithm uses a discrimination tree for learning an automaton instead of an observation
34
+ # tree. Also, our implementation is extended to infer a VPA (visibly pushdown automaton) that is
35
+ # an extension of DFA which can recognize some non-regular languages (nested words).
36
+ # It is the default algorithm in this library.
37
+ # - L# is a modern algorithm for automata learning, introduced by
38
+ # [Vaandrager et al. (2022) "A New Approach for Active Automata Learning Based on Apartness"](https://link.springer.com/chapter/10.1007/978-3-030-99524-9_12).
39
+ # This algorithm uses apartness relation and an observation tree for learning an automaton.
40
+ # In many cases, it reduces the numbers of queries, but the data structure and algorithm have
41
+ # some overheads. If a query is slow (e.g., forking a process), this algorithm may be a good option.
42
+ #
43
+ # ## Example
44
+ #
45
+ # This library provides `Lernen.learn` method as a good frontend for learning an automaton.
46
+ #
47
+ # In the most simple way to use it, we need to give `alphabet` and a block to infer a program
48
+ # to `Lernen.learn`. See the below example. This example is a program to learn a prediction on
49
+ # the binary language as a DFA and print it as a [mermaid](https://mermaid.js.org) diagram.
50
+ #
51
+ # ```ruby
52
+ # dfa = Lernen.learn(alphabet: %w[0 1]) do |word|
53
+ # word.count("1") % 4 == 3
54
+ # end
55
+ # puts dfa.to_mermaid
56
+ #
57
+ # # Output:
58
+ # # flowchart TD
59
+ # # 0((0))
60
+ # # 1((1))
61
+ # # 2((2))
62
+ # # 3(((3)))
63
+ # #
64
+ # # 0 -- "'0'" --> 0
65
+ # # 0 -- "'1'" --> 1
66
+ # # 1 -- "'0'" --> 1
67
+ # # 1 -- "'1'" --> 2
68
+ # # 2 -- "'0'" --> 2
69
+ # # 2 -- "'1'" --> 3
70
+ # # 3 -- "'0'" --> 3
71
+ # # 3 -- "'1'" --> 0
72
+ # ```
73
+ #
74
+ # Of course, we can specify more parameters to `Lernen.learn` for learning other kinds of automata
75
+ # such as Moore or Mealy machines. Please refer the `Lernen.learn` doc.
22
76
  module Lernen
23
- # Learn an automaton.
77
+ # @rbs!
78
+ # type oracle_type = :exhaustive_search
79
+ # | :random_walk
80
+ # | :random_word
81
+ # | :random_well_matched_word
82
+ # | :simulator
83
+ #
84
+ # type algorithm_name = :lstar
85
+ # | :kearns_vazirani
86
+ # | :lsharp
87
+
88
+ # Learn an automaton by using the given parameters.
89
+ #
90
+ # This method is a frontend of the learning algorithms. Actual implementations are placed under
91
+ # the `Lernen::Algorithm` namespace.
92
+ #
93
+ # ## Parameters
94
+ #
95
+ # This method takes a lot of parameters, but almost of parameters are optional. To start learning,
96
+ # we need to give `alphabet` and a block of a program to infer an automaton.
97
+ #
98
+ # - `alphabet`: An input alphabet. This must be given as an `Array` object.
99
+ # - `call_alphabet`: A call input alphabet of VPA. If this is specified, `automaton_type` is specified
100
+ # as `:vpa` automatically.
101
+ # - `return_alphabet`: A return input alphabet of VPA.
102
+ # - `sul`: A system under learning. If an automaton instance is given, it is converted it to a simulator
103
+ # and use it as a SUL. Or, if it is not specified, we use a block as a SUL.
104
+ # - `oracle`: An equivalence oracle. It is one of `:exhaustive_search`, `:random_walk`, `:random_word`, or
105
+ # an actual instance of `Equiv::Oracle`. If the value is a symbol, an `Equiv::Oracle` instance of the specified
106
+ # kind is created with `oracle_params`. The default value is `:random_word` if `automaton_type` is one of `:dfa`,
107
+ # `:moore`, and `:mealy`, or the default value is `:random_well_matched_word` if `automaton_type` is either `:spa`
108
+ # or `:vpa`.
109
+ # - `oracle_params`: A hash of parameters for equivalence oracle. The default value is `{}`.
110
+ # - `algorithm`: An algorithm name to use. It is one of `:lstar`, `:kearns_vazirani`, or `:lsharp`. The default value
111
+ # is `:kearns_vazirani` (if `automaton_type` is one of `:dfa`, `:moore`, and `:mealy`), or `:kearns_vazirani_vpa`
112
+ # (if `automaton_type` is `vpa`), or `:procedural` (if `automaton_type` is `spa`).
113
+ # - `automaton_type`: A type of automaton to infer. It is one of `:dfa`, `:mealy`, `:moore`, `:vpa`, and `:spa`.
114
+ # The default value is `:dfa`, but it becomes `:vpa` or `:spa` if `call_alphabet` or `return_input` is specified.
115
+ # - `params`: A hash of parameter to pass a learning algorithm. The default value is `{}`.
116
+ # - `random`: A PRNG instance. It is used by an equivalence oracle.
117
+ #
118
+ #: [In] (
119
+ # alphabet: Array[In],
120
+ # sul: Automaton::DFA[In] | System::MooreLikeSUL[In, bool],
121
+ # ?oracle: oracle_type | Equiv::Oracle[In, bool],
122
+ # ?oracle_params: Hash[Symbol, untyped],
123
+ # ?algorithm: algorithm_name,
124
+ # ?automaton_type: :dfa,
125
+ # ?params: Hash[Symbol, untyped],
126
+ # ?random: Random
127
+ # ) -> Automaton::DFA[In]
128
+ #: [In] (
129
+ # alphabet: Array[In],
130
+ # ?oracle: oracle_type | Equiv::Oracle[In, bool],
131
+ # ?oracle_params: Hash[Symbol, untyped],
132
+ # ?algorithm: algorithm_name,
133
+ # ?automaton_type: :dfa,
134
+ # ?params: Hash[Symbol, untyped],
135
+ # ?random: Random
136
+ # ) { (Array[In]) -> bool } -> Automaton::DFA[In]
137
+ #: [In, Out] (
138
+ # alphabet: Array[In],
139
+ # sul: Automaton::Mealy[In, Out] | System::SUL[In, Out],
140
+ # ?oracle: oracle_type | Equiv::Oracle[In, Out],
141
+ # ?oracle_params: Hash[Symbol, untyped],
142
+ # ?algorithm: algorithm_name,
143
+ # automaton_type: :mealy,
144
+ # ?params: Hash[Symbol, untyped],
145
+ # ?random: Random
146
+ # ) -> Automaton::Mealy[In, Out]
147
+ #: [In, Out] (
148
+ # alphabet: Array[In],
149
+ # ?oracle: oracle_type | Equiv::Oracle[In, Out],
150
+ # ?oracle_params: Hash[Symbol, untyped],
151
+ # ?algorithm: algorithm_name,
152
+ # automaton_type: :mealy,
153
+ # ?params: Hash[Symbol, untyped],
154
+ # ?random: Random
155
+ # ) { (Array[In]) -> Out } -> Automaton::Mealy[In, Out]
156
+ #: [In, Out] (
157
+ # alphabet: Array[In],
158
+ # sul: Automaton::Moore[In, Out] | System::MooreLikeSUL[In, Out],
159
+ # ?oracle: oracle_type | Equiv::Oracle[In, Out],
160
+ # ?oracle_params: Hash[Symbol, untyped],
161
+ # ?algorithm: algorithm_name,
162
+ # automaton_type: :moore,
163
+ # ?params: Hash[Symbol, untyped],
164
+ # ?random: Random
165
+ # ) -> Automaton::Moore[In, Out]
166
+ #: [In, Out] (
167
+ # alphabet: Array[In],
168
+ # ?oracle: oracle_type | Equiv::Oracle[In, Out],
169
+ # ?oracle_params: Hash[Symbol, untyped],
170
+ # ?algorithm: algorithm_name,
171
+ # automaton_type: :moore,
172
+ # ?params: Hash[Symbol, untyped],
173
+ # ?random: Random
174
+ # ) { (Array[In]) -> Out } -> Automaton::Moore[In, Out]
175
+ #: [In, Call, Return] (
176
+ # alphabet: Array[In],
177
+ # call_alphabet: Array[Call],
178
+ # return_alphabet: Array[Return],
179
+ # sul: Automaton::VPA[In, Call, Return] | System::MooreLikeSUL[In | Call | Return, bool],
180
+ # ?oracle: oracle_type | Equiv::Oracle[In | Call | Return, bool],
181
+ # ?oracle_params: Hash[Symbol, untyped],
182
+ # ?algorithm: :kearns_vazirani_vpa,
183
+ # ?automaton_type: :vpa,
184
+ # ?params: Hash[Symbol, untyped],
185
+ # ?random: Random
186
+ # ) -> Automaton::VPA[In, Call, Return]
187
+ #: [In, Call, Return] (
188
+ # alphabet: Array[In],
189
+ # call_alphabet: Array[Call],
190
+ # return_alphabet: Array[Return],
191
+ # ?oracle: oracle_type | Equiv::Oracle[In | Call | Return, bool],
192
+ # ?oracle_params: Hash[Symbol, untyped],
193
+ # ?algorithm: :kearns_vazirani_vpa,
194
+ # ?automaton_type: :vpa,
195
+ # ?params: Hash[Symbol, untyped],
196
+ # ?random: Random
197
+ # ) { (Array[In | Call | Return]) -> bool } -> Automaton::VPA[In, Call, Return]
198
+ #: [In, Call, Return] (
199
+ # alphabet: Array[In],
200
+ # call_alphabet: Array[Call],
201
+ # return_input: Return,
202
+ # sul: Automaton::SPA[In, Call, Return] | System::MooreLikeSUL[In | Call | Return, bool],
203
+ # ?oracle: oracle_type | Equiv::Oracle[In | Call | Return, bool],
204
+ # ?oracle_params: Hash[Symbol, untyped],
205
+ # ?algorithm: :procedural,
206
+ # ?automaton_type: :spa,
207
+ # ?params: Hash[Symbol, untyped],
208
+ # ?random: Random
209
+ # ) -> Automaton::SPA[In, Call, Return]
210
+ #: [In, Call, Return] (
211
+ # alphabet: Array[In],
212
+ # call_alphabet: Array[Call],
213
+ # return_input: Return,
214
+ # ?oracle: oracle_type | Equiv::Oracle[In | Call | Return, bool],
215
+ # ?oracle_params: Hash[Symbol, untyped],
216
+ # ?algorithm: :procedural,
217
+ # ?automaton_type: :spa,
218
+ # ?params: Hash[Symbol, untyped],
219
+ # ?random: Random
220
+ # ) { (Array[In | Call | Return]) -> bool } -> Automaton::SPA[In, Call, Return]
24
221
  def self.learn(
25
222
  alphabet:,
26
223
  call_alphabet: nil,
27
224
  return_alphabet: nil,
225
+ return_input: nil,
28
226
  sul: nil,
29
- oracle: :random_walk,
227
+ oracle: nil,
30
228
  oracle_params: {},
31
- algorithm: :kearns_vazirani,
229
+ algorithm: nil,
32
230
  automaton_type: nil,
33
231
  params: {},
34
232
  random: Random,
35
233
  &sul_block
36
234
  )
37
- automaton_type ||= call_alphabet ? :vpa : :dfa
235
+ automaton = nil
38
236
 
39
237
  case sul
40
- when SUL
238
+ when System::SUL
41
239
  # Do nothing
42
- when Automaton
43
- automaton_type = sul.type
44
- sul = SUL.from_automaton(sul)
240
+ when Automaton::TransitionSystem
241
+ automaton = sul
242
+ oracle ||= :simulator
243
+ automaton_type ||= sul.type
244
+ sul = System.from_automaton(sul) # steep:ignore
45
245
  when nil
46
- sul = SUL.from_block(&sul_block)
246
+ sul = System.from_block(&sul_block) # steep:ignore
47
247
  else
48
248
  raise ArgumentError, "Unsupported SUL: #{sul}"
49
249
  end
50
250
 
51
- full_alphabet =
251
+ automaton_type ||=
252
+ if call_alphabet
253
+ return_input ? :spa : :vpa
254
+ else
255
+ :dfa
256
+ end
257
+
258
+ merged_alphabet =
52
259
  case automaton_type
53
260
  in :dfa | :moore | :mealy
54
261
  alphabet
55
- in :vpa
262
+ in :vpa | :spa
263
+ return_alphabet ||= [return_input]
56
264
  alphabet + call_alphabet + return_alphabet
57
265
  end
58
266
 
267
+ oracle ||= %i[vpa spa].include?(automaton_type) ? :random_well_matched_word : :random_word
268
+
59
269
  case oracle
60
- when Oracle
270
+ when Equiv::Oracle
61
271
  # Do nothing
62
- when :breadth_first_exploration
63
- oracle = BreadthFirstExplorationOracle.new(full_alphabet, sul, **oracle_params)
272
+ when :exhaustive_search
273
+ oracle = Equiv::ExhaustiveSearchOracle.new(merged_alphabet, sul, **oracle_params)
64
274
  when :random_walk
65
- oracle = RandomWalkOracle.new(full_alphabet, sul, random:, **oracle_params)
275
+ oracle = Equiv::RandomWalkOracle.new(merged_alphabet, sul, random:, **oracle_params)
276
+ when :random_word
277
+ oracle = Equiv::RandomWordOracle.new(merged_alphabet, sul, random:, **oracle_params)
278
+ when :random_well_matched_word
279
+ oracle =
280
+ Equiv::RandomWellMatchedWordOracle.new(
281
+ alphabet,
282
+ call_alphabet, # steep:ignore
283
+ return_alphabet, # steep:ignore
284
+ sul,
285
+ random:,
286
+ **oracle_params
287
+ )
288
+ when :simulator
289
+ oracle =
290
+ case automaton
291
+ when Automaton::Mealy
292
+ Equiv::TransitionSystemSimulatorOracle.new(alphabet, automaton, sul)
293
+ when Automaton::DFA, Automaton::Moore
294
+ Equiv::MooreLikeSimulatorOracle.new(alphabet, automaton, sul)
295
+ when Automaton::VPA
296
+ Equiv::VPASimulatorOracle.new(alphabet, call_alphabet, return_alphabet, automaton, sul) # steep:ignore
297
+ when Automaton::SPA
298
+ Equiv::SPASimulatorOracle.new(alphabet, call_alphabet, automaton, sul) # steep:ignore
299
+ else
300
+ raise ArgumentError, "Cannot simulate automaton: #{automaton}"
301
+ end
66
302
  else
67
303
  raise ArgumentError, "Unsupported oracle: #{oracle}"
68
304
  end
69
305
 
306
+ algorithm ||=
307
+ case automaton_type
308
+ in :dfa | :moore | :mealy
309
+ :kearns_vazirani
310
+ in :vpa
311
+ :kearns_vazirani_vpa
312
+ in :spa
313
+ :procedural
314
+ end
315
+
70
316
  case algorithm
71
317
  in :lstar
72
- LStar.learn(alphabet, sul, oracle, automaton_type:, **params)
318
+ Algorithm::LStar.learn(alphabet, sul, oracle, automaton_type:, **params)
73
319
  in :kearns_vazirani
74
- KearnsVazirani.learn(alphabet, sul, oracle, automaton_type:, call_alphabet:, return_alphabet:, **params)
320
+ Algorithm::KearnsVazirani.learn(alphabet, sul, oracle, automaton_type:, **params)
321
+ in :kearns_vazirani_vpa
322
+ Algorithm::KearnsVaziraniVPA.learn(alphabet, call_alphabet, return_alphabet, sul, oracle, **params)
75
323
  in :lsharp
76
- LSharp.learn(alphabet, sul, oracle, automaton_type:, **params)
324
+ Algorithm::LSharp.learn(alphabet, sul, oracle, automaton_type:, **params)
325
+ in :procedural
326
+ Algorithm::Procedural.learn(alphabet, call_alphabet, return_input, sul, oracle, **params)
77
327
  end
78
328
  end
79
329
  end
@@ -0,0 +1,16 @@
1
+ ---
2
+ path: ".gem_rbs_collection"
3
+ gems:
4
+ - name: prism
5
+ version: 1.0.0
6
+ source:
7
+ type: rubygems
8
+ - name: simplecov
9
+ version: '0.22'
10
+ source:
11
+ type: git
12
+ name: ruby/gem_rbs_collection
13
+ revision: 4f55d83688a772342f0d96966cd51d06af03c2c8
14
+ remote: https://github.com/ruby/gem_rbs_collection.git
15
+ repo_dir: gems
16
+ gemfile_lock_path: Gemfile.lock
@@ -0,0 +1,14 @@
1
+ sources:
2
+ - type: git
3
+ name: ruby/gem_rbs_collection
4
+ remote: https://github.com/ruby/gem_rbs_collection.git
5
+ revision: main
6
+ repo_dir: gems
7
+
8
+ path: .gem_rbs_collection
9
+
10
+ gems:
11
+ - name: prism
12
+ ignore: false
13
+ - name: simplecov
14
+ ignore: false
data/renovate.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": [
4
+ "config:recommended"
5
+ ]
6
+ }
@@ -0,0 +1,30 @@
1
+ # Generated from lib/lernen/algorithm/cex_processor/acex.rb with RBS::Inline
2
+
3
+ module Lernen
4
+ module Algorithm
5
+ module CexProcessor
6
+ # Acex represents an abstract counterexample.
7
+ #
8
+ # Note that this class is *abstract*. We should implement the following method:
9
+ #
10
+ # - `#compute_effect(index)`
11
+ class Acex
12
+ @cache: Array[bool | nil]
13
+
14
+ # : (Integer size) -> void
15
+ def initialize: (Integer size) -> void
16
+
17
+ # : () -> Integer
18
+ def size: () -> Integer
19
+
20
+ # : (Integer index) -> bool
21
+ def effect: (Integer index) -> bool
22
+
23
+ private
24
+
25
+ # : (Integer index) -> bool
26
+ def compute_effect: (Integer index) -> bool
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ # Generated from lib/lernen/algorithm/cex_processor/prefix_transformer_acex.rb with RBS::Inline
2
+
3
+ module Lernen
4
+ module Algorithm
5
+ module CexProcessor
6
+ # PrefixTransformerAcex is an implementation of `Acex` for classic prefix transformers.
7
+ #
8
+ # @rbs generic Conf
9
+ # @rbs generic In
10
+ # @rbs generic Out
11
+ class PrefixTransformerAcex[Conf, In, Out] < Acex
12
+ # : (
13
+ # Array[In] cex,
14
+ # System::SUL[In, Out] sul,
15
+ # Automaton::TransitionSystem[Conf, In, Out] hypothesis,
16
+ # ^(Conf) -> Array[In] conf_to_prefix
17
+ # ) -> void
18
+ def initialize: (Array[In] cex, System::SUL[In, Out] sul, Automaton::TransitionSystem[Conf, In, Out] hypothesis, ^(Conf) -> Array[In] conf_to_prefix) -> void
19
+
20
+ private
21
+
22
+ # @rbs override
23
+ def compute_effect: ...
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,59 @@
1
+ # Generated from lib/lernen/algorithm/cex_processor.rb with RBS::Inline
2
+
3
+ module Lernen
4
+ module Algorithm
5
+ type cex_processing_method = :linear | :binary | :exponential
6
+
7
+ # CexProcessor contains implementations of counterexample processing functions.
8
+ #
9
+ # A counterexample is a word that leads to the different result between
10
+ # a hypothesis automaton and a SUL (i.e., `hypothesis.run(cex)[0].last != sul.query_last(cex)`).
11
+ # Where `h[n] = conf_to_prefix[hypothesis.run(cex[0...n])[1]]`, there
12
+ # are some `n` (where `0 <= n < cex.size`) such that
13
+ # `sul.query_last(h[n] + cex[n...]) != sul.query_last(h[n + 1] + cex[n + 1...])`
14
+ # because `sul.query_last(cex) == sul.query_last(h[0] + cex[n...])` and
15
+ # `sul.query_last(h[cex.size] + cex[cex.size...]) == hypothesis.run(cex)[0].last`.
16
+ # Finding such a position `n` from `cex` is called "counterexample processing".
17
+ #
18
+ # The result `n` of counterexample processing has a good property for automata
19
+ # learning. Because `sul.query_last(h[n] + cex[n...]) != sul.query_last(h[n + 1] + cex[n + 1...])`,
20
+ # a prefix `h[n] + cex[n]` leads a different state than a state of `h[n + 1]`
21
+ # with a suffix `cex[n + 1...]`.
22
+ #
23
+ # For counterexample processing, we can use some searching approach such like
24
+ # linear or binrary search. Using binary search for counterexample processing,
25
+ # it is known as the Rivest-Schapire (RS) optimization typically. For the more
26
+ # detailed information, please refer [Isberner and Steffen (2014) "An Abstract
27
+ # Framework for Counterexample Analysis in Active Automata Learning"](https://proceedings.mlr.press/v34/isberner14a).
28
+ module CexProcessor
29
+ # Processes a given counterexample in the `cex_processing` way.
30
+ #
31
+ # It returns `n` such that `acex.effect(n) != acex.effect(n + 1)`.
32
+ #
33
+ # : (
34
+ # Acex acex,
35
+ # ?cex_processing: cex_processing_method
36
+ # ) -> Integer
37
+ def self.process: (Acex acex, ?cex_processing: cex_processing_method) -> Integer
38
+
39
+ private
40
+
41
+ # Processes a given counterexample by linear search.
42
+ #
43
+ # : (Acex acex, low: Integer, high: Integer) -> Integer
44
+ def self.process_linear: (Acex acex, low: Integer, high: Integer) -> Integer
45
+
46
+ # Processes a given counterexample by binary search.
47
+ #
48
+ # It is known as the Rivest-Schapire (RS) optimization.
49
+ #
50
+ # : (Acex acex, low: Integer, high: Integer) -> Integer
51
+ def self.process_binary: (Acex acex, low: Integer, high: Integer) -> Integer
52
+
53
+ # Processes a given counterexample by exponential seatch.
54
+ #
55
+ # : (Acex acex, low: Integer, high: Integer) -> Integer
56
+ def self.process_exponential: (Acex acex, low: Integer, high: Integer) -> Integer
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,68 @@
1
+ # Generated from lib/lernen/algorithm/kearns_vazirani/discrimination_tree.rb with RBS::Inline
2
+
3
+ module Lernen
4
+ module Algorithm
5
+ module KearnsVazirani
6
+ # DiscriminationTree is an implementation of discrimination tree data structure.
7
+ #
8
+ # This data structure is used for Kearns-Vazirani algorithm.
9
+ #
10
+ # @rbs generic In -- Type for input alphabet
11
+ # @rbs generic Out -- Type for output values
12
+ class DiscriminationTree[In, Out]
13
+ type tree[In, Out] = Node[In, Out] | Leaf[In]
14
+
15
+ class Node[In, Out] < Data
16
+ attr_reader suffix: Array[In]
17
+ attr_reader branch: Hash[Out, tree[In, Out]]
18
+ def self.[]: [In, Out] (Array[In] suffix, Hash[Out, tree[In, Out]] branch) -> Node[In, Out]
19
+ end
20
+
21
+ class Leaf[In] < Data
22
+ attr_reader prefix: Array[In]
23
+ def self.[]: [In] (Array[In] prefix) -> Leaf[In]
24
+ end
25
+
26
+ @alphabet: Array[In]
27
+
28
+ @sul: System::SUL[In, Out]
29
+
30
+ @automaton_type: :dfa | :mealy | :moore
31
+
32
+ @cex_processing: cex_processing_method
33
+
34
+ @path_hash: Hash[Array[In], Array[Out]]
35
+
36
+ @root: Node[In, Out]
37
+
38
+ # : (
39
+ # Array[In] alphabet,
40
+ # System::SUL[In, Out] sul,
41
+ # cex: Array[In],
42
+ # automaton_type: :dfa | :mealy | :moore,
43
+ # cex_processing: cex_processing_method
44
+ # ) -> void
45
+ def initialize: (Array[In] alphabet, System::SUL[In, Out] sul, cex: Array[In], automaton_type: :dfa | :mealy | :moore, cex_processing: cex_processing_method) -> void
46
+
47
+ # Returns a prefix discriminated by `word`.
48
+ #
49
+ # : (Array[In] word) -> Array[In]
50
+ def sift: (Array[In] word) -> Array[In]
51
+
52
+ # Constructs a hypothesis automaton from this discrimination tree.
53
+ #
54
+ # : () -> [Automaton::TransitionSystem[Integer, In, Out], Hash[Integer, Array[In]]]
55
+ def build_hypothesis: () -> [ Automaton::TransitionSystem[Integer, In, Out], Hash[Integer, Array[In]] ]
56
+
57
+ # Update this classification tree by the given `cex`.
58
+ #
59
+ # : (
60
+ # Array[In] cex,
61
+ # Automaton::TransitionSystem[Integer, In, Out] hypothesis,
62
+ # Hash[Integer, Array[In]] state_to_prefix
63
+ # ) -> void
64
+ def refine_hypothesis: (Array[In] cex, Automaton::TransitionSystem[Integer, In, Out] hypothesis, Hash[Integer, Array[In]] state_to_prefix) -> void
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,51 @@
1
+ # Generated from lib/lernen/algorithm/kearns_vazirani/kearns_vazirani_learner.rb with RBS::Inline
2
+
3
+ module Lernen
4
+ module Algorithm
5
+ module KearnsVazirani
6
+ # KearnzVaziraniLearner is an implementation of Kearnz-Vazirani algorithm.
7
+ #
8
+ # Kearns-Vazirani is introduced by [Kearns & Vazirani (1994) "An Introduction to
9
+ # Computational Learning Theory"](https://direct.mit.edu/books/monograph/2604/An-Introduction-to-Computational-Learning-Theory).
10
+ #
11
+ # @rbs generic In -- Type for input alphabet
12
+ # @rbs generic Out -- Type for output values
13
+ class KearnsVaziraniLearner[In, Out] < Learner[In, Out]
14
+ @alphabet: Array[In]
15
+
16
+ @sul: System::SUL[In, Out]
17
+
18
+ @oracle: Equiv::Oracle[In, Out]
19
+
20
+ @automaton_type: :dfa | :moore | :mealy
21
+
22
+ @cex_processing: cex_processing_method
23
+
24
+ @tree: DiscriminationTree[In, Out] | nil
25
+
26
+ # : (
27
+ # Array[In] alphabet, System::SUL[In, Out] sul,
28
+ # automaton_type: :dfa | :moore | :mealy,
29
+ # ?cex_processing: cex_processing_method
30
+ # ) -> void
31
+ def initialize: (Array[In] alphabet, System::SUL[In, Out] sul, automaton_type: :dfa | :moore | :mealy, ?cex_processing: cex_processing_method) -> void
32
+
33
+ # @rbs override
34
+ def add_alphabet: ...
35
+
36
+ # @rbs override
37
+ def build_hypothesis: ...
38
+
39
+ # @rbs override
40
+ def refine_hypothesis: ...
41
+
42
+ private
43
+
44
+ # Constructs the first hypothesis automaton.
45
+ #
46
+ # : () -> Automaton::TransitionSystem[Integer, In, Out]
47
+ def build_first_hypothesis: () -> Automaton::TransitionSystem[Integer, In, Out]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,32 @@
1
+ # Generated from lib/lernen/algorithm/kearns_vazirani.rb with RBS::Inline
2
+
3
+ module Lernen
4
+ module Algorithm
5
+ # KearnzVazirani provides an implementation of Kearnz-Vazirani algorithm.
6
+ #
7
+ # Kearns-Vazirani is introduced by [Kearns & Vazirani (1994) "An Introduction to
8
+ # Computational Learning Theory"](https://direct.mit.edu/books/monograph/2604/An-Introduction-to-Computational-Learning-Theory).
9
+ module KearnsVazirani
10
+ # Runs Kearns-Vazirani algorithm and returns an inferred automaton.
11
+ #
12
+ # : [In] (
13
+ # Array[In] alphabet, System::SUL[In, bool] sul, Equiv::Oracle[In, bool] oracle,
14
+ # automaton_type: :dfa,
15
+ # ?cex_processing: cex_processing_method, ?max_learning_rounds: Integer | nil
16
+ # ) -> Automaton::DFA[In]
17
+ # : [In, Out] (
18
+ # Array[In] alphabet, System::SUL[In, Out] sul, Equiv::Oracle[In, Out] oracle,
19
+ # automaton_type: :mealy,
20
+ # ?cex_processing: cex_processing_method, ?max_learning_rounds: Integer | nil
21
+ # ) -> Automaton::Mealy[In, Out]
22
+ # : [In, Out] (
23
+ # Array[In] alphabet, System::SUL[In, Out] sul, Equiv::Oracle[In, Out] oracle,
24
+ # automaton_type: :moore,
25
+ # ?cex_processing: cex_processing_method, ?max_learning_rounds: Integer | nil
26
+ # ) -> Automaton::Moore[In, Out]
27
+ def self.learn: [In] (Array[In] alphabet, System::SUL[In, bool] sul, Equiv::Oracle[In, bool] oracle, automaton_type: :dfa, ?cex_processing: cex_processing_method, ?max_learning_rounds: Integer | nil) -> Automaton::DFA[In]
28
+ | [In, Out] (Array[In] alphabet, System::SUL[In, Out] sul, Equiv::Oracle[In, Out] oracle, automaton_type: :mealy, ?cex_processing: cex_processing_method, ?max_learning_rounds: Integer | nil) -> Automaton::Mealy[In, Out]
29
+ | [In, Out] (Array[In] alphabet, System::SUL[In, Out] sul, Equiv::Oracle[In, Out] oracle, automaton_type: :moore, ?cex_processing: cex_processing_method, ?max_learning_rounds: Integer | nil) -> Automaton::Moore[In, Out]
30
+ end
31
+ end
32
+ end