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/lstar.rb DELETED
@@ -1,170 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lernen
4
- # ObservationTable is an observation table implementation.
5
- class ObservationTable
6
- def initialize(alphabet, sul, automaton_type:)
7
- @alphabet = alphabet
8
- @sul = sul
9
- @automaton_type = automaton_type
10
-
11
- @prefixes = [[]]
12
- @suffixes = []
13
- @table = {}
14
-
15
- case @automaton_type
16
- in :dfa | :moore
17
- @suffixes << []
18
- in :mealy
19
- @alphabet.each { |a| @suffixes << [a] }
20
- end
21
-
22
- update_table
23
- end
24
-
25
- attr_reader :prefixes, :suffixes
26
-
27
- # Finds new prefixes to close.
28
- def find_prefixes_to_close
29
- prefixes_to_close = []
30
- unclosed_rows = Set.new
31
-
32
- prefix_rows = @prefixes.to_set { @table[_1] }
33
-
34
- extended_prefixes = @prefixes.flat_map { |q| @alphabet.map { |a| q + [a] } }
35
- extended_prefixes.each do |qa|
36
- row = @table[qa]
37
- unless prefix_rows.include?(row) || unclosed_rows.include?(row)
38
- prefixes_to_close << qa
39
- unclosed_rows << row
40
- end
41
- end
42
-
43
- return if prefixes_to_close.empty?
44
-
45
- prefixes_to_close.sort_by!(&:size).reverse!
46
- end
47
-
48
- # Checks consistency and returns a new suffix to add if this observation table
49
- # is inconsistent.
50
- def check_consistency
51
- @prefixes.combination(2) do |(q1, q2)|
52
- next unless @table[q1] == @table[q2]
53
-
54
- @alphabet.each do |a|
55
- q1a = q1 + [a]
56
- q2a = q2 + [a]
57
- next if @table[q1a] == @table[q2a]
58
-
59
- @suffixes.each_with_index do |e, i|
60
- next if @table[q1a][i] == @table[q2a][i]
61
-
62
- return [a] + e
63
- end
64
- end
65
- end
66
-
67
- nil
68
- end
69
-
70
- # Update observation table entries.
71
- def update_table
72
- extended_prefixes = @prefixes.flat_map { |q| [q] + @alphabet.map { |a| q + [a] } }
73
-
74
- extended_prefixes.each do |qa|
75
- @table[qa] ||= []
76
- next if @table[qa].size == @suffixes.size
77
- @suffixes.each_with_index do |e, i|
78
- next if i < @table[qa].size
79
- inputs = qa + e
80
- outputs = inputs.empty? ? [@sul.query_empty] : @sul.query(inputs)
81
- @table[qa] += [outputs.last]
82
- end
83
- end
84
- end
85
-
86
- # Constructs a hypothesis automaton from this observation table.
87
- def to_hypothesis
88
- state_to_prefix = @prefixes.each_with_index.to_h { |q, i| [i, q] }
89
- row_to_state = @prefixes.each_with_index.to_h { |q, i| [@table[q], i] }
90
-
91
- transitions = {}
92
- @prefixes.each_with_index do |q, i|
93
- @alphabet.each_with_index do |a, j|
94
- case @automaton_type
95
- in :moore | :dfa
96
- transitions[[i, a]] = row_to_state[@table[q + [a]]]
97
- in :mealy
98
- transitions[[i, a]] = [@table[q][j], row_to_state[@table[q + [a]]]]
99
- end
100
- end
101
- end
102
-
103
- automaton =
104
- case @automaton_type
105
- in :dfa
106
- accept_states = state_to_prefix.to_a.filter { |(_, q)| @table[q][0] }.to_set { |(i, _)| i }
107
- DFA.new(0, accept_states, transitions)
108
- in :moore
109
- outputs = state_to_prefix.transform_values { |q| @table[q][0] }
110
- Moore.new(0, outputs, transitions)
111
- in :mealy
112
- Mealy.new(0, transitions)
113
- end
114
-
115
- [automaton, state_to_prefix]
116
- end
117
- end
118
-
119
- # LStar is an implementation of Angluin's L* algorithm.
120
- module LStar
121
- # Runs Angluin's L* algoritghm and returns an inferred automaton.
122
- def self.learn(alphabet, sul, oracle, automaton_type:, cex_processing: :binary, max_learning_rounds: nil)
123
- observation_table = ObservationTable.new(alphabet, sul, automaton_type:)
124
- learning_rounds = 0
125
-
126
- loop do
127
- break if max_learning_rounds && learning_rounds == max_learning_rounds
128
- learning_rounds += 1
129
-
130
- if cex_processing.nil?
131
- new_suffix = observation_table.check_consistency
132
- until new_suffix.nil?
133
- observation_table.suffixes << new_suffix
134
- observation_table.update_table
135
- new_suffix = observation_table.check_consistency
136
- end
137
- end
138
-
139
- new_prefixes = observation_table.find_prefixes_to_close
140
- until new_prefixes.nil?
141
- observation_table.prefixes.push(*new_prefixes)
142
- observation_table.update_table
143
- new_prefixes = observation_table.find_prefixes_to_close
144
- end
145
-
146
- hypothesis, state_to_prefix = observation_table.to_hypothesis
147
- cex = oracle.find_cex(hypothesis)
148
- break if cex.nil?
149
-
150
- if cex_processing.nil?
151
- all_prefixes = (0..cex.size).map { |n| cex[0...n] }
152
- all_prefixes.each do |prefix|
153
- observation_table.prefixes << prefix unless observation_table.prefixes.include?(prefix)
154
- end
155
- else
156
- old_prefix, new_input, new_suffix =
157
- CexProcessor.process(sul, hypothesis, cex, state_to_prefix, cex_processing:)
158
- _, old_state = hypothesis.run(old_prefix)
159
- new_prefix = state_to_prefix[old_state] + [new_input]
160
- observation_table.prefixes << new_prefix unless observation_table.prefixes.include?(new_prefix)
161
- observation_table.suffixes << new_suffix unless observation_table.suffixes.include?(new_suffix)
162
- end
163
- observation_table.update_table
164
- end
165
-
166
- hypothesis, = observation_table.to_hypothesis
167
- hypothesis
168
- end
169
- end
170
- end
data/lib/lernen/oracle.rb DELETED
@@ -1,119 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lernen
4
- # Oracle is an equivalence oracle.
5
- #
6
- # Note that this class is *abstract*. You should implement the following method:
7
- #
8
- # - `#find_cex(hypothesis)`
9
- class Oracle
10
- def initialize(alphabet, sul)
11
- @alphabet = alphabet
12
- @sul = sul
13
-
14
- @num_calls = 0
15
- @num_queries = 0
16
- @num_steps = 0
17
- @current_state = nil
18
- end
19
-
20
- # Returns statistics information as a `Hash` object.
21
- def stats
22
- { num_calls: @num_calls, num_queries: @num_queries, num_steps: @num_steps }
23
- end
24
-
25
- # Finds a conterexample against the given `hypothesis` automaton.
26
- # If it is found, it returns the counterexample inputs, or it returns `nil` otherwise.
27
- #
28
- # This is *abstract*.
29
- def find_cex(_hypothesis)
30
- raise TypeError, "abstract method: `step`"
31
- end
32
-
33
- # Resets the internal states of this oracle.
34
- def reset_internal(hypothesis)
35
- @current_state = hypothesis.initial
36
-
37
- @sul.shutdown
38
- @sul.setup
39
-
40
- @num_queries += 1
41
- end
42
- end
43
-
44
- # This equivalence oracles uses bradth-first exploration of all possible input
45
- # combinations up to a specified depth for equivalence checking.
46
- class BreadthFirstExplorationOracle < Oracle
47
- def initialize(alphabet, sul, depth: 5)
48
- super(alphabet, sul)
49
-
50
- @depth = depth
51
- end
52
-
53
- # Finds a conterexample against the given `hypothesis` automaton.
54
- def find_cex(hypothesis)
55
- @num_calls += 1
56
-
57
- @alphabet.product(*[@alphabet] * (@depth - 1)) do |inputs|
58
- reset_internal(hypothesis)
59
-
60
- inputs.each_with_index do |input, i|
61
- @num_steps += 1
62
- h_out, @current_state = hypothesis.step(@current_state, input)
63
- s_out = @sul.step(input)
64
-
65
- if h_out != s_out
66
- @sul.shutdown
67
- return inputs[0..i]
68
- end
69
- end
70
- end
71
-
72
- @sul.shutdown
73
- nil
74
- end
75
- end
76
-
77
- # This equivalence oracles uses random-walk exploration for equivalence checking.
78
- class RandomWalkOracle < Oracle
79
- def initialize(alphabet, sul, step_limit: 3000, reset_prob: 0.09, random: Random)
80
- super(alphabet, sul)
81
-
82
- @step_limit = step_limit
83
- @reset_prob = reset_prob
84
- @random = random
85
- end
86
-
87
- # Finds a conterexample against the given `hypothesis` automaton.
88
- def find_cex(hypothesis)
89
- @num_calls += 1
90
-
91
- random_steps_done = 0
92
- inputs = []
93
- reset_internal(hypothesis)
94
-
95
- while random_steps_done < @step_limit
96
- random_steps_done += 1
97
-
98
- if @random.rand < @reset_prob
99
- inputs = []
100
- reset_internal(hypothesis)
101
- end
102
-
103
- inputs << @alphabet.sample(random: @random)
104
-
105
- @num_steps += 1
106
- h_out, @current_state = hypothesis.step(@current_state, inputs.last)
107
- s_out = @sul.step(inputs.last)
108
-
109
- if h_out != s_out
110
- @sul.shutdown
111
- return inputs
112
- end
113
- end
114
-
115
- @sul.shutdown
116
- nil
117
- end
118
- end
119
- end
data/lib/lernen/sul.rb DELETED
@@ -1,210 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lernen
4
- # SUL is a System Under Learning.
5
- #
6
- # It is an abtraction of a system under learning (SUL) which accepts an operation
7
- # called "membership query"; it takes an input string (word) and returns a sequence
8
- # of outputs corresponding to the input string.
9
- #
10
- # This SUL assumes the system is much like Mealy machine; that is, a transition puts
11
- # an output, and query does not accept the empty string due to no outputs.
12
- #
13
- # Note that this class is *abstract*. You should implement the following method:
14
- #
15
- # - `#step(input)`
16
- class SUL
17
- # Creates a SUL from the given block as an implementation of a membership query.
18
- def self.from_block(cache: true, &) = BlockSUL.new(cache:, &)
19
-
20
- # Creates a SUL from the given automaton as an implementation.
21
- def self.from_automaton(automaton, cache: true) =
22
- case automaton
23
- when DFA
24
- DFASimulatorSUL.new(automaton, cache:)
25
- when Mealy
26
- MealySimulatorSUL.new(automaton, cache:)
27
- when Moore
28
- MooreSimulatorSUL.new(automaton, cache:)
29
- end
30
-
31
- def initialize(cache: true)
32
- @cache = cache ? {} : nil
33
- @num_cached_queries = 0
34
- @num_queries = 0
35
- @num_steps = 0
36
- end
37
-
38
- # Returns statistics information as a `Hash` object.
39
- def stats
40
- {
41
- num_cache: @cache&.size || 0,
42
- num_cached_queries: @num_cached_queries,
43
- num_queries: @num_queries,
44
- num_steps: @num_steps
45
- }
46
- end
47
-
48
- # Runs a membership query with the given inputs.
49
- #
50
- # Note that this method does not accept the empty string due to no outpus.
51
- # If you need to call `query` with the empty string, you should use `MooreSUL#query_empty` instead.
52
- def query(inputs)
53
- cached = @cache && @cache[inputs]
54
- if cached
55
- @num_cached_queries += 1
56
- return cached
57
- end
58
-
59
- if inputs.empty?
60
- raise ArgumentError, "`query` does not accept the empty string. Please use `query_empty` instead."
61
- end
62
-
63
- setup
64
-
65
- outputs = inputs.map { |input| step(input) }
66
-
67
- shutdown
68
-
69
- @num_queries += 1
70
- @num_steps += inputs.size
71
-
72
- @cache[inputs] = outputs if @cache
73
-
74
- outputs
75
- end
76
-
77
- # It is a setup procedure of this SUL.
78
- #
79
- # Note that it does nothing by default.
80
- def setup
81
- end
82
-
83
- # It is a shutdown procedure of this SUL.
84
- #
85
- # Note that it does nothing by default.
86
- def shutdown
87
- end
88
-
89
- # Consumes the given `input` and returns the correspoding output.
90
- #
91
- # This is *abstract*.
92
- def step(_input)
93
- raise TypeError, "abstract method: `step`"
94
- end
95
- end
96
-
97
- # MooreSUL is a System Under Learning (SUL) for a system much like Moore machine.
98
- #
99
- # By contrast to `SUL`, this accepts a query with the empty string additionally.
100
- #
101
- # Note that this class is *abstract*. You should implement the following method:
102
- #
103
- # - `#step(input)`
104
- # - `#query_empty`
105
- class MooreSUL < SUL
106
- def initialize(cache: true)
107
- super
108
- end
109
-
110
- # Runs a membership query with the empty input.
111
- #
112
- # This is *abstract*.
113
- def query_empty
114
- raise TypeError, "abstract method: `query_empty`"
115
- end
116
- end
117
-
118
- # BaseSimulatorSUL is a base implementation of SUL on automaton simulators.
119
- module BaseSimulatorSUL
120
- # It is a setup procedure of this SUL.
121
- def setup
122
- @state = @automaton.initial
123
- end
124
-
125
- # It is a shutdown procedure of this SUL.
126
- def shutdown
127
- @state = nil
128
- end
129
-
130
- # Runs a membership query with the given inputs.
131
- def step(input)
132
- output, @state = @automaton.step(@state, input)
133
- output
134
- end
135
- end
136
-
137
- # DFASimulatorSUL is a SUL on a DFA simuator.
138
- class DFASimulatorSUL < MooreSUL
139
- include BaseSimulatorSUL
140
-
141
- def initialize(automaton, cache: true)
142
- super(cache:)
143
-
144
- @automaton = automaton
145
- @state = nil
146
- end
147
-
148
- # Runs a membership query with the empty input.
149
- def query_empty
150
- @automaton.accept_states.include?(@automaton.initial_state)
151
- end
152
- end
153
-
154
- # MealySimulatorSUL is a SUL on a Mealy simuator.
155
- class MealySimulatorSUL < SUL
156
- include BaseSimulatorSUL
157
-
158
- def initialize(automaton, cache: true)
159
- super(cache:)
160
-
161
- @automaton = automaton
162
- @state = nil
163
- end
164
- end
165
-
166
- # MooreSimulatorSUL is a SUL on a Moore simuator.
167
- class MooreSimulatorSUL < MooreSUL
168
- include BaseSimulatorSUL
169
-
170
- def initialize(automaton, cache: true)
171
- super(cache:)
172
-
173
- @automaton = automaton
174
- @state = nil
175
- end
176
-
177
- # Runs a membership query with the empty input.
178
- def query_empty
179
- @automaton.outputs[@automaton.initial_state]
180
- end
181
- end
182
-
183
- # BlockSUL is a System Under Learning (SUL) constructed from a block.
184
- #
185
- # A block is expected to behave like a membership query.
186
- class BlockSUL < MooreSUL
187
- def initialize(cache: true, &block)
188
- super(cache:)
189
-
190
- @block = block
191
- @inputs = []
192
- end
193
-
194
- # It is a setup procedure of this SUL.
195
- def setup
196
- @inputs = []
197
- end
198
-
199
- # Runs a membership query with the given inputs.
200
- def step(input)
201
- @inputs << input
202
- @block.call(@inputs)
203
- end
204
-
205
- # Runs a membership query with the empty input.
206
- def query_empty
207
- @block.call([])
208
- end
209
- end
210
- end